From f0652b84089fc53b5a2659bcda0b204ce815584b Mon Sep 17 00:00:00 2001 From: James Date: Sat, 12 Nov 2022 11:42:08 -0500 Subject: [PATCH 01/10] bgk: impl custom dist func --- src/psc_bgk.cxx | 73 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/src/psc_bgk.cxx b/src/psc_bgk.cxx index ba7da4a29..665a77ab8 100644 --- a/src/psc_bgk.cxx +++ b/src/psc_bgk.cxx @@ -209,6 +209,39 @@ inline double getIonDensity(double rho) return std::exp(-potential / g.T_i); } +// ====================================================================== +// v_phi_cdf +// Cumulative distribution function for azimuthal electron velocity + +double v_phi_cdf(double v_phi, double rho) +{ + // convert units + v_phi *= 1000; + rho *= 1000; + + double k = .1; + double h0 = .9; + double B = g.Hx; + double sqrt2 = std::sqrt(2); + + double rho_sqr = sqr(rho); + + double gamma = 1 + 8 * k * rho_sqr; + double alpha = + 1 - h0 / std::sqrt(gamma) * std::exp(-k * sqr(B) * sqr(rho_sqr) / gamma); + + double mean0 = 0; + double stdev0 = 1; + + double mean1 = 4 * k * B * rho_sqr * rho / gamma; + double stdev1 = 1 / std::sqrt(gamma); + + double m0 = (1 + std::erf((v_phi - mean0) / (stdev0 * sqrt2))) / 2; + double m1 = (1 + std::erf((v_phi - mean1) / (stdev1 * sqrt2))) / 2; + + return m0 / alpha + m1 * (1 - 1 / alpha); +} + // ====================================================================== // initializeParticles @@ -220,8 +253,8 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts, auto&& qDensity = -psc::mflds::interior(divGradPhi.grid(), divGradPhi.gt()); - auto npt_init = [&](int kind, Double3 crd, int p, Int3 idx, - psc_particle_npt& npt) { + auto init_np = [&](int kind, Double3 crd, int p, Int3 idx, + psc_particle_np& np) { double y = getCoord(crd[1]); double z = getCoord(crd[2]); double rho = sqrt(sqr(y) + sqr(z)); @@ -229,26 +262,32 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts, switch (kind) { case KIND_ELECTRON: - npt.n = (qDensity(idx[0], idx[1], idx[2], 0, p) - - getIonDensity(rho) * g.q_i) / - g.q_e; + np.n = (qDensity(idx[0], idx[1], idx[2], 0, p) - + getIonDensity(rho) * g.q_i) / + g.q_e; if (rho != 0) { - double v_phi = parsedData->get_interpolated(COL_V_PHI, rho); - double coef = g.v_e_coef * (g.reverse_v ? -1 : 1) * - (g.reverse_v_half && y < 0 ? -1 : 1); - npt.p[0] = 0; - npt.p[1] = coef * g.m_e * v_phi * -z / rho; - npt.p[2] = coef * g.m_e * v_phi * y / rho; + np.p = [=]() { + rng::InvertedCdf v_phi_dist{ + [=](double v_phi) { return v_phi_cdf(v_phi, rho); }}; + rng::Normal v_rho_dist{0, 1}; + double v_phi = v_phi_dist.get(); + double v_rho = v_rho_dist.get(); + + double coef = g.v_e_coef * (g.reverse_v ? -1 : 1) * + (g.reverse_v_half && y < 0 ? -1 : 1); + double p_x = 0; + double p_y = coef * g.m_e * v_phi * -z / rho; + double p_z = coef * g.m_e * v_phi * y / rho; + return Double3{p_x, p_y, p_z}; + }; } else { - setAll(npt.p, 0); + np.p = [=]() { return Double3{0, 0, 0}; }; } - setAll(npt.T, g.T_e_coef * parsedData->get_interpolated(COL_TE, rho)); break; case KIND_ION: - npt.n = getIonDensity(rho); - setAll(npt.p, 0); - setAll(npt.T, g.T_i); + np.n = getIonDensity(rho); + np.p = [=]() { return Double3{0, 0, 0}; }; break; default: assert(false); @@ -256,7 +295,7 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts, }; partitionAndSetupParticles(setup_particles, balance, grid_ptr, mprts, - npt_init); + init_np); } // ====================================================================== From c8c9451a45d1cdf67c90a00b10c8e4f8e27f994f Mon Sep 17 00:00:00 2001 From: James Date: Wed, 16 Nov 2022 12:24:28 -0500 Subject: [PATCH 02/10] bgk: be smarter about making new dists --- src/psc_bgk.cxx | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/psc_bgk.cxx b/src/psc_bgk.cxx index 665a77ab8..cbcb4a3c1 100644 --- a/src/psc_bgk.cxx +++ b/src/psc_bgk.cxx @@ -242,6 +242,34 @@ double v_phi_cdf(double v_phi, double rho) return m0 / alpha + m1 * (1 - 1 / alpha); } +struct pdist +{ + pdist(double y, double z, double rho) + : y{y}, z{z}, rho{rho}, v_phi_dist{[=](double v_phi) { + return v_phi_cdf(v_phi, rho); + }} + {} + + Double3 operator()() + { + double v_phi = v_phi_dist.get(); + double v_rho = v_rho_dist.get(); + + double coef = g.v_e_coef * (g.reverse_v ? -1 : 1) * + (g.reverse_v_half && y < 0 ? -1 : 1); + double p_x = 0; + double p_y = coef * g.m_e * (v_phi * -z + v_rho * y) / rho; + double p_z = coef * g.m_e * (v_phi * y + v_rho * z) / rho; + return Double3{p_x, p_y, p_z}; + } + +private: + double y, z, rho; + rng::InvertedCdf v_phi_dist; + rng::Normal v_rho_dist{ + 0, 1. / 1000.}; // FIXME magic number 1000 here and elsewhere +}; + // ====================================================================== // initializeParticles @@ -266,20 +294,7 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts, getIonDensity(rho) * g.q_i) / g.q_e; if (rho != 0) { - np.p = [=]() { - rng::InvertedCdf v_phi_dist{ - [=](double v_phi) { return v_phi_cdf(v_phi, rho); }}; - rng::Normal v_rho_dist{0, 1}; - double v_phi = v_phi_dist.get(); - double v_rho = v_rho_dist.get(); - - double coef = g.v_e_coef * (g.reverse_v ? -1 : 1) * - (g.reverse_v_half && y < 0 ? -1 : 1); - double p_x = 0; - double p_y = coef * g.m_e * v_phi * -z / rho; - double p_z = coef * g.m_e * v_phi * y / rho; - return Double3{p_x, p_y, p_z}; - }; + np.p = pdist(y, z, rho); } else { np.p = [=]() { return Double3{0, 0, 0}; }; } From e95ed596eda7c945d970e26971e5dc5b1c7a29b7 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 21 Nov 2022 11:47:08 -0500 Subject: [PATCH 03/10] bgk: use maxwellan for p=0 avoids division by 0, and is what it should be anyways --- src/psc_bgk.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/psc_bgk.cxx b/src/psc_bgk.cxx index cbcb4a3c1..1b63e8755 100644 --- a/src/psc_bgk.cxx +++ b/src/psc_bgk.cxx @@ -287,6 +287,7 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts, double z = getCoord(crd[2]); double rho = sqrt(sqr(y) + sqr(z)); + double Ti = g.T_i; switch (kind) { case KIND_ELECTRON: @@ -296,13 +297,16 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts, if (rho != 0) { np.p = pdist(y, z, rho); } else { - np.p = [=]() { return Double3{0, 0, 0}; }; + double Te = parsedData->get_interpolated(COL_TE, 0); + np.p = setup_particles.createMaxwellian( + {np.kind, np.n, {0, 0, 0}, {Te, Te, Te}, np.tag}); } break; case KIND_ION: np.n = getIonDensity(rho); - np.p = [=]() { return Double3{0, 0, 0}; }; + np.p = setup_particles.createMaxwellian( + {np.kind, np.n, {0, 0, 0}, {Ti, Ti, Ti}, np.tag}); break; default: assert(false); From ec4899c59c34b0cdc518f3d1e5bd8f0a244025c1 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 30 Dec 2022 15:02:33 -0500 Subject: [PATCH 04/10] bgk: rm magic number 1000 in unit conversions --- src/psc_bgk.cxx | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/psc_bgk.cxx b/src/psc_bgk.cxx index 1b63e8755..32ce090d8 100644 --- a/src/psc_bgk.cxx +++ b/src/psc_bgk.cxx @@ -209,15 +209,28 @@ inline double getIonDensity(double rho) return std::exp(-potential / g.T_i); } +// ====================================================================== +// get_beta +// Return the conversion factor from paper units to psc units. + +double get_beta() +{ + // PSC is normalized as c=1, but the paper has electron thermal velocity + // v_e=1. Beta is v_e/c = sqrt(Te_paper) / sqrt(Te_psc) + const double PAPER_ELECTRON_TEMPERATURE = 1.; + const double pscElectronTemperature = parsedData->get_interpolated(COL_TE, 0); + return std::sqrt(pscElectronTemperature / PAPER_ELECTRON_TEMPERATURE); +} + // ====================================================================== // v_phi_cdf // Cumulative distribution function for azimuthal electron velocity double v_phi_cdf(double v_phi, double rho) { - // convert units - v_phi *= 1000; - rho *= 1000; + // convert units from psc to paper + v_phi /= get_beta(); + rho /= get_beta(); double k = .1; double h0 = .9; @@ -245,9 +258,11 @@ double v_phi_cdf(double v_phi, double rho) struct pdist { pdist(double y, double z, double rho) - : y{y}, z{z}, rho{rho}, v_phi_dist{[=](double v_phi) { - return v_phi_cdf(v_phi, rho); - }} + : y{y}, + z{z}, + rho{rho}, + v_phi_dist{[=](double v_phi) { return v_phi_cdf(v_phi, rho); }}, + v_rho_dist{0, get_beta()} {} Double3 operator()() @@ -266,8 +281,7 @@ struct pdist private: double y, z, rho; rng::InvertedCdf v_phi_dist; - rng::Normal v_rho_dist{ - 0, 1. / 1000.}; // FIXME magic number 1000 here and elsewhere + rng::Normal v_rho_dist; }; // ====================================================================== From 92549ba4a4511dea2b0094e44ae7f376c56f2fa8 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 26 Jan 2023 16:02:05 -0500 Subject: [PATCH 05/10] bgk: swap if branches in init_np --- src/psc_bgk.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/psc_bgk.cxx b/src/psc_bgk.cxx index 32ce090d8..85b9d3af0 100644 --- a/src/psc_bgk.cxx +++ b/src/psc_bgk.cxx @@ -308,12 +308,12 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts, np.n = (qDensity(idx[0], idx[1], idx[2], 0, p) - getIonDensity(rho) * g.q_i) / g.q_e; - if (rho != 0) { - np.p = pdist(y, z, rho); - } else { - double Te = parsedData->get_interpolated(COL_TE, 0); + if (rho == 0) { + double Te = parsedData->get_interpolated(COL_TE, rho); np.p = setup_particles.createMaxwellian( {np.kind, np.n, {0, 0, 0}, {Te, Te, Te}, np.tag}); + } else { + np.p = pdist(y, z, rho); } break; From 55954630b91298e58484aba215468577681ee97e Mon Sep 17 00:00:00 2001 From: James Date: Thu, 26 Jan 2023 16:02:17 -0500 Subject: [PATCH 06/10] bgk (params): add maxwellian option --- src/psc_bgk_util/bgk_params.hxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/psc_bgk_util/bgk_params.hxx b/src/psc_bgk_util/bgk_params.hxx index 959d54eb9..7f0587c15 100644 --- a/src/psc_bgk_util/bgk_params.hxx +++ b/src/psc_bgk_util/bgk_params.hxx @@ -32,6 +32,7 @@ struct PscBgkParams double T_e_coef; // multiplier for electron temperature bool reverse_v; // whether or not to reverse electron velocity bool reverse_v_half; // whether or not to reverse electron velocity for y<0 + bool maxwellian; // whether or not to use Maxwellian instead of exact sol // For 3D cases int n_grid_3; // number of grid points in 3rd dimension @@ -67,6 +68,8 @@ struct PscBgkParams v_e_coef = parsedParams.getOrDefault("v_e_coef", 1); T_e_coef = parsedParams.getOrDefault("T_e_coef", 1); + maxwellian = parsedParams.getOrDefault("maxwellian", false); + n_grid_3 = parsedParams.getOrDefault("n_grid_3", 1); box_size_3 = parsedParams.getOrDefault("box_size_3", 1); if (n_grid_3 < parsedParams.get("n_cells_per_patch")) { From 273ca5afcf16bbce8b56290a3f0a910dec34fd5a Mon Sep 17 00:00:00 2001 From: James Date: Thu, 26 Jan 2023 16:11:53 -0500 Subject: [PATCH 07/10] bgk: use g.maxwellian --- src/psc_bgk.cxx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/psc_bgk.cxx b/src/psc_bgk.cxx index 85b9d3af0..1d6bde8a0 100644 --- a/src/psc_bgk.cxx +++ b/src/psc_bgk.cxx @@ -312,6 +312,16 @@ void initializeParticles(Balance& balance, Grid_t*& grid_ptr, Mparticles& mprts, double Te = parsedData->get_interpolated(COL_TE, rho); np.p = setup_particles.createMaxwellian( {np.kind, np.n, {0, 0, 0}, {Te, Te, Te}, np.tag}); + } else if (g.maxwellian) { + double Te = parsedData->get_interpolated(COL_TE, rho); + double vphi = parsedData->get_interpolated(COL_V_PHI, rho); + double coef = g.v_e_coef * (g.reverse_v ? -1 : 1) * + (g.reverse_v_half && y < 0 ? -1 : 1); + + double pz = coef * g.m_e * vphi * y / rho; + double py = coef * g.m_e * -vphi * z / rho; + np.p = setup_particles.createMaxwellian( + {np.kind, np.n, {0, py, pz}, {Te, Te, Te}, np.tag}); } else { np.p = pdist(y, z, rho); } From d35341e054e789e3520fc67c922344bf2afe84d7 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 27 Jan 2023 14:38:57 -0500 Subject: [PATCH 08/10] bgk: mv h0, k to bgk_params --- src/psc_bgk.cxx | 10 ++++------ src/psc_bgk_util/bgk_params.hxx | 3 +++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/psc_bgk.cxx b/src/psc_bgk.cxx index 1d6bde8a0..13252474a 100644 --- a/src/psc_bgk.cxx +++ b/src/psc_bgk.cxx @@ -232,21 +232,19 @@ double v_phi_cdf(double v_phi, double rho) v_phi /= get_beta(); rho /= get_beta(); - double k = .1; - double h0 = .9; double B = g.Hx; double sqrt2 = std::sqrt(2); double rho_sqr = sqr(rho); - double gamma = 1 + 8 * k * rho_sqr; - double alpha = - 1 - h0 / std::sqrt(gamma) * std::exp(-k * sqr(B) * sqr(rho_sqr) / gamma); + double gamma = 1 + 8 * g.k * rho_sqr; + double alpha = 1 - g.h0 / std::sqrt(gamma) * + std::exp(-g.k * sqr(B) * sqr(rho_sqr) / gamma); double mean0 = 0; double stdev0 = 1; - double mean1 = 4 * k * B * rho_sqr * rho / gamma; + double mean1 = 4 * g.k * B * rho_sqr * rho / gamma; double stdev1 = 1 / std::sqrt(gamma); double m0 = (1 + std::erf((v_phi - mean0) / (stdev0 * sqrt2))) / 2; diff --git a/src/psc_bgk_util/bgk_params.hxx b/src/psc_bgk_util/bgk_params.hxx index 7f0587c15..03f88650b 100644 --- a/src/psc_bgk_util/bgk_params.hxx +++ b/src/psc_bgk_util/bgk_params.hxx @@ -18,6 +18,9 @@ struct PscBgkParams int n_patches; // number of patches int nicell; // number of particles per gripdoint when density=1 + double k = .1; // a parameter for BGK solutions + double h0 = .9; // a parameter for BGK solutions + int fields_every; // interval for pfd output int moments_every; // interval for pfd_moments output int gauss_every; // interval for gauss output/checking From 7ed54464db4a5d670ba8e831ee7549c6b723abec Mon Sep 17 00:00:00 2001 From: James Date: Fri, 27 Jan 2023 15:14:35 -0500 Subject: [PATCH 09/10] bgk (params): enable auto-calculated box size --- src/psc_bgk_util/bgk_params.hxx | 41 +++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/psc_bgk_util/bgk_params.hxx b/src/psc_bgk_util/bgk_params.hxx index 03f88650b..cda216caa 100644 --- a/src/psc_bgk_util/bgk_params.hxx +++ b/src/psc_bgk_util/bgk_params.hxx @@ -1,13 +1,47 @@ #pragma once +#include #include "params_parser.hxx" +// ====================================================================== +// getCalculatedBoxSize +// +// Calculate the radius where the spike in the exact distribution function ends, +// according to the equation (in paper units): +// v_phi = max_v = 4*k*B*r^3 / (1 + 8*k*r^2) +// The exact solution can be decomposed into the difference of two Gaussians. +// The positive Gaussian has a mean of 0 and stdev of 1, independently of +// all parameters. "max_v" represents an upper limit for v_phi according +// to this term. +// The negative Gaussian has a mean given by the RHS of the equation. It drifts +// up, approaching a line with slope B/2. The negative Gaussian is the source +// of the spike. + +double getCalculatedBoxSize(double B, double k) +{ + double max_v = 3.; + // solve cubic with linear coefficient = 0 + double a = 4. * k * B; + double b = -8. * max_v * k; + double d = -max_v; + + double p = -b / (3. * a); + double t = -d / (2. * a); + double q = p * p * p + t; + double s = sqrt(t * (2. * q - t)); + + double beta = .001; // FIXME don't hardcode this (see psc_bgk.cxx get_beta()) + double extra_multiplier = 1.5; + double r = (std::cbrt(q + s) + std::cbrt(q - s) + p) * beta; + return extra_multiplier * 2 * r; +} + // ====================================================================== // PscBgkParams struct PscBgkParams { - double box_size; // physical length of region along y and z + double box_size; // physical length of region along y and z; -1 -> auto double Hx; // strength of transverse magnetic field double q_i; // ion charge double n_i; // ion number density @@ -82,5 +116,8 @@ struct PscBgkParams if (n_patches_3 <= 0) n_patches_3 = n_grid_3 / parsedParams.get("n_cells_per_patch"); } + + if (box_size <= 0) + box_size = getCalculatedBoxSize(Hx, k); } -}; \ No newline at end of file +}; From 49672c1a9b278783b3b0c3e880337f9b75a9ac31 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 2 Mar 2023 15:21:06 -0500 Subject: [PATCH 10/10] bgk: make v_x maxwellian --- src/psc_bgk.cxx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/psc_bgk.cxx b/src/psc_bgk.cxx index 13252474a..144984800 100644 --- a/src/psc_bgk.cxx +++ b/src/psc_bgk.cxx @@ -260,17 +260,19 @@ struct pdist z{z}, rho{rho}, v_phi_dist{[=](double v_phi) { return v_phi_cdf(v_phi, rho); }}, - v_rho_dist{0, get_beta()} + v_rho_dist{0, get_beta()}, + v_x_dist{0, get_beta()} {} Double3 operator()() { double v_phi = v_phi_dist.get(); double v_rho = v_rho_dist.get(); + double v_x = v_x_dist.get(); double coef = g.v_e_coef * (g.reverse_v ? -1 : 1) * (g.reverse_v_half && y < 0 ? -1 : 1); - double p_x = 0; + double p_x = coef * g.m_e * v_x; double p_y = coef * g.m_e * (v_phi * -z + v_rho * y) / rho; double p_z = coef * g.m_e * (v_phi * y + v_rho * z) / rho; return Double3{p_x, p_y, p_z}; @@ -280,6 +282,7 @@ struct pdist double y, z, rho; rng::InvertedCdf v_phi_dist; rng::Normal v_rho_dist; + rng::Normal v_x_dist; }; // ======================================================================