SpECTRE Documentation Coverage Report
Current view: top level - ControlSystem - Controller.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 23.1 % 26 6
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 <array>
       7              : #include <cstddef>
       8              : #include <limits>
       9              : 
      10              : #include "DataStructures/DataVector.hpp"
      11              : #include "Options/String.hpp"
      12              : 
      13              : /// \cond
      14              : namespace PUP {
      15              : class er;
      16              : }  // namespace PUP
      17              : /// \endcond
      18              : 
      19              : /// \ingroup ControlSystemGroup
      20              : /// A PND (proportional to Q and N derivatives of Q) controller that computes
      21              : /// the control signal:
      22              : /// \f[ U(t) = \sum_{k=0}^{N} a_{k} \frac{d^kQ}{dt^k} \f]
      23              : /// where N is specified by the template parameter `DerivOrder`.
      24              : ///
      25              : /// If an averager is used for `q_and_derivs` (as we typically do), there is an
      26              : /// induced time offset, \f$\Delta t\f$, due to the time-weighted averaging.
      27              : /// Therefore, the `q_and_derivs` that we have in hand are at some time
      28              : /// \f$t_{0}\f$. However, we desire `q_and_derivs` at the current time
      29              : /// \f$t = t_{0} + \Delta t\f$ to determine the appropriate control
      30              : /// signal. We accomplish this by Taylor expanding
      31              : /// \f$Q(t_{0} + \Delta t)\f$. The averager allows for averaging of
      32              : /// \f$Q\f$ and its derivatives OR to not average \f$Q\f$ while still averaging
      33              : /// the derivatives (the derivatives are always averaged in order to reduce
      34              : /// noise due to numerical differentiation). When they are both averaged, the
      35              : /// time offset will be identical for \f$Q\f$ and the derivatives,
      36              : /// i.e. `q_time_offset` = `deriv_time_offset`. If an unaveraged \f$Q\f$ is
      37              : /// used, then the time offset associated with \f$Q\f$ is zero,
      38              : /// i.e. `q_time_offset`=0. and the derivative time offset, `deriv_time_offset`,
      39              : /// remains non-zero.
      40              : template <size_t DerivOrder>
      41            1 : class Controller {
      42              :  public:
      43            0 :   struct UpdateFraction {
      44            0 :     using type = double;
      45            0 :     static constexpr Options::String help = {
      46              :         "Fraction of damping timescale used to determine how often to update "
      47              :         "functions of time."};
      48              :   };
      49              : 
      50            0 :   using options = tmpl::list<UpdateFraction>;
      51            0 :   static constexpr Options::String help{
      52              :       "Computes control signal used to reset highest derivative of a function "
      53              :       "of time. Also determines when a function of time needs to be updated "
      54              :       "next."};
      55              : 
      56            0 :   Controller(const double update_fraction)
      57              :       : update_fraction_(update_fraction) {}
      58              : 
      59            0 :   Controller() = default;
      60            0 :   Controller(Controller&&) = default;
      61            0 :   Controller& operator=(Controller&&) = default;
      62            0 :   Controller(const Controller&) = default;
      63            0 :   Controller& operator=(const Controller&) = default;
      64            0 :   ~Controller() = default;
      65              : 
      66            0 :   DataVector operator()(const double time, const DataVector& timescales,
      67              :                         const std::array<DataVector, DerivOrder>& q_and_derivs,
      68              :                         double q_time_offset, double deriv_time_offset);
      69              : 
      70              :   /// Takes the current minimum of all timescales for a specific control system
      71              :   /// and uses that to set the time between updates
      72            1 :   void assign_time_between_updates(const double current_min_timescale) {
      73              :     time_between_updates_ = update_fraction_ * current_min_timescale;
      74              :   }
      75              : 
      76              :   /// Returns `true` if the controller is ready to calculate the control signal
      77            1 :   bool is_ready(const double time) {
      78              :     return time >= last_update_time_ + time_between_updates_;
      79              :   }
      80              : 
      81              :   /// Calculates the new expiration time as the old expiration time plus the
      82              :   /// time between updates
      83            1 :   double next_expiration_time(const double current_expiration_time) {
      84              :     return current_expiration_time + time_between_updates_;
      85              :   }
      86              : 
      87              :   /// Sets the last time updated to the initial time
      88            1 :   void set_initial_update_time(const double initial_time) {
      89              :     last_update_time_ = initial_time;
      90              :   }
      91              : 
      92              :   /// Return the update fraction
      93            1 :   double get_update_fraction() const { return update_fraction_; }
      94              : 
      95              :   // NOLINTNEXTLINE(google-runtime-references)
      96            0 :   void pup(PUP::er& p) {
      97              :     p | update_fraction_;
      98              :     p | time_between_updates_;
      99              :     p | last_update_time_;
     100              :   }
     101              : 
     102              :   template <size_t LocalDerivOrder>
     103              :   // NOLINTNEXTLINE(readability-redundant-declaration) false positive
     104            0 :   friend bool operator==(const Controller<LocalDerivOrder>& lhs,
     105              :                          const Controller<LocalDerivOrder>& rhs);
     106              : 
     107              :  private:
     108              :   // If update_fraction_ isn't set we need to error
     109            0 :   double update_fraction_{std::numeric_limits<double>::signaling_NaN()};
     110              :   // If this time_between_updates_ isn't set, the default should just be that
     111              :   // the functions of time are never updated (i.e. infinity)
     112            0 :   double time_between_updates_{std::numeric_limits<double>::infinity()};
     113              :   // Set to a large negative number to signify that we haven't updated yet and
     114              :   // should as early as possible
     115            0 :   double last_update_time_{-std::numeric_limits<double>::max()};
     116              : };
     117              : 
     118              : template <size_t DerivOrder>
     119            0 : bool operator!=(const Controller<DerivOrder>& lhs,
     120              :                 const Controller<DerivOrder>& rhs);
        

Generated by: LCOV version 2.0-1