mapflpy 1.1.9__tar.gz → 1.2.0__tar.gz
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.
- {mapflpy-1.1.9 → mapflpy-1.2.0}/PKG-INFO +1 -1
- {mapflpy-1.1.9 → mapflpy-1.2.0}/environment.yml +1 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/mapflpy/globals.py +33 -1
- {mapflpy-1.1.9 → mapflpy-1.2.0}/mapflpy/scripts.py +237 -2
- {mapflpy-1.1.9 → mapflpy-1.2.0}/mapflpy/tracer.py +11 -11
- {mapflpy-1.1.9 → mapflpy-1.2.0}/mapflpy/utils.py +5 -3
- {mapflpy-1.1.9 → mapflpy-1.2.0}/meson.build +20 -2
- {mapflpy-1.1.9 → mapflpy-1.2.0}/noxfile.py +17 -1
- {mapflpy-1.1.9 → mapflpy-1.2.0}/pyproject.toml +2 -2
- {mapflpy-1.1.9 → mapflpy-1.2.0}/src/mapfl/mapfl.f +233 -45
- {mapflpy-1.1.9 → mapflpy-1.2.0}/src/mapfl/psi_io.f90 +3 -1
- {mapflpy-1.1.9 → mapflpy-1.2.0}/src/mapflpy_fortran.f90 +6 -2
- {mapflpy-1.1.9 → mapflpy-1.2.0}/.f2py_f2cmap +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/.gitattributes +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/.github/workflows/docs.yml +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/.github/workflows/publish.yml +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/.gitignore +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/.python-version +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/LICENSE.txt +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/README.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/Makefile +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/make.bat +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/_static/.gitkeep +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/_static/assets/.gitkeep +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/_templates/autosummary/attribute.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/_templates/autosummary/class.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/_templates/autosummary/class_method.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/_templates/autosummary/data.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/_templates/autosummary/exception.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/_templates/autosummary/function.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/_templates/autosummary/method.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/_templates/autosummary/module.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/_templates/autosummary/package.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/_templates/autosummary/property.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/_templates/autosummary/static_method.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/api/index.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/conf.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/guide/development.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/guide/index.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/guide/installation.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/guide/overview.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/docs/source/index.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/01_getting_started/GALLERY_HEADER.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/01_getting_started/p01_tracing_forward.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/01_getting_started/p02_tracing_backward.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/01_getting_started/p03_tracing_forward_backward.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/01_getting_started/p04_adding_a_magnetogram.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/02_using_tracer_class/GALLERY_HEADER.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/02_using_tracer_class/p01_using_defaults.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/02_using_tracer_class/p02_setting_tracing_direction.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/02_using_tracer_class/p03_adjusting_mapfl_params.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/02_using_tracer_class/p04_manipulating_field_data.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/03_using_tracermp_class/GALLERY_HEADER.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/03_using_tracermp_class/p01_using_defaults.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/03_using_tracermp_class/p02_creating_multiple_tracermp.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/03_using_tracermp_class/p03_magnetic_field.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/04_advanced_examples/GALLERY_HEADER.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/04_advanced_examples/p01_interdomain_tracing.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/04_advanced_examples/p02_coloring_by_polarity.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/04_advanced_examples/p03_filtering_fieldlines.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/examples/GALLERY_HEADER.rst +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/mapflpy/__init__.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/mapflpy/_version.py.in +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/mapflpy/data.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/mapflpy/fortran/__init__.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/mapflpy/py.typed +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/src/f2py_f2cmap_mapflpy +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/__init__.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/conftest.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/data/reference_traces.npz +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/test_module.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/test_tracer/__init__.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/test_tracer/test_class_primitives.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/test_tracer/test_fortran_calls.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/test_tracer/test_param_editing.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/test_tracer/test_trace_routines.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/test_tracermp/__init__.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/test_tracermp/test_class_primitives_.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/test_tracermp/test_fortran_calls_.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/test_tracermp/test_param_editing_.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/test_tracermp/test_trace_routines_.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tests/utils.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tools/make_build.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tools/make_docs.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tools/make_intersphinx.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tools/pyproject_version.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tools/python_version.py +0 -0
- {mapflpy-1.1.9 → mapflpy-1.2.0}/tools/python_versions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mapflpy
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Fast field line tracing for spherical vector fields
|
|
5
5
|
Keywords: solar physics,space physics,heliophysics,magnetohydrodynamic,science,mas,predsci,psi,modeling,fortran
|
|
6
6
|
Author: Ryder Davidson, Cooper Downs, Andres Reyes, Asher Pembroke
|
|
@@ -30,7 +30,7 @@ __all__ = [
|
|
|
30
30
|
# It contains the geometry of the traces, their start and end positions,
|
|
31
31
|
# and whether they were traced to a boundary.
|
|
32
32
|
#------------------------------------------------------------------------------
|
|
33
|
-
Traces = namedtuple('Traces', ['geometry', 'start_pos', 'end_pos', 'traced_to_boundary'])
|
|
33
|
+
Traces = namedtuple('Traces', ['geometry', 'start_pos', 'end_pos', 'traced_to_boundary', 'integral'])
|
|
34
34
|
Traces.__doc__ = (
|
|
35
35
|
"""Named tuple for storing magnetic fieldline trace information.
|
|
36
36
|
|
|
@@ -45,9 +45,39 @@ Traces.__doc__ = (
|
|
|
45
45
|
Array of shape (N, 3) containing the ending positions of each fieldline.
|
|
46
46
|
traced_to_boundary : ndarray
|
|
47
47
|
Boolean array of shape (N,) indicating whether each fieldline was traced to a boundary.
|
|
48
|
+
integral : ndarray
|
|
49
|
+
Array of shape (N,) containing the field aligned integral. By default the integral
|
|
50
|
+
is just the field line length, but certain mapflpy options can change this behavior,
|
|
51
|
+
currently `integrate_along_fl_`, `scalar_input_file_`, `weight_integral_by_area_`, and
|
|
52
|
+
max_along_fl_`.
|
|
48
53
|
"""
|
|
49
54
|
)
|
|
50
55
|
|
|
56
|
+
# ------------------------------------------------------------------------------
|
|
57
|
+
# Named tuple for storing mapping information produced by some of the mapping
|
|
58
|
+
# scripts in `mapflpy.scripts`
|
|
59
|
+
# ------------------------------------------------------------------------------
|
|
60
|
+
Mapping = namedtuple('Mapping', ['r', 't', 'p', 'traced_to_boundary', 'integral'])
|
|
61
|
+
Mapping.__doc__ = (
|
|
62
|
+
"""Named tuple for storing magnetic fieldline mapping information.
|
|
63
|
+
|
|
64
|
+
Attributes
|
|
65
|
+
----------
|
|
66
|
+
r : ndarray
|
|
67
|
+
Array containing the radial positions of the mapping endpoints [R_sun]).
|
|
68
|
+
t : ndarray
|
|
69
|
+
Array containing the theta (co-latitude) positions of the mapping endpoints [radians].
|
|
70
|
+
p : ndarray
|
|
71
|
+
Array containing the phi (longitude) positions of the mapping endpoints [radians].
|
|
72
|
+
traced_to_boundary : ndarray
|
|
73
|
+
Boolean array indicating whether each fieldline was traced to a boundary.
|
|
74
|
+
integral : ndarray
|
|
75
|
+
Array containing the values of the field line integral. By default the integral
|
|
76
|
+
is just the field line length, but certain mapflpy options can change this behavior,
|
|
77
|
+
currently `integrate_along_fl_`, `scalar_input_file_`, `weight_integral_by_area_`, and
|
|
78
|
+
`max_along_fl_`.
|
|
79
|
+
"""
|
|
80
|
+
)
|
|
51
81
|
|
|
52
82
|
# ------------------------------------------------------------------------------
|
|
53
83
|
# Type aliases for improved code readability.
|
|
@@ -182,6 +212,8 @@ DEFAULT_FIELDS = MappingProxyType({
|
|
|
182
212
|
DEFAULT_PARAMS = MappingProxyType({
|
|
183
213
|
'integrate_along_fl_': False,
|
|
184
214
|
'scalar_input_file_': '',
|
|
215
|
+
'weight_integral_by_area_': False,
|
|
216
|
+
'max_along_fl_': False,
|
|
185
217
|
'verbose_': False,
|
|
186
218
|
'cubic_': False,
|
|
187
219
|
'trace_fwd_': False,
|
|
@@ -20,15 +20,16 @@ concatenation.
|
|
|
20
20
|
|
|
21
21
|
"""
|
|
22
22
|
from __future__ import annotations
|
|
23
|
+
import concurrent.futures
|
|
23
24
|
import copy
|
|
24
25
|
from contextlib import ExitStack
|
|
25
26
|
from functools import partial
|
|
26
|
-
from typing import Optional, Iterable, Tuple
|
|
27
|
+
from typing import Optional, Iterable, Tuple, Callable
|
|
27
28
|
|
|
28
29
|
import numpy as np
|
|
29
30
|
from numpy.typing import NDArray, ArrayLike
|
|
30
31
|
|
|
31
|
-
from mapflpy.globals import DEFAULT_BUFFER_SIZE, Traces, PathType, DirectionType
|
|
32
|
+
from mapflpy.globals import DEFAULT_BUFFER_SIZE, Traces, Mapping, PathType, DirectionType, ContextType
|
|
32
33
|
from mapflpy.tracer import TracerMP
|
|
33
34
|
from mapflpy.utils import shift_phi_traces, shift_phi_lps, fetch_default_launch_points
|
|
34
35
|
|
|
@@ -180,6 +181,240 @@ def run_fwdbwd_tracing(br: PathType,
|
|
|
180
181
|
return tracer.trace_fbwd(launch_points, buffer_size)
|
|
181
182
|
|
|
182
183
|
|
|
184
|
+
def map_field_lines_in_parallel(trace_function: Callable,
|
|
185
|
+
r_in: ArrayLike,
|
|
186
|
+
t_in: ArrayLike,
|
|
187
|
+
p_in: ArrayLike,
|
|
188
|
+
nproc: int = 4):
|
|
189
|
+
"""Map field lines from r, t, p positions in parallel using a given tracing script.
|
|
190
|
+
|
|
191
|
+
The `trace_function` is a callable function that wraps the desired mapflpy script that uses TracerMP in
|
|
192
|
+
the background. It must only accept one argument (`launch_points`) and otherwise wraps a tracing
|
|
193
|
+
script that is defined with any specific keywords that determine how the trace is done.
|
|
194
|
+
Currently only :func:`run_forward_tracing`, :func:`run_backward_tracing` make sense to wrap here.
|
|
195
|
+
|
|
196
|
+
The r,t,p input arrays can be any shape (1D, 2D, 3D, etc.) as long as it is consistent.
|
|
197
|
+
|
|
198
|
+
.. warning::
|
|
199
|
+
The wrapped trace function should specify the `buffer_size` keyword to a small number (e.g. 3)
|
|
200
|
+
since the trace geometry is not used anyway. This is essential for keeping the memory footprint
|
|
201
|
+
small and for the mapping to compute as quickly as possible.
|
|
202
|
+
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
trace_function : Callable
|
|
206
|
+
A function that wraps the mapflpy forward or backwards tracing script to call in parallel
|
|
207
|
+
(:func:`run_forward_tracing`, :func:`run_backward_tracing`) and sets the desired parameters to
|
|
208
|
+
run the wrapped script with.
|
|
209
|
+
Only the endpoints are used, so only one of forward or backwards mapping makes sense.
|
|
210
|
+
r_in : ArrayLike
|
|
211
|
+
An N-Dimensional array of radial positions to map from.
|
|
212
|
+
t_in : ArrayLike
|
|
213
|
+
An N-Dimensional array of theta positions to map from (shape must match `r_in`).
|
|
214
|
+
p_in : ArrayLike
|
|
215
|
+
An N-Dimensional array of phi positions to map from (shape must match `r_in`).
|
|
216
|
+
nproc : int, optional
|
|
217
|
+
The number of processes to spawn. This should be equal to or less than the number of threads
|
|
218
|
+
that can be used on the machine.
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
mapping : :class:`~mapflpy.globals.Mapping`
|
|
223
|
+
A namedtuple containing the mapping results (:class:`mapflpy.globals.Mapping`).
|
|
224
|
+
|
|
225
|
+
See Also
|
|
226
|
+
--------
|
|
227
|
+
:func:`map_pt_forward`
|
|
228
|
+
:func:`map_pt_backward`
|
|
229
|
+
:func:`run_forward_tracing`
|
|
230
|
+
:func:`run_backward_tracing`
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
# convert the r,t,p positions to a list of launchpoints split into nproc chunks
|
|
234
|
+
r1d = r_in.ravel()
|
|
235
|
+
t1d = t_in.ravel()
|
|
236
|
+
p1d = p_in.ravel()
|
|
237
|
+
lp_all = np.stack([r1d, t1d, p1d], axis=0)
|
|
238
|
+
lp_chunks = np.array_split(lp_all, nproc, axis=1)
|
|
239
|
+
|
|
240
|
+
# run the mapping function on nprocs
|
|
241
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=nproc) as executor:
|
|
242
|
+
results = list(executor.map(trace_function, lp_chunks))
|
|
243
|
+
|
|
244
|
+
# collect the results back into one array
|
|
245
|
+
end_pos_1d = np.concatenate([result.end_pos for result in results], axis=1)
|
|
246
|
+
traced_to_boundary_1d = np.concatenate([result.traced_to_boundary for result in results])
|
|
247
|
+
integral_1d = np.concatenate([result.integral for result in results])
|
|
248
|
+
|
|
249
|
+
# reshape and return the Mapping object
|
|
250
|
+
r_out = np.reshape(end_pos_1d[0, :], r_in.shape)
|
|
251
|
+
t_out = np.reshape(end_pos_1d[1, :], t_in.shape)
|
|
252
|
+
p_out = np.reshape(end_pos_1d[2, :], p_in.shape)
|
|
253
|
+
traced_to_boundary = np.reshape(traced_to_boundary_1d, r_in.shape)
|
|
254
|
+
integral = np.reshape(integral_1d, r_in.shape)
|
|
255
|
+
|
|
256
|
+
return Mapping(r_out, t_out, p_out, traced_to_boundary, integral)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def map_pt_forward(br: PathType,
|
|
260
|
+
bt: PathType,
|
|
261
|
+
bp: PathType,
|
|
262
|
+
p1d: ArrayLike,
|
|
263
|
+
t1d: ArrayLike,
|
|
264
|
+
radius: float = 1.0,
|
|
265
|
+
timeout: float = 3600.,
|
|
266
|
+
context: Optional[ContextType] = 'fork',
|
|
267
|
+
nproc: int = 4,
|
|
268
|
+
**mapfl_params):
|
|
269
|
+
"""Map field lines forwards on a phi, theta grid at constant radius.
|
|
270
|
+
|
|
271
|
+
The inputs are similar to instantiating a Tracer class except one also
|
|
272
|
+
specifies 1D arrays that define a grid in phi, theta and a radius to map from.
|
|
273
|
+
|
|
274
|
+
Currently the mapping radius defaults to 1.0 or must be set. In the future
|
|
275
|
+
this will default to the inner radius of the main mesh of the B files.
|
|
276
|
+
|
|
277
|
+
Notes
|
|
278
|
+
-----
|
|
279
|
+
This is a very specific helper function for a common task. Generic mappings
|
|
280
|
+
can be built by following this as an example for wrapping :func:`map_field_lines_in_parallel`.
|
|
281
|
+
|
|
282
|
+
Parameters
|
|
283
|
+
----------
|
|
284
|
+
br : PathType
|
|
285
|
+
Path to the Br magnetic field file.
|
|
286
|
+
bt : PathType
|
|
287
|
+
Path to the Bt magnetic field file.
|
|
288
|
+
bp : PathType
|
|
289
|
+
Path to the Bp magnetic field file.
|
|
290
|
+
p1d : ArrayLike
|
|
291
|
+
A 1D numpy array of phi (longitude) positions in radians that will be used to
|
|
292
|
+
build the mapping grid.
|
|
293
|
+
t1d : ArrayLike
|
|
294
|
+
A 1D numpy array of theta (co-latitude) positions in radians that will be used to
|
|
295
|
+
build the mapping grid.
|
|
296
|
+
radius : float, optional
|
|
297
|
+
Radius to map forward from. Default 1.0.
|
|
298
|
+
timeout : float, optional
|
|
299
|
+
Timeout in seconds for interprocess communication. Default is 3600 seconds.
|
|
300
|
+
context : ContextType, optional
|
|
301
|
+
The multiprocessing context to use when spawning the subprocess. Since many
|
|
302
|
+
processes are launched, generally 'fork' is recommended. Behavior may depend
|
|
303
|
+
on the system architecture. Default is 'fork'.
|
|
304
|
+
nproc : int, optional
|
|
305
|
+
The number of processes to spawn. This should be equal to or less than the number of threads
|
|
306
|
+
that can be used on the machine.
|
|
307
|
+
**mapfl_params : dict
|
|
308
|
+
Additional tracing parameters passed to the subprocess.
|
|
309
|
+
|
|
310
|
+
Returns
|
|
311
|
+
-------
|
|
312
|
+
mapping : :class:`~mapflpy.globals.Mapping`
|
|
313
|
+
A namedtuple containing the mapping results (:class:`mapflpy.globals.Mapping`).
|
|
314
|
+
|
|
315
|
+
See Also
|
|
316
|
+
--------
|
|
317
|
+
:func:`map_field_lines_in_parallel`
|
|
318
|
+
"""
|
|
319
|
+
|
|
320
|
+
# build a 2D grid of p and t locations from the input 1D arrays
|
|
321
|
+
p2d, t2d = np.meshgrid(p1d, t1d)
|
|
322
|
+
# the r locations are on the specified radius
|
|
323
|
+
r2d = np.ones_like(p2d)*radius
|
|
324
|
+
|
|
325
|
+
# define the wrapped trace function
|
|
326
|
+
def trace_function(launch_points):
|
|
327
|
+
traces = run_forward_tracing(br, bt, bp, launch_points=launch_points,
|
|
328
|
+
buffer_size=3, context=context, timeout=timeout,
|
|
329
|
+
**mapfl_params)
|
|
330
|
+
return traces
|
|
331
|
+
|
|
332
|
+
# compute the mapping
|
|
333
|
+
mapping = map_field_lines_in_parallel(trace_function, r2d, t2d, p2d, nproc=nproc)
|
|
334
|
+
|
|
335
|
+
return mapping
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def map_pt_backward(br: PathType,
|
|
339
|
+
bt: PathType,
|
|
340
|
+
bp: PathType,
|
|
341
|
+
p1d: ArrayLike,
|
|
342
|
+
t1d: ArrayLike,
|
|
343
|
+
radius: float = 30.0,
|
|
344
|
+
timeout: float = 3600.,
|
|
345
|
+
context: Optional[ContextType] = 'fork',
|
|
346
|
+
nproc: int = 4,
|
|
347
|
+
**mapfl_params):
|
|
348
|
+
"""Map field lines backwards on a phi, theta grid at constant radius.
|
|
349
|
+
|
|
350
|
+
The inputs are similar to instantiating a Tracer class except one also
|
|
351
|
+
specifies 1D arrays that define a grid in phi, theta and a radius to map from.
|
|
352
|
+
|
|
353
|
+
Currently the mapping radius defaults to 30 or must be set. In the future
|
|
354
|
+
this will default to the outer radius of the main mesh of the B files.
|
|
355
|
+
|
|
356
|
+
Notes
|
|
357
|
+
-----
|
|
358
|
+
This is a very specific helper function for a common task. Generic mappings
|
|
359
|
+
can be built by following this as an example for wrapping :func:`map_field_lines_in_parallel`.
|
|
360
|
+
|
|
361
|
+
Parameters
|
|
362
|
+
----------
|
|
363
|
+
br : PathType
|
|
364
|
+
Path to the Br magnetic field file.
|
|
365
|
+
bt : PathType
|
|
366
|
+
Path to the Bt magnetic field file.
|
|
367
|
+
bp : PathType
|
|
368
|
+
Path to the Bp magnetic field file.
|
|
369
|
+
p1d : ArrayLike
|
|
370
|
+
A 1D numpy array of phi (longitude) positions in radians that will be used to
|
|
371
|
+
build the mapping grid.
|
|
372
|
+
t1d : ArrayLike
|
|
373
|
+
A 1D numpy array of theta (co-latitude) positions in radians that will be used to
|
|
374
|
+
build the mapping grid.
|
|
375
|
+
radius : float, optional
|
|
376
|
+
Radius to map backwards from. Default 30.0.
|
|
377
|
+
timeout : float, optional
|
|
378
|
+
Timeout in seconds for interprocess communication. Default is 3600 seconds.
|
|
379
|
+
context : ContextType, optional
|
|
380
|
+
The multiprocessing context to use when spawning the subprocess. Since many
|
|
381
|
+
processes are launched, generally 'fork' is recommended. Behavior may depend
|
|
382
|
+
on the system architecture. Default is 'fork'.
|
|
383
|
+
nproc : int, optional
|
|
384
|
+
The number of processes to spawn. This should be equal to or less than the number of threads
|
|
385
|
+
that can be used on the machine.
|
|
386
|
+
**mapfl_params : dict
|
|
387
|
+
Additional tracing parameters passed to the subprocess.
|
|
388
|
+
|
|
389
|
+
Returns
|
|
390
|
+
-------
|
|
391
|
+
mapping : :class:`~mapflpy.globals.Mapping`
|
|
392
|
+
A namedtuple containing the mapping results (:class:`mapflpy.globals.Mapping`).
|
|
393
|
+
|
|
394
|
+
See Also
|
|
395
|
+
--------
|
|
396
|
+
:func:`map_field_lines_in_parallel`
|
|
397
|
+
"""
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
# build a 2D grid of p and t locations from the input 1D arrays
|
|
401
|
+
p2d, t2d = np.meshgrid(p1d, t1d)
|
|
402
|
+
# the r locations are on the specified radius
|
|
403
|
+
r2d = np.ones_like(p2d)*radius
|
|
404
|
+
|
|
405
|
+
# define the wrapped trace function
|
|
406
|
+
def trace_function(launch_points):
|
|
407
|
+
traces = run_backward_tracing(br, bt, bp, launch_points=launch_points,
|
|
408
|
+
buffer_size=3, context=context, timeout=timeout,
|
|
409
|
+
**mapfl_params)
|
|
410
|
+
return traces
|
|
411
|
+
|
|
412
|
+
# compute the mapping
|
|
413
|
+
mapping = map_field_lines_in_parallel(trace_function, r2d, t2d, p2d, nproc=nproc)
|
|
414
|
+
|
|
415
|
+
return mapping
|
|
416
|
+
|
|
417
|
+
|
|
183
418
|
def inter_domain_tracing(br_cor: PathType,
|
|
184
419
|
bt_cor: PathType,
|
|
185
420
|
bp_cor: PathType,
|
|
@@ -54,9 +54,6 @@ _BS0 = np.zeros(3, order='F').astype(np.float64)
|
|
|
54
54
|
_BS1 = np.zeros(3, order='F').astype(np.float64)
|
|
55
55
|
"""Sentinel array for :meth:`mapfl.trace` **bs1** parameter."""
|
|
56
56
|
|
|
57
|
-
_S = np.zeros(1, order='F').astype(np.float64)
|
|
58
|
-
"""Sentinel array for :meth:`mapfl.trace` **s** parameter."""
|
|
59
|
-
|
|
60
57
|
|
|
61
58
|
def state_modifier(method):
|
|
62
59
|
"""
|
|
@@ -289,19 +286,20 @@ def _mapflpy_trace_listener(pipe):
|
|
|
289
286
|
traces = np.full((buffer_size, *lps.shape), np.nan, order='F').astype(np.float64)
|
|
290
287
|
s1 = np.zeros(lps.shape, np.float64, order='F')
|
|
291
288
|
mask = np.full((1, lps.shape[1]), False, order='F')
|
|
289
|
+
integral = np.full((1, lps.shape[1]), 0.0, order='F')
|
|
292
290
|
for i in range(lps.shape[1]):
|
|
293
291
|
trace_args = dict(
|
|
294
292
|
s0=lps[:, i],
|
|
295
293
|
s1=s1[:, i],
|
|
296
294
|
bs0=_BS0,
|
|
297
295
|
bs1=_BS1,
|
|
298
|
-
s=
|
|
296
|
+
s=integral[:,i],
|
|
299
297
|
traced_to_r_boundary=mask[:, i],
|
|
300
298
|
svec=traces[:, :, i],
|
|
301
299
|
svec_n=buffer_size
|
|
302
300
|
)
|
|
303
301
|
mapfl.trace(**trace_args)
|
|
304
|
-
return Traces(traces, lps, s1, mask[0, :])
|
|
302
|
+
return Traces(traces, lps, s1, mask[0, :], integral[0,:])
|
|
305
303
|
|
|
306
304
|
while True:
|
|
307
305
|
method, *args = pipe.recv()
|
|
@@ -866,27 +864,29 @@ class Tracer(_Tracer):
|
|
|
866
864
|
Structured container with traced fieldline geometry, starting points, ending points,
|
|
867
865
|
and whether the fieldlines reached the radial boundary of the provided mesh.
|
|
868
866
|
"""
|
|
869
|
-
# `traces`, `s1`, and `
|
|
870
|
-
# and are used to store the traced fieldlines, their end points,
|
|
867
|
+
# `traces`, `s1`, `mask`, and `integral` are initialized to Fortran-contiguous arrays
|
|
868
|
+
# and are used to store the traced fieldlines, their end points, boundary masks, and
|
|
869
|
+
# the scalar field integral along the field line (length by default).
|
|
871
870
|
# `mapflpy_fortran` alters these arrays in-place during tracing.
|
|
872
871
|
# Each array must have a dimensionality > 1 so that a numpy view is passed.
|
|
873
872
|
traces = np.full((buff, *lps.shape), np.nan, order='F').astype(np.float64)
|
|
874
873
|
s1 = np.zeros(lps.shape, np.float64, order='F')
|
|
875
874
|
mask = np.full((1, lps.shape[1]), False, order='F')
|
|
875
|
+
integral = np.full((1, lps.shape[1]), 0.0, order='F')
|
|
876
876
|
|
|
877
|
-
# Since the parameters _bs0
|
|
878
|
-
# we can use the globals `_BS0
|
|
877
|
+
# Since the parameters _bs0 and _bs1 are discarded after the trace,
|
|
878
|
+
# we can use the globals `_BS0` and `_BS1` as placeholders to avoid having
|
|
879
879
|
# to repeatedly create and destruct arrays.
|
|
880
880
|
for i in range(lps.shape[1]):
|
|
881
881
|
self._mapfl.trace(s0=lps[:, i],
|
|
882
882
|
s1=s1[:, i],
|
|
883
883
|
bs0=_BS0,
|
|
884
884
|
bs1=_BS1,
|
|
885
|
-
s=
|
|
885
|
+
s=integral[:,i],
|
|
886
886
|
traced_to_r_boundary=mask[:, i],
|
|
887
887
|
svec=traces[:, :, i],
|
|
888
888
|
svec_n=buff)
|
|
889
|
-
return Traces(traces, lps, s1, mask[0, :])
|
|
889
|
+
return Traces(traces, lps, s1, mask[0, :], integral[0,:])
|
|
890
890
|
|
|
891
891
|
|
|
892
892
|
class TracerMP(_Tracer):
|
|
@@ -220,7 +220,8 @@ def combine_fwd_bwd_traces(fwd_traces: Traces,
|
|
|
220
220
|
fwd_traces.geometry]),
|
|
221
221
|
bwd_traces.end_pos,
|
|
222
222
|
fwd_traces.end_pos,
|
|
223
|
-
fwd_traces.traced_to_boundary & bwd_traces.traced_to_boundary
|
|
223
|
+
fwd_traces.traced_to_boundary & bwd_traces.traced_to_boundary,
|
|
224
|
+
fwd_traces.integral + bwd_traces.integral)
|
|
224
225
|
return combined_traces
|
|
225
226
|
|
|
226
227
|
|
|
@@ -423,7 +424,8 @@ def trim_fieldline_nan_buffer(traces: Traces | np.ndarray) -> List[np.ndarray]:
|
|
|
423
424
|
|
|
424
425
|
def combine_and_pad_fieldlines(arrs: list | tuple,
|
|
425
426
|
to_trace: bool = False,
|
|
426
|
-
traced_to_boundary: Optional[NDArray[np.bool_]] = None
|
|
427
|
+
traced_to_boundary: Optional[NDArray[np.bool_]] = None,
|
|
428
|
+
integral: Optional[NDArray[np.bool_]] = None
|
|
427
429
|
) -> np.ndarray | Traces:
|
|
428
430
|
"""
|
|
429
431
|
NaN-pad a list of variable-length 3D point arrays into a single dense array.
|
|
@@ -499,7 +501,7 @@ def combine_and_pad_fieldlines(arrs: list | tuple,
|
|
|
499
501
|
geometry[:a.shape[1], :, i] = a.T # (3, ni) -> (ni, 3)
|
|
500
502
|
start_pos[:, i] = a[:,0]
|
|
501
503
|
end_pos[:, i] = a[:, -1]
|
|
502
|
-
return Traces(geometry, start_pos, end_pos, traced_to_boundary)
|
|
504
|
+
return Traces(geometry, start_pos, end_pos, traced_to_boundary, integral)
|
|
503
505
|
|
|
504
506
|
|
|
505
507
|
|
|
@@ -47,8 +47,16 @@ fortranobject_c = f2py_inc_dir / 'fortranobject.c'
|
|
|
47
47
|
|
|
48
48
|
# ---- HDF5 dependencies
|
|
49
49
|
hdf5_c = dependency('hdf5', required : true)
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
|
|
51
|
+
# On Linux, meson resolves pkg-config deps to absolute library paths, which
|
|
52
|
+
# drops the -L flag from the link command. Bare -lhdf5_fortran then fails
|
|
53
|
+
# because the conda env's lib/ is never on the search path. Use find_library
|
|
54
|
+
# with an explicit dir (taken from the HDF5 pkg-config libdir) so both macOS
|
|
55
|
+
# and Linux get a resolved path, not a bare -l flag.
|
|
56
|
+
_hdf5_libdir = hdf5_c.get_variable(pkgconfig : 'libdir', default_value : '')
|
|
57
|
+
_hdf5_dirs = _hdf5_libdir != '' ? [_hdf5_libdir] : []
|
|
58
|
+
hdf5f = cc.find_library('hdf5_fortran', dirs : _hdf5_dirs, required : true)
|
|
59
|
+
hdf5f_hl = cc.find_library('hdf5_hl_fortran', dirs : _hdf5_dirs, required : true)
|
|
52
60
|
|
|
53
61
|
# Optional: OpenMP
|
|
54
62
|
# omp_dep = dependency('openmp', required : false)
|
|
@@ -110,9 +118,19 @@ configure_file(
|
|
|
110
118
|
install_subdir(
|
|
111
119
|
'mapflpy',
|
|
112
120
|
install_dir : py.get_install_dir(),
|
|
121
|
+
exclude_directories : ['fortran'],
|
|
113
122
|
exclude_files: [
|
|
114
123
|
'_version.py.in',
|
|
115
124
|
'__pycache__',
|
|
116
125
|
'*.pyc',
|
|
117
126
|
]
|
|
118
127
|
)
|
|
128
|
+
|
|
129
|
+
# Install mapflpy/fortran/__init__.py separately, so that the install_subdir
|
|
130
|
+
# above can exclude the entire fortran/ directory. Without this exclusion,
|
|
131
|
+
# any compiled .so left in the source tree (e.g. from a dev install) would
|
|
132
|
+
# be bundled into every wheel produced by "python -m build".
|
|
133
|
+
install_data(
|
|
134
|
+
'mapflpy/fortran/__init__.py',
|
|
135
|
+
install_dir : py.get_install_dir() / 'mapflpy' / 'fortran',
|
|
136
|
+
)
|
|
@@ -70,7 +70,23 @@ def _build_env(session: nox.Session) -> Path:
|
|
|
70
70
|
*pyproject["tool"][PROJECT_NAME].get("conda", []),
|
|
71
71
|
channel="conda-forge"
|
|
72
72
|
)
|
|
73
|
-
session.
|
|
73
|
+
env_dir = Path(session.env_dir).resolve()
|
|
74
|
+
lib_dir = str(env_dir / "lib")
|
|
75
|
+
# Override CONDA_PREFIX so conda-forge gfortran's spec file uses this env's
|
|
76
|
+
# lib dir for its implicit library search paths, not the active outer env.
|
|
77
|
+
env = {"CONDA_PREFIX": str(env_dir)}
|
|
78
|
+
env.update(_darwin_sdk_env())
|
|
79
|
+
if platform.system() == "Darwin":
|
|
80
|
+
# Pass an explicit rpath so delocate-wheel can bundle deps even when
|
|
81
|
+
# meson would otherwise embed a @loader_path-relative path.
|
|
82
|
+
env["LDFLAGS"] = (env.get("LDFLAGS", "") + f" -Wl,-rpath,{lib_dir}").strip()
|
|
83
|
+
# conda-forge's ld64_osx-64 linker may be invoked from the micromamba
|
|
84
|
+
# pkgs cache path (hard-linked), so @loader_path/../lib/libtapi.dylib
|
|
85
|
+
# resolves against the cache dir rather than the installed env. Set a
|
|
86
|
+
# fallback so dyld finds libtapi.dylib in the env's lib/ dir instead.
|
|
87
|
+
existing = os.environ.get("DYLD_FALLBACK_LIBRARY_PATH", "")
|
|
88
|
+
env["DYLD_FALLBACK_LIBRARY_PATH"] = f"{lib_dir}:{existing}".strip(":")
|
|
89
|
+
session.env.update(env)
|
|
74
90
|
|
|
75
91
|
|
|
76
92
|
def _dist_env(session: nox.Session) -> Path:
|
|
@@ -16,7 +16,7 @@ requires = [
|
|
|
16
16
|
# ----------------
|
|
17
17
|
[project]
|
|
18
18
|
name = "mapflpy"
|
|
19
|
-
version = "1.
|
|
19
|
+
version = "1.2.0"
|
|
20
20
|
description = "Fast field line tracing for spherical vector fields"
|
|
21
21
|
authors = [
|
|
22
22
|
{name = "Predictive Science Inc.", email = "webmaster@predsci.com"},
|
|
@@ -296,4 +296,4 @@ conda = [
|
|
|
296
296
|
"pkg-config",
|
|
297
297
|
"hdf5",
|
|
298
298
|
"zlib"
|
|
299
|
-
]
|
|
299
|
+
]
|