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

openmc-dev / openmc / 13183837570

06 Feb 2025 04:53PM UTC coverage: 82.603% (-2.3%) from 84.867%
13183837570

Pull #3087

github

web-flow
Merge e28236a86 into 6e0f156d3
Pull Request #3087: wheel building with scikit build core

107125 of 129687 relevant lines covered (82.6%)

12608230.73 hits per line

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

85.63
/src/output.cpp
1
#include "openmc/output.h"
2

3
#include <algorithm> // for transform, max
4
#include <cstdio>    // for stdout
5
#include <cstring>   // for strlen
6
#include <ctime>     // for time, localtime
7
#include <fstream>
8
#include <iomanip> // for setw, setprecision, put_time
9
#include <ios>     // for fixed, scientific, left
10
#include <iostream>
11
#include <sstream>
12
#include <unordered_map>
13
#include <utility> // for pair
14

15
#include <fmt/core.h>
16
#include <fmt/ostream.h>
17
#ifdef _OPENMP
18
#include <omp.h>
19
#endif
20
#include "xtensor/xview.hpp"
21

22
#include "openmc/capi.h"
23
#include "openmc/cell.h"
24
#include "openmc/constants.h"
25
#include "openmc/eigenvalue.h"
26
#include "openmc/error.h"
27
#include "openmc/geometry.h"
28
#include "openmc/lattice.h"
29
#include "openmc/math_functions.h"
30
#include "openmc/message_passing.h"
31
#include "openmc/mgxs_interface.h"
32
#include "openmc/nuclide.h"
33
#include "openmc/plot.h"
34
#include "openmc/random_ray/flat_source_domain.h"
35
#include "openmc/reaction.h"
36
#include "openmc/settings.h"
37
#include "openmc/simulation.h"
38
#include "openmc/surface.h"
39
#include "openmc/tallies/derivative.h"
40
#include "openmc/tallies/filter.h"
41
#include "openmc/tallies/tally.h"
42
#include "openmc/tallies/tally_scoring.h"
43
#include "openmc/timer.h"
44

45
namespace openmc {
46

47
//==============================================================================
48

49
void title()
4,576✔
50
{
51
  fmt::print("                                %%%%%%%%%%%%%%%\n"
3,824✔
52
             "                           %%%%%%%%%%%%%%%%%%%%%%%%\n"
53
             "                        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"
54
             "                      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"
55
             "                    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"
56
             "                   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"
57
             "                                    %%%%%%%%%%%%%%%%%%%%%%%%\n"
58
             "                                     %%%%%%%%%%%%%%%%%%%%%%%%\n"
59
             "                 ###############      %%%%%%%%%%%%%%%%%%%%%%%%\n"
60
             "                ##################     %%%%%%%%%%%%%%%%%%%%%%%\n"
61
             "                ###################     %%%%%%%%%%%%%%%%%%%%%%%\n"
62
             "                ####################     %%%%%%%%%%%%%%%%%%%%%%\n"
63
             "                #####################     %%%%%%%%%%%%%%%%%%%%%\n"
64
             "                ######################     %%%%%%%%%%%%%%%%%%%%\n"
65
             "                #######################     %%%%%%%%%%%%%%%%%%\n"
66
             "                 #######################     %%%%%%%%%%%%%%%%%\n"
67
             "                 ######################     %%%%%%%%%%%%%%%%%\n"
68
             "                  ####################     %%%%%%%%%%%%%%%%%\n"
69
             "                    #################     %%%%%%%%%%%%%%%%%\n"
70
             "                     ###############     %%%%%%%%%%%%%%%%\n"
71
             "                       ############     %%%%%%%%%%%%%%%\n"
72
             "                          ########     %%%%%%%%%%%%%%\n"
73
             "                                      %%%%%%%%%%%\n\n");
74

75
  // Write version information
76
  fmt::print(
3,824✔
77
    "                 | The OpenMC Monte Carlo Code\n"
78
    "       Copyright | 2011-2024 MIT, UChicago Argonne LLC, and contributors\n"
79
    "         License | https://docs.openmc.org/en/latest/license.html\n"
80
    "         Version | {}.{}.{}{}\n",
81
    VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE, VERSION_DEV ? "-dev" : "");
9,152✔
82
#ifdef GIT_SHA1
83
  fmt::print("        Git SHA1 | {}\n", GIT_SHA1);
3,824✔
84
#endif
85

86
  // Write the date and time
87
  fmt::print("       Date/Time | {}\n", time_stamp());
9,152✔
88

89
#ifdef OPENMC_MPI
90
  // Write number of processors
91
  fmt::print("   MPI Processes | {}\n", mpi::n_procs);
1,552✔
92
#endif
93

94
#ifdef _OPENMP
95
  // Write number of OpenMP threads
96
  fmt::print("  OpenMP Threads | {}\n", omp_get_max_threads());
4,570✔
97
#endif
98
  fmt::print("\n");
4,576✔
99
  std::fflush(stdout);
4,576✔
100
}
4,576✔
101

102
//==============================================================================
103

104
std::string header(const char* msg)
30,564✔
105
{
106
  // Determine how many times to repeat the '=' character.
107
  int n_prefix = (63 - strlen(msg)) / 2;
30,564✔
108
  int n_suffix = n_prefix;
30,564✔
109
  if ((strlen(msg) % 2) == 0)
30,564✔
110
    ++n_suffix;
8,179✔
111

112
  // Convert to uppercase.
113
  std::string upper(msg);
30,564✔
114
  std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
30,564✔
115

116
  // Add ===>  <=== markers.
117
  std::stringstream out;
30,564✔
118
  out << ' ';
30,564✔
119
  for (int i = 0; i < n_prefix; i++)
793,011✔
120
    out << '=';
762,447✔
121
  out << ">     " << upper << "     <";
30,564✔
122
  for (int i = 0; i < n_suffix; i++)
801,190✔
123
    out << '=';
770,626✔
124

125
  return out.str();
61,128✔
126
}
30,564✔
127

128
std::string header(const std::string& msg)
17,575✔
129
{
130
  return header(msg.c_str());
17,575✔
131
}
132

133
void header(const char* msg, int level)
12,989✔
134
{
135
  auto out = header(msg);
12,989✔
136

137
  // Print header based on verbosity level.
138
  if (settings::verbosity >= level) {
12,989✔
139
    fmt::print("\n{}\n\n", out);
10,775✔
140
    std::fflush(stdout);
12,953✔
141
  }
142
}
12,989✔
143

144
//==============================================================================
145

146
std::string time_stamp()
13,297✔
147
{
148
  std::stringstream ts;
13,297✔
149
  std::time_t t = std::time(nullptr); // get time now
13,297✔
150
  ts << std::put_time(std::localtime(&t), "%Y-%m-%d %H:%M:%S");
13,297✔
151
  return ts.str();
26,594✔
152
}
13,297✔
153

154
//==============================================================================
155

156
void print_particle(Particle& p)
36✔
157
{
158
  // Display particle type and ID.
159
  switch (p.type()) {
36✔
160
  case ParticleType::neutron:
36✔
161
    fmt::print("Neutron ");
30✔
162
    break;
36✔
163
  case ParticleType::photon:
×
164
    fmt::print("Photon ");
×
165
    break;
×
166
  case ParticleType::electron:
×
167
    fmt::print("Electron ");
×
168
    break;
×
169
  case ParticleType::positron:
×
170
    fmt::print("Positron ");
×
171
    break;
×
172
  default:
×
173
    fmt::print("Unknown Particle ");
×
174
  }
175
  fmt::print("{}\n", p.id());
66✔
176

177
  // Display particle geometry hierarchy.
178
  for (auto i = 0; i < p.n_coord(); i++) {
72✔
179
    fmt::print("  Level {}\n", i);
30✔
180

181
    if (p.coord(i).cell != C_NONE) {
36✔
182
      const Cell& c {*model::cells[p.coord(i).cell]};
36✔
183
      fmt::print("    Cell             = {}\n", c.id_);
72✔
184
    }
185

186
    if (p.coord(i).universe != C_NONE) {
36✔
187
      const Universe& u {*model::universes[p.coord(i).universe]};
36✔
188
      fmt::print("    Universe         = {}\n", u.id_);
72✔
189
    }
190

191
    if (p.coord(i).lattice != C_NONE) {
36✔
192
      const Lattice& lat {*model::lattices[p.coord(i).lattice]};
×
193
      fmt::print("    Lattice          = {}\n", lat.id_);
×
194
      fmt::print("    Lattice position = ({},{},{})\n", p.coord(i).lattice_i[0],
×
195
        p.coord(i).lattice_i[1], p.coord(i).lattice_i[2]);
×
196
    }
197

198
    fmt::print("    r = {}\n", p.coord(i).r);
66✔
199
    fmt::print("    u = {}\n", p.coord(i).u);
72✔
200
  }
201

202
  // Display miscellaneous info.
203
  if (p.surface() != SURFACE_NONE) {
36✔
204
    // Surfaces identifiers are >= 1, but indices are >= 0 so we need -1
205
    const Surface& surf {*model::surfaces[p.surface_index()]};
×
206
    fmt::print("  Surface = {}\n", (p.surface() > 0) ? surf.id_ : -surf.id_);
×
207
  }
208
  fmt::print("  Weight = {}\n", p.wgt());
66✔
209
  if (settings::run_CE) {
36✔
210
    fmt::print("  Energy = {}\n", p.E());
72✔
211
  } else {
212
    fmt::print("  Energy Group = {}\n", p.g());
×
213
  }
214
  fmt::print("  Delayed Group = {}\n\n", p.delayed_group());
66✔
215
}
36✔
216

217
//==============================================================================
218

219
void print_plot()
276✔
220
{
221
  header("PLOTTING SUMMARY", 5);
276✔
222
  if (settings::verbosity < 5)
276✔
223
    return;
×
224

225
  for (const auto& pl : model::plots) {
756✔
226
    fmt::print("Plot ID: {}\n", pl->id());
880✔
227
    fmt::print("Plot file: {}\n", pl->path_plot());
880✔
228
    fmt::print("Universe depth: {}\n", pl->level());
880✔
229
    pl->print_info(); // prints type-specific plot info
480✔
230
    fmt::print("\n");
480✔
231
  }
232
}
233

234
//==============================================================================
235

236
void print_overlap_check()
×
237
{
238
#ifdef OPENMC_MPI
239
  vector<int64_t> temp(model::overlap_check_count);
240
  MPI_Reduce(temp.data(), model::overlap_check_count.data(),
241
    model::overlap_check_count.size(), MPI_INT64_T, MPI_SUM, 0, mpi::intracomm);
242
#endif
243

244
  if (mpi::master) {
×
245
    header("cell overlap check summary", 1);
×
246
    fmt::print(" Cell ID      No. Overlap Checks\n");
×
247

248
    vector<int32_t> sparse_cell_ids;
×
249
    for (int i = 0; i < model::cells.size(); i++) {
×
250
      fmt::print(
×
251
        " {:8} {:17}\n", model::cells[i]->id_, model::overlap_check_count[i]);
×
252
      if (model::overlap_check_count[i] < 10) {
×
253
        sparse_cell_ids.push_back(model::cells[i]->id_);
×
254
      }
255
    }
256

257
    fmt::print("\n There were {} cells with less than 10 overlap checks\n",
×
258
      sparse_cell_ids.size());
×
259
    for (auto id : sparse_cell_ids) {
×
260
      fmt::print(" {}", id);
×
261
    }
262
    fmt::print("\n");
×
263
  }
264
}
265

266
//==============================================================================
267

268
void print_usage()
×
269
{
270
  if (mpi::master) {
×
271
    fmt::print(
×
272
      "Usage: openmc [options] [path]\n\n"
273
      "Options:\n"
274
      "  -c, --volume           Run in stochastic volume calculation mode\n"
275
      "  -g, --geometry-debug   Run with geometry debugging on\n"
276
      "  -n, --particles        Number of particles per generation\n"
277
      "  -p, --plot             Run in plotting mode\n"
278
      "  -r, --restart          Restart a previous run from a state point\n"
279
      "                         or a particle restart file\n"
280
      "  -s, --threads          Number of OpenMP threads\n"
281
      "  -t, --track            Write tracks for all particles (up to "
282
      "max_tracks)\n"
283
      "  -e, --event            Run using event-based parallelism\n"
284
      "  -v, --version          Show version information\n"
285
      "  -h, --help             Show this message\n");
286
  }
287
}
288

289
//==============================================================================
290

291
void print_version()
12✔
292
{
293
  if (mpi::master) {
12✔
294
    fmt::print("OpenMC version {}.{}.{}\n", VERSION_MAJOR, VERSION_MINOR,
12✔
295
      VERSION_RELEASE);
296
#ifdef GIT_SHA1
297
    fmt::print("Git SHA1: {}\n", GIT_SHA1);
12✔
298
#endif
299
    fmt::print("Copyright (c) 2011-2024 MIT, UChicago Argonne LLC, and "
12✔
300
               "contributors\nMIT/X license at "
301
               "<https://docs.openmc.org/en/latest/license.html>\n");
302
  }
303
}
12✔
304

305
//==============================================================================
306

307
void print_build_info()
12✔
308
{
309
  const std::string n("no");
12✔
310
  const std::string y("yes");
12✔
311

312
  std::string mpi(n);
12✔
313
  std::string phdf5(n);
12✔
314
  std::string dagmc(n);
12✔
315
  std::string libmesh(n);
12✔
316
  std::string png(n);
12✔
317
  std::string profiling(n);
12✔
318
  std::string coverage(n);
12✔
319
  std::string mcpl(n);
12✔
320
  std::string ncrystal(n);
12✔
321
  std::string uwuw(n);
12✔
322

323
#ifdef PHDF5
324
  phdf5 = y;
5✔
325
#endif
326
#ifdef OPENMC_MPI
327
  mpi = y;
5✔
328
#endif
329
#ifdef DAGMC
330
  dagmc = y;
1✔
331
#endif
332
#ifdef LIBMESH
333
  libmesh = y;
2✔
334
#endif
335
#ifdef OPENMC_MCPL
336
  mcpl = y;
12✔
337
#endif
338
#ifdef NCRYSTAL
339
  ncrystal = y;
1✔
340
#endif
341
#ifdef USE_LIBPNG
342
  png = y;
12✔
343
#endif
344
#ifdef PROFILINGBUILD
345
  profiling = y;
346
#endif
347
#ifdef COVERAGEBUILD
348
  coverage = y;
12✔
349
#endif
350
#ifdef OPENMC_UWUW
351
  uwuw = y;
1✔
352
#endif
353

354
  // Wraps macro variables in quotes
355
#define STRINGIFY(x) STRINGIFY2(x)
356
#define STRINGIFY2(x) #x
357

358
  if (mpi::master) {
12✔
359
    fmt::print("Build type:            {}\n", STRINGIFY(BUILD_TYPE));
12✔
360
    fmt::print("Compiler ID:           {} {}\n", STRINGIFY(COMPILER_ID),
12✔
361
      STRINGIFY(COMPILER_VERSION));
362
    fmt::print("MPI enabled:           {}\n", mpi);
12✔
363
    fmt::print("Parallel HDF5 enabled: {}\n", phdf5);
12✔
364
    fmt::print("PNG support:           {}\n", png);
12✔
365
    fmt::print("DAGMC support:         {}\n", dagmc);
12✔
366
    fmt::print("libMesh support:       {}\n", libmesh);
12✔
367
    fmt::print("MCPL support:          {}\n", mcpl);
12✔
368
    fmt::print("NCrystal support:      {}\n", ncrystal);
12✔
369
    fmt::print("Coverage testing:      {}\n", coverage);
12✔
370
    fmt::print("Profiling flags:       {}\n", profiling);
12✔
371
    fmt::print("UWUW support:          {}\n", uwuw);
12✔
372
  }
373
}
12✔
374

375
//==============================================================================
376

377
void print_columns()
2,517✔
378
{
379
  if (settings::entropy_on) {
2,517✔
380
    fmt::print("  Bat./Gen.      k       Entropy         Average k \n"
96✔
381
               "  =========   ========   ========   ====================\n");
382
  } else {
383
    fmt::print("  Bat./Gen.      k            Average k\n"
2,421✔
384
               "  =========   ========   ====================\n");
385
  }
386
}
2,517✔
387

388
//==============================================================================
389

390
void print_generation()
48,002✔
391
{
392
  // Determine overall generation index and number of active generations
393
  int idx = overall_generation() - 1;
48,002✔
394
  int n = simulation::current_batch > settings::n_inactive
96,004✔
395
            ? settings::gen_per_batch * simulation::n_realizations +
48,002✔
396
                simulation::current_gen
397
            : 0;
398

399
  // write out batch/generation and generation k-effective
400
  auto batch_and_gen = std::to_string(simulation::current_batch) + "/" +
96,004✔
401
                       std::to_string(simulation::current_gen);
96,004✔
402
  fmt::print("  {:>9}   {:8.5f}", batch_and_gen, simulation::k_generation[idx]);
88,002✔
403

404
  // write out entropy info
405
  if (settings::entropy_on) {
48,002✔
406
    fmt::print("   {:8.5f}", simulation::entropy[idx]);
5,520✔
407
  }
408

409
  if (n > 1) {
48,002✔
410
    fmt::print("   {:8.5f} +/-{:8.5f}", simulation::keff, simulation::keff_std);
36,837✔
411
  }
412
  fmt::print("\n");
40,000✔
413
  std::fflush(stdout);
48,002✔
414
}
48,002✔
415

416
//==============================================================================
417

418
void show_time(const char* label, double secs, int indent_level)
48,717✔
419
{
420
  int width = 33 - indent_level * 2;
48,717✔
421
  fmt::print("{0:{1}} {2:<{3}} = {4:>10.4e} seconds\n", "", 2 * indent_level,
89,358✔
422
    label, width, secs);
423
}
48,717✔
424

425
void show_rate(const char* label, double particles_per_sec)
5,603✔
426
{
427
  fmt::print(" {:<33} = {:.6} particles/second\n", label, particles_per_sec);
5,603✔
428
}
5,603✔
429

430
void print_runtime()
3,835✔
431
{
432
  using namespace simulation;
433

434
  // display header block
435
  header("Timing Statistics", 6);
3,835✔
436
  if (settings::verbosity < 6)
3,835✔
437
    return;
×
438

439
  // display time elapsed for various sections
440
  show_time("Total time for initialization", time_initialize.elapsed());
3,835✔
441
  show_time("Reading cross sections", time_read_xs.elapsed(), 1);
3,835✔
442
  show_time("Total time in simulation",
3,835✔
443
    time_inactive.elapsed() + time_active.elapsed());
3,835✔
444
  show_time("Time in transport only", time_transport.elapsed(), 1);
3,835✔
445
  if (settings::event_based) {
3,835✔
446
    show_time("Particle initialization", time_event_init.elapsed(), 2);
125✔
447
    show_time("XS lookups", time_event_calculate_xs.elapsed(), 2);
125✔
448
    show_time("Advancing", time_event_advance_particle.elapsed(), 2);
125✔
449
    show_time("Surface crossings", time_event_surface_crossing.elapsed(), 2);
125✔
450
    show_time("Collisions", time_event_collision.elapsed(), 2);
125✔
451
    show_time("Particle death", time_event_death.elapsed(), 2);
125✔
452
  }
453
  if (settings::run_mode == RunMode::EIGENVALUE) {
3,835✔
454
    show_time("Time in inactive batches", time_inactive.elapsed(), 1);
2,445✔
455
  }
456
  show_time("Time in active batches", time_active.elapsed(), 1);
3,835✔
457
  if (settings::run_mode == RunMode::EIGENVALUE) {
3,835✔
458
    show_time("Time synchronizing fission bank", time_bank.elapsed(), 1);
2,445✔
459
    show_time("Sampling source sites", time_bank_sample.elapsed(), 2);
2,445✔
460
    show_time("SEND/RECV source sites", time_bank_sendrecv.elapsed(), 2);
2,445✔
461
  }
462
  show_time("Time accumulating tallies", time_tallies.elapsed(), 1);
3,835✔
463
  show_time("Time writing statepoints", time_statepoint.elapsed(), 1);
3,835✔
464
  show_time("Total time for finalization", time_finalize.elapsed());
3,835✔
465
  show_time("Total time elapsed", time_total.elapsed());
3,835✔
466

467
  // Calculate particle rate in active/inactive batches
468
  int n_active = simulation::current_batch - settings::n_inactive;
3,835✔
469
  double speed_inactive = 0.0;
3,835✔
470
  double speed_active;
471
  if (settings::restart_run) {
3,835✔
472
    if (simulation::restart_batch < settings::n_inactive) {
24✔
473
      speed_inactive = (settings::n_particles *
×
474
                         (settings::n_inactive - simulation::restart_batch) *
×
475
                         settings::gen_per_batch) /
×
476
                       time_inactive.elapsed();
×
477
      speed_active =
×
478
        (settings::n_particles * n_active * settings::gen_per_batch) /
×
479
        time_active.elapsed();
×
480
    } else {
481
      speed_active = (settings::n_particles *
48✔
482
                       (settings::n_batches - simulation::restart_batch) *
24✔
483
                       settings::gen_per_batch) /
24✔
484
                     time_active.elapsed();
24✔
485
    }
486
  } else {
487
    if (settings::n_inactive > 0) {
3,811✔
488
      speed_inactive = (settings::n_particles * settings::n_inactive *
3,536✔
489
                         settings::gen_per_batch) /
1,768✔
490
                       time_inactive.elapsed();
1,768✔
491
    }
492
    speed_active =
3,811✔
493
      (settings::n_particles * n_active * settings::gen_per_batch) /
3,811✔
494
      time_active.elapsed();
3,811✔
495
  }
496

497
  // display calculation rate
498
  if (!(settings::restart_run &&
3,835✔
499
        (simulation::restart_batch >= settings::n_inactive)) &&
24✔
500
      settings::n_inactive > 0) {
3,811✔
501
    show_rate("Calculation Rate (inactive)", speed_inactive);
1,768✔
502
  }
503
  show_rate("Calculation Rate (active)", speed_active);
3,835✔
504
}
505

506
//==============================================================================
507

508
std::pair<double, double> mean_stdev(const double* x, int n)
11,290,288✔
509
{
510
  double mean = x[static_cast<int>(TallyResult::SUM)] / n;
11,290,288✔
511
  double stdev =
512
    n > 1 ? std::sqrt(std::max(0.0,
22,578,680✔
513
              (x[static_cast<int>(TallyResult::SUM_SQ)] / n - mean * mean) /
22,578,680✔
514
                (n - 1)))
11,288,392✔
515
          : 0.0;
11,290,288✔
516
  return {mean, stdev};
11,290,288✔
517
}
518

519
//==============================================================================
520

521
void print_results()
3,835✔
522
{
523
  // display header block for results
524
  header("Results", 4);
3,835✔
525
  if (settings::verbosity < 4)
3,835✔
526
    return;
×
527

528
  // Calculate t-value for confidence intervals
529
  int n = simulation::n_realizations;
3,835✔
530
  double alpha, t_n1, t_n3;
531
  if (settings::confidence_intervals) {
3,835✔
532
    alpha = 1.0 - CONFIDENCE_LEVEL;
12✔
533
    t_n1 = t_percentile(1.0 - alpha / 2.0, n - 1);
12✔
534
    t_n3 = t_percentile(1.0 - alpha / 2.0, n - 3);
12✔
535
  } else {
536
    t_n1 = 1.0;
3,823✔
537
    t_n3 = 1.0;
3,823✔
538
  }
539

540
  // write global tallies
541
  const auto& gt = simulation::global_tallies;
3,835✔
542
  double mean, stdev;
543
  if (n > 1) {
3,835✔
544
    if (settings::run_mode == RunMode::EIGENVALUE) {
3,715✔
545
      std::tie(mean, stdev) = mean_stdev(&gt(GlobalTally::K_COLLISION, 0), n);
2,409✔
546
      fmt::print(" k-effective (Collision)     = {:.5f} +/- {:.5f}\n", mean,
2,007✔
547
        t_n1 * stdev);
2,409✔
548
      std::tie(mean, stdev) = mean_stdev(&gt(GlobalTally::K_TRACKLENGTH, 0), n);
2,409✔
549
      fmt::print(" k-effective (Track-length)  = {:.5f} +/- {:.5f}\n", mean,
2,007✔
550
        t_n1 * stdev);
2,409✔
551
      std::tie(mean, stdev) = mean_stdev(&gt(GlobalTally::K_ABSORPTION, 0), n);
2,409✔
552
      fmt::print(" k-effective (Absorption)    = {:.5f} +/- {:.5f}\n", mean,
2,007✔
553
        t_n1 * stdev);
2,409✔
554
      if (n > 3) {
2,409✔
555
        double k_combined[2];
556
        openmc_get_keff(k_combined);
2,349✔
557
        fmt::print(" Combined k-effective        = {:.5f} +/- {:.5f}\n",
2,349✔
558
          k_combined[0], k_combined[1]);
559
      }
560
    }
561
    std::tie(mean, stdev) = mean_stdev(&gt(GlobalTally::LEAKAGE, 0), n);
3,715✔
562
    fmt::print(
3,087✔
563
      " Leakage Fraction            = {:.5f} +/- {:.5f}\n", mean, t_n1 * stdev);
7,430✔
564
  } else {
565
    if (mpi::master)
120✔
566
      warning("Could not compute uncertainties -- only one "
120✔
567
              "active batch simulated!");
568

569
    if (settings::run_mode == RunMode::EIGENVALUE) {
120✔
570
      fmt::print(" k-effective (Collision)    = {:.5f}\n",
30✔
571
        gt(GlobalTally::K_COLLISION, TallyResult::SUM) / n);
36✔
572
      fmt::print(" k-effective (Track-length) = {:.5f}\n",
30✔
573
        gt(GlobalTally::K_TRACKLENGTH, TallyResult::SUM) / n);
36✔
574
      fmt::print(" k-effective (Absorption)   = {:.5f}\n",
30✔
575
        gt(GlobalTally::K_ABSORPTION, TallyResult::SUM) / n);
72✔
576
    }
577
    fmt::print(" Leakage Fraction           = {:.5f}\n",
100✔
578
      gt(GlobalTally::LEAKAGE, TallyResult::SUM) / n);
240✔
579
  }
580
  fmt::print("\n");
3,187✔
581
  std::fflush(stdout);
3,835✔
582
}
583

584
//==============================================================================
585

586
const std::unordered_map<int, const char*> score_names = {
587
  {SCORE_FLUX, "Flux"},
588
  {SCORE_TOTAL, "Total Reaction Rate"},
589
  {SCORE_SCATTER, "Scattering Rate"},
590
  {SCORE_NU_SCATTER, "Scattering Production Rate"},
591
  {SCORE_ABSORPTION, "Absorption Rate"},
592
  {SCORE_FISSION, "Fission Rate"},
593
  {SCORE_NU_FISSION, "Nu-Fission Rate"},
594
  {SCORE_KAPPA_FISSION, "Kappa-Fission Rate"},
595
  {SCORE_EVENTS, "Events"},
596
  {SCORE_DECAY_RATE, "Decay Rate"},
597
  {SCORE_DELAYED_NU_FISSION, "Delayed-Nu-Fission Rate"},
598
  {SCORE_PROMPT_NU_FISSION, "Prompt-Nu-Fission Rate"},
599
  {SCORE_INVERSE_VELOCITY, "Flux-Weighted Inverse Velocity"},
600
  {SCORE_FISS_Q_PROMPT, "Prompt fission power"},
601
  {SCORE_FISS_Q_RECOV, "Recoverable fission power"},
602
  {SCORE_CURRENT, "Current"},
603
  {SCORE_PULSE_HEIGHT, "pulse-height"},
604
};
605

606
//! Create an ASCII output file showing all tally results.
607

608
void write_tallies()
4,159✔
609
{
610
  if (model::tallies.empty())
4,159✔
611
    return;
1,602✔
612

613
  // Set filename for tallies_out
614
  std::string filename = fmt::format("{}tallies.out", settings::path_output);
2,125✔
615

616
  // Open the tallies.out file.
617
  std::ofstream tallies_out;
2,557✔
618
  tallies_out.open(filename, std::ios::out | std::ios::trunc);
2,557✔
619

620
  // Loop over each tally.
621
  for (auto i_tally = 0; i_tally < model::tallies.size(); ++i_tally) {
20,132✔
622
    const auto& tally {*model::tallies[i_tally]};
17,575✔
623

624
    // Write header block.
625
    std::string tally_header("TALLY " + std::to_string(tally.id_));
17,575✔
626
    if (!tally.name_.empty())
17,575✔
627
      tally_header += ": " + tally.name_;
1,382✔
628
    fmt::print(tallies_out, "{}\n\n", header(tally_header));
35,150✔
629

630
    if (!tally.writable_) {
17,575✔
631
      fmt::print(tallies_out, " Internal\n\n");
×
632
      continue;
×
633
    }
634

635
    // Calculate t-value for confidence intervals
636
    double t_value = 1;
17,575✔
637
    if (settings::confidence_intervals) {
17,575✔
638
      auto alpha = 1 - CONFIDENCE_LEVEL;
12✔
639
      t_value = t_percentile(1 - alpha * 0.5, tally.n_realizations_ - 1);
12✔
640
    }
641

642
    // Write derivative information.
643
    if (tally.deriv_ != C_NONE) {
17,575✔
644
      const auto& deriv {model::tally_derivs[tally.deriv_]};
240✔
645
      switch (deriv.variable) {
240✔
646
      case DerivativeVariable::DENSITY:
96✔
647
        fmt::print(tallies_out, " Density derivative Material {}\n",
96✔
648
          deriv.diff_material);
96✔
649
        break;
96✔
650
      case DerivativeVariable::NUCLIDE_DENSITY:
96✔
651
        fmt::print(tallies_out,
96✔
652
          " Nuclide density derivative Material {} Nuclide {}\n",
653
          deriv.diff_material, data::nuclides[deriv.diff_nuclide]->name_);
96✔
654
        break;
96✔
655
      case DerivativeVariable::TEMPERATURE:
48✔
656
        fmt::print(tallies_out, " Temperature derivative Material {}\n",
48✔
657
          deriv.diff_material);
48✔
658
        break;
48✔
659
      default:
×
660
        fatal_error(fmt::format("Differential tally dependent variable for "
×
661
                                "tally {} not defined in output.cpp",
662
          tally.id_));
×
663
      }
664
    }
665

666
    // Initialize Filter Matches Object
667
    vector<FilterMatch> filter_matches;
35,150✔
668
    // Allocate space for tally filter matches
669
    filter_matches.resize(model::tally_filters.size());
17,575✔
670

671
    // Loop over all filter bin combinations.
672
    auto filter_iter = FilterBinIter(tally, false, &filter_matches);
17,575✔
673
    auto end = FilterBinIter(tally, true, &filter_matches);
17,575✔
674
    for (; filter_iter != end; ++filter_iter) {
6,614,029✔
675
      auto filter_index = filter_iter.index_;
6,596,454✔
676

677
      // Print info about this combination of filter bins.  The stride check
678
      // prevents redundant output.
679
      int indent = 0;
6,596,454✔
680
      for (auto i = 0; i < tally.filters().size(); ++i) {
18,985,968✔
681
        if (filter_index % tally.strides(i) == 0) {
12,389,514✔
682
          auto i_filt = tally.filters(i);
9,554,659✔
683
          const auto& filt {*model::tally_filters[i_filt]};
9,554,659✔
684
          auto& match {filter_matches[i_filt]};
9,554,659✔
685
          fmt::print(tallies_out, "{0:{1}}{2}\n", "", indent + 1,
19,109,318✔
686
            filt.text_label(match.i_bin_));
19,109,318✔
687
        }
688
        indent += 2;
12,389,514✔
689
      }
690

691
      // Loop over all nuclide and score combinations.
692
      int score_index = 0;
6,596,454✔
693
      for (auto i_nuclide : tally.nuclides_) {
14,750,628✔
694
        // Write label for this nuclide bin.
695
        if (i_nuclide == -1) {
8,154,174✔
696
          fmt::print(tallies_out, "{0:{1}}Total Material\n", "", indent + 1);
10,120,332✔
697
        } else {
698
          if (settings::run_CE) {
3,094,008✔
699
            fmt::print(tallies_out, "{0:{1}}{2}\n", "", indent + 1,
3,093,600✔
700
              data::nuclides[i_nuclide]->name_);
3,093,600✔
701
          } else {
702
            fmt::print(tallies_out, "{0:{1}}{2}\n", "", indent + 1,
408✔
703
              data::mg.nuclides_[i_nuclide].name);
408✔
704
          }
705
        }
706

707
        // Write the score, mean, and uncertainty.
708
        indent += 2;
8,154,174✔
709
        for (auto score : tally.scores_) {
19,433,520✔
710
          std::string score_name =
711
            score > 0 ? reaction_name(score) : score_names.at(score);
11,279,346✔
712
          double mean, stdev;
713
          std::tie(mean, stdev) =
11,279,346✔
714
            mean_stdev(&tally.results_(filter_index, score_index, 0),
11,279,346✔
715
              tally.n_realizations_);
22,558,692✔
716
          fmt::print(tallies_out, "{0:{1}}{2:<36} {3:.6} +/- {4:.6}\n", "",
11,279,346✔
717
            indent + 1, score_name, mean, t_value * stdev);
11,279,346✔
718
          score_index += 1;
11,279,346✔
719
        }
11,279,346✔
720
        indent -= 2;
8,154,174✔
721
      }
722
    }
723
  }
17,575✔
724
}
2,557✔
725

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