From 3a76de235cff47b327a1926af4ebef01dbd834f4 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 6 Apr 2018 16:18:02 -0500 Subject: [PATCH 001/226] Update setup.py Update python classifiers --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6f5e39c6..45249ee9 100644 --- a/setup.py +++ b/setup.py @@ -61,9 +61,9 @@ def read(*names, **kwargs): 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - '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', From f6e3217ca9e8f37d459bab04661d85d800ce47ae Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 6 Jul 2018 16:18:22 -0500 Subject: [PATCH 002/226] compiler warnings Change syntax of write statement to adhere to modern standards. Reformatted comments for legibility. --- src/fortranapex/makeapexsh.f90 | 66 +++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/src/fortranapex/makeapexsh.f90 b/src/fortranapex/makeapexsh.f90 index 0bfd485c..a082ca0a 100644 --- a/src/fortranapex/makeapexsh.f90 +++ b/src/fortranapex/makeapexsh.f90 @@ -1,18 +1,19 @@ -!*************************************************************************************************** +!******************************************************************************* ! ! File Name: makeapexsh.f90 ! Authors: John Emmert, Art Richmond ! Date: 11/13/2009 ! Version: 1.0 -! Description: Creates and saves spherical harmonic expansion coefficients for QD coordinate -! conversion. -! References: Richmond, A. D., Ionospheric Electrodynamics Using Magnetic Apex Coordinates, -! J. Geomag. Geoelectr., 47, 191-212, 1995. -! Emmert, J. T., A. D. Richmond, and D. P. Drob, A computationally compact -! representation of Magnetic-Apex and Quasi-Dipole coordinates with smooth base -! vectors, J. Geophys. Res., 115, Axxxxx, doi:10.1029/2010JA015326, 2010. +! Description: Creates and saves spherical harmonic expansion coefficients for +! QD coordinate conversion. +! References: Richmond, A. D., Ionospheric Electrodynamics Using Magnetic Apex +! Coordinates, J. Geomag. Geoelectr., 47, 191-212, 1995. +! Emmert, J. T., A. D. Richmond, and D. P. Drob, A computationally +! compact representation of Magnetic-Apex and Quasi-Dipole +! coordinates with smooth base vectors, J. Geophys. Res., 115, +! Axxxxx, doi:10.1029/2010JA015326, 2010. ! -!*************************************************************************************************** +!******************************************************************************* ! ! MAKEAPXSH ! Computes and saves harmonic coefficients for coordinate conversions. @@ -20,20 +21,22 @@ ! CALL MAKEAPXSH (DATAFILE, EPOCHS, NEPOCHS, L, M, N) ! ! INPUT ARGUMENTS -! DATAFILE Name of output data file that will contain the conversion coefficients -! EPOCHS Array of ordered epochs (decimal years, yyyy.y) for which coefficients are to be -! computed. +! DATAFILE Name of output data file that will contain the conversion +! coefficients +! EPOCHS Array of ordered epochs (decimal years, yyyy.y) for which +! coefficients are to be computed. ! NEPOCHS Number of elements in EPOCHS. ! L Maximum order of vertical polynomial expansion. ! M Maximum order of spherical harmonic expansion. -! N Maximum degree of spherical harmonic expansion. N must be equal or greater than M. +! N Maximum degree of spherical harmonic expansion. N must be equal +! or greater than M. ! ! DEPENDENCIES ! apex.f, magfld.f, apexsh.f90 ! -!*************************************************************************************************** +!******************************************************************************* -subroutine makeapxsh(datafilein,epochgridin,nepochin,lmaxin,mmaxin,nmaxin) +subroutine makeapxsh(datafilein, epochgridin, nepochin, lmaxin, mmaxin, nmaxin) use apxshmodule @@ -51,16 +54,19 @@ subroutine makeapxsh(datafilein,epochgridin,nepochin,lmaxin,mmaxin,nmaxin) integer(4) :: l, ish, i, j, Rpt(0:2) real(4) :: glon, glat, alt, lonspace, latspace, lat0 real(8) :: rhospace - real(8) :: norm1, norm2, cosmplat, sinmplat, cosmplon, sinmplon + real(8) :: norm1, norm2, cosmplat, sinmplat, cosmplon + real(8) :: sinmplon real(8) :: thetag, phig, thetaq, phiq, latwgt real(8) :: xq0, yq0, zq0, xg, yg, zg real(8), allocatable :: altgrid(:), altfn(:,:), shg(:) - real(8), allocatable :: Gg(:,:), Gq(:,:), Fg(:), Fq(:), cfdiag(:), coefftemp(:) - real(8), allocatable :: Dxq(:), Dyq(:), Dzq(:), Dxg(:), Dyg(:), Dzg(:) + real(8), allocatable :: Gg(:,:), Gq(:,:), Fg(:), Fq(:), cfdiag(:) + real(8), allocatable :: coefftemp(:) + real(8), allocatable :: Dxq(:), Dyq(:), Dzq(:), Dxg(:), Dyg(:) + real(8), allocatable :: Dzg(:) integer(4) :: NMAXIGRF real(4) :: GB(1:255) - real(4) :: A,ALAT,ALON,dum1,dum2,dum3,dum4,dum5 + real(4) :: A, ALAT, ALON, dum1, dum2, dum3, dum4, dum5 external COFRM, APEX @@ -135,13 +141,15 @@ subroutine makeapxsh(datafilein,epochgridin,nepochin,lmaxin,mmaxin,nmaxin) latwgt = dsin(thetag) do ilon = 0, nlon-1 - !COMPUTE SPHERICAL HARMONICS OF CURRENT GEODETIC LONGITUDE AND LATITUDE + !COMPUTE SPHERICAL HARMONICS OF CURRENT GEODETIC LONGITUDE AND + !LATITUDE glon = lonspace*real(ilon) - 180E0 phig = dble(glon) * dtor call shcalc(thetag,phig) shg = sh - !COMPUTE REFERENCE (DIPOLE) MAGNETIC LATITUDE AND LONGITUDE OF CURRENT LOCATION + !COMPUTE REFERENCE (DIPOLE) MAGNETIC LATITUDE AND LONGITUDE OF + !CURRENT LOCATION xq0 = dot_product(coeff0(Rpt,iepoch,0),shg(Rpt)) yq0 = dot_product(coeff0(Rpt,iepoch,1),shg(Rpt)) zq0 = dot_product(coeff0(Rpt,iepoch,2),shg(Rpt)) @@ -158,7 +166,8 @@ subroutine makeapxsh(datafilein,epochgridin,nepochin,lmaxin,mmaxin,nmaxin) thetaq = dasin(cosqlat) if (ALAT .lt. 0) thetaq = pi - thetaq - !COMPUTE RESIDUAL QD COORDINATES (FITTING DATA FOR GEODETIC TO QD TRANSFORMATION) + !COMPUTE RESIDUAL QD COORDINATES (FITTING DATA FOR GEODETIC TO QD + !TRANSFORMATION) xq = cosqlat*dcos(phiq) - xq0 yq = cosqlat*dsin(phiq) - yq0 zq = dcos(thetaq) - zq0 @@ -166,7 +175,8 @@ subroutine makeapxsh(datafilein,epochgridin,nepochin,lmaxin,mmaxin,nmaxin) !COMPUTE SPHERICAL HARMONICS OF CURRENT QD LATITUDE AND LONGITUDE call shcalc(thetaq,phiq) - !COMPUTE RESIDUAL GD COORDINATES (FITTING DATA FOR QD TO GEODETIC TRANSFORMATION) + !COMPUTE RESIDUAL GD COORDINATES (FITTING DATA FOR QD TO GEODETIC + !TRANSFORMATION) xg = latwgt*dcos(phig) - dot_product(coeff0(Rpt,iepoch,3),sh(Rpt)) yg = latwgt*dsin(phig) - dot_product(coeff0(Rpt,iepoch,4),sh(Rpt)) zg = dcos(thetag) - dot_product(coeff0(Rpt,iepoch,5),sh(Rpt)) @@ -217,15 +227,15 @@ subroutine makeapxsh(datafilein,epochgridin,nepochin,lmaxin,mmaxin,nmaxin) !WRITE COEFFICIENTS TO OUTPUT FILE open(unit=iun, file=trim(datafilein), form='unformatted') - write(iun), nepoch, nmax, mmax, lmax, nterm - write(iun), epochgrid, coeff0 + write(iun) nepoch, nmax, mmax, lmax, nterm + write(iun) epochgrid, coeff0 close(iun) return end subroutine makeapxsh -!*************************************************************************************************** +!******************************************************************************* subroutine choldc(a,n,np,p) @@ -252,7 +262,7 @@ subroutine choldc(a,n,np,p) end subroutine choldc -!*************************************************************************************************** +!******************************************************************************* subroutine cholsl(a,n,np,p,b,x) @@ -280,4 +290,4 @@ subroutine cholsl(a,n,np,p,b,x) end subroutine cholsl -!*************************************************************************************************** +!******************************************************************************* From 19a3c96fdc35b3c2fc60a634d07965aad8ac8639 Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 6 Jul 2018 16:18:51 -0500 Subject: [PATCH 003/226] static makefile Added a static compilation option for the makefile. Won't work on Mac OS X. --- src/fortranapex/Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/fortranapex/Makefile b/src/fortranapex/Makefile index bebb81ea..1baa9c07 100644 --- a/src/fortranapex/Makefile +++ b/src/fortranapex/Makefile @@ -7,6 +7,12 @@ FC = gfortran LD = $(FC) LDFLAGS = #-ipo FFLAGS = -O3 -fPIC #-ipo -check bounds -check pointer -check uninit +SFLAGS = -static +STATIC = 0 + +ifneq ($(STATIC),0) + LDFLAGS += $(SFLAGS) +endif #MKL_INCLUDE = -I/global/apps/intel/mkl/10.1.0.015/include/ LIBS = #-L/global/apps/intel/mkl/10.1.0.015/ -shared-intel -Wl,--start-group /global/apps/intel/mkl/10.1.0.015/lib/em64t/libmkl_intel_lp6\4.a /global/apps/intel/mkl/10.1.0.015/lib/em64t/libmkl_core.a From 5a6027182b49048fd8c65ac8c2e88604045f5441 Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 6 Jul 2018 16:38:29 -0500 Subject: [PATCH 004/226] static setup Allow for static compliation on non-mac machines. --- setup.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6f5e39c6..bc095375 100644 --- a/setup.py +++ b/setup.py @@ -16,8 +16,14 @@ extensions = [] else: from numpy.distutils.core import setup, Extension + + static = 0 + if(environ.get('WHEELS', None) == 'True' and + environ.get('OSTYPE', None) != 'darwin'): + static = 1 + extensions = [ - Extension(name='apexpy.fortranapex', + Extension(name='apexpy.fortranapex', extra_compile_args=static, sources=['src/fortranapex/magfld.f', 'src/fortranapex/apex.f', 'src/fortranapex/makeapexsh.f90', 'src/fortranapex/apexsh.f90', @@ -64,6 +70,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', From 39cec22ec730c4284b67c384e1dab8e69bd611c7 Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 6 Jul 2018 16:41:31 -0500 Subject: [PATCH 005/226] python versions Removed support for 3.3 and added support for 3.7. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index bc095375..96848e59 100644 --- a/setup.py +++ b/setup.py @@ -67,10 +67,10 @@ def read(*names, **kwargs): 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', 'Topic :: Scientific/Engineering :: Physics', 'Topic :: Utilities', From ef300198f35aa033cb64af1a748444642d5a2f5f Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 6 Jul 2018 17:43:22 -0500 Subject: [PATCH 006/226] wheel logic Improved wheel logic so that no user specified environment variables are required. --- setup.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 96848e59..63144d7a 100644 --- a/setup.py +++ b/setup.py @@ -16,14 +16,15 @@ extensions = [] else: from numpy.distutils.core import setup, Extension + import sys static = 0 - if(environ.get('WHEELS', None) == 'True' and - environ.get('OSTYPE', None) != 'darwin'): + if 'bdist_wheel' in sys.argv and environ.get('OSTYPE', None) != 'darwin': static = 1 - + extensions = [ - Extension(name='apexpy.fortranapex', extra_compile_args=static, + Extension(name='apexpy.fortranapex', + extra_compile_args="STATIC=%d".format(static), sources=['src/fortranapex/magfld.f', 'src/fortranapex/apex.f', 'src/fortranapex/makeapexsh.f90', 'src/fortranapex/apexsh.f90', From 4925b048d15a54e8aa64a77e0fc4235f1fe43622 Mon Sep 17 00:00:00 2001 From: aburrell Date: Fri, 6 Jul 2018 17:53:09 -0500 Subject: [PATCH 007/226] static makefile Added comments containing two of the individual flags needed in comments and the general static flag as the real flag. --- src/fortranapex/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fortranapex/Makefile b/src/fortranapex/Makefile index 1baa9c07..b33ae4e5 100644 --- a/src/fortranapex/Makefile +++ b/src/fortranapex/Makefile @@ -7,7 +7,7 @@ FC = gfortran LD = $(FC) LDFLAGS = #-ipo FFLAGS = -O3 -fPIC #-ipo -check bounds -check pointer -check uninit -SFLAGS = -static +SFLAGS = -static #-libgfortran -static-libgcc (AGB, static should work?) STATIC = 0 ifneq ($(STATIC),0) From 78436883c836d30f5d3ac2e1b642014b77540fd2 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 10 Jul 2018 12:46:58 -0500 Subject: [PATCH 008/226] error message Improved error message --- src/apexpy/apex.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 90eba187..289b0508 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -14,8 +14,9 @@ try: from . import fortranapex as fa except: - print("ERROR: fortranapex module could not be imported. apexpy probably" - " won't work") + print("ERROR: fortranapex module could not be imported, so apexpy probably" + " won't work. Make sure you have a gfortran compiler. Wheels " + "installation assumes your compiler lives in /opt/local/bin") # make sure invalid warnings are always shown From 6a51eefda8d4baca8e42e99221ee31a9715c8c9a Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 10 Jul 2018 12:49:23 -0500 Subject: [PATCH 009/226] static flag option Changed static flag option since f2py doesn't use the makefile --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 63144d7a..7234a373 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ extensions = [ Extension(name='apexpy.fortranapex', - extra_compile_args="STATIC=%d".format(static), + extra_compile_args="LDFLAGS=-static", sources=['src/fortranapex/magfld.f', 'src/fortranapex/apex.f', 'src/fortranapex/makeapexsh.f90', 'src/fortranapex/apexsh.f90', From bd12bc25aaf3304befcbe420ee9fc2fc1399426f Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 10 Jul 2018 12:52:36 -0500 Subject: [PATCH 010/226] no more setup.py static This is giving travis coniption fits. --- setup.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/setup.py b/setup.py index 7234a373..d32c4c30 100644 --- a/setup.py +++ b/setup.py @@ -16,15 +16,9 @@ extensions = [] else: from numpy.distutils.core import setup, Extension - import sys - - static = 0 - if 'bdist_wheel' in sys.argv and environ.get('OSTYPE', None) != 'darwin': - static = 1 extensions = [ Extension(name='apexpy.fortranapex', - extra_compile_args="LDFLAGS=-static", sources=['src/fortranapex/magfld.f', 'src/fortranapex/apex.f', 'src/fortranapex/makeapexsh.f90', 'src/fortranapex/apexsh.f90', From adacefb42e36e0def9bd92348071e821b9625e52 Mon Sep 17 00:00:00 2001 From: aburrell Date: Tue, 10 Jul 2018 13:04:01 -0500 Subject: [PATCH 011/226] removed 3.7 3.7 isn't ready for testing yet. --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index d32c4c30..09468a70 100644 --- a/setup.py +++ b/setup.py @@ -65,7 +65,6 @@ def read(*names, **kwargs): 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', 'Topic :: Scientific/Engineering :: Physics', 'Topic :: Utilities', From a1a51ea3ae1f74bb06e42c4f34f852dba32dc351 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Tue, 17 Jul 2018 18:08:48 -0700 Subject: [PATCH 012/226] Added functionality for obtaining apex B vectors. --- src/apexpy/apex.py | 139 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 90eba187..93550bce 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -17,6 +17,8 @@ print("ERROR: fortranapex module could not be imported. apexpy probably" " won't work") +import ctypes + # make sure invalid warnings are always shown warnings.filterwarnings('always', message='.*set to -9999 where*', @@ -64,11 +66,15 @@ class Apex(object): """ - def __init__(self, date=None, refh=0, datafile=None): + def __init__(self, date=None, refh=0, datafile=None, fortranlib=None): if datafile is None: datafile = os.path.join(os.path.dirname(__file__), 'apexsh.dat') + if fortranlib is None: + temp = os.path.dirname(__file__) + fortranlib = os.path.join(temp,'fortranapex.so') + self.RE = 6371.009 # mean Earth radius self.set_refh(refh) # reference height @@ -85,7 +91,13 @@ def __init__(self, date=None, refh=0, datafile=None): if not os.path.isfile(datafile): raise IOError('Datafile does not exist: {}'.format(datafile)) + if not os.path.isfile(fortranlib): + raise IOError('Datafile does not exist: {}'.format(fortranlib)) + self.datafile = datafile + self.fortranlib = fortranlib + self._cfa = ctypes.CDLL(fortranlib) + self.set_epoch(self.year) # vectorize fortran functions @@ -111,6 +123,8 @@ def __init__(self, date=None, refh=0, datafile=None): # vectorize other nonvectorized functions self._apex2qd = np.frompyfunc(self._apex2qd_nonvectorized, 3, 2) self._qd2apex = np.frompyfunc(self._qd2apex_nonvectorized, 3, 2) + self._get_babs = np.frompyfunc(self._get_babs_nonvectorized, 3, 1) + def convert(self, lat, lon, source, dest, height=0, datetime=None, precision=1e-10, ssheight=50*6371): @@ -920,7 +934,12 @@ def set_epoch(self, year): """ + # f2py fa.loadapxsh(self.datafile, np.float(year)) + # ctypes + date = ctypes.c_float(year) + self._cfa.cofrm_(ctypes.byref(date)) + self.year = year def set_refh(self, refh): @@ -939,3 +958,121 @@ def set_refh(self, refh): """ self.refh = refh + + def _get_babs_nonvectorized(self, glat, glon, height): + + # setup input args for feldg + IENTY = ctypes.c_int(1) + GLAT = ctypes.c_float(glat) + GLON = ctypes.c_float(glon) + ALT = ctypes.c_float(height) + # setup output args for feldg + BNRTH = ctypes.c_float(0) + BEAST = ctypes.c_float(0) + BDOWN = ctypes.c_float(0) + BABS = ctypes.c_float(0) + + self._cfa.feldg_(ctypes.byref(IENTY),ctypes.byref(GLAT), + ctypes.byref(GLON),ctypes.byref(ALT), + ctypes.byref(BNRTH),ctypes.byref(BEAST), + ctypes.byref(BDOWN),ctypes.byref(BABS) + ) + + return BABS.value + + def get_babs(self, glat, glon, height): + """Returns the magnitude of the IGRF magnetic field. + + Parameters + ========== + glat : array_like + Geodetic latitude + glon : array_like + Geodetic longitude + height : array_like + Altitude in km + + Returns + ======= + babs : ndarray or float + Magnitude of the IGRF magnetic field + + """ + + babs = self._get_babs(glat, glon, height) + + # if array is returned, the dtype is object, so convert to float + return np.float64(babs) + + def apex_bvectors(self, lat, lon, height, coords='geo', precision=1e-10): + """Returns the magnetic field vectors in apex coordinates. + + The apex magnetic field vectors described by Richmond [1995] [4]_ and + Emmert et al. [2010] [5]_, specfically the Be3 and Bd3 components. The + vector components are geodetic east, north, and up. + + Parameters + ========== + lat, lon : (N,) array_like or float + Latitude + lat : (N,) array_like or float + Longitude + height : (N,) array_like or float + Altitude in km + coords : {'geo', 'apex', 'qd'}, optional + Input coordinate system + precision : float, optional + Precision of output (degrees) when converting to geo. A negative + value of this argument produces a low-precision calculation of + geodetic lat/lon based only on their spherical harmonic + representation. + A positive value causes the underlying Fortran routine to iterate + until feeding the output geo lat/lon into geo2qd (APXG2Q) reproduces + the input QD lat/lon to within the specified precision (all + coordinates being converted to geo are converted to QD first and + passed through APXG2Q). + + Returns + ======= + Be3: (1, N) or (1,) ndarray + e3 : (3, N) or (3,) ndarray + Bd3: (1, N) or (1,) ndarray + d3 : (3, N) or (3,) ndarray + + Note + ==== + Be3 is not equivalent to the magnitude of the IGRF magnitude, but is + instead equal to the IGRF magnitude divided by a scaling factor, D. + Similarly, Bd3 is the IGRF magnitude multiplied by D. + + See Richmond, A. D. (1995) [4]_ equations 3.13 and 3.14 + + References + ========== + + .. [4] Richmond, A. D. (1995), Ionospheric Electrodynamics Using + Magnetic Apex Coordinates, Journal of geomagnetism and + geoelectricity, 47(2), 191–212, :doi:`10.5636/jgg.47.191`. + + .. [5] Emmert, J. T., A. D. Richmond, and D. P. Drob (2010), + A computationally compact representation of Magnetic-Apex + and Quasi-Dipole coordinates with smooth base vectors, + J. Geophys. Res., 115(A8), A08322, :doi:`10.1029/2010JA015326`. + + """ + + + glat, glon = self.convert(lat, lon, coords, 'geo', height=height, + precision=precision) + + babs = self.get_babs(glat, glon, height) + + _, _, _, _, _, _, d1, d2, d3, _, _, e3 = self.basevectors_apex(glat, \ + glon, height, coords='geo') + d1_cross_d2 = np.cross(d1.T,d2.T).T + D = np.sqrt(np.sum(d1_cross_d2**2,axis=0)) + + Be3 = babs / D + Bd3 = babs * D + + return Be3, e3, Bd3, d3 \ No newline at end of file From a8649b1837a8d334a4b8a53efebee663e1a9b924 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Wed, 18 Jul 2018 00:52:20 -0700 Subject: [PATCH 013/226] Moved fa definition inside each test function so that fortran library is initialized properly for each test. --- tests/test_fortranapex.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test_fortranapex.py b/tests/test_fortranapex.py index 56f27799..cd9f7470 100644 --- a/tests/test_fortranapex.py +++ b/tests/test_fortranapex.py @@ -15,11 +15,8 @@ ############################################################################## -fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), 2000) - - def test_apxg2q(): - + fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), 2000) qlat, qlon, f1, f2, f = fa.apxg2q(60, 15, 100, 1) assert_allclose(qlat, 56.531288146972656) assert_allclose(qlon, 94.1068344116211) @@ -29,6 +26,7 @@ def test_apxg2q(): def test_apxg2all(): + fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), 2000) qlat, qlon, mlat, mlon, f1, f2, f, d1, d2, d3, d, e1, e2, e3 = fa.apxg2all(60, 15, 100, 300, 1) assert_allclose(qlat, 56.531288146972656) assert_allclose(qlon, 94.1068344116211) @@ -47,6 +45,7 @@ def test_apxg2all(): def test_apxq2g(): + fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), 2000) glat, glon, error = fa.apxq2g(60, 15, 100, 1e-2) assert_allclose(glat, 50.97946548461914) assert_allclose(glon, -66.16902923583984) @@ -54,6 +53,7 @@ def test_apxq2g(): def test_g2q2d(): + fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), 2000) for lat in [0, 30, 60, 89]: for lon in [-179, -90, 0, 90, 179]: qlat, qlon, _, _, _ = fa.apxg2q(lat, lon, 100, 0) @@ -63,6 +63,7 @@ def test_g2q2d(): def test_apxq2g_lowprecision(): + fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), 2000) glat, glon, error = fa.apxq2g(60, 15, 100, -1) assert_allclose(glat, 51.00891876220703) assert_allclose(glon, -66.11973571777344) From 8a9030472d09dc89cbd3b5940e18c84b182f0eb0 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Wed, 18 Jul 2018 01:06:34 -0700 Subject: [PATCH 014/226] Added tests for apex_bvectors and get_babs. --- tests/test_Apex.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index fbcd1088..65a71ca4 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -1279,5 +1279,53 @@ def test_set_refh(): assert_allclose(ret_500, fa.apxg2all(60, 15, 100, 500, 0)[2:4]) +###============================================================================ +### Test the get_babs() method +###============================================================================ + + +def test_get_babs(): + inputs = [[[80],[100],[300]],[range(50,90,8),range(0,360,80),[300]*5], + [90.0,0,1000]] + temp1 = np.array([4.22045410e-05, 5.15672743e-05, 4.98150200e-05, + 5.06769359e-05, 4.91028428e-05]) + expected = [[5.1303124427795412e-05], temp1, [3.793962299823761e-05]] + + A = Apex(date=2018.1, refh=0) + for i in range(len(inputs)): + outputs = A.get_babs(*inputs[i]) + if isinstance(outputs,np.float64): + outputs = [outputs] + for j,output in enumerate(outputs): + assert_allclose(output, expected[i][j], rtol=0, atol=1e-5) + + +###============================================================================ +### Test the apex_bvectors() method +###============================================================================ + + +def test_apex_bvectors(): + inputs = [[80,81],[100,120],[100,200]] + + expected = (np.array([5.94623305e-05, 5.95450722e-05]), + np.array([[ 0.02008877, 0.00303204], + [ 0.03571109, 0.03377986], + [-0.94045794, -0.89848483]]), + np.array([ 5.26919505e-05, 4.81377429e-05]), + np.array([[ 0.02266997, 0.00375055], + [ 0.04029961, 0.04178477], + [-1.0612973 , -1.1114012 ]]) + ) + + A = Apex(date=2018.1, refh=0) + + outputs = A.apex_bvectors(*inputs,coords='geo', precision=1e-10) + for i,output in enumerate(outputs): + for j in range(output.size): + assert_allclose(output.ravel()[j], expected[i].ravel()[j], rtol=0, + atol=1e-5) + + if __name__ == '__main__': pytest.main() From bd8ce682d1e405e04793cd29509e0fb3c75bdcf8 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Wed, 18 Jul 2018 01:08:09 -0700 Subject: [PATCH 015/226] Minor documentation update. --- src/apexpy/apex.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 93550bce..f369c6bc 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -977,11 +977,11 @@ def _get_babs_nonvectorized(self, glat, glon, height): ctypes.byref(BNRTH),ctypes.byref(BEAST), ctypes.byref(BDOWN),ctypes.byref(BABS) ) - - return BABS.value + # BABS is in guass, so convert to tesla + return BABS.value / 10000.0 def get_babs(self, glat, glon, height): - """Returns the magnitude of the IGRF magnetic field. + """Returns the magnitude of the IGRF magnetic field in tesla. Parameters ========== From 3bc2a4068d1dd398a9feba5e8940d5a7e935ba08 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Wed, 18 Jul 2018 01:51:17 -0700 Subject: [PATCH 016/226] Fix that allows ctypes to properly find the apex fortran library. --- src/apexpy/apex.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index f369c6bc..67229740 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -72,8 +72,7 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None): datafile = os.path.join(os.path.dirname(__file__), 'apexsh.dat') if fortranlib is None: - temp = os.path.dirname(__file__) - fortranlib = os.path.join(temp,'fortranapex.so') + fortranlib = fa.__file__ self.RE = 6371.009 # mean Earth radius self.set_refh(refh) # reference height @@ -1075,4 +1074,4 @@ def apex_bvectors(self, lat, lon, height, coords='geo', precision=1e-10): Be3 = babs / D Bd3 = babs * D - return Be3, e3, Bd3, d3 \ No newline at end of file + return Be3, e3, Bd3, d3 From fd6d33776fb40bb6363034b25215a7448a84c9d9 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Wed, 18 Jul 2018 15:53:37 -0700 Subject: [PATCH 017/226] Renamed apex_bvectors to bvectors_apex method so it aligns with naming scheme of other methods. --- src/apexpy/apex.py | 2 +- tests/test_Apex.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 67229740..c0b7713f 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -1003,7 +1003,7 @@ def get_babs(self, glat, glon, height): # if array is returned, the dtype is object, so convert to float return np.float64(babs) - def apex_bvectors(self, lat, lon, height, coords='geo', precision=1e-10): + def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): """Returns the magnetic field vectors in apex coordinates. The apex magnetic field vectors described by Richmond [1995] [4]_ and diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 65a71ca4..7af98b61 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -1301,11 +1301,11 @@ def test_get_babs(): ###============================================================================ -### Test the apex_bvectors() method +### Test the bvectors_apex() method ###============================================================================ -def test_apex_bvectors(): +def test_bvectors_apex(): inputs = [[80,81],[100,120],[100,200]] expected = (np.array([5.94623305e-05, 5.95450722e-05]), @@ -1320,7 +1320,7 @@ def test_apex_bvectors(): A = Apex(date=2018.1, refh=0) - outputs = A.apex_bvectors(*inputs,coords='geo', precision=1e-10) + outputs = A.bvectors_apex(*inputs,coords='geo', precision=1e-10) for i,output in enumerate(outputs): for j in range(output.size): assert_allclose(output.ravel()[j], expected[i].ravel()[j], rtol=0, From 10358dc30a1098320896d62c613c89878c5a921f Mon Sep 17 00:00:00 2001 From: Achim Morschhauser <21514612+sputnik-a@users.noreply.github.com> Date: Mon, 11 May 2020 13:41:51 +0200 Subject: [PATCH 018/226] Read IGRF coefficients from file IGRF coefficients are directly read from ASCII-file instead of using hard-coded values. This omits adding the coefficients manually in the future. --- src/fortranapex/Makefile | 5 +- src/fortranapex/igrf.f90 | 136 +++ src/fortranapex/igrf13coeffs.txt | 199 +++++ src/fortranapex/magfld.f | 1351 +----------------------------- 4 files changed, 366 insertions(+), 1325 deletions(-) create mode 100644 src/fortranapex/igrf.f90 create mode 100644 src/fortranapex/igrf13coeffs.txt diff --git a/src/fortranapex/Makefile b/src/fortranapex/Makefile index bebb81ea..0ef49522 100644 --- a/src/fortranapex/Makefile +++ b/src/fortranapex/Makefile @@ -13,7 +13,7 @@ LIBS = #-L/global/apps/intel/mkl/10.1.0.015/ -shared-intel -Wl,--start-group /g PROG = apextest -OBJS = checkapexsh.o apexsh.o makeapexsh.o apex.o magfld.o +OBJS = checkapexsh.o apexsh.o makeapexsh.o apex.o magfld.o igrf.o $(PROG) : $(OBJS) $(LD) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS) @@ -21,6 +21,9 @@ $(PROG) : $(OBJS) apexsh.o : apexsh.f90 $(FC) -c $(FFLAGS) $< +igrf.o : igrf.f90 + $(FC) -c $(FFLAGS) $< + checkapexsh.o : checkapexsh.f90 $(FC) -c $(FFLAGS) $< diff --git a/src/fortranapex/igrf.f90 b/src/fortranapex/igrf.f90 new file mode 100644 index 00000000..aba1cfea --- /dev/null +++ b/src/fortranapex/igrf.f90 @@ -0,0 +1,136 @@ + module igrf + + implicit none + + contains + + subroutine read_igrf(filename_in,GYR,HYR,GT,HT,NEPO,NGHT,EPOCH,NMXE) + + implicit none + + real*8,allocatable,intent(inout) :: GYR(:,:,:),HYR(:,:,:) + real*8,allocatable,intent(inout) :: GT(:,:),HT(:,:) + real*4,allocatable,intent(inout) :: EPOCH(:),NMXE(:) + integer, intent(out) :: NEPO,NGHT + character(len=*),intent(in) :: filename_in + + character(len=10000) :: s + character(len=100) :: junk + integer :: state, i, offset, pos,o + integer :: num_sh, L_max + integer :: num_epochs + integer :: l,m,e + real*8,allocatable :: g(:,:) + integer,allocatable :: nm(:,:) + + ! Get number of Gauss coefficients + !num_sh = NGHT-2*sqrt(real(NGHT)) + !write(*,*) num_sh + + write(*,*) '----------------------------' + write(*,*) ' Read IGRF coefficeints ' + write(*,*) '----------------------------' + + ! Open IGRF file + open(unit=100, file=filename_in,status='old',iostat=state) + if (state /= 0) then + stop "File open error" + end if + + ! Skip comment lines + do + read(unit=100,fmt='(A)') s + if (s(1:1) .ne. '#') exit + enddo + + ! Read epochs + num_epochs=count([(s(i:i+3),i=1,len_trim(s))].eq.'IGRF') + num_epochs=count([(s(i:i+3),i=1,len_trim(s))].eq.'DGRF')+num_epochs + allocate(EPOCH(1:num_epochs)) + write(*,*) 'Number of epochs read: ',num_epochs + allocate(NMXE(1:num_epochs)) + + ! Read epochs + read(100,*,iostat=state) s + do i=1,num_epochs + EPOCH(i) = 1900+(i-1)*5.0d0 + enddo + + ! Number of coefficients + do i=1,num_epochs + if (EPOCH(i) .ge. 2000.0d0) then + NMXE(i) = 13 + elseif (EPOCH(i) .ge. 1900.0d0) then + NMXE(i) = 10 + else + write(*,*) 'ERROR: Epoch unavailable!' + exit + endif + enddo + + ! Save file position + offset = ftell(100) + + ! Get the number of lines (coefficients) + num_sh = 0 + do + read(unit=100,fmt=*,iostat=state) + if (state < 0) exit + num_sh = num_sh+1 + enddo + L_max = sqrt(num_sh+1.0d0)-1 + close(100) + + ! Restore file position, must re-open after reaching EOF + open(unit=100, file=filename_in,status='old',iostat=state) + call fseek(100,offset,0,state) + + ! Assign the variables for Gauss coefficients + allocate(g(1:num_sh,1:num_epochs)) + allocate(nm(1:num_sh,2)) + + ! Read coefficients + do i=1,num_sh + read(100,*,iostat=state) s,nm(i,1),nm(i,2),g(i,:) + if (state < 0) exit + enddo + close(100) + + ! Assign the return values + NGHT = (sqrt(num_sh+1.0)+1)**2 + NEPO = num_epochs + + + ! Assign the Gauss coefficients + allocate(GYR(NGHT,NGHT,NEPO),GT(NGHT,NGHT)) + allocate(HYR(NGHT,NGHT,NEPO),HT(NGHT,NGHT)) + GYR=0.0d0 + GT =0.0d0 + HYR=0.0d0 + HT =0.0d0 + write(*,*) 'ASSIGN COEFFICIENTS: ',NEPO,NGHT + do e=1,NEPO+1 + do l=1,L_max + if (e .le. NEPO) then + GYR(l+1,1,e) = g(l**2,e) + else + GT(l+1,1) = g(l**2,e) + endif + do m=1,l + if (m .le. l) then + pos = 1 + m*(L_max+2) + l + if (e .le. NEPO) then + GYR(l+1,m+1,e)=g(l**2+2*m-1,e) + HYR(l+1,m+1,e)=g(l**2+2*m ,e) + else + GT(l+1,m+1)=g(l**2+2*m-1,e) + HT(l+1,m+1)=g(l**2+2*m ,e) + endif + endif + enddo + enddo + enddo + return + end subroutine read_igrf + + end module igrf diff --git a/src/fortranapex/igrf13coeffs.txt b/src/fortranapex/igrf13coeffs.txt new file mode 100644 index 00000000..9b362e06 --- /dev/null +++ b/src/fortranapex/igrf13coeffs.txt @@ -0,0 +1,199 @@ +# 13th 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 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 2020.0 2020-25 +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 -29441.46 -29404.8 5.7 +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.77 -1450.9 7.4 +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 4795.99 4652.5 -25.9 +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.88 -2499.6 -11.0 +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.20 2982.0 -7.0 +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.41 -2991.6 -30.2 +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.35 1677.0 -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 -642.17 -734.6 -22.4 +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.33 1363.2 2.2 +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.26 -2381.2 -5.9 +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.29 -82.1 6.0 +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.85 1236.2 3.1 +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 245.04 241.9 -1.1 +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 581.69 525.7 -12.0 +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.70 -543.4 0.5 +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.42 903.0 -1.2 +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.68 809.5 -1.6 +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.54 281.9 -0.1 +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.49 86.3 -5.9 +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.43 -158.4 6.5 +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.85 -309.4 5.2 +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.95 199.7 3.6 +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.38 48.0 -5.1 +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.23 -349.7 -5.0 +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.91 -234.3 -0.3 +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.14 363.2 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 46.98 47.7 0.0 +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.35 187.8 -0.6 +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 196.98 208.3 2.5 +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.94 -140.7 0.2 +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.14 -121.2 -0.6 +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.40 -151.2 1.3 +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 15.98 32.3 3.0 +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.30 13.5 0.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.12 98.9 0.3 +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 69.55 66.0 -0.5 +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.57 65.5 -0.3 +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.61 -19.1 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.79 72.9 0.4 +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.30 25.1 -1.6 +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.85 -121.5 1.3 +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.74 52.8 -1.3 +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.93 -36.2 -1.4 +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.64 -64.5 0.8 +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.14 13.5 0.0 +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.35 8.9 0.0 +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.85 -64.7 0.9 +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.41 68.1 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.29 80.6 -0.1 +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 -75.99 -76.7 -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.27 -51.5 0.6 +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.79 -8.2 0.0 +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.53 -16.9 0.6 +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.82 56.5 0.7 +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.59 2.2 -0.8 +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.07 15.8 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.45 23.5 -0.2 +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.32 6.4 -0.5 +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.27 -2.2 -1.1 +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.88 -7.2 -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.50 -27.2 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.61 9.8 0.8 +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.32 -1.8 0.3 +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 23.98 23.7 0.0 +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.89 9.7 0.1 +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.04 8.4 -0.2 +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.78 -17.6 -0.1 +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.26 -15.3 0.6 +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.16 -0.5 0.4 +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.18 12.8 -0.2 +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.56 -21.1 -0.1 +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.60 -11.7 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.33 15.3 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.16 14.9 -0.3 +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.76 13.7 0.3 +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.69 3.6 -0.4 +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.98 -16.5 -0.1 +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.10 -6.9 0.5 +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.02 -0.3 0.4 +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.26 2.8 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.33 5.0 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.83 8.4 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.77 -23.4 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.02 2.9 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.76 11.0 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.22 -1.5 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.74 9.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.67 -1.1 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.74 -5.1 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.20 -13.2 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.88 -6.3 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.10 1.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.79 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.68 8.8 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.04 0.4 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.06 -9.3 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 -3.89 -1.4 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.54 -11.9 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.44 9.6 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 -2.01 -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.26 -6.2 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.28 3.4 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.17 -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.40 -0.2 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.55 1.7 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.55 3.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.55 -0.9 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.40 4.8 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.70 0.7 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.92 -8.6 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.67 -0.9 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.61 -0.1 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.13 1.9 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.16 -4.3 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.33 1.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.85 -3.4 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.80 -2.4 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.12 -0.1 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.59 -3.8 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.72 -8.8 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.00 3.0 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.40 -1.4 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.00 0.0 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.30 -2.5 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.11 2.5 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.08 2.3 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.60 -0.6 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.79 -0.9 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.05 -0.4 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.58 0.3 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.76 0.6 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.70 -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.20 -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.14 -0.1 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.12 -1.7 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.70 1.4 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.44 -1.6 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.22 -0.6 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.57 -3.0 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.44 0.2 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.01 -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.49 3.1 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.34 -2.6 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 -2.09 -2.0 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.16 -0.1 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.08 -1.2 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.46 0.5 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.37 0.5 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.23 1.3 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.75 1.4 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.89 -1.2 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.19 -1.8 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.85 0.7 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.27 0.1 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.10 0.3 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.72 0.8 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.54 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.09 -0.2 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.37 -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.29 0.6 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.43 -0.5 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.23 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.22 0.1 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.89 -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.94 -1.1 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.16 0.0 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.03 -0.3 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.72 0.5 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.02 0.1 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.92 -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.88 -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.42 0.5 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.49 0.6 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.63 0.7 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.56 1.4 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.42 -0.3 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.50 -0.4 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 0.96 0.8 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.24 -1.3 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.19 0.0 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.10 -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.81 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.42 0.3 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.13 0.0 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.04 -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.38 0.4 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.48 0.5 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.08 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.48 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.46 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.30 -0.4 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.35 -0.5 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.43 -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.36 -0.4 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.71 -0.6 0.0 diff --git a/src/fortranapex/magfld.f b/src/fortranapex/magfld.f index a391bbb5..d8311bcf 100644 --- a/src/fortranapex/magfld.f +++ b/src/fortranapex/magfld.f @@ -95,1341 +95,45 @@ SUBROUTINE COFRM (DATE) C C Jan. 2010 (Maute) update with IGRF11 (same instructions as Sep. 2005 C comment - - DOUBLE PRECISION F,F0 +C +C May 2020 (Achim Morschhauser): Update with routine to read +C IGRF coefficients file directly. +C + use igrf +C +c implicit none +C + REAL(4) DATE,DATEL + REAL(4) F,F0 + REAL(4), ALLOCATABLE :: EPOCH(:),NMXE(:) COMMON /MAGCOF/ NMAX,GB(255),GV(225),ICHG DATA ICHG /-99999/ C NEPO = Number of epochs C NGH = Single dimensioned array size of 2D version (GYR or HYR) C NGHT = Single dimensioned array size of 2D version (GT or HT) - PARAMETER (NEPO = 24, NGH = 225*NEPO, NGHT = 225) - DIMENSION GYR(15,15,NEPO), HYR(15,15,NEPO), EPOCH(NEPO), - + GT (15,15), HT (15,15), NMXE(NEPO), - + GY1D(NGH), HY1D(NGH), - + GT1D(NGHT), HT1D(NGHT) - EQUIVALENCE (GYR(1,1,1),GY1D(1)), (HYR(1,1,1),HY1D(1)), - + (GT (1,1), GT1D(1)), (HT (1,1), HT1D(1)) +!! PARAMETER (NEPO = 24, NGH = 225*NEPO, NGHT = 225) + INTEGER NEPO,NGHT,NGH + REAL*8, ALLOCATABLE :: GYR(:,:,:),HYR(:,:,:) + REAL*8, ALLOCATABLE :: GT(:,:),HT(:,:) + + CHARACTER(LEN=1000) :: FILENAME - SAVE DATEL, EPOCH, NMXE, GYR, HYR, GT, HT, GY1D, HY1D, GT1D, HT1D - DATA DATEL /-999./, - + EPOCH / 1900, 1905, 1910, 1915, 1920, 1925, 1930, 1935, 1940, - + 1945, 1950, 1955, 1960, 1965, 1970, 1975, 1980, 1985, - + 1990, 1995, 2000, 2005, 2010, 2015/, - + NMXE / 10, 10, 10, 10, 10, 10, 10, 10, 10, - + 10, 10, 10, 10, 10, 10, 10, 10, 10, - + 10, 10, 13, 13, 13, 13/ - -C g(n,m) for 1900 -C Fields across a line are (degree) n=1,13; lines are (order) m=0,13 as indicated -C in column 6; e.g., for 1965 g(n=3,m=0) = 1297 or g(n=6,m=6) = -111 -C -C 1 2 3 4 5 6 7 8 9 -C 10 11 12 13 (n) - DATA (GY1D(I),I=1,145) /0, - O -31543, -677, 1022, 876, -184, 63, 70, 11, 8, - + -3, 0, 0, 0, 2*0, - 1 -2298, 2905, -1469, 628, 328, 61, -55, 8, 10, - + -4, 0, 0, 0, 3*0, - 2 924, 1256, 660, 264, -11, 0, -4, 1, - + 2, 0, 0, 0, 4*0, - 3 572, -361, 5, -217, 34, -9, -11, - + -5, 0, 0, 0, 5*0, - 4 134, -86, -58, -41, 1, 12, - + -2, 0, 0, 0, 6*0, - 5 -16, 59, -21, 2, 1, - + 6, 0, 0, 0, 7*0, - 6 -90, 18, -9, -2, - + 4, 0, 0, 0, 8*0, - 7 6, 5, 2, - + 0, 0, 0, 0, 9*0, - 8 8, -1, - + 2, 0, 0, 0, 10*0, - 9 -1/ - DATA (GY1D(I),I=146,225) / - + 2, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1905 - DATA (GY1D(I),I=226,370) /0, - O -31464, -728, 1037, 880, -192, 62, 70, 11, 8, - + -3, 0, 0, 0, 2*0, - 1 -2298, 2928, -1494, 643, 328, 60, -54, 8, 10, - + -4, 0, 0, 0, 3*0, - 2 1041, 1239, 653, 259, -11, 0, -4, 1, - + 2, 0, 0, 0, 4*0, - 3 635, -380, -1, -221, 33, -9, -11, - + -5, 0, 0, 0, 5*0, - 4 146, -93, -57, -41, 1, 12, - + -2, 0, 0, 0, 6*0, - 5 -26, 57, -20, 2, 1, - + 6, 0, 0, 0, 7*0, - 6 -92, 18, -8, -2, - + 4, 0, 0, 0, 8*0, - 7 6, 5, 2, - + 0, 0, 0, 0, 9*0, - 8 8, 0, - + 2, 0, 0, 0, 10*0, - 9 -1/ - DATA (GY1D(I),I=371,450) / - + 2, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1910 - DATA (GY1D(I),I=451,595) /0, - O -31354, -769, 1058, 884, -201, 62, 71, 11, 8, - + -3, 0, 0, 0, 2*0, - 1 -2297, 2948, -1524, 660, 327, 58, -54, 8, 10, - + -4, 0, 0, 0, 3*0, - 2 1176, 1223, 644, 253, -11, 1, -4, 1, - + 2, 0, 0, 0, 4*0, - 3 705, -400, -9, -224, 32, -9, -11, - + -5, 0, 0, 0, 5*0, - 4 160, -102, -54, -40, 1, 12, - + -2, 0, 0, 0, 6*0, - 5 -38, 54, -19, 2, 1, - + 6, 0, 0, 0, 7*0, - 6 -95, 18, -8, -2, - + 4, 0, 0, 0, 8*0, - 7 6, 5, 2, - + 0, 0, 0, 0, 9*0, - 8 8, 0, - + 2, 0, 0, 0, 10*0, - 9 -1/ - DATA (GY1D(I),I=596,675) / - + 2, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1915 - DATA (GY1D(I),I=676,820) /0, - O -31212, -802, 1084, 887, -211, 61, 72, 11, 8, - + -3, 0, 0, 0, 2*0, - 1 -2306, 2956, -1559, 678, 327, 57, -54, 8, 10, - + -4, 0, 0, 0, 3*0, - 2 1309, 1212, 631, 245, -10, 2, -4, 1, - + 2, 0, 0, 0, 4*0, - 3 778, -416, -16, -228, 31, -9, -11, - + -5, 0, 0, 0, 5*0, - 4 178, -111, -51, -38, 2, 12, - + -2, 0, 0, 0, 6*0, - 5 -51, 49, -18, 3, 1, - + 6, 0, 0, 0, 7*0, - 6 -98, 19, -8, -2, - + 4, 0, 0, 0, 8*0, - 7 6, 6, 2, - + 0, 0, 0, 0, 9*0, - 8 8, 0, - + 1, 0, 0, 0, 10*0, - 9 -1/ - DATA (GY1D(I),I=821,900) / - + 2, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1920 - DATA (GY1D(I),I=901,1045) /0, - O -31060, -839, 1111, 889, -221, 61, 73, 11, 8, - + -3, 0, 0, 0, 2*0, - 1 -2317, 2959, -1600, 695, 326, 55, -54, 7, 10, - + -4, 0, 0, 0, 3*0, - 2 1407, 1205, 616, 236, -10, 2, -3, 1, - + 2, 0, 0, 0, 4*0, - 3 839, -424, -23, -233, 29, -9, -11, - + -5, 0, 0, 0, 5*0, - 4 199, -119, -46, -37, 2, 12, - + -2, 0, 0, 0, 6*0, - 5 -62, 44, -16, 4, 1, - + 6, 0, 0, 0, 7*0, - 6 -101, 19, -7, -2, - + 4, 0, 0, 0, 8*0, - 7 6, 6, 2, - + 0, 0, 0, 0, 9*0, - 8 8, 0, - + 1, 0, 0, 0, 10*0, - 9 -1/ - DATA (GY1D(I),I=1046,1125) / - + 3, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1925 - DATA (GY1D(I),I=1126,1270) /0, - O -30926, -893, 1140, 891, -230, 61, 73, 11, 8, - + -3, 0, 0, 0, 2*0, - 1 -2318, 2969, -1645, 711, 326, 54, -54, 7, 10, - + -4, 0, 0, 0, 3*0, - 2 1471, 1202, 601, 226, -9, 3, -3, 1, - + 2, 0, 0, 0, 4*0, - 3 881, -426, -28, -238, 27, -9, -11, - + -5, 0, 0, 0, 5*0, - 4 217, -125, -40, -35, 2, 12, - + -2, 0, 0, 0, 6*0, - 5 -69, 39, -14, 4, 1, - + 6, 0, 0, 0, 7*0, - 6 -103, 19, -7, -2, - + 4, 0, 0, 0, 8*0, - 7 6, 7, 2, - + 0, 0, 0, 0, 9*0, - 8 8, 0, - + 1, 0, 0, 0, 10*0, - 9 -1/ - DATA (GY1D(I),I=1271,1350) / - + 3, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1930 - DATA (GY1D(I),I=1351,1495) /0, - O -30805, -951, 1172, 896, -237, 60, 74, 11, 8, - + -3, 0, 0, 0, 2*0, - 1 -2316, 2980, -1692, 727, 327, 53, -54, 7, 10, - + -4, 0, 0, 0, 3*0, - 2 1517, 1205, 584, 218, -9, 4, -3, 1, - + 2, 0, 0, 0, 4*0, - 3 907, -422, -32, -242, 25, -9, -12, - + -5, 0, 0, 0, 5*0, - 4 234, -131, -32, -34, 2, 12, - + -2, 0, 0, 0, 6*0, - 5 -74, 32, -12, 5, 1, - + 6, 0, 0, 0, 7*0, - 6 -104, 18, -6, -2, - + 4, 0, 0, 0, 8*0, - 7 6, 8, 3, - + 0, 0, 0, 0, 9*0, - 8 8, 0, - + 1, 0, 0, 0, 10*0, - 9 -2/ - DATA (GY1D(I),I=1496,1575) / - + 3, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1935 - DATA (GY1D(I),I=1576,1720) /0, - O -30715, -1018, 1206, 903, -241, 59, 74, 11, 8, - + -3, 0, 0, 0, 2*0, - 1 -2306, 2984, -1740, 744, 329, 53, -53, 7, 10, - + -4, 0, 0, 0, 3*0, - 2 1550, 1215, 565, 211, -8, 4, -3, 1, - + 2, 0, 0, 0, 4*0, - 3 918, -415, -33, -246, 23, -9, -12, - + -5, 0, 0, 0, 5*0, - 4 249, -136, -25, -33, 1, 11, - + -2, 0, 0, 0, 6*0, - 5 -76, 25, -11, 6, 1, - + 6, 0, 0, 0, 7*0, - 6 -106, 18, -6, -2, - + 4, 0, 0, 0, 8*0, - 7 6, 8, 3, - + 0, 0, 0, 0, 9*0, - 8 7, 0, - + 2, 0, 0, 0, 10*0, - 9 -2/ - DATA (GY1D(I),I=1721,1800) / - + 3, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1940 - DATA (GY1D(I),I=1801,1945) /0, - O -30654, -1106, 1240, 914, -241, 57, 74, 11, 8, - + -3, 0, 0, 0, 2*0, - 1 -2292, 2981, -1790, 762, 334, 54, -53, 7, 10, - + -4, 0, 0, 0, 3*0, - 2 1566, 1232, 550, 208, -7, 4, -3, 1, - + 2, 0, 0, 0, 4*0, - 3 916, -405, -33, -249, 20, -10, -12, - + -5, 0, 0, 0, 5*0, - 4 265, -141, -18, -31, 1, 11, - + -2, 0, 0, 0, 6*0, - 5 -76, 18, -9, 6, 1, - + 6, 0, 0, 0, 7*0, - 6 -107, 17, -5, -2, - + 4, 0, 0, 0, 8*0, - 7 5, 9, 3, - + 0, 0, 0, 0, 9*0, - 8 7, 1, - + 2, 0, 0, 0, 10*0, - 9 -2/ - DATA (GY1D(I),I=1946,2025) / - + 3, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1945 - DATA (GY1D(I),I=2026,2170) /0, - O -30594, -1244, 1282, 944, -253, 59, 70, 13, 5, - + -3, 0, 0, 0, 2*0, - 1 -2285, 2990, -1834, 776, 346, 57, -40, 7, -21, - + 11, 0, 0, 0, 3*0, - 2 1578, 1255, 544, 194, 6, 0, -8, 1, - + 1, 0, 0, 0, 4*0, - 3 913, -421, -20, -246, 0, -5, -11, - + 2, 0, 0, 0, 5*0, - 4 304, -142, -25, -29, 9, 3, - + -5, 0, 0, 0, 6*0, - 5 -82, 21, -10, 7, 16, - + -1, 0, 0, 0, 7*0, - 6 -104, 15, -10, -3, - + 8, 0, 0, 0, 8*0, - 7 29, 7, -4, - + -1, 0, 0, 0, 9*0, - 8 2, -3, - + -3, 0, 0, 0, 10*0, - 9 -4/ - DATA (GY1D(I),I=2171,2250) / - + 5, 0, 0, 0, 11*0, - O -2, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1950 - DATA (GY1D(I),I=2251,2395) /0, - O -30554, -1341, 1297, 954, -240, 54, 65, 22, 3, - + -8, 0, 0, 0, 2*0, - 1 -2250, 2998, -1889, 792, 349, 57, -55, 15, -7, - + 4, 0, 0, 0, 3*0, - 2 1576, 1274, 528, 211, 4, 2, -4, -1, - + -1, 0, 0, 0, 4*0, - 3 896, -408, -20, -247, 1, -1, -25, - + 13, 0, 0, 0, 5*0, - 4 303, -147, -16, -40, 11, 10, - + -4, 0, 0, 0, 6*0, - 5 -76, 12, -7, 15, 5, - + 4, 0, 0, 0, 7*0, - 6 -105, 5, -13, -5, - + 12, 0, 0, 0, 8*0, - 7 19, 5, -2, - + 3, 0, 0, 0, 9*0, - 8 -1, 3, - + 2, 0, 0, 0, 10*0, - 9 8/ - DATA (GY1D(I),I=2396,2475) / - + 10, 0, 0, 0, 11*0, - O 3, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1955 - DATA (GY1D(I),I=2476,2620) /0, - O -30500, -1440, 1302, 958, -229, 47, 65, 11, 4, - + -3, 0, 0, 0, 2*0, - 1 -2215, 3003, -1944, 796, 360, 57, -56, 9, 9, - + -5, 0, 0, 0, 3*0, - 2 1581, 1288, 510, 230, 3, 2, -6, -4, - + -1, 0, 0, 0, 4*0, - 3 882, -397, -23, -247, 10, -14, -5, - + 2, 0, 0, 0, 5*0, - 4 290, -152, -8, -32, 6, 2, - + -3, 0, 0, 0, 6*0, - 5 -69, 7, -11, 10, 4, - + 7, 0, 0, 0, 7*0, - 6 -107, 9, -7, 1, - + 4, 0, 0, 0, 8*0, - 7 18, 6, 2, - + -2, 0, 0, 0, 9*0, - 8 9, 2, - + 6, 0, 0, 0, 10*0, - 9 5/ - DATA (GY1D(I),I=2621,2700) / - + -2, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1960 - DATA (GY1D(I),I=2701,2845) /0, - O -30421, -1555, 1302, 957, -222, 46, 67, 15, 4, - + 1, 0, 0, 0, 2*0, - 1 -2169, 3002, -1992, 800, 362, 58, -56, 6, 6, - + -3, 0, 0, 0, 3*0, - 2 1590, 1289, 504, 242, 1, 5, -4, 0, - + 4, 0, 0, 0, 4*0, - 3 878, -394, -26, -237, 15, -11, -9, - + 0, 0, 0, 0, 5*0, - 4 269, -156, -1, -32, 2, 1, - + -1, 0, 0, 0, 6*0, - 5 -63, -2, -7, 10, 4, - + 4, 0, 0, 0, 7*0, - 6 -113, 17, -5, -1, - + 6, 0, 0, 0, 8*0, - 7 8, 10, -2, - + 1, 0, 0, 0, 9*0, - 8 8, 3, - + -1, 0, 0, 0, 10*0, - 9 -1/ - DATA (GY1D(I),I=2846,2925) / - + 2, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1965 - DATA (GY1D(I),I=2926,3070) /0, - O -30334, -1662, 1297, 957, -219, 45, 75, 13, 8, - + -2, 0, 0, 0, 2*0, - 1 -2119, 2997, -2038, 804, 358, 61, -57, 5, 10, - + -3, 0, 0, 0, 3*0, - 2 1594, 1292, 479, 254, 8, 4, -4, 2, - + 2, 0, 0, 0, 4*0, - 3 856, -390, -31, -228, 13, -14, -13, - + -5, 0, 0, 0, 5*0, - 4 252, -157, 4, -26, 0, 10, - + -2, 0, 0, 0, 6*0, - 5 -62, 1, -6, 8, -1, - + 4, 0, 0, 0, 7*0, - 6 -111, 13, -1, -1, - + 4, 0, 0, 0, 8*0, - 7 1, 11, 5, - + 0, 0, 0, 0, 9*0, - 8 4, 1, - + 2, 0, 0, 0, 10*0, - 9 -2/ - DATA (GY1D(I),I=3071,3150) / - + 2, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1970 - DATA (GY1D(I),I=3151,3295) /0, - O -30220, -1781, 1287, 952, -216, 43, 72, 14, 8, - + -3, 0, 0, 0, 2*0, - 1 -2068, 3000, -2091, 800, 359, 64, -57, 6, 10, - + -3, 0, 0, 0, 3*0, - 2 1611, 1278, 461, 262, 15, 1, -2, 2, - + 2, 0, 0, 0, 4*0, - 3 838, -395, -42, -212, 14, -13, -12, - + -5, 0, 0, 0, 5*0, - 4 234, -160, 2, -22, -3, 10, - + -1, 0, 0, 0, 6*0, - 5 -56, 3, -2, 5, -1, - + 6, 0, 0, 0, 7*0, - 6 -112, 13, 0, 0, - + 4, 0, 0, 0, 8*0, - 7 -2, 11, 3, - + 1, 0, 0, 0, 9*0, - 8 3, 1, - + 0, 0, 0, 0, 10*0, - 9 -1/ - DATA (GY1D(I),I=3296,3375) / - + 3, 0, 0, 0, 11*0, - O -1, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1975 - DATA (GY1D(I),I=3376,3520) /0, - O -30100, -1902, 1276, 946, -218, 45, 71, 14, 7, - + -3, 0, 0, 0, 2*0, - 1 -2013, 3010, -2144, 791, 356, 66, -56, 6, 10, - + -3, 0, 0, 0, 3*0, - 2 1632, 1260, 438, 264, 28, 1, -1, 2, - + 2, 0, 0, 0, 4*0, - 3 830, -405, -59, -198, 16, -12, -12, - + -5, 0, 0, 0, 5*0, - 4 216, -159, 1, -14, -8, 10, - + -2, 0, 0, 0, 6*0, - 5 -49, 6, 0, 4, -1, - + 5, 0, 0, 0, 7*0, - 6 -111, 12, 0, -1, - + 4, 0, 0, 0, 8*0, - 7 -5, 10, 4, - + 1, 0, 0, 0, 9*0, - 8 1, 1, - + 0, 0, 0, 0, 10*0, - 9 -2/ - DATA (GY1D(I),I=3521,3600) / - + 3, 0, 0, 0, 11*0, - O -1, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1980 - DATA (GY1D(I),I=3601,3745) /0, - O -29992, -1997, 1281, 938, -218, 48, 72, 18, 5, - + -4, 0, 0, 0, 2*0, - 1 -1956, 3027, -2180, 782, 357, 66, -59, 6, 10, - + -4, 0, 0, 0, 3*0, - 2 1663, 1251, 398, 261, 42, 2, 0, 1, - + 2, 0, 0, 0, 4*0, - 3 833, -419, -74, -192, 21, -11, -12, - + -5, 0, 0, 0, 5*0, - 4 199, -162, 4, -12, -7, 9, - + -2, 0, 0, 0, 6*0, - 5 -48, 14, 1, 4, -3, - + 5, 0, 0, 0, 7*0, - 6 -108, 11, 3, -1, - + 3, 0, 0, 0, 8*0, - 7 -2, 6, 7, - + 1, 0, 0, 0, 9*0, - 8 -1, 2, - + 2, 0, 0, 0, 10*0, - 9 -5/ - DATA (GY1D(I),I=3746,3825) / - + 3, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1985 - DATA (GY1D(I),I=3826,3970) /0, - O -29873, -2072, 1296, 936, -214, 53, 74, 21, 5, - + -4, 0, 0, 0, 2*0, - 1 -1905, 3044, -2208, 780, 355, 65, -62, 6, 10, - + -4, 0, 0, 0, 3*0, - 2 1687, 1247, 361, 253, 51, 3, 0, 1, - + 3, 0, 0, 0, 4*0, - 3 829, -424, -93, -185, 24, -11, -12, - + -5, 0, 0, 0, 5*0, - 4 170, -164, 4, -6, -9, 9, - + -2, 0, 0, 0, 6*0, - 5 -46, 16, 4, 4, -3, - + 5, 0, 0, 0, 7*0, - 6 -102, 10, 4, -1, - + 3, 0, 0, 0, 8*0, - 7 0, 4, 7, - + 1, 0, 0, 0, 9*0, - 8 -4, 1, - + 2, 0, 0, 0, 10*0, - 9 -5/ - DATA (GY1D(I),I=3971,4050) / - + 3, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1990 - DATA (GY1D(I),I=4051,4195) /0, - O -29775, -2131, 1314, 939, -214, 61, 77, 23, 4, - + -3, 0, 0, 0, 2*0, - 1 -1848, 3059, -2239, 780, 353, 65, -64, 5, 9, - + -4, 0, 0, 0, 3*0, - 2 1686, 1248, 325, 245, 59, 2, -1, 1, - + 2, 0, 0, 0, 4*0, - 3 802, -423, -109, -178, 26, -10, -12, - + -5, 0, 0, 0, 5*0, - 4 141, -165, 3, -1, -12, 9, - + -2, 0, 0, 0, 6*0, - 5 -36, 18, 5, 3, -4, - + 4, 0, 0, 0, 7*0, - 6 -96, 9, 4, -2, - + 3, 0, 0, 0, 8*0, - 7 0, 2, 7, - + 1, 0, 0, 0, 9*0, - 8 -6, 1, - + 3, 0, 0, 0, 10*0, - 9 -6/ - DATA (GY1D(I),I=4196,4275) / - + 3, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 1995 - DATA (GY1D(I),I=4276,4420) /0, - O -29692, -2200, 1335, 940, -214, 68, 77, 25, 4, - + -3, 0, 0, 0, 2*0, - 1 -1784, 3070, -2267, 780, 352, 67, -72, 6, 9, - + -6, 0, 0, 0, 3*0, - 2 1681, 1249, 290, 235, 68, 1, -6, 3, - + 2, 0, 0, 0, 4*0, - 3 759, -418, -118, -170, 28, -9, -10, - + -4, 0, 0, 0, 5*0, - 4 122, -166, -1, 5, -14, 8, - + -1, 0, 0, 0, 6*0, - 5 -17, 19, 4, 9, -8, - + 4, 0, 0, 0, 7*0, - 6 -93, 8, 6, -1, - + 2, 0, 0, 0, 8*0, - 7 -2, -5, 10, - + 2, 0, 0, 0, 9*0, - 8 -7, -2, - + 5, 0, 0, 0, 10*0, - 9 -8/ - DATA (GY1D(I),I=4421,4500) / - + 1, 0, 0, 0, 11*0, - O 0, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C g(n,m) for 2000 - DATA (GY1D(I),I=4501,4645) /0, - O-29619.4,-2267.7,1339.6, 932.3,-218.8, 72.3, 79.0, 24.4, 5.0, - + -2.6, 2.7, -2.2, -0.2, 2*0, - 1 -1728.2, 3068.4,-2288.0, 786.8, 351.4, 68.2, -74.0, 6.6, 9.4, - + -6.0, -1.7, -0.3, -0.9, 3*0, - 2 1670.9,1252.1, 250.0, 222.3, 74.2, 0.0, -9.2, 3.0, - + 1.7, -1.9, 0.2, 0.3, 4*0, - 3 714.5,-403.0,-130.4,-160.9, 33.3, -7.9, -8.4, - + -3.1, 1.5, 0.9, 0.1, 5*0, - 4 111.3,-168.6, -5.9, 9.1, -16.6, 6.3, - + -0.5, -0.1, -0.2, -0.4, 6*0, - 5 -12.9, 16.9, 6.9, 9.1, -8.9, - + 3.7, 0.1, 0.9, 1.3, 7*0, - 6 -90.4, 7.3, 7.0, -1.5, - + 1.0, -0.7, -0.5, -0.4, 8*0, - 7 -1.2, -7.9, 9.3, - + 2.0, 0.7, 0.3, 0.7, 9*0, - 8 -7.0, -4.3, - + 4.2, 1.7, -0.3, -0.4, 10*0, - 9 -8.2/ - DATA (GY1D(I),I=4646,4725) / - + 0.3, 0.1, -0.4, 0.3, 11*0, - O -1.1, 1.2, -0.1, -0.1, 12*0, - 1 4.0, -0.2, 0.4, 13*0, - 2 -0.4, 0.0, 14*0, - 3 0.1, 16*0/ -C g(n,m) for 2005 - DATA (GY1D(I),I=4726,4870) /0, - O-29554.63,-2337.24,1336.30,920.55,-227.00, 73.60, 79.88, 24.80, - + 5.58, -2.17, 2.95, -2.15, -0.16, 2*0, - 1-1669.05,3047.69,-2305.83,797.96,354.41, 69.56,-74.46, 7.62, - + 9.76, -6.12, -1.60, -0.29, -0.88, 3*0, - 2 1657.76,1246.39,210.65,208.95, 76.74, -1.65,-11.73, - + 3.58, 1.42, -1.88, 0.21, 0.30, 4*0, - 3 672.51,-379.86,-136.54,-151.34, 38.73, -6.88, - + -6.94, -2.35, 1.44, 0.89, 0.28, 5*0, - 4 100.00,-168.05,-14.58, 12.30,-18.11, 5.01, - + -0.15, -0.31, -0.38, -0.43, 6*0, - 5 -13.55, 14.58, 9.37, 10.17,-10.76, - + 3.06, 0.29, 0.96, 1.18, 7*0, - 6 -86.36, 5.42, 9.36, -1.25, - + 0.29, -0.79, -0.30, -0.37, 8*0, - 7 1.94,-11.25, 8.76, - + 2.06, 0.53, 0.46, 0.75, 9*0, - 8 -4.87, -6.66, - + 3.77, 1.80, -0.35, -0.26, 10*0, - 9 -9.22/ - DATA (GY1D(I),I=4871,4950) / - + -0.21, 0.16, -0.36, 0.35, 11*0, - O -2.09, 0.96, 0.08, -0.05, 12*0, - 1 3.99, -0.49, 0.41, 13*0, - 2 -0.08, -0.10, 14*0, - 3 -0.18, 16*0/ -C g(n,m) for 2010 - DATA (GY1D(I),I=4951,5095) /0, - O -29496.57,-2396.06,1339.85,912.66, -230.87,72.78,80.44,24.41,5.5, - + -1.94, 3.05, -2.12, -0.09, 2*0, - 1 -1586.42, 3026.34, -2326.54,808.97,357.29,68.69, -75.0,8.21,9.45, - + -6.24, -1.48, -0.21, -0.89, 3*0, - 2 1668.17, 1232.1, 166.58, 200.26, 75.92, -4.55, -14.5, 3.45, - + 0.89, -2.03, 0.3, 0.31, 4*0, - 3 633.73, -356.83, -141.05, -141.4, 45.24, -5.59, -5.27, - + -1.07, 1.65, 1.04, 0.42, 5*0, - 4 89.4, -163.17, -22.83, 14.0, -19.34, 3.13, - + -0.16, -0.51, -0.63, -0.45, 6*0, - 5 -8.03, 13.1, 10.46, 11.61, -12.38, - + 2.45, 0.54, 0.95, 1.08, 7*0, - 6 -78.09, 1.64, 10.85, -0.76, - + -0.33, -0.79, -0.11, -0.31, 8*0, - 7 4.92, -14.05, 8.43, - + 2.13, 0.37, 0.52, 0.78, 9*0, - 8 -3.54, -8.42, - + 3.09, 1.79, -0.39, -0.18, 10*0, - 9 -10.08/ - DATA (GY1D(I),I=5096,5175) / - + -1.03, 0.12, -0.37, 0.38, 11*0, - O -2.8, 0.75, 0.21, 0.02, 12*0, - 1 3.75, -0.77, 0.42, 13*0, - 2 0.04, -0.26, 14*0, - 3 -0.26, 16*0/ -C g(n,m) for 2015 - DATA (GY1D(I),I=5176,5320) /0, - O -29442.0, -2445.1, 1350.7, 907.6, -232.6, 70.0, 81.6, 24.2, 5.4, - + -1.9, 3.1, -1.9, 0.0, 2*0, - 1 -1501.0, 3012.9, -2352.3, 813.7, 360.1, 67.7, -76.1, 8.8, 8.8, - + -6.3, -1.5, -0.2, -0.9, 3*0, - 2 1676.7, 1225.6, 120.4, 192.4, 72.7, -6.8, -16.9, 3.1, - + 0.1, -2.3, 0.4, 0.4, 4*0, - 3 582.0, -334.9, -140.9, -129.9, 51.8, -3.2, -3.3, - + 0.5, 2.0, 1.2, 0.5, 5*0, - 4 70.4, -157.5, -28.9, 15.0, -20.6, 0.7, - + -0.5, -0.8, -0.8, -0.5, 6*0, - 5 4.1, 13.2, 9.4, 13.4, -13.3, - + 1.8, 0.6, 0.9, 1.0, 7*0, - 6 -70.9, -2.8, 11.7, -0.1, - + -0.7, -0.7, 0.1, -0.2, 8*0, - 7 6.8, -15.9, 8.7, - + 2.1, 0.2, 0.5, 0.8, 9*0, - 8 -2.0, -9.1, - + 2.4, 1.7, -0.3, -0.1, 10*0, - 9 -10.5/ - DATA (GY1D(I),I=5321,5400) / - + -1.8, -0.2, -0.4, 0.3, 11*0, - O -3.6, 0.4, 0.2, 0.1, 12*0, - 1 3.5, -0.9, 0.5, 13*0, - 2 0.0, -0.4, 14*0, - 3 -0.3, 16*0/ -C h(n,m) for 1900 - DATA (HY1D(I),I=1,145) /16*0, - 1 5922, -1061, -330, 195, -210, -9, -45, 8, -20, - + 2, 0, 0, 0, 3*0, - 2 1121, 3, -69, 53, 83, -13, -14, 14, - + 1, 0, 0, 0, 4*0, - 3 523, -210, -33, 2, -10, 7, 5, - + 2, 0, 0, 0, 5*0, - 4 -75, -124, -35, -1, -13, -3, - + 6, 0, 0, 0, 6*0, - 5 3, 36, 28, 5, -2, - + -4, 0, 0, 0, 7*0, - 6 -69, -12, 16, 8, - + 0, 0, 0, 0, 8*0, - 7 -22, -5, 10, - + -2, 0, 0, 0, 9*0, - 8 -18, -2, - + 4, 0, 0, 0, 10*0, - 9 2/ - DATA (HY1D(I),I=146,225) / - + 0, 0, 0, 0, 11*0, - O -6, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1905 - DATA (HY1D(I),I=226,370) /16*0, - 1 5909, -1086, -357, 203, -193, -7, -46, 8, -20, - + 2, 0, 0, 0, 3*0, - 2 1065, 34, -77, 56, 86, -14, -15, 14, - + 1, 0, 0, 0, 4*0, - 3 480, -201, -32, 4, -11, 7, 5, - + 2, 0, 0, 0, 5*0, - 4 -65, -125, -32, 0, -13, -3, - + 6, 0, 0, 0, 6*0, - 5 11, 32, 28, 5, -2, - + -4, 0, 0, 0, 7*0, - 6 -67, -12, 16, 8, - + 0, 0, 0, 0, 8*0, - 7 -22, -5, 10, - + -2, 0, 0, 0, 9*0, - 8 -18, -2, - + 4, 0, 0, 0, 10*0, - 9 2/ - DATA (HY1D(I),I=371,450) / - + 0, 0, 0, 0, 11*0, - O -6, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1910 - DATA (HY1D(I),I=451,595) /16*0, - 1 5898, -1128, -389, 211, -172, -5, -47, 8, -20, - + 2, 0, 0, 0, 3*0, - 2 1000, 62, -90, 57, 89, -14, -15, 14, - + 1, 0, 0, 0, 4*0, - 3 425, -189, -33, 5, -12, 6, 5, - + 2, 0, 0, 0, 5*0, - 4 -55, -126, -29, 1, -13, -3, - + 6, 0, 0, 0, 6*0, - 5 21, 28, 28, 5, -2, - + -4, 0, 0, 0, 7*0, - 6 -65, -13, 16, 8, - + 0, 0, 0, 0, 8*0, - 7 -22, -5, 10, - + -2, 0, 0, 0, 9*0, - 8 -18, -2, - + 4, 0, 0, 0, 10*0, - 9 2/ - DATA (HY1D(I),I=596,675) / - + 0, 0, 0, 0, 11*0, - O -6, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1915 - DATA (HY1D(I),I=676,820) /16*0, - 1 5875, -1191, -421, 218, -148, -2, -48, 8, -20, - + 2, 0, 0, 0, 3*0, - 2 917, 84, -109, 58, 93, -14, -15, 14, - + 1, 0, 0, 0, 4*0, - 3 360, -173, -34, 8, -12, 6, 5, - + 2, 0, 0, 0, 5*0, - 4 -51, -126, -26, 2, -13, -3, - + 6, 0, 0, 0, 6*0, - 5 32, 23, 28, 5, -2, - + -4, 0, 0, 0, 7*0, - 6 -62, -15, 16, 8, - + 0, 0, 0, 0, 8*0, - 7 -22, -5, 10, - + -2, 0, 0, 0, 9*0, - 8 -18, -2, - + 4, 0, 0, 0, 10*0, - 9 2/ - DATA (HY1D(I),I=821,900) / - + 0, 0, 0, 0, 11*0, - O -6, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1920 - DATA (HY1D(I),I=901,1045) /16*0, - 1 5845, -1259, -445, 220, -122, 0, -49, 8, -20, - + 2, 0, 0, 0, 3*0, - 2 823, 103, -134, 58, 96, -14, -15, 14, - + 1, 0, 0, 0, 4*0, - 3 293, -153, -38, 11, -13, 6, 5, - + 2, 0, 0, 0, 5*0, - 4 -57, -125, -22, 4, -14, -3, - + 6, 0, 0, 0, 6*0, - 5 43, 18, 28, 5, -2, - + -4, 0, 0, 0, 7*0, - 6 -57, -16, 17, 9, - + 0, 0, 0, 0, 8*0, - 7 -22, -5, 10, - + -2, 0, 0, 0, 9*0, - 8 -19, -2, - + 4, 0, 0, 0, 10*0, - 9 2/ - DATA (HY1D(I),I=1046,1125) / - + 0, 0, 0, 0, 11*0, - O -6, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1925 - DATA (HY1D(I),I=1126,1270) /16*0, - 1 5817, -1334, -462, 216, -96, 3, -50, 8, -20, - + 2, 0, 0, 0, 3*0, - 2 728, 119, -163, 58, 99, -14, -15, 14, - + 1, 0, 0, 0, 4*0, - 3 229, -130, -44, 14, -14, 6, 5, - + 2, 0, 0, 0, 5*0, - 4 -70, -122, -18, 5, -14, -3, - + 6, 0, 0, 0, 6*0, - 5 51, 13, 29, 5, -2, - + -4, 0, 0, 0, 7*0, - 6 -52, -17, 17, 9, - + 0, 0, 0, 0, 8*0, - 7 -21, -5, 10, - + -2, 0, 0, 0, 9*0, - 8 -19, -2, - + 4, 0, 0, 0, 10*0, - 9 2/ - DATA (HY1D(I),I=1271,1350) / - + 0, 0, 0, 0, 11*0, - O -6, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1930 - DATA (HY1D(I),I=1351,1495) /16*0, - 1 5808, -1424, -480, 205, -72, 4, -51, 8, -20, - + 2, 0, 0, 0, 3*0, - 2 644, 133, -195, 60, 102, -15, -15, 14, - + 1, 0, 0, 0, 4*0, - 3 166, -109, -53, 19, -14, 5, 5, - + 2, 0, 0, 0, 5*0, - 4 -90, -118, -16, 6, -14, -3, - + 6, 0, 0, 0, 6*0, - 5 58, 8, 29, 5, -2, - + -4, 0, 0, 0, 7*0, - 6 -46, -18, 18, 9, - + 0, 0, 0, 0, 8*0, - 7 -20, -5, 10, - + -2, 0, 0, 0, 9*0, - 8 -19, -2, - + 4, 0, 0, 0, 10*0, - 9 2/ - DATA (HY1D(I),I=1496,1575) / - + 0, 0, 0, 0, 11*0, - O -6, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1935 - DATA (HY1D(I),I=1576,1720) /16*0, - 1 5812, -1520, -494, 188, -51, 4, -52, 8, -20, - + 2, 0, 0, 0, 3*0, - 2 586, 146, -226, 64, 104, -17, -15, 15, - + 1, 0, 0, 0, 4*0, - 3 101, -90, -64, 25, -14, 5, 5, - + 2, 0, 0, 0, 5*0, - 4 -114, -115, -15, 7, -15, -3, - + 6, 0, 0, 0, 6*0, - 5 64, 4, 29, 5, -3, - + -4, 0, 0, 0, 7*0, - 6 -40, -19, 18, 9, - + 0, 0, 0, 0, 8*0, - 7 -19, -5, 11, - + -1, 0, 0, 0, 9*0, - 8 -19, -2, - + 4, 0, 0, 0, 10*0, - 9 2/ - DATA (HY1D(I),I=1721,1800) / - + 0, 0, 0, 0, 11*0, - O -6, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1940 - DATA (HY1D(I),I=1801,1945) /16*0, - 1 5821, -1614, -499, 169, -33, 4, -52, 8, -21, - + 2, 0, 0, 0, 3*0, - 2 528, 163, -252, 71, 105, -18, -14, 15, - + 1, 0, 0, 0, 4*0, - 3 43, -72, -75, 33, -14, 5, 5, - + 2, 0, 0, 0, 5*0, - 4 -141, -113, -15, 7, -15, -3, - + 6, 0, 0, 0, 6*0, - 5 69, 0, 29, 5, -3, - + -4, 0, 0, 0, 7*0, - 6 -33, -20, 19, 9, - + 0, 0, 0, 0, 8*0, - 7 -19, -5, 11, - + -1, 0, 0, 0, 9*0, - 8 -19, -2, - + 4, 0, 0, 0, 10*0, - 9 2/ - DATA (HY1D(I),I=1946,2025) / - + 0, 0, 0, 0, 11*0, - O -6, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1945 - DATA (HY1D(I),I=2026,2170) /16*0, - 1 5810, -1702, -499, 144, -12, 6, -45, 12, -27, - + 5, 0, 0, 0, 3*0, - 2 477, 186, -276, 95, 100, -18, -21, 17, - + 1, 0, 0, 0, 4*0, - 3 -11, -55, -67, 16, 2, -12, 29, - + -20, 0, 0, 0, 5*0, - 4 -178, -119, -9, 6, -7, -9, - + -1, 0, 0, 0, 6*0, - 5 82, -16, 28, 2, 4, - + -6, 0, 0, 0, 7*0, - 6 -39, -17, 18, 9, - + 6, 0, 0, 0, 8*0, - 7 -22, 3, 6, - + -4, 0, 0, 0, 9*0, - 8 -11, 1, - + -2, 0, 0, 0, 10*0, - 9 8/ - DATA (HY1D(I),I=2171,2250) / - + 0, 0, 0, 0, 11*0, - O -2, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1950 - DATA (HY1D(I),I=2251,2395) /16*0, - 1 5815, -1810, -476, 136, 3, -1, -35, 5, -24, - + 13, 0, 0, 0, 3*0, - 2 381, 206, -278, 103, 99, -17, -22, 19, - + -2, 0, 0, 0, 4*0, - 3 -46, -37, -87, 33, 0, 0, 12, - + -10, 0, 0, 0, 5*0, - 4 -210, -122, -12, 10, -21, 2, - + 2, 0, 0, 0, 6*0, - 5 80, -12, 36, -8, 2, - + -3, 0, 0, 0, 7*0, - 6 -30, -18, 17, 8, - + 6, 0, 0, 0, 8*0, - 7 -16, -4, 8, - + -3, 0, 0, 0, 9*0, - 8 -17, -11, - + 6, 0, 0, 0, 10*0, - 9 -7/ - DATA (HY1D(I),I=2396,2475) / - + 11, 0, 0, 0, 11*0, - O 8, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1955 - DATA (HY1D(I),I=2476,2620) /16*0, - 1 5820, -1898, -462, 133, 15, -9, -50, 10, -11, - + -4, 0, 0, 0, 3*0, - 2 291, 216, -274, 110, 96, -24, -15, 12, - + 0, 0, 0, 0, 4*0, - 3 -83, -23, -98, 48, -4, 5, 7, - + -8, 0, 0, 0, 5*0, - 4 -230, -121, -16, 8, -23, 6, - + -2, 0, 0, 0, 6*0, - 5 78, -12, 28, 3, -2, - + -4, 0, 0, 0, 7*0, - 6 -24, -20, 23, 10, - + 1, 0, 0, 0, 8*0, - 7 -18, -4, 7, - + -3, 0, 0, 0, 9*0, - 8 -13, -6, - + 7, 0, 0, 0, 10*0, - 9 5/ - DATA (HY1D(I),I=2621,2700) / - + -1, 0, 0, 0, 11*0, - O -3, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1960 - DATA (HY1D(I),I=2701,2845) /16*0, - 1 5791, -1967, -414, 135, 16, -10, -55, 11, -18, - + 4, 0, 0, 0, 3*0, - 2 206, 224, -278, 125, 99, -28, -14, 12, - + 1, 0, 0, 0, 4*0, - 3 -130, 3, -117, 60, -6, 7, 2, - + 0, 0, 0, 0, 5*0, - 4 -255, -114, -20, 7, -18, 0, - + 2, 0, 0, 0, 6*0, - 5 81, -11, 23, 4, -3, - + -5, 0, 0, 0, 7*0, - 6 -17, -18, 23, 9, - + 1, 0, 0, 0, 8*0, - 7 -17, 1, 8, - + -1, 0, 0, 0, 9*0, - 8 -20, 0, - + 6, 0, 0, 0, 10*0, - 9 5/ - DATA (HY1D(I),I=2846,2925) / - + 0, 0, 0, 0, 11*0, - O -7, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1965 - DATA (HY1D(I),I=2926,3070) /16*0, - 1 5776, -2016, -404, 148, 19, -11, -61, 7, -22, - + 2, 0, 0, 0, 3*0, - 2 114, 240, -269, 128, 100, -27, -12, 15, - + 1, 0, 0, 0, 4*0, - 3 -165, 13, -126, 68, -2, 9, 7, - + 2, 0, 0, 0, 5*0, - 4 -269, -97, -32, 6, -16, -4, - + 6, 0, 0, 0, 6*0, - 5 81, -8, 26, 4, -5, - + -4, 0, 0, 0, 7*0, - 6 -7, -23, 24, 10, - + 0, 0, 0, 0, 8*0, - 7 -12, -3, 10, - + -2, 0, 0, 0, 9*0, - 8 -17, -4, - + 3, 0, 0, 0, 10*0, - 9 1/ - DATA (HY1D(I),I=3071,3150) / - + 0, 0, 0, 0, 11*0, - O -6, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1970 - DATA (HY1D(I),I=3151,3295) /16*0, - 1 5737, -2047, -366, 167, 26, -12, -70, 7, -21, - + 1, 0, 0, 0, 3*0, - 2 25, 251, -266, 139, 100, -27, -15, 16, - + 1, 0, 0, 0, 4*0, - 3 -196, 26, -139, 72, -4, 6, 6, - + 3, 0, 0, 0, 5*0, - 4 -279, -91, -37, 8, -17, -4, - + 4, 0, 0, 0, 6*0, - 5 83, -6, 23, 6, -5, - + -4, 0, 0, 0, 7*0, - 6 1, -23, 21, 10, - + 0, 0, 0, 0, 8*0, - 7 -11, -6, 11, - + -1, 0, 0, 0, 9*0, - 8 -16, -2, - + 3, 0, 0, 0, 10*0, - 9 1/ - DATA (HY1D(I),I=3296,3375) / - + 1, 0, 0, 0, 11*0, - O -4, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1975 - DATA (HY1D(I),I=3376,3520) /16*0, - 1 5675, -2067, -333, 191, 31, -13, -77, 6, -21, - + 1, 0, 0, 0, 3*0, - 2 -68, 262, -265, 148, 99, -26, -16, 16, - + 1, 0, 0, 0, 4*0, - 3 -223, 39, -152, 75, -5, 4, 7, - + 3, 0, 0, 0, 5*0, - 4 -288, -83, -41, 10, -19, -4, - + 4, 0, 0, 0, 6*0, - 5 88, -4, 22, 6, -5, - + -4, 0, 0, 0, 7*0, - 6 11, -23, 18, 10, - + -1, 0, 0, 0, 8*0, - 7 -12, -10, 11, - + -1, 0, 0, 0, 9*0, - 8 -17, -3, - + 3, 0, 0, 0, 10*0, - 9 1/ - DATA (HY1D(I),I=3521,3600) / - + 1, 0, 0, 0, 11*0, - O -5, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1980 - DATA (HY1D(I),I=3601,3745) /16*0, - 1 5604, -2129, -336, 212, 46, -15, -82, 7, -21, - + 1, 0, 0, 0, 3*0, - 2 -200, 271, -257, 150, 93, -27, -18, 16, - + 0, 0, 0, 0, 4*0, - 3 -252, 53, -151, 71, -5, 4, 9, - + 3, 0, 0, 0, 5*0, - 4 -297, -78, -43, 16, -22, -5, - + 6, 0, 0, 0, 6*0, - 5 92, -2, 18, 9, -6, - + -4, 0, 0, 0, 7*0, - 6 17, -23, 16, 9, - + 0, 0, 0, 0, 8*0, - 7 -10, -13, 10, - + -1, 0, 0, 0, 9*0, - 8 -15, -6, - + 4, 0, 0, 0, 10*0, - 9 2/ - DATA (HY1D(I),I=3746,3825) / - + 0, 0, 0, 0, 11*0, - O -6, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1985 - DATA (HY1D(I),I=3826,3970) /16*0, - 1 5500, -2197, -310, 232, 47, -16, -83, 8, -21, - + 1, 0, 0, 0, 3*0, - 2 -306, 284, -249, 150, 88, -27, -19, 15, - + 0, 0, 0, 0, 4*0, - 3 -297, 69, -154, 69, -2, 5, 9, - + 3, 0, 0, 0, 5*0, - 4 -297, -75, -48, 20, -23, -6, - + 6, 0, 0, 0, 6*0, - 5 95, -1, 17, 11, -6, - + -4, 0, 0, 0, 7*0, - 6 21, -23, 14, 9, - + 0, 0, 0, 0, 8*0, - 7 -7, -15, 9, - + -1, 0, 0, 0, 9*0, - 8 -11, -7, - + 4, 0, 0, 0, 10*0, - 9 2/ - DATA (HY1D(I),I=3971,4050) / - + 0, 0, 0, 0, 11*0, - O -6, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1990 - DATA (HY1D(I),I=4051,4195) /16*0, - 1 5406, -2279, -284, 247, 46, -16, -80, 10, -20, - + 2, 0, 0, 0, 3*0, - 2 -373, 293, -240, 154, 82, -26, -19, 15, - + 1, 0, 0, 0, 4*0, - 3 -352, 84, -153, 69, 0, 6, 11, - + 3, 0, 0, 0, 5*0, - 4 -299, -69, -52, 21, -22, -7, - + 6, 0, 0, 0, 6*0, - 5 97, 1, 17, 12, -7, - + -4, 0, 0, 0, 7*0, - 6 24, -23, 12, 9, - + 0, 0, 0, 0, 8*0, - 7 -4, -16, 8, - + -2, 0, 0, 0, 9*0, - 8 -10, -7, - + 3, 0, 0, 0, 10*0, - 9 2/ - DATA (HY1D(I),I=4196,4275) / - + -1, 0, 0, 0, 11*0, - O -6, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 1995 - DATA (HY1D(I),I=4276,4420) /16*0, - 1 5306, -2366, -262, 262, 46, -17, -69, 11, -20, - + 1, 0, 0, 0, 3*0, - 2 -413, 302, -236, 165, 72, -25, -21, 15, - + 0, 0, 0, 0, 4*0, - 3 -427, 97, -143, 67, 4, 8, 12, - + 4, 0, 0, 0, 5*0, - 4 -306, -55, -58, 24, -23, -6, - + 5, 0, 0, 0, 6*0, - 5 107, 1, 17, 15, -8, - + -5, 0, 0, 0, 7*0, - 6 36, -24, 11, 8, - + -1, 0, 0, 0, 8*0, - 7 -6, -16, 5, - + -2, 0, 0, 0, 9*0, - 8 -4, -8, - + 1, 0, 0, 0, 10*0, - 9 3/ - DATA (HY1D(I),I=4421,4500) / - + -2, 0, 0, 0, 11*0, - O -7, 0, 0, 0, 12*0, - 1 0, 0, 0, 13*0, - 2 0, 0, 14*0, - 3 0, 16*0/ -C h(n,m) for 2000 - DATA (HY1D(I),I=4501,4645) /16*0, - 1 5186.1,-2481.6,-227.6, 272.6, 43.8, -17.4, -64.6, 11.9, -19.7, - + 1.7, 0.1, -0.4, -0.9, 3*0, - 2 -458.0, 293.4,-231.9, 171.9, 63.7, -24.2, -21.5, 13.4, - + 0.0, 1.3, 0.3, 0.2, 4*0, - 3 -491.1, 119.8,-133.1, 65.1, 6.2, 8.5, 12.5, - + 4.0, -0.9, 2.5, 1.8, 5*0, - 4 -303.8, -39.3, -61.2, 24.0, -21.5, -6.2, - + 4.9, -2.6, -2.6, -0.4, 6*0, - 5 106.3, 0.7, 14.8, 15.5, -8.4, - + -5.9, 0.9, 0.7, -1.0, 7*0, - 6 43.8, -25.4, 8.9, 8.4, - + -1.2, -0.7, 0.3, -0.1, 8*0, - 7 -5.8, -14.9, 3.8, - + -2.9, -2.8, 0.0, 0.7, 9*0, - 8 -2.1, -8.2, - + 0.2, -0.9, 0.0, 0.3, 10*0, - 9 4.8/ - DATA (HY1D(I),I=4646,4725) / - + -2.2, -1.2, 0.3, 0.6, 11*0, - O -7.4, -1.9, -0.9, 0.3, 12*0, - 1 -0.9, -0.4, -0.2, 13*0, - 2 0.8, -0.5, 14*0, - 3 -0.9, 16*0/ -C h(n,m) for 2005 - DATA (HY1D(I),I=4726,4870) /16*0, - 1 5077.99,-2594.50,-198.86,282.07, 42.72,-20.33,-61.14, 11.20, - + -20.11, 2.19, 0.26, -0.55, -0.76, 3*0, - 2 -515.43,269.72,-225.23,180.25, 54.75,-22.57,-20.88,12.69, - + 0.10, 1.44, 0.23, 0.33, 4*0, - 3 -524.72,145.15,-123.45, 63.63, 6.82, 9.83,12.67, - + 4.46, -0.77, 2.38, 1.72, 5*0, - 4 -305.36,-19.57,-63.53, 25.35,-19.71,-6.72, - + 4.76, -2.27, -2.63, -0.54, 6*0, - 5 103.85, 0.24, 10.93, 16.22, -8.16, - + -6.58, 0.90, 0.61, -1.07, 7*0, - 6 50.94,-26.32, 7.61, 8.10, - + -1.01, -0.58, 0.40, -0.04, 8*0, - 7 -4.64,-12.76, 2.92, - + -3.47, -2.69, 0.01, 0.63, 9*0, - 8 -0.06, -7.73, - + -0.86, -1.08, 0.02, 0.21, 10*0, - 9 6.01/ - DATA (HY1D(I),I=4871,4950) / - + -2.31, -1.58, 0.28, 0.53, 11*0, - O -7.93, -1.90, -0.87, 0.38, 12*0, - 1 -1.39, -0.34, -0.22, 13*0, - 2 0.88, -0.57, 14*0, - 3 -0.82, 16*0/ -C h(n,m) for 2010 - DATA (HY1D(I),I=4951,5095) /16*0, - 1 4944.26, -2708.54,-160.4,286.48,44.58,-20.9, -57.8,10.84, -20.54, - + 2.73, 0.13, -0.87, -0.87, 3*0, - 2 -575.73, 251.75, -211.03, 189.01, 44.18, -21.2, -20.03, 11.51, - + -0.1, 1.67, 0.27, 0.3, 4*0, - 3 -537.03, 164.46, -118.06, 61.54, 6.54, 11.83, 12.75, - + 4.71, -0.66, 2.13, 1.66, 5*0, - 4 -309.72, -0.01, -66.26, 24.96, -17.41, -7.14, - + 4.44, -1.76, -2.49, -0.59, 6*0, - 5 101.04, 3.02, 7.03, 16.71, -7.42, - + -7.22, 0.85, 0.49, -1.14, 7*0, - 6 55.4, -27.61, 6.96, 7.97, - + -0.96, -0.39, 0.59, -0.07, 8*0, - 7 -3.28, -10.74, 2.14, - + -3.95, -2.51, 0.0, 0.54, 9*0, - 8 1.64, -6.08, - + -1.99, -1.27, 0.13, 0.1, 10*0, - 9 7.01/ - DATA (HY1D(I),I=5096,5175) / - + -1.97, -2.11, 0.27, 0.49, 11*0, - O -8.31, -1.94, -0.86, 0.44, 12*0, - 1 -1.86, -0.23, -0.25, 13*0, - 2 0.87, -0.53, 14*0, - 3 -0.79, 16*0/ -C h(n,m) for 2015 - DATA (HY1D(I),I=5176,5320) /16*0, - 1 4797.1, -2845.6, -115.3, 283.3, 47.3, -20.8, -54.1, 10.1, -21.6, - + 3.2, -0.1, -1.1, -0.9, 3*0, - 2 -641.9, 244.9, -188.7, 197, 33.2, -19.5, -18.3, 10.8, - + -0.4, 2.0, 0.4, 0.4, 4*0, - 3 -538.4, 180.9, -119.3, 58.9, 5.7, 13.3, 11.8, - + 4.6, -0.7, 1.9, 1.6, 5*0, - 4 -329.5, 16.0, -66.7, 24.4, -14.6, -6.8, - + 4.4, -1.1, -2.2, -0.5, 6*0, - 5 100.2, 7.3, 3.4, 16.2, -6.9, - + -7.9, 0.8, 0.3, -1.2, 7*0, - 6 62.6, -27.4, 5.7, 7.8, - + -0.6, -0.2, 0.7, -0.1, 8*0, - 7 -2.2, -9.1, 1.0, - + -4.2, -2.2, -0.1, 0.4, 9*0, - 8 2.1, -4.0, - + -2.8, -1.4, 0.3, -0.1, 10*0, - 9 8.4/ - DATA (HY1D(I),I=5321,5400) / - + -1.2, -2.5, 0.2, 0.4, 11*0, - O -8.7, -2.0, -0.9, 0.5, 12*0, - 1 -2.4, -0.1, -0.3, 13*0, - 2 0.7, -0.4, 14*0, - 3 -0.8, 16*0/ -C Secular variation rates are nominally okay through 2020 - DATA (GT1D(I),I=1,145) /0, - O 10.3, -8.7, 3.4, -0.7, -0.2, -0.3, 0.3, 0.2, 0.0, - + 0.0, 0.0, 0.0, 0.0, 2*0, - 1 18.1, -3.3, -5.5, 0.2, 0.5, -0.1, -0.2, 0.0, 0.0, - + 0.0, 0.0, 0.0, 0.0, 3*0, - 2 2.1, -0.7, -9.1, -1.3, -0.7, -0.5, -0.6, 0.0, - + 0.0, 0.0, 0.0, 0.0, 4*0, - 3 -10.1, 4.1, -0.1, 2.1, 1.3, 0.5, 0.0, - + 0.0, 0.0, 0.0, 0.0, 5*0, - 4 -4.3, 1.4, -1.2, 0.1, -0.2, 0.0, - + 0.0, 0.0, 0.0, 0.0, 6*0, - 5 3.9, 0.3, -0.6, 0.4, 0.0, - + 0.0, 0.0, 0.0, 0.0, 7*0, - 6 1.6, -0.8, 0.1, 0.0, - + 0.0, 0.0, 0.0, 0.0, 8*0, - 7 0.2, -0.4, 0.0, - + 0.0, 0.0, 0.0, 0.0, 9*0, - 8 0.3, 0.0, - + 0.0, 0.0, 0.0, 0.0, 10*0, - 9 0.0/ - DATA (GT1D(I),I=146,225) / - + 0.0, 0.0, 0.0, 0.0, 11*0, - O 0.0, 0.0, 0.0, 0.0, 12*0, - 1 0.0, 0.0, 0.0, 13*0, - 2 0.0, 0.0, 14*0, - 3 0.0, 16*0/ - DATA (HT1D(I),I=1,145) /16*0, - 1 -26.6, -27.4, 8.2, -1.3, 0.6, 0.0, 0.8, -0.3, 0.0, - + 0.0, 0.0, 0.0, 0.0, 3*0, - 2 -14.1, -0.4, 5.3, 1.7, -2.1, 0.4, 0.3, 0.0, - + 0.0, 0.0, 0.0, 0.0, 4*0, - 3 1.8, 2.9, -1.2, -0.7, -0.2, 0.1, 0.0, - + 0.0, 0.0, 0.0, 0.0, 5*0, - 4 -5.2, 3.4, 0.2, -0.3, 0.5, 0.0, - + 0.0, 0.0, 0.0, 0.0, 6*0, - 5 0.0, 0.9, -0.6, -0.2, 0.0, - + 0.0, 0.0, 0.0, 0.0, 7*0, - 6 1.0, 0.1, -0.3, 0.0, - + 0.0, 0.0, 0.0, 0.0, 8*0, - 7 -0.2, 0.3, 0.0, - + 0.0, 0.0, 0.0, 0.0, 9*0, - 8 0.0, 0.0, - + 0.0, 0.0, 0.0, 0.0, 10*0, - 9 0.0/ - DATA (HT1D(I),I=146,225) / - + 0.0, 0.0, 0.0, 0.0, 11*0, - O 0.0, 0.0, 0.0, 0.0, 12*0, - 1 0.0, 0.0, 0.0, 13*0, - 2 0.0, 0.0, 14*0, - 3 0.0, 16*0/ + SAVE DATEL, GYR, HYR, GT, HT, NEPO, EPOCH, NGHT, NMXE + DATA DATEL /-999./ C Do not need to load new coefficients if date has not changed ICHG = 0 IF (DATE .EQ. DATEL) GO TO 300 DATEL = DATE ICHG = 1 + +c Load coefficients + FILENAME='igrf13coeffs.txt' + if (.not. allocated(GYR)) then + call read_igrf(filename,GYR,HYR,GT,HT,NEPO,NGHT,EPOCH,NMXE) + endif + NGH=NGHT*NEPO C Trap out of range date: IF (DATE .LT. EPOCH(1)) GO TO 9100 @@ -1441,6 +145,7 @@ SUBROUTINE COFRM (DATE) 100 CONTINUE 110 CONTINUE + NGH=NGHT*NEPO NMAX = NMXE(IY) TIME = DATE T = TIME-EPOCH(IY) @@ -1478,9 +183,7 @@ SUBROUTINE COFRM (DATE) GV(I) = GB(I) / RNN GV(I1) = GB(I1) / RNN 200 I = I+2 - 300 CONTINUE - RETURN C Error trap diagnostics: From 2079814d316fe7efaca5a99e6017c684da8d69af Mon Sep 17 00:00:00 2001 From: Achim Morschhauser <21514612+sputnik-a@users.noreply.github.com> Date: Mon, 11 May 2020 14:12:35 +0200 Subject: [PATCH 019/226] Makefile adjusted for igrf module IGRF.f90 needs to be compiled first to make module available in linking. --- src/fortranapex/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fortranapex/Makefile b/src/fortranapex/Makefile index 0ef49522..d085135e 100644 --- a/src/fortranapex/Makefile +++ b/src/fortranapex/Makefile @@ -13,7 +13,7 @@ LIBS = #-L/global/apps/intel/mkl/10.1.0.015/ -shared-intel -Wl,--start-group /g PROG = apextest -OBJS = checkapexsh.o apexsh.o makeapexsh.o apex.o magfld.o igrf.o +OBJS = igrf.o checkapexsh.o apexsh.o makeapexsh.o apex.o magfld.o $(PROG) : $(OBJS) $(LD) $(LDFLAGS) $(OBJS) -o $(PROG) $(LIBS) From c41ebc7a329ce98963fa49c53a10b149fb07b628 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Sun, 10 Jan 2021 23:53:55 -0400 Subject: [PATCH 020/226] adding support for numpy.ndarray and numpy.datetime64 to helpers.subsol --- src/apexpy/helpers.py | 48 ++++++++++++++++++++++--------------------- tests/test_helpers.py | 26 +++++++++++++++++++++++ 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/src/apexpy/helpers.py b/src/apexpy/helpers.py index 3f362a58..525c0d8c 100644 --- a/src/apexpy/helpers.py +++ b/src/apexpy/helpers.py @@ -171,56 +171,58 @@ def subsol(datetime): """ # convert to year, day of year and seconds since midnight - year = datetime.year - doy = datetime.timetuple().tm_yday - ut = datetime.hour * 3600 + datetime.minute * 60 + datetime.second + if isinstance(datetime, dt.datetime): + year = np.asanyarray([datetime.year]) + doy = np.asanyarray([datetime.timetuple().tm_yday]) + ut = np.asanyarray([datetime.hour * 3600 + datetime.minute * 60 + datetime.second]) + elif isinstance(datetime, np.ndarray): + times = datetime.astype('datetime64[s]') # works for datetime of wrong precision or unix epoch + year_floor = times.astype('datetime64[Y]') + day_floor = times.astype('datetime64[D]') + year = year_floor.astype(int) + 1970 + doy = (day_floor - year_floor).astype(int) + 1 + ut = (times.astype('datetime64[s]') - day_floor).astype(float) + else: + raise ValueError("input must be datetime.datetime or numpy array") - if not 1601 <= year <= 2100: + if not (np.all(1601 <= year) and np.all(year <= 2100)): raise ValueError('Year must be in [1601, 2100]') yr = year - 2000 - nleap = int(np.floor((year - 1601.0) / 4.0)) + nleap = np.floor((year - 1601.0) / 4.0).astype(int) nleap -= 99 - if year <= 1900: - ncent = int(np.floor((year - 1601.0) / 100.0)) + mask_1900 = year <= 1900 + if np.any(mask_1900): + ncent = np.floor((year[mask_1900] - 1601.0) / 100.0).astype(int) ncent = 3 - ncent - nleap = nleap + ncent + nleap[mask_1900] = nleap[mask_1900] + ncent l0 = -79.549 + (-0.238699 * (yr - 4.0 * nleap) + 3.08514e-2 * nleap) g0 = -2.472 + (-0.2558905 * (yr - 4.0 * nleap) - 3.79617e-2 * nleap) - # Days (including fraction) since 12 UT on January 1 of IYR: df = (ut / 86400.0 - 1.5) + doy - # Mean longitude of Sun: lmean = l0 + 0.9856474 * df - # Mean anomaly in radians: grad = np.radians(g0 + 0.9856003 * df) - # Ecliptic longitude: - lmrad = np.radians(lmean + 1.915 * np.sin(grad) - + 0.020 * np.sin(2.0 * grad)) + lmrad = np.radians(lmean + 1.915 * np.sin(grad) + 0.020 * np.sin(2.0 * grad)) sinlm = np.sin(lmrad) - # Obliquity of ecliptic in radians: epsrad = np.radians(23.439 - 4e-7 * (df + 365 * yr + nleap)) - # Right ascension: alpha = np.degrees(np.arctan2(np.cos(epsrad) * sinlm, np.cos(lmrad))) - # Declination, which is also the subsolar latitude: sslat = np.degrees(np.arcsin(np.sin(epsrad) * sinlm)) - # Equation of time (degrees): etdeg = lmean - alpha - nrot = round(etdeg / 360.0) + nrot = np.round(etdeg / 360.0) etdeg = etdeg - 360.0 * nrot - # Subsolar longitude: - sslon = 180.0 - (ut / 240.0 + etdeg) # Earth rotates one degree every 240 s. - nrot = round(sslon / 360.0) + sslon = 180.0 - (ut / 240.0 + etdeg) # Earth rotates one degree every 240 s. + nrot = np.round(sslon / 360.0) sslon = sslon - 360.0 * nrot - + if isinstance(datetime, dt.datetime): + return sslat[0], sslon[0] return sslat, sslon diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 97d4cd44..2364a9f3 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -155,5 +155,31 @@ def test_subsol(): assert_allclose(helpers.subsol(dt.datetime(2100, 12, 31, 23, 59, 59)), (-23.021061422069053, -179.23129780639425)) + +def datetime64_to_datetime(dt64): + """Convert a numpy.datetime64 to datetime.datetime, works outside 32 bit int seconds range of 1970 + """ + year_floor = dt64.astype('datetime64[Y]') + month_floor = dt64.astype('datetime64[M]') + day_floor = dt64.astype('datetime64[D]') + year = year_floor.astype(int) + 1970 + month = (month_floor - year_floor).astype('timedelta64[M]').astype(int) + 1 + day = (day_floor - month_floor).astype('timedelta64[D]').astype(int) + 1 + return dt.datetime(year, month, day) + + +def test_subsol_array(): + """Verify that getting the subsolar point at an array of numpy.datetime64 is the same as converting individual + datetime.datetime + """ + dates = np.arange(np.datetime64("1601"), np.datetime64("2100"), np.timedelta64(100, 'D')).astype('datetime64[s]') + sslat, sslon = helpers.subsol(dates) + for i, date in enumerate(dates): + datetime = datetime64_to_datetime(date) + true_sslat, true_sslon = helpers.subsol(datetime) + assert sslat[i] == true_sslat + assert sslon[i] == true_sslon + + if __name__ == '__main__': pytest.main() From 4db3fafa63063e0369a58d466330c8c2651ffea2 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Fri, 22 Jan 2021 17:24:19 -0400 Subject: [PATCH 021/226] adding support for numpy.ndarray and numpy.datetime64 to helpers.subsol --- src/apexpy/helpers.py | 50 ++++++++++++++++++++++--------------------- tests/test_helpers.py | 28 +++++++++++++++++++++++- 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/apexpy/helpers.py b/src/apexpy/helpers.py index 3f362a58..08eb7b6a 100644 --- a/src/apexpy/helpers.py +++ b/src/apexpy/helpers.py @@ -171,56 +171,58 @@ def subsol(datetime): """ # convert to year, day of year and seconds since midnight - year = datetime.year - doy = datetime.timetuple().tm_yday - ut = datetime.hour * 3600 + datetime.minute * 60 + datetime.second + if isinstance(datetime, dt.datetime): + year = np.asanyarray([datetime.year]) + doy = np.asanyarray([datetime.timetuple().tm_yday]) + ut = np.asanyarray([datetime.hour * 3600 + datetime.minute * 60 + datetime.second]) + elif isinstance(datetime, np.ndarray): + times = datetime.astype('datetime64[s]') # works for datetime of wrong precision or unix epoch + year_floor = times.astype('datetime64[Y]') + day_floor = times.astype('datetime64[D]') + year = year_floor.astype(int) + 1970 + doy = (day_floor - year_floor).astype(int) + 1 + ut = (times.astype('datetime64[s]') - day_floor).astype(float) + else: + raise ValueError("input must be datetime.datetime or numpy array") - if not 1601 <= year <= 2100: + if not (np.all(1601 <= year) and np.all(year <= 2100)): raise ValueError('Year must be in [1601, 2100]') yr = year - 2000 - nleap = int(np.floor((year - 1601.0) / 4.0)) + nleap = np.floor((year - 1601.0) / 4.0).astype(int) nleap -= 99 - if year <= 1900: - ncent = int(np.floor((year - 1601.0) / 100.0)) + mask_1900 = year <= 1900 + if np.any(mask_1900): + ncent = np.floor((year[mask_1900] - 1601.0) / 100.0).astype(int) ncent = 3 - ncent - nleap = nleap + ncent + nleap[mask_1900] = nleap[mask_1900] + ncent l0 = -79.549 + (-0.238699 * (yr - 4.0 * nleap) + 3.08514e-2 * nleap) g0 = -2.472 + (-0.2558905 * (yr - 4.0 * nleap) - 3.79617e-2 * nleap) - # Days (including fraction) since 12 UT on January 1 of IYR: df = (ut / 86400.0 - 1.5) + doy - # Mean longitude of Sun: lmean = l0 + 0.9856474 * df - # Mean anomaly in radians: grad = np.radians(g0 + 0.9856003 * df) - # Ecliptic longitude: - lmrad = np.radians(lmean + 1.915 * np.sin(grad) - + 0.020 * np.sin(2.0 * grad)) + lmrad = np.radians(lmean + 1.915 * np.sin(grad) + 0.020 * np.sin(2.0 * grad)) sinlm = np.sin(lmrad) - # Obliquity of ecliptic in radians: epsrad = np.radians(23.439 - 4e-7 * (df + 365 * yr + nleap)) - # Right ascension: alpha = np.degrees(np.arctan2(np.cos(epsrad) * sinlm, np.cos(lmrad))) - # Declination, which is also the subsolar latitude: sslat = np.degrees(np.arcsin(np.sin(epsrad) * sinlm)) - # Equation of time (degrees): etdeg = lmean - alpha - nrot = round(etdeg / 360.0) + nrot = np.round(etdeg / 360.0) etdeg = etdeg - 360.0 * nrot - # Subsolar longitude: - sslon = 180.0 - (ut / 240.0 + etdeg) # Earth rotates one degree every 240 s. - nrot = round(sslon / 360.0) + sslon = 180.0 - (ut / 240.0 + etdeg) # Earth rotates one degree every 240 s. + nrot = np.round(sslon / 360.0) sslon = sslon - 360.0 * nrot - - return sslat, sslon + if isinstance(datetime, dt.datetime): + return sslat[0], sslon[0] + return sslat, sslon \ No newline at end of file diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 97d4cd44..d671eaf1 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -155,5 +155,31 @@ def test_subsol(): assert_allclose(helpers.subsol(dt.datetime(2100, 12, 31, 23, 59, 59)), (-23.021061422069053, -179.23129780639425)) + +def datetime64_to_datetime(dt64): + """Convert a numpy.datetime64 to datetime.datetime, works outside 32 bit int seconds range of 1970 + """ + year_floor = dt64.astype('datetime64[Y]') + month_floor = dt64.astype('datetime64[M]') + day_floor = dt64.astype('datetime64[D]') + year = year_floor.astype(int) + 1970 + month = (month_floor - year_floor).astype('timedelta64[M]').astype(int) + 1 + day = (day_floor - month_floor).astype('timedelta64[D]').astype(int) + 1 + return dt.datetime(year, month, day) + + +def test_subsol_array(): + """Verify that getting the subsolar point at an array of numpy.datetime64 is the same as converting individual + datetime.datetime + """ + dates = np.arange(np.datetime64("1601"), np.datetime64("2100"), np.timedelta64(100, 'D')).astype('datetime64[s]') + sslat, sslon = helpers.subsol(dates) + for i, date in enumerate(dates): + datetime = datetime64_to_datetime(date) + true_sslat, true_sslon = helpers.subsol(datetime) + assert sslat[i] == true_sslat + assert sslon[i] == true_sslon + + if __name__ == '__main__': - pytest.main() + pytest.main() \ No newline at end of file From f1ccae88a54a50c79a273eade425cdf8c91a3b8f Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 2 Feb 2021 10:35:17 -0500 Subject: [PATCH 022/226] STY: added docstrings Added some missing docstrings an simplified some tests. --- tests/test_fortranapex.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/test_fortranapex.py b/tests/test_fortranapex.py index cd9f7470..d9c5ba04 100644 --- a/tests/test_fortranapex.py +++ b/tests/test_fortranapex.py @@ -16,7 +16,10 @@ def test_apxg2q(): - fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), 2000) + """Test fortran apex geographic to quasi-dipole + """ + fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), + 2000) qlat, qlon, f1, f2, f = fa.apxg2q(60, 15, 100, 1) assert_allclose(qlat, 56.531288146972656) assert_allclose(qlon, 94.1068344116211) @@ -45,15 +48,19 @@ def test_apxg2all(): def test_apxq2g(): - fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), 2000) + """ Test fortran quasi-dipole to geographic + """ + fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), + 2000) glat, glon, error = fa.apxq2g(60, 15, 100, 1e-2) - assert_allclose(glat, 50.97946548461914) - assert_allclose(glon, -66.16902923583984) - assert_allclose(error, 0.00010020843910751864) + assert_allclose([glat, glon, error], + [50.97946548461914, -66.16902923583984, + 0.00010020843910751864], atol=1e-6) def test_g2q2d(): - fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), 2000) + fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), + 2000) for lat in [0, 30, 60, 89]: for lon in [-179, -90, 0, 90, 179]: qlat, qlon, _, _, _ = fa.apxg2q(lat, lon, 100, 0) From 5bf0787f6c1af217fff66ec6fbe34751f3cf15d8 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 2 Feb 2021 10:36:22 -0500 Subject: [PATCH 023/226] STY: improved PEP8 Made a handful of PEP8 improvemeents and added a missing docstring. --- tests/test_cmd.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 75a6f5f4..03b1b079 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -2,11 +2,10 @@ from __future__ import division, print_function, absolute_import, unicode_literals +import numpy as np import os import subprocess -import numpy as np - os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) @@ -92,10 +91,12 @@ def test_convert_single_line(): def test_convert_stdin_stdout(): - p = subprocess.Popen('echo 60 15 | apexpy geo apex 2015 --height 300', - shell=True, stdout=subprocess.PIPE) - stdout, _ = p.communicate() - p.wait() + """ Test use of pipe input to command-line call + """ + pipe = subprocess.Popen('echo 60 15 | apexpy geo apex 2015 --height 300', + shell=True, stdout=subprocess.PIPE) + stdout, _ = pipe.communicate() + pipe.wait() np.testing.assert_allclose(np.array(stdout.split(b' '), dtype=float), [57.469547, 93.639816], rtol=1e-4) From be4fb5692f4aaa2bd01a73a6e0ba92572eed9d58 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 2 Feb 2021 10:37:40 -0500 Subject: [PATCH 024/226] TST: fixed failing unit tests Fixed unit tests that were failing on Travis due to a change in warning behaviour. Also added some missing docstrings and added more constraints to the unit tests under scrutiny. --- tests/test_Apex.py | 73 ++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 7af98b61..1f800a97 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -359,9 +359,9 @@ def test_convert_invalid_transformation(): def test_geo2apex(): - A = Apex(date=2000, refh=300) - lat, lon = A.geo2apex(60, 15, 100) - assert_allclose((lat, lon), A._geo2apex(60, 15, 100)) + apex2000 = Apex(date=2000, refh=300) + lat, lon = apex2000.geo2apex(60, 15, 100) + assert_allclose((lat, lon), apex2000._geo2apex(60, 15, 100)) assert type(lat) != np.ndarray assert type(lon) != np.ndarray @@ -387,14 +387,16 @@ def test_geo2apex_invalid_lat(): def test_geo2apex_undefined_warning(): - A = Apex(date=2000, refh=10000) - with warnings.catch_warnings(record=True) as w: - ret = A.geo2apex(0, 0, 0) - A.geo2apex(0, 0, 0) - assert ret[0] == -9999 - assert len(w) == 2 - assert issubclass(w[-1].category, UserWarning) - assert 'set to -9999 where' in str(w[-1].message) + """Test warning and fill values for an undefined location + """ + with warnings.catch_warnings(record=True) as wmsg: + apex2000 = Apex(date=2000, refh=10000) + ret = apex2000.geo2apex(0, 0, 0) + + assert ret[0] == -9999 + assert len(wmsg) == 1 + assert issubclass(wmsg[-1].category, UserWarning) + assert 'set to -9999 where' in str(wmsg[-1].message) ###============================================================================ @@ -749,15 +751,17 @@ def test_map_to_height_same_height(): def test_map_to_height_conjugate(): - A = Apex(date=2000, refh=300) - assert_allclose(A.map_to_height(60, 15, 100, 10000, conjugate=True, - precision=1e-10), + """Test results of map_to_height using conjugacy + """ + apex2000 = Apex(date=2000, refh=300) + assert_allclose(apex2000.map_to_height(60, 15, 100, 10000, conjugate=True, + precision=1e-10), (-25.424892425537109, 27.310417175292969, - 1.2074182222931995e-6)) - assert_allclose(A.map_to_height(30, 170, 100, 500, conjugate=True, - precision=1e-2), + 1.2074182222931995e-6), atol=1e-6) + assert_allclose(apex2000.map_to_height(30, 170, 100, 500, conjugate=True, + precision=1e-2), (-13.76642894744873, 164.24259948730469, - 0.00056820799363777041)) + 0.00056820799363777041), atol=1e-6) def test_map_to_height_vectorization(): @@ -1189,28 +1193,21 @@ def test_basevectors_apex_delta(): def test_basevectors_apex_invalid_scalar(): - A = Apex(date=2000, refh=10000) - with warnings.catch_warnings(record=True) as w: - (f1, f2, f3, g1, g2, g3, d1, d2, d3, e1, e2, - e3) = A.basevectors_apex(0, 0, 0) - A.basevectors_apex(0, 0, 0) - assert len(w) == 2 - assert issubclass(w[-1].category, UserWarning) - assert 'set to -9999 where' in str(w[-1].message) + """ Test warning and fill values for calculating base vectors with bad value + """ + apex2000 = Apex(date=2000, refh=10000) + with warnings.catch_warnings(record=True) as wmsg: + base_vecs = apex2000.basevectors_apex(0, 0, 0) + + assert issubclass(wmsg[-1].category, UserWarning) + assert 'set to -9999 where' in str(wmsg[-1].message) invalid = [-9999, -9999, -9999] - assert not np.allclose(f1, invalid[:2]) - assert not np.allclose(f2, invalid[:2]) - assert_allclose(f3, invalid) - assert_allclose(g1, invalid) - assert_allclose(g2, invalid) - assert_allclose(g3, invalid) - assert_allclose(d1, invalid) - assert_allclose(d2, invalid) - assert_allclose(d3, invalid) - assert_allclose(e1, invalid) - assert_allclose(e2, invalid) - assert_allclose(e3, invalid) + for i, bvec in enumerate(base_vecs): + if i < 2: + assert not np.allclose(bvec, invalid[:2]) + else: + assert_allclose(bvec, invalid) ###============================================================================ From ad0b0dfe980b6de6fd8304bac10b5a875d5c1b3f Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 2 Feb 2021 10:42:46 -0500 Subject: [PATCH 025/226] STY: reverted style and added comments Reverted style include whitespace around code pertaining to a block comment and ajusted line length. --- src/apexpy/helpers.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/apexpy/helpers.py b/src/apexpy/helpers.py index 08eb7b6a..efa904a3 100644 --- a/src/apexpy/helpers.py +++ b/src/apexpy/helpers.py @@ -200,29 +200,41 @@ def subsol(datetime): l0 = -79.549 + (-0.238699 * (yr - 4.0 * nleap) + 3.08514e-2 * nleap) g0 = -2.472 + (-0.2558905 * (yr - 4.0 * nleap) - 3.79617e-2 * nleap) + # Days (including fraction) since 12 UT on January 1 of IYR: df = (ut / 86400.0 - 1.5) + doy + # Mean longitude of Sun: lmean = l0 + 0.9856474 * df + # Mean anomaly in radians: grad = np.radians(g0 + 0.9856003 * df) + # Ecliptic longitude: - lmrad = np.radians(lmean + 1.915 * np.sin(grad) + 0.020 * np.sin(2.0 * grad)) + lmrad = np.radians(lmean + 1.915 * np.sin(grad) + + 0.020 * np.sin(2.0 * grad)) sinlm = np.sin(lmrad) + # Obliquity of ecliptic in radians: epsrad = np.radians(23.439 - 4e-7 * (df + 365 * yr + nleap)) + # Right ascension: alpha = np.degrees(np.arctan2(np.cos(epsrad) * sinlm, np.cos(lmrad))) + # Declination, which is also the subsolar latitude: sslat = np.degrees(np.arcsin(np.sin(epsrad) * sinlm)) + # Equation of time (degrees): etdeg = lmean - alpha nrot = np.round(etdeg / 360.0) etdeg = etdeg - 360.0 * nrot - # Subsolar longitude: - sslon = 180.0 - (ut / 240.0 + etdeg) # Earth rotates one degree every 240 s. + + # Subsolar longitude calculation. Earth rotates one degree every 240 s. + sslon = 180.0 - (ut / 240.0 + etdeg) nrot = np.round(sslon / 360.0) sslon = sslon - 360.0 * nrot + + # Return a single value from the output if the input was a single value if isinstance(datetime, dt.datetime): return sslat[0], sslon[0] - return sslat, sslon \ No newline at end of file + return sslat, sslon From 46460ce5ba4cb3a5af79771db80e7455c83c55c5 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 2 Feb 2021 10:49:16 -0500 Subject: [PATCH 026/226] DOC: updated docstring style Future-proofed the new doctrings. --- tests/test_helpers.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index d671eaf1..0a3ba425 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -157,7 +157,7 @@ def test_subsol(): def datetime64_to_datetime(dt64): - """Convert a numpy.datetime64 to datetime.datetime, works outside 32 bit int seconds range of 1970 + """np.datetime64 to dt.datetime, works outside 32 bit int sec range of 1970 """ year_floor = dt64.astype('datetime64[Y]') month_floor = dt64.astype('datetime64[M]') @@ -169,8 +169,13 @@ def datetime64_to_datetime(dt64): def test_subsol_array(): - """Verify that getting the subsolar point at an array of numpy.datetime64 is the same as converting individual - datetime.datetime + """Verify subsolar point calculation using an array of np.datetime64 + + Notes + ----- + Tested by ensurnig the array of np.datetime64 is equivalent to converting + using single dt.datetime values + """ dates = np.arange(np.datetime64("1601"), np.datetime64("2100"), np.timedelta64(100, 'D')).astype('datetime64[s]') sslat, sslon = helpers.subsol(dates) @@ -182,4 +187,4 @@ def test_subsol_array(): if __name__ == '__main__': - pytest.main() \ No newline at end of file + pytest.main() From c605b738f9f319faf7dcbe53e9ea09c962339857 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 2 Feb 2021 11:00:03 -0500 Subject: [PATCH 027/226] DOC: updated changelog Updated the changelog with a summary of updates in this pull request. --- CHANGELOG.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f591370b..5959c9fa 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,13 @@ Changelog ========= +1.2.0 (2021-03-XX) +------------------ +* Improved the subsol routine to allow array input +* Improved PEP8 compliance +* Added some missing docstrings to unit tests + + 1.0.3 (2018-04-05) ----------------------------------------- * Updated badges and added DOI From 99f31adfe9ffc3a4de2e99684d7df8f4a4b534e7 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Wed, 3 Feb 2021 11:43:00 -0400 Subject: [PATCH 028/226] updated docstring for `helpers.subsol` and AUTHORS.rst --- AUTHORS.rst | 1 + src/apexpy/helpers.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 893405e4..315a268f 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -7,6 +7,7 @@ This python wrapper is made by: * Karl M. Laundal * Christer van der Meeren * Angeline G. Burrell (maintainer) +* Gregory Starr Fortran code by Emmert et al. [2010] [1]_. Quasi-dipole and modified apex coordinates are defined by Richmond [1995] [2]_. The code uses diff --git a/src/apexpy/helpers.py b/src/apexpy/helpers.py index efa904a3..3f28eed8 100644 --- a/src/apexpy/helpers.py +++ b/src/apexpy/helpers.py @@ -146,7 +146,7 @@ def subsol(datetime): Parameters ========== - datetime : :class:`datetime.datetime` + datetime : :class:`datetime.datetime` or :class:`numpy.ndarray[datetime64]` Returns ======= From c6566aa548f1f395aa49e186681b46690324d996 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Wed, 3 Feb 2021 13:44:35 -0400 Subject: [PATCH 029/226] removing ctypes from apex.py, adding fortranapex.pyf, updating setup.py --- setup.py | 2 +- src/apexpy/apex.py | 23 +-- src/fortranapex/fortranapex.pyf | 336 ++++++++++++++++++++++++++++++++ 3 files changed, 340 insertions(+), 21 deletions(-) create mode 100644 src/fortranapex/fortranapex.pyf diff --git a/setup.py b/setup.py index 09468a70..a46ce4d6 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ sources=['src/fortranapex/magfld.f', 'src/fortranapex/apex.f', 'src/fortranapex/makeapexsh.f90', 'src/fortranapex/apexsh.f90', - 'src/fortranapex/checkapexsh.f90'])] + 'src/fortranapex/checkapexsh.f90', 'src/fortranapex/fortranapex.pyf'])] def read(*names, **kwargs): return io.open( diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index a02d044a..59369f79 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -96,7 +96,6 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None): self.datafile = datafile self.fortranlib = fortranlib - self._cfa = ctypes.CDLL(fortranlib) self.set_epoch(self.year) @@ -938,7 +937,7 @@ def set_epoch(self, year): fa.loadapxsh(self.datafile, np.float(year)) # ctypes date = ctypes.c_float(year) - self._cfa.cofrm_(ctypes.byref(date)) + fa.cofrm(year) self.year = year @@ -960,25 +959,9 @@ def set_refh(self, refh): self.refh = refh def _get_babs_nonvectorized(self, glat, glon, height): - - # setup input args for feldg - IENTY = ctypes.c_int(1) - GLAT = ctypes.c_float(glat) - GLON = ctypes.c_float(glon) - ALT = ctypes.c_float(height) - # setup output args for feldg - BNRTH = ctypes.c_float(0) - BEAST = ctypes.c_float(0) - BDOWN = ctypes.c_float(0) - BABS = ctypes.c_float(0) - - self._cfa.feldg_(ctypes.byref(IENTY),ctypes.byref(GLAT), - ctypes.byref(GLON),ctypes.byref(ALT), - ctypes.byref(BNRTH),ctypes.byref(BEAST), - ctypes.byref(BDOWN),ctypes.byref(BABS) - ) + bnorth, beast, bdown, babs = fa.feldg(1, glat, glon, height) # BABS is in guass, so convert to tesla - return BABS.value / 10000.0 + return babs / 10000.0 def get_babs(self, glat, glon, height): """Returns the magnitude of the IGRF magnetic field in tesla. diff --git a/src/fortranapex/fortranapex.pyf b/src/fortranapex/fortranapex.pyf new file mode 100644 index 00000000..fa4a6bd0 --- /dev/null +++ b/src/fortranapex/fortranapex.pyf @@ -0,0 +1,336 @@ +! -*- f90 -*- +! Note: the context of this file is case sensitive. + +python module fortranapex ! in + interface ! in :fortranapex + subroutine cofrm(date) ! in :fortranapex:magfld.f + real :: date + integer :: nmax + real dimension(255) :: gb + real dimension(225) :: gv + integer, optional :: ichg=-99999 + common /magcof/ nmax,gb,gv,ichg + end subroutine cofrm + subroutine dypol(colat,elon,vp) ! in :fortranapex:magfld.f + real :: colat + real :: elon + real :: vp + integer :: nmax + real dimension(255) :: gb + real dimension(225) :: gv + integer :: ichg + common /magcof/ nmax,gb,gv,ichg + end subroutine dypol + subroutine feldg(ienty,glat,glon,alt,bnrth,beast,bdown,babs) ! in :fortranapex:magfld.f + integer intent(in) :: ienty + real(4) intent(in) :: glat + real(4) intent(in) :: glon + real(4) intent(in) :: alt + real(4) intent(out) :: bnrth + real(4) intent(out) :: beast + real(4) intent(out) :: bdown + real(4) intent(out) :: babs + integer :: nmax + real dimension(255) :: gb + real dimension(225) :: gv + integer :: ichg + common /magcof/ nmax,gb,gv,ichg + end subroutine feldg + subroutine gd2cart(gdlat,glon,alt,x,y,z) ! in :fortranapex:magfld.f + real :: gdlat + real :: glon + real :: alt + real :: x + real :: y + real :: z + end subroutine gd2cart + subroutine convrt(i,gdlat,alt,x1,x2) ! in :fortranapex:magfld.f + integer :: i + real :: gdlat + real :: alt + real :: x1 + real :: x2 + end subroutine convrt + subroutine apex(date,dlat,dlon,alt,a,alat,alon,bmag,xmag,ymag,zmag,v) ! in :fortranapex:apex.f + real :: date + real :: dlat + real :: dlon + real :: alt + real :: a + real :: alat + real :: alon + real :: bmag + real :: xmag + real :: ymag + real :: zmag + real :: v + real :: colat + real :: elon + real :: vp + real :: ctp + real :: stp + common /dipole/ colat,elon,vp,ctp,stp + end subroutine apex + subroutine linapx(gdlat,glon,alt,a,alat,alon,xmag,ymag,zmag,f) ! in :fortranapex:apex.f + real :: gdlat + real :: glon + real :: alt + real :: a + real :: alat + real :: alon + real :: xmag + real :: ymag + real :: zmag + real :: f + real :: bx + real :: by + real :: bz + real :: bb + real dimension(3,3) :: yapx + real :: colat + real :: elon + real :: vp + real :: ctp + real :: stp + integer :: nstp + real dimension(3) :: y + real dimension(3) :: yp + real :: sgn + real :: ds + common /fldcomd/ bx,by,bz,bb + common /apxin/ yapx + common /dipole/ colat,elon,vp,ctp,stp + common /itra/ nstp,y,yp,sgn,ds + end subroutine linapx + subroutine itrace(iapx) ! in :fortranapex:apex.f + integer :: iapx + real dimension(3,3) :: yapx + real :: bx + real :: by + real :: bz + real :: bb + integer :: nstp + real dimension(3) :: y + real dimension(3) :: yold + real :: sgn + real :: ds + common /apxin/ yapx + common /fldcomd/ bx,by,bz,bb + common /itra/ nstp,y,yold,sgn,ds + end subroutine itrace + subroutine fndapx(alt,zmag,a,alat,alon) ! in :fortranapex:apex.f + real :: alt + real :: zmag + real :: a + real :: alat + real :: alon + real dimension(3,3) :: yapx + real :: colat + real :: elon + real :: vp + real :: ctp + real :: stp + common /apxin/ yapx + common /dipole/ colat,elon,vp,ctp,stp + end subroutine fndapx + subroutine dipapx(gdlat,gdlon,alt,bnorth,beast,bdown,a,alon) ! in :fortranapex:apex.f + real :: gdlat + real :: gdlon + real :: alt + real :: bnorth + real :: beast + real :: bdown + real :: a + real :: alon + real :: colat + real :: elon + real :: vp + real :: ctp + real :: stp + common /dipole/ colat,elon,vp,ctp,stp + end subroutine dipapx + function fint(x1,x2,x3,y1,y2,y3,xfit) ! in :fortranapex:apex.f + real :: x1 + real :: x2 + real :: x3 + real :: y1 + real :: y2 + real :: y3 + real :: xfit + real :: fint + end function fint + module apxshmodule ! in :fortranapex:apexsh.f90 + integer(kind=4) :: nterm + integer(kind=4) :: nmax + integer(kind=4) :: mmax + integer(kind=4) :: lmax + integer(kind=4) :: nepoch + integer(kind=4) :: ntermsh + integer(kind=4) :: vecflag + real(kind=8), allocatable,dimension(:,:,:) :: coeff0 + real(kind=8), allocatable,dimension(:,:) :: qcoeff0 + real(kind=8), allocatable,dimension(:,:) :: gcoeff0 + real(kind=8), allocatable,dimension(:) :: xqcoeff + real(kind=8), allocatable,dimension(:) :: yqcoeff + real(kind=8), allocatable,dimension(:) :: zqcoeff + real(kind=8), allocatable,dimension(:) :: dxqdrhocoeff + real(kind=8), allocatable,dimension(:) :: dyqdrhocoeff + real(kind=8), allocatable,dimension(:) :: dzqdrhocoeff + real(kind=8), allocatable,dimension(:) :: xgcoeff + real(kind=8), allocatable,dimension(:) :: ygcoeff + real(kind=8), allocatable,dimension(:) :: zgcoeff + real(kind=8), allocatable,dimension(:) :: sh + real(kind=8), allocatable,dimension(:) :: shgradtheta + real(kind=8), allocatable,dimension(:) :: shgradphi + real(kind=8), allocatable,dimension(:) :: polynomq + real(kind=8), allocatable,dimension(:) :: dpolynomq + real(kind=8), allocatable,dimension(:) :: polynomg + real(kind=8), allocatable,dimension(:,:) :: pbar + real(kind=8), allocatable,dimension(:,:) :: vbar + real(kind=8), allocatable,dimension(:,:) :: wbar + real(kind=4), allocatable,dimension(:) :: epochgrid + real(kind=8) :: h + real(kind=8) :: reph + real(kind=8) :: rho + real(kind=8) :: xq + real(kind=8) :: yq + real(kind=8) :: zq + real(kind=8) :: qlat + real(kind=8) :: qlon + real(kind=8) :: sinqlat + real(kind=8) :: cosqlat + real(kind=8) :: cosqlon + real(kind=8) :: sinqlon + real(kind=8) dimension(3) :: xqgrad + real(kind=8) dimension(3) :: yqgrad + real(kind=8) dimension(3) :: zqgrad + real(kind=8) dimension(3) :: qlatgrad + real(kind=8) dimension(3) :: qlongrad + real(kind=8), parameter,optional :: pi=3.14159265358979323846d0 + real(kind=8), parameter,optional,depend(pi) :: dtor=pi/180d0 + real(kind=8), parameter,optional,depend(pi) :: pid2=pi/2d0 + real(kind=8), parameter,optional,depend(pi) :: twopi=2d0*pi + real(kind=8), parameter,optional :: req=6378.1370d0 + real(kind=8), parameter,optional :: eps=1.d0/298.257223563d0 + real(kind=8), parameter,optional,depend(req,eps) :: re=req*(1-eps/3d0) + real(kind=8), parameter,optional,depend(eps) :: ecc2=0.0066943799901413165 + real(kind=4), parameter,optional :: missing=-9999.0 + character(len=1000) :: datafile + real(kind=4) :: epoch + real(kind=4) :: altlastq + real(kind=4) :: altlastg + logical, optional :: loadflag=.true. + end module apxshmodule + subroutine loadapxsh(datafilenew,epochnew) ! in :fortranapex:apexsh.f90 + use apxshmodule + character*1000 :: datafilenew + real(kind=4) :: epochnew + end subroutine loadapxsh + subroutine allocatearrays ! in :fortranapex:apexsh.f90 + use apxshmodule + end subroutine allocatearrays + subroutine apxg2q(glat,glon,alt,vecflagin,qlatout,qlonout,f1,f2,f) ! in :fortranapex:apexsh.f90 + use apxshmodule + real(kind=4) intent(in) :: glat + real(kind=4) intent(in) :: glon + real(kind=4) intent(in) :: alt + integer(kind=4) intent(in) :: vecflagin + real(kind=4) intent(out) :: qlatout + real(kind=4) intent(out) :: qlonout + real(kind=4) dimension(2),intent(out) :: f1 + real(kind=4) dimension(2),intent(out) :: f2 + real(kind=4) intent(out) :: f + end subroutine apxg2q + subroutine apxg2all(glat,glon,alt,hr,vecflagin,qlatout,qlonout,mlat,mlon,f1,f2,f,d1,d2,d3,d,e1,e2,e3) ! in :fortranapex:apexsh.f90 + use apxshmodule + real(kind=4) intent(in) :: glat + real(kind=4) intent(in) :: glon + real(kind=4) intent(in) :: alt + real(kind=4) intent(in) :: hr + integer(kind=4) intent(in) :: vecflagin + real(kind=4) intent(out) :: qlatout + real(kind=4) intent(out) :: qlonout + real(kind=4) intent(out) :: mlat + real(kind=4) intent(out) :: mlon + real(kind=4) dimension(2),intent(out) :: f1 + real(kind=4) dimension(2),intent(out) :: f2 + real(kind=4) intent(out) :: f + real(kind=4) dimension(3),intent(out) :: d1 + real(kind=4) dimension(3),intent(out) :: d2 + real(kind=4) dimension(3),intent(out) :: d3 + real(kind=4) intent(out) :: d + real(kind=4) dimension(3),intent(out) :: e1 + real(kind=4) dimension(3),intent(out) :: e2 + real(kind=4) dimension(3),intent(out) :: e3 + end subroutine apxg2all + subroutine apxq2g(qlat0,qlon0,alt,prec,glatout,glonout,error) ! in :fortranapex:apexsh.f90 + use apxshmodule + real(kind=4) intent(in) :: qlat0 + real(kind=4) intent(in) :: qlon0 + real(kind=4) intent(in) :: alt + real(kind=4) intent(in) :: prec + real(kind=4) intent(out) :: glatout + real(kind=4) intent(out) :: glonout + real(kind=4) intent(out) :: error + end subroutine apxq2g + subroutine shcalc(theta,phi) ! in :fortranapex:apexsh.f90 + use apxshmodule + real(kind=8) intent(in) :: theta + real(kind=8) intent(in) :: phi + end subroutine shcalc + module alfbasismodule ! in :fortranapex:apexsh.f90 + integer(kind=4) :: nmax0 + integer(kind=4) :: mmax0 + real(kind=8), allocatable,dimension(:,:) :: anm + real(kind=8), allocatable,dimension(:) :: cm + real(kind=8), allocatable,dimension(:,:) :: bnm + real(kind=8), allocatable,dimension(:,:) :: dnm + real(kind=8), allocatable,dimension(:) :: en + real(kind=8), allocatable,dimension(:) :: marr + real(kind=8), allocatable,dimension(:) :: narr + end module alfbasismodule + subroutine alfbasisinit(nmax0in,mmax0in) ! in :fortranapex:apexsh.f90 + use alfbasismodule + integer(kind=4) intent(in) :: nmax0in + integer(kind=4) intent(in) :: mmax0in + end subroutine alfbasisinit + subroutine alfbasis(nmax,mmax,theta,p,v,w) ! in :fortranapex:apexsh.f90 + use alfbasismodule + integer(kind=4) intent(in) :: nmax + integer(kind=4) intent(in) :: mmax + real(kind=8) intent(in) :: theta + real(kind=8) dimension(nmax + 1,mmax + 1),intent(out),depend(nmax,mmax) :: p + real(kind=8) dimension(nmax + 1,mmax + 1),intent(out),depend(nmax,mmax) :: v + real(kind=8) dimension(nmax + 1,mmax + 1),intent(out),depend(nmax,mmax) :: w + end subroutine alfbasis + subroutine makeapxsh(datafilein,epochgridin,nepochin,lmaxin,mmaxin,nmaxin) ! in :fortranapex:makeapexsh.f90 + use apxshmodule + character*128 intent(in) :: datafilein + real(kind=4) dimension(31),intent(in) :: epochgridin + integer(kind=4) intent(in) :: nepochin + integer(kind=4) intent(in) :: lmaxin + integer(kind=4) intent(in) :: mmaxin + integer(kind=4) intent(in) :: nmaxin + integer(kind=4) :: nmaxigrf + real(kind=4) dimension(255) :: gb + common /magcof/ nmaxigrf,gb + end subroutine makeapxsh + subroutine choldc(a,n,np,p) ! in :fortranapex:makeapexsh.f90 + real(kind=8) dimension(np,np),intent(inout) :: a + integer(kind=4) intent(in) :: n + integer(kind=4), optional,intent(in),check(shape(a,0)==np),depend(a) :: np=shape(a,0) + real(kind=8) dimension(n),intent(out),depend(n) :: p + end subroutine choldc + subroutine cholsl(a,n,np,p,b,x) ! in :fortranapex:makeapexsh.f90 + real(kind=8) dimension(np,np),intent(in) :: a + integer(kind=4), optional,intent(in),check(len(p)>=n),depend(p) :: n=len(p) + integer(kind=4), optional,intent(in),check(shape(a,0)==np),depend(a) :: np=shape(a,0) + real(kind=8) dimension(n),intent(in) :: p + real(kind=8) dimension(n),intent(in),depend(n) :: b + real(kind=8) dimension(n),intent(out),depend(n) :: x + end subroutine cholsl + end interface +end python module fortranapex + +! This file was auto-generated with f2py (version:2). +! See http://cens.ioc.ee/projects/f2py2e/ From 248f05b4ce7535d9e5401839a8b7421d38a947f4 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Wed, 3 Feb 2021 16:13:37 -0400 Subject: [PATCH 030/226] modifying appveyor.yml, removing 3.4 and fix-compiler-error.py --- appveyor.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index dd562deb..447c3e90 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,18 +17,6 @@ environment: MINICONDA_HOME: C:\Miniconda TESTSCRIPT: 'py.test' - - TESTENV: '3.4-nocover-64' - INSTALL_LIBPYTHON: libpython - PYTHON_VERSION: '3.4' - MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' - - - TESTENV: '3.4-nocover-32' - INSTALL_LIBPYTHON: libpython - PYTHON_VERSION: '3.4' - MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' - - TESTENV: '3.5-nocover-64' PYTHON_VERSION: '3.5' MINICONDA_HOME: C:\Miniconda-x64 @@ -51,6 +39,18 @@ environment: MINICONDA_HOME: C:\Miniconda TESTSCRIPT: 'py.test' + - TESTENV: '3.7-nocover-64' + INSTALL_LIBPYTHON: libpython + PYTHON_VERSION: '3.7' + MINICONDA_HOME: C:\Miniconda-x64 + TESTSCRIPT: 'py.test' + + - TESTENV: '3.7-nocover-32' + INSTALL_LIBPYTHON: libpython + PYTHON_VERSION: '3.7' + MINICONDA_HOME: C:\Miniconda + TESTSCRIPT: 'py.test' + - TESTENV: 'check' INSTALL_LIBPYTHON: libpython PYTHON_VERSION: '2.7' @@ -68,7 +68,7 @@ install: # remove file which conflicts with installation of package 'readme' on case-insensitive file systems - del C:\pythontest\Lib\site-packages\README # to stop the unnecessary "Only MS compiler supported with gfortran on win64" error on some configurations (numpy 1.9?) - - python ci\fix-compiler-error.py +# - python ci\fix-compiler-error.py - '%MINICONDA_HOME%\Scripts\activate C:\pythontest' - where python - where pip From d5725953b9753ed1ab0e391a076a2b627aad872e Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Wed, 3 Feb 2021 16:46:22 -0400 Subject: [PATCH 031/226] adding MinGW to path --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 447c3e90..53da8894 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -61,7 +61,7 @@ environment: init: - ps: echo $env:TESTENV install: - - set PATH=%PATH%;C:\msys64\mingw64\bin + - set PATH=%PATH%;C:\msys64\mingw64\bin;C:\MinGW\bin - '%MINICONDA_HOME%\Scripts\conda config --set always_yes yes' - '%MINICONDA_HOME%\Scripts\conda update -q conda' - '%MINICONDA_HOME%\Scripts\conda create -q -p C:\pythontest python=%PYTHON_VERSION% %INSTALL_LIBPYTHON% pytest numpy' From 4314f9710644dbcfd587290d03ba631175004c41 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 16:43:37 -0500 Subject: [PATCH 032/226] Update appveyor.yml --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 53da8894..d3bd5db6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -61,7 +61,7 @@ environment: init: - ps: echo $env:TESTENV install: - - set PATH=%PATH%;C:\msys64\mingw64\bin;C:\MinGW\bin + - set PATH=%PATH% - '%MINICONDA_HOME%\Scripts\conda config --set always_yes yes' - '%MINICONDA_HOME%\Scripts\conda update -q conda' - '%MINICONDA_HOME%\Scripts\conda create -q -p C:\pythontest python=%PYTHON_VERSION% %INSTALL_LIBPYTHON% pytest numpy' @@ -73,7 +73,7 @@ install: - where python - where pip - '%INSTALL_EXTRA_DEPS%' - - python setup.py clean --all build_ext --force --inplace --compiler=mingw32 + - python setup.py clean --all build_ext --inplace - python setup.py develop test_script: - '%TESTSCRIPT%' From 6a470996c2927275a9c51fbdb89dd20605f2db04 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 16:52:07 -0500 Subject: [PATCH 033/226] Update appveyor.yml --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index d3bd5db6..90581c61 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -73,7 +73,7 @@ install: - where python - where pip - '%INSTALL_EXTRA_DEPS%' - - python setup.py clean --all build_ext --inplace + - python setup.py clean --all build_ext --inplace --force --compiler=msvc - python setup.py develop test_script: - '%TESTSCRIPT%' From 963dfe814867e12d1a6a0e6ec0734315955ddc73 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 17:08:32 -0500 Subject: [PATCH 034/226] Update appveyor.yml --- appveyor.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 90581c61..6fbb0559 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,21 +1,23 @@ +image: +- Visual Studio 2017 version: '{branch}-{build}' build: off configuration: Release environment: - global: - WITH_COMPILER: 'cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd' matrix: - TESTENV: '2.7-nocover-64' INSTALL_LIBPYTHON: libpython PYTHON_VERSION: '2.7' MINICONDA_HOME: C:\Miniconda-x64 TESTSCRIPT: 'py.test' + MINGW_DIR: C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin - TESTENV: '2.7-nocover-32' INSTALL_LIBPYTHON: libpython PYTHON_VERSION: '2.7' MINICONDA_HOME: C:\Miniconda TESTSCRIPT: 'py.test' + MINGW_DIR: C:\MinGW\bin - TESTENV: '3.5-nocover-64' PYTHON_VERSION: '3.5' @@ -61,7 +63,7 @@ environment: init: - ps: echo $env:TESTENV install: - - set PATH=%PATH% + - set PATH=%MINGW_DIR%;%PATH% - '%MINICONDA_HOME%\Scripts\conda config --set always_yes yes' - '%MINICONDA_HOME%\Scripts\conda update -q conda' - '%MINICONDA_HOME%\Scripts\conda create -q -p C:\pythontest python=%PYTHON_VERSION% %INSTALL_LIBPYTHON% pytest numpy' From f05f5deaef1b436296815e29891758bb10fd6fcf Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 17:10:10 -0500 Subject: [PATCH 035/226] Update appveyor.yml --- appveyor.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 6fbb0559..ec7ebd39 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -63,6 +63,10 @@ environment: init: - ps: echo $env:TESTENV install: + - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` + https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` + Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` + throw "There are newer queued builds for this pull request, failing early." } - set PATH=%MINGW_DIR%;%PATH% - '%MINICONDA_HOME%\Scripts\conda config --set always_yes yes' - '%MINICONDA_HOME%\Scripts\conda update -q conda' From b00d4d31803195400cc364884a5eeb6a1a849e79 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 17:18:55 -0500 Subject: [PATCH 036/226] Update appveyor.yml --- appveyor.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ec7ebd39..fa9a8bed 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,3 @@ -image: -- Visual Studio 2017 version: '{branch}-{build}' build: off configuration: Release @@ -79,7 +77,7 @@ install: - where python - where pip - '%INSTALL_EXTRA_DEPS%' - - python setup.py clean --all build_ext --inplace --force --compiler=msvc + - python setup.py clean --all build_ext --inplace --force - python setup.py develop test_script: - '%TESTSCRIPT%' From e5e2141902941846307a4c56a090dc46056124eb Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 17:21:43 -0500 Subject: [PATCH 037/226] Update appveyor.yml --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index fa9a8bed..b8a5bcc7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,3 +1,5 @@ +image: +- Visual Studio 2019 version: '{branch}-{build}' build: off configuration: Release From cd2c8412e75cc8ee288450f50a8d6e2acb398c06 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 17:23:19 -0500 Subject: [PATCH 038/226] Update appveyor.yml --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index b8a5bcc7..c4d75ae0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ image: -- Visual Studio 2019 +- Visual Studio 2015 version: '{branch}-{build}' build: off configuration: Release From 6c0864878e26e686a84b76e7b822cb3e0275bc4f Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 17:43:29 -0500 Subject: [PATCH 039/226] Update appveyor.yml --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index c4d75ae0..e868b033 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -68,6 +68,7 @@ install: Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` throw "There are newer queued builds for this pull request, failing early." } - set PATH=%MINGW_DIR%;%PATH% + - dir C:\MinGW\bin - '%MINICONDA_HOME%\Scripts\conda config --set always_yes yes' - '%MINICONDA_HOME%\Scripts\conda update -q conda' - '%MINICONDA_HOME%\Scripts\conda create -q -p C:\pythontest python=%PYTHON_VERSION% %INSTALL_LIBPYTHON% pytest numpy' From 3da8dca94bf439443e58328256bc51eec8f3f03f Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 17:45:54 -0500 Subject: [PATCH 040/226] Update appveyor.yml --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index e868b033..eed3ea20 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ environment: PYTHON_VERSION: '2.7' MINICONDA_HOME: C:\Miniconda TESTSCRIPT: 'py.test' - MINGW_DIR: C:\MinGW\bin + MINGW_DIR: C:\cygwin - TESTENV: '3.5-nocover-64' PYTHON_VERSION: '3.5' From 77598e9828622b8b0575c5ca0edc233436d849ab Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 17:47:46 -0500 Subject: [PATCH 041/226] Update appveyor.yml --- appveyor.yml | 43 +------------------------------------------ 1 file changed, 1 insertion(+), 42 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index eed3ea20..1a1f1527 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,47 +19,6 @@ environment: TESTSCRIPT: 'py.test' MINGW_DIR: C:\cygwin - - TESTENV: '3.5-nocover-64' - PYTHON_VERSION: '3.5' - MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' - - - TESTENV: '3.5-nocover-32' - PYTHON_VERSION: '3.5' - MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' - - - TESTENV: '3.6-nocover-64' - INSTALL_LIBPYTHON: libpython - PYTHON_VERSION: '3.6' - MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' - - - TESTENV: '3.6-nocover-32' - INSTALL_LIBPYTHON: libpython - PYTHON_VERSION: '3.6' - MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' - - - TESTENV: '3.7-nocover-64' - INSTALL_LIBPYTHON: libpython - PYTHON_VERSION: '3.7' - MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' - - - TESTENV: '3.7-nocover-32' - INSTALL_LIBPYTHON: libpython - PYTHON_VERSION: '3.7' - MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' - - - TESTENV: 'check' - INSTALL_LIBPYTHON: libpython - PYTHON_VERSION: '2.7' - MINICONDA_HOME: 'C:\Miniconda' - TESTSCRIPT: 'python setup.py check --strict --metadata --restructuredtext && check-manifest && flake8 src tests' - INSTALL_EXTRA_DEPS: 'pip install docutils check-manifest flake8 readme pygments' - init: - ps: echo $env:TESTENV install: @@ -68,7 +27,7 @@ install: Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` throw "There are newer queued builds for this pull request, failing early." } - set PATH=%MINGW_DIR%;%PATH% - - dir C:\MinGW\bin + - dir C:\msys64 - '%MINICONDA_HOME%\Scripts\conda config --set always_yes yes' - '%MINICONDA_HOME%\Scripts\conda update -q conda' - '%MINICONDA_HOME%\Scripts\conda create -q -p C:\pythontest python=%PYTHON_VERSION% %INSTALL_LIBPYTHON% pytest numpy' From c2412c8f50f7ea9142e92e1a55ad2514e2cee50c Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 17:59:34 -0500 Subject: [PATCH 042/226] Update appveyor.yml --- appveyor.yml | 52 ++++++++++++++++++++++------------------------------ 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 1a1f1527..f7111e13 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,23 +1,19 @@ -image: -- Visual Studio 2015 version: '{branch}-{build}' build: off configuration: Release environment: matrix: - - TESTENV: '2.7-nocover-64' - INSTALL_LIBPYTHON: libpython - PYTHON_VERSION: '2.7' - MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' - MINGW_DIR: C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin - - - TESTENV: '2.7-nocover-32' - INSTALL_LIBPYTHON: libpython - PYTHON_VERSION: '2.7' - MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' - MINGW_DIR: C:\cygwin + - PYTHON_VERSION: "2.7" + CONDAPATH: 'C:\Miniconda' + - PYTHON_VERSION: "3.5" + CONDAPATH: 'C:\Miniconda35' + - PYTHON_VERSION: "3.6" + CONDAPATH: 'C:\Miniconda36' + - PYTHON_VERSION: "3.7" + CONDAPATH: 'C:\Miniconda37' +platform: + - x86 + - x64 init: - ps: echo $env:TESTENV @@ -26,21 +22,17 @@ install: https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` throw "There are newer queued builds for this pull request, failing early." } - - set PATH=%MINGW_DIR%;%PATH% - - dir C:\msys64 - - '%MINICONDA_HOME%\Scripts\conda config --set always_yes yes' - - '%MINICONDA_HOME%\Scripts\conda update -q conda' - - '%MINICONDA_HOME%\Scripts\conda create -q -p C:\pythontest python=%PYTHON_VERSION% %INSTALL_LIBPYTHON% pytest numpy' - # remove file which conflicts with installation of package 'readme' on case-insensitive file systems - - del C:\pythontest\Lib\site-packages\README - # to stop the unnecessary "Only MS compiler supported with gfortran on win64" error on some configurations (numpy 1.9?) -# - python ci\fix-compiler-error.py - - '%MINICONDA_HOME%\Scripts\activate C:\pythontest' - - where python - - where pip - - '%INSTALL_EXTRA_DEPS%' - - python setup.py clean --all build_ext --inplace --force - - python setup.py develop + - if "%PLATFORM%" == "x86" set PATH=%CONDPATH%\bin;%CONDAPATH%\Scripts;%PATH% + - if "%PLATFORM%" == "x64" set PATH=%CONDPATH%-x64\bin;%CONDAPATH%-x64\Scripts;%PATH% + - conda config --set always_yes yes + - conda update -q conda + - conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy + - conda activate C:\pythontest + - conda install -q -c msys2 m2w64-toolchain + - python.exe setup.py config_fc + - python.exe setup.py config --compiler=mingw32 --fcompiler=gfortran + - python.exe setup.py build + - python.exe setup.py develop test_script: - '%TESTSCRIPT%' From dcfc4ed1b4a01188d6792b5a411f452664e087dd Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 18:02:11 -0500 Subject: [PATCH 043/226] Update appveyor.yml --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index f7111e13..e52af675 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,7 +27,7 @@ install: - conda config --set always_yes yes - conda update -q conda - conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy - - conda activate C:\pythontest + - activate C:\pythontest - conda install -q -c msys2 m2w64-toolchain - python.exe setup.py config_fc - python.exe setup.py config --compiler=mingw32 --fcompiler=gfortran From 432093c2b0cc446271cde8f0599ba33a5607e6e2 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 18:08:37 -0500 Subject: [PATCH 044/226] Update appveyor.yml --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index e52af675..ccdff25f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,7 +34,7 @@ install: - python.exe setup.py build - python.exe setup.py develop test_script: - - '%TESTSCRIPT%' + - py.test after_test: - python setup.py sdist bdist_wheel From 4074af07fb46f6700a096e03a1d63426731b8a9c Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 18:13:21 -0500 Subject: [PATCH 045/226] Update appveyor.yml --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index ccdff25f..6ffef324 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -31,8 +31,8 @@ install: - conda install -q -c msys2 m2w64-toolchain - python.exe setup.py config_fc - python.exe setup.py config --compiler=mingw32 --fcompiler=gfortran - - python.exe setup.py build - python.exe setup.py develop + test_script: - py.test From 8c9e1db1aa51566c681045897c5344ac8b8aa255 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 18:18:40 -0500 Subject: [PATCH 046/226] Update appveyor.yml --- appveyor.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 6ffef324..86f0a637 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,9 +29,8 @@ install: - conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy - activate C:\pythontest - conda install -q -c msys2 m2w64-toolchain - - python.exe setup.py config_fc - - python.exe setup.py config --compiler=mingw32 --fcompiler=gfortran - - python.exe setup.py develop + - python setup.py clean --all build_ext --force --inplace --compiler=mingw32 + - python setup.py develop test_script: - py.test From d32b5c1efe0bc3188a1baef017d69d464a85cc34 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 18:26:30 -0500 Subject: [PATCH 047/226] Update appveyor.yml --- appveyor.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 86f0a637..2d799327 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,7 +29,9 @@ install: - conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy - activate C:\pythontest - conda install -q -c msys2 m2w64-toolchain - - python setup.py clean --all build_ext --force --inplace --compiler=mingw32 + - python.exe setup.py config_fc + - python.exe setup.py config --compiler=mingw32 --fcompiler=gfortran + - python setup.py clean --all build_ext --force --inplace - python setup.py develop test_script: From af9ca6556a024386b4532d23b32cc52331ef45f7 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 18:39:05 -0500 Subject: [PATCH 048/226] Update appveyor.yml --- appveyor.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 2d799327..13716c5a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,9 +29,7 @@ install: - conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy - activate C:\pythontest - conda install -q -c msys2 m2w64-toolchain - - python.exe setup.py config_fc - - python.exe setup.py config --compiler=mingw32 --fcompiler=gfortran - - python setup.py clean --all build_ext --force --inplace + - python setup.py clean --all build_ext --force --inplace --compiler=mingw32 --fcompiler=gfortran - python setup.py develop test_script: From 0e82a27bf6f946c12387ccbb102f0c502ee58ec4 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 18:44:32 -0500 Subject: [PATCH 049/226] Update appveyor.yml --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 13716c5a..3532ac73 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,6 +29,7 @@ install: - conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy - activate C:\pythontest - conda install -q -c msys2 m2w64-toolchain + - python.exe setup.py config_fc - python setup.py clean --all build_ext --force --inplace --compiler=mingw32 --fcompiler=gfortran - python setup.py develop From 1f378ae69454d6ee8a834e873b09a7b8021b4a1f Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 18:56:50 -0500 Subject: [PATCH 050/226] Update appveyor.yml --- appveyor.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 3532ac73..cb8df0df 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,9 +29,7 @@ install: - conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy - activate C:\pythontest - conda install -q -c msys2 m2w64-toolchain - - python.exe setup.py config_fc - - python setup.py clean --all build_ext --force --inplace --compiler=mingw32 --fcompiler=gfortran - - python setup.py develop + - pip install -e . test_script: - py.test From 88332ffaa89096a996ed36de3dc4d98768c37ad4 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 19:03:10 -0500 Subject: [PATCH 051/226] Update appveyor.yml --- appveyor.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index cb8df0df..96d3da24 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -26,10 +26,12 @@ install: - if "%PLATFORM%" == "x64" set PATH=%CONDPATH%-x64\bin;%CONDAPATH%-x64\Scripts;%PATH% - conda config --set always_yes yes - conda update -q conda - - conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy + - conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy libpython - activate C:\pythontest - conda install -q -c msys2 m2w64-toolchain - - pip install -e . + - python.exe setup.py config_fc + - python.exe setup.py config --compiler=mingw32 --fcompiler=gfortran + - python setup.py develop test_script: - py.test From 8ea467f7b65206df4bcdb6091718f9e788620bba Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 20:51:18 -0500 Subject: [PATCH 052/226] Update appveyor.yml --- appveyor.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 96d3da24..b3fd2be1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -49,13 +49,3 @@ deploy_script: artifacts: - path: dist\* -matrix: - allow_failures: - - PYTHON_VERSION: '3.5' - - TESTENV: '2.7-nocover-64' - - TESTENV: '3.5-nocover-64' - - TESTENV: '3.6-nocover-64' - - TESTENV: 'check' -### 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 19c06e5ba6446bcdb289773984316f7f58a8343f Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 21:24:15 -0500 Subject: [PATCH 053/226] Update appveyor.yml --- appveyor.yml | 68 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index b3fd2be1..db2fdf70 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,38 +3,78 @@ build: off configuration: Release environment: matrix: - - PYTHON_VERSION: "2.7" - CONDAPATH: 'C:\Miniconda' - - PYTHON_VERSION: "3.5" - CONDAPATH: 'C:\Miniconda35' - - PYTHON_VERSION: "3.6" - CONDAPATH: 'C:\Miniconda36' - - PYTHON_VERSION: "3.7" - CONDAPATH: 'C:\Miniconda37' -platform: - - x86 - - x64 + - TESTENV: '2.7-nocover-64' + PYTHON_VERSION: '2.7' + MINICONDA_HOME: C:\Miniconda-x64 + TESTSCRIPT: 'py.test' + + - TESTENV: '2.7-nocover-32' + PYTHON_VERSION: '2.7' + MINICONDA_HOME: C:\Miniconda + TESTSCRIPT: 'py.test' + + - TESTENV: '3.5-nocover-64' + PYTHON_VERSION: '3.5' + MINICONDA_HOME: C:\Miniconda-x64 + TESTSCRIPT: 'py.test' + + - TESTENV: '3.5-nocover-32' + PYTHON_VERSION: '3.5' + MINICONDA_HOME: C:\Miniconda + TESTSCRIPT: 'py.test' + + - TESTENV: '3.6-nocover-64' + PYTHON_VERSION: '3.6' + MINICONDA_HOME: C:\Miniconda-x64 + TESTSCRIPT: 'py.test' + + - TESTENV: '3.6-nocover-32' + PYTHON_VERSION: '3.6' + MINICONDA_HOME: C:\Miniconda + TESTSCRIPT: 'py.test' + + - TESTENV: '3.7-nocover-64' + PYTHON_VERSION: '3.7' + MINICONDA_HOME: C:\Miniconda-x64 + TESTSCRIPT: 'py.test' + + - TESTENV: '3.7-nocover-32' + PYTHON_VERSION: '3.7' + MINICONDA_HOME: C:\Miniconda + TESTSCRIPT: 'py.test' + + - TESTENV: 'check' + PYTHON_VERSION: '2.7' + MINICONDA_HOME: 'C:\Miniconda' + TESTSCRIPT: 'python setup.py check --strict --metadata --restructuredtext && check-manifest && flake8 src tests' + INSTALL_EXTRA_DEPS: 'pip install docutils check-manifest flake8 readme pygments' init: - ps: echo $env:TESTENV + install: + # this quickly fails the job if there is another pipeline queued ahead of it - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` throw "There are newer queued builds for this pull request, failing early." } - - if "%PLATFORM%" == "x86" set PATH=%CONDPATH%\bin;%CONDAPATH%\Scripts;%PATH% - - if "%PLATFORM%" == "x64" set PATH=%CONDPATH%-x64\bin;%CONDAPATH%-x64\Scripts;%PATH% + - '%MINICONDA_HOME%\Scripts\conda config --set always_yes yes' + - '%MINICONDA_HOME%\Scripts\conda update -q conda' + - '%MINICONDA_HOME%\Scripts\conda create -q -p C:\pythontest python=%PYTHON_VERSION% %INSTALL_LIBPYTHON% pytest numpy' + # remove file which conflicts with installation of package 'readme' on case-insensitive file systems + - del C:\pythontest\Lib\site-packages\README - conda config --set always_yes yes - conda update -q conda - conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy libpython - activate C:\pythontest - conda install -q -c msys2 m2w64-toolchain + - '%INSTALL_EXTRA_DEPS%' - python.exe setup.py config_fc - python.exe setup.py config --compiler=mingw32 --fcompiler=gfortran - python setup.py develop test_script: - - py.test + - '%TESTSCRIPT%' after_test: - python setup.py sdist bdist_wheel From d916e64a3dcd9a1fb819936abbeddb8825e4ca62 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 21:27:12 -0500 Subject: [PATCH 054/226] Update appveyor.yml --- appveyor.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index db2fdf70..b9520b07 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -60,14 +60,11 @@ install: throw "There are newer queued builds for this pull request, failing early." } - '%MINICONDA_HOME%\Scripts\conda config --set always_yes yes' - '%MINICONDA_HOME%\Scripts\conda update -q conda' - - '%MINICONDA_HOME%\Scripts\conda create -q -p C:\pythontest python=%PYTHON_VERSION% %INSTALL_LIBPYTHON% pytest numpy' + - '%MINICONDA_HOME%\Scripts\conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy libpython' # remove file which conflicts with installation of package 'readme' on case-insensitive file systems - - del C:\pythontest\Lib\site-packages\README - - conda config --set always_yes yes - - conda update -q conda - - conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy libpython - - activate C:\pythontest - - conda install -q -c msys2 m2w64-toolchain + - del C:\pythontest\Lib\site-packages\README + - '%MINICONDA_HOME%\Scripts\activate C:\pythontest' + - '%MINICONDA_HOME%\Scripts\conda install -q -c msys2 m2w64-toolchain' - '%INSTALL_EXTRA_DEPS%' - python.exe setup.py config_fc - python.exe setup.py config --compiler=mingw32 --fcompiler=gfortran From f85fa24392fd8a93a546758615cb8e688007bf29 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Wed, 3 Feb 2021 22:09:43 -0500 Subject: [PATCH 055/226] Update appveyor.yml --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index b9520b07..e3e5fc46 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -68,7 +68,7 @@ install: - '%INSTALL_EXTRA_DEPS%' - python.exe setup.py config_fc - python.exe setup.py config --compiler=mingw32 --fcompiler=gfortran - - python setup.py develop + - python setup.py install test_script: - '%TESTSCRIPT%' From 7eeba125f22b73502be6cb35788dabce5323d090 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Wed, 3 Feb 2021 23:54:35 -0400 Subject: [PATCH 056/226] updating apex.py, helpers.py and __main__.py to be pep8 compliant --- src/apexpy/__main__.py | 1 + src/apexpy/apex.py | 46 ++++++++++++++---------------------------- src/apexpy/helpers.py | 1 + 3 files changed, 17 insertions(+), 31 deletions(-) diff --git a/src/apexpy/__main__.py b/src/apexpy/__main__.py index 8b4eb9b3..6514149d 100644 --- a/src/apexpy/__main__.py +++ b/src/apexpy/__main__.py @@ -67,5 +67,6 @@ def main(): args.height, datetime=datetime) np.savetxt(args.file_out, np.column_stack((lats, lons)), fmt='%.8f') + if __name__ == '__main__': sys.exit(main()) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 59369f79..5f5f7ab0 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -13,10 +13,11 @@ # below try..catch required for autodoc to work on readthedocs try: from . import fortranapex as fa -except: +except ImportError as e: print("ERROR: fortranapex module could not be imported, so apexpy probably" " won't work. Make sure you have a gfortran compiler. Wheels " "installation assumes your compiler lives in /opt/local/bin") + raise e import ctypes @@ -76,7 +77,7 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None): fortranlib = fa.__file__ self.RE = 6371.009 # mean Earth radius - self.set_refh(refh) # reference height + self.set_refh(refh) # reference height if date is None: self.year = helpers.toYearFraction(dt.datetime.now()) @@ -84,7 +85,7 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None): try: # convert date/datetime object to decimal year self.year = helpers.toYearFraction(date) - except: + except AttributeError: # failed so date is probably int/float, use directly self.year = date @@ -124,7 +125,6 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None): self._qd2apex = np.frompyfunc(self._qd2apex_nonvectorized, 3, 2) self._get_babs = np.frompyfunc(self._get_babs_nonvectorized, 3, 1) - def convert(self, lat, lon, source, dest, height=0, datetime=None, precision=1e-10, ssheight=50*6371): """Converts between geodetic, modified apex, quasi-dipole and MLT. @@ -431,7 +431,7 @@ def _qd2apex_nonvectorized(self, qlat, qlon, height): qlat = helpers.checklat(qlat, name='qlat') alon = qlon - hA = self.get_apex(qlat, height) # apex height + hA = self.get_apex(qlat, height) # apex height if hA < self.refh: if np.isclose(hA, self.refh, rtol=0, atol=1e-5): @@ -617,8 +617,8 @@ def _map_EV_to_height(self, alat, alon, height, newheight, X, EV): raise ValueError(EV + ' must be (3, N) or (3,) ndarray') X = np.reshape(X, (3, np.size(X)//3)) - _, _, _, _, _, _, d1, d2, _, e1, e2, _ = self.basevectors_apex(alat, \ - alon, height, coords='apex') + _, _, _, _, _, _, d1, d2, _, e1, e2, _ = self.basevectors_apex(alat, + alon, height, coords='apex') if EV == 'E': v1 = e1 @@ -634,8 +634,8 @@ def _map_EV_to_height(self, alat, alon, height, newheight, X, EV): X1 = np.sum(X*v1, axis=0) # E dot e1 or V dot d1 X2 = np.sum(X*v2, axis=0) # E dot e2 or V dot d2 - _, _, _, _, _, _, d1, d2, _, e1, e2, _ = self.basevectors_apex(alat, \ - alon, newheight, coords='apex') + _, _, _, _, _, _, d1, d2, _, e1, e2, _ = self.basevectors_apex(alat, + alon, newheight, coords='apex') if EV == 'E': v1 = d1 @@ -678,7 +678,6 @@ def map_E_to_height(self, alat, alon, height, newheight, E): components) """ - return self._map_EV_to_height(alat, alon, height, newheight, E, 'E') def map_V_to_height(self, alat, alon, height, newheight, V): @@ -787,11 +786,6 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): Altitude in km coords : {'geo', 'apex', 'qd'}, optional Input coordinate system - return_all : bool, optional - Will also return f3, g1, g2, and g3, and f1 and f2 have 3 components - (the last component is zero). Requires `lat`, `lon`, and `height` - to be broadcast to 1D (at least one of the parameters must be 1D - and the other two parameters must be 1D or 0D). precision : float, optional Precision of output (degrees) when converting to geo. A negative value of this argument produces a low-precision calculation of @@ -872,11 +866,9 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): F = np.cross(F1.T, F2.T).T[-1] cosI = helpers.getcosIm(alat) k = np.array([0, 0, 1], dtype=np.float64).reshape((3, 1)) - g1 = ((self.RE + np.float64(height)) / (self.RE + self.refh))**(3/2) \ - * d1 / F - g2 = -1.0 / (2.0 * F * np.tan(np.radians(qlat))) * \ - (k + ((self.RE + np.float64(height)) / (self.RE + self.refh)) - * d2 / cosI) + g1 = ((self.RE + np.float64(height)) / (self.RE + self.refh))**(3/2) * d1 / F + g2 = -1.0 / (2.0 * F * np.tan(np.radians(qlat))) * (k + ((self.RE + np.float64(height)) / + (self.RE + self.refh)) * d2 / cosI) g3 = k*F f3 = np.cross(g1.T, g2.T).T @@ -932,13 +924,9 @@ def set_epoch(self, year): Decimal year """ - # f2py fa.loadapxsh(self.datafile, np.float(year)) - # ctypes - date = ctypes.c_float(year) fa.cofrm(year) - self.year = year def set_refh(self, refh): @@ -955,7 +943,6 @@ def set_refh(self, refh): and is only relevant for conversions involving apex (not quasi-dipole). """ - self.refh = refh def _get_babs_nonvectorized(self, glat, glon, height): @@ -1043,17 +1030,14 @@ def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): J. Geophys. Res., 115(A8), A08322, :doi:`10.1029/2010JA015326`. """ - - glat, glon = self.convert(lat, lon, coords, 'geo', height=height, precision=precision) babs = self.get_babs(glat, glon, height) - _, _, _, _, _, _, d1, d2, d3, _, _, e3 = self.basevectors_apex(glat, \ - glon, height, coords='geo') - d1_cross_d2 = np.cross(d1.T,d2.T).T - D = np.sqrt(np.sum(d1_cross_d2**2,axis=0)) + _, _, _, _, _, _, d1, d2, d3, _, _, e3 = self.basevectors_apex(glat, glon, height, coords='geo') + d1_cross_d2 = np.cross(d1.T, d2.T).T + D = np.sqrt(np.sum(d1_cross_d2**2, axis=0)) Be3 = babs / D Bd3 = babs * D diff --git a/src/apexpy/helpers.py b/src/apexpy/helpers.py index 3f28eed8..1b909212 100644 --- a/src/apexpy/helpers.py +++ b/src/apexpy/helpers.py @@ -8,6 +8,7 @@ import datetime as dt import numpy as np + def checklat(lat, name='lat'): """Makes sure the latitude is inside [-90, 90], clipping close values (tolerance 1e-4). From 65a708fc6a87ec2e3fe5c3433d53a585dc33c689 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Thu, 4 Feb 2021 00:25:34 -0400 Subject: [PATCH 057/226] updating apex.py to be pep8 compliant --- src/apexpy/apex.py | 67 +++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 5f5f7ab0..bdff6080 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -19,9 +19,6 @@ "installation assumes your compiler lives in /opt/local/bin") raise e -import ctypes - - # make sure invalid warnings are always shown warnings.filterwarnings('always', message='.*set to -9999 where*', module='apexpy.apex') @@ -126,7 +123,7 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None): self._get_babs = np.frompyfunc(self._get_babs_nonvectorized, 3, 1) def convert(self, lat, lon, source, dest, height=0, datetime=None, - precision=1e-10, ssheight=50*6371): + precision=1e-10, ssheight=50 * 6371): """Converts between geodetic, modified apex, quasi-dipole and MLT. Parameters @@ -479,14 +476,14 @@ def qd2apex(self, qlat, qlon, height): # if array is returned, the dtype is object, so convert to float return np.float64(alat), np.float64(alon) - def mlon2mlt(self, mlon, datetime, ssheight=50*6371): + def mlon2mlt(self, mlon, datetime, ssheight=50 * 6371): """Computes the magnetic local time at the specified magnetic longitude and UT. Parameters ========== mlon : array_like - Magnetic longitude (apex and quasi-dipole longitude are always + Magnetic longitude (apex and quasi-dipole longitude are always equal) datetime : :class:`datetime.datetime` Date and time @@ -513,9 +510,9 @@ def mlon2mlt(self, mlon, datetime, ssheight=50*6371): ssalat, ssalon = self.geo2apex(ssglat, ssglon, ssheight) # np.float64 will ensure lists are converted to arrays - return (180 + np.float64(mlon) - ssalon)/15 % 24 + return (180 + np.float64(mlon) - ssalon) / 15 % 24 - def mlt2mlon(self, mlt, datetime, ssheight=50*6371): + def mlt2mlon(self, mlt, datetime, ssheight=50 * 6371): """Computes the magnetic longitude at the specified magnetic local time and UT. @@ -549,7 +546,7 @@ def mlt2mlon(self, mlt, datetime, ssheight=50*6371): ssalat, ssalon = self.geo2apex(ssglat, ssglon, ssheight) # np.float64 will ensure lists are converted to arrays - return (15*np.float64(mlt) - 180 + ssalon + 360) % 360 + return (15 * np.float64(mlt) - 180 + ssalon + 360) % 360 def map_to_height(self, glat, glon, height, newheight, conjugate=False, precision=1e-10): @@ -610,15 +607,14 @@ def map_to_height(self, glat, glon, height, newheight, conjugate=False, def _map_EV_to_height(self, alat, alon, height, newheight, X, EV): # make sure X is array of correct shape - if(not (np.ndim(X) == 1 and np.size(X) == 3) and - not (np.ndim(X) == 2 and np.shape(X)[0] == 3)): + if (not (np.ndim(X) == 1 and np.size(X) == 3) and + not (np.ndim(X) == 2 and np.shape(X)[0] == 3)): # raise ValueError because if passing e.g. a (6,) ndarray the # reshape below will work even though the input is invalid raise ValueError(EV + ' must be (3, N) or (3,) ndarray') - X = np.reshape(X, (3, np.size(X)//3)) + X = np.reshape(X, (3, np.size(X) // 3)) - _, _, _, _, _, _, d1, d2, _, e1, e2, _ = self.basevectors_apex(alat, - alon, height, coords='apex') + _, _, _, _, _, _, d1, d2, _, e1, e2, _ = self.basevectors_apex(alat, alon, height, coords='apex') if EV == 'E': v1 = e1 @@ -628,14 +624,13 @@ def _map_EV_to_height(self, alat, alon, height, newheight, X, EV): v2 = d2 # make sure v1 and v2 have shape (3, N) - v1 = np.reshape(v1, (3, v1.size//3)) - v2 = np.reshape(v2, (3, v2.size//3)) + v1 = np.reshape(v1, (3, v1.size // 3)) + v2 = np.reshape(v2, (3, v2.size // 3)) - X1 = np.sum(X*v1, axis=0) # E dot e1 or V dot d1 - X2 = np.sum(X*v2, axis=0) # E dot e2 or V dot d2 + X1 = np.sum(X * v1, axis=0) # E dot e1 or V dot d1 + X2 = np.sum(X * v2, axis=0) # E dot e2 or V dot d2 - _, _, _, _, _, _, d1, d2, _, e1, e2, _ = self.basevectors_apex(alat, - alon, newheight, coords='apex') + _, _, _, _, _, _, d1, d2, _, e1, e2, _ = self.basevectors_apex(alat, alon, newheight, coords='apex') if EV == 'E': v1 = d1 @@ -645,10 +640,10 @@ def _map_EV_to_height(self, alat, alon, height, newheight, X, EV): v2 = e2 # make sure v1 and v2 have shape (3, N) - v1 = np.reshape(v1, (3, v1.size//3)) - v2 = np.reshape(v2, (3, v2.size//3)) + v1 = np.reshape(v1, (3, v1.size // 3)) + v2 = np.reshape(v2, (3, v2.size // 3)) - X_mapped = X1[np.newaxis, :]*v1 + X2[np.newaxis, :]*v2 + X_mapped = X1[np.newaxis, :] * v1 + X2[np.newaxis, :] * v2 return np.squeeze(X_mapped) @@ -851,14 +846,14 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): e3 = np.vstack(e3).T # make sure arrays are 2D - f1 = f1.reshape((2, f1.size//2)) - f2 = f2.reshape((2, f2.size//2)) - d1 = d1.reshape((3, d1.size//3)) - d2 = d2.reshape((3, d2.size//3)) - d3 = d3.reshape((3, d3.size//3)) - e1 = e1.reshape((3, e1.size//3)) - e2 = e2.reshape((3, e2.size//3)) - e3 = e3.reshape((3, e3.size//3)) + f1 = f1.reshape((2, f1.size // 2)) + f2 = f2.reshape((2, f2.size // 2)) + d1 = d1.reshape((3, d1.size // 3)) + d2 = d2.reshape((3, d2.size // 3)) + d3 = d3.reshape((3, d3.size // 3)) + e1 = e1.reshape((3, e1.size // 3)) + e2 = e2.reshape((3, e2.size // 3)) + e3 = e3.reshape((3, e3.size // 3)) # compute f3, g1, g2, g3 F1 = np.vstack((f1, np.zeros_like(f1[0]))) @@ -866,10 +861,10 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): F = np.cross(F1.T, F2.T).T[-1] cosI = helpers.getcosIm(alat) k = np.array([0, 0, 1], dtype=np.float64).reshape((3, 1)) - g1 = ((self.RE + np.float64(height)) / (self.RE + self.refh))**(3/2) * d1 / F + g1 = ((self.RE + np.float64(height)) / (self.RE + self.refh)) ** (3 / 2) * d1 / F g2 = -1.0 / (2.0 * F * np.tan(np.radians(qlat))) * (k + ((self.RE + np.float64(height)) / (self.RE + self.refh)) * d2 / cosI) - g3 = k*F + g3 = k * F f3 = np.cross(g1.T, g2.T).T if np.any(alat == -9999): @@ -910,7 +905,7 @@ def get_apex(self, lat, height=None): if height is None: height = self.refh - cos_lat_squared = np.cos(np.radians(lat))**2 + cos_lat_squared = np.cos(np.radians(lat)) ** 2 apex_height = (self.RE + height) / cos_lat_squared - self.RE return apex_height @@ -1015,7 +1010,7 @@ def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): instead equal to the IGRF magnitude divided by a scaling factor, D. Similarly, Bd3 is the IGRF magnitude multiplied by D. - See Richmond, A. D. (1995) [4]_ equations 3.13 and 3.14 + See Richmond, A. D. (1995) [4]_ equations 3.13 and 3.14 References ========== @@ -1037,7 +1032,7 @@ def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): _, _, _, _, _, _, d1, d2, d3, _, _, e3 = self.basevectors_apex(glat, glon, height, coords='geo') d1_cross_d2 = np.cross(d1.T, d2.T).T - D = np.sqrt(np.sum(d1_cross_d2**2, axis=0)) + D = np.sqrt(np.sum(d1_cross_d2 ** 2, axis=0)) Be3 = babs / D Bd3 = babs * D From 3ee78e6d794ef1d93d8fdf463c0527e456ca10fa Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Thu, 4 Feb 2021 00:03:59 -0500 Subject: [PATCH 058/226] adding pip update to appveyor.yml --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index e3e5fc46..e908c909 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -65,6 +65,7 @@ install: - del C:\pythontest\Lib\site-packages\README - '%MINICONDA_HOME%\Scripts\activate C:\pythontest' - '%MINICONDA_HOME%\Scripts\conda install -q -c msys2 m2w64-toolchain' + - python -m pip install --upgrade pip - '%INSTALL_EXTRA_DEPS%' - python.exe setup.py config_fc - python.exe setup.py config --compiler=mingw32 --fcompiler=gfortran From aab54330a4dad6a13e897768f954245d964b4798 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 4 Feb 2021 11:08:12 -0500 Subject: [PATCH 059/226] TST: updated AppVeyor template Incorporated changes made by @gregstarr into the AppVeyor template. --- ci/templates/appveyor.yml | 47 +++++++++++++-------------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/ci/templates/appveyor.yml b/ci/templates/appveyor.yml index 0e6f4542..7f8bcb39 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/appveyor.yml @@ -2,33 +2,20 @@ version: '{branch}-{build}' build: off configuration: Release environment: - global: - WITH_COMPILER: 'cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd' matrix: -{% 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 %} +{% for env, config in tox_environments|dictsort %}{% if not config.cover %} - TESTENV: '{{ env }}-64' - {%- if config.python != 'python3.5' %} - - INSTALL_LIBPYTHON: libpython - {%- endif %} - PYTHON_VERSION: '{{ config.python[-3:] }}' MINICONDA_HOME: C:\Miniconda-x64 TESTSCRIPT: 'py.test' - TESTENV: '{{ env }}-32' - {%- if config.python != 'python3.5' %} - - INSTALL_LIBPYTHON: libpython - {%- endif %} - PYTHON_VERSION: '{{ config.python[-3:] }}' MINICONDA_HOME: C:\Miniconda TESTSCRIPT: 'py.test' {% endif %}{% endfor %} - TESTENV: 'check' - INSTALL_LIBPYTHON: libpython PYTHON_VERSION: '2.7' MINICONDA_HOME: 'C:\Miniconda' TESTSCRIPT: 'python setup.py check --strict --metadata --restructuredtext && check-manifest && flake8 src tests' @@ -37,20 +24,24 @@ environment: init: - ps: echo $env:TESTENV install: - - set PATH=%PATH%;C:\msys64\mingw64\bin + # This quickly fails the job if there is another pipeline queued ahead of it + - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` + https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` + Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` + throw "There are newer queued builds for this pull request, failing early." } - '%MINICONDA_HOME%\Scripts\conda config --set always_yes yes' - '%MINICONDA_HOME%\Scripts\conda update -q conda' - - '%MINICONDA_HOME%\Scripts\conda create -q -p C:\pythontest python=%PYTHON_VERSION% %INSTALL_LIBPYTHON% pytest numpy' - # remove file which conflicts with installation of package 'readme' on case-insensitive file systems + - '%MINICONDA_HOME%\Scripts\conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy libpython' + # Remove file which conflicts with installation of package 'readme' on case-insensitive file systems - del C:\pythontest\Lib\site-packages\README - # to stop the unnecessary "Only MS compiler supported with gfortran on win64" error on some configurations (numpy 1.9?) - - python ci\fix-compiler-error.py - '%MINICONDA_HOME%\Scripts\activate C:\pythontest' - - where python - - where pip + - '%MINICONDA_HOME%\Scripts\conda install -q -c msys2 m2w64-toolchain' + - python -m pip install --upgrade pip - '%INSTALL_EXTRA_DEPS%' - - python setup.py clean --all build_ext --force --inplace --compiler=mingw32 - - python setup.py develop + - python setup.py config_fc + - python setup.py config --compiler=mingw32 --fcompiler=gfortran + - python setup.py install + test_script: - '%TESTSCRIPT%' @@ -58,7 +49,7 @@ after_test: - python setup.py sdist bdist_wheel deploy_script: - # if tagged commit, build/upload wheel + # If tagged commit, build/upload wheel - IF "%APPVEYOR_REPO_TAG%"=="true" IF NOT "%TESTENV%"=="check" ( pip install twine && python setup.py register && @@ -67,13 +58,7 @@ deploy_script: artifacts: - path: dist\* -matrix: - allow_failures: - - PYTHON_VERSION: '3.5' - - TESTENV: '2.7-nocover-64' - - TESTENV: '3.5-nocover-64' - - TESTENV: '3.6-nocover-64' - - TESTENV: 'check' + ### 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 b07339dbf48309a4ed2c1f1c5cfa33cc6211bc57 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 4 Feb 2021 11:08:45 -0500 Subject: [PATCH 060/226] TST: updated travis template Updated travis template to get python versions from setup.cfg. --- ci/templates/.travis.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index cbdd12f4..b6da4b68 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -1,10 +1,9 @@ language: python python: - - '2.7' - - '3.4' - - '3.5' - - '3.6' +{% for env, config in tox_environments|dictsort %}{% if not config.cover %} + - "{{ config.python[-3:] }}" +{% endif %}{% endfor %} sudo: false From 61260f7eea09d3233731855d9a506e2ea8e96109 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 4 Feb 2021 11:09:39 -0500 Subject: [PATCH 061/226] MAINT: updated supported python versions Added testing support for python 3.7-3.9 and removed testing for 3.4 and 3.5. --- docs/installation.rst | 7 ++----- setup.cfg | 5 +++-- setup.py | 5 +++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index a95f482b..f64841f0 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -18,12 +18,9 @@ such as gfortran. The package has been tested with the following setups (others might work, too): -* Windows (64 bit Python), Linux (64 bit), and Mac (64 bit) -* Python 2.7, 3.5, 3.6, (and 3.4 on Linux/Mac [2]_) +* Windows (32/64 bit Python), Linux (64 bit), and Mac (64 bit) +* Python 2.7, 3.6, 3.7, 3.8, 3.9 .. [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 `_. -.. [2] I do not know how to compile the Fortran extension on Windows in a - manner that is compatible with the omitted python versions. If you get - it working, let me know! diff --git a/setup.cfg b/setup.cfg index 0fb509ae..8f832237 100644 --- a/setup.cfg +++ b/setup.cfg @@ -59,9 +59,10 @@ multi_line_output=0 python_versions = 2.7 - 3.4 - 3.5 3.6 + 3.7 + 3.8 + 3.9 dependencies = # 1.4: Django==1.4.16 !python_versions[3.*] diff --git a/setup.py b/setup.py index a46ce4d6..1e3e183b 100644 --- a/setup.py +++ b/setup.py @@ -62,9 +62,10 @@ def read(*names, **kwargs): 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: Implementation :: CPython', 'Topic :: Scientific/Engineering :: Physics', 'Topic :: Utilities', From dbebf1dabd350d26e05588106ffdec9f98257ac8 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 4 Feb 2021 11:10:13 -0500 Subject: [PATCH 062/226] TST: updated CI routines Updated versions in tox, travis, and appveyor files. --- .travis.yml | 9 +++++---- appveyor.yml | 49 +++++++++++++++++++++++++++++++------------------ tox.ini | 51 +++++++++++++++++++++++++++++++++++---------------- 3 files changed, 71 insertions(+), 38 deletions(-) diff --git a/.travis.yml b/.travis.yml index cbdd12f4..35812a81 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,11 @@ language: python python: - - '2.7' - - '3.4' - - '3.5' - - '3.6' + - "2.7" + - "3.6" + - "3.7" + - "3.8" + - "3.9" sudo: false diff --git a/appveyor.yml b/appveyor.yml index e908c909..16a62131 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,16 +13,6 @@ environment: MINICONDA_HOME: C:\Miniconda TESTSCRIPT: 'py.test' - - TESTENV: '3.5-nocover-64' - PYTHON_VERSION: '3.5' - MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' - - - TESTENV: '3.5-nocover-32' - PYTHON_VERSION: '3.5' - MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' - - TESTENV: '3.6-nocover-64' PYTHON_VERSION: '3.6' MINICONDA_HOME: C:\Miniconda-x64 @@ -32,7 +22,7 @@ environment: PYTHON_VERSION: '3.6' MINICONDA_HOME: C:\Miniconda TESTSCRIPT: 'py.test' - + - TESTENV: '3.7-nocover-64' PYTHON_VERSION: '3.7' MINICONDA_HOME: C:\Miniconda-x64 @@ -43,6 +33,26 @@ environment: MINICONDA_HOME: C:\Miniconda TESTSCRIPT: 'py.test' + - TESTENV: '3.8-nocover-64' + PYTHON_VERSION: '3.8' + MINICONDA_HOME: C:\Miniconda-x64 + TESTSCRIPT: 'py.test' + + - TESTENV: '3.8-nocover-32' + PYTHON_VERSION: '3.8' + MINICONDA_HOME: C:\Miniconda + TESTSCRIPT: 'py.test' + + - TESTENV: '3.9-nocover-64' + PYTHON_VERSION: '3.9' + MINICONDA_HOME: C:\Miniconda-x64 + TESTSCRIPT: 'py.test' + + - TESTENV: '3.9-nocover-32' + PYTHON_VERSION: '3.9' + MINICONDA_HOME: C:\Miniconda + TESTSCRIPT: 'py.test' + - TESTENV: 'check' PYTHON_VERSION: '2.7' MINICONDA_HOME: 'C:\Miniconda' @@ -51,9 +61,8 @@ environment: init: - ps: echo $env:TESTENV - install: - # this quickly fails the job if there is another pipeline queued ahead of it + # This quickly fails the job if there is another pipeline queued ahead of it - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` @@ -61,14 +70,14 @@ install: - '%MINICONDA_HOME%\Scripts\conda config --set always_yes yes' - '%MINICONDA_HOME%\Scripts\conda update -q conda' - '%MINICONDA_HOME%\Scripts\conda create -q -p C:\pythontest python=%PYTHON_VERSION% pytest numpy libpython' - # remove file which conflicts with installation of package 'readme' on case-insensitive file systems - - del C:\pythontest\Lib\site-packages\README + # Remove file which conflicts with installation of package 'readme' on case-insensitive file systems + - del C:\pythontest\Lib\site-packages\README - '%MINICONDA_HOME%\Scripts\activate C:\pythontest' - '%MINICONDA_HOME%\Scripts\conda install -q -c msys2 m2w64-toolchain' - python -m pip install --upgrade pip - '%INSTALL_EXTRA_DEPS%' - - python.exe setup.py config_fc - - python.exe setup.py config --compiler=mingw32 --fcompiler=gfortran + - python setup.py config_fc + - python setup.py config --compiler=mingw32 --fcompiler=gfortran - python setup.py install test_script: @@ -78,7 +87,7 @@ after_test: - python setup.py sdist bdist_wheel deploy_script: - # if tagged commit, build/upload wheel + # If tagged commit, build/upload wheel - IF "%APPVEYOR_REPO_TAG%"=="true" IF NOT "%TESTENV%"=="check" ( pip install twine && python setup.py register && @@ -87,3 +96,7 @@ deploy_script: artifacts: - path: dist\* + +### 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/tox.ini b/tox.ini index 36546b36..9ac1b77d 100644 --- a/tox.ini +++ b/tox.ini @@ -4,12 +4,14 @@ envlist = check, 2.7, 2.7-nocover, - 3.4, - 3.4-nocover, - 3.5, - 3.5-nocover, 3.6, 3.6-nocover, + 3.7, + 3.7-nocover, + 3.8, + 3.8-nocover, + 3.9, + 3.9-nocover, report, docs @@ -137,8 +139,8 @@ deps = [testenv:2.7-nocover] basepython = {env:TOXPYTHON:python2.7} -[testenv:3.4] -basepython = {env:TOXPYTHON:python3.4} +[testenv:3.6] +basepython = {env:TOXPYTHON:python3.6} setenv = {[testenv]setenv} WITH_COVERAGE=yes @@ -151,11 +153,11 @@ deps = {[testenv]deps} pytest-cov -[testenv:3.4-nocover] -basepython = {env:TOXPYTHON:python3.4} +[testenv:3.6-nocover] +basepython = {env:TOXPYTHON:python3.6} -[testenv:3.5] -basepython = {env:TOXPYTHON:python3.5} +[testenv:3.7] +basepython = {env:TOXPYTHON:python3.7} setenv = {[testenv]setenv} WITH_COVERAGE=yes @@ -168,11 +170,11 @@ deps = {[testenv]deps} pytest-cov -[testenv:3.5-nocover] -basepython = {env:TOXPYTHON:python3.5} +[testenv:3.7-nocover] +basepython = {env:TOXPYTHON:python3.7} -[testenv:3.6] -basepython = {env:TOXPYTHON:python3.6} +[testenv:3.8] +basepython = {env:TOXPYTHON:python3.8} setenv = {[testenv]setenv} WITH_COVERAGE=yes @@ -185,6 +187,23 @@ deps = {[testenv]deps} pytest-cov -[testenv:3.6-nocover] -basepython = {env:TOXPYTHON:python3.6} +[testenv:3.8-nocover] +basepython = {env:TOXPYTHON:python3.8} + +[testenv:3.9] +basepython = {env:TOXPYTHON:python3.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 + +[testenv:3.9-nocover] +basepython = {env:TOXPYTHON:python3.9} From 8a9b0e54f5c16147f7c0c7449cc05cadec713b52 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 4 Feb 2021 11:10:45 -0500 Subject: [PATCH 063/226] DOC: added summary of PR to changelog Added a summary of the changes to the changelog. --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5959c9fa..febc1913 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,8 @@ Changelog * Improved the subsol routine to allow array input * Improved PEP8 compliance * Added some missing docstrings to unit tests +* Fixed AppVeyor test environment +* Updated python test versions 1.0.3 (2018-04-05) From d705d3db4f977f92f5b15e0970535adb616e6afc Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Thu, 4 Feb 2021 11:29:15 -0500 Subject: [PATCH 064/226] Update src/apexpy/apex.py change error variable name from 'e' to 'err' Co-authored-by: Angeline Burrell --- src/apexpy/apex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index bdff6080..97287407 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -13,7 +13,7 @@ # below try..catch required for autodoc to work on readthedocs try: from . import fortranapex as fa -except ImportError as e: +except ImportError as err: print("ERROR: fortranapex module could not be imported, so apexpy probably" " won't work. Make sure you have a gfortran compiler. Wheels " "installation assumes your compiler lives in /opt/local/bin") From 3414e889c9aded4d7f415ad968a882633742e3b4 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Thu, 4 Feb 2021 11:29:34 -0500 Subject: [PATCH 065/226] Update src/apexpy/apex.py change error variable name from 'e' to 'err' Co-authored-by: Angeline Burrell --- src/apexpy/apex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 97287407..081bc899 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -17,7 +17,7 @@ print("ERROR: fortranapex module could not be imported, so apexpy probably" " won't work. Make sure you have a gfortran compiler. Wheels " "installation assumes your compiler lives in /opt/local/bin") - raise e + raise err # make sure invalid warnings are always shown warnings.filterwarnings('always', message='.*set to -9999 where*', From fb029a6a5f45dd7cfcd9f7838881063fe74cc44b Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Thu, 4 Feb 2021 11:30:08 -0500 Subject: [PATCH 066/226] Update src/apexpy/apex.py clarifying an AttributeError Co-authored-by: Angeline Burrell --- src/apexpy/apex.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 081bc899..5a3fe2d3 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -83,7 +83,8 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None): # convert date/datetime object to decimal year self.year = helpers.toYearFraction(date) except AttributeError: - # failed so date is probably int/float, use directly + # Failed while finding datetime attribute, so + # date is probably an int or float; use directly self.year = date if not os.path.isfile(datafile): From 9d22c88e8526bc1cf49f8019a1ce2ab6f5f4edd8 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Thu, 4 Feb 2021 15:14:31 -0500 Subject: [PATCH 067/226] Update test_Apex.py printing out warnings caught in test_geo2apex_undefined_warning --- tests/test_Apex.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 1f800a97..98037f4a 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -393,6 +393,7 @@ def test_geo2apex_undefined_warning(): apex2000 = Apex(date=2000, refh=10000) ret = apex2000.geo2apex(0, 0, 0) + print(wmsg) assert ret[0] == -9999 assert len(wmsg) == 1 assert issubclass(wmsg[-1].category, UserWarning) From aba2aca6c5ef03eff50696f8ed5d0726762bbe26 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Thu, 4 Feb 2021 16:38:46 -0500 Subject: [PATCH 068/226] Update test_Apex.py adding additional warning message printing to test_geo2apex_undefined_warning for debug purposes --- tests/test_Apex.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 98037f4a..14e167ca 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -394,6 +394,7 @@ def test_geo2apex_undefined_warning(): ret = apex2000.geo2apex(0, 0, 0) print(wmsg) + print([w.message for w in wmsg]) assert ret[0] == -9999 assert len(wmsg) == 1 assert issubclass(wmsg[-1].category, UserWarning) From 70bab14c90bb714c9bc09b5a12f7ead42c7d557f Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Thu, 4 Feb 2021 17:33:38 -0500 Subject: [PATCH 069/226] Update apex.py changing a call of `numpy.float()` to `numpy.float64` to avoid a deprecation warning --- src/apexpy/apex.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 5a3fe2d3..3c5d5f48 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -921,9 +921,10 @@ def set_epoch(self, year): """ # f2py - fa.loadapxsh(self.datafile, np.float(year)) - fa.cofrm(year) - self.year = year + self.year = np.float64(year) + fa.loadapxsh(self.datafile, self.year) + fa.cofrm(self.year) + def set_refh(self, refh): """Updates the apex reference height for all subsequent conversions. From 88e34c34a62d075a661b9847a927fa64a82e3ff0 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Thu, 4 Feb 2021 18:11:04 -0500 Subject: [PATCH 070/226] Update apex.py removing whitespace to make apex.py pep8 compliant --- src/apexpy/apex.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 3c5d5f48..252a2cdc 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -924,7 +924,6 @@ def set_epoch(self, year): self.year = np.float64(year) fa.loadapxsh(self.datafile, self.year) fa.cofrm(self.year) - def set_refh(self, refh): """Updates the apex reference height for all subsequent conversions. From 59c2da4bd0966ba80a9812b65bea0dbb77f195e5 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Fri, 5 Feb 2021 08:28:20 -0500 Subject: [PATCH 071/226] Update tests/test_Apex.py removing debugging print statements from test_geo2apex_undefined_warning Co-authored-by: Angeline Burrell --- tests/test_Apex.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 14e167ca..1f800a97 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -393,8 +393,6 @@ def test_geo2apex_undefined_warning(): apex2000 = Apex(date=2000, refh=10000) ret = apex2000.geo2apex(0, 0, 0) - print(wmsg) - print([w.message for w in wmsg]) assert ret[0] == -9999 assert len(wmsg) == 1 assert issubclass(wmsg[-1].category, UserWarning) From a30b58e88677961a2083359b44bf23238b75f5d9 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 5 Feb 2021 11:41:29 -0500 Subject: [PATCH 072/226] STY: update docstrings Update docstring section dividers. --- src/apexpy/helpers.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/apexpy/helpers.py b/src/apexpy/helpers.py index 1b909212..35d7c5f2 100644 --- a/src/apexpy/helpers.py +++ b/src/apexpy/helpers.py @@ -14,20 +14,20 @@ def checklat(lat, name='lat'): (tolerance 1e-4). Parameters - ========== - lat : array_like + ---------- + lat : array-like latitude name : str, optional parameter name to use in the exception message Returns - ======= + ------- lat : ndarray or float Same as input where values just outside the range have been clipped to [-90, 90] Raises - ====== + ------ ValueError if any values are too far outside the range [-90, 90] """ @@ -57,12 +57,12 @@ def getsinIm(alat): """Computes sinIm from modified apex latitude. Parameters - ========== - alat : array_like + ---------- + alat : array-like Modified apex latitude Returns - ======= + ------- sinIm : ndarray or float """ @@ -76,12 +76,12 @@ def getcosIm(alat): """Computes cosIm from modified apex latitude. Parameters - ========== - alat : array_like + ---------- + alat : array-like Modified apex latitude Returns - ======= + ------- cosIm : ndarray or float """ @@ -96,16 +96,16 @@ def toYearFraction(date): year. Parameters - ========== + ---------- date : :class:`datetime.date` or :class:`datetime.datetime` Returns - ======= + ------- year : float Decimal year Notes - ===== + ----- The algorithm is taken from http://stackoverflow.com/a/6451892/2978652 """ @@ -128,12 +128,12 @@ def gc2gdlat(gclat): """Converts geocentric latitude to geodetic latitude using WGS84. Parameters - ========== - gclat : array_like + --------- + gclat : array-like Geocentric latitude Returns - ======= + ------- gdlat : ndarray or float Geodetic latitude @@ -146,18 +146,18 @@ def subsol(datetime): """Finds subsolar geocentric latitude and longitude. Parameters - ========== + ---------- datetime : :class:`datetime.datetime` or :class:`numpy.ndarray[datetime64]` Returns - ======= + ------- sbsllat : float Latitude of subsolar point sbsllon : float Longitude of subsolar point 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 From 26bfd77f9f944515261649e93279b15eb75cefb1 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 5 Feb 2021 11:42:21 -0500 Subject: [PATCH 073/226] STY: updated docs and warnings Updated docstrings and error messages to use warnings in all instances. --- src/apexpy/apex.py | 143 ++++++++++++++++++++++++--------------------- 1 file changed, 75 insertions(+), 68 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 252a2cdc..cef4feb2 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -2,21 +2,21 @@ from __future__ import division, print_function, absolute_import -import os -import warnings import datetime as dt - import numpy as np +import os +import warnings from . import helpers -# below try..catch required for autodoc to work on readthedocs +# Below try..catch required for autodoc to work on readthedocs try: from . import fortranapex as fa except ImportError as err: - print("ERROR: fortranapex module could not be imported, so apexpy probably" - " won't work. Make sure you have a gfortran compiler. Wheels " - "installation assumes your compiler lives in /opt/local/bin") + warnings.warn("".join(["fortranapex module could not be imported, so ", + "apexpy probably won't work. Make sure you have ", + "a gfortran compiler. Wheels installation ", + "assumes your compiler lives in /opt/local/bin"])) raise err # make sure invalid warnings are always shown @@ -25,6 +25,8 @@ class ApexHeightError(ValueError): + """Specialized error type definition + """ pass @@ -33,7 +35,7 @@ class Apex(object): calculations. Parameters - ========== + ---------- date : float, :class:`dt.date`, or :class:`dt.datetime`, optional Determines which IGRF coefficients are used in conversions. Uses current date as default. If float, use decimal year. @@ -44,7 +46,7 @@ class Apex(object): Path to custom coefficient file Attributes - ========== + ---------- year : float Decimal year used for the IGRF model refh : float @@ -53,11 +55,11 @@ class Apex(object): Path to coefficient file Notes - ===== + ----- The calculations use IGRF-12 with coefficients from 1900 to 2020 [1]_. References - ========== + ---------- .. [1] Thébault, E. et al. (2015), International Geomagnetic Reference Field: the 12th generation, Earth, Planets and Space, 67(1), 79, @@ -77,10 +79,10 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None): self.set_refh(refh) # reference height if date is None: - self.year = helpers.toYearFraction(dt.datetime.now()) + self.year = helpers.toYearFraction(dt.datetime.utcnow()) else: try: - # convert date/datetime object to decimal year + # Convert date/datetime object to decimal year self.year = helpers.toYearFraction(date) except AttributeError: # Failed while finding datetime attribute, so @@ -88,10 +90,11 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None): self.year = date if not os.path.isfile(datafile): - raise IOError('Datafile does not exist: {}'.format(datafile)) + raise IOError('Data file does not exist: {}'.format(datafile)) if not os.path.isfile(fortranlib): - raise IOError('Datafile does not exist: {}'.format(fortranlib)) + raise IOError('Fortran library does not exist: {}'.format( + fortranlib)) self.datafile = datafile self.fortranlib = fortranlib @@ -128,7 +131,7 @@ def convert(self, lat, lon, source, dest, height=0, datetime=None, """Converts between geodetic, modified apex, quasi-dipole and MLT. Parameters - ========== + ---------- lat : array_like Latitude lon : array_like @@ -158,7 +161,7 @@ def convert(self, lat, lon, source, dest, height=0, datetime=None, prevents the South-Atlantic Anomaly (SAA) from influencing the MLT. Returns - ======= + ------- lat : ndarray or float Converted latitude (if converting to MLT, output latitude is apex) lat : ndarray or float @@ -217,7 +220,7 @@ def geo2apex(self, glat, glon, height): """Converts geodetic to modified apex coordinates. Parameters - ========== + ---------- glat : array_like Geodetic latitude glon : array_like @@ -226,7 +229,7 @@ def geo2apex(self, glat, glon, height): Altitude in km Returns - ======= + ------- alat : ndarray or float Modified apex latitude alon : ndarray or float @@ -249,7 +252,7 @@ def apex2geo(self, alat, alon, height, precision=1e-10): """Converts modified apex to geodetic coordinates. Parameters - ========== + ---------- alat : array_like Modified apex latitude alon : array_like @@ -265,7 +268,7 @@ def apex2geo(self, alat, alon, height, precision=1e-10): within the specified precision. Returns - ======= + ------- glat : ndarray or float Geodetic latitude glon : ndarray or float @@ -288,7 +291,7 @@ def geo2qd(self, glat, glon, height): """Converts geodetic to quasi-dipole coordinates. Parameters - ========== + ---------- glat : array_like Geodetic latitude glon : array_like @@ -297,7 +300,7 @@ def geo2qd(self, glat, glon, height): Altitude in km Returns - ======= + ------- qlat : ndarray or float Quasi-dipole latitude qlon : ndarray or float @@ -316,7 +319,7 @@ def qd2geo(self, qlat, qlon, height, precision=1e-10): """Converts quasi-dipole to geodetic coordinates. Parameters - ========== + ---------- qlat : array_like Quasi-dipole latitude qlon : array_like @@ -332,7 +335,7 @@ def qd2geo(self, qlat, qlon, height, precision=1e-10): within the specified precision. Returns - ======= + ------- glat : ndarray or float Geodetic latitude glon : ndarray or float @@ -397,7 +400,7 @@ def apex2qd(self, alat, alon, height): """Converts modified apex to quasi-dipole coordinates. Parameters - ========== + ---------- alat : array_like Modified apex latitude alon : array_like @@ -406,14 +409,14 @@ def apex2qd(self, alat, alon, height): Altitude in km Returns - ======= + ------- qlat : ndarray or float Quasi-dipole latitude qlon : ndarray or float Quasi-dipole longitude Raises - ====== + ------ ApexHeightError if `height` > apex height @@ -450,7 +453,7 @@ def qd2apex(self, qlat, qlon, height): """Converts quasi-dipole to modified apex coordinates. Parameters - ========== + ---------- qlat : array_like Quasi-dipole latitude qlon : array_like @@ -459,14 +462,14 @@ def qd2apex(self, qlat, qlon, height): Altitude in km Returns - ======= + ------- alat : ndarray or float Modified apex latitude alon : ndarray or float Modified apex longitude Raises - ====== + ------ ApexHeightError if apex height < reference height @@ -482,7 +485,7 @@ def mlon2mlt(self, mlon, datetime, ssheight=50 * 6371): and UT. Parameters - ========== + ---------- mlon : array_like Magnetic longitude (apex and quasi-dipole longitude are always equal) @@ -495,12 +498,12 @@ def mlon2mlt(self, mlon, datetime, ssheight=50 * 6371): prevents the South-Atlantic Anomaly (SAA) from influencing the MLT. Returns - ======= + ------- mlt : ndarray or float Magnetic local time [0, 24) Notes - ===== + ----- To compute the MLT, we find the apex longitude of the subsolar point at the given time. Then the MLT of the given point will be computed from the separation in magnetic longitude from this point (1 hour = 15 @@ -518,7 +521,7 @@ def mlt2mlon(self, mlt, datetime, ssheight=50 * 6371): and UT. Parameters - ========== + ---------- mlt : array_like Magnetic local time datetime : :class:`datetime.datetime` @@ -530,13 +533,13 @@ def mlt2mlon(self, mlt, datetime, ssheight=50 * 6371): prevents the South-Atlantic Anomaly (SAA) from influencing the MLT. Returns - ======= + ------- mlon : ndarray or float Magnetic longitude [0, 360) (apex and quasi-dipole longitude are always equal) Notes - ===== + ----- To compute the magnetic longitude, we find the apex longitude of the subsolar point at the given time. Then the magnetic longitude of the given point will be computed from the separation in magnetic local time @@ -555,7 +558,7 @@ def map_to_height(self, glat, glon, height, newheight, conjugate=False, or conjugate hemisphere. Parameters - ========== + ---------- glat : array_like Geodetic latitude glon : array_like @@ -576,7 +579,7 @@ def map_to_height(self, glat, glon, height, newheight, conjugate=False, within the specified precision. Returns - ======= + ------- newglat : ndarray or float Geodetic latitude of mapped point newglon : ndarray or float @@ -587,7 +590,7 @@ def map_to_height(self, glat, glon, height, newheight, conjugate=False, into geo2qd (APXG2Q) Notes - ===== + ----- The mapping is done by converting glat/glon/height to modified apex lat/lon, and converting back to geographic using newheight (if conjugate, use negative apex latitude when converting back) @@ -631,7 +634,8 @@ def _map_EV_to_height(self, alat, alon, height, newheight, X, EV): X1 = np.sum(X * v1, axis=0) # E dot e1 or V dot d1 X2 = np.sum(X * v2, axis=0) # E dot e2 or V dot d2 - _, _, _, _, _, _, d1, d2, _, e1, e2, _ = self.basevectors_apex(alat, alon, newheight, coords='apex') + _, _, _, _, _, _, d1, d2, _, e1, e2, _ = self.basevectors_apex( + alat, alon, newheight, coords='apex') if EV == 'E': v1 = d1 @@ -654,7 +658,7 @@ def map_E_to_height(self, alat, alon, height, newheight, E): It is assumed that the electric field is perpendicular to B. Parameters - ========== + ---------- alat : (N,) array_like or float Modified apex latitude alon : (N,) array_like or float @@ -668,7 +672,7 @@ def map_E_to_height(self, alat, alon, height, newheight, E): north, and up components Returns - ======= + ------- E : (3, N) or (3,) ndarray The electric field at `newheight` (geodetic east, north, and up components) @@ -682,7 +686,7 @@ def map_V_to_height(self, alat, alon, height, newheight, V): It is assumed that the electric field is perpendicular to B. Parameters - ========== + ---------- alat : (N,) array_like or float Modified apex latitude alon : (N,) array_like or float @@ -696,7 +700,7 @@ def map_V_to_height(self, alat, alon, height, newheight, V): east, north, and up components Returns - ======= + ------- V : (3, N) or (3,) ndarray The electric drift velocity at `newheight` (geodetic east, north, and up components) @@ -714,7 +718,7 @@ def basevectors_qd(self, lat, lon, height, coords='geo', precision=1e-10): north. Parameters - ========== + ---------- lat : (N,) array_like or float Latitude lon : (N,) array_like or float @@ -735,12 +739,12 @@ def basevectors_qd(self, lat, lon, height, coords='geo', precision=1e-10): passed through APXG2Q). Returns - ======= + ------- f1 : (2, N) or (2,) ndarray f2 : (2, N) or (2,) ndarray References - ========== + ---------- .. [2] Richmond, A. D. (1995), Ionospheric Electrodynamics Using Magnetic Apex Coordinates, Journal of geomagnetism and geoelectricity, 47(2), 191–212, :doi:`10.5636/jgg.47.191`. @@ -773,7 +777,7 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): north, and up (only east and north for `f1` and `f2`). Parameters - ========== + ---------- lat, lon : (N,) array_like or float Latitude lat : (N,) array_like or float @@ -794,12 +798,12 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): passed through APXG2Q). Returns - ======= + ------- f1, f2 : (2, N) or (2,) ndarray f3, g1, g2, g3, d1, d2, d3, e1, e2, e3 : (3, N) or (3,) ndarray Note - ==== + ---- `f3`, `g1`, `g2`, and `g3` are not part of the Fortran code by Emmert et al. [2010] [5]_. They are calculated by this Python library according to the following equations in @@ -811,7 +815,7 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): * `f3`: Eqn. 6.8 References - ========== + ---------- .. [4] Richmond, A. D. (1995), Ionospheric Electrodynamics Using Magnetic Apex Coordinates, Journal of geomagnetism and @@ -862,16 +866,18 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): F = np.cross(F1.T, F2.T).T[-1] cosI = helpers.getcosIm(alat) k = np.array([0, 0, 1], dtype=np.float64).reshape((3, 1)) - g1 = ((self.RE + np.float64(height)) / (self.RE + self.refh)) ** (3 / 2) * d1 / F - g2 = -1.0 / (2.0 * F * np.tan(np.radians(qlat))) * (k + ((self.RE + np.float64(height)) / - (self.RE + self.refh)) * d2 / cosI) + g1 = ((self.RE + np.float64(height)) + / (self.RE + self.refh)) ** (3 / 2) * d1 / F + g2 = -1.0 / (2.0 * F * np.tan(np.radians(qlat))) * ( + k + ((self.RE + np.float64(height)) / + (self.RE + self.refh)) * d2 / cosI) g3 = k * F f3 = np.cross(g1.T, g2.T).T if np.any(alat == -9999): - warnings.warn(('Base vectors g, d, e, and f3 set to -9999 where ' - 'apex latitude is undefined (apex height may be < ' - 'reference height)')) + warnings.warn(''.join(['Base vectors g, d, e, and f3 set to -9999 ', + 'where apex latitude is undefined (apex ', + 'height may be < reference height)'])) f3 = np.where(alat == -9999, -9999, f3) g1 = np.where(alat == -9999, -9999, g1) g2 = np.where(alat == -9999, -9999, g2) @@ -915,7 +921,7 @@ def set_epoch(self, year): """Updates the epoch for all subsequent conversions. Parameters - ========== + ---------- year : float Decimal year @@ -929,12 +935,12 @@ def set_refh(self, refh): """Updates the apex reference height for all subsequent conversions. Parameters - ========== + ---------- refh : float Apex reference height in km Notes - ===== + ----- The reference height is the height to which field lines will be mapped, and is only relevant for conversions involving apex (not quasi-dipole). @@ -950,7 +956,7 @@ def get_babs(self, glat, glon, height): """Returns the magnitude of the IGRF magnetic field in tesla. Parameters - ========== + ---------- glat : array_like Geodetic latitude glon : array_like @@ -959,7 +965,7 @@ def get_babs(self, glat, glon, height): Altitude in km Returns - ======= + ------- babs : ndarray or float Magnitude of the IGRF magnetic field @@ -978,7 +984,7 @@ def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): vector components are geodetic east, north, and up. Parameters - ========== + ---------- lat, lon : (N,) array_like or float Latitude lat : (N,) array_like or float @@ -999,14 +1005,14 @@ def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): passed through APXG2Q). Returns - ======= + ------- Be3: (1, N) or (1,) ndarray e3 : (3, N) or (3,) ndarray Bd3: (1, N) or (1,) ndarray d3 : (3, N) or (3,) ndarray Note - ==== + ---- Be3 is not equivalent to the magnitude of the IGRF magnitude, but is instead equal to the IGRF magnitude divided by a scaling factor, D. Similarly, Bd3 is the IGRF magnitude multiplied by D. @@ -1014,7 +1020,7 @@ def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): See Richmond, A. D. (1995) [4]_ equations 3.13 and 3.14 References - ========== + ---------- .. [4] Richmond, A. D. (1995), Ionospheric Electrodynamics Using Magnetic Apex Coordinates, Journal of geomagnetism and @@ -1031,7 +1037,8 @@ def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): babs = self.get_babs(glat, glon, height) - _, _, _, _, _, _, d1, d2, d3, _, _, e3 = self.basevectors_apex(glat, glon, height, coords='geo') + _, _, _, _, _, _, d1, d2, d3, _, _, e3 = self.basevectors_apex( + glat, glon, height, coords='geo') d1_cross_d2 = np.cross(d1.T, d2.T).T D = np.sqrt(np.sum(d1_cross_d2 ** 2, axis=0)) From 87440dc48ec8fad96ca0cee7f4e77bd6e0a3a242 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 5 Feb 2021 11:43:27 -0500 Subject: [PATCH 074/226] ENH: added IGRF coefficient file Added an IGRF coefficient file. --- src/fortranapex/igrf13coeffs.txt | 199 +++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 src/fortranapex/igrf13coeffs.txt diff --git a/src/fortranapex/igrf13coeffs.txt b/src/fortranapex/igrf13coeffs.txt new file mode 100644 index 00000000..9b362e06 --- /dev/null +++ b/src/fortranapex/igrf13coeffs.txt @@ -0,0 +1,199 @@ +# 13th 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 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 2020.0 2020-25 +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 -29441.46 -29404.8 5.7 +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.77 -1450.9 7.4 +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 4795.99 4652.5 -25.9 +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.88 -2499.6 -11.0 +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.20 2982.0 -7.0 +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.41 -2991.6 -30.2 +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.35 1677.0 -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 -642.17 -734.6 -22.4 +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.33 1363.2 2.2 +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.26 -2381.2 -5.9 +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.29 -82.1 6.0 +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.85 1236.2 3.1 +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 245.04 241.9 -1.1 +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 581.69 525.7 -12.0 +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.70 -543.4 0.5 +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.42 903.0 -1.2 +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.68 809.5 -1.6 +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.54 281.9 -0.1 +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.49 86.3 -5.9 +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.43 -158.4 6.5 +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.85 -309.4 5.2 +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.95 199.7 3.6 +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.38 48.0 -5.1 +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.23 -349.7 -5.0 +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.91 -234.3 -0.3 +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.14 363.2 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 46.98 47.7 0.0 +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.35 187.8 -0.6 +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 196.98 208.3 2.5 +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.94 -140.7 0.2 +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.14 -121.2 -0.6 +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.40 -151.2 1.3 +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 15.98 32.3 3.0 +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.30 13.5 0.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.12 98.9 0.3 +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 69.55 66.0 -0.5 +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.57 65.5 -0.3 +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.61 -19.1 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.79 72.9 0.4 +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.30 25.1 -1.6 +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.85 -121.5 1.3 +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.74 52.8 -1.3 +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.93 -36.2 -1.4 +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.64 -64.5 0.8 +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.14 13.5 0.0 +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.35 8.9 0.0 +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.85 -64.7 0.9 +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.41 68.1 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.29 80.6 -0.1 +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 -75.99 -76.7 -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.27 -51.5 0.6 +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.79 -8.2 0.0 +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.53 -16.9 0.6 +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.82 56.5 0.7 +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.59 2.2 -0.8 +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.07 15.8 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.45 23.5 -0.2 +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.32 6.4 -0.5 +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.27 -2.2 -1.1 +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.88 -7.2 -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.50 -27.2 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.61 9.8 0.8 +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.32 -1.8 0.3 +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 23.98 23.7 0.0 +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.89 9.7 0.1 +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.04 8.4 -0.2 +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.78 -17.6 -0.1 +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.26 -15.3 0.6 +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.16 -0.5 0.4 +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.18 12.8 -0.2 +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.56 -21.1 -0.1 +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.60 -11.7 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.33 15.3 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.16 14.9 -0.3 +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.76 13.7 0.3 +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.69 3.6 -0.4 +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.98 -16.5 -0.1 +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.10 -6.9 0.5 +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.02 -0.3 0.4 +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.26 2.8 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.33 5.0 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.83 8.4 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.77 -23.4 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.02 2.9 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.76 11.0 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.22 -1.5 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.74 9.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.67 -1.1 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.74 -5.1 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.20 -13.2 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.88 -6.3 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.10 1.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.79 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.68 8.8 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.04 0.4 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.06 -9.3 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 -3.89 -1.4 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.54 -11.9 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.44 9.6 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 -2.01 -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.26 -6.2 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.28 3.4 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.17 -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.40 -0.2 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.55 1.7 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.55 3.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.55 -0.9 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.40 4.8 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.70 0.7 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.92 -8.6 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.67 -0.9 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.61 -0.1 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.13 1.9 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.16 -4.3 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.33 1.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.85 -3.4 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.80 -2.4 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.12 -0.1 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.59 -3.8 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.72 -8.8 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.00 3.0 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.40 -1.4 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.00 0.0 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.30 -2.5 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.11 2.5 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.08 2.3 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.60 -0.6 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.79 -0.9 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.05 -0.4 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.58 0.3 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.76 0.6 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.70 -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.20 -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.14 -0.1 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.12 -1.7 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.70 1.4 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.44 -1.6 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.22 -0.6 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.57 -3.0 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.44 0.2 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.01 -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.49 3.1 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.34 -2.6 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 -2.09 -2.0 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.16 -0.1 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.08 -1.2 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.46 0.5 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.37 0.5 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.23 1.3 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.75 1.4 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.89 -1.2 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.19 -1.8 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.85 0.7 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.27 0.1 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.10 0.3 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.72 0.8 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.54 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.09 -0.2 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.37 -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.29 0.6 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.43 -0.5 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.23 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.22 0.1 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.89 -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.94 -1.1 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.16 0.0 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.03 -0.3 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.72 0.5 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.02 0.1 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.92 -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.88 -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.42 0.5 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.49 0.6 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.63 0.7 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.56 1.4 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.42 -0.3 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.50 -0.4 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 0.96 0.8 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.24 -1.3 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.19 0.0 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.10 -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.81 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.42 0.3 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.13 0.0 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.04 -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.38 0.4 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.48 0.5 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.08 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.48 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.46 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.30 -0.4 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.35 -0.5 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.43 -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.36 -0.4 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.71 -0.6 0.0 From 7608a90671e13600887a38c7575e86ec2d270758 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 5 Feb 2021 11:53:13 -0500 Subject: [PATCH 075/226] DOC: updated author list Updated list of authors. --- AUTHORS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index cb8f921d..a2ca555c 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -8,6 +8,7 @@ This python wrapper is made by: * Christer van der Meeren * Angeline G. Burrell (maintainer) * Ashton Reimer +* Gregory Starr Fortran code by Emmert et al. [2010] [1]_. Quasi-dipole and modified apex coordinates are defined by Richmond [1995] [2]_. The code uses From 69bb5aaf3ab3e3d2372c2ec1d690418ef995508a Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 5 Feb 2021 11:55:40 -0500 Subject: [PATCH 076/226] DOC: updated CHANGELOG Added summary of recent changes to changelog. --- CHANGELOG.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c8556c52..30ff054c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,15 @@ Changelog ========= +1.1.0 (2021-03-XX) +------------------ +* Improved the subsol routine to allow array input +* Improved PEP8 compliance +* Added some missing docstrings to unit tests +* Fixed AppVeyor test environment +* Updated python test versions +* Adapted Fortran to read IRGF coefficients from a text file (currently IGRF-13) + 1.0.4 (2019-04-05) ---------------------------------------- * Updated installation instructions From d772864e6c813c3c37bd79c8f27657c322e94f25 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 5 Feb 2021 11:58:47 -0500 Subject: [PATCH 077/226] DOC: update README Updated README with potential wheels issue. --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 7331ea1f..f289c686 100644 --- a/README.rst +++ b/README.rst @@ -17,7 +17,9 @@ Install (requires NumPy before installation):: pip install apexpy -This assumes that the same version of libgfortran is installed in the same location as when the pip wheel was built. If not, you may have trouble importing apexpy and you will have to build apexpy yourself using:: +This assumes that the same version of libgfortran is installed in the same +location as when the pip wheel was built (if a wheel was used). If not, you may +have trouble importing apexpy. If you run into trouble, try the command:: pip install --global-option='build_ext' apexpy From 210e77da3421d967644eeb3aec1ad5d438d1bf0e Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 5 Feb 2021 12:03:06 -0500 Subject: [PATCH 078/226] DOC: merged installation instructions Merged the installation instructions from the last release and the current developments. --- docs/installation.rst | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 579b6789..a4c26e9f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -13,20 +13,24 @@ When you have NumPy, install this package at the command line using pip install apexpy -This assumes that the same version of libgfortran is installed in the same location as when the pip wheel was built. If not, you may have trouble importing apexpy and you will have to build apexpy yourself using: +This assumes that the same version of libgfortran is installed in the same +location as when the pip wheel was built (in the event that a wheel is +available). If not, you may have trouble importing apexpy. In the event that +you run into issues, you can get around this problem by using +``pip`` [1]_:: -pip install --global-option='build_ext' apexpy + pip install --global-option='build_ext' apexpy which requires both libgfortran and gfortran to be installed on your system. +This is the default option for Linux, and so should not be an issue there. + +The package has been tested with the following setups (others might work, too): The package has been tested with the following setups (others might work, too): -* Windows (64 bit Python), Linux (64 bit), and Mac (64 bit) -* Python 2.7, 3.5, 3.6, (and 3.4 on Linux/Mac [2]_) +* Windows (32/64 bit Python), Linux (64 bit), and Mac (64 bit) +* Python 2.7, 3.6, 3.7, 3.8, 3.9 .. [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 `_. -.. [2] I do not know how to compile the Fortran extension on Windows in a - manner that is compatible with the omitted python versions. If you get - it working, let me know! From 683391f7a37ddb7caee614d04dc23add5234d67b Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 5 Feb 2021 12:52:56 -0500 Subject: [PATCH 079/226] TST: resolved conflicts in Apex unit tests Resolved the merge conflicts between the main and develop branch unit tests. --- tests/test_Apex.py | 134 +++++++++++++++++++++++++++++++-------------- 1 file changed, 92 insertions(+), 42 deletions(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 1ce815a1..f87016ba 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -314,7 +314,7 @@ def test_convert_qd2mlt(): datetime = dt.datetime(2000, 3, 9, 14, 25, 58) apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.convert(60, 15, 'qd', 'mlt', height=100, - datetime=datetime, ssheight=2e5)[1], + datetime=datetime, ssheight=2e5)[1], apex_out.mlon2mlt(15, datetime, ssheight=2e5)) @@ -333,7 +333,7 @@ def test_convert_mlt2apex(): datetime = dt.datetime(2000, 3, 9, 14, 25, 58) apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.convert(60, 15, 'mlt', 'apex', height=100, - datetime=datetime, ssheight=2e5), + datetime=datetime, ssheight=2e5), (60, apex_out.mlt2mlon(15, datetime, ssheight=2e5))) @@ -402,12 +402,16 @@ def test_geo2apex_invalid_lat(): def test_geo2apex_undefined_warning(): - apex_out = Apex(date=2000, refh=10000) - with warnings.catch_warnings(record=True) as w: + """Test warning and fill values for an undefined location + """ + with warnings.catch_warnings(record=True) as wmsg: + apex_out = Apex(date=2000, refh=10000) ret = apex_out.geo2apex(0, 0, 0) - assert ret[0] == -9999 - assert issubclass(w[-1].category, UserWarning) - assert 'set to -9999 where' in str(w[-1].message) + + assert ret[0] == -9999 + assert len(wmsg) == 1 + assert issubclass(wmsg[-1].category, UserWarning) + assert 'set to -9999 where' in str(wmsg[-1].message) ###============================================================================ @@ -420,7 +424,7 @@ def test_apex2geo(): lat, lon, error = apex_out.apex2geo(60, 15, 100, precision=1e-2) assert_allclose((lat, lon, error), apex_out.qd2geo(*apex_out.apex2qd(60, 15, 100), height=100, - precision=1e-2)) + precision=1e-2)) assert type(lat) != np.ndarray assert type(lon) != np.ndarray assert type(error) != np.ndarray @@ -543,8 +547,8 @@ def test_apex2qd_invalid_lat(): apex_out.apex2qd(90, 0, 0) apex_out.apex2qd(-90, 0, 0) - assert_allclose(apex_out.apex2qd(90+1e-5, 0, 0), apex_out.apex2qd(90, 0, 0), - rtol=0, atol=1e-8) + assert_allclose(apex_out.apex2qd(90+1e-5, 0, 0), + apex_out.apex2qd(90, 0, 0), rtol=0, atol=1e-8) def test_apex2qd_apexheight_close(): @@ -588,8 +592,8 @@ def test_qd2apex_invalid_lat(): apex_out.qd2apex(90, 0, 0) apex_out.qd2apex(-90, 0, 0) - assert_allclose(apex_out.qd2apex(90+1e-5, 0, 0), apex_out.qd2apex(90, 0, 0), - rtol=0, atol=1e-8) + assert_allclose(apex_out.qd2apex(90+1e-5, 0, 0), + apex_out.qd2apex(90, 0, 0), rtol=0, atol=1e-8) def test_qd2apex_apexheight_close(): @@ -633,7 +637,7 @@ def test_mlon2mlt_1Darray(): def test_mlon2mlt_2Darray(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.mlon2mlt([[0, 180], [0, 180]], - dt.datetime(2000, 2, 3, 4, 5, 6)), + dt.datetime(2000, 2, 3, 4, 5, 6)), [[23.019261, 11.019261], [23.019261, 11.019261]], rtol=1e-4) @@ -755,30 +759,32 @@ def test_mlt2mlon2mlt(): def test_map_to_height(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.map_to_height(60, 15, 100, 10000, conjugate=False, - precision=1e-10), + precision=1e-10), (31.841459274291992, 17.916629791259766, 0)) assert_allclose(apex_out.map_to_height(30, 170, 100, 500, conjugate=False, - precision=1e-2), + precision=1e-2), (25.727252960205078, 169.60546875, 0.00017655163537710905)) def test_map_to_height_same_height(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.map_to_height(60, 15, 100, 100, conjugate=False, - precision=1e-10), + precision=1e-10), (60, 15, 3.4150946248701075e-6), rtol=1e-5) def test_map_to_height_conjugate(): + """Test results of map_to_height using conjugacy + """ apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.map_to_height(60, 15, 100, 10000, conjugate=True, - precision=1e-10), + precision=1e-10), (-25.424892425537109, 27.310417175292969, - 1.2074182222931995e-6)) + 1.2074182222931995e-6), atol=1e-6) assert_allclose(apex_out.map_to_height(30, 170, 100, 500, conjugate=True, - precision=1e-2), + precision=1e-2), (-13.76642894744873, 164.24259948730469, - 0.00056820799363777041)) + 0.00056820799363777041), atol=1e-6) def test_map_to_height_vectorization(): @@ -829,31 +835,32 @@ def test_map_E_to_height(): # vectorize lat assert_allclose(apex_out.map_E_to_height([60, 70], 15, 100, 500, - np.array([[1, 2, 3]]*2).T), + np.array([[1, 2, 3]]*2).T), np.array([out_60_15_100_500, out_70_15_100_500]).T, rtol=1e-5) # vectorize lon assert_allclose(apex_out.map_E_to_height(60, [15, 30], 100, 500, - np.array([[1, 2, 3]]*2).T), + np.array([[1, 2, 3]]*2).T), np.array([out_60_15_100_500, out_60_30_100_500]).T, rtol=1e-5) # vectorize height assert_allclose(apex_out.map_E_to_height(60, 15, [100, 200], 500, - np.array([[1, 2, 3]]*2).T), + np.array([[1, 2, 3]]*2).T), np.array([out_60_15_100_500, out_60_15_200_500]).T, rtol=1e-5) # vectorize newheight assert_allclose(apex_out.map_E_to_height(60, 15, 100, [500, 1000], - np.array([[1, 2, 3]]*2).T), + np.array([[1, 2, 3]]*2).T), np.array([out_60_15_100_500, out_60_15_100_1000]).T, rtol=1e-5) # vectorize E assert_allclose(apex_out.map_E_to_height(60, 15, 100, 500, - np.array([[1, 2, 3], [2, 3, 4]]).T), + np.array([[1, 2, 3], + [2, 3, 4]]).T), np.array([out_60_15_100_500, out_60_15_100_500_234]).T, rtol=1e-5) @@ -1212,26 +1219,21 @@ def test_basevectors_apex_delta(): def test_basevectors_apex_invalid_scalar(): + """ Test warning and fill values for calculating base vectors with bad value + """ apex_out = Apex(date=2000, refh=10000) - with warnings.catch_warnings(record=True) as w: - (f1, f2, f3, g1, g2, g3, d1, d2, d3, e1, e2, - e3) = apex_out.basevectors_apex(0, 0, 0) - assert issubclass(w[-1].category, UserWarning) - assert 'set to -9999 where' in str(w[-1].message) + with warnings.catch_warnings(record=True) as wmsg: + base_vecs = apex_out.basevectors_apex(0, 0, 0) + + assert issubclass(wmsg[-1].category, UserWarning) + assert 'set to -9999 where' in str(wmsg[-1].message) invalid = [-9999, -9999, -9999] - assert not np.allclose(f1, invalid[:2]) - assert not np.allclose(f2, invalid[:2]) - assert_allclose(f3, invalid) - assert_allclose(g1, invalid) - assert_allclose(g2, invalid) - assert_allclose(g3, invalid) - assert_allclose(d1, invalid) - assert_allclose(d2, invalid) - assert_allclose(d3, invalid) - assert_allclose(e1, invalid) - assert_allclose(e2, invalid) - assert_allclose(e3, invalid) + for i, bvec in enumerate(base_vecs): + if i < 2: + assert not np.allclose(bvec, invalid[:2]) + else: + assert_allclose(bvec, invalid) ###============================================================================ @@ -1301,5 +1303,53 @@ def test_set_refh(): assert_allclose(ret_500, fa.apxg2all(60, 15, 100, 500, 0)[2:4]) +###============================================================================ +### Test the get_babs() method +###============================================================================ + + +def test_get_babs(): + inputs = [[[80],[100],[300]],[range(50,90,8),range(0,360,80),[300]*5], + [90.0,0,1000]] + temp1 = np.array([4.22045410e-05, 5.15672743e-05, 4.98150200e-05, + 5.06769359e-05, 4.91028428e-05]) + expected = [[5.1303124427795412e-05], temp1, [3.793962299823761e-05]] + + apex_out = Apex(date=2018.1, refh=0) + for i in range(len(inputs)): + outputs = apex_out.get_babs(*inputs[i]) + if isinstance(outputs,np.float64): + outputs = [outputs] + for j,output in enumerate(outputs): + assert_allclose(output, expected[i][j], rtol=0, atol=1e-5) + + +###============================================================================ +### Test the bvectors_apex() method +###============================================================================ + + +def test_bvectors_apex(): + inputs = [[80,81],[100,120],[100,200]] + + expected = (np.array([5.94623305e-05, 5.95450722e-05]), + np.array([[ 0.02008877, 0.00303204], + [ 0.03571109, 0.03377986], + [-0.94045794, -0.89848483]]), + np.array([ 5.26919505e-05, 4.81377429e-05]), + np.array([[ 0.02266997, 0.00375055], + [ 0.04029961, 0.04178477], + [-1.0612973 , -1.1114012 ]]) + ) + + apex_out = Apex(date=2018.1, refh=0) + + outputs = apex_out.bvectors_apex(*inputs,coords='geo', precision=1e-10) + for i,output in enumerate(outputs): + for j in range(output.size): + assert_allclose(output.ravel()[j], expected[i].ravel()[j], rtol=0, + atol=1e-5) + + if __name__ == '__main__': pytest.main() From 8431fe90d48e100c7f6f1dfe8ff6bee8a2be57eb Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 5 Feb 2021 13:03:28 -0500 Subject: [PATCH 080/226] TST: resolved merge conflicts Resolved merge conflicts in the command unit tests. --- tests/test_cmd.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 35f84059..dd4b86db 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -10,6 +10,7 @@ os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) outfile = 'tests/output.txt' + def setup_function(function): try: os.remove(outfile) @@ -34,8 +35,8 @@ def test_module_invocation(): def test_convert_YYYY(): pipe = subprocess.Popen(['python', '-m', 'apexpy', 'geo', 'apex', '2015', - '--height', '300', - '-i', 'tests/test_convert.txt', '-o', outfile]) + '--height', '300', '-i', 'tests/test_convert.txt', + '-o', outfile]) pipe.communicate() pipe.wait() assert os.path.isfile(outfile) @@ -97,6 +98,8 @@ def test_convert_single_line(): def test_convert_stdin_stdout(): + """ Test use of pipe input to command-line call + """ pipe = subprocess.Popen('echo 60 15 | apexpy geo apex 2015 --height 300', shell=True, stdout=subprocess.PIPE) stdout, _ = pipe.communicate() @@ -106,7 +109,9 @@ def test_convert_stdin_stdout(): def test_convert_refh(): - pipe = subprocess.Popen('echo 60 15 | apexpy geo apex 2000 --height 100 --refh=300', shell=True, stdout=subprocess.PIPE) + pipe = subprocess.Popen( + 'echo 60 15 | apexpy geo apex 2000 --height 100 --refh=300', + shell=True, stdout=subprocess.PIPE) stdout, _ = pipe.communicate() pipe.wait() np.testing.assert_allclose(np.array(stdout.split(b' '), dtype=float), From bf8bb95b298f3da974fc0f638625d6254a8d17ea Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Sat, 6 Feb 2021 10:58:17 -0500 Subject: [PATCH 081/226] Update MANIFEST.in adding CODE_OF_CONDUCT.md to MANIFEST.in --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index e69b26a5..09a2cfa4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -13,6 +13,7 @@ include CHANGELOG.rst include CONTRIBUTING.rst include LICENSE include README.rst +include CODE_OF_CONDUCT.md include tox.ini .travis.yml appveyor.yml .codeclimate.yml .landscape.yaml From c0fe57e3125109f37d6e800e17dc183e0b5e3fb2 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 12:10:14 -0500 Subject: [PATCH 082/226] Revert "ENH: added IGRF coefficient file" This reverts commit 87440dc48ec8fad96ca0cee7f4e77bd6e0a3a242. --- src/fortranapex/igrf13coeffs.txt | 199 ------------------------------- 1 file changed, 199 deletions(-) delete mode 100644 src/fortranapex/igrf13coeffs.txt diff --git a/src/fortranapex/igrf13coeffs.txt b/src/fortranapex/igrf13coeffs.txt deleted file mode 100644 index 9b362e06..00000000 --- a/src/fortranapex/igrf13coeffs.txt +++ /dev/null @@ -1,199 +0,0 @@ -# 13th 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 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 2020.0 2020-25 -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 -29441.46 -29404.8 5.7 -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.77 -1450.9 7.4 -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 4795.99 4652.5 -25.9 -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.88 -2499.6 -11.0 -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.20 2982.0 -7.0 -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.41 -2991.6 -30.2 -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.35 1677.0 -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 -642.17 -734.6 -22.4 -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.33 1363.2 2.2 -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.26 -2381.2 -5.9 -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.29 -82.1 6.0 -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.85 1236.2 3.1 -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 245.04 241.9 -1.1 -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 581.69 525.7 -12.0 -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.70 -543.4 0.5 -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.42 903.0 -1.2 -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.68 809.5 -1.6 -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.54 281.9 -0.1 -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.49 86.3 -5.9 -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.43 -158.4 6.5 -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.85 -309.4 5.2 -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.95 199.7 3.6 -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.38 48.0 -5.1 -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.23 -349.7 -5.0 -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.91 -234.3 -0.3 -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.14 363.2 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 46.98 47.7 0.0 -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.35 187.8 -0.6 -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 196.98 208.3 2.5 -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.94 -140.7 0.2 -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.14 -121.2 -0.6 -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.40 -151.2 1.3 -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 15.98 32.3 3.0 -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.30 13.5 0.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.12 98.9 0.3 -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 69.55 66.0 -0.5 -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.57 65.5 -0.3 -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.61 -19.1 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.79 72.9 0.4 -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.30 25.1 -1.6 -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.85 -121.5 1.3 -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.74 52.8 -1.3 -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.93 -36.2 -1.4 -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.64 -64.5 0.8 -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.14 13.5 0.0 -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.35 8.9 0.0 -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.85 -64.7 0.9 -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.41 68.1 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.29 80.6 -0.1 -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 -75.99 -76.7 -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.27 -51.5 0.6 -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.79 -8.2 0.0 -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.53 -16.9 0.6 -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.82 56.5 0.7 -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.59 2.2 -0.8 -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.07 15.8 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.45 23.5 -0.2 -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.32 6.4 -0.5 -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.27 -2.2 -1.1 -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.88 -7.2 -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.50 -27.2 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.61 9.8 0.8 -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.32 -1.8 0.3 -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 23.98 23.7 0.0 -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.89 9.7 0.1 -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.04 8.4 -0.2 -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.78 -17.6 -0.1 -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.26 -15.3 0.6 -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.16 -0.5 0.4 -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.18 12.8 -0.2 -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.56 -21.1 -0.1 -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.60 -11.7 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.33 15.3 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.16 14.9 -0.3 -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.76 13.7 0.3 -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.69 3.6 -0.4 -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.98 -16.5 -0.1 -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.10 -6.9 0.5 -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.02 -0.3 0.4 -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.26 2.8 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.33 5.0 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.83 8.4 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.77 -23.4 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.02 2.9 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.76 11.0 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.22 -1.5 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.74 9.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.67 -1.1 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.74 -5.1 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.20 -13.2 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.88 -6.3 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.10 1.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.79 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.68 8.8 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.04 0.4 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.06 -9.3 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 -3.89 -1.4 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.54 -11.9 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.44 9.6 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 -2.01 -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.26 -6.2 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.28 3.4 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.17 -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.40 -0.2 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.55 1.7 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.55 3.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.55 -0.9 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.40 4.8 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.70 0.7 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.92 -8.6 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.67 -0.9 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.61 -0.1 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.13 1.9 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.16 -4.3 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.33 1.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.85 -3.4 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.80 -2.4 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.12 -0.1 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.59 -3.8 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.72 -8.8 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.00 3.0 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.40 -1.4 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.00 0.0 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.30 -2.5 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.11 2.5 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.08 2.3 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.60 -0.6 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.79 -0.9 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.05 -0.4 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.58 0.3 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.76 0.6 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.70 -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.20 -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.14 -0.1 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.12 -1.7 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.70 1.4 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.44 -1.6 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.22 -0.6 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.57 -3.0 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.44 0.2 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.01 -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.49 3.1 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.34 -2.6 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 -2.09 -2.0 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.16 -0.1 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.08 -1.2 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.46 0.5 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.37 0.5 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.23 1.3 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.75 1.4 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.89 -1.2 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.19 -1.8 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.85 0.7 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.27 0.1 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.10 0.3 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.72 0.8 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.54 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.09 -0.2 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.37 -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.29 0.6 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.43 -0.5 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.23 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.22 0.1 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.89 -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.94 -1.1 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.16 0.0 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.03 -0.3 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.72 0.5 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.02 0.1 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.92 -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.88 -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.42 0.5 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.49 0.6 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.63 0.7 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.56 1.4 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.42 -0.3 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.50 -0.4 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 0.96 0.8 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.24 -1.3 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.19 0.0 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.10 -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.81 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.42 0.3 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.13 0.0 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.04 -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.38 0.4 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.48 0.5 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.08 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.48 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.46 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.30 -0.4 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.35 -0.5 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.43 -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.36 -0.4 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.71 -0.6 0.0 From dbf6d760ea25ee4d760d198bfa4529451b9ff6e2 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 12:26:43 -0500 Subject: [PATCH 083/226] ENH: moved config info to setup.cfg Moved information from bumpversion and coveralls configuraiton files into setup.cfg. Removed unused cookiecutter configuration file. --- .bumpversion.cfg | 11 ----------- .cookiecutterrc | 34 ---------------------------------- .coveragerc | 12 ------------ setup.cfg | 41 +++++++++++++++++++++++++++++++---------- 4 files changed, 31 insertions(+), 67 deletions(-) delete mode 100644 .bumpversion.cfg delete mode 100644 .cookiecutterrc delete mode 100644 .coveragerc diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index 6b6b77f6..00000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,11 +0,0 @@ -[bumpversion] -current_version = 1.0.3 -commit = True -tag = False - -[bumpversion:file:setup.py] - -[bumpversion:file:docs/conf.py] - -[bumpversion:file:src/apexpy/__init__.py] - diff --git a/.cookiecutterrc b/.cookiecutterrc deleted file mode 100644 index f42ed2b0..00000000 --- a/.cookiecutterrc +++ /dev/null @@ -1,34 +0,0 @@ -# This file exists so you can easily regenerate your project. -# -# Unfortunately cookiecutter can't use this right away so -# you have to copy this file to ~/.cookiecutterrc - -default_context: - - appveyor: 'yes' - c_extension_optional: 'no' - c_extension_support: 'yes' - codacy: 'yes' - codeclimate: 'yes' - codecov: 'yes' - command_line_interface: 'no' - coveralls: 'yes' - distribution_name: 'apexpy' - email: 'cmeeren@gmail.com' - full_name: 'Christer van der Meeren' - github_username: 'cmeeren' - landscape: 'yes' - package_name: 'apexpy' - project_name: 'AACGM-v2 Python library' - project_short_description: '"A Python wrapper for modified apex coordinates"' - release_date: '2015-11-18' - repo_name: 'apexpy' - requiresio: 'yes' - scrutinizer: 'yes' - sphinx_theme: 'readthedocs' - test_matrix_configurator: 'yes' - test_runner: 'pytest' - travis: 'yes' - version: '0.1.0' - website: 'https://github.com/cmeeren/apexpy' - year: '2015' diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 772866d2..00000000 --- a/.coveragerc +++ /dev/null @@ -1,12 +0,0 @@ -[paths] -source = src - -[run] -branch = True -source = src -parallel = true - -[report] -show_missing = true -precision = 2 -omit = *migrations* diff --git a/setup.cfg b/setup.cfg index 8f832237..49cd112b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,9 +1,33 @@ [aliases] release = register clean --all sdist +[bumpversion] +current_version = 1.0.4 +commit = True +tag = True + +[bumpversion:file:setup.py] + +[bumpversion:file:docs/conf.py] + +[bumpversion:file:src/apexpy/__init__.py] + +[coverage:paths] +source = src + +[coverage:run] +branch = True +source = src +parallel = true + +[coverage:report] +show_missing = true +precision = 2 +omit = *migrations* + [flake8] -max-line-length = 140 -exclude = tests/*,*/migrations/*,*/south_migrations/* +max-line-length = 80 +exclude = */migrations/*,*/south_migrations/* [pytest] norecursedirs = @@ -28,9 +52,13 @@ addopts = --doctest-modules --doctest-glob=\*.rst --tb=short +flake8-ignore = + *.py W503 + docs/conf.py ALL + src/apexpy/__init__.py F401 [isort] -line_length=120 +line_length=80 known_first_party=apexpy default_section=THIRDPARTY length_sort=1 @@ -64,13 +92,6 @@ python_versions = 3.8 3.9 -dependencies = -# 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 nocover: false From 8d7ac43c2196a36fdabd09cdac9449fe5c4242f0 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 12:27:08 -0500 Subject: [PATCH 084/226] MAINT: updated email and version Updated the maintainer email and current version number. --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 1e3e183b..0589d3c5 100644 --- a/setup.py +++ b/setup.py @@ -34,14 +34,14 @@ def read(*names, **kwargs): if __name__ == "__main__": setup( name='apexpy', - version='1.0.3', + version='1.0.4', license='MIT', description='A Python wrapper for Apex coordinates', long_description='%s\n%s' % (read('README.rst'), re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst'))), author='Christer van der Meeren; Angeline G. Burrell', - author_email='agb073000@utdallas.edu', + author_email='angeline.burrell@nrl.navy.mil', url='https://github.com/aburrell/apexpy', packages=find_packages('src'), package_dir={'': 'src'}, From 7f69a0660f868acf718bf9ef0547720c99c63532 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 12:31:07 -0500 Subject: [PATCH 085/226] STY: updated PEP8 Updated line length issues in apex.py and helpers.py. Fixed some problems in test_Apex.py. --- src/apexpy/apex.py | 3 ++- src/apexpy/helpers.py | 6 ++++-- tests/test_Apex.py | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index cef4feb2..2da8663e 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -618,7 +618,8 @@ def _map_EV_to_height(self, alat, alon, height, newheight, X, EV): raise ValueError(EV + ' must be (3, N) or (3,) ndarray') X = np.reshape(X, (3, np.size(X) // 3)) - _, _, _, _, _, _, d1, d2, _, e1, e2, _ = self.basevectors_apex(alat, alon, height, coords='apex') + _, _, _, _, _, _, d1, d2, _, e1, e2, _ = self.basevectors_apex( + alat, alon, height, coords='apex') if EV == 'E': v1 = e1 diff --git a/src/apexpy/helpers.py b/src/apexpy/helpers.py index 35d7c5f2..0fcdd6de 100644 --- a/src/apexpy/helpers.py +++ b/src/apexpy/helpers.py @@ -175,9 +175,11 @@ def subsol(datetime): if isinstance(datetime, dt.datetime): year = np.asanyarray([datetime.year]) doy = np.asanyarray([datetime.timetuple().tm_yday]) - ut = np.asanyarray([datetime.hour * 3600 + datetime.minute * 60 + datetime.second]) + ut = np.asanyarray([datetime.hour * 3600 + datetime.minute * 60 + + datetime.second]) elif isinstance(datetime, np.ndarray): - times = datetime.astype('datetime64[s]') # works for datetime of wrong precision or unix epoch + # This conversion works for datetime of wrong precision or unit epoch + times = datetime.astype('datetime64[s]') year_floor = times.astype('datetime64[Y]') day_floor = times.astype('datetime64[D]') year = year_floor.astype(int) + 1970 diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 1f800a97..f72731c2 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -1315,9 +1315,9 @@ def test_bvectors_apex(): [-1.0612973 , -1.1114012 ]]) ) - A = Apex(date=2018.1, refh=0) + apex_out = Apex(date=2018.1, refh=0) - outputs = A.bvectors_apex(*inputs,coords='geo', precision=1e-10) + outputs = apex_out.bvectors_apex(*inputs, coords='geo', precision=1e-10) for i,output in enumerate(outputs): for j in range(output.size): assert_allclose(output.ravel()[j], expected[i].ravel()[j], rtol=0, From 301d933bb6096becaf7a7a3b22db41ad52efcce7 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 12:41:57 -0500 Subject: [PATCH 086/226] BUG: fixed character limit The character limit in upstream files was larger than the limit here, so it was fixed to be the same. --- src/fortranapex/checkapexsh.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fortranapex/checkapexsh.f90 b/src/fortranapex/checkapexsh.f90 index 37ef8858..ceb07491 100644 --- a/src/fortranapex/checkapexsh.f90 +++ b/src/fortranapex/checkapexsh.f90 @@ -19,7 +19,7 @@ program checkapexsh integer(4), parameter :: nepochgrid=25 integer(4) :: lmax=3, nmmax=6 - character(128) :: apexshfile='apexsh.dat' + character(1000) :: apexshfile='apexsh.dat' real(4) :: epochgrid(0:nepochgrid-1) real(4) :: epoch real(4) :: glat, glon, alt, hr, prec, error From b96023485902870682cd3fe25b33cda21f546176 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 12:49:02 -0500 Subject: [PATCH 087/226] BUG: updaetd sources in setup.py Updated the source files in setup.py to include new IGRF loading routine. --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1e3e183b..2ee13318 100644 --- a/setup.py +++ b/setup.py @@ -21,8 +21,10 @@ Extension(name='apexpy.fortranapex', sources=['src/fortranapex/magfld.f', 'src/fortranapex/apex.f', 'src/fortranapex/makeapexsh.f90', + 'src/fortranapex/igrf.f90', 'src/fortranapex/apexsh.f90', - 'src/fortranapex/checkapexsh.f90', 'src/fortranapex/fortranapex.pyf'])] + 'src/fortranapex/checkapexsh.f90', + 'src/fortranapex/fortranapex.pyf'])] def read(*names, **kwargs): return io.open( From ccdc402048662044c699eaf221f798cc50708b85 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 12:50:30 -0500 Subject: [PATCH 088/226] STY: updated tabbing Python 3.7 was unable to find the `end module igrf` command due to tabbing style. --- src/fortranapex/igrf.f90 | 253 +++++++++++++++++++-------------------- 1 file changed, 126 insertions(+), 127 deletions(-) diff --git a/src/fortranapex/igrf.f90 b/src/fortranapex/igrf.f90 index aba1cfea..16cd7399 100644 --- a/src/fortranapex/igrf.f90 +++ b/src/fortranapex/igrf.f90 @@ -1,136 +1,135 @@ - module igrf +module igrf - implicit none - - contains - - subroutine read_igrf(filename_in,GYR,HYR,GT,HT,NEPO,NGHT,EPOCH,NMXE) - - implicit none - - real*8,allocatable,intent(inout) :: GYR(:,:,:),HYR(:,:,:) - real*8,allocatable,intent(inout) :: GT(:,:),HT(:,:) - real*4,allocatable,intent(inout) :: EPOCH(:),NMXE(:) - integer, intent(out) :: NEPO,NGHT - character(len=*),intent(in) :: filename_in - - character(len=10000) :: s - character(len=100) :: junk - integer :: state, i, offset, pos,o - integer :: num_sh, L_max - integer :: num_epochs - integer :: l,m,e - real*8,allocatable :: g(:,:) - integer,allocatable :: nm(:,:) - - ! Get number of Gauss coefficients - !num_sh = NGHT-2*sqrt(real(NGHT)) - !write(*,*) num_sh - - write(*,*) '----------------------------' - write(*,*) ' Read IGRF coefficeints ' - write(*,*) '----------------------------' - - ! Open IGRF file - open(unit=100, file=filename_in,status='old',iostat=state) - if (state /= 0) then - stop "File open error" - end if - - ! Skip comment lines - do - read(unit=100,fmt='(A)') s - if (s(1:1) .ne. '#') exit - enddo - - ! Read epochs - num_epochs=count([(s(i:i+3),i=1,len_trim(s))].eq.'IGRF') - num_epochs=count([(s(i:i+3),i=1,len_trim(s))].eq.'DGRF')+num_epochs - allocate(EPOCH(1:num_epochs)) - write(*,*) 'Number of epochs read: ',num_epochs - allocate(NMXE(1:num_epochs)) - - ! Read epochs - read(100,*,iostat=state) s - do i=1,num_epochs - EPOCH(i) = 1900+(i-1)*5.0d0 - enddo - - ! Number of coefficients - do i=1,num_epochs - if (EPOCH(i) .ge. 2000.0d0) then - NMXE(i) = 13 - elseif (EPOCH(i) .ge. 1900.0d0) then - NMXE(i) = 10 + implicit none + +contains + + subroutine read_igrf(filename_in,GYR,HYR,GT,HT,NEPO,NGHT,EPOCH,NMXE) + + implicit none + + real*8,allocatable,intent(inout) :: GYR(:,:,:),HYR(:,:,:) + real*8,allocatable,intent(inout) :: GT(:,:),HT(:,:) + real*4,allocatable,intent(inout) :: EPOCH(:),NMXE(:) + integer, intent(out) :: NEPO,NGHT + character(len=*),intent(in) :: filename_in + + character(len=10000) :: s + character(len=100) :: junk + integer :: state, i, offset, pos,o + integer :: num_sh, L_max + integer :: num_epochs + integer :: l,m,e + real*8,allocatable :: g(:,:) + integer,allocatable :: nm(:,:) + + ! Get number of Gauss coefficients + !num_sh = NGHT-2*sqrt(real(NGHT)) + !write(*,*) num_sh + + write(*,*) '----------------------------' + write(*,*) ' Read IGRF coefficeints ' + write(*,*) '----------------------------' + + ! Open IGRF file + open(unit=100, file=filename_in,status='old',iostat=state) + if (state /= 0) then + stop "File open error" + end if + + ! Skip comment lines + do + read(unit=100,fmt='(A)') s + if (s(1:1) .ne. '#') exit + enddo + + ! Read epochs + num_epochs=count([(s(i:i+3),i=1,len_trim(s))].eq.'IGRF') + num_epochs=count([(s(i:i+3),i=1,len_trim(s))].eq.'DGRF')+num_epochs + allocate(EPOCH(1:num_epochs)) + write(*,*) 'Number of epochs read: ',num_epochs + allocate(NMXE(1:num_epochs)) + + ! Read epochs + read(100,*,iostat=state) s + do i=1,num_epochs + EPOCH(i) = 1900+(i-1)*5.0d0 + enddo + + ! Number of coefficients + do i=1,num_epochs + if (EPOCH(i) .ge. 2000.0d0) then + NMXE(i) = 13 + elseif (EPOCH(i) .ge. 1900.0d0) then + NMXE(i) = 10 + else + write(*,*) 'ERROR: Epoch unavailable!' + exit + endif + enddo + + ! Save file position + offset = ftell(100) + + ! Get the number of lines (coefficients) + num_sh = 0 + do + read(unit=100,fmt=*,iostat=state) + if (state < 0) exit + num_sh = num_sh+1 + enddo + L_max = sqrt(num_sh+1.0d0)-1 + close(100) + + ! Restore file position, must re-open after reaching EOF + open(unit=100, file=filename_in,status='old',iostat=state) + call fseek(100,offset,0,state) + + ! Assign the variables for Gauss coefficients + allocate(g(1:num_sh,1:num_epochs)) + allocate(nm(1:num_sh,2)) + + ! Read coefficients + do i=1,num_sh + read(100,*,iostat=state) s,nm(i,1),nm(i,2),g(i,:) + if (state < 0) exit + enddo + close(100) + + ! Assign the return values + NGHT = (sqrt(num_sh+1.0)+1)**2 + NEPO = num_epochs + + ! Assign the Gauss coefficients + allocate(GYR(NGHT,NGHT,NEPO),GT(NGHT,NGHT)) + allocate(HYR(NGHT,NGHT,NEPO),HT(NGHT,NGHT)) + GYR=0.0d0 + GT =0.0d0 + HYR=0.0d0 + HT =0.0d0 + write(*,*) 'ASSIGN COEFFICIENTS: ',NEPO,NGHT + do e=1,NEPO+1 + do l=1,L_max + if (e .le. NEPO) then + GYR(l+1,1,e) = g(l**2,e) else - write(*,*) 'ERROR: Epoch unavailable!' - exit + GT(l+1,1) = g(l**2,e) endif - enddo - - ! Save file position - offset = ftell(100) - - ! Get the number of lines (coefficients) - num_sh = 0 - do - read(unit=100,fmt=*,iostat=state) - if (state < 0) exit - num_sh = num_sh+1 - enddo - L_max = sqrt(num_sh+1.0d0)-1 - close(100) - - ! Restore file position, must re-open after reaching EOF - open(unit=100, file=filename_in,status='old',iostat=state) - call fseek(100,offset,0,state) - - ! Assign the variables for Gauss coefficients - allocate(g(1:num_sh,1:num_epochs)) - allocate(nm(1:num_sh,2)) - - ! Read coefficients - do i=1,num_sh - read(100,*,iostat=state) s,nm(i,1),nm(i,2),g(i,:) - if (state < 0) exit - enddo - close(100) - - ! Assign the return values - NGHT = (sqrt(num_sh+1.0)+1)**2 - NEPO = num_epochs - - - ! Assign the Gauss coefficients - allocate(GYR(NGHT,NGHT,NEPO),GT(NGHT,NGHT)) - allocate(HYR(NGHT,NGHT,NEPO),HT(NGHT,NGHT)) - GYR=0.0d0 - GT =0.0d0 - HYR=0.0d0 - HT =0.0d0 - write(*,*) 'ASSIGN COEFFICIENTS: ',NEPO,NGHT - do e=1,NEPO+1 - do l=1,L_max - if (e .le. NEPO) then - GYR(l+1,1,e) = g(l**2,e) - else - GT(l+1,1) = g(l**2,e) - endif - do m=1,l - if (m .le. l) then + do m=1,l + if (m .le. l) then pos = 1 + m*(L_max+2) + l if (e .le. NEPO) then - GYR(l+1,m+1,e)=g(l**2+2*m-1,e) - HYR(l+1,m+1,e)=g(l**2+2*m ,e) + GYR(l+1,m+1,e)=g(l**2+2*m-1,e) + HYR(l+1,m+1,e)=g(l**2+2*m ,e) else - GT(l+1,m+1)=g(l**2+2*m-1,e) - HT(l+1,m+1)=g(l**2+2*m ,e) + GT(l+1,m+1)=g(l**2+2*m-1,e) + HT(l+1,m+1)=g(l**2+2*m ,e) endif - endif - enddo + endif enddo - enddo - return - end subroutine read_igrf + enddo + enddo + return + end subroutine read_igrf - end module igrf +end module igrf From 17dccd99a90712dd38b8c10797d5c5778a1c6fd0 Mon Sep 17 00:00:00 2001 From: Achim Morschhauser <21514612+sputnik-a@users.noreply.github.com> Date: Mon, 8 Feb 2021 19:02:59 +0100 Subject: [PATCH 089/226] Update AUTHORS.rst Added to author list as suggested by aburrell --- AUTHORS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index a2ca555c..e6992cbb 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -9,6 +9,7 @@ This python wrapper is made by: * Angeline G. Burrell (maintainer) * Ashton Reimer * Gregory Starr +* Achim Morschhauser Fortran code by Emmert et al. [2010] [1]_. Quasi-dipole and modified apex coordinates are defined by Richmond [1995] [2]_. The code uses From 0e744511b5b93ebcf6090d5c4993d206f50a9785 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 16:52:54 -0500 Subject: [PATCH 090/226] STY: made PEP8 changes Updated code to pass flake8. --- tests/test_Apex.py | 230 +++++++++++++++++++++++---------------------- 1 file changed, 117 insertions(+), 113 deletions(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 191da60f..cf60c924 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -13,17 +13,17 @@ from apexpy import Apex, ApexHeightError, helpers -############################################################################## -# NOTE: whenever function outputs are tested against hard-coded numbers, # -# the test results (numbers) were obtained by running the code that is # -# tested. Therefore these tests below only check that nothing changes when # -# refactoring etc., and not if the results are actually correct # -############################################################################## +# ---------------------------------------------------------------------------- +# NOTE: whenever function outputs are tested against hard-coded numbers, the +# test results (numbers) were obtained by running the code that is tested. +# Therefore these tests below only check that nothing changes when refactoring, +# etc., and not if the results are actually correct. +# ----------------------------------------------------------------------------- -###============================================================================ -### Test initiating the Apex class -###============================================================================ +# ============================================================================ +# Test initiating the Apex class +# ============================================================================ def test_init_defaults(): @@ -57,9 +57,9 @@ def test_init_datafile_IOError(): Apex(date=2015, datafile='foo/path/to/datafile.blah') -###============================================================================ -### Test the low-level interfaces to the fortran wrappers -###============================================================================ +# ============================================================================ +# Test the low-level interfaces to the fortran wrappers +# ============================================================================ def test__geo2qd_scalar(): apex_out = Apex(date=2000, refh=300) @@ -157,7 +157,7 @@ def test__geo2apexall_array(): assert_allclose(ret[i].astype(float), np.array([[ret1[i], ret2[i]], [ret3[i], ret4[i]]], dtype=float)) - except: + except TypeError: # ret[i] is array of arrays assert_allclose(ret[i][0, 0], ret1[i]) assert_allclose(ret[i][0, 1], ret2[i]) @@ -177,7 +177,7 @@ def test__qd2geo_scalar(): def test__qd2geo_array(): apex_out = Apex(date=2000, refh=300) lats, lons, errs = apex_out._qd2geo([[0, 30], [60, 90]], 15, - [[100, 200], [300, 400]], 1e-2) + [[100, 200], [300, 400]], 1e-2) lat1, lon1, err1 = fa.apxq2g(0, 15, 100, 1e-2) lat2, lon2, err2 = fa.apxq2g(30, 15, 200, 1e-2) lat3, lon3, err3 = fa.apxq2g(60, 15, 300, 1e-2) @@ -244,9 +244,9 @@ def test__basevec_longitude(): fa.apxg2q(lat, 15, 100, 1)[2:4]) -###============================================================================ -### Test the convert() method -###============================================================================ +# ============================================================================ +# Test the convert() method +# ============================================================================ def test_convert_geo2apex(): @@ -314,7 +314,7 @@ def test_convert_qd2mlt(): datetime = dt.datetime(2000, 3, 9, 14, 25, 58) apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.convert(60, 15, 'qd', 'mlt', height=100, - datetime=datetime, ssheight=2e5)[1], + datetime=datetime, ssheight=2e5)[1], apex_out.mlon2mlt(15, datetime, ssheight=2e5)) @@ -333,7 +333,7 @@ def test_convert_mlt2apex(): datetime = dt.datetime(2000, 3, 9, 14, 25, 58) apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.convert(60, 15, 'mlt', 'apex', height=100, - datetime=datetime, ssheight=2e5), + datetime=datetime, ssheight=2e5), (60, apex_out.mlt2mlon(15, datetime, ssheight=2e5))) @@ -368,9 +368,9 @@ def test_convert_invalid_transformation(): apex_out.convert(0, 0, 'geo', 'foobar') -###============================================================================ -### Test the geo2apex() method -###============================================================================ +# ============================================================================ +# Test the geo2apex() method +# ============================================================================ def test_geo2apex(): @@ -414,9 +414,9 @@ def test_geo2apex_undefined_warning(): assert 'set to -9999 where' in str(wmsg[-1].message) -###============================================================================ -### Test the apex2geo() method -###============================================================================ +# ============================================================================ +# Test the apex2geo() method +# ============================================================================ def test_apex2geo(): @@ -424,7 +424,7 @@ def test_apex2geo(): lat, lon, error = apex_out.apex2geo(60, 15, 100, precision=1e-2) assert_allclose((lat, lon, error), apex_out.qd2geo(*apex_out.apex2qd(60, 15, 100), height=100, - precision=1e-2)) + precision=1e-2)) assert type(lat) != np.ndarray assert type(lon) != np.ndarray assert type(error) != np.ndarray @@ -450,9 +450,9 @@ def test_apex2geo_invalid_lat(): apex_out.apex2geo(90, 0, 0, 1e-2), rtol=0, atol=1e-8) -###============================================================================ -### Test the geo2qd() method -###============================================================================ +# ============================================================================ +# Test the geo2qd() method +# ============================================================================ def test_geo2qd(): @@ -483,9 +483,9 @@ def test_geo2qd_invalid_lat(): rtol=0, atol=1e-8) -###============================================================================ -### Test the qd2geo() method -###============================================================================ +# ============================================================================ +# Test the qd2geo() method +# ============================================================================ def test_qd2geo(): @@ -517,9 +517,9 @@ def test_qd2geo_invalid_lat(): apex_out.qd2geo(90, 0, 0, 1e-2), rtol=0, atol=1e-8) -###============================================================================ -### Test the apex2qd() method -###============================================================================ +# ============================================================================ +# Test the apex2qd() method +# ============================================================================ def test_apex2qd(): @@ -562,9 +562,9 @@ def test_apex2qd_apexheight_over(): apex_out.apex2qd(0, 15, 301) -###============================================================================ -### Test the qd2apex() method -###============================================================================ +# ============================================================================ +# Test the qd2apex() method +# ============================================================================ def test_qd2apex(): @@ -608,9 +608,9 @@ def test_qd2apex_apexheight_over(): apex_out.qd2apex(0, 15, 299) -###============================================================================ -### Test mlon2mlt() -###============================================================================ +# ============================================================================ +# Test mlon2mlt() +# ============================================================================ def test_mlon2mlt_scalar(): @@ -637,7 +637,7 @@ def test_mlon2mlt_1Darray(): def test_mlon2mlt_2Darray(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.mlon2mlt([[0, 180], [0, 180]], - dt.datetime(2000, 2, 3, 4, 5, 6)), + dt.datetime(2000, 2, 3, 4, 5, 6)), [[23.019261, 11.019261], [23.019261, 11.019261]], rtol=1e-4) @@ -660,16 +660,16 @@ def test_mlon2mlt_offset(): def test_mlon2mlt_range(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.mlon2mlt(range(0, 361, 30), - dt.datetime(2000, 2, 3, 4, 5, 6)), + dt.datetime(2000, 2, 3, 4, 5, 6)), [23.01963, 1.01963, 3.01963, 5.01963, 7.01963, 9.01963, 11.01963, 13.01963, 15.01963, 17.01963, 19.01963, 21.01963, 23.01963], rtol=1e-4) -###============================================================================ -### Test mlt2mlon() -###============================================================================ +# ============================================================================ +# Test mlt2mlon() +# ============================================================================ def test_mlt2mlon_scalar(): @@ -696,7 +696,7 @@ def test_mlt2mlon_1Darray(): def test_mlt2mlon_2Darray(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.mlt2mlon([[0, 12], [0, 12]], - dt.datetime(2000, 2, 3, 4, 5, 6)), + dt.datetime(2000, 2, 3, 4, 5, 6)), [[14.705551, 194.705551], [14.705551, 194.705551]], rtol=1e-4) @@ -719,16 +719,16 @@ def test_mlt2mlon_offset(): def test_mlt2mlon_range(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.mlt2mlon(range(0, 25, 2), - dt.datetime(2000, 2, 3, 4, 5, 6)), + dt.datetime(2000, 2, 3, 4, 5, 6)), [14.705551, 44.705551, 74.705551, 104.705551, 134.705551, 164.705551, 194.705551, 224.705551, 254.705551, 284.705551, 314.705551, 344.705551, 14.705551], rtol=1e-4) -###============================================================================ -### Test mlt/mlon back and forth -###============================================================================ +# ============================================================================ +# Test mlt/mlon back and forth +# ============================================================================ def test_mlon2mlt2mlon(): @@ -751,25 +751,25 @@ def test_mlt2mlon2mlt(): assert_allclose(apex_out.mlt2mlon(apex_out.mlon2mlt(360, date), date), 0) -###============================================================================ -### Test the map_to_height() method -###============================================================================ +# ============================================================================ +# Test the map_to_height() method +# ============================================================================ def test_map_to_height(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.map_to_height(60, 15, 100, 10000, conjugate=False, - precision=1e-10), + precision=1e-10), (31.841459274291992, 17.916629791259766, 0)) assert_allclose(apex_out.map_to_height(30, 170, 100, 500, conjugate=False, - precision=1e-2), + precision=1e-2), (25.727252960205078, 169.60546875, 0.00017655163537710905)) def test_map_to_height_same_height(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.map_to_height(60, 15, 100, 100, conjugate=False, - precision=1e-10), + precision=1e-10), (60, 15, 3.4150946248701075e-6), rtol=1e-5) @@ -790,13 +790,17 @@ def test_map_to_height_conjugate(): def test_map_to_height_vectorization(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.map_to_height([60, 60], 15, 100, 100), - ([60]*2, [15]*2, [3.4150946248701075e-6]*2), rtol=1e-5) + ([60] * 2, [15] * 2, [3.4150946248701075e-6] * 2), + rtol=1e-5) assert_allclose(apex_out.map_to_height(60, [15, 15], 100, 100), - ([60]*2, [15]*2, [3.4150946248701075e-6]*2), rtol=1e-5) + ([60] * 2, [15] * 2, [3.4150946248701075e-6] * 2), + rtol=1e-5) assert_allclose(apex_out.map_to_height(60, 15, [100, 100], 100), - ([60]*2, [15]*2, [3.4150946248701075e-6]*2), rtol=1e-5) + ([60] * 2, [15] * 2, [3.4150946248701075e-6] * 2), + rtol=1e-5) assert_allclose(apex_out.map_to_height(60, 15, 100, [100, 100]), - ([60]*2, [15]*2, [3.4150946248701075e-6]*2), rtol=1e-5) + ([60] * 2, [15] * 2, [3.4150946248701075e-6] * 2), + rtol=1e-5) def test_map_to_height_ApexHeightError(): @@ -805,9 +809,9 @@ def test_map_to_height_ApexHeightError(): apex_out.map_to_height(0, 15, 100, 10000) -###============================================================================ -### Test the map_E_to_height() method -###============================================================================ +# ============================================================================ +# Test the map_E_to_height() method +# ============================================================================ def test_map_E_to_height(): @@ -835,38 +839,39 @@ def test_map_E_to_height(): # vectorize lat assert_allclose(apex_out.map_E_to_height([60, 70], 15, 100, 500, - np.array([[1, 2, 3]]*2).T), + np.array([[1, 2, 3]] * 2).T), np.array([out_60_15_100_500, out_70_15_100_500]).T, rtol=1e-5) # vectorize lon assert_allclose(apex_out.map_E_to_height(60, [15, 30], 100, 500, - np.array([[1, 2, 3]]*2).T), + np.array([[1, 2, 3]] * 2).T), np.array([out_60_15_100_500, out_60_30_100_500]).T, rtol=1e-5) # vectorize height assert_allclose(apex_out.map_E_to_height(60, 15, [100, 200], 500, - np.array([[1, 2, 3]]*2).T), + np.array([[1, 2, 3]] * 2).T), np.array([out_60_15_100_500, out_60_15_200_500]).T, rtol=1e-5) # vectorize newheight assert_allclose(apex_out.map_E_to_height(60, 15, 100, [500, 1000], - np.array([[1, 2, 3]]*2).T), + np.array([[1, 2, 3]] * 2).T), np.array([out_60_15_100_500, out_60_15_100_1000]).T, rtol=1e-5) # vectorize E assert_allclose(apex_out.map_E_to_height(60, 15, 100, 500, - np.array([[1, 2, 3], [2, 3, 4]]).T), + np.array([[1, 2, 3], + [2, 3, 4]]).T), np.array([out_60_15_100_500, out_60_15_100_500_234]).T, rtol=1e-5) -###============================================================================ -### Test the map_V_to_height() method -###============================================================================ +# ============================================================================ +# Test the map_V_to_height() method +# ============================================================================ def test_map_V_to_height(): @@ -894,25 +899,25 @@ def test_map_V_to_height(): # vectorize lat assert_allclose(apex_out.map_V_to_height([60, 70], 15, 100, 500, - np.array([[1, 2, 3]]*2).T), + np.array([[1, 2, 3]] * 2).T), np.array([out_60_15_100_500, out_70_15_100_500]).T, rtol=1e-5) # vectorize lon assert_allclose(apex_out.map_V_to_height(60, [15, 30], 100, 500, - np.array([[1, 2, 3]]*2).T), + np.array([[1, 2, 3]] * 2).T), np.array([out_60_15_100_500, out_60_30_100_500]).T, rtol=1e-5) # vectorize height assert_allclose(apex_out.map_V_to_height(60, 15, [100, 200], 500, - np.array([[1, 2, 3]]*2).T), + np.array([[1, 2, 3]] * 2).T), np.array([out_60_15_100_500, out_60_15_200_500]).T, rtol=1e-5) # vectorize newheight assert_allclose(apex_out.map_V_to_height(60, 15, 100, [500, 1000], - np.array([[1, 2, 3]]*2).T), + np.array([[1, 2, 3]] * 2).T), np.array([out_60_15_100_500, out_60_15_100_1000]).T, rtol=1e-5) @@ -924,9 +929,9 @@ def test_map_V_to_height(): rtol=1e-5) -###============================================================================ -### Test basevectors_qd() -###============================================================================ +# ============================================================================ +# Test basevectors_qd() +# ============================================================================ # test coords @@ -952,8 +957,8 @@ def test_basevectors_qd_scalar_qd(): precision=1e-2), apex_out._basevec(glat, glon, 100)) -# test shapes and vectorization of arguments +# test shapes and vectorization of arguments def test_basevectors_qd_scalar_shape(): apex_out = Apex(date=2000, refh=300) ret = apex_out.basevectors_qd(60, 15, 100) @@ -987,9 +992,9 @@ def test_basevectors_qd_array(): assert_allclose(f2[:, 1], f2_lat30) -###============================================================================ -### Test basevectors_apex() -###============================================================================ +# ============================================================================ +# Test basevectors_apex() +# ============================================================================ # test against return from _geo2apexall for different coords @@ -1235,9 +1240,9 @@ def test_basevectors_apex_invalid_scalar(): assert_allclose(bvec, invalid) -###============================================================================ -### Test the get_apex() method -###============================================================================ +# ============================================================================ +# Test the get_apex() method +# ============================================================================ def test_get_apex(): @@ -1259,9 +1264,9 @@ def test_get_apex_invalid_lat(): rtol=0, atol=1e-8) -###============================================================================ -### Test the set_epoch() method -###============================================================================ +# ============================================================================ +# Test the set_epoch() method +# ============================================================================ def test_set_epoch(): @@ -1285,9 +1290,9 @@ def test_set_epoch(): assert_allclose(ret_2000_8_py, ret_2000_8_apex) -###============================================================================ -### Test the set_refh() method -###============================================================================ +# ============================================================================ +# Test the set_refh() method +# ============================================================================ def test_set_refh(): @@ -1302,49 +1307,48 @@ def test_set_refh(): assert_allclose(ret_500, fa.apxg2all(60, 15, 100, 500, 0)[2:4]) -###============================================================================ -### Test the get_babs() method -###============================================================================ +# ============================================================================ +# Test the get_babs() method +# ============================================================================ def test_get_babs(): - inputs = [[[80],[100],[300]],[range(50,90,8),range(0,360,80),[300]*5], - [90.0,0,1000]] + inputs = [[[80], [100], [300]], [range(50, 90, 8), range(0, 360, 80), + [300] * 5], [90.0, 0, 1000]] temp1 = np.array([4.22045410e-05, 5.15672743e-05, 4.98150200e-05, 5.06769359e-05, 4.91028428e-05]) expected = [[5.1303124427795412e-05], temp1, [3.793962299823761e-05]] - A = Apex(date=2018.1, refh=0) + apex_out = Apex(date=2018.1, refh=0) for i in range(len(inputs)): - outputs = A.get_babs(*inputs[i]) - if isinstance(outputs,np.float64): + outputs = apex_out.get_babs(*inputs[i]) + if isinstance(outputs, np.float64): outputs = [outputs] - for j,output in enumerate(outputs): + for j, output in enumerate(outputs): assert_allclose(output, expected[i][j], rtol=0, atol=1e-5) -###============================================================================ -### Test the bvectors_apex() method -###============================================================================ +# ============================================================================ +# Test the bvectors_apex() method +# ============================================================================ def test_bvectors_apex(): - inputs = [[80,81],[100,120],[100,200]] + inputs = [[80, 81], [100, 120], [100, 200]] expected = (np.array([5.94623305e-05, 5.95450722e-05]), - np.array([[ 0.02008877, 0.00303204], - [ 0.03571109, 0.03377986], + np.array([[0.02008877, 0.00303204], + [0.03571109, 0.03377986], [-0.94045794, -0.89848483]]), - np.array([ 5.26919505e-05, 4.81377429e-05]), - np.array([[ 0.02266997, 0.00375055], - [ 0.04029961, 0.04178477], - [-1.0612973 , -1.1114012 ]]) - ) + np.array([5.26919505e-05, 4.81377429e-05]), + np.array([[0.02266997, 0.00375055], + [0.04029961, 0.04178477], + [-1.0612973, -1.1114012]])) apex_out = Apex(date=2018.1, refh=0) outputs = apex_out.bvectors_apex(*inputs, coords='geo', precision=1e-10) - for i,output in enumerate(outputs): + for i, output in enumerate(outputs): for j in range(output.size): assert_allclose(output.ravel()[j], expected[i].ravel()[j], rtol=0, atol=1e-5) From de7e1382d3460fd1444181fdf7e05b474015f307 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 16:55:58 -0500 Subject: [PATCH 091/226] STY: made PEP8 changes Made the PEP8 changes needed to pass flake8. --- tests/test_cmd.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 39ea1f22..6a63ffd0 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -10,12 +10,14 @@ os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) outfile = 'tests/output.txt' + def setup_function(function): try: os.remove(outfile) - except: + except OSError: pass + teardown_function = setup_function @@ -108,7 +110,9 @@ def test_convert_stdin_stdout(): def test_convert_refh(): - pipe = subprocess.Popen('echo 60 15 | apexpy geo apex 2000 --height 100 --refh=300', shell=True, stdout=subprocess.PIPE) + pipe = subprocess.Popen( + 'echo 60 15 | apexpy geo apex 2000 --height 100 --refh=300', + shell=True, stdout=subprocess.PIPE) stdout, _ = pipe.communicate() pipe.wait() np.testing.assert_allclose(np.array(stdout.split(b' '), dtype=float), From d6576dfa656332afd057125597291580a1933660 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 16:57:50 -0500 Subject: [PATCH 092/226] STY: made PEP8 updates Updated style to pass flake8. --- tests/test_fortranapex.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/test_fortranapex.py b/tests/test_fortranapex.py index d9c5ba04..b97cb812 100644 --- a/tests/test_fortranapex.py +++ b/tests/test_fortranapex.py @@ -1,18 +1,17 @@ # -*- coding: utf-8 -*- +from numpy.testing import assert_allclose import os - import pytest -from numpy.testing import assert_allclose import apexpy from apexpy import fortranapex as fa -############################################################################## -# NOTE: the test results (numbers) were obtained by running the code that is # -# tested, therefore the tests below only check that nothing changes when # -# refactoring etc., and not if the results are actually correct # -############################################################################## +# ---------------------------------------------------------------------------- +# NOTE: the test results (numbers) were obtained by running the code that is +# tested, therefore the tests below only check that nothing changes when +# refactoring etc., and not if the results are actually correct +# ---------------------------------------------------------------------------- def test_apxg2q(): @@ -29,8 +28,10 @@ def test_apxg2q(): def test_apxg2all(): - fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), 2000) - qlat, qlon, mlat, mlon, f1, f2, f, d1, d2, d3, d, e1, e2, e3 = fa.apxg2all(60, 15, 100, 300, 1) + fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), + 2000) + qlat, qlon, mlat, mlon, f1, f2, f, d1, d2, d3, d, e1, e2, e3 = fa.apxg2all( + 60, 15, 100, 300, 1) assert_allclose(qlat, 56.531288146972656) assert_allclose(qlon, 94.1068344116211) assert_allclose(mlat, 55.94841766357422) @@ -70,7 +71,8 @@ def test_g2q2d(): def test_apxq2g_lowprecision(): - fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), 2000) + fa.loadapxsh(os.path.join(os.path.dirname(apexpy.__file__), 'apexsh.dat'), + 2000) glat, glon, error = fa.apxq2g(60, 15, 100, -1) assert_allclose(glat, 51.00891876220703) assert_allclose(glon, -66.11973571777344) From 9b5384549ea6127a4e6a1f64d620be6144645e09 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 17:01:30 -0500 Subject: [PATCH 093/226] STY: updated comments and line length Updated the comment style and line lengths to comply with new flake8 standards. --- tests/test_helpers.py | 58 ++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 68f0b72d..575c1644 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -11,17 +11,17 @@ from apexpy import helpers -############################################################################## -# NOTE: whenever function outputs are tested against hard-coded numbers, # -# the test results (numbers) were obtained by running the code that is # -# tested. Therefore these tests below only check that nothing changes when # -# refactoring etc., and not if the results are actually correct # -############################################################################## +# ---------------------------------------------------------------------------- +# NOTE: whenever function outputs are tested against hard-coded numbers, the +# test results (numbers) were obtained by running the code that is tested. +# Therefore these tests below only check that nothing changes when refactoring, +# etc., and not if the results are actually correct +# ---------------------------------------------------------------------------- -###============================================================================ -### Test checklat -###============================================================================ +# ============================================================================ +# Test checklat +# ============================================================================ def test_checklat_scalar(): assert helpers.checklat(90) == 90 @@ -64,9 +64,9 @@ def test_checklat_array(): helpers.checklat([-90-1e-5, -90, 0, 90, 90+1e-4]) -###============================================================================ -### Test getsinIm -###============================================================================ +# ============================================================================ +# Test getsinIm +# ============================================================================ def test_getsinIm_scalar(): assert_allclose(helpers.getsinIm(60), 0.96076892283052284) @@ -85,9 +85,9 @@ def test_getsinIm_2Darray(): [0.96076892283052284, 0.33257924500670238]]) -###============================================================================ -### Test getcosIm -###============================================================================ +# ============================================================================ +# Test getcosIm +# ============================================================================ def test_getcosIm_scalar(): @@ -107,9 +107,9 @@ def test_getcosIm_2Darray(): [0.27735009811261463, 0.94307531289434765]]) -###============================================================================ -### Test toYearFraction -###============================================================================ +# ============================================================================ +# Test toYearFraction +# ============================================================================ def test_toYearFraction(): @@ -124,9 +124,9 @@ def test_toYearFraction(): 2005.943624682902) -###============================================================================ -### Test gc2gdlat -###============================================================================ +# ============================================================================ +# Test gc2gdlat +# ============================================================================ def test_gc2gdlat(): @@ -136,9 +136,9 @@ def test_gc2gdlat(): assert_allclose(helpers.gc2gdlat(60), 60.166364190170931) -###============================================================================ -### Test subsol -###============================================================================ +# ============================================================================ +# Test subsol +# ============================================================================ def test_subsol(): assert_allclose(helpers.subsol(dt.datetime(2005, 2, 3, 4, 5, 6)), @@ -157,7 +157,12 @@ def test_subsol(): def datetime64_to_datetime(dt64): - """Convert a numpy.datetime64 to datetime.datetime, works outside 32 bit int seconds range of 1970 + """Convert numpy datetime64 object to a datetime datetime object + + Notes + ----- + Works outside 32 bit int second range of 1970 + """ year_floor = dt64.astype('datetime64[Y]') month_floor = dt64.astype('datetime64[M]') @@ -177,7 +182,8 @@ def test_subsol_array(): using single dt.datetime values """ - dates = np.arange(np.datetime64("1601"), np.datetime64("2100"), np.timedelta64(100, 'D')).astype('datetime64[s]') + dates = np.arange(np.datetime64("1601"), np.datetime64("2100"), + np.timedelta64(100, 'D')).astype('datetime64[s]') sslat, sslon = helpers.subsol(dates) for i, date in enumerate(dates): datetime = datetime64_to_datetime(date) From 557a81eea93040eb52d4e896e40e74ebcb7617c9 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 17:05:10 -0500 Subject: [PATCH 094/226] TST: moved pytest config Moved all of the pytest configuration to the setup.cfg file. --- pytest.ini | 4 ---- setup.cfg | 23 ++++++++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) delete mode 100644 pytest.ini diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 77a53936..00000000 --- a/pytest.ini +++ /dev/null @@ -1,4 +0,0 @@ -[pytest] -log_cli=true -log_level=NOTSET -addopts = -vv --ignore=src --ignore=ci --ignore=docs --doctest-modules --doctest-glob='*.rst' \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 49cd112b..162c488d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,6 +30,8 @@ max-line-length = 80 exclude = */migrations/*,*/south_migrations/* [pytest] +log_cli=true +log_level=NOTSET norecursedirs = .git .tox @@ -43,15 +45,18 @@ python_files = *_test.py tests.py addopts = - -rxEfsw - --strict - --ignore=docs/conf.py - --ignore=setup.py - --ignore=ci - --ignore=.eggs - --doctest-modules - --doctest-glob=\*.rst - --tb=short + -vv + --ignore=src + --ignore=ci + --ignore=docs + --doctest-modules + --doctest-glob='*.rst' + -rxEfsw + --strict + --ignore=docs/conf.py + --ignore=setup.py + --ignore=.eggs + --tb=short flake8-ignore = *.py W503 docs/conf.py ALL From 17002f5bdc4ae10275f0ab084340d6812ff4ad4f Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 17:07:58 -0500 Subject: [PATCH 095/226] MAINT: removed unused testing tool Removed old CI configuration tool, Landscape. --- .landscape.yaml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .landscape.yaml diff --git a/.landscape.yaml b/.landscape.yaml deleted file mode 100644 index 3129377d..00000000 --- a/.landscape.yaml +++ /dev/null @@ -1,7 +0,0 @@ -max-line-length: 140 -doc-warnings: yes -ignore-paths: - - docs - - ci - - tests - - setup.py \ No newline at end of file From 3b054ffb0c6bb4c1f7932c757a850a1e4439ae08 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 8 Feb 2021 17:08:21 -0500 Subject: [PATCH 096/226] MAINT: updated manifest Updated manifest, removing inclusion of removed files. --- MANIFEST.in | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 09a2cfa4..54c446b5 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,11 +3,6 @@ graft src graft ci graft tests -include .bumpversion.cfg -include .coveragerc -include .cookiecutterrc -include pytest.ini - include AUTHORS.rst include CHANGELOG.rst include CONTRIBUTING.rst @@ -15,6 +10,6 @@ include LICENSE include README.rst include CODE_OF_CONDUCT.md -include tox.ini .travis.yml appveyor.yml .codeclimate.yml .landscape.yaml +include tox.ini .travis.yml appveyor.yml .codeclimate.yml global-exclude *.py[cod~] __pycache__ *.so *.dylib From dadafb8c6ed9c0d5d45e7d7bac2d3e55fc2dbe2e Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 9 Feb 2021 08:07:04 -0500 Subject: [PATCH 097/226] TST: updated pytest config Updated pytest configuration section header. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 162c488d..b3f75ce4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,7 +29,7 @@ omit = *migrations* max-line-length = 80 exclude = */migrations/*,*/south_migrations/* -[pytest] +[tool:pytest] log_cli=true log_level=NOTSET norecursedirs = From 9cba872b3b3a5611a221e6abdc0ad0b77d6476da Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 9 Feb 2021 08:14:54 -0500 Subject: [PATCH 098/226] DOC: updated issue templates Updated existing issue templates to add titles and labels. --- .github/ISSUE_TEMPLATE/bug_report.md | 8 ++++---- .github/ISSUE_TEMPLATE/question.md | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index ab87bce1..07969566 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,8 +1,8 @@ --- name: Bug report about: Create a report to help us improve -title: '' -labels: '' +title: 'BUG: ' +labels: 'bug' assignees: '' --- @@ -25,8 +25,8 @@ If applicable, add screenshots to help explain your problem. **Computer (please complete the following information):** - OS: [e.g. iOS] - - Version [e.g. 22] -- Python version [e.g. 3.6] + - Python version [e.g. 3.6] + - Compiler and version [e.g. ifort 22] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 244d9314..4e19ac62 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -1,8 +1,8 @@ --- name: Question about: Ask a question (and help us improve our documentation) -title: '' -labels: '' +title: 'Question: ' +labels: 'question' assignees: '' --- @@ -25,8 +25,8 @@ If applicable, add screenshots to help explain your question. **Desktop (please complete the following information):** - OS: [e.g. iOS] - - Version [e.g. 22] - Python version [e.g. 3.6] + - Compiler and version [e.g. gfortran 22] **Additional context** Add any other context about the problem here. From 2cc8e38dadbd5657c370543a4538e499aacedff7 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 9 Feb 2021 08:15:19 -0500 Subject: [PATCH 099/226] DOC: added more templates Added a pull request and enhancement template. --- .github/ISSUE_TEMPLATE/feature_request.md | 26 +++++++++++++ .github/pull_request_template.md | 47 +++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/pull_request_template.md diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..685ddb34 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,26 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: 'ENH: ' +labels: 'enhancement' +assignees: '' +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always +frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. Keep the scope as +narrow as possible, to make it easier to implement + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've +considered. + +**Additional context** +Add any other context or screenshots about the feature request here. + +**Reminders** +This is a volunteer-driven project. Code contributions are welcome, as is help +testing new code. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..682270ca --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,47 @@ +# Description + +Please include a summary of the change and which issue is fixed. Please also +include relevant motivation and context. List any dependencies that are required +for this change. Please see ``CONTRIBUTING.rst`` for more guidelines. + +Fixes # (issue) + +## Type of change + +Please delete options that are not relevant. + +- Bug fix (non-breaking change which fixes an issue) +- New feature (non-breaking change which adds functionality) +- Breaking change (fix or feature that would cause existing functionality + to not work as expected) +- This change requires a documentation update +- Release candidate + +# How Has This Been Tested? + +Please describe the tests that you ran to verify your changes. Provide +instructions so we can reproduce. Please also list any relevant details for +your test configuration + +- Test A +- Test B + +**Test Configuration**: +* Operating system: (e.g., Hal) +* Python version number: (e.g., 3.7) +* Compiler with version number: (e.g. gfortran 9.0) +* Any details about your local setup that are relevant + +# Checklist: + +- [ ] Make sure you are merging into the ``develop`` (not ``main``) branch +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] Any dependent changes have been merged and published in downstream modules +- [ ] Add a note to ``Changelog.rst``, summarising the changes +- [ ] Add yourself to ``AUTHORS.rst`` and ``.zenodo.json`` \ No newline at end of file From 91749b630807a352027a4622f91b059e19aacf3c Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 9 Feb 2021 09:32:12 -0500 Subject: [PATCH 100/226] BUG: updated exception type Updated the expected exception failure. --- tests/test_Apex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index cf60c924..14c839e9 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -157,7 +157,7 @@ def test__geo2apexall_array(): assert_allclose(ret[i].astype(float), np.array([[ret1[i], ret2[i]], [ret3[i], ret4[i]]], dtype=float)) - except TypeError: + except ValueError: # ret[i] is array of arrays assert_allclose(ret[i][0, 0], ret1[i]) assert_allclose(ret[i][0, 1], ret2[i]) From 3fa5b561d1ae962727dcaedb848db1dfe4cb1a46 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 9 Feb 2021 09:32:48 -0500 Subject: [PATCH 101/226] MAINT: updated docstrings Updated docstrings to remove warnings raised by sphinx. --- src/apexpy/apex.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 2da8663e..047605c5 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -986,9 +986,9 @@ def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): Parameters ---------- - lat, lon : (N,) array_like or float - Latitude lat : (N,) array_like or float + Latitude + lon : (N,) array_like or float Longitude height : (N,) array_like or float Altitude in km @@ -1012,8 +1012,8 @@ def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): Bd3: (1, N) or (1,) ndarray d3 : (3, N) or (3,) ndarray - Note - ---- + Notes + ----- Be3 is not equivalent to the magnitude of the IGRF magnitude, but is instead equal to the IGRF magnitude divided by a scaling factor, D. Similarly, Bd3 is the IGRF magnitude multiplied by D. @@ -1022,15 +1022,8 @@ def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): References ---------- - - .. [4] Richmond, A. D. (1995), Ionospheric Electrodynamics Using - Magnetic Apex Coordinates, Journal of geomagnetism and - geoelectricity, 47(2), 191–212, :doi:`10.5636/jgg.47.191`. - - .. [5] Emmert, J. T., A. D. Richmond, and D. P. Drob (2010), - A computationally compact representation of Magnetic-Apex - and Quasi-Dipole coordinates with smooth base vectors, - J. Geophys. Res., 115(A8), A08322, :doi:`10.1029/2010JA015326`. + Richmond, A. D. (1995) [4]_ + Emmert, J. T. et al. (2010) [5]_ """ glat, glon = self.convert(lat, lon, coords, 'geo', height=height, From 21c9b3735e6a920389f1727a9f0ece5d415084a8 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 9 Feb 2021 09:33:07 -0500 Subject: [PATCH 102/226] ENH: added docs makefile Added a makefile for the documentation. --- docs/Makefile | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 docs/Makefile diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..28012895 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = src/apexpy +SOURCEDIR = . +BUILDDIR = .build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) From cf296c04949386a5ef8266d337932c2bacde8c54 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 9 Feb 2021 09:33:52 -0500 Subject: [PATCH 103/226] ENH: added maintenance instructions Added instructions for updating the coefficients to the documentation. --- docs/maintenance.rst | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 docs/maintenance.rst diff --git a/docs/maintenance.rst b/docs/maintenance.rst new file mode 100644 index 00000000..85c081d0 --- /dev/null +++ b/docs/maintenance.rst @@ -0,0 +1,36 @@ +Package Maintenance +=================== + +Updating IGRF +------------- + +The `International Geomagnetic Reference Field `_ +is regularly updated to reflect the most recent changes to the Terrestrial +magnetic field. apexpy currently uses IRGF-13 coefficients, which are provided +in the ``apexpy/src/fortranapex/igrf13coeff.txt`` file. To change or update the +magnetic field coefficients used by apexpy, you need to update the fortran code. +Assuming your new coefficient file has the same format, the process is simple: + +1. Clone the repository or your fork of the repository (see :ref:`contributing`) +2. Update ``apexpy/src/fortranapex/magfld.f`` variable ``FILENAME`` by setting + it equal to the new target filename +3. Optional: Test the fortran compilation by running the test executable to + ensure that the new coefficients are used and deviate an acceptable amount + from prior coefficients:: + + make + apextest + make clean + +4. Install the python package from the command line + (see :ref:`installation-cmd`) + +Updating tests and style standards +----------------------------------- + +apexpy is in the process of updating unit and integration tests to reduce code +duplication and implementing cleaner style standards. Additionally, some parts +of the fortran code adhere to older coding standards and raise warnings when +compiled with newer compilers. If you would like to assist in these efforts +(help would be appreciated), please discuss your potential contribution with +the current maintainer to ensure a minimal duplication of effort. From 1a869cf7c5971183b8f37615385bec70d358b0c6 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 9 Feb 2021 09:34:15 -0500 Subject: [PATCH 104/226] ENH: added more installation options Added more types of installation instructions to the docs. --- docs/installation.rst | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 313f2f13..73cb339d 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1,19 +1,30 @@ -============ +.. _installation: + 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 +`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 +When you have NumPy, you may use either PyPi or GitHub to install this package. + + +.. _installation-pip: + +PyPi +---- +This is the most straigforward option! From the command line use ``pip`` [1]_:: pip install apexpy -This assumes that the same version of libgfortran is installed in the same location as when the pip wheel was built. If not, you may have trouble importing apexpy and you will have to build apexpy yourself using: +This assumes that the same version of libgfortran is installed in the same +location as when the pip wheel was built. If not, you may have trouble +importing apexpy and you will have to build apexpy yourself using: pip install --global-option='build_ext' apexpy @@ -24,6 +35,20 @@ The package has been tested with the following setups (others might work, too): * Windows (32/64 bit Python), Linux (64 bit), and Mac (64 bit) * Python 2.7, 3.6, 3.7, 3.8, 3.9 + +.. _installation-cmd: + +GitHub +------ +If you are indending on modifying or contributing to apexpy, it's easier to +install apexpy by forking the repository and installing it locally or within +a virtual environment. After clonining the fork (see :ref:`contributing`), +you may install by:: + + cd apexpy + python setup.py --user + + .. [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 `_. From 204c9e613a3f59de5514cad8416e135726551a0d Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 9 Feb 2021 09:34:36 -0500 Subject: [PATCH 105/226] DOC: updated index Updated the documentation index to include the new file. --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index 40f35b5e..6ed104f3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,6 +10,7 @@ Contents usage reference/index contributing + maintenance authors changelog From 2ce0bf4711cfc1ffcca46eb9479d7d3f9fb7792e Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 9 Feb 2021 09:34:56 -0500 Subject: [PATCH 106/226] ENH: added label to contributing Added a reference label to the contributing documentation. --- docs/contributing.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/contributing.rst b/docs/contributing.rst index e582053e..d1310bed 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -1 +1,4 @@ +.. _contributing: + .. include:: ../CONTRIBUTING.rst + From ac64a3cc7b192f4bb2f7717bad1d5ebb76b0ff55 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 9 Feb 2021 09:35:35 -0500 Subject: [PATCH 107/226] STY: updated contributing style Updated the style of the document and added a subsection with style guidelines. --- CONTRIBUTING.rst | 78 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index bc71d7c5..2445c6d9 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,71 +1,107 @@ -============ 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 * Detailed steps to reproduce the bug 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 `apexpy` for local development: -1. `Fork apexpy on GitHub `_. +1. `Fork apexpy on GitHub `_. 2. Clone your fork locally:: git clone git@github.com:your_name_here/apexpy.git -3. Create a branch for local development:: +3. Create a branch for local development based off of the ``develop`` branch:: - git checkout -b name-of-your-bugfix-or-feature + git checkout -b name-of-your-bugfix-or-feature origin/develop - Now you can make your changes locally. Add tests for bugs and new features in the relevant test file in the ``tests`` directory. 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 the relevant test file in the ``tests`` directory. The tests are run with + ``pytest`` and can be written as normal functions (starting with ``test_``) + containing a standard ``assert`` statement for testing output. -4. When you're done making changes, run ``py.test`` locally if you can:: +4. When you're done making changes, run ``pytest`` locally if you can:: - py.test + python -m pytest 5. Commit your changes and push your branch to GitHub:: git add . - git commit -m "Brief description of your changes" + git commit -m "ACRONYM: 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. The continuous integration (CI) testing servers will automatically test the whole codebase, including your changes, for multiple versions of Python on both Windows and Linux. + The project now uses the `NumPy acronyms `_ + for development workflow in the commit messages. + +6. Submit a pull request through the GitHub website. Pull requests should be + made to the ``develop`` branch. The continuous integration (CI) testing + servers will automatically test the whole codebase, including your changes, + for multiple versions of Python on both Windows and Linux. 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: 1. Include passing tests for your changes 2. Update/add documentation if relevant 3. Add a note to ``CHANGELOG.rst`` about the changes -4. Add yourself to ``AUTHORS.rst`` +4. Add yourself to ``AUTHORS.rst`` and ``.zenodo.json`` with your + `ORCiD `_ + +Style Guidelines +^^^^^^^^^^^^^^^^ + +In general, apexpy follows PEP8 and numpydoc guidelines. PyTest is used to run +the unit and integration tests, flake8 checks for style, and sphinx-build +performs documentation tests. However, there are certain additional style +elements that have been settled on to ensure the project maintains a consistent +coding style: + +* Line breaks should occur before a binary operator (ignoring flake8 W503) +* Preferably break long lines on open parentheses instead of using backslashes +* Use no more than 80 characters per line +* Several dependent packages have common nicknames, including: + * `import datetime as dt` + * `import numpy as np` +* Provide tests with informative failure statements and descriptive, one-line + docstrings. + +apexpy is working on modernizing its code style to adhere to these guidelines. From 1c0b96a34024fcae23a6dc5bea58fde2854bf45b Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 9 Feb 2021 09:35:59 -0500 Subject: [PATCH 108/226] DOC: updated changelog Added a summary of this PR to the changelog. --- CHANGELOG.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 51f052f2..d5194349 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,13 +2,14 @@ Changelog ========= -1.2.0 (2021-03-XX) +1.1.0 (2021-03-XX) ------------------ * Improved the subsol routine to allow array input * Improved PEP8 compliance * Added some missing docstrings to unit tests * Fixed AppVeyor test environment * Updated python test versions +* Updated community and package documentation 1.0.4 (2019-04-05) ---------------------------------------- From ee9a57c3c664d84bea8177cdb95305e321656e06 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 9 Feb 2021 10:30:09 -0500 Subject: [PATCH 109/226] STY: made PEP8 updates Removed trailing whitespace, fixed line length, and improved a docstring description. --- src/apexpy/apex.py | 2 +- src/apexpy/helpers.py | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 047605c5..7388120e 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -1022,7 +1022,7 @@ def bvectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): References ---------- - Richmond, A. D. (1995) [4]_ + Richmond, A. D. (1995) [4]_ Emmert, J. T. et al. (2010) [5]_ """ diff --git a/src/apexpy/helpers.py b/src/apexpy/helpers.py index ac2ead9d..bebe837d 100644 --- a/src/apexpy/helpers.py +++ b/src/apexpy/helpers.py @@ -148,6 +148,7 @@ def subsol(datetime): Parameters ---------- datetime : :class:`datetime.datetime` or :class:`numpy.ndarray[datetime64]` + Date and time in UTC (naive objects are treated as UTC) Returns ------- @@ -171,7 +172,7 @@ def subsol(datetime): by K. Laundal. """ - # convert to year, day of year and seconds since midnight + # Convert to year, day of year and seconds since midnight if isinstance(datetime, dt.datetime): year = np.asanyarray([datetime.year]) doy = np.asanyarray([datetime.timetuple().tm_yday]) @@ -203,21 +204,30 @@ def subsol(datetime): l0 = -79.549 + (-0.238699 * (yr - 4.0 * nleap) + 3.08514e-2 * nleap) g0 = -2.472 + (-0.2558905 * (yr - 4.0 * nleap) - 3.79617e-2 * nleap) + # Days (including fraction) since 12 UT on January 1 of IYR: df = (ut / 86400.0 - 1.5) + doy + # Mean longitude of Sun: lmean = l0 + 0.9856474 * df + # Mean anomaly in radians: grad = np.radians(g0 + 0.9856003 * df) + # Ecliptic longitude: - lmrad = np.radians(lmean + 1.915 * np.sin(grad) + 0.020 * np.sin(2.0 * grad)) + lmrad = np.radians(lmean + 1.915 * np.sin(grad) + + 0.020 * np.sin(2.0 * grad)) sinlm = np.sin(lmrad) + # Obliquity of ecliptic in radians: epsrad = np.radians(23.439 - 4e-7 * (df + 365 * yr + nleap)) + # Right ascension: alpha = np.degrees(np.arctan2(np.cos(epsrad) * sinlm, np.cos(lmrad))) + # Declination, which is also the subsolar latitude: sslat = np.degrees(np.arcsin(np.sin(epsrad) * sinlm)) + # Equation of time (degrees): etdeg = lmean - alpha nrot = np.round(etdeg / 360.0) From 12ba06a678857c0744efe232c248d7ef5364fadb Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 10 Feb 2021 16:52:23 -0500 Subject: [PATCH 110/226] STY: moved setup metadata Moved setup metadata to setup.cfg. --- setup.cfg | 58 +++++++++++++++++++++++++++++++++++++++++ setup.py | 78 +++---------------------------------------------------- 2 files changed, 61 insertions(+), 75 deletions(-) diff --git a/setup.cfg b/setup.cfg index b3f75ce4..8bdfb699 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,61 @@ +[metadata] +name = apexpy +version = 1.0.4 +license = MIT +description = "A Python wrapper for Apex coordinates" +long_description = file: README.rst, CHANGELOG.rst +author = Crister van der Meeren, et al. +author_email = angeline.burrell@nrll.navy.mil +url = https://github.com/aburrell/apexpy +keywords = + apex + modified apex + quasi-dipole + quasi dipole + coordinates + magnetic coordinates + mlt + magnetic local time + conversion + converting +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Science/Research + License :: OSI Approved :: MIT License + Operating System :: Unix + Operating System :: POSIX + Operating System :: Microsoft :: Windows + Operating System :: MacOS :: MacOS X + Programming Language :: Python + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: Implementation :: CPython + Topic :: Scientific/Engineering :: Physics + Topic :: Utilities + +[options] +zip_safe = False +packages = find: +package_dir = + =src +install_requires = numpy +include_package_data = True +include_entry_points = True + +[options.entry_points] +console_scripts = + apexpy = apexpy.__main__:main + +[options.packages.find] +where = src + +[options.package_data] +apexpy = apexsh.dat + [aliases] release = register clean --all sdist diff --git a/setup.py b/setup.py index d6ade3f1..e4c97fe9 100644 --- a/setup.py +++ b/setup.py @@ -2,21 +2,15 @@ # -*- encoding: utf-8 -*- from __future__ import absolute_import -import io -import re from glob import glob from os import path, environ - -from setuptools import find_packages +from setuptools import setup # Include extensions only when not on readthedocs.org if environ.get('READTHEDOCS', None) == 'True': - from setuptools import setup - from distutils.core import Extension extensions = [] else: from numpy.distutils.core import setup, Extension - extensions = [ Extension(name='apexpy.fortranapex', sources=['src/fortranapex/magfld.f', 'src/fortranapex/apex.f', @@ -26,71 +20,5 @@ 'src/fortranapex/checkapexsh.f90', 'src/fortranapex/fortranapex.pyf'])] -def read(*names, **kwargs): - return io.open( - path.join(path.dirname(__file__), *names), - encoding=kwargs.get('encoding', 'utf8') - ).read() - - -if __name__ == "__main__": - setup( - name='apexpy', - version='1.0.4', - license='MIT', - description='A Python wrapper for Apex coordinates', - long_description='%s\n%s' % (read('README.rst'), - re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', - read('CHANGELOG.rst'))), - author='Christer van der Meeren; Angeline G. Burrell', - author_email='angeline.burrell@nrl.navy.mil', - url='https://github.com/aburrell/apexpy', - packages=find_packages('src'), - package_dir={'': 'src'}, - py_modules=[path.splitext(path.basename(pp))[0] - for pp in glob('src/*.py')], - package_data={'apexpy': ['apexsh.dat']}, - zip_safe=False, - 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', - 'Operating System :: Unix', - 'Operating System :: POSIX', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: MacOS :: MacOS X', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: Implementation :: CPython', - 'Topic :: Scientific/Engineering :: Physics', - 'Topic :: Utilities', - ], - keywords=[ - 'apex', - 'modified apex', - 'quasi-dipole', - 'quasi dipole', - 'coordinates', - 'magnetic coordinates', - 'mlt', - 'magnetic local time', - 'conversion', - 'converting', - ], - install_requires=[ - 'numpy', - ], - ext_modules=extensions, - entry_points={ - 'console_scripts': [ - 'apexpy = apexpy.__main__:main', - ] - }, - ) +setup(py_modules=[path.splitext(path.basename(pp))[0] + for pp in glob('src/*.py')], ext_modules=extensions) From 39252fcc560620fe996c011612040dc64e76839a Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 10 Feb 2021 16:52:58 -0500 Subject: [PATCH 111/226] ENH: added project.toml Added a build-system environment. --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..073b80eb --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["numpy", "setuptools", "wheel"] +build-backend = "setuptools.build_meta" From 8f717f9887bba4e36d645a64069138033a155cdd Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Thu, 11 Feb 2021 12:07:26 -0500 Subject: [PATCH 112/226] Create .zenodo.json --- .zenodo.json | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .zenodo.json diff --git a/.zenodo.json b/.zenodo.json new file mode 100644 index 00000000..57d10de6 --- /dev/null +++ b/.zenodo.json @@ -0,0 +1,34 @@ +{ + "license": { + "id": "MIT" + }, + "notes": "When referencing this package, please cite both the package DOI and the AACGM-v2 journal article: Shepherd, S. G. (2014), Altitude\u2010adjusted corrected geomagnetic coordinates: Definition and functional approximations, Journal of Geophysical Research: Space Physics, 119, 7501\u20137521, doi:10.1002/2014JA020264.", + "references": [ + "Emmert, J. T., A. D. Richmond, and D. P. Drob (2010), A computationally compact representation of Magnetic-Apex and Quasi-Dipole coordinates with smooth base vectors, J. Geophys. Res., 115(A8), A08322, doi:10.1029/2010JA015326.", + "Richmond, A. D. (1995), Ionospheric Electrodynamics Using Magnetic Apex Coordinates, Journal of geomagnetism and geoelectricity, 47(2), 191–212, doi:10.5636/jgg.47.191." + ], + "keywords": [ + "Magnetic Apex Coordinates", + "Quasi Dipole Coordinates", + "MLT", + "Magnetic Local Time", + "Conversion", + "Coordinate Conversion", + "Converting", + "Ionosphere", + "Magnetic Field", + "Space Physics", + "Heliophysics" + ], + "creators": [ + { + "orcid": "0000-0001-8875-9326", + "affiliation": "Naval Research Laboratory", + "name": "Angeline G. Burrell" + }, + { + "orcid": "0000-0002-8043-0953", + "name": "Christer van der Meeren" + } + ] +} From 3d8f069ead8e577cfedf28101cc9799530444bec Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Thu, 11 Feb 2021 12:11:09 -0500 Subject: [PATCH 113/226] Update .zenodo.json --- .zenodo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.zenodo.json b/.zenodo.json index 57d10de6..5c1b98a3 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -2,7 +2,7 @@ "license": { "id": "MIT" }, - "notes": "When referencing this package, please cite both the package DOI and the AACGM-v2 journal article: Shepherd, S. G. (2014), Altitude\u2010adjusted corrected geomagnetic coordinates: Definition and functional approximations, Journal of Geophysical Research: Space Physics, 119, 7501\u20137521, doi:10.1002/2014JA020264.", + "notes": "When referencing this package, please cite both the package DOI and the Apex Coordinates journal article: Emmert, J. T., A. D. Richmond, and D. P. Drob (2010), A computationally compact representation of Magnetic-Apex and Quasi-Dipole coordinates with smooth base vectors, J. Geophys. Res., 115(A8), A08322, doi:10.1029/2010JA015326.", "references": [ "Emmert, J. T., A. D. Richmond, and D. P. Drob (2010), A computationally compact representation of Magnetic-Apex and Quasi-Dipole coordinates with smooth base vectors, J. Geophys. Res., 115(A8), A08322, doi:10.1029/2010JA015326.", "Richmond, A. D. (1995), Ionospheric Electrodynamics Using Magnetic Apex Coordinates, Journal of geomagnetism and geoelectricity, 47(2), 191–212, doi:10.5636/jgg.47.191." From 62386296f3f1dbca727aa726453265a345cf0a57 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Thu, 11 Feb 2021 12:52:24 -0500 Subject: [PATCH 114/226] Update MANIFEST.in adding .zenodo.json --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 54c446b5..8565e669 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,6 +9,7 @@ include CONTRIBUTING.rst include LICENSE include README.rst include CODE_OF_CONDUCT.md +include .zenodo.json include tox.ini .travis.yml appveyor.yml .codeclimate.yml From 33f653adaa64810d03cc523d34489c5056f60901 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Thu, 11 Feb 2021 12:59:37 -0500 Subject: [PATCH 115/226] Update .zenodo.json --- .zenodo.json | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index 5c1b98a3..10f9794e 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -21,14 +21,30 @@ "Heliophysics" ], "creators": [ + { + "orcid": "0000-0002-8043-0953", + "name": "Christer van der Meeren" + }, + { + "orcid": "0000-0001-5028-4943", + "name": "Karl M. Laundal" + }, { "orcid": "0000-0001-8875-9326", "affiliation": "Naval Research Laboratory", "name": "Angeline G. Burrell" - }, + }, { - "orcid": "0000-0002-8043-0953", - "name": "Christer van der Meeren" + "orcid": "0000-0002-3487-3630", + "name": "Gregory Starr" + }, + { + "orcid": "0000-0002-4621-3453", + "name": "Ashton S. Reimer" + }, + { + "orcid": "0000-0003-4227-8687", + "name": "Joachim Morschhäuser" } ] } From 75a94416fed46bc58728ee50e8bbd26322537c57 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Thu, 11 Feb 2021 22:38:21 -0500 Subject: [PATCH 116/226] Update .zenodo.json --- .zenodo.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.zenodo.json b/.zenodo.json index 10f9794e..a0aaac4f 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -43,7 +43,6 @@ "name": "Ashton S. Reimer" }, { - "orcid": "0000-0003-4227-8687", "name": "Joachim Morschhäuser" } ] From 1c3336d66fd02777a7dcf735ee87fb42e7528a3f Mon Sep 17 00:00:00 2001 From: Achim Morschhauser <21514612+sputnik-a@users.noreply.github.com> Date: Fri, 12 Feb 2021 12:17:35 +0100 Subject: [PATCH 117/226] Update .zenodo.json Correct co-author information entry in .zenodo.json --- .zenodo.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.zenodo.json b/.zenodo.json index a0aaac4f..5bbd04b5 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -43,7 +43,9 @@ "name": "Ashton S. Reimer" }, { - "name": "Joachim Morschhäuser" + "orcid": "0000-0001-7955-4441", + "affiliation": "GFZ German Research Centre for Geosciences", + "name": "Achim Morschhauser" } ] } From 8e9c308995c941c43ec5dc6cbaabafa8183bdd1a Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Fri, 12 Feb 2021 09:28:50 -0500 Subject: [PATCH 118/226] Update apex.py remove documentation for the non-`return_all` case of `basevectors_apex` --- src/apexpy/apex.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 7388120e..4b396881 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -800,7 +800,6 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): Returns ------- - f1, f2 : (2, N) or (2,) ndarray f3, g1, g2, g3, d1, d2, d3, e1, e2, e3 : (3, N) or (3,) ndarray Note From b0d20ca56194332f7e6e1759d9cc1d8d6d850ddc Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Fri, 12 Feb 2021 10:26:39 -0500 Subject: [PATCH 119/226] Update apex.py adding a note to the `Apex` class to address #25 --- src/apexpy/apex.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 4b396881..bfd34f6e 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -57,6 +57,8 @@ class Apex(object): Notes ----- The calculations use IGRF-12 with coefficients from 1900 to 2020 [1]_. + + The geodetic reference ellipsoid is WGS84. References ---------- From c5be0ca475ab8573ab3961de527612932e9f4988 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Fri, 12 Feb 2021 12:18:05 -0500 Subject: [PATCH 120/226] Update apex.py removing whitespace to make apex.py pep8 compliant --- src/apexpy/apex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index bfd34f6e..cae8082f 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -57,7 +57,7 @@ class Apex(object): Notes ----- The calculations use IGRF-12 with coefficients from 1900 to 2020 [1]_. - + The geodetic reference ellipsoid is WGS84. References From a9a24c2cc4a65b80840f242d5c1c583867a4bcc4 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 15 Feb 2021 15:09:23 -0400 Subject: [PATCH 121/226] changing cofrm to accept a coefficients file and updating set_epoch to provide it --- src/apexpy/apex.py | 3 +- src/fortranapex/fortranapex.pyf | 140 ++++++++++++++++---------------- src/fortranapex/magfld.f | 8 +- 3 files changed, 77 insertions(+), 74 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index cae8082f..15c7cfd6 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -931,7 +931,8 @@ def set_epoch(self, year): # f2py self.year = np.float64(year) fa.loadapxsh(self.datafile, self.year) - fa.cofrm(self.year) + igrf_fn = os.path.join(os.path.dirname(__file__), '..', 'fortranapex', 'igrf13coeffs.txt') + fa.cofrm(self.year, igrf_fn) def set_refh(self, refh): """Updates the apex reference height for all subsequent conversions. diff --git a/src/fortranapex/fortranapex.pyf b/src/fortranapex/fortranapex.pyf index fa4a6bd0..f200f551 100644 --- a/src/fortranapex/fortranapex.pyf +++ b/src/fortranapex/fortranapex.pyf @@ -3,55 +3,7 @@ python module fortranapex ! in interface ! in :fortranapex - subroutine cofrm(date) ! in :fortranapex:magfld.f - real :: date - integer :: nmax - real dimension(255) :: gb - real dimension(225) :: gv - integer, optional :: ichg=-99999 - common /magcof/ nmax,gb,gv,ichg - end subroutine cofrm - subroutine dypol(colat,elon,vp) ! in :fortranapex:magfld.f - real :: colat - real :: elon - real :: vp - integer :: nmax - real dimension(255) :: gb - real dimension(225) :: gv - integer :: ichg - common /magcof/ nmax,gb,gv,ichg - end subroutine dypol - subroutine feldg(ienty,glat,glon,alt,bnrth,beast,bdown,babs) ! in :fortranapex:magfld.f - integer intent(in) :: ienty - real(4) intent(in) :: glat - real(4) intent(in) :: glon - real(4) intent(in) :: alt - real(4) intent(out) :: bnrth - real(4) intent(out) :: beast - real(4) intent(out) :: bdown - real(4) intent(out) :: babs - integer :: nmax - real dimension(255) :: gb - real dimension(225) :: gv - integer :: ichg - common /magcof/ nmax,gb,gv,ichg - end subroutine feldg - subroutine gd2cart(gdlat,glon,alt,x,y,z) ! in :fortranapex:magfld.f - real :: gdlat - real :: glon - real :: alt - real :: x - real :: y - real :: z - end subroutine gd2cart - subroutine convrt(i,gdlat,alt,x1,x2) ! in :fortranapex:magfld.f - integer :: i - real :: gdlat - real :: alt - real :: x1 - real :: x2 - end subroutine convrt - subroutine apex(date,dlat,dlon,alt,a,alat,alon,bmag,xmag,ymag,zmag,v) ! in :fortranapex:apex.f + subroutine apex(date,dlat,dlon,alt,a,alat,alon,bmag,xmag,ymag,zmag,v) ! in :fortranapex:.\apex.f real :: date real :: dlat real :: dlon @@ -71,7 +23,7 @@ python module fortranapex ! in real :: stp common /dipole/ colat,elon,vp,ctp,stp end subroutine apex - subroutine linapx(gdlat,glon,alt,a,alat,alon,xmag,ymag,zmag,f) ! in :fortranapex:apex.f + subroutine linapx(gdlat,glon,alt,a,alat,alon,xmag,ymag,zmag,f) ! in :fortranapex:.\apex.f real :: gdlat real :: glon real :: alt @@ -102,7 +54,7 @@ python module fortranapex ! in common /dipole/ colat,elon,vp,ctp,stp common /itra/ nstp,y,yp,sgn,ds end subroutine linapx - subroutine itrace(iapx) ! in :fortranapex:apex.f + subroutine itrace(iapx) ! in :fortranapex:.\apex.f integer :: iapx real dimension(3,3) :: yapx real :: bx @@ -118,7 +70,7 @@ python module fortranapex ! in common /fldcomd/ bx,by,bz,bb common /itra/ nstp,y,yold,sgn,ds end subroutine itrace - subroutine fndapx(alt,zmag,a,alat,alon) ! in :fortranapex:apex.f + subroutine fndapx(alt,zmag,a,alat,alon) ! in :fortranapex:.\apex.f real :: alt real :: zmag real :: a @@ -133,7 +85,7 @@ python module fortranapex ! in common /apxin/ yapx common /dipole/ colat,elon,vp,ctp,stp end subroutine fndapx - subroutine dipapx(gdlat,gdlon,alt,bnorth,beast,bdown,a,alon) ! in :fortranapex:apex.f + subroutine dipapx(gdlat,gdlon,alt,bnorth,beast,bdown,a,alon) ! in :fortranapex:.\apex.f real :: gdlat real :: gdlon real :: alt @@ -149,7 +101,7 @@ python module fortranapex ! in real :: stp common /dipole/ colat,elon,vp,ctp,stp end subroutine dipapx - function fint(x1,x2,x3,y1,y2,y3,xfit) ! in :fortranapex:apex.f + function fint(x1,x2,x3,y1,y2,y3,xfit) ! in :fortranapex:.\apex.f real :: x1 real :: x2 real :: x3 @@ -159,7 +111,7 @@ python module fortranapex ! in real :: xfit real :: fint end function fint - module apxshmodule ! in :fortranapex:apexsh.f90 + module apxshmodule ! in :fortranapex:.\apexsh.f90 integer(kind=4) :: nterm integer(kind=4) :: nmax integer(kind=4) :: mmax @@ -221,15 +173,15 @@ python module fortranapex ! in real(kind=4) :: altlastg logical, optional :: loadflag=.true. end module apxshmodule - subroutine loadapxsh(datafilenew,epochnew) ! in :fortranapex:apexsh.f90 + subroutine loadapxsh(datafilenew,epochnew) ! in :fortranapex:.\apexsh.f90 use apxshmodule - character*1000 :: datafilenew - real(kind=4) :: epochnew + character*1000 intent(in) :: datafilenew + real(kind=4) intent(in) :: epochnew end subroutine loadapxsh - subroutine allocatearrays ! in :fortranapex:apexsh.f90 + subroutine allocatearrays ! in :fortranapex:.\apexsh.f90 use apxshmodule end subroutine allocatearrays - subroutine apxg2q(glat,glon,alt,vecflagin,qlatout,qlonout,f1,f2,f) ! in :fortranapex:apexsh.f90 + subroutine apxg2q(glat,glon,alt,vecflagin,qlatout,qlonout,f1,f2,f) ! in :fortranapex:.\apexsh.f90 use apxshmodule real(kind=4) intent(in) :: glat real(kind=4) intent(in) :: glon @@ -241,7 +193,7 @@ python module fortranapex ! in real(kind=4) dimension(2),intent(out) :: f2 real(kind=4) intent(out) :: f end subroutine apxg2q - subroutine apxg2all(glat,glon,alt,hr,vecflagin,qlatout,qlonout,mlat,mlon,f1,f2,f,d1,d2,d3,d,e1,e2,e3) ! in :fortranapex:apexsh.f90 + subroutine apxg2all(glat,glon,alt,hr,vecflagin,qlatout,qlonout,mlat,mlon,f1,f2,f,d1,d2,d3,d,e1,e2,e3) ! in :fortranapex:.\apexsh.f90 use apxshmodule real(kind=4) intent(in) :: glat real(kind=4) intent(in) :: glon @@ -263,7 +215,7 @@ python module fortranapex ! in real(kind=4) dimension(3),intent(out) :: e2 real(kind=4) dimension(3),intent(out) :: e3 end subroutine apxg2all - subroutine apxq2g(qlat0,qlon0,alt,prec,glatout,glonout,error) ! in :fortranapex:apexsh.f90 + subroutine apxq2g(qlat0,qlon0,alt,prec,glatout,glonout,error) ! in :fortranapex:.\apexsh.f90 use apxshmodule real(kind=4) intent(in) :: qlat0 real(kind=4) intent(in) :: qlon0 @@ -273,12 +225,12 @@ python module fortranapex ! in real(kind=4) intent(out) :: glonout real(kind=4) intent(out) :: error end subroutine apxq2g - subroutine shcalc(theta,phi) ! in :fortranapex:apexsh.f90 + subroutine shcalc(theta,phi) ! in :fortranapex:.\apexsh.f90 use apxshmodule real(kind=8) intent(in) :: theta real(kind=8) intent(in) :: phi end subroutine shcalc - module alfbasismodule ! in :fortranapex:apexsh.f90 + module alfbasismodule ! in :fortranapex:.\apexsh.f90 integer(kind=4) :: nmax0 integer(kind=4) :: mmax0 real(kind=8), allocatable,dimension(:,:) :: anm @@ -289,12 +241,12 @@ python module fortranapex ! in real(kind=8), allocatable,dimension(:) :: marr real(kind=8), allocatable,dimension(:) :: narr end module alfbasismodule - subroutine alfbasisinit(nmax0in,mmax0in) ! in :fortranapex:apexsh.f90 + subroutine alfbasisinit(nmax0in,mmax0in) ! in :fortranapex:.\apexsh.f90 use alfbasismodule integer(kind=4) intent(in) :: nmax0in integer(kind=4) intent(in) :: mmax0in end subroutine alfbasisinit - subroutine alfbasis(nmax,mmax,theta,p,v,w) ! in :fortranapex:apexsh.f90 + subroutine alfbasis(nmax,mmax,theta,p,v,w) ! in :fortranapex:.\apexsh.f90 use alfbasismodule integer(kind=4) intent(in) :: nmax integer(kind=4) intent(in) :: mmax @@ -303,7 +255,57 @@ python module fortranapex ! in real(kind=8) dimension(nmax + 1,mmax + 1),intent(out),depend(nmax,mmax) :: v real(kind=8) dimension(nmax + 1,mmax + 1),intent(out),depend(nmax,mmax) :: w end subroutine alfbasis - subroutine makeapxsh(datafilein,epochgridin,nepochin,lmaxin,mmaxin,nmaxin) ! in :fortranapex:makeapexsh.f90 + subroutine cofrm(date,filename) ! in :fortranapex:.\magfld.f + use igrf + real(kind=4) intent(in) :: date + character*1000 intent(in) :: filename + integer :: nmax + real dimension(255) :: gb + real dimension(225) :: gv + integer, optional :: ichg=-99999 + common /magcof/ nmax,gb,gv,ichg + end subroutine cofrm + subroutine dypol(colat,elon,vp) ! in :fortranapex:.\magfld.f + real :: colat + real :: elon + real :: vp + integer :: nmax + real dimension(255) :: gb + real dimension(225) :: gv + integer :: ichg + common /magcof/ nmax,gb,gv,ichg + end subroutine dypol + subroutine feldg(ienty,glat,glon,alt,bnrth,beast,bdown,babs) ! in :fortranapex:.\magfld.f + integer intent(in) :: ienty + real intent(in) :: glat + real intent(in) :: glon + real intent(in) :: alt + real intent(out) :: bnrth + real intent(out) :: beast + real intent(out) :: bdown + real intent(out) :: babs + integer :: nmax + real dimension(255) :: gb + real dimension(225) :: gv + integer :: ichg + common /magcof/ nmax,gb,gv,ichg + end subroutine feldg + subroutine gd2cart(gdlat,glon,alt,x,y,z) ! in :fortranapex:.\magfld.f + real :: gdlat + real :: glon + real :: alt + real :: x + real :: y + real :: z + end subroutine gd2cart + subroutine convrt(i,gdlat,alt,x1,x2) ! in :fortranapex:.\magfld.f + integer :: i + real :: gdlat + real :: alt + real :: x1 + real :: x2 + end subroutine convrt + subroutine makeapxsh(datafilein,epochgridin,nepochin,lmaxin,mmaxin,nmaxin) ! in :fortranapex:.\makeapexsh.f90 use apxshmodule character*128 intent(in) :: datafilein real(kind=4) dimension(31),intent(in) :: epochgridin @@ -315,13 +317,13 @@ python module fortranapex ! in real(kind=4) dimension(255) :: gb common /magcof/ nmaxigrf,gb end subroutine makeapxsh - subroutine choldc(a,n,np,p) ! in :fortranapex:makeapexsh.f90 + subroutine choldc(a,n,np,p) ! in :fortranapex:.\makeapexsh.f90 real(kind=8) dimension(np,np),intent(inout) :: a integer(kind=4) intent(in) :: n integer(kind=4), optional,intent(in),check(shape(a,0)==np),depend(a) :: np=shape(a,0) real(kind=8) dimension(n),intent(out),depend(n) :: p end subroutine choldc - subroutine cholsl(a,n,np,p,b,x) ! in :fortranapex:makeapexsh.f90 + subroutine cholsl(a,n,np,p,b,x) ! in :fortranapex:.\makeapexsh.f90 real(kind=8) dimension(np,np),intent(in) :: a integer(kind=4), optional,intent(in),check(len(p)>=n),depend(p) :: n=len(p) integer(kind=4), optional,intent(in),check(shape(a,0)==np),depend(a) :: np=shape(a,0) diff --git a/src/fortranapex/magfld.f b/src/fortranapex/magfld.f index d8311bcf..1240170a 100644 --- a/src/fortranapex/magfld.f +++ b/src/fortranapex/magfld.f @@ -1,6 +1,6 @@ C FILE NAME: magfld.f - SUBROUTINE COFRM (DATE) + SUBROUTINE COFRM (DATE, FILENAME) C Define the International Geomagnetic Reference Field (IGRF) as a C scalar potential field using a truncated series expansion with C Schmidt semi-normalized associated Legendre functions of degree n and @@ -9,7 +9,8 @@ SUBROUTINE COFRM (DATE) C rate after the last epoch. C C INPUTS: -C DATE = yyyy.fraction (UT) +C DATE = yyyy.fraction (UT)\ +C FILENAME = filename C OUTPUTS (in comnon block MAGCOF): C NMAX = Maximum order of spherical harmonic coefficients used C GB = Coefficients for magnetic field calculation @@ -129,9 +130,8 @@ SUBROUTINE COFRM (DATE) ICHG = 1 c Load coefficients - FILENAME='igrf13coeffs.txt' if (.not. allocated(GYR)) then - call read_igrf(filename,GYR,HYR,GT,HT,NEPO,NGHT,EPOCH,NMXE) + call read_igrf(FILENAME,GYR,HYR,GT,HT,NEPO,NGHT,EPOCH,NMXE) endif NGH=NGHT*NEPO From fb27511518a68b07c7d7c3c098ac57973198d769 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 15 Feb 2021 15:15:06 -0400 Subject: [PATCH 122/226] modifying fortranapex.pyf to clean up PR: removing '.\' --- src/fortranapex/fortranapex.pyf | 48 ++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/fortranapex/fortranapex.pyf b/src/fortranapex/fortranapex.pyf index f200f551..e639655f 100644 --- a/src/fortranapex/fortranapex.pyf +++ b/src/fortranapex/fortranapex.pyf @@ -3,7 +3,7 @@ python module fortranapex ! in interface ! in :fortranapex - subroutine apex(date,dlat,dlon,alt,a,alat,alon,bmag,xmag,ymag,zmag,v) ! in :fortranapex:.\apex.f + subroutine apex(date,dlat,dlon,alt,a,alat,alon,bmag,xmag,ymag,zmag,v) ! in :fortranapex:apex.f real :: date real :: dlat real :: dlon @@ -23,7 +23,7 @@ python module fortranapex ! in real :: stp common /dipole/ colat,elon,vp,ctp,stp end subroutine apex - subroutine linapx(gdlat,glon,alt,a,alat,alon,xmag,ymag,zmag,f) ! in :fortranapex:.\apex.f + subroutine linapx(gdlat,glon,alt,a,alat,alon,xmag,ymag,zmag,f) ! in :fortranapex:apex.f real :: gdlat real :: glon real :: alt @@ -54,7 +54,7 @@ python module fortranapex ! in common /dipole/ colat,elon,vp,ctp,stp common /itra/ nstp,y,yp,sgn,ds end subroutine linapx - subroutine itrace(iapx) ! in :fortranapex:.\apex.f + subroutine itrace(iapx) ! in :fortranapex:apex.f integer :: iapx real dimension(3,3) :: yapx real :: bx @@ -70,7 +70,7 @@ python module fortranapex ! in common /fldcomd/ bx,by,bz,bb common /itra/ nstp,y,yold,sgn,ds end subroutine itrace - subroutine fndapx(alt,zmag,a,alat,alon) ! in :fortranapex:.\apex.f + subroutine fndapx(alt,zmag,a,alat,alon) ! in :fortranapex:apex.f real :: alt real :: zmag real :: a @@ -85,7 +85,7 @@ python module fortranapex ! in common /apxin/ yapx common /dipole/ colat,elon,vp,ctp,stp end subroutine fndapx - subroutine dipapx(gdlat,gdlon,alt,bnorth,beast,bdown,a,alon) ! in :fortranapex:.\apex.f + subroutine dipapx(gdlat,gdlon,alt,bnorth,beast,bdown,a,alon) ! in :fortranapex:apex.f real :: gdlat real :: gdlon real :: alt @@ -101,7 +101,7 @@ python module fortranapex ! in real :: stp common /dipole/ colat,elon,vp,ctp,stp end subroutine dipapx - function fint(x1,x2,x3,y1,y2,y3,xfit) ! in :fortranapex:.\apex.f + function fint(x1,x2,x3,y1,y2,y3,xfit) ! in :fortranapex:apex.f real :: x1 real :: x2 real :: x3 @@ -111,7 +111,7 @@ python module fortranapex ! in real :: xfit real :: fint end function fint - module apxshmodule ! in :fortranapex:.\apexsh.f90 + module apxshmodule ! in :fortranapex:apexsh.f90 integer(kind=4) :: nterm integer(kind=4) :: nmax integer(kind=4) :: mmax @@ -173,15 +173,15 @@ python module fortranapex ! in real(kind=4) :: altlastg logical, optional :: loadflag=.true. end module apxshmodule - subroutine loadapxsh(datafilenew,epochnew) ! in :fortranapex:.\apexsh.f90 + subroutine loadapxsh(datafilenew,epochnew) ! in :fortranapex:apexsh.f90 use apxshmodule character*1000 intent(in) :: datafilenew real(kind=4) intent(in) :: epochnew end subroutine loadapxsh - subroutine allocatearrays ! in :fortranapex:.\apexsh.f90 + subroutine allocatearrays ! in :fortranapex:apexsh.f90 use apxshmodule end subroutine allocatearrays - subroutine apxg2q(glat,glon,alt,vecflagin,qlatout,qlonout,f1,f2,f) ! in :fortranapex:.\apexsh.f90 + subroutine apxg2q(glat,glon,alt,vecflagin,qlatout,qlonout,f1,f2,f) ! in :fortranapex:apexsh.f90 use apxshmodule real(kind=4) intent(in) :: glat real(kind=4) intent(in) :: glon @@ -193,7 +193,7 @@ python module fortranapex ! in real(kind=4) dimension(2),intent(out) :: f2 real(kind=4) intent(out) :: f end subroutine apxg2q - subroutine apxg2all(glat,glon,alt,hr,vecflagin,qlatout,qlonout,mlat,mlon,f1,f2,f,d1,d2,d3,d,e1,e2,e3) ! in :fortranapex:.\apexsh.f90 + subroutine apxg2all(glat,glon,alt,hr,vecflagin,qlatout,qlonout,mlat,mlon,f1,f2,f,d1,d2,d3,d,e1,e2,e3) ! in :fortranapex:apexsh.f90 use apxshmodule real(kind=4) intent(in) :: glat real(kind=4) intent(in) :: glon @@ -215,7 +215,7 @@ python module fortranapex ! in real(kind=4) dimension(3),intent(out) :: e2 real(kind=4) dimension(3),intent(out) :: e3 end subroutine apxg2all - subroutine apxq2g(qlat0,qlon0,alt,prec,glatout,glonout,error) ! in :fortranapex:.\apexsh.f90 + subroutine apxq2g(qlat0,qlon0,alt,prec,glatout,glonout,error) ! in :fortranapex:apexsh.f90 use apxshmodule real(kind=4) intent(in) :: qlat0 real(kind=4) intent(in) :: qlon0 @@ -225,12 +225,12 @@ python module fortranapex ! in real(kind=4) intent(out) :: glonout real(kind=4) intent(out) :: error end subroutine apxq2g - subroutine shcalc(theta,phi) ! in :fortranapex:.\apexsh.f90 + subroutine shcalc(theta,phi) ! in :fortranapex:apexsh.f90 use apxshmodule real(kind=8) intent(in) :: theta real(kind=8) intent(in) :: phi end subroutine shcalc - module alfbasismodule ! in :fortranapex:.\apexsh.f90 + module alfbasismodule ! in :fortranapex:apexsh.f90 integer(kind=4) :: nmax0 integer(kind=4) :: mmax0 real(kind=8), allocatable,dimension(:,:) :: anm @@ -241,12 +241,12 @@ python module fortranapex ! in real(kind=8), allocatable,dimension(:) :: marr real(kind=8), allocatable,dimension(:) :: narr end module alfbasismodule - subroutine alfbasisinit(nmax0in,mmax0in) ! in :fortranapex:.\apexsh.f90 + subroutine alfbasisinit(nmax0in,mmax0in) ! in :fortranapex:apexsh.f90 use alfbasismodule integer(kind=4) intent(in) :: nmax0in integer(kind=4) intent(in) :: mmax0in end subroutine alfbasisinit - subroutine alfbasis(nmax,mmax,theta,p,v,w) ! in :fortranapex:.\apexsh.f90 + subroutine alfbasis(nmax,mmax,theta,p,v,w) ! in :fortranapex:apexsh.f90 use alfbasismodule integer(kind=4) intent(in) :: nmax integer(kind=4) intent(in) :: mmax @@ -255,7 +255,7 @@ python module fortranapex ! in real(kind=8) dimension(nmax + 1,mmax + 1),intent(out),depend(nmax,mmax) :: v real(kind=8) dimension(nmax + 1,mmax + 1),intent(out),depend(nmax,mmax) :: w end subroutine alfbasis - subroutine cofrm(date,filename) ! in :fortranapex:.\magfld.f + subroutine cofrm(date,filename) ! in :fortranapex:magfld.f use igrf real(kind=4) intent(in) :: date character*1000 intent(in) :: filename @@ -265,7 +265,7 @@ python module fortranapex ! in integer, optional :: ichg=-99999 common /magcof/ nmax,gb,gv,ichg end subroutine cofrm - subroutine dypol(colat,elon,vp) ! in :fortranapex:.\magfld.f + subroutine dypol(colat,elon,vp) ! in :fortranapex:magfld.f real :: colat real :: elon real :: vp @@ -275,7 +275,7 @@ python module fortranapex ! in integer :: ichg common /magcof/ nmax,gb,gv,ichg end subroutine dypol - subroutine feldg(ienty,glat,glon,alt,bnrth,beast,bdown,babs) ! in :fortranapex:.\magfld.f + subroutine feldg(ienty,glat,glon,alt,bnrth,beast,bdown,babs) ! in :fortranapex:magfld.f integer intent(in) :: ienty real intent(in) :: glat real intent(in) :: glon @@ -290,7 +290,7 @@ python module fortranapex ! in integer :: ichg common /magcof/ nmax,gb,gv,ichg end subroutine feldg - subroutine gd2cart(gdlat,glon,alt,x,y,z) ! in :fortranapex:.\magfld.f + subroutine gd2cart(gdlat,glon,alt,x,y,z) ! in :fortranapex:magfld.f real :: gdlat real :: glon real :: alt @@ -298,14 +298,14 @@ python module fortranapex ! in real :: y real :: z end subroutine gd2cart - subroutine convrt(i,gdlat,alt,x1,x2) ! in :fortranapex:.\magfld.f + subroutine convrt(i,gdlat,alt,x1,x2) ! in :fortranapex:magfld.f integer :: i real :: gdlat real :: alt real :: x1 real :: x2 end subroutine convrt - subroutine makeapxsh(datafilein,epochgridin,nepochin,lmaxin,mmaxin,nmaxin) ! in :fortranapex:.\makeapexsh.f90 + subroutine makeapxsh(datafilein,epochgridin,nepochin,lmaxin,mmaxin,nmaxin) ! in :fortranapex:makeapexsh.f90 use apxshmodule character*128 intent(in) :: datafilein real(kind=4) dimension(31),intent(in) :: epochgridin @@ -317,13 +317,13 @@ python module fortranapex ! in real(kind=4) dimension(255) :: gb common /magcof/ nmaxigrf,gb end subroutine makeapxsh - subroutine choldc(a,n,np,p) ! in :fortranapex:.\makeapexsh.f90 + subroutine choldc(a,n,np,p) ! in :fortranapex:makeapexsh.f90 real(kind=8) dimension(np,np),intent(inout) :: a integer(kind=4) intent(in) :: n integer(kind=4), optional,intent(in),check(shape(a,0)==np),depend(a) :: np=shape(a,0) real(kind=8) dimension(n),intent(out),depend(n) :: p end subroutine choldc - subroutine cholsl(a,n,np,p,b,x) ! in :fortranapex:.\makeapexsh.f90 + subroutine cholsl(a,n,np,p,b,x) ! in :fortranapex:makeapexsh.f90 real(kind=8) dimension(np,np),intent(in) :: a integer(kind=4), optional,intent(in),check(len(p)>=n),depend(p) :: n=len(p) integer(kind=4), optional,intent(in),check(shape(a,0)==np),depend(a) :: np=shape(a,0) From 824bb20665c8d5a00cf29b1e35f5faa7b18f3fa7 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 15 Feb 2021 15:22:13 -0400 Subject: [PATCH 123/226] moving igrfcoeffs.txt --- src/apexpy/apex.py | 2 +- src/{fortranapex => apexpy}/igrf13coeffs.txt | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{fortranapex => apexpy}/igrf13coeffs.txt (100%) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 15c7cfd6..ca1d67ff 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -931,7 +931,7 @@ def set_epoch(self, year): # f2py self.year = np.float64(year) fa.loadapxsh(self.datafile, self.year) - igrf_fn = os.path.join(os.path.dirname(__file__), '..', 'fortranapex', 'igrf13coeffs.txt') + igrf_fn = os.path.join(os.path.dirname(__file__), 'igrf13coeffs.txt') fa.cofrm(self.year, igrf_fn) def set_refh(self, refh): diff --git a/src/fortranapex/igrf13coeffs.txt b/src/apexpy/igrf13coeffs.txt similarity index 100% rename from src/fortranapex/igrf13coeffs.txt rename to src/apexpy/igrf13coeffs.txt From ad252fa56eb8674503b59e872cee0fc35aaa0682 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 15 Feb 2021 15:25:07 -0400 Subject: [PATCH 124/226] adding igrfcoeffs.txt to setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d6ade3f1..27a28332 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ def read(*names, **kwargs): package_dir={'': 'src'}, py_modules=[path.splitext(path.basename(pp))[0] for pp in glob('src/*.py')], - package_data={'apexpy': ['apexsh.dat']}, + package_data={'apexpy': ['apexsh.dat', 'igrfcoeffs.txt']}, zip_safe=False, classifiers=[ # complete classifier list: From b9ce6f729f6a22f9ce7c4c29a26370360013c1ba Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 15 Feb 2021 15:30:31 -0400 Subject: [PATCH 125/226] adding igrfcoeffs.txt to setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 27a28332..97d88aca 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ def read(*names, **kwargs): package_dir={'': 'src'}, py_modules=[path.splitext(path.basename(pp))[0] for pp in glob('src/*.py')], - package_data={'apexpy': ['apexsh.dat', 'igrfcoeffs.txt']}, + package_data={'apexpy': ['apexsh.dat', 'igrf13coeffs.txt']}, zip_safe=False, classifiers=[ # complete classifier list: From dc6a0ee37e3be2e5ff96368ddf1fcaed31aea6ca Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 15 Feb 2021 15:51:31 -0400 Subject: [PATCH 126/226] removing print statements from igrf.f90 --- src/fortranapex/igrf.f90 | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/fortranapex/igrf.f90 b/src/fortranapex/igrf.f90 index 16cd7399..e997a6c8 100644 --- a/src/fortranapex/igrf.f90 +++ b/src/fortranapex/igrf.f90 @@ -27,10 +27,6 @@ subroutine read_igrf(filename_in,GYR,HYR,GT,HT,NEPO,NGHT,EPOCH,NMXE) !num_sh = NGHT-2*sqrt(real(NGHT)) !write(*,*) num_sh - write(*,*) '----------------------------' - write(*,*) ' Read IGRF coefficeints ' - write(*,*) '----------------------------' - ! Open IGRF file open(unit=100, file=filename_in,status='old',iostat=state) if (state /= 0) then @@ -47,7 +43,6 @@ subroutine read_igrf(filename_in,GYR,HYR,GT,HT,NEPO,NGHT,EPOCH,NMXE) num_epochs=count([(s(i:i+3),i=1,len_trim(s))].eq.'IGRF') num_epochs=count([(s(i:i+3),i=1,len_trim(s))].eq.'DGRF')+num_epochs allocate(EPOCH(1:num_epochs)) - write(*,*) 'Number of epochs read: ',num_epochs allocate(NMXE(1:num_epochs)) ! Read epochs @@ -107,7 +102,6 @@ subroutine read_igrf(filename_in,GYR,HYR,GT,HT,NEPO,NGHT,EPOCH,NMXE) GT =0.0d0 HYR=0.0d0 HT =0.0d0 - write(*,*) 'ASSIGN COEFFICIENTS: ',NEPO,NGHT do e=1,NEPO+1 do l=1,L_max if (e .le. NEPO) then From 7abd6f3aac50e1be88e3e678ec04a63dcd66eed4 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 15 Feb 2021 18:11:08 -0400 Subject: [PATCH 127/226] fixing bug caused by np.sign(qlat) when qlat is zero in apex.py #28 --- src/apexpy/apex.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index ca1d67ff..41b8db6f 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -445,9 +445,8 @@ def _qd2apex_nonvectorized(self, qlat, qlon, height): estr += '({:.3g}) for qlat {:.3g}'.format(self.refh, qlat) raise ApexHeightError(estr) - alat = np.sign(qlat) * np.degrees(np.arccos(np.sqrt((self.RE + - self.refh) / - (self.RE + hA)))) + sqlat = np.sign(qlat) if qlat != 0 else 1 + alat = sqlat * np.degrees(np.arccos(np.sqrt((self.RE + self.refh) / (self.RE + hA)))) return alat, alon From b28d0f446ef174a8f404c3f37082e19678e59a52 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 15 Feb 2021 20:33:51 -0400 Subject: [PATCH 128/226] reducing line length in apex.py to be pep8 compliant --- src/apexpy/apex.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 41b8db6f..9f24169b 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -446,7 +446,8 @@ def _qd2apex_nonvectorized(self, qlat, qlon, height): raise ApexHeightError(estr) sqlat = np.sign(qlat) if qlat != 0 else 1 - alat = sqlat * np.degrees(np.arccos(np.sqrt((self.RE + self.refh) / (self.RE + hA)))) + alat = sqlat * np.degrees(np.arccos(np.sqrt((self.RE + self.refh) / + (self.RE + hA)))) return alat, alon From f68dc7c4c2f6bb40ccf0f4ef9c96146d9f645fd1 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Tue, 16 Feb 2021 11:21:48 -0400 Subject: [PATCH 129/226] Update src/apexpy/apex.py Co-authored-by: Angeline Burrell --- src/apexpy/apex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 9f24169b..114fa29c 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -804,7 +804,7 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): ------- f3, g1, g2, g3, d1, d2, d3, e1, e2, e3 : (3, N) or (3,) ndarray - Note + Notes ---- `f3`, `g1`, `g2`, and `g3` are not part of the Fortran code by Emmert et al. [2010] [5]_. They are calculated by this From 0914e5b02fc39a93dcac2f37a3a8df38cf637576 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Tue, 16 Feb 2021 11:21:54 -0400 Subject: [PATCH 130/226] Update src/apexpy/apex.py Co-authored-by: Angeline Burrell --- src/apexpy/apex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 114fa29c..a619b75e 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -56,7 +56,7 @@ class Apex(object): Notes ----- - The calculations use IGRF-12 with coefficients from 1900 to 2020 [1]_. + The calculations use IGRF-13 with coefficients from 1900 to 2025 [1]_. The geodetic reference ellipsoid is WGS84. From 0fa94f793755b0ad1eb4ff93b4e82354190a341d Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 09:58:19 -0500 Subject: [PATCH 131/226] ENH: added pyproject toml Added a toml file to improve pip installation in virtual environments. --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 073b80eb..e85c5edf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,2 @@ [build-system] requires = ["numpy", "setuptools", "wheel"] -build-backend = "setuptools.build_meta" From a4f30f9a76e4ff8011c1edb3c376d6a78d35496d Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 09:59:04 -0500 Subject: [PATCH 132/226] MAINT: removed unneeded version bump Removed outdated version bump to setup.py (now in setup.cfg). --- setup.cfg | 2 -- 1 file changed, 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 8bdfb699..4b9fdeef 100644 --- a/setup.cfg +++ b/setup.cfg @@ -64,8 +64,6 @@ current_version = 1.0.4 commit = True tag = True -[bumpversion:file:setup.py] - [bumpversion:file:docs/conf.py] [bumpversion:file:src/apexpy/__init__.py] From 9d5f8d05d39fcb7e35a82c0470f6c6b755795fb4 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 09:59:52 -0500 Subject: [PATCH 133/226] BUG: fixed bug in bootstrap Fixed bug where "dependencies" is an optional kwarg. --- ci/bootstrap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/bootstrap.py b/ci/bootstrap.py index 466bb8fe..db5c7db1 100644 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -42,7 +42,7 @@ tox_environments = {} for (alias, conf) in matrix.from_file(join(base_path, "setup.cfg")).items(): python = conf["python_versions"] - deps = conf["dependencies"] + deps = conf["dependencies"] if "dependencies" in conf.keys() else "" if "coverage_flags" in conf: cover = {"false": False, "true": True}[conf["coverage_flags"].lower()] if "environment_variables" in conf: From 91a509e12b4ee04ef1b9c2e880299c36dadfb127 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 10:00:36 -0500 Subject: [PATCH 134/226] TST: updated tox and travis yml Updated tox and travis yml to follow aacgmv2 style, hopefully fixing coveralls. --- .travis.yml | 19 +++++----- ci/templates/.travis.yml | 19 +++++----- ci/templates/tox.ini | 42 +++++++--------------- tox.ini | 77 ++++++++++++++++++++++------------------ 4 files changed, 73 insertions(+), 84 deletions(-) diff --git a/.travis.yml b/.travis.yml index 35812a81..f6deb98e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,22 +23,21 @@ before_install: - python --version - uname -a - lsb_release -a - - pip install numpy install: - - pip install coveralls pytest-cov coverage codecov + - pip install numpy coveralls pytest-cov tox-travis - python setup.py clean --all build_ext --force --inplace - - python setup.py develop - - pip install tox-travis + - python setup.py install script: - - tox + - | + if [ $TRAVIS_PYTHON_VERSION == "3.7" ]; then + tox -e check,docs + fi + - tox -e $TRAVIS_PYTHON_VERSION + after_success: - - coverage combine - - coverage report - - coveralls --merge=extension-coveralls.json [] - - coveralls - - codecov + - coveralls --rcfile=setup.cfg notifications: email: diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index b6da4b68..4bd7c4b8 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -21,22 +21,21 @@ before_install: - python --version - uname -a - lsb_release -a - - pip install numpy install: - - pip install coveralls pytest-cov coverage codecov + - pip install numpy coveralls pytest-cov tox-travis - python setup.py clean --all build_ext --force --inplace - - python setup.py develop - - pip install tox-travis + - python setup.py install script: - - tox + - | + if [ $TRAVIS_PYTHON_VERSION == "3.7" ]; then + tox -e check,docs + fi + - tox -e $TRAVIS_PYTHON_VERSION + after_success: - - coverage combine - - coverage report - - coveralls --merge=extension-coveralls.json [] - - coveralls - - codecov + - coveralls --rcfile=setup.cfg notifications: email: diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index dbfcdb41..6fc351b0 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -25,18 +25,6 @@ commands = # python setup.py develop {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} -[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 @@ -56,7 +44,6 @@ passenv = * [testenv:check] -basepython = python3.4 deps = docutils check-manifest @@ -68,7 +55,7 @@ usedevelop = false commands = python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} - flake8 src tests + flake8 --ignore=F401,W503 src tests [testenv:coveralls] deps = @@ -78,19 +65,7 @@ usedevelop = false commands = coverage combine 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 [] - + coveralls --rcfile=setup.cfg --merge=extension-coveralls.json [] [testenv:extension-coveralls] deps = @@ -98,10 +73,9 @@ deps = skip_install = true usedevelop = false commands = - coveralls --build-root=. --include=src --dump=extension-coveralls.json [] + coveralls --rcfile=setup.cfg --build-root=. --include=src --dump=extension-coveralls.json [] [testenv:report] -basepython = python3.4 deps = coverage skip_install = true usedevelop = false @@ -131,7 +105,7 @@ setenv = usedevelop = true commands = python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} + python -m pytest {posargs:--cov --cov-report=term-missing -vv --doctest-glob='*.rst'} {% endif %} {% if config.cover or config.deps %} deps = @@ -144,4 +118,12 @@ deps = {{ dep }} {% endfor %} +{% if not config.cover %} +[testenv:{{ config.python[-3:] }}-buildonly-nocover] +basepython = {env:TOXPYTHON:{{ config.python }}} +deps = +skip_install = true +commands = +{% endif %} + {% endfor %} diff --git a/tox.ini b/tox.ini index 9ac1b77d..5e9b2c24 100644 --- a/tox.ini +++ b/tox.ini @@ -32,18 +32,6 @@ commands = # python setup.py develop {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} -[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 @@ -63,7 +51,6 @@ passenv = * [testenv:check] -basepython = python3.4 deps = docutils check-manifest @@ -75,7 +62,7 @@ usedevelop = false commands = python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} - flake8 src tests + flake8 --ignore=F401,W503 src tests [testenv:coveralls] deps = @@ -85,19 +72,7 @@ usedevelop = false commands = coverage combine 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 [] - + coveralls --rcfile=setup.cfg --merge=extension-coveralls.json [] [testenv:extension-coveralls] deps = @@ -105,10 +80,9 @@ deps = skip_install = true usedevelop = false commands = - coveralls --build-root=. --include=src --dump=extension-coveralls.json [] + coveralls --rcfile=setup.cfg --build-root=. --include=src --dump=extension-coveralls.json [] [testenv:report] -basepython = python3.4 deps = coverage skip_install = true usedevelop = false @@ -131,14 +105,21 @@ setenv = usedevelop = true commands = python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} + python -m pytest {posargs:--cov --cov-report=term-missing -vv --doctest-glob='*.rst'} deps = {[testenv]deps} pytest-cov + [testenv:2.7-nocover] basepython = {env:TOXPYTHON:python2.7} +[testenv:2.7-buildonly-nocover] +basepython = {env:TOXPYTHON:python2.7} +deps = +skip_install = true +commands = + [testenv:3.6] basepython = {env:TOXPYTHON:python3.6} setenv = @@ -148,14 +129,21 @@ setenv = usedevelop = true commands = python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} + python -m pytest {posargs:--cov --cov-report=term-missing -vv --doctest-glob='*.rst'} deps = {[testenv]deps} pytest-cov + [testenv:3.6-nocover] basepython = {env:TOXPYTHON:python3.6} +[testenv:3.6-buildonly-nocover] +basepython = {env:TOXPYTHON:python3.6} +deps = +skip_install = true +commands = + [testenv:3.7] basepython = {env:TOXPYTHON:python3.7} setenv = @@ -165,14 +153,21 @@ setenv = usedevelop = true commands = python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} + python -m pytest {posargs:--cov --cov-report=term-missing -vv --doctest-glob='*.rst'} deps = {[testenv]deps} pytest-cov + [testenv:3.7-nocover] basepython = {env:TOXPYTHON:python3.7} +[testenv:3.7-buildonly-nocover] +basepython = {env:TOXPYTHON:python3.7} +deps = +skip_install = true +commands = + [testenv:3.8] basepython = {env:TOXPYTHON:python3.8} setenv = @@ -182,14 +177,21 @@ setenv = usedevelop = true commands = python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} + python -m pytest {posargs:--cov --cov-report=term-missing -vv --doctest-glob='*.rst'} deps = {[testenv]deps} pytest-cov + [testenv:3.8-nocover] basepython = {env:TOXPYTHON:python3.8} +[testenv:3.8-buildonly-nocover] +basepython = {env:TOXPYTHON:python3.8} +deps = +skip_install = true +commands = + [testenv:3.9] basepython = {env:TOXPYTHON:python3.9} setenv = @@ -199,11 +201,18 @@ setenv = usedevelop = true commands = python setup.py clean --all build_ext --force --inplace - {posargs:py.test --cov --cov-report=term-missing -vv --doctest-glob='*.rst'} + python -m pytest {posargs:--cov --cov-report=term-missing -vv --doctest-glob='*.rst'} deps = {[testenv]deps} pytest-cov + [testenv:3.9-nocover] basepython = {env:TOXPYTHON:python3.9} +[testenv:3.9-buildonly-nocover] +basepython = {env:TOXPYTHON:python3.9} +deps = +skip_install = true +commands = + From 93260a36ef7a55d59d1821ed4977e53e4da2b861 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 10:05:37 -0500 Subject: [PATCH 135/226] BUG: updated Manifest Added pyproject.toml to the manifest. --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 54c446b5..d8725242 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -10,6 +10,6 @@ include LICENSE include README.rst include CODE_OF_CONDUCT.md -include tox.ini .travis.yml appveyor.yml .codeclimate.yml +include tox.ini .travis.yml appveyor.yml .codeclimate.yml pyproject.toml global-exclude *.py[cod~] __pycache__ *.so *.dylib From 37f54ad812f59933bb0938965a0d6d2bd4417c33 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 12:19:37 -0500 Subject: [PATCH 136/226] TST: added missing module Added missing support module `sphinx_rtd_theme`. --- .travis.yml | 2 +- ci/templates/.travis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f6deb98e..0523394a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ before_install: - lsb_release -a install: - - pip install numpy coveralls pytest-cov tox-travis + - pip install numpy coveralls pytest-cov tox-travis sphinx_rtd_theme - python setup.py clean --all build_ext --force --inplace - python setup.py install diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 4bd7c4b8..5bf7cfd2 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -23,7 +23,7 @@ before_install: - lsb_release -a install: - - pip install numpy coveralls pytest-cov tox-travis + - pip install numpy coveralls pytest-cov tox-travis sphinx_rtd_theme - python setup.py clean --all build_ext --force --inplace - python setup.py install From 162258b555972f35db1d0e05cfb8e8a98167db41 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 12:20:21 -0500 Subject: [PATCH 137/226] TST: updated testing commands Updated testing commands to new pytest standard and updated python check version. --- appveyor.yml | 24 ++++++++++++------------ ci/templates/appveyor.yml | 8 ++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 16a62131..c1255264 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,58 +6,58 @@ environment: - TESTENV: '2.7-nocover-64' PYTHON_VERSION: '2.7' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '2.7-nocover-32' PYTHON_VERSION: '2.7' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.6-nocover-64' PYTHON_VERSION: '3.6' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.6-nocover-32' PYTHON_VERSION: '3.6' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.7-nocover-64' PYTHON_VERSION: '3.7' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.7-nocover-32' PYTHON_VERSION: '3.7' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.8-nocover-64' PYTHON_VERSION: '3.8' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.8-nocover-32' PYTHON_VERSION: '3.8' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.9-nocover-64' PYTHON_VERSION: '3.9' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.9-nocover-32' PYTHON_VERSION: '3.9' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: 'check' - PYTHON_VERSION: '2.7' + PYTHON_VERSION: '3.7' MINICONDA_HOME: 'C:\Miniconda' TESTSCRIPT: 'python setup.py check --strict --metadata --restructuredtext && check-manifest && flake8 src tests' - INSTALL_EXTRA_DEPS: 'pip install docutils check-manifest flake8 readme pygments' + INSTALL_EXTRA_DEPS: 'pip install docutils check-manifest flake8 readme pygments sphinx_rtd_theme' init: - ps: echo $env:TESTENV diff --git a/ci/templates/appveyor.yml b/ci/templates/appveyor.yml index 7f8bcb39..c8842a10 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/appveyor.yml @@ -7,19 +7,19 @@ environment: - TESTENV: '{{ env }}-64' PYTHON_VERSION: '{{ config.python[-3:] }}' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '{{ env }}-32' PYTHON_VERSION: '{{ config.python[-3:] }}' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' {% endif %}{% endfor %} - TESTENV: 'check' - PYTHON_VERSION: '2.7' + PYTHON_VERSION: '3.7' MINICONDA_HOME: 'C:\Miniconda' TESTSCRIPT: 'python setup.py check --strict --metadata --restructuredtext && check-manifest && flake8 src tests' - INSTALL_EXTRA_DEPS: 'pip install docutils check-manifest flake8 readme pygments' + INSTALL_EXTRA_DEPS: 'pip install docutils check-manifest flake8 readme pygments sphinx_rtd_theme' init: - ps: echo $env:TESTENV From f63061755cf34aa4d569ce03d83bcc50b0f213b5 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 12:21:10 -0500 Subject: [PATCH 138/226] TST: added function Added import of setuptools find_packages function, which appeared to be needed in python 2.7. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e4c97fe9..0274b1f4 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from glob import glob from os import path, environ -from setuptools import setup +from setuptools import setup, find_packages # Include extensions only when not on readthedocs.org if environ.get('READTHEDOCS', None) == 'True': From 6a0bec41b98533755da65950fa458f92966d6e56 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 12:51:13 -0500 Subject: [PATCH 139/226] TST: fixed python 2.7 implementation Fixed setup for python 2. --- setup.cfg | 4 ---- setup.py | 9 +++++++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/setup.cfg b/setup.cfg index 4b9fdeef..cd268d87 100644 --- a/setup.cfg +++ b/setup.cfg @@ -39,7 +39,6 @@ classifiers = [options] zip_safe = False -packages = find: package_dir = =src install_requires = numpy @@ -50,9 +49,6 @@ include_entry_points = True console_scripts = apexpy = apexpy.__main__:main -[options.packages.find] -where = src - [options.package_data] apexpy = apexsh.dat diff --git a/setup.py b/setup.py index 0274b1f4..cd7314ab 100644 --- a/setup.py +++ b/setup.py @@ -5,6 +5,7 @@ from glob import glob from os import path, environ from setuptools import setup, find_packages +import sys # Include extensions only when not on readthedocs.org if environ.get('READTHEDOCS', None) == 'True': @@ -20,5 +21,9 @@ 'src/fortranapex/checkapexsh.f90', 'src/fortranapex/fortranapex.pyf'])] -setup(py_modules=[path.splitext(path.basename(pp))[0] - for pp in glob('src/*.py')], ext_modules=extensions) +setup_kwargs = {'py_modules': [path.splitext(path.basename(pp))[0] + for pp in glob('src/*.py')], + 'ext_modules': extensions, + 'packages': find_packages(where='src')} + +setup(**setup_kwargs) From 4ccce510b93c6dfa1fb72a3e7f2e523bfce27e5b Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 13:21:21 -0500 Subject: [PATCH 140/226] TST: moved dependent installations Moved dependent installations in travis to pre-package installation. --- .travis.yml | 2 +- ci/templates/.travis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0523394a..96a2c9fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,9 +23,9 @@ before_install: - python --version - uname -a - lsb_release -a + - pip install numpy coveralls pytest-cov tox-travis sphinx_rtd_theme install: - - pip install numpy coveralls pytest-cov tox-travis sphinx_rtd_theme - python setup.py clean --all build_ext --force --inplace - python setup.py install diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 5bf7cfd2..3e0a544a 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -21,9 +21,9 @@ before_install: - python --version - uname -a - lsb_release -a + - pip install numpy coveralls pytest-cov tox-travis sphinx_rtd_theme install: - - pip install numpy coveralls pytest-cov tox-travis sphinx_rtd_theme - python setup.py clean --all build_ext --force --inplace - python setup.py install From 17b82e6b4c4a6966dcd3fbd320ba8ff93c945b3d Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 15:50:17 -0500 Subject: [PATCH 141/226] MAINT: updated installation links Updated the links in the installation guide. --- docs/installation.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 1b22dc4b..3ccd6ee6 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -4,11 +4,12 @@ Installation ============ This package requires NumPy, which you can install alone or as a part of SciPy. -`Some Python distributions `_ +`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 `_. +`pre-compiled binaries of NumPy/SciPy `_, and Linux users may have +NumPy/SciPy available in +`their repositories `_. When you have NumPy, you may use either PyPi or GitHub to install this package. @@ -54,4 +55,4 @@ you may install by:: .. [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 `_. + `get it here `_. From 85a8e76cff45bfbf6bdf6d05b046bf163659ea71 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 15:51:10 -0500 Subject: [PATCH 142/226] MAINT: updated ref links Updated the links in the Fortran code reference file. --- docs/reference/fortranapex.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/reference/fortranapex.rst b/docs/reference/fortranapex.rst index 31526761..2b0d7fd8 100644 --- a/docs/reference/fortranapex.rst +++ b/docs/reference/fortranapex.rst @@ -1,9 +1,13 @@ apexpy.fortranapex ================== -This module is the interface to the apex Fortran library by Emmert et al. [2010] [1]_. The interface is not documented. Use :class:`apexpy.Apex` for all conversions and calculations. You can find some documentation of the actual Fortran library in the source file `apexsh.f90 `_. +This module is the interface to the apex Fortran library by +Emmert et al. [2010] [1]_. The interface is not documented. +Use :class:`apexpy.Apex` for all conversions and calculations. You can find +some documentation of the actual Fortran library in the source file +`apexsh.f90 `_. .. [1] Emmert, J. T., A. D. Richmond, and D. P. Drob (2010), A computationally compact representation of Magnetic-Apex and Quasi-Dipole coordinates with smooth base vectors, - J. Geophys. Res., 115(A8), A08322, :doi:`10.1029/2010JA015326`. \ No newline at end of file + J. Geophys. Res., 115(A8), A08322, :doi:`10.1029/2010JA015326`. From ed68da69270d2e9deba090244d481d7e0a7dd573 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 15:51:34 -0500 Subject: [PATCH 143/226] MAINT: updated badges Updated README badges and links. --- README.rst | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/README.rst b/README.rst index f289c686..76dc7252 100644 --- a/README.rst +++ b/README.rst @@ -79,10 +79,9 @@ Badges * - docs - |docs| * - tests - - | |travis| |appveyor| |requires| - | |coveralls| |codecov| - | |landscape| |codeclimate| - | |scrutinizer| |codacy| + - | |travis| |appveyor| + | |coveralls| |requires| + | |codeclimate| |scrutinizer| |codacy| * - package - | |version| |supported-versions| | |wheel| |supported-implementations| @@ -100,28 +99,21 @@ Badges :target: https://ci.appveyor.com/project/aburrell/apexpy .. |requires| image:: https://requires.io/github/aburrell/apexpy/requirements.svg?branch=master - :alt: Requirements Status - :target: https://requires.io/github/aburrell/apexpy/requirements/?branch=master + :alt: Requirements Status + :target: https://requires.io/github/aburrell/apexpy/requirements/?branch=master .. |coveralls| image:: https://coveralls.io/repos/github/aburrell/apexpy/badge.svg?branch=master :alt: Coverage Status :target: https://coveralls.io/github/aburrell/apexpy?branch=master -.. |codecov| image:: https://codecov.io/github/aburrell/apexpy/coverage.svg?branch=master - :alt: Coverage Status - :target: https://codecov.io/github/aburrell/apexpy - -.. |landscape| image:: https://landscape.io/github/aburrell/apexpy/master/landscape.svg?style=flat - :target: https://landscape.io/github/aburrell/apexpy/master - :alt: Code Quality Status +.. |codacy| image:: https://api.codacy.com/project/badge/Grade/7d4c1a6c60e747ca95cdf97746c39cda + :alt: Codacy Badge + :target: https://app.codacy.com/gh/aburrell/apexpy?utm_source=github.com&utm_medium=referral&utm_content=aburrell/apexpy&utm_campaign=Badge_Grade -.. |codacy| image:: https://img.shields.io/codacy/af7fdf6be28841f283dfdbc1c01fa82a.svg?style=flat - :target: https://www.codacy.com/app/aburrell/apexpy - :alt: Codacy Code Quality Status - -.. |codeclimate| image:: https://codeclimate.com/github/cmeeren/apexpy/badges/gpa.svg +.. |codeclimate| image:: https://api.codeclimate.com/v1/badges/da1d972dee790da595f8/maintainability.svg :target: https://codeclimate.com/github/aburrell/apexpy :alt: CodeClimate Quality Status + .. |version| image:: https://img.shields.io/pypi/v/apexpy.svg?style=flat :alt: PyPI Package latest release :target: https://pypi.python.org/pypi/apexpy From 207342fefe02d3ae24f4364ff8bdb74b49f3f10a Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 16:09:02 -0500 Subject: [PATCH 144/226] STY: made flake8 corrections Made flake8 operator and spacing corrections. --- src/apexpy/apex.py | 25 ++++++++++++------------- src/apexpy/helpers.py | 12 +++++++----- tests/test_Apex.py | 40 ++++++++++++++++++++-------------------- tests/test_helpers.py | 16 ++++++++-------- 4 files changed, 47 insertions(+), 46 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 7388120e..8b3d8d5a 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -106,12 +106,12 @@ def __init__(self, date=None, refh=0, datafile=None, fortranlib=None): lambda glat, glon, height: fa.apxg2q(glat, (glon + 180) % 360 - 180, height, 0)[:2], 3, 2) self._geo2apex = np.frompyfunc( - lambda glat, glon, height: fa.apxg2all(glat, (glon + 180) % 360 - - 180, height, self.refh, + lambda glat, glon, height: fa.apxg2all(glat, (glon + 180) % 360 + - 180, height, self.refh, 0)[2:4], 3, 2) self._geo2apexall = np.frompyfunc( - lambda glat, glon, height: fa.apxg2all(glat, (glon + 180) % 360 - - 180, height, self.refh, + lambda glat, glon, height: fa.apxg2all(glat, (glon + 180) % 360 + - 180, height, self.refh, 1), 3, 14) self._qd2geo = np.frompyfunc( lambda qlat, qlon, height, precision: fa.apxq2g(qlat, (qlon + 180) @@ -391,8 +391,8 @@ def _apex2qd_nonvectorized(self, alat, alon, height): estr += '{:.3g} for alat {:.3g}'.format(hA, alat) raise ApexHeightError(estr) - qlat = np.sign(alat) * np.degrees(np.arccos(np.sqrt((self.RE + height) / - (self.RE + hA)))) + qlat = np.sign(alat) * np.degrees(np.arccos(np.sqrt((self.RE + height) + / (self.RE + hA)))) return qlat, qlon @@ -443,9 +443,8 @@ def _qd2apex_nonvectorized(self, qlat, qlon, height): estr += '({:.3g}) for qlat {:.3g}'.format(self.refh, qlat) raise ApexHeightError(estr) - alat = np.sign(qlat) * np.degrees(np.arccos(np.sqrt((self.RE + - self.refh) / - (self.RE + hA)))) + alat = np.sign(qlat) * np.degrees(np.arccos( + np.sqrt((self.RE + self.refh) / (self.RE + hA)))) return alat, alon @@ -611,8 +610,8 @@ def map_to_height(self, glat, glon, height, newheight, conjugate=False, def _map_EV_to_height(self, alat, alon, height, newheight, X, EV): # make sure X is array of correct shape - if (not (np.ndim(X) == 1 and np.size(X) == 3) and - not (np.ndim(X) == 2 and np.shape(X)[0] == 3)): + if (not (np.ndim(X) == 1 and np.size(X) == 3) and not ( + np.ndim(X) == 2 and np.shape(X)[0] == 3)): # raise ValueError because if passing e.g. a (6,) ndarray the # reshape below will work even though the input is invalid raise ValueError(EV + ' must be (3, N) or (3,) ndarray') @@ -870,8 +869,8 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): g1 = ((self.RE + np.float64(height)) / (self.RE + self.refh)) ** (3 / 2) * d1 / F g2 = -1.0 / (2.0 * F * np.tan(np.radians(qlat))) * ( - k + ((self.RE + np.float64(height)) / - (self.RE + self.refh)) * d2 / cosI) + k + ((self.RE + np.float64(height)) + / (self.RE + self.refh)) * d2 / cosI) g3 = k * F f3 = np.cross(g1.T, g2.T).T diff --git a/src/apexpy/helpers.py b/src/apexpy/helpers.py index bebe837d..ef82c4db 100644 --- a/src/apexpy/helpers.py +++ b/src/apexpy/helpers.py @@ -69,7 +69,8 @@ def getsinIm(alat): alat = np.float64(alat) - return 2*np.sin(np.radians(alat))/np.sqrt(4 - 3*np.cos(np.radians(alat))**2) + return 2 * np.sin(np.radians(alat)) / np.sqrt(4 - 3 + * np.cos(np.radians(alat))**2) def getcosIm(alat): @@ -88,7 +89,8 @@ def getcosIm(alat): alat = np.float64(alat) - return np.cos(np.radians(alat))/np.sqrt(4 - 3*np.cos(np.radians(alat))**2) + return np.cos(np.radians(alat)) / np.sqrt(4 - 3 + * np.cos(np.radians(alat))**2) def toYearFraction(date): @@ -115,11 +117,11 @@ def sinceEpoch(date): return time.mktime(date.timetuple()) year = date.year startOfThisYear = dt.datetime(year=year, month=1, day=1) - startOfNextYear = dt.datetime(year=year+1, month=1, day=1) + startOfNextYear = dt.datetime(year=year + 1, month=1, day=1) yearElapsed = sinceEpoch(date) - sinceEpoch(startOfThisYear) yearDuration = sinceEpoch(startOfNextYear) - sinceEpoch(startOfThisYear) - fraction = yearElapsed/yearDuration + fraction = yearElapsed / yearDuration return date.year + fraction @@ -139,7 +141,7 @@ def gc2gdlat(gclat): """ WGS84_e2 = 0.006694379990141317 # WGS84 first eccentricity squared - return np.rad2deg(-np.arctan(np.tan(np.deg2rad(gclat))/(WGS84_e2 - 1))) + return np.rad2deg(-np.arctan(np.tan(np.deg2rad(gclat)) / (WGS84_e2 - 1))) def subsol(datetime): diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 09092762..df7d193d 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -93,7 +93,7 @@ def test__geo2qd_longitude(): apex_out._geo2qd(60, 180, 100)) for i in range(-5, 5): for lat in [0, 30, 60, 90]: - assert_allclose(apex_out._geo2qd(lat, 15+i*360, 100), + assert_allclose(apex_out._geo2qd(lat, 15 + i * 360, 100), fa.apxg2q(lat, 15, 100, 0)[:2]) @@ -129,7 +129,7 @@ def test__geo2apex_longitude(): apex_out._geo2apex(60, 180, 100)) for i in range(-5, 5): for lat in [0, 30, 60, 90]: - assert_allclose(apex_out._geo2apex(lat, 15+i*360, 100), + assert_allclose(apex_out._geo2apex(lat, 15 + i * 360, 100), fa.apxg2all(lat, 15, 100, 300, 0)[2:4]) @@ -200,7 +200,7 @@ def test__qd2geo_longitude(): apex_out._qd2geo(60, 180, 100, 1e-2)) for i in range(-5, 5): for lat in [0, 30, 60, 90]: - assert_allclose(apex_out._qd2geo(lat, 15+i*360, 100, 1e-2), + assert_allclose(apex_out._qd2geo(lat, 15 + i * 360, 100, 1e-2), fa.apxq2g(lat, 15, 100, 1e-2)) @@ -240,7 +240,7 @@ def test__basevec_longitude(): apex_out._basevec(60, 180, 100)) for i in range(-5, 5): for lat in [0, 30, 60, 90]: - assert_allclose(apex_out._basevec(lat, 15+i*360, 100), + assert_allclose(apex_out._basevec(lat, 15 + i * 360, 100), fa.apxg2q(lat, 15, 100, 1)[2:4]) @@ -356,7 +356,7 @@ def test_convert_invalid_lat(): apex_out.convert(90, 0, 'geo', 'geo') apex_out.convert(-90, 0, 'geo', 'geo') - assert_allclose(apex_out.convert(90+1e-5, 0, 'geo', 'apex'), + assert_allclose(apex_out.convert(90 + 1e-5, 0, 'geo', 'apex'), apex_out.convert(90, 0, 'geo', 'apex'), rtol=0, atol=1e-8) @@ -397,7 +397,7 @@ def test_geo2apex_invalid_lat(): apex_out.geo2apex(90, 0, 0) apex_out.geo2apex(-90, 0, 0) - assert_allclose(apex_out.geo2apex(90+1e-5, 0, 0), + assert_allclose(apex_out.geo2apex(90 + 1e-5, 0, 0), apex_out.geo2apex(90, 0, 0), rtol=0, atol=1e-8) @@ -447,7 +447,7 @@ def test_apex2geo_invalid_lat(): apex_out.apex2geo(90, 0, 0, 1e-2) apex_out.apex2geo(-90, 0, 0, 1e-2) - assert_allclose(apex_out.apex2geo(90+1e-5, 0, 0, 1e-2), + assert_allclose(apex_out.apex2geo(90 + 1e-5, 0, 0, 1e-2), apex_out.apex2geo(90, 0, 0, 1e-2), rtol=0, atol=1e-8) @@ -480,7 +480,7 @@ def test_geo2qd_invalid_lat(): apex_out.geo2qd(90, 0, 0) apex_out.geo2qd(-90, 0, 0) - assert_allclose(apex_out.geo2qd(90+1e-5, 0, 0), apex_out.geo2qd(90, 0, 0), + assert_allclose(apex_out.geo2qd(90 + 1e-5, 0, 0), apex_out.geo2qd(90, 0, 0), rtol=0, atol=1e-8) @@ -514,7 +514,7 @@ def test_qd2geo_invalid_lat(): apex_out.qd2geo(90, 0, 0, precision=1e-2) apex_out.qd2geo(-90, 0, 0, precision=1e-2) - assert_allclose(apex_out.qd2geo(90+1e-5, 0, 0, 1e-2), + assert_allclose(apex_out.qd2geo(90 + 1e-5, 0, 0, 1e-2), apex_out.qd2geo(90, 0, 0, 1e-2), rtol=0, atol=1e-8) @@ -548,13 +548,13 @@ def test_apex2qd_invalid_lat(): apex_out.apex2qd(90, 0, 0) apex_out.apex2qd(-90, 0, 0) - assert_allclose(apex_out.apex2qd(90+1e-5, 0, 0), + assert_allclose(apex_out.apex2qd(90 + 1e-5, 0, 0), apex_out.apex2qd(90, 0, 0), rtol=0, atol=1e-8) def test_apex2qd_apexheight_close(): apex_out = Apex(date=2000, refh=300) - apex_out.apex2qd(0, 15, 300+1e-6) + apex_out.apex2qd(0, 15, 300 + 1e-6) def test_apex2qd_apexheight_over(): @@ -593,13 +593,13 @@ def test_qd2apex_invalid_lat(): apex_out.qd2apex(90, 0, 0) apex_out.qd2apex(-90, 0, 0) - assert_allclose(apex_out.qd2apex(90+1e-5, 0, 0), + assert_allclose(apex_out.qd2apex(90 + 1e-5, 0, 0), apex_out.qd2apex(90, 0, 0), rtol=0, atol=1e-8) def test_qd2apex_apexheight_close(): apex_out = Apex(date=2000, refh=300) - assert_allclose(apex_out.qd2apex(0, 15, 300-1e-5), + assert_allclose(apex_out.qd2apex(0, 15, 300 - 1e-5), apex_out.qd2apex(0, 15, 300)) @@ -624,7 +624,7 @@ def test_mlon2mlt_scalar(): def test_mlon2mlt_ssheight(): apex_out = Apex(date=2000, refh=300) mlt = apex_out.mlon2mlt(0, dt.datetime(2000, 2, 3, 4, 5, 6), - ssheight=50*2000) + ssheight=50 * 2000) assert_allclose(mlt, 23.026712036132814) @@ -655,7 +655,7 @@ def test_mlon2mlt_offset(): assert_allclose(apex_out.mlon2mlt(0, date), apex_out.mlon2mlt(-15, date) + 1) assert_allclose(apex_out.mlon2mlt(0, date), - apex_out.mlon2mlt(-10*15, date) + 10) + apex_out.mlon2mlt(-10 * 15, date) + 10) def test_mlon2mlt_range(): @@ -683,7 +683,7 @@ def test_mlt2mlon_scalar(): def test_mlt2mlon_ssheight(): apex_out = Apex(date=2000, refh=300) mlt = apex_out.mlt2mlon(0, dt.datetime(2000, 2, 3, 4, 5, 6), - ssheight=50*2000) + ssheight=50 * 2000) assert_allclose(mlt, 14.599319458007812) @@ -1219,8 +1219,8 @@ def test_basevectors_apex_delta(): e = [e1, e2, e3] for i, j in [(i, j) for i in range(3) for j in range(3)]: delta = 1 if i == j else 0 - assert_allclose(np.sum(f[i]*g[j]), delta, rtol=0, atol=1e-5) - assert_allclose(np.sum(d[i]*e[j]), delta, rtol=0, atol=1e-5) + assert_allclose(np.sum(f[i] * g[j]), delta, rtol=0, atol=1e-5) + assert_allclose(np.sum(d[i] * e[j]), delta, rtol=0, atol=1e-5) def test_basevectors_apex_invalid_scalar(): @@ -1261,7 +1261,7 @@ def test_get_apex_invalid_lat(): apex_out.get_apex(90) apex_out.get_apex(-90) - assert_allclose(apex_out.get_apex(90+1e-5), apex_out.get_apex(90), + assert_allclose(apex_out.get_apex(90 + 1e-5), apex_out.get_apex(90), rtol=0, atol=1e-8) @@ -1337,7 +1337,7 @@ def test_get_babs(): def test_bvectors_apex(): inputs = [[80, 81], [100, 120], [100, 200]] - expected = (np.array([5.94623305e-05, 5.95450722e-05]), + expected = (np.array([5.94623305e-05, 5.95450722e-05]), np.array([[0.02008877, 0.00303204], [0.03571109, 0.03377986], [-0.94045794, -0.89848483]]), diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 575c1644..12e8553a 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -28,17 +28,17 @@ def test_checklat_scalar(): assert helpers.checklat(0) == 0 assert helpers.checklat(-90) == -90 - assert helpers.checklat(90+1e-5) == 90 - assert helpers.checklat(-90-1e-5) == -90 + assert helpers.checklat(90 + 1e-5) == 90 + assert helpers.checklat(-90 - 1e-5) == -90 assert type(helpers.checklat(0.)) == float assert type(helpers.checklat(0)) == int - assert type(helpers.checklat(90+1e-5)) == int + assert type(helpers.checklat(90 + 1e-5)) == int with pytest.raises(ValueError): - helpers.checklat(90+1e-4) + helpers.checklat(90 + 1e-4) with pytest.raises(ValueError): - helpers.checklat(-90-1e-4) + helpers.checklat(-90 - 1e-4) def test_checklat_message(): @@ -51,17 +51,17 @@ def test_checklat_message(): def test_checklat_array(): - assert_allclose(helpers.checklat([-90-1e-5, -90, 0, 90, 90+1e-5]), + assert_allclose(helpers.checklat([-90 - 1e-5, -90, 0, 90, 90 + 1e-5]), np.array([-90, -90, 0, 90, 90]), rtol=0, atol=1e-8) assert type(helpers.checklat([0])) == list assert type(helpers.checklat(np.array([0]))) == np.ndarray with pytest.raises(ValueError): - helpers.checklat([-90-1e-4, -90, 0, 90, 90+1e-5]) + helpers.checklat([-90 - 1e-4, -90, 0, 90, 90 + 1e-5]) with pytest.raises(ValueError): - helpers.checklat([-90-1e-5, -90, 0, 90, 90+1e-4]) + helpers.checklat([-90 - 1e-5, -90, 0, 90, 90 + 1e-4]) # ============================================================================ From a35cf504bb7c625c288e154f2802aa76442fc2ba Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 16:09:52 -0500 Subject: [PATCH 145/226] STY: made flake8 corrections Made flake8 corrections. --- src/apexpy/__init__.py | 7 ++++--- src/apexpy/__main__.py | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/apexpy/__init__.py b/src/apexpy/__init__.py index dc8fef3b..3c842cdf 100644 --- a/src/apexpy/__init__.py +++ b/src/apexpy/__init__.py @@ -2,12 +2,13 @@ from .apex import Apex, ApexHeightError from . import helpers -# below try..catch required for autodoc to work on readthedocs + +# Below try..catch required for autodoc to work on readthedocs try: from . import fortranapex except ImportError: - print("ERROR: fortranapex module could not be imported. " + - "apexpy probably won't work") + print("".join(["ERROR: fortranapex module could not be imported. ", + "apexpy probably won't work"])) __version__ = "1.0.3" diff --git a/src/apexpy/__main__.py b/src/apexpy/__main__.py index 6514149d..7539b488 100644 --- a/src/apexpy/__main__.py +++ b/src/apexpy/__main__.py @@ -56,12 +56,12 @@ def main(): if 'mlt' in [args.source, args.dest] and len(args.date) < 14: desc = 'full date/time YYYYMMDDHHMMSS required for MLT calculations' raise ValueError(desc) - if 9 <= len(args.date) <= 13: - desc = 'full date/time must be given as YYYYMMDDHHMMSS, not ' + \ - 'YYYYMMDDHHMMSS'[:len(args.date)] + if 9 <= len(args.date) and len(args.date) <= 13: + desc = 'full date/time must be given as YYYYMMDDHHMMSS, not ' \ + + 'YYYYMMDDHHMMSS'[:len(args.date)] raise ValueError(desc) datetime = dt.datetime.strptime(args.date, - '%Y%m%d%H%M%S'[:len(args.date)-2]) + '%Y%m%d%H%M%S'[:len(args.date) - 2]) A = apexpy.Apex(date=datetime, refh=args.refh) lats, lons = A.convert(array[:, 0], array[:, 1], args.source, args.dest, args.height, datetime=datetime) From 412de048f9fa89e82d536457c5bc82fecc6a47e7 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 16:10:20 -0500 Subject: [PATCH 146/226] STY: added `long_description_content_type` Added `long_description_content_type` definition. --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index cd268d87..3d262c37 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,6 +4,7 @@ version = 1.0.4 license = MIT description = "A Python wrapper for Apex coordinates" long_description = file: README.rst, CHANGELOG.rst +long_description_content_type = 'text/x-rst' author = Crister van der Meeren, et al. author_email = angeline.burrell@nrll.navy.mil url = https://github.com/aburrell/apexpy From fbdfac7f049a24214bba0d4422221a1862b2a7c4 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 16:10:46 -0500 Subject: [PATCH 147/226] TST: added backend Added backend to toml. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index e85c5edf..073b80eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,3 @@ [build-system] requires = ["numpy", "setuptools", "wheel"] +build-backend = "setuptools.build_meta" From 5379b1f14516609aee28f453274308d0b2a720ee Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 16:14:00 -0500 Subject: [PATCH 148/226] TST: fixed broken tests Fixed broken docs and check tests. --- .travis.yml | 2 +- ci/templates/.travis.yml | 2 +- ci/templates/tox.ini | 8 ++++++-- tox.ini | 8 ++++++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 96a2c9fe..6f45269a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ before_install: - python --version - uname -a - lsb_release -a - - pip install numpy coveralls pytest-cov tox-travis sphinx_rtd_theme + - pip install numpy coveralls pytest-cov tox-travis install: - python setup.py clean --all build_ext --force --inplace diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 3e0a544a..8cd93718 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -21,7 +21,7 @@ before_install: - python --version - uname -a - lsb_release -a - - pip install numpy coveralls pytest-cov tox-travis sphinx_rtd_theme + - pip install numpy coveralls pytest-cov tox-travis install: - python setup.py clean --all build_ext --force --inplace diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index 6fc351b0..17dbd73a 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -27,6 +27,7 @@ commands = [testenv:docs] deps = + sphinx_rtd_theme -r{toxinidir}/docs/requirements.txt commands = sphinx-build {posargs:-E} -b html docs dist/docs @@ -48,14 +49,17 @@ deps = docutils check-manifest flake8 - readme + numpy pygments + readme + twine skip_install = true usedevelop = false commands = - python setup.py check --strict --metadata --restructuredtext + python setup.py sdist check-manifest {toxinidir} flake8 --ignore=F401,W503 src tests + twine check dist/* [testenv:coveralls] deps = diff --git a/tox.ini b/tox.ini index 5e9b2c24..f04662c6 100644 --- a/tox.ini +++ b/tox.ini @@ -34,6 +34,7 @@ commands = [testenv:docs] deps = + sphinx_rtd_theme -r{toxinidir}/docs/requirements.txt commands = sphinx-build {posargs:-E} -b html docs dist/docs @@ -55,14 +56,17 @@ deps = docutils check-manifest flake8 - readme + numpy pygments + readme + twine skip_install = true usedevelop = false commands = - python setup.py check --strict --metadata --restructuredtext + python setup.py sdist check-manifest {toxinidir} flake8 --ignore=F401,W503 src tests + twine check dist/* [testenv:coveralls] deps = From 6042abca1ea70a11e33aa6c3938bb7729f6c499e Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 18 Feb 2021 16:46:35 -0500 Subject: [PATCH 149/226] BUG: fixed formatting error Fixed formatting error in cfg file. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 3d262c37..c43f7916 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,7 +4,7 @@ version = 1.0.4 license = MIT description = "A Python wrapper for Apex coordinates" long_description = file: README.rst, CHANGELOG.rst -long_description_content_type = 'text/x-rst' +long_description_content_type = text/x-rst author = Crister van der Meeren, et al. author_email = angeline.burrell@nrll.navy.mil url = https://github.com/aburrell/apexpy From f302a0c1c81b32e3821b41aa085eb66c3150aee3 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 11:23:16 -0500 Subject: [PATCH 150/226] TST: added coverage run options Added more coverage options. --- setup.cfg | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/setup.cfg b/setup.cfg index c43f7916..4ef87b57 100644 --- a/setup.cfg +++ b/setup.cfg @@ -70,11 +70,14 @@ source = src [coverage:run] branch = True +relative_files = True +include = */apexpy/* + */tests/* source = src -parallel = true +parallel = True [coverage:report] -show_missing = true +show_missing = True precision = 2 omit = *migrations* @@ -83,8 +86,8 @@ max-line-length = 80 exclude = */migrations/*,*/south_migrations/* [tool:pytest] -log_cli=true -log_level=NOTSET +log_cli = True +log_level = NOTSET norecursedirs = .git .tox @@ -99,28 +102,28 @@ python_files = tests.py addopts = -vv - --ignore=src - --ignore=ci - --ignore=docs + --ignore = src + --ignore = ci + --ignore = docs --doctest-modules - --doctest-glob='*.rst' + --doctest-glob = '*.rst' -rxEfsw --strict - --ignore=docs/conf.py - --ignore=setup.py - --ignore=.eggs - --tb=short + --ignore = docs/conf.py + --ignore = setup.py + --ignore = .eggs + --tb = short flake8-ignore = *.py W503 docs/conf.py ALL src/apexpy/__init__.py F401 [isort] -line_length=80 -known_first_party=apexpy -default_section=THIRDPARTY -length_sort=1 -multi_line_output=0 +line_length = 80 +known_first_party = apexpy +default_section = THIRDPARTY +length_sort = 1 +multi_line_output = 0 [matrix] # This is the configuration for the `./bootstrap.py` script. From ed276ae51fb9f4c7f6429b6662a4164dd11888ad Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 11:24:09 -0500 Subject: [PATCH 151/226] TST: addressed appveyor warning Addressed warning raised in appveyor check run by using distutils sdist. --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index cd7314ab..e74c45e6 100644 --- a/setup.py +++ b/setup.py @@ -12,6 +12,7 @@ extensions = [] else: from numpy.distutils.core import setup, Extension + from numpy.distutils.command import sdist extensions = [ Extension(name='apexpy.fortranapex', sources=['src/fortranapex/magfld.f', 'src/fortranapex/apex.f', From 74209d467c8bd8f648e333beb7ad4a97b46eb57b Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 11:24:35 -0500 Subject: [PATCH 152/226] TST: removed check run Removed check run from travis, only run on appveyor. --- .travis.yml | 2 +- ci/templates/.travis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6f45269a..5ae23185 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,7 @@ install: script: - | if [ $TRAVIS_PYTHON_VERSION == "3.7" ]; then - tox -e check,docs + tox -e docs fi - tox -e $TRAVIS_PYTHON_VERSION diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 8cd93718..e7404641 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -30,7 +30,7 @@ install: script: - | if [ $TRAVIS_PYTHON_VERSION == "3.7" ]; then - tox -e check,docs + tox -e docs fi - tox -e $TRAVIS_PYTHON_VERSION From ffa3959d2b43b62becae835cc2dad2dd8bcc456f Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 11:25:19 -0500 Subject: [PATCH 153/226] TST: removed spelling test support Remove support for spelling test, code relies on vaporware. --- docs/conf.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 664bb38b..c2e30138 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,10 +11,6 @@ 'sphinx.ext.napoleon', 'sphinx.ext.extlinks' ] -if os.getenv('SPELLCHECK'): - extensions += 'sphinxcontrib.spelling', - spelling_show_suggestions = True - spelling_lang = 'en_US' source_suffix = '.rst' master_doc = 'index' From 3598ee6ae4400a9b44d426b17ccabbdfcc56e861 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 11:54:50 -0500 Subject: [PATCH 154/226] BUG: removed sdist import Removed the sdist import and the unused sys import. --- setup.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/setup.py b/setup.py index e74c45e6..fce16a1f 100644 --- a/setup.py +++ b/setup.py @@ -5,14 +5,12 @@ from glob import glob from os import path, environ from setuptools import setup, find_packages -import sys # Include extensions only when not on readthedocs.org if environ.get('READTHEDOCS', None) == 'True': extensions = [] else: from numpy.distutils.core import setup, Extension - from numpy.distutils.command import sdist extensions = [ Extension(name='apexpy.fortranapex', sources=['src/fortranapex/magfld.f', 'src/fortranapex/apex.f', From 04d56d1c4e43eac2aaf36ac214f6cc9fa672038f Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 12:02:31 -0500 Subject: [PATCH 155/226] TST: updated pytest command Finished updating pytest commands. --- ci/templates/tox.ini | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index 17dbd73a..1ac0c72c 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -23,7 +23,7 @@ commands = #conda install -q -p {toxworkdir}\{envdir} numpy python setup.py clean --all build_ext --force --inplace # python setup.py develop - {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} + python -m pytest {posargs: -vv --ignore=src --doctest-glob='*.rst'} [testenv:docs] deps = diff --git a/tox.ini b/tox.ini index f04662c6..2faa98b2 100644 --- a/tox.ini +++ b/tox.ini @@ -30,7 +30,7 @@ commands = #conda install -q -p {toxworkdir}\{envdir} numpy python setup.py clean --all build_ext --force --inplace # python setup.py develop - {posargs:py.test -vv --ignore=src --doctest-glob='*.rst'} + python -m pytest {posargs: -vv --ignore=src --doctest-glob='*.rst'} [testenv:docs] deps = From b17cf99a3be639db6ebe43efd179ae691744d715 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 12:09:20 -0500 Subject: [PATCH 156/226] TEST: possible fix in pytest settings Changed capitalized True to all-lower true. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 4ef87b57..10a4cb69 100644 --- a/setup.cfg +++ b/setup.cfg @@ -86,7 +86,7 @@ max-line-length = 80 exclude = */migrations/*,*/south_migrations/* [tool:pytest] -log_cli = True +log_cli = true log_level = NOTSET norecursedirs = .git From 19bdf9987ad7fbac949a0d4d19eb1ebb8cc9bda0 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 12:30:51 -0500 Subject: [PATCH 157/226] BUG: fixed whitespace Removed whitespace for flags. --- setup.cfg | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/setup.cfg b/setup.cfg index 10a4cb69..416bc627 100644 --- a/setup.cfg +++ b/setup.cfg @@ -100,19 +100,20 @@ python_files = test_*.py *_test.py tests.py +# The options below do not allow whitespace between the flag and the equalitys addopts = -vv - --ignore = src - --ignore = ci - --ignore = docs + --ignore=src + --ignore=ci + --ignore=docs --doctest-modules - --doctest-glob = '*.rst' + --doctest-glob='*.rst' -rxEfsw --strict - --ignore = docs/conf.py - --ignore = setup.py - --ignore = .eggs - --tb = short + --ignore=docs/conf.py + --ignore=setup.py + --ignore=.eggs + --tb=short flake8-ignore = *.py W503 docs/conf.py ALL From 74650f3de0d62c36e19fcdc0ce82a8f118e19c38 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 14:38:05 -0500 Subject: [PATCH 158/226] MAINT: updated source ignore Specify ignoring only the Fortran source code. --- ci/templates/tox.ini | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index 1ac0c72c..57ae29b4 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -23,7 +23,7 @@ commands = #conda install -q -p {toxworkdir}\{envdir} numpy python setup.py clean --all build_ext --force --inplace # python setup.py develop - python -m pytest {posargs: -vv --ignore=src --doctest-glob='*.rst'} + python -m pytest {posargs: -vv --ignore=src/fortranapex --doctest-glob='*.rst'} [testenv:docs] deps = diff --git a/tox.ini b/tox.ini index 2faa98b2..6e4f4d6a 100644 --- a/tox.ini +++ b/tox.ini @@ -30,7 +30,7 @@ commands = #conda install -q -p {toxworkdir}\{envdir} numpy python setup.py clean --all build_ext --force --inplace # python setup.py develop - python -m pytest {posargs: -vv --ignore=src --doctest-glob='*.rst'} + python -m pytest {posargs: -vv --ignore=src/fortranapex --doctest-glob='*.rst'} [testenv:docs] deps = From 31d8dd06caac74d5b61502df260592072dc061dd Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 16:38:20 -0500 Subject: [PATCH 159/226] TST: removed strict flag Removed deprecated `strict` flag. --- setup.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index b3f75ce4..ed005def 100644 --- a/setup.cfg +++ b/setup.cfg @@ -52,7 +52,6 @@ addopts = --doctest-modules --doctest-glob='*.rst' -rxEfsw - --strict --ignore=docs/conf.py --ignore=setup.py --ignore=.eggs From 37b58f4309eebec9b2f0466f6ae4d8c921170dfa Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 16:38:39 -0500 Subject: [PATCH 160/226] STY: updated formatting Updated formatting for section headers. --- src/apexpy/apex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index a619b75e..a33aa2a9 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -805,7 +805,7 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): f3, g1, g2, g3, d1, d2, d3, e1, e2, e3 : (3, N) or (3,) ndarray Notes - ---- + ----- `f3`, `g1`, `g2`, and `g3` are not part of the Fortran code by Emmert et al. [2010] [5]_. They are calculated by this Python library according to the following equations in From 92efa169e501e0127ca76c9855d25d75e7a275ae Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 16:39:08 -0500 Subject: [PATCH 161/226] TST: updated filename construction Updated filename construction, using `os.path.join` in all instances. --- tests/test_cmd.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index d795445d..66bae573 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -8,8 +8,9 @@ import subprocess os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')) -outfile = 'tests/output.txt' - +outfile = os.path.join('tests', 'output.txt') +infile = os.path.join('tests', 'test_convert.txt') +singlefile = os.path.join('tests', 'test_convert_single_line.txt') def setup_function(function): try: @@ -23,10 +24,10 @@ def setup_function(function): def test_module_invocation(): pipe = subprocess.Popen(['python', '-m', 'apexpy', 'geo', 'apex', '2015', - '--height', '300', '-i', 'tests/test_convert.txt', - '-o', outfile]) + '--height', '300', '-i', infile, '-o', outfile]) pipe.communicate() pipe.wait() + assert os.path.isfile(outfile) data = np.loadtxt(outfile) np.testing.assert_allclose(data, [[57.469547, 93.639816], @@ -36,8 +37,7 @@ def test_module_invocation(): def test_convert_YYYY(): pipe = subprocess.Popen(['python', '-m', 'apexpy', 'geo', 'apex', '2015', - '--height', '300', '-i', 'tests/test_convert.txt', - '-o', outfile]) + '--height', '300', '-i', infile, '-o', outfile]) pipe.communicate() pipe.wait() assert os.path.isfile(outfile) @@ -49,8 +49,7 @@ def test_convert_YYYY(): def test_convert_YYYYMM(): pipe = subprocess.Popen(['python', '-m', 'apexpy', 'geo', 'apex', '201501', - '--height', '300', '-i', 'tests/test_convert.txt', - '-o', outfile]) + '--height', '300', '-i', infile, '-o', outfile]) pipe.communicate() pipe.wait() assert os.path.isfile(outfile) @@ -63,7 +62,7 @@ def test_convert_YYYYMM(): def test_convert_YYYYMMDD(): pipe = subprocess.Popen(['python', '-m', 'apexpy', 'geo', 'apex', '20150101', '--height', '300', '-i', - 'tests/test_convert.txt', '-o', outfile]) + infile, '-o', outfile]) pipe.communicate() pipe.wait() assert os.path.isfile(outfile) @@ -76,7 +75,7 @@ def test_convert_YYYYMMDD(): def test_convert_YYYYMMDDHHMMSS(): pipe = subprocess.Popen(['python', '-m', 'apexpy', 'geo', 'apex', '20150101000000', '--height', '300', '-i', - 'tests/test_convert.txt', '-o', outfile]) + infile, '-o', outfile]) pipe.communicate() pipe.wait() assert os.path.isfile(outfile) @@ -89,8 +88,7 @@ def test_convert_YYYYMMDDHHMMSS(): def test_convert_single_line(): pipe = subprocess.Popen(['python', '-m', 'apexpy', 'geo', 'apex', '20150101000000', '--height', '300', '-i', - 'tests/test_convert_single_line.txt', '-o', - outfile]) + singlefile, '-o', outfile]) pipe.communicate() pipe.wait() assert os.path.isfile(outfile) @@ -122,8 +120,7 @@ def test_convert_refh(): def test_convert_mlt(): pipe = subprocess.Popen(['python', '-m', 'apexpy', 'geo', 'mlt', '20150101000000', '--height', '300', '-i', - 'tests/test_convert_single_line.txt', '-o', - outfile]) + singlefile, '-o', outfile]) pipe.communicate() pipe.wait() assert os.path.isfile(outfile) From aa90952d3e85b93d2c7abac702603c98cab6e622 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 16:48:29 -0500 Subject: [PATCH 162/226] DOC: clarified parameters Clarified the parameters in the docstring. --- src/apexpy/apex.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index a33aa2a9..4f9e2e97 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -781,9 +781,9 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): Parameters ---------- - lat, lon : (N,) array_like or float - Latitude lat : (N,) array_like or float + Latitude + lon : (N,) array_like or float Longitude height : (N,) array_like or float Altitude in km From d3002f332f1a9b61026887e078c4d3a1e6f5e14a Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 16:52:15 -0500 Subject: [PATCH 163/226] DOC: added a note about the geodetic system Added a note about the geodetic system to the README. --- README.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index f289c686..a2b8a896 100644 --- a/README.rst +++ b/README.rst @@ -7,8 +7,9 @@ Overview This is a Python wrapper for the Apex fortran library by Emmert et al. [2010] [1]_, which allows converting between geodetic, modified apex, and quasi-dipole coordinates as well as getting modified apex and -quasi-dipole base vectors (Richmond [1995] [2]_). MLT calculations are also -included. The package is free software (MIT license). +quasi-dipole base vectors (Richmond [1995] [2]_). The geodetic system used here +is WGS84. MLT calculations are also included. The package is free software +(MIT license). Quick start =========== From 8c5de57bf7cae8ec7d7b57593a2b39b84067e6d9 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 17:00:37 -0500 Subject: [PATCH 164/226] TST: added unit test for equator conversions Added a unit test based on the edge case provided by @bharding512 --- tests/test_Apex.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 09092762..3ff5b12f 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -309,6 +309,17 @@ def test_convert_qd2apex(): assert_allclose(apex_out.convert(60, 15, 'qd', 'apex', height=100), apex_out.qd2apex(60, 15, height=100)) + +def test_convert_qd2apex_at_equator(): + """Test the quasi-dipole to apex conversion at the magnetic equator + """ + apex_out = Apex(date=2000, refh=80) + elat, elon = apex_out.convert(lat=0.0, lon=0, source='qd', dest='apex', + height=120.0) + clat, clon = apex_out.convert(lat=0.001, lon=0, source='qd', dest='apex', + height=120.0) + assert_allclose([elat, elon], [clat, clon], atol=1e-4) + def test_convert_qd2mlt(): datetime = dt.datetime(2000, 3, 9, 14, 25, 58) From 7ad084273f95b22b2ee6c42467ba6fbf128652c5 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 17:06:23 -0500 Subject: [PATCH 165/226] STY: removed trailing whitespace Removed trailing whitespace for PEP8 compliance. --- tests/test_Apex.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 3ff5b12f..80db26e6 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -309,10 +309,9 @@ def test_convert_qd2apex(): assert_allclose(apex_out.convert(60, 15, 'qd', 'apex', height=100), apex_out.qd2apex(60, 15, height=100)) - + def test_convert_qd2apex_at_equator(): - """Test the quasi-dipole to apex conversion at the magnetic equator - """ + """Test the quasi-dipole to apex conversion at the magnetic equator """ apex_out = Apex(date=2000, refh=80) elat, elon = apex_out.convert(lat=0.0, lon=0, source='qd', dest='apex', height=120.0) From 4192f3b50258c42e29af616ce1fe28eab3a3401d Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 19 Feb 2021 17:21:42 -0500 Subject: [PATCH 166/226] STY: removed more trailing whitespace Removed yet more trailing whitespace. --- tests/test_Apex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 80db26e6..c7cc2317 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -318,7 +318,7 @@ def test_convert_qd2apex_at_equator(): clat, clon = apex_out.convert(lat=0.001, lon=0, source='qd', dest='apex', height=120.0) assert_allclose([elat, elon], [clat, clon], atol=1e-4) - + def test_convert_qd2mlt(): datetime = dt.datetime(2000, 3, 9, 14, 25, 58) From 5829657e6b7d02dc66cc5520a4b01f35f56aa185 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Fri, 19 Feb 2021 19:38:50 -0400 Subject: [PATCH 167/226] Update test_cmd.py adding whitespace for pep8 compliance --- tests/test_cmd.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 66bae573..20faa1da 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -12,6 +12,7 @@ infile = os.path.join('tests', 'test_convert.txt') singlefile = os.path.join('tests', 'test_convert_single_line.txt') + def setup_function(function): try: os.remove(outfile) From 12fcc7c1bc81c08a32ecae70922dcdd2c6dbceb3 Mon Sep 17 00:00:00 2001 From: Gregory Starr Date: Fri, 19 Feb 2021 19:42:06 -0400 Subject: [PATCH 168/226] Update test_Apex.py removing trailing whitespace to satisfy codacy --- tests/test_Apex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index c7cc2317..130094a8 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -311,7 +311,7 @@ def test_convert_qd2apex(): def test_convert_qd2apex_at_equator(): - """Test the quasi-dipole to apex conversion at the magnetic equator """ + """Test the quasi-dipole to apex conversion at the magnetic equator""" apex_out = Apex(date=2000, refh=80) elat, elon = apex_out.convert(lat=0.0, lon=0, source='qd', dest='apex', height=120.0) From 738669329ae3dc4fee9c2178f6fcd93b3bc2684f Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 22 Feb 2021 16:18:30 -0500 Subject: [PATCH 169/226] DOC: added Intel options as comments Added Intel compilation options as comments. --- src/fortranapex/Makefile | 4 ++-- src/fortranapex/igrf.f90 | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/fortranapex/Makefile b/src/fortranapex/Makefile index 6013c863..97ef22f4 100644 --- a/src/fortranapex/Makefile +++ b/src/fortranapex/Makefile @@ -2,8 +2,8 @@ # Ver. 1.0. 27/8-10 - Based on Makefile for Einar Stiansen. -FC = gfortran -#FC = mpif90 +# If using Intel compilers, uncomment a line at the top of the igrf.f90 file +FC = gfortran # mpif90, ifort LD = $(FC) LDFLAGS = #-ipo FFLAGS = -O3 -fPIC #-ipo -check bounds -check pointer -check uninit diff --git a/src/fortranapex/igrf.f90 b/src/fortranapex/igrf.f90 index e997a6c8..dcaba09b 100644 --- a/src/fortranapex/igrf.f90 +++ b/src/fortranapex/igrf.f90 @@ -1,3 +1,6 @@ +! Uncomment below if using an Intel compiler +! use ifport + module igrf implicit none From d93a69335e9b8fd0221305a21e9528515c7178b9 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 22 Feb 2021 16:19:17 -0500 Subject: [PATCH 170/226] DOC: added Intel-specific instructions Added instructions for people who really want to use an Intel complier. --- docs/installation.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 3ccd6ee6..2d780571 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -51,7 +51,15 @@ you may install by:: cd apexpy python setup.py --user - + + +Another benefit of installing apexpy from the command line is specifying the +fortran compiler you would like to use. By default, apexpy uses +`gfortran `_, but you can alter the +Makefile in ``src/fortranapex`` to use other compilers or specify different +compilation flags. However, if using an Intel compiler, you will need to +uncomment a line at the top of ``src/fortranapex/igrf.f90`` to ensure all +necessary libraries are imported. .. [1] pip is included with Python 2 from v2.7.9 and Python 3 from v3.4. If you don't have pip, From 1568665b8cc0173eb1284bc8c4aa8bd23c56df77 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 18:32:47 -0400 Subject: [PATCH 171/226] changing missing / invalid latitudes to nan, adding nan tests --- src/apexpy/apex.py | 42 ++++++++++++++++++++++++------------------ src/apexpy/helpers.py | 23 +++-------------------- tests/test_Apex.py | 28 ++++++++++++++++++++++++---- tests/test_helpers.py | 22 +++++++++++++++------- 4 files changed, 66 insertions(+), 49 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index a2364a17..0250afe6 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -20,7 +20,7 @@ raise err # make sure invalid warnings are always shown -warnings.filterwarnings('always', message='.*set to -9999 where*', +warnings.filterwarnings('always', message='.*set to NaN where*', module='apexpy.apex') @@ -243,9 +243,13 @@ def geo2apex(self, glat, glon, height): alat, alon = self._geo2apex(glat, glon, height) - if np.any(np.float64(alat) == -9999): - warnings.warn('Apex latitude set to -9999 where undefined ' + if np.any(alat == -9999): + warnings.warn('Apex latitude set to NaN where undefined ' '(apex height may be < reference height)') + if np.isscalar(alat): + alat = np.nan + else: + alat[alat == -9999] = np.nan # if array is returned, dtype is object, so convert to float return np.float64(alat), np.float64(alon) @@ -389,12 +393,13 @@ def _apex2qd_nonvectorized(self, alat, alon, height): # allow for values that are close hA = height else: - estr = 'height {:.3g} is > apex height '.format(np.max(height)) - estr += '{:.3g} for alat {:.3g}'.format(hA, alat) + estr = f'height {np.max(height):.3g} is > apex height ' \ + f'{hA:.3g} for alat {alat:.3g}' raise ApexHeightError(estr) - qlat = np.sign(alat) * np.degrees(np.arccos(np.sqrt((self.RE + height) - / (self.RE + hA)))) + salat = np.sign(alat) if alat != 0 else 1 + qlat = salat * np.degrees(np.arccos(np.sqrt((self.RE + height) / + (self.RE + hA)))) return qlat, qlon @@ -877,19 +882,20 @@ def basevectors_apex(self, lat, lon, height, coords='geo', precision=1e-10): f3 = np.cross(g1.T, g2.T).T if np.any(alat == -9999): - warnings.warn(''.join(['Base vectors g, d, e, and f3 set to -9999 ', + warnings.warn(''.join(['Base vectors g, d, e, and f3 set to NaN ', 'where apex latitude is undefined (apex ', 'height may be < reference height)'])) - f3 = np.where(alat == -9999, -9999, f3) - g1 = np.where(alat == -9999, -9999, g1) - g2 = np.where(alat == -9999, -9999, g2) - g3 = np.where(alat == -9999, -9999, g3) - d1 = np.where(alat == -9999, -9999, d1) - d2 = np.where(alat == -9999, -9999, d2) - d3 = np.where(alat == -9999, -9999, d3) - e1 = np.where(alat == -9999, -9999, e1) - e2 = np.where(alat == -9999, -9999, e2) - e3 = np.where(alat == -9999, -9999, e3) + mask = alat == -9999 + f3 = np.where(mask, np.nan, f3) + g1 = np.where(mask, np.nan, g1) + g2 = np.where(mask, np.nan, g2) + g3 = np.where(mask, np.nan, g3) + d1 = np.where(mask, np.nan, d1) + d2 = np.where(mask, np.nan, d2) + d3 = np.where(mask, np.nan, d3) + e1 = np.where(mask, np.nan, e1) + e2 = np.where(mask, np.nan, e2) + e3 = np.where(mask, np.nan, e3) return tuple(np.squeeze(x) for x in [f1, f2, f3, g1, g2, g3, d1, d2, d3, e1, e2, e3]) diff --git a/src/apexpy/helpers.py b/src/apexpy/helpers.py index ef82c4db..d5113d01 100644 --- a/src/apexpy/helpers.py +++ b/src/apexpy/helpers.py @@ -31,26 +31,9 @@ def checklat(lat, name='lat'): ValueError if any values are too far outside the range [-90, 90] """ - - if np.all(np.float64(lat) >= -90) and np.all(np.float64(lat) <= 90): - return lat - - if np.isscalar(lat): - if lat > 90 and np.isclose(lat, 90, rtol=0, atol=1e-4): - lat = 90 - return lat - elif lat < -90 and np.isclose(lat, -90, rtol=0, atol=1e-4): - lat = -90 - return lat - else: - lat = np.float64(lat) # make sure we have an array, not list - lat[(lat > 90) & (np.isclose(lat, 90, rtol=0, atol=1e-4))] = 90 - lat[(lat < -90) & (np.isclose(lat, -90, rtol=0, atol=1e-4))] = -90 - if np.all(lat >= -90) and np.all(lat <= 90): - return lat - - # we haven't returned yet, so raise exception - raise ValueError(name + ' must be in [-90, 90]') + if np.any(np.abs(lat) > 90 + 1e-5): + raise ValueError(name + ' must be in [-90, 90]') + return np.clip(lat, -90.0, 90.0) def getsinIm(alat): diff --git a/tests/test_Apex.py b/tests/test_Apex.py index b62055e1..c4beb6e8 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -4,6 +4,7 @@ import datetime as dt import warnings +import itertools import numpy as np import pytest @@ -378,6 +379,25 @@ def test_convert_invalid_transformation(): apex_out.convert(0, 0, 'geo', 'foobar') +coord_names = ['geo', 'apex', 'qd'] + + +@pytest.mark.parametrize('transform', itertools.product(coord_names, coord_names)) +def test_convert_withnan(transform): + N_nans = 5 + in_lat = np.arange(0, 10, dtype=float) + in_lat[:N_nans] = np.nan + in_lon = np.arange(0, 10, dtype=float) + in_lon[:N_nans] = np.nan + src, dest = transform + apex_out = Apex(date=2000, refh=80) + out_lat, out_lon = apex_out.convert(in_lat, in_lon, src, dest, height=120) + assert np.all(np.isnan(out_lat[:N_nans])) + assert np.all(np.isnan(out_lon[:N_nans])) + assert np.all(np.isfinite(out_lat[N_nans:])) + assert np.all(np.isfinite(out_lat[N_nans:])) + + # ============================================================================ # Test the geo2apex() method # ============================================================================ @@ -418,10 +438,10 @@ def test_geo2apex_undefined_warning(): apex_out = Apex(date=2000, refh=10000) ret = apex_out.geo2apex(0, 0, 0) - assert ret[0] == -9999 + assert np.isnan(ret[0]) assert len(wmsg) == 1 assert issubclass(wmsg[-1].category, UserWarning) - assert 'set to -9999 where' in str(wmsg[-1].message) + assert 'set to NaN where' in str(wmsg[-1].message) # ============================================================================ @@ -1241,9 +1261,9 @@ def test_basevectors_apex_invalid_scalar(): base_vecs = apex_out.basevectors_apex(0, 0, 0) assert issubclass(wmsg[-1].category, UserWarning) - assert 'set to -9999 where' in str(wmsg[-1].message) + assert 'set to NaN where' in str(wmsg[-1].message) - invalid = [-9999, -9999, -9999] + invalid = np.ones(3) * np.nan for i, bvec in enumerate(base_vecs): if i < 2: assert not np.allclose(bvec, invalid[:2]) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 12e8553a..bf2201bd 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -31,10 +31,6 @@ def test_checklat_scalar(): assert helpers.checklat(90 + 1e-5) == 90 assert helpers.checklat(-90 - 1e-5) == -90 - assert type(helpers.checklat(0.)) == float - assert type(helpers.checklat(0)) == int - assert type(helpers.checklat(90 + 1e-5)) == int - with pytest.raises(ValueError): helpers.checklat(90 + 1e-4) with pytest.raises(ValueError): @@ -54,9 +50,6 @@ def test_checklat_array(): assert_allclose(helpers.checklat([-90 - 1e-5, -90, 0, 90, 90 + 1e-5]), np.array([-90, -90, 0, 90, 90]), rtol=0, atol=1e-8) - assert type(helpers.checklat([0])) == list - assert type(helpers.checklat(np.array([0]))) == np.ndarray - with pytest.raises(ValueError): helpers.checklat([-90 - 1e-4, -90, 0, 90, 90 + 1e-5]) @@ -64,6 +57,21 @@ def test_checklat_array(): helpers.checklat([-90 - 1e-5, -90, 0, 90, 90 + 1e-4]) +def test_checklat_withnan(): + in_lat = np.array([-90 - 1e-5, -90, 0, 90, 90 + 1e-5, np.nan, np.nan]) + fin_mask = np.isfinite(in_lat) + out_lat = helpers.checklat(in_lat) + assert_allclose(np.array([-90, -90, 0, 90, 90]), out_lat[fin_mask], rtol=0, atol=1e-8) + + assert np.all(np.isnan(out_lat[~fin_mask])) + + with pytest.raises(ValueError): + helpers.checklat([-90 - 1e-4, np.nan, np.nan, 90 + 1e-5]) + + with pytest.raises(ValueError): + helpers.checklat([-90 - 1e-5, np.nan, np.nan, 90 + 1e-4]) + + # ============================================================================ # Test getsinIm # ============================================================================ From 02a35c683a77487d7069ecc1d59d3ed207dcbb79 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 18:57:17 -0400 Subject: [PATCH 172/226] fixing python 2.7 compatibility issue --- src/apexpy/apex.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 0250afe6..26623444 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -393,8 +393,8 @@ def _apex2qd_nonvectorized(self, alat, alon, height): # allow for values that are close hA = height else: - estr = f'height {np.max(height):.3g} is > apex height ' \ - f'{hA:.3g} for alat {alat:.3g}' + estr = 'height {:.3g} is > apex height '.format(np.max(height)) +\ + '{:.3g} for alat {alat:.3g}'.format(hA) raise ApexHeightError(estr) salat = np.sign(alat) if alat != 0 else 1 From b48364aa0cf4502d8c3ccd517838634be95a68d8 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 19:03:18 -0400 Subject: [PATCH 173/226] fixing python 2.7 compatibility issue --- src/apexpy/apex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 26623444..5519ca94 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -394,7 +394,7 @@ def _apex2qd_nonvectorized(self, alat, alon, height): hA = height else: estr = 'height {:.3g} is > apex height '.format(np.max(height)) +\ - '{:.3g} for alat {alat:.3g}'.format(hA) + '{:.3g} for alat {:.3g}'.format(hA, alat) raise ApexHeightError(estr) salat = np.sign(alat) if alat != 0 else 1 From 46c51c34eb72c3a69d1cb34c3930cc8949407ae3 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 19:22:37 -0400 Subject: [PATCH 174/226] fixing 2.7 compatibility issue in apex.py --- src/apexpy/apex.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 5519ca94..9d57d75a 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -243,6 +243,7 @@ def geo2apex(self, glat, glon, height): alat, alon = self._geo2apex(glat, glon, height) + alat = np.float64(alat) if np.any(alat == -9999): warnings.warn('Apex latitude set to NaN where undefined ' '(apex height may be < reference height)') From 1c6628254b56d3a38233647c1f395f4e68587f9c Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 19:37:35 -0400 Subject: [PATCH 175/226] fixing 2.7 compatibility issue in apex.py --- src/apexpy/apex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 9d57d75a..b0e10fa1 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -243,7 +243,7 @@ def geo2apex(self, glat, glon, height): alat, alon = self._geo2apex(glat, glon, height) - alat = np.float64(alat) + print(alat) if np.any(alat == -9999): warnings.warn('Apex latitude set to NaN where undefined ' '(apex height may be < reference height)') From 18cb28760afe38ad40be5ffc635ddc9897501298 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 19:53:42 -0400 Subject: [PATCH 176/226] getting appveyor working again --- appveyor.yml | 21 ++++++++++----------- ci/templates/appveyor.yml | 4 ++-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index c1255264..e163e279 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,52 +6,52 @@ environment: - TESTENV: '2.7-nocover-64' PYTHON_VERSION: '2.7' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'python -m pytest' + TESTSCRIPT: 'python -m pytest tests' - TESTENV: '2.7-nocover-32' PYTHON_VERSION: '2.7' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'python -m pytest' + TESTSCRIPT: 'python -m pytest tests' - TESTENV: '3.6-nocover-64' PYTHON_VERSION: '3.6' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'python -m pytest' + TESTSCRIPT: 'python -m pytest tests' - TESTENV: '3.6-nocover-32' PYTHON_VERSION: '3.6' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'python -m pytest' + TESTSCRIPT: 'python -m pytest tests' - TESTENV: '3.7-nocover-64' PYTHON_VERSION: '3.7' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'python -m pytest' + TESTSCRIPT: 'python -m pytest tests' - TESTENV: '3.7-nocover-32' PYTHON_VERSION: '3.7' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'python -m pytest' + TESTSCRIPT: 'python -m pytest tests' - TESTENV: '3.8-nocover-64' PYTHON_VERSION: '3.8' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'python -m pytest' + TESTSCRIPT: 'python -m pytest tests' - TESTENV: '3.8-nocover-32' PYTHON_VERSION: '3.8' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'python -m pytest' + TESTSCRIPT: 'python -m pytest tests' - TESTENV: '3.9-nocover-64' PYTHON_VERSION: '3.9' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'python -m pytest' + TESTSCRIPT: 'python -m pytest tests' - TESTENV: '3.9-nocover-32' PYTHON_VERSION: '3.9' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'python -m pytest' + TESTSCRIPT: 'python -m pytest tests' - TESTENV: 'check' PYTHON_VERSION: '3.7' @@ -99,4 +99,3 @@ artifacts: ### 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 c8842a10..d58ecdf2 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/appveyor.yml @@ -7,12 +7,12 @@ environment: - TESTENV: '{{ env }}-64' PYTHON_VERSION: '{{ config.python[-3:] }}' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'python -m pytest' + TESTSCRIPT: 'python -m pytest tests' - TESTENV: '{{ env }}-32' PYTHON_VERSION: '{{ config.python[-3:] }}' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'python -m pytest' + TESTSCRIPT: 'python -m pytest tests' {% endif %}{% endfor %} - TESTENV: 'check' From 1b3b506e5462e72e0032197aef051246d764f924 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 20:08:25 -0400 Subject: [PATCH 177/226] trying to get appveyor to work --- appveyor.yml | 21 +++++++++++---------- ci/templates/appveyor.yml | 4 ++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index e163e279..035da006 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,52 +6,52 @@ environment: - TESTENV: '2.7-nocover-64' PYTHON_VERSION: '2.7' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'python -m pytest tests' + TESTSCRIPT: 'py.test' - TESTENV: '2.7-nocover-32' PYTHON_VERSION: '2.7' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'python -m pytest tests' + TESTSCRIPT: 'py.test' - TESTENV: '3.6-nocover-64' PYTHON_VERSION: '3.6' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'python -m pytest tests' + TESTSCRIPT: 'py.test' - TESTENV: '3.6-nocover-32' PYTHON_VERSION: '3.6' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'python -m pytest tests' + TESTSCRIPT: 'py.test' - TESTENV: '3.7-nocover-64' PYTHON_VERSION: '3.7' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'python -m pytest tests' + TESTSCRIPT: 'py.test' - TESTENV: '3.7-nocover-32' PYTHON_VERSION: '3.7' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'python -m pytest tests' + TESTSCRIPT: 'py.test' - TESTENV: '3.8-nocover-64' PYTHON_VERSION: '3.8' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'python -m pytest tests' + TESTSCRIPT: 'py.test' - TESTENV: '3.8-nocover-32' PYTHON_VERSION: '3.8' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'python -m pytest tests' + TESTSCRIPT: 'py.test' - TESTENV: '3.9-nocover-64' PYTHON_VERSION: '3.9' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'python -m pytest tests' + TESTSCRIPT: 'py.test' - TESTENV: '3.9-nocover-32' PYTHON_VERSION: '3.9' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'python -m pytest tests' + TESTSCRIPT: 'py.test' - TESTENV: 'check' PYTHON_VERSION: '3.7' @@ -99,3 +99,4 @@ artifacts: ### 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 d58ecdf2..8df26ead 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/appveyor.yml @@ -7,12 +7,12 @@ environment: - TESTENV: '{{ env }}-64' PYTHON_VERSION: '{{ config.python[-3:] }}' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'python -m pytest tests' + TESTSCRIPT: 'py.test' - TESTENV: '{{ env }}-32' PYTHON_VERSION: '{{ config.python[-3:] }}' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'python -m pytest tests' + TESTSCRIPT: 'py.test' {% endif %}{% endfor %} - TESTENV: 'check' From c76144b9cc33a62599f2a73d3c92d4a469e63d0c Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 20:27:46 -0400 Subject: [PATCH 178/226] removing print statement --- src/apexpy/apex.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index b0e10fa1..5519ca94 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -243,7 +243,6 @@ def geo2apex(self, glat, glon, height): alat, alon = self._geo2apex(glat, glon, height) - print(alat) if np.any(alat == -9999): warnings.warn('Apex latitude set to NaN where undefined ' '(apex height may be < reference height)') From 441c791a1b413bc4cf66ea554eb25a33970730ca Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 20:38:52 -0400 Subject: [PATCH 179/226] trying to fix appveyor --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 5c663397..416bc627 100644 --- a/setup.cfg +++ b/setup.cfg @@ -109,6 +109,7 @@ addopts = --doctest-modules --doctest-glob='*.rst' -rxEfsw + --strict --ignore=docs/conf.py --ignore=setup.py --ignore=.eggs From 1bac017abc657f3e633215fb648b5fb6787a3f28 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 20:47:17 -0400 Subject: [PATCH 180/226] experiment to see if appveyor is working --- tests/test_helpers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index bf2201bd..06df055b 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -200,5 +200,9 @@ def test_subsol_array(): assert sslon[i] == true_sslon +def test_fail(): + assert False + + if __name__ == '__main__': pytest.main() From 8305ce341a33d858513c6a4719de8fa6f7ce83f3 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 21:17:43 -0400 Subject: [PATCH 181/226] fixing setup.py install issue --- setup.cfg | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 416bc627..98eeb12c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,7 +51,9 @@ console_scripts = apexpy = apexpy.__main__:main [options.package_data] -apexpy = apexsh.dat +apexpy = + apexsh.dat + igrf13coeffs.txt [aliases] release = register clean --all sdist From 18c9221f7a136e24867b7e85431cd115233e2c44 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 21:19:53 -0400 Subject: [PATCH 182/226] cleaning up MR --- appveyor.yml | 20 ++++++++++---------- ci/templates/appveyor.yml | 4 ++-- setup.cfg | 1 - tests/test_helpers.py | 4 ---- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 035da006..c1255264 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,52 +6,52 @@ environment: - TESTENV: '2.7-nocover-64' PYTHON_VERSION: '2.7' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '2.7-nocover-32' PYTHON_VERSION: '2.7' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.6-nocover-64' PYTHON_VERSION: '3.6' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.6-nocover-32' PYTHON_VERSION: '3.6' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.7-nocover-64' PYTHON_VERSION: '3.7' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.7-nocover-32' PYTHON_VERSION: '3.7' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.8-nocover-64' PYTHON_VERSION: '3.8' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.8-nocover-32' PYTHON_VERSION: '3.8' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.9-nocover-64' PYTHON_VERSION: '3.9' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '3.9-nocover-32' PYTHON_VERSION: '3.9' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: 'check' PYTHON_VERSION: '3.7' diff --git a/ci/templates/appveyor.yml b/ci/templates/appveyor.yml index 8df26ead..c8842a10 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/appveyor.yml @@ -7,12 +7,12 @@ environment: - TESTENV: '{{ env }}-64' PYTHON_VERSION: '{{ config.python[-3:] }}' MINICONDA_HOME: C:\Miniconda-x64 - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' - TESTENV: '{{ env }}-32' PYTHON_VERSION: '{{ config.python[-3:] }}' MINICONDA_HOME: C:\Miniconda - TESTSCRIPT: 'py.test' + TESTSCRIPT: 'python -m pytest' {% endif %}{% endfor %} - TESTENV: 'check' diff --git a/setup.cfg b/setup.cfg index 98eeb12c..88dc9a01 100644 --- a/setup.cfg +++ b/setup.cfg @@ -111,7 +111,6 @@ addopts = --doctest-modules --doctest-glob='*.rst' -rxEfsw - --strict --ignore=docs/conf.py --ignore=setup.py --ignore=.eggs diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 06df055b..bf2201bd 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -200,9 +200,5 @@ def test_subsol_array(): assert sslon[i] == true_sslon -def test_fail(): - assert False - - if __name__ == '__main__': pytest.main() From d9f521f91ae65060b1ee9642d15955528f98eef8 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 21:53:44 -0400 Subject: [PATCH 183/226] fixing the undefined test --- tests/test_Apex.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index c4beb6e8..6c57ad50 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -431,17 +431,16 @@ def test_geo2apex_invalid_lat(): apex_out.geo2apex(90, 0, 0), rtol=0, atol=1e-8) -def test_geo2apex_undefined_warning(): +def test_geo2apex_undefined_warning(recwarn): """Test warning and fill values for an undefined location """ - with warnings.catch_warnings(record=True) as wmsg: - apex_out = Apex(date=2000, refh=10000) - ret = apex_out.geo2apex(0, 0, 0) + apex_out = Apex(date=2000, refh=10000) + ret = apex_out.geo2apex(0, 0, 0) assert np.isnan(ret[0]) - assert len(wmsg) == 1 - assert issubclass(wmsg[-1].category, UserWarning) - assert 'set to NaN where' in str(wmsg[-1].message) + assert len(recwarn) == 1 + assert issubclass(recwarn[-1].category, UserWarning) + assert 'set to NaN where' in str(recwarn[-1].message) # ============================================================================ @@ -1253,15 +1252,14 @@ def test_basevectors_apex_delta(): assert_allclose(np.sum(d[i] * e[j]), delta, rtol=0, atol=1e-5) -def test_basevectors_apex_invalid_scalar(): +def test_basevectors_apex_invalid_scalar(recwarn): """ Test warning and fill values for calculating base vectors with bad value """ apex_out = Apex(date=2000, refh=10000) - with warnings.catch_warnings(record=True) as wmsg: - base_vecs = apex_out.basevectors_apex(0, 0, 0) + base_vecs = apex_out.basevectors_apex(0, 0, 0) - assert issubclass(wmsg[-1].category, UserWarning) - assert 'set to NaN where' in str(wmsg[-1].message) + assert issubclass(recwarn[-1].category, UserWarning) + assert 'set to NaN where' in str(recwarn[-1].message) invalid = np.ones(3) * np.nan for i, bvec in enumerate(base_vecs): From af851ba9a48e2eec98eed4df0aad804bae54d9ff Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 22:17:58 -0400 Subject: [PATCH 184/226] adding error checking to apex.py to avoid silent missing file error in igrf.f90 --- src/apexpy/apex.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 5519ca94..78e8a5a7 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -938,6 +938,8 @@ def set_epoch(self, year): self.year = np.float64(year) fa.loadapxsh(self.datafile, self.year) igrf_fn = os.path.join(os.path.dirname(__file__), 'igrf13coeffs.txt') + if not os.path.exists(igrf_fn): + raise OSError("File {} does not exist".format(igrf_fn)) fa.cofrm(self.year, igrf_fn) def set_refh(self, refh): From 7d008ca0d9373ee7fdfe23641812d62853044eb1 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Mon, 22 Feb 2021 22:57:09 -0400 Subject: [PATCH 185/226] fixing pep8 issues, moving pep8 checker so it runs first --- appveyor.yml | 10 +++++----- ci/templates/appveyor.yml | 10 +++++----- src/apexpy/apex.py | 4 ++-- tests/test_Apex.py | 4 ++-- tests/test_helpers.py | 3 ++- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index c1255264..2d36f949 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,6 +3,11 @@ build: off configuration: Release environment: matrix: + - TESTENV: 'check' + PYTHON_VERSION: '3.7' + MINICONDA_HOME: 'C:\Miniconda' + TESTSCRIPT: 'python setup.py check --strict --metadata --restructuredtext && check-manifest && flake8 src tests' + INSTALL_EXTRA_DEPS: 'pip install docutils check-manifest flake8 readme pygments sphinx_rtd_theme' - TESTENV: '2.7-nocover-64' PYTHON_VERSION: '2.7' MINICONDA_HOME: C:\Miniconda-x64 @@ -53,11 +58,6 @@ environment: MINICONDA_HOME: C:\Miniconda TESTSCRIPT: 'python -m pytest' - - TESTENV: 'check' - PYTHON_VERSION: '3.7' - MINICONDA_HOME: 'C:\Miniconda' - TESTSCRIPT: 'python setup.py check --strict --metadata --restructuredtext && check-manifest && flake8 src tests' - INSTALL_EXTRA_DEPS: 'pip install docutils check-manifest flake8 readme pygments sphinx_rtd_theme' init: - ps: echo $env:TESTENV diff --git a/ci/templates/appveyor.yml b/ci/templates/appveyor.yml index c8842a10..817f71d1 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/appveyor.yml @@ -3,6 +3,11 @@ build: off configuration: Release environment: matrix: + - TESTENV: 'check' + PYTHON_VERSION: '3.7' + MINICONDA_HOME: 'C:\Miniconda' + TESTSCRIPT: 'python setup.py check --strict --metadata --restructuredtext && check-manifest && flake8 src tests' + INSTALL_EXTRA_DEPS: 'pip install docutils check-manifest flake8 readme pygments sphinx_rtd_theme' {% for env, config in tox_environments|dictsort %}{% if not config.cover %} - TESTENV: '{{ env }}-64' PYTHON_VERSION: '{{ config.python[-3:] }}' @@ -15,11 +20,6 @@ environment: TESTSCRIPT: 'python -m pytest' {% endif %}{% endfor %} - - TESTENV: 'check' - PYTHON_VERSION: '3.7' - MINICONDA_HOME: 'C:\Miniconda' - TESTSCRIPT: 'python setup.py check --strict --metadata --restructuredtext && check-manifest && flake8 src tests' - INSTALL_EXTRA_DEPS: 'pip install docutils check-manifest flake8 readme pygments sphinx_rtd_theme' init: - ps: echo $env:TESTENV diff --git a/src/apexpy/apex.py b/src/apexpy/apex.py index 78e8a5a7..a7ccd777 100644 --- a/src/apexpy/apex.py +++ b/src/apexpy/apex.py @@ -393,8 +393,8 @@ def _apex2qd_nonvectorized(self, alat, alon, height): # allow for values that are close hA = height else: - estr = 'height {:.3g} is > apex height '.format(np.max(height)) +\ - '{:.3g} for alat {:.3g}'.format(hA, alat) + estr = 'height {:.3g} is > apex height'.format(np.max(height))\ + + ' {:.3g} for alat {:.3g}'.format(hA, alat) raise ApexHeightError(estr) salat = np.sign(alat) if alat != 0 else 1 diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 6c57ad50..ef8d1180 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -3,7 +3,6 @@ from __future__ import division, absolute_import, unicode_literals import datetime as dt -import warnings import itertools import numpy as np @@ -382,7 +381,8 @@ def test_convert_invalid_transformation(): coord_names = ['geo', 'apex', 'qd'] -@pytest.mark.parametrize('transform', itertools.product(coord_names, coord_names)) +@pytest.mark.parametrize('transform', itertools.product(coord_names, + coord_names)) def test_convert_withnan(transform): N_nans = 5 in_lat = np.arange(0, 10, dtype=float) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index bf2201bd..53803442 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -61,7 +61,8 @@ def test_checklat_withnan(): in_lat = np.array([-90 - 1e-5, -90, 0, 90, 90 + 1e-5, np.nan, np.nan]) fin_mask = np.isfinite(in_lat) out_lat = helpers.checklat(in_lat) - assert_allclose(np.array([-90, -90, 0, 90, 90]), out_lat[fin_mask], rtol=0, atol=1e-8) + assert_allclose(np.array([-90, -90, 0, 90, 90]), out_lat[fin_mask], + rtol=0, atol=1e-8) assert np.all(np.isnan(out_lat[~fin_mask])) From 52cddd31993582ad69888e45417e0dd8487fbe35 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 23 Feb 2021 10:22:38 -0500 Subject: [PATCH 186/226] TST: parametrized more unit tests Made small style changes, added some missing docstrings, and parametrized unit tests for helpers.checklat. --- src/apexpy/helpers.py | 1 + tests/test_Apex.py | 15 ++++++------ tests/test_helpers.py | 56 +++++++++++++++++++++---------------------- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/src/apexpy/helpers.py b/src/apexpy/helpers.py index d5113d01..76a47b42 100644 --- a/src/apexpy/helpers.py +++ b/src/apexpy/helpers.py @@ -33,6 +33,7 @@ def checklat(lat, name='lat'): """ if np.any(np.abs(lat) > 90 + 1e-5): raise ValueError(name + ' must be in [-90, 90]') + return np.clip(lat, -90.0, 90.0) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index ef8d1180..a7fd93c5 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -384,18 +384,19 @@ def test_convert_invalid_transformation(): @pytest.mark.parametrize('transform', itertools.product(coord_names, coord_names)) def test_convert_withnan(transform): - N_nans = 5 + """Test Apex.convert success with NaN input""" + num_nans = 5 in_lat = np.arange(0, 10, dtype=float) - in_lat[:N_nans] = np.nan + in_lat[:num_nans] = np.nan in_lon = np.arange(0, 10, dtype=float) - in_lon[:N_nans] = np.nan + in_lon[:num_nans] = np.nan src, dest = transform apex_out = Apex(date=2000, refh=80) out_lat, out_lon = apex_out.convert(in_lat, in_lon, src, dest, height=120) - assert np.all(np.isnan(out_lat[:N_nans])) - assert np.all(np.isnan(out_lon[:N_nans])) - assert np.all(np.isfinite(out_lat[N_nans:])) - assert np.all(np.isfinite(out_lat[N_nans:])) + assert np.all(np.isnan(out_lat[:num_nans])) + assert np.all(np.isnan(out_lon[:num_nans])) + assert np.all(np.isfinite(out_lat[num_nans:])) + assert np.all(np.isfinite(out_lat[num_nans:])) # ============================================================================ diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 53803442..e420a77b 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -23,41 +23,47 @@ # Test checklat # ============================================================================ -def test_checklat_scalar(): - assert helpers.checklat(90) == 90 - assert helpers.checklat(0) == 0 - assert helpers.checklat(-90) == -90 +@pytest.mark.parametrize('lat', [(90), (0), (-90), (np.nan)]) +def test_checklat_scalar(lat): + """Test good latitude check with scalars""" + if np.isnan(lat): + assert np.isnan(helpers.checklat(lat)) + else: + assert helpers.checklat(lat) == lat - assert helpers.checklat(90 + 1e-5) == 90 - assert helpers.checklat(-90 - 1e-5) == -90 - with pytest.raises(ValueError): - helpers.checklat(90 + 1e-4) - with pytest.raises(ValueError): - helpers.checklat(-90 - 1e-4) +@pytest.mark.parametrize('lat', [(90 + 1e-5), (-90 - 1e-5)]) +def test_checklat_scalar_clip(lat): + """Test good latitude check with scalars just beyond the lat limits""" + assert helpers.checklat(lat) == np.sign(lat) * np.floor(abs(lat)) + +@pytest.mark.parametrize('in_args,msg', + [([90 + 1e-4], "lat must be in"), + ([-90 - 1e-4, 'glat'], "glat must be in"), + ([[-90 - 1e-5, -90, 0, 90, 90 + 1e-4], 'glat'], + "glat must be in"), + ([[-90 - 1e-4, -90, np.nan, np.nan, 90 + 1e-5]], + 'lat must be in')]) +def test_checklat_error(in_args, msg): + """Test bad latitude raises ValueError with appropriate message""" + with pytest.raises(ValueError) as verr: + helpers.checklat(*in_args) -def test_checklat_message(): - with pytest.raises(ValueError) as excinfo: - helpers.checklat(100) - assert str(excinfo.value).startswith('lat must be in') - with pytest.raises(ValueError) as excinfo: - helpers.checklat(100, name='glat') - assert str(excinfo.value).startswith('glat') + assert str(verr.value).startswith(msg) def test_checklat_array(): + """Test good latitude with finite values""" assert_allclose(helpers.checklat([-90 - 1e-5, -90, 0, 90, 90 + 1e-5]), np.array([-90, -90, 0, 90, 90]), rtol=0, atol=1e-8) - with pytest.raises(ValueError): - helpers.checklat([-90 - 1e-4, -90, 0, 90, 90 + 1e-5]) + return - with pytest.raises(ValueError): - helpers.checklat([-90 - 1e-5, -90, 0, 90, 90 + 1e-4]) +def test_checklat_array_withnan(): + """Test good latitude input mixed with NaNs""" -def test_checklat_withnan(): in_lat = np.array([-90 - 1e-5, -90, 0, 90, 90 + 1e-5, np.nan, np.nan]) fin_mask = np.isfinite(in_lat) out_lat = helpers.checklat(in_lat) @@ -66,12 +72,6 @@ def test_checklat_withnan(): assert np.all(np.isnan(out_lat[~fin_mask])) - with pytest.raises(ValueError): - helpers.checklat([-90 - 1e-4, np.nan, np.nan, 90 + 1e-5]) - - with pytest.raises(ValueError): - helpers.checklat([-90 - 1e-5, np.nan, np.nan, 90 + 1e-4]) - # ============================================================================ # Test getsinIm From 7faa885e8f3a8e8e59d56a711655f5510b6effb9 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 23 Feb 2021 10:39:59 -0500 Subject: [PATCH 187/226] STY: implemented codacy suggestions Implemented codacy suggestions for docstrings. --- tests/test_Apex.py | 12 +++++------- tests/test_helpers.py | 14 +++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index a7fd93c5..825be988 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -311,7 +311,7 @@ def test_convert_qd2apex(): def test_convert_qd2apex_at_equator(): - """Test the quasi-dipole to apex conversion at the magnetic equator""" + """Test the quasi-dipole to apex conversion at the magnetic equator.""" apex_out = Apex(date=2000, refh=80) elat, elon = apex_out.convert(lat=0.0, lon=0, source='qd', dest='apex', height=120.0) @@ -384,7 +384,7 @@ def test_convert_invalid_transformation(): @pytest.mark.parametrize('transform', itertools.product(coord_names, coord_names)) def test_convert_withnan(transform): - """Test Apex.convert success with NaN input""" + """Test Apex.convert success with NaN input.""" num_nans = 5 in_lat = np.arange(0, 10, dtype=float) in_lat[:num_nans] = np.nan @@ -433,8 +433,7 @@ def test_geo2apex_invalid_lat(): def test_geo2apex_undefined_warning(recwarn): - """Test warning and fill values for an undefined location - """ + """Test warning and fill values for an undefined location.""" apex_out = Apex(date=2000, refh=10000) ret = apex_out.geo2apex(0, 0, 0) @@ -805,8 +804,7 @@ def test_map_to_height_same_height(): def test_map_to_height_conjugate(): - """Test results of map_to_height using conjugacy - """ + """Test results of map_to_height using conjugacy.""" apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.map_to_height(60, 15, 100, 10000, conjugate=True, precision=1e-10), @@ -1254,7 +1252,7 @@ def test_basevectors_apex_delta(): def test_basevectors_apex_invalid_scalar(recwarn): - """ Test warning and fill values for calculating base vectors with bad value + """Test warning and fill values for calculating base vectors with bad value. """ apex_out = Apex(date=2000, refh=10000) base_vecs = apex_out.basevectors_apex(0, 0, 0) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index e420a77b..bd54f786 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -25,7 +25,7 @@ @pytest.mark.parametrize('lat', [(90), (0), (-90), (np.nan)]) def test_checklat_scalar(lat): - """Test good latitude check with scalars""" + """Test good latitude check with scalars.""" if np.isnan(lat): assert np.isnan(helpers.checklat(lat)) else: @@ -34,7 +34,7 @@ def test_checklat_scalar(lat): @pytest.mark.parametrize('lat', [(90 + 1e-5), (-90 - 1e-5)]) def test_checklat_scalar_clip(lat): - """Test good latitude check with scalars just beyond the lat limits""" + """Test good latitude check with scalars just beyond the lat limits.""" assert helpers.checklat(lat) == np.sign(lat) * np.floor(abs(lat)) @@ -46,7 +46,7 @@ def test_checklat_scalar_clip(lat): ([[-90 - 1e-4, -90, np.nan, np.nan, 90 + 1e-5]], 'lat must be in')]) def test_checklat_error(in_args, msg): - """Test bad latitude raises ValueError with appropriate message""" + """Test bad latitude raises ValueError with appropriate message.""" with pytest.raises(ValueError) as verr: helpers.checklat(*in_args) @@ -54,7 +54,7 @@ def test_checklat_error(in_args, msg): def test_checklat_array(): - """Test good latitude with finite values""" + """Test good latitude with finite values.""" assert_allclose(helpers.checklat([-90 - 1e-5, -90, 0, 90, 90 + 1e-5]), np.array([-90, -90, 0, 90, 90]), rtol=0, atol=1e-8) @@ -62,7 +62,7 @@ def test_checklat_array(): def test_checklat_array_withnan(): - """Test good latitude input mixed with NaNs""" + """Test good latitude input mixed with NaNs.""" in_lat = np.array([-90 - 1e-5, -90, 0, 90, 90 + 1e-5, np.nan, np.nan]) fin_mask = np.isfinite(in_lat) @@ -166,7 +166,7 @@ def test_subsol(): def datetime64_to_datetime(dt64): - """Convert numpy datetime64 object to a datetime datetime object + """Convert numpy datetime64 object to a datetime datetime object. Notes ----- @@ -183,7 +183,7 @@ def datetime64_to_datetime(dt64): def test_subsol_array(): - """Verify subsolar point calculation using an array of np.datetime64 + """Verify subsolar point calculation using an array of np.datetime64. Notes ----- From d5f3107348557d01fa985628413db901cf267013 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 23 Feb 2021 10:58:19 -0500 Subject: [PATCH 188/226] TST: added unit test for new error catch Added a unit test for a missing IGRF coefficient file. --- tests/test_Apex.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 825be988..536a759d 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -4,10 +4,10 @@ import datetime as dt import itertools - import numpy as np -import pytest from numpy.testing import assert_allclose +import os +import pytest from apexpy import fortranapex as fa from apexpy import Apex, ApexHeightError, helpers @@ -1298,6 +1298,7 @@ def test_get_apex_invalid_lat(): def test_set_epoch(): + """Test successful setting of Apex epoch.""" apex_out = Apex(date=2000.2, refh=300) assert_allclose(apex_out.year, 2000.2) ret_2000_2_py = apex_out._geo2apex(60, 15, 100) @@ -1318,6 +1319,29 @@ def test_set_epoch(): assert_allclose(ret_2000_8_py, ret_2000_8_apex) +def test_set_epoch_file_error(): + """Test raises OSError when IGRF coefficient file is missing.""" + + # Ensure the coefficient file exists + igrf_file = os.path.join(os.path.dirname(helpers.__file__), + 'igrf13coeffs.txt') + tmp_file = "temp_coeff.txt" + assert os.path.isfile(igrf_file) + + # Move the coefficient file + os.rename(igrf_file, tmp_file) + + # Test missing coefficient file failure + with pytest.Raises(OSError) as oerr: + Apex(date=2000.2, refh=300) + + assert str(oerr.value).startswith( + "File {:} does not exist".format(igrf_file)) + + # Move the coefficient file back + os.rename(tmp_file, igrf_file) + + # ============================================================================ # Test the set_refh() method # ============================================================================ From b83a5dd1c43ad3626f4c15fab9baf3f091d3a70b Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 23 Feb 2021 11:08:07 -0500 Subject: [PATCH 189/226] BUG: fixed pytest call Fixed call to pytest.raises. --- tests/test_Apex.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 536a759d..50e7150d 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -1321,7 +1321,6 @@ def test_set_epoch(): def test_set_epoch_file_error(): """Test raises OSError when IGRF coefficient file is missing.""" - # Ensure the coefficient file exists igrf_file = os.path.join(os.path.dirname(helpers.__file__), 'igrf13coeffs.txt') @@ -1332,7 +1331,7 @@ def test_set_epoch_file_error(): os.rename(igrf_file, tmp_file) # Test missing coefficient file failure - with pytest.Raises(OSError) as oerr: + with pytest.raises(OSError) as oerr: Apex(date=2000.2, refh=300) assert str(oerr.value).startswith( From bb778d5c87a0c33d7c895be2bb0ed00c533fcf65 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 23 Feb 2021 11:54:22 -0500 Subject: [PATCH 190/226] MAINT: updated gitignore Added local f2py wrappers to .gitignore. --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index fc800807..a2a7645b 100644 --- a/.gitignore +++ b/.gitignore @@ -92,3 +92,8 @@ docs/_build *.exe *.out *.app + +# Local f2py wrappers +src/fortranapex/fortranapex-f2pywrappers.f* +src/fortranapex/fortranapex-f2pywrappers2.f* +src/fortranapex/fortranapexmodule.c From 441e776c3ad49a851b0b94f8de34c80a26298cad Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Tue, 23 Feb 2021 13:52:32 -0400 Subject: [PATCH 191/226] making test_set_epoch_file_error more robust --- tests/test_Apex.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 50e7150d..6bc747d0 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -1319,26 +1319,26 @@ def test_set_epoch(): assert_allclose(ret_2000_8_py, ret_2000_8_apex) -def test_set_epoch_file_error(): - """Test raises OSError when IGRF coefficient file is missing.""" +@pytest.fixture() +def igrf_file(): # Ensure the coefficient file exists - igrf_file = os.path.join(os.path.dirname(helpers.__file__), - 'igrf13coeffs.txt') + original_file = os.path.join(os.path.dirname(helpers.__file__), + 'igrf13coeffs.txt') tmp_file = "temp_coeff.txt" - assert os.path.isfile(igrf_file) - + assert os.path.isfile(original_file) # Move the coefficient file - os.rename(igrf_file, tmp_file) + os.rename(original_file, tmp_file) + yield original_file + # Move the coefficient file back + os.rename(tmp_file, original_file) + +def test_set_epoch_file_error(igrf_file): + """Test raises OSError when IGRF coefficient file is missing.""" # Test missing coefficient file failure with pytest.raises(OSError) as oerr: Apex(date=2000.2, refh=300) - - assert str(oerr.value).startswith( - "File {:} does not exist".format(igrf_file)) - - # Move the coefficient file back - os.rename(tmp_file, igrf_file) + assert str(oerr.value).startswith("File {:} does not exist".format(igrf_file)) # ============================================================================ From 8dfa7e53a2c049f0210f0aca8f5aa49a727851e1 Mon Sep 17 00:00:00 2001 From: Greg Starr Date: Tue, 23 Feb 2021 14:39:52 -0400 Subject: [PATCH 192/226] fixing test_Apex.py for pep8 --- tests/test_Apex.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 6bc747d0..143f90d0 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -1338,7 +1338,8 @@ def test_set_epoch_file_error(igrf_file): # Test missing coefficient file failure with pytest.raises(OSError) as oerr: Apex(date=2000.2, refh=300) - assert str(oerr.value).startswith("File {:} does not exist".format(igrf_file)) + error_string = "File {:} does not exist".format(igrf_file) + assert str(oerr.value).startswith(error_string) # ============================================================================ From 6959ebf0cec0ce9d864ba81c297399c44bb7d4ff Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 23 Feb 2021 15:57:07 -0500 Subject: [PATCH 193/226] REL: updated changelog and version number Updated the changelog and bumped the version number. --- CHANGELOG.rst | 7 +++++-- docs/conf.py | 2 +- pyproject.toml | 1 - setup.cfg | 4 ++-- src/apexpy/__init__.py | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 58479bde..a5c5af8a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,15 +2,18 @@ Changelog ========= -1.1.0 (2021-03-XX) +1.1.0 (2021-03-05) ------------------ -* Adapted Fortran to read IRGF coefficients from a text file (currently IGRF-13) +* Adapted Fortran to read IRGF coefficients from a file (updated to IGRF-13) * Improved the subsol routine to allow array input * Improved PEP8 compliance * Added some missing docstrings to unit tests * Fixed AppVeyor test environment * Updated python test versions * Updated community and package documentation +* Fixed bug where NaNs caused array input to crash +* Fixed bug in quasi-dipole to apex conversion at equator +* Removed duplicate CI services 1.0.4 (2019-04-05) ---------------------------------------- diff --git a/docs/conf.py b/docs/conf.py index c2e30138..ae98b0eb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,7 +18,7 @@ year = u'2015' author = u'Christer van der Meeren, Angeline G. Burrell' copyright = '{0}, {1}'.format(year, author) -version = release = u'1.0.3' +version = release = u'1.1.0' # on_rtd is whether we are on readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' diff --git a/pyproject.toml b/pyproject.toml index 073b80eb..e85c5edf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,2 @@ [build-system] requires = ["numpy", "setuptools", "wheel"] -build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg index 88dc9a01..17bb6c46 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = apexpy -version = 1.0.4 +version = 1.1.0 license = MIT description = "A Python wrapper for Apex coordinates" long_description = file: README.rst, CHANGELOG.rst @@ -59,7 +59,7 @@ apexpy = release = register clean --all sdist [bumpversion] -current_version = 1.0.4 +current_version = 1.1.0 commit = True tag = True diff --git a/src/apexpy/__init__.py b/src/apexpy/__init__.py index 3c842cdf..a4e21bbb 100644 --- a/src/apexpy/__init__.py +++ b/src/apexpy/__init__.py @@ -11,6 +11,6 @@ "apexpy probably won't work"])) -__version__ = "1.0.3" +__version__ = "1.1.0" __all__ = ['Apex', 'fortranapex', 'helpers', 'ApexHeightError'] From e8bb18a453faa4763f851b2956c9dd418ac5e05f Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 23 Feb 2021 16:00:46 -0500 Subject: [PATCH 194/226] DOC: updated main branch name Changed name of main branch from `master` to `main`. --- README.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index f247bc48..79e380d4 100644 --- a/README.rst +++ b/README.rst @@ -91,21 +91,21 @@ Badges :target: https://readthedocs.org/projects/apexpy :alt: Documentation Status -.. |travis| image:: https://travis-ci.org/aburrell/apexpy.svg?branch=master +.. |travis| image:: https://travis-ci.org/aburrell/apexpy.svg?branch=main :alt: Travis-CI Build Status :target: https://travis-ci.org/aburrell/apexpy -.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/aburrell/apexpy?branch=master&svg=true +.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/aburrell/apexpy?branch=main&svg=true :alt: AppVeyor Build Status :target: https://ci.appveyor.com/project/aburrell/apexpy -.. |requires| image:: https://requires.io/github/aburrell/apexpy/requirements.svg?branch=master +.. |requires| image:: https://requires.io/github/aburrell/apexpy/requirements.svg?branch=main :alt: Requirements Status - :target: https://requires.io/github/aburrell/apexpy/requirements/?branch=master + :target: https://requires.io/github/aburrell/apexpy/requirements/?branch=main -.. |coveralls| image:: https://coveralls.io/repos/github/aburrell/apexpy/badge.svg?branch=master +.. |coveralls| image:: https://coveralls.io/repos/github/aburrell/apexpy/badge.svg?branch=main :alt: Coverage Status - :target: https://coveralls.io/github/aburrell/apexpy?branch=master + :target: https://coveralls.io/github/aburrell/apexpy?branch=main .. |codacy| image:: https://api.codacy.com/project/badge/Grade/7d4c1a6c60e747ca95cdf97746c39cda :alt: Codacy Badge @@ -135,7 +135,7 @@ Badges :alt: Supported implementations :target: https://pypi.python.org/pypi/apexpy -.. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/aburrell/apexpy/master.svg?style=flat +.. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/aburrell/apexpy/main.svg?style=flat :alt: Scrutinizer Status :target: https://scrutinizer-ci.com/g/aburrell/apexpy/ From 1d53d1d106acb68a889ba88cfa9aa006a98af175 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 23 Feb 2021 16:37:22 -0500 Subject: [PATCH 195/226] DOC: updated docs Updated documentation to reflect recent updates that did not correctly propagate their changes to the docs. --- AUTHORS.rst | 2 +- CONTRIBUTING.rst | 16 +++++++++------- docs/maintenance.rst | 16 ++++------------ 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index e6992cbb..72f92cc5 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -7,8 +7,8 @@ This python wrapper is made by: * Karl M. Laundal * Christer van der Meeren * Angeline G. Burrell (maintainer) -* Ashton Reimer * Gregory Starr +* Ashton Reimer * Achim Morschhauser Fortran code by Emmert et al. [2010] [1]_. Quasi-dipole and modified diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 2445c6d9..f90b1be5 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -95,13 +95,15 @@ performs documentation tests. However, there are certain additional style elements that have been settled on to ensure the project maintains a consistent coding style: -* Line breaks should occur before a binary operator (ignoring flake8 W503) -* Preferably break long lines on open parentheses instead of using backslashes -* Use no more than 80 characters per line -* Several dependent packages have common nicknames, including: - * `import datetime as dt` - * `import numpy as np` -* Provide tests with informative failure statements and descriptive, one-line +- Line breaks should occur before a binary operator (ignoring flake8 W503) +- Preferably break long lines on open parentheses instead of using backslashes +- Use no more than 80 characters per line +- Several dependent packages have common nicknames, including: + + * ``import datetime as dt`` + * ``import numpy as np`` + +- Provide tests with informative failure statements and descriptive, one-line docstrings. apexpy is working on modernizing its code style to adhere to these guidelines. diff --git a/docs/maintenance.rst b/docs/maintenance.rst index 85c081d0..484a1bc0 100644 --- a/docs/maintenance.rst +++ b/docs/maintenance.rst @@ -7,22 +7,14 @@ Updating IGRF The `International Geomagnetic Reference Field `_ is regularly updated to reflect the most recent changes to the Terrestrial magnetic field. apexpy currently uses IRGF-13 coefficients, which are provided -in the ``apexpy/src/fortranapex/igrf13coeff.txt`` file. To change or update the -magnetic field coefficients used by apexpy, you need to update the fortran code. +in the ``apexpy/src/apexpy/igrf13coeff.txt`` file. To change or update the +magnetic field coefficients used by apexpy, you need to update the python code. Assuming your new coefficient file has the same format, the process is simple: 1. Clone the repository or your fork of the repository (see :ref:`contributing`) -2. Update ``apexpy/src/fortranapex/magfld.f`` variable ``FILENAME`` by setting +2. Update ``apexpy/src/apexpy/apex.py`` variable ``igrf_fn`` by setting it equal to the new target filename -3. Optional: Test the fortran compilation by running the test executable to - ensure that the new coefficients are used and deviate an acceptable amount - from prior coefficients:: - - make - apextest - make clean - -4. Install the python package from the command line +3. Install the python package from the command line (see :ref:`installation-cmd`) Updating tests and style standards From 7331b759b9407f8792148c8c7f01de11ebb0992f Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 24 Feb 2021 08:50:18 -0500 Subject: [PATCH 196/226] MAINT: applied suggestions from code review Applied suggestions made by @gregstarr during code review including: - clarifying the compiler/apexpy version details, and - fixing spelling errors. --- .github/ISSUE_TEMPLATE/bug_report.md | 3 ++- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- .github/ISSUE_TEMPLATE/question.md | 3 ++- .github/pull_request_template.md | 4 ++-- src/fortranapex/magfld.f | 4 ++-- tests/test_helpers.py | 2 +- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 07969566..bae46180 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -26,7 +26,8 @@ If applicable, add screenshots to help explain your problem. **Computer (please complete the following information):** - OS: [e.g. iOS] - Python version [e.g. 3.6] - - Compiler and version [e.g. ifort 22] + - Compiler with version [e.g., gfortran 47] + - Apexpy version/branch [e.g., 1.1.0] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 685ddb34..541a67ee 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -19,7 +19,7 @@ A clear and concise description of any alternative solutions or features you've considered. **Additional context** -Add any other context or screenshots about the feature request here. +Add any other context or screenshots about the feature request here. For context, the apexpy version/branch you're working on, your system detail, your python version, and your compiler (with version) may or may not be relevant. **Reminders** This is a volunteer-driven project. Code contributions are welcome, as is help diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 4e19ac62..c4506019 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -26,7 +26,8 @@ If applicable, add screenshots to help explain your question. **Desktop (please complete the following information):** - OS: [e.g. iOS] - Python version [e.g. 3.6] - - Compiler and version [e.g. gfortran 22] + - Compiler with version [e.g., gfortran 22] + - Apexpy version/branch [e.g., 1.1.0] **Additional context** Add any other context about the problem here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 682270ca..99382153 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -30,7 +30,7 @@ your test configuration * Operating system: (e.g., Hal) * Python version number: (e.g., 3.7) * Compiler with version number: (e.g. gfortran 9.0) -* Any details about your local setup that are relevant +* Any details about your local setup that are relevant (e.g., apexpy version/branch) # Checklist: @@ -44,4 +44,4 @@ your test configuration - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules - [ ] Add a note to ``Changelog.rst``, summarising the changes -- [ ] Add yourself to ``AUTHORS.rst`` and ``.zenodo.json`` \ No newline at end of file +- [ ] Add yourself to ``AUTHORS.rst`` and ``.zenodo.json`` diff --git a/src/fortranapex/magfld.f b/src/fortranapex/magfld.f index 1240170a..52dda6f1 100644 --- a/src/fortranapex/magfld.f +++ b/src/fortranapex/magfld.f @@ -9,8 +9,8 @@ SUBROUTINE COFRM (DATE, FILENAME) C rate after the last epoch. C C INPUTS: -C DATE = yyyy.fraction (UT)\ -C FILENAME = filename +C DATE = yyyy.fraction (UT) +C FILENAME = filename for IGRF coefficient file C OUTPUTS (in comnon block MAGCOF): C NMAX = Maximum order of spherical harmonic coefficients used C GB = Coefficients for magnetic field calculation diff --git a/tests/test_helpers.py b/tests/test_helpers.py index bd54f786..6725e55d 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -187,7 +187,7 @@ def test_subsol_array(): Notes ----- - Tested by ensurnig the array of np.datetime64 is equivalent to converting + Tested by ensuring the array of np.datetime64 is equivalent to converting using single dt.datetime values """ From 92cd648f56d1d09e9a540b03c86a5923007ffd27 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 24 Feb 2021 13:34:16 -0500 Subject: [PATCH 197/226] TST: attempted bugfix in pip installation Attempting to fix pip installation by removing potentially unnecessary packages. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e85c5edf..1b68fba3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,2 @@ [build-system] -requires = ["numpy", "setuptools", "wheel"] +requires = ["numpy"] From 6e7a8e138be9652e3e3f946089ba0765661a0d92 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 24 Feb 2021 16:16:43 -0500 Subject: [PATCH 198/226] TST: updated pyproject requires Updated pyproject.toml to include the setup requirements needed by numpy. https://github.com/pypa/pip/issues/9242 indicates that testing on TestPyPi does not work for the reasons it is failing locally. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1b68fba3..83de03ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,2 @@ [build-system] -requires = ["numpy"] +requires = ["setuptools", "wheel", "Cython", "numpy"] From 272b5196e76ded655bc241602e8aebeff229d5d0 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 25 Feb 2021 08:37:27 -0500 Subject: [PATCH 199/226] MAINT: added import for next commit Added an import necessary for the next commit. --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index ae98b0eb..bd5d437e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import os +import re extensions = [ From 293ef7a0c1d2cbe96d6dba32326858cbd44dcaad Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 25 Feb 2021 08:38:14 -0500 Subject: [PATCH 200/226] MAINT: removed unnecessary bumpversion Removed unneeded bumpversion call. --- setup.cfg | 2 -- 1 file changed, 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 17bb6c46..48318f0e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,8 +63,6 @@ current_version = 1.1.0 commit = True tag = True -[bumpversion:file:docs/conf.py] - [bumpversion:file:src/apexpy/__init__.py] [coverage:paths] From 85c8260afac1cba7e4698e0cfc9d34b52a15d504 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 25 Feb 2021 08:41:34 -0500 Subject: [PATCH 201/226] MAINT: Updated docs/conf.py Added code section to get version number from apexpy init. Co-authored-by: Ashton Reimer --- docs/conf.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index bd5d437e..d2a2aec9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,7 +19,13 @@ year = u'2015' author = u'Christer van der Meeren, Angeline G. Burrell' copyright = '{0}, {1}'.format(year, author) -version = release = u'1.1.0' +# Get version number from __init__.py +here = os.path.abspath(os.path.dirname(__file__)) +regex = "(?<=__version__..\s)\S+" +with open(os.path.join(here,'../src/apexpy/__init__.py'),'r', encoding='utf-8') as f: + text = f.read() +match = re.findall(regex,text) +version = release = match[0].strip("'") # on_rtd is whether we are on readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' From b6030a59a836071060b0636151ec79c09d4c3a32 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Thu, 25 Feb 2021 21:30:50 -0800 Subject: [PATCH 202/226] [DOC] added some dev and maintenance notes --- src/fortranapex/readme.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/fortranapex/readme.txt b/src/fortranapex/readme.txt index 99262664..e82ff5af 100644 --- a/src/fortranapex/readme.txt +++ b/src/fortranapex/readme.txt @@ -38,3 +38,21 @@ field lines. 5. 2010ja015326-txts05.txt magfld.f: Fortran-77 code for evaluating IGRF. + +############################################## + +DEV NOTES: + +The above text is copy-pasted from the auxiliary materials for the Emmert et al. (2010) paper. + +In February 2021, the magfld.f source was modified so that it could read IGRF coefficients from the text file instead of from hard-coded constants. Specifically the COFRM subroutine was modified. This also required modifying APEX subroutine in apex.f, the makeapxsh subroutine in makeapexsh.f90, and the checkapexsh program in checkapexsh.f90. + + +MAINTENANCE NOTE: + +After updating the IGRF coefficients file, we need to rebuild the apexsh.dat file. This is done by: + +1) Adding the next 5 year epoch to "epochgrid" and updateing the "nepochgrid" variable in checkapexsh.f90 + For example, if epochgrid has up to the year 2020 and the newest IGRF coefficients are good up to 2025, we should add 2025 to epochgrid and then increment nepochgrid by 1. +2) Building the "apextest" binary using the "make" command. +3) Copying the resulting "apexsh.dat" file to the apexpy/src/apexpy directory. From 6bbe8f719b63fe7b42382921875168fa32efca3f Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Thu, 25 Feb 2021 21:47:30 -0800 Subject: [PATCH 203/226] [DOC] updated notes --- src/fortranapex/readme.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/fortranapex/readme.txt b/src/fortranapex/readme.txt index e82ff5af..26c07086 100644 --- a/src/fortranapex/readme.txt +++ b/src/fortranapex/readme.txt @@ -54,5 +54,6 @@ After updating the IGRF coefficients file, we need to rebuild the apexsh.dat fil 1) Adding the next 5 year epoch to "epochgrid" and updateing the "nepochgrid" variable in checkapexsh.f90 For example, if epochgrid has up to the year 2020 and the newest IGRF coefficients are good up to 2025, we should add 2025 to epochgrid and then increment nepochgrid by 1. -2) Building the "apextest" binary using the "make" command. -3) Copying the resulting "apexsh.dat" file to the apexpy/src/apexpy directory. +2) checkapexsh.f90 also expects the IGRF coefficient file to be in the same directory with the name "igrf13coeffs.txt", so you may also need to update the "igrffilein" variable as well. +3) Building the "apextest" binary using the "make" command. +4) Copying the resulting "apexsh.dat" file to the apexpy/src/apexpy directory. \ No newline at end of file From 74eedf0c5ca3b63d078e3f1654cc45b8e5c19988 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Thu, 25 Feb 2021 22:30:44 -0800 Subject: [PATCH 204/226] [BUG] adding IGRF coefficients file to other fortran files (changes weren't applied to all files when magfld.f was modified. --- src/fortranapex/apex.f | 7 +++++-- src/fortranapex/checkapexsh.f90 | 10 +++++++++- src/fortranapex/makeapexsh.f90 | 17 +++++++++++++---- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/fortranapex/apex.f b/src/fortranapex/apex.f index 5ec65cbe..9c87ac45 100644 --- a/src/fortranapex/apex.f +++ b/src/fortranapex/apex.f @@ -1,6 +1,6 @@ C FILE NAME: apex.f - SUBROUTINE APEX (DATE,DLAT,DLON,ALT, + SUBROUTINE APEX (DATE,IGRFFILEIN,DLAT,DLON,ALT, + A,ALAT,ALON,BMAG,XMAG,YMAG,ZMAG,V) C Calculate apex radius, latitude, longitude; and magnetic field and C scalar magnetic potential. @@ -46,6 +46,8 @@ SUBROUTINE APEX (DATE,DLAT,DLON,ALT, C - Refine FNDAPX to insure |Bdown/Btot| < 1.E-6 at apex C Nov 2009: Change definition of earth's mean radius (RE) from 6371.2 C to the WGS84 value (6371.0088), by J.T. Emmert, NRL +C Feb 2021: Modified by Ashton Reimer to pass IGRF coefficients file +C to the COFRM subroutine call. C C------------------------------------------------------------------------------ C Reference Spheroid Change March 2004 @@ -106,7 +108,8 @@ SUBROUTINE APEX (DATE,DLAT,DLON,ALT, PARAMETER (RE = 6371.0088, DTOR = .01745329251994330) COMMON /DIPOLE/ COLAT,ELON,VP,CTP,STP - CALL COFRM (DATE) + CHARACTER(LEN=1000), intent(in) :: IGRFFILEIN + CALL COFRM (DATE,IGRFFILEIN) CALL DYPOL (CLATP,POLON,VPOL) COLAT = CLATP CTP = COS(CLATP*DTOR) diff --git a/src/fortranapex/checkapexsh.f90 b/src/fortranapex/checkapexsh.f90 index ceb07491..6022d1d8 100644 --- a/src/fortranapex/checkapexsh.f90 +++ b/src/fortranapex/checkapexsh.f90 @@ -12,6 +12,13 @@ ! vectors, J. Geophys. Res., 115, Axxxxx, doi:10.1029/2010JA015326, 2010. ! !******************************************************************************* +! +! HISTORY (blame): +! +! 25 Feb 2021: Modified by Ashton Reimer to pass IGRF coefficients file to the +! makeapxsh subroutine call. +! +!******************************************************************************* program checkapexsh @@ -20,6 +27,7 @@ program checkapexsh integer(4), parameter :: nepochgrid=25 integer(4) :: lmax=3, nmmax=6 character(1000) :: apexshfile='apexsh.dat' + character(len=1000) :: igrffilein='igrf13coeffs.txt' real(4) :: epochgrid(0:nepochgrid-1) real(4) :: epoch real(4) :: glat, glon, alt, hr, prec, error @@ -36,7 +44,7 @@ program checkapexsh 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,2020.0/) - call makeapxsh(apexshfile, epochgrid, nepochgrid, lmax, nmmax, nmmax) + call makeapxsh(apexshfile, igrffilein, epochgrid, nepochgrid, lmax, nmmax, nmmax) !HEIGHT PROFILE OF QD COORDINATES epoch = 2005.0 diff --git a/src/fortranapex/makeapexsh.f90 b/src/fortranapex/makeapexsh.f90 index a082ca0a..784444b6 100644 --- a/src/fortranapex/makeapexsh.f90 +++ b/src/fortranapex/makeapexsh.f90 @@ -15,14 +15,22 @@ ! !******************************************************************************* ! +! HISTORY (blame): +! +! 25 Feb 2021: Modified by Ashton Reimer to pass IGRF coefficients file to the +! COFRM and APEX subroutine calls. +! +!******************************************************************************* +! ! MAKEAPXSH ! Computes and saves harmonic coefficients for coordinate conversions. ! -! CALL MAKEAPXSH (DATAFILE, EPOCHS, NEPOCHS, L, M, N) +! CALL MAKEAPXSH (DATAFILE, IGRFFILE, EPOCHS, NEPOCHS, L, M, N) ! ! INPUT ARGUMENTS ! DATAFILE Name of output data file that will contain the conversion ! coefficients +! IGRFFILE Name of the input IGRF coefficients file ! EPOCHS Array of ordered epochs (decimal years, yyyy.y) for which ! coefficients are to be computed. ! NEPOCHS Number of elements in EPOCHS. @@ -36,7 +44,7 @@ ! !******************************************************************************* -subroutine makeapxsh(datafilein, epochgridin, nepochin, lmaxin, mmaxin, nmaxin) +subroutine makeapxsh(datafilein, igrffilein, epochgridin, nepochin, lmaxin, mmaxin, nmaxin) use apxshmodule @@ -45,6 +53,7 @@ subroutine makeapxsh(datafilein, epochgridin, nepochin, lmaxin, mmaxin, nmaxin) COMMON /MAGCOF/ NMAXIGRF, GB character(128), intent(in) :: datafilein + character(len=1000), intent(in) :: igrffilein real(4), intent(in) :: epochgridin(0:30) integer(4), intent(in) :: nmaxin, mmaxin, lmaxin, nepochin @@ -122,7 +131,7 @@ subroutine makeapxsh(datafilein, epochgridin, nepochin, lmaxin, mmaxin, nmaxin) !RETRIEVE IGRF, COMPUTE DIPOLE ROTATION PARAMETERS, !AND SET THE CORRESPONDING EXPANSION COEFFICIENTS - call COFRM(epochgrid(iepoch)) + call COFRM(epochgrid(iepoch),igrffilein) sinmplat = dble(GB(2) / sqrt(GB(2)*GB(2) + GB(3)*GB(3) + GB(4)*GB(4))) cosmplat = dsqrt(1 - sinmplat*sinmplat) sinmplon = dble(GB(4) / sqrt(GB(3)*GB(3) + GB(4)*GB(4))) @@ -159,7 +168,7 @@ subroutine makeapxsh(datafilein, epochgridin, nepochin, lmaxin, mmaxin, nmaxin) !COMPUTE QD LATITUDE AND LONGITUDE OF CURRENT LOCATION alt = sngl(altgrid(ialt)) - call APEX(epochgrid(iepoch),glat,glon,alt,A,ALAT,ALON,dum1,dum2,dum3,dum4,dum5) + call APEX(epochgrid(iepoch),igrffilein,glat,glon,alt,A,ALAT,ALON,dum1,dum2,dum3,dum4,dum5) cosqlat = dsqrt( (Re + altgrid(ialt)) / (Re + Req*(dble(A)-1D0)) ) if (cosqlat .gt. 1D0) cosqlat = 1D0 phiq = dble(ALON) * dtor From 6b4c42ac19fc97deeedc4db159e21a38e1e76b0a Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Thu, 25 Feb 2021 22:36:05 -0800 Subject: [PATCH 205/226] [MNT]: updated using igrf13 coefficients, pushed epoch out to 2025. --- src/apexpy/apexsh.dat | Bin 235336 -> 244748 bytes src/fortranapex/checkapexsh.f90 | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apexpy/apexsh.dat b/src/apexpy/apexsh.dat index 10284f5db284b3bfbad3895757aff92a2f439bc9..5cd1809912b327562b8c4cf333930f1060f8d018 100644 GIT binary patch literal 244748 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 JTicFp{|k9$w)Ox3 literal 235336 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

+lVlp*<2%COS&VrZ9-u<;OPO!-o+hIBqlg_X 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

ZYpyuFw5 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 JXYSng{{WSM#xDQ> diff --git a/src/fortranapex/checkapexsh.f90 b/src/fortranapex/checkapexsh.f90 index 6022d1d8..5580f771 100644 --- a/src/fortranapex/checkapexsh.f90 +++ b/src/fortranapex/checkapexsh.f90 @@ -24,7 +24,7 @@ program checkapexsh implicit none - integer(4), parameter :: nepochgrid=25 + integer(4), parameter :: nepochgrid=26 integer(4) :: lmax=3, nmmax=6 character(1000) :: apexshfile='apexsh.dat' character(len=1000) :: igrffilein='igrf13coeffs.txt' @@ -43,7 +43,7 @@ program checkapexsh epochgrid = (/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,2020.0/) + 2005.0,2010.0,2015.0,2020.0,2025.0/) call makeapxsh(apexshfile, igrffilein, epochgrid, nepochgrid, lmax, nmmax, nmmax) !HEIGHT PROFILE OF QD COORDINATES From c6b6975ad64c18723ad80f96b4fe9996ab30c04c Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Thu, 25 Feb 2021 23:07:18 -0800 Subject: [PATCH 206/226] update since we updated the sources --- src/fortranapex/fortranapex.pyf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/fortranapex/fortranapex.pyf b/src/fortranapex/fortranapex.pyf index e639655f..926bfd56 100644 --- a/src/fortranapex/fortranapex.pyf +++ b/src/fortranapex/fortranapex.pyf @@ -3,8 +3,9 @@ python module fortranapex ! in interface ! in :fortranapex - subroutine apex(date,dlat,dlon,alt,a,alat,alon,bmag,xmag,ymag,zmag,v) ! in :fortranapex:apex.f + subroutine apex(date,igrffilein,dlat,dlon,alt,a,alat,alon,bmag,xmag,ymag,zmag,v) ! in :fortranapex:apex.f real :: date + character*1000 intent(in) :: filename real :: dlat real :: dlon real :: alt @@ -305,9 +306,10 @@ python module fortranapex ! in real :: x1 real :: x2 end subroutine convrt - subroutine makeapxsh(datafilein,epochgridin,nepochin,lmaxin,mmaxin,nmaxin) ! in :fortranapex:makeapexsh.f90 + subroutine makeapxsh(datafilein,igrffilein,epochgridin,nepochin,lmaxin,mmaxin,nmaxin) ! in :fortranapex:makeapexsh.f90 use apxshmodule character*128 intent(in) :: datafilein + character(len=1000), intent(in) :: igrffilein real(kind=4) dimension(31),intent(in) :: epochgridin integer(kind=4) intent(in) :: nepochin integer(kind=4) intent(in) :: lmaxin From 1725417caffbd84933eab72579464449a912efee Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Thu, 25 Feb 2021 23:24:58 -0800 Subject: [PATCH 207/226] [DOC] add instructions for updating apexsh.dat --- docs/maintenance.rst | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/maintenance.rst b/docs/maintenance.rst index 484a1bc0..f01f0acc 100644 --- a/docs/maintenance.rst +++ b/docs/maintenance.rst @@ -17,6 +17,29 @@ Assuming your new coefficient file has the same format, the process is simple: 3. Install the python package from the command line (see :ref:`installation-cmd`) +Updating ``apexsh.dat`` +----------------------- + +After updating the IGRF coefficients file the ``apexsh.dat`` file needs to be +rebuilt. This file is what makes apexpy so perfromant. For more details, see +Emmert et al. [2010] [1]_. + +Updating ``apexsh.dat`` is done by modifying and building the fortran source code +in the ``apexpy/src/fortranapex`` directory. Working in that directory: + +1. Make sure a copy of the latest IGRF coefficient file is present in the + directory. +2. Modify the ``igrffilein`` in ``checkapexsh.f90`` to the name of the IGRF + coefficient file (``igrf13coeff.txt`` for example). +3. Modify ``checkapexsh.f90`` by adding the next 5 year epoch to the + ``epochgrid`` variable and updating the ``nepochgrid`` variable as + necessary. For example, if the newest IGRF coefficients are good up to 2025 + and ``epochgrid`` only has up to the year 2020, then add 2025 to + ``epochgrid`` and then increment ``nepochgrid`` by 1. +4. Build the ``apextest`` binary by running the ``make`` command. +5. Execute the ``apextest`` binary to generate the new ``apexsh.dat`` file +4. Copy the new ``apexsh.dat`` file to the ``apexpy/src/apexpy`` directory. + Updating tests and style standards ----------------------------------- @@ -26,3 +49,9 @@ of the fortran code adhere to older coding standards and raise warnings when compiled with newer compilers. If you would like to assist in these efforts (help would be appreciated), please discuss your potential contribution with the current maintainer to ensure a minimal duplication of effort. + + +.. [1] Emmert, J. T., A. D. Richmond, and D. P. Drob (2010), + A computationally compact representation of Magnetic-Apex + and Quasi-Dipole coordinates with smooth base vectors, + J. Geophys. Res., 115(A8), A08322, :doi:`10.1029/2010JA015326`. \ No newline at end of file From 43d2d8711b91cd8afefe3b5c31c46ee4da18195f Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Fri, 26 Feb 2021 00:46:58 -0800 Subject: [PATCH 208/226] [MNT]: updated expected outputs that changed by expected tiny amounts due to update to IGRF13 --- tests/test_Apex.py | 63 +++++++++++++++++++-------------------- tests/test_cmd.py | 34 ++++++++++----------- tests/test_fortranapex.py | 16 +++++----- 3 files changed, 55 insertions(+), 58 deletions(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 143f90d0..708c9abf 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -705,7 +705,7 @@ def test_mlon2mlt_range(): def test_mlt2mlon_scalar(): apex_out = Apex(date=2000, refh=300) mlt = apex_out.mlt2mlon(0, dt.datetime(2000, 2, 3, 4, 5, 6)) - assert_allclose(mlt, 14.705551147460938) + assert_allclose(mlt, 14.705535888671875) assert type(mlt) != np.ndarray @@ -790,17 +790,18 @@ def test_map_to_height(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.map_to_height(60, 15, 100, 10000, conjugate=False, precision=1e-10), - (31.841459274291992, 17.916629791259766, 0)) + (31.841466903686523, 17.916635513305664, + 1.7075473124350538e-6)) assert_allclose(apex_out.map_to_height(30, 170, 100, 500, conjugate=False, precision=1e-2), - (25.727252960205078, 169.60546875, 0.00017655163537710905)) + (25.727270126342773, 169.60546875, 0.00017573432705830783)) def test_map_to_height_same_height(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.map_to_height(60, 15, 100, 100, conjugate=False, precision=1e-10), - (60, 15, 3.4150946248701075e-6), rtol=1e-5) + (60.0, 15.000003814697266, 0.0), rtol=1e-5) def test_map_to_height_conjugate(): @@ -808,7 +809,7 @@ def test_map_to_height_conjugate(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.map_to_height(60, 15, 100, 10000, conjugate=True, precision=1e-10), - (-25.424892425537109, 27.310417175292969, + (-25.424888610839844, 27.310426712036133, 1.2074182222931995e-6), atol=1e-6) assert_allclose(apex_out.map_to_height(30, 170, 100, 500, conjugate=True, precision=1e-2), @@ -819,17 +820,13 @@ def test_map_to_height_conjugate(): def test_map_to_height_vectorization(): apex_out = Apex(date=2000, refh=300) assert_allclose(apex_out.map_to_height([60, 60], 15, 100, 100), - ([60] * 2, [15] * 2, [3.4150946248701075e-6] * 2), - rtol=1e-5) + ([60] * 2, [15.00000381] * 2, [0] * 2), rtol=1e-5) assert_allclose(apex_out.map_to_height(60, [15, 15], 100, 100), - ([60] * 2, [15] * 2, [3.4150946248701075e-6] * 2), - rtol=1e-5) + ([60] * 2, [15.00000381] * 2, [0] * 2), rtol=1e-5) assert_allclose(apex_out.map_to_height(60, 15, [100, 100], 100), - ([60] * 2, [15] * 2, [3.4150946248701075e-6] * 2), - rtol=1e-5) + ([60] * 2, [15.00000381] * 2, [0] * 2), rtol=1e-5) assert_allclose(apex_out.map_to_height(60, 15, 100, [100, 100]), - ([60] * 2, [15] * 2, [3.4150946248701075e-6] * 2), - rtol=1e-5) + ([60] * 2, [15.00000381] * 2, [0] * 2), rtol=1e-5) def test_map_to_height_ApexHeightError(): @@ -845,12 +842,12 @@ def test_map_to_height_ApexHeightError(): def test_map_E_to_height(): apex_out = Apex(date=2000, refh=300) - out_60_15_100_500 = [0.7115211, 2.3562392, 0.57259707] - out_60_15_100_500_234 = [1.560284, 3.439154, 0.782339] - out_60_15_100_1000 = [0.677964, 2.089811, 0.558601] - out_60_15_200_500 = [0.723773, 2.427366, 0.590826] - out_60_30_100_500 = [0.686265, 2.375296, 0.600594] - out_70_15_100_500 = [0.727605, 2.180817, 0.291414] + out_60_15_100_500 = [0.71152183, 2.35624876, 0.57260784] + out_60_15_100_500_234 = [1.56028502, 3.43916636, 0.78235384] + out_60_15_100_1000 = [0.67796492, 2.08982134, 0.55860785] + out_60_15_200_500 = [0.72377397, 2.42737471, 0.59083726] + out_60_30_100_500 = [0.68626344, 2.37530133, 0.60060124] + out_70_15_100_500 = [0.72760378, 2.18082305, 0.29141979] # scalar assert_allclose(apex_out.map_E_to_height(60, 15, 100, 500, [1, 2, 3]), @@ -905,12 +902,12 @@ def test_map_E_to_height(): def test_map_V_to_height(): apex_out = Apex(date=2000, refh=300) - out_60_15_100_500 = [0.819719, 2.845114, 0.695437] - out_60_15_100_500_234 = [1.830277, 4.14345, 0.947624] - out_60_15_100_1000 = [0.924577, 3.149964, 0.851343] - out_60_15_200_500 = [0.803882, 2.793206, 0.682839] - out_60_30_100_500 = [0.761412, 2.878837, 0.736549] - out_70_15_100_500 = [0.846819, 2.592572, 0.347919] + out_60_15_100_500 = [0.81971957, 2.84512495, 0.69545001] + out_60_15_100_500_234 = [1.83027746, 4.14346436, 0.94764179] + out_60_15_100_1000 = [0.92457698, 3.14997661, 0.85135187] + out_60_15_200_500 = [0.80388262, 2.79321504, 0.68285158] + out_60_30_100_500 = [0.76141245, 2.87884673, 0.73655941] + out_70_15_100_500 = [0.84681866, 2.5925821, 0.34792655] # scalar assert_allclose(apex_out.map_V_to_height(60, 15, 100, 500, [1, 2, 3]), @@ -1388,14 +1385,14 @@ def test_get_babs(): def test_bvectors_apex(): inputs = [[80, 81], [100, 120], [100, 200]] - expected = (np.array([5.94623305e-05, 5.95450722e-05]), - np.array([[0.02008877, 0.00303204], - [0.03571109, 0.03377986], - [-0.94045794, -0.89848483]]), - np.array([5.26919505e-05, 4.81377429e-05]), - np.array([[0.02266997, 0.00375055], - [0.04029961, 0.04178477], - [-1.0612973, -1.1114012]])) + expected = (np.array([5.95166171e-05, 5.95958974e-05]), + np.array([[0.0191583, 0.0020023], + [0.03547136, 0.03392595], + [-0.9412518 , -0.8991005]]), + np.array([5.28257734e-05, 4.82450628e-05]), + np.array([[0.02158486, 0.00247339], + [0.03996412, 0.04190787], + [-1.0604696, -1.110636]])) apex_out = Apex(date=2018.1, refh=0) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 20faa1da..36822670 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -31,9 +31,9 @@ def test_module_invocation(): assert os.path.isfile(outfile) data = np.loadtxt(outfile) - np.testing.assert_allclose(data, [[57.469547, 93.639816], - [58.522701, 94.044762], - [59.571465, 94.477257]], rtol=1e-4) + np.testing.assert_allclose(data, [[57.47145462, 93.62657928], + [58.52458191, 94.03150177], + [59.57331467, 94.46398163]], rtol=1e-4) def test_convert_YYYY(): @@ -43,9 +43,9 @@ def test_convert_YYYY(): pipe.wait() assert os.path.isfile(outfile) data = np.loadtxt(outfile) - np.testing.assert_allclose(data, [[57.469547, 93.639816], - [58.522701, 94.044762], - [59.571465, 94.477257]], rtol=1e-4) + np.testing.assert_allclose(data, [[57.47145462, 93.62657928], + [58.52458191, 94.03150177], + [59.57331467, 94.46398163]], rtol=1e-4) def test_convert_YYYYMM(): @@ -55,9 +55,9 @@ def test_convert_YYYYMM(): pipe.wait() assert os.path.isfile(outfile) data = np.loadtxt(outfile) - np.testing.assert_allclose(data, [[57.469547, 93.639816], - [58.522701, 94.044762], - [59.571465, 94.477257]], rtol=1e-4) + np.testing.assert_allclose(data, [[57.47145462, 93.62657928], + [58.52458191, 94.03150177], + [59.57331467, 94.46398163]], rtol=1e-4) def test_convert_YYYYMMDD(): @@ -68,9 +68,9 @@ def test_convert_YYYYMMDD(): pipe.wait() assert os.path.isfile(outfile) data = np.loadtxt(outfile) - np.testing.assert_allclose(data, [[57.469547, 93.639816], - [58.522701, 94.044762], - [59.571465, 94.477257]], rtol=1e-4) + np.testing.assert_allclose(data, [[57.47145462, 93.62657928], + [58.52458191, 94.03150177], + [59.57331467, 94.46398163]], rtol=1e-4) def test_convert_YYYYMMDDHHMMSS(): @@ -81,9 +81,9 @@ def test_convert_YYYYMMDDHHMMSS(): pipe.wait() assert os.path.isfile(outfile) data = np.loadtxt(outfile) - np.testing.assert_allclose(data, [[57.469547, 93.639816], - [58.522701, 94.044762], - [59.571465, 94.477257]], rtol=1e-4) + np.testing.assert_allclose(data, [[57.47145462, 93.62657928], + [58.52458191, 94.03150177], + [59.57331467, 94.46398163]], rtol=1e-4) def test_convert_single_line(): @@ -94,7 +94,7 @@ def test_convert_single_line(): pipe.wait() assert os.path.isfile(outfile) data = np.loadtxt(outfile) - np.testing.assert_allclose(data, [57.469547, 93.639816], rtol=1e-4) + np.testing.assert_allclose(data, [57.47145462, 93.62657928], rtol=1e-4) def test_convert_stdin_stdout(): @@ -105,7 +105,7 @@ def test_convert_stdin_stdout(): stdout, _ = pipe.communicate() pipe.wait() np.testing.assert_allclose(np.array(stdout.split(b' '), dtype=float), - [57.469547, 93.639816], rtol=1e-4) + [57.47145462, 93.62657928], rtol=1e-4) def test_convert_refh(): diff --git a/tests/test_fortranapex.py b/tests/test_fortranapex.py index b97cb812..89552308 100644 --- a/tests/test_fortranapex.py +++ b/tests/test_fortranapex.py @@ -22,7 +22,7 @@ def test_apxg2q(): qlat, qlon, f1, f2, f = fa.apxg2q(60, 15, 100, 1) assert_allclose(qlat, 56.531288146972656) assert_allclose(qlon, 94.1068344116211) - assert_allclose(f1, [1.07978308, 0.10027108], rtol=1e-6) + assert_allclose(f1, [1.079783, 0.10027137], rtol=1e-6) assert_allclose(f2, [-0.24546318, 0.90718889], rtol=1e-6) assert_allclose(f, 1.0041800737380981) @@ -36,16 +36,16 @@ def test_apxg2all(): assert_allclose(qlon, 94.1068344116211) assert_allclose(mlat, 55.94841766357422) assert_allclose(mlon, 94.1068344116211) - assert_allclose(f1, [1.07978308, 0.10027108], rtol=1e-6) + assert_allclose(f1, [1.079783, 0.10027137], rtol=1e-6) assert_allclose(f2, [-0.24546318, 0.90718889], rtol=1e-6) assert_allclose(f, 1.0041800737380981) - assert_allclose(d1, [0.94957006, 0.2569305, 0.09049489], rtol=1e-6) - assert_allclose(d2, [0.10011058, -1.07805467, -0.33892459], rtol=1e-6) - assert_allclose(d3, [0.00865366, 0.27327025, -0.86666465], rtol=1e-6) - assert_allclose(d, 1.1003910303115845) - assert_allclose(e1, [1.02692986, 0.08382936, 0.03668636], rtol=1e-6) + assert_allclose(d1, [0.9495701, 0.25693053, 0.09049474], rtol=1e-6) + assert_allclose(d2, [0.10011087, -1.0780545, -0.33892432], rtol=1e-6) + assert_allclose(d3, [0.00865356, 0.27327004, -0.8666646], rtol=1e-6) + assert_allclose(d, 1.100391149520874) + assert_allclose(e1, [1.0269295, 0.08382964, 0.03668632], rtol=1e-6) assert_allclose(e2, [0.24740215, -0.82374191, -0.25726584], rtol=1e-6) - assert_allclose(e3, [0.01047837, 0.33089212, -1.04940987], rtol=1e-6) + assert_allclose(e3, [0.01047826, 0.33089194, -1.04941], rtol=1e-6) def test_apxq2g(): From da62167a772135efa7afe40265828295a9a21894 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Fri, 26 Feb 2021 00:55:58 -0800 Subject: [PATCH 209/226] [DOC]: updated expected output in examples --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 79e380d4..f5d5ebb0 100644 --- a/README.rst +++ b/README.rst @@ -35,20 +35,20 @@ perform the desired calculations. Some simple examples:: >>> # geo to apex, scalar input >>> mlat, mlon = A.convert(60, 15, 'geo', 'apex', height=300) >>> print("{:.12f}, {:.12f}".format(mlat, mlon)) - 57.469573974609, 93.633583068848 + 57.477310180664, 93.590156555176 >>> # apex to geo, array input >>> glat, glon = A.convert([90, -90], 0, 'apex', 'geo', height=0) >>> print(["{:.12f}, {:.12f}".format(ll, glon[i]) for i,ll in enumerate(glat)]) - ['83.099594116211, -84.594589233398', '-74.388267517090, 125.714927673340'] + ['83.103820800781, -84.526657104492', '-74.388252258301, 125.736274719238'] >>> # geo to MLT >>> import datetime as dt >>> mlat, mlt = A.convert(60, 15, 'geo', 'mlt', datetime=dt.datetime(2015, 2, 10, 18, 0, 0)) >>> print("{:.12f}, {:.12f}".format(mlat, mlt)) - 56.590423583984, 19.108103879293 + 56.598316192627, 19.107861709595 >>> # can also convert magnetic longitude to mlt >>> mlt = A.mlon2mlt(120, dt.datetime(2015, 2, 10, 18, 0, 0)) >>> print("{:.2f}".format(mlt)) - 20.89 + 20.90 If you don't know or use Python, you can also use the command line. See details in the full documentation. From 6b9f3c2dbe367dc6f9fc127948bb93bd51a68815 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 26 Feb 2021 07:36:58 -0500 Subject: [PATCH 210/226] TST: improve cmd test robustness Improve robustness of unit tests for command line calls by ensuring access to apexpy on systems without the executable in the path. --- tests/test_cmd.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 36822670..f81c1d21 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -100,8 +100,9 @@ def test_convert_single_line(): def test_convert_stdin_stdout(): """ Test use of pipe input to command-line call """ - pipe = subprocess.Popen('echo 60 15 | apexpy geo apex 2015 --height 300', - shell=True, stdout=subprocess.PIPE) + pipe = subprocess.Popen( + 'echo 60 15 | python -m apexpy geo apex 2015 --height 300', + shell=True, stdout=subprocess.PIPE) stdout, _ = pipe.communicate() pipe.wait() np.testing.assert_allclose(np.array(stdout.split(b' '), dtype=float), @@ -110,7 +111,7 @@ def test_convert_stdin_stdout(): def test_convert_refh(): pipe = subprocess.Popen( - 'echo 60 15 | apexpy geo apex 2000 --height 100 --refh=300', + 'echo 60 15 | python -m apexpy geo apex 2000 --height 100 --refh=300', shell=True, stdout=subprocess.PIPE) stdout, _ = pipe.communicate() pipe.wait() From 71279c82788a861a9ba3ac5cfb54113b3ed7b06e Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 26 Feb 2021 12:25:01 -0500 Subject: [PATCH 211/226] STY: removed extra whitespace Remove the extra whitespace before a comma. --- tests/test_Apex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_Apex.py b/tests/test_Apex.py index 708c9abf..9414b02e 100644 --- a/tests/test_Apex.py +++ b/tests/test_Apex.py @@ -1388,7 +1388,7 @@ def test_bvectors_apex(): expected = (np.array([5.95166171e-05, 5.95958974e-05]), np.array([[0.0191583, 0.0020023], [0.03547136, 0.03392595], - [-0.9412518 , -0.8991005]]), + [-0.9412518, -0.8991005]]), np.array([5.28257734e-05, 4.82450628e-05]), np.array([[0.02158486, 0.00247339], [0.03996412, 0.04190787], From f80d7d5ae29c68dfd9996fcc34b97bf8d56ee31e Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 26 Feb 2021 15:14:36 -0500 Subject: [PATCH 212/226] TST: added relative tolerance Added a relative tolerance limit to `test_apxg2all` unit test. --- tests/test_fortranapex.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_fortranapex.py b/tests/test_fortranapex.py index 89552308..666d15ae 100644 --- a/tests/test_fortranapex.py +++ b/tests/test_fortranapex.py @@ -32,17 +32,17 @@ def test_apxg2all(): 2000) qlat, qlon, mlat, mlon, f1, f2, f, d1, d2, d3, d, e1, e2, e3 = fa.apxg2all( 60, 15, 100, 300, 1) - assert_allclose(qlat, 56.531288146972656) - assert_allclose(qlon, 94.1068344116211) - assert_allclose(mlat, 55.94841766357422) - assert_allclose(mlon, 94.1068344116211) + assert_allclose(qlat, 56.531288146972656, rtol=1e-6) + assert_allclose(qlon, 94.1068344116211, rtol=1e-6) + assert_allclose(mlat, 55.94841766357422, rtol=1e-6) + assert_allclose(mlon, 94.1068344116211, rtol=1e-6) assert_allclose(f1, [1.079783, 0.10027137], rtol=1e-6) assert_allclose(f2, [-0.24546318, 0.90718889], rtol=1e-6) - assert_allclose(f, 1.0041800737380981) + assert_allclose(f, 1.0041800737380981, rtol=1e-6) assert_allclose(d1, [0.9495701, 0.25693053, 0.09049474], rtol=1e-6) assert_allclose(d2, [0.10011087, -1.0780545, -0.33892432], rtol=1e-6) assert_allclose(d3, [0.00865356, 0.27327004, -0.8666646], rtol=1e-6) - assert_allclose(d, 1.100391149520874) + assert_allclose(d, 1.100391149520874, rtol=1e-6) assert_allclose(e1, [1.0269295, 0.08382964, 0.03668632], rtol=1e-6) assert_allclose(e2, [0.24740215, -0.82374191, -0.25726584], rtol=1e-6) assert_allclose(e3, [0.01047826, 0.33089194, -1.04941], rtol=1e-6) From 13a63e074261212b3fe9c6e9dc571e352ac8420b Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Fri, 26 Feb 2021 13:55:58 -0800 Subject: [PATCH 213/226] DOC: fixing grammar Co-authored-by: Angeline Burrell --- docs/maintenance.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/maintenance.rst b/docs/maintenance.rst index f01f0acc..00b81d04 100644 --- a/docs/maintenance.rst +++ b/docs/maintenance.rst @@ -21,7 +21,7 @@ Updating ``apexsh.dat`` ----------------------- After updating the IGRF coefficients file the ``apexsh.dat`` file needs to be -rebuilt. This file is what makes apexpy so perfromant. For more details, see +rebuilt. This file is what makes apexpy perfromant. For more details, see Emmert et al. [2010] [1]_. Updating ``apexsh.dat`` is done by modifying and building the fortran source code @@ -54,4 +54,4 @@ the current maintainer to ensure a minimal duplication of effort. .. [1] Emmert, J. T., A. D. Richmond, and D. P. Drob (2010), A computationally compact representation of Magnetic-Apex and Quasi-Dipole coordinates with smooth base vectors, - J. Geophys. Res., 115(A8), A08322, :doi:`10.1029/2010JA015326`. \ No newline at end of file + J. Geophys. Res., 115(A8), A08322, :doi:`10.1029/2010JA015326`. From 1bf6d83be4584233168b692050420a0314d0e695 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Fri, 26 Feb 2021 13:56:18 -0800 Subject: [PATCH 214/226] DOC: fixing grammar Co-authored-by: Angeline Burrell --- docs/maintenance.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/maintenance.rst b/docs/maintenance.rst index 00b81d04..1cd3ab3a 100644 --- a/docs/maintenance.rst +++ b/docs/maintenance.rst @@ -28,7 +28,7 @@ Updating ``apexsh.dat`` is done by modifying and building the fortran source cod in the ``apexpy/src/fortranapex`` directory. Working in that directory: 1. Make sure a copy of the latest IGRF coefficient file is present in the - directory. + selfsame directory. 2. Modify the ``igrffilein`` in ``checkapexsh.f90`` to the name of the IGRF coefficient file (``igrf13coeff.txt`` for example). 3. Modify ``checkapexsh.f90`` by adding the next 5 year epoch to the From 399c3ac70ff985c3d3289d5de9a26a925d46118c Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Fri, 26 Feb 2021 13:56:28 -0800 Subject: [PATCH 215/226] DOC: fixing grammar Co-authored-by: Angeline Burrell --- docs/maintenance.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/maintenance.rst b/docs/maintenance.rst index 1cd3ab3a..1bf3ea8c 100644 --- a/docs/maintenance.rst +++ b/docs/maintenance.rst @@ -30,7 +30,7 @@ in the ``apexpy/src/fortranapex`` directory. Working in that directory: 1. Make sure a copy of the latest IGRF coefficient file is present in the selfsame directory. 2. Modify the ``igrffilein`` in ``checkapexsh.f90`` to the name of the IGRF - coefficient file (``igrf13coeff.txt`` for example). + coefficient file (``igrf13coeff.txt``, for example). 3. Modify ``checkapexsh.f90`` by adding the next 5 year epoch to the ``epochgrid`` variable and updating the ``nepochgrid`` variable as necessary. For example, if the newest IGRF coefficients are good up to 2025 From 70b64b6121f0b0c48498c32ee59bff3314be28dd Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Fri, 26 Feb 2021 13:57:19 -0800 Subject: [PATCH 216/226] DOC: typo --- docs/maintenance.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/maintenance.rst b/docs/maintenance.rst index 1bf3ea8c..3c3435e7 100644 --- a/docs/maintenance.rst +++ b/docs/maintenance.rst @@ -21,7 +21,7 @@ Updating ``apexsh.dat`` ----------------------- After updating the IGRF coefficients file the ``apexsh.dat`` file needs to be -rebuilt. This file is what makes apexpy perfromant. For more details, see +rebuilt. This file is what makes apexpy performant. For more details, see Emmert et al. [2010] [1]_. Updating ``apexsh.dat`` is done by modifying and building the fortran source code From 6adc7121b6c4ba599f75cffba1cc67adc2f83717 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Tue, 2 Mar 2021 14:44:43 -0800 Subject: [PATCH 217/226] DOC: include quick install instructions for windows 10 --- README.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index f5d5ebb0..5ac295dd 100644 --- a/README.rst +++ b/README.rst @@ -23,7 +23,12 @@ location as when the pip wheel was built (if a wheel was used). If not, you may have trouble importing apexpy. If you run into trouble, try the command:: pip install --global-option='build_ext' apexpy - + +or on Windows with the Mingw32 compiler, this command:: + + pip install --no-use-pep517 --global-option build_ext \ + --global-option --compiler=mingw32 apexpy + which requires both libgfortran and gfortran to be installed on your system. Conversion is done by creating an ``Apex`` object and using its methods to From bd40c36d88bc6d6dda663734377ae925ab7807a4 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Tue, 2 Mar 2021 14:47:51 -0800 Subject: [PATCH 218/226] DOC: added win10 specific installation instructions --- docs/installation.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/installation.rst b/docs/installation.rst index 2d780571..64482509 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -31,6 +31,11 @@ you run into issues, you can get around this problem by using pip install --global-option='build_ext' apexpy +or on Windows with the Mingw32 compiler, this command:: + + pip install --no-use-pep517 --global-option build_ext \ + --global-option --compiler=mingw32 apexpy + which requires both libgfortran and gfortran to be installed on your system. This is the default option for Linux, and so should not be an issue there. From 39bc4bafdb4cad65df6b986d1d37df6da96207ba Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Tue, 2 Mar 2021 14:49:11 -0800 Subject: [PATCH 219/226] DOC: using pyproject.toml, so updated readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 5ac295dd..53737bce 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ is WGS84. MLT calculations are also included. The package is free software Quick start =========== -Install (requires NumPy before installation):: +Install from PyPi using ``pip``:: pip install apexpy From ba2ad18d8dc4fd11280895f12c2cf9e1e60dd7fb Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 3 Mar 2021 08:28:29 -0500 Subject: [PATCH 220/226] MAINT: improved documentation Fixed wording and spellings. Co-authored-by: Ashton Reimer --- README.rst | 2 +- docs/installation.rst | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 53737bce..08328d84 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,7 @@ is WGS84. MLT calculations are also included. The package is free software Quick start =========== -Install from PyPi using ``pip``:: +Install from PyPI using ``pip``:: pip install apexpy diff --git a/docs/installation.rst b/docs/installation.rst index 64482509..cb307396 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -11,14 +11,14 @@ NumPy/SciPy, Windows/Mac users should install NumPy/SciPy available in `their repositories `_. -When you have NumPy, you may use either PyPi or GitHub to install this package. +When you have NumPy, you may use either PyPI or GitHub to install this package. .. _installation-pip: -PyPi +PyPI ---- -This is the most straigforward option! From the command line use +This is the most straightforward option! From the command line use ``pip`` [1]_:: pip install apexpy @@ -55,8 +55,11 @@ a virtual environment. After clonining the fork (see :ref:`contributing`), you may install by:: cd apexpy - python setup.py --user + python setup.py develop --user +or with ``pip``:: + cd apexpy + pip install -e . Another benefit of installing apexpy from the command line is specifying the fortran compiler you would like to use. By default, apexpy uses From bee098baaf5d7999ebd8bb209cb985475efb1412 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Thu, 4 Mar 2021 17:40:48 -0800 Subject: [PATCH 221/226] DOC: Update docs/installation.rst Co-authored-by: Angeline Burrell --- docs/installation.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index cb307396..61bf818a 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -23,10 +23,7 @@ This is the most straightforward option! From the command line use pip install apexpy -This assumes that the same version of libgfortran is installed in the same -location as when the pip wheel was built (in the event that a wheel is -available). If not, you may have trouble importing apexpy. In the event that -you run into issues, you can get around this problem by using +In the event that you run into issues, you can get around this problem by using ``pip`` [1]_:: pip install --global-option='build_ext' apexpy From 31fec9f86b02e016405f6530066d9442cd41c1e8 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Thu, 4 Mar 2021 17:41:39 -0800 Subject: [PATCH 222/226] DOC: Update docs/installation.rst Co-authored-by: Angeline Burrell --- docs/installation.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 61bf818a..ef736466 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -60,8 +60,7 @@ or with ``pip``:: Another benefit of installing apexpy from the command line is specifying the fortran compiler you would like to use. By default, apexpy uses -`gfortran `_, but you can alter the -Makefile in ``src/fortranapex`` to use other compilers or specify different +`numpy`'s `f2py`, but you can change this using the global `--compiler` flag when running the `python setup.py install` command. compilation flags. However, if using an Intel compiler, you will need to uncomment a line at the top of ``src/fortranapex/igrf.f90`` to ensure all necessary libraries are imported. From a453754d65e4f83f4f2f957a841a4970dffcfad0 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Thu, 4 Mar 2021 17:42:22 -0800 Subject: [PATCH 223/226] DOC: Update src/fortranapex/readme.txt Co-authored-by: Angeline Burrell --- src/fortranapex/readme.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fortranapex/readme.txt b/src/fortranapex/readme.txt index 26c07086..8c7894aa 100644 --- a/src/fortranapex/readme.txt +++ b/src/fortranapex/readme.txt @@ -52,8 +52,8 @@ MAINTENANCE NOTE: After updating the IGRF coefficients file, we need to rebuild the apexsh.dat file. This is done by: -1) Adding the next 5 year epoch to "epochgrid" and updateing the "nepochgrid" variable in checkapexsh.f90 +1) Adding the next 5 year epoch to "epochgrid" and updating the "nepochgrid" variable in checkapexsh.f90 For example, if epochgrid has up to the year 2020 and the newest IGRF coefficients are good up to 2025, we should add 2025 to epochgrid and then increment nepochgrid by 1. 2) checkapexsh.f90 also expects the IGRF coefficient file to be in the same directory with the name "igrf13coeffs.txt", so you may also need to update the "igrffilein" variable as well. 3) Building the "apextest" binary using the "make" command. -4) Copying the resulting "apexsh.dat" file to the apexpy/src/apexpy directory. \ No newline at end of file +4) Copying the resulting "apexsh.dat" file to the apexpy/src/apexpy directory. From 7d05562d9f36e1d379d06baf5e0a2f8b0c5e9ce8 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Thu, 4 Mar 2021 17:42:38 -0800 Subject: [PATCH 224/226] DOC: Update docs/installation.rst Co-authored-by: Angeline Burrell --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index ef736466..43525323 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -61,7 +61,7 @@ or with ``pip``:: Another benefit of installing apexpy from the command line is specifying the fortran compiler you would like to use. By default, apexpy uses `numpy`'s `f2py`, but you can change this using the global `--compiler` flag when running the `python setup.py install` command. -compilation flags. However, if using an Intel compiler, you will need to +However, if using an Intel compiler, you will need to uncomment a line at the top of ``src/fortranapex/igrf.f90`` to ensure all necessary libraries are imported. From b50ca4e95e84ec252bec65f87fd8c7720a3b40db Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Thu, 4 Mar 2021 22:41:42 -0800 Subject: [PATCH 225/226] DOC: simplified build instructions --- README.rst | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 08328d84..93a22d35 100644 --- a/README.rst +++ b/README.rst @@ -22,12 +22,7 @@ This assumes that the same version of libgfortran is installed in the same location as when the pip wheel was built (if a wheel was used). If not, you may have trouble importing apexpy. If you run into trouble, try the command:: - pip install --global-option='build_ext' apexpy - -or on Windows with the Mingw32 compiler, this command:: - - pip install --no-use-pep517 --global-option build_ext \ - --global-option --compiler=mingw32 apexpy + pip install --no-binary :apexpy: apexpy which requires both libgfortran and gfortran to be installed on your system. From 48ee3e67a27b2ebdb1c5c112d68a9038206d3877 Mon Sep 17 00:00:00 2001 From: Ashton Reimer Date: Thu, 4 Mar 2021 22:48:50 -0800 Subject: [PATCH 226/226] DOC: more accurate installation instructions --- docs/installation.rst | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 43525323..8d30bdb2 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -26,15 +26,12 @@ This is the most straightforward option! From the command line use In the event that you run into issues, you can get around this problem by using ``pip`` [1]_:: - pip install --global-option='build_ext' apexpy - -or on Windows with the Mingw32 compiler, this command:: - - pip install --no-use-pep517 --global-option build_ext \ - --global-option --compiler=mingw32 apexpy + pip install --no-binary :apexpy: apexpy which requires both libgfortran and gfortran to be installed on your system. -This is the default option for Linux, and so should not be an issue there. +This is the default option for Linux, and so should not be an issue there. On +Windows with the Mingw32 compiler, you might find `this information `_ +useful for helping build apexpy. The package has been tested with the following setups (others might work, too):