Vlasiator ebf0dd394 on dev (v5.4.0 + 1054 commits)
Loading...
Searching...
No Matches
fieldmodels.py
Go to the documentation of this file.
1#!/usr/bin/env python import matplotlib.pyplot as plt
2
3# /*
4# * This file is part of Vlasiator.
5# * Copyright 2010-2016 Finnish Meteorological Institute
6# * Copyright 2017-2019 University of Helsinki
7# *
8# * For details of usage, see the COPYING file and read the "Rules of the Road"
9# * at http://www.physics.helsinki.fi/vlasiator/
10# *
11# * This program is free software; you can redistribute it and/or modify
12# * it under the terms of the GNU General Public License as published by
13# * the Free Software Foundation; either version 2 of the License, or
14# * (at your option) any later version.
15# *
16# * This program is distributed in the hope that it will be useful,
17# * but WITHOUT ANY WARRANTY; without even the implied warranty of
18# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# * GNU General Public License for more details.
20# *
21# * You should have received a copy of the GNU General Public License along
22# * with this program; if not, write to the Free Software Foundation, Inc.,
23# * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24# */
25import numpy as np
26import math
27
28''' Testing routine for different dipole formulations
29 Call this module from other testing / plotting routines
30
31'''
32
33RE=6371000.
34class dipole(object):
35 ''' Class generating dipole fields
36 '''
37
38 moment_base = 8.e15
39
40 #RE=6371000.
41 minimumR=1e-3*RE
42
43 def __init__(self, centerx, centery, centerz, tilt_phi, tilt_theta, mult=1.0, radius_f=None, radius_z=None):
44 self.radius = np.zeros(2)# // Radial extents of full and zero dipole
45
46 self.q = np.zeros(3)# // Dipole moment# set to (0,0,moment) for z-aligned
47 self.center = np.zeros(3)# // Coordinates where the dipole sits# set to (0,0,0)
48
49 self.center[0]=centerx
50 self.center[1]=centery
51 self.center[2]=centerz
52 self.tilt_angle_phi = tilt_phi * math.pi/180.
53 self.tilt_angle_theta = tilt_theta * math.pi/180.
54 self.moment = mult*self.moment_base
55 self.q[0]=-np.sin(self.tilt_angle_phi)*np.cos(self.tilt_angle_theta)*self.moment
56 self.q[1]=-np.sin(self.tilt_angle_phi)*np.sin(self.tilt_angle_theta)*self.moment
57 self.q[2]=-np.cos(self.tilt_angle_phi)*self.moment
58
59 if radius_f is not None:
60 self.radius[0]=radius_f*RE
61 else:
62 self.radius[0]=10.*RE
63 if radius_z is not None:
64 self.radius[1]=radius_z*RE
65 else:
66 self.radius[1]=40.*RE
67
68 def set_dipole(self, centerx, centery, centerz, tilt_phi, tilt_theta, mult=1.0, radius_f=None, radius_z=None):
69 self.center[0]=centerx
70 self.center[1]=centery
71 self.center[2]=centerz
72 self.tilt_angle_phi = tilt_phi * math.pi/180.
73 self.tilt_angle_theta = tilt_theta * math.pi/180.
74 self.moment = mult*self.moment_base
75 self.q[0]=-np.sin(self.tilt_angle_phi)*np.cos(self.tilt_angle_theta)*self.moment
76 self.q[1]=-np.sin(self.tilt_angle_phi)*np.sin(self.tilt_angle_theta)*self.moment
77 self.q[2]=-np.cos(self.tilt_angle_phi)*self.moment
78 if radius_f is not None:
79 self.radius[0]=radius_f*RE
80 if radius_z is not None:
81 self.radius[1]=radius_z*RE
82
83 def get_old(self, x,y,z,derivative,fComponent,dComponent):
84 r = np.zeros(3)
85 r[0]= x-self.center[0]
86 r[1]= y-self.center[1]
87 r[2]= z-self.center[2]
88 r2 = r[0]*r[0]+r[1]*r[1]+r[2]*r[2]
89 if(r2<self.minimumR*self.minimumR):
90 # r2=self.minimumR*self.minimumR;
91 return 0.0 # set zero field inside dipole
92
93 r5 = (r2*r2*np.sqrt(r2))
94 rdotq=self.q[0]*r[0] + self.q[1]*r[1] +self.q[2]*r[2]
95 B=( 3*r[fComponent]*rdotq-self.q[fComponent]*r2)/r5
96
97 if(derivative == 0):
98
99 return B
100 elif(derivative == 1):
101 #//first derivatives
102 if(dComponent==fComponent):
103 sameComponent=1
104 else:
105 sameComponent=0
106
107 return -5*B*r[dComponent]/r2+(3*self.q[dComponent]*r[fComponent] - 2*self.q[fComponent]*r[dComponent] + 3*rdotq*sameComponent)/r5
108 print("ERROR")
109 return 0#; // dummy, but prevents gcc from yelling
110
111 def get_ldp(self, x,y,z,derivative,fComponent,dComponent):
112 r = np.zeros(3)
113 r[0]= x-self.center[0]
114 r[1]= y-self.center[1]
115 r[2]= z-self.center[2]
116 r2 = r[0]*r[0]+r[2]*r[2]
117 if(r2<self.minimumR*self.minimumR):
118 # r2=self.minimumR*self.minimumR;
119 return 0.0 # set zero field inside dipole
120
121 r6 = (r2*r2*r2)
122 D = self.q[2] * 126.2e6 / 8e15
123 DerivativeSameComponent=D*( 2*r[2]*(r[2]*r[2]-3*r[0]*r[0]))/r6
124 DerivativeDiffComponent=D*( 2*r[0]*(r[0]*r[0]-3*r[2]*r[2]))/r6
125
126 if(derivative == 0):
127 if(fComponent == 0):
128 return D*2*r[0]*r[2]/(r2*r2)
129 if(fComponent == 2):
130 return D*(r[2]*r[2]-r[0]*r[0])/(r2*r2)
131 if(fComponent == 1):
132 return 0
133 elif(derivative == 1):
134 if(dComponent== 1 or fComponent==1):
135 return 0
136 elif(dComponent==fComponent):
137 if(fComponent == 0):
138 return DerivativeSameComponent
139 elif(fComponent == 2):
140 return -DerivativeSameComponent
141 else:
142 return DerivativeDiffComponent
143 print("ERROR")
144 return 0#; // dummy, but prevents gcc from yelling
145
146 def get(self, x,y,z,derivative,fComponent,dComponent):
147
148 r = np.zeros(3)
149 r[0]= x-self.center[0]
150 r[1]= y-self.center[1]
151 r[2]= z-self.center[2]
152
153 r2 = r[0]*r[0]+r[1]*r[1]+r[2]*r[2]
154
155 if(r2<self.minimumR*self.minimumR):
156 # r2=self.minimumR*self.minimumR
157 return 0.0# #set zero field inside dipole
158
159 if(r2>=self.radius[1]*self.radius[1]):
160 return 0.0# #set zero field and derivatives outside "zero radius"
161
162 # /* This function is called from within other calls, one component at a time.
163 # The component in question is defined using the fComponent index. If a derivative
164 # is requested, the direction of the derivative is defined using dComponent. */
165
166 r1 = np.sqrt(r2)
167 r5 = (r2*r2*r1)
168 rdotq=self.q[0]*r[0] + self.q[1]*r[1] +self.q[2]*r[2]
169 B=( 3*r[fComponent]*rdotq-self.q[fComponent]*r2)/r5
170
171 if(derivative == 0) and (r1 <= self.radius[0]):
172 # Full dipole field within full radius
173 return B
174
175 if(derivative == 1) and (r1 <= self.radius[0]):
176 #first derivatives of full field
177 if(dComponent==fComponent):
178 sameComponent=1
179 else:
180 sameComponent=0
181
182 # /* Confirmed Battarbee 26.04.2019: This is the correct
183 # 3D dipole derivative. */
184 return -5*B*r[dComponent]/r2+(3*self.q[dComponent]*r[fComponent] - 2*self.q[fComponent]*r[dComponent] + 3*rdotq*sameComponent)/r5
185
186 # /* Within transition range (between "full radius" and "zero radius"), use
187 # a vector potential scaled with the smootherstep function. Calculated
188 # and coded by Markus Battarbee, 30.04.2019 */
189
190 # Calculate vector potential within transition range
191 A=np.zeros(3)
192 A[0] = (self.q[1]*r[2]-self.q[2]*r[1]) / (r2*r1)#
193 A[1] = (self.q[2]*r[0]-self.q[0]*r[2]) / (r2*r1)#
194 A[2] = (self.q[0]*r[1]-self.q[1]*r[0]) / (r2*r1)#
195 # Coordinate within smootherstep function
196 Sx = -(r1-self.radius[1])/(self.radius[1]-self.radius[0])
197 Sx2 = Sx*Sx
198 # Smootherstep and its radial derivative
199 S2 = 6.*Sx2*Sx2*Sx - 15.*Sx2*Sx2 + 10.*Sx2*Sx
200 dS2dr = -(30.*Sx2*Sx2 - 60.*Sx2*Sx + 30.*Sx2)/(self.radius[1]-self.radius[0])
201
202 # Alternatively, smoothstep (does not look good!)
203 #S2 = 3.*Sx2 - 2.*Sx2*Sx
204 #dS2dr = -(6.*Sx - 6.*Sx2)/(radius[1]-radius[0])
205
206 #print("r",r1,"Sx",Sx,"S2",S2)
207
208 # Cartesian derivatives of S2
209 dS2cart=np.zeros(3)
210 dS2cart[0] = (r[0]/r1)*dS2dr
211 dS2cart[1] = (r[1]/r1)*dS2dr
212 dS2cart[2] = (r[2]/r1)*dS2dr#
213 #print("r",r1,"S2",S2,"dSdx",dS2cart[0],"dSdy",dS2cart[1],"dSdz",dS2cart[2])
214
215 if(derivative == 0) and (r1 > self.radius[0]):
216 # /* Within transition range (between radius[0] and radius[1]) we
217 # multiply the magnetic field with the S2 smootherstep function
218 # and add an additional corrective term to remove divergence. This
219 # is based on using the dipole field vector potential and scaling
220 # it using the smootherstep function S2.
221
222 # Notation:
223 # q = dipole moment (vector)
224 # r = position vector
225 # R = position distance
226
227 # The regular dipole field vector potential
228 # A(r) = (mu0/4 pi R^3) * (q cross r)
229
230 # The smootherstep function
231 # ( 0, Sx<=0
232 # S2(Sx) = ( 6 Sx^5 -15 Sx^4 +10 Sx^3, 0<=Sx<=1
233 # ( 1, Sx>=1
234
235 # Radial distance scaling for S2
236 # Sx = -(R-radius[1])/(radius[1]-radius[0])
237
238 # The scaled vector potential is A'(r) = A(r)*S2(Sx)
239
240 # The scaled magnetic field is
241 # B'(r) = del cross A'(r)
242 # =(NRL)= S2(Sx) del cross A(r) + del S2(Sx) cross A(r)
243 # = S2(Sx) B(r) + del S2(Sx) cross A(r)
244
245 # */
246 delS2crossA=np.zeros(3)
247 delS2crossA[0] = dS2cart[1]*A[2] - dS2cart[2]*A[1]
248 delS2crossA[1] = dS2cart[2]*A[0] - dS2cart[0]*A[2]
249 delS2crossA[2] = dS2cart[0]*A[1] - dS2cart[1]*A[0]
250
251 return S2*B + delS2crossA[fComponent]
252
253 elif(derivative == 1) and (r1 > self.radius[0]):
254 # /* first derivatives of field calculated from diminishing vector potential
255
256 # del B'(r) = S2(Sx) del B(r) + B(r) del S2(Sx) + del (del S2(Sx) cross A(r))
257
258 # component-wise:
259
260 # del Bx = S2(Sx) del Bx + del S2(Sx) Bx + del(del S2(Sx) cross A)@i=x
261 # del By = S2(Sx) del By + del S2(Sx) By + del(del S2(Sx) cross A)@i=y
262 # del Bz = S2(Sx) del Bz + del S2(Sx) Bz + del(del S2(Sx) cross A)@i=z
263
264 # where
265
266 # del(del S2(Sx) cross A)@i=x = del (dS2/dy Az - dS/dz Ay)
267 # = del(dS/dy) Az + dS/dy del Az - del(DS/dz) Ay - dS/dz del Ay
268
269 # del(del S2(Sx) cross A)@i=y = del (dS2/dz Ax - dS/dx Az)
270 # = del(dS/dz) Ax + dS/dz del Ax - del(DS/dx) Az - dS/dx del Az
271
272 # del(del S2(Sx) cross A)@i=z = del (dS2/dx Ay - dS/dy Ax)
273 # = del(dS/dx) Ay + dS/dx del Ay - del(DS/dy) Ax - dS/dy del Ax
274
275
276 # **********/
277
278 if(dComponent==fComponent):
279 sameComponent=1
280 else:
281 sameComponent=0
282
283 # Regular derivative of B
284 delB = -5*B*r[dComponent]/r2 + (3*self.q[dComponent]*r[fComponent] - 2*self.q[fComponent]*r[dComponent] + 3*rdotq*sameComponent)/r5
285
286 # Calculate del Ax, del Ay, del Az
287 delAx=np.zeros(3)
288 delAy=np.zeros(3)
289 delAz=np.zeros(3)
290 delAx[0] = (-3./(r2*r2*r1))*(self.q[1]*r[2]-self.q[2]*r[1])*r[0]
291 delAx[1] = (-3./(r2*r2*r1))*(self.q[1]*r[2]-self.q[2]*r[1])*r[1] -self.q[2]/(r2*r1)
292 delAx[2] = (-3./(r2*r2*r1))*(self.q[1]*r[2]-self.q[2]*r[1])*r[2] +self.q[1]/(r2*r1)
293 delAy[0] = (-3./(r2*r2*r1))*(self.q[2]*r[0]-self.q[0]*r[2])*r[0] +self.q[2]/(r2*r1)
294 delAy[1] = (-3./(r2*r2*r1))*(self.q[2]*r[0]-self.q[0]*r[2])*r[1]
295 delAy[2] = (-3./(r2*r2*r1))*(self.q[2]*r[0]-self.q[0]*r[2])*r[2] -self.q[0]/(r2*r1)
296 delAz[0] = (-3./(r2*r2*r1))*(self.q[0]*r[1]-self.q[1]*r[0])*r[0] -self.q[1]/(r2*r1)
297 delAz[1] = (-3./(r2*r2*r1))*(self.q[0]*r[1]-self.q[1]*r[0])*r[1] +self.q[0]/(r2*r1)
298 delAz[2] = (-3./(r2*r2*r1))*(self.q[0]*r[1]-self.q[1]*r[0])*r[2]
299
300 ddidS2dr = 60.*(2.*Sx2*Sx - 3.*Sx2 + Sx)/(r2*(self.radius[1]-self.radius[0])*(self.radius[1]-self.radius[0]))
301
302 # Calculate del (dS2/dx), del (dS2/dy), del (dS2/dz)
303 deldS2dx=np.zeros(3)
304 deldS2dy=np.zeros(3)
305 deldS2dz=np.zeros(3)
306 deldS2dx[0] = ddidS2dr*r[0]*r[0] -(r[0]/(r2*r1))*dS2dr*r[0] + dS2dr/r1
307 deldS2dx[1] = ddidS2dr*r[0]*r[1] -(r[0]/(r2*r1))*dS2dr*r[1]
308 deldS2dx[2] = ddidS2dr*r[0]*r[2] -(r[0]/(r2*r1))*dS2dr*r[2]
309 deldS2dy[0] = ddidS2dr*r[1]*r[0] -(r[1]/(r2*r1))*dS2dr*r[0]
310 deldS2dy[1] = ddidS2dr*r[1]*r[1] -(r[1]/(r2*r1))*dS2dr*r[1] + dS2dr/r1
311 deldS2dy[2] = ddidS2dr*r[1]*r[2] -(r[1]/(r2*r1))*dS2dr*r[2]
312 deldS2dz[0] = ddidS2dr*r[2]*r[0] -(r[2]/(r2*r1))*dS2dr*r[0]
313 deldS2dz[1] = ddidS2dr*r[2]*r[1] -(r[2]/(r2*r1))*dS2dr*r[1]
314 deldS2dz[2] = ddidS2dr*r[2]*r[2] -(r[2]/(r2*r1))*dS2dr*r[2] + dS2dr/r1
315
316 # Calculate del(del S2(Sx) cross A)@i=x, del(del S2(Sx) cross A)@i=y, del(del S2(Sx) cross A)@i=z
317 ddS2crossA=np.zeros([3,3])
318 # derivatives of X-directional field
319 ddS2crossA[0][0] = deldS2dy[0]*A[2] + dS2cart[1]*delAz[0] - deldS2dz[0]*A[1] - dS2cart[2]*delAy[0]
320 ddS2crossA[0][1] = deldS2dy[1]*A[2] + dS2cart[1]*delAz[1] - deldS2dz[1]*A[1] - dS2cart[2]*delAy[1]
321 ddS2crossA[0][2] = deldS2dy[2]*A[2] + dS2cart[1]*delAz[2] - deldS2dz[2]*A[1] - dS2cart[2]*delAy[2]
322 # derivatives of Y-directional field
323 ddS2crossA[1][0] = deldS2dz[0]*A[0] + dS2cart[2]*delAx[0] - deldS2dx[0]*A[2] - dS2cart[0]*delAz[0]
324 ddS2crossA[1][1] = deldS2dz[1]*A[0] + dS2cart[2]*delAx[1] - deldS2dx[1]*A[2] - dS2cart[0]*delAz[1]
325 ddS2crossA[1][2] = deldS2dz[2]*A[0] + dS2cart[2]*delAx[2] - deldS2dx[2]*A[2] - dS2cart[0]*delAz[2]
326 # derivatives of Z-directional field
327 ddS2crossA[2][0] = deldS2dx[0]*A[1] + dS2cart[0]*delAy[0] - deldS2dy[0]*A[0] - dS2cart[1]*delAx[0]
328 ddS2crossA[2][1] = deldS2dx[1]*A[1] + dS2cart[0]*delAy[1] - deldS2dy[1]*A[0] - dS2cart[1]*delAx[1]
329 ddS2crossA[2][2] = deldS2dx[2]*A[1] + dS2cart[0]*delAy[2] - deldS2dy[2]*A[0] - dS2cart[1]*delAx[2]
330
331 return S2*delB + dS2cart[dComponent]*B + ddS2crossA[fComponent][dComponent]
332
333 print("ERROR")
334 return 0 # dummy, but prevents gcc from yelling
335
336 def getX(self, x,y,z,derivative,fComponent,dComponent):
337 r = np.zeros(3)
338 r[0]= x-self.center[0]
339 r[1]= y-self.center[1]
340 r[2]= z-self.center[2]
341
342 r2 = r[0]*r[0]+r[1]*r[1]+r[2]*r[2]
343
344 if(r2<self.minimumR*self.minimumR):
345 # r2=self.minimumR*self.minimumR
346 return 0.0# #set zero field inside dipole
347
348 if(x>=self.radius[1]):
349 return 0.0# #set zero field and derivatives outside "zero radius"
350
351 # /* This function is called from within other calls, one component at a time.
352 # The component in question is defined using the fComponent index. If a derivative
353 # is requested, the direction of the derivative is defined using dComponent. */
354
355 r1 = np.sqrt(r2)
356 r5 = (r2*r2*r1)
357 rdotq=self.q[0]*r[0] + self.q[1]*r[1] +self.q[2]*r[2]
358 B=( 3*r[fComponent]*rdotq-self.q[fComponent]*r2)/r5
359
360 if(derivative == 0) and (x <= self.radius[0]):
361 # Full dipole field within full radius
362 return B
363
364 if(derivative == 1) and (x <= self.radius[0]):
365 #first derivatives of full field
366 if(dComponent==fComponent):
367 sameComponent=1
368 else:
369 sameComponent=0
370
371 # /* Confirmed Battarbee 26.04.2019: This is the correct
372 # 3D dipole derivative. */
373 return -5*B*r[dComponent]/r2+(3*self.q[dComponent]*r[fComponent] - 2*self.q[fComponent]*r[dComponent] + 3*rdotq*sameComponent)/r5
374
375 # /* Within transition range (between "full radius" and "zero radius"), use
376 # a vector potential scaled with the smootherstep function. Calculated
377 # and coded by Markus Battarbee, 30.04.2019 */
378
379 # Calculate vector potential within transition range
380 A=np.zeros(3)
381 A[0] = (self.q[1]*r[2]-self.q[2]*r[1]) / (r2*r1)#
382 A[1] = (self.q[2]*r[0]-self.q[0]*r[2]) / (r2*r1)#
383 A[2] = (self.q[0]*r[1]-self.q[1]*r[0]) / (r2*r1)#
384 # Coordinate within smootherstep function
385 Sx = -(x-self.radius[1])/(self.radius[1]-self.radius[0])
386 Sx2 = Sx*Sx
387 # Smootherstep and its radial derivative
388 S2 = 6.*Sx2*Sx2*Sx - 15.*Sx2*Sx2 + 10.*Sx2*Sx
389 dS2dr = -(30.*Sx2*Sx2 - 60.*Sx2*Sx + 30.*Sx2)/(self.radius[1]-self.radius[0])
390
391 # Alternatively, smoothstep (does not look good!)
392 #S2 = 3.*Sx2 - 2.*Sx2*Sx
393 #dS2dr = -(6.*Sx - 6.*Sx2)/(radius[1]-radius[0])
394
395 #print("r",r1,"Sx",Sx,"S2",S2)
396
397 # Cartesian derivatives of S2
398 dS2cart=np.zeros(3)
399 dS2cart[0] = dS2dr #(r[0]/r1)*dS2dr
400 dS2cart[1] = 0.#(r[1]/r1)*dS2dr
401 dS2cart[2] = 0.#(r[2]/r1)*dS2dr#
402 #print("r",r1,"S2",S2,"dSdx",dS2cart[0],"dSdy",dS2cart[1],"dSdz",dS2cart[2])
403
404 if(derivative == 0) and (x > self.radius[0]):
405 # /* Within transition range (between radius[0] and radius[1]) we
406 # multiply the magnetic field with the S2 smootherstep function
407 # and add an additional corrective term to remove divergence. This
408 # is based on using the dipole field vector potential and scaling
409 # it using the smootherstep function S2.
410
411 # Notation:
412 # q = dipole moment (vector)
413 # r = position vector
414 # R = position distance
415
416 # The regular dipole field vector potential
417 # A(r) = (mu0/4 pi R^3) * (q cross r)
418
419 # The smootherstep function
420 # ( 0, Sx<=0
421 # S2(Sx) = ( 6 Sx^5 -15 Sx^4 +10 Sx^3, 0<=Sx<=1
422 # ( 1, Sx>=1
423
424 # Radial distance scaling for S2
425 # Sx = -(R-radius[1])/(radius[1]-radius[0])
426
427 # The scaled vector potential is A'(r) = A(r)*S2(Sx)
428
429 # The scaled magnetic field is
430 # B'(r) = del cross A'(r)
431 # =(NRL)= S2(Sx) del cross A(r) + del S2(Sx) cross A(r)
432 # = S2(Sx) B(r) + del S2(Sx) cross A(r)
433
434 # */
435 delS2crossA=np.zeros(3)
436 delS2crossA[0] = 0.#dS2cart[1]*A[2] - dS2cart[2]*A[1]
437 delS2crossA[1] = - dS2cart[0]*A[2] #dS2cart[2]*A[0] - dS2cart[0]*A[2]
438 delS2crossA[2] = dS2cart[0]*A[1] #- dS2cart[1]*A[0]
439
440 return S2*B + delS2crossA[fComponent]
441
442 elif(derivative == 1) and (x > self.radius[0]):
443 # /* first derivatives of field calculated from diminishing vector potential
444
445 # del B'(r) = S2(Sx) del B(r) + B(r) del S2(Sx) + del (del S2(Sx) cross A(r))
446
447 # component-wise:
448
449 # del Bx = S2(Sx) del Bx + del S2(Sx) Bx + del(del S2(Sx) cross A)@i=x
450 # del By = S2(Sx) del By + del S2(Sx) By + del(del S2(Sx) cross A)@i=y
451 # del Bz = S2(Sx) del Bz + del S2(Sx) Bz + del(del S2(Sx) cross A)@i=z
452
453 # where
454
455 # del(del S2(Sx) cross A)@i=x = del (dS2/dy Az - dS/dz Ay)
456 # = del(dS/dy) Az + dS/dy del Az - del(DS/dz) Ay - dS/dz del Ay
457
458 # del(del S2(Sx) cross A)@i=y = del (dS2/dz Ax - dS/dx Az)
459 # = del(dS/dz) Ax + dS/dz del Ax - del(DS/dx) Az - dS/dx del Az
460
461 # del(del S2(Sx) cross A)@i=z = del (dS2/dx Ay - dS/dy Ax)
462 # = del(dS/dx) Ay + dS/dx del Ay - del(DS/dy) Ax - dS/dy del Ax
463
464
465 # **********/
466
467 if(dComponent==fComponent):
468 sameComponent=1
469 else:
470 sameComponent=0
471
472 # Regular derivative of B
473 delB = -5*B*r[dComponent]/r2 + (3*self.q[dComponent]*r[fComponent] - 2*self.q[fComponent]*r[dComponent] + 3*rdotq*sameComponent)/r5
474
475 # Calculate del Ax, del Ay, del Az
476 delAx=np.zeros(3)
477 delAy=np.zeros(3)
478 delAz=np.zeros(3)
479 # delAx[0] = (-3./(r2*r2*r1))*(self.q[1]*r[2]-self.q[2]*r[1])*r[0]
480 # delAx[1] = (-3./(r2*r2*r1))*(self.q[1]*r[2]-self.q[2]*r[1])*r[1] -self.q[2]/(r2*r1)
481 # delAx[2] = (-3./(r2*r2*r1))*(self.q[1]*r[2]-self.q[2]*r[1])*r[2] +self.q[1]/(r2*r1)
482 delAy[0] = (-3./(r2*r2*r1))*(self.q[2]*r[0]-self.q[0]*r[2])*r[0] +self.q[2]/(r2*r1)
483 delAy[1] = (-3./(r2*r2*r1))*(self.q[2]*r[0]-self.q[0]*r[2])*r[1]
484 delAy[2] = (-3./(r2*r2*r1))*(self.q[2]*r[0]-self.q[0]*r[2])*r[2] -self.q[0]/(r2*r1)
485 delAz[0] = (-3./(r2*r2*r1))*(self.q[0]*r[1]-self.q[1]*r[0])*r[0] -self.q[1]/(r2*r1)
486 delAz[1] = (-3./(r2*r2*r1))*(self.q[0]*r[1]-self.q[1]*r[0])*r[1] +self.q[0]/(r2*r1)
487 delAz[2] = (-3./(r2*r2*r1))*(self.q[0]*r[1]-self.q[1]*r[0])*r[2]
488
489 #ddidS2dr = 60.*(2.*Sx2*Sx - 3.*Sx2 + Sx)/(r2*(radius[1]-radius[0])*(radius[1]-radius[0]))
490 ddxdS2dx = 60.*(2.*Sx2*Sx - 3.*Sx2 + Sx)/((self.radius[1]-self.radius[0])*(self.radius[1]-self.radius[0]))
491
492 # Calculate del (dS2/dx), del (dS2/dy), del (dS2/dz)
493 deldS2dx=np.zeros(3)
494 deldS2dy=np.zeros(3)
495 deldS2dz=np.zeros(3)
496 deldS2dx[0] = ddxdS2dx
497 deldS2dx[1] = 0.
498 deldS2dx[2] = 0.
499 # deldS2dx[0] = ddxdS2dr*r[0]*r[0] -(r[0]/(r2*r1))*dS2dr*r[0] + dS2dr/r1
500 # deldS2dx[1] = ddxdS2dr*r[0]*r[1] -(r[0]/(r2*r1))*dS2dr*r[1]
501 # deldS2dx[2] = ddxdS2dr*r[0]*r[2] -(r[0]/(r2*r1))*dS2dr*r[2]
502 # deldS2dy[0] = ddidS2dr*r[1]*r[0] -(r[1]/(r2*r1))*dS2dr*r[0]
503 # deldS2dy[1] = ddidS2dr*r[1]*r[1] -(r[1]/(r2*r1))*dS2dr*r[1] + dS2dr/r1
504 # deldS2dy[2] = ddidS2dr*r[1]*r[2] -(r[1]/(r2*r1))*dS2dr*r[2]
505 # deldS2dz[0] = ddidS2dr*r[2]*r[0] -(r[2]/(r2*r1))*dS2dr*r[0]
506 # deldS2dz[1] = ddidS2dr*r[2]*r[1] -(r[2]/(r2*r1))*dS2dr*r[1]
507 # deldS2dz[2] = ddidS2dr*r[2]*r[2] -(r[2]/(r2*r1))*dS2dr*r[2] + dS2dr/r1
508
509 # Calculate del(del S2(Sx) cross A)@i=x, del(del S2(Sx) cross A)@i=y, del(del S2(Sx) cross A)@i=z
510 ddS2crossA=np.zeros([3,3])
511 # derivatives of X-directional field
512 ddS2crossA[0][0] = 0.#deldS2dy[0]*A[2] + dS2cart[1]*delAz[0] - deldS2dz[0]*A[1] - dS2cart[2]*delAy[0]
513 ddS2crossA[0][1] = 0.#deldS2dy[1]*A[2] + dS2cart[1]*delAz[1] - deldS2dz[1]*A[1] - dS2cart[2]*delAy[1]
514 ddS2crossA[0][2] = 0.#deldS2dy[2]*A[2] + dS2cart[1]*delAz[2] - deldS2dz[2]*A[1] - dS2cart[2]*delAy[2]
515 # derivatives of Y-directional field
516 ddS2crossA[1][0] = - deldS2dx[0]*A[2] - dS2cart[0]*delAz[0] #deldS2dz[0]*A[0] + dS2cart[2]*delAx[0]
517 ddS2crossA[1][1] = - deldS2dx[1]*A[2] - dS2cart[0]*delAz[1] #deldS2dz[1]*A[0] + dS2cart[2]*delAx[1]
518 ddS2crossA[1][2] = - deldS2dx[2]*A[2] - dS2cart[0]*delAz[2] #deldS2dz[2]*A[0] + dS2cart[2]*delAx[2]
519 # derivatives of Z-directional field
520 ddS2crossA[2][0] = deldS2dx[0]*A[1] + dS2cart[0]*delAy[0] #- deldS2dy[0]*A[0] - dS2cart[1]*delAx[0]
521 ddS2crossA[2][1] = deldS2dx[1]*A[1] + dS2cart[0]*delAy[1] #- deldS2dy[1]*A[0] - dS2cart[1]*delAx[1]
522 ddS2crossA[2][2] = deldS2dx[2]*A[1] + dS2cart[0]*delAy[2] #- deldS2dy[2]*A[0] - dS2cart[1]*delAx[2]
523
524 return S2*delB + dS2cart[dComponent]*B + ddS2crossA[fComponent][dComponent]
525
526 print("ERROR")
527 return 0 # dummy, but prevents gcc from yelling
528
529
530
531
532
533
534
535
536
537
538
539
540
541class IMFpotential(object):
542 ''' Class generating a scaling vector potential for the inflow IMF
543 '''
544 # The vector potential for a constant field is defined as
545 # A = 0.5 * B cross r
546
547 def __init__(self, radius_z=10, radius_f=40, IMF=[0.,0.,-5.e-9]):
548 self.radius = np.zeros(2)# // X-extents of zero and full field
549 self.radius[0]=radius_z*RE
550 self.radius[1]=radius_f*RE
551 self.IMF = IMF
552
553 def set_IMF(self, radius_z=10, radius_f=40, IMF=[0.,0.,-5.e-9]):
554 self.radius[0]=radius_z*RE
555 self.radius[1]=radius_f*RE
556 self.IMF = IMF
557
558 def get(self, x,y,z,derivative,fComponent,dComponent):
559 r = np.zeros(3)
560 r[0]= x
561 r[1]= y
562 r[2]= z
563
564 # Simple constant fields outside variation zone
565 if(x<self.radius[0]):
566 return 0.0
567 if(x>self.radius[1]):
568 if derivative==0:
569 return self.IMF[fComponent]
570 else:
571 return 0.0
572
573 A = np.zeros(3)
574 A[0] = 0.5*(self.IMF[1]*r[2] - self.IMF[2]*r[1])
575 A[1] = 0.5*(self.IMF[2]*r[0] - self.IMF[0]*r[2])
576 A[2] = 0.5*(self.IMF[0]*r[1] - self.IMF[1]*r[0])
577
578 B = self.IMF[fComponent]
579
580 # Coordinate within smootherstep function
581 Sx = (x-self.radius[0])/(self.radius[1]-self.radius[0])
582 Sx2 = Sx*Sx
583 # Smootherstep and its x-derivative
584 S2 = 6.*Sx2*Sx2*Sx - 15.*Sx2*Sx2 + 10.*Sx2*Sx
585 dS2dx = (30.*Sx2*Sx2 - 60.*Sx2*Sx + 30.*Sx2)/(self.radius[1]-self.radius[0])
586
587 # Cartesian derivatives of S2
588 dS2cart=np.zeros(3)
589 dS2cart[0] = dS2dx
590 dS2cart[1] = 0.
591 dS2cart[2] = 0.
592
593
594 if(derivative == 0):
595 # The scaled magnetic field is
596 # B'(r) = del cross A'(r)
597 # =(NRL)= S2(Sx) del cross A(r) + del S2(Sx) cross A(r)
598 # = S2(Sx) B(r) + del S2(Sx) cross A(r)
599
600 delS2crossA=np.zeros(3)
601 delS2crossA[0] = 0.#dS2cart[1]*A[2] - dS2cart[2]*A[1]
602 delS2crossA[1] = - dS2cart[0]*A[2] #dS2cart[2]*A[0] - dS2cart[0]*A[2]
603 delS2crossA[2] = dS2cart[0]*A[1] #- dS2cart[1]*A[0]
604
605 return S2*B + delS2crossA[fComponent]
606
607 elif(derivative == 1):
608 # Regular derivative of B
609 delB = 0.
610
611 # Calculate del Ax, del Ay, del Az
612 delAx=np.zeros(3)
613 delAy=np.zeros(3)
614 delAz=np.zeros(3)
615 delAx[0] = 0.
616 delAx[1] = -0.5*self.IMF[2]
617 delAx[2] = 0.5*self.IMF[1]
618 delAy[0] = 0.5*self.IMF[2]
619 delAy[1] = 0.0
620 delAy[2] = -0.5*self.IMF[0]
621 delAz[0] = -0.5*self.IMF[1]
622 delAz[1] = 0.5*self.IMF[0]
623 delAz[2] = 0.0
624
625 #ddidS2dr = 60.*(2.*Sx2*Sx - 3.*Sx2 + Sx)/(r2*(radius[1]-radius[0])*(radius[1]-radius[0]))
626 ddxdS2dx = 60.*(2.*Sx2*Sx - 3.*Sx2 + Sx)/((self.radius[1]-self.radius[0])*(self.radius[1]-self.radius[0]))
627
628 # Calculate del (dS2/dx), del (dS2/dy), del (dS2/dz)
629 deldS2dx=np.zeros(3)
630 deldS2dy=np.zeros(3)
631 deldS2dz=np.zeros(3)
632 deldS2dx[0] = ddxdS2dx
633 deldS2dx[1] = 0.
634 deldS2dx[2] = 0.
635
636 # Calculate del(del S2(Sx) cross A)@i=x, del(del S2(Sx) cross A)@i=y, del(del S2(Sx) cross A)@i=z
637 ddS2crossA=np.zeros([3,3])
638
639 # derivatives of X-directional field
640 ddS2crossA[0][0] = deldS2dy[0]*A[2] + dS2cart[1]*delAz[0] - deldS2dz[0]*A[1] - dS2cart[2]*delAy[0]
641 ddS2crossA[0][1] = deldS2dy[1]*A[2] + dS2cart[1]*delAz[1] - deldS2dz[1]*A[1] - dS2cart[2]*delAy[1]
642 ddS2crossA[0][2] = deldS2dy[2]*A[2] + dS2cart[1]*delAz[2] - deldS2dz[2]*A[1] - dS2cart[2]*delAy[2]
643 # derivatives of Y-directional field
644 ddS2crossA[1][0] = deldS2dz[0]*A[0] + dS2cart[2]*delAx[0] - deldS2dx[0]*A[2] - dS2cart[0]*delAz[0]
645 ddS2crossA[1][1] = deldS2dz[1]*A[0] + dS2cart[2]*delAx[1] - deldS2dx[1]*A[2] - dS2cart[0]*delAz[1]
646 ddS2crossA[1][2] = deldS2dz[2]*A[0] + dS2cart[2]*delAx[2] - deldS2dx[2]*A[2] - dS2cart[0]*delAz[2]
647 # derivatives of Z-directional field
648 ddS2crossA[2][0] = deldS2dx[0]*A[1] + dS2cart[0]*delAy[0] - deldS2dy[0]*A[0] - dS2cart[1]*delAx[0]
649 ddS2crossA[2][1] = deldS2dx[1]*A[1] + dS2cart[0]*delAy[1] - deldS2dy[1]*A[0] - dS2cart[1]*delAx[1]
650 ddS2crossA[2][2] = deldS2dx[2]*A[1] + dS2cart[0]*delAy[2] - deldS2dy[2]*A[0] - dS2cart[1]*delAx[2]
651
652 return S2*delB + dS2cart[dComponent]*B + ddS2crossA[fComponent][dComponent]
653
654 print("ERROR")
655 return 0#; // dummy, but prevents gcc from yelling
set_IMF(self, radius_z=10, radius_f=40, IMF=[0., 0.,-5.e-9])
get(self, x, y, z, derivative, fComponent, dComponent)
__init__(self, radius_z=10, radius_f=40, IMF=[0., 0.,-5.e-9])
set_dipole(self, centerx, centery, centerz, tilt_phi, tilt_theta, mult=1.0, radius_f=None, radius_z=None)
get_old(self, x, y, z, derivative, fComponent, dComponent)
get_ldp(self, x, y, z, derivative, fComponent, dComponent)
__init__(self, centerx, centery, centerz, tilt_phi, tilt_theta, mult=1.0, radius_f=None, radius_z=None)
getX(self, x, y, z, derivative, fComponent, dComponent)
get(self, x, y, z, derivative, fComponent, dComponent)
if(setIndex< columnData->setColumnOffsets.size())