SpECTRE Documentation Coverage Report
Current view: top level - IO/Exporter - ModalSpacetimeInterpolator.hpp Coverage Total Hit
Commit: a37d7b3c1803b88b537d2aef1c27ca5e6b499398 Lines: 23.8 % 21 5
Test Date: 2026-11-12 04:47:19
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 <map>
       9              : #include <optional>
      10              : #include <string>
      11              : #include <unordered_map>
      12              : #include <variant>
      13              : #include <vector>
      14              : 
      15              : #include "DataStructures/Tensor/TypeAliases.hpp"
      16              : #include "Domain/Domain.hpp"
      17              : #include "Domain/FunctionsOfTime/FunctionOfTime.hpp"
      18              : #include "Domain/Structure/ElementId.hpp"
      19              : #include "Domain/Structure/ElementSearchTree.hpp"
      20              : #include "IO/Logging/Verbosity.hpp"
      21              : #include "NumericalAlgorithms/Interpolation/UniformCardinalBSpline.hpp"
      22              : #include "NumericalAlgorithms/Spectral/Mesh.hpp"
      23              : #include "Utilities/Gsl.hpp"
      24              : 
      25              : namespace spectre::Exporter {
      26              : 
      27              : /*!
      28              :  * \brief Interpolate tensor components in both space and time using modal
      29              :  * data. This is much more efficient and accurate than interpolating nodal
      30              :  * data.
      31              :  *
      32              :  * \details This class builds a time interpolant for every modal coefficient of
      33              :  * every tensor component on each element. It reads volume data one element at
      34              :  * a time, converts nodal values to modal coefficients, and constructs a
      35              :  * cardinal cubic B-spline in time for each mode. At evaluation time it locates
      36              :  * the element containing the target point, evaluates the modal interpolants at
      37              :  * the requested time, and evaluates the Legendre series at the element-logical
      38              :  * point.
      39              :  *
      40              :  * ### How to use
      41              :  *
      42              :  * In a simulation, create a few events that write out volume data at different
      43              :  * time intervals using ObserveFields. The observation times need to be uniform
      44              :  * within each subfile, so either observe at a fixed slab interval if the slab
      45              :  * size is constant, or at fixed times using a dense trigger. The idea is that
      46              :  * there is a very coarse grid written very frequently and a finer grid written
      47              :  * less frequently. This way, the lowest modes are resolved very accurately and
      48              :  * the higher modes are approximated with a larger error. A useful choice in
      49              :  * practice is:
      50              :  *
      51              :  * - Observe the lowest 3-4 modes very frequently, e.g. every 0.5M or even more
      52              :  *   often.
      53              :  * - Observe the lowest 6-7 modes 10 times less frequently.
      54              :  * - Observe all modes 10 times less frequently again.
      55              :  *
      56              :  * Important: use the `ProjectToMesh` option of ObserveFields to ensure that
      57              :  * the data is truncated cleanly in modal space. Do not use `InterpolateToMesh`
      58              :  * as this creates a catastrophic aliasing error. Also, do not use the
      59              :  * `CleanFunctionsOfTime` event, as this removes the required global history of
      60              :  * the functions of time.
      61              :  *
      62              :  * Then construct the ModalSpacetimeInterpolator from these subfiles in
      63              :  * priority order, from the coarse mesh observed most frequently to the fine
      64              :  * mesh that defines the final spatial resolution. The first subfile containing
      65              :  * a mode owns it, and later subfiles only supply the modes that are missing on
      66              :  * the earlier meshes. The subfile meshes must therefore be nested, i.e. have
      67              :  * component-wise nondecreasing extents, and all subfiles must contain the same
      68              :  * elements and the same domain. The subfiles may have different time steps and
      69              :  * nonaligned observation times; the available time interval is their
      70              :  * intersection.
      71              :  *
      72              :  * ### Error tolerance and compression
      73              :  *
      74              :  * The time series of every owned mode is compressed into an
      75              :  * `intrp::UniformCardinalBSpline` on the coarsest uniform time grid that
      76              :  * reproduces the samples to within an absolute error tolerance, and modes
      77              :  * whose amplitude never exceeds the tolerance are dropped entirely since they
      78              :  * are not important for reconstructing the field (this can be up to 80% of the
      79              :  * modes in practice). This way, the interpolator only keeps the data that is
      80              :  * necessary.
      81              :  *
      82              :  * By default, the tolerance for each element and tensor component is estimated
      83              :  * from the lowest mode in the first subfile: it is the interpolation error of
      84              :  * that mode when using all of its observed data, so all other modes are
      85              :  * interpolated with a conservatively similar error. Note that this infers the
      86              :  * tolerance from the temporal variability of the data rather than from an
      87              :  * accuracy requirement, so specify the tolerance explicitly when you know the
      88              :  * accuracy you need. The estimate requires at least 9 observations in the
      89              :  * first subfile; an explicit tolerance does not. Either way this is a per-mode
      90              :  * compression heuristic. It does not bound the total reconstructed field error
      91              :  * and does not detect temporal undersampling in the original volume data.
      92              :  *
      93              :  * ### Limitations
      94              :  *
      95              :  * Only the Legendre basis is supported for now. Changes to the element
      96              :  * topology or per-element mesh within one time series (e.g. AMR), restarts
      97              :  * that produce non-uniform observation times, and element migration between
      98              :  * files within one time series are not supported. Files from different
      99              :  * simulation segments must be joined first, with overlapping observations
     100              :  * removed. Time-dependent domains use the global functions of time stored
     101              :  * with the final subfile, and all functions required by the domain must
     102              :  * cover the full available time interval.
     103              :  *
     104              :  * Since the time grid of each mode is uniform, it is a good idea to use a
     105              :  * separate interpolator for parts of the evolution that are dynamically
     106              :  * different (e.g. junk radiation vs. inspiral vs. merger vs. ringdown). A
     107              :  * wrapper that patches together several interpolators may be added in the
     108              :  * future.
     109              :  *
     110              :  * \note Due to a bug in the boost cardinal cubic B-spline implementation that
     111              :  * was fixed only recently, see
     112              :  * https://github.com/boostorg/math/commit/4809e714d4806c07da3a3def0c4550daa0529b8d,
     113              :  * the interpolator can only be built with boost version 1.81 or later. See
     114              :  * `intrp::UniformCardinalBSpline` for details.
     115              :  */
     116              : template <size_t Dim, typename Frame = ::Frame::Inertial>
     117            1 : class ModalSpacetimeInterpolator {
     118              :  public:
     119              :   /*!
     120              :    * \brief Construct from one or more volume files and nested volume
     121              :    * subfiles.
     122              :    *
     123              :    * \param volume_files_or_glob A list of volume H5 files, or a glob string
     124              :    *     that resolves to volume files. The files may distribute the elements
     125              :    *     of each observation across nodes.
     126              :    * \param subfiles_in_priority_order Volume subfile names ordered from the
     127              :    *     preferred source of low modes to the source of the final spatial
     128              :    *     mesh. The meshes must have component-wise nondecreasing extents.
     129              :    * \param tensor_components Tensor component names to interpolate. The
     130              :    *     returned values have this order.
     131              :    * \param start_time Optional lower bound used to select observations from
     132              :    *     every subfile.
     133              :    * \param end_time Optional upper bound used to select observations from
     134              :    *     every subfile.
     135              :    * \param error_tolerance Optional absolute tolerance used to drop and
     136              :    *     compress modes. By default the tolerance is estimated from the
     137              :    *     lowest mode in the first subfile, which then needs at least 9
     138              :    *     observations.
     139              :    * \param verbosity Controls diagnostic output during construction.
     140              :    */
     141            1 :   ModalSpacetimeInterpolator(
     142              :       const std::variant<std::vector<std::string>, std::string>&
     143              :           volume_files_or_glob,
     144              :       std::vector<std::string> subfiles_in_priority_order,
     145              :       std::vector<std::string> tensor_components,
     146              :       std::optional<double> start_time = std::nullopt,
     147              :       std::optional<double> end_time = std::nullopt,
     148              :       std::optional<double> error_tolerance = std::nullopt,
     149              :       Verbosity verbosity = Verbosity::Quiet);
     150              : 
     151            0 :   ModalSpacetimeInterpolator(ModalSpacetimeInterpolator&&) = default;
     152            0 :   ModalSpacetimeInterpolator& operator=(ModalSpacetimeInterpolator&&) = default;
     153            0 :   ModalSpacetimeInterpolator(const ModalSpacetimeInterpolator&) = delete;
     154            0 :   ModalSpacetimeInterpolator& operator=(const ModalSpacetimeInterpolator&) =
     155              :       delete;
     156            0 :   ~ModalSpacetimeInterpolator() = default;
     157              : 
     158              :   /*!
     159              :    * \brief Interpolate the tensor components at a spacetime point.
     160              :    *
     161              :    * \param result Output buffer, resized to the number of tensor components.
     162              :    * \param target_point Coordinates of the target point in `Frame`.
     163              :    * \param time Time at which to evaluate the interpolant.
     164              :    * \param block_order Optional block-ordering hint that speeds up repeated
     165              :    *     block searches.
     166              :    */
     167            1 :   void interpolate_to_point(gsl::not_null<std::vector<double>*> result,
     168              :                             const tnsr::I<double, Dim, Frame>& target_point,
     169              :                             double time,
     170              :                             std::optional<gsl::not_null<std::vector<size_t>*>>
     171              :                                 block_order = std::nullopt) const;
     172              : 
     173              :   /// Tensor components in the order returned by `interpolate_to_point()`
     174            1 :   const std::vector<std::string>& tensor_components() const {
     175              :     return tensor_components_;
     176              :   }
     177              : 
     178              :   /// Inclusive time interval shared by all input subfiles
     179            1 :   const std::array<double, 2>& time_bounds() const { return time_bounds_; }
     180              : 
     181              :  private:
     182            0 :   using ModeInterpolant = std::optional<intrp::UniformCardinalBSpline>;
     183              : 
     184            0 :   struct ElementData {
     185            0 :     Mesh<Dim> mesh{};
     186              :     // Indexed by [component][mode on the final mesh]
     187            0 :     std::vector<std::vector<ModeInterpolant>> interpolants;
     188              :   };
     189              : 
     190            0 :   std::vector<std::string> tensor_components_;
     191            0 :   std::array<double, 2> time_bounds_{};
     192            0 :   Domain<Dim> domain_{};
     193            0 :   domain::FunctionsOfTimeMap functions_of_time_;
     194            0 :   std::map<size_t, domain::ElementSearchTree<Dim>> element_search_trees_{};
     195            0 :   std::unordered_map<ElementId<Dim>, ElementData> element_data_{};
     196              : };
     197              : 
     198              : }  // namespace spectre::Exporter
        

Generated by: LCOV version 2.0-1