14 using detail::solver_recv;
15 using detail::solver_send;
20 template <
typename FieldLHS,
typename FieldRHS>
21 FFTOpenPoissonSolver<FieldLHS, FieldRHS>::FFTOpenPoissonSolver()
27 , meshComplex_m(nullptr)
28 , layoutComplex_m(nullptr)
32 , layout2n1_m(nullptr)
34 setDefaultParameters();
37 template <
typename FieldLHS,
typename FieldRHS>
38 FFTOpenPoissonSolver<FieldLHS, FieldRHS>::FFTOpenPoissonSolver(rhs_type& rhs,
39 ParameterList& params)
44 , meshComplex_m(nullptr)
45 , layoutComplex_m(nullptr)
49 , layout2n1_m(nullptr)
51 using T =
typename FieldLHS::value_type::value_type;
52 static_assert(std::is_floating_point<T>::value,
"Not a floating point type");
54 setDefaultParameters();
55 this->params_m.merge(params);
56 this->params_m.update(
"output_type", Base::SOL);
61 template <
typename FieldLHS,
typename FieldRHS>
62 FFTOpenPoissonSolver<FieldLHS, FieldRHS>::FFTOpenPoissonSolver(lhs_type& lhs, rhs_type& rhs,
63 ParameterList& params)
68 , meshComplex_m(nullptr)
69 , layoutComplex_m(nullptr)
73 , layout2n1_m(nullptr)
75 using T =
typename FieldLHS::value_type::value_type;
76 static_assert(std::is_floating_point<T>::value,
"Not a floating point type");
78 setDefaultParameters();
79 this->params_m.merge(params);
87 template <
typename FieldLHS,
typename FieldRHS>
92 static IpplTimings::TimerRef initialize = IpplTimings::getTimer(
"Initialize");
93 IpplTimings::startTimer(initialize);
97 IpplTimings::stopTimer(initialize);
104 template <
typename FieldLHS,
typename FieldRHS>
107 const int out = this->params_m.template get<int>(
"output_type");
109 if (out != Base::SOL_AND_GRAD) {
111 "FFTOpenPoissonSolver::setGradFD()",
112 "Cannot use gradient for Efield computation unless output type is SOL_AND_GRAD");
121 template <
typename FieldLHS,
typename FieldRHS>
122 void FFTOpenPoissonSolver<FieldLHS, FieldRHS>::initializeFields() {
124 const int alg = this->params_m.template get<int>(
"algorithm");
125 const int greensFunctionType = this->params_m.template get<int>(
"greens_function");
126 const bool hessian = this->params_m.template get<bool>(
"hessian");
129 if ((alg != Algorithm::VICO) && (alg != Algorithm::HOCKNEY)
130 && (alg != Algorithm::BIHARMONIC) && (alg != Algorithm::DCT_VICO)) {
131 throw IpplException(
"FFTOpenPoissonSolver::initializeFields()",
132 "Currently only HOCKNEY, VICO, DCT_VICO, and BIHARMONIC are "
133 "supported for open BCs");
136 if ((greensFunctionType != GreenFunction::STANDARD)
137 && (greensFunctionType != GreenFunction::INTEGRATED)) {
138 throw IpplException(
"FFTOpenPoissonSolver::initializeFields()",
139 "Currently only STANDARD and INTEGRATED Green's functions are "
140 "supported for open BCs");
143 if ((greensFunctionType == GreenFunction::INTEGRATED)
144 && ((alg != Algorithm::HOCKNEY) || (Dim != 3))) {
145 throw IpplException(
"FFTOpenPoissonSolver::initializeFields()",
146 "The integrated Green's function is currently only implemented "
147 "for 3D HOCKNEY open BCs");
151 if ((Dim == 2) && (alg != Algorithm::HOCKNEY)) {
152 throw IpplException(
"FFTOpenPoissonSolver::initializeFields()",
153 "Only HOCKNEY supports 2D!");
154 }
else if (Dim == 1) {
155 throw IpplException(
"FFTOpenPoissonSolver::initializeFields()",
156 "Only 2D and 3D solves supported!");
160 layout_mp = &(this->rhs_mp->getLayout());
161 mesh_mp = &(this->rhs_mp->get_mesh());
162 mpi::Communicator comm = layout_mp->comm;
165 hr_m = mesh_mp->getMeshSpacing();
166 vector_type origin = mesh_mp->getOrigin();
169 domain_m = layout_mp->getDomain();
172 for (
unsigned int i = 0; i < Dim; ++i) {
173 nr_m[i] = domain_m[i].length();
176 domain2_m[i] = Index(2 * nr_m[i]);
180 std::array<bool, Dim> isParallel = layout_mp->isParallel();
183 using mesh_type =
typename lhs_type::Mesh_t;
184 mesh2_m = std::unique_ptr<mesh_type>(
new mesh_type(domain2_m, hr_m, origin));
185 layout2_m = std::unique_ptr<FieldLayout_t>(
new FieldLayout_t(comm, domain2_m, isParallel));
191 unsigned int RCDirection = this->params_m.template get<int>(
"r2c_direction");
192 for (
unsigned int i = 0; i < Dim; ++i) {
193 if (i == RCDirection) {
194 domainComplex_m[RCDirection] = Index(nr_m[RCDirection] + 1);
196 domainComplex_m[i] = Index(2 * nr_m[i]);
201 meshComplex_m = std::unique_ptr<mesh_type>(
new mesh_type(domainComplex_m, hr_m, origin));
203 std::unique_ptr<FieldLayout_t>(
new FieldLayout_t(comm, domainComplex_m, isParallel));
206 storage_field.initialize(*mesh2_m, *layout2_m);
207 rho2tr_m.initialize(*meshComplex_m, *layoutComplex_m);
208 grntr_m.initialize(*meshComplex_m, *layoutComplex_m);
210 int out = this->params_m.template get<int>(
"output_type");
211 if (((out == Base::GRAD || out == Base::SOL_AND_GRAD) && !isGradFD_m) || hessian) {
212 temp_m.initialize(*meshComplex_m, *layoutComplex_m);
216 hess_m.initialize(*mesh_mp, *layout_mp);
220 fft_m = std::make_unique<FFT_t>(*layout2_m, *layoutComplex_m, this->params_m);
225 if (alg == Algorithm::VICO || alg == Algorithm::BIHARMONIC) {
227 static IpplTimings::TimerRef initialize_vico =
228 IpplTimings::getTimer(
"Initialize: extra Vico");
229 IpplTimings::startTimer(initialize_vico);
231 for (
unsigned int i = 0; i < Dim; ++i) {
232 domain4_m[i] = Index(4 * nr_m[i]);
236 using mesh_type =
typename lhs_type::Mesh_t;
237 mesh4_m = std::unique_ptr<mesh_type>(
new mesh_type(domain4_m, hr_m, origin));
239 std::unique_ptr<FieldLayout_t>(
new FieldLayout_t(comm, domain4_m, isParallel));
242 grnL_m.initialize(*mesh4_m, *layout4_m);
245 fft4n_m = std::make_unique<FFT<CCTransform, CxField_gt>>(*layout4_m, this->params_m);
247 IpplTimings::stopTimer(initialize_vico);
252 if (alg == Algorithm::DCT_VICO) {
254 static IpplTimings::TimerRef initialize_vico =
255 IpplTimings::getTimer(
"Initialize: extra Vico");
256 IpplTimings::startTimer(initialize_vico);
259 for (
unsigned int i = 0; i < Dim; ++i) {
260 domain2n1_m[i] = Index(2 * nr_m[i] + 1);
264 mesh2n1_m = std::unique_ptr<mesh_type>(
new mesh_type(domain2n1_m, hr_m, origin));
266 std::unique_ptr<FieldLayout_t>(
new FieldLayout_t(comm, domain2n1_m, isParallel));
269 grn2n1_m.initialize(*mesh2n1_m, *layout2n1_m);
272 fft2n1_m = std::make_unique<FFT<Cos1Transform, Field_t>>(*layout2n1_m, this->params_m);
274 IpplTimings::stopTimer(initialize_vico);
278 if ((alg == Algorithm::HOCKNEY) && (greensFunctionType == GreenFunction::STANDARD)) {
280 static IpplTimings::TimerRef initialize_hockney =
281 IpplTimings::getTimer(
"Initialize: extra Hockney");
282 IpplTimings::startTimer(initialize_hockney);
284 for (
unsigned int gd = 0; gd < Dim; ++gd) {
285 grnIField_m[gd].initialize(*mesh2_m, *layout2_m);
288 auto view = grnIField_m[gd].getView();
289 const int nghost = grnIField_m[gd].getNghost();
290 const auto& ldom = layout2_m->getLocalNDIndex();
293 const int size = nr_m[gd];
296 using index_array_type =
typename RangePolicy<Dim>::index_array_type;
298 "Helper index Green field initialization",
299 grnIField_m[gd].getFieldRangePolicy(),
300 KOKKOS_LAMBDA(
const index_array_type& args) {
301 scalar_type checkVal = 0.0;
305 for (
unsigned d = 0; d < Dim; ++d) {
306 igVec[d] += ldom[d].first();
307 checkVal += igVec[d];
311 const bool outsideN = (igVec[gd] >= size);
313 (2 * size * outsideN - igVec[gd]) * (2 * size * outsideN - igVec[gd]);
316 const bool isOrig = (checkVal == 0);
317 apply(view, args) += isOrig * 1.0;
320 IpplTimings::stopTimer(initialize_hockney);
323 static IpplTimings::TimerRef warmup = IpplTimings::getTimer(
"Warmup");
324 IpplTimings::startTimer(warmup);
327 fft_m->warmup(rho2_mr, rho2tr_m);
328 if (alg == Algorithm::VICO || alg == Algorithm::BIHARMONIC) {
329 fft4n_m->warmup(grnL_m);
331 if (alg == Algorithm::DCT_VICO) {
332 fft2n1_m->warmup(grn2n1_m);
335 IpplTimings::stopTimer(warmup);
339 if (alg == Algorithm::VICO || alg == Algorithm::BIHARMONIC) {
342 if (alg == Algorithm::DCT_VICO) {
349 static IpplTimings::TimerRef ginit = IpplTimings::getTimer(
"Green Init");
350 IpplTimings::startTimer(ginit);
352 IpplTimings::stopTimer(ginit);
357 template <
typename FieldLHS,
typename FieldRHS>
360 static IpplTimings::TimerRef solve = IpplTimings::getTimer(
"Solve");
361 IpplTimings::startTimer(solve);
364 const int out = this->params_m.template get<int>(
"output_type");
367 const int alg = this->params_m.template get<int>(
"algorithm");
370 const bool hessian = this->params_m.template get<bool>(
"hessian");
373 mesh_mp = &(this->rhs_mp->get_mesh());
378 for (
unsigned int i = 0; i < Dim; ++i) {
379 if (hr_m[i] != mesh_mp->getMeshSpacing(i)) {
380 hr_m[i] = mesh_mp->getMeshSpacing(i);
386 mesh2_m->setMeshSpacing(hr_m);
387 meshComplex_m->setMeshSpacing(hr_m);
393 static IpplTimings::TimerRef stod = IpplTimings::getTimer(
"Solve: Physical to double");
394 IpplTimings::startTimer(stod);
399 const int ranks = Comm->size();
401 auto view2 = rho2_mr.getView();
402 auto view1 = this->rhs_mp->getView();
404 const int nghost2 = rho2_mr.getNghost();
405 const int nghost1 = this->rhs_mp->getNghost();
407 const auto& ldom2 = layout2_m->getLocalNDIndex();
408 const auto& ldom1 = layout_mp->getLocalNDIndex();
414 const auto& lDomains2 = layout2_m->getHostLocalDomains();
417 std::vector<MPI_Request> requests(0);
419 for (
int i = 0; i < ranks; ++i) {
420 if (lDomains2[i].touches(ldom1)) {
421 auto intersection = lDomains2[i].intersect(ldom1);
423 solver_send(mpi::tag::OPEN_SOLVER, 0, i, intersection, ldom1, nghost1, view1,
429 const auto& lDomains1 = layout_mp->getHostLocalDomains();
431 for (
int i = 0; i < ranks; ++i) {
432 if (lDomains1[i].touches(ldom2)) {
433 auto intersection = lDomains1[i].intersect(ldom2);
435 mpi::Communicator::size_type nrecvs;
436 nrecvs = intersection.size();
438 buffer_type buf = Comm->getBuffer<memory_space, Trhs>(nrecvs);
440 Comm->recv(i, mpi::tag::OPEN_SOLVER, fd_m, *buf, nrecvs *
sizeof(Trhs), nrecvs);
443 unpack(intersection, view2, fd_m, nghost2, ldom2);
448 if (requests.size() > 0) {
449 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
451 ippl::Comm->freeAllBuffers();
454 "Write rho on the doubled grid", this->rhs_mp->getFieldRangePolicy(),
455 KOKKOS_LAMBDA(
const index_array_type& args) {
456 scalar_type checkVal = 0;
458 Vector<int, Dim> igVec1 = args - nghost1;
459 Vector<int, Dim> igVec2 = args - nghost2;
461 for (
unsigned d = 0; d < Dim; ++d) {
462 igVec1[d] += ldom1[d].first();
463 igVec2[d] += ldom2[d].first();
465 checkVal += Kokkos::abs(igVec1[d] - igVec2[d]);
472 const bool isQuadrant1 = (checkVal == 0);
473 apply(view2, args) =
apply(view1, args) * isQuadrant1;
477 IpplTimings::stopTimer(stod);
480 static IpplTimings::TimerRef fftrho = IpplTimings::getTimer(
"FFT: Rho");
481 IpplTimings::startTimer(fftrho);
484 fft_m->transform(FORWARD, rho2_mr, rho2tr_m);
486 IpplTimings::stopTimer(fftrho);
496 rho2tr_m = -rho2tr_m * grntr_m;
499 if ((out == Base::SOL) || (out == Base::SOL_AND_GRAD)) {
501 static IpplTimings::TimerRef fftc = IpplTimings::getTimer(
"FFT: Convolution");
502 IpplTimings::startTimer(fftc);
505 fft_m->transform(BACKWARD, rho2_mr, rho2tr_m);
507 IpplTimings::stopTimer(fftc);
515 for (
unsigned int i = 0; i < Dim; ++i) {
517 case Algorithm::HOCKNEY:
518 rho2_mr = rho2_mr * 2.0 * nr_m[i] * hr_m[i];
520 case Algorithm::VICO:
521 case Algorithm::BIHARMONIC:
522 rho2_mr = rho2_mr * 2.0 * (1.0 / 4.0);
524 case Algorithm::DCT_VICO:
525 rho2_mr = rho2_mr * (1.0 / 4.0);
529 "FFTOpenPoissonSolver::initializeFields()",
530 "Currently only HOCKNEY, VICO, DCT_VICO, and BIHARMONIC are "
531 "supported for open BCs");
536 static IpplTimings::TimerRef dtos = IpplTimings::getTimer(
"Solve: Double to physical");
537 IpplTimings::startTimer(dtos);
546 const auto& lDomains1 = layout_mp->getHostLocalDomains();
548 std::vector<MPI_Request> requests(0);
550 for (
int i = 0; i < ranks; ++i) {
551 if (lDomains1[i].touches(ldom2)) {
552 auto intersection = lDomains1[i].intersect(ldom2);
554 solver_send(mpi::tag::OPEN_SOLVER, 0, i, intersection, ldom2, nghost2,
555 view2, fd_m, requests);
560 const auto& lDomains2 = layout2_m->getHostLocalDomains();
562 for (
int i = 0; i < ranks; ++i) {
563 if (ldom1.touches(lDomains2[i])) {
564 auto intersection = ldom1.intersect(lDomains2[i]);
566 mpi::Communicator::size_type nrecvs;
567 nrecvs = intersection.size();
569 buffer_type buf = Comm->getBuffer<memory_space, Trhs>(nrecvs);
571 Comm->recv(i, mpi::tag::OPEN_SOLVER, fd_m, *buf, nrecvs *
sizeof(Trhs),
575 unpack(intersection, view1, fd_m, nghost1, ldom1);
580 if (requests.size() > 0) {
581 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
583 ippl::Comm->freeAllBuffers();
587 "Write the solution into the LHS on physical grid",
588 this->rhs_mp->getFieldRangePolicy(),
589 KOKKOS_LAMBDA(
const index_array_type& args) {
590 scalar_type checkVal = 0;
592 Vector<int, Dim> igVec1 = args - nghost1;
593 Vector<int, Dim> igVec2 = args - nghost2;
595 for (
unsigned d = 0; d < Dim; ++d) {
596 igVec1[d] += ldom1[d].first();
597 igVec2[d] += ldom2[d].first();
599 checkVal += Kokkos::abs(igVec1[d] - igVec2[d]);
606 const bool isQuadrant1 = (checkVal == 0);
607 apply(view1, args) =
apply(view2, args) * isQuadrant1;
610 IpplTimings::stopTimer(dtos);
616 if (isGradFD_m && (out == Base::SOL_AND_GRAD)) {
617 *(this->lhs_mp) = -
grad(*this->rhs_mp);
621 if (((out == Base::GRAD) || (out == Base::SOL_AND_GRAD)) && (!isGradFD_m)) {
623 static IpplTimings::TimerRef efield = IpplTimings::getTimer(
"Solve: Electric field");
624 IpplTimings::startTimer(efield);
627 auto viewL = this->lhs_mp->getView();
628 const int nghostL = this->lhs_mp->getNghost();
631 auto viewR = rho2tr_m.getView();
632 const int nghostR = rho2tr_m.getNghost();
633 const auto& ldomR = layoutComplex_m->getLocalNDIndex();
636 auto view_g = temp_m.getView();
639 const scalar_type pi = Kokkos::numbers::pi_v<scalar_type>;
640 const Kokkos::complex<Trhs> I = {0.0, 1.0};
643 vector_type hsize = hr_m;
644 Vector<int, Dim> N = nr_m;
647 for (
size_t gd = 0; gd < Dim; ++gd) {
650 "Gradient - E field", rho2tr_m.getFieldRangePolicy(),
651 KOKKOS_LAMBDA(
const index_array_type& args) {
653 Vector<int, Dim> igVec = args - nghostR;
654 for (
unsigned d = 0; d < Dim; ++d) {
655 igVec[d] += ldomR[d].first();
659 const scalar_type Len = N[gd] * hsize[gd];
660 const bool shift = (igVec[gd] > N[gd]);
661 const bool notMid = (igVec[gd] != N[gd]);
663 k_gd = notMid * (pi / Len) * (igVec[gd] - shift * 2 * N[gd]);
665 apply(view_g, args) = -(I * k_gd) *
apply(viewR, args);
669 static IpplTimings::TimerRef ffte = IpplTimings::getTimer(
"FFT: Efield");
670 IpplTimings::startTimer(ffte);
673 fft_m->transform(BACKWARD, rho2_mr, temp_m);
675 IpplTimings::stopTimer(ffte);
678 for (
unsigned int i = 0; i < Dim; ++i) {
680 case Algorithm::HOCKNEY:
681 rho2_mr = rho2_mr * 2.0 * nr_m[i] * hr_m[i];
683 case Algorithm::VICO:
684 case Algorithm::BIHARMONIC:
685 rho2_mr = rho2_mr * 2.0 * (1.0 / 4.0);
687 case Algorithm::DCT_VICO:
688 rho2_mr = rho2_mr * (1.0 / 4.0);
692 "FFTOpenPoissonSolver::initializeFields()",
693 "Currently only HOCKNEY, VICO, DCT_VICO, and BIHARMONIC are "
694 "supported for open BCs");
699 static IpplTimings::TimerRef edtos =
700 IpplTimings::getTimer(
"Efield: double to phys.");
701 IpplTimings::startTimer(edtos);
709 const auto& lDomains1 = layout_mp->getHostLocalDomains();
710 std::vector<MPI_Request> requests(0);
712 for (
int i = 0; i < ranks; ++i) {
713 if (lDomains1[i].touches(ldom2)) {
714 auto intersection = lDomains1[i].intersect(ldom2);
716 solver_send(mpi::tag::OPEN_SOLVER, 0, i, intersection, ldom2, nghost2,
717 view2, fd_m, requests);
722 const auto& lDomains2 = layout2_m->getHostLocalDomains();
724 for (
int i = 0; i < ranks; ++i) {
725 if (ldom1.touches(lDomains2[i])) {
726 auto intersection = ldom1.intersect(lDomains2[i]);
728 mpi::Communicator::size_type nrecvs;
729 nrecvs = intersection.size();
731 buffer_type buf = Comm->getBuffer<memory_space, Trhs>(nrecvs);
733 Comm->recv(i, mpi::tag::OPEN_SOLVER, fd_m, *buf, nrecvs *
sizeof(Trhs),
737 unpack(intersection, viewL, gd, fd_m, nghostL, ldom1);
742 if (requests.size() > 0) {
743 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
745 ippl::Comm->freeAllBuffers();
749 "Write the E-field on physical grid", this->lhs_mp->getFieldRangePolicy(),
750 KOKKOS_LAMBDA(
const index_array_type& args) {
751 scalar_type checkVal = 0;
753 Vector<int, Dim> igVec1 = args - nghostL;
754 Vector<int, Dim> igVec2 = args - nghost2;
756 for (
unsigned d = 0; d < Dim; ++d) {
757 igVec1[d] += ldom1[d].first();
758 igVec2[d] += ldom2[d].first();
760 checkVal += Kokkos::abs(igVec1[d] - igVec2[d]);
767 const bool isQuadrant1 = (checkVal == 0);
768 apply(viewL, args)[gd] =
apply(view2, args) * isQuadrant1;
771 IpplTimings::stopTimer(edtos);
773 IpplTimings::stopTimer(efield);
779 static IpplTimings::TimerRef
hess = IpplTimings::getTimer(
"Solve: Hessian");
780 IpplTimings::startTimer(
hess);
783 auto viewH = hess_m.getView();
784 const int nghostH = hess_m.getNghost();
787 auto viewR = rho2tr_m.getView();
788 const int nghostR = rho2tr_m.getNghost();
789 const auto& ldomR = layoutComplex_m->getLocalNDIndex();
792 auto view_g = temp_m.getView();
795 const scalar_type pi = Kokkos::numbers::pi_v<scalar_type>;
798 vector_type hsize = hr_m;
799 Vector<int, Dim> N = nr_m;
802 for (
size_t row = 0; row < Dim; ++row) {
803 for (
size_t col = 0; col < Dim; ++col) {
809 "Hessian", rho2tr_m.getFieldRangePolicy(),
810 KOKKOS_LAMBDA(
const index_array_type& args) {
812 Vector<int, Dim> igVec = args - nghostR;
813 for (
unsigned d = 0; d < Dim; ++d) {
814 igVec[d] += ldomR[d].first();
819 for (
size_t d = 0; d < Dim; ++d) {
820 const scalar_type Len = N[d] * hsize[d];
821 const bool shift = (igVec[d] > N[d]);
822 const bool isMid = (igVec[d] == N[d]);
823 const bool notDiag = (row != col);
825 kVec[d] = (1 - (notDiag * isMid)) * (pi / Len)
826 * (igVec[d] - shift * 2 * N[d]);
829 apply(view_g, args) = -(kVec[col] * kVec[row]) *
apply(viewR, args);
833 static IpplTimings::TimerRef ffth = IpplTimings::getTimer(
"FFT: Hessian");
834 IpplTimings::startTimer(ffth);
837 fft_m->transform(BACKWARD, rho2_mr, temp_m);
839 IpplTimings::stopTimer(ffth);
842 for (
unsigned int i = 0; i < Dim; ++i) {
844 case Algorithm::HOCKNEY:
845 rho2_mr = rho2_mr * 2.0 * nr_m[i] * hr_m[i];
847 case Algorithm::VICO:
848 case Algorithm::BIHARMONIC:
849 rho2_mr = rho2_mr * 2.0 * (1.0 / 4.0);
851 case Algorithm::DCT_VICO:
852 rho2_mr = rho2_mr * (1.0 / 4.0);
856 "FFTOpenPoissonSolver::initializeFields()",
857 "Currently only HOCKNEY, VICO, DCT_VICO, and BIHARMONIC are "
858 "supported for open BCs");
863 static IpplTimings::TimerRef hdtos =
864 IpplTimings::getTimer(
"Hessian: double to phys.");
865 IpplTimings::startTimer(hdtos);
873 const auto& lDomains1 = layout_mp->getHostLocalDomains();
874 std::vector<MPI_Request> requests(0);
876 for (
int i = 0; i < ranks; ++i) {
877 if (lDomains1[i].touches(ldom2)) {
878 auto intersection = lDomains1[i].intersect(ldom2);
880 solver_send(mpi::tag::OPEN_SOLVER, 0, i, intersection, ldom2,
881 nghost2, view2, fd_m, requests);
886 const auto& lDomains2 = layout2_m->getHostLocalDomains();
888 for (
int i = 0; i < ranks; ++i) {
889 if (ldom1.touches(lDomains2[i])) {
890 auto intersection = ldom1.intersect(lDomains2[i]);
892 mpi::Communicator::size_type nrecvs;
893 nrecvs = intersection.size();
895 buffer_type buf = Comm->getBuffer<memory_space, Trhs>(nrecvs);
897 Comm->recv(i, mpi::tag::OPEN_SOLVER, fd_m, *buf,
898 nrecvs *
sizeof(Trhs), nrecvs);
901 unpack(intersection, viewH, fd_m, nghostH, ldom1, row, col);
906 if (requests.size() > 0) {
907 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
909 ippl::Comm->freeAllBuffers();
913 "Write Hessian on physical grid", hess_m.getFieldRangePolicy(),
914 KOKKOS_LAMBDA(
const index_array_type& args) {
915 scalar_type checkVal = 0;
917 Vector<int, Dim> igVec1 = args - nghostH;
918 Vector<int, Dim> igVec2 = args - nghost2;
920 for (
unsigned d = 0; d < Dim; ++d) {
921 igVec1[d] += ldom1[d].first();
922 igVec2[d] += ldom2[d].first();
924 checkVal += Kokkos::abs(igVec1[d] - igVec2[d]);
931 const bool isQuadrant1 = (checkVal == 0);
932 apply(viewH, args)[row][col] =
apply(view2, args) * isQuadrant1;
935 IpplTimings::stopTimer(hdtos);
938 IpplTimings::stopTimer(
hess);
940 IpplTimings::stopTimer(solve);
946 template <
typename FieldLHS,
typename FieldRHS>
947 KOKKOS_INLINE_FUNCTION
typename FFTOpenPoissonSolver<FieldLHS, FieldRHS>::scalar_type
949 const scalar_type& numerator,
const scalar_type& denominator) {
950 const scalar_type pi = Kokkos::numbers::pi_v<scalar_type>;
951 if (denominator == scalar_type(0)) {
952 if (numerator > scalar_type(0)) {
953 return scalar_type(0.5) * pi;
955 if (numerator < scalar_type(0)) {
956 return -scalar_type(0.5) * pi;
958 return scalar_type(0);
960 return Kokkos::atan(numerator / denominator);
963 template <
typename FieldLHS,
typename FieldRHS>
964 KOKKOS_INLINE_FUNCTION
typename FFTOpenPoissonSolver<FieldLHS, FieldRHS>::scalar_type
965 FFTOpenPoissonSolver<FieldLHS, FieldRHS>::integratedGreenLogTerm(
const scalar_type& a,
966 const scalar_type& b,
967 const scalar_type& c,
968 const scalar_type& r) {
969 const scalar_type coeff = b * c;
970 return (coeff == scalar_type(0)) ? scalar_type(0) : coeff * Kokkos::log(a + r);
973 template <
typename FieldLHS,
typename FieldRHS>
974 KOKKOS_INLINE_FUNCTION
typename FFTOpenPoissonSolver<FieldLHS, FieldRHS>::scalar_type
975 FFTOpenPoissonSolver<FieldLHS, FieldRHS>::integratedGreenAntiderivative(
const scalar_type& x,
976 const scalar_type& y,
977 const scalar_type& z) {
978 const scalar_type r2 = x * x + y * y + z * z;
979 if (r2 == scalar_type(0)) {
980 return scalar_type(0);
983 const scalar_type r = Kokkos::sqrt(r2);
984 scalar_type value = scalar_type(0);
985 value -= scalar_type(0.5) * z * z * integratedGreenAtanTerm(x * y, z * r);
986 value -= scalar_type(0.5) * y * y * integratedGreenAtanTerm(x * z, y * r);
987 value -= scalar_type(0.5) * x * x * integratedGreenAtanTerm(y * z, x * r);
988 value += integratedGreenLogTerm(x, y, z, r);
989 value += integratedGreenLogTerm(y, x, z, r);
990 value += integratedGreenLogTerm(z, x, y, r);
994 template <
typename FieldLHS,
typename FieldRHS>
995 KOKKOS_INLINE_FUNCTION
typename FFTOpenPoissonSolver<FieldLHS, FieldRHS>::scalar_type
996 FFTOpenPoissonSolver<FieldLHS, FieldRHS>::integratedGreenAverage(
const scalar_type& x,
997 const scalar_type& y,
998 const scalar_type& z,
999 const vector_type& h) {
1000 const scalar_type half = scalar_type(0.5);
1001 const scalar_type x0 = x - half * h[0];
1002 const scalar_type x1 = x + half * h[0];
1003 const scalar_type y0 = y - half * h[1];
1004 const scalar_type y1 = y + half * h[1];
1005 const scalar_type z0 = z - half * h[2];
1006 const scalar_type z1 = z + half * h[2];
1008 scalar_type integral = integratedGreenAntiderivative(x1, y1, z1);
1009 integral -= integratedGreenAntiderivative(x0, y1, z1);
1010 integral -= integratedGreenAntiderivative(x1, y0, z1);
1011 integral += integratedGreenAntiderivative(x0, y0, z1);
1012 integral -= integratedGreenAntiderivative(x1, y1, z0);
1013 integral += integratedGreenAntiderivative(x0, y1, z0);
1014 integral += integratedGreenAntiderivative(x1, y0, z0);
1015 integral -= integratedGreenAntiderivative(x0, y0, z0);
1017 return integral / (h[0] * h[1] * h[2]);
1020 template <
typename FieldLHS,
typename FieldRHS>
1022 const scalar_type pi = Kokkos::numbers::pi_v<scalar_type>;
1025 const int alg = this->params_m.template get<int>(
"algorithm");
1026 const int greensFunctionType = this->params_m.template get<int>(
"greens_function");
1030 if (alg == Algorithm::VICO || alg == Algorithm::BIHARMONIC) {
1031 if constexpr (Dim == 3) {
1038 for (
unsigned int i = 0; i < Dim; ++i) {
1039 hs_m[i] = pi * 0.5 / l[i];
1040 L_sum = L_sum + l[i] * l[i];
1046 for (
unsigned int i = 0; i < Dim; ++i) {
1047 origin[i] = -2 * nr_m[i] * pi / l[i];
1051 mesh4_m->setMeshSpacing(hs_m);
1054 L_sum = Kokkos::sqrt(L_sum);
1056 L_sum = 1.1 * L_sum;
1060 const int nghost_g = grnL_m.getNghost();
1061 const auto& ldom_g = layout4_m->getLocalNDIndex();
1063 Vector<int, Dim> size = nr_m;
1066 if (alg == Algorithm::VICO) {
1068 "Initialize Green's function ", grnL_m.getFieldRangePolicy(),
1069 KOKKOS_LAMBDA(
const index_array_type& args) {
1070 scalar_type checkVal = 0;
1073 Vector<int, Dim> igVec = args - nghost_g;
1074 for (
unsigned d = 0; d < Dim; ++d) {
1075 igVec[d] += ldom_g[d].first();
1076 checkVal += igVec[d];
1081 for (
unsigned d = 0; d < Dim; ++d) {
1082 bool isOutside = (igVec[d] > 2 * size[d] - 1);
1083 const Tg t = igVec[d] * hs_m[d] + isOutside * origin[d];
1086 s = Kokkos::sqrt(s);
1090 const bool isOrig = (checkVal == 0);
1091 const Tg analyticLim = -L_sum * L_sum * 0.5;
1092 const Tg value = -2.0
1093 * (Kokkos::sin(0.5 * L_sum * s) / (s + isOrig * 1.0))
1094 * (Kokkos::sin(0.5 * L_sum * s) / (s + isOrig * 1.0));
1096 apply(view_g, args) = (!isOrig) * value + isOrig * analyticLim;
1098 }
else if (alg == Algorithm::BIHARMONIC) {
1100 "Initialize Green's function ", grnL_m.getFieldRangePolicy(),
1101 KOKKOS_LAMBDA(
const index_array_type& args) {
1102 scalar_type checkVal = 0;
1105 Vector<int, Dim> igVec = args - nghost_g;
1106 for (
unsigned d = 0; d < Dim; ++d) {
1107 igVec[d] += ldom_g[d].first();
1108 checkVal += igVec[d];
1113 for (
unsigned d = 0; d < Dim; ++d) {
1114 bool isOutside = (igVec[d] > 2 * size[d] - 1);
1115 const Tg t = igVec[d] * hs_m[d] + isOutside * origin[d];
1118 s = Kokkos::sqrt(s);
1122 const bool isOrig = (checkVal == 0);
1123 const Tg analyticLim = -L_sum * L_sum * L_sum * L_sum / 8.0;
1125 -((2 - (L_sum * L_sum * s * s)) * Kokkos::cos(L_sum * s)
1126 + 2 * L_sum * s * Kokkos::sin(L_sum * s) - 2)
1127 / (2 * s * s * s * s + isOrig * 1.0);
1129 apply(view_g, args) = (!isOrig) * value + isOrig * analyticLim;
1134 static IpplTimings::TimerRef fft4 = IpplTimings::getTimer(
"FFT: Precomputation");
1135 IpplTimings::startTimer(fft4);
1138 fft4n_m->transform(BACKWARD, grnL_m);
1140 IpplTimings::stopTimer(fft4);
1145 typename Field_t::view_type view = grn_mr.getView();
1146 const int nghost = grn_mr.getNghost();
1147 const auto& ldom = layout2_m->getLocalNDIndex();
1150 static IpplTimings::TimerRef ifftshift = IpplTimings::getTimer(
"Vico shift loop");
1151 IpplTimings::startTimer(ifftshift);
1154 const int ranks = Comm->size();
1157 communicateVico(size, view_g, ldom_g, nghost_g, view, ldom, nghost);
1160 using mdrange_type = Kokkos::MDRangePolicy<Kokkos::Rank<3>>;
1161 Kokkos::parallel_for(
1162 "Restrict domain of Green's function from 4N to 2N",
1163 mdrange_type({nghost, nghost, nghost}, {view.extent(0) - nghost - size[0],
1164 view.extent(1) - nghost - size[1],
1165 view.extent(2) - nghost - size[2]}),
1166 KOKKOS_LAMBDA(
const int i,
const int j,
const int k) {
1168 const int ig = i + ldom[0].first() - nghost;
1169 const int jg = j + ldom[1].first() - nghost;
1170 const int kg = k + ldom[2].first() - nghost;
1172 const int ig2 = i + ldom_g[0].first() - nghost_g;
1173 const int jg2 = j + ldom_g[1].first() - nghost_g;
1174 const int kg2 = k + ldom_g[2].first() - nghost_g;
1176 const bool isOrig = (ig == ig2) && (jg == jg2) && (kg == kg2);
1177 view(i, j, k) = isOrig * real(view_g(i, j, k));
1180 const int s = 2 * size[0] - ig - 1 - ldom_g[0].first() + nghost_g;
1181 const int p = 2 * size[1] - jg - 1 - ldom_g[1].first() + nghost_g;
1182 const int q = 2 * size[2] - kg - 1 - ldom_g[2].first() + nghost_g;
1184 view(s, j, k) = real(view_g(i + 1, j, k));
1185 view(i, p, k) = real(view_g(i, j + 1, k));
1186 view(i, j, q) = real(view_g(i, j, k + 1));
1187 view(s, j, q) = real(view_g(i + 1, j, k + 1));
1188 view(s, p, k) = real(view_g(i + 1, j + 1, k));
1189 view(i, p, q) = real(view_g(i, j + 1, k + 1));
1190 view(s, p, q) = real(view_g(i + 1, j + 1, k + 1));
1193 IpplTimings::stopTimer(ifftshift);
1195 }
else if (alg == Algorithm::DCT_VICO) {
1196 if constexpr (Dim == 3) {
1203 for (
unsigned int i = 0; i < Dim; ++i) {
1204 hs_m[i] = Kokkos::numbers::pi_v<Trhs> * 0.5 / l[i];
1205 L_sum = L_sum + l[i] * l[i];
1212 mesh2n1_m->setMeshSpacing(hs_m);
1215 L_sum = Kokkos::sqrt(L_sum);
1216 L_sum = 1.1 * L_sum;
1219 Vector<int, Dim> size = nr_m;
1222 typename Field_t::view_type view_g2n1 = grn2n1_m.getView();
1223 const int nghost_g2n1 = grn2n1_m.getNghost();
1224 const auto& ldom_g2n1 = layout2n1_m->getLocalNDIndex();
1227 "Initialize 2N+1 Green's function ", grn2n1_m.getFieldRangePolicy(),
1228 KOKKOS_LAMBDA(
const index_array_type& args) {
1229 scalar_type checkVal = 0;
1232 Vector<int, Dim> igVec = args - nghost_g2n1;
1233 for (
unsigned d = 0; d < Dim; ++d) {
1234 igVec[d] += ldom_g2n1[d].first();
1235 checkVal += igVec[d];
1240 for (
unsigned d = 0; d < Dim; ++d) {
1241 double t = igVec[d] * hs_m[d];
1244 s = Kokkos::sqrt(s);
1246 const bool isOrig = (checkVal == 0);
1247 const double val = -2.0
1248 * (Kokkos::sin(0.5 * L_sum * s) / (s + isOrig * 1.0))
1249 * (Kokkos::sin(0.5 * L_sum * s) / (s + isOrig * 1.0));
1250 const double analyticLim = -L_sum * L_sum * 0.5;
1252 apply(view_g2n1, args) = ((!isOrig) * val) + (isOrig * analyticLim);
1256 static IpplTimings::TimerRef fft4 = IpplTimings::getTimer(
"FFT: Precomputation");
1257 IpplTimings::startTimer(fft4);
1260 fft2n1_m->transform(BACKWARD, grn2n1_m);
1262 IpplTimings::stopTimer(fft4);
1267 typename Field_t::view_type view = grn_mr.getView();
1268 const int nghost = grn_mr.getNghost();
1269 const auto& ldom = layout2_m->getLocalNDIndex();
1272 static IpplTimings::TimerRef ifftshift = IpplTimings::getTimer(
"Vico shift loop");
1273 IpplTimings::startTimer(ifftshift);
1276 int ranks = ippl::Comm->size();
1279 using mdrange_type = Kokkos::MDRangePolicy<Kokkos::Rank<3>>;
1282 communicateVico(size, view_g2n1, ldom_g2n1, nghost_g2n1, view, ldom, nghost);
1285 Kokkos::parallel_for(
1286 "Restrict domain of Green's function from 2N+1 to 2N",
1287 mdrange_type({nghost, nghost, nghost}, {view.extent(0) - nghost - size[0],
1288 view.extent(1) - nghost - size[1],
1289 view.extent(2) - nghost - size[2]}),
1290 KOKKOS_LAMBDA(
const int i,
const int j,
const int k) {
1292 const int ig = i + ldom[0].first() - nghost;
1293 const int jg = j + ldom[1].first() - nghost;
1294 const int kg = k + ldom[2].first() - nghost;
1296 const int ig2 = i + ldom_g2n1[0].first() - nghost_g2n1;
1297 const int jg2 = j + ldom_g2n1[1].first() - nghost_g2n1;
1298 const int kg2 = k + ldom_g2n1[2].first() - nghost_g2n1;
1300 const bool isOrig = ((ig == ig2) && (jg == jg2) && (kg == kg2));
1301 view(i, j, k) = isOrig * view_g2n1(i, j, k);
1304 const int s = 2 * size[0] - ig - 1 - ldom_g2n1[0].first() + nghost_g2n1;
1305 const int p = 2 * size[1] - jg - 1 - ldom_g2n1[1].first() + nghost_g2n1;
1306 const int q = 2 * size[2] - kg - 1 - ldom_g2n1[2].first() + nghost_g2n1;
1308 view(s, j, k) = view_g2n1(i + 1, j, k);
1309 view(i, p, k) = view_g2n1(i, j + 1, k);
1310 view(i, j, q) = view_g2n1(i, j, k + 1);
1311 view(s, j, q) = view_g2n1(i + 1, j, k + 1);
1312 view(s, p, k) = view_g2n1(i + 1, j + 1, k);
1313 view(i, p, q) = view_g2n1(i, j + 1, k + 1);
1314 view(s, p, q) = view_g2n1(i + 1, j + 1, k + 1);
1321 typename Field_t::view_type view = grn_mr.getView();
1322 const int nghost = grn_mr.getNghost();
1323 const auto& ldom = layout2_m->getLocalNDIndex();
1325 if (greensFunctionType == GreenFunction::INTEGRATED) {
1326 Vector<int, Dim> nr = nr_m;
1327 vector_type hs = hr_m;
1330 "Integrated Green's function ", grn_mr.getFieldRangePolicy(),
1331 KOKKOS_LAMBDA(
const index_array_type& args) {
1332 Vector<int, Dim> igVec = args - nghost;
1333 for (
unsigned d = 0; d < Dim; ++d) {
1334 igVec[d] += ldom[d].first();
1337 scalar_type offset[Dim];
1338 for (
unsigned int d = 0; d < Dim; ++d) {
1339 const int igSigned =
1340 (igVec[d] < nr[d]) ? igVec[d] : igVec[d] - 2 * nr[d];
1341 offset[d] =
static_cast<scalar_type
>(igSigned) * hs[d];
1344 const scalar_type avg =
1345 integratedGreenAverage(offset[0], offset[1], offset[2], hs);
1346 apply(view, args) = -avg / (scalar_type(4) * pi);
1353 for (
unsigned int i = 0; i < Dim; ++i) {
1354 grn_mr = grn_mr + grnIField_m[i] * hrsq[i];
1359 grn_mr = log(sqrt(grn_mr)) / (2 * pi);
1360 }
else if (Dim == 3) {
1361 grn_mr = -1.0 / (4.0 * pi * sqrt(grn_mr));
1366 "Regularize Green's function ", grn_mr.getFieldRangePolicy(),
1367 KOKKOS_LAMBDA(
const index_array_type& args) {
1368 scalar_type checkVal = 0;
1371 Vector<int, Dim> igVec = args - nghost;
1372 for (
unsigned d = 0; d < Dim; ++d) {
1373 igVec[d] += ldom[d].first();
1374 checkVal += igVec[d];
1378 const bool isOrig = (checkVal == 0);
1380 isOrig * (-1.0 / (4.0 * pi)) + (!isOrig) *
apply(view, args);
1386 static IpplTimings::TimerRef fftg = IpplTimings::getTimer(
"FFT: Green");
1387 IpplTimings::startTimer(fftg);
1390 fft_m->transform(FORWARD, grn_mr, grntr_m);
1392 IpplTimings::stopTimer(fftg);
1395 template <
typename FieldLHS,
typename FieldRHS>
1398 const ippl::NDIndex<Dim> ldom_g,
const int nghost_g,
typename Field_t::view_type view,
1400 const auto& lDomains2 = layout2_m->getHostLocalDomains();
1401 const auto& lDomains4 = layout4_m->getHostLocalDomains();
1403 std::vector<MPI_Request> requests(0);
1404 const int ranks = Comm->size();
1408 for (
unsigned i = 0; i < Dim; i++) {
1443 for (
unsigned i = 0; i < Dim; i++) {
1448 for (
int i = 0; i < ranks; ++i) {
1449 auto domain2 = lDomains2[i];
1451 if (domain2.touches(none)) {
1452 auto intersection = domain2.intersect(none);
1454 if (ldom_g.touches(intersection)) {
1455 intersection = intersection.intersect(ldom_g);
1457 solver_send(mpi::tag::VICO_SOLVER, 0, i, intersection, ldom_g, nghost_g, view_g,
1462 if (domain2.touches(x)) {
1463 auto intersection = domain2.intersect(x);
1464 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
1465 (2 * size[0] - intersection[0].last()), -1);
1469 domain4[1] = intersection[1];
1470 domain4[2] = intersection[2];
1472 if (ldom_g.touches(domain4)) {
1473 intersection = ldom_g.
intersect(domain4);
1475 solver_send(mpi::tag::VICO_SOLVER, 1, i, intersection, ldom_g, nghost_g, view_g,
1480 if (domain2.touches(y)) {
1481 auto intersection = domain2.intersect(y);
1482 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
1483 (2 * size[1] - intersection[1].last()), -1);
1486 domain4[0] = intersection[0];
1488 domain4[2] = intersection[2];
1490 if (ldom_g.touches(domain4)) {
1491 intersection = ldom_g.
intersect(domain4);
1493 solver_send(mpi::tag::VICO_SOLVER, 2, i, intersection, ldom_g, nghost_g, view_g,
1498 if (domain2.touches(z)) {
1499 auto intersection = domain2.intersect(z);
1500 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
1501 (2 * size[2] - intersection[2].last()), -1);
1504 domain4[0] = intersection[0];
1505 domain4[1] = intersection[1];
1508 if (ldom_g.touches(domain4)) {
1509 intersection = ldom_g.
intersect(domain4);
1511 solver_send(mpi::tag::VICO_SOLVER, 3, i, intersection, ldom_g, nghost_g, view_g,
1516 if (domain2.touches(xy)) {
1517 auto intersection = domain2.intersect(xy);
1518 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
1519 (2 * size[0] - intersection[0].last()), -1);
1520 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
1521 (2 * size[1] - intersection[1].last()), -1);
1526 domain4[2] = intersection[2];
1528 if (ldom_g.touches(domain4)) {
1529 intersection = ldom_g.
intersect(domain4);
1531 solver_send(mpi::tag::VICO_SOLVER, 4, i, intersection, ldom_g, nghost_g, view_g,
1536 if (domain2.touches(yz)) {
1537 auto intersection = domain2.intersect(yz);
1538 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
1539 (2 * size[1] - intersection[1].last()), -1);
1540 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
1541 (2 * size[2] - intersection[2].last()), -1);
1544 domain4[0] = intersection[0];
1548 if (ldom_g.touches(domain4)) {
1549 intersection = ldom_g.
intersect(domain4);
1550 solver_send(mpi::tag::VICO_SOLVER, 5, i, intersection, ldom_g, nghost_g, view_g,
1555 if (domain2.touches(xz)) {
1556 auto intersection = domain2.intersect(xz);
1557 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
1558 (2 * size[0] - intersection[0].last()), -1);
1559 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
1560 (2 * size[2] - intersection[2].last()), -1);
1564 domain4[1] = intersection[1];
1567 if (ldom_g.touches(domain4)) {
1568 intersection = ldom_g.
intersect(domain4);
1569 solver_send(mpi::tag::VICO_SOLVER, 6, i, intersection, ldom_g, nghost_g, view_g,
1574 if (domain2.touches(xyz)) {
1575 auto intersection = domain2.intersect(xyz);
1576 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
1577 (2 * size[0] - intersection[0].last()), -1);
1578 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
1579 (2 * size[1] - intersection[1].last()), -1);
1580 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
1581 (2 * size[2] - intersection[2].last()), -1);
1588 if (ldom_g.touches(domain4)) {
1589 intersection = ldom_g.
intersect(domain4);
1590 solver_send(mpi::tag::VICO_SOLVER, 7, i, intersection, ldom_g, nghost_g, view_g,
1597 for (
int i = 0; i < ranks; ++i) {
1598 if (ldom.touches(none)) {
1599 auto intersection = ldom.
intersect(none);
1601 if (lDomains4[i].touches(intersection)) {
1602 intersection = intersection.intersect(lDomains4[i]);
1604 solver_recv(mpi::tag::VICO_SOLVER, 0, i, intersection, ldom, nghost, view,
1609 if (ldom.touches(x)) {
1612 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
1613 (2 * size[0] - intersection[0].last()), -1);
1617 domain4[1] = intersection[1];
1618 domain4[2] = intersection[2];
1620 if (lDomains4[i].touches(domain4)) {
1621 domain4 = lDomains4[i].
intersect(domain4);
1622 domain4[0] =
ippl::Index(2 * size[0] - domain4[0].first(),
1623 2 * size[0] - domain4[0].last(), -1);
1625 intersection = intersection.intersect(domain4);
1627 solver_recv(mpi::tag::VICO_SOLVER, 1, i, intersection, ldom, nghost, view, fd_m,
1628 {
true,
false,
false});
1632 if (ldom.touches(y)) {
1635 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
1636 (2 * size[1] - intersection[1].last()), -1);
1639 domain4[0] = intersection[0];
1641 domain4[2] = intersection[2];
1643 if (lDomains4[i].touches(domain4)) {
1644 domain4 = lDomains4[i].
intersect(domain4);
1645 domain4[1] =
ippl::Index(2 * size[1] - domain4[1].first(),
1646 2 * size[1] - domain4[1].last(), -1);
1648 intersection = intersection.intersect(domain4);
1650 solver_recv(mpi::tag::VICO_SOLVER, 2, i, intersection, ldom, nghost, view, fd_m,
1651 {
false,
true,
false});
1655 if (ldom.touches(z)) {
1658 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
1659 (2 * size[2] - intersection[2].last()), -1);
1662 domain4[0] = intersection[0];
1663 domain4[1] = intersection[1];
1666 if (lDomains4[i].touches(domain4)) {
1667 domain4 = lDomains4[i].
intersect(domain4);
1668 domain4[2] =
ippl::Index(2 * size[2] - domain4[2].first(),
1669 2 * size[2] - domain4[2].last(), -1);
1671 intersection = intersection.intersect(domain4);
1673 solver_recv(mpi::tag::VICO_SOLVER, 3, i, intersection, ldom, nghost, view, fd_m,
1674 {
false,
false,
true});
1678 if (ldom.touches(xy)) {
1681 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
1682 (2 * size[0] - intersection[0].last()), -1);
1683 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
1684 (2 * size[1] - intersection[1].last()), -1);
1689 domain4[2] = intersection[2];
1691 if (lDomains4[i].touches(domain4)) {
1692 domain4 = lDomains4[i].
intersect(domain4);
1693 domain4[0] =
ippl::Index(2 * size[0] - domain4[0].first(),
1694 2 * size[0] - domain4[0].last(), -1);
1695 domain4[1] =
ippl::Index(2 * size[1] - domain4[1].first(),
1696 2 * size[1] - domain4[1].last(), -1);
1698 intersection = intersection.intersect(domain4);
1700 solver_recv(mpi::tag::VICO_SOLVER, 4, i, intersection, ldom, nghost, view, fd_m,
1701 {
true,
true,
false});
1705 if (ldom.touches(yz)) {
1708 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
1709 (2 * size[1] - intersection[1].last()), -1);
1710 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
1711 (2 * size[2] - intersection[2].last()), -1);
1714 domain4[0] = intersection[0];
1718 if (lDomains4[i].touches(domain4)) {
1719 domain4 = lDomains4[i].
intersect(domain4);
1720 domain4[1] =
ippl::Index(2 * size[1] - domain4[1].first(),
1721 2 * size[1] - domain4[1].last(), -1);
1722 domain4[2] =
ippl::Index(2 * size[2] - domain4[2].first(),
1723 2 * size[2] - domain4[2].last(), -1);
1725 intersection = intersection.intersect(domain4);
1727 solver_recv(mpi::tag::VICO_SOLVER, 5, i, intersection, ldom, nghost, view, fd_m,
1728 {
false,
true,
true});
1732 if (ldom.touches(xz)) {
1735 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
1736 (2 * size[0] - intersection[0].last()), -1);
1737 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
1738 (2 * size[2] - intersection[2].last()), -1);
1742 domain4[1] = intersection[1];
1745 if (lDomains4[i].touches(domain4)) {
1746 domain4 = lDomains4[i].
intersect(domain4);
1747 domain4[0] =
ippl::Index(2 * size[0] - domain4[0].first(),
1748 2 * size[0] - domain4[0].last(), -1);
1749 domain4[2] =
ippl::Index(2 * size[2] - domain4[2].first(),
1750 2 * size[2] - domain4[2].last(), -1);
1752 intersection = intersection.intersect(domain4);
1754 solver_recv(mpi::tag::VICO_SOLVER, 6, i, intersection, ldom, nghost, view, fd_m,
1755 {
true,
false,
true});
1759 if (ldom.touches(xyz)) {
1760 auto intersection = ldom.
intersect(xyz);
1762 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
1763 (2 * size[0] - intersection[0].last()), -1);
1764 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
1765 (2 * size[1] - intersection[1].last()), -1);
1766 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
1767 (2 * size[2] - intersection[2].last()), -1);
1774 if (lDomains4[i].touches(domain4)) {
1775 domain4 = lDomains4[i].
intersect(domain4);
1776 domain4[0] =
ippl::Index(2 * size[0] - domain4[0].first(),
1777 2 * size[0] - domain4[0].last(), -1);
1778 domain4[1] =
ippl::Index(2 * size[1] - domain4[1].first(),
1779 2 * size[1] - domain4[1].last(), -1);
1780 domain4[2] =
ippl::Index(2 * size[2] - domain4[2].first(),
1781 2 * size[2] - domain4[2].last(), -1);
1783 intersection = intersection.intersect(domain4);
1785 solver_recv(mpi::tag::VICO_SOLVER, 7, i, intersection, ldom, nghost, view, fd_m,
1786 {
true,
true,
true});
1791 if (requests.size() > 0) {
1792 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
1794 ippl::Comm->freeAllBuffers();
1798 template <
typename FieldLHS,
typename FieldRHS>
1799 void FFTOpenPoissonSolver<FieldLHS, FieldRHS>::communicateVico(
1803 const auto& lDomains2 = layout2_m->getHostLocalDomains();
1804 const auto& lDomains2n1 = layout2n1_m->getHostLocalDomains();
1806 std::vector<MPI_Request> requests(0);
1807 const int ranks = ippl::Comm->size();
1811 for (
unsigned i = 0; i < Dim; i++) {
1846 for (
unsigned i = 0; i < Dim; i++) {
1851 for (
int i = 0; i < ranks; ++i) {
1852 auto domain2 = lDomains2[i];
1854 if (domain2.touches(none)) {
1855 auto intersection = domain2.intersect(none);
1857 if (ldom_g.touches(intersection)) {
1858 intersection = intersection.intersect(ldom_g);
1860 solver_send(mpi::tag::VICO_SOLVER, 0, i, intersection, ldom_g, nghost_g, view_g,
1865 if (domain2.touches(x)) {
1866 auto intersection = domain2.intersect(x);
1867 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
1868 (2 * size[0] - intersection[0].last()), -1);
1871 domain2n1[0] = xdom;
1872 domain2n1[1] = intersection[1];
1873 domain2n1[2] = intersection[2];
1875 if (ldom_g.touches(domain2n1)) {
1876 intersection = ldom_g.
intersect(domain2n1);
1878 solver_send(mpi::tag::VICO_SOLVER, 1, i, intersection, ldom_g, nghost_g, view_g,
1883 if (domain2.touches(y)) {
1884 auto intersection = domain2.intersect(y);
1885 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
1886 (2 * size[1] - intersection[1].last()), -1);
1889 domain2n1[0] = intersection[0];
1890 domain2n1[1] = ydom;
1891 domain2n1[2] = intersection[2];
1893 if (ldom_g.touches(domain2n1)) {
1894 intersection = ldom_g.
intersect(domain2n1);
1896 solver_send(mpi::tag::VICO_SOLVER, 2, i, intersection, ldom_g, nghost_g, view_g,
1901 if (domain2.touches(z)) {
1902 auto intersection = domain2.intersect(z);
1903 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
1904 (2 * size[2] - intersection[2].last()), -1);
1907 domain2n1[0] = intersection[0];
1908 domain2n1[1] = intersection[1];
1909 domain2n1[2] = zdom;
1911 if (ldom_g.touches(domain2n1)) {
1912 intersection = ldom_g.
intersect(domain2n1);
1914 solver_send(mpi::tag::VICO_SOLVER, 3, i, intersection, ldom_g, nghost_g, view_g,
1919 if (domain2.touches(xy)) {
1920 auto intersection = domain2.intersect(xy);
1921 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
1922 (2 * size[0] - intersection[0].last()), -1);
1923 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
1924 (2 * size[1] - intersection[1].last()), -1);
1927 domain2n1[0] = xdom;
1928 domain2n1[1] = ydom;
1929 domain2n1[2] = intersection[2];
1931 if (ldom_g.touches(domain2n1)) {
1932 intersection = ldom_g.
intersect(domain2n1);
1934 solver_send(mpi::tag::VICO_SOLVER, 4, i, intersection, ldom_g, nghost_g, view_g,
1939 if (domain2.touches(yz)) {
1940 auto intersection = domain2.intersect(yz);
1941 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
1942 (2 * size[1] - intersection[1].last()), -1);
1943 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
1944 (2 * size[2] - intersection[2].last()), -1);
1947 domain2n1[0] = intersection[0];
1948 domain2n1[1] = ydom;
1949 domain2n1[2] = zdom;
1951 if (ldom_g.touches(domain2n1)) {
1952 intersection = ldom_g.
intersect(domain2n1);
1954 solver_send(mpi::tag::VICO_SOLVER, 5, i, intersection, ldom_g, nghost_g, view_g,
1959 if (domain2.touches(xz)) {
1960 auto intersection = domain2.intersect(xz);
1961 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
1962 (2 * size[0] - intersection[0].last()), -1);
1963 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
1964 (2 * size[2] - intersection[2].last()), -1);
1967 domain2n1[0] = xdom;
1968 domain2n1[1] = intersection[1];
1969 domain2n1[2] = zdom;
1971 if (ldom_g.touches(domain2n1)) {
1972 intersection = ldom_g.
intersect(domain2n1);
1974 solver_send(mpi::tag::VICO_SOLVER, 6, i, intersection, ldom_g, nghost_g, view_g,
1979 if (domain2.touches(xyz)) {
1980 auto intersection = domain2.intersect(xyz);
1981 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
1982 (2 * size[0] - intersection[0].last()), -1);
1983 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
1984 (2 * size[1] - intersection[1].last()), -1);
1985 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
1986 (2 * size[2] - intersection[2].last()), -1);
1989 domain2n1[0] = xdom;
1990 domain2n1[1] = ydom;
1991 domain2n1[2] = zdom;
1993 if (ldom_g.touches(domain2n1)) {
1994 intersection = ldom_g.
intersect(domain2n1);
1996 solver_send(mpi::tag::VICO_SOLVER, 7, i, intersection, ldom_g, nghost_g, view_g,
2003 for (
int i = 0; i < ranks; ++i) {
2004 if (ldom.touches(none)) {
2005 auto intersection = ldom.
intersect(none);
2007 if (lDomains2n1[i].touches(intersection)) {
2008 intersection = intersection.intersect(lDomains2n1[i]);
2010 solver_recv(mpi::tag::VICO_SOLVER, 0, i, intersection, ldom, nghost, view,
2015 if (ldom.touches(x)) {
2018 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
2019 (2 * size[0] - intersection[0].last()), -1);
2022 domain2n1[0] = xdom;
2023 domain2n1[1] = intersection[1];
2024 domain2n1[2] = intersection[2];
2026 if (lDomains2n1[i].touches(domain2n1)) {
2027 domain2n1 = lDomains2n1[i].
intersect(domain2n1);
2028 domain2n1[0] =
ippl::Index(2 * size[0] - domain2n1[0].first(),
2029 2 * size[0] - domain2n1[0].last(), -1);
2031 intersection = intersection.intersect(domain2n1);
2033 solver_recv(mpi::tag::VICO_SOLVER, 1, i, intersection, ldom, nghost, view, fd_m,
2034 {
true,
false,
false});
2038 if (ldom.touches(y)) {
2041 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
2042 (2 * size[1] - intersection[1].last()), -1);
2045 domain2n1[0] = intersection[0];
2046 domain2n1[1] = ydom;
2047 domain2n1[2] = intersection[2];
2049 if (lDomains2n1[i].touches(domain2n1)) {
2050 domain2n1 = lDomains2n1[i].
intersect(domain2n1);
2051 domain2n1[1] =
ippl::Index(2 * size[1] - domain2n1[1].first(),
2052 2 * size[1] - domain2n1[1].last(), -1);
2054 intersection = intersection.intersect(domain2n1);
2056 solver_recv(mpi::tag::VICO_SOLVER, 2, i, intersection, ldom, nghost, view, fd_m,
2057 {
false,
true,
false});
2061 if (ldom.touches(z)) {
2064 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
2065 (2 * size[2] - intersection[2].last()), -1);
2068 domain2n1[0] = intersection[0];
2069 domain2n1[1] = intersection[1];
2070 domain2n1[2] = zdom;
2072 if (lDomains2n1[i].touches(domain2n1)) {
2073 domain2n1 = lDomains2n1[i].
intersect(domain2n1);
2074 domain2n1[2] =
ippl::Index(2 * size[2] - domain2n1[2].first(),
2075 2 * size[2] - domain2n1[2].last(), -1);
2077 intersection = intersection.intersect(domain2n1);
2079 solver_recv(mpi::tag::VICO_SOLVER, 3, i, intersection, ldom, nghost, view, fd_m,
2080 {
false,
false,
true});
2084 if (ldom.touches(xy)) {
2087 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
2088 (2 * size[0] - intersection[0].last()), -1);
2089 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
2090 (2 * size[1] - intersection[1].last()), -1);
2093 domain2n1[0] = xdom;
2094 domain2n1[1] = ydom;
2095 domain2n1[2] = intersection[2];
2097 if (lDomains2n1[i].touches(domain2n1)) {
2098 domain2n1 = lDomains2n1[i].
intersect(domain2n1);
2099 domain2n1[0] =
ippl::Index(2 * size[0] - domain2n1[0].first(),
2100 2 * size[0] - domain2n1[0].last(), -1);
2101 domain2n1[1] =
ippl::Index(2 * size[1] - domain2n1[1].first(),
2102 2 * size[1] - domain2n1[1].last(), -1);
2104 intersection = intersection.intersect(domain2n1);
2106 solver_recv(mpi::tag::VICO_SOLVER, 4, i, intersection, ldom, nghost, view, fd_m,
2107 {
true,
true,
false});
2111 if (ldom.touches(yz)) {
2114 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
2115 (2 * size[1] - intersection[1].last()), -1);
2116 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
2117 (2 * size[2] - intersection[2].last()), -1);
2120 domain2n1[0] = intersection[0];
2121 domain2n1[1] = ydom;
2122 domain2n1[2] = zdom;
2124 if (lDomains2n1[i].touches(domain2n1)) {
2125 domain2n1 = lDomains2n1[i].
intersect(domain2n1);
2126 domain2n1[1] =
ippl::Index(2 * size[1] - domain2n1[1].first(),
2127 2 * size[1] - domain2n1[1].last(), -1);
2128 domain2n1[2] =
ippl::Index(2 * size[2] - domain2n1[2].first(),
2129 2 * size[2] - domain2n1[2].last(), -1);
2131 intersection = intersection.intersect(domain2n1);
2133 solver_recv(mpi::tag::VICO_SOLVER, 5, i, intersection, ldom, nghost, view, fd_m,
2134 {
false,
true,
true});
2138 if (ldom.touches(xz)) {
2141 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
2142 (2 * size[0] - intersection[0].last()), -1);
2143 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
2144 (2 * size[2] - intersection[2].last()), -1);
2147 domain2n1[0] = xdom;
2148 domain2n1[1] = intersection[1];
2149 domain2n1[2] = zdom;
2151 if (lDomains2n1[i].touches(domain2n1)) {
2152 domain2n1 = lDomains2n1[i].
intersect(domain2n1);
2153 domain2n1[0] =
ippl::Index(2 * size[0] - domain2n1[0].first(),
2154 2 * size[0] - domain2n1[0].last(), -1);
2155 domain2n1[2] =
ippl::Index(2 * size[2] - domain2n1[2].first(),
2156 2 * size[2] - domain2n1[2].last(), -1);
2158 intersection = intersection.intersect(domain2n1);
2160 solver_recv(mpi::tag::VICO_SOLVER, 6, i, intersection, ldom, nghost, view, fd_m,
2161 {
true,
false,
true});
2165 if (ldom.touches(xyz)) {
2166 auto intersection = ldom.
intersect(xyz);
2168 auto xdom =
ippl::Index((2 * size[0] - intersection[0].first()),
2169 (2 * size[0] - intersection[0].last()), -1);
2170 auto ydom =
ippl::Index((2 * size[1] - intersection[1].first()),
2171 (2 * size[1] - intersection[1].last()), -1);
2172 auto zdom =
ippl::Index((2 * size[2] - intersection[2].first()),
2173 (2 * size[2] - intersection[2].last()), -1);
2176 domain2n1[0] = xdom;
2177 domain2n1[1] = ydom;
2178 domain2n1[2] = zdom;
2180 if (lDomains2n1[i].touches(domain2n1)) {
2181 domain2n1 = lDomains2n1[i].
intersect(domain2n1);
2182 domain2n1[0] =
ippl::Index(2 * size[0] - domain2n1[0].first(),
2183 2 * size[0] - domain2n1[0].last(), -1);
2184 domain2n1[1] =
ippl::Index(2 * size[1] - domain2n1[1].first(),
2185 2 * size[1] - domain2n1[1].last(), -1);
2186 domain2n1[2] =
ippl::Index(2 * size[2] - domain2n1[2].first(),
2187 2 * size[2] - domain2n1[2].last(), -1);
2189 intersection = intersection.intersect(domain2n1);
2191 solver_recv(mpi::tag::VICO_SOLVER, 7, i, intersection, ldom, nghost, view, fd_m,
2192 {
true,
true,
true});
2197 if (requests.size() > 0) {
2198 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
2200 ippl::Comm->freeAllBuffers();
2211 template <
typename FieldLHS,
typename FieldRHS>
2213 const Vector<double, Dim>& shift) {
2214 const int alg = this->params_m.template get<int>(
"algorithm");
2215 const int greensFunctionType = this->params_m.template get<int>(
"greens_function");
2216 if (alg != Algorithm::HOCKNEY) {
2217 throw IpplException(
"FFTOpenPoissonSolver::shiftedGreensFunction",
2218 "Shifted Green's function is only implemented for HOCKNEY.");
2220 if ((greensFunctionType == GreenFunction::INTEGRATED) && (Dim != 3)) {
2221 throw IpplException(
"FFTOpenPoissonSolver::shiftedGreensFunction",
2222 "Shifted integrated Green's function is only implemented for 3D.");
2233 mesh_mp = &(this->rhs_mp->get_mesh());
2234 for (
unsigned int i = 0; i < Dim; ++i) {
2235 hr_m[i] = mesh_mp->getMeshSpacing(i);
2237 mesh2_m->setMeshSpacing(hr_m);
2238 meshComplex_m->setMeshSpacing(hr_m);
2240 const scalar_type pi = Kokkos::numbers::pi_v<scalar_type>;
2242 typename Field_t::view_type view = grn_mr.getView();
2243 const int nghost = grn_mr.getNghost();
2244 const auto& ldom = layout2_m->getLocalNDIndex();
2247 Vector<int, Dim> nr = nr_m;
2249 Vector<double, Dim> shft = shift;
2252 if (greensFunctionType == GreenFunction::INTEGRATED) {
2254 "Shifted integrated Green's function", grn_mr.getFieldRangePolicy(),
2255 KOKKOS_LAMBDA(
const index_array_type& args) {
2256 Vector<int, Dim> igVec = args - nghost;
2257 for (
unsigned d = 0; d < Dim; ++d) {
2258 igVec[d] += ldom[d].first();
2261 scalar_type offset[Dim];
2262 for (
unsigned int d = 0; d < Dim; ++d) {
2263 const int igSigned = (igVec[d] < nr[d]) ? igVec[d] : igVec[d] - 2 * nr[d];
2264 offset[d] =
static_cast<scalar_type
>(igSigned) * hs[d]
2265 -
static_cast<scalar_type
>(shft[d]);
2268 const scalar_type avg =
2269 integratedGreenAverage(offset[0], offset[1], offset[2], hs);
2270 apply(view, args) = -avg / (scalar_type(4) * pi);
2274 scalar_type hmin2 = hs[0] * hs[0];
2275 for (
unsigned int d = 1; d < Dim; ++d) {
2276 hmin2 = (hs[d] * hs[d] < hmin2) ? (hs[d] * hs[d]) : hmin2;
2278 const scalar_type regThresh = 0.25 * hmin2;
2281 "Shifted Green's function", grn_mr.getFieldRangePolicy(),
2282 KOKKOS_LAMBDA(
const index_array_type& args) {
2284 Vector<int, Dim> igVec = args - nghost;
2285 for (
unsigned d = 0; d < Dim; ++d) {
2286 igVec[d] += ldom[d].first();
2294 for (
unsigned int d = 0; d < Dim; ++d) {
2295 const double ig_signed = (igVec[d] < nr[d])
2296 ?
static_cast<double>(igVec[d])
2297 :
static_cast<double>(igVec[d] - 2 * nr[d]);
2298 const double xoff = ig_signed * hs[d] - shft[d];
2305 const bool nearSing = (rsq < regThresh);
2306 const scalar_type r = Kokkos::sqrt(rsq + nearSing * regThresh);
2307 apply(view, args) = -1.0 / (4.0 * pi * r);
2312 static IpplTimings::TimerRef fftsg = IpplTimings::getTimer(
"FFT: Shifted Green");
2313 IpplTimings::startTimer(fftsg);
2315 fft_m->transform(FORWARD, grn_mr, grntr_m);
2317 IpplTimings::stopTimer(fftsg);
Definition IpplException.h:6
typename detail::ViewType< T, Dim, ViewArgs... >::view_type view_type
View type storing the data.
Definition BareField.h:50
Definition FFTOpenPoissonSolver.h:32
Definition FieldLayout.h:166
KOKKOS_INLINE_FUNCTION NDIndex< Dim > intersect(const NDIndex< Dim > &) const
Definition NDIndex.hpp:59
detail::meta_hess< Field > hess(Field &u)
Definition FieldOperations.hpp:106
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) apply(const View &view, const Coords &coords)
Definition IpplOperations.h:64
detail::meta_grad< Field > grad(Field &u)
Definition FieldOperations.hpp:12