def demosuncor(T, glat, glon, alt_m): # %% Solar location with GLOW yd, utsec = datetime2yeardoy(T)[:2] solar = xarray.DataArray(np.empty((T.size, 2, 4)), dims=['time', 'algorithm', 'coord'], coords={ 'time': T, 'algorithm': ['glow', 'astropy'], 'coord': ['dec', 'ra', 'gst', 'sza'] }) tic = time() for d, s, t in zip(yd, utsec, T): solar.loc[t, 'glow', ['dec', 'ra', 'gst']] = glowfort.sun_angles.suncor(d, s) fortsec = time() - tic solar.loc[:, 'glow', ['dec', 'ra', 'gst']] = np.degrees( solar.loc[:, 'glow', ['dec', 'ra', 'gst']]) # %% solar location with AstroPy tic = time() sza, sun, sunobs = solarzenithangle(T, glat, glon, alt_m) pysec = time() - tic solar.loc[:, 'astropy', 'dec'] = sun.dec.degree solar.loc[:, 'astropy', 'ra'] = sun.ra.degree solar.loc[:, 'astropy', 'gst'] = sunobs.obstime.sidereal_time('apparent', 'greenwich').degree solar.loc[:, 'astropy', 'sza'] = sza print( f'in seconds, fortran time: {fortsec:.3f} python time: {pysec:.3f} ') return solar
def demosolzen(t: datetime, glat: float, glon: float): """demo the solar zenith angle calclation vs AstroPy""" # %% SZA with glow yd, utsec = datetime2yeardoy(dtime) sza_glow = np.empty_like(dtime, dtype=float) for j, (d, s) in enumerate(zip(yd, utsec)): sza_glow[j] = glowfort.sun_angles.solzen(d, s, glat, glon) return sza_glow
def test_yearint(): for t in T: yd, utsec = sd.datetime2yeardoy(t) utsec2 = sd.datetime2utsec(t) if isinstance(t, datetime.datetime): assert sd.yeardoy2datetime(yd, utsec) == t elif isinstance(t, np.datetime64): assert sd.yeardoy2datetime(yd, utsec) == t.astype(datetime.datetime) elif isinstance(t, str): assert sd.yeardoy2datetime(yd, utsec) == parse(t) else: assert sd.yeardoy2datetime(yd, utsec).date() == t assert utsec == utsec2 # %% array y, s = sd.datetime2yeardoy(Tdt) assert (sd.yeardoy2datetime(y, s) == T[0]).all()
def runglowaurora(params: dict, z_km: np.ndarray = None) -> xarray.Dataset: """ Runs Fortran GLOW program and collects results in friendly arrays with metadata. """ # %% (-2) check/process user inputs assert isinstance(params['flux'], (float, int, np.ndarray)) assert isinstance(params['E0'], (float, int)) assert isinstance(params['t0'], (datetime, str)) assert isinstance(params['glat'], (float, int)) assert isinstance(params['glon'], (float, int)) # %% (-1) if no manual f10.7 and ap, autoload by date if not 'f107a' in params or params['f107a'] is None: if getApF107 is None: raise ImportError(GRIDERR) f107Ap = getApF107(params['t0']) params['f107a'] = params['f107p'] = f107Ap['f107s'].item() params['f107'] = f107Ap['f107'].item() params['Ap'] = (f107Ap['Ap'].item(), ) * 7 # %% flux grid / date eflux = np.atleast_1d(params['flux']) yeardoy, utsec = datetime2yeardoy(params['t0'])[:2] # %% (0) define altitude grid [km] # FIXME: dynamic grid z_km = np.array( list(range(80, 110, 1)) + [110., 111.5, 113., 114.5] + list(range(116, 136, 2)) + [137., 140., 144., 148., 153., 158., 164., 170] + list( chain(range(176, 204, 7), range(205, 253, 8), range(254, 299, 9), range(300, 650, 10)))) if z_km is None: if glowalt is not None: z_km = glowalt() else: raise ImportError(GRIDERR) # %% (1) setup flux at top of ionosphere ener, dE = glowfort.egrid() if eflux.size == 1: logging.info( 'generating maxwellian input differential number flux spectrum') # maxwellian input PhiTop at top of ionosphere phitop = glowfort.maxt(eflux, params['E0'], ener, dE, itail=0, fmono=0, emono=0) elif eflux.size > 1: # eigenprofile generation, one non-zero bin at a time logging.info('running in eigenprofile mode') # FIXME should we interpolate instead? Maybe not, as long as we're consistent ref. Semeter 2006 e0ind = find_nearest(ener, params['e0'])[0] phitop = np.zeros_like(ener) phitop[e0ind] = ener[e0ind] # value in glow grid closest to zett grid else: return ValueError( 'I do not understand your electron flux input. Should be scalar or vector' ) phi = np.stack((ener, dE, phitop), 1) # Nalt x 3 assert phi.shape[1] == 3 # %% (2) msis,iri,glow model glowfort.glowbasic( yeardoy, utsec, params['glat'], params['glon'] % 360, params['f107a'], params['f107'], params['f107p'], params['Ap'], z_km, pyphi=phi, pyverbose=False, ) # %% (3) collect outputs lamb = [ 3371, 4278, 5200, 5577, 6300, 7320, 10400, 3466, 7774, 8446, 3726, 'LBH', 1356, 1493, 1304 ] # same for ZETA and ZCETA ions = [ 'nO+(2P)', 'nO+(2D)', 'nO+(4S)', 'nN+', 'nN2+', 'nO2+', 'nNO+', 'nO', 'nO2', 'nN2', 'nNO' ] neut = ['O', 'O2', 'N2'] sim = xarray.Dataset() # %% array of volume emission rates at each altitude; cm-3 s-1: sim['zeta'] = xarray.DataArray(glowfort.cglow.zeta.T, dims=['z_km', 'wavelength_nm'], coords={ 'z_km': z_km, 'wavelength_nm': lamb }) # %% array of contributions to each v.e.r at each alt; cm-3 s-1 Nalt x Nwavelength x Nprocess sim['zceta'] = xarray.DataArray( glowfort.cglow.zceta.T, # See Glow.txt. dims=['z_km', 'wavelength_nm', 'process'], coords={ 'z_km': z_km, 'wavelength_nm': lamb, 'process': range(glowfort.cglow.zceta.T.shape[2]) }) # %% electron impact ionization rates calculated by ETRANS; cm-3 s-1 sim['sion'] = xarray.DataArray(glowfort.cglow.sion.T, dims=['z_km', 'neutral_species'], coords={ 'z_km': z_km, 'neutral_species': neut }) # %% total photoionization rate at each altitude, cm-3 s-1 sim['tpi'] = xarray.DataArray(glowfort.cglow.tpi, dims=['z_km'], coords={'z_km': z_km}) # %% total electron impact ionization rate at each altitude, cm-3 s-1 sim['tei'] = xarray.DataArray(glowfort.cglow.tei, dims=['z_km'], coords={'z_km': z_km}) # %% PESPEC photoelectron production rate at energy, altitude; cm-3 s-1 sim['pespec'] = xarray.DataArray(glowfort.cglow.pespec.T, dims=['z_km', 'eV'], coords={ 'z_km': z_km, 'eV': ener }) # sim['photIon'] = xarray.DataArray(np.hstack((photI[:,None],ImpI[:,None],ecalc[:,None],ion)), # dims=['z_km','type'], # coords={'z_km':z_km, # 'type':['photoIoniz','eImpactIoniz','ne']+products}) # # sim['isr'] = xarray.DataArray(isr, # dims=['z_km','param'], # coords={'z_km':z_km,'param':['ne','Te','Ti']}) # # sim['phitop'] = xarray.DataArray(phi[:,2], # dims=['eV'], # coords={'eV':phi[:,0]}) # sim.attrs['sza'] = np.degrees(glowfort.cglow.sza) # # sim['tez'] = xarray.DataArray(glowfort.cglow.tez, # dims=['z_km'], coords={'z_km':z_km}) # %% production and loss rates # prate = prate.T; lrate=lrate.T #fortran to C ordering 2x170x20, only first 12 columns are used # # #column labels by inspection of fortran/gchem.f staring after "DO 150 I=1,JMAX" (thanks Stan!) # sim['prates'] = xarray.DataArray(prate[1,:,:12], #columns 12:20 are identically zero # dims=['z_km','reaction'], # coords={'z_km':z_km, # 'reaction':['O+(2P)','O+(2D)','O+(4S)','N+','N2+','O2+','NO+', # 'N2(A)','N(2P)','N(2D)','O(1S)','O(1D)']} # ) # # sim['lrates'] = xarray.DataArray(lrate[1,:,:12], #columns 12:20 are identically zero # dims=['z_km','reaction'], # coords={'z_km':z_km, # 'reaction':['O+(2P)','O+(2D)','O+(4S)','N+','N2+','O2+','NO+', # 'N2(A)','N(2P)','N(2D)','O(1S)','O(1D)']} # ) # # sim['sion'] = xarray.DataArray(glowfort.cglow.sion, # dims=['gas','z_km'], # coords={'gas':['O','O2','N2'], # 'z_km':z_km}) return sim
def rundayglow(time, glat, glon, f107a, f107, f107p, ap, conj=True): ''' Run GLOW for no auroral input, to simulate Dayglow. conj = whether to account for photoelectrons from conjugate hemisphere After running, extra variables can be found in glowfort.cglow. Unfortunately you have to dig into the Fortran code to see what they mean. ''' # %% (-2) check/process user inputs assert isinstance(time, (datetime, str)) assert isinstance(glat, (float, int)) assert isinstance(glon, (float, int)) # %% flux grid / date yd, utsec = datetime2yeardoy(time)[:2] # %% (0) define altitude grid [km] # z = glowalt() z = np.concatenate( (range(30, 110, 1), np.logspace(np.log10(110), np.log10(1200), 90))) # %% (1) setup external flux at top of ionosphere. Set it to zero. Photoelectron flux from # conjugate hemisphere will be calculated internally, if conj=True. ener, dE = glowfort.egrid() phitop = np.zeros_like(ener) phi = np.hstack((ener[:, None], dE[:, None], phitop[:, None])) # %% (2) msis,iri,glow model iconj = int(conj) # convert boolean to int for passing to Fortran. ion, ecalc, photI, ImpI, isr, UV = glowfort.dayglow( z, yd, utsec, glat, glon % 360, f107a, f107, f107p, ap, phi, iconj) # %% handle the outputs including common blocks zeta = glowfort.cglow.zeta.T # columns 11:20 are identically zero lamb = [ 3371., 4278., 5200., 5577., 6300., 7320., 10400., 3466., 7774., 8446., 3726., 1356., 1304., 1027., 989., 1900. ] products = [ 'nO+(2P)', 'nO+(2D)', 'nO+(4S)', 'nN+', 'nN2+', 'nO2+', 'nNO+', 'nO', 'nO2', 'nN2', 'nNO' ] sim = xarray.Dataset() sim['ver'] = xarray.DataArray(np.concatenate((zeta[:, :11], UV.T), axis=1), dims=['z_km', 'wavelength_nm'], coords={ 'z_km': z, 'wavelength_nm': lamb }) sim['photIon'] = xarray.DataArray( dims=['z_km', 'type'], coords={ 'z_km': z, 'type': ['photoIoniz', 'eImpactIoniz', 'ne'] + products }, data=np.hstack((photI[:, None], ImpI[:, None], ecalc[:, None], ion))) sim['isr'] = xarray.DataArray(dims=['z_km', 'param'], coords={ 'z_km': z, 'param': ['ne', 'Te', 'Ti'] }, data=isr) sim['phitop'] = xarray.DataArray(dims=['eV'], coords={'eV': phi[:, 0]}, data=phi[:, 2]) sim['zceta'] = xarray.DataArray( dims=['z_km', 'wavelength_nm', 'type'], coords={ 'z_km': z, 'wavelength_nm': lamb[:11] }, data=glowfort.cglow.zceta.T[:, :11, :] ) # Nalt x Nwavelengths xNproductionEmissions sim.attrs['sza'] = np.degrees(glowfort.cglow.sza) sim['tez'] = xarray.DataArray(dims=['z_km'], coords={'z_km': z}, data=glowfort.cglow.tez) sim['sion'] = xarray.DataArray(dims=['gas', 'z_km'], coords={ 'gas': ['O', 'O2', 'N2'], 'z_km': z }, data=glowfort.cglow.sion) return sim
jmax = 170 # glow.h eflux = 1. e0 = 1e3 maxind = 112 glat = 70 glon = 0 # like aurora.in ap = 4 f107 = 100 f107a = 100 nmaj = 3 nst = 6 dtime = datetime(1999, 12, 21) # yd, utsec = datetime2yeardoy(dtime)[:2] def test_pythonglow(): params = {'t0': '2013-04-14T15:54', 'glat': 65, 'glon': -148, 'flux': 1, 'E0': 1000, 'makeplot': [], 'zlim': None, 'plotformat': [], } sim = glowiono.runglowaurora(params)