geopack-vectorize 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
geopack/models/t04.py ADDED
@@ -0,0 +1,2215 @@
1
+ import numpy as np
2
+
3
+ # t04 is identical to t01 except for several factors.
4
+
5
+ def t04(parmod,ps,x,y,z):
6
+ """
7
+ A data-based model of the external (i.e., without earth's contribution) part of the
8
+ magnetospheric magnetic field, calibrated by
9
+ (1) solar wind pressure pdyn (nanopascals),
10
+ (2) dst (nanotesla),
11
+ (3) byimf,
12
+ (4) bzimf (nanotesla)
13
+ (5-10) indices w1 - w6, calculated as time integrals from the beginning of a storm
14
+ see the reference (3) below, for a detailed definition of those variables
15
+
16
+ :param parmod: The elements are explained above.
17
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
18
+ :return: bx,by,bz. Field components in GSM system, in nT.
19
+ Computed as a sum of contributions from principal field sources.
20
+
21
+ Assembled: March 25, 2004; Updated: August 2 & 31, December 27, 2004.
22
+ A bug eliminated March 14, 2005 (might cause compilation problems with some fortran compilers)
23
+
24
+ Attention: The model is based on data taken sunward from x=-15Re, and hence becomes invalid at larger tailward distances !!! *
25
+
26
+ REFERENCES:
27
+ (1) N. A. Tsyganenko, A new data-based model of the near magnetosphere magnetic field:
28
+ 1. Mathematical structure.
29
+ 2. Parameterization and fitting to observations. JGR v. 107(A8), 1176/1179, doi:10.1029/2001JA000219/220, 2002.
30
+ (2) N. A. Tsyganenko, H. J. Singer, J. C. Kasper, Storm-time distortion of the
31
+ inner magnetosphere: How severe can it get ? JGR v. 108(A5), 1209, doi:10.1029/2002JA009808, 2003.
32
+ (3) N. A. Tsyganenko and M. I. Sitnov, Modeling the dynamics of the inner magnetosphere during
33
+ strong geomagnetic storms, J. Geophys. Res., v. 110 (A3), A03208, doi: 10.1029/2004JA010798, 2005.
34
+ """
35
+
36
+ a = np.array([
37
+ 1.00000,5.44118,0.891995,9.09684,0.00000,-7.18972,12.2700,
38
+ -4.89408,0.00000,0.870536,1.36081,0.00000,0.688650,0.602330,
39
+ 0.00000,0.316346,1.22728,-0.363620E-01,-0.405821,0.452536,
40
+ 0.755831,0.215662,0.152759,5.96235,23.2036,11.2994,69.9596,
41
+ 0.989596,-0.132131E-01,0.985681,0.344212E-01,1.02389,0.207867,
42
+ 1.51220,0.682715E-01,1.84714,1.76977,1.37690,0.696350,0.343280,
43
+ 3.28846,111.293,5.82287,4.39664,0.383403,0.648176,0.318752E-01,
44
+ 0.581168,1.15070,0.843004,0.394732,0.846509,0.916555,0.550920,
45
+ 0.180725,0.898772,0.387365,2.26596,1.29123,0.436819,1.28211,
46
+ 1.33199,.405553,1.6229,.699074,1.26131,2.42297,.537116,.619441])
47
+
48
+
49
+ iopgen,ioptt,iopb,iopr = [0.]*4
50
+
51
+ pdyn=parmod[0]
52
+ dst_ast=parmod[1]*0.8-13*np.sqrt(pdyn)
53
+ bximf,byimf,bzimf=[0.,parmod[2],parmod[3]]
54
+ w1,w2,w3,w4,w5,w6 = parmod[4:10]
55
+ pss,xx,yy,zz = [ps,x,y,z]
56
+
57
+ return extern(iopgen,ioptt,iopb,iopr,a,69,pdyn,dst_ast,bximf,byimf,bzimf,
58
+ w1,w2,w3,w4,w5,w6,pss,xx,yy,zz)
59
+
60
+
61
+ def extern(iopgen,iopt,iopb,iopr,a,ntot,pdyn,dst,bximf,byimf,bzimf,w1,w2,w3,w4,w5,w6,ps,x,y,z):
62
+ """
63
+ :param iopgen: general option flag:
64
+ iopgen=0 - calculate total field
65
+ iopgen=1 - dipole shielding only
66
+ iopgen=2 - tail field only
67
+ iopgen=3 - birkeland field only
68
+ iopgen=4 - ring current field only
69
+ iopgen=5 - interconnection field only
70
+ :param iopt: tail field flag:
71
+ iopt=0 - both modes
72
+ iopt=1 - mode 1 only
73
+ iopt=2 - mode 2 only
74
+ :param iopb: birkeland field flag:
75
+ iopb=0 - all 4 terms
76
+ iopb=1 - region 1, modes 1 and 2
77
+ iopb=2 - region 2, modes 1 and 2
78
+ :param iopr: ring current flag:
79
+ iopr=0 - both src and prc
80
+ iopr=1 - src only
81
+ iopr=2 - prc only
82
+ """
83
+
84
+ # common /tail/ dxshift1,dxshift2,d,deltady ! the common blocks forward nonlinear parameters
85
+ # common /birkpar/ xkappa1,xkappa2
86
+ # common /rcpar/ sc_sy,sc_as,phi
87
+ # common /g/ g
88
+ # common /rh0/ rh0
89
+ global dxshift1, dxshift2, d, deltady
90
+ global xkappa1, xkappa2
91
+ global sc_sy, sc_pr, phi
92
+ global g
93
+ global rh0
94
+
95
+ a0_a,a0_s0,a0_x0 = [34.586,1.1960,3.4397] # Shue et al. parameters
96
+ dsig = 0.005
97
+ rh0,rh2 = [8.0,-5.2]
98
+
99
+ xappa = (pdyn/2.)**a[22] # overall scaling parameter
100
+ rh0 = 7.5 # tail hinging distance
101
+ g = 35.0 # tail warping parameter
102
+
103
+ xappa3=xappa**3
104
+
105
+ xx=x*xappa
106
+ yy=y*xappa
107
+ zz=z*xappa
108
+
109
+ sps=np.sin(ps)
110
+
111
+ x0=a0_x0/xappa
112
+ am=a0_a/xappa
113
+ s0=a0_s0
114
+
115
+ # Calculate "imf" components outside the magnetopause layer (hence begin with "o")
116
+ # They are needed only if the point (x,y,z) is within the transition magnetopause layer or outside the magnetosphere:
117
+ factimf=a[19]
118
+
119
+ oimfx=0.
120
+ oimfy=byimf*factimf
121
+ oimfz=bzimf*factimf
122
+
123
+ r=np.sqrt(x**2+y**2+z**2)
124
+ xss=x
125
+ zss=z
126
+
127
+ # begin iterative search of unwarped coords (to find sigma)
128
+ dd = 1.
129
+ while dd > 1e-6:
130
+ xsold=xss
131
+ zsold=zss
132
+
133
+ rh=rh0+rh2*(zss/r)**2
134
+ sinpsas=sps/(1+(r/rh)**3)**0.33333333
135
+ cospsas=np.sqrt(1-sinpsas**2)
136
+ zss=x*sinpsas+z*cospsas
137
+ xss=x*cospsas-z*sinpsas
138
+ dd=np.abs(xss-xsold)+np.abs(zss-zsold)
139
+
140
+ rho2=y**2+zss**2
141
+ asq=am**2
142
+ xmxm=am+xss-x0
143
+ if xmxm < 0: xmxm = 0 # the boundary is a cylinder tailward of x=x0-am
144
+ axx0=xmxm**2
145
+ aro=asq+rho2
146
+ sigma=np.sqrt((aro+axx0+np.sqrt((aro+axx0)**2-4.*asq*axx0))/(2.*asq))
147
+
148
+ # Now, there are three possible cases:
149
+ # (1) inside the magnetosphere
150
+ # (2) in the boundary layer
151
+ # (3) outside the magnetosphere and b.layer
152
+
153
+ # First of all, consider the cases (1) and (2):
154
+ if sigma < (s0+dsig): # cases (1) or (2); calculate the model field (with the potential "penetrated" interconnection field):
155
+
156
+ bxcf,bycf,bzcf = [0.]*3
157
+ if iopgen <= 1:
158
+ cfx,cfy,cfz = shlcar3x3(xx,yy,zz,ps) # dipole shielding field
159
+ bxcf=cfx*xappa3
160
+ bycf=cfy*xappa3
161
+ bzcf=cfz*xappa3
162
+
163
+ bxt1,byt1,bzt1,bxt2,byt2,bzt2 = [0.]*6
164
+ if (iopgen == 0) | (iopgen == 2):
165
+ dstt = -20.
166
+ if dst < dstt: dstt = dst
167
+ znam = np.abs(dstt)**0.37
168
+ dxshift1=a[23]-a[24]/znam
169
+ dxshift2=a[25]-a[26]/znam
170
+ d=a[35]*np.exp(-w1/a[36])+a[68]
171
+ deltady=4.7
172
+ bxt1,byt1,bzt1,bxt2,byt2,bzt2 = deformed(iopt,ps,xx,yy,zz)
173
+
174
+ bxr11,byr11,bzr11, bxr12,byr12,bzr12, bxr21,byr21,bzr21, bxr22,byr22,bzr22 = [0.]*12
175
+ if (iopgen == 0) | (iopgen == 3):
176
+ znam = np.abs(dst)
177
+ if dst >= -20: znam = 20.
178
+ xkappa1=a[31]*(znam/20)**a[32]
179
+ xkappa2=a[33]*(znam/20)**a[34]
180
+ # Birkeland field (two modes for r1 and two modes for r2)
181
+ bxr11,byr11,bzr11, bxr12,byr12,bzr12, bxr21,byr21,bzr21, bxr22,byr22,bzr22 = \
182
+ birk_tot(iopb,ps,xx,yy,zz)
183
+
184
+ bxsrc,bysrc,bzsrc, bxprc,byprc,bzprc = [0.]*6
185
+ if (iopgen == 0) | (iopgen == 4):
186
+ phi=a[37]
187
+ znam=np.abs(dst)
188
+ if dst >= -20: znam = 20
189
+ sc_sy=a[27]*(20/znam)**a[28]*xappa
190
+ sc_pr=a[29]*(20/znam)**a[30]*xappa
191
+ # shielded ring current (src and prc)
192
+ bxsrc,bysrc,bzsrc, bxprc,byprc,bzprc = full_rc(iopr,ps,xx,yy,zz)
193
+
194
+ hximf,hyimf,hzimf = [0.]*3
195
+ if (iopgen == 0) | (iopgen == 5):
196
+ # These are components of the penetrated field per unit of the penetration coefficient.
197
+ # In other words, these are derivatives of the penetration field components with respect
198
+ # to the penetration coefficient. We assume that only the transverse component of the
199
+ # field penetrates inside.
200
+ hximf,hyimf,hzimf = [0.,byimf,bzimf]
201
+
202
+ # Now, add up all the components:
203
+ dlp1=(pdyn/2)**a[20]
204
+ dlp2=(pdyn/2)**a[21]
205
+
206
+ tamp1=a[1]+a[2]*dlp1+a[3]*a[38]*w1/np.sqrt(w1**2+a[38]**2)+a[4]*dst
207
+ tamp2=a[5]+a[6]*dlp2+a[7]*a[39]*w2/np.sqrt(w2**2+a[39]**2)+a[8]*dst
208
+ a_src=a[9] +a[10]*a[40]*w3/np.sqrt(w3**2+a[40]**2)+a[11]*dst
209
+ a_prc=a[12]+a[13]*a[41]*w4/np.sqrt(w4**2+a[41]**2)+a[14]*dst
210
+ a_r11=a[15]+a[16]*a[42]*w5/np.sqrt(w5**2+a[42]**2)
211
+ a_r21=a[17]+a[18]*a[43]*w6/np.sqrt(w6**2+a[43]**2)
212
+
213
+ bbx=a[0]*bxcf + tamp1*bxt1+tamp2*bxt2 + a_src*bxsrc+a_prc*bxprc + a_r11*bxr11+a_r21*bxr21 + a[19]*hximf
214
+ bby=a[0]*bycf + tamp1*byt1+tamp2*byt2 + a_src*bysrc+a_prc*byprc + a_r11*byr11+a_r21*byr21 + a[19]*hyimf
215
+ bbz=a[0]*bzcf + tamp1*bzt1+tamp2*bzt2 + a_src*bzsrc+a_prc*bzprc + a_r11*bzr11+a_r21*bzr21 + a[19]*hzimf
216
+
217
+ # And we have the total external field.
218
+ # Now, let us check whether we have the case (1). if yes - we are done:
219
+ if sigma < (s0-dsig): # (x,y,z) is inside the magnetosphere
220
+ bx,by,bz = [bbx,bby,bbz]
221
+ else: # this is the most complex case: we are inside the interpolation region
222
+ fint=0.5*(1.-(sigma-s0)/dsig)
223
+ fext=0.5*(1.+(sigma-s0)/dsig)
224
+
225
+ qx,qy,qz = dipole(ps,x,y,z)
226
+ bx=(bbx+qx)*fint+oimfx*fext -qx
227
+ by=(bby+qy)*fint+oimfy*fext -qy
228
+ bz=(bbz+qz)*fint+oimfz*fext -qz
229
+ # The cases (1) and (2) are exhausted; the only remaining possibility is now the case (3):
230
+ else:
231
+ qx,qy,qz = dipole(ps,x,y,z)
232
+ bx=oimfx-qx
233
+ by=oimfy-qy
234
+ bz=oimfz-qz
235
+
236
+ return bx,by,bz
237
+
238
+ def shlcar3x3(x,y,z, ps):
239
+ """
240
+ This subroutine returns the shielding field for the earth's dipole, represented by
241
+ 2x3x3=18 "cartesian" harmonics, tilted with respect to the z=0 plane (nb#4, p.74)
242
+
243
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
244
+ :param ps: geo-dipole tilt angle in radius.
245
+ :return: bx,by,bz. Field components in GSM system, in nT.
246
+ """
247
+ # The 36 coefficients enter in pairs in the amplitudes of the "cartesian" harmonics (A(1)-A(36).
248
+ # The 14 nonlinear parameters (A(37)-A(50) are the scales Pi,Ri,Qi,and Si entering the arguments of exponents, sines, and cosines in each of the
249
+ # 18 "cartesian" harmonics plus two tilt angles for the cartesian harmonics (one for the psi=0 mode and another for the psi=90 mode)
250
+
251
+ a = np.array([
252
+ -901.2327248,895.8011176,817.6208321,-845.5880889,-83.73539535,
253
+ 86.58542841,336.8781402,-329.3619944,-311.2947120,308.6011161,
254
+ 31.94469304,-31.30824526,125.8739681,-372.3384278,-235.4720434,
255
+ 286.7594095,21.86305585,-27.42344605,-150.4874688,2.669338538,
256
+ 1.395023949,-.5540427503,-56.85224007,3.681827033,-43.48705106,
257
+ 5.103131905,1.073551279,-.6673083508,12.21404266,4.177465543,
258
+ 5.799964188,-.3977802319,-1.044652977,.5703560010,3.536082962,
259
+ -3.222069852,9.620648151,6.082014949,27.75216226,12.44199571,
260
+ 5.122226936,6.982039615,20.12149582,6.150973118,4.663639687,
261
+ 15.73319647,2.303504968,5.840511214,.8385953499E-01,.3477844929])
262
+
263
+ p1,p2,p3, r1,r2,r3, q1,q2,q3, s1,s2,s3 = a[36:48]
264
+ t1,t2 = a[48:50]
265
+
266
+ cps=np.cos(ps)
267
+ sps=np.sin(ps)
268
+ s2ps=2*cps # modified here (sin(2*ps) instead of sin(3*ps))
269
+
270
+ st1=np.sin(ps*t1)
271
+ ct1=np.cos(ps*t1)
272
+ st2=np.sin(ps*t2)
273
+ ct2=np.cos(ps*t2)
274
+
275
+ x1=x*ct1-z*st1
276
+ z1=x*st1+z*ct1
277
+ x2=x*ct2-z*st2
278
+ z2=x*st2+z*ct2
279
+
280
+ # make the terms in the 1st sum ("perpendicular" symmetry):
281
+ # i=1:
282
+ sqpr= np.sqrt(1/p1**2+1/r1**2)
283
+ cyp = np.cos(y/p1)
284
+ syp = np.sin(y/p1)
285
+ czr = np.cos(z1/r1)
286
+ szr = np.sin(z1/r1)
287
+ expr= np.exp(sqpr*x1)
288
+ fx1 =-sqpr*expr*cyp*szr
289
+ hy1 = expr/p1*syp*szr
290
+ fz1 =-expr*cyp/r1*czr
291
+ hx1 = fx1*ct1+fz1*st1
292
+ hz1 =-fx1*st1+fz1*ct1
293
+
294
+ sqpr= np.sqrt(1/p1**2+1/r2**2)
295
+ cyp = np.cos(y/p1)
296
+ syp = np.sin(y/p1)
297
+ czr = np.cos(z1/r2)
298
+ szr = np.sin(z1/r2)
299
+ expr= np.exp(sqpr*x1)
300
+ fx2 =-sqpr*expr*cyp*szr
301
+ hy2 = expr/p1*syp*szr
302
+ fz2 =-expr*cyp/r2*czr
303
+ hx2 = fx2*ct1+fz2*st1
304
+ hz2 =-fx2*st1+fz2*ct1
305
+
306
+ sqpr= np.sqrt(1/p1**2+1/r3**2)
307
+ cyp = np.cos(y/p1)
308
+ syp = np.sin(y/p1)
309
+ czr = np.cos(z1/r3)
310
+ szr = np.sin(z1/r3)
311
+ expr= np.exp(sqpr*x1)
312
+ fx3 =-expr*cyp*(sqpr*z1*czr+szr/r3*(x1+1/sqpr))
313
+ hy3 = expr/p1*syp*(z1*czr+x1/r3*szr/sqpr)
314
+ fz3 =-expr*cyp*(czr*(1+x1/r3**2/sqpr)-z1/r3*szr)
315
+ hx3 = fx3*ct1+fz3*st1
316
+ hz3 =-fx3*st1+fz3*ct1
317
+
318
+ # i=2:
319
+ sqpr= np.sqrt(1/p2**2+1/r1**2)
320
+ cyp = np.cos(y/p2)
321
+ syp = np.sin(y/p2)
322
+ czr = np.cos(z1/r1)
323
+ szr = np.sin(z1/r1)
324
+ expr= np.exp(sqpr*x1)
325
+ fx4 =-sqpr*expr*cyp*szr
326
+ hy4 = expr/p2*syp*szr
327
+ fz4 =-expr*cyp/r1*czr
328
+ hx4 = fx4*ct1+fz4*st1
329
+ hz4 =-fx4*st1+fz4*ct1
330
+
331
+ sqpr= np.sqrt(1/p2**2+1/r2**2)
332
+ cyp = np.cos(y/p2)
333
+ syp = np.sin(y/p2)
334
+ czr = np.cos(z1/r2)
335
+ szr = np.sin(z1/r2)
336
+ expr= np.exp(sqpr*x1)
337
+ fx5 =-sqpr*expr*cyp*szr
338
+ hy5 = expr/p2*syp*szr
339
+ fz5 =-expr*cyp/r2*czr
340
+ hx5 = fx5*ct1+fz5*st1
341
+ hz5 =-fx5*st1+fz5*ct1
342
+
343
+ sqpr= np.sqrt(1/p2**2+1/r3**2)
344
+ cyp = np.cos(y/p2)
345
+ syp = np.sin(y/p2)
346
+ czr = np.cos(z1/r3)
347
+ szr = np.sin(z1/r3)
348
+ expr= np.exp(sqpr*x1)
349
+ fx6 =-expr*cyp*(sqpr*z1*czr+szr/r3*(x1+1/sqpr))
350
+ hy6 = expr/p2*syp*(z1*czr+x1/r3*szr/sqpr)
351
+ fz6 =-expr*cyp*(czr*(1+x1/r3**2/sqpr)-z1/r3*szr)
352
+ hx6 = fx6*ct1+fz6*st1
353
+ hz6 =-fx6*st1+fz6*ct1
354
+
355
+ # i=3:
356
+ sqpr= np.sqrt(1/p3**2+1/r1**2)
357
+ cyp = np.cos(y/p3)
358
+ syp = np.sin(y/p3)
359
+ czr = np.cos(z1/r1)
360
+ szr = np.sin(z1/r1)
361
+ expr= np.exp(sqpr*x1)
362
+ fx7 =-sqpr*expr*cyp*szr
363
+ hy7 = expr/p3*syp*szr
364
+ fz7 =-expr*cyp/r1*czr
365
+ hx7 = fx7*ct1+fz7*st1
366
+ hz7 =-fx7*st1+fz7*ct1
367
+
368
+ sqpr= np.sqrt(1/p3**2+1/r2**2)
369
+ cyp = np.cos(y/p3)
370
+ syp = np.sin(y/p3)
371
+ czr = np.cos(z1/r2)
372
+ szr = np.sin(z1/r2)
373
+ expr= np.exp(sqpr*x1)
374
+ fx8 =-sqpr*expr*cyp*szr
375
+ hy8 = expr/p3*syp*szr
376
+ fz8 =-expr*cyp/r2*czr
377
+ hx8 = fx8*ct1+fz8*st1
378
+ hz8 =-fx8*st1+fz8*ct1
379
+
380
+ sqpr= np.sqrt(1/p3**2+1/r3**2)
381
+ cyp = np.cos(y/p3)
382
+ syp = np.sin(y/p3)
383
+ czr = np.cos(z1/r3)
384
+ szr = np.sin(z1/r3)
385
+ expr= np.exp(sqpr*x1)
386
+ fx9 =-expr*cyp*(sqpr*z1*czr+szr/r3*(x1+1/sqpr))
387
+ hy9 = expr/p3*syp*(z1*czr+x1/r3*szr/sqpr)
388
+ fz9 =-expr*cyp*(czr*(1+x1/r3**2/sqpr)-z1/r3*szr)
389
+ hx9 = fx9*ct1+fz9*st1
390
+ hz9 =-fx9*st1+fz9*ct1
391
+
392
+ a1=a[0]+a[1]*cps
393
+ a2=a[2]+a[3]*cps
394
+ a3=a[4]+a[5]*cps
395
+ a4=a[6]+a[7]*cps
396
+ a5=a[8]+a[9]*cps
397
+ a6=a[10]+a[11]*cps
398
+ a7=a[12]+a[13]*cps
399
+ a8=a[14]+a[15]*cps
400
+ a9=a[16]+a[17]*cps
401
+ bx=a1*hx1+a2*hx2+a3*hx3+a4*hx4+a5*hx5+a6*hx6+a7*hx7+a8*hx8+a9*hx9
402
+ by=a1*hy1+a2*hy2+a3*hy3+a4*hy4+a5*hy5+a6*hy6+a7*hy7+a8*hy8+a9*hy9
403
+ bz=a1*hz1+a2*hz2+a3*hz3+a4*hz4+a5*hz5+a6*hz6+a7*hz7+a8*hz8+a9*hz9
404
+
405
+
406
+ # make the terms in the 2nd sum ("parallel" symmetry):
407
+ # i=1
408
+ sqqs= np.sqrt(1/q1**2+1/s1**2)
409
+ cyq = np.cos(y/q1)
410
+ syq = np.sin(y/q1)
411
+ czs = np.cos(z2/s1)
412
+ szs = np.sin(z2/s1)
413
+ exqs= np.exp(sqqs*x2)
414
+ fx1 =-sqqs*exqs*cyq*czs *sps
415
+ hy1 = exqs/q1*syq*czs *sps
416
+ fz1 = exqs*cyq/s1*szs *sps
417
+ hx1 = fx1*ct2+fz1*st2
418
+ hz1 =-fx1*st2+fz1*ct2
419
+
420
+ sqqs= np.sqrt(1/q1**2+1/s2**2)
421
+ cyq = np.cos(y/q1)
422
+ syq = np.sin(y/q1)
423
+ czs = np.cos(z2/s2)
424
+ szs = np.sin(z2/s2)
425
+ exqs= np.exp(sqqs*x2)
426
+ fx2 =-sqqs*exqs*cyq*czs *sps
427
+ hy2 = exqs/q1*syq*czs *sps
428
+ fz2 = exqs*cyq/s2*szs *sps
429
+ hx2 = fx2*ct2+fz2*st2
430
+ hz2 =-fx2*st2+fz2*ct2
431
+
432
+ sqqs= np.sqrt(1/q1**2+1/s3**2)
433
+ cyq = np.cos(y/q1)
434
+ syq = np.sin(y/q1)
435
+ czs = np.cos(z2/s3)
436
+ szs = np.sin(z2/s3)
437
+ exqs= np.exp(sqqs*x2)
438
+ fx3 =-sqqs*exqs*cyq*czs *sps
439
+ hy3 = exqs/q1*syq*czs *sps
440
+ fz3 = exqs*cyq/s3*szs *sps
441
+ hx3 = fx3*ct2+fz3*st2
442
+ hz3 =-fx3*st2+fz3*ct2
443
+
444
+ # i=2:
445
+ sqqs= np.sqrt(1/q2**2+1/s1**2)
446
+ cyq = np.cos(y/q2)
447
+ syq = np.sin(y/q2)
448
+ czs = np.cos(z2/s1)
449
+ szs = np.sin(z2/s1)
450
+ exqs= np.exp(sqqs*x2)
451
+ fx4 =-sqqs*exqs*cyq*czs *sps
452
+ hy4 = exqs/q2*syq*czs *sps
453
+ fz4 = exqs*cyq/s1*szs *sps
454
+ hx4 = fx4*ct2+fz4*st2
455
+ hz4 =-fx4*st2+fz4*ct2
456
+
457
+ sqqs= np.sqrt(1/q2**2+1/s2**2)
458
+ cyq = np.cos(y/q2)
459
+ syq = np.sin(y/q2)
460
+ czs = np.cos(z2/s2)
461
+ szs = np.sin(z2/s2)
462
+ exqs= np.exp(sqqs*x2)
463
+ fx5 =-sqqs*exqs*cyq*czs *sps
464
+ hy5 = exqs/q2*syq*czs *sps
465
+ fz5 = exqs*cyq/s2*szs *sps
466
+ hx5 = fx5*ct2+fz5*st2
467
+ hz5 =-fx5*st2+fz5*ct2
468
+
469
+ sqqs= np.sqrt(1/q2**2+1/s3**2)
470
+ cyq = np.cos(y/q2)
471
+ syq = np.sin(y/q2)
472
+ czs = np.cos(z2/s3)
473
+ szs = np.sin(z2/s3)
474
+ exqs= np.exp(sqqs*x2)
475
+ fx6 =-sqqs*exqs*cyq*czs *sps
476
+ hy6 = exqs/q2*syq*czs *sps
477
+ fz6 = exqs*cyq/s3*szs *sps
478
+ hx6 = fx6*ct2+fz6*st2
479
+ hz6 =-fx6*st2+fz6*ct2
480
+
481
+ # i=3:
482
+ sqqs= np.sqrt(1/q3**2+1/s1**2)
483
+ cyq = np.cos(y/q3)
484
+ syq = np.sin(y/q3)
485
+ czs = np.cos(z2/s1)
486
+ szs = np.sin(z2/s1)
487
+ exqs= np.exp(sqqs*x2)
488
+ fx7 =-sqqs*exqs*cyq*czs *sps
489
+ hy7 = exqs/q3*syq*czs *sps
490
+ fz7 = exqs*cyq/s1*szs *sps
491
+ hx7 = fx7*ct2+fz7*st2
492
+ hz7 =-fx7*st2+fz7*ct2
493
+
494
+ sqqs= np.sqrt(1/q3**2+1/s2**2)
495
+ cyq = np.cos(y/q3)
496
+ syq = np.sin(y/q3)
497
+ czs = np.cos(z2/s2)
498
+ szs = np.sin(z2/s2)
499
+ exqs= np.exp(sqqs*x2)
500
+ fx8 =-sqqs*exqs*cyq*czs *sps
501
+ hy8 = exqs/q3*syq*czs *sps
502
+ fz8 = exqs*cyq/s2*szs *sps
503
+ hx8 = fx8*ct2+fz8*st2
504
+ hz8 =-fx8*st2+fz8*ct2
505
+
506
+ sqqs= np.sqrt(1/q3**2+1/s3**2)
507
+ cyq = np.cos(y/q3)
508
+ syq = np.sin(y/q3)
509
+ czs = np.cos(z2/s3)
510
+ szs = np.sin(z2/s3)
511
+ exqs= np.exp(sqqs*x2)
512
+ fx9 =-sqqs*exqs*cyq*czs *sps
513
+ hy9 = exqs/q3*syq*czs *sps
514
+ fz9 = exqs*cyq/s3*szs *sps
515
+ hx9 = fx9*ct2+fz9*st2
516
+ hz9 =-fx9*st2+fz9*ct2
517
+
518
+ a1=a[18]+a[19]*s2ps
519
+ a2=a[20]+a[21]*s2ps
520
+ a3=a[22]+a[23]*s2ps
521
+ a4=a[24]+a[25]*s2ps
522
+ a5=a[26]+a[27]*s2ps
523
+ a6=a[28]+a[29]*s2ps
524
+ a7=a[30]+a[31]*s2ps
525
+ a8=a[32]+a[33]*s2ps
526
+ a9=a[34]+a[35]*s2ps
527
+
528
+ bx=bx+a1*hx1+a2*hx2+a3*hx3+a4*hx4+a5*hx5+a6*hx6+a7*hx7+a8*hx8+a9*hx9
529
+ by=by+a1*hy1+a2*hy2+a3*hy3+a4*hy4+a5*hy5+a6*hy6+a7*hy7+a8*hy8+a9*hy9
530
+ bz=bz+a1*hz1+a2*hz2+a3*hz3+a4*hz4+a5*hz5+a6*hz6+a7*hz7+a8*hz8+a9*hz9
531
+
532
+ return bx, by, bz
533
+
534
+ def deformed(iopt, ps, x,y,z):
535
+ """
536
+ Calculates gsm components of two unit-amplitude tail field modes, taking into account
537
+ both effects of dipole tilt: warping in y-z (done by the subroutine warped) and bending
538
+ in x-z (done by this subroutine)
539
+
540
+ :param iopt: tail field mode flag: iopt=0 - the two tail modes are added up; iopt=1 - mode 1 only; iopt=2 - mode 2 only
541
+ :param ps: geo-dipole tilt angle in radius.
542
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
543
+ :return:
544
+ """
545
+
546
+ # rh0,rh1,rh2, and ieps control the tilt-related deformation of the tail field
547
+ # common /rh0/ rh0
548
+ global rh0
549
+ rh2,ieps = [-5.2,3]
550
+
551
+ sps = np.sin(ps)
552
+ r2 = x**2+y**2+z**2
553
+ r = np.sqrt(r2)
554
+ zr = z/r
555
+ rh = rh0+rh2*zr**2
556
+ drhdr = -zr/r*2*rh2*zr
557
+ drhdz = 2*rh2*zr/r
558
+ rrh = r/rh
559
+ f = 1/(1+rrh**ieps)**(1/ieps)
560
+ dfdr = -rrh**(ieps-1)*f**(ieps+1)/rh
561
+ dfdrh = -rrh*dfdr
562
+
563
+ spsas = sps*f
564
+ cpsas = np.sqrt(1-spsas**2)
565
+
566
+ xas = x*cpsas-z*spsas
567
+ zas = x*spsas+z*cpsas
568
+
569
+ facps = sps/cpsas*(dfdr+dfdrh*drhdr)/r
570
+ psasx = facps*x
571
+ psasy = facps*y
572
+ psasz = facps*z+sps/cpsas*dfdrh*drhdz
573
+
574
+ dxasdx = cpsas-zas*psasx
575
+ dxasdy =-zas*psasy
576
+ dxasdz =-spsas-zas*psasz
577
+ dzasdx = spsas+xas*psasx
578
+ dzasdy = xas*psasy
579
+ dzasdz = cpsas+xas*psasz
580
+ fac1 = dxasdz*dzasdy-dxasdy*dzasdz
581
+ fac2 = dxasdx*dzasdz-dxasdz*dzasdx
582
+ fac3 = dzasdx*dxasdy-dxasdx*dzasdy
583
+
584
+ # deform:
585
+ bxas1,byas1,bzas1, bxas2,byas2,bzas2 = warped(iopt,ps,xas,y,zas)
586
+
587
+ bx1=bxas1*dzasdz-bzas1*dxasdz +byas1*fac1
588
+ by1=byas1*fac2
589
+ bz1=bzas1*dxasdx-bxas1*dzasdx +byas1*fac3
590
+
591
+ bx2=bxas2*dzasdz-bzas2*dxasdz +byas2*fac1
592
+ by2=byas2*fac2
593
+ bz2=bzas2*dxasdx-bxas2*dzasdx +byas2*fac3
594
+
595
+ return bx1,by1,bz1, bx2,by2,bz2
596
+
597
+ def warped(iopt, ps, x,y,z):
598
+ """
599
+ Calculates GSM components of the warped field for two tail unit modes. The warping deformation
600
+ is imposed on the unwarped field, computed by the subroutine "unwarped". The warping parameter
601
+ g was obtained by least squares fitting to the entire dataset.
602
+
603
+ :param iopt: tail field mode flag: iopt=0 - the two tail modes are added up; iopt=1 - mode 1 only; iopt=2 - mode 2 only
604
+ :param ps: geo-dipole tilt angle in radius.
605
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
606
+ :return:
607
+ """
608
+
609
+ # common /g/ g
610
+ global g
611
+ dgdx,xl,dxldx = [0.,20,0]
612
+
613
+ sps=np.sin(ps)
614
+ rho2=y**2+z**2
615
+ rho=np.sqrt(rho2)
616
+
617
+ if (y == 0) & (z == 0):
618
+ phi=0.
619
+ cphi=1.
620
+ sphi=0.
621
+ else:
622
+ phi=np.arctan2(z,y)
623
+ cphi=y/rho
624
+ sphi=z/rho
625
+
626
+ rr4l4=rho/(rho2**2+xl**4)
627
+
628
+ f=phi+g*rho2*rr4l4*cphi*sps
629
+ dfdphi=1-g*rho2*rr4l4*sphi*sps
630
+ dfdrho=g*rr4l4**2*(3*xl**4-rho2**2)*cphi*sps
631
+ dfdx=rr4l4*cphi*sps*(dgdx*rho2-g*rho*rr4l4*4*xl**3*dxldx)
632
+
633
+ cf=np.cos(f)
634
+ sf=np.sin(f)
635
+ yas=rho*cf
636
+ zas=rho*sf
637
+
638
+ bx_as1,by_as1,bz_as1, bx_as2,by_as2,bz_as2 = unwarped(iopt,x,yas,zas)
639
+
640
+ brho_as = by_as1*cf+bz_as1*sf # deform the 1st mode
641
+ bphi_as = -by_as1*sf+bz_as1*cf
642
+ brho_s = brho_as*dfdphi
643
+ bphi_s = bphi_as-rho*(bx_as1*dfdx+brho_as*dfdrho)
644
+ bx1 = bx_as1*dfdphi
645
+ by1 = brho_s*cphi-bphi_s*sphi
646
+ bz1 = brho_s*sphi+bphi_s*cphi # done
647
+
648
+ brho_as = by_as2*cf+bz_as2*sf # deform the 2nd mode
649
+ bphi_as = -by_as2*sf+bz_as2*cf
650
+ brho_s = brho_as*dfdphi
651
+ bphi_s = bphi_as-rho*(bx_as2*dfdx+brho_as*dfdrho)
652
+ bx2 = bx_as2*dfdphi
653
+ by2 = brho_s*cphi-bphi_s*sphi
654
+ bz2 = brho_s*sphi+bphi_s*cphi # done
655
+
656
+ return bx1,by1,bz1, bx2,by2,bz2
657
+
658
+ def unwarped(iopt, x,y,z):
659
+ """
660
+ Calculates GSM components of the shielded field of two tail modes with unit amplitudes, without any
661
+ warping or bending. Nonlinear parameters of the modes are forwarded here via a common block /tail/.
662
+ :param iopt: tail field mode flag: iopt=0 - the two tail modes are added up; iopt=1 - mode 1 only; iopt=2 - mode 2 only
663
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
664
+ :return:
665
+ """
666
+
667
+ # common /tail/ dxshift1,dxshift2,d,deltady
668
+ global dxshift1, dxshift2, d, deltady
669
+
670
+ deltadx1,alpha1,xshift1 = [1.,1.1,6]
671
+ deltadx2,alpha2,xshift2 = [0.,.25,4]
672
+
673
+ a1 = np.array([
674
+ -25.45869857,57.35899080,317.5501869,-2.626756717,-93.38053698,
675
+ -199.6467926,-858.8129729,34.09192395,845.4214929,-29.07463068,
676
+ 47.10678547,-128.9797943,-781.7512093,6.165038619,167.8905046,
677
+ 492.0680410,1654.724031,-46.77337920,-1635.922669,40.86186772,
678
+ -.1349775602,-.9661991179e-01,-.1662302354,.002810467517,.2487355077,
679
+ .1025565237,-14.41750229,-.8185333989,11.07693629,.7569503173,
680
+ -9.655264745,112.2446542,777.5948964,-5.745008536,-83.03921993,
681
+ -490.2278695,-1155.004209,39.08023320,1172.780574,-39.44349797,
682
+ -14.07211198,-40.41201127,-313.2277343,2.203920979,8.232835341,
683
+ 197.7065115,391.2733948,-18.57424451,-437.2779053,23.04976898,
684
+ 11.75673963,13.60497313,4.691927060,18.20923547,27.59044809,
685
+ 6.677425469,1.398283308,2.839005878,31.24817706,24.53577264])
686
+
687
+ a2 = np.array([
688
+ -287187.1962,4970.499233,410490.1952,-1347.839052,-386370.3240,
689
+ 3317.983750,-143462.3895,5706.513767,171176.2904,250.8882750,
690
+ -506570.8891,5733.592632,397975.5842,9771.762168,-941834.2436,
691
+ 7990.975260,54313.10318,447.5388060,528046.3449,12751.04453,
692
+ -21920.98301,-21.05075617,31971.07875,3012.641612,-301822.9103,
693
+ -3601.107387,1797.577552,-6.315855803,142578.8406,13161.93640,
694
+ 804184.8410,-14168.99698,-851926.6360,-1890.885671,972475.6869,
695
+ -8571.862853,26432.49197,-2554.752298,-482308.3431,-4391.473324,
696
+ 105155.9160,-1134.622050,-74353.53091,-5382.670711,695055.0788,
697
+ -916.3365144,-12111.06667,67.20923358,-367200.9285,-21414.14421,
698
+ 14.75567902,20.75638190,59.78601609,16.86431444,32.58482365,
699
+ 23.69472951,17.24977936,13.64902647,68.40989058,11.67828167])
700
+
701
+ xm1,xm2 = [-12.,-12]
702
+ bx1,by1,bz1, bx2,by2,bz2 = [0.]*6
703
+
704
+ if iopt < 2: # iopt = 0 or 1
705
+ xsc1 = (x-xshift1-dxshift1)*alpha1-xm1*(alpha1-1)
706
+ ysc1 = y*alpha1
707
+ zsc1 = z*alpha1
708
+ d0sc1 = d*alpha1 # here we use a single value d0 of the thickness for both modes
709
+
710
+ fx1,fy1,fz1 = taildisk(d0sc1,deltadx1,deltady,xsc1,ysc1,zsc1)
711
+ hx1,hy1,hz1 = shlcar5x5(a1,x,y,z,dxshift1)
712
+
713
+ bx1=fx1+hx1
714
+ by1=fy1+hy1
715
+ bz1=fz1+hz1
716
+
717
+ if iopt != 1: # iop = 0 or 2
718
+ xsc2 = (x-xshift2-dxshift2)*alpha2-xm2*(alpha2-1)
719
+ ysc2 = y*alpha2
720
+ zsc2 = z*alpha2
721
+ d0sc2 = d*alpha2 # here we use a single value d0 of the thickness for both modes
722
+
723
+ fx2,fy2,fz2 = taildisk(d0sc2,deltadx2,deltady,xsc2,ysc2,zsc2)
724
+ hx2,hy2,hz2 = shlcar5x5(a2,x,y,z,dxshift2)
725
+
726
+ bx2=fx2+hx2
727
+ by2=fy2+hy2
728
+ bz2=fz2+hz2
729
+
730
+ return bx1,by1,bz1, bx2,by2,bz2
731
+
732
+ def taildisk(d0,deltadx,deltady, x,y,z):
733
+ """
734
+ This subroutine computes the components of the tail current field, similar to that described by
735
+ Tsyganenko and peredo (1994). The difference is that now we use spacewarping, as described in
736
+ our paper on modeling Birkeland currents (Tsyganenko and stern, 1996) instead of shearing it in
737
+ the spirit of the T89 tail model.
738
+
739
+ :param d0:
740
+ :param deltadx:
741
+ :param deltady:
742
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
743
+ :return: bx,by,bz. Field components in GSM system, in nT.
744
+ """
745
+
746
+ f = np.array([-71.09346626,-1014.308601,-1272.939359,-3224.935936,-44546.86232])
747
+ b = np.array([10.90101242,12.68393898,13.51791954,14.86775017,15.12306404])
748
+ c = np.array([.7954069972,.6716601849,1.174866319,2.565249920,10.01986790])
749
+
750
+ rho=np.sqrt(x**2+y**2)
751
+ drhodx=x/rho
752
+ drhody=y/rho
753
+
754
+ dex=np.exp(x/7)
755
+ d=d0+deltady*(y/20)**2+deltadx*dex # The last term (introduced 10/11/2000) makes the sheet thicken sunward, to avoid problems in the subsolar region
756
+ dddy=deltady*y*0.005
757
+ dddx=deltadx/7*dex
758
+
759
+ dzeta=np.sqrt(z**2+d**2) # this is the same simple way to spread out the sheet, as that used in t89
760
+ ddzetadx=d*dddx/dzeta
761
+ ddzetady=d*dddy/dzeta
762
+ ddzetadz=z/dzeta
763
+
764
+ dbx,dby,dbz = [0.0,0,0]
765
+
766
+ for i in range(5):
767
+ bi=b[i]
768
+ ci=c[i]
769
+
770
+ s1=np.sqrt((rho+bi)**2+(dzeta+ci)**2)
771
+ s2=np.sqrt((rho-bi)**2+(dzeta+ci)**2)
772
+
773
+ ds1drho=(rho+bi)/s1
774
+ ds2drho=(rho-bi)/s2
775
+ ds1ddz=(dzeta+ci)/s1
776
+ ds2ddz=(dzeta+ci)/s2
777
+
778
+ ds1dx=ds1drho*drhodx+ds1ddz*ddzetadx
779
+ ds1dy=ds1drho*drhody+ds1ddz*ddzetady
780
+ ds1dz= ds1ddz*ddzetadz
781
+
782
+ ds2dx=ds2drho*drhodx+ds2ddz*ddzetadx
783
+ ds2dy=ds2drho*drhody+ds2ddz*ddzetady
784
+ ds2dz= ds2ddz*ddzetadz
785
+
786
+ s1ts2=s1*s2
787
+ s1ps2=s1+s2
788
+ s1ps2sq=s1ps2**2
789
+
790
+ fac1=np.sqrt(s1ps2sq-(2*bi)**2)
791
+ asas=fac1/(s1ts2*s1ps2sq)
792
+ dasds1=(1/(fac1*s2)-asas/s1ps2*(s2*s2+s1*(3*s1+4*s2)))/(s1*s1ps2)
793
+ dasds2=(1/(fac1*s1)-asas/s1ps2*(s1*s1+s2*(3*s2+4*s1)))/(s2*s1ps2)
794
+
795
+ dasdx=dasds1*ds1dx+dasds2*ds2dx
796
+ dasdy=dasds1*ds1dy+dasds2*ds2dy
797
+ dasdz=dasds1*ds1dz+dasds2*ds2dz
798
+
799
+ dbx=dbx-f[i]*x*dasdz
800
+ dby=dby-f[i]*y*dasdz
801
+ dbz=dbz+f[i]*(2*asas+x*dasdx+y*dasdy)
802
+
803
+ return dbx, dby, dbz
804
+
805
+ def shlcar5x5(a,x,y,z,dshift):
806
+ """
807
+ This code returns the shielding field represented by 5x5=25 "cartesian" harmonics
808
+
809
+ :param a:
810
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
811
+ :param dshift:
812
+ :return:
813
+ """
814
+
815
+ # The nlin coefficients are the amplitudes of the "cartesian" harmonics (a(1)-a(nlin).
816
+ # The nnp nonlinear parameters (a(nlin+1)-a(ntot) are the scales pi and ri entering the arguments of exponents, sines,
817
+ # and cosines in each of the nlin "cartesian" harmonics
818
+
819
+ dhx,dhy,dhz = [0.]*3
820
+
821
+ l=0
822
+ for i in range(5):
823
+ rp=1/a[50+i]
824
+ cypi=np.cos(y*rp)
825
+ sypi=np.sin(y*rp)
826
+
827
+ for k in range(5):
828
+ rr=1/a[55+k]
829
+ szrk=np.sin(z*rr)
830
+ czrk=np.cos(z*rr)
831
+ sqpr=np.sqrt(rp**2+rr**2)
832
+ epr= np.exp(x*sqpr)
833
+
834
+ dbx=-sqpr*epr*cypi*szrk
835
+ dby= rp*epr*sypi*szrk
836
+ dbz=-rr*epr*cypi*czrk
837
+
838
+ coef=a[l]+a[l+1]*dshift
839
+ l += 2
840
+ dhx=dhx+coef*dbx
841
+ dhy=dhy+coef*dby
842
+ dhz=dhz+coef*dbz
843
+
844
+ return dhx,dhy,dhz
845
+
846
+ def birk_tot(iopb, ps, x,y,z):
847
+ """
848
+
849
+ :param iopb: birkeland field mode flag:
850
+ iopb=0 - all components; iopb=1 - region 1, modes 1 & 2; iopb=2 - region 2, modes 1 & 2
851
+ :param ps: geo-dipole tilt angle in radius.
852
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
853
+ :return: bx11,by11,bz11, bx12,by12,bz12, bx21,by21,bz21, bx22,by22,bz22.
854
+ """
855
+
856
+ # common /birkpar/ xkappa1,xkappa2 ! input parameters, specified from s/r extall
857
+ # common /dphi_b_rho0/ dphi,b,rho_0,xkappa ! parameters, controlling the day-night asymmetry of f.a.c.
858
+ global xkappa1, xkappa2
859
+ global dphi, b, rho_0, xkappa
860
+
861
+ sh11 = np.array([
862
+ 46488.84663,-15541.95244,-23210.09824,-32625.03856,-109894.4551,
863
+ -71415.32808,58168.94612,55564.87578,-22890.60626,-6056.763968,
864
+ 5091.368100,239.7001538,-13899.49253,4648.016991,6971.310672,
865
+ 9699.351891,32633.34599,21028.48811,-17395.96190,-16461.11037,
866
+ 7447.621471,2528.844345,-1934.094784,-588.3108359,-32588.88216,
867
+ 10894.11453,16238.25044,22925.60557,77251.11274,50375.97787,
868
+ -40763.78048,-39088.60660,15546.53559,3559.617561,-3187.730438,
869
+ 309.1487975,88.22153914,-243.0721938,-63.63543051,191.1109142,
870
+ 69.94451996,-187.9539415,-49.89923833,104.0902848,-120.2459738,
871
+ 253.5572433,89.25456949,-205.6516252,-44.93654156,124.7026309,
872
+ 32.53005523,-98.85321751,-36.51904756,98.88241690,24.88493459,
873
+ -55.04058524,61.14493565,-128.4224895,-45.35023460,105.0548704,
874
+ -43.66748755,119.3284161,31.38442798,-92.87946767,-33.52716686,
875
+ 89.98992001,25.87341323,-48.86305045,59.69362881,-126.5353789,
876
+ -44.39474251,101.5196856,59.41537992,41.18892281,80.86101200,
877
+ 3.066809418,7.893523804,30.56212082,10.36861082,8.222335945,
878
+ 19.97575641,2.050148531,4.992657093,2.300564232,.2256245602,-.05841594319])
879
+
880
+ sh12 = np.array([
881
+ 210260.4816,-1443587.401,-1468919.281,281939.2993,-1131124.839,
882
+ 729331.7943,2573541.307,304616.7457,468887.5847,181554.7517,
883
+ -1300722.650,-257012.8601,645888.8041,-2048126.412,-2529093.041,
884
+ 571093.7972,-2115508.353,1122035.951,4489168.802,75234.22743,
885
+ 823905.6909,147926.6121,-2276322.876,-155528.5992,-858076.2979,
886
+ 3474422.388,3986279.931,-834613.9747,3250625.781,-1818680.377,
887
+ -7040468.986,-414359.6073,-1295117.666,-346320.6487,3565527.409,
888
+ 430091.9496,-.1565573462,7.377619826,.4115646037,-6.146078880,
889
+ 3.808028815,-.5232034932,1.454841807,-12.32274869,-4.466974237,
890
+ -2.941184626,-.6172620658,12.64613490,1.494922012,-21.35489898,
891
+ -1.652256960,16.81799898,-1.404079922,-24.09369677,-10.99900839,
892
+ 45.94237820,2.248579894,31.91234041,7.575026816,-45.80833339,
893
+ -1.507664976,14.60016998,1.348516288,-11.05980247,-5.402866968,
894
+ 31.69094514,12.28261196,-37.55354174,4.155626879,-33.70159657,
895
+ -8.437907434,36.22672602,145.0262164,70.73187036,85.51110098,
896
+ 21.47490989,24.34554406,31.34405345,4.655207476,5.747889264,
897
+ 7.802304187,1.844169801,4.867254550,2.941393119,.1379899178,.06607020029])
898
+
899
+ sh21 = np.array([
900
+ 162294.6224,503885.1125,-27057.67122,-531450.1339,84747.05678,
901
+ -237142.1712,84133.61490,259530.0402,69196.05160,-189093.5264,
902
+ -19278.55134,195724.5034,-263082.6367,-818899.6923,43061.10073,
903
+ 863506.6932,-139707.9428,389984.8850,-135167.5555,-426286.9206,
904
+ -109504.0387,295258.3531,30415.07087,-305502.9405,100785.3400,
905
+ 315010.9567,-15999.50673,-332052.2548,54964.34639,-152808.3750,
906
+ 51024.67566,166720.0603,40389.67945,-106257.7272,-11126.14442,
907
+ 109876.2047,2.978695024,558.6019011,2.685592939,-338.0004730,
908
+ -81.99724090,-444.1102659,89.44617716,212.0849592,-32.58562625,
909
+ -982.7336105,-35.10860935,567.8931751,-1.917212423,-260.2023543,
910
+ -1.023821735,157.5533477,23.00200055,232.0603673,-36.79100036,
911
+ -111.9110936,18.05429984,447.0481000,15.10187415,-258.7297813,
912
+ -1.032340149,-298.6402478,-1.676201415,180.5856487,64.52313024,
913
+ 209.0160857,-53.85574010,-98.52164290,14.35891214,536.7666279,
914
+ 20.09318806,-309.7349530,58.54144539,67.45226850,97.92374406,
915
+ 4.752449760,10.46824379,32.91856110,12.05124381,9.962933904,
916
+ 15.91258637,1.804233877,6.578149088,2.515223491,.1930034238,-.02261109942])
917
+
918
+ sh22 = np.array([
919
+ -131287.8986,-631927.6885,-318797.4173,616785.8782,-50027.36189,
920
+ 863099.9833,47680.20240,-1053367.944,-501120.3811,-174400.9476,
921
+ 222328.6873,333551.7374,-389338.7841,-1995527.467,-982971.3024,
922
+ 1960434.268,297239.7137,2676525.168,-147113.4775,-3358059.979,
923
+ -2106979.191,-462827.1322,1017607.960,1039018.475,520266.9296,
924
+ 2627427.473,1301981.763,-2577171.706,-238071.9956,-3539781.111,
925
+ 94628.16420,4411304.724,2598205.733,637504.9351,-1234794.298,
926
+ -1372562.403,-2.646186796,-31.10055575,2.295799273,19.20203279,
927
+ 30.01931202,-302.1028550,-14.78310655,162.1561899,.4943938056,
928
+ 176.8089129,-.2444921680,-100.6148929,9.172262228,137.4303440,
929
+ -8.451613443,-84.20684224,-167.3354083,1321.830393,76.89928813,
930
+ -705.7586223,18.28186732,-770.1665162,-9.084224422,436.3368157,
931
+ -6.374255638,-107.2730177,6.080451222,65.53843753,143.2872994,
932
+ -1028.009017,-64.22739330,547.8536586,-20.58928632,597.3893669,
933
+ 10.17964133,-337.7800252,159.3532209,76.34445954,84.74398828,
934
+ 12.76722651,27.63870691,32.69873634,5.145153451,6.310949163,
935
+ 6.996159733,1.971629939,4.436299219,2.904964304,.1486276863,.06859991529])
936
+
937
+ xkappa=xkappa1 # forwarded in birk_1n2
938
+ x_sc=xkappa1-1.1 # forwarded in birk_shl
939
+
940
+ bx11,by11,bz11, bx12,by12,bz12, bx21,by21,bz21, bx22,by22,bz22 = [0]*12
941
+
942
+ if (iopb == 0) | (iopb == 1):
943
+ fx11,fy11,fz11 = birk_1n2(1,1,ps,x,y,z) # region 1, mode 1
944
+ hx11,hy11,hz11 = birk_shl(sh11,ps,x_sc,x,y,z)
945
+ bx11=fx11+hx11
946
+ by11=fy11+hy11
947
+ bz11=fz11+hz11
948
+
949
+
950
+ fx12,fy12,fz12 = birk_1n2(1,2,ps,x,y,z) # region 1, mode 2
951
+ hx12,hy12,hz12 = birk_shl(sh12,ps,x_sc,x,y,z)
952
+ bx12=fx12+hx12
953
+ by12=fy12+hy12
954
+ bz12=fz12+hz12
955
+
956
+ xkappa=xkappa2 # forwarded in birk_1n2
957
+ x_sc=xkappa2-1.0 # forwarded in birk_shl
958
+
959
+ if (iopb == 0) | (iopb == 2):
960
+ fx21,fy21,fz21 = birk_1n2(2,1,ps,x,y,z) # region 2, mode 1
961
+ hx21,hy21,hz21 = birk_shl(sh21,ps,x_sc,x,y,z)
962
+ bx21=fx21+hx21
963
+ by21=fy21+hy21
964
+ bz21=fz21+hz21
965
+
966
+ fx22,fy22,fz22 = birk_1n2(2,2,ps,x,y,z) # region 2, mode 2
967
+ hx22,hy22,hz22 = birk_shl(sh22,ps,x_sc,x,y,z)
968
+ bx22=fx22+hx22
969
+ by22=fy22+hy22
970
+ bz22=fz22+hz22
971
+
972
+ return bx11,by11,bz11, bx12,by12,bz12, bx21,by21,bz21, bx22,by22,bz22
973
+
974
+ def birk_1n2(numb,mode,ps,x,y,z): # NB# 6, p.60
975
+ """
976
+ Calculates components of region 1/2 field in spherical coords. Derived from the s/r dipdef2c
977
+ (which does the same job, but input/output there was in spherical coords, while here we use cartesian ones)
978
+ :param numb: numb=1 (2) for region 1 (2) currents
979
+ :param mode: mode=1 yields simple sinusoidal mlt variation, with maximum current at dawn/dusk meridian
980
+ while mode=2 yields the second harmonic.
981
+ :param ps: geo-dipole tilt angle in radius.
982
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
983
+ :return: bx,by,bz. Field components in GSM system, in nT.
984
+ """
985
+
986
+ # common /dphi_b_rho0/ dphi,b,rho_0,xkappa ! these parameters control day-night asymmetry of f.a.c., as follows:
987
+ # (1) dphi: half-difference (in radians) between day and night latitude of fac oval at ionospheric altitude; typical value: 0.06
988
+ # (2) b: an asymmetry factor at high-altitudes; for b=0, the only asymmetry is that from dphi; typical values: 0.35-0.70
989
+ # (3) rho_0: a fixed parameter, defining the distance rho, at which the latitude shift gradually saturates and stops increasing; its value was assumed fixed, equal to 7.0.
990
+ # (4) xkappa: an overall scaling factor, which can be used for changing the size of the f.a.c. oval
991
+
992
+
993
+ global dtheta, m, dphi, b, rho_0, xkappa
994
+
995
+ # parameters of the tilt-dependent deformation of the untilted F.A.C. field
996
+ beta = 0.9
997
+ rh = 10.
998
+ eps = 3.
999
+ b=0.5
1000
+ rho_0=7.0
1001
+
1002
+ a11 = np.array([
1003
+ .1618068350, -.1797957553, 2.999642482, -.9322708978, -.6811059760,
1004
+ .2099057262, -8.358815746, -14.86033550, .3838362986, -16.30945494,
1005
+ 4.537022847, 2.685836007, 27.97833029, 6.330871059, 1.876532361,
1006
+ 18.95619213, .9651528100, .4217195118, -.08957770020, -1.823555887,
1007
+ .7457045438, -.5785916524, -1.010200918, .01112389357, .09572927448,
1008
+ -.3599292276, 8.713700514, .9763932955, 3.834602998, 2.492118385, .7113544659])
1009
+ a12 = np.array([
1010
+ .7058026940, -.2845938535, 5.715471266, -2.472820880, -.7738802408,
1011
+ .3478293930, -11.37653694, -38.64768867, .6932927651, -212.4017288,
1012
+ 4.944204937, 3.071270411, 33.05882281, 7.387533799, 2.366769108,
1013
+ 79.22572682, .6154290178, .5592050551, -.1796585105, -1.654932210,
1014
+ .7309108776, -.4926292779, -1.130266095, -.009613974555, .1484586169,
1015
+ -.2215347198, 7.883592948, .02768251655, 2.950280953, 1.212634762, .5567714182])
1016
+ a21 = np.array([
1017
+ .1278764024, -.2320034273, 1.805623266, -32.37241440, -.9931490648,
1018
+ .3175085630, -2.492465814, -16.21600096, .2695393416, -6.752691265,
1019
+ 3.971794901, 14.54477563, 41.10158386, 7.912889730, 1.258297372,
1020
+ 9.583547721, 1.014141963, .5104134759, -.1790430468, -1.756358428,
1021
+ .7561986717, -.6775248254, -.04014016420, .01446794851, .1200521731,
1022
+ -.2203584559, 4.508963850, .8221623576, 1.779933730, 1.102649543, .8867880020])
1023
+ a22 = np.array([
1024
+ .4036015198, -.3302974212, 2.827730930, -45.44405830, -1.611103927,
1025
+ .4927112073, -.003258457559, -49.59014949, .3796217108, -233.7884098,
1026
+ 4.312666980, 18.05051709, 28.95320323, 11.09948019, .7471649558,
1027
+ 67.10246193, .5667096597, .6468519751, -.1560665317, -1.460805289,
1028
+ .7719653528, -.6658988668, .2515179349E-05, .02426021891, .1195003324,
1029
+ -.2625739255, 4.377172556, .2421190547, 2.503482679, 1.071587299, .7247997430])
1030
+
1031
+
1032
+ m=mode
1033
+ if numb == 1:
1034
+ dphi=0.055
1035
+ dtheta=0.06
1036
+ elif numb == 2:
1037
+ dphi=0.030
1038
+ dtheta=0.09
1039
+ else:
1040
+ raise ValueError
1041
+
1042
+
1043
+ xsc=x*xkappa
1044
+ ysc=y*xkappa
1045
+ zsc=z*xkappa
1046
+ rho=np.sqrt(xsc**2+zsc**2)
1047
+
1048
+ rsc=np.sqrt(xsc**2+ysc**2+zsc**2) # scaled
1049
+ rho2=rho_0**2
1050
+
1051
+ if (xsc == 0) & (zsc == 0):
1052
+ phi=0.
1053
+ else:
1054
+ phi=np.arctan2(-zsc,xsc) # from cartesian to cylindrical (rho,phi,y)
1055
+
1056
+ sphic=np.sin(phi)
1057
+ cphic=np.cos(phi) # "c" means "cylindrical", to distinguish from spherical phi
1058
+
1059
+ brack=dphi+b*rho2/(rho2+1)*(rho**2-1)/(rho2+rho**2)
1060
+ r1rh=(rsc-1)/rh
1061
+ psias=beta*ps/(1+r1rh**eps)**(1/eps)
1062
+
1063
+ phis=phi-brack*np.sin(phi) -psias
1064
+ dphisphi=1-brack*np.cos(phi)
1065
+ dphisrho=-2*b*rho2*rho/(rho2+rho**2)**2*np.sin(phi) \
1066
+ +beta*ps*r1rh**(eps-1)*rho/(rh*rsc*(1+r1rh**eps)**(1/eps+1))
1067
+ dphisdy= beta*ps*r1rh**(eps-1)*ysc/(rh*rsc*(1+r1rh**eps)**(1/eps+1))
1068
+
1069
+ sphics=np.sin(phis)
1070
+ cphics=np.cos(phis)
1071
+
1072
+ xs= rho*cphics
1073
+ zs=-rho*sphics
1074
+
1075
+ if numb ==1:
1076
+ if mode == 1: [bxs,byas,bzs] = twocones(a11,xs,ysc,zs)
1077
+ elif mode == 2: [bxs,byas,bzs] = twocones(a12,xs,ysc,zs)
1078
+ else: raise ValueError
1079
+ else:
1080
+ if mode == 1: [bxs,byas,bzs] = twocones(a21,xs,ysc,zs)
1081
+ elif mode == 2: [bxs,byas,bzs] = twocones(a22,xs,ysc,zs)
1082
+ else: raise ValueError
1083
+
1084
+ brhoas = bxs*cphics-bzs*sphics
1085
+ bphias = -bxs*sphics-bzs*cphics
1086
+
1087
+ brho_s=brhoas*dphisphi *xkappa # scaling
1088
+ bphi_s=(bphias-rho*(byas*dphisdy+brhoas*dphisrho)) *xkappa
1089
+ by_s=byas*dphisphi *xkappa
1090
+
1091
+ bx=brho_s*cphic-bphi_s*sphic
1092
+ by=by_s
1093
+ bz=-brho_s*sphic-bphi_s*cphic
1094
+
1095
+ return bx,by,bz
1096
+
1097
+
1098
+
1099
+ def twocones (a,x,y,z):
1100
+ """
1101
+ Adds fields from two cones (northern and southern), with a proper symmetry of the current and field,
1102
+ corresponding to the region 1 Birkeland currents. (NB #6, p.58).
1103
+
1104
+ :param a:
1105
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
1106
+ :return: bx,by,bz. Field components in GSM system, in nT.
1107
+ """
1108
+
1109
+ bxn,byn,bzn = one_cone(a,x, y, z)
1110
+ bxs,bys,bzs = one_cone(a,x,-y,-z)
1111
+ bx=bxn-bxs
1112
+ by=byn+bys
1113
+ bz=bzn+bzs
1114
+
1115
+ return bx,by,bz
1116
+
1117
+ def one_cone(a,x,y,z):
1118
+ """
1119
+ Returns field components for a deformed conical current system, fitted to a Biosavart field.
1120
+ Here only the northern cone is taken into account.
1121
+
1122
+ :param a: dimension a(31)
1123
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
1124
+ :return: bx,by,bz. Field components in GSM system, in nT.
1125
+ """
1126
+
1127
+ # common /dtheta/ dtheta
1128
+ # common /modenum/ m
1129
+ global dtheta, m
1130
+
1131
+ # just for numerical differentiation
1132
+ dr = 1e-6
1133
+ dt = 1e-6
1134
+
1135
+ theta0=a[30]
1136
+
1137
+ rho2=x**2+y**2
1138
+ rho=np.sqrt(rho2)
1139
+ r=np.sqrt(rho2+z**2)
1140
+ theta=np.arctan2(rho,z)
1141
+ phi=np.arctan2(y,x)
1142
+
1143
+ # make the deformation of coordinates:
1144
+ rs=r_s(a,r,theta)
1145
+ thetas=theta_s(a,r,theta)
1146
+ phis=phi
1147
+
1148
+ # calculate field components at the new position (asterisked):
1149
+ btast,bfast = fialcos(rs,thetas,phis,m,theta0,dtheta) # mode #m
1150
+
1151
+ # now transform b{r,t,f}_ast by the deformation tensor:
1152
+ # first of all, find the derivatives:
1153
+ drsdr=(r_s(a,r+dr,theta)-r_s(a,r-dr,theta))/(2*dr)
1154
+ drsdt=(r_s(a,r,theta+dt)-r_s(a,r,theta-dt))/(2*dt)
1155
+ dtsdr=(theta_s(a,r+dr,theta)-theta_s(a,r-dr,theta))/(2*dr)
1156
+ dtsdt=(theta_s(a,r,theta+dt)-theta_s(a,r,theta-dt))/(2*dt)
1157
+
1158
+ stsst=np.sin(thetas)/np.sin(theta)
1159
+ rsr=rs/r
1160
+
1161
+ br =-rsr/r*stsst*btast*drsdt # NB#6, p.43 brast does not enter here
1162
+ btheta = rsr*stsst*btast*drsdr # (it is identically zero in our case)
1163
+ bphi = rsr*bfast*(drsdr*dtsdt-drsdt*dtsdr)
1164
+
1165
+ s=rho/r
1166
+ c=z/r
1167
+ sf=y/rho
1168
+ cf=x/rho
1169
+
1170
+ be=br*s+btheta*c
1171
+
1172
+ bx=a[0]*(be*cf-bphi*sf)
1173
+ by=a[0]*(be*sf+bphi*cf)
1174
+ bz=a[0]*(br*c-btheta*s)
1175
+
1176
+ return bx,by,bz
1177
+
1178
+ def r_s(a,r,theta):
1179
+
1180
+ # dimension a(31)
1181
+ return r+a[1]/r+a[2]*r/np.sqrt(r**2+a[10]**2)+a[3]*r/(r**2+a[11]**2) \
1182
+ +(a[4]+a[5]/r+a[6]*r/np.sqrt(r**2+a[12]**2)+a[7]*r/(r**2+a[13]**2))*np.cos(theta) \
1183
+ +(a[8]*r/np.sqrt(r**2+a[14]**2)+a[9]*r/(r**2+a[15]**2)**2)*np.cos(2*theta)
1184
+
1185
+ def theta_s(a,r,theta):
1186
+ # dimension a(31)
1187
+ return theta+(a[16]+a[17]/r+a[18]/r**2+a[19]*r/np.sqrt(r**2+a[26]**2))*np.sin(theta) \
1188
+ +(a[20]+a[21]*r/np.sqrt(r**2+a[27]**2)+a[22]*r/(r**2+a[28]**2))*np.sin(2*theta) \
1189
+ +(a[23]+a[24]/r+a[25]*r/(r**2+a[29]**2))*np.sin(3*theta)
1190
+
1191
+
1192
+ def fialcos(r,theta,phi,n,theta0,dt):
1193
+ """
1194
+ Conical model of Birkeland current field; based on the old s/r fialco (of 1990-91) NB of 1985-86-88,
1195
+ note of March 5, but here both input and output are in spherical CDS.
1196
+ :param r:
1197
+ :param theta:
1198
+ :param phi:
1199
+ :param n:
1200
+ :param theta0:
1201
+ :param dt:
1202
+ :return: btheta,bphi.
1203
+ """
1204
+
1205
+ # btn, and bpn are the arrays of btheta and bphi (btn(i), bpn(i) correspond to i-th mode).
1206
+ # only first n mode amplitudes are computed (n<=10).
1207
+ # theta0 is the angular half-width of the cone, dt is the angular h.-w. of the current layer
1208
+ # note: br=0 (because only radial currents are present in this model)
1209
+
1210
+ # dimension btn(10),bpn(10),ccos(10),ssin(10)
1211
+ btn = np.empty(10)
1212
+ bpn = np.empty(10)
1213
+ ccos = np.empty(10)
1214
+ ssin = np.empty(10)
1215
+
1216
+ sinte=np.sin(theta)
1217
+ ro=r*sinte
1218
+ coste=np.cos(theta)
1219
+ sinfi=np.sin(phi)
1220
+ cosfi=np.cos(phi)
1221
+ tg=sinte/(1+coste) # tan(theta/2)
1222
+ ctg=sinte/(1-coste) # cot(theta/2)
1223
+
1224
+
1225
+ tetanp=theta0+dt
1226
+ tetanm=theta0-dt
1227
+ if theta >= tetanm:
1228
+ tgp=np.tan(tetanp*0.5)
1229
+ tgm=np.tan(tetanm*0.5)
1230
+ tgm2=tgm*tgm
1231
+ tgp2=tgp*tgp
1232
+
1233
+ [cosm1, sinm1] = [1.,0]
1234
+ tm = 1
1235
+ [tgm2m,tgp2m] = [1.,1]
1236
+
1237
+ for m in range(1,n+1):
1238
+ tm=tm*tg
1239
+ ccos[m-1]=cosm1*cosfi-sinm1*sinfi
1240
+ ssin[m-1]=sinm1*cosfi+cosm1*sinfi
1241
+ cosm1=ccos[m-1]
1242
+ sinm1=ssin[m-1]
1243
+ if theta < tetanm:
1244
+ t=tm
1245
+ dtt=0.5*m*tm*(tg+ctg)
1246
+ dtt0=0
1247
+ elif theta < tetanp:
1248
+ tgm2m=tgm2m*tgm2
1249
+ fc=1/(tgp-tgm)
1250
+ fc1=1/(2*m+1)
1251
+ tgm2m1=tgm2m*tgm
1252
+ tg21=1+tg*tg
1253
+ t=fc*(tm*(tgp-tg)+fc1*(tm*tg-tgm2m1/tm))
1254
+ dtt=0.5*m*fc*tg21*(tm/tg*(tgp-tg)-fc1*(tm-tgm2m1/(tm*tg)))
1255
+ dtt0=0.5*fc*((tgp+tgm)*(tm*tg-fc1*(tm*tg-tgm2m1/tm))+tm*(1-tgp*tgm)-(1+tgm2)*tgm2m/tm)
1256
+ else:
1257
+ tgp2m=tgp2m*tgp2
1258
+ tgm2m=tgm2m*tgm2
1259
+ fc=1/(tgp-tgm)
1260
+ fc1=1/(2*m+1)
1261
+ t=fc*fc1*(tgp2m*tgp-tgm2m*tgm)/tm
1262
+ dtt=-t*m*0.5*(tg+ctg)
1263
+
1264
+ btn[m-1]=m*t*ccos[m-1]/ro
1265
+ bpn[m-1]=-dtt*ssin[m-1]/r
1266
+
1267
+ btheta=btn[n-1] *800.
1268
+ bphi =bpn[n-1] *800.
1269
+
1270
+ return btheta, bphi
1271
+
1272
+
1273
+
1274
+
1275
+ def birk_shl(a,ps,x_sc, x,y,z):
1276
+ """
1277
+ B due to the Birkeland current shield.
1278
+ :param a: coefficient.
1279
+ :param ps: geo-dipole tilt angle in radius.
1280
+ :param x_sc:
1281
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
1282
+ :return: bx,by,bz. Field components in GSM system, in nT.
1283
+ """
1284
+
1285
+ cps=np.cos(ps)
1286
+ sps=np.sin(ps)
1287
+
1288
+ s3ps=2*cps
1289
+
1290
+ pst1=ps*a[84]
1291
+ pst2=ps*a[85]
1292
+
1293
+ st1=np.sin(pst1)
1294
+ ct1=np.cos(pst1)
1295
+ st2=np.sin(pst2)
1296
+ ct2=np.cos(pst2)
1297
+
1298
+ x1=x*ct1-z*st1
1299
+ z1=x*st1+z*ct1
1300
+ x2=x*ct2-z*st2
1301
+ z2=x*st2+z*ct2
1302
+
1303
+ l=0
1304
+ [bx,by,bz] = [0,0,0]
1305
+
1306
+ for m in range(1,3): # m=1 is for the 1st sum ("perp." symmetry) and m=2 is for the second sum ("parall." symmetry)
1307
+ for i in range(1,4):
1308
+ p = a[71 + i]
1309
+ q = a[77 + i]
1310
+ cypi = np.cos(y/p)
1311
+ cyqi = np.cos(y/q)
1312
+ sypi = np.sin(y/p)
1313
+ syqi = np.sin(y/q)
1314
+
1315
+ for k in range(1,4):
1316
+ r=a[74+k]
1317
+ s=a[80+k]
1318
+ szrk=np.sin(z1/r)
1319
+ czsk=np.cos(z2/s)
1320
+ czrk=np.cos(z1/r)
1321
+ szsk=np.sin(z2/s)
1322
+ sqpr=np.sqrt(1/p**2+1/r**2)
1323
+ sqqs=np.sqrt(1/q**2+1/s**2)
1324
+ epr=np.exp(x1*sqpr)
1325
+ eqs=np.exp(x2*sqqs)
1326
+
1327
+ for n in range(1,3): # n=1 is for the first part of each coefficient and n=2 is for the second one
1328
+ for nn in range(1,3): # nn = 1,2 further splits the coefficients into 2 parts, to take into account the scale factor dependence
1329
+ if m == 1:
1330
+ fx = -sqpr*epr*cypi*szrk
1331
+ fy = epr*sypi*szrk/p
1332
+ fz = -epr*cypi*czrk/r
1333
+ if n == 1:
1334
+ if nn == 1:
1335
+ [hx,hy,hz] = [fx,fy,fz]
1336
+ else:
1337
+ [hx,hy,hz] = [fx*x_sc, fy*x_sc, fz*x_sc]
1338
+ else:
1339
+ if nn == 1:
1340
+ [hx,hy,hz] = [fx*cps, fy*cps, fz*cps]
1341
+ else:
1342
+ [hx,hy,hz] = [fx*cps*x_sc, fy*cps*x_sc, fz*cps*x_sc]
1343
+ else: # m == 2
1344
+ fx = -sps*sqqs*eqs*cyqi*czsk
1345
+ fy = sps/q*eqs*syqi*czsk
1346
+ fz = sps/s*eqs*cyqi*szsk
1347
+ if n == 1:
1348
+ if nn == 1:
1349
+ [hx,hy,hz] = [fx,fy,fz]
1350
+ else:
1351
+ [hx,hy,hz] = [fx*x_sc, fy*x_sc, fz*x_sc]
1352
+ else:
1353
+ if nn == 1:
1354
+ [hx,hy,hz] = [fx*s3ps,fy*s3ps,fz*s3ps]
1355
+ else:
1356
+ [hx,hy,hz] = [fx*s3ps*x_sc, fy*s3ps*x_sc, fz*s3ps*x_sc]
1357
+ l=l+1
1358
+ if m == 1:
1359
+ hxr = hx*ct1+hz*st1
1360
+ hzr = -hx*st1+hz*ct1
1361
+ else:
1362
+ hxr = hx*ct2+hz*st2
1363
+ hzr = -hx*st2+hz*ct2
1364
+
1365
+ bx = bx+hxr*a[l-1]
1366
+ by = by+hy *a[l-1]
1367
+ bz = bz+hzr*a[l-1]
1368
+
1369
+ return bx,by,bz
1370
+
1371
+
1372
+
1373
+ def full_rc(iopr,ps,x,y,z):
1374
+ """
1375
+ Calculates GSM field components of the symmetric (src) and partial (prc) components of the ring current
1376
+ :param iopr: a ring current calculation flag (for least-squares fitting only):
1377
+ iopr=0 - both src and prc fields are calculated; opr=1 - src only; opr=2 - prc only
1378
+ :param ps: geo-dipole tilt angle in radius.
1379
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
1380
+ :return:
1381
+ """
1382
+
1383
+ # src provides a depression of -28 nt at earth
1384
+ # prc corresponds to the pressure difference of 2 npa between midnight and noon ring current particle pressure and yields a depression of -17 nt at x=-6re
1385
+
1386
+ # sc_sy and sc_pr are scaling factors for the symmetric and partial components: values larger than 1 result in spatially larger currents
1387
+ # phi is the rotation angle in radians of the partial ring current (measured from midnight toward dusk)
1388
+ # common /rcpar/ sc_sy,sc_pr,phi
1389
+ global sc_sy, sc_pr, phi
1390
+
1391
+
1392
+ # corrected values(as of may 2006)
1393
+
1394
+ c_sy = np.array([ # sy short for symmetric
1395
+ -957.2534900,-817.5450246,583.2991249,758.8568270,
1396
+ 13.17029064,68.94173502,-15.29764089,-53.43151590,27.34311724,
1397
+ 149.5252826,-11.00696044,-179.7031814,953.0914774,817.2340042,
1398
+ -581.0791366,-757.5387665,-13.10602697,-68.58155678,15.22447386,
1399
+ 53.15535633,-27.07982637,-149.1413391,10.91433279,179.3251739,
1400
+ -6.028703251,1.303196101,-1.345909343,-1.138296330,-0.06642634348,
1401
+ -0.3795246458,.07487833559,.2891156371,-.5506314391,-.4443105812,
1402
+ 0.2273682152,0.01086886655,-9.130025352,1.118684840,1.110838825,
1403
+ .1219761512,-.06263009645,-.1896093743,.03434321042,.01523060688,
1404
+ -.4913171541,-.2264814165,-.04791374574,.1981955976,-68.32678140,
1405
+ -48.72036263,14.03247808,16.56233733,2.369921099,6.200577111,
1406
+ -1.415841250,-0.8184867835,-3.401307527,-8.490692287,3.217860767,
1407
+ -9.037752107,66.09298105,48.23198578,-13.67277141,-16.27028909,
1408
+ -2.309299411,-6.016572391,1.381468849,0.7935312553,3.436934845,
1409
+ 8.260038635,-3.136213782,8.833214943,8.041075485,8.024818618,
1410
+ 35.54861873,12.55415215,1.738167799,3.721685353,23.06768025,
1411
+ 6.871230562,6.806229878,21.35990364,1.687412298,3.500885177,
1412
+ 0.3498952546,0.6595919814 ])
1413
+
1414
+
1415
+ c_pr = np.array([ # pr short for partial
1416
+ -64820.58481, -63965.62048, 66267.93413, 135049.7504, -36.56316878,
1417
+ 124.6614669, 56.75637955, -87.56841077, 5848.631425, 4981.097722,
1418
+ -6233.712207, -10986.40188, 68716.52057, 65682.69473, -69673.32198,
1419
+ -138829.3568, 43.45817708, -117.9565488, -62.14836263, 79.83651604,
1420
+ -6211.451069, -5151.633113, 6544.481271, 11353.03491, 23.72352603,
1421
+ -256.4846331, 25.77629189, 145.2377187, -4.472639098, -3.554312754,
1422
+ 2.936973114, 2.682302576, 2.728979958, 26.43396781, -9.312348296,
1423
+ -29.65427726, -247.5855336, -206.9111326, 74.25277664, 106.4069993,
1424
+ 15.45391072, 16.35943569, -5.965177750, -6.079451700, 115.6748385,
1425
+ -35.27377307, -32.28763497, -32.53122151, 93.74409310, 84.25677504,
1426
+ -29.23010465, -43.79485175, -6.434679514, -6.620247951, 2.443524317,
1427
+ 2.266538956, -43.82903825, 6.904117876, 12.24289401, 17.62014361,
1428
+ 152.3078796, 124.5505289, -44.58690290, -63.02382410, -8.999368955,
1429
+ -9.693774119, 3.510930306, 3.770949738, -77.96705716, 22.07730961,
1430
+ 20.46491655, 18.67728847, 9.451290614, 9.313661792, 644.7620970,
1431
+ 418.2515954, 7.183754387, 35.62128817, 19.43180682, 39.57218411,
1432
+ 15.69384715, 7.123215241, 2.300635346, 21.90881131, -.01775839370, .3996346710])
1433
+
1434
+
1435
+ hxsrc,hysrc,hzsrc, hxprc,hyprc,hzprc = src_prc(iopr, sc_sy,sc_pr, phi, ps, x,y,z)
1436
+
1437
+ x_sc=sc_sy-1
1438
+ fsx,fsy,fsz = [0.]*3
1439
+ if (iopr == 0) | (iopr == 1):
1440
+ fsx,fsy,fsz = rc_shield(c_sy,ps,x_sc, x,y,z)
1441
+
1442
+ x_sc=sc_pr-1
1443
+ fpx,fpy,fpz = [0.]*3
1444
+ if (iopr == 0) | (iopr == 2):
1445
+ fpx,fpy,fpz = rc_shield(c_pr,ps,x_sc, x,y,z)
1446
+
1447
+ bxsrc=hxsrc+fsx
1448
+ bysrc=hysrc+fsy
1449
+ bzsrc=hzsrc+fsz
1450
+
1451
+ bxprc=hxprc+fpx
1452
+ byprc=hyprc+fpy
1453
+ bzprc=hzprc+fpz
1454
+
1455
+ return bxsrc,bysrc,bzsrc,bxprc,byprc,bzprc
1456
+
1457
+
1458
+
1459
+ def src_prc(iopr,sc_sy,sc_pr,phi,ps, x,y,z):
1460
+ """
1461
+ Returns field components from a model ring current, including its symmetric part and a partial ring current,
1462
+ closed via birkeland currents. based on results, described in a paper "modeling the inner magnetosphere:
1463
+ asymmetric ring current and region 2 birkeland currents revisited" (jgr, dec.2000).
1464
+ :param iopr: a ring current calculation flag (for least-squares fitting only):
1465
+ iopr=0 - both src and prc fields are calculated; opr=1 - src only; opr=2 - prc only
1466
+ :param sc_sy, sc_pr: scale factors for the above components; taking sc<1 or sc>1 makes the currents shrink or expand, respectively.
1467
+ :param phi: the rotation angle (radians) of the partial ring current (measured from midnight toward dusk)
1468
+ :param ps: geo-dipole tilt angle in radius.
1469
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
1470
+ :return: bxsrc,bysrc,bzsrc, bxprc,byprc,bzprc. Field components in GSM system, in nT. For the symmetric part and partial ring current.
1471
+ """
1472
+
1473
+ # 1. transform to tilted coordinates (i.e., sm coordinates):
1474
+ cps=np.cos(ps)
1475
+ sps=np.sin(ps)
1476
+
1477
+ xt=x*cps-z*sps
1478
+ zt=z*cps+x*sps
1479
+
1480
+ # 2. scale the coordinates for the symmetric and partial rc components:
1481
+ xts=xt/sc_sy # symmetric
1482
+ yts=y /sc_sy
1483
+ zts=zt/sc_sy
1484
+
1485
+ xta=xt/sc_pr # partial
1486
+ yta=y /sc_pr
1487
+ zta=zt/sc_pr
1488
+
1489
+ # 3. calculate components of the total field in the tilted (solar-magnetic) coordinate system:
1490
+
1491
+ # only for least squares fitting:
1492
+ bxs,bys,bzs = [0.]*3
1493
+ bxa_s,bya_s,bza_s = [0.]*3
1494
+ bxa_qr,bya_qr,bza_q = [0.]*3
1495
+
1496
+ # 3a. symmetric field:
1497
+ if iopr <= 1:
1498
+ bxs,bys,bzs = rc_symm(xts,yts,zts)
1499
+ if (iopr == 0) | (iopr == 2):
1500
+ bxa_s,bya_s,bza_s = prc_symm(xta,yta,zta)
1501
+
1502
+ # 3b. rotate the scaled sm coordinates by phi around zsm axis and calculate quadrupole prc field in those coords:
1503
+ cp=np.cos(phi)
1504
+ sp=np.sin(phi)
1505
+ xr=xta*cp-yta*sp
1506
+ yr=xta*sp+yta*cp
1507
+ if (iopr == 0) | (iopr == 2):
1508
+ bxa_qr,bya_qr,bza_q = prc_quad(xr,yr,zta)
1509
+
1510
+ # 3c.transform the quadrupole field components back to the sm coords:
1511
+ bxa_q= bxa_qr*cp+bya_qr*sp
1512
+ bya_q=-bxa_qr*sp+bya_qr*cp
1513
+
1514
+ # 3d. find the total field of prc (symm.+quadr.) in the sm coords:
1515
+ bxp=bxa_s+bxa_q
1516
+ byp=bya_s+bya_q
1517
+ bzp=bza_s+bza_q
1518
+
1519
+ # 4. transform the fields of both parts of the ring current back to the gsm system:
1520
+ bxsrc=bxs*cps+bzs*sps # symmetric rc
1521
+ bysrc=bys
1522
+ bzsrc=bzs*cps-bxs*sps
1523
+
1524
+ bxprc=bxp*cps+bzp*sps # partial rc
1525
+ byprc=byp
1526
+ bzprc=bzp*cps-bxp*sps
1527
+
1528
+ return bxsrc,bysrc,bzsrc, bxprc,byprc,bzprc
1529
+
1530
+
1531
+ def rc_symm(x,y,z):
1532
+ """
1533
+ Calculates the field components from a model ring current, due to its symmetric part.
1534
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
1535
+ :return: bx,by,bz. Field components in GSM system, in nT.
1536
+ """
1537
+
1538
+ # ds=sin(theta) at the boundary of the linearity region; dc=sqrt(1-ds**2); drd=1/(2*d)
1539
+ ds = 1e-2
1540
+ dc = 0.99994999875
1541
+ d = 1e-4
1542
+ drd = 5e3
1543
+
1544
+ rho2=x**2+y**2
1545
+ r2=rho2+z**2
1546
+ r=np.sqrt(r2)
1547
+ rp=r+d
1548
+ rm=r-d
1549
+ sint=np.sqrt(rho2)/r
1550
+ cost=z/r
1551
+
1552
+ # too close to the z-axis; using a linear approximation a_phi~sint to avoid the singularity problem
1553
+ if sint < ds:
1554
+ a=ap(r,ds,dc)/ds
1555
+ dardr=(rp*ap(rp,ds,dc)-rm*ap(rm,ds,dc))*drd
1556
+ fxy=z*(2*a-dardr)/(r*r2)
1557
+ bx=fxy*x
1558
+ by=fxy*y
1559
+ bz=(2*a*cost**2+dardr*sint**2)/r
1560
+ else:
1561
+ theta=np.arctan2(sint,cost)
1562
+ tp=theta+d
1563
+ tm=theta-d
1564
+ sintp=np.sin(tp)
1565
+ sintm=np.sin(tm)
1566
+ costp=np.cos(tp)
1567
+ costm=np.cos(tm)
1568
+ br=(sintp*ap(r,sintp,costp)-sintm*ap(r,sintm,costm))/(r*sint)*drd
1569
+ bt=(rm*ap(rm,sint,cost)-rp*ap(rp,sint,cost))/r*drd
1570
+ fxy=(br+bt*cost/sint)/r
1571
+ bx=fxy*x
1572
+ by=fxy*y
1573
+ bz=br*cost-bt*sint
1574
+
1575
+ return bx, by, bz
1576
+
1577
+ def ap(r,sint,cost):
1578
+ """
1579
+ Calculates azimuthal component of the vector potential of the symmetric part of the model ring current.
1580
+ :param r:
1581
+ :param sint:
1582
+ :param cost:
1583
+ :return:
1584
+ """
1585
+
1586
+ # Updated 04/20/06 (nb#9, p.37)
1587
+ a1,a2,rrc1,dd1,rrc2,dd2,p1,r1,dr1,dla1,p2,r2,dr2,dla2,p3,r3,dr3 = [
1588
+ -456.5289941,375.9055332,4.274684950,2.439528329,3.367557287,
1589
+ 3.146382545,-0.2291904607,3.746064740,1.508802177,0.5873525737,
1590
+ 0.1556236119,4.993638842,3.324180497,0.4368407663,0.1855957207,
1591
+ 2.969226745,2.243367377]
1592
+
1593
+
1594
+ # indicates whether we are too close to the axis of symmetry, where the inversion of dipolar coordinates becomes inaccurate
1595
+ prox = False
1596
+ sint1=sint
1597
+ cost1=cost
1598
+ # too close to z-axis; use linear interpolation between sint=0 & sint=0.01
1599
+ if (sint1 < 1.e-2):
1600
+ sint1=1.e-2
1601
+ cost1=0.99994999875
1602
+ prox=True
1603
+
1604
+ alpha=sint1**2/r # r,theta -> alpha,gamma
1605
+ gamma=cost1/r**2
1606
+
1607
+ arg1=-((r-r1)/dr1)**2-(cost1/dla1)**2
1608
+ arg2=-((r-r2)/dr2)**2-(cost1/dla2)**2
1609
+ arg3=-((r-r3)/dr3)**2
1610
+
1611
+ if arg1 < -500: # to prevent "floating underflow" crashes
1612
+ dexp1=0.
1613
+ else:
1614
+ dexp1=np.exp(arg1)
1615
+
1616
+ if arg2 < -500: # to prevent "floating underflow" crashes
1617
+ dexp2=0.
1618
+ else:
1619
+ dexp2=np.exp(arg2)
1620
+
1621
+ if arg3 < -500: # to prevent "floating underflow" crashes
1622
+ dexp3=0.
1623
+ else:
1624
+ dexp3=np.exp(arg3)
1625
+
1626
+ # alpha -> alpha_s (deformed)
1627
+ alpha_s=alpha*(1+p1*dexp1+p2*dexp2+p3*dexp3)
1628
+ gamma_s=gamma
1629
+ gammas2=gamma_s**2
1630
+
1631
+ # alpha_s,gamma_s -> rs,sints,costs
1632
+ alsqh=alpha_s**2/2
1633
+ f=64/27*gammas2+alsqh**2
1634
+ q=(np.sqrt(f)+alsqh)**(1/3)
1635
+ c=q-4*gammas2**(1/3)/(3*q)
1636
+ if c < 0: c=0
1637
+ g=np.sqrt(c**2+4*gammas2**(1/3))
1638
+ rs=4/((np.sqrt(2*g-c)+np.sqrt(c))*(g+c))
1639
+ costs=gamma_s*rs**2
1640
+ sints=np.sqrt(1-costs**2)
1641
+ rhos=rs*sints
1642
+ rhos2=rhos**2
1643
+ zs=rs*costs
1644
+
1645
+ # TODO looks like this part is repetative.
1646
+ p=(rrc1+rhos)**2+zs**2+dd1**2
1647
+ xk2=4*rrc1*rhos/p
1648
+ xk=np.sqrt(xk2)
1649
+ xkrho12=xk*np.sqrt(rhos) # see nb#4, p.3
1650
+
1651
+ xk2s = 1-xk2
1652
+ dl = np.log(1/xk2s)
1653
+ elk = 1.38629436112 + xk2s*(0.09666344259+xk2s*(0.03590092383+xk2s*(0.03742563713+xk2s*0.01451196212))) \
1654
+ + dl*(0.5+xk2s*(0.12498593597+xk2s*(0.06880248576+xk2s*(0.03328355346+xk2s*0.00441787012))))
1655
+ ele = 1+xk2s*(0.44325141463+xk2s*(0.0626060122+xk2s*(0.04757383546+xk2s*0.01736506451))) \
1656
+ + dl*xk2s*(0.2499836831+xk2s*(0.09200180037+xk2s*(0.04069697526+xk2s*0.00526449639)))
1657
+ aphi1=((1-xk2*0.5)*elk-ele)/xkrho12
1658
+
1659
+
1660
+ p=(rrc2+rhos)**2+zs**2+dd2**2
1661
+ xk2=4*rrc2*rhos/p
1662
+ xk=np.sqrt(xk2)
1663
+ xkrho12=xk*np.sqrt(rhos) # see nb#4, p.3
1664
+
1665
+ xk2s = 1-xk2
1666
+ dl = np.log(1/xk2s)
1667
+ elk = 1.38629436112 + xk2s*(0.09666344259+xk2s*(0.03590092383+xk2s*(0.03742563713+xk2s*0.01451196212))) \
1668
+ + dl*(0.5+xk2s*(0.12498593597+xk2s*(0.06880248576+xk2s*(0.03328355346+xk2s*0.00441787012))))
1669
+ ele = 1+xk2s*(0.44325141463+xk2s*(0.0626060122+xk2s*(0.04757383546+xk2s*0.01736506451))) \
1670
+ + dl*xk2s*(0.2499836831+xk2s*(0.09200180037+xk2s*(0.04069697526+xk2s*0.00526449639)))
1671
+ aphi2=((1-xk2*0.5)*elk-ele)/xkrho12
1672
+
1673
+ ap=a1*aphi1+a2*aphi2
1674
+ if prox:
1675
+ ap=ap*sint/sint1 # linear interpolation, if too close to the z-axis
1676
+
1677
+ return ap
1678
+
1679
+
1680
+ def prc_symm(x,y,z):
1681
+ """
1682
+ Calculates the field components from a model ring current, due to a partial ring current.
1683
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
1684
+ :return: bx,by,bz. Field components in GSM system, in nT.
1685
+ """
1686
+
1687
+ # ds=sin(theta) at the boundary of the linearity region; dc=sqrt(1-ds**2); drd=1/(2*d)
1688
+ ds = 1e-2
1689
+ dc = 0.99994999875
1690
+ d = 1e-4
1691
+ drd = 5e3
1692
+
1693
+ rho2=x**2+y**2
1694
+ r2=rho2+z**2
1695
+ r=np.sqrt(r2)
1696
+ rp=r+d
1697
+ rm=r-d
1698
+ sint=np.sqrt(rho2)/r
1699
+ cost=z/r
1700
+
1701
+ # too close to the z-axis; using a linear approximation a_phi~sint to avoid the singularity problem
1702
+ if sint < ds:
1703
+ a=apprc(r,ds,dc)/ds
1704
+ dardr=(rp*apprc(rp,ds,dc)-rm*apprc(rm,ds,dc))*drd
1705
+ fxy=z*(2*a-dardr)/(r*r2)
1706
+ bx=fxy*x
1707
+ by=fxy*y
1708
+ bz=(2*a*cost**2+dardr*sint**2)/r
1709
+ else:
1710
+ theta=np.arctan2(sint,cost)
1711
+ tp=theta+d
1712
+ tm=theta-d
1713
+ sintp=np.sin(tp)
1714
+ sintm=np.sin(tm)
1715
+ costp=np.cos(tp)
1716
+ costm=np.cos(tm)
1717
+ br=(sintp*apprc(r,sintp,costp)-sintm*apprc(r,sintm,costm))/(r*sint)*drd
1718
+ bt=(rm*apprc(rm,sint,cost)-rp*apprc(rp,sint,cost))/r*drd
1719
+ fxy=(br+bt*cost/sint)/r
1720
+ bx=fxy*x
1721
+ by=fxy*y
1722
+ bz=br*cost-bt*sint
1723
+
1724
+ return bx, by, bz
1725
+
1726
+ def apprc(r,sint,cost):
1727
+ """
1728
+ Calculates azimuthal component of the vector potential of the symmetric part of the model partial ring current.
1729
+ :param r:
1730
+ :param sint:
1731
+ :param cost:
1732
+ :return:
1733
+ """
1734
+
1735
+ a1,a2,rrc1,dd1,rrc2,dd2,p1,alpha1,dal1,beta1,dg1,p2,alpha2,dal2,beta2,dg2,beta3,p3, \
1736
+ alpha3,dal3,beta4,dg3,beta5,q0,q1,alpha4,dal4,dg4,q2,alpha5,dal5,dg5,beta6,beta7 = [
1737
+ -80.11202281,12.58246758,6.560486035,1.930711037,3.827208119,
1738
+ .7789990504,.3058309043,.1817139853,.1257532909,3.422509402,
1739
+ .04742939676,-4.800458958,-.02845643596,.2188114228,2.545944574,
1740
+ .00813272793,.35868244,103.1601001,-.00764731187,.1046487459,
1741
+ 2.958863546,.01172314188,.4382872938,.01134908150,14.51339943,
1742
+ .2647095287,.07091230197,.01512963586,6.861329631,.1677400816,
1743
+ .04433648846,.05553741389,.7665599464,.7277854652]
1744
+
1745
+ prox=False
1746
+ sint1=sint
1747
+ cost1=cost
1748
+ # too close to z-axis; use linear interpolation between sint=0 & sint=0.01
1749
+ if (sint1 < 1.e-2):
1750
+ sint1=1.e-2
1751
+ cost1=0.99994999875
1752
+ prox=True
1753
+
1754
+ alpha=sint1**2/r # r,theta -> alpha,gamma
1755
+ gamma=cost1/r**2
1756
+
1757
+ arg1=-(gamma/dg1)**2
1758
+ arg2=-((alpha-alpha4)/dal4)**2-(gamma/dg4)**2
1759
+
1760
+ if arg1 < -500: # to prevent "floating underflow" crashes
1761
+ dexp1=0.
1762
+ else:
1763
+ dexp1=np.exp(arg1)
1764
+
1765
+ if arg2 < -500: # to prevent "floating underflow" crashes
1766
+ dexp2=0.
1767
+ else:
1768
+ dexp2=np.exp(arg2)
1769
+
1770
+ # alpha -> alpha_s (deformed)
1771
+ alpha_s = alpha*(1 + p1/(1+((alpha-alpha1)/dal1)**2)**beta1*dexp1
1772
+ + p2*(alpha-alpha2)/(1+((alpha-alpha2)/dal2)**2)**beta2/(1+(gamma/dg2)**2)**beta3
1773
+ + p3*(alpha-alpha3)**2/(1.+((alpha-alpha3)/dal3)**2)**beta4/(1+(gamma/dg3)**2)**beta5)
1774
+ # gamma -> gamma_s (deformed)
1775
+ gamma_s = gamma*(1 + q0 + q1*(alpha-alpha4)*dexp2
1776
+ + q2*(alpha-alpha5)/(1+((alpha-alpha5)/dal5)**2)**beta6/(1+(gamma/dg5)**2)**beta7)
1777
+
1778
+ gammas2 = gamma_s**2
1779
+
1780
+ # alpha_s,gamma_s -> rs,sints,costs
1781
+ alsqh=alpha_s**2/2.
1782
+ f=64./27.*gammas2+alsqh**2
1783
+ q=(np.sqrt(f)+alsqh)**(1/3)
1784
+ c=q-4.*gammas2**(1/3)/(3.*q)
1785
+ if c < 0: c=0
1786
+ g=np.sqrt(c**2+4*gammas2**(1/3))
1787
+ rs=4./((np.sqrt(2*g-c)+np.sqrt(c))*(g+c))
1788
+ costs=gamma_s*rs**2
1789
+ sints=np.sqrt(1-costs**2)
1790
+ rhos=rs*sints
1791
+ rhos2=rhos**2
1792
+ zs=rs*costs
1793
+
1794
+
1795
+ # TODO looks like this part is repetative.
1796
+ p=(rrc1+rhos)**2+zs**2+dd1**2
1797
+ xk2=4*rrc1*rhos/p
1798
+ xk=np.sqrt(xk2)
1799
+ xkrho12=xk*np.sqrt(rhos) # see nb#4, p.3
1800
+
1801
+ xk2s = 1-xk2
1802
+ dl = np.log(1/xk2s)
1803
+ elk = 1.38629436112 + xk2s*(0.09666344259+xk2s*(0.03590092383+xk2s*(0.03742563713+xk2s*0.01451196212))) \
1804
+ + dl*(0.5+xk2s*(0.12498593597+xk2s*(0.06880248576+xk2s*(0.03328355346+xk2s*0.00441787012))))
1805
+ ele = 1 + xk2s*(0.44325141463+xk2s*(0.0626060122+xk2s*(0.04757383546+xk2s*0.01736506451))) \
1806
+ + dl*xk2s*(0.2499836831+xk2s*(0.09200180037+xk2s*(0.04069697526+xk2s*0.00526449639)))
1807
+ aphi1=((1-xk2*0.5)*elk-ele)/xkrho12
1808
+
1809
+
1810
+ p=(rrc2+rhos)**2+zs**2+dd2**2
1811
+ xk2=4*rrc2*rhos/p
1812
+ xk=np.sqrt(xk2)
1813
+ xkrho12=xk*np.sqrt(rhos) # see nb#4, p.3
1814
+
1815
+ xk2s = 1-xk2
1816
+ dl = np.log(1/xk2s)
1817
+ elk = 1.38629436112 + xk2s*(0.09666344259+xk2s*(0.03590092383+xk2s*(0.03742563713+xk2s*0.01451196212))) \
1818
+ + dl*(0.5+xk2s*(0.12498593597+xk2s*(0.06880248576+xk2s*(0.03328355346+xk2s*0.00441787012))))
1819
+ ele = 1 + xk2s*(0.44325141463+xk2s*(0.0626060122+xk2s*(0.04757383546+xk2s*0.01736506451))) \
1820
+ + dl*xk2s*(0.2499836831+xk2s*(0.09200180037+xk2s*(0.04069697526+xk2s*0.00526449639)))
1821
+ aphi2=((1-xk2*0.5)*elk-ele)/xkrho12
1822
+
1823
+ apprc=a1*aphi1+a2*aphi2
1824
+ if prox:
1825
+ apprc=apprc*sint/sint1 # linear interpolation, if too close to the z-axis
1826
+
1827
+ return apprc
1828
+
1829
+
1830
+ def prc_quad(x,y,z):
1831
+ """
1832
+ Calculates components of the field from the "quadrupole" component of the partial ring current.
1833
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
1834
+ :return: bx,by,bz. Field components in GSM system, in nT.
1835
+ """
1836
+ d = 1e-4
1837
+ dd = 2e-4
1838
+ ds = 1e-2
1839
+ dc = 0.99994999875
1840
+
1841
+ rho2=x**2+y**2
1842
+ r=np.sqrt(rho2+z**2)
1843
+ rho=np.sqrt(rho2)
1844
+ sint=rho/r
1845
+ cost=z/r
1846
+ rp=r+d
1847
+ rm=r-d
1848
+
1849
+ if sint > ds:
1850
+ cphi=x/rho
1851
+ sphi=y/rho
1852
+ br=br_prc_q(r,sint,cost)
1853
+ bt=bt_prc_q(r,sint,cost)
1854
+ dbrr=(br_prc_q(rp,sint,cost)-br_prc_q(rm,sint,cost))/dd
1855
+ theta=np.arctan2(sint,cost)
1856
+ tp=theta+d
1857
+ tm=theta-d
1858
+ sintp=np.sin(tp)
1859
+ costp=np.cos(tp)
1860
+ sintm=np.sin(tm)
1861
+ costm=np.cos(tm)
1862
+ dbtt=(bt_prc_q(r,sintp,costp)-bt_prc_q(r,sintm,costm))/dd
1863
+ bx=sint*(br+(br+r*dbrr+dbtt)*sphi**2)+cost*bt
1864
+ by=-sint*sphi*cphi*(br+r*dbrr+dbtt)
1865
+ bz=(br*cost-bt*sint)*cphi
1866
+ else:
1867
+ st=ds
1868
+ ct=dc
1869
+ if z < 0: ct=-dc
1870
+ theta=np.arctan2(st,ct)
1871
+ tp=theta+d
1872
+ tm=theta-d
1873
+ sintp=np.sin(tp)
1874
+ costp=np.cos(tp)
1875
+ sintm=np.sin(tm)
1876
+ costm=np.cos(tm)
1877
+ br=br_prc_q(r,st,ct)
1878
+ bt=bt_prc_q(r,st,ct)
1879
+ dbrr=(br_prc_q(rp,st,ct)-br_prc_q(rm,st,ct))/dd
1880
+ dbtt=(bt_prc_q(r,sintp,costp)-bt_prc_q(r,sintm,costm))/dd
1881
+ fcxy=r*dbrr+dbtt
1882
+ bx=(br*(x**2+2.*y**2)+fcxy*y**2)/(r*st)**2+bt*cost
1883
+ by=-(br+fcxy)*x*y/(r*st)**2
1884
+ bz=(br*cost/st-bt)*x/r
1885
+
1886
+ return bx,by,bz
1887
+
1888
+ def br_prc_q(r,sint,cost):
1889
+ """
1890
+ Calculates the radial component of the "quadrupole" part of the model partial ring current.
1891
+ :param r:
1892
+ :param sint:
1893
+ :param cost:
1894
+ :return:
1895
+ """
1896
+
1897
+ a1 = -21.2666329
1898
+ a2 = 32.24527521
1899
+ a3 = -6.062894078
1900
+ a4 = 7.515660734
1901
+ a5 = 233.7341288
1902
+ a6 = -227.1195714
1903
+ a7 = 8.483233889
1904
+ a8 = 16.80642754
1905
+ a9 = -24.63534184
1906
+ a10 = 9.067120578
1907
+ a11 = -1.052686913
1908
+ a12 = -12.08384538
1909
+ a13 = 18.61969572
1910
+ a14 = -12.71686069
1911
+ a15 = 47017.35679
1912
+ a16 = -50646.71204
1913
+ a17 = 7746.058231
1914
+ a18 = 1.531069371
1915
+ xk1 = 2.318824273
1916
+ al1 = 0.1417519429
1917
+ dal1 = 0.6388013110e-02
1918
+ b1 = 5.303934488
1919
+ be1 = 4.213397467
1920
+ xk2 = 0.7955534018
1921
+ al2 = 0.1401142771
1922
+ dal2 = 0.2306094179e-01
1923
+ b2 = 3.462235072
1924
+ be2 = 2.568743010
1925
+ xk3 = 3.477425908
1926
+ xk4 = 1.922155110
1927
+ al3 = 0.1485233485
1928
+ dal3 = 0.2319676273e-01
1929
+ b3 = 7.830223587
1930
+ be3 = 8.492933868
1931
+ al4 = 0.1295221828
1932
+ dal4 = 0.01753008801
1933
+ dg1 = 0.01125504083
1934
+ al5 = 0.1811846095
1935
+ dal5 = 0.04841237481
1936
+ dg2 = 0.01981805097
1937
+ c1 = 6.557801891
1938
+ c2 = 6.348576071
1939
+ c3 = 5.744436687
1940
+ al6 = 0.2265212965
1941
+ dal6 = 0.1301957209
1942
+ drm = 0.5654023158
1943
+
1944
+ sint2=sint**2
1945
+ cost2=cost**2
1946
+ sc=sint*cost
1947
+ alpha=sint2/r
1948
+ gamma=cost/r**2
1949
+
1950
+ f,fa,fs = ffs(alpha,al1,dal1)
1951
+ d1=sc*f**xk1/((r/b1)**be1+1.)
1952
+ d2=d1*cost2
1953
+
1954
+ f,fa,fs = ffs(alpha,al2,dal2)
1955
+ d3=sc*fs**xk2/((r/b2)**be2+1.)
1956
+ d4=d3*cost2
1957
+
1958
+ f,fa,fs = ffs(alpha,al3,dal3)
1959
+ d5=sc*(alpha**xk3)*(fs**xk4)/((r/b3)**be3+1.)
1960
+ d6=d5*cost2
1961
+
1962
+ arga=((alpha-al4)/dal4)**2+1.
1963
+ argg=1.+(gamma/dg1)**2
1964
+
1965
+ d7=sc/arga/argg
1966
+ d8=d7/arga
1967
+ d9=d8/arga
1968
+ d10=d9/arga
1969
+
1970
+ arga=((alpha-al5)/dal5)**2+1.
1971
+ argg=1.+(gamma/dg2)**2
1972
+
1973
+ d11=sc/arga/argg
1974
+ d12=d11/arga
1975
+ d13=d12/arga
1976
+ d14=d13/arga
1977
+
1978
+ d15=sc/(r**4+c1**4)
1979
+ d16=sc/(r**4+c2**4)*cost2
1980
+ d17=sc/(r**4+c3**4)*cost2**2
1981
+
1982
+ f,fa,fs = ffs(alpha,al6,dal6)
1983
+ d18=sc*fs/(1.+((r-1.2)/drm)**2)
1984
+
1985
+ br_prc_q=a1*d1+a2*d2+a3*d3+a4*d4+a5*d5+a6*d6+a7*d7+a8*d8+a9*d9+ \
1986
+ a10*d10+a11*d11+a12*d12+a13*d13+a14*d14+a15*d15+a16*d16+a17*d17+a18*d18
1987
+
1988
+ return br_prc_q
1989
+
1990
+ def bt_prc_q(r,sint,cost):
1991
+ """
1992
+ Calculates the theta component of the "quadrupole" part of the model partial ring current.
1993
+
1994
+ :param r:
1995
+ :param sint:
1996
+ :param cost:
1997
+ :return:
1998
+ """
1999
+
2000
+ # all linear parameters here were multiplied by 0.1, so that they correspond to p_0=1 npa,
2001
+ # rather than the original value of 10 npa assumed in the biot-savart integral.
2002
+ a1 = 12.74640393
2003
+ a2 = -7.516393516
2004
+ a3 = -5.476233865
2005
+ a4 = 3.212704645
2006
+ a5 = -59.10926169
2007
+ a6 = 46.62198189
2008
+ a7 = -.01644280062
2009
+ a8 = 0.1234229112
2010
+ a9 = -.08579198697
2011
+ a10 = 0.01321366966
2012
+ a11 = 0.8970494003
2013
+ a12 = 9.136186247
2014
+ a13 = -38.19301215
2015
+ a14 = 21.73775846
2016
+ a15 = -410.0783424
2017
+ a16 = -69.90832690
2018
+ a17 = -848.8543440
2019
+ xk1 = 1.243288286
2020
+ al1 = 0.2071721360
2021
+ dal1 = 0.05030555417
2022
+ b1 = 7.471332374
2023
+ be1 = 3.180533613
2024
+ xk2 = 1.376743507
2025
+ al2 = 0.1568504222
2026
+ dal2 = 0.02092910682
2027
+ be2 = 1.985148197
2028
+ xk3 = 0.3157139940
2029
+ xk4 = 1.056309517
2030
+ al3 = 0.1701395257
2031
+ dal3 = 0.1019870070
2032
+ b3 = 6.293740981
2033
+ be3 = 5.671824276
2034
+ al4 = 0.1280772299
2035
+ dal4 = 0.02189060799
2036
+ dg1 = 0.01040696080
2037
+ al5 = 0.1648265607
2038
+ dal5 = 0.04701592613
2039
+ dg2 = 0.01526400086
2040
+ c1 = 12.88384229
2041
+ c2 = 3.361775101
2042
+ c3 = 23.44173897
2043
+
2044
+ sint2=sint**2
2045
+ cost2=cost**2
2046
+ sc=sint*cost
2047
+ alpha=sint2/r
2048
+ gamma=cost/r**2
2049
+
2050
+ f,fa,fs = ffs(alpha,al1,dal1)
2051
+ d1=f**xk1/((r/b1)**be1+1.)
2052
+ d2=d1*cost2
2053
+
2054
+ f,fa,fs = ffs(alpha,al2,dal2)
2055
+ d3=fa**xk2/r**be2
2056
+ d4=d3*cost2
2057
+
2058
+ f,fa,fs = ffs(alpha,al3,dal3)
2059
+ d5=fs**xk3*alpha**xk4/((r/b3)**be3+1.)
2060
+ d6=d5*cost2
2061
+
2062
+ f,fa,fs = ffs(gamma,0.,dg1)
2063
+ fcc=(1.+((alpha-al4)/dal4)**2)
2064
+ d7 =1./fcc*fs
2065
+ d8 =d7/fcc
2066
+ d9 =d8/fcc
2067
+ d10=d9/fcc
2068
+
2069
+ arg=1.+((alpha-al5)/dal5)**2
2070
+ d11=1./arg/(1.+(gamma/dg2)**2)
2071
+ d12=d11/arg
2072
+ d13=d12/arg
2073
+ d14=d13/arg
2074
+
2075
+ d15=1./(r**4+c1**2)
2076
+ d16=cost2/(r**4+c2**2)
2077
+ d17=cost2**2/(r**4+c3**2)
2078
+
2079
+ bt_prc_q = a1*d1+a2*d2+a3*d3+a4*d4+a5*d5+a6*d6+a7*d7+a8*d8+a9*d9+ \
2080
+ a10*d10+a11*d11+a12*d12+a13*d13+a14*d14+a15*d15+a16*d16+a17*d17
2081
+
2082
+ return bt_prc_q
2083
+
2084
+ def ffs(a, a0, da):
2085
+ sq1 = np.sqrt((a + a0) ** 2 + da ** 2)
2086
+ sq2 = np.sqrt((a - a0) ** 2 + da ** 2)
2087
+ fa = 2. / (sq1 + sq2)
2088
+ f = fa * a
2089
+ fs = 0.5 * (sq1 + sq2) / (sq1 * sq2) * (1.-f * f)
2090
+
2091
+ return f, fa, fs
2092
+
2093
+
2094
+ def rc_shield(a,ps,x_sc,x,y,z):
2095
+ """
2096
+ B due to the ring current shield.
2097
+ :param a: coefficient.
2098
+ :param ps: geo-dipole tilt angle in radius.
2099
+ :param x_sc: scaling factors.
2100
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
2101
+ :return: bx,by,bz. Field components in GSM system, in nT.
2102
+ """
2103
+
2104
+ fac_sc = (x_sc+1)**3
2105
+
2106
+ cps = np.cos(ps)
2107
+ sps = np.sin(ps)
2108
+
2109
+ s3ps=2*cps
2110
+
2111
+ pst1=ps*a[84]
2112
+ pst2=ps*a[85]
2113
+
2114
+ st1=np.sin(pst1)
2115
+ ct1=np.cos(pst1)
2116
+ st2=np.sin(pst2)
2117
+ ct2=np.cos(pst2)
2118
+
2119
+ x1=x*ct1-z*st1
2120
+ z1=x*st1+z*ct1
2121
+ x2=x*ct2-z*st2
2122
+ z2=x*st2+z*ct2
2123
+
2124
+ l=0
2125
+ [bx,by,bz] = [0.]*3
2126
+
2127
+ for m in range(2): # m=1 is for the 1st sum ("perp." symmetry) and m=2 is for the second sum ("parall." symmetry)
2128
+ for i in range(3):
2129
+ p=a[72+i]
2130
+ q=a[78+i]
2131
+ cypi=np.cos(y/p)
2132
+ cyqi=np.cos(y/q)
2133
+ sypi=np.sin(y/p)
2134
+ syqi=np.sin(y/q)
2135
+
2136
+ for k in range(3):
2137
+ r=a[75+k]
2138
+ s=a[81+k]
2139
+ szrk=np.sin(z1/r)
2140
+ czsk=np.cos(z2/s)
2141
+ czrk=np.cos(z1/r)
2142
+ szsk=np.sin(z2/s)
2143
+ sqpr=np.sqrt(1/p**2+1/r**2)
2144
+ sqqs=np.sqrt(1/q**2+1/s**2)
2145
+ epr=np.exp(x1*sqpr)
2146
+ eqs=np.exp(x2*sqqs)
2147
+
2148
+ for n in range(2): # n=1 is for the first part of each coefficient and n=2 is for the second one
2149
+ for nn in range(2): # nn = 1,2 further splits the coefficients into 2 parts, to take into account the scale factor dependence
2150
+ if m == 0:
2151
+ fx = -sqpr*epr*cypi*szrk*fac_sc
2152
+ fy = epr*sypi*szrk/p *fac_sc
2153
+ fz = -epr*cypi*czrk/r *fac_sc
2154
+ if n == 0:
2155
+ if nn == 0:
2156
+ [hx,hy,hz] = [fx,fy,fz]
2157
+ else:
2158
+ [hx,hy,hz] = [fx*x_sc, fy*x_sc, fz*x_sc]
2159
+ else:
2160
+ if nn == 0:
2161
+ [hx,hy,hz] = [fx*cps, fy*cps, fz*cps]
2162
+ else:
2163
+ [hx,hy,hz] = [fx*cps*x_sc, fy*cps*x_sc, fz*cps*x_sc]
2164
+ else: # m == 2
2165
+ fx = -sps*sqqs*eqs*cyqi*czsk*fac_sc
2166
+ fy = sps/q*eqs*syqi*czsk *fac_sc
2167
+ fz = sps/s*eqs*cyqi*szsk *fac_sc
2168
+ if n == 0:
2169
+ if nn == 0:
2170
+ [hx,hy,hz] = [fx,fy,fz]
2171
+ else:
2172
+ [hx,hy,hz] = [fx*x_sc,fy*x_sc,fz*x_sc]
2173
+ else:
2174
+ if nn == 0:
2175
+ [hx,hy,hz] = [fx*s3ps,fy*s3ps,fz*s3ps]
2176
+ else:
2177
+ [hx,hy,hz] = [fx*s3ps*x_sc, fy*s3ps*x_sc, fz*s3ps*x_sc]
2178
+
2179
+ if m == 0:
2180
+ hxr = hx*ct1+hz*st1
2181
+ hzr = -hx*st1+hz*ct1
2182
+ else:
2183
+ hxr = hx*ct2+hz*st2
2184
+ hzr = -hx*st2+hz*ct2
2185
+
2186
+ bx = bx+hxr*a[l]
2187
+ by = by+hy *a[l]
2188
+ bz = bz+hzr*a[l]
2189
+ l=l+1
2190
+
2191
+ return bx, by, bz
2192
+
2193
+
2194
+ def dipole(ps, x,y,z):
2195
+ """
2196
+ Calculates GSM components of a geo-dipole field with the dipole moment corresponding to the epoch of 2000.
2197
+
2198
+ :param ps: geo-dipole tilt angle in radius.
2199
+ :param x,y,z: GSM coordinates in Re (1 Re = 6371.2 km)
2200
+ :return: bx,by,bz. Field components in GSM system, in nT.
2201
+ """
2202
+
2203
+ sps = np.sin(ps)
2204
+ cps = np.cos(ps)
2205
+
2206
+ p = x**2
2207
+ u = z**2
2208
+ v = 3*z*x
2209
+ t = y**2
2210
+ q = 30115./np.sqrt(p+t+u)**5
2211
+ bx = q*((t+u-2*p)*sps-v*cps)
2212
+ by = -3*y*q*(x*sps+z*cps)
2213
+ bz = q*((p+t-2*u)*cps-v*sps)
2214
+
2215
+ return bx,by,bz