Vlasiator ebf0dd394 on dev (v5.4.0 + 1054 commits)
Loading...
Searching...
No Matches
Magnetosphere.cpp
Go to the documentation of this file.
1/*
2 * This file is part of Vlasiator.
3 * Copyright 2010-2016 Finnish Meteorological Institute
4 *
5 * For details of usage, see the COPYING file and read the "Rules of the Road"
6 * at http://www.physics.helsinki.fi/vlasiator/
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23#include <cstdlib>
24#include <iostream>
25#include <cmath>
26#include <array>
27
28#include "../../common.h"
37
38#include "Magnetosphere.h"
40
41using namespace std;
42using namespace spatial_cell;
43
44namespace projects {
47
49 typedef Readparameters RP;
50 // Common (field / etc.) parameters
51 RP::add("Magnetosphere.constBgBX", "Constant flat Bx component in the whole simulation box. Default is none.", 0.0);
52 RP::add("Magnetosphere.constBgBY", "Constant flat By component in the whole simulation box. Default is none.", 0.0);
53 RP::add("Magnetosphere.constBgBZ", "Constant flat Bz component in the whole simulation box. Default is none.", 0.0);
54 RP::add("Magnetosphere.noDipoleInSW", "If set to 1, the dipole magnetic field is not set in the solar wind inflow cells. Default 0.", 0.0);
55 RP::add("Magnetosphere.dipoleScalingFactor","Scales the field strength of the magnetic dipole compared to Earths.", 1.0);
56 RP::add("Magnetosphere.dipoleType","0: Normal 3D dipole, 1: line-dipole for 2D polar simulations, 2: line-dipole with mirror, 3: 3D dipole with mirror", 0);
57 RP::add("Magnetosphere.dipoleMirrorLocationX","x-coordinate of dipole Mirror", -1.0);
58
59 RP::add("Magnetosphere.refine_L4radius","Radius of L4-refined sphere or cap", 6.0e7);
60 RP::add("Magnetosphere.refine_L4nosexmin","Low x-value of nose L4-refined box", 5.5e7);
61
62 RP::add("Magnetosphere.refine_L3radius","Radius of L3-refined sphere or cap", 6.371e7); // 10 RE
63 RP::add("Magnetosphere.refine_L3nosexmin","Low x-value of nose L3-refined box", 5.0e7); //
64 RP::add("Magnetosphere.refine_L3tailheight","Height in +-z of tail L3-refined box", 1.0e7); //
65 RP::add("Magnetosphere.refine_L3tailwidth","Width in +-y of tail L3-refined box", 5.0e7); // 10 RE
66 RP::add("Magnetosphere.refine_L3tailxmin","Low x-value of tail L3-refined box", -20.0e7); // 10 RE
67 RP::add("Magnetosphere.refine_L3tailxmax","High x-value of tail L3-refined box", -5.0e7); // 10 RE
68
69 RP::add("Magnetosphere.refine_L2radius","Radius of L2-refined sphere", 9.5565e7); // 15 RE
70 RP::add("Magnetosphere.refine_L2tailthick","Thickness of L2-refined tail region", 3.1855e7); // 5 RE
71 RP::add("Magnetosphere.refine_L1radius","Radius of L1-refined sphere", 1.59275e8); // 25 RE
72 RP::add("Magnetosphere.refine_L1tailthick","Thickness of L1-refined tail region", 6.371e7); // 10 RE
73
74 RP::add("Magnetosphere.dipoleTiltPhi","Magnitude of dipole tilt, in degrees", 0.0);
75 RP::add("Magnetosphere.dipoleTiltTheta","Direction of dipole tilt from Sun-Earth-line, in degrees", 0.0);
76 RP::add("Magnetosphere.dipoleXFull","X-coordinate up to which dipole is at full strength, in metres", 9.5565e7); // 15 RE
77 RP::add("Magnetosphere.dipoleXZero","X-coordinate after which dipole is at zero strength, in metres", 1.9113e8); // 30 RE
78 RP::add("Magnetosphere.dipoleInflowBX","Inflow magnetic field Bx component to which the vector potential dipole converges. Default is none.", 0.0);
79 RP::add("Magnetosphere.dipoleInflowBY","Inflow magnetic field By component to which the vector potential dipole converges. Default is none.", 0.0);
80 RP::add("Magnetosphere.dipoleInflowBZ","Inflow magnetic field Bz component to which the vector potential dipole converges. Default is none.", 0.0);
81 //GG 28.5.26: Adding dipole offset code. Assuming SI units + guessing what the params will be named in cfg
82 RP::add("Magnetosphere.dipoleXOffset", "Distance of dipole from centre position in x. Default is none.", 0.0);
83 RP::add("Magnetosphere.dipoleYOffset", "Distance of dipole from centre position in y. Default is none.", 0.0);
84 RP::add("Magnetosphere.dipoleZOffset", "Distance of dipole from centre position in z. Default is none.", 0.0);
85
86
87 //New Parameter for zeroing out derivativeNew Parameter for zeroing out derivativess
88 RP::add("Magnetosphere.zeroOutDerivativesX","Zero Out Perpendicular components", 1.0);
89 RP::add("Magnetosphere.zeroOutDerivativesY","Zero Out Perpendicular components", 1.0);
90 RP::add("Magnetosphere.zeroOutDerivativesZ","Zero Out Perpendicular components", 1.0);
91
92 // Per-population parameters
93 for(uint i=0; i< getObjectWrapper().particleSpecies.size(); i++) {
94 const std::string& pop = getObjectWrapper().particleSpecies[i].name;
95
96 RP::add(pop + "_Magnetosphere.rho", "Tail region number density (m^-3)", 0.0);
97 RP::add(pop + "_Magnetosphere.T", "Temperature (K)", 0.0);
98 RP::add(pop + "_Magnetosphere.VX0", "Initial bulk velocity in x-direction", 0.0);
99 RP::add(pop + "_Magnetosphere.VY0", "Initial bulk velocity in y-direction", 0.0);
100 RP::add(pop + "_Magnetosphere.VZ0", "Initial bulk velocity in z-direction", 0.0);
101 RP::add(pop + "_Magnetosphere.taperInnerRadius", "Inner radius of the zone with a density tapering from the ionospheric value to the background (m)", 0.0);
102 RP::add(pop + "_Magnetosphere.taperOuterRadius", "Outer radius of the zone with a density tapering from the ionospheric value to the background (m)", 0.0);
103 }
104 }
105
107 int myRank;
108 MPI_Comm_rank(MPI_COMM_WORLD,&myRank);
109
111 SysBoundary& sysBoundaryContainer = getObjectWrapper().sysBoundaryContainer;
112
113 Real dummy;
114 typedef Readparameters RP;
115 RP::get("Magnetosphere.constBgBX", this->constBgB[0]);
116 RP::get("Magnetosphere.constBgBY", this->constBgB[1]);
117 RP::get("Magnetosphere.constBgBZ", this->constBgB[2]);
118 RP::get("Magnetosphere.noDipoleInSW", dummy);
119 this->noDipoleInSW = dummy == 1 ? true:false;
120 RP::get("Magnetosphere.dipoleScalingFactor", this->dipoleScalingFactor);
121
122 RP::get("Magnetosphere.dipoleMirrorLocationX", this->dipoleMirrorLocationX);
123
124 RP::get("Magnetosphere.dipoleType", this->dipoleType);
125
126 /* Enforce "dipole" (incl. correction terms) in solar wind with dipole type 4. */
127 if ((this->dipoleType == 4) && (this->noDipoleInSW)) {
128 if(myRank == MASTER_RANK) {
129 std::cerr<<"Note: Initializing Magnetosphere with dipole type 4, which requires the dipole + vector potential "
130 <<"correction terms in the solar wind. Thus overriding the config and setting Magnetosphere.noDipoleInSW=0."<<std::endl;
131 }
132 this->noDipoleInSW = false;
133 }
134
136 if (sysBoundaryContainer.existSysBoundary("Copysphere")) {
137 RP::get("copysphere.radius", this->ionosphereRadius);
138 RP::get("copysphere.centerX", this->center[0]);
139 RP::get("copysphere.centerY", this->center[1]);
140 RP::get("copysphere.centerZ", this->center[2]);
141 RP::get("copysphere.geometry", this->ionosphereGeometry);
142 } else if (sysBoundaryContainer.existSysBoundary("Ionosphere")) {
143 RP::get("ionosphere.radius", this->ionosphereRadius);
144 RP::get("ionosphere.centerX", this->center[0]);
145 RP::get("ionosphere.centerY", this->center[1]);
146 RP::get("ionosphere.centerZ", this->center[2]);
147 RP::get("ionosphere.geometry", this->ionosphereGeometry);
148 } else {
149 if(myRank == MASTER_RANK) {
150 std::cerr<<"Warning in initializing Magnetosphere: Could not find inner boundary (ionosphere or copysphere)!"<<std::endl;
151 }
152 }
153 if(ionosphereRadius < 1000.) {
154 // For really small ionospheric radius values, assume R_E units
156 if(myRank == MASTER_RANK) {
157 std::cerr<<"[Magnetosphere] Note: ionosphereRadius given was < 1000, assuming units of R_E and scaling for you."<<std::endl;
158 }
159 }
160
161 RP::get("Magnetosphere.refine_L4radius", this->refine_L4radius);
162 RP::get("Magnetosphere.refine_L4nosexmin", this->refine_L4nosexmin);
163
164 RP::get("Magnetosphere.refine_L3radius", this->refine_L3radius);
165 RP::get("Magnetosphere.refine_L3nosexmin", this->refine_L3nosexmin);
166 RP::get("Magnetosphere.refine_L3tailwidth", this->refine_L3tailwidth);
167 RP::get("Magnetosphere.refine_L3tailheight", this->refine_L3tailheight);
168 RP::get("Magnetosphere.refine_L3tailxmin", this->refine_L3tailxmin);
169 RP::get("Magnetosphere.refine_L3tailxmax", this->refine_L3tailxmax);
170
171 RP::get("Magnetosphere.refine_L2radius", this->refine_L2radius);
172 RP::get("Magnetosphere.refine_L2tailthick", this->refine_L2tailthick);
173 RP::get("Magnetosphere.refine_L1radius", this->refine_L1radius);
174 RP::get("Magnetosphere.refine_L1tailthick", this->refine_L1tailthick);
175
176 RP::get("Magnetosphere.dipoleTiltPhi", this->dipoleTiltPhi);
177 RP::get("Magnetosphere.dipoleTiltTheta", this->dipoleTiltTheta);
178 RP::get("Magnetosphere.dipoleXFull", this->dipoleXFull);
179 RP::get("Magnetosphere.dipoleXZero", this->dipoleXZero);
180 RP::get("Magnetosphere.dipoleInflowBX", this->dipoleInflowB[0]);
181 RP::get("Magnetosphere.dipoleInflowBY", this->dipoleInflowB[1]);
182 RP::get("Magnetosphere.dipoleInflowBZ", this->dipoleInflowB[2]);
183 //GG 28.5.26: Adding dipole offset code. Assuming SI units + guessing what the params will be named in cfg
184 RP::get("Magnetosphere.dipoleXOffset", this->dipoleXOffset);
185 RP::get("Magnetosphere.dipoleYOffset", this->dipoleYOffset);
186 RP::get("Magnetosphere.dipoleZOffset", this->dipoleZOffset);
188 //GG 8.6.26: Sanity check - we don't know the planet radius but we do know the ionosphere radius
190 if(myRank == MASTER_RANK) {
191 std::cerr<<"[Magnetosphere] WARNING: dipole offset position vector exceeds the ionosphere radius. "
192 <<"This is very likely to cause problems."<<std::endl;
193 }
194 }
195
196
197 RP::get("Magnetosphere.zeroOutDerivativesX", this->zeroOutComponents[0]);
198 RP::get("Magnetosphere.zeroOutDerivativesY", this->zeroOutComponents[1]);
199 RP::get("Magnetosphere.zeroOutDerivativesZ", this->zeroOutComponents[2]);
200
201 // Per-population parameters
202 for(uint i=0; i< getObjectWrapper().particleSpecies.size(); i++) {
203 const std::string& pop = getObjectWrapper().particleSpecies[i].name;
205
206 RP::get(pop + "_Magnetosphere.rho", sP.rho);
207 RP::get(pop + "_Magnetosphere.T", sP.T);
208 RP::get(pop + "_Magnetosphere.VX0", sP.V0[0]);
209 RP::get(pop + "_Magnetosphere.VY0", sP.V0[1]);
210 RP::get(pop + "_Magnetosphere.VZ0", sP.V0[2]);
211
213 if (sysBoundaryContainer.existSysBoundary("Copysphere")) {
214 RP::get(pop + "_copysphere.rho", sP.ionosphereRho);
215 RP::get(pop + "_copysphere.T", sP.ionosphereT);
216 RP::get(pop + "_copysphere.VX0", sP.ionosphereV0[0]);
217 RP::get(pop + "_copysphere.VY0", sP.ionosphereV0[1]);
218 RP::get(pop + "_copysphere.VZ0", sP.ionosphereV0[2]);
219 } else if (sysBoundaryContainer.existSysBoundary("Ionosphere")) {
220 RP::get(pop + "_ionosphere.rho", sP.ionosphereRho);
221 RP::get(pop + "_ionosphere.T", sP.ionosphereT);
222 RP::get(pop + "_ionosphere.VX0", sP.ionosphereV0[0]);
223 RP::get(pop + "_ionosphere.VY0", sP.ionosphereV0[1]);
224 RP::get(pop + "_ionosphere.VZ0", sP.ionosphereV0[2]);
225 }
226 RP::get(pop + "_Magnetosphere.taperInnerRadius", sP.taperInnerRadius);
227 RP::get(pop + "_Magnetosphere.taperOuterRadius", sP.taperOuterRadius);
228 // Backward-compatibility: cfgs from before Sep 2021 setting pop_ionosphere.taperRadius will fail with the unknown option.
229 // Some fail-safety checks
230 if(sP.taperInnerRadius < 0 || sP.taperOuterRadius < 0) {
231 if(myRank == MASTER_RANK) {
232 cerr << "Error: " << pop << "_Magnetosphere.taperInnerRadius and tapeOuterRadius should be >= 0! Aborting." << endl;
233 }
234 abort();
235 }
237 if(myRank == MASTER_RANK) {
238 cerr << "Error: " << pop << "_Magnetosphere.taperInnerRadius should be <= taperOuterRadius! Aborting." << endl;
239 }
240 abort();
241 }
242 if(sP.taperOuterRadius > 0 && sP.taperOuterRadius <= this->ionosphereRadius) {
243 if(myRank == MASTER_RANK) {
244 cerr << "Error: " << pop << "_Magnetosphere.taperOuterRadius is non-zero yet smaller than ionosphere.radius / copysphere.radius! Aborting." << endl;
245 }
246 abort();
247 }
248 if(sP.taperInnerRadius == 0 && sP.taperOuterRadius > 0) {
249 if(myRank == MASTER_RANK) {
250 cerr << "Warning: " << pop << "_Magnetosphere.taperInnerRadius is zero (default), now setting this to the same value as ionosphere.radius / copysphere.radius, that is " << this->ionosphereRadius << ". Set/change " << pop << "_Magnetosphere.taperInnerRadius if this is not the expected behavior." << endl;
251 }
252 sP.taperInnerRadius = this->ionosphereRadius;
253 }
254 if(sP.ionosphereT == 0) {
255 if(myRank == MASTER_RANK) {
256 if (sysBoundaryContainer.existSysBoundary("Copysphere")) {
257 cerr << "Warning: " << pop << "_copysphere.T is zero (default), now setting to the same value as " << pop << "_Magnetosphere.T, that is " << sP.T << ". Set/change " << pop << "_copysphere.T if this is not the expected behavior." << endl;
258 } else if (sysBoundaryContainer.existSysBoundary("Ionosphere")) {
259 cerr << "Warning: " << pop << "_ionosphere.T is zero (default), now setting to the same value as " << pop << "_Magnetosphere.T, that is " << sP.T << ". Set/change " << pop << "_ionosphere.T if this is not the expected behavior." << endl;
260 }
261 }
262 sP.ionosphereT = sP.T;
263 }
264 if(sP.ionosphereRho == 0) {
265 if(myRank == MASTER_RANK) {
266 if (sysBoundaryContainer.existSysBoundary("Copysphere")) {
267 cerr << "Warning: " << pop << "_copysphere.rho is zero (default), now setting to the same value as " << pop << "_Magnetosphere.rho, that is " << sP.rho << ". Set/change " << pop << "_copysphere.rho if this is not the expected behavior." << endl;
268 } else if (sysBoundaryContainer.existSysBoundary("Ionosphere")) {
269 cerr << "Warning: " << pop << "_ionosphere.rho is zero (default), now setting to the same value as " << pop << "_Magnetosphere.rho, that is " << sP.rho << ". Set/change " << pop << "_ionosphere.rho if this is not the expected behavior." << endl;
270 }
271 }
272 sP.ionosphereRho = sP.rho;
273 }
274
275 speciesParams.push_back(sP);
276 }
277
278 }
279
281 return Project::initialize();
282 }
283
285
287 Real radius;
288 switch(this->ionosphereGeometry) {
289 case 0:
290 // infinity-norm, result is a diamond/square with diagonals aligned on the axes in 2D
291 radius = fabs(x-center[0]) + fabs(y-center[1]) + fabs(z-center[2]);
292 break;
293 case 1:
294 // 1-norm, result is is a grid-aligned square in 2D
295 radius = max(max(fabs(x-center[0]), fabs(y-center[1])), fabs(z-center[2]));
296 break;
297 case 2:
298 // 2-norm (Cartesian), result is a circle in 2D
299 radius = sqrt((x-center[0])*(x-center[0]) + (y-center[1])*(y-center[1]) + (z-center[2])*(z-center[2]));
300 break;
301 case 3:
302 // cylinder aligned with y-axis, use with polar plane/line dipole
303 radius = sqrt((x-center[0])*(x-center[0]) + (z-center[2])*(z-center[2]));
304 break;
305 default:
306 std::cerr << __FILE__ << ":" << __LINE__ << ":" << "ionosphere.geometry has to be 0, 1, 2 or 3." << std::endl;
307 abort();
308 }
309 return radius;
310 }
311
312 /* set 0-centered dipole */
316 Dipole bgFieldDipole;
317 LineDipole bgFieldLineDipole;
318 VectorDipole bgVectorDipole;
319
320 phiprof::Timer switchDipoleTypeTimer {"switch-dipoleType"};
321 // The hardcoded constants of dipole and line dipole moments are obtained
322 // from Daldorff et al (2014), see
323 // https://github.com/fmihpc/vlasiator/issues/20 for a derivation of the
324 // values used here.
325 switch(this->dipoleType) {
326 //GG 28.5.26: Can I just set some centre parameters using RP::add(whatever_offset) for each \vec{x}_i component
327 //Then set them default to 0.0 so nothing changes if they don't exist in the cfg, then always call them here?
328 case 0:
329 bgFieldDipole.initialize(8e15 *this->dipoleScalingFactor, 0.0, 0.0, 0.0, 0.0 );//set dipole moment
330 setBackgroundField(bgFieldDipole, bgb, technical, fsgrid);
331 SBC::ionosphereGrid.setDipoleField(bgFieldDipole);
332 break;
333 case 1:
334 bgFieldLineDipole.initialize(126.2e6 *this->dipoleScalingFactor, 0.0, 0.0, 0.0 );//set dipole moment
335 setBackgroundField(bgFieldLineDipole, bgb, technical, fsgrid);
336 SBC::ionosphereGrid.setDipoleField(bgFieldLineDipole);
337 break;
338 case 2:
339 bgFieldLineDipole.initialize(126.2e6 *this->dipoleScalingFactor, 0.0, 0.0, 0.0 );//set dipole moment
340 setBackgroundField(bgFieldLineDipole, bgb, technical, fsgrid);
341 //Append mirror dipole
342 bgFieldLineDipole.initialize(126.2e6 *this->dipoleScalingFactor, this->dipoleMirrorLocationX, 0.0, 0.0 );
343 setBackgroundField(bgFieldLineDipole, bgb, technical, fsgrid, true);
344 SBC::ionosphereGrid.setDipoleField(bgFieldLineDipole);
345 break;
346 case 3:
347 bgFieldDipole.initialize(8e15 *this->dipoleScalingFactor, 0.0, 0.0, 0.0, 0.0 );//set dipole moment
348 setBackgroundField(bgFieldDipole, bgb, technical, fsgrid);
349 SBC::ionosphereGrid.setDipoleField(bgFieldDipole);
350 //Append mirror dipole
351 bgFieldDipole.initialize(8e15 *this->dipoleScalingFactor, this->dipoleMirrorLocationX, 0.0, 0.0, 0.0 );//mirror
352 setBackgroundField(bgFieldDipole, bgb, technical, fsgrid, true);
353 break;
354 case 4: // Vector potential dipole, vanishes or optionally scales to static inflow value after a given x-coordinate
355 // What we in fact do is we place the regular dipole in the background field, and the
356 // corrective terms in the perturbed field. This maintains the BGB as curl-free.
357 //bgFieldDipole.initialize(8e15 *this->dipoleScalingFactor, 0.0, 0.0, 0.0, 0.0 ); //set dipole moment
358
359 //GG 28.5.26: Adding dipole offset code. Assuming SI units + guessing what the params will be named in cfg
360 bgFieldDipole.initialize(8e15 *this->dipoleScalingFactor, this->dipoleXOffset, this->dipoleYOffset, this->dipoleZOffset, 0.0 ); //set dipole moment
361
362 setBackgroundField(bgFieldDipole, bgb, technical, fsgrid);
363 SBC::ionosphereGrid.setDipoleField(bgFieldDipole);
364 // Now we calculate the difference required to scale the dipole to zero as we approach the inflow,
365 // and store it inside the BgBGrid object for use by e.g. boundary conditions.
366 bgFieldDipole.initialize(-8e15 *this->dipoleScalingFactor, this->dipoleXOffset, this->dipoleYOffset, this->dipoleZOffset, 0.0 );
367 setPerturbedField(bgFieldDipole, bgb, technical, fsgrid, fsgrids::bgbfield::BGBXVDCORR);
368 bgVectorDipole.initialize(8e15 *this->dipoleScalingFactor, this->dipoleXOffset, this->dipoleYOffset, this->dipoleZOffset, this->dipoleTiltPhi*M_PI/180., this->dipoleTiltTheta*M_PI/180., this->dipoleXFull, this->dipoleXZero, this->dipoleInflowB[0], this->dipoleInflowB[1], this->dipoleInflowB[2]);
369 setPerturbedField(bgVectorDipole, bgb, technical, fsgrid, fsgrids::bgbfield::BGBXVDCORR, true);
370 if (P::isRestart == false) {
371 // If we are starting a new simulation, we also copy this data into perB.
372 fsgrid.parallel_for([](int timerId) -> phiprof::Timer { return phiprof::Timer{timerId}; },
373 phiprof::initializeTimer("setProjectBField-loop"), technical,
374 [=](const fsgrid::Coordinates &coordinates, const fsgrid::FsStencil& stencil, cuint sysBoundaryFlag, cuint sysBoundaryLayer) {
375 const auto& BGBcell = bgb[stencil.ooo()];
376 auto& PERBcell = perb[stencil.ooo()];
380 });
381 }
382 break;
383 default:
384 setBackgroundFieldToZero(fsgrid, technical, bgb);
385 }
386 switchDipoleTypeTimer.stop();
387
388 const auto zeroOutComponents_l = this->zeroOutComponents; // local copies for lambda capture
389 const auto dipoleType_l = this->dipoleType;
390 const auto noDipoleInSW_l = this->noDipoleInSW;
391 const auto constBgB_l = this->constBgB;
392 fsgrid.parallel_for([](int timerId) -> phiprof::Timer { return phiprof::Timer{timerId}; },
393 phiprof::initializeTimer("zeroing-out"), technical,
394 [=](const fsgrid::Coordinates &coordinates, const fsgrid::FsStencil& stencil, cuint sysBoundaryFlag, cuint sysBoundaryLayer) {
395 bool doZeroOut;
396 //Force field to zero in the perpendicular direction for 2D (1D) simulations. Otherwise we have unphysical components.
397 doZeroOut = P::xcells_ini ==1 && zeroOutComponents_l[0]==1;
398
399 if(doZeroOut) {
400 auto& cell = bgb[stencil.ooo()];
401 cell[fsgrids::bgbfield::BGBX] = 0;
402 cell[fsgrids::bgbfield::BGBXVOL] = 0.0;
403 cell[fsgrids::bgbfield::dBGBydx] = 0.0;
404 cell[fsgrids::bgbfield::dBGBzdx] = 0.0;
405 cell[fsgrids::bgbfield::dBGBxdy] = 0.0;
406 cell[fsgrids::bgbfield::dBGBxdz] = 0.0;
411 }
412
413 doZeroOut = P::ycells_ini ==1 && zeroOutComponents_l[1]==1;
414 if(doZeroOut) {
415 /*2D simulation in x and z. Set By and derivatives along Y, and derivatives of By to zero*/
416 auto& cell = bgb[stencil.ooo()];
417 cell[fsgrids::bgbfield::BGBY] = 0.0;
418 cell[fsgrids::bgbfield::BGBYVOL] = 0.0;
419 cell[fsgrids::bgbfield::dBGBxdy] = 0.0;
420 cell[fsgrids::bgbfield::dBGBzdy] = 0.0;
421 cell[fsgrids::bgbfield::dBGBydx] = 0.0;
422 cell[fsgrids::bgbfield::dBGBydz] = 0.0;
427 }
428
429 doZeroOut = P::zcells_ini ==1 && zeroOutComponents_l[2]==1;
430 if(doZeroOut) {
431 auto& cell = bgb[stencil.ooo()];
432 cell[fsgrids::bgbfield::BGBX] = 0;
433 cell[fsgrids::bgbfield::BGBY] = 0;
434 cell[fsgrids::bgbfield::BGBYVOL] = 0.0;
435 cell[fsgrids::bgbfield::BGBXVOL] = 0.0;
436 cell[fsgrids::bgbfield::dBGBxdy] = 0.0;
437 cell[fsgrids::bgbfield::dBGBxdz] = 0.0;
438 cell[fsgrids::bgbfield::dBGBydx] = 0.0;
439 cell[fsgrids::bgbfield::dBGBydz] = 0.0;
444 }
445
446 // Remove dipole from inflow cells if this is requested
447 if(noDipoleInSW_l) {
448 auto& cell = bgb[stencil.ooo()];
449 if (sysBoundaryFlag == sysboundarytype::MAXWELLIAN) {
450 cell.fill(0.0);
451 if ( (dipoleType_l==4) && (P::isRestart == false) ) {
452 // If we set BGB to zero here, then we should also set perB in new runs to zero.
453 auto& pb = perb[stencil.ooo()];
454 pb.fill(0.0);
455 }
456 }
457 }
458 });
459
460 phiprof::Timer addConstantTimer {"add-constant-field"};
461 // Superimpose constant background field if needed
462 if(constBgB_l[0] != 0.0 || constBgB_l[1] != 0.0 || constBgB_l[2] != 0.0) {
463 ConstantField bgConstantField;
464 bgConstantField.initialize(constBgB_l[0], constBgB_l[1], constBgB_l[2]);
465 setBackgroundField(bgConstantField, bgb, technical, fsgrid, true);
466 SBC::ionosphereGrid.setConstantBackgroundField(constBgB_l);
467 }
468 addConstantTimer.stop();
469 phiprof::Timer storeNodeTimer {"ionosphereGrid.storeNodeB"};
470 SBC::ionosphereGrid.storeNodeB();
471 storeNodeTimer.stop();
472 }
473
474 /* Evaluates local SpatialCell properties for the project and population,
475 then loops over the requested velocity blocks
476 and populates them
477 */
479 const uint popID,
480 const uint nRequested
481 ) const {
482 const MagnetosphereSpeciesParameters& sP = this->speciesParams[popID];
483
484 // Fetch spatial cell center coordinates
485 const Real x = cell->parameters[CellParams::XCRD] + 0.5*cell->parameters[CellParams::DX];
486 const Real y = cell->parameters[CellParams::YCRD] + 0.5*cell->parameters[CellParams::DY];
487 const Real z = cell->parameters[CellParams::ZCRD] + 0.5*cell->parameters[CellParams::DZ];
488
489 const Real mass = getObjectWrapper().particleSpecies[popID].mass;
490 Real initRho = sP.rho;
491 Real initT = sP.T;
492 // getV0() includes tapering
493 std::array<Real, 3> initV0 = this->getV0(x, y, z, popID)[0];
494 const Real initV0X = initV0[0];
495 const Real initV0Y = initV0[1];
496 const Real initV0Z = initV0[2];
497
498 Real radius = this->geometryRadius(x,y,z);
499 if(radius < sP.taperOuterRadius) {
500 // sine tapering
501 initRho = sP.rho - (sP.rho-sP.ionosphereRho)*0.5*(1.0+sin(M_PI*(radius-sP.taperInnerRadius)/(sP.taperOuterRadius-sP.taperInnerRadius)+0.5*M_PI));
502 initT = sP.T - (sP.T-sP.ionosphereT)*0.5*(1.0+sin(M_PI*(radius-sP.taperInnerRadius)/(sP.taperOuterRadius-sP.taperInnerRadius)+0.5*M_PI));
503 if(radius <= sP.taperInnerRadius) {
504 initRho = sP.ionosphereRho;
505 initT = sP.ionosphereT;
506 }
507 }
508
509 #ifdef USE_GPU
512 #else
515 #endif
516 // Loop over blocks
517 Realf rhosum = 0;
519 {WID, WID, WID, nRequested},
520 ARCH_LOOP_LAMBDA (const uint i, const uint j, const uint k, const uint initIndex, Realf *lsum ) {
521 vmesh::GlobalID *GIDlist = vmesh->getGrid()->data();
522 Realf* bufferData = VBC->getData();
523 const vmesh::GlobalID blockGID = GIDlist[initIndex];
524 // Calculate parameters for new block
525 Real blockCoords[6];
526 vmesh->getBlockInfo(blockGID,&blockCoords[0]);
527 creal vxBlock = blockCoords[0];
528 creal vyBlock = blockCoords[1];
529 creal vzBlock = blockCoords[2];
530 creal dvxCell = blockCoords[3];
531 creal dvyCell = blockCoords[4];
532 creal dvzCell = blockCoords[5];
533 ARCH_INNER_BODY(i, j, k, initIndex, lsum) {
534 creal vx = vxBlock + (i+0.5)*dvxCell - initV0X;
535 creal vy = vyBlock + (j+0.5)*dvyCell - initV0Y;
536 creal vz = vzBlock + (k+0.5)*dvzCell - initV0Z;
537 const Realf value = MaxwellianPhaseSpaceDensity(vx,vy,vz,initT,initRho,mass);
538 bufferData[initIndex*WID3 + k*WID2 + j*WID + i] = value;
539 //lsum[0] += value;
540 };
541 }, rhosum);
542 return rhosum;
543 }
544
545 /* Evaluates local SpatialCell properties for the project and population,
546 then evaluates the phase-space density at the given coordinates.
547 Used as a probe for projectTriAxisSearch.
548 */
550 const uint popID,
551 Real vx_in, Real vy_in, Real vz_in
552 ) const {
553 const MagnetosphereSpeciesParameters& sP = this->speciesParams[popID];
554
555 // Fetch spatial cell center coordinates
556 const Real x = cell->parameters[CellParams::XCRD] + 0.5*cell->parameters[CellParams::DX];
557 const Real y = cell->parameters[CellParams::YCRD] + 0.5*cell->parameters[CellParams::DY];
558 const Real z = cell->parameters[CellParams::ZCRD] + 0.5*cell->parameters[CellParams::DZ];
559
560 const Real mass = getObjectWrapper().particleSpecies[popID].mass;
561 Real initRho = sP.rho;
562 Real initT = sP.T;
563 // getV0() includes tapering
564 std::array<Real, 3> initV0 = this->getV0(x, y, z, popID)[0];
565 const Real initV0X = initV0[0];
566 const Real initV0Y = initV0[1];
567 const Real initV0Z = initV0[2];
568
569 Real radius = this->geometryRadius(x,y,z);
570 if(radius < sP.taperOuterRadius) {
571 // sine tapering
572 initRho = sP.rho - (sP.rho-sP.ionosphereRho)*0.5*(1.0+sin(M_PI*(radius-sP.taperInnerRadius)/(sP.taperOuterRadius-sP.taperInnerRadius)+0.5*M_PI));
573 initT = sP.T - (sP.T-sP.ionosphereT)*0.5*(1.0+sin(M_PI*(radius-sP.taperInnerRadius)/(sP.taperOuterRadius-sP.taperInnerRadius)+0.5*M_PI));
574 if(radius <= sP.taperInnerRadius) {
575 initRho = sP.ionosphereRho;
576 initT = sP.ionosphereT;
577 }
578 }
579
580 creal vx = vx_in - initV0X;
581 creal vy = vy_in - initV0Y;
582 creal vz = vz_in - initV0Z;
583 const Realf value = MaxwellianPhaseSpaceDensity(vx,vy,vz,initT,initRho,mass);
584 return value;
585 }
586
587 vector<std::array<Real, 3> > Magnetosphere::getV0(
588 creal x,
589 creal y,
590 creal z,
591 const uint popID
592 ) const {
593 const MagnetosphereSpeciesParameters& sP = this->speciesParams[popID];
594
595 vector<std::array<Real, 3> > centerPoints;
596 std::array<Real, 3> V0 {{sP.V0[0], sP.V0[1], sP.V0[2]}};
597 std::array<Real, 3> ionosphereV0 = {{sP.ionosphereV0[0], sP.ionosphereV0[1], sP.ionosphereV0[2]}};
598
599 Real radius = this->geometryRadius(x,y,z);
600 if(radius < sP.taperOuterRadius) {
601 // sine tapering
602 Real q=0.5*(1.0-sin(M_PI*(radius-sP.taperInnerRadius)/(sP.taperOuterRadius-sP.taperInnerRadius)+0.5*M_PI));
603
604 for(uint i=0; i<3; i++) {
605 V0[i]=q*(V0[i]-ionosphereV0[i])+ionosphereV0[i];
606 if(radius <= sP.taperInnerRadius) {
607 V0[i] = ionosphereV0[i];
608 }
609 }
610 }
611
612 centerPoints.push_back(V0);
613 return centerPoints;
614 }
615
616 bool Magnetosphere::refineSpatialCells( dccrg::Dccrg<spatial_cell::SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid ) const {
617 phiprof::Timer refineSCTimer {"Magnetosphere: refine spatial cells"};
618
619 int myRank;
620 MPI_Comm_rank(MPI_COMM_WORLD,&myRank);
621
622 if(myRank == MASTER_RANK) {
623 std::cout << "Maximum refinement level is " << mpiGrid.mapping.get_maximum_refinement_level() << std::endl;
624 }
625
626 std::vector<CellID> cells = getLocalCells();
627
628 // L1 refinement.
630 //#pragma omp parallel for
631 for (uint i = 0; i < cells.size(); ++i) {
632 CellID id = cells[i];
633 std::array<double,3> xyz = mpiGrid.get_center(id);
634
635 Real radius2 = pow(xyz[0], 2) + pow(xyz[1], 2) + pow(xyz[2], 2);
636 bool inSphere = radius2 < refine_L1radius*refine_L1radius;
637 bool inTail = xyz[0] < 0 && fabs(xyz[1]) < refine_L1radius && fabs(xyz[2]) < refine_L1tailthick;
638 if ((inSphere || inTail) && radius2 < P::refineRadius * P::refineRadius) {
639 //#pragma omp critical
640 mpiGrid.refine_completely(id);
641 }
642 }
643
644 cells = mpiGrid.stop_refining();
645 if (myRank == MASTER_RANK) {
646 std::cout << "Finished first level of refinement" << endl;
647 }
648 #ifdef DEBUG_VLASIATOR
649 if (cells.size() > 0) {
650 std::cout << "Rank " << myRank << " refined " << cells.size() << " cells to level 1" << std::endl;
651 }
652 #endif //DEBUG_VLASIATOR
653 }
654
655 // L2 refinement.
657 //#pragma omp parallel for
658 for (uint i = 0; i < cells.size(); ++i) {
659 CellID id = cells[i];
660 std::array<double,3> xyz = mpiGrid.get_center(id);
661
662 Real radius2 = pow(xyz[0], 2) + pow(xyz[1], 2) + pow(xyz[2], 2);
663 bool inSphere = radius2 < pow(refine_L2radius, 2);
664 bool inTail = xyz[0] < 0 && fabs(xyz[1]) < refine_L2radius && fabs(xyz[2])<refine_L2tailthick;
665 if ((inSphere || inTail) && radius2 < P::refineRadius * P ::refineRadius) {
666 //#pragma omp critical
667 mpiGrid.refine_completely(id);
668 }
669 }
670 cells = mpiGrid.stop_refining();
671 if(myRank == MASTER_RANK) {
672 std::cout << "Finished second level of refinement" << endl;
673 }
674 #ifdef DEBUG_VLASIATOR
675 if (cells.size() > 0) {
676 std::cout << "Rank " << myRank << " refined " << cells.size() << " cells to level 2" << std::endl;
677 }
678 #endif //DEBUG_VLASIATOR
679
680 }
681
682 // L3 refinement.
684 //#pragma omp parallel for
685 for (uint i = 0; i < cells.size(); ++i) {
686 CellID id = cells[i];
687 std::array<double,3> xyz = mpiGrid.get_center(id);
688
689 Real radius2 = pow(xyz[0], 2) + pow(xyz[1], 2) + pow(xyz[2], 2);
690 bool inNoseCap = (xyz[0]>refine_L3nosexmin) && (radius2<refine_L3radius*refine_L3radius);
691 bool inTail = (xyz[0]>refine_L3tailxmin) && (xyz[0]<refine_L3tailxmax) && (fabs(xyz[1])<refine_L3tailwidth) && (fabs(xyz[2])<refine_L3tailheight);
692 if ((inNoseCap || inTail) && radius2 < P::refineRadius * P::refineRadius) {
693 //#pragma omp critical
694 mpiGrid.refine_completely(id);
695 }
696 }
697 cells = mpiGrid.stop_refining();
698 if (myRank == MASTER_RANK) {
699 std::cout << "Finished third level of refinement" << endl;
700 }
701 #ifdef DEBUG_VLASIATOR
702 if (cells.size() > 0) {
703 std::cout << "Rank " << myRank << " refined " << cells.size() << " cells to level 3" << std::endl;
704 }
705 #endif //DEBUG_VLASIATOR
706 }
707
708 // L4 refinement.
710 //#pragma omp parallel for
711 for (uint i = 0; i < cells.size(); ++i) {
712 CellID id = cells[i];
713 std::array<double,3> xyz = mpiGrid.get_center(id);
714
715 Real radius2 = (xyz[0]*xyz[0]+xyz[1]*xyz[1]+xyz[2]*xyz[2]);
716
717 // Check if cell is within the nose cap
718 bool inNose = refine_L4nosexmin && radius2<refine_L4radius*refine_L4radius;
719 if (inNose && radius2 < P::refineRadius * P::refineRadius) {
720 //#pragma omp critical
721 mpiGrid.refine_completely(id);
722 }
723 }
724
725 cells = mpiGrid.stop_refining();
726 if (myRank == MASTER_RANK) {
727 std::cout << "Finished fourth level of refinement" << endl;
728 }
729 #ifdef DEBUG_VLASIATOR
730 if (cells.size() > 0) {
731 std::cout << "Rank " << myRank << " refined " << cells.size() << " cells to level 4" << std::endl;
732 }
733 #endif //DEBUG_VLASIATOR
734 }
735
736 return true;
737 }
738
739 bool Magnetosphere::forceRefinement( dccrg::Dccrg<spatial_cell::SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid, int n ) const {
740
741 int myRank;
742 MPI_Comm_rank(MPI_COMM_WORLD,&myRank);
743
744 if(myRank == MASTER_RANK) {
745 std::cout << "Maximum refinement level is " << mpiGrid.mapping.get_maximum_refinement_level() << std::endl;
746 }
747
748 for (CellID id : getLocalCells()) {
749 std::array<double,3> xyz {mpiGrid.get_center(id)};
750 Real radius2 {pow(xyz[0], 2) + pow(xyz[1], 2) + pow(xyz[2], 2)};
751 int refLevel {mpiGrid.get_refinement_level(id)};
752 int refineTarget {0};
753
755 bool inSphere = radius2 < refine_L1radius*refine_L1radius;
756 bool inTail = xyz[0] < 0 && fabs(xyz[1]) < refine_L1radius && fabs(xyz[2]) < refine_L1tailthick;
757 if ((inSphere || inTail) && radius2 < P::refineRadius * P ::refineRadius)
758 ++refineTarget;
759 }
761 bool inSphere = radius2 < pow(refine_L2radius, 2);
762 bool inTail = xyz[0] < 0 && fabs(xyz[1]) < refine_L2radius && fabs(xyz[2])<refine_L2tailthick;
763 if ((inSphere || inTail) && radius2 < P::refineRadius * P ::refineRadius)
764 ++refineTarget;
765 }
767 bool inNoseCap = (xyz[0]>refine_L3nosexmin) && (radius2<refine_L3radius*refine_L3radius);
768 bool inTail = (xyz[0]>refine_L3tailxmin) && (xyz[0]<refine_L3tailxmax) && (fabs(xyz[1])<refine_L3tailwidth) && (fabs(xyz[2])<refine_L3tailheight);
769 if ((inNoseCap || inTail) && radius2 < P::refineRadius * P ::refineRadius)
770 ++refineTarget;
771 }
773 bool inNose = refine_L4nosexmin && radius2<refine_L4radius*refine_L4radius;
774 if (inNose && radius2 < P::refineRadius * P ::refineRadius)
775 ++refineTarget;
776 }
777
778 if (!canRefine(mpiGrid[id])) {
779 mpiGrid.dont_refine(id);
780 mpiGrid.dont_unrefine(id);
781 } else if (refLevel <= n && refLevel < refineTarget) {
782 mpiGrid.refine_completely(id);
783 } else if (refLevel >= mpiGrid.mapping.get_maximum_refinement_level() - n && refLevel > refineTarget) {
784 mpiGrid.unrefine_completely(id);
785 } else {
786 mpiGrid.dont_unrefine(id);
787 }
788 }
789
790 return true;
791 }
792
793} // namespace projects
q
Definition Dispersion.m:49
for i
Definition Dispersion.m:24
sqrt(1.0+vA *vA/(c *c))) % Ion-acoustic wave cS
#define ARCH_INNER_BODY(...)
#define ARCH_LOOP_LAMBDA
void setBackgroundFieldToZero(FieldSolverGrid &fsgrid, fsgrids::technicalspan technical, fsgrids::bgbspan bgb)
void setBackgroundField(const FieldFunction &bgFunction, fsgrids::bgbspan bgb, fsgrids::technicalspan technical, FieldSolverGrid &fsgrid, bool append)
void setPerturbedField(const FieldFunction &bfFunction, std::span< std::array< Real, numFields > > b, fsgrids::technicalspan technical, FieldSolverGrid &fsgrid, int offset=fsgrids::bfield::PERBX, bool append=false)
void initialize(const double Bx, const double By, const double Bz)
void initialize(const double moment, const double center_x, const double center_y, const double center_z, const double tilt_angle)
Definition dipole.cpp:32
void initialize(const double moment, const double center_x, const double center_y, const double center_z)
SysBoundary contains the SysBoundaryConditions used in the simulation.
Definition sysboundary.h:54
bool existSysBoundary(std::string name)
Boolean check if queried sysboundarycondition exists Note: this queries against the parsed list of na...
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)
virtual void getParameters(void) override
std::vector< MagnetosphereSpeciesParameters > speciesParams
virtual Realf probePhaseSpace(spatial_cell::SpatialCell *cell, const uint popID, Real vx_in, Real vy_in, Real vz_in) const override
bool forceRefinement(dccrg::Dccrg< spatial_cell::SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, int n) const override
std::array< Real, 3 > constBgB
static void addParameters(void)
virtual void setProjectBField(fsgrids::perbspan perb, fsgrids::bgbspan bgb, fsgrids::technicalspan technical, FieldSolverGrid &fsgrid) override
Real geometryRadius(Real x, Real y, Real z) const
virtual std::vector< std::array< Real, 3 > > getV0(creal x, creal y, creal z, const uint popID) const override
Return a vector containing the velocity coordinate of the centre of each ion population in the distri...
virtual void calcCellParameters(spatial_cell::SpatialCell *cell, creal &t) override
virtual bool initialize(void) override
bool refineSpatialCells(dccrg::Dccrg< spatial_cell::SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid) const override
virtual Realf fillPhaseSpace(spatial_cell::SpatialCell *cell, const uint popID, const uint nRequested) const override
virtual bool canRefine(spatial_cell::SpatialCell *cell) const
Definition project.cpp:421
virtual bool initialize()
Definition project.cpp:113
virtual void getParameters()
Definition project.cpp:103
vmesh::VelocityMesh * get_velocity_mesh(const size_t &popID)
vmesh::VelocityBlockContainer * get_velocity_blocks(const size_t &popID)
std::array< Real, CellParams::N_SPATIAL_CELL_PARAMS > parameters
vmesh::VelocityBlockContainer * dev_get_velocity_blocks(const size_t &popID)
vmesh::VelocityMesh * dev_get_velocity_mesh(const size_t &popID)
const std::vector< CellID > & getLocalCells()
Definition main.cpp:39
#define WID
Definition common.h:514
#define MASTER_RANK
Definition common.h:67
const int WID3
Definition common.h:517
const int WID2
Definition common.h:516
const uint32_t cuint
Definition definitions.h:50
float Real
Definition definitions.h:41
uint64_t CellID
Definition definitions.h:54
float Realf
Definition definitions.h:33
fsgrid::FsGrid< FS_STENCIL_WIDTH > FieldSolverGrid
Definition definitions.h:78
const float creal
Definition definitions.h:42
int myRank
Definition gpu_base.cpp:48
const int j
const int k
ObjectWrapper & getObjectWrapper()
Definition main.cpp:33
SphericalTriGrid ionosphereGrid
static void parallel_reduce(const uint(&limits)[NDim], Lambda loop_body, T &sum)
@ BGBYVOL
Definition common.h:379
@ BGBZVDCORR
Definition common.h:383
@ dBGBYVOLdx
Definition common.h:393
@ dBGBZVOLdx
Definition common.h:396
@ BGBY
Definition common.h:376
@ dBGBxdz
Definition common.h:385
@ dBGBydx
Definition common.h:386
@ BGBYVDCORR
Definition common.h:382
@ dBGBYVOLdz
Definition common.h:395
@ BGBXVOL
Definition common.h:378
@ dBGBydz
Definition common.h:387
@ dBGBZVOLdy
Definition common.h:397
@ BGBX
Definition common.h:375
@ dBGBXVOLdy
Definition common.h:391
@ dBGBzdx
Definition common.h:388
@ dBGBxdy
Definition common.h:384
@ BGBXVDCORR
Definition common.h:381
@ dBGBzdy
Definition common.h:389
@ dBGBXVOLdz
Definition common.h:392
std::span< std::array< Real, fsgrids::bfield::N_BFIELD > > perbspan
Definition common.h:434
std::span< technical > technicalspan
Definition common.h:452
std::span< std::array< Real, bgbfield::N_BGB > > bgbspan
Definition common.h:444
@ PERBY
Definition common.h:276
@ PERBZ
Definition common.h:277
@ PERBX
Definition common.h:275
const Real R_E
Definition common.h:575
ARCH_HOSTDEV Realf MaxwellianPhaseSpaceDensity(creal &vx, creal &vy, creal &vz, creal &T, creal &rho, creal &mass)
Definition project.h:45
uint32_t GlobalID
Definition definitions.h:59
SysBoundary sysBoundaryContainer
std::vector< species::Species > particleSpecies
static Real refineRadius
Definition parameters.h:212
static uint zcells_ini
Definition parameters.h:50
static int amrMaxSpatialRefLevel
Definition parameters.h:190
static uint ycells_ini
Definition parameters.h:49
static int amrMaxAllowedSpatialRefLevel
Definition parameters.h:191
static uint xcells_ini
Definition parameters.h:48
static bool isRestart
Definition parameters.h:176
static ARCH_HOSTDEV VecSimple< T > max(VecSimple< T > const &l, VecSimple< T > const &r)