From dcdba00dfa42838c7409060ef476a4d59d7d3be9 Mon Sep 17 00:00:00 2001 From: Virginie Grandgirard Date: Wed, 20 Mar 2024 22:56:22 +0000 Subject: [PATCH] Advection field in RP instead of XY For the merge --------------------------------------------------------- Add in addition of the advection field in XY, an advection field in RP. Add a AdvectionFieldFinder class which computes advection field along XY and along RP. Modify BslAdvectionRP class to add an operator for the advection field on RP as input. Remove the VlasovPoissonSolver class. For the MR ------------------------------------------------------------ The advection operator will take the advection field in RP instead of XY. TO DO ----------------------------------------------------------------- - [x] Check documentations and formulas; - [x] Remove vlasovpoissonsolver.hpp and associated documentation; - [x] Add tests; - [x] Remove modifications made for the diocotron simulation, only use the advection field in XY; - [x] Add a case in the predictor-corrector operators? or these operators are made for the guiding-center case and better work with only advection field in XY; - Create a function set_initialization which return exact_rho.initialisation and exact_rho.equilibrium in diocotron simulation? - Check if the following had been done or has to be done: * geometry_PC -> foot finder * advect_feet -> foot finder * compute advection field -> PseudoCartesian() * remove AdvectionDomain and give PseudoCartesian mapping instead See merge request gysela-developpers/gyselalibxx!309 -------------------------------------------- Co-authored-by: Pauline Vidal Co-authored-by: Emily Bourne Co-authored-by: Thomas Padioleau Co-authored-by: Baptiste LEGOUIX --- .../geometryRTheta/animation_rho_phi.py | 2 +- .../geometryRTheta/diocotron/diocotron.cpp | 6 +- .../geometryRTheta/diocotron/params.yaml | 2 +- .../vortex_merger/vortex_merger.cpp | 6 +- src/geometryRTheta/CMakeLists.txt | 1 + src/geometryRTheta/README.md | 6 +- src/geometryRTheta/advection/README.md | 146 ++++- .../advection/bsl_advection_rp.hpp | 101 +++- src/geometryRTheta/advection/iadvectionrp.hpp | 27 +- .../advection/spline_foot_finder.hpp | 3 - .../advection_field/CMakeLists.txt | 16 + src/geometryRTheta/advection_field/README.md | 258 +++++++++ .../advection_field/advection_field_rp.hpp | 527 ++++++++++++++++++ src/geometryRTheta/poisson/README.md | 47 -- .../poisson/vlasovpoissonsolver.hpp | 296 ---------- src/geometryRTheta/time_solver/CMakeLists.txt | 1 + .../time_solver/bsl_predcorr.hpp | 74 ++- .../bsl_predcorr_second_order_explicit.hpp | 96 ++-- .../bsl_predcorr_second_order_implicit.hpp | 83 +-- tests/geometryRTheta/CMakeLists.txt | 1 + .../advection_simulation_utils.hpp | 10 +- .../advection_field_rp/CMakeLists.txt | 52 ++ .../advection_field_gtest.cpp | 315 +++++++++++ .../test_cases_adv_field.hpp | 478 ++++++++++++++++ .../polar_poisson/CMakeLists.txt | 35 +- .../polar_poisson/test_vlasov_poisson.py | 68 --- .../sll/mapping/czarny_to_cartesian.hpp | 2 +- 27 files changed, 2060 insertions(+), 599 deletions(-) create mode 100644 src/geometryRTheta/advection_field/CMakeLists.txt create mode 100644 src/geometryRTheta/advection_field/README.md create mode 100644 src/geometryRTheta/advection_field/advection_field_rp.hpp delete mode 100644 src/geometryRTheta/poisson/vlasovpoissonsolver.hpp create mode 100644 tests/geometryRTheta/advection_field_rp/CMakeLists.txt create mode 100644 tests/geometryRTheta/advection_field_rp/advection_field_gtest.cpp create mode 100644 tests/geometryRTheta/advection_field_rp/test_cases_adv_field.hpp delete mode 100644 tests/geometryRTheta/polar_poisson/test_vlasov_poisson.py diff --git a/post-process/PythonScripts/geometryRTheta/animation_rho_phi.py b/post-process/PythonScripts/geometryRTheta/animation_rho_phi.py index 5209ed9a1..c870019c7 100644 --- a/post-process/PythonScripts/geometryRTheta/animation_rho_phi.py +++ b/post-process/PythonScripts/geometryRTheta/animation_rho_phi.py @@ -12,7 +12,7 @@ # --------------------------------------------------------------------------- current_folder = Path(__file__).parent -perturb_defaults = {'dioctotron':False, 'vortex_merger':True} +perturb_defaults = {'diocotron':False, 'vortex_merger':True} parser = argparse.ArgumentParser(description="Plot and save the density and the electrical potential solutions of a given output folder.") diff --git a/simulations/geometryRTheta/diocotron/diocotron.cpp b/simulations/geometryRTheta/diocotron/diocotron.cpp index 0df3dc8d5..04a5703b0 100644 --- a/simulations/geometryRTheta/diocotron/diocotron.cpp +++ b/simulations/geometryRTheta/diocotron/diocotron.cpp @@ -42,7 +42,6 @@ #include "spline_interpolator_2d_rp.hpp" #include "spline_quadrature.hpp" #include "trapezoid_quadrature.hpp" -#include "vlasovpoissonsolver.hpp" @@ -200,10 +199,9 @@ int main(int argc, char** argv) AdvectionPhysicalDomain advection_domain(mapping); - SplineFootFinder - find_feet(time_stepper, advection_domain, grid, builder, spline_evaluator_extrapol); + SplineFootFinder find_feet(time_stepper, advection_domain, builder, spline_evaluator_extrapol); - BslAdvectionRP advection_operator(interpolator, find_feet); + BslAdvectionRP advection_operator(interpolator, find_feet, mapping); diff --git a/simulations/geometryRTheta/diocotron/params.yaml b/simulations/geometryRTheta/diocotron/params.yaml index 151ce42f3..6b65de1af 100644 --- a/simulations/geometryRTheta/diocotron/params.yaml +++ b/simulations/geometryRTheta/diocotron/params.yaml @@ -8,7 +8,7 @@ Mesh: Time: delta_t: 0.1 - final_T: 100.0 + final_T: 70. Perturbation: charge_Q: 0 diff --git a/simulations/geometryRTheta/vortex_merger/vortex_merger.cpp b/simulations/geometryRTheta/vortex_merger/vortex_merger.cpp index 4a52076a7..3dccdd9d2 100644 --- a/simulations/geometryRTheta/vortex_merger/vortex_merger.cpp +++ b/simulations/geometryRTheta/vortex_merger/vortex_merger.cpp @@ -42,7 +42,6 @@ #include "spline_interpolator_2d_rp.hpp" #include "spline_quadrature.hpp" #include "trapezoid_quadrature.hpp" -#include "vlasovpoissonsolver.hpp" #include "vortex_merger_equilibrium.hpp" #include "vortex_merger_initialization.hpp" @@ -181,10 +180,9 @@ int main(int argc, char** argv) AdvectionPhysicalDomain advection_domain(mapping); - SplineFootFinder - find_feet(time_stepper, advection_domain, grid, builder, spline_evaluator_extrapol); + SplineFootFinder find_feet(time_stepper, advection_domain, builder, spline_evaluator_extrapol); - BslAdvectionRP advection_operator(interpolator, find_feet); + BslAdvectionRP advection_operator(interpolator, find_feet, mapping); diff --git a/src/geometryRTheta/CMakeLists.txt b/src/geometryRTheta/CMakeLists.txt index 22982823b..1f096409f 100644 --- a/src/geometryRTheta/CMakeLists.txt +++ b/src/geometryRTheta/CMakeLists.txt @@ -8,5 +8,6 @@ add_subdirectory(initialization) add_subdirectory(interpolation) add_subdirectory(poisson) add_subdirectory(time_solver) +add_subdirectory(advection_field) diff --git a/src/geometryRTheta/README.md b/src/geometryRTheta/README.md index 5144aed06..73e2e25be 100644 --- a/src/geometryRTheta/README.md +++ b/src/geometryRTheta/README.md @@ -4,14 +4,16 @@ The `geometryRTheta` folder contains all the code describing methods which are s - [advection](./advection/README.md) - Code describing advection operator and time integration methods used on 2D polar domain. +- [advection\_field](./advection_field/README.md) - Code describing computation of the advection field given to the advection operator. + - [geometry](./geometry/README.md) - All the dimension tags used for a gyrokinetic semi-Lagrangian simulation in a curvilinear geometry. +- [initialization](./initialization/README.md) - Initializes the simulations on a 2D polar domain. + - [interpolation](./interpolation/README.md) - Code describing interpolation methods on 2D polar domain. - [poisson](./poisson/README.md) - Code describing the polar Poisson solver. -- [initialization](./initialization/README.md) - Initializes the simulations on a 2D polar domain. - - [time\_solver](./time_solver/README.md) - The methods to solve in time the equations system. diff --git a/src/geometryRTheta/advection/README.md b/src/geometryRTheta/advection/README.md index 1d8a8692a..e7eaa09b4 100644 --- a/src/geometryRTheta/advection/README.md +++ b/src/geometryRTheta/advection/README.md @@ -1,20 +1,17 @@ # Advection operator -## Advection operator - -### Studied equation +## Studied equation The studied equation is the following 2D transport equation type : ```math \partial_t f(t,x,y) + A(t,x,y)\cdot\nabla f(t,x,y) = 0, ``` -with $`f(0,x,y) = f_0(x,y)`$ and $A$ the advection field. - -**We want to solve it on a polar grid.** +with $`f(0,x,y) = f_0(x,y)`$ and *A* the advection field. +**We want to solve it on a polar grid so we have:** $`(t,x,y) = (t,x(r,\theta),y(r,\theta))`$. -### Backward Semi-Lagrangian method +## Backward Semi-Lagrangian method The method used to solve the equation is a Backward Semi-Lagrangian method (BSL). It uses the conservation along the characteristics property: @@ -38,7 +35,7 @@ So to compute the advected function at the next time step, -### Time integration methods +## Time integration methods There are multiple time integration methods available which are implemented in the ITimeStepper child classes. For example: - Explicit Euler method: Euler; @@ -53,7 +50,7 @@ We are listing the different schemes for this equation $`\partial_t X (t) = A_x( We write $X (t) = X (t; s, x, y)$, $X^n = X(t^n)$ and $A^n(X) = A(t^n, X)$ for a time discretisation $`\{t^n; t^n > t^{n-1}, \forall n\}_n`$. -#### Explicit Euler method +### Explicit Euler method - Scheme: $X^n = X^{n+1} - dt A^{n+1}(X^{n+1})$ @@ -61,7 +58,7 @@ $X^n = X^{n+1} - dt A^{n+1}(X^{n+1})$ - Convergence order : 1. -#### Crank-Nicolson method +### Crank-Nicolson method - Scheme: $X^{k+1} = X^{n+1} - \frac{dt}{2} \left( A^{n+1}(X^{n+1}) + A^k(X^k) \right)$ and @@ -70,7 +67,7 @@ $X^{n+1} = X^{k+1}$ once converged. - Convergence order : 2. -#### RK3 method +### RK3 method - Scheme: $`X^n = X^{n+1} - \frac{dt}{6} \left( k_1 + 4 k_2 + k_3 \right)`$ @@ -83,7 +80,7 @@ $`X^n = X^{n+1} - \frac{dt}{6} \left( k_1 + 4 k_2 + k_3 \right)`$ -#### RK4 method +### RK4 method - Scheme: $`X^n = X^{n+1} - \frac{dt}{6} \left( k_1 + 2 k_2 + 2 k_3 + k_4\right)`$ @@ -97,7 +94,7 @@ $`X^n = X^{n+1} - \frac{dt}{6} \left( k_1 + 2 k_2 + 2 k_3 + k_4\right)`$ -### Advection domain +## Advection domain There are two advection domains implemented: - the physical domain: AdvectionPhysicalDomain; @@ -146,7 +143,124 @@ and $`(J_{\mathcal{F}}J_{\mathcal{G}}^{-1})^{-1}`$ is well-defined. The details **Remark 2:** if the mapping function is analytically invertible, it is less costly to advect in the physical domain. -## Unit tests + +## Advection Field + +In the studied equation, the advection field is given along the physical domain axis: +```math +\partial_t f + A_x \partial_x f + A_y \partial_y f = 0. +``` + +The BslAdvectionRP operator can take as input the advection field along the physical domain axis or the advection field along the logical domain axis, +```math +A = (A_x, A_y) \quad \text{or} \quad A = (A_r, A_\theta). +``` + +The advection field can be computed thanks to the AdvectionFieldFinder operator. This operator returns the advection field along the physical domain axes or the advection field along the logical domain axes (see [advection\_field\_rp](./../advection_field/README.md)). + +* If the advection field is directly given along the physical domain axes, no treatment is needed in the BslAdvectionRP operator. + +* If the advection field is given along the logical domain axes, then we need to compute the advection field along the physical domain axes to advect in the physical domain. + +**In the guiding-center case**, the advection field is computed from the electric field, +```math +A = - E \wedge e_z = -\nabla \phi \wedge e_z. +``` + +In [the documentation for the advection field](./../advection_field/README.md), we show that +```math +\nabla_{xy} \phi = J D_{G}^{-1}\nabla_{r\theta} \phi, +``` + +with *J* the Jacobian matrix and the following diagonal matrix +```math +D_{G} += +\begin{bmatrix} + \sqrt{g_{11}} & 0 \\ + 0 & \sqrt{g_{22}} \\ +\end{bmatrix} +``` + +with the metric tensor $`G = J^TJ = [g_{ij}]_{ij}`$. + +It gives the following relation for the electric field +```math +\begin{bmatrix} + E_x \\ + E_y \\ +\end{bmatrix} += +J D_{G}^{-1} +\begin{bmatrix} + E_r \\ + E_\theta \\ +\end{bmatrix} += +\begin{bmatrix} + \frac{j_{11}}{\sqrt{g_{11}}} & \frac{j_{12}}{\sqrt{g_{22}}} \\ + \frac{j_{21}}{\sqrt{g_{11}}} & \frac{j_{22}}{\sqrt{g_{22}}} \\ +\end{bmatrix} +\begin{bmatrix} + E_r \\ + E_\theta \\ +\end{bmatrix}. +``` + + +We deduce that +```math +\begin{bmatrix} + A_x \\ + A_y +\end{bmatrix} += +\begin{bmatrix} + - Ey \\ + E_x +\end{bmatrix} += +\begin{bmatrix} + - \frac{j_{21}}{\sqrt{g_{11}}}E_r - \frac{j_{22}}{\sqrt{g_{22}}} E_\theta\\ + \frac{j_{11}}{\sqrt{g_{11}}}E_r + \frac{j_{12}}{\sqrt{g_{22}}} E_\theta \\ +\end{bmatrix} += +\begin{bmatrix} + \frac{j_{22}}{\sqrt{g_{22}}} & - \frac{j_{21}}{\sqrt{g_{11}}} \\ + - \frac{j_{12}}{\sqrt{g_{22}}} & \frac{j_{11}}{\sqrt{g_{11}}} +\end{bmatrix} +\begin{bmatrix} + - E_\theta \\ + E_r +\end{bmatrix} += +\det(JD_{G}^{-1}) ((JD_{G}^{-1})^{-1})^{T} +\begin{bmatrix} + - E_\theta \\ + E_r +\end{bmatrix} += +\det(J) (J^{-1})^{T} det(D_{G}^{-1}) D_G +\begin{bmatrix} + A_r \\ + A_\theta +\end{bmatrix}. +``` + +So, from the advection field along the logical domain axis, we multiply by +```math +\det(J) det(D_{G}^{-1}) (J^{-1})^{T} D_G += +\begin{bmatrix} + \frac{j_{22}}{\sqrt{g_{22}}} & - \frac{j_{21}}{\sqrt{g_{11}}} \\ + - \frac{j_{12}}{\sqrt{g_{22}}} & \frac{j_{11}}{\sqrt{g_{11}}} +\end{bmatrix}, +``` + +to get the advection field along the physical domain axis. + + +# Unit tests The test of the advection operator are implemented in the `tests/geometryRTheta/advection_2d_rp/` folder ([advection\_2d\_rp](./../../../tests/geometryRTheta/advection_2d_rp/README.md)). @@ -181,14 +295,14 @@ for $n = 1, 2, 4, 8, ...$. -## References +# References [1] Edoardo Zoni, Yaman Güçlü. "Solving hyperbolic-elliptic problems on singular mapped disk-like domains with the method of characteristics and spline finite elements". ([https://doi.org/10.1016/j.jcp.2019.108889](https://doi.org/10.1016/j.jcp.2019.108889).) Journal of Computational Physics (2019). -## Contents +# Contents This folder contains: - advection\_domain.hpp : define the different advection domains (AdvectionDomain). diff --git a/src/geometryRTheta/advection/bsl_advection_rp.hpp b/src/geometryRTheta/advection/bsl_advection_rp.hpp index 712165e31..49ddbea9e 100644 --- a/src/geometryRTheta/advection/bsl_advection_rp.hpp +++ b/src/geometryRTheta/advection/bsl_advection_rp.hpp @@ -56,7 +56,7 @@ * @see AdvectionDomain * */ -template +template class BslAdvectionRP : public IAdvectionRP { private: @@ -64,6 +64,8 @@ class BslAdvectionRP : public IAdvectionRP FootFinder const& m_find_feet; + Mapping const& m_mapping; + public: /** @@ -74,15 +76,20 @@ class BslAdvectionRP : public IAdvectionRP * characteristic computed. * @param[in] foot_finder * An IFootFinder which computes the characteristic feet. + * @param[in] mapping + * The mapping function from the logical domain to the physical + * domain. * * @tparam IFootFinder * A child class of IFootFinder. */ BslAdvectionRP( PreallocatableSplineInterpolatorRP const& function_interpolator, - FootFinder const& foot_finder) + FootFinder const& foot_finder, + Mapping const& mapping) : m_interpolator(function_interpolator) , m_find_feet(foot_finder) + , m_mapping(mapping) { } @@ -94,28 +101,102 @@ class BslAdvectionRP : public IAdvectionRP * * @param [in, out] allfdistribu * A ChunkSpan containing the values of the function we want to advect. - * @param [in] advection_field + * @param [in] advection_field_xy * A VectorDViewRP containing the values of the advection field - * in the physical domain. + * on the physical domain axes. * @param [in] dt * A time step used. * * @return A ChunkSpan to allfdistribu advected on the time step given. */ - DSpanRP operator()(DSpanRP allfdistribu, VectorDViewRP advection_field, double dt) - const + DSpanRP operator()( + DSpanRP allfdistribu, + VectorDViewRP advection_field_xy, + double dt) const { // Pre-allocate some memory to prevent allocation later in loop std::unique_ptr const interpolator_ptr = m_interpolator.preallocate(); - // Initialisation the feet - FieldRP feet_rp(advection_field.domain()); - ddc::for_each(advection_field.domain(), [&](IndexRP const irp) { + // Initialise the feet + FieldRP feet_rp(advection_field_xy.domain()); + ddc::for_each(advection_field_xy.domain(), [&](IndexRP const irp) { feet_rp(irp) = ddc::coordinate(irp); }); // Compute the characteristic feet at tn ---------------------------------------------------- - m_find_feet(feet_rp.span_view(), advection_field, dt); + m_find_feet(feet_rp.span_view(), advection_field_xy, dt); + + // Interpolate the function on the characteristic feet. ------------------------------------- + (*interpolator_ptr)(allfdistribu, feet_rp.span_cview()); + + return allfdistribu; + } + + + /** + * @brief Allocate a ChunkSpan to the advected function. + * + * @param [in, out] allfdistribu + * A ChunkSpan containing the values of the function we want to advect. + * @param [in] advection_field_rp + * A VectorDViewRP containing the values of the advection field + * on the logical domain axis. + * @param [in] advection_field_xy_center + * A CoordXY containing the value of the advection field on the + * physical domain axis at the O-point. + * @param [in] dt + * A time step used. + * + * @return A ChunkSpan to allfdistribu advected on the time step given. + */ + DSpanRP operator()( + DSpanRP allfdistribu, + VectorDViewRP advection_field_rp, + CoordXY const& advection_field_xy_center, + double dt) const + { + IDomainRP grid(allfdistribu.domain()); + + const int npoints_p = IDomainP(grid).size(); + IDomainRP const grid_without_Opoint(grid.remove_first(IVectRP(1, 0))); + IDomainRP const Opoint_grid(grid.take_first(IVectRP(1, npoints_p))); + + + // Convert advection field on RP to advection field on XY + VectorDFieldRP advection_field_xy(grid); + + ddc::for_each(grid_without_Opoint, [&](IndexRP const irp) { + CoordRP const coord_rp(ddc::coordinate(irp)); + + std::array, 2> J; // Jacobian matrix + m_mapping.jacobian_matrix(coord_rp, J); + std::array, 2> G; // Metric tensor + m_mapping.metric_tensor(coord_rp, G); + + ddcHelper::get(advection_field_xy)(irp) + = ddcHelper::get(advection_field_rp)(irp) * J[1][1] / std::sqrt(G[1][1]) + + ddcHelper::get(advection_field_rp)(irp) * -J[1][0] + / std::sqrt(G[0][0]); + ddcHelper::get(advection_field_xy)(irp) + = ddcHelper::get(advection_field_rp)(irp) * -J[0][1] / std::sqrt(G[1][1]) + + ddcHelper::get(advection_field_rp)(irp) * J[0][0] + / std::sqrt(G[0][0]); + }); + + ddc::for_each(Opoint_grid, [&](IndexRP const irp) { + ddcHelper::get(advection_field_xy)(irp) = CoordX(advection_field_xy_center); + ddcHelper::get(advection_field_xy)(irp) = CoordY(advection_field_xy_center); + }); + + // Pre-allocate some memory to prevent allocation later in loop + std::unique_ptr const interpolator_ptr = m_interpolator.preallocate(); + + // Initialise the feet + FieldRP feet_rp(grid); + ddc::for_each(grid, [&](IndexRP const irp) { feet_rp(irp) = ddc::coordinate(irp); }); + + // Compute the characteristic feet at tn ---------------------------------------------------- + m_find_feet(feet_rp.span_view(), advection_field_xy, dt); // Interpolate the function on the characteristic feet. ------------------------------------- (*interpolator_ptr)(allfdistribu, feet_rp.span_cview()); diff --git a/src/geometryRTheta/advection/iadvectionrp.hpp b/src/geometryRTheta/advection/iadvectionrp.hpp index 6506ced00..89235c65e 100644 --- a/src/geometryRTheta/advection/iadvectionrp.hpp +++ b/src/geometryRTheta/advection/iadvectionrp.hpp @@ -15,12 +15,13 @@ class IAdvectionRP virtual ~IAdvectionRP() = default; /** - * @brief Advect a function along the advection field given on dt. + * @brief Advect a function along the advection field given on dt + * with a given advection field along XY. * * @param[in, out] allfdistribu * The function to be advected. * @param[in] advection_field - * The advection field. + * The advection field along the physical domain axes, XY. * @param[in] dt * The time step. * @@ -30,4 +31,26 @@ class IAdvectionRP DSpanRP allfdistribu, VectorDViewRP advection_field, double const dt) const = 0; + + /** + * @brief Advect a function along the advection field given on dt + * with a given advection field along RP. + * + * @param[in, out] allfdistribu + * The function to be advected. + * @param[in] advection_field + * The advection field along the logical domain axes, RP. + * @param[in] advection_field_xy_center + * The advection field along the physical domain axes, XY + * at the center point. + * @param[in] dt + * The time step. + * + * @return A ChunkSpan to the advected function (allfdistribu). + */ + virtual DSpanRP operator()( + DSpanRP allfdistribu, + VectorDViewRP advection_field, + CoordXY const& advection_field_xy_center, + double const dt) const = 0; }; diff --git a/src/geometryRTheta/advection/spline_foot_finder.hpp b/src/geometryRTheta/advection/spline_foot_finder.hpp index 15c2f596e..5b898d8e4 100644 --- a/src/geometryRTheta/advection/spline_foot_finder.hpp +++ b/src/geometryRTheta/advection/spline_foot_finder.hpp @@ -47,8 +47,6 @@ class SplineFootFinder : public IFootFinder * @param[in] advection_domain * An AdvectionDomain object which defines in which domain we * advect the characteristics. - * @param[in] dom - * The domain on which the feet are defined. * @param[in] builder_advection_field * The spline builder which computes the spline representation * of the advection field. @@ -65,7 +63,6 @@ class SplineFootFinder : public IFootFinder SplineFootFinder( TimeStepper const& time_stepper, AdvectionDomain const& advection_domain, - IDomainRP const& dom, SplineRPBuilder const& builder_advection_field, SplineRPEvaluator const& evaluator_advection_field) : m_time_stepper(time_stepper) diff --git a/src/geometryRTheta/advection_field/CMakeLists.txt b/src/geometryRTheta/advection_field/CMakeLists.txt new file mode 100644 index 000000000..c08621390 --- /dev/null +++ b/src/geometryRTheta/advection_field/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.15) + + +add_library("advection_field_RTheta" INTERFACE) +target_include_directories("advection_field_RTheta" + INTERFACE + "$" +) +target_link_libraries("advection_field_RTheta" INTERFACE + DDC::DDC + sll::splines + gslx::speciesinfo + gslx::utils + gslx::poisson_RTheta +) +add_library("gslx::advection_field_RTheta" ALIAS "advection_field_RTheta") \ No newline at end of file diff --git a/src/geometryRTheta/advection_field/README.md b/src/geometryRTheta/advection_field/README.md new file mode 100644 index 000000000..4d681d923 --- /dev/null +++ b/src/geometryRTheta/advection_field/README.md @@ -0,0 +1,258 @@ +# Advection Field finder + + +The operator implemented here is a previous step to the advection operator. +It computes the advection field along the axes in the physical domain or the axes in the logical domain. + +Currently, the implemented case is: +* Guiding center equations system. + + +## Guiding center case + +The studied equation system is of the following type : +```math +\partial_t \rho + A\cdot\nabla \rho = 0, \\ +A = E \wedge e_z, \\ +E = - \nabla \phi, \\ +- \nabla \cdot \nabla \phi = \rho, +``` + +with $`\rho`$ the density, $`\phi`$ the electrostatic potential and $`E`$ the electrical field. + +The AdvectionFieldFinder computes the advection field $`A`$ from the electrical field $`\phi`$ returned by the PolarSplineFEMPoissonSolver. +It has two types of `operator()`: +* one returning the advection field along the axis of the physical domain: $`A = (A_x, A_y)`$ +* and the another returning the advection field along the axis of the logical domain: $`A = (A_r, A_\theta)`$. + +The PolarSplineFEMPoissonSolver can return the solution $`\phi`$ of the Poisson equation under two forms: +* a Chunk of values of the solution on the mesh points of the grid; +* a PolarSpline representation of the solution. + +The AdvectionFieldFinder can handle as input the two forms. +If a Chunk is given as input, it computes the spline representation (on the cross-product of two 1D bases) using a SplineBuilder2D. +The spline representation is needed to compute the derivatives of the function $`\phi`$. +If the PolarSpline representation is given as input, it can directly compute the derivatives of the function $`\phi`$. + +Once the advection field computed, it is given as input to the BslAdvectionRP operator to advect the density $`\rho`$ function. +The BslAdvectionRP operator can handle the advection with an advection field along $`(x,y)`$ and with an advection field along $`(r,\theta)`$. +But as the BslAdvectionRP operator advects in the physical domain, it is recommend to work with the advection field along $`(x,y)`$. + + +### Advection field along the physical domain axis + +Thanks to the spline representation, the derivatives $`\partial_r \phi`$ and $`\partial_\theta \phi`$ are computed. +The computation of the electrical field can be ill-defined around the O-point so we treat this area separately. + +* If $`r > \varepsilon`$, we use +```math +\begin{bmatrix} + \partial_x \phi \\ + \partial_y \phi \\ +\end{bmatrix} += +J^{-T} +\begin{bmatrix} + \partial_r \phi \\ + \partial_\theta \phi \\ +\end{bmatrix} +``` + +with $`J`$ the Jacobian matrix of the mapping $`\mathcal{F}: (r,\theta)\mapsto(x,y)`$. Then the electric field is given by +```math +E = -\nabla \phi += +\begin{bmatrix} + - \partial_x \phi \\ + - \partial_y \phi \\ +\end{bmatrix} +``` + +and the advection field by +```math +A = E\wedge e_z += +\begin{bmatrix} + - E_y \\ + E_x \\ +\end{bmatrix} += +\begin{bmatrix} + \partial_y \phi \\ + - \partial_x \phi \\ +\end{bmatrix}. +``` + +* If $`r \leq \varepsilon`$, we linearise. The method is detailed in Edoardo Zoni's article [1]. We use only the derivatives along $`r`$ at two linearly independent directions of $`\theta`$ : $`\theta_1`$ and $`\theta_2`$ +```math +\partial_r \phi (0, \theta_1) = \left[\partial_r x \partial_x \phi + \partial_r y \partial_y \phi \right] (0, \theta_1), \\ +\partial_r \phi (0, \theta_2) = \left[\partial_r x \partial_x \phi + \partial_r y \partial_y \phi \right] (0, \theta_2). +``` + +From these equations, we deduce the (unique) values of $`\partial_x\phi`$ and $`\partial_y\phi`$ at $`(x,y) = (0,0)`$, + +```math +\begin{bmatrix} + \partial_x \phi (0, \theta) \\ + \partial_y \phi (0, \theta) \\ +\end{bmatrix} + = + \begin{bmatrix} + \partial_r x (0, \theta_1) & \partial_r y (0, \theta_1) \\ + \partial_r x (0, \theta_2) & \partial_r y (0, \theta_2) \\ +\end{bmatrix} ^{-1} +\begin{bmatrix} + \partial_r \phi (0, \theta_1) \\ + \partial_r \phi (0, \theta_2) \\ +\end{bmatrix}. +``` + +Then we compute $`E`$ at $`(x,y) = (0,0)`$ and $`(x,y) = \mathcal{F}(\varepsilon,\theta)`$ $`\forall \theta`$ (for $`\varepsilon\neq 0`$, we use the Jacobian matrix as previously) and we linearise + +```math +E_x(r, \theta) = \left( 1 - \frac{r}{\varepsilon} \right) E_x(0, \theta) + \frac{r}{\varepsilon} E_x(\varepsilon, \theta), \\ +E_y(r, \theta) = \left( 1 - \frac{r}{\varepsilon} \right) E_y(0, \theta) + \frac{r}{\varepsilon} E_y(\varepsilon, \theta), +``` + +As previously, we compute the advection field by +```math +A = E\wedge e_z += +\begin{bmatrix} + - E_y \\ + E_x \\ +\end{bmatrix} += +\begin{bmatrix} + \partial_y \phi \\ + - \partial_x \phi \\ +\end{bmatrix}. +``` + +(In the code, we chose $`\theta_1 = \frac{\pi}{4}`$ and $`\theta_2 = - \frac{\pi}{4}`$, and $\varepsilon = 10^{-12}$.) + + +### Advection field along the logical domain axis + +Firstly, the derivatives $`\partial_r \phi`$ and $`\partial_\theta \phi`$ are also computed here. + +#### General coordinates system +* In **general coordinates system**, the gradiant of a function is given by + +```math +\nabla f = \sum_i \sum_j \partial_{x_i} f g^{ij} \sqrt{g_{jj}} \hat{e}_j, +``` + +with +* $`J`$ the Jacobian matrix associated the the mapping function of the system $`\mathcal{F}:(x_1, x_2)\mapsto(y_1,y_2)`$, +* $`G = J^T J = [g_{ij}]_{ij}`$ the metric tensor, +* $`G^{-1} = [g^{ij}]_{ij}`$ the inverse metric tensor +* and $`\hat{e}_j`$ the normalized covariant vectors. + +In 2D, it can be rewritten as the following matrix system +```math +\nabla f = +D_{G} G^{-T} +\begin{bmatrix} + \partial_{x_1} f \\ + \partial_{x_2} f \\ +\end{bmatrix} += +\begin{bmatrix} + \sqrt{g_{11}} & 0 \\ + 0 & \sqrt{g_{22}} \\ +\end{bmatrix} +\begin{bmatrix} + g^{11} & g^{21} \\ + g^{12} & g^{22} \\ +\end{bmatrix} +\begin{bmatrix} + \partial_{x_1} f \\ + \partial_{x_2} f \\ +\end{bmatrix}. +``` + +**Remark:** We can prove that $`\det(D_{G} G^{-T}) = 0`$ if $`\det(J) = 0`$ (if the coefficients of the matrix $`D_G`$ are not null). +So for an invertible matrix, we also have the relation +```math +\begin{bmatrix} + \partial_{x_1} f \\ + \partial_{x_2} f \\ +\end{bmatrix} += +G^{T}D_{G}^{-1} \nabla f. +``` + +From the relation +```math +\begin{bmatrix} + \partial_{y_1} f \\ + \partial_{y_2} f \\ +\end{bmatrix} += +J^{-T} +\begin{bmatrix} + \partial_{x_1} f \\ + \partial_{x_2} f \\ +\end{bmatrix}, +``` + +we deduce the following relation for invertible case +```math +\nabla_{y_1, y_2} f += +J D_{G}^{-1} +\nabla_{x_1, x_2} f, +``` + +with $`\nabla_{y_1, y_2} f = [\partial_{y_1} f, \partial_{y_2} f]^T`$ and $`\nabla_{x_1, x_2} f = \sum_i \sum_j \partial_{x_i} f g^{ij} \sqrt{g_{jj}} \hat{e}_j`$. + + +#### Aplication to the advection field +* In our case, we use this formula to compute the electric field along the logical axis: +```math +E += -\nabla \phi += - D_{G} (G^{-1})^{T} +\begin{bmatrix} + \partial_{r} \phi \\ + \partial_{\theta} \phi \\ +\end{bmatrix}. +``` + +Then the advection field is given by +```math +A += E \wedge e_z += +\begin{bmatrix} + -E_{\theta} \\ + E_r \\ +\end{bmatrix}. +``` + +Warning, the matrix $`(G^{-1})^{T}`$ is ill-defined for $r = 0$. + +*Example: circular mapping:* +```math +(G^{-1})^{T} += +\begin{bmatrix} + 1 & 0 \\ + 0 & \frac{1}{r^2} \\ +\end{bmatrix}. +``` + +In the code, the O-point is differently treated. The domain is splitted between a domain without the O-point ($`(0,\theta), \forall \theta`$) and the domain containing only the O-point. For the first domain, we compute the advection field along the logical axis as explain previously. On the second domain, we compute the unique value of the advection field along the physical axis using the linearisation done in the *Advection field along the physical domain axis* section. + + + +# References + +[1] Edoardo Zoni, Yaman Güçlü, "Solving hyperbolic-elliptic problems on singular mapped disk-like domains with the +method of characteristics and spline finite elements", https://doi.org/10.1016/j.jcp.2019.108889, Journal of Computational Physics, 2019. + + +# Contents + +* advection\_field\_rp.hpp : containing AdvectionFieldFinder with the advection field computation for the guiding center simulation. \ No newline at end of file diff --git a/src/geometryRTheta/advection_field/advection_field_rp.hpp b/src/geometryRTheta/advection_field/advection_field_rp.hpp new file mode 100644 index 000000000..7132e75da --- /dev/null +++ b/src/geometryRTheta/advection_field/advection_field_rp.hpp @@ -0,0 +1,527 @@ +#pragma once + + +#include + +#include +#include + +#include +#include +#include +#include + +#include "ipoissonsolver.hpp" +#include "poisson_rhs_function.hpp" +#include "polarpoissonsolver.hpp" + + + +/** + * @brief Solve the Poisson equation and return the electric + * field for the coupled Vlasov equation. + * + * The Vlasov-Poisson equations are given by + * + * - (1) @f$ \partial_t \rho - E_y \partial_x \rho + E_x \partial_y\rho = 0 @f$, + * + * - (2) @f$ - \Delta \phi = \rho @f$, + * + * - (3) and @f$ E = -\nabla \phi @f$. + * + * The functions are defined on a logical domain, and the mapping from the logical + * domain to the physical domain is written @f$\mathcal{F}@f$. + * + * We here focus on equation (3). The @f$ \phi @f$ is already computed + * on B-splines with the given Poisson solver. Then in the AdvectionFieldRP::operator() + * we compute the advection field (@f$A = E \wedge e_z@f$) thanks to (3) using the B-splines coefficients. + * Depending on the given mapping, the computation at the center point is not + * always well-defined so we linearize around the center point as explained + * in Edoardo Zoni's article (https://doi.org/10.1016/j.jcp.2019.108889). + * + * The advection field can be computed along the logical domain axis or the physical domain + * axis. + * + * 1- In the first case, we compute the electric field thanks to (3) and + * - @f$ \nabla_{x,y} \phi(r, \theta) = (J^{-1})^{T} [\partial_r \phi, \partial_\theta \phi]^T @f$, + * - @f$ E(r, \theta) = -\nabla_{x,y} \phi(r, \theta) @f$, + * + * For @f$ r < \varepsilon @f$, @f$(J^{-1})^{T}@f$ is ill-defined so we linearize + * @f$ E(r, \theta) = \left( 1 - \frac{r}{\varepsilon} \right) E(0, \theta) + * + \frac{r}{\varepsilon} E(\varepsilon, \theta) @f$, + * + * with @f$ E(0, \theta) @f$ computed thanks to + * + * - @f$ \partial_r \phi (0, \theta_1) = \left[\partial_r x \partial_x \phi + * + \partial_r y \partial_y \phi \right](0, \theta_1) @f$, + * + * - @f$ \partial_r \phi (0, \theta_2) = \left[\partial_r x \partial_x \phi + * + \partial_r y \partial_y \phi \right] (0, \theta_2) @f$, + * + * where @f$ \theta_1 @f$ and @f$ \theta_2 @f$ correspond to + * linearly independent directions. + * + * + * Then the advection field along the physical domain axis + * is given by @f$A = E \wedge e_z@f$. + * + * + * 2- In the second case, the advection field along the logical domain axis + * is computed with + * - @f$ \nabla \phi = \sum_{i,j} \partial_{x_i} f g^{ij} \sqrt{g_{jj}} \hat{e}_j@f$, + * - with @f$g^{ij}@f$, the coefficients of the inverse metrix tensor, + * - @f$g_{jj}@f$, the coefficients of the metrix tensor, + * - @f$\hat{e}_j@f$, the normalized covariants vectors. + * + * Then, we compute @f$ E = -\nabla \phi @f$ and @f$A = E \wedge e_z@f$. + * + * + * The equation (1) is solved thanks to advection operator (IAdvectionRP). + * + * + * @tparam Mapping A Curvilinear2DToCartesian class. + * + * + * @see PolarSplineFEMPoissonSolver + * + */ +template +class AdvectionFieldFinder +{ +public: + /** + * @brief Define a 2x2 matrix with an 2D array of an 2D array. + */ + using Matrix_2x2 = std::array, 2>; + +private: + Mapping const& m_mapping; + + PolarSplineEvaluator const m_polar_spline_evaluator; + + SplineRPEvaluator const m_spline_evaluator; + + double const m_epsilon; + + static constexpr int n_overlap_cells = PolarBSplinesRP::continuity + 1; + +public: + /** + * @brief Instantiate a AdvectionFieldRP . + * + * @param[in] mapping + * The mapping @f$ \mathcal{F} @f$ from the logical domain to the physical domain. + * @param[in] epsilon + * The parameter @f$ \varepsilon @f$ for the linearization of the + * electric field. + */ + AdvectionFieldFinder(Mapping const& mapping, double const epsilon = 1e-12) + : m_mapping(mapping) + , m_polar_spline_evaluator(g_polar_null_boundary_2d) + , m_spline_evaluator( + g_null_boundary_2d, + g_null_boundary_2d, + g_null_boundary_2d, + g_null_boundary_2d) + , m_epsilon(epsilon) {}; + + ~AdvectionFieldFinder() {}; + + + + // ------------------------------------------------------------------------------------------- + // COMPUTE ADVECTION FIELD IN XY: | + // Advection field along the physical directions. | + // ------------------------------------------------------------------------------------------- + + + /** + * @brief Compute the advection field from a Chunk of @f$\phi@f$ values. + * + * @param[in] electrostatic_potential + * The values of the solution @f$\phi@f$ of the Poisson equation (2). + * @param[out] advection_field_xy + * The advection field on the physical axis. + */ + void operator()( + DSpanRP electrostatic_potential, + VectorFieldSpan> advection_field_xy) const + { + auto const grid = advection_field_xy.domain(); + + // Compute the spline representation of the electrostatic potential + SplineRPBuilder const builder(grid); + BSDomainRP const dom_bsplinesRP = builder.spline_domain(); + Spline2D electrostatic_potential_coef(dom_bsplinesRP); + builder(electrostatic_potential_coef, electrostatic_potential); + + (*this)(electrostatic_potential_coef.span_view(), advection_field_xy); + } + + + + /** + * @brief Compute the advection field from a spline representation of @f$\phi@f$ solution. + * The B-splines basis used is the cross-product of two 1D B-splines basis. + * + * @param[in] electrostatic_potential_coef + * The spline representation of the solution @f$\phi@f$ of the Poisson equation (2). + * @param[out] advection_field_xy + * The advection field on the physical axis. + */ + void operator()( + Spline2DSpan electrostatic_potential_coef, + VectorFieldSpan> advection_field_xy) const + { + compute_advection_field_XY( + m_spline_evaluator, + electrostatic_potential_coef, + advection_field_xy); + } + + + /** + * @brief Compute the advection field from the Poisson equation solution. + * The B-splines basis used is the polar B-splines (PolarSpline). + * + * @param[in] electrostatic_potential_coef + * The polar spline representation of the solution @f$\phi@f$ of the Poisson equation (2). + * @param[out] advection_field_xy + * The advection field on the physical axis. + */ + void operator()( + SplinePolar& electrostatic_potential_coef, + VectorFieldSpan> advection_field_xy) const + { + compute_advection_field_XY( + m_polar_spline_evaluator, + electrostatic_potential_coef, + advection_field_xy); + } + + +private: + /** + * @brief Compute the advection field along the physical axis. + * + * @param[in] evaluator + * The spline evaluator used to evaluated electrostatic_potential_coef. + * @param[in] electrostatic_potential_coef + * The spline representation of the solution @f$\phi@f$ of the Poisson equation (2). + * @param[out] advection_field_xy + * The advection field on the physical axis. + */ + template + void compute_advection_field_XY( + Evaluator evaluator, + SplineType& electrostatic_potential_coef, + VectorFieldSpan> advection_field_xy) const + { + assert((std::is_same_v< + Evaluator, + SplineRPEvaluator> && std::is_same_v) + || (std::is_same_v< + Evaluator, + PolarSplineEvaluator< + PolarBSplinesRP>> && std::is_same_v)); + + IDomainRP const grid = advection_field_xy.domain(); + VectorDFieldRP electric_field(grid); + + FieldRP coords(grid); + ddc::for_each(grid, [&](IndexRP const irp) { coords(irp) = ddc::coordinate(irp); }); + + // > computation of the phi derivatives + DFieldRP deriv_r_phi(grid); + DFieldRP deriv_p_phi(grid); + + evaluator.deriv_dim_1( + deriv_r_phi.span_view(), + coords.span_cview(), + electrostatic_potential_coef); + evaluator.deriv_dim_2( + deriv_p_phi.span_view(), + coords.span_cview(), + electrostatic_potential_coef); + + // > computation of the electric field + ddc::for_each(grid, [&](IndexRP const irp) { + double const r = ddc::coordinate(ddc::select(irp)); + double const th = ddc::coordinate(ddc::select(irp)); + + if (r > m_epsilon) { + CoordRP const coord_rp(r, th); + + Matrix_2x2 inv_J; // Inverse Jacobian matrix + m_mapping.inv_jacobian_matrix(coord_rp, inv_J); + + // Gradiant of phi in the physical domain (Cartesian domain) + double const deriv_x_phi + = deriv_r_phi(irp) * inv_J[0][0] + deriv_p_phi(irp) * inv_J[1][0]; + double const deriv_y_phi + = deriv_r_phi(irp) * inv_J[0][1] + deriv_p_phi(irp) * inv_J[1][1]; + + // E = -grad phi + ddcHelper::get(electric_field)(irp) = -deriv_x_phi; + ddcHelper::get(electric_field)(irp) = -deriv_y_phi; + + } else { + // Linearisation of the electric field + double const th1 = M_PI / 4.; + double const th2 = -M_PI / 4. + 2 * M_PI; + + // --- Value at r = 0: + CoordRP const coord_1_0(0, th1); + CoordRP const coord_2_0(0, th2); + + double const dr_x_1 = m_mapping.jacobian_11(coord_1_0); // dr_x (0, th1) + double const dr_y_1 = m_mapping.jacobian_21(coord_1_0); // dr_y (0, th1) + + double const dr_x_2 = m_mapping.jacobian_11(coord_2_0); // dr_x (0, th2) + double const dr_y_2 = m_mapping.jacobian_21(coord_2_0); // dr_y (0, th2) + + double deriv_r_phi_1 + = evaluator.deriv_dim_1(coord_1_0, electrostatic_potential_coef); + double deriv_r_phi_2 + = evaluator.deriv_dim_1(coord_2_0, electrostatic_potential_coef); + + double const determinant = dr_x_1 * dr_y_2 - dr_x_2 * dr_y_1; + + double const deriv_x_phi_0 + = (dr_y_2 * deriv_r_phi_1 - dr_y_1 * deriv_r_phi_2) / determinant; + double const deriv_y_phi_0 + = (-dr_x_2 * deriv_r_phi_1 + dr_x_1 * deriv_r_phi_2) / determinant; + + // E = -grad phi + double const electric_field_x_0 = -deriv_x_phi_0; + double const electric_field_y_0 = -deriv_y_phi_0; + + + + // --- Value at r = m_epsilon: + CoordRP const coord_rp_epsilon(m_epsilon, th); + + Matrix_2x2 inv_J_eps; // Jacobian matrix + m_mapping.inv_jacobian_matrix(coord_rp_epsilon, inv_J_eps); + + double const deriv_r_phi_epsilon + = evaluator.deriv_dim_1(coord_rp_epsilon, electrostatic_potential_coef); + double const deriv_p_phi_epsilon + = evaluator.deriv_dim_2(coord_rp_epsilon, electrostatic_potential_coef); + + // Gradiant of phi in the physical domain (Cartesian domain) + double const deriv_x_phi_epsilon = deriv_r_phi_epsilon * inv_J_eps[0][0] + + deriv_p_phi_epsilon * inv_J_eps[1][0]; + double const deriv_y_phi_epsilon = deriv_r_phi_epsilon * inv_J_eps[0][1] + + deriv_p_phi_epsilon * inv_J_eps[1][1]; + + // E = -grad phi + double const electric_field_x_epsilon = -deriv_x_phi_epsilon; + double const electric_field_y_epsilon = -deriv_y_phi_epsilon; + + + // --- Linearisation: + ddcHelper::get(electric_field)(irp) + = electric_field_x_0 * (1 - r / m_epsilon) + + electric_field_x_epsilon * r / m_epsilon; + ddcHelper::get(electric_field)(irp) + = electric_field_y_0 * (1 - r / m_epsilon) + + electric_field_y_epsilon * r / m_epsilon; + } + + // > computation of the advection field + ddcHelper::get(advection_field_xy)(irp) + = -ddcHelper::get(electric_field)(irp); + ddcHelper::get(advection_field_xy)(irp) + = ddcHelper::get(electric_field)(irp); + }); + } + + + +public: + // ------------------------------------------------------------------------------------------- + // COMPUTE ADVECTION FIELD IN RP: | + // Advection field along the logical directions. | + // ------------------------------------------------------------------------------------------- + + + /** + * @brief Compute the advection field from a Chunk of @f$\phi@f$ values. + * + * @param[in] electrostatic_potential + * The values of the solution @f$\phi@f$ of the Poisson equation (2). + * @param[out] advection_field_rp + * The advection field on the logical axis. + * @param[out] advection_field_xy_center + * The advection field on the physical axis at the O-point. + */ + void operator()( + DSpanRP electrostatic_potential, + VectorFieldSpan> advection_field_rp, + CoordXY& advection_field_xy_center) const + { + auto const grid = electrostatic_potential.domain(); + + // Compute the spline representation of the electrostatic potential + SplineRPBuilder const builder(grid); + BSDomainRP const dom_bsplinesRP = builder.spline_domain(); + Spline2D electrostatic_potential_coef(dom_bsplinesRP); + builder(electrostatic_potential_coef, electrostatic_potential); + + (*this)(electrostatic_potential_coef.span_view(), + advection_field_rp, + advection_field_xy_center); + } + + + + /** + * @brief Compute the advection field from a spline representation of @f$\phi@f$. + * The B-splines basis used is the cross-product of two 1D B-splines basis. + * + * @param[in] electrostatic_potential_coef + * The spline representation of the solution @f$\phi@f$ of the Poisson equation (2). + * @param[out] advection_field_rp + * The advection field on the logical axis. + * @param[out] advection_field_xy_center + * The advection field on the physical axis at the O-point. + */ + void operator()( + Spline2DSpan electrostatic_potential_coef, + VectorFieldSpan> advection_field_rp, + CoordXY& advection_field_xy_center) const + { + compute_advection_field_RP( + m_spline_evaluator, + electrostatic_potential_coef, + advection_field_rp, + advection_field_xy_center); + } + + + /** + * @brief Compute the advection field from the Poisson equation. + * The B-splines basis used is the polar B-splines (PolarSpline). + * + * @param[in] electrostatic_potential_coef + * The polar spline representation of the solution @f$\phi@f$ of the Poisson equation (2). + * @param[out] advection_field_rp + * The advection field on the logical axis. + * @param[out] advection_field_xy_center + * The advection field on the physical axis at the O-point. + */ + void operator()( + SplinePolar& electrostatic_potential_coef, + VectorFieldSpan> advection_field_rp, + CoordXY& advection_field_xy_center) const + { + compute_advection_field_RP( + m_polar_spline_evaluator, + electrostatic_potential_coef, + advection_field_rp, + advection_field_xy_center); + } + + + +private: + /** + * @brief Compute the advection field along the logical axis. + * + * @param[in] evaluator + * The spline evaluator used to evaluated electrostatic_potential_coef. + * @param[in] electrostatic_potential_coef + * The spline representation of the solution @f$\phi@f$ of the Poisson equation (2). + * @param[out] advection_field_rp + * The advection field on the logical axis on a domain without O-point. + * @param[out] advection_field_xy_center + * The advection field on the physical axis at the O-point. + */ + template + void compute_advection_field_RP( + Evaluator evaluator, + SplineType& electrostatic_potential_coef, + VectorFieldSpan> advection_field_rp, + CoordXY& advection_field_xy_center) const + { + assert((std::is_same_v< + Evaluator, + SplineRPEvaluator> && std::is_same_v) + || (std::is_same_v< + Evaluator, + PolarSplineEvaluator< + PolarBSplinesRP>> && std::is_same_v)); + + IDomainRP const grid_without_Opoint = advection_field_rp.domain(); + + FieldRP coords(grid_without_Opoint); + ddc::for_each(grid_without_Opoint, [&](IndexRP const irp) { + coords(irp) = ddc::coordinate(irp); + }); + + // > computation of the phi derivatives + DFieldRP deriv_r_phi(grid_without_Opoint); + DFieldRP deriv_p_phi(grid_without_Opoint); + + evaluator.deriv_dim_1( + deriv_r_phi.span_view(), + coords.span_cview(), + electrostatic_potential_coef); + evaluator.deriv_dim_2( + deriv_p_phi.span_view(), + coords.span_cview(), + electrostatic_potential_coef); + + // > computation of the advection field + ddc::for_each(grid_without_Opoint, [&](IndexRP const irp) { + CoordRP const coord_rp(ddc::coordinate(irp)); + + Matrix_2x2 J; // Jacobian matrix + m_mapping.jacobian_matrix(coord_rp, J); + Matrix_2x2 inv_G; // Inverse metric tensor + m_mapping.inverse_metric_tensor(coord_rp, inv_G); + Matrix_2x2 G; // Metric tensor + m_mapping.metric_tensor(coord_rp, G); + + // E = -grad phi + double const electric_field_r + = (-deriv_r_phi(irp) * inv_G[0][0] - deriv_p_phi(irp) * inv_G[1][0]) + * std::sqrt(G[0][0]); + double const electric_field_p + = (-deriv_r_phi(irp) * inv_G[0][1] - deriv_p_phi(irp) * inv_G[1][1]) + * std::sqrt(G[1][1]); + + // A = E \wedge e_z + ddcHelper::get(advection_field_rp)(irp) = -electric_field_p; + ddcHelper::get(advection_field_rp)(irp) = electric_field_r; + }); + + // SPECIAL TREATMENT FOR THE O-POINT ===================================================== + // Linearisation of the electric field + double const th1 = M_PI / 4.; + double const th2 = -M_PI / 4. + 2 * M_PI; + + // --- Value at r = 0: + CoordRP const coord_1_0(0, th1); + CoordRP const coord_2_0(0, th2); + + double const dr_x_1 = m_mapping.jacobian_11(coord_1_0); // dr_x (0, th1) + double const dr_y_1 = m_mapping.jacobian_21(coord_1_0); // dr_y (0, th1) + + double const dr_x_2 = m_mapping.jacobian_11(coord_2_0); // dr_x (0, th2) + double const dr_y_2 = m_mapping.jacobian_21(coord_2_0); // dr_y (0, th2) + + double const deriv_r_phi_1 = evaluator.deriv_dim_1(coord_1_0, electrostatic_potential_coef); + double const deriv_r_phi_2 = evaluator.deriv_dim_1(coord_2_0, electrostatic_potential_coef); + + double const determinant = dr_x_1 * dr_y_2 - dr_x_2 * dr_y_1; + + double const deriv_x_phi_0 + = (dr_y_2 * deriv_r_phi_1 - dr_y_1 * deriv_r_phi_2) / determinant; + double const deriv_y_phi_0 + = (-dr_x_2 * deriv_r_phi_1 + dr_x_1 * deriv_r_phi_2) / determinant; + + advection_field_xy_center = CoordXY(deriv_y_phi_0, -deriv_x_phi_0); + } +}; diff --git a/src/geometryRTheta/poisson/README.md b/src/geometryRTheta/poisson/README.md index 4185114f1..af08e1524 100644 --- a/src/geometryRTheta/poisson/README.md +++ b/src/geometryRTheta/poisson/README.md @@ -69,49 +69,6 @@ So we compute the solution B-splines coefficients $`\{\phi_l\}_l`$ by solving th - -## Evaluation of electric field - -(See for more details, Edoardo Zoni's article, https://doi.org/10.1016/j.jcp.2019.108889 .) - -We write in this section $`\nabla_{r,\theta}`$ the gradient in the logical coordinates $(r, \theta)$ -and $`\nabla_{x,y}`$ the gradient in the physical coordinates $(x, y)$. - -Coupled with the Vlasov equation -```math -\partial_t \rho - E_y \partial_x \rho + E_x \partial_y\rho = 0, -``` - -the VlasovPoissonSolver also computes the electric field $E$ in the physical domain from the solution of PolarSplineFEMPoissonSolver. - -The electric field is given by $E = -\nabla \phi$. The solution of the Poisson solver is defined -on the logical domain, so we can easily compute $`\nabla_{r,\theta} \phi`$. From that, we use the Jacobian -matrix of the mapping $\mathcal{F}$: - -```math -E_r e_r + E_\theta e_\theta = -\nabla_{r,\theta} \phi,\\ -E_x e_x + E_y e_y = J_{\mathcal{F}}(r,\theta)) (E_r e_r + E_\theta e_\theta), -``` - -with $`e_i = \partial_{x_i}x`$ the unnormalized local contravariant base. - -However the inverse Jacobian matrix $`J_{\mathcal{F}}`$ can be ill-defined at the O-point. In -Edoardo Zoni's article, they suggest to linearize around the O-point: - -for $`r < \varepsilon`$, -```math -E(r, \theta) = \left( 1 - \frac{r}{\varepsilon} \right) E(0, \theta) + \frac{r}{\varepsilon} E(\varepsilon, \theta) -``` - -with $E(0, \theta)$ computed thanks to -* $`\partial_r \phi (0, \theta_1) = \left[\partial_r x \partial_x \phi + \partial_r y \partial_y \phi \right] (0, \theta_1)`$, and -* $`\partial_r \phi (0, \theta_2) = \left[\partial_r x \partial_x \phi + \partial_r y \partial_y \phi \right] (0, \theta_2)`$, -* where $`\theta_1`$ and $`\theta_2`$ correspond to linearly independent directions. - - -(In the code, we chose $`\theta_1 = \frac{\pi}{4}`$ and $`\theta_2 = - \frac{\pi}{4}`$, and $\varepsilon = 10^{-12}$.) - - ## Unit tests The test are implemented in the `tests/geometryRTheta/polar_poisson/` folder @@ -129,8 +86,6 @@ The PolarSplineFEMPoissonSolver is tested on a circular mapping (CircularToCarte * cartesian solution: $\phi(x,y) = C (1+r(x,y))^6 (1 - r(x,y))^6 \cos(2\pi x) \sin(2\pi y)$, * with $C = 2^{12}1e-4$. - The VlasovPoissonSolver is tested on a circular mapping (CircularToCartesian) and on a Czarny mapping (CzarnyToCartesian) - with the same Poisson coeffiecients and for the cartesian solution. ## References @@ -146,5 +101,3 @@ method of characteristics and spline finite elements", https://doi.org/10.1016/j * ipoissonsolver.hpp : Define a base class for the Poisson solvers: IPoissonSolver. * polarpoissonsolver.hpp : Define a Poisson solver using FEM on B-splines: PolarSplineFEMPoissonSolver. * poisson\_rhs\_function.hpp : Define a rhs object (PoissonRHSFunction) for the Poisson equation (mainly used for vlasovpoissonsolver.hpp): PoissonRHSFunction. - * vlasovpoissonsolver.hpp : Define a class which solves the Poisson equation and computes the electric field: VlasovPoissonSolver. - diff --git a/src/geometryRTheta/poisson/vlasovpoissonsolver.hpp b/src/geometryRTheta/poisson/vlasovpoissonsolver.hpp deleted file mode 100644 index 7211db40e..000000000 --- a/src/geometryRTheta/poisson/vlasovpoissonsolver.hpp +++ /dev/null @@ -1,296 +0,0 @@ -#pragma once - - -#include - -#include -#include - -#include -#include -#include -#include - -#include "ipoissonsolver.hpp" -#include "poisson_rhs_function.hpp" -#include "polarpoissonsolver.hpp" - - - -/** - * @brief Solve the Poisson equation and return the electric - * field for the coupled Vlasov equation. - * - * The Vlasov-Poisson equations are given by - * - * - (1) @f$ \partial_t \rho - E_y \partial_x \rho + E_x \partial_y\rho = 0 @f$, - * - * - (2) @f$ - \Delta \phi = \rho @f$, - * - * - (3) and @f$ E = -\nabla \phi @f$. - * - * The functions are defined on a logical domain, and the mapping from the logical - * domain to the physical domain is written @f$\mathcal{F}@f$. - * - * We here focus on equations (2) and (3). We first compute @f$ \phi @f$ - * on B-splines with the given Poisson solver. Then in the VlasovPoissonSolver::operator() - * we compute the electric field thanks to (3) using the B-splines coefficients. - * Depending on the given mapping, the computation at the center point is not - * always well-defined so we linearize around the center point as explained - * in Edoardo Zoni's article (https://doi.org/10.1016/j.jcp.2019.108889). - * - * With (3), we compute the electric field - * - * - @f$ E(r, \theta) = -\nabla_{x,y} \phi(r, \theta) @f$, - * - * with @f$\nabla_{x,y} \phi(r, \theta) @f$ from - * @f$ \nabla_{x,y} \phi(r, \theta) - * = (J_{\mathcal{F}}^{-1})^T \nabla_{r, \theta} \phi(r, \theta) @f$, - * - * and - * @f$ \nabla \phi = \sum_{ij} \partial_{x_i} \phi g^{i,j} e_j @f$ - * with @f$ g^{i,j} @f$ the coefficients of the inverse metric tensor - * and @f$ e_j @f$ the unnormalized local covariant base: - * - * - * - @f$ \nabla \phi(r,\theta) \cdot e_r = \partial_r \phi(r,\theta) g^{1,1} + \partial_\theta \phi(r,\theta)g^{2,1} @f$, - * - * - @f$ \nabla \phi(r,\theta) \cdot e_\theta = \partial_r \phi(r,\theta) g^{1,2} + \partial_\theta \phi(r,\theta)g^{2,2} @f$. - * - * - * - * For @f$ r < \varepsilon @f$, - * @f$ E(r, \theta) = \left( 1 - \frac{r}{\varepsilon} \right) E(0, \theta) - * + \frac{r}{\varepsilon} E(\varepsilon, \theta) @f$, - * - * with @f$ E(0, \theta) @f$ computed thanks to - * - * - @f$ \partial_r \phi (0, \theta_1) = \left[\partial_r x \partial_x \phi - * + \partial_r y \partial_y \phi \right](0, \theta_1) @f$, - * - * - @f$ \partial_r \phi (0, \theta_2) = \left[\partial_r x \partial_x \phi - * + \partial_r y \partial_y \phi \right] (0, \theta_2) @f$, - * - * where @f$ \theta_1 @f$ and @f$ \theta_2 @f$ correspond to - * linearly independent directions. - * - * - * The equation (1) is computed thanks to advection operator. - * - * - * @tparam Mapping A Curvilinear2DToCartesian class. - * - * - * @see PolarSplineFEMPoissonSolver - * - */ -template -class VlasovPoissonSolver : public IPoissonSolver -{ -public: - /** - * @brief Define a 2x2 matrix with an 2D array of an 2D array. - */ - using Matrix_2x2 = std::array, 2>; - -private: - Mapping const& m_mapping; - - SplineRPBuilder const& m_rhs_builder; - SplineRPEvaluator const& m_rhs_evaluator; - - PolarSplineEvaluator const m_polar_spline_evaluator; - - PolarSplineFEMPoissonSolver const& m_poisson_solver; - - double const m_epsilon; - - - static constexpr int n_overlap_cells = PolarBSplinesRP::continuity + 1; - -public: - /** - * @brief Instantiate a VlasovPoissonSolver. - * - * @param[in] mapping - * The mapping @f$ \mathcal{F} @f$ from the logical domain to the physical domain. - * @param[in] builder - * The splines builder for the rhs. - * @param[in] evaluator - * The splines evaluator for the rhs. - * @param[in] poisson_solver - * The Poisson solver used to solve (2). - * @param[in] epsilon - * The parameter @f$ \varepsilon @f$ for the linearization of the - * electric field. - */ - VlasovPoissonSolver( - Mapping const& mapping, - SplineRPBuilder const& builder, - SplineRPEvaluator const& evaluator, - PolarSplineFEMPoissonSolver const& poisson_solver, - double const epsilon = 1e-12) - : m_mapping(mapping) - , m_rhs_builder(builder) - , m_rhs_evaluator(evaluator) - , m_polar_spline_evaluator(g_polar_null_boundary_2d) - , m_poisson_solver(poisson_solver) - , m_epsilon(epsilon) {}; - - ~VlasovPoissonSolver() {}; - - - /** - * @brief Compute the electric field from the Poisson equation. - * - * @param[out] electrostatic_potential - * The solution @f$\phi@f$ of the Poisson equation (2). - * @param[out] electric_field - * The electric field @f$E = -\nabla \phi@f$. - * @param[in] allfdistribu - * The rhs @f$\rho@f$ of the Poisson equation (2). - */ - void operator()( - DSpanRP electrostatic_potential, - VectorFieldSpan> electric_field, - DViewRP allfdistribu) const - { - auto const grid = ddc::get_domain(allfdistribu); - - FieldRP coords(grid); - ddc::for_each(grid, [&](IndexRP const irp) { coords(irp) = ddc::coordinate(irp); }); - - auto const dom_bsplinesRP = m_rhs_builder.spline_domain(); - - Spline2D allfdistribu_coef(dom_bsplinesRP); - - BSDomainR radial_bsplines(ddc::discrete_space().full_domain().remove_first( - ddc::DiscreteVector {n_overlap_cells})); - BSDomainP polar_domain(ddc::discrete_space().full_domain()); - SplinePolar electrostatic_potential_coef( - PolarBSplinesRP::singular_domain(), - BSDomainRP(radial_bsplines, polar_domain)); - - - m_rhs_builder(allfdistribu_coef, allfdistribu); - PoissonRHSFunction const charge_density_coord(allfdistribu_coef, m_rhs_evaluator); - - m_poisson_solver(charge_density_coord, electrostatic_potential_coef); - - - m_polar_spline_evaluator( - electrostatic_potential.span_view(), - coords.span_cview(), - electrostatic_potential_coef); - - - // Computation of the advection_field: - DFieldRP deriv_r_phi(grid); - DFieldRP deriv_p_phi(grid); - - - m_polar_spline_evaluator.deriv_dim_1( - deriv_r_phi.span_view(), - coords.span_cview(), - electrostatic_potential_coef); - m_polar_spline_evaluator.deriv_dim_2( - deriv_p_phi.span_view(), - coords.span_cview(), - electrostatic_potential_coef); - - ddc::for_each(grid, [&](IndexRP const irp) { - double const r = ddc::coordinate(ddc::select(irp)); - double const th = ddc::coordinate(ddc::select(irp)); - - if (r > m_epsilon) { - CoordRP const coord_rp(r, th); - - Matrix_2x2 J; // Jacobian matrix - m_mapping.jacobian_matrix(coord_rp, J); - Matrix_2x2 inv_G; // Inverse metric tensor - m_mapping.inverse_metric_tensor(coord_rp, inv_G); - - // E = -grad phi - double const electric_field_r - = -deriv_r_phi(irp) * inv_G[0][0] - deriv_p_phi(irp) * inv_G[1][0]; - double const electric_field_th - = -deriv_r_phi(irp) * inv_G[0][1] - deriv_p_phi(irp) * inv_G[1][1]; - - // compute the electric field in the physical domain (Cartesian domain) - ddcHelper::get(electric_field)(irp) - = electric_field_r * J[0][0] + electric_field_th * J[0][1]; - ddcHelper::get(electric_field)(irp) - = electric_field_r * J[1][0] + electric_field_th * J[1][1]; - - } else { - // Linearisation of the electric field - double const th1 = M_PI / 4.; - double const th2 = -M_PI / 4. + 2 * M_PI; - - // --- Value at r = 0: - CoordRP const coord_1_0(0, th1); - CoordRP const coord_2_0(0, th2); - - double const dr_x_1 = m_mapping.jacobian_11(coord_1_0); // dr_x (0, th1) - double const dr_y_1 = m_mapping.jacobian_21(coord_1_0); // dr_y (0, th1) - - double const dr_x_2 = m_mapping.jacobian_11(coord_2_0); // dr_x (0, th2) - double const dr_y_2 = m_mapping.jacobian_21(coord_2_0); // dr_y (0, th2) - - double deriv_r_phi_1 - = m_polar_spline_evaluator - .deriv_dim_1(coord_1_0, electrostatic_potential_coef); - double deriv_r_phi_2 - = m_polar_spline_evaluator - .deriv_dim_1(coord_2_0, electrostatic_potential_coef); - - double const deriv_y_phi_0 = 1 / (dr_y_2 - dr_y_1 * dr_x_2 / dr_x_1) - * (deriv_r_phi_2 - deriv_r_phi_1 * dr_x_2 / dr_x_1); - - double const deriv_x_phi_0 = 1 / dr_x_1 * (deriv_r_phi_1 - deriv_y_phi_0 * dr_y_1); - - // E = -grad phi - double const electric_field_x_0 = -deriv_x_phi_0; - double const electric_field_y_0 = -deriv_y_phi_0; - - - - // --- Value at r = m_epsilon: - CoordRP const coord_rp_epsilon(m_epsilon, th); - Matrix_2x2 J_eps; // Jacobian matrix - m_mapping.jacobian_matrix(coord_rp_epsilon, J_eps); - Matrix_2x2 inv_G_eps; // Inverse metric tensor - m_mapping.inverse_metric_tensor(coord_rp_epsilon, inv_G_eps); - - double deriv_r_phi_epsilon - = m_polar_spline_evaluator - .deriv_dim_1(coord_rp_epsilon, electrostatic_potential_coef); - double deriv_p_phi_epsilon - = m_polar_spline_evaluator - .deriv_dim_2(coord_rp_epsilon, electrostatic_potential_coef); - - - // E = -grad phi - double const electric_field_r = -deriv_r_phi_epsilon * inv_G_eps[0][0] - - deriv_p_phi_epsilon * inv_G_eps[1][0]; - double const electric_field_th = -deriv_r_phi_epsilon * inv_G_eps[0][1] - - deriv_p_phi_epsilon * inv_G_eps[1][1]; - - // compute the electric field in the physical domain (cartesian domain) - double const electric_field_x_epsilon - = electric_field_r * J_eps[0][0] + electric_field_th * J_eps[0][1]; - double const electric_field_y_epsilon - = electric_field_r * J_eps[1][0] + electric_field_th * J_eps[1][1]; - - - // --- Linearisation: - ddcHelper::get(electric_field)(irp) - = electric_field_x_0 * (1 - r / m_epsilon) - + electric_field_x_epsilon * r / m_epsilon; - ddcHelper::get(electric_field)(irp) - = electric_field_y_0 * (1 - r / m_epsilon) - + electric_field_y_epsilon * r / m_epsilon; - } - }); - } -}; diff --git a/src/geometryRTheta/time_solver/CMakeLists.txt b/src/geometryRTheta/time_solver/CMakeLists.txt index 705402785..9c4f3f54e 100644 --- a/src/geometryRTheta/time_solver/CMakeLists.txt +++ b/src/geometryRTheta/time_solver/CMakeLists.txt @@ -17,6 +17,7 @@ target_link_libraries(time_integration_rp gslx::advection_rp gslx::utils gslx::timestepper + gslx::advection_field_RTheta ) add_library(gslx::time_integration_rp ALIAS time_integration_rp) diff --git a/src/geometryRTheta/time_solver/bsl_predcorr.hpp b/src/geometryRTheta/time_solver/bsl_predcorr.hpp index 6a81b269f..e0ee18277 100644 --- a/src/geometryRTheta/time_solver/bsl_predcorr.hpp +++ b/src/geometryRTheta/time_solver/bsl_predcorr.hpp @@ -15,6 +15,7 @@ #include "sll/spline_evaluator_2d.hpp" #include "advection_domain.hpp" +#include "advection_field_rp.hpp" #include "bsl_advection_rp.hpp" #include "geometry.hpp" #include "ifoot_finder.hpp" @@ -23,8 +24,6 @@ #include "polarpoissonsolver.hpp" #include "rk2.hpp" #include "spline_interpolator_2d_rp.hpp" -#include "vlasovpoissonsolver.hpp" - /** * @brief Predictor-corrector for the Vlasov-Poisson equations. @@ -42,11 +41,13 @@ * for @f$ n \geq 0 @f$, * * First, it advects on a half time step: - * - 1. & 2. From @f$\rho^n@f$, it computes @f$\phi^n@f$ and @f$E^n@f$ with a VlasovPoissonSolver; + * - 1. From @f$\rho^n@f$, it computes @f$\phi^n@f$ with a PolarSplineFEMPoissonSolver; + * - 2. From @f$\phi^n@f$, it computes @f$A^n@f$ with a AdvectionFieldFinder; * - 3. From @f$\rho^n@f$ and @f$A^n@f$, it computes @f$\rho^{n+1/2}@f$ with a BslAdvectionRP on @f$\frac{dt}{2}@f$; * * Secondly, it advects on a full time step: - * - 4. & 5. From @f$\rho^{n+1/2}@f$, it computes @f$\phi^{n+1/2}@f$ and @f$E^{n+1/2}@f$ with a VlasovPoissonSolver; + * - 4. From @f$\rho^{n+1/2}@f$, it computes @f$\phi^{n+1/2}@f$ with a PolarSplineFEMPoissonSolver; + * - 5. From @f$\phi^{n+1/2}@f$, it computes @f$A^{n+1/2}@f$ with a AdvectionFieldFinder; * - 6. From @f$\rho^n@f$ and @f$A^{n+1/2}@f$, it computes @f$\rho^{n+1}@f$ with a BslAdvectionRP on @f$dt@f$. * * @tparam Mapping @@ -69,9 +70,12 @@ class BslPredCorrRP : public ITimeSolverRP Mapping const& m_mapping; - BslAdvectionRP const& m_advection_solver; + BslAdvectionRP const& m_advection_solver; + + PolarSplineFEMPoissonSolver const& m_poisson_solver; - VlasovPoissonSolver const m_vlasov_poisson_solver; + SplineRPBuilder const& m_builder; + SplineRPEvaluator const& m_spline_evaluator; public: @@ -98,14 +102,16 @@ class BslPredCorrRP : public ITimeSolverRP BslPredCorrRP( AdvectionDomain const& advection_domain, Mapping const& mapping, - BslAdvectionRP const& advection_solver, + BslAdvectionRP const& advection_solver, SplineRPBuilder const& builder, SplineRPEvaluator const& rhs_evaluator, PolarSplineFEMPoissonSolver const& poisson_solver) : m_advection_domain(advection_domain) , m_mapping(mapping) , m_advection_solver(advection_solver) - , m_vlasov_poisson_solver(mapping, builder, rhs_evaluator, poisson_solver) + , m_poisson_solver(poisson_solver) + , m_builder(builder) + , m_spline_evaluator(rhs_evaluator) { } @@ -122,10 +128,28 @@ class BslPredCorrRP : public ITimeSolverRP // Grid. ------------------------------------------------------------------------------------------ IDomainRP grid(allfdistribu.domain()); + FieldRP coords(grid); + ddc::for_each(grid, [&](IndexRP const irp) { coords(irp) = ddc::coordinate(irp); }); + AdvectionFieldFinder advection_field_computer(m_mapping); + + BSDomainR radial_bsplines(ddc::discrete_space().full_domain().remove_first( + ddc::DiscreteVector {PolarBSplinesRP::continuity + 1})); + BSDomainP polar_domain(ddc::discrete_space().full_domain()); + + SplinePolar electrostatic_potential_coef( + PolarBSplinesRP::singular_domain(), + BSDomainRP(radial_bsplines, polar_domain)); + PolarSplineEvaluator polar_spline_evaluator( + g_polar_null_boundary_2d); + DFieldRP electrical_potential0(grid); - VectorDFieldRP electric_field0(grid); - m_vlasov_poisson_solver(electrical_potential0, electric_field0, allfdistribu); + + Spline2D allfdistribu_coef(m_builder.spline_domain()); + m_builder(allfdistribu_coef, allfdistribu); + PoissonRHSFunction const charge_density_coord(allfdistribu_coef, m_spline_evaluator); + m_poisson_solver(charge_density_coord, coords, electrical_potential0); + ddc::PdiEvent("iteration") .with("iter", 0) .and_with("time", 0) @@ -135,28 +159,22 @@ class BslPredCorrRP : public ITimeSolverRP std::function, DViewRP)> define_advection_field = [&](VectorDSpanRP advection_field, DViewRP allfdistribu) { - // --- compute electric field: - DFieldRP electrical_potential(grid); - VectorDFieldRP electric_field(grid); - m_vlasov_poisson_solver(electrical_potential, electric_field, allfdistribu); - - // --- compute advection field from electric field: - ddc::for_each(advection_field.domain(), [&](IndexRP const idx) { - ddcHelper::get(advection_field)(idx) - = -ddcHelper::get(electric_field)(idx); - }); - ddc::parallel_deepcopy( - ddcHelper::get(advection_field), - ddcHelper::get(electric_field)); + // --- compute electrostatic potential: + Spline2D allfdistribu_coef(m_builder.spline_domain()); + m_builder(allfdistribu_coef, allfdistribu); + PoissonRHSFunction const + charge_density_coord(allfdistribu_coef, m_spline_evaluator); + m_poisson_solver(charge_density_coord, electrostatic_potential_coef); + + // --- compute advection field: + advection_field_computer(electrostatic_potential_coef, advection_field); }; - std::function, double)> advect_allfdistribu = [&](DSpanRP allfdistribu, VectorDViewRP advection_field, double dt) { m_advection_solver(allfdistribu, advection_field, dt); }; - RK2> time_stepper(grid); start_time = std::chrono::system_clock::now(); @@ -169,8 +187,10 @@ class BslPredCorrRP : public ITimeSolverRP advect_allfdistribu); DFieldRP electrical_potential(grid); - VectorDFieldRP electric_field(grid); - m_vlasov_poisson_solver(electrical_potential, electric_field, allfdistribu); + Spline2D allfdistribu_coef(m_builder.spline_domain()); + m_builder(allfdistribu_coef, allfdistribu); + PoissonRHSFunction const charge_density_coord(allfdistribu_coef, m_spline_evaluator); + m_poisson_solver(charge_density_coord, coords, electrical_potential); ddc::PdiEvent("iteration") .with("iter", iter + 1) diff --git a/src/geometryRTheta/time_solver/bsl_predcorr_second_order_explicit.hpp b/src/geometryRTheta/time_solver/bsl_predcorr_second_order_explicit.hpp index c1582255d..67463b972 100644 --- a/src/geometryRTheta/time_solver/bsl_predcorr_second_order_explicit.hpp +++ b/src/geometryRTheta/time_solver/bsl_predcorr_second_order_explicit.hpp @@ -14,6 +14,7 @@ #include "sll/spline_evaluator_2d.hpp" #include "advection_domain.hpp" +#include "advection_field_rp.hpp" #include "bsl_advection_rp.hpp" #include "euler.hpp" #include "geometry.hpp" @@ -22,7 +23,7 @@ #include "polarpoissonsolver.hpp" #include "spline_foot_finder.hpp" #include "spline_interpolator_2d_rp.hpp" -#include "vlasovpoissonsolver.hpp" + /** @@ -42,13 +43,15 @@ * for @f$ n \geq 0 @f$, * * First, it predicts: - * - 1. & 2. From @f$\rho^n@f$, it computes @f$\phi^n@f$ and @f$E^n@f$ with a VlasovPoissonSolver; + * - 1. From @f$\rho^n@f$, it computes @f$\phi^n@f$ with a PolarSplineFEMPoissonSolver; + * - 2. From @f$\phi^n@f$, it computes @f$A^n@f$ with a AdvectionFieldFinder; * - 3. From @f$\rho^n@f$ and @f$A^n@f$, it computes @f$\rho^P@f$ with a BslAdvectionRP on @f$ dt @f$; * * We write @f$X^P@f$ the characteristic feet such that @f$\partial_t X^P = A^n(X^n)@f$. * * Secondly, it corrects: - * - 4. & 5. From @f$\rho^P@f$, it computes @f$\phi^P@f$ and @f$E^P@f$ with a VlasovPoissonSolver; + * - 4. From @f$\rho^P@f$, it computes @f$\phi^P@f$ with a PolarSplineFEMPoissonSolver; + * - 5. From @f$\phi^P@f$, it computes @f$A^P@f$ with a AdvectionFieldFinder; * - 6. From @f$\rho^n@f$ and @f$\frac{A^{P}(X^n) + A^n(X^P)}{2} @f$, it computes @f$\rho^{n+1}@f$ with a BslAdvectionRP on @f$ dt @f$. * * (With @f$X^C@f$ the characteristic feet such that @f$\partial_t X^C = \frac{A^{P}(X^n) + A^n(X^P)}{2} @f$.) @@ -76,12 +79,13 @@ class BslExplicitPredCorrRP : public ITimeSolverRP Mapping const& m_mapping; - BslAdvectionRP> const& m_advection_solver; + BslAdvectionRP, Mapping> const& + m_advection_solver; EulerMethod const m_euler; SplineFootFinder const m_find_feet; - VlasovPoissonSolver const m_vlasov_poisson_solver; + PolarSplineFEMPoissonSolver const& m_poisson_solver; Builder const& m_builder; Evaluator const& m_evaluator; @@ -116,7 +120,8 @@ class BslExplicitPredCorrRP : public ITimeSolverRP BslExplicitPredCorrRP( AdvectionDomain const& advection_domain, Mapping const& mapping, - BslAdvectionRP>& advection_solver, + BslAdvectionRP, Mapping>& + advection_solver, IDomainRP const& grid, SplineRPBuilder const& builder, SplineRPEvaluator const& rhs_evaluator, @@ -126,8 +131,8 @@ class BslExplicitPredCorrRP : public ITimeSolverRP , m_mapping(mapping) , m_advection_solver(advection_solver) , m_euler(grid) - , m_find_feet(m_euler, advection_domain, grid, builder, advection_evaluator) - , m_vlasov_poisson_solver(mapping, builder, rhs_evaluator, poisson_solver) + , m_find_feet(m_euler, advection_domain, builder, advection_evaluator) + , m_poisson_solver(poisson_solver) , m_builder(builder) , m_evaluator(advection_evaluator) @@ -148,9 +153,22 @@ class BslExplicitPredCorrRP : public ITimeSolverRP // Grid. ------------------------------------------------------------------------------------------ IDomainRP const grid(allfdistribu.domain()); + FieldRP coords(grid); + ddc::for_each(grid, [&](IndexRP const irp) { coords(irp) = ddc::coordinate(irp); }); + + BSDomainR radial_bsplines(ddc::discrete_space().full_domain().remove_first( + ddc::DiscreteVector {PolarBSplinesRP::continuity + 1})); + BSDomainP polar_domain(ddc::discrete_space().full_domain()); + // --- Electrostatic potential (phi). ------------------------------------------------------------- DFieldRP electrical_potential(grid); + SplinePolar electrostatic_potential_coef( + PolarBSplinesRP::singular_domain(), + BSDomainRP(radial_bsplines, polar_domain)); + + PolarSplineEvaluator polar_spline_evaluator( + g_polar_null_boundary_2d); // --- For the computation of advection field from the electrostatic potential (phi): ------------- VectorDFieldRP electric_field(grid); @@ -158,22 +176,24 @@ class BslExplicitPredCorrRP : public ITimeSolverRP VectorDFieldRP advection_field(grid); VectorDFieldRP advection_field_predicted(grid); + AdvectionFieldFinder advection_field_computer(m_mapping); + - // --- Parameter for linearisation of advection field: -------------------------------------------- - // STEP 1&2: From rho^n, we compute E^n: Poisson equation + derivatives - m_vlasov_poisson_solver( - electrical_potential.span_view(), - electric_field.span_view(), - allfdistribu); + // --- Parameter for linearisation of advection field: -------------------------------------------- start_time = std::chrono::system_clock::now(); for (int iter(0); iter < steps; ++iter) { double const time = iter * dt; - // STEP 1&2: From rho^n, we compute E^n: Poisson equation + derivatives - m_vlasov_poisson_solver( + // STEP 1: From rho^n, we compute phi^n: Poisson equation + Spline2D allfdistribu_coef(m_builder.spline_domain()); + m_builder(allfdistribu_coef, allfdistribu); + PoissonRHSFunction const charge_density_coord_1(allfdistribu_coef, m_evaluator); + m_poisson_solver(charge_density_coord_1, electrostatic_potential_coef); + + polar_spline_evaluator( electrical_potential.span_view(), - electric_field.span_view(), - allfdistribu); + coords.span_cview(), + electrostatic_potential_coef); ddc::PdiEvent("iteration") .with("iter", iter) @@ -181,14 +201,8 @@ class BslExplicitPredCorrRP : public ITimeSolverRP .and_with("density", allfdistribu) .and_with("electrical_potential", electrical_potential); - // --- compute advection field A^n from electric field E^n: - ddc::for_each(advection_field.domain(), [&](IndexRP const idx) { - ddcHelper::get(advection_field)(idx) - = -ddcHelper::get(electric_field)(idx); - }); - ddc::parallel_deepcopy( - ddcHelper::get(advection_field), - ddcHelper::get(electric_field)); + // STEP 2: From phi^n, we compute A^n: + advection_field_computer(electrostatic_potential_coef, advection_field); // STEP 3: From rho^n and A^n, we compute rho^P: Vlasov equation @@ -205,21 +219,13 @@ class BslExplicitPredCorrRP : public ITimeSolverRP m_find_feet(feet_coords.span_view(), advection_field.span_view(), dt); - // STEP 4&5: From rho^P, we compute E^P: Poisson equation + derivatives - m_vlasov_poisson_solver( - electrical_potential.span_view(), - electric_field_predicted.span_view(), - allfdistribu_predicted); - - // --- compute advection field $A^P$ from electric field $E^P$: - ddc::for_each(advection_field_predicted.domain(), [&](IndexRP const idx) { - ddcHelper::get(advection_field_predicted)(idx) - = -ddcHelper::get(electric_field_predicted)(idx); - }); - ddc::parallel_deepcopy( - ddcHelper::get(advection_field_predicted), - ddcHelper::get(electric_field_predicted)); + // STEP 4: From rho^P, we compute phi^P: Poisson equation + m_builder(allfdistribu_coef, allfdistribu); + PoissonRHSFunction const charge_density_coord_4(allfdistribu_coef, m_evaluator); + m_poisson_solver(charge_density_coord_4, electrostatic_potential_coef); + // STEP 5: From phi^P, we compute A^P: + advection_field_computer(electrostatic_potential_coef, advection_field_predicted); // --- we evaluate the advection field A^n at the characteristic feet X^P @@ -259,11 +265,11 @@ class BslExplicitPredCorrRP : public ITimeSolverRP m_advection_solver(allfdistribu, advection_field.span_view(), dt); } - // STEP 1&2: From rho^n, we compute E^n: Poisson equation + derivatives - m_vlasov_poisson_solver( - electrical_potential.span_view(), - electric_field.span_view(), - allfdistribu); + // STEP 1: From rho^n, we compute phi^n: Poisson equation + Spline2D allfdistribu_coef(m_builder.spline_domain()); + m_builder(allfdistribu_coef, allfdistribu); + PoissonRHSFunction const charge_density_coord(allfdistribu_coef, m_evaluator); + m_poisson_solver(charge_density_coord, coords, electrical_potential); ddc::PdiEvent("last_iteration") .with("iter", steps) diff --git a/src/geometryRTheta/time_solver/bsl_predcorr_second_order_implicit.hpp b/src/geometryRTheta/time_solver/bsl_predcorr_second_order_implicit.hpp index eee5554f0..9f7ba59d6 100644 --- a/src/geometryRTheta/time_solver/bsl_predcorr_second_order_implicit.hpp +++ b/src/geometryRTheta/time_solver/bsl_predcorr_second_order_implicit.hpp @@ -14,6 +14,7 @@ #include "sll/spline_evaluator_2d.hpp" #include "advection_domain.hpp" +#include "advection_field_rp.hpp" #include "bsl_advection_rp.hpp" #include "geometry.hpp" #include "itimesolver.hpp" @@ -21,7 +22,7 @@ #include "polarpoissonsolver.hpp" #include "spline_foot_finder.hpp" #include "spline_interpolator_2d_rp.hpp" -#include "vlasovpoissonsolver.hpp" + /** @@ -41,13 +42,15 @@ * for @f$ n \geq 0 @f$, * * First, it predicts: - * - 1. & 2. From @f$\rho^n@f$, it computes @f$\phi^n@f$ and @f$E^n@f$ with a VlasovPoissonSolver; + * - 1. From @f$\rho^n@f$, it computes @f$\phi^n@f$ with a PolarSplineFEMPoissonSolver; + * - 2. From @f$\phi^n@f$, it computes @f$A^n@f$ with a AdvectionFieldFinder; * - 3. From @f$\rho^n@f$ and @f$A^n@f$, it computes implicitly @f$\rho^P@f$ with a BslAdvectionRP on @f$ \frac{dt}{4} @f$: * - the characteristic feet @f$X^P@f$ is such that @f$X^P = X^k@f$ with @f$X^k@f$ the result of the implicit method: * - @f$ X^k = X^n - \frac{dt}{4} \partial_t X^k@f$. * * Secondly, it corrects: - * - 4. & 5. From @f$\rho^P@f$, it computes @f$\phi^P@f$ and @f$E^P@f$ with a VlasovPoissonSolver; + * - 4. From @f$\rho^P@f$, it computes @f$\phi^P@f$ with a PolarSplineFEMPoissonSolver; + * - 5. From @f$\phi^P@f$, it computes @f$A^P@f$ with a AdvectionFieldFinder; * - 6. From @f$\rho^n@f$ and @f$ A^{P} @f$, it computes @f$\rho^{n+1}@f$ with a BslAdvectionRP on @f$ \frac{dt}{2} @f$. * - the characteristic feet @f$X^C@f$ is such that @f$X^C = X^k@f$ with @f$X^k@f$ the result of the implicit method: * - @f$\partial_t X^k = A^P(X^n) + A^P(X^{k-1}) @f$, @@ -76,12 +79,13 @@ class BslImplicitPredCorrRP : public ITimeSolverRP Mapping const& m_mapping; - BslAdvectionRP> const& m_advection_solver; + BslAdvectionRP, Mapping> const& + m_advection_solver; EulerMethod const m_euler; SplineFootFinder const m_foot_finder; - VlasovPoissonSolver const m_vlasov_poisson_solver; + PolarSplineFEMPoissonSolver const& m_poisson_solver; Builder const& m_builder; Evaluator const& m_evaluator; @@ -116,7 +120,8 @@ class BslImplicitPredCorrRP : public ITimeSolverRP BslImplicitPredCorrRP( AdvectionDomain const& advection_domain, Mapping const& mapping, - BslAdvectionRP> const& advection_solver, + BslAdvectionRP, Mapping> const& + advection_solver, IDomainRP const& grid, SplineRPBuilder const& builder, SplineRPEvaluator const& rhs_evaluator, @@ -126,8 +131,8 @@ class BslImplicitPredCorrRP : public ITimeSolverRP , m_mapping(mapping) , m_advection_solver(advection_solver) , m_euler(grid) - , m_foot_finder(m_euler, advection_domain, grid, builder, advection_evaluator) - , m_vlasov_poisson_solver(mapping, builder, rhs_evaluator, poisson_solver) + , m_foot_finder(m_euler, advection_domain, builder, advection_evaluator) + , m_poisson_solver(poisson_solver) , m_builder(builder) , m_evaluator(advection_evaluator) @@ -148,20 +153,43 @@ class BslImplicitPredCorrRP : public ITimeSolverRP // Grid. ------------------------------------------------------------------------------------------ IDomainRP const grid(allfdistribu.domain()); + FieldRP coords(grid); + ddc::for_each(grid, [&](IndexRP const irp) { coords(irp) = ddc::coordinate(irp); }); + + BSDomainR radial_bsplines(ddc::discrete_space().full_domain().remove_first( + ddc::DiscreteVector {PolarBSplinesRP::continuity + 1})); + BSDomainP polar_domain(ddc::discrete_space().full_domain()); // --- Electrostatic potential (phi). ------------------------------------------------------------- DFieldRP electrical_potential(grid); + SplinePolar electrostatic_potential_coef( + PolarBSplinesRP::singular_domain(), + BSDomainRP(radial_bsplines, polar_domain)); + + PolarSplineEvaluator polar_spline_evaluator( + g_polar_null_boundary_2d); // --- For the computation of advection field from the electrostatic potential (phi): ------------- VectorDFieldRP electric_field(grid); VectorDFieldRP advection_field(grid); + AdvectionFieldFinder advection_field_computer(m_mapping); + + start_time = std::chrono::system_clock::now(); for (int iter(0); iter < steps; ++iter) { - // STEP 1&2: From rho^n, we compute E^n: Poisson equation + derivatives - m_vlasov_poisson_solver(electrical_potential, electric_field, allfdistribu); + // STEP 1: From rho^n, we compute phi^n: Poisson equation + Spline2D allfdistribu_coef(m_builder.spline_domain()); + m_builder(allfdistribu_coef, allfdistribu); + PoissonRHSFunction const charge_density_coord_1(allfdistribu_coef, m_evaluator); + m_poisson_solver(charge_density_coord_1, electrostatic_potential_coef); + + polar_spline_evaluator( + electrical_potential.span_view(), + coords.span_cview(), + electrostatic_potential_coef); ddc::PdiEvent("iteration") .with("iter", iter) @@ -169,14 +197,9 @@ class BslImplicitPredCorrRP : public ITimeSolverRP .and_with("density", allfdistribu) .and_with("electrical_potential", electrical_potential); - // --- compute advection field $A^n$ from electric field $E^n$: - ddc::for_each(advection_field.domain(), [&](IndexRP const idx) { - ddcHelper::get(advection_field)(idx) - = -ddcHelper::get(electric_field)(idx); - }); - ddc::parallel_deepcopy( - ddcHelper::get(advection_field), - ddcHelper::get(electric_field)); + + // STEP 2: From phi^n, we compute A^n: + advection_field_computer(electrostatic_potential_coef, advection_field); // STEP 3: From rho^n and A^n, we compute rho^P: Vlasov equation @@ -242,17 +265,13 @@ class BslImplicitPredCorrRP : public ITimeSolverRP m_foot_finder(feet_coords.span_view(), advection_field_k_tot.span_cview(), dt / 2.); - // STEP 4&5: From rho^P, we compute E^P: Poisson equation + derivatives - m_vlasov_poisson_solver(electrical_potential, electric_field, allfdistribu_predicted); + // STEP 4: From rho^P, we compute phi^P: Poisson equation + m_builder(allfdistribu_coef, allfdistribu); + PoissonRHSFunction const charge_density_coord_4(allfdistribu_coef, m_evaluator); + m_poisson_solver(charge_density_coord_4, electrostatic_potential_coef); - // --- compute advection field A^P from electric field E^P: - ddc::for_each(advection_field.domain(), [&](IndexRP const idx) { - ddcHelper::get(advection_field)(idx) - = -ddcHelper::get(electric_field)(idx); - }); - ddc::parallel_deepcopy( - ddcHelper::get(advection_field), - ddcHelper::get(electric_field)); + // STEP 5: From phi^P, we compute A^P: + advection_field_computer(electrostatic_potential_coef, advection_field); // STEP 6: From rho^n and A^P, we compute rho^{n+1}: Vlasov equation @@ -296,8 +315,11 @@ class BslImplicitPredCorrRP : public ITimeSolverRP m_advection_solver(allfdistribu, advection_field_k_tot.span_cview(), dt); } - // STEP 1&2: From rho^n, we compute E^n: Poisson equation + derivatives - m_vlasov_poisson_solver(electrical_potential, electric_field, allfdistribu); + // STEP 1: From rho^n, we compute phi^n: Poisson equation + Spline2D allfdistribu_coef(m_builder.spline_domain()); + m_builder(allfdistribu_coef, allfdistribu); + PoissonRHSFunction const charge_density_coord(allfdistribu_coef, m_evaluator); + m_poisson_solver(charge_density_coord, coords, electrical_potential); ddc::PdiEvent("last_iteration") .with("iter", steps) @@ -389,6 +411,5 @@ class BslImplicitPredCorrRP : public ITimeSolverRP }); } while ((square_difference_feet > tau * tau) and (count < max_count)); - //std::cout << ">>> diff = " << square_difference_feet << std::endl; } }; diff --git a/tests/geometryRTheta/CMakeLists.txt b/tests/geometryRTheta/CMakeLists.txt index 273ff4a2d..69dcadcab 100644 --- a/tests/geometryRTheta/CMakeLists.txt +++ b/tests/geometryRTheta/CMakeLists.txt @@ -9,3 +9,4 @@ endif() add_subdirectory(2d_spline_interpolator) add_subdirectory(quadrature) add_subdirectory(advection_2d_rp) +add_subdirectory(advection_field_rp) diff --git a/tests/geometryRTheta/advection_2d_rp/advection_simulation_utils.hpp b/tests/geometryRTheta/advection_2d_rp/advection_simulation_utils.hpp index 6312bb5ba..07532e77e 100644 --- a/tests/geometryRTheta/advection_2d_rp/advection_simulation_utils.hpp +++ b/tests/geometryRTheta/advection_2d_rp/advection_simulation_utils.hpp @@ -353,14 +353,10 @@ void simulate( bool if_save_feet, std::string const& output_folder) { - SplineFootFinder const foot_finder( - time_stepper, - advection_domain, - grid, - advection_builder, - advection_evaluator); + SplineFootFinder const + foot_finder(time_stepper, advection_domain, advection_builder, advection_evaluator); - BslAdvectionRP advection_operator(function_interpolator, foot_finder); + BslAdvectionRP advection_operator(function_interpolator, foot_finder, mapping); auto function_to_be_advected_test = simulation.get_test_function(); auto advection_field_test = simulation.get_advection_field(); diff --git a/tests/geometryRTheta/advection_field_rp/CMakeLists.txt b/tests/geometryRTheta/advection_field_rp/CMakeLists.txt new file mode 100644 index 000000000..d8bdf339b --- /dev/null +++ b/tests/geometryRTheta/advection_field_rp/CMakeLists.txt @@ -0,0 +1,52 @@ + + + +######################################################################################################### +# All possiibilities for SIMULATION: # +# "TRANSLATION" "ROTATION" "DECENTERED_ROTATION" # +# # +######################################################################################################### + +function (advection_field_test_executable SIMULATION) + set(test_name_gtest "advection_field_gtest_${SIMULATION}") + + add_executable("${test_name_gtest}" + advection_field_gtest.cpp + test_cases_adv_field.hpp + ../../main.cpp + ) + target_compile_features("${test_name_gtest}" PUBLIC cxx_std_17) + target_link_libraries("${test_name_gtest}" + PUBLIC + DDC::DDC + DDC::PDI_Wrapper + paraconf::paraconf + PDI::pdi + + Eigen3::Eigen + + sll::splines + gslx::paraconfpp + + GTest::gtest + GTest::gmock + + gslx::poisson_RTheta + gslx::geometry_RTheta + gslx::interpolation_2D_rp + gslx::advection_rp + gslx::time_integration_rp + gslx::quadrature + gslx::utils + gslx::diocotron_init_eq + gslx::simulation_utils + ) + target_compile_definitions("${test_name_gtest}" PUBLIC -D${SIMULATION}) + gtest_discover_tests("${test_name_gtest}" ) +endfunction() + +# Select the parameters of the simulation: +advection_field_test_executable(TRANSLATION) +advection_field_test_executable(ROTATION) +advection_field_test_executable(DECENTRED_ROTATION) + diff --git a/tests/geometryRTheta/advection_field_rp/advection_field_gtest.cpp b/tests/geometryRTheta/advection_field_rp/advection_field_gtest.cpp new file mode 100644 index 000000000..c798cb277 --- /dev/null +++ b/tests/geometryRTheta/advection_field_rp/advection_field_gtest.cpp @@ -0,0 +1,315 @@ +/** + * Test the computation of the advection field passing by polar axis. + * Also test the advection with an advection field along the polar axis given as input. +*/ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "advection_domain.hpp" +#include "bsl_advection_rp.hpp" +#include "bsl_predcorr.hpp" +#include "bsl_predcorr_second_order_explicit.hpp" +#include "bsl_predcorr_second_order_implicit.hpp" +#include "compute_norms.hpp" +#include "crank_nicolson.hpp" +#include "euler.hpp" +#include "geometry.hpp" +#include "quadrature.hpp" +#include "rk3.hpp" +#include "rk4.hpp" +#include "simulation_utils_tools.hpp" +#include "spline_foot_finder.hpp" +#include "spline_interpolator_2d_rp.hpp" +#include "spline_quadrature.hpp" +#include "test_cases_adv_field.hpp" +#include "trapezoid_quadrature.hpp" + + +namespace { +using PoissonSolver = PolarSplineFEMPoissonSolver; +using DiscreteMapping = DiscreteToCartesian; +using Mapping = CircularToCartesian; + +using Evaluator = SplineEvaluator2D; +using Builder = SplineBuilder2D; +using Interpolator = SplineInterpolatorRP; + +namespace fs = std::filesystem; + +} // end namespace + + + +TEST(AdvectionFieldRPComputation, TestAdvectionFieldFinder) +{ + // SETUP ========================================================================================== + std::chrono::time_point start_simulation; + std::chrono::time_point end_simulation; + + start_simulation = std::chrono::system_clock::now(); + // Build the grid for the space. ------------------------------------------------------------------ + int const Nr(32); + int const Nt(64); + double const dt(0.05); + double const final_T(1.6); + + double const rmin(0); + double const rmax(1); + + CoordR const r_min(rmin); + CoordR const r_max(rmax); + IVectR const r_size(Nr); + + CoordP const p_min(0.0); + CoordP const p_max(2.0 * M_PI); + IVectP const p_size(Nt); + + std::vector r_knots(r_size + 1); + std::vector p_knots(p_size + 1); + + double const dr((r_max - r_min) / r_size); + double const dp((p_max - p_min) / p_size); + + r_knots[0] = r_min; + for (int i(1); i < r_size; ++i) { + r_knots[i] = r_min + i * dr; + } + r_knots[r_size] = r_max; + + p_knots[p_size] = p_min; + for (int i(1); i < p_size; ++i) { + p_knots[i] = CoordP(p_min + i * dp); + } + p_knots[p_size] = p_max; + + + // Creating mesh & supports: + ddc::init_discrete_space(r_knots); + ddc::init_discrete_space(p_knots); + + ddc::init_discrete_space(SplineInterpPointsR::get_sampling()); + ddc::init_discrete_space(SplineInterpPointsP::get_sampling()); + + IDomainR const interpolation_domain_R(SplineInterpPointsR::get_domain()); + IDomainP const interpolation_domain_P(SplineInterpPointsP::get_domain()); + IDomainRP const grid(interpolation_domain_R, interpolation_domain_P); + + // Split the domain of the advection field along RP + const int npoints_p = IDomainP(grid).size(); + IDomainRP const grid_without_Opoint(grid.remove_first(IVectRP(1, 0))); + IDomainRP const Opoint_grid(grid.take_first(IVectRP(1, npoints_p))); + + + // Build domains for polar B-splines. ------------------------------------------------------------- + BSDomainR radial_bsplines(ddc::discrete_space().full_domain().remove_first( + ddc::DiscreteVector {PolarBSplinesRP::continuity + 1})); + BSDomainP polar_domain(ddc::discrete_space().full_domain()); + + + // OPERATORS ====================================================================================== + SplineRBuilder const r_builder(interpolation_domain_R); + SplinePBuilder const p_builder(interpolation_domain_P); + SplineRPBuilder const builder(grid); + + ConstantExtrapolationBoundaryValue2D boundary_condition_r_left( + r_min); + ConstantExtrapolationBoundaryValue2D boundary_condition_r_right( + r_max); + + SplineRPEvaluator spline_evaluator_extrapol( + boundary_condition_r_left, + boundary_condition_r_right, + g_null_boundary_2d, + g_null_boundary_2d); + + + PolarSplineEvaluator polar_spline_evaluator( + g_polar_null_boundary_2d); + + // --- Define the mapping. ------------------------------------------------------------------------ + const Mapping mapping; + DiscreteMapping const discrete_mapping + = DiscreteMapping::analytical_to_discrete(mapping, builder, spline_evaluator_extrapol); + + ddc::init_discrete_space(discrete_mapping, r_builder, p_builder); + + BSDomainRP const dom_bsplinesRP = builder.spline_domain(); + + + // --- Advection operator ------------------------------------------------------------------------- + Evaluator spline_evaluator( + g_null_boundary_2d, + g_null_boundary_2d, + g_null_boundary_2d, + g_null_boundary_2d); + + PreallocatableSplineInterpolatorRP interpolator(builder, spline_evaluator); + + AdvectionPhysicalDomain advection_domain(mapping); + + RK3, VectorDFieldRP> const time_stepper(grid); + SplineFootFinder find_feet(time_stepper, advection_domain, builder, spline_evaluator_extrapol); + + BslAdvectionRP advection_operator(interpolator, find_feet, mapping); + + // --- Advection field finder --------------------------------------------------------------------- + AdvectionFieldFinder advection_field_computer(mapping); + + + // --- Choice of the simulation ------------------------------------------------------------------- +#if defined(TRANSLATION) + TranslationAdvectionFieldSimulation simulation(mapping, rmin, rmax); +#elif defined(ROTATION) + RotationAdvectionFieldSimulation simulation(mapping, rmin, rmax); +#elif defined(DECENTRED_ROTATION) + DecentredRotationAdvectionFieldSimulation simulation(mapping); +#endif + + // ================================================================================================ + // SIMULATION DATA | + // ================================================================================================ + + // --- Time parameters ---------------------------------------------------------------------------- + int const iter_nb = final_T * int(1 / dt); + + // ================================================================================================ + // INITIALISATION | + // ================================================================================================ + DFieldRP allfdistribu_rp(grid); + DFieldRP allfdistribu_xy(grid); + + VectorDFieldRP advection_field_exact(grid); + VectorDFieldRP advection_field_rp(grid_without_Opoint); + VectorDFieldRP advection_field_xy(grid); + VectorDFieldRP advection_field_xy_from_rp(grid); + CoordXY advection_field_xy_center; + + DFieldRP electrostatic_potential(grid); + + + + // Initialize functions ****************************************** + auto function = simulation.get_test_function(); + auto phi_function = simulation.get_electrostatique_potential(); + auto advection_field = simulation.get_advection_field(); + for_each(grid, [&](IndexRP const irp) { + CoordRP const coord_rp(ddc::coordinate(irp)); + CoordXY const coord_xy(mapping(coord_rp)); + + allfdistribu_rp(irp) = function(coord_rp); + allfdistribu_xy(irp) = allfdistribu_rp(irp); + electrostatic_potential(irp) = phi_function(coord_xy, 0); + + CoordXY const evaluated_advection_field = advection_field(coord_xy, 0); + ddcHelper::get(advection_field_exact)(irp) = CoordX(evaluated_advection_field); + ddcHelper::get(advection_field_exact)(irp) = CoordY(evaluated_advection_field); + }); + + + // Constant advection fields ************************************* + advection_field_computer( + electrostatic_potential, + advection_field_rp, + advection_field_xy_center); + advection_field_computer(electrostatic_potential, advection_field_xy); + + + // Compare advection fields --- + VectorDFieldRP difference_between_fields_exact_and_xy(grid); + // > Compare the advection field computed on XY to the exact advection field + for_each(grid, [&](IndexRP const irp) { + ddcHelper::get(difference_between_fields_exact_and_xy)(irp) + = ddcHelper::get(advection_field_exact)(irp) + - ddcHelper::get(advection_field_xy)(irp); + ddcHelper::get(difference_between_fields_exact_and_xy)(irp) + = ddcHelper::get(advection_field_exact)(irp) + - ddcHelper::get(advection_field_xy)(irp); + }); + + + // > Compare the advection field computed on RP to the advection field computed on XY + VectorDFieldRP difference_between_fields_xy_and_rp(grid); + for_each(grid_without_Opoint, [&](IndexRP const irp) { + CoordRP const coord_rp(ddc::coordinate(irp)); + + std::array, 2> J; // Jacobian matrix + mapping.jacobian_matrix(coord_rp, J); + std::array, 2> G; // Metric tensor + mapping.metric_tensor(coord_rp, G); + + // computation made in BslAdvectionRP operator: + ddcHelper::get(advection_field_xy_from_rp)(irp) + = ddcHelper::get(advection_field_rp)(irp) * J[1][1] / std::sqrt(G[1][1]) + + ddcHelper::get(advection_field_rp)(irp) * -J[1][0] / std::sqrt(G[0][0]); + ddcHelper::get(advection_field_xy_from_rp)(irp) + = ddcHelper::get(advection_field_rp)(irp) * -J[0][1] / std::sqrt(G[1][1]) + + ddcHelper::get(advection_field_rp)(irp) * J[0][0] / std::sqrt(G[0][0]); + + // compare + ddcHelper::get(difference_between_fields_xy_and_rp)(irp) + = ddcHelper::get(advection_field_xy_from_rp)(irp) + - ddcHelper::get(advection_field_xy)(irp); + ddcHelper::get(difference_between_fields_xy_and_rp)(irp) + = ddcHelper::get(advection_field_xy_from_rp)(irp) + - ddcHelper::get(advection_field_xy)(irp); + }); + + for_each(Opoint_grid, [&](IndexRP const irp) { + // computation made in BslAdvectionRP operator: + ddcHelper::get(advection_field_xy_from_rp)(irp) = CoordX(advection_field_xy_center); + ddcHelper::get(advection_field_xy_from_rp)(irp) = CoordY(advection_field_xy_center); + + // compare + ddcHelper::get(difference_between_fields_xy_and_rp)(irp) + = ddcHelper::get(advection_field_xy_from_rp)(irp) + - ddcHelper::get(advection_field_xy)(irp); + ddcHelper::get(difference_between_fields_xy_and_rp)(irp) + = ddcHelper::get(advection_field_xy_from_rp)(irp) + - ddcHelper::get(advection_field_xy)(irp); + }); + + // --- Check the difference on advection fields -------------------------------------------------- + for_each(grid, [&](IndexRP const irp) { + EXPECT_LE(abs(ddcHelper::get(difference_between_fields_exact_and_xy)(irp)), 1e-6); + EXPECT_LE(abs(ddcHelper::get(difference_between_fields_exact_and_xy)(irp)), 1e-6); + + EXPECT_LE(abs(ddcHelper::get(difference_between_fields_xy_and_rp)(irp)), 1e-14); + EXPECT_LE(abs(ddcHelper::get(difference_between_fields_xy_and_rp)(irp)), 1e-14); + }); + + + // ================================================================================================ + // SIMULATION | + // ================================================================================================ + for (int iter(0); iter < iter_nb; ++iter) { + advection_operator(allfdistribu_rp, advection_field_rp, advection_field_xy_center, dt); + advection_operator(allfdistribu_xy, advection_field_xy, dt); + + // Check the advected functions --- + for_each(grid, [&](IndexRP const irp) { + EXPECT_NEAR(allfdistribu_rp(irp), allfdistribu_xy(irp), 1e-13); + }); + } + + end_simulation = std::chrono::system_clock::now(); + display_time_difference("Simulation time: ", start_simulation, end_simulation); +} diff --git a/tests/geometryRTheta/advection_field_rp/test_cases_adv_field.hpp b/tests/geometryRTheta/advection_field_rp/test_cases_adv_field.hpp new file mode 100644 index 000000000..f977bedc3 --- /dev/null +++ b/tests/geometryRTheta/advection_field_rp/test_cases_adv_field.hpp @@ -0,0 +1,478 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../advection_2d_rp/test_cases.hpp" + +#include "geometry.hpp" +#include "spline_interpolator_2d_rp.hpp" + +/* + * This file defines + * - the TEST ELECTROSTATICAL POTENTIALS, + * - and the TEST SIMULATIONS. + */ + + + +// TEST ELECTROSTATICAL POTENTIALS ------------------------------------------------ +/** + * @brief Base class for the advection field for the tests of the 2D polar advection + * operator. + * + * @see BslAdvectionRP + * @see AdvectionSimulation + */ +class ElectrostaticalPotentialSimulation +{ +public: + virtual ~ElectrostaticalPotentialSimulation() = default; + + /** + * @brief Get the advection field in the physical domain. + * + * @param[in] coord + * The coordinate in the physical domain. + * @param[in] t + * Time component. + * + * @return The advection field in the physical domain. + */ + virtual double operator()(CoordXY const coord, double const t = 0.) const = 0; + + /** + * @brief Get the characteristic feet in the physical domain. + * + * @param[in] coord + * The original coordinate in the physical domain. + * @param[in] t + * Time component. + * + * @return The characteristic feet in the physical domain. + */ + virtual CoordXY exact_feet(CoordXY coord, double t) const = 0; +}; + + +/** + * @brief Advection field for the tests of the 2D polar advection operator. + * + * + * The test advection field is given in Edoardo Zoni's article + * (https://doi.org/10.1016/j.jcp.2019.108889): + * + * @f$ A(x,y) = \omega [y_c - y, x - x_c]@f$. + * + * The characteristic feet are then given by + * - @f$ X(t + dt) = x_c + (X(t) - x_c) \cos(\omega dt) - (Y(t) - y_c) \sin(\omega dt) @f$, + * - @f$ Y(t + dt) = y_c + (X(t) - x_c) \sin(\omega dt) + (Y(t) - y_c) \cos(\omega dt) @f$. + * + */ +class ElectrostaticalPotentialSimulation_decentred_rotation + : public ElectrostaticalPotentialSimulation +{ +private: + double const m_omega; + double const m_xc; + double const m_yc; + double const m_x_bar; + double const m_y_bar; + +public: + /** + * @brief Instantiate an ElectrostaticalPotentialSimulation_decentred_rotation advection field. + * + */ + ElectrostaticalPotentialSimulation_decentred_rotation() + : m_omega(2 * M_PI) + , m_xc(0.25) + , m_yc(0.) + , m_x_bar(0.) + , m_y_bar(0.) {}; + ~ElectrostaticalPotentialSimulation_decentred_rotation() {}; + + double operator()(CoordXY const coord, double const t) const final + { + double const x = ddc::get(coord); + double const y = ddc::get(coord); + return m_omega * (-0.5 * x * x + m_xc * x - 0.5 * y * y + m_yc * y); + }; + + CoordXY exact_feet(CoordXY coord, double const t) const final + { + double const x = ddc::get(coord); + double const y = ddc::get(coord); + double const foot_x + = m_xc + (x - m_xc) * std::cos(m_omega * -t) - (y - m_yc) * std::sin(m_omega * -t); + double const foot_y + = m_yc + (x - m_xc) * std::sin(m_omega * -t) + (y - m_yc) * std::cos(m_omega * -t); + return CoordXY(foot_x, foot_y); + }; +}; + + + +/** + * @brief Advection field for the tests of the 2D polar advection operator. + * + * + * The test advection field for a translation in the physical domain is given by : + * + * @f$ A(x,y) = [v_x, v_y]@f$. + * + * with @f$ v_x @f$ and @f$ v_y @f$ constants. + * + * The characteristic feet are then given by + * - @f$ X(t + dt) = X(t) + dt v_x @f$, + * - @f$ Y(t + dt) = Y(t) + dt v_y @f$. + * + */ +class ElectrostaticalPotentialSimulation_translation : public ElectrostaticalPotentialSimulation +{ +private: + CoordXY const m_velocity; + +public: + /** + * @brief Instantiate an ElectrostaticalPotentialSimulation_translation advection field. + * + * @param[in] vx + * The constant first component of the advection field in the physical domain. + * @param[in] vy + * The constant second component of the advection field in the physical domain. + */ + ElectrostaticalPotentialSimulation_translation(CoordVx vx, CoordVy vy) + : m_velocity(ddc::get(vx), ddc::get(vy)) {}; + ~ElectrostaticalPotentialSimulation_translation() {}; + + double operator()(CoordXY const coord, double const t) const final + { + double const vx = ddc::get(m_velocity); + double const vy = ddc::get(m_velocity); + return -vy * ddc::get(coord) + vx * ddc::get(coord); + }; + + CoordXY exact_feet(CoordXY coord, double const t) const final + { + return coord - t * m_velocity; + }; +}; + + + +/** + * @brief Advection field for the tests of the 2D polar advection operator. + * + * + * The test advection field for a rotation in the physical domain is given by : + * + * @f$ A(x,y) = J_{\mathcal{F}}[v_r, v_\theta]@f$. + * + * with @f$ v_r @f$ and @f$ v_\theta @f$ constants and @f$ \mathcal{F}@f$ the + * circular mapping. + * + * The characteristic feet are then given by + * - @f$ (X(t + dt), Y(t + dt)) = \mathcal{F} (R(t + dt), \Theta(t + dt))@f$, + * - with + * - @f$ R(t + dt) = R(t) + dt v_r @f$, + * - @f$ \Theta(t + dt) = \Theta(t) + dt v_\theta @f$, + * - and @f$ (R(t), \Theta(t)) = \mathcal{F}^{-1} (X(t), Y(t))@f$. + * + */ +class ElectrostaticalPotentialSimulation_rotation : public ElectrostaticalPotentialSimulation +{ +private: + double const m_vr; + double const m_vp; + CircularToCartesian const m_mapping; + +public: + /** + * @brief Instantiate an ElectrostaticalPotentialSimulation_rotation advection field. + * + * @param[in] vp + * The constant second polar component of the advection field in the physical domain. + */ + ElectrostaticalPotentialSimulation_rotation(CoordVp vp) : m_vr(0), m_vp(vp), m_mapping() {}; + ~ElectrostaticalPotentialSimulation_rotation() {}; + + double operator()(CoordXY const coord, double const t) const final + { + CoordRP const coord_rp(m_mapping(coord)); + double const r = ddc::get(coord_rp); + return -0.5 * r * r * m_vp; + }; + + CoordXY exact_feet(CoordXY coord_xy, double const t) const final + { + CoordRP const coord_rp(m_mapping(coord_xy)); + CoordRP const velocity(m_vr, m_vp); + return m_mapping(coord_rp - t * velocity); + }; +}; + + + +// TEST SIMULATIONS --------------------------------------------------------------- +/** + * @brief Base class for the tests simulation of the 2D polar advection operator. + * + * The simulations are: + * - TranslationAdvectionFieldSimulation, + * - RotationAdvectionFieldSimulation, + * - DecentredRotationAdvectionFieldSimulation. + * + * @see BslAdvectionRP + * @see FunctionToBeAdvected + * @see ElectrostaticalPotentialSimulation + * @see AdvectionField + */ +template < + class ElectrostaticalPotentialSimulation, + class FunctionToBeAdvected, + class AdvectionField> +class AdvectionFieldSimulation +{ +protected: + /** + * @brief The chosen electrostatical potential for the simulation. + */ + ElectrostaticalPotentialSimulation const m_electrostatical_potential; + /** + * @brief The chosen function to be advected for the simulation. + */ + FunctionToBeAdvected const m_function; + /** + * @brief The chosen advection field for the simulation. + */ + AdvectionField const m_advection_field; + + +public: + /** + * @brief Instantiate a AdvectionSimulation simulation. + * + * @param[in] electrostatical_potential + * An ElectrostaticalPotentialSimulation type object. + * @param[in] function + * A FunctionToBeAdvected type object. + * @param[in] advection_field + * A AdvectionField type object. + */ + AdvectionFieldSimulation( + ElectrostaticalPotentialSimulation const electrostatical_potential, + FunctionToBeAdvected const function, + AdvectionField const advection_field) + : m_electrostatical_potential(electrostatical_potential) + , m_function(function) + , m_advection_field(advection_field) {}; + virtual ~AdvectionFieldSimulation() = default; + + + /** + * @brief Get the electrostatical potential of the simulation. + * + * @return A constant reference to the electrostatical potential created in + * the ElectrostaticalPotentialSimulation child class. + */ + ElectrostaticalPotentialSimulation const& get_electrostatique_potential() const + { + return m_electrostatical_potential; + }; + + /** + * @brief Get the test function of the simulation. + * + * @return A constant reference to the test function created in the AdvectionSimulation child class. + */ + FunctionToBeAdvected const& get_test_function() const + { + return m_function; + }; + + + /** + * @brief Get the advection field of the simulation. + * + * @return A constant reference to the advection field created in the AdvectionSimulation child class. + */ + AdvectionField const& get_advection_field() const + { + return m_advection_field; + }; +}; + + +/** + * @brief Simulation of a translated Gaussian. + * + * Define a simulation with a Gaussian defined by FunctionToBeAdvected_gaussian + * and a translation advection field defined by ElectrostaticalPotentialSimulation_translation: + * + * - @f$ f(x, y) = C \exp\left( - \frac{(x - x_0)^2}{2\sigma_x^2} - \frac{(y - y_0)^2}{2\sigma_y^2} \right)@f$ + * with + * - @f$ C = 1 @f$, + * - @f$ x_0 = -0.2 @f$ , + * - @f$ y_0 = -0.2 @f$ , + * - @f$ \sigma_x = 0.1 @f$ , + * - @f$ \sigma_y = 0.1 @f$ ; + * + * and + * - @f$ A(x,y) = [v_x, v_y]@f$, + * + * with @f$ v_x @f$ and @f$ v_y @f$ constants. + * + * @see FunctionToBeAdvected_gaussian + * @see ElectrostaticalPotentialSimulation_translation + * @see AdvectionField_translation + */ +template +class TranslationAdvectionFieldSimulation + : public AdvectionFieldSimulation< + ElectrostaticalPotentialSimulation_translation, + FunctionToBeAdvected_gaussian, + AdvectionField_translation> +{ +public: + /** + * @brief Instantiate a TranslationSimulation simulation. + * + * @param[in] mapping + * The mapping from the logical domain to the physical domain. + * @param[in] rmin + * The minimum value of @f$ r@f$ on the logical domain. + * @param[in] rmax + * The maximum value of @f$ r@f$ on the logical domain. + */ + TranslationAdvectionFieldSimulation( + Mapping const& mapping, + double const rmin, + double const rmax) + : AdvectionFieldSimulation< + ElectrostaticalPotentialSimulation_translation, + FunctionToBeAdvected_gaussian, + AdvectionField_translation>( + ElectrostaticalPotentialSimulation_translation( + CoordVx(-std::cos(2 * M_PI * 511. / 4096.) / 2.), + CoordVy(-std::sin(2 * M_PI * 511. / 4096.) / 2.)), + FunctionToBeAdvected_gaussian< + Mapping>(mapping, 1., -0.2, -0.2, 0.1, 0.1, rmin, rmax), + AdvectionField_translation( + CoordVx(-std::cos(2 * M_PI * 511. / 4096.) / 2.), + CoordVy(-std::sin(2 * M_PI * 511. / 4096.) / 2.))) {}; +}; + + +/** + * @brief Simulation of a rotated Gaussian. + * + * Define a simulation with a Gaussian defined by FunctionToBeAdvected_gaussian + * and a rotation advection field defined by ElectrostaticalPotentialSimulation_rotation: + * + * + * - @f$ f(x, y) = C \exp\left( - \frac{(x - x_0)^2}{2\sigma_x^2} - \frac{(y - y_0)^2}{2\sigma_y^2} \right)@f$ + * with + * - @f$ C = 1 @f$, + * - @f$ x_0 = -0.2 @f$ , + * - @f$ y_0 = -0.2 @f$ , + * - @f$ \sigma_x = 0.1 @f$ , + * - @f$ \sigma_y = 0.1 @f$ ; + * + * and + * - @f$ A(x,y) = J_{\mathcal{F}}[v_r, v_\theta]@f$, + * + * with @f$ v_r @f$ and @f$ v_\theta @f$ constants and @f$ \mathcal{F}@f$ the + * circular mapping. + * + * @see FunctionToBeAdvected_gaussian + * @see ElectrostaticalPotentialSimulation_rotation + * @see AdvectionField_rotation + */ +template +class RotationAdvectionFieldSimulation + : public AdvectionFieldSimulation< + ElectrostaticalPotentialSimulation_rotation, + FunctionToBeAdvected_gaussian, + AdvectionField_rotation> +{ +public: + /** + * @brief Instantiate a RotationSimulation simulation. + * + * @param[in] mapping + * The mapping from the logical domain to the physical domain. + * @param[in] rmin + * The minimum value of @f$ r@f$ on the logical domain. + * @param[in] rmax + * The maximum value of @f$ r@f$ on the logical domain. + */ + RotationAdvectionFieldSimulation(Mapping const& mapping, double const rmin, double const rmax) + : AdvectionFieldSimulation< + ElectrostaticalPotentialSimulation_rotation, + FunctionToBeAdvected_gaussian, + AdvectionField_rotation>( + ElectrostaticalPotentialSimulation_rotation(CoordVp(2 * M_PI)), + FunctionToBeAdvected_gaussian< + Mapping>(mapping, 1., -0.2, -0.2, 0.1, 0.1, rmin, rmax), + AdvectionField_rotation(CoordVr(0), CoordVp(2 * M_PI))) {}; +}; + + +/** + * @brief Simulation of a decentred rotated elipse-type function. + * + * The simulation is given in Edoardo Zoni's article + * (https://doi.org/10.1016/j.jcp.2019.108889). + * + * Define a simulation with a elipse-type function defined by FunctionToBeAdvected_cos_4_elipse + * and a decentred rotation advection field defined by ElectrostaticalPotentialSimulation_decentred_rotation: + * + * - @f$ f(x, y) = \frac{1}{2}\left( G(r_1(x,y)) + G(r_2(x,y))\right)@f$ + * + * with + * - @f$G(r) = \cos\left(\frac{\pi r}{2 a}\right)^4 * 1_{r +class DecentredRotationAdvectionFieldSimulation + : public AdvectionFieldSimulation< + ElectrostaticalPotentialSimulation_decentred_rotation, + FunctionToBeAdvected_cos_4_elipse, + AdvectionField_decentred_rotation> +{ +public: + /** + * @brief Instantiate a DecentredRotationSimulation simulation. + * + * @param[in] mapping + * The mapping from the logical domain to the physical domain. + */ + DecentredRotationAdvectionFieldSimulation(Mapping const& mapping) + : AdvectionFieldSimulation< + ElectrostaticalPotentialSimulation_decentred_rotation, + FunctionToBeAdvected_cos_4_elipse, + AdvectionField_decentred_rotation>( + ElectrostaticalPotentialSimulation_decentred_rotation(), + FunctionToBeAdvected_cos_4_elipse(mapping), + AdvectionField_decentred_rotation()) {}; +}; diff --git a/tests/geometryRTheta/polar_poisson/CMakeLists.txt b/tests/geometryRTheta/polar_poisson/CMakeLists.txt index 3233343dd..aaf17fce9 100644 --- a/tests/geometryRTheta/polar_poisson/CMakeLists.txt +++ b/tests/geometryRTheta/polar_poisson/CMakeLists.txt @@ -29,37 +29,4 @@ foreach(MAPPING_TYPE "CIRCULAR_MAPPING" "CZARNY_MAPPING") set_property(TEST TestPoissonConvergence_${MAPPING_TYPE}_${SOLUTION} PROPERTY TIMEOUT 200) set_property(TEST TestPoissonConvergence_${MAPPING_TYPE}_${SOLUTION} PROPERTY COST 100) endforeach() -endforeach() - - - - -foreach(MAPPING_TYPE "CIRCULAR_MAPPING" "CZARNY_MAPPING") - set(test_name "vlasov_polar_poisson_convergence_${MAPPING_TYPE}") - add_executable("${test_name}" - test_cases.cpp - vlasovpoissonsolver.cpp - ) - target_compile_features("${test_name}" PUBLIC cxx_std_17) - target_link_libraries("${test_name}" - PUBLIC - DDC::DDC - DDC::PDI_Wrapper - paraconf::paraconf - PDI::pdi - sll::splines - gslx::poisson_RTheta - gslx::paraconfpp - Eigen3::Eigen - gslx::geometry_RTheta - ) - target_compile_definitions("${test_name}" PUBLIC -D${MAPPING_TYPE}) - - find_package(Python3 REQUIRED COMPONENTS Interpreter) - - add_test(NAME TestPoissonConvergence_${MAPPING_TYPE}_${SOLUTION} - COMMAND "$" "${CMAKE_CURRENT_SOURCE_DIR}/test_vlasov_poisson.py" - "$") - set_property(TEST TestPoissonConvergence_${MAPPING_TYPE}_${SOLUTION} PROPERTY TIMEOUT 200) - set_property(TEST TestPoissonConvergence_${MAPPING_TYPE}_${SOLUTION} PROPERTY COST 100) -endforeach() +endforeach() \ No newline at end of file diff --git a/tests/geometryRTheta/polar_poisson/test_vlasov_poisson.py b/tests/geometryRTheta/polar_poisson/test_vlasov_poisson.py deleted file mode 100644 index 93b7ec7da..000000000 --- a/tests/geometryRTheta/polar_poisson/test_vlasov_poisson.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/python3 -""" -A file used for testing polarpoissonfemsolver.cpp. -It provides 2 input files of different sizes and uses the results -to calculate the order of convergence. -Cubic splines should produce 4th order convergence. -As few points are used the convergence order will not -yet have converged so we simply verify that order > 3.5. - -Inputs: the executable associated with the file polarpoissonfemsolver.cpp. -""" -import subprocess -import sys - -import numpy as np - -executable = sys.argv[1] - -with open("poisson.yaml", "w", encoding="utf-8") as f: - print("Mesh:", file=f) - print(" r_size: 32", file=f) - print(" p_size: 64", file=f) - -with subprocess.Popen([executable, "poisson.yaml"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) as p: - out, err = p.communicate() - assert p.returncode == 0 - -print(out) -if err: - print(err) - -out_lines = out.split('\n') -error_64 = [float(l.split(' ')[4]) for l in out_lines if "Max error function : " in l][0] -error_dx_64 = [float(l.split(' ')[4]) for l in out_lines if "Max error Ex : " in l][0] -error_dy_64 = [float(l.split(' ')[4]) for l in out_lines if "Max error Ey : " in l][0] - - -with open("poisson.yaml", "w", encoding="utf-8") as f: - print("Mesh:", file=f) - print(" r_size: 64", file=f) - print(" p_size: 128", file=f) - -with subprocess.Popen([executable, "poisson.yaml"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) as p: - out, err = p.communicate() - assert p.returncode == 0 - -print(out) -if err: - print(err) - -out_lines = out.split('\n') -error_128 = [float(l.split(' ')[4]) for l in out_lines if "Max error function : " in l][0] -error_dx_128 = [float(l.split(' ')[4]) for l in out_lines if "Max error Ex : " in l][0] -error_dy_128 = [float(l.split(' ')[4]) for l in out_lines if "Max error Ey : " in l][0] - - -order = np.log(error_64/error_128) / np.log(2) -order_dx = np.log(error_dx_64/error_dx_128) / np.log(2) -order_dy = np.log(error_dy_64/error_dy_128) / np.log(2) - -print("Measured order of the electric potential : ", order) -print("Measured order of the electric field in x : ", order_dx) -print("Measured order of the electric field in y : ", order_dy) - - -assert 3.5 < order -assert 2.5 < order_dx -assert 2.5 < order_dy diff --git a/vendor/sll/include/sll/mapping/czarny_to_cartesian.hpp b/vendor/sll/include/sll/mapping/czarny_to_cartesian.hpp index 75e99171c..55b1ba6f1 100644 --- a/vendor/sll/include/sll/mapping/czarny_to_cartesian.hpp +++ b/vendor/sll/include/sll/mapping/czarny_to_cartesian.hpp @@ -36,7 +36,7 @@ * * and * @f$ \det(J(r, \theta)) = \frac{- r}{ \sqrt{1 + \epsilon(\epsilon + 2 r \cos(\theta))}} - * \frac{1}{2 - \sqrt{1 + \epsilon(\epsilon + 2 r \cos(\theta))}}. @f$ + * \frac{e\xi}{2 - \sqrt{1 + \epsilon(\epsilon + 2 r \cos(\theta))}}. @f$ * * * @see AnalyticalInvertibleCurvilinear2DToCartesian