LCOV - code coverage report
Current view: top level - src/magneticField - UF23Field.cpp (source / functions) Coverage Total Hit
Test: coverage.info.cleaned Lines: 98.7 % 304 300
Test Date: 2026-06-18 09:49:19 Functions: 100.0 % 11 11

            Line data    Source code
       1              : #include "crpropa/magneticField/UF23Field.h"
       2              : 
       3              : #include <exception>
       4              : #include <limits>
       5              : #include <string>
       6              : #include <cmath>
       7              : 
       8              : // local helper functions and constants
       9              : namespace uf23 {
      10              : 
      11              :   template<typename T>
      12              :   crpropa::Vector3d CylToCart(const T v, const double cosPhi, const double sinPhi)
      13              :   {
      14          195 :     return crpropa::Vector3d(v[0] * cosPhi - v[1] * sinPhi,
      15          195 :                              v[0] * sinPhi + v[1] * cosPhi,
      16           56 :                              v[2]);
      17              :   }
      18              : 
      19              :   template<typename T>
      20              :   crpropa::Vector3d CartToCyl(const T v, const double cosPhi, const double sinPhi)
      21              :   {
      22            9 :     return crpropa::Vector3d(v[0] * cosPhi + v[1] * sinPhi,
      23              :                              -v[0] * sinPhi + v[1] * cosPhi,
      24              :                              v[2]);
      25              :   }
      26              : 
      27              :   // logistic sigmoid function
      28              :   inline
      29              :   double
      30              :   Sigmoid(const double x, const double x0, const double w)
      31              :   {
      32          185 :     return 1 / (1 + exp(-(x-x0)/w));
      33              :   }
      34              : 
      35              :   // angle between v0 = (cos(phi0), sin(phi0)) and v1 = (cos(phi1), sin(phi1))
      36              :   inline
      37              :   double
      38            6 :   DeltaPhi(const double phi0, const double phi1)
      39              :   {
      40            6 :     return acos(cos(phi1)*cos(phi0) + sin(phi1)*sin(phi0));
      41              :   }
      42              : 
      43              :   // Interal units used in this code.
      44              :   // Convert to crpropa with e.g. uf23::kpc / crpropa::kpc.
      45              :   // The conversion is, however, only needed in the single non-private
      46              :   // getField() method, all other functions use uf23 units.
      47              :   const double kPi = 3.1415926535897932384626;
      48              :   const double kTwoPi = 2*kPi;
      49              :   const double degree = kPi/180.;
      50              :   const double kpc = 1;
      51              :   const double microgauss = 1;
      52              :   const double megayear = 1;
      53              :   const double Gpc = 1e6*kpc;
      54              :   const double pc = 1e-3*kpc;
      55              :   const double second  = megayear / (1e6*60*60*24*365.25);
      56              :   const double kilometer = kpc / 3.0856775807e+16;
      57              : }
      58              : 
      59              : namespace crpropa {
      60            8 : UF23Field::UF23Field(const ModelType mt) :
      61            8 :   fModelType(mt),
      62            8 :   fMaxRadiusSquared(pow(30*uf23::kpc, 2))
      63              : {
      64              : 
      65              :   // all but expX model have a-->\infty, Eq.(38)
      66            8 :   fPoloidalA    =  1 * Gpc;
      67              : 
      68            8 :   switch (fModelType) {
      69              :     // ---------------------------------------------
      70            1 :   case base: {
      71            1 :     fDiskB1        =  1.0878565e+00 * uf23::microgauss;
      72            1 :     fDiskB2        =  2.6605034e+00 * uf23::microgauss;
      73            1 :     fDiskB3        =  3.1166311e+00 * uf23::microgauss;
      74            1 :     fDiskH         =  7.9408965e-01 * uf23::kpc;
      75            1 :     fDiskPhase1    =  2.6316589e+02 * uf23::degree;
      76            1 :     fDiskPhase2    =  9.7782269e+01 * uf23::degree;
      77            1 :     fDiskPhase3    =  3.5112281e+01 * uf23::degree;
      78            1 :     fDiskPitch     =  1.0106900e+01 * uf23::degree;
      79            1 :     fDiskW         =  1.0720909e-01 * uf23::kpc;
      80            1 :     fPoloidalB     =  9.7775487e-01 * uf23::microgauss;
      81            1 :     fPoloidalP     =  1.4266186e+00 * uf23::kpc;
      82            1 :     fPoloidalR     =  7.2925417e+00 * uf23::kpc;
      83            1 :     fPoloidalW     =  1.1188158e-01 * uf23::kpc;
      84            1 :     fPoloidalZ     =  4.4597373e+00 * uf23::kpc;
      85            1 :     fStriation     =  3.4557571e-01;
      86            1 :     fToroidalBN    =  3.2556760e+00 * uf23::microgauss;
      87            1 :     fToroidalBS    = -3.0914569e+00 * uf23::microgauss;
      88            1 :     fToroidalR     =  1.0193815e+01 * uf23::kpc;
      89            1 :     fToroidalW     =  1.6936993e+00 * uf23::kpc;
      90            1 :     fToroidalZ     =  4.0242749e+00 * uf23::kpc;
      91            1 :     break;
      92              :   }
      93            1 :   case cre10: {
      94              :     // ---------------------------------------------
      95            1 :     fDiskB1        =  1.2035697e+00 * uf23::microgauss;
      96            1 :     fDiskB2        =  2.7478490e+00 * uf23::microgauss;
      97            1 :     fDiskB3        =  3.2104342e+00 * uf23::microgauss;
      98            1 :     fDiskH         =  8.0844932e-01 * uf23::kpc;
      99            1 :     fDiskPhase1    =  2.6515882e+02 * uf23::degree;
     100            1 :     fDiskPhase2    =  9.8211313e+01 * uf23::degree;
     101            1 :     fDiskPhase3    =  3.5944588e+01 * uf23::degree;
     102            1 :     fDiskPitch     =  1.0162759e+01 * uf23::degree;
     103            1 :     fDiskW         =  1.0824003e-01 * uf23::kpc;
     104            1 :     fPoloidalB     =  9.6938453e-01 * uf23::microgauss;
     105            1 :     fPoloidalP     =  1.4150957e+00 * uf23::kpc;
     106            1 :     fPoloidalR     =  7.2987296e+00 * uf23::kpc;
     107            1 :     fPoloidalW     =  1.0923051e-01 * uf23::kpc;
     108            1 :     fPoloidalZ     =  4.5748332e+00 * uf23::kpc;
     109            1 :     fStriation     =  2.4950386e-01;
     110            1 :     fToroidalBN    =  3.7308133e+00 * uf23::microgauss;
     111            1 :     fToroidalBS    = -3.5039958e+00 * uf23::microgauss;
     112            1 :     fToroidalR     =  1.0407507e+01 * uf23::kpc;
     113            1 :     fToroidalW     =  1.7398375e+00 * uf23::kpc;
     114            1 :     fToroidalZ     =  2.9272800e+00 * uf23::kpc;
     115            1 :     break;
     116              :   }
     117            1 :   case nebCor: {
     118              :     // ---------------------------------------------
     119            1 :     fDiskB1        =  1.4081935e+00 * uf23::microgauss;
     120            1 :     fDiskB2        =  3.5292400e+00 * uf23::microgauss;
     121            1 :     fDiskB3        =  4.1290147e+00 * uf23::microgauss;
     122            1 :     fDiskH         =  8.1151971e-01 * uf23::kpc;
     123            1 :     fDiskPhase1    =  2.6447529e+02 * uf23::degree;
     124            1 :     fDiskPhase2    =  9.7572660e+01 * uf23::degree;
     125            1 :     fDiskPhase3    =  3.6403798e+01 * uf23::degree;
     126            1 :     fDiskPitch     =  1.0151183e+01 * uf23::degree;
     127            1 :     fDiskW         =  1.1863734e-01 * uf23::kpc;
     128            1 :     fPoloidalB     =  1.3485916e+00 * uf23::microgauss;
     129            1 :     fPoloidalP     =  1.3414395e+00 * uf23::kpc;
     130            1 :     fPoloidalR     =  7.2473841e+00 * uf23::kpc;
     131            1 :     fPoloidalW     =  1.4318227e-01 * uf23::kpc;
     132            1 :     fPoloidalZ     =  4.8242603e+00 * uf23::kpc;
     133            1 :     fStriation     =  3.8610837e-10;
     134            1 :     fToroidalBN    =  4.6491142e+00 * uf23::microgauss;
     135            1 :     fToroidalBS    = -4.5006610e+00 * uf23::microgauss;
     136            1 :     fToroidalR     =  1.0205288e+01 * uf23::kpc;
     137            1 :     fToroidalW     =  1.7004868e+00 * uf23::kpc;
     138            1 :     fToroidalZ     =  3.5557767e+00 * uf23::kpc;
     139            1 :     break;
     140              :   }
     141            1 :   case neCL: {
     142              :     // ---------------------------------------------
     143            1 :     fDiskB1        =  1.4259645e+00 * uf23::microgauss;
     144            1 :     fDiskB2        =  1.3543223e+00 * uf23::microgauss;
     145            1 :     fDiskB3        =  3.4390669e+00 * uf23::microgauss;
     146            1 :     fDiskH         =  6.7405199e-01 * uf23::kpc;
     147            1 :     fDiskPhase1    =  1.9961898e+02 * uf23::degree;
     148            1 :     fDiskPhase2    =  1.3541461e+02 * uf23::degree;
     149            1 :     fDiskPhase3    =  6.4909767e+01 * uf23::degree;
     150            1 :     fDiskPitch     =  1.1867859e+01 * uf23::degree;
     151            1 :     fDiskW         =  6.1162799e-02 * uf23::kpc;
     152            1 :     fPoloidalB     =  9.8387831e-01 * uf23::microgauss;
     153            1 :     fPoloidalP     =  1.6773615e+00 * uf23::kpc;
     154            1 :     fPoloidalR     =  7.4084361e+00 * uf23::kpc;
     155            1 :     fPoloidalW     =  1.4168192e-01 * uf23::kpc;
     156            1 :     fPoloidalZ     =  3.6521188e+00 * uf23::kpc;
     157            1 :     fStriation     =  3.3600213e-01;
     158            1 :     fToroidalBN    =  2.6256593e+00 * uf23::microgauss;
     159            1 :     fToroidalBS    = -2.5699466e+00 * uf23::microgauss;
     160            1 :     fToroidalR     =  1.0134257e+01 * uf23::kpc;
     161            1 :     fToroidalW     =  1.1547728e+00 * uf23::kpc;
     162            1 :     fToroidalZ     =  4.5585463e+00 * uf23::kpc;
     163            1 :     break;
     164              :   }
     165            1 :   case spur: {
     166              :     // ---------------------------------------------
     167            1 :     fDiskB1        = -4.2993328e+00 * uf23::microgauss;
     168            1 :     fDiskH         =  7.5019749e-01 * uf23::kpc;
     169            1 :     fDiskPhase1    =  1.5589875e+02 * uf23::degree;
     170            1 :     fDiskPitch     =  1.2074432e+01 * uf23::degree;
     171            1 :     fDiskW         =  1.2263120e-01 * uf23::kpc;
     172            1 :     fPoloidalB     =  9.9302987e-01 * uf23::microgauss;
     173            1 :     fPoloidalP     =  1.3982374e+00 * uf23::kpc;
     174            1 :     fPoloidalR     =  7.1973387e+00 * uf23::kpc;
     175            1 :     fPoloidalW     =  1.2262244e-01 * uf23::kpc;
     176            1 :     fPoloidalZ     =  4.4853270e+00 * uf23::kpc;
     177            1 :     fSpurCenter    =  1.5718686e+02 * uf23::degree;
     178            1 :     fSpurLength    =  3.1839577e+01 * uf23::degree;
     179            1 :     fSpurWidth     =  1.0318114e+01 * uf23::degree;
     180            1 :     fStriation     =  3.3022369e-01;
     181            1 :     fToroidalBN    =  2.9286724e+00 * uf23::microgauss;
     182            1 :     fToroidalBS    = -2.5979895e+00 * uf23::microgauss;
     183            1 :     fToroidalR     =  9.7536425e+00 * uf23::kpc;
     184            1 :     fToroidalW     =  1.4210055e+00 * uf23::kpc;
     185            1 :     fToroidalZ     =  6.0941229e+00 * uf23::kpc;
     186            1 :     break;
     187              :   }
     188            1 :   case synCG: {
     189              :     // ---------------------------------------------
     190            1 :     fDiskB1        =  8.1386878e-01 * uf23::microgauss;
     191            1 :     fDiskB2        =  2.0586930e+00 * uf23::microgauss;
     192            1 :     fDiskB3        =  2.9437335e+00 * uf23::microgauss;
     193            1 :     fDiskH         =  6.2172353e-01 * uf23::kpc;
     194            1 :     fDiskPhase1    =  2.2988551e+02 * uf23::degree;
     195            1 :     fDiskPhase2    =  9.7388282e+01 * uf23::degree;
     196            1 :     fDiskPhase3    =  3.2927367e+01 * uf23::degree;
     197            1 :     fDiskPitch     =  9.9034844e+00 * uf23::degree;
     198            1 :     fDiskW         =  6.6517521e-02 * uf23::kpc;
     199            1 :     fPoloidalB     =  8.0883734e-01 * uf23::microgauss;
     200            1 :     fPoloidalP     =  1.5820957e+00 * uf23::kpc;
     201            1 :     fPoloidalR     =  7.4625235e+00 * uf23::kpc;
     202            1 :     fPoloidalW     =  1.5003765e-01 * uf23::kpc;
     203            1 :     fPoloidalZ     =  3.5338550e+00 * uf23::kpc;
     204            1 :     fStriation     =  6.3434763e-01;
     205            1 :     fToroidalBN    =  2.3991193e+00 * uf23::microgauss;
     206            1 :     fToroidalBS    = -2.0919944e+00 * uf23::microgauss;
     207            1 :     fToroidalR     =  9.4227834e+00 * uf23::kpc;
     208            1 :     fToroidalW     =  9.1608418e-01 * uf23::kpc;
     209            1 :     fToroidalZ     =  5.5844594e+00 * uf23::kpc;
     210            1 :     break;
     211              :   }
     212            1 :   case twistX: {
     213              :     // ---------------------------------------------
     214            1 :     fDiskB1        =  1.3741995e+00 * uf23::microgauss;
     215            1 :     fDiskB2        =  2.0089881e+00 * uf23::microgauss;
     216            1 :     fDiskB3        =  1.5212463e+00 * uf23::microgauss;
     217            1 :     fDiskH         =  9.3806180e-01 * uf23::kpc;
     218            1 :     fDiskPhase1    =  2.3560316e+02 * uf23::degree;
     219            1 :     fDiskPhase2    =  1.0189856e+02 * uf23::degree;
     220            1 :     fDiskPhase3    =  5.6187572e+01 * uf23::degree;
     221            1 :     fDiskPitch     =  1.2100979e+01 * uf23::degree;
     222            1 :     fDiskW         =  1.4933338e-01 * uf23::kpc;
     223            1 :     fPoloidalB     =  6.2793114e-01 * uf23::microgauss;
     224            1 :     fPoloidalP     =  2.3292519e+00 * uf23::kpc;
     225            1 :     fPoloidalR     =  7.9212358e+00 * uf23::kpc;
     226            1 :     fPoloidalW     =  2.9056201e-01 * uf23::kpc;
     227            1 :     fPoloidalZ     =  2.6274437e+00 * uf23::kpc;
     228            1 :     fStriation     =  7.7616317e-01;
     229            1 :     fTwistingTime  =  5.4733549e+01 * uf23::megayear;
     230            1 :     break;
     231              :   }
     232            1 :   case expX: {
     233              :     // ---------------------------------------------
     234            1 :     fDiskB1        =  9.9258148e-01 * uf23::microgauss;
     235            1 :     fDiskB2        =  2.1821124e+00 * uf23::microgauss;
     236            1 :     fDiskB3        =  3.1197345e+00 * uf23::microgauss;
     237            1 :     fDiskH         =  7.1508681e-01 * uf23::kpc;
     238            1 :     fDiskPhase1    =  2.4745741e+02 * uf23::degree;
     239            1 :     fDiskPhase2    =  9.8578879e+01 * uf23::degree;
     240            1 :     fDiskPhase3    =  3.4884485e+01 * uf23::degree;
     241            1 :     fDiskPitch     =  1.0027070e+01 * uf23::degree;
     242            1 :     fDiskW         =  9.8524736e-02 * uf23::kpc;
     243            1 :     fPoloidalA     =  6.1938701e+00 * uf23::kpc;
     244            1 :     fPoloidalB     =  5.8357990e+00 * uf23::microgauss;
     245            1 :     fPoloidalP     =  1.9510779e+00 * uf23::kpc;
     246            1 :     fPoloidalR     =  2.4994376e+00 * uf23::kpc;
     247              :     // internally, xi is fitted and z = tan(xi)*a
     248            1 :     fPoloidalXi    =  2.0926122e+01 * uf23::degree;
     249            1 :     fPoloidalZ     =  fPoloidalA*tan(fPoloidalXi);
     250            1 :     fStriation     =  5.1440500e-01;
     251            1 :     fToroidalBN    =  2.7077434e+00 * uf23::microgauss;
     252            1 :     fToroidalBS    = -2.5677104e+00 * uf23::microgauss;
     253            1 :     fToroidalR     =  1.0134022e+01 * uf23::kpc;
     254            1 :     fToroidalW     =  2.0956159e+00 * uf23::kpc;
     255            1 :     fToroidalZ     =  5.4564991e+00 * uf23::kpc;
     256            1 :     break;
     257              :   }
     258            0 :   default: {
     259            0 :     throw std::runtime_error("unknown field model");
     260              :     break;
     261              :   }
     262              :   }
     263              : 
     264            8 :   fSinPitch = sin(fDiskPitch);
     265            8 :   fCosPitch = cos(fDiskPitch);
     266            8 :   fTanPitch = tan(fDiskPitch);
     267              : 
     268            8 : }
     269              : 
     270              : Vector3d
     271           72 : UF23Field::getField(const Vector3d &position)
     272              :   const
     273              : {
     274              :   Vector3d posInKpc = position / kpc;
     275           72 :   return (this->operator()(posInKpc)) / uf23::microgauss * microgauss;
     276              : }
     277              : 
     278              : 
     279              : Vector3d
     280           72 : UF23Field::operator()(const Vector3d& posInKpc)
     281              :   const
     282              : {
     283              :   const auto pos = posInKpc * uf23::kpc;
     284           72 :   if (pos.getR2() > fMaxRadiusSquared)
     285              :     return Vector3d(0, 0, 0);
     286              :   else {
     287           72 :     const auto diskField = getDiskField(pos);
     288           72 :     const auto haloField = getHaloField(pos);
     289              :     return (diskField + haloField) / uf23::microgauss;
     290              :   }
     291              : }
     292              : 
     293              : Vector3d
     294           72 : UF23Field::getDiskField(const Vector3d& pos)
     295              :   const
     296              : {
     297           72 :   if (fModelType == spur)
     298            9 :     return getSpurField(pos.x, pos.y, pos.z);
     299              :   else
     300           63 :     return getSpiralField(pos.x, pos.y, pos.z);
     301              : }
     302              : 
     303              : 
     304              : Vector3d
     305           72 : UF23Field::getHaloField(const Vector3d& pos)
     306              :   const
     307              : {
     308           72 :   if (fModelType == twistX)
     309            9 :     return getTwistedHaloField(pos.x, pos.y, pos.z);
     310              :   else
     311              :     return
     312           63 :       getToroidalHaloField(pos.x, pos.y, pos.z) +
     313          126 :       getPoloidalHaloField(pos.x, pos.y, pos.z);
     314              : }
     315              : 
     316              : 
     317              : Vector3d
     318            9 : UF23Field::getTwistedHaloField(const double x, const double y, const double z)
     319              :   const
     320              : {
     321            9 :   const double r = sqrt(x*x + y*y);
     322            9 :   const double cosPhi = r > std::numeric_limits<double>::min() ? x / r : 1;
     323            8 :   const double sinPhi = r > std::numeric_limits<double>::min() ? y / r : 0;
     324              : 
     325            9 :   const Vector3d bXCart = getPoloidalHaloField(x, y, z);
     326            9 :   const double bXCartTmp[3] = {bXCart.x, bXCart.y, bXCart.z};
     327              :   const Vector3d bXCyl = uf23::CartToCyl(bXCartTmp, cosPhi, sinPhi);
     328              : 
     329              :   const double bZ = bXCyl.z;
     330              :   const double bR = bXCyl.x;
     331              : 
     332              :   double bPhi = 0;
     333              : 
     334            9 :   if (fTwistingTime != 0 && r != 0) {
     335              :     // radial rotation curve parameters (fit to Reid et al 2014)
     336              :     const double v0 = -240 * uf23::kilometer/uf23::second;
     337              :     const double r0 = 1.6 * uf23::kpc;
     338              :     // vertical gradient (Levine+08)
     339              :     const double z0 = 10 * uf23::kpc;
     340              : 
     341              :     // Eq.(43)
     342            8 :     const double fr = 1 - exp(-r/r0);
     343              :     // Eq.(44)
     344            8 :     const double t0 = exp(2*std::abs(z)/z0);
     345            8 :     const double gz = 2 / (1 + t0);
     346              : 
     347              :     // Eq. (46)
     348            8 :     const double signZ = z < 0 ? -1 : 1;
     349            8 :     const double deltaZ =  -signZ * v0 * fr / z0  * t0 * pow(gz, 2);
     350              :     // Eq. (47)
     351            8 :     const double deltaR = v0 * ((1-fr)/r0 - fr/r) * gz;
     352              : 
     353              :     // Eq.(45)
     354            8 :     bPhi = (bZ * deltaZ + bR * deltaR) * fTwistingTime;
     355              : 
     356              :   }
     357              :   const double bCylX[3] = {bR, bPhi , bZ};
     358            9 :   return uf23::CylToCart(bCylX, cosPhi, sinPhi);
     359              : }
     360              : 
     361              : Vector3d
     362           63 : UF23Field::getToroidalHaloField(const double x, const double y, const double z)
     363              :   const
     364              : {
     365           63 :   const double r2 = x*x + y*y;
     366           63 :   const double r = sqrt(r2);
     367              :   const double absZ = std::abs(z);
     368              : 
     369           63 :   const double b0 = z >= 0 ? fToroidalBN : fToroidalBS;
     370           63 :   const double rh = fToroidalR;
     371           63 :   const double z0 = fToroidalZ;
     372           63 :   const double fwh = fToroidalW;
     373              :   const double sigmoidR = uf23::Sigmoid(r, rh, fwh);
     374           63 :   const double sigmoidZ = uf23::Sigmoid(absZ, fDiskH, fDiskW);
     375              : 
     376              :   // Eq. (21)
     377           63 :   const double bPhi = b0 * (1. - sigmoidR) * sigmoidZ * exp(-absZ/z0);
     378              : 
     379              :   const double bCyl[3] = {0, bPhi, 0};
     380           63 :   const double cosPhi = r > std::numeric_limits<double>::min() ? x / r : 1;
     381           56 :   const double sinPhi = r > std::numeric_limits<double>::min() ? y / r : 0;
     382           63 :   return uf23::CylToCart(bCyl, cosPhi, sinPhi);
     383              : }
     384              : 
     385              : Vector3d
     386           72 : UF23Field::getPoloidalHaloField(const double x, const double y, const double z)
     387              :   const
     388              : {
     389           72 :   const double r2 = x*x + y*y;
     390           72 :   const double r = sqrt(r2);
     391              : 
     392           72 :   const double c = pow(fPoloidalA/fPoloidalZ, fPoloidalP);
     393           72 :   const double a0p = pow(fPoloidalA, fPoloidalP);
     394           72 :   const double rp = pow(r, fPoloidalP);
     395           72 :   const double abszp = pow(std::abs(z), fPoloidalP);
     396           72 :   const double cabszp = c*abszp;
     397              : 
     398              :   /*
     399              :     since $\sqrt{a^2 + b} - a$ is numerical unstable for $b\ll a$,
     400              :     we use $(\sqrt{a^2 + b} - a) \frac{\sqrt{a^2 + b} + a}{\sqrt{a^2
     401              :     + b} + a} = \frac{b}{\sqrt{a^2 + b} + a}$}
     402              :   */
     403              : 
     404           72 :   const double t0 = a0p + cabszp - rp;
     405           72 :   const double t1 = sqrt(pow(t0, 2) + 4*a0p*rp);
     406           72 :   const double ap = 2*a0p*rp / (t1  + t0);
     407              : 
     408              :   double a = 0;
     409           72 :   if (ap < 0) {
     410            0 :     if (r > std::numeric_limits<double>::min()) {
     411              :       // this should never happen
     412            0 :       throw std::runtime_error("ap = " + std::to_string(ap));
     413              :     }
     414              :     else
     415              :       a = 0;
     416              :   }
     417              :   else
     418           72 :     a = pow(ap, 1/fPoloidalP);
     419              : 
     420              :   // Eq.(29) and Eq.(32)
     421              :   const double radialDependence =
     422           72 :     fModelType == expX ?
     423            9 :     exp(-a/fPoloidalR) :
     424           63 :     1 - uf23::Sigmoid(a, fPoloidalR, fPoloidalW);
     425              : 
     426              :   // Eq.(28)
     427           72 :   const double Bzz = fPoloidalB * radialDependence;
     428              : 
     429              :   // (r/a)
     430           72 :   const double rOverA =  1 / pow(2*a0p / (t1  + t0), 1/fPoloidalP);
     431              : 
     432              :   // Eq.(35) for p=n
     433           72 :   const double signZ = z < 0 ? -1 : 1;
     434              :   const double Br =
     435           72 :     Bzz * c * a / rOverA * signZ * pow(std::abs(z), fPoloidalP - 1) / t1;
     436              : 
     437              :   // Eq.(36) for p=n
     438           72 :   const double Bz = Bzz * pow(rOverA, fPoloidalP-2) * (ap + a0p) / t1;
     439              : 
     440           72 :   if (r < std::numeric_limits<double>::min())
     441              :     return Vector3d(0, 0, Bz);
     442              :   else {
     443              :     const double bCylX[3] = {Br, 0 , Bz};
     444           64 :     const double cosPhi =  x / r;
     445           64 :     const double sinPhi =  y / r;
     446              :     return uf23::CylToCart(bCylX, cosPhi, sinPhi);
     447              :   }
     448              : }
     449              : 
     450              : Vector3d
     451            9 : UF23Field::getSpurField(const double x, const double y, const double z)
     452              :   const
     453              : {
     454              :   // reference approximately at solar radius
     455              :   const double rRef = 8.2*uf23::kpc;
     456              : 
     457              :   // cylindrical coordinates
     458            9 :   const double r2 = x*x + y*y;
     459            9 :   const double r = sqrt(r2);
     460            9 :   if (r < std::numeric_limits<double>::min())
     461              :     return Vector3d(0, 0, 0);
     462              : 
     463            8 :   double phi = atan2(y, x);
     464            8 :   if (phi < 0)
     465            4 :     phi += uf23::kTwoPi;
     466              : 
     467            8 :   const double phiRef = fDiskPhase1;
     468              :   int iBest = -2;
     469              :   double bestDist = -1;
     470           32 :   for (int i = -1; i <= 1; ++i) {
     471           24 :     const double pphi = phi - phiRef + i*uf23::kTwoPi;
     472           24 :     const double rr = rRef*exp(pphi * fTanPitch);
     473           24 :     if (bestDist < 0 || std::abs(r-rr) < bestDist) {
     474           11 :       bestDist =  std::abs(r-rr);
     475              :       iBest = i;
     476              :     }
     477              :   }
     478            8 :   if (iBest == 0) {
     479            3 :     const double phi0 = phi - log(r/rRef) / fTanPitch;
     480              : 
     481              :     // Eq. (16)
     482            3 :     const double deltaPhi0 = uf23::DeltaPhi(phiRef, phi0);
     483            3 :     const double delta = deltaPhi0 / fSpurWidth;
     484            3 :     const double B = fDiskB1 * exp(-0.5*pow(delta, 2));
     485              : 
     486              :     // Eq. (18)
     487              :     const double wS = 5*uf23::degree;
     488            3 :     const double phiC = fSpurCenter;
     489            3 :     const double deltaPhiC = uf23::DeltaPhi(phiC, phi);
     490            3 :     const double lC = fSpurLength;
     491            3 :     const double gS = 1 - uf23::Sigmoid(std::abs(deltaPhiC), lC, wS);
     492              : 
     493              :     // Eq. (13)
     494            3 :     const double hd = 1 - uf23::Sigmoid(std::abs(z), fDiskH, fDiskW);
     495              : 
     496              :     // Eq. (17)
     497            3 :     const double bS = rRef/r * B * hd * gS;
     498            3 :     const double bCyl[3] = {bS * fSinPitch, bS * fCosPitch, 0};
     499            3 :     const double cosPhi = x / r;
     500            3 :     const double sinPhi = y / r;
     501              :     return uf23::CylToCart(bCyl, cosPhi, sinPhi);
     502              :   }
     503              :   else
     504              :     return Vector3d(0, 0, 0);
     505              : 
     506              : }
     507              : 
     508              : Vector3d
     509           63 : UF23Field::getSpiralField(const double x, const double y, const double z)
     510              :   const
     511              : {
     512              :   // reference radius
     513              :   const double rRef = 5*uf23::kpc;
     514              :   // inner boundary of spiral field
     515              :   const double rInner = 5*uf23::kpc;
     516              :   const double wInner = 0.5*uf23::kpc;
     517              :   // outer boundary of spiral field
     518              :   const double rOuter = 20*uf23::kpc;
     519              :   const double wOuter = 0.5*uf23::kpc;
     520              : 
     521              :   // cylindrical coordinates
     522           63 :   const double r2 = x*x + y*y;
     523           63 :   if (r2 == 0)
     524              :     return Vector3d(0, 0, 0);
     525           56 :   const double r = sqrt(r2);
     526           56 :   const double phi = atan2(y, x);
     527              : 
     528              :   // Eq.(13)
     529           56 :   const double hdz = 1 - uf23::Sigmoid(std::abs(z), fDiskH, fDiskW);
     530              : 
     531              :   // Eq.(14) times rRef divided by r
     532              :   const double rFacI = uf23::Sigmoid(r, rInner, wInner);
     533           56 :   const double rFacO = 1 - uf23::Sigmoid(r, rOuter, wOuter);
     534              :   // (using lim r--> 0 (1-exp(-r^2))/r --> r - r^3/2 + ...)
     535           56 :   const double rFac =  r > 1e-5*uf23::pc ? (1-exp(-r*r)) / r : r * (1 - r2/2);
     536           56 :   const double gdrTimesRrefByR = rRef * rFac * rFacO * rFacI;
     537              : 
     538              :   // Eq. (12)
     539           56 :   const double phi0 = phi - log(r/rRef) / fTanPitch;
     540              : 
     541              :   // Eq. (10)
     542           56 :   const double b =
     543           56 :     fDiskB1 * cos(1 * (phi0 - fDiskPhase1)) +
     544           56 :     fDiskB2 * cos(2 * (phi0 - fDiskPhase2)) +
     545           56 :     fDiskB3 * cos(3 * (phi0 - fDiskPhase3));
     546              : 
     547              :   // Eq. (11)
     548           56 :   const double fac = hdz * gdrTimesRrefByR;
     549              :   const double bCyl[3] =
     550           56 :     { b * fac * fSinPitch,
     551           56 :       b * fac * fCosPitch,
     552              :       0};
     553              : 
     554           56 :   const double cosPhi = x / r;
     555           56 :   const double sinPhi = y / r;
     556              :   return uf23::CylToCart(bCyl, cosPhi, sinPhi);
     557              : }
     558              : }
        

Generated by: LCOV version 2.0-1