geopack-vectorize 2.0.0__tar.gz → 2.0.2__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.
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/CHANGELOG.md +27 -1
- geopack_vectorize-2.0.2/PKG-INFO +465 -0
- geopack_vectorize-2.0.2/README.md +417 -0
- geopack_vectorize-2.0.2/examples/notebooks/03_performance_comparison.ipynb +1861 -0
- geopack_vectorize-2.0.2/examples/notebooks/07_fieldline_geometry_and_derivatives.ipynb +1182 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/examples/notebooks/directional_derivatives_maps/dipole_field_directional_derivatives.ipynb +35 -13
- geopack_vectorize-2.0.2/examples/notebooks/directional_derivatives_maps/t96_field_directional_derivatives.ipynb +1782 -0
- geopack_vectorize-2.0.2/examples/python_code_samples/_helpers.py +84 -0
- {geopack_vectorize-2.0.0/examples → geopack_vectorize-2.0.2/examples/python_code_samples}/basic_usage.py +45 -81
- {geopack_vectorize-2.0.0/examples → geopack_vectorize-2.0.2/examples/python_code_samples}/field_line_9_derivatives_example.py +10 -62
- {geopack_vectorize-2.0.0/examples → geopack_vectorize-2.0.2/examples/python_code_samples}/field_line_geometry_example.py +51 -71
- geopack_vectorize-2.0.2/examples/readme_examples.py +145 -0
- geopack_vectorize-2.0.2/geopack/Mageometry/__init__.py +52 -0
- {geopack_vectorize-2.0.0/geopack/vectorized → geopack_vectorize-2.0.2/geopack/Mageometry}/field_line_geometry.py +0 -2
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/__init__.py +66 -28
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/geopack.py +21 -13
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/models/t01.py +1 -1
- geopack_vectorize-2.0.2/geopack/t01.py +5 -0
- geopack_vectorize-2.0.2/geopack/t04.py +5 -0
- geopack_vectorize-2.0.2/geopack/t89.py +5 -0
- geopack_vectorize-2.0.2/geopack/t96.py +5 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/vectorized/__init__.py +10 -38
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/vectorized/coordinates.py +15 -8
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/vectorized/coordinates_complex.py +7 -4
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/vectorized/igrf.py +3 -5
- geopack_vectorize-2.0.2/geopack/vectorized/models/__init__.py +8 -0
- geopack_vectorize-2.0.2/geopack/vectorized/models/t01.py +1969 -0
- geopack_vectorize-2.0.2/geopack/vectorized/models/t04.py +1985 -0
- geopack_vectorize-2.0.2/geopack/vectorized/models/t89.py +358 -0
- geopack_vectorize-2.0.2/geopack/vectorized/models/t96.py +2446 -0
- geopack_vectorize-2.0.2/geopack_vectorize.egg-info/PKG-INFO +465 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack_vectorize.egg-info/SOURCES.txt +22 -8
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack_vectorize.egg-info/requires.txt +4 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/pyproject.toml +11 -4
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/setup.py +2 -2
- geopack_vectorize-2.0.2/tests/installed_smoke.py +107 -0
- geopack_vectorize-2.0.2/tests/test_coordinate_broadcasting.py +79 -0
- geopack_vectorize-2.0.2/tests/test_distribution.py +73 -0
- geopack_vectorize-2.0.2/tests/test_model_input_regressions.py +67 -0
- geopack_vectorize-2.0.0/PKG-INFO +0 -346
- geopack_vectorize-2.0.0/README.md +0 -302
- geopack_vectorize-2.0.0/examples/notebooks/03_performance_comparison.ipynb +0 -1749
- geopack_vectorize-2.0.0/examples/notebooks/07_fieldline_geometry_and_derivatives.ipynb +0 -1152
- geopack_vectorize-2.0.0/examples/notebooks/directional_derivatives_maps/t96_field_directional_derivatives.ipynb +0 -4
- geopack_vectorize-2.0.0/examples/test_9_formulas.py +0 -134
- geopack_vectorize-2.0.0/examples/test_unit_vectors.py +0 -162
- geopack_vectorize-2.0.0/geopack_vectorize.egg-info/PKG-INFO +0 -346
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/LICENSE +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/MANIFEST.in +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/examples/notebooks/01_coordinate_transformations_guide.ipynb +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/examples/notebooks/02_magnetic_field_models_guide.ipynb +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/examples/notebooks/04_accuracy_validation.ipynb +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/examples/notebooks/05_field_line_tracing_guide.ipynb +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/examples/notebooks/06_field_line_tracing_validation.ipynb +0 -0
- {geopack_vectorize-2.0.0/examples/notebooks → geopack_vectorize-2.0.2/examples/python_code_samples}/magnetic_field_simple_sample.py +0 -0
- {geopack_vectorize-2.0.0/geopack/vectorized → geopack_vectorize-2.0.2/geopack/Mageometry}/field_line_directional_derivatives.py +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/core.py +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/igrf_coeffs/igrf10coeffs.txt +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/igrf_coeffs/igrf11coeffs.txt +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/igrf_coeffs/igrf12coeffs.txt +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/igrf_coeffs/igrf13coeffs.txt +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/igrf_coeffs/igrf14coeffs.txt +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/igrf_coeffs/igrf9coeffs.txt +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/models/__init__.py +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/models/t04.py +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/models/t89.py +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/models/t96.py +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/print_structure.py +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/vectorized/condip1_exact.py +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack/vectorized/trace.py +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack_vectorize.egg-info/dependency_links.txt +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/geopack_vectorize.egg-info/top_level.txt +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/requirements.txt +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/setup.cfg +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/tests/test_trace_vectorized.py +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/tests/test_trace_vectorized_with_vectorized_models.py +0 -0
- {geopack_vectorize-2.0.0 → geopack_vectorize-2.0.2}/tests/test_vectorized_models.py +0 -0
|
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [2.0.2] - 2026-11-04
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Preserve the T01 IMF clock angle for negative By instead of replacing it
|
|
14
|
+
with 2*pi, correcting both scalar and vectorized magnetic fields.
|
|
15
|
+
- Accept integer coordinates without truncation in vectorized T01/T96,
|
|
16
|
+
and preserve NumPy broadcasting and multidimensional inputs in T01.
|
|
17
|
+
- Broadcast all coordinate/vector components before transforms and determine
|
|
18
|
+
scalar outputs from every input, including spherical transforms and the
|
|
19
|
+
IGRF GSM/GSW wrappers.
|
|
20
|
+
|
|
21
|
+
## [2.0.1] - 2026-10-28
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- Include every `geopack` subpackage in wheels and source distributions, including
|
|
25
|
+
`geopack.vectorized.models`, so a normal pip installation can be imported.
|
|
26
|
+
- Load bundled IGRF coefficients on import without network access or writes to
|
|
27
|
+
the installation directory. Updates remain available through `update_igrf()`,
|
|
28
|
+
using HTTPS and a bounded request timeout.
|
|
29
|
+
- Restore `t89_vectorized`, `t96_vectorized`, `t01_vectorized`, and `t04_vectorized`
|
|
30
|
+
imports from `geopack.vectorized`, including the quick-start import on PyPI.
|
|
31
|
+
- Restore upstream scalar model imports (`from geopack.t89 import t89`, and the
|
|
32
|
+
corresponding T96, T01, and T04 paths) used by consumers such as pySPEDAS.
|
|
33
|
+
|
|
8
34
|
## [2.0.0] - 2026-02-11
|
|
9
35
|
|
|
10
36
|
### Breaking
|
|
@@ -96,4 +122,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
96
122
|
|
|
97
123
|
## [1.0.11] - Previous releases
|
|
98
124
|
|
|
99
|
-
See git history for changes in previous versions.
|
|
125
|
+
See git history for changes in previous versions.
|
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: geopack-vectorize
|
|
3
|
+
Version: 2.0.2
|
|
4
|
+
Summary: Python implementation of geopack and Tsyganenko models with optimized vectorization
|
|
5
|
+
Home-page: https://github.com/Butadiene/geopack-vectorize
|
|
6
|
+
Author: geopack-vectorize contributors
|
|
7
|
+
Author-email: Sheng Tian <ts0110@atmos.ucla.edu>
|
|
8
|
+
Maintainer-email: Sheng Tian <ts0110@atmos.ucla.edu>
|
|
9
|
+
License: MIT
|
|
10
|
+
Project-URL: Homepage, https://github.com/Butadiene/geopack-vectorize
|
|
11
|
+
Project-URL: Documentation, https://github.com/Butadiene/geopack-vectorize#readme
|
|
12
|
+
Project-URL: Repository, https://github.com/Butadiene/geopack-vectorize
|
|
13
|
+
Project-URL: Issues, https://github.com/Butadiene/geopack-vectorize/issues
|
|
14
|
+
Keywords: geopack,space physics,Tsyganenko model,magnetosphere,geomagnetic field
|
|
15
|
+
Platform: any
|
|
16
|
+
Classifier: Development Status :: 4 - Beta
|
|
17
|
+
Classifier: Intended Audience :: Science/Research
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
27
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
28
|
+
Requires-Python: >=3.7
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: numpy>=1.16.0
|
|
32
|
+
Requires-Dist: scipy>=1.0.0
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
35
|
+
Requires-Dist: pytest-cov>=2.0; extra == "dev"
|
|
36
|
+
Requires-Dist: setuptools>=61.0; extra == "dev"
|
|
37
|
+
Requires-Dist: wheel; extra == "dev"
|
|
38
|
+
Requires-Dist: matplotlib>=3.0; extra == "dev"
|
|
39
|
+
Provides-Extra: examples
|
|
40
|
+
Requires-Dist: matplotlib>=3.0; extra == "examples"
|
|
41
|
+
Requires-Dist: jupyter>=1.0; extra == "examples"
|
|
42
|
+
Requires-Dist: pandas>=1.0; extra == "examples"
|
|
43
|
+
Requires-Dist: psutil>=5.0; extra == "examples"
|
|
44
|
+
Dynamic: author
|
|
45
|
+
Dynamic: home-page
|
|
46
|
+
Dynamic: license-file
|
|
47
|
+
Dynamic: platform
|
|
48
|
+
|
|
49
|
+
# GEOPACK-VECTORIZE
|
|
50
|
+
|
|
51
|
+
A vectorized implementation extending the excellent Python [geopack](https://github.com/tsssss/geopack) library by Sheng Tian, adding high-performance NumPy-based versions of Tsyganenko magnetospheric field models and field line tracing algorithms.
|
|
52
|
+
|
|
53
|
+
## Overview
|
|
54
|
+
|
|
55
|
+
This project builds upon the solid foundation of the Python geopack library, which provides faithful implementations of the Tsyganenko magnetospheric field models (T89, T96, T01, T04) and the IGRF geomagnetic field model, originally developed in Fortran by N.A. Tsyganenko.
|
|
56
|
+
|
|
57
|
+
geopack-vectorize extends the original geopack by adding vectorized implementations that leverage NumPy for parallel computation, achieving 20-150x performance improvements while maintaining machine-precision accuracy and full backward compatibility with the original scalar functions.
|
|
58
|
+
|
|
59
|
+
### What This Project Adds
|
|
60
|
+
|
|
61
|
+
- **Vectorized Field Models**: NumPy-based implementations of all Tsyganenko models (T89, T96, T01, T04) that process arrays of points simultaneously
|
|
62
|
+
- **Vectorized Field Line Tracing**: Parallel tracing of multiple field lines with improved boundary interpolation
|
|
63
|
+
- **Vectorized Coordinate Transforms**: Array-based transformations between all coordinate systems
|
|
64
|
+
- **Full Backward Compatibility**: All original geopack functions remain unchanged and available
|
|
65
|
+
- **Comprehensive Validation**: Extensive test suite ensuring < 10⁻¹¹ relative error vs original implementations
|
|
66
|
+
|
|
67
|
+
## Publication and Citation
|
|
68
|
+
|
|
69
|
+
geopack-vectorize, including its Mageometry component for local-frame analysis of magnetic field geometry, is described in the following paper published in *Earth, Planets and Space*. If you use this work in your research, please cite:
|
|
70
|
+
|
|
71
|
+
> Isono, K., Yoshikawa, A. & Katoh, Y. (2026). A local-frame approach to three-dimensional analysis of magnetospheric magnetic fields. *Earth, Planets and Space*, **78**, Article 201. [doi:10.1186/s40623-026-02528-2](https://doi.org/10.1186/s40623-026-02528-2).
|
|
72
|
+
|
|
73
|
+
## Upgrading to v2.0.0
|
|
74
|
+
|
|
75
|
+
As of v2.0.0, the PyPI package name has changed from `geopack-vectorized` to `geopack-vectorize`. The old `geopack-vectorized` package will remain available for a transitional period but will eventually be removed. Please update your installation:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip uninstall geopack-vectorized
|
|
79
|
+
pip install geopack-vectorize
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Other notable changes from v1.1.4 to v2.0.0:
|
|
83
|
+
- Internal module structure reorganized (all vectorized code now under `geopack/vectorized/`)
|
|
84
|
+
- All comments and docstrings translated to English
|
|
85
|
+
- Docstrings standardized to NumPy format
|
|
86
|
+
- Example notebooks cleaned up and reorganized
|
|
87
|
+
- See [RELEASE_NOTES_v2.0.0.md](docs/releases/RELEASE_NOTES_v2.0.0.md) for the full list of changes
|
|
88
|
+
|
|
89
|
+
**Top-level imports are unchanged** — existing code using `from geopack import t96_vectorized` etc. will continue to work without modification.
|
|
90
|
+
|
|
91
|
+
## Installation
|
|
92
|
+
|
|
93
|
+
### Requirements
|
|
94
|
+
- Python 3.7+
|
|
95
|
+
- NumPy
|
|
96
|
+
- SciPy
|
|
97
|
+
|
|
98
|
+
### Install from PyPI
|
|
99
|
+
```bash
|
|
100
|
+
pip install geopack-vectorize
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
IGRF coefficient files are included in the package. Importing `geopack` and
|
|
104
|
+
running the field models work offline. To explicitly download newer coefficient
|
|
105
|
+
files, call `geopack.update_igrf()` followed by `geopack.init_igrf()`; downloading
|
|
106
|
+
requires internet access.
|
|
107
|
+
|
|
108
|
+
The upstream scalar import paths, such as `from geopack.t89 import t89`, are
|
|
109
|
+
also supported. Both `geopack` and `geopack-vectorize` install files into the
|
|
110
|
+
same `geopack` Python package. In environments that require both distributions
|
|
111
|
+
(for example through pySPEDAS), install `geopack-vectorize` last; subsequently
|
|
112
|
+
reinstalling or removing either distribution can overwrite or remove shared
|
|
113
|
+
files. Use a separate virtual environment when testing or changing installations.
|
|
114
|
+
|
|
115
|
+
### Install from Source
|
|
116
|
+
```bash
|
|
117
|
+
git clone https://github.com/Butadiene/geopack-vectorize.git
|
|
118
|
+
cd geopack-vectorize
|
|
119
|
+
pip install -e .
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Usage Examples
|
|
123
|
+
|
|
124
|
+
All vectorized functions accept both scalars and NumPy arrays. Call `geopack.recalc(ut)` once before using any model or transform. See [`examples/readme_examples.py`](examples/readme_examples.py) for a runnable version of the code below. For detailed function descriptions, please also refer to the [upstream geopack README](https://github.com/tsssss/geopack).
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
import geopack
|
|
128
|
+
import numpy as np
|
|
129
|
+
|
|
130
|
+
ut = 100 # Unix timestamp (seconds since 1970-01-01)
|
|
131
|
+
ps = geopack.recalc(ut)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Coordinate Transformations
|
|
135
|
+
```python
|
|
136
|
+
from geopack import geogsm_vectorized
|
|
137
|
+
|
|
138
|
+
# Convert multiple GEO points to GSM (j=1: GEO→GSM, j=-1: GSM→GEO)
|
|
139
|
+
x_geo = np.array([1.0, 2.0, 3.0])
|
|
140
|
+
y_geo = np.array([0.5, 1.0, 1.5])
|
|
141
|
+
z_geo = np.array([0.0, 0.0, 0.0])
|
|
142
|
+
|
|
143
|
+
x_gsm, y_gsm, z_gsm = geogsm_vectorized(x_geo, y_geo, z_geo, j=1)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Internal Field (IGRF and Dipole)
|
|
147
|
+
```python
|
|
148
|
+
from geopack import igrf_gsm_vectorized
|
|
149
|
+
|
|
150
|
+
# IGRF magnetic field at multiple GSM positions (Earth radii)
|
|
151
|
+
x = np.array([2.0, 3.0, 4.0, 5.0])
|
|
152
|
+
y = np.zeros(4)
|
|
153
|
+
z = np.zeros(4)
|
|
154
|
+
|
|
155
|
+
bx, by, bz = igrf_gsm_vectorized(x, y, z) # returns nT
|
|
156
|
+
|
|
157
|
+
# Dipole field at the same positions (accepts scalars or arrays)
|
|
158
|
+
dx, dy, dz = geopack.dip(x, y, z)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Tsyganenko External Field Models
|
|
162
|
+
```python
|
|
163
|
+
from geopack import t96_vectorized
|
|
164
|
+
|
|
165
|
+
# T96 parameters: [Pdyn, Dst, ByIMF, BzIMF, 0, 0, 0, 0, 0, 0]
|
|
166
|
+
parmod = np.array([2.0, -20.0, 0.0, -5.0, 0, 0, 0, 0, 0, 0])
|
|
167
|
+
|
|
168
|
+
x = np.array([5.0, 6.0, 7.0, 8.0, 9.0]) # GSM coordinates (Re)
|
|
169
|
+
y = np.zeros(5)
|
|
170
|
+
z = np.zeros(5)
|
|
171
|
+
|
|
172
|
+
bx, by, bz = t96_vectorized(parmod, ps, x, y, z) # GSM components (nT)
|
|
173
|
+
|
|
174
|
+
# Tsyganenko models give only the external (magnetospheric) field.
|
|
175
|
+
# Add an internal field to get the total magnetic field:
|
|
176
|
+
bx_int, by_int, bz_int = geopack.dip(x, y, z)
|
|
177
|
+
bx_total = bx + bx_int
|
|
178
|
+
by_total = by + by_int
|
|
179
|
+
bz_total = bz + bz_int
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Field Line Tracing
|
|
183
|
+
```python
|
|
184
|
+
from geopack import trace_vectorized
|
|
185
|
+
|
|
186
|
+
# Trace using dipole (internal) + T96 (external)
|
|
187
|
+
x0 = np.array([5.0, 6.0, 7.0, 8.0])
|
|
188
|
+
y0 = np.zeros(4)
|
|
189
|
+
z0 = np.zeros(4)
|
|
190
|
+
|
|
191
|
+
xf, yf, zf, status = trace_vectorized(
|
|
192
|
+
x0, y0, z0, dir=-1, rlim=30, parmod=parmod, exname="t96", inname="dip"
|
|
193
|
+
)
|
|
194
|
+
# status: 0 = hit inner boundary, 1 = hit outer boundary, 2 = max steps
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Field Line Geometry (Frenet-Serret Frame)
|
|
198
|
+
```python
|
|
199
|
+
from geopack import field_line_curvature, field_line_frenet_frame
|
|
200
|
+
|
|
201
|
+
# Combined model: dipole (internal) + T96 (external)
|
|
202
|
+
def dip_plus_t96(parmod, ps, x, y, z):
|
|
203
|
+
bx_d, by_d, bz_d = geopack.dip(x, y, z)
|
|
204
|
+
bx_t, by_t, bz_t = t96_vectorized(parmod, ps, x, y, z)
|
|
205
|
+
return bx_d + bx_t, by_d + by_t, bz_d + bz_t
|
|
206
|
+
|
|
207
|
+
# Curvature at several points along the noon meridian
|
|
208
|
+
x = np.array([5.0, 6.0, 7.0, 8.0])
|
|
209
|
+
y = np.zeros(4)
|
|
210
|
+
z = np.zeros(4)
|
|
211
|
+
|
|
212
|
+
kappa = field_line_curvature(dip_plus_t96, parmod, ps, x, y, z, delta=1e-3)
|
|
213
|
+
# kappa: field line curvature [1/Re]
|
|
214
|
+
|
|
215
|
+
# Full Frenet-Serret frame (tangent, normal, binormal) + curvature
|
|
216
|
+
tx, ty, tz, nx, ny, nz, bx, by, bz, curvature = \
|
|
217
|
+
field_line_frenet_frame(dip_plus_t96, parmod, ps, x, y, z, delta=1e-3)
|
|
218
|
+
# curvature [1/Re]; tangent, normal, binormal are unit vectors (dimensionless)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Field Line Directional Derivatives
|
|
222
|
+
```python
|
|
223
|
+
from geopack import field_line_directional_derivatives
|
|
224
|
+
|
|
225
|
+
# All 9 directional derivatives (using combined dipole + T96 model)
|
|
226
|
+
derivs = field_line_directional_derivatives(
|
|
227
|
+
dip_plus_t96, parmod, ps, x, y, z, delta=1e-3
|
|
228
|
+
)
|
|
229
|
+
# All derivative values are in units of [1/Re]
|
|
230
|
+
|
|
231
|
+
# Tangential derivatives (∂/∂T)
|
|
232
|
+
# derivs['dT_dT_n'] (∂T/∂T)·n = κ (curvature)
|
|
233
|
+
# derivs['dT_dT_b'] (∂T/∂T)·b = 0 (identity)
|
|
234
|
+
# derivs['dn_dT_b'] (∂n/∂T)·b = τ (torsion)
|
|
235
|
+
|
|
236
|
+
# Normal derivatives (∂/∂n)
|
|
237
|
+
# derivs['dT_dn_n'] (∂T/∂n)·n
|
|
238
|
+
# derivs['dT_dn_b'] (∂T/∂n)·b
|
|
239
|
+
# derivs['dn_dn_b'] (∂n/∂n)·b
|
|
240
|
+
|
|
241
|
+
# Binormal derivatives (∂/∂b)
|
|
242
|
+
# derivs['dn_db_b'] (∂n/∂b)·b
|
|
243
|
+
# derivs['dn_db_T'] (∂n/∂b)·T
|
|
244
|
+
# derivs['db_db_T'] (∂b/∂b)·T
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
> **Note:** The field line geometry modules now live in `geopack.Mageometry`. Top-level imports (including the legacy `*_vectorized` aliases such as `field_line_curvature_vectorized`) keep working, but deep-path imports from the old location (e.g. `from geopack.vectorized.field_line_geometry import ...`) are no longer supported.
|
|
248
|
+
|
|
249
|
+
## Vectorized Components
|
|
250
|
+
|
|
251
|
+
### Coordinate Transformations
|
|
252
|
+
|
|
253
|
+
All vectorized transforms accept scalar or NumPy array inputs. Each pairwise transform uses a direction flag `j`: `j=1` for the forward direction, `j=-1` for the inverse. All coordinate values are in Earth radii (Re); angles in radians. See [SPENVIS Coordinate Transformations](https://www.spenvis.oma.be/help/background/coortran/coortran.html) for coordinate system definitions.
|
|
254
|
+
|
|
255
|
+
| Function | Arguments | Forward (j=1) | Inverse (j=-1) |
|
|
256
|
+
|----------|-----------|---------------|-----------------|
|
|
257
|
+
| `geogsm_vectorized` | `(x, y, z, j)` | GEO → GSM | GSM → GEO |
|
|
258
|
+
| `geomag_vectorized` | `(x, y, z, j)` | GEO → MAG | MAG → GEO |
|
|
259
|
+
| `geigeo_vectorized` | `(x, y, z, j)` | GEI → GEO | GEO → GEI |
|
|
260
|
+
| `gsmgse_vectorized` | `(x, y, z, j)` | GSM → GSE | GSE → GSM |
|
|
261
|
+
| `smgsm_vectorized` | `(x, y, z, j)` | SM → GSM | GSM → SM |
|
|
262
|
+
| `magsm_vectorized` | `(x, y, z, j)` | MAG → SM | SM → MAG |
|
|
263
|
+
| `gswgsm_vectorized` | `(x, y, z, j)` | GSW → GSM | GSM → GSW |
|
|
264
|
+
|
|
265
|
+
Spherical/Cartesian and field-vector transforms:
|
|
266
|
+
- `sphcar_vectorized(r, theta, phi, j)` — Spherical ↔ Cartesian (j=1: Sph→Cart, j=-1: Cart→Sph)
|
|
267
|
+
- `bspcar_vectorized(theta, phi, br, btheta, bphi)` — B-field components: Spherical → Cartesian
|
|
268
|
+
- `bcarsp_vectorized(x, y, z, bx, by, bz)` — B-field components: Cartesian → Spherical
|
|
269
|
+
|
|
270
|
+
### Internal Field (IGRF and Dipole)
|
|
271
|
+
|
|
272
|
+
Vectorized IGRF and dipole field functions. All return magnetic field components in nanotesla (nT). IGRF covers years 1900–2025 with extrapolation beyond.
|
|
273
|
+
|
|
274
|
+
- `igrf_geo_vectorized(r, theta, phi)` — IGRF in spherical GEO coordinates (r in Re, angles in radians); returns `(br, btheta, bphi)`
|
|
275
|
+
- `igrf_gsm_vectorized(x, y, z)` — IGRF in GSM Cartesian coordinates (Re); returns `(bx, by, bz)`
|
|
276
|
+
- `igrf_gsw_vectorized(x, y, z)` — IGRF in GSW Cartesian coordinates (Re); returns `(bx, by, bz)`
|
|
277
|
+
- `dip(x, y, z)` — Dipole field in GSM coordinates (Re); natively array-compatible via NumPy operations
|
|
278
|
+
|
|
279
|
+
### External Field Models (Tsyganenko)
|
|
280
|
+
|
|
281
|
+
Tsyganenko magnetospheric field models. All take positions in **GSM coordinates** (Re) and return `(bx, by, bz)` in nanotesla (nT, GSM). `ps` is the dipole tilt angle (radians) returned by `recalc()`.
|
|
282
|
+
|
|
283
|
+
- `t89_vectorized(iopt, ps, x, y, z)` — T89 model; `iopt` is the Kp index (1–7)
|
|
284
|
+
- `t96_vectorized(parmod, ps, x, y, z)` — T96 model; `parmod = [Pdyn, Dst, ByIMF, BzIMF, 0, 0, 0, 0, 0, 0]`
|
|
285
|
+
- `t01_vectorized(parmod, ps, x, y, z)` — T01 model; `parmod = [Pdyn, Dst, ByIMF, BzIMF, G1, G2, 0, 0, 0, 0]`
|
|
286
|
+
- `t04_vectorized(parmod, ps, x, y, z)` — T04 model; `parmod = [Pdyn, Dst, ByIMF, BzIMF, W1, W2, W3, W4, W5, W6]`
|
|
287
|
+
|
|
288
|
+
For detailed parameter descriptions, see the [upstream geopack README](https://github.com/tsssss/geopack).
|
|
289
|
+
|
|
290
|
+
**Note:** Tsyganenko models provide only the *external* (magnetospheric) contribution. To obtain the total magnetic field, add an internal field (IGRF or dipole):
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
# Total field = internal (dipole) + external (T96)
|
|
294
|
+
bx_int, by_int, bz_int = geopack.dip(x, y, z)
|
|
295
|
+
bx_ext, by_ext, bz_ext = t96_vectorized(parmod, ps, x, y, z)
|
|
296
|
+
|
|
297
|
+
bx_total = bx_int + bx_ext
|
|
298
|
+
by_total = by_int + by_ext
|
|
299
|
+
bz_total = bz_int + bz_ext
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Field Line Tracing
|
|
303
|
+
|
|
304
|
+
`trace_vectorized` traces magnetic field lines from given starting points to inner or outer boundaries.
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
trace_vectorized(xi, yi, zi, dir, rlim, r0, parmod, exname, inname, ...)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
| Parameter | Type | Default | Description |
|
|
311
|
+
|-----------|------|---------|-------------|
|
|
312
|
+
| `xi, yi, zi` | float or array | *(required)* | Starting positions in GSM coordinates (Re) |
|
|
313
|
+
| `dir` | float | `1.0` | Tracing direction: `+1` antiparallel to **B**, `-1` parallel to **B** |
|
|
314
|
+
| `rlim` | float | `10.0` | Outer boundary radius (Re); tracing stops when r >= rlim |
|
|
315
|
+
| `r0` | float | `1.0` | Inner boundary sphere radius (Re); tracing stops when r <= r0 |
|
|
316
|
+
| `parmod` | array | `2` | Model parameters (scalar Kp for T89; 10-element array for T96/T01/T04) |
|
|
317
|
+
| `exname` | str | `"t89"` | External field model: `"t89"`, `"t96"`, `"t01"`, or `"t04"` |
|
|
318
|
+
| `inname` | str | `"igrf"` | Internal field model: `"igrf"` or `"dip"` |
|
|
319
|
+
| `maxloop` | int | `1000` | Maximum number of integration steps per trace |
|
|
320
|
+
| `return_full_path` | bool | `False` | If `True`, returns full field line trajectories as masked arrays |
|
|
321
|
+
| `strict_scalar_models` | bool | `True` | If `True`, evaluates field models point-by-point for bitwise match with scalar `trace()` |
|
|
322
|
+
| `return_nsteps` | bool | `False` | If `True`, also returns the number of integration steps per trace |
|
|
323
|
+
|
|
324
|
+
**Returns:** `(xf, yf, zf, status)` — final positions (Re, GSM) and integer status codes:
|
|
325
|
+
- `0` — reached inner boundary (r <= r0)
|
|
326
|
+
- `1` — reached outer boundary (r >= rlim)
|
|
327
|
+
- `2` — exceeded maximum integration steps
|
|
328
|
+
|
|
329
|
+
Optional returns (appended when the corresponding flag is `True`):
|
|
330
|
+
- `xx, yy, zz` — full path arrays (`return_full_path`)
|
|
331
|
+
- `nsteps` — per-trace step counts (`return_nsteps`)
|
|
332
|
+
|
|
333
|
+
Related functions for field line analysis:
|
|
334
|
+
- Field line geometry (curvature, torsion, Frenet-Serret frame) — see [Usage Examples](#field-line-geometry-frenet-serret-frame)
|
|
335
|
+
- Directional derivatives along field lines — see [Usage Examples](#field-line-directional-derivatives)
|
|
336
|
+
|
|
337
|
+
## Documentation and Examples
|
|
338
|
+
|
|
339
|
+
Example notebooks are available in `examples/notebooks/`. Install matplotlib and pandas to run them.
|
|
340
|
+
|
|
341
|
+
### Tutorial Notebooks
|
|
342
|
+
- `01_coordinate_transformations_guide` — Coordinate system transforms
|
|
343
|
+
- `02_magnetic_field_models_guide` — Field model usage (T89, T96, T01, T04)
|
|
344
|
+
- `03_performance_comparison` — Scalar vs vectorized benchmarks
|
|
345
|
+
- `04_accuracy_validation` — Numerical accuracy verification
|
|
346
|
+
- `05_field_line_tracing_guide` — Field line tracing tutorial
|
|
347
|
+
- `06_field_line_tracing_validation` — Tracing accuracy validation
|
|
348
|
+
- `07_fieldline_geometry_and_derivatives` — Frenet-Serret frame and directional derivatives
|
|
349
|
+
|
|
350
|
+
### Advanced Examples (`examples/notebooks/directional_derivatives_maps/`)
|
|
351
|
+
- `dipole_field_directional_derivatives` — Dipole field directional derivative maps
|
|
352
|
+
- `t96_field_directional_derivatives` — T96 model directional derivative and FAC maps
|
|
353
|
+
|
|
354
|
+
## Performance Benchmarks
|
|
355
|
+
|
|
356
|
+
Regenerate this table with [`benchmark/readme_benchmarks.py`](benchmark/readme_benchmarks.py) (`--plain` for plain text output).
|
|
357
|
+
|
|
358
|
+
| Component | Scalar (100 pts) [s] | Vectorized [s] | Speedup |
|
|
359
|
+
|-----------|--------------------:|---------------------:|--------:|
|
|
360
|
+
| Coordinate Transforms (subset) | 0.000 | 0.000 | **6.3x** |
|
|
361
|
+
| IGRF (GSW) | 0.006 | 0.002 | **3.2x** |
|
|
362
|
+
| T89 Model | 0.005 | 0.000 | **22.1x** |
|
|
363
|
+
| T96 Model | 0.127 | 0.023 | **5.6x** |
|
|
364
|
+
| T01 Model | 0.205 | 0.027 | **7.5x** |
|
|
365
|
+
| T04 Model | 0.200 | 0.025 | **7.9x** |
|
|
366
|
+
| Field Line Tracing (vectorized field models) [scalar extrap from 50] | 2.343 | 2.225 | **1.1x** |
|
|
367
|
+
|
|
368
|
+
| Component | Scalar (1000 pts) [s] | Vectorized [s] | Speedup |
|
|
369
|
+
|-----------|---------------------:|---------------------:|--------:|
|
|
370
|
+
| Coordinate Transforms (subset) | 0.004 | 0.000 | **48.9x** |
|
|
371
|
+
| IGRF (GSW) | 0.068 | 0.006 | **11.3x** |
|
|
372
|
+
| T89 Model | 0.045 | 0.000 | **116x** |
|
|
373
|
+
| T96 Model | 1.046 | 0.036 | **29.2x** |
|
|
374
|
+
| T01 Model | 1.815 | 0.039 | **46.0x** |
|
|
375
|
+
| T04 Model | 1.820 | 0.042 | **43.5x** |
|
|
376
|
+
| Field Line Tracing (vectorized field models) [scalar extrap from 50] | 21.607 | 3.050 | **7.1x** |
|
|
377
|
+
|
|
378
|
+
### Where Does the Speedup Come From?
|
|
379
|
+
|
|
380
|
+
The speedup is not simply a function-call-count effect. Modeling the time of one vectorized call on an n-element array as `t_vec(n) ≈ a + b·n` separates a fixed per-call overhead `a` from the marginal cost per point `b`:
|
|
381
|
+
|
|
382
|
+
| Component | Overhead a [ms/call] | Marginal b [µs/point] | Scalar [µs/point] | Per-point ratio | Break-even n* |
|
|
383
|
+
|-----------|---------------------:|----------------------:|------------------:|----------------:|--------------:|
|
|
384
|
+
| T89 | 0.26 | 0.57 | 46.9 | **82x** | 2 |
|
|
385
|
+
| T96 | 11 | 8.9 | 1090 | **123x** | 14 |
|
|
386
|
+
| T01 | 16 | 21.2 | 1828 | **86x** | 10 |
|
|
387
|
+
| T04 | 15 | 29.1 | 1870 | **64x** | 8 |
|
|
388
|
+
| IGRF (GSW) | 1.3 | 3.6 | 57.9 | **16x** | 42 |
|
|
389
|
+
|
|
390
|
+
- **A vectorized call with n = 1 is slower than a scalar call** (the fixed overhead `a` is paid on every call), so the gain does not come from merely reducing the number of function calls. Below the break-even size n\* the scalar functions are faster.
|
|
391
|
+
- **The overhead `a` scales with model complexity** (T89 ≪ T96/T01/T04): it is the accumulated per-array-operation cost (NumPy dispatch, temporaries, both branches of `np.where`), not a constant per-call cost.
|
|
392
|
+
- **The per-point cost ratio far exceeds the SIMD width** for float64 (4 lanes AVX2 / 8 lanes AVX-512), so SIMD alone cannot explain it. The dominant mechanism is amortizing the Python interpreter's per-operation overhead across array elements in NumPy's compiled loops.
|
|
393
|
+
|
|
394
|
+
Regenerate this table with [`benchmark/readme_overhead_decomposition.py`](benchmark/readme_overhead_decomposition.py) (`--plain` for plain text output). A step-by-step version with figures is in [`examples/notebooks/03_performance_comparison.ipynb`](examples/notebooks/03_performance_comparison.ipynb), Section 3c.
|
|
395
|
+
|
|
396
|
+
## Accuracy Validation
|
|
397
|
+
|
|
398
|
+
The vectorized implementations are validated against the original scalar functions across a 100 × 100 grid in the X-Z meridian plane (Y = 0, X: 2 to −10 Re, Z: 6 to −6 Re) using the combined IGRF + T96 total field. Relative error is defined as (B_scalar − B_vector) / |B_scalar|.
|
|
399
|
+
|
|
400
|
+
Regenerate these figures with [`benchmark/readme_validation.py`](benchmark/readme_validation.py).
|
|
401
|
+
|
|
402
|
+
| Metric | Value |
|
|
403
|
+
|--------|------:|
|
|
404
|
+
| Points evaluated | 9,792 |
|
|
405
|
+
| Max \|relative error\| | 9.56 × 10⁻¹² |
|
|
406
|
+
| Mean \|relative error\| | 6.72 × 10⁻¹⁴ |
|
|
407
|
+
| Median \|relative error\| | 2.54 × 10⁻¹⁶ |
|
|
408
|
+
| Scalar computation | 23.72 s |
|
|
409
|
+
| Vectorized computation | 0.21 s |
|
|
410
|
+
| Speedup | **116x** |
|
|
411
|
+
|
|
412
|
+
The median error is near double-precision machine epsilon (~1.1 × 10⁻¹⁶), confirming that the vectorized path reproduces the scalar results to floating-point precision.
|
|
413
|
+
|
|
414
|
+
**Error distribution** — Most points cluster below 10⁻¹⁵; the tail extends to ~10⁻¹¹.
|
|
415
|
+
|
|
416
|
+

|
|
417
|
+
|
|
418
|
+
**Spatial error map** — The largest errors concentrate near the magnetopause/cusp boundary where T96 current-sheet gradients are steepest, but remain negligible everywhere.
|
|
419
|
+
|
|
420
|
+

|
|
421
|
+
|
|
422
|
+
## Technical Details
|
|
423
|
+
|
|
424
|
+
### Vectorization Approach
|
|
425
|
+
- Full NumPy broadcasting support
|
|
426
|
+
- Elimination of all Python loops
|
|
427
|
+
- Optimized conditional logic using `np.where`
|
|
428
|
+
- Safe numerical operations with proper edge case handling
|
|
429
|
+
- Memory-efficient implementations
|
|
430
|
+
|
|
431
|
+
### Accuracy Guarantees
|
|
432
|
+
- Maximum relative error < 10⁻¹¹ vs scalar implementations (IGRF + T96 total field)
|
|
433
|
+
- Median relative error at machine epsilon (~10⁻¹⁶)
|
|
434
|
+
- Validated across 9,792 grid points in the X-Z meridian plane
|
|
435
|
+
- Comprehensive test suite with configurable tolerances
|
|
436
|
+
- Proper handling of boundary conditions
|
|
437
|
+
|
|
438
|
+
### Performance Optimization
|
|
439
|
+
- Batch processing capabilities for millions of points
|
|
440
|
+
- Linear memory scaling with input size
|
|
441
|
+
- GPU-ready array operations
|
|
442
|
+
- Minimal Python overhead
|
|
443
|
+
|
|
444
|
+
## Attribution and Acknowledgments
|
|
445
|
+
|
|
446
|
+
This project extends the excellent Python [geopack](https://github.com/tsssss/geopack) implementation by Sheng Tian, which has been invaluable to the space physics community. The original geopack provides a robust, well-tested foundation that faithfully reproduces the Fortran implementations.
|
|
447
|
+
|
|
448
|
+
The original Fortran GEOPACK code and Tsyganenko models were developed by N.A. Tsyganenko and are available at:
|
|
449
|
+
- https://geo.phys.spbu.ru/~tsyganenko/modeling.html
|
|
450
|
+
- https://ccmc.gsfc.nasa.gov/models/
|
|
451
|
+
|
|
452
|
+
We are grateful to both Sheng Tian for the Python implementation and N.A. Tsyganenko for the original models that have been fundamental to magnetospheric physics research for decades.
|
|
453
|
+
|
|
454
|
+
## License
|
|
455
|
+
|
|
456
|
+
This project maintains the MIT License from the original geopack implementation.
|
|
457
|
+
|
|
458
|
+
## References
|
|
459
|
+
|
|
460
|
+
- Tian, S., Frissell, N., w2ruf, Lewis, J. & Lei Cai, Ph. D. tsssss/geopack: v1.0.12. Zenodo https://doi.org/10.5281/zenodo.15110787 (2025).
|
|
461
|
+
|
|
462
|
+
- Tsyganenko, N. A. (1995), "Modeling the Earth's magnetospheric magnetic field", J. Geophys. Res.
|
|
463
|
+
- Tsyganenko, N. A. (2002), "A model of the near magnetosphere with a dawn-dusk asymmetry", J. Geophys. Res.
|
|
464
|
+
- Tsyganenko, N. A. and M. I. Sitnov (2005), "Modeling the dynamics of the inner magnetosphere during strong geomagnetic storms", J. Geophys. Res.
|
|
465
|
+
- International Geomagnetic Reference Field: https://www.ngdc.noaa.gov/IAGA/vmod/igrf.html
|