##// END OF EJS Templates
minor adjustments 1
minor adjustments 1

File last commit:

r1738:e8767bd42c24
r1739:e8800a48ec81
Show More
BField.py
507 lines | 18.0 KiB | text/x-python | PythonLexer
"""
Converted to Object-oriented Programming by Freddy Galindo, ROJ, 07 October 2009.
Added to signal Chain by Joab Apaza, ROJ, Jun 2023.
"""
import numpy
import time
import os
from scipy.special import lpmn
from schainpy.model.utils import Astro_Coords
class BField():
def __init__(self,year=None,doy=None,site=1,heights=None,alpha_i=90):
"""
BField class creates an object to get the Magnetic field for a specific date and
height(s).
Parameters
----------
year = A scalar giving the desired year. If the value is None (default value) then
the current year will be used.
doy = A scalar giving the desired day of the year. If the value is None (default va-
lue) then the current doy will be used.
site = An integer to choose the geographic coordinates of the place where the magne-
tic field will be computed. The default value is over Jicamarca (site=1)
heights = An array giving the heights (km) where the magnetic field will be modeled By default the magnetic field will be computed at 100, 500 and 1000km.
alpha_i = Angle to interpolate the magnetic field.
Modification History
--------------------
Converted to Object-oriented Programming by Freddy Galindo, ROJ, 07 October 2009.
Added to signal Chain by Joab Apaza, ROJ, Jun 2023.
"""
tmp = time.localtime()
if year==None: year = tmp[0]
if doy==None: doy = tmp[7]
self.year = year
self.doy = doy
self.site = site
if heights is None:
heights = numpy.array([100,500,1000])
else:
heights = numpy.array(heights)
self.heights = heights
self.alpha_i = alpha_i
def getBField(self,maglimits=numpy.array([-37,-37,37,37])):
"""
getBField models the magnetic field for a different heights in a specific date.
Parameters
----------
maglimits = An 4-elements array giving ..... The default value is [-7,-7,7,7].
Return
------
dcos = An 4-dimensional array giving the directional cosines of the magnetic field
over the desired place.
alpha = An 3-dimensional array giving the angle of the magnetic field over the desi-
red place.
Modification History
--------------------
Converted to Python by Freddy R. Galindo, ROJ, 07 October 2009.
"""
x_ant = numpy.array([1,0,0])
y_ant = numpy.array([0,1,0])
z_ant = numpy.array([0,0,1])
if self.site==0:
title_site = "Magnetic equator"
coord_site = numpy.array([-76+52./60.,-11+57/60.,0.5])
elif self.site==1:
title_site = 'Jicamarca'
coord_site = [-76-52./60.,-11-57/60.,0.5]
heta = (45+5.35)*numpy.pi/180. # (50.35 and 1.46 from Fleish Thesis)
delta = -1.46*numpy.pi/180
x_ant1 = numpy.roll(self.rotvector(self.rotvector(x_ant,1,delta),3,theta),1)
y_ant1 = numpy.roll(self.rotvector(self.rotvector(y_ant,1,delta),3,theta),1)
z_ant1 = numpy.roll(self.rotvector(self.rotvector(z_ant,1,delta),3,theta),1)
ang0 = -1*coord_site[0]*numpy.pi/180.
ang1 = coord_site[1]*numpy.pi/180.
x_ant = self.rotvector(self.rotvector(x_ant1,2,ang1),3,ang0)
y_ant = self.rotvector(self.rotvector(y_ant1,2,ang1),3,ang0)
z_ant = self.rotvector(self.rotvector(z_ant1,2,ang1),3,ang0)
elif self.site==2: #AMISR
title_site = 'AMISR 14'
coord_site = [-76.874913, -11.953371, 0.52984]
theta = (0.0977)*numpy.pi/180. # 0.0977
delta = 0.110*numpy.pi/180 # 0.11
x_ant1 = numpy.roll(self.rotvector(self.rotvector(x_ant,1,delta),3,theta),1)
y_ant1 = numpy.roll(self.rotvector(self.rotvector(y_ant,1,delta),3,theta),1)
z_ant1 = numpy.roll(self.rotvector(self.rotvector(z_ant,1,delta),3,theta),1)
ang0 = -1*coord_site[0]*numpy.pi/180.
ang1 = coord_site[1]*numpy.pi/180.
x_ant = self.rotvector(self.rotvector(x_ant1,2,ang1),3,ang0)
y_ant = self.rotvector(self.rotvector(y_ant1,2,ang1),3,ang0)
z_ant = self.rotvector(self.rotvector(z_ant1,2,ang1),3,ang0)
else:
# print "No defined Site. Skip..."
return None
nhei = self.heights.size
pt_intercep = numpy.zeros((nhei,2))
nfields = 1
grid_res = 2.5
nlon = int(int(maglimits[2] - maglimits[0])/grid_res + 1)
nlat = int(int(maglimits[3] - maglimits[1])/grid_res + 1)
location = numpy.zeros((nlon,nlat,2))
mlon = numpy.atleast_2d(numpy.arange(nlon)*grid_res + maglimits[0])
mrep = numpy.atleast_2d(numpy.zeros(nlat) + 1)
location0 = numpy.dot(mlon.transpose(),mrep)
mlat = numpy.atleast_2d(numpy.arange(nlat)*grid_res + maglimits[1])
mrep = numpy.atleast_2d(numpy.zeros(nlon) + 1)
location1 = numpy.dot(mrep.transpose(),mlat)
location[:,:,0] = location0
location[:,:,1] = location1
alpha = numpy.zeros((nlon,nlat,nhei))
rr = numpy.zeros((nlon,nlat,nhei,3))
dcos = numpy.zeros((nlon,nlat,nhei,2))
global first_time
first_time = None
for ilon in numpy.arange(nlon):
for ilat in numpy.arange(nlat):
outs = self.__bdotk(self.heights,
self.year + self.doy/366.,
coord_site[1],
coord_site[0],
coord_site[2],
coord_site[1]+location[ilon,ilat,1],
location[ilon,ilat,0]*720./180.)
alpha[ilon, ilat,:] = outs[1]
rr[ilon, ilat,:,:] = outs[3]
mrep = numpy.atleast_2d((numpy.zeros(nhei)+1)).transpose()
tmp = outs[3]*numpy.dot(mrep,numpy.atleast_2d(x_ant))
tmp = tmp.sum(axis=1)
dcos[ilon,ilat,:,0] = tmp/numpy.sqrt((outs[3]**2).sum(axis=1))
mrep = numpy.atleast_2d((numpy.zeros(nhei)+1)).transpose()
tmp = outs[3]*numpy.dot(mrep,numpy.atleast_2d(y_ant))
tmp = tmp.sum(axis=1)
dcos[ilon,ilat,:,1] = tmp/numpy.sqrt((outs[3]**2).sum(axis=1))
return dcos, alpha, nlon, nlat
def __bdotk(self,heights,tm,gdlat=-11.95,gdlon=-76.8667,gdalt=0.0,decd=-12.88, ham=-4.61666667):
global first_time
# Mean Earth radius in Km WGS 84
a_igrf = 6371.2
bk = numpy.zeros(heights.size)
alpha = numpy.zeros(heights.size)
bfm = numpy.zeros(heights.size)
rr = numpy.zeros((heights.size,3))
rgc = numpy.zeros((heights.size,3))
ObjGeodetic = Astro_Coords.Geodetic(gdlat,gdalt)
[gclat,gcalt] = ObjGeodetic.change2geocentric()
gclat = gclat*numpy.pi/180.
gclon = gdlon*numpy.pi/180.
# Antenna position from center of Earth
ca_vector = [numpy.cos(gclat)*numpy.cos(gclon),numpy.cos(gclat)*numpy.sin(gclon),numpy.sin(gclat)]
ca_vector = gcalt*numpy.array(ca_vector)
dec = decd*numpy.pi/180.
# K vector respect to the center of earth.
klon = gclon + ham*numpy.pi/720.
k_vector = [numpy.cos(dec)*numpy.cos(klon),numpy.cos(dec)*numpy.sin(klon),numpy.sin(dec)]
k_vector = numpy.array(k_vector)
for ih in numpy.arange(heights.size):
# Vector from Earth's center to volume of interest
rr[ih,:] = k_vector*heights[ih]
cv_vector = numpy.squeeze(ca_vector) + rr[ih,:]
cv_gcalt = numpy.sqrt(numpy.sum(cv_vector**2.))
cvxy = numpy.sqrt(numpy.sum(cv_vector[0:2]**2.))
radial = cv_vector/cv_gcalt
east = numpy.array([-1*cv_vector[1],cv_vector[0],0])/cvxy
comp1 = east[1]*radial[2] - radial[1]*east[2]
comp2 = east[2]*radial[0] - radial[2]*east[0]
comp3 = east[0]*radial[1] - radial[0]*east[1]
north = -1*numpy.array([comp1, comp2, comp3])
rr_k = cv_vector - numpy.squeeze(ca_vector)
u_rr = rr_k/numpy.sqrt(numpy.sum(rr_k**2.))
cv_gclat = numpy.arctan2(cv_vector[2],cvxy)
cv_gclon = numpy.arctan2(cv_vector[1],cv_vector[0])
bhei = cv_gcalt-a_igrf
blat = cv_gclat*180./numpy.pi
blon = cv_gclon*180./numpy.pi
bfield = self.__igrfkudeki(bhei,tm,blat,blon)
B = (bfield[0]*north + bfield[1]*east - bfield[2]*radial)*1.0e-5
bfm[ih] = numpy.sqrt(numpy.sum(B**2.)) #module
bk[ih] = numpy.sum(u_rr*B)
alpha[ih] = numpy.arccos(bk[ih]/bfm[ih])*180/numpy.pi
rgc[ih,:] = numpy.array([cv_gclon, cv_gclat, cv_gcalt])
return bk, alpha, bfm, rr, rgc
def __igrfkudeki(self,heights,time,latitude,longitude,ae=6371.2):
"""
__igrfkudeki calculates the International Geomagnetic Reference Field for given in-
put conditions based on IGRF2005 coefficients.
Parameters
----------
heights = Scalar or vector giving the height above the Earth of the point in ques-
tion in kilometers.
time = Scalar or vector giving the decimal year of time in question (e.g. 1991.2).
latitude = Latitude of point in question in decimal degrees. Scalar or vector.
longitude = Longitude of point in question in decimal degrees. Scalar or vector.
ae =
first_time =
Return
------
bn =
be =
bd =
bmod =
balpha =
first_time =
Modification History
--------------------
Converted to Python by Freddy R. Galindo, ROJ, 03 October 2009.
"""
global first_time
global gs, hs, nvec, mvec, maxcoef
heights = numpy.atleast_1d(heights)
time = numpy.atleast_1d(time)
latitude = numpy.atleast_1d(latitude)
longitude = numpy.atleast_1d(longitude)
if numpy.max(latitude)==90:
# print "Field calculations are not supported at geographic poles"
pass
# output arrays
bn = numpy.zeros(heights.size)
be = numpy.zeros(heights.size)
bd = numpy.zeros(heights.size)
if first_time==None:first_time=0
time0 = time[0]
if time!=first_time:
#print "Getting coefficients for", time0
[periods,g,h ] = self.__readIGRFcoeff()
top_year = numpy.max(periods)
nperiod = (top_year - 1900)/5 + 1
maxcoef = 10
if time0>=2000:maxcoef = 12
# Normalization array for Schmidt fucntions
multer = numpy.zeros((2+maxcoef,1+maxcoef)) + 1
for cn in (numpy.arange(maxcoef)+1):
for rm in (numpy.arange(cn)+1):
tmp = numpy.arange(2*rm) + cn - rm + 1.
multer[rm+1,cn] = ((-1.)**rm)*numpy.sqrt(2./tmp.prod())
schmidt = multer[1:,1:].transpose()
# n and m arrays
nvec = numpy.atleast_2d(numpy.arange(maxcoef)+2)
mvec = numpy.atleast_2d(numpy.arange(maxcoef+1)).transpose()
# Time adjusted igrf g and h with Schmidt normalization
# IGRF coefficient arrays: g0(n,m), n=1, maxcoeff,m=0, maxcoeff, ...
if time0<top_year:
dtime = (time0 - 1900) % 5
ntime = int((time0 - 1900 - dtime)/5)
else:
# Estimating coefficients for times > top_year
dtime = (time0 - top_year) + 5
ntime = int(g[:,0,0].size - 2)
g0 = g[ntime,1:maxcoef+1,:maxcoef+1]
h0 = h[ntime,1:maxcoef+1,:maxcoef+1]
gdot = g[ntime+1,1:maxcoef+1,:maxcoef+1]-g[ntime,1:maxcoef+1,:maxcoef+1]
hdot = h[ntime+1,1:maxcoef+1,:maxcoef+1]-h[ntime,1:maxcoef+1,:maxcoef+1]
gs = (g0 + dtime*(gdot/5.))*schmidt[:maxcoef,0:maxcoef+1]
hs = (h0 + dtime*(hdot/5.))*schmidt[:maxcoef,0:maxcoef+1]
first_time = time0
for ii in numpy.arange(heights.size):
# Height dependence array rad = (ae/(ae+height))**(n+3)
rad = numpy.atleast_2d((ae/(ae + heights[ii]))**(nvec+1))
# Sin and Cos of m times longitude phi arrays
mphi = mvec*longitude[ii]*numpy.pi/180.
cosmphi = numpy.atleast_2d(numpy.cos(mphi))
sinmphi = numpy.atleast_2d(numpy.sin(mphi))
# Cos of colatitude theta
c = numpy.cos((90 - latitude[ii])*numpy.pi/180.)
# Legendre functions p(n,m|c)
[p,dp]= lpmn(maxcoef+1,maxcoef+1,c)
p = p[:,:-1].transpose()
s = numpy.sqrt((1. - c)*(1 + c))
# Generate derivative array dpdtheta = -s*dpdc
dpdtheta = c*p/s
for m in numpy.arange(maxcoef+2): dpdtheta[:,m] = m*dpdtheta[:,m]
dpdtheta = dpdtheta + numpy.roll(p,-1,axis=1)
# Extracting arrays required for field calculations
p = p[1:maxcoef+1,:maxcoef+1]
dpdtheta = dpdtheta[1:maxcoef+1,:maxcoef+1]
# Weigh p and dpdtheta with gs and hs coefficients.
gp = gs*p
hp = hs*p
gdpdtheta = gs*dpdtheta
hdpdtheta = hs*dpdtheta
# Calcultate field components
matrix0 = numpy.dot(gdpdtheta,cosmphi)
matrix1 = numpy.dot(hdpdtheta,sinmphi)
bn[ii] = numpy.dot(rad,(matrix0 + matrix1))
matrix0 = numpy.dot(hp,(mvec*cosmphi))
matrix1 = numpy.dot(gp,(mvec*sinmphi))
be[ii] = numpy.dot((-1*rad),((matrix0 - matrix1)/s))
matrix0 = numpy.dot(gp,cosmphi)
matrix1 = numpy.dot(hp,sinmphi)
bd[ii] = numpy.dot((-1*nvec*rad),(matrix0 + matrix1))
bmod = numpy.sqrt(bn**2. + be**2. + bd**2.)
btheta = numpy.arctan(bd/numpy.sqrt(be**2. + bn**2.))*180/numpy.pi
balpha = numpy.arctan(be/bn)*180./numpy.pi
#bn : north
#be : east
#bn : radial
#bmod : module
return bn, be, bd, bmod, btheta, balpha
def str2num(self, datum):
try:
return int(datum)
except:
try:
return float(datum)
except:
return datum
def __readIGRFfile(self, filename):
list_years=[]
for i in range(1,26):
list_years.append(1895.0 + i*5)
epochs=list_years
epochs.append(epochs[-1]+5)
nepochs = numpy.shape(epochs)
gg = numpy.zeros((13,14,nepochs[0]),dtype=float)
hh = numpy.zeros((13,14,nepochs[0]),dtype=float)
coeffs_file=open(filename)
lines=coeffs_file.readlines()
coeffs_file.close()
for line in lines:
items = line.split()
g_h = items[0]
n = self.str2num(items[1])
m = self.str2num(items[2])
coeffs = items[3:]
for i in range(len(coeffs)-1):
coeffs[i] = self.str2num(coeffs[i])
#coeffs = numpy.array(coeffs)
ncoeffs = numpy.shape(coeffs)[0]
if g_h == 'g':
# print n," g ",m
gg[n-1,m,:]=coeffs
elif g_h=='h':
# print n," h ",m
hh[n-1,m,:]=coeffs
# else :
# continue
# Ultimo Reordenamiento para almacenar .
gg[:,:,nepochs[0]-1] = gg[:,:,nepochs[0]-2] + 5*gg[:,:,nepochs[0]-1]
hh[:,:,nepochs[0]-1] = hh[:,:,nepochs[0]-2] + 5*hh[:,:,nepochs[0]-1]
# return numpy.array([gg,hh])
periods = numpy.array(epochs)
g = gg
h = hh
return periods, g, h
def __readIGRFcoeff(self,filename="igrf10coeffs.dat"):
"""
__readIGRFcoeff reads the coefficients from a binary file which is located in the
folder "resource."
Parameter
---------
filename = A string to specify the name of the file which contains thec coeffs. The
default value is "igrf10coeffs.dat"
Return
------
periods = A lineal array giving...
g1 =
h1 =
Modification History
--------------------
Converted to Python by Freddy R. Galindo, ROJ, 03 October 2009.
"""
base_path = os.path.dirname(os.path.abspath(__file__))
filename = os.path.join(base_path, "igrf13coeffs.txt")
period_v, g_v, h_v = self.__readIGRFfile(filename)
g2 = numpy.zeros((14,14,26))
h2 = numpy.zeros((14,14,26))
g2[1:14,:,:] = g_v
h2[1:14,:,:] = h_v
g = numpy.transpose(g2, (2,0,1))
h = numpy.transpose(h2, (2,0,1))
periods = period_v.copy()
return periods, g, h
def rotvector(self,vector,axis=1,ang=0):
"""
rotvector function returns the new vector generated rotating the rectagular coords.
Parameters
----------
vector = A lineal 3-elements array (x,y,z).
axis = A integer to specify the axis used to rotate the coord systems. The default
value is 1.
axis = 1 -> Around "x"
axis = 2 -> Around "y"
axis = 3 -> Around "z"
ang = Angle of rotation (in radians). The default value is zero.
Return
------
rotvector = A lineal array of 3 elements giving the new coordinates.
Modification History
--------------------
Converted to Python by Freddy R. Galindo, ROJ, 01 October 2009.
"""
if axis==1:
t = [[1,0,0],[0,numpy.cos(ang),numpy.sin(ang)],[0,-numpy.sin(ang),numpy.cos(ang)]]
elif axis==2:
t = [[numpy.cos(ang),0,-numpy.sin(ang)],[0,1,0],[numpy.sin(ang),0,numpy.cos(ang)]]
elif axis==3:
t = [[numpy.cos(ang),numpy.sin(ang),0],[-numpy.sin(ang),numpy.cos(ang),0],[0,0,1]]
rotvector = numpy.array(numpy.dot(numpy.array(t),numpy.array(vector)))
return rotvector