Skip to content

Commit

Permalink
Merge pull request #114 from rkitano-1/develop
Browse files Browse the repository at this point in the history
ENH: Issue #87
  • Loading branch information
jklenzing committed Aug 3, 2020
2 parents 3bbf8dc + e0a4bf9 commit 4c57d61
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 27 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [next version] - 2020-07-27
- Using numpy 1.16 as minimum test version in accordance with NEP 29
- Removed the sami2py-1.00.namelist and version.txt files from run_name
- Added ability to input custom ExB Drifts as a Fourier Series
- return_fourier function in utils.py
- plot_exb function in _core_class.py
- Testing return_fourier function in test_utils.py

## [0.2.2] - 2020-07-17
- Added simple port of core data to netcdf file
Expand Down
50 changes: 25 additions & 25 deletions sami2py/_core_class.py
Expand Up @@ -4,15 +4,12 @@
# Full license can be found in License.md
# -----------------------------------------------------------------------------
"""Wrapper for running sami2 model
Classes
-------------------------------------------------------------------------------
Model
Loads, reshapes, and holds SAMI2 output for a given model run
specified by the user.
-------------------------------------------------------------------------------
Moduleauthor
-------------------------------------------------------------------------------
Jeff Klenzing (JK), 1 Dec 2017, Goddard Space Flight Center (GSFC)
Expand All @@ -21,7 +18,7 @@
from os import path
import numpy as np
import xarray as xr
from sami2py.utils import generate_path, get_unformatted_data
from sami2py.utils import generate_path, get_unformatted_data, return_fourier


class Model(object):
Expand All @@ -30,7 +27,6 @@ class Model(object):
def __init__(self, tag, lon, year, day, outn=False, test=False):
""" Loads a previously run sami2 model and sorts into
appropriate array shapes
Parameters
----------
tag : (string)
Expand All @@ -47,25 +43,21 @@ def __init__(self, tag, lon, year, day, outn=False, test=False):
test : (boolean)
if true : use test model output
if false : look for user made model output
Returns
-------
self : model class object containing OCB file data
Attributes
----------
ut : (1D ndarray)
Universal Time (hrs)
slt : (1D ndarray)
Solar Local Time (hr)
glat : (2D ndarray)
Geographic Latitude (deg)
glon : (2D ndarray)
Geographic Longitude (deg)
zalt : (2D ndarray)
Altitude (km)
deni : (4D ndarray)
Ion density by species (cm^-3)
vsi : (4D ndarray)
Expand All @@ -74,12 +66,10 @@ def __init__(self, tag, lon, year, day, outn=False, test=False):
Ion Temperature by species (K)
te : (3D ndarray)
Electron Temperature (K)
Examples
--------
To load a previous model run:
ModelRun = sami2py.Model(tag='run_name', lon=0, year=2012, day=210)
"""

self.tag = tag
Expand All @@ -93,19 +83,16 @@ def __init__(self, tag, lon, year, day, outn=False, test=False):

def __repr__(self):
"""Make a printable representation of a Model object
Returns
-------
out : (string)
string containing a printable representation of a Model object
Examples
--------
Load the model
ModelRun = sami2py.Model(tag='run_name', lon=0, year=2012, day=210)
Check the model information
ModelRun
"""

out = ['']
Expand Down Expand Up @@ -149,7 +136,6 @@ def __repr__(self):

def _calculate_slt(self):
"""Calculates Solar Local Time for reference point of model
Returns
-------
self.slt : (float)
Expand All @@ -164,7 +150,6 @@ def _calculate_slt(self):

def _load_model(self):
"""Loads model results
Returns
-------
void
Expand Down Expand Up @@ -263,12 +248,10 @@ def _load_model(self):

def _generate_metadata(self, namelist):
"""Reads the namelist and generates MetaData based on Parameters
Parameters
-----------
namelist : (list)
variable namelist from SAMI2 model
Returns
-------
void
Expand Down Expand Up @@ -345,26 +328,22 @@ def find_int(name, ind):

def check_standard_model(self, model_type="all"):
"""Checks for standard atmospheric inputs
Parameters
----------
model_type : (str)
Limit check to certain models (default='all')
Not currently implemented
Returns
-------
mod_keys : (list)
List of modified keyword for self.MetaData, empty
if no modifications were made
Examples
--------
Load the model
ModelRun = sami2py.Model(tag='run_name', lon=0, year=2012, day=210)
Check the model information for changes to the standard inputs
ModelRun.check_standard_model()
"""
mod_keys = list()
meta_keys = list(self.MetaData.keys())
Expand All @@ -391,15 +370,13 @@ def to_netcdf(self, path=''):

def plot_lat_alt(self, time_step=0, species=1):
"""Plots input parameter as a function of latitude and altitude
Parameters
----------
time_step : (int)
time index for SAMI2 model results
species : (int)
ion species index :
0: H+, 1: O+, 2: NO+, 3: O2+, 4: He+, 5: N2+, 6: N+
Examples
--------
Load the model
Expand All @@ -408,7 +385,6 @@ def plot_lat_alt(self, time_step=0, species=1):
ModelRun.plot_lat_alt()
Plot the H+ density at the 100th time step (initial step is 0)
ModelRun.plot_lat_alt(time_step=99, species=0)
"""
import matplotlib.pyplot as plt
import warnings
Expand All @@ -425,3 +401,27 @@ def plot_lat_alt(self, time_step=0, species=1):
plt.ylabel('Altitude (km)')

return fig

def plot_exb(self):
"""Plots ExB drifts from the return_fourier function
Examples
--------
Load the model
ModelRun = sami2py.Model(tag='exb', lon=0, year=2012, day=210)
Plot ExB drifts
ModelRun.plot_exb()
"""
import matplotlib.pyplot as plt
import warnings

warnings.warn(' '.join(["Model.plot_exb is deprecated and will be",
"removed in a future version. ",
"Use sami2py_vis instead"]),
DeprecationWarning)

plt.xlabel('Time (hrs)')
plt.ylabel('ExB Drifts')
plt.plot(return_fourier(self.slt, self.MetaData['Fourier Coeffs']))

return plt
47 changes: 46 additions & 1 deletion sami2py/tests/test_utils.py
Expand Up @@ -60,7 +60,6 @@ class TestArchiveDir():
"""Test basic functionality of the set_archive_dir function"""
def test_set_archive_dir(self):
"""Test that set_archive_dir has set and stored the archive directory
To leave the archive directory unchanged it must be gathered and
reset after the test is complete
"""
Expand Down Expand Up @@ -127,3 +126,49 @@ def file_open_error(self):
"""File open should raise an error if invalid file path provided"""
with pytest.raises(IOError):
sami2py.utils.get_unformatted_data(self.model_pathU, 'glat')


class TestFourierFunction():
"""Test basic functionality of the return_fourier function"""
def setup(self):
"""Setup the x and coefficient variables with general x values and
coefficients"""
self.x = np.array([0.11, 0.36, 0.61, 0.86, 1.12, 1.37, 1.62, 1.88,
2.13, 2.38, 2.64, 2.89, 3.14, 3.4, 3.65, 3.9,
4.16, 4.41, 4.66, 4.92, 5.17, 5.42, 5.68, 5.93,
6.18, 6.44, 6.69, 6.94, 7.2, 7.45, 7.7, 7.95,
8.2, 8.46, 8.71, 8.96, 9.21, 9.46, 9.72, 9.97,
10.23, 10.49, 10.74, 10.99, 11.24, 11.49, 11.74,
11.99, 12.24, 12.49, 12.74, 13., 13.26, 13.51, 13.76,
14.02, 14.27, 14.53, 14.79, 15.04, 15.29, 15.54,
15.79, 16.04, 16.29, 16.54, 16.79, 17.04, 17.29,
17.54, 17.8, 18.05, 18.31, 18.56, 18.81, 19.07,
19.32, 19.57, 19.83, 20.08, 20.33, 20.59, 20.84,
21.09, 21.35, 21.6, 21.85, 22.11, 22.36, 22.61,
22.86, 23.12, 23.37, 23.624, 23.87])

self.coeffs = np.zeros((10, 2))

def teardown(self):
"""Resets both parameters"""
del self.x, self.coeffs

def test_cos(self):
"""Test the cos function when coeffs are all 0s except for one with 1
value
"""
self.coeffs[0, 0] = 1.0

y = sami2py.utils.return_fourier(self.x, self.coeffs)
target = np.cos(np.pi * self.x / 12.)
assert (y == target).all()

def test_sin(self):
"""Test the sine function when coeffs are all 0s except for one with 1
value
"""
self.coeffs[0, 1] = 1.0

y = sami2py.utils.return_fourier(self.x, self.coeffs)
target = np.sin(np.pi * self.x / 12.)
assert (y == target).all()
38 changes: 37 additions & 1 deletion sami2py/utils.py
Expand Up @@ -13,6 +13,9 @@
set_archive_dir(path=None, store=None)
Allows user to specify the location where the model outputs will be stored
return_fourier(x, coeffs)
Returns Fourier Series up to NumF Coefficients
get_unformatted_data(dat_dir, var_name, nz, nf, ni, nt, reshape=False)
routine to interpret unformatted binary files created by the SAMI2 model
-------------------------------------------------------------------------------
Expand All @@ -24,6 +27,7 @@
"""

import os
import numpy as np


def generate_path(tag, lon, year, day, test=False):
Expand Down Expand Up @@ -113,6 +117,39 @@ def set_archive_dir(path=None, store=True):
raise ValueError('Path does not lead to a valid directory.')


def return_fourier(x, coeffs):
"""
Returns a Fourier series up to NumF coefficients
Parameters
----------
x : (1d ndarray)
solar local time in hours (slt)
coeffs : (array)
10x2 array of fourier coefficients
Returns
--------
y : (array)
result of the fourier series
"""
def cos_a(x, n):
"""simple cosine"""
return np.cos(n * np.pi * x / 12.0)

def sin_a(x, n):
"""simple sine"""
return np.sin(n * np.pi * x / 12.0)

NumF = coeffs.shape

y = 0.0 * x
for i in range(0, NumF[0]):
y += coeffs[i, 0] * cos_a(x, i + 1) + coeffs[i, 1] * sin_a(x, i + 1)

return y


def get_unformatted_data(dat_dir, var_name, reshape=False, dim=(0, 0)):
"""Routine to interpret unformatted binary files created by the SAMI2 model
Expand All @@ -139,7 +176,6 @@ def get_unformatted_data(dat_dir, var_name, reshape=False, dim=(0, 0)):
float_data : (numpy.ndarray)
unformatted data organized into a numpy array for handling in python
"""
import numpy as np

binary_file = open(os.path.join(dat_dir, var_name + 'u.dat'), 'rb')
float_data = np.fromfile(binary_file, dtype='float32')
Expand Down

0 comments on commit 4c57d61

Please sign in to comment.