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

openmc-dev / openmc / 12601844593

03 Jan 2025 06:01PM UTC coverage: 84.823% (-0.003%) from 84.826%
12601844593

Pull #3129

github

web-flow
Merge 7fc29c9d4 into 775c41512
Pull Request #3129: Compute material volumes in mesh elements based on raytracing

194 of 220 new or added lines in 4 files covered. (88.18%)

1044 existing lines in 27 files now uncovered.

49959 of 58898 relevant lines covered (84.82%)

33907140.98 hits per line

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

87.5
/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

18
namespace openmc {
19

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

24
void openmc_run_random_ray()
357✔
25
{
26
  //////////////////////////////////////////////////////////
27
  // Run forward simulation
28
  //////////////////////////////////////////////////////////
29

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

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

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

42
  // Initialize OpenMC general data structures
43
  openmc_simulation_init();
357✔
44

45
  // Validate that inputs meet requirements for random ray mode
46
  if (mpi::master)
357✔
47
    validate_random_ray_inputs();
252✔
48

49
  // Declare forward flux so that it can be saved for later adjoint simulation
50
  vector<double> forward_flux;
357✔
51

52
  {
53
    // Initialize Random Ray Simulation Object
54
    RandomRaySimulation sim;
357✔
55

56
    // Initialize fixed sources, if present
57
    sim.prepare_fixed_sources();
357✔
58

59
    // Begin main simulation timer
60
    simulation::time_total.start();
357✔
61

62
    // Execute random ray simulation
63
    sim.simulate();
357✔
64

65
    // End main simulation timer
66
    simulation::time_total.stop();
357✔
67

68
    // Normalize and save the final forward flux
69
    forward_flux = sim.domain()->scalar_flux_final_;
357✔
70

71
    double source_normalization_factor =
72
      sim.domain()->compute_fixed_source_normalization_factor() /
357✔
73
      (settings::n_batches - settings::n_inactive);
357✔
74

75
#pragma omp parallel for
189✔
76
    for (uint64_t i = 0; i < forward_flux.size(); i++) {
291,432✔
77
      forward_flux[i] *= source_normalization_factor;
291,264✔
78
    }
79

80
    // Finalize OpenMC
81
    openmc_simulation_finalize();
357✔
82

83
    // Reduce variables across MPI ranks
84
    sim.reduce_simulation_statistics();
357✔
85

86
    // Output all simulation results
87
    sim.output_simulation_results();
357✔
88
  }
357✔
89

90
  //////////////////////////////////////////////////////////
91
  // Run adjoint simulation (if enabled)
92
  //////////////////////////////////////////////////////////
93

94
  if (adjoint_needed) {
357✔
95
    reset_timers();
34✔
96

97
    // Configure the domain for adjoint simulation
98
    FlatSourceDomain::adjoint_ = true;
34✔
99

100
    if (mpi::master)
34✔
101
      header("ADJOINT FLUX SOLVE", 3);
24✔
102

103
    // Initialize OpenMC general data structures
104
    openmc_simulation_init();
34✔
105

106
    // Initialize Random Ray Simulation Object
107
    RandomRaySimulation adjoint_sim;
34✔
108

109
    // Initialize adjoint fixed sources, if present
110
    adjoint_sim.prepare_fixed_sources_adjoint(forward_flux);
34✔
111

112
    // Transpose scattering matrix
113
    adjoint_sim.domain()->transpose_scattering_matrix();
34✔
114

115
    // Swap nu_sigma_f and chi
116
    adjoint_sim.domain()->nu_sigma_f_.swap(adjoint_sim.domain()->chi_);
34✔
117

118
    // Begin main simulation timer
119
    simulation::time_total.start();
34✔
120

121
    // Execute random ray simulation
122
    adjoint_sim.simulate();
34✔
123

124
    // End main simulation timer
125
    simulation::time_total.stop();
34✔
126

127
    // Finalize OpenMC
128
    openmc_simulation_finalize();
34✔
129

130
    // Reduce variables across MPI ranks
131
    adjoint_sim.reduce_simulation_statistics();
34✔
132

133
    // Output all simulation results
134
    adjoint_sim.output_simulation_results();
34✔
135
  }
34✔
136
}
357✔
137

138
// Enforces restrictions on inputs in random ray mode.  While there are
139
// many features that don't make sense in random ray mode, and are therefore
140
// unsupported, we limit our testing/enforcement operations only to inputs
141
// that may cause erroneous/misleading output or crashes from the solver.
142
void validate_random_ray_inputs()
252✔
143
{
144
  // Validate tallies
145
  ///////////////////////////////////////////////////////////////////
146
  for (auto& tally : model::tallies) {
828✔
147

148
    // Validate score types
149
    for (auto score_bin : tally->scores_) {
1,296✔
150
      switch (score_bin) {
720✔
151
      case SCORE_FLUX:
720✔
152
      case SCORE_TOTAL:
153
      case SCORE_FISSION:
154
      case SCORE_NU_FISSION:
155
      case SCORE_EVENTS:
156
        break;
720✔
UNCOV
157
      default:
×
UNCOV
158
        fatal_error(
×
159
          "Invalid score specified. Only flux, total, fission, nu-fission, and "
160
          "event scores are supported in random ray mode.");
161
      }
162
    }
163

164
    // Validate filter types
165
    for (auto f : tally->filters()) {
1,224✔
166
      auto& filter = *model::tally_filters[f];
648✔
167

168
      switch (filter.type()) {
648✔
169
      case FilterType::CELL:
648✔
170
      case FilterType::CELL_INSTANCE:
171
      case FilterType::DISTRIBCELL:
172
      case FilterType::ENERGY:
173
      case FilterType::MATERIAL:
174
      case FilterType::MESH:
175
      case FilterType::UNIVERSE:
176
        break;
648✔
UNCOV
177
      default:
×
UNCOV
178
        fatal_error("Invalid filter specified. Only cell, cell_instance, "
×
179
                    "distribcell, energy, material, mesh, and universe filters "
180
                    "are supported in random ray mode.");
181
      }
182
    }
183
  }
184

185
  // Validate MGXS data
186
  ///////////////////////////////////////////////////////////////////
187
  for (auto& material : data::mg.macro_xs_) {
912✔
188
    if (!material.is_isotropic) {
660✔
UNCOV
189
      fatal_error("Anisotropic MGXS detected. Only isotropic XS data sets "
×
190
                  "supported in random ray mode.");
191
    }
192
    if (material.get_xsdata().size() > 1) {
660✔
UNCOV
193
      fatal_error("Non-isothermal MGXS detected. Only isothermal XS data sets "
×
194
                  "supported in random ray mode.");
195
    }
196
  }
197

198
  // Validate ray source
199
  ///////////////////////////////////////////////////////////////////
200

201
  // Check for independent source
202
  IndependentSource* is =
203
    dynamic_cast<IndependentSource*>(RandomRay::ray_source_.get());
252✔
204
  if (!is) {
252✔
UNCOV
205
    fatal_error("Invalid ray source definition. Ray source must provided and "
×
206
                "be of type IndependentSource.");
207
  }
208

209
  // Check for box source
210
  SpatialDistribution* space_dist = is->space();
252✔
211
  SpatialBox* sb = dynamic_cast<SpatialBox*>(space_dist);
252✔
212
  if (!sb) {
252✔
UNCOV
213
    fatal_error(
×
214
      "Invalid ray source definition -- only box sources are allowed.");
215
  }
216

217
  // Check that box source is not restricted to fissionable areas
218
  if (sb->only_fissionable()) {
252✔
UNCOV
219
    fatal_error(
×
220
      "Invalid ray source definition -- fissionable spatial distribution "
221
      "not allowed.");
222
  }
223

224
  // Check for isotropic source
225
  UnitSphereDistribution* angle_dist = is->angle();
252✔
226
  Isotropic* id = dynamic_cast<Isotropic*>(angle_dist);
252✔
227
  if (!id) {
252✔
UNCOV
228
    fatal_error("Invalid ray source definition -- only isotropic sources are "
×
229
                "allowed.");
230
  }
231

232
  // Validate external sources
233
  ///////////////////////////////////////////////////////////////////
234
  if (settings::run_mode == RunMode::FIXED_SOURCE) {
252✔
235
    if (model::external_sources.size() < 1) {
192✔
UNCOV
236
      fatal_error("Must provide a particle source (in addition to ray source) "
×
237
                  "in fixed source random ray mode.");
238
    }
239

240
    for (int i = 0; i < model::external_sources.size(); i++) {
384✔
241
      Source* s = model::external_sources[i].get();
192✔
242

243
      // Check for independent source
244
      IndependentSource* is = dynamic_cast<IndependentSource*>(s);
192✔
245

246
      if (!is) {
192✔
UNCOV
247
        fatal_error(
×
248
          "Only IndependentSource external source types are allowed in "
249
          "random ray mode");
250
      }
251

252
      // Check for isotropic source
253
      UnitSphereDistribution* angle_dist = is->angle();
192✔
254
      Isotropic* id = dynamic_cast<Isotropic*>(angle_dist);
192✔
255
      if (!id) {
192✔
UNCOV
256
        fatal_error(
×
257
          "Invalid source definition -- only isotropic external sources are "
258
          "allowed in random ray mode.");
259
      }
260

261
      // Validate that a domain ID was specified
262
      if (is->domain_ids().size() == 0) {
192✔
UNCOV
263
        fatal_error("Fixed sources must be specified by domain "
×
264
                    "id (cell, material, or universe) in random ray mode.");
265
      }
266

267
      // Check that a discrete energy distribution was used
268
      Distribution* d = is->energy();
192✔
269
      Discrete* dd = dynamic_cast<Discrete*>(d);
192✔
270
      if (!dd) {
192✔
UNCOV
271
        fatal_error(
×
272
          "Only discrete (multigroup) energy distributions are allowed for "
273
          "external sources in random ray mode.");
274
      }
275
    }
276
  }
277

278
  // Validate plotting files
279
  ///////////////////////////////////////////////////////////////////
280
  for (int p = 0; p < model::plots.size(); p++) {
252✔
281

282
    // Get handle to OpenMC plot object
UNCOV
283
    Plot* openmc_plot = dynamic_cast<Plot*>(model::plots[p].get());
×
284

285
    // Random ray plots only support voxel plots
UNCOV
286
    if (!openmc_plot) {
×
UNCOV
287
      warning(fmt::format(
×
288
        "Plot {} will not be used for end of simulation data plotting -- only "
289
        "voxel plotting is allowed in random ray mode.",
290
        p));
UNCOV
291
      continue;
×
UNCOV
292
    } else if (openmc_plot->type_ != Plot::PlotType::voxel) {
×
UNCOV
293
      warning(fmt::format(
×
294
        "Plot {} will not be used for end of simulation data plotting -- only "
295
        "voxel plotting is allowed in random ray mode.",
296
        p));
UNCOV
297
      continue;
×
298
    }
299
  }
300

301
  // Warn about slow MPI domain replication, if detected
302
  ///////////////////////////////////////////////////////////////////
303
#ifdef OPENMC_MPI
304
  if (mpi::n_procs > 1) {
105✔
305
    warning(
105✔
306
      "Domain replication in random ray is supported, but suffers from poor "
307
      "scaling of source all-reduce operations. Performance may severely "
308
      "degrade beyond just a few MPI ranks. Domain decomposition may be "
309
      "implemented in the future to provide efficient scaling.");
310
  }
311
#endif
312
}
252✔
313

314
//==============================================================================
315
// RandomRaySimulation implementation
316
//==============================================================================
317

318
RandomRaySimulation::RandomRaySimulation()
391✔
319
  : negroups_(data::mg.num_energy_groups_)
391✔
320
{
321
  // There are no source sites in random ray mode, so be sure to disable to
322
  // ensure we don't attempt to write source sites to statepoint
323
  settings::source_write = false;
391✔
324

325
  // Random ray mode does not have an inner loop over generations within a
326
  // batch, so set the current gen to 1
327
  simulation::current_gen = 1;
391✔
328

329
  switch (RandomRay::source_shape_) {
391✔
330
  case RandomRaySourceShape::FLAT:
255✔
331
    domain_ = make_unique<FlatSourceDomain>();
255✔
332
    break;
255✔
333
  case RandomRaySourceShape::LINEAR:
136✔
334
  case RandomRaySourceShape::LINEAR_XY:
335
    domain_ = make_unique<LinearSourceDomain>();
136✔
336
    break;
136✔
UNCOV
337
  default:
×
UNCOV
338
    fatal_error("Unknown random ray source shape");
×
339
  }
340

341
  // Convert OpenMC native MGXS into a more efficient format
342
  // internal to the random ray solver
343
  domain_->flatten_xs();
391✔
344
}
391✔
345

346
void RandomRaySimulation::prepare_fixed_sources()
357✔
347
{
348
  if (settings::run_mode == RunMode::FIXED_SOURCE) {
357✔
349
    // Transfer external source user inputs onto random ray source regions
350
    domain_->convert_external_sources();
272✔
351
    domain_->count_external_source_regions();
272✔
352
  }
353
}
357✔
354

355
void RandomRaySimulation::prepare_fixed_sources_adjoint(
34✔
356
  vector<double>& forward_flux)
357
{
358
  if (settings::run_mode == RunMode::FIXED_SOURCE) {
34✔
359
    domain_->set_adjoint_sources(forward_flux);
17✔
360
  }
361
}
34✔
362

363
void RandomRaySimulation::simulate()
391✔
364
{
365
  // Random ray power iteration loop
366
  while (simulation::current_batch < settings::n_batches) {
11,781✔
367

368
    // Initialize the current batch
369
    initialize_batch();
11,390✔
370
    initialize_generation();
11,390✔
371

372
    // Reset total starting particle weight used for normalizing tallies
373
    simulation::total_weight = 1.0;
11,390✔
374

375
    // Update source term (scattering + fission)
376
    domain_->update_neutron_source(k_eff_);
11,390✔
377

378
    // Reset scalar fluxes, iteration volume tallies, and region hit flags to
379
    // zero
380
    domain_->batch_reset();
11,390✔
381

382
    // Start timer for transport
383
    simulation::time_transport.start();
11,390✔
384

385
// Transport sweep over all random rays for the iteration
386
#pragma omp parallel for schedule(dynamic)                                     \
6,030✔
387
  reduction(+ : total_geometric_intersections_)
6,030✔
388
    for (int i = 0; i < simulation::work_per_rank; i++) {
284,360✔
389
      RandomRay ray(i, domain_.get());
279,000✔
390
      total_geometric_intersections_ +=
279,000✔
391
        ray.transport_history_based_single_ray();
279,000✔
392
    }
279,000✔
393

394
    simulation::time_transport.stop();
11,390✔
395

396
    // If using multiple MPI ranks, perform all reduce on all transport results
397
    domain_->all_reduce_replicated_source_regions();
11,390✔
398

399
    // Normalize scalar flux and update volumes
400
    domain_->normalize_scalar_flux_and_volumes(
11,390✔
401
      settings::n_particles * RandomRay::distance_active_);
402

403
    // Add source to scalar flux, compute number of FSR hits
404
    int64_t n_hits = domain_->add_source_to_scalar_flux();
11,390✔
405

406
    if (settings::run_mode == RunMode::EIGENVALUE) {
11,390✔
407
      // Compute random ray k-eff
408
      k_eff_ = domain_->compute_k_eff(k_eff_);
2,040✔
409

410
      // Store random ray k-eff into OpenMC's native k-eff variable
411
      global_tally_tracklength = k_eff_;
2,040✔
412
    }
413

414
    // Execute all tallying tasks, if this is an active batch
415
    if (simulation::current_batch > settings::n_inactive) {
11,390✔
416

417
      // Add this iteration's scalar flux estimate to final accumulated estimate
418
      domain_->accumulate_iteration_flux();
4,420✔
419

420
      if (mpi::master) {
4,420✔
421
        // Generate mapping between source regions and tallies
422
        if (!domain_->mapped_all_tallies_) {
3,120✔
423
          domain_->convert_source_regions_to_tallies();
276✔
424
        }
425

426
        // Use above mapping to contribute FSR flux data to appropriate tallies
427
        domain_->random_ray_tally();
3,120✔
428
      }
429
    }
430

431
    // Set phi_old = phi_new
432
    domain_->flux_swap();
11,390✔
433

434
    // Check for any obvious insabilities/nans/infs
435
    instability_check(n_hits, k_eff_, avg_miss_rate_);
11,390✔
436

437
    // Finalize the current batch
438
    finalize_generation();
11,390✔
439
    finalize_batch();
11,390✔
440
  } // End random ray power iteration loop
441
}
391✔
442

443
void RandomRaySimulation::reduce_simulation_statistics()
391✔
444
{
445
  // Reduce number of intersections
446
#ifdef OPENMC_MPI
447
  if (mpi::n_procs > 1) {
230✔
448
    uint64_t total_geometric_intersections_reduced = 0;
230✔
449
    MPI_Reduce(&total_geometric_intersections_,
230✔
450
      &total_geometric_intersections_reduced, 1, MPI_UNSIGNED_LONG, MPI_SUM, 0,
451
      mpi::intracomm);
452
    total_geometric_intersections_ = total_geometric_intersections_reduced;
230✔
453
  }
454
#endif
455
}
391✔
456

457
void RandomRaySimulation::output_simulation_results() const
391✔
458
{
459
  // Print random ray results
460
  if (mpi::master) {
391✔
461
    print_results_random_ray(total_geometric_intersections_,
552✔
462
      avg_miss_rate_ / settings::n_batches, negroups_,
276✔
463
      domain_->n_source_regions_, domain_->n_external_source_regions_);
276✔
464
    if (model::plots.size() > 0) {
276✔
UNCOV
465
      domain_->output_to_vtk();
×
466
    }
467
  }
468
}
391✔
469

470
// Apply a few sanity checks to catch obvious cases of numerical instability.
471
// Instability typically only occurs if ray density is extremely low.
472
void RandomRaySimulation::instability_check(
11,390✔
473
  int64_t n_hits, double k_eff, double& avg_miss_rate) const
474
{
475
  double percent_missed = ((domain_->n_source_regions_ - n_hits) /
11,390✔
476
                            static_cast<double>(domain_->n_source_regions_)) *
11,390✔
477
                          100.0;
11,390✔
478
  avg_miss_rate += percent_missed;
11,390✔
479

480
  if (mpi::master) {
11,390✔
481
    if (percent_missed > 10.0) {
8,040✔
UNCOV
482
      warning(fmt::format(
×
483
        "Very high FSR miss rate detected ({:.3f}%). Instability may occur. "
484
        "Increase ray density by adding more rays and/or active distance.",
485
        percent_missed));
486
    } else if (percent_missed > 1.0) {
8,040✔
UNCOV
487
      warning(
×
UNCOV
488
        fmt::format("Elevated FSR miss rate detected ({:.3f}%). Increasing "
×
489
                    "ray density by adding more rays and/or active "
490
                    "distance may improve simulation efficiency.",
491
          percent_missed));
492
    }
493

494
    if (k_eff > 10.0 || k_eff < 0.01 || !(std::isfinite(k_eff))) {
8,040✔
UNCOV
495
      fatal_error("Instability detected");
×
496
    }
497
  }
498
}
11,390✔
499

500
// Print random ray simulation results
501
void RandomRaySimulation::print_results_random_ray(
276✔
502
  uint64_t total_geometric_intersections, double avg_miss_rate, int negroups,
503
  int64_t n_source_regions, int64_t n_external_source_regions) const
504
{
505
  using namespace simulation;
506

507
  if (settings::verbosity >= 6) {
276✔
508
    double total_integrations = total_geometric_intersections * negroups;
276✔
509
    double time_per_integration =
510
      simulation::time_transport.elapsed() / total_integrations;
276✔
511
    double misc_time = time_total.elapsed() - time_update_src.elapsed() -
276✔
512
                       time_transport.elapsed() - time_tallies.elapsed() -
276✔
513
                       time_bank_sendrecv.elapsed();
276✔
514

515
    header("Simulation Statistics", 4);
276✔
516
    fmt::print(
276✔
517
      " Total Iterations                  = {}\n", settings::n_batches);
518
    fmt::print(" Flat Source Regions (FSRs)        = {}\n", n_source_regions);
276✔
519
    fmt::print(
230✔
520
      " FSRs Containing External Sources  = {}\n", n_external_source_regions);
521
    fmt::print(" Total Geometric Intersections     = {:.4e}\n",
230✔
522
      static_cast<double>(total_geometric_intersections));
276✔
523
    fmt::print("   Avg per Iteration               = {:.4e}\n",
230✔
524
      static_cast<double>(total_geometric_intersections) / settings::n_batches);
276✔
525
    fmt::print("   Avg per Iteration per FSR       = {:.2f}\n",
230✔
526
      static_cast<double>(total_geometric_intersections) /
276✔
527
        static_cast<double>(settings::n_batches) / n_source_regions);
552✔
528
    fmt::print(" Avg FSR Miss Rate per Iteration   = {:.4f}%\n", avg_miss_rate);
276✔
529
    fmt::print(" Energy Groups                     = {}\n", negroups);
276✔
530
    fmt::print(
230✔
531
      " Total Integrations                = {:.4e}\n", total_integrations);
532
    fmt::print("   Avg per Iteration               = {:.4e}\n",
230✔
533
      total_integrations / settings::n_batches);
276✔
534

535
    std::string estimator;
276✔
536
    switch (domain_->volume_estimator_) {
276✔
537
    case RandomRayVolumeEstimator::SIMULATION_AVERAGED:
24✔
538
      estimator = "Simulation Averaged";
24✔
539
      break;
24✔
540
    case RandomRayVolumeEstimator::NAIVE:
24✔
541
      estimator = "Naive";
24✔
542
      break;
24✔
543
    case RandomRayVolumeEstimator::HYBRID:
228✔
544
      estimator = "Hybrid";
228✔
545
      break;
228✔
UNCOV
546
    default:
×
UNCOV
547
      fatal_error("Invalid volume estimator type");
×
548
    }
549
    fmt::print(" Volume Estimator Type             = {}\n", estimator);
230✔
550

551
    std::string adjoint_true = (FlatSourceDomain::adjoint_) ? "ON" : "OFF";
828✔
552
    fmt::print(" Adjoint Flux Mode                 = {}\n", adjoint_true);
230✔
553

554
    header("Timing Statistics", 4);
276✔
555
    show_time("Total time for initialization", time_initialize.elapsed());
276✔
556
    show_time("Reading cross sections", time_read_xs.elapsed(), 1);
276✔
557
    show_time("Total simulation time", time_total.elapsed());
276✔
558
    show_time("Transport sweep only", time_transport.elapsed(), 1);
276✔
559
    show_time("Source update only", time_update_src.elapsed(), 1);
276✔
560
    show_time("Tally conversion only", time_tallies.elapsed(), 1);
276✔
561
    show_time("MPI source reductions only", time_bank_sendrecv.elapsed(), 1);
276✔
562
    show_time("Other iteration routines", misc_time, 1);
276✔
563
    if (settings::run_mode == RunMode::EIGENVALUE) {
276✔
564
      show_time("Time in inactive batches", time_inactive.elapsed());
72✔
565
    }
566
    show_time("Time in active batches", time_active.elapsed());
276✔
567
    show_time("Time writing statepoints", time_statepoint.elapsed());
276✔
568
    show_time("Total time for finalization", time_finalize.elapsed());
276✔
569
    show_time("Time per integration", time_per_integration);
276✔
570
  }
276✔
571

572
  if (settings::verbosity >= 4 && settings::run_mode == RunMode::EIGENVALUE) {
276✔
573
    header("Results", 4);
72✔
574
    fmt::print(" k-effective                       = {:.5f} +/- {:.5f}\n",
72✔
575
      simulation::keff, simulation::keff_std);
576
  }
577
}
276✔
578

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