Vlasiator ebf0dd394 on dev (v5.4.0 + 1054 commits)
Loading...
Searching...
No Matches
vectordipole.cpp
Go to the documentation of this file.
1/*
2 * This file is part of Vlasiator.
3 * Copyright 2010-2016 Finnish Meteorological Institute
4 * Copyright 2017-2019 University of Helsinki
5 *
6 * For details of usage, see the COPYING file and read the "Rules of the Road"
7 * at http://www.physics.helsinki.fi/vlasiator/
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23/*
24Background magnetic field class of Vlasiator.
25*/
26
27#include <stdlib.h>
28#include <math.h>
29#include "vectordipole.hpp"
30#include "../common.h"
31
32// tilt_angle_phi is from the z-axis in radians
33// tilt_angle_theta is from the Sun-Earth-line in radians
34void VectorDipole::initialize(const double moment,const double center_x, const double center_y, const double center_z, const double tilt_angle_phi, const double tilt_angle_theta, const double xlimit_f, const double xlimit_z, const double IMF_Bx, const double IMF_By, const double IMF_Bz){
35 this->initialized = true;
36
37 q[0]=-sin(tilt_angle_phi)*cos(tilt_angle_theta)*moment;
38 q[1]=-sin(tilt_angle_phi)*sin(tilt_angle_theta)*moment;
39 q[2]=-cos(tilt_angle_phi)*moment;
40
41 center[0]=center_x;
42 center[1]=center_y;
43 center[2]=center_z;
44
45 // Scale dipole as a function of x-coordinate
46 xlimit[0]=xlimit_f; // Full dipole when x < xlimit_f
47 xlimit[1]=xlimit_z; // Zero dipole when x > xlimit_z
48
49 // Going from xlimit_f to xlimit_z, scale in IMF B-field
50 IMF[0]=IMF_Bx;
51 IMF[1]=IMF_By;
52 IMF[2]=IMF_Bz;
53
54 // TODO: If values for xlimit are zero, instead place them as 15 RE and Xmax-2*cellsize?
55}
56
57
58
59double VectorDipole::operator()( double x, double y, double z, coordinate component, unsigned int derivative, coordinate dcomponent) const {
60 const double minimumR=1e-3*physicalconstants::R_E; //The dipole field is defined to be outside of Earth, and units are in meters
61 if(this->initialized==false)
62 return 0.0;
63 double r[3];
64
65 r[0]= x-center[0];
66 r[1]= y-center[1];
67 r[2]= z-center[2];
68
69 double r2 = r[0]*r[0]+r[1]*r[1]+r[2]*r[2];
70
71 if(r2<minimumR*minimumR)
72 // r2=minimumR*minimumR;
73 return 0.0; //set zero field inside dipole
74
75 if(r[0]>=xlimit[1]){
76 //set zero or IMF field and derivatives outside "zero x limit"
77 if(derivative == 0) {
78 return IMF[component];
79 } else {
80 return 0.0;
81 }
82 }
83 /* This function is called from within other calls, one component at a time.
84 The component in question is defined using the component index. If a derivative
85 is requested, the direction of the derivative is defined using dcomponent. */
86
87 const double r1 = sqrt(r2);
88 const double r5 = (r2*r2*r1);
89 const double rdotq=q[0]*r[0] + q[1]*r[1] +q[2]*r[2];
90 const double B=( 3*r[component]*rdotq-q[component]*r2)/r5;
91
92 if((derivative == 0) && (r[0] <= xlimit[0])) {
93 // Full dipole field within full xlimit
94 return B;
95 }
96
97 if((derivative == 1) && (r[0] <= xlimit[0])){
98 //first derivatives of full field
99 unsigned int sameComponent;
100 if(dcomponent==component) {
101 sameComponent=1;
102 } else {
103 sameComponent=0;
104 }
105
106 /* Confirmed Battarbee 26.04.2019: This is the correct
107 3D dipole derivative. */
108 return -5*B*r[dcomponent]/r2+
109 (3*q[dcomponent]*r[component] -
110 2*q[component]*r[dcomponent] +
111 3*rdotq*sameComponent)/r5;
112 }
113
114 /* Within transition range (between "full x limit" and "zero x limit"), use
115 a vector potential scaled with the smootherstep function. Calculated
116 and coded by Markus Battarbee, 08.05.2019 */
117
118 // Calculate vector potential within transition range
119 double A[3];
120 A[0] = (q[1]*r[2]-q[2]*r[1]) / (r2*r1);
121 A[1] = (q[2]*r[0]-q[0]*r[2]) / (r2*r1);
122 A[2] = (q[0]*r[1]-q[1]*r[0]) / (r2*r1);
123 // Calculate vector potential for IMF scaling
124 double IMFA[3];
125 IMFA[0] = 0.5*(IMF[1]*r[2] - IMF[2]*r[1]);
126 IMFA[1] = 0.5*(IMF[2]*r[0] - IMF[0]*r[2]);
127 IMFA[2] = 0.5*(IMF[0]*r[1] - IMF[1]*r[0]);
128 const double IMFB = IMF[component];
129
130 // Coordinate within smootherstep function (x-coordinate only)
131 const double s = -(r[0]-xlimit[1])/(xlimit[1]-xlimit[0]);
132 const double ss = s*s;
133 // Smootherstep and its x-directional derivative
134 const double S2 = 6.*ss*ss*s - 15.*ss*ss + 10.*ss*s;
135 const double dS2dx = -(30.*ss*ss - 60.*ss*s + 30.*ss)/(xlimit[1]-xlimit[0]);
136
137 // Smootherstep for IMF
138 const double IMFs = (r[0]-xlimit[0])/(xlimit[1]-xlimit[0]);
139 const double IMFss = IMFs*IMFs;
140 // Smootherstep and its x-directional derivative
141 const double IMFS2 = 6.*IMFss*IMFss*IMFs - 15.*IMFss*IMFss + 10.*IMFss*IMFs;
142 const double IMFdS2dx = (30.*IMFss*IMFss - 60.*IMFss*IMFs + 30.*IMFss)/(xlimit[1]-xlimit[0]);
143
144 // Cartesian derivatives of S2
145 double dS2cart[3];
146 dS2cart[0] = dS2dx; //(r[0]/r1)*dS2dr;
147 dS2cart[1] = 0; //(r[1]/r1)*dS2dr;
148 dS2cart[2] = 0; //(r[2]/r1)*dS2dr;
149
150 // Cartesian derivatives of S2
151 double IMFdS2cart[3];
152 IMFdS2cart[0] = IMFdS2dx; //(r[0]/r1)*dS2dr;
153 IMFdS2cart[1] = 0; //(r[1]/r1)*dS2dr;
154 IMFdS2cart[2] = 0; //(r[2]/r1)*dS2dr;
155
156 if(derivative == 0) {
157 /* Within transition range (between xlimit[0] and xlimit[1]) we
158 multiply the magnetic field with the S2 smootherstep function
159 and add an additional corrective term to remove divergence. This
160 is based on using the dipole field vector potential and scaling
161 it using the smootherstep function S2.
162
163 Notation:
164 q = dipole moment (vector)
165 r = position vector
166 x = x-coordinate r[0]
167 R = position distance
168
169 The regular dipole field vector potential
170 A(r) = (mu0/4 pi R^3) * (q cross r)
171
172 The smootherstep function
173 ( 0, s<=0
174 S2(s) = ( 6s^5 -15s^4 +10s^3, 0<=s<=1
175 ( 1, s>=1
176
177 Radial distance scaling for S2
178 s = -(x-xlimit[1])/(xlimit[1]-xlimit[0])
179 ds = -dx/(xlimit[1]-xlimit[0])
180
181 The scaled vector potential is A'(r) = A(r)*S2(s)
182
183 The scaled magnetic field is
184 B'(r) = del cross A'(r)
185 =(NRL)= S2(s) del cross A(r) + del S2(s) cross A(r)
186 = S2(s) B(r) + del S2(s) cross A(r)
187
188 */
189 double delS2crossA[3];
190 //delS2crossA[0] = dS2cart[1]*A[2] - dS2cart[2]*A[1];
191 //delS2crossA[1] = dS2cart[2]*A[0] - dS2cart[0]*A[2];
192 //delS2crossA[2] = dS2cart[0]*A[1] - dS2cart[1]*A[0];
193 // Don't calculate zero terms
194 delS2crossA[0] = 0;
195 delS2crossA[1] = -dS2cart[0]*A[2];
196 delS2crossA[2] = dS2cart[0]*A[1];
197
198 double IMFdelS2crossA[3];
199 // Don't calculate zero terms
200 IMFdelS2crossA[0] = 0;
201 IMFdelS2crossA[1] = -IMFdS2cart[0]*IMFA[2];
202 IMFdelS2crossA[2] = IMFdS2cart[0]*IMFA[1];
203
204 //return S2*B + delS2crossA[component];
205 return S2*B + delS2crossA[component] + IMFS2*IMFB + IMFdelS2crossA[component];
206 }
207
208 else if(derivative == 1) {
209 /* first derivatives of field calculated from diminishing vector potential
210
211 del B'(r) = S2(s) del B(r) + B(r) del S2(s) + del (del S2(s) cross A(r))
212
213 component-wise:
214
215 del Bx = S2(s) del Bx + del S2(s) Bx + del(del S2(s) cross A)@i=x
216 del By = S2(s) del By + del S2(s) By + del(del S2(s) cross A)@i=y
217 del Bz = S2(s) del Bz + del S2(s) Bz + del(del S2(s) cross A)@i=z
218
219 where
220
221 del(del S2(s) cross A)@i=x = del (dS2/dy Az - dS2/dz Ay)
222 = del(dS2/dy) Az + dS2/dy del Az - del(DS/dz) Ay - dS2/dz del Ay
223
224 del(del S2(s) cross A)@i=y = del (dS2/dz Ax - dS2/dx Az)
225 = del(dS2/dz) Ax + dS2/dz del Ax - del(DS/dx) Az - dS2/dx del Az
226
227 del(del S2(s) cross A)@i=z = del (dS2/dx Ay - dS2/dy Ax)
228 = del(dS2/dx) Ay + dS2/dx del Ay - del(DS/dy) Ax - dS2/dy del Ax
229
230 note that dS2/dy == dS2/dz == 0
231 **********/
232
233 unsigned int sameComponent;
234 if(dcomponent==component) {
235 sameComponent=1;
236 } else {
237 sameComponent=0;
238 }
239
240 // Regular derivative of B
241 const double delB = -5*B*r[dcomponent]/r2+
242 (3*q[dcomponent]*r[component] -
243 2*q[component]*r[dcomponent] +
244 3*rdotq*sameComponent)/r5;
245
246 // IMF field is constant
247 const double IMFdelB = 0.;
248
249 // Calculate del Ax, del Ay, del Az
250 double delAy[3];
251 double delAz[3];
252 delAy[0] = (-3./(r2*r2*r1))*(q[2]*r[0]-q[0]*r[2])*r[0] +q[2]/(r2*r1);
253 delAy[1] = (-3./(r2*r2*r1))*(q[2]*r[0]-q[0]*r[2])*r[1];
254 delAy[2] = (-3./(r2*r2*r1))*(q[2]*r[0]-q[0]*r[2])*r[2] -q[0]/(r2*r1);
255 delAz[0] = (-3./(r2*r2*r1))*(q[0]*r[1]-q[1]*r[0])*r[0] -q[1]/(r2*r1);
256 delAz[1] = (-3./(r2*r2*r1))*(q[0]*r[1]-q[1]*r[0])*r[1] +q[0]/(r2*r1);
257 delAz[2] = (-3./(r2*r2*r1))*(q[0]*r[1]-q[1]*r[0])*r[2];
258 // derivatives of x-directional component of A are not needed here
259 //double delAx[3];
260 //delAx[0] = (-3./(r2*r2*r1))*(q[1]*r[2]-q[2]*r[1])*r[0];
261 //delAx[1] = (-3./(r2*r2*r1))*(q[1]*r[2]-q[2]*r[1])*r[1] -q[2]/(r2*r1);
262 //delAx[2] = (-3./(r2*r2*r1))*(q[1]*r[2]-q[2]*r[1])*r[2] +q[1]/(r2*r1);
263
264 // Calculate del IMFAx, del IMFAy, del IMFAz
265 //double IMFdelAx[3]; // Unused, since always zero
266 //IMFdelAx[0] = 0.;
267 //IMFdelAx[1] = -0.5*IMF[2];
268 //IMFdelAx[2] = 0.5*IMF[1];
269 double IMFdelAy[3];
270 IMFdelAy[0] = 0.5*IMF[2];
271 IMFdelAy[1] = 0.0;
272 IMFdelAy[2] = -0.5*IMF[0];
273 double IMFdelAz[3];
274 IMFdelAz[0] = -0.5*IMF[1];
275 IMFdelAz[1] = 0.5*IMF[0];
276 IMFdelAz[2] = 0.0;
277
278 // Calculate del (dS2/dx), del (dS2/dy), del (dS2/dz)
279 // Of course now only del (dS2/dx) is non-zero
280 const double ddidS2dx = 60.*(2.*ss*s - 3.*ss + s)/((xlimit[1]-xlimit[0])*(xlimit[1]-xlimit[0]));
281 // This is the same for IMF field scaling as well
282
283 double deldS2dx[3];
284 //double deldS2dy[3];
285 //double deldS2dz[3];
286 deldS2dx[0] = ddidS2dx;
287 deldS2dx[1] = 0;
288 deldS2dx[2] = 0;
289 /*
290 ddidS2dr = 60.*(2.*ss*s - 3.*ss + s)/(r2*(xlimit[1]-xlimit[0])*(xlimit[1]-xlimit[0]));
291 deldS2dx[0] = ddidS2dr*r[0]*r[0] -(r[0]/(r2*r1))*dS2dr*r[0] + dS2dr/r1;
292 deldS2dx[1] = ddidS2dr*r[0]*r[1] -(r[0]/(r2*r1))*dS2dr*r[1];
293 deldS2dx[2] = ddidS2dr*r[0]*r[2] -(r[0]/(r2*r1))*dS2dr*r[2];
294 deldS2dy[0] = ddidS2dr*r[1]*r[0] -(r[1]/(r2*r1))*dS2dr*r[0];
295 deldS2dy[1] = ddidS2dr*r[1]*r[1] -(r[1]/(r2*r1))*dS2dr*r[1] + dS2dr/r1;
296 deldS2dy[2] = ddidS2dr*r[1]*r[2] -(r[1]/(r2*r1))*dS2dr*r[2];
297 deldS2dz[0] = ddidS2dr*r[2]*r[0] -(r[2]/(r2*r1))*dS2dr*r[0];
298 deldS2dz[1] = ddidS2dr*r[2]*r[1] -(r[2]/(r2*r1))*dS2dr*r[1];
299 deldS2dz[2] = ddidS2dr*r[2]*r[2] -(r[2]/(r2*r1))*dS2dr*r[2] + dS2dr/r1;
300
301 // Calculate del(del S2(s) cross A)@i=x, del(del S2(s) cross A)@i=y, del(del S2(s) cross A)@i=z
302 double ddS2crossA[3][3];
303 // derivatives of X-directional field
304 ddS2crossA[0][0] = deldS2dy[0]*A[2] + dS2cart[1]*delAz[0] - deldS2dz[0]*A[1] - dS2cart[2]*delAy[0];
305 ddS2crossA[0][1] = deldS2dy[1]*A[2] + dS2cart[1]*delAz[1] - deldS2dz[1]*A[1] - dS2cart[2]*delAy[1];
306 ddS2crossA[0][2] = deldS2dy[2]*A[2] + dS2cart[1]*delAz[2] - deldS2dz[2]*A[1] - dS2cart[2]*delAy[2];
307 // derivatives of Y-directional field
308 ddS2crossA[1][0] = deldS2dz[0]*A[0] + dS2cart[2]*delAx[0] - deldS2dx[0]*A[2] - dS2cart[0]*delAz[0];
309 ddS2crossA[1][1] = deldS2dz[1]*A[0] + dS2cart[2]*delAx[1] - deldS2dx[1]*A[2] - dS2cart[0]*delAz[1];
310 ddS2crossA[1][2] = deldS2dz[2]*A[0] + dS2cart[2]*delAx[2] - deldS2dx[2]*A[2] - dS2cart[0]*delAz[2];
311 // derivatives of Z-directional field
312 ddS2crossA[2][0] = deldS2dx[0]*A[1] + dS2cart[0]*delAy[0] - deldS2dy[0]*A[0] - dS2cart[1]*delAx[0];
313 ddS2crossA[2][1] = deldS2dx[1]*A[1] + dS2cart[0]*delAy[1] - deldS2dy[1]*A[0] - dS2cart[1]*delAx[1];
314 ddS2crossA[2][2] = deldS2dx[2]*A[1] + dS2cart[0]*delAy[2] - deldS2dy[2]*A[0] - dS2cart[1]*delAx[2];
315 */
316
317 // Only include components which are nonzero
318 double ddS2crossA[3][3];
319 // derivatives of X-directional field
320 ddS2crossA[0][0] = 0;
321 ddS2crossA[0][1] = 0;
322 ddS2crossA[0][2] = 0;
323 // derivatives of Y-directional field
324 ddS2crossA[1][0] = - deldS2dx[0]*A[2] - dS2cart[0]*delAz[0];
325 ddS2crossA[1][1] = - deldS2dx[1]*A[2] - dS2cart[0]*delAz[1];
326 ddS2crossA[1][2] = - deldS2dx[2]*A[2] - dS2cart[0]*delAz[2];
327 // derivatives of Z-directional field
328 ddS2crossA[2][0] = deldS2dx[0]*A[1] + dS2cart[0]*delAy[0];
329 ddS2crossA[2][1] = deldS2dx[1]*A[1] + dS2cart[0]*delAy[1];
330 ddS2crossA[2][2] = deldS2dx[2]*A[1] + dS2cart[0]*delAy[2];
331
332 // Now for IMF portion
333 // Only include components which are nonzero
334 double IMFddS2crossA[3][3];
335 // derivatives of X-directional field
336 IMFddS2crossA[0][0] = 0;
337 IMFddS2crossA[0][1] = 0;
338 IMFddS2crossA[0][2] = 0;
339 // derivatives of Y-directional field
340 IMFddS2crossA[1][0] = - deldS2dx[0]*IMFA[2] - IMFdS2cart[0]*IMFdelAz[0];
341 IMFddS2crossA[1][1] = - deldS2dx[1]*IMFA[2] - IMFdS2cart[0]*IMFdelAz[1];
342 IMFddS2crossA[1][2] = - deldS2dx[2]*IMFA[2] - IMFdS2cart[0]*IMFdelAz[2];
343 // derivatives of Z-directional field
344 IMFddS2crossA[2][0] = deldS2dx[0]*IMFA[1] + IMFdS2cart[0]*IMFdelAy[0];
345 IMFddS2crossA[2][1] = deldS2dx[1]*IMFA[1] + IMFdS2cart[0]*IMFdelAy[1];
346 IMFddS2crossA[2][2] = deldS2dx[2]*IMFA[1] + IMFdS2cart[0]*IMFdelAy[2];
347
348 //return S2*delB + dS2cart[dcomponent]*B + ddS2crossA[component][dcomponent];
349 return S2*delB + dS2cart[dcomponent]*B + ddS2crossA[component][dcomponent] +
350 IMFS2*IMFdelB + IMFdS2cart[dcomponent]*IMFB + IMFddS2crossA[component][dcomponent];
351 }
352
353 return 0; // dummy, but prevents gcc from yelling
354}
355
356
357
358
359
360
sqrt(1.0+vA *vA/(c *c))) % Ion-acoustic wave cS
double xlimit[2]
void initialize(const double moment, const double center_x, const double center_y, const double center_z, const double tilt_angle_phi, const double tilt_angle_theta, const double xlimit_f, const double xlimit_z, const double IMF_Bx, const double IMF_By, const double IMF_Bz)
double operator()(double x, double y, double z, coordinate component, unsigned int derivative=0, coordinate dcomponent=X) const
double center[3]
double IMF[3]
coordinate
Definition functions.hpp:28
const Real R_E
Definition common.h:575