Branch data Line data Source code
1 : : /**
2 : : * @file NonStandardFDTDSolver.hpp
3 : : * @brief Impelmentation of the NonStandardFDTDSolver class functions.
4 : : */
5 : :
6 : : namespace ippl {
7 : :
8 : : /**
9 : : * @brief Constructor for the NonStandardFDTDSolver class.
10 : : *
11 : : * This constructor initializes the NonStandardFDTDSolver with the given source field and
12 : : * electromagnetic fields. It checks the dispersion-free CFL condition and initializes the
13 : : * solver.
14 : : *
15 : : * @param source The source field.
16 : : * @param E The electric field.
17 : : * @param B The magnetic field.
18 : : */
19 : : template <typename EMField, typename SourceField, fdtd_bc boundary_conditions>
20 : 0 : NonStandardFDTDSolver<EMField, SourceField, boundary_conditions>::NonStandardFDTDSolver(
21 : : SourceField& source, EMField& E, EMField& B)
22 : 0 : : FDTDSolverBase<EMField, SourceField, boundary_conditions>(source, E, B) {
23 [ # # ]: 0 : auto hx = source.get_mesh().getMeshSpacing();
24 [ # # ]: 0 : if ((hx[2] / hx[0]) * (hx[2] / hx[0]) + (hx[2] / hx[1]) * (hx[2] / hx[1]) >= 1) {
25 [ # # # # : 0 : throw IpplException("NonStandardFDTDSolver Constructor",
# # ]
26 : : "Dispersion-free CFL condition not satisfiable\n");
27 : : }
28 [ # # ]: 0 : initialize();
29 : 0 : }
30 : :
31 : : /**
32 : : * @brief Advances the simulation by one time step.
33 : : *
34 : : * This function updates the fields by one time step using non-standard FDTD method.
35 : : * It calculates the new field values based on the current and previous field values,
36 : : * as well as the source field. The boundary conditions are applied after the update.
37 : : */
38 : : template <typename EMField, typename SourceField, fdtd_bc boundary_conditions>
39 : 0 : void NonStandardFDTDSolver<EMField, SourceField, boundary_conditions>::step() {
40 [ # # ]: 0 : const auto& ldom = this->layout_mp->getLocalNDIndex();
41 : 0 : const int nghost = this->A_n.getNghost();
42 [ # # ]: 0 : const auto aview = this->A_n.getView();
43 [ # # ]: 0 : const auto anp1view = this->A_np1.getView();
44 [ # # ]: 0 : const auto anm1view = this->A_nm1.getView();
45 [ # # ]: 0 : const auto source_view = Maxwell<EMField, SourceField>::JN_mp->getView();
46 : :
47 : 0 : const scalar calA =
48 : : 0.25
49 : 0 : * (1
50 : 0 : + 0.02
51 : 0 : / (static_cast<scalar>(Kokkos::pow(this->hr_m[2] / this->hr_m[0], 2))
52 : 0 : + static_cast<scalar>(Kokkos::pow(this->hr_m[2] / this->hr_m[1], 2))));
53 : 0 : nondispersive<scalar> ndisp{
54 : : .a1 =
55 : : 2
56 : 0 : * (1
57 : 0 : - (1 - 2 * calA) * static_cast<scalar>(Kokkos::pow(this->dt / this->hr_m[0], 2))
58 : 0 : - (1 - 2 * calA) * static_cast<scalar>(Kokkos::pow(this->dt / this->hr_m[1], 2))
59 : 0 : - static_cast<scalar>(Kokkos::pow(this->dt / this->hr_m[2], 2))),
60 : 0 : .a2 = static_cast<scalar>(Kokkos::pow(this->dt / this->hr_m[0], 2)),
61 : 0 : .a4 = static_cast<scalar>(Kokkos::pow(this->dt / this->hr_m[1], 2)),
62 : 0 : .a6 = static_cast<scalar>(Kokkos::pow(this->dt / this->hr_m[2], 2))
63 : 0 : - 2 * calA * static_cast<scalar>(Kokkos::pow(this->dt / this->hr_m[0], 2))
64 : 0 : - 2 * calA * static_cast<scalar>(Kokkos::pow(this->dt / this->hr_m[1], 2)),
65 : 0 : .a8 = static_cast<scalar>(Kokkos::pow(this->dt, 2))};
66 [ # # ]: 0 : Vector<uint32_t, Dim> true_nr = this->nr_m;
67 : 0 : true_nr += (nghost * 2);
68 : 0 : constexpr uint32_t one_if_absorbing_otherwise_0 =
69 : : boundary_conditions == absorbing
70 : : ? 1
71 : : : 0; // 1 if absorbing, 0 otherwise, indicates the start index of the field
72 [ # # ]: 0 : Kokkos::parallel_for(
73 [ # # # # : 0 : ippl::getRangePolicy(aview, nghost), KOKKOS_LAMBDA(size_t i, size_t j, size_t k) {
# # # # #
# # # # #
# # # # ]
74 : : // global indices
75 [ # # ]: 0 : uint32_t ig = i + ldom.first()[0];
76 [ # # ]: 0 : uint32_t jg = j + ldom.first()[1];
77 [ # # ]: 0 : uint32_t kg = k + ldom.first()[2];
78 : : // check if at a boundary of the field
79 : 0 : uint32_t val =
80 : 0 : uint32_t(ig == one_if_absorbing_otherwise_0)
81 [ # # ]: 0 : + (uint32_t(jg == one_if_absorbing_otherwise_0) << 1)
82 [ # # ]: 0 : + (uint32_t(kg == one_if_absorbing_otherwise_0) << 2)
83 [ # # ]: 0 : + (uint32_t(ig == true_nr[0] - one_if_absorbing_otherwise_0 - 1) << 3)
84 [ # # ]: 0 : + (uint32_t(jg == true_nr[1] - one_if_absorbing_otherwise_0 - 1) << 4)
85 [ # # ]: 0 : + (uint32_t(kg == true_nr[2] - one_if_absorbing_otherwise_0 - 1) << 5);
86 : : // update the interior field values
87 [ # # ]: 0 : if (!val) {
88 : 0 : anp1view(i, j, k) =
89 [ # # # # : 0 : -anm1view(i, j, k) + ndisp.a1 * aview(i, j, k)
# # ]
90 [ # # ]: 0 : + ndisp.a2
91 [ # # # # : 0 : * (calA * aview(i + 1, j, k - 1) + (1 - 2 * calA) * aview(i + 1, j, k)
# # # # ]
92 [ # # # # ]: 0 : + calA * aview(i + 1, j, k + 1))
93 [ # # ]: 0 : + ndisp.a2
94 [ # # # # : 0 : * (calA * aview(i - 1, j, k - 1) + (1 - 2 * calA) * aview(i - 1, j, k)
# # # # ]
95 [ # # # # ]: 0 : + calA * aview(i - 1, j, k + 1))
96 [ # # ]: 0 : + ndisp.a4
97 [ # # # # : 0 : * (calA * aview(i, j + 1, k - 1) + (1 - 2 * calA) * aview(i, j + 1, k)
# # # # ]
98 [ # # # # ]: 0 : + calA * aview(i, j + 1, k + 1))
99 [ # # ]: 0 : + ndisp.a4
100 [ # # # # : 0 : * (calA * aview(i, j - 1, k - 1) + (1 - 2 * calA) * aview(i, j - 1, k)
# # # # ]
101 [ # # # # ]: 0 : + calA * aview(i, j - 1, k + 1))
102 [ # # # # : 0 : + ndisp.a6 * aview(i, j, k + 1) + ndisp.a6 * aview(i, j, k - 1)
# # # # ]
103 [ # # # # ]: 0 : + ndisp.a8 * source_view(i, j, k);
104 : : }
105 : : });
106 [ # # # # ]: 0 : Kokkos::fence();
107 [ # # ]: 0 : this->A_np1.fillHalo();
108 [ # # ]: 0 : this->applyBCs();
109 : 0 : }
110 : :
111 : : /**
112 : : * @brief Initializes the solver.
113 : : *
114 : : * This function initializes the solver by setting up the layout, mesh, and fields.
115 : : * It retrieves the mesh spacing, domain, and mesh size, and initializes the fields to zero.
116 : : */
117 : : template <typename EMField, typename SourceField, fdtd_bc boundary_conditions>
118 : 0 : void NonStandardFDTDSolver<EMField, SourceField, boundary_conditions>::initialize() {
119 : : // get layout and mesh
120 : 0 : this->layout_mp = &(this->JN_mp->getLayout());
121 : 0 : this->mesh_mp = &(this->JN_mp->get_mesh());
122 : :
123 : : // get mesh spacing, domain, and mesh size
124 : 0 : this->hr_m = this->mesh_mp->getMeshSpacing();
125 : 0 : this->dt = this->hr_m[2];
126 : 0 : this->domain_m = this->layout_mp->getDomain();
127 [ # # ]: 0 : for (unsigned int i = 0; i < Dim; ++i)
128 : 0 : this->nr_m[i] = this->domain_m[i].length();
129 : :
130 : : // initialize fields
131 : 0 : this->A_nm1.initialize(*this->mesh_mp, *this->layout_mp);
132 : 0 : this->A_n.initialize(*this->mesh_mp, *this->layout_mp);
133 : 0 : this->A_np1.initialize(*this->mesh_mp, *this->layout_mp);
134 : :
135 [ # # ]: 0 : this->A_nm1 = 0.0;
136 [ # # ]: 0 : this->A_n = 0.0;
137 [ # # ]: 0 : this->A_np1 = 0.0;
138 : :
139 : : // set the mesh domain
140 : : if constexpr (boundary_conditions == periodic) {
141 : 0 : this->setPeriodicBoundaryConditions();
142 : : }
143 : 0 : };
144 : : } // namespace ippl
|