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

openmc-dev / openmc / 27444237628

12 Jun 2026 09:31PM UTC coverage: 81.304% (+0.02%) from 81.281%
27444237628

Pull #3971

github

web-flow
Merge 9487507b3 into 02eb999af
Pull Request #3971: Delta tracking

18480 of 26797 branches covered (68.96%)

Branch coverage included in aggregate %.

592 of 644 new or added lines in 20 files covered. (91.93%)

31 existing lines in 3 files now uncovered.

59819 of 69507 relevant lines covered (86.06%)

49514105.74 hits per line

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

83.04
/src/event.cpp
1
#include "openmc/event.h"
2

3
#include "openmc/bank.h"
4
#include "openmc/error.h"
5
#include "openmc/material.h"
6
#include "openmc/settings.h"
7
#include "openmc/simulation.h"
8
#include "openmc/timer.h"
9

10
namespace openmc {
11

12
//==============================================================================
13
// Global variables
14
//==============================================================================
15

16
namespace simulation {
17

18
SharedArray<EventQueueItem> calculate_fuel_xs_queue;
19
SharedArray<EventQueueItem> calculate_nonfuel_xs_queue;
20
SharedArray<EventQueueItem> advance_particle_queue;
21
SharedArray<EventQueueItem> surface_crossing_queue;
22
SharedArray<EventQueueItem> collision_queue;
23

24
vector<Particle> particles;
25

26
} // namespace simulation
27

28
//==============================================================================
29
// Non-member functions
30
//==============================================================================
31

32
void init_event_queues(int64_t n_particles)
293✔
33
{
34
  simulation::calculate_fuel_xs_queue.reserve(n_particles);
293✔
35
  simulation::calculate_nonfuel_xs_queue.reserve(n_particles);
293✔
36
  simulation::advance_particle_queue.reserve(n_particles);
293✔
37
  simulation::surface_crossing_queue.reserve(n_particles);
293✔
38
  simulation::collision_queue.reserve(n_particles);
293✔
39

40
  simulation::particles.resize(n_particles);
293✔
41
}
293✔
42

43
void free_event_queues(void)
×
44
{
45
  simulation::calculate_fuel_xs_queue.clear();
×
46
  simulation::calculate_nonfuel_xs_queue.clear();
×
47
  simulation::advance_particle_queue.clear();
×
48
  simulation::surface_crossing_queue.clear();
×
49
  simulation::collision_queue.clear();
×
50

51
  simulation::particles.clear();
×
52
}
×
53

54
void dispatch_xs_event(int64_t buffer_idx)
265,091,237✔
55
{
56
  Particle& p = simulation::particles[buffer_idx];
265,091,237✔
57
  if (p.material() == MATERIAL_VOID ||
265,091,237✔
58
      !model::materials[p.material()]->fissionable()) {
247,420,174✔
59
    simulation::calculate_nonfuel_xs_queue.thread_safe_append({p, buffer_idx});
76,199,271✔
60
  } else {
61
    simulation::calculate_fuel_xs_queue.thread_safe_append({p, buffer_idx});
188,891,966✔
62
  }
63
}
265,091,237✔
64

65
void process_death_events(int64_t n_particles)
3,996✔
66
{
67
  simulation::time_event_death.start();
3,996✔
68
#pragma omp parallel for schedule(runtime)
3,876✔
69
  for (int64_t i = 0; i < n_particles; i++) {
100,120✔
70
    Particle& p = simulation::particles[i];
100,000✔
71
    p.event_death();
100,000✔
72
  }
73
  simulation::time_event_death.stop();
3,996✔
74
}
3,996✔
75

76
//==============================================================================
77
// Functions for surface tracking
78
//==============================================================================
79

80
void process_init_events(int64_t n_particles, int64_t source_offset)
3,221✔
81
{
82
  simulation::time_event_init.start();
3,221✔
83
#pragma omp parallel for schedule(runtime)
3,221✔
84
  for (int64_t i = 0; i < n_particles; i++) {
×
85
    initialize_particle_track(
86
      simulation::particles[i], source_offset + i + 1, false);
87
    dispatch_xs_event(i);
88
  }
89
  simulation::time_event_init.stop();
3,221✔
90
}
3,221✔
91

92
void process_calculate_xs_events(SharedArray<EventQueueItem>& queue)
1,275,261✔
93
{
94
  simulation::time_event_calculate_xs.start();
1,275,261✔
95

96
  // TODO: If using C++17, we could perform a parallel sort of the queue by
97
  // particle type, material type, and then energy, in order to improve cache
98
  // locality and reduce thread divergence on GPU. However, the parallel
99
  // algorithms typically require linking against an additional library (Intel
100
  // TBB). Prior to C++17, std::sort is a serial only operation, which in this
101
  // case makes it too slow to be practical for most test problems.
102
  //
103
  // std::sort(std::execution::par_unseq, queue.data(), queue.data() +
104
  // queue.size());
105

106
  int64_t offset = simulation::advance_particle_queue.size();
1,275,261✔
107

108
#pragma omp parallel for schedule(runtime)
1,275,261✔
109
  for (int64_t i = 0; i < queue.size(); i++) {
×
110
    Particle* p = &simulation::particles[queue[i].idx];
111
    p->event_calculate_xs();
112

113
    // After executing a calculate_xs event, particles will
114
    // always require an advance event. Therefore, we don't need to use
115
    // the protected enqueuing function.
116
    simulation::advance_particle_queue[offset + i] = queue[i];
117
  }
118

119
  simulation::advance_particle_queue.resize(offset + queue.size());
1,275,261✔
120

121
  queue.resize(0);
1,275,261✔
122

123
  simulation::time_event_calculate_xs.stop();
1,275,261✔
124
}
1,275,261✔
125

126
void process_advance_particle_events()
1,269,971✔
127
{
128
  simulation::time_event_advance_particle.start();
1,269,971✔
129

130
#pragma omp parallel for schedule(runtime)
1,269,971✔
131
  for (int64_t i = 0; i < simulation::advance_particle_queue.size(); i++) {
×
132
    int64_t buffer_idx = simulation::advance_particle_queue[i].idx;
133
    Particle& p = simulation::particles[buffer_idx];
134
    p.event_advance();
135
    if (!p.alive())
×
136
      continue;
137
    if (p.collision_distance() > p.boundary().distance()) {
×
138
      simulation::surface_crossing_queue.thread_safe_append({p, buffer_idx});
139
    } else {
140
      simulation::collision_queue.thread_safe_append({p, buffer_idx});
141
    }
142
  }
143

144
  simulation::advance_particle_queue.resize(0);
1,269,971✔
145

146
  simulation::time_event_advance_particle.stop();
1,269,971✔
147
}
1,269,971✔
148

149
void process_surface_crossing_events()
265,955✔
150
{
151
  simulation::time_event_surface_crossing.start();
265,955✔
152

153
#pragma omp parallel for schedule(runtime)
265,955✔
154
  for (int64_t i = 0; i < simulation::surface_crossing_queue.size(); i++) {
×
155
    int64_t buffer_idx = simulation::surface_crossing_queue[i].idx;
156
    Particle& p = simulation::particles[buffer_idx];
157
    p.event_cross_surface();
158
    p.event_check_limit_and_revive();
159
    if (p.alive())
×
160
      dispatch_xs_event(buffer_idx);
161
  }
162

163
  simulation::surface_crossing_queue.resize(0);
265,955✔
164

165
  simulation::time_event_surface_crossing.stop();
265,955✔
166
}
265,955✔
167

168
void process_collision_events()
1,022,656✔
169
{
170
  simulation::time_event_collision.start();
1,022,656✔
171

172
#pragma omp parallel for schedule(runtime)
1,022,656✔
173
  for (int64_t i = 0; i < simulation::collision_queue.size(); i++) {
×
174
    int64_t buffer_idx = simulation::collision_queue[i].idx;
175
    Particle& p = simulation::particles[buffer_idx];
176
    p.event_collide();
177
    p.event_check_limit_and_revive();
178
    if (p.alive())
×
179
      dispatch_xs_event(buffer_idx);
180
  }
181

182
  simulation::collision_queue.resize(0);
1,022,656✔
183

184
  simulation::time_event_collision.stop();
1,022,656✔
185
}
1,022,656✔
186

187
void process_transport_events()
3,616✔
188
{
189
  while (true) {
3,837,459✔
190
    int64_t max = std::max({simulation::calculate_fuel_xs_queue.size(),
3,837,459✔
191
      simulation::calculate_nonfuel_xs_queue.size(),
3,837,459✔
192
      simulation::advance_particle_queue.size(),
3,837,459✔
193
      simulation::surface_crossing_queue.size(),
3,837,459✔
194
      simulation::collision_queue.size()});
3,837,459✔
195

196
    if (max == 0) {
3,837,459✔
197
      break;
198
    } else if (max == simulation::calculate_fuel_xs_queue.size()) {
3,833,843✔
199
      process_calculate_xs_events(simulation::calculate_fuel_xs_queue);
466,126✔
200
    } else if (max == simulation::calculate_nonfuel_xs_queue.size()) {
3,367,717✔
201
      process_calculate_xs_events(simulation::calculate_nonfuel_xs_queue);
809,135✔
202
    } else if (max == simulation::advance_particle_queue.size()) {
2,558,582✔
203
      process_advance_particle_events();
1,269,971✔
204
    } else if (max == simulation::surface_crossing_queue.size()) {
1,288,611✔
205
      process_surface_crossing_events();
265,955✔
206
    } else if (max == simulation::collision_queue.size()) {
1,022,656!
207
      process_collision_events();
1,022,656✔
208
    }
209
  }
210
}
3,616✔
211

212
void process_init_secondary_events(int64_t n_particles, int64_t offset,
395✔
213
  const SharedArray<SourceSite>& shared_secondary_bank)
214
{
215
  simulation::time_event_init.start();
395✔
216
#pragma omp parallel for schedule(runtime)
395✔
217
  for (int64_t i = 0; i < n_particles; i++) {
×
218
    initialize_particle_track(simulation::particles[i], offset + i + 1, true);
219
    const SourceSite& site = shared_secondary_bank[offset + i];
220
    simulation::particles[i].event_revive_from_secondary(site);
221
    if (simulation::particles[i].alive()) {
×
222
      dispatch_xs_event(i);
223
    }
224
  }
225
  simulation::time_event_init.stop();
395✔
226
}
395✔
227

228
//==============================================================================
229
// Functions for delta tracking
230
//==============================================================================
231

232
void process_delta_init_events(int64_t n_particles, int64_t source_offset)
380✔
233
{
234
  simulation::time_event_init.start();
380✔
235
#pragma omp parallel for schedule(runtime)
260✔
236
  for (int64_t i = 0; i < n_particles; i++) {
100,120✔
237
    simulation::particles[i].delta_tracking() = true;
100,000✔
238
    initialize_particle_track(
100,000✔
239
      simulation::particles[i], source_offset + i + 1, false);
100,000✔
240

241
    simulation::advance_particle_queue[i] = {simulation::particles[i], i};
100,000✔
242
  }
243
  simulation::advance_particle_queue.resize(n_particles);
380✔
244
  simulation::time_event_init.stop();
380✔
245
}
380✔
246

247
void process_delta_calculate_xs_events(SharedArray<EventQueueItem>& queue)
676,993✔
248
{
249
  simulation::time_event_calculate_xs.start();
676,993✔
250

251
  // TODO: If using C++17, we could perform a parallel sort of the queue by
252
  // particle type, material type, and then energy, in order to improve cache
253
  // locality and reduce thread divergence on GPU. However, the parallel
254
  // algorithms typically require linking against an additional library (Intel
255
  // TBB). Prior to C++17, std::sort is a serial only operation, which in this
256
  // case makes it too slow to be practical for most test problems.
257
  //
258
  // std::sort(std::execution::par_unseq, queue.data(), queue.data() +
259
  // queue.size());
260

261
  int64_t offset = simulation::collision_queue.size();
676,993✔
262

263
#pragma omp parallel for schedule(runtime)
444,624✔
264
  for (int64_t i = 0; i < queue.size(); i++) {
12,672,244✔
265
    Particle* p = &simulation::particles[queue[i].idx];
12,439,875✔
266
    p->event_calculate_xs();
12,439,875✔
267

268
    // After executing a calculate_xs event in delta tracking, particles will
269
    // always require a collision event. Therefore, we don't need to use
270
    // the protected enqueuing function.
271
    simulation::collision_queue[offset + i] = queue[i];
12,439,875✔
272
  }
273

274
  simulation::collision_queue.resize(offset + queue.size());
676,993✔
275

276
  queue.resize(0);
676,993✔
277

278
  simulation::time_event_calculate_xs.stop();
676,993✔
279
}
676,993✔
280

281
void process_delta_advance_particle_events()
1,190,828✔
282
{
283
  simulation::time_event_advance_particle.start();
1,190,828✔
284

285
#pragma omp parallel for schedule(runtime)
773,146✔
286
  for (int64_t i = 0; i < simulation::advance_particle_queue.size(); i++) {
37,931,482✔
287
    int64_t buffer_idx = simulation::advance_particle_queue[i].idx;
37,513,800✔
288
    Particle& p = simulation::particles[buffer_idx];
37,513,800✔
289
    p.event_delta_advance();
37,513,800✔
290
    if (!p.alive())
37,513,800!
291
      continue;
292

293
    if (p.type() == ParticleType::electron() ||
37,513,800✔
294
        p.type() == ParticleType::positron()) {
14,372,170✔
295
      // Electrons / positrons collide in place and don't require cross section
296
      // calculations. Can append to the collision queue directly.
297
      simulation::collision_queue.thread_safe_append({p, buffer_idx});
23,162,560✔
298
      continue;
23,162,560✔
299
    }
300

301
    if (p.collision_distance() < p.boundary().distance()) {
14,351,240✔
302
      // We need to compute cross sections prior to processing a collision.
303
      dispatch_xs_event(buffer_idx);
12,439,875✔
304
    } else {
305
      simulation::surface_crossing_queue.thread_safe_append({p, buffer_idx});
1,911,365✔
306
    }
307
  }
308

309
  simulation::advance_particle_queue.resize(0);
1,190,828✔
310

311
  simulation::time_event_advance_particle.stop();
1,190,828✔
312
}
1,190,828✔
313

314
void process_delta_surface_crossing_events()
56,625✔
315
{
316
  simulation::time_event_surface_crossing.start();
56,625✔
317

318
#pragma omp parallel for schedule(runtime)
36,685✔
319
  for (int64_t i = 0; i < simulation::surface_crossing_queue.size(); i++) {
1,931,305✔
320
    int64_t buffer_idx = simulation::surface_crossing_queue[i].idx;
1,911,365✔
321
    Particle& p = simulation::particles[buffer_idx];
1,911,365✔
322
    p.event_cross_surface();
1,911,365✔
323
    p.event_check_limit_and_revive();
1,911,365✔
324
    if (p.alive())
1,911,365!
325
      simulation::advance_particle_queue.thread_safe_append({p, buffer_idx});
1,911,365✔
326
  }
327

328
  simulation::surface_crossing_queue.resize(0);
56,625✔
329

330
  simulation::time_event_surface_crossing.stop();
56,625✔
331
}
56,625✔
332

333
void process_delta_collision_events()
1,138,577✔
334
{
335
  simulation::time_event_collision.start();
1,138,577✔
336

337
#pragma omp parallel for schedule(runtime)
739,492✔
338
  for (int64_t i = 0; i < simulation::collision_queue.size(); i++) {
36,001,520✔
339
    int64_t buffer_idx = simulation::collision_queue[i].idx;
35,602,435✔
340
    Particle& p = simulation::particles[buffer_idx];
35,602,435✔
341

342
    if (p.type() == ParticleType::electron() ||
35,602,435✔
343
        p.type() == ParticleType::positron()) {
12,460,805✔
344
      p.event_collide();
23,162,560✔
345
    } else {
346
      if (p.macro_xs().total / p.majorant() > 1.0) {
12,439,875!
347
        p.mark_as_lost(fmt::format(
×
348
          "Ratio of the total cross section ({}) to the majorant "
349
          "cross section ({}) for particle {} ({}) with energy {} is "
350
          "greater than unity!",
351
          p.macro_xs().total, p.majorant(), p.id(), p.type().str(), p.E()));
×
352
        continue;
353
      }
354

355
      if (prn(p.current_seed()) < (p.macro_xs().total / p.majorant())) {
12,439,875✔
356
        // Real collision, need to process the collision prior to enqueuing an
357
        // advance event.
358
        p.event_collide();
4,575,320✔
359
      }
360
    }
361

362
    p.event_check_limit_and_revive();
35,602,435✔
363
    if (p.alive()) {
35,602,435✔
364
      simulation::advance_particle_queue.thread_safe_append({p, buffer_idx});
35,502,435✔
365
    }
366
  }
367

368
  simulation::collision_queue.resize(0);
1,138,577✔
369

370
  simulation::time_event_collision.stop();
1,138,577✔
371
}
1,138,577✔
372

373
void process_delta_transport_events()
380✔
374
{
375
  while (true) {
3,063,403✔
376
    int64_t max = std::max({simulation::calculate_fuel_xs_queue.size(),
3,063,403✔
377
      simulation::calculate_nonfuel_xs_queue.size(),
3,063,403✔
378
      simulation::advance_particle_queue.size(),
3,063,403✔
379
      simulation::surface_crossing_queue.size(),
3,063,403✔
380
      simulation::collision_queue.size()});
3,063,403✔
381

382
    if (max == 0) {
3,063,403✔
383
      break;
384
    } else if (max == simulation::calculate_fuel_xs_queue.size()) {
3,063,023✔
385
      process_delta_calculate_xs_events(simulation::calculate_fuel_xs_queue);
142,598✔
386
    } else if (max == simulation::calculate_nonfuel_xs_queue.size()) {
2,920,425✔
387
      process_delta_calculate_xs_events(simulation::calculate_nonfuel_xs_queue);
534,395✔
388
    } else if (max == simulation::advance_particle_queue.size()) {
2,386,030✔
389
      process_delta_advance_particle_events();
1,190,828✔
390
    } else if (max == simulation::surface_crossing_queue.size()) {
1,195,202✔
391
      process_delta_surface_crossing_events();
56,625✔
392
    } else if (max == simulation::collision_queue.size()) {
1,138,577!
393
      process_delta_collision_events();
1,138,577✔
394
    }
395
  }
396
}
380✔
397

NEW
398
void process_delta_init_secondary_events(int64_t n_particles, int64_t offset,
×
399
  const SharedArray<SourceSite>& shared_secondary_bank)
400
{
NEW
401
  simulation::time_event_init.start();
×
402
#pragma omp parallel for schedule(runtime)
403
  for (int64_t i = 0; i < n_particles; i++) {
×
404
    simulation::particles[i].delta_tracking() = true;
405
    initialize_particle_track(simulation::particles[i], offset + i + 1, true);
406
    const SourceSite& site = shared_secondary_bank[offset + i];
407
    simulation::particles[i].event_revive_from_secondary(site);
408

409
    if (simulation::particles[i].alive()) {
×
410
      simulation::particles[i].event_calculate_xs();
411
      simulation::advance_particle_queue.thread_safe_append(
412
        {simulation::particles[i], i});
413
    }
414
  }
NEW
UNCOV
415
  simulation::time_event_init.stop();
×
NEW
UNCOV
416
}
×
417

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

© 2026 Coveralls, Inc