Passed
Pull Request — master (#60)
by Angeline
01:04
created

aacgmv2.utils.gc2gd_lat()   A

Complexity

Conditions 1

Size

Total Lines 19
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 19
rs 10
c 0
b 0
f 0
cc 1
nop 1
1
# Copyright (C) 2019 NRL
2
# Author: Angeline Burrell
3
# Disclaimer: This code is under the MIT license, whose details can be found at
4
# the root in the LICENSE file
5
#
6
# -*- coding: utf-8 -*-
7
"""utilities that support the AACGM-V2 C functions.
8
9
References
10
----------
11
Laundal, K. M. and A. D. Richmond (2016), Magnetic Coordinate Systems, Space
12
 Sci. Rev., doi:10.1007/s11214-016-0275-y.
13
14
"""
15
16
from __future__ import division, absolute_import, unicode_literals
17
import datetime as dt
18
import numpy as np
19
20
import aacgmv2
21
22
23
def gc2gd_lat(gc_lat):
24
    """Convert geocentric latitude to geodetic latitude using WGS84.
25
26
    Parameters
27
    -----------
28
    gc_lat : (array_like or float)
29
        Geocentric latitude in degrees N
30
31
    Returns
32
    ---------
33
    gd_lat : (same as input)
34
        Geodetic latitude in degrees N
35
36
    """
37
38
    wgs84_e2 = 0.006694379990141317 - 1.0
39
    gd_lat = np.rad2deg(-np.arctan(np.tan(np.deg2rad(gc_lat)) / wgs84_e2))
40
41
    return gd_lat
42
43
44
def subsol(year, doy, utime):
45
    """Finds subsolar geocentric longitude and latitude.
46
47
    Parameters
48
    ----------
49
    year : (int)
50
        Calendar year between 1601 and 2100
51
    doy : (int)
52
        Day of year between 1-365/366
53
    utime : (float)
54
        Seconds since midnight on the specified day
55
56
    Returns
57
    -------
58
    sbsllon : (float)
59
        Subsolar longitude in degrees E for the given date/time
60
    sbsllat : (float)
61
        Subsolar latitude in degrees N for the given date/time
62
63
    Raises
64
    ------
65
    ValueError if year is out of range
66
67
    Notes
68
    -----
69
    Based on formulas in Astronomical Almanac for the year 1996, p. C24.
70
    (U.S. Government Printing Office, 1994). Usable for years 1601-2100,
71
    inclusive. According to the Almanac, results are good to at least 0.01
72
    degree latitude and 0.025 degrees longitude between years 1950 and 2050.
73
    Accuracy for other years has not been tested. Every day is assumed to have
74
    exactly 86400 seconds; thus leap seconds that sometimes occur on December
75
    31 are ignored (their effect is below the accuracy threshold of the
76
    algorithm).
77
78
    References
79
    ----------
80
    After Fortran code by A. D. Richmond, NCAR. Translated from IDL
81
    by K. Laundal.
82
83
    """
84
85
    # Convert from 4 digit year to 2 digit year
86
    yr2 = year - 2000
87
88
    if year >= 2101 or year <= 1600:
89
        raise ValueError('subsol valid between 1601-2100. Input year is:', year)
90
91
    # Determine if this year is a leap year
92
    nleap = np.floor((year - 1601) / 4)
93
    nleap = nleap - 99
94
    if year <= 1900:
95
        ncent = np.floor((year - 1601) / 100)
96
        ncent = 3 - ncent
97
        nleap = nleap + ncent
98
99
    # Calculate some of the coefficients needed to deterimine the mean longitude
100
    # of the sun and the mean anomaly
101
    l_0 = -79.549 + (-0.238699 * (yr2 - 4 * nleap) + 3.08514e-2 * nleap)
102
    g_0 = -2.472 + (-0.2558905 * (yr2 - 4 * nleap) - 3.79617e-2 * nleap)
103
104
    # Days (including fraction) since 12 UT on January 1 of IYR2:
105
    dfrac = (utime / 86400 - 1.5) + doy
106
107
    # Mean longitude of Sun:
108
    l_sun = l_0 + 0.9856474 * dfrac
109
110
    # Mean anomaly:
111
    grad = np.radians(g_0 + 0.9856003 * dfrac)
112
113
    # Ecliptic longitude:
114
    lmrad = np.radians(l_sun + 1.915 * np.sin(grad) + 0.020 * np.sin(2 * grad))
115
    sinlm = np.sin(lmrad)
116
117
    # Days (including fraction) since 12 UT on January 1 of 2000:
118
    epoch_day = dfrac + 365.0 * yr2 + nleap
119
120
    # Obliquity of ecliptic:
121
    epsrad = np.radians(23.439 - 4.0e-7 * epoch_day)
122
123
    # Right ascension:
124
    alpha = np.degrees(np.arctan2(np.cos(epsrad) * sinlm, np.cos(lmrad)))
125
126
    # Declination, which is the subsolar latitude:
127
    sbsllat = np.degrees(np.arcsin(np.sin(epsrad) * sinlm))
128
129
    # Equation of time (degrees):
130
    etdeg = l_sun - alpha
131
    etdeg = etdeg - 360.0 * np.round(etdeg / 360.0)
132
133
    # Apparent time (degrees):
134
    aptime = utime / 240.0 + etdeg    # Earth rotates one degree every 240 s.
135
136
    # Subsolar longitude:
137
    sbsllon = 180.0 - aptime
138
    sbsllon = sbsllon - 360.0 * np.round(sbsllon / 360.0)
139
140
    return sbsllon, sbsllat
141
142
143
def igrf_dipole_axis(date):
144
    """Get Cartesian unit vector pointing at dipole pole in the north,
145
    according to IGRF
146
147
    Parameters
148
    ----------
149
    date : (dt.datetime)
150
        Date and time
151
152
    Returns
153
    -------
154
    m_0: (np.ndarray)
155
        Cartesian 3 element unit vector pointing at dipole pole in the north
156
        (geocentric coords)
157
158
    Notes
159
    -----
160
    IGRF coefficients are read from the igrf12coeffs.txt file. It should also
161
    work after IGRF updates.  The dipole coefficients are interpolated to the
162
    date, or extrapolated if date > latest IGRF model
163
164
    """
165
166
    # get time in years, as float:
167
    year = date.year
168
    doy = date.timetuple().tm_yday
169
    year_days = dt.date(date.year, 12, 31).timetuple().tm_yday
170
    year = year + doy / year_days
171
172
    # read the IGRF coefficients
173
    with open(aacgmv2.IGRF_COEFFS, 'r') as f_igrf:
174
        lines = f_igrf.readlines()
175
176
    years = lines[3].split()[3:][:-1]
177
    years = np.array(years, dtype=float)  # time array
178
179
    g10 = lines[4].split()[3:]
180
    g11 = lines[5].split()[3:]
181
    h11 = lines[6].split()[3:]
182
183
    # secular variation coefficients (for extrapolation)
184
    g10sv = np.float32(g10[-1])
185
    g11sv = np.float32(g11[-1])
186
    h11sv = np.float32(h11[-1])
187
188
    # model coefficients:
189
    g10 = np.array(g10[:-1], dtype=float)
190
    g11 = np.array(g11[:-1], dtype=float)
191
    h11 = np.array(h11[:-1], dtype=float)
192
193
    # get the gauss coefficient at given time:
194
    if year <= years[-1] and year >= years[0]:
195
        # regular interpolation
196
        g10 = np.interp(year, years, g10)
197
        g11 = np.interp(year, years, g11)
198
        h11 = np.interp(year, years, h11)
199
    else:
200
        # extrapolation
201
        dyear = year - years[-1]
202
        g10 = g10[-1] + g10sv * dyear
203
        g11 = g11[-1] + g11sv * dyear
204
        h11 = h11[-1] + h11sv * dyear
205
206
    # calculate pole position
207
    B_0 = np.sqrt(g10**2 + g11**2 + h11**2)
208
209
    # Calculate output
210
    m_0 = -np.array([g11, h11, g10]) / B_0
211
212
    return m_0
213