24 typename SplineEvaluator::evaluation_domain_type,
25 typename SplineEvaluator::batched_evaluation_domain_type,
26 typename SplineEvaluator::memory_space,
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>);
38 typename SplineEvaluator::evaluation_domain_type,
39 typename SplineEvaluator::batched_evaluation_domain_type,
40 typename SplineEvaluator::memory_space,
41 Kokkos::layout_right>;
45 using GridPDEDim =
typename SplineBuilder::interpolation_discrete_dimension_type;
47 using InputBSplines =
typename SplineBuilder::bsplines_type;
49 using PDEDim =
typename GridPDEDim::continuous_dimension_type;
51 using CoordPDEDim = Coord<PDEDim>;
53 using IdxPDEDim = Idx<GridPDEDim>;
56 using field_type =
typename base_type::field_type;
58 using vector_field_type =
typename base_type::vector_field_type;
60 using fem_idx_range_type =
typename base_type::laplacian_idx_range_type;
62 using batch_idx_range_type =
typename base_type::batch_idx_range_type;
64 using batch_index_type =
typename base_type::batch_index_type;
66 using memory_space =
typename base_type::memory_space;
68 using exec_space =
typename SplineEvaluator::exec_space;
78 using IdxQ = Idx<GridPDEDimQ>;
81 using IdxStepQ = IdxStep<GridPDEDimQ>;
84 using IdxRangeQ = IdxRange<GridPDEDimQ>;
91 using DQFieldMem = DFieldMem<IdxRangeQ, memory_space>;
93 using DQConstField = DConstField<IdxRangeQ, memory_space>;
100 using CoordQField = Field<CoordPDEDim, IdxRangeQ, memory_space>;
114 using FEMBSplines = std::conditional_t<
115 ddc::is_uniform_bsplines_v<InputBSplines> && !InputBSplines::is_periodic(),
119 using FEMEvalExtrapolationRule = std::conditional_t<
120 FEMBSplines::is_periodic(),
121 ddc::PeriodicExtrapolationRule<PDEDim>,
122 ddc::NullExtrapolationRule>;
124 template <
class IdxRange>
125 struct FEMSplineEvaluatorBuilder;
127 template <
class... DimX>
128 struct FEMSplineEvaluatorBuilder<IdxRange<DimX...>>
130 using type = ddc::SplineEvaluator<
131 Kokkos::DefaultExecutionSpace,
132 Kokkos::DefaultExecutionSpace::memory_space,
135 FEMEvalExtrapolationRule,
136 FEMEvalExtrapolationRule,
140 using FEMSplineEvaluator =
typename FEMSplineEvaluatorBuilder<
141 typename SplineEvaluator::batched_evaluation_domain_type>::type;
143 using IdxFEMBSplines = Idx<FEMBSplines>;
145 using IdxRangeFEMBSplines = IdxRange<FEMBSplines>;
147 using IdxRangeBatchedFEMBSplines =
typename FEMSplineEvaluator::batched_spline_domain_type;
149 using FEMBSplinesCoeffMem = DFieldMem<IdxRangeFEMBSplines, memory_space>;
151 using BatchedFEMBSplinesCoeffMem = DFieldMem<IdxRangeBatchedFEMBSplines, memory_space>;
153 using BatchedFEMBSplinesCoeff =
typename BatchedFEMBSplinesCoeffMem::span_type;
156 using IdxRangeBSplines = IdxRange<InputBSplines>;
157 using IdxBSplines = Idx<InputBSplines>;
159 using IdxRangeBatchedBSplines =
typename SplineEvaluator::batched_spline_domain_type;
162 typename SplineEvaluator::batched_evaluation_domain_type::discrete_element_type;
164 using CoordFieldMem = FieldMem<
166 typename FEMSplineEvaluator::batched_evaluation_domain_type,
171 typename FEMSplineEvaluator::batched_evaluation_domain_type,
175 using RHSBSplines = InputBSplines;
177 using RHSSplineCoeffMem = DFieldMem<IdxRangeBatchedBSplines, memory_space>;
178 using RHSSplineCoeff =
typename RHSSplineCoeffMem::span_type;
180 using RHSQuadTags = ddc::
181 type_seq_merge_t<typename base_type::batch_tags, ddc::detail::TypeSeq<GridPDEDimQ>>;
183 using IdxRangeRHSQuadrature = ddc::detail::convert_type_seq_to_discrete_domain_t<RHSQuadTags>;
185 using IdxRHSQuadrature =
typename IdxRangeRHSQuadrature::discrete_element_type;
189 static int constexpr s_degree = InputBSplines::degree();
192 static int constexpr s_npts_gauss = InputBSplines::degree() + 1;
195 SplineBuilder
const& m_spline_builder;
197 SplineEvaluator m_spline_evaluator;
199 FEMSplineEvaluator m_spline_fem_evaluator;
203 DQFieldMem m_quad_coef;
205 std::unique_ptr<Matrix> m_fem_matrix;
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(
220 IdxStepQ(s_npts_gauss * ddc::discrete_space<InputBSplines>().ncells())))
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);
228 for (Idx<break_point_grid>
const idx : break_point_idx_range) {
229 break_points(idx) = ddc::coordinate(idx);
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(
239 get_field(quad_coef_host),
240 get_const_field(break_points));
241 ddc::parallel_deepcopy(m_quad_coef, quad_coef_host);
243 ddc::init_discrete_space<GridPDEDimQ>(eval_pts_data);
245 if constexpr (InputBSplines::is_periodic()) {
246 build_periodic_matrix();
248 build_non_periodic_matrix();
261 field_type
operator()(field_type phi, field_type rho)
const override
263 batch_idx_range_type idx_range_batch(get_idx_range(phi));
264 IdxRangeBatchedFEMBSplines phi_coefs_idx_range(
266 ddc::discrete_space<FEMBSplines>().full_domain());
267 BatchedFEMBSplinesCoeffMem phi_coefs_alloc(phi_coefs_idx_range);
268 BatchedFEMBSplinesCoeff phi_coefs(phi_coefs_alloc);
271 CoordFieldMem eval_pts_alloc(get_idx_range(phi));
272 CoordField eval_pts = get_field(eval_pts_alloc);
274 ddc::parallel_for_each(
276 get_idx_range(eval_pts),
277 KOKKOS_LAMBDA(full_index
const idx) {
278 eval_pts(idx) = ddc::coordinate(ddc::select<GridPDEDim>(idx));
281 FEMSplineEvaluator spline_nu_evaluator_proxy = m_spline_fem_evaluator;
283 m_spline_fem_evaluator(phi, get_const_field(eval_pts), get_const_field(phi_coefs));
300 field_type
operator()(field_type phi, vector_field_type E, field_type rho)
const override
302 batch_idx_range_type idx_range_batch(get_idx_range(phi));
303 IdxRangeBatchedFEMBSplines phi_coefs_idx_range(
305 ddc::discrete_space<FEMBSplines>().full_domain());
306 BatchedFEMBSplinesCoeffMem phi_coefs_alloc(phi_coefs_idx_range);
307 BatchedFEMBSplinesCoeff phi_coefs(phi_coefs_alloc);
310 CoordFieldMem eval_pts_alloc(get_idx_range(phi));
311 CoordField eval_pts = get_field(eval_pts_alloc);
313 ddc::parallel_for_each(
315 get_idx_range(eval_pts),
316 KOKKOS_LAMBDA(full_index
const idx) {
317 eval_pts(idx) = ddc::coordinate(ddc::select<GridPDEDim>(idx));
320 FEMSplineEvaluator spline_nu_evaluator_proxy = m_spline_fem_evaluator;
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));
325 ddc::parallel_for_each(
328 KOKKOS_LAMBDA(full_index
const idx) { E(idx) = -E(idx); });
333 void build_periodic_matrix()
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;
342 m_fem_matrix = Matrix::make_new_block_with_banded_region(
346 positive_definite_symmetric,
349 auto quad_coef_host = ddc::create_mirror_and_copy(get_const_field(m_quad_coef));
351 IdxRangeFEMBSplines spline_idx_range = ddc::discrete_space<FEMBSplines>().full_domain();
352 IdxFEMBSplines first_bspline_idx = spline_idx_range.front();
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);
368 a_jk += derivs[j] * derivs[k] * quad_coef_host(ix);
370 m_fem_matrix->set_element(j_idx, k_idx, a_jk);
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));
381 host_t<FEMBSplinesCoeffMem> int_vals(idx_range_bspline);
382 ddc::integrals(Kokkos::DefaultHostExecutionSpace(), get_field(int_vals));
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));
391 m_fem_matrix->factorize();
394 void build_non_periodic_matrix()
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;
405 m_fem_matrix = Matrix::make_new_banded(
409 positive_definite_symmetric);
411 auto quad_coef_host = ddc::create_mirror_and_copy(get_const_field(m_quad_coef));
413 IdxRangeFEMBSplines spline_idx_range = ddc::discrete_space<FEMBSplines>().full_domain();
414 IdxFEMBSplines first_bspline_idx = spline_idx_range.front();
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;
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);
433 a_jk += derivs[j] * derivs[k] * quad_coef_host(ix);
435 m_fem_matrix->set_element(j_idx, k_idx, a_jk);
442 m_fem_matrix->factorize();
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));
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);
468 IdxFEMBSplines last_basis_element(nbasis_proxy - 1);
470 ddc::parallel_fill(phi_spline_coef, 0.0);
473 BatchedFEMBSplinesCoeff rhs(phi_spline_coef);
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));
481 ddc::parallel_for_each(
484 KOKKOS_LAMBDA(IdxRHSQuadrature
const idx) {
485 batch_index_type ib(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(
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;
500 Kokkos::atomic_fetch_add(
502 rho_val * values[j] * quad_coef_proxy(iq));
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);
510 int constexpr n_implicit_min_bcs(!InputBSplines::is_periodic());
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();
520 m_fem_matrix->solve_inplace(phi_rhs_host);
523 ddc::parallel_deepcopy(phi_spline_coef, phi_spline_coef_host);
525 if constexpr (!InputBSplines::is_periodic()) {
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);
530 IdxFEMBSplines first_repeat_bspline(ddc::discrete_space<FEMBSplines>().nbasis());
533 ddc::parallel_for_each(
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));
549 static FEMSplineEvaluator jit_build_nubsplinesx(SplineEvaluator
const& spline_evaluator)
551 if constexpr (InputBSplines::is_periodic()) {
552 return spline_evaluator;
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());
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);
567 ddc::init_discrete_space<FEMBSplines>(break_points);
570 return FEMSplineEvaluator(ddc::NullExtrapolationRule(), ddc::NullExtrapolationRule());