From 523d937ef477af7dccf0fdc0dd449c583514d50e Mon Sep 17 00:00:00 2001 From: "Michael Hirsch, Ph.D" Date: Mon, 12 Oct 2020 11:17:50 -0400 Subject: [PATCH] use python -m iri2016. instead of console_scripts --- .travis.yml | 28 ++++++++++++ README.md | 9 ++-- setup.cfg | 12 +----- src/iri2016/__main__.py | 71 ------------------------------- src/iri2016/altitude.py | 33 ++++++++++++++ src/iri2016/base.py | 6 +-- src/iri2016/latitude.py | 43 +++++++++++++++++++ src/iri2016/profile.py | 8 ++-- src/iri2016/tests/test_scripts.py | 19 +++------ src/iri2016/time.py | 33 ++++++++++++++ 10 files changed, 157 insertions(+), 105 deletions(-) create mode 100644 .travis.yml delete mode 100644 src/iri2016/__main__.py create mode 100644 src/iri2016/altitude.py create mode 100644 src/iri2016/latitude.py create mode 100644 src/iri2016/time.py diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f9b12db --- /dev/null +++ b/.travis.yml @@ -0,0 +1,28 @@ +dist: bionic +# Matlab R2020a libstdc++ needs Ubuntu 18.04 + +language: matlab + +matlab: +- latest + +os: +- linux + +git: +- depth: 3 +- quiet: true + +addons: + apt: + packages: + - gfortran + snaps: + - name: cmake + confinement: classic + +before_script: +- export PATH=/snap/bin:$PATH + +script: +- matlab -batch "r = runtests('iri2016'); assert(~isempty(r)); assertSuccess(r)" diff --git a/README.md b/README.md index 6c94171..c96eb8c 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![image](https://zenodo.org/badge/DOI/10.5281/zenodo.240895.svg)](https://doi.org/10.5281/zenodo.240895) ![Actions Status](https://github.com/space-physics/iri2016/workflows/ci/badge.svg) +[![Build Status](https://travis-ci.com/space-physics/iri2016.svg?branch=master)](https://travis-ci.com/space-physics/iri2016) [![PyPi version](https://img.shields.io/pypi/pyversions/iri2016.svg)](https://pypi.python.org/pypi/iri2016) [![PyPi Download stats](https://static.pepy.tech/badge/iri2016)](https://pepy.tech/project/iri2016) @@ -13,7 +14,7 @@ A Fortran compiler is required to build the IRI2016 code. ## Install -**Prerequisites** +Prerequisites * Fortran compiler--any modern Fortran compiler will do. Here's how to get Gfortran: * Linux: `apt install gfortran` @@ -55,21 +56,21 @@ ctest -S iri2016/src/iri2016/setup.cmake -VV * Altitude Profile: plot density and temperatures vs altitude ```sh - iri2016_altitude 2003-11-21T12 -11.95 -76.77 + python -m iri2016.altitude 2003-11-21T12 -11.95 -76.77 ``` ![image](./figures/iri1DExample01.png) * Latitude profile: plot densities and height at the peak of F2, F2, and E regions vs geographic latitude ```sh - iri2016_latitude 2004-11-21T17 -76.77 + python -m iri2016.latitude 2004-11-21T17 -76.77 ``` ![image](./figures/iri1DExample02.png) * Time profile: plot densities and height at the peak of F2, F2, and E regions vs UTC ```sh - iri2016_time 2014-11-21 2014-11-22 1 -11.95 -76.77 + python -m iri2016.time 2014-11-21 2014-11-22 1 -11.95 -76.77 ``` ![image](./figures/plasma.png) diff --git a/setup.cfg b/setup.cfg index bb1959d..8b43c01 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = iri2016 -version = 1.10.2 +version = 1.11.0 author = Michael Hirsch, Ph.D.; Ronald Ilma author_email = scivision@users.noreply.github.com description = IRI2016 International Reference Ionosphere from Python @@ -14,9 +14,7 @@ classifiers = Intended Audience :: Science/Research Operating System :: OS Independent Programming Language :: Fortran - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3 Topic :: Scientific/Engineering :: Atmospheric Science license_files = LICENSE.txt @@ -55,9 +53,3 @@ plot = pyigrf12 cartopy pyapex - -[options.entry_points] -console_scripts = - iri2016_altitude = iri2016.__main__:altitude - iri2016_latitude = iri2016.__main__:latitude - iri2016_time = iri2016.__main__:time_profile diff --git a/src/iri2016/__main__.py b/src/iri2016/__main__.py deleted file mode 100644 index 9a5a6d4..0000000 --- a/src/iri2016/__main__.py +++ /dev/null @@ -1,71 +0,0 @@ -import iri2016 as iri - -from pathlib import Path -from argparse import ArgumentParser -from datetime import timedelta - -try: - from matplotlib.pyplot import show - import iri2016.plots as piri -except ImportError: - show = piri = None - - -def altitude(): - """ Height Profile Example """ - - if show is None: - raise ImportError("need matplotlib") - - p = ArgumentParser(description="IRI2016 altitude profile") - p.add_argument("time", help="time of simulation") - p.add_argument("latlon", help="geodetic latitude, longitude (degrees)", type=float, nargs=2) - p.add_argument("-alt_km", help="altitude START STOP STEP (km)", type=float, nargs=3, default=(80, 1000, 10)) - P = p.parse_args() - - iono = iri.IRI(P.time, P.alt_km, *P.latlon) - - piri.altprofile(iono) - show() - - -def latitude(): - """ latitude Profile Example """ - - if show is None: - raise ImportError("need matplotlib") - - p = ArgumentParser(description="IRI2016 latitude profile") - p.add_argument("glon", help="geodetic longitude (degrees)", type=float) - p.add_argument("-glat", help="geodetic latitude START STOP STEP (degrees)", type=float, nargs=3, default=(-60, 60, 2.0)) - p.add_argument("-alt_km", help="altitude (km)", type=float, default=300.0) - p.add_argument("-o", "--outfn", help="write data to file") - P = p.parse_args() - - iono = iri.geoprofile(latrange=P.glat, glon=P.glon, altkm=P.alt_km, time="2004-01-01T17") - - if P.outfn: - outfn = Path(P.outfn).expanduser() - print("writing", outfn) - iono.to_netcdf(outfn) - - piri.latprofile(iono) - show() - - -def time_profile(): - """ IRI2016 time profile """ - - if show is None: - raise ImportError("need matplotlib") - - p = ArgumentParser() - p.add_argument("time", help="start yy-mm-dd, stop yy-mm-dd, step_hour", nargs=3) - p.add_argument("latlon", help="geodetic latitude, longitude (degrees)", nargs=2, type=float) - p.add_argument("-alt_km", help="altitude START STOP STEP (km)", type=float, nargs=3, default=(100, 200, 20)) - P = p.parse_args() - - sim = iri.timeprofile((P.time[0], P.time[1]), timedelta(hours=float(P.time[2])), P.alt_km, *P.latlon) - - piri.timeprofile(sim) - show() diff --git a/src/iri2016/altitude.py b/src/iri2016/altitude.py new file mode 100644 index 0000000..5a3aedc --- /dev/null +++ b/src/iri2016/altitude.py @@ -0,0 +1,33 @@ +from .base import IRI + +from argparse import ArgumentParser +import typing as T + + +def main(time: str, alt_km: T.Sequence[float], glat: float, glon: float): + """ Height Profile Example """ + + return IRI(time, alt_km, glat, glon) + + +def cli(): + p = ArgumentParser(description="IRI2016 altitude profile") + p.add_argument("time", help="time of simulation") + p.add_argument("latlon", help="geodetic latitude, longitude (degrees)", type=float, nargs=2) + p.add_argument("-alt_km", help="altitude START STOP STEP (km)", type=float, nargs=3, default=(80, 1000, 10)) + P = p.parse_args() + + iono = main(P.time, P.alt_km, *P.latlon) + + try: + from matplotlib.pyplot import show + import iri2016.plots as piri + + piri.altprofile(iono) + show() + except ImportError: + pass + + +if __name__ == "__main__": + cli() diff --git a/src/iri2016/base.py b/src/iri2016/base.py index 9ea88f1..031fb0f 100644 --- a/src/iri2016/base.py +++ b/src/iri2016/base.py @@ -5,7 +5,7 @@ import io import os import numpy as np -import typing +import typing as T import importlib.resources iri_name = "iri2016_driver" @@ -21,13 +21,13 @@ __all__ = ["IRI"] -def IRI(time: datetime, altkmrange: typing.Sequence[float], glat: float, glon: float) -> xarray.Dataset: +def IRI(time: T.Union[str, datetime], altkmrange: T.Sequence[float], glat: float, glon: float) -> xarray.Dataset: if isinstance(time, str): time = parse(time) assert len(altkmrange) == 3, "altitude (km) min, max, step" - assert isinstance(glat, float) and isinstance(glon, float), "glat, glon is scalar" + assert isinstance(glat, (int, float)) and isinstance(glon, (int, float)), "glat, glon is scalar" with importlib.resources.path(__package__, iri_name) as exe: cmd = [ diff --git a/src/iri2016/latitude.py b/src/iri2016/latitude.py new file mode 100644 index 0000000..cfc2f95 --- /dev/null +++ b/src/iri2016/latitude.py @@ -0,0 +1,43 @@ +from .profile import geoprofile + +from pathlib import Path +from argparse import ArgumentParser +import typing as T + + +def main(time: str, alt_km: float, glat: T.Sequence[float], glon: float, outfn: Path = None): + """ latitude Profile Example """ + + iono = geoprofile(latrange=glat, glon=glon, altkm=alt_km, time=time) + + if outfn: + outfn = Path(outfn).expanduser() + print("writing", outfn) + iono.to_netcdf(outfn) + + return iono + + +def cli(): + p = ArgumentParser(description="IRI2016 latitude profile") + p.add_argument("time", help="time of simulation") + p.add_argument("glon", help="geodetic longitude (degrees)", type=float) + p.add_argument("-glat", help="geodetic latitude START STOP STEP (degrees)", type=float, nargs=3, default=(-60, 60, 2.0)) + p.add_argument("-alt_km", help="altitude (km)", type=float, default=300.0) + p.add_argument("-o", "--outfn", help="write data to file") + P = p.parse_args() + + iono = main(P.time, P.alt_km, P.glat, P.glon, P.outfn) + + try: + from matplotlib.pyplot import show + import iri2016.plots as piri + + piri.latprofile(iono) + show() + except ImportError: + pass + + +if __name__ == "__main__": + cli() diff --git a/src/iri2016/profile.py b/src/iri2016/profile.py index 98da991..17dde64 100644 --- a/src/iri2016/profile.py +++ b/src/iri2016/profile.py @@ -1,7 +1,7 @@ import xarray from dateutil.parser import parse from datetime import datetime, timedelta -import typing +import typing as T import numpy as np from .base import IRI @@ -9,7 +9,7 @@ __all__ = ["datetimerange", "timeprofile", "geoprofile"] -def datetimerange(start: datetime, end: datetime, step: timedelta) -> typing.List[datetime]: +def datetimerange(start: datetime, end: datetime, step: timedelta) -> T.List[datetime]: """like range() for datetime""" if isinstance(start, str): start = parse(start) @@ -24,7 +24,7 @@ def datetimerange(start: datetime, end: datetime, step: timedelta) -> typing.Lis return [start + i * step for i in range((end - start) // step)] -def timeprofile(tlim: tuple, dt: timedelta, altkmrange: list, glat: float, glon: float) -> xarray.Dataset: +def timeprofile(tlim: tuple, dt: timedelta, altkmrange: T.Sequence[float], glat: float, glon: float) -> xarray.Dataset: """compute IRI altitude profile over time range for fixed lat/lon """ @@ -51,7 +51,7 @@ def timeprofile(tlim: tuple, dt: timedelta, altkmrange: list, glat: float, glon: return iono -def geoprofile(latrange: typing.Sequence[float], glon: float, altkm: float, time: datetime) -> xarray.Dataset: +def geoprofile(latrange: T.Sequence[float], glon: float, altkm: float, time: T.Union[str, datetime]) -> xarray.Dataset: """compute IRI altitude profiles at time, over lat or lon range """ diff --git a/src/iri2016/tests/test_scripts.py b/src/iri2016/tests/test_scripts.py index 479bd1c..3296386 100755 --- a/src/iri2016/tests/test_scripts.py +++ b/src/iri2016/tests/test_scripts.py @@ -1,22 +1,15 @@ -import pytest +import iri2016.time as time_profile +import iri2016.latitude as lat_profile +import iri2016.altitude as alt_profile -import subprocess -import os - -@pytest.mark.skipif(os.environ.get("CI") is not None, reason="CI no display") def test_latitude(): - pytest.importorskip("matplotlib") - subprocess.check_call(["iri2016_latitude", "-148"]) + lat_profile.main("2012-01-01", 300, (-60, 60, 2.0), -148) -@pytest.mark.skipif(os.environ.get("CI") is not None, reason="CI no display") def test_time(): - pytest.importorskip("matplotlib") - subprocess.check_call(["iri2016_time", "2012-01-01", "2012-01-02", "1.0", "65", "-148"]) + time_profile.main(("2012-01-01", "2012-01-02", 1.0), (100, 200, 20), 65, -148) -@pytest.mark.skipif(os.environ.get("CI") is not None, reason="CI no display") def test_alt(): - pytest.importorskip("matplotlib") - subprocess.check_call(["iri2016_altitude", "2012-01-01", "65", "-148"]) + alt_profile.main("2012-01-01", (80, 1000, 10), 65, -148) diff --git a/src/iri2016/time.py b/src/iri2016/time.py new file mode 100644 index 0000000..b4eace1 --- /dev/null +++ b/src/iri2016/time.py @@ -0,0 +1,33 @@ +from .profile import timeprofile + +from argparse import ArgumentParser +from datetime import timedelta +import typing as T + + +def main(time: T.Sequence[str], alt_km: T.Sequence[float], glat: float, glon: float): + """ IRI2016 time profile """ + return timeprofile((time[0], time[1]), timedelta(hours=float(time[2])), alt_km, glat, glon) + + +def cli(): + p = ArgumentParser() + p.add_argument("time", help="start yy-mm-dd, stop yy-mm-dd, step_hour", nargs=3) + p.add_argument("latlon", help="geodetic latitude, longitude (degrees)", nargs=2, type=float) + p.add_argument("-alt_km", help="altitude START STOP STEP (km)", type=float, nargs=3, default=(100, 200, 20)) + P = p.parse_args() + + iono = main(P.time, P.alt_km, *P.latlon) + + try: + from matplotlib.pyplot import show + import iri2016.plots as piri + + piri.timeprofile(iono) + show() + except ImportError: + pass + + +if __name__ == "__main__": + cli()