Skip to content

Commit

Permalink
Merge pull request #68 from greglucas/main-namespace
Browse files Browse the repository at this point in the history
MNT/ENH: Export commonly used functions to the main namespace
  • Loading branch information
greglucas authored Nov 19, 2024
2 parents 8efe798 + a31294f commit 0156cee
Show file tree
Hide file tree
Showing 15 changed files with 176 additions and 128 deletions.
24 changes: 16 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,25 @@

All notable changes to this project will be documented in this file.

## [Unreleased]

## [v0.10.0] 2024-11-19

- **ADDED** Top-level function exports to avoid submodule imports
- `pymsis.calculate()` is the primrary entrypoint to running the MSIS
model and calculating the atmosphere at the requested data points.
- `pymsis.msis.run()` was not very descriptive and caused issues with IDL
bridging into Python and wanting that name reserved. To avoid this, the
function has been renamed `calculate` and is available as `pymsis.calculate()`
now. The `pymsis.msis.run()` is still available as an alias for now, but
may be removed in the future.
- **ADDED** Variable enumeration for easier indexing into output arrays.
- This can be used as `pymsis.Variable.O2` for getting the `O2` species index.
For example, `output_array[..., pymsis.Variable.O2]`.
- **ADDED** Python 3.13 and 3.13t support
- **ADDED** Multithreaded support.
- The underlying MSIS libraries are not threadsafe due
to the use of many global/save variables. There is a lock around the
extension modules so that only one thread will be calling the routines
at a time, so the Python library is safe to use in a multi-threaded context.
- **ADDED** Variable enumeration for easier indexing into output arrays.
- This can be used as `msis.Variable.O2` for getting the `O2` species index.
For example, `output_array[..., msis.Variable.O2]`.
- **MAINTENANCE** Default `-O1` optimization level for all builds.
- Previously, this
was only done on Windows machines. Users can change this by updating
Expand All @@ -30,7 +38,7 @@ All notable changes to this project will be documented in this file.
- **DEPRECATED** Calling `msis00f.pytselec()` and `msis00f.pygtd7d` functions.
- Use `msis00f.pyinitswitch` and `msis00f.pymsiscalc` instead.
- This helps with standardization across the extension modules. These extension
should rarely be used by external people and `msis.run()` is a better entry
should rarely be used by external people and `pymsis.calculate()` is a better entry
to using the package.

## [v0.9.0] - 2024-04-03
Expand All @@ -57,7 +65,7 @@ All notable changes to this project will be documented in this file.
## [v0.6.0] - 2022-11-14

- **ADDED** Automatic download of F10.7 and ap data for users.
- This means that F10.7 and ap are optional inputs to the `msis.run()`
- This means that F10.7 and ap are optional inputs to the `pymsis.calculate()`
function during historical periods and the routines will automatically
fetch the proper input data.

Expand All @@ -67,7 +75,7 @@ All notable changes to this project will be documented in this file.

- **ADDED** MSIS2.1, a new version of MSIS.
- This is the first version that contains NO.
- This is the new default used in `msis.run()`.
- This is the new default used in `pymsis.calculate()`.
- **MAINTENANCE** Added more wheels to the release and CI systems for testing.

## [v0.4.0] - 2022-02-26
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ there is a web viewer that uses pymsis: <https://swx-trec.com/msis>

```python
import numpy as np
from pymsis import msis
import pymsis

dates = np.arange(np.datetime64("2003-10-28T00:00"), np.datetime64("2003-11-04T00:00"), np.timedelta64(30, "m"))
# geomagnetic_activity=-1 is a storm-time run
data = msis.run(dates, 0, 0, 400, geomagnetic_activity=-1)
data = pymsis.calculate(dates, 0, 0, 400, geomagnetic_activity=-1)

# Plot the data
import matplotlib.pyplot as plt
Expand All @@ -56,7 +56,7 @@ is developed by the Naval Research Laboratory.
Note that the MSIS2 code is not available for commercial use without
contacting NRL. See the [MSIS2 license file](https://github.com/SWxTREC/pymsis/blob/main/MSIS2_LICENSE)) for explicit
details. We do not repackage the MSIS source code in this
repository for that reason. However, we do provide utilities to easily
repository for that reason. However, utility functions are provided to easily
download and extract the original source code. By using that code you
agree to their terms and conditions.

Expand Down
39 changes: 28 additions & 11 deletions docs/source/reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,47 @@ API reference
.. currentmodule:: pymsis

This page gives an overview of the routines within the pymsis package.
To run the code, use the :mod:`pymsis.msis` module.
To calculate atmospheric constituents at grid points, use the :func:`~.calculate` function.
This is typically the only function you will need to use. You can access the output variables
using the :class:`~.Variable` enumeration for easier indexing into the output data array.
For example, as ``output_array[..., Variable.MASS_DENSITY]``.

.. autosummary::
:toctree: generated/
:nosignatures:

msis.run
msis.create_options
msis.create_input
calculate
Variable

msis module
-----------

The output of the model is stored in basic numpy arrays with the final
dimension being the variable/species. To get the output in a more human-readable
format, use the :class:`~.Variable` enumeration that provides
easier indexing into the output arrays.
For more control and help creating properly formatted inputs, one can
use the :mod:`pymsis.msis` module. This module provides functions to
create input data, create the options list, and run the model.

.. autosummary::
:toctree: generated/
:nosignatures:

msis.Variable
msis.create_input
msis.create_options
msis.calculate

To get input data for historical events, use the :mod:`pymsis.utils` module.
utils module
------------

The MSIS model requires solar forcing data (F10.7 and ap) to calculate the state of the atmosphere.
When running over past time periods, this data is automatically downloaded and used if not specified by the user.
For more control over the F10.7 and Ap values the model is using, one can input the desired values
into the :func:`~.calculate` call ``calculate(..., f107s=..., f107as=..., aps=...)``.
To get the values used automatically by the model, the :mod:`pymsis.utils` module has several
helper routines to download new files (will retrieve a file with data up to the present) and
get the proper F10.7 and ap values to use for a specific date and time.

.. autosummary::
:toctree: generated/
:nosignatures:

utils.download_f107_ap
utils.get_f107_ap
utils.get_f107_ap
8 changes: 4 additions & 4 deletions examples/plot_altitude_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import matplotlib.pyplot as plt
import numpy as np

from pymsis import msis
import pymsis


lon = 0
Expand All @@ -24,17 +24,17 @@
aps = [[ap] * 7]

date = np.datetime64("2003-01-01T00:00")
output_midnight = msis.run(date, lon, lat, alts, f107, f107a, aps)
output_midnight = pymsis.calculate(date, lon, lat, alts, f107, f107a, aps)
date = np.datetime64("2003-01-01T12:00")
output_noon = msis.run(date, lon, lat, alts, f107, f107a, aps)
output_noon = pymsis.calculate(date, lon, lat, alts, f107, f107a, aps)

# output is now of the shape (1, 1, 1, 1000, 11)
# Get rid of the single dimensions
output_midnight = np.squeeze(output_midnight)
output_noon = np.squeeze(output_noon)

_, ax = plt.subplots()
for variable in msis.Variable:
for variable in pymsis.Variable:
if variable.name in ("Total mass density", "Temperature"):
# Ignore non-number densities
continue
Expand Down
6 changes: 3 additions & 3 deletions examples/plot_annual_variation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import matplotlib.pyplot as plt
import numpy as np

from pymsis import msis
import pymsis


lon = 0
Expand All @@ -27,7 +27,7 @@
f107as = [f107a] * ndates
aps = [[ap] * 7] * ndates

output = msis.run(dates, lon, lat, alt, f107s, f107as, aps)
output = pymsis.calculate(dates, lon, lat, alt, f107s, f107as, aps)
# output is now of the shape (ndates, 1, 1, 1, 11)
# Get rid of the single dimensions
output = np.squeeze(output)
Expand All @@ -36,7 +36,7 @@
variation = 100 * (output / output.mean(axis=0) - 1)

_, ax = plt.subplots()
for variable in msis.Variable:
for variable in pymsis.Variable:
if variable.name == "NO":
# There is currently no NO data
continue
Expand Down
6 changes: 3 additions & 3 deletions examples/plot_diurnal_variation.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import matplotlib.pyplot as plt
import numpy as np

from pymsis import msis
import pymsis


lon = 0
Expand All @@ -28,7 +28,7 @@
f107as = [f107a] * ndates
aps = [[ap] * 7] * ndates

output = msis.run(dates, lon, lat, alt, f107s, f107as, aps)
output = pymsis.calculate(dates, lon, lat, alt, f107s, f107as, aps)
# output is now of the shape (ndates, 1, 1, 1, 11)
# Get rid of the single dimensions
output = np.squeeze(output)
Expand All @@ -37,7 +37,7 @@
variation = 100 * (output / output.mean(axis=0) - 1)

_, ax = plt.subplots()
for variable in msis.Variable:
for variable in pymsis.Variable:
ax.plot(dates, variation[:, variable], label=variable.name)

ax.legend(
Expand Down
6 changes: 3 additions & 3 deletions examples/plot_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import matplotlib.pyplot as plt
import numpy as np

from pymsis import msis
import pymsis


lons = range(-180, 185, 5)
Expand All @@ -23,14 +23,14 @@
date = np.datetime64("2003-01-01T12:00")
aps = [[ap] * 7]

output = msis.run(date, lons, lats, alt, f107, f107a, aps)
output = pymsis.calculate(date, lons, lats, alt, f107, f107a, aps)
# output is now of the shape (1, nlons, nlats, 1, 11)
# Get rid of the single dimensions
output = np.squeeze(output)

_, ax = plt.subplots()
xx, yy = np.meshgrid(lons, lats)
mesh = ax.pcolormesh(xx, yy, output[:, :, msis.Variable.O2].T, shading="auto")
mesh = ax.pcolormesh(xx, yy, output[:, :, pymsis.Variable.O2].T, shading="auto")
plt.colorbar(mesh, label="Number density (/m$^3$)")

ax.set_title(f"O$_2$ number density at {alt} km")
Expand Down
10 changes: 5 additions & 5 deletions examples/plot_surface_animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import numpy as np
from matplotlib.animation import FuncAnimation

from pymsis import msis
import pymsis


lons = range(-180, 185, 5)
Expand All @@ -30,7 +30,7 @@
f107as = [f107a] * ndates
aps = [[ap] * 7] * ndates

output = msis.run(dates, lons, lats, alt, f107s, f107as, aps)
output = pymsis.calculate(dates, lons, lats, alt, f107s, f107as, aps)
# output is now of the shape (ndates, nlons, nlats, 1, 11)
# Get rid of the single dimensions
output = np.squeeze(output)
Expand All @@ -42,14 +42,14 @@
vmin, vmax = 1e13, 8e13
norm = matplotlib.colors.Normalize(vmin, vmax)
mesh = ax_mesh.pcolormesh(
xx, yy, output[0, :, :, msis.Variable.N].T, shading="auto", norm=norm
xx, yy, output[0, :, :, pymsis.Variable.N].T, shading="auto", norm=norm
)
plt.colorbar(
mesh, label=f"N number density at {alt} km (/m$^3$)", orientation="horizontal"
)

time_data = output[:, len(lons) // 2, len(lats) // 2, :]
ax_time.plot(dates, time_data[:, msis.Variable.N], c="k")
ax_time.plot(dates, time_data[:, pymsis.Variable.N], c="k")
ax_time.set_xlim(dates[0], dates[-1])
ax_time.set_ylim(vmin, vmax)
ax_time.xaxis.set_major_locator(mdates.HourLocator(interval=3))
Expand All @@ -74,7 +74,7 @@ def update_surface(t):
date_string = dates[t].astype("O").strftime("%H:%M")
title.set_text(f"{date_string}")
time_line.set_xdata([dates[t]])
mesh.set_array(output[t, :, :, msis.Variable.N].T)
mesh.set_array(output[t, :, :, pymsis.Variable.N].T)
sun.set_data([sun_loc[t]], [0])


Expand Down
12 changes: 6 additions & 6 deletions examples/plot_version_diff_altitude.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import matplotlib.pyplot as plt
import numpy as np

from pymsis import msis
import pymsis


lon = 0
Expand All @@ -24,13 +24,13 @@
aps = [[ap] * 7]

date = np.datetime64("2003-01-01T00:00")
output_midnight2 = msis.run(date, lon, lat, alts, f107, f107a, aps)
output_midnight0 = msis.run(date, lon, lat, alts, f107, f107a, aps, version=0)
output_midnight2 = pymsis.calculate(date, lon, lat, alts, f107, f107a, aps)
output_midnight0 = pymsis.calculate(date, lon, lat, alts, f107, f107a, aps, version=0)
diff_midnight = (output_midnight2 - output_midnight0) / output_midnight0 * 100

date = np.datetime64("2003-01-01T12:00")
output_noon2 = msis.run(date, lon, lat, alts, f107, f107a, aps)
output_noon0 = msis.run(date, lon, lat, alts, f107, f107a, aps, version=0)
output_noon2 = pymsis.calculate(date, lon, lat, alts, f107, f107a, aps)
output_noon0 = pymsis.calculate(date, lon, lat, alts, f107, f107a, aps, version=0)
diff_noon = (output_noon2 - output_noon0) / output_noon0 * 100


Expand All @@ -40,7 +40,7 @@
diff_noon = np.squeeze(diff_noon)

_, ax = plt.subplots()
for variable in msis.Variable:
for variable in pymsis.Variable:
if variable.name in ("NO", "Total mass density", "Temperature"):
# There is currently no NO data for earlier versions,
# also ignore non-number densities
Expand Down
8 changes: 4 additions & 4 deletions examples/plot_version_diff_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import matplotlib.pyplot as plt
import numpy as np

from pymsis import msis
import pymsis


lons = range(-180, 185, 5)
Expand All @@ -25,8 +25,8 @@
date = np.datetime64("2003-01-01T12:00")
aps = [[ap] * 7]

output2 = msis.run(date, lons, lats, alt, f107, f107a, aps)
output0 = msis.run(date, lons, lats, alt, f107, f107a, aps, version=0)
output2 = pymsis.calculate(date, lons, lats, alt, f107, f107a, aps)
output0 = pymsis.calculate(date, lons, lats, alt, f107, f107a, aps, version=0)
diff = (output2 - output0) / output0 * 100
# diff is now of the shape (1, nlons, nlats, 1, 11)
# Get rid of the single dimensions
Expand All @@ -36,7 +36,7 @@
xx, yy = np.meshgrid(lons, lats)
norm = mpl.colors.Normalize(-50, 50)
cmap = mpl.colormaps["RdBu_r"]
for i, variable in enumerate(msis.Variable):
for i, variable in enumerate(pymsis.Variable):
if i > 8:
break
ax = axarr.flatten()[i]
Expand Down
4 changes: 4 additions & 0 deletions pymsis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@

import importlib.metadata

from pymsis.msis import Variable, calculate


__version__ = importlib.metadata.version("pymsis")

__all__ = ["__version__", "Variable", "calculate"]
Loading

0 comments on commit 0156cee

Please sign in to comment.