fargopy 0.4.0__py3-none-any.whl → 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fargopy/__init__.py +8 -346
- fargopy/base.py +377 -0
- fargopy/bin/ifargopy +91 -0
- fargopy/bin/vfargopy +2111 -0
- fargopy/data/fargopy_logo.png +0 -0
- fargopy/fields.py +1400 -415
- fargopy/flux.py +809 -723
- fargopy/plot.py +553 -8
- fargopy/simulation.py +1548 -577
- fargopy/sys.py +116 -65
- fargopy/tests/test_base.py +8 -0
- fargopy/tests/test_flux.py +76 -0
- fargopy/tests/test_interp.py +132 -0
- fargopy-1.0.1.data/scripts/ifargopy +91 -0
- fargopy-1.0.1.data/scripts/vfargopy +2111 -0
- fargopy-1.0.1.dist-info/METADATA +450 -0
- fargopy-1.0.1.dist-info/RECORD +21 -0
- {fargopy-0.4.0.dist-info → fargopy-1.0.1.dist-info}/WHEEL +1 -1
- fargopy-1.0.1.dist-info/licenses/LICENSE +661 -0
- fargopy/fsimulation.py +0 -429
- fargopy/tests/test___init__.py +0 -0
- fargopy/util.py +0 -21
- fargopy/version.py +0 -1
- fargopy-0.4.0.data/scripts/ifargopy +0 -15
- fargopy-0.4.0.dist-info/METADATA +0 -492
- fargopy-0.4.0.dist-info/RECORD +0 -17
- fargopy-0.4.0.dist-info/licenses/LICENSE +0 -21
- {fargopy-0.4.0.dist-info → fargopy-1.0.1.dist-info}/entry_points.txt +0 -0
- {fargopy-0.4.0.dist-info → fargopy-1.0.1.dist-info}/top_level.txt +0 -0
fargopy/sys.py
CHANGED
|
@@ -17,52 +17,67 @@ from select import select
|
|
|
17
17
|
# Classes
|
|
18
18
|
###############################################################
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
# /////////////////////////////////////
|
|
21
22
|
# UTIL CLASS
|
|
22
|
-
|
|
23
|
+
# /////////////////////////////////////
|
|
23
24
|
class Sys(object):
|
|
25
|
+
"""System utilities for command execution, directory locking, and resource monitoring.
|
|
26
|
+
|
|
27
|
+
The ``Sys`` class provides a collection of static methods to interact with
|
|
28
|
+
the operating system. It handles shell command execution with output capturing,
|
|
29
|
+
directory locking mechanisms for concurrent safety, and system pause functionalities.
|
|
30
|
+
"""
|
|
24
31
|
|
|
25
32
|
QERROR = True
|
|
26
|
-
STDERR =
|
|
27
|
-
STDOUT =
|
|
28
|
-
OUT =
|
|
33
|
+
STDERR = ""
|
|
34
|
+
STDOUT = ""
|
|
35
|
+
OUT = ""
|
|
29
36
|
|
|
30
37
|
@staticmethod
|
|
31
|
-
def run(cmd,quiet=True):
|
|
32
|
-
"""Run a system command
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
def run(cmd, quiet=True):
|
|
39
|
+
"""Run a system shell command.
|
|
40
|
+
|
|
41
|
+
Executes a command string in the shell and captures its stdout and stderr.
|
|
42
|
+
Useful for running FARGO3D executables or other system tools.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
cmd : str
|
|
47
|
+
The command string to execute.
|
|
48
|
+
quiet : bool, optional
|
|
49
|
+
If True, suppress printing output to stdout (default: True).
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
tuple
|
|
54
|
+
(error_code, output_list)
|
|
55
|
+
- error_code (int): 0 for success, non-zero for error.
|
|
56
|
+
- output_list (list): List of output lines (strings).
|
|
57
|
+
|
|
58
|
+
Examples
|
|
59
|
+
--------
|
|
60
|
+
Run a simple shell command:
|
|
61
|
+
|
|
62
|
+
>>> err, out = fp.Sys.run("ls -l")
|
|
48
63
|
"""
|
|
49
64
|
fargopy.Debug.trace(f"sysrun::cmd = {cmd}")
|
|
50
65
|
|
|
51
|
-
out=[]
|
|
66
|
+
out = []
|
|
52
67
|
for path in Sys._run(cmd):
|
|
53
68
|
try:
|
|
54
69
|
if not quiet:
|
|
55
|
-
print(path.decode(
|
|
56
|
-
out += [path.decode(
|
|
70
|
+
print(path.decode("utf-8"))
|
|
71
|
+
out += [path.decode("utf-8")]
|
|
57
72
|
except:
|
|
58
|
-
out += [(path[0],path[1].decode(
|
|
59
|
-
|
|
60
|
-
Sys.STDOUT =
|
|
61
|
-
if len(out)>1:
|
|
62
|
-
Sys.STDOUT =
|
|
63
|
-
|
|
73
|
+
out += [(path[0], path[1].decode("utf-8"))]
|
|
74
|
+
|
|
75
|
+
Sys.STDOUT = ""
|
|
76
|
+
if len(out) > 1:
|
|
77
|
+
Sys.STDOUT = "\n".join(out[:-1])
|
|
78
|
+
|
|
64
79
|
Sys.STDERR = out[-1][1]
|
|
65
|
-
if len(Sys.STDERR)>0:
|
|
80
|
+
if len(Sys.STDERR) > 0:
|
|
66
81
|
Sys.QERROR = out[-1][0]
|
|
67
82
|
if Sys.QERROR == 0:
|
|
68
83
|
Sys.QERROR = -1
|
|
@@ -74,30 +89,36 @@ class Sys(object):
|
|
|
74
89
|
|
|
75
90
|
if fargopy.Debug.VERBOSE:
|
|
76
91
|
error = out[-1][0]
|
|
77
|
-
if Sys.QERROR>0:
|
|
92
|
+
if Sys.QERROR > 0:
|
|
78
93
|
fargopy.Debug.trace(f"sysrun::Error check Sys.STDERR.")
|
|
79
|
-
elif Sys.QERROR<0:
|
|
80
|
-
fargopy.Debug.trace(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
elif Sys.QERROR < 0:
|
|
95
|
+
fargopy.Debug.trace(
|
|
96
|
+
f"sysrun::Done. Still some issues must be check. Check Sys.STDOUT and Sys.STDERR for details."
|
|
97
|
+
)
|
|
98
|
+
elif Sys.QERROR == 0:
|
|
99
|
+
fargopy.Debug.trace(
|
|
100
|
+
f"sysrun::Done. You're great. Check Sys.STDOUT for details."
|
|
101
|
+
)
|
|
102
|
+
|
|
84
103
|
Sys.OUT = out
|
|
85
|
-
return Sys.QERROR,out
|
|
104
|
+
return Sys.QERROR, out
|
|
86
105
|
|
|
87
106
|
@staticmethod
|
|
88
107
|
def _run(cmd):
|
|
89
|
-
p=subprocess.Popen(
|
|
108
|
+
p = subprocess.Popen(
|
|
109
|
+
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
|
|
110
|
+
)
|
|
90
111
|
while True:
|
|
91
112
|
line = p.stdout.readline().rstrip()
|
|
92
113
|
if not line:
|
|
93
114
|
break
|
|
94
115
|
yield line
|
|
95
|
-
(output,error)=p.communicate()
|
|
96
|
-
yield p.returncode,error
|
|
116
|
+
(output, error) = p.communicate()
|
|
117
|
+
yield p.returncode, error
|
|
97
118
|
|
|
98
119
|
@staticmethod
|
|
99
120
|
def simple(cmd):
|
|
100
|
-
return
|
|
121
|
+
return subprocess.call(cmd, shell=True)
|
|
101
122
|
|
|
102
123
|
@staticmethod
|
|
103
124
|
def get_memory():
|
|
@@ -105,21 +126,38 @@ class Sys(object):
|
|
|
105
126
|
return svmem
|
|
106
127
|
|
|
107
128
|
@staticmethod
|
|
108
|
-
def lock(dir,content=dict()):
|
|
109
|
-
"""
|
|
129
|
+
def lock(dir, content=dict()):
|
|
130
|
+
"""Create a lock file in a directory.
|
|
131
|
+
|
|
132
|
+
Creates a ``fargopy.lock`` file containing the provided content to
|
|
133
|
+
signal that the directory is in use or processing.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
dir : str
|
|
138
|
+
Path to the directory to lock.
|
|
139
|
+
content : dict, optional
|
|
140
|
+
Dictionary of metadata to store in the lock file.
|
|
110
141
|
"""
|
|
111
142
|
if not os.path.isdir(dir):
|
|
112
143
|
print(f"Locking directory '{dir}' not found.")
|
|
113
144
|
return
|
|
114
|
-
|
|
145
|
+
|
|
115
146
|
filename = f"{dir}/fargopy.lock"
|
|
116
|
-
with open(filename,
|
|
117
|
-
file_object.write(
|
|
147
|
+
with open(filename, "w") as file_object:
|
|
148
|
+
file_object.write(
|
|
149
|
+
json.dumps(content, default=lambda obj: "<not serializable>")
|
|
150
|
+
)
|
|
118
151
|
file_object.close()
|
|
119
152
|
|
|
120
153
|
@staticmethod
|
|
121
154
|
def unlock(dir):
|
|
122
|
-
"""
|
|
155
|
+
"""Remove the lock file from a directory.
|
|
156
|
+
|
|
157
|
+
Parameters
|
|
158
|
+
----------
|
|
159
|
+
dir : str
|
|
160
|
+
Path to the directory to unlock.
|
|
123
161
|
"""
|
|
124
162
|
if not os.path.isdir(dir):
|
|
125
163
|
print(f"Locking directory '{dir}' not found.")
|
|
@@ -127,9 +165,9 @@ class Sys(object):
|
|
|
127
165
|
filename = f"{dir}/fargopy.lock"
|
|
128
166
|
if os.path.isfile(filename):
|
|
129
167
|
fargopy.Sys.simple(f"rm -rf {filename}")
|
|
130
|
-
|
|
168
|
+
|
|
131
169
|
@staticmethod
|
|
132
|
-
def is_locked(dir,verbose=False):
|
|
170
|
+
def is_locked(dir, verbose=False):
|
|
133
171
|
if not os.path.isdir(dir):
|
|
134
172
|
if verbose:
|
|
135
173
|
print(f"Locking directory '{dir}' not found.")
|
|
@@ -141,21 +179,34 @@ class Sys(object):
|
|
|
141
179
|
with open(filename) as file_handler:
|
|
142
180
|
info = json.load(file_handler)
|
|
143
181
|
return info
|
|
144
|
-
|
|
145
|
-
@staticmethod
|
|
146
|
-
def sleep_timeout(timeout=5,msg=None):
|
|
147
|
-
"""This routine sleeps for a 'time'. In the meanwhile checks if there is a keyboard interrupt
|
|
148
|
-
(Enter or Ctrl+C) and interrupt sleeping
|
|
149
182
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
183
|
+
@staticmethod
|
|
184
|
+
def sleep_timeout(timeout=5, msg=None):
|
|
185
|
+
"""Sleep for a specified duration, interruptible by user input.
|
|
186
|
+
|
|
187
|
+
Sleeps for ``timeout`` seconds. Checks for keyboard interrupt (Enter or Ctrl+C)
|
|
188
|
+
during the sleep period.
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
timeout : int, optional
|
|
193
|
+
Sleep duration in seconds (default: 5).
|
|
194
|
+
msg : str, optional
|
|
195
|
+
Message to display before sleeping.
|
|
196
|
+
|
|
197
|
+
Returns
|
|
198
|
+
-------
|
|
199
|
+
bool
|
|
200
|
+
True if interrupted, False if timeout completed.
|
|
201
|
+
|
|
202
|
+
Examples
|
|
203
|
+
--------
|
|
204
|
+
Sleep for 10 seconds or until user interrupt:
|
|
205
|
+
|
|
206
|
+
>>> if fp.Sys.sleep_timeout(10, "Press Enter to skip"):
|
|
207
|
+
... print("Skipped")
|
|
157
208
|
"""
|
|
158
|
-
try:
|
|
209
|
+
try:
|
|
159
210
|
if msg:
|
|
160
211
|
print(msg)
|
|
161
212
|
rlist, wlist, xlist = select([sys.stdin], [], [], timeout)
|
|
@@ -165,4 +216,4 @@ class Sys(object):
|
|
|
165
216
|
return False
|
|
166
217
|
except KeyboardInterrupt:
|
|
167
218
|
print("Interrupting")
|
|
168
|
-
return True
|
|
219
|
+
return True
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pytest
|
|
4
|
+
import fargopy as fp
|
|
5
|
+
|
|
6
|
+
FILE = __file__
|
|
7
|
+
ROOTDIR = os.path.abspath(os.path.dirname(FILE))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture(scope="session")
|
|
11
|
+
def sim():
|
|
12
|
+
# Load the local test simulation only once per test session
|
|
13
|
+
fp.Simulation.download_precomputed("p3disoj")
|
|
14
|
+
return fp.Simulation(output_dir=f"/tmp/p3disoj")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_sphere_tessellation_properties():
|
|
18
|
+
# basic sanity checks on sphere tessellation
|
|
19
|
+
s = fp.flux.Surface(type="sphere", radius=1.0, subdivisions=1)
|
|
20
|
+
# number of triangles for 1 subdivision: 20*(4**1) = 80
|
|
21
|
+
assert s.num_triangles == 20 * (4**1)
|
|
22
|
+
assert s.centers.shape == (s.num_triangles, 3)
|
|
23
|
+
assert s.normals.shape == (s.num_triangles, 3)
|
|
24
|
+
assert s.areas.shape == (s.num_triangles,)
|
|
25
|
+
# areas positive
|
|
26
|
+
assert np.all(s.areas > 0)
|
|
27
|
+
# normals are unit length
|
|
28
|
+
norms = np.linalg.norm(s.normals, axis=1)
|
|
29
|
+
assert np.allclose(norms, 1.0, atol=1e-6)
|
|
30
|
+
# total area approximates sphere area 4*pi*r^2
|
|
31
|
+
total_area = np.sum(s.areas)
|
|
32
|
+
assert np.isfinite(total_area)
|
|
33
|
+
assert np.isclose(total_area, 4 * np.pi * s.radius**2, rtol=0.12)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_plane_tessellation_and_areas():
|
|
37
|
+
# plane tessellation with subdivisions should yield correct area sum
|
|
38
|
+
s = fp.flux.Surface(type="plane", radius=1.0, subdivisions=4, width=2.0, length=3.0)
|
|
39
|
+
assert s.centers.shape[0] == s.subdivisions**2
|
|
40
|
+
assert np.all(s.areas > 0)
|
|
41
|
+
total_area = np.sum(s.areas)
|
|
42
|
+
assert np.isclose(total_area, 2.0 * 3.0, rtol=1e-8)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_cylinder_tessellation_basic():
|
|
46
|
+
# cylinder tessellation exposes top/bottom/lateral arrays
|
|
47
|
+
s = fp.flux.Surface(type="cylinder", radius=0.5, height=1.0, subdivisions=8)
|
|
48
|
+
# ensure expected attributes exist and have consistent sizes
|
|
49
|
+
assert hasattr(s, "top_centers")
|
|
50
|
+
assert hasattr(s, "bottom_centers")
|
|
51
|
+
assert hasattr(s, "lateral_centers")
|
|
52
|
+
assert s.top_centers.shape[0] == s.bottom_centers.shape[0]
|
|
53
|
+
assert s.lateral_centers.shape[0] > 0
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# FAILED
|
|
57
|
+
def test_total_mass_grid_integration(sim):
|
|
58
|
+
# Use the test simulation to run total_mass on a small sphere
|
|
59
|
+
s = fp.flux.Surface(
|
|
60
|
+
type="sphere", radius=0.1, subdivisions=0, center=(0.0, 0.0, 0.0)
|
|
61
|
+
)
|
|
62
|
+
# compute mass for a single snapshot (should be finite and non-negative)
|
|
63
|
+
mass = s.total_mass(sim, field="gasdens", snapshot=1, follow_planet=False)
|
|
64
|
+
assert np.isfinite(mass)
|
|
65
|
+
assert mass >= 0.0
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_total_mass_multiple_snapshots_returns_array(sim):
|
|
69
|
+
s = fp.flux.Surface(
|
|
70
|
+
type="sphere", radius=0.1, subdivisions=0, center=(0.0, 0.0, 0.0)
|
|
71
|
+
)
|
|
72
|
+
masses = s.total_mass(sim, field="gasdens", snapshot=[1, 2], follow_planet=False)
|
|
73
|
+
assert isinstance(masses, np.ndarray)
|
|
74
|
+
assert masses.shape[0] == 2
|
|
75
|
+
assert np.all(np.isfinite(masses))
|
|
76
|
+
assert np.all(masses >= 0.0)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# fargopy/tests/test___interp.py
|
|
2
|
+
import os
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pytest
|
|
5
|
+
import fargopy as fp
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.fixture(scope="session")
|
|
9
|
+
def sim():
|
|
10
|
+
# Load the local test simulation only once per test session
|
|
11
|
+
fp.Simulation.download_precomputed("p3disoj")
|
|
12
|
+
return fp.Simulation(output_dir=f"/tmp/p3disoj")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_interpolacion_1d_point(sim):
|
|
16
|
+
data = sim.load_field(
|
|
17
|
+
fields="gasdens",
|
|
18
|
+
slice="phi=0,theta=1.56",
|
|
19
|
+
snapshot=(1, 2),
|
|
20
|
+
interpolate=True,
|
|
21
|
+
)
|
|
22
|
+
x = 1.2
|
|
23
|
+
valor = data.evaluate(1.2, var1=x)
|
|
24
|
+
# If it returns a 0-d array or a scalar, both are acceptable; it must be finite
|
|
25
|
+
assert np.isfinite(valor).all(), "1D interpolation must return a finite value"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_interpolacion_1d_array(sim):
|
|
29
|
+
data = sim.load_field(
|
|
30
|
+
fields="gasdens",
|
|
31
|
+
slice="phi=0,theta=1.56",
|
|
32
|
+
snapshot=(1, 2),
|
|
33
|
+
interpolate=True,
|
|
34
|
+
)
|
|
35
|
+
x = np.array([1.2, 1.3, 1.4])
|
|
36
|
+
valor = data.evaluate(1.2, var1=x)
|
|
37
|
+
assert np.asarray(valor).shape == x.shape, (
|
|
38
|
+
"1D interpolation must preserve the input shape"
|
|
39
|
+
)
|
|
40
|
+
assert np.isfinite(valor).any(), (
|
|
41
|
+
"At least some points should be finite if they lie inside the domain"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_interpolacion_2d_point(sim):
|
|
46
|
+
data = sim.load_field(
|
|
47
|
+
fields="gasdens",
|
|
48
|
+
slice="theta=1.56",
|
|
49
|
+
snapshot=(1, 2),
|
|
50
|
+
interpolate=True,
|
|
51
|
+
)
|
|
52
|
+
# Conservative point (typically inside the domain)
|
|
53
|
+
x = 1.2
|
|
54
|
+
y = 0.14
|
|
55
|
+
valor = data.evaluate(
|
|
56
|
+
1.2, var1=x, var2=y, interpolator="griddata", method="nearest"
|
|
57
|
+
)
|
|
58
|
+
assert np.isscalar(valor) or np.asarray(valor).shape == (), (
|
|
59
|
+
"2D point interpolation must return a scalar/0-d value"
|
|
60
|
+
)
|
|
61
|
+
assert np.isfinite(valor), (
|
|
62
|
+
"2D point interpolation must return a finite value (nearest)"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_interpolacion_2d_array(sim):
|
|
67
|
+
data = sim.load_field(
|
|
68
|
+
fields="gasdens",
|
|
69
|
+
slice="theta=1.56",
|
|
70
|
+
snapshot=(1, 2),
|
|
71
|
+
interpolate=True,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Your y=[1.3,1.4,1.5] values are very likely outside the domain -> NaNs (linear) or spurious values.
|
|
75
|
+
# Here we use "safe" points and, additionally, nearest-neighbor interpolation to avoid NaNs
|
|
76
|
+
# due to the convex hull limitation.
|
|
77
|
+
x = np.array([0.9, 1.0, 1.1])
|
|
78
|
+
y = np.array([0.05, 0.10, 0.15])
|
|
79
|
+
|
|
80
|
+
valor = data.evaluate(
|
|
81
|
+
1.2, var1=x, var2=y, interpolator="griddata", method="nearest"
|
|
82
|
+
)
|
|
83
|
+
valor = np.asarray(valor)
|
|
84
|
+
|
|
85
|
+
assert valor.shape == x.shape, (
|
|
86
|
+
"2D interpolation must return an array with the same shape as the input"
|
|
87
|
+
)
|
|
88
|
+
assert np.isfinite(valor).all(), (
|
|
89
|
+
"With nearest and in-domain points, NaNs should not appear"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# FAILING
|
|
94
|
+
def test_interpolacion_3d_point(sim):
|
|
95
|
+
data = sim.load_field(
|
|
96
|
+
fields="gasdens",
|
|
97
|
+
snapshot=(1, 2),
|
|
98
|
+
interpolate=True,
|
|
99
|
+
)
|
|
100
|
+
x, y, z = 1.2, 1.3, 1.4
|
|
101
|
+
valor = data.evaluate(
|
|
102
|
+
1.2, var1=x, var2=y, var3=z, interpolator="griddata", method="nearest"
|
|
103
|
+
)
|
|
104
|
+
assert np.isscalar(valor) or np.asarray(valor).shape == (), (
|
|
105
|
+
"3D point interpolation must return a scalar/0-d value"
|
|
106
|
+
)
|
|
107
|
+
assert np.isfinite(valor), (
|
|
108
|
+
"3D point interpolation must return a finite value (nearest)"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_interpolacion_3d_array(sim):
|
|
113
|
+
data = sim.load_field(
|
|
114
|
+
fields="gasdens",
|
|
115
|
+
snapshot=(1, 2),
|
|
116
|
+
interpolate=True,
|
|
117
|
+
)
|
|
118
|
+
x = np.array([1.2, 1.3, 1.4])
|
|
119
|
+
y = np.array([1.3, 1.4, 1.5])
|
|
120
|
+
z = np.array([0.024, 0.14, 0.2])
|
|
121
|
+
|
|
122
|
+
valor = data.evaluate(
|
|
123
|
+
1.2, var1=x, var2=y, var3=z, interpolator="griddata", method="nearest"
|
|
124
|
+
)
|
|
125
|
+
valor = np.asarray(valor)
|
|
126
|
+
|
|
127
|
+
assert valor.shape == x.shape, (
|
|
128
|
+
"3D interpolation must return an array with the same shape as the input"
|
|
129
|
+
)
|
|
130
|
+
assert np.isfinite(valor).all(), (
|
|
131
|
+
"With nearest, NaNs should not appear except for extremely out-of-domain points"
|
|
132
|
+
)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!python
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_version():
|
|
9
|
+
try:
|
|
10
|
+
try:
|
|
11
|
+
from importlib.metadata import version
|
|
12
|
+
return version("fargopy")
|
|
13
|
+
except ImportError:
|
|
14
|
+
# Fallback for Python < 3.8
|
|
15
|
+
import pkg_resources
|
|
16
|
+
return pkg_resources.get_distribution("fargopy").version
|
|
17
|
+
except Exception:
|
|
18
|
+
# Fallback if package not installed or in development
|
|
19
|
+
try:
|
|
20
|
+
import fargopy.version
|
|
21
|
+
return fargopy.version.version
|
|
22
|
+
except ImportError:
|
|
23
|
+
return "unknown"
|
|
24
|
+
|
|
25
|
+
def run_verify():
|
|
26
|
+
try:
|
|
27
|
+
import fargopy
|
|
28
|
+
v = get_version()
|
|
29
|
+
print(f"fargopy {v} is successfully installed.")
|
|
30
|
+
print(f"Location: {os.path.dirname(fargopy.__file__)}")
|
|
31
|
+
except ImportError:
|
|
32
|
+
print("Error: fargopy is not installed or cannot be imported.")
|
|
33
|
+
sys.exit(1)
|
|
34
|
+
|
|
35
|
+
def run_tests():
|
|
36
|
+
try:
|
|
37
|
+
import fargopy
|
|
38
|
+
package_dir = os.path.dirname(fargopy.__file__)
|
|
39
|
+
test_dir = os.path.join(package_dir, 'tests')
|
|
40
|
+
if not os.path.exists(test_dir):
|
|
41
|
+
print(f"Error: tests directory not found at {test_dir}")
|
|
42
|
+
sys.exit(1)
|
|
43
|
+
|
|
44
|
+
print(f"Running tests in {test_dir}...")
|
|
45
|
+
subprocess.check_call([sys.executable, "-m", "pytest", test_dir])
|
|
46
|
+
except ImportError:
|
|
47
|
+
print("Error: fargopy is not installed.")
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
except subprocess.CalledProcessError as e:
|
|
50
|
+
sys.exit(e.returncode)
|
|
51
|
+
|
|
52
|
+
def main():
|
|
53
|
+
parser = argparse.ArgumentParser(description="fargopy interactive shell and utilities")
|
|
54
|
+
parser.add_argument("--verify", action="store_true", help="Verify installation and show version")
|
|
55
|
+
parser.add_argument("--test", action="store_true", help="Run the distributed tests")
|
|
56
|
+
|
|
57
|
+
# If no arguments are provided, or just unknown args (which arguably should be passed to ipython?
|
|
58
|
+
# but for now let's stick to the requested logic: optional args for cmd, else launch ipython)
|
|
59
|
+
# The issue is that argparse will parse all args. If the user wants to pass args to ipython, this might conflict.
|
|
60
|
+
# However, standard pattern is usually wrapper handles its flags, pass others.
|
|
61
|
+
# But ifargopy historically just wraps ipython.
|
|
62
|
+
|
|
63
|
+
# Let's check if we have specific flags.
|
|
64
|
+
if "--verify" in sys.argv:
|
|
65
|
+
run_verify()
|
|
66
|
+
return
|
|
67
|
+
if "--test" in sys.argv:
|
|
68
|
+
run_tests()
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
# Original behavior: launch IPython
|
|
72
|
+
user_home = os.path.expanduser("~")
|
|
73
|
+
fp_dotdir = os.path.join(user_home, ".fargopy")
|
|
74
|
+
|
|
75
|
+
if not os.path.isdir(fp_dotdir):
|
|
76
|
+
# First time initialization logic
|
|
77
|
+
init_script = "/tmp/ifargopy_initialize.py"
|
|
78
|
+
with open(init_script, "w") as f:
|
|
79
|
+
f.write("import fargopy as fp\n")
|
|
80
|
+
f.write("fp.initialize('configure')\n")
|
|
81
|
+
f.write("print('We have configured fargopy for the first time. Run it again.')\n")
|
|
82
|
+
|
|
83
|
+
subprocess.call(["ipython", "-i", init_script])
|
|
84
|
+
else:
|
|
85
|
+
startup_script = os.path.join(fp_dotdir, "ifargopy.py")
|
|
86
|
+
# Pass all arguments to ipython except the script name itself
|
|
87
|
+
cmd = ["ipython", "-i", startup_script] + sys.argv[1:]
|
|
88
|
+
subprocess.call(cmd)
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
main()
|