Skip to content

Commit

Permalink
Merge pull request #152 from aburrell/rcv2.1.0
Browse files Browse the repository at this point in the history
Version 2.1.0 for develop
  • Loading branch information
aburrell authored Jan 7, 2025
2 parents 87146c8 + db6311f commit 85164ea
Show file tree
Hide file tree
Showing 22 changed files with 234 additions and 132 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
run: |
pip install build
python -m build .
pip install -r docs/requirements.txt
pip install .[doc]
- name: Check documentation build
run: sphinx-build -E -b html docs dist/docs
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ jobs:
# Support different GA Mac environmnets
- python-version: "3.9"
os: "macos-13"
numpy_ver: "latest"
- python-version: "3.10"
os: "macos-13"
numpy_ver: "latest"
- python-version: "3.11"
os: "macos-latest"
numpy_ver: "latest"
- python-version: "3.12"
os: "macos-latest"
numpy_ver: "latest"
# NEP29 compliance settings
- python-version: "3.10"
numpy_ver: "1.25"
Expand Down
33 changes: 19 additions & 14 deletions .zenodo.json
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
{
"license": {
"id": "MIT"
},
},
"notes": "When referencing this package, please cite both the package DOI and the Apex Coordinates journal article: Emmert, J. T., A. D. Richmond, and D. P. Drob (2010), A computationally compact representation of Magnetic-Apex and Quasi-Dipole coordinates with smooth base vectors, J. Geophys. Res., 115(A8), A08322, doi:10.1029/2010JA015326.",
"references": [
"Emmert, J. T., A. D. Richmond, and D. P. Drob (2010), A computationally compact representation of Magnetic-Apex and Quasi-Dipole coordinates with smooth base vectors, J. Geophys. Res., 115(A8), A08322, doi:10.1029/2010JA015326.",
"Richmond, A. D. (1995), Ionospheric Electrodynamics Using Magnetic Apex Coordinates, Journal of geomagnetism and geoelectricity, 47(2), 191–212, doi:10.5636/jgg.47.191."
],
],
"keywords": [
"Magnetic Apex Coordinates",
"Quasi Dipole Coordinates",
"MLT",
"Magnetic Local Time",
"Conversion",
"Coordinate Conversion",
"Converting",
"Ionosphere",
"Magnetic Field",
"Space Physics",
"MLT",
"Magnetic Local Time",
"Conversion",
"Coordinate Conversion",
"Converting",
"Ionosphere",
"Magnetic Field",
"Space Physics",
"Heliophysics"
],
],
"creators": [
{
"orcid": "0000-0002-8043-0953",
"orcid": "0000-0002-8043-0953",
"name": "van der Meeren, Christer"
},
{
"orcid": "0000-0001-5028-4943",
"name": "Laundal, Karl M."
},
{
"orcid": "0000-0001-8875-9326",
"affiliation": "Naval Research Laboratory",
"orcid": "0000-0001-8875-9326",
"affiliation": "Naval Research Laboratory",
"name": "Burrell, Angeline G."
},
{
Expand All @@ -55,6 +55,11 @@
"orcid": "0000-0001-9741-4063",
"affiliation": "GFZ German Research Centre for Geosciences",
"name": "Michaelis, Ingo"
},
{
"orcid": "0000-0001-8321-6074",
"affiliation": "Goddard Space Flight Center",
"name": "Klenzing, Jeff"
}
]
}
6 changes: 5 additions & 1 deletion AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ This python wrapper is made by:
* Ashton Reimer
* Achim Morschhauser
* Ingo Michaelis
* Jeff Klenzing

Fortran code by Emmert et al. [2010] [1]_. Quasi-dipole and modified
apex coordinates are defined by Richmond [1995] [2]_. The code uses
IGRF-12 with coefficients valid through 2020 [Thébault et al., 2015] [3]_.
IGRF-14 with coefficients valid through 2030. A special issue on IGRF-14 is
currently accepting
`submissions <https://www.springeropen.com/collections/IGRF14>`_. A reference
for IGRF-12 is [Thébault et al., 2015] [3]_.

.. [1] Emmert, J. T., A. D. Richmond, and D. P. Drob (2010),
A computationally compact representation of Magnetic-Apex
Expand Down
9 changes: 7 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@
Changelog
=========

2.1.0 (2024-12-XX)
2.1.0 (2025-01-07)
------------------
* Adapted codebase to read IRGF coefficients from a file (updated to IGRF-14)
* Updated docs to reflect missing command-line executable
* Updated package to be compliant and installable with numpy 2.0+
* Added tests for Python 3.12 and NEP29
* Fixed link to logo in the README
* Updated pyproject.toml to include most metadata instead of setup.cfg
* Added a citation section to the docs
* Fixed the command-line executable
* Updated code to address deprecation warnings around np.float64 use
* Updated code to remove use of datetime `utcnow`
* Updated meson build requirements to include ninja for Windows builds instead of python-dev-tools

2.0.2 (2024-11-12)
------------------
Expand Down
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ graft docs
graft fortranapex
graft .github

recursive-include apexpy *.txt meson.build
recursive-include apexpy *.txt apexsh.dat meson.build

include *.rst
include *.md
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -143,5 +143,5 @@ Badges
.. |doi| image:: https://www.zenodo.org/badge/doi/10.5281/zenodo.4585641.svg
:target: https://doi.org/10.5281/zenodo.1214206

.. |logo| image:: https://github.com/aburrell/apexpy/blob/main/docs/apexpy.png
.. |logo| image:: https://github.com/aburrell/apexpy/blob/main/docs/apexpy.png?raw=true
:alt: ApexPy logo: yellow magnetic field lines surrounding the Earth's surface, which is blue
8 changes: 7 additions & 1 deletion apexpy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Conversion functions between geodetic and apex magnetic coordinates."""
from importlib import metadata
from sys import stderr

# Below try..catch required for autodoc to work on readthedocs
Expand All @@ -12,5 +14,9 @@
from apexpy import helpers # noqa F401

# Define the global variables
__version__ = "2.0.1"
try:
__version__ = metadata.version('apexpy')
except metadata.PackageNotFoundError:
# Windows installation is not finding the version automatically
__version__ = "2.1.0"
__all__ = ['Apex', 'fortranapex', 'helpers', 'ApexHeightError']
8 changes: 6 additions & 2 deletions apexpy/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,12 @@ def main():
lats, lons = apex_obj.convert(arg_array[:, 0], arg_array[:, 1], args.source,
args.dest, args.height, datetime=in_time)

# Save the output to a file
np.savetxt(args.file_out, np.column_stack((lats, lons)), fmt='%.8f')
# Save the output to a file. Use the name for non-stdout inputs
if args.file_out.name.lower().find('stdout') >= 0:
fout_name = args.file_out
else:
fout_name = args.file_out.name
np.savetxt(fout_name, np.column_stack((lats, lons)), fmt='%.8f')

return

Expand Down
57 changes: 40 additions & 17 deletions apexpy/apex.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None):
# If datafile is not specified, use the package default, otherwise
# check that the provided file exists
if datafile is None:
datafile = str(resources.path(__package__,
'apexsh.dat').__enter__())
datafile = os.path.join(resources.files(__package__), 'apexsh.dat')
else:
if not os.path.isfile(datafile):
raise IOError('Data file does not exist: {}'.format(datafile))
Expand All @@ -120,8 +119,8 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None):
self.fortranlib = fortranlib

# Set the IGRF coefficient text file name
self.igrf_fn = str(resources.path(__package__,
'igrf14coeffs.txt').__enter__())
self.igrf_fn = os.path.join(resources.files(__package__),
'igrf14coeffs.txt')

# Update the Fortran epoch using the year defined above
self.set_epoch(self.year)
Expand Down Expand Up @@ -565,7 +564,10 @@ def geo2apex(self, glat, glon, height):
alat[alat == -9999] = np.nan

# If array is returned, dtype is object, so convert to float
return np.float64(alat), np.float64(alon)
alat = helpers.set_array_float(alat)
alon = helpers.set_array_float(alon)

return alat, alon

def apex2geo(self, alat, alon, height, precision=1e-10):
"""Converts modified apex to geodetic coordinates.
Expand Down Expand Up @@ -634,7 +636,10 @@ def geo2qd(self, glat, glon, height):
qlat, qlon = self._geo2qd(glat, glon, height)

# If array is returned, dtype is object, so convert to float
return np.float64(qlat), np.float64(qlon)
qlat = helpers.set_array_float(qlat)
qlon = helpers.set_array_float(qlon)

return qlat, qlon

def qd2geo(self, qlat, qlon, height, precision=1e-10):
"""Converts quasi-dipole to geodetic coordinates.
Expand Down Expand Up @@ -674,7 +679,11 @@ def qd2geo(self, qlat, qlon, height, precision=1e-10):
glat, glon, error = self._qd2geo(qlat, qlon, height, precision)

# If array is returned, dtype is object, so convert to float
return np.float64(glat), np.float64(glon), np.float64(error)
glat = helpers.set_array_float(glat)
glon = helpers.set_array_float(glon)
error = helpers.set_array_float(error)

return glat, glon, error

def apex2qd(self, alat, alon, height):
"""Converts modified apex to quasi-dipole coordinates.
Expand Down Expand Up @@ -706,7 +715,10 @@ def apex2qd(self, alat, alon, height):
qlat, qlon = self._apex2qd(alat, alon, height)

# If array is returned, the dtype is object, so convert to float
return np.float64(qlat), np.float64(qlon)
qlat = helpers.set_array_float(qlat)
qlon = helpers.set_array_float(qlon)

return qlat, qlon

def qd2apex(self, qlat, qlon, height):
"""Converts quasi-dipole to modified apex coordinates.
Expand Down Expand Up @@ -737,7 +749,10 @@ def qd2apex(self, qlat, qlon, height):
alat, alon = self._qd2apex(qlat, qlon, height)

# If array is returned, the dtype is object, so convert to float
return np.float64(alat), np.float64(alon)
alat = helpers.set_array_float(alat)
alon = helpers.set_array_float(alon)

return alat, alon

def mlon2mlt(self, mlon, dtime, ssheight=318550):
"""Computes the magnetic local time at the specified magnetic longitude
Expand Down Expand Up @@ -777,8 +792,11 @@ def mlon2mlt(self, mlon, dtime, ssheight=318550):
_, ssalon = self.geo2apex(ssglat, ssglon, ssheight)

# Calculate the magnetic local time (0-24 h range) from apex longitude.
# np.float64 will ensure lists are converted to arrays
mlt = (180 + np.float64(mlon) - ssalon) / 15 % 24
# Ensure lists are converted to arrays
mlt = (180 + np.asarray(mlon) - ssalon) / 15 % 24

if mlt.shape == ():
mlt = np.float64(mlt)

return mlt

Expand Down Expand Up @@ -818,8 +836,11 @@ def mlt2mlon(self, mlt, dtime, ssheight=318550):
_, ssalon = self.geo2apex(ssglat, ssglon, ssheight)

# Calculate the magnetic longitude (0-360 h range) from MLT.
# np.float64 will ensure lists are converted to arrays
mlon = (15 * np.float64(mlt) - 180 + ssalon + 360) % 360
# Ensure lists are converted to arrays
mlon = (15 * np.asarray(mlt) - 180 + ssalon + 360) % 360

if mlon.shape == ():
mlon = np.float64(mlon)

return mlon

Expand Down Expand Up @@ -1130,10 +1151,10 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10):
k_unit = np.array([0, 0, 1], dtype=np.float64).reshape((3, 1))

# Calculate the remaining quasi-dipole base vectors
g1 = ((self.RE + np.float64(height))
g1 = ((self.RE + np.asarray(height))
/ (self.RE + self.refh)) ** (3 / 2) * d1 / F_scalar
g2 = -1.0 / (2.0 * F_scalar * np.tan(np.radians(qlat))) * (
k_unit + ((self.RE + np.float64(height))
k_unit + ((self.RE + np.asarray(height))
/ (self.RE + self.refh)) * d2 / cos_mag_inc)
g3 = k_unit * F_scalar
f3 = np.cross(g1.T, g2.T).T
Expand All @@ -1154,7 +1175,7 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10):
return out

def get_apex(self, lat, height=None):
""" Calculate apex height
"""Calculate the apex height along a field line.
Parameters
----------
Expand Down Expand Up @@ -1264,7 +1285,9 @@ def get_babs(self, glat, glon, height):
babs = self._get_babs(glat, glon, height)

# If array is returned, the dtype is object, so convert to float
return np.float64(babs)
babs = helpers.set_array_float(babs)

return babs

def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10):
"""Returns the magnetic field vectors in apex coordinates.
Expand Down
25 changes: 24 additions & 1 deletion apexpy/helpers.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
# -*- coding: utf-8 -*-

"""This module contains helper functions used by :class:`~apexpy.Apex`."""

import datetime as dt
import numpy as np
import time


def set_array_float(in_val):
"""Set array data type to float.
Parameters
----------
in_val : any
Input value, only modified if it is a np.ndarray
Returns
-------
out_val : any
Output value, if `in_val` was an array, `out_val` will be an array of
type `np.float64`.
"""

if isinstance(in_val, np.ndarray):
out_val = in_val.astype(np.float64)
else:
out_val = in_val

return out_val


def checklat(lat, name='lat'):
"""Makes sure the latitude is inside [-90, 90], clipping close values
(tolerance 1e-4).
Expand Down
3 changes: 2 additions & 1 deletion apexpy/tests/test_fortranapex.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from numpy.testing import assert_allclose
from importlib import resources
import os
import pytest

import apexpy
Expand All @@ -25,7 +26,7 @@ class TestFortranApex(object):

def setup_method(self):
"""Initialize each test."""
datafile = str(resources.path(apexpy, 'apexsh.dat').__enter__())
datafile = os.path.join(resources.files(apexpy), 'apexsh.dat')
fa.loadapxsh(datafile, 2000)

# Set the inputs
Expand Down
Loading

0 comments on commit 85164ea

Please sign in to comment.