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

openmc-dev / openmc / 19102841639

05 Nov 2025 01:01PM UTC coverage: 82.019% (-3.1%) from 85.155%
19102841639

Pull #3252

github

web-flow
Merge 3c71decac into bd76fc056
Pull Request #3252: Adding vtkhdf option to write vtk data

16722 of 23233 branches covered (71.98%)

Branch coverage included in aggregate %.

61 of 66 new or added lines in 1 file covered. (92.42%)

3177 existing lines in 103 files now uncovered.

54247 of 63294 relevant lines covered (85.71%)

42990146.99 hits per line

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

83.36
/src/random_ray/random_ray_simulation.cpp
1
#include "openmc/random_ray/random_ray_simulation.h"
2

3
#include "openmc/eigenvalue.h"
4
#include "openmc/geometry.h"
5
#include "openmc/message_passing.h"
6
#include "openmc/mgxs_interface.h"
7
#include "openmc/output.h"
8
#include "openmc/plot.h"
9
#include "openmc/random_ray/flat_source_domain.h"
10
#include "openmc/random_ray/random_ray.h"
11
#include "openmc/simulation.h"
12
#include "openmc/source.h"
13
#include "openmc/tallies/filter.h"
14
#include "openmc/tallies/tally.h"
15
#include "openmc/tallies/tally_scoring.h"
16
#include "openmc/timer.h"
17
#include "openmc/weight_windows.h"
18

19
namespace openmc {
20

21
//==============================================================================
22
// Non-member functions
23
//==============================================================================
24

25
void openmc_run_random_ray()
577✔
26
{
27
  //////////////////////////////////////////////////////////
28
  // Run forward simulation
29
  //////////////////////////////////////////////////////////
30

31
  // Check if adjoint calculation is needed. If it is, we will run the forward
32
  // calculation first and then the adjoint calculation later.
33
  bool adjoint_needed = FlatSourceDomain::adjoint_;
577✔
34

35
  // Configure the domain for forward simulation
36
  FlatSourceDomain::adjoint_ = false;
577✔
37

38
  // If we're going to do an adjoint simulation afterwards, report that this is
39
  // the initial forward flux solve.
40
  if (adjoint_needed && mpi::master)
577✔
41
    header("FORWARD FLUX SOLVE", 3);
56✔
42

43
  // Initialize OpenMC general data structures
44
  openmc_simulation_init();
577✔
45

46
  // Validate that inputs meet requirements for random ray mode
47
  if (mpi::master)
577✔
48
    validate_random_ray_inputs();
397✔
49

50
  // Initialize Random Ray Simulation Object
51
  RandomRaySimulation sim;
577✔
52

53
  // Initialize fixed sources, if present
54
  sim.apply_fixed_sources_and_mesh_domains();
577✔
55

56
  // Begin main simulation timer
57
  simulation::time_total.start();
577✔
58

59
  // Execute random ray simulation
60
  sim.simulate();
577✔
61

62
  // End main simulation timer
63
  simulation::time_total.stop();
577✔
64

65
  // Normalize and save the final forward flux
66
  double source_normalization_factor =
67
    sim.domain()->compute_fixed_source_normalization_factor() /
577✔
68
    (settings::n_batches - settings::n_inactive);
577✔
69

70
#pragma omp parallel for
325✔
71
  for (uint64_t se = 0; se < sim.domain()->n_source_elements(); se++) {
1,159,472✔
72
    sim.domain()->source_regions_.scalar_flux_final(se) *=
1,159,220✔
73
      source_normalization_factor;
74
  }
75

76
  // Finalize OpenMC
77
  openmc_simulation_finalize();
577✔
78

79
  // Output all simulation results
80
  sim.output_simulation_results();
577✔
81

82
  //////////////////////////////////////////////////////////
83
  // Run adjoint simulation (if enabled)
84
  //////////////////////////////////////////////////////////
85

86
  if (!adjoint_needed) {
577✔
87
    return;
496✔
88
  }
89

90
  reset_timers();
81✔
91

92
  // Configure the domain for adjoint simulation
93
  FlatSourceDomain::adjoint_ = true;
81✔
94

95
  if (mpi::master)
81✔
96
    header("ADJOINT FLUX SOLVE", 3);
56✔
97

98
  // Initialize OpenMC general data structures
99
  openmc_simulation_init();
81✔
100

101
  sim.domain()->k_eff_ = 1.0;
81✔
102

103
  // Initialize adjoint fixed sources, if present
104
  sim.prepare_fixed_sources_adjoint();
81✔
105

106
  // Transpose scattering matrix
107
  sim.domain()->transpose_scattering_matrix();
81✔
108

109
  // Swap nu_sigma_f and chi
110
  sim.domain()->nu_sigma_f_.swap(sim.domain()->chi_);
81✔
111

112
  // Begin main simulation timer
113
  simulation::time_total.start();
81✔
114

115
  // Execute random ray simulation
116
  sim.simulate();
81✔
117

118
  // End main simulation timer
119
  simulation::time_total.stop();
81✔
120

121
  // Finalize OpenMC
122
  openmc_simulation_finalize();
81✔
123

124
  // Output all simulation results
125
  sim.output_simulation_results();
81✔
126
}
577✔
127

128
// Enforces restrictions on inputs in random ray mode.  While there are
129
// many features that don't make sense in random ray mode, and are therefore
130
// unsupported, we limit our testing/enforcement operations only to inputs
131
// that may cause erroneous/misleading output or crashes from the solver.
132
void validate_random_ray_inputs()
397✔
133
{
134
  // Validate tallies
135
  ///////////////////////////////////////////////////////////////////
136
  for (auto& tally : model::tallies) {
1,278✔
137

138
    // Validate score types
139
    for (auto score_bin : tally->scores_) {
1,938✔
140
      switch (score_bin) {
1,057!
141
      case SCORE_FLUX:
1,057✔
142
      case SCORE_TOTAL:
143
      case SCORE_FISSION:
144
      case SCORE_NU_FISSION:
145
      case SCORE_EVENTS:
146
        break;
1,057✔
UNCOV
147
      default:
×
UNCOV
148
        fatal_error(
×
149
          "Invalid score specified. Only flux, total, fission, nu-fission, and "
150
          "event scores are supported in random ray mode.");
151
      }
152
    }
153

154
    // Validate filter types
155
    for (auto f : tally->filters()) {
1,918✔
156
      auto& filter = *model::tally_filters[f];
1,037✔
157

158
      switch (filter.type()) {
1,037!
159
      case FilterType::CELL:
1,037✔
160
      case FilterType::CELL_INSTANCE:
161
      case FilterType::DISTRIBCELL:
162
      case FilterType::ENERGY:
163
      case FilterType::MATERIAL:
164
      case FilterType::MESH:
165
      case FilterType::UNIVERSE:
166
      case FilterType::PARTICLE:
167
        break;
1,037✔
UNCOV
168
      default:
×
UNCOV
169
        fatal_error("Invalid filter specified. Only cell, cell_instance, "
×
170
                    "distribcell, energy, material, mesh, and universe filters "
171
                    "are supported in random ray mode.");
172
      }
173
    }
174
  }
175

176
  // Validate MGXS data
177
  ///////////////////////////////////////////////////////////////////
178
  for (auto& material : data::mg.macro_xs_) {
1,476✔
179
    if (!material.is_isotropic) {
1,079!
UNCOV
180
      fatal_error("Anisotropic MGXS detected. Only isotropic XS data sets "
×
181
                  "supported in random ray mode.");
182
    }
183
    if (material.get_xsdata().size() > 1) {
1,079!
184
      warning("Non-isothermal MGXS detected. Only isothermal XS data sets "
×
185
              "supported in random ray mode. Using lowest temperature.");
186
    }
187
    for (int g = 0; g < data::mg.num_energy_groups_; g++) {
5,591✔
188
      if (material.exists_in_model) {
4,512✔
189
        // Temperature and angle indices, if using multiple temperature
190
        // data sets and/or anisotropic data sets.
191
        // TODO: Currently assumes we are only using single temp/single angle
192
        // data.
193
        const int t = 0;
4,468✔
194
        const int a = 0;
4,468✔
195
        double sigma_t =
196
          material.get_xs(MgxsType::TOTAL, g, NULL, NULL, NULL, t, a);
4,468✔
197
        if (sigma_t <= 0.0) {
4,468!
UNCOV
198
          fatal_error("No zero or negative total macroscopic cross sections "
×
199
                      "allowed in random ray mode. If the intention is to make "
200
                      "a void material, use a cell fill of 'None' instead.");
201
        }
202
      }
203
    }
204
  }
205

206
  // Validate ray source
207
  ///////////////////////////////////////////////////////////////////
208

209
  // Check for independent source
210
  IndependentSource* is =
211
    dynamic_cast<IndependentSource*>(RandomRay::ray_source_.get());
397!
212
  if (!is) {
397!
213
    fatal_error("Invalid ray source definition. Ray source must provided and "
×
214
                "be of type IndependentSource.");
215
  }
216

217
  // Check for box source
218
  SpatialDistribution* space_dist = is->space();
397✔
219
  SpatialBox* sb = dynamic_cast<SpatialBox*>(space_dist);
397!
220
  if (!sb) {
397!
UNCOV
221
    fatal_error(
×
222
      "Invalid ray source definition -- only box sources are allowed.");
223
  }
224

225
  // Check that box source is not restricted to fissionable areas
226
  if (sb->only_fissionable()) {
397!
UNCOV
227
    fatal_error(
×
228
      "Invalid ray source definition -- fissionable spatial distribution "
229
      "not allowed.");
230
  }
231

232
  // Check for isotropic source
233
  UnitSphereDistribution* angle_dist = is->angle();
397✔
234
  Isotropic* id = dynamic_cast<Isotropic*>(angle_dist);
397!
235
  if (!id) {
397!
236
    fatal_error("Invalid ray source definition -- only isotropic sources are "
×
237
                "allowed.");
238
  }
239

240
  // Validate external sources
241
  ///////////////////////////////////////////////////////////////////
242
  if (settings::run_mode == RunMode::FIXED_SOURCE) {
397✔
243
    if (model::external_sources.size() < 1) {
276!
UNCOV
244
      fatal_error("Must provide a particle source (in addition to ray source) "
×
245
                  "in fixed source random ray mode.");
246
    }
247

248
    for (int i = 0; i < model::external_sources.size(); i++) {
552✔
249
      Source* s = model::external_sources[i].get();
276✔
250

251
      // Check for independent source
252
      IndependentSource* is = dynamic_cast<IndependentSource*>(s);
276!
253

254
      if (!is) {
276!
UNCOV
255
        fatal_error(
×
256
          "Only IndependentSource external source types are allowed in "
257
          "random ray mode");
258
      }
259

260
      // Check for isotropic source
261
      UnitSphereDistribution* angle_dist = is->angle();
276✔
262
      Isotropic* id = dynamic_cast<Isotropic*>(angle_dist);
276!
263
      if (!id) {
276!
UNCOV
264
        fatal_error(
×
265
          "Invalid source definition -- only isotropic external sources are "
266
          "allowed in random ray mode.");
267
      }
268

269
      // Validate that a domain ID was specified OR that it is a point source
270
      auto sp = dynamic_cast<SpatialPoint*>(is->space());
276!
271
      if (is->domain_ids().size() == 0 && !sp) {
276!
UNCOV
272
        fatal_error("Fixed sources must be point source or spatially "
×
273
                    "constrained by domain id (cell, material, or universe) in "
274
                    "random ray mode.");
275
      } else if (is->domain_ids().size() > 0 && sp) {
276✔
276
        // If both a domain constraint and a non-default point source location
277
        // are specified, notify user that domain constraint takes precedence.
278
        if (sp->r().x == 0.0 && sp->r().y == 0.0 && sp->r().z == 0.0) {
242!
279
          warning("Fixed source has both a domain constraint and a point "
242✔
280
                  "type spatial distribution. The domain constraint takes "
281
                  "precedence in random ray mode -- point source coordinate "
282
                  "will be ignored.");
283
        }
284
      }
285

286
      // Check that a discrete energy distribution was used
287
      Distribution* d = is->energy();
276✔
288
      Discrete* dd = dynamic_cast<Discrete*>(d);
276!
289
      if (!dd) {
276!
UNCOV
290
        fatal_error(
×
291
          "Only discrete (multigroup) energy distributions are allowed for "
292
          "external sources in random ray mode.");
293
      }
294
    }
295
  }
296

297
  // Validate plotting files
298
  ///////////////////////////////////////////////////////////////////
299
  for (int p = 0; p < model::plots.size(); p++) {
397!
300

301
    // Get handle to OpenMC plot object
UNCOV
302
    const auto& openmc_plottable = model::plots[p];
×
UNCOV
303
    Plot* openmc_plot = dynamic_cast<Plot*>(openmc_plottable.get());
×
304

305
    // Random ray plots only support voxel plots
UNCOV
306
    if (!openmc_plot) {
×
UNCOV
307
      warning(fmt::format(
×
308
        "Plot {} will not be used for end of simulation data plotting -- only "
309
        "voxel plotting is allowed in random ray mode.",
UNCOV
310
        openmc_plottable->id()));
×
UNCOV
311
      continue;
×
UNCOV
312
    } else if (openmc_plot->type_ != Plot::PlotType::voxel) {
×
UNCOV
313
      warning(fmt::format(
×
314
        "Plot {} will not be used for end of simulation data plotting -- only "
315
        "voxel plotting is allowed in random ray mode.",
UNCOV
316
        openmc_plottable->id()));
×
317
      continue;
×
318
    }
319
  }
320

321
  // Warn about slow MPI domain replication, if detected
322
  ///////////////////////////////////////////////////////////////////
323
#ifdef OPENMC_MPI
324
  if (mpi::n_procs > 1) {
181✔
325
    warning(
180✔
326
      "MPI parallelism is not supported by the random ray solver. All work "
327
      "will be performed by rank 0. Domain decomposition may be implemented in "
328
      "the future to provide efficient MPI scaling.");
329
  }
330
#endif
331

332
  // Warn about instability resulting from linear sources in small regions
333
  // when generating weight windows with FW-CADIS and an overlaid mesh.
334
  ///////////////////////////////////////////////////////////////////
335
  if (RandomRay::source_shape_ == RandomRaySourceShape::LINEAR &&
529✔
336
      variance_reduction::weight_windows.size() > 0) {
132✔
337
    warning(
11✔
338
      "Linear sources may result in negative fluxes in small source regions "
339
      "generated by mesh subdivision. Negative sources may result in low "
340
      "quality FW-CADIS weight windows. We recommend you use flat source mode "
341
      "when generating weight windows with an overlaid mesh tally.");
342
  }
343
}
397✔
344

345
void openmc_reset_random_ray()
8,104✔
346
{
347
  FlatSourceDomain::volume_estimator_ = RandomRayVolumeEstimator::HYBRID;
8,104✔
348
  FlatSourceDomain::volume_normalized_flux_tallies_ = false;
8,104✔
349
  FlatSourceDomain::adjoint_ = false;
8,104✔
350
  FlatSourceDomain::mesh_domain_map_.clear();
8,104✔
351
  RandomRay::ray_source_.reset();
8,104✔
352
  RandomRay::source_shape_ = RandomRaySourceShape::FLAT;
8,104✔
353
  RandomRay::sample_method_ = RandomRaySampleMethod::PRNG;
8,104✔
354
}
8,104✔
355

356
//==============================================================================
357
// RandomRaySimulation implementation
358
//==============================================================================
359

360
RandomRaySimulation::RandomRaySimulation()
577✔
361
  : negroups_(data::mg.num_energy_groups_)
577✔
362
{
363
  // There are no source sites in random ray mode, so be sure to disable to
364
  // ensure we don't attempt to write source sites to statepoint
365
  settings::source_write = false;
577✔
366

367
  // Random ray mode does not have an inner loop over generations within a
368
  // batch, so set the current gen to 1
369
  simulation::current_gen = 1;
577✔
370

371
  switch (RandomRay::source_shape_) {
577!
372
  case RandomRaySourceShape::FLAT:
337✔
373
    domain_ = make_unique<FlatSourceDomain>();
337✔
374
    break;
337✔
375
  case RandomRaySourceShape::LINEAR:
240✔
376
  case RandomRaySourceShape::LINEAR_XY:
377
    domain_ = make_unique<LinearSourceDomain>();
240✔
378
    break;
240✔
UNCOV
379
  default:
×
UNCOV
380
    fatal_error("Unknown random ray source shape");
×
381
  }
382

383
  // Convert OpenMC native MGXS into a more efficient format
384
  // internal to the random ray solver
385
  domain_->flatten_xs();
577✔
386
}
577✔
387

388
void RandomRaySimulation::apply_fixed_sources_and_mesh_domains()
577✔
389
{
390
  domain_->apply_meshes();
577✔
391
  if (settings::run_mode == RunMode::FIXED_SOURCE) {
577✔
392
    // Transfer external source user inputs onto random ray source regions
393
    domain_->convert_external_sources();
401✔
394
    domain_->count_external_source_regions();
401✔
395
  }
396
}
577✔
397

398
void RandomRaySimulation::prepare_fixed_sources_adjoint()
81✔
399
{
400
  domain_->source_regions_.adjoint_reset();
81✔
401
  if (settings::run_mode == RunMode::FIXED_SOURCE) {
81✔
402
    domain_->set_adjoint_sources();
65✔
403
  }
404
}
81✔
405

406
void RandomRaySimulation::simulate()
658✔
407
{
408
  // Random ray power iteration loop
409
  while (simulation::current_batch < settings::n_batches) {
17,630✔
410
    // Initialize the current batch
411
    initialize_batch();
16,972✔
412
    initialize_generation();
16,972✔
413

414
    // MPI not supported in random ray solver, so all work is done by rank 0
415
    // TODO: Implement domain decomposition for MPI parallelism
416
    if (mpi::master) {
16,972✔
417

418
      // Reset total starting particle weight used for normalizing tallies
419
      simulation::total_weight = 1.0;
11,672✔
420

421
      // Update source term (scattering + fission)
422
      domain_->update_all_neutron_sources();
11,672✔
423

424
      // Reset scalar fluxes, iteration volume tallies, and region hit flags
425
      // to zero
426
      domain_->batch_reset();
11,672✔
427

428
      // At the beginning of the simulation, if mesh subdivision is in use, we
429
      // need to swap the main source region container into the base container,
430
      // as the main source region container will be used to hold the true
431
      // subdivided source regions. The base container will therefore only
432
      // contain the external source region information, the mesh indices,
433
      // material properties, and initial guess values for the flux/source.
434

435
      // Start timer for transport
436
      simulation::time_transport.start();
11,672✔
437

438
// Transport sweep over all random rays for the iteration
439
#pragma omp parallel for schedule(dynamic)                                     \
6,372✔
440
  reduction(+ : total_geometric_intersections_)
6,372✔
441
      for (int i = 0; i < settings::n_particles; i++) {
853,800✔
442
        RandomRay ray(i, domain_.get());
848,500✔
443
        total_geometric_intersections_ +=
848,500✔
444
          ray.transport_history_based_single_ray();
848,500✔
445
      }
848,500✔
446

447
      simulation::time_transport.stop();
11,672✔
448

449
      // Add any newly discovered source regions to the main source region
450
      // container.
451
      domain_->finalize_discovered_source_regions();
11,672✔
452

453
      // Normalize scalar flux and update volumes
454
      domain_->normalize_scalar_flux_and_volumes(
11,672✔
455
        settings::n_particles * RandomRay::distance_active_);
456

457
      // Add source to scalar flux, compute number of FSR hits
458
      int64_t n_hits = domain_->add_source_to_scalar_flux();
11,672✔
459

460
      // Apply transport stabilization factors
461
      domain_->apply_transport_stabilization();
11,672✔
462

463
      if (settings::run_mode == RunMode::EIGENVALUE) {
11,672✔
464
        // Compute random ray k-eff
465
        domain_->compute_k_eff();
2,090✔
466

467
        // Store random ray k-eff into OpenMC's native k-eff variable
468
        global_tally_tracklength = domain_->k_eff_;
2,090✔
469
      }
470

471
      // Execute all tallying tasks, if this is an active batch
472
      if (simulation::current_batch > settings::n_inactive) {
11,672✔
473

474
        // Add this iteration's scalar flux estimate to final accumulated
475
        // estimate
476
        domain_->accumulate_iteration_flux();
4,736✔
477

478
        // Use above mapping to contribute FSR flux data to appropriate
479
        // tallies
480
        domain_->random_ray_tally();
4,736✔
481
      }
482

483
      // Set phi_old = phi_new
484
      domain_->flux_swap();
11,672✔
485

486
      // Check for any obvious insabilities/nans/infs
487
      instability_check(n_hits, domain_->k_eff_, avg_miss_rate_);
11,672✔
488
    } // End MPI master work
489

490
    // Finalize the current batch
491
    finalize_generation();
16,972✔
492
    finalize_batch();
16,972✔
493
  } // End random ray power iteration loop
494

495
  domain_->count_external_source_regions();
658✔
496
}
658✔
497

498
void RandomRaySimulation::output_simulation_results() const
658✔
499
{
500
  // Print random ray results
501
  if (mpi::master) {
658✔
502
    print_results_random_ray(total_geometric_intersections_,
453✔
503
      avg_miss_rate_ / settings::n_batches, negroups_,
453✔
504
      domain_->n_source_regions(), domain_->n_external_source_regions_);
453✔
505
    if (model::plots.size() > 0) {
453!
UNCOV
506
      domain_->output_to_vtk();
×
507
    }
508
  }
509
}
658✔
510

511
// Apply a few sanity checks to catch obvious cases of numerical instability.
512
// Instability typically only occurs if ray density is extremely low.
513
void RandomRaySimulation::instability_check(
11,672✔
514
  int64_t n_hits, double k_eff, double& avg_miss_rate) const
515
{
516
  double percent_missed = ((domain_->n_source_regions() - n_hits) /
11,672✔
517
                            static_cast<double>(domain_->n_source_regions())) *
11,672✔
518
                          100.0;
11,672✔
519
  avg_miss_rate += percent_missed;
11,672✔
520

521
  if (mpi::master) {
11,672!
522
    if (percent_missed > 10.0) {
11,672✔
523
      warning(fmt::format(
957✔
524
        "Very high FSR miss rate detected ({:.3f}%). Instability may occur. "
525
        "Increase ray density by adding more rays and/or active distance.",
526
        percent_missed));
527
    } else if (percent_missed > 1.0) {
10,715✔
528
      warning(
2!
529
        fmt::format("Elevated FSR miss rate detected ({:.3f}%). Increasing "
4!
530
                    "ray density by adding more rays and/or active "
531
                    "distance may improve simulation efficiency.",
532
          percent_missed));
533
    }
534

535
    if (k_eff > 10.0 || k_eff < 0.01 || !(std::isfinite(k_eff))) {
11,672!
UNCOV
536
      fatal_error(fmt::format("Instability detected: k-eff = {:.5f}", k_eff));
×
537
    }
538
  }
539
}
11,672✔
540

541
// Print random ray simulation results
542
void RandomRaySimulation::print_results_random_ray(
453✔
543
  uint64_t total_geometric_intersections, double avg_miss_rate, int negroups,
544
  int64_t n_source_regions, int64_t n_external_source_regions) const
545
{
546
  using namespace simulation;
547

548
  if (settings::verbosity >= 6) {
453!
549
    double total_integrations = total_geometric_intersections * negroups;
453✔
550
    double time_per_integration =
551
      simulation::time_transport.elapsed() / total_integrations;
453✔
552
    double misc_time = time_total.elapsed() - time_update_src.elapsed() -
453✔
553
                       time_transport.elapsed() - time_tallies.elapsed() -
453✔
554
                       time_bank_sendrecv.elapsed();
453✔
555

556
    header("Simulation Statistics", 4);
453✔
557
    fmt::print(
453✔
558
      " Total Iterations                  = {}\n", settings::n_batches);
559
    fmt::print(
453✔
560
      " Number of Rays per Iteration      = {}\n", settings::n_particles);
561
    fmt::print(" Inactive Distance                 = {} cm\n",
453✔
562
      RandomRay::distance_inactive_);
563
    fmt::print(" Active Distance                   = {} cm\n",
453✔
564
      RandomRay::distance_active_);
565
    fmt::print(" Source Regions (SRs)              = {}\n", n_source_regions);
453✔
566
    fmt::print(
371✔
567
      " SRs Containing External Sources   = {}\n", n_external_source_regions);
568
    fmt::print(" Total Geometric Intersections     = {:.4e}\n",
371✔
569
      static_cast<double>(total_geometric_intersections));
453✔
570
    fmt::print("   Avg per Iteration               = {:.4e}\n",
371✔
571
      static_cast<double>(total_geometric_intersections) / settings::n_batches);
453✔
572
    fmt::print("   Avg per Iteration per SR        = {:.2f}\n",
371✔
573
      static_cast<double>(total_geometric_intersections) /
453✔
574
        static_cast<double>(settings::n_batches) / n_source_regions);
906✔
575
    fmt::print(" Avg SR Miss Rate per Iteration    = {:.4f}%\n", avg_miss_rate);
453✔
576
    fmt::print(" Energy Groups                     = {}\n", negroups);
453✔
577
    fmt::print(
371✔
578
      " Total Integrations                = {:.4e}\n", total_integrations);
579
    fmt::print("   Avg per Iteration               = {:.4e}\n",
371✔
580
      total_integrations / settings::n_batches);
453✔
581

582
    std::string estimator;
453✔
583
    switch (domain_->volume_estimator_) {
453!
584
    case RandomRayVolumeEstimator::SIMULATION_AVERAGED:
22✔
585
      estimator = "Simulation Averaged";
22✔
586
      break;
22✔
587
    case RandomRayVolumeEstimator::NAIVE:
68✔
588
      estimator = "Naive";
68✔
589
      break;
68✔
590
    case RandomRayVolumeEstimator::HYBRID:
363✔
591
      estimator = "Hybrid";
363✔
592
      break;
363✔
UNCOV
593
    default:
×
UNCOV
594
      fatal_error("Invalid volume estimator type");
×
595
    }
596
    fmt::print(" Volume Estimator Type             = {}\n", estimator);
371✔
597

598
    std::string adjoint_true = (FlatSourceDomain::adjoint_) ? "ON" : "OFF";
1,359✔
599
    fmt::print(" Adjoint Flux Mode                 = {}\n", adjoint_true);
371✔
600

601
    std::string shape;
906✔
602
    switch (RandomRay::source_shape_) {
453!
603
    case RandomRaySourceShape::FLAT:
277✔
604
      shape = "Flat";
277✔
605
      break;
277✔
606
    case RandomRaySourceShape::LINEAR:
143✔
607
      shape = "Linear";
143✔
608
      break;
143✔
609
    case RandomRaySourceShape::LINEAR_XY:
33✔
610
      shape = "Linear XY";
33✔
611
      break;
33✔
UNCOV
612
    default:
×
UNCOV
613
      fatal_error("Invalid random ray source shape");
×
614
    }
615
    fmt::print(" Source Shape                      = {}\n", shape);
371✔
616
    std::string sample_method =
617
      (RandomRay::sample_method_ == RandomRaySampleMethod::PRNG) ? "PRNG"
453✔
618
                                                                 : "Halton";
1,359✔
619
    fmt::print(" Sample Method                     = {}\n", sample_method);
371✔
620

621
    if (domain_->is_transport_stabilization_needed_) {
453✔
622
      fmt::print(" Transport XS Stabilization Used   = YES (rho = {:.3f})\n",
11✔
623
        FlatSourceDomain::diagonal_stabilization_rho_);
624
    } else {
625
      fmt::print(" Transport XS Stabilization Used   = NO\n");
442✔
626
    }
627

628
    header("Timing Statistics", 4);
453✔
629
    show_time("Total time for initialization", time_initialize.elapsed());
453✔
630
    show_time("Reading cross sections", time_read_xs.elapsed(), 1);
453✔
631
    show_time("Total simulation time", time_total.elapsed());
453✔
632
    show_time("Transport sweep only", time_transport.elapsed(), 1);
453✔
633
    show_time("Source update only", time_update_src.elapsed(), 1);
453✔
634
    show_time("Tally conversion only", time_tallies.elapsed(), 1);
453✔
635
    show_time("MPI source reductions only", time_bank_sendrecv.elapsed(), 1);
453✔
636
    show_time("Other iteration routines", misc_time, 1);
453✔
637
    if (settings::run_mode == RunMode::EIGENVALUE) {
453✔
638
      show_time("Time in inactive batches", time_inactive.elapsed());
132✔
639
    }
640
    show_time("Time in active batches", time_active.elapsed());
453✔
641
    show_time("Time writing statepoints", time_statepoint.elapsed());
453✔
642
    show_time("Total time for finalization", time_finalize.elapsed());
453✔
643
    show_time("Time per integration", time_per_integration);
453✔
644
  }
453✔
645

646
  if (settings::verbosity >= 4 && settings::run_mode == RunMode::EIGENVALUE) {
453!
647
    header("Results", 4);
132✔
648
    fmt::print(" k-effective                       = {:.5f} +/- {:.5f}\n",
132✔
649
      simulation::keff, simulation::keff_std);
650
  }
651
}
453✔
652

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

© 2025 Coveralls, Inc