|
| 1 | +//======================================================================================== |
| 2 | +// AthenaXXX astrophysical plasma code |
| 3 | +// Copyright(C) 2020 James M. Stone <[email protected]> and the Athena code team |
| 4 | +// Licensed under the 3-clause BSD License (the "LICENSE") |
| 5 | +//======================================================================================== |
| 6 | +//! \file z4c_one_puncture.cpp |
| 7 | +// \brief Problem generator for a single puncture placed at the origin of the domain |
| 8 | + |
| 9 | +#include <algorithm> |
| 10 | +#include <cmath> |
| 11 | +#include <sstream> |
| 12 | +#include <iomanip> |
| 13 | +#include <iostream> // endl |
| 14 | +#include <limits> // numeric_limits::max() |
| 15 | +#include <memory> |
| 16 | +#include <string> // c_str(), string |
| 17 | +#include <vector> |
| 18 | +#include <cctype> |
| 19 | +#include <random> |
| 20 | + |
| 21 | +#include "athena.hpp" |
| 22 | +#include "parameter_input.hpp" |
| 23 | +#include "globals.hpp" |
| 24 | +#include "mesh/mesh.hpp" |
| 25 | +#include "coordinates/cell_locations.hpp" |
| 26 | +#include "geodesic-grid/gauss_legendre.hpp" |
| 27 | +#include "utils/spherical_harm.hpp" |
| 28 | + |
| 29 | +using u32 = uint_least32_t; |
| 30 | +using engine = std::mt19937; |
| 31 | + |
| 32 | +// void ADMOnePuncture(MeshBlockPack *pmbp, ParameterInput *pin); |
| 33 | + |
| 34 | +//---------------------------------------------------------------------------------------- |
| 35 | +//! \fn ProblemGenerator::UserProblem_() |
| 36 | +//! \brief Problem Generator for single puncture |
| 37 | +void ProblemGenerator::UserProblem(ParameterInput *pin, const bool restart) { |
| 38 | + MeshBlockPack *pmbp = pmy_mesh_->pmb_pack; |
| 39 | + auto &indcs = pmy_mesh_->mb_indcs; |
| 40 | + |
| 41 | + // ADMOnePuncture(pmbp, pin); |
| 42 | + int ntheta = pin->GetOrAddInteger("problem", "ntheta", 16); |
| 43 | + |
| 44 | + GaussLegendreGrid grid(pmbp, ntheta, 1); |
| 45 | + |
| 46 | + // test that the cross integral of spherical harmonics are delta functions. |
| 47 | + // First initialize 10 random pairs of l and m, with 0 <= l <=ntheta. |
| 48 | + |
| 49 | + std::random_device os_seed; |
| 50 | + const u32 seed = os_seed(); |
| 51 | + |
| 52 | + engine generator( seed ); |
| 53 | + std::uniform_int_distribution< u32 > distribute_l( 1, ntheta-1); |
| 54 | + |
| 55 | + std::vector<int> ls; |
| 56 | + std::vector<int> ms; |
| 57 | + |
| 58 | + for (int repetition = 0; repetition < 10; ++repetition) { |
| 59 | + int l = distribute_l(generator); |
| 60 | + std::uniform_int_distribution< u32 > distribute_m( -l, l); |
| 61 | + int m = distribute_m(generator); |
| 62 | + ls.push_back(l); |
| 63 | + ms.push_back(m); |
| 64 | + } |
| 65 | + |
| 66 | + double ylmR1,ylmI1,ylmR2,ylmI2; |
| 67 | + double int_r, int_i; |
| 68 | + bool failed = false; |
| 69 | + double max_err = 0; |
| 70 | + |
| 71 | + // outer loop over pairs of spherical harmonics |
| 72 | + for (int n1 = 0; n1 < 10; ++n1) |
| 73 | + for (int n2 = n1; n2 < 10; ++n2) { |
| 74 | + // reset doubles to store integration value |
| 75 | + int_r = 0; |
| 76 | + int_i = 0; |
| 77 | + |
| 78 | + // iterate over the angles |
| 79 | + for (int ip = 0; ip < grid.nangles; ++ip) { |
| 80 | + Real theta = grid.polar_pos.h_view(ip,0); |
| 81 | + Real phi = grid.polar_pos.h_view(ip,1); |
| 82 | + Real weight = grid.int_weights.h_view(ip); |
| 83 | + SWSphericalHarm(&ylmR1,&ylmI1, ls[n1], ms[n1], 0, theta, phi); |
| 84 | + SWSphericalHarm(&ylmR2,&ylmI2, ls[n2], ms[n2], 0, theta, phi); |
| 85 | + // complex conjugate |
| 86 | + ylmI2 *= -1; |
| 87 | + int_r += weight*(ylmR1*ylmR2 - ylmI1*ylmI2); |
| 88 | + int_i += weight*(ylmR1*ylmI2 + ylmR2*ylmI1); |
| 89 | + } |
| 90 | + |
| 91 | + if (ls[n1] == ls[n2] && ms[n1] == ms[n2]) { |
| 92 | + max_err = (abs(int_r-1)> max_err) ? abs(int_r-1) : max_err; |
| 93 | + max_err = (abs(int_i)> max_err) ? abs(int_i) : max_err; |
| 94 | + |
| 95 | + if (abs(int_r-1) >= 1e-10 || abs(int_i) >= 1e-10) { |
| 96 | + failed = true; |
| 97 | + } |
| 98 | + } else { |
| 99 | + max_err = (abs(int_r)> max_err) ? abs(int_r) : max_err; |
| 100 | + max_err = (abs(int_i)> max_err) ? abs(int_i) : max_err; |
| 101 | + |
| 102 | + if (abs(int_r) >= 1e-10 || abs(int_i) >= 1e-10) { |
| 103 | + failed = true; |
| 104 | + } |
| 105 | + } |
| 106 | + if (failed == true) { |
| 107 | + std::cout << "Gauss Legendre Integral Test Failed"<< std::endl; |
| 108 | + std::cout << "l1=" << ls[n1] << '\t' << "m1=" << ms[n1]<< std::endl; |
| 109 | + std::cout << "l2=" << ls[n2] << '\t' << "m2=" << ms[n2]<< std::endl; |
| 110 | + std::cout << "value is " << int_r << "+1j*" << int_i << std::endl; |
| 111 | + std::cout << std::endl; |
| 112 | + exit(EXIT_FAILURE); |
| 113 | + } |
| 114 | + } |
| 115 | + std::cout << "Test Passed with Maximum Error = " << max_err << std::endl; |
| 116 | + |
| 117 | + return; |
| 118 | +} |
0 commit comments