OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
FieldSolver.cpp
Go to the documentation of this file.
2
3#include <fstream>
4#include <iomanip>
5#include <sstream>
6
7#include <filesystem>
8
10#include "Physics/Physics.h"
11#include "Utilities/Util.h"
12
13extern Inform* gmsg;
14
15template <>
16template <typename Solver>
17void FieldSolver<double, 3>::initSolverWithParams(const ippl::ParameterList& sp) {
18 Inform m("FieldSolver::initSolverWithParams");
19 this->getSolver().template emplace<Solver>();
20 Solver& solver = std::get<Solver>(this->getSolver());
21 solver.mergeParameters(sp);
22 m << level3 << "Initialized solver with params: " << this->getStype() << endl;
23
24 // test if rho_m exists (just in case)
25 if (!rho_m) {
26 throw OpalException("FieldSolver::initSolverWithParams", "rho_m is not initialized.");
27 }
28
29 solver.setRhs(*rho_m);
30 m << level5 << "Set solver RHS." << endl;
31
32 if constexpr ((std::is_same_v<Solver, CGSolver_t<T, Dim>>) /*||
33 (std::is_same_v<Solver, FEMSolver_t<T, Dim>>) ||
34 (std::is_same_v<Solver, FEMPreconSolver_t<T, Dim>>)*/) {
36 throw OpalException(
37 "FieldSolver::initSolverWithParams",
38 "Cannot use CGSolver yet, not fully implemented.");
39 // The CG solver and FEMPoissonSolver compute the potential
40 // directly and use this to get the electric field
41 solver.setLhs(*phi_m);
42 solver.setGradient(*E_m);
43 m << level5 << "Set gradient for CG." << endl;
44 } else {
45 // The periodic Poisson solver, Open boundaries solver,
46 // and the TG solver compute the electric field directly
47 solver.setLhs(*E_m);
48 }
49
50 m << level4 << "Set solver RHS+LHS." << endl;
51 call_counter_m = 0;
52}
53
54template <>
56 /*
57 what == ef
58 */
59
60 Inform m("FieldSolver::dumpVectorField");
61
62 // std::variant<Field_t<3>*, VField_t<double, 3>* > field;
63
64 if (ippl::Comm->size() > 1 || call_counter_m < 2) {
65 m << level5 << "Skipping vector field dump for multiple ranks or first call." << endl;
66 return;
67 }
68
69 /* Save the files in the output directory of the simulation. The file
70 * name of vector fields is
71 *
72 * 'basename'-'name'_field-'******'.dat
73 *
74 * and of scalar fields
75 *
76 * 'basename'-'name'_scalar-'******'.dat
77 *
78 * with
79 * 'basename': OPAL input file name (*.in)
80 * 'name': field name (input argument of function)
81 * '******': call_counter_m padded with zeros to 6 digits
82 */
83
84 std::string dirname = "data/";
85
86 std::string type;
87 std::string unit;
88
89 if (Util::toUpper(what) == "EF") {
90 type = "vector";
91 unit = "";
92 }
93
94 VField_t<double, 3>* field = this->getE();
95
96 auto localIdx = field->getOwned();
97 auto mesh_mp = &(field->get_mesh());
98 auto spacing = mesh_mp->getMeshSpacing();
99 auto origin = mesh_mp->getOrigin();
100 int nghost = field->getNghost(); // ghosts are excluded in getLocalNDIndex()
101
102 auto fieldV = field->getView();
103 auto field_hostV = field->getHostMirror();
104 Kokkos::deep_copy(field_hostV, fieldV);
105
106 std::filesystem::path file(dirname);
107 std::string basename = OpalData::getInstance()->getInputBasename();
108 std::ostringstream oss;
109 oss << basename << "-" << (what + std::string("_") + type) << "-" << std::setfill('0')
110 << std::setw(6) << call_counter_m << ".dat";
111 std::string filename = oss.str();
112 file /= filename;
113 std::ofstream fout(file.string(), std::ios::out);
114
115 fout << std::setprecision(9);
116
117 fout << "# " << Util::toUpper(what) << " " << type << " data on grid" << std::endl
118 << "# origin= " << std::fixed << origin << " h= " << std::fixed << spacing << std::endl
119 << "#" << std::setw(4) << "i" << std::setw(5) << "j" << std::setw(5) << "k"
120 << std::setw(17) << "x [m]" << std::setw(17) << "y [m]" << std::setw(17) << "z [m]";
121
122 fout << std::setw(10) << what << "x [" << unit << "]" << std::setw(10) << what << "y [" << unit
123 << "]" << std::setw(10) << what << "z [" << unit << "]";
124
125 fout << std::endl;
126
127 for (int i = localIdx[0].first() + nghost; i <= localIdx[0].last() + nghost; i++) {
128 for (int j = localIdx[1].first() + nghost; j <= localIdx[1].last() + nghost; j++) {
129 for (int k = localIdx[2].first() + nghost; k <= localIdx[2].last() + nghost; k++) {
130 // define the physical points (cell-centered)
131 double x = (i - nghost) * spacing[0] + origin[0];
132 double y = (j - nghost) * spacing[1] + origin[1];
133 double z = (k - nghost) * spacing[2] + origin[2];
134
135 fout << std::setw(5) << i << std::setw(5) << j << std::setw(5) << k << std::setw(17)
136 << x << std::setw(17) << y << std::setw(17) << z << std::scientific << "\t"
137 << field_hostV(i, j, k)[0] << "\t" << field_hostV(i, j, k)[1] << "\t"
138 << field_hostV(i, j, k)[2] << std::endl;
139 }
140 }
141 }
142 fout.close();
143 m << level5 << "*** FINISHED DUMPING " + Util::toUpper(what) + " FIELD *** to " << file.string()
144 << endl;
145}
146
147template <>
149 /*
150 what == phi | rho
151 */
152
153 Inform m("FS::dumpScalField() ");
154 m << level5 << "Dumping scalar field: " << what << endl;
155
156 if (ippl::Comm->size() > 1 /*|| call_counter_m<2*/) {
157 m << level5 << "Skipping scalar field dump for multiple ranks or first call." << endl;
158 return;
159 }
160
161 /* Save the files in the output directory of the simulation. The file
162 * name of vector fields is
163 *
164 * 'basename'-'name'_field-'******'.dat
165 *
166 * and of scalar fields
167 *
168 * 'basename'-'name'_scalar-'******'.dat
169 *
170 * with
171 * 'basename': OPAL input file name (*.in)
172 * 'name': field name (input argument of function)
173 * '******': call_counter_m padded with zeros to 6 digits
174 */
175
176 // Needs to be empty...?
177 std::string dirname = "data/";
178
179 std::string type;
180 std::string unit;
181 bool isVectorField = false;
182
183 if (Util::toUpper(what) == "RHO") {
184 type = "scalar";
185 unit = "Cb/m^3";
186 } else if (Util::toUpper(what) == "PHI") {
187 type = "scalar";
188 unit = "V";
189 }
190
191 Field_t<3>* field = (this->getStype() == "CG" && Util::toUpper(what) == "PHI")
192 ? this->getPhi()
193 : this->getRho(); // both rho and phi are in the same variable (in
194 // place computation)
195
196 // auto localIdx = field->getOwned();
197 ippl::NDIndex<3> localIdx = field->getLayout().getLocalNDIndex();
198 int nghost = field->getNghost(); // ghosts are excluded in getLocalNDIndex(), but we still need
199 // to shift indices
200 auto mesh_mp = &(field->get_mesh());
201 auto spacing = mesh_mp->getMeshSpacing();
202 auto origin = mesh_mp->getOrigin();
203
204 Field_t<3>::view_type fieldV = field->getView();
205 Field_t<3>::host_mirror_type field_hostV = field->getHostMirror();
206 Kokkos::deep_copy(field_hostV, fieldV);
207
208 std::filesystem::path file(dirname);
209 std::string basename = OpalData::getInstance()->getInputBasename();
210 std::ostringstream oss;
211 oss << basename << "-" << (what + std::string("_") + type) << "-" << std::setfill('0')
212 << std::setw(6) << call_counter_m << ".dat";
213 std::string filename = oss.str();
214 file /= filename;
215 std::ofstream fout(file.string(), std::ios::out);
216
217 fout << std::setprecision(9);
218
219 fout << "# " << Util::toUpper(what) << " " << type << " data on grid" << std::endl
220 << "# origin= " << std::fixed << origin << " h= " << std::fixed << spacing
221 << " nghosts=" << nghost << std::endl
222 << "#" << std::setw(4) << "i" << std::setw(5) << "j" << std::setw(5) << "k"
223 << std::setw(17) << "x [m]" << std::setw(17) << "y [m]" << std::setw(17) << "z [m]";
224
225 if (isVectorField) {
226 fout << std::setw(10) << what << "x [" << unit << "]" << std::setw(10) << what << "y ["
227 << unit << "]" << std::setw(10) << what << "z [" << unit << "]";
228 } else {
229 fout << std::setw(13) << what << " [" << unit << "]";
230 }
231
232 fout << std::endl;
233
234 if (Util::toUpper(what) == "RHO") {
235 for (int i = localIdx[0].first() + nghost; i <= localIdx[0].last() + nghost; i++) {
236 for (int j = localIdx[1].first() + nghost; j <= localIdx[1].last() + nghost; j++) {
237 for (int k = localIdx[2].first() + nghost; k <= localIdx[2].last() + nghost; k++) {
238 // define the physical points (cell-centered)
239 double x = (i - nghost) * spacing[0] + origin[0];
240 double y = (j - nghost) * spacing[1] + origin[1];
241 double z = (k - nghost) * spacing[2] + origin[2];
242
243 fout << std::setw(5) << i << std::setw(5) << j << std::setw(5) << k
244 << std::setw(17) << x << std::setw(17) << y << std::setw(17) << z
245 << std::scientific << "\t" << field_hostV(i, j, k) << std::endl;
246 }
247 }
248 }
249 } else {
250 for (int i = localIdx[0].first() + nghost; i <= localIdx[0].last() + nghost; i++) {
251 for (int j = localIdx[1].first() + nghost; j <= localIdx[1].last() + nghost; j++) {
252 for (int k = localIdx[2].first() + nghost; k <= localIdx[2].last() + nghost; k++) {
253 // define the physical points (cell-centered)
254 double x = (i - nghost) * spacing[0] + origin[0];
255 double y = (j - nghost) * spacing[1] + origin[1];
256 double z = (k - nghost) * spacing[2] + origin[2];
257
258 // "+ 1" matches OPAL indexing in the output
259 fout << std::setw(5) << i << std::setw(5) << j << std::setw(5) << k
260 << std::setw(17) << x << std::setw(17) << y << std::setw(17) << z
261 << std::scientific << "\t" << field_hostV(i, j, k) << std::endl;
262 }
263 }
264 }
265 }
266 fout.close();
267 m << level5 << "*** FINISHED DUMPING " + Util::toUpper(what) + " FIELD *** to " << file.string()
268 << endl;
269}
270
271template <>
273 ippl::ParameterList sp;
274 sp.add("output_type", OpenSolver_t<double, 3>::SOL_AND_GRAD);
275 sp.add("use_heffte_defaults", false);
276 sp.add("use_pencils", true);
277 sp.add("use_reorder", false);
278 sp.add("use_gpu_aware", true);
279 sp.add("comm", ippl::p2p_pl);
280 sp.add("r2c_direction", 0);
281 sp.add("algorithm", OpenSolver_t<double, 3>::HOCKNEY);
282 initSolverWithParams<OpenSolver_t<double, 3>>(sp);
283}
284
285template <>
287 if constexpr (Dim == 2 || Dim == 3) {
288 ippl::ParameterList sp;
289 // Needs sol for phi_m testing/output and GRAD for E_m computation
291 sp.add("output_type", FFTSolver_t<double, 3>::GRAD);
292 // sp.add("output_type", OpenSolver_t<double, 3>::SOL_AND_GRAD);
293 sp.add("use_heffte_defaults", false);
294 sp.add("use_pencils", true);
295 sp.add("use_reorder", false);
296 sp.add("use_gpu_aware", true);
297 sp.add("comm", ippl::p2p_pl);
298 sp.add("r2c_direction", 0);
299 initSolverWithParams<FFTSolver_t<double, 3>>(sp);
300 } else {
301 throw OpalException(
302 "FieldSolver<double,3>::initFFTSolver",
303 "FFTSolver_t is only implemented for 2D and 3D fields.");
304 }
305}
306
307template <>
309 ippl::ParameterList sp;
310 sp.add("output_type", CGSolver_t<double, 3>::GRAD);
312 sp.add("tolerance", 1e-12);
313
314 initSolverWithParams<CGSolver_t<double, 3>>(sp);
315}
316
317template <>
319 ippl::ParameterList sp;
320 if constexpr (Dim == 2 || Dim == 3) {
321 initSolverWithParams<NullSolver_t<T, Dim>>(sp);
322 } else {
323 throw OpalException(
324 "FieldSolver<double,3>::initNullSolver",
325 "NullSolver_t is only implemented for 2D and 3D fields.");
326 }
327}
328
329template <>
331 Inform m;
332 if (this->getStype() == "FFT") {
333 initFFTSolver();
334 } else if (this->getStype() == "OPEN") {
335 initOpenSolver();
336 } else if (this->getStype() == "CG") {
337 initCGSolver();
338 } else if (this->getStype() == "NONE") {
339 initNullSolver();
340 } else {
341 throw OpalException(
342 "FieldSolver::initSolver",
343 "No known solver matches the argument: " + this->getStype());
344 }
345}
346
347/*template <>
348void FieldSolver<double,3>::setPotentialBCs() {
349 // CG requires explicit periodic boundary conditions while the periodic Poisson solver
350 // simply assumes them
351 if (this->getStype() == "CG") {
352 typedef ippl::BConds<Field<double, 3>, 3> bc_type;
353 bc_type allPeriodic;
354 for (unsigned int i = 0; i < 2 * 3; ++i) {
355 allPeriodic[i] = std::make_shared<ippl::PeriodicFace<Field<double, 3>>>(i);
356 }
357 phi_m->setFieldBC(allPeriodic);
358 }
359 }*/
360
361template <>
362void FieldSolver<double, 3>::runSolver(bool force_skip_field_dump) {
363 constexpr int Dim = 3;
364 Inform m("FieldSolver::runSolver");
365 /*
366 Add this output such that there is no possible unused variable warning for
367 force_skip_field_dump, which is only used in the debug output functions
368 for now.
369 */
370 m << level3 << "Running solver with type: " << this->getStype()
371 << ". Force skip field dump: " << force_skip_field_dump << endl;
372
373 if (this->getStype() == "CG") {
374 CGSolver_t<double, 3>& solver = std::get<CGSolver_t<double, 3>>(this->getSolver());
375#ifdef OPALX_FIELD_DEBUG
376 if (!force_skip_field_dump) this->dumpScalField("rho");
377#endif
378 // std::get<CGSolver_t<double, 3>>(this->getSolver()).solve();
379 solver.solve();
380#ifdef OPALX_FIELD_DEBUG
381 if (!force_skip_field_dump) this->dumpScalField("phi");
382#endif
383 /*
384 if (ippl::Comm->rank() == 0) {
385 std::stringstream fname;
386 fname << "data/CG_";
387 fname << ippl::Comm->size();
388 fname << ".csv";
389
390 Inform log(NULL, fname.str().c_str(), Inform::APPEND);
391 int iterations = solver.getIterationCount();
392 // Assume the dummy solve is the first call
393 if (iterations == 0) {
394 log << "residue,iterations" << endl;
395 }
396 // Don't print the dummy solve
397 if (iterations > 0) {
398 log << solver.getResidue() << "," << iterations << endl;
399 }
400 }
401 ippl::Comm->barrier();*/
402 int iterations = solver.getIterationCount();
403 int residue = solver.getResidue();
404 m << level4 << "CG solver finished. Iterations: " << iterations << ", Residue: " << residue
405 << endl;
406 } else if (this->getStype() == "FFT") {
407 if constexpr (Dim == 2 || Dim == 3) {
408#ifdef OPALX_FIELD_DEBUG
409 if (!force_skip_field_dump) this->dumpScalField("rho");
410#endif
411
412 std::get<FFTSolver_t<double, 3>>(this->getSolver()).solve();
413#ifdef OPALX_FIELD_DEBUG
415 if (!force_skip_field_dump) this->dumpScalField("phi");
416 if (!force_skip_field_dump) this->dumpVectField("ef");
417#endif
418 }
419 } else if (this->getStype() == "P3M") {
420 if constexpr (Dim == 3) {
421 std::get<FFTTruncatedGreenSolver_t<double, 3>>(this->getSolver()).solve();
422 }
423 } else if (this->getStype() == "OPEN") {
424 if constexpr (Dim == 3) {
425#ifdef OPALX_FIELD_DEBUG
426 if (!force_skip_field_dump) this->dumpScalField("rho");
427#endif
428 std::get<OpenSolver_t<double, 3>>(this->getSolver()).solve();
429#ifdef OPALX_FIELD_DEBUG
430 if (!force_skip_field_dump) this->dumpScalField("phi");
431 if (!force_skip_field_dump) this->dumpVectField("ef");
432#endif
433 }
434 } else if (this->getStype() == "NONE") {
435 std::get<NullSolver_t<T, Dim>>(this->getSolver()).solve();
436 } else {
437 throw OpalException(
438 "FieldSolver::runSolver",
439 "No known solver matches the argument: " + this->getStype());
440 }
441
442 call_counter_m++;
443}
444
445template <>
446void FieldSolver<double, 3>::runShiftedOpenSolver(const ippl::Vector<double, 3>& shift) {
447 if (this->getStype() != "OPEN") {
448 throw OpalException(
449 "FieldSolver::runShiftedOpenSolver",
450 "SHIFTED_GREENS_FUNCTION requires FIELDSOLVER type OPEN (got '" + this->getStype()
451 + "').");
452 }
453
454 Inform m("FieldSolver::runShiftedOpenSolver");
455 m << level4 << "Running shifted open solver with shift = " << shift << endl;
456
457 auto& openSolver = std::get<OpenSolver_t<double, 3>>(this->getSolver());
458
459 // Install the translated kernel (overwrites grntr_m inside IPPL).
460 openSolver.shiftedGreensFunction(shift);
461
462#ifdef OPALX_FIELD_DEBUG
463 this->dumpScalField("rho");
464#endif
465
466 openSolver.solve();
467
468#ifdef OPALX_FIELD_DEBUG
469 this->dumpScalField("phi");
470 this->dumpVectField("ef");
471#endif
472
473 // Restore the standard Green's function so subsequent primary solves in
474 // later bins do not silently reuse the shifted kernel. See the header
475 // doc for the defensive-guard rationale.
476 openSolver.greensFunction();
477
478 call_counter_m++;
479}
480
481// Implement getCouplingConstant
482template <>
484 /*
485 In SI units, the coupling constant for the electric field is
486 1/(4*pi*epsilon_0). However, some solvers seem to use different conventions
487 (likely due to different Green's function conventions or FFT normalizations).
488
489 As I can tell, the IPPL solvers all need 1/eps0. However, just in case,
490 leave it like this for now. Perhaps later this can be changed to simply
491 something like `return 1.0 / Physics::epsilon_0;`.
492 */
493
495 const std::string stype = this->getStype();
496 if (stype == "OPEN") {
497 return 1.0 / Physics::epsilon_0;
498 } else if (stype == "FFT") {
499 return 1.0 / Physics::epsilon_0;
500 } else if (stype == "CG") {
501 return 1.0 / Physics::epsilon_0;
502 }
503
504 // Standard coupling constant (from before)
505 return 1.0 / (4.0 * Physics::pi * Physics::epsilon_0);
506}
507
508template <>
510 Inform m("FieldSolver::setPotentialBCs");
511 // Check if BC handler is set
512 if (!hasValidBCHandler()) {
513 throw OpalException("FieldSolver::setPotentialBCs", "BC Handler not set or invalid.");
514 }
515
516 if (this->getStype() != "CG") {
517 m << level3 << "Potential BCs only need to be set for CG solver. Current solver type: "
518 << this->getStype() << endl;
519 return;
520 }
521
522 // Need to do it like that, because for some reason IPPL wants a reference,
523 // therefore cannot simply say "setFieldBC(...toIPPLBConds())".
524 typedef ippl::BConds<Field_t<Dim>, Dim> bc_type;
525 bc_type bc_container = getBCHandler()->toIPPLBConds<Field_t<Dim>>();
526 phi_m->setFieldBC(bc_container);
527 // rho_m->setFieldBC(bc_container);
528 m << level4 << "Potential BCs in FieldSolver updated using BCHandler." << endl;
529}
530
531/*
533template<>
534void FieldSolver<double,3>::initP3MSolver() {
535 // if constexpr (Dim == 3) {
536 ippl::ParameterList sp;
537 sp.add("output_type", P3MSolver_t<double, 3>::GRAD);
538 sp.add("use_heffte_defaults", false);
539 sp.add("use_pencils", true);
540 sp.add("use_reorder", false);
541 sp.add("use_gpu_aware", true);
542 sp.add("comm", ippl::p2p_pl);
543 sp.add("r2c_direction", 0);
544
545 initSolverWithParams<P3MSolver_t<double, 3>>(sp);
546 // } else {
547 // throw std::runtime_error("Unsupported dimensionality for P3M solver");
548 // }
549}
550
551*/
Inform * gmsg
Definition changes.cpp:7
constexpr unsigned Dim
Definition OPALTypes.h:7
ippl::Field< double, Dim, ViewArgs... > Field_t
Definition PBunchDefs.h:28
ippl::Field< ippl::Vector< T, Dim >, Dim, ViewArgs... > VField_t
Definition PBunchDefs.h:31
void runShiftedOpenSolver(const ippl::Vector< double, Dim > &shift)
Run an Open-solver solve with a shifted free-space Green's function.
void dumpVectField(std::string what)
void initSolver() override
void initFFTSolver()
void initCGSolver()
void runSolver() override
void initSolverWithParams(const ippl::ParameterList &sp)
void initOpenSolver()
T getCouplingConstant() const
Get the solver's coupling constant.
void initNullSolver()
void dumpScalField(std::string what)
void setPotentialBCs()
Set boundary conditions for the electrostatic potential field.
std::string getInputBasename()
get input file name without extension
Definition OpalData.cpp:571
static OpalData * getInstance()
Definition OpalData.cpp:193
constexpr double epsilon_0
The permittivity of vacuum in As/Vm.
Definition Physics.h:66
constexpr double pi
The value of.
Definition Physics.h:36
std::string toUpper(const std::string &str)
Definition Util.cpp:141