• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cyclus / cyclus / 27474992905

13 Jun 2026 06:14PM UTC coverage: 36.8% (+0.07%) from 36.732%
27474992905

push

github

web-flow
Merge pull request #1912 from dean-krueger/progress-bar

Add a Progress Bar to Cyclus Simulations

232 of 363 new or added lines in 3 files covered. (63.91%)

2 existing lines in 2 files now uncovered.

52990 of 143996 relevant lines covered (36.8%)

16322.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.72
/src/timer.cc
1
#include "platform.h"
2
// Implements the Timer class
3
#include "timer.h"
4

5
#include <algorithm>
6
#include <iostream>
7
#include <string>
8
#include <cstdlib>
9
#include <cmath>
10
#if CYCLUS_IS_PARALLEL
11
#include <omp.h>
12
#endif  // CYCLUS_IS_PARALLEL
13

14
#include "agent.h"
15
#include "error.h"
16
#include "logger.h"
17
#include "pyhooks.h"
18
#include "sim_init.h"
19

20
namespace cyclus {
21

22
void Timer::RunSim() {
266✔
23
  LogLevel saved_level = Logger::ReportLevel();
266✔
24
  if (quiet_) {
266✔
25
    // Set log level below LEV_ERROR (lowest level) to suppress all CLOG output
26
    Logger::SetReportLevel(static_cast<LogLevel>(-1));
27
  }
28

29
  CLOG(LEV_INFO1) << "Simulation set to run from start=" << 0
266✔
30
                  << " to end=" << si_.duration;
×
31
  CLOG(LEV_INFO1) << "Beginning simulation";
266✔
32

33
  ExchangeManager<Material> matl_manager(ctx_);
266✔
34
  ExchangeManager<Product> genrsrc_manager(ctx_);
266✔
35

36
  if (!progress_bar_ && ProgressBarEnabled()) {
266✔
37
    SetupProgressBar();
266✔
38
  }
39

40
  while (time_ < si_.duration) {
7,798✔
41
    CLOG(LEV_INFO1) << "Current time: " << time_;
7,537✔
42

43
    if (want_snapshot_) {
7,537✔
44
      want_snapshot_ = false;
28✔
45
      SimInit::Snapshot(ctx_);
28✔
46
    }
47

48
    // run through phases
49
    DoBuild();
7,537✔
50
    CLOG(LEV_INFO2) << "Beginning Tick for time: " << time_;
7,537✔
51
    DoTick();
7,537✔
52
    CLOG(LEV_INFO2) << "Beginning DRE for time: " << time_;
7,537✔
53
    DoResEx(&matl_manager, &genrsrc_manager);
7,537✔
54
    CLOG(LEV_INFO2) << "Beginning Tock for time: " << time_;
7,536✔
55
    DoTock();
7,536✔
56
    CLOG(LEV_INFO2) << "Beginning Decision for time: " << time_;
7,536✔
57
    DoDecision();
7,536✔
58
    DoDecom();
7,536✔
59

60
#ifdef CYCLUS_WITH_PYTHON
61
    EventLoop();
7,536✔
62
#endif
63

64
    time_++;
7,536✔
65
    RedrawProgressBar();
7,536✔
66

67

68
    if (want_kill_) {
7,536✔
69
      break;
70
    }
71
  }
72

73
  ctx_->NewDatum("Finish")
265✔
74
      ->AddVal("EarlyTerm", want_kill_)
75
      ->AddVal("EndTime", time_ - 1)
265✔
76
      ->Record();
530✔
77

78
  SimInit::Snapshot(
265✔
79
      ctx_);  // always do a snapshot at the end of every simulation
80

81
  if (quiet_) {
265✔
82
    Logger::SetReportLevel(saved_level);
83
  }
84
}
265✔
85

86
void Timer::DoBuild() {
7,537✔
87
  // build queued agents
88
  std::vector<std::pair<std::string, Agent*>> build_list = build_queue_[time_];
7,537✔
89
  for (int i = 0; i < build_list.size(); ++i) {
23,360✔
90
    Agent* m = ctx_->CreateAgent<Agent>(build_list[i].first);
47,469✔
91
    Agent* parent = build_list[i].second;
15,823✔
92
    CLOG(LEV_INFO3) << "Building a " << build_list[i].first << " from parent "
15,823✔
93
                    << build_list[i].second;
×
94
    m->Build(parent);
15,823✔
95
    if (parent != NULL) {
15,823✔
96
      parent->BuildNotify(m);
63✔
97
    } else {
98
      CLOG(LEV_DEBUG1) << "Hey! Listen! Built an Agent without a Parent.";
15,760✔
99
    }
100
  }
101
}
7,537✔
102

103
void Timer::DoTick() {
7,537✔
104
  for (TimeListener* agent : py_tickers_) {
73,897✔
105
    agent->Tick();
66,360✔
106
  }
107

108
#pragma omp parallel for
7,537✔
109
  for (size_t i = 0; i < cpp_tickers_.size(); ++i) {
110
    cpp_tickers_[i]->Tick();
111
  }
112
}
7,537✔
113

114
void Timer::DoResEx(ExchangeManager<Material>* matmgr,
7,537✔
115
                    ExchangeManager<Product>* genmgr) {
116
  matmgr->Execute();
7,537✔
117
  genmgr->Execute();
7,536✔
118
}
7,536✔
119

120
void Timer::DoTock() {
7,536✔
121
  for (TimeListener* agent : py_tickers_) {
73,894✔
122
    agent->Tock();
66,358✔
123
  }
124

125
#pragma omp parallel for
7,536✔
126
  for (size_t i = 0; i < cpp_tickers_.size(); ++i) {
127
    cpp_tickers_[i]->Tock();
128
  }
129

130
  if (si_.explicit_inventory || si_.explicit_inventory_compact) {
7,536✔
131
    std::set<Agent*> ags = ctx_->agent_list_;
170✔
132
    std::vector<Agent*> agent_vec(ags.begin(), ags.end());
170✔
133
#pragma omp parallel for
170✔
134
    for (int i = 0; i < agent_vec.size(); i++) {
135
      Agent* a = agent_vec[i];
136
      if (a->enter_time() != -1) {
137
        RecordInventories(a);
138
      }
139
    }
140
  }
141
}
7,536✔
142

143
void Timer::DoDecision() {
7,536✔
144
  for (std::map<int, TimeListener*>::iterator agent = tickers_.begin();
7,536✔
145
       agent != tickers_.end();
73,834✔
146
       agent++) {
147
    agent->second->Decision();
66,298✔
148
  }
149
}
7,536✔
150

151
void Timer::RecordInventories(Agent* a) {
510✔
152
  Inventories invs = a->SnapshotInv();
510✔
153
  Inventories::iterator it2;
154
  for (it2 = invs.begin(); it2 != invs.end(); ++it2) {
680✔
155
    std::string name = it2->first;
156
    std::vector<Resource::Ptr> mats = it2->second;
170✔
157
    if (mats.empty() || ResCast<Material>(mats[0]) == NULL) {
680✔
158
      continue;  // skip non-material inventories
159
    }
160

161
    Material::Ptr m = ResCast<Material>(mats[0]->Clone());
170✔
162
    for (int i = 1; i < mats.size(); i++) {
935✔
163
      m->Absorb(ResCast<Material>(mats[i]->Clone()));
3,060✔
164
    }
165
    RecordInventory(a, name, m);
510✔
166
  }
170✔
167
}
510✔
168

169
void Timer::RecordInventory(Agent* a, std::string name, Material::Ptr m) {
170✔
170
  if (si_.explicit_inventory) {
170✔
171
    CompMap c = m->comp()->mass();
180✔
172
    compmath::Normalize(&c, m->quantity());
90✔
173
    CompMap::iterator it;
174
    for (it = c.begin(); it != c.end(); ++it) {
270✔
175
      ctx_->NewDatum("ExplicitInventory")
180✔
176
          ->AddVal("AgentId", a->id())
180✔
177
          ->AddVal("Time", time_)
178
          ->AddVal("InventoryName", name)
179
          ->AddVal("NucId", it->first)
180
          ->AddVal("Quantity", it->second)
181
          ->AddVal("Units", m->units())
360✔
182
          ->Record();
1,260✔
183
    }
184
  }
185

186
  if (si_.explicit_inventory_compact) {
170✔
187
    CompMap c = m->comp()->mass();
80✔
188
    compmath::Normalize(&c, 1);
80✔
189
    ctx_->NewDatum("ExplicitInventoryCompact")
80✔
190
        ->AddVal("AgentId", a->id())
80✔
191
        ->AddVal("Time", time_)
192
        ->AddVal("InventoryName", name)
193
        ->AddVal("Quantity", m->quantity())
80✔
194
        ->AddVal("Units", m->units())
160✔
195
        ->AddVal("Composition", c)
196
        ->Record();
720✔
197
  }
198
}
170✔
199

200
void Timer::DoDecom() {
7,536✔
201
  // decommission queued agents
202
  std::vector<Agent*> decom_list = decom_queue_[time_];
7,536✔
203
  for (int i = 0; i < decom_list.size(); ++i) {
19,205✔
204
    Agent* m = decom_list[i];
11,669✔
205
    if (m->parent() != NULL) {
11,669✔
206
      m->parent()->DecomNotify(m);
824✔
207
    }
208
    m->Decommission();
11,669✔
209
  }
210
}
7,536✔
211

212
void Timer::RegisterTimeListener(TimeListener* agent) {
18,925✔
213
  tickers_[agent->id()] = agent;
18,925✔
214
  if (agent->IsShim()) {
18,925✔
215
    py_tickers_.push_back(agent);
18,925✔
216
  } else {
217
    cpp_tickers_.push_back(agent);
×
218
  }
219
}
18,925✔
220

221
void Timer::UnregisterTimeListener(TimeListener* tl) {
11,668✔
222
  tickers_.erase(tl->id());
11,668✔
223
  if (tl->IsShim()) {
11,668✔
224
    py_tickers_.erase(std::remove(py_tickers_.begin(), py_tickers_.end(), tl),
11,668✔
225
                      py_tickers_.end());
226
  } else {
227
    cpp_tickers_.erase(
×
228
        std::remove(cpp_tickers_.begin(), cpp_tickers_.end(), tl),
×
229
        cpp_tickers_.end());
230
  }
231
}
11,668✔
232

233
void Timer::SchedBuild(Agent* parent, std::string proto_name, int t) {
21,031✔
234
  if (t <= time_) {
21,031✔
235
    throw ValueError("Cannot schedule build for t < [current-time]");
×
236
  }
237
  build_queue_[t].push_back(std::make_pair(proto_name, parent));
21,031✔
238
}
21,031✔
239

240
void Timer::SchedDecom(Agent* m, int t) {
11,858✔
241
  if (t < time_) {
11,858✔
242
    throw ValueError("Cannot schedule decommission for t < [current-time]");
×
243
  }
244

245
  // It is possible that a single agent may be scheduled for decommissioning
246
  // multiple times. If this happens, we cannot just add it to the queue again
247
  // - the duplicate entries will result in a double delete attempt and
248
  // segfaults and otherwise bad things.  Remove previous decommissionings
249
  // before scheduling this new one.
250
  std::map<int, std::vector<Agent*>>::iterator it;
251
  bool done = false;
252
  for (it = decom_queue_.begin(); it != decom_queue_.end(); ++it) {
109,617✔
253
    int t = it->first;
99,420✔
254
    std::vector<Agent*> ags = it->second;
99,420✔
255
    for (int i = 0; i < ags.size(); i++) {
6,848,090✔
256
      if (ags[i] == m) {
6,750,331✔
257
        CLOG(LEV_WARN) << "scheduled over previous decommissioning of "
1,661✔
258
                       << m->id();
×
259
        decom_queue_[t].erase(decom_queue_[t].begin() + i);
1,661✔
260
        done = true;
261
        break;
262
      }
263
    }
264
    if (done) {
265
      break;
266
    }
267
  }
268

269
  decom_queue_[t].push_back(m);
11,858✔
270
}
11,858✔
271

272
int Timer::time() {
473,763✔
273
  return time_;
473,763✔
274
}
275

276
void Timer::Reset() {
×
277
  tickers_.clear();
278
  cpp_tickers_.clear();
279
  py_tickers_.clear();
280
  build_queue_.clear();
281
  decom_queue_.clear();
282
  si_ = SimInfo(0);
×
283

284
  progress_bar_.reset();
UNCOV
285
}
×
286

287
void Timer::Initialize(Context* ctx, SimInfo si) {
619✔
288
  if (si.m0 < 1 || si.m0 > 12) {
619✔
289
    throw ValueError("Invalid month0; must be between 1 and 12 (inclusive).");
×
290
  }
291

292
  want_kill_ = false;
619✔
293
  ctx_ = ctx;
619✔
294
  time_ = 0;
619✔
295
  si_ = si;
619✔
296

297
  if (si.branch_time > -1) {
619✔
298
    time_ = si.branch_time;
4✔
299
  }
300
}
619✔
301

302
bool Timer::ProgressBarEnabled() {
266✔
303
  const char* env_var = std::getenv("CYCLUS_PROGRESS_BAR");
266✔
304
  if (env_var) {
266✔
NEW
305
    std::string val(env_var);
×
NEW
306
    return !(val == "0" || val == "false" || val == "no" || val == "off");
×
307
  }
308

309
  // Disable with verbose logging to avoid interfering with debug output.
310
  return cyclus::Logger::ReportLevel() <= cyclus::LEV_WARN;
266✔
311
}
312

313
void Timer::SetupProgressBar() {
266✔
314
  progress_origin_ = time_;
266✔
315
  progress_span_ = std::max(si_.duration - progress_origin_, 1);
266✔
316

317
  progress_bar_.reset();
318
  progress_update_frequency_ = ProgressUpdateFrequency(si_.duration);
266✔
319
  progress_bar_.reset(new indicators::ProgressBar{
320
      indicators::option::BarWidth{50},
266✔
321
      indicators::option::MaxProgress{ProgressValue(progress_span_)},
266✔
322
      indicators::option::ShowPercentage{true},
266✔
323
  });
266✔
324
}
266✔
325

326
void Timer::RedrawProgressBar() {
7,536✔
327
  int completed_steps = time_ - progress_origin_;
7,536✔
328

329
  if (progress_bar_ &&
7,536✔
330
      (completed_steps % progress_update_frequency_ == 0 ||
7,536✔
NEW
331
      completed_steps == progress_span_)) {
×
332

333
    const size_t progress = ProgressValue(completed_steps);
7,536✔
334
    // Postfix must be set before set_progress: set_option does not redraw,
335
    // but set_progress calls print_progress() which reads postfix_text.
336
    progress_bar_->set_option(
7,536✔
337
        indicators::option::PostfixText{
7,536✔
338
            " (" + std::to_string(progress) + "/" +
15,072✔
339
            std::to_string(progress_span_) + ")"});
30,144✔
340
    progress_bar_->set_progress(progress);
7,536✔
341
  }
342
}
7,536✔
343

344
int Timer::ProgressUpdateFrequency(int duration) {
266✔
345
  return std::max(1, duration / 100);
266✔
346
}
347

348
size_t Timer::ProgressValue(int completed_steps) {
7,802✔
349
  return static_cast<size_t>(
350
      std::min(std::max(completed_steps, 0), progress_span_));
7,802✔
351
}
352

353
int Timer::dur() {
×
354
  return si_.duration;
×
355
}
356

357
int Timer::CalcTimeDiff(int year, int month) {
36✔
358

359
  int start_time = si_.y0 * cyclusYear + si_.m0 * cyclusMonth;
36✔
360
  int time = std::max(year,0) * cyclusYear + std::max(month,0) * cyclusMonth;
40✔
361

362
  // if time is 0, then invalid combination of year and month were given
363
  if (time == 0 ) {
36✔
364
    CLOG(LEV_WARN) << "Invalid year and month combination given to Timer::CalcTimeDiff. Returning 0. "
4✔
365
                   "Year: " << year << " Month: " << month;
×
366

367
    return 0;
4✔
368
  } 
369

370
  // Casting because ctx_->dt() is uint64_t and so negatives don't play nice
371
  return (time - start_time) / static_cast<int>(ctx_->dt());
32✔
372

373
}
374

375

376
}  // namespace cyclus
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc