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

openmc-dev / openmc / 13341039720

15 Feb 2025 02:46AM UTC coverage: 85.006% (+0.1%) from 84.867%
13341039720

Pull #2655

github

web-flow
Merge 1b7b4ad1e into be4396c12
Pull Request #2655: Raytrace plots

402 of 453 new or added lines in 5 files covered. (88.74%)

1175 existing lines in 53 files now uncovered.

50436 of 59332 relevant lines covered (85.01%)

35668719.75 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()
374✔
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_;
374✔
33

34
  // Configure the domain for forward simulation
35
  FlatSourceDomain::adjoint_ = false;
374✔
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)
374✔
40
    header("FORWARD FLUX SOLVE", 3);
36✔
41

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

94
  if (adjoint_needed) {
374✔
95
    reset_timers();
51✔
96

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

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

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

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

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

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

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

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

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

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

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

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

133
    // Output all simulation results
134
    adjoint_sim.output_simulation_results();
51✔
135
  }
51✔
136
}
374✔
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()
264✔
143
{
144
  // Validate tallies
145
  ///////////////////////////////////////////////////////////////////
146
  for (auto& tally : model::tallies) {
888✔
147

148
    // Validate score types
149
    for (auto score_bin : tally->scores_) {
1,392✔
150
      switch (score_bin) {
768✔
151
      case SCORE_FLUX:
768✔
152
      case SCORE_TOTAL:
153
      case SCORE_FISSION:
154
      case SCORE_NU_FISSION:
155
      case SCORE_EVENTS:
156
        break;
768✔
157
      default:
×
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,344✔
166
      auto& filter = *model::tally_filters[f];
720✔
167

168
      switch (filter.type()) {
720✔
169
      case FilterType::CELL:
720✔
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
      case FilterType::PARTICLE:
177
        break;
720✔
178
      default:
×
UNCOV
179
        fatal_error("Invalid filter specified. Only cell, cell_instance, "
×
180
                    "distribcell, energy, material, mesh, and universe filters "
181
                    "are supported in random ray mode.");
182
      }
183
    }
184
  }
185

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

199
  // Validate ray source
200
  ///////////////////////////////////////////////////////////////////
201

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

315
//==============================================================================
316
// RandomRaySimulation implementation
317
//==============================================================================
318

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

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

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

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

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

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

364
void RandomRaySimulation::simulate()
425✔
365
{
366
  // Random ray power iteration loop
367
  while (simulation::current_batch < settings::n_batches) {
12,155✔
368

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

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

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

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

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

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

395
    simulation::time_transport.stop();
11,730✔
396

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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