def calc_magdi(self): ''' GITM 3DION and 3DMAG files contain the magnetic field in North-East-Vertical coordinates. This routine computes the magnetic inclination and declination. ''' import math mag = None # Test to determine the type of file we have if(self.attrs['file'].find("ION") > 0 or self.attrs['file'].find("MAG") > 0): mag = self else: if not 'magfile' in self.attrs: print("No 3D MAG/ION file associated with this GITM Binary") elif(self.attrs['magfile'].find("ION") <= 0 and self.attrs['magfile'].find("MAG") <= 0): print("No 3D MAG/ION file associated with this GITM Binary") else: mag = GitmBin(self.attrs['magfile']) if mag is not None: dec_frac = mag['B.F. East'] / mag['B.F. North'] inc_sign = -1.0 * mag['B.F. Vertical'] / abs(mag['B.F. Vertical']) inc_pmag = mag['B.F. North']**2 + mag['B.F. East']**2 for ilon in range(self.attrs['nLon']): for ilat in range(self.attrs['nLat']): for ialt,df in enumerate(dec_frac[ilon,ilat]): dec_frac[ilon,ilat,ialt] = math.atan(df) * 180.0 / np.pi i = (inc_sign[ilon,ilat,ialt] * math.sqrt(inc_pmag[ilon,ilat,ialt]) / mag['B.F. Magnitude'][ilon,ilat,ialt]) inc_pmag[ilon,ilat,ialt] = math.acos(i) * 180.0 / np.pi if inc_pmag[ilon,ilat,ialt] > 90.0: inc_pmag[ilon,ilat,ialt] -= 180.0 self['Declination'] = dmarray.copy(dec_frac) self['Declination'].attrs = {"name":"Declination", "scale":"linear", "units":"degrees"} self['Inclination'] = dmarray.copy(inc_pmag) self['Inclination'].attrs = {"name":"Inclination", "scale":"linear", "units":"degrees"} del dec_frac, inc_sign, inc_pmag if not 'B.F. East' in self: self['B.F. East'] = dmarray.copy(mag['B.F. East']) self['B.F. North'] = dmarray.copy(mag['B.F. North']) self['B.F. Vertical'] = dmarray.copy(mag['B.F. Vertical']) self['B.F. Magnitude'] = dmarray.copy(mag['B.F. Magnitude']) if not 'Magnetic Latitude' in self: self['Magnetic Latitude']=dmarray.copy(mag['Magnetic Latitude']) self['Magnetic Longitude']=dmarray.copy(mag['Magnetic Longitude'])
def calc_magdi(self): """ GITM 3DION and 3DMAG files contain the magnetic field in North-East-Vertical coordinates. This routine computes the magnetic inclination and declination. """ import math mag = None # Test to determine the type of file we have if self.attrs["file"].find("ION") > 0 or self.attrs["file"].find("MAG") > 0: mag = self else: if not self.attrs.has_key("magfile"): print "No 3D MAG/ION file associated with this GITM Binary" elif self.attrs["magfile"].find("ION") <= 0 and self.attrs["magfile"].find("MAG") <= 0: print "No 3D MAG/ION file associated with this GITM Binary" else: mag = GitmBin(self.attrs["magfile"]) if mag is not None: dec_frac = mag["B.F. East"] / mag["B.F. North"] inc_sign = -1.0 * mag["B.F. Vertical"] / abs(mag["B.F. Vertical"]) inc_pmag = mag["B.F. North"] ** 2 + mag["B.F. East"] ** 2 for ilon in range(self.attrs["nLon"]): for ilat in range(self.attrs["nLat"]): for ialt, df in enumerate(dec_frac[ilon, ilat]): dec_frac[ilon, ilat, ialt] = math.atan(df) * 180.0 / np.pi i = ( inc_sign[ilon, ilat, ialt] * math.sqrt(inc_pmag[ilon, ilat, ialt]) / mag["B.F. Magnitude"][ilon, ilat, ialt] ) inc_pmag[ilon, ilat, ialt] = math.acos(i) * 180.0 / np.pi if inc_pmag[ilon, ilat, ialt] > 90.0: inc_pmag[ilon, ilat, ialt] -= 180.0 self["Declination"] = dmarray.copy(dec_frac) self["Declination"].attrs = {"name": "Declination", "scale": "linear", "units": "degrees"} self["Inclination"] = dmarray.copy(inc_pmag) self["Inclination"].attrs = {"name": "Inclination", "scale": "linear", "units": "degrees"} del dec_frac, inc_sign, inc_pmag if not self.has_key("B.F. East"): self["B.F. East"] = dmarray.copy(mag["B.F. East"]) self["B.F. North"] = dmarray.copy(mag["B.F. North"]) self["B.F. Vertical"] = dmarray.copy(mag["B.F. Vertical"]) self["B.F. Magnitude"] = dmarray.copy(mag["B.F. Magnitude"]) if not self.has_key("Magnetic Latitude"): self["Magnetic Latitude"] = dmarray.copy(mag["Magnetic Latitude"]) self["Magnetic Longitude"] = dmarray.copy(mag["Magnetic Longitude"])
def append_data(self, varlist): ''' A routine to append more variables to an existing GITM data structure. New variables can only be added from the same file (specified in the 'file' attribute). ''' temp = GitmBin(self.attrs['file'], varlist, False) for nkey in temp.keys(): if not nkey in self: self[nkey] = dmarray.copy(temp[nkey])
def calc_2dion(self): ''' A routine to calculate the 2D ionospheric parameters: VTEC, hmF2, NmF2. To perform these calculations, electron density ("e-") must be one of the available data types. ''' import scipy.integrate as integ from scipy.interpolate import interp1d from scipy.signal import argrelextrema calc_tec = False if 'e-' in self: if 'VTEC' in self is False: calc_tec = True self['VTEC'] = dmarray(self['e-'] * 1.0e-16, attrs={ 'units': 'TECU', 'scale': 'linear', 'name': 'Vertical TEC' }) self['NmF2'] = dmarray.copy(self['e-']) self['NmF2'].attrs['name'] = 'N$_m$F$_2$' self['hmF2'] = dmarray(self['Altitude'] / 1000.0, attrs={ 'units': 'km', 'scale': 'linear', 'name': 'h$_m$F$_2$' }) alt = np.linspace(min(self['hmF2'][0, 0, 2:-2]), max(self['hmF2'][0, 0, 2:-2]), 1000) for ilon in range(self.attrs['nLon']): for ilat in range(self.attrs['nLat']): if calc_tec is True: # Integrate electron density over altitude vtec = integ.simps(self['VTEC'][ilon, ilat, 2:-2], self['Altitude'][ilon, ilat, 2:-2], "avg") self['VTEC'][ilon, ilat, :] = vtec # Interpolate over the electron density altitude profile eprof = interp1d(self['hmF2'][ilon, ilat, 2:-2], self['NmF2'][ilon, ilat, 2:-2], kind="cubic") try: edens = eprof(alt) except: alt = np.linspace(min(self['hmF2'][ilon, ilat, 2:-2]), max(self['hmF2'][ilon, ilat, 2:-2]), 1000) edens = eprof(alt) # Find the local maxima of the electron density profile emax = argrelextrema(edens, np.greater) emax_list = list(emax[0]) saddle = False if len(emax_list) == 0: # No local maxima were found, try identifying # saddle points saddle = True elif len(emax_list) == 1: if max(edens) == edens[emax_list[0]]: # Only one maxima exists and is realistic. Sometimes # a profile will have inflection points instead of # local maxima and this can confuse the routine self['NmF2'][ilon, ilat, :] = edens[emax_list[0]] self['hmF2'][ilon, ilat, :] = alt[emax_list[0]] else: saddle = True elif alt[emax_list[-1]] < 120.0: saddle = True else: # More than one maxima exists. Seperate hmF2 from hmF1 # and spurious local maxima NmF2 = list(edens[emax_list]) HmF2 = list(alt[emax_list]) # If the global maximum is over 200 km, # this is the F2 peak eindex = NmF2.index(max(NmF2)) if HmF2[eindex] <= 200.0 and HmF2[eindex] == min(HmF2): # The global max may be the F1 peak, see if the # secondary (or lessor) maxima is at the upper # limit of the model. If so, remove this point # from consideration if max(HmF2) > self['hmF2'][ilon, ilat, -5]: eindex = HmF2.index(max(HmF2)) emax_list.pop(eindex) NmF2.pop(eindex) HmF2.pop(eindex) eindex = NmF2.index(max(NmF2)) if len(emax_list) > 1: # If there are multiple maxima after the upper # boundary has been removed, choose the largest # maxima above 200 km since the hmF1 is often # larger than the hmF2 emax_list.pop(eindex) NmF2.pop(eindex) eindex = NmF2.index(max(NmF2)) # Set the hmF2 and NmF2 self['NmF2'][ilon, ilat, :] = edens[emax_list[eindex]] self['hmF2'][ilon, ilat, :] = alt[emax_list[eindex]] if saddle: # It is difficult to find saddle points. Examine # the rate of change of density delta_alt = alt[1] - alt[0] # Equally spaced edens_dot = np.diff(edens) / delta_alt # Identify inflection points by looking for # minima in the derivative of electron density. edot_min = argrelextrema(edens_dot, np.less) emin_list = list(edot_min[0]) edens_min = list(edens[emin_list]) emax = np.max(edens_min) eindex = emin_list[edens_min.index(emax)] # Assign the inflection with the largest # electron density to the ion peak self['NmF2'][ilon, ilat, :] = edens[eindex] self['hmF2'][ilon, ilat, :] = alt[eindex]
def calc_magvel(self): ''' Gitm 3DIon files contain the magnetic coordinates that allow the field-aligned and field-perpendicular velocities to be computed. The 3DIon file must be from the same run as the 3DAll file so that the geographic grid is the same. If a 3D Ion file was not produced in the original run, don't fret! You can get one by using the same UAM.in file. Unless the magnetic field is varying secularly, you can get away with producing just one 3DIon file. It is better to have a matching 3D Ion file for every 3D All file, however. ''' import math import string import sys ion = None # If this is a 3D ION file we don't need a mag file if self.attrs['file'].find("ION") > 0: ion = self else: if not 'magfile' in self.attrs: print("No 3D MAG/ION file associated with this GITM Binary") elif (self.attrs['magfile'].find("ION") <= 0 and self.attrs['magfile'].find("MAG") <= 0): print("No 3D MAG/ION file associated with this GITM Binary") else: ion = GitmBin(self.attrs['magfile']) # Compute the field-aligned unit vector in East, North, # and Vertical coordinates if ion: bhat_e = ion['B.F. East'] / ion['B.F. Magnitude'] bhat_n = ion['B.F. North'] / ion['B.F. Magnitude'] bhat_v = ion['B.F. Vertical'] / ion['B.F. Magnitude'] # Compute the zonal unit vector in East, North, Vertical coord mag = np.sqrt( np.square(ion['B.F. East']) + np.square(ion['B.F. North'])) zhat_e = ion['B.F. North'] / mag zhat_n = ion['B.F. East'] / mag # zhat_v is identically zero # Compute the meridional unit vector in East, North, Vertical coord mhat_e = (-ion['B.F. East'] * ion['B.F. Vertical'] / (mag * ion['B.F. Magnitude'])) mhat_n = (-ion['B.F. North'] * ion['B.F. Vertical'] / (mag * ion['B.F. Magnitude'])) mhat_v = mag / ion['B.F. Magnitude'] # Compute the magnetic coordinate velocities for each overlapping # latitude, longitude, and altitude. Also include the mag coord. for item in self.keys(): if (string.find(item, "V!") >= 0 or string.find(item, "Gravity") >= 0 or string.find(item, "PressGrad") >= 0): sp = string.split(item) units = self[item].attrs['units'] scale = self[item].attrs['scale'] # extract the non-directional part of the name if not sp[0] in self: east = string.join([sp[0], "(east)"], " ") north = string.join([sp[0], "(north)"], " ") up = string.join([sp[0], "(up)"], " ") par = string.join([sp[0], "(par)"], " ") zon = string.join([sp[0], "(zon)"], " ") mer = string.join([sp[0], "(mer)"], " ") name = self[east].attrs['name'] vp = bhat_e * self[east] + bhat_n * self[ north] + bhat_v * self[up] vz = zhat_e * self[east] + zhat_n * self[north] vm = mhat_e * self[east] + mhat_n * self[ north] + mhat_v * self[up] elif sp[0].find("Gravity") >= 0: par = string.join([sp[0], "(par)"], " ") zon = string.join([sp[0], "(zon)"], " ") mer = string.join([sp[0], "(mer)"], " ") name = "%s$_{east}$" % (self[item].attrs['name']) vp = bhat_v * self[item] vz = 0.0 * self[item] vm = mhat_v * self[item] self[par] = dmarray.copy(vp) self[par].attrs = { 'units': '%s{\mathdefault{,\,positive\,mag\,north}}' % (units), 'scale': scale, 'name': name.replace("east", "\parallel") } self[zon] = dmarray.copy(vz) self[zon].attrs = { 'units': '%s{\mathdefault{,\,positive\,east}}' % (units), 'scale': scale, 'name': name.replace("east", "zon") } self[mer] = dmarray.copy(vm) self[mer].attrs = { 'units': '%s{\mathdefault{,\,positive\,up}}' % (units), 'scale': scale, 'name': name.replace("east", "mer") } if not 'B.F. East' in self: self['B.F. East'] = dmarray.copy(ion['B.F. East']) self['B.F. North'] = dmarray.copy(ion['B.F. North']) self['B.F. Vertical'] = dmarray.copy(ion['B.F. Vertical']) self['B.F. Magnitude'] = dmarray.copy(ion['B.F. Magnitude']) if not 'Magnetic Latitude' in self: self['Magnetic Latitude'] = dmarray.copy( ion['Magnetic Latitude']) self['Magnetic Longitude'] = dmarray.copy( ion['Magnetic Longitude'])
def calc_magdi(self): ''' GITM 3DION and 3DMAG files contain the magnetic field in North-East-Vertical coordinates. This routine computes the magnetic inclination and declination. ''' import math mag = None # Test to determine the type of file we have if (self.attrs['file'].find("ION") > 0 or self.attrs['file'].find("MAG") > 0): mag = self else: if not 'magfile' in self.attrs: print("No 3D MAG/ION file associated with this GITM Binary") elif (self.attrs['magfile'].find("ION") <= 0 and self.attrs['magfile'].find("MAG") <= 0): print("No 3D MAG/ION file associated with this GITM Binary") else: mag = GitmBin(self.attrs['magfile']) if mag is not None: dec_frac = mag['B.F. East'] / mag['B.F. North'] inc_sign = -1.0 * mag['B.F. Vertical'] / abs(mag['B.F. Vertical']) inc_pmag = mag['B.F. North']**2 + mag['B.F. East']**2 for ilon in range(self.attrs['nLon']): for ilat in range(self.attrs['nLat']): for ialt, df in enumerate(dec_frac[ilon, ilat]): dec_frac[ilon, ilat, ialt] = math.atan(df) * 180.0 / np.pi i = (inc_sign[ilon, ilat, ialt] * math.sqrt(inc_pmag[ilon, ilat, ialt]) / mag['B.F. Magnitude'][ilon, ilat, ialt]) inc_pmag[ilon, ilat, ialt] = math.acos(i) * 180.0 / np.pi if inc_pmag[ilon, ilat, ialt] > 90.0: inc_pmag[ilon, ilat, ialt] -= 180.0 self['Declination'] = dmarray.copy(dec_frac) self['Declination'].attrs = { "name": "Declination", "scale": "linear", "units": "degrees" } self['Inclination'] = dmarray.copy(inc_pmag) self['Inclination'].attrs = { "name": "Inclination", "scale": "linear", "units": "degrees" } del dec_frac, inc_sign, inc_pmag if not 'B.F. East' in self: self['B.F. East'] = dmarray.copy(mag['B.F. East']) self['B.F. North'] = dmarray.copy(mag['B.F. North']) self['B.F. Vertical'] = dmarray.copy(mag['B.F. Vertical']) self['B.F. Magnitude'] = dmarray.copy(mag['B.F. Magnitude']) if not 'Magnetic Latitude' in self: self['Magnetic Latitude'] = dmarray.copy( mag['Magnetic Latitude']) self['Magnetic Longitude'] = dmarray.copy( mag['Magnetic Longitude'])
def calc_2dion(self): ''' A routine to calculate the 2D ionospheric parameters: VTEC, hmF2, NmF2. To perform these calculations, electron density ("e-") must be one of the available data types. ''' import scipy.integrate as integ from scipy.interpolate import interp1d from scipy.signal import argrelextrema calc_tec = False if 'e-' in self: if 'VTEC' in self is False: calc_tec = True self['VTEC'] = dmarray(self['e-'] * 1.0e-16, attrs={'units':'TECU', 'scale':'linear', 'name':'Vertical TEC'}) self['NmF2'] = dmarray.copy(self['e-']) self['NmF2'].attrs['name'] = 'N$_m$F$_2$' self['hmF2'] = dmarray(self['Altitude'] / 1000.0, attrs={'units':'km', 'scale':'linear', 'name':'h$_m$F$_2$'}) alt = np.linspace(min(self['hmF2'][0,0,2:-2]), max(self['hmF2'][0,0,2:-2]),1000) for ilon in range(self.attrs['nLon']): for ilat in range(self.attrs['nLat']): if calc_tec is True: # Integrate electron density over altitude vtec = integ.simps(self['VTEC'][ilon,ilat,2:-2], self['Altitude'][ilon,ilat,2:-2], "avg") self['VTEC'][ilon,ilat,:] = vtec # Interpolate over the electron density altitude profile eprof = interp1d(self['hmF2'][ilon,ilat,2:-2], self['NmF2'][ilon,ilat,2:-2], kind="cubic") try: edens = eprof(alt) except: alt = np.linspace(min(self['hmF2'][ilon,ilat,2:-2]), max(self['hmF2'][ilon,ilat,2:-2]), 1000) edens = eprof(alt) # Find the local maxima of the electron density profile emax = argrelextrema(edens, np.greater) emax_list = list(emax[0]) saddle = False if len(emax_list) == 0: # No local maxima were found, try identifying # saddle points saddle = True elif len(emax_list) == 1: if max(edens) == edens[emax_list[0]]: # Only one maxima exists and is realistic. Sometimes # a profile will have inflection points instead of # local maxima and this can confuse the routine self['NmF2'][ilon,ilat,:] = edens[emax_list[0]] self['hmF2'][ilon,ilat,:] = alt[emax_list[0]] else: saddle = True elif alt[emax_list[-1]] < 120.0: saddle = True else: # More than one maxima exists. Seperate hmF2 from hmF1 # and spurious local maxima NmF2 = list(edens[emax_list]) HmF2 = list(alt[emax_list]) # If the global maximum is over 200 km, # this is the F2 peak eindex = NmF2.index(max(NmF2)) if HmF2[eindex] <= 200.0 and HmF2[eindex] == min(HmF2): # The global max may be the F1 peak, see if the # secondary (or lessor) maxima is at the upper # limit of the model. If so, remove this point # from consideration if max(HmF2) > self['hmF2'][ilon,ilat,-5]: eindex = HmF2.index(max(HmF2)) emax_list.pop(eindex) NmF2.pop(eindex) HmF2.pop(eindex) eindex = NmF2.index(max(NmF2)) if len(emax_list) > 1: # If there are multiple maxima after the upper # boundary has been removed, choose the largest # maxima above 200 km since the hmF1 is often # larger than the hmF2 emax_list.pop(eindex) NmF2.pop(eindex) eindex = NmF2.index(max(NmF2)) # Set the hmF2 and NmF2 self['NmF2'][ilon,ilat,:] = edens[emax_list[eindex]] self['hmF2'][ilon,ilat,:] = alt[emax_list[eindex]] if saddle: # It is difficult to find saddle points. Examine # the rate of change of density delta_alt = alt[1] - alt[0] # Equally spaced edens_dot = np.diff(edens) / delta_alt # Identify inflection points by looking for # minima in the derivative of electron density. edot_min = argrelextrema(edens_dot, np.less) emin_list = list(edot_min[0]) edens_min = list(edens[emin_list]) emax = np.max(edens_min) eindex = emin_list[edens_min.index(emax)] # Assign the inflection with the largest # electron density to the ion peak self['NmF2'][ilon,ilat,:] = edens[eindex] self['hmF2'][ilon,ilat,:] = alt[eindex]
def calc_magvel(self): ''' Gitm 3DIon files contain the magnetic coordinates that allow the field-aligned and field-perpendicular velocities to be computed. The 3DIon file must be from the same run as the 3DAll file so that the geographic grid is the same. If a 3D Ion file was not produced in the original run, don't fret! You can get one by using the same UAM.in file. Unless the magnetic field is varying secularly, you can get away with producing just one 3DIon file. It is better to have a matching 3D Ion file for every 3D All file, however. ''' import math import string import sys ion = None # If this is a 3D ION file we don't need a mag file if self.attrs['file'].find("ION") > 0: ion = self else: if not 'magfile' in self.attrs: print("No 3D MAG/ION file associated with this GITM Binary") elif(self.attrs['magfile'].find("ION") <= 0 and self.attrs['magfile'].find("MAG") <= 0): print("No 3D MAG/ION file associated with this GITM Binary") else: ion = GitmBin(self.attrs['magfile']) # Compute the field-aligned unit vector in East, North, # and Vertical coordinates if ion: bhat_e = ion['B.F. East'] / ion['B.F. Magnitude'] bhat_n = ion['B.F. North'] / ion['B.F. Magnitude'] bhat_v = ion['B.F. Vertical'] / ion['B.F. Magnitude'] # Compute the zonal unit vector in East, North, Vertical coord mag = np.sqrt(np.square(ion['B.F. East']) + np.square(ion['B.F. North'])) zhat_e = ion['B.F. North'] / mag zhat_n = ion['B.F. East'] / mag # zhat_v is identically zero # Compute the meridional unit vector in East, North, Vertical coord mhat_e = (-ion['B.F. East']*ion['B.F. Vertical'] / (mag * ion['B.F. Magnitude'])) mhat_n = (-ion['B.F. North']*ion['B.F. Vertical'] / (mag * ion['B.F. Magnitude'])) mhat_v = mag / ion['B.F. Magnitude'] # Compute the magnetic coordinate velocities for each overlapping # latitude, longitude, and altitude. Also include the mag coord. for item in self.keys(): if(string.find(item, "V!") >= 0 or string.find(item, "Gravity") >= 0 or string.find(item, "PressGrad") >= 0): sp = string.split(item) units = self[item].attrs['units'] scale = self[item].attrs['scale'] # extract the non-directional part of the name if not sp[0] in self: east = string.join([sp[0], "(east)"], " ") north = string.join([sp[0], "(north)"], " ") up = string.join([sp[0], "(up)"], " ") par = string.join([sp[0], "(par)"], " ") zon = string.join([sp[0], "(zon)"], " ") mer = string.join([sp[0], "(mer)"], " ") name = self[east].attrs['name'] vp=bhat_e*self[east]+bhat_n*self[north]+bhat_v*self[up] vz=zhat_e*self[east]+zhat_n*self[north] vm=mhat_e*self[east]+mhat_n*self[north]+mhat_v*self[up] elif sp[0].find("Gravity") >= 0: par = string.join([sp[0], "(par)"], " ") zon = string.join([sp[0], "(zon)"], " ") mer = string.join([sp[0], "(mer)"], " ") name = "%s$_{east}$" % (self[item].attrs['name']) vp=bhat_v*self[item] vz=0.0*self[item] vm=mhat_v*self[item] self[par] = dmarray.copy(vp) self[par].attrs = {'units':'%s{\mathdefault{,\,positive\,mag\,north}}'%(units), 'scale':scale, 'name':name.replace("east", "\parallel")} self[zon] = dmarray.copy(vz) self[zon].attrs = {'units':'%s{\mathdefault{,\,positive\,east}}'%(units), 'scale':scale, 'name':name.replace("east", "zon")} self[mer] = dmarray.copy(vm) self[mer].attrs = {'units':'%s{\mathdefault{,\,positive\,up}}'%(units), 'scale':scale, 'name':name.replace("east", "mer")} if not 'B.F. East' in self: self['B.F. East'] = dmarray.copy(ion['B.F. East']) self['B.F. North'] = dmarray.copy(ion['B.F. North']) self['B.F. Vertical'] = dmarray.copy(ion['B.F. Vertical']) self['B.F. Magnitude'] = dmarray.copy(ion['B.F. Magnitude']) if not 'Magnetic Latitude' in self: self['Magnetic Latitude'] = dmarray.copy(ion['Magnetic Latitude']) self['Magnetic Longitude'] = dmarray.copy(ion['Magnetic Longitude'])