|
5 | 5 |
|
6 | 6 | #include "fields.h"
|
7 | 7 | #include "meshblock.h"
|
| 8 | +#include "qmath.h" |
8 | 9 | #include "sim_params.h"
|
9 | 10 |
|
10 | 11 | #ifdef NTTINY_ENABLED
|
@@ -94,6 +95,88 @@ namespace ntt {
|
94 | 95 | }
|
95 | 96 | };
|
96 | 97 |
|
| 98 | + template <Dimension D, SimulationEngine S> |
| 99 | + struct Maxwellian { |
| 100 | + Maxwellian(const Meshblock<D, S>& mblock) : pool { *(mblock.random_pool_ptr) } {} |
| 101 | + // Juttner-Synge distribution |
| 102 | + Inline void JS(vec_t<Dim3>& v, const real_t& temp) const { |
| 103 | + typename RandomNumberPool_t::generator_type rand_gen = pool.get_state(); |
| 104 | + real_t u { ZERO }, eta { ZERO }, theta { ZERO }; |
| 105 | + real_t X1 { ZERO }, X2 { ZERO }; |
| 106 | + if (temp < 0.1) { |
| 107 | + // Juttner-Synge distribution using the Box-Muller method - non-relativistic |
| 108 | + while (AlmostEqual(u, ZERO)) { |
| 109 | + u = rand_gen.frand(); |
| 110 | + } |
| 111 | + eta = math::sqrt(-TWO * math::log(u)); |
| 112 | + while (AlmostEqual(theta, ZERO)) { |
| 113 | + theta = constant::TWO_PI * rand_gen.frand(); |
| 114 | + } |
| 115 | + u = eta * math::cos(theta) * math::sqrt(temp); |
| 116 | + } else { |
| 117 | + // Juttner-Synge distribution using the Sobol method - relativistic |
| 118 | + u = ONE; |
| 119 | + while (SQR(eta) <= SQR(u)) { |
| 120 | + while (AlmostEqual(X1, ZERO)) { |
| 121 | + X1 = rand_gen.frand() * rand_gen.frand() * rand_gen.frand(); |
| 122 | + } |
| 123 | + u = -temp * math::log(X1); |
| 124 | + X1 = rand_gen.frand(); |
| 125 | + while (AlmostEqual(X1, 0)) { |
| 126 | + X1 = rand_gen.frand(); |
| 127 | + } |
| 128 | + eta = u - temp * math::log(X1); |
| 129 | + } |
| 130 | + } |
| 131 | + X1 = rand_gen.frand(); |
| 132 | + X2 = rand_gen.frand(); |
| 133 | + v[0] = u * (TWO * X1 - ONE); |
| 134 | + v[2] = TWO * u * math::sqrt(X1 * (ONE - X1)); |
| 135 | + v[1] = v[2] * math::cos(constant::TWO_PI * X2); |
| 136 | + v[2] = v[2] * math::sin(constant::TWO_PI * X2); |
| 137 | + pool.free_state(rand_gen); |
| 138 | + } |
| 139 | + // Boost a symmetric distribution to a relativistic speed using flipping method |
| 140 | + // https://arxiv.org/pdf/1504.03910.pdf |
| 141 | + Inline void boost(vec_t<Dim3>& v, |
| 142 | + const real_t& boost_vel, |
| 143 | + const short& boost_direction) const { |
| 144 | + typename RandomNumberPool_t::generator_type rand_gen = pool.get_state(); |
| 145 | + real_t boost_beta { boost_vel / math::sqrt(ONE + SQR(boost_vel)) }; |
| 146 | + real_t boost_gamma { boost_vel / boost_beta }; |
| 147 | + real_t ut { math::sqrt(ONE + SQR(v[0]) + SQR(v[1]) + SQR(v[2])) }; |
| 148 | + if (-boost_beta * v[boost_direction] > ut * rand_gen.frand()) { |
| 149 | + v[boost_direction] = -v[boost_direction]; |
| 150 | + } |
| 151 | + pool.free_state(rand_gen); |
| 152 | + v[boost_direction] = boost_gamma * (v[boost_direction] + boost_beta * ut); |
| 153 | + } |
| 154 | + |
| 155 | + Inline void operator()(vec_t<Dim3>& v, |
| 156 | + const real_t& temp, |
| 157 | + const real_t& boost_vel = ZERO, |
| 158 | + const short& boost_direction = 0) const { |
| 159 | + if (AlmostEqual(temp, ZERO)) { |
| 160 | + v[0] = ZERO; |
| 161 | + v[1] = ZERO; |
| 162 | + v[2] = ZERO; |
| 163 | + } else { |
| 164 | + JS(v, temp); |
| 165 | + } |
| 166 | + if (!AlmostEqual(boost_vel, ZERO)) { |
| 167 | + if (boost_direction < 0) { |
| 168 | + boost(v, -boost_vel, -boost_direction - 1); |
| 169 | + } else if (boost_direction > 0) { |
| 170 | + boost(v, boost_vel, boost_direction - 1); |
| 171 | + } |
| 172 | + // no boost when boost_direction == 0 |
| 173 | + } |
| 174 | + } |
| 175 | + |
| 176 | + private: |
| 177 | + RandomNumberPool_t pool; |
| 178 | + }; |
| 179 | + |
97 | 180 | /* -------------------------------------------------------------------------- */
|
98 | 181 | /* Spatial distribution */
|
99 | 182 | /* -------------------------------------------------------------------------- */
|
|
0 commit comments