Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
9e35bb0
add CI
icfaust May 4, 2025
97cc509
indenting
icfaust May 4, 2025
3897bc6
indenting
icfaust May 4, 2025
99fb653
indenting
icfaust May 4, 2025
1be24dc
try again
icfaust May 5, 2025
5c3bf78
try again
icfaust May 5, 2025
55e35e0
add testing steps
icfaust May 5, 2025
d29f498
address scipy.nan
icfaust May 5, 2025
34cb22c
deal with other constants
icfaust May 5, 2025
1033957
address tests
icfaust May 5, 2025
5e302bb
move to numpy array
icfaust May 5, 2025
0e0ca22
swap core to numpy
icfaust May 5, 2025
e2a3b73
update test file
icfaust May 5, 2025
5e1b971
remove scipy aspects
icfaust May 5, 2025
4e95f0c
remove import
icfaust May 5, 2025
c16bca9
remove import
icfaust May 5, 2025
dbf70d9
remove import
icfaust May 5, 2025
afec72d
remove import
icfaust May 5, 2025
d22c490
remove import
icfaust May 5, 2025
e7874b5
remove import
icfaust May 5, 2025
b6156aa
test
icfaust May 5, 2025
5d80e7c
test
icfaust May 5, 2025
521bb8b
test
icfaust May 5, 2025
828a33a
test
icfaust May 5, 2025
ec169c2
test
icfaust May 5, 2025
a550dc3
test
icfaust May 5, 2025
3ba6860
test
icfaust May 5, 2025
8e9d7ea
prep for f2py removal:
icfaust May 7, 2025
cb56e7c
switch to setuptools
icfaust May 7, 2025
872bd7a
check folder
icfaust May 7, 2025
109411f
missing file addition
icfaust May 7, 2025
0e1c35c
remove diagnostics
icfaust May 7, 2025
67a4492
try to patch around it
icfaust May 7, 2025
8119304
diagnose
icfaust May 7, 2025
3b23d45
lets see if this goes poorly
icfaust May 7, 2025
4c4770b
just stop the unittesting
icfaust May 7, 2025
886f0a9
add initial interfaces
icfaust May 8, 2025
432fb3f
missing header
icfaust May 8, 2025
36bd8e3
change copyright
icfaust May 8, 2025
60ecb3b
temporary save
icfaust May 8, 2025
e57df6f
init to NULL
icfaust May 8, 2025
36624fd
it compiles
icfaust May 13, 2025
2d7deb6
fixes
icfaust May 13, 2025
c34e22f
runnable, check for speed
icfaust May 14, 2025
44f1473
try to get it to work in another directory
icfaust May 14, 2025
25c3f4e
try to get it to work in another directory
icfaust May 14, 2025
48b3d20
try to get it to work in another directory
icfaust May 14, 2025
b553f49
try again
icfaust May 14, 2025
e4a1621
last steps
icfaust May 14, 2025
26765d4
remove nulls
icfaust May 14, 2025
47a9064
add endline
icfaust May 14, 2025
f55a6fb
last steps
icfaust May 14, 2025
8f9ce83
try to generalize ci a bit
icfaust May 14, 2025
cfc1b85
Update _tricubmodule.c
icfaust May 14, 2025
2dce6b8
Update _tricubmodule.c
icfaust May 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: CI
on:
pull_request:
branches: [ "master" ]
push:
branches:
- master
workflow_dispatch:

permissions: read-all

concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref_name }}-${{ github.event.number || github.sha }}
cancel-in-progress: true

jobs:
build-and-test:
strategy:
fail-fast: false
matrix:
os: [
{ name: arm Ubuntu-24.04, label: ubuntu-24.04-arm },
{ name: x86 Ubuntu-latest, label: ubuntu-latest },
{ name: x86 Windows-latest, label: windows-latest },
]

name: CI-${{ matrix.os.name }}
runs-on: ${{ matrix.os.label }}
steps:
- name: Checkout EqTools
uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install EqTools
run: python -m pip install .
- name: Run tests
run: python tests/test.py
- name: Run unittests
# Windows cannot properly load the python2 test data pickle file
if: runner.os != 'Windows'
run: python tests/unittests.py || true
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ The following packages are required or recommended:

- NumPy: Required.
- SciPy: Required.
- F2PY: Optional, needed to build the optional `trispline` module.
- setuptools: Required.
- matplotlib: Optional, needed to produce plot of flux surfaces.
- MDSplus: Optional, needed to use data stored in MDSplus trees.
- dd: Optional, needed to use data from ASDEX-Upgrade shotfiles.

All of these should be available via pip (and should be installed automatically if you run `pip install eqtools` as described in the next section). If you wish to build in place, you may first need to run:

pip install numpy scipy f2py matplotlib
pip install numpy scipy setuptools matplotlib

Installation
------------
Expand Down
78 changes: 39 additions & 39 deletions eqtools/AUGData.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
working with ASDEX Upgrade experimental data.
"""

import scipy
import numpy
from collections import namedtuple

from .core import PropertyAccessMixin, ModuleWarning, Equilibrium, inPolygon
Expand Down Expand Up @@ -436,8 +436,8 @@ def getFluxVol(self, length_unit=3):
if self._fluxVol is None:
try:
fluxVolNode = self._MDSTree('Vol') # Lpf is unreliable so I have to do this trick....
temp = scipy.where(
scipy.sum(fluxVolNode.data, axis=0)[::2] != 0
temp = numpy.where(
numpy.sum(fluxVolNode.data, axis=0)[::2] != 0
)[0].max() + 1 # Find the where the volume is non-zero, give the maximum index and add one (for the core value)

self._fluxVol = fluxVolNode.data[:self._timeidxend][
Expand Down Expand Up @@ -510,17 +510,17 @@ def getRLCFS(self, length_unit=1):
try:
rgeo = self.getSSQ('Rgeo')
RLCFSNode = self.getSSQ('rays')
RLCFStemp = scipy.hstack(
(scipy.atleast_2d(RLCFSNode.data[:, -1]).T, RLCFSNode.data)
RLCFStemp = numpy.hstack(
(numpy.atleast_2d(RLCFSNode.data[:, -1]).T, RLCFSNode.data)
)
templen = RLCFSNode.data.shape

self._RLCFS = scipy.tile(
self._RLCFS = numpy.tile(
rgeo.data, (templen[1] + 1, 1)
).T + RLCFStemp * scipy.cos(
scipy.tile(
).T + RLCFStemp * numpy.cos(
numpy.tile(
(
scipy.linspace(0, 2 * scipy.pi, templen[1] + 1)
numpy.linspace(0, 2 * numpy.pi, templen[1] + 1)
), (templen[0], 1)
)
) # construct a 2d grid of angles, take cos, multiply by radius
Expand Down Expand Up @@ -550,16 +550,16 @@ def getZLCFS(self, length_unit=1):
try:
zgeo = self.getSSQ('Zgeo')
ZLCFSNode = self.getSSQ('rays')
ZLCFStemp = scipy.hstack(
(scipy.atleast_2d(ZLCFSNode.data[:, -1]).T, ZLCFSNode.data)
ZLCFStemp = numpy.hstack(
(numpy.atleast_2d(ZLCFSNode.data[:, -1]).T, ZLCFSNode.data)
)
templen = ZLCFSNode.data.shape

self._ZLCFS = scipy.tile(
self._ZLCFS = numpy.tile(
zgeo.data, (templen[1] + 1, 1)
).T + ZLCFStemp * scipy.sin(
scipy.tile(
(scipy.linspace(0, 2 * scipy.pi, templen[1] + 1)),
).T + ZLCFStemp * numpy.sin(
numpy.tile(
(numpy.linspace(0, 2 * numpy.pi, templen[1] + 1)),
(templen[0], 1)
)
) # construct a 2d grid of angles, take sin, multiply by radius
Expand Down Expand Up @@ -624,14 +624,14 @@ def remapLCFS(self, mask=False):
v = path.vertices
RLCFS_frame.extend(v[:, 0])
ZLCFS_frame.extend(v[:, 1])
RLCFS_frame.append(scipy.nan)
ZLCFS_frame.append(scipy.nan)
RLCFS_frame = scipy.array(RLCFS_frame)
ZLCFS_frame = scipy.array(ZLCFS_frame)
RLCFS_frame.append(numpy.nan)
ZLCFS_frame.append(numpy.nan)
RLCFS_frame = numpy.array(RLCFS_frame)
ZLCFS_frame = numpy.array(ZLCFS_frame)

# generate masking array to vessel
if mask:
maskarr = scipy.array([False for i in range(len(RLCFS_frame))])
maskarr = numpy.array([False for i in range(len(RLCFS_frame))])
for i, x in enumerate(RLCFS_frame):
y = ZLCFS_frame[i]
maskarr[i] = inPolygon(Rlim, Zlim, x, y)
Expand All @@ -644,8 +644,8 @@ def remapLCFS(self, mask=False):
RLCFS_stores.append(RLCFS_frame)
ZLCFS_stores.append(ZLCFS_frame)

RLCFS = scipy.zeros((nt, maxlen))
ZLCFS = scipy.zeros((nt, maxlen))
RLCFS = numpy.zeros((nt, maxlen))
ZLCFS = numpy.zeros((nt, maxlen))
for i in range(nt):
RLCFS_frame = RLCFS_stores[i]
ZLCFS_frame = ZLCFS_stores[i]
Expand Down Expand Up @@ -1097,9 +1097,9 @@ def getBtVac(self):
btaxvNode = self._MDSTree('Bave')
# technically Bave is the average over the volume, but for the core its a singular value
self._btaxv = btaxvNode.data[
:self._timeidxend, scipy.sum(btaxvNode.data, 0) != 0
:self._timeidxend, numpy.sum(btaxvNode.data, 0) != 0
][:, -1]
self._btaxv *= scipy.sign(self.getBCentr())
self._btaxv *= numpy.sign(self.getBCentr())
self._defaultUnits['_btaxv'] = str(btaxvNode.unit)
except (PyddError, AttributeError):
raise ValueError('data retrieval failed.')
Expand Down Expand Up @@ -1141,7 +1141,7 @@ def getIpCalc(self):
if self._IpCalc is None:
try:
IpCalcNode = self._MDSTree('IpiPSI')
self._IpCalc = scipy.squeeze(IpCalcNode.data)[
self._IpCalc = numpy.squeeze(IpCalcNode.data)[
:self._timeidxend
]
self._defaultUnits['_IpCalc'] = str(IpCalcNode.unit)
Expand Down Expand Up @@ -1425,7 +1425,7 @@ def getCurrentSign(self):
currentSign (Integer): 1 for positive-direction current, -1 for negative.
"""
if self._currentSign is None:
self._currentSign = -1 if scipy.mean(self.getIpMeas()) > 1e5 else 1
self._currentSign = -1 if numpy.mean(self.getIpMeas()) > 1e5 else 1
return self._currentSign

def getParam(self, path):
Expand Down Expand Up @@ -1460,21 +1460,21 @@ def getSSQ(self, inp, **kwargs):
# create a dict mapping the various quantities to positions in the in the data array
self._SSQname = SSQnameNode.data
try:
self._SSQname = scipy.char.strip(
self._SSQname = numpy.char.strip(
SSQnameNode.data.view(
'S' + str(SSQnameNode.data.shape[1])
)
) # concatenate and strip blanks
except ValueError:
self._SSQname = scipy.char.strip(
self._SSQname = numpy.char.strip(
SSQnameNode.data.T.view(
'S' + str(SSQnameNode.data.shape[0])
)
) # concatenate and strip blanks

self._SSQname = self._SSQname[self._SSQname != ''] # remove empty entries
self._SSQname = dict(
zip(self._SSQname, scipy.arange(self._SSQname.shape[0]))
zip(self._SSQname, numpy.arange(self._SSQname.shape[0]))
) # zip the dict together

self._SSQ = self._MDSTree('SSQ').data
Expand Down Expand Up @@ -1536,7 +1536,7 @@ def rz2BR(
`Z` or be a scalar. Default is True (evaluate ALL `R`, `Z` at
EACH element in `t`).
make_grid (Boolean): Set to True to pass `R` and `Z` through
:py:func:`scipy.meshgrid` before evaluating. If this is set to
:py:func:`numpy.meshgrid` before evaluating. If this is set to
True, `R` and `Z` must each only have a single dimension, but
can have different lengths. Default is False (do not form
meshgrid).
Expand Down Expand Up @@ -1569,7 +1569,7 @@ def rz2BR(

* **BR** (`Array or scalar float`) - The major radial component of
the magnetic field. If all of the input arguments are scalar, then
a scalar is returned. Otherwise, a scipy Array is returned. If `R`
a scalar is returned. Otherwise, a numpy Array is returned. If `R`
and `Z` both have the same shape then `BR` has this shape as well,
unless the `make_grid` keyword was True, in which case `BR` has
shape (len(`Z`), len(`R`)).
Expand Down Expand Up @@ -1609,7 +1609,7 @@ def rz2BR(
return super(AUGDDData, self).rz2BR(
R, Z, t, return_t=return_t, make_grid=make_grid, each_t=each_t,
length_unit=length_unit
) / (2 * scipy.pi)
) / (2 * numpy.pi)

def rz2BZ(
self, R, Z, t, return_t=False, make_grid=False, each_t=True,
Expand Down Expand Up @@ -1650,7 +1650,7 @@ def rz2BZ(
`Z` or be a scalar. Default is True (evaluate ALL `R`, `Z` at
EACH element in `t`).
make_grid (Boolean): Set to True to pass `R` and `Z` through
:py:func:`scipy.meshgrid` before evaluating. If this is set to
:py:func:`numpy.meshgrid` before evaluating. If this is set to
True, `R` and `Z` must each only have a single dimension, but
can have different lengths. Default is False (do not form
meshgrid).
Expand Down Expand Up @@ -1683,7 +1683,7 @@ def rz2BZ(

* **BZ** (`Array or scalar float`) - The vertical component of the
magnetic field. If all of the input arguments are scalar, then a
scalar is returned. Otherwise, a scipy Array is returned. If `R`
scalar is returned. Otherwise, a numpy Array is returned. If `R`
and `Z` both have the same shape then `BZ` has this shape as well,
unless the `make_grid` keyword was True, in which case `BZ` has
shape (len(`Z`), len(`R`)).
Expand Down Expand Up @@ -1720,7 +1720,7 @@ def rz2BZ(

BZ_mat = Eq_instance.rz2BZ(R, Z, 0.2, make_grid=True)
"""
return super(AUGDDData, self).rz2BZ(R, Z, t, return_t=return_t, make_grid=make_grid, each_t=each_t, length_unit=length_unit)/(2*scipy.pi)
return super(AUGDDData, self).rz2BZ(R, Z, t, return_t=return_t, make_grid=make_grid, each_t=each_t, length_unit=length_unit)/(2*numpy.pi)


class YGCAUGInterface(object):
Expand Down Expand Up @@ -1812,13 +1812,13 @@ class YGCAUGInterface(object):
}

# ONLY CERTAIN YGC FILES EXIST I MEAN CMON ITS NOT THAT MUCH MEMORY
_ygc_shotfiles = scipy.array([0, 948, 8650, 9401, 12751, 14051, 14601, 16315, 18204, 19551, 21485, 25891, 30136])
_ygc_shotfiles = numpy.array([0, 948, 8650, 9401, 12751, 14051, 14601, 16315, 18204, 19551, 21485, 25891, 30136])

def _getData(self, shot):
try:

self._ygc_shot = self._ygc_shotfiles[
scipy.searchsorted(self._ygc_shotfiles, [shot], 'right') - 1
numpy.searchsorted(self._ygc_shotfiles, [shot], 'right') - 1
][0] # find nearest shotfile which is the before it

if self._ygc_shot < 8650:
Expand Down Expand Up @@ -1900,8 +1900,8 @@ def getMachineCrossSectionFull(self, shot):
x.append(None)
y.append(None)

x = scipy.array(x[:-1])
y = scipy.array(y[:-1])
x = numpy.array(x[:-1])
y = numpy.array(y[:-1])
return (x, y)


Expand Down
6 changes: 3 additions & 3 deletions eqtools/CModEFIT.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
working with C-Mod EFIT data.
"""

import scipy
import numpy as np

from .EFIT import EFITTree
from .core import PropertyAccessMixin, ModuleWarning
Expand Down Expand Up @@ -367,8 +367,8 @@ def getMachineCrossSectionFull(self):
x.append(None)
y.append(None)

x = scipy.array(x)
y = scipy.array(y)
x = np.array(x)
y = np.array(y)
return (x, y)

def getRCentr(self, length_unit=1):
Expand Down
7 changes: 4 additions & 3 deletions eqtools/EFIT.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
with EFIT data.
"""

import numpy
import scipy
from collections import namedtuple

Expand Down Expand Up @@ -601,12 +602,12 @@ def remapLCFS(self, mask=False):
ZLCFS_frame.extend(v[:, 1])
RLCFS_frame.append(scipy.nan)
ZLCFS_frame.append(scipy.nan)
RLCFS_frame = scipy.array(RLCFS_frame)
ZLCFS_frame = scipy.array(ZLCFS_frame)
RLCFS_frame = numpy.array(RLCFS_frame)
ZLCFS_frame = numpy.array(ZLCFS_frame)

# generate masking array to vessel
if mask:
maskarr = scipy.array([False for i in range(len(RLCFS_frame))])
maskarr = numpy.array([False for i in range(len(RLCFS_frame))])
for i, x in enumerate(RLCFS_frame):
y = ZLCFS_frame[i]
maskarr[i] = inPolygon(Rlim, Zlim, x, y)
Expand Down
24 changes: 12 additions & 12 deletions eqtools/FromArrays.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

from .core import Equilibrium

import scipy
import numpy


class ArrayEquilibrium(Equilibrium):
Expand Down Expand Up @@ -91,17 +91,17 @@ class ArrayEquilibrium(Equilibrium):
"""
def __init__(self, psiRZ, rGrid, zGrid, time, q, fluxVol, psiLCFS, psiAxis,
rmag, zmag, Rout, **kwargs):
self._psiRZ = scipy.asarray(psiRZ, dtype=float)
self._rGrid = scipy.asarray(rGrid, dtype=float)
self._zGrid = scipy.asarray(zGrid, dtype=float)
self._time = scipy.asarray(time, dtype=float)
self._qpsi = scipy.asarray(q, dtype=float)
self._fluxVol = scipy.asarray(fluxVol, dtype=float)
self._psiLCFS = scipy.asarray(psiLCFS, dtype=float)
self._psiAxis = scipy.asarray(psiAxis, dtype=float)
self._rmag = scipy.asarray(rmag, dtype=float)
self._zmag = scipy.asarray(zmag, dtype=float)
self._RmidLCFS = scipy.asarray(Rout, dtype=float)
self._psiRZ = numpy.asarray(psiRZ, dtype=float)
self._rGrid = numpy.asarray(rGrid, dtype=float)
self._zGrid = numpy.asarray(zGrid, dtype=float)
self._time = numpy.asarray(time, dtype=float)
self._qpsi = numpy.asarray(q, dtype=float)
self._fluxVol = numpy.asarray(fluxVol, dtype=float)
self._psiLCFS = numpy.asarray(psiLCFS, dtype=float)
self._psiAxis = numpy.asarray(psiAxis, dtype=float)
self._rmag = numpy.asarray(rmag, dtype=float)
self._zmag = numpy.asarray(zmag, dtype=float)
self._RmidLCFS = numpy.asarray(Rout, dtype=float)

self._defaultUnits = {}
self._defaultUnits['_psiRZ'] = 'Wb/rad'
Expand Down
4 changes: 2 additions & 2 deletions eqtools/NSTXEFIT.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
working with NSTX EFIT data.
"""

import scipy
import numpy

from .EFIT import EFITTree
from .core import PropertyAccessMixin, ModuleWarning
Expand Down Expand Up @@ -215,7 +215,7 @@ def getRmidPsi(self, length_unit=1):
self._defaultUnits['_RmidPsi'], length_unit
)
else:
unit_factor = scipy.array([1.])
unit_factor = numpy.array([1.])

with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=RuntimeWarning)
Expand Down
Loading