From 10ef05999bedcd4626117888c1a048c768a66b17 Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 8 Mar 2018 20:11:21 -0600 Subject: [PATCH 001/101] Author and version update Updated author name and version number --- AUTHORS.rst | 3 ++- CHANGELOG.rst | 6 ++++++ README.rst | 6 +++++- docs/conf.py | 5 ++--- docs/installation.rst | 8 ++++---- setup.py | 19 +++++++++++++------ 6 files changed, 32 insertions(+), 15 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index ae2f6f32..b5c919ad 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -2,5 +2,6 @@ Authors ======= -* Christer van der Meeren - https://github.com/cmeeren +* Angeline G. Burrell - https://github.com/aburrell +* Christer van der Meeren * Karl M. Laundal diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c2528abe..0b8e86d6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,12 @@ Changelog ========= +2.0.1 (2017-03-09) +----------------------------------------- + +* Update to use AACGM-v2.4, which includes changes to the inverse MLT and + dipole tilt functions and some minor bug fixes + 2.0.0 (2016-11-03) ----------------------------------------- diff --git a/README.rst b/README.rst index 573e73da..6c8c1efc 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,10 @@ Overview |docs| |version| This is a Python wrapper for the `AACGM-v2 C library -`_, which allows converting between geographic and magnetic coordinates. The currently included version of the C library is 2.2. The wrapper is provided "as is" in the hopes that it will be useful to the space science community, and will not automatically be updated when new versions of the C library is released. MLT calculations are included in the wrapper (not part of the C library, please see the documentation for implementation details). The package is free software (MIT license). +`_, which allows +converting between geographic and magnetic coordinates. The currently included +version of the C library is 2.4. The package is free software +(MIT license). Quick start =========== @@ -46,6 +49,7 @@ Documentation ============= https://aacgmv2.readthedocs.org/ +http://superdarn.thayer.dartmouth.edu/aacgm.html Badges ====== diff --git a/docs/conf.py b/docs/conf.py index 4e33f6dc..fa670ad2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,7 +3,6 @@ import os - extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', @@ -23,9 +22,9 @@ master_doc = 'index' project = u'AACGM-v2 Python library' year = u'2015' -author = u'Christer van der Meeren' +author = u'Angeline G. Burrell, Christer van der Meeren' copyright = '{0}, {1}'.format(year, author) -version = release = u'2.0.0' +version = release = u'2.0.1' # on_rtd is whether we are on readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' diff --git a/docs/installation.rst b/docs/installation.rst index acda0c34..67230411 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -10,8 +10,8 @@ When you have NumPy, install this package at the command line using ``pip`` [1]_ The package has been tested with the following setups (others might work, too): -* Windows (32/64 bit) and Linux (64 bit) -* Python 2.7, 3.3, 3.4 and 3.5 -* NumPy 1.8, 1.9, 1.10 +* Mac (64 bit), Windows (32/64 bit), and Linux (64 bit) +* Python 2.7, 3.3, 3.4, 3.5, and 3.6 +* NumPy 1.8, 1.9, 1.10, 1.14.1 -.. [1] pip is included with Python 2 from v2.7.9 and Python 3 from v3.4. If you don't have pip, `get it here `_. \ No newline at end of file +.. [1] pip is included with Python 2 from v2.7.9 and Python 3 from v3.4. If you don't have pip, `get it here `_. diff --git a/setup.py b/setup.py index 50d220bc..f5f5cac5 100644 --- a/setup.py +++ b/setup.py @@ -34,13 +34,15 @@ def read(*names, **kwargs): setup( name='aacgmv2', - version='2.0.0', + version='2.0.1', license='MIT', description='A Python wrapper for AACGM-v2 magnetic coordinates', - long_description='%s\n%s' % (read('README.rst'), re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst'))), - author='Christer van der Meeren', - author_email='cmeeren@gmail.com', - url='https://github.com/cmeeren/aacgmv2', + long_description='%s\n%s' % (read('README.rst'), + re.sub(':[a-z]+:`~?(.*?)`', + r'``\1``', read('CHANGELOG.rst'))), + author='Angeline G. Burrell, Christer van der Meeren', + author_email='agb073000@utdallas.edu', + url='https://github.com/aburrell/aacgmv2', packages=find_packages('src'), package_dir={'': 'src'}, py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], @@ -53,6 +55,7 @@ def read(*names, **kwargs): 'License :: OSI Approved :: MIT License', 'Operating System :: Unix', 'Operating System :: POSIX', + 'Operating System :: MacOS :: MacOS X', 'Operating System :: Microsoft :: Windows', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', @@ -60,6 +63,7 @@ def read(*names, **kwargs): 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: Implementation :: CPython', 'Topic :: Scientific/Engineering :: Physics', 'Topic :: Utilities', @@ -80,7 +84,10 @@ def read(*names, **kwargs): ], ext_modules=[ Extension('aacgmv2._aacgmv2', - sources=['src/aacgmv2/aacgmv2module.c', 'src/c_aacgm_v2/aacgmlib_v2.c', 'src/c_aacgm_v2/genmag.c', 'src/c_aacgm_v2/igrflib.c'], + sources=['src/aacgmv2/aacgmv2module.c', + 'src/c_aacgm_v2/aacgmlib_v2.c', + 'src/c_aacgm_v2/genmag.c', + 'src/c_aacgm_v2/igrflib.c'], include_dirs=['src/c_aacgm_v2']) ], entry_points={ From 4198f26da00bb21d03c913261703b65e54766ede Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 8 Mar 2018 20:13:53 -0600 Subject: [PATCH 002/101] AACGM-v2 source code Update source code to AACGM v 2.4 --- src/c_aacgm_v2/README.txt | 185 +++- src/c_aacgm_v2/aacgmlib_v2.c | 1993 +++++++++++++++++----------------- src/c_aacgm_v2/astalglib.c | 728 +++++++++++++ src/c_aacgm_v2/genmag.c | 22 +- src/c_aacgm_v2/igrflib.c | 1650 ++++++++++++++-------------- src/c_aacgm_v2/mlt_v2.c | 283 +++++ src/c_aacgm_v2/rtime.c | 210 ++++ 7 files changed, 3241 insertions(+), 1830 deletions(-) create mode 100755 src/c_aacgm_v2/astalglib.c create mode 100755 src/c_aacgm_v2/mlt_v2.c create mode 100755 src/c_aacgm_v2/rtime.c diff --git a/src/c_aacgm_v2/README.txt b/src/c_aacgm_v2/README.txt index ef5816c0..b7e79b65 100644 --- a/src/c_aacgm_v2/README.txt +++ b/src/c_aacgm_v2/README.txt @@ -1,7 +1,7 @@ +AACGM-v2 Software +v2.4 20170601 -AACGM v2 Software C - -Instructions: +C Instructions: 1. Download the coefficients and put them in a convenient directory @@ -16,72 +16,136 @@ Instructions: Note that if you used the old AACGM software from JHU/APL you should have a similar variable already set. -3. Untar the contents of the .tar file into a directory - -4. Build the test program by running: - - gcc -o test_aacgm test_aacgm.c aacgmlib_v2.c igrflib.c genmag.c -lm -static + AGB: I altered the routines to allow the file root to be entered as input - Note that on older systems you might need to remove the -static flag - -5. Run the test program by running: +3. Untar the contents of the .tar file into a directory - test_aacgm +4. Setup IGRF by putting the IGRF coefficients (igrf12coeffs.txt) somewhere + or leaving them in the current directory and setting the environment + variable IGRF_COEFFS to the fully qualified path, i.e., - The output should look something like: + IGRF_COEFFS=/directory_you_put_IGRF_coefs_in/igrf12coeffs.txt + AGB: I altered the routines to allow the filename to be entered as input -*************************************************************************** -* AACGM v2 ERROR: No Date/Time Set * -* * -* You must specifiy the date and time in order to use AACGM coordinates, * -* which depend on the internal (IGRF) magnetic field. Before calling * -* AACGM_v2_Convert() you must set the date and time to the integer values * -* using the function: * -* * -* AACGM_v2_SetDateTime(year,month,day,hour,minute,second); * -* * -* or to the current computer time in UT using the function: * -* * -* AACGM_v2_SetNow(); * -* * -* subsequent calls to AACGM_v2_Convert() will use the last date and time * -* that was set, so update to the actual date and time that is desired. * -*************************************************************************** +5. Build the test program by running: + gcc -o test_aacgm test_aacgm.c aacgmlib_v2.c igrflib.c genmag.c astalglib.c + mlt_v2.c time.c -lm -Setting time to : 20140322 0311:00 -lat = 45.500000, lon = -23.500000, height = 1135.000000 -mlat = 48.377539, mlon = 57.822458, r = 1.000000 +6. Run the test program by running: + test_aacgm -lat = 45.500000, lon = -23.500000, height = 1135.000000 -mlat = 49.425800, mlon = 58.259686, r = 1.000000 + AGB: or by running: + + test_aacgm aagcm_coeff_fileroot igrf_filename + Both commands will yeild the same result + -lat = 65.500000, lon = 93.500000, height = 1135.000000 -mlat = 62.251076, mlon = 166.990581, r = 1.000000 + The output should look something like: +================================================================================ + +AACGM-v2 Test Program + +================================================================================ + +TEST: no date/time (this will return an error.) + +************************************************************************** +* AACGM-v2 ERROR: No Date/Time Set * +* * +* You must specifiy the date and time in order to use AACGM coordinates, * +* which depend on the internal (IGRF) magnetic field. Before calling * +* AACGM_v2_Convert() you must set the date and time to the integer values* +* using the function: * +* * +* AACGM_v2_SetDateTime(year,month,day,hour,minute,second); * +* * +* or to the current computer time in UT using the function: * +* * +* AACGM_v2_SetNow(); * +* * +* subsequent calls to AACGM_v2_Convert() will use the last date and time * +* that was set, so update to the actual date and time that is desired. * +************************************************************************** + +TEST: Setting time to : 20140322 0311:00 + +TEST: geographic to AACGM-v2 + GLAT GLON HEIGHT MLAT MLON R + 45.500000 -23.500000 1135.000000 48.189618 57.763454 1.177533 + +TEST: AACGM-v2 to geographic + MLAT MLON HEIGHT GLAT GLON HEIGHT + 48.189618 57.763454 1131.097495 45.440775 -23.472757 1134.977896 + +Do the same thing but use field-line tracing + +TEST: geographic to AACGM-v2 (TRACE) + GLAT GLON HEIGHT MLAT MLON R + 45.500000 -23.500000 1135.000000 48.194757 57.758831 1.177533 + +TEST: AACGM-v2 to geographic (TRACE) + MLAT MLON HEIGHT GLAT GLON HEIGHT + 48.194757 57.758831 1131.097495 45.500000 -23.500000 1135.000000 + +-------------------------------------------------------------------------------- + +Testing MLT +-------------------------------------------------------------------------------- + + GLAT GLON HEIGHT MLAT MLON MLT +TRACE 37.000000 -88.000000 300.000000 48.840368 -17.006090 1.977745 +COEFF 37.000000 -88.000000 300.000000 48.844355 -16.999464 1.978187 + + +Array: + 45.0000 0.0000 150.0000 40.2841 76.6676 8.2227 + 45.0000 1.0000 150.0000 40.2447 77.4899 8.2775 + 45.0000 2.0000 150.0000 40.2108 78.3157 8.3325 + 45.0000 3.0000 150.0000 40.1822 79.1452 8.3878 + 45.0000 4.0000 150.0000 40.1587 79.9785 8.4434 + 45.0000 5.0000 150.0000 40.1400 80.8157 8.4992 + 45.0000 6.0000 150.0000 40.1261 81.6569 8.5553 + 45.0000 7.0000 150.0000 40.1165 82.5020 8.6116 + 45.0000 8.0000 150.0000 40.1111 83.3513 8.6682 + 45.0000 9.0000 150.0000 40.1097 84.2048 8.7251 + 45.0000 10.0000 150.0000 40.1121 85.0624 8.7823 + 45.0000 11.0000 150.0000 40.1179 85.9243 8.8398 + 45.0000 12.0000 150.0000 40.1271 86.7904 8.8975 + 45.0000 13.0000 150.0000 40.1394 87.6608 8.9555 + 45.0000 14.0000 150.0000 40.1546 88.5354 9.0138 + 45.0000 15.0000 150.0000 40.1725 89.4143 9.0724 + 45.0000 16.0000 150.0000 40.1930 90.2976 9.1313 + 45.0000 17.0000 150.0000 40.2157 91.1850 9.1905 + 45.0000 18.0000 150.0000 40.2405 92.0768 9.2499 + 45.0000 19.0000 150.0000 40.2673 92.9728 9.3097 -lat = 65.500000, lon = 93.500000, height = 0.000000 -mlat = 60.799240, mlon = 166.518084, r = 1.000000 +IMPORTANT NOTES: -lat = 75.500000, lon = 73.500000, height = 0.000000 -mlat = 70.420669, mlon = 150.743259, r = 1.000000 +1. Magnetic local time (MLT) functions have been restored: + double MLTConvertYMDHMS_v2(int yr,int mo,int dy,int hr,int mt,int sc, + double mlon); + double MLTConvertYrsec_v2(int yr,int yrsec, double mlon); + double MLTConvertEpoch_v2(double epoch, double mlon); -lat = 75.500000, lon = 73.500000, height = 0.000000 -mlat = 70.726381, mlon = 150.672892, r = 1.000000 + Note that AACGM-v2 longitude is much less sensitive to altitude; maximum + difference of <1 degree (5 min in MLT) over the range 0-2000 km. For this + reason there is no height passed directly into the MLT routines. The value + of AACGM-v2 longitude does change with altitude and variations of MLT with + altitude above a given geographic location do exist. -IMPORTANT NOTES: - -1. The function AACGM_v2_Convert is a direct replacement for the function +2. The function AACGM_v2_Convert is a direct replacement for the function AACGMConvert that is used in much of the SD software. This is your starting point, but you can modify the test program as you like. -2. New user-space functions have been added that allow users to set the +3. New user-space functions have been added that allow users to set the date and time. The functions are: AACGM_v2_SetDateTime(int year, int month, int day, int hour, @@ -96,11 +160,34 @@ IMPORTANT NOTES: determine what the correspondence between changes in time and AACGM lat/lon are. -3. You must set the date and time at least once or the code will not run. +4. You must set the date and time at least once or the code will not run. -4. A new user-space function has been added that allow users to see what +5. A new user-space function has been added that allow users to see what date and time are being used. The function is: AACGM_v2_GetDateTime(int *year, int *month, int *day, int *hour, int *minute, int *second, int *doy); + +This package include the following files: + +AACGM C software: + +README.txt ; this file +release_notes.txt ; details of changes to v2.4 +aacgmlib_v2.c ; AACGM-v2 functions +aacgmlib_v2.h ; AACGM-v2 header file +genmag.c ; general purpose functions +genmag.h ; general purpose header file +igrflib.c ; internal IGRF functions +igrflib.h ; internal IGRF header file +time.c ; internal date/time functions +time.h ; internal date/time header file +astalg.c ; Astronomical algorithms functions +astalg.h ; Astronomical algorithms header file +mlt_v2.c ; MLT-v2 functions +mlt_v2.h ; MLT-v2 header file +igrf12coeffs.txt ; IGRF12 coefficients +test_aacgm.c ; testing and example program +LICENSE-AstAlg.txt ; license file for Astro algrorithms + diff --git a/src/c_aacgm_v2/aacgmlib_v2.c b/src/c_aacgm_v2/aacgmlib_v2.c index e0289aa2..a797b8ff 100644 --- a/src/c_aacgm_v2/aacgmlib_v2.c +++ b/src/c_aacgm_v2/aacgmlib_v2.c @@ -18,6 +18,10 @@ ; is not big enought on some 32-bit systems. ; Switched from NAN to HUGE_VAL for undefined result ; 20140918 SGS v1.0 change function names to _v2 for wider distribution +; 20150810 SGS v1.1 added code to default to geodetic coordinates for inverse +; transformation. This code was left out in the C version. +; 20170308 SGS v1.2 Added static to global variables in order to work with RST +; library. ; ; Functions: ; @@ -25,7 +29,7 @@ ; AACGM_v2_Alt2CGM - not used ; AACGM_v2_CGM2Alt ; AACGM_v2_Sgn -; convert_geo_coord +; convert_geo_coord_v2 ; AACGM_v2_LoadCoefFP ; AACGM_v2_LoadCoef ; AACGM_v2_LoadCoefs @@ -53,48 +57,39 @@ #include "igrflib.h" #include "genmag.h" -#ifndef M_PI - #define M_PI 3.14159265358979323846 -#endif - -#ifdef _MSC_VER - #include - #define INFINITY (DBL_MAX+DBL_MAX) - #define NAN (INFINITY-INFINITY) -#endif - #define DEBUG 0 /* put these in the library header file when you figure out how to do so... */ -struct { - int year; - int month; - int day; - int hour; - int minute; - int second; - int dayno; - int daysinyear; -} aacgm_date = {-1,-1,-1,-1,-1,-1,-1}; - -int myear = 0; /* model year: 5-year epoch */ -double fyear = 0.; /* floating point year */ - -int myear_old = -1; -double fyear_old = -1.; - -double height_old[2] = {-1,-1}; - -struct { - double coef[AACGM_KMAX][NCOORD][POLYORD][NFLAG]; /* interpolated coefs */ - double coefs[AACGM_KMAX][NCOORD][POLYORD][NFLAG][2]; /* bracketing coefs */ +static struct { + int year; + int month; + int day; + int hour; + int minute; + int second; + int dayno; + int daysinyear; +} aacgm_date = {-1,-1,-1,-1,-1,-1,-1,-1}; + +static int myear = 0; /* model year: 5-year epoch */ +static double fyear = 0.; /* floating point year */ + +static int myear_old = -1; +static double fyear_old = -1.; + +static double height_old[2] = {-1,-1}; + +static struct { + double coef[AACGM_KMAX][NCOORD][POLYORD][NFLAG]; /* interpolated coefs */ + double coefs[AACGM_KMAX][NCOORD][POLYORD][NFLAG][2]; /* bracketing coefs */ } sph_harm_model; +/* SGS added for MSC compatibility */ #ifndef complex - struct complex { - double x; - double y; - }; +struct complex { + double x; + double y; +}; #endif @@ -159,199 +154,199 @@ struct { int AACGM_v2_Rylm(double colat, double lon, int order, double *ylmval) { - struct complex z1, z2; - struct complex q_fac, q_val; - int k, l, m, kk; - int ia,ib,ic,id; - double cos_theta, sin_theta; - double cos_lon, sin_lon; - double l2, tl, fac; - double ca, cb, d1; - double *ffff; - /* long unsigned *fact; not big enough for 32-bit machines */ - double *fact; - - cos_theta = cos(colat); - sin_theta = sin(colat); - - cos_lon = cos(lon); - sin_lon = sin(lon); - - d1 = -sin_theta; - z2.x = cos_lon; - z2.y = sin_lon; - - z1.x = d1 * z2.x; - z1.y = d1 * z2.y; - q_fac = z1; - - /* - * Generate Zonal Harmonics (P_l^(m=0) for l = 1,order) using recursion - * relation (6.8.7), p. 252, Numerical Recipes in C, 2nd. ed., Press. W. - * et al. Cambridge University Press, 1992) for case where m = 0. - * - * l Pl = cos(theta) (2l-1) Pl-1 - (l-1) Pl-2 (6.8.7) - * - * where Pl = P_l^(m=0) are the associated Legendre polynomials - * - */ - - ylmval[0] = 1; /* l = 0, m = 0 */ - ylmval[2] = cos_theta; /* l = 1, m = 0 */ - - for (l=2; l<=order; l++) { - /* indices for previous two values: k = l * (l+1) + m with m=0 */ - ia = (l-2)*(l-1); - ib = (l-1)*l; - ic = l * (l+1); - - ylmval[ic] = (cos_theta * (2*l-1) * ylmval[ib] - (l-1)*ylmval[ia])/l; - } - - /* - * Generate P_l^l for l = 1 to (order+1)^2 using algorithm based upon (6.8.8) - * in Press et al., but incorporate longitude dependence, i.e., sin/cos (phi) - * - * Pll = (-1)^l (2l-1)!! (sin^2(theta))^(l/2) - * - * where Plm = P_l^m are the associated Legendre polynomials - * - */ - - q_val = q_fac; - ylmval[3] = q_val.x; /* l = 1, m = +1 */ - ylmval[1] = -q_val.y; /* l = 1, m = -1 */ - for (l=2; l<=order; l++) { - d1 = l*2 - 1.; - z2.x = d1 * q_fac.x; - z2.y = d1 * q_fac.y; - z1.x = z2.x * q_val.x - z2.y * q_val.y; - z1.y = z2.x * q_val.y + z2.y * q_val.x; - q_val = z1; - - /* indices for previous two values: k = l * (l+1) + m */ - ia = l*(l+2); /* m = +l */ - ib = l*l; /* m = -l */ - - ylmval[ia] = q_val.x; - ylmval[ib] = -q_val.y; - } - - /* - * Generate P_l,l-1 to P_(order+1)^2,l-1 using algorithm based upon (6.8.9) - * in Press et al., but incorporate longitude dependence, i.e., sin/cos (phi) - * - * Pl,l-1 = cos(theta) (2l-1) Pl-1,l-1 - * - */ - - for (l=2; l<=order; l++) { - - l2 = l*l; - tl = 2*l; - /* indices for Pl,l-1; Pl-1,l-1; Pl,-(l-1); Pl-1,-(l-1) */ - ia = l2 - 1; - ib = l2 - tl + 1; - ic = l2 + tl - 1; - id = l2 + 1; - - fac = tl - 1; - ylmval[ic] = fac * cos_theta * ylmval[ia]; /* Pl,l-1 */ - ylmval[id] = fac * cos_theta * ylmval[ib]; /* Pl,-(l-1) */ - } - - /* - * Generate remaining P_l+2,m to P_(order+1)^2,m for each m = 1 to order-2 - * using algorithm based upon (6.8.7) in Press et al., but incorporate - * longitude dependence, i.e., sin/cos (phi). - * - * for each m value 1 to order-2 we have P_mm and P_m+1,m so we can compute - * P_m+2,m; P_m+3,m; etc. - * - */ - - for (m=1; m<=order-2; m++) { - for (l=m+2; l<=order; l++) { - ca = ((double) (2*l-1))/(l-m); - cb = ((double) (l+m-1))/(l-m); - - l2 = l*l; - ic = l2 + l + m; - ib = l2 - l + m; - ia = l2 - l - l - l + 2 + m; - /* positive m */ - ylmval[ic] = ca * cos_theta * ylmval[ib] - cb * ylmval[ia]; - - ic -= (m+m); - ib -= (m+m); - ia -= (m+m); - /* negative m */ - ylmval[ic] = ca * cos_theta * ylmval[ib] - cb * ylmval[ia]; - } - } - - /* - * Normalization added here (SGS) - * - * Note that this is NOT the standard spherical harmonic normalization factors - * - * The recursive algorithms above treat positive and negative values of m in - * the same manner. In order to use these algorithms the normalization must - * also be modified to reflect the symmetry. - * - * Output values have been checked against those obtained using the internal - * IDL legendre() function to obtain the various associated legendre - * polynomials. - * - * As stated above, I think that this normalization may be unnecessary. The - * important thing is that the various spherical harmonics are orthogonal, - * rather than orthonormal. - * - */ - - /* determine array of factorials */ - /*fact = (long unsigned *)malloc(sizeof(long unsigned)*(2*order+2));*/ - fact = (double *)malloc(sizeof(double)*(2*order+2)); - if (fact == NULL) return (-1); - fact[0] = fact[1] = 1; - for (k=2; k <= 2*order+1; k++) fact[k] = k*fact[k-1]; - - ffff = (double *)malloc(sizeof(double)*(order+1)*(order+1)); - if (ffff == NULL) return (-1); - - /* determine normalization factors */ - for (l=0; l<=order; l++) { - for (m=0; m<=l; m++) { - k = l * (l+1) + m; /* 1D index for l,m */ - ffff[k] = sqrt((2*l+1)/(4*M_PI) * fact[l-m]/fact[l+m]); - ylmval[k] *= ffff[k]; - } - for (m=-l; m<0; m++) { - k = l * (l+1) + m; /* 1D index for l,m */ - kk = l * (l+1) - m; - ylmval[k] *= ffff[kk] * ((-m % 2) ? -1 : 1); - } - } - - #if DEBUG > 1 - for (k=0; k<2*order+1; k++) - /*printf("%03d %lu\n", k, fact[k]);*/ - printf("%03d %lf\n", k, fact[k]); - #endif - - free(fact); - free(ffff); - - #if DEBUG > 10 + struct complex z1, z2; + struct complex q_fac, q_val; + int k, l, m, kk; + int ia,ib,ic,id; + double cos_theta, sin_theta; + double cos_lon, sin_lon; + double l2, tl, fac; + double ca, cb, d1; + double *ffff; + /* long unsigned *fact; not big enough for 32-bit machines */ + double *fact; + + cos_theta = cos(colat); + sin_theta = sin(colat); + + cos_lon = cos(lon); + sin_lon = sin(lon); + + d1 = -sin_theta; + z2.x = cos_lon; + z2.y = sin_lon; + + z1.x = d1 * z2.x; + z1.y = d1 * z2.y; + q_fac = z1; + + /* + * Generate Zonal Harmonics (P_l^(m=0) for l = 1,order) using recursion + * relation (6.8.7), p. 252, Numerical Recipes in C, 2nd. ed., Press. W. + * et al. Cambridge University Press, 1992) for case where m = 0. + * + * l Pl = cos(theta) (2l-1) Pl-1 - (l-1) Pl-2 (6.8.7) + * + * where Pl = P_l^(m=0) are the associated Legendre polynomials + * + */ + + ylmval[0] = 1; /* l = 0, m = 0 */ + ylmval[2] = cos_theta; /* l = 1, m = 0 */ + + for (l=2; l<=order; l++) { + /* indices for previous two values: k = l * (l+1) + m with m=0 */ + ia = (l-2)*(l-1); + ib = (l-1)*l; + ic = l * (l+1); + + ylmval[ic] = (cos_theta * (2*l-1) * ylmval[ib] - (l-1)*ylmval[ia])/l; + } + + /* + * Generate P_l^l for l = 1 to (order+1)^2 using algorithm based upon (6.8.8) + * in Press et al., but incorporate longitude dependence, i.e., sin/cos (phi) + * + * Pll = (-1)^l (2l-1)!! (sin^2(theta))^(l/2) + * + * where Plm = P_l^m are the associated Legendre polynomials + * + */ + + q_val = q_fac; + ylmval[3] = q_val.x; /* l = 1, m = +1 */ + ylmval[1] = -q_val.y; /* l = 1, m = -1 */ + for (l=2; l<=order; l++) { + d1 = l*2 - 1.; + z2.x = d1 * q_fac.x; + z2.y = d1 * q_fac.y; + z1.x = z2.x * q_val.x - z2.y * q_val.y; + z1.y = z2.x * q_val.y + z2.y * q_val.x; + q_val = z1; + + /* indices for previous two values: k = l * (l+1) + m */ + ia = l*(l+2); /* m = +l */ + ib = l*l; /* m = -l */ + + ylmval[ia] = q_val.x; + ylmval[ib] = -q_val.y; + } + + /* + * Generate P_l,l-1 to P_(order+1)^2,l-1 using algorithm based upon (6.8.9) + * in Press et al., but incorporate longitude dependence, i.e., sin/cos (phi) + * + * Pl,l-1 = cos(theta) (2l-1) Pl-1,l-1 + * + */ + + for (l=2; l<=order; l++) { + + l2 = l*l; + tl = 2*l; + /* indices for Pl,l-1; Pl-1,l-1; Pl,-(l-1); Pl-1,-(l-1) */ + ia = l2 - 1; + ib = l2 - tl + 1; + ic = l2 + tl - 1; + id = l2 + 1; + + fac = tl - 1; + ylmval[ic] = fac * cos_theta * ylmval[ia]; /* Pl,l-1 */ + ylmval[id] = fac * cos_theta * ylmval[ib]; /* Pl,-(l-1) */ + } + + /* + * Generate remaining P_l+2,m to P_(order+1)^2,m for each m = 1 to order-2 + * using algorithm based upon (6.8.7) in Press et al., but incorporate + * longitude dependence, i.e., sin/cos (phi). + * + * for each m value 1 to order-2 we have P_mm and P_m+1,m so we can compute + * P_m+2,m; P_m+3,m; etc. + * + */ + + for (m=1; m<=order-2; m++) { + for (l=m+2; l<=order; l++) { + ca = ((double) (2*l-1))/(l-m); + cb = ((double) (l+m-1))/(l-m); + + l2 = l*l; + ic = l2 + l + m; + ib = l2 - l + m; + ia = l2 - l - l - l + 2 + m; + /* positive m */ + ylmval[ic] = ca * cos_theta * ylmval[ib] - cb * ylmval[ia]; + + ic -= (m+m); + ib -= (m+m); + ia -= (m+m); + /* negative m */ + ylmval[ic] = ca * cos_theta * ylmval[ib] - cb * ylmval[ia]; + } + } + + /* + * Normalization added here (SGS) + * + * Note that this is NOT the standard spherical harmonic normalization factors + * + * The recursive algorithms above treat positive and negative values of m in + * the same manner. In order to use these algorithms the normalization must + * also be modified to reflect the symmetry. + * + * Output values have been checked against those obtained using the internal + * IDL legendre() function to obtain the various associated legendre + * polynomials. + * + * As stated above, I think that this normalization may be unnecessary. The + * important thing is that the various spherical harmonics are orthogonal, + * rather than orthonormal. + * + */ + + /* determine array of factorials */ + /*fact = (long unsigned *)malloc(sizeof(long unsigned)*(2*order+2));*/ + fact = (double *)malloc(sizeof(double)*(2*order+2)); + if (fact == NULL) return (-1); + fact[0] = fact[1] = 1; + for (k=2; k <= 2*order+1; k++) fact[k] = k*fact[k-1]; + + ffff = (double *)malloc(sizeof(double)*(order+1)*(order+1)); + if (ffff == NULL) return (-1); + + /* determine normalization factors */ for (l=0; l<=order; l++) { for (m=0; m<=l; m++) { - k = l * (l+1) + m; /* 1D index for l,m */ - printf("%03d %lf\n", k, ylmval[k]); - } - } - #endif + k = l * (l+1) + m; /* 1D index for l,m */ + ffff[k] = sqrt((2*l+1)/(4*M_PI) * fact[l-m]/fact[l+m]); + ylmval[k] *= ffff[k]; + } + for (m=-l; m<0; m++) { + k = l * (l+1) + m; /* 1D index for l,m */ + kk = l * (l+1) - m; + ylmval[k] *= ffff[kk] * ((-m % 2) ? -1 : 1); + } + } + + #if DEBUG > 1 + for (k=0; k<2*order+1; k++) + /*printf("%03d %lu\n", k, fact[k]);*/ + printf("%03d %lf\n", k, fact[k]); + #endif + + free(fact); + free(ffff); + + #if DEBUG > 10 + for (l=0; l<=order; l++) { + for (m=0; m<=l; m++) { + k = l * (l+1) + m; /* 1D index for l,m */ + printf("%03d %lf\n", k, ylmval[k]); + } + } + #endif - return (0); + return (0); } /*----------------------------------------------------------------------------- @@ -389,26 +384,26 @@ int AACGM_v2_Rylm(double colat, double lon, int order, double *ylmval) void AACGM_v2_Alt2CGM(double r_height_in, double r_lat_alt, double *r_lat_adj) { - double eps =1e-9; - double unim =0.9999999; + double eps =1e-9; + double unim =0.9999999; - double r1; - double r0, ra; + double r1; + double r0, ra; - #if DEBUG > 0 - printf("AACGM_v2_Alt2CGM\n"); - #endif + #if DEBUG > 0 + printf("AACGM_v2_Alt2CGM\n"); + #endif - /* Computing 2nd power */ - r1 = cos(r_lat_alt*M_PI/180.); - ra = r1 * r1; - if (ra < eps) ra = eps; + /* Computing 2nd power */ + r1 = cos(r_lat_alt*DTOR); + ra = r1 * r1; + if (ra < eps) ra = eps; - r0 = (r_height_in/RE + 1.) / ra; - if (r0 < unim) r0 = unim; + r0 = (r_height_in/RE + 1.) / ra; + if (r0 < unim) r0 = unim; - r1 = acos(sqrt(1/r0)); - *r_lat_adj = AACGM_v2_Sgn(r1, r_lat_alt)*180/M_PI; + r1 = acos(sqrt(1/r0)); + *r_lat_adj = AACGM_v2_Sgn(r1, r_lat_alt)/DTOR; } /*----------------------------------------------------------------------------- @@ -448,27 +443,27 @@ void AACGM_v2_Alt2CGM(double r_height_in, double r_lat_alt, double *r_lat_adj) int AACGM_v2_CGM2Alt(double r_height_in, double r_lat_in, double *r_lat_adj) { - double unim=1; - double r1; - double ra; - int error=0; - - #if DEBUG > 0 - printf("AACGM_v2_CGM2Alt\n"); - #endif - - /* convert from AACGM to at-altitude coordinates */ - r1 = cos(r_lat_in*M_PI/180.); - ra = (r_height_in/RE + 1)*(r1*r1); - if (ra > unim) { - ra = unim; - error = 1; - } - - r1 = acos(sqrt(ra)); - *r_lat_adj = AACGM_v2_Sgn(r1,r_lat_in)*180/M_PI; - - return (error); + double unim=1; + double r1; + double ra; + int error=0; + + #if DEBUG > 0 + printf("AACGM_v2_CGM2Alt\n"); + #endif + + /* convert from AACGM to at-altitude coordinates */ + r1 = cos(r_lat_in*DTOR); + ra = (r_height_in/RE + 1)*(r1*r1); + if (ra > unim) { + ra = unim; + error = 1; + } + + r1 = acos(sqrt(ra)); + *r_lat_adj = AACGM_v2_Sgn(r1,r_lat_in)/DTOR; + + return (error); } /*----------------------------------------------------------------------------- @@ -495,23 +490,23 @@ int AACGM_v2_CGM2Alt(double r_height_in, double r_lat_in, double *r_lat_adj) double AACGM_v2_Sgn(double a, double b) { - double x=0; - x = (a >= 0) ? a : -a; - return (b >= 0) ? x: -x; + double x=0; + x = (a >= 0) ? a : -a; + return (b >= 0) ? x: -x; } /*----------------------------------------------------------------------------- ; ; NAME: -; convert_geo_coord +; convert_geo_coord_v2 ; ; PURPOSE: ; Second-level function used to determine the lat/lon of the input ; coordinates. ; ; CALLING SEQUENCE: -; err = convert_geo_coord(in_lat,in_lon,height, out_lat,out_lon, -; code,order); +; err = convert_geo_coord_v2(in_lat,in_lon,height, out_lat,out_lon, +; code,order, igrf_filename); ; ; Input Arguments: ; in_lat - latitude in degrees @@ -535,189 +530,197 @@ double AACGM_v2_Sgn(double a, double b) ;+----------------------------------------------------------------------------- */ -int convert_geo_coord(double lat_in, double lon_in, double height_in, - double *lat_out, double *lon_out, int code, int order) { +int convert_geo_coord_v2(double lat_in, double lon_in, double height_in, + double *lat_out, double *lon_out, int code, int order, + char *igrf_filename) { - int i,j,k,l,m,f,a,t,flag; - int i_err64, err; +/* int i,j,k,l,m,f,a,t,flag;*/ + int i,j,k,l,m,flag; + int i_err64, err; -// extern int rylm(); - double ylmval[AACGM_KMAX]; - double colat_temp; - double lon_output; +/* extern int rylm(); */ + double ylmval[AACGM_KMAX]; + double colat_temp; + double lon_output; - double lat_adj=0; - double lat_alt=0; - double colat_input; + double lat_adj=0; +/* double lat_alt=0; */ + double colat_input; - double alt_var_cu=0, lon_temp=0, alt_var_sq=0, alt_var_qu=0; - double colat_output=0, r=0, x=0, y=0, z=0; - double ztmp, fac; - double alt_var=0; - double lon_input=0; - double llh[3]; - - static double cint[AACGM_KMAX][NCOORD][NFLAG]; - - #if DEBUG > 0 - printf("convert_geo_coord\n"); - #endif - - /* no date/time set so use current time */ - if (aacgm_date.year < 0) { /* AACGM_v2_SetNow();*/ - AACGM_v2_errmsg(0); - return -128; - } - - /* TRACE */ /* call tracing functions here and return */ - if ((code & TRACE) || (height_in > MAXALT && (code & ALLOWTRACE))) { - if (A2G & code) { /* AACGM-v2 to geographic */ - err = AACGM_v2_Trace_inv(lat_in,lon_in,height_in, lat_out,lon_out); - if ((code & GEOCENTRIC) == 0) { - geoc2geod(*lat_out,*lon_out,(RE+height_in)/RE, llh); - *lat_out = llh[0]; - } - } else { - err = AACGM_v2_Trace(lat_in,lon_in,height_in, lat_out,lon_out); - } - - return (err); - } - - /* determine the altitude dependence of the coefficients */ - flag = (A2G & code); /* 0 for G2A; 1 for A2G */ - if (height_in != height_old[flag]) { - alt_var = height_in/(double)MAXALT; - alt_var_sq = alt_var * alt_var; - alt_var_cu = alt_var * alt_var_sq; - alt_var_qu = alt_var * alt_var_cu; - - #if DEBUG > 1 - printf("alt_var = %lf\n", alt_var); - printf("alt_var_qu = %lf\n", alt_var_qu); - #endif - - #if DEBUG > 0 - printf("** HEIGHT INTERPOLATION **\n"); - #endif - - for (i=0; i 10 - printf("%lf %lf\n", cint[j][i][flag], - sph_harm_model.coef[j][i][0][flag]); - #endif - - } - } - height_old[flag] = height_in; - } - #if DEBUG > 1 - printf("cint[0][0][%d] = %lf\n", flag, cint[0][0][flag]); - printf("cint[%d][0][%d] = %lf\n", - AACGM_KMAX-1, flag, cint[AACGM_KMAX-1][0][flag]); - printf("cint[%d][%d][%d] = %lf\n", AACGM_KMAX-1, NCOORD-1, flag, - cint[AACGM_KMAX-1][NCOORD-1][flag]); - #endif - - x = y = z = 0; - - lon_input = lon_in*M_PI/180.0; - - if (flag == 0) colat_input = (90.-lat_in)*M_PI/180.0; - else { - /* use intermediate "at-altitude" coordinates for inverse trans. */ - i_err64 = AACGM_v2_CGM2Alt(height_in, lat_in, &lat_adj); - - if (i_err64 != 0) return -64; - colat_input= (90. - lat_adj)*M_PI/180; - } - - /* Compute the values of the spherical harmonic functions. - * NOTE: this function was adapted to use orthonormal SH functions */ - AACGM_v2_Rylm(colat_input, lon_input, order, ylmval); - - for (l=0; l<=order; l++) { - for (m=-l; m<=l; m++) { - k = l * (l+1) + m; /* SGS: changes indexing */ - - x += cint[k][0][flag]*ylmval[k]; - y += cint[k][1][flag]*ylmval[k]; - z += cint[k][2][flag]*ylmval[k]; - } - } - - /* COMMENT: SGS - * - * This answers one of my questions about how the coordinates for AACGM are - * guaranteed to be on the unit sphere. Here they compute xyz indpendently - * using the SH coefficients for each coordinate. They reject anything that - * is +/- .1 Re from the surface of the Earth. They then scale each xyz - * coordinate by the computed radial distance. This is a TERRIBLE way to do - * things... but necessary for the inverse transformation. - */ - - /* SGS - new method that ensures position is on unit sphere and results in a - * much better fit. Uses z coordinate only for sign, i.e., hemisphere. - */ - if (flag == 0) { - fac = x*x + y*y; - if (fac > 1.) { - /* we are in the forbidden region and the solution is undefined */ - *lat_out = NAN; - *lon_out = NAN; - - return 0; /* return -64; */ - } - - ztmp = sqrt(1. - fac); - z = (z < 0) ? -ztmp : ztmp; - - colat_temp = acos(z); - } else { - /* SGS - for inverse the old normalization produces lower overall errors...*/ - r = sqrt(x*x + y*y + z*z); - if ((r< 0.9) || (r > 1.1)) return -32; - - z /= r; - x /= r; - y /= r; - - if (z > 1.) colat_temp = 0; - else if (z < -1.) colat_temp = M_PI; - else colat_temp = acos(z); - } - - if ((fabs(x) < 1e-8) && (fabs(y) < 1e-8)) lon_temp = 0; - else lon_temp = atan2(y,x); - - lon_output = lon_temp; - - /* SGS - not used for forward transformation */ - /* - if (flag == 0) { - lat_alt =90 - colat_temp*180/PI; - altitude_to_cgm(height_in, lat_alt,&lat_adj); - colat_output = (90. - lat_adj) * PI/180; - } else colat_output = colat_temp; - */ - colat_output = colat_temp; - - *lat_out = (double) (90 - colat_output*180/M_PI); - *lon_out = (double) (lon_output*180/M_PI); - - if ((code & GEOCENTRIC) == 0 && (code & A2G)) { - geoc2geod(*lat_out,*lon_out,(RE+height_in)/RE, llh); - *lat_out = llh[0]; - } - - return 0; + double alt_var_cu=0, lon_temp=0, alt_var_sq=0, alt_var_qu=0; + double colat_output=0, r=0, x=0, y=0, z=0; + double ztmp, fac; + double alt_var=0; + double lon_input=0; + + static double cint[AACGM_KMAX][NCOORD][NFLAG]; + + #if DEBUG > 0 + printf("convert_geo_coord_v2\n"); + #endif + + /* no date/time set so use current time */ + if (aacgm_date.year < 0) { /* AACGM_v2_SetNow();*/ + AACGM_v2_errmsg(0); + return -128; + } + + /* TRACE */ /* call tracing functions here and return */ + if ((code & TRACE) || (height_in > MAXALT && (code & ALLOWTRACE))) { + if (A2G & code) { /* AACGM-v2 to geographic */ + err = AACGM_v2_Trace_inv(lat_in,lon_in,height_in, lat_out,lon_out, + igrf_filename); + + /* v2.3 moved to AACGM_v2_Convert + if ((code & GEOCENTRIC) == 0) { + geoc2geod(*lat_out,*lon_out,(RE+height_in)/RE, llh); + *lat_out = llh[0]; + } */ + } else { + err = AACGM_v2_Trace(lat_in,lon_in,height_in, lat_out,lon_out, + igrf_filename); + } + + return (err); + } + + /* determine the altitude dependence of the coefficients */ + flag = (A2G & code); /* 0 for G2A; 1 for A2G */ + if (height_in != height_old[flag]) { + alt_var = height_in/(double)MAXALT; + alt_var_sq = alt_var * alt_var; + alt_var_cu = alt_var * alt_var_sq; + alt_var_qu = alt_var * alt_var_cu; + + #if DEBUG > 1 + printf("alt_var = %lf\n", alt_var); + printf("alt_var_qu = %lf\n", alt_var_qu); + #endif + + #if DEBUG > 0 + printf("** HEIGHT INTERPOLATION **\n"); + #endif + + for (i=0; i 10 + printf("%35.30lf %35.30lf\n", cint[j][i][flag], + sph_harm_model.coef[j][i][0][flag]); + #endif + + } + } + + height_old[flag] = height_in; + } + #if DEBUG > 1 + printf("cint[0][0][%d] = %lf\n", flag, cint[0][0][flag]); + printf("cint[%d][0][%d] = %lf\n", + AACGM_KMAX-1, flag, cint[AACGM_KMAX-1][0][flag]); + printf("cint[%d][%d][%d] = %lf\n", AACGM_KMAX-1, NCOORD-1, flag, + cint[AACGM_KMAX-1][NCOORD-1][flag]); + #endif + + x = y = z = 0; + + lon_input = lon_in*DTOR; + + if (flag == 0) { + colat_input = (90.-lat_in)*DTOR; + } else { + /* use intermediate "at-altitude" coordinates for inverse trans. */ + i_err64 = AACGM_v2_CGM2Alt(height_in, lat_in, &lat_adj); + + if (i_err64 != 0) return -64; + colat_input= (90. - lat_adj)*DTOR; + } + + /* Compute the values of the spherical harmonic functions. + * NOTE: this function was adapted to use orthonormal SH functions */ + AACGM_v2_Rylm(colat_input, lon_input, order, ylmval); + + for (l=0; l<=order; l++) { + for (m=-l; m<=l; m++) { + k = l * (l+1) + m; /* SGS: changes indexing */ + + x += cint[k][0][flag]*ylmval[k]; + y += cint[k][1][flag]*ylmval[k]; + z += cint[k][2][flag]*ylmval[k]; + } + } + + /* COMMENT: SGS + * + * This answers one of my questions about how the coordinates for AACGM are + * guaranteed to be on the unit sphere. Here they compute xyz indpendently + * using the SH coefficients for each coordinate. They reject anything that + * is +/- .1 Re from the surface of the Earth. They then scale each xyz + * coordinate by the computed radial distance. This is a TERRIBLE way to do + * things... but necessary for the inverse transformation. + */ + + /* SGS - new method that ensures position is on unit sphere and results in a + * much better fit. Uses z coordinate only for sign, i.e., hemisphere. + */ + if (flag == 0) { + fac = x*x + y*y; + if (fac > 1.) { + /* we are in the forbidden region and the solution is undefined */ + *lat_out = HUGE_VAL; + *lon_out = HUGE_VAL; + + return -64; + } + + ztmp = sqrt(1. - fac); + z = (z < 0) ? -ztmp : ztmp; + + colat_temp = acos(z); + } else { + /* SGS - for inverse the old normalization produces lower overall errors...*/ + r = sqrt(x*x + y*y + z*z); + if ((r< 0.9) || (r > 1.1)) return -32; + + z /= r; + x /= r; + y /= r; + + if (z > 1.) colat_temp = 0; + else if (z < -1.) colat_temp = M_PI; + else colat_temp = acos(z); + } + + if ((fabs(x) < 1e-8) && (fabs(y) < 1e-8)) lon_temp = 0; + else lon_temp = atan2(y,x); + + lon_output = lon_temp; + + /* SGS - not used for forward transformation */ + /* + if (flag == 0) { + lat_alt =90 - colat_temp*180/PI; + altitude_to_cgm(height_in, lat_alt,&lat_adj); + colat_output = (90. - lat_adj) * PI/180; + } else colat_output = colat_temp; + */ + colat_output = colat_temp; + + *lat_out = (double) (90. - colat_output/DTOR); + *lon_out = (double) (lon_output/DTOR); + + /* v2.3 moved to AACGM_v2_Convert + if ((code & GEOCENTRIC) == 0 && (code & A2G)) { + geoc2geod(*lat_out,*lon_out,(RE+height_in)/RE, llh); + *lat_out = llh[0]; + } */ + + return 0; } @@ -744,53 +747,53 @@ int convert_geo_coord(double lat_in, double lon_in, double height_in, int AACGM_v2_LoadCoefFP(FILE *fp, int code) { - /* char tmp[64]; */ - double tmp; - int f,l,a,t,i; - - #if DEBUG > 0 - printf("AACGM_v2_LoadCoefFP\n"); - #endif - - if (fp==NULL) { - #if DEBUG > 0 - printf("NULL file pointer in AACGM_v2_LoadCoefFP\n"); - #endif - return -1; - } - - for (f=0;f 0 - printf("FILE error, aborting\n"); - #endif - fclose(fp); - return -1; - } - - sph_harm_model.coefs[t][a][l][f][code] = tmp; - } - } - } - } - - #if DEBUG > 10 - for (f=0;f 0 + printf("AACGM_v2_LoadCoefFP\n"); + #endif + + if (fp==NULL) { + #if DEBUG > 0 + printf("NULL file pointer in AACGM_v2_LoadCoefFP\n"); + #endif + return -1; + } + + for (f=0;f 0 + printf("FILE error, aborting\n"); + #endif + fclose(fp); + return -1; + } + + sph_harm_model.coefs[t][a][l][f][code] = tmp; + } + } + } + } + + #if DEBUG > 10 + for (f=0;f 0 - printf("loading %s\n", fname); - #endif - - fp = fopen(fname,"r"); - if (fp==NULL) { - #if DEBUG > 0 - printf("NULL file pointer in AACGM_v2_LoadCoef\n"); - #endif - return -1; - } - - err = AACGM_v2_LoadCoefFP(fp, code); - if (err != 0) { - #if DEBUG > 0 - printf("error in AACGM_v2_LoadCoefFP\n"); - #endif - return -2; - } - fclose(fp); - - return 0; + FILE *fp=NULL; + int err=-2; + + #if DEBUG > 0 + printf("loading %s\n", fname); + #endif + + fp = fopen(fname,"r"); + if (fp==NULL) { + #if DEBUG > 0 + printf("NULL file pointer in AACGM_v2_LoadCoef\n"); + #endif + return -1; + } + + err = AACGM_v2_LoadCoefFP(fp, code); + if (err != 0) { + #if DEBUG > 0 + printf("error in AACGM_v2_LoadCoefFP\n"); + #endif + return -2; + } + fclose(fp); + + return 0; } /*----------------------------------------------------------------------------- @@ -854,11 +857,12 @@ int AACGM_v2_LoadCoef(char *fname, int code) ; Load two sets of spherical harmonic coefficients. ; ; CALLING SEQUENCE: -; err = AACGM_v2_LoadCoefs(myear); +; err = AACGM_v2_LoadCoefs(myear, root); ; ; Input Arguments: ; myear - 5-year epoch year prior to desired time; bracketing ; set is +5 years. +; root - directory and file root for AACGM coefficient files ; ; Return Value: ; error code @@ -866,46 +870,48 @@ int AACGM_v2_LoadCoef(char *fname, int code) ;+----------------------------------------------------------------------------- */ -int AACGM_v2_LoadCoefs(int year) +int AACGM_v2_LoadCoefs(int year, char *root) { - char fname[256]; - char root[256]; - char yrstr[5]; - int ret=0; - - #if DEBUG > 0 - printf("AACGM_v2_LoadCoefs\n"); - #endif - /* default location of coefficient files */ - strcpy(root,getenv("AACGM_v2_DAT_PREFIX")); - if (strlen(root)==0) return -1; - - if (year <= 0) return -1; -// if (year==0) year=DEFAULT_YEAR; - sprintf(yrstr,"%4.4d",year); -// strcpy(fname,getenv("AACGM_DAT_PREFIX")); - - strcpy(fname,root); - strcat(fname,yrstr); - strcat(fname,".asc"); - #if DEBUG > 0 - printf("AACGM_v2_LoadCoefs: %s\n", fname); - #endif - ret = AACGM_v2_LoadCoef(fname,G2A); /* forward coefficients */ - if (ret != 0) return ret; - - sprintf(yrstr,"%4.4d",year+5); - strcpy(fname,root); - strcat(fname,yrstr); - strcat(fname,".asc"); - #if DEBUG > 0 - printf("AACGM_v2_LoadCoefs: %s\n", fname); - #endif - ret += AACGM_v2_LoadCoef(fname,A2G); /* inverse coefficients */ - - myear_old = year; - - return ret; + char fname[256]; + char yrstr[5]; + int ret=0; + + #if DEBUG > 0 + printf("AACGM_v2_LoadCoefs\n"); + #endif + /* default location of coefficient files */ + if(strlen(root) == 0 && getenv("AACGM_v2_DAT_PREFIX") != (char *)(NULL)) + strcpy(root,getenv("AACGM_v2_DAT_PREFIX")); + + if (strlen(root)==0) { + AACGM_v2_errmsg(2); + return -1; + } + + if (year <= 0) return -1; + sprintf(yrstr,"%4.4d",year); + + strcpy(fname,root); + strcat(fname,yrstr); + strcat(fname,".asc"); + #if DEBUG > 0 + printf("AACGM_v2_LoadCoefs: %s\n", fname); + #endif + ret = AACGM_v2_LoadCoef(fname,G2A); /* forward coefficients */ + if (ret != 0) return ret; + + sprintf(yrstr,"%4.4d",year+5); + strcpy(fname,root); + strcat(fname,yrstr); + strcat(fname,".asc"); + #if DEBUG > 0 + printf("AACGM_v2_LoadCoefs: %s\n", fname); + #endif + ret += AACGM_v2_LoadCoef(fname,A2G); /* inverse coefficients */ + + myear_old = year; + + return ret; } /*----------------------------------------------------------------------------- @@ -919,7 +925,7 @@ int AACGM_v2_LoadCoefs(int year) ; ; CALLING SEQUENCE: ; err = AACGM_v2_Convert(in_lat, in_lon, height, -; out_lat, out_lon, r, code); +; out_lat, out_lon, r, code, igrf_filename); ; ; Input Arguments: ; int_lat - double precision input latitude in degrees @@ -934,83 +940,115 @@ int AACGM_v2_LoadCoefs(int year) ; GEOCENTRIC - assume inputs are geocentric w/ RE=6371.2 ; ; Output Arguments: -; out_lat - double precision output latitude in degrees -; out_lon - double precision output longitude in degrees -; r - double radial distance in Re (always = 1.0) +; out_lat - output latitude in degrees +; out_lon - output longitude in degrees +; r - geocentric radial distance in Re ; ; Return Value: ; error code ; +; +; NOTES: +; +; All AACGM-v2 conversions are done in geocentric coordinates using a +; value of 6371.2 km for the Earth radius. +; +; For G2A conversion inputs are geographic latitude, longitude and +; height (glat,glon,height), specified as either geocentric or +; geodetic (default). For geodetic inputs a conversion to geocentric +; coordinates is performed, which changes the values of +; glat,glon,height. The output is AACGM-v2 latitude, longitude and +; the geocentric radius (mlat,mlon,r) using the geocentric height +; in units of RE. +; +; For A2G conversion inputs are AACGM-v2 latitude, longitude and the +; geocentric height (mlat,mlon,height). The latter can be obtained +; from the r output of the G2A conversion. The output is geographic +; latitude, longitude and height (glat,glon,height). If the +; gedodetic option is desired (default) a conversion of the outputs +; is performed, which changes the values of glat,glon,height. +; ;+----------------------------------------------------------------------------- */ int AACGM_v2_Convert(double in_lat, double in_lon, double height, - double *out_lat, double *out_lon, double *r, int code) + double *out_lat, double *out_lon, double *r, int code, + char *igrf_filename) { - int err; - int order=10; /* pass in so a lower order would be allowed? */ - double rtp[3]; - - #if DEBUG > 0 - printf("AACGM_v2_Convert\n"); - #endif - - /* height < 0 km */ - if (height < 0) { - fprintf(stderr, "WARNING: coordinate transformations are not intended " - "for altitudes < 0 km: %lf\n", height); - /* return -2; */ - } - - /* height > 2000 km not allowed for coefficients */ - if (height > MAXALT && !(code & (TRACE|ALLOWTRACE|BADIDEA))) { - fprintf(stderr, "ERROR: coefficients are not valid for altitudes " - "above %d km: %lf.\n", MAXALT, height); - fprintf(stderr, " You must either use field-line tracing " + int err; + int order=10; /* pass in so a lower order would be allowed? */ + double rtp[3]; + double llh[3]; + + #if DEBUG > 0 + printf("AACGM_v2_Convert\n"); + #endif + + /* height < 0 km */ + if (height < 0) { + fprintf(stderr, "WARNING: coordinate transformations are not intended " + "for altitudes < 0 km: %lf\n", height); + /* return -2; */ + } + + /* height > 2000 km not allowed for coefficients */ + if (height > MAXALT && !(code & (TRACE|ALLOWTRACE|BADIDEA))) { + fprintf(stderr, "ERROR: coefficients are not valid for altitudes " + "above %d km: %lf.\n", MAXALT, height); + fprintf(stderr, " You must either use field-line tracing " "(TRACE or ALLOWTRACE) or\n" - " indicate that you know this is a very bad idea " - "(BADIDEA)\n\n"); - return -4; - } - - /* latitude out of bounds */ - if (fabs(in_lat) > 90.) { - fprintf(stderr, "ERROR: latitude must be in the range -90 to +90 degrees: " - "%lf\n", in_lat); - return -8; - } - -// if (in_lon < 0) in_lon += 360.0; -// if (in_lon > 180.0) in_lon -= 360.0; - /* longitude out of bounds */ - if ((in_lon < -180) || (in_lon > 180)) { - fprintf(stderr, "ERROR: longitude must be in the range -180 to 180 " - "degrees: %lf\n", in_lon); - return -16; - } - -//printf("%d %d %d %d\n", -// code, GEOCENTRIC, code & GEOCENTRIC, (code & GEOCENTRIC)==0); - if ((code & GEOCENTRIC) == 0 && (code & A2G) == 0) { -//printf("GEODETIC\n"); - /* coordinates are given in geodetic coordinates and must be converted */ - geod2geoc(in_lat, in_lon, height, rtp); -//printf("lat: %f %f\n", in_lat, 90.d - rtp[1]/DTOR); -//printf("lon: %f %f\n", in_lon, rtp[2]/DTOR); -//printf("alt: %f %f\n", height, (rtp[0]-1.d)*RE); -//printf("\n"); - - /* modify lat/lon/alt to geocentric values */ - in_lat = 90. - rtp[1]/DTOR; - in_lon = rtp[2]/DTOR; - height = (rtp[0]-1.)*RE; - } - - err = convert_geo_coord(in_lat,in_lon,height, out_lat,out_lon, code,order); - *r = 1.0; - - if (err !=0) return -1; - return 0; + " indicate that you know this is a very bad idea " + "(BADIDEA)\n\n"); + return -4; + } + + /* latitude out of bounds */ + if (fabs(in_lat) > 90.) { + fprintf(stderr, "ERROR: latitude must be in the range -90 to +90 degrees: " + "%lf\n", in_lat); + return -8; + } + + /* longitude out of bounds */ +/* SGS v2.3 removing requirement that longitude be -180 to 180. Does not seems + * to matter and is inconsistent with IDL version: -180 to 180. + + if ((in_lon < -180) || (in_lon > 180)) { + fprintf(stderr, "ERROR: longitude must be in the range -180 to 180 " + "degrees: %lf\n", in_lon); + return -16; + } + */ + + /* if forward calculation (G2A) and input coordinates are given in geodetic + coordinates (default) then must first convert to geocentric coordinates */ + if ((code & GEOCENTRIC) == 0 && (code & A2G) == 0) { + geod2geoc(in_lat,in_lon,height, rtp); + + /* modify lat/lon/alt to geocentric values */ + in_lat = 90. - rtp[1]/DTOR; + in_lon = rtp[2]/DTOR; + height = (rtp[0]-1.)*RE; + } + + /* all inputs are geocentric */ + err = convert_geo_coord_v2(in_lat,in_lon,height, out_lat,out_lon, code,order, + igrf_filename); + /* all outputs are geocentric */ + + if ((code & A2G) == 0) { /* forward: G2A */ + *r = (height + RE)/RE; /* geocentric radial distance in RE */ + } else { /* inverse: A2G */ + if ((code & GEOCENTRIC) == 0) { /* geodetic outputs */ + geoc2geod(*out_lat,*out_lon,(RE+height)/RE, llh); + *out_lat = llh[0]; + height = llh[2]; + } + *r = height; /* height in km */ + } + + if (err !=0) return -1; + return 0; } /*----------------------------------------------------------------------------- @@ -1023,7 +1061,7 @@ int AACGM_v2_Convert(double in_lat, double in_lon, double height, ; any calls to AACGM_v2_Convert. ; ; CALLING SEQUENCE: -; err = AACGM_v2_SetDateTime(year, month, day, hour, minute, second); +; err = AACGM_v2_SetDateTime(year, month, day, hour, minute, second, root) ; ; Input Arguments: ; year - year [1900-2020) @@ -1032,6 +1070,7 @@ int AACGM_v2_Convert(double in_lat, double in_lon, double height, ; hour - hour of day [00-24] ; minute - minute of hour [00-60] ; second - second of minute [00-60] +; root - directory and file root of AACGM coefficient file ; ; Return Value: ; error code @@ -1040,38 +1079,38 @@ int AACGM_v2_Convert(double in_lat, double in_lon, double height, */ int AACGM_v2_SetDateTime(int year, int month, int day, - int hour, int minute, int second) + int hour, int minute, int second, char *root) { - int err, doy, ndays; - double fyear; - - doy = dayno(year,month,day,&ndays); - fyear = year + ((doy-1) + (hour + (minute + second/60.)/60.)/24.) / ndays; - - if ((fyear < IGRF_FIRST_EPOCH) || (fyear >= IGRF_LAST_EPOCH + 5.)) { - AACGM_v2_errmsg(1); - return (-1); - } - - aacgm_date.year = year; - aacgm_date.month = month; - aacgm_date.day = day; - aacgm_date.hour = hour; - aacgm_date.minute = minute; - aacgm_date.second = second; - aacgm_date.dayno = doy; - aacgm_date.daysinyear = ndays; - - #if DEBUG > 0 - printf("AACGM_v2_SetDateTime\n"); - printf("%03d: %04d%02d%02d %02d%02d:%02d\n", - aacgm_date.dayno, aacgm_date.year, aacgm_date.month, aacgm_date.day, - aacgm_date.hour, aacgm_date.minute, aacgm_date.second); - #endif - - err = AACGM_v2_TimeInterp(); - - return err; + int err, doy, ndays; + double fyear; + + doy = dayno(year,month,day,&ndays); + fyear = year + ((doy-1) + (hour + (minute + second/60.)/60.)/24.) / ndays; + + if ((fyear < IGRF_FIRST_EPOCH) || (fyear >= IGRF_LAST_EPOCH + 5.)) { + AACGM_v2_errmsg(1); + return (-1); + } + + aacgm_date.year = year; + aacgm_date.month = month; + aacgm_date.day = day; + aacgm_date.hour = hour; + aacgm_date.minute = minute; + aacgm_date.second = second; + aacgm_date.dayno = doy; + aacgm_date.daysinyear = ndays; + + #if DEBUG > 0 + printf("AACGM_v2_SetDateTime\n"); + printf("%03d: %04d%02d%02d %02d%02d:%02d\n", + aacgm_date.dayno, aacgm_date.year, aacgm_date.month, aacgm_date.day, + aacgm_date.hour, aacgm_date.minute, aacgm_date.second); + #endif + + err = AACGM_v2_TimeInterp(root); + + return err; } /*----------------------------------------------------------------------------- @@ -1101,17 +1140,17 @@ int AACGM_v2_SetDateTime(int year, int month, int day, */ int AACGM_v2_GetDateTime(int *year, int *month, int *day, - int *hour, int *minute, int *second, int *dayno) + int *hour, int *minute, int *second, int *dayno) { - *year = aacgm_date.year; - *month = aacgm_date.month; - *day = aacgm_date.day; - *hour = aacgm_date.hour; - *minute = aacgm_date.minute; - *second = aacgm_date.second; - *dayno = aacgm_date.dayno; - - return 0; + *year = aacgm_date.year; + *month = aacgm_date.month; + *day = aacgm_date.day; + *hour = aacgm_date.hour; + *minute = aacgm_date.minute; + *second = aacgm_date.second; + *dayno = aacgm_date.dayno; + + return 0; } /*----------------------------------------------------------------------------- @@ -1123,7 +1162,7 @@ int AACGM_v2_GetDateTime(int *year, int *month, int *day, ; Function to set date and time to current computer time in UT. ; ; CALLING SEQUENCE: -; err = AACGM_v2_SetNow(); +; err = AACGM_v2_SetNow(root); ; ; Return Value: ; error code @@ -1131,46 +1170,49 @@ int AACGM_v2_GetDateTime(int *year, int *month, int *day, ;+----------------------------------------------------------------------------- */ -int AACGM_v2_SetNow(void) +int AACGM_v2_SetNow(char *root) { - /* current time */ - int err, doy,ndays; - double fyear; - time_t now; - struct tm *tm_now; - - now = time(NULL); - tm_now = gmtime(&now); /* right now in UT */ - - doy = dayno(tm_now->tm_year,tm_now->tm_mon,tm_now->tm_mday,&ndays); - fyear = tm_now->tm_year + ((doy-1) + (tm_now->tm_hour + (tm_now->tm_min + - tm_now->tm_sec/60.)/60.)/24.) / ndays; - - if ((fyear < IGRF_FIRST_EPOCH) || (fyear >= IGRF_LAST_EPOCH + 5.)) { - fprintf(stderr, "\nDate range for AACGM-v2 is [%4d - %4d)\n\n", - IGRF_FIRST_EPOCH, IGRF_LAST_EPOCH + 5); - return (-1); - } - - aacgm_date.year = (*tm_now).tm_year + 1900; - aacgm_date.month = (*tm_now).tm_mon + 1; - aacgm_date.day = (*tm_now).tm_mday; - aacgm_date.hour = (*tm_now).tm_hour; - aacgm_date.minute = (*tm_now).tm_min; - aacgm_date.second = (*tm_now).tm_sec; - aacgm_date.dayno = (*tm_now).tm_yday + 1; - aacgm_date.daysinyear = ndays; - - #if DEBUG > 0 - printf("AACGM_v2_SetNow\n"); - printf("%03d: %04d%02d%02d %02d%02d:%02d\n", - aacgm_date.dayno, aacgm_date.year, aacgm_date.month, aacgm_date.day, - aacgm_date.hour, aacgm_date.minute, aacgm_date.second); - #endif - - err = AACGM_v2_TimeInterp(); - - return err; + /* current time */ + int err, doy,ndays; + double fyear; + time_t now; + struct tm *tm_now; + + time(&now); + tm_now = gmtime(&now); /* right now in UT */ + + doy = dayno(1900 + tm_now->tm_year,tm_now->tm_mon,tm_now->tm_mday,&ndays); + fyear = 1900 + tm_now->tm_year + ((doy-1) + (tm_now->tm_hour + + (tm_now->tm_min + tm_now->tm_sec/60.)/60.)/24.) / ndays; + + if ((fyear < IGRF_FIRST_EPOCH) || (fyear >= IGRF_LAST_EPOCH + 5.)) { + fprintf(stderr, "\nDate range for AACGM-v2 is [%4d - %4d)\n\n", + IGRF_FIRST_EPOCH, IGRF_LAST_EPOCH + 5); + fprintf(stderr, "%04d%02d%02d %02d%02d:%02d\n", tm_now->tm_year, + tm_now->tm_mon, tm_now->tm_mday, tm_now->tm_hour, + tm_now->tm_min, tm_now->tm_sec); + return (-1); + } + + aacgm_date.year = (*tm_now).tm_year + 1900; + aacgm_date.month = (*tm_now).tm_mon + 1; + aacgm_date.day = (*tm_now).tm_mday; + aacgm_date.hour = (*tm_now).tm_hour; + aacgm_date.minute = (*tm_now).tm_min; + aacgm_date.second = (*tm_now).tm_sec; + aacgm_date.dayno = (*tm_now).tm_yday + 1; + aacgm_date.daysinyear = ndays; + + #if DEBUG > 0 + printf("AACGM_v2_SetNow\n"); + printf("%03d: %04d%02d%02d %02d%02d:%02d\n", + aacgm_date.dayno, aacgm_date.year, aacgm_date.month, aacgm_date.day, + aacgm_date.hour, aacgm_date.minute, aacgm_date.second); + #endif + + err = AACGM_v2_TimeInterp(root); + + return err; } /*----------------------------------------------------------------------------- @@ -1189,46 +1231,53 @@ int AACGM_v2_SetNow(void) void AACGM_v2_errmsg(int ecode) { - char estr[100]; - - fprintf(stderr, "\n" - "**************************************************************************" - "\n"); - - switch (ecode) { - case 0: /* no Date/Time set */ - fprintf(stderr, - "* AACGM-v2 ERROR: No Date/Time Set *\n" - "* *\n" - "* You must specifiy the date and time in order to use AACGM coordinates, *\n" - "* which depend on the internal (IGRF) magnetic field. Before calling *\n" - "* AACGM_v2_Convert() you must set the date and time to the integer values*\n" - "* using the function: *\n" - "* *\n" - "* AACGM_v2_SetDateTime(year,month,day,hour,minute,second); *\n" - "* *\n" - "* or to the current computer time in UT using the function: *\n" - "* *\n" - "* AACGM_v2_SetNow(); *\n" - "* *\n" - "* subsequent calls to AACGM_v2_Convert() will use the last date and time *\n" - "* that was set, so update to the actual date and time that is desired. *" - "\n"); - break; - - case 1: /* Date/Time out of bounds */ - fprintf(stderr, - "* AACGM-v2 ERROR: Date out of bounds *\n" - "* *\n" - "* The current date range for AACGM-v2 coordinates is [1990-2020), which *\n" - "* corresponds to the date range for the IGRF12 model, including the *\n" - "* 5-year secular variation. *" - "\n"); - break; - } - fprintf(stderr, - "**************************************************************************" - "\n\n"); + + fprintf(stderr, "\n" + "**************************************************************************" + "\n"); + + switch (ecode) { + case 0: /* no Date/Time set */ + fprintf(stderr, + "* AACGM-v2 ERROR: No Date/Time Set *\n" + "* *\n" + "* You must specifiy the date and time in order to use AACGM coordinates, *\n" + "* which depend on the internal (IGRF) magnetic field. Before calling *\n" + "* AACGM_v2_Convert() you must set the date and time to the integer values*\n" + "* using the function: *\n" + "* *\n" + "* AACGM_v2_SetDateTime(year,month,day,hour,minute,second); *\n" + "* *\n" + "* or to the current computer time in UT using the function: *\n" + "* *\n" + "* AACGM_v2_SetNow(root); *\n" + "* *\n" + "* subsequent calls to AACGM_v2_Convert() will use the last date and time *\n" + "* that was set, so update to the actual date and time that is desired. *" + "\n"); + break; + + case 1: /* Date/Time out of bounds */ + fprintf(stderr, + "* AACGM-v2 ERROR: Date out of bounds *\n" + "* *\n" + "* The current date range for AACGM-v2 coordinates is [1990-2020), which *\n" + "* corresponds to the date range for the IGRF12 model, including the *\n" + "* 5-year secular variation. *" + "\n"); + break; + + case 2: /* COEF Path not set */ + fprintf(stderr, + "* AACGM-v2 ERROR: AACGM_v2_DAT_PREFIX path not set *\n" + "* *\n" + "* You must set the environment variable AACGM_v2_DAT_PREFIX to the *\n" + "\n"); + break; + } + fprintf(stderr, + "**************************************************************************" + "\n\n"); } /*----------------------------------------------------------------------------- @@ -1240,241 +1289,241 @@ void AACGM_v2_errmsg(int ecode) ; Interpolate coefficients between adjacent 5-year epochs ; ; CALLING SEQUENCE: -; err = AACGM_v2_TimeInterp(); +; err = AACGM_v2_TimeInterp(root); ; ;+----------------------------------------------------------------------------- */ -int AACGM_v2_TimeInterp(void) +int AACGM_v2_TimeInterp(char *root) { - int myear,f,l,a,t,err; - double fyear; - - /* myear is the epoch model year */ - myear = aacgm_date.year/5*5; - if (myear != myear_old) { /* load the new coefficients, if needed */ - err = AACGM_v2_LoadCoefs(myear); - if (err != 0) return err; - fyear_old = -1; /* force time interpolation */ - height_old[0] = -1.; /* force height interpolation */ - height_old[1] = -1.; - } - - /* fyear is the floating point time */ - fyear = aacgm_date.year + ((aacgm_date.dayno-1) + (aacgm_date.hour + - (aacgm_date.minute + aacgm_date.second/60.)/60.)/24.)/ - aacgm_date.daysinyear; - - /* time interpolation right here */ - if (fyear != fyear_old) { - #if DEBUG > 0 - printf("** TIME INTERPOLATION **\n"); - #endif - - for (f=0;f 0 + printf("** TIME INTERPOLATION **\n"); + #endif + + for (f=0;f 0.) ? -1 : 1; /* N or S hemisphere */ - - dsRE = dsRE0; - - /* - ; trace to magnetic equator - ; - ; Note that there is the possibility that the magnetic equator lies - ; at an altitude above the surface of the Earth but below the starting - ; altitude. I am not certain of the definition of CGM, but these - ; fieldlines map to very different locations than the solutions that - ; lie above the starting altitude. I am considering the solution for - ; this set of fieldlines as undefined; just like those that lie below - ; the surface of the Earth. - */ - while (idir*xyzm[2] < 0.) { - - for (kk=0;kk<3;kk++) xyzp[kk] = xyzg[kk]; /* save as previous */ - - AACGM_v2_RK45(xyzg, idir, &dsRE, eps, 1); // set to 0 for RK4: /noadapt) - - /* convert to magnetic Dipole coordinates */ - geo2mag(xyzg, xyzm); - - k++; - } - niter = k; - - if (niter > 1) { - /* now bisect stepsize (fixed) to land on magnetic equator w/in 1 m */ - for (k=0;k<3;k++) xyzc[k] = xyzp[k]; - kk = 0L; - while (dsRE > 1e-3/RE) { - dsRE *= .5; - for (k=0;k<3;k++) xyzp[k] = xyzc[k]; - AACGM_v2_RK45(xyzc, idir, &dsRE, eps, 0); /* using RK4 */ - geo2mag(xyzc,xyzm); - - /* Is it possible that resetting here causes a doubling of the tol? */ - if (idir * xyzm[2] > 0) - for (k=0;k<3;k++) xyzc[k] = xyzp[k]; - - kk++; - } - niter += kk; - } - - /* 'trace' back to reference surface along Dipole field lines */ - Lshell = sqrt(xyzc[0]*xyzc[0] + xyzc[1]*xyzc[1] + xyzc[2]*xyzc[2]); - if (Lshell < (RE+alt)/RE) { /* magnetic equator is below ... */ - *lat_out = NAN; - *lon_out = NAN; - - err = -1; - } else { - geo2mag(xyzc, xyzm); /* geographic to magnetic */ - car2sph(xyzm, rtp); - - *lat_out = -idir*acos(sqrt(1./Lshell))/DTOR; - *lon_out = rtp[2]/DTOR; - if (*lon_out > 180) *lon_out -= 360.; - - err = 0; - } - - return (err); + int err, kk, idir; + unsigned long k,niter; + double ds, dsRE, dsRE0, eps, Lshell; + double rtp[3],xyzg[3],xyzm[3],xyzc[3],xyzp[3]; + + /* set date for IGRF model */ + IGRF_SetDateTime(aacgm_date.year, aacgm_date.month, aacgm_date.day, + aacgm_date.hour, aacgm_date.minute, aacgm_date.second, + igrf_filename); + + /* Q: these could eventually be command-line options */ + ds = 1.; + dsRE = ds/RE; + dsRE0 = dsRE; + eps = 1.e-4/RE; + + /* for the model we are doing the tracing in geocentric coordinates */ + rtp[0] = (RE+alt)/RE; /* distance in RE; 1.0 is surface of sphere */ + rtp[1] = (90.-lat_in)*DTOR; /* colatitude in radians */ + rtp[2] = lon_in*DTOR; /* longitude in radians */ + + k = 0L; + /* convert position to Cartesian coords */ + sph2car(rtp, xyzg); + + /* convert to magnetic Dipole coordinates */ + geo2mag(xyzg, xyzm); + + idir = (xyzm[2] > 0.) ? -1 : 1; /* N or S hemisphere */ + + dsRE = dsRE0; + + /* + ; trace to magnetic equator + ; + ; Note that there is the possibility that the magnetic equator lies + ; at an altitude above the surface of the Earth but below the starting + ; altitude. I am not certain of the definition of CGM, but these + ; fieldlines map to very different locations than the solutions that + ; lie above the starting altitude. I am considering the solution for + ; this set of fieldlines as undefined; just like those that lie below + ; the surface of the Earth. + */ + while (idir*xyzm[2] < 0.) { + + for (kk=0;kk<3;kk++) xyzp[kk] = xyzg[kk]; /* save as previous */ + + AACGM_v2_RK45(xyzg, idir, &dsRE, eps, 1); /* set to 0 for RK4: /noadapt) */ + + /* convert to magnetic Dipole coordinates */ + geo2mag(xyzg, xyzm); + + k++; + } + niter = k; + + if (niter > 1) { + /* now bisect stepsize (fixed) to land on magnetic equator w/in 1 m */ + for (k=0;k<3;k++) xyzc[k] = xyzp[k]; + kk = 0L; + while (dsRE > 1e-3/RE) { + dsRE *= .5; + for (k=0;k<3;k++) xyzp[k] = xyzc[k]; + AACGM_v2_RK45(xyzc, idir, &dsRE, eps, 0); /* using RK4 */ + geo2mag(xyzc,xyzm); + + /* Is it possible that resetting here causes a doubling of the tol? */ + if (idir * xyzm[2] > 0) + for (k=0;k<3;k++) xyzc[k] = xyzp[k]; + + kk++; + } + niter += kk; + } + + /* 'trace' back to reference surface along Dipole field lines */ + Lshell = sqrt(xyzc[0]*xyzc[0] + xyzc[1]*xyzc[1] + xyzc[2]*xyzc[2]); + if (Lshell < (RE+alt)/RE) { /* magnetic equator is below ... */ + *lat_out = NAN; + *lon_out = NAN; + + err = -1; + } else { + geo2mag(xyzc, xyzm); /* geographic to magnetic */ + car2sph(xyzm, rtp); + + *lat_out = -idir*acos(sqrt(1./Lshell))/DTOR; + *lon_out = rtp[2]/DTOR; + if (*lon_out > 180) *lon_out -= 360.; + + err = 0; + } + + return (err); } int AACGM_v2_Trace_inv(double lat_in, double lon_in, double alt, - double *lat_out, double *lon_out) + double *lat_out, double *lon_out, char *igrf_filename) { - int err, kk, idir; - unsigned long k,niter; - double ds, dsRE, dsRE0, eps, Lshell; - double rtp[3],xyzg[3],xyzm[3],xyzc[3],xyzp[3]; - - // Q: will this load coefficients each time??? - /* set date for IGRF model */ - IGRF_SetDateTime(aacgm_date.year, aacgm_date.month, aacgm_date.day, - aacgm_date.hour, aacgm_date.minute, aacgm_date.second); - - // Q: these could eventually be command-line options - ds = 1.; - dsRE = ds/RE; - dsRE0 = dsRE; - eps = 1.e-4/RE; - - // Q: Test this - /* poles map to infinity */ - if (fabs(fabs(lat_in) - 90.) < 1e-6) - lat_in += (lat_in > 0) ? -1e-6 : 1e-6; - - Lshell = 1./(cos(lat_in*DTOR)*cos(lat_in*DTOR)); - if (Lshell <(RE+alt)/RE) { /* solution does not exist; the starting - * position at the magnetic equator is below - * the altitude of interest */ - *lat_out = NAN; - *lon_out = NAN; - err = -1; - } else { - /* magnetic Cartesian coordinates of fieldline trace starting point */ - xyzm[0] = Lshell*cos(lon_in*DTOR); - xyzm[1] = Lshell*sin(lon_in*DTOR); - xyzm[2] = 0.; - - /* geographic Cartesian coordinates of starting point */ - mag2geo(xyzm, xyzg); - - /* geographic spherical coordinates of starting point */ - car2sph(xyzg,rtp); - - k = 0L; - - /* direction of trace is determined by the starting hemisphere? */ - idir = (lat_in > 0) ? 1 : -1; - - dsRE = dsRE0; - - /* trace back to altitude above Earth */ - while (rtp[0] > (RE + alt)/RE) { - for (kk=0;kk<3;kk++) xyzp[kk] = xyzg[kk]; /* save as previous */ - - AACGM_v2_RK45(xyzg, idir, &dsRE, eps, 1); // set to 0 for RK4: /noadapt) - - car2sph(xyzg, rtp); - - k++; - } - niter = k; - - if (niter > 1) { - /* now bisect stepsize (fixed) to land on magnetic equator w/in 1 m */ - for (k=0;k<3;k++) xyzc[k] = xyzp[k]; - kk = 0L; - while (dsRE > 1e-3/RE) { - dsRE *= .5; - for (k=0;k<3;k++) xyzp[k] = xyzc[k]; - AACGM_v2_RK45(xyzc, idir, &dsRE, eps, 0); /* using RK4 */ - - car2sph(xyzc, rtp); - - if (rtp[0] < (RE + alt)/RE) - for (k=0;k<3;k++) xyzc[k] = xyzp[k]; - - kk++; - } - niter += kk; - } - - *lat_out = 90. - rtp[1]/DTOR; - *lon_out = rtp[2]/DTOR; - if (*lon_out > 180) *lon_out -= 360.; - err = 0; - } - - return (0); + int err, kk, idir; + unsigned long k,niter; + double ds, dsRE, dsRE0, eps, Lshell; + double rtp[3],xyzg[3],xyzm[3],xyzc[3],xyzp[3]; + + /* set date for IGRF model */ + IGRF_SetDateTime(aacgm_date.year, aacgm_date.month, aacgm_date.day, + aacgm_date.hour, aacgm_date.minute, aacgm_date.second, + igrf_filename); + + /* Q: these could eventually be command-line options */ + ds = 1.; + dsRE = ds/RE; + dsRE0 = dsRE; + eps = 1.e-4/RE; + + /* Q: Test this */ + /* poles map to infinity */ + if (fabs(fabs(lat_in) - 90.) < 1e-6) + lat_in += (lat_in > 0) ? -1e-6 : 1e-6; + + Lshell = 1./(cos(lat_in*DTOR)*cos(lat_in*DTOR)); + if (Lshell <(RE+alt)/RE) { /* solution does not exist; the starting + * position at the magnetic equator is below + * the altitude of interest */ + *lat_out = NAN; + *lon_out = NAN; + err = -1; + } else { + /* magnetic Cartesian coordinates of fieldline trace starting point */ + xyzm[0] = Lshell*cos(lon_in*DTOR); + xyzm[1] = Lshell*sin(lon_in*DTOR); + xyzm[2] = 0.; + + /* geographic Cartesian coordinates of starting point */ + mag2geo(xyzm, xyzg); + + /* geographic spherical coordinates of starting point */ + car2sph(xyzg,rtp); + + k = 0L; + + /* direction of trace is determined by the starting hemisphere? */ + idir = (lat_in > 0) ? 1 : -1; + + dsRE = dsRE0; + + /* trace back to altitude above Earth */ + while (rtp[0] > (RE + alt)/RE) { + for (kk=0;kk<3;kk++) xyzp[kk] = xyzg[kk]; /* save as previous */ + + AACGM_v2_RK45(xyzg, idir, &dsRE, eps, 1); /* set to 0 for RK4: /noadapt)*/ + + car2sph(xyzg, rtp); + + k++; + } + niter = k; + + if (niter > 1) { + /* now bisect stepsize (fixed) to land on magnetic equator w/in 1 m */ + for (k=0;k<3;k++) xyzc[k] = xyzp[k]; + kk = 0L; + while (dsRE > 1e-3/RE) { + dsRE *= .5; + for (k=0;k<3;k++) xyzp[k] = xyzc[k]; + AACGM_v2_RK45(xyzc, idir, &dsRE, eps, 0); /* using RK4 */ + + car2sph(xyzc, rtp); + + if (rtp[0] < (RE + alt)/RE) + for (k=0;k<3;k++) xyzc[k] = xyzp[k]; + + kk++; + } + niter += kk; + } + + *lat_out = 90. - rtp[1]/DTOR; + *lon_out = rtp[2]/DTOR; + if (*lon_out > 180) *lon_out -= 360.; + err = 0; + } + + return (err); } diff --git a/src/c_aacgm_v2/astalglib.c b/src/c_aacgm_v2/astalglib.c new file mode 100755 index 00000000..fab747ff --- /dev/null +++ b/src/c_aacgm_v2/astalglib.c @@ -0,0 +1,728 @@ +/* astalglib.c + * + * collection of Astronomical Algorithms see LICENSE.txt + * + * minor reformatting by SGS for including in AACGM-v2 software + * + */ + +/* ----------------- REFERENCE ------------------------ + +The software contained herein is derived from algorithms published +in the book _Astronomical Algorithms_, Second Edition, by Jean Meeus, +publisher: Willman-Bell, Inc., Richmond, Virginia, 1998 (corrections +dated 2005). + +The book will be referred to as "Meeus" for short. + +*/ + +#include +#include +#include "astalg.h" + +/* AstAlg_apparent_obliquity + ========================= + Author: Kile Baker +*/ + +/* This function calculates the apparent obliquity (angle between + the earth's spin axis and the ecliptic). It uses the + mean obliquity and the effect of the lunar position. + + Calling sequence: + eps = AstAlg_apparent_obliquity(double julian_day) + + The value is returned in degrees. +*/ + +double AstAlg_apparent_obliquity(double jd) { + + static double last_jd, last_eps; + + /* if we've already calculated the value just return it */ + + if (jd == last_jd) return (last_eps); + + last_jd = jd; + last_eps = AstAlg_mean_obliquity(jd) + + 0.00256 * cos(AstAlg_DTOR * AstAlg_lunar_ascending_node(jd)); + + return (last_eps); +} + +/* AstAlg_apparent_solar_longitude + =============================== + Author: Kile Baker +*/ + +/* This function calculates the apparent solar longitude for a given + Julian Day using the geometric solar longitude along with + the effect of the position of the moon. + + Calling Sequence: + asl = AstAlg_apparent_solar_longitude(double jd) + + The returned value is in degrees. +*/ + +double AstAlg_apparent_solar_longitude(double jd) { + + static double last_jd, last_asl; + + /* if we've already calculated the value simply return it */ + + if (jd == last_jd) return (last_asl); + + last_jd = jd; + last_asl = AstAlg_geometric_solar_longitude(jd) - 0.00569 - + 0.00478*sin(AstAlg_DTOR * AstAlg_lunar_ascending_node(jd)); + + return (last_asl); +} + +/* AstAlg_dday + ============= + Author: Kile Baker +*/ + +/* This routine is a fairly trivial one. In the AstAlg software + it is commonly necessary to know the decimal day (i.e. day of month + and fraction thereof. This routine simply calculates the decimal + day as a double precision number from the day, hour, minute, and second. + + No attempt is made to check the validity of the arguments so you can + create a garbage result if you give it nonsense values like setting + the day to 1000 or the hour to 50, etc. +*/ + +double AstAlg_dday(int day, int hour, int minute, int second) { + +/* SGS Changed */ +/* Kile, this is WRONG! Nathanial found this bug */ +/* return (double) (day + hour/24.0 + minute/60.0 + second/3600.0);*/ + return ((double) (day + (hour + minute/60.0 + second/3600.0)/24.)); +} + +/* AstAlg_equation_of_time + ========================= + Author: Kile Baker +*/ + +/* This function returns the equation of time for a given + Julian date. + + The returned value is the difference between the apparent time and the + mean solar time. The correction is returned in minutes and is always + between + and - 20. + + A positive value menas that the true sun crosses the observer's meridian + before the mean sun (i.e. mean time is lagging). + + Calling sequence: + + eqt = AstAlg_equation_of_time(double jd) + + This routine calls AstAlg_mean_solar_longitude, + AstAlg_solar_right_ascension, AstAlg_mean_obliquity, + and AstAlg_nutation_corr. +*/ + +double AstAlg_equation_of_time(double jd) { + + static double last_jd, last_eqt; + + double eqt; + double dpsi, deps, sml, sra, obliq; + + /* if we've already calculated the value, simply return it */ + + if (jd == last_jd) return (last_eqt); + + /* first get all the separate pieces */ + sml = AstAlg_mean_solar_longitude(jd); + sra = AstAlg_solar_right_ascension(jd); + obliq = AstAlg_mean_obliquity(jd); + AstAlg_nutation_corr(jd, &dpsi, &deps); + + /* now put it all together */ + + eqt = sml - 0.0057183 - sra + dpsi*cos(AstAlg_DTOR*(obliq + deps)); + + eqt = dmod(eqt,360.0); + + /* now convert from degrees to minutes */ + eqt = 4.0 * eqt; + + if (eqt > 20.0) eqt = eqt - 24.0*60.0; /*wrap back 24 hours*/ + if (eqt < -20.0) eqt = 24.0*60.0 + eqt; /*wrap forward 24 hours */ + + last_jd = jd; + last_eqt = eqt; + + return (eqt); +} + +/* AstAlg_geometric_solar_longitude + ================================ + Author: Kile Baker +*/ + +/* This function calculates the geometric_solar_longitude. + + Calling sequence: + gsl = AstAlg_geometric_solar_longitude(julian_day) + + The returned value is in degrees from 0 to 360 + + The function requires the mean solar longitude and the + mean solar anomaly. + +*/ + +double AstAlg_geometric_solar_longitude(double jd) { + + static double last_jd, last_gsl; + + double sml, sma, tau, gc; + + /* if we've already calculated the value for this Julian Day just + return it */ + + if (last_jd == jd) return (last_gsl); + + /* compute the delta-tau in centuries from the reference time J2000 */ + tau = (jd - J2000)/36525.0; + + sml = AstAlg_mean_solar_longitude(jd); + + /* we need the mean solar anomaly in radians because it's used in some + sine functions */ + sma = AstAlg_DTOR * AstAlg_mean_solar_anomaly(jd); + + gc = (1.914602 - 0.004817*tau - 0.000014*(tau*tau)) * sin(sma) + + (0.019993 - 0.000101*tau) * sin(2.0*sma) + + 0.000289 * sin(3.0*sma); + + sml += gc; +/* sml = sml + gc;*/ + + /* make sure the value is between 0 and 360 degrees */ + sml = dmod(sml, 360.0); + + if (sml < 0.0) sml += 360.0; + + last_jd = jd; + last_gsl = sml; + + return (sml); +} + +/* AstAlg_jde2calendar + =================== + Author: Kile Baker +*/ + +/* This routine converts Julian Day to calendar year, month, day, + hour, minute and second */ + +void AstAlg_jde2calendar(double jd, + int *year, + int *month, + int *day, + int *hour, + int *minute, + int *second) { + + long a,b,c,d,e, z; + long alpha; + double f; + double dday, resid; + +/* this is a rather complicated calculation and uses some clever tricks + that I (Kile Baker) don't really understand. It all comes out of + Meeus (chapter 7) and it all seems to work. */ + + jd += 0.5; + +/* z is the integer part of jd+.5 and f is the remaining fraction of a day */ + + z = (long) jd; + f = jd - z; + + if (z < 2299161) a = z; + else { + alpha = (long) ((z - 1867216.25)/36524.25); + a = z + 1 + alpha - (long)(alpha/4); + } + + b = a + 1524; + c = (long) ((b - 122.1)/365.25); + d = (long) (365.25 * c); + e = (long) ((b - d)/30.6001); +/* NOTE: Meeus states emphatically that the constant 30.6001 must be used + to avoid calculating dates such as Feb. 0 instead of Jan. 31. */ + +/* the value of e is basically giving us the month */ + + if (e < 14) *month = e-1; + else *month = e-13; + +/* the value of c is basically giving us the year */ + + if (*month > 2) *year = c - 4716; + else *year = c - 4715; + +/* now calclulate the decimal day */ + + dday = b - d - (double) ((long) (30.6001 * e)) + f; + +/* extract the integer part of the day and get the left over residual + in hours */ + + *day = (int) dday; + resid = (dday - *day) * 24.0; + +/* extract the integer part of the hours and get the left over minutes */ + + *hour = (int) resid; + resid = (resid - *hour)*60.0; + +/* extract the integer part of the minutes and get the left over seconds */ + *minute = (int) resid; + resid = (resid - *minute)*60.0; + +/* round of the value of second to the nearest second */ + + *second = (int)(resid + 0.5); +} + +/* AstAlg_jde + ========== + Author: Kile Baker +*/ + +/* This routine returns the Julian Day as a double precision value + + Calling sequence: + + jd = AstAlg_jde( int year, int month, double day) + +*/ + +/* NOTE: Meeus uses Terrestrial Dynamic (TD) time, sometimes referred to + as Ephemeris Time. The 'E' in the name of this function (jde) is + a reminder that the time is Julian Day in Ephemeris time. + + All of the rest of the algorithms provided in this set of software + calculate their values with reference to Jan 1, 2000 at noon TD. + This reference time is referred to as J2000 and is a defined constant + in AstAlg.h. + + Since all the calculations involve differences between the JDE value + and J2000, the difference between TD at UT can safely be ignored. + The only difference comes from any leaap seconds that have been applied + between the reference time and the time of interest. Those differences + are sufficiently small (a few seconds) so that we ignore them. + + If you want to have higher accuracy you probably need to calculate the + decimal day in terms of TD rather than UT. Meeus has details of how + to do this. +*/ + +double AstAlg_jde(int year, int month, double day) { + +/* NOTE: the value of 'year' must be the full 4-digit year */ + + int a; + double b; + +/* if the month is January or February, treat it as belonging + to the previous year. This makes the leap year correction + easier to handle */ + + if (month <= 2) { + year--; + month += 12; + } + +/* the next few lines perform the leap year correction. It deals + with the centuries correctly */ + + a = (int) (year/100); + b = (double) (2 - a + a/4); + +/* ideally, the constant 30.6001 could simply be 30.6, but adding the + additional .0001 to it ensures that truncation error doesn't result + in an incorrect calculation */ + + return ((double) ( (long) (365.25*(year + 4716)) + + (double) ( (long) (30.6001 * (month + 1)))) + day + b - 1524.5); +} + +/* AstAlg_lunar_ascending_node + =========================== + Author: Kile Baker +*/ + +/* This function calculates the location of the moon's ascending node. + This value affects the nutation of the Earth's spin axis. + + Calling Sequence: + asc_node = AstAlg_lunar_ascending_node(double jd) + + where jd is the Julian Day +*/ + +double AstAlg_lunar_ascending_node(double jd) { + + static double last_jd, last_ascn; + double tau, omega; + +/* if we've already calculated the value just return it */ + + if (jd == last_jd) return (last_ascn); + +/* calculate the delta-time in centuries with respect to + the reference time J2000 */ + + tau = (jd - J2000)/36525.0; + +/* omega = 125 - 1934 * tau + .002*tau^2 + t^3/4.5e5 */ + + omega = (((tau/4.50e5 + 2.0708e-3)*tau - 1.934136261e3)*tau) + 125.04452; + +/* now make sure omega is between 0 and 360 */ + + omega = dmod(omega, 360.0); + + if (omega < 0.0) omega += 360.0; + + last_jd = jd; + last_ascn = omega; + + return (omega); +} + +/* AstAlg_mean_lunar_longitude + =========================== + Author: Kile Baker +*/ + +/* This function calculates the mean lunar longitude for a given + Julian Day. + + Calling Sequence: + + lunlong = AstAlg_mean_lunar_longitude(double jd) + +*/ + +double AstAlg_mean_lunar_longitude(double jd) { + + static double last_jd, last_llong; + double tau, llong; + +/* simply return the value if we've already calculated it */ + + if (jd == last_jd) return (last_llong); + +/* calculate the detla-time from the reference time J2000 in centuries */ + + tau = (jd - J2000)/36525.0; + + llong = 218.3165 + 481267.8813 * tau; + +/* make sure the resulting value is between 0 and 360 */ + + llong = dmod(llong, 360.0); + + if (llong < 0) llong += 360.0; + + last_jd = jd; + last_llong = llong; + + return (llong); +} + +/* AstAlg_apparent_solar_longitude + =============================== + Author: Kile Baker +*/ + +/* This function calcules the mean obliquity of the earth. That is + the inclination of the rotation axis relative to the ecliptic plane. + + Calling Sequence: + + e0 = AstAlg_mean_obliquity(julian_date) + + The returned value is in degrees. + +*/ + +double AstAlg_mean_obliquity(double jd) { + + static double last_jd, last_e0; + double tau; + const double coefs[] = {23.439291111111, -0.0130041666667, -1.638888889e-7, + 5.036111111e-7}; + +/* if we've already calculated this value for this date simply return it */ + + if (jd == last_jd) return (last_e0); + + /* the coefficients in Meeus are given in degrees, minutes and seconds + so I have to convert them to double precision degrees first. + Although this could be done before compilation it would make + it hard to check the code with the book. I therefore do + the calculation here but only the first time the routine is called. + */ + +/* if (coeffs[3] != 0.001813/3600.0) { + coeffs[3] = 0.001813/3600.0; + coeffs[2] = -0.00059/3600.0; + coeffs[1] = -46.8150/3600.0; + coeffs[0] = 23.0 + 26.0/60.0 + 21.448/3600.0; + } +*/ + + tau = (jd - J2000)/36525.0; + +/* Now calculate the value of e0 */ + last_e0 = ((((coefs[3]*tau) + coefs[2]) * tau) + coefs[1]) * tau + coefs[0]; + last_jd = jd; + + return (last_e0); +} + +/* AstAlg_apparent_solar_anomaly + ============================= + Author: Kile Baker +*/ + +/* This function calculates the mean solar anomaly for a given Julian day +(see function AstAlg_jde.c). + + Calling Sequence: + slong = AstAlg_mean_solar_anomaly(double jd); + + The returned value is in degrees and ranges from 0 to 360 + +*/ + +double AstAlg_mean_solar_anomaly( double jd ) { + +/* last calculated value for this function is saved. This is done + to avoid recalculating the value multiple times, since it is used + in several other functions in the AstAlg library */ + + static double last_jd, last_sma; + + double tau, sma; + + if (jd == last_jd) return (last_sma); + +/* calculate the difference between the requested Julian Day and + the reference value, J2000, in centuries */ + + tau = (jd - J2000)/36525.0; + sma = 357.5291130 + 35999.05029 * tau - 0.0001537 * (tau*tau); + +/* make sure the value is between 0 and 360 */ + + sma = dmod(sma, 360); + + if (sma < 0.0) sma += 360.0; + + last_jd = jd; + last_sma = sma; + + return (sma); +} + +/* AstAlg_mean_solar_longitude + =========================== + Author: Kile Baker +*/ + +/* This function calculates the mean solar longitude for a given Julian day (see function AstAlg_jde.c). + + Calling Sequence: + slong = AstAlg_mean_solar_longitude(double jd); + + The returned value is in degrees and ranges from 0 to 360 + +*/ + +double AstAlg_mean_solar_longitude( double jd ) { + +/* the mean solar longitude gets called from several other functions + and we don't want to have to recalculate the value every time, so + the last calculated value is saved as a static value */ + + static double last_jd, last_sl; + + static const double coefs[] = {280.4664567, 360007.6982779, + 0.03032028, 2.00276381406e-5, + -6.53594771242e-5, -0.50e-6 }; + double tau, sl; + int i; + + if (jd == last_jd) return (last_sl); + +/* calculate the difference between the requested Julian day and the + reference Julian day, J2000, in terms of milennia */ + + tau = (jd - J2000)/365250.0; + +/* now calculate the solar longitude using the delta-time tau and the + coefficients */ + + sl = 0.0; + for (i=5; i>=0; i--) { + sl = tau * sl + coefs[i]; + } + +/* make sure the result is between 0 and 360 degrees */ + + sl = dmod(sl, 360); + + if (sl < 0.0) sl += 360.0; + + last_jd = jd; + last_sl = sl; + + return (sl); +} + +/* AstAlg_nutation_corr + ==================== + Author: Kile Baker +*/ + +/* This routine calculates the corrections to the solar longitude and + obliquity that arize due to the nutation of the earth's spin. + + Calling Sequence: + + void AstAlg_nutation_corr(double jd, double *slong_corr, double *obliq_corr) + +*/ + +void AstAlg_nutation_corr(double jd, double *slong_corr, double *obliq_corr) +{ + double slong, lunlong, omega; + static double last_jd, last_slcorr, last_oblcorr; + +/* just return the values if they've already been calculated */ + + if (jd == last_jd) { + *slong_corr = last_slcorr; + *obliq_corr = last_oblcorr; + return; /* SGS added */ + } + +/* get the mean solar longitude and mean lunar longitude in radians */ + + slong = AstAlg_DTOR * AstAlg_mean_solar_longitude(jd); + lunlong = AstAlg_DTOR * AstAlg_mean_lunar_longitude(jd); + omega = AstAlg_DTOR * AstAlg_lunar_ascending_node(jd); + +/* the next line computes the correction to the solar longitude in arcsecs */ + + *slong_corr = -17.20 * sin(omega) - 1.32 * sin(2.0*slong) - + 0.23 * sin(2.0*lunlong) + 0.21 * sin(2.0*omega); + +/* convert from arcsec to degrees */ + *slong_corr = *slong_corr/3600.0; + +/* Next we calculate the correction to the obliquity in arcsecs */ + + *obliq_corr = 9.20 * cos(omega) + 0.57 * cos(2.0*slong) + + 0.10 * cos(2.0*lunlong) - 0.09 * cos(2.0*omega); + + *obliq_corr = *obliq_corr/3600.0; + +/* save the calculated values in case their needed again */ + last_jd = jd; + last_slcorr = *slong_corr; + last_oblcorr = *obliq_corr; +} + +/* AstAlg_apparent_solar_declination + ================================= + Author: Kile Baker +*/ + +/* This function returns the apparent declination of the sun for a given + Julian Day. It uses the apparent_obliquity and the apparent solar + longitude. + + Calling sequence: + + sdec = AstAlg_solar_declination(double jd) + + The value is returned in degrees. +*/ + +double AstAlg_solar_declination(double jd) { + + static double last_jd, last_sdec; + double sindec; + +/* if we've already calculated the value simply return it */ + + if (jd == last_jd) return (last_sdec); + + sindec = sin(AstAlg_DTOR * AstAlg_apparent_obliquity(jd)) * + sin(AstAlg_DTOR * AstAlg_apparent_solar_longitude(jd)); + + last_jd = jd; + last_sdec = asin(sindec)/AstAlg_DTOR; + + return (last_sdec); +} + +/* AstAlg_solar_right_ascension + ============================ + Author: Kile Baker +*/ + +/* This function returns the right ascension of the sun for a given + Julian date. + + Calling sequence: + ra = AstAlg_solar_right_ascension(double jd) + + The value is returned in DEGREES, not in the more traditional + hours. Convert to hour angle by dividing degrees by 15. + + The function calls the functions AstAlg_apparent_solar_longitude + and AstAlg_apparent_obliquity + +*/ + +double AstAlg_solar_right_ascension(double jd) { + + static double last_jd, last_ra; + double eps, slong, alpha; + +/* if we've already calculated the value, simply return it. */ + + if (jd == last_jd) return (last_ra); + + /* get the solar longitude in radians */ + slong = AstAlg_DTOR * AstAlg_apparent_solar_longitude(jd); + + /* get the obliquity in radians */ + eps = AstAlg_DTOR * AstAlg_apparent_obliquity(jd); + + alpha = atan2(cos(eps)*sin(slong),cos(slong)); + + last_ra = alpha/AstAlg_DTOR; + last_jd = jd; + + return (last_ra); +} + diff --git a/src/c_aacgm_v2/genmag.c b/src/c_aacgm_v2/genmag.c index eeba0d5a..957e1cd7 100644 --- a/src/c_aacgm_v2/genmag.c +++ b/src/c_aacgm_v2/genmag.c @@ -26,19 +26,19 @@ int dayno(int year, int month, int day, int *diy) { - int k,tot; - int ndays[] = {31,28,31,30,31,30,31,31,30,31,30,31}; + int k,tot; + int ndays[] = {31,28,31,30,31,30,31,31,30,31,30,31}; - *diy = 365; - if(((year%4==0)&&(year%100!=0))||(year%400==0)) { - ndays[1]++; - *diy = 366; - } + *diy = 365; + if(((year%4==0)&&(year%100!=0))||(year%400==0)) { + ndays[1]++; + *diy = 366; + } - tot = 0; - for (k=0; k #include "igrflib.h" #include "genmag.h" +#include "astalg.h" -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -//#define DEBUG 1 +/*#define DEBUG 1*/ /* TO DO: should these stuff go in igrflib.h? */ -struct { - int year; - int month; - int day; - int hour; - int minute; - int second; - int dayno; - int daysinyear; -} igrf_date = {-1,-1,-1,-1,-1,-1,-1}; - -struct { - double ctcl; - double ctsl; - double stcl; - double stsl; - double ct0; - double st0; - double cl0; - double sl0; +static struct { + int year; + int month; + int day; + int hour; + int minute; + int second; + int dayno; + int daysinyear; +} igrf_date = {-1,-1,-1,-1,-1,-1,-1,-1}; + +static struct { + double ctcl; + double ctsl; + double stcl; + double stsl; + double ct0; + double st0; + double cl0; + double sl0; } geopack = {0.,0.,0.,0.,0.,0.,0.,0.}; -double IGRF_coef_set[MAXNYR][IGRF_MAXK]; /* all the coefficients */ -double IGRF_svs[IGRF_MAXK]; /* secular variations */ -double IGRF_coefs[IGRF_MAXK]; /* interpolated coefficients */ -int nmx; /* order of expansion */ +static double IGRF_coef_set[MAXNYR][IGRF_MAXK]; /* all the coefficients */ +static double IGRF_svs[IGRF_MAXK]; /* secular variations */ +static double IGRF_coefs[IGRF_MAXK]; /* interpolated coefficients */ +static int nmx; /* order of expansion */ /*----------------------------------------------------------------------------- ; for debugging @@ -46,11 +43,11 @@ int nmx; /* order of expansion */ */ void pause(void) { - char ch; + char ch; - fprintf(stdout, "(Hit Enter to coninue..."); - scanf("%c", &ch); - fprintf(stdout, "\n"); + fprintf(stdout, "(Hit Enter to coninue..."); + scanf("%c", &ch); + fprintf(stdout, "\n"); } /*----------------------------------------------------------------------------- @@ -73,7 +70,7 @@ void pause(void) ; k 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ... ; ; CALLING SEQUENCE: -; err = IGRF_loadcoeffs(filename); +; err = IGRF_loadcoeffs(); ; ; Input Arguments: ; filename - name of file which contains IGRF coefficients; default @@ -87,213 +84,229 @@ void pause(void) int IGRF_loadcoeffs(char *filename) { - int i,j,k,l,m,n, ll,mm; - int fac, len; - int iyear, nyear; - int dgrf[MAXNYR], epoch[MAXNYR]; - char jnk; - char header[2][MAXSTR], line[MAXSTR]; - double fyear; - double coef, sv; - double Slm[IGRF_MAXK], fctrl[2*IGRF_ORDER+1], dfc[2*IGRF_ORDER]; - FILE *fp; - - #if DEBUG > 0 - printf("IGRF_loadceoffs\n"); - #endif - - #if DEBUG > 0 - printf("Schmidt quasi-normalization factors\n"); - printf("===================================\n\n"); - #endif - - /* factorial */ - fctrl[0] = fctrl[1] = 1.; - for (k=2; k<= 2*IGRF_ORDER; k++) - fctrl[k] = k*fctrl[k-1]; - - //for(k=0; k<=2*IGRF_ORDER; k++) printf("%lf\n", fctrl[k]); - - /* double factorial */ - dfc[1] = 1; - for (k=3; k<2*IGRF_ORDER; k+=2) - dfc[k] = dfc[k-2]*k; - - for (l=0; l<=IGRF_ORDER; l++) { - for (m=0; m<=l; m++) { - k = l * (l+1) + m; /* 1D index for l,m */ - n = l * (l+1) - m; /* 1D index for l,m */ - - fac = (m) ? 2 : 1; - /* Davis 2004; Wertz 1978 recursion - Slm[k] = Slm[n] = sqrt(fac*fctrl[l-m]/fctrl[l+m])*dfc[2*l-1]/fctrl[l-m]; - */ - /* Winch 2004 */ - Slm[k] = Slm[n] = sqrt(fac*fctrl[l-m]/fctrl[l+m]); - - #if DEBUG > 0 - printf("$ %2d %2d %2d %e %e %e\n", l, m, k, fctrl[l-m],fctrl[l+m],Slm[k]); - printf("$ %2d %2d %2d %e %e %e\n", l,-m, n, fctrl[l-m],fctrl[l+m],Slm[n]); - #endif - } - } - - /* get the coefficients */ - fp = fopen(filename, "r"); - if (fp == NULL) { - fprintf(stderr, "File not found: %s\n", filename); - return (-1); - } - - /* read first two header lines */ - for (k=0; k<2; k++) { - jnk = ' '; - m = 0; - while (jnk != '\n') { - fscanf(fp, "%c", &jnk); - header[k][m] = (jnk == '\n') ? (char)0 : jnk; - m++; - } - } - - /* get next line */ - jnk = ' '; - m = 0; - while (jnk != '\n') { - fscanf(fp, "%c", &jnk); - line[m] = (jnk == '\n') ? (char)0 : jnk; - m++; - } - len = m; - #if DEBUG > 0 - fprintf(stderr, "%s\n", line); - #endif - - /* count how many D/IGRF years */ - nyear = 0; - for (m=0; m MAXNYR) { - fprintf(stderr, "Too many years in file: %d\n", nyear); - return (-2); - } - #if DEBUG > 0 - fprintf(stderr, "%d years\n", nyear); - #endif - - iyear = 0; - for (m=0; m 0 - for (m=0; m 0 + printf("IGRF_loadceoffs\n"); + #endif + + /* file containing the IGRF coefficients */ + if(strlen(filename) == 0 && getenv("IGRF_COEFFS") != (char *)(NULL)) + filename = getenv("IGRF_COEFFS"); + + if(strlen(filename) == 0) + { + printf("\n"); + printf("*************************************************************\n"); + printf("* You MUST set the environment variable IGRF_COEFFS \n"); + printf("*************************************************************\n"); + return (-99); + } +/* strcpy(filename,getenv("IGRF_COEFFS")); */ + + #if DEBUG > 1 + printf("Schmidt quasi-normalization factors\n"); + printf("===================================\n\n"); + #endif + + /* factorial */ + fctrl[0] = fctrl[1] = 1.; + for (k=2; k<= 2*IGRF_ORDER; k++) + fctrl[k] = k*fctrl[k-1]; + + /*for(k=0; k<=2*IGRF_ORDER; k++) printf("%lf\n", fctrl[k]); */ + + /* double factorial */ + dfc[1] = 1; + for (k=3; k<2*IGRF_ORDER; k+=2) + dfc[k] = dfc[k-2]*k; + + for (l=0; l<=IGRF_ORDER; l++) { + for (m=0; m<=l; m++) { + k = l * (l+1) + m; /* 1D index for l,m */ + n = l * (l+1) - m; /* 1D index for l,m */ + + fac = (m) ? 2 : 1; + /* Davis 2004; Wertz 1978 recursion + Slm[k] = Slm[n] = sqrt(fac*fctrl[l-m]/fctrl[l+m])*dfc[2*l-1]/fctrl[l-m]; + */ + /* Winch 2004 */ + Slm[k] = Slm[n] = sqrt(fac*fctrl[l-m]/fctrl[l+m]); + + #if DEBUG > 1 + printf("$ %2d %2d %2d %e %e %e\n", l, m, k, fctrl[l-m],fctrl[l+m],Slm[k]); + printf("$ %2d %2d %2d %e %e %e\n", l,-m, n, fctrl[l-m],fctrl[l+m],Slm[n]); + #endif + } + } + + /* get the coefficients */ + fp = fopen(filename, "r"); + if (fp == NULL) { + fprintf(stderr, "File not found: %s\n", filename); + return (-1); + } + + /* read first two header lines */ + for (k=0; k<2; k++) { + jnk = ' '; + m = 0; + while (jnk != '\n') { + fscanf(fp, "%c", &jnk); +/* header[k][m] = (jnk == '\n') ? (char)0 : jnk;*/ + m++; + } + } + + /* get next line */ + jnk = ' '; + m = 0; + while (jnk != '\n') { + fscanf(fp, "%c", &jnk); + line[m] = (jnk == '\n') ? (char)0 : jnk; + m++; + } + len = m; + #if DEBUG > 1 + fprintf(stderr, "%s\n", line); + #endif + + /* count how many D/IGRF years */ + nyear = 0; + for (m=0; m MAXNYR) { + fprintf(stderr, "Too many years in file: %d\n", nyear); + return (-2); + } + #if DEBUG > 1 + fprintf(stderr, "%d years\n", nyear); + #endif + + iyear = 0; + for (m=0; m 1 + for (m=0; m 0 - fprintf(stderr, "%8.2lf\n", fyear); - #endif - } - - #if DEBUG > 0 - for (m=0; m 0 - fprintf(stderr, "%d %d %d %d %f\n", k, l, n, 0, IGRF_coef_set[n][k]); - #endif - } - fscanf(fp, "%lf", &sv); /* secular variation */ - IGRF_svs[k] = sv * Slm[k]; /* NORMALIZE */ - fscanf(fp, "%c", &jnk); /* */ - - for (m=1; m<=l; m++) { - k = l * (l+1) + m; /* 1D index for l,m */ - fscanf(fp, "%c", &jnk); /* g or h */ - fscanf(fp, "%d %d", &ll, &mm); /* l amd m */ - - for (n=0; n 0 - fprintf(stderr, "%d %d %d %d %f\n", k, l, n, m, IGRF_coef_set[n][k]); - #endif - } - fscanf(fp, "%lf", &sv); /* secular variation */ - IGRF_svs[k] = sv * Slm[k]; /* NORMALIZE */ - fscanf(fp, "%c", &jnk); /* */ - - k = l * (l+1) - m; /* 1D index for l,m */ - fscanf(fp, "%c", &jnk); /* g or h */ - fscanf(fp, "%d %d", &ll, &mm); /* l amd m */ - for (n=0; n 0 - fprintf(stderr, "%d %d %d %d %f\n", k, l, n, -m, IGRF_coef_set[n][k]); - #endif - } - fscanf(fp, "%lf", &sv); /* secular variation */ - IGRF_svs[k] = sv * Slm[k]; /* NORMALIZE */ - - /* note, some files end each line with while others are */ - fscanf(fp, "%c", &jnk); /* */ - if (jnk == 13) fscanf(fp, "%c", &jnk); /* */ - } - - #if DEBUG > 0 - pause(); - #endif - } - fclose(fp); - - #if DEBUG > 0 - for (n=0; n 0 - fprintf(stderr, "%d\n", (2000-1900)/5); - /* print coefficients in order */ - for (l=0; l<=IGRF_ORDER; l++) { - for (m=-l; m<=l; m++) { - k = l * (l+1) + m; - fprintf(stderr, "%2d %3d %3d: %e\n", l,m,k, - IGRF_coef_set[(1980-1900)/5][k]); - } - } - pause(); - #endif - - return (0); + */ + jnk = ' '; + while (jnk != 'm') fscanf(fp, "%c", &jnk); + + /* read the years, which should be 5-year integer epochs... */ + for (m=0; m 1 + fprintf(stderr, "%8.2lf\n", fyear); + #endif + } + + #if DEBUG > 1 + for (m=0; m 1 + fprintf(stderr, "%d %d %d %d %f\n", k, l, n, 0, IGRF_coef_set[n][k]); + #endif + } + fscanf(fp, "%lf", &sv); /* secular variation */ + IGRF_svs[k] = sv * Slm[k]; /* NORMALIZE */ + fscanf(fp, "%c", &jnk); /* */ + + for (m=1; m<=l; m++) { + k = l * (l+1) + m; /* 1D index for l,m */ + fscanf(fp, "%c", &jnk); /* g or h */ + fscanf(fp, "%d %d", &ll, &mm); /* l amd m */ + + for (n=0; n 1 + fprintf(stderr, "%d %d %d %d %f\n", k, l, n, m, IGRF_coef_set[n][k]); + #endif + } + fscanf(fp, "%lf", &sv); /* secular variation */ + IGRF_svs[k] = sv * Slm[k]; /* NORMALIZE */ + fscanf(fp, "%c", &jnk); /* */ + + k = l * (l+1) - m; /* 1D index for l,m */ + fscanf(fp, "%c", &jnk); /* g or h */ + fscanf(fp, "%d %d", &ll, &mm); /* l amd m */ + for (n=0; n 1 + fprintf(stderr, "%d %d %d %d %f\n", k, l, n, -m, IGRF_coef_set[n][k]); + #endif + } + fscanf(fp, "%lf", &sv); /* secular variation */ + IGRF_svs[k] = sv * Slm[k]; /* NORMALIZE */ + + /* note, some files end each line with while others are */ + fscanf(fp, "%c", &jnk); /* */ + if (jnk == 13) fscanf(fp, "%c", &jnk); /* */ + } + + #if DEBUG > 2 + pause(); + #endif + } + fclose(fp); + + #if DEBUG > 1 + for (n=0; n 1 + fprintf(stderr, "%d\n", (2000-1900)/5); + /* print coefficients in order */ + for (l=0; l<=IGRF_ORDER; l++) { + for (m=-l; m<=l; m++) { + k = l * (l+1) + m; + fprintf(stderr, "%2d %3d %3d: %e\n", l,m,k, + IGRF_coef_set[(1980-1900)/5][k]); + } + } + pause(); + #endif + + return (0); } /*----------------------------------------------------------------------------- @@ -337,10 +350,10 @@ int IGRF_loadcoeffs(char *filename) int IGRF_Plm(double theta, int order, double *plmval, double *dplmval) { int l,m,k,n,p; - double a,b; /* factors */ - double st,ct,kfac; + double a,b; /* factors */ + double st,ct; - if (order > IGRF_ORDER) return (-1); + if (order > IGRF_ORDER) return (-1); st = sin(theta); ct = cos(theta); @@ -351,54 +364,54 @@ int IGRF_Plm(double theta, int order, double *plmval, double *dplmval) { for (l=1; l<=order; l++) { k = l * (l+1) + l; /* l = m */ n = (l-1) * l + l-1; /* l-1 = m-l, i.e., previous l=m */ - /* Davis 2004; Wertz 1978 recursion + /* Davis 2004; Wertz 1978 recursion plmval[k] = plmval[n]*st; - dplmval[k] = dplmval[n]*st + plmval[n]*ct; - */ - /* numerical recipies in C */ -// a = 1-2*l; /* reverse order to remove Condon-Shortley phase */ - a = 2*l-1; + dplmval[k] = dplmval[n]*st + plmval[n]*ct; + */ + /* numerical recipies in C */ + /* a = 1-2*l;*/ /* reverse order to remove Condon-Shortley phase */ + a = 2*l-1; plmval[k] = a*plmval[n]*st; - dplmval[k] = a*(dplmval[n]*st + plmval[n]*ct); + dplmval[k] = a*(dplmval[n]*st + plmval[n]*ct); - #if DEBUG > 0 + #if DEBUG > 1 printf("%2d %3d %e %e\n", l, k, plmval[k], dplmval[k]); - #endif + #endif } - plmval[2] = ct; /* 1,0 */ - dplmval[2] = -st; /* 1,0 */ + plmval[2] = ct; /* 1,0 */ + dplmval[2] = -st; /* 1,0 */ /* compute values of P^{l,m} and dP^{l,m}/dtheta */ for (l=2; l<=order; l++) { for (m=0; m 0 + */ + if (m == l-1) { + plmval[k] = a*ct*plmval[n]/(l-m); + dplmval[k] = a*(ct*dplmval[n] - st*plmval[n])/(l-m); + } else { + b = l+m-1; + plmval[k] = (a*ct*plmval[n] - b*plmval[p])/(l-m); + dplmval[k] = (a*(ct*dplmval[n] - st*plmval[n]) - b*dplmval[p])/(l-m); + } + #if DEBUG > 1 printf("%2d %2d %3d %e %e\n", l, m, k, plmval[k], dplmval[k]); - #endif + #endif } } @@ -434,94 +447,89 @@ int IGRF_Plm(double theta, int order, double *plmval, double *dplmval) { int IGRF_compute(const double rtp[], double brtp[]) { - int k,l,m,n; -// double brr,btt,bpp; - double tbrtp[3], st, theta; - double aor, afac; - double dplmval[IGRF_MAXK], plmval[IGRF_MAXK]; - double cosm_arr[IGRF_ORDER+1], sinm_arr[IGRF_ORDER+1]; - - #if DEBUG > 0 - printf("IGRF_compute\n"); - #endif - - /* no date/time set so bail */ - if (igrf_date.year < 0) { - IGRF_msg_notime(); - return -128; - } - - /* Must avoid singularit at the poles (dividing by sin(theta) later) */ - theta = rtp[1]; - st = sin(theta); - if (fabs(st) < 1e-15) theta += (st < 0.) ? 1e-15 : -1e-15; - - /* Compute the values of the Legendre Polynomials, and derivatives */ - IGRF_Plm(theta,nmx,plmval,dplmval); - -// aor = RE/r; /* a/r, where RE = a */ -// aor = RE/rtp[0]; /* a/r, where RE = a */ - aor = 1./rtp[0]; /* r is in units of RE to be consistent with geopack, */ - /* we want RE/r */ - - //printf("aor = %lf\n", aor); - afac = aor*aor; - - /* array of trig functions in phi for faster computation */ - for (k=0; k<=IGRF_ORDER; k++) { -// cosm_arr[k] = cos(k*phi); -// sinm_arr[k] = sin(k*phi); - cosm_arr[k] = cos(k*rtp[2]); - sinm_arr[k] = sin(k*rtp[2]); - } - -// *br = *btheta = *bphi = 0.; - for (k=0;k<3;k++) brtp[k] = 0; - - for (l=1; l<=nmx; l++) { /* no l = 0 term in IGRF */ -// brr = btt = bpp = 0.; - for (k=0;k<3;k++) tbrtp[k] = 0; -// printf("l = %d, afac = %lf\n", l, afac); - for (m=0; m<=l; m++) { - k = l*(l+1) + m; /* g */ - n = l*(l+1) - m; /* h */ - - tbrtp[0] += (IGRF_coefs[k]*cosm_arr[m] + IGRF_coefs[n]*sinm_arr[m]) * - plmval[k]; - tbrtp[1] += (IGRF_coefs[k]*cosm_arr[m] + IGRF_coefs[n]*sinm_arr[m]) * - dplmval[k]; - tbrtp[2] += (-IGRF_coefs[k]*sinm_arr[m] + IGRF_coefs[n]*cosm_arr[m]) * - m*plmval[k]; - -// printf("%2d %2d %e %e %e\n", l,m, IGRF_coefs[k],IGRF_coefs[n], plmval[k]); -// printf("[]: %e %e %e\n", tbrtp[0], tbrtp[1], tbrtp[2]); -// printf(" %2d: brr=%lf, coef[k]=%lf, coef[n]=%lf, plmval[k]=%lf\n", -// m,brr,IGRF_coefs[k],IGRF_coefs[n],plmval[k]); -// printf(" %2d: brr=%lf, cosm=%lf, sinm=%lf\n", m,brr,cosm_arr[m],sinm_arr[m]); - } -// printf("%2d brr = %lf\n", l,brr); - afac *= aor; - -// *br += afac*(l+1)*brr; -// *btheta -= afac*btt; -// *bphi -= afac*bpp; - brtp[0] += afac*(l+1)*tbrtp[0]; - brtp[1] -= afac*tbrtp[1]; - brtp[2] -= afac*tbrtp[2]; - } - -// *bphi /= sin(theta); + int k,l,m,n; +/* double brr,btt,bpp; */ + double tbrtp[3], st, theta; + double aor, afac; + double dplmval[IGRF_MAXK], plmval[IGRF_MAXK]; + double cosm_arr[IGRF_ORDER+1], sinm_arr[IGRF_ORDER+1]; + + #if DEBUG > 0 + printf("IGRF_compute\n"); + #endif + + /* no date/time set so bail */ + if (igrf_date.year < 0) { + IGRF_msg_notime(); + return -128; + } + + /* Must avoid singularit at the poles (dividing by sin(theta) later) */ + theta = rtp[1]; + st = sin(theta); + if (fabs(st) < 1e-15) theta += (st < 0.) ? 1e-15 : -1e-15; + + /* Compute the values of the Legendre Polynomials, and derivatives */ + IGRF_Plm(theta,nmx,plmval,dplmval); + +/* aor = RE/r;*/ /* a/r, where RE = a */ +/* aor = RE/rtp[0];*/ /* a/r, where RE = a */ + aor = 1./rtp[0]; /* r is in units of RE to be consistent with geopack, */ + /* we want RE/r */ + + /*printf("aor = %lf\n", aor);*/ + afac = aor*aor; + + /* array of trig functions in phi for faster computation */ + for (k=0; k<=IGRF_ORDER; k++) { + cosm_arr[k] = cos(k*rtp[2]); + sinm_arr[k] = sin(k*rtp[2]); + } + + for (k=0;k<3;k++) brtp[k] = 0; + + for (l=1; l<=nmx; l++) { /* no l = 0 term in IGRF */ + for (k=0;k<3;k++) tbrtp[k] = 0; + for (m=0; m<=l; m++) { + k = l*(l+1) + m; /* g */ + n = l*(l+1) - m; /* h */ + + tbrtp[0] += (IGRF_coefs[k]*cosm_arr[m] + IGRF_coefs[n]*sinm_arr[m]) * + plmval[k]; + tbrtp[1] += (IGRF_coefs[k]*cosm_arr[m] + IGRF_coefs[n]*sinm_arr[m]) * + dplmval[k]; + tbrtp[2] += (-IGRF_coefs[k]*sinm_arr[m] + IGRF_coefs[n]*cosm_arr[m]) * + m*plmval[k]; + +/* printf("%2d %2d %e %e %e\n", l,m, IGRF_coefs[k],IGRF_coefs[n], plmval[k]); +// printf("[]: %e %e %e\n", tbrtp[0], tbrtp[1], tbrtp[2]); +// printf(" %2d: brr=%lf, coef[k]=%lf, coef[n]=%lf, plmval[k]=%lf\n", +// m,brr,IGRF_coefs[k],IGRF_coefs[n],plmval[k]); +// printf(" %2d: brr=%lf, cosm=%lf, sinm=%lf\n", m,brr,cosm_arr[m],sinm_arr[m]);*/ + } +/* printf("%2d brr = %lf\n", l,brr);*/ + afac *= aor; + +/* *br += afac*(l+1)*brr; +// *btheta -= afac*btt; +// *bphi -= afac*bpp; */ + brtp[0] += afac*(l+1)*tbrtp[0]; + brtp[1] -= afac*tbrtp[1]; + brtp[2] -= afac*tbrtp[2]; + } + +/* *bphi /= sin(theta); //printf("*** %e %e\n", brtp[2], sin(rtp[1])); -//printf("*** %e %e %e\n", brtp[0], brtp[1], brtp[2]); - brtp[2] /= sin(theta); -// if (sin(rtp[1]) > 1.e-19) { /* from geopack ... */ -// brtp[2] /= sin(rtp[1]); -// } else { -// if (cos(rtp[1]) < 0.) brtp[2] = -brtp[2]; -// } -//printf("*** %e %e %e\n", brtp[0], brtp[1], brtp[2]); - - return (0); +//printf("*** %e %e %e\n", brtp[0], brtp[1], brtp[2]); */ + brtp[2] /= sin(theta); +/* if (sin(rtp[1]) > 1.e-19) { *//* from geopack ... */ +/* brtp[2] /= sin(rtp[1]); +// } else { +// if (cos(rtp[1]) < 0.) brtp[2] = -brtp[2]; +// } +//printf("*** %e %e %e\n", brtp[0], brtp[1], brtp[2]); */ + + return (0); } /*----------------------------------------------------------------------------- @@ -542,91 +550,91 @@ int IGRF_compute(const double rtp[], double brtp[]) { */ int IGRF_interpolate_coefs(void) { - int i,k,l,m, myear; - double fyear; - double g10,g11,h11,sq,sqq,sqr; + int i,k,l,m, myear; + double fyear; + double g10,g11,h11,sq,sqq,sqr; - #if DEBUG > 0 - printf("** TIME INTERPOLATION **\n"); - #endif + #if DEBUG > 0 + printf("** TIME INTERPOLATION **\n"); + #endif - /* fyear is the floating point time */ - fyear = igrf_date.year + ((igrf_date.dayno-1) + (igrf_date.hour + - (igrf_date.minute + igrf_date.second/60.)/60.)/24.)/ - igrf_date.daysinyear; + /* fyear is the floating point time */ + fyear = igrf_date.year + ((igrf_date.dayno-1) + (igrf_date.hour + + (igrf_date.minute + igrf_date.second/60.)/60.)/24.)/ + igrf_date.daysinyear; - /* NOTE: FORTRAN code allows 10-year extrapolation beyond last epoch. + /* NOTE: FORTRAN code allows 10-year extrapolation beyond last epoch. * Here we are limiting to only 5 */ - if (fyear < IGRF_FIRST_EPOCH || fyear > IGRF_LAST_EPOCH + 5) { - /* reset date */ - igrf_date.year = igrf_date.month = igrf_date.day = -1; - igrf_date.hour = igrf_date.minute = igrf_date.second = -1; - igrf_date.dayno = igrf_date.daysinyear = -1; - - fprintf(stdout, "Date range for current IGRF model is: %4d to %4d\n\n", - IGRF_FIRST_EPOCH, IGRF_LAST_EPOCH+5); - return (-3); - } - - myear = igrf_date.year/5*5; /* epoch year */ - nmx = (igrf_date.year < 1995) ? 10 : 13; /* order of expansion */ - i = (myear - IGRF_FIRST_EPOCH)/5; /* index of first set of coefs */ - - if (fyear < IGRF_LAST_EPOCH) { - /* interpolate bounding coefficients */ - for (l=1; l<=nmx; l++) { /* no l = 0 term in IGRF */ - for (m=-l; m<=l; m++) { - k = l * (l+1) + m; /* SGS: changes indexing */ - IGRF_coefs[k] = IGRF_coef_set[i][k] + (fyear-myear)* - (IGRF_coef_set[i+1][k]-IGRF_coef_set[i][k])/5; - } - } - } else { - /* use secular varation */ - for (l=1; l<=nmx; l++) { /* no l = 0 term in IGRF */ - for (m=-l; m<=l; m++) { - k = l * (l+1) + m; /* SGS: changes indexing */ - IGRF_coefs[k] = IGRF_coef_set[i][k] + (fyear-myear)*IGRF_svs[k]; - } - } - } - - /* compute the components of the unit vector EzMag in geographic coordinates: - * sin(theta0)*cos(lambda0), sin(theta0)*sin(lambda0) - */ + if (fyear < IGRF_FIRST_EPOCH || fyear > IGRF_LAST_EPOCH + 5) { + /* reset date */ + igrf_date.year = igrf_date.month = igrf_date.day = -1; + igrf_date.hour = igrf_date.minute = igrf_date.second = -1; + igrf_date.dayno = igrf_date.daysinyear = -1; + + fprintf(stdout, "Date range for current IGRF model is: %4d to %4d\n\n", + IGRF_FIRST_EPOCH, IGRF_LAST_EPOCH+5); + return (-3); + } + + myear = igrf_date.year/5*5; /* epoch year */ + nmx = (igrf_date.year < 1995) ? 10 : 13; /* order of expansion */ + i = (myear - IGRF_FIRST_EPOCH)/5; /* index of first set of coefs */ + + if (fyear < IGRF_LAST_EPOCH) { + /* interpolate bounding coefficients */ + for (l=1; l<=nmx; l++) { /* no l = 0 term in IGRF */ + for (m=-l; m<=l; m++) { + k = l * (l+1) + m; /* SGS: changes indexing */ + IGRF_coefs[k] = IGRF_coef_set[i][k] + (fyear-myear)* + (IGRF_coef_set[i+1][k]-IGRF_coef_set[i][k])/5; + } + } + } else { + /* use secular varation */ + for (l=1; l<=nmx; l++) { /* no l = 0 term in IGRF */ + for (m=-l; m<=l; m++) { + k = l * (l+1) + m; /* SGS: changes indexing */ + IGRF_coefs[k] = IGRF_coef_set[i][k] + (fyear-myear)*IGRF_svs[k]; + } + } + } + + /* compute the components of the unit vector EzMag in geographic coordinates: + * sin(theta0)*cos(lambda0), sin(theta0)*sin(lambda0) + */ /* C & IDL index: k = l * (l+1) + m */ - g10 = -IGRF_coefs[2]; /* 1*2+0 = 2 */ - g11 = IGRF_coefs[3]; /* 1*2+1 = 3 */ - h11 = IGRF_coefs[1]; /* 1*2-1 = 1 */ - - sq = g11*g11 + h11*h11; - - sqq = sqrt(sq); - sqr = sqrt(g10*g10 + sq); - - geopack.sl0 = -h11/sqq; - geopack.cl0 = -g11/sqq; - geopack.st0 = sqq/sqr; - geopack.ct0 = g10/sqr; - - geopack.stcl = geopack.st0*geopack.cl0; - geopack.stsl = geopack.st0*geopack.sl0; - geopack.ctsl = geopack.ct0*geopack.sl0; - geopack.ctcl = geopack.ct0*geopack.cl0; - - #if DEBUG > 0 - printf("sl0 = %lf\n", geopack.sl0); - printf("cl0 = %lf\n", geopack.cl0); - printf("st0 = %lf\n", geopack.st0); - printf("ct0 = %lf\n", geopack.ct0); - printf("stcl = %lf\n", geopack.stcl); - printf("stsl = %lf\n", geopack.stsl); - printf("ctsl = %lf\n", geopack.ctsl); - printf("ctcl = %lf\n", geopack.ctcl); - #endif - - return (0); + g10 = -IGRF_coefs[2]; /* 1*2+0 = 2 */ + g11 = IGRF_coefs[3]; /* 1*2+1 = 3 */ + h11 = IGRF_coefs[1]; /* 1*2-1 = 1 */ + + sq = g11*g11 + h11*h11; + + sqq = sqrt(sq); + sqr = sqrt(g10*g10 + sq); + + geopack.sl0 = -h11/sqq; + geopack.cl0 = -g11/sqq; + geopack.st0 = sqq/sqr; + geopack.ct0 = g10/sqr; + + geopack.stcl = geopack.st0*geopack.cl0; + geopack.stsl = geopack.st0*geopack.sl0; + geopack.ctsl = geopack.ct0*geopack.sl0; + geopack.ctcl = geopack.ct0*geopack.cl0; + + #if DEBUG > 0 + printf("sl0 = %lf\n", geopack.sl0); + printf("cl0 = %lf\n", geopack.cl0); + printf("st0 = %lf\n", geopack.st0); + printf("ct0 = %lf\n", geopack.ct0); + printf("stcl = %lf\n", geopack.stcl); + printf("stsl = %lf\n", geopack.stsl); + printf("ctsl = %lf\n", geopack.ctsl); + printf("ctcl = %lf\n", geopack.ctcl); + #endif + + return (0); } /*----------------------------------------------------------------------------- @@ -639,7 +647,7 @@ int IGRF_interpolate_coefs(void) { ; any calls to IGRF functions. ; ; CALLING SEQUENCE: -; err = IGRF_SetDateTime(year, month, day, hour, minute, second); +; err = IGRF_SetDateTime(year, month, day, hour, minute, second, filename) ; ; Input Arguments: ; year - year [1965-2014] @@ -648,6 +656,7 @@ int IGRF_interpolate_coefs(void) { ; hour - hour of day [00-24] ; minute - minute of hour [00-60] ; second - second of minute [00-60] +; filename - file with IGRF coefficients ; ; Return Value: ; error code @@ -656,42 +665,39 @@ int IGRF_interpolate_coefs(void) { */ int IGRF_SetDateTime(int year, int month, int day, - int hour, int minute, int second) + int hour, int minute, int second, char *filename) { - char *fname; - int err; - - /* load coefficients if not already loaded */ - if (igrf_date.year != year || igrf_date.month != month || igrf_date.day != day) { - fname = getenv("IGRF_12_COEFFS"); - if (fname==NULL) { - err = IGRF_loadcoeffs(IGRF_FILE); - } else { - err = IGRF_loadcoeffs(fname); - } - } - - - if (err) return (err); - - igrf_date.year = year; - igrf_date.month = month; - igrf_date.day = day; - igrf_date.hour = hour; - igrf_date.minute = minute; - igrf_date.second = second; - igrf_date.dayno = dayno(year,month,day,&(igrf_date.daysinyear)); - - #if DEBUG > 0 - printf("IGRF_SetDateTime\n"); - printf("%03d: %04d%02d%02d %02d%02d:%02d\n", - igrf_date.dayno, igrf_date.year, igrf_date.month, igrf_date.day, - igrf_date.hour, igrf_date.minute, igrf_date.second); - #endif - - err = IGRF_interpolate_coefs(); - - return (err); + int err = 0; + + /* load coefficients if not already loaded */ + if (igrf_date.year < 0) + err = IGRF_loadcoeffs(filename); + + if (err) return (err); + + if (igrf_date.year != year || igrf_date.month != month || + igrf_date.day != day || igrf_date.hour != hour || + igrf_date.minute != minute || igrf_date.second != second) { + + igrf_date.year = year; + igrf_date.month = month; + igrf_date.day = day; + igrf_date.hour = hour; + igrf_date.minute = minute; + igrf_date.second = second; + igrf_date.dayno = dayno(year,month,day,&(igrf_date.daysinyear)); + + #if DEBUG > 0 + printf("IGRF_SetDateTime\n"); + printf("%03d: %04d%02d%02d %02d%02d:%02d\n", + igrf_date.dayno, igrf_date.year, igrf_date.month, igrf_date.day, + igrf_date.hour, igrf_date.minute, igrf_date.second); + #endif + + err = IGRF_interpolate_coefs(); + } + + return (err); } /*----------------------------------------------------------------------------- @@ -721,17 +727,17 @@ int IGRF_SetDateTime(int year, int month, int day, */ int IGRF_GetDateTime(int *year, int *month, int *day, - int *hour, int *minute, int *second, int *dayno) + int *hour, int *minute, int *second, int *dayno) { - *year = igrf_date.year; - *month = igrf_date.month; - *day = igrf_date.day; - *hour = igrf_date.hour; - *minute = igrf_date.minute; - *second = igrf_date.second; - *dayno = igrf_date.dayno; - - return 0; + *year = igrf_date.year; + *month = igrf_date.month; + *day = igrf_date.day; + *hour = igrf_date.hour; + *minute = igrf_date.minute; + *second = igrf_date.second; + *dayno = igrf_date.dayno; + + return 0; } /*----------------------------------------------------------------------------- @@ -743,7 +749,7 @@ int IGRF_GetDateTime(int *year, int *month, int *day, ; Function to set date and time to current computer time in UT. ; ; CALLING SEQUENCE: -; err = IGRF_SetNow(); +; err = IGRF_SetNow(filename); ; ; Return Value: ; error code @@ -751,54 +757,111 @@ int IGRF_GetDateTime(int *year, int *month, int *day, ;+----------------------------------------------------------------------------- */ -int IGRF_SetNow(void) +int IGRF_SetNow(char *filename) +{ + /* current time */ + int err = 0; + int dyno; + time_t now; + struct tm *tm_now; + + /* load coefficients if not already loaded */ + if (igrf_date.year < 0) + err = IGRF_loadcoeffs(filename); + + if (err) return (err); + + now = time(NULL); + tm_now = gmtime(&now); /* right now in UT */ + + igrf_date.year = (*tm_now).tm_year + 1900; + igrf_date.month = (*tm_now).tm_mon + 1; + igrf_date.day = (*tm_now).tm_mday; + igrf_date.hour = (*tm_now).tm_hour; + igrf_date.minute = (*tm_now).tm_min; + igrf_date.second = (*tm_now).tm_sec; + igrf_date.dayno = (*tm_now).tm_yday + 1; + dyno = dayno(igrf_date.year,0,0,&(igrf_date.daysinyear)); + + #if DEBUG > 0 + printf("IGRF_SetNow\n"); + printf("%03d: %04d%02d%02d %02d%02d:%02d\n", + igrf_date.dayno, igrf_date.year, igrf_date.month, igrf_date.day, + igrf_date.hour, igrf_date.minute, igrf_date.second); + #endif + + fprintf(stderr, "\nIGRF: No date/time specified, using current time: "); + fprintf(stderr, "%04d%02d%02d %02d%02d:%02d\n\n", + igrf_date.year, igrf_date.month, igrf_date.day, + igrf_date.hour, igrf_date.minute, igrf_date.second); + + err = IGRF_interpolate_coefs(); + + return (err); +} + +/*----------------------------------------------------------------------------- +; +; NAME: +; IGRF_Tilt +; +; PURPOSE: +; Function to return dipole tilt angle for the given UT time. +; +; CALLING SEQUENCE: +; tilt = IGRF_Tilt(year,month,day, hour,minute,second); +; +; Return Value: +; dipole tilt angle in degrees +; +;+----------------------------------------------------------------------------- +*/ + +double IGRF_Tilt(int yr, int mo, int dy, int hr, int mt, int sc, char *filename) { - /* current time */ - char *fname; - int err,dyno; - double fyear; - time_t now; - struct tm *tm_now; - - /* load coefficients if not already loaded */ - if (igrf_date.year != (*tm_now).tm_year + 1900 || igrf_date.month != (*tm_now).tm_mon + 1 || igrf_date.day != (*tm_now).tm_mday) { - fname = getenv("IGRF_12_COEFFS"); - if (fname==NULL) { - err = IGRF_loadcoeffs(IGRF_FILE); - } else { - err = IGRF_loadcoeffs(fname); - } - } - - if (err) return (err); - - now = time(NULL); - tm_now = gmtime(&now); /* right now in UT */ - - igrf_date.year = (*tm_now).tm_year + 1900; - igrf_date.month = (*tm_now).tm_mon + 1; - igrf_date.day = (*tm_now).tm_mday; - igrf_date.hour = (*tm_now).tm_hour; - igrf_date.minute = (*tm_now).tm_min; - igrf_date.second = (*tm_now).tm_sec; - igrf_date.dayno = (*tm_now).tm_yday + 1; - dyno = dayno(igrf_date.year,0,0,&(igrf_date.daysinyear)); - - #if DEBUG > 0 - printf("IGRF_SetNow\n"); - printf("%03d: %04d%02d%02d %02d%02d:%02d\n", - igrf_date.dayno, igrf_date.year, igrf_date.month, igrf_date.day, - igrf_date.hour, igrf_date.minute, igrf_date.second); - #endif - - fprintf(stderr, "\nIGRF: No date/time specified, using current time: "); - fprintf(stderr, "%04d%02d%02d %02d%02d:%02d\n\n", - igrf_date.year, igrf_date.month, igrf_date.day, - igrf_date.hour, igrf_date.minute, igrf_date.second); - - err = IGRF_interpolate_coefs(); - - return (err); + double sps,s1,s2,s3,q; + double d1,d2,d3; + double dd,jd,dec,sras; + double dyn; + int diy; + double gst,cgst,sgst; + double fday,dj,d__1; + double rad = 57.295779513; + double dtwopi = 360.; + + IGRF_SetDateTime(yr,mo,dy,hr,mt,sc, filename); + + dd = AstAlg_dday(dy,hr,mt,sc); + jd = AstAlg_jde(yr,mo,dd); + dec = AstAlg_solar_declination(jd)*DTOR; + sras = AstAlg_solar_right_ascension(jd)*DTOR; + + s1 = cos(sras) * cos(dec); + s2 = sin(sras) * cos(dec); + s3 = sin(dec); + + dyn = dayno(yr,mo,dy, &diy); + + /* need Greenwich Mean Sidereal Time */ + /* SGS: seems like this should be somewhere in astalg.c, but can't find it */ + fday = ((double)hr*3600. + mt*60.+sc)/86400.; + dj = ((double)yr - 1900.)*365 + ((double)yr - 1901)/4. + dyn - .5 + fday; + d__1 = dj*0.9856473354 + 279.690983 + fday*360. + 180.; + /* SGS: double modulus */ + q = d__1/dtwopi; + q = (q >= 0) ? floor(q) : -floor(-q); + gst = (d__1 - dtwopi*q)/rad; + + sgst = sin(gst); + cgst = cos(gst); + + d1 = geopack.stcl * cgst - geopack.stsl * sgst; + d2 = geopack.stcl * sgst + geopack.stsl * cgst; + d3 = geopack.ct0; + + sps = d1*s1 + d2*s2 + d3*s3; + + return (asin(sps)/DTOR); } /*----------------------------------------------------------------------------- @@ -825,11 +888,11 @@ fprintf(stderr, "* calling IGRF functions you must set the date and time to the integer *\n" "* using the function: *\n" "* *\n" -"* IGRF_SetDateTime(year,month,day,hour,minute,second); *\n" +"* IGRF_SetDateTime(year,month,day,hour,minute,second,filename); *\n" "* *\n" "* or to the current computer time in UT using the function: *\n" "* *\n" -"* IGRF_SetNow(); *\n" +"* IGRF_SetNow(filename); *\n" "* *\n" "* subsequent calls to IGRF functions will use the last date and time *\n" "* that was set, so update to the actual date and time that is desired. *\n" @@ -863,22 +926,21 @@ fprintf(stderr, ; ;+----------------------------------------------------------------------------- */ -//int sph2car(double r,double theta,double phi, double *x,double *y,double *z) { int sph2car(const double rtp[], double xyz[]) { - double sq; + double sq; - sq = rtp[0]*sin(rtp[1]); - xyz[0] = sq*cos(rtp[2]); - xyz[1] = sq*sin(rtp[2]); - xyz[2] = rtp[0]*cos(rtp[1]); + sq = rtp[0]*sin(rtp[1]); + xyz[0] = sq*cos(rtp[2]); + xyz[1] = sq*sin(rtp[2]); + xyz[2] = rtp[0]*cos(rtp[1]); /* - sq = r*sin(theta); - *x = sq*cos(phi); - *y = sq*sin(phi); - *z = r *cos(theta); + sq = r*sin(theta); + *x = sq*cos(phi); + *y = sq*sin(phi); + *z = r *cos(theta); */ - return (0); + return (0); } /*----------------------------------------------------------------------------- @@ -910,39 +972,38 @@ int sph2car(const double rtp[], double xyz[]) { ;+----------------------------------------------------------------------------- */ -//int car2sph(double x,double y,double z, double *r,double *theta,double *phi) { int car2sph(const double xyz[], double rtp[]) { - double sq; - - sq = xyz[0]*xyz[0] + xyz[1]*xyz[1]; - rtp[0] = sqrt(sq + xyz[2]*xyz[2]); - - if (sq == 0.) { - rtp[2] = 0.; - rtp[1] = (xyz[2] < 0) ? M_PI : 0.; - } else { - sq = sqrt(sq); - rtp[2] = atan2(xyz[1],xyz[0]); - rtp[1] = atan2(sq,xyz[2]); - if (rtp[2] < 0) rtp[2] += 2*M_PI; - } + double sq; + + sq = xyz[0]*xyz[0] + xyz[1]*xyz[1]; + rtp[0] = sqrt(sq + xyz[2]*xyz[2]); + + if (sq == 0.) { + rtp[2] = 0.; + rtp[1] = (xyz[2] < 0) ? M_PI : 0.; + } else { + sq = sqrt(sq); + rtp[2] = atan2(xyz[1],xyz[0]); + rtp[1] = atan2(sq,xyz[2]); + if (rtp[2] < 0) rtp[2] += 2*M_PI; + } /* - sq = x*x + y*y; - *r = sqrt(sq + z*z); - - if (sq == 0.) { - *phi = 0.; - *theta = (z < 0) ? M_PI : 0.; - } else { - sq = sqrt(sq); - *phi = atan2(y,x); - *theta = atan2(sq,z); - if (*phi < 0) *phi += 2*M_PI; - } + sq = x*x + y*y; + *r = sqrt(sq + z*z); + + if (sq == 0.) { + *phi = 0.; + *theta = (z < 0) ? M_PI : 0.; + } else { + sq = sqrt(sq); + *phi = atan2(y,x); + *theta = atan2(sq,z); + if (*phi < 0) *phi += 2*M_PI; + } */ - return (0); - } + return (0); + } /*----------------------------------------------------------------------------- ; @@ -973,33 +1034,31 @@ int car2sph(const double xyz[], double rtp[]) { ;+----------------------------------------------------------------------------- */ -//int bspcar(double theta,double phi, double br,double btheta,double bphi, -// double *bx, double *by, double *bz) { int bspcar(double theta,double phi, const double brtp[], double bxyz[]) { - double st,ct,sp,cp,be; + double st,ct,sp,cp,be; - st = sin(theta); - ct = cos(theta); - sp = sin(phi); - cp = cos(phi); - be = brtp[0]*st + brtp[1]*ct; + st = sin(theta); + ct = cos(theta); + sp = sin(phi); + cp = cos(phi); + be = brtp[0]*st + brtp[1]*ct; - bxyz[0] = be*cp - brtp[2]*sp; - bxyz[1] = be*sp + brtp[2]*cp; - bxyz[2] = brtp[0]*ct - brtp[1]*st; + bxyz[0] = be*cp - brtp[2]*sp; + bxyz[1] = be*sp + brtp[2]*cp; + bxyz[2] = brtp[0]*ct - brtp[1]*st; /* - st = sin(theta); - ct = cos(theta); - sp = sin(phi); - cp = cos(phi); - be = br*st + btheta*ct; - - *bx = be*cp - bphi*sp; - *by = be*sp + bphi*cp; - *bz = br*ct - btheta*st; + st = sin(theta); + ct = cos(theta); + sp = sin(phi); + cp = cos(phi); + be = br*st + btheta*ct; + + *bx = be*cp - bphi*sp; + *by = be*sp + bphi*cp; + *bz = br*ct - btheta*st; */ - return (0); + return (0); } /*----------------------------------------------------------------------------- @@ -1031,82 +1090,78 @@ int bspcar(double theta,double phi, const double brtp[], double bxyz[]) { ;+----------------------------------------------------------------------------- */ -//int bcarsp(double x,double y,double z, double bx,double by,double bz, -// double *br,double *btheta,double *bphi) { int bcarsp(const double xyz[], const double bxyz[], double brtp[]) { - double r,rho,rho2,cp,sp,ct,st; - - rho2 = xyz[0]*xyz[0] + xyz[1]*xyz[1]; - r = sqrt(rho2 + xyz[2]*xyz[2]); - rho = sqrt(rho2); - - if (rho == 0.) { - cp = 1.; - sp = 0.; - } else { - cp = xyz[0]/rho; - sp = xyz[1]/rho; - } - - ct = xyz[2]/r; - st = rho/r; - - brtp[0] = (xyz[0]*bxyz[0] + xyz[1]*bxyz[1] + xyz[2]*bxyz[2])/r; - brtp[1] = (bxyz[0]*cp + bxyz[1]*sp)*ct - bxyz[1]*st; - brtp[2] = bxyz[1]*cp - bxyz[0]*sp; + double r,rho,rho2,cp,sp,ct,st; + + rho2 = xyz[0]*xyz[0] + xyz[1]*xyz[1]; + r = sqrt(rho2 + xyz[2]*xyz[2]); + rho = sqrt(rho2); + + if (rho == 0.) { + cp = 1.; + sp = 0.; + } else { + cp = xyz[0]/rho; + sp = xyz[1]/rho; + } + + ct = xyz[2]/r; + st = rho/r; + + brtp[0] = (xyz[0]*bxyz[0] + xyz[1]*bxyz[1] + xyz[2]*bxyz[2])/r; + brtp[1] = (bxyz[0]*cp + bxyz[1]*sp)*ct - bxyz[1]*st; + brtp[2] = bxyz[1]*cp - bxyz[0]*sp; /* - rho2 = x*x+y*y; - r = sqrt(rho2 + z*z); - rho = sqrt(rho2); - - if (rho == 0.) { - cp = 1.; - sp = 0.; - } else { - cp = x/rho; - sp = y/rho; - } - - ct = z/r; - st = rho/r; - - *br = (x*bx + y*by + z*bz)/r; - *btheta = (bx*cp + by*sp)*ct - bz*st; - *bphi = by*cp - bx*sp; + rho2 = x*x+y*y; + r = sqrt(rho2 + z*z); + rho = sqrt(rho2); + + if (rho == 0.) { + cp = 1.; + sp = 0.; + } else { + cp = x/rho; + sp = y/rho; + } + + ct = z/r; + st = rho/r; + + *br = (x*bx + y*by + z*bz)/r; + *btheta = (bx*cp + by*sp)*ct - bz*st; + *bphi = by*cp - bx*sp; */ - return (0); + return (0); } -//int geo2mag(double xg,double yg,double zg, double *xm,double *ym,double *zm) { int geo2mag(const double xyzg[], double xyzm[]) { - xyzm[0] = xyzg[0]*geopack.ctcl + xyzg[1]*geopack.ctsl - xyzg[2]*geopack.st0; - xyzm[1] = xyzg[1]*geopack.cl0 - xyzg[0]*geopack.sl0; - xyzm[2] = xyzg[0]*geopack.stcl + xyzg[1]*geopack.stsl + xyzg[2]*geopack.ct0; + xyzm[0] = xyzg[0]*geopack.ctcl + xyzg[1]*geopack.ctsl - xyzg[2]*geopack.st0; + xyzm[1] = xyzg[1]*geopack.cl0 - xyzg[0]*geopack.sl0; + xyzm[2] = xyzg[0]*geopack.stcl + xyzg[1]*geopack.stsl + xyzg[2]*geopack.ct0; /* - *xm = xg*geopack.ctcl + yg*geopack.ctsl - zg*geopack.st0; - *ym = yg*geopack.cl0 - xg*geopack.sl0; - *zm = xg*geopack.stcl + yg*geopack.stsl + zg*geopack.ct0; + *xm = xg*geopack.ctcl + yg*geopack.ctsl - zg*geopack.st0; + *ym = yg*geopack.cl0 - xg*geopack.sl0; + *zm = xg*geopack.stcl + yg*geopack.stsl + zg*geopack.ct0; */ - return (0); + return (0); } -//int mag2geo(double xm,double ym,double zm, double *xg,double *yg,double *zg) { int mag2geo(const double xyzm[], double xyzg[]) { - xyzg[0] = xyzm[0]*geopack.ctcl - xyzm[1]*geopack.sl0 + xyzm[2]*geopack.stcl; - xyzg[1] = xyzm[0]*geopack.ctsl + xyzm[1]*geopack.cl0 + xyzm[2]*geopack.stsl; - xyzg[2] = xyzm[2]*geopack.ct0 - xyzm[0]*geopack.st0; + xyzg[0] = xyzm[0]*geopack.ctcl - xyzm[1]*geopack.sl0 + xyzm[2]*geopack.stcl; + xyzg[1] = xyzm[0]*geopack.ctsl + xyzm[1]*geopack.cl0 + xyzm[2]*geopack.stsl; + xyzg[2] = xyzm[2]*geopack.ct0 - xyzm[0]*geopack.st0; /* - *xg = xm*geopack.ctcl - ym*geopack.sl0 + zm*geopack.stcl; - *yg = xm*geopack.ctsl + ym*geopack.cl0 + zm*geopack.stsl; - *zg = zm*geopack.ct0 - xm*geopack.st0; + *xg = xm*geopack.ctcl - ym*geopack.sl0 + zm*geopack.stcl; + *yg = xm*geopack.ctsl + ym*geopack.cl0 + zm*geopack.stsl; + *zg = zm*geopack.ct0 - xm*geopack.st0; */ - return (0); + return (0); } /*----------------------------------------------------------------------------- @@ -1138,30 +1193,30 @@ int mag2geo(const double xyzm[], double xyzg[]) { int geod2geoc(double lat, double lon, double alt, double rtp[]) { - double a,b,f,a2,b2,st,ct,one,two,three,rho,cd,sd; - double r,theta,phi; - - a = 6378.1370; /* semi-major axis */ - f = 1./298.257223563; /* flattening */ - b = a*(1. -f); /* semi-minor axis */ - a2 = a*a; - b2 = b*b; - theta = (90. -lat)*DTOR; /* colatitude in radians */ - st = sin(theta); - ct = cos(theta); - one = a2*st*st; - two = b2*ct*ct; - three = one + two; - rho = sqrt(three); /* [km] */ - r = sqrt(alt*(alt+2*rho) + (a2*one + b2*two)/three); /* [km] */ - cd = (alt+rho)/r; - sd = (a2-b2)/rho *ct*st/r; - - rtp[0] = r/RE; /* units of RE */ - rtp[1] = acos(ct*cd - st*sd); - rtp[2] = lon*DTOR; - - return (0); + double a,b,f,a2,b2,st,ct,one,two,three,rho,cd,sd; + double r,theta; + + a = 6378.1370; /* semi-major axis */ + f = 1./298.257223563; /* flattening */ + b = a*(1. -f); /* semi-minor axis */ + a2 = a*a; + b2 = b*b; + theta = (90. -lat)*DTOR; /* colatitude in radians */ + st = sin(theta); + ct = cos(theta); + one = a2*st*st; + two = b2*ct*ct; + three = one + two; + rho = sqrt(three); /* [km] */ + r = sqrt(alt*(alt+2*rho) + (a2*one + b2*two)/three); /* [km] */ + cd = (alt+rho)/r; + sd = (a2-b2)/rho *ct*st/r; + + rtp[0] = r/RE; /* units of RE */ + rtp[1] = acos(ct*cd - st*sd); + rtp[2] = lon*DTOR; + + return (0); } /*----------------------------------------------------------------------------- @@ -1192,11 +1247,11 @@ int geod2geoc(double lat, double lon, double alt, double rtp[]) { int plh2xyz(double lat, double lon, double alt, double rtp[]) { - double a,b,f,ee,st,ct,sp,cp,N,Nac,x,y,z,r,t; + double a,b,f,ee,st,ct,sp,cp,N,Nac,x,y,z,r,t; - a = 6378.1370; /* semi-major axis */ - f = 1./298.257223563; /* flattening */ - b = a*(1. -f); /* semi-minor axis */ + a = 6378.1370; /* semi-major axis */ + f = 1./298.257223563; /* flattening */ + b = a*(1. -f); /* semi-minor axis */ ee = (2. - f) * f; st = sin(lat*DTOR); @@ -1214,7 +1269,7 @@ int plh2xyz(double lat, double lon, double alt, double rtp[]) r = sqrt(Nac*Nac + z*z); t = acos(z/r); - rtp[0] = r/RE; /* units of RE */ + rtp[0] = r/RE; /* units of RE */ rtp[1] = t; rtp[2] = lon*DTOR; @@ -1248,8 +1303,8 @@ int plh2xyz(double lat, double lon, double alt, double rtp[]) int geoc2geod(double lat, double lon, double r, double llh[]) { - double a,f,b,ee,e4,aa, theta,phi, st,ct,sp,cp, x,y,z; - double k0i,pp,zeta,rho,s,rho3,t,u,v,w,kappa; + double a,f,b,ee,e4,aa, theta,phi, st,ct,sp,cp, x,y,z; + double k0i,pp,zeta,rho,s,rho3,t,u,v,w,kappa; a = 6378.1370; /* semi-major axis */ f = 1./298.257223563; /* flattening */ @@ -1283,8 +1338,8 @@ int geoc2geod(double lat, double lon, double r, double llh[]) kappa = 1. + ee*(sqrt(u+v+w*w) + w)/(u + v); llh[0] = atan2(z*kappa,sqrt(pp))/DTOR; - llh[1] = lon; - llh[2] = sqrt(pp + z*z*kappa*kappa)/ee * (1./kappa - k0i); + llh[1] = lon; + llh[2] = sqrt(pp + z*z*kappa*kappa)/ee * (1./kappa - k0i); return (0); } @@ -1313,18 +1368,18 @@ int geoc2geod(double lat, double lon, double r, double llh[]) */ int AACGM_v2_Newval(double xyz[], int idir, double ds, double k[]) { - int j; - double rtp[3], brtp[3], bxyz[3]; - double bmag; + int j; + double rtp[3], brtp[3], bxyz[3]; + double bmag; - car2sph(xyz, rtp); /* convert to spherical coords */ - IGRF_compute(rtp, brtp); /* compute the IGRF field here */ - bspcar(rtp[1],rtp[2], brtp, bxyz); /* convert field to Cartesian */ + car2sph(xyz, rtp); /* convert to spherical coords */ + IGRF_compute(rtp, brtp); /* compute the IGRF field here */ + bspcar(rtp[1],rtp[2], brtp, bxyz); /* convert field to Cartesian */ - bmag = sqrt(bxyz[0]*bxyz[0] + bxyz[1]*bxyz[1] + bxyz[2]*bxyz[2]); - for (j=0; j<3; j++) k[j] = ds*idir*bxyz[j]/bmag; + bmag = sqrt(bxyz[0]*bxyz[0] + bxyz[1]*bxyz[1] + bxyz[2]*bxyz[2]); + for (j=0; j<3; j++) k[j] = ds*idir*bxyz[j]/bmag; - return (0); + return (0); } /*----------------------------------------------------------------------------- @@ -1366,113 +1421,112 @@ int AACGM_v2_Newval(double xyz[], int idir, double ds, double k[]) { */ int AACGM_v2_RK45(double xyz[], int idir, double *ds, double eps, int code) { - char ch; - int k; - double bmag,tmp,rr,delt; - double k1[3],k2[3],k3[3],k4[3],k5[3],k6[3], w1[3],w2[3]; - double rtp[3], brtp[3], bxyz[3]; - double xyztmp[3]; + int k; + double bmag,rr,delt; + double k1[3],k2[3],k3[3],k4[3],k5[3],k6[3], w1[3],w2[3]; + double rtp[3], brtp[3], bxyz[3]; + double xyztmp[3]; -//function test_aacgm_rk45, x,y,z, idir, ds, eps, noadapt=noadapt, $ -// max_ds=max_ds, RRds=RRds +/*function test_aacgm_rk45, x,y,z, idir, ds, eps, noadapt=noadapt, $ + * max_ds=max_ds, RRds=RRds*/ /* - ; if noadapt is set then just do straight RK4 and ds is spatial step size - ; in kilometers - ; default is to do adapative step size where eps is error in km - ; set max_ds to the maximum step size (in RE) to prevent too large step + ; if noadapt is set then just do straight RK4 and ds is spatial step size + ; in kilometers + ; default is to do adapative step size where eps is error in km + ; set max_ds to the maximum step size (in RE) to prevent too large step */ - /* convert position to spherical coords */ - car2sph(xyz, rtp); - - /* compute IGRF field in spherical coords */ - IGRF_compute(rtp, brtp); - - /* convert field from spherical coords to Cartesian */ - bspcar(rtp[1],rtp[2], brtp, bxyz); - - /* magnitude of field to normalize vector */ - bmag = sqrt(bxyz[0]*bxyz[0] + bxyz[1]*bxyz[1] + bxyz[2]*bxyz[2]); - - if (code == 0) { /* no adaptive stepping */ - /**************\ - * RK4 Method * - \**************/ - for (k=0;k<3;k++) k1[k] = (*ds)*idir*bxyz[k]/bmag; - for (k=0;k<3;k++) xyztmp[k] = xyz[k] + .5*k1[k]; - AACGM_v2_Newval(xyztmp,idir,*ds, k2); - for (k=0;k<3;k++) xyztmp[k] = xyz[k] + .5*k2[k]; - AACGM_v2_Newval(xyztmp,idir,*ds, k3); - for (k=0;k<3;k++) xyztmp[k] = xyz[k] + k3[k]; - AACGM_v2_Newval(xyztmp,idir,*ds, k4); - - for (k=0; k<3; k++) - xyz[k] += (k1[k] + k2[k]+k2[k] + k3[k]+k3[k] + k4[k])/6.; - } else { - - /************************\ - * Adaptive RK45 method * - \************************/ - rr = eps+1; /* just to get into the loop */ - while (rr > eps) { - for (k=0;k<3;k++) k1[k] = (*ds)*idir*bxyz[k]/bmag; - for (k=0;k<3;k++) xyztmp[k] = xyz[k] + k1[k]/4.; - AACGM_v2_Newval(xyztmp,idir,*ds, k2); - for (k=0;k<3;k++) xyztmp[k] = xyz[k] + (3.*k1[k] + 9.*k2[k])/32.; - AACGM_v2_Newval(xyztmp,idir,*ds, k3); - for (k=0;k<3;k++) xyztmp[k] = xyz[k] + (1932.*k1[k] - 7200.*k2[k] + - 7296.*k3[k])/2197.; - AACGM_v2_Newval(xyztmp,idir,*ds, k4); - for (k=0;k<3;k++) - xyztmp[k] = xyz[k] + 439.*k1[k]/216. - 8.*k2[k] + - 3680.*k3[k]/513. - 845.*k4[k]/4104.; - AACGM_v2_Newval(xyztmp,idir,*ds, k5); - for (k=0;k<3;k++) - xyztmp[k] = xyz[k] - 8.*k1[k]/27. + 2.*k2[k] - 3544.*k3[k]/2565. + - 1859.*k4[k]/4104. - 11.*k5[k]/40.; - AACGM_v2_Newval(xyztmp,idir,*ds, k6); - - rr = 0.; - for (k=0;k<3;k++) { - w1[k] = xyz[k] + 25.*k1[k]/216. + 1408.*k3[k]/2565. + - 2197.*k4[k]/4104. - k5[k]/5.; - w2[k] = xyz[k] + 16.*k1[k]/135. + 6656.*k3[k]/12825. + - 28561.*k4[k]/56430. - 9.*k5[k]/50. + - 2.*k6[k]/55.; - rr += (w1[k]-w2[k])*(w1[k]-w2[k]); - } - rr = sqrt(rr)/(*ds); - - if (fabs(rr) > 1e-16) { - delt = 0.84 *pow(eps/rr,0.25); /* this formula sucks because I have - no it where it came from. - Obviously it involves factors in - the LTEs of the two methods, but - I cannot find them written down - anywhere. */ - //newds = ds * delt; - //ds = newds; - *ds *= delt; - - /* maximum stepsize is fixed to max_ds in units of Re */ - //if keyword_set(max_ds) then ds = min([max_ds,ds]) - /* maximum stepsize is r^2 * 1km, where r is in units of Re */ - //if keyword_set(RRds) then ds = min([50*r*r*r/RE, ds]) - *ds = MIN(50*rtp[0]*rtp[0]*rtp[0]/RE, *ds); - } /* otherwise leave the stepsize alone */ - } - - /* we use the RK4 solution */ - for (k=0;k<3;k++) xyz[k] = w1[k]; - /* - ; I would assume that using the higher order RK5 method is better, but - ; there is the suggestion that using the RK4 solution guarantees accuracy - ; while the RK5 does not. Apparently some texts are now suggesting using - ; the RK5 solution... - for (k=0;k<3;k++) xyz[k] = w2[k]; - */ - } - - return (0); + /* convert position to spherical coords */ + car2sph(xyz, rtp); + + /* compute IGRF field in spherical coords */ + IGRF_compute(rtp, brtp); + + /* convert field from spherical coords to Cartesian */ + bspcar(rtp[1],rtp[2], brtp, bxyz); + + /* magnitude of field to normalize vector */ + bmag = sqrt(bxyz[0]*bxyz[0] + bxyz[1]*bxyz[1] + bxyz[2]*bxyz[2]); + + if (code == 0) { /* no adaptive stepping */ + /**************\ + * RK4 Method * + \**************/ + for (k=0;k<3;k++) k1[k] = (*ds)*idir*bxyz[k]/bmag; + for (k=0;k<3;k++) xyztmp[k] = xyz[k] + .5*k1[k]; + AACGM_v2_Newval(xyztmp,idir,*ds, k2); + for (k=0;k<3;k++) xyztmp[k] = xyz[k] + .5*k2[k]; + AACGM_v2_Newval(xyztmp,idir,*ds, k3); + for (k=0;k<3;k++) xyztmp[k] = xyz[k] + k3[k]; + AACGM_v2_Newval(xyztmp,idir,*ds, k4); + + for (k=0; k<3; k++) + xyz[k] += (k1[k] + k2[k]+k2[k] + k3[k]+k3[k] + k4[k])/6.; + } else { + + /************************\ + * Adaptive RK45 method * + \************************/ + rr = eps+1; /* just to get into the loop */ + while (rr > eps) { + for (k=0;k<3;k++) k1[k] = (*ds)*idir*bxyz[k]/bmag; + for (k=0;k<3;k++) xyztmp[k] = xyz[k] + k1[k]/4.; + AACGM_v2_Newval(xyztmp,idir,*ds, k2); + for (k=0;k<3;k++) xyztmp[k] = xyz[k] + (3.*k1[k] + 9.*k2[k])/32.; + AACGM_v2_Newval(xyztmp,idir,*ds, k3); + for (k=0;k<3;k++) xyztmp[k] = xyz[k] + (1932.*k1[k] - 7200.*k2[k] + + 7296.*k3[k])/2197.; + AACGM_v2_Newval(xyztmp,idir,*ds, k4); + for (k=0;k<3;k++) + xyztmp[k] = xyz[k] + 439.*k1[k]/216. - 8.*k2[k] + + 3680.*k3[k]/513. - 845.*k4[k]/4104.; + AACGM_v2_Newval(xyztmp,idir,*ds, k5); + for (k=0;k<3;k++) + xyztmp[k] = xyz[k] - 8.*k1[k]/27. + 2.*k2[k] - 3544.*k3[k]/2565. + + 1859.*k4[k]/4104. - 11.*k5[k]/40.; + AACGM_v2_Newval(xyztmp,idir,*ds, k6); + + rr = 0.; + for (k=0;k<3;k++) { + w1[k] = xyz[k] + 25.*k1[k]/216. + 1408.*k3[k]/2565. + + 2197.*k4[k]/4104. - k5[k]/5.; + w2[k] = xyz[k] + 16.*k1[k]/135. + 6656.*k3[k]/12825. + + 28561.*k4[k]/56430. - 9.*k5[k]/50. + + 2.*k6[k]/55.; + rr += (w1[k]-w2[k])*(w1[k]-w2[k]); + } + rr = sqrt(rr)/(*ds); + + if (fabs(rr) > 1e-16) { + delt = 0.84 *pow(eps/rr,0.25); /* this formula sucks because I have + no it where it came from. + Obviously it involves factors in + the LTEs of the two methods, but + I cannot find them written down + anywhere. */ + /*newds = ds * delt; + //ds = newds;*/ + *ds *= delt; + + /* maximum stepsize is fixed to max_ds in units of Re */ + /*if keyword_set(max_ds) then ds = min([max_ds,ds])*/ + /* maximum stepsize is r^2 * 1km, where r is in units of Re */ + /*if keyword_set(RRds) then ds = min([50*r*r*r/RE, ds])*/ + *ds = MIN(50*rtp[0]*rtp[0]*rtp[0]/RE, *ds); + } /* otherwise leave the stepsize alone */ + } + + /* we use the RK4 solution */ + for (k=0;k<3;k++) xyz[k] = w1[k]; + /* + ; I would assume that using the higher order RK5 method is better, but + ; there is the suggestion that using the RK4 solution guarantees accuracy + ; while the RK5 does not. Apparently some texts are now suggesting using + ; the RK5 solution... + for (k=0;k<3;k++) xyz[k] = w2[k]; + */ + } + + return (0); } diff --git a/src/c_aacgm_v2/mlt_v2.c b/src/c_aacgm_v2/mlt_v2.c new file mode 100755 index 00000000..d7ee7abd --- /dev/null +++ b/src/c_aacgm_v2/mlt_v2.c @@ -0,0 +1,283 @@ + +#include +#include +#include +#include "aacgmlib_v2.h" +#include "rtime.h" +#include "astalg.h" + +/*----------------------------------------------------------------------------- + MLT functions for use with AACGM-v2 +; +; C user functions intended to compute the magnetic local time at a given +; time and location given by AACGM-v2 coordinates. +; +; Code based on legacy routines authored by Kile Baker, Simon Wing and Robin +; Barnes. +; +; Significant changes include: +; +; - discontinued use of common block used for interpolation between two values +; separated by 10 minutes. +; +; - MLT is based on the relative AACGM-v2 longitudes of the desired location to +; that of a reference location. The reference location is taken to be the +; subsolar point at 700 km altitude. The reference AACGM-v2 longitude is +; computed using AACGM-v2 coefficients, which act to interpolate through +; regions where AACGM coordinates are undefined, thereby providing a +; mechanism for defining the reference longitude in a consistent manner. +; Differences with values determined at 0 km altitude (where defined) are +; typically <1 minute and always <5 minutes (in MLT). + +; 20170601 SGS v1.2 MLTConvert_v2 now calls AACGM_v2_SetDateTime() if the +; AACGM-v2 date/time is not currently set OR if the +; date/time passed into one of the public functions differs +; from the AACGM-v2 date/time by more than 30 days. In each +; case the AACGM-v2 coefficients are loaded and interpolated +; which could impact other calls to AACGM_v2_Convert() if +; the date/time is not reset. + +; Changes + + Combined MLTAst and MLTAst1 + + Removed *mlson variable since it is only used for doing linear + interpolation between two periods separated by 10 minutes. + +; +; Public Functions: +; ----------------- +; +; mlt = MLTConvertYMDHMS_v2(yr,mo,dy,hr,mt,sc, mlon, igrf_filename); +; mlon = inv_MLTConvertYMDHMS_v2(yr,mo,dy,hr,mt,sc, mlt, igrf_filename); +; +; mlt = MLTConvertEpoch_v2(epoch, mlon, igrf_filename); +; mlon = inv_MLTConvertEpoch_v2(epoch, mlt, igrf_filename); +; +; mlt = MLTConvertYrsec_v2(yr,yrsec, mlon, igrf_filename); +; mlon = inv_MLTConvertYrsec_v2(yr,yrsec, mlt, igrf_filename); +; +; Private Functions: +; ------------------ +; +; mlt = MLTConvert_v2(yr,mo,dy,hr,mt,sc, mlon, root, igrf_filename); +; mlon = inv_MLTConvert_v2(yr,mo,dy,hr,mt,sc, mlt, root, igrf_filename); +; +; +*/ + +double mlon_ref = -1; + +struct { + int yr; + int mo; + int dy; + int hr; + int mt; + int sc; +} mlt_date = {-1,-1,-1,-1,-1,-1}; + +/* + * Accepts scalars but only recomputes mlon_ref if time has changed from last + * call so computation is fast(er) for multiple calls at same date/time + * + * No options here. Use IDL version for development + * + */ +double MLTConvert_v2(int yr, int mo, int dy, int hr, int mt ,int sc, + double mlon, char *root, char *igrf_filename) +{ + int err; + int ayr,amo,ady,ahr,amt,asc,adyn; + double dd,jd,eqt,dec,ut,at; + double slon,mlat,r; + double hgt,aacgm_mlt; + double ajd; + + err = 0; + AACGM_v2_GetDateTime(&ayr, &amo, &ady, &ahr, &amt, &asc, &adyn); + if (ayr < 0) { + /* AACGM date/time not set so set it to the date/time passed in */ + err = AACGM_v2_SetDateTime(yr,mo,dy,hr,mt,sc, root); + if (err != 0) return (err); + } else { + /* If date/time passed into function differs from AACGM data/time by more + * than 30 days, recompute the AACGM-v2 coefficients */ + ajd = TimeYMDHMSToJulian(ayr,amo,ady,ahr,amt,asc); + jd = TimeYMDHMSToJulian(yr,mo,dy,hr,mt,sc); + if (fabs(jd-ajd) > 30.0) { + err = AACGM_v2_SetDateTime(yr,mo,dy,hr,mt,sc, root); + } + if (err != 0) return (err); + } + +/* check for bad input, which can come from undefined region, and return NAN */ + if (!isfinite(mlon)) { + return (NAN); + } + + hgt = 700.; /* AACGM-v2 coefficients are defined everywhere above this + * altitude. */ + + if (mlt_date.yr != yr || mlt_date.mo != mo || mlt_date.dy != dy || + mlt_date.hr != hr || mlt_date.mt != mt || mlt_date.sc != sc) { + /* date/time has changed so recompute */ + mlt_date.yr = yr; + mlt_date.mo = mo; + mlt_date.dy = dy; + mlt_date.hr = hr; + mlt_date.mt = mt; + mlt_date.sc = sc; + + /* compute corrected time */ + dd = AstAlg_dday(dy,hr,mt,sc); + jd = AstAlg_jde(yr,mo,dd); + eqt = AstAlg_equation_of_time(jd); + dec = AstAlg_solar_declination(jd); + ut = hr*3600. + mt*60. + sc; + at = ut + eqt*60.; + + /* comparision with IDL version is exact + printf("\n\n"); + printf("dd = %20.12lf\n", dd); + printf("jd = %20.12lf\n", jd); + printf("eqt = %20.12lf\n", eqt); + printf("dec = %20.12lf\n", dec); + printf("\n\n"); + */ + + /* compute reference longitude */ + slon = (43200.-at)*15./3600.; /* subsolar point */ + + /* compute AACGM-v2 coordinates of reference point */ + err = AACGM_v2_Convert(dec, slon, hgt, &mlat, &mlon_ref, &r, G2A, + igrf_filename); + + /* check for error: this should NOT happen... */ + if (err != 0) { + err = -99; + return (NAN); + } + } +/*printf("** %lf\n", mlon_ref);*/ + + aacgm_mlt = 12. + (mlon - mlon_ref)/15.; /* MLT based on subsolar point */ + + /* comparision with IDL version is exact */ + + while (aacgm_mlt > 24.) aacgm_mlt -= 24.; + while (aacgm_mlt < 0.) aacgm_mlt += 24.; + + return (aacgm_mlt); +} + +/* inverse function: MLT to AACGM-v2 magnetic longitude */ +double inv_MLTConvert_v2(int yr, int mo, int dy, int hr, int mt ,int sc, + double mlt, char *igrf_filename) +{ + int err; + double dd,jd,eqt,dec,ut,at; + double slon,mlat,r; + double hgt,aacgm_mlon; + +/* check for bad input, which should not happen for MLT, and return NAN */ + if (!isfinite(mlt)) { + return (NAN); + } + + hgt = 700.; /* AACGM-v2 coefficients are defined everywhere above this + * altitude. */ + + if (mlt_date.yr != yr || mlt_date.mo != mo || mlt_date.dy != dy || + mlt_date.hr != hr || mlt_date.mt != mt || mlt_date.sc != sc) { + /* date/time has changed so recompute */ + mlt_date.yr = yr; + mlt_date.mo = mo; + mlt_date.dy = dy; + mlt_date.hr = hr; + mlt_date.mt = mt; + mlt_date.sc = sc; + + /* compute corrected time */ + dd = AstAlg_dday(dy,hr,mt,sc); + jd = AstAlg_jde(yr,mo,dd); + eqt = AstAlg_equation_of_time(jd); + dec = AstAlg_solar_declination(jd); + ut = hr*3600. + mt*60. + sc; + at = ut + eqt*60.; + + /* compute reference longitude */ + slon = (43200.-at)*15./3600.; /* subsolar point */ + + /* compute AACGM-v2 coordinates of reference point */ + err = AACGM_v2_Convert(dec, slon, hgt, &mlat, &mlon_ref, &r, G2A, + igrf_filename); + + /* check for error: this should NOT happen... */ + if (err != 0) { + err = -99; + return (NAN); + } + } + + aacgm_mlon = (mlt - 12.)*15. + mlon_ref; /* mlon based on subsolar point */ + + while (aacgm_mlon > 180.) aacgm_mlon -= 360.; + while (aacgm_mlon < -180.) aacgm_mlon += 360.; + + return (aacgm_mlon); +} + +double MLTConvertYMDHMS_v2(int yr, int mo, int dy, int hr, int mt, int sc, + double mlon, char *root, char *igrf_filename) +{ + return (MLTConvert_v2(yr,mo,dy,hr,mt,sc,mlon,root,igrf_filename)); +} + +double inv_MLTConvertYMDHMS_v2(int yr, int mo, int dy, int hr, int mt, int sc, + double mlt, char *igrf_filename) +{ + return (inv_MLTConvert_v2(yr,mo,dy,hr,mt,sc,mlt,igrf_filename)); +} + +double MLTConvertYrsec_v2(int yr,int yr_sec, double mlon, char *root, + char *igrf_filename) +{ + int mo,dy,hr,mt,sc; + + TimeYrsecToYMDHMS(yr_sec,yr,&mo,&dy,&hr,&mt,&sc); + + return (MLTConvert_v2(yr,mo,dy,hr,mt,sc,mlon,root,igrf_filename)); +} + +double inv_MLTConvertYrsec_v2(int yr, int yr_sec, double mlt, + char *igrf_filename) +{ + int mo,dy,hr,mt,sc; + + TimeYrsecToYMDHMS(yr_sec,yr,&mo,&dy,&hr,&mt,&sc); + + return (inv_MLTConvert_v2(yr,mo,dy,hr,mt,sc,mlt,igrf_filename)); +} + +double MLTConvertEpoch_v2(double epoch, double mlon, char *root, + char *igrf_filename) +{ + int yr,mo,dy,hr,mt; + double sc; + + TimeEpochToYMDHMS(epoch,&yr,&mo,&dy,&hr,&mt,&sc); + + return (MLTConvert_v2(yr,mo,dy,hr,mt,(int)sc, mlon, root, igrf_filename)); +} + +double inv_MLTConvertEpoch_v2(double epoch, double mlt, char *igrf_filename) +{ + int yr,mo,dy,hr,mt; + double sc; + + TimeEpochToYMDHMS(epoch,&yr,&mo,&dy,&hr,&mt,&sc); + + return (inv_MLTConvert_v2(yr,mo,dy,hr,mt,(int)sc, mlt, igrf_filename)); +} + diff --git a/src/c_aacgm_v2/rtime.c b/src/c_aacgm_v2/rtime.c new file mode 100755 index 00000000..9745023a --- /dev/null +++ b/src/c_aacgm_v2/rtime.c @@ -0,0 +1,210 @@ +/* rtime.c + ======= + Author: R.J.Barnes +*/ + +/* + (c) 2010 JHU/APL & Others - Please Consult LICENSE.superdarn-rst.3.2-beta-4-g32f7302.txt for more information. + +*/ + +#include +#include +#include +#include +#include +#include +#include "rtime.h" + +#define DAY_SEC 86400 + +int TimeYMDHMSToYrsec(int yr,int mo,int dy,int hr,int mn,int sc) { + + time_t clock; + struct tm tm; + char *tz; + + memset(&tm,0,sizeof(struct tm)); + tm.tm_year=yr-1900; + tm.tm_mon=0; + tm.tm_mday=1; + tm.tm_hour=0; + tm.tm_min=0; + tm.tm_sec=0; + + tz = getenv("TZ"); + setenv("TZ", "", 1); + tzset(); + clock=mktime(&tm); + + memset(&tm,0,sizeof(struct tm)); + tm.tm_year=yr-1900; + tm.tm_mon=mo-1; + tm.tm_mday=dy; + tm.tm_hour=hr; + tm.tm_min=mn; + tm.tm_sec=sc; + clock=mktime(&tm)-clock; + + + if (tz) setenv("TZ", tz, 1); + else unsetenv("TZ"); + tzset(); + + return (int) clock; +} + +void TimeYrsecToYMDHMS(int yrsec,int yr,int *mo,int *dy,int *hr,int *mn, + int *sc) { + + + time_t clock; + struct tm tmyr; + struct tm *tm; + char *tz; + + memset(&tmyr,0,sizeof(struct tm)); + tmyr.tm_year=yr-1900; + tmyr.tm_mon=0; + tmyr.tm_mday=1; + tmyr.tm_hour=0; + tmyr.tm_min=0; + tmyr.tm_sec=0; + + tz = getenv("TZ"); + setenv("TZ", "", 1); + tzset(); + + clock=mktime(&tmyr); + if (tz) setenv("TZ", tz, 1); + else unsetenv("TZ"); + tzset(); + + clock=clock+yrsec; + tm=gmtime(&clock); + + *mo=tm->tm_mon+1; + *dy=tm->tm_mday; + *hr=tm->tm_hour; + *mn=tm->tm_min; + *sc=tm->tm_sec; +} + + +double TimeYMDHMSToEpoch(int yr,int mo,int dy,int hr,int mn,double sc) { + + time_t clock; + struct tm tm; + char *tz; + + memset(&tm,0,sizeof(struct tm)); + tm.tm_year=yr-1900; + tm.tm_mon=mo-1; + tm.tm_mday=dy; + tm.tm_hour=hr; + tm.tm_min=mn; + tm.tm_sec=floor(sc); + + tz = getenv("TZ"); + setenv("TZ", "", 1); + tzset(); + clock=mktime(&tm); + if (tz) setenv("TZ", tz, 1); + else unsetenv("TZ"); + tzset(); + + return clock+(sc-floor(sc)); +} + +void TimeEpochToYMDHMS(double tme,int *yr,int *mo,int *dy,int *hr,int *mn, + double *sc) { + time_t clock; + struct tm *tm; + + clock=floor(tme); + tm=gmtime(&clock); + + *yr=tm->tm_year+1900; + *mo=tm->tm_mon+1; + *dy=tm->tm_mday; + *hr=tm->tm_hour; + *mn=tm->tm_min; + *sc=tm->tm_sec+(tme-floor(tme)); +} + +double TimeYMDHMSToJulian(int yr,int mo,int dy,int hr,int mt,double sc) { + + int A,B,i; + double jdoy; + double dfrac; + yr=yr-1; + i=yr/100; + A=i; + i=A/4; + B=2-A+i; + i=365.25*yr; + i+=30.6001*14; + jdoy=i+1720994.5+B; + + + dfrac=1+TimeYMDHMSToYrsec(yr+1,mo,dy,hr,mt,sc)/DAY_SEC; + + return jdoy+dfrac; + +} + + +int TimeJulianToYMDHMS(double jd,int *yr,int *mo, + int *dy,int *hr,int *mt,double *sc) { + + int Z,month; + int hour,minute; + + double A,B,C,D,E,F,alpha,day,year,factor,second; + + factor=0.5/DAY_SEC/1000; + F=(jd+0.5)-floor(jd+0.5); + if ((F+factor)>=1.0) { + jd=jd+factor; + F=0.0; + } + + Z=floor(jd+0.5); + + if (Z<2299161) A=Z; + else { + alpha=floor((Z-1867216.25)/36524.25); + A=Z+1+alpha-floor(alpha/4); + } + + B=A+1524; + C=floor((B-122.1)/365.25); + D=floor(365.25*C); + E=floor((B-D)/30.6001); + day=B-D-floor(30.6001*E)+F; + + if (E<13.5) month=floor(E-0.5); + else month=floor(E-12.5); + if (month>2.5) year=C-4716; + else year=C-4715; + + + + *yr=(int) year; + *mo=month; + *dy=(int) floor(day); + + /* okay now use the residual of the day to work out the time */ + + A=(day-floor(day))*DAY_SEC; + + hour=(int) (A/3600.0); + minute=(int) ((A-hour*3600)/60); + second=A-hour*3600-minute*60; + + *hr=hour; + *mt=minute; + *sc=second; + return 0; +} + From 13e165068970f4f50b173a2e8fc44344eeba870e Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 8 Mar 2018 20:21:01 -0600 Subject: [PATCH 003/101] Updated header files Updated header files for AACGM v 2.4 --- src/c_aacgm_v2/aacgmlib_v2.h | 58 +++++++++++++--------- src/c_aacgm_v2/astalg.h | 95 ++++++++++++++++++++++++++++++++++++ src/c_aacgm_v2/igrflib.h | 24 ++++++--- src/c_aacgm_v2/mlt_v2.h | 22 +++++++++ src/c_aacgm_v2/rtime.h | 26 ++++++++++ 5 files changed, 197 insertions(+), 28 deletions(-) create mode 100755 src/c_aacgm_v2/astalg.h create mode 100755 src/c_aacgm_v2/mlt_v2.h create mode 100755 src/c_aacgm_v2/rtime.h diff --git a/src/c_aacgm_v2/aacgmlib_v2.h b/src/c_aacgm_v2/aacgmlib_v2.h index 989169c5..6a51f466 100644 --- a/src/c_aacgm_v2/aacgmlib_v2.h +++ b/src/c_aacgm_v2/aacgmlib_v2.h @@ -6,21 +6,33 @@ * defines and structures *****************************************************************************/ -#define RE 6371.2 /* Earth Radius */ -#define MAXALT 2000 /* maximum altitude in km */ -#define NCOORD 3 /* xyz */ -#define POLYORD 5 /* quartic polynomial fit in altitude */ -#define NFLAG 2 /* 0: geo to AACGM, 1: AACGM to geo */ -#define SHORDER 10 /* order of Spherical Harmonic expansion */ +#define RE 6371.2 /* Earth Radius */ +#define MAXALT 2000 /* maximum altitude in km */ +#define NCOORD 3 /* xyz */ +#define POLYORD 5 /* quartic polynomial fit in altitude */ +#define NFLAG 2 /* 0: geo to AACGM, 1: AACGM to geo */ +#define SHORDER 10 /* order of Spherical Harmonic expansion */ #define AACGM_KMAX ((SHORDER+1)*(SHORDER+1)) /* number of SH coefficients */ /* options for AACGM-v2 coordinate determination */ -#define G2A 0 /* convert geographic (geodetic) to AACGM-v2 coords */ -#define A2G 1 /* convert AACGM-v2 to geographic (geodetic) coords */ -#define TRACE 2 /* use field-line tracing to compute coordinates */ -#define ALLOWTRACE 4 /* if height is >2000 km use tracing, else use coefs */ -#define BADIDEA 8 /* use coefficients above 2000 km; Terrible idea!! */ -#define GEOCENTRIC 16 /* assume inputs are geocentric with sphere RE */ +#define G2A 0 /* convert geographic (geodetic) to AACGM-v2 coords */ +#define A2G 1 /* convert AACGM-v2 to geographic (geodetic) coords */ +#define TRACE 2 /* use field-line tracing to compute coordinates */ +#define ALLOWTRACE 4 /* if height is >2000 km use tracing, else use coefs */ +#define BADIDEA 8 /* use coefficients above 2000 km; Terrible idea!! */ +#define GEOCENTRIC 16 /* assume inputs are geocentric with sphere RE */ +#ifndef M_PI + #define M_PI 3.14159265358979323846 /* define M_PI if not already */ +#endif + +/* added for MSC compatibility */ +#ifdef _MSC_VER + #ifndef NAN + #include + #define INFINITY (DBL_MAX+DBL_MAX) + #define NAN (INFINITY-INFINITY) + #endif +#endif /***************************************************************************** * function prototypes @@ -31,27 +43,29 @@ int AACGM_v2_Rylm(double colat, double lon, int order, double *ylmval); void AACGM_v2_Alt2CGM(double r_height_in, double r_lat_alt, double *r_lat_adj); int AACGM_v2_CGM2Alt(double r_height_in, double r_lat_in, double *r_lat_adj); double AACGM_v2_Sgn(double a, double b); -int convert_geo_coord(double lat_in, double lon_in, double height_in, - double *lat_out, double *lon_out, int flag, int order); +int convert_geo_coord_v2(double lat_in, double lon_in, double height_in, + double *lat_out, double *lon_out, int flag, int order, + char *igrf_filename); int AACGM_v2_LoadCoefFP(FILE *fp, int code); int AACGM_v2_LoadCoef(char *fname, int code); -int AACGM_v2_LoadCoefs(int year); -int AACGM_v2_TimeInterp(void); +int AACGM_v2_LoadCoefs(int year, char *root); +int AACGM_v2_TimeInterp(char *root); void AACGM_v2_errmsg(int ecode); int AACGM_v2_Trace(double lat_in, double lon_in, double alt, - double *lat_out, double *lon_out); + double *lat_out, double *lon_out, char *igrf_filename); int AACGM_v2_Trace_inv(double lat_in, double lon_in, double alt, - double *lat_out, double *lon_out); + double *lat_out, double *lon_out, char *igrf_filename); /* public functions */ int AACGM_v2_Convert(double in_lat, double in_lon, double height, - double *out_lat, double *out_lon, double *r, int code); + double *out_lat, double *out_lon, double *r, int code, + char *igrf_filename); int AACGM_v2_SetDateTime(int year, int month, int day, - int hour, int minute, int second); + int hour, int minute, int second, char *root); int AACGM_v2_GetDateTime(int *year, int *month, int *day, - int *hour, int *minute, int *second, int *dayno); -int AACGM_v2_SetNow(void); + int *hour, int *minute, int *second, int *dayno); +int AACGM_v2_SetNow(char *root); #endif diff --git a/src/c_aacgm_v2/astalg.h b/src/c_aacgm_v2/astalg.h new file mode 100755 index 00000000..d232a7d9 --- /dev/null +++ b/src/c_aacgm_v2/astalg.h @@ -0,0 +1,95 @@ +/* astalg.h + ======== + Author: Kile Baker +*/ + +/* + Copyright and License Information + + This source file is part of a library of files implementing + portions of the algorithms given in the book _Astronomical + Algorithms_ by Jean Meeus. + + Software Copyright (C) 2006, U.S. Government + Author: Kile B. Baker + National Science Foundation + 4201 Wilson Blvd, + Arlington, VA 22230 + email: kbaker@nsf.gov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + + + +*/ + + + +/* ----------------- REFERENCE ------------------------ + +The software contained herein is derived from algorithms published +in the book _Astronomical Algorithms_, Second Edition, by Jean Meeus, +publisher: Willman-Bell, Inc., Richmond, Virginia, 1998 (corrections +dated 2005). + +The book will be referred to as "Meeus" for short. + + +*/ + +#ifndef _AstAlg_H +#define _AstAlg_H + +#define J2000 (2451545.0) +/* The reference time for all the algorithms in Meeus. The reference + date is January 1, 2000 at UT (actually TD) noon (i.e. day 1.5 of 2000). + + The zero time for Julian Day is noon on the beginning of the year in + -4712. +*/ +/* I need the value of PI, so include the math.h definitions */ + +#include + +#define AstAlg_DTOR (M_PI/180.0) + +/* we need a floating point version of the % binary operator */ + +#define dmod(a,b) ((double) ((((long) a) % ((long) b)) + (a - (long) a))) + +double AstAlg_apparent_obliquity(double jd); +double AstAlg_apparent_solar_longitude(double jd); +double AstAlg_dday(int day, int hour, int minute, int second); +double AstAlg_equation_of_time(double jd); +double AstAlg_geometric_solar_longitude(double jd); +double AstAlg_jde(int year, int month, double day); +void AstAlg_jde2calendar(double jd, + int *year, + int *month, + int *day, + int *hour, + int *minute, + int *second); +double AstAlg_lunar_ascending_node(double jd); +double AstAlg_mean_lunar_longitude(double jd); +double AstAlg_mean_obliquity(double jd); +double AstAlg_mean_solar_anomaly( double jd ); +double AstAlg_mean_solar_longitude( double jd ); +void AstAlg_nutation_corr(double jd, double *slong_corr, double *obliq_corr); +double AstAlg_solar_declination(double jd); +double AstAlg_solar_right_ascension(double jd); + +#endif diff --git a/src/c_aacgm_v2/igrflib.h b/src/c_aacgm_v2/igrflib.h index 4308d88b..091a758a 100644 --- a/src/c_aacgm_v2/igrflib.h +++ b/src/c_aacgm_v2/igrflib.h @@ -1,16 +1,23 @@ +#ifndef _IGRFLIB_ +#define _IGRFLIB_ #define IGRF_FIRST_EPOCH 1900 #define IGRF_LAST_EPOCH 2015 -#define IGRF_FILE "igrf12coeffs.txt" /* current IGRF model */ +/* SGS using IGRF_COEFFS environment variable */ +/*#define IGRF_FILE "igrf12coeffs.txt"*/ /* current IGRF model */ #ifndef RE #define RE 6371.2 /* Earth Radius */ #endif +#ifndef M_PI +#define M_PI 3.14159265358979323846 /* define M_PI if not already */ +#endif + #define MAXSTR 200 /* maximum string length */ #define MAXNYR 100 /* maximum number of epochs */ #define IGRF_ORDER 13 /* maximum order of SH expansion */ -#define IGRF_MAXK ((IGRF_ORDER+1)*(IGRF_ORDER+1)) /* # of SH coefficients */ +#define IGRF_MAXK ((IGRF_ORDER+1)*(IGRF_ORDER+1)) /* # of SH coefficients */ #define DTOR (M_PI/180.) #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -18,17 +25,20 @@ /* function prototypes */ /* private functions */ -int igrf_loadcoeffs(char *filename); +int IGRF_loadcoeffs(char *filename); +int IGRF_interpolate_coefs(void); void pause(void); void IGRF_msg_notime(void); /* public functions */ int IGRF_compute(const double rtp[], double brtp[]); -int IGRF_SetNow(void); +int IGRF_SetNow(char *filename); int IGRF_GetDateTime(int *year, int *month, int *day, - int *hour, int *minute, int *second, int *dayno); + int *hour, int *minute, int *second, int *dayno); int IGRF_SetDateTime(int year, int month, int day, - int hour, int minute, int second); + int hour, int minute, int second, char *filename); +double IGRF_Tilt(int yr, int mo, int dy, int hr, int mt, int sc, + char *filename); /* some geopack functionality */ int geo2mag(const double xyzg[], double xyzm[]); @@ -45,3 +55,5 @@ int geoc2geod(double lat, double lon, double r, double llh[]); int AACGM_v2_Newval(double xyz[], int idir, double ds, double k[]); int AACGM_v2_RK45(double xyz[], int idir, double *ds, double eps, int code); +#endif + diff --git a/src/c_aacgm_v2/mlt_v2.h b/src/c_aacgm_v2/mlt_v2.h new file mode 100755 index 00000000..80ea7ed9 --- /dev/null +++ b/src/c_aacgm_v2/mlt_v2.h @@ -0,0 +1,22 @@ + +#ifndef _MLT_v2_H +#define _MLT_v2_H + +double MLTConvert_v2(int yr, int mo, int dy, int hr, int mt ,int sc, + double mlon, char *root, char *igrf_filename); +double inv_MLTConvert_v2(int yr, int mo, int dy, int hr, int mt ,int sc, + double mlt, char *igrf_filename); +double MLTConvertYMDHMS_v2(int yr,int mo,int dy,int hr,int mt,int sc, + double mlon, char *root, char *igrf_filename); +double inv_MLTConvertYMDHMS_v2(int yr,int mo,int dy,int hr,int mt,int sc, + double mlt, char *igrf_filename); +double MLTConvertYrsec_v2(int yr,int yrsec, double mlon, char *root, + char *igrf_filename); +double inv_MLTConvertYrsec_v2(int yr, int yrsec, double mlt, + char *igrf_filename); +double MLTConvertEpoch_v2(double epoch, double mlon, char *root, + char *igrf_filename); +double inv_MLTConvertEpoch_v2(double epoch, double mlt, char *igrf_filename); + +#endif + diff --git a/src/c_aacgm_v2/rtime.h b/src/c_aacgm_v2/rtime.h new file mode 100755 index 00000000..79fd9f83 --- /dev/null +++ b/src/c_aacgm_v2/rtime.h @@ -0,0 +1,26 @@ +/* rtime.h + +====== + Author: R.J.Barnes +*/ + +/* + (c) 2010 JHU/APL & Others - Please Consult LICENSE.superdarn-rst.3.2-beta-4-g32f7302.txt for more information. +*/ + +#ifndef _RTIME_H +#define _RTIME_H + +void TimeYrsecToYMDHMS(int time,int yr, + int *mo,int *dy, int *hr,int *mn,int *sc); +int TimeYMDHMSToYrsec(int yr, int mo, int dy, int hr, int mn, int sc); +double TimeYMDHMSToEpoch(int yr,int mo,int dy,int hr,int mn,double sc); +void TimeEpochToYMDHMS(double tme, + int *yr,int *mo,int *dy,int *hr,int *mn, double *sc); +double TimeYMDHMSToJulian(int yr,int mo,int dy,int hr,int mt,double sc); +int TimeJulianToYMDHMS(double jd, + int *yr,int *mo, int *dy,int *hr,int *mt,double *sc); +void TimeReadClock(int *yr, int *mo, int *dy, int *hr, int *mn, int *sc, + int *usc); + +#endif + From 6081daceea4d0265cfecc5bd1eb9b3e21fe8af3e Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 8 Mar 2018 20:21:20 -0600 Subject: [PATCH 004/101] Removed duplicate file Removed duplicate coefficient file --- src/c_aacgm_v2/igrf12coeffs.txt | 199 -------------------------------- 1 file changed, 199 deletions(-) delete mode 100644 src/c_aacgm_v2/igrf12coeffs.txt diff --git a/src/c_aacgm_v2/igrf12coeffs.txt b/src/c_aacgm_v2/igrf12coeffs.txt deleted file mode 100644 index 92304705..00000000 --- a/src/c_aacgm_v2/igrf12coeffs.txt +++ /dev/null @@ -1,199 +0,0 @@ -# 12th Generation International Geomagnetic Reference Field Schmidt semi-normalised spherical harmonic coefficients, degree n=1,13 -# in units nanoTesla for IGRF and definitive DGRF main-field models (degree n=1,8 nanoTesla/year for secular variation (SV)) -c/s deg ord IGRF IGRF IGRF IGRF IGRF IGRF IGRF IGRF IGRF DGRF DGRF DGRF DGRF DGRF DGRF DGRF DGRF DGRF DGRF DGRF DGRF DGRF DGRF IGRF SV -g/h n m 1900.0 1905.0 1910.0 1915.0 1920.0 1925.0 1930.0 1935.0 1940.0 1945.0 1950.0 1955.0 1960.0 1965.0 1970.0 1975.0 1980.0 1985.0 1990.0 1995.0 2000.0 2005.0 2010.0 2015.0 2015-20 -g 1 0 -31543 -31464 -31354 -31212 -31060 -30926 -30805 -30715 -30654 -30594 -30554 -30500 -30421 -30334 -30220 -30100 -29992 -29873 -29775 -29692 -29619.4 -29554.63 -29496.57 -29442.0 10.3 -g 1 1 -2298 -2298 -2297 -2306 -2317 -2318 -2316 -2306 -2292 -2285 -2250 -2215 -2169 -2119 -2068 -2013 -1956 -1905 -1848 -1784 -1728.2 -1669.05 -1586.42 -1501.0 18.1 -h 1 1 5922 5909 5898 5875 5845 5817 5808 5812 5821 5810 5815 5820 5791 5776 5737 5675 5604 5500 5406 5306 5186.1 5077.99 4944.26 4797.1 -26.6 -g 2 0 -677 -728 -769 -802 -839 -893 -951 -1018 -1106 -1244 -1341 -1440 -1555 -1662 -1781 -1902 -1997 -2072 -2131 -2200 -2267.7 -2337.24 -2396.06 -2445.1 -8.7 -g 2 1 2905 2928 2948 2956 2959 2969 2980 2984 2981 2990 2998 3003 3002 2997 3000 3010 3027 3044 3059 3070 3068.4 3047.69 3026.34 3012.9 -3.3 -h 2 1 -1061 -1086 -1128 -1191 -1259 -1334 -1424 -1520 -1614 -1702 -1810 -1898 -1967 -2016 -2047 -2067 -2129 -2197 -2279 -2366 -2481.6 -2594.50 -2708.54 -2845.6 -27.4 -g 2 2 924 1041 1176 1309 1407 1471 1517 1550 1566 1578 1576 1581 1590 1594 1611 1632 1663 1687 1686 1681 1670.9 1657.76 1668.17 1676.7 2.1 -h 2 2 1121 1065 1000 917 823 728 644 586 528 477 381 291 206 114 25 -68 -200 -306 -373 -413 -458.0 -515.43 -575.73 -641.9 -14.1 -g 3 0 1022 1037 1058 1084 1111 1140 1172 1206 1240 1282 1297 1302 1302 1297 1287 1276 1281 1296 1314 1335 1339.6 1336.30 1339.85 1350.7 3.4 -g 3 1 -1469 -1494 -1524 -1559 -1600 -1645 -1692 -1740 -1790 -1834 -1889 -1944 -1992 -2038 -2091 -2144 -2180 -2208 -2239 -2267 -2288.0 -2305.83 -2326.54 -2352.3 -5.5 -h 3 1 -330 -357 -389 -421 -445 -462 -480 -494 -499 -499 -476 -462 -414 -404 -366 -333 -336 -310 -284 -262 -227.6 -198.86 -160.40 -115.3 8.2 -g 3 2 1256 1239 1223 1212 1205 1202 1205 1215 1232 1255 1274 1288 1289 1292 1278 1260 1251 1247 1248 1249 1252.1 1246.39 1232.10 1225.6 -0.7 -h 3 2 3 34 62 84 103 119 133 146 163 186 206 216 224 240 251 262 271 284 293 302 293.4 269.72 251.75 244.9 -0.4 -g 3 3 572 635 705 778 839 881 907 918 916 913 896 882 878 856 838 830 833 829 802 759 714.5 672.51 633.73 582.0 -10.1 -h 3 3 523 480 425 360 293 229 166 101 43 -11 -46 -83 -130 -165 -196 -223 -252 -297 -352 -427 -491.1 -524.72 -537.03 -538.4 1.8 -g 4 0 876 880 884 887 889 891 896 903 914 944 954 958 957 957 952 946 938 936 939 940 932.3 920.55 912.66 907.6 -0.7 -g 4 1 628 643 660 678 695 711 727 744 762 776 792 796 800 804 800 791 782 780 780 780 786.8 797.96 808.97 813.7 0.2 -h 4 1 195 203 211 218 220 216 205 188 169 144 136 133 135 148 167 191 212 232 247 262 272.6 282.07 286.48 283.3 -1.3 -g 4 2 660 653 644 631 616 601 584 565 550 544 528 510 504 479 461 438 398 361 325 290 250.0 210.65 166.58 120.4 -9.1 -h 4 2 -69 -77 -90 -109 -134 -163 -195 -226 -252 -276 -278 -274 -278 -269 -266 -265 -257 -249 -240 -236 -231.9 -225.23 -211.03 -188.7 5.3 -g 4 3 -361 -380 -400 -416 -424 -426 -422 -415 -405 -421 -408 -397 -394 -390 -395 -405 -419 -424 -423 -418 -403.0 -379.86 -356.83 -334.9 4.1 -h 4 3 -210 -201 -189 -173 -153 -130 -109 -90 -72 -55 -37 -23 3 13 26 39 53 69 84 97 119.8 145.15 164.46 180.9 2.9 -g 4 4 134 146 160 178 199 217 234 249 265 304 303 290 269 252 234 216 199 170 141 122 111.3 100.00 89.40 70.4 -4.3 -h 4 4 -75 -65 -55 -51 -57 -70 -90 -114 -141 -178 -210 -230 -255 -269 -279 -288 -297 -297 -299 -306 -303.8 -305.36 -309.72 -329.5 -5.2 -g 5 0 -184 -192 -201 -211 -221 -230 -237 -241 -241 -253 -240 -229 -222 -219 -216 -218 -218 -214 -214 -214 -218.8 -227.00 -230.87 -232.6 -0.2 -g 5 1 328 328 327 327 326 326 327 329 334 346 349 360 362 358 359 356 357 355 353 352 351.4 354.41 357.29 360.1 0.5 -h 5 1 -210 -193 -172 -148 -122 -96 -72 -51 -33 -12 3 15 16 19 26 31 46 47 46 46 43.8 42.72 44.58 47.3 0.6 -g 5 2 264 259 253 245 236 226 218 211 208 194 211 230 242 254 262 264 261 253 245 235 222.3 208.95 200.26 192.4 -1.3 -h 5 2 53 56 57 58 58 58 60 64 71 95 103 110 125 128 139 148 150 150 154 165 171.9 180.25 189.01 197.0 1.7 -g 5 3 5 -1 -9 -16 -23 -28 -32 -33 -33 -20 -20 -23 -26 -31 -42 -59 -74 -93 -109 -118 -130.4 -136.54 -141.05 -140.9 -0.1 -h 5 3 -33 -32 -33 -34 -38 -44 -53 -64 -75 -67 -87 -98 -117 -126 -139 -152 -151 -154 -153 -143 -133.1 -123.45 -118.06 -119.3 -1.2 -g 5 4 -86 -93 -102 -111 -119 -125 -131 -136 -141 -142 -147 -152 -156 -157 -160 -159 -162 -164 -165 -166 -168.6 -168.05 -163.17 -157.5 1.4 -h 5 4 -124 -125 -126 -126 -125 -122 -118 -115 -113 -119 -122 -121 -114 -97 -91 -83 -78 -75 -69 -55 -39.3 -19.57 -0.01 16.0 3.4 -g 5 5 -16 -26 -38 -51 -62 -69 -74 -76 -76 -82 -76 -69 -63 -62 -56 -49 -48 -46 -36 -17 -12.9 -13.55 -8.03 4.1 3.9 -h 5 5 3 11 21 32 43 51 58 64 69 82 80 78 81 81 83 88 92 95 97 107 106.3 103.85 101.04 100.2 0.0 -g 6 0 63 62 62 61 61 61 60 59 57 59 54 47 46 45 43 45 48 53 61 68 72.3 73.60 72.78 70.0 -0.3 -g 6 1 61 60 58 57 55 54 53 53 54 57 57 57 58 61 64 66 66 65 65 67 68.2 69.56 68.69 67.7 -0.1 -h 6 1 -9 -7 -5 -2 0 3 4 4 4 6 -1 -9 -10 -11 -12 -13 -15 -16 -16 -17 -17.4 -20.33 -20.90 -20.8 0.0 -g 6 2 -11 -11 -11 -10 -10 -9 -9 -8 -7 6 4 3 1 8 15 28 42 51 59 68 74.2 76.74 75.92 72.7 -0.7 -h 6 2 83 86 89 93 96 99 102 104 105 100 99 96 99 100 100 99 93 88 82 72 63.7 54.75 44.18 33.2 -2.1 -g 6 3 -217 -221 -224 -228 -233 -238 -242 -246 -249 -246 -247 -247 -237 -228 -212 -198 -192 -185 -178 -170 -160.9 -151.34 -141.40 -129.9 2.1 -h 6 3 2 4 5 8 11 14 19 25 33 16 33 48 60 68 72 75 71 69 69 67 65.1 63.63 61.54 58.9 -0.7 -g 6 4 -58 -57 -54 -51 -46 -40 -32 -25 -18 -25 -16 -8 -1 4 2 1 4 4 3 -1 -5.9 -14.58 -22.83 -28.9 -1.2 -h 6 4 -35 -32 -29 -26 -22 -18 -16 -15 -15 -9 -12 -16 -20 -32 -37 -41 -43 -48 -52 -58 -61.2 -63.53 -66.26 -66.7 0.2 -g 6 5 59 57 54 49 44 39 32 25 18 21 12 7 -2 1 3 6 14 16 18 19 16.9 14.58 13.10 13.2 0.3 -h 6 5 36 32 28 23 18 13 8 4 0 -16 -12 -12 -11 -8 -6 -4 -2 -1 1 1 0.7 0.24 3.02 7.3 0.9 -g 6 6 -90 -92 -95 -98 -101 -103 -104 -106 -107 -104 -105 -107 -113 -111 -112 -111 -108 -102 -96 -93 -90.4 -86.36 -78.09 -70.9 1.6 -h 6 6 -69 -67 -65 -62 -57 -52 -46 -40 -33 -39 -30 -24 -17 -7 1 11 17 21 24 36 43.8 50.94 55.40 62.6 1.0 -g 7 0 70 70 71 72 73 73 74 74 74 70 65 65 67 75 72 71 72 74 77 77 79.0 79.88 80.44 81.6 0.3 -g 7 1 -55 -54 -54 -54 -54 -54 -54 -53 -53 -40 -55 -56 -56 -57 -57 -56 -59 -62 -64 -72 -74.0 -74.46 -75.00 -76.1 -0.2 -h 7 1 -45 -46 -47 -48 -49 -50 -51 -52 -52 -45 -35 -50 -55 -61 -70 -77 -82 -83 -80 -69 -64.6 -61.14 -57.80 -54.1 0.8 -g 7 2 0 0 1 2 2 3 4 4 4 0 2 2 5 4 1 1 2 3 2 1 0.0 -1.65 -4.55 -6.8 -0.5 -h 7 2 -13 -14 -14 -14 -14 -14 -15 -17 -18 -18 -17 -24 -28 -27 -27 -26 -27 -27 -26 -25 -24.2 -22.57 -21.20 -19.5 0.4 -g 7 3 34 33 32 31 29 27 25 23 20 0 1 10 15 13 14 16 21 24 26 28 33.3 38.73 45.24 51.8 1.3 -h 7 3 -10 -11 -12 -12 -13 -14 -14 -14 -14 2 0 -4 -6 -2 -4 -5 -5 -2 0 4 6.2 6.82 6.54 5.7 -0.2 -g 7 4 -41 -41 -40 -38 -37 -35 -34 -33 -31 -29 -40 -32 -32 -26 -22 -14 -12 -6 -1 5 9.1 12.30 14.00 15.0 0.1 -h 7 4 -1 0 1 2 4 5 6 7 7 6 10 8 7 6 8 10 16 20 21 24 24.0 25.35 24.96 24.4 -0.3 -g 7 5 -21 -20 -19 -18 -16 -14 -12 -11 -9 -10 -7 -11 -7 -6 -2 0 1 4 5 4 6.9 9.37 10.46 9.4 -0.6 -h 7 5 28 28 28 28 28 29 29 29 29 28 36 28 23 26 23 22 18 17 17 17 14.8 10.93 7.03 3.4 -0.6 -g 7 6 18 18 18 19 19 19 18 18 17 15 5 9 17 13 13 12 11 10 9 8 7.3 5.42 1.64 -2.8 -0.8 -h 7 6 -12 -12 -13 -15 -16 -17 -18 -19 -20 -17 -18 -20 -18 -23 -23 -23 -23 -23 -23 -24 -25.4 -26.32 -27.61 -27.4 0.1 -g 7 7 6 6 6 6 6 6 6 6 5 29 19 18 8 1 -2 -5 -2 0 0 -2 -1.2 1.94 4.92 6.8 0.2 -h 7 7 -22 -22 -22 -22 -22 -21 -20 -19 -19 -22 -16 -18 -17 -12 -11 -12 -10 -7 -4 -6 -5.8 -4.64 -3.28 -2.2 -0.2 -g 8 0 11 11 11 11 11 11 11 11 11 13 22 11 15 13 14 14 18 21 23 25 24.4 24.80 24.41 24.2 0.2 -g 8 1 8 8 8 8 7 7 7 7 7 7 15 9 6 5 6 6 6 6 5 6 6.6 7.62 8.21 8.8 0.0 -h 8 1 8 8 8 8 8 8 8 8 8 12 5 10 11 7 7 6 7 8 10 11 11.9 11.20 10.84 10.1 -0.3 -g 8 2 -4 -4 -4 -4 -3 -3 -3 -3 -3 -8 -4 -6 -4 -4 -2 -1 0 0 -1 -6 -9.2 -11.73 -14.50 -16.9 -0.6 -h 8 2 -14 -15 -15 -15 -15 -15 -15 -15 -14 -21 -22 -15 -14 -12 -15 -16 -18 -19 -19 -21 -21.5 -20.88 -20.03 -18.3 0.3 -g 8 3 -9 -9 -9 -9 -9 -9 -9 -9 -10 -5 -1 -14 -11 -14 -13 -12 -11 -11 -10 -9 -7.9 -6.88 -5.59 -3.2 0.5 -h 8 3 7 7 6 6 6 6 5 5 5 -12 0 5 7 9 6 4 4 5 6 8 8.5 9.83 11.83 13.3 0.1 -g 8 4 1 1 1 2 2 2 2 1 1 9 11 6 2 0 -3 -8 -7 -9 -12 -14 -16.6 -18.11 -19.34 -20.6 -0.2 -h 8 4 -13 -13 -13 -13 -14 -14 -14 -15 -15 -7 -21 -23 -18 -16 -17 -19 -22 -23 -22 -23 -21.5 -19.71 -17.41 -14.6 0.5 -g 8 5 2 2 2 3 4 4 5 6 6 7 15 10 10 8 5 4 4 4 3 9 9.1 10.17 11.61 13.4 0.4 -h 8 5 5 5 5 5 5 5 5 5 5 2 -8 3 4 4 6 6 9 11 12 15 15.5 16.22 16.71 16.2 -0.2 -g 8 6 -9 -8 -8 -8 -7 -7 -6 -6 -5 -10 -13 -7 -5 -1 0 0 3 4 4 6 7.0 9.36 10.85 11.7 0.1 -h 8 6 16 16 16 16 17 17 18 18 19 18 17 23 23 24 21 18 16 14 12 11 8.9 7.61 6.96 5.7 -0.3 -g 8 7 5 5 5 6 6 7 8 8 9 7 5 6 10 11 11 10 6 4 2 -5 -7.9 -11.25 -14.05 -15.9 -0.4 -h 8 7 -5 -5 -5 -5 -5 -5 -5 -5 -5 3 -4 -4 1 -3 -6 -10 -13 -15 -16 -16 -14.9 -12.76 -10.74 -9.1 0.3 -g 8 8 8 8 8 8 8 8 8 7 7 2 -1 9 8 4 3 1 -1 -4 -6 -7 -7.0 -4.87 -3.54 -2.0 0.3 -h 8 8 -18 -18 -18 -18 -19 -19 -19 -19 -19 -11 -17 -13 -20 -17 -16 -17 -15 -11 -10 -4 -2.1 -0.06 1.64 2.1 0.0 -g 9 0 8 8 8 8 8 8 8 8 8 5 3 4 4 8 8 7 5 5 4 4 5.0 5.58 5.50 5.4 0.0 -g 9 1 10 10 10 10 10 10 10 10 10 -21 -7 9 6 10 10 10 10 10 9 9 9.4 9.76 9.45 8.8 0.0 -h 9 1 -20 -20 -20 -20 -20 -20 -20 -20 -21 -27 -24 -11 -18 -22 -21 -21 -21 -21 -20 -20 -19.7 -20.11 -20.54 -21.6 0.0 -g 9 2 1 1 1 1 1 1 1 1 1 1 -1 -4 0 2 2 2 1 1 1 3 3.0 3.58 3.45 3.1 0.0 -h 9 2 14 14 14 14 14 14 14 15 15 17 19 12 12 15 16 16 16 15 15 15 13.4 12.69 11.51 10.8 0.0 -g 9 3 -11 -11 -11 -11 -11 -11 -12 -12 -12 -11 -25 -5 -9 -13 -12 -12 -12 -12 -12 -10 -8.4 -6.94 -5.27 -3.3 0.0 -h 9 3 5 5 5 5 5 5 5 5 5 29 12 7 2 7 6 7 9 9 11 12 12.5 12.67 12.75 11.8 0.0 -g 9 4 12 12 12 12 12 12 12 11 11 3 10 2 1 10 10 10 9 9 9 8 6.3 5.01 3.13 0.7 0.0 -h 9 4 -3 -3 -3 -3 -3 -3 -3 -3 -3 -9 2 6 0 -4 -4 -4 -5 -6 -7 -6 -6.2 -6.72 -7.14 -6.8 0.0 -g 9 5 1 1 1 1 1 1 1 1 1 16 5 4 4 -1 -1 -1 -3 -3 -4 -8 -8.9 -10.76 -12.38 -13.3 0.0 -h 9 5 -2 -2 -2 -2 -2 -2 -2 -3 -3 4 2 -2 -3 -5 -5 -5 -6 -6 -7 -8 -8.4 -8.16 -7.42 -6.9 0.0 -g 9 6 -2 -2 -2 -2 -2 -2 -2 -2 -2 -3 -5 1 -1 -1 0 -1 -1 -1 -2 -1 -1.5 -1.25 -0.76 -0.1 0.0 -h 9 6 8 8 8 8 9 9 9 9 9 9 8 10 9 10 10 10 9 9 9 8 8.4 8.10 7.97 7.8 0.0 -g 9 7 2 2 2 2 2 2 3 3 3 -4 -2 2 -2 5 3 4 7 7 7 10 9.3 8.76 8.43 8.7 0.0 -h 9 7 10 10 10 10 10 10 10 11 11 6 8 7 8 10 11 11 10 9 8 5 3.8 2.92 2.14 1.0 0.0 -g 9 8 -1 0 0 0 0 0 0 0 1 -3 3 2 3 1 1 1 2 1 1 -2 -4.3 -6.66 -8.42 -9.1 0.0 -h 9 8 -2 -2 -2 -2 -2 -2 -2 -2 -2 1 -11 -6 0 -4 -2 -3 -6 -7 -7 -8 -8.2 -7.73 -6.08 -4.0 0.0 -g 9 9 -1 -1 -1 -1 -1 -1 -2 -2 -2 -4 8 5 -1 -2 -1 -2 -5 -5 -6 -8 -8.2 -9.22 -10.08 -10.5 0.0 -h 9 9 2 2 2 2 2 2 2 2 2 8 -7 5 5 1 1 1 2 2 2 3 4.8 6.01 7.01 8.4 0.0 -g 10 0 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -8 -3 1 -2 -3 -3 -4 -4 -3 -3 -2.6 -2.17 -1.94 -1.9 0.0 -g 10 1 -4 -4 -4 -4 -4 -4 -4 -4 -4 11 4 -5 -3 -3 -3 -3 -4 -4 -4 -6 -6.0 -6.12 -6.24 -6.3 0.0 -h 10 1 2 2 2 2 2 2 2 2 2 5 13 -4 4 2 1 1 1 1 2 1 1.7 2.19 2.73 3.2 0.0 -g 10 2 2 2 2 2 2 2 2 2 2 1 -1 -1 4 2 2 2 2 3 2 2 1.7 1.42 0.89 0.1 0.0 -h 10 2 1 1 1 1 1 1 1 1 1 1 -2 0 1 1 1 1 0 0 1 0 0.0 0.10 -0.10 -0.4 0.0 -g 10 3 -5 -5 -5 -5 -5 -5 -5 -5 -5 2 13 2 0 -5 -5 -5 -5 -5 -5 -4 -3.1 -2.35 -1.07 0.5 0.0 -h 10 3 2 2 2 2 2 2 2 2 2 -20 -10 -8 0 2 3 3 3 3 3 4 4.0 4.46 4.71 4.6 0.0 -g 10 4 -2 -2 -2 -2 -2 -2 -2 -2 -2 -5 -4 -3 -1 -2 -1 -2 -2 -2 -2 -1 -0.5 -0.15 -0.16 -0.5 0.0 -h 10 4 6 6 6 6 6 6 6 6 6 -1 2 -2 2 6 4 4 6 6 6 5 4.9 4.76 4.44 4.4 0.0 -g 10 5 6 6 6 6 6 6 6 6 6 -1 4 7 4 4 6 5 5 5 4 4 3.7 3.06 2.45 1.8 0.0 -h 10 5 -4 -4 -4 -4 -4 -4 -4 -4 -4 -6 -3 -4 -5 -4 -4 -4 -4 -4 -4 -5 -5.9 -6.58 -7.22 -7.9 0.0 -g 10 6 4 4 4 4 4 4 4 4 4 8 12 4 6 4 4 4 3 3 3 2 1.0 0.29 -0.33 -0.7 0.0 -h 10 6 0 0 0 0 0 0 0 0 0 6 6 1 1 0 0 -1 0 0 0 -1 -1.2 -1.01 -0.96 -0.6 0.0 -g 10 7 0 0 0 0 0 0 0 0 0 -1 3 -2 1 0 1 1 1 1 1 2 2.0 2.06 2.13 2.1 0.0 -h 10 7 -2 -2 -2 -2 -2 -2 -2 -1 -1 -4 -3 -3 -1 -2 -1 -1 -1 -1 -2 -2 -2.9 -3.47 -3.95 -4.2 0.0 -g 10 8 2 2 2 1 1 1 1 2 2 -3 2 6 -1 2 0 0 2 2 3 5 4.2 3.77 3.09 2.4 0.0 -h 10 8 4 4 4 4 4 4 4 4 4 -2 6 7 6 3 3 3 4 4 3 1 0.2 -0.86 -1.99 -2.8 0.0 -g 10 9 2 2 2 2 3 3 3 3 3 5 10 -2 2 2 3 3 3 3 3 1 0.3 -0.21 -1.03 -1.8 0.0 -h 10 9 0 0 0 0 0 0 0 0 0 0 11 -1 0 0 1 1 0 0 -1 -2 -2.2 -2.31 -1.97 -1.2 0.0 -g 10 10 0 0 0 0 0 0 0 0 0 -2 3 0 0 0 -1 -1 0 0 0 0 -1.1 -2.09 -2.80 -3.6 0.0 -h 10 10 -6 -6 -6 -6 -6 -6 -6 -6 -6 -2 8 -3 -7 -6 -4 -5 -6 -6 -6 -7 -7.4 -7.93 -8.31 -8.7 0.0 -g 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2.7 2.95 3.05 3.1 0.0 -g 11 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1.7 -1.60 -1.48 -1.5 0.0 -h 11 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0.26 0.13 -0.1 0.0 -g 11 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1.9 -1.88 -2.03 -2.3 0.0 -h 11 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.3 1.44 1.67 2.0 0.0 -g 11 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.5 1.44 1.65 2.0 0.0 -h 11 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -0.77 -0.66 -0.7 0.0 -g 11 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.1 -0.31 -0.51 -0.8 0.0 -h 11 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -2.6 -2.27 -1.76 -1.1 0.0 -g 11 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0.29 0.54 0.6 0.0 -h 11 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.9 0.90 0.85 0.8 0.0 -g 11 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.7 -0.79 -0.79 -0.7 0.0 -h 11 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.7 -0.58 -0.39 -0.2 0.0 -g 11 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0.53 0.37 0.2 0.0 -h 11 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -2.8 -2.69 -2.51 -2.2 0.0 -g 11 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.7 1.80 1.79 1.7 0.0 -h 11 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -1.08 -1.27 -1.4 0.0 -g 11 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0.16 0.12 -0.2 0.0 -h 11 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1.2 -1.58 -2.11 -2.5 0.0 -g 11 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.2 0.96 0.75 0.4 0.0 -h 11 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1.9 -1.90 -1.94 -2.0 0.0 -g 11 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4.0 3.99 3.75 3.5 0.0 -h 11 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -1.39 -1.86 -2.4 0.0 -g 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -2.2 -2.15 -2.12 -1.9 0.0 -g 12 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.3 -0.29 -0.21 -0.2 0.0 -h 12 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.55 -0.87 -1.1 0.0 -g 12 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0.21 0.30 0.4 0.0 -h 12 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.23 0.27 0.4 0.0 -g 12 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.9 0.89 1.04 1.2 0.0 -h 12 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2.5 2.38 2.13 1.9 0.0 -g 12 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.2 -0.38 -0.63 -0.8 0.0 -h 12 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -2.6 -2.63 -2.49 -2.2 0.0 -g 12 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.9 0.96 0.95 0.9 0.0 -h 12 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0.61 0.49 0.3 0.0 -g 12 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.5 -0.30 -0.11 0.1 0.0 -h 12 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.40 0.59 0.7 0.0 -g 12 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.46 0.52 0.5 0.0 -h 12 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0 0.01 0.00 -0.1 0.0 -g 12 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.3 -0.35 -0.39 -0.3 0.0 -h 12 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0 0.02 0.13 0.3 0.0 -g 12 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.36 -0.37 -0.4 0.0 -h 12 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.28 0.27 0.2 0.0 -g 12 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.1 0.08 0.21 0.2 0.0 -h 12 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -0.87 -0.86 -0.9 0.0 -g 12 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.2 -0.49 -0.77 -0.9 0.0 -h 12 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.34 -0.23 -0.1 0.0 -g 12 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.08 0.04 0.0 0.0 -h 12 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.8 0.88 0.87 0.7 0.0 -g 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.2 -0.16 -0.09 0.0 0.0 -g 13 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -0.88 -0.89 -0.9 0.0 -h 13 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -0.76 -0.87 -0.9 0.0 -g 13 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.30 0.31 0.4 0.0 -h 13 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0.33 0.30 0.4 0.0 -g 13 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0.28 0.42 0.5 0.0 -h 13 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.8 1.72 1.66 1.6 0.0 -g 13 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.43 -0.45 -0.5 0.0 -h 13 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.54 -0.59 -0.5 0.0 -g 13 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.3 1.18 1.08 1.0 0.0 -h 13 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1.0 -1.07 -1.14 -1.2 0.0 -g 13 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.37 -0.31 -0.2 0.0 -h 13 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.1 -0.04 -0.07 -0.1 0.0 -g 13 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0.75 0.78 0.8 0.0 -h 13 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0.63 0.54 0.4 0.0 -g 13 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.4 -0.26 -0.18 -0.1 0.0 -h 13 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.21 0.10 -0.1 0.0 -g 13 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.35 0.38 0.3 0.0 -h 13 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.6 0.53 0.49 0.4 0.0 -g 13 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.1 -0.05 0.02 0.1 0.0 -h 13 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.38 0.44 0.5 0.0 -g 13 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4 0.41 0.42 0.5 0.0 -h 13 11 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.2 -0.22 -0.25 -0.3 0.0 -g 13 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0 -0.10 -0.26 -0.4 0.0 -h 13 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.5 -0.57 -0.53 -0.4 0.0 -g 13 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 -0.18 -0.26 -0.3 0.0 -h 13 13 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -0.9 -0.82 -0.79 -0.8 0.0 From 213375f9315e99c382d8350672276cf625a94ca0 Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 8 Mar 2018 20:34:52 -0600 Subject: [PATCH 005/101] AACGM v2.4 test update Updated test code for AACGM v 2.4 --- src/c_aacgm_v2/test_aacgm.c | 449 ++++++++++++++++++++---------------- 1 file changed, 251 insertions(+), 198 deletions(-) diff --git a/src/c_aacgm_v2/test_aacgm.c b/src/c_aacgm_v2/test_aacgm.c index b7705555..443be9bc 100644 --- a/src/c_aacgm_v2/test_aacgm.c +++ b/src/c_aacgm_v2/test_aacgm.c @@ -1,212 +1,265 @@ #include +#include #include "aacgmlib_v2.h" +#include "mlt_v2.h" + +#define CR printf("\n") +#define DEBUG 1 void next(void); +void line(char ch, int n); -int main(void) +int main(int argc, char *argv[]) { -double lat,lon,hgt; -double h; -double rtp[3]; -double mlat,mlon,r; -int err; -int year, month, day, hour, minute, second; - -printf("AACGM-v2 Test Program\n\n"); - -/* compute AACGM-v2 lat/lon with no time specified */ -printf("TEST: no date/time\n"); -lat = 45.5; -lon = -23.5; -hgt = 1135.; -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A); -if (err == 0) { - printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); - printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); - printf("\n\n"); -} -next(); - -printf("TEST: Setting time to : %04d%02d%02d %02d%02d:%02d\n", 1850,1,22,0,0,0); -AACGM_v2_SetDateTime(1850, 1, 22, 0,0,0); /* this should fail */ -next(); -printf("TEST: Setting time to : %04d%02d%02d %02d%02d:%02d\n", 1900,1,22,0,0,0); -AACGM_v2_SetDateTime(1900, 1, 22, 0,0,0); /* this is valid */ -next(); -printf("TEST: Setting time to : %04d%02d%02d %02d%02d:%02d\n", 2020,1,22,0,0,0); -AACGM_v2_SetDateTime(2020, 1, 22, 0,0,0); /* this shoudl fail */ -next(); -printf("TEST: Setting time to : %04d%02d%02d %02d%02d:%02d\n", 2019,1,22,0,0,0); -AACGM_v2_SetDateTime(2019, 1, 22, 0,0,0); /* this is valid */ -next(); - -year = 2014; -month = 3; -day = 22; -hour = 3; -minute = 11; -second = 0; -printf("TEST: Setting time to : %04d%02d%02d %02d%02d:%02d\n", - year, month, day, hour, minute, second); - -/* set date and time */ -AACGM_v2_SetDateTime(year, month, day, hour, minute, second); - -printf("TEST: geographic to AACGM-v2\n"); -/* compute AACGM lat/lon */ -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A); - -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -//printf("%lf %lf\n", fyear, fyear_old); -printf("\n\n"); -next(); - -printf("TEST: AACGM-v2 to geographic\n"); -/* do the inverse: A2G */ -err = AACGM_v2_Convert(mlat,mlon,hgt, &lat,&lon, &r, A2G); - -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -printf("\n\n"); -next(); - -/* compare tracing to coefficients */ -lat = 45.5; -lon = -23.5; -hgt = 150.; - -/* set date and time */ -AACGM_v2_SetDateTime(2018,1,1,0,0,0); - -printf("TEST: geographic to AACGM-v2; coefficients\n"); -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A); -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -next(); - -printf("TEST: geographic to AACGM-v2; tracing\n"); -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A|TRACE); -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -next(); - -printf("TEST: geographic to AACGM-v2; too high\n"); -hgt = 2500; -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A); -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -next(); - -printf("TEST: geographic to AACGM-v2; trace high\n"); -hgt = 7500; -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A|TRACE); -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -next(); - -printf("TEST: geographic to AACGM-v2; coefficient high\n"); -hgt = 7500; -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A|BADIDEA); -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -next(); - -printf("TEST: geographic to AACGM-v2; trace and back\n"); -hgt = 0; -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A|TRACE); -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -err = AACGM_v2_Convert(mlat,mlon,hgt, &lat,&lon, &r, A2G|TRACE); -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); - -/* the proper altitude in geocentric coordinates is given by: */ -geod2geoc(lat,lon,hgt, rtp); -h = (rtp[0]-1.d)*RE; -err = AACGM_v2_Convert(mlat,mlon,h, &lat,&lon, &r, A2G|TRACE); -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); - -next(); - -lat = 45.5; -lon = -23.5; - -printf("TEST: geographic to AACGM-v2; coeff and back\n"); -hgt = 0; -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A); -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -err = AACGM_v2_Convert(mlat,mlon,hgt, &lat,&lon, &r, A2G); -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -next(); - -return (0); - -/* pick a different year */ -year = 1997; -AACGM_v2_SetDateTime(year, month, day, hour, minute, second); - -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A); - -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -//printf("%lf %lf\n", fyear, fyear_old); -printf("\n\n"); - -/* pick a different lat/lon; should not need to do any interpolations */ -lat = 65.5; -lon = 93.5; -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A); - -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -//printf("%lf %lf\n", fyear, fyear_old); -printf("\n\n"); - -/* pick a different height; should only need to do height interpolation */ -hgt = 0.; -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A); - -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -printf("\n\n"); - -/* do another lat/lon; no interpolations */ -lat = 75.5; -lon = 73.5; -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A); - -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -printf("\n\n"); - -/* pick another year; should require loading new coeffs and both interps */ -year = 2004; -month = 3; -day = 22; -hour = 3; -minute = 11; -second = 0; - -AACGM_v2_SetDateTime(year, month, day, hour, minute, second); - -err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A); - -printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); -printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); -printf("\n\n"); + double lat,lon,hgt; + double h, mlt_c, mlt_t; + double rtp[3]; + double mlat,mlon,r; + int k, err, npts; + int yr, mo, dy, hr, mt, sc; + + /* Allow input of AACGM and IGRF database files instead of env variables */ + char root[1000], igrf_filename[1000]; + + if(argc > 1) + { + memcpy(root, argv[1], strlen(argv[1])+1); + root[strlen(argv[1])] = '\0'; + } + else root[0] = '\0'; + + if(argc > 2) + { + memcpy(igrf_filename, argv[2], strlen(argv[2])+1); + igrf_filename[strlen(argv[2])] = '\0'; + } + else igrf_filename[0] = '\0'; + /* End of reading optional input */ + + line('=',80); + printf("\nAACGM-v2 Test Program\n\n"); + line('=',80); + CR; + + /* compute AACGM-v2 lat/lon with no time specified */ + printf("TEST: no date/time (this will return an error.)\n"); + lat = 45.5; + lon = -23.5; + hgt = 1135.; + err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A, igrf_filename); + if (err == 0) + { + printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); + printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); + printf("\n\n"); + } + + #if (DEBUG > 1) + next(); + + printf("TEST: Setting time to : %04d%02d%02d %02d%02d:%02d (will fail)\n", + 1850,1,22,0,0,0); + AACGM_v2_SetDateTime(1850, 1, 22, 0,0,0); /* this should fail */ + printf("TEST: Setting time to : %04d%02d%02d %02d%02d:%02d\n", + 1900,1,22,0,0,0); + AACGM_v2_SetDateTime(1900, 1, 22, 0,0,0); /* this is valid */ + printf("TEST: Setting time to : %04d%02d%02d %02d%02d:%02d (will fail)\n", 2020,1,22,0,0,0); + AACGM_v2_SetDateTime(2020, 1, 22, 0,0,0); /* this shoudl fail */ + printf("TEST: Setting time to : %04d%02d%02d %02d%02d:%02d\n", + 2019,1,22,0,0,0); + AACGM_v2_SetDateTime(2019, 1, 22, 0,0,0); /* this is valid */ + next(); + #endif + + yr = 2014; + mo = 3; + dy = 22; + hr = 3; + mt = 11; + sc = 0; + printf("TEST: Setting time to : %04d%02d%02d %02d%02d:%02d\n", + yr, mo, dy, hr, mt, sc); + CR; + + /* set date and time */ + AACGM_v2_SetDateTime(yr, mo, dy, hr, mt, sc, root); + + lat = 45.5; + lon = -23.5; + hgt = 1135.; + + printf("TEST: geographic to AACGM-v2\n"); + /* compute AACGM lat/lon */ + err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A, igrf_filename); + + printf(" GLAT GLON HEIGHT MLAT MLON R\n"); + printf(" %lf %lf %lf %lf %lf %lf", lat,lon,hgt, mlat,mlon,r); + printf("\n\n"); + #if (DEBUG > 1) + next(); + #endif + + printf("TEST: AACGM-v2 to geographic\n"); + /* do the inverse: A2G */ + hgt = (r-1.)*RE; + err = AACGM_v2_Convert(mlat,mlon,hgt, &lat,&lon, &h, A2G, igrf_filename); + + printf(" MLAT MLON HEIGHT GLAT GLON "); + printf("HEIGHT\n %lf %lf %lf %lf %lf %lf", mlat,mlon,hgt, lat,lon,h); + printf("\n\n"); + #if (DEBUG > 1) + next(); + #endif + + /* same thing but using field-line tracing */ + lat = 45.5; + lon = -23.5; + hgt = 1135.; + + printf("Do the same thing but use field-line tracing\n\n"); + printf("TEST: geographic to AACGM-v2 (TRACE)\n"); + /* compute AACGM lat/lon */ + err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A|TRACE,igrf_filename); + + printf(" GLAT GLON HEIGHT MLAT MLON R\n"); + printf(" %lf %lf %lf %lf %lf %lf", lat,lon,hgt, mlat,mlon,r); + printf("\n\n"); + #if (DEBUG > 1) + next(); + #endif + + printf("TEST: AACGM-v2 to geographic (TRACE)\n"); + /* do the inverse: A2G */ + hgt = (r-1.)*RE; + err = AACGM_v2_Convert(mlat,mlon,hgt, &lat,&lon, &h, A2G|TRACE,igrf_filename); + + printf(" MLAT MLON HEIGHT GLAT GLON HEIGHT\n"); + printf(" %lf %lf %lf %lf %lf %lf", mlat,mlon,hgt, lat,lon,h); + printf("\n\n"); + #if (DEBUG > 1) + next(); + #endif + + /* compare tracing to coefficients */ + lat = 45.5; + lon = -23.5; + hgt = 150.; + + /* set date and time */ + AACGM_v2_SetDateTime(2018,1,1,0,0,0,root); + + #if (DEBUG >1) + printf("TEST: geographic to AACGM-v2; coefficients\n"); + err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A, igrf_filename); + printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); + printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); + next(); + #endif + + #if (DEBUG > 1) + printf("TEST: geographic to AACGM-v2; tracing\n"); + err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A|TRACE,igrf_filename); + printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); + printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); + next(); + #endif + + #if (DEBUG > 1) + printf("TEST: geographic to AACGM-v2; too high\n"); + hgt = 2500; + err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A, igrf_filename); + printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); + printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); + next(); + #endif + + #if (DEBUG > 1) + printf("TEST: geographic to AACGM-v2; trace high\n"); + hgt = 7500; + err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A|TRACE,igrf_filename); + printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); + printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); + next(); + #endif + + #if (DEBUG > 1) + printf("TEST: geographic to AACGM-v2; coefficient high\n"); + hgt = 7500; + err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A|BADIDEA, + igrf_filename); + printf("lat = %lf, lon = %lf, height = %lf\n", lat,lon,hgt); + printf("mlat = %lf, mlon = %lf, r = %lf\n", mlat,mlon,r); + next(); + #endif + + line('-',80); + CR; + printf("Testing MLT\n"); + line('-',80); + CR; + + lat = 77.; + lat = 37.; + lon = -88.; + hgt = 300.; + + yr = 2003; + mo = 5; + dy = 17; + hr = 7; + mt = 53; + sc = 16; + + /* compute AACGM lat/lon */ + AACGM_v2_SetDateTime(yr, mo, dy, hr, mt, sc, root); + err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A|TRACE,igrf_filename); + mlt_t = MLTConvertYMDHMS_v2(yr,mo,dy,hr,mt,sc,mlon,root,igrf_filename); + printf(" GLAT GLON HEIGHT MLAT MLON MLT\n"); + printf("TRACE %lf %lf %lf %lf %lf %lf", lat,lon,hgt, mlat,mlon,mlt_t); + printf("\n"); + + err = AACGM_v2_Convert(lat,lon,hgt, &mlat,&mlon, &r, G2A, igrf_filename); + mlt_c = MLTConvertYMDHMS_v2(yr,mo,dy,hr,mt,sc,mlon,root,igrf_filename); + printf("COEFF %lf %lf %lf %lf %lf %lf", lat,lon,hgt, mlat,mlon,mlt_c); + printf("\n\n"); + + npts = 20; + printf("\n"); + printf("Array:\n"); + for (k=0; k Date: Thu, 8 Mar 2018 21:57:13 -0600 Subject: [PATCH 006/101] Updated function calls and parameters Updated function calls and parameters to use native MLT function, and remove dependence on environmental variables --- src/aacgmv2/__init__.py | 99 +++-- src/aacgmv2/__main__.py | 114 ++++-- src/aacgmv2/aacgmv2module.c | 420 ++++++++++++++------ src/aacgmv2/wrapper.py | 763 +++++++++++++++++++----------------- 4 files changed, 853 insertions(+), 543 deletions(-) diff --git a/src/aacgmv2/__init__.py b/src/aacgmv2/__init__.py index 80b4fb81..5f3d35ec 100644 --- a/src/aacgmv2/__init__.py +++ b/src/aacgmv2/__init__.py @@ -1,29 +1,70 @@ -import os as _os - -__version__ = "2.0.0" - -# path and filename prefix for the IGRF coefficients -AACGM_v2_DAT_PREFIX = _os.path.join(_os.path.realpath(_os.path.dirname(__file__)), 'aacgm_coeffs', 'aacgm_coeffs-12-') -IGRF_12_COEFFS = _os.path.join(_os.path.realpath(_os.path.dirname(__file__)), 'igrf12coeffs.txt') - - -def set_coeff_path(): - '''Sets the environment variables ``AACGM_v2_DAT_PREFIX`` and - ``IGRF_12_COEFFS`` (for the current process). These are required for the - C library to function correctly. This function is automatically called - when importing aacgmv2. You may need to call this manually if you use - multithreading or spawn child processes (untested). - ''' - _os.environ['AACGM_v2_DAT_PREFIX'] = AACGM_v2_DAT_PREFIX - _os.environ['IGRF_12_COEFFS'] = IGRF_12_COEFFS - -set_coeff_path() - - -# NOTE: it is important that we import _aacgmv2 AFTER setting the -# environment variables above, otherwise it doesn't seem to inherit them -from aacgmv2 import _aacgmv2 # noqa: E402 - -from aacgmv2.wrapper import convert, convert_mlt, subsol # noqa: E402 - -__all__ = ['_aacgmv2', 'convert', 'convert_mlt', 'subsol', 'set_coeff_path', 'AACGM_v2_DAT_PREFIX', 'IGRF_12_COEFFS'] +# -*- coding: utf-8 -*- +"""aacgmv2 + +Parameters +----------- +AACGM_v2_DAT_PREFIX +IGRF_12_COEFFS +G2A +A2G +TRACE +ALLOWTRACE +BADIDEA +GEOCENTRIC + +Functions +------------ +convert_latlon +convert_latlon_arr +convert_str_to_bit +convert_bool_to_bit +get_aacgm_coord +get_aacgm_coord_arr +convert +set_datetime +mlt_convert +mlt_convert_yrsec +inv_mlt_convert +inv_mlt_convert_yrsec +""" +import os.path as _path +import logging +__version__ = "2.0.1" + +# path and filename prefix for the IGRF coefficients +AACGM_v2_DAT_PREFIX = _path.join(_path.realpath(_path.dirname(__file__)), + 'aacgm_coeffs', 'aacgm_coeffs-12-') +IGRF_12_COEFFS = _path.join(_path.realpath(_path.dirname(__file__)), + 'igrf12coeffs.txt') + +try: + from wrapper import convert_latlon, convert_str_to_bit, get_aacgm_coord + from wrapper import convert_latlon_arr, get_aacgm_coord_arr + from wrapper import convert_bool_to_bit +except Exception, e: + logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) + +try: + from aacgmv2 import convert +except Exception, e: + logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) + +try: + from aacgmv2 import set_datetime +except Exception, e: + logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) + +try: + from aacgmv2 import mlt_convert, inv_mlt_convert +except Exception, e: + logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) + +try: + from aacgmv2 import mlt_convert_yrsec, inv_mlt_convert_yrsec +except Exception, e: + logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) + +try: + from aacgmv2 import G2A, A2G, TRACE, ALLOWTRACE, BADIDEA, GEOCENTRIC +except Exception, e: + logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) diff --git a/src/aacgmv2/__main__.py b/src/aacgmv2/__main__.py index 633265b5..6d3cf857 100644 --- a/src/aacgmv2/__main__.py +++ b/src/aacgmv2/__main__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -'''Executed when aacgmv2 is invoked with python -m aacgmv2''' +"""Executed when aacgmv2 is invoked with python -m aacgmv2""" from __future__ import division, print_function, absolute_import @@ -24,54 +24,90 @@ def main(): - '''Entry point for the script''' - - parser = argparse.ArgumentParser(description='Converts between geographical coordinates, AACGM-v2, and MLT') - - subparsers = parser.add_subparsers(title='Subcommands', prog='aacgmv2', dest='subcommand', - description='for help, run %(prog)s SUBCOMMAND -h') + """Entry point for the script""" + + desc = 'Converts between geographical coordinates, AACGM-v2, and MLT' + parser = argparse.ArgumentParser(description=desc) + + desc = 'for help, run %(prog)s SUBCOMMAND -h' + subparsers = parser.add_subparsers(title='Subcommands', prog='aacgmv2', + dest='subcommand', description=desc) subparsers.required = True - parser_convert = subparsers.add_parser('convert', help=('convert to/from geomagnetic coordinates. Input file must ' - 'have lines of the form "LAT LON ALT".')) - parser_convert_mlt = subparsers.add_parser('convert_mlt', help=('convert between magnetic local time (MLT) and ' - 'AACGM-v2 longitude. Input file must have a single ' - 'number on each line.')) - + + desc = 'convert to/from geomagnetic coordinates. Input file must have lines' + desc += 'of the form "LAT LON ALT".' + parser_convert = subparsers.add_parser('convert', help=(desc)) + + desc = 'convert between magnetic local time (MLT) and AACGM-v2 longitude. ' + desc += 'Input file must have a single number on each line.' + parser_convert_mlt = subparsers.add_parser('mlt_convert', help=(desc)) + + desc = 'input file (stdin if none specified)' for p in [parser_convert, parser_convert_mlt]: - p.add_argument('-i', '--input', dest='file_in', metavar='FILE_IN', type=argparse.FileType('r'), - default=STDIN, help='input file (stdin if none specified)') - p.add_argument('-o', '--output', dest='file_out', metavar='FILE_OUT', type=argparse.FileType('wb'), - default=STDOUT, help='output file (stdout if none specified)') + p.add_argument('-i', '--input', dest='file_in', metavar='FILE_IN', + type=argparse.FileType('r'), default=STDIN, help=desc) + p.add_argument('-o', '--output', dest='file_out', metavar='FILE_OUT', + type=argparse.FileType('wb'), default=STDOUT, + help='output file (stdout if none specified)') + desc = 'date for magnetic field model (1900-2020, default: today)' parser_convert.add_argument('-d', '--date', dest='date', metavar='YYYYMMDD', - help='date for magnetic field model (1900-2020, default: today)') - parser_convert.add_argument('-v', '--a2g', dest='a2g', action='store_true', default=False, - help='invert - convert AACGM to geographic instead of geographic to AACGM') - parser_convert.add_argument('-t', '--trace', dest='trace', action='store_true', default=False, - help='use field-line tracing instead of coefficients') - parser_convert.add_argument('-a', '--allowtrace', dest='allowtrace', action='store_true', default=False, - help='automatically use field-line tracing above 2000 km') - parser_convert.add_argument('-b', '--badidea', dest='badidea', action='store_true', default=False, - help='allow use of coefficients above 2000 km (bad idea!)') - parser_convert.add_argument('-g', '--geocentric', dest='geocentric', action='store_true', default=False, - help='assume inputs are geocentric with Earth radius 6371.2 km') - - parser_convert_mlt.add_argument('datetime', metavar='YYYYMMDDHHMMSS', help='date and time for conversion') - parser_convert_mlt.add_argument('-v', '--m2a', dest='m2a', action='store_true', default=False, - help='invert - convert MLT to AACGM longitude instead of AACGM longitude to MLT') + help=desc) - args = parser.parse_args() + desc = 'invert - convert AACGM to geographic instead of geographic to AACGM' + parser_convert.add_argument('-v', '--a2g', dest='a2g', action='store_true', + default=False, help=desc) + + desc = 'use field-line tracing instead of coefficients' + parser_convert.add_argument('-t', '--trace', dest='trace', + action='store_true', default=False, help=desc) + + desc = 'automatically use field-line tracing above 2000 km' + parser_convert.add_argument('-a', '--allowtrace', dest='allowtrace', + action='store_true', default=False, help=desc) + desc = 'allow use of coefficients above 2000 km (bad idea!)' + parser_convert.add_argument('-b', '--badidea', dest='badidea', + action='store_true', default=False, help=desc) + + desc = 'assume inputs are geocentric with Earth radius 6371.2 km' + parser_convert.add_argument('-g', '--geocentric', dest='geocentric', + action='store_true', default=False, help=desc) + + parser_convert_mlt.add_argument('datetime', metavar='YYYYMMDDHHMMSS', + help='date and time for conversion') + + desc = 'invert - convert MLT to AACGM longitude instead of AACGM longitude' + desc += ' to MLT' + parser_convert_mlt.add_argument('-v', '--m2a', dest='m2a', + action='store_true', default=False, + help=desc) + + args = parser.parse_args() array = np.loadtxt(args.file_in, ndmin=2) if args.subcommand == 'convert': - date = dt.date.today() if args.date is None else dt.datetime.strptime(args.date, '%Y%m%d') - lats, lons = aacgmv2.convert(array[:, 0], array[:, 1], array[:, 2], date=date, a2g=args.a2g, trace=args.trace, - allowtrace=args.allowtrace, badidea=args.badidea, geocentric=args.geocentric) + date = dt.date.today() if args.date is None else \ + dt.datetime.strptime(args.date, '%Y%m%d') + code = aacgmv2.convert_bool_to_bit(a2g=args.a2g, trace=args.trace, + allowtrace=args.allowtrace, + badidea=args.badidea, + geocentric=args.geocentric) + lats, lons = aacgmv2.convert_latlon_arr(array[:,0], array[:,1], + array[:,2], dtime=date, + code=code) np.savetxt(args.file_out, np.column_stack((lats, lons)), fmt='%.8f') - elif args.subcommand == 'convert_mlt': - datetime = dt.datetime.strptime(args.datetime, '%Y%m%d%H%M%S') - out = aacgmv2.convert_mlt(array, datetime, m2a=args.m2a) + elif args.subcommand == 'mlt_convert': + dtime = dt.datetime.strptime(args.datetime, '%Y%m%d%H%M%S') + if args.m2a: + out = aacgmv2.inv_mlt_convert(dtime.year, dtime.month, dtime.day, + dtime.hour, dtime.minute, + dtime.second, array[:,1], + aacgmv2.IGRF_12_COEFFS) + else: + out = aacgmv2.mlt_convert(dtime.year, dtime.month, dtime.day, + dtime.hour, dtime.minute, dtime.second, + array[:,1], aacgmv2.IGRF_12_COEFFS) np.savetxt(args.file_out, out, fmt='%.8f') diff --git a/src/aacgmv2/aacgmv2module.c b/src/aacgmv2/aacgmv2module.c index 25e0894f..beccfa44 100644 --- a/src/aacgmv2/aacgmv2module.c +++ b/src/aacgmv2/aacgmv2module.c @@ -1,155 +1,351 @@ -#include "Python.h" #include + +/***************************************************************************** + * Author: Angeline G. Burrell, UTDallas, April 2017 + * + * Comments: python wrapper to AACGM functions based on test_aacgm.c + * Based on wrapper built by C. Meeren + * Originally written for DaViTpy and adapted to update AACGMV2 + * + * References: Shepherd, S. G. (2014), Altitude‐adjusted corrected geomagnetic + * coordinates: Definition and functional approximations, Journal + * of Geophysical Research: Space Physics, 119, p 7501-7521, + * doi:10.1002/2014JA020264 + *****************************************************************************/ + +#include + #include "aacgmlib_v2.h" +#include "mlt_v2.h" PyObject *module; - -static PyObject * -aacgmv2_setDateTime(PyObject *self, PyObject *args) +static PyObject *aacgm_v2_setdatetime(PyObject *self, PyObject *args) { - int year, month, day, hour, minute, second; - int err; + int year, month, day, hour, minute, second, err; - if (!PyArg_ParseTuple(args, "iiiiii", &year, &month, &day, &hour, &minute, &second)) { - return NULL; + char *root; + + /* Parse the input as a tupple */ + if(!PyArg_ParseTuple(args, "iiiiiis", &year, &month, &day, &hour, &minute, + &second, &root)) + return(NULL); + + /* Call the AACGM routine */ + err = AACGM_v2_SetDateTime(year, month, day, hour, minute, second, root); + + if(err < 0) + { + PyErr_Format(PyExc_RuntimeError, + "AACGM_v2_SetDateTime returned error code %d", err); + return(NULL); } - err = AACGM_v2_SetDateTime(year, month, day, hour, minute, second); + Py_RETURN_NONE; +} - if (err < 0) { - PyErr_Format(PyExc_RuntimeError, "AACGM_v2_SetDateTime returned error code %d", err); - return NULL; +static PyObject *aacgm_v2_convert(PyObject *self, PyObject *args) +{ + int code, err; + + double in_lat, in_lon, in_h, out_lat, out_lon, out_r; + + char *igrf_file; + + /* Parse the input as a tupple */ + if(!PyArg_ParseTuple(args, "dddis", &in_lat, &in_lon, &in_h, &code, + &igrf_file)) + return(NULL); + + /* Call the AACGM routine */ + err = AACGM_v2_Convert(in_lat, in_lon, in_h, &out_lat, &out_lon, &out_r, + code, igrf_file); + + if(err < 0) + { + PyErr_Format(PyExc_RuntimeError, + "AACGM_v2_Convert returned error code %d", err); + return(NULL); } - Py_RETURN_NONE; + return Py_BuildValue("ddd", out_lat, out_lon, out_r); } +static PyObject *mltconvert_v2(PyObject *self, PyObject *args) +{ + int yr, mo, dy, hr, mt, sc; + + double mlon, mlt; -static PyObject * -aacgmv2_aacgmConvert(PyObject *self, PyObject *args) + char *igrf_file, *root; + + /* Parse the input as a tupple */ + if(!PyArg_ParseTuple(args, "iiiiiidss", &yr, &mo, &dy, &hr, &mt, &sc, &mlon, + &root, &igrf_file)) + return(NULL); + + /* Call the AACGM routine */ + mlt = MLTConvert_v2(yr, mo, dy, hr, mt, sc, mlon, root, igrf_file); + + return Py_BuildValue("d", mlt); +} + +static PyObject *mltconvert_yrsec_v2(PyObject *self, PyObject *args) { - double in_lat, in_lon, height; - double out_lat, out_lon, r; - int code; - int err; + int yr, yr_sec; - if (!PyArg_ParseTuple(args, "dddi", &in_lat, &in_lon, &height, &code)) { - return NULL; - } + double mlon, mlt; - err = AACGM_v2_Convert(in_lat, in_lon, height, &out_lat, &out_lon, &r, code); - if (err < 0) { - PyErr_Format(PyExc_RuntimeError, "AACGM_v2_Convert returned error code %d", err); - return NULL; - } + char *igrf_file, *root; + + /* Parse the input as a tupple */ + if(!PyArg_ParseTuple(args, "iidss", &yr, &yr_sec, &mlon, &root, &igrf_file)) + return(NULL); + + /* Call the AACGM routine */ + mlt = MLTConvertYrsec_v2(yr, yr_sec, mlon, root, igrf_file); - return Py_BuildValue("ddd", out_lat, out_lon, r); + return Py_BuildValue("d", mlt); } +static PyObject *inv_mltconvert_v2(PyObject *self, PyObject *args) +{ + int yr, mo, dy, hr, mt, sc; + + double mlon, mlt; + + char *igrf_file; + + /* Parse the input as a tupple */ + if(!PyArg_ParseTuple(args, "iiiiiids", &yr, &mo, &dy, &hr, &mt, &sc, &mlt, + &igrf_file)) + return(NULL); + + /* Call the AACGM routine */ + mlon = inv_MLTConvert_v2(yr, mo, dy, hr, mt, sc, mlt, igrf_file); + + return Py_BuildValue("d", mlon); +} + +static PyObject *inv_mltconvert_yrsec_v2(PyObject *self, PyObject *args) +{ + int yr, yr_sec; -static PyMethodDef aacgmv2Methods[] = { - { "setDateTime" , aacgmv2_setDateTime , METH_VARARGS, "setDateTime(year, month, day, hour, minute, second)\n\ + double mlon, mlt; + + char *igrf_file; + + /* Parse the input as a tupple */ + if(!PyArg_ParseTuple(args, "iidss", &yr, &yr_sec, &mlt, &igrf_file)) + return(NULL); + + /* Call the AACGM routine */ + mlon = inv_MLTConvertYrsec_v2(yr, yr_sec, mlt, igrf_file); + + return Py_BuildValue("d", mlon); +} + +static PyMethodDef aacgm_v2_methods[] = { + { "set_datetime", aacgm_v2_setdatetime, METH_VARARGS, + "set_datetime(year, month, day, hour, minute, second, root)\n\ \n\ -Sets the date and time for the IGRF magnetic field. \n\ +Sets the date and time for the IGRF magnetic field.\n\ +\n\ +Parameters \n\ +-------------\n\ +year : (int)\n\ + Four digit year starting from 1900, ending 2020\n\ +month : (int)\n\ + Month of year ranging from 1-12\n\ +day : (int)\n\ + Day of month (1-31)\n\ +hour : (int)\n\ + Hour of day (0-23)\n\ +minute : (int)\n\ + Minute of hour (0-59)\n\ +second : (int)\n\ + Seconds of minute (0-59)\n\ +root : (str)\n\ + AACGM coefficient filename root\n\ + (e.g. /davitpydir/tables/aacgm/aacgm_coeffs-12-)\n\ +\n\ +Returns\n\ +-------------\n\ +Void\n" }, + { "convert", aacgm_v2_convert, METH_VARARGS, + "convert(in_lat, in_lon, height, code, igrf_file)\n\ +\n\ +Converts between geographic/dedic and magnetic coordinates.\n\ \n\ Parameters\n\ -==========\n\ -year : int [1900, 2020)\n\ - ..\n\ -month : int [1, 12]\n\ - ..\n\ -day : int [1, 31]\n\ - ..\n\ -hour : int [0, 24]\n\ - ..\n\ -minute : int [0, 60]\n\ - ..\n\ -second : int [0, 60]\n\ - .." }, - { "aacgmConvert", aacgmv2_aacgmConvert, METH_VARARGS, "aacgmConvert(in_lat, in_lon, height, code) -> out_lat, out_lon, r\n\ -\n\ -Converts between geographic and magnetic coordinates.\n\ +-------------\n\ +in_lat : (float)\n\ + Input latitude in degrees N (code specifies type of latitude)\n\ +in_lon : (float)\n\ + Input longitude in degrees E (code specifies type of longitude)\n\ +height : (float)\n\ + Altitude above the surface of the earth in km\n\ +code : (int) \n\ + Bitwise code for passing options into converter (default=0)\n\ + 0 - G2A - geographic (geodetic) to AACGM-v2 \n\ + 1 - A2G - AACGM-v2 to geographic (geodetic) \n\ + 2 - TRACE - use field-line tracing, not coefficients\n\ + 4 - ALLOWTRACE - use trace only above 2000 km\n\ + 8 - BADIDEA - use coefficients above 2000 km\n\ + 16 - GEOCENTRIC - assume inputs are geocentric w/ RE=6371.2\n\ +igrf_file : (str)\n\ + Full filename of IGRF coefficient file\n\ +\n\ +Returns \n\ +-------\n\ +out_lat : (float)\n\ + Output latitude in degrees\n\ +out_lon : (float)\n\ + Output longitude in degrees\n\ +out_r : (float)\n\ + Geocentric radial distance in Re\n", }, + {"mlt_convert", mltconvert_v2, METH_VARARGS, + "mlt_convert(yr, mo, dy, hr, mt, sc, mlon, root, igrf_file)\n\ +\n\ +Converts from universal time to magnetic local time.\n\ \n\ Parameters\n\ -----------\n\ -in_lat : float [-90, 90]\n\ - Input latitude \n\ -in_lon : float [-180, 180]\n\ - Input longitude\n\ -height : float\n\ - Input altitude\n\ -code : int\n\ - Bitwise code for passing options into converter. The codes and their names (defined in this module) are given in the table below.\n\ +-------------\n\ +yr : (int)\n\ + 4 digit integer year (1900-2020)\n\ +mo : (int)\n\ + Month of year (1-12)\n\ +dy : (int)\n\ + Day of month (1-31)\n\ +hr : (int)\n\ + hours of day (0-23)\n\ +mt : (int)\n\ + Minutes of hour (0-59)\n\ +sc : (int)\n\ + Seconds of minute (0-59)\n\ +mlon : (float)\n\ + Magnetic longitude\n\ +root : (str)\n\ + Root of the AACGM coefficient files\n\ + (e.g. /davitpydir/tables/aacgm/aacgm_coeffs-12-)\n\ +igrf_file : (str)\n\ + Full filename of IGRF coefficient file\n\ \n\ -Returns\n\ -=======\n\ -out_lat : float\n\ - Converted latitude\n\ -out_lon : float\n\ - Converted longitude\n\ -r : float\n\ - Not used, always 1.0\n\ -\n\ -Notes\n\ -=====\n\ -The bitwise codes are:\n\ -\n\ -====== ============ =============\n\ - Code Name Description\n\ -====== ============ =============\n\ - 0 G2A Convert geographic to AACGM-v2.\n\ - 1 A2G Convert AACGM-v2 to geographic.\n\ - 2 TRACE Use field-line tracing instead of coefficients. More precise, but significantly slower.\n\ - 4 ALLOWTRACE Automatically use field-line tracing above 2000 km. If not set, cause exception to be thrown for these altitudes unless TRACE or BADIDEA is set.\n\ - 8 BADIDEA Allow use of coefficients above 2000 km (bad idea!)\n\ - 16 GEOCENTRIC Assume inputs are geocentric with Earth radius 6371.2 km.\n\ -====== ============ =============\n\ - \n\ -For example, to convert from AACGM-v2 to geographpic using field-line tracing, use either of the following:\n\ -\n\ - >>> aacgmConvert(in_lat, in_lon, height, A2G | TRACE)\n\ - >>> aacgmConvert(in_lat, in_lon, height, 1 | 2)\n\ - >>> aacgmConvert(in_lat, in_lon, height, 3)" }, - { NULL, NULL, 0, NULL } +Returns \n\ +-------\n\ +mlt : (float)\n\ + Magnetic local time (hours)\n" }, + + {"mlt_convert_yrsec", mltconvert_yrsec_v2, METH_VARARGS, + "mlt_convert_yrsec(yr, yr_sec, mlon, root, igrf_file)\n\ +\n\ +Converts from universal time to magnetic local time.\n\ +\n\ +Parameters\n\ +-------------\n\ +yr : (int)\n\ + 4 digit integer year (1900-2020)\n\ +yr_sec : (int)\n\ + Seconds of year (0-31622400)\n\ +mlon : (float)\n\ + Magnetic longitude\n\ +root : (str)\n\ + Root of the AACGM coefficient files\n\ + (e.g. /davitpydir/tables/aacgm/aacgm_coeffs-12-)\n\ +igrf_file : (str)\n\ + Full filename of IGRF coefficient file\n\ +\n\ +Returns \n\ +-------\n\ +mlt : (float)\n\ + Magnetic local time (hours)\n" }, + {"inv_mlt_convert", inv_mltconvert_v2, METH_VARARGS, + "inv_mlt_convert(yr, mo, dy, hr, mt, sc, mlt, igrf_file)\n\ +\n\ +Converts from universal time and magnetic local time to magnetic longitude.\n\ +\n\ +Parameters\n\ +-------------\n\ +yr : (int)\n\ + 4 digit integer year (1900-2020)\n\ +mo : (int)\n\ + Month of year (1-12)\n\ +dy : (int)\n\ + Day of month (1-31)\n\ +hr : (int)\n\ + hours of day (0-23)\n\ +mt : (int)\n\ + Minutes of hour (0-59)\n\ +sc : (int)\n\ + Seconds of minute (0-59)\n\ +mlt : (float)\n\ + Magnetic local time\n\ +igrf_file : (str)\n\ + Full filename of IGRF coefficient file\n\ +\n\ +Returns \n\ +-------\n\ +mlon : (float)\n\ + Magnetic longitude (degrees)\n" }, + + {"inv_mlt_convert_yrsec", inv_mltconvert_yrsec_v2, METH_VARARGS, + "inv_mlt_convert_yrsec(yr, yr_sec, mlt, igrf_file)\n\ +\n\ +Converts from universal time and magnetic local time to magnetic longitude.\n\ +\n\ +Parameters\n\ +-------------\n\ +yr : (int)\n\ + 4 digit integer year (1900-2020)\n\ +yr_sec : (int)\n\ + Seconds of year (0-31622400)\n\ +mlt : (float)\n\ + Magnetic local time\n\ +igrf_file : (str)\n\ + Full filename of IGRF coefficient file\n\ +\n\ +Returns \n\ +-------\n\ +mlon : (float)\n\ + Magnetic longitude (degrees)\n" }, + { NULL, NULL, 0, NULL } }; +/* Different versions of python require different constant declarations */ #if PY_MAJOR_VERSION >= 3 - - static struct PyModuleDef aacgmv2module = { - PyModuleDef_HEAD_INIT, - "_aacgmv2", /* name of module */ - "This module contains the interface to the AACGM-v2 C library.", /* module documentation, may be NULL */ - -1, /* size of per-interpreter state of the module, - or -1 if the module keeps state in global variables. */ - aacgmv2Methods + static struct PyModuleDef aacgm_module = { + PyModuleDef_HEAD_INIT, + "aacgmv2", /* name of module */ + "Interface to the AACGM-v2 C library.", /* module documentation */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + aacgm_v2_methods }; - - PyMODINIT_FUNC PyInit__aacgmv2(void) + PyMODINIT_FUNC PyInit_aacgm(void) { - module = PyModule_Create(&aacgmv2module); - PyModule_AddIntConstant(module, "G2A", G2A); - PyModule_AddIntConstant(module, "A2G", A2G); - PyModule_AddIntConstant(module, "TRACE", TRACE); - PyModule_AddIntConstant(module, "ALLOWTRACE", ALLOWTRACE); - PyModule_AddIntConstant(module, "BADIDEA", BADIDEA); - PyModule_AddIntConstant(module, "GEOCENTRIC", GEOCENTRIC); - return module; + module = PyModule_Create(&aacgm_module); + PyModule_AddIntConstant(module, "G2A", G2A); + PyModule_AddIntConstant(module, "A2G", A2G); + PyModule_AddIntConstant(module, "TRACE", TRACE); + PyModule_AddIntConstant(module, "ALLOWTRACE", ALLOWTRACE); + PyModule_AddIntConstant(module, "BADIDEA", BADIDEA); + PyModule_AddIntConstant(module, "GEOCENTRIC", GEOCENTRIC); + return module; } #else - PyMODINIT_FUNC init_aacgmv2(void) + PyMODINIT_FUNC initaacgm(void) { - module = Py_InitModule("_aacgmv2", aacgmv2Methods); - PyModule_AddIntConstant(module, "G2A", G2A); - PyModule_AddIntConstant(module, "A2G", A2G); - PyModule_AddIntConstant(module, "TRACE", TRACE); - PyModule_AddIntConstant(module, "ALLOWTRACE", ALLOWTRACE); - PyModule_AddIntConstant(module, "BADIDEA", BADIDEA); - PyModule_AddIntConstant(module, "GEOCENTRIC", GEOCENTRIC); + module = Py_InitModule("aacgm", aacgm_v2_methods); + PyModule_AddIntConstant(module, "G2A", G2A); + PyModule_AddIntConstant(module, "A2G", A2G); + PyModule_AddIntConstant(module, "TRACE", TRACE); + PyModule_AddIntConstant(module, "ALLOWTRACE", ALLOWTRACE); + PyModule_AddIntConstant(module, "BADIDEA", BADIDEA); + PyModule_AddIntConstant(module, "GEOCENTRIC", GEOCENTRIC); } #endif diff --git a/src/aacgmv2/wrapper.py b/src/aacgmv2/wrapper.py index e854c0cb..e1ccfca2 100644 --- a/src/aacgmv2/wrapper.py +++ b/src/aacgmv2/wrapper.py @@ -1,400 +1,437 @@ # -*- coding: utf-8 -*- -'''This module provides a user-friendly pythonic wrapper for the low-level C interface functions.''' - -from __future__ import division, print_function, absolute_import, unicode_literals - -import datetime as dt -import calendar -import warnings -import os as _os - +"""Pythonic wrappers for AACGM-V2 C functions. + +Functions +-------------- +convert_latlon : Converts scalar location +convert_latlon_arr : Converts array location +get_aacgm_coord : Get scalar magnetic lat, lon, mlt from geographic location +get_aacgm_coord_arr : Get array magnetic lat, lon, mlt from geographic location +convert_str_to_bit : Convert human readible AACGM flag to bits +convert_bool_to_bit : Convert boolian flags to bits +-------------- +""" + +from __future__ import division, absolute_import, unicode_literals import numpy as np +import datetime as dt +import logging +import aacgmv2 -from aacgmv2._aacgmv2 import A2G, TRACE, BADIDEA, ALLOWTRACE, GEOCENTRIC, setDateTime, aacgmConvert - -IGRF_12_COEFFS = _os.path.join(_os.path.realpath(_os.path.dirname(__file__)), 'igrf12coeffs.txt') - -aacgmConvert_vectorized = np.vectorize(aacgmConvert) - - -def convert(lat, lon, alt, date=None, a2g=False, trace=False, allowtrace=False, badidea=False, geocentric=False): - '''Converts to/from geomagnetic coordinates. - - This is a user-friendly pythonic wrapper for the low-level C interface - functions available in :mod:`aacgmv2._aacgmv2`. +def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, + coeff_prefix=None): + """Converts between geomagnetic coordinates and AACGM coordinates Parameters - ========== - lat,lon,alt : array_like - Input latitude(s), longitude(s) and altitude(s). They must be - `broadcastable to the same shape `_. - date : :class:`datetime.date`/:class:`datetime.datetime`, optional - The date/time to use for the magnetic field model, default ``None`` (uses - current time). Must be between 1900 and 2020. - a2g : bool, optional - Convert from AACGM-v2 to geographic coordinates, default ``False`` - (converts geographic to AACGM-v2). - trace : bool, optional - Use field-line tracing, default ``False`` (uses coefficients). Tracing - is more precise and needed at altitudes > 2000 km, but significantly - slower. - allowtrace : bool, optional - Automatically use field-line tracing above 2000 km, default ``False`` - (raises an exception for these altitudes unless ``trace=True`` or - ``badidea=True``). - badidea : bool, optional - Allow use of coefficients above 2000 km (bad idea!) - geocentric : bool, optional - Assume inputs are geocentric with Earth radius 6371.2 km. + ------------ + in_lat : (float) + Input latitude in degrees N (code specifies type of latitude) + in_lon : (float) + Input longitude in degrees E (code specifies type of longitude) + height : (float) + Altitude above the surface of the earth in km + dtime : (datetime) + Datetime for magnetic field + code : (str or int) + Bit code or string denoting which type(s) of conversion to perform + G2A - geographic (geodetic) to AACGM-v2 + A2G - AACGM-v2 to geographic (geodetic) + TRACE - use field-line tracing, not coefficients + ALLOWTRACE - use trace only above 2000 km + BADIDEA - use coefficients above 2000 km + GEOCENTRIC - assume inputs are geocentric w/ RE=6371.2 + (default is "G2A") + igrf_file : (str or NoneType) + Full filename of IGRF coefficient file or None to use + aacgmv2.IGRF_12_COEFFS. (default=None) + coeff_prefix : (str or NoneType) + Location and file prefix for aacgm coefficient files or None to use + aacgmv2.AACGM_v2_DAT_PREFIX. (default=None) Returns - ======= - - lat_out : ``numpy.ndarray`` - Converted latitude - lon_out : ``numpy.ndarray`` - Converted longitude - - Raises - ====== - - ValueError - if max(alt) > 2000 and neither of `trace`, `allowtrace`, or `badidea` is ``True`` - ValueError - if latitude is outside the range -90 to +90 degrees - RuntimeError - if there was a problem in the C extension - - Notes - ===== - - This function exclusively relies on the `AACGM-v2 C library - `_. Specifically, - it calls the functions :func:`_aacgmv2.setDateTime` and - :func:`_aacgmv2.aacgmConvert`, which are simple interfaces to the - C library functions :func:`AACGM_v2_SetDateTime` and - :func:`AACGM_v2_Convert`. Details of the techniques used to derive the - AACGM-v2 coefficients are described by Shepherd, 2014 [1]_. - - .. [1] Shepherd, S. G. (2014), Altitude-adjusted corrected geomagnetic - coordinates: Definition and functional approximations, - J. Geophys. Res. Space Physics, 119, 7501--7521, - doi:`10.1002/2014JA020264 `_. - - ''' - - # check values - if np.min(alt) < 0: - warnings.warn('Coordinate transformations are not intended for altitudes < 0 km', UserWarning) + ------- + out_lat : (float) + Output latitude in degrees + out_lon : (float) + Output longitude in degrees + out_r : (float) + Geocentric radial distance in R + """ + # Define coefficient file prefix if not supplied + if coeff_prefix is None: + coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX + + # Define IGRF file if not supplied + if igrf_file is None: + igrf_file = aacgmv2.IGRF_12_COEFFS + + # Test time + if isinstance(dtime, dt.date): + date = dt.datetime.combine(dtime, dt.time(0)) + + assert isinstance(dtime, dt.datetime), \ + logging.error('time must be specified as datetime object') + + # Test height + if height < 0: + logging.warn('conversion not intended for altitudes < 0 km') + + # Test code + if isinstance(code, str): + code = code.upper() + + if(height > 2000 and code.find("TRACE") < 0 and + code.find("ALLOWTRACE") < 0 and code.find("BADIDEA")): + estr = 'coefficients are not valid for altitudes above 2000 km. You' + estr += ' must either use field-line tracing (trace=True ' + estr += 'or allowtrace=True) or indicate you know this ' + estr += 'is a bad idea' + logging.error(estr) + + # make flag + bit_code = convert_str_to_bit(code) + else: + bit_code = code + + assert isinstance(bit_code, int), \ + logging.error("unknown code {:}".format(bit_code)) + + # Test latitude range + if abs(in_lat) > 90.0: + assert abs(in_lat) <= 90.1, logging.error('unrealistic latitude') + in_lat = np.sign(in_lat) * 90.0 + + # Constrain longitudes between -180 and 180 + in_lon = ((in_lon + 180.0) % 360.0) - 180.0 + + # Set current date and time + aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, dtime.hour, + dtime.minute, dtime.second, coeff_prefix) - if np.max(alt) > 2000 and not (trace or allowtrace or badidea): - raise ValueError('Coefficients are not valid for altitudes above 2000 km. You must either use field-line ' - 'tracing (trace=True or allowtrace=True) or indicate you know this is a bad idea ' - '(badidea=True)') - - # check if latitudes are > 90.1 (to allow some room for rounding errors, which will be clipped) - if np.max(np.abs(lat)) > 90.1: - raise ValueError('Latitude must be in the range -90 to +90 degrees') - np.clip(lat, -90, 90) - - # constrain longitudes between -180 and 180 - lon = ((np.asarray(lon) + 180) % 360) - 180 - - # set to current date if none is given - if date is None: - date = dt.datetime.now() - - # add time info if only date is given - if isinstance(date, dt.date): - date = dt.datetime.combine(date, dt.time(0)) - - # set current date and time - setDateTime(date.year, date.month, date.day, date.hour, date.minute, date.second) - - # make flag - flag = A2G*a2g + TRACE*trace + ALLOWTRACE*allowtrace + BADIDEA*badidea + GEOCENTRIC*geocentric # convert - lat_out, lon_out, _ = aacgmConvert_vectorized(lat, lon, alt, flag) - - return lat_out, lon_out + lat_out, lon_out, r_out = aacgmv2.convert(in_lat, in_lon, height, bit_code, + igrf_file) + return lat_out, lon_out, r_out -def convert_mlt(arr, datetime, m2a=False): - '''Converts between magnetic local time (MLT) and AACGM-v2 longitude. - .. note:: This function is not related to the AACGM-v2 C library, but is provided as - a convenience in the hopes that it might be useful for some purposes. +def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", + igrf_file=None, coeff_prefix=None): + """Converts between geomagnetic coordinates and AACGM coordinates Parameters - ========== - arr : array_like or float - Magnetic longitudes or MLTs to convert. - datetime : :class:`datetime.datetime` - Date and time for MLT conversion in Universal Time (UT). - m2a : bool - Convert MLT to AACGM-v2 longitude (default is ``False``, which implies - conversion from AACGM-v2 longitude to MLT). + ------------ + in_lat : (np.ndarray) + Input latitude in degrees N (code specifies type of latitude) + in_lon : (np.ndarray) + Input longitude in degrees E (code specifies type of longitude) + height : (np.ndarray) + Altitude above the surface of the earth in km + dtime : (datetime) + Single datetime object for magnetic field + code : (int or str) + Bit code or string denoting which type(s) of conversion to perform + G2A - geographic (geodetic) to AACGM-v2 + A2G - AACGM-v2 to geographic (geodetic) + TRACE - use field-line tracing, not coefficients + ALLOWTRACE - use trace only above 2000 km + BADIDEA - use coefficients above 2000 km + GEOCENTRIC - assume inputs are geocentric w/ RE=6371.2 + (default = "G2A") + igrf_file : (str or NoneType) + Full filename of IGRF coefficient file or None to use + aacgmv2.IGRF_12_COEFFS. (default=None) + coeff_prefix : (str or NoneType) + Location and file prefix for aacgm coefficient files or None to use + aacgmv2.AACGM_v2_DAT_PREFIX. (default=None) Returns - ======= - out : numpy.ndarray - Converted coordinates/MLT - - Notes - ===== - - The MLT conversion is not part of the AACGM-v2 C library and is instead based - on Laundal et al., 2016 [1]_. A brief summary of the method is provided below. - - MLT is defined as - - MLT = (magnetic longitude - magnetic noon meridian longitude) / 15 + 12 - - where the magnetic noon meridian longitude is the centered dipole longitude - of the subsolar point. - - There are two important reasons for using centered dipole instead of AACGM for - this calculation. One reason is that the AACGM longitude of the subsolar point - is often undefined (being at low latitudes). More importantly, if the subsolar - point close to ground was used, the MLT at polar latitudes would be affected - by non-dipole features at low latitudes, such as the South Atlantic Anomaly. - This is not desirable; since the Sun-Earth interaction takes place at polar - field lines, it is these field lines the MLT should describe. - - In calculating the centered dipole longitude of the subsolar point, we use - the first three IGRF Gauss coefficients, using linear interpolation between - the model updates every five years. - - Both input and output MLON are taken modulo 360 to ensure they are between - 0 and 360 degrees. Similarly, input/output MLT are taken modulo 24. - For implementation of the subsolar point calculation, see :func:`subsol`. - - .. [1] Laundal, K. M. and A. D. Richmond (2016), Magnetic Coordinate Systems, - Space Sci. Rev., doi:`10.1007/s11214-016-0275-y `_. - - ''' - d2r = np.pi/180 - - # find subsolar point - yr = datetime.year - doy = datetime.timetuple().tm_yday - ssm = datetime.hour*3600 + datetime.minute*60 + datetime.second - subsol_lon, subsol_lat = subsol(yr, doy, ssm) - - # unit vector pointing at subsolar point: - s = np.array([np.cos(subsol_lat * d2r) * np.cos(subsol_lon * d2r), - np.cos(subsol_lat * d2r) * np.sin(subsol_lon * d2r), - np.sin(subsol_lat * d2r)]) - - # convert subsolar coordinates to centered dipole coordinates - z = igrf_dipole_axis(datetime) # Cartesian axis pointing at Northern dipole pole - y = np.cross(np.array([0, 0, 1]), z) - y = y/np.linalg.norm(y) - x = np.cross(y, z) - R = np.vstack((x, y, z)) - s_cd = R.dot(s) - - # centered dipole longitude of subsolar point: - mlon_subsol = np.arctan2(s_cd[1], s_cd[0])/d2r - - # convert the input array - if m2a: # MLT to AACGM - mlt = np.asarray(arr) % 24 - mlon = (15*(mlt - 12) + mlon_subsol) % 360 - return mlon - else: # AACGM to MLT - mlon = np.asarray(arr) % 360 - mlt = ((mlon - mlon_subsol)/15 + 12) % 24 - return mlt + ------- + out_lat : (np.ndarray) + Output latitudes in degrees + out_lon : (np.ndarray) + Output longitudes in degrees + out_r : (np.ndarray) + Geocentric radial distances in R + """ + # If someone was lazy and entered a list instead of a numpy array, + # recast it here + + if isinstance(in_lat, list): + in_lat = np.array(in_lat) + + if isinstance(in_lon, list): + in_lon = np.array(in_lon) + + if isinstance(height, list): + height = np.array(height) + + # Ensure that lat, lon, and height are the same length or if the lengths + # differ that the different ones contain only a single value + ulen = np.unique([height.shape, in_lat.shape, in_lon.shape]) + if ulen.shape[0] > 2 or (ulen.shape[0] == 2 and ulen[0] > 1): + logging.error("mismatched input arrays") + return None, None, None + + # Define coefficient file prefix if not supplied + if coeff_prefix is None: + coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX + + # Define IGRF file if not supplied + if igrf_file is None: + igrf_file = aacgmv2.IGRF_12_COEFFS + + # Test time + if isinstance(dtime, dt.date): + date = dt.datetime.combine(dtime, dt.time(0)) + + assert isinstance(dtime, dt.datetime), \ + logging.error('time must be specified as datetime object') + + # Test height + if np.min(height) < 0: + logging.warn('conversion not intended for altitudes < 0 km') + + # Test code + if isinstance(code, str): + code = code.upper() + + if(np.max(height) > 2000 and code.find("TRACE") < 0 and + code.find("ALLOWTRACE") < 0 and code.find("BADIDEA")): + estr = 'coefficients are not valid for altitudes above 2000 km. You' + estr += ' must either use field-line tracing (trace=True ' + estr += 'or allowtrace=True) or indicate you know this ' + estr += 'is a bad idea' + logging.error(estr) + + # make flag + bit_code = convert_str_to_bit(code) + else: + bit_code = code + + assert isinstance(bit_code, int), \ + logging.error("unknown code {:}".format(bit_code)) + + # Test latitude range + if np.abs(in_lat).max() > 90.0: + assert np.abs(in_lat).max() <= 90.1, \ + logging.error('unrealistic latitude') + in_lat = np.clip(in_lat, -90.0, 90.0) + + # Constrain longitudes between -180 and 180 + in_lon = ((in_lon + 180.0) % 360.0) - 180.0 + + # Set current date and time + aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, dtime.hour, + dtime.minute, dtime.second, coeff_prefix) + + # Vectorise the AACGM code + convert_vectorised = np.vectorize(aacgmv2.convert) + # convert + lat_out, lon_out, r_out = convert_vectorised(in_lat, in_lon, height, + bit_code, igrf_file) -def subsol(year, doy, ut): - '''Finds subsolar geocentric longitude and latitude. + return lat_out, lon_out, r_out - Helper function for :func:`convert_mlt`. +def get_aacgm_coord(glat, glon, height, dtime, method="TRACE", + igrf_file=None, coeff_prefix=None): + """Get AACGM latitude, longitude, and magnetic local time Parameters - ========== - year : int [1601, 2100] - Calendar year - doy : int [1, 365/366] - Day of year - ut : float - Seconds since midnight on the specified day + ------------ + glat : (float) + Geodetic latitude in degrees N + glon : (float) + Geodetic longitude in degrees E + height : (float) + Altitude above the surface of the earth in km + dtime : (datetime) + Date and time to calculate magnetic location + method : (str) + String denoting which type(s) of conversion to perform + TRACE - use field-line tracing, not coefficients + ALLOWTRACE - use trace only above 2000 km + BADIDEA - use coefficients above 2000 km + GEOCENTRIC - assume inputs are geocentric w/ RE=6371.2 + (default = "TRACE") + igrf_file : (str or NoneType) + Full filename of IGRF coefficient file or None to use + aacgmv2.IGRF_12_COEFFS. (default=None) + coeff_prefix : (str or NoneType) + Location and file prefix for aacgm coefficient files or None to use + aacgmv2.AACGM_v2_DAT_PREFIX. (default=None) Returns - ======= - sbsllon : float - Subsolar longitude for the given date/time - sbsllat : float - Subsolar latitude for the given date/time - - Notes - ===== - - Based on formulas in Astronomical Almanac for the year 1996, p. C24. - (U.S. Government Printing Office, 1994). Usable for years 1601-2100, - inclusive. According to the Almanac, results are good to at least 0.01 - degree latitude and 0.025 degrees longitude between years 1950 and 2050. - Accuracy for other years has not been tested. Every day is assumed to have - exactly 86400 seconds; thus leap seconds that sometimes occur on December - 31 are ignored (their effect is below the accuracy threshold of the - algorithm). - - After Fortran code by A. D. Richmond, NCAR. Translated from IDL - by K. Laundal. - - ''' - - from numpy import sin, cos, pi, arctan2, arcsin - - yr = year - 2000 - - if year >= 2101: - print('subsol.py: subsol invalid after 2100. Input year is:', year) - - nleap = np.floor((year-1601)/4) - nleap = nleap - 99 - if year <= 1900: - if year <= 1600: - print('subsol.py: subsol invalid before 1601. Input year is:', year) - ncent = np.floor((year-1601)/100) - ncent = 3 - ncent - nleap = nleap + ncent - - l0 = -79.549 + (-0.238699*(yr-4*nleap) + 3.08514e-2*nleap) - - g0 = -2.472 + (-0.2558905*(yr-4*nleap) - 3.79617e-2*nleap) - - # Days (including fraction) since 12 UT on January 1 of IYR: - df = (ut/86400 - 1.5) + doy - - # Addition to Mean longitude of Sun since January 1 of IYR: - lf = 0.9856474*df - - # Addition to Mean anomaly since January 1 of IYR: - gf = 0.9856003*df - - # Mean longitude of Sun: - l = l0 + lf - - # Mean anomaly: - g = g0 + gf - grad = g*pi/180 - - # Ecliptic longitude: - lmbda = l + 1.915*sin(grad) + 0.020*sin(2*grad) - lmrad = lmbda*pi/180 - sinlm = sin(lmrad) - - # Days (including fraction) since 12 UT on January 1 of 2000: - n = df + 365*yr + nleap - - # Obliquity of ecliptic: - epsilon = 23.439 - 4e-7*n - epsrad = epsilon*pi/180 - - # Right ascension: - alpha = arctan2(cos(epsrad)*sinlm, cos(lmrad)) * 180/pi - - # Declination: - delta = arcsin(sin(epsrad)*sinlm) * 180/pi - - # Subsolar latitude: - sbsllat = delta - - # Equation of time (degrees): - etdeg = l - alpha - nrot = round(etdeg/360) - etdeg = etdeg - 360*nrot - - # Apparent time (degrees): - aptime = ut/240 + etdeg # Earth rotates one degree every 240 s. - - # Subsolar longitude: - sbsllon = 180 - aptime - nrot = round(sbsllon/360) - sbsllon = sbsllon - 360*nrot - - return sbsllon, sbsllat + ------- + mlat : (float) + magnetic latitude in degrees + mlon : (float) + magnetic longitude in degrees + mlt : (float) + magnetic local time in hours + """ + # Define coefficient file prefix if not supplied + if coeff_prefix is None: + coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX + + # Define IGRF file if not supplied + if igrf_file is None: + igrf_file = aacgmv2.IGRF_12_COEFFS + + # Initialize return values + mlat = None + mlon = None + mlt = None + + try: + # Get magnetic lat and lon. + mlat, mlon, mr = convert_latlon(glat, glon, height, dtime, + code="G2A|{:s}".format(method), + igrf_file=igrf_file, + coeff_prefix=coeff_prefix) + # Get magnetic local time + mlt = aacgmv2.mlt_convert(dtime.year, dtime.month, dtime.day, + dtime.hour, dtime.minute, dtime.second, mlon, + coeff_prefix, igrf_file) + except: + logging.error("Unable to get magnetic lat/lon") + + return mlat, mlon, mlt + + +def get_aacgm_coord_arr(glat, glon, height, dtime, method="TRACE", + igrf_file=None, coeff_prefix=None): + """Get AACGM latitude, longitude, and magnetic local time + Parameters + ------------ + glat : (np.array or list) + Geodetic latitude in degrees N + glon : (np.array or list) + Geodetic longitude in degrees E + height : (np.array or list) + Altitude above the surface of the earth in km + dtime : (datetime) + Date and time to calculate magnetic location + method : (str) + String denoting which type(s) of conversion to perform + TRACE - use field-line tracing, not coefficients + ALLOWTRACE - use trace only above 2000 km + BADIDEA - use coefficients above 2000 km + GEOCENTRIC - assume inputs are geocentric w/ RE=6371.2 + (default = "TRACE") + igrf_file : (str or NoneType) + Full filename of IGRF coefficient file or None to use + aacgmv2.IGRF_12_COEFFS. (default=None) + coeff_prefix : (str or NoneType) + Location and file prefix for aacgm coefficient files or None to use + aacgmv2.AACGM_v2_DAT_PREFIX. (default=None) -def gc2gd_lat(gc_lat): - '''Convert geocentric latitude to geodetic latitude using WGS84. + Returns + ------- + mlat : (float) + magnetic latitude in degrees + mlon : (float) + magnetic longitude in degrees + mlt : (float) + magnetic local time in hours + """ + # Define coefficient file prefix if not supplied + if coeff_prefix is None: + coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX + + # Define IGRF file if not supplied + if igrf_file is None: + igrf_file = aacgmv2.IGRF_12_COEFFS + + # Initialize return values + mlat = None + mlon = None + mlt = None + + try: + # Get magnetic lat and lon. + mlat, mlon, mr = convert_latlon_arr(glat, glon, height, dtime, + code="G2A|{:s}".format(method), + igrf_file=igrf_file, + coeff_prefix=coeff_prefix) + + if mlon is not None: + # Get magnetic local time + mlt_vectorised = np.vectorize(aacgmv2.mlt_convert) + mlt = mlt_vectorised(dtime.year, dtime.month, dtime.day, + dtime.hour, dtime.minute, dtime.second, mlon, + coeff_prefix, igrf_file) + except: + logging.error("Unable to get magnetic lat/lon") + + return mlat, mlon, mlt + +def convert_str_to_bit(code): + """convert string code specification to bit code specification Parameters - ========== - gc_lat : array_like or float - Geocentric latitude + ------------ + code : (str) + Bitwise code for passing options into converter (default=0) + G2A - geographic (geodetic) to AACGM-v2 + A2G - AACGM-v2 to geographic (geodetic) + TRACE - use field-line tracing, not coefficients + ALLOWTRACE - use trace only above 2000 km + BADIDEA - use coefficients above 2000 km + GEOCENTRIC - assume inputs are geocentric w/ RE=6371.2 Returns - ======= - gd_lat : same as input - Geodetic latitude + -------- + bit_code : (int) + code specification in bits + """ + convert_code = {"G2A": aacgmv2.G2A, "A2G": aacgmv2.A2G, + "TRACE": aacgmv2.TRACE, "GEOCENTRIC": aacgmv2.GEOCENTRIC, + "ALLOWTRACE": aacgmv2.ALLOWTRACE, + "BADIDEA": aacgmv2.BADIDEA} + + code = code.upper() - ''' - WGS84_e2 = 0.006694379990141317 - return np.rad2deg(-np.arctan(np.tan(np.deg2rad(gc_lat))/(WGS84_e2 - 1))) + bit_code = sum([convert_code[k] for k in convert_code.keys() + if code.find(k) >= 0]) + return bit_code -def igrf_dipole_axis(date): - '''Get Cartesian unit vector pointing at dipole pole in the north, according to IGRF +def convert_bool_to_bit(a2g=False, trace=False, allowtrace=False, + badidea=False, geocentric=False): + """convert boolian flags to bit code specification Parameters - ========== - date : :class:`datetime.datetime` - Date and time + ---------- + a2g : (bool) + True for AACGM-v2 to geographic (geodetic), False otherwise + (default=False) + trace : (bool) + If True, use field-line tracing, not coefficients (default=False) + allowtrace : (bool) + If True, use trace only above 2000 km (default=False) + badidea : (bool) + If True, use coefficients above 2000 km (default=False) + geocentric : (bool) + True for geodetic, False for geocentric w/RE=6371.2 (default=False) Returns - ======= - m: numpy.ndarray - Cartesian 3 element vector pointing at dipole pole in the north (geocentric coords) - - Notes - ===== - IGRF coefficients are read from the igrf12coeffs.txt file. It should also work after IGRF updates. - The dipole coefficients are interpolated to the date, or extrapolated if date > latest IGRF model - ''' - - # get time in years, as float: - year = date.year - doy = date.timetuple().tm_yday - year = year + doy/(365 + calendar.isleap(year)) - - # read the IGRF coefficients - with open(IGRF_12_COEFFS, 'r') as f: - lines = f.readlines() - - years = lines[3].split()[3:][:-1] - years = np.array(years, dtype=float) # time array - - g10 = lines[4].split()[3:] - g11 = lines[5].split()[3:] - h11 = lines[6].split()[3:] - - # secular variation coefficients (for extrapolation) - g10sv = np.float32(g10[-1]) - g11sv = np.float32(g11[-1]) - h11sv = np.float32(h11[-1]) - - # model coefficients: - g10 = np.array(g10[:-1], dtype=float) - g11 = np.array(g11[:-1], dtype=float) - h11 = np.array(h11[:-1], dtype=float) - - # get the gauss coefficient at given time: - if year <= years[-1]: # regular interpolation - g10 = np.interp(year, years, g10) - g11 = np.interp(year, years, g11) - h11 = np.interp(year, years, h11) - else: # extrapolation - dt = year - years[-1] - g10 = g10[-1] + g10sv * dt - g11 = g11[-1] + g11sv * dt - h11 = h11[-1] + h11sv * dt - - # calculate pole position - B0 = np.sqrt(g10**2 + g11**2 + h11**2) - - return -np.array([g11, h11, g10])/B0 + -------- + bit_code : (int) + code specification in bits + """ + bit_code = aacgmv2.A2G if a2g else aacgmv2.G2A + + if trace: + bit_code += aacgmv2.TRACE + if allowtrace: + bit_code += aacgmv2.ALLOWTRACE + if badidea: + bit_code += aacgmv2.BADIDEA + if geocentric: + bit_code += aacgmv2.GEOCENTRIC + + return bit_code From 80239d82b6f6227c57ae3e868f201c56d8bd7ec2 Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 9 Mar 2018 17:00:26 -0600 Subject: [PATCH 007/101] Change directory structure Changed directory structure to reflect more common organisation. Need to fix setup.py --- LICENSE-AstAlg.txt | 28 +++++++++++++++++ {src/aacgmv2 => aacgmv2}/__init__.py | 11 ++++--- {src/aacgmv2 => aacgmv2}/__main__.py | 0 .../aacgm_coeffs/aacgm_coeffs-12-1900.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1905.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1910.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1915.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1920.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1925.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1930.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1935.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1940.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1945.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1950.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1955.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1960.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1965.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1970.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1975.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1980.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1985.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1990.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-1995.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-2000.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-2005.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-2010.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-2015.asc | 0 .../aacgm_coeffs/aacgm_coeffs-12-2020.asc | 0 {src/aacgmv2 => aacgmv2}/aacgmv2module.c | 4 +-- {src/aacgmv2 => aacgmv2}/igrf12coeffs.txt | 0 {tests => aacgmv2/tests}/test_c_aacgmv2.py | 0 {tests => aacgmv2/tests}/test_cmd_aacgmv2.py | 0 {tests => aacgmv2/tests}/test_convert.txt | 0 {tests => aacgmv2/tests}/test_convert_mlt.txt | 0 .../tests}/test_convert_mlt_single_line.txt | 0 .../tests}/test_convert_single_line.txt | 0 {tests => aacgmv2/tests}/test_py_aacgmv2.py | 0 {src/aacgmv2 => aacgmv2}/wrapper.py | 0 {src/c_aacgm_v2 => c_aacgmv2}/README.txt | 0 .../include}/aacgmlib_v2.h | 0 .../c_aacgm_v2 => c_aacgmv2/include}/astalg.h | 0 .../c_aacgm_v2 => c_aacgmv2/include}/genmag.h | 0 .../include}/igrflib.h | 0 .../c_aacgm_v2 => c_aacgmv2/include}/mlt_v2.h | 0 {src/c_aacgm_v2 => c_aacgmv2/include}/rtime.h | 0 .../src}/aacgmlib_v2.c | 0 {src/c_aacgm_v2 => c_aacgmv2/src}/astalglib.c | 0 {src/c_aacgm_v2 => c_aacgmv2/src}/genmag.c | 0 {src/c_aacgm_v2 => c_aacgmv2/src}/igrflib.c | 0 {src/c_aacgm_v2 => c_aacgmv2/src}/mlt_v2.c | 0 {src/c_aacgm_v2 => c_aacgmv2/src}/rtime.c | 0 .../c_aacgm_v2 => c_aacgmv2/src}/test_aacgm.c | 0 setup.cfg | 2 ++ setup.py | 31 ++++++++++--------- 54 files changed, 54 insertions(+), 22 deletions(-) create mode 100644 LICENSE-AstAlg.txt rename {src/aacgmv2 => aacgmv2}/__init__.py (77%) rename {src/aacgmv2 => aacgmv2}/__main__.py (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1900.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1905.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1910.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1915.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1920.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1925.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1930.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1935.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1940.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1945.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1950.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1955.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1960.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1965.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1970.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1975.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1980.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1985.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1990.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-1995.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-2000.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-2005.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-2010.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-2015.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgm_coeffs/aacgm_coeffs-12-2020.asc (100%) rename {src/aacgmv2 => aacgmv2}/aacgmv2module.c (99%) rename {src/aacgmv2 => aacgmv2}/igrf12coeffs.txt (100%) rename {tests => aacgmv2/tests}/test_c_aacgmv2.py (100%) rename {tests => aacgmv2/tests}/test_cmd_aacgmv2.py (100%) rename {tests => aacgmv2/tests}/test_convert.txt (100%) rename {tests => aacgmv2/tests}/test_convert_mlt.txt (100%) rename {tests => aacgmv2/tests}/test_convert_mlt_single_line.txt (100%) rename {tests => aacgmv2/tests}/test_convert_single_line.txt (100%) rename {tests => aacgmv2/tests}/test_py_aacgmv2.py (100%) rename {src/aacgmv2 => aacgmv2}/wrapper.py (100%) rename {src/c_aacgm_v2 => c_aacgmv2}/README.txt (100%) rename {src/c_aacgm_v2 => c_aacgmv2/include}/aacgmlib_v2.h (100%) rename {src/c_aacgm_v2 => c_aacgmv2/include}/astalg.h (100%) mode change 100755 => 100644 rename {src/c_aacgm_v2 => c_aacgmv2/include}/genmag.h (100%) rename {src/c_aacgm_v2 => c_aacgmv2/include}/igrflib.h (100%) rename {src/c_aacgm_v2 => c_aacgmv2/include}/mlt_v2.h (100%) mode change 100755 => 100644 rename {src/c_aacgm_v2 => c_aacgmv2/include}/rtime.h (100%) mode change 100755 => 100644 rename {src/c_aacgm_v2 => c_aacgmv2/src}/aacgmlib_v2.c (100%) rename {src/c_aacgm_v2 => c_aacgmv2/src}/astalglib.c (100%) mode change 100755 => 100644 rename {src/c_aacgm_v2 => c_aacgmv2/src}/genmag.c (100%) rename {src/c_aacgm_v2 => c_aacgmv2/src}/igrflib.c (100%) rename {src/c_aacgm_v2 => c_aacgmv2/src}/mlt_v2.c (100%) mode change 100755 => 100644 rename {src/c_aacgm_v2 => c_aacgmv2/src}/rtime.c (100%) mode change 100755 => 100644 rename {src/c_aacgm_v2 => c_aacgmv2/src}/test_aacgm.c (100%) diff --git a/LICENSE-AstAlg.txt b/LICENSE-AstAlg.txt new file mode 100644 index 00000000..c0efb746 --- /dev/null +++ b/LICENSE-AstAlg.txt @@ -0,0 +1,28 @@ +Copyright and License Information + + This source file is part of a library of files implementing + portions of the algorithms given in the book _Astronomical + Algorithms_ by Jean Meeus. + + Software Copyright (C) 2006, U.S. Government + Author: Kile B. Baker + National Science Foundation + 4201 Wilson Blvd, + Arlington, VA 22230 + email: kbaker@nsf.gov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + diff --git a/src/aacgmv2/__init__.py b/aacgmv2/__init__.py similarity index 77% rename from src/aacgmv2/__init__.py rename to aacgmv2/__init__.py index 5f3d35ec..87fd9beb 100644 --- a/src/aacgmv2/__init__.py +++ b/aacgmv2/__init__.py @@ -45,26 +45,27 @@ logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) try: - from aacgmv2 import convert + from aacgmv2._aacgmv2 import convert except Exception, e: logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) try: - from aacgmv2 import set_datetime + from aacgmv2._aacgmv2 import set_datetime except Exception, e: logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) try: - from aacgmv2 import mlt_convert, inv_mlt_convert + from aacgmv2._aacgmv2 import mlt_convert, inv_mlt_convert except Exception, e: logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) try: - from aacgmv2 import mlt_convert_yrsec, inv_mlt_convert_yrsec + from aacgmv2._aacgmv2 import mlt_convert_yrsec, inv_mlt_convert_yrsec except Exception, e: logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) try: - from aacgmv2 import G2A, A2G, TRACE, ALLOWTRACE, BADIDEA, GEOCENTRIC + from aacgmv2._aacgmv2 import (G2A, A2G, TRACE, ALLOWTRACE, BADIDEA, + GEOCENTRIC) except Exception, e: logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) diff --git a/src/aacgmv2/__main__.py b/aacgmv2/__main__.py similarity index 100% rename from src/aacgmv2/__main__.py rename to aacgmv2/__main__.py diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1900.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1900.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1900.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1900.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1905.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1905.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1905.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1905.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1910.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1910.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1910.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1910.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1915.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1915.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1915.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1915.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1920.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1920.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1920.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1920.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1925.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1925.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1925.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1925.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1930.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1930.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1930.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1930.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1935.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1935.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1935.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1935.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1940.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1940.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1940.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1940.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1945.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1945.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1945.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1945.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1950.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1950.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1950.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1950.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1955.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1955.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1955.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1955.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1960.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1960.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1960.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1960.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1965.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1965.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1965.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1965.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1970.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1970.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1970.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1970.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1975.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1975.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1975.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1975.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1980.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1980.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1980.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1980.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1985.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1985.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1985.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1985.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1990.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1990.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1990.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1990.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1995.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1995.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1995.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-1995.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2000.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2000.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2000.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2000.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2005.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2005.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2005.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2005.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2010.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2010.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2010.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2010.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2015.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2015.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2015.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2015.asc diff --git a/src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2020.asc b/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2020.asc similarity index 100% rename from src/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2020.asc rename to aacgmv2/aacgm_coeffs/aacgm_coeffs-12-2020.asc diff --git a/src/aacgmv2/aacgmv2module.c b/aacgmv2/aacgmv2module.c similarity index 99% rename from src/aacgmv2/aacgmv2module.c rename to aacgmv2/aacgmv2module.c index beccfa44..06d701c8 100644 --- a/src/aacgmv2/aacgmv2module.c +++ b/aacgmv2/aacgmv2module.c @@ -314,7 +314,7 @@ mlon : (float)\n\ /* Different versions of python require different constant declarations */ #if PY_MAJOR_VERSION >= 3 - static struct PyModuleDef aacgm_module = { + static struct PyModuleDef aacgmv2module = { PyModuleDef_HEAD_INIT, "aacgmv2", /* name of module */ "Interface to the AACGM-v2 C library.", /* module documentation */ @@ -325,7 +325,7 @@ mlon : (float)\n\ PyMODINIT_FUNC PyInit_aacgm(void) { - module = PyModule_Create(&aacgm_module); + module = PyModule_Create(&aacgmv2module); PyModule_AddIntConstant(module, "G2A", G2A); PyModule_AddIntConstant(module, "A2G", A2G); PyModule_AddIntConstant(module, "TRACE", TRACE); diff --git a/src/aacgmv2/igrf12coeffs.txt b/aacgmv2/igrf12coeffs.txt similarity index 100% rename from src/aacgmv2/igrf12coeffs.txt rename to aacgmv2/igrf12coeffs.txt diff --git a/tests/test_c_aacgmv2.py b/aacgmv2/tests/test_c_aacgmv2.py similarity index 100% rename from tests/test_c_aacgmv2.py rename to aacgmv2/tests/test_c_aacgmv2.py diff --git a/tests/test_cmd_aacgmv2.py b/aacgmv2/tests/test_cmd_aacgmv2.py similarity index 100% rename from tests/test_cmd_aacgmv2.py rename to aacgmv2/tests/test_cmd_aacgmv2.py diff --git a/tests/test_convert.txt b/aacgmv2/tests/test_convert.txt similarity index 100% rename from tests/test_convert.txt rename to aacgmv2/tests/test_convert.txt diff --git a/tests/test_convert_mlt.txt b/aacgmv2/tests/test_convert_mlt.txt similarity index 100% rename from tests/test_convert_mlt.txt rename to aacgmv2/tests/test_convert_mlt.txt diff --git a/tests/test_convert_mlt_single_line.txt b/aacgmv2/tests/test_convert_mlt_single_line.txt similarity index 100% rename from tests/test_convert_mlt_single_line.txt rename to aacgmv2/tests/test_convert_mlt_single_line.txt diff --git a/tests/test_convert_single_line.txt b/aacgmv2/tests/test_convert_single_line.txt similarity index 100% rename from tests/test_convert_single_line.txt rename to aacgmv2/tests/test_convert_single_line.txt diff --git a/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py similarity index 100% rename from tests/test_py_aacgmv2.py rename to aacgmv2/tests/test_py_aacgmv2.py diff --git a/src/aacgmv2/wrapper.py b/aacgmv2/wrapper.py similarity index 100% rename from src/aacgmv2/wrapper.py rename to aacgmv2/wrapper.py diff --git a/src/c_aacgm_v2/README.txt b/c_aacgmv2/README.txt similarity index 100% rename from src/c_aacgm_v2/README.txt rename to c_aacgmv2/README.txt diff --git a/src/c_aacgm_v2/aacgmlib_v2.h b/c_aacgmv2/include/aacgmlib_v2.h similarity index 100% rename from src/c_aacgm_v2/aacgmlib_v2.h rename to c_aacgmv2/include/aacgmlib_v2.h diff --git a/src/c_aacgm_v2/astalg.h b/c_aacgmv2/include/astalg.h old mode 100755 new mode 100644 similarity index 100% rename from src/c_aacgm_v2/astalg.h rename to c_aacgmv2/include/astalg.h diff --git a/src/c_aacgm_v2/genmag.h b/c_aacgmv2/include/genmag.h similarity index 100% rename from src/c_aacgm_v2/genmag.h rename to c_aacgmv2/include/genmag.h diff --git a/src/c_aacgm_v2/igrflib.h b/c_aacgmv2/include/igrflib.h similarity index 100% rename from src/c_aacgm_v2/igrflib.h rename to c_aacgmv2/include/igrflib.h diff --git a/src/c_aacgm_v2/mlt_v2.h b/c_aacgmv2/include/mlt_v2.h old mode 100755 new mode 100644 similarity index 100% rename from src/c_aacgm_v2/mlt_v2.h rename to c_aacgmv2/include/mlt_v2.h diff --git a/src/c_aacgm_v2/rtime.h b/c_aacgmv2/include/rtime.h old mode 100755 new mode 100644 similarity index 100% rename from src/c_aacgm_v2/rtime.h rename to c_aacgmv2/include/rtime.h diff --git a/src/c_aacgm_v2/aacgmlib_v2.c b/c_aacgmv2/src/aacgmlib_v2.c similarity index 100% rename from src/c_aacgm_v2/aacgmlib_v2.c rename to c_aacgmv2/src/aacgmlib_v2.c diff --git a/src/c_aacgm_v2/astalglib.c b/c_aacgmv2/src/astalglib.c old mode 100755 new mode 100644 similarity index 100% rename from src/c_aacgm_v2/astalglib.c rename to c_aacgmv2/src/astalglib.c diff --git a/src/c_aacgm_v2/genmag.c b/c_aacgmv2/src/genmag.c similarity index 100% rename from src/c_aacgm_v2/genmag.c rename to c_aacgmv2/src/genmag.c diff --git a/src/c_aacgm_v2/igrflib.c b/c_aacgmv2/src/igrflib.c similarity index 100% rename from src/c_aacgm_v2/igrflib.c rename to c_aacgmv2/src/igrflib.c diff --git a/src/c_aacgm_v2/mlt_v2.c b/c_aacgmv2/src/mlt_v2.c old mode 100755 new mode 100644 similarity index 100% rename from src/c_aacgm_v2/mlt_v2.c rename to c_aacgmv2/src/mlt_v2.c diff --git a/src/c_aacgm_v2/rtime.c b/c_aacgmv2/src/rtime.c old mode 100755 new mode 100644 similarity index 100% rename from src/c_aacgm_v2/rtime.c rename to c_aacgmv2/src/rtime.c diff --git a/src/c_aacgm_v2/test_aacgm.c b/c_aacgmv2/src/test_aacgm.c similarity index 100% rename from src/c_aacgm_v2/test_aacgm.c rename to c_aacgmv2/src/test_aacgm.c diff --git a/setup.cfg b/setup.cfg index 609b8d13..0fd147e4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,8 +63,10 @@ python_versions = 3.3 3.4 3.5 + 3.6 dependencies = + np1.14: numpy>=1.14,<1.15 !python_versions[3.3] np1.10: numpy>=1.10,<1.11 np1.9: numpy>=1.9,<1.10 np1.8: numpy>=1.8,<1.9 diff --git a/setup.py b/setup.py index f5f5cac5..a7a73bb6 100644 --- a/setup.py +++ b/setup.py @@ -6,11 +6,7 @@ import os import re from glob import glob -from os.path import basename -from os.path import dirname -from os.path import join -from os.path import relpath -from os.path import splitext +from os import path from setuptools import find_packages from setuptools import setup @@ -19,7 +15,7 @@ def read(*names, **kwargs): return io.open( - join(dirname(__file__), *names), + path.join(path.dirname(__file__), *names), encoding=kwargs.get('encoding', 'utf8') ).read() @@ -43,13 +39,15 @@ def read(*names, **kwargs): author='Angeline G. Burrell, Christer van der Meeren', author_email='agb073000@utdallas.edu', url='https://github.com/aburrell/aacgmv2', - packages=find_packages('src'), - package_dir={'': 'src'}, - py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], + packages=find_packages('aacgmv2'), + package_dir={'': 'aacgmv2'}, + py_modules=[path.splitext(path.basename(psrc))[0] + for psrc in glob('aacgmv2/*.py')], package_data={'aacgmv2': ['aacgm_coeffs/*.asc', 'igrf12coeffs.txt']}, zip_safe=False, classifiers=[ - # complete classifier list: http://pypi.python.org/pypi?%3Aaction=list_classifiers + # complete classifier list: + # http://pypi.python.org/pypi?%3Aaction=list_classifiers 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: MIT License', @@ -84,11 +82,14 @@ def read(*names, **kwargs): ], ext_modules=[ Extension('aacgmv2._aacgmv2', - sources=['src/aacgmv2/aacgmv2module.c', - 'src/c_aacgm_v2/aacgmlib_v2.c', - 'src/c_aacgm_v2/genmag.c', - 'src/c_aacgm_v2/igrflib.c'], - include_dirs=['src/c_aacgm_v2']) + sources=['aacgmv2/aacgmv2module.c', + 'c_aacgmv2/src/aacgmlib_v2.c', + 'c_aacgmv2/src/astalglib.c', + 'c_aacgmv2/src/genmag.c', + 'c_aacgmv2/src/igrflib.c', + 'c_aacgmv2/src/mlt_v2.c', + 'c_aacgmv2/src/rtime.c'], + include_dirs=['c_aacgmv2/include']) ], entry_points={ 'console_scripts': [ From a575dbb7422718bb83be47c2badb4fc56ae59124 Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 12 Mar 2018 11:51:52 -0500 Subject: [PATCH 008/101] Fixed installation Fixed installation to reflect changes in directory structure. --- setup.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index a7a73bb6..1a8c3a33 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -from __future__ import absolute_import, print_function +from __future__ import absolute_import import io import os @@ -12,14 +12,12 @@ from setuptools import setup from distutils.core import Extension - def read(*names, **kwargs): return io.open( path.join(path.dirname(__file__), *names), encoding=kwargs.get('encoding', 'utf8') ).read() - # enable code coverage for C code # We can't use CFLAGS=-coverage in tox.ini, since that may mess with # compiling dependencies (e.g. numpy). Therefore we set PY_CCOV=-coverage @@ -79,9 +77,10 @@ def read(*names, **kwargs): ], install_requires=[ 'numpy', + 'logging', ], ext_modules=[ - Extension('aacgmv2._aacgmv2', + Extension('_aacgmv2', sources=['aacgmv2/aacgmv2module.c', 'c_aacgmv2/src/aacgmlib_v2.c', 'c_aacgmv2/src/astalglib.c', From 8d39cc4819f566a7029cb4911bf35c2f7fc6abc5 Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 12 Mar 2018 12:39:47 -0500 Subject: [PATCH 009/101] Updated badge files Updated files that are used to talk with badge generating applications --- .bumpversion.cfg | 4 +- .codeclimate.yml | 2 +- .cookiecutterrc | 14 ++-- .landscape.yaml | 2 +- .travis.yml | 14 ++++ README.rst | 5 +- appveyor.yml | 93 +++++++++++++++++++++ ci/appveyor-bootstrap.py | 59 ++++++++----- ci/appveyor-download.py | 19 +++-- ci/bootstrap.py | 49 +++++------ tox.ini | 176 ++++++++++++++++++++++++++++++++++++++- 11 files changed, 366 insertions(+), 71 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index f20b4205..aab4ddca 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.0.0 +current_version = 2.0.1 commit = True tag = True @@ -7,5 +7,5 @@ tag = True [bumpversion:file:docs/conf.py] -[bumpversion:file:src/aacgmv2/__init__.py] +[bumpversion:file:aacgmv2/__init__.py] diff --git a/.codeclimate.yml b/.codeclimate.yml index b3025c95..7a814397 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -3,6 +3,6 @@ languages: Python: true exclude_paths: - "ci/" - - "tests/" + - "aacgmv2/tests/" - "docs/" - "setup.py" \ No newline at end of file diff --git a/.cookiecutterrc b/.cookiecutterrc index 9e897cbb..b4f1dc90 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -14,14 +14,14 @@ default_context: command_line_interface: 'no' coveralls: 'yes' distribution_name: 'aacgmv2' - email: 'cmeeren@gmail.com' - full_name: 'Christer van der Meeren' - github_username: 'cmeeren' + email: 'agb073000@utdallas.edu' + full_name: 'Angeline G. Burrell' + github_username: 'aburrell' landscape: 'yes' package_name: 'aacgmv2' project_name: 'AACGM-v2 Python library' project_short_description: '"A Python wrapper for AACGM-v2 magnetic coordinates"' - release_date: '2015-06-10' + release_date: '2018-03-12' repo_name: 'aacgmv2' requiresio: 'yes' scrutinizer: 'yes' @@ -29,6 +29,6 @@ default_context: test_matrix_configurator: 'yes' test_runner: 'pytest' travis: 'yes' - version: '0.1.0' - website: 'https://github.com/cmeeren/aacgmv2' - year: '2015' + version: '2.0.1' + website: 'https://github.com/aburrell/aacgmv2' + year: '2018' diff --git a/.landscape.yaml b/.landscape.yaml index 3129377d..e8c75d79 100644 --- a/.landscape.yaml +++ b/.landscape.yaml @@ -3,5 +3,5 @@ doc-warnings: yes ignore-paths: - docs - ci - - tests + - aacgmv2/tests - setup.py \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 1fbd6d93..91cbed8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,8 @@ env: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check + - TOXENV=2.7-np1.14,extension-coveralls,coveralls + - TOXENV=2.7-np1.14-nocover - TOXENV=2.7-np1.10,extension-coveralls,coveralls - TOXENV=2.7-np1.10-nocover - TOXENV=2.7-np1.8,extension-coveralls,coveralls @@ -18,18 +20,30 @@ env: - TOXENV=3.3-np1.8-nocover - TOXENV=3.3-np1.9,extension-coveralls,coveralls - TOXENV=3.3-np1.9-nocover + - TOXENV=3.4-np1.14,extension-coveralls,coveralls + - TOXENV=3.4-np1.14-nocover - TOXENV=3.4-np1.10,extension-coveralls,coveralls - TOXENV=3.4-np1.10-nocover - TOXENV=3.4-np1.8,extension-coveralls,coveralls - TOXENV=3.4-np1.8-nocover - TOXENV=3.4-np1.9,extension-coveralls,coveralls - TOXENV=3.4-np1.9-nocover + - TOXENV=3.5-np1.14,extension-coveralls,coveralls + - TOXENV=3.5-np1.14-nocover - TOXENV=3.5-np1.10,extension-coveralls,coveralls - TOXENV=3.5-np1.10-nocover - TOXENV=3.5-np1.8,extension-coveralls,coveralls - TOXENV=3.5-np1.8-nocover - TOXENV=3.5-np1.9,extension-coveralls,coveralls - TOXENV=3.5-np1.9-nocover + - TOXENV=3.6-np1.14,extension-coveralls,coveralls + - TOXENV=3.6-np1.14-nocover + - TOXENV=3.6-np1.10,extension-coveralls,coveralls + - TOXENV=3.6-np1.10-nocover + - TOXENV=3.6-np1.8,extension-coveralls,coveralls + - TOXENV=3.6-np1.8-nocover + - TOXENV=3.6-np1.9,extension-coveralls,coveralls + - TOXENV=3.6-np1.9-nocover before_install: - python --version - uname -a diff --git a/README.rst b/README.rst index 6c8c1efc..6647db58 100644 --- a/README.rst +++ b/README.rst @@ -13,7 +13,7 @@ version of the C library is 2.4. The package is free software Quick start =========== -Install (requires NumPy):: +Install (requires NumPy and logging):: pip install aacgmv2 @@ -43,7 +43,8 @@ Convert between AACGM and MLT:: >>> mlon array([ 159.08967974, 339.08967974]) -If you don't know or use Python, you can also use the command line. See details in the full documentation. +If you don't know or use Python, you can also use the command line. See details +in the full documentation. Documentation ============= diff --git a/appveyor.yml b/appveyor.yml index e67cc470..c0ca2255 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -52,10 +52,34 @@ environment: PYTHON_HOME: C:\python35-x64 PYTHON_VERSION: '3.5' PYTHON_ARCH: '64' + - TOXENV: '3.6-buildonly-nocover' + TOXPYTHON: C:\python36\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + - TOXENV: '3.6-buildonly-nocover' + TOXPYTHON: C:\python36-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' - TOXENV: check PYTHON_HOME: C:\Python27 PYTHON_VERSION: '2.7' PYTHON_ARCH: '32' + - TOXENV: '2.7-np1.14-nocover' + TOXPYTHON: C:\python27\python.exe + WINDOWS_SDK_VERSION: v7.0 + PYTHON_HOME: C:\python27 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '32' + - TOXENV: '2.7-np1.14-nocover' + TOXPYTHON: C:\python27-x64\python.exe + WINDOWS_SDK_VERSION: v7.0 + PYTHON_HOME: C:\python27-x64 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '64' - TOXENV: '2.7-np1.10-nocover' TOXPYTHON: C:\python27\python.exe WINDOWS_SDK_VERSION: v7.0 @@ -128,6 +152,18 @@ environment: PYTHON_HOME: C:\python33-x64 PYTHON_VERSION: '3.3' PYTHON_ARCH: '64' + - TOXENV: '3.4-np1.14-nocover' + TOXPYTHON: C:\python34\python.exe + WINDOWS_SDK_VERSION: v7.0 + PYTHON_HOME: C:\python34 + PYTHON_VERSION: '3.4' + PYTHON_ARCH: '32' + - TOXENV: '3.4-np1.14-nocover' + TOXPYTHON: C:\python34-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\python34-x64 + PYTHON_VERSION: '3.4' + PYTHON_ARCH: '64' - TOXENV: '3.4-np1.10-nocover' TOXPYTHON: C:\python34\python.exe WINDOWS_SDK_VERSION: v7.0 @@ -164,6 +200,16 @@ environment: PYTHON_HOME: C:\python34-x64 PYTHON_VERSION: '3.4' PYTHON_ARCH: '64' + - TOXENV: '3.5-np1.14-nocover' + TOXPYTHON: C:\python35\python.exe + PYTHON_HOME: C:\python35 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '32' + - TOXENV: '3.5-np1.14-nocover' + TOXPYTHON: C:\python35-x64\python.exe + PYTHON_HOME: C:\python35-x64 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '64' - TOXENV: '3.5-np1.10-nocover' TOXPYTHON: C:\python35\python.exe PYTHON_HOME: C:\python35 @@ -194,6 +240,46 @@ environment: PYTHON_HOME: C:\python35-x64 PYTHON_VERSION: '3.5' PYTHON_ARCH: '64' + - TOXENV: '3.6-np1.14-nocover' + TOXPYTHON: C:\python36\python.exe + PYTHON_HOME: C:\python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + - TOXENV: '3.6-np1.14-nocover' + TOXPYTHON: C:\python36-x64\python.exe + PYTHON_HOME: C:\python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' + - TOXENV: '3.6-np1.10-nocover' + TOXPYTHON: C:\python36\python.exe + PYTHON_HOME: C:\python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + - TOXENV: '3.6-np1.10-nocover' + TOXPYTHON: C:\python36-x64\python.exe + PYTHON_HOME: C:\python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' + - TOXENV: '3.6-np1.8-nocover' + TOXPYTHON: C:\python36\python.exe + PYTHON_HOME: C:\python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + - TOXENV: '3.6-np1.8-nocover' + TOXPYTHON: C:\python36-x64\python.exe + PYTHON_HOME: C:\python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' + - TOXENV: '3.6-np1.9-nocover' + TOXPYTHON: C:\python36\python.exe + PYTHON_HOME: C:\python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + - TOXENV: '3.6-np1.9-nocover' + TOXPYTHON: C:\python36-x64\python.exe + PYTHON_HOME: C:\python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' init: - ps: echo $env:TOXENV - ps: ls C:\Python* @@ -229,14 +315,21 @@ matrix: - TOXENV: '3.4-np1.8,codecov' - TOXENV: '3.5-np1.8-nocover' - TOXENV: '3.5-np1.8,codecov' + - TOXENV: '3.6-np1.8-nocover' + - TOXENV: '3.6-np1.8,codecov' - TOXENV: '3.5-np1.9-nocover' - TOXENV: '3.5-np1.9,codecov' + - TOXENV: '3.6-np1.9-nocover' + - TOXENV: '3.6-np1.9,codecov' - TOXENV: '3.5-np1.10-nocover' - TOXENV: '3.5-np1.10,codecov' + - TOXENV: '3.6-np1.10-nocover' + - TOXENV: '3.6-np1.10,codecov' - TOXENV: '2.7-buildonly-nocover' - TOXENV: '3.3-buildonly-nocover' - TOXENV: '3.4-buildonly-nocover' - TOXENV: '3.5-buildonly-nocover' + - TOXENV: '3.6-buildonly-nocover' ### To enable remote debugging uncomment this: # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/ci/appveyor-bootstrap.py b/ci/appveyor-bootstrap.py index bc351bc0..e0d1a0ab 100644 --- a/ci/appveyor-bootstrap.py +++ b/ci/appveyor-bootstrap.py @@ -1,8 +1,10 @@ """ -AppVeyor will at least have few Pythons around so there's no point of implementing a bootstrapper in PowerShell. +AppVeyor will at least have few Pythons around so there's no point of +implementing a bootstrapper in PowerShell. This is a port of https://github.com/pypa/python-packaging-user-guide/blob/master/source/code/install.ps1 -with various fixes and improvements that just weren't feasible to implement in PowerShell. +with various fixes and improvements that just weren't feasible to implement in +PowerShell. """ from __future__ import print_function from os import environ @@ -18,29 +20,39 @@ GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" GET_PIP_PATH = "C:\get-pip.py" URLS = { - ("2.6", "64"): BASE_URL + "2.6.6/python-2.6.6.amd64.msi", - ("2.6", "32"): BASE_URL + "2.6.6/python-2.6.6.msi", - ("2.7", "64"): BASE_URL + "2.7.10/python-2.7.10.amd64.msi", - ("2.7", "32"): BASE_URL + "2.7.10/python-2.7.10.msi", - # NOTE: no .msi installer for 3.3.6 - ("3.3", "64"): BASE_URL + "3.3.3/python-3.3.3.amd64.msi", - ("3.3", "32"): BASE_URL + "3.3.3/python-3.3.3.msi", - ("3.4", "64"): BASE_URL + "3.4.3/python-3.4.3.amd64.msi", - ("3.4", "32"): BASE_URL + "3.4.3/python-3.4.3.msi", - ("3.5", "64"): BASE_URL + "3.5.0/python-3.5.0-amd64.exe", - ("3.5", "32"): BASE_URL + "3.5.0/python-3.5.0.exe", + ("2.7", "64"): BASE_URL + "2.7.14/python-2.7.14.amd64.msi", + ("2.7", "32"): BASE_URL + "2.7.14/python-2.7.14.msi", + # NOTE: IF FAILS, USE 3.3.3 + ("3.3", "64"): BASE_URL + "3.3.7/python-3.3.7.amd64.msi", + ("3.3", "32"): BASE_URL + "3.3.7/python-3.3.7.msi", + ("3.4", "64"): BASE_URL + "3.4.8/python-3.4.8.amd64.msi", + ("3.4", "32"): BASE_URL + "3.4.8/python-3.4.8.msi", + ("3.5", "64"): BASE_URL + "3.5.5/python-3.5.5-amd64.exe", + ("3.5", "32"): BASE_URL + "3.5.5/python-3.5.5.exe", + ("3.6", "64"): BASE_URL + "3.6.4/python-3.6.4-amd64.exe", + ("3.6", "32"): BASE_URL + "3.6.4/python-3.6.4.exe", } INSTALL_CMD = { - # Commands are allowed to fail only if they are not the last command. Eg: uninstall (/x) allowed to fail. + # Commands are allowed to fail only if they are not the last command. + # E.g.: uninstall (/x) allowed to fail. "2.6": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], - ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], + ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", + "TARGETDIR={home}"]], "2.7": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], - ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], + ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", + "TARGETDIR={home}"]], "3.3": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], - ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], + ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", + "TARGETDIR={home}"]], "3.4": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], - ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], - "3.5": [["{path}", "/quiet", "TargetDir={home}"]], + ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", + "TARGETDIR={home}"]], + "3.5": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], + ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", + "TARGETDIR={home}"]], + "3.6": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], + ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", + "TARGETDIR={home}"]], } @@ -59,7 +71,8 @@ def report(count, size, total): def install_python(version, arch, home): - print("Installing Python", version, "for", arch, "bit architecture to", home) + print("Installing Python", version, "for", arch, "bit architecture to", + home) if exists(home): return @@ -112,6 +125,8 @@ def install_packages(home, *packages): if __name__ == "__main__": - install_python(environ['PYTHON_VERSION'], environ['PYTHON_ARCH'], environ['PYTHON_HOME']) + install_python(environ['PYTHON_VERSION'], environ['PYTHON_ARCH'], + environ['PYTHON_HOME']) install_pip(environ['PYTHON_HOME']) - install_packages(environ['PYTHON_HOME'], "setuptools>=18.0.1", "wheel", "tox", "virtualenv>=13.1.0") + install_packages(environ['PYTHON_HOME'], "setuptools>=18.0.1", "wheel", + "tox", "virtualenv>=13.1.0") diff --git a/ci/appveyor-download.py b/ci/appveyor-download.py index fa5e80d1..e8bc4634 100644 --- a/ci/appveyor-download.py +++ b/ci/appveyor-download.py @@ -16,8 +16,9 @@ def make_auth_headers(): """Make the authentication headers needed to use the Appveyor API.""" if not os.path.exists(".appveyor.token"): raise RuntimeError( - "Please create a file named `.appveyor.token` in the current directory. " - "You can get the token from https://ci.appveyor.com/api-token" + "Please create a file named `.appveyor.token` in the current " + " directory. You can get the token from " + "https://ci.appveyor.com/api-token" ) with open(".appveyor.token") as f: token = f.read().strip() @@ -35,7 +36,8 @@ def make_url(url, **kwargs): def get_project_build(account_project): """Get the details of the latest Appveyor build.""" - url = make_url("/projects/{account_project}", account_project=account_project) + url = make_url("/projects/{account_project}", + account_project=account_project) response = requests.get(url, headers=make_auth_headers()) return response.json() @@ -47,7 +49,8 @@ def download_latest_artifacts(account_project): print("Build {0[build][version]}, {1} jobs: {0[build][message]}".format(build, len(jobs))) for job in jobs: name = job['name'].partition(':')[2].split(',')[0].strip() - print(" {0}: {1[status]}, {1[artifactsCount]} artifacts".format(name, job)) + print(" {0}: {1[status]}, {1[artifactsCount]} artifacts".format(name, + job)) url = make_url("/buildjobs/{jobid}/artifacts", jobid=job['jobId']) response = requests.get(url, headers=make_auth_headers()) @@ -96,10 +99,10 @@ def unpack_zipfile(filename): ensure_dirs(name) z.extract(name) -parser = argparse.ArgumentParser(description='Download artifacts from AppVeyor.') -parser.add_argument('name', - metavar='ID', - help='Project ID in AppVeyor. Example: ionelmc/python-nameless') +desc = 'Download artifacts from AppVeyor.' +parser = argparse.ArgumentParser(description=desc) +desc = 'Project ID in AppVeyor. Example: ionelmc/python-nameless' +parser.add_argument('name', metavar='ID', help=desc) if __name__ == "__main__": # import logging diff --git a/ci/bootstrap.py b/ci/bootstrap.py index 466bb8fe..089ee4b2 100644 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -4,61 +4,62 @@ import os import sys -from os.path import exists -from os.path import join -from os.path import dirname -from os.path import abspath - if __name__ == "__main__": - base_path = dirname(dirname(abspath(__file__))) + base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) print("Project path: {0}".format(base_path)) - env_path = join(base_path, ".tox", "bootstrap") + env_path = os.path.join(base_path, ".tox", "bootstrap") if sys.platform == "win32": - bin_path = join(env_path, "Scripts") + bin_path = os.path.join(env_path, "Scripts") else: - bin_path = join(env_path, "bin") - if not exists(env_path): + bin_path = os.path.join(env_path, "bin") + if not os.path.exists(env_path): import subprocess print("Making bootstrap env in: {0} ...".format(env_path)) try: subprocess.check_call(["virtualenv", env_path]) except Exception: - subprocess.check_call([sys.executable, "-m", "virtualenv", env_path]) + subprocess.check_call([sys.executable, "-m", "virtualenv", + env_path]) print("Installing `jinja2` and `matrix` into bootstrap environment ...") - subprocess.check_call([join(bin_path, "pip"), "install", "jinja2", "matrix"]) - activate = join(bin_path, "activate_this.py") - exec(compile(open(activate, "rb").read(), activate, "exec"), dict(__file__=activate)) + subprocess.check_call([os.path.join(bin_path, "pip"), "install", + "jinja2", "matrix"]) + activate = os.path.join(bin_path, "activate_this.py") + exec(compile(open(activate, "rb").read(), activate, "exec"), + dict(__file__=activate)) import jinja2 import matrix jinja = jinja2.Environment( - loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), + loader=jinja2.FileSystemLoader(os.path.join(base_path, "ci", + "templates")), trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True ) - tox_environments = {} - for (alias, conf) in matrix.from_file(join(base_path, "setup.cfg")).items(): + tox_envs = {} + for (alias, conf) in matrix.from_file(os.path.join(base_path, + "setup.cfg")).items(): python = conf["python_versions"] deps = conf["dependencies"] if "coverage_flags" in conf: - cover = {"false": False, "true": True}[conf["coverage_flags"].lower()] + cover = {"false": False, + "true": True}[conf["coverage_flags"].lower()] if "environment_variables" in conf: env_vars = conf["environment_variables"] - tox_environments[alias] = { + tox_envs[alias] = { "python": "python" + python if "py" not in python else python, "deps": deps.split(), } if "coverage_flags" in conf: - tox_environments[alias].update(cover=cover) + tox_envs[alias].update(cover=cover) if "environment_variables" in conf: - tox_environments[alias].update(env_vars=env_vars.split()) + tox_envs[alias].update(env_vars=env_vars.split()) - for name in os.listdir(join("ci", "templates")): - with open(join(base_path, name), "w") as fh: - fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) + for name in os.listdir(os.path.join("ci", "templates")): + with open(os.path.join(base_path, name), "w") as fh: + fh.write(jinja.get_template(name).render(tox_environments=tox_envs)) print("Wrote {}".format(name)) print("DONE.") diff --git a/tox.ini b/tox.ini index f781bee7..e791867c 100644 --- a/tox.ini +++ b/tox.ini @@ -2,18 +2,20 @@ envlist = clean, check, - 2.7-np1.10, - 2.7-np1.10-nocover, 2.7-np1.8, 2.7-np1.8-nocover, 2.7-np1.9, 2.7-np1.9-nocover, - 3.3-np1.10, - 3.3-np1.10-nocover, + 2.7-np1.10, + 2.7-np1.10-nocover, + 2.7-np1.14, + 2.7-np1.14-nocover, 3.3-np1.8, 3.3-np1.8-nocover, 3.3-np1.9, 3.3-np1.9-nocover, + 3.3-np1.10, + 3.3-np1.10-nocover, 3.4-np1.10, 3.4-np1.10-nocover, 3.4-np1.8, @@ -22,10 +24,24 @@ envlist = 3.4-np1.9-nocover, 3.5-np1.10, 3.5-np1.10-nocover, + 3.4-np1.14, + 3.4-np1.14-nocover, 3.5-np1.8, 3.5-np1.8-nocover, 3.5-np1.9, 3.5-np1.9-nocover, + 3.5-np1.10, + 3.5-np1.10-nocover, + 3.5-np1.14, + 3.5-np1.14-nocover, + 3.6-np1.8, + 3.6-np1.8-nocover, + 3.6-np1.9, + 3.6-np1.9-nocover, + 3.6-np1.10, + 3.6-np1.10-nocover, + 3.6-np1.14, + 3.6-np1.14-nocover, report, docs @@ -132,6 +148,21 @@ skip_install = true usedevelop = false deps = coverage +[testenv:2.7-np1.14] +basepython = {env:TOXPYTHON:python2.7} +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +deps = + {[testenv]deps} + pytest-cov + numpy>=1.14,<1.15 + [testenv:2.7-np1.10] basepython = {env:TOXPYTHON:python2.7} setenv = @@ -153,6 +184,12 @@ deps = {[testenv]deps} numpy>=1.10,<1.11 +[testenv:2.7-np1.14-nocover] +basepython = {env:TOXPYTHON:python2.7} +deps = + {[testenv]deps} + numpy>=1.14,<1.15 + [testenv:2.7-np1.8] basepython = {env:TOXPYTHON:python2.7} setenv = @@ -279,6 +316,27 @@ deps = {[testenv]deps} numpy>=1.10,<1.11 +[testenv:3.4-np1.14] +basepython = {env:TOXPYTHON:python3.4} +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +deps = + {[testenv]deps} + pytest-cov + numpy>=1.14,<1.15 + +[testenv:3.4-np1.14-nocover] +basepython = {env:TOXPYTHON:python3.4} +deps = + {[testenv]deps} + numpy>=1.14,<1.15 + [testenv:3.4-np1.8] basepython = {env:TOXPYTHON:python3.4} setenv = @@ -342,6 +400,27 @@ deps = {[testenv]deps} numpy>=1.10,<1.11 +[testenv:3.5-np1.14] +basepython = {env:TOXPYTHON:python3.5} +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +deps = + {[testenv]deps} + pytest-cov + numpy>=1.14,<1.15 + +[testenv:3.5-np1.14-nocover] +basepython = {env:TOXPYTHON:python3.5} +deps = + {[testenv]deps} + numpy>=1.14,<1.15 + [testenv:3.5-np1.8] basepython = {env:TOXPYTHON:python3.5} setenv = @@ -384,6 +463,89 @@ deps = {[testenv]deps} numpy>=1.9,<1.10 +[testenv:3.6-np1.10] +basepython = {env:TOXPYTHON:python3.6} +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +deps = + {[testenv]deps} + pytest-cov + numpy>=1.10,<1.11 + +[testenv:3.6-np1.10-nocover] +basepython = {env:TOXPYTHON:python3.6} +deps = + {[testenv]deps} + numpy>=1.10,<1.11 + +[testenv:3.6-np1.14] +basepython = {env:TOXPYTHON:python3.6} +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +deps = + {[testenv]deps} + pytest-cov + numpy>=1.14,<1.15 + +[testenv:3.6-np1.14-nocover] +basepython = {env:TOXPYTHON:python3.6} +deps = + {[testenv]deps} + numpy>=1.14,<1.15 + +[testenv:3.6-np1.8] +basepython = {env:TOXPYTHON:python3.6} +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +deps = + {[testenv]deps} + pytest-cov + numpy>=1.8,<1.9 + +[testenv:3.6-np1.8-nocover] +basepython = {env:TOXPYTHON:python3.6} +deps = + {[testenv]deps} + numpy>=1.8,<1.9 + +[testenv:3.6-np1.9] +basepython = {env:TOXPYTHON:python3.6} +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +deps = + {[testenv]deps} + pytest-cov + numpy>=1.9,<1.10 + +[testenv:3.6-np1.9-nocover] +basepython = {env:TOXPYTHON:python3.6} +deps = + {[testenv]deps} + numpy>=1.9,<1.10 [testenv:2.7-buildonly-nocover] basepython = {env:TOXPYTHON:python2.7} @@ -409,4 +571,10 @@ deps = skip_install = true commands = +[testenv:3.6-buildonly-nocover] +basepython = {env:TOXPYTHON:python3.6} +deps = +skip_install = true +commands = + From 84b7b95c403544c23faadbb647dd16868da08b35 Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 12 Mar 2018 13:24:02 -0500 Subject: [PATCH 010/101] Import bugfix Fixed handling of routines under aacgmv2 and _aacgmv2. --- aacgmv2/__init__.py | 71 +++++++++++++++++------------------------ aacgmv2/__main__.py | 17 ++++++---- aacgmv2/aacgmv2module.c | 8 ++--- aacgmv2/wrapper.py | 44 +++++++++++++------------ 4 files changed, 68 insertions(+), 72 deletions(-) diff --git a/aacgmv2/__init__.py b/aacgmv2/__init__.py index 87fd9beb..848a26ee 100644 --- a/aacgmv2/__init__.py +++ b/aacgmv2/__init__.py @@ -1,31 +1,38 @@ # -*- coding: utf-8 -*- """aacgmv2 +Modules +--------------------------------------------------------------------------- +_aacgmv2 : Contains functions and variables from c code +--------------------------------------------------------------------------- + Parameters ------------ +--------------------------------------------------------------------------- AACGM_v2_DAT_PREFIX IGRF_12_COEFFS -G2A -A2G -TRACE -ALLOWTRACE -BADIDEA -GEOCENTRIC +_aacgmv2.G2A +_aacgmv2.A2G +_aacgmv2.TRACE +_aacgmv2.ALLOWTRACE +_aacgmv2.BADIDEA +_aacgmv2.GEOCENTRIC +--------------------------------------------------------------------------- Functions ------------- -convert_latlon +--------------------------------------------------------------------------- convert_latlon_arr convert_str_to_bit convert_bool_to_bit get_aacgm_coord get_aacgm_coord_arr -convert -set_datetime -mlt_convert -mlt_convert_yrsec -inv_mlt_convert -inv_mlt_convert_yrsec +_aacgmv2.convert +_aacgmv2.set_datetime +_aacgmv2.mlt_convert +_aacgmv2.mlt_convert_yrsec +_aacgmv2.inv_mlt_convert +_aacgmv2.inv_mlt_convert_yrsec +--------------------------------------------------------------------------- + """ import os.path as _path import logging @@ -37,35 +44,17 @@ IGRF_12_COEFFS = _path.join(_path.realpath(_path.dirname(__file__)), 'igrf12coeffs.txt') -try: - from wrapper import convert_latlon, convert_str_to_bit, get_aacgm_coord - from wrapper import convert_latlon_arr, get_aacgm_coord_arr - from wrapper import convert_bool_to_bit -except Exception, e: - logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) - -try: - from aacgmv2._aacgmv2 import convert -except Exception, e: - logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) - -try: - from aacgmv2._aacgmv2 import set_datetime -except Exception, e: - logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) - -try: - from aacgmv2._aacgmv2 import mlt_convert, inv_mlt_convert -except Exception, e: - logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) +# Imports +#--------------------------------------------------------------------- try: - from aacgmv2._aacgmv2 import mlt_convert_yrsec, inv_mlt_convert_yrsec -except Exception, e: + from aacgmv2.wrapper import (convert_latlon, get_aacgm_coord) + from aacgmv2.wrapper import (convert_latlon_arr, get_aacgm_coord_arr) + from aacgmv2.wrapper import (convert_bool_to_bit, convert_str_to_bit) +except Exception as e: logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) try: - from aacgmv2._aacgmv2 import (G2A, A2G, TRACE, ALLOWTRACE, BADIDEA, - GEOCENTRIC) -except Exception, e: + from aacgmv2 import (_aacgmv2) +except Exception as e: logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) diff --git a/aacgmv2/__main__.py b/aacgmv2/__main__.py index 6d3cf857..a486dbbe 100644 --- a/aacgmv2/__main__.py +++ b/aacgmv2/__main__.py @@ -100,14 +100,17 @@ def main(): elif args.subcommand == 'mlt_convert': dtime = dt.datetime.strptime(args.datetime, '%Y%m%d%H%M%S') if args.m2a: - out = aacgmv2.inv_mlt_convert(dtime.year, dtime.month, dtime.day, - dtime.hour, dtime.minute, - dtime.second, array[:,1], - aacgmv2.IGRF_12_COEFFS) + out = aacgmv2._aacgmv2.inv_mlt_convert(dtime.year, dtime.month, + dtime.day, dtime.hour, + dtime.minute, dtime.second, + array[:,1], + aacgmv2.IGRF_12_COEFFS) else: - out = aacgmv2.mlt_convert(dtime.year, dtime.month, dtime.day, - dtime.hour, dtime.minute, dtime.second, - array[:,1], aacgmv2.IGRF_12_COEFFS) + out = aacgmv2._aacgmv2.mlt_convert(dtime.year, dtime.month, + dtime.day, dtime.hour, + dtime.minute, dtime.second, + array[:,1], + aacgmv2.IGRF_12_COEFFS) np.savetxt(args.file_out, out, fmt='%.8f') diff --git a/aacgmv2/aacgmv2module.c b/aacgmv2/aacgmv2module.c index 06d701c8..6875bd69 100644 --- a/aacgmv2/aacgmv2module.c +++ b/aacgmv2/aacgmv2module.c @@ -316,14 +316,14 @@ mlon : (float)\n\ #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef aacgmv2module = { PyModuleDef_HEAD_INIT, - "aacgmv2", /* name of module */ + "_aacgmv2", /* name of module */ "Interface to the AACGM-v2 C library.", /* module documentation */ -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ aacgm_v2_methods }; - PyMODINIT_FUNC PyInit_aacgm(void) + PyMODINIT_FUNC PyInit__aacgmv2(void) { module = PyModule_Create(&aacgmv2module); PyModule_AddIntConstant(module, "G2A", G2A); @@ -337,9 +337,9 @@ mlon : (float)\n\ #else - PyMODINIT_FUNC initaacgm(void) + PyMODINIT_FUNC init_aacgmv2(void) { - module = Py_InitModule("aacgm", aacgm_v2_methods); + module = Py_InitModule("_aacgmv2", aacgm_v2_methods); PyModule_AddIntConstant(module, "G2A", G2A); PyModule_AddIntConstant(module, "A2G", A2G); PyModule_AddIntConstant(module, "TRACE", TRACE); diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index e1ccfca2..6c7ffa61 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -105,13 +105,14 @@ def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, in_lon = ((in_lon + 180.0) % 360.0) - 180.0 # Set current date and time - aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, dtime.hour, - dtime.minute, dtime.second, coeff_prefix) + aacgmv2._aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, + dtime.hour, dtime.minute, dtime.second, + coeff_prefix) # convert - lat_out, lon_out, r_out = aacgmv2.convert(in_lat, in_lon, height, bit_code, - igrf_file) + lat_out, lon_out, r_out = aacgmv2._aacgmv2.convert(in_lat, in_lon, height, + bit_code, igrf_file) return lat_out, lon_out, r_out @@ -223,11 +224,12 @@ def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", in_lon = ((in_lon + 180.0) % 360.0) - 180.0 # Set current date and time - aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, dtime.hour, - dtime.minute, dtime.second, coeff_prefix) + aacgmv2._aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, + dtime.hour, dtime.minute, dtime.second, + coeff_prefix) # Vectorise the AACGM code - convert_vectorised = np.vectorize(aacgmv2.convert) + convert_vectorised = np.vectorize(aacgmv2._aacgmv2.convert) # convert lat_out, lon_out, r_out = convert_vectorised(in_lat, in_lon, height, @@ -292,9 +294,10 @@ def get_aacgm_coord(glat, glon, height, dtime, method="TRACE", igrf_file=igrf_file, coeff_prefix=coeff_prefix) # Get magnetic local time - mlt = aacgmv2.mlt_convert(dtime.year, dtime.month, dtime.day, - dtime.hour, dtime.minute, dtime.second, mlon, - coeff_prefix, igrf_file) + mlt = aacgmv2._aacgmv2.mlt_convert(dtime.year, dtime.month, dtime.day, + dtime.hour, dtime.minute, + dtime.second, mlon, + coeff_prefix, igrf_file) except: logging.error("Unable to get magnetic lat/lon") @@ -360,7 +363,7 @@ def get_aacgm_coord_arr(glat, glon, height, dtime, method="TRACE", if mlon is not None: # Get magnetic local time - mlt_vectorised = np.vectorize(aacgmv2.mlt_convert) + mlt_vectorised = np.vectorize(aacgmv2._aacgmv2.mlt_convert) mlt = mlt_vectorised(dtime.year, dtime.month, dtime.day, dtime.hour, dtime.minute, dtime.second, mlon, coeff_prefix, igrf_file) @@ -388,10 +391,11 @@ def convert_str_to_bit(code): bit_code : (int) code specification in bits """ - convert_code = {"G2A": aacgmv2.G2A, "A2G": aacgmv2.A2G, - "TRACE": aacgmv2.TRACE, "GEOCENTRIC": aacgmv2.GEOCENTRIC, - "ALLOWTRACE": aacgmv2.ALLOWTRACE, - "BADIDEA": aacgmv2.BADIDEA} + convert_code = {"G2A": aacgmv2._aacgmv2.G2A, "A2G": aacgmv2._aacgmv2.A2G, + "TRACE": aacgmv2._aacgmv2.TRACE, + "GEOCENTRIC": aacgmv2._aacgmv2.GEOCENTRIC, + "ALLOWTRACE": aacgmv2._aacgmv2.ALLOWTRACE, + "BADIDEA": aacgmv2._aacgmv2.BADIDEA} code = code.upper() @@ -423,15 +427,15 @@ def convert_bool_to_bit(a2g=False, trace=False, allowtrace=False, bit_code : (int) code specification in bits """ - bit_code = aacgmv2.A2G if a2g else aacgmv2.G2A + bit_code = aacgmv2._aacgmv2.A2G if a2g else aacgmv2._aacgmv2.G2A if trace: - bit_code += aacgmv2.TRACE + bit_code += aacgmv2._aacgmv2.TRACE if allowtrace: - bit_code += aacgmv2.ALLOWTRACE + bit_code += aacgmv2._aacgmv2.ALLOWTRACE if badidea: - bit_code += aacgmv2.BADIDEA + bit_code += aacgmv2._aacgmv2.BADIDEA if geocentric: - bit_code += aacgmv2.GEOCENTRIC + bit_code += aacgmv2._aacgmv2.GEOCENTRIC return bit_code From b862e410056ba5b4752caff2f7140425a82dd1fd Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 12 Mar 2018 19:31:42 -0500 Subject: [PATCH 011/101] Error message update Updated code to improve array functionality and error messages --- aacgmv2/wrapper.py | 122 ++++++++++++++++++++++++++++----------------- 1 file changed, 77 insertions(+), 45 deletions(-) diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index 6c7ffa61..f3afb8e7 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -17,6 +17,7 @@ import datetime as dt import logging import aacgmv2 +import aacgmv2._aacgmv2 as c_aacgmv2 def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, coeff_prefix=None): @@ -76,8 +77,13 @@ def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, if height < 0: logging.warn('conversion not intended for altitudes < 0 km') + # Initialise output + lat_out = np.nan + lon_out = np.nan + r_out = np.nan + # Test code - if isinstance(code, str): + try: code = code.upper() if(height > 2000 and code.find("TRACE") < 0 and @@ -87,10 +93,11 @@ def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, estr += 'or allowtrace=True) or indicate you know this ' estr += 'is a bad idea' logging.error(estr) + return lat_out, lon_out, r_out # make flag bit_code = convert_str_to_bit(code) - else: + except: bit_code = code assert isinstance(bit_code, int), \ @@ -105,29 +112,29 @@ def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, in_lon = ((in_lon + 180.0) % 360.0) - 180.0 # Set current date and time - aacgmv2._aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, - dtime.hour, dtime.minute, dtime.second, - coeff_prefix) - + c_aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, dtime.hour, + dtime.minute, dtime.second, coeff_prefix) - # convert - lat_out, lon_out, r_out = aacgmv2._aacgmv2.convert(in_lat, in_lon, height, - bit_code, igrf_file) + # convert location + try: + lat_out, lon_out, r_out = c_aacgmv2.convert(in_lat, in_lon, height, + bit_code, igrf_file) + except: pass return lat_out, lon_out, r_out - def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, coeff_prefix=None): - """Converts between geomagnetic coordinates and AACGM coordinates + """Converts between geomagnetic coordinates and AACGM coordinates. At least + one of in_lat, in_lon, and height must be a list or array Parameters ------------ - in_lat : (np.ndarray) + in_lat : (np.ndarray or list or float) Input latitude in degrees N (code specifies type of latitude) - in_lon : (np.ndarray) + in_lon : (np.ndarray or list or float) Input longitude in degrees E (code specifies type of longitude) - height : (np.ndarray) + height : (np.ndarray or list or float) Altitude above the surface of the earth in km dtime : (datetime) Single datetime object for magnetic field @@ -156,9 +163,7 @@ def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", out_r : (np.ndarray) Geocentric radial distances in R """ - # If someone was lazy and entered a list instead of a numpy array, - # recast it here - + # If a list was entered instead of a numpy array, recast it here if isinstance(in_lat, list): in_lat = np.array(in_lat) @@ -168,12 +173,34 @@ def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", if isinstance(height, list): height = np.array(height) + # If one or two of these elements is a float or int, create an array + test_array = np.array([hasattr(in_lat, "shape"), hasattr(in_lon, "shape"), + hasattr(height, "shape")]) + if not test_array.all(): + if test_array.any(): + arr_shape = in_lat.shape if test_array.argmax() == 0 else \ + (in_lon.shape if test_array.argmax() == 1 else + height.shape) + if not test_array[0]: + in_lat = np.ones(shape=arr_shape, dtype=float) * in_lat + if not test_array[1]: + in_lon = np.ones(shape=arr_shape, dtype=float) * in_lon + if not test_array[2]: + height = np.ones(shape=arr_shape, dtype=float) * height + else: + logging.info("for a single location, consider using convert_latlon") + in_lat = np.array([in_lat]) + in_lon = np.array([in_lon]) + height = np.array([height]) + # Ensure that lat, lon, and height are the same length or if the lengths # differ that the different ones contain only a single value - ulen = np.unique([height.shape, in_lat.shape, in_lon.shape]) - if ulen.shape[0] > 2 or (ulen.shape[0] == 2 and ulen[0] > 1): - logging.error("mismatched input arrays") - return None, None, None + if not (in_lat.shape == in_lon.shape and in_lat.shape == height.shape): + ulen = np.unique([in_lat.shape, in_lon.shape, height.shape]) + if ulen.min() != (1,): + logging.error("mismatched input arrays") + sys.exit(1) + return None, None, None # Define coefficient file prefix if not supplied if coeff_prefix is None: @@ -194,21 +221,27 @@ def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", if np.min(height) < 0: logging.warn('conversion not intended for altitudes < 0 km') + # Initialise output + lat_out = np.empty(shape=in_lat.shape, dtype=float) * np.nan + lon_out = np.empty(shape=in_lon.shape, dtype=float) * np.nan + r_out = np.empty(shape=height.shape, dtype=float) * np.nan + # Test code - if isinstance(code, str): + try: code = code.upper() - if(np.max(height) > 2000 and code.find("TRACE") < 0 and + if(np.nanmax(height) > 2000 and code.find("TRACE") < 0 and code.find("ALLOWTRACE") < 0 and code.find("BADIDEA")): estr = 'coefficients are not valid for altitudes above 2000 km. You' estr += ' must either use field-line tracing (trace=True ' estr += 'or allowtrace=True) or indicate you know this ' estr += 'is a bad idea' logging.error(estr) + return lat_out, lon_out, r_out # make flag bit_code = convert_str_to_bit(code) - else: + except: bit_code = code assert isinstance(bit_code, int), \ @@ -224,16 +257,17 @@ def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", in_lon = ((in_lon + 180.0) % 360.0) - 180.0 # Set current date and time - aacgmv2._aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, - dtime.hour, dtime.minute, dtime.second, - coeff_prefix) + c_aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, dtime.hour, + dtime.minute, dtime.second, coeff_prefix) # Vectorise the AACGM code - convert_vectorised = np.vectorize(aacgmv2._aacgmv2.convert) + convert_vectorised = np.vectorize(c_aacgmv2.convert) # convert - lat_out, lon_out, r_out = convert_vectorised(in_lat, in_lon, height, - bit_code, igrf_file) + try: + lat_out, lon_out, r_out = convert_vectorised(in_lat, in_lon, height, + bit_code, igrf_file) + except: pass return lat_out, lon_out, r_out @@ -294,10 +328,9 @@ def get_aacgm_coord(glat, glon, height, dtime, method="TRACE", igrf_file=igrf_file, coeff_prefix=coeff_prefix) # Get magnetic local time - mlt = aacgmv2._aacgmv2.mlt_convert(dtime.year, dtime.month, dtime.day, - dtime.hour, dtime.minute, - dtime.second, mlon, - coeff_prefix, igrf_file) + mlt = c_aacgmv2.mlt_convert(dtime.year, dtime.month, dtime.day, + dtime.hour, dtime.minute, dtime.second, + mlon, coeff_prefix, igrf_file) except: logging.error("Unable to get magnetic lat/lon") @@ -363,7 +396,7 @@ def get_aacgm_coord_arr(glat, glon, height, dtime, method="TRACE", if mlon is not None: # Get magnetic local time - mlt_vectorised = np.vectorize(aacgmv2._aacgmv2.mlt_convert) + mlt_vectorised = np.vectorize(c_aacgmv2.mlt_convert) mlt = mlt_vectorised(dtime.year, dtime.month, dtime.day, dtime.hour, dtime.minute, dtime.second, mlon, coeff_prefix, igrf_file) @@ -391,11 +424,10 @@ def convert_str_to_bit(code): bit_code : (int) code specification in bits """ - convert_code = {"G2A": aacgmv2._aacgmv2.G2A, "A2G": aacgmv2._aacgmv2.A2G, - "TRACE": aacgmv2._aacgmv2.TRACE, - "GEOCENTRIC": aacgmv2._aacgmv2.GEOCENTRIC, - "ALLOWTRACE": aacgmv2._aacgmv2.ALLOWTRACE, - "BADIDEA": aacgmv2._aacgmv2.BADIDEA} + convert_code = {"G2A": c_aacgmv2.G2A, "A2G": c_aacgmv2.A2G, + "TRACE": c_aacgmv2.TRACE, "BADIDEA": c_aacgmv2.BADIDEA, + "GEOCENTRIC": c_aacgmv2.GEOCENTRIC, + "ALLOWTRACE": c_aacgmv2.ALLOWTRACE} code = code.upper() @@ -427,15 +459,15 @@ def convert_bool_to_bit(a2g=False, trace=False, allowtrace=False, bit_code : (int) code specification in bits """ - bit_code = aacgmv2._aacgmv2.A2G if a2g else aacgmv2._aacgmv2.G2A + bit_code = c_aacgmv2.A2G if a2g else c_aacgmv2.G2A if trace: - bit_code += aacgmv2._aacgmv2.TRACE + bit_code += c_aacgmv2.TRACE if allowtrace: - bit_code += aacgmv2._aacgmv2.ALLOWTRACE + bit_code += c_aacgmv2.ALLOWTRACE if badidea: - bit_code += aacgmv2._aacgmv2.BADIDEA + bit_code += c_aacgmv2.BADIDEA if geocentric: - bit_code += aacgmv2._aacgmv2.GEOCENTRIC + bit_code += c_aacgmv2.GEOCENTRIC return bit_code From 2b655079ba69e626fb5ed8e49f6bdd46487d642e Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 12 Mar 2018 19:32:08 -0500 Subject: [PATCH 012/101] Move files to data directory Move test files to a file directory --- aacgmv2/tests/{ => test_data}/test_convert.txt | 0 aacgmv2/tests/{ => test_data}/test_convert_mlt.txt | 0 aacgmv2/tests/{ => test_data}/test_convert_mlt_single_line.txt | 0 aacgmv2/tests/{ => test_data}/test_convert_single_line.txt | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename aacgmv2/tests/{ => test_data}/test_convert.txt (100%) rename aacgmv2/tests/{ => test_data}/test_convert_mlt.txt (100%) rename aacgmv2/tests/{ => test_data}/test_convert_mlt_single_line.txt (100%) rename aacgmv2/tests/{ => test_data}/test_convert_single_line.txt (100%) diff --git a/aacgmv2/tests/test_convert.txt b/aacgmv2/tests/test_data/test_convert.txt similarity index 100% rename from aacgmv2/tests/test_convert.txt rename to aacgmv2/tests/test_data/test_convert.txt diff --git a/aacgmv2/tests/test_convert_mlt.txt b/aacgmv2/tests/test_data/test_convert_mlt.txt similarity index 100% rename from aacgmv2/tests/test_convert_mlt.txt rename to aacgmv2/tests/test_data/test_convert_mlt.txt diff --git a/aacgmv2/tests/test_convert_mlt_single_line.txt b/aacgmv2/tests/test_data/test_convert_mlt_single_line.txt similarity index 100% rename from aacgmv2/tests/test_convert_mlt_single_line.txt rename to aacgmv2/tests/test_data/test_convert_mlt_single_line.txt diff --git a/aacgmv2/tests/test_convert_single_line.txt b/aacgmv2/tests/test_data/test_convert_single_line.txt similarity index 100% rename from aacgmv2/tests/test_convert_single_line.txt rename to aacgmv2/tests/test_data/test_convert_single_line.txt From 3d8a4e160c8f02d4e25265c2a071603ec3b702b7 Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 12 Mar 2018 19:32:26 -0500 Subject: [PATCH 013/101] Update tests Update tests to reflect current routines --- aacgmv2/tests/test_c_aacgmv2.py | 435 ++++++++++------ aacgmv2/tests/test_cmd_aacgmv2.py | 268 +++++----- aacgmv2/tests/test_py_aacgmv2.py | 800 ++++++++++++++++++++++-------- 3 files changed, 1009 insertions(+), 494 deletions(-) diff --git a/aacgmv2/tests/test_c_aacgmv2.py b/aacgmv2/tests/test_c_aacgmv2.py index 1b95e749..03da9190 100644 --- a/aacgmv2/tests/test_c_aacgmv2.py +++ b/aacgmv2/tests/test_c_aacgmv2.py @@ -1,159 +1,286 @@ -from __future__ import division, print_function, absolute_import, unicode_literals +# -*- coding: utf-8 -*- +from __future__ import division, absolute_import, unicode_literals import numpy as np import pytest - import aacgmv2 -from aacgmv2._aacgmv2 import A2G, G2A, TRACE, BADIDEA, ALLOWTRACE, GEOCENTRIC - - -def test_module_structure(): - assert aacgmv2 - assert aacgmv2._aacgmv2 - assert aacgmv2._aacgmv2.setDateTime - assert aacgmv2._aacgmv2.aacgmConvert - - -def test_constants(): - assert aacgmv2._aacgmv2.G2A == 0 - assert aacgmv2._aacgmv2.A2G == 1 - assert aacgmv2._aacgmv2.TRACE == 2 - assert aacgmv2._aacgmv2.ALLOWTRACE == 4 - assert aacgmv2._aacgmv2.BADIDEA == 8 - assert aacgmv2._aacgmv2.GEOCENTRIC == 16 - - -def test_setDateTime(): - assert aacgmv2._aacgmv2.setDateTime(2013, 1, 1, 0, 0, 0) is None - assert aacgmv2._aacgmv2.setDateTime(2015, 3, 4, 5, 6, 7) is None - assert aacgmv2._aacgmv2.setDateTime(2017, 12, 31, 23, 59, 59) is None - - -def test_aacgmConvert_G2A_coeff(): - aacgmv2._aacgmv2.setDateTime(2014, 3, 22, 3, 11, 0) - - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(45.5, -23.5, 1135, G2A) - np.testing.assert_almost_equal(mlat, 48.1896, decimal=4) - np.testing.assert_almost_equal(mlon, 57.7635, decimal=4) - assert r == 1 - - aacgmv2._aacgmv2.setDateTime(2018, 1, 1, 0, 0, 0) - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(60, 0, 300, G2A) - np.testing.assert_almost_equal(mlat, 58.1633, decimal=4) - np.testing.assert_almost_equal(mlon, 81.0719, decimal=4) - assert r == 1 - - -def test_aacgmConvert_A2G_coeff(): - aacgmv2._aacgmv2.setDateTime(2014, 3, 22, 3, 11, 0) - - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(45.5, -23.5, 1135, A2G) - np.testing.assert_almost_equal(mlat, 30.7534, decimal=4) - np.testing.assert_almost_equal(mlon, -94.1806, decimal=4) - assert r == 1 - - aacgmv2._aacgmv2.setDateTime(2018, 1, 1, 0, 0, 0) - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(60, 0, 300, A2G) - np.testing.assert_almost_equal(mlat, 50.3910, decimal=4) - np.testing.assert_almost_equal(mlon, -77.7919, decimal=4) - assert r == 1 - - -def test_aacgmConvert_G2A_TRACE(): - aacgmv2._aacgmv2.setDateTime(2014, 3, 22, 3, 11, 0) - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(45.5, -23.5, 1135, G2A | TRACE) - np.testing.assert_almost_equal(mlat, 48.1948, decimal=4) - np.testing.assert_almost_equal(mlon, 57.7588, decimal=4) - assert r == 1 - - aacgmv2._aacgmv2.setDateTime(2018, 1, 1, 0, 0, 0) - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(60, 0, 300, G2A | TRACE) - np.testing.assert_almost_equal(mlat, 58.1633, decimal=4) - np.testing.assert_almost_equal(mlon, 81.0756, decimal=4) - assert r == 1 - - -def test_aacgmConvert_A2G_TRACE(): - aacgmv2._aacgmv2.setDateTime(2014, 3, 22, 3, 11, 0) - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(45.5, -23.5, 1135, A2G | TRACE) - np.testing.assert_almost_equal(mlat, 30.7644, decimal=4) - np.testing.assert_almost_equal(mlon, -94.1809, decimal=4) - assert r == 1 - - aacgmv2._aacgmv2.setDateTime(2018, 1, 1, 0, 0, 0) - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(60, 0, 300, A2G | TRACE) - np.testing.assert_almost_equal(mlat, 50.3958, decimal=4) - np.testing.assert_almost_equal(mlon, -77.8019, decimal=4) - assert r == 1 - - -def test_aacgmConvert_high_denied(): - aacgmv2._aacgmv2.setDateTime(2014, 3, 22, 3, 11, 0) - with pytest.raises(RuntimeError): - aacgmv2._aacgmv2.aacgmConvert(45.5, -23.5, 5500, G2A) - - -def test_aacgmConvert_high_TRACE(): - aacgmv2._aacgmv2.setDateTime(2014, 3, 22, 3, 11, 0) - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(45.5, -23.5, 5500, G2A | TRACE) - np.testing.assert_almost_equal(mlat, 59.9748, decimal=4) - np.testing.assert_almost_equal(mlon, 57.7425, decimal=4) - assert r == 1 - - -def test_aacgmConvert_high_ALLOWTRACE(): - aacgmv2._aacgmv2.setDateTime(2014, 3, 22, 3, 11, 0) - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(45.5, -23.5, 5500, G2A | ALLOWTRACE) - np.testing.assert_almost_equal(mlat, 59.9748, decimal=4) - np.testing.assert_almost_equal(mlon, 57.7425, decimal=4) - assert r == 1 - - -def test_aacgmConvert_high_BADIDEA(): - aacgmv2._aacgmv2.setDateTime(2014, 3, 22, 3, 11, 0) - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(45.5, -23.5, 5500, G2A | BADIDEA) - np.testing.assert_almost_equal(mlat, 58.7154, decimal=4) - np.testing.assert_almost_equal(mlon, 56.5830, decimal=4) - assert r == 1 - - -def test_aacgmConvert_GEOCENTRIC_G2A_coeff(): - aacgmv2._aacgmv2.setDateTime(2014, 3, 22, 3, 11, 0) - - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(45.5, -23.5, 1135, G2A | GEOCENTRIC) - np.testing.assert_almost_equal(mlat, 48.3779, decimal=4) - np.testing.assert_almost_equal(mlon, 57.7974, decimal=4) - assert r == 1 - - -def test_aacgmConvert_GEOCENTRIC_A2G_coeff(): - aacgmv2._aacgmv2.setDateTime(2014, 3, 22, 3, 11, 0) - - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(45.5, -23.5, 1135, A2G | GEOCENTRIC) - np.testing.assert_almost_equal(mlat, 30.6101, decimal=4) - np.testing.assert_almost_equal(mlon, -94.1806, decimal=4) - assert r == 1 - - -def test_aacgmConvert_GEOCENTRIC_G2A_TRACE(): - aacgmv2._aacgmv2.setDateTime(2014, 3, 22, 3, 11, 0) - - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(45.5, -23.5, 1135, G2A | TRACE | GEOCENTRIC) - np.testing.assert_almost_equal(mlat, 48.3830, decimal=4) - np.testing.assert_almost_equal(mlon, 57.7926, decimal=4) - assert r == 1 - - -def test_aacgmConvert_GEOCENTRIC_A2G_TRACE(): - aacgmv2._aacgmv2.setDateTime(2014, 3, 22, 3, 11, 0) - - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(45.5, -23.5, 1135, A2G | TRACE | GEOCENTRIC) - np.testing.assert_almost_equal(mlat, 30.6211, decimal=4) - np.testing.assert_almost_equal(mlon, -94.1809, decimal=4) - assert r == 1 - -def test_forbidden(): - mlat, mlon, r = aacgmv2._aacgmv2.aacgmConvert(7, 0, 0, G2A) - assert np.isnan(mlat) - assert np.isnan(mlon) +class TestCAACGMV2: + def setup(self): + """Runs before every method to create a clean testing setup""" + self.date1_args = (2014, 3, 22, 3, 11, 0, aacgmv2.AACGM_v2_DAT_PREFIX) + self.date2_args = (2018, 1, 1, 0, 0, 0, aacgmv2.AACGM_v2_DAT_PREFIX) + self.long_date = [2014, 3, 22, 3, 11, 0] + + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.date1_args, self.date2_args, self.long_date + + def test_module_structure(self): + """Test module structure""" + assert aacgmv2 + assert aacgmv2._aacgmv2 + assert aacgmv2._aacgmv2.set_datetime + assert aacgmv2._aacgmv2.convert + assert aacgmv2._aacgmv2.inv_mlt_convert + assert aacgmv2._aacgmv2.inv_mlt_convert_yrsec + assert aacgmv2._aacgmv2.mlt_convert + assert aacgmv2._aacgmv2.mlt_convert_yrsec + + def test_constants(self): + """Test module constants""" + ans1 = aacgmv2._aacgmv2.G2A == 0 + ans2 = aacgmv2._aacgmv2.A2G == 1 + ans3 = aacgmv2._aacgmv2.TRACE == 2 + ans4 = aacgmv2._aacgmv2.ALLOWTRACE == 4 + ans5 = aacgmv2._aacgmv2.BADIDEA == 8 + ans6 = aacgmv2._aacgmv2.GEOCENTRIC == 16 + + assert ans1 & ans2 & ans3 & ans4 & ans5 & ans6 + + def test_set_datetime(self): + """Test set_datetime""" + ans1 = aacgmv2._aacgmv2.set_datetime(*self.date1_args) is None + ans2 = aacgmv2._aacgmv2.set_datetime(*self.date2_args) is None + + assert ans1 & ans2 + + def test_fail_set_datetime(self): + """Test unsuccessful set_datetime""" + with pytest.raises(RuntimeError): + aacgmv2._aacgmv2.set_datetime(1013, 1, 1, 0, 0, 0, + aacgmv2.AACGM_v2_DAT_PREFIX) + + def test_convert_G2A_coeff(self): + """Test convert from geographic to magnetic coordinates""" + code = aacgmv2._aacgmv2.G2A + + aacgmv2._aacgmv2.set_datetime(*self.date1_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 48.1896, decimal=4) + np.testing.assert_almost_equal(mlon, 57.7635, decimal=4) + np.testing.assert_almost_equal(r, 1.1775, decimal=4) + + aacgmv2._aacgmv2.set_datetime(*self.date2_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(60, 0, 300, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 58.1633, decimal=4) + np.testing.assert_almost_equal(mlon, 81.0719, decimal=4) + np.testing.assert_almost_equal(r, 1.0457, decimal=4) + + def test_convert_A2G_coeff(self): + """Test convert from magnetic to geodetic coordinates""" + code = aacgmv2._aacgmv2.A2G + + aacgmv2._aacgmv2.set_datetime(*self.date1_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 30.7534, decimal=4) + np.testing.assert_almost_equal(mlon, -94.1806, decimal=4) + np.testing.assert_almost_equal(r, 1133.6241, decimal=4) + + aacgmv2._aacgmv2.set_datetime(*self.date2_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(60, 0, 300, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 50.3910, decimal=4) + np.testing.assert_almost_equal(mlon, -77.7919, decimal=4) + np.testing.assert_almost_equal(r, 305.7138, decimal=4) + + def test_convert_G2A_TRACE(self): + """Test convert from geodetic to magnetic coordinates using trace""" + code = aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.TRACE + + aacgmv2._aacgmv2.set_datetime(*self.date1_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 48.1948, decimal=4) + np.testing.assert_almost_equal(mlon, 57.7588, decimal=4) + np.testing.assert_almost_equal(r, 1.1775, decimal=4) + + aacgmv2._aacgmv2.set_datetime(*self.date2_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(60, 0, 300, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 58.1633, decimal=4) + np.testing.assert_almost_equal(mlon, 81.0756, decimal=4) + np.testing.assert_almost_equal(r, 1.0457, decimal=4) + + def test_convert_A2G_TRACE(self): + """Test convert from magnetic to geodetic coordinates using trace""" + code = aacgmv2._aacgmv2.A2G + aacgmv2._aacgmv2.TRACE + aacgmv2._aacgmv2.set_datetime(*self.date1_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 30.7644, decimal=4) + np.testing.assert_almost_equal(mlon, -94.1809, decimal=4) + np.testing.assert_almost_equal(r, 1133.6277, decimal=4) + + aacgmv2._aacgmv2.set_datetime(*self.date2_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(60, 0, 300, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 50.3958, decimal=4) + np.testing.assert_almost_equal(mlon, -77.8019, decimal=4) + np.testing.assert_almost_equal(r, 305.7156, decimal=4) + + def test_convert_high_denied(self): + """Test for failure when converting to high altitude geodetic to + magnetic coordinates""" + aacgmv2._aacgmv2.set_datetime(*self.date1_args) + with pytest.raises(RuntimeError): + aacgmv2._aacgmv2.convert(45.5, -23.5, 5500, aacgmv2._aacgmv2.G2A, + aacgmv2.IGRF_12_COEFFS) + + def test_convert_high_TRACE(self): + """Test convert from high altitude geodetic to magnetic coordinates + using trace""" + code = aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.TRACE + aacgmv2._aacgmv2.set_datetime(*self.date1_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 5500, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 59.9748, decimal=4) + np.testing.assert_almost_equal(mlon, 57.7425, decimal=4) + np.testing.assert_almost_equal(r, 1.8626, decimal=4) + + def test_convert_high_ALLOWTRACE(self): + """Test convert from high altitude geodetic to magnetic coordinates + by allowing IGRF tracing""" + code = aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.ALLOWTRACE + aacgmv2._aacgmv2.set_datetime(*self.date1_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 5500, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 59.9748, decimal=4) + np.testing.assert_almost_equal(mlon, 57.7425, decimal=4) + np.testing.assert_almost_equal(r, 1.8626, decimal=4) + + def test_convert_high_BADIDEA(self): + """Test convert from high altitude geodetic to magnetic coordinates + using coefficients""" + code = aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.BADIDEA + aacgmv2._aacgmv2.set_datetime(*self.date1_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 5500, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 58.7154, decimal=4) + np.testing.assert_almost_equal(mlon, 56.5830, decimal=4) + np.testing.assert_almost_equal(r, 1.8626, decimal=4) + + def test_convert_GEOCENTRIC_G2A_coeff(self): + """Test convert from geographic to magnetic coordinates""" + code = aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.GEOCENTRIC + aacgmv2._aacgmv2.set_datetime(*self.date1_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 48.3779, decimal=4) + np.testing.assert_almost_equal(mlon, 57.7974, decimal=4) + np.testing.assert_almost_equal(r, 1.1781, decimal=4) + + def test_convert_GEOCENTRIC_A2G_coeff(self): + """Test convert from magnetic to geocentric coordinates""" + code = aacgmv2._aacgmv2.A2G + aacgmv2._aacgmv2.GEOCENTRIC + aacgmv2._aacgmv2.set_datetime(*self.date1_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 30.6101, decimal=4) + np.testing.assert_almost_equal(mlon, -94.1806, decimal=4) + np.testing.assert_almost_equal(r, 1135.0000, decimal=4) + + def test_convert_GEOCENTRIC_G2A_TRACE(self): + """Test convert from geographic to magnetic coordinates using trace""" + code = aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.TRACE + \ + aacgmv2._aacgmv2.GEOCENTRIC + aacgmv2._aacgmv2.set_datetime(*self.date1_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 48.3830, decimal=4) + np.testing.assert_almost_equal(mlon, 57.7926, decimal=4) + np.testing.assert_almost_equal(r, 1.1781, decimal=4) + + def test_convert_GEOCENTRIC_A2G_TRACE(self): + """Test convert from magnetic to geographic coordinates using trace""" + code = aacgmv2._aacgmv2.A2G + aacgmv2._aacgmv2.TRACE + \ + aacgmv2._aacgmv2.GEOCENTRIC + aacgmv2._aacgmv2.set_datetime(*self.date1_args) + mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(mlat, 30.6211, decimal=4) + np.testing.assert_almost_equal(mlon, -94.1809, decimal=4) + np.testing.assert_almost_equal(r, 1135.0000, decimal=4) + + def test_forbidden(self): + """Test convert failure""" + with pytest.raises(RuntimeError): + mloc = aacgmv2._aacgmv2.convert(7, 0, 0, aacgmv2._aacgmv2.G2A, + aacgmv2.IGRF_12_COEFFS) + + def test_inv_mlt_convert(self): + """Test MLT inversion""" + mlt_args_1 = list(flatten([long_date, 12.0, aacgmv2.IGRF_12_COEFFS])) + mlt_args_2 = list(flatten([long_date, 25.0, aacgmv2.IGRF_12_COEFFS])) + mlt_args_3 = list(flatten([long_date, -1.0, aacgmv2.IGRF_12_COEFFS])) + + mlon_1 = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args_1) + mlon_2 = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args_2) + mlon_3 = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args_3) + + np.testing.assert_almost_equal(mlon_1, -153.5339, decimal=4) + np.testing.assert_almost_equal(mlon_2, 41.4661, decimal=4) + np.testing.assert_almost_equal(mlon_2, 11.4661, decimal=4) + + def test_inv_mlt_convert_yrsec(self): + """Test MLT inversion with year and seconds of year""" + import datetime as dt + dtime = dt.datetime(*long_date) + soy = (int(dtime.strftime("%j"))-1) * 86400 + dtime.hour * 3600 + \ + dtime.minute * 60 + dtime.second + + mlt_args_1 = [dtime.year, soy, 12.0, aacgmv2.IGRF_12_COEFFS] + mlt_args_2 = [dtime.year, soy, 25.0, aacgmv2.IGRF_12_COEFFS] + mlt_args_3 = [dtime.year, soy, -1.0, aacgmv2.IGRF_12_COEFFS] + + mlon_1 = aacgmv2._aacgmv2.inv_mlt_convert_yrsec(*mlt_args_1) + mlon_2 = aacgmv2._aacgmv2.inv_mlt_convert_yrsec(*mlt_args_2) + mlon_3 = aacgmv2._aacgmv2.inv_mlt_convert_yrsec(*mlt_args_3) + + np.testing.assert_almost_equal(mlon_1, -153.5339, decimal=4) + np.testing.assert_almost_equal(mlon_2, 41.4661, decimal=4) + np.testing.assert_almost_equal(mlon_2, 11.4661, decimal=4) + + def test_mlt_convert(self): + """Test MLT calculation""" + mlt_args_1 = list(flatten([long_date, 270.0, + aacgmv2.AACGM_v2_DAT_PREFIX, + aacgmv2.IGRF_12_COEFFS])) + mlt_args_2 = list(flatten([long_date, 80.0, + aacgmv2.AACGM_v2_DAT_PREFIX, + aacgmv2.IGRF_12_COEFFS])) + mlt_args_3 = list(flatten([long_date, -90.0, + aacgmv2.AACGM_v2_DAT_PREFIX, + aacgmv2.IGRF_12_COEFFS])) + + mlt_1 = aacgmv2._aacgmv2.mlt_convert(*mlt_args_1) + mlt_2 = aacgmv2._aacgmv2.mlt_convert(*mlt_args_2) + mlt_3 = aacgmv2._aacgmv2.mlt_convert(*mlt_args_3) + + np.testing.assert_almost_equal(mlt_1, 16.2356, decimal=4) + np.testing.assert_almost_equal(mlt_2, 3.5689, decimal=4) + np.testing.assert_equal(mlt_1, mlt_3) + + def test_mlt_convert_yrsec(self): + """Test MLT calculation using year and seconds of year""" + import datetime as dt + dtime = dt.datetime(*long_date) + soy = (int(dtime.strftime("%j"))-1) * 86400 + dtime.hour * 3600 + \ + dtime.minute * 60 + dtime.second + mlt_args_1 = [dtime.year, soy, 270.0, aacgmv2.AACGM_v2_DAT_PREFIX, + aacgmv2.IGRF_12_COEFFS] + mlt_args_2 = [dtime.year, soy, 80.0, aacgmv2.AACGM_v2_DAT_PREFIX, + aacgmv2.IGRF_12_COEFFS] + mlt_args_3 = [dtime.year, soy, -90.0, aacgmv2.AACGM_v2_DAT_PREFIX, + aacgmv2.IGRF_12_COEFFS] + + mlt_1 = aacgmv2._aacgmv2.mlt_convert(*mlt_args_1) + mlt_2 = aacgmv2._aacgmv2.mlt_convert(*mlt_args_2) + mlt_3 = aacgmv2._aacgmv2.mlt_convert(*mlt_args_3) + + np.testing.assert_almost_equal(mlt_1, 16.2356, decimal=4) + np.testing.assert_almost_equal(mlt_2, 3.5689, decimal=4) + np.testing.assert_equal(mlt_1, mlt_3) diff --git a/aacgmv2/tests/test_cmd_aacgmv2.py b/aacgmv2/tests/test_cmd_aacgmv2.py index b6332aec..a60fbb38 100644 --- a/aacgmv2/tests/test_cmd_aacgmv2.py +++ b/aacgmv2/tests/test_cmd_aacgmv2.py @@ -1,134 +1,144 @@ # -*- coding: utf-8 -*- +from __future__ import division, absolute_import, unicode_literals -from __future__ import division, print_function, absolute_import, unicode_literals - -import os import subprocess - import numpy as np - -def setup_function(function): - try: - os.remove('tests/output.txt') - except: - pass - -teardown_function = setup_function - - -def test_module_invocation(): - p = subprocess.Popen(['python', '-m', 'aacgmv2', 'convert', '-i', 'tests/test_convert.txt', '-d', '20150224', - '-o', 'tests/output.txt']) - p.communicate() - p.wait() - data = np.loadtxt('tests/output.txt') - np.testing.assert_allclose(data, [[57.4761, 93.5572], [58.5332, 93.9607], [59.5852, 94.3897]], rtol=1e-4) - - -def test_convert_g2a(): - p = subprocess.Popen(['aacgmv2', 'convert', '-i', 'tests/test_convert.txt', '-d', '20150224', - '-o', 'tests/output.txt']) - p.communicate() - p.wait() - data = np.loadtxt('tests/output.txt') - np.testing.assert_allclose(data, [[57.4761, 93.5572], [58.5332, 93.9607], [59.5852, 94.3897]], rtol=1e-4) - - -def test_convert_a2g(): - p = subprocess.Popen(['aacgmv2', 'convert', '-i', 'tests/test_convert.txt', '-d', '20150224', - '-o', 'tests/output.txt', '-v']) - p.communicate() - p.wait() - data = np.loadtxt('tests/output.txt') - np.testing.assert_allclose(data, [[51.6547, -66.6601], [52.6725, -66.7555], [53.6914, -66.8552]], rtol=1e-4) - - -def test_convert_trace_g2a(): - p = subprocess.Popen(['aacgmv2', 'convert', '-i', 'tests/test_convert.txt', '-d', '20150224', - '-o', 'tests/output.txt', '-t']) - p.communicate() - p.wait() - data = np.loadtxt('tests/output.txt') - np.testing.assert_allclose(data, [[57.4736, 93.5676], [58.5305, 93.9716], [59.5825, 94.4009]], rtol=1e-4) - - -def test_convert_trace_a2g(): - p = subprocess.Popen(['aacgmv2', 'convert', '-i', 'tests/test_convert.txt', '-d', '20150224', - '-o', 'tests/output.txt', '-t', '-v']) - p.communicate() - p.wait() - data = np.loadtxt('tests/output.txt') - np.testing.assert_allclose(data, [[51.6454, -66.6444], [52.6671, -66.7432], [53.6899, -66.8469]], rtol=1e-4) - - -def test_convert_geocentric(): - p = subprocess.Popen(['aacgmv2', 'convert', '-i', 'tests/test_convert.txt', '-d', '20150224', - '-o', 'tests/output.txt', '-g']) - p.communicate() - p.wait() - data = np.loadtxt('tests/output.txt') - np.testing.assert_allclose(data, [[57.6697, 93.6319], [58.7223, 94.0385], [59.7695, 94.4708]], rtol=1e-4) - - -def test_convert_today(): - p = subprocess.Popen(['aacgmv2', 'convert', '-i', 'tests/test_convert.txt']) - p.communicate() - p.wait() - - -def test_convert_single_line(): - p = subprocess.Popen(['aacgmv2', 'convert', '-i', 'tests/test_convert_single_line.txt', - '-d', '20150224', '-o', 'tests/output.txt']) - p.communicate() - p.wait() - data = np.loadtxt('tests/output.txt') - np.testing.assert_allclose(data, [57.4761, 93.5572], rtol=1e-4) - - -def test_convert_stdin_stdout(): - p = subprocess.Popen('echo 60 15 300 | aacgmv2 convert -d 20150224', shell=True, stdout=subprocess.PIPE) - stdout, _ = p.communicate() - p.wait() - assert b'57.47612194 93.55719875' in stdout - - -def test_convert_mlt_a2m(): - p = subprocess.Popen(['aacgmv2', 'convert_mlt', '-i', 'tests/test_convert_mlt.txt', - '20150224140015', '-o', 'tests/output.txt']) - p.communicate() - p.wait() - data = np.loadtxt('tests/output.txt') - np.testing.assert_allclose(data, [9.056476, 9.78981, 10.523143], rtol=1e-6) - - -def test_convert_mlt_m2a(): - p = subprocess.Popen(['aacgmv2', 'convert_mlt', '-i', 'tests/test_convert_mlt.txt', - '20150224140015', '-o', 'tests/output.txt', '-v']) - p.communicate() - p.wait() - data = np.loadtxt('tests/output.txt') - np.testing.assert_allclose(data, [240.152854, 45.152854, 210.152854], rtol=1e-6) - - -def test_convert_mlt_single_line(): - p = subprocess.Popen(['aacgmv2', 'convert_mlt', '-i', 'tests/test_convert_mlt_single_line.txt', - '20150224140015', '-o', 'tests/output.txt']) - p.communicate() - p.wait() - data = np.loadtxt('tests/output.txt') - np.testing.assert_allclose(data, 9.0564764, rtol=1e-6) - - -def test_convert_mlt_stdin_stdout(): - p = subprocess.Popen('echo 12 | aacgmv2 convert_mlt -v 20150224140015', shell=True, stdout=subprocess.PIPE) - stdout, _ = p.communicate() - p.wait() - assert b'45.15285362' in stdout - - -def test_convert_mlt_stdin_stdout_order(): - p = subprocess.Popen('echo 12 | aacgmv2 convert_mlt 20150224140015 -v', shell=True, stdout=subprocess.PIPE) - stdout, _ = p.communicate() - p.wait() - assert b'45.15285362' in stdout +class testCmdAACGMV2: + def setup(self): + """Runs before every method to create a clean testing setup""" + self.output = "tests/test_data/output.txt" + self.convert = "tests/test_data/test_convert.txt" + self.single = 'tests/test_data/test_convert_single_line.txt' + self.mlt = 'tests/test_data/test_convert_mlt.txt' + self.mlt_single = 'tests/test_data/test_convert_mlt_single_line.txt' + + def teardown(self): + """Runs after every method to clean up previous testing""" + import os + + if os.path.isfile(self.output): + os.remove(self.output) + + del self.output, self.convert, self.single, self.mlt, self.mlt_single + + def test_module_invocation(self): + p = subprocess.Popen(['python', '-m', 'aacgmv2', 'convert', '-i', + 'tests/test_convert.txt', '-d', '20150224', + '-o', self.output]) + p.communicate() + p.wait() + data = np.loadtxt(self.output) + np.testing.assert_allclose(data, [[57.4761, 93.5572], + [58.5332, 93.9607], + [59.5852, 94.3897]], rtol=1e-4) + + def test_convert_g2a(self): + p = subprocess.Popen(['aacgmv2', 'convert', '-i', self.convert, '-d', + '20150224', '-o', self.output]) + p.communicate() + p.wait() + data = np.loadtxt(self.output) + np.testing.assert_allclose(data, [[57.4761, 93.5572], + [58.5332, 93.9607], + [59.5852, 94.3897]], rtol=1e-4) + + def test_convert_a2g(self): + p = subprocess.Popen(['aacgmv2', 'convert', '-i', self.convert, + '-d', '20150224', '-o', self.output, '-v']) + p.communicate() + p.wait() + data = np.loadtxt(self.output) + np.testing.assert_allclose(data, [[51.6547, -66.6601], + [52.6725, -66.7555], + [53.6914, -66.8552]], rtol=1e-4) + + def test_convert_trace_g2a(self): + p = subprocess.Popen(['aacgmv2', 'convert', '-i', self.convert, '-d', + '20150224', '-o', self.output, '-t']) + p.communicate() + p.wait() + data = np.loadtxt(self.output) + np.testing.assert_allclose(data, [[57.4736, 93.5676], + [58.5305, 93.9716], + [59.5825, 94.4009]], rtol=1e-4) + + def test_convert_trace_a2g(self): + p = subprocess.Popen(['aacgmv2', 'convert', '-i', self.convert, '-d', + '20150224', '-o', self.output, '-t', '-v']) + p.communicate() + p.wait() + data = np.loadtxt(self.output) + np.testing.assert_allclose(data, [[51.6454, -66.6444], + [52.6671, -66.7432], + [53.6899, -66.8469]], rtol=1e-4) + + def test_convert_geocentric(self): + p = subprocess.Popen(['aacgmv2', 'convert', '-i', self.convert, '-d', + '20150224', '-o', self.output, '-g']) + p.communicate() + p.wait() + data = np.loadtxt(self.output) + np.testing.assert_allclose(data, [[57.6697, 93.6319], + [58.7223, 94.0385], + [59.7695, 94.4708]], rtol=1e-4) + + def test_convert_today(self): + p = subprocess.Popen(['aacgmv2', 'convert', '-i', self.convert]) + p.communicate() + p.wait() + + def test_convert_single_line(self): + p = subprocess.Popen(['aacgmv2', 'convert', '-i', self.single, '-d', + '20150224', '-o', self.output]) + p.communicate() + p.wait() + data = np.loadtxt(self.output) + np.testing.assert_allclose(data, [57.4761, 93.5572], rtol=1e-4) + + def test_convert_stdin_stdout(self): + p = subprocess.Popen('echo 60 15 300 | aacgmv2 convert -d 20150224', + shell=True, stdout=subprocess.PIPE) + stdout, _ = p.communicate() + p.wait() + assert b'57.47612194 93.55719875' in stdout + + def test_convert_mlt_a2m(self): + p = subprocess.Popen(['aacgmv2', 'convert_mlt', '-i', self.mlt, + '20150224140015', '-o', self.output]) + p.communicate() + p.wait() + data = np.loadtxt(self.output) + np.testing.assert_allclose(data, [9.056476, 9.78981, 10.523143], + rtol=1e-6) + + def test_convert_mlt_m2a(self): + p = subprocess.Popen(['aacgmv2', 'convert_mlt', '-i', self.mlt, + '20150224140015', '-o', self.output, '-v']) + p.communicate() + p.wait() + data = np.loadtxt(self.output) + np.testing.assert_allclose(data, [240.152854, 45.152854, 210.152854], + rtol=1e-6) + + def test_convert_mlt_single_line(self): + p = subprocess.Popen(['aacgmv2', 'convert_mlt', '-i', self.mlt_single, + '20150224140015', '-o', self.output]) + p.communicate() + p.wait() + data = np.loadtxt(self.output) + np.testing.assert_allclose(data, 9.0564764, rtol=1e-6) + + def test_convert_mlt_stdin_stdout(self): + p = subprocess.Popen('echo 12 | aacgmv2 convert_mlt -v 20150224140015', + shell=True, stdout=subprocess.PIPE) + stdout, _ = p.communicate() + p.wait() + assert b'45.15285362' in stdout + + def test_convert_mlt_stdin_stdout_order(self): + p = subprocess.Popen('echo 12 | aacgmv2 convert_mlt 20150224140015 -v', + shell=True, stdout=subprocess.PIPE) + stdout, _ = p.communicate() + p.wait() + assert b'45.15285362' in stdout diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index 8741efbb..706a45bf 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -1,218 +1,596 @@ # -*- coding: utf-8 -*- - -from __future__ import division, print_function, absolute_import, unicode_literals +from __future__ import division, absolute_import, unicode_literals import datetime as dt - import numpy as np import pytest - import aacgmv2 -from aacgmv2._aacgmv2 import A2G, G2A, TRACE, BADIDEA, ALLOWTRACE, GEOCENTRIC - -date = (2015, 1, 1, 0, 0, 0) -dtObj = dt.datetime(*date) - - -def test_module_structure(): - assert aacgmv2 - assert aacgmv2.convert - - -def test_output_type(): - lat, lon = aacgmv2.convert(60, 0, 300, dtObj) - print(type(lat)) - print(lat.shape) - print(lat.size) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - - lat, lon = aacgmv2.convert([60], [0], [300], dtObj) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - - lat, lon = aacgmv2.convert([60, 61], [0, 0], [300, 300], dtObj) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - - lat, lon = aacgmv2.convert([60, 61, 62], 0, 300, dtObj) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - - lat, lon = aacgmv2.convert(np.array([60, 61, 62]), 0, 300, dtObj) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - - lat, lon = aacgmv2.convert(np.array([[60, 61, 62], [63, 64, 65]]), 0, 300, dtObj) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - - -def test_output_shape_size(): - lat, lon = aacgmv2.convert(60, 0, 300, dtObj) - assert lat.shape == tuple() - assert lon.shape == tuple() - assert lat.size == 1 - assert lon.size == 1 - - lat, lon = aacgmv2.convert([60], [0], [300], dtObj) - assert lat.shape == (1,) - assert lon.shape == (1,) - assert lat.size == 1 - assert lon.size == 1 - - lat, lon = aacgmv2.convert([60, 61], [0, 0], [300, 300], dtObj) - assert lat.shape == (2,) - assert lon.shape == (2,) - assert lat.size == 2 - assert lon.size == 2 - - lat, lon = aacgmv2.convert([60, 61, 62], 0, 300, dtObj) - assert lat.shape == (3,) - assert lon.shape == (3,) - assert lat.size == 3 - assert lon.size == 3 - - lat, lon = aacgmv2.convert(np.array([60, 61, 62]), 0, 300, dtObj) - assert lat.shape == (3,) - assert lon.shape == (3,) - assert lat.size == 3 - assert lon.size == 3 - - lat, lon = aacgmv2.convert(np.array([[60, 61, 62], - [63, 64, 65]]), - 0, 300, dtObj) - assert lat.shape == (2, 3) - assert lon.shape == (2, 3) - assert lat.size == 6 - assert lon.size == 6 - - -def test_convert_result_values_shape(): - lat, lon = aacgmv2.convert(np.array([[60, 61, 62], - [63, 64, 65]]), - 0, 300, dtObj) - aacgmv2._aacgmv2.setDateTime(*date) - assert (lat[0, 0], lon[0, 0], 1) == aacgmv2._aacgmv2.aacgmConvert(60, 0, 300, G2A) - assert (lat[0, 1], lon[0, 1], 1) == aacgmv2._aacgmv2.aacgmConvert(61, 0, 300, G2A) - assert (lat[0, 2], lon[0, 2], 1) == aacgmv2._aacgmv2.aacgmConvert(62, 0, 300, G2A) - assert (lat[1, 0], lon[1, 0], 1) == aacgmv2._aacgmv2.aacgmConvert(63, 0, 300, G2A) - assert (lat[1, 1], lon[1, 1], 1) == aacgmv2._aacgmv2.aacgmConvert(64, 0, 300, G2A) - assert (lat[1, 2], lon[1, 2], 1) == aacgmv2._aacgmv2.aacgmConvert(65, 0, 300, G2A) - - -def test_convert_datetime_date(): - lat_1, lon_1 = aacgmv2.convert(60, 0, 300, dt.date(2013, 12, 1)) - lat_2, lon_2 = aacgmv2.convert(60, 0, 300, dt.datetime(2013, 12, 1, 0, 0, 0)) - assert lat_1 == lat_2 - assert lon_1 == lon_2 - - -def test_convert_result_values_G2A_coeff(): - lat_p, lon_p = aacgmv2.convert(60, 0, 300, dtObj) - aacgmv2._aacgmv2.setDateTime(*date) - lat_c, lon_c, _ = aacgmv2._aacgmv2.aacgmConvert(60, 0, 300, G2A) - assert lat_p == lat_c - assert lon_p == lon_c - - -def test_convert_result_values_A2G_coeff(): - lat_p, lon_p = aacgmv2.convert(60, 0, 300, dtObj, a2g=True) - aacgmv2._aacgmv2.setDateTime(*date) - lat_c, lon_c, _ = aacgmv2._aacgmv2.aacgmConvert(60, 0, 300, A2G) - assert lat_p == lat_c - assert lon_p == lon_c - - -def test_convert_result_values_G2A_trace(): - lat_p, lon_p = aacgmv2.convert(60, 0, 300, dtObj, trace=True) - aacgmv2._aacgmv2.setDateTime(*date) - lat_c, lon_c, _ = aacgmv2._aacgmv2.aacgmConvert(60, 0, 300, G2A | TRACE) - assert lat_p == lat_c - assert lon_p == lon_c - - -def test_convert_result_values_A2G_trace(): - lat_p, lon_p = aacgmv2.convert(60, 0, 300, dtObj, a2g=True, trace=True) - aacgmv2._aacgmv2.setDateTime(*date) - lat_c, lon_c, _ = aacgmv2._aacgmv2.aacgmConvert(60, 0, 300, A2G | TRACE) - assert lat_p == lat_c - assert lon_p == lon_c - - -def test_convert_result_values_allowtrace(): - lat, lon = aacgmv2.convert(60, 0, [300, 5000], dtObj, allowtrace=True) - aacgmv2._aacgmv2.setDateTime(*date) - assert (lat[0], lon[0], 1) == aacgmv2._aacgmv2.aacgmConvert(60, 0, 300, ALLOWTRACE) - assert (lat[1], lon[1], 1) == aacgmv2._aacgmv2.aacgmConvert(60, 0, 5000, ALLOWTRACE) - - -def test_convert_result_values_badidea(): - lat, lon = aacgmv2.convert(60, 0, [300, 5000], dtObj, badidea=True) - aacgmv2._aacgmv2.setDateTime(*date) - assert (lat[0], lon[0], 1) == aacgmv2._aacgmv2.aacgmConvert(60, 0, 300, BADIDEA) - assert (lat[1], lon[1], 1) == aacgmv2._aacgmv2.aacgmConvert(60, 0, 5000, BADIDEA) - - -def test_convert_result_values_geocentric(): - lat_p, lon_p = aacgmv2.convert(60, 0, 300, dtObj, geocentric=True) - aacgmv2._aacgmv2.setDateTime(*date) - lat_c, lon_c, _ = aacgmv2._aacgmv2.aacgmConvert(60, 0, 300, GEOCENTRIC) - assert lat_p == lat_c - assert lon_p == lon_c - - -def test_warning_below_ground(): - with pytest.warns(UserWarning): - aacgmv2.convert(60, 0, -1, dtObj) - with pytest.warns(UserWarning): - aacgmv2.convert(60, 0, [300, -1], dtObj) - - -def test_exception_maxalt(): - with pytest.raises(ValueError): - aacgmv2.convert(60, 0, 2001, dtObj) - with pytest.raises(ValueError): - aacgmv2.convert(60, 0, [300, 2001], dtObj) - - # the following should not raise exceptions - aacgmv2.convert(60, 0, 2001, dtObj, trace=True) - aacgmv2.convert(60, 0, 2001, dtObj, allowtrace=True) - aacgmv2.convert(60, 0, 2001, dtObj, badidea=True) - - -def test_exception_lat90(): - with pytest.raises(ValueError): - aacgmv2.convert(91, 0, 300, dtObj) - with pytest.raises(ValueError): - aacgmv2.convert(-91, 0, 300, dtObj) - with pytest.raises(ValueError): - aacgmv2.convert([60, 91], 0, 300, dtObj) - with pytest.raises(ValueError): - aacgmv2.convert([60, -91], 0, 300, dtObj) - - # the following should not raise exceptions - aacgmv2.convert(90, 0, 300, dtObj) - aacgmv2.convert(-90, 0, 300, dtObj) - - -def test_forbidden(): - mlat, mlon = aacgmv2.convert(7, 0, 0) - assert np.isnan(mlat) - assert np.isnan(mlon) - - -def test_MLT_forward_backward(): - mlon = aacgmv2.convert_mlt(12, dtObj, m2a=True) - mlt = aacgmv2.convert_mlt(mlon, dtObj) - np.testing.assert_allclose(mlt, 12) - - -def test_MLT_a2m(): - mlt = aacgmv2.convert_mlt([1, 12, 23], dt.datetime(2015, 2, 24, 14, 0, 15)) - np.testing.assert_allclose(mlt, [9.056476, 9.78981 , 10.523143], rtol=1e-6) - -def test_MLT_m2a(): - mlon = aacgmv2.convert_mlt([1, 12, 23], dt.datetime(2015, 2, 24, 14, 0, 15), m2a=True) - np.testing.assert_allclose(mlon, [240.152854, 45.152854, 210.152854], rtol=1e-6) +class TestPyAACGMV2: + def setup(self): + """Runs before every method to create a clean testing setup""" + self.date_args = (2015, 1, 1, 0, 0, 0, aacgmv2.AACGM_v2_DAT_PREFIX) + self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0) + self.ddate = dt.date(2015, 1, 1) + + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.date_args, self.dtime, self.ddate + + def test_module_structure(self): + """Test module structure""" + assert aacgmv2 + assert aacgmv2.convert_bool_to_bit + assert aacgmv2.convert_str_to_bit + assert aacgmv2.convert_latlon + assert aacgmv2.convert_latlon_arr + assert aacgmv2.get_aacgm_coord + assert aacgmv2.get_aacgm_coord_arr + assert aacgmv2.wrapper + + def test_module_parameters(self): + """Test module constants""" + path1 = "aacgmv2/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-" + arg1 = aacgmv2.AACGM_v2_DAT_PREFIX.find(path1) >= 0 + + path2 = "aacgmv2/aacgmv2/igrf12coeffs.txt" + arg2 = aacgmv2.IGRF_12_COEFFS.find(path2) >= 0 + + assert arg1 & arg2 + + def test_convert_latlon(self): + """Test single value latlon conversion""" + lat, lon, r = aacgmv2.convert_latlon(60, 0, 300, self.dtime) + np.testing.assert_almost_equal(lat, 58.2258, decimal=4) + np.testing.assert_almost_equal(lon, 81.1685, decimal=4) + np.testing.assert_almost_equal(r, 1.0457, decimal=4) + + def test_convert_latlon_badidea_failure(self): + """Test single value latlon conversion with a bad flag""" + code = "G2A | BADIDEA" + lat, lon, r = aacgmv2.convert_latlon(60, 0, 3000, self.dtime, code) + assert np.isnan(lat) & np.isnan(lon) & np.isnan(r) + + def test_convert_latlon_location_failure(self): + """Test single value latlon conversion with a bad location""" + lat, lon, r = aacgmv2.convert_latlon(0, 0, 0, self.dtime) + assert np.isnan(lat) & np.isnan(lon) & np.isnan(r) + + def test_convert_latlon_time_failure(self): + """Test single value latlon conversion with a bad datetime""" + with pytest.raises(AssertionError): + lat, lon, r = aacgmv2.convert_latlon(60, 0, 300, None) + + def test_convert_latlon_arr_single_val(self): + """Test array latlon conversion for a single value""" + lat, lon, r = aacgmv2.convert_latlon(60, 0, 300, self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert isinstance(r, np.ndarray) + assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 1 + np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) + np.testing.assert_allclose(r, [1.0457], rtol=1e-4) + + def test_convert_latlon_arr_list(self): + """Test array latlon conversion for list input""" + lat, lon, r = aacgmv2.convert_latlon_arr([60], [0], [300], self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert isinstance(r, np.ndarray) + assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 1 + np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) + np.testing.assert_allclose(r, [1.0457], rtol=1e-4) + + lat, lon, r = aacgmv2.convert_latlon_arr([60, 61], [0, 0], [300, 300], + self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert isinstance(r, np.ndarray) + assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 2 + np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) + np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) + + def test_convert_latlon_arr_arr(self): + """Test array latlon conversion for array input""" + lat, lon, r = aacgmv2.convert_latlon_arr(np.array([60]), np.array([0]), + np.array([300]), self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert isinstance(r, np.ndarray) + assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 1 + np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) + np.testing.assert_allclose(r, [1.0457], rtol=1e-4) + + lat, lon, r = aacgmv2.convert_latlon_arr(np.array([60, 61]), + np.array([0, 0]), + np.array([300, 300]), + self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert isinstance(r, np.ndarray) + assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 2 + np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) + np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) + + def test_convert_latlon_arr_unequal(self): + """Test array latlon conversion for unequal sized input""" + lat, lon, r = aacgmv2.convert_latlon_arr([60, 61], 0, 300, self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert isinstance(r, np.ndarray) + assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 2 + np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) + np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) + + lat, lon, r = aacgmv2.convert_latlon_arr(np.array([60, 61]), 0, 300, + self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert isinstance(r, np.ndarray) + assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 2 + np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) + np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) + + lat, lon, r = aacgmv2.convert_latlon_arr(np.array([[60, 61, 62], + [63, 64, 65]]), 0, + 300, self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert isinstance(r, np.ndarray) + assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (2, 3) + np.testing.assert_allclose(lat, [[58.2258, 59.3186, 60.4040], + [61.4820, 62.5528, 63.6164]], + rtol=1e-4) + np.testing.assert_allclose(lon, [[81.1685, 81.6140, 82.0872], + [82.5909, 83.1286, 83.7039]], + rtol=1e-4) + np.testing.assert_allclose(r, [[1.0457, 1.0456, 1.0456] + [1.0455, 1.0455, 1.0454]], rtol=1e-4) + + lat, lon, r = aacgmv2.convert_latlon_arr(np.array([[60, 61, 62], + [63, 64, 65]]), [0], + [300], self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert isinstance(r, np.ndarray) + assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (2, 3) + np.testing.assert_allclose(lat, [[58.2258, 59.3186, 60.4040], + [61.4820, 62.5528, 63.6164]], + rtol=1e-4) + np.testing.assert_allclose(lon, [[81.1685, 81.6140, 82.0872], + [82.5909, 83.1286, 83.7039]], + rtol=1e-4) + np.testing.assert_allclose(r, [[1.0457, 1.0456, 1.0456] + [1.0455, 1.0455, 1.0454]], rtol=1e-4) + + def test_convert_latlon_arr_badidea_failure(self): + """Test array latlon conversion failure for BADIDEA""" + code = "G2A | BADIDEA" + lat, lon, r = aacgmv2.convert_latlon_arr([60], [0], [3000], self.dtime, + code) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert isinstance(r, np.ndarray) + assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (1,) + assert np.all(np.isnan(lat), np.isnan(lon), np.isnan(r)]) + + def test_convert_latlon_arr_location_failure(self): + """Test array latlon conversion with a bad location""" + lat, lon, r = aacgmv2.convert_latlon_arr([0], [0], [0], self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert isinstance(r, np.ndarray) + assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (1,) + assert np.all([np.isnan(lat), np.isnan(lon), np.isnan(r)]) + + def test_convert_latlon_arr_time_failure(self): + """Test array latlon conversion with a bad time""" + with pytest.raises(AssertionError): + lat, lon, r = aacgmv2.convert_latlon_arr([60], [0], [300], None) + + def test_convert_latlon_datetime_date(self): + """Test single latlon conversion with date and datetime input""" + lat_1, lon_1, r_1 = aacgmv2.convert_latlon(60, 0, 300, self.ddate) + lat_2, lon_2, r_2 = aacgmv2.convert_latlon(60, 0, 300, self.dtime) + assert lat_1 == lat_2 + assert lon_1 == lon_2 + assert r_1 == r_2 + + def test_convert_latlon_datetime_date(self): + """Test array latlon conversion with date and datetime input""" + lat_1, lon_1, r_1 = aacgmv2.convert_latlon_arr([60], [0], [300], + self.ddate) + lat_2, lon_2, r_2 = aacgmv2.convert_latlon_arr([60], [0], [300], + self.dtime) + assert lat_1 == lat_2 + assert lon_1 == lon_2 + assert r_1 == r_2 + + def test_warning_below_ground_convert_latlon(self): + """ Test that a warning is issued if altitude is below zero""" + import testfixtures + lwarn = u"conversion not intended for altitudes < 0 km" + + with testfixtures.LogCapture() as l: + lat, lon, r = aacgmv2.convert_latlon(60, 0, -1, self.dtime) + + assert l.check(("root", "WARNING", lwarn)) is None + l.uninstall() + + def test_warning_below_ground_convert_latlon_arr(self): + """ Test that a warning is issued if altitude is below zero""" + import testfixtures + lwarn = u"conversion not intended for altitudes < 0 km" + + with testfixtures.LogCapture() as l: + lat, lon, r = aacgmv2.convert_latlon_arr([60], [0], [-1], + self.dtime) + + assert l.check(("root", "WARNING", lwarn)) is None + l.uninstall() + + def test_convert_latlon_maxalt_failure(self): + """For a single value, test failure for an altitude too high for + coefficients""" + lat, lon, r = aacgmv2.convert_latlon(60, 0, 2001, self.dtime) + assert np.isnan(lat) & np.isnan(lon) & np.isnan(r) + + def test_convert_latlon_arr_maxalt_failure(self): + """For an array, test failure for an altitude too high for + coefficients""" + lat, lon, r = aacgmv2.convert_latlon_arr([60], [0], [2001], self.dtime) + assert np.all([np.isnan(lat), np.isnan(lon), np.isnan(r)]) + + def test_convert_latlon_lat_failure(self): + """Test error return for co-latitudes above 90 for a single value""" + with pytest.raises(AssertionError): + aacgmv2.convert_latlon(91, 0, 300, self.dtime) + + with pytest.raises(AssertionError): + aacgmv2.convert_latlon(-91, 0, 300, self.dtime) + + def test_convert_latlon_arr_lat_failure(self): + """Test error return for co-latitudes above 90 for an array""" + with pytest.raises(AssertionError): + aacgmv2.convert_latlon_arr([91, 60, -91], 0, 300, self.dtime) + + def test_get_aacgm_coord(self): + """Test single value AACGMV2 calculation, defaults to TRACE""" + mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 0, 300, self.dtime) + np.testing.assert_almost_equal(mlat, 58.2257, decimal=4) + np.testing.assert_almost_equal(mlon, 81.1761, decimal=4) + np.testing.assert_almost_equal(mlt, 0.1889, decimal=4) + + def test_get_aacgm_coord_badidea_failure(self): + """Test single value AACGMV2 calculation with a bad flag""" + method = "BADIDEA" + mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 0, 3000, self.dtime, + method=method) + assert np.isnan(mlat) & np.isnan(mlon) & np.isnan(mlt) + + def test_get_aacgm_coord_location_failure(self): + """Test single value AACGMV2 calculation with a bad location""" + mlat, mlon, mlt = aacgmv2.get_aacgm_coord(0, 0, 0, self.dtime) + assert np.isnan(mlat) & np.isnan(mlon) & np.isnan(mlt) + + def test_get_aacgm_coord_time_failure(self): + """Test single value AACGMV2 calculation with a bad datetime""" + import testfixtures + lerr_1 = u"time must be specified as datetime object" + lerr_2 = u"Unable to get magnetic lat/lon" + + with testfixtures.LogCapture() as l: + mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 0, 300, None) + + assert l.check(("root", "ERROR", lerr_1), + ("root", "ERROR", lerr_2)) is None + l.uninstall() + + def test_get_aacgm_coord_arr_single_val(self): + """Test array AACGMV2 calculation for a single value""" + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(60, 0, 300, self.dtime) + assert isinstance(mlat, np.ndarray) + assert isinstance(mlon, np.ndarray) + assert isinstance(mlt, np.ndarray) + assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + mlt.shape[0] == 1 + np.testing.assert_allclose(mlat, [58.2247], rtol=1e-4) + np.testing.assert_allclose(mlon, [81.1761], rtol=1e-4) + np.testing.assert_allclose(mlt, [0.1889], rtol=1e-4) + + def test_get_aacgm_coord_arr_list(self): + """Test array AACGMV2 calculation for list input""" + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60], [0], [300], + self.dtime) + assert isinstance(mlat, np.ndarray) + assert isinstance(mlon, np.ndarray) + assert isinstance(mlt, np.ndarray) + assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + mlt.shape[0] == 1 + np.testing.assert_allclose(mlat, [58.2247], rtol=1e-4) + np.testing.assert_allclose(mlon, [81.1761], rtol=1e-4) + np.testing.assert_allclose(mlt, [0.1889], rtol=1e-4) + + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60, 61], [0, 0], + [300, 300], self.dtime) + assert isinstance(mlat, np.ndarray) + assert isinstance(mlon, np.ndarray) + assert isinstance(r, np.ndarray) + assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + mlt.shape[0] == 2 + np.testing.assert_allclose(mlat, [58.2247, 59.3165], rtol=1e-4) + np.testing.assert_allclose(mlon, [81.1761, 81.6228], rtol=1e-4) + np.testing.assert_allclose(mlt, [0.1889, 0.2187], rtol=1e-4) + + def test_get_aacgm_coord_arr_arr(self): + """Test array AACGMV2 calculation for array input""" + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([60]), + np.array([0]), + np.array([300]), + self.dtime) + assert isinstance(mlat, np.ndarray) + assert isinstance(mlon, np.ndarray) + assert isinstance(r, np.ndarray) + assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + mlt.shape[0] == 1 + np.testing.assert_allclose(mlat, [58.2247], rtol=1e-4) + np.testing.assert_allclose(mlon, [81.1761], rtol=1e-4) + np.testing.assert_allclose(mlt, [0.1889], rtol=1e-4) + + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([60, 61]), + np.array([0, 0]), + np.array([300, 300]), + self.dtime) + assert isinstance(mlat, np.ndarray) + assert isinstance(mlon, np.ndarray) + assert isinstance(r, np.ndarray) + assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + mlt.shape[0] == 2 + np.testing.assert_allclose(mlat, [58.2247, 59.3165], rtol=1e-4) + np.testing.assert_allclose(mlon, [81.1761, 81.6228], rtol=1e-4) + np.testing.assert_allclose(mlt, [0.1889, 0.2187], rtol=1e-4) + + def test_get_aacgm_coord_arr_unequal(self): + """Test array AACGMV2 calculation for unequal sized input""" + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60, 61], 0, 300, + self.dtime) + assert isinstance(mlat, np.ndarray) + assert isinstance(mlon, np.ndarray) + assert isinstance(r, np.ndarray) + assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + mlt.shape[0] == 2 + np.testing.assert_allclose(mlat, [58.2247, 59.3165], rtol=1e-4) + np.testing.assert_allclose(mlon, [81.1761, 81.6228], rtol=1e-4) + np.testing.assert_allclose(mlt, [0.1889, 0.2187], rtol=1e-4) + + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([60, 61]), 0, + 300, self.dtime) + assert isinstance(mlat, np.ndarray) + assert isinstance(mlon, np.ndarray) + assert isinstance(r, np.ndarray) + assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + mlt.shape[0] == 2 + np.testing.assert_allclose(mlat, [58.2247, 59.3165], rtol=1e-4) + np.testing.assert_allclose(mlon, [81.1761, 81.6228], rtol=1e-4) + np.testing.assert_allclose(mlt, [0.1889, 0.2187], rtol=1e-4) + + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([[60, 61, 62], + [63, 64, 65]]), 0, + 300, self.dtime) + assert isinstance(mlat, np.ndarray) + assert isinstance(mlon, np.ndarray) + assert isinstance(mlt, np.ndarray) + assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + mlt.shape == (2, 3) + np.testing.assert_allclose(mlat, [[58.2247, 59.3165, 60.4009], + [61.4781, 62.5482, 63.6114]], + rtol=1e-4) + np.testing.assert_allclose(mlon, [[81.1761, 81.6228, 82.0970], + [82.6014, 83.1394, 83.7146]], + rtol=1e-4) + np.testing.assert_allclose(mlt, [[0.1889, 0.2187, 0.2503], + [0.2839, 0.3198, 0.3582]], rtol=1e-4) + + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([[60, 61, 62], + [63, 64, 65]]), + [0], [300], self.dtime) + assert isinstance(mlat, np.ndarray) + assert isinstance(mlon, np.ndarray) + assert isinstance(mlt, np.ndarray) + assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + mlt.shape == (2, 3) + np.testing.assert_allclose(mlat, [[58.2247, 59.3165, 60.4009], + [61.4781, 62.5482, 63.6114]], + rtol=1e-4) + np.testing.assert_allclose(mlon, [[81.1761, 81.6228, 82.0970], + [82.6014, 83.1394, 83.7146]], + rtol=1e-4) + np.testing.assert_allclose(mlt, [[0.1889, 0.2187, 0.2503], + [0.2839, 0.3198, 0.3582]], rtol=1e-4) + + def test_get_aacgm_coord_arr_badidea_failure(self): + """Test array AACGMV2 calculation failure for BADIDEA""" + method = "BADIDEA" + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60], [0], [3000], + self.dtime, method=method) + assert isinstance(mlat, np.ndarray) + assert isinstance(mlon, np.ndarray) + assert isinstance(mlt, np.ndarray) + assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + mlt.shape == (1,) + assert np.all(np.isnan(mlat), np.isnan(mlon), np.isnan(mlt)]) + + def test_get_aacgm_coord_arr_location_failure(self): + """Test array AACGMV2 calculation with a bad location""" + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([0], [0], [0], self.dtime) + assert isinstance(mlat, np.ndarray) + assert isinstance(mlon, np.ndarray) + assert isinstance(mlt, np.ndarray) + assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + mlt.shape == (1,) + assert np.all([np.isnan(mlat), np.isnan(mlon), np.isnan(mlt)]) + + def test_get_aacgm_coord_arr_time_failure(self): + """Test array AACGMV2 calculation with a bad time""" + import testfixtures + lerr_1 = u"time must be specified as datetime object" + lerr_2 = u"Unable to get magnetic lat/lon" + + with testfixtures.LogCapture() as l: + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60], [0], [300], + None) + + assert l.check(("root", "ERROR", lerr_1), + ("root", "ERROR", lerr_2)) is None + l.uninstall() + + def test_get_aacgm_coord_datetime_date(self): + """Test single AACGMV2 calculation with date and datetime input""" + mlat_1, mlon_1, mlt_1 = aacgmv2.get_aacgm_coord(60, 0, 300, self.ddate) + mlat_2, mlon_2, mlt_2 = aacgmv2.get_aacgm_coord(60, 0, 300, self.dtime) + assert mlat_1 == mlat_2 + assert mlon_1 == mlon_2 + assert r_1 == r_2 + + def test_get_aacgm_coord_datetime_date(self): + """Test array AACGMV2 calculation with date and datetime input""" + mlat_1, mlon_1, mlt_1 = aacgmv2.get_aacgm_coord_arr([60], [0], [300], + self.ddate) + mlat_2, mlon_2, mlt_2 = aacgmv2.get_aacgm_coord_arr([60], [0], [300], + self.dtime) + assert mlat_1 == mlat_2 + assert mlon_1 == mlon_2 + assert r_1 == r_2 + + def test_warning_below_ground_get_aacgm_coord(self): + """ Test that a warning is issued if altitude is below zero""" + import testfixtures + lwarn = u"conversion not intended for altitudes < 0 km" + + with testfixtures.LogCapture() as l: + mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 0, -1, self.dtime) + + assert l.check(("root", "WARNING", lwarn)) is None + l.uninstall() + + def test_warning_below_ground_get_aacgm_coord_arr(self): + """ Test that a warning is issued if altitude is below zero""" + import testfixtures + lwarn = u"conversion not intended for altitudes < 0 km" + + with testfixtures.LogCapture() as l: + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60], [0], [-1], + self.dtime) + + assert l.check(("root", "WARNING", lwarn)) is None + l.uninstall() + + def test_get_aacgm_coord_maxalt_failure(self): + """For a single value, test failure for an altitude too high for + coefficients""" + method = "" + mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 0, 2001, self.dtime, + method=method) + assert np.isnan(mlat) & np.isnan(mlon) & np.isnan(mlt) + + def test_get_aacgm_coord_arr_maxalt_failure(self): + """For an array, test failure for an altitude too high for + coefficients""" + method = "" + mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60], [0], [2001], + self.dtime, method=method) + assert np.all([np.isnan(mlat), np.isnan(mlon), np.isnan(mlt)]) + + def test_get_aacgm_coord_mlat_failure(self): + """Test error return for co-latitudes above 90 for a single value""" + import testfixtures + lerr_1 = u"unrealistic latitude" + lerr_2 = u"Unable to get magnetic lat/lon" + + with testfixtures.LogCapture() as lhigh: + mlat, mlon, mlt = aacgmv2.get_aacgm_coord(91, 0, 300, self.dtime) + + assert lhigh.check(("root", "ERROR", lerr_1), + ("root", "ERROR", lerr_2)) is None + + with testfixtures.LogCapture() as llow: + mlat, mlon, mlt = aacgmv2.get_aacgm_coord(-91, 0, 300, self.dtime) + + assert llow.check(("root", "ERROR", lerr_1), + ("root", "ERROR", lerr_2)) is None + + lhigh.uninstall() + llow.uninstall() + + def test_get_aacgm_coord_arr_mlat_failure(self): + """Test error return for co-latitudes above 90 for an array""" + import testfixtures + lerr_1 = u"unrealistic latitude" + lerr_2 = u"Unable to get magnetic lat/lon" + + with testfixtures.LogCapture() as l: + aacgmv2.get_aacgm_coord_arr([91, 60, -91], 0, 300, self.dtime) + + assert l.check(("root", "ERROR", lerr_1), + ("root", "ERROR", lerr_2)) is None + l.uninstall() + + def test_convert_str_to_bit_g2a(self): + """Test conversion from string code to bit G2A""" + assert aacgmv2.convert_str_to_bit("G2A") == aacgmv2._aacgmv2.G2A + + def test_convert_str_to_bit_a2g(self): + """Test conversion from string code to bit A2G""" + assert aacgmv2.convert_str_to_bit("A2G") == aacgmv2._aacgmv2.A2G + + def test_convert_str_to_bit_trace(self): + """Test conversion from string code to bit TRACE""" + assert aacgmv2.convert_str_to_bit("TRACE") == aacgmv2._aacgmv2.TRACE + + def test_convert_str_to_bit_allowtrace(self): + """Test conversion from string code to bit ALLOWTRACE""" + assert aacgmv2.convert_str_to_bit("ALLOWTRACE") == \ + aacgmv2._aacgmv2.ALLOWTRACE + + def test_convert_str_to_bit_badidea(self): + """Test conversion from string code to bit BADIDEA""" + assert aacgmv2.convert_str_to_bit("BADIDEA") == aacgmv2._aacgmv2.BADIDEA + + def test_convert_str_to_bit_geocentric(self): + """Test conversion from string code to bit GEOCENTRIC""" + assert aacgmv2.convert_str_to_bit("GEOCENTRIC") == \ + aacgmv2._aacgmv2.GEOCENTRIC + + def test_convert_bool_to_bit_g2a(self): + """Test conversion from string code to bit G2A""" + assert aacgmv2.convert_bool_to_bit(g2a=True) == aacgmv2._aacgmv2.G2A + + def test_convert_bool_to_bit_a2g(self): + """Test conversion from string code to bit A2G""" + assert aacgmv2.convert_bool_to_bit(a2g=True) == aacgmv2._aacgmv2.A2G + + def test_convert_bool_to_bit_trace(self): + """Test conversion from string code to bit TRACE""" + assert aacgmv2.convert_bool_to_bit(trace=True) == aacgmv2._aacgmv2.TRACE + + def test_convert_bool_to_bit_allowtrace(self): + """Test conversion from string code to bit ALLOWTRACE""" + assert aacgmv2.convert_bool_to_bit(allowtrace=True) == \ + aacgmv2._aacgmv2.ALLOWTRACE + + def test_convert_bool_to_bit_badidea(self): + """Test conversion from string code to bit BADIDEA""" + assert aacgmv2.convert_bool_to_bit(badidea=True) == \ + aacgmv2._aacgmv2.BADIDEA + + def test_convert_bool_to_bit_geocentric(self): + """Test conversion from string code to bit GEOCENTRIC""" + assert aacgmv2.convert_bool_to_bit(geocentric=True) == \ + aacgmv2._aacgmv2.GEOCENTRIC From 74bfb35fcfdc88e70ac1bb3807e70baacf6a185c Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 12 Mar 2018 19:36:18 -0500 Subject: [PATCH 014/101] update file manifest update manifest file --- MANIFEST.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 05582f8b..f0d737a9 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,4 @@ graft docs -graft examples -graft src graft ci graft tests @@ -14,8 +12,10 @@ include AUTHORS.rst include CHANGELOG.rst include CONTRIBUTING.rst include LICENSE +include LICENSE-AstAlg.txt include README.rst +include c_aacgmv2/README.txt include tox.ini .travis.yml appveyor.yml .codeclimate.yml .landscape.yaml -global-exclude *.py[cod] __pycache__ *.so *.dylib +global-exclude *.py[cod] __pycache__ *.so *.dylib *.dSYM From 8ababbf9c3292ea6f04e2d35a3574ee206572cb8 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 11:35:30 -0500 Subject: [PATCH 015/101] Deprecated routines Add deprecated routines and tests for them. --- aacgmv2/__init__.py | 24 ++- aacgmv2/depricated.py | 301 ++++++++++++++++++++++++++++++ aacgmv2/tests/test_dep_aacgmv2.py | 258 +++++++++++++++++++++++++ 3 files changed, 579 insertions(+), 4 deletions(-) create mode 100644 aacgmv2/depricated.py create mode 100644 aacgmv2/tests/test_dep_aacgmv2.py diff --git a/aacgmv2/__init__.py b/aacgmv2/__init__.py index 848a26ee..fca3b4d5 100644 --- a/aacgmv2/__init__.py +++ b/aacgmv2/__init__.py @@ -25,6 +25,10 @@ convert_bool_to_bit get_aacgm_coord get_aacgm_coord_arr +convert +convert_mlt +subsol +set_coeff_path _aacgmv2.convert _aacgmv2.set_datetime _aacgmv2.mlt_convert @@ -33,6 +37,11 @@ _aacgmv2.inv_mlt_convert_yrsec --------------------------------------------------------------------------- +Modules +--------------------------------------------------------------------------- +depricated +_aacgmv2 +--------------------------------------------------------------------------- """ import os.path as _path import logging @@ -51,10 +60,17 @@ from aacgmv2.wrapper import (convert_latlon, get_aacgm_coord) from aacgmv2.wrapper import (convert_latlon_arr, get_aacgm_coord_arr) from aacgmv2.wrapper import (convert_bool_to_bit, convert_str_to_bit) -except Exception as e: - logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) +except Exception as err: + logging.exception(__file__ + ' -> aacgmv2: ' + str(err)) + +try: + from aacgmv2 import (depricated) + from aacgmv2.depricated import (convert, convert_mlt, subsol) + from aacgmv2.depricated import (set_coeff_path) +except Exception as err: + logging.exception(__file__ + ' -> aacgmv2: ' + str(err)) try: from aacgmv2 import (_aacgmv2) -except Exception as e: - logging.exception(__file__ + ' -> aacgmv2: ' + str(e)) +except Exception as err: + logging.exception(__file__ + ' -> aacgmv2: ' + str(err)) diff --git a/aacgmv2/depricated.py b/aacgmv2/depricated.py new file mode 100644 index 00000000..7883dd1c --- /dev/null +++ b/aacgmv2/depricated.py @@ -0,0 +1,301 @@ +# -*- coding: utf-8 -*- +"""Pythonic wrappers for AACGM-V2 C functions that were depricated in the +change from version 2.0.0 to version 2.0.2 + +Functions +------------------------------------------------------------------------------- +convert : Converts array location +convert_mlt : Get array mlt +set_coeff_path : Previously set environment variables, no longer used +subsol : finds subsolar geocentric longitude and latitude +gc2gd_lat : Convert between geocentric and geodetic coordinates +igrf_dipole_axis : Get Cartesian unit vector pointing at the IGRF north dipole +------------------------------------------------------------------------------ + +References +------------------------------------------------------------------------------- +Laundal, K. M. and A. D. Richmond (2016), Magnetic Coordinate Systems, Space + Sci. Rev., doi:10.1007/s11214-016-0275-y. +------------------------------------------------------------------------------- +""" + +from __future__ import division, absolute_import, unicode_literals +import numpy as np +import logging +import aacgmv2 + +def convert(lat, lon, alt, date=None, a2g=False, trace=False, allowtrace=False, + badidea=False, geocentric=False): + """Converts between geomagnetic coordinates and AACGM coordinates + + Parameters + ------------ + lat : (float) + Input latitude in degrees N (code specifies type of latitude) + lon : (float) + Input longitude in degrees E (code specifies type of longitude) + alt : (float) + Altitude above the surface of the earth in km + date : (datetime) + Datetime for magnetic field + a2g : (bool) + True for AACGM-v2 to geographic (geodetic), False otherwise + (default=False) + trace : (bool) + If True, use field-line tracing, not coefficients (default=False) + allowtrace : (bool) + If True, use trace only above 2000 km (default=False) + badidea : (bool) + If True, use coefficients above 2000 km (default=False) + geocentric : (bool) + True for geodetic, False for geocentric w/RE=6371.2 (default=False) + + Returns + ------- + lat_out : (float) + Output latitude in degrees N + lon_out : (float) + Output longitude in degrees E + """ + # construct a code from the boolian flags + bit_code = aacgmv2.convert_bool_to_bit(a2g=a2g, trace=trace, + allowtrace=allowtrace, + badidea=badidea, + geocentric=geocentric) + + # convert location + lat_out, lon_out, r_out = aacgmv2.convert_latlon_arr(lat, lon, alt, date, + code=bit_code) + + return lat_out, lon_out + +def convert_mlt(arr, dtime, m2a=False): + """Converts between magnetic local time (MLT) and AACGM-v2 longitude + + Parameters + ------------ + arr : (array_line or float) + Magnetic longitudes or MLTs to convert + dtime : (datetime.datetime) + Date and time for MLT conversion in Universal Time (UT). + m2a : (bool) + Convert MLT to AACGM-v2 longitude (True) or magnetic longitude to MLT + (False). (default=False) + + Returns + -------- + out : (np.ndarray) + Converted coordinates/MLT + + Notes + ------- + This routine previously based on Laundal et al. 2016, but now uses the + improved calculation available in AACGM-V2.4. + """ + import aacgmv2._aacgmv2 as c_aacgmv2 + + if m2a: + out = aacgmv2._aacgmv2.inv_mlt_convert(dtime.year, dtime.month, + dtime.day, dtime.hour, + dtime.minute, dtime.second, + arr, aacgmv2.IGRF_12_COEFFS) + else: + out = aacgmv2._aacgmv2.mlt_convert(dtime.year, dtime.month, dtime.day, + dtime.hour, dtime.minute, + dtime.second, arr, + aacgmv2.IGRF_12_COEFFS) + + return out + +def set_coeff_path(): + """This depricated routine used to set environment variables, and now is + not needed. + """ + + logging.warning("this routine is no longer needed") + return + +def subsol(year, doy, ut): + """Finds subsolar geocentric longitude and latitude. + + Parameters + ------------ + year : (int) + Calendar year between 1601 and 2100 + doy : (int) + Day of year between 1-365/366 + ut : (float) + Seconds since midnight on the specified day + + Returns + --------- + sbsllon : (float) + Subsolar longitude for the given date/time + sbsllat : (float) + Subsolar lattude for the given date/time + + Notes + -------- + Based on formulas in Astronomical Almanac for the year 1996, p. C24. + (U.S. Government Printing Office, 1994). Usable for years 1601-2100, + inclusive. According to the Almanac, results are good to at least 0.01 + degree latitude and 0.025 degrees longitude between years 1950 and 2050. + Accuracy for other years has not been tested. Every day is assumed to have + exactly 86400 seconds; thus leap seconds that sometimes occur on December + 31 are ignored (their effect is below the accuracy threshold of the + algorithm). + After Fortran code by A. D. Richmond, NCAR. Translated from IDL + by K. Laundal. + """ + yr = year - 2000 + + if year >= 2101: + logging.error('subsol invalid after 2100. Input year is:', year) + + nleap = np.floor((year - 1601) / 4) + nleap = nleap - 99 + if year <= 1900: + if year <= 1600: + print('subsol.py: subsol invalid before 1601. Input year is:', year) + ncent = np.floor((year - 1601) / 100) + ncent = 3 - ncent + nleap = nleap + ncent + + l0 = -79.549 + (-0.238699 * (yr - 4 * nleap) + 3.08514e-2 * nleap) + g0 = -2.472 + (-0.2558905 * (yr - 4 * nleap) - 3.79617e-2 * nleap) + + # Days (including fraction) since 12 UT on January 1 of IYR: + df = (ut / 86400 - 1.5) + doy + + # Addition to Mean longitude of Sun since January 1 of IYR: + lf = 0.9856474 * df + + # Addition to Mean anomaly since January 1 of IYR: + gf = 0.9856003 * df + + # Mean longitude of Sun: + l = l0 + lf + + # Mean anomaly: + grad = np.radians(g0 + gf) + + # Ecliptic longitude: + lmrad = np.radians(l + 1.915 * np.sin(grad) + 0.020 * np.sin(2 * grad)) + sinlm = np.sin(lmrad) + + # Days (including fraction) since 12 UT on January 1 of 2000: + n = df + 365.0 * yr + nleap + + # Obliquity of ecliptic: + epsrad = np.radians(23.439 - 4.0e-7 * n) + + # Right ascension: + alpha = np.degrees(np.arctan2(np.cos(epsrad) * sinlm, np.cos(lmrad))) + + # Declination: + delta = np.degrees(np.arcsin(np.sin(epsrad) * sinlm)) + + # Subsolar latitude: + sbsllat = delta + + # Equation of time (degrees): + etdeg = l - alpha + nrot = np.round(etdeg / 360.0) + etdeg = etdeg - 360.0 * nrot + + # Apparent time (degrees): + aptime = ut / 240.0 + etdeg # Earth rotates one degree every 240 s. + + # Subsolar longitude: + sbsllon = 180.0 - aptime + nrot = np.round(sbsllon / 360.0) + sbsllon = sbsllon - 360.0 * nrot + + return sbsllon, sbsllat + +def gc2gd_lat(gc_lat): + """Convert geocentric latitude to geodetic latitude using WGS84. + + Parameters + ----------- + gc_lat : (array_like or float) + Geocentric latitude in degrees N + + Returns + --------- + gd_lat : (same as input) + Geodetic latitude in degrees N + """ + WGS84_e2 = 0.006694379990141317 - 1.0 + return np.rad2deg(-np.arctan(np.tan(np.deg2rad(gc_lat)) / WGS84_e2)) + +def igrf_dipole_axis(date): + """Get Cartesian unit vector pointing at dipole pole in the north, + according to IGRF + + Parameters + ------------- + date : (dt.datetime) + Date and time + + Returns + ---------- + m: (np.ndarray) + Cartesian 3 element vector pointing at dipole pole in the north + (geocentric coords) + + Notes + ---------- + IGRF coefficients are read from the igrf12coeffs.txt file. It should also + work after IGRF updates. The dipole coefficients are interpolated to the + date, or extrapolated if date > latest IGRF model + """ + import datetime as dt + + # get time in years, as float: + year = date.year + doy = date.timetuple().tm_yday + year_days = int(dt.date(date.year, 12, 31).strftime("%j")) + year = year + doy / year_days + + # read the IGRF coefficients + with open(aacgmv2.IGRF_12_COEFFS, 'r') as f: + lines = f.readlines() + + years = lines[3].split()[3:][:-1] + years = np.array(years, dtype=float) # time array + + g10 = lines[4].split()[3:] + g11 = lines[5].split()[3:] + h11 = lines[6].split()[3:] + + # secular variation coefficients (for extrapolation) + g10sv = np.float32(g10[-1]) + g11sv = np.float32(g11[-1]) + h11sv = np.float32(h11[-1]) + + # model coefficients: + g10 = np.array(g10[:-1], dtype=float) + g11 = np.array(g11[:-1], dtype=float) + h11 = np.array(h11[:-1], dtype=float) + + # get the gauss coefficient at given time: + if year <= years[-1]: + # regular interpolation + g10 = np.interp(year, years, g10) + g11 = np.interp(year, years, g11) + h11 = np.interp(year, years, h11) + else: + # extrapolation + dt = year - years[-1] + g10 = g10[-1] + g10sv * dt + g11 = g11[-1] + g11sv * dt + h11 = h11[-1] + h11sv * dt + + # calculate pole position + B0 = np.sqrt(g10**2 + g11**2 + h11**2) + + # Calculate output + m = -np.array([g11, h11, g10]) / B0 + + return m diff --git a/aacgmv2/tests/test_dep_aacgmv2.py b/aacgmv2/tests/test_dep_aacgmv2.py new file mode 100644 index 00000000..284defa2 --- /dev/null +++ b/aacgmv2/tests/test_dep_aacgmv2.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 -*- +from __future__ import division, absolute_import, unicode_literals + +import datetime as dt +import numpy as np +import pytest +import aacgmv2 + +class TestDepAACGMV2: + def setup(self): + """Runs before every method to create a clean testing setup""" + self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0) + self.ddate = dt.date(2015, 1, 1) + + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.dtime, self.ddate + + def test_module_structure(self): + """Test module structure for depricated routines""" + assert aacgmv2 + assert aacgmv2.convert + assert aacgmv2.convert_mlt + assert aacgmv2.set_coeff_path + assert aacgmv2.subsol + assert aacgmv2.depricated + assert aacgmv2.depricated.gc2gd_lat + assert aacgmv2.depricated.igrf_dipole_axis + + def test_set_coeff_path(self): + """Test the depricated routine for appropriate warning""" + import testfixtures + lwarn = u"this routine is no longer needed" + + with testfixtures.LogCapture() as l: + aacgmv2.set_coeff_path() + + assert l.check(("root", "WARNING", lwarn)) is None + l.uninstall() + + def test_convert_single_val(self): + """Test conversion for a single value""" + lat, lon = aacgmv2.convert(60, 0, 300, self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert lat.shape == lon.shape & lat.shape[0] == 1 + np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) + + def test_convert_list(self): + """Test conversion for list input""" + lat, lon = aacgmv2.convert_latlon_arr([60], [0], [300], self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert lat.shape == lon.shape & lat.shape[0] == 1 + np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) + + lat, lon = aacgmv2.convert([60, 61], [0, 0], [300, 300], self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert lat.shape == lon.shape & lat.shape[0] == 2 + np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) + + def test_convert_arr(self): + """Test conversion for array input""" + lat, lon = aacgmv2.convert(np.array([60]), np.array([0]), + np.array([300]), self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert lat.shape == lon.shape & lat.shape[0] == 1 + np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) + + lat, lon = aacgmv2.convert(np.array([60, 61]), np.array([0, 0]), + np.array([300, 300]), self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert lat.shape == lon.shape & lat.shape[0] == 2 + np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) + + def test_convert_unequal(self): + """Test array latlon conversion for unequal sized input""" + lat, lon = aacgmv2.convert([60, 61], 0, 300, self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert lat.shape == lon.shape & lat.shape[0] == 2 + np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) + + lat, lon = aacgmv2.convert(np.array([60, 61]), 0, 300, self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert lat.shape == lon.shape & lat.shape[0] == 2 + np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) + np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) + + lat, lon = aacgmv2.convert(np.array([[60, 61, 62], [63, 64, 65]]), 0, + 300, self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert lat.shape == lon.shape & lat.shape == (2, 3) + np.testing.assert_allclose(lat, [[58.2258, 59.3186, 60.4040], + [61.4820, 62.5528, 63.6164]], + rtol=1e-4) + np.testing.assert_allclose(lon, [[81.1685, 81.6140, 82.0872], + [82.5909, 83.1286, 83.7039]], + rtol=1e-4) + + lat, lon = aacgmv2.convert(np.array([[60, 61, 62], [63, 64, 65]]), [0], + [300], self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert lat.shape == lon.shape & lat.shape == (2, 3) + np.testing.assert_allclose(lat, [[58.2258, 59.3186, 60.4040], + [61.4820, 62.5528, 63.6164]], + rtol=1e-4) + np.testing.assert_allclose(lon, [[81.1685, 81.6140, 82.0872], + [82.5909, 83.1286, 83.7039]], + rtol=1e-4) + + def test_convert_badidea_failure(self): + """Test conversion failure for BADIDEA""" + lat, lon = aacgmv2.convert([60], [0], [3000], self.dtime, badidea=True) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert lat.shape == lon.shape & lat.shape == (1,) + assert np.all(np.isnan(lat), np.isnan(lon)]) + + def test_convert_location_failure(self): + """Test conversion with a bad location""" + lat, lon = aacgmv2.convert([0], [0], [0], self.dtime) + assert isinstance(lat, np.ndarray) + assert isinstance(lon, np.ndarray) + assert lat.shape == lon.shape & lat.shape == (1,) + assert np.all([np.isnan(lat), np.isnan(lon)]) + + def test_convert_time_failure(self): + """Test conversion with a bad time""" + with pytest.raises(AssertionError): + lat, lon = aacgmv2.convert([60], [0], [300], None) + + def test_convert_datetime_date(self): + """Test conversion with date and datetime input""" + lat_1, lon_1 = aacgmv2.convert([60], [0], [300], self.ddate) + lat_2, lon_2 = aacgmv2.convert([60], [0], [300], self.dtime) + assert lat_1 == lat_2 + assert lon_1 == lon_2 + + def test_warning_below_ground_convert(self): + """ Test that a warning is issued if altitude is below zero""" + import testfixtures + lwarn = u"conversion not intended for altitudes < 0 km" + + with testfixtures.LogCapture() as l: + lat, lon = aacgmv2.convert([60], [0], [-1], self.dtime) + + assert l.check(("root", "WARNING", lwarn)) is None + l.uninstall() + + def test_convert_maxalt_failure(self): + """For an array, test failure for an altitude too high for + coefficients""" + lat, lon = aacgmv2.convert([60], [0], [2001], self.dtime) + assert np.all([np.isnan(lat), np.isnan(lon)]) + + def test_convert_lat_failure(self): + """Test error return for co-latitudes above 90 for an array""" + with pytest.raises(AssertionError): + aacgmv2.convert([91, 60, -91], 0, 300, self.dtime) + + def test_inv_convert_mlt_single(self): + """Test MLT inversion for a single value""" + mlon_1 = aacgmv2.convert_mlt(12.0, self.dtime, m2a=False) + mlon_2 = aacgmv2.convert_mlt(25.0, self.dtime, m2a=False) + mlon_3 = aacgmv2.convert_mlt(-1.0, self.dtime, m2a=False) + + np.testing.assert_almost_equal(mlon_1, -153.5339, decimal=4) + np.testing.assert_almost_equal(mlon_2, 41.4661, decimal=4) + np.testing.assert_almost_equal(mlon_2, 11.4661, decimal=4) + + def test_inv_convert_mlt_list(self): + """Test MLT inversion for a list""" + mlt_list = [12.0, 25.0, -1.0] + mlon = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=False) + + np.testing.assert_allclose(mlon, [-153.5339, 41.4661, 11.4661], + rtol=1.0e-4) + + def test_inv_convert_mlt_arr(self): + """Test MLT inversion for an array""" + mlt_arr = np.array([12.0, 25.0, -1.0]) + mlon = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=False) + + np.testing.assert_allclose(mlon, [-153.5339, 41.4661, 11.4661], + rtol=1.0e-4) + + def test_mlt_convert_single(self): + """Test MLT calculation for a single value""" + mlt_1 = aacgmv2.convert_mlt(270.0, self.dtime, m2a=True) + mlt_2 = aacgmv2.convert_mlt(80.0, self.dtime, m2a=True) + mlt_3 = aacgmv2.convert_mlt(-90.0, self.dtime, m2a=True) + + np.testing.assert_almost_equal(mlt_1, 16.2356, decimal=4) + np.testing.assert_almost_equal(mlt_2, 3.5689, decimal=4) + np.testing.assert_equal(mlt_1, mlt_3) + + def test_mlt_convert_list(self): + """Test MLT calculation for a list""" + mlt_list = [270.0, 80.0, -90.0] + mlt = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=True) + + np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-4) + + def test_mlt_convert_arr(self): + """Test MLT calculation for an array""" + mlt_arr = np.array([270.0, 80.0, -90.0]) + mlt = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=True) + + np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-4) + + def test_subsol(self): + """Test the subsolar calculation""" + doy = int(self.dtime.strftime("%j")) + ut = self.dtime.hour * 3600.0 + self.dtime.minute * 60.0 + \ + self.dtime.second + slon, slat = aacgmv2.subsol(self.dtime.year, doy, ut) + + np.testing.assert_almost_equal(slon, -179.2004, decimal=4) + np.testing.assert_almost_equal(slat, -23.0431, decimal=4) + + def test_gc2gd_lat(self): + """Test the geocentric to geodetic conversion""" + gd_lat = aacgmv2.depricated.gc2gd_lat(45.0) + + np.testing.assert_almost_equal(gd_lat, 45.1924, decimal=4) + + def test_gc2gd_lat_list(self): + """Test the geocentric to geodetic conversion""" + gc_lat = [45.0, -45.0] + gd_lat = aacgmv2.depricated.gc2gd_lat(gc_lat) + + np.testing.assert_allclose(gd_lat, [45.1924, -45.1924], rtol=1.0e-4) + + def test_gc2gd_lat_arr(self): + """Test the geocentric to geodetic conversion""" + gc_lat = np.array([45.0, -45.0]) + gd_lat = aacgmv2.depricated.gc2gd_lat(gc_lat) + + np.testing.assert_allclose(gd_lat, [45.1924, -45.1924], rtol=1.0e-4) + + def test_igrf_dipole_axis(self): + """Test the IGRF dipole axis calculation""" + m = aacgmv2.depricated.igrf_dipole_axis(self.dtime) + + np.testing.assert_allclose(m, [0.0503, -0.1607, 0.9857], rtol=1.0e-4) From cd8f697ce680f37a6b576552cef5fcf1056d74cf Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 11:36:03 -0500 Subject: [PATCH 016/101] bug and PEP8 Fixed bug where two tests had same name, and some PEP8 inconsistencies. --- aacgmv2/tests/test_c_aacgmv2.py | 2 +- aacgmv2/tests/test_py_aacgmv2.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aacgmv2/tests/test_c_aacgmv2.py b/aacgmv2/tests/test_c_aacgmv2.py index 03da9190..69e53dac 100644 --- a/aacgmv2/tests/test_c_aacgmv2.py +++ b/aacgmv2/tests/test_c_aacgmv2.py @@ -243,7 +243,7 @@ def test_inv_mlt_convert_yrsec(self): np.testing.assert_almost_equal(mlon_1, -153.5339, decimal=4) np.testing.assert_almost_equal(mlon_2, 41.4661, decimal=4) np.testing.assert_almost_equal(mlon_2, 11.4661, decimal=4) - + def test_mlt_convert(self): """Test MLT calculation""" mlt_args_1 = list(flatten([long_date, 270.0, diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index 706a45bf..ecb661ad 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -203,7 +203,7 @@ def test_convert_latlon_datetime_date(self): assert lon_1 == lon_2 assert r_1 == r_2 - def test_convert_latlon_datetime_date(self): + def test_convert_latlon_arr_datetime_date(self): """Test array latlon conversion with date and datetime input""" lat_1, lon_1, r_1 = aacgmv2.convert_latlon_arr([60], [0], [300], self.ddate) @@ -223,7 +223,7 @@ def test_warning_below_ground_convert_latlon(self): assert l.check(("root", "WARNING", lwarn)) is None l.uninstall() - + def test_warning_below_ground_convert_latlon_arr(self): """ Test that a warning is issued if altitude is below zero""" import testfixtures @@ -255,7 +255,7 @@ def test_convert_latlon_lat_failure(self): with pytest.raises(AssertionError): aacgmv2.convert_latlon(-91, 0, 300, self.dtime) - + def test_convert_latlon_arr_lat_failure(self): """Test error return for co-latitudes above 90 for an array""" with pytest.raises(AssertionError): From 9dcde022a481e15a5d4c7410700766079c144c1a Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 12:16:48 -0500 Subject: [PATCH 017/101] Bug in python tests Fixed bug in python tests. --- aacgmv2/tests/test_dep_aacgmv2.py | 2 +- aacgmv2/tests/test_py_aacgmv2.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aacgmv2/tests/test_dep_aacgmv2.py b/aacgmv2/tests/test_dep_aacgmv2.py index 284defa2..4c44b349 100644 --- a/aacgmv2/tests/test_dep_aacgmv2.py +++ b/aacgmv2/tests/test_dep_aacgmv2.py @@ -127,7 +127,7 @@ def test_convert_badidea_failure(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert lat.shape == lon.shape & lat.shape == (1,) - assert np.all(np.isnan(lat), np.isnan(lon)]) + assert np.all([np.isnan(lat), np.isnan(lon)]) def test_convert_location_failure(self): """Test conversion with a bad location""" diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index ecb661ad..cb706b0d 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -179,7 +179,7 @@ def test_convert_latlon_arr_badidea_failure(self): assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (1,) - assert np.all(np.isnan(lat), np.isnan(lon), np.isnan(r)]) + assert np.all([np.isnan(lat), np.isnan(lon), np.isnan(r)]) def test_convert_latlon_arr_location_failure(self): """Test array latlon conversion with a bad location""" @@ -425,7 +425,7 @@ def test_get_aacgm_coord_arr_badidea_failure(self): assert isinstance(mlt, np.ndarray) assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ mlt.shape == (1,) - assert np.all(np.isnan(mlat), np.isnan(mlon), np.isnan(mlt)]) + assert np.all([np.isnan(mlat), np.isnan(mlon), np.isnan(mlt)]) def test_get_aacgm_coord_arr_location_failure(self): """Test array AACGMV2 calculation with a bad location""" From bee4a35de7b38f89a085e851911d7120ea50a641 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 13:03:51 -0500 Subject: [PATCH 018/101] travis update Attempt to simplify tox matrix for different python versions --- .travis.yml | 53 ++----- tox.ini | 422 +++------------------------------------------------- 2 files changed, 34 insertions(+), 441 deletions(-) diff --git a/.travis.yml b/.travis.yml index 91cbed8d..0bbbcf70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,49 +1,24 @@ language: python -python: '3.5' +python: + - "2.7" + - "3.3" + - "3.4" + - "3.5" + - "3.6" sudo: false env: global: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check - - TOXENV=2.7-np1.14,extension-coveralls,coveralls - - TOXENV=2.7-np1.14-nocover - - TOXENV=2.7-np1.10,extension-coveralls,coveralls - - TOXENV=2.7-np1.10-nocover - - TOXENV=2.7-np1.8,extension-coveralls,coveralls - - TOXENV=2.7-np1.8-nocover - - TOXENV=2.7-np1.9,extension-coveralls,coveralls - - TOXENV=2.7-np1.9-nocover - - TOXENV=3.3-np1.10,extension-coveralls,coveralls - - TOXENV=3.3-np1.10-nocover - - TOXENV=3.3-np1.8,extension-coveralls,coveralls - - TOXENV=3.3-np1.8-nocover - - TOXENV=3.3-np1.9,extension-coveralls,coveralls - - TOXENV=3.3-np1.9-nocover - - TOXENV=3.4-np1.14,extension-coveralls,coveralls - - TOXENV=3.4-np1.14-nocover - - TOXENV=3.4-np1.10,extension-coveralls,coveralls - - TOXENV=3.4-np1.10-nocover - - TOXENV=3.4-np1.8,extension-coveralls,coveralls - - TOXENV=3.4-np1.8-nocover - - TOXENV=3.4-np1.9,extension-coveralls,coveralls - - TOXENV=3.4-np1.9-nocover - - TOXENV=3.5-np1.14,extension-coveralls,coveralls - - TOXENV=3.5-np1.14-nocover - - TOXENV=3.5-np1.10,extension-coveralls,coveralls - - TOXENV=3.5-np1.10-nocover - - TOXENV=3.5-np1.8,extension-coveralls,coveralls - - TOXENV=3.5-np1.8-nocover - - TOXENV=3.5-np1.9,extension-coveralls,coveralls - - TOXENV=3.5-np1.9-nocover - - TOXENV=3.6-np1.14,extension-coveralls,coveralls - - TOXENV=3.6-np1.14-nocover - - TOXENV=3.6-np1.10,extension-coveralls,coveralls - - TOXENV=3.6-np1.10-nocover - - TOXENV=3.6-np1.8,extension-coveralls,coveralls - - TOXENV=3.6-np1.8-nocover - - TOXENV=3.6-np1.9,extension-coveralls,coveralls - - TOXENV=3.6-np1.9-nocover + - TOXENV=np1.14,extension-coveralls,coveralls + - TOXENV=np1.14-nocover + - TOXENV=np1.10,extension-coveralls,coveralls + - TOXENV=np1.10-nocover + - TOXENV=np1.8,extension-coveralls,coveralls + - TOXENV=np1.8-nocover + - TOXENV=np1.9,extension-coveralls,coveralls + - TOXENV=np1.9-nocover before_install: - python --version - uname -a diff --git a/tox.ini b/tox.ini index e791867c..370ff917 100644 --- a/tox.ini +++ b/tox.ini @@ -2,46 +2,14 @@ envlist = clean, check, - 2.7-np1.8, - 2.7-np1.8-nocover, - 2.7-np1.9, - 2.7-np1.9-nocover, - 2.7-np1.10, - 2.7-np1.10-nocover, - 2.7-np1.14, - 2.7-np1.14-nocover, - 3.3-np1.8, - 3.3-np1.8-nocover, - 3.3-np1.9, - 3.3-np1.9-nocover, - 3.3-np1.10, - 3.3-np1.10-nocover, - 3.4-np1.10, - 3.4-np1.10-nocover, - 3.4-np1.8, - 3.4-np1.8-nocover, - 3.4-np1.9, - 3.4-np1.9-nocover, - 3.5-np1.10, - 3.5-np1.10-nocover, - 3.4-np1.14, - 3.4-np1.14-nocover, - 3.5-np1.8, - 3.5-np1.8-nocover, - 3.5-np1.9, - 3.5-np1.9-nocover, - 3.5-np1.10, - 3.5-np1.10-nocover, - 3.5-np1.14, - 3.5-np1.14-nocover, - 3.6-np1.8, - 3.6-np1.8-nocover, - 3.6-np1.9, - 3.6-np1.9-nocover, - 3.6-np1.10, - 3.6-np1.10-nocover, - 3.6-np1.14, - 3.6-np1.14-nocover, + np1.8, + np1.8-nocover, + np1.9, + np1.9-nocover, + np1.10, + np1.10-nocover, + np1.14, + np1.14-nocover, report, docs @@ -89,7 +57,7 @@ passenv = * [testenv:check] -basepython = python3.4 +basepython = python3.6 deps = docutils check-manifest @@ -134,7 +102,7 @@ commands = coveralls --build-root=. --include=src --dump=extension-coveralls.json [] [testenv:report] -basepython = python3.4 +basepython = python3.6 deps = coverage skip_install = true usedevelop = false @@ -148,8 +116,7 @@ skip_install = true usedevelop = false deps = coverage -[testenv:2.7-np1.14] -basepython = {env:TOXPYTHON:python2.7} +[testenv:np1.14] setenv = {[testenv]setenv} WITH_COVERAGE=yes @@ -163,8 +130,7 @@ deps = pytest-cov numpy>=1.14,<1.15 -[testenv:2.7-np1.10] -basepython = {env:TOXPYTHON:python2.7} +[testenv:np1.10] setenv = {[testenv]setenv} WITH_COVERAGE=yes @@ -178,20 +144,17 @@ deps = pytest-cov numpy>=1.10,<1.11 -[testenv:2.7-np1.10-nocover] -basepython = {env:TOXPYTHON:python2.7} +[testenv:np1.10-nocover] deps = {[testenv]deps} numpy>=1.10,<1.11 -[testenv:2.7-np1.14-nocover] -basepython = {env:TOXPYTHON:python2.7} +[testenv:np1.14-nocover] deps = {[testenv]deps} numpy>=1.14,<1.15 -[testenv:2.7-np1.8] -basepython = {env:TOXPYTHON:python2.7} +[testenv:np1.8] setenv = {[testenv]setenv} WITH_COVERAGE=yes @@ -205,14 +168,12 @@ deps = pytest-cov numpy>=1.8,<1.9 -[testenv:2.7-np1.8-nocover] -basepython = {env:TOXPYTHON:python2.7} +[testenv:np1.8-nocover] deps = {[testenv]deps} numpy>=1.8,<1.9 -[testenv:2.7-np1.9] -basepython = {env:TOXPYTHON:python2.7} +[testenv:np1.9] setenv = {[testenv]setenv} WITH_COVERAGE=yes @@ -226,355 +187,12 @@ deps = pytest-cov numpy>=1.9,<1.10 -[testenv:2.7-np1.9-nocover] -basepython = {env:TOXPYTHON:python2.7} +[testenv:np1.9-nocover] deps = {[testenv]deps} numpy>=1.9,<1.10 -[testenv:3.3-np1.10] -basepython = {env:TOXPYTHON:python3.3} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.10,<1.11 - -[testenv:3.3-np1.10-nocover] -basepython = {env:TOXPYTHON:python3.3} -deps = - {[testenv]deps} - numpy>=1.10,<1.11 - -[testenv:3.3-np1.8] -basepython = {env:TOXPYTHON:python3.3} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.8,<1.9 - -[testenv:3.3-np1.8-nocover] -basepython = {env:TOXPYTHON:python3.3} -deps = - {[testenv]deps} - numpy>=1.8,<1.9 - -[testenv:3.3-np1.9] -basepython = {env:TOXPYTHON:python3.3} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.9,<1.10 - -[testenv:3.3-np1.9-nocover] -basepython = {env:TOXPYTHON:python3.3} -deps = - {[testenv]deps} - numpy>=1.9,<1.10 - -[testenv:3.4-np1.10] -basepython = {env:TOXPYTHON:python3.4} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.10,<1.11 - -[testenv:3.4-np1.10-nocover] -basepython = {env:TOXPYTHON:python3.4} -deps = - {[testenv]deps} - numpy>=1.10,<1.11 - -[testenv:3.4-np1.14] -basepython = {env:TOXPYTHON:python3.4} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.14,<1.15 - -[testenv:3.4-np1.14-nocover] -basepython = {env:TOXPYTHON:python3.4} -deps = - {[testenv]deps} - numpy>=1.14,<1.15 - -[testenv:3.4-np1.8] -basepython = {env:TOXPYTHON:python3.4} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.8,<1.9 - -[testenv:3.4-np1.8-nocover] -basepython = {env:TOXPYTHON:python3.4} -deps = - {[testenv]deps} - numpy>=1.8,<1.9 - -[testenv:3.4-np1.9] -basepython = {env:TOXPYTHON:python3.4} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.9,<1.10 - -[testenv:3.4-np1.9-nocover] -basepython = {env:TOXPYTHON:python3.4} -deps = - {[testenv]deps} - numpy>=1.9,<1.10 - -[testenv:3.5-np1.10] -basepython = {env:TOXPYTHON:python3.5} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.10,<1.11 - -[testenv:3.5-np1.10-nocover] -basepython = {env:TOXPYTHON:python3.5} -deps = - {[testenv]deps} - numpy>=1.10,<1.11 - -[testenv:3.5-np1.14] -basepython = {env:TOXPYTHON:python3.5} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.14,<1.15 - -[testenv:3.5-np1.14-nocover] -basepython = {env:TOXPYTHON:python3.5} -deps = - {[testenv]deps} - numpy>=1.14,<1.15 - -[testenv:3.5-np1.8] -basepython = {env:TOXPYTHON:python3.5} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.8,<1.9 - -[testenv:3.5-np1.8-nocover] -basepython = {env:TOXPYTHON:python3.5} -deps = - {[testenv]deps} - numpy>=1.8,<1.9 - -[testenv:3.5-np1.9] -basepython = {env:TOXPYTHON:python3.5} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.9,<1.10 - -[testenv:3.5-np1.9-nocover] -basepython = {env:TOXPYTHON:python3.5} -deps = - {[testenv]deps} - numpy>=1.9,<1.10 - -[testenv:3.6-np1.10] -basepython = {env:TOXPYTHON:python3.6} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.10,<1.11 - -[testenv:3.6-np1.10-nocover] -basepython = {env:TOXPYTHON:python3.6} -deps = - {[testenv]deps} - numpy>=1.10,<1.11 - -[testenv:3.6-np1.14] -basepython = {env:TOXPYTHON:python3.6} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.14,<1.15 - -[testenv:3.6-np1.14-nocover] -basepython = {env:TOXPYTHON:python3.6} -deps = - {[testenv]deps} - numpy>=1.14,<1.15 - -[testenv:3.6-np1.8] -basepython = {env:TOXPYTHON:python3.6} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.8,<1.9 - -[testenv:3.6-np1.8-nocover] -basepython = {env:TOXPYTHON:python3.6} -deps = - {[testenv]deps} - numpy>=1.8,<1.9 - -[testenv:3.6-np1.9] -basepython = {env:TOXPYTHON:python3.6} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.9,<1.10 - -[testenv:3.6-np1.9-nocover] -basepython = {env:TOXPYTHON:python3.6} -deps = - {[testenv]deps} - numpy>=1.9,<1.10 - -[testenv:2.7-buildonly-nocover] -basepython = {env:TOXPYTHON:python2.7} -deps = -skip_install = true -commands = - -[testenv:3.3-buildonly-nocover] -basepython = {env:TOXPYTHON:python3.3} +[testenv:buildonly-nocover] deps = skip_install = true -commands = - -[testenv:3.4-buildonly-nocover] -basepython = {env:TOXPYTHON:python3.4} -deps = -skip_install = true -commands = - -[testenv:3.5-buildonly-nocover] -basepython = {env:TOXPYTHON:python3.5} -deps = -skip_install = true -commands = - -[testenv:3.6-buildonly-nocover] -basepython = {env:TOXPYTHON:python3.6} -deps = -skip_install = true -commands = - - +commands = \ No newline at end of file From e410c9bb3333a272718cfc479c95911371d893a3 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 15:42:42 -0500 Subject: [PATCH 019/101] Test and function bugs Fix bugs in tests and functions. --- aacgmv2/__main__.py | 1 + aacgmv2/depricated.py | 1 + aacgmv2/tests/test_c_aacgmv2.py | 55 +++++++++++++++---------------- aacgmv2/tests/test_dep_aacgmv2.py | 40 +++++++++++----------- aacgmv2/tests/test_py_aacgmv2.py | 53 ++++++++++++++++++----------- aacgmv2/wrapper.py | 16 ++++++--- 6 files changed, 92 insertions(+), 74 deletions(-) diff --git a/aacgmv2/__main__.py b/aacgmv2/__main__.py index a486dbbe..a83c2c15 100644 --- a/aacgmv2/__main__.py +++ b/aacgmv2/__main__.py @@ -110,6 +110,7 @@ def main(): dtime.day, dtime.hour, dtime.minute, dtime.second, array[:,1], + aacgmv2.AACGM_v2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS) np.savetxt(args.file_out, out, fmt='%.8f') diff --git a/aacgmv2/depricated.py b/aacgmv2/depricated.py index 7883dd1c..29ca09fb 100644 --- a/aacgmv2/depricated.py +++ b/aacgmv2/depricated.py @@ -103,6 +103,7 @@ def convert_mlt(arr, dtime, m2a=False): out = aacgmv2._aacgmv2.mlt_convert(dtime.year, dtime.month, dtime.day, dtime.hour, dtime.minute, dtime.second, arr, + aacgmv2.AACGM_v2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS) return out diff --git a/aacgmv2/tests/test_c_aacgmv2.py b/aacgmv2/tests/test_c_aacgmv2.py index 69e53dac..fea5483e 100644 --- a/aacgmv2/tests/test_c_aacgmv2.py +++ b/aacgmv2/tests/test_c_aacgmv2.py @@ -213,29 +213,30 @@ def test_forbidden(self): def test_inv_mlt_convert(self): """Test MLT inversion""" - mlt_args_1 = list(flatten([long_date, 12.0, aacgmv2.IGRF_12_COEFFS])) - mlt_args_2 = list(flatten([long_date, 25.0, aacgmv2.IGRF_12_COEFFS])) - mlt_args_3 = list(flatten([long_date, -1.0, aacgmv2.IGRF_12_COEFFS])) - - mlon_1 = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args_1) - mlon_2 = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args_2) - mlon_3 = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args_3) + mlt_args = list(self.long_date) + mlt_args.extend([12.0, aacgmv2.IGRF_12_COEFFS]) + mlon = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args) + np.testing.assert_almost_equal(mlon, -153.5339, decimal=4) - np.testing.assert_almost_equal(mlon_1, -153.5339, decimal=4) - np.testing.assert_almost_equal(mlon_2, 41.4661, decimal=4) - np.testing.assert_almost_equal(mlon_2, 11.4661, decimal=4) + mlt_args[-2] = 25.0 + mlon = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args) + np.testing.assert_almost_equal(mlon, 41.4661, decimal=4) + + mlt_args[-2] = -1.0 + mlon = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args) + np.testing.assert_almost_equal(mlon, 11.4661, decimal=4) def test_inv_mlt_convert_yrsec(self): """Test MLT inversion with year and seconds of year""" import datetime as dt - dtime = dt.datetime(*long_date) + dtime = dt.datetime(*self.long_date) soy = (int(dtime.strftime("%j"))-1) * 86400 + dtime.hour * 3600 + \ dtime.minute * 60 + dtime.second mlt_args_1 = [dtime.year, soy, 12.0, aacgmv2.IGRF_12_COEFFS] mlt_args_2 = [dtime.year, soy, 25.0, aacgmv2.IGRF_12_COEFFS] mlt_args_3 = [dtime.year, soy, -1.0, aacgmv2.IGRF_12_COEFFS] - + mlon_1 = aacgmv2._aacgmv2.inv_mlt_convert_yrsec(*mlt_args_1) mlon_2 = aacgmv2._aacgmv2.inv_mlt_convert_yrsec(*mlt_args_2) mlon_3 = aacgmv2._aacgmv2.inv_mlt_convert_yrsec(*mlt_args_3) @@ -246,28 +247,24 @@ def test_inv_mlt_convert_yrsec(self): def test_mlt_convert(self): """Test MLT calculation""" - mlt_args_1 = list(flatten([long_date, 270.0, - aacgmv2.AACGM_v2_DAT_PREFIX, - aacgmv2.IGRF_12_COEFFS])) - mlt_args_2 = list(flatten([long_date, 80.0, - aacgmv2.AACGM_v2_DAT_PREFIX, - aacgmv2.IGRF_12_COEFFS])) - mlt_args_3 = list(flatten([long_date, -90.0, - aacgmv2.AACGM_v2_DAT_PREFIX, - aacgmv2.IGRF_12_COEFFS])) - - mlt_1 = aacgmv2._aacgmv2.mlt_convert(*mlt_args_1) - mlt_2 = aacgmv2._aacgmv2.mlt_convert(*mlt_args_2) - mlt_3 = aacgmv2._aacgmv2.mlt_convert(*mlt_args_3) + mlt_args = list(self.long_date) + mlt_args.extend([270.0, aacgmv2.AACGM_v2_DAT_PREFIX, + aacgmv2.IGRF_12_COEFFS]) + mlt = aacgmv2._aacgmv2.mlt_convert(*mlt_args) + np.testing.assert_almost_equal(mlt, 16.2356, decimal=4) - np.testing.assert_almost_equal(mlt_1, 16.2356, decimal=4) - np.testing.assert_almost_equal(mlt_2, 3.5689, decimal=4) - np.testing.assert_equal(mlt_1, mlt_3) + mlt_args[-3] = 80.0 + mlt = aacgmv2._aacgmv2.mlt_convert(*mlt_args) + np.testing.assert_almost_equal(mlt, 3.5689, decimal=4) + + mlt_args[-3] = -90.0 + mlt = aacgmv2._aacgmv2.mlt_convert(*mlt_args) + np.testing.assert_almost_equal(mlt, 16.2356, decimal=4) def test_mlt_convert_yrsec(self): """Test MLT calculation using year and seconds of year""" import datetime as dt - dtime = dt.datetime(*long_date) + dtime = dt.datetime(*self.long_date) soy = (int(dtime.strftime("%j"))-1) * 86400 + dtime.hour * 3600 + \ dtime.minute * 60 + dtime.second mlt_args_1 = [dtime.year, soy, 270.0, aacgmv2.AACGM_v2_DAT_PREFIX, diff --git a/aacgmv2/tests/test_dep_aacgmv2.py b/aacgmv2/tests/test_dep_aacgmv2.py index 4c44b349..b6c124e5 100644 --- a/aacgmv2/tests/test_dep_aacgmv2.py +++ b/aacgmv2/tests/test_dep_aacgmv2.py @@ -43,23 +43,23 @@ def test_convert_single_val(self): lat, lon = aacgmv2.convert(60, 0, 300, self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape[0] == 1 + assert lat.shape == lon.shape & lat.shape == (1,) np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) def test_convert_list(self): """Test conversion for list input""" - lat, lon = aacgmv2.convert_latlon_arr([60], [0], [300], self.dtime) + lat, lon = aacgmv2.convert([60], [0], [300], self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape[0] == 1 + assert lat.shape == lon.shape & lat.shape == (1,) np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) lat, lon = aacgmv2.convert([60, 61], [0, 0], [300, 300], self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape[0] == 2 + assert lat.shape == lon.shape & lat.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) @@ -69,7 +69,7 @@ def test_convert_arr(self): np.array([300]), self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape[0] == 1 + assert lat.shape == lon.shape & lat.shape == (1,) np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) @@ -77,23 +77,23 @@ def test_convert_arr(self): np.array([300, 300]), self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape[0] == 2 + assert lat.shape == lon.shape & lat.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) def test_convert_unequal(self): - """Test array latlon conversion for unequal sized input""" + """Test conversion for unequal sized input""" lat, lon = aacgmv2.convert([60, 61], 0, 300, self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape[0] == 2 + assert lat.shape == lon.shape & lat.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) lat, lon = aacgmv2.convert(np.array([60, 61]), 0, 300, self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape[0] == 2 + assert lat.shape == lon.shape & lat.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) @@ -173,9 +173,9 @@ def test_convert_lat_failure(self): def test_inv_convert_mlt_single(self): """Test MLT inversion for a single value""" - mlon_1 = aacgmv2.convert_mlt(12.0, self.dtime, m2a=False) - mlon_2 = aacgmv2.convert_mlt(25.0, self.dtime, m2a=False) - mlon_3 = aacgmv2.convert_mlt(-1.0, self.dtime, m2a=False) + mlon_1 = aacgmv2.convert_mlt(12.0, self.dtime, m2a=True) + mlon_2 = aacgmv2.convert_mlt(25.0, self.dtime, m2a=True) + mlon_3 = aacgmv2.convert_mlt(-1.0, self.dtime, m2a=True) np.testing.assert_almost_equal(mlon_1, -153.5339, decimal=4) np.testing.assert_almost_equal(mlon_2, 41.4661, decimal=4) @@ -184,7 +184,7 @@ def test_inv_convert_mlt_single(self): def test_inv_convert_mlt_list(self): """Test MLT inversion for a list""" mlt_list = [12.0, 25.0, -1.0] - mlon = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=False) + mlon = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=True) np.testing.assert_allclose(mlon, [-153.5339, 41.4661, 11.4661], rtol=1.0e-4) @@ -192,16 +192,16 @@ def test_inv_convert_mlt_list(self): def test_inv_convert_mlt_arr(self): """Test MLT inversion for an array""" mlt_arr = np.array([12.0, 25.0, -1.0]) - mlon = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=False) + mlon = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=True) np.testing.assert_allclose(mlon, [-153.5339, 41.4661, 11.4661], rtol=1.0e-4) def test_mlt_convert_single(self): """Test MLT calculation for a single value""" - mlt_1 = aacgmv2.convert_mlt(270.0, self.dtime, m2a=True) - mlt_2 = aacgmv2.convert_mlt(80.0, self.dtime, m2a=True) - mlt_3 = aacgmv2.convert_mlt(-90.0, self.dtime, m2a=True) + mlt_1 = aacgmv2.convert_mlt(270.0, self.dtime, m2a=False) + mlt_2 = aacgmv2.convert_mlt(80.0, self.dtime, m2a=False) + mlt_3 = aacgmv2.convert_mlt(-90.0, self.dtime, m2a=False) np.testing.assert_almost_equal(mlt_1, 16.2356, decimal=4) np.testing.assert_almost_equal(mlt_2, 3.5689, decimal=4) @@ -210,14 +210,14 @@ def test_mlt_convert_single(self): def test_mlt_convert_list(self): """Test MLT calculation for a list""" mlt_list = [270.0, 80.0, -90.0] - mlt = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=True) + mlt = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=False) np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-4) def test_mlt_convert_arr(self): """Test MLT calculation for an array""" mlt_arr = np.array([270.0, 80.0, -90.0]) - mlt = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=True) + mlt = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=False) np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-4) @@ -255,4 +255,4 @@ def test_igrf_dipole_axis(self): """Test the IGRF dipole axis calculation""" m = aacgmv2.depricated.igrf_dipole_axis(self.dtime) - np.testing.assert_allclose(m, [0.0503, -0.1607, 0.9857], rtol=1.0e-4) + np.testing.assert_allclose(m, [0.0503, -0.1606, 0.9857], rtol=1.0e-4) diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index cb706b0d..69ef1674 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -67,7 +67,7 @@ def test_convert_latlon_arr_single_val(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 1 + assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (1,) np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) np.testing.assert_allclose(r, [1.0457], rtol=1e-4) @@ -78,7 +78,7 @@ def test_convert_latlon_arr_list(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 1 + assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (1,) np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) np.testing.assert_allclose(r, [1.0457], rtol=1e-4) @@ -88,7 +88,7 @@ def test_convert_latlon_arr_list(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 2 + assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) @@ -100,7 +100,7 @@ def test_convert_latlon_arr_arr(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 1 + assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (1,) np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) np.testing.assert_allclose(r, [1.0457], rtol=1e-4) @@ -112,7 +112,7 @@ def test_convert_latlon_arr_arr(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 2 + assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) @@ -123,7 +123,7 @@ def test_convert_latlon_arr_unequal(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 2 + assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) @@ -133,7 +133,7 @@ def test_convert_latlon_arr_unequal(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape[0] == 2 + assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) @@ -264,7 +264,7 @@ def test_convert_latlon_arr_lat_failure(self): def test_get_aacgm_coord(self): """Test single value AACGMV2 calculation, defaults to TRACE""" mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 0, 300, self.dtime) - np.testing.assert_almost_equal(mlat, 58.2257, decimal=4) + np.testing.assert_almost_equal(mlat, 58.2247, decimal=4) np.testing.assert_almost_equal(mlon, 81.1761, decimal=4) np.testing.assert_almost_equal(mlt, 0.1889, decimal=4) @@ -300,7 +300,7 @@ def test_get_aacgm_coord_arr_single_val(self): assert isinstance(mlon, np.ndarray) assert isinstance(mlt, np.ndarray) assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ - mlt.shape[0] == 1 + mlt.shape == (1,) np.testing.assert_allclose(mlat, [58.2247], rtol=1e-4) np.testing.assert_allclose(mlon, [81.1761], rtol=1e-4) np.testing.assert_allclose(mlt, [0.1889], rtol=1e-4) @@ -313,7 +313,7 @@ def test_get_aacgm_coord_arr_list(self): assert isinstance(mlon, np.ndarray) assert isinstance(mlt, np.ndarray) assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ - mlt.shape[0] == 1 + mlt.shape == (1,) np.testing.assert_allclose(mlat, [58.2247], rtol=1e-4) np.testing.assert_allclose(mlon, [81.1761], rtol=1e-4) np.testing.assert_allclose(mlt, [0.1889], rtol=1e-4) @@ -324,7 +324,7 @@ def test_get_aacgm_coord_arr_list(self): assert isinstance(mlon, np.ndarray) assert isinstance(r, np.ndarray) assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ - mlt.shape[0] == 2 + mlt.shape == (2,) np.testing.assert_allclose(mlat, [58.2247, 59.3165], rtol=1e-4) np.testing.assert_allclose(mlon, [81.1761, 81.6228], rtol=1e-4) np.testing.assert_allclose(mlt, [0.1889, 0.2187], rtol=1e-4) @@ -337,9 +337,9 @@ def test_get_aacgm_coord_arr_arr(self): self.dtime) assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) - assert isinstance(r, np.ndarray) + assert isinstance(mlt, np.ndarray) assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ - mlt.shape[0] == 1 + mlt.shape == (1,) np.testing.assert_allclose(mlat, [58.2247], rtol=1e-4) np.testing.assert_allclose(mlon, [81.1761], rtol=1e-4) np.testing.assert_allclose(mlt, [0.1889], rtol=1e-4) @@ -350,9 +350,9 @@ def test_get_aacgm_coord_arr_arr(self): self.dtime) assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) - assert isinstance(r, np.ndarray) + assert isinstance(mlt, np.ndarray) assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ - mlt.shape[0] == 2 + mlt.shape == (2,) np.testing.assert_allclose(mlat, [58.2247, 59.3165], rtol=1e-4) np.testing.assert_allclose(mlon, [81.1761, 81.6228], rtol=1e-4) np.testing.assert_allclose(mlt, [0.1889, 0.2187], rtol=1e-4) @@ -363,9 +363,9 @@ def test_get_aacgm_coord_arr_unequal(self): self.dtime) assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) - assert isinstance(r, np.ndarray) + assert isinstance(mlt, np.ndarray) assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ - mlt.shape[0] == 2 + mlt.shape == (2,) np.testing.assert_allclose(mlat, [58.2247, 59.3165], rtol=1e-4) np.testing.assert_allclose(mlon, [81.1761, 81.6228], rtol=1e-4) np.testing.assert_allclose(mlt, [0.1889, 0.2187], rtol=1e-4) @@ -374,9 +374,9 @@ def test_get_aacgm_coord_arr_unequal(self): 300, self.dtime) assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) - assert isinstance(r, np.ndarray) + assert isinstance(mlt, np.ndarray) assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ - mlt.shape[0] == 2 + mlt.shape == (2,) np.testing.assert_allclose(mlat, [58.2247, 59.3165], rtol=1e-4) np.testing.assert_allclose(mlon, [81.1761, 81.6228], rtol=1e-4) np.testing.assert_allclose(mlt, [0.1889, 0.2187], rtol=1e-4) @@ -568,9 +568,22 @@ def test_convert_str_to_bit_geocentric(self): assert aacgmv2.convert_str_to_bit("GEOCENTRIC") == \ aacgmv2._aacgmv2.GEOCENTRIC + def test_convert_str_to_bit_lowercase(self): + """Test conversion from string code to bit for a lowercase code""" + assert aacgmv2.convert_str_to_bit("g2a") == aacgmv2._aacgmv2.G2A + + def test_convert_str_to_bit_spaces(self): + """Test conversion from string code to bit for a code with spaces""" + assert aacgmv2.convert_str_to_bit("G2A | trace") == \ + aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.TRACE + + def test_convert_str_to_bit_invalid(self): + """Test conversion from string code to bit for an invalid code""" + assert aacgmv2.convert_str_to_bit("ggoogg|") == aacgmv2._aacgmv2.G2A + def test_convert_bool_to_bit_g2a(self): """Test conversion from string code to bit G2A""" - assert aacgmv2.convert_bool_to_bit(g2a=True) == aacgmv2._aacgmv2.G2A + assert aacgmv2.convert_bool_to_bit() == aacgmv2._aacgmv2.G2A def test_convert_bool_to_bit_a2g(self): """Test conversion from string code to bit A2G""" diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index f3afb8e7..d08e9981 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -68,7 +68,7 @@ def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, # Test time if isinstance(dtime, dt.date): - date = dt.datetime.combine(dtime, dt.time(0)) + dtime = dt.datetime.combine(dtime, dt.time(0)) assert isinstance(dtime, dt.datetime), \ logging.error('time must be specified as datetime object') @@ -212,7 +212,7 @@ def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", # Test time if isinstance(dtime, dt.date): - date = dt.datetime.combine(dtime, dt.time(0)) + dtime = dt.datetime.combine(dtime, dt.time(0)) assert isinstance(dtime, dt.datetime), \ logging.error('time must be specified as datetime object') @@ -423,16 +423,22 @@ def convert_str_to_bit(code): -------- bit_code : (int) code specification in bits + + Notes + -------- + Multiple codes should be seperated by pipes '|'. Invalid parts of the code + are ignored and no code defaults to 'G2A'. """ convert_code = {"G2A": c_aacgmv2.G2A, "A2G": c_aacgmv2.A2G, "TRACE": c_aacgmv2.TRACE, "BADIDEA": c_aacgmv2.BADIDEA, "GEOCENTRIC": c_aacgmv2.GEOCENTRIC, "ALLOWTRACE": c_aacgmv2.ALLOWTRACE} - code = code.upper() + # Force upper case, remove any spaces, and split along pipes + codes = code.upper().replace(" ", "").split("|") - bit_code = sum([convert_code[k] for k in convert_code.keys() - if code.find(k) >= 0]) + # Add the valid parts of the code, invalid elements are ignored + bit_code = sum([convert_code[k] for k in convert_code.keys() if k in codes]) return bit_code From 75c95a6a0f1df7ef1b5131107f05146945b18de9 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 15:43:14 -0500 Subject: [PATCH 020/101] Allow 3.3 np1.14 failure Allow failure of np1.14 with python 3.3 --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 0bbbcf70..d6188aba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,3 +41,7 @@ notifications: matrix: allow_failures: - env: "TOXENV=check" + - env: "TOXENV=np114-nocover" + python: 3.3 + - env: "TOXENV=np114-cover" + python: 3.3 From 61179b83f2b9bb673e00083657cc55fd3d34568f Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 15:43:56 -0500 Subject: [PATCH 021/101] Condense tox environment Condense writing tox environment. --- appveyor.yml | 32 +++++++------- tox.ini | 120 ++++++++++----------------------------------------- 2 files changed, 39 insertions(+), 113 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index c0ca2255..017ba65b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -309,22 +309,22 @@ artifacts: matrix: allow_failures: - TOXENV: 'check' - - TOXENV: '3.3-np1.8-nocover' - - TOXENV: '3.3-np1.8,codecov' - - TOXENV: '3.4-np1.8-nocover' - - TOXENV: '3.4-np1.8,codecov' - - TOXENV: '3.5-np1.8-nocover' - - TOXENV: '3.5-np1.8,codecov' - - TOXENV: '3.6-np1.8-nocover' - - TOXENV: '3.6-np1.8,codecov' - - TOXENV: '3.5-np1.9-nocover' - - TOXENV: '3.5-np1.9,codecov' - - TOXENV: '3.6-np1.9-nocover' - - TOXENV: '3.6-np1.9,codecov' - - TOXENV: '3.5-np1.10-nocover' - - TOXENV: '3.5-np1.10,codecov' - - TOXENV: '3.6-np1.10-nocover' - - TOXENV: '3.6-np1.10,codecov' + - TOXENV: '3.3-np18-nocover' + - TOXENV: '3.3-np18-cover,codecov' + - TOXENV: '3.4-np18-nocover' + - TOXENV: '3.4-np18-cover,codecov' + - TOXENV: '3.5-np18-nocover' + - TOXENV: '3.5-np18-cover,codecov' + - TOXENV: '3.6-np18-nocover' + - TOXENV: '3.6-np18-cover,codecov' + - TOXENV: '3.5-np19-nocover' + - TOXENV: '3.5-np19-cover,codecov' + - TOXENV: '3.6-np19-nocover' + - TOXENV: '3.6-np19-cover,codecov' + - TOXENV: '3.5-np110-nocover' + - TOXENV: '3.5-np110-cover,codecov' + - TOXENV: '3.6-np110-nocover' + - TOXENV: '3.6-np110-cover,codecov' - TOXENV: '2.7-buildonly-nocover' - TOXENV: '3.3-buildonly-nocover' - TOXENV: '3.4-buildonly-nocover' diff --git a/tox.ini b/tox.ini index 370ff917..7babac67 100644 --- a/tox.ini +++ b/tox.ini @@ -2,14 +2,7 @@ envlist = clean, check, - np1.8, - np1.8-nocover, - np1.9, - np1.9-nocover, - np1.10, - np1.10-nocover, - np1.14, - np1.14-nocover, + np{18,19,110,114}-{cover,nocover}, report, docs @@ -17,15 +10,37 @@ envlist = setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes + cover: WITH_COVERAGE=yes + cover: PY_CCOV=-coverage passenv = * +usedevelop = + cover: true deps = pytest pytest-capturelog + testfixtures + np18: numpy>=1.8,<1.9 + np19: numpy>=1.9,<1.10 + np110: numpy>=1.10,<1.11 + np114: numpy>=1.14,<1.15 + cover: pytest-cov commands = python setup.py clean --all build_ext --force --inplace {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} + cover: {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +[testenv:codecov] +deps = + codecov +skip_install = true +usedevelop = false +commands = + coverage combine + coverage report + coverage xml --ignore-errors + codecov [] + [testenv:spell] setenv = SPELLCHECK=1 @@ -57,7 +72,6 @@ passenv = * [testenv:check] -basepython = python3.6 deps = docutils check-manifest @@ -81,17 +95,6 @@ commands = coverage report coveralls --merge=extension-coveralls.json [] -[testenv:codecov] -deps = - codecov -skip_install = true -usedevelop = false -commands = - coverage combine - coverage report - coverage xml --ignore-errors - codecov [] - [testenv:extension-coveralls] deps = @@ -102,7 +105,6 @@ commands = coveralls --build-root=. --include=src --dump=extension-coveralls.json [] [testenv:report] -basepython = python3.6 deps = coverage skip_install = true usedevelop = false @@ -116,82 +118,6 @@ skip_install = true usedevelop = false deps = coverage -[testenv:np1.14] -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.14,<1.15 - -[testenv:np1.10] -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.10,<1.11 - -[testenv:np1.10-nocover] -deps = - {[testenv]deps} - numpy>=1.10,<1.11 - -[testenv:np1.14-nocover] -deps = - {[testenv]deps} - numpy>=1.14,<1.15 - -[testenv:np1.8] -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.8,<1.9 - -[testenv:np1.8-nocover] -deps = - {[testenv]deps} - numpy>=1.8,<1.9 - -[testenv:np1.9] -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - numpy>=1.9,<1.10 - -[testenv:np1.9-nocover] -deps = - {[testenv]deps} - numpy>=1.9,<1.10 - [testenv:buildonly-nocover] deps = skip_install = true From 17595e3400ec69d167de127bd4c6b9534d8e2cbf Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 15:53:15 -0500 Subject: [PATCH 022/101] tox update possibly helpful tox update --- appveyor.yml | 32 ++++++++++++++++---------------- tox.ini | 10 +++++----- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 017ba65b..6c8576b6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -309,22 +309,22 @@ artifacts: matrix: allow_failures: - TOXENV: 'check' - - TOXENV: '3.3-np18-nocover' - - TOXENV: '3.3-np18-cover,codecov' - - TOXENV: '3.4-np18-nocover' - - TOXENV: '3.4-np18-cover,codecov' - - TOXENV: '3.5-np18-nocover' - - TOXENV: '3.5-np18-cover,codecov' - - TOXENV: '3.6-np18-nocover' - - TOXENV: '3.6-np18-cover,codecov' - - TOXENV: '3.5-np19-nocover' - - TOXENV: '3.5-np19-cover,codecov' - - TOXENV: '3.6-np19-nocover' - - TOXENV: '3.6-np19-cover,codecov' - - TOXENV: '3.5-np110-nocover' - - TOXENV: '3.5-np110-cover,codecov' - - TOXENV: '3.6-np110-nocover' - - TOXENV: '3.6-np110-cover,codecov' + - TOXENV: '3.3-np1.8-nocover' + - TOXENV: '3.3-np1.8-cover,codecov' + - TOXENV: '3.4-np1.8-nocover' + - TOXENV: '3.4-np1.8-cover,codecov' + - TOXENV: '3.5-np1.8-nocover' + - TOXENV: '3.5-np1.8-cover,codecov' + - TOXENV: '3.6-np1.8-nocover' + - TOXENV: '3.6-np1.8-cover,codecov' + - TOXENV: '3.5-np1.9-nocover' + - TOXENV: '3.5-np1.9-cover,codecov' + - TOXENV: '3.6-np1.9-nocover' + - TOXENV: '3.6-np1.9-cover,codecov' + - TOXENV: '3.5-np1.10-nocover' + - TOXENV: '3.5-np1.10-cover,codecov' + - TOXENV: '3.6-np1.10-nocover' + - TOXENV: '3.6-np1.10-cover,codecov' - TOXENV: '2.7-buildonly-nocover' - TOXENV: '3.3-buildonly-nocover' - TOXENV: '3.4-buildonly-nocover' diff --git a/tox.ini b/tox.ini index 7babac67..8fbb640a 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ envlist = clean, check, - np{18,19,110,114}-{cover,nocover}, + np{1.8,1.9,1.10,1.14}-{cover,nocover}, report, docs @@ -20,10 +20,10 @@ deps = pytest pytest-capturelog testfixtures - np18: numpy>=1.8,<1.9 - np19: numpy>=1.9,<1.10 - np110: numpy>=1.10,<1.11 - np114: numpy>=1.14,<1.15 + np1.8: numpy>=1.8,<1.9 + np1.9: numpy>=1.9,<1.10 + np1.10: numpy>=1.10,<1.11 + np1.14: numpy>=1.14,<1.15 cover: pytest-cov commands = python setup.py clean --all build_ext --force --inplace From 66a3bc25f4b91ea57ca231e7a4bc0814ceb0e1ed Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 16:41:21 -0500 Subject: [PATCH 023/101] undepricated convert_mlt Moved convert_mlt from depricated to non-deprecated. --- aacgmv2/__init__.py | 5 +- aacgmv2/depricated.py | 40 ----------- aacgmv2/tests/test_dep_aacgmv2.py | 51 -------------- aacgmv2/tests/test_py_aacgmv2.py | 51 ++++++++++++++ aacgmv2/wrapper.py | 111 ++++++++++++++++++++---------- 5 files changed, 129 insertions(+), 129 deletions(-) diff --git a/aacgmv2/__init__.py b/aacgmv2/__init__.py index fca3b4d5..e16574b1 100644 --- a/aacgmv2/__init__.py +++ b/aacgmv2/__init__.py @@ -57,7 +57,7 @@ #--------------------------------------------------------------------- try: - from aacgmv2.wrapper import (convert_latlon, get_aacgm_coord) + from aacgmv2.wrapper import (convert_latlon, convert_mlt, get_aacgm_coord) from aacgmv2.wrapper import (convert_latlon_arr, get_aacgm_coord_arr) from aacgmv2.wrapper import (convert_bool_to_bit, convert_str_to_bit) except Exception as err: @@ -65,8 +65,7 @@ try: from aacgmv2 import (depricated) - from aacgmv2.depricated import (convert, convert_mlt, subsol) - from aacgmv2.depricated import (set_coeff_path) + from aacgmv2.depricated import (convert, subsol, set_coeff_path) except Exception as err: logging.exception(__file__ + ' -> aacgmv2: ' + str(err)) diff --git a/aacgmv2/depricated.py b/aacgmv2/depricated.py index 29ca09fb..a8cf4e5e 100644 --- a/aacgmv2/depricated.py +++ b/aacgmv2/depricated.py @@ -5,7 +5,6 @@ Functions ------------------------------------------------------------------------------- convert : Converts array location -convert_mlt : Get array mlt set_coeff_path : Previously set environment variables, no longer used subsol : finds subsolar geocentric longitude and latitude gc2gd_lat : Convert between geocentric and geodetic coordinates @@ -69,45 +68,6 @@ def convert(lat, lon, alt, date=None, a2g=False, trace=False, allowtrace=False, return lat_out, lon_out -def convert_mlt(arr, dtime, m2a=False): - """Converts between magnetic local time (MLT) and AACGM-v2 longitude - - Parameters - ------------ - arr : (array_line or float) - Magnetic longitudes or MLTs to convert - dtime : (datetime.datetime) - Date and time for MLT conversion in Universal Time (UT). - m2a : (bool) - Convert MLT to AACGM-v2 longitude (True) or magnetic longitude to MLT - (False). (default=False) - - Returns - -------- - out : (np.ndarray) - Converted coordinates/MLT - - Notes - ------- - This routine previously based on Laundal et al. 2016, but now uses the - improved calculation available in AACGM-V2.4. - """ - import aacgmv2._aacgmv2 as c_aacgmv2 - - if m2a: - out = aacgmv2._aacgmv2.inv_mlt_convert(dtime.year, dtime.month, - dtime.day, dtime.hour, - dtime.minute, dtime.second, - arr, aacgmv2.IGRF_12_COEFFS) - else: - out = aacgmv2._aacgmv2.mlt_convert(dtime.year, dtime.month, dtime.day, - dtime.hour, dtime.minute, - dtime.second, arr, - aacgmv2.AACGM_v2_DAT_PREFIX, - aacgmv2.IGRF_12_COEFFS) - - return out - def set_coeff_path(): """This depricated routine used to set environment variables, and now is not needed. diff --git a/aacgmv2/tests/test_dep_aacgmv2.py b/aacgmv2/tests/test_dep_aacgmv2.py index b6c124e5..27dd024a 100644 --- a/aacgmv2/tests/test_dep_aacgmv2.py +++ b/aacgmv2/tests/test_dep_aacgmv2.py @@ -20,7 +20,6 @@ def test_module_structure(self): """Test module structure for depricated routines""" assert aacgmv2 assert aacgmv2.convert - assert aacgmv2.convert_mlt assert aacgmv2.set_coeff_path assert aacgmv2.subsol assert aacgmv2.depricated @@ -171,56 +170,6 @@ def test_convert_lat_failure(self): with pytest.raises(AssertionError): aacgmv2.convert([91, 60, -91], 0, 300, self.dtime) - def test_inv_convert_mlt_single(self): - """Test MLT inversion for a single value""" - mlon_1 = aacgmv2.convert_mlt(12.0, self.dtime, m2a=True) - mlon_2 = aacgmv2.convert_mlt(25.0, self.dtime, m2a=True) - mlon_3 = aacgmv2.convert_mlt(-1.0, self.dtime, m2a=True) - - np.testing.assert_almost_equal(mlon_1, -153.5339, decimal=4) - np.testing.assert_almost_equal(mlon_2, 41.4661, decimal=4) - np.testing.assert_almost_equal(mlon_2, 11.4661, decimal=4) - - def test_inv_convert_mlt_list(self): - """Test MLT inversion for a list""" - mlt_list = [12.0, 25.0, -1.0] - mlon = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=True) - - np.testing.assert_allclose(mlon, [-153.5339, 41.4661, 11.4661], - rtol=1.0e-4) - - def test_inv_convert_mlt_arr(self): - """Test MLT inversion for an array""" - mlt_arr = np.array([12.0, 25.0, -1.0]) - mlon = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=True) - - np.testing.assert_allclose(mlon, [-153.5339, 41.4661, 11.4661], - rtol=1.0e-4) - - def test_mlt_convert_single(self): - """Test MLT calculation for a single value""" - mlt_1 = aacgmv2.convert_mlt(270.0, self.dtime, m2a=False) - mlt_2 = aacgmv2.convert_mlt(80.0, self.dtime, m2a=False) - mlt_3 = aacgmv2.convert_mlt(-90.0, self.dtime, m2a=False) - - np.testing.assert_almost_equal(mlt_1, 16.2356, decimal=4) - np.testing.assert_almost_equal(mlt_2, 3.5689, decimal=4) - np.testing.assert_equal(mlt_1, mlt_3) - - def test_mlt_convert_list(self): - """Test MLT calculation for a list""" - mlt_list = [270.0, 80.0, -90.0] - mlt = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=False) - - np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-4) - - def test_mlt_convert_arr(self): - """Test MLT calculation for an array""" - mlt_arr = np.array([270.0, 80.0, -90.0]) - mlt = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=False) - - np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-4) - def test_subsol(self): """Test the subsolar calculation""" doy = int(self.dtime.strftime("%j")) diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index 69ef1674..9c8bd4ca 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -22,6 +22,7 @@ def test_module_structure(self): assert aacgmv2 assert aacgmv2.convert_bool_to_bit assert aacgmv2.convert_str_to_bit + assert aacgmv2.convert_mlt assert aacgmv2.convert_latlon assert aacgmv2.convert_latlon_arr assert aacgmv2.get_aacgm_coord @@ -607,3 +608,53 @@ def test_convert_bool_to_bit_geocentric(self): """Test conversion from string code to bit GEOCENTRIC""" assert aacgmv2.convert_bool_to_bit(geocentric=True) == \ aacgmv2._aacgmv2.GEOCENTRIC + + def test_inv_convert_mlt_single(self): + """Test MLT inversion for a single value""" + mlon_1 = aacgmv2.convert_mlt(12.0, self.dtime, m2a=True) + mlon_2 = aacgmv2.convert_mlt(25.0, self.dtime, m2a=True) + mlon_3 = aacgmv2.convert_mlt(-1.0, self.dtime, m2a=True) + + np.testing.assert_almost_equal(mlon_1, -153.5339, decimal=4) + np.testing.assert_almost_equal(mlon_2, 41.4661, decimal=4) + np.testing.assert_almost_equal(mlon_2, 11.4661, decimal=4) + + def test_inv_convert_mlt_list(self): + """Test MLT inversion for a list""" + mlt_list = [12.0, 25.0, -1.0] + mlon = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=True) + + np.testing.assert_allclose(mlon, [-153.5339, 41.4661, 11.4661], + rtol=1.0e-4) + + def test_inv_convert_mlt_arr(self): + """Test MLT inversion for an array""" + mlt_arr = np.array([12.0, 25.0, -1.0]) + mlon = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=True) + + np.testing.assert_allclose(mlon, [-153.5339, 41.4661, 11.4661], + rtol=1.0e-4) + + def test_mlt_convert_single(self): + """Test MLT calculation for a single value""" + mlt_1 = aacgmv2.convert_mlt(270.0, self.dtime, m2a=False) + mlt_2 = aacgmv2.convert_mlt(80.0, self.dtime, m2a=False) + mlt_3 = aacgmv2.convert_mlt(-90.0, self.dtime, m2a=False) + + np.testing.assert_almost_equal(mlt_1, 16.2356, decimal=4) + np.testing.assert_almost_equal(mlt_2, 3.5689, decimal=4) + np.testing.assert_equal(mlt_1, mlt_3) + + def test_mlt_convert_list(self): + """Test MLT calculation for a list""" + mlt_list = [270.0, 80.0, -90.0] + mlt = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=False) + + np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-4) + + def test_mlt_convert_arr(self): + """Test MLT calculation for an array""" + mlt_arr = np.array([270.0, 80.0, -90.0]) + mlt = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=False) + + np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-4) diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index d08e9981..d2519501 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -9,6 +9,7 @@ get_aacgm_coord_arr : Get array magnetic lat, lon, mlt from geographic location convert_str_to_bit : Convert human readible AACGM flag to bits convert_bool_to_bit : Convert boolian flags to bits +convert_mlt : Get array mlt -------------- """ @@ -316,23 +317,19 @@ def get_aacgm_coord(glat, glon, height, dtime, method="TRACE", if igrf_file is None: igrf_file = aacgmv2.IGRF_12_COEFFS - # Initialize return values - mlat = None - mlon = None - mlt = None + # Initialize code + code = "G2A|{:s}".format(method) - try: - # Get magnetic lat and lon. - mlat, mlon, mr = convert_latlon(glat, glon, height, dtime, - code="G2A|{:s}".format(method), - igrf_file=igrf_file, - coeff_prefix=coeff_prefix) - # Get magnetic local time - mlt = c_aacgmv2.mlt_convert(dtime.year, dtime.month, dtime.day, - dtime.hour, dtime.minute, dtime.second, - mlon, coeff_prefix, igrf_file) - except: - logging.error("Unable to get magnetic lat/lon") + # Get magnetic lat and lon. + mlat, mlon, mr = convert_latlon(glat, glon, height, dtime, code=code, + igrf_file=igrf_file, + coeff_prefix=coeff_prefix) + # Get magnetic local time + if np.isnan(mlon): + mlt = np.nan + else: + mlt = convert_mlt(mlon, dtime, m2a=False, coeff_prefix=coeff_prefix, + igrf_file=igrf_file) return mlat, mlon, mlt @@ -382,26 +379,20 @@ def get_aacgm_coord_arr(glat, glon, height, dtime, method="TRACE", if igrf_file is None: igrf_file = aacgmv2.IGRF_12_COEFFS - # Initialize return values - mlat = None - mlon = None - mlt = None + # Initialize code + code = "G2A|{:s}".format(method) - try: - # Get magnetic lat and lon. - mlat, mlon, mr = convert_latlon_arr(glat, glon, height, dtime, - code="G2A|{:s}".format(method), - igrf_file=igrf_file, - coeff_prefix=coeff_prefix) - - if mlon is not None: - # Get magnetic local time - mlt_vectorised = np.vectorize(c_aacgmv2.mlt_convert) - mlt = mlt_vectorised(dtime.year, dtime.month, dtime.day, - dtime.hour, dtime.minute, dtime.second, mlon, - coeff_prefix, igrf_file) - except: - logging.error("Unable to get magnetic lat/lon") + # Get magnetic lat and lon. + mlat, mlon, mr = convert_latlon_arr(glat, glon, height, dtime, code=code, + igrf_file=igrf_file, + coeff_prefix=coeff_prefix) + + if np.all(np.isnan(mlon)): + mlt = np.nan + else: + # Get magnetic local time + mlt = convert_mlt(mlon, dtime, m2a=False, igrf_file=igrf_file, + coeff_prefix=coeff_prefix) return mlat, mlon, mlt @@ -477,3 +468,53 @@ def convert_bool_to_bit(a2g=False, trace=False, allowtrace=False, bit_code += c_aacgmv2.GEOCENTRIC return bit_code + +def convert_mlt(arr, dtime, m2a=False, igrf_file=None, coeff_prefix=None): + """Converts between magnetic local time (MLT) and AACGM-v2 longitude + + Parameters + ------------ + arr : (array_line or float) + Magnetic longitudes or MLTs to convert + dtime : (datetime.datetime) + Date and time for MLT conversion in Universal Time (UT). + m2a : (bool) + Convert MLT to AACGM-v2 longitude (True) or magnetic longitude to MLT + (False). (default=False) + igrf_file : (str or NoneType) + Full filename of IGRF coefficient file or None to use + aacgmv2.IGRF_12_COEFFS. (default=None) + coeff_prefix : (str or NoneType) + Location and file prefix for aacgm coefficient files or None to use + aacgmv2.AACGM_v2_DAT_PREFIX. (default=None) + + Returns + -------- + out : (np.ndarray) + Converted coordinates/MLT + + Notes + ------- + This routine previously based on Laundal et al. 2016, but now uses the + improved calculation available in AACGM-V2.4. + """ + # Define coefficient file prefix if not supplied + if coeff_prefix is None: + coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX + + # Define IGRF file if not supplied + if igrf_file is None: + igrf_file = aacgmv2.IGRF_12_COEFFS + + if m2a: + inv_vectorised = np.vectorize(c_aacgmv2.inv_mlt_convert) + out = inv_vectorised(dtime.year, dtime.month, dtime.day, dtime.hour, + dtime.minute, dtime.second, arr, igrf_file) + else: + # Get magnetic local time + mlt_vectorised = np.vectorize(c_aacgmv2.mlt_convert) + out = mlt_vectorised(dtime.year, dtime.month, dtime.day, dtime.hour, + dtime.minute, dtime.second, arr, coeff_prefix, + igrf_file) + + return out From 0d70c9eb76f503fe787f176798f9565e8df6e7e7 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 16:41:43 -0500 Subject: [PATCH 024/101] Updated README examples Updated examples in README to reflect new routines. --- README.rst | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.rst b/README.rst index 6647db58..4693d065 100644 --- a/README.rst +++ b/README.rst @@ -19,29 +19,29 @@ Install (requires NumPy and logging):: Convert between AACGM and geographic coordinates:: - >>> from aacgmv2 import convert - >>> from datetime import date + >>> import aacgmv2 + >>> import datetime as dt >>> # geo to AACGM, single numbers - >>> mlat, mlon = convert(60, 15, 300, date(2013, 11, 3)) - >>> mlat - array(57.47207691280528) - >>> mlon - array(93.62138045643167) + >>> dtime = dt.datetime(2013, 11, 3) + >>> mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 15, 300, dtime) + >>> "{:.4f} {:.4f} {:.4f}".format(mlat, mlon, mlt) + '58.2247 81.1761 0.1889' >>> # AACGM to geo, mix arrays/numbers - >>> glat, glon = convert([90, -90], 0, 0, date(2013, 11, 3), a2g=True) - >>> glat - array([ 82.96656071, -74.33854592]) - >>> glon - array([ -84.66516034, 125.84014944]) + >>> glat, glon alt = aacgmv2.convert_latlon_arr([90, -90], 0, 0, dtime, code="A2G") + >>> ["{:.4f} {:.4f} {:.4f}".format(lat, glon[i], alt[i]) for i,lat in enumerate(glat)] + ['82.9666 -84.6652 14.1244', '-74.3385 125.8401 12.8771'] Convert between AACGM and MLT:: - >>> from aacgmv2 import convert_mlt - >>> from datetime import datetime + >>> import aacgmv2 + >>> import datetime as dt >>> # MLT to AACGM - >>> mlon = convert_mlt([0, 12], datetime(2013, 11, 3, 18, 0), m2a=True) - >>> mlon - array([ 159.08967974, 339.08967974]) + >>> dtime = dt.datetime(2013, 11, 3, 0, 0, 0) + >>> mlon_check = aacgmv2.convert_mlt([mlt, 12], dtime, m2a=True) + >>> abs(mlon_check[0] - mlon) < 1.0e-4 + True + >>> ["{:.4f}".format(lon) for lon in mlon_check] + ['81.1761', '-101.6577'] If you don't know or use Python, you can also use the command line. See details in the full documentation. From 6434825ad2f0b63565955e9d4960ab7278774d8a Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 16:41:58 -0500 Subject: [PATCH 025/101] tox fix Possible fix for tox file --- tox.ini | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/tox.ini b/tox.ini index 8fbb640a..22be7b3e 100644 --- a/tox.ini +++ b/tox.ini @@ -10,25 +10,34 @@ envlist = setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes - cover: WITH_COVERAGE=yes - cover: PY_CCOV=-coverage passenv = * -usedevelop = - cover: true deps = pytest pytest-capturelog testfixtures +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} + +[testenv:np{1.8,1.9,1.10,1.14}-{cover,nocover}] +deps = + {[testenv]deps} + cover: pytest-cov np1.8: numpy>=1.8,<1.9 np1.9: numpy>=1.9,<1.10 np1.10: numpy>=1.10,<1.11 np1.14: numpy>=1.14,<1.15 - cover: pytest-cov -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} - cover: {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} + +cover: + setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage + usedevelop = true + commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} [testenv:codecov] deps = From e35fa3ba2684d43f16c23862b14432fb7e2ae557 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 17:06:48 -0500 Subject: [PATCH 026/101] broke up cover and nocover broke up cover and nocover in hopes that things will work. --- tox.ini | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/tox.ini b/tox.ini index 22be7b3e..63430be7 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,8 @@ envlist = clean, check, - np{1.8,1.9,1.10,1.14}-{cover,nocover}, + np{1.8,1.9,1.10,1.14}, + np{1.8,1.9,1.10,1.14}-nocover, report, docs @@ -20,24 +21,30 @@ commands = python setup.py clean --all build_ext --force --inplace {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} -[testenv:np{1.8,1.9,1.10,1.14}-{cover,nocover}] +[testenv:np{1.8,1.9,1.10,1.14}] +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true deps = {[testenv]deps} - cover: pytest-cov + pytest-cov np1.8: numpy>=1.8,<1.9 np1.9: numpy>=1.9,<1.10 np1.10: numpy>=1.10,<1.11 np1.14: numpy>=1.14,<1.15 +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -cover: - setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage - usedevelop = true - commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +[testenv:np{1.8,1.9,1.10,1.14}-nocover] +deps = + {[testenv]deps} + np1.8: numpy>=1.8,<1.9 + np1.9: numpy>=1.9,<1.10 + np1.10: numpy>=1.10,<1.11 + np1.14: numpy>=1.14,<1.15 [testenv:codecov] deps = From 42273870381042c7c0fabd672165261606627d29 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 22:43:27 -0500 Subject: [PATCH 027/101] more tox changes Like it says --- .travis.yml | 4 ++-- tox.ini | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index d6188aba..54ae9fba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,7 +41,7 @@ notifications: matrix: allow_failures: - env: "TOXENV=check" - - env: "TOXENV=np114-nocover" + - env: "TOXENV=np1.14-nocover" python: 3.3 - - env: "TOXENV=np114-cover" + - env: "TOXENV=np1.14,extension-coveralls,coveralls" python: 3.3 diff --git a/tox.ini b/tox.ini index 63430be7..b19e39a3 100644 --- a/tox.ini +++ b/tox.ini @@ -2,8 +2,10 @@ envlist = clean, check, - np{1.8,1.9,1.10,1.14}, - np{1.8,1.9,1.10,1.14}-nocover, + py{2.7,3.4,3.5,3.6}-np{1.8,1.9,1.10,1.14}, + py{2.7,3.4,3.5,3.6}-np{1.8,1.9,1.10,1.14}-nocover, + py3.3-np{1.8,1.9,1.10}, + py3.3-np{1.8,1.9,1.10}-nocover, report, docs @@ -21,7 +23,13 @@ commands = python setup.py clean --all build_ext --force --inplace {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} -[testenv:np{1.8,1.9,1.10,1.14}] +[testenv:py{2.7,3.3,3.4,3.5,3.6}-np{1.8,1.9,1.10,1.14}] +basepython = + py2.7: {env:TOXPYTHON:python2.7} + py3.3: {env:TOXPYTHON:python2.7} + py3.4: {env:TOXPYTHON:python2.7} + py3.5: {env:TOXPYTHON:python2.7} + py3.6: {env:TOXPYTHON:python2.7} setenv = {[testenv]setenv} WITH_COVERAGE=yes @@ -38,7 +46,13 @@ commands = python setup.py clean --all build_ext --force --inplace {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -[testenv:np{1.8,1.9,1.10,1.14}-nocover] +[testenv:py{2.7,3.3,3.4,3.5,3.6}-np{1.8,1.9,1.10,1.14}-nocover] +basepython = + py2.7: {env:TOXPYTHON:python2.7} + py3.3: {env:TOXPYTHON:python2.7} + py3.4: {env:TOXPYTHON:python2.7} + py3.5: {env:TOXPYTHON:python2.7} + py3.6: {env:TOXPYTHON:python2.7} deps = {[testenv]deps} np1.8: numpy>=1.8,<1.9 @@ -88,6 +102,7 @@ passenv = * [testenv:check] +basepython = python3.4 deps = docutils check-manifest @@ -121,6 +136,7 @@ commands = coveralls --build-root=. --include=src --dump=extension-coveralls.json [] [testenv:report] +basepython = python3.4 deps = coverage skip_install = true usedevelop = false From 921e7519eec526572d7468672ab6cb881fe90186 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 13 Mar 2018 22:56:13 -0500 Subject: [PATCH 028/101] more consistent matrix assignment --- .travis.yml | 18 ++++++------------ tox.ini | 26 ++++++-------------------- 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/.travis.yml b/.travis.yml index 54ae9fba..72e2c615 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,14 +11,8 @@ env: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check - - TOXENV=np1.14,extension-coveralls,coveralls - - TOXENV=np1.14-nocover - - TOXENV=np1.10,extension-coveralls,coveralls - - TOXENV=np1.10-nocover - - TOXENV=np1.8,extension-coveralls,coveralls - - TOXENV=np1.8-nocover - - TOXENV=np1.9,extension-coveralls,coveralls - - TOXENV=np1.9-nocover + - TOXENV=np{1.8,1.9,1.10,1.14},extension-coveralls,coveralls + - TOXENV=np{1.8,1.9,1.10,1.14}-nocover before_install: - python --version - uname -a @@ -41,7 +35,7 @@ notifications: matrix: allow_failures: - env: "TOXENV=check" - - env: "TOXENV=np1.14-nocover" - python: 3.3 - - env: "TOXENV=np1.14,extension-coveralls,coveralls" - python: 3.3 + - python: 3.3 + env: "TOXENV=np1.14-nocover" + - python: 3.3 + env: "TOXENV=np1.14,extension-coveralls,coveralls" \ No newline at end of file diff --git a/tox.ini b/tox.ini index b19e39a3..0dc710be 100644 --- a/tox.ini +++ b/tox.ini @@ -2,10 +2,8 @@ envlist = clean, check, - py{2.7,3.4,3.5,3.6}-np{1.8,1.9,1.10,1.14}, - py{2.7,3.4,3.5,3.6}-np{1.8,1.9,1.10,1.14}-nocover, - py3.3-np{1.8,1.9,1.10}, - py3.3-np{1.8,1.9,1.10}-nocover, + np{1.8,1.9,1.10,1.14}, + np{1.8,1.9,1.10,1.14}-nocover, report, docs @@ -23,13 +21,7 @@ commands = python setup.py clean --all build_ext --force --inplace {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} -[testenv:py{2.7,3.3,3.4,3.5,3.6}-np{1.8,1.9,1.10,1.14}] -basepython = - py2.7: {env:TOXPYTHON:python2.7} - py3.3: {env:TOXPYTHON:python2.7} - py3.4: {env:TOXPYTHON:python2.7} - py3.5: {env:TOXPYTHON:python2.7} - py3.6: {env:TOXPYTHON:python2.7} +[testenv:np{1.8,1.9,1.10,1.14}] setenv = {[testenv]setenv} WITH_COVERAGE=yes @@ -46,13 +38,7 @@ commands = python setup.py clean --all build_ext --force --inplace {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -[testenv:py{2.7,3.3,3.4,3.5,3.6}-np{1.8,1.9,1.10,1.14}-nocover] -basepython = - py2.7: {env:TOXPYTHON:python2.7} - py3.3: {env:TOXPYTHON:python2.7} - py3.4: {env:TOXPYTHON:python2.7} - py3.5: {env:TOXPYTHON:python2.7} - py3.6: {env:TOXPYTHON:python2.7} +[testenv:np{1.8,1.9,1.10,1.14}-nocover] deps = {[testenv]deps} np1.8: numpy>=1.8,<1.9 @@ -102,7 +88,7 @@ passenv = * [testenv:check] -basepython = python3.4 +#basepython = python3.4 deps = docutils check-manifest @@ -136,7 +122,7 @@ commands = coveralls --build-root=. --include=src --dump=extension-coveralls.json [] [testenv:report] -basepython = python3.4 +#basepython = python3.4 deps = coverage skip_install = true usedevelop = false From 438bfa988c5a5c908f2828599beff8a350c594db Mon Sep 17 00:00:00 2001 From: aburrell Date: Wed, 14 Mar 2018 16:44:26 -0500 Subject: [PATCH 029/101] Main bugs Fixed bugs in command line routine, including misnaming the mlt flag and not accounting for undeprecated convert_mlt routine. --- aacgmv2/__main__.py | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/aacgmv2/__main__.py b/aacgmv2/__main__.py index a83c2c15..7663a1c3 100644 --- a/aacgmv2/__main__.py +++ b/aacgmv2/__main__.py @@ -8,9 +8,7 @@ import sys import argparse import datetime as dt - import numpy as np - import aacgmv2 try: @@ -40,7 +38,7 @@ def main(): desc = 'convert between magnetic local time (MLT) and AACGM-v2 longitude. ' desc += 'Input file must have a single number on each line.' - parser_convert_mlt = subparsers.add_parser('mlt_convert', help=(desc)) + parser_convert_mlt = subparsers.add_parser('convert_mlt', help=(desc)) desc = 'input file (stdin if none specified)' for p in [parser_convert, parser_convert_mlt]: @@ -93,25 +91,13 @@ def main(): allowtrace=args.allowtrace, badidea=args.badidea, geocentric=args.geocentric) - lats, lons = aacgmv2.convert_latlon_arr(array[:,0], array[:,1], - array[:,2], dtime=date, - code=code) - np.savetxt(args.file_out, np.column_stack((lats, lons)), fmt='%.8f') - elif args.subcommand == 'mlt_convert': + lats, lons, rs = aacgmv2.convert_latlon_arr(array[:,0], array[:,1], + array[:,2], dtime=date, + code=code) + np.savetxt(args.file_out, np.column_stack((lats, lons, rs)), fmt='%.8f') + elif args.subcommand == 'convert_mlt': dtime = dt.datetime.strptime(args.datetime, '%Y%m%d%H%M%S') - if args.m2a: - out = aacgmv2._aacgmv2.inv_mlt_convert(dtime.year, dtime.month, - dtime.day, dtime.hour, - dtime.minute, dtime.second, - array[:,1], - aacgmv2.IGRF_12_COEFFS) - else: - out = aacgmv2._aacgmv2.mlt_convert(dtime.year, dtime.month, - dtime.day, dtime.hour, - dtime.minute, dtime.second, - array[:,1], - aacgmv2.AACGM_v2_DAT_PREFIX, - aacgmv2.IGRF_12_COEFFS) + out = aacgmv2.convert_mlt(array[:,0], dtime, m2a=args.m2a) np.savetxt(args.file_out, out, fmt='%.8f') From 0d72cab768aabd6597bc91f8248f5d198c248958 Mon Sep 17 00:00:00 2001 From: aburrell Date: Wed, 14 Mar 2018 16:44:51 -0500 Subject: [PATCH 030/101] convert_mlt Fixed bug in convert_mlt, where AACGM date was not initialised. --- aacgmv2/wrapper.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index d2519501..7056f9cb 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -506,7 +506,12 @@ def convert_mlt(arr, dtime, m2a=False, igrf_file=None, coeff_prefix=None): if igrf_file is None: igrf_file = aacgmv2.IGRF_12_COEFFS + # Set current date and time + c_aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, dtime.hour, + dtime.minute, dtime.second, coeff_prefix) + if m2a: + # Get the magnetic longitude inv_vectorised = np.vectorize(c_aacgmv2.inv_mlt_convert) out = inv_vectorised(dtime.year, dtime.month, dtime.day, dtime.hour, dtime.minute, dtime.second, arr, igrf_file) From 82114bba703ad587d2408fd4c125178fdaa53b4c Mon Sep 17 00:00:00 2001 From: aburrell Date: Wed, 14 Mar 2018 16:45:26 -0500 Subject: [PATCH 031/101] Usage Example in usage changed to reflect current output (changes in MLT routine, outputing R as well as lat and lon) --- docs/usage.rst | 69 ++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index 28b78f7d..e43c4bb1 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -7,21 +7,17 @@ Python library For full documentation of the functions, see :doc:`Reference → aacgmv2 `. - >>> from aacgmv2 import convert - >>> from datetime import date - >>> # geo to AACGM, single numbers - >>> mlat, mlon = convert(60, 15, 300, date(2013, 11, 3)) - >>> mlat - array(57.47207691280528) - >>> mlon - array(93.62138045643167) - >>> # AACGM to geo, mix arrays/numbers - >>> glat, glon = convert([90, -90], 0, 0, date(2013, 11, 3), a2g=True) - >>> glat - array([ 82.96656071, -74.33854592]) - >>> glon - array([ -84.66516034, 125.84014944]) - + >>> import aacgmv2 + >>> import datetime as dt + >>> # geo to AACGM, single numbers + >>> dtime = dt.datetime(2013, 11, 3) + >>> mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 15, 300, dtime) + >>> "{:.4f} {:.4f} {:.4f}".format(mlat, mlon, mlt) + '58.2247 81.1761 0.1889' + >>> # AACGM to geo, mix arrays/numbers + >>> glat, glon alt = aacgmv2.convert_latlon_arr([90, -90], 0, 0, dtime, code="A2G") + >>> ["{:.4f} {:.4f} {:.4f}".format(lat, glon[i], alt[i]) for i,lat in enumerate(glat)] + ['82.9666 -84.6652 14.1244', '-74.3385 125.8401 12.8771'] @@ -30,13 +26,19 @@ Command-line interface .. highlight:: none -The Python package also installs a command called ``aacgmv2`` with two sub-commands, ``aacgmv2 convert`` and ``aacgmv2 convert_mlt``. The command-line interface allows you to make use of the Python library even if you don't know or use Python. See :doc:`Reference → Command-line interface ` for a list of arguments to the commands. Below are some simple usage examples. +The Python package also installs a command called ``aacgmv2`` with several +sub-commands that allow conversion between geographic/geodetic and AACGM-v2 +magnetic coordinates (mlat, mlon, and mlt). The command-line interface allows +you to make use of the Python library even if you don't know or use Python. See +:doc:`Reference → Command-line interface ` for a list of +arguments to the commands. Below are some simple usage examples. Convert geographical/magnetic coordinates ----------------------------------------- -Produce a file called e.g. ``input.txt`` with the input latitudes, longitudes and altitudes on each row separated by whitespace:: +Produce a file called e.g. ``input.txt`` with the input latitudes, longitudes +and altitudes on each row separated by whitespace:: # lat lon alt # comment lines like these are ignored @@ -44,34 +46,41 @@ Produce a file called e.g. ``input.txt`` with the input latitudes, longitudes an 61 15 300 62 15 300 -To convert this to AACGM-v2 for the date 2015-02-24, run the command ``aacgmv2 convert -i input.txt -o output.txt -d 20150224``. The output file will look like this:: +To convert this to AACGM-v2 for the date 2015-02-24, run the command +``python -m aacgmv2 convert -i input.txt -o output.txt -d 20150224``. The +output file will look like this:: - 57.47612194 93.55719875 - 58.53323704 93.96069212 - 59.58522105 94.38968625 + 57.47612194 93.55719875 1.04566346 + 58.53323704 93.96069212 1.04561304 + 59.58522105 94.38968625 1.04556369 Alternatively, you can skip the files and just use command-line piping:: - $ echo 60 15 300 | aacgmv2 convert -d 20150224 - 57.47612194 93.55719875 + $ echo 60 15 300 | python -m aacgmv2 convert -d 20150224 + 57.47612194 93.55719875 1.04566346 Convert MLT ----------- -This works in much the same way as ``convert``. The file should only contain a single column of numbers (MLTs or magnetic longitudes, depending on which way you're converting):: +This works in much the same way as ``convert``. The file should only contain a +single column of numbers (MLTs or magnetic longitudes, depending on which way +you're converting):: 1 12 23 -To convert these MLTs to magnetic longitudes at 2015-02-24 14:00:15, run e.g. ``aacgmv2 convert_mlt 20150224140015 -i input.txt -o output.txt -v`` (note that the date/time is a required parameter). The output file will then look like this:: +To convert these MLTs to magnetic longitudes at 2015-02-24 14:00:15, run e.g. +``aacgmv2 convert_mlt 20150224140015 -i input.txt -o output.txt -v`` (note that +the date/time is a required parameter). The output file will then look like +this:: - 240.13651777 - 45.13651777 - 210.13651777 + -120.34354125 + 44.65645875 + -150.34354125 Like with ``convert``, you can use stdin/stdout instead of input/output files:: - $ echo 12 | aacgmv2 convert_mlt 20150224140015 -v - 45.13651777 + $ echo 12 | python -m aacgmv2 convert_mlt 20150224140015 -v + 44.65645875 From aeb5e7292b59531a97ea128892bbfcdae82d28df Mon Sep 17 00:00:00 2001 From: aburrell Date: Wed, 14 Mar 2018 18:58:05 -0500 Subject: [PATCH 032/101] mlt date initialisation Adopted the inverse MLT calculation to follow the same behaviour as the MLT calculation --- c_aacgmv2/include/mlt_v2.h | 9 +++++---- c_aacgmv2/src/mlt_v2.c | 40 ++++++++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/c_aacgmv2/include/mlt_v2.h b/c_aacgmv2/include/mlt_v2.h index 80ea7ed9..c0c86290 100644 --- a/c_aacgmv2/include/mlt_v2.h +++ b/c_aacgmv2/include/mlt_v2.h @@ -5,18 +5,19 @@ double MLTConvert_v2(int yr, int mo, int dy, int hr, int mt ,int sc, double mlon, char *root, char *igrf_filename); double inv_MLTConvert_v2(int yr, int mo, int dy, int hr, int mt ,int sc, - double mlt, char *igrf_filename); + double mlt, char *root, char *igrf_filename); double MLTConvertYMDHMS_v2(int yr,int mo,int dy,int hr,int mt,int sc, double mlon, char *root, char *igrf_filename); double inv_MLTConvertYMDHMS_v2(int yr,int mo,int dy,int hr,int mt,int sc, - double mlt, char *igrf_filename); + double mlt, char *root, char *igrf_filename); double MLTConvertYrsec_v2(int yr,int yrsec, double mlon, char *root, char *igrf_filename); -double inv_MLTConvertYrsec_v2(int yr, int yrsec, double mlt, +double inv_MLTConvertYrsec_v2(int yr, int yrsec, double mlt, char *root, char *igrf_filename); double MLTConvertEpoch_v2(double epoch, double mlon, char *root, char *igrf_filename); -double inv_MLTConvertEpoch_v2(double epoch, double mlt, char *igrf_filename); +double inv_MLTConvertEpoch_v2(double epoch, double mlt, char *root, + char *igrf_filename); #endif diff --git a/c_aacgmv2/src/mlt_v2.c b/c_aacgmv2/src/mlt_v2.c index d7ee7abd..9def0c79 100644 --- a/c_aacgmv2/src/mlt_v2.c +++ b/c_aacgmv2/src/mlt_v2.c @@ -36,6 +36,11 @@ ; case the AACGM-v2 coefficients are loaded and interpolated ; which could impact other calls to AACGM_v2_Convert() if ; the date/time is not reset. +; 20180314 AGB v1.2a Changed inv_MLTConvert_v2 to call AACGM_v2_SetDateTime() if +; the AACGM-v2 date/time is not currently set OR if the +; date/time passed into one of the public functions differs +; from the AACGM-v2 date/time by more than 30 days. This way +; it has the same behaviour as MLTConvert_v2 ; Changes @@ -173,13 +178,31 @@ double MLTConvert_v2(int yr, int mo, int dy, int hr, int mt ,int sc, /* inverse function: MLT to AACGM-v2 magnetic longitude */ double inv_MLTConvert_v2(int yr, int mo, int dy, int hr, int mt ,int sc, - double mlt, char *igrf_filename) + double mlt, char *root, char *igrf_filename) { int err; - double dd,jd,eqt,dec,ut,at; + int ayr,amo,ady,ahr,amt,asc,adyn; + double dd,jd,eqt,dec,ut,at,ajd; double slon,mlat,r; double hgt,aacgm_mlon; + err = 0; + AACGM_v2_GetDateTime(&ayr, &amo, &ady, &ahr, &amt, &asc, &adyn); + if (ayr < 0) { + /* AACGM date/time not set so set it to the date/time passed in */ + err = AACGM_v2_SetDateTime(yr,mo,dy,hr,mt,sc,root); + if (err != 0) return (err); + } else { + /* If date/time passed into function differs from AACGM data/time by more + * than 30 days, recompute the AACGM-v2 coefficients */ + ajd = TimeYMDHMSToJulian(ayr,amo,ady,ahr,amt,asc); + jd = TimeYMDHMSToJulian(yr,mo,dy,hr,mt,sc); + if (fabs(jd-ajd) > 30.0) { + err = AACGM_v2_SetDateTime(yr,mo,dy,hr,mt,sc, root); + } + if (err != 0) return (err); + } + /* check for bad input, which should not happen for MLT, and return NAN */ if (!isfinite(mlt)) { return (NAN); @@ -235,9 +258,9 @@ double MLTConvertYMDHMS_v2(int yr, int mo, int dy, int hr, int mt, int sc, } double inv_MLTConvertYMDHMS_v2(int yr, int mo, int dy, int hr, int mt, int sc, - double mlt, char *igrf_filename) + double mlt, char *root, char *igrf_filename) { - return (inv_MLTConvert_v2(yr,mo,dy,hr,mt,sc,mlt,igrf_filename)); + return (inv_MLTConvert_v2(yr,mo,dy,hr,mt,sc,mlt,root,igrf_filename)); } double MLTConvertYrsec_v2(int yr,int yr_sec, double mlon, char *root, @@ -250,14 +273,14 @@ double MLTConvertYrsec_v2(int yr,int yr_sec, double mlon, char *root, return (MLTConvert_v2(yr,mo,dy,hr,mt,sc,mlon,root,igrf_filename)); } -double inv_MLTConvertYrsec_v2(int yr, int yr_sec, double mlt, +double inv_MLTConvertYrsec_v2(int yr, int yr_sec, double mlt, char *root, char *igrf_filename) { int mo,dy,hr,mt,sc; TimeYrsecToYMDHMS(yr_sec,yr,&mo,&dy,&hr,&mt,&sc); - return (inv_MLTConvert_v2(yr,mo,dy,hr,mt,sc,mlt,igrf_filename)); + return (inv_MLTConvert_v2(yr,mo,dy,hr,mt,sc,mlt,root,igrf_filename)); } double MLTConvertEpoch_v2(double epoch, double mlon, char *root, @@ -271,13 +294,14 @@ double MLTConvertEpoch_v2(double epoch, double mlon, char *root, return (MLTConvert_v2(yr,mo,dy,hr,mt,(int)sc, mlon, root, igrf_filename)); } -double inv_MLTConvertEpoch_v2(double epoch, double mlt, char *igrf_filename) +double inv_MLTConvertEpoch_v2(double epoch, double mlt, char *root, + char *igrf_filename) { int yr,mo,dy,hr,mt; double sc; TimeEpochToYMDHMS(epoch,&yr,&mo,&dy,&hr,&mt,&sc); - return (inv_MLTConvert_v2(yr,mo,dy,hr,mt,(int)sc, mlt, igrf_filename)); + return (inv_MLTConvert_v2(yr,mo,dy,hr,mt,(int)sc, mlt, root, igrf_filename)); } From bd22fb5e7c6bb96b4094b3614b6e741ebd0fa394 Mon Sep 17 00:00:00 2001 From: aburrell Date: Wed, 14 Mar 2018 18:58:38 -0500 Subject: [PATCH 033/101] Debugging Debugging to resolve errors uncovered with tox testing. --- README.rst | 10 +- aacgmv2/aacgmv2module.c | 32 ++++--- aacgmv2/depricated.py | 8 ++ aacgmv2/tests/test_c_aacgmv2.py | 44 +++++---- aacgmv2/tests/test_dep_aacgmv2.py | 31 +++--- aacgmv2/tests/test_py_aacgmv2.py | 154 +++++++++++++----------------- aacgmv2/wrapper.py | 27 +++--- 7 files changed, 155 insertions(+), 151 deletions(-) diff --git a/README.rst b/README.rst index 4693d065..56ed643a 100644 --- a/README.rst +++ b/README.rst @@ -25,9 +25,9 @@ Convert between AACGM and geographic coordinates:: >>> dtime = dt.datetime(2013, 11, 3) >>> mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 15, 300, dtime) >>> "{:.4f} {:.4f} {:.4f}".format(mlat, mlon, mlt) - '58.2247 81.1761 0.1889' + '57.4698 93.6300 1.4822' >>> # AACGM to geo, mix arrays/numbers - >>> glat, glon alt = aacgmv2.convert_latlon_arr([90, -90], 0, 0, dtime, code="A2G") + >>> glat, glon, alt = aacgmv2.convert_latlon_arr([90, -90], 0, 0, dtime, code="A2G") >>> ["{:.4f} {:.4f} {:.4f}".format(lat, glon[i], alt[i]) for i,lat in enumerate(glat)] ['82.9666 -84.6652 14.1244', '-74.3385 125.8401 12.8771'] @@ -37,11 +37,9 @@ Convert between AACGM and MLT:: >>> import datetime as dt >>> # MLT to AACGM >>> dtime = dt.datetime(2013, 11, 3, 0, 0, 0) - >>> mlon_check = aacgmv2.convert_mlt([mlt, 12], dtime, m2a=True) - >>> abs(mlon_check[0] - mlon) < 1.0e-4 - True + >>> mlon_check = aacgmv2.convert_mlt([1.4822189, 12], dtime, m2a=True) >>> ["{:.4f}".format(lon) for lon in mlon_check] - ['81.1761', '-101.6577'] + ['93.6300', '-108.6033'] If you don't know or use Python, you can also use the command line. See details in the full documentation. diff --git a/aacgmv2/aacgmv2module.c b/aacgmv2/aacgmv2module.c index 6875bd69..7a68c901 100644 --- a/aacgmv2/aacgmv2module.c +++ b/aacgmv2/aacgmv2module.c @@ -85,7 +85,7 @@ static PyObject *mltconvert_v2(PyObject *self, PyObject *args) return(NULL); /* Call the AACGM routine */ - mlt = MLTConvert_v2(yr, mo, dy, hr, mt, sc, mlon, root, igrf_file); + mlt = MLTConvertYMDHMS_v2(yr, mo, dy, hr, mt, sc, mlon, root, igrf_file); return Py_BuildValue("d", mlt); } @@ -114,15 +114,15 @@ static PyObject *inv_mltconvert_v2(PyObject *self, PyObject *args) double mlon, mlt; - char *igrf_file; + char *root, *igrf_file; /* Parse the input as a tupple */ - if(!PyArg_ParseTuple(args, "iiiiiids", &yr, &mo, &dy, &hr, &mt, &sc, &mlt, - &igrf_file)) + if(!PyArg_ParseTuple(args, "iiiiiidss", &yr, &mo, &dy, &hr, &mt, &sc, &mlt, + &root, &igrf_file)) return(NULL); /* Call the AACGM routine */ - mlon = inv_MLTConvert_v2(yr, mo, dy, hr, mt, sc, mlt, igrf_file); + mlon = inv_MLTConvertYMDHMS_v2(yr, mo, dy, hr, mt, sc, mlt, root, igrf_file); return Py_BuildValue("d", mlon); } @@ -133,14 +133,14 @@ static PyObject *inv_mltconvert_yrsec_v2(PyObject *self, PyObject *args) double mlon, mlt; - char *igrf_file; + char *root, *igrf_file; /* Parse the input as a tupple */ - if(!PyArg_ParseTuple(args, "iidss", &yr, &yr_sec, &mlt, &igrf_file)) + if(!PyArg_ParseTuple(args, "iidss", &yr, &yr_sec, &mlt, &root, &igrf_file)) return(NULL); /* Call the AACGM routine */ - mlon = inv_MLTConvertYrsec_v2(yr, yr_sec, mlt, igrf_file); + mlon = inv_MLTConvertYrsec_v2(yr, yr_sec, mlt, root, igrf_file); return Py_BuildValue("d", mlon); } @@ -167,7 +167,7 @@ second : (int)\n\ Seconds of minute (0-59)\n\ root : (str)\n\ AACGM coefficient filename root\n\ - (e.g. /davitpydir/tables/aacgm/aacgm_coeffs-12-)\n\ + (e.g. ~/aacgmv2/aacgmv2/aacgm_coeff/aacgm_coeffs-12-)\n\ \n\ Returns\n\ -------------\n\ @@ -227,7 +227,7 @@ mlon : (float)\n\ Magnetic longitude\n\ root : (str)\n\ Root of the AACGM coefficient files\n\ - (e.g. /davitpydir/tables/aacgm/aacgm_coeffs-12-)\n\ + (e.g. ~/aacgmv2/aacgmv2/aacgm_coeff/aacgm_coeffs-12-)\n\ igrf_file : (str)\n\ Full filename of IGRF coefficient file\n\ \n\ @@ -251,7 +251,7 @@ mlon : (float)\n\ Magnetic longitude\n\ root : (str)\n\ Root of the AACGM coefficient files\n\ - (e.g. /davitpydir/tables/aacgm/aacgm_coeffs-12-)\n\ + (e.g. ~/aacgmv2/aacgmv2/aacgm_coeff/aacgm_coeffs-12-)\n\ igrf_file : (str)\n\ Full filename of IGRF coefficient file\n\ \n\ @@ -260,7 +260,7 @@ Returns \n\ mlt : (float)\n\ Magnetic local time (hours)\n" }, {"inv_mlt_convert", inv_mltconvert_v2, METH_VARARGS, - "inv_mlt_convert(yr, mo, dy, hr, mt, sc, mlt, igrf_file)\n\ + "inv_mlt_convert(yr, mo, dy, hr, mt, sc, mlt, root, igrf_file)\n\ \n\ Converts from universal time and magnetic local time to magnetic longitude.\n\ \n\ @@ -280,6 +280,9 @@ sc : (int)\n\ Seconds of minute (0-59)\n\ mlt : (float)\n\ Magnetic local time\n\ +root : (str)\n\ + Root of the AACGM coefficient files\n\ + (e.g. ~/aacgmv2/aacgmv2/aacgm_coeff/aacgm_coeffs-12-)\n\ igrf_file : (str)\n\ Full filename of IGRF coefficient file\n\ \n\ @@ -289,7 +292,7 @@ mlon : (float)\n\ Magnetic longitude (degrees)\n" }, {"inv_mlt_convert_yrsec", inv_mltconvert_yrsec_v2, METH_VARARGS, - "inv_mlt_convert_yrsec(yr, yr_sec, mlt, igrf_file)\n\ + "inv_mlt_convert_yrsec(yr, yr_sec, mlt, root, igrf_file)\n\ \n\ Converts from universal time and magnetic local time to magnetic longitude.\n\ \n\ @@ -301,6 +304,9 @@ yr_sec : (int)\n\ Seconds of year (0-31622400)\n\ mlt : (float)\n\ Magnetic local time\n\ +root : (str)\n\ + Root of the AACGM coefficient files\n\ + (e.g. ~/aacgmv2/aacgmv2/aacgm_coeff/aacgm_coeffs-12-)\n\ igrf_file : (str)\n\ Full filename of IGRF coefficient file\n\ \n\ diff --git a/aacgmv2/depricated.py b/aacgmv2/depricated.py index a8cf4e5e..6bbd37c0 100644 --- a/aacgmv2/depricated.py +++ b/aacgmv2/depricated.py @@ -56,6 +56,14 @@ def convert(lat, lon, alt, date=None, a2g=False, trace=False, allowtrace=False, lon_out : (float) Output longitude in degrees E """ + if(np.array(height).max() > 2000 and not trace and not allowtrace and + badidea): + estr = 'coefficients are not valid for altitudes above 2000 km. You' + estr += ' must either use field-line tracing (trace=True ' + estr += 'or allowtrace=True) or indicate you know this is a bad idea' + logging.error(estr) + raise ValueError + # construct a code from the boolian flags bit_code = aacgmv2.convert_bool_to_bit(a2g=a2g, trace=trace, allowtrace=allowtrace, diff --git a/aacgmv2/tests/test_c_aacgmv2.py b/aacgmv2/tests/test_c_aacgmv2.py index fea5483e..53bac62c 100644 --- a/aacgmv2/tests/test_c_aacgmv2.py +++ b/aacgmv2/tests/test_c_aacgmv2.py @@ -214,17 +214,18 @@ def test_forbidden(self): def test_inv_mlt_convert(self): """Test MLT inversion""" mlt_args = list(self.long_date) - mlt_args.extend([12.0, aacgmv2.IGRF_12_COEFFS]) + mlt_args.extend([12.0, aacgmv2.AACGM_v2_DAT_PREFIX, + aacgmv2.IGRF_12_COEFFS]) mlon = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args) - np.testing.assert_almost_equal(mlon, -153.5339, decimal=4) + np.testing.assert_almost_equal(mlon, -153.5931, decimal=4) - mlt_args[-2] = 25.0 + mlt_args[-3] = 25.0 mlon = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args) - np.testing.assert_almost_equal(mlon, 41.4661, decimal=4) + np.testing.assert_almost_equal(mlon, 41.4069, decimal=4) - mlt_args[-2] = -1.0 + mlt_args[-3] = -1.0 mlon = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args) - np.testing.assert_almost_equal(mlon, 11.4661, decimal=4) + np.testing.assert_almost_equal(mlon, 11.4069, decimal=4) def test_inv_mlt_convert_yrsec(self): """Test MLT inversion with year and seconds of year""" @@ -233,17 +234,20 @@ def test_inv_mlt_convert_yrsec(self): soy = (int(dtime.strftime("%j"))-1) * 86400 + dtime.hour * 3600 + \ dtime.minute * 60 + dtime.second - mlt_args_1 = [dtime.year, soy, 12.0, aacgmv2.IGRF_12_COEFFS] - mlt_args_2 = [dtime.year, soy, 25.0, aacgmv2.IGRF_12_COEFFS] - mlt_args_3 = [dtime.year, soy, -1.0, aacgmv2.IGRF_12_COEFFS] + mlt_args_1 = [dtime.year, soy, 12.0, aacgmv2.AACGM_v2_DAT_PREFIX, + aacgmv2.IGRF_12_COEFFS] + mlt_args_2 = [dtime.year, soy, 25.0, aacgmv2.AACGM_v2_DAT_PREFIX, + aacgmv2.IGRF_12_COEFFS] + mlt_args_3 = [dtime.year, soy, -1.0, aacgmv2.AACGM_v2_DAT_PREFIX, + aacgmv2.IGRF_12_COEFFS] mlon_1 = aacgmv2._aacgmv2.inv_mlt_convert_yrsec(*mlt_args_1) mlon_2 = aacgmv2._aacgmv2.inv_mlt_convert_yrsec(*mlt_args_2) mlon_3 = aacgmv2._aacgmv2.inv_mlt_convert_yrsec(*mlt_args_3) - np.testing.assert_almost_equal(mlon_1, -153.5339, decimal=4) - np.testing.assert_almost_equal(mlon_2, 41.4661, decimal=4) - np.testing.assert_almost_equal(mlon_2, 11.4661, decimal=4) + np.testing.assert_almost_equal(mlon_1, -153.5931, decimal=4) + np.testing.assert_almost_equal(mlon_2, 41.4069, decimal=4) + np.testing.assert_almost_equal(mlon_3, 11.4069, decimal=4) def test_mlt_convert(self): """Test MLT calculation""" @@ -251,15 +255,15 @@ def test_mlt_convert(self): mlt_args.extend([270.0, aacgmv2.AACGM_v2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS]) mlt = aacgmv2._aacgmv2.mlt_convert(*mlt_args) - np.testing.assert_almost_equal(mlt, 16.2356, decimal=4) + np.testing.assert_almost_equal(mlt, 16.2395, decimal=4) mlt_args[-3] = 80.0 mlt = aacgmv2._aacgmv2.mlt_convert(*mlt_args) - np.testing.assert_almost_equal(mlt, 3.5689, decimal=4) + np.testing.assert_almost_equal(mlt, 3.5729, decimal=4) mlt_args[-3] = -90.0 mlt = aacgmv2._aacgmv2.mlt_convert(*mlt_args) - np.testing.assert_almost_equal(mlt, 16.2356, decimal=4) + np.testing.assert_almost_equal(mlt, 16.2395, decimal=4) def test_mlt_convert_yrsec(self): """Test MLT calculation using year and seconds of year""" @@ -274,10 +278,10 @@ def test_mlt_convert_yrsec(self): mlt_args_3 = [dtime.year, soy, -90.0, aacgmv2.AACGM_v2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS] - mlt_1 = aacgmv2._aacgmv2.mlt_convert(*mlt_args_1) - mlt_2 = aacgmv2._aacgmv2.mlt_convert(*mlt_args_2) - mlt_3 = aacgmv2._aacgmv2.mlt_convert(*mlt_args_3) + mlt_1 = aacgmv2._aacgmv2.mlt_convert_yrsec(*mlt_args_1) + mlt_2 = aacgmv2._aacgmv2.mlt_convert_yrsec(*mlt_args_2) + mlt_3 = aacgmv2._aacgmv2.mlt_convert_yrsec(*mlt_args_3) - np.testing.assert_almost_equal(mlt_1, 16.2356, decimal=4) - np.testing.assert_almost_equal(mlt_2, 3.5689, decimal=4) + np.testing.assert_almost_equal(mlt_1, 16.2395, decimal=4) + np.testing.assert_almost_equal(mlt_2, 3.5729, decimal=4) np.testing.assert_equal(mlt_1, mlt_3) diff --git a/aacgmv2/tests/test_dep_aacgmv2.py b/aacgmv2/tests/test_dep_aacgmv2.py index 27dd024a..356e3d02 100644 --- a/aacgmv2/tests/test_dep_aacgmv2.py +++ b/aacgmv2/tests/test_dep_aacgmv2.py @@ -42,7 +42,7 @@ def test_convert_single_val(self): lat, lon = aacgmv2.convert(60, 0, 300, self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape == (1,) + assert lat.shape == lon.shape and lat.shape == (1,) np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) @@ -51,14 +51,14 @@ def test_convert_list(self): lat, lon = aacgmv2.convert([60], [0], [300], self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape == (1,) + assert lat.shape == lon.shape and lat.shape == (1,) np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) lat, lon = aacgmv2.convert([60, 61], [0, 0], [300, 300], self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape == (2,) + assert lat.shape == lon.shape and lat.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) @@ -68,7 +68,7 @@ def test_convert_arr(self): np.array([300]), self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape == (1,) + assert lat.shape == lon.shape and lat.shape == (1,) np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) @@ -76,7 +76,7 @@ def test_convert_arr(self): np.array([300, 300]), self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape == (2,) + assert lat.shape == lon.shape and lat.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) @@ -85,14 +85,14 @@ def test_convert_unequal(self): lat, lon = aacgmv2.convert([60, 61], 0, 300, self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape == (2,) + assert lat.shape == lon.shape and lat.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) lat, lon = aacgmv2.convert(np.array([60, 61]), 0, 300, self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape == (2,) + assert lat.shape == lon.shape and lat.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) @@ -100,7 +100,7 @@ def test_convert_unequal(self): 300, self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape == (2, 3) + assert lat.shape == lon.shape and lat.shape == (2, 3) np.testing.assert_allclose(lat, [[58.2258, 59.3186, 60.4040], [61.4820, 62.5528, 63.6164]], rtol=1e-4) @@ -112,7 +112,7 @@ def test_convert_unequal(self): [300], self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape == (2, 3) + assert lat.shape == lon.shape and lat.shape == (2, 3) np.testing.assert_allclose(lat, [[58.2258, 59.3186, 60.4040], [61.4820, 62.5528, 63.6164]], rtol=1e-4) @@ -122,18 +122,16 @@ def test_convert_unequal(self): def test_convert_badidea_failure(self): """Test conversion failure for BADIDEA""" - lat, lon = aacgmv2.convert([60], [0], [3000], self.dtime, badidea=True) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape == (1,) - assert np.all([np.isnan(lat), np.isnan(lon)]) + with pytest.raises(ValueError): + lat, lon = aacgmv2.convert([60], [0], [3000], self.dtime, + badidea=True) def test_convert_location_failure(self): """Test conversion with a bad location""" lat, lon = aacgmv2.convert([0], [0], [0], self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape & lat.shape == (1,) + assert lat.shape == lon.shape and lat.shape == (1,) assert np.all([np.isnan(lat), np.isnan(lon)]) def test_convert_time_failure(self): @@ -204,4 +202,5 @@ def test_igrf_dipole_axis(self): """Test the IGRF dipole axis calculation""" m = aacgmv2.depricated.igrf_dipole_axis(self.dtime) - np.testing.assert_allclose(m, [0.0503, -0.1606, 0.9857], rtol=1.0e-4) + np.testing.assert_allclose(m, [0.050253, -0.160608, 0.985738], + rtol=1.0e-4) diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index 9c8bd4ca..c700ed56 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -64,11 +64,11 @@ def test_convert_latlon_time_failure(self): def test_convert_latlon_arr_single_val(self): """Test array latlon conversion for a single value""" - lat, lon, r = aacgmv2.convert_latlon(60, 0, 300, self.dtime) + lat, lon, r = aacgmv2.convert_latlon_arr(60, 0, 300, self.dtime) assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (1,) + assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (1,) np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) np.testing.assert_allclose(r, [1.0457], rtol=1e-4) @@ -79,7 +79,7 @@ def test_convert_latlon_arr_list(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (1,) + assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (1,) np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) np.testing.assert_allclose(r, [1.0457], rtol=1e-4) @@ -89,7 +89,7 @@ def test_convert_latlon_arr_list(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (2,) + assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) @@ -101,7 +101,7 @@ def test_convert_latlon_arr_arr(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (1,) + assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (1,) np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) np.testing.assert_allclose(r, [1.0457], rtol=1e-4) @@ -113,7 +113,7 @@ def test_convert_latlon_arr_arr(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (2,) + assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) @@ -124,7 +124,7 @@ def test_convert_latlon_arr_unequal(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (2,) + assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) @@ -134,7 +134,7 @@ def test_convert_latlon_arr_unequal(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (2,) + assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2,) np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) @@ -145,7 +145,7 @@ def test_convert_latlon_arr_unequal(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (2, 3) + assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2, 3) np.testing.assert_allclose(lat, [[58.2258, 59.3186, 60.4040], [61.4820, 62.5528, 63.6164]], rtol=1e-4) @@ -161,7 +161,7 @@ def test_convert_latlon_arr_unequal(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (2, 3) + assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2, 3) np.testing.assert_allclose(lat, [[58.2258, 59.3186, 60.4040], [61.4820, 62.5528, 63.6164]], rtol=1e-4) @@ -179,7 +179,7 @@ def test_convert_latlon_arr_badidea_failure(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (1,) + assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (1,) assert np.all([np.isnan(lat), np.isnan(lon), np.isnan(r)]) def test_convert_latlon_arr_location_failure(self): @@ -188,7 +188,7 @@ def test_convert_latlon_arr_location_failure(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape & lat.shape == r.shape & r.shape == (1,) + assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (1,) assert np.all([np.isnan(lat), np.isnan(lon), np.isnan(r)]) def test_convert_latlon_arr_time_failure(self): @@ -283,28 +283,20 @@ def test_get_aacgm_coord_location_failure(self): def test_get_aacgm_coord_time_failure(self): """Test single value AACGMV2 calculation with a bad datetime""" - import testfixtures - lerr_1 = u"time must be specified as datetime object" - lerr_2 = u"Unable to get magnetic lat/lon" - - with testfixtures.LogCapture() as l: + with pytest.raises(AssertionError): mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 0, 300, None) - assert l.check(("root", "ERROR", lerr_1), - ("root", "ERROR", lerr_2)) is None - l.uninstall() - def test_get_aacgm_coord_arr_single_val(self): """Test array AACGMV2 calculation for a single value""" mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(60, 0, 300, self.dtime) assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ mlt.shape == (1,) - np.testing.assert_allclose(mlat, [58.2247], rtol=1e-4) - np.testing.assert_allclose(mlon, [81.1761], rtol=1e-4) - np.testing.assert_allclose(mlt, [0.1889], rtol=1e-4) + np.testing.assert_allclose(mlat, [58.22474610], rtol=1e-4) + np.testing.assert_allclose(mlon, [81.17611033], rtol=1e-4) + np.testing.assert_allclose(mlt, [0.18891995], rtol=1e-4) def test_get_aacgm_coord_arr_list(self): """Test array AACGMV2 calculation for list input""" @@ -313,22 +305,22 @@ def test_get_aacgm_coord_arr_list(self): assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ mlt.shape == (1,) - np.testing.assert_allclose(mlat, [58.2247], rtol=1e-4) - np.testing.assert_allclose(mlon, [81.1761], rtol=1e-4) - np.testing.assert_allclose(mlt, [0.1889], rtol=1e-4) + np.testing.assert_allclose(mlat, [58.22474610], rtol=1e-4) + np.testing.assert_allclose(mlon, [81.17611033], rtol=1e-4) + np.testing.assert_allclose(mlt, [0.18891995], rtol=1e-4) mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60, 61], [0, 0], [300, 300], self.dtime) assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) assert isinstance(r, np.ndarray) - assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ mlt.shape == (2,) - np.testing.assert_allclose(mlat, [58.2247, 59.3165], rtol=1e-4) - np.testing.assert_allclose(mlon, [81.1761, 81.6228], rtol=1e-4) - np.testing.assert_allclose(mlt, [0.1889, 0.2187], rtol=1e-4) + np.testing.assert_allclose(mlat, [58.22474610, 59.31648007], rtol=1e-4) + np.testing.assert_allclose(mlon, [81.17611033, 81.62281360], rtol=1e-4) + np.testing.assert_allclose(mlt, [0.18891995, 0.21870017], rtol=1e-4) def test_get_aacgm_coord_arr_arr(self): """Test array AACGMV2 calculation for array input""" @@ -339,11 +331,11 @@ def test_get_aacgm_coord_arr_arr(self): assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ mlt.shape == (1,) - np.testing.assert_allclose(mlat, [58.2247], rtol=1e-4) - np.testing.assert_allclose(mlon, [81.1761], rtol=1e-4) - np.testing.assert_allclose(mlt, [0.1889], rtol=1e-4) + np.testing.assert_almost_equal(mlat[0], 58.2247, decimal=4) + np.testing.assert_almost_equal(mlon[0], 81.1761, decimal=4) + np.testing.assert_almost_equal(mlt[0], 0.1889, decimal=4) mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([60, 61]), np.array([0, 0]), @@ -352,11 +344,11 @@ def test_get_aacgm_coord_arr_arr(self): assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ mlt.shape == (2,) - np.testing.assert_allclose(mlat, [58.2247, 59.3165], rtol=1e-4) - np.testing.assert_allclose(mlon, [81.1761, 81.6228], rtol=1e-4) - np.testing.assert_allclose(mlt, [0.1889, 0.2187], rtol=1e-4) + np.testing.assert_allclose(mlat, [58.22474610, 59.31648007], rtol=1e-4) + np.testing.assert_allclose(mlon, [81.17611033, 81.62281360], rtol=1e-4) + np.testing.assert_allclose(mlt, [0.18891995, 0.21870017], rtol=1e-4) def test_get_aacgm_coord_arr_unequal(self): """Test array AACGMV2 calculation for unequal sized input""" @@ -365,22 +357,22 @@ def test_get_aacgm_coord_arr_unequal(self): assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ mlt.shape == (2,) - np.testing.assert_allclose(mlat, [58.2247, 59.3165], rtol=1e-4) - np.testing.assert_allclose(mlon, [81.1761, 81.6228], rtol=1e-4) - np.testing.assert_allclose(mlt, [0.1889, 0.2187], rtol=1e-4) + np.testing.assert_allclose(mlat, [58.22474610, 59.31648007], rtol=1e-4) + np.testing.assert_allclose(mlon, [81.17611033, 81.62281360], rtol=1e-4) + np.testing.assert_allclose(mlt, [0.18891995, 0.21870017], rtol=1e-4) mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([60, 61]), 0, 300, self.dtime) assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ mlt.shape == (2,) - np.testing.assert_allclose(mlat, [58.2247, 59.3165], rtol=1e-4) - np.testing.assert_allclose(mlon, [81.1761, 81.6228], rtol=1e-4) - np.testing.assert_allclose(mlt, [0.1889, 0.2187], rtol=1e-4) + np.testing.assert_allclose(mlat, [58.22474610, 59.31648007], rtol=1e-4) + np.testing.assert_allclose(mlon, [81.17611033, 81.62281360], rtol=1e-4) + np.testing.assert_allclose(mlt, [0.18891995, 0.21870017], rtol=1e-4) mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([[60, 61, 62], [63, 64, 65]]), 0, @@ -388,16 +380,16 @@ def test_get_aacgm_coord_arr_unequal(self): assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ mlt.shape == (2, 3) np.testing.assert_allclose(mlat, [[58.2247, 59.3165, 60.4009], [61.4781, 62.5482, 63.6114]], - rtol=1e-4) + rtol=1e-3) np.testing.assert_allclose(mlon, [[81.1761, 81.6228, 82.0970], [82.6014, 83.1394, 83.7146]], - rtol=1e-4) + rtol=1e-3) np.testing.assert_allclose(mlt, [[0.1889, 0.2187, 0.2503], - [0.2839, 0.3198, 0.3582]], rtol=1e-4) + [0.2839, 0.3198, 0.3582]], rtol=1e-3) mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([[60, 61, 62], [63, 64, 65]]), @@ -405,16 +397,16 @@ def test_get_aacgm_coord_arr_unequal(self): assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ mlt.shape == (2, 3) np.testing.assert_allclose(mlat, [[58.2247, 59.3165, 60.4009], [61.4781, 62.5482, 63.6114]], - rtol=1e-4) + rtol=1e-3) np.testing.assert_allclose(mlon, [[81.1761, 81.6228, 82.0970], [82.6014, 83.1394, 83.7146]], - rtol=1e-4) + rtol=1e-3) np.testing.assert_allclose(mlt, [[0.1889, 0.2187, 0.2503], - [0.2839, 0.3198, 0.3582]], rtol=1e-4) + [0.2839, 0.3198, 0.3582]], rtol=1e-3) def test_get_aacgm_coord_arr_badidea_failure(self): """Test array AACGMV2 calculation failure for BADIDEA""" @@ -424,7 +416,7 @@ def test_get_aacgm_coord_arr_badidea_failure(self): assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ mlt.shape == (1,) assert np.all([np.isnan(mlat), np.isnan(mlon), np.isnan(mlt)]) @@ -434,24 +426,16 @@ def test_get_aacgm_coord_arr_location_failure(self): assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape & mlat.shape == mlt.shape & \ + assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ mlt.shape == (1,) assert np.all([np.isnan(mlat), np.isnan(mlon), np.isnan(mlt)]) def test_get_aacgm_coord_arr_time_failure(self): """Test array AACGMV2 calculation with a bad time""" - import testfixtures - lerr_1 = u"time must be specified as datetime object" - lerr_2 = u"Unable to get magnetic lat/lon" - - with testfixtures.LogCapture() as l: + with pytest.raises(AssertionError): mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60], [0], [300], None) - assert l.check(("root", "ERROR", lerr_1), - ("root", "ERROR", lerr_2)) is None - l.uninstall() - def test_get_aacgm_coord_datetime_date(self): """Test single AACGMV2 calculation with date and datetime input""" mlat_1, mlon_1, mlt_1 = aacgmv2.get_aacgm_coord(60, 0, 300, self.ddate) @@ -512,20 +496,21 @@ def test_get_aacgm_coord_arr_maxalt_failure(self): def test_get_aacgm_coord_mlat_failure(self): """Test error return for co-latitudes above 90 for a single value""" import testfixtures - lerr_1 = u"unrealistic latitude" - lerr_2 = u"Unable to get magnetic lat/lon" + lerr = u"unrealistic latitude" with testfixtures.LogCapture() as lhigh: - mlat, mlon, mlt = aacgmv2.get_aacgm_coord(91, 0, 300, self.dtime) + with pytest.raises(AssertionError): + mlat, mlon, mlt = aacgmv2.get_aacgm_coord(91, 0, 300, + self.dtime) - assert lhigh.check(("root", "ERROR", lerr_1), - ("root", "ERROR", lerr_2)) is None + assert lhigh.check(("root", "ERROR", lerr)) is None with testfixtures.LogCapture() as llow: - mlat, mlon, mlt = aacgmv2.get_aacgm_coord(-91, 0, 300, self.dtime) + with pytest.raises(AssertionError): + mlat, mlon, mlt = aacgmv2.get_aacgm_coord(-91, 0, 300, + self.dtime) - assert llow.check(("root", "ERROR", lerr_1), - ("root", "ERROR", lerr_2)) is None + assert llow.check(("root", "ERROR", lerr)) is None lhigh.uninstall() llow.uninstall() @@ -533,14 +518,13 @@ def test_get_aacgm_coord_mlat_failure(self): def test_get_aacgm_coord_arr_mlat_failure(self): """Test error return for co-latitudes above 90 for an array""" import testfixtures - lerr_1 = u"unrealistic latitude" - lerr_2 = u"Unable to get magnetic lat/lon" + lerr = u"unrealistic latitude" with testfixtures.LogCapture() as l: - aacgmv2.get_aacgm_coord_arr([91, 60, -91], 0, 300, self.dtime) + with pytest.raises(AssertionError): + aacgmv2.get_aacgm_coord_arr([91, 60, -91], 0, 300, self.dtime) - assert l.check(("root", "ERROR", lerr_1), - ("root", "ERROR", lerr_2)) is None + assert l.check(("root", "ERROR", lerr)) is None l.uninstall() def test_convert_str_to_bit_g2a(self): @@ -625,7 +609,7 @@ def test_inv_convert_mlt_list(self): mlon = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=True) np.testing.assert_allclose(mlon, [-153.5339, 41.4661, 11.4661], - rtol=1.0e-4) + rtol=1.0e-3) def test_inv_convert_mlt_arr(self): """Test MLT inversion for an array""" @@ -633,7 +617,7 @@ def test_inv_convert_mlt_arr(self): mlon = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=True) np.testing.assert_allclose(mlon, [-153.5339, 41.4661, 11.4661], - rtol=1.0e-4) + rtol=1.0e-3) def test_mlt_convert_single(self): """Test MLT calculation for a single value""" @@ -650,11 +634,11 @@ def test_mlt_convert_list(self): mlt_list = [270.0, 80.0, -90.0] mlt = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=False) - np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-4) + np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-3) def test_mlt_convert_arr(self): """Test MLT calculation for an array""" mlt_arr = np.array([270.0, 80.0, -90.0]) mlt = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=False) - np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-4) + np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-3) diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index 7056f9cb..a478840c 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -388,11 +388,11 @@ def get_aacgm_coord_arr(glat, glon, height, dtime, method="TRACE", coeff_prefix=coeff_prefix) if np.all(np.isnan(mlon)): - mlt = np.nan + mlt = np.empty(shape=mlat.shape, dtype=float) * np.nan else: # Get magnetic local time - mlt = convert_mlt(mlon, dtime, m2a=False, igrf_file=igrf_file, - coeff_prefix=coeff_prefix) + mlt = convert_mlt(mlon, dtime, m2a=False, coeff_prefix=coeff_prefix, + igrf_file=igrf_file) return mlat, mlon, mlt @@ -469,7 +469,7 @@ def convert_bool_to_bit(a2g=False, trace=False, allowtrace=False, return bit_code -def convert_mlt(arr, dtime, m2a=False, igrf_file=None, coeff_prefix=None): +def convert_mlt(arr, dtime, m2a=False, coeff_prefix=None, igrf_file=None): """Converts between magnetic local time (MLT) and AACGM-v2 longitude Parameters @@ -481,12 +481,12 @@ def convert_mlt(arr, dtime, m2a=False, igrf_file=None, coeff_prefix=None): m2a : (bool) Convert MLT to AACGM-v2 longitude (True) or magnetic longitude to MLT (False). (default=False) - igrf_file : (str or NoneType) - Full filename of IGRF coefficient file or None to use - aacgmv2.IGRF_12_COEFFS. (default=None) coeff_prefix : (str or NoneType) Location and file prefix for aacgm coefficient files or None to use aacgmv2.AACGM_v2_DAT_PREFIX. (default=None) + igrf_file : (str or NoneType) + Full filename of IGRF coefficient file or None to use + aacgmv2.IGRF_12_COEFFS. (default=None) Returns -------- @@ -506,15 +506,20 @@ def convert_mlt(arr, dtime, m2a=False, igrf_file=None, coeff_prefix=None): if igrf_file is None: igrf_file = aacgmv2.IGRF_12_COEFFS - # Set current date and time - c_aacgmv2.set_datetime(dtime.year, dtime.month, dtime.day, dtime.hour, - dtime.minute, dtime.second, coeff_prefix) + # Test time + if isinstance(dtime, dt.date): + dtime = dt.datetime.combine(dtime, dt.time(0)) + assert isinstance(dtime, dt.datetime), \ + logging.error('time must be specified as datetime object') + + # Calculate desired location, C routines set date and time if m2a: # Get the magnetic longitude inv_vectorised = np.vectorize(c_aacgmv2.inv_mlt_convert) out = inv_vectorised(dtime.year, dtime.month, dtime.day, dtime.hour, - dtime.minute, dtime.second, arr, igrf_file) + dtime.minute, dtime.second, arr, coeff_prefix, + igrf_file) else: # Get magnetic local time mlt_vectorised = np.vectorize(c_aacgmv2.mlt_convert) From 61774e30e1555e607aab1d7e51cd351be6816abb Mon Sep 17 00:00:00 2001 From: aburrell Date: Wed, 14 Mar 2018 18:59:00 -0500 Subject: [PATCH 034/101] tox improvements possible tox improvements --- .travis.yml | 10 +++------- tox.ini | 25 ++++++++----------------- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/.travis.yml b/.travis.yml index 72e2c615..bd4ff560 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,8 @@ env: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check - - TOXENV=np{1.8,1.9,1.10,1.14},extension-coveralls,coveralls - - TOXENV=np{1.8,1.9,1.10,1.14}-nocover + - TOXENV=extension-coveralls,coveralls,codecov + - TOXENV=nocover before_install: - python --version - uname -a @@ -34,8 +34,4 @@ notifications: on_failure: always matrix: allow_failures: - - env: "TOXENV=check" - - python: 3.3 - env: "TOXENV=np1.14-nocover" - - python: 3.3 - env: "TOXENV=np1.14,extension-coveralls,coveralls" \ No newline at end of file + - env: "TOXENV=check" \ No newline at end of file diff --git a/tox.ini b/tox.ini index 0dc710be..0b5fa789 100644 --- a/tox.ini +++ b/tox.ini @@ -2,8 +2,8 @@ envlist = clean, check, - np{1.8,1.9,1.10,1.14}, - np{1.8,1.9,1.10,1.14}-nocover, + {2.7,3.3,3.4,3.5,3.6}, + {2.7,3.3,3.4,3.5,3.6}-nocover, report, docs @@ -15,13 +15,12 @@ passenv = * deps = pytest - pytest-capturelog testfixtures commands = python setup.py clean --all build_ext --force --inplace {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} -[testenv:np{1.8,1.9,1.10,1.14}] +[testenv:{2.7,3.3,3.4,3.5,3.6}] setenv = {[testenv]setenv} WITH_COVERAGE=yes @@ -30,21 +29,15 @@ usedevelop = true deps = {[testenv]deps} pytest-cov - np1.8: numpy>=1.8,<1.9 - np1.9: numpy>=1.9,<1.10 - np1.10: numpy>=1.10,<1.11 - np1.14: numpy>=1.14,<1.15 + numpy commands = python setup.py clean --all build_ext --force --inplace {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -[testenv:np{1.8,1.9,1.10,1.14}-nocover] +[testenv:{2.7,3.3,3.4,3.5,3.6}-nocover] deps = {[testenv]deps} - np1.8: numpy>=1.8,<1.9 - np1.9: numpy>=1.9,<1.10 - np1.10: numpy>=1.10,<1.11 - np1.14: numpy>=1.14,<1.15 + numpy [testenv:codecov] deps = @@ -88,10 +81,9 @@ passenv = * [testenv:check] -#basepython = python3.4 deps = docutils - check-manifest +# check-manifest flake8 readme pygments @@ -99,7 +91,7 @@ skip_install = true usedevelop = false commands = python setup.py check --strict --metadata --restructuredtext - check-manifest {toxinidir} +# check-manifest {toxinidir} flake8 src tests [testenv:coveralls] @@ -122,7 +114,6 @@ commands = coveralls --build-root=. --include=src --dump=extension-coveralls.json [] [testenv:report] -#basepython = python3.4 deps = coverage skip_install = true usedevelop = false From c63356b4ffefe8d8164c8b30fdce02bcbb150be6 Mon Sep 17 00:00:00 2001 From: aburrell Date: Wed, 14 Mar 2018 18:59:24 -0500 Subject: [PATCH 035/101] pytest update Updated to use current pytest command --- setup.cfg | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/setup.cfg b/setup.cfg index 0fd147e4..b078e6d0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,7 +5,7 @@ release = register clean --all sdist max-line-length = 140 exclude = tests/*,*/migrations/*,*/south_migrations/* -[pytest] +[tool:pytest] norecursedirs = .git .tox @@ -70,11 +70,6 @@ dependencies = np1.10: numpy>=1.10,<1.11 np1.9: numpy>=1.9,<1.10 np1.8: numpy>=1.8,<1.9 -# 1.4: Django==1.4.16 !python_versions[3.*] -# 1.5: Django==1.5.11 -# 1.6: Django==1.6.8 -# 1.7: Django==1.7.1 !python_versions[2.6] -# Deps commented above are provided as examples. That's what you would use in a Django project. coverage_flags = : true From 03e3c497d8e664b97ef4fde1f34010a326d9d285 Mon Sep 17 00:00:00 2001 From: aburrell Date: Wed, 14 Mar 2018 19:00:11 -0500 Subject: [PATCH 036/101] gitignore Updated to ignore .pytest_cache --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9e8fc169..939de61b 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ pip-log.txt .coverage .tox .coverage.* +.pytest_cache nosetests.xml coverage.xml htmlcov From 0655d1adade0e9c0f3d9fcea06ed19341f4f9ebc Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 15 Mar 2018 13:10:24 -0500 Subject: [PATCH 037/101] variable name Fixed incorrect variable name in flag test --- aacgmv2/depricated.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aacgmv2/depricated.py b/aacgmv2/depricated.py index 6bbd37c0..e1d40986 100644 --- a/aacgmv2/depricated.py +++ b/aacgmv2/depricated.py @@ -56,7 +56,7 @@ def convert(lat, lon, alt, date=None, a2g=False, trace=False, allowtrace=False, lon_out : (float) Output longitude in degrees E """ - if(np.array(height).max() > 2000 and not trace and not allowtrace and + if(np.array(alt).max() > 2000 and not trace and not allowtrace and badidea): estr = 'coefficients are not valid for altitudes above 2000 km. You' estr += ' must either use field-line tracing (trace=True ' From 3f865187321a5f1610a6910ed1704ddbebd9d817 Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 15 Mar 2018 13:11:00 -0500 Subject: [PATCH 038/101] python tests Fixed python tests, primarily assigning the correct number of significant figures. --- aacgmv2/tests/test_py_aacgmv2.py | 159 ++++++++++++++++++------------- 1 file changed, 91 insertions(+), 68 deletions(-) diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index c700ed56..f53ec45f 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -69,9 +69,9 @@ def test_convert_latlon_arr_single_val(self): assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (1,) - np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) - np.testing.assert_allclose(r, [1.0457], rtol=1e-4) + np.testing.assert_allclose(lat, [58.2257709], rtol=1e-4) + np.testing.assert_allclose(lon, [81.16846959], rtol=1e-4) + np.testing.assert_allclose(r, [1.04566346], rtol=1e-4) def test_convert_latlon_arr_list(self): """Test array latlon conversion for list input""" @@ -80,9 +80,9 @@ def test_convert_latlon_arr_list(self): assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (1,) - np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) - np.testing.assert_allclose(r, [1.0457], rtol=1e-4) + np.testing.assert_allclose(lat, [58.2257709], rtol=1e-4) + np.testing.assert_allclose(lon, [81.16846959], rtol=1e-4) + np.testing.assert_allclose(r, [1.04566346], rtol=1e-4) lat, lon, r = aacgmv2.convert_latlon_arr([60, 61], [0, 0], [300, 300], self.dtime) @@ -90,9 +90,9 @@ def test_convert_latlon_arr_list(self): assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2,) - np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) - np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) + np.testing.assert_allclose(lat, [58.22577090, 59.31860933], rtol=1e-4) + np.testing.assert_allclose(lon, [81.16846959, 81.61398933], rtol=1e-4) + np.testing.assert_allclose(r, [1.04566346, 1.04561304], rtol=1e-4) def test_convert_latlon_arr_arr(self): """Test array latlon conversion for array input""" @@ -102,9 +102,9 @@ def test_convert_latlon_arr_arr(self): assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (1,) - np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) - np.testing.assert_allclose(r, [1.0457], rtol=1e-4) + np.testing.assert_allclose(lat, [58.2257709], rtol=1e-4) + np.testing.assert_allclose(lon, [81.16846959], rtol=1e-4) + np.testing.assert_allclose(r, [1.04566346], rtol=1e-4) lat, lon, r = aacgmv2.convert_latlon_arr(np.array([60, 61]), np.array([0, 0]), @@ -114,9 +114,9 @@ def test_convert_latlon_arr_arr(self): assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2,) - np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) - np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) + np.testing.assert_allclose(lat, [58.22577090, 59.31860933], rtol=1e-4) + np.testing.assert_allclose(lon, [81.16846959, 81.61398933], rtol=1e-4) + np.testing.assert_allclose(r, [1.04566346, 1.04561304], rtol=1e-4) def test_convert_latlon_arr_unequal(self): """Test array latlon conversion for unequal sized input""" @@ -125,9 +125,9 @@ def test_convert_latlon_arr_unequal(self): assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2,) - np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) - np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) + np.testing.assert_allclose(lat, [58.22577090, 59.31860933], rtol=1e-4) + np.testing.assert_allclose(lon, [81.16846959, 81.61398933], rtol=1e-4) + np.testing.assert_allclose(r, [1.04566346, 1.04561304], rtol=1e-4) lat, lon, r = aacgmv2.convert_latlon_arr(np.array([60, 61]), 0, 300, self.dtime) @@ -135,9 +135,9 @@ def test_convert_latlon_arr_unequal(self): assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2,) - np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) - np.testing.assert_allclose(r, [1.0457, 1.0456], rtol=1e-4) + np.testing.assert_allclose(lat, [58.22577090, 59.31860933], rtol=1e-4) + np.testing.assert_allclose(lon, [81.16846959, 81.61398933], rtol=1e-4) + np.testing.assert_allclose(r, [1.04566346, 1.04561304], rtol=1e-4) lat, lon, r = aacgmv2.convert_latlon_arr(np.array([[60, 61, 62], [63, 64, 65]]), 0, @@ -145,15 +145,17 @@ def test_convert_latlon_arr_unequal(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2, 3) - np.testing.assert_allclose(lat, [[58.2258, 59.3186, 60.4040], - [61.4820, 62.5528, 63.6164]], + assert r.shape == lon.shape and lat.shape == r.shape and \ + r.shape == (2, 3) + np.testing.assert_allclose(lat, [[58.2257709, 59.3186093, 60.4039740], + [61.4819893, 62.5527635, 63.6163840]], rtol=1e-4) - np.testing.assert_allclose(lon, [[81.1685, 81.6140, 82.0872], - [82.5909, 83.1286, 83.7039]], + np.testing.assert_allclose(lon, [[81.1684696, 81.6139893, 82.0871880], + [82.5909499, 83.1285895, 83.7039272]], + rtol=1e-4) + np.testing.assert_allclose(r, [[1.04566346, 1.04561304, 1.04556369], + [1.04551548, 1.04546847, 1.04542272]], rtol=1e-4) - np.testing.assert_allclose(r, [[1.0457, 1.0456, 1.0456] - [1.0455, 1.0455, 1.0454]], rtol=1e-4) lat, lon, r = aacgmv2.convert_latlon_arr(np.array([[60, 61, 62], [63, 64, 65]]), [0], @@ -161,15 +163,18 @@ def test_convert_latlon_arr_unequal(self): assert isinstance(lat, np.ndarray) assert isinstance(lon, np.ndarray) assert isinstance(r, np.ndarray) - assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2, 3) - np.testing.assert_allclose(lat, [[58.2258, 59.3186, 60.4040], - [61.4820, 62.5528, 63.6164]], + assert r.shape == lon.shape and lat.shape == r.shape and \ + r.shape == (2, 3) + + np.testing.assert_allclose(lat, [[58.2257709, 59.3186093, 60.4039740], + [61.4819893, 62.5527635, 63.6163840]], rtol=1e-4) - np.testing.assert_allclose(lon, [[81.1685, 81.6140, 82.0872], - [82.5909, 83.1286, 83.7039]], + np.testing.assert_allclose(lon, [[81.1684696, 81.6139893, 82.0871880], + [82.5909499, 83.1285895, 83.7039272]], + rtol=1e-4) + np.testing.assert_allclose(r, [[1.04566346, 1.04561304, 1.04556369], + [1.04551548, 1.04546847, 1.04542272]], rtol=1e-4) - np.testing.assert_allclose(r, [[1.0457, 1.0456, 1.0456] - [1.0455, 1.0455, 1.0454]], rtol=1e-4) def test_convert_latlon_arr_badidea_failure(self): """Test array latlon conversion failure for BADIDEA""" @@ -315,7 +320,7 @@ def test_get_aacgm_coord_arr_list(self): [300, 300], self.dtime) assert isinstance(mlat, np.ndarray) assert isinstance(mlon, np.ndarray) - assert isinstance(r, np.ndarray) + assert isinstance(mlt, np.ndarray) assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ mlt.shape == (2,) np.testing.assert_allclose(mlat, [58.22474610, 59.31648007], rtol=1e-4) @@ -382,14 +387,15 @@ def test_get_aacgm_coord_arr_unequal(self): assert isinstance(mlt, np.ndarray) assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ mlt.shape == (2, 3) - np.testing.assert_allclose(mlat, [[58.2247, 59.3165, 60.4009], - [61.4781, 62.5482, 63.6114]], - rtol=1e-3) - np.testing.assert_allclose(mlon, [[81.1761, 81.6228, 82.0970], - [82.6014, 83.1394, 83.7146]], - rtol=1e-3) - np.testing.assert_allclose(mlt, [[0.1889, 0.2187, 0.2503], - [0.2839, 0.3198, 0.3582]], rtol=1e-3) + np.testing.assert_allclose(mlat, [[58.2247461, 59.3164801, 60.4008651], + [61.4780560, 62.5481858, 63.6113609]], + rtol=1e-4) + np.testing.assert_allclose(mlon, [[81.1761103, 81.6228136, 82.0969646], + [82.6013918, 83.1393547, 83.7146224]], + rtol=1e-4) + np.testing.assert_allclose(mlt, [[0.18891995, 0.21870017, 0.25031024], + [0.28393872, 0.31980291, 0.35815409]], + rtol=1e-4) mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([[60, 61, 62], [63, 64, 65]]), @@ -440,19 +446,20 @@ def test_get_aacgm_coord_datetime_date(self): """Test single AACGMV2 calculation with date and datetime input""" mlat_1, mlon_1, mlt_1 = aacgmv2.get_aacgm_coord(60, 0, 300, self.ddate) mlat_2, mlon_2, mlt_2 = aacgmv2.get_aacgm_coord(60, 0, 300, self.dtime) - assert mlat_1 == mlat_2 - assert mlon_1 == mlon_2 - assert r_1 == r_2 - def test_get_aacgm_coord_datetime_date(self): + np.testing.assert_almost_equal(mlat_1, mlat_2, decimal=6) + np.testing.assert_almost_equal(mlon_1, mlon_2, decimal=6) + np.testing.assert_almost_equal(mlt_1, mlt_2, decimal=6) + + def test_get_aacgm_coord_arr_datetime_date(self): """Test array AACGMV2 calculation with date and datetime input""" mlat_1, mlon_1, mlt_1 = aacgmv2.get_aacgm_coord_arr([60], [0], [300], self.ddate) mlat_2, mlon_2, mlt_2 = aacgmv2.get_aacgm_coord_arr([60], [0], [300], self.dtime) - assert mlat_1 == mlat_2 - assert mlon_1 == mlon_2 - assert r_1 == r_2 + np.testing.assert_almost_equal(mlat_1, mlat_2, decimal=6) + np.testing.assert_almost_equal(mlon_1, mlon_2, decimal=6) + np.testing.assert_almost_equal(mlt_1, mlt_2, decimal=6) def test_warning_below_ground_get_aacgm_coord(self): """ Test that a warning is issued if altitude is below zero""" @@ -599,46 +606,62 @@ def test_inv_convert_mlt_single(self): mlon_2 = aacgmv2.convert_mlt(25.0, self.dtime, m2a=True) mlon_3 = aacgmv2.convert_mlt(-1.0, self.dtime, m2a=True) - np.testing.assert_almost_equal(mlon_1, -153.5339, decimal=4) - np.testing.assert_almost_equal(mlon_2, 41.4661, decimal=4) - np.testing.assert_almost_equal(mlon_2, 11.4661, decimal=4) + np.testing.assert_almost_equal(mlon_1, -101.657689, decimal=4) + np.testing.assert_almost_equal(mlon_2, 93.34231102, decimal=4) + np.testing.assert_almost_equal(mlon_3, 63.34231102, decimal=4) def test_inv_convert_mlt_list(self): """Test MLT inversion for a list""" mlt_list = [12.0, 25.0, -1.0] mlon = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=True) - np.testing.assert_allclose(mlon, [-153.5339, 41.4661, 11.4661], - rtol=1.0e-3) + np.testing.assert_allclose(mlon, [-101.657689, 93.342311, 63.342311], + rtol=1.0e-4) def test_inv_convert_mlt_arr(self): """Test MLT inversion for an array""" mlt_arr = np.array([12.0, 25.0, -1.0]) mlon = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=True) - np.testing.assert_allclose(mlon, [-153.5339, 41.4661, 11.4661], - rtol=1.0e-3) + np.testing.assert_allclose(mlon, [-101.657689, 93.342311, 63.342311], + rtol=1.0e-4) + + def test_inv_convert_mlt_wrapping(self): + """Test MLT wrapping""" + mlt_arr = np.array([1.0, 25.0, -1.0, 23.0]) + mlon = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=True) + + np.testing.assert_almost_equal(mlon[0], mlon[1], decimal=6) + np.testing.assert_almost_equal(mlon[2], mlon[3], decimal=6) + + def test_mlt_convert_mlon_wrapping(self): + """Test mlon wrapping""" + mlon_arr = np.array([270.0, -90.0, 1.0, 361.0]) + mlt = aacgmv2.convert_mlt(mlon_arr, self.dtime, m2a=False) + + np.testing.assert_almost_equal(mlt[0], mlt[1], decimal=6) + np.testing.assert_almost_equal(mlt[2], mlt[3], decimal=6) def test_mlt_convert_single(self): """Test MLT calculation for a single value""" mlt_1 = aacgmv2.convert_mlt(270.0, self.dtime, m2a=False) mlt_2 = aacgmv2.convert_mlt(80.0, self.dtime, m2a=False) - mlt_3 = aacgmv2.convert_mlt(-90.0, self.dtime, m2a=False) - np.testing.assert_almost_equal(mlt_1, 16.2356, decimal=4) - np.testing.assert_almost_equal(mlt_2, 3.5689, decimal=4) - np.testing.assert_equal(mlt_1, mlt_3) + np.testing.assert_almost_equal(mlt_1, 12.77717927, decimal=4) + np.testing.assert_almost_equal(mlt_2, 0.1105126, decimal=4) def test_mlt_convert_list(self): """Test MLT calculation for a list""" - mlt_list = [270.0, 80.0, -90.0] - mlt = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=False) + mlon_list = [270.0, 80.0, -95.0] + mlt = aacgmv2.convert_mlt(mlon_list, self.dtime, m2a=False) - np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-3) + np.testing.assert_allclose(mlt, [12.77717927, 0.1105126, 12.44384593], + rtol=1.0e-4) def test_mlt_convert_arr(self): """Test MLT calculation for an array""" - mlt_arr = np.array([270.0, 80.0, -90.0]) - mlt = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=False) + mlon_arr = np.array([270.0, 80.0, -95.0]) + mlt = aacgmv2.convert_mlt(mlon_arr, self.dtime, m2a=False) - np.testing.assert_allclose(mlt, [16.2356, 3.5689, 16.2356], rtol=1.0e-3) + np.testing.assert_allclose(mlt, [12.77717927, 0.1105126, 12.44384593], + rtol=1.0e-4) From 93affd4bf197b7d1af88870a5aa65018741fc126 Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 15 Mar 2018 13:16:55 -0500 Subject: [PATCH 039/101] Usage Fixed usage to reflect results and not have a bug in output --- docs/usage.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index e43c4bb1..0cfdbdb6 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -13,9 +13,9 @@ For full documentation of the functions, see :doc:`Reference → aacgmv2 >> dtime = dt.datetime(2013, 11, 3) >>> mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 15, 300, dtime) >>> "{:.4f} {:.4f} {:.4f}".format(mlat, mlon, mlt) - '58.2247 81.1761 0.1889' + '57.4698 93.6300 1.4822' >>> # AACGM to geo, mix arrays/numbers - >>> glat, glon alt = aacgmv2.convert_latlon_arr([90, -90], 0, 0, dtime, code="A2G") + >>> glat, glon, alt = aacgmv2.convert_latlon_arr([90, -90], 0, 0, dtime, code="A2G") >>> ["{:.4f} {:.4f} {:.4f}".format(lat, glon[i], alt[i]) for i,lat in enumerate(glat)] ['82.9666 -84.6652 14.1244', '-74.3385 125.8401 12.8771'] From e53dd638a4e10aa1fdca8ccb331a4814d77c3a4a Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 15 Mar 2018 13:17:51 -0500 Subject: [PATCH 040/101] documentation compilation Hopefully fixed documentation compilation by including requirement for sphinx_rtd_theme and updating coverage source directory to the new directory structure. --- .coveragerc | 8 ++++++-- docs/conf.py | 3 ++- docs/requirements.txt | 1 + tox.ini | 1 + 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.coveragerc b/.coveragerc index 772866d2..8a55c3fe 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,9 +1,13 @@ [paths] -source = src +source = + aacgmv2 + c_aacgmv2 [run] branch = True -source = src +source = + aacgmv2 + c_aacgmv2 parallel = true [report] diff --git a/docs/conf.py b/docs/conf.py index fa670ad2..09313527 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -28,7 +28,8 @@ # on_rtd is whether we are on readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' -if not on_rtd: # only import and set the theme if we're building docs locally +# only import and set the theme if we're building docs locally +if not on_rtd: import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] diff --git a/docs/requirements.txt b/docs/requirements.txt index 8edfbc77..51b6ed5d 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,5 @@ sphinx sphinxcontrib-napoleon numpydoc +sphinx_rtd_theme -e . diff --git a/tox.ini b/tox.ini index 0b5fa789..07989cb2 100644 --- a/tox.ini +++ b/tox.ini @@ -65,6 +65,7 @@ deps = [testenv:docs] deps = -r{toxinidir}/docs/requirements.txt + sphinx_rtd_theme commands = sphinx-build {posargs:-E} -b html docs dist/docs sphinx-build -b linkcheck docs dist/docs From 78851d9dd741585c98eb4f88fc263656554e7966 Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 15 Mar 2018 16:40:21 -0500 Subject: [PATCH 041/101] bootstrap update updated bootstrap to include needed dependencies and remove tests for all versions of numpy --- .travis.yml | 21 +++-- appveyor.yml | 211 +++---------------------------------------- ci/bootstrap.py | 4 +- ci/templates/tox.ini | 4 +- setup.cfg | 6 +- tox.ini | 173 +++++++++++++++++++++++++++-------- 6 files changed, 167 insertions(+), 252 deletions(-) diff --git a/.travis.yml b/.travis.yml index bd4ff560..338431d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,21 @@ language: python -python: - - "2.7" - - "3.3" - - "3.4" - - "3.5" - - "3.6" +python: '3.5' sudo: false env: global: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check - - TOXENV=extension-coveralls,coveralls,codecov - - TOXENV=nocover + - TOXENV=2.7,extension-coveralls,coveralls,codecov + - TOXENV=2.7-nocover + - TOXENV=3.3,extension-coveralls,coveralls,codecov + - TOXENV=3.3-nocover + - TOXENV=3.4,extension-coveralls,coveralls,codecov + - TOXENV=3.4-nocover + - TOXENV=3.5,extension-coveralls,coveralls,codecov + - TOXENV=3.5-nocover + - TOXENV=3.6,extension-coveralls,coveralls,codecov + - TOXENV=3.6-nocover before_install: - python --version - uname -a @@ -34,4 +37,4 @@ notifications: on_failure: always matrix: allow_failures: - - env: "TOXENV=check" \ No newline at end of file + - env: "TOXENV=check" diff --git a/appveyor.yml b/appveyor.yml index 6c8576b6..c4b3736b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -52,234 +52,56 @@ environment: PYTHON_HOME: C:\python35-x64 PYTHON_VERSION: '3.5' PYTHON_ARCH: '64' - - TOXENV: '3.6-buildonly-nocover' - TOXPYTHON: C:\python36\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' - - TOXENV: '3.6-buildonly-nocover' - TOXPYTHON: C:\python36-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python36-x64 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '64' - TOXENV: check PYTHON_HOME: C:\Python27 PYTHON_VERSION: '2.7' PYTHON_ARCH: '32' - - TOXENV: '2.7-np1.14-nocover' - TOXPYTHON: C:\python27\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - - TOXENV: '2.7-np1.14-nocover' - TOXPYTHON: C:\python27-x64\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\python27-x64 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '64' - - TOXENV: '2.7-np1.10-nocover' - TOXPYTHON: C:\python27\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - - TOXENV: '2.7-np1.10-nocover' - TOXPYTHON: C:\python27-x64\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\python27-x64 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '64' - - TOXENV: '2.7-np1.8-nocover' + - TOXENV: '2.7-nocover' TOXPYTHON: C:\python27\python.exe WINDOWS_SDK_VERSION: v7.0 PYTHON_HOME: C:\python27 PYTHON_VERSION: '2.7' PYTHON_ARCH: '32' - - TOXENV: '2.7-np1.8-nocover' + - TOXENV: '2.7-nocover' TOXPYTHON: C:\python27-x64\python.exe WINDOWS_SDK_VERSION: v7.0 PYTHON_HOME: C:\python27-x64 PYTHON_VERSION: '2.7' PYTHON_ARCH: '64' - - TOXENV: '2.7-np1.9-nocover' - TOXPYTHON: C:\python27\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - - TOXENV: '2.7-np1.9-nocover' - TOXPYTHON: C:\python27-x64\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\python27-x64 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '64' - - TOXENV: '3.3-np1.10-nocover' - TOXPYTHON: C:\python33\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python33 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '32' - - TOXENV: '3.3-np1.10-nocover' - TOXPYTHON: C:\python33-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python33-x64 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '64' - - TOXENV: '3.3-np1.8-nocover' + - TOXENV: '3.3-nocover' TOXPYTHON: C:\python33\python.exe WINDOWS_SDK_VERSION: v7.1 PYTHON_HOME: C:\python33 PYTHON_VERSION: '3.3' PYTHON_ARCH: '32' - - TOXENV: '3.3-np1.8-nocover' + - TOXENV: '3.3-nocover' TOXPYTHON: C:\python33-x64\python.exe WINDOWS_SDK_VERSION: v7.1 PYTHON_HOME: C:\python33-x64 PYTHON_VERSION: '3.3' PYTHON_ARCH: '64' - - TOXENV: '3.3-np1.9-nocover' - TOXPYTHON: C:\python33\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python33 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '32' - - TOXENV: '3.3-np1.9-nocover' - TOXPYTHON: C:\python33-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python33-x64 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '64' - - TOXENV: '3.4-np1.14-nocover' - TOXPYTHON: C:\python34\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\python34 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '32' - - TOXENV: '3.4-np1.14-nocover' - TOXPYTHON: C:\python34-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python34-x64 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '64' - - TOXENV: '3.4-np1.10-nocover' - TOXPYTHON: C:\python34\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\python34 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '32' - - TOXENV: '3.4-np1.10-nocover' - TOXPYTHON: C:\python34-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python34-x64 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '64' - - TOXENV: '3.4-np1.8-nocover' - TOXPYTHON: C:\python34\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\python34 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '32' - - TOXENV: '3.4-np1.8-nocover' - TOXPYTHON: C:\python34-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python34-x64 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '64' - - TOXENV: '3.4-np1.9-nocover' + - TOXENV: '3.4-nocover' TOXPYTHON: C:\python34\python.exe WINDOWS_SDK_VERSION: v7.0 PYTHON_HOME: C:\python34 PYTHON_VERSION: '3.4' PYTHON_ARCH: '32' - - TOXENV: '3.4-np1.9-nocover' + - TOXENV: '3.4-nocover' TOXPYTHON: C:\python34-x64\python.exe WINDOWS_SDK_VERSION: v7.1 PYTHON_HOME: C:\python34-x64 PYTHON_VERSION: '3.4' PYTHON_ARCH: '64' - - TOXENV: '3.5-np1.14-nocover' + - TOXENV: '3.5-nocover' TOXPYTHON: C:\python35\python.exe PYTHON_HOME: C:\python35 PYTHON_VERSION: '3.5' PYTHON_ARCH: '32' - - TOXENV: '3.5-np1.14-nocover' + - TOXENV: '3.5-nocover' TOXPYTHON: C:\python35-x64\python.exe PYTHON_HOME: C:\python35-x64 PYTHON_VERSION: '3.5' PYTHON_ARCH: '64' - - TOXENV: '3.5-np1.10-nocover' - TOXPYTHON: C:\python35\python.exe - PYTHON_HOME: C:\python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - - TOXENV: '3.5-np1.10-nocover' - TOXPYTHON: C:\python35-x64\python.exe - PYTHON_HOME: C:\python35-x64 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '64' - - TOXENV: '3.5-np1.8-nocover' - TOXPYTHON: C:\python35\python.exe - PYTHON_HOME: C:\python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - - TOXENV: '3.5-np1.8-nocover' - TOXPYTHON: C:\python35-x64\python.exe - PYTHON_HOME: C:\python35-x64 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '64' - - TOXENV: '3.5-np1.9-nocover' - TOXPYTHON: C:\python35\python.exe - PYTHON_HOME: C:\python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - - TOXENV: '3.5-np1.9-nocover' - TOXPYTHON: C:\python35-x64\python.exe - PYTHON_HOME: C:\python35-x64 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '64' - - TOXENV: '3.6-np1.14-nocover' - TOXPYTHON: C:\python36\python.exe - PYTHON_HOME: C:\python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' - - TOXENV: '3.6-np1.14-nocover' - TOXPYTHON: C:\python36-x64\python.exe - PYTHON_HOME: C:\python36-x64 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '64' - - TOXENV: '3.6-np1.10-nocover' - TOXPYTHON: C:\python36\python.exe - PYTHON_HOME: C:\python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' - - TOXENV: '3.6-np1.10-nocover' - TOXPYTHON: C:\python36-x64\python.exe - PYTHON_HOME: C:\python36-x64 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '64' - - TOXENV: '3.6-np1.8-nocover' - TOXPYTHON: C:\python36\python.exe - PYTHON_HOME: C:\python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' - - TOXENV: '3.6-np1.8-nocover' - TOXPYTHON: C:\python36-x64\python.exe - PYTHON_HOME: C:\python36-x64 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '64' - - TOXENV: '3.6-np1.9-nocover' - TOXPYTHON: C:\python36\python.exe - PYTHON_HOME: C:\python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' - - TOXENV: '3.6-np1.9-nocover' - TOXPYTHON: C:\python36-x64\python.exe - PYTHON_HOME: C:\python36-x64 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '64' init: - ps: echo $env:TOXENV - ps: ls C:\Python* @@ -310,26 +132,19 @@ matrix: allow_failures: - TOXENV: 'check' - TOXENV: '3.3-np1.8-nocover' - - TOXENV: '3.3-np1.8-cover,codecov' + - TOXENV: '3.3-np1.8,codecov' - TOXENV: '3.4-np1.8-nocover' - - TOXENV: '3.4-np1.8-cover,codecov' + - TOXENV: '3.4-np1.8,codecov' - TOXENV: '3.5-np1.8-nocover' - - TOXENV: '3.5-np1.8-cover,codecov' - - TOXENV: '3.6-np1.8-nocover' - - TOXENV: '3.6-np1.8-cover,codecov' + - TOXENV: '3.5-np1.8,codecov' - TOXENV: '3.5-np1.9-nocover' - - TOXENV: '3.5-np1.9-cover,codecov' - - TOXENV: '3.6-np1.9-nocover' - - TOXENV: '3.6-np1.9-cover,codecov' + - TOXENV: '3.5-np1.9,codecov' - TOXENV: '3.5-np1.10-nocover' - - TOXENV: '3.5-np1.10-cover,codecov' - - TOXENV: '3.6-np1.10-nocover' - - TOXENV: '3.6-np1.10-cover,codecov' + - TOXENV: '3.5-np1.10,codecov' - TOXENV: '2.7-buildonly-nocover' - TOXENV: '3.3-buildonly-nocover' - TOXENV: '3.4-buildonly-nocover' - TOXENV: '3.5-buildonly-nocover' - - TOXENV: '3.6-buildonly-nocover' ### To enable remote debugging uncomment this: # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/ci/bootstrap.py b/ci/bootstrap.py index 089ee4b2..aec07822 100644 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -42,7 +42,7 @@ for (alias, conf) in matrix.from_file(os.path.join(base_path, "setup.cfg")).items(): python = conf["python_versions"] - deps = conf["dependencies"] + #deps = conf["dependencies"] if "coverage_flags" in conf: cover = {"false": False, "true": True}[conf["coverage_flags"].lower()] @@ -51,7 +51,7 @@ tox_envs[alias] = { "python": "python" + python if "py" not in python else python, - "deps": deps.split(), + #"deps": deps.split(), } if "coverage_flags" in conf: tox_envs[alias].update(cover=cover) diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index a5906ae3..194a7dca 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -16,7 +16,9 @@ passenv = * deps = pytest - pytest-capturelog + testfixtures + logging + numpy commands = python setup.py clean --all build_ext --force --inplace {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} diff --git a/setup.cfg b/setup.cfg index b078e6d0..e16224a1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -65,11 +65,7 @@ python_versions = 3.5 3.6 -dependencies = - np1.14: numpy>=1.14,<1.15 !python_versions[3.3] - np1.10: numpy>=1.10,<1.11 - np1.9: numpy>=1.9,<1.10 - np1.8: numpy>=1.8,<1.9 +#dependencies = coverage_flags = : true diff --git a/tox.ini b/tox.ini index 07989cb2..9f460f1f 100644 --- a/tox.ini +++ b/tox.ini @@ -2,8 +2,16 @@ envlist = clean, check, - {2.7,3.3,3.4,3.5,3.6}, - {2.7,3.3,3.4,3.5,3.6}-nocover, + 2.7, + 2.7-nocover, + 3.3, + 3.3-nocover, + 3.4, + 3.4-nocover, + 3.5, + 3.5-nocover, + 3.6, + 3.6-nocover, report, docs @@ -16,40 +24,12 @@ passenv = deps = pytest testfixtures -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} - -[testenv:{2.7,3.3,3.4,3.5,3.6}] -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -deps = - {[testenv]deps} - pytest-cov + logging numpy commands = python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} - -[testenv:{2.7,3.3,3.4,3.5,3.6}-nocover] -deps = - {[testenv]deps} - numpy + {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} -[testenv:codecov] -deps = - codecov -skip_install = true -usedevelop = false -commands = - coverage combine - coverage report - coverage xml --ignore-errors - codecov [] - [testenv:spell] setenv = SPELLCHECK=1 @@ -65,7 +45,6 @@ deps = [testenv:docs] deps = -r{toxinidir}/docs/requirements.txt - sphinx_rtd_theme commands = sphinx-build {posargs:-E} -b html docs dist/docs sphinx-build -b linkcheck docs dist/docs @@ -82,9 +61,10 @@ passenv = * [testenv:check] +basepython = python3.4 deps = docutils -# check-manifest + check-manifest flake8 readme pygments @@ -92,7 +72,7 @@ skip_install = true usedevelop = false commands = python setup.py check --strict --metadata --restructuredtext -# check-manifest {toxinidir} + check-manifest {toxinidir} flake8 src tests [testenv:coveralls] @@ -105,6 +85,17 @@ commands = coverage report coveralls --merge=extension-coveralls.json [] +[testenv:codecov] +deps = + codecov +skip_install = true +usedevelop = false +commands = + coverage combine + coverage report + coverage xml --ignore-errors + codecov [] + [testenv:extension-coveralls] deps = @@ -115,6 +106,7 @@ commands = coveralls --build-root=. --include=src --dump=extension-coveralls.json [] [testenv:report] +basepython = python3.4 deps = coverage skip_install = true usedevelop = false @@ -128,7 +120,114 @@ skip_install = true usedevelop = false deps = coverage -[testenv:buildonly-nocover] +[testenv:2.7] +basepython = {env:TOXPYTHON:python2.7} +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +deps = + {[testenv]deps} + pytest-cov + +[testenv:2.7-nocover] +basepython = {env:TOXPYTHON:python2.7} + +[testenv:3.3] +basepython = {env:TOXPYTHON:python3.3} +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +deps = + {[testenv]deps} + pytest-cov + +[testenv:3.3-nocover] +basepython = {env:TOXPYTHON:python3.3} + +[testenv:3.4] +basepython = {env:TOXPYTHON:python3.4} +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +deps = + {[testenv]deps} + pytest-cov + +[testenv:3.4-nocover] +basepython = {env:TOXPYTHON:python3.4} + +[testenv:3.5] +basepython = {env:TOXPYTHON:python3.5} +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +deps = + {[testenv]deps} + pytest-cov + +[testenv:3.5-nocover] +basepython = {env:TOXPYTHON:python3.5} + +[testenv:3.6] +basepython = {env:TOXPYTHON:python3.6} +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} +deps = + {[testenv]deps} + pytest-cov + +[testenv:3.6-nocover] +basepython = {env:TOXPYTHON:python3.6} + + +[testenv:2.7-buildonly-nocover] +basepython = {env:TOXPYTHON:python2.7} deps = skip_install = true -commands = \ No newline at end of file +commands = + +[testenv:3.3-buildonly-nocover] +basepython = {env:TOXPYTHON:python3.3} +deps = +skip_install = true +commands = + +[testenv:3.4-buildonly-nocover] +basepython = {env:TOXPYTHON:python3.4} +deps = +skip_install = true +commands = + +[testenv:3.5-buildonly-nocover] +basepython = {env:TOXPYTHON:python3.5} +deps = +skip_install = true +commands = + + From ea8a1efdb27f5782772e30b45dc3d033d35270b5 Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 15 Mar 2018 16:40:44 -0500 Subject: [PATCH 042/101] Updated manifest Updated manifest to include all files --- MANIFEST.in | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index f0d737a9..e7e51b8c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,10 @@ graft docs graft ci -graft tests +graft aacgmv2/tests +graft c_aacgmv2 + +recursive-include aacgmv2 *.asc +recursive-include aacgmv2 *.txt include .bumpversion.cfg include .coveragerc @@ -14,8 +18,7 @@ include CONTRIBUTING.rst include LICENSE include LICENSE-AstAlg.txt include README.rst -include c_aacgmv2/README.txt include tox.ini .travis.yml appveyor.yml .codeclimate.yml .landscape.yaml -global-exclude *.py[cod] __pycache__ *.so *.dylib *.dSYM +global-exclude *.py[cod] __pycache__ *.so *.dylib *.dSYM .pytest_cache From 867f20d1e5a7b1dd74e3193d0261f157f8282d3e Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 15 Mar 2018 16:41:14 -0500 Subject: [PATCH 043/101] badge update Changed badge locations from cmeeren to aburrell --- README.rst | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.rst b/README.rst index 56ed643a..d58c5bcf 100644 --- a/README.rst +++ b/README.rst @@ -71,36 +71,36 @@ Badges :target: https://readthedocs.org/projects/aacgmv2 :alt: Documentation Status -.. |travis| image:: https://travis-ci.org/cmeeren/aacgmv2.svg?branch=master +.. |travis| image:: https://travis-ci.org/aburrell/aacgmv2.svg?branch=master :alt: Travis-CI Build Status - :target: https://travis-ci.org/cmeeren/aacgmv2 + :target: https://travis-ci.org/aburrell/aacgmv2 -.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/cmeeren/aacgmv2?branch=master&svg=true +.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/aburrell/aacgmv2?branch=master&svg=true :alt: AppVeyor Build Status - :target: https://ci.appveyor.com/project/cmeeren/aacgmv2 + :target: https://ci.appveyor.com/project/aburrell/aacgmv2 -.. |requires| image:: https://requires.io/github/cmeeren/aacgmv2/requirements.svg?branch=master +.. |requires| image:: https://requires.io/github/aburrell/aacgmv2/requirements.svg?branch=master :alt: Requirements Status - :target: https://requires.io/github/cmeeren/aacgmv2/requirements/?branch=master + :target: https://requires.io/github/aburrell/aacgmv2/requirements/?branch=master -.. |coveralls| image:: https://coveralls.io/repos/cmeeren/aacgmv2/badge.svg?branch=master&service=github +.. |coveralls| image:: https://coveralls.io/repos/aburrell/aacgmv2/badge.svg?branch=master&service=github :alt: Coverage Status - :target: https://coveralls.io/github/cmeeren/aacgmv2 + :target: https://coveralls.io/github/aburrell/aacgmv2 -.. |codecov| image:: https://codecov.io/github/cmeeren/aacgmv2/coverage.svg?branch=master +.. |codecov| image:: https://codecov.io/github/aburrell/aacgmv2/coverage.svg?branch=master :alt: Coverage Status - :target: https://codecov.io/github/cmeeren/aacgmv2 + :target: https://codecov.io/github/aburrell/aacgmv2 -.. |landscape| image:: https://landscape.io/github/cmeeren/aacgmv2/master/landscape.svg?style=flat - :target: https://landscape.io/github/cmeeren/aacgmv2/master +.. |landscape| image:: https://landscape.io/github/aburrell/aacgmv2/master/landscape.svg?style=flat + :target: https://landscape.io/github/aburrell/aacgmv2/master :alt: Code Quality Status .. |codacy| image:: https://img.shields.io/codacy/af7fdf6be28841f283dfdbc1c01fa82a.svg?style=flat - :target: https://www.codacy.com/app/cmeeren/aacgmv2 + :target: https://www.codacy.com/app/aburrell/aacgmv2 :alt: Codacy Code Quality Status -.. |codeclimate| image:: https://codeclimate.com/github/cmeeren/aacgmv2/badges/gpa.svg - :target: https://codeclimate.com/github/cmeeren/aacgmv2 +.. |codeclimate| image:: https://codeclimate.com/github/aburrell/aacgmv2/badges/gpa.svg + :target: https://codeclimate.com/github/aburrell/aacgmv2 :alt: CodeClimate Quality Status .. |version| image:: https://img.shields.io/pypi/v/aacgmv2.svg?style=flat :alt: PyPI Package latest release @@ -122,6 +122,6 @@ Badges :alt: Supported implementations :target: https://pypi.python.org/pypi/aacgmv2 -.. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/cmeeren/aacgmv2/master.svg?style=flat +.. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/aburrell/aacgmv2/master.svg?style=flat :alt: Scrutinizer Status - :target: https://scrutinizer-ci.com/g/cmeeren/aacgmv2/ + :target: https://scrutinizer-ci.com/g/aburrell/aacgmv2/ From 599304dad6af9fd7cc14cdf750d5251104b70e9f Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 15 Mar 2018 16:41:30 -0500 Subject: [PATCH 044/101] test requires Added test_requires to setup --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index 1a8c3a33..250c7080 100644 --- a/setup.py +++ b/setup.py @@ -79,6 +79,10 @@ def read(*names, **kwargs): 'numpy', 'logging', ], + test_requires=[ + 'pytest', + 'testfixtures' + ], ext_modules=[ Extension('_aacgmv2', sources=['aacgmv2/aacgmv2module.c', From 82b35b6e07deaaab2e096af859a23b6de08feb26 Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 15 Mar 2018 16:56:54 -0500 Subject: [PATCH 045/101] no tox for travis Removed tox dependency for travis --- #tox.ini# | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++ .travis.yml | 31 ++++-------- 2 files changed, 144 insertions(+), 21 deletions(-) create mode 100644 #tox.ini# diff --git a/#tox.ini# b/#tox.ini# new file mode 100644 index 00000000..f1c343ea --- /dev/null +++ b/#tox.ini# @@ -0,0 +1,134 @@ +[tox] +envlist = + clean, + check, + py{2.7,3.3,3.4,3.5,3.6}, + py{2.7,3.3,3.4,3.5,3.6}-nocover, + report, + docs + +[testenv] +setenv = + PYTHONPATH={toxinidir}/tests + PYTHONUNBUFFERED=yes +passenv = + * +deps = + pytest + testfixtures +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} + +[testenv:py{2.7,3.3,3.4,3.5,3.6}] +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes + PY_CCOV=-coverage +usedevelop = true +deps = + {[testenv]deps} + pytest-cov + numpy +commands = + python setup.py clean --all build_ext --force --inplace + {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} + +[testenv:py{2.7,3.3,3.4,3.5,3.6}-nocover] +deps = + {[testenv]deps} + numpy + +[testenv:codecov] +deps = + codecov +skip_install = true +usedevelop = false +commands = + coverage combine + coverage report + coverage xml --ignore-errors + codecov [] + +[testenv:spell] +setenv = + SPELLCHECK=1 +commands = + sphinx-build -b spelling docs dist/docs +skip_install = true +usedevelop = true +deps = + -r{toxinidir}/docs/requirements.txt + sphinxcontrib-spelling + pyenchant + +[testenv:docs] +deps = + -r{toxinidir}/docs/requirements.txt + sphinx_rtd_theme +commands = + sphinx-build {posargs:-E} -b html docs dist/docs + sphinx-build -b linkcheck docs dist/docs + +[testenv:bootstrap] +deps = + jinja2 + matrix +skip_install = true +usedevelop = false +commands = + python ci/bootstrap.py +passenv = + * + +[testenv:check] +deps = + docutils +# check-manifest + flake8 + readme + pygments +skip_install = true +usedevelop = false +commands = + python setup.py check --strict --metadata --restructuredtext +# check-manifest {toxinidir} + flake8 src tests + +[testenv:coveralls] +deps = + coveralls +skip_install = true +usedevelop = false +commands = + coverage combine + coverage report + coveralls --merge=extension-coveralls.json [] + + +[testenv:extension-coveralls] +deps = + cpp-coveralls +skip_install = true +usedevelop = false +commands = + coveralls --build-root=. --include=src --dump=extension-coveralls.json [] + +[testenv:report] +deps = coverage +skip_install = true +usedevelop = false +commands = + coverage combine + coverage report + +[testenv:clean] +commands = coverage erase +skip_install = true +usedevelop = false +deps = coverage + +[testenv:buildonly-nocover] +deps = +skip_install = true +commands = \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 338431d7..c3502159 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,40 +1,29 @@ language: python -python: '3.5' +python: + '2.7' + '3.3' + '3.4' + '3.5' + '3.6' sudo: false env: global: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - matrix: - - TOXENV=check - - TOXENV=2.7,extension-coveralls,coveralls,codecov - - TOXENV=2.7-nocover - - TOXENV=3.3,extension-coveralls,coveralls,codecov - - TOXENV=3.3-nocover - - TOXENV=3.4,extension-coveralls,coveralls,codecov - - TOXENV=3.4-nocover - - TOXENV=3.5,extension-coveralls,coveralls,codecov - - TOXENV=3.5-nocover - - TOXENV=3.6,extension-coveralls,coveralls,codecov - - TOXENV=3.6-nocover before_install: - python --version - uname -a - lsb_release -a install: - - pip install tox + - pip install coveralls - virtualenv --version - easy_install --version - pip --version - tox --version + - "python setup.py install" script: - - tox -v -after_failure: - - more .tox/log/* | cat - - more .tox/*/log/* | cat + - "coverage run --source=aacgmv2 setup.py test" notifications: email: on_success: never on_failure: always -matrix: - allow_failures: - - env: "TOXENV=check" +after_success: coveralls From 3b2faadb531f50c4cba186d1d9f25b4c54021be6 Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 15 Mar 2018 19:29:03 -0500 Subject: [PATCH 046/101] Fix travis bug Fixed bug in specifying python language versions --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index c3502159..56b521df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: python python: - '2.7' - '3.3' - '3.4' - '3.5' - '3.6' + - "2.7" + - "3.3" + - "3.4" + - "3.5" + - "3.6" sudo: false env: global: From d5271608fd4674ea06f17884cbea1ddc4888a1a2 Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 15 Mar 2018 20:50:53 -0500 Subject: [PATCH 047/101] travis update removed tox command from tox-less version --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 56b521df..61bb0ae8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,10 +15,6 @@ before_install: - lsb_release -a install: - pip install coveralls - - virtualenv --version - - easy_install --version - - pip --version - - tox --version - "python setup.py install" script: - "coverage run --source=aacgmv2 setup.py test" From 515979882f2da0d44bc7115675b035ee1a819889 Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 15 Mar 2018 23:50:22 -0500 Subject: [PATCH 048/101] Removed unwanted file Remove file that was added accidentally in a commit. --- #tox.ini# | 134 ------------------------------------------------------ 1 file changed, 134 deletions(-) delete mode 100644 #tox.ini# diff --git a/#tox.ini# b/#tox.ini# deleted file mode 100644 index f1c343ea..00000000 --- a/#tox.ini# +++ /dev/null @@ -1,134 +0,0 @@ -[tox] -envlist = - clean, - check, - py{2.7,3.3,3.4,3.5,3.6}, - py{2.7,3.3,3.4,3.5,3.6}-nocover, - report, - docs - -[testenv] -setenv = - PYTHONPATH={toxinidir}/tests - PYTHONUNBUFFERED=yes -passenv = - * -deps = - pytest - testfixtures -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} - -[testenv:py{2.7,3.3,3.4,3.5,3.6}] -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -deps = - {[testenv]deps} - pytest-cov - numpy -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} - -[testenv:py{2.7,3.3,3.4,3.5,3.6}-nocover] -deps = - {[testenv]deps} - numpy - -[testenv:codecov] -deps = - codecov -skip_install = true -usedevelop = false -commands = - coverage combine - coverage report - coverage xml --ignore-errors - codecov [] - -[testenv:spell] -setenv = - SPELLCHECK=1 -commands = - sphinx-build -b spelling docs dist/docs -skip_install = true -usedevelop = true -deps = - -r{toxinidir}/docs/requirements.txt - sphinxcontrib-spelling - pyenchant - -[testenv:docs] -deps = - -r{toxinidir}/docs/requirements.txt - sphinx_rtd_theme -commands = - sphinx-build {posargs:-E} -b html docs dist/docs - sphinx-build -b linkcheck docs dist/docs - -[testenv:bootstrap] -deps = - jinja2 - matrix -skip_install = true -usedevelop = false -commands = - python ci/bootstrap.py -passenv = - * - -[testenv:check] -deps = - docutils -# check-manifest - flake8 - readme - pygments -skip_install = true -usedevelop = false -commands = - python setup.py check --strict --metadata --restructuredtext -# check-manifest {toxinidir} - flake8 src tests - -[testenv:coveralls] -deps = - coveralls -skip_install = true -usedevelop = false -commands = - coverage combine - coverage report - coveralls --merge=extension-coveralls.json [] - - -[testenv:extension-coveralls] -deps = - cpp-coveralls -skip_install = true -usedevelop = false -commands = - coveralls --build-root=. --include=src --dump=extension-coveralls.json [] - -[testenv:report] -deps = coverage -skip_install = true -usedevelop = false -commands = - coverage combine - coverage report - -[testenv:clean] -commands = coverage erase -skip_install = true -usedevelop = false -deps = coverage - -[testenv:buildonly-nocover] -deps = -skip_install = true -commands = \ No newline at end of file From ad457150db507e71bba166c4af7624b034bac62d Mon Sep 17 00:00:00 2001 From: aburrell Date: Thu, 15 Mar 2018 23:50:46 -0500 Subject: [PATCH 049/101] Travis update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated bootstrap template for travis CI that doesn’t use tox, but works. --- ci/templates/.travis.yml | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 71bab06f..8454cddc 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -1,34 +1,25 @@ language: python -python: '3.5' +python: + - "2.7" + - "3.3" + - "3.4" + - "3.5" + - "3.6" sudo: false env: global: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - matrix: - - TOXENV=check -{% for env, config in tox_environments|dictsort %} - - TOXENV={{ env }}{% if config.cover %},extension-coveralls,coveralls,codecov{% endif %} - -{% endfor %} before_install: - python --version - uname -a - lsb_release -a install: - - pip install tox - - virtualenv --version - - easy_install --version - - pip --version - - tox --version + - pip install coveralls + - "python setup.py install" script: - - tox -v -after_failure: - - more .tox/log/* | cat - - more .tox/*/log/* | cat + - "coverage run --source=aacgmv2 setup.py test" +after_sucess: coveralls notifications: email: on_success: never - on_failure: always -matrix: - allow_failures: - - env: "TOXENV=check" + on_failure: always \ No newline at end of file From 198deda229bf0cb68b9fa484c52f91fc2d378604 Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 16 Mar 2018 00:04:13 -0500 Subject: [PATCH 050/101] updated allowed failures Updated appveyor allowed failures to reflect the lack of numpy version testing. --- appveyor.yml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index c4b3736b..663ff52d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -130,17 +130,8 @@ artifacts: - path: dist\* matrix: allow_failures: - - TOXENV: 'check' - - TOXENV: '3.3-np1.8-nocover' - - TOXENV: '3.3-np1.8,codecov' - - TOXENV: '3.4-np1.8-nocover' - - TOXENV: '3.4-np1.8,codecov' - - TOXENV: '3.5-np1.8-nocover' - - TOXENV: '3.5-np1.8,codecov' - - TOXENV: '3.5-np1.9-nocover' - - TOXENV: '3.5-np1.9,codecov' - - TOXENV: '3.5-np1.10-nocover' - - TOXENV: '3.5-np1.10,codecov' + - TOXENV: '3.5-nocover' + - TOXENV: '3.5,codecov' - TOXENV: '2.7-buildonly-nocover' - TOXENV: '3.3-buildonly-nocover' - TOXENV: '3.4-buildonly-nocover' From 854423948ee6bbd5477f7ae5630805eb4dd53d98 Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 16 Mar 2018 08:56:56 -0500 Subject: [PATCH 051/101] Add python 3.6 to appveyor Added python 3.6 to appveyor, updated allow_failures in template. --- appveyor.yml | 23 +++++++++++++++++++++++ ci/templates/appveyor.yml | 27 ++++++++++++++++----------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 663ff52d..f67f9d72 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -52,6 +52,18 @@ environment: PYTHON_HOME: C:\python35-x64 PYTHON_VERSION: '3.5' PYTHON_ARCH: '64' + - TOXENV: '3.6-buildonly-nocover' + TOXPYTHON: C:\python35\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + - TOXENV: '3.6-buildonly-nocover' + TOXPYTHON: C:\python36-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' - TOXENV: check PYTHON_HOME: C:\Python27 PYTHON_VERSION: '2.7' @@ -102,6 +114,16 @@ environment: PYTHON_HOME: C:\python35-x64 PYTHON_VERSION: '3.5' PYTHON_ARCH: '64' + - TOXENV: '3.6-nocover' + TOXPYTHON: C:\python36\python.exe + PYTHON_HOME: C:\python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + - TOXENV: '3.6-nocover' + TOXPYTHON: C:\python36-x64\python.exe + PYTHON_HOME: C:\python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' init: - ps: echo $env:TOXENV - ps: ls C:\Python* @@ -136,6 +158,7 @@ matrix: - TOXENV: '3.3-buildonly-nocover' - TOXENV: '3.4-buildonly-nocover' - TOXENV: '3.5-buildonly-nocover' + - TOXENV: '3.6-buildonly-nocover' ### To enable remote debugging uncomment this: # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/ci/templates/appveyor.yml b/ci/templates/appveyor.yml index d5e097ae..6d729434 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/appveyor.yml @@ -52,11 +52,23 @@ environment: PYTHON_HOME: C:\python35-x64 PYTHON_VERSION: '3.5' PYTHON_ARCH: '64' + - TOXENV: '3.6-buildonly-nocover' + TOXPYTHON: C:\python35\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + - TOXENV: '3.6-buildonly-nocover' + TOXPYTHON: C:\python36-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' - TOXENV: check PYTHON_HOME: C:\Python27 PYTHON_VERSION: '2.7' PYTHON_ARCH: '32' -{% for env, config in tox_environments|dictsort %}{% if config.python in ('python2.6', 'python2.7', 'python3.3', 'python3.4', 'python3.5') and not config.cover %} +{% for env, config in tox_environments|dictsort %}{% if config.python in ('python2.6', 'python2.7', 'python3.3', 'python3.4', 'python3.5', 'python3.6') and not config.cover %} - TOXENV: '{{ env }}{% if config.cover %},codecov{% endif %}' TOXPYTHON: C:\{{ config.python.replace('.', '') }}\python.exe {%- if config.python != 'python3.5' %} @@ -107,20 +119,13 @@ artifacts: matrix: allow_failures: - TOXENV: 'check' - - TOXENV: '3.3-np1.8-nocover' - - TOXENV: '3.3-np1.8,codecov' - - TOXENV: '3.4-np1.8-nocover' - - TOXENV: '3.4-np1.8,codecov' - - TOXENV: '3.5-np1.8-nocover' - - TOXENV: '3.5-np1.8,codecov' - - TOXENV: '3.5-np1.9-nocover' - - TOXENV: '3.5-np1.9,codecov' - - TOXENV: '3.5-np1.10-nocover' - - TOXENV: '3.5-np1.10,codecov' + - TOXENV: '3.5-nocover' + - TOXENV: '3.5,codecov' - TOXENV: '2.7-buildonly-nocover' - TOXENV: '3.3-buildonly-nocover' - TOXENV: '3.4-buildonly-nocover' - TOXENV: '3.5-buildonly-nocover' + - TOXENV: '3.6-buildonly-nocover' ### To enable remote debugging uncomment this: # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) From 8d0083dccdb4ec97584caa9d6cfb096cafec116a Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 16 Mar 2018 12:34:23 -0500 Subject: [PATCH 052/101] windows bugs Fixed some bugs that will help windows compilation and execution: - renamed test_requirements in setup.py to work with wheel - renamed variables in read function in setup.py to remove unsafe variable name - added 3.6-buildonly-nocover environment to tox - commented out python 2.6 in appveyor bootstrap. --- ci/appveyor-bootstrap.py | 6 +++--- setup.py | 11 +++++------ tox.ini | 6 ++++++ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/ci/appveyor-bootstrap.py b/ci/appveyor-bootstrap.py index e0d1a0ab..03d78330 100644 --- a/ci/appveyor-bootstrap.py +++ b/ci/appveyor-bootstrap.py @@ -35,9 +35,9 @@ INSTALL_CMD = { # Commands are allowed to fail only if they are not the last command. # E.g.: uninstall (/x) allowed to fail. - "2.6": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], - ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", - "TARGETDIR={home}"]], + # "2.6": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], + # ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", + # "TARGETDIR={home}"]], "2.7": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], diff --git a/setup.py b/setup.py index 250c7080..040ff8b1 100644 --- a/setup.py +++ b/setup.py @@ -12,9 +12,9 @@ from setuptools import setup from distutils.core import Extension -def read(*names, **kwargs): +def read(fname): return io.open( - path.join(path.dirname(__file__), *names), + path.join(path.dirname(__file__), fname), encoding=kwargs.get('encoding', 'utf8') ).read() @@ -79,10 +79,9 @@ def read(*names, **kwargs): 'numpy', 'logging', ], - test_requires=[ - 'pytest', - 'testfixtures' - ], + extra_requires={'test':['pytest', + 'testfixtures'], + }, ext_modules=[ Extension('_aacgmv2', sources=['aacgmv2/aacgmv2module.c', diff --git a/tox.ini b/tox.ini index 9f460f1f..efdedcc3 100644 --- a/tox.ini +++ b/tox.ini @@ -230,4 +230,10 @@ deps = skip_install = true commands = +[testenv:3.6-buildonly-nocover] +basepython = {env:TOXPYTHON:python3.6} +deps = +skip_install = true +commands = + From 6fd60ab77c65d5c51bbbdf483b07c80c2f439fc8 Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 16 Mar 2018 12:49:12 -0500 Subject: [PATCH 053/101] setup bug re-added missing kwargs to read function --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 040ff8b1..325ec0da 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ from setuptools import setup from distutils.core import Extension -def read(fname): +def read(fname, **kwargs): return io.open( path.join(path.dirname(__file__), fname), encoding=kwargs.get('encoding', 'utf8') From 4b8f940a342ace907fd7490da47e9a8bc68217de Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 16 Mar 2018 13:53:56 -0500 Subject: [PATCH 054/101] contributing Updated contributing documentation to point to aburrell --- CONTRIBUTING.rst | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index b16b8cb1..a3a231db 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -2,18 +2,21 @@ Contributing ============ -Bug reports, feature suggestions and other contributions are greatly appreciated! While I can't promise to implement everything, I will always try to respond in a timely manner. +Bug reports, feature suggestions and other contributions are greatly +appreciated! While I can't promise to implement everything, I will always try +to respond in a timely manner. Short version ============= -* Submit bug reports and feature requests at `GitHub `_ +* Submit bug reports and feature requests at `GitHub `_ * Make pull requests to the ``develop`` branch Bug reports =========== -When `reporting a bug `_ please include: +When `reporting a bug `_ please +include: * Your operating system name and version * Any details about your local setup that might be helpful in troubleshooting @@ -22,20 +25,22 @@ When `reporting a bug `_ please inclu Feature requests and feedback ============================= -The best way to send feedback is to file an issue at `GitHub `_. +The best way to send feedback is to file an issue at +`GitHub `_. If you are proposing a feature: * Explain in detail how it would work. * Keep the scope as narrow as possible, to make it easier to implement. -* Remember that this is a volunteer-driven project, and that code contributions are welcome :) +* Remember that this is a volunteer-driven project, and that code contributions + are welcome :) Development =========== To set up `aacgmv2` for local development: -1. `Fork aacgmv2 on GitHub `_. +1. `Fork aacgmv2 on GitHub `_. 2. Clone your fork locally:: git clone git@github.com:your_name_here/aacgmv2.git @@ -44,9 +49,16 @@ To set up `aacgmv2` for local development: git checkout -b name-of-your-bugfix-or-feature - Now you can make your changes locally. Add tests for bugs and new features in ``tests/test_py_aacgmv2.py`` (for the wrapper), ``test_c_aacgmv2.py`` (for the C extension), or ``tests/test_cmd_aacgmv2.py`` (for the command-line interface). The tests are run with ``py.test`` and can be written as normal functions (starting with ``test_``) containing a standard ``assert`` statement for testing output. + Now you can make your changes locally. Add tests for bugs and new features + in ``tests/test_py_aacgmv2.py`` (for the wrapper), ``test_c_aacgmv2.py`` + (for the C extension), or ``tests/test_cmd_aacgmv2.py`` (for the + command-line interface). ``tests/test_dep_aacgmv2.py`` includes tests for + deprecated functions. The tests are run with ``py.test`` and can be + written as normal functions (starting with ``test_``) containing a standard + ``assert`` statement for testing output, or use the numpy testing suite. -4. When you're done making changes, run all the checks, doc builder and spell checker with `tox `_ [1]_:: +4. When you're done making changes, run all the checks, doc builder and spell + checker with `tox `_ [1]_:: tox @@ -56,12 +68,14 @@ To set up `aacgmv2` for local development: git commit -m "Brief description of your changes" git push origin name-of-your-bugfix-or-feature -6. Submit a pull request through the GitHub website. Pull requests should be made to the ``develop`` branch. +6. Submit a pull request through the GitHub website. Pull requests should be + made to the ``develop`` branch. Pull Request Guidelines ----------------------- -If you need some code review or feedback while you're developing the code, just make a pull request. +If you need some code review or feedback while you're developing the code, just +make a pull request. For merging, you should: @@ -70,9 +84,10 @@ For merging, you should: 3. Add a note to ``CHANGELOG.rst`` about the changes 4. Add yourself to ``AUTHORS.rst`` -.. [1] If you don't have all the necessary Python versions available locally or have trouble - building NumPy in all the testing environments, you can rely on Travis and - AppVeyor - they will run the tests for each change you add in the pull request. +.. [1] If you don't have all the necessary Python versions available locally or + have trouble building all the testing environments, you can rely on + Travis and AppVeyor - they will run the tests for each change you add in + the pull request. Tips ---- From 5c50a75eb3f336e492ad342f691d6596e98cf091 Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 16 Mar 2018 13:54:10 -0500 Subject: [PATCH 055/101] extras require Fixed typo in extras_require --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 325ec0da..9c372386 100644 --- a/setup.py +++ b/setup.py @@ -79,7 +79,7 @@ def read(fname, **kwargs): 'numpy', 'logging', ], - extra_requires={'test':['pytest', + extras_require={'test':['pytest', 'testfixtures'], }, ext_modules=[ From 91eaaccad5ac950c99a993dbfb9762fe9ac89831 Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 16 Mar 2018 14:22:42 -0500 Subject: [PATCH 056/101] math c in windows Added definition for pi in astalg.h, as this is not included in math.h by default on windows. --- c_aacgmv2/include/astalg.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/c_aacgmv2/include/astalg.h b/c_aacgmv2/include/astalg.h index d232a7d9..bc7f54af 100644 --- a/c_aacgmv2/include/astalg.h +++ b/c_aacgmv2/include/astalg.h @@ -64,6 +64,10 @@ The book will be referred to as "Meeus" for short. #include +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + #define AstAlg_DTOR (M_PI/180.0) /* we need a floating point version of the % binary operator */ From 6d25300c903012d6e62370418205178871ab86cf Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 16 Mar 2018 15:33:50 -0500 Subject: [PATCH 057/101] possible windows workaround Possible windows workaround for isfinite. --- c_aacgmv2/src/mlt_v2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/c_aacgmv2/src/mlt_v2.c b/c_aacgmv2/src/mlt_v2.c index 9def0c79..e4aa2bed 100644 --- a/c_aacgmv2/src/mlt_v2.c +++ b/c_aacgmv2/src/mlt_v2.c @@ -6,6 +6,10 @@ #include "rtime.h" #include "astalg.h" +#ifndef isfinite +#include +#endif + /*----------------------------------------------------------------------------- MLT functions for use with AACGM-v2 ; From 446da4cf6873af37fdedf16d7c6d9b5ee9f0c2de Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 16 Mar 2018 16:04:52 -0500 Subject: [PATCH 058/101] isfinite macro Defined macro for isfinite, if this is not included in math.h --- c_aacgmv2/src/mlt_v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c_aacgmv2/src/mlt_v2.c b/c_aacgmv2/src/mlt_v2.c index e4aa2bed..a6a32837 100644 --- a/c_aacgmv2/src/mlt_v2.c +++ b/c_aacgmv2/src/mlt_v2.c @@ -7,7 +7,7 @@ #include "astalg.h" #ifndef isfinite -#include +#define isfinite(fpclassify(x) != FP_NAN && fpclassify(x) != FP_INFINITE) #endif /*----------------------------------------------------------------------------- From 20f563497afb6c8db6ebcfb95884839dbe9ab61f Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 16 Mar 2018 16:11:47 -0500 Subject: [PATCH 059/101] removed logging module Removed old logging module and replaced with logbook. --- aacgmv2/__init__.py | 2 +- aacgmv2/depricated.py | 2 +- aacgmv2/wrapper.py | 2 +- setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aacgmv2/__init__.py b/aacgmv2/__init__.py index e16574b1..8f7eae4c 100644 --- a/aacgmv2/__init__.py +++ b/aacgmv2/__init__.py @@ -44,7 +44,7 @@ --------------------------------------------------------------------------- """ import os.path as _path -import logging +import logbook as logging __version__ = "2.0.1" # path and filename prefix for the IGRF coefficients diff --git a/aacgmv2/depricated.py b/aacgmv2/depricated.py index e1d40986..3a762cd7 100644 --- a/aacgmv2/depricated.py +++ b/aacgmv2/depricated.py @@ -20,7 +20,7 @@ from __future__ import division, absolute_import, unicode_literals import numpy as np -import logging +import logbook as logging import aacgmv2 def convert(lat, lon, alt, date=None, a2g=False, trace=False, allowtrace=False, diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index a478840c..5f4f4715 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -16,7 +16,7 @@ from __future__ import division, absolute_import, unicode_literals import numpy as np import datetime as dt -import logging +import logbook as logging import aacgmv2 import aacgmv2._aacgmv2 as c_aacgmv2 diff --git a/setup.py b/setup.py index 9c372386..6790e6a9 100644 --- a/setup.py +++ b/setup.py @@ -77,7 +77,7 @@ def read(fname, **kwargs): ], install_requires=[ 'numpy', - 'logging', + 'logbook', ], extras_require={'test':['pytest', 'testfixtures'], From 2be13fa1c6f77437c7b82c20cb4be6cc9945200f Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 16 Mar 2018 17:33:25 -0500 Subject: [PATCH 060/101] fixed macro bug fixed bug in macro --- c_aacgmv2/src/mlt_v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c_aacgmv2/src/mlt_v2.c b/c_aacgmv2/src/mlt_v2.c index a6a32837..0a07a52a 100644 --- a/c_aacgmv2/src/mlt_v2.c +++ b/c_aacgmv2/src/mlt_v2.c @@ -7,7 +7,7 @@ #include "astalg.h" #ifndef isfinite -#define isfinite(fpclassify(x) != FP_NAN && fpclassify(x) != FP_INFINITE) +#define isfinite fpclassify(x) != FP_NAN && fpclassify(x) != FP_INFINITE #endif /*----------------------------------------------------------------------------- From d1ad65f32067b05524241450812963b1eabaf0a8 Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 10:06:33 -0500 Subject: [PATCH 061/101] logging reference Removed old reference to deprecated module --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index efdedcc3..96ed14da 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ passenv = deps = pytest testfixtures - logging + logbook numpy commands = python setup.py clean --all build_ext --force --inplace From 45f264e18c5cb6cdc834d99a62a69b8a52378b2d Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 10:06:53 -0500 Subject: [PATCH 062/101] read function Cleaned up read function to be easier to read --- setup.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 6790e6a9..75073d04 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# -*- encoding: utf-8 -*- +#-*- encoding: utf-8 -*- from __future__ import absolute_import import io @@ -13,10 +13,8 @@ from distutils.core import Extension def read(fname, **kwargs): - return io.open( - path.join(path.dirname(__file__), fname), - encoding=kwargs.get('encoding', 'utf8') - ).read() + return io.open(path.join(path.dirname(__file__), fname), + encoding=kwargs.get('encoding', 'utf8')).read() # enable code coverage for C code # We can't use CFLAGS=-coverage in tox.ini, since that may mess with From dd3525263e65ce16d291698f159a7382db402428 Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 10:07:26 -0500 Subject: [PATCH 063/101] isfinite Updated isfinite to not use the floating point macros that are not available in windows. --- c_aacgmv2/src/mlt_v2.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/c_aacgmv2/src/mlt_v2.c b/c_aacgmv2/src/mlt_v2.c index 0a07a52a..2866b3b4 100644 --- a/c_aacgmv2/src/mlt_v2.c +++ b/c_aacgmv2/src/mlt_v2.c @@ -6,8 +6,16 @@ #include "rtime.h" #include "astalg.h" +#ifndef NAN +#define NAN sqrt(-1) +#endif + +#ifndef INFINITE +#define INFINITE 1/0 +#endif + #ifndef isfinite -#define isfinite fpclassify(x) != FP_NAN && fpclassify(x) != FP_INFINITE +#define isfinite 1 ? float(x) != NAN && float(x) != INFINITE : 0 #endif /*----------------------------------------------------------------------------- From 0755e918bb7c45062a463c39cc3f2312763ba11b Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 10:37:33 -0500 Subject: [PATCH 064/101] removed infinite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit System doesn’t handle definition of infinite very well. --- c_aacgmv2/src/mlt_v2.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/c_aacgmv2/src/mlt_v2.c b/c_aacgmv2/src/mlt_v2.c index 2866b3b4..0bc8a6bc 100644 --- a/c_aacgmv2/src/mlt_v2.c +++ b/c_aacgmv2/src/mlt_v2.c @@ -10,12 +10,9 @@ #define NAN sqrt(-1) #endif -#ifndef INFINITE -#define INFINITE 1/0 -#endif - +/* Macro infinite cannot be defined by systems that don't have it */ #ifndef isfinite -#define isfinite 1 ? float(x) != NAN && float(x) != INFINITE : 0 +#define isfinite 1 ? x != NAN : 0 #endif /*----------------------------------------------------------------------------- @@ -133,8 +130,8 @@ double MLTConvert_v2(int yr, int mo, int dy, int hr, int mt ,int sc, return (NAN); } - hgt = 700.; /* AACGM-v2 coefficients are defined everywhere above this - * altitude. */ + hgt = 700.0; /* AACGM-v2 coefficients are defined everywhere above this + * altitude. */ if (mlt_date.yr != yr || mlt_date.mo != mo || mlt_date.dy != dy || mlt_date.hr != hr || mlt_date.mt != mt || mlt_date.sc != sc) { @@ -178,12 +175,12 @@ double MLTConvert_v2(int yr, int mo, int dy, int hr, int mt ,int sc, } /*printf("** %lf\n", mlon_ref);*/ - aacgm_mlt = 12. + (mlon - mlon_ref)/15.; /* MLT based on subsolar point */ + aacgm_mlt = 12.0 + (mlon - mlon_ref)/15.0; /* MLT based on subsolar point */ /* comparision with IDL version is exact */ - while (aacgm_mlt > 24.) aacgm_mlt -= 24.; - while (aacgm_mlt < 0.) aacgm_mlt += 24.; + while (aacgm_mlt > 24.0) aacgm_mlt -= 24.0; + while (aacgm_mlt < 0.0) aacgm_mlt += 24.0; return (aacgm_mlt); } From d3bf6afc84e6995f4e177bcc3902937206c9b73b Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 11:41:26 -0500 Subject: [PATCH 065/101] MLT fix Fixed definition of a macro function --- c_aacgmv2/src/mlt_v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c_aacgmv2/src/mlt_v2.c b/c_aacgmv2/src/mlt_v2.c index 0bc8a6bc..4a4c083e 100644 --- a/c_aacgmv2/src/mlt_v2.c +++ b/c_aacgmv2/src/mlt_v2.c @@ -11,7 +11,7 @@ #endif /* Macro infinite cannot be defined by systems that don't have it */ -#ifndef isfinite +#ifndef isfinite(x) #define isfinite 1 ? x != NAN : 0 #endif From 3aab6557b4c23244270f81cb69dbf1e0e24c39d6 Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 14:06:45 -0500 Subject: [PATCH 066/101] defined windows env functions Defined windows versions of setenv, unsetenv. --- c_aacgmv2/src/rtime.c | 57 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/c_aacgmv2/src/rtime.c b/c_aacgmv2/src/rtime.c index 9745023a..f811c649 100644 --- a/c_aacgmv2/src/rtime.c +++ b/c_aacgmv2/src/rtime.c @@ -14,10 +14,65 @@ #include #include #include +#include #include "rtime.h" #define DAY_SEC 86400 +/* For windows, define setenv */ +#if defined(_WIN32) || defined(_WIN64) +int setenv(const char *name, const char *value, int overwrite) +{ + int setsize; + char envset[1000], *testenv; + + if(!overwrite) + { + testenv = getenv(name); + setsize = strlen(testenv); + if(setsize > 0) return -1; + } + + sprintf(envset, "%s=%s", name, value); + return putenv(envset); +} +#endif + +/* For windows, define unsetenv */ +#if defined(_WIN32) || defined(_WIN64) +int unsetenv(const char *name) +{ + extern char **environ; + char **ep, **sp; + size_t len; + + if (name == NULL || name[0] == '\0' || strchr(name, '=') != NULL) + { + errno = EINVAL; + return -1; + } + + len = strlen(name); + + for (ep = environ; *ep != NULL; ) + { + if (strncmp(*ep, name, len) == 0 && (*ep)[len] == '=') + { + /* Remove found entry by shifting all successive entries */ + /* back one element */ + for (sp = ep; *sp != NULL; sp++) + *sp = *(sp + 1); + + /* Continue around the loop to further instances of 'name' */ + + } + else ep++; + } + + return 0; +} +#endif + int TimeYMDHMSToYrsec(int yr,int mo,int dy,int hr,int mn,int sc) { time_t clock; @@ -112,7 +167,7 @@ double TimeYMDHMSToEpoch(int yr,int mo,int dy,int hr,int mn,double sc) { if (tz) setenv("TZ", tz, 1); else unsetenv("TZ"); tzset(); - + return clock+(sc-floor(sc)); } From d207a5499b11fc903a9bd38d464cc6dd649ab95d Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 14:07:19 -0500 Subject: [PATCH 067/101] windows if Used windows flags to trigger setting isfinite locally --- c_aacgmv2/src/mlt_v2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/c_aacgmv2/src/mlt_v2.c b/c_aacgmv2/src/mlt_v2.c index 4a4c083e..9f3da9c5 100644 --- a/c_aacgmv2/src/mlt_v2.c +++ b/c_aacgmv2/src/mlt_v2.c @@ -10,9 +10,9 @@ #define NAN sqrt(-1) #endif -/* Macro infinite cannot be defined by systems that don't have it */ -#ifndef isfinite(x) -#define isfinite 1 ? x != NAN : 0 +/* Macro infinite can't be defined, limit to NAN */ +#if defined(_WIN32) || defined(_WIN64) +#define isfinite(x) 1 ? x != NAN : 0 #endif /*----------------------------------------------------------------------------- From 710637750576191bc60a7684e86c68703c8fbbb5 Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 15:07:54 -0500 Subject: [PATCH 068/101] removed testfixtures Removed deprecated module testfixtures --- aacgmv2/tests/test_dep_aacgmv2.py | 16 ++++----- aacgmv2/tests/test_py_aacgmv2.py | 56 +++++++++++++++---------------- ci/templates/tox.ini | 2 -- setup.py | 3 +- tox.ini | 1 - 5 files changed, 36 insertions(+), 42 deletions(-) diff --git a/aacgmv2/tests/test_dep_aacgmv2.py b/aacgmv2/tests/test_dep_aacgmv2.py index 356e3d02..f9354fa8 100644 --- a/aacgmv2/tests/test_dep_aacgmv2.py +++ b/aacgmv2/tests/test_dep_aacgmv2.py @@ -28,14 +28,14 @@ def test_module_structure(self): def test_set_coeff_path(self): """Test the depricated routine for appropriate warning""" - import testfixtures + import logbook lwarn = u"this routine is no longer needed" - with testfixtures.LogCapture() as l: + with logbook.TestHandler() as handler: aacgmv2.set_coeff_path() + assert handler.has_warning(lwarn) - assert l.check(("root", "WARNING", lwarn)) is None - l.uninstall() + handler.close() def test_convert_single_val(self): """Test conversion for a single value""" @@ -148,14 +148,14 @@ def test_convert_datetime_date(self): def test_warning_below_ground_convert(self): """ Test that a warning is issued if altitude is below zero""" - import testfixtures + import logbook lwarn = u"conversion not intended for altitudes < 0 km" - with testfixtures.LogCapture() as l: + with logbook.TestHandler() as handler: lat, lon = aacgmv2.convert([60], [0], [-1], self.dtime) + assert handler.has_warning(lwarn) - assert l.check(("root", "WARNING", lwarn)) is None - l.uninstall() + handler.close() def test_convert_maxalt_failure(self): """For an array, test failure for an altitude too high for diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index f53ec45f..dc716159 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -221,26 +221,26 @@ def test_convert_latlon_arr_datetime_date(self): def test_warning_below_ground_convert_latlon(self): """ Test that a warning is issued if altitude is below zero""" - import testfixtures + import logbook lwarn = u"conversion not intended for altitudes < 0 km" - with testfixtures.LogCapture() as l: + with logbook.TestHandler() as handler: lat, lon, r = aacgmv2.convert_latlon(60, 0, -1, self.dtime) + assert handler.has_warning(lwarn) - assert l.check(("root", "WARNING", lwarn)) is None - l.uninstall() + handler.close() def test_warning_below_ground_convert_latlon_arr(self): """ Test that a warning is issued if altitude is below zero""" - import testfixtures + import logbook lwarn = u"conversion not intended for altitudes < 0 km" - with testfixtures.LogCapture() as l: + with logbook.TestHandler() as handler: lat, lon, r = aacgmv2.convert_latlon_arr([60], [0], [-1], self.dtime) + assert handler.has_warning(lwarn) - assert l.check(("root", "WARNING", lwarn)) is None - l.uninstall() + handler.close() def test_convert_latlon_maxalt_failure(self): """For a single value, test failure for an altitude too high for @@ -463,26 +463,26 @@ def test_get_aacgm_coord_arr_datetime_date(self): def test_warning_below_ground_get_aacgm_coord(self): """ Test that a warning is issued if altitude is below zero""" - import testfixtures + import logbook lwarn = u"conversion not intended for altitudes < 0 km" - with testfixtures.LogCapture() as l: + with logbook.TestHandler() as handler: mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 0, -1, self.dtime) + assert handler.has_warning(lwarn) - assert l.check(("root", "WARNING", lwarn)) is None - l.uninstall() + handler.close() def test_warning_below_ground_get_aacgm_coord_arr(self): """ Test that a warning is issued if altitude is below zero""" - import testfixtures + import logbook lwarn = u"conversion not intended for altitudes < 0 km" - with testfixtures.LogCapture() as l: + with logbook.TestHandler() as handler: mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60], [0], [-1], self.dtime) + assert handler.has_warning(lwarn) - assert l.check(("root", "WARNING", lwarn)) is None - l.uninstall() + handler.close() def test_get_aacgm_coord_maxalt_failure(self): """For a single value, test failure for an altitude too high for @@ -502,37 +502,35 @@ def test_get_aacgm_coord_arr_maxalt_failure(self): def test_get_aacgm_coord_mlat_failure(self): """Test error return for co-latitudes above 90 for a single value""" - import testfixtures + import logbook lerr = u"unrealistic latitude" - with testfixtures.LogCapture() as lhigh: + with logbook.TestHandler() as hhigh: with pytest.raises(AssertionError): mlat, mlon, mlt = aacgmv2.get_aacgm_coord(91, 0, 300, self.dtime) + assert hhigh.has_error(lerr) - assert lhigh.check(("root", "ERROR", lerr)) is None - - with testfixtures.LogCapture() as llow: + with logbook.TestHandler() as hlow: with pytest.raises(AssertionError): mlat, mlon, mlt = aacgmv2.get_aacgm_coord(-91, 0, 300, self.dtime) + assert hlow.has_error(lerr) - assert llow.check(("root", "ERROR", lerr)) is None - - lhigh.uninstall() - llow.uninstall() + hhigh.close() + hlow.close() def test_get_aacgm_coord_arr_mlat_failure(self): """Test error return for co-latitudes above 90 for an array""" - import testfixtures + import logbook lerr = u"unrealistic latitude" - with testfixtures.LogCapture() as l: + with logbook.TestHandler() as handler: with pytest.raises(AssertionError): aacgmv2.get_aacgm_coord_arr([91, 60, -91], 0, 300, self.dtime) + assert handler.has_error(lerr) - assert l.check(("root", "ERROR", lerr)) is None - l.uninstall() + handler.close() def test_convert_str_to_bit_g2a(self): """Test conversion from string code to bit G2A""" diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index 194a7dca..b65048b7 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -16,8 +16,6 @@ passenv = * deps = pytest - testfixtures - logging numpy commands = python setup.py clean --all build_ext --force --inplace diff --git a/setup.py b/setup.py index 75073d04..84b6c328 100644 --- a/setup.py +++ b/setup.py @@ -77,8 +77,7 @@ def read(fname, **kwargs): 'numpy', 'logbook', ], - extras_require={'test':['pytest', - 'testfixtures'], + extras_require={'test':['pytest'], }, ext_modules=[ Extension('_aacgmv2', diff --git a/tox.ini b/tox.ini index 96ed14da..d7ea7a0c 100644 --- a/tox.ini +++ b/tox.ini @@ -23,7 +23,6 @@ passenv = * deps = pytest - testfixtures logbook numpy commands = From 00f1a72d0a97fc5682ea4592450d95ad0e8731ae Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 15:31:27 -0500 Subject: [PATCH 069/101] path problems Fixed unix/mac/linux specific path structure --- aacgmv2/tests/test_py_aacgmv2.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index dc716159..f7642279 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -31,11 +31,14 @@ def test_module_structure(self): def test_module_parameters(self): """Test module constants""" - path1 = "aacgmv2/aacgmv2/aacgm_coeffs/aacgm_coeffs-12-" - arg1 = aacgmv2.AACGM_v2_DAT_PREFIX.find(path1) >= 0 + from os import path - path2 = "aacgmv2/aacgmv2/igrf12coeffs.txt" - arg2 = aacgmv2.IGRF_12_COEFFS.find(path2) >= 0 + path1 = path.join("aacgmv2", "aacgmv2", "aacgm_coeffs", + "aacgm_coeffs-12-") + assert aacgmv2.AACGM_v2_DAT_PREFIX.find(path1) >= 0 + + path2 = path.join("aacgmv2", "aacgmv2", "igrf12coeffs.txt") + assert aacgmv2.IGRF_12_COEFFS.find(path2) >= 0 assert arg1 & arg2 From 007b942326cb5b5f3bf434145cb263c0349111cf Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 15:37:20 -0500 Subject: [PATCH 070/101] Removed testing for python 3.3 Dependencies no longer support python 3.3, stop testing for this version. --- .travis.yml | 1 - appveyor.yml | 25 ------------------------- ci/appveyor-bootstrap.py | 6 ------ ci/templates/.travis.yml | 1 - ci/templates/appveyor.yml | 15 +-------------- ci/templates/tox.ini | 6 ------ tox.ini | 25 ------------------------- 7 files changed, 1 insertion(+), 78 deletions(-) diff --git a/.travis.yml b/.travis.yml index 61bb0ae8..0d6554d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: python python: - "2.7" - - "3.3" - "3.4" - "3.5" - "3.6" diff --git a/appveyor.yml b/appveyor.yml index f67f9d72..6e3ff721 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,18 +16,6 @@ environment: PYTHON_HOME: C:\python27-x64 PYTHON_VERSION: '2.7' PYTHON_ARCH: '64' - - TOXENV: '3.3-buildonly-nocover' - TOXPYTHON: C:\python33\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python33 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '32' - - TOXENV: '3.3-buildonly-nocover' - TOXPYTHON: C:\python33-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python33-x64 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '64' - TOXENV: '3.4-buildonly-nocover' TOXPYTHON: C:\python34\python.exe WINDOWS_SDK_VERSION: v7.1 @@ -80,18 +68,6 @@ environment: PYTHON_HOME: C:\python27-x64 PYTHON_VERSION: '2.7' PYTHON_ARCH: '64' - - TOXENV: '3.3-nocover' - TOXPYTHON: C:\python33\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python33 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '32' - - TOXENV: '3.3-nocover' - TOXPYTHON: C:\python33-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python33-x64 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '64' - TOXENV: '3.4-nocover' TOXPYTHON: C:\python34\python.exe WINDOWS_SDK_VERSION: v7.0 @@ -155,7 +131,6 @@ matrix: - TOXENV: '3.5-nocover' - TOXENV: '3.5,codecov' - TOXENV: '2.7-buildonly-nocover' - - TOXENV: '3.3-buildonly-nocover' - TOXENV: '3.4-buildonly-nocover' - TOXENV: '3.5-buildonly-nocover' - TOXENV: '3.6-buildonly-nocover' diff --git a/ci/appveyor-bootstrap.py b/ci/appveyor-bootstrap.py index 03d78330..9cd1b8c1 100644 --- a/ci/appveyor-bootstrap.py +++ b/ci/appveyor-bootstrap.py @@ -22,9 +22,6 @@ URLS = { ("2.7", "64"): BASE_URL + "2.7.14/python-2.7.14.amd64.msi", ("2.7", "32"): BASE_URL + "2.7.14/python-2.7.14.msi", - # NOTE: IF FAILS, USE 3.3.3 - ("3.3", "64"): BASE_URL + "3.3.7/python-3.3.7.amd64.msi", - ("3.3", "32"): BASE_URL + "3.3.7/python-3.3.7.msi", ("3.4", "64"): BASE_URL + "3.4.8/python-3.4.8.amd64.msi", ("3.4", "32"): BASE_URL + "3.4.8/python-3.4.8.msi", ("3.5", "64"): BASE_URL + "3.5.5/python-3.5.5-amd64.exe", @@ -41,9 +38,6 @@ "2.7": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], - "3.3": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], - ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", - "TARGETDIR={home}"]], "3.4": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 8454cddc..3be799b4 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -1,7 +1,6 @@ language: python python: - "2.7" - - "3.3" - "3.4" - "3.5" - "3.6" diff --git a/ci/templates/appveyor.yml b/ci/templates/appveyor.yml index 6d729434..d06b8676 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/appveyor.yml @@ -16,18 +16,6 @@ environment: PYTHON_HOME: C:\python27-x64 PYTHON_VERSION: '2.7' PYTHON_ARCH: '64' - - TOXENV: '3.3-buildonly-nocover' - TOXPYTHON: C:\python33\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python33 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '32' - - TOXENV: '3.3-buildonly-nocover' - TOXPYTHON: C:\python33-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\python33-x64 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '64' - TOXENV: '3.4-buildonly-nocover' TOXPYTHON: C:\python34\python.exe WINDOWS_SDK_VERSION: v7.1 @@ -68,7 +56,7 @@ environment: PYTHON_HOME: C:\Python27 PYTHON_VERSION: '2.7' PYTHON_ARCH: '32' -{% for env, config in tox_environments|dictsort %}{% if config.python in ('python2.6', 'python2.7', 'python3.3', 'python3.4', 'python3.5', 'python3.6') and not config.cover %} +{% for env, config in tox_environments|dictsort %}{% if config.python in ('python2.6', 'python2.7', 'python3.4', 'python3.5', 'python3.6') and not config.cover %} - TOXENV: '{{ env }}{% if config.cover %},codecov{% endif %}' TOXPYTHON: C:\{{ config.python.replace('.', '') }}\python.exe {%- if config.python != 'python3.5' %} @@ -122,7 +110,6 @@ matrix: - TOXENV: '3.5-nocover' - TOXENV: '3.5,codecov' - TOXENV: '2.7-buildonly-nocover' - - TOXENV: '3.3-buildonly-nocover' - TOXENV: '3.4-buildonly-nocover' - TOXENV: '3.5-buildonly-nocover' - TOXENV: '3.6-buildonly-nocover' diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index b65048b7..74d75977 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -148,12 +148,6 @@ deps = skip_install = true commands = -[testenv:3.3-buildonly-nocover] -basepython = {env:TOXPYTHON:python3.3} -deps = -skip_install = true -commands = - [testenv:3.4-buildonly-nocover] basepython = {env:TOXPYTHON:python3.4} deps = diff --git a/tox.ini b/tox.ini index d7ea7a0c..e039ae73 100644 --- a/tox.ini +++ b/tox.ini @@ -4,8 +4,6 @@ envlist = check, 2.7, 2.7-nocover, - 3.3, - 3.3-nocover, 3.4, 3.4-nocover, 3.5, @@ -136,23 +134,6 @@ deps = [testenv:2.7-nocover] basepython = {env:TOXPYTHON:python2.7} -[testenv:3.3] -basepython = {env:TOXPYTHON:python3.3} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes - PY_CCOV=-coverage -usedevelop = true -commands = - python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} -deps = - {[testenv]deps} - pytest-cov - -[testenv:3.3-nocover] -basepython = {env:TOXPYTHON:python3.3} - [testenv:3.4] basepython = {env:TOXPYTHON:python3.4} setenv = @@ -211,12 +192,6 @@ deps = skip_install = true commands = -[testenv:3.3-buildonly-nocover] -basepython = {env:TOXPYTHON:python3.3} -deps = -skip_install = true -commands = - [testenv:3.4-buildonly-nocover] basepython = {env:TOXPYTHON:python3.4} deps = From 02b901e0191875acb56803c2bc5933cdcd3e2b72 Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 17:10:06 -0500 Subject: [PATCH 071/101] Remove unused assert Remove unused assert in test_py_aacgmv2.py --- aacgmv2/tests/test_py_aacgmv2.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index f7642279..6f4d4033 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -40,8 +40,6 @@ def test_module_parameters(self): path2 = path.join("aacgmv2", "aacgmv2", "igrf12coeffs.txt") assert aacgmv2.IGRF_12_COEFFS.find(path2) >= 0 - assert arg1 & arg2 - def test_convert_latlon(self): """Test single value latlon conversion""" lat, lon, r = aacgmv2.convert_latlon(60, 0, 300, self.dtime) From 485c6fb73bc6a2dce1f297bd40e31db5c1600683 Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 17:23:33 -0500 Subject: [PATCH 072/101] tox back in travis Putting tox back into travis, but hopefully better. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0d6554d2..cad54914 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,8 +15,8 @@ before_install: install: - pip install coveralls - "python setup.py install" -script: - - "coverage run --source=aacgmv2 setup.py test" + - pip install tox-travis +script: tox notifications: email: on_success: never From ef9676d51832469e4c52f50f751ec29649304034 Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 17:57:07 -0500 Subject: [PATCH 073/101] Updated bootstrap Updated bootstrap templates to create acceptable .travis.yml, appveyor.yml, and tox.ini files. --- .travis.yml | 4 ++-- appveyor.yml | 3 +++ ci/templates/.travis.yml | 4 ++-- ci/templates/tox.ini | 1 + setup.cfg | 1 - tox.ini | 6 ------ 6 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index cad54914..4710203b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,8 +17,8 @@ install: - "python setup.py install" - pip install tox-travis script: tox +after_sucess: coveralls notifications: email: on_success: never - on_failure: always -after_success: coveralls + on_failure: always \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 6e3ff721..49498bef 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -92,11 +92,13 @@ environment: PYTHON_ARCH: '64' - TOXENV: '3.6-nocover' TOXPYTHON: C:\python36\python.exe + WINDOWS_SDK_VERSION: v7.0 PYTHON_HOME: C:\python36 PYTHON_VERSION: '3.6' PYTHON_ARCH: '32' - TOXENV: '3.6-nocover' TOXPYTHON: C:\python36-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 PYTHON_HOME: C:\python36-x64 PYTHON_VERSION: '3.6' PYTHON_ARCH: '64' @@ -128,6 +130,7 @@ artifacts: - path: dist\* matrix: allow_failures: + - TOXENV: 'check' - TOXENV: '3.5-nocover' - TOXENV: '3.5,codecov' - TOXENV: '2.7-buildonly-nocover' diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 3be799b4..4710203b 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -15,8 +15,8 @@ before_install: install: - pip install coveralls - "python setup.py install" -script: - - "coverage run --source=aacgmv2 setup.py test" + - pip install tox-travis +script: tox after_sucess: coveralls notifications: email: diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index 74d75977..08636290 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -16,6 +16,7 @@ passenv = * deps = pytest + logbook numpy commands = python setup.py clean --all build_ext --force --inplace diff --git a/setup.cfg b/setup.cfg index e16224a1..7789f1ab 100644 --- a/setup.cfg +++ b/setup.cfg @@ -60,7 +60,6 @@ multi_line_output=0 python_versions = 2.7 - 3.3 3.4 3.5 3.6 diff --git a/tox.ini b/tox.ini index e039ae73..df0e8b02 100644 --- a/tox.ini +++ b/tox.ini @@ -204,10 +204,4 @@ deps = skip_install = true commands = -[testenv:3.6-buildonly-nocover] -basepython = {env:TOXPYTHON:python3.6} -deps = -skip_install = true -commands = - From a53ef1bbd4afcb9c2ef71ca56f3877e9d787389a Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 18:57:12 -0500 Subject: [PATCH 074/101] 3.6 missing from tox Fixed error that omitted 3.6 from tox --- appveyor.yml | 6 +++--- ci/templates/appveyor.yml | 6 +++--- ci/templates/tox.ini | 6 ++++++ tox.ini | 6 ++++++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 49498bef..3aeac6da 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -41,7 +41,7 @@ environment: PYTHON_VERSION: '3.5' PYTHON_ARCH: '64' - TOXENV: '3.6-buildonly-nocover' - TOXPYTHON: C:\python35\python.exe + TOXPYTHON: C:\python36\python.exe WINDOWS_SDK_VERSION: v7.1 PYTHON_HOME: C:\python36 PYTHON_VERSION: '3.6' @@ -131,8 +131,8 @@ artifacts: matrix: allow_failures: - TOXENV: 'check' - - TOXENV: '3.5-nocover' - - TOXENV: '3.5,codecov' + - TOXENV: '3.6-nocover' + - TOXENV: '3.6,codecov' - TOXENV: '2.7-buildonly-nocover' - TOXENV: '3.4-buildonly-nocover' - TOXENV: '3.5-buildonly-nocover' diff --git a/ci/templates/appveyor.yml b/ci/templates/appveyor.yml index d06b8676..9af1d4ef 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/appveyor.yml @@ -41,7 +41,7 @@ environment: PYTHON_VERSION: '3.5' PYTHON_ARCH: '64' - TOXENV: '3.6-buildonly-nocover' - TOXPYTHON: C:\python35\python.exe + TOXPYTHON: C:\python36\python.exe WINDOWS_SDK_VERSION: v7.1 PYTHON_HOME: C:\python36 PYTHON_VERSION: '3.6' @@ -107,8 +107,8 @@ artifacts: matrix: allow_failures: - TOXENV: 'check' - - TOXENV: '3.5-nocover' - - TOXENV: '3.5,codecov' + - TOXENV: '3.6-nocover' + - TOXENV: '3.6,codecov' - TOXENV: '2.7-buildonly-nocover' - TOXENV: '3.4-buildonly-nocover' - TOXENV: '3.5-buildonly-nocover' diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index 08636290..3a4a4716 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -161,4 +161,10 @@ deps = skip_install = true commands = +[testenv:3.6-buildonly-nocover] +basepython = {env:TOXPYTHON:python3.6} +deps = +skip_install = true +commands = + diff --git a/tox.ini b/tox.ini index df0e8b02..e039ae73 100644 --- a/tox.ini +++ b/tox.ini @@ -204,4 +204,10 @@ deps = skip_install = true commands = +[testenv:3.6-buildonly-nocover] +basepython = {env:TOXPYTHON:python3.6} +deps = +skip_install = true +commands = + From fa6df912025682054bc9a413cf87ddfd60e739ac Mon Sep 17 00:00:00 2001 From: aburrell Date: Mon, 19 Mar 2018 20:42:10 -0500 Subject: [PATCH 075/101] small improvements small improvements that may improve clarity in the bootstrap code --- ci/appveyor-bootstrap.py | 10 +++++----- ci/templates/tox.ini | 1 - tox.ini | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/ci/appveyor-bootstrap.py b/ci/appveyor-bootstrap.py index 9cd1b8c1..e7b55399 100644 --- a/ci/appveyor-bootstrap.py +++ b/ci/appveyor-bootstrap.py @@ -52,13 +52,13 @@ def download_file(url, path): print("Downloading: {} (into {})".format(url, path)) - progress = [0, 0] + progress = 0 def report(count, size, total): - progress[0] = count * size - if progress[0] - progress[1] > 1000000: - progress[1] = progress[0] - print("Downloaded {:,}/{:,} ...".format(progress[1], total)) + progress_total = count * size + if progress_total - progress > 1000000: + progress = progress_total + print("Downloaded {:,}/{:,} ...".format(progress, total)) dest, _ = urlretrieve(url, path, reporthook=report) return dest diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index 3a4a4716..06e96a34 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -88,7 +88,6 @@ commands = coverage xml --ignore-errors codecov [] - [testenv:extension-coveralls] deps = cpp-coveralls diff --git a/tox.ini b/tox.ini index e039ae73..704fe5ea 100644 --- a/tox.ini +++ b/tox.ini @@ -93,7 +93,6 @@ commands = coverage xml --ignore-errors codecov [] - [testenv:extension-coveralls] deps = cpp-coveralls From a200d2837fa80d5db4644b112ce13fc0be3d92fe Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 11:35:29 -0500 Subject: [PATCH 076/101] C simplification Removed unnecessary if-loop --- c_aacgmv2/include/rtime.h | 3 ++- c_aacgmv2/src/rtime.c | 11 +++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/c_aacgmv2/include/rtime.h b/c_aacgmv2/include/rtime.h index 79fd9f83..2614dd1a 100644 --- a/c_aacgmv2/include/rtime.h +++ b/c_aacgmv2/include/rtime.h @@ -4,7 +4,8 @@ */ /* - (c) 2010 JHU/APL & Others - Please Consult LICENSE.superdarn-rst.3.2-beta-4-g32f7302.txt for more information. + (c) 2010 JHU/APL & Others - Please Consult + LICENSE.superdarn-rst.3.2-beta-4-g32f7302.txt for more information. */ #ifndef _RTIME_H diff --git a/c_aacgmv2/src/rtime.c b/c_aacgmv2/src/rtime.c index f811c649..6ea3918d 100644 --- a/c_aacgmv2/src/rtime.c +++ b/c_aacgmv2/src/rtime.c @@ -1,11 +1,13 @@ /* rtime.c ======= Author: R.J.Barnes + A.G.Burrell */ /* - (c) 2010 JHU/APL & Others - Please Consult LICENSE.superdarn-rst.3.2-beta-4-g32f7302.txt for more information. - + (c) 2010 JHU/APL & Others - Please Consult + LICENSE.superdarn-rst.3.2-beta-4-g32f7302.txt for more information. + Also LICENSE */ #include @@ -19,7 +21,7 @@ #define DAY_SEC 86400 -/* For windows, define setenv */ +/* For windows, define setenv and unsetenv */ #if defined(_WIN32) || defined(_WIN64) int setenv(const char *name, const char *value, int overwrite) { @@ -36,10 +38,7 @@ int setenv(const char *name, const char *value, int overwrite) sprintf(envset, "%s=%s", name, value); return putenv(envset); } -#endif -/* For windows, define unsetenv */ -#if defined(_WIN32) || defined(_WIN64) int unsetenv(const char *name) { extern char **environ; From 8b2f72ea5a2e3cd4f2e9a208ca7d0adfed963a9b Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 12:20:24 -0500 Subject: [PATCH 077/101] wrapper suggestions Made changes related to wrapper suggestions. --- aacgmv2/depricated.py | 9 --- aacgmv2/tests/test_py_aacgmv2.py | 32 ++++++++++ aacgmv2/wrapper.py | 100 +++++++++++++++++-------------- 3 files changed, 88 insertions(+), 53 deletions(-) diff --git a/aacgmv2/depricated.py b/aacgmv2/depricated.py index 3a762cd7..19570bea 100644 --- a/aacgmv2/depricated.py +++ b/aacgmv2/depricated.py @@ -5,7 +5,6 @@ Functions ------------------------------------------------------------------------------- convert : Converts array location -set_coeff_path : Previously set environment variables, no longer used subsol : finds subsolar geocentric longitude and latitude gc2gd_lat : Convert between geocentric and geodetic coordinates igrf_dipole_axis : Get Cartesian unit vector pointing at the IGRF north dipole @@ -76,14 +75,6 @@ def convert(lat, lon, alt, date=None, a2g=False, trace=False, allowtrace=False, return lat_out, lon_out -def set_coeff_path(): - """This depricated routine used to set environment variables, and now is - not needed. - """ - - logging.warning("this routine is no longer needed") - return - def subsol(year, doy, ut): """Finds subsolar geocentric longitude and latitude. diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index 6f4d4033..bbb69ba5 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -664,3 +664,35 @@ def test_mlt_convert_arr(self): np.testing.assert_allclose(mlt, [12.77717927, 0.1105126, 12.44384593], rtol=1.0e-4) + + class TestCoeffPath: + def setup(self): + """Runs before every method to create a clean testing setup""" + self.igrf_out = None + self.aacgm_out = None + + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.igrf_out, self.aacgm_out + + def test_set_coeff_path_default(self): + """Test the coefficient path setting using defaults""" + self.igrf_out, self.coeff_out = aacgmv2.set_coeff_path() + + assert self.igrf_out == aacgmv2.IGRF_12_COEFF + assert self.coeff_out == aacgmv2.AACGM_v2_DAT_PREFIX + + def test_set_coeff_path_different(self): + """Test the coefficient path setting""" + self.igrf_out, self.coeff_out = aacgmv2.set_coeff_path("hi", "bye") + + assert self.igrf_out == "hi" + assert self.coeff_out == "bye" + + def test_set_coeff_path_mix(self): + """Test the coefficient path setting using a mix of input""" + self.igrf_out, self.coeff_out = aacgmv2.set_coeff_path( \ + coeff_prefix="hi") + + assert self.igrf_out == aacgmv2.IGRF_12_COEFF + assert self.coeff_out == "hi" diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index 5f4f4715..aa326a35 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -3,6 +3,7 @@ Functions -------------- +set_coeff_path : Set the coefficient paths using default or supplied values convert_latlon : Converts scalar location convert_latlon_arr : Converts array location get_aacgm_coord : Get scalar magnetic lat, lon, mlt from geographic location @@ -14,12 +15,41 @@ """ from __future__ import division, absolute_import, unicode_literals -import numpy as np import datetime as dt +import numpy as np import logbook as logging import aacgmv2 import aacgmv2._aacgmv2 as c_aacgmv2 +def set_coeff_path(igrf_file=None, coeff_prefix=None): + """This routine sets the two path variables. + + Parameters + ----------- + igrf_file : (str or NoneType) + Full filename of IGRF coefficient file or None to use + aacgmv2.IGRF_12_COEFFS. (default=None) + coeff_prefix : (str or NoneType) + Location and file prefix for aacgm coefficient files or None to use + aacgmv2.AACGM_v2_DAT_PREFIX. (default=None) + + Returns + --------- + igrf_file : (str) + Full filename of IGRF coefficient file + coeff_prefix : (str) + Location and file prefix for aacgm coefficient files + """ + # Define coefficient file prefix if not supplied + if coeff_prefix is None: + coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX + + # Define IGRF file if not supplied + if igrf_file is None: + igrf_file = aacgmv2.IGRF_12_COEFFS + + return igrf_file, coeff_prefix + def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, coeff_prefix=None): """Converts between geomagnetic coordinates and AACGM coordinates @@ -60,12 +90,8 @@ def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, Geocentric radial distance in R """ # Define coefficient file prefix if not supplied - if coeff_prefix is None: - coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX - - # Define IGRF file if not supplied - if igrf_file is None: - igrf_file = aacgmv2.IGRF_12_COEFFS + igrf_file, coeff_prefix = set_coeff_path(igrf_file=igrf_file, + coeff_prefix=coeff_prefix) # Test time if isinstance(dtime, dt.date): @@ -98,7 +124,7 @@ def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, # make flag bit_code = convert_str_to_bit(code) - except: + except AttributeError: bit_code = code assert isinstance(bit_code, int), \ @@ -120,7 +146,8 @@ def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, try: lat_out, lon_out, r_out = c_aacgmv2.convert(in_lat, in_lon, height, bit_code, igrf_file) - except: pass + except: + pass return lat_out, lon_out, r_out @@ -200,16 +227,11 @@ def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", ulen = np.unique([in_lat.shape, in_lon.shape, height.shape]) if ulen.min() != (1,): logging.error("mismatched input arrays") - sys.exit(1) return None, None, None # Define coefficient file prefix if not supplied - if coeff_prefix is None: - coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX - - # Define IGRF file if not supplied - if igrf_file is None: - igrf_file = aacgmv2.IGRF_12_COEFFS + igrf_file, coeff_prefix = set_coeff_path(igrf_file=igrf_file, + coeff_prefix=coeff_prefix) # Test time if isinstance(dtime, dt.date): @@ -226,7 +248,7 @@ def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", lat_out = np.empty(shape=in_lat.shape, dtype=float) * np.nan lon_out = np.empty(shape=in_lon.shape, dtype=float) * np.nan r_out = np.empty(shape=height.shape, dtype=float) * np.nan - + # Test code try: code = code.upper() @@ -242,7 +264,7 @@ def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", # make flag bit_code = convert_str_to_bit(code) - except: + except AttributeError: bit_code = code assert isinstance(bit_code, int), \ @@ -268,7 +290,8 @@ def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", try: lat_out, lon_out, r_out = convert_vectorised(in_lat, in_lon, height, bit_code, igrf_file) - except: pass + except: + pass return lat_out, lon_out, r_out @@ -310,20 +333,16 @@ def get_aacgm_coord(glat, glon, height, dtime, method="TRACE", magnetic local time in hours """ # Define coefficient file prefix if not supplied - if coeff_prefix is None: - coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX - - # Define IGRF file if not supplied - if igrf_file is None: - igrf_file = aacgmv2.IGRF_12_COEFFS + igrf_file, coeff_prefix = set_coeff_path(igrf_file=igrf_file, + coeff_prefix=coeff_prefix) # Initialize code code = "G2A|{:s}".format(method) # Get magnetic lat and lon. - mlat, mlon, mr = convert_latlon(glat, glon, height, dtime, code=code, - igrf_file=igrf_file, - coeff_prefix=coeff_prefix) + mlat, mlon, _ = convert_latlon(glat, glon, height, dtime, code=code, + igrf_file=igrf_file, + coeff_prefix=coeff_prefix) # Get magnetic local time if np.isnan(mlon): mlt = np.nan @@ -372,20 +391,16 @@ def get_aacgm_coord_arr(glat, glon, height, dtime, method="TRACE", magnetic local time in hours """ # Define coefficient file prefix if not supplied - if coeff_prefix is None: - coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX - - # Define IGRF file if not supplied - if igrf_file is None: - igrf_file = aacgmv2.IGRF_12_COEFFS + igrf_file, coeff_prefix = set_coeff_path(igrf_file=igrf_file, + coeff_prefix=coeff_prefix) # Initialize code code = "G2A|{:s}".format(method) # Get magnetic lat and lon. - mlat, mlon, mr = convert_latlon_arr(glat, glon, height, dtime, code=code, - igrf_file=igrf_file, - coeff_prefix=coeff_prefix) + mlat, mlon, _ = convert_latlon_arr(glat, glon, height, dtime, code=code, + igrf_file=igrf_file, + coeff_prefix=coeff_prefix) if np.all(np.isnan(mlon)): mlt = np.empty(shape=mlat.shape, dtype=float) * np.nan @@ -429,7 +444,7 @@ def convert_str_to_bit(code): codes = code.upper().replace(" ", "").split("|") # Add the valid parts of the code, invalid elements are ignored - bit_code = sum([convert_code[k] for k in convert_code.keys() if k in codes]) + bit_code = sum([convert_code[k] for k in codes]) return bit_code @@ -479,7 +494,7 @@ def convert_mlt(arr, dtime, m2a=False, coeff_prefix=None, igrf_file=None): dtime : (datetime.datetime) Date and time for MLT conversion in Universal Time (UT). m2a : (bool) - Convert MLT to AACGM-v2 longitude (True) or magnetic longitude to MLT + Convert MLT to AACGM-v2 longitude (True) or magnetic longitude to MLT (False). (default=False) coeff_prefix : (str or NoneType) Location and file prefix for aacgm coefficient files or None to use @@ -499,12 +514,9 @@ def convert_mlt(arr, dtime, m2a=False, coeff_prefix=None, igrf_file=None): improved calculation available in AACGM-V2.4. """ # Define coefficient file prefix if not supplied - if coeff_prefix is None: - coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX + igrf_file, coeff_prefix = set_coeff_path(igrf_file=igrf_file, + coeff_prefix=coeff_prefix) - # Define IGRF file if not supplied - if igrf_file is None: - igrf_file = aacgmv2.IGRF_12_COEFFS # Test time if isinstance(dtime, dt.date): From ec76974b3750e6776fa93d103132e105c19577b4 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 13:53:52 -0500 Subject: [PATCH 078/101] scrutinizer deprecated Changes to deprecated routines suggested by scrutinizer --- aacgmv2/__init__.py | 5 +-- aacgmv2/depricated.py | 77 +++++++++++++++++++------------------------ 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/aacgmv2/__init__.py b/aacgmv2/__init__.py index 8f7eae4c..b7b06326 100644 --- a/aacgmv2/__init__.py +++ b/aacgmv2/__init__.py @@ -25,10 +25,10 @@ convert_bool_to_bit get_aacgm_coord get_aacgm_coord_arr +set_coeff_path convert convert_mlt subsol -set_coeff_path _aacgmv2.convert _aacgmv2.set_datetime _aacgmv2.mlt_convert @@ -60,12 +60,13 @@ from aacgmv2.wrapper import (convert_latlon, convert_mlt, get_aacgm_coord) from aacgmv2.wrapper import (convert_latlon_arr, get_aacgm_coord_arr) from aacgmv2.wrapper import (convert_bool_to_bit, convert_str_to_bit) + from aacgmv2.wrapper import (et_coeff_path) except Exception as err: logging.exception(__file__ + ' -> aacgmv2: ' + str(err)) try: from aacgmv2 import (depricated) - from aacgmv2.depricated import (convert, subsol, set_coeff_path) + from aacgmv2.depricated import (convert, subsol) except Exception as err: logging.exception(__file__ + ' -> aacgmv2: ' + str(err)) diff --git a/aacgmv2/depricated.py b/aacgmv2/depricated.py index 19570bea..0c8631de 100644 --- a/aacgmv2/depricated.py +++ b/aacgmv2/depricated.py @@ -62,7 +62,7 @@ def convert(lat, lon, alt, date=None, a2g=False, trace=False, allowtrace=False, estr += 'or allowtrace=True) or indicate you know this is a bad idea' logging.error(estr) raise ValueError - + # construct a code from the boolian flags bit_code = aacgmv2.convert_bool_to_bit(a2g=a2g, trace=trace, allowtrace=allowtrace, @@ -70,12 +70,12 @@ def convert(lat, lon, alt, date=None, a2g=False, trace=False, allowtrace=False, geocentric=geocentric) # convert location - lat_out, lon_out, r_out = aacgmv2.convert_latlon_arr(lat, lon, alt, date, - code=bit_code) + lat_out, lon_out, _ = aacgmv2.convert_latlon_arr(lat, lon, alt, date, + code=bit_code) return lat_out, lon_out -def subsol(year, doy, ut): +def subsol(year, doy, utime): """Finds subsolar geocentric longitude and latitude. Parameters @@ -84,7 +84,7 @@ def subsol(year, doy, ut): Calendar year between 1601 and 2100 doy : (int) Day of year between 1-365/366 - ut : (float) + utime : (float) Seconds since midnight on the specified day Returns @@ -107,7 +107,7 @@ def subsol(year, doy, ut): After Fortran code by A. D. Richmond, NCAR. Translated from IDL by K. Laundal. """ - yr = year - 2000 + yr2 = year - 2000 if year >= 2101: logging.error('subsol invalid after 2100. Input year is:', year) @@ -121,55 +121,44 @@ def subsol(year, doy, ut): ncent = 3 - ncent nleap = nleap + ncent - l0 = -79.549 + (-0.238699 * (yr - 4 * nleap) + 3.08514e-2 * nleap) - g0 = -2.472 + (-0.2558905 * (yr - 4 * nleap) - 3.79617e-2 * nleap) - - # Days (including fraction) since 12 UT on January 1 of IYR: - df = (ut / 86400 - 1.5) + doy - - # Addition to Mean longitude of Sun since January 1 of IYR: - lf = 0.9856474 * df + l_0 = -79.549 + (-0.238699 * (yr2 - 4 * nleap) + 3.08514e-2 * nleap) + g_0 = -2.472 + (-0.2558905 * (yr2 - 4 * nleap) - 3.79617e-2 * nleap) - # Addition to Mean anomaly since January 1 of IYR: - gf = 0.9856003 * df + # Days (including fraction) since 12 UT on January 1 of IYR2: + dfrac = (utime / 86400 - 1.5) + doy # Mean longitude of Sun: - l = l0 + lf + l_sun = l_0 + 0.9856474 * dfrac # Mean anomaly: - grad = np.radians(g0 + gf) + grad = np.radians(g_0 + 0.9856003 * dfrac) # Ecliptic longitude: - lmrad = np.radians(l + 1.915 * np.sin(grad) + 0.020 * np.sin(2 * grad)) + lmrad = np.radians(l_sun + 1.915 * np.sin(grad) + 0.020 * np.sin(2 * grad)) sinlm = np.sin(lmrad) # Days (including fraction) since 12 UT on January 1 of 2000: - n = df + 365.0 * yr + nleap + epoch_day = dfrac + 365.0 * yr2 + nleap # Obliquity of ecliptic: - epsrad = np.radians(23.439 - 4.0e-7 * n) + epsrad = np.radians(23.439 - 4.0e-7 * epoch_day) # Right ascension: alpha = np.degrees(np.arctan2(np.cos(epsrad) * sinlm, np.cos(lmrad))) - # Declination: - delta = np.degrees(np.arcsin(np.sin(epsrad) * sinlm)) - - # Subsolar latitude: - sbsllat = delta + # Declination, which is the subsolar latitude: + sbsllat = np.degrees(np.arcsin(np.sin(epsrad) * sinlm)) # Equation of time (degrees): - etdeg = l - alpha - nrot = np.round(etdeg / 360.0) - etdeg = etdeg - 360.0 * nrot + etdeg = l_sun - alpha + etdeg = etdeg - 360.0 * np.round(etdeg / 360.0) # Apparent time (degrees): - aptime = ut / 240.0 + etdeg # Earth rotates one degree every 240 s. + aptime = utime / 240.0 + etdeg # Earth rotates one degree every 240 s. # Subsolar longitude: sbsllon = 180.0 - aptime - nrot = np.round(sbsllon / 360.0) - sbsllon = sbsllon - 360.0 * nrot + sbsllon = sbsllon - 360.0 * np.round(sbsllon / 360.0) return sbsllon, sbsllat @@ -186,8 +175,8 @@ def gc2gd_lat(gc_lat): gd_lat : (same as input) Geodetic latitude in degrees N """ - WGS84_e2 = 0.006694379990141317 - 1.0 - return np.rad2deg(-np.arctan(np.tan(np.deg2rad(gc_lat)) / WGS84_e2)) + wgs84_e2 = 0.006694379990141317 - 1.0 + return np.rad2deg(-np.arctan(np.tan(np.deg2rad(gc_lat)) / wgs84_e2)) def igrf_dipole_axis(date): """Get Cartesian unit vector pointing at dipole pole in the north, @@ -219,8 +208,8 @@ def igrf_dipole_axis(date): year = year + doy / year_days # read the IGRF coefficients - with open(aacgmv2.IGRF_12_COEFFS, 'r') as f: - lines = f.readlines() + with open(aacgmv2.IGRF_12_COEFFS, 'r') as f_igrf: + lines = f_igrf.readlines() years = lines[3].split()[3:][:-1] years = np.array(years, dtype=float) # time array @@ -247,15 +236,15 @@ def igrf_dipole_axis(date): h11 = np.interp(year, years, h11) else: # extrapolation - dt = year - years[-1] - g10 = g10[-1] + g10sv * dt - g11 = g11[-1] + g11sv * dt - h11 = h11[-1] + h11sv * dt + dyear = year - years[-1] + g10 = g10[-1] + g10sv * dyear + g11 = g11[-1] + g11sv * dyear + h11 = h11[-1] + h11sv * dyear # calculate pole position - B0 = np.sqrt(g10**2 + g11**2 + h11**2) + B_0 = np.sqrt(g10**2 + g11**2 + h11**2) # Calculate output - m = -np.array([g11, h11, g10]) / B0 - - return m + m_0 = -np.array([g11, h11, g10]) / B_0 + + return m_0 From 5218dd3bb9a1bc4d26838fd6d75c50fe728026c8 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 13:54:28 -0500 Subject: [PATCH 079/101] python tests Change to python tests, breaking the class into subclasses, and making large tests small. --- aacgmv2/tests/test_py_aacgmv2.py | 1487 ++++++++++++++++++------------ 1 file changed, 881 insertions(+), 606 deletions(-) diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index bbb69ba5..5e3b70fe 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -40,630 +40,905 @@ def test_module_parameters(self): path2 = path.join("aacgmv2", "aacgmv2", "igrf12coeffs.txt") assert aacgmv2.IGRF_12_COEFFS.find(path2) >= 0 - def test_convert_latlon(self): - """Test single value latlon conversion""" - lat, lon, r = aacgmv2.convert_latlon(60, 0, 300, self.dtime) - np.testing.assert_almost_equal(lat, 58.2258, decimal=4) - np.testing.assert_almost_equal(lon, 81.1685, decimal=4) - np.testing.assert_almost_equal(r, 1.0457, decimal=4) - - def test_convert_latlon_badidea_failure(self): - """Test single value latlon conversion with a bad flag""" - code = "G2A | BADIDEA" - lat, lon, r = aacgmv2.convert_latlon(60, 0, 3000, self.dtime, code) - assert np.isnan(lat) & np.isnan(lon) & np.isnan(r) - - def test_convert_latlon_location_failure(self): - """Test single value latlon conversion with a bad location""" - lat, lon, r = aacgmv2.convert_latlon(0, 0, 0, self.dtime) - assert np.isnan(lat) & np.isnan(lon) & np.isnan(r) - - def test_convert_latlon_time_failure(self): - """Test single value latlon conversion with a bad datetime""" - with pytest.raises(AssertionError): - lat, lon, r = aacgmv2.convert_latlon(60, 0, 300, None) - - def test_convert_latlon_arr_single_val(self): - """Test array latlon conversion for a single value""" - lat, lon, r = aacgmv2.convert_latlon_arr(60, 0, 300, self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert isinstance(r, np.ndarray) - assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (1,) - np.testing.assert_allclose(lat, [58.2257709], rtol=1e-4) - np.testing.assert_allclose(lon, [81.16846959], rtol=1e-4) - np.testing.assert_allclose(r, [1.04566346], rtol=1e-4) - - def test_convert_latlon_arr_list(self): - """Test array latlon conversion for list input""" - lat, lon, r = aacgmv2.convert_latlon_arr([60], [0], [300], self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert isinstance(r, np.ndarray) - assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (1,) - np.testing.assert_allclose(lat, [58.2257709], rtol=1e-4) - np.testing.assert_allclose(lon, [81.16846959], rtol=1e-4) - np.testing.assert_allclose(r, [1.04566346], rtol=1e-4) - - lat, lon, r = aacgmv2.convert_latlon_arr([60, 61], [0, 0], [300, 300], - self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert isinstance(r, np.ndarray) - assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2,) - np.testing.assert_allclose(lat, [58.22577090, 59.31860933], rtol=1e-4) - np.testing.assert_allclose(lon, [81.16846959, 81.61398933], rtol=1e-4) - np.testing.assert_allclose(r, [1.04566346, 1.04561304], rtol=1e-4) - - def test_convert_latlon_arr_arr(self): - """Test array latlon conversion for array input""" - lat, lon, r = aacgmv2.convert_latlon_arr(np.array([60]), np.array([0]), - np.array([300]), self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert isinstance(r, np.ndarray) - assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (1,) - np.testing.assert_allclose(lat, [58.2257709], rtol=1e-4) - np.testing.assert_allclose(lon, [81.16846959], rtol=1e-4) - np.testing.assert_allclose(r, [1.04566346], rtol=1e-4) - - lat, lon, r = aacgmv2.convert_latlon_arr(np.array([60, 61]), - np.array([0, 0]), - np.array([300, 300]), - self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert isinstance(r, np.ndarray) - assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2,) - np.testing.assert_allclose(lat, [58.22577090, 59.31860933], rtol=1e-4) - np.testing.assert_allclose(lon, [81.16846959, 81.61398933], rtol=1e-4) - np.testing.assert_allclose(r, [1.04566346, 1.04561304], rtol=1e-4) - - def test_convert_latlon_arr_unequal(self): - """Test array latlon conversion for unequal sized input""" - lat, lon, r = aacgmv2.convert_latlon_arr([60, 61], 0, 300, self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert isinstance(r, np.ndarray) - assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2,) - np.testing.assert_allclose(lat, [58.22577090, 59.31860933], rtol=1e-4) - np.testing.assert_allclose(lon, [81.16846959, 81.61398933], rtol=1e-4) - np.testing.assert_allclose(r, [1.04566346, 1.04561304], rtol=1e-4) - - lat, lon, r = aacgmv2.convert_latlon_arr(np.array([60, 61]), 0, 300, - self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert isinstance(r, np.ndarray) - assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (2,) - np.testing.assert_allclose(lat, [58.22577090, 59.31860933], rtol=1e-4) - np.testing.assert_allclose(lon, [81.16846959, 81.61398933], rtol=1e-4) - np.testing.assert_allclose(r, [1.04566346, 1.04561304], rtol=1e-4) - - lat, lon, r = aacgmv2.convert_latlon_arr(np.array([[60, 61, 62], - [63, 64, 65]]), 0, - 300, self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert isinstance(r, np.ndarray) - assert r.shape == lon.shape and lat.shape == r.shape and \ - r.shape == (2, 3) - np.testing.assert_allclose(lat, [[58.2257709, 59.3186093, 60.4039740], - [61.4819893, 62.5527635, 63.6163840]], - rtol=1e-4) - np.testing.assert_allclose(lon, [[81.1684696, 81.6139893, 82.0871880], - [82.5909499, 83.1285895, 83.7039272]], - rtol=1e-4) - np.testing.assert_allclose(r, [[1.04566346, 1.04561304, 1.04556369], - [1.04551548, 1.04546847, 1.04542272]], - rtol=1e-4) - - lat, lon, r = aacgmv2.convert_latlon_arr(np.array([[60, 61, 62], - [63, 64, 65]]), [0], - [300], self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert isinstance(r, np.ndarray) - assert r.shape == lon.shape and lat.shape == r.shape and \ - r.shape == (2, 3) - - np.testing.assert_allclose(lat, [[58.2257709, 59.3186093, 60.4039740], - [61.4819893, 62.5527635, 63.6163840]], - rtol=1e-4) - np.testing.assert_allclose(lon, [[81.1684696, 81.6139893, 82.0871880], - [82.5909499, 83.1285895, 83.7039272]], - rtol=1e-4) - np.testing.assert_allclose(r, [[1.04566346, 1.04561304, 1.04556369], - [1.04551548, 1.04546847, 1.04542272]], - rtol=1e-4) - - def test_convert_latlon_arr_badidea_failure(self): - """Test array latlon conversion failure for BADIDEA""" - code = "G2A | BADIDEA" - lat, lon, r = aacgmv2.convert_latlon_arr([60], [0], [3000], self.dtime, - code) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert isinstance(r, np.ndarray) - assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (1,) - assert np.all([np.isnan(lat), np.isnan(lon), np.isnan(r)]) - - def test_convert_latlon_arr_location_failure(self): - """Test array latlon conversion with a bad location""" - lat, lon, r = aacgmv2.convert_latlon_arr([0], [0], [0], self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert isinstance(r, np.ndarray) - assert r.shape == lon.shape and lat.shape == r.shape and r.shape == (1,) - assert np.all([np.isnan(lat), np.isnan(lon), np.isnan(r)]) - - def test_convert_latlon_arr_time_failure(self): - """Test array latlon conversion with a bad time""" - with pytest.raises(AssertionError): - lat, lon, r = aacgmv2.convert_latlon_arr([60], [0], [300], None) - - def test_convert_latlon_datetime_date(self): - """Test single latlon conversion with date and datetime input""" - lat_1, lon_1, r_1 = aacgmv2.convert_latlon(60, 0, 300, self.ddate) - lat_2, lon_2, r_2 = aacgmv2.convert_latlon(60, 0, 300, self.dtime) - assert lat_1 == lat_2 - assert lon_1 == lon_2 - assert r_1 == r_2 - - def test_convert_latlon_arr_datetime_date(self): - """Test array latlon conversion with date and datetime input""" - lat_1, lon_1, r_1 = aacgmv2.convert_latlon_arr([60], [0], [300], - self.ddate) - lat_2, lon_2, r_2 = aacgmv2.convert_latlon_arr([60], [0], [300], - self.dtime) - assert lat_1 == lat_2 - assert lon_1 == lon_2 - assert r_1 == r_2 - - def test_warning_below_ground_convert_latlon(self): - """ Test that a warning is issued if altitude is below zero""" - import logbook - lwarn = u"conversion not intended for altitudes < 0 km" - - with logbook.TestHandler() as handler: - lat, lon, r = aacgmv2.convert_latlon(60, 0, -1, self.dtime) - assert handler.has_warning(lwarn) - - handler.close() - - def test_warning_below_ground_convert_latlon_arr(self): - """ Test that a warning is issued if altitude is below zero""" - import logbook - lwarn = u"conversion not intended for altitudes < 0 km" - - with logbook.TestHandler() as handler: - lat, lon, r = aacgmv2.convert_latlon_arr([60], [0], [-1], - self.dtime) - assert handler.has_warning(lwarn) - - handler.close() - - def test_convert_latlon_maxalt_failure(self): - """For a single value, test failure for an altitude too high for - coefficients""" - lat, lon, r = aacgmv2.convert_latlon(60, 0, 2001, self.dtime) - assert np.isnan(lat) & np.isnan(lon) & np.isnan(r) - - def test_convert_latlon_arr_maxalt_failure(self): - """For an array, test failure for an altitude too high for - coefficients""" - lat, lon, r = aacgmv2.convert_latlon_arr([60], [0], [2001], self.dtime) - assert np.all([np.isnan(lat), np.isnan(lon), np.isnan(r)]) - - def test_convert_latlon_lat_failure(self): - """Test error return for co-latitudes above 90 for a single value""" - with pytest.raises(AssertionError): - aacgmv2.convert_latlon(91, 0, 300, self.dtime) - - with pytest.raises(AssertionError): - aacgmv2.convert_latlon(-91, 0, 300, self.dtime) - - def test_convert_latlon_arr_lat_failure(self): - """Test error return for co-latitudes above 90 for an array""" - with pytest.raises(AssertionError): - aacgmv2.convert_latlon_arr([91, 60, -91], 0, 300, self.dtime) - - def test_get_aacgm_coord(self): - """Test single value AACGMV2 calculation, defaults to TRACE""" - mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 0, 300, self.dtime) - np.testing.assert_almost_equal(mlat, 58.2247, decimal=4) - np.testing.assert_almost_equal(mlon, 81.1761, decimal=4) - np.testing.assert_almost_equal(mlt, 0.1889, decimal=4) - - def test_get_aacgm_coord_badidea_failure(self): - """Test single value AACGMV2 calculation with a bad flag""" - method = "BADIDEA" - mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 0, 3000, self.dtime, - method=method) - assert np.isnan(mlat) & np.isnan(mlon) & np.isnan(mlt) - - def test_get_aacgm_coord_location_failure(self): - """Test single value AACGMV2 calculation with a bad location""" - mlat, mlon, mlt = aacgmv2.get_aacgm_coord(0, 0, 0, self.dtime) - assert np.isnan(mlat) & np.isnan(mlon) & np.isnan(mlt) - - def test_get_aacgm_coord_time_failure(self): - """Test single value AACGMV2 calculation with a bad datetime""" - with pytest.raises(AssertionError): - mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 0, 300, None) - - def test_get_aacgm_coord_arr_single_val(self): - """Test array AACGMV2 calculation for a single value""" - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(60, 0, 300, self.dtime) - assert isinstance(mlat, np.ndarray) - assert isinstance(mlon, np.ndarray) - assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ - mlt.shape == (1,) - np.testing.assert_allclose(mlat, [58.22474610], rtol=1e-4) - np.testing.assert_allclose(mlon, [81.17611033], rtol=1e-4) - np.testing.assert_allclose(mlt, [0.18891995], rtol=1e-4) - - def test_get_aacgm_coord_arr_list(self): - """Test array AACGMV2 calculation for list input""" - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60], [0], [300], + del path1, path2 + + class TestConvertLatLon: + def setup(self): + """Runs before every method to create a clean testing setup""" + self.lat_out = None + self.lon_out = None + self.r_out = None + + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.lat_out, self.lon_out, self.r_out + + def test_convert_latlon(self): + """Test single value latlon conversion""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon(60, 0, 300, self.dtime) + np.testing.assert_almost_equal(self.lat_out, 58.2258, decimal=4) + np.testing.assert_almost_equal(self.lon_out, 81.1685, decimal=4) + np.testing.assert_almost_equal(self.r_out, 1.0457, decimal=4) + + def test_convert_latlon_badidea_failure(self): + """Test single value latlon conversion with a bad flag""" + code = "G2A | BADIDEA" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon(60, 0, 3000, self.dtime, code) + assert (np.isnan(self.lat_out) & np.isnan(self.lon_out) & + np.isnan(self.r_out)) + del code + + def test_convert_latlon_location_failure(self): + """Test single value latlon conversion with a bad location""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon(0, 0, 0, self.dtime) + assert (np.isnan(self.lat_out) & np.isnan(self.lon_out) & + np.isnan(self.r_out)) + + def test_convert_latlon_time_failure(self): + """Test single value latlon conversion with a bad datetime""" + with pytest.raises(AssertionError): + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon(60, 0, 300, None) + + def test_convert_latlon_datetime_date(self): + """Test single latlon conversion with date and datetime input""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon(60, 0, 300, self.ddate) + lat_2, lon_2, r_2 = aacgmv2.convert_latlon(60, 0, 300, self.dtime) + + assert self.lat_out == lat_2 + assert self.lon_out == lon_2 + assert self.r_out == r_2 + + del lat_2, lon_2, r_2 + + def test_warning_below_ground_convert_latlon(self): + """ Test that a warning is issued if altitude is below zero""" + import logbook + lwarn = u"conversion not intended for altitudes < 0 km" + + with logbook.TestHandler() as handler: + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon(60, 0, -1, self.dtime) + assert handler.has_warning(lwarn) + + handler.close() + + def test_convert_latlon_maxalt_failure(self): + """For a single value, test failure for an altitude too high for + coefficients""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon(60, 0, 2001, self.dtime) + assert (np.isnan(self.lat_out) & np.isnan(self.lon_out) & + np.isnan(self.r_out)) + + def test_convert_latlon_lat_failure(self): + """Test error return for co-latitudes above 90 for a single value""" + with pytest.raises(AssertionError): + aacgmv2.convert_latlon(91, 0, 300, self.dtime) + + with pytest.raises(AssertionError): + aacgmv2.convert_latlon(-91, 0, 300, self.dtime) + + class TestConvertLatLonArr: + def setup(self): + """Runs before every method to create a clean testing setup""" + self.lat_out = None + self.lon_out = None + self.r_out = None + + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.lat_out, self.lon_out, self.r_out + + def test_convert_latlon_arr_single_val(self): + """Test array latlon conversion for a single value""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr(60, 0, 300, self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (1,)) + + np.testing.assert_allclose(self.lat_out, [58.2257709], rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959], rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346], rtol=1e-4) + + def test_convert_latlon_arr_list_single(self): + """Test array latlon conversion for list input of single values""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [300], self.dtime) - assert isinstance(mlat, np.ndarray) - assert isinstance(mlon, np.ndarray) - assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ - mlt.shape == (1,) - np.testing.assert_allclose(mlat, [58.22474610], rtol=1e-4) - np.testing.assert_allclose(mlon, [81.17611033], rtol=1e-4) - np.testing.assert_allclose(mlt, [0.18891995], rtol=1e-4) - - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60, 61], [0, 0], + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (1,)) + + np.testing.assert_allclose(self.lat_out, [58.2257709], rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959], rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346], rtol=1e-4) + + def test_convert_latlon_arr_list(self): + """Test array latlon conversion for list input""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60, 61], [0, 0], [300, 300], self.dtime) - assert isinstance(mlat, np.ndarray) - assert isinstance(mlon, np.ndarray) - assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ - mlt.shape == (2,) - np.testing.assert_allclose(mlat, [58.22474610, 59.31648007], rtol=1e-4) - np.testing.assert_allclose(mlon, [81.17611033, 81.62281360], rtol=1e-4) - np.testing.assert_allclose(mlt, [0.18891995, 0.21870017], rtol=1e-4) - - def test_get_aacgm_coord_arr_arr(self): - """Test array AACGMV2 calculation for array input""" - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([60]), + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (2,)) + + np.testing.assert_allclose(self.lat_out, [58.22577090, 59.31860933], + rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959, 81.61398933], + rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346, 1.04561304], + rtol=1e-4) + + def test_convert_latlon_arr_arr_single(self): + """Test array latlon conversion for array input of shape (1,)""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr(np.array([60]), np.array([0]), np.array([300]), self.dtime) - assert isinstance(mlat, np.ndarray) - assert isinstance(mlon, np.ndarray) - assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ - mlt.shape == (1,) - np.testing.assert_almost_equal(mlat[0], 58.2247, decimal=4) - np.testing.assert_almost_equal(mlon[0], 81.1761, decimal=4) - np.testing.assert_almost_equal(mlt[0], 0.1889, decimal=4) - - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([60, 61]), - np.array([0, 0]), - np.array([300, 300]), - self.dtime) - assert isinstance(mlat, np.ndarray) - assert isinstance(mlon, np.ndarray) - assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ - mlt.shape == (2,) - np.testing.assert_allclose(mlat, [58.22474610, 59.31648007], rtol=1e-4) - np.testing.assert_allclose(mlon, [81.17611033, 81.62281360], rtol=1e-4) - np.testing.assert_allclose(mlt, [0.18891995, 0.21870017], rtol=1e-4) - - def test_get_aacgm_coord_arr_unequal(self): - """Test array AACGMV2 calculation for unequal sized input""" - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60, 61], 0, 300, + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (1,)) + + np.testing.assert_allclose(self.lat_out, [58.2257709], rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959], rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346], rtol=1e-4) + + def test_convert_latlon_arr_arr(self): + """Test array latlon conversion for array input""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr(np.array([60, 61]), + np.array([0, 0]), + np.array([300, 300]), + self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (2,)) + + np.testing.assert_allclose(self.lat_out, [58.22577090, 59.31860933], + rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959, 81.61398933], + rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346, 1.04561304], + rtol=1e-4) + + def test_convert_latlon_arr_list_mix(self): + """Test array latlon conversion for mixed types with list""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60, 61], 0, 300, self.dtime) - assert isinstance(mlat, np.ndarray) - assert isinstance(mlon, np.ndarray) - assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ - mlt.shape == (2,) - np.testing.assert_allclose(mlat, [58.22474610, 59.31648007], rtol=1e-4) - np.testing.assert_allclose(mlon, [81.17611033, 81.62281360], rtol=1e-4) - np.testing.assert_allclose(mlt, [0.18891995, 0.21870017], rtol=1e-4) - - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([60, 61]), 0, + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (2,)) + + np.testing.assert_allclose(self.lat_out, [58.22577090, 59.31860933], + rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959, 81.61398933], + rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346, 1.04561304], + rtol=1e-4) + + def test_convert_latlon_arr_arr_mix(self): + """Test array latlon conversion for mixed type with an array""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr(np.array([60, 61]), 0, 300, self.dtime) - assert isinstance(mlat, np.ndarray) - assert isinstance(mlon, np.ndarray) - assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ - mlt.shape == (2,) - np.testing.assert_allclose(mlat, [58.22474610, 59.31648007], rtol=1e-4) - np.testing.assert_allclose(mlon, [81.17611033, 81.62281360], rtol=1e-4) - np.testing.assert_allclose(mlt, [0.18891995, 0.21870017], rtol=1e-4) - - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([[60, 61, 62], - [63, 64, 65]]), 0, - 300, self.dtime) - assert isinstance(mlat, np.ndarray) - assert isinstance(mlon, np.ndarray) - assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ - mlt.shape == (2, 3) - np.testing.assert_allclose(mlat, [[58.2247461, 59.3164801, 60.4008651], - [61.4780560, 62.5481858, 63.6113609]], - rtol=1e-4) - np.testing.assert_allclose(mlon, [[81.1761103, 81.6228136, 82.0969646], - [82.6013918, 83.1393547, 83.7146224]], - rtol=1e-4) - np.testing.assert_allclose(mlt, [[0.18891995, 0.21870017, 0.25031024], - [0.28393872, 0.31980291, 0.35815409]], - rtol=1e-4) - - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr(np.array([[60, 61, 62], + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (2,)) + + np.testing.assert_allclose(self.lat_out, [58.22577090, 59.31860933], + rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959, 81.61398933], + rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346, 1.04561304], + rtol=1e-4) + + def test_convert_latlon_arr_mult_arr_mix(self): + """Test array latlon conversion for mix type with multi-dim array""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr(np.array([[60, 61, 62], + [63, 64, 65]]), + 0, 300, self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (2, 3)) + + np.testing.assert_allclose(self.lat_out, + [[58.2257709, 59.3186093, 60.4039740], + [61.4819893, 62.5527635, 63.6163840]], + rtol=1e-4) + np.testing.assert_allclose(self.lon_out, + [[81.1684696, 81.6139893, 82.0871880], + [82.5909499, 83.1285895, 83.7039272]], + rtol=1e-4) + np.testing.assert_allclose(self.r_out, + [[1.04566346, 1.04561304, 1.04556369], + [1.04551548, 1.04546847, 1.04542272]], + rtol=1e-4) + + + def test_convert_latlon_arr_mult_arr_unequal(self): + """Test array latlon conversion for unequal sized multi-dim array""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr(np.array([[60, 61, 62], [63, 64, 65]]), - [0], [300], self.dtime) - assert isinstance(mlat, np.ndarray) - assert isinstance(mlon, np.ndarray) - assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ - mlt.shape == (2, 3) - np.testing.assert_allclose(mlat, [[58.2247, 59.3165, 60.4009], - [61.4781, 62.5482, 63.6114]], - rtol=1e-3) - np.testing.assert_allclose(mlon, [[81.1761, 81.6228, 82.0970], - [82.6014, 83.1394, 83.7146]], - rtol=1e-3) - np.testing.assert_allclose(mlt, [[0.1889, 0.2187, 0.2503], - [0.2839, 0.3198, 0.3582]], rtol=1e-3) - - def test_get_aacgm_coord_arr_badidea_failure(self): - """Test array AACGMV2 calculation failure for BADIDEA""" - method = "BADIDEA" - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60], [0], [3000], - self.dtime, method=method) - assert isinstance(mlat, np.ndarray) - assert isinstance(mlon, np.ndarray) - assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ - mlt.shape == (1,) - assert np.all([np.isnan(mlat), np.isnan(mlon), np.isnan(mlt)]) - - def test_get_aacgm_coord_arr_location_failure(self): - """Test array AACGMV2 calculation with a bad location""" - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([0], [0], [0], self.dtime) - assert isinstance(mlat, np.ndarray) - assert isinstance(mlon, np.ndarray) - assert isinstance(mlt, np.ndarray) - assert mlt.shape == mlon.shape and mlat.shape == mlt.shape and \ - mlt.shape == (1,) - assert np.all([np.isnan(mlat), np.isnan(mlon), np.isnan(mlt)]) - - def test_get_aacgm_coord_arr_time_failure(self): - """Test array AACGMV2 calculation with a bad time""" - with pytest.raises(AssertionError): - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60], [0], [300], + np.array([0]), + np.array([300]), + self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (2, 3)) + + np.testing.assert_allclose(self.lat_out, + [[58.2257709, 59.3186093, 60.4039740], + [61.4819893, 62.5527635, 63.6163840]], + rtol=1e-4) + np.testing.assert_allclose(self.lon_out, + [[81.1684696, 81.6139893, 82.0871880], + [82.5909499, 83.1285895, 83.7039272]], + rtol=1e-4) + np.testing.assert_allclose(self.r_out, + [[1.04566346, 1.04561304, 1.04556369], + [1.04551548, 1.04546847, 1.04542272]], + rtol=1e-4) + + def test_convert_latlon_arr_badidea_failure(self): + """Test array latlon conversion failure for BADIDEA""" + code = "G2A | BADIDEA" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [3000], + self.dtime, code) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (1,)) + assert np.all([np.isnan(self.lat_out), np.isnan(self.lon_out), + np.isnan(self.r_out)]) + + def test_convert_latlon_arr_location_failure(self): + """Test array latlon conversion with a bad location""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([0], [0], [0], self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (1,)) + assert np.all([np.isnan(self.lat_out), np.isnan(self.lon_out), + np.isnan(self.r_out)]) + + def test_convert_latlon_arr_time_failure(self): + """Test array latlon conversion with a bad time""" + with pytest.raises(AssertionError): + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [300], None) - def test_get_aacgm_coord_datetime_date(self): - """Test single AACGMV2 calculation with date and datetime input""" - mlat_1, mlon_1, mlt_1 = aacgmv2.get_aacgm_coord(60, 0, 300, self.ddate) - mlat_2, mlon_2, mlt_2 = aacgmv2.get_aacgm_coord(60, 0, 300, self.dtime) + def test_convert_latlon_arr_datetime_date(self): + """Test array latlon conversion with date and datetime input""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [300], + self.ddate) + lat_2, lon_2, r_2 = aacgmv2.convert_latlon_arr([60], [0], [300], + self.dtime) + assert self.lat_out == lat_2 + assert self.lon_out == lon_2 + assert self.r_out == r_2 + + del lat_2, lon_2, r_2 + + def test_warning_below_ground_convert_latlon_arr(self): + """ Test that a warning is issued if altitude is below zero""" + import logbook + lwarn = u"conversion not intended for altitudes < 0 km" + + with logbook.TestHandler() as handler: + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [-1], + self.dtime) + assert handler.has_warning(lwarn) - np.testing.assert_almost_equal(mlat_1, mlat_2, decimal=6) - np.testing.assert_almost_equal(mlon_1, mlon_2, decimal=6) - np.testing.assert_almost_equal(mlt_1, mlt_2, decimal=6) + handler.close() - def test_get_aacgm_coord_arr_datetime_date(self): - """Test array AACGMV2 calculation with date and datetime input""" - mlat_1, mlon_1, mlt_1 = aacgmv2.get_aacgm_coord_arr([60], [0], [300], - self.ddate) - mlat_2, mlon_2, mlt_2 = aacgmv2.get_aacgm_coord_arr([60], [0], [300], - self.dtime) - np.testing.assert_almost_equal(mlat_1, mlat_2, decimal=6) - np.testing.assert_almost_equal(mlon_1, mlon_2, decimal=6) - np.testing.assert_almost_equal(mlt_1, mlt_2, decimal=6) - - def test_warning_below_ground_get_aacgm_coord(self): - """ Test that a warning is issued if altitude is below zero""" - import logbook - lwarn = u"conversion not intended for altitudes < 0 km" - - with logbook.TestHandler() as handler: - mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 0, -1, self.dtime) - assert handler.has_warning(lwarn) - - handler.close() - - def test_warning_below_ground_get_aacgm_coord_arr(self): - """ Test that a warning is issued if altitude is below zero""" - import logbook - lwarn = u"conversion not intended for altitudes < 0 km" - - with logbook.TestHandler() as handler: - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60], [0], [-1], - self.dtime) - assert handler.has_warning(lwarn) - - handler.close() - - def test_get_aacgm_coord_maxalt_failure(self): - """For a single value, test failure for an altitude too high for - coefficients""" - method = "" - mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 0, 2001, self.dtime, - method=method) - assert np.isnan(mlat) & np.isnan(mlon) & np.isnan(mlt) - - def test_get_aacgm_coord_arr_maxalt_failure(self): - """For an array, test failure for an altitude too high for - coefficients""" - method = "" - mlat, mlon, mlt = aacgmv2.get_aacgm_coord_arr([60], [0], [2001], - self.dtime, method=method) - assert np.all([np.isnan(mlat), np.isnan(mlon), np.isnan(mlt)]) - - def test_get_aacgm_coord_mlat_failure(self): - """Test error return for co-latitudes above 90 for a single value""" - import logbook - lerr = u"unrealistic latitude" - - with logbook.TestHandler() as hhigh: - with pytest.raises(AssertionError): - mlat, mlon, mlt = aacgmv2.get_aacgm_coord(91, 0, 300, - self.dtime) - assert hhigh.has_error(lerr) + def test_convert_latlon_arr_maxalt_failure(self): + """For an array, test failure for an altitude too high for + coefficients""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [2001], + self.dtime) + assert np.all([np.isnan(self.lat_out), np.isnan(self.lon_out), + np.isnan(self.r_out)]) - with logbook.TestHandler() as hlow: + def test_convert_latlon_arr_lat_failure(self): + """Test error return for co-latitudes above 90 for an array""" with pytest.raises(AssertionError): - mlat, mlon, mlt = aacgmv2.get_aacgm_coord(-91, 0, 300, - self.dtime) - assert hlow.has_error(lerr) + aacgmv2.convert_latlon_arr([91, 60, -91], 0, 300, self.dtime) - hhigh.close() - hlow.close() + class TestGetAACGMCoord: + def setup(self): + """Runs before every method to create a clean testing setup""" + self.mlat_out = None + self.mlon_out = None + self.mlt_out = None - def test_get_aacgm_coord_arr_mlat_failure(self): - """Test error return for co-latitudes above 90 for an array""" - import logbook - lerr = u"unrealistic latitude" + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.mlat_out, self.mlon_out, self.mlt_out + + def test_get_aacgm_coord(self): + """Test single value AACGMV2 calculation, defaults to TRACE""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 300, self.dtime) + + np.testing.assert_almost_equal(self.mlat_out, 58.2247, decimal=4) + np.testing.assert_almost_equal(self.mlon_out, 81.1761, decimal=4) + np.testing.assert_almost_equal(self.mlt_out, 0.1889, decimal=4) + + def test_get_aacgm_coord_badidea_failure(self): + """Test single value AACGMV2 calculation with a bad flag""" + method = "BADIDEA" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 3000, self.dtime, + method=method) + + assert (np.isnan(self.mlat_out) & np.isnan(self.mlon_out) & + np.isnan(self.mlt_out)) + del method + + def test_get_aacgm_coord_location_failure(self): + """Test single value AACGMV2 calculation with a bad location""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(0, 0, 0, self.dtime) + assert (np.isnan(self.mlat_out) & np.isnan(self.mlon_out) & + np.isnan(self.mlt_out)) + + def test_get_aacgm_coord_time_failure(self): + """Test single value AACGMV2 calculation with a bad datetime""" + with pytest.raises(AssertionError): + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 300, None) + + def test_get_aacgm_coord_datetime_date(self): + """Test single AACGMV2 calculation with date and datetime input""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 300, self.ddate) + mlat_2, mlon_2, mlt_2 = aacgmv2.get_aacgm_coord(60, 0, 300, + self.dtime) + + np.testing.assert_almost_equal(self.mlat_out, mlat_2, decimal=6) + np.testing.assert_almost_equal(self.mlon_out, mlon_2, decimal=6) + np.testing.assert_almost_equal(self.mlt_out, mlt_2, decimal=6) + + del mlat_2, mlon_2, mlt_2 + + def test_warning_below_ground_get_aacgm_coord(self): + """ Test that a warning is issued if altitude is below zero""" + import logbook + lwarn = u"conversion not intended for altitudes < 0 km" + + with logbook.TestHandler() as handler: + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, -1, self.dtime) + assert handler.has_warning(lwarn) + + handler.close() + + def test_get_aacgm_coord_maxalt_failure(self): + """For a single value, test failure for an altitude too high for + coefficients""" + method = "" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 2001, self.dtime, + method=method) + assert (np.isnan(self.mlat_out) & np.isnan(self.mlon_out) & + np.isnan(self.mlt_out)) + + def test_get_aacgm_coord_mlat_failure(self): + """Test error return for co-latitudes above 90 for a single value""" + import logbook + lerr = u"unrealistic latitude" + + with logbook.TestHandler() as hhigh: + with pytest.raises(AssertionError): + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(91, 0, 300, + self.dtime) + assert hhigh.has_error(lerr) + + with logbook.TestHandler() as hlow: + with pytest.raises(AssertionError): + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(-91, 0, 300, + self.dtime) + assert hlow.has_error(lerr) + + hhigh.close() + hlow.close() + + class TestGetAACGMCoordArr: + def setup(self): + """Runs before every method to create a clean testing setup""" + self.mlat_out = None + self.mlon_out = None + self.mlt_out = None - with logbook.TestHandler() as handler: + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.mlat_out, self.mlon_out, self.mlt_out + + def test_get_aacgm_coord_arr_single_val(self): + """Test array AACGMV2 calculation for a single value""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr(60, 0, 300, self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (1,)) + + np.testing.assert_allclose(self.mlat_out, [58.22474610], rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, [81.17611033], rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, [0.18891995], rtol=1e-4) + + def test_get_aacgm_coord_arr_list_single(self): + """Test array AACGMV2 calculation for list input of single values""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [300], + self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (1,)) + + np.testing.assert_allclose(self.mlat_out, [58.22474610], rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, [81.17611033], rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, [0.18891995], rtol=1e-4) + + def test_get_aacgm_coord_arr_list(self): + """Test array AACGMV2 calculation for list input""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60, 61], [0, 0], + [300, 300], self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (2,)) + + np.testing.assert_allclose(self.mlat_out, + [58.22474610, 59.31648007], rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, + [81.17611033, 81.62281360], rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, + [0.18891995, 0.21870017], rtol=1e-4) + + def test_get_aacgm_coord_arr_arr_single(self): + """Test array AACGMV2 calculation for array with a single value""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr(np.array([60]), + np.array([0]), + np.array([300]), + self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (1,)) + + np.testing.assert_almost_equal(self.mlat_out, 58.2247, decimal=4) + np.testing.assert_almost_equal(self.mlon_out, 81.1761, decimal=4) + np.testing.assert_almost_equal(self.mlt_out, 0.1889, decimal=4) + + def test_get_aacgm_coord_arr_arr(self): + """Test array AACGMV2 calculation for an array""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr(np.array([60, 61]), + np.array([0, 0]), + np.array([300, 300]), + self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (2,)) + + np.testing.assert_allclose(self.mlat_out, + [58.22474610, 59.31648007], rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, + [81.17611033, 81.62281360], rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, + [0.18891995, 0.21870017], rtol=1e-4) + + def test_get_aacgm_coord_arr_list_mix(self): + """Test array AACGMV2 calculation for a list and floats""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60, 61], 0, 300, + self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (2,)) + + np.testing.assert_allclose(self.mlat_out, + [58.22474610, 59.31648007], rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, + [81.17611033, 81.62281360], rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, + [0.18891995, 0.21870017], rtol=1e-4) + + def test_get_aacgm_coord_arr_arr_mix(self): + """Test array AACGMV2 calculation for an array and floats""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr(np.array([60, 61]), 0, + 300, self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (2,)) + + np.testing.assert_allclose(self.mlat_out, + [58.22474610, 59.31648007], rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, + [81.17611033, 81.62281360], rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, + [0.18891995, 0.21870017], rtol=1e-4) + + def test_get_aacgm_coord_arr_mult_arr_mix(self): + """Test array AACGMV2 calculation for a multi-dim array and + floats""" + mlat_in = np.array([[60, 61, 62], [63, 64, 65]]) + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr(mlat_in, 0, 300, + self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (2, 3)) + + np.testing.assert_allclose(self.mlat_out, + [[58.2247461, 59.3164801, 60.4008651], + [61.4780560, 62.5481858, 63.6113609]], + rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, + [[81.1761103, 81.6228136, 82.0969646], + [82.6013918, 83.1393547, 83.7146224]], + rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, + [[0.18891995, 0.21870017, 0.25031024], + [0.28393872, 0.31980291, 0.35815409]], + rtol=1e-4) + del mlat_in + + def test_get_aacgm_coord_arr_arr_unequal(self): + """Test array AACGMV2 calculation for unequal arrays""" + mlat_in = np.array([[60, 61, 62], [63, 64, 65]]) + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr(mlat_in, np.array([0]), + np.array([300]), + self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (2, 3)) + + np.testing.assert_allclose(self.mlat_out, + [[58.2247, 59.3165, 60.4009], + [61.4781, 62.5482, 63.6114]], rtol=1e-3) + np.testing.assert_allclose(self.mlon_out, + [[81.1761, 81.6228, 82.0970], + [82.6014, 83.1394, 83.7146]], rtol=1e-3) + np.testing.assert_allclose(self.mlt_out, + [[0.1889, 0.2187, 0.2503], + [0.2839, 0.3198, 0.3582]], rtol=1e-3) + del mlat_in + + def test_get_aacgm_coord_arr_badidea_failure(self): + """Test array AACGMV2 calculation failure for BADIDEA""" + method = "BADIDEA" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [3000], + self.dtime, + method=method) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (1,)) + assert np.all([np.isnan(self.mlat_out), np.isnan(self.mlon_out), + np.isnan(self.mlt_out)]) + del method + + def test_get_aacgm_coord_arr_location_failure(self): + """Test array AACGMV2 calculation with a bad location""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([0], [0], [0], + self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (1,)) + assert np.all([np.isnan(self.mlat_out), np.isnan(self.mlon_out), + np.isnan(self.mlt_out)]) + + def test_get_aacgm_coord_arr_time_failure(self): + """Test array AACGMV2 calculation with a bad time""" with pytest.raises(AssertionError): - aacgmv2.get_aacgm_coord_arr([91, 60, -91], 0, 300, self.dtime) - assert handler.has_error(lerr) - - handler.close() - - def test_convert_str_to_bit_g2a(self): - """Test conversion from string code to bit G2A""" - assert aacgmv2.convert_str_to_bit("G2A") == aacgmv2._aacgmv2.G2A - - def test_convert_str_to_bit_a2g(self): - """Test conversion from string code to bit A2G""" - assert aacgmv2.convert_str_to_bit("A2G") == aacgmv2._aacgmv2.A2G - - def test_convert_str_to_bit_trace(self): - """Test conversion from string code to bit TRACE""" - assert aacgmv2.convert_str_to_bit("TRACE") == aacgmv2._aacgmv2.TRACE - - def test_convert_str_to_bit_allowtrace(self): - """Test conversion from string code to bit ALLOWTRACE""" - assert aacgmv2.convert_str_to_bit("ALLOWTRACE") == \ - aacgmv2._aacgmv2.ALLOWTRACE - - def test_convert_str_to_bit_badidea(self): - """Test conversion from string code to bit BADIDEA""" - assert aacgmv2.convert_str_to_bit("BADIDEA") == aacgmv2._aacgmv2.BADIDEA - - def test_convert_str_to_bit_geocentric(self): - """Test conversion from string code to bit GEOCENTRIC""" - assert aacgmv2.convert_str_to_bit("GEOCENTRIC") == \ - aacgmv2._aacgmv2.GEOCENTRIC - - def test_convert_str_to_bit_lowercase(self): - """Test conversion from string code to bit for a lowercase code""" - assert aacgmv2.convert_str_to_bit("g2a") == aacgmv2._aacgmv2.G2A - - def test_convert_str_to_bit_spaces(self): - """Test conversion from string code to bit for a code with spaces""" - assert aacgmv2.convert_str_to_bit("G2A | trace") == \ - aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.TRACE - - def test_convert_str_to_bit_invalid(self): - """Test conversion from string code to bit for an invalid code""" - assert aacgmv2.convert_str_to_bit("ggoogg|") == aacgmv2._aacgmv2.G2A - - def test_convert_bool_to_bit_g2a(self): - """Test conversion from string code to bit G2A""" - assert aacgmv2.convert_bool_to_bit() == aacgmv2._aacgmv2.G2A - - def test_convert_bool_to_bit_a2g(self): - """Test conversion from string code to bit A2G""" - assert aacgmv2.convert_bool_to_bit(a2g=True) == aacgmv2._aacgmv2.A2G - - def test_convert_bool_to_bit_trace(self): - """Test conversion from string code to bit TRACE""" - assert aacgmv2.convert_bool_to_bit(trace=True) == aacgmv2._aacgmv2.TRACE - - def test_convert_bool_to_bit_allowtrace(self): - """Test conversion from string code to bit ALLOWTRACE""" - assert aacgmv2.convert_bool_to_bit(allowtrace=True) == \ - aacgmv2._aacgmv2.ALLOWTRACE - - def test_convert_bool_to_bit_badidea(self): - """Test conversion from string code to bit BADIDEA""" - assert aacgmv2.convert_bool_to_bit(badidea=True) == \ - aacgmv2._aacgmv2.BADIDEA - - def test_convert_bool_to_bit_geocentric(self): - """Test conversion from string code to bit GEOCENTRIC""" - assert aacgmv2.convert_bool_to_bit(geocentric=True) == \ - aacgmv2._aacgmv2.GEOCENTRIC - - def test_inv_convert_mlt_single(self): - """Test MLT inversion for a single value""" - mlon_1 = aacgmv2.convert_mlt(12.0, self.dtime, m2a=True) - mlon_2 = aacgmv2.convert_mlt(25.0, self.dtime, m2a=True) - mlon_3 = aacgmv2.convert_mlt(-1.0, self.dtime, m2a=True) - - np.testing.assert_almost_equal(mlon_1, -101.657689, decimal=4) - np.testing.assert_almost_equal(mlon_2, 93.34231102, decimal=4) - np.testing.assert_almost_equal(mlon_3, 63.34231102, decimal=4) - - def test_inv_convert_mlt_list(self): - """Test MLT inversion for a list""" - mlt_list = [12.0, 25.0, -1.0] - mlon = aacgmv2.convert_mlt(mlt_list, self.dtime, m2a=True) - - np.testing.assert_allclose(mlon, [-101.657689, 93.342311, 63.342311], - rtol=1.0e-4) - - def test_inv_convert_mlt_arr(self): - """Test MLT inversion for an array""" - mlt_arr = np.array([12.0, 25.0, -1.0]) - mlon = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=True) - - np.testing.assert_allclose(mlon, [-101.657689, 93.342311, 63.342311], - rtol=1.0e-4) - - def test_inv_convert_mlt_wrapping(self): - """Test MLT wrapping""" - mlt_arr = np.array([1.0, 25.0, -1.0, 23.0]) - mlon = aacgmv2.convert_mlt(mlt_arr, self.dtime, m2a=True) - - np.testing.assert_almost_equal(mlon[0], mlon[1], decimal=6) - np.testing.assert_almost_equal(mlon[2], mlon[3], decimal=6) - - def test_mlt_convert_mlon_wrapping(self): - """Test mlon wrapping""" - mlon_arr = np.array([270.0, -90.0, 1.0, 361.0]) - mlt = aacgmv2.convert_mlt(mlon_arr, self.dtime, m2a=False) - - np.testing.assert_almost_equal(mlt[0], mlt[1], decimal=6) - np.testing.assert_almost_equal(mlt[2], mlt[3], decimal=6) - - def test_mlt_convert_single(self): - """Test MLT calculation for a single value""" - mlt_1 = aacgmv2.convert_mlt(270.0, self.dtime, m2a=False) - mlt_2 = aacgmv2.convert_mlt(80.0, self.dtime, m2a=False) - - np.testing.assert_almost_equal(mlt_1, 12.77717927, decimal=4) - np.testing.assert_almost_equal(mlt_2, 0.1105126, decimal=4) - - def test_mlt_convert_list(self): - """Test MLT calculation for a list""" - mlon_list = [270.0, 80.0, -95.0] - mlt = aacgmv2.convert_mlt(mlon_list, self.dtime, m2a=False) - - np.testing.assert_allclose(mlt, [12.77717927, 0.1105126, 12.44384593], - rtol=1.0e-4) - - def test_mlt_convert_arr(self): - """Test MLT calculation for an array""" - mlon_arr = np.array([270.0, 80.0, -95.0]) - mlt = aacgmv2.convert_mlt(mlon_arr, self.dtime, m2a=False) - - np.testing.assert_allclose(mlt, [12.77717927, 0.1105126, 12.44384593], - rtol=1.0e-4) + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [300], + None) + + def test_get_aacgm_coord_arr_datetime_date(self): + """Test array AACGMV2 calculation with date and datetime input""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [300], + self.ddate) + mlat_2, mlon_2, mlt_2 = aacgmv2.get_aacgm_coord_arr([60], [0], + [300], + self.dtime) + + np.testing.assert_almost_equal(self.mlat_out, mlat_2, decimal=6) + np.testing.assert_almost_equal(self.mlon_out, mlon_2, decimal=6) + np.testing.assert_almost_equal(self.mlt_1, mlt_2, decimal=6) + + del mlat_2, mlon_2, mlt_2 + + def test_warning_below_ground_get_aacgm_coord_arr(self): + """ Test that a warning is issued if altitude is below zero""" + import logbook + lwarn = u"conversion not intended for altitudes < 0 km" + + with logbook.TestHandler() as handler: + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [-1], + self.dtime) + assert handler.has_warning(lwarn) + + handler.close() + + def test_get_aacgm_coord_arr_maxalt_failure(self): + """For an array, test failure for an altitude too high for + coefficients""" + method = "" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [2001], + self.dtime, + method=method) + assert np.all([np.isnan(self.mlat_out), np.isnan(self.mlon_out), + np.isnan(self.mlt_out)]) + del method + + def test_get_aacgm_coord_arr_mlat_failure(self): + """Test error return for co-latitudes above 90 for an array""" + import logbook + lerr = u"unrealistic latitude" + + with logbook.TestHandler() as handler: + with pytest.raises(AssertionError): + aacgmv2.get_aacgm_coord_arr([91, 60, -91], 0, 300, + self.dtime) + assert handler.has_error(lerr) + + handler.close() + + class TestConvertCode: + def test_convert_str_to_bit_g2a(self): + """Test conversion from string code to bit G2A""" + assert aacgmv2.convert_str_to_bit("G2A") == aacgmv2._aacgmv2.G2A + + def test_convert_str_to_bit_a2g(self): + """Test conversion from string code to bit A2G""" + assert aacgmv2.convert_str_to_bit("A2G") == aacgmv2._aacgmv2.A2G + + def test_convert_str_to_bit_trace(self): + """Test conversion from string code to bit TRACE""" + assert aacgmv2.convert_str_to_bit("TRACE") == aacgmv2._aacgmv2.TRACE + + def test_convert_str_to_bit_allowtrace(self): + """Test conversion from string code to bit ALLOWTRACE""" + assert aacgmv2.convert_str_to_bit("ALLOWTRACE") == \ + aacgmv2._aacgmv2.ALLOWTRACE + + def test_convert_str_to_bit_badidea(self): + """Test conversion from string code to bit BADIDEA""" + assert aacgmv2.convert_str_to_bit("BADIDEA") == \ + aacgmv2._aacgmv2.BADIDEA + + def test_convert_str_to_bit_geocentric(self): + """Test conversion from string code to bit GEOCENTRIC""" + assert aacgmv2.convert_str_to_bit("GEOCENTRIC") == \ + aacgmv2._aacgmv2.GEOCENTRIC + + def test_convert_str_to_bit_lowercase(self): + """Test conversion from string code to bit for a lowercase code""" + assert aacgmv2.convert_str_to_bit("g2a") == aacgmv2._aacgmv2.G2A + + def test_convert_str_to_bit_spaces(self): + """Test conversion from string code to bit for a code with spaces""" + assert aacgmv2.convert_str_to_bit("G2A | trace") == \ + aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.TRACE + + def test_convert_str_to_bit_invalid(self): + """Test conversion from string code to bit for an invalid code""" + assert aacgmv2.convert_str_to_bit("ggoogg|") == aacgmv2._aacgmv2.G2A + + def test_convert_bool_to_bit_g2a(self): + """Test conversion from string code to bit G2A""" + assert aacgmv2.convert_bool_to_bit() == aacgmv2._aacgmv2.G2A + + def test_convert_bool_to_bit_a2g(self): + """Test conversion from string code to bit A2G""" + assert aacgmv2.convert_bool_to_bit(a2g=True) == aacgmv2._aacgmv2.A2G + + def test_convert_bool_to_bit_trace(self): + """Test conversion from string code to bit TRACE""" + assert aacgmv2.convert_bool_to_bit(trace=True) == \ + aacgmv2._aacgmv2.TRACE + + def test_convert_bool_to_bit_allowtrace(self): + """Test conversion from string code to bit ALLOWTRACE""" + assert aacgmv2.convert_bool_to_bit(allowtrace=True) == \ + aacgmv2._aacgmv2.ALLOWTRACE + + def test_convert_bool_to_bit_badidea(self): + """Test conversion from string code to bit BADIDEA""" + assert aacgmv2.convert_bool_to_bit(badidea=True) == \ + aacgmv2._aacgmv2.BADIDEA + + def test_convert_bool_to_bit_geocentric(self): + """Test conversion from string code to bit GEOCENTRIC""" + assert aacgmv2.convert_bool_to_bit(geocentric=True) == \ + aacgmv2._aacgmv2.GEOCENTRIC + + class TestMLTConvert: + def setup(self): + """Runs before every method to create a clean testing setup""" + self.mlon_out = None + self.mlt_out = None + self.mlon_list = [270.0, 80.0, -95.0] + self.mlt_list = [12.0, 25.0, -1.0] + self.mlon_comp = [-101.657689, 93.34231102, 63.34231102] + self.mlt_comp = [12.77717927, 0.1105126, 12.44384593] + + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.mlon_out, self.mlt_out, self.mlt_list, self.mlon_list + del self.mlon_comp, self.mlt_comp + + def test_inv_convert_mlt_single(self): + """Test MLT inversion for a single value""" + for i,mlt in enumerate(self.mlt_list): + self.mlon_out = aacgmv2.convert_mlt(mlt, self.dtime, m2a=True) + np.testing.assert_almost_equal(self.mlon_out, self.mlon_comp[i], + decimal=4) + + def test_inv_convert_mlt_list(self): + """Test MLT inversion for a list""" + self.mlon_out = aacgmv2.convert_mlt(self.mlt_list, self.dtime, + m2a=True) + np.testing.assert_allclose(self.mlon_out, self.mlon_comp, + rtol=1.0e-4) + + def test_inv_convert_mlt_arr(self): + """Test MLT inversion for an array""" + self.mlon_out = aacgmv2.convert_mlt(np.array(self.mlt_list), + self.dtime, m2a=True) + + np.testing.assert_allclose(self.mlon_out, self.mlon_comp, + rtol=1.0e-4) + + def test_inv_convert_mlt_wrapping(self): + """Test MLT wrapping""" + self.mlon_out = aacgmv2.convert_mlt(np.array([1, 25, -1, 23]), + self.dtime, m2a=True) + + np.testing.assert_almost_equal(self.mlon_out[0], self.mlon_out[1], + decimal=6) + np.testing.assert_almost_equal(self.mlon_out[2], self.mlon_out[3], + decimal=6) + + def test_mlt_convert_mlon_wrapping(self): + """Test mlon wrapping""" + self.mlt_out = aacgmv2.convert_mlt(np.array([270, -90, 1, 361]), + self.dtime, m2a=False) + + np.testing.assert_almost_equal(self.mlt_out[0], self.mlt_out[1], + decimal=6) + np.testing.assert_almost_equal(self.mlt_out[2], self.mlt_out[3], + decimal=6) + + def test_mlt_convert_single(self): + """Test MLT calculation for a single value""" + for i,mlon in enumerate(self.mlon_list): + self.mlt_out = aacgmv2.convert_mlt(mlon, self.dtime, m2a=False) + np.testing.assert_almost_equal(self.mlt_out, self.mlt_comp[i], + decimal=4) + + def test_mlt_convert_list(self): + """Test MLT calculation for a list""" + self.mlt_out = aacgmv2.convert_mlt(self.mlon_list, self.dtime, + m2a=False) + np.testing.assert_allclose(self.mlt_out, self.mlt_comp, rtol=1.0e-4) + + def test_mlt_convert_arr(self): + """Test MLT calculation for an array""" + self.mlt_out = aacgmv2.convert_mlt(np.array(self.mlon_list), + self.dtime, m2a=False) + np.testing.assert_allclose(self.mlt_out, self.mlt_comp, rtol=1.0e-4) class TestCoeffPath: def setup(self): From b0943f9fc5d2493cbee15b7414deb5cb28557e94 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 13:56:30 -0500 Subject: [PATCH 080/101] misspelled filename Fixed misspelled filename --- aacgmv2/__init__.py | 6 +++--- aacgmv2/{depricated.py => deprecated.py} | 0 aacgmv2/tests/test_dep_aacgmv2.py | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) rename aacgmv2/{depricated.py => deprecated.py} (100%) diff --git a/aacgmv2/__init__.py b/aacgmv2/__init__.py index b7b06326..b5e9e0cd 100644 --- a/aacgmv2/__init__.py +++ b/aacgmv2/__init__.py @@ -39,7 +39,7 @@ Modules --------------------------------------------------------------------------- -depricated +deprecated _aacgmv2 --------------------------------------------------------------------------- """ @@ -65,8 +65,8 @@ logging.exception(__file__ + ' -> aacgmv2: ' + str(err)) try: - from aacgmv2 import (depricated) - from aacgmv2.depricated import (convert, subsol) + from aacgmv2 import (deprecated) + from aacgmv2.deprecated import (convert, subsol) except Exception as err: logging.exception(__file__ + ' -> aacgmv2: ' + str(err)) diff --git a/aacgmv2/depricated.py b/aacgmv2/deprecated.py similarity index 100% rename from aacgmv2/depricated.py rename to aacgmv2/deprecated.py diff --git a/aacgmv2/tests/test_dep_aacgmv2.py b/aacgmv2/tests/test_dep_aacgmv2.py index f9354fa8..53377d45 100644 --- a/aacgmv2/tests/test_dep_aacgmv2.py +++ b/aacgmv2/tests/test_dep_aacgmv2.py @@ -17,17 +17,17 @@ def teardown(self): del self.dtime, self.ddate def test_module_structure(self): - """Test module structure for depricated routines""" + """Test module structure for deprecated routines""" assert aacgmv2 assert aacgmv2.convert assert aacgmv2.set_coeff_path assert aacgmv2.subsol - assert aacgmv2.depricated - assert aacgmv2.depricated.gc2gd_lat - assert aacgmv2.depricated.igrf_dipole_axis + assert aacgmv2.deprecated + assert aacgmv2.deprecated.gc2gd_lat + assert aacgmv2.deprecated.igrf_dipole_axis def test_set_coeff_path(self): - """Test the depricated routine for appropriate warning""" + """Test the deprecated routine for appropriate warning""" import logbook lwarn = u"this routine is no longer needed" @@ -180,27 +180,27 @@ def test_subsol(self): def test_gc2gd_lat(self): """Test the geocentric to geodetic conversion""" - gd_lat = aacgmv2.depricated.gc2gd_lat(45.0) + gd_lat = aacgmv2.deprecated.gc2gd_lat(45.0) np.testing.assert_almost_equal(gd_lat, 45.1924, decimal=4) def test_gc2gd_lat_list(self): """Test the geocentric to geodetic conversion""" gc_lat = [45.0, -45.0] - gd_lat = aacgmv2.depricated.gc2gd_lat(gc_lat) + gd_lat = aacgmv2.deprecated.gc2gd_lat(gc_lat) np.testing.assert_allclose(gd_lat, [45.1924, -45.1924], rtol=1.0e-4) def test_gc2gd_lat_arr(self): """Test the geocentric to geodetic conversion""" gc_lat = np.array([45.0, -45.0]) - gd_lat = aacgmv2.depricated.gc2gd_lat(gc_lat) + gd_lat = aacgmv2.deprecated.gc2gd_lat(gc_lat) np.testing.assert_allclose(gd_lat, [45.1924, -45.1924], rtol=1.0e-4) def test_igrf_dipole_axis(self): """Test the IGRF dipole axis calculation""" - m = aacgmv2.depricated.igrf_dipole_axis(self.dtime) + m = aacgmv2.deprecated.igrf_dipole_axis(self.dtime) np.testing.assert_allclose(m, [0.050253, -0.160608, 0.985738], rtol=1.0e-4) From a0b8a37d70cc07c552cb3a6cf602e17e4e888449 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 14:09:46 -0500 Subject: [PATCH 081/101] scrutinizer deprecation tests Scrutinizer suggestions for deprecation tests --- aacgmv2/tests/test_dep_aacgmv2.py | 203 ++++++++++++++++-------------- 1 file changed, 109 insertions(+), 94 deletions(-) diff --git a/aacgmv2/tests/test_dep_aacgmv2.py b/aacgmv2/tests/test_dep_aacgmv2.py index 53377d45..14af67a9 100644 --- a/aacgmv2/tests/test_dep_aacgmv2.py +++ b/aacgmv2/tests/test_dep_aacgmv2.py @@ -11,10 +11,12 @@ def setup(self): """Runs before every method to create a clean testing setup""" self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0) self.ddate = dt.date(2015, 1, 1) + self.lat = None + self.lon = None def teardown(self): """Runs after every method to clean up previous testing""" - del self.dtime, self.ddate + del self.dtime, self.ddate, self.lat, self.lon def test_module_structure(self): """Test module structure for deprecated routines""" @@ -39,112 +41,125 @@ def test_set_coeff_path(self): def test_convert_single_val(self): """Test conversion for a single value""" - lat, lon = aacgmv2.convert(60, 0, 300, self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape and lat.shape == (1,) - np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) + self.lat, self.lon = aacgmv2.convert(60, 0, 300, self.dtime) + assert isinstance(self.lat, np.ndarray) + assert isinstance(self.lon, np.ndarray) + assert self.lat.shape == self.lon.shape and self.lat.shape == (1,) + np.testing.assert_allclose(self.lat, [58.2258], rtol=1e-4) + np.testing.assert_allclose(self.lon, [81.1685], rtol=1e-4) def test_convert_list(self): """Test conversion for list input""" - lat, lon = aacgmv2.convert([60], [0], [300], self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape and lat.shape == (1,) - np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) - - lat, lon = aacgmv2.convert([60, 61], [0, 0], [300, 300], self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape and lat.shape == (2,) - np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) + self.lat, self.lon = aacgmv2.convert([60], [0], [300], self.dtime) + assert isinstance(self.lat, np.ndarray) + assert isinstance(self.lon, np.ndarray) + assert self.lat.shape == self.lon.shape and self.lat.shape == (1,) + np.testing.assert_allclose(self.lat, [58.2258], rtol=1e-4) + np.testing.assert_allclose(self.lon, [81.1685], rtol=1e-4) + + self.lat, self.lon = aacgmv2.convert([60, 61], [0, 0], [300, 300], + self.dtime) + assert isinstance(self.lat, np.ndarray) + assert isinstance(self.lon, np.ndarray) + assert self.lat.shape == self.lon.shape and self.lat.shape == (2,) + np.testing.assert_allclose(self.lat, [58.2258, 59.3186], rtol=1e-4) + np.testing.assert_allclose(self.lon, [81.1685, 81.6140], rtol=1e-4) + + def test_convert_arr_single(self): + """Test conversion for array input with one element""" + self.lat, self.lon = aacgmv2.convert(np.array([60]), np.array([0]), + np.array([300]), self.dtime) + assert isinstance(self.lat, np.ndarray) + assert isinstance(self.lon, np.ndarray) + assert self.lat.shape == self.lon.shape and self.lat.shape == (1,) + np.testing.assert_allclose(self.lat, [58.2258], rtol=1e-4) + np.testing.assert_allclose(self.lon, [81.1685], rtol=1e-4) def test_convert_arr(self): """Test conversion for array input""" - lat, lon = aacgmv2.convert(np.array([60]), np.array([0]), - np.array([300]), self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape and lat.shape == (1,) - np.testing.assert_allclose(lat, [58.2258], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685], rtol=1e-4) - - lat, lon = aacgmv2.convert(np.array([60, 61]), np.array([0, 0]), - np.array([300, 300]), self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape and lat.shape == (2,) - np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) - - def test_convert_unequal(self): - """Test conversion for unequal sized input""" - lat, lon = aacgmv2.convert([60, 61], 0, 300, self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape and lat.shape == (2,) - np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) - - lat, lon = aacgmv2.convert(np.array([60, 61]), 0, 300, self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape and lat.shape == (2,) - np.testing.assert_allclose(lat, [58.2258, 59.3186], rtol=1e-4) - np.testing.assert_allclose(lon, [81.1685, 81.6140], rtol=1e-4) - - lat, lon = aacgmv2.convert(np.array([[60, 61, 62], [63, 64, 65]]), 0, - 300, self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape and lat.shape == (2, 3) - np.testing.assert_allclose(lat, [[58.2258, 59.3186, 60.4040], + self.lat, self.lon = aacgmv2.convert(np.array([60, 61]), + np.array([0, 0]), + np.array([300, 300]), self.dtime) + assert isinstance(self.lat, np.ndarray) + assert isinstance(self.lon, np.ndarray) + assert self.lat.shape == self.lon.shape and self.lat.shape == (2,) + np.testing.assert_allclose(self.lat, [58.2258, 59.3186], rtol=1e-4) + np.testing.assert_allclose(self.lon, [81.1685, 81.6140], rtol=1e-4) + + def test_convert_list_mix(self): + """Test conversion for a list and floats""" + self.lat, self.lon = aacgmv2.convert([60, 61], 0, 300, self.dtime) + assert isinstance(self.lat, np.ndarray) + assert isinstance(self.lon, np.ndarray) + assert self.lat.shape == self.lon.shape and self.lat.shape == (2,) + np.testing.assert_allclose(self.lat, [58.2258, 59.3186], rtol=1e-4) + np.testing.assert_allclose(self.lon, [81.1685, 81.6140], rtol=1e-4) + + def test_convert_arr_mix(self): + """Test conversion for an array and floats""" + self.lat, self.lon = aacgmv2.convert(np.array([60, 61]), 0, 300, + self.dtime) + assert isinstance(self.lat, np.ndarray) + assert isinstance(self.lon, np.ndarray) + assert self.lat.shape == self.lon.shape and self.lat.shape == (2,) + np.testing.assert_allclose(self.lat, [58.2258, 59.3186], rtol=1e-4) + np.testing.assert_allclose(self.lon, [81.1685, 81.6140], rtol=1e-4) + + def test_convert_mult_array_mix(self): + """Test conversion for a multi-dim array and floats""" + self.lat, self.lon = aacgmv2.convert(np.array([[60, 61, 62], + [63, 64, 65]]), 0, + 300, self.dtime) + assert isinstance(self.lat, np.ndarray) + assert isinstance(self.lon, np.ndarray) + assert self.lat.shape == self.lon.shape and self.lat.shape == (2, 3) + np.testing.assert_allclose(self.lat, [[58.2258, 59.3186, 60.4040], [61.4820, 62.5528, 63.6164]], rtol=1e-4) - np.testing.assert_allclose(lon, [[81.1685, 81.6140, 82.0872], + np.testing.assert_allclose(self.lon, [[81.1685, 81.6140, 82.0872], [82.5909, 83.1286, 83.7039]], rtol=1e-4) - lat, lon = aacgmv2.convert(np.array([[60, 61, 62], [63, 64, 65]]), [0], - [300], self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape and lat.shape == (2, 3) - np.testing.assert_allclose(lat, [[58.2258, 59.3186, 60.4040], - [61.4820, 62.5528, 63.6164]], + def test_convert_unequal_arra(self): + """Test conversion for unequal sized arrays""" + self.lat, self.lon = aacgmv2.convert(np.array([[60, 61, 62], + [63, 64, 65]]), + np.array([0]), np.array([300]), + self.dtime) + assert isinstance(self.lat, np.ndarray) + assert isinstance(self.lon, np.ndarray) + assert self.lat.shape == self.lon.shape and self.lat.shape == (2, 3) + np.testing.assert_allclose(self.lat, [[58.2258, 59.3186, 60.4040], + [61.4820, 62.5528, 63.6164]], rtol=1e-4) - np.testing.assert_allclose(lon, [[81.1685, 81.6140, 82.0872], - [82.5909, 83.1286, 83.7039]], + np.testing.assert_allclose(self.lon, [[81.1685, 81.6140, 82.0872], + [82.5909, 83.1286, 83.7039]], rtol=1e-4) def test_convert_badidea_failure(self): """Test conversion failure for BADIDEA""" with pytest.raises(ValueError): - lat, lon = aacgmv2.convert([60], [0], [3000], self.dtime, - badidea=True) + self.lat, self.lon = aacgmv2.convert([60], [0], [3000], self.dtime, + badidea=True) def test_convert_location_failure(self): """Test conversion with a bad location""" - lat, lon = aacgmv2.convert([0], [0], [0], self.dtime) - assert isinstance(lat, np.ndarray) - assert isinstance(lon, np.ndarray) - assert lat.shape == lon.shape and lat.shape == (1,) - assert np.all([np.isnan(lat), np.isnan(lon)]) + self.lat, self.lon = aacgmv2.convert([0], [0], [0], self.dtime) + assert isinstance(self.lat, np.ndarray) + assert isinstance(self.lon, np.ndarray) + assert self.lat.shape == self.lon.shape and self.lat.shape == (1,) + assert np.all([np.isnan(self.lat), np.isnan(self.lon)]) def test_convert_time_failure(self): """Test conversion with a bad time""" with pytest.raises(AssertionError): - lat, lon = aacgmv2.convert([60], [0], [300], None) + self.lat, self.lon = aacgmv2.convert([60], [0], [300], None) def test_convert_datetime_date(self): """Test conversion with date and datetime input""" - lat_1, lon_1 = aacgmv2.convert([60], [0], [300], self.ddate) - lat_2, lon_2 = aacgmv2.convert([60], [0], [300], self.dtime) - assert lat_1 == lat_2 - assert lon_1 == lon_2 + self.lat, self.lon = aacgmv2.convert([60], [0], [300], self.ddate) + np.testing.assert_allclose(self.lat, [58.2258], rtol=1e-4) + np.testing.assert_allclose(self.lon, [81.1685], rtol=1e-4) def test_warning_below_ground_convert(self): """ Test that a warning is issued if altitude is below zero""" @@ -152,7 +167,7 @@ def test_warning_below_ground_convert(self): lwarn = u"conversion not intended for altitudes < 0 km" with logbook.TestHandler() as handler: - lat, lon = aacgmv2.convert([60], [0], [-1], self.dtime) + self.lat, self.lon = aacgmv2.convert([60], [0], [-1], self.dtime) assert handler.has_warning(lwarn) handler.close() @@ -160,8 +175,8 @@ def test_warning_below_ground_convert(self): def test_convert_maxalt_failure(self): """For an array, test failure for an altitude too high for coefficients""" - lat, lon = aacgmv2.convert([60], [0], [2001], self.dtime) - assert np.all([np.isnan(lat), np.isnan(lon)]) + self.lat, self.lon = aacgmv2.convert([60], [0], [2001], self.dtime) + assert np.all([np.isnan(self.lat), np.isnan(self.lon)]) def test_convert_lat_failure(self): """Test error return for co-latitudes above 90 for an array""" @@ -173,30 +188,30 @@ def test_subsol(self): doy = int(self.dtime.strftime("%j")) ut = self.dtime.hour * 3600.0 + self.dtime.minute * 60.0 + \ self.dtime.second - slon, slat = aacgmv2.subsol(self.dtime.year, doy, ut) + self.lon, self.lat = aacgmv2.subsol(self.dtime.year, doy, ut) - np.testing.assert_almost_equal(slon, -179.2004, decimal=4) - np.testing.assert_almost_equal(slat, -23.0431, decimal=4) + np.testing.assert_almost_equal(self.lon, -179.2004, decimal=4) + np.testing.assert_almost_equal(self.lat, -23.0431, decimal=4) def test_gc2gd_lat(self): """Test the geocentric to geodetic conversion""" - gd_lat = aacgmv2.deprecated.gc2gd_lat(45.0) + self.lat = aacgmv2.deprecated.gc2gd_lat(45.0) - np.testing.assert_almost_equal(gd_lat, 45.1924, decimal=4) + np.testing.assert_almost_equal(self.lat, 45.1924, decimal=4) def test_gc2gd_lat_list(self): """Test the geocentric to geodetic conversion""" - gc_lat = [45.0, -45.0] - gd_lat = aacgmv2.deprecated.gc2gd_lat(gc_lat) + self.lat = [45.0, -45.0] + self.lat = aacgmv2.deprecated.gc2gd_lat(self.lat) - np.testing.assert_allclose(gd_lat, [45.1924, -45.1924], rtol=1.0e-4) + np.testing.assert_allclose(self.lat, [45.1924, -45.1924], rtol=1.0e-4) def test_gc2gd_lat_arr(self): """Test the geocentric to geodetic conversion""" - gc_lat = np.array([45.0, -45.0]) - gd_lat = aacgmv2.deprecated.gc2gd_lat(gc_lat) + self.lat = np.array([45.0, -45.0]) + self.lat = aacgmv2.deprecated.gc2gd_lat(self.lat) - np.testing.assert_allclose(gd_lat, [45.1924, -45.1924], rtol=1.0e-4) + np.testing.assert_allclose(self.lat, [45.1924, -45.1924], rtol=1.0e-4) def test_igrf_dipole_axis(self): """Test the IGRF dipole axis calculation""" From 444ad53d620383dcf7aaf821c15b5bf424aa8c9c Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 14:14:10 -0500 Subject: [PATCH 082/101] Scritinizer init Made some of the scrutinizer __init__.py suggestions. --- aacgmv2/__init__.py | 4 ++-- aacgmv2/tests/test_py_aacgmv2.py | 6 +++--- aacgmv2/wrapper.py | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/aacgmv2/__init__.py b/aacgmv2/__init__.py index b5e9e0cd..6abb8c64 100644 --- a/aacgmv2/__init__.py +++ b/aacgmv2/__init__.py @@ -8,7 +8,7 @@ Parameters --------------------------------------------------------------------------- -AACGM_v2_DAT_PREFIX +AACGM_V2_DAT_PREFIX IGRF_12_COEFFS _aacgmv2.G2A _aacgmv2.A2G @@ -48,7 +48,7 @@ __version__ = "2.0.1" # path and filename prefix for the IGRF coefficients -AACGM_v2_DAT_PREFIX = _path.join(_path.realpath(_path.dirname(__file__)), +AACGM_V2_DAT_PREFIX = _path.join(_path.realpath(_path.dirname(__file__)), 'aacgm_coeffs', 'aacgm_coeffs-12-') IGRF_12_COEFFS = _path.join(_path.realpath(_path.dirname(__file__)), 'igrf12coeffs.txt') diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index 5e3b70fe..c5c73bb6 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -9,7 +9,7 @@ class TestPyAACGMV2: def setup(self): """Runs before every method to create a clean testing setup""" - self.date_args = (2015, 1, 1, 0, 0, 0, aacgmv2.AACGM_v2_DAT_PREFIX) + self.date_args = (2015, 1, 1, 0, 0, 0, aacgmv2.AACGM_V2_DAT_PREFIX) self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0) self.ddate = dt.date(2015, 1, 1) @@ -35,7 +35,7 @@ def test_module_parameters(self): path1 = path.join("aacgmv2", "aacgmv2", "aacgm_coeffs", "aacgm_coeffs-12-") - assert aacgmv2.AACGM_v2_DAT_PREFIX.find(path1) >= 0 + assert aacgmv2.AACGM_V2_DAT_PREFIX.find(path1) >= 0 path2 = path.join("aacgmv2", "aacgmv2", "igrf12coeffs.txt") assert aacgmv2.IGRF_12_COEFFS.find(path2) >= 0 @@ -955,7 +955,7 @@ def test_set_coeff_path_default(self): self.igrf_out, self.coeff_out = aacgmv2.set_coeff_path() assert self.igrf_out == aacgmv2.IGRF_12_COEFF - assert self.coeff_out == aacgmv2.AACGM_v2_DAT_PREFIX + assert self.coeff_out == aacgmv2.AACGM_V2_DAT_PREFIX def test_set_coeff_path_different(self): """Test the coefficient path setting""" diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index aa326a35..0a576c07 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -31,7 +31,7 @@ def set_coeff_path(igrf_file=None, coeff_prefix=None): aacgmv2.IGRF_12_COEFFS. (default=None) coeff_prefix : (str or NoneType) Location and file prefix for aacgm coefficient files or None to use - aacgmv2.AACGM_v2_DAT_PREFIX. (default=None) + aacgmv2.AACGM_V2_DAT_PREFIX. (default=None) Returns --------- @@ -42,7 +42,7 @@ def set_coeff_path(igrf_file=None, coeff_prefix=None): """ # Define coefficient file prefix if not supplied if coeff_prefix is None: - coeff_prefix = aacgmv2.AACGM_v2_DAT_PREFIX + coeff_prefix = aacgmv2.AACGM_V2_DAT_PREFIX # Define IGRF file if not supplied if igrf_file is None: @@ -78,7 +78,7 @@ def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, aacgmv2.IGRF_12_COEFFS. (default=None) coeff_prefix : (str or NoneType) Location and file prefix for aacgm coefficient files or None to use - aacgmv2.AACGM_v2_DAT_PREFIX. (default=None) + aacgmv2.AACGM_V2_DAT_PREFIX. (default=None) Returns ------- @@ -180,7 +180,7 @@ def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", aacgmv2.IGRF_12_COEFFS. (default=None) coeff_prefix : (str or NoneType) Location and file prefix for aacgm coefficient files or None to use - aacgmv2.AACGM_v2_DAT_PREFIX. (default=None) + aacgmv2.AACGM_V2_DAT_PREFIX. (default=None) Returns ------- @@ -321,7 +321,7 @@ def get_aacgm_coord(glat, glon, height, dtime, method="TRACE", aacgmv2.IGRF_12_COEFFS. (default=None) coeff_prefix : (str or NoneType) Location and file prefix for aacgm coefficient files or None to use - aacgmv2.AACGM_v2_DAT_PREFIX. (default=None) + aacgmv2.AACGM_V2_DAT_PREFIX. (default=None) Returns ------- @@ -379,7 +379,7 @@ def get_aacgm_coord_arr(glat, glon, height, dtime, method="TRACE", aacgmv2.IGRF_12_COEFFS. (default=None) coeff_prefix : (str or NoneType) Location and file prefix for aacgm coefficient files or None to use - aacgmv2.AACGM_v2_DAT_PREFIX. (default=None) + aacgmv2.AACGM_V2_DAT_PREFIX. (default=None) Returns ------- @@ -498,7 +498,7 @@ def convert_mlt(arr, dtime, m2a=False, coeff_prefix=None, igrf_file=None): (False). (default=False) coeff_prefix : (str or NoneType) Location and file prefix for aacgm coefficient files or None to use - aacgmv2.AACGM_v2_DAT_PREFIX. (default=None) + aacgmv2.AACGM_V2_DAT_PREFIX. (default=None) igrf_file : (str or NoneType) Full filename of IGRF coefficient file or None to use aacgmv2.IGRF_12_COEFFS. (default=None) From 814f97d91b07ea285ea2ac11a350e8b0d3a7cc0c Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 14:19:05 -0500 Subject: [PATCH 083/101] main suggestions scrutinizer suggestions for command line execution routine --- aacgmv2/__main__.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/aacgmv2/__main__.py b/aacgmv2/__main__.py index 7663a1c3..6fd39a7e 100644 --- a/aacgmv2/__main__.py +++ b/aacgmv2/__main__.py @@ -26,27 +26,27 @@ def main(): desc = 'Converts between geographical coordinates, AACGM-v2, and MLT' parser = argparse.ArgumentParser(description=desc) - + desc = 'for help, run %(prog)s SUBCOMMAND -h' subparsers = parser.add_subparsers(title='Subcommands', prog='aacgmv2', dest='subcommand', description=desc) subparsers.required = True - + desc = 'convert to/from geomagnetic coordinates. Input file must have lines' desc += 'of the form "LAT LON ALT".' parser_convert = subparsers.add_parser('convert', help=(desc)) - + desc = 'convert between magnetic local time (MLT) and AACGM-v2 longitude. ' desc += 'Input file must have a single number on each line.' parser_convert_mlt = subparsers.add_parser('convert_mlt', help=(desc)) desc = 'input file (stdin if none specified)' - for p in [parser_convert, parser_convert_mlt]: - p.add_argument('-i', '--input', dest='file_in', metavar='FILE_IN', - type=argparse.FileType('r'), default=STDIN, help=desc) - p.add_argument('-o', '--output', dest='file_out', metavar='FILE_OUT', - type=argparse.FileType('wb'), default=STDOUT, - help='output file (stdout if none specified)') + for pp in [parser_convert, parser_convert_mlt]: + pp.add_argument('-i', '--input', dest='file_in', metavar='FILE_IN', + type=argparse.FileType('r'), default=STDIN, help=desc) + pp.add_argument('-o', '--output', dest='file_out', metavar='FILE_OUT', + type=argparse.FileType('wb'), default=STDOUT, + help='output file (stdout if none specified)') desc = 'date for magnetic field model (1900-2020, default: today)' parser_convert.add_argument('-d', '--date', dest='date', metavar='YYYYMMDD', @@ -91,13 +91,14 @@ def main(): allowtrace=args.allowtrace, badidea=args.badidea, geocentric=args.geocentric) - lats, lons, rs = aacgmv2.convert_latlon_arr(array[:,0], array[:,1], - array[:,2], dtime=date, - code=code) - np.savetxt(args.file_out, np.column_stack((lats, lons, rs)), fmt='%.8f') + lats, lons, alts = aacgmv2.convert_latlon_arr(array[:, 0], array[:, 1], + array[:, 2], dtime=date, + code=code) + np.savetxt(args.file_out, np.column_stack((lats, lons, alts)), + fmt='%.8f') elif args.subcommand == 'convert_mlt': dtime = dt.datetime.strptime(args.datetime, '%Y%m%d%H%M%S') - out = aacgmv2.convert_mlt(array[:,0], dtime, m2a=args.m2a) + out = aacgmv2.convert_mlt(array[:, 0], dtime, m2a=args.m2a) np.savetxt(args.file_out, out, fmt='%.8f') From d9f385e257026f1739d85e55989e6564eeb8e922 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 14:57:13 -0500 Subject: [PATCH 084/101] c tests Reduced duplication in tests --- aacgmv2/tests/test_c_aacgmv2.py | 282 +++++++++++++++++++------------- 1 file changed, 169 insertions(+), 113 deletions(-) diff --git a/aacgmv2/tests/test_c_aacgmv2.py b/aacgmv2/tests/test_c_aacgmv2.py index 53bac62c..9e351589 100644 --- a/aacgmv2/tests/test_c_aacgmv2.py +++ b/aacgmv2/tests/test_c_aacgmv2.py @@ -8,13 +8,21 @@ class TestCAACGMV2: def setup(self): """Runs before every method to create a clean testing setup""" - self.date1_args = (2014, 3, 22, 3, 11, 0, aacgmv2.AACGM_v2_DAT_PREFIX) - self.date2_args = (2018, 1, 1, 0, 0, 0, aacgmv2.AACGM_v2_DAT_PREFIX) + self.date_args = [(2014, 3, 22, 3, 11, 0, aacgmv2.AACGM_v2_DAT_PREFIX), + (2018, 1, 1, 0, 0, 0, aacgmv2.AACGM_v2_DAT_PREFIX)] self.long_date = [2014, 3, 22, 3, 11, 0] + self.mlat = None + self.mlon = None + self.rshell = None + self.mlt = None + self.lat_in = [45.5, 60] + self.lon_in = [-23.5, 0] + self.alt_in = [1135, 300] def teardown(self): """Runs after every method to clean up previous testing""" - del self.date1_args, self.date2_args, self.long_date + del self.date_args, self.long_date, self.mlat, self.mlon, self.mlt + del self.lat_in, self.lon_in, self.alt_in def test_module_structure(self): """Test module structure""" @@ -37,13 +45,12 @@ def test_constants(self): ans6 = aacgmv2._aacgmv2.GEOCENTRIC == 16 assert ans1 & ans2 & ans3 & ans4 & ans5 & ans6 + del ans1, ans2, ans3, ans4, ans5, ans6 def test_set_datetime(self): """Test set_datetime""" - ans1 = aacgmv2._aacgmv2.set_datetime(*self.date1_args) is None - ans2 = aacgmv2._aacgmv2.set_datetime(*self.date2_args) is None - - assert ans1 & ans2 + for darg in self.date_args: + assert aacgmv2._aacgmv2.set_datetime(*darg) is None def test_fail_set_datetime(self): """Test unsuccessful set_datetime""" @@ -53,157 +60,195 @@ def test_fail_set_datetime(self): def test_convert_G2A_coeff(self): """Test convert from geographic to magnetic coordinates""" - code = aacgmv2._aacgmv2.G2A - - aacgmv2._aacgmv2.set_datetime(*self.date1_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, - aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 48.1896, decimal=4) - np.testing.assert_almost_equal(mlon, 57.7635, decimal=4) - np.testing.assert_almost_equal(r, 1.1775, decimal=4) - - aacgmv2._aacgmv2.set_datetime(*self.date2_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(60, 0, 300, code, - aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 58.1633, decimal=4) - np.testing.assert_almost_equal(mlon, 81.0719, decimal=4) - np.testing.assert_almost_equal(r, 1.0457, decimal=4) + lat_comp = [48.1896, 58.1633] + lon_comp = [57.7635, 81.0719] + r_comp = [1.1775, 1.0457] + + for i,darg in enumerate(self.date_args): + aacgmv2._aacgmv2.set_datetime(*darg) + (self.mlat, self.mlon, + self.rshell) = aacgmv2._aacgmv2.convert(self.lat_in[i], + self.lon_in[i], + self.alt_in[i], + aacgmv2._aacgmv2.G2A, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(self.mlat, lat_comp[i], decimal=4) + np.testing.assert_almost_equal(self.mlon, lon_comp[i], decimal=4) + np.testing.assert_almost_equal(self.rshell, r_comp[i], decimal=4) + + del lat_comp, lon_comp, r_comp def test_convert_A2G_coeff(self): """Test convert from magnetic to geodetic coordinates""" - code = aacgmv2._aacgmv2.A2G - - aacgmv2._aacgmv2.set_datetime(*self.date1_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, - aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 30.7534, decimal=4) - np.testing.assert_almost_equal(mlon, -94.1806, decimal=4) - np.testing.assert_almost_equal(r, 1133.6241, decimal=4) - - aacgmv2._aacgmv2.set_datetime(*self.date2_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(60, 0, 300, code, - aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 50.3910, decimal=4) - np.testing.assert_almost_equal(mlon, -77.7919, decimal=4) - np.testing.assert_almost_equal(r, 305.7138, decimal=4) + lat_comp = [30.7534, 50.3910] + lon_comp = [-94.1806, -77.7919] + r_comp = [1133.6241, 305.7138] + + for i,darg in enumerate(self.date_args): + aacgmv2._aacgmv2.set_datetime(*self.date1_args) + (self.mlat, self.mlon, + self.rshell) = aacgmv2._aacgmv2.convert(self.lat_in[i], + self.lon_in[i], + self.alt_in[i], + aacgmv2._aacgmv2.A2G, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(self.mlat, lat_comp[i], decimal=4) + np.testing.assert_almost_equal(self.mlon, lon_comp[i], decimal=4) + np.testing.assert_almost_equal(self.rshell, r_comp[i], decimal=4) + + del lat_comp, lon_comp, r_comp def test_convert_G2A_TRACE(self): """Test convert from geodetic to magnetic coordinates using trace""" code = aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.TRACE - - aacgmv2._aacgmv2.set_datetime(*self.date1_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, - aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 48.1948, decimal=4) - np.testing.assert_almost_equal(mlon, 57.7588, decimal=4) - np.testing.assert_almost_equal(r, 1.1775, decimal=4) - - aacgmv2._aacgmv2.set_datetime(*self.date2_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(60, 0, 300, code, - aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 58.1633, decimal=4) - np.testing.assert_almost_equal(mlon, 81.0756, decimal=4) - np.testing.assert_almost_equal(r, 1.0457, decimal=4) + trace_lat = [48.1948, 58.1633] + trace_lon = [57.7588, 81.0756] + trace_r = [1.1775, 1.0457] + + for i,dargs in enumerate(self.date_args): + aacgmv2._aacgmv2.set_datetime(*dargs) + (self.mlat, self.mlon, + self.rshell) = aacgmv2._aacgmv2.convert(self.lat_in[i], + self.lon_in[i], + self.alt_in[i], code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(self.mlat, trace_lat[i], decimal=4) + np.testing.assert_almost_equal(self.mlon, trace_lon[i], decimal=4) + np.testing.assert_almost_equal(self.rshell, trace_r[i], decimal=4) + + del code, trace_lat, trace_lon, trace_r def test_convert_A2G_TRACE(self): """Test convert from magnetic to geodetic coordinates using trace""" code = aacgmv2._aacgmv2.A2G + aacgmv2._aacgmv2.TRACE - aacgmv2._aacgmv2.set_datetime(*self.date1_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, - aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 30.7644, decimal=4) - np.testing.assert_almost_equal(mlon, -94.1809, decimal=4) - np.testing.assert_almost_equal(r, 1133.6277, decimal=4) - - aacgmv2._aacgmv2.set_datetime(*self.date2_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(60, 0, 300, code, - aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 50.3958, decimal=4) - np.testing.assert_almost_equal(mlon, -77.8019, decimal=4) - np.testing.assert_almost_equal(r, 305.7156, decimal=4) + trace_lat = [30.7644, 50.3958] + trace_lon = [-94.1809, -77.8019] + trace_r = [1133.6277, 305.7156] + + for i,dargs in enumerate(self.date_args): + aacgmv2._aacgmv2.set_datetime(*dargs) + (self.mlat, self.mlon, + self.rshell) = aacgmv2._aacgmv2.convert(self.lat_in[i], + self.lon_in[i], + self.alt_in[i], code, + aacgmv2.IGRF_12_COEFFS) + np.testing.assert_almost_equal(self.mlat, trace_lat[i], decimal=4) + np.testing.assert_almost_equal(self.mlon, trace_lon[i], decimal=4) + np.testing.assert_almost_equal(self.rshell, trace_r[i], decimal=4) + + del code, trace_lat, trace_lon, trace_r def test_convert_high_denied(self): """Test for failure when converting to high altitude geodetic to magnetic coordinates""" - aacgmv2._aacgmv2.set_datetime(*self.date1_args) + aacgmv2._aacgmv2.set_datetime(*self.date_args[0]) with pytest.raises(RuntimeError): - aacgmv2._aacgmv2.convert(45.5, -23.5, 5500, aacgmv2._aacgmv2.G2A, + aacgmv2._aacgmv2.convert(self.lat_in[0], self.lon_in[0], 5500, + aacgmv2._aacgmv2.G2A, aacgmv2.IGRF_12_COEFFS) def test_convert_high_TRACE(self): """Test convert from high altitude geodetic to magnetic coordinates using trace""" code = aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.TRACE - aacgmv2._aacgmv2.set_datetime(*self.date1_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 5500, code, + aacgmv2._aacgmv2.set_datetime(*self.date_args[0]) + (self.mlat, self.mlon, + self.rshell) = aacgmv2._aacgmv2.convert(self.lat_in[0], self.lon_in[0], + 5500, code, aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 59.9748, decimal=4) - np.testing.assert_almost_equal(mlon, 57.7425, decimal=4) - np.testing.assert_almost_equal(r, 1.8626, decimal=4) + np.testing.assert_almost_equal(self.mlat, 59.9748, decimal=4) + np.testing.assert_almost_equal(self.mlon, 57.7425, decimal=4) + np.testing.assert_almost_equal(self.rshell, 1.8626, decimal=4) + + del code def test_convert_high_ALLOWTRACE(self): """Test convert from high altitude geodetic to magnetic coordinates by allowing IGRF tracing""" code = aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.ALLOWTRACE - aacgmv2._aacgmv2.set_datetime(*self.date1_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 5500, code, + aacgmv2._aacgmv2.set_datetime(*self.date_args[0]) + (self.mlat, self.mlon, + self.rshell) = aacgmv2._aacgmv2.convert(self.lat_in[0], self.lon_in[0], + 5500, code, aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 59.9748, decimal=4) - np.testing.assert_almost_equal(mlon, 57.7425, decimal=4) - np.testing.assert_almost_equal(r, 1.8626, decimal=4) + np.testing.assert_almost_equal(self.mlat, 59.9748, decimal=4) + np.testing.assert_almost_equal(self.mlon, 57.7425, decimal=4) + np.testing.assert_almost_equal(self.rshell, 1.8626, decimal=4) + + del code def test_convert_high_BADIDEA(self): """Test convert from high altitude geodetic to magnetic coordinates using coefficients""" code = aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.BADIDEA - aacgmv2._aacgmv2.set_datetime(*self.date1_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 5500, code, + aacgmv2._aacgmv2.set_datetime(*self.date_args[0]) + (self.mlat, self.mlon, + self.rshell) = aacgmv2._aacgmv2.convert(self.lat_in[0], self.lon_in[0], + 5500, code, aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 58.7154, decimal=4) - np.testing.assert_almost_equal(mlon, 56.5830, decimal=4) - np.testing.assert_almost_equal(r, 1.8626, decimal=4) + np.testing.assert_almost_equal(self.mlat, 58.7154, decimal=4) + np.testing.assert_almost_equal(self.mlon, 56.5830, decimal=4) + np.testing.assert_almost_equal(self.rshell, 1.8626, decimal=4) + + del code def test_convert_GEOCENTRIC_G2A_coeff(self): """Test convert from geographic to magnetic coordinates""" code = aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.GEOCENTRIC - aacgmv2._aacgmv2.set_datetime(*self.date1_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, + aacgmv2._aacgmv2.set_datetime(*self.date_args[0]) + (self.mlat, self.mlon, + self.rshell) = aacgmv2._aacgmv2.convert(self.lat_in[0], self.lon_in[0], + self.alt_in[0], code, aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 48.3779, decimal=4) - np.testing.assert_almost_equal(mlon, 57.7974, decimal=4) - np.testing.assert_almost_equal(r, 1.1781, decimal=4) + np.testing.assert_almost_equal(self.mlat, 48.3779, decimal=4) + np.testing.assert_almost_equal(self.mlon, 57.7974, decimal=4) + np.testing.assert_almost_equal(self.rshell, 1.1781, decimal=4) + del code + def test_convert_GEOCENTRIC_A2G_coeff(self): """Test convert from magnetic to geocentric coordinates""" code = aacgmv2._aacgmv2.A2G + aacgmv2._aacgmv2.GEOCENTRIC - aacgmv2._aacgmv2.set_datetime(*self.date1_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, + aacgmv2._aacgmv2.set_datetime(*self.date_args[0]) + (self.mlat, self.mlon, + self.rshell) = aacgmv2._aacgmv2.convert(self.lat_in[0], self.lon_in[0], + self.alt_in[0], code, aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 30.6101, decimal=4) - np.testing.assert_almost_equal(mlon, -94.1806, decimal=4) - np.testing.assert_almost_equal(r, 1135.0000, decimal=4) + np.testing.assert_almost_equal(self.mlat, 30.6101, decimal=4) + np.testing.assert_almost_equal(self.mlon, -94.1806, decimal=4) + np.testing.assert_almost_equal(self.rshell, 1135.0000, decimal=4) + + del code def test_convert_GEOCENTRIC_G2A_TRACE(self): """Test convert from geographic to magnetic coordinates using trace""" code = aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.TRACE + \ aacgmv2._aacgmv2.GEOCENTRIC - aacgmv2._aacgmv2.set_datetime(*self.date1_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, + aacgmv2._aacgmv2.set_datetime(*self.date_args[0]) + (self.mlat, self.mlon, + self.rshell) = aacgmv2._aacgmv2.convert(self.lat_in[0], self.lon_in[0], + self.alt_in[0], code, aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 48.3830, decimal=4) - np.testing.assert_almost_equal(mlon, 57.7926, decimal=4) - np.testing.assert_almost_equal(r, 1.1781, decimal=4) + np.testing.assert_almost_equal(self.mlat, 48.3830, decimal=4) + np.testing.assert_almost_equal(self.mlon, 57.7926, decimal=4) + np.testing.assert_almost_equal(self.rshell, 1.1781, decimal=4) + + del code def test_convert_GEOCENTRIC_A2G_TRACE(self): """Test convert from magnetic to geographic coordinates using trace""" code = aacgmv2._aacgmv2.A2G + aacgmv2._aacgmv2.TRACE + \ aacgmv2._aacgmv2.GEOCENTRIC - aacgmv2._aacgmv2.set_datetime(*self.date1_args) - mlat, mlon, r = aacgmv2._aacgmv2.convert(45.5, -23.5, 1135, code, + aacgmv2._aacgmv2.set_datetime(*self.date_args[0]) + (self.mlat, self.mlon, + self.rshell) = aacgmv2._aacgmv2.convert(self.lat_in[0], self.lon_in[0], + self.alt_in[0], code, aacgmv2.IGRF_12_COEFFS) - np.testing.assert_almost_equal(mlat, 30.6211, decimal=4) - np.testing.assert_almost_equal(mlon, -94.1809, decimal=4) - np.testing.assert_almost_equal(r, 1135.0000, decimal=4) + np.testing.assert_almost_equal(self.mlat, 30.6211, decimal=4) + np.testing.assert_almost_equal(self.mlon, -94.1809, decimal=4) + np.testing.assert_almost_equal(self.rshell, 1135.0000, decimal=4) + + del code def test_forbidden(self): """Test convert failure""" @@ -211,21 +256,25 @@ def test_forbidden(self): mloc = aacgmv2._aacgmv2.convert(7, 0, 0, aacgmv2._aacgmv2.G2A, aacgmv2.IGRF_12_COEFFS) + del mloc + def test_inv_mlt_convert(self): """Test MLT inversion""" mlt_args = list(self.long_date) mlt_args.extend([12.0, aacgmv2.AACGM_v2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS]) - mlon = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args) - np.testing.assert_almost_equal(mlon, -153.5931, decimal=4) + self.mlon = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args) + np.testing.assert_almost_equal(self.mlon, -153.5931, decimal=4) mlt_args[-3] = 25.0 - mlon = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args) - np.testing.assert_almost_equal(mlon, 41.4069, decimal=4) + self.mlon = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args) + np.testing.assert_almost_equal(self.mlon, 41.4069, decimal=4) mlt_args[-3] = -1.0 - mlon = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args) - np.testing.assert_almost_equal(mlon, 11.4069, decimal=4) + self.mlon = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args) + np.testing.assert_almost_equal(self.mlon, 11.4069, decimal=4) + + del mlt_args def test_inv_mlt_convert_yrsec(self): """Test MLT inversion with year and seconds of year""" @@ -249,21 +298,26 @@ def test_inv_mlt_convert_yrsec(self): np.testing.assert_almost_equal(mlon_2, 41.4069, decimal=4) np.testing.assert_almost_equal(mlon_3, 11.4069, decimal=4) + del dtime, soy, mlt_args_1, mlt_args_2, mlt_args_3, mlon_1, mlon_2 + del mlon_3 + def test_mlt_convert(self): """Test MLT calculation""" mlt_args = list(self.long_date) mlt_args.extend([270.0, aacgmv2.AACGM_v2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS]) - mlt = aacgmv2._aacgmv2.mlt_convert(*mlt_args) - np.testing.assert_almost_equal(mlt, 16.2395, decimal=4) + self.mlt = aacgmv2._aacgmv2.mlt_convert(*mlt_args) + np.testing.assert_almost_equal(self.mlt, 16.2395, decimal=4) mlt_args[-3] = 80.0 - mlt = aacgmv2._aacgmv2.mlt_convert(*mlt_args) - np.testing.assert_almost_equal(mlt, 3.5729, decimal=4) + self.mlt = aacgmv2._aacgmv2.mlt_convert(*mlt_args) + np.testing.assert_almost_equal(self.mlt, 3.5729, decimal=4) mlt_args[-3] = -90.0 - mlt = aacgmv2._aacgmv2.mlt_convert(*mlt_args) - np.testing.assert_almost_equal(mlt, 16.2395, decimal=4) + self.mlt = aacgmv2._aacgmv2.mlt_convert(*mlt_args) + np.testing.assert_almost_equal(self.mlt, 16.2395, decimal=4) + + del mlt_args def test_mlt_convert_yrsec(self): """Test MLT calculation using year and seconds of year""" @@ -285,3 +339,5 @@ def test_mlt_convert_yrsec(self): np.testing.assert_almost_equal(mlt_1, 16.2395, decimal=4) np.testing.assert_almost_equal(mlt_2, 3.5729, decimal=4) np.testing.assert_equal(mlt_1, mlt_3) + + del dtime, soy, mlt_args_1, mlt_args_2, mlt_args_3, mlt_1, mlt_2, mlt_3 From 0552b17852c154f1d9db95d48d2d8109b86eb60d Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 15:27:47 -0500 Subject: [PATCH 085/101] version number Changed version number to reflect C and IDL version number --- .bumpversion.cfg | 2 +- aacgmv2/__init__.py | 2 +- docs/conf.py | 2 +- setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index aab4ddca..21f497ba 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.0.1 +current_version = 2.4.0 commit = True tag = True diff --git a/aacgmv2/__init__.py b/aacgmv2/__init__.py index 6abb8c64..003e0b2b 100644 --- a/aacgmv2/__init__.py +++ b/aacgmv2/__init__.py @@ -45,7 +45,7 @@ """ import os.path as _path import logbook as logging -__version__ = "2.0.1" +__version__ = "2.4.0" # path and filename prefix for the IGRF coefficients AACGM_V2_DAT_PREFIX = _path.join(_path.realpath(_path.dirname(__file__)), diff --git a/docs/conf.py b/docs/conf.py index 09313527..55711c52 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,7 +24,7 @@ year = u'2015' author = u'Angeline G. Burrell, Christer van der Meeren' copyright = '{0}, {1}'.format(year, author) -version = release = u'2.0.1' +version = release = u'2.4.0' # on_rtd is whether we are on readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' diff --git a/setup.py b/setup.py index 84b6c328..c7111cd2 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def read(fname, **kwargs): setup( name='aacgmv2', - version='2.0.1', + version='2.4.0', license='MIT', description='A Python wrapper for AACGM-v2 magnetic coordinates', long_description='%s\n%s' % (read('README.rst'), From 78777cd60a7548fbc0bdd782ea6d3da760b8736d Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 15:28:02 -0500 Subject: [PATCH 086/101] debug Debugging tests --- aacgmv2/tests/test_c_aacgmv2.py | 22 +- aacgmv2/tests/test_py_aacgmv2.py | 1747 +++++++++++++++--------------- 2 files changed, 878 insertions(+), 891 deletions(-) diff --git a/aacgmv2/tests/test_c_aacgmv2.py b/aacgmv2/tests/test_c_aacgmv2.py index 9e351589..55eda0d9 100644 --- a/aacgmv2/tests/test_c_aacgmv2.py +++ b/aacgmv2/tests/test_c_aacgmv2.py @@ -8,8 +8,8 @@ class TestCAACGMV2: def setup(self): """Runs before every method to create a clean testing setup""" - self.date_args = [(2014, 3, 22, 3, 11, 0, aacgmv2.AACGM_v2_DAT_PREFIX), - (2018, 1, 1, 0, 0, 0, aacgmv2.AACGM_v2_DAT_PREFIX)] + self.date_args = [(2014, 3, 22, 3, 11, 0, aacgmv2.AACGM_V2_DAT_PREFIX), + (2018, 1, 1, 0, 0, 0, aacgmv2.AACGM_V2_DAT_PREFIX)] self.long_date = [2014, 3, 22, 3, 11, 0] self.mlat = None self.mlon = None @@ -56,7 +56,7 @@ def test_fail_set_datetime(self): """Test unsuccessful set_datetime""" with pytest.raises(RuntimeError): aacgmv2._aacgmv2.set_datetime(1013, 1, 1, 0, 0, 0, - aacgmv2.AACGM_v2_DAT_PREFIX) + aacgmv2.AACGM_V2_DAT_PREFIX) def test_convert_G2A_coeff(self): """Test convert from geographic to magnetic coordinates""" @@ -261,7 +261,7 @@ def test_forbidden(self): def test_inv_mlt_convert(self): """Test MLT inversion""" mlt_args = list(self.long_date) - mlt_args.extend([12.0, aacgmv2.AACGM_v2_DAT_PREFIX, + mlt_args.extend([12.0, aacgmv2.AACGM_V2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS]) self.mlon = aacgmv2._aacgmv2.inv_mlt_convert(*mlt_args) np.testing.assert_almost_equal(self.mlon, -153.5931, decimal=4) @@ -283,11 +283,11 @@ def test_inv_mlt_convert_yrsec(self): soy = (int(dtime.strftime("%j"))-1) * 86400 + dtime.hour * 3600 + \ dtime.minute * 60 + dtime.second - mlt_args_1 = [dtime.year, soy, 12.0, aacgmv2.AACGM_v2_DAT_PREFIX, + mlt_args_1 = [dtime.year, soy, 12.0, aacgmv2.AACGM_V2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS] - mlt_args_2 = [dtime.year, soy, 25.0, aacgmv2.AACGM_v2_DAT_PREFIX, + mlt_args_2 = [dtime.year, soy, 25.0, aacgmv2.AACGM_V2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS] - mlt_args_3 = [dtime.year, soy, -1.0, aacgmv2.AACGM_v2_DAT_PREFIX, + mlt_args_3 = [dtime.year, soy, -1.0, aacgmv2.AACGM_V2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS] mlon_1 = aacgmv2._aacgmv2.inv_mlt_convert_yrsec(*mlt_args_1) @@ -304,7 +304,7 @@ def test_inv_mlt_convert_yrsec(self): def test_mlt_convert(self): """Test MLT calculation""" mlt_args = list(self.long_date) - mlt_args.extend([270.0, aacgmv2.AACGM_v2_DAT_PREFIX, + mlt_args.extend([270.0, aacgmv2.AACGM_V2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS]) self.mlt = aacgmv2._aacgmv2.mlt_convert(*mlt_args) np.testing.assert_almost_equal(self.mlt, 16.2395, decimal=4) @@ -325,11 +325,11 @@ def test_mlt_convert_yrsec(self): dtime = dt.datetime(*self.long_date) soy = (int(dtime.strftime("%j"))-1) * 86400 + dtime.hour * 3600 + \ dtime.minute * 60 + dtime.second - mlt_args_1 = [dtime.year, soy, 270.0, aacgmv2.AACGM_v2_DAT_PREFIX, + mlt_args_1 = [dtime.year, soy, 270.0, aacgmv2.AACGM_V2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS] - mlt_args_2 = [dtime.year, soy, 80.0, aacgmv2.AACGM_v2_DAT_PREFIX, + mlt_args_2 = [dtime.year, soy, 80.0, aacgmv2.AACGM_V2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS] - mlt_args_3 = [dtime.year, soy, -90.0, aacgmv2.AACGM_v2_DAT_PREFIX, + mlt_args_3 = [dtime.year, soy, -90.0, aacgmv2.AACGM_V2_DAT_PREFIX, aacgmv2.IGRF_12_COEFFS] mlt_1 = aacgmv2._aacgmv2.mlt_convert_yrsec(*mlt_args_1) diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index c5c73bb6..9417adb8 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -9,9 +9,6 @@ class TestPyAACGMV2: def setup(self): """Runs before every method to create a clean testing setup""" - self.date_args = (2015, 1, 1, 0, 0, 0, aacgmv2.AACGM_V2_DAT_PREFIX) - self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0) - self.ddate = dt.date(2015, 1, 1) def teardown(self): """Runs after every method to clean up previous testing""" @@ -42,932 +39,922 @@ def test_module_parameters(self): del path1, path2 - class TestConvertLatLon: - def setup(self): - """Runs before every method to create a clean testing setup""" - self.lat_out = None - self.lon_out = None - self.r_out = None - - def teardown(self): - """Runs after every method to clean up previous testing""" - del self.lat_out, self.lon_out, self.r_out - - def test_convert_latlon(self): - """Test single value latlon conversion""" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon(60, 0, 300, self.dtime) - np.testing.assert_almost_equal(self.lat_out, 58.2258, decimal=4) - np.testing.assert_almost_equal(self.lon_out, 81.1685, decimal=4) - np.testing.assert_almost_equal(self.r_out, 1.0457, decimal=4) - - def test_convert_latlon_badidea_failure(self): - """Test single value latlon conversion with a bad flag""" - code = "G2A | BADIDEA" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon(60, 0, 3000, self.dtime, code) - assert (np.isnan(self.lat_out) & np.isnan(self.lon_out) & - np.isnan(self.r_out)) - del code - - def test_convert_latlon_location_failure(self): - """Test single value latlon conversion with a bad location""" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon(0, 0, 0, self.dtime) - assert (np.isnan(self.lat_out) & np.isnan(self.lon_out) & - np.isnan(self.r_out)) - - def test_convert_latlon_time_failure(self): - """Test single value latlon conversion with a bad datetime""" - with pytest.raises(AssertionError): - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon(60, 0, 300, None) - - def test_convert_latlon_datetime_date(self): - """Test single latlon conversion with date and datetime input""" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon(60, 0, 300, self.ddate) - lat_2, lon_2, r_2 = aacgmv2.convert_latlon(60, 0, 300, self.dtime) - - assert self.lat_out == lat_2 - assert self.lon_out == lon_2 - assert self.r_out == r_2 - - del lat_2, lon_2, r_2 - - def test_warning_below_ground_convert_latlon(self): - """ Test that a warning is issued if altitude is below zero""" - import logbook - lwarn = u"conversion not intended for altitudes < 0 km" +class TestConvertLatLon(TestPyAACGMV2): - with logbook.TestHandler() as handler: - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon(60, 0, -1, self.dtime) - assert handler.has_warning(lwarn) - - handler.close() + def setup(self): + """Runs before every method to create a clean testing setup""" + self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0) + self.ddate = dt.date(2015, 1, 1) + self.lat_out = None + self.lon_out = None + self.r_out = None - def test_convert_latlon_maxalt_failure(self): - """For a single value, test failure for an altitude too high for - coefficients""" + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.lat_out, self.lon_out, self.r_out, self.dtime, self.ddate + + def test_convert_latlon(self): + """Test single value latlon conversion""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon(60, 0, 300, self.dtime) + np.testing.assert_almost_equal(self.lat_out, 58.2258, decimal=4) + np.testing.assert_almost_equal(self.lon_out, 81.1685, decimal=4) + np.testing.assert_almost_equal(self.r_out, 1.0457, decimal=4) + + def test_convert_latlon_badidea_failure(self): + """Test single value latlon conversion with a bad flag""" + code = "G2A | BADIDEA" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon(60, 0, 3000, self.dtime, code) + assert (np.isnan(self.lat_out) & np.isnan(self.lon_out) & + np.isnan(self.r_out)) + del code + + def test_convert_latlon_location_failure(self): + """Test single value latlon conversion with a bad location""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon(0, 0, 0, self.dtime) + assert (np.isnan(self.lat_out) & np.isnan(self.lon_out) & + np.isnan(self.r_out)) + + def test_convert_latlon_time_failure(self): + """Test single value latlon conversion with a bad datetime""" + with pytest.raises(AssertionError): (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon(60, 0, 2001, self.dtime) - assert (np.isnan(self.lat_out) & np.isnan(self.lon_out) & - np.isnan(self.r_out)) + self.r_out) = aacgmv2.convert_latlon(60, 0, 300, None) - def test_convert_latlon_lat_failure(self): - """Test error return for co-latitudes above 90 for a single value""" - with pytest.raises(AssertionError): - aacgmv2.convert_latlon(91, 0, 300, self.dtime) + def test_convert_latlon_datetime_date(self): + """Test single latlon conversion with date and datetime input""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon(60, 0, 300, self.ddate) + lat_2, lon_2, r_2 = aacgmv2.convert_latlon(60, 0, 300, self.dtime) - with pytest.raises(AssertionError): - aacgmv2.convert_latlon(-91, 0, 300, self.dtime) + assert self.lat_out == lat_2 + assert self.lon_out == lon_2 + assert self.r_out == r_2 - class TestConvertLatLonArr: - def setup(self): - """Runs before every method to create a clean testing setup""" - self.lat_out = None - self.lon_out = None - self.r_out = None + del lat_2, lon_2, r_2 - def teardown(self): - """Runs after every method to clean up previous testing""" - del self.lat_out, self.lon_out, self.r_out + def test_warning_below_ground_convert_latlon(self): + """ Test that a warning is issued if altitude is below zero""" + import logbook + lwarn = u"conversion not intended for altitudes < 0 km" - def test_convert_latlon_arr_single_val(self): - """Test array latlon conversion for a single value""" + with logbook.TestHandler() as handler: (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr(60, 0, 300, self.dtime) - - assert isinstance(self.lat_out, np.ndarray) - assert isinstance(self.lon_out, np.ndarray) - assert isinstance(self.r_out, np.ndarray) - assert (self.r_out.shape == self.lon_out.shape and - self.lat_out.shape == self.r_out.shape and - self.r_out.shape == (1,)) + self.r_out) = aacgmv2.convert_latlon(60, 0, -1, self.dtime) + assert handler.has_warning(lwarn) - np.testing.assert_allclose(self.lat_out, [58.2257709], rtol=1e-4) - np.testing.assert_allclose(self.lon_out, [81.16846959], rtol=1e-4) - np.testing.assert_allclose(self.r_out, [1.04566346], rtol=1e-4) + handler.close() - def test_convert_latlon_arr_list_single(self): - """Test array latlon conversion for list input of single values""" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [300], - self.dtime) + def test_convert_latlon_maxalt_failure(self): + """For a single value, test failure for an altitude too high for + coefficients""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon(60, 0, 2001, self.dtime) + assert (np.isnan(self.lat_out) & np.isnan(self.lon_out) & + np.isnan(self.r_out)) - assert isinstance(self.lat_out, np.ndarray) - assert isinstance(self.lon_out, np.ndarray) - assert isinstance(self.r_out, np.ndarray) - assert (self.r_out.shape == self.lon_out.shape and - self.lat_out.shape == self.r_out.shape and - self.r_out.shape == (1,)) + def test_convert_latlon_lat_failure(self): + """Test error return for co-latitudes above 90 for a single value""" + with pytest.raises(AssertionError): + aacgmv2.convert_latlon(91, 0, 300, self.dtime) - np.testing.assert_allclose(self.lat_out, [58.2257709], rtol=1e-4) - np.testing.assert_allclose(self.lon_out, [81.16846959], rtol=1e-4) - np.testing.assert_allclose(self.r_out, [1.04566346], rtol=1e-4) + with pytest.raises(AssertionError): + aacgmv2.convert_latlon(-91, 0, 300, self.dtime) - def test_convert_latlon_arr_list(self): - """Test array latlon conversion for list input""" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr([60, 61], [0, 0], - [300, 300], self.dtime) - - assert isinstance(self.lat_out, np.ndarray) - assert isinstance(self.lon_out, np.ndarray) - assert isinstance(self.r_out, np.ndarray) - assert (self.r_out.shape == self.lon_out.shape and - self.lat_out.shape == self.r_out.shape and - self.r_out.shape == (2,)) - - np.testing.assert_allclose(self.lat_out, [58.22577090, 59.31860933], - rtol=1e-4) - np.testing.assert_allclose(self.lon_out, [81.16846959, 81.61398933], - rtol=1e-4) - np.testing.assert_allclose(self.r_out, [1.04566346, 1.04561304], - rtol=1e-4) - - def test_convert_latlon_arr_arr_single(self): - """Test array latlon conversion for array input of shape (1,)""" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr(np.array([60]), - np.array([0]), - np.array([300]), - self.dtime) - - assert isinstance(self.lat_out, np.ndarray) - assert isinstance(self.lon_out, np.ndarray) - assert isinstance(self.r_out, np.ndarray) - assert (self.r_out.shape == self.lon_out.shape and - self.lat_out.shape == self.r_out.shape and - self.r_out.shape == (1,)) - - np.testing.assert_allclose(self.lat_out, [58.2257709], rtol=1e-4) - np.testing.assert_allclose(self.lon_out, [81.16846959], rtol=1e-4) - np.testing.assert_allclose(self.r_out, [1.04566346], rtol=1e-4) - - def test_convert_latlon_arr_arr(self): - """Test array latlon conversion for array input""" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr(np.array([60, 61]), - np.array([0, 0]), - np.array([300, 300]), - self.dtime) +class TestConvertLatLonArr(TestPyAACGMV2): + def setup(self): + """Runs before every method to create a clean testing setup""" + self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0) + self.ddate = dt.date(2015, 1, 1) + self.lat_out = None + self.lon_out = None + self.r_out = None - assert isinstance(self.lat_out, np.ndarray) - assert isinstance(self.lon_out, np.ndarray) - assert isinstance(self.r_out, np.ndarray) - assert (self.r_out.shape == self.lon_out.shape and - self.lat_out.shape == self.r_out.shape and - self.r_out.shape == (2,)) - - np.testing.assert_allclose(self.lat_out, [58.22577090, 59.31860933], - rtol=1e-4) - np.testing.assert_allclose(self.lon_out, [81.16846959, 81.61398933], - rtol=1e-4) - np.testing.assert_allclose(self.r_out, [1.04566346, 1.04561304], - rtol=1e-4) - - def test_convert_latlon_arr_list_mix(self): - """Test array latlon conversion for mixed types with list""" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr([60, 61], 0, 300, - self.dtime) - assert isinstance(self.lat_out, np.ndarray) - assert isinstance(self.lon_out, np.ndarray) - assert isinstance(self.r_out, np.ndarray) - assert (self.r_out.shape == self.lon_out.shape and - self.lat_out.shape == self.r_out.shape and - self.r_out.shape == (2,)) - - np.testing.assert_allclose(self.lat_out, [58.22577090, 59.31860933], - rtol=1e-4) - np.testing.assert_allclose(self.lon_out, [81.16846959, 81.61398933], - rtol=1e-4) - np.testing.assert_allclose(self.r_out, [1.04566346, 1.04561304], - rtol=1e-4) - - def test_convert_latlon_arr_arr_mix(self): - """Test array latlon conversion for mixed type with an array""" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr(np.array([60, 61]), 0, - 300, self.dtime) - - assert isinstance(self.lat_out, np.ndarray) - assert isinstance(self.lon_out, np.ndarray) - assert isinstance(self.r_out, np.ndarray) - assert (self.r_out.shape == self.lon_out.shape and - self.lat_out.shape == self.r_out.shape and - self.r_out.shape == (2,)) - - np.testing.assert_allclose(self.lat_out, [58.22577090, 59.31860933], - rtol=1e-4) - np.testing.assert_allclose(self.lon_out, [81.16846959, 81.61398933], - rtol=1e-4) - np.testing.assert_allclose(self.r_out, [1.04566346, 1.04561304], - rtol=1e-4) - - def test_convert_latlon_arr_mult_arr_mix(self): - """Test array latlon conversion for mix type with multi-dim array""" + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.lat_out, self.lon_out, self.r_out, self.dtime, self.ddate + + def test_convert_latlon_arr_single_val(self): + """Test array latlon conversion for a single value""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr(60, 0, 300, self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (1,)) + + np.testing.assert_allclose(self.lat_out, [58.2257709], rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959], rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346], rtol=1e-4) + + def test_convert_latlon_arr_list_single(self): + """Test array latlon conversion for list input of single values""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [300], self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (1,)) + + np.testing.assert_allclose(self.lat_out, [58.2257709], rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959], rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346], rtol=1e-4) + + def test_convert_latlon_arr_list(self): + """Test array latlon conversion for list input""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60, 61], [0, 0], [300, 300], + self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (2,)) + + np.testing.assert_allclose(self.lat_out, [58.22577090, 59.31860933], + rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959, 81.61398933], + rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346, 1.04561304], + rtol=1e-4) + + def test_convert_latlon_arr_arr_single(self): + """Test array latlon conversion for array input of shape (1,)""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr(np.array([60]), np.array([0]), + np.array([300]), self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (1,)) + + np.testing.assert_allclose(self.lat_out, [58.2257709], rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959], rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346], rtol=1e-4) + + def test_convert_latlon_arr_arr(self): + """Test array latlon conversion for array input""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr(np.array([60, 61]), + np.array([0, 0]), + np.array([300, 300]), + self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (2,)) + + np.testing.assert_allclose(self.lat_out, [58.22577090, 59.31860933], + rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959, 81.61398933], + rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346, 1.04561304], + rtol=1e-4) + + def test_convert_latlon_arr_list_mix(self): + """Test array latlon conversion for mixed types with list""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60, 61], 0, 300, self.dtime) + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (2,)) + + np.testing.assert_allclose(self.lat_out, [58.22577090, 59.31860933], + rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959, 81.61398933], + rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346, 1.04561304], + rtol=1e-4) + + def test_convert_latlon_arr_arr_mix(self): + """Test array latlon conversion for mixed type with an array""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr(np.array([60, 61]), 0, + 300, self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (2,)) + + np.testing.assert_allclose(self.lat_out, [58.22577090, 59.31860933], + rtol=1e-4) + np.testing.assert_allclose(self.lon_out, [81.16846959, 81.61398933], + rtol=1e-4) + np.testing.assert_allclose(self.r_out, [1.04566346, 1.04561304], + rtol=1e-4) + + def test_convert_latlon_arr_mult_arr_mix(self): + """Test array latlon conversion for mix type with multi-dim array""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr(np.array([[60, 61, 62], + [63, 64, 65]]), + 0, 300, self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (2, 3)) + + np.testing.assert_allclose(self.lat_out, + [[58.2257709, 59.3186093, 60.4039740], + [61.4819893, 62.5527635, 63.6163840]], + rtol=1e-4) + np.testing.assert_allclose(self.lon_out, + [[81.1684696, 81.6139893, 82.0871880], + [82.5909499, 83.1285895, 83.7039272]], + rtol=1e-4) + np.testing.assert_allclose(self.r_out, + [[1.04566346, 1.04561304, 1.04556369], + [1.04551548, 1.04546847, 1.04542272]], + rtol=1e-4) + + def test_convert_latlon_arr_mult_arr_unequal(self): + """Test array latlon conversion for unequal sized multi-dim array""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr(np.array([[60, 61, 62], + [63, 64, 65]]), + np.array([0]), + np.array([300]), self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (2, 3)) + + np.testing.assert_allclose(self.lat_out, + [[58.2257709, 59.3186093, 60.4039740], + [61.4819893, 62.5527635, 63.6163840]], + rtol=1e-4) + np.testing.assert_allclose(self.lon_out, + [[81.1684696, 81.6139893, 82.0871880], + [82.5909499, 83.1285895, 83.7039272]], + rtol=1e-4) + np.testing.assert_allclose(self.r_out, + [[1.04566346, 1.04561304, 1.04556369], + [1.04551548, 1.04546847, 1.04542272]], + rtol=1e-4) + + def test_convert_latlon_arr_badidea_failure(self): + """Test array latlon conversion failure for BADIDEA""" + code = "G2A | BADIDEA" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [3000], + self.dtime, code) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (1,)) + assert np.all([np.isnan(self.lat_out), np.isnan(self.lon_out), + np.isnan(self.r_out)]) + + def test_convert_latlon_arr_location_failure(self): + """Test array latlon conversion with a bad location""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([0], [0], [0], self.dtime) + + assert isinstance(self.lat_out, np.ndarray) + assert isinstance(self.lon_out, np.ndarray) + assert isinstance(self.r_out, np.ndarray) + assert (self.r_out.shape == self.lon_out.shape and + self.lat_out.shape == self.r_out.shape and + self.r_out.shape == (1,)) + assert np.all([np.isnan(self.lat_out), np.isnan(self.lon_out), + np.isnan(self.r_out)]) + + def test_convert_latlon_arr_time_failure(self): + """Test array latlon conversion with a bad time""" + with pytest.raises(AssertionError): (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr(np.array([[60, 61, 62], - [63, 64, 65]]), - 0, 300, self.dtime) - - assert isinstance(self.lat_out, np.ndarray) - assert isinstance(self.lon_out, np.ndarray) - assert isinstance(self.r_out, np.ndarray) - assert (self.r_out.shape == self.lon_out.shape and - self.lat_out.shape == self.r_out.shape and - self.r_out.shape == (2, 3)) - - np.testing.assert_allclose(self.lat_out, - [[58.2257709, 59.3186093, 60.4039740], - [61.4819893, 62.5527635, 63.6163840]], - rtol=1e-4) - np.testing.assert_allclose(self.lon_out, - [[81.1684696, 81.6139893, 82.0871880], - [82.5909499, 83.1285895, 83.7039272]], - rtol=1e-4) - np.testing.assert_allclose(self.r_out, - [[1.04566346, 1.04561304, 1.04556369], - [1.04551548, 1.04546847, 1.04542272]], - rtol=1e-4) - - - def test_convert_latlon_arr_mult_arr_unequal(self): - """Test array latlon conversion for unequal sized multi-dim array""" + self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [300], None) + + def test_convert_latlon_arr_datetime_date(self): + """Test array latlon conversion with date and datetime input""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [300], self.ddate) + lat_2, lon_2, r_2 = aacgmv2.convert_latlon_arr([60], [0], [300], + self.dtime) + assert self.lat_out == lat_2 + assert self.lon_out == lon_2 + assert self.r_out == r_2 + + del lat_2, lon_2, r_2 + + def test_warning_below_ground_convert_latlon_arr(self): + """ Test that a warning is issued if altitude is below zero""" + import logbook + lwarn = u"conversion not intended for altitudes < 0 km" + + with logbook.TestHandler() as handler: (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr(np.array([[60, 61, 62], - [63, 64, 65]]), - np.array([0]), - np.array([300]), + self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [-1], self.dtime) + assert handler.has_warning(lwarn) - assert isinstance(self.lat_out, np.ndarray) - assert isinstance(self.lon_out, np.ndarray) - assert isinstance(self.r_out, np.ndarray) - assert (self.r_out.shape == self.lon_out.shape and - self.lat_out.shape == self.r_out.shape and - self.r_out.shape == (2, 3)) - - np.testing.assert_allclose(self.lat_out, - [[58.2257709, 59.3186093, 60.4039740], - [61.4819893, 62.5527635, 63.6163840]], - rtol=1e-4) - np.testing.assert_allclose(self.lon_out, - [[81.1684696, 81.6139893, 82.0871880], - [82.5909499, 83.1285895, 83.7039272]], - rtol=1e-4) - np.testing.assert_allclose(self.r_out, - [[1.04566346, 1.04561304, 1.04556369], - [1.04551548, 1.04546847, 1.04542272]], - rtol=1e-4) - - def test_convert_latlon_arr_badidea_failure(self): - """Test array latlon conversion failure for BADIDEA""" - code = "G2A | BADIDEA" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [3000], - self.dtime, code) - - assert isinstance(self.lat_out, np.ndarray) - assert isinstance(self.lon_out, np.ndarray) - assert isinstance(self.r_out, np.ndarray) - assert (self.r_out.shape == self.lon_out.shape and - self.lat_out.shape == self.r_out.shape and - self.r_out.shape == (1,)) - assert np.all([np.isnan(self.lat_out), np.isnan(self.lon_out), - np.isnan(self.r_out)]) - - def test_convert_latlon_arr_location_failure(self): - """Test array latlon conversion with a bad location""" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr([0], [0], [0], self.dtime) - - assert isinstance(self.lat_out, np.ndarray) - assert isinstance(self.lon_out, np.ndarray) - assert isinstance(self.r_out, np.ndarray) - assert (self.r_out.shape == self.lon_out.shape and - self.lat_out.shape == self.r_out.shape and - self.r_out.shape == (1,)) - assert np.all([np.isnan(self.lat_out), np.isnan(self.lon_out), - np.isnan(self.r_out)]) - - def test_convert_latlon_arr_time_failure(self): - """Test array latlon conversion with a bad time""" - with pytest.raises(AssertionError): - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [300], - None) + handler.close() - def test_convert_latlon_arr_datetime_date(self): - """Test array latlon conversion with date and datetime input""" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [300], - self.ddate) - lat_2, lon_2, r_2 = aacgmv2.convert_latlon_arr([60], [0], [300], - self.dtime) - assert self.lat_out == lat_2 - assert self.lon_out == lon_2 - assert self.r_out == r_2 - - del lat_2, lon_2, r_2 - - def test_warning_below_ground_convert_latlon_arr(self): - """ Test that a warning is issued if altitude is below zero""" - import logbook - lwarn = u"conversion not intended for altitudes < 0 km" - - with logbook.TestHandler() as handler: - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [-1], - self.dtime) - assert handler.has_warning(lwarn) - - handler.close() - - def test_convert_latlon_arr_maxalt_failure(self): - """For an array, test failure for an altitude too high for - coefficients""" - (self.lat_out, self.lon_out, - self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [2001], - self.dtime) - assert np.all([np.isnan(self.lat_out), np.isnan(self.lon_out), - np.isnan(self.r_out)]) - - def test_convert_latlon_arr_lat_failure(self): - """Test error return for co-latitudes above 90 for an array""" - with pytest.raises(AssertionError): - aacgmv2.convert_latlon_arr([91, 60, -91], 0, 300, self.dtime) + def test_convert_latlon_arr_maxalt_failure(self): + """For an array, test failure for an altitude too high for + coefficients""" + (self.lat_out, self.lon_out, + self.r_out) = aacgmv2.convert_latlon_arr([60], [0], [2001], self.dtime) + assert np.all([np.isnan(self.lat_out), np.isnan(self.lon_out), + np.isnan(self.r_out)]) - class TestGetAACGMCoord: - def setup(self): - """Runs before every method to create a clean testing setup""" - self.mlat_out = None - self.mlon_out = None - self.mlt_out = None + def test_convert_latlon_arr_lat_failure(self): + """Test error return for co-latitudes above 90 for an array""" + with pytest.raises(AssertionError): + aacgmv2.convert_latlon_arr([91, 60, -91], 0, 300, self.dtime) - def teardown(self): - """Runs after every method to clean up previous testing""" - del self.mlat_out, self.mlon_out, self.mlt_out +class TestGetAACGMCoord(TestPyAACGMV2): + def setup(self): + """Runs before every method to create a clean testing setup""" + self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0) + self.ddate = dt.date(2015, 1, 1) + self.mlat_out = None + self.mlon_out = None + self.mlt_out = None - def test_get_aacgm_coord(self): - """Test single value AACGMV2 calculation, defaults to TRACE""" + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.mlat_out, self.mlon_out, self.mlt_out, self.dtime, self.ddate + + def test_get_aacgm_coord(self): + """Test single value AACGMV2 calculation, defaults to TRACE""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 300, self.dtime) + + np.testing.assert_almost_equal(self.mlat_out, 58.2247, decimal=4) + np.testing.assert_almost_equal(self.mlon_out, 81.1761, decimal=4) + np.testing.assert_almost_equal(self.mlt_out, 0.1889, decimal=4) + + def test_get_aacgm_coord_badidea_failure(self): + """Test single value AACGMV2 calculation with a bad flag""" + method = "BADIDEA" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 3000, self.dtime, + method=method) + + assert (np.isnan(self.mlat_out) & np.isnan(self.mlon_out) & + np.isnan(self.mlt_out)) + del method + + def test_get_aacgm_coord_location_failure(self): + """Test single value AACGMV2 calculation with a bad location""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(0, 0, 0, self.dtime) + assert (np.isnan(self.mlat_out) & np.isnan(self.mlon_out) & + np.isnan(self.mlt_out)) + + def test_get_aacgm_coord_time_failure(self): + """Test single value AACGMV2 calculation with a bad datetime""" + with pytest.raises(AssertionError): (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 300, self.dtime) + self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 300, None) - np.testing.assert_almost_equal(self.mlat_out, 58.2247, decimal=4) - np.testing.assert_almost_equal(self.mlon_out, 81.1761, decimal=4) - np.testing.assert_almost_equal(self.mlt_out, 0.1889, decimal=4) + def test_get_aacgm_coord_datetime_date(self): + """Test single AACGMV2 calculation with date and datetime input""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 300, self.ddate) + mlat_2, mlon_2, mlt_2 = aacgmv2.get_aacgm_coord(60, 0, 300, self.dtime) - def test_get_aacgm_coord_badidea_failure(self): - """Test single value AACGMV2 calculation with a bad flag""" - method = "BADIDEA" - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 3000, self.dtime, - method=method) + np.testing.assert_almost_equal(self.mlat_out, mlat_2, decimal=6) + np.testing.assert_almost_equal(self.mlon_out, mlon_2, decimal=6) + np.testing.assert_almost_equal(self.mlt_out, mlt_2, decimal=6) - assert (np.isnan(self.mlat_out) & np.isnan(self.mlon_out) & - np.isnan(self.mlt_out)) - del method + del mlat_2, mlon_2, mlt_2 - def test_get_aacgm_coord_location_failure(self): - """Test single value AACGMV2 calculation with a bad location""" - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord(0, 0, 0, self.dtime) - assert (np.isnan(self.mlat_out) & np.isnan(self.mlon_out) & - np.isnan(self.mlt_out)) + def test_warning_below_ground_get_aacgm_coord(self): + """ Test that a warning is issued if altitude is below zero""" + import logbook + lwarn = u"conversion not intended for altitudes < 0 km" - def test_get_aacgm_coord_time_failure(self): - """Test single value AACGMV2 calculation with a bad datetime""" + with logbook.TestHandler() as handler: + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, -1, self.dtime) + assert handler.has_warning(lwarn) + + handler.close() + + def test_get_aacgm_coord_maxalt_failure(self): + """For a single value, test failure for an altitude too high for + coefficients""" + method = "" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 2001, self.dtime, + method=method) + assert (np.isnan(self.mlat_out) & np.isnan(self.mlon_out) & + np.isnan(self.mlt_out)) + + def test_get_aacgm_coord_mlat_failure(self): + """Test error return for co-latitudes above 90 for a single value""" + import logbook + lerr = u"unrealistic latitude" + + with logbook.TestHandler() as hhigh: with pytest.raises(AssertionError): (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 300, None) - - def test_get_aacgm_coord_datetime_date(self): - """Test single AACGMV2 calculation with date and datetime input""" - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 300, self.ddate) - mlat_2, mlon_2, mlt_2 = aacgmv2.get_aacgm_coord(60, 0, 300, - self.dtime) - - np.testing.assert_almost_equal(self.mlat_out, mlat_2, decimal=6) - np.testing.assert_almost_equal(self.mlon_out, mlon_2, decimal=6) - np.testing.assert_almost_equal(self.mlt_out, mlt_2, decimal=6) - - del mlat_2, mlon_2, mlt_2 - - def test_warning_below_ground_get_aacgm_coord(self): - """ Test that a warning is issued if altitude is below zero""" - import logbook - lwarn = u"conversion not intended for altitudes < 0 km" + self.mlt_out) = aacgmv2.get_aacgm_coord(91, 0, 300, self.dtime) + assert hhigh.has_error(lerr) - with logbook.TestHandler() as handler: + with logbook.TestHandler() as hlow: + with pytest.raises(AssertionError): (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, -1, self.dtime) - assert handler.has_warning(lwarn) - - handler.close() - - def test_get_aacgm_coord_maxalt_failure(self): - """For a single value, test failure for an altitude too high for - coefficients""" - method = "" - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord(60, 0, 2001, self.dtime, - method=method) - assert (np.isnan(self.mlat_out) & np.isnan(self.mlon_out) & - np.isnan(self.mlt_out)) - - def test_get_aacgm_coord_mlat_failure(self): - """Test error return for co-latitudes above 90 for a single value""" - import logbook - lerr = u"unrealistic latitude" - - with logbook.TestHandler() as hhigh: - with pytest.raises(AssertionError): - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord(91, 0, 300, - self.dtime) - assert hhigh.has_error(lerr) - - with logbook.TestHandler() as hlow: - with pytest.raises(AssertionError): - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord(-91, 0, 300, - self.dtime) - assert hlow.has_error(lerr) - - hhigh.close() - hlow.close() - - class TestGetAACGMCoordArr: - def setup(self): - """Runs before every method to create a clean testing setup""" - self.mlat_out = None - self.mlon_out = None - self.mlt_out = None - - def teardown(self): - """Runs after every method to clean up previous testing""" - del self.mlat_out, self.mlon_out, self.mlt_out - - def test_get_aacgm_coord_arr_single_val(self): - """Test array AACGMV2 calculation for a single value""" - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr(60, 0, 300, self.dtime) - - assert isinstance(self.mlat_out, np.ndarray) - assert isinstance(self.mlon_out, np.ndarray) - assert isinstance(self.mlt_out, np.ndarray) - assert (self.mlt_out.shape == self.mlon_out.shape and - self.mlat_out.shape == self.mlt_out.shape and - self.mlt_out.shape == (1,)) - - np.testing.assert_allclose(self.mlat_out, [58.22474610], rtol=1e-4) - np.testing.assert_allclose(self.mlon_out, [81.17611033], rtol=1e-4) - np.testing.assert_allclose(self.mlt_out, [0.18891995], rtol=1e-4) - - def test_get_aacgm_coord_arr_list_single(self): - """Test array AACGMV2 calculation for list input of single values""" - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [300], - self.dtime) - - assert isinstance(self.mlat_out, np.ndarray) - assert isinstance(self.mlon_out, np.ndarray) - assert isinstance(self.mlt_out, np.ndarray) - assert (self.mlt_out.shape == self.mlon_out.shape and - self.mlat_out.shape == self.mlt_out.shape and - self.mlt_out.shape == (1,)) - - np.testing.assert_allclose(self.mlat_out, [58.22474610], rtol=1e-4) - np.testing.assert_allclose(self.mlon_out, [81.17611033], rtol=1e-4) - np.testing.assert_allclose(self.mlt_out, [0.18891995], rtol=1e-4) - - def test_get_aacgm_coord_arr_list(self): - """Test array AACGMV2 calculation for list input""" - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60, 61], [0, 0], - [300, 300], self.dtime) - - assert isinstance(self.mlat_out, np.ndarray) - assert isinstance(self.mlon_out, np.ndarray) - assert isinstance(self.mlt_out, np.ndarray) - assert (self.mlt_out.shape == self.mlon_out.shape and - self.mlat_out.shape == self.mlt_out.shape and - self.mlt_out.shape == (2,)) - - np.testing.assert_allclose(self.mlat_out, - [58.22474610, 59.31648007], rtol=1e-4) - np.testing.assert_allclose(self.mlon_out, - [81.17611033, 81.62281360], rtol=1e-4) - np.testing.assert_allclose(self.mlt_out, - [0.18891995, 0.21870017], rtol=1e-4) - - def test_get_aacgm_coord_arr_arr_single(self): - """Test array AACGMV2 calculation for array with a single value""" - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr(np.array([60]), - np.array([0]), - np.array([300]), + self.mlt_out) = aacgmv2.get_aacgm_coord(-91, 0, 300, self.dtime) + assert hlow.has_error(lerr) - assert isinstance(self.mlat_out, np.ndarray) - assert isinstance(self.mlon_out, np.ndarray) - assert isinstance(self.mlt_out, np.ndarray) - assert (self.mlt_out.shape == self.mlon_out.shape and - self.mlat_out.shape == self.mlt_out.shape and - self.mlt_out.shape == (1,)) + hhigh.close() + hlow.close() - np.testing.assert_almost_equal(self.mlat_out, 58.2247, decimal=4) - np.testing.assert_almost_equal(self.mlon_out, 81.1761, decimal=4) - np.testing.assert_almost_equal(self.mlt_out, 0.1889, decimal=4) +class TestGetAACGMCoordArr(TestPyAACGMV2): + def setup(self): + """Runs before every method to create a clean testing setup""" + self.mlat_out = None + self.mlon_out = None + self.mlt_out = None - def test_get_aacgm_coord_arr_arr(self): - """Test array AACGMV2 calculation for an array""" + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.mlat_out, self.mlon_out, self.mlt_out + + def test_get_aacgm_coord_arr_single_val(self): + """Test array AACGMV2 calculation for a single value""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr(60, 0, 300, self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (1,)) + + np.testing.assert_allclose(self.mlat_out, [58.22474610], rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, [81.17611033], rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, [0.18891995], rtol=1e-4) + + def test_get_aacgm_coord_arr_list_single(self): + """Test array AACGMV2 calculation for list input of single values""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [300], + self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (1,)) + + np.testing.assert_allclose(self.mlat_out, [58.22474610], rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, [81.17611033], rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, [0.18891995], rtol=1e-4) + + def test_get_aacgm_coord_arr_list(self): + """Test array AACGMV2 calculation for list input""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60, 61], [0, 0], + [300, 300], self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (2,)) + + np.testing.assert_allclose(self.mlat_out, + [58.22474610, 59.31648007], rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, + [81.17611033, 81.62281360], rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, + [0.18891995, 0.21870017], rtol=1e-4) + + def test_get_aacgm_coord_arr_arr_single(self): + """Test array AACGMV2 calculation for array with a single value""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr(np.array([60]), + np.array([0]), + np.array([300]), + self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (1,)) + + np.testing.assert_almost_equal(self.mlat_out, 58.2247, decimal=4) + np.testing.assert_almost_equal(self.mlon_out, 81.1761, decimal=4) + np.testing.assert_almost_equal(self.mlt_out, 0.1889, decimal=4) + + def test_get_aacgm_coord_arr_arr(self): + """Test array AACGMV2 calculation for an array""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr(np.array([60, 61]), + np.array([0, 0]), + np.array([300, 300]), + self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (2,)) + + np.testing.assert_allclose(self.mlat_out, + [58.22474610, 59.31648007], rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, + [81.17611033, 81.62281360], rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, + [0.18891995, 0.21870017], rtol=1e-4) + + def test_get_aacgm_coord_arr_list_mix(self): + """Test array AACGMV2 calculation for a list and floats""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60, 61], 0, 300, + self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (2,)) + + np.testing.assert_allclose(self.mlat_out, + [58.22474610, 59.31648007], rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, + [81.17611033, 81.62281360], rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, + [0.18891995, 0.21870017], rtol=1e-4) + + def test_get_aacgm_coord_arr_arr_mix(self): + """Test array AACGMV2 calculation for an array and floats""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr(np.array([60, 61]), 0, + 300, self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (2,)) + + np.testing.assert_allclose(self.mlat_out, + [58.22474610, 59.31648007], rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, + [81.17611033, 81.62281360], rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, + [0.18891995, 0.21870017], rtol=1e-4) + + def test_get_aacgm_coord_arr_mult_arr_mix(self): + """Test array AACGMV2 calculation for a multi-dim array and + floats""" + mlat_in = np.array([[60, 61, 62], [63, 64, 65]]) + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr(mlat_in, 0, 300, + self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (2, 3)) + + np.testing.assert_allclose(self.mlat_out, + [[58.2247461, 59.3164801, 60.4008651], + [61.4780560, 62.5481858, 63.6113609]], + rtol=1e-4) + np.testing.assert_allclose(self.mlon_out, + [[81.1761103, 81.6228136, 82.0969646], + [82.6013918, 83.1393547, 83.7146224]], + rtol=1e-4) + np.testing.assert_allclose(self.mlt_out, + [[0.18891995, 0.21870017, 0.25031024], + [0.28393872, 0.31980291, 0.35815409]], + rtol=1e-4) + del mlat_in + + def test_get_aacgm_coord_arr_arr_unequal(self): + """Test array AACGMV2 calculation for unequal arrays""" + mlat_in = np.array([[60, 61, 62], [63, 64, 65]]) + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr(mlat_in, np.array([0]), + np.array([300]), + self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (2, 3)) + + np.testing.assert_allclose(self.mlat_out, + [[58.2247, 59.3165, 60.4009], + [61.4781, 62.5482, 63.6114]], rtol=1e-3) + np.testing.assert_allclose(self.mlon_out, + [[81.1761, 81.6228, 82.0970], + [82.6014, 83.1394, 83.7146]], rtol=1e-3) + np.testing.assert_allclose(self.mlt_out, + [[0.1889, 0.2187, 0.2503], + [0.2839, 0.3198, 0.3582]], rtol=1e-3) + del mlat_in + + def test_get_aacgm_coord_arr_badidea_failure(self): + """Test array AACGMV2 calculation failure for BADIDEA""" + method = "BADIDEA" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [3000], + self.dtime, method=method) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (1,)) + assert np.all([np.isnan(self.mlat_out), np.isnan(self.mlon_out), + np.isnan(self.mlt_out)]) + del method + + def test_get_aacgm_coord_arr_location_failure(self): + """Test array AACGMV2 calculation with a bad location""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([0], [0], [0], self.dtime) + + assert isinstance(self.mlat_out, np.ndarray) + assert isinstance(self.mlon_out, np.ndarray) + assert isinstance(self.mlt_out, np.ndarray) + assert (self.mlt_out.shape == self.mlon_out.shape and + self.mlat_out.shape == self.mlt_out.shape and + self.mlt_out.shape == (1,)) + assert np.all([np.isnan(self.mlat_out), np.isnan(self.mlon_out), + np.isnan(self.mlt_out)]) + + def test_get_aacgm_coord_arr_time_failure(self): + """Test array AACGMV2 calculation with a bad time""" + with pytest.raises(AssertionError): (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr(np.array([60, 61]), - np.array([0, 0]), - np.array([300, 300]), - self.dtime) + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [300], + None) + + def test_get_aacgm_coord_arr_datetime_date(self): + """Test array AACGMV2 calculation with date and datetime input""" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [300], + self.ddate) + mlat_2, mlon_2, mlt_2 = aacgmv2.get_aacgm_coord_arr([60], [0], [300], + self.dtime) - assert isinstance(self.mlat_out, np.ndarray) - assert isinstance(self.mlon_out, np.ndarray) - assert isinstance(self.mlt_out, np.ndarray) - assert (self.mlt_out.shape == self.mlon_out.shape and - self.mlat_out.shape == self.mlt_out.shape and - self.mlt_out.shape == (2,)) - - np.testing.assert_allclose(self.mlat_out, - [58.22474610, 59.31648007], rtol=1e-4) - np.testing.assert_allclose(self.mlon_out, - [81.17611033, 81.62281360], rtol=1e-4) - np.testing.assert_allclose(self.mlt_out, - [0.18891995, 0.21870017], rtol=1e-4) - - def test_get_aacgm_coord_arr_list_mix(self): - """Test array AACGMV2 calculation for a list and floats""" - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60, 61], 0, 300, - self.dtime) + np.testing.assert_almost_equal(self.mlat_out, mlat_2, decimal=6) + np.testing.assert_almost_equal(self.mlon_out, mlon_2, decimal=6) + np.testing.assert_almost_equal(self.mlt_1, mlt_2, decimal=6) - assert isinstance(self.mlat_out, np.ndarray) - assert isinstance(self.mlon_out, np.ndarray) - assert isinstance(self.mlt_out, np.ndarray) - assert (self.mlt_out.shape == self.mlon_out.shape and - self.mlat_out.shape == self.mlt_out.shape and - self.mlt_out.shape == (2,)) - - np.testing.assert_allclose(self.mlat_out, - [58.22474610, 59.31648007], rtol=1e-4) - np.testing.assert_allclose(self.mlon_out, - [81.17611033, 81.62281360], rtol=1e-4) - np.testing.assert_allclose(self.mlt_out, - [0.18891995, 0.21870017], rtol=1e-4) - - def test_get_aacgm_coord_arr_arr_mix(self): - """Test array AACGMV2 calculation for an array and floats""" - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr(np.array([60, 61]), 0, - 300, self.dtime) - - assert isinstance(self.mlat_out, np.ndarray) - assert isinstance(self.mlon_out, np.ndarray) - assert isinstance(self.mlt_out, np.ndarray) - assert (self.mlt_out.shape == self.mlon_out.shape and - self.mlat_out.shape == self.mlt_out.shape and - self.mlt_out.shape == (2,)) - - np.testing.assert_allclose(self.mlat_out, - [58.22474610, 59.31648007], rtol=1e-4) - np.testing.assert_allclose(self.mlon_out, - [81.17611033, 81.62281360], rtol=1e-4) - np.testing.assert_allclose(self.mlt_out, - [0.18891995, 0.21870017], rtol=1e-4) - - def test_get_aacgm_coord_arr_mult_arr_mix(self): - """Test array AACGMV2 calculation for a multi-dim array and - floats""" - mlat_in = np.array([[60, 61, 62], [63, 64, 65]]) - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr(mlat_in, 0, 300, - self.dtime) + del mlat_2, mlon_2, mlt_2 - assert isinstance(self.mlat_out, np.ndarray) - assert isinstance(self.mlon_out, np.ndarray) - assert isinstance(self.mlt_out, np.ndarray) - assert (self.mlt_out.shape == self.mlon_out.shape and - self.mlat_out.shape == self.mlt_out.shape and - self.mlt_out.shape == (2, 3)) - - np.testing.assert_allclose(self.mlat_out, - [[58.2247461, 59.3164801, 60.4008651], - [61.4780560, 62.5481858, 63.6113609]], - rtol=1e-4) - np.testing.assert_allclose(self.mlon_out, - [[81.1761103, 81.6228136, 82.0969646], - [82.6013918, 83.1393547, 83.7146224]], - rtol=1e-4) - np.testing.assert_allclose(self.mlt_out, - [[0.18891995, 0.21870017, 0.25031024], - [0.28393872, 0.31980291, 0.35815409]], - rtol=1e-4) - del mlat_in - - def test_get_aacgm_coord_arr_arr_unequal(self): - """Test array AACGMV2 calculation for unequal arrays""" - mlat_in = np.array([[60, 61, 62], [63, 64, 65]]) - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr(mlat_in, np.array([0]), - np.array([300]), - self.dtime) + def test_warning_below_ground_get_aacgm_coord_arr(self): + """ Test that a warning is issued if altitude is below zero""" + import logbook + lwarn = u"conversion not intended for altitudes < 0 km" - assert isinstance(self.mlat_out, np.ndarray) - assert isinstance(self.mlon_out, np.ndarray) - assert isinstance(self.mlt_out, np.ndarray) - assert (self.mlt_out.shape == self.mlon_out.shape and - self.mlat_out.shape == self.mlt_out.shape and - self.mlt_out.shape == (2, 3)) - - np.testing.assert_allclose(self.mlat_out, - [[58.2247, 59.3165, 60.4009], - [61.4781, 62.5482, 63.6114]], rtol=1e-3) - np.testing.assert_allclose(self.mlon_out, - [[81.1761, 81.6228, 82.0970], - [82.6014, 83.1394, 83.7146]], rtol=1e-3) - np.testing.assert_allclose(self.mlt_out, - [[0.1889, 0.2187, 0.2503], - [0.2839, 0.3198, 0.3582]], rtol=1e-3) - del mlat_in - - def test_get_aacgm_coord_arr_badidea_failure(self): - """Test array AACGMV2 calculation failure for BADIDEA""" - method = "BADIDEA" - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [3000], - self.dtime, - method=method) - - assert isinstance(self.mlat_out, np.ndarray) - assert isinstance(self.mlon_out, np.ndarray) - assert isinstance(self.mlt_out, np.ndarray) - assert (self.mlt_out.shape == self.mlon_out.shape and - self.mlat_out.shape == self.mlt_out.shape and - self.mlt_out.shape == (1,)) - assert np.all([np.isnan(self.mlat_out), np.isnan(self.mlon_out), - np.isnan(self.mlt_out)]) - del method - - def test_get_aacgm_coord_arr_location_failure(self): - """Test array AACGMV2 calculation with a bad location""" + with logbook.TestHandler() as handler: (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr([0], [0], [0], + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [-1], self.dtime) - - assert isinstance(self.mlat_out, np.ndarray) - assert isinstance(self.mlon_out, np.ndarray) - assert isinstance(self.mlt_out, np.ndarray) - assert (self.mlt_out.shape == self.mlon_out.shape and - self.mlat_out.shape == self.mlt_out.shape and - self.mlt_out.shape == (1,)) - assert np.all([np.isnan(self.mlat_out), np.isnan(self.mlon_out), - np.isnan(self.mlt_out)]) - - def test_get_aacgm_coord_arr_time_failure(self): - """Test array AACGMV2 calculation with a bad time""" + assert handler.has_warning(lwarn) + + handler.close() + + def test_get_aacgm_coord_arr_maxalt_failure(self): + """For an array, test failure for an altitude too high for + coefficients""" + method = "" + (self.mlat_out, self.mlon_out, + self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [2001], + self.dtime, method=method) + assert np.all([np.isnan(self.mlat_out), np.isnan(self.mlon_out), + np.isnan(self.mlt_out)]) + del method + + def test_get_aacgm_coord_arr_mlat_failure(self): + """Test error return for co-latitudes above 90 for an array""" + import logbook + lerr = u"unrealistic latitude" + + with logbook.TestHandler() as handler: with pytest.raises(AssertionError): - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [300], - None) + aacgmv2.get_aacgm_coord_arr([91, 60, -91], 0, 300, + self.dtime) + assert handler.has_error(lerr) + + handler.close() + +class TestConvertCode(TestPyAACGMV2): + def test_convert_str_to_bit_g2a(self): + """Test conversion from string code to bit G2A""" + assert aacgmv2.convert_str_to_bit("G2A") == aacgmv2._aacgmv2.G2A + + def test_convert_str_to_bit_a2g(self): + """Test conversion from string code to bit A2G""" + assert aacgmv2.convert_str_to_bit("A2G") == aacgmv2._aacgmv2.A2G + + def test_convert_str_to_bit_trace(self): + """Test conversion from string code to bit TRACE""" + assert aacgmv2.convert_str_to_bit("TRACE") == aacgmv2._aacgmv2.TRACE + + def test_convert_str_to_bit_allowtrace(self): + """Test conversion from string code to bit ALLOWTRACE""" + assert aacgmv2.convert_str_to_bit("ALLOWTRACE") == \ + aacgmv2._aacgmv2.ALLOWTRACE + + def test_convert_str_to_bit_badidea(self): + """Test conversion from string code to bit BADIDEA""" + assert aacgmv2.convert_str_to_bit("BADIDEA") == \ + aacgmv2._aacgmv2.BADIDEA + + def test_convert_str_to_bit_geocentric(self): + """Test conversion from string code to bit GEOCENTRIC""" + assert aacgmv2.convert_str_to_bit("GEOCENTRIC") == \ + aacgmv2._aacgmv2.GEOCENTRIC + + def test_convert_str_to_bit_lowercase(self): + """Test conversion from string code to bit for a lowercase code""" + assert aacgmv2.convert_str_to_bit("g2a") == aacgmv2._aacgmv2.G2A + + def test_convert_str_to_bit_spaces(self): + """Test conversion from string code to bit for a code with spaces""" + assert aacgmv2.convert_str_to_bit("G2A | trace") == \ + aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.TRACE + + def test_convert_str_to_bit_invalid(self): + """Test conversion from string code to bit for an invalid code""" + assert aacgmv2.convert_str_to_bit("ggoogg|") == aacgmv2._aacgmv2.G2A + + def test_convert_bool_to_bit_g2a(self): + """Test conversion from string code to bit G2A""" + assert aacgmv2.convert_bool_to_bit() == aacgmv2._aacgmv2.G2A + + def test_convert_bool_to_bit_a2g(self): + """Test conversion from string code to bit A2G""" + assert aacgmv2.convert_bool_to_bit(a2g=True) == aacgmv2._aacgmv2.A2G + + def test_convert_bool_to_bit_trace(self): + """Test conversion from string code to bit TRACE""" + assert aacgmv2.convert_bool_to_bit(trace=True) == aacgmv2._aacgmv2.TRACE + + def test_convert_bool_to_bit_allowtrace(self): + """Test conversion from string code to bit ALLOWTRACE""" + assert aacgmv2.convert_bool_to_bit(allowtrace=True) == \ + aacgmv2._aacgmv2.ALLOWTRACE + + def test_convert_bool_to_bit_badidea(self): + """Test conversion from string code to bit BADIDEA""" + assert aacgmv2.convert_bool_to_bit(badidea=True) == \ + aacgmv2._aacgmv2.BADIDEA + + def test_convert_bool_to_bit_geocentric(self): + """Test conversion from string code to bit GEOCENTRIC""" + assert aacgmv2.convert_bool_to_bit(geocentric=True) == \ + aacgmv2._aacgmv2.GEOCENTRIC + +class TestMLTConvert(TestPyAACGMV2): + def setup(self): + """Runs before every method to create a clean testing setup""" + self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0) + self.ddate = dt.date(2015, 1, 1) + self.mlon_out = None + self.mlt_out = None + self.mlon_list = [270.0, 80.0, -95.0] + self.mlt_list = [12.0, 25.0, -1.0] + self.mlon_comp = [-101.657689, 93.34231102, 63.34231102] + self.mlt_comp = [12.77717927, 0.1105126, 12.44384593] - def test_get_aacgm_coord_arr_datetime_date(self): - """Test array AACGMV2 calculation with date and datetime input""" - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [300], - self.ddate) - mlat_2, mlon_2, mlt_2 = aacgmv2.get_aacgm_coord_arr([60], [0], - [300], - self.dtime) + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.mlon_out, self.mlt_out, self.mlt_list, self.mlon_list + del self.mlon_comp, self.mlt_comp + + def test_inv_convert_mlt_single(self): + """Test MLT inversion for a single value""" + for i,mlt in enumerate(self.mlt_list): + self.mlon_out = aacgmv2.convert_mlt(mlt, self.dtime, m2a=True) + np.testing.assert_almost_equal(self.mlon_out, self.mlon_comp[i], + decimal=4) + + def test_inv_convert_mlt_list(self): + """Test MLT inversion for a list""" + self.mlon_out = aacgmv2.convert_mlt(self.mlt_list, self.dtime, m2a=True) + np.testing.assert_allclose(self.mlon_out, self.mlon_comp, rtol=1.0e-4) + + def test_inv_convert_mlt_arr(self): + """Test MLT inversion for an array""" + self.mlon_out = aacgmv2.convert_mlt(np.array(self.mlt_list), self.dtime, + m2a=True) + + np.testing.assert_allclose(self.mlon_out, self.mlon_comp, rtol=1.0e-4) + + def test_inv_convert_mlt_wrapping(self): + """Test MLT wrapping""" + self.mlon_out = aacgmv2.convert_mlt(np.array([1, 25, -1, 23]), + self.dtime, m2a=True) + + np.testing.assert_almost_equal(self.mlon_out[0], self.mlon_out[1], + decimal=6) + np.testing.assert_almost_equal(self.mlon_out[2], self.mlon_out[3], + decimal=6) + + def test_mlt_convert_mlon_wrapping(self): + """Test mlon wrapping""" + self.mlt_out = aacgmv2.convert_mlt(np.array([270, -90, 1, 361]), + self.dtime, m2a=False) + + np.testing.assert_almost_equal(self.mlt_out[0], self.mlt_out[1], + decimal=6) + np.testing.assert_almost_equal(self.mlt_out[2], self.mlt_out[3], + decimal=6) + + def test_mlt_convert_single(self): + """Test MLT calculation for a single value""" + for i,mlon in enumerate(self.mlon_list): + self.mlt_out = aacgmv2.convert_mlt(mlon, self.dtime, m2a=False) + np.testing.assert_almost_equal(self.mlt_out, self.mlt_comp[i], + decimal=4) + + def test_mlt_convert_list(self): + """Test MLT calculation for a list""" + self.mlt_out = aacgmv2.convert_mlt(self.mlon_list, self.dtime, + m2a=False) + np.testing.assert_allclose(self.mlt_out, self.mlt_comp, rtol=1.0e-4) + + def test_mlt_convert_arr(self): + """Test MLT calculation for an array""" + self.mlt_out = aacgmv2.convert_mlt(np.array(self.mlon_list), + self.dtime, m2a=False) + np.testing.assert_allclose(self.mlt_out, self.mlt_comp, rtol=1.0e-4) + +class TestCoeffPath(TestPyAACGMV2): + def setup(self): + """Runs before every method to create a clean testing setup""" + self.igrf_out = None + self.aacgm_out = None - np.testing.assert_almost_equal(self.mlat_out, mlat_2, decimal=6) - np.testing.assert_almost_equal(self.mlon_out, mlon_2, decimal=6) - np.testing.assert_almost_equal(self.mlt_1, mlt_2, decimal=6) + def teardown(self): + """Runs after every method to clean up previous testing""" + del self.igrf_out, self.aacgm_out - del mlat_2, mlon_2, mlt_2 + def test_set_coeff_path_default(self): + """Test the coefficient path setting using defaults""" + self.igrf_out, self.coeff_out = aacgmv2.set_coeff_path() - def test_warning_below_ground_get_aacgm_coord_arr(self): - """ Test that a warning is issued if altitude is below zero""" - import logbook - lwarn = u"conversion not intended for altitudes < 0 km" + assert self.igrf_out == aacgmv2.IGRF_12_COEFF + assert self.coeff_out == aacgmv2.AACGM_V2_DAT_PREFIX - with logbook.TestHandler() as handler: - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [-1], - self.dtime) - assert handler.has_warning(lwarn) + def test_set_coeff_path_different(self): + """Test the coefficient path setting""" + self.igrf_out, self.coeff_out = aacgmv2.set_coeff_path("hi", "bye") - handler.close() + assert self.igrf_out == "hi" + assert self.coeff_out == "bye" - def test_get_aacgm_coord_arr_maxalt_failure(self): - """For an array, test failure for an altitude too high for - coefficients""" - method = "" - (self.mlat_out, self.mlon_out, - self.mlt_out) = aacgmv2.get_aacgm_coord_arr([60], [0], [2001], - self.dtime, - method=method) - assert np.all([np.isnan(self.mlat_out), np.isnan(self.mlon_out), - np.isnan(self.mlt_out)]) - del method - - def test_get_aacgm_coord_arr_mlat_failure(self): - """Test error return for co-latitudes above 90 for an array""" - import logbook - lerr = u"unrealistic latitude" - - with logbook.TestHandler() as handler: - with pytest.raises(AssertionError): - aacgmv2.get_aacgm_coord_arr([91, 60, -91], 0, 300, - self.dtime) - assert handler.has_error(lerr) - - handler.close() - - class TestConvertCode: - def test_convert_str_to_bit_g2a(self): - """Test conversion from string code to bit G2A""" - assert aacgmv2.convert_str_to_bit("G2A") == aacgmv2._aacgmv2.G2A - - def test_convert_str_to_bit_a2g(self): - """Test conversion from string code to bit A2G""" - assert aacgmv2.convert_str_to_bit("A2G") == aacgmv2._aacgmv2.A2G - - def test_convert_str_to_bit_trace(self): - """Test conversion from string code to bit TRACE""" - assert aacgmv2.convert_str_to_bit("TRACE") == aacgmv2._aacgmv2.TRACE - - def test_convert_str_to_bit_allowtrace(self): - """Test conversion from string code to bit ALLOWTRACE""" - assert aacgmv2.convert_str_to_bit("ALLOWTRACE") == \ - aacgmv2._aacgmv2.ALLOWTRACE - - def test_convert_str_to_bit_badidea(self): - """Test conversion from string code to bit BADIDEA""" - assert aacgmv2.convert_str_to_bit("BADIDEA") == \ - aacgmv2._aacgmv2.BADIDEA - - def test_convert_str_to_bit_geocentric(self): - """Test conversion from string code to bit GEOCENTRIC""" - assert aacgmv2.convert_str_to_bit("GEOCENTRIC") == \ - aacgmv2._aacgmv2.GEOCENTRIC - - def test_convert_str_to_bit_lowercase(self): - """Test conversion from string code to bit for a lowercase code""" - assert aacgmv2.convert_str_to_bit("g2a") == aacgmv2._aacgmv2.G2A - - def test_convert_str_to_bit_spaces(self): - """Test conversion from string code to bit for a code with spaces""" - assert aacgmv2.convert_str_to_bit("G2A | trace") == \ - aacgmv2._aacgmv2.G2A + aacgmv2._aacgmv2.TRACE - - def test_convert_str_to_bit_invalid(self): - """Test conversion from string code to bit for an invalid code""" - assert aacgmv2.convert_str_to_bit("ggoogg|") == aacgmv2._aacgmv2.G2A - - def test_convert_bool_to_bit_g2a(self): - """Test conversion from string code to bit G2A""" - assert aacgmv2.convert_bool_to_bit() == aacgmv2._aacgmv2.G2A - - def test_convert_bool_to_bit_a2g(self): - """Test conversion from string code to bit A2G""" - assert aacgmv2.convert_bool_to_bit(a2g=True) == aacgmv2._aacgmv2.A2G - - def test_convert_bool_to_bit_trace(self): - """Test conversion from string code to bit TRACE""" - assert aacgmv2.convert_bool_to_bit(trace=True) == \ - aacgmv2._aacgmv2.TRACE - - def test_convert_bool_to_bit_allowtrace(self): - """Test conversion from string code to bit ALLOWTRACE""" - assert aacgmv2.convert_bool_to_bit(allowtrace=True) == \ - aacgmv2._aacgmv2.ALLOWTRACE - - def test_convert_bool_to_bit_badidea(self): - """Test conversion from string code to bit BADIDEA""" - assert aacgmv2.convert_bool_to_bit(badidea=True) == \ - aacgmv2._aacgmv2.BADIDEA - - def test_convert_bool_to_bit_geocentric(self): - """Test conversion from string code to bit GEOCENTRIC""" - assert aacgmv2.convert_bool_to_bit(geocentric=True) == \ - aacgmv2._aacgmv2.GEOCENTRIC - - class TestMLTConvert: - def setup(self): - """Runs before every method to create a clean testing setup""" - self.mlon_out = None - self.mlt_out = None - self.mlon_list = [270.0, 80.0, -95.0] - self.mlt_list = [12.0, 25.0, -1.0] - self.mlon_comp = [-101.657689, 93.34231102, 63.34231102] - self.mlt_comp = [12.77717927, 0.1105126, 12.44384593] - - def teardown(self): - """Runs after every method to clean up previous testing""" - del self.mlon_out, self.mlt_out, self.mlt_list, self.mlon_list - del self.mlon_comp, self.mlt_comp - - def test_inv_convert_mlt_single(self): - """Test MLT inversion for a single value""" - for i,mlt in enumerate(self.mlt_list): - self.mlon_out = aacgmv2.convert_mlt(mlt, self.dtime, m2a=True) - np.testing.assert_almost_equal(self.mlon_out, self.mlon_comp[i], - decimal=4) - - def test_inv_convert_mlt_list(self): - """Test MLT inversion for a list""" - self.mlon_out = aacgmv2.convert_mlt(self.mlt_list, self.dtime, - m2a=True) - np.testing.assert_allclose(self.mlon_out, self.mlon_comp, - rtol=1.0e-4) - - def test_inv_convert_mlt_arr(self): - """Test MLT inversion for an array""" - self.mlon_out = aacgmv2.convert_mlt(np.array(self.mlt_list), - self.dtime, m2a=True) - - np.testing.assert_allclose(self.mlon_out, self.mlon_comp, - rtol=1.0e-4) - - def test_inv_convert_mlt_wrapping(self): - """Test MLT wrapping""" - self.mlon_out = aacgmv2.convert_mlt(np.array([1, 25, -1, 23]), - self.dtime, m2a=True) - - np.testing.assert_almost_equal(self.mlon_out[0], self.mlon_out[1], - decimal=6) - np.testing.assert_almost_equal(self.mlon_out[2], self.mlon_out[3], - decimal=6) - - def test_mlt_convert_mlon_wrapping(self): - """Test mlon wrapping""" - self.mlt_out = aacgmv2.convert_mlt(np.array([270, -90, 1, 361]), - self.dtime, m2a=False) - - np.testing.assert_almost_equal(self.mlt_out[0], self.mlt_out[1], - decimal=6) - np.testing.assert_almost_equal(self.mlt_out[2], self.mlt_out[3], - decimal=6) - - def test_mlt_convert_single(self): - """Test MLT calculation for a single value""" - for i,mlon in enumerate(self.mlon_list): - self.mlt_out = aacgmv2.convert_mlt(mlon, self.dtime, m2a=False) - np.testing.assert_almost_equal(self.mlt_out, self.mlt_comp[i], - decimal=4) - - def test_mlt_convert_list(self): - """Test MLT calculation for a list""" - self.mlt_out = aacgmv2.convert_mlt(self.mlon_list, self.dtime, - m2a=False) - np.testing.assert_allclose(self.mlt_out, self.mlt_comp, rtol=1.0e-4) - - def test_mlt_convert_arr(self): - """Test MLT calculation for an array""" - self.mlt_out = aacgmv2.convert_mlt(np.array(self.mlon_list), - self.dtime, m2a=False) - np.testing.assert_allclose(self.mlt_out, self.mlt_comp, rtol=1.0e-4) - - class TestCoeffPath: - def setup(self): - """Runs before every method to create a clean testing setup""" - self.igrf_out = None - self.aacgm_out = None - - def teardown(self): - """Runs after every method to clean up previous testing""" - del self.igrf_out, self.aacgm_out - - def test_set_coeff_path_default(self): - """Test the coefficient path setting using defaults""" - self.igrf_out, self.coeff_out = aacgmv2.set_coeff_path() - - assert self.igrf_out == aacgmv2.IGRF_12_COEFF - assert self.coeff_out == aacgmv2.AACGM_V2_DAT_PREFIX - - def test_set_coeff_path_different(self): - """Test the coefficient path setting""" - self.igrf_out, self.coeff_out = aacgmv2.set_coeff_path("hi", "bye") - - assert self.igrf_out == "hi" - assert self.coeff_out == "bye" - - def test_set_coeff_path_mix(self): - """Test the coefficient path setting using a mix of input""" - self.igrf_out, self.coeff_out = aacgmv2.set_coeff_path( \ - coeff_prefix="hi") - - assert self.igrf_out == aacgmv2.IGRF_12_COEFF - assert self.coeff_out == "hi" + def test_set_coeff_path_mix(self): + """Test the coefficient path setting using a mix of input""" + (self.igrf_out, + self.coeff_out) = aacgmv2.set_coeff_path(coeff_prefix="hi") + + assert self.igrf_out == aacgmv2.IGRF_12_COEFF + assert self.coeff_out == "hi" From 2ddf818e0ceb1341fab28dbe677153bb5bd73f69 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 15:43:56 -0500 Subject: [PATCH 087/101] debugging changes Debugging changes made. --- aacgmv2/__init__.py | 2 +- aacgmv2/tests/test_c_aacgmv2.py | 4 +--- aacgmv2/tests/test_dep_aacgmv2.py | 11 ---------- aacgmv2/tests/test_py_aacgmv2.py | 36 ++++++++++++++----------------- aacgmv2/wrapper.py | 2 +- 5 files changed, 19 insertions(+), 36 deletions(-) diff --git a/aacgmv2/__init__.py b/aacgmv2/__init__.py index 003e0b2b..c86c2521 100644 --- a/aacgmv2/__init__.py +++ b/aacgmv2/__init__.py @@ -60,7 +60,7 @@ from aacgmv2.wrapper import (convert_latlon, convert_mlt, get_aacgm_coord) from aacgmv2.wrapper import (convert_latlon_arr, get_aacgm_coord_arr) from aacgmv2.wrapper import (convert_bool_to_bit, convert_str_to_bit) - from aacgmv2.wrapper import (et_coeff_path) + from aacgmv2.wrapper import (set_coeff_path) except Exception as err: logging.exception(__file__ + ' -> aacgmv2: ' + str(err)) diff --git a/aacgmv2/tests/test_c_aacgmv2.py b/aacgmv2/tests/test_c_aacgmv2.py index 55eda0d9..dd245e20 100644 --- a/aacgmv2/tests/test_c_aacgmv2.py +++ b/aacgmv2/tests/test_c_aacgmv2.py @@ -85,7 +85,7 @@ def test_convert_A2G_coeff(self): r_comp = [1133.6241, 305.7138] for i,darg in enumerate(self.date_args): - aacgmv2._aacgmv2.set_datetime(*self.date1_args) + aacgmv2._aacgmv2.set_datetime(*self.date_args[i]) (self.mlat, self.mlon, self.rshell) = aacgmv2._aacgmv2.convert(self.lat_in[i], self.lon_in[i], @@ -256,8 +256,6 @@ def test_forbidden(self): mloc = aacgmv2._aacgmv2.convert(7, 0, 0, aacgmv2._aacgmv2.G2A, aacgmv2.IGRF_12_COEFFS) - del mloc - def test_inv_mlt_convert(self): """Test MLT inversion""" mlt_args = list(self.long_date) diff --git a/aacgmv2/tests/test_dep_aacgmv2.py b/aacgmv2/tests/test_dep_aacgmv2.py index 14af67a9..e1a3356d 100644 --- a/aacgmv2/tests/test_dep_aacgmv2.py +++ b/aacgmv2/tests/test_dep_aacgmv2.py @@ -28,17 +28,6 @@ def test_module_structure(self): assert aacgmv2.deprecated.gc2gd_lat assert aacgmv2.deprecated.igrf_dipole_axis - def test_set_coeff_path(self): - """Test the deprecated routine for appropriate warning""" - import logbook - lwarn = u"this routine is no longer needed" - - with logbook.TestHandler() as handler: - aacgmv2.set_coeff_path() - assert handler.has_warning(lwarn) - - handler.close() - def test_convert_single_val(self): """Test conversion for a single value""" self.lat, self.lon = aacgmv2.convert(60, 0, 300, self.dtime) diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index 9417adb8..d75b3eeb 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -7,12 +7,6 @@ import aacgmv2 class TestPyAACGMV2: - def setup(self): - """Runs before every method to create a clean testing setup""" - - def teardown(self): - """Runs after every method to clean up previous testing""" - del self.date_args, self.dtime, self.ddate def test_module_structure(self): """Test module structure""" @@ -39,7 +33,7 @@ def test_module_parameters(self): del path1, path2 -class TestConvertLatLon(TestPyAACGMV2): +class TestConvertLatLon: def setup(self): """Runs before every method to create a clean testing setup""" @@ -123,7 +117,7 @@ def test_convert_latlon_lat_failure(self): with pytest.raises(AssertionError): aacgmv2.convert_latlon(-91, 0, 300, self.dtime) -class TestConvertLatLonArr(TestPyAACGMV2): +class TestConvertLatLonArr: def setup(self): """Runs before every method to create a clean testing setup""" self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0) @@ -394,7 +388,7 @@ def test_convert_latlon_arr_lat_failure(self): with pytest.raises(AssertionError): aacgmv2.convert_latlon_arr([91, 60, -91], 0, 300, self.dtime) -class TestGetAACGMCoord(TestPyAACGMV2): +class TestGetAACGMCoord: def setup(self): """Runs before every method to create a clean testing setup""" self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0) @@ -495,16 +489,18 @@ def test_get_aacgm_coord_mlat_failure(self): hhigh.close() hlow.close() -class TestGetAACGMCoordArr(TestPyAACGMV2): +class TestGetAACGMCoordArr: def setup(self): """Runs before every method to create a clean testing setup""" + self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0) + self.ddate = dt.date(2015, 1, 1) self.mlat_out = None self.mlon_out = None self.mlt_out = None def teardown(self): """Runs after every method to clean up previous testing""" - del self.mlat_out, self.mlon_out, self.mlt_out + del self.mlat_out, self.mlon_out, self.mlt_out, self.dtime, self.ddate def test_get_aacgm_coord_arr_single_val(self): """Test array AACGMV2 calculation for a single value""" @@ -743,7 +739,7 @@ def test_get_aacgm_coord_arr_datetime_date(self): np.testing.assert_almost_equal(self.mlat_out, mlat_2, decimal=6) np.testing.assert_almost_equal(self.mlon_out, mlon_2, decimal=6) - np.testing.assert_almost_equal(self.mlt_1, mlt_2, decimal=6) + np.testing.assert_almost_equal(self.mlt_out, mlt_2, decimal=6) del mlat_2, mlon_2, mlt_2 @@ -784,7 +780,7 @@ def test_get_aacgm_coord_arr_mlat_failure(self): handler.close() -class TestConvertCode(TestPyAACGMV2): +class TestConvertCode: def test_convert_str_to_bit_g2a(self): """Test conversion from string code to bit G2A""" assert aacgmv2.convert_str_to_bit("G2A") == aacgmv2._aacgmv2.G2A @@ -852,7 +848,7 @@ def test_convert_bool_to_bit_geocentric(self): assert aacgmv2.convert_bool_to_bit(geocentric=True) == \ aacgmv2._aacgmv2.GEOCENTRIC -class TestMLTConvert(TestPyAACGMV2): +class TestMLTConvert: def setup(self): """Runs before every method to create a clean testing setup""" self.dtime = dt.datetime(2015, 1, 1, 0, 0, 0) @@ -872,9 +868,9 @@ def teardown(self): def test_inv_convert_mlt_single(self): """Test MLT inversion for a single value""" for i,mlt in enumerate(self.mlt_list): - self.mlon_out = aacgmv2.convert_mlt(mlt, self.dtime, m2a=True) - np.testing.assert_almost_equal(self.mlon_out, self.mlon_comp[i], - decimal=4) + self.mlon_out = aacgmv2.convert_mlt(mlt, self.dtime, m2a=True) + np.testing.assert_almost_equal(self.mlon_out, self.mlon_comp[i], + decimal=4) def test_inv_convert_mlt_list(self): """Test MLT inversion for a list""" @@ -927,7 +923,7 @@ def test_mlt_convert_arr(self): self.dtime, m2a=False) np.testing.assert_allclose(self.mlt_out, self.mlt_comp, rtol=1.0e-4) -class TestCoeffPath(TestPyAACGMV2): +class TestCoeffPath: def setup(self): """Runs before every method to create a clean testing setup""" self.igrf_out = None @@ -941,7 +937,7 @@ def test_set_coeff_path_default(self): """Test the coefficient path setting using defaults""" self.igrf_out, self.coeff_out = aacgmv2.set_coeff_path() - assert self.igrf_out == aacgmv2.IGRF_12_COEFF + assert self.igrf_out == aacgmv2.IGRF_12_COEFFS assert self.coeff_out == aacgmv2.AACGM_V2_DAT_PREFIX def test_set_coeff_path_different(self): @@ -956,5 +952,5 @@ def test_set_coeff_path_mix(self): (self.igrf_out, self.coeff_out) = aacgmv2.set_coeff_path(coeff_prefix="hi") - assert self.igrf_out == aacgmv2.IGRF_12_COEFF + assert self.igrf_out == aacgmv2.IGRF_12_COEFFS assert self.coeff_out == "hi" diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index 0a576c07..d6aca32e 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -444,7 +444,7 @@ def convert_str_to_bit(code): codes = code.upper().replace(" ", "").split("|") # Add the valid parts of the code, invalid elements are ignored - bit_code = sum([convert_code[k] for k in codes]) + bit_code = sum([convert_code[k] for k in codes if k in convert_code.keys()]) return bit_code From c503f80263996c608f9104732aea5afbb3df5638 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 15:52:59 -0500 Subject: [PATCH 088/101] coveralls improvement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coveralls isn’t running, this is because of tox and hopefully these lines will fix it. --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4710203b..e0e358a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,10 @@ install: - pip install coveralls - "python setup.py install" - pip install tox-travis -script: tox +script: + - tox + - coverage run --source aacgmv2 -m py.test + - coverage report -m after_sucess: coveralls notifications: email: From dcc196e8b143055cb825bc43ff03530b27939018 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 16:29:56 -0500 Subject: [PATCH 089/101] document changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed document output format so that ValueError isn’t encountered in doctest Also, changed output for covert_mlt to mirror input. --- README.rst | 19 ++++++++++--------- aacgmv2/wrapper.py | 3 +++ docs/usage.rst | 14 ++++++-------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/README.rst b/README.rst index d58c5bcf..24bbd13d 100644 --- a/README.rst +++ b/README.rst @@ -21,25 +21,26 @@ Convert between AACGM and geographic coordinates:: >>> import aacgmv2 >>> import datetime as dt + >>> import numpy as np + >>> np.set_printoptions(precision=4) >>> # geo to AACGM, single numbers >>> dtime = dt.datetime(2013, 11, 3) - >>> mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 15, 300, dtime) - >>> "{:.4f} {:.4f} {:.4f}".format(mlat, mlon, mlt) - '57.4698 93.6300 1.4822' + >>> np.array(aacgmv2.get_aacgm_coord(60, 15, 300, dtime)) + array([57.4698, 93.63 , 1.4822]) >>> # AACGM to geo, mix arrays/numbers - >>> glat, glon, alt = aacgmv2.convert_latlon_arr([90, -90], 0, 0, dtime, code="A2G") - >>> ["{:.4f} {:.4f} {:.4f}".format(lat, glon[i], alt[i]) for i,lat in enumerate(glat)] - ['82.9666 -84.6652 14.1244', '-74.3385 125.8401 12.8771'] + >>> aacgmv2.convert_latlon_arr([90, -90], 0, 0, dtime, code="A2G") + (array([ 82.9666, -74.3385]), array([-84.6652, 125.8401]), array([14.1244, 12.8771])) Convert between AACGM and MLT:: >>> import aacgmv2 >>> import datetime as dt + >>> import numpy as np + >>> np.set_printoptions(precision=4) >>> # MLT to AACGM >>> dtime = dt.datetime(2013, 11, 3, 0, 0, 0) - >>> mlon_check = aacgmv2.convert_mlt([1.4822189, 12], dtime, m2a=True) - >>> ["{:.4f}".format(lon) for lon in mlon_check] - ['93.6300', '-108.6033'] + >>> aacgmv2.convert_mlt([1.4822189, 12], dtime, m2a=True) + array([ 93.63 , -108.6033]) If you don't know or use Python, you can also use the command line. See details in the full documentation. diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index d6aca32e..2a294b88 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -539,4 +539,7 @@ def convert_mlt(arr, dtime, m2a=False, coeff_prefix=None, igrf_file=None): dtime.minute, dtime.second, arr, coeff_prefix, igrf_file) + if hasattr(out, "shape") and out.shape == (): + out = float(out) + return out diff --git a/docs/usage.rst b/docs/usage.rst index 0cfdbdb6..8c914a3d 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -9,17 +9,15 @@ For full documentation of the functions, see :doc:`Reference → aacgmv2 >> import aacgmv2 >>> import datetime as dt + >>> import numpy as np + >>> np.set_printoptions(precision=4) >>> # geo to AACGM, single numbers >>> dtime = dt.datetime(2013, 11, 3) - >>> mlat, mlon, mlt = aacgmv2.get_aacgm_coord(60, 15, 300, dtime) - >>> "{:.4f} {:.4f} {:.4f}".format(mlat, mlon, mlt) - '57.4698 93.6300 1.4822' + >>> np.array(aacgmv2.get_aacgm_coord(60, 15, 300, dtime)) + array([57.4698, 93.63 , 1.4822]) >>> # AACGM to geo, mix arrays/numbers - >>> glat, glon, alt = aacgmv2.convert_latlon_arr([90, -90], 0, 0, dtime, code="A2G") - >>> ["{:.4f} {:.4f} {:.4f}".format(lat, glon[i], alt[i]) for i,lat in enumerate(glat)] - ['82.9666 -84.6652 14.1244', '-74.3385 125.8401 12.8771'] - - + >>> aacgmv2.convert_latlon_arr([90, -90], 0, 0, dtime, code="A2G") + (array([ 82.9666, -74.3385]), array([-84.6652, 125.8401]), array([14.1244, 12.8771])) Command-line interface ====================== From c7e27a7c9689856d391a1aa1dc3611570f788015 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 16:48:18 -0500 Subject: [PATCH 090/101] removed cyclic imports Cleaned up imports --- aacgmv2/deprecated.py | 4 +++- aacgmv2/wrapper.py | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/aacgmv2/deprecated.py b/aacgmv2/deprecated.py index 0c8631de..22d57c56 100644 --- a/aacgmv2/deprecated.py +++ b/aacgmv2/deprecated.py @@ -20,7 +20,6 @@ from __future__ import division, absolute_import, unicode_literals import numpy as np import logbook as logging -import aacgmv2 def convert(lat, lon, alt, date=None, a2g=False, trace=False, allowtrace=False, badidea=False, geocentric=False): @@ -55,6 +54,8 @@ def convert(lat, lon, alt, date=None, a2g=False, trace=False, allowtrace=False, lon_out : (float) Output longitude in degrees E """ + import aacgmv2 + if(np.array(alt).max() > 2000 and not trace and not allowtrace and badidea): estr = 'coefficients are not valid for altitudes above 2000 km. You' @@ -200,6 +201,7 @@ def igrf_dipole_axis(date): date, or extrapolated if date > latest IGRF model """ import datetime as dt + import aacgmv2 # get time in years, as float: year = date.year diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index 2a294b88..84d6a6ba 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -18,8 +18,6 @@ import datetime as dt import numpy as np import logbook as logging -import aacgmv2 -import aacgmv2._aacgmv2 as c_aacgmv2 def set_coeff_path(igrf_file=None, coeff_prefix=None): """This routine sets the two path variables. @@ -40,6 +38,8 @@ def set_coeff_path(igrf_file=None, coeff_prefix=None): coeff_prefix : (str) Location and file prefix for aacgm coefficient files """ + import aacgmv2 + # Define coefficient file prefix if not supplied if coeff_prefix is None: coeff_prefix = aacgmv2.AACGM_V2_DAT_PREFIX @@ -89,6 +89,8 @@ def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, out_r : (float) Geocentric radial distance in R """ + import aacgmv2._aacgmv2 as c_aacgmv2 + # Define coefficient file prefix if not supplied igrf_file, coeff_prefix = set_coeff_path(igrf_file=igrf_file, coeff_prefix=coeff_prefix) @@ -191,6 +193,8 @@ def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", out_r : (np.ndarray) Geocentric radial distances in R """ + import aacgmv2._aacgmv2 as c_aacgmv2 + # If a list was entered instead of a numpy array, recast it here if isinstance(in_lat, list): in_lat = np.array(in_lat) @@ -435,6 +439,8 @@ def convert_str_to_bit(code): Multiple codes should be seperated by pipes '|'. Invalid parts of the code are ignored and no code defaults to 'G2A'. """ + import aacgmv2._aacgmv2 as c_aacgmv2 + convert_code = {"G2A": c_aacgmv2.G2A, "A2G": c_aacgmv2.A2G, "TRACE": c_aacgmv2.TRACE, "BADIDEA": c_aacgmv2.BADIDEA, "GEOCENTRIC": c_aacgmv2.GEOCENTRIC, @@ -471,6 +477,8 @@ def convert_bool_to_bit(a2g=False, trace=False, allowtrace=False, bit_code : (int) code specification in bits """ + import aacgmv2._aacgmv2 as c_aacgmv2 + bit_code = c_aacgmv2.A2G if a2g else c_aacgmv2.G2A if trace: @@ -513,6 +521,8 @@ def convert_mlt(arr, dtime, m2a=False, coeff_prefix=None, igrf_file=None): This routine previously based on Laundal et al. 2016, but now uses the improved calculation available in AACGM-V2.4. """ + import aacgmv2._aacgmv2 as c_aacgmv2 + # Define coefficient file prefix if not supplied igrf_file, coeff_prefix = set_coeff_path(igrf_file=igrf_file, coeff_prefix=coeff_prefix) From 036428ffa3fd020ced1bbdd5e5ca102bce03a02b Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 16:48:54 -0500 Subject: [PATCH 091/101] improved docs Updated docs using np.set_printoptions formatting --- README.rst | 12 ++++++------ docs/usage.rst | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 24bbd13d..6458a683 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ Overview |docs| |version| This is a Python wrapper for the `AACGM-v2 C library -`_, which allows +`_, which allows converting between geographic and magnetic coordinates. The currently included version of the C library is 2.4. The package is free software (MIT license). @@ -22,25 +22,25 @@ Convert between AACGM and geographic coordinates:: >>> import aacgmv2 >>> import datetime as dt >>> import numpy as np - >>> np.set_printoptions(precision=4) + >>> np.set_printoptions(formatter={'float_kind': lambda x:'{:.4f}'.format(x)}) >>> # geo to AACGM, single numbers >>> dtime = dt.datetime(2013, 11, 3) >>> np.array(aacgmv2.get_aacgm_coord(60, 15, 300, dtime)) - array([57.4698, 93.63 , 1.4822]) + array([57.4698, 93.6300, 1.4822]) >>> # AACGM to geo, mix arrays/numbers >>> aacgmv2.convert_latlon_arr([90, -90], 0, 0, dtime, code="A2G") - (array([ 82.9666, -74.3385]), array([-84.6652, 125.8401]), array([14.1244, 12.8771])) + (array([82.9666, -74.3385]), array([-84.6652, 125.8401]), array([14.1244, 12.8771])) Convert between AACGM and MLT:: >>> import aacgmv2 >>> import datetime as dt >>> import numpy as np - >>> np.set_printoptions(precision=4) + >>> np.set_printoptions(formatter={'float_kind': lambda x:'{:.4f}'.format(x)}) >>> # MLT to AACGM >>> dtime = dt.datetime(2013, 11, 3, 0, 0, 0) >>> aacgmv2.convert_mlt([1.4822189, 12], dtime, m2a=True) - array([ 93.63 , -108.6033]) + array([93.6300, -108.6033]) If you don't know or use Python, you can also use the command line. See details in the full documentation. diff --git a/docs/usage.rst b/docs/usage.rst index 8c914a3d..1841f011 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -10,14 +10,14 @@ For full documentation of the functions, see :doc:`Reference → aacgmv2 >> import aacgmv2 >>> import datetime as dt >>> import numpy as np - >>> np.set_printoptions(precision=4) + >>> np.set_printoptions(formatter={'float_kind': lambda x:'{:.4f}'.format(x)}) >>> # geo to AACGM, single numbers >>> dtime = dt.datetime(2013, 11, 3) >>> np.array(aacgmv2.get_aacgm_coord(60, 15, 300, dtime)) - array([57.4698, 93.63 , 1.4822]) + array([57.4698, 93.6300, 1.4822]) >>> # AACGM to geo, mix arrays/numbers >>> aacgmv2.convert_latlon_arr([90, -90], 0, 0, dtime, code="A2G") - (array([ 82.9666, -74.3385]), array([-84.6652, 125.8401]), array([14.1244, 12.8771])) + (array([82.9666, -74.3385]), array([-84.6652, 125.8401]), array([14.1244, 12.8771])) Command-line interface ====================== From 83bdb171138e5bc3cd4da3742ea25dd6fa31ddc2 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 16:53:56 -0500 Subject: [PATCH 092/101] improved coveralls call Removed broken command from travis.yml --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e0e358a5..76753986 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,6 @@ install: script: - tox - coverage run --source aacgmv2 -m py.test - - coverage report -m after_sucess: coveralls notifications: email: From 8b973b3098a5f39deff44419edef53e9f1934919 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 17:09:21 -0500 Subject: [PATCH 093/101] codacy suggestions Codacy suggestions --- README.rst | 2 +- aacgmv2/__init__.py | 8 ++++---- aacgmv2/tests/test_c_aacgmv2.py | 5 +++-- aacgmv2/wrapper.py | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index 6458a683..6561629d 100644 --- a/README.rst +++ b/README.rst @@ -97,7 +97,7 @@ Badges :alt: Code Quality Status .. |codacy| image:: https://img.shields.io/codacy/af7fdf6be28841f283dfdbc1c01fa82a.svg?style=flat - :target: https://www.codacy.com/app/aburrell/aacgmv2 + :target: https://app.codacy.com/aburrell/aacgmv2 :alt: Codacy Code Quality Status .. |codeclimate| image:: https://codeclimate.com/github/aburrell/aacgmv2/badges/gpa.svg diff --git a/aacgmv2/__init__.py b/aacgmv2/__init__.py index c86c2521..2bfc749c 100644 --- a/aacgmv2/__init__.py +++ b/aacgmv2/__init__.py @@ -25,10 +25,10 @@ convert_bool_to_bit get_aacgm_coord get_aacgm_coord_arr -set_coeff_path convert convert_mlt -subsol +wrapper.set_coeff_path +deprecated.subsol _aacgmv2.convert _aacgmv2.set_datetime _aacgmv2.mlt_convert @@ -40,6 +40,7 @@ Modules --------------------------------------------------------------------------- deprecated +wrapper _aacgmv2 --------------------------------------------------------------------------- """ @@ -60,13 +61,12 @@ from aacgmv2.wrapper import (convert_latlon, convert_mlt, get_aacgm_coord) from aacgmv2.wrapper import (convert_latlon_arr, get_aacgm_coord_arr) from aacgmv2.wrapper import (convert_bool_to_bit, convert_str_to_bit) - from aacgmv2.wrapper import (set_coeff_path) except Exception as err: logging.exception(__file__ + ' -> aacgmv2: ' + str(err)) try: from aacgmv2 import (deprecated) - from aacgmv2.deprecated import (convert, subsol) + from aacgmv2.deprecated import (convert) except Exception as err: logging.exception(__file__ + ' -> aacgmv2: ' + str(err)) diff --git a/aacgmv2/tests/test_c_aacgmv2.py b/aacgmv2/tests/test_c_aacgmv2.py index dd245e20..3e28b36e 100644 --- a/aacgmv2/tests/test_c_aacgmv2.py +++ b/aacgmv2/tests/test_c_aacgmv2.py @@ -50,7 +50,8 @@ def test_constants(self): def test_set_datetime(self): """Test set_datetime""" for darg in self.date_args: - assert aacgmv2._aacgmv2.set_datetime(*darg) is None + arg1 = aacgmv2._aacgmv2.set_datetime(*darg) is None + assert arg1 def test_fail_set_datetime(self): """Test unsuccessful set_datetime""" @@ -85,7 +86,7 @@ def test_convert_A2G_coeff(self): r_comp = [1133.6241, 305.7138] for i,darg in enumerate(self.date_args): - aacgmv2._aacgmv2.set_datetime(*self.date_args[i]) + aacgmv2._aacgmv2.set_datetime(*darg) (self.mlat, self.mlon, self.rshell) = aacgmv2._aacgmv2.convert(self.lat_in[i], self.lon_in[i], diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index 84d6a6ba..1311b4e6 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -34,7 +34,7 @@ def set_coeff_path(igrf_file=None, coeff_prefix=None): Returns --------- igrf_file : (str) - Full filename of IGRF coefficient file + Full filename of IGRF coefficient file coeff_prefix : (str) Location and file prefix for aacgm coefficient files """ From 71a45a5a1cc97b9eb9fa8b761cc433b2611ae950 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 17:19:04 -0500 Subject: [PATCH 094/101] updated bootstrap Updated travis bootstrap template --- ci/templates/.travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 4710203b..76753986 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -16,7 +16,9 @@ install: - pip install coveralls - "python setup.py install" - pip install tox-travis -script: tox +script: + - tox + - coverage run --source aacgmv2 -m py.test after_sucess: coveralls notifications: email: From 5f39f2182eaac8806d81f1953cef5706e0da9c3e Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 17:26:56 -0500 Subject: [PATCH 095/101] module structure bug Fixed tests for new module structure --- aacgmv2/tests/test_dep_aacgmv2.py | 3 +-- aacgmv2/tests/test_py_aacgmv2.py | 8 +++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/aacgmv2/tests/test_dep_aacgmv2.py b/aacgmv2/tests/test_dep_aacgmv2.py index e1a3356d..c1faf4fd 100644 --- a/aacgmv2/tests/test_dep_aacgmv2.py +++ b/aacgmv2/tests/test_dep_aacgmv2.py @@ -22,8 +22,7 @@ def test_module_structure(self): """Test module structure for deprecated routines""" assert aacgmv2 assert aacgmv2.convert - assert aacgmv2.set_coeff_path - assert aacgmv2.subsol + assert aacgmv2.deprecated.subsol assert aacgmv2.deprecated assert aacgmv2.deprecated.gc2gd_lat assert aacgmv2.deprecated.igrf_dipole_axis diff --git a/aacgmv2/tests/test_py_aacgmv2.py b/aacgmv2/tests/test_py_aacgmv2.py index d75b3eeb..0f1a5b0d 100644 --- a/aacgmv2/tests/test_py_aacgmv2.py +++ b/aacgmv2/tests/test_py_aacgmv2.py @@ -19,6 +19,7 @@ def test_module_structure(self): assert aacgmv2.get_aacgm_coord assert aacgmv2.get_aacgm_coord_arr assert aacgmv2.wrapper + assert aacgmv2.wrapper.set_coeff_path def test_module_parameters(self): """Test module constants""" @@ -935,14 +936,15 @@ def teardown(self): def test_set_coeff_path_default(self): """Test the coefficient path setting using defaults""" - self.igrf_out, self.coeff_out = aacgmv2.set_coeff_path() + self.igrf_out, self.coeff_out = aacgmv2.wrapper.set_coeff_path() assert self.igrf_out == aacgmv2.IGRF_12_COEFFS assert self.coeff_out == aacgmv2.AACGM_V2_DAT_PREFIX def test_set_coeff_path_different(self): """Test the coefficient path setting""" - self.igrf_out, self.coeff_out = aacgmv2.set_coeff_path("hi", "bye") + self.igrf_out, self.coeff_out = aacgmv2.wrapper.set_coeff_path("hi", + "bye") assert self.igrf_out == "hi" assert self.coeff_out == "bye" @@ -950,7 +952,7 @@ def test_set_coeff_path_different(self): def test_set_coeff_path_mix(self): """Test the coefficient path setting using a mix of input""" (self.igrf_out, - self.coeff_out) = aacgmv2.set_coeff_path(coeff_prefix="hi") + self.coeff_out) = aacgmv2.wrapper.set_coeff_path(coeff_prefix="hi") assert self.igrf_out == aacgmv2.IGRF_12_COEFFS assert self.coeff_out == "hi" From 7ac335b054388cffaac8b8f707a458a686d1d497 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 20 Mar 2018 17:32:41 -0500 Subject: [PATCH 096/101] more bugfix Found one more instance of the wrong module path --- aacgmv2/tests/test_dep_aacgmv2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aacgmv2/tests/test_dep_aacgmv2.py b/aacgmv2/tests/test_dep_aacgmv2.py index c1faf4fd..aba41198 100644 --- a/aacgmv2/tests/test_dep_aacgmv2.py +++ b/aacgmv2/tests/test_dep_aacgmv2.py @@ -176,7 +176,7 @@ def test_subsol(self): doy = int(self.dtime.strftime("%j")) ut = self.dtime.hour * 3600.0 + self.dtime.minute * 60.0 + \ self.dtime.second - self.lon, self.lat = aacgmv2.subsol(self.dtime.year, doy, ut) + self.lon, self.lat = aacgmv2.deprecated.subsol(self.dtime.year, doy, ut) np.testing.assert_almost_equal(self.lon, -179.2004, decimal=4) np.testing.assert_almost_equal(self.lat, -23.0431, decimal=4) From 3f9ce1fa9a2b146c6bdefdaec1d68653229ff67d Mon Sep 17 00:00:00 2001 From: aburrell Date: Wed, 21 Mar 2018 11:25:01 -0500 Subject: [PATCH 097/101] documentation updates Update documentation to reflect changes in code. --- CHANGELOG.rst | 10 ++++++++-- README.rst | 1 + docs/installation.rst | 16 +++++++++++----- docs/reference/_aacgmv2.rst | 2 +- docs/reference/aacgmv2.rst | 7 +++++++ docs/reference/cli.rst | 11 ++++++++--- 6 files changed, 36 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0b8e86d6..6029d0e1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,12 +2,18 @@ Changelog ========= -2.0.1 (2017-03-09) +2.4.0 (2017-03-21) ----------------------------------------- * Update to use AACGM-v2.4, which includes changes to the inverse MLT and dipole tilt functions and some minor bug fixes - +* Updated file structure +* Updated methods, retaining old methods in deprecated module +* Added testing for python 3.6 +* Updated dependencies, removing support for python 3.3 +* Tested on Mac OSX +* Updated comments to include units for input and output + 2.0.0 (2016-11-03) ----------------------------------------- diff --git a/README.rst b/README.rst index 6561629d..17d95f8e 100644 --- a/README.rst +++ b/README.rst @@ -49,6 +49,7 @@ Documentation ============= https://aacgmv2.readthedocs.org/ + http://superdarn.thayer.dartmouth.edu/aacgm.html Badges diff --git a/docs/installation.rst b/docs/installation.rst index 67230411..c8fb34f5 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -2,16 +2,22 @@ Installation ============ -This package requires NumPy, which you can install alone or as a part of SciPy. `Some Python distributions `_ come with NumPy/SciPy pre-installed. For Python distributions without NumPy/SciPy, Windows/Mac users should install `pre-compiled binaries of NumPy/SciPy `_, and Linux users may have NumPy/SciPy available in `their repositories `_. +This package requires NumPy, which you can install alone or as a part of SciPy. +`Some Python distributions `_ come with NumPy/SciPy pre-installed. For Python distributions +without NumPy/SciPy, Windows/Mac users should install +`pre-compiled binaries of NumPy/SciPy `_, and Linux users may have NumPy/SciPy +available in `their repositories `_. -When you have NumPy, install this package at the command line using ``pip`` [1]_:: +When you have NumPy, install this package at the command line using +``pip`` [1]_:: pip install aacgmv2 The package has been tested with the following setups (others might work, too): * Mac (64 bit), Windows (32/64 bit), and Linux (64 bit) -* Python 2.7, 3.3, 3.4, 3.5, and 3.6 -* NumPy 1.8, 1.9, 1.10, 1.14.1 +* Python 2.7, 3.4, 3.5, and 3.6 -.. [1] pip is included with Python 2 from v2.7.9 and Python 3 from v3.4. If you don't have pip, `get it here `_. +.. [1] pip is included with Python 2 from v2.7.9 and Python 3 from v3.4. If you + don't have pip, + `get it here `_. diff --git a/docs/reference/_aacgmv2.rst b/docs/reference/_aacgmv2.rst index 46bfe215..67f53871 100644 --- a/docs/reference/_aacgmv2.rst +++ b/docs/reference/_aacgmv2.rst @@ -1,7 +1,7 @@ aacgmv2._aacgmv2 ================ -This submodule contains the interface to the AACGM-v2 C library. For the user-friendly wrapper, see :func:`aacgmv2.convert`. +This submodule contains the interface to the AACGM-v2 C library. For the user-friendly wrapper, see the functions in :py:module:`aacgmv2.wrapper`. .. automodule:: aacgmv2._aacgmv2 :members: diff --git a/docs/reference/aacgmv2.rst b/docs/reference/aacgmv2.rst index 26998c85..5bf1daaf 100644 --- a/docs/reference/aacgmv2.rst +++ b/docs/reference/aacgmv2.rst @@ -5,3 +5,10 @@ These functions are available when you ``import aacgmv2``. .. automodule:: aacgmv2 :members: + +.. automodule:: aacgmv2.wrapper + :members: + +.. automodule:: aacgmv2.deprecated + :members: + diff --git a/docs/reference/cli.rst b/docs/reference/cli.rst index ff526ee9..7249ee6d 100644 --- a/docs/reference/cli.rst +++ b/docs/reference/cli.rst @@ -3,9 +3,14 @@ Command-line interface .. highlight:: none -When you install this package you will get a command called ``aacgmv2``. It has two subcommands, ``convert`` and ``convert_mlt``, which correspond to the functions :py:func:`aacgmv2.convert` and :py:func:`aacgmv2.convert_mlt`. See the documentation for these functions for a more thorough explanation of arguments and behaviour. - -You can get help on the two commands by running ``aacgmv2 convert -h`` and ``aacgmv2 convert_mlt -h``. +When you install this package you will get a command called ``aacgmv2``. It has +two subcommands, ``convert`` and ``convert_mlt``, which correspond to the +functions :py:func:`aacgmv2.convert_latlon_arr` and +:py:func:`aacgmv2.convert_mlt`. See the documentation for these functions for a +more thorough explanation of arguments and behaviour. + +You can get help on the two commands by running ``aacgmv2 convert -h`` and +``aacgmv2 convert_mlt -h``. convert ------- From 5966fee9526ea0e7b2a2866d3fa26033ab50b6f4 Mon Sep 17 00:00:00 2001 From: aburrell Date: Wed, 21 Mar 2018 12:20:37 -0500 Subject: [PATCH 098/101] improved docstrings Improved docstrings to specifiy units in all locations. --- aacgmv2/deprecated.py | 8 ++++---- aacgmv2/wrapper.py | 26 ++++++++++++++------------ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/aacgmv2/deprecated.py b/aacgmv2/deprecated.py index 22d57c56..6157d7d4 100644 --- a/aacgmv2/deprecated.py +++ b/aacgmv2/deprecated.py @@ -91,9 +91,9 @@ def subsol(year, doy, utime): Returns --------- sbsllon : (float) - Subsolar longitude for the given date/time + Subsolar longitude in degrees E for the given date/time sbsllat : (float) - Subsolar lattude for the given date/time + Subsolar latitude in degrees N for the given date/time Notes -------- @@ -190,8 +190,8 @@ def igrf_dipole_axis(date): Returns ---------- - m: (np.ndarray) - Cartesian 3 element vector pointing at dipole pole in the north + m_0: (np.ndarray) + Cartesian 3 element unit vector pointing at dipole pole in the north (geocentric coords) Notes diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index 1311b4e6..5c27c036 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -83,11 +83,12 @@ def convert_latlon(in_lat, in_lon, height, dtime, code="G2A", igrf_file=None, Returns ------- out_lat : (float) - Output latitude in degrees + Output latitude in degrees N out_lon : (float) - Output longitude in degrees + Output longitude in degrees E out_r : (float) - Geocentric radial distance in R + Geocentric radial distance (R_Earth) or altitude above the surface of + the Earth (km) """ import aacgmv2._aacgmv2 as c_aacgmv2 @@ -187,11 +188,12 @@ def convert_latlon_arr(in_lat, in_lon, height, dtime, code="G2A", Returns ------- out_lat : (np.ndarray) - Output latitudes in degrees + Output latitudes in degrees N out_lon : (np.ndarray) - Output longitudes in degrees + Output longitudes in degrees E out_r : (np.ndarray) - Geocentric radial distances in R + Geocentric radial distance (R_Earth) or altitude above the surface of + the Earth (km) """ import aacgmv2._aacgmv2 as c_aacgmv2 @@ -330,9 +332,9 @@ def get_aacgm_coord(glat, glon, height, dtime, method="TRACE", Returns ------- mlat : (float) - magnetic latitude in degrees + magnetic latitude in degrees N mlon : (float) - magnetic longitude in degrees + magnetic longitude in degrees E mlt : (float) magnetic local time in hours """ @@ -388,9 +390,9 @@ def get_aacgm_coord_arr(glat, glon, height, dtime, method="TRACE", Returns ------- mlat : (float) - magnetic latitude in degrees + magnetic latitude in degrees N mlon : (float) - magnetic longitude in degrees + magnetic longitude in degrees E mlt : (float) magnetic local time in hours """ @@ -498,7 +500,7 @@ def convert_mlt(arr, dtime, m2a=False, coeff_prefix=None, igrf_file=None): Parameters ------------ arr : (array_line or float) - Magnetic longitudes or MLTs to convert + Magnetic longitudes (degrees E) or MLTs (hours) to convert dtime : (datetime.datetime) Date and time for MLT conversion in Universal Time (UT). m2a : (bool) @@ -514,7 +516,7 @@ def convert_mlt(arr, dtime, m2a=False, coeff_prefix=None, igrf_file=None): Returns -------- out : (np.ndarray) - Converted coordinates/MLT + Converted coordinates/MLT in degrees E or hours (as appropriate) Notes ------- From 9245c0aa2a0d9f41636edb2094dda8ef42f53ca7 Mon Sep 17 00:00:00 2001 From: aburrell Date: Wed, 21 Mar 2018 12:40:14 -0500 Subject: [PATCH 099/101] PEP8 update removed trailing whitespace --- aacgmv2/wrapper.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aacgmv2/wrapper.py b/aacgmv2/wrapper.py index 5c27c036..9ed22714 100644 --- a/aacgmv2/wrapper.py +++ b/aacgmv2/wrapper.py @@ -524,12 +524,11 @@ def convert_mlt(arr, dtime, m2a=False, coeff_prefix=None, igrf_file=None): improved calculation available in AACGM-V2.4. """ import aacgmv2._aacgmv2 as c_aacgmv2 - + # Define coefficient file prefix if not supplied igrf_file, coeff_prefix = set_coeff_path(igrf_file=igrf_file, coeff_prefix=coeff_prefix) - # Test time if isinstance(dtime, dt.date): dtime = dt.datetime.combine(dtime, dt.time(0)) From c240c472a166b6e4f86c4c2f34da9e53a13820e6 Mon Sep 17 00:00:00 2001 From: aburrell Date: Wed, 21 Mar 2018 12:49:24 -0500 Subject: [PATCH 100/101] init docstring removed second module section --- aacgmv2/__init__.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/aacgmv2/__init__.py b/aacgmv2/__init__.py index 2bfc749c..319a37cf 100644 --- a/aacgmv2/__init__.py +++ b/aacgmv2/__init__.py @@ -4,6 +4,8 @@ Modules --------------------------------------------------------------------------- _aacgmv2 : Contains functions and variables from c code +deprecated : Contains deprecated functions from previous versions +wrapper : Contains current python functions --------------------------------------------------------------------------- Parameters @@ -36,13 +38,6 @@ _aacgmv2.inv_mlt_convert _aacgmv2.inv_mlt_convert_yrsec --------------------------------------------------------------------------- - -Modules ---------------------------------------------------------------------------- -deprecated -wrapper -_aacgmv2 ---------------------------------------------------------------------------- """ import os.path as _path import logbook as logging From ff5117e23565b906d631ac6b1c7c006d66fb8551 Mon Sep 17 00:00:00 2001 From: aburrell Date: Wed, 21 Mar 2018 12:59:20 -0500 Subject: [PATCH 101/101] badge update Removed coveralls (not connecting) and codecov. Updated target location for codacy. --- README.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 17d95f8e..8dbc4b77 100644 --- a/README.rst +++ b/README.rst @@ -62,7 +62,6 @@ Badges - |docs| * - tests - | |travis| |appveyor| |requires| - | |coveralls| |codecov| | |landscape| |codeclimate| | |scrutinizer| |codacy| * - package @@ -97,9 +96,9 @@ Badges :target: https://landscape.io/github/aburrell/aacgmv2/master :alt: Code Quality Status -.. |codacy| image:: https://img.shields.io/codacy/af7fdf6be28841f283dfdbc1c01fa82a.svg?style=flat - :target: https://app.codacy.com/aburrell/aacgmv2 - :alt: Codacy Code Quality Status +.. |codacy| image:: https://api.codacy.com/project/badge/Grade/b64ee44194f148f5bdb0f00c7cf16ab8 + :target: https://www.codacy.com/app/aburrell/aacgmv2?utm_source=github.com&utm_medium=referral&utm_content=aburrell/aacgmv2&utm_campaign=Badge_Grade + :alt: Codacy Code Quality Status .. |codeclimate| image:: https://codeclimate.com/github/aburrell/aacgmv2/badges/gpa.svg :target: https://codeclimate.com/github/aburrell/aacgmv2