SpECTRE Documentation Coverage Report
Current view: top level - ParallelAlgorithms/Interpolation/Events - InterpolateWithoutInterpComponent.hpp Hit Total Coverage
Commit: 9ddc33268b29014a4956c8f0c24ca90b397463e1 Lines: 2 14 14.3 %
Date: 2024-04-26 20:00:04
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 <array>
       7             : #include <cstddef>
       8             : #include <limits>
       9             : #include <optional>
      10             : #include <pup.h>
      11             : #include <set>
      12             : #include <utility>
      13             : #include <vector>
      14             : 
      15             : #include "DataStructures/DataBox/DataBox.hpp"
      16             : #include "DataStructures/DataVector.hpp"
      17             : #include "DataStructures/IdPair.hpp"
      18             : #include "DataStructures/Index.hpp"
      19             : #include "Domain/Creators/Tags/Domain.hpp"
      20             : #include "Domain/Domain.hpp"
      21             : #include "Domain/ElementLogicalCoordinates.hpp"
      22             : #include "Domain/FunctionsOfTime/FunctionOfTime.hpp"
      23             : #include "Domain/Structure/BlockId.hpp"
      24             : #include "Domain/Structure/ElementId.hpp"
      25             : #include "Domain/Tags.hpp"
      26             : #include "NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp"
      27             : #include "Options/String.hpp"
      28             : #include "Parallel/GlobalCache.hpp"
      29             : #include "Parallel/Invoke.hpp"
      30             : #include "Parallel/ParallelComponentHelpers.hpp"
      31             : #include "ParallelAlgorithms/EventsAndTriggers/Event.hpp"
      32             : #include "ParallelAlgorithms/Interpolation/Actions/InterpolationTargetVarsFromElement.hpp"
      33             : #include "ParallelAlgorithms/Interpolation/Events/GetComputeItemsOnSource.hpp"
      34             : #include "ParallelAlgorithms/Interpolation/PointInfoTag.hpp"
      35             : #include "ParallelAlgorithms/Interpolation/Targets/Sphere.hpp"
      36             : #include "PointwiseFunctions/GeneralRelativity/Tags.hpp"
      37             : #include "Utilities/Algorithm.hpp"
      38             : #include "Utilities/CartesianProduct.hpp"
      39             : #include "Utilities/ConstantExpressions.hpp"
      40             : #include "Utilities/PrettyType.hpp"
      41             : #include "Utilities/Serialization/CharmPupable.hpp"
      42             : #include "Utilities/TMPL.hpp"
      43             : #include "Utilities/TypeTraits/CreateGetStaticMemberVariableOrDefault.hpp"
      44             : #include "Utilities/TypeTraits/IsA.hpp"
      45             : 
      46             : /// \cond
      47             : namespace Events::Tags {
      48             : template <size_t Dim>
      49             : struct ObserverMesh;
      50             : template <size_t Dim, typename Fr>
      51             : struct ObserverCoordinates;
      52             : }  // namespace Events::Tags
      53             : namespace Tags {
      54             : template <typename TagsList>
      55             : struct Variables;
      56             : }  // namespace Tags
      57             : template <size_t VolumeDim>
      58             : class ElementId;
      59             : namespace intrp {
      60             : template <typename Metavariables, typename Tag>
      61             : struct InterpolationTarget;
      62             : }  // namespace intrp
      63             : /// \endcond
      64             : 
      65             : namespace intrp::Events {
      66             : /// \cond
      67             : template <size_t VolumeDim, typename InterpolationTargetTag,
      68             :           typename SourceVarTags>
      69             : class InterpolateWithoutInterpComponent;
      70             : /// \endcond
      71             : 
      72             : /*!
      73             :  * \brief Does an interpolation onto an InterpolationTargetTag by calling
      74             :  * Actions on the InterpolationTarget component.
      75             :  *
      76             :  * \note The `intrp::TargetPoints::Sphere` target is handled specially because
      77             :  * it has the potential to be very slow due to it usually having the most points
      78             :  * out of all the stationary targets. An optimization for the future would be to
      79             :  * have each target be responsible for intelligently computing the
      80             :  * `block_logical_coordinates` for it's own points.
      81             :  */
      82             : template <size_t VolumeDim, typename InterpolationTargetTag,
      83             :           typename... SourceVarTags>
      84           1 : class InterpolateWithoutInterpComponent<VolumeDim, InterpolationTargetTag,
      85             :                                         tmpl::list<SourceVarTags...>>
      86             :     : public Event {
      87             :  private:
      88           0 :   using frame = typename InterpolationTargetTag::compute_target_points::frame;
      89             : 
      90             :  public:
      91             :   /// \cond
      92             :   explicit InterpolateWithoutInterpComponent(CkMigrateMessage* /*unused*/) {}
      93             :   using PUP::able::register_constructor;
      94             :   WRAPPED_PUPable_decl_template(InterpolateWithoutInterpComponent);  // NOLINT
      95             :   /// \endcond
      96             : 
      97           0 :   using options = tmpl::list<>;
      98           0 :   static constexpr Options::String help =
      99             :       "Does interpolation using the given InterpolationTargetTag, "
     100             :       "without an Interpolator ParallelComponent.";
     101             : 
     102           0 :   static std::string name() {
     103             :     return pretty_type::name<InterpolationTargetTag>();
     104             :   }
     105             : 
     106           0 :   InterpolateWithoutInterpComponent() = default;
     107             : 
     108           0 :   using compute_tags_for_observation_box =
     109             :       detail::get_compute_items_on_source_or_default_t<InterpolationTargetTag,
     110             :                                                        tmpl::list<>>;
     111             : 
     112           0 :   using return_tags = tmpl::list<>;
     113           0 :   using argument_tags =
     114             :       tmpl::list<typename InterpolationTargetTag::temporal_id,
     115             :                  Tags::InterpPointInfoBase,
     116             :                  ::Events::Tags::ObserverMesh<VolumeDim>,
     117             :                  // We always grab the DG coords because we use them to create a
     118             :                  // bounding box for the sphere target optimization. DG coords
     119             :                  // have points on the boundary, while FD coords don't. If we
     120             :                  // had used FD coords, it's possible a target point would fall
     121             :                  // between the outermost gridpoints of two elements. This point
     122             :                  // would be outside our bounding box, and thus wouldn't get
     123             :                  // interpolated to. We avoid this by always using DG coords,
     124             :                  // even if the mesh is FD.
     125             :                  domain::Tags::Coordinates<VolumeDim, frame>, SourceVarTags...>;
     126             : 
     127             :   template <typename ParallelComponent, typename Metavariables>
     128           0 :   void operator()(
     129             :       const typename InterpolationTargetTag::temporal_id::type& temporal_id,
     130             :       const typename Tags::InterpPointInfo<Metavariables>::type& point_infos,
     131             :       const Mesh<VolumeDim>& mesh,
     132             :       const tnsr::I<DataVector, VolumeDim, frame> coordinates,
     133             :       const typename SourceVarTags::type&... source_vars_input,
     134             :       Parallel::GlobalCache<Metavariables>& cache,
     135             :       const ElementId<VolumeDim>& array_index,
     136             :       const ParallelComponent* const /*meta*/,
     137             :       const ObservationValue& /*observation_value*/) const {
     138             :     const tnsr::I<DataVector, VolumeDim, frame>& all_target_points =
     139             :         get<Vars::PointInfoTag<InterpolationTargetTag, VolumeDim>>(point_infos);
     140             :     std::vector<std::optional<IdPair<
     141             :         domain::BlockId, tnsr::I<double, VolumeDim, ::Frame::BlockLogical>>>>
     142             :         block_logical_coords{};
     143             : 
     144             :     // The sphere target is special because we have a better idea of where the
     145             :     // points will be.
     146             :     if constexpr (tt::is_a_v<
     147             :                       TargetPoints::Sphere,
     148             :                       typename InterpolationTargetTag::compute_target_points>) {
     149             :       static_assert(VolumeDim == 3,
     150             :                     "Sphere target can only be used for VolumeDim = 3.");
     151             :       // Extremum for x, y, z, r
     152             :       std::array<std::pair<double, double>, 4> min_max_coordinates{};
     153             : 
     154             :       const auto& sphere =
     155             :           Parallel::get<Tags::Sphere<InterpolationTargetTag>>(cache);
     156             :       const std::array<double, 3>& center = sphere.center;
     157             : 
     158             :       // Calculate r^2 from center of sphere because sqrt is expensive
     159             :       DataVector radii_squared{get<0>(coordinates).size(), 0.0};
     160             :       for (size_t i = 0; i < VolumeDim; i++) {
     161             :         radii_squared += square(coordinates.get(i) - gsl::at(center, i));
     162             :       }
     163             : 
     164             :       // Compute min and max
     165             :       {
     166             :         const auto [min, max] = alg::minmax_element(radii_squared);
     167             :         min_max_coordinates[3].first = *min;
     168             :         min_max_coordinates[3].second = *max;
     169             :       }
     170             : 
     171             :       const std::set<double>& radii_of_sphere_target = sphere.radii;
     172             :       const size_t l_max = sphere.l_max;
     173             : 
     174             :       const size_t number_of_angular_points = (l_max + 1) * (2 * l_max + 1);
     175             :       // first size_t = position of first radius in bounds
     176             :       // second size_t = total bounds to use/check
     177             :       std::optional<std::pair<size_t, size_t>> offset_and_num_points{};
     178             : 
     179             :       // Have a very small buffer just in case of roundoff
     180             :       double epsilon =
     181             :           (min_max_coordinates[3].second - min_max_coordinates[3].first) *
     182             :           std::numeric_limits<double>::epsilon() * 100.0;
     183             :       size_t offset_index = 0;
     184             :       // Check if any radii of the target are within the radii of our element
     185             :       for (double radius : radii_of_sphere_target) {
     186             :         const double square_radius = square(radius);
     187             :         if (square_radius >=
     188             :                 (gsl::at(min_max_coordinates, 3).first - epsilon) and
     189             :             square_radius <=
     190             :                 (gsl::at(min_max_coordinates, 3).second + epsilon)) {
     191             :           if (offset_and_num_points.has_value()) {
     192             :             offset_and_num_points->second += number_of_angular_points;
     193             :           } else {
     194             :             offset_and_num_points =
     195             :                 std::make_pair(offset_index * number_of_angular_points,
     196             :                                number_of_angular_points);
     197             :           }
     198             :         }
     199             :         offset_index++;
     200             :       }
     201             : 
     202             :       // If no radii pass through this element, there's nothing to do so return
     203             :       if (not offset_and_num_points.has_value()) {
     204             :         return;
     205             :       }
     206             : 
     207             :       // Get the x,y,z bounds
     208             :       for (size_t i = 0; i < VolumeDim; i++) {
     209             :         const auto [min, max] = alg::minmax_element(coordinates.get(i));
     210             :         gsl::at(min_max_coordinates, i).first = *min;
     211             :         gsl::at(min_max_coordinates, i).second = *max;
     212             :       }
     213             : 
     214             :       const tnsr::I<DataVector, VolumeDim, frame> target_points_to_check{};
     215             :       // Use the offset and number of points to create a view. We assume that if
     216             :       // there are multiple radii in this element, that they are successive
     217             :       // radii. If this wasn't true, that'd be a really weird topology.
     218             :       for (size_t i = 0; i < VolumeDim; i++) {
     219             :         make_const_view(make_not_null(&target_points_to_check.get(i)),
     220             :                         all_target_points.get(i), offset_and_num_points->first,
     221             :                         offset_and_num_points->second);
     222             :       }
     223             : 
     224             :       // To break out of inner loop and skip the point
     225             :       bool skip_point = false;
     226             :       tnsr::I<double, VolumeDim, frame> sphere_coords_to_map{};
     227             :       // Other code below expects block_logical_coords to be sized to the total
     228             :       // number of points on the target (including all radii). By default these
     229             :       // will all be nullopt and we'll only fill the ones we need
     230             :       block_logical_coords.resize(get<0>(all_target_points).size());
     231             : 
     232             :       const Block<VolumeDim>& block =
     233             :           Parallel::get<domain::Tags::Domain<VolumeDim>>(cache)
     234             :               .blocks()[array_index.block_id()];
     235             : 
     236             :       // Now for every radius in this element, we check if their points are
     237             :       // within the x,y,z bounds of the element. If they are, map the point to
     238             :       // the block logical frame and add it to the vector f all block logical
     239             :       // coordinates.
     240             :       for (size_t index = 0; index < get<0>(target_points_to_check).size();
     241             :            index++) {
     242             :         skip_point = false;
     243             :         for (size_t i = 0; i < VolumeDim; i++) {
     244             :           const double coord = target_points_to_check.get(i)[index];
     245             :           epsilon = (gsl::at(min_max_coordinates, i).second -
     246             :                      gsl::at(min_max_coordinates, i).first) *
     247             :                     std::numeric_limits<double>::epsilon() * 100.0;
     248             :           // If a point is outside any of the bounding box, skip it
     249             :           if (coord < (gsl::at(min_max_coordinates, i).first - epsilon) or
     250             :               coord > (gsl::at(min_max_coordinates, i).second + epsilon)) {
     251             :             skip_point = true;
     252             :             break;
     253             :           }
     254             : 
     255             :           sphere_coords_to_map.get(i) = coord;
     256             :         }
     257             : 
     258             :         if (skip_point) {
     259             :           continue;
     260             :         }
     261             : 
     262             :         std::optional<tnsr::I<double, VolumeDim, ::Frame::BlockLogical>>
     263             :             block_coords_of_target_point{};
     264             : 
     265             :         if constexpr (Parallel::is_in_global_cache<
     266             :                           Metavariables, domain::Tags::FunctionsOfTime>) {
     267             :           const auto& functions_of_time =
     268             :               Parallel::get<domain::Tags::FunctionsOfTime>(cache);
     269             :           const double time =
     270             :               InterpolationTarget_detail::get_temporal_id_value(temporal_id);
     271             : 
     272             :           block_coords_of_target_point = block_logical_coordinates_single_point(
     273             :               sphere_coords_to_map, block, time, functions_of_time);
     274             :         } else {
     275             :           block_coords_of_target_point = block_logical_coordinates_single_point(
     276             :               sphere_coords_to_map, block);
     277             :         }
     278             : 
     279             :         if (block_coords_of_target_point.has_value()) {
     280             :           // Get index into vector of all grid points of the target. This is
     281             :           // just the offset + index
     282             :           block_logical_coords[offset_and_num_points->first + index] =
     283             :               make_id_pair(domain::BlockId(array_index.block_id()),
     284             :                            std::move(block_coords_of_target_point.value()));
     285             :         }
     286             :       }
     287             :     } else {
     288             :       (void)coordinates;
     289             :       block_logical_coords = InterpolationTarget_detail::block_logical_coords<
     290             :           InterpolationTargetTag>(cache, all_target_points, temporal_id);
     291             :     }
     292             : 
     293             :     const std::vector<ElementId<VolumeDim>> element_ids{{array_index}};
     294             :     const auto element_coord_holders =
     295             :         element_logical_coordinates(element_ids, block_logical_coords);
     296             : 
     297             :     if (element_coord_holders.count(array_index) == 0) {
     298             :       // There are no target points in this element, so we don't need
     299             :       // to do anything.
     300             :       return;
     301             :     }
     302             : 
     303             :     // There are points in this element, so interpolate to them and
     304             :     // send the interpolated data to the target.  This is done
     305             :     // in several steps:
     306             :     const auto& element_coord_holder = element_coord_holders.at(array_index);
     307             : 
     308             :     // 1. Get the list of variables
     309             :     Variables<typename InterpolationTargetTag::vars_to_interpolate_to_target>
     310             :         interp_vars(mesh.number_of_grid_points());
     311             : 
     312             :     if constexpr (InterpolationTarget_detail::has_compute_vars_to_interpolate_v<
     313             :                       InterpolationTargetTag>) {
     314             :       // 1a. Call compute_vars_to_interpolate.  Need the source in a
     315             :       // Variables, so copy the variables here.
     316             :       // This copy would be unnecessary if we passed a Variables into
     317             :       // InterpolateWithoutInterpComponent instead of passing
     318             :       // individual Tensors, which would require that this Variables is
     319             :       // something in the DataBox. (Note that
     320             :       // InterpolationTarget_detail::compute_dest_vars_from_source_vars
     321             :       // allows the source variables to be different from the
     322             :       // destination variables).
     323             :       Variables<tmpl::list<SourceVarTags...>> source_vars(
     324             :           mesh.number_of_grid_points());
     325             :       [[maybe_unused]] const auto copy_to_variables =
     326             :           [&source_vars](const auto source_var_tag_v, const auto& source_var) {
     327             :             using source_var_tag = tmpl::type_from<decltype(source_var_tag_v)>;
     328             :             get<source_var_tag>(source_vars) = source_var;
     329             :             return 0;
     330             :           };
     331             :       expand_pack(copy_to_variables(tmpl::type_<SourceVarTags>{},
     332             :                                     source_vars_input)...);
     333             : 
     334             :       InterpolationTarget_detail::compute_dest_vars_from_source_vars<
     335             :           InterpolationTargetTag>(make_not_null(&interp_vars), source_vars,
     336             :                                   get<domain::Tags::Domain<VolumeDim>>(cache),
     337             :                                   mesh, array_index, cache, temporal_id);
     338             :     } else {
     339             :       // 1b. There is no compute_vars_to_interpolate. So copy the
     340             :       // source vars directly into the variables.
     341             :       // This copy would be unnecessary if:
     342             :       //   - We passed a Variables into InterpolateWithoutInterpComponent
     343             :       //     instead of passing individual Tensors.
     344             :       //  and
     345             :       //   - This Variables was actually something in the DataBox.
     346             :       //  and
     347             :       //   - Either the passed-in Variables was exactly the same as
     348             :       //     InterpolationTargetTag::vars_to_interpolate_to_target,
     349             :       //     or IrregularInterpolant::interpolate had the ability to
     350             :       //     interpolate only a subset of the Variables passed into it,
     351             :       //     or IrregularInterpolant::interpolate can interpolate individual
     352             :       //     DataVectors.
     353             :       [[maybe_unused]] const auto copy_to_variables =
     354             :           [&interp_vars](const auto tensor_tag_v, const auto& tensor) {
     355             :             using tensor_tag = tmpl::type_from<decltype(tensor_tag_v)>;
     356             :             get<tensor_tag>(interp_vars) = tensor;
     357             :             return 0;
     358             :           };
     359             :       expand_pack(copy_to_variables(tmpl::type_<SourceVarTags>{},
     360             :                                     source_vars_input)...);
     361             :     }
     362             : 
     363             :     // 2. Set up interpolator
     364             :     intrp::Irregular<VolumeDim> interpolator(
     365             :         mesh, element_coord_holder.element_logical_coords);
     366             : 
     367             :     // 3. Interpolate and send interpolated data to target
     368             :     auto& receiver_proxy = Parallel::get_parallel_component<
     369             :         InterpolationTarget<Metavariables, InterpolationTargetTag>>(cache);
     370             :     Parallel::simple_action<
     371             :         Actions::InterpolationTargetVarsFromElement<InterpolationTargetTag>>(
     372             :         receiver_proxy,
     373             :         std::vector<Variables<
     374             :             typename InterpolationTargetTag::vars_to_interpolate_to_target>>(
     375             :             {interpolator.interpolate(interp_vars)}),
     376             :         block_logical_coords,
     377             :         std::vector<std::vector<size_t>>({element_coord_holder.offsets}),
     378             :         temporal_id);
     379             :   }
     380             : 
     381           0 :   using is_ready_argument_tags = tmpl::list<>;
     382             : 
     383             :   template <typename ArrayIndex, typename Component, typename Metavariables>
     384           0 :   bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,
     385             :                 const ArrayIndex& /*array_index*/,
     386             :                 const Component* const /*meta*/) const {
     387             :     return true;
     388             :   }
     389             : 
     390           1 :   bool needs_evolved_variables() const override { return true; }
     391             : };
     392             : 
     393             : /// \cond
     394             : template <size_t VolumeDim, typename InterpolationTargetTag,
     395             :           typename... SourceVarTags>
     396             : PUP::able::PUP_ID
     397             :     InterpolateWithoutInterpComponent<VolumeDim, InterpolationTargetTag,
     398             :                                       tmpl::list<SourceVarTags...>>::my_PUP_ID =
     399             :         0;  // NOLINT
     400             : /// \endcond
     401             : 
     402             : }  // namespace intrp::Events

Generated by: LCOV version 1.14