Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ jobs:
- name: Set up the data directory
run: |
mkdir gdmData
python -c "import GeospaceDataManagement as gdm; gdm.params['data_dirs'] = './gdmData'; print(gdm.params); print(gdm.citation); import os; os.path.isfile(gdm.citation)"
ls ~/.gdm
python -c "import GeospaceDataManagement as gdm; gdm.params['data_dirs'] = './gdmData'"

- name: Check documentation build
run: sphinx-build -E -b html docs dist/docs
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
a variable did not exist
* DOC:
- Added missing sections to docstrings
- Added information about space weather instruments and methods

[0.0.1] - 2026-09-25
--------------------
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[![Pytest with Flake8](https://github.com/USNavalResearchLaboratory/GeospaceDataManagement/actions/workflows/main.yml/badge.svg)](https://github.com/USNavalResearchLaboratory/GeospaceDataManagement/actions/workflows/main.yml) [![Coverage Status](https://coveralls.io/repos/github/USNavalResearchLaboratory/GeospaceDataManagement/badge.svg)](https://coveralls.io/github/USNavalResearchLaboratory/GeospaceDataManagement)

GeospaceDataManagement provides a simple and flexible interface for robust data
analysis from beginning to end - with a focus on downloading, loading, cleaning
and managing scientific data. The project's plug-in design allows analysis
Expand Down
12 changes: 12 additions & 0 deletions docs/examples.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Examples
========

Here are some examples that demonstrate how to use various tools and features.

.. toctree::
examples/ex_sw_init.rst
examples/ex_f107.rst
examples/ex_kp.rst
examples/ex_ace.rst
examples/ex_mult_downloads.rst
examples/ex_mock_downloads.rst
86 changes: 86 additions & 0 deletions docs/examples/ex_ace.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
.. _exace:

Make ACE SWEPAM behave like OMNI Data
=====================================

NASA OMNI data is *the* place to go for solar wind (SW) parameters. However,
this data set is not available in real time. Fortunately, we can use the
`OMNI processing <https://omniweb.gsfc.nasa.gov/html/omni_min_data.html>`_ to
normalize the ACE SWEPAM SW proton density and ion temperature. This is
performed using the :py:func:`GeospaceDataManagement.instruments.methods.ace.ace_swepam_hourly_omni_norm`
function. The example below uses historic ACE SWEPAM data so that we can
compare the results to OMNI.

::


import datetime as dt
import matplotlib as mpl
import matplotlib.pyplot as plt

import GeospaceDataManagement as gdm

ace = gdm.Instrument(inst_module=gdm.instruments.ace_swepam,
tag='historic', update_files=True)

# Add the OMNI conversion routine to the ACE instrument
ace.custom_attach(gdm.instruments.methods.ace.ace_swepam_hourly_omni_norm)

# Pick a day with a geomagnetic storm
stime = dt.datetime(2014, 3, 26)

# If needed, download the data
if stime not in ace.files.files.index:
ace.download(start=stime)

# Load the data
ace.load(date=stime)

print(ace.variables)


This should yield ``Index(['jd', 'sec', 'status', 'sw_proton_dens',
'sw_bulk_speed', 'sw_ion_temp', 'sw_proton_dens_norm', 'sw_ion_temp_norm'],
dtype='object')``. The variables with the ``'norm'`` suffix were added by the
conversion function. Also get the OMNI Data. Now, let's plot the two data sets
together.

::


fig = plt.figure()
fig.suptitle("L1 ACE and L2 OMNI HRO data for {:}".format(stime.date()))
ax_dens = fig.add_subplot(211)
ax_temp = fig.add_subplot(212)

ax_dens.plot(ace.index, ace['sw_proton_dens_norm'], 'k-', label='ACE')
ax_dens.plot(omni.index, omni['proton_density'], 'b-', label='OMNI')
ax_temp.plot(ace.index, ace['sw_ion_temp_norm'], 'k-', label='ACE')
ax_temp.plot(omni.index, omni['T'], 'b-', label='OMNI')
ax_temp.set_yscale('log')
ax_dens.xaxis.set_major_formatter(mpl.dates.DateFormatter(''))
ax_temp.xaxis.set_major_formatter(mpl.dates.DateFormatter('%H:%M'))
ax_dens.set_xlim(stime, stime + dt.timedelta(days=1))
ax_temp.set_xlim(stime, stime + dt.timedelta(days=1))
ax_temp.set_xlabel('Universal Time (HH:MM)')
ax_dens.set_ylabel(r'SW N$_p$ ({:s})'.format(omni.meta['proton_density',
'units']))
ax_temp.set_ylabel(r'SW T$_i$ ({:s})'.format(omni.meta['T', 'units']))
ax_dens.legend(loc=1, fontsize='medium', ncol=2)

# If not running in interactive mode
plt.show()


This figure shows that the temperature adjustment agrees relatively well, but
that the proton density has a large offset. This descrepency can be attributed
to the data source. The :py:mod:`GeospaceDataManagement.instruments.ace_swepam`
module uses L1 data, while OMNI uses L2 data that has undergone additional
processing. For this time period most of the temperature data is of high
quality in the L2 data, but all the proton density data is flagged for removal.
The ACE L2 data is accessible through CDAWeb or the `ACE database
<https://izw1.caltech.edu/ACE/ASC/level2/index.html>`_.

.. image:: ../images/ex_ace_omni.png
:align: center

88 changes: 88 additions & 0 deletions docs/examples/ex_f107.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
.. _exf107:

Load F\ :sub:`10.7`\ from Multiple Sources
==========================================

As shown in :ref:`exswinit`, there are several sources that provide
F\ :sub:`10.7`\ over different time periods. It can be useful to combine these
data sets into a single :py:class:`GeospaceDataManagement.Instrument` object.
This may be done using the
:py:func:`~GeospaceDataManagement.instruments.methods.f107.combine_f107`
function.

::


import datetime as dt
import matplotlib as mpl
import matplotlib.pyplot as plt

import GeospaceDataManagement as gdm

f107_hist = gdm.Instrument(inst_module=gdm.instruments.sw_f107,
tag='historic', update_files=True)
f107_prel = gdm.Instrument(inst_module=gdm.instruments.sw_f107,
tag='prelim', update_files=True)
f107_fore = gdm.Instrument(inst_module=gdm.instruments.sw_f107,
tag='45day', update_files=True)

# If needed, download the data
f107_hist.download(start=f107_hist.lasp_stime, stop=dt.datetime(2018, 4, 1))
f107_prel.download(start=f107_hist.files.files.index[-1],
stop=f107_prel.today())
f107_fore.download(start=f107_fore.today())

# Check the downloaded file range for the historic source
print(f107_hist.files.files.index.min(), f107_hist.files.files.index.max())


This should yield ``1947-02-01 00:00:00 2018-04-30 00:00:00``. Now, combine the
data in these :py:class:`GeospaceDataManagement.Instruments` using the
:py:func:`~GeospaceDataManagement.instruments.methods.f107.combine_f107`
function twice, after loading the available data

::

# Load all the data
f107_hist.load(date=f107_hist.lasp_stime,
end_date=f107_hist.files.files.index.max() +
dt.timedelta(days=1))
f107_prel.load(fname=f107_prel.files.files[0],
stop_fname=f107_prel.files.files[-1])
f107_fore.load(date=f107_fore.today())

# Combine the historic sources for all available times
f107 = gdm.instruments.methods.f107.combine_f107(f107_hist, f107_prel)

# Combine the 45 day measurements and forecast
f107 = gdm.instruments.methods.f107.combine_f107(f107, f107_fore)

# Check the combined Instrument index
print(f107.index[0], f107.index[-1])


This print statement yields ``1947-02-14 00:00:00 2022-10-07 00:00:00``, where
the end date is roughly 45 days in the future to account for the forecasted
data. The combined :py:class:`GeospaceDataManagement.Instrument` varialbe,
``f107`` can be used to plot the F\ :sub:`10.7`\ over time.

::


fig = plt.figure()
ax = fig.add_subplot(111)

ax.plot(f107.index, f107['f107'], 'k.', ms=1)
ax.xaxis.set_major_formatter(mpl.dates.DateFormatter('%Y'))
ax.xaxis.set_major_locator(mpl.dates.YearLocator(11))
ax.set_xlim(f107.index[0], f107.index[-1])
ax.set_xlabel('Universal Time (Year C.E.)')
ax.set_ylabel(r'F$_{10.7}$ (sfu)')

# If not running in interactive mode
plt.show()


.. image:: ../images/ex_f107_all.png
:align: center

97 changes: 97 additions & 0 deletions docs/examples/ex_kp.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
.. _exkp:

Load Kp from Multiple Sources
=============================

Following from the previous F\ :sub:`10.7`\ example, :ref:`exf107`, the space
weather methods also have a routine to combine several sources of Kp over
different time periods. This may be done using
:py:func:`~GeospaceDataManagement.instruments.methods.kp_ap.combine_kp`.

::


import datetime as dt
import matplotlib as mpl
import matplotlib.pyplot as plt

import GeospaceDataManagement as gdm

kp_his = gdm.Instrument(inst_module=gdm.instruments.sw_kp,
tag='def', update_files=True)
kp_rec = gdm.Instrument(inst_module=gdm.instruments.sw_kp,
tag='recent', update_files=True)
kp_for = gdm.Instrument(inst_module=gdm.instruments.sw_kp,
tag='forecast', update_files=True)

# Set the time range
stime = dt.datetime(1999, 1, 1)
etime = kp_for.today()

# If needed, download the data
kp_his.download(start=stime, stop=etime)
kp_rec.download(start=stime, stop=etime)
kp_for.download(start=etime)

# Combine the Kp sources for all available times
kp = gdm.instruments.methods.kp_ap.combine_kp(kp_his, kp_rec, kp_for,
stime, etime)

# Check the combined Instrument index
print(kp.index[0], kp.index[-1])


This yields ``1999-01-01 00:00:00 2022-08-23 21:00:00``, where the last date is
your current date (not the date that I created this example). Now, we can take
the combined :py:class:`GeospaceDataManagement.Instrument` can be used to plot
the Kp over time.

::


fig = plt.figure()
ax = fig.add_subplot(111)

# Color code data by activity level
ax.bar(kp.data.loc[kp['Kp'] < 4, 'Kp'].index,
kp.data.loc[kp['Kp'] < 4, 'Kp'], color='green', label='Quiet')
ax.bar(kp.data.loc[(kp['Kp'] >= 4) & (kp['Kp'] < 7), 'Kp'].index,
kp.data.loc[(kp['Kp'] >= 4) & (kp['Kp'] < 7), 'Kp'], color='orange',
label='Active')
ax.bar(kp.data.loc[kp['Kp'] >= 7, 'Kp'].index,
kp.data.loc[kp['Kp'] >= 7, 'Kp'], color='firebrick', label='Storm')

# Format the figure
ax.set_xlim(stime, etime)
ax.set_ylim(0, 9)
ax.xaxis.set_major_formatter(mpl.dates.DateFormatter('%Y'))
ax.set_xlabel('Universal Time (Year C.E.)')
ax.set_ylabel(r'Kp')
ax.legend(loc=1, fontsize='medium')

# If not running in interactive mode
plt.show()


.. image:: ../images/ex_kp_all.png
:align: center



Convert Kp to ap
================

The Kp and ap have a well established relationship, which takes the logarithmic
Kp index and converts it to a linear scale that is easier to handle numerically.
The :py:func:`~GeospaceDataManagement.instruments.methods.kp_ap.convert_3hr_kp_to_ap`
converts Kp to ap, as shown below.

::


gdm.instruments.methods.kp_ap.convert_3hr_kp_to_ap(kp)

print("Max: {:.1f} -> {:.1f}, Min: {:.1f} -> {:.1f}".format(
kp['Kp'].max(), kp['3hr_ap'].max(), kp['Kp'].min(), kp['3hr_ap'].min()))

This yields ``Max: 9.0 -> 400.0, Min: 0.0 -> 0.0``.
Loading
Loading