SpECTRE Documentation Coverage Report
Current view: top level - Elliptic/BoundaryConditions - AnalyticSolution.hpp Hit Total Coverage
Commit: ecb8a275e1aebab77dcce48a5e098ed4e486ab4b Lines: 3 27 11.1 %
Date: 2026-08-22 01:05:40
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 1.14