0

I have a time domain simulation running timesteps are adaptive and may grow or shrink, so there aren't a fixed number of steps to a constant time.

I want to produce an output every (simulation) time interval (eg 1 second), but this does not correlate with a fixed number of steps.

There is a method which gets called every step (both the time and step count are arguments), but I only want it to return true once a second. This method is currently declared const.

(Timesteps will typically be 1000-per-second so I don't need to make provision for the case where one step completely oversteps a time interval).

I don't know how I make this perform actions once per second, without refactoring to remove the const flag.

I can achieve the once per second output if I make the functor stateful and record the previous time which was reported. However I would prefer not to make the method non-const as this will:

  • need to propagate non-const to all invoking methods (a step backwards)
  • require refactoring of the invoking code (not a big problem)
  • risk race conditions when we try to use it in multithreaded settings

Note: I know I could use const_cast or raw pointers to achieve the mutability despite const, but this is not a good solution and won't help the race conditions/multithreading risks.

My functor class (which currently won't compile due to the cost)

class FixedTimeOutputDecider {

    /// <summary>
    /// This should return true once per "regularity" which defaults to once per second
    /// </summary>
    
   public:
    FixedTimeOutputDecider(double regularity = 1.0) : _regularity(regularity), _next_output_time(0) {}

    bool should_output(int step, double time) const {
        if (time > _next_output_time) {
            // the next time to output should be the next integer*regularity
            _next_output_time = (int(floor(time / _regularity)) + 1) * _regularity;

            return true;
        } else {
            return false;
        }
    }

   
  bool operator()(int step, double time /*extra data here*/) const { 
    if(should_output(step, time)){
      //Outputing code here
    } 
  }

   private:
    const double _regularity;
    double _next_output_time;
};

Is it possible to output once per second in a stateless functor given the irregular step size?

Edit: Following comments/questions about the context and surrounding code, here is a bigger (not minimal) example of the use of the class https://godbolt.org/z/jecaMnx7o .

11
  • Why don't you simply count up the time passed at each call and if the accumulated time is greater than or equal to 1s, do your thing? (And then maybe remember any excess time and remember to account for it on the next step). Commented Sep 4 at 14:35
  • Side note: if you need to keep track of things in a const member function, then sometimes a mutable member variable is the answer.. Commented Sep 4 at 14:39
  • Decouple "view" from "model" your model has its own internal pace seperate from your view. Just let the model run (it should even run if you don't look) and poll every time interval to make a snapshot which you then display. (this assumes your model is not crazily huge). Commented Sep 4 at 14:45
  • @3CEZVQ my mistake, I cut too much while minimising the code, I'll put the operator back...... Commented Sep 4 at 14:45
  • "I know I could use const_cast or raw pointers to achieve the mutability despite const," -- Another, and more direct, way to achieve mutability despite const is mutable. It is something that's usually better to avoid, but so are the alternatives you listed. Was mutable overlooked? Commented Sep 4 at 15:09

2 Answers 2

4

Your problem can be solved using a sampling buffer pattern. The sampling buffer is a mutex-protected buffer with a single data element. It allows sampling a stream of data.

The thread producing the data writes to the sampling buffer at whatever rate it needs. The reading thread reads at its own rate. This allows the reader to always read the most recent value in the buffer without needing to synchronize the writing and reading rates. Creating a reader to read every second would be done using the sleep_until() function.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the good try but this is along the wrong lines, due to me. I failed to distinguish between this "chronological time" experienced by a person watching the simulation; and "simulation" time, which is just a double I calculate inside the simulation which is presented as a result.
1

You can save the constness of your class and make it stateless by moving _next_output_time out of the class, but instead you have to return it from operator() (to be able to save it by the caller) and receive it in operator() as input parameter.

The drawback is that the code becomes heavier.

#include <cmath>
#include <iostream>
#include <utility>

class Outputter {
    public:
    virtual std::pair<bool, double> operator()(int step, double time, double prev_time) const = 0;
};

class FixedTimeOutputDecider: public Outputter {
   public:

    FixedTimeOutputDecider(double regularity = 1.0) : _regularity(regularity) {}

    std::pair<bool, double> should_output(int step, double time, double prev_time) const {
        if (time > prev_time) {
            // the next time to output should be the next integer*regularity
            return {true, (int(floor(time / _regularity)) + 1) * _regularity};
        } else {
            return {false, 0.0};
        }
    }
   
  std::pair<bool, double> operator()(int step, double time, double prev_time) const override 
  {
    auto result = should_output(step, time, prev_time);
    if(result.first) {
      //Outputing code here
      std::cout << step << " " << time << " " << prev_time << " " << result.second << std::endl;
    } 
    return result;
  }

   private:
    const double _regularity;
};


void simulation1(const Outputter& outputter) {
    std::pair<bool, double> result = {false, 0.0};
    result = outputter(1, 0.05, result.second);
    result = outputter(2, 0.55, result.second);
    result = outputter(3, 1.08, result.second);
    result = outputter(4, 1.09, result.second);
}

void simulation2 (const Outputter& outputter) {
    std::pair<bool, double> result = {false, 0.0};
    result = outputter(101, 0.2, result.second);
    result = outputter(102, 1.2, result.second);
    result = outputter(103, 1.8, result.second);
    result = outputter(104, 2.2, result.second);
}


int main(int argc, char* argv[])
{
    FixedTimeOutputDecider outputter;

    //These can be in any order/parallel
    simulation2(outputter);
    simulation1(outputter);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.