SpECTRE Documentation Coverage Report
Current view: top level - Elliptic/BoundaryConditions - AnalyticSolution.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 3 27 11.1 %
Date: 2025-12-05 05:03:31
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 <vector>
      12             : 
      13             : #include "DataStructures/DataBox/MetavariablesTag.hpp"
      14             : #include "DataStructures/Tensor/Tensor.hpp"
      15             : #include "Domain/Tags.hpp"
      16             : #include "Domain/Tags/FaceNormal.hpp"
      17             : #include "Elliptic/BoundaryConditions/BoundaryCondition.hpp"
      18             : #include "Elliptic/BoundaryConditions/BoundaryConditionType.hpp"
      19             : #include "Elliptic/BoundaryConditions/Tags.hpp"
      20             : #include "NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp"
      21             : #include "Options/String.hpp"
      22             : #include "PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp"
      23             : #include "Utilities/CallWithDynamicType.hpp"
      24             : #include "Utilities/ErrorHandling/Error.hpp"
      25             : #include "Utilities/Gsl.hpp"
      26             : #include "Utilities/Serialization/CharmPupable.hpp"
      27             : #include "Utilities/TMPL.hpp"
      28             : #include "Utilities/TaggedTuple.hpp"
      29             : 
      30           1 : namespace elliptic::BoundaryConditions {
      31             : 
      32             : /// \cond
      33             : template <typename System, size_t Dim = System::volume_dim,
      34             :           typename FieldTags = typename System::primal_fields,
      35             :           typename FluxTags = typename System::primal_fluxes>
      36             : struct AnalyticSolution;
      37             : /// \endcond
      38             : 
      39             : /*!
      40             :  * \brief Impose the analytic solution on the boundary.
      41             :  *
      42             :  * The user can select to impose the analytic solution as Dirichlet or
      43             :  * Neumann boundary conditions for each field separately.  Dirichlet
      44             :  * boundary conditions are imposed on the fields and Neumann boundary
      45             :  * conditions are imposed on the fluxes.
      46             :  */
      47             : template <typename System, size_t Dim, typename... FieldTags,
      48             :           typename... FluxTags>
      49           1 : class AnalyticSolution<System, Dim, tmpl::list<FieldTags...>,
      50             :                        tmpl::list<FluxTags...>>
      51             :     : public BoundaryCondition<Dim> {
      52             :  private:
      53           0 :   using Base = BoundaryCondition<Dim>;
      54             : 
      55             :  public:
      56           0 :   struct Solution {
      57           0 :     using type = std::unique_ptr<elliptic::analytic_data::AnalyticSolution>;
      58           0 :     static constexpr Options::String help = {
      59             :         "The analytic solution to impose on the boundary"};
      60             :   };
      61             : 
      62           0 :   using options =
      63             :       tmpl::list<Solution,
      64             :                  elliptic::OptionTags::BoundaryConditionType<FieldTags>...>;
      65           0 :   static constexpr Options::String help =
      66             :       "Boundary conditions from the analytic solution";
      67             : 
      68           0 :   AnalyticSolution() = default;
      69           0 :   AnalyticSolution(const AnalyticSolution& rhs) : Base(rhs) { *this = rhs; }
      70           0 :   AnalyticSolution& operator=(const AnalyticSolution& rhs) {
      71             :     if (rhs.solution_ != nullptr) {
      72             :       solution_ = rhs.solution_->get_clone();
      73             :     } else {
      74             :       solution_ = nullptr;
      75             :     }
      76             :     boundary_condition_types_ = rhs.boundary_condition_types_;
      77             :     return *this;
      78             :   }
      79           0 :   AnalyticSolution(AnalyticSolution&&) = default;
      80           0 :   AnalyticSolution& operator=(AnalyticSolution&&) = default;
      81           0 :   ~AnalyticSolution() = default;
      82             : 
      83             :   /// \cond
      84             :   explicit AnalyticSolution(CkMigrateMessage* m) : Base(m) {}
      85             :   using PUP::able::register_constructor;
      86             :   WRAPPED_PUPable_decl_template(AnalyticSolution);
      87             :   /// \endcond
      88             : 
      89             :   /// Select which `elliptic::BoundaryConditionType` to apply for each field
      90           1 :   explicit AnalyticSolution(
      91             :       std::unique_ptr<elliptic::analytic_data::AnalyticSolution> solution,
      92             :       // This pack expansion repeats the type `elliptic::BoundaryConditionType`
      93             :       // for each system field
      94             :       const typename elliptic::OptionTags::BoundaryConditionType<
      95             :           FieldTags>::type... boundary_condition_types)
      96             :       : solution_(std::move(solution)),
      97             :         boundary_condition_types_{boundary_condition_types...} {}
      98             : 
      99           0 :   std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()
     100             :       const override {
     101             :     return std::make_unique<AnalyticSolution>(*this);
     102             :   }
     103             : 
     104           0 :   std::vector<elliptic::BoundaryConditionType> boundary_condition_types()
     105             :       const override {
     106             :     std::vector<elliptic::BoundaryConditionType> result{};
     107             :     const auto collect = [&result](
     108             :                              const auto tag_v,
     109             :                              const elliptic::BoundaryConditionType bc_type) {
     110             :       using tag = std::decay_t<decltype(tag_v)>;
     111             :       for (size_t i = 0; i < tag::type::size(); ++i) {
     112             :         result.push_back(bc_type);
     113             :       }
     114             :     };
     115             :     EXPAND_PACK_LEFT_TO_RIGHT(collect(
     116             :         FieldTags{}, get<elliptic::Tags::BoundaryConditionType<FieldTags>>(
     117             :                          boundary_condition_types_)));
     118             :     return result;
     119             :   }
     120             : 
     121           0 :   using argument_tags =
     122             :       tmpl::list<Parallel::Tags::Metavariables,
     123             :                  domain::Tags::Coordinates<Dim, Frame::Inertial>,
     124             :                  ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<
     125             :                      Dim, Frame::Inertial>>>;
     126           0 :   using volume_tags = tmpl::list<Parallel::Tags::Metavariables>;
     127             : 
     128             :   template <typename Metavariables>
     129           0 :   void apply(const gsl::not_null<typename FieldTags::type*>... fields,
     130             :              const gsl::not_null<typename FieldTags::type*>... n_dot_fluxes,
     131             :              const TensorMetafunctions::prepend_spatial_index<
     132             :                  typename FieldTags::type, Dim, UpLo::Lo,
     133             :                  Frame::Inertial>&... /*deriv_fields*/,
     134             :              const Metavariables& /*meta*/,
     135             :              const tnsr::I<DataVector, Dim>& face_inertial_coords,
     136             :              const tnsr::i<DataVector, Dim>& face_normal) const {
     137             :     // Retrieve variables for both Dirichlet and Neumann conditions, then decide
     138             :     // which to impose. We could also retrieve either the field for the flux for
     139             :     // each field individually based on the selection, but that would incur the
     140             :     // overhead of calling into the analytic solution multiple times and
     141             :     // possibly computing temporary quantities multiple times. This performance
     142             :     // consideration is probably irrelevant because the boundary conditions are
     143             :     // only evaluated once at the beginning of the solve.
     144             :     using analytic_tags = tmpl::list<FieldTags..., FluxTags...>;
     145             :     using factory_classes =
     146             :         typename Metavariables::factory_creation::factory_classes;
     147             :     const auto solution_vars = call_with_dynamic_type<
     148             :         tuples::tagged_tuple_from_typelist<analytic_tags>,
     149             :         tmpl::at<factory_classes, elliptic::analytic_data::AnalyticSolution>>(
     150             :         solution_.get(), [&face_inertial_coords](const auto* const derived) {
     151             :           return derived->variables(face_inertial_coords, analytic_tags{});
     152             :         });
     153             :     const auto impose_boundary_condition = [this, &solution_vars, &face_normal](
     154             :                                                auto field_tag_v,
     155             :                                                auto flux_tag_v,
     156             :                                                const auto field,
     157             :                                                const auto n_dot_flux) {
     158             :       using field_tag = decltype(field_tag_v);
     159             :       using flux_tag = decltype(flux_tag_v);
     160             :       switch (get<elliptic::Tags::BoundaryConditionType<field_tag>>(
     161             :           boundary_condition_types_)) {
     162             :         case elliptic::BoundaryConditionType::Dirichlet:
     163             :           *field = get<field_tag>(solution_vars);
     164             :           break;
     165             :         case elliptic::BoundaryConditionType::Neumann:
     166             :           normal_dot_flux(n_dot_flux, face_normal,
     167             :                           get<flux_tag>(solution_vars));
     168             :           break;
     169             :         default:
     170             :           ERROR("Unsupported boundary condition type: "
     171             :                 << get<elliptic::Tags::BoundaryConditionType<field_tag>>(
     172             :                        boundary_condition_types_));
     173             :       }
     174             :     };
     175             :     EXPAND_PACK_LEFT_TO_RIGHT(impose_boundary_condition(FieldTags{}, FluxTags{},
     176             :                                                         fields, n_dot_fluxes));
     177             :   }
     178             : 
     179           0 :   using argument_tags_linearized = tmpl::list<>;
     180           0 :   using volume_tags_linearized = tmpl::list<>;
     181             : 
     182           0 :   void apply_linearized(
     183             :       const gsl::not_null<typename FieldTags::type*>... fields,
     184             :       const gsl::not_null<typename FieldTags::type*>... n_dot_fluxes,
     185             :       const TensorMetafunctions::prepend_spatial_index<
     186             :           typename FieldTags::type, Dim, UpLo::Lo,
     187             :           Frame::Inertial>&... /*deriv_fields*/) const {
     188             :     const auto impose_boundary_condition = [this](auto field_tag_v,
     189             :                                                   const auto field,
     190             :                                                   const auto n_dot_flux) {
     191             :       using field_tag = decltype(field_tag_v);
     192             :       switch (get<elliptic::Tags::BoundaryConditionType<field_tag>>(
     193             :           boundary_condition_types_)) {
     194             :         case elliptic::BoundaryConditionType::Dirichlet:
     195             :           for (auto& field_component : *field) {
     196             :             field_component = 0.;
     197             :           }
     198             :           break;
     199             :         case elliptic::BoundaryConditionType::Neumann:
     200             :           for (auto& n_dot_flux_component : *n_dot_flux) {
     201             :             n_dot_flux_component = 0.;
     202             :           }
     203             :           break;
     204             :         default:
     205             :           ERROR("Unsupported boundary condition type: "
     206             :                 << get<elliptic::Tags::BoundaryConditionType<field_tag>>(
     207             :                        boundary_condition_types_));
     208             :       }
     209             :     };
     210             :     EXPAND_PACK_LEFT_TO_RIGHT(
     211             :         impose_boundary_condition(FieldTags{}, fields, n_dot_fluxes));
     212             :   }
     213             : 
     214             :   // NOLINTNEXTLINE
     215           0 :   void pup(PUP::er& p) override;
     216             : 
     217             :  private:
     218           0 :   std::unique_ptr<elliptic::analytic_data::AnalyticSolution> solution_{nullptr};
     219             :   tuples::TaggedTuple<elliptic::Tags::BoundaryConditionType<FieldTags>...>
     220           0 :       boundary_condition_types_{};
     221             : };
     222             : 
     223             : template <typename System, size_t Dim, typename... FieldTags,
     224             :           typename... FluxTags>
     225             : void AnalyticSolution<System, Dim, tmpl::list<FieldTags...>,
     226             :                       tmpl::list<FluxTags...>>::pup(PUP::er& p) {
     227             :   Base::pup(p);
     228             :   p | solution_;
     229             :   p | boundary_condition_types_;
     230             : }
     231             : 
     232             : /// \cond
     233             : template <typename System, size_t Dim, typename... FieldTags,
     234             :           typename... FluxTags>
     235             : PUP::able::PUP_ID AnalyticSolution<System, Dim, tmpl::list<FieldTags...>,
     236             :                                    tmpl::list<FluxTags...>>::my_PUP_ID =
     237             :     0;  // NOLINT
     238             : /// \endcond
     239             : 
     240             : }  // namespace elliptic::BoundaryConditions

Generated by: LCOV version 1.14