Skip to content

Simulation Performance

Steven A White edited this page Dec 23, 2020 · 1 revision

Introduction

Biogears is a numerical simulation engine that is designed to compute the full body physiology of a single patient on a single thread. You can run multiple engines across multiple threads or on a single thread to solve for multiple patient, but a single simulation will not split work across logical cores. This means that the ratio of simulations to wall clock time is directly proportional to a single logical cores FLOP performance. The following article goes in to some detail of how BioGears advances time and ways to limit bio-gears to realtime processing if its not desired to run BioGears at its peek simulation rate.

Advancing Model Time

Calling virtual void AdvanceModelTime(double time, const TimeUnit& unit = TimeUnit::s, bool appendDataTrack = false) override; Allows the user to determine a length of time to advance the current simulation mode. As it takes in a TimeUnit the user could decide to advance time by simulated hours at a time.

But the circuit solver of BioGears requires a smaller granular step inorder to resolve converge so individual calls to AdvanceModelTime are broken in to deltaTs based on the simulation resolution which is set in BioGearsConfiguation.xml with a default of 0.02 or 50hz. Everytime you call AdvanceModelTime(double,TimeUnit, bool) it will call AdvanceModelTime(bool) by the Time provided multipled by 1/timestep. The actual wall clock time of this advancement is dependent on the host hardware and may or many not be faster then realtime.

Throttling BioGears

Many use cases require BioGears to throttle itself to realtime. As BioGears will run faster then realtime on most hardware the most common solution is to run all engine activity in a second thread of your main application and sleep between calls to time advancement. A common C++ throttle loop can be found in the BioGears Visualizer code.

void Scenario::physiology_thread_main()
{
  using namespace std::chrono_literals;

  auto current_time = std::chrono::steady_clock::now();
  std::chrono::time_point<std::chrono::steady_clock> prev;
  while (_running) {
    prev = current_time;
    physiology_thread_step();
    current_time = std::chrono::steady_clock::now();
    if (_throttle) {
      while ((current_time - prev) < 100ms) {
        std::this_thread::sleep_for(16ms);
        current_time = std::chrono::steady_clock::now();
      }
    }
  }
}

inline void Scenario::physiology_thread_step()
{
  if (!_paused) {
    _engine_mutex.lock();
    _engine->AdvanceModelTime(0.1, biogears::TimeUnit::s);
    _engine_mutex.unlock();
  } else {
    std::this_thread::sleep_for(16ms);
  }
}