ATMOS/pyatmos/jb2008/JB2008_subfunc.py
2021-06-07 13:00:32 +08:00

457 lines
16 KiB
Python

import numpy as np
from numba import jit
from ..utils import Const
@jit(nopython=True)
def JB2008(AMJD,YRDAY,SUN,SAT,F10,F10B,S10,S10B,M10,M10B,Y10,Y10B,DSTDTC):
'''
Jacchia-Bowman 2008 Model Atmosphere
This is the CIRA "Integration Form" of a Jacchia Model.
There are no tabular values of density. Instead, the barometricequation and diffusion equation are integrated numerically using
the Newton-Coates method to produce the density profile up to the input position.
INPUT:
AMJD : Date and Time, in modified Julian Days and Fraction (MJD = JD-2400000.5)
SUN[0] : Right Ascension of Sun (radians)
SUN[1] : Declination of Sun (radians)
SAT[0] : Right Ascension of Position (radians)
SAT[1] : Geocentric Latitude of Position (radians)
SAT[2] : Height of Position (km)
F10 : 10.7-cm Solar Flux (1.0E-22*W/(M**2*Hz)) (Tabular time 1.0 day earlier)
F10B : 10.7-cm Solar Flux, ave. 81-day centered on the input time (Tabular time 1.0 day earlier)
S10 : EUV index (26-34 nm) scaled to F10 (Tabular time 1.0 day earlier)
S10B : EUV 81-day ave. centered index (Tabular time 1.0 day earlier)
M10 : MG2 index scaled to F10 (Tabular time 2.0 days earlier)
M10B : MG2 81-day ave. centered index (Tabular time 2.0 days earlier)
Y10 : Solar X-Ray & Lya index scaled to F10 (Tabular time 5.0 days earlier)
Y10B : Solar X-Ray & Lya 81-day ave. centered index (Tabular time 5.0 days earlier)
DSTDTC : Temperature change computed from Dst index
OUTPUT:
TEMP(1): Exospheric Temperature above Input Position (K)
TEMP(2): Temperature at Input Position (K)
RHO : Total Mass-Desnity at Input Position (kg/m**3)
Reference:
Bowman, Bruce R., etc. : "A New Empirical Thermospheric
Density Model JB2008 Using New Solar and Geomagnetic Indices",
AIAA/AAS 2008, COSPAR CIRA 2008 Model
Note:
The program is translated from the fortran source code written by Bruce R Bowman (HQ AFSPC, Space Analysis Division), 2008
'''
# The alpha are the thermal diffusion coefficients in Eq. (6)
TEMP = np.zeros(2)
ALPHA = np.zeros(5)
ALPHA[4] = -0.38
# AL10 is DLOG(10.0)
AL10 = Const.al10
# The AMW are the molecular weights in order: N2, O2, O, Ar, He & H
AMW = np.array([28.0134,31.9988,15.9994,39.9480,4.0026,1.00797])
# AVOGAD is Avogadro's number in mks units (molecules/kmol)
AVOGAD = Const.avogad
PI = Const.pi
TWOPI,FOURPI = Const.twopi,Const.fourpi
PIOV2,PIOV4 = Const.pivo2,Const.pivo4
# The FRAC are the assumed sea-level volume fractions in order: N2, O2, Ar, and He
FRAC = np.array([0.7811,0.20955,9.34e-3,1.289e-5])
# RSTAR is the universal gas-constant in mks units (joules/K/kmol)
RSTAR = Const.rstar
# The R# are values used to establish height step sizes in the regimes 90km to 105km, 105km to 500km and 500km upward.
R1,R2,R3 = 0.01,0.025,0.075
# The WT are weights for the Newton-Cotes Five-Point Quad. formula
WT = np.array([7,32,12,32,7])*2/45
# The CHT are coefficients for high altitude density correction
CHT = np.array([0.22,-0.2e-2,0.115e-2,-0.211e-5])
DEGRAD = Const.degrad
# Equation (14)
FN = (F10B/240)**0.25
if FN > 1: FN = 1
FSB = F10B*FN + S10B*(1 - FN)
TSUBC = 392.4 + 3.227*FSB + 0.298*(F10-F10B) + 2.259*(S10-S10B) + 0.312*(M10-M10B) + 0.178*(Y10-Y10B)
# Equation (15)
ETA = np.abs(SAT[1] - SUN[1])/2
THETA = np.abs(SAT[1] + SUN[1])/2
# Equation (16)
H = SAT[0] - SUN[0]
TAU = H - 0.64577182 + 0.10471976 * np.sin(H + 0.75049158)
GLAT,ZHT = SAT[1],SAT[2]
GLST = H + PI
GLSTHR = (GLST/DEGRAD)*(24/360)
if GLSTHR >= 24: GLSTHR -= 24
if GLSTHR < 0: GLSTHR += 24
# Equation (17)
C = np.cos(ETA)**2.5
S = np.sin(THETA)**2.5
DF = S + (C - S) * np.abs(np.cos(0.5 * TAU))**3
TSUBL = TSUBC * (1 + 0.31 * DF)
# Compute correction to dTc for local solar time and lat correction
DTCLST = DTSUB(F10,GLSTHR,GLAT,ZHT)
# Compute the local exospheric temperature.
# Add geomagnetic storm effect from input dTc value
TEMP[0] = TSUBL + DSTDTC
TINF = TEMP[0] + DTCLST
# Equation (9)
TSUBX = 444.3807 + 0.02385 * TINF - 392.8292 * np.exp(-0.0021357 * TINF)
# Equation (11)
GSUBX = 0.054285714 * (TSUBX - 183)
# The TC array will be an argument in the call to XLOCAL, which evaluates Equation (10) or Equation (13)
TC = np.zeros(4)
TC[0],TC[1] = TSUBX,GSUBX
# A AND GSUBX/A OF Equation (13)
TC[2] = (TINF - TSUBX)/PIOV2
TC[3] = GSUBX/TC[2]
# Equation (5)
Z1 = 90
Z2 = min(SAT[2],105)
AL = np.log(Z2/Z1)
N = int(AL/R1) + 1
ZR = np.exp(AL/N)
AMBAR1 = XAMBAR(Z1)
TLOC1 = XLOCAL(Z1,TC)
ZEND = Z1
SUM2 = 0
AIN = AMBAR1 * XGRAV(Z1)/TLOC1
for I in range(N):
Z = ZEND
ZEND = ZR * Z
DZ = 0.25 * (ZEND-Z)
SUM1 = WT[0]*AIN
for J in range(1,5):
Z += DZ
AMBAR2 = XAMBAR(Z)
TLOC2 = XLOCAL(Z,TC)
GRAVL = XGRAV(Z)
AIN = AMBAR2 * GRAVL/TLOC2
SUM1 += WT[J] * AIN
SUM2 += DZ * SUM1
FACT1 = 1e3/RSTAR
RHO = 3.46e-6 * AMBAR2 * TLOC1 * np.exp(-FACT1*SUM2) /AMBAR1 /TLOC2
# Equation (2)
ANM = AVOGAD * RHO
AN = ANM/AMBAR2
# Equation (3)
FACT2 = ANM/28.96
ALN = np.zeros(6)
ALN[0] = np.log(FRAC[0]*FACT2)
ALN[3] = np.log(FRAC[2]*FACT2)
ALN[4] = np.log(FRAC[3]*FACT2)
# Equation (4)
ALN[1] = np.log(FACT2 * (1 + FRAC[1]) - AN)
ALN[2] = np.log(2 * (AN - FACT2))
if SAT[2] <= 105:
TEMP[1] = TLOC2
# Put in negligible hydrogen for use in DO-LOOP 13
ALN[5] = ALN[4] - 25
else:
# Equation (6)
Z3 = min(SAT[2],500)
AL = np.log(Z3/Z)
N = int(AL/R2) + 1
ZR = np.exp(AL/N)
SUM2 = 0
AIN = GRAVL/TLOC2
for I in range(N):
Z = ZEND
ZEND = ZR * Z
DZ = 0.25 * (ZEND - Z)
SUM1 = WT[0] * AIN
for J in range(1,5):
Z += DZ
TLOC3 = XLOCAL(Z,TC)
GRAVL = XGRAV(Z)
AIN = GRAVL/TLOC3
SUM1 += WT[J] * AIN
SUM2 += DZ * SUM1
Z4 = max(SAT[2],500)
AL = np.log(Z4/Z)
R = R2
if SAT[2] > 500: R = R3
N = int(AL/R) + 1
ZR = np.exp(AL/N)
SUM3 = 0
for I in range(N):
Z = ZEND
ZEND = ZR * Z
DZ = 0.25 * (ZEND - Z)
SUM1 = WT[0] * AIN
for J in range(1,5):
Z += DZ
TLOC4 = XLOCAL(Z,TC)
GRAVL = XGRAV(Z)
AIN = GRAVL/TLOC4
SUM1 += WT[J] * AIN
SUM3 += DZ * SUM1
if SAT[2] <= 500:
T500 = TLOC4
TEMP[1] = TLOC3
ALTR = np.log(TLOC3/TLOC2)
FACT2 = FACT1 * SUM2
HSIGN = 1
else:
T500 = TLOC3
TEMP[1] = TLOC4
ALTR = np.log(TLOC4/TLOC2)
FACT2 = FACT1 * (SUM2 + SUM3)
HSIGN = -1
ALN[:-1] -= (1 + ALPHA) * ALTR + FACT2 * AMW[:-1]
# Equation (7) - Note that in CIRA72, AL10T5 = DLOG10(T500)
AL10T5 = np.log10(TINF)
ALNH5 = (5.5 * AL10T5 - 39.4) * AL10T5 + 73.13
ALN[5] = AL10 * (ALNH5 + 6) + HSIGN * (np.log(TLOC4/TLOC3) + FACT1 * SUM3 * AMW[5])
# Equation (24) - J70 Seasonal-Latitudinal Variation
TRASH = (AMJD - 36204) / 365.2422
CAPPHI = TRASH%1
DLRSL = 0.02 * (SAT[2] - 90) * np.exp(-0.045 * (SAT[2] - 90)) * np.sign(SAT[1]) * np.sin(TWOPI * CAPPHI+ 1.72) * np.sin(SAT[1])**2
# Equation (23) - Computes the semiannual variation
DLRSA = 0
if Z < 2e3:
# Use new semiannual model
FZZ,GTZ,DLRSA = SEMIAN08(YRDAY,ZHT,F10B,S10B,M10B)
if FZZ < 0: DLRSA = 0
# Sum the delta-log-rhos and apply to the number densities.
# In CIRA72 the following equation contains an actual sum, namely DLR = AL10 * (DLRGM + DLRSA + DLRSL)
# However, for Jacchia 70, there is no DLRGM or DLRSA.
DLR = AL10 * (DLRSL + DLRSA)
ALN += DLR
# Compute mass-density and mean-molecular-weight and convert number density logs from natural to common.
AN = np.exp(ALN)
SUMNM = (AN*AMW).sum()
AL10N = ALN/AL10
RHO = SUMNM/AVOGAD
# Compute the high altitude exospheric density correction factor
FEX = 1
if ZHT >= 1e3 and ZHT < 1.5e3:
ZETA = (ZHT - 1e3) * 0.002
ZETA2 = ZETA**2
ZETA3 = ZETA**3
F15C = CHT[0] + CHT[1]*F10B + (CHT[2] + CHT[3]*F10B)*1.5e3
F15C_ZETA = (CHT[2] + CHT[3]*F10B) * 500
FEX2 = 3 * F15C - F15C_ZETA - 3
FEX3 = F15C_ZETA - 2 * F15C + 2
FEX = 1 + FEX2 * ZETA2 + FEX3 * ZETA3
if ZHT >= 1.5e3: FEX = CHT[0] + CHT[1]*F10B + CHT[2]*ZHT + CHT[3]*F10B*ZHT
# Apply the exospheric density correction factor.
RHO *= FEX
return TEMP,RHO
@jit(nopython=True)
def XAMBAR(Z):
'''
Evaluates Equation (1)
'''
C = np.array([28.15204,-8.5586e-2,1.2840e-4,-1.0056e-5,-1.0210e-5,1.5044e-6,9.9826e-8])
DZ = Z - 100
AMB = C[6]
for i in range(5,-1,-1): AMB = DZ * AMB + C[i]
return AMB
@jit(nopython=True)
def XGRAV(Z):
'''
Evaluates Equation (8)
'''
return 9.80665/(1 + Z/6356.766)**2
@jit(nopython=True)
def XLOCAL(Z,TC):
'''
Evaluates Equation (10) or Equation (13), depending on Z
'''
DZ = Z - 125
if DZ > 0:
XLOCAL = TC[0] + TC[2] * np.arctan(TC[3]*DZ*(1 + 4.5e-6*DZ**2.5))
else:
XLOCAL = ((-9.8204695e-6 * DZ - 7.3039742e-4) * DZ**2 + 1)* DZ * TC[1] + TC[0]
return XLOCAL
@jit(nopython=True)
def DTSUB (F10,XLST,XLAT,ZHT):
'''
COMPUTE dTc correction for Jacchia-Bowman model
Calling Args:
F10 = (I) F10 FLUX
XLST = (I) LOCAL SOLAR TIME (HOURS 0-23.999)
XLAT = (I) XLAT = SAT LAT (RAD)
ZHT = (I) ZHT = HEIGHT (KM)
DTC = (O) dTc correction
'''
B = np.array([-4.57512297, -5.12114909, -69.3003609,\
203.716701, 703.316291, -1943.49234,\
1106.51308, -174.378996, 1885.94601,\
-7093.71517, 9224.54523, -3845.08073,\
-6.45841789, 40.9703319, -482.006560,\
1818.70931, -2373.89204, 996.703815,36.1416936])
C = np.array([-15.5986211, -5.12114909, -69.3003609,\
203.716701, 703.316291, -1943.49234,\
1106.51308, -220.835117, 1432.56989,\
-3184.81844, 3289.81513, -1353.32119,\
19.9956489, -12.7093998, 21.2825156,\
-2.75555432, 11.0234982, 148.881951,\
-751.640284, 637.876542, 12.7093998,\
-21.2825156, 2.75555432])
DTC = 0
tx = XLST/24
ycs = np.cos(XLAT)
F = (F10 - 100)/100
# calculates dTc
if ZHT >= 120 and ZHT <= 200:
H = (ZHT - 200)/50
DTC200 = C[16] + C[17]*tx*ycs + C[18]*tx**2*ycs + C[19]*tx**3*ycs + C[20]*F*ycs + C[21]*tx*F*ycs + C[22]*tx**2*F*ycs
sum_ = C[0] + B[1]*F + C[2]*tx*F + C[3]*tx**2*F + C[4]*tx**3*F + C[5]*tx**4*F + C[6]*tx**5*F +\
C[7]*tx*ycs + C[8]*tx**2*ycs + C[9]*tx**3*ycs + C[10]*tx**4*ycs + C[11]*tx**5*ycs + C[12]*ycs+\
C[13]*F*ycs + C[14]*tx*F*ycs + C[15]*tx**2*F*ycs
DTC200DZ = sum_
CC = 3*DTC200 - DTC200DZ
DD = DTC200 - CC
ZP = (ZHT-120)/80
DTC = CC*ZP*ZP + DD*ZP*ZP*ZP
if ZHT > 200 and ZHT <= 240:
H = (ZHT - 200)/50
sum_ = C[0]*H + B[1]*F*H + C[2]*tx*F*H + C[3]*tx**2*F*H + C[4]*tx**3*F*H + C[5]*tx**4*F*H + C[6]*tx**5*F*H+\
C[7]*tx*ycs*H + C[8]*tx**2*ycs*H + C[9]*tx**3*ycs*H + C[10]*tx**4*ycs*H + C[11]*tx**5*ycs*H + C[12]*ycs*H+\
C[13]*F*ycs*H + C[14]*tx*F*ycs*H + C[15]*tx**2*F*ycs*H + C[16] + C[17]*tx*ycs + C[18]*tx**2*ycs +\
C[19]*tx**3*ycs + C[20]*F*ycs + C[21]*tx*F*ycs + C[22]*tx**2*F*ycs
DTC = sum_
if ZHT > 240 and ZHT <= 300.0:
H = 0.8
sum_ = C[0]*H + B[1]*F*H + C[2]*tx*F*H + C[3]*tx**2*F*H + C[4]*tx**3*F*H + C[5]*tx**4*F*H + C[6]*tx**5*F*H +\
C[7]*tx*ycs*H + C[8]*tx**2*ycs*H + C[9]*tx**3*ycs*H + C[10]*tx**4*ycs*H + C[11]*tx**5*ycs*H + C[12]*ycs*H+\
C[13]*F*ycs*H + C[14]*tx*F*ycs*H + C[15]*tx**2*F*ycs*H + C[16] + C[17]*tx*ycs + C[18]*tx**2*ycs +\
C[19]*tx**3*ycs + C[20]*F*ycs + C[21]*tx*F*ycs + C[22]*tx**2*F*ycs
AA = sum_
BB = C[0] + B[1]*F + C[2]*tx*F + C[3]*tx**2*F + C[4]*tx**3*F + C[5]*tx**4*F + C[6]*tx**5*F +\
C[7]*tx*ycs + C[8]*tx**2*ycs + C[9]*tx**3*ycs + C[10]*tx**4*ycs + C[11]*tx**5*ycs + C[12]*ycs +\
C[13]*F*ycs + C[14]*tx*F*ycs + C[15]*tx**2*F*ycs
H = 3
sum_ = B[0] + B[1]*F + B[2]*tx*F + B[3]*tx**2*F + B[4]*tx**3*F + B[5]*tx**4*F + B[6]*tx**5*F + \
B[7]*tx*ycs + B[8]*tx**2*ycs + B[9]*tx**3*ycs + B[10]*tx**4*ycs + B[11]*tx**5*ycs + B[12]*H*ycs +\
B[13]*tx*H*ycs + B[14]*tx**2*H*ycs + B[15]*tx**3*H*ycs + B[16]*tx**4*H*ycs + B[17]*tx**5*H*ycs + B[18]*ycs
DTC300 = sum_
sum_ = B[12]*ycs + B[13]*tx*ycs + B[14]*tx**2*ycs + B[15]*tx**3*ycs + B[16]*tx**4*ycs + B[17]*tx**5*ycs
DTC300DZ = sum_
CC = 3*DTC300 - DTC300DZ - 3*AA - 2*BB
DD = DTC300 - AA - BB - CC
ZP = (ZHT-240)/60
DTC = AA + BB*ZP + CC*ZP*ZP + DD*ZP*ZP*ZP
if ZHT > 300 and ZHT <= 600:
H = ZHT/100
sum_ = B[0] + B[1]*F + B[2]*tx*F + B[3]*tx**2*F + B[4]*tx**3*F + B[5]*tx**4*F + B[6]*tx**5*F +\
B[7]*tx*ycs + B[8]*tx**2*ycs + B[9]*tx**3*ycs + B[10]*tx**4*ycs + B[11]*tx**5*ycs + B[12]*H*ycs +\
B[13]*tx*H*ycs + B[14]*tx**2*H*ycs + B[15]*tx**3*H*ycs + B[16]*tx**4*H*ycs + B[17]*tx**5*H*ycs + B[18]*ycs
DTC = sum_
if ZHT > 600 and ZHT <= 800.0:
ZP = (ZHT - 600)/100
HP = 6
AA = B[0] + B[1]*F + B[2]*tx*F + B[3]*tx**2*F + B[4]*tx**3*F + B[5]*tx**4*F + B[6]*tx**5*F +\
B[7]*tx*ycs + B[8]*tx**2*ycs + B[9]*tx**3*ycs + B[10]*tx**4*ycs + B[11]*tx**5*ycs + B[12]*HP*ycs +\
B[13]*tx*HP*ycs + B[14]*tx**2*HP*ycs+ B[15]*tx**3*HP*ycs + B[16]*tx**4*HP*ycs + B[17]*tx**5*HP*ycs + B[18]*ycs
BB = B[12]*ycs + B[13]*tx*ycs + B[14]*tx**2*ycs + B[15]*tx**3*ycs + B[16]*tx**4*ycs + B[17]*tx**5*ycs
CC = -(3*AA+4*BB)/4
DD = (AA+BB)/4
DTC = AA + BB*ZP + CC*ZP*ZP + DD*ZP*ZP*ZP
return DTC
@jit(nopython=True)
def SEMIAN08 (DAY,HT,F10B,S10B,M10B):
'''
COMPUTE SEMIANNUAL VARIATION (DELTA LOG RHO)
INPUT DAY, HEIGHT, F10B, S10B, M10B FSMB
025. 650. 150. 148. 147. 151.
OUTPUT FUNCTIONS FZ, GT, AND DEL LOG RHO VALUE
DAY (I) DAY OF YEAR
HT (I) HEIGHT (KM)
F10B (I) AVE 81-DAY CENTERED F10
S10B (I) AVE 81-DAY CENTERED S10
M10B (I) AVE 81-DAY CENTERED M10
FZZ (O) SEMIANNUAL AMPLITUDE
GTZ (O) SEMIANNUAL PHASE FUNCTION
DRLOG (O) DELTA LOG RHO
'''
TWOPI = Const.twopi
# FZ GLOBAL MODEL VALUES
# 1997-2006 FIT:
FZM = np.array([0.2689,-0.01176, 0.02782,-0.02782, 0.3470e-3])
# GT GLOBAL MODEL VALUES
# 1997-2006 FIT:
GTM = np.array([-0.3633, 0.08506, 0.2401,-0.1897, -0.2554,-0.01790, 0.5650e-3,-0.6407e-3,-0.3418e-2,-0.1252e-2])
# COMPUTE NEW 81-DAY CENTERED SOLAR INDEX FOR FZ
FSMB = F10B - 0.7*S10B - 0.04*M10B
HTZ = HT/1e3
FZZ = FZM[0] + FZM[1]*FSMB + FZM[2]*FSMB*HTZ + FZM[3]*FSMB*HTZ**2 + FZM[4]*FSMB**2*HTZ
# COMPUTE DAILY 81-DAY CENTERED SOLAR INDEX FOR GT
FSMB = F10B - 0.75*S10B - 0.37*M10B
TAU = (DAY-1)/365
SIN1P = np.sin(TWOPI*TAU)
COS1P = np.cos(TWOPI*TAU)
SIN2P = np.sin(2*TWOPI*TAU)
COS2P = np.cos(2*TWOPI*TAU)
GTZ = GTM[0] + GTM[1]*SIN1P + GTM[2]*COS1P + GTM[3]*SIN2P + GTM[4]*COS2P + GTM[5]*FSMB + GTM[6]*FSMB*SIN1P + GTM[7]*FSMB*COS1P + GTM[8]*FSMB*SIN2P + GTM[9]*FSMB*COS2P
if FZZ < 1e-6: FZZ = 1e-6
DRLOG = FZZ*GTZ
return FZZ,GTZ,DRLOG