SpECTRE Documentation Coverage Report
Current view: top level - Elliptic/BoundaryConditions - AnalyticSolution.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 11.1 % 27 3
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            Line data    Source code
       1            0 : // Distributed under the MIT License.
       2              : // See LICENSE.txt for details.
       3              : 
       4              : #pragma once
       5              : 
       6              : #include <cstddef>
       7              : #include <memory>
       8              : #include <ostream>
       9              : #include <pup.h>
      10              : #include <string>
      11              : #include <type_traits>
      12              : #include <utility>
      13              : #include <vector>
      14              : 
      15              : #include "DataStructures/DataBox/MetavariablesTag.hpp"
      16              : #include "DataStructures/TaggedTuple.hpp"
      17              : #include "DataStructures/Tensor/Tensor.hpp"
      18              : #include "Domain/Tags.hpp"
      19              : #include "Domain/Tags/FaceNormal.hpp"
      20              : #include "Elliptic/BoundaryConditions/BoundaryCondition.hpp"
      21              : #include "Elliptic/BoundaryConditions/BoundaryConditionType.hpp"
      22              : #include "Elliptic/BoundaryConditions/Tags.hpp"
      23              : #include "NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp"
      24              : #include "Options/String.hpp"
      25              : #include "PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp"
      26              : #include "Utilities/CallWithDynamicType.hpp"
      27              : #include "Utilities/ErrorHandling/Error.hpp"
      28              : #include "Utilities/Gsl.hpp"
      29              : #include "Utilities/Serialization/CharmPupable.hpp"
      30              : #include "Utilities/Serialization/Serialize.hpp"
      31              : #include "Utilities/TMPL.hpp"
      32              : 
      33            1 : namespace elliptic::BoundaryConditions {
      34              : namespace detail {
      35              : 
      36              : template <typename Solution, size_t Dim, typename Tag, typename = std::void_t<>>
      37              : struct has_boundary_variables : std::false_type {};
      38              : 
      39              : template <typename Solution, size_t Dim, typename Tag>
      40              : struct has_boundary_variables<
      41              :     Solution, Dim, Tag,
      42              :     std::void_t<decltype(std::declval<const Solution&>().variables(
      43              :         std::declval<const tnsr::I<DataVector, Dim, Frame::Inertial>&>(),
      44              :         tmpl::list<Tag>{}))>> : std::true_type {};
      45              : 
      46              : template <typename Solution, size_t Dim, typename Tag>
      47              : constexpr bool has_boundary_variables_v =
      48              :     has_boundary_variables<Solution, Dim, Tag>::value;
      49              : 
      50              : }  // namespace detail
      51              : 
      52              : /// \cond
      53              : template <typename System, size_t Dim = System::volume_dim,
      54              :           typename FieldTags = typename System::primal_fields,
      55              :           typename FluxTags = typename System::primal_fluxes>
      56              : struct AnalyticSolution;
      57              : /// \endcond
      58              : 
      59              : /*!
      60              :  * \brief Impose the analytic solution on the boundary.
      61              :  *
      62              :  * The user can select to impose the analytic solution as Dirichlet or
      63              :  * Neumann boundary conditions for each field separately.  Dirichlet
      64              :  * boundary conditions are imposed on the fields and Neumann boundary
      65              :  * conditions are imposed on the fluxes.
      66              :  */
      67              : template <typename System, size_t Dim, typename... FieldTags,
      68              :           typename... FluxTags>
      69            1 : class AnalyticSolution<System, Dim, tmpl::list<FieldTags...>,
      70              :                        tmpl::list<FluxTags...>>
      71              :     : public BoundaryCondition<Dim> {
      72              :  private:
      73            0 :   using Base = BoundaryCondition<Dim>;
      74              : 
      75              :  public:
      76            0 :   struct Solution {
      77            0 :     using type = std::unique_ptr<elliptic::analytic_data::InitialGuess>;
      78            0 :     static constexpr Options::String help = {
      79              :         "The analytic data to impose on the boundary"};
      80              :   };
      81              : 
      82            0 :   using options =
      83              :       tmpl::list<Solution,
      84              :                  elliptic::OptionTags::BoundaryConditionType<FieldTags>...>;
      85            0 :   static constexpr Options::String help =
      86              :       "Boundary conditions from the analytic solution";
      87              : 
      88            0 :   AnalyticSolution() = default;
      89            0 :   AnalyticSolution(const AnalyticSolution& rhs) : Base(rhs) { *this = rhs; }
      90            0 :   AnalyticSolution& operator=(const AnalyticSolution& rhs) {
      91              :     if (rhs.solution_ != nullptr) {
      92              :       solution_ = serialize_and_deserialize<
      93              :           std::unique_ptr<elliptic::analytic_data::InitialGuess>>(
      94              :           rhs.solution_);
      95              :     } else {
      96              :       solution_ = nullptr;
      97              :     }
      98              :     boundary_condition_types_ = rhs.boundary_condition_types_;
      99              :     return *this;
     100              :   }
     101            0 :   AnalyticSolution(AnalyticSolution&&) = default;
     102            0 :   AnalyticSolution& operator=(AnalyticSolution&&) = default;
     103            0 :   ~AnalyticSolution() = default;
     104              : 
     105              :   /// \cond
     106              :   explicit AnalyticSolution(CkMigrateMessage* m) : Base(m) {}
     107              :   using PUP::able::register_constructor;
     108              :   WRAPPED_PUPable_decl_template(AnalyticSolution);
     109              :   /// \endcond
     110              : 
     111              :   /// Select which `elliptic::BoundaryConditionType` to apply for each field
     112            1 :   explicit AnalyticSolution(
     113              :       std::unique_ptr<elliptic::analytic_data::InitialGuess> solution,
     114              :       // This pack expansion repeats the type `elliptic::BoundaryConditionType`
     115              :       // for each system field
     116              :       const typename elliptic::OptionTags::BoundaryConditionType<
     117              :           FieldTags>::type... boundary_condition_types)
     118              :       : solution_(std::move(solution)),
     119              :         boundary_condition_types_{boundary_condition_types...} {}
     120              : 
     121            0 :   std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()
     122              :       const override {
     123              :     return std::make_unique<AnalyticSolution>(*this);
     124              :   }
     125              : 
     126            0 :   std::vector<elliptic::BoundaryConditionType> boundary_condition_types()
     127              :       const override {
     128              :     std::vector<elliptic::BoundaryConditionType> result{};
     129              :     const auto collect = [&result](
     130              :                              const auto tag_v,
     131              :                              const elliptic::BoundaryConditionType bc_type) {
     132              :       using tag = std::decay_t<decltype(tag_v)>;
     133              :       for (size_t i = 0; i < tag::type::size(); ++i) {
     134              :         result.push_back(bc_type);
     135              :       }
     136              :     };
     137              :     EXPAND_PACK_LEFT_TO_RIGHT(collect(
     138              :         FieldTags{}, get<elliptic::Tags::BoundaryConditionType<FieldTags>>(
     139              :                          boundary_condition_types_)));
     140              :     return result;
     141              :   }
     142              : 
     143            0 :   using argument_tags =
     144              :       tmpl::list<Parallel::Tags::Metavariables,
     145              :                  domain::Tags::Coordinates<Dim, Frame::Inertial>,
     146              :                  ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<
     147              :                      Dim, Frame::Inertial>>>;
     148            0 :   using volume_tags = tmpl::list<Parallel::Tags::Metavariables>;
     149              : 
     150              :   template <typename Metavariables>
     151            0 :   void apply(const gsl::not_null<typename FieldTags::type*>... fields,
     152              :              const gsl::not_null<typename FieldTags::type*>... n_dot_fluxes,
     153              :              const TensorMetafunctions::prepend_spatial_index<
     154              :                  typename FieldTags::type, Dim, UpLo::Lo,
     155              :                  Frame::Inertial>&... /*deriv_fields*/,
     156              :              const Metavariables& /*meta*/,
     157              :              const tnsr::I<DataVector, Dim>& face_inertial_coords,
     158              :              const tnsr::i<DataVector, Dim>& face_normal) const {
     159              :     using factory_classes =
     160              :         typename Metavariables::factory_creation::factory_classes;
     161              :     call_with_dynamic_type<
     162              :         void, tmpl::at<factory_classes, elliptic::analytic_data::InitialGuess>>(
     163              :         solution_.get(), [this, &face_inertial_coords, &face_normal, &fields...,
     164              :                           &n_dot_fluxes...](const auto* const derived) {
     165              :           const auto impose_boundary_condition = [this, &face_inertial_coords,
     166              :                                                   &face_normal, derived](
     167              :                                                      auto field_tag_v,
     168              :                                                      auto flux_tag_v,
     169              :                                                      const auto field,
     170              :                                                      const auto n_dot_flux) {
     171              :             using field_tag = std::decay_t<decltype(field_tag_v)>;
     172              :             using flux_tag = std::decay_t<decltype(flux_tag_v)>;
     173              :             using derived_type = std::decay_t<decltype(*derived)>;
     174              :             switch (get<elliptic::Tags::BoundaryConditionType<field_tag>>(
     175              :                 boundary_condition_types_)) {
     176              :               case elliptic::BoundaryConditionType::Dirichlet: {
     177              :                 if constexpr (detail::has_boundary_variables_v<
     178              :                                   derived_type, Dim, field_tag>) {
     179              :                   const auto solution_vars = derived->variables(
     180              :                       face_inertial_coords, tmpl::list<field_tag>{});
     181              :                   *field = get<field_tag>(solution_vars);
     182              :                 } else {
     183              :                   ERROR(
     184              :                       "The analytic data does not provide the field required "
     185              :                       "for this Dirichlet boundary condition.");
     186              :                 }
     187              :                 break;
     188              :               }
     189              :               case elliptic::BoundaryConditionType::Neumann: {
     190              :                 if constexpr (detail::has_boundary_variables_v<derived_type,
     191              :                                                                Dim, flux_tag>) {
     192              :                   const auto solution_vars = derived->variables(
     193              :                       face_inertial_coords, tmpl::list<flux_tag>{});
     194              :                   normal_dot_flux(n_dot_flux, face_normal,
     195              :                                   get<flux_tag>(solution_vars));
     196              :                 } else {
     197              :                   ERROR(
     198              :                       "The analytic data does not provide the flux required "
     199              :                       "for this Neumann boundary condition.");
     200              :                 }
     201              :                 break;
     202              :               }
     203              :               default:
     204              :                 ERROR("Unsupported boundary condition type: "
     205              :                       << get<elliptic::Tags::BoundaryConditionType<field_tag>>(
     206              :                              boundary_condition_types_));
     207              :             }
     208              :           };
     209              :           EXPAND_PACK_LEFT_TO_RIGHT(impose_boundary_condition(
     210              :               FieldTags{}, FluxTags{}, fields, n_dot_fluxes));
     211              :         });
     212              :   }
     213              : 
     214            0 :   using argument_tags_linearized = tmpl::list<>;
     215            0 :   using volume_tags_linearized = tmpl::list<>;
     216              : 
     217            0 :   void apply_linearized(
     218              :       const gsl::not_null<typename FieldTags::type*>... fields,
     219              :       const gsl::not_null<typename FieldTags::type*>... n_dot_fluxes,
     220              :       const TensorMetafunctions::prepend_spatial_index<
     221              :           typename FieldTags::type, Dim, UpLo::Lo,
     222              :           Frame::Inertial>&... /*deriv_fields*/) const {
     223              :     const auto impose_boundary_condition =
     224              :         [this](auto field_tag_v, const auto field, const auto n_dot_flux) {
     225              :           using field_tag = decltype(field_tag_v);
     226              :           switch (get<elliptic::Tags::BoundaryConditionType<field_tag>>(
     227              :               boundary_condition_types_)) {
     228              :             case elliptic::BoundaryConditionType::Dirichlet:
     229              :               for (auto& field_component : *field) {
     230              :                 field_component = 0.;
     231              :               }
     232              :               break;
     233              :             case elliptic::BoundaryConditionType::Neumann:
     234              :               for (auto& n_dot_flux_component : *n_dot_flux) {
     235              :                 n_dot_flux_component = 0.;
     236              :               }
     237              :               break;
     238              :             default:
     239              :               ERROR("Unsupported boundary condition type: "
     240              :                     << get<elliptic::Tags::BoundaryConditionType<field_tag>>(
     241              :                            boundary_condition_types_));
     242              :           }
     243              :         };
     244              :     EXPAND_PACK_LEFT_TO_RIGHT(
     245              :         impose_boundary_condition(FieldTags{}, fields, n_dot_fluxes));
     246              :   }
     247              : 
     248              :   // NOLINTNEXTLINE
     249            0 :   void pup(PUP::er& p) override;
     250              : 
     251              :  private:
     252            0 :   std::unique_ptr<elliptic::analytic_data::InitialGuess> solution_{nullptr};
     253              :   tuples::TaggedTuple<elliptic::Tags::BoundaryConditionType<FieldTags>...>
     254            0 :       boundary_condition_types_{};
     255              : };
     256              : 
     257              : template <typename System, size_t Dim, typename... FieldTags,
     258              :           typename... FluxTags>
     259              : void AnalyticSolution<System, Dim, tmpl::list<FieldTags...>,
     260              :                       tmpl::list<FluxTags...>>::pup(PUP::er& p) {
     261              :   Base::pup(p);
     262              :   p | solution_;
     263              :   p | boundary_condition_types_;
     264              : }
     265              : 
     266              : /// \cond
     267              : template <typename System, size_t Dim, typename... FieldTags,
     268              :           typename... FluxTags>
     269              : PUP::able::PUP_ID AnalyticSolution<System, Dim, tmpl::list<FieldTags...>,
     270              :                                    tmpl::list<FluxTags...>>::my_PUP_ID =
     271              :     0;  // NOLINT
     272              : /// \endcond
     273              : 
     274              : }  // namespace elliptic::BoundaryConditions
        

Generated by: LCOV version 2.0-1