SpECTRE Documentation Coverage Report
Current view: top level - ControlSystem - Controller.hpp Hit Total Coverage
Commit: 298623cdd84dec4702f71d2fbf9fd1e7d4ee486b Lines: 6 26 23.1 %
Date: 2024-10-05 00:18:03
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 1.14