LCOV - code coverage report
Current view: top level - src/magneticField/turbulentField - GridTurbulence.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 89 104 85.6 %
Date: 2024-04-29 14:43:01 Functions: 7 13 53.8 %

          Line data    Source code
       1             : #include "crpropa/magneticField/turbulentField/GridTurbulence.h"
       2             : #include "crpropa/GridTools.h"
       3             : #include "crpropa/Random.h"
       4             : 
       5             : #ifdef CRPROPA_HAVE_FFTW3F
       6             : 
       7             : namespace crpropa {
       8             : 
       9             : 
      10           6 : GridTurbulence::GridTurbulence(const TurbulenceSpectrum &spectrum,
      11             :                                const GridProperties &gridProp,
      12           6 :                                unsigned int seed)
      13           6 :     : TurbulentField(spectrum), seed(seed) {
      14           6 :         initGrid(gridProp);
      15           6 :         checkGridRequirements(gridPtr, spectrum.getLmin(), spectrum.getLmax());
      16           6 :         initTurbulence();
      17           6 : }
      18             : 
      19           6 : void GridTurbulence::initGrid(const GridProperties &p) {
      20           6 :         gridPtr = new Grid3f(p);
      21           6 : }
      22             : 
      23           4 : Vector3d GridTurbulence::getField(const Vector3d &pos) const {
      24           4 :         return gridPtr->interpolate(pos);
      25             : }
      26             : 
      27           1 : const ref_ptr<Grid3f> &GridTurbulence::getGrid() const { return gridPtr; }
      28             : 
      29           6 : void GridTurbulence::initTurbulence() {
      30             : 
      31             :         Vector3d spacing = gridPtr->getSpacing();
      32             :         size_t n = gridPtr->getNx(); // size of array
      33           6 :         size_t n2 = (size_t)floor(n / 2) +
      34             :                     1; // size array in z-direction in configuration space
      35             : 
      36             :         // arrays to hold the complex vector components of the B(k)-field
      37             :         fftwf_complex *Bkx, *Bky, *Bkz;
      38           6 :         Bkx = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * n * n * n2);
      39           6 :         Bky = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * n * n * n2);
      40           6 :         Bkz = (fftwf_complex *)fftwf_malloc(sizeof(fftwf_complex) * n * n * n2);
      41             : 
      42           6 :         Random random;
      43           6 :         if (seed != 0)
      44           4 :                 random.seed(seed); // use given seed
      45             : 
      46             :         // calculate the n possible discrete wave numbers
      47           6 :         double K[n];
      48         390 :         for (size_t i = 0; i < n; i++)
      49         384 :                 K[i] = ((double)i / n - i / (n / 2));
      50             : 
      51             :         // double kMin = 2*M_PI / lMax; // * 2 * spacing.x; // spacing.x / lMax;
      52             :         // double kMax = 2*M_PI / lMin; // * 2 * spacing.x; // spacing.x / lMin;
      53           6 :         double kMin = spacing.x / spectrum.getLmax();
      54           6 :         double kMax = spacing.x / spectrum.getLmin();
      55           6 :         auto lambda = 1 / spacing.x * 2 * M_PI;
      56             : 
      57             :         Vector3f n0(1, 1, 1); // arbitrary vector to construct orthogonal base
      58             : 
      59         390 :         for (size_t ix = 0; ix < n; ix++) {
      60       24960 :                 for (size_t iy = 0; iy < n; iy++) {
      61      835584 :                         for (size_t iz = 0; iz < n2; iz++) {
      62             :         
      63             :                                 Vector3f ek, e1, e2;  // orthogonal base
      64             : 
      65      811008 :                                 size_t i = ix * n * n2 + iy * n2 + iz;
      66      811008 :                                 ek.setXYZ(K[ix], K[iy], K[iz]);
      67      811008 :                                 double k = ek.getR();
      68             : 
      69             :                                 // wave outside of turbulent range -> B(k) = 0
      70      811008 :                                 if ((k < kMin) || (k > kMax)) {
      71      395939 :                                         Bkx[i][0] = 0;
      72      395939 :                                         Bkx[i][1] = 0;
      73      395939 :                                         Bky[i][0] = 0;
      74      395939 :                                         Bky[i][1] = 0;
      75      395939 :                                         Bkz[i][0] = 0;
      76      395939 :                                         Bkz[i][1] = 0;
      77             :                                         continue;
      78             :                                 }
      79             : 
      80             :                                 // construct an orthogonal base ek, e1, e2
      81      415069 :                                 if (ek.isParallelTo(n0, float(1e-3))) {
      82             :                                         // ek parallel to (1,1,1)
      83             :                                         e1.setXYZ(-1., 1., 0);
      84             :                                         e2.setXYZ(1., 1., -2.);
      85             :                                 } else {
      86             :                                         // ek not parallel to (1,1,1)
      87             :                                         e1 = n0.cross(ek);
      88             :                                         e2 = ek.cross(e1);
      89             :                                 }
      90             :                                 e1 /= e1.getR();
      91             :                                 e2 /= e2.getR();
      92             : 
      93             :                                 // random orientation perpendicular to k
      94      415069 :                                 double theta = 2 * M_PI * random.rand();
      95      415069 :                                 Vector3f b = e1 * std::cos(theta) + e2 * std::sin(theta); // real b-field vector
      96             : 
      97             :                                 // normal distributed amplitude with mean = 0
      98      415069 :                                 b *= std::sqrt(spectrum.energySpectrum(k*lambda));
      99             :                                 
     100             :                                 // uniform random phase
     101      415069 :                                 double phase = 2 * M_PI * random.rand();
     102      415069 :                                 double cosPhase = std::cos(phase); // real part
     103      415069 :                                 double sinPhase = std::sin(phase); // imaginary part
     104             : 
     105      415069 :                                 Bkx[i][0] = b.x * cosPhase;
     106      415069 :                                 Bkx[i][1] = b.x * sinPhase;
     107      415069 :                                 Bky[i][0] = b.y * cosPhase;
     108      415069 :                                 Bky[i][1] = b.y * sinPhase;
     109      415069 :                                 Bkz[i][0] = b.z * cosPhase;
     110      415069 :                                 Bkz[i][1] = b.z * sinPhase;
     111             :                         } // for iz
     112             :                 }     // for iy
     113             :         }         // for ix
     114             : 
     115           6 :         executeInverseFFTInplace(gridPtr, Bkx, Bky, Bkz);
     116             : 
     117           6 :         fftwf_free(Bkx);
     118           6 :         fftwf_free(Bky);
     119           6 :         fftwf_free(Bkz);
     120             : 
     121          24 :         scaleGrid(gridPtr, spectrum.getBrms() /
     122          12 :                                rmsFieldStrength(gridPtr)); // normalize to Brms
     123          12 : }
     124             : 
     125             : // Check the grid properties before the FFT procedure
     126          13 : void GridTurbulence::checkGridRequirements(ref_ptr<Grid3f> grid, double lMin,
     127             :                                            double lMax) {
     128             :         size_t Nx = grid->getNx();
     129             :         size_t Ny = grid->getNy();
     130             :         size_t Nz = grid->getNz();
     131             :         Vector3d spacing = grid->getSpacing();
     132             : 
     133          13 :         if ((Nx != Ny) or (Ny != Nz))
     134           0 :                 throw std::runtime_error("turbulentField: only cubic grid supported");
     135          13 :         if ((spacing.x != spacing.y) or (spacing.y != spacing.z))
     136           0 :                 throw std::runtime_error("turbulentField: only equal spacing suported");
     137          13 :         if (lMin < 2 * spacing.x)
     138           1 :                 throw std::runtime_error("turbulentField: lMin < 2 * spacing");
     139          12 :         if (lMax > Nx * spacing.x) // before was (lMax > Nx * spacing.x / 2), why?
     140           1 :                 throw std::runtime_error("turbulentField: lMax > size");
     141          11 :         if (lMax < lMin) 
     142           1 :                 throw std::runtime_error("lMax < lMin");
     143          10 : }
     144             : 
     145             : // Execute inverse discrete FFT in-place for a 3D grid, from complex to real
     146             : // space
     147          10 : void GridTurbulence::executeInverseFFTInplace(ref_ptr<Grid3f> grid,
     148             :                                               fftwf_complex *Bkx,
     149             :                                               fftwf_complex *Bky,
     150             :                                               fftwf_complex *Bkz) {
     151             : 
     152             :         size_t n = grid->getNx(); // size of array
     153          10 :         size_t n2 = (size_t)floor(n / 2) +
     154             :                     1; // size array in z-direction in configuration space
     155             : 
     156             :         // in-place, complex to real, inverse Fourier transformation on each
     157             :         // component note that the last elements of B(x) are unused now
     158             :         float *Bx = (float *)Bkx;
     159          10 :         fftwf_plan plan_x = fftwf_plan_dft_c2r_3d(n, n, n, Bkx, Bx, FFTW_ESTIMATE);
     160          10 :         fftwf_execute(plan_x);
     161          10 :         fftwf_destroy_plan(plan_x);
     162             : 
     163             :         float *By = (float *)Bky;
     164          10 :         fftwf_plan plan_y = fftwf_plan_dft_c2r_3d(n, n, n, Bky, By, FFTW_ESTIMATE);
     165          10 :         fftwf_execute(plan_y);
     166          10 :         fftwf_destroy_plan(plan_y);
     167             : 
     168             :         float *Bz = (float *)Bkz;
     169          10 :         fftwf_plan plan_z = fftwf_plan_dft_c2r_3d(n, n, n, Bkz, Bz, FFTW_ESTIMATE);
     170          10 :         fftwf_execute(plan_z);
     171          10 :         fftwf_destroy_plan(plan_z);
     172             : 
     173             :         // save to grid
     174         650 :         for (size_t ix = 0; ix < n; ix++) {
     175       41600 :                 for (size_t iy = 0; iy < n; iy++) {
     176     2662400 :                         for (size_t iz = 0; iz < n; iz++) {
     177     2621440 :                                 size_t i = ix * n * 2 * n2 + iy * 2 * n2 + iz;
     178             :                                 Vector3f &b = grid->get(ix, iy, iz);
     179     2621440 :                                 b.x = Bx[i];
     180     2621440 :                                 b.y = By[i];
     181     2621440 :                                 b.z = Bz[i];
     182             :                         }
     183             :                 }
     184             :         }
     185          10 : }
     186             : 
     187           0 : Vector3f GridTurbulence::getMeanFieldVector() const {
     188           0 :         return meanFieldVector(gridPtr);
     189             : }
     190             : 
     191           0 : double GridTurbulence::getMeanFieldStrength() const {
     192           0 :         return meanFieldStrength(gridPtr);
     193             : }
     194             : 
     195           0 : double GridTurbulence::getRmsFieldStrength() const {
     196           0 :         return rmsFieldStrength(gridPtr);
     197             : }
     198             : 
     199           0 : std::array<float, 3> GridTurbulence::getRmsFieldStrengthPerAxis() const {
     200           0 :         return rmsFieldStrengthPerAxis(gridPtr);
     201             : }
     202             :         
     203           0 : std::vector<std::pair<int, float>> GridTurbulence::getPowerSpectrum() const {
     204           0 :         return gridPowerSpectrum(gridPtr);
     205             : }
     206             : 
     207           0 : void GridTurbulence::dumpToFile(std::string filename) const {
     208           0 :         dumpGrid(gridPtr, filename);
     209           0 : }
     210             : 
     211             : } // namespace crpropa
     212             : 
     213             : #endif // CRPROPA_HAVE_FFTW3F

Generated by: LCOV version 1.14