Gyselalib++
 
Loading...
Searching...
No Matches
fem_1d_poisson_solver.hpp
1// SPDX-License-Identifier: MIT
2#pragma once
3#include <ddc/ddc.hpp>
4#include <ddc/kernels/splines.hpp>
5
6#include <sll/gauss_legendre_integration.hpp>
7#include <sll/matrix.hpp>
8
9#include "ddc_alias_inline_functions.hpp"
10#include "ddc_aliases.hpp"
11#include "ipoisson_solver.hpp"
12
13
21template <class SplineBuilder, class SplineEvaluator>
23 : public IPoissonSolver<
24 typename SplineEvaluator::evaluation_domain_type,
25 typename SplineEvaluator::batched_evaluation_domain_type,
26 typename SplineEvaluator::memory_space,
27 Kokkos::layout_right>
28{
29 static_assert(std::is_same_v<
30 typename SplineBuilder::batched_interpolation_domain_type,
31 typename SplineEvaluator::batched_evaluation_domain_type>);
32 static_assert(std::is_same_v<
33 typename SplineBuilder::interpolation_discrete_dimension_type,
34 typename SplineEvaluator::evaluation_discrete_dimension_type>);
35
36private:
38 typename SplineEvaluator::evaluation_domain_type,
39 typename SplineEvaluator::batched_evaluation_domain_type,
40 typename SplineEvaluator::memory_space,
41 Kokkos::layout_right>;
42
43private:
45 using GridPDEDim = typename SplineBuilder::interpolation_discrete_dimension_type;
46
47 using InputBSplines = typename SplineBuilder::bsplines_type;
48
49 using PDEDim = typename GridPDEDim::continuous_dimension_type;
50
51 using CoordPDEDim = Coord<PDEDim>;
52
53 using IdxPDEDim = Idx<GridPDEDim>;
54
55private:
56 using field_type = typename base_type::field_type;
57
58 using vector_field_type = typename base_type::vector_field_type;
59
60 using fem_idx_range_type = typename base_type::laplacian_idx_range_type;
61
62 using batch_idx_range_type = typename base_type::batch_idx_range_type;
63
64 using batch_index_type = typename base_type::batch_index_type;
65
66 using memory_space = typename base_type::memory_space;
67
68 using exec_space = typename SplineEvaluator::exec_space;
69
70public:
72 struct GridPDEDimQ : NonUniformGridBase<PDEDim>
73 {
74 };
75
76private:
78 using IdxQ = Idx<GridPDEDimQ>;
79
81 using IdxStepQ = IdxStep<GridPDEDimQ>;
82
84 using IdxRangeQ = IdxRange<GridPDEDimQ>;
85
91 using DQFieldMem = DFieldMem<IdxRangeQ, memory_space>;
93 using DQConstField = DConstField<IdxRangeQ, memory_space>;
94
100 using CoordQField = Field<CoordPDEDim, IdxRangeQ, memory_space>;
101
102public:
109 struct HiddenFEMBSplines : ddc::NonUniformBSplines<PDEDim, InputBSplines::degree()>
110 {
111 };
112
113private:
114 using FEMBSplines = std::conditional_t<
115 ddc::is_uniform_bsplines_v<InputBSplines> && !InputBSplines::is_periodic(),
117 InputBSplines>;
118
119 using FEMEvalExtrapolationRule = std::conditional_t<
120 FEMBSplines::is_periodic(),
121 ddc::PeriodicExtrapolationRule<PDEDim>,
122 ddc::NullExtrapolationRule>;
123
124 template <class IdxRange>
125 struct FEMSplineEvaluatorBuilder;
126
127 template <class... DimX>
128 struct FEMSplineEvaluatorBuilder<IdxRange<DimX...>>
129 {
130 using type = ddc::SplineEvaluator<
131 Kokkos::DefaultExecutionSpace,
132 Kokkos::DefaultExecutionSpace::memory_space,
133 FEMBSplines,
134 GridPDEDim,
135 FEMEvalExtrapolationRule,
136 FEMEvalExtrapolationRule,
137 DimX...>;
138 };
139
140 using FEMSplineEvaluator = typename FEMSplineEvaluatorBuilder<
141 typename SplineEvaluator::batched_evaluation_domain_type>::type;
142
143 using IdxFEMBSplines = Idx<FEMBSplines>;
144
145 using IdxRangeFEMBSplines = IdxRange<FEMBSplines>;
146
147 using IdxRangeBatchedFEMBSplines = typename FEMSplineEvaluator::batched_spline_domain_type;
148
149 using FEMBSplinesCoeffMem = DFieldMem<IdxRangeFEMBSplines, memory_space>;
150
151 using BatchedFEMBSplinesCoeffMem = DFieldMem<IdxRangeBatchedFEMBSplines, memory_space>;
152
153 using BatchedFEMBSplinesCoeff = typename BatchedFEMBSplinesCoeffMem::span_type;
154
155private:
156 using IdxRangeBSplines = IdxRange<InputBSplines>;
157 using IdxBSplines = Idx<InputBSplines>;
158
159 using IdxRangeBatchedBSplines = typename SplineEvaluator::batched_spline_domain_type;
160
161 using full_index =
162 typename SplineEvaluator::batched_evaluation_domain_type::discrete_element_type;
163
164 using CoordFieldMem = FieldMem<
165 CoordPDEDim,
166 typename FEMSplineEvaluator::batched_evaluation_domain_type,
167 memory_space>;
168
169 using CoordField
170 = Field<CoordPDEDim,
171 typename FEMSplineEvaluator::batched_evaluation_domain_type,
172 memory_space>;
173
174private:
175 using RHSBSplines = InputBSplines;
176
177 using RHSSplineCoeffMem = DFieldMem<IdxRangeBatchedBSplines, memory_space>;
178 using RHSSplineCoeff = typename RHSSplineCoeffMem::span_type;
179
180 using RHSQuadTags = ddc::
181 type_seq_merge_t<typename base_type::batch_tags, ddc::detail::TypeSeq<GridPDEDimQ>>;
182
183 using IdxRangeRHSQuadrature = ddc::detail::convert_type_seq_to_discrete_domain_t<RHSQuadTags>;
184
185 using IdxRHSQuadrature = typename IdxRangeRHSQuadrature::discrete_element_type;
186
187private:
188 // Spline degree in x direction
189 static int constexpr s_degree = InputBSplines::degree();
190
191 // Gauss points used for integration computation
192 static int constexpr s_npts_gauss = InputBSplines::degree() + 1;
193
194private:
195 SplineBuilder const& m_spline_builder;
196
197 SplineEvaluator m_spline_evaluator;
198
199 FEMSplineEvaluator m_spline_fem_evaluator;
200
201 int m_matrix_size;
202
203 DQFieldMem m_quad_coef;
204
205 std::unique_ptr<Matrix> m_fem_matrix;
206
207public:
214 FEM1DPoissonSolver(SplineBuilder const& spline_builder, SplineEvaluator const& spline_evaluator)
215 : m_spline_builder(spline_builder)
216 , m_spline_evaluator(spline_evaluator)
217 , m_spline_fem_evaluator(jit_build_nubsplinesx(spline_evaluator))
218 , m_quad_coef(IdxRangeQ(
219 IdxQ(0),
220 IdxStepQ(s_npts_gauss * ddc::discrete_space<InputBSplines>().ncells())))
221 {
222 using break_point_grid = ddc::knot_discrete_dimension_t<InputBSplines>;
223 IdxRange<break_point_grid> break_point_idx_range
224 = ddc::discrete_space<InputBSplines>().break_point_domain();
225 host_t<FieldMem<CoordPDEDim, IdxRange<break_point_grid>>> break_points(
226 break_point_idx_range);
227
228 for (Idx<break_point_grid> const idx : break_point_idx_range) {
229 break_points(idx) = ddc::coordinate(idx);
230 }
231
232 // Calculate the integration coefficients
233 GaussLegendre<PDEDim> const gl(s_npts_gauss);
234 std::vector<CoordPDEDim> eval_pts_data(get_idx_range(m_quad_coef).size());
235 host_t<CoordQField> const eval_pts(eval_pts_data.data(), get_idx_range(m_quad_coef));
236 auto quad_coef_host = ddc::create_mirror_and_copy(get_field(m_quad_coef));
237 gl.compute_points_and_weights_on_mesh(
238 eval_pts,
239 get_field(quad_coef_host),
240 get_const_field(break_points));
241 ddc::parallel_deepcopy(m_quad_coef, quad_coef_host);
242
243 ddc::init_discrete_space<GridPDEDimQ>(eval_pts_data);
244 // Build the finite elements matrix
245 if constexpr (InputBSplines::is_periodic()) {
246 build_periodic_matrix();
247 } else {
248 build_non_periodic_matrix();
249 }
250 }
251
261 field_type operator()(field_type phi, field_type rho) const override
262 {
263 batch_idx_range_type idx_range_batch(get_idx_range(phi));
264 IdxRangeBatchedFEMBSplines phi_coefs_idx_range(
265 idx_range_batch,
266 ddc::discrete_space<FEMBSplines>().full_domain());
267 BatchedFEMBSplinesCoeffMem phi_coefs_alloc(phi_coefs_idx_range);
268 BatchedFEMBSplinesCoeff phi_coefs(phi_coefs_alloc);
269 solve_matrix_system(phi_coefs, rho);
270
271 CoordFieldMem eval_pts_alloc(get_idx_range(phi));
272 CoordField eval_pts = get_field(eval_pts_alloc);
273
274 ddc::parallel_for_each(
275 exec_space(),
276 get_idx_range(eval_pts),
277 KOKKOS_LAMBDA(full_index const idx) {
278 eval_pts(idx) = ddc::coordinate(ddc::select<GridPDEDim>(idx));
279 });
280
281 FEMSplineEvaluator spline_nu_evaluator_proxy = m_spline_fem_evaluator;
282
283 m_spline_fem_evaluator(phi, get_const_field(eval_pts), get_const_field(phi_coefs));
284
285 return phi;
286 }
287
300 field_type operator()(field_type phi, vector_field_type E, field_type rho) const override
301 {
302 batch_idx_range_type idx_range_batch(get_idx_range(phi));
303 IdxRangeBatchedFEMBSplines phi_coefs_idx_range(
304 idx_range_batch,
305 ddc::discrete_space<FEMBSplines>().full_domain());
306 BatchedFEMBSplinesCoeffMem phi_coefs_alloc(phi_coefs_idx_range);
307 BatchedFEMBSplinesCoeff phi_coefs(phi_coefs_alloc);
308 solve_matrix_system(phi_coefs, rho);
309
310 CoordFieldMem eval_pts_alloc(get_idx_range(phi));
311 CoordField eval_pts = get_field(eval_pts_alloc);
312
313 ddc::parallel_for_each(
314 exec_space(),
315 get_idx_range(eval_pts),
316 KOKKOS_LAMBDA(full_index const idx) {
317 eval_pts(idx) = ddc::coordinate(ddc::select<GridPDEDim>(idx));
318 });
319
320 FEMSplineEvaluator spline_nu_evaluator_proxy = m_spline_fem_evaluator;
321
322 m_spline_fem_evaluator(phi, get_const_field(eval_pts), get_const_field(phi_coefs));
323 m_spline_fem_evaluator.deriv(E, get_const_field(eval_pts), get_const_field(phi_coefs));
324
325 ddc::parallel_for_each(
326 exec_space(),
327 get_idx_range(phi),
328 KOKKOS_LAMBDA(full_index const idx) { E(idx) = -E(idx); });
329 return phi;
330 }
331
332private:
333 void build_periodic_matrix()
334 {
335 int constexpr n_lower_diags = s_degree + 1;
336 int const nbasis = ddc::discrete_space<FEMBSplines>().nbasis();
337 m_matrix_size = nbasis + 1;
338 bool const positive_definite_symmetric = false;
339
340 // Matrix with block is used instead of periodic to contain the
341 // Dirichlet boundary conditions
342 m_fem_matrix = Matrix::make_new_block_with_banded_region(
343 m_matrix_size,
344 n_lower_diags,
345 n_lower_diags,
346 positive_definite_symmetric,
347 n_lower_diags);
348
349 auto quad_coef_host = ddc::create_mirror_and_copy(get_const_field(m_quad_coef));
350
351 IdxRangeFEMBSplines spline_idx_range = ddc::discrete_space<FEMBSplines>().full_domain();
352 IdxFEMBSplines first_bspline_idx = spline_idx_range.front();
353
354 // Fill the banded part of the matrix
355 std::array<double, s_degree + 1> derivs_alloc;
356 DSpan1D derivs = as_span(derivs_alloc);
357 ddc::for_each(get_idx_range(m_quad_coef), [&](IdxQ const ix) {
358 CoordPDEDim const coord = ddc::coordinate(ix);
359 IdxFEMBSplines const jmin_idx
360 = ddc::discrete_space<FEMBSplines>().eval_deriv(derivs, coord);
361 std::size_t j_min = (jmin_idx - first_bspline_idx).value();
362 for (int j = 0; j < s_degree + 1; ++j) {
363 for (int k = 0; k < s_degree + 1; ++k) {
364 int const j_idx = (j + j_min) % nbasis;
365 int const k_idx = (k + j_min) % nbasis;
366 double a_jk = m_fem_matrix->get_element(j_idx, k_idx);
367 // Update element
368 a_jk += derivs[j] * derivs[k] * quad_coef_host(ix);
369
370 m_fem_matrix->set_element(j_idx, k_idx, a_jk);
371 }
372 }
373 });
374
375 // Impose the boundary conditions
376 IdxRangeFEMBSplines const bspline_full_idx_range
377 = ddc::discrete_space<FEMBSplines>().full_domain();
378 IdxRangeFEMBSplines const idx_range_bspline
379 = bspline_full_idx_range.take_first(IdxStep<FEMBSplines>(nbasis));
380
381 host_t<FEMBSplinesCoeffMem> int_vals(idx_range_bspline);
382 ddc::integrals(Kokkos::DefaultHostExecutionSpace(), get_field(int_vals));
383
384 for (IdxFEMBSplines const ix : idx_range_bspline) {
385 int const i = (ix - first_bspline_idx).value();
386 m_fem_matrix->set_element(nbasis, i, int_vals(ix));
387 m_fem_matrix->set_element(i, nbasis, int_vals(ix));
388 }
389
390 // Factorize the matrix ready to call solve
391 m_fem_matrix->factorize();
392 }
393
394 void build_non_periodic_matrix()
395 {
396 // Matrix contains all elements of the basis except the first
397 // and last in order to impose Dirichlet conditions
398 int const nbasis = ddc::discrete_space<FEMBSplines>().nbasis();
399 m_matrix_size = nbasis - 2;
400 int constexpr n_lower_diags = s_degree;
401 bool const positive_definite_symmetric = false;
402
403 // Matrix with block is used instead of periodic to contain the
404 // Dirichlet boundary conditions
405 m_fem_matrix = Matrix::make_new_banded(
406 m_matrix_size,
407 n_lower_diags,
408 n_lower_diags,
409 positive_definite_symmetric);
410
411 auto quad_coef_host = ddc::create_mirror_and_copy(get_const_field(m_quad_coef));
412
413 IdxRangeFEMBSplines spline_idx_range = ddc::discrete_space<FEMBSplines>().full_domain();
414 IdxFEMBSplines first_bspline_idx = spline_idx_range.front();
415
416 // Fill the banded part of the matrix
417 std::array<double, s_degree + 1> derivs_alloc;
418 DSpan1D derivs = as_span(derivs_alloc);
419 ddc::for_each(get_idx_range(m_quad_coef), [&](IdxQ const ix) {
420 CoordPDEDim const coord = ddc::coordinate(ix);
421 IdxFEMBSplines const jmin_idx
422 = ddc::discrete_space<FEMBSplines>().eval_deriv(derivs, coord);
423 std::size_t j_min = (jmin_idx - first_bspline_idx).value();
424 for (int j = 0; j < s_degree + 1; ++j) {
425 for (int k = 0; k < s_degree + 1; ++k) {
426 int const j_idx = (j + j_min) % nbasis - 1;
427 int const k_idx = (k + j_min) % nbasis - 1;
428
429 if (j_idx != -1 && j_idx != m_matrix_size && k_idx != -1
430 && k_idx != m_matrix_size) {
431 double a_jk = m_fem_matrix->get_element(j_idx, k_idx);
432 // Update element
433 a_jk += derivs[j] * derivs[k] * quad_coef_host(ix);
434
435 m_fem_matrix->set_element(j_idx, k_idx, a_jk);
436 }
437 }
438 }
439 });
440
441 // Factorize the matrix ready to call solve
442 m_fem_matrix->factorize();
443 }
444
445
446public:
453 void solve_matrix_system(BatchedFEMBSplinesCoeff phi_spline_coef, field_type rho) const
454 {
455 // Calculate the spline representation of the RHS.
456 IdxRangeBatchedBSplines rho_spline_coef_idx_range(
457 batch_idx_range_type(get_idx_range(rho)),
458 get_spline_idx_range(m_spline_builder));
459 RHSSplineCoeffMem rho_spline_coef_alloc(rho_spline_coef_idx_range);
460 RHSSplineCoeff rho_spline_coef(rho_spline_coef_alloc);
461 m_spline_builder(rho_spline_coef, get_const_field(rho));
462
463 IdxRange<FEMBSplines> fem_idx_range = ddc::discrete_space<FEMBSplines>().full_domain();
464 int const nbasis_proxy = ddc::discrete_space<FEMBSplines>().nbasis();
465 SplineEvaluator spline_evaluator_proxy = m_spline_evaluator;
466 DQConstField quad_coef_proxy = get_const_field(m_quad_coef);
467
468 IdxFEMBSplines last_basis_element(nbasis_proxy - 1);
469
470 ddc::parallel_fill(phi_spline_coef, 0.0);
471
472 // Create the rhs as an alias for phi_spline_coef as the matrix equation is solved in place.
473 BatchedFEMBSplinesCoeff rhs(phi_spline_coef);
474
475 batch_idx_range_type batch_idx_range(get_idx_range(rho));
476 IdxRangeRHSQuadrature rhs_build_idx_range(batch_idx_range, get_idx_range(m_quad_coef));
477
478 // Fill phi_rhs(i) with \int rho(x) b_i(x) dx
479 // Rk: phi_rhs no longer contains spline coefficients, but is the
480 // RHS of the matrix equation
481 ddc::parallel_for_each(
482 exec_space(),
483 rhs_build_idx_range,
484 KOKKOS_LAMBDA(IdxRHSQuadrature const idx) {
485 batch_index_type ib(idx);
486 IdxQ iq(idx);
487 CoordPDEDim const coord = ddc::coordinate(iq);
488 std::array<double, s_degree + 1> values_alloc;
489 DSpan1D values = as_span(values_alloc);
490 IdxFEMBSplines const jmin
491 = ddc::discrete_space<FEMBSplines>().eval_basis(values, coord);
492 double const rho_val = spline_evaluator_proxy(
493 coord,
494 DConstField<IdxRangeBSplines>(rho_spline_coef[ib]));
495 for (int j = 0; j < s_degree + 1; ++j) {
496 IdxFEMBSplines j_idx = jmin + j;
497 while (j_idx > last_basis_element) {
498 j_idx -= nbasis_proxy;
499 }
500 Kokkos::atomic_fetch_add(
501 &rhs(ib, j_idx),
502 rho_val * values[j] * quad_coef_proxy(iq));
503 }
504 });
505
506 auto phi_spline_coef_host_alloc = ddc::create_mirror_and_copy(phi_spline_coef);
507 host_t<BatchedFEMBSplinesCoeff> phi_spline_coef_host
508 = get_field(phi_spline_coef_host_alloc);
509
510 int constexpr n_implicit_min_bcs(!InputBSplines::is_periodic());
511
512 ddc::for_each(batch_idx_range, [&](batch_index_type ib) {
513 IdxRangeFEMBSplines solve_idx_range(
514 fem_idx_range.front() + n_implicit_min_bcs,
515 IdxStep<FEMBSplines>(m_matrix_size));
516 DSpan1D const phi_rhs_host
517 = phi_spline_coef_host[ib][solve_idx_range].allocation_mdspan();
518
519 // Solve the matrix equation to find the spline coefficients of phi
520 m_fem_matrix->solve_inplace(phi_rhs_host);
521 });
522
523 ddc::parallel_deepcopy(phi_spline_coef, phi_spline_coef_host);
524
525 if constexpr (!InputBSplines::is_periodic()) {
526 // Apply Dirichlet BCs
527 ddc::parallel_fill(phi_spline_coef[fem_idx_range.front()], 0.0);
528 ddc::parallel_fill(phi_spline_coef[fem_idx_range.back()], 0.0);
529 } else {
530 IdxFEMBSplines first_repeat_bspline(ddc::discrete_space<FEMBSplines>().nbasis());
531 // Copy the first d coefficients into the last d coefficients
532 // These coefficients refer to the same InputBSplines which cross the boundaries
533 ddc::parallel_for_each(
534 exec_space(),
535 batch_idx_range,
536 KOKKOS_LAMBDA(batch_index_type ib) {
537 for (int i = 0; i < s_degree; i++) {
538 phi_spline_coef(ib, first_repeat_bspline + i)
539 = phi_spline_coef(ib, IdxFEMBSplines(i));
540 }
541 });
542 }
543 }
544
545private:
546 //===========================================================================
547 // Create a non-uniform spline evaluator from the provided evaluator
548 //===========================================================================
549 static FEMSplineEvaluator jit_build_nubsplinesx(SplineEvaluator const& spline_evaluator)
550 {
551 if constexpr (InputBSplines::is_periodic()) {
552 return spline_evaluator;
553 } else {
554 static_assert(
555 (ddc::is_uniform_bsplines_v<typename SplineEvaluator::bsplines_type>)
556 || ddc::is_non_uniform_bsplines_v<typename SplineEvaluator::bsplines_type>);
557 if constexpr (ddc::is_uniform_bsplines_v<typename SplineEvaluator::bsplines_type>) {
558 using break_point_grid = ddc::knot_discrete_dimension_t<InputBSplines>;
559 IdxRange<break_point_grid> break_point_idx_range
560 = ddc::discrete_space<InputBSplines>().break_point_domain();
561 std::vector<CoordPDEDim> break_points(break_point_idx_range.size());
562
563 for (Idx<break_point_grid> const idx : break_point_idx_range) {
564 break_points[(idx - break_point_idx_range.front()).value()]
565 = ddc::coordinate(idx);
566 }
567 ddc::init_discrete_space<FEMBSplines>(break_points);
568 }
569 // Boundary values are never evaluated
570 return FEMSplineEvaluator(ddc::NullExtrapolationRule(), ddc::NullExtrapolationRule());
571 }
572 }
573};
A class to solve the following equation: using a Finite Element Method.
Definition fem_1d_poisson_solver.hpp:28
field_type operator()(field_type phi, vector_field_type E, field_type rho) const override
An operator which calculates the solution to Poisson's equation and its derivative: .
Definition fem_1d_poisson_solver.hpp:300
FEM1DPoissonSolver(SplineBuilder const &spline_builder, SplineEvaluator const &spline_evaluator)
Construct the FemQNSolver operator.
Definition fem_1d_poisson_solver.hpp:214
field_type operator()(field_type phi, field_type rho) const override
An operator which calculates the solution to Poisson's equation: .
Definition fem_1d_poisson_solver.hpp:261
void solve_matrix_system(BatchedFEMBSplinesCoeff phi_spline_coef, field_type rho) const
[SHOULD BE PRIVATE (Kokkos limitation)]
Definition fem_1d_poisson_solver.hpp:453
Definition gauss_legendre_integration.hpp:27
Definition ipoisson_solver.hpp:10
The grid of quadrature points along the PDEDim direction.
Definition fem_1d_poisson_solver.hpp:73
The internal type of the FEM basis.
Definition fem_1d_poisson_solver.hpp:110