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

cyclus / cyclus / 26773857321

01 Jun 2026 06:26PM UTC coverage: 36.715% (+0.02%) from 36.694%
26773857321

push

github

web-flow
Merge pull request #1907 from gonuke/timeshift

Timeshift

51 of 54 new or added lines in 3 files covered. (94.44%)

52720 of 143591 relevant lines covered (36.72%)

16261.92 hits per line

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

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

5
#include <iostream>
6
#include <string>
7
#if CYCLUS_IS_PARALLEL
8
#include <omp.h>
9
#endif  // CYCLUS_IS_PARALLEL
10

11
#include "agent.h"
12
#include "error.h"
13
#include "logger.h"
14
#include "pyhooks.h"
15
#include "sim_init.h"
16

17
namespace cyclus {
18

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

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

30
  ExchangeManager<Material> matl_manager(ctx_);
266✔
31
  ExchangeManager<Product> genrsrc_manager(ctx_);
266✔
32
  while (time_ < si_.duration) {
7,798✔
33
    CLOG(LEV_INFO1) << "Current time: " << time_;
7,537✔
34

35
    if (want_snapshot_) {
7,537✔
36
      want_snapshot_ = false;
28✔
37
      SimInit::Snapshot(ctx_);
28✔
38
    }
39

40
    // run through phases
41
    DoBuild();
7,537✔
42
    CLOG(LEV_INFO2) << "Beginning Tick for time: " << time_;
7,537✔
43
    DoTick();
7,537✔
44
    CLOG(LEV_INFO2) << "Beginning DRE for time: " << time_;
7,537✔
45
    DoResEx(&matl_manager, &genrsrc_manager);
7,537✔
46
    CLOG(LEV_INFO2) << "Beginning Tock for time: " << time_;
7,536✔
47
    DoTock();
7,536✔
48
    CLOG(LEV_INFO2) << "Beginning Decision for time: " << time_;
7,536✔
49
    DoDecision();
7,536✔
50
    DoDecom();
7,536✔
51

52
#ifdef CYCLUS_WITH_PYTHON
53
    EventLoop();
7,536✔
54
#endif
55

56
    time_++;
7,536✔
57

58
    if (want_kill_) {
7,536✔
59
      break;
60
    }
61
  }
62

63
  ctx_->NewDatum("Finish")
265✔
64
      ->AddVal("EarlyTerm", want_kill_)
65
      ->AddVal("EndTime", time_ - 1)
265✔
66
      ->Record();
530✔
67

68
  SimInit::Snapshot(
265✔
69
      ctx_);  // always do a snapshot at the end of every simulation
70

71
  if (quiet_) {
265✔
72
    Logger::SetReportLevel(saved_level);
73
  }
74
}
265✔
75

76
void Timer::DoBuild() {
7,537✔
77
  // build queued agents
78
  std::vector<std::pair<std::string, Agent*>> build_list = build_queue_[time_];
7,537✔
79
  for (int i = 0; i < build_list.size(); ++i) {
23,358✔
80
    Agent* m = ctx_->CreateAgent<Agent>(build_list[i].first);
47,463✔
81
    Agent* parent = build_list[i].second;
15,821✔
82
    CLOG(LEV_INFO3) << "Building a " << build_list[i].first << " from parent "
15,821✔
83
                    << build_list[i].second;
×
84
    m->Build(parent);
15,821✔
85
    if (parent != NULL) {
15,821✔
86
      parent->BuildNotify(m);
61✔
87
    } else {
88
      CLOG(LEV_DEBUG1) << "Hey! Listen! Built an Agent without a Parent.";
15,760✔
89
    }
90
  }
91
}
7,537✔
92

93
void Timer::DoTick() {
7,537✔
94
  for (TimeListener* agent : py_tickers_) {
73,889✔
95
    agent->Tick();
66,352✔
96
  }
97

98
#pragma omp parallel for
7,537✔
99
  for (size_t i = 0; i < cpp_tickers_.size(); ++i) {
100
    cpp_tickers_[i]->Tick();
101
  }
102
}
7,537✔
103

104
void Timer::DoResEx(ExchangeManager<Material>* matmgr,
7,537✔
105
                    ExchangeManager<Product>* genmgr) {
106
  matmgr->Execute();
7,537✔
107
  genmgr->Execute();
7,536✔
108
}
7,536✔
109

110
void Timer::DoTock() {
7,536✔
111
  for (TimeListener* agent : py_tickers_) {
73,886✔
112
    agent->Tock();
66,350✔
113
  }
114

115
#pragma omp parallel for
7,536✔
116
  for (size_t i = 0; i < cpp_tickers_.size(); ++i) {
117
    cpp_tickers_[i]->Tock();
118
  }
119

120
  if (si_.explicit_inventory || si_.explicit_inventory_compact) {
7,536✔
121
    std::set<Agent*> ags = ctx_->agent_list_;
170✔
122
    std::vector<Agent*> agent_vec(ags.begin(), ags.end());
170✔
123
#pragma omp parallel for
170✔
124
    for (int i = 0; i < agent_vec.size(); i++) {
125
      Agent* a = agent_vec[i];
126
      if (a->enter_time() != -1) {
127
        RecordInventories(a);
128
      }
129
    }
130
  }
131
}
7,536✔
132

133
void Timer::DoDecision() {
7,536✔
134
  for (std::map<int, TimeListener*>::iterator agent = tickers_.begin();
7,536✔
135
       agent != tickers_.end();
73,826✔
136
       agent++) {
137
    agent->second->Decision();
66,290✔
138
  }
139
}
7,536✔
140

141
void Timer::RecordInventories(Agent* a) {
510✔
142
  Inventories invs = a->SnapshotInv();
510✔
143
  Inventories::iterator it2;
144
  for (it2 = invs.begin(); it2 != invs.end(); ++it2) {
680✔
145
    std::string name = it2->first;
146
    std::vector<Resource::Ptr> mats = it2->second;
170✔
147
    if (mats.empty() || ResCast<Material>(mats[0]) == NULL) {
680✔
148
      continue;  // skip non-material inventories
149
    }
150

151
    Material::Ptr m = ResCast<Material>(mats[0]->Clone());
170✔
152
    for (int i = 1; i < mats.size(); i++) {
935✔
153
      m->Absorb(ResCast<Material>(mats[i]->Clone()));
3,060✔
154
    }
155
    RecordInventory(a, name, m);
510✔
156
  }
170✔
157
}
510✔
158

159
void Timer::RecordInventory(Agent* a, std::string name, Material::Ptr m) {
170✔
160
  if (si_.explicit_inventory) {
170✔
161
    CompMap c = m->comp()->mass();
180✔
162
    compmath::Normalize(&c, m->quantity());
90✔
163
    CompMap::iterator it;
164
    for (it = c.begin(); it != c.end(); ++it) {
270✔
165
      ctx_->NewDatum("ExplicitInventory")
180✔
166
          ->AddVal("AgentId", a->id())
180✔
167
          ->AddVal("Time", time_)
168
          ->AddVal("InventoryName", name)
169
          ->AddVal("NucId", it->first)
170
          ->AddVal("Quantity", it->second)
171
          ->AddVal("Units", m->units())
360✔
172
          ->Record();
1,260✔
173
    }
174
  }
175

176
  if (si_.explicit_inventory_compact) {
170✔
177
    CompMap c = m->comp()->mass();
80✔
178
    compmath::Normalize(&c, 1);
80✔
179
    ctx_->NewDatum("ExplicitInventoryCompact")
80✔
180
        ->AddVal("AgentId", a->id())
80✔
181
        ->AddVal("Time", time_)
182
        ->AddVal("InventoryName", name)
183
        ->AddVal("Quantity", m->quantity())
80✔
184
        ->AddVal("Units", m->units())
240✔
185
        ->AddVal("Composition", c)
186
        ->Record();
720✔
187
  }
188
}
170✔
189

190
void Timer::DoDecom() {
7,536✔
191
  // decommission queued agents
192
  std::vector<Agent*> decom_list = decom_queue_[time_];
7,536✔
193
  for (int i = 0; i < decom_list.size(); ++i) {
19,205✔
194
    Agent* m = decom_list[i];
11,669✔
195
    if (m->parent() != NULL) {
11,669✔
196
      m->parent()->DecomNotify(m);
824✔
197
    }
198
    m->Decommission();
11,669✔
199
  }
200
}
7,536✔
201

202
void Timer::RegisterTimeListener(TimeListener* agent) {
18,923✔
203
  tickers_[agent->id()] = agent;
18,923✔
204
  if (agent->IsShim()) {
18,923✔
205
    py_tickers_.push_back(agent);
18,923✔
206
  } else {
207
    cpp_tickers_.push_back(agent);
×
208
  }
209
}
18,923✔
210

211
void Timer::UnregisterTimeListener(TimeListener* tl) {
11,668✔
212
  tickers_.erase(tl->id());
11,668✔
213
  if (tl->IsShim()) {
11,668✔
214
    py_tickers_.erase(std::remove(py_tickers_.begin(), py_tickers_.end(), tl),
11,668✔
215
                      py_tickers_.end());
216
  } else {
217
    cpp_tickers_.erase(
×
218
        std::remove(cpp_tickers_.begin(), cpp_tickers_.end(), tl),
×
219
        cpp_tickers_.end());
220
  }
221
}
11,668✔
222

223
void Timer::SchedBuild(Agent* parent, std::string proto_name, int t) {
21,029✔
224
  if (t <= time_) {
21,029✔
225
    throw ValueError("Cannot schedule build for t < [current-time]");
×
226
  }
227
  build_queue_[t].push_back(std::make_pair(proto_name, parent));
21,029✔
228
}
21,029✔
229

230
void Timer::SchedDecom(Agent* m, int t) {
11,858✔
231
  if (t < time_) {
11,858✔
232
    throw ValueError("Cannot schedule decommission for t < [current-time]");
×
233
  }
234

235
  // It is possible that a single agent may be scheduled for decommissioning
236
  // multiple times. If this happens, we cannot just add it to the queue again
237
  // - the duplicate entries will result in a double delete attempt and
238
  // segfaults and otherwise bad things.  Remove previous decommissionings
239
  // before scheduling this new one.
240
  std::map<int, std::vector<Agent*>>::iterator it;
241
  bool done = false;
242
  for (it = decom_queue_.begin(); it != decom_queue_.end(); ++it) {
109,721✔
243
    int t = it->first;
99,517✔
244
    std::vector<Agent*> ags = it->second;
99,517✔
245
    for (int i = 0; i < ags.size(); i++) {
6,857,654✔
246
      if (ags[i] == m) {
6,759,791✔
247
        CLOG(LEV_WARN) << "scheduled over previous decommissioning of "
1,654✔
248
                       << m->id();
×
249
        decom_queue_[t].erase(decom_queue_[t].begin() + i);
1,654✔
250
        done = true;
251
        break;
252
      }
253
    }
254
    if (done) {
255
      break;
256
    }
257
  }
258

259
  decom_queue_[t].push_back(m);
11,858✔
260
}
11,858✔
261

262
int Timer::time() {
473,722✔
263
  return time_;
473,722✔
264
}
265

266
void Timer::Reset() {
×
267
  tickers_.clear();
268
  cpp_tickers_.clear();
269
  py_tickers_.clear();
270
  build_queue_.clear();
271
  decom_queue_.clear();
272
  si_ = SimInfo(0);
×
273
}
×
274

275
void Timer::Initialize(Context* ctx, SimInfo si) {
618✔
276
  if (si.m0 < 1 || si.m0 > 12) {
618✔
277
    throw ValueError("Invalid month0; must be between 1 and 12 (inclusive).");
×
278
  }
279

280
  want_kill_ = false;
618✔
281
  ctx_ = ctx;
618✔
282
  time_ = 0;
618✔
283
  si_ = si;
618✔
284

285
  if (si.branch_time > -1) {
618✔
286
    time_ = si.branch_time;
4✔
287
  }
288
}
618✔
289

290
int Timer::dur() {
×
291
  return si_.duration;
×
292
}
293

294
int Timer::CalcTimeDiff(int year, int month) {
36✔
295

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

299
  // if time is 0, then invalid combination of year and month were given
300
  if (time == 0 ) {
36✔
301
    CLOG(LEV_WARN) << "Invalid year and month combination given to Timer::CalcTimeDiff. Returning 0. "
4✔
NEW
302
                   "Year: " << year << " Month: " << month;
×
303

304
    return 0;
4✔
305
  } 
306

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

310
}
311

312

313
Timer::Timer() : time_(0), si_(0), want_snapshot_(false), want_kill_(false) {}
2,678✔
314

315
}  // 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