Example #1
0
def test_run_options(input_data):
    # Default options is all 1's, so make sure they are equivalent
    assert_array_equal(msis.run(*input_data, options=None),
                       msis.run(*input_data, options=[1] * 25))

    with pytest.raises(ValueError, match='options needs to be a list'):
        msis.run(*input_data, options=[1] * 22)
Example #2
0
def test_run_versions(input_data):
    # Make sure we accept these version numbers and don't throw an error
    for ver in [0, 2, '00', '2']:
        msis.run(*input_data, version=ver)
    # Test for something outside of that list for an error to be thrown
    with pytest.raises(ValueError, match='The MSIS version selected'):
        msis.run(*input_data, version=1)
Example #3
0
def test_run_wrapped_lon(input_data, expected_output):
    date, _, lat, alt, f107, f107a, ap = input_data

    input_data = (date, -180, lat, alt, f107, f107a, ap)
    output1 = msis.run(*input_data)
    input_data = (date, 180, lat, alt, f107, f107a, ap)
    output2 = msis.run(*input_data)
    assert_allclose(output1, output2, rtol=1e-5)

    input_data = (date, 0, lat, alt, f107, f107a, ap)
    output1 = msis.run(*input_data)
    input_data = (date, 360, lat, alt, f107, f107a, ap)
    output2 = msis.run(*input_data)
    assert_allclose(output1, output2, rtol=1e-5)
Example #4
0
def run_msis(dates, lons, lats, alts, f107s, f107as, aps, options):
    """Call MSIS looping over all possible inputs.

    Parameters
    ----------
    dates : list of dates
        Date and time to calculate the output at.
    lons : list of floats
        Longitudes to calculate the output at.
    lats : list of floats
        Latitudes to calculate the output at.
    alts : list of floats
        Altitudes to calculate the output at.
    f107s : list of floats
        F107 value for the given date(s).
    f107as : list of floats
        F107 running 100-day average for the given date(s).
    aps : list of floats
        Ap for the given date(s).
    options : list of floats (length 25)
        A list of options (switches) to the model
    """
    # 11 == 10 densities + 1 temperature
    # Output density: He, O, N2, O2, Ar, Total (gm/cm3), H, N, Anomalous O
    # Output temp: exospheric, specific altitude

    _, input_data = msis.create_input(dates, lons, lats, alts, f107s, f107as,
                                      aps)

    output = msis.run(dates, lons, lats, alts, f107s, f107as, aps, options)

    # Force to float, JSON serializer in future calls does not work
    # with float32 output
    # Return the altitutdes, latitudes, longitudes with the data
    return (input_data[:, 2:5], output.reshape(-1, 11).astype(np.float))
Example #5
0
def test_run_poles(input_data):
    # Test that moving in longitude around a pole
    # returns the same values
    # North pole
    date, _, _, alt, f107, f107a, ap = input_data
    input_data = (date, 0, 90, alt, f107, f107a, ap)
    output1 = msis.run(*input_data)
    input_data = (date, 20, 90, alt, f107, f107a, ap)
    output2 = msis.run(*input_data)
    assert_allclose(output1, output2, rtol=1e-5)

    # South pole
    input_data = (date, 0, -90, alt, f107, f107a, ap)
    output1 = msis.run(*input_data)
    input_data = (date, 20, -90, alt, f107, f107a, ap)
    output2 = msis.run(*input_data)
    assert_allclose(output1, output2, rtol=1e-5)
Example #6
0
def test_run_multi_point00(input_data, expected_output00):
    date, lon, lat, alt, f107, f107a, ap = input_data
    # 5 x 5 surface
    input_data = (date, [lon] * 5, [lat] * 5, alt, f107, f107a, ap)
    output = msis.run(*input_data, version=0)
    assert output.shape == (1, 5, 5, 1, 11)
    expected = np.tile(expected_output00, (5, 5, 1))
    assert_allclose(np.squeeze(output), expected, rtol=1e-5)
Example #7
0
def test_run_multi_point(input_data, expected_output):
    # test multi-point run, like a satellite fly-through
    # and make sure we don't grid the input data
    # 5 input points
    date, lon, lat, alt, f107, f107a, ap = input_data
    input_data = ([date] * 5, [lon] * 5, [lat] * 5, [alt] * 5, [f107] * 5,
                  [f107a] * 5, ap * 5)
    output = msis.run(*input_data)
    assert output.shape == (5, 11)
    expected = np.tile(expected_output, (5, 1))
    assert_allclose(np.squeeze(output), expected, rtol=1e-5)
Example #8
0
def run_input_line(line):
    items = line.split()
    expected = np.array(items[9:], dtype=np.float32)

    # Needs to be "-3:" due to strip() taking off leading spaces
    doy = int(items[0][-3:]) - 1  # Python wants DOY to start with 0
    sec, alt, lat, lon, _, f107a, f107, ap = [float(x) for x in items[1:9]]
    ap = [[ap] * 7]
    year = int(items[0][:-3])
    # Two digit year
    if year > 60:
        year += 1900
    else:
        year += 2000
    date = (np.datetime64(str(year)) + np.timedelta64(doy, 'D') +
            np.timedelta64(int(sec), 's'))

    test_inp = (date, lon, lat, alt, f107, f107a, ap)
    test_output = np.squeeze(msis.run(*test_inp))

    # Rearrange the run's output to match the expected
    # ordering of elements
    x = np.array([
        test_output[4],
        test_output[3],
        test_output[1],
        test_output[2],
        test_output[6],
        test_output[0],
        test_output[5],
        test_output[7],
        test_output[8],
        test_output[10],
    ])
    # Different units in the test file (cgs)
    x[np.isclose(x, 9.9e-38, atol=1e-38)] = np.nan
    x[:-1] *= 1e-6
    x[5] *= 1e3
    x[np.isnan(x)] = 9.999e-38
    # The output print statements are messy.
    # They technically give 4 decimal places, but only truly 3 decimal places
    # in scientific notation. Example: 0.8888e+23 -> 8.888e+24
    # We will require relative comparisons of 2e-3 to account for the
    # last digit errors.
    assert_allclose(x, expected, rtol=2e-3)
Example #9
0
def lambda_handler(event, context):
    """Handle the incoming API event and route properly."""
    # Parse the incoming path and route the event properly
    if 'surface' in event['path']:
        return surface_handler(event, context)

    if 'altitude' in event['path']:
        return altitude_handler(event, context)

    # Data is passed in the body, so pull it out of there
    data = json.loads(event['body'])
    # Validate the event
    validate_event(data)

    # Convert the dates
    data['dates'] = [
        datetime.strptime(x, "%Y-%m-%dT%H:%M") for x in data['dates']
    ]

    # Initialize the model, default is all ones
    options = data.get("options", [1] * 25)
    if len(options) != 25:
        raise ValueError("options requires a length 25 array")

    # 1 Ap for each date, need to make it a n x 7 array
    aps = [[ap] * 7 for ap in data['aps']]

    # Call the main loop
    output = msis.run(data['dates'], data['lons'], data['lats'], data['alts'],
                      data['f107s'], data['f107as'], aps, options)

    return {
        'statusCode': 200,
        'body': json.dumps(output.tolist()).replace("NaN", 'null'),
        "headers": {
            "Access-Control-Allow-Origin": "*",
            "content-type": "application/json"
        }
    }
Example #10
0
import matplotlib.pyplot as plt
import numpy as np

from pymsis import msis


lon = 0
lat = 70
alts = np.linspace(0, 1000, 1000)
f107 = 150
f107a = 150
ap = 7
aps = [[ap]*7]

date = np.datetime64('2003-01-01T00:00')
output_midnight = msis.run(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 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)

variables = ['Total mass density', 'N2', 'O2', 'O', 'He',
             'H', 'Ar', 'N', 'Anomalous O', 'NO', 'Temperature']

_, ax = plt.subplots()
for i, label in enumerate(variables):
    if label in ('NO', 'Total mass density', 'Temperature'):
        # There is currently no NO data, also ignore non-number densities
Example #11
0
lons = range(-180, 185, 5)
lats = range(-90, 95, 5)
alt = 200
f107 = 150
f107a = 150
ap = 4
# Diurnal data
dates = np.arange('2003-01-01', '2003-01-02', dtype='datetime64[30m]')
ndates = len(dates)
# (F107, F107a, ap) all need to be specified at the same length as dates
f107s = [f107] * ndates
f107as = [f107a] * ndates
aps = [[ap] * 7] * ndates

output = msis.run(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)

i = 7  # N

fig, (ax_time, ax_mesh) = plt.subplots(nrows=2,
                                       gridspec_kw={'height_ratios': [1, 4]},
                                       constrained_layout=True)
xx, yy = np.meshgrid(lons, lats)
vmin, vmax = 1e13, 8e13
norm = matplotlib.colors.Normalize(vmin, vmax)
mesh = ax_mesh.pcolormesh(xx,
                          yy,
                          output[0, :, :, i].T,
Example #12
0
"""
import matplotlib.pyplot as plt
import numpy as np

from pymsis import msis

lon = 0
lat = 70
alts = np.linspace(0, 1000, 1000)
f107 = 150
f107a = 150
ap = 7
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)
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)
diff_noon = (output_noon2 - output_noon0) / output_noon0 * 100

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

variables = [
    'Total mass density', 'N2', 'O2', 'O', 'He', 'H', 'Ar', 'N', 'Anomalous O',
Example #13
0
import matplotlib.pyplot as plt
import numpy as np

from pymsis import msis

lons = range(-180, 185, 5)
lats = range(-90, 95, 5)
alt = 200
f107 = 150
f107a = 150
ap = 7
# One years worth of data at the 12th hour every day
date = np.datetime64('2003-01-01T12:00')
aps = [[ap] * 7]

output = msis.run(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)

i = 2  # O2

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

ax.set_title(f"O$_2$ number density at {alt} km")
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
Example #14
0
def test_run_version00_low_altitude(input_data):
    # There is no O, H, N below 72.5 km, should be NaN
    date, lon, lat, _, f107, f107a, ap = input_data
    input_data = (date, lon, lat, 71, f107, f107a, ap)
    output = msis.run(*input_data, version=0)
    assert np.all(np.isnan(np.squeeze(output)[[3, 5, 7]]))
Example #15
0
def test_run_version00(input_data, expected_output00):
    output = msis.run(*input_data, version=0)
    assert output.shape == (1, 11)
    assert_allclose(np.squeeze(output), expected_output00, rtol=1e-5)
Example #16
0
import matplotlib.pyplot as plt
import numpy as np

from pymsis import msis

lons = range(-180, 185, 5)
lats = range(-90, 95, 5)
alt = 200
f107 = 150
f107a = 150
ap = 7
# One years worth of data at the 12th hour every day
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)
diff = (output2 - output0) / output0 * 100
#  diff is now of the shape (1, nlons, nlats, 1, 11)
# Get rid of the single dimensions
diff = np.squeeze(diff)

variables = [
    'Total mass density', 'N2', 'O2', 'O', 'He', 'H', 'Ar', 'N', 'Anomalous O',
    'NO', 'Temperature'
]

fig, axarr = plt.subplots(nrows=3,
                          ncols=3,
                          constrained_layout=True,
                          figsize=(8, 6))
Example #17
0
def test_run_single_point(input_data, expected_output):
    output = msis.run(*input_data)
    assert output.shape == (1, 11)
    assert_allclose(np.squeeze(output), expected_output, rtol=1e-5)