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

openmc-dev / openmc / 21106440915

18 Jan 2026 05:07AM UTC coverage: 80.822% (-1.2%) from 82.044%
21106440915

Pull #3732

github

web-flow
Merge e0d6fcdcf into 5847b0de2
Pull Request #3732: Volume Calculation enhancement including refactoring and real-valued scoring implementation

15996 of 22249 branches covered (71.9%)

Branch coverage included in aggregate %.

229 of 255 new or added lines in 3 files covered. (89.8%)

1115 existing lines in 52 files now uncovered.

53688 of 63970 relevant lines covered (83.93%)

13914511.73 hits per line

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

69.96
/src/initialize.cpp
1
#include "openmc/initialize.h"
2

3
#include <clocale>
4
#include <cstddef>
5
#include <cstdlib> // for getenv
6
#include <cstring>
7
#include <string>
8

9
#ifdef _OPENMP
10
#include <omp.h>
11
#endif
12
#include <fmt/core.h>
13

14
#include "openmc/capi.h"
15
#include "openmc/chain.h"
16
#include "openmc/constants.h"
17
#include "openmc/cross_sections.h"
18
#include "openmc/error.h"
19
#include "openmc/file_utils.h"
20
#include "openmc/geometry_aux.h"
21
#include "openmc/hdf5_interface.h"
22
#include "openmc/material.h"
23
#include "openmc/memory.h"
24
#include "openmc/message_passing.h"
25
#include "openmc/mgxs_interface.h"
26
#include "openmc/nuclide.h"
27
#include "openmc/openmp_interface.h"
28
#include "openmc/output.h"
29
#include "openmc/plot.h"
30
#include "openmc/random_lcg.h"
31
#include "openmc/settings.h"
32
#include "openmc/simulation.h"
33
#include "openmc/string_utils.h"
34
#include "openmc/summary.h"
35
#include "openmc/tallies/tally.h"
36
#include "openmc/thermal.h"
37
#include "openmc/timer.h"
38
#include "openmc/vector.h"
39
#include "openmc/volume_calc.h"
40
#include "openmc/weight_windows.h"
41

42
#ifdef OPENMC_LIBMESH_ENABLED
43
#include "libmesh/libmesh.h"
44
#endif
45

46
int openmc_init(int argc, char* argv[], const void* intracomm)
1,258✔
47
{
48
  using namespace openmc;
49

50
#ifdef OPENMC_MPI
51
  // Check if intracomm was passed
52
  MPI_Comm comm;
53
  if (intracomm) {
54
    comm = *static_cast<const MPI_Comm*>(intracomm);
55
  } else {
56
    comm = MPI_COMM_WORLD;
57
  }
58

59
  // Initialize MPI for C++
60
  initialize_mpi(comm);
61
#endif
62

63
  // Parse command-line arguments
64
  int err = parse_command_line(argc, argv);
1,258✔
65
  if (err)
1,252✔
66
    return err;
2✔
67

68
#ifdef OPENMC_LIBMESH_ENABLED
69
  const int n_threads = num_threads();
70
  // initialize libMesh if it hasn't been initialized already
71
  // (if initialized externally, the libmesh_init object needs to be provided
72
  // also)
73
  if (!settings::libmesh_init && !libMesh::initialized()) {
74
#ifdef OPENMC_MPI
75
    // pass command line args, empty MPI communicator, and number of threads.
76
    // Because libMesh was not initialized, we assume that OpenMC is the primary
77
    // application and that its main MPI comm should be used.
78
    settings::libmesh_init =
79
      make_unique<libMesh::LibMeshInit>(argc, argv, comm, n_threads);
80
#else
81
    // pass command line args, empty MPI communicator, and number of threads
82
    settings::libmesh_init =
83
      make_unique<libMesh::LibMeshInit>(argc, argv, 0, n_threads);
84
#endif
85

86
    settings::libmesh_comm = &(settings::libmesh_init->comm());
87
  }
88

89
#endif
90

91
  // Start total and initialization timer
92
  simulation::time_total.start();
1,250✔
93
  simulation::time_initialize.start();
1,250✔
94

95
#ifdef _OPENMP
96
  // If OMP_SCHEDULE is not set, default to a static schedule
97
  char* envvar = std::getenv("OMP_SCHEDULE");
98
  if (!envvar) {
99
    omp_set_schedule(omp_sched_static, 0);
100
  }
101
#endif
102

103
  // Initialize random number generator -- if the user specifies a seed and/or
104
  // stride, it will be re-initialized later
105
  openmc::openmc_set_seed(DEFAULT_SEED);
1,250✔
106
  openmc::openmc_set_stride(DEFAULT_STRIDE);
1,250✔
107

108
  // Copy previous locale and set locale to C. This is a workaround for an issue
109
  // whereby when openmc_init is called from the plotter, the Qt application
110
  // framework first calls std::setlocale, which affects how pugixml reads
111
  // floating point numbers due to a bug:
112
  // https://github.com/zeux/pugixml/issues/469
113
  std::string prev_locale = std::setlocale(LC_ALL, nullptr);
1,250✔
114
  if (std::setlocale(LC_ALL, "C") == NULL) {
1,250!
115
    fatal_error("Cannot set locale to C.");
×
116
  }
117

118
  // Read XML input files
119
  if (!read_model_xml())
1,250✔
120
    read_separate_xml_files();
208✔
121

122
  // Reset locale to previous state
123
  if (std::setlocale(LC_ALL, prev_locale.c_str()) == NULL) {
1,230!
124
    fatal_error("Cannot reset locale.");
×
125
  }
126

127
  // Write some initial output under the header if needed
128
  initial_output();
1,230✔
129

130
  // Check for particle restart run
131
  if (settings::particle_restart_run)
1,230✔
132
    settings::run_mode = RunMode::PARTICLE;
6✔
133

134
  // Stop initialization timer
135
  simulation::time_initialize.stop();
1,230✔
136
  simulation::time_total.stop();
1,230✔
137

138
  return 0;
1,230✔
139
}
1,230✔
140

141
namespace openmc {
142

143
#ifdef OPENMC_MPI
144
void initialize_mpi(MPI_Comm intracomm)
145
{
146
  mpi::intracomm = intracomm;
147

148
  // Initialize MPI
149
  int flag;
150
  MPI_Initialized(&flag);
151
  if (!flag)
152
    MPI_Init(nullptr, nullptr);
153

154
  // Determine number of processes and rank for each
155
  MPI_Comm_size(intracomm, &mpi::n_procs);
156
  MPI_Comm_rank(intracomm, &mpi::rank);
157
  mpi::master = (mpi::rank == 0);
158

159
  // Create bank datatype
160
  SourceSite b;
161
  MPI_Aint disp[11];
162
  MPI_Get_address(&b.r, &disp[0]);
163
  MPI_Get_address(&b.u, &disp[1]);
164
  MPI_Get_address(&b.E, &disp[2]);
165
  MPI_Get_address(&b.time, &disp[3]);
166
  MPI_Get_address(&b.wgt, &disp[4]);
167
  MPI_Get_address(&b.delayed_group, &disp[5]);
168
  MPI_Get_address(&b.surf_id, &disp[6]);
169
  MPI_Get_address(&b.particle, &disp[7]);
170
  MPI_Get_address(&b.parent_nuclide, &disp[8]);
171
  MPI_Get_address(&b.parent_id, &disp[9]);
172
  MPI_Get_address(&b.progeny_id, &disp[10]);
173
  for (int i = 10; i >= 0; --i) {
174
    disp[i] -= disp[0];
175
  }
176

177
  int blocks[] {3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1};
178
  MPI_Datatype types[] {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE,
179
    MPI_DOUBLE, MPI_INT, MPI_INT, MPI_INT, MPI_INT, MPI_LONG, MPI_LONG};
180
  MPI_Type_create_struct(11, blocks, disp, types, &mpi::source_site);
181
  MPI_Type_commit(&mpi::source_site);
182

183
  CollisionTrackSite bc;
184
  MPI_Aint dispc[16];
185
  MPI_Get_address(&bc.r, &dispc[0]);             // double
186
  MPI_Get_address(&bc.u, &dispc[1]);             // double
187
  MPI_Get_address(&bc.E, &dispc[2]);             // double
188
  MPI_Get_address(&bc.dE, &dispc[3]);            // double
189
  MPI_Get_address(&bc.time, &dispc[4]);          // double
190
  MPI_Get_address(&bc.wgt, &dispc[5]);           // double
191
  MPI_Get_address(&bc.event_mt, &dispc[6]);      // int
192
  MPI_Get_address(&bc.delayed_group, &dispc[7]); // int
193
  MPI_Get_address(&bc.cell_id, &dispc[8]);       // int
194
  MPI_Get_address(&bc.nuclide_id, &dispc[9]);    // int
195
  MPI_Get_address(&bc.material_id, &dispc[10]);  // int
196
  MPI_Get_address(&bc.universe_id, &dispc[11]);  // int
197
  MPI_Get_address(&bc.n_collision, &dispc[12]);  // int
198
  MPI_Get_address(&bc.particle, &dispc[13]);     // int
199
  MPI_Get_address(&bc.parent_id, &dispc[14]);    // int64_t
200
  MPI_Get_address(&bc.progeny_id, &dispc[15]);   // int64_t
201
  for (int i = 15; i >= 0; --i) {
202
    dispc[i] -= dispc[0];
203
  }
204

205
  int blocksc[] = {3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
206
  MPI_Datatype typesc[] = {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE,
207
    MPI_DOUBLE, MPI_DOUBLE, MPI_INT, MPI_INT, MPI_INT, MPI_INT, MPI_INT,
208
    MPI_INT, MPI_INT, MPI_INT, MPI_INT64_T, MPI_INT64_T};
209

210
  MPI_Type_create_struct(
211
    16, blocksc, dispc, typesc, &mpi::collision_track_site);
212
  MPI_Type_commit(&mpi::collision_track_site);
213

214
  // Volume Calculation data types
215
  VolumeCalculation::CalcResults cr;
216
  MPI_Aint cr_disp[1], cr_d;
217
  MPI_Get_address(&cr, &cr_d);
218
  MPI_Get_address(&cr.cost, &cr_disp[0]);
219
  for (int i = 0; i < 1; i++) {
220
    cr_disp[i] -= cr_d;
221
  }
222

223
  int cr_blocks[] {1};
224
  MPI_Datatype cr_types[] {MPI_DOUBLE};
225
  MPI_Type_create_struct(
226
    1, cr_blocks, cr_disp, cr_types, &mpi::mpi_volume_results);
227
  MPI_Type_commit(&mpi::mpi_volume_results);
228

229
  VolumeCalculation::VolTally vt = VolumeCalculation::VolTally();
230
  MPI_Aint vt_disp[3], vt_d;
231
  MPI_Get_address(&vt, &vt_d);
232
  MPI_Get_address(&vt.score, &vt_disp[0]);
233
  MPI_Get_address(&vt.score_acc, &vt_disp[1]);
234
  MPI_Get_address(&vt.index, &vt_disp[2]);
235
  for (int i = 0; i < 3; i++) {
236
    vt_disp[i] -= vt_d;
237
  }
238

239
  int vt_blocks[] {1, 2, 1};
240
  MPI_Datatype vt_types[] {MPI_DOUBLE, MPI_DOUBLE, MPI_INT32_T};
241
  MPI_Type_create_struct(
242
    3, vt_blocks, vt_disp, vt_types, &mpi::mpi_volume_tally);
243
  MPI_Type_commit(&mpi::mpi_volume_tally);
244
}
245
#endif // OPENMC_MPI
246

247
int parse_command_line(int argc, char* argv[])
1,258✔
248
{
249
  int last_flag = 0;
1,258✔
250
  for (int i = 1; i < argc; ++i) {
1,444✔
251
    std::string arg {argv[i]};
188✔
252
    if (arg[0] == '-') {
188✔
253
      if (arg == "-p" || arg == "--plot") {
170!
254
        settings::run_mode = RunMode::PLOTTING;
22✔
255
        settings::check_overlaps = true;
22✔
256

257
      } else if (arg == "-n" || arg == "--particles") {
148!
258
        i += 1;
×
259
        settings::n_particles = std::stoll(argv[i]);
×
260

261
      } else if (arg == "-q" || arg == "--verbosity") {
148!
262
        i += 1;
×
263
        settings::verbosity = std::stoi(argv[i]);
×
264
        if (settings::verbosity > 10 || settings::verbosity < 1) {
×
265
          auto msg = fmt::format("Invalid verbosity: {}.", settings::verbosity);
×
266
          strcpy(openmc_err_msg, msg.c_str());
×
267
          return OPENMC_E_INVALID_ARGUMENT;
×
268
        }
×
269

270
      } else if (arg == "-e" || arg == "--event") {
148!
UNCOV
271
        settings::event_based = true;
×
272
      } else if (arg == "-r" || arg == "--restart") {
148!
273
        i += 1;
16✔
274
        // Check what type of file this is
275
        hid_t file_id = file_open(argv[i], 'r', true);
16✔
276
        std::string filetype;
16✔
277
        read_attribute(file_id, "filetype", filetype);
16✔
278
        file_close(file_id);
16✔
279

280
        // Set path and flag for type of run
281
        if (filetype == "statepoint") {
16✔
282
          settings::path_statepoint = argv[i];
10✔
283
          settings::path_statepoint_c = settings::path_statepoint.c_str();
10✔
284
          settings::restart_run = true;
10✔
285
        } else if (filetype == "particle restart") {
6!
286
          settings::path_particle_restart = argv[i];
6✔
287
          settings::particle_restart_run = true;
6✔
288
        } else {
289
          auto msg =
290
            fmt::format("Unrecognized file after restart flag: {}.", filetype);
×
291
          strcpy(openmc_err_msg, msg.c_str());
×
292
          return OPENMC_E_INVALID_ARGUMENT;
×
293
        }
×
294

295
        // If its a restart run check for additional source file
296
        if (settings::restart_run && i + 1 < argc) {
16!
297
          // Check if it has extension we can read
298
          if (ends_with(argv[i + 1], ".h5")) {
×
299

300
            // Check file type is a source file
301
            file_id = file_open(argv[i + 1], 'r', true);
×
302
            read_attribute(file_id, "filetype", filetype);
×
303
            file_close(file_id);
×
304
            if (filetype != "source") {
×
305
              std::string msg {
306
                "Second file after restart flag must be a source file"};
×
307
              strcpy(openmc_err_msg, msg.c_str());
×
308
              return OPENMC_E_INVALID_ARGUMENT;
×
309
            }
×
310

311
            // It is a source file
312
            settings::path_sourcepoint = argv[i + 1];
×
313
            i += 1;
×
314

315
          } else {
316
            // Source is in statepoint file
317
            settings::path_sourcepoint = settings::path_statepoint;
×
318
          }
319

320
        } else {
×
321
          // Source is assumed to be in statepoint file
322
          settings::path_sourcepoint = settings::path_statepoint;
16✔
323
        }
324

325
      } else if (arg == "-g" || arg == "--geometry-debug") {
148!
326
        settings::check_overlaps = true;
×
327
      } else if (arg == "-c" || arg == "--volume") {
132✔
328
        settings::run_mode = RunMode::VOLUME;
118✔
329
      } else if (arg == "-s" || arg == "--threads") {
14!
330
        // Read number of threads
331
        i += 1;
4✔
332

333
#ifdef _OPENMP
334
        // Read and set number of OpenMP threads
335
        int n_threads = std::stoi(argv[i]);
336
        if (n_threads < 1) {
337
          std::string msg {"Number of threads must be positive."};
338
          strcpy(openmc_err_msg, msg.c_str());
339
          return OPENMC_E_INVALID_ARGUMENT;
340
        }
341
        omp_set_num_threads(n_threads);
342
#else
343
        if (mpi::master) {
4!
344
          warning("Ignoring number of threads specified on command line.");
4✔
345
        }
346
#endif
347

348
      } else if (arg == "-?" || arg == "-h" || arg == "--help") {
10!
349
        print_usage();
×
350
        return OPENMC_E_UNASSIGNED;
×
351

352
      } else if (arg == "-v" || arg == "--version") {
10!
353
        print_version();
2✔
354
        print_build_info();
2✔
355
        return OPENMC_E_UNASSIGNED;
2✔
356

357
      } else if (arg == "-t" || arg == "--track") {
8!
358
        settings::write_all_tracks = true;
8✔
359

360
      } else {
361
        fmt::print(stderr, "Unknown option: {}\n", argv[i]);
×
362
        print_usage();
×
363
        return OPENMC_E_UNASSIGNED;
×
364
      }
365

366
      last_flag = i;
168✔
367
    }
368
  }
188✔
369

370
  // Determine directory where XML input files are
371
  if (argc > 1 && last_flag < argc - 1) {
1,256✔
372
    settings::path_input = std::string(argv[last_flag + 1]);
18✔
373

374
    // check that the path is either a valid directory or file
375
    if (!dir_exists(settings::path_input) &&
34✔
376
        !file_exists(settings::path_input)) {
16✔
377
      fatal_error(fmt::format(
6✔
378
        "The path specified to the OpenMC executable '{}' does not exist.",
379
        settings::path_input));
380
    }
381

382
    // Add slash at end of directory if it isn't there
383
    if (!ends_with(settings::path_input, "/") &&
24!
384
        dir_exists(settings::path_input)) {
12✔
385
      settings::path_input += "/";
2✔
386
    }
387
  }
388

389
  return 0;
1,250✔
390
}
391

392
bool read_model_xml()
1,250✔
393
{
394
  std::string model_filename = settings::path_input;
1,250✔
395

396
  // if the current filename is a directory, append the default model filename
397
  if (model_filename.empty() || dir_exists(model_filename))
1,250✔
398
    model_filename += "model.xml";
1,240✔
399

400
  // if this file doesn't exist, stop here
401
  if (!file_exists(model_filename))
1,250✔
402
    return false;
208✔
403

404
  // try to process the path input as an XML file
405
  pugi::xml_document doc;
1,042✔
406
  if (!doc.load_file(model_filename.c_str())) {
1,042!
407
    fatal_error(fmt::format(
×
408
      "Error reading from single XML input file '{}'", model_filename));
409
  }
410

411
  pugi::xml_node root = doc.document_element();
1,042✔
412

413
  // Read settings
414
  if (!check_for_node(root, "settings")) {
1,042!
415
    fatal_error("No <settings> node present in the model.xml file.");
×
416
  }
417
  auto settings_root = root.child("settings");
1,042✔
418

419
  // Verbosity
420
  if (check_for_node(settings_root, "verbosity") && settings::verbosity == -1) {
1,042!
421
    settings::verbosity = std::stoi(get_node_value(settings_root, "verbosity"));
4✔
422
  } else if (settings::verbosity == -1) {
1,038!
423
    settings::verbosity = 7;
1,038✔
424
  }
425

426
  // To this point, we haven't displayed any output since we didn't know what
427
  // the verbosity is. Now that we checked for it, show the title if necessary
428
  if (mpi::master) {
1,042!
429
    if (settings::verbosity >= 2)
1,042✔
430
      title();
1,038✔
431
  }
432

433
  write_message(
1,042✔
434
    fmt::format("Reading model XML file '{}' ...", model_filename), 5);
2,084✔
435

436
  read_settings_xml(settings_root);
1,042✔
437

438
  // If other XML files are present, display warning
439
  // that they will be ignored
440
  auto other_inputs = {"materials.xml", "geometry.xml", "settings.xml",
1,030✔
441
    "tallies.xml", "plots.xml"};
1,030✔
442
  for (const auto& input : other_inputs) {
6,044✔
443
    if (file_exists(settings::path_input + input)) {
5,046✔
444
      warning((fmt::format("Other XML file input(s) are present. These files "
32✔
445
                           "may be ignored in favor of the {} file.",
446
        model_filename)));
447
      break;
32✔
448
    }
449
  }
450

451
  // Read data from chain file
452
  read_chain_file_xml();
1,030✔
453

454
  // Read materials and cross sections
455
  if (!check_for_node(root, "materials")) {
1,030!
456
    fatal_error(fmt::format(
×
457
      "No <materials> node present in the {} file.", model_filename));
458
  }
459

460
  if (settings::run_mode != RunMode::PLOTTING) {
1,030✔
461
    read_cross_sections_xml(root.child("materials"));
1,014✔
462
  }
463
  read_materials_xml(root.child("materials"));
1,030✔
464

465
  // Read geometry
466
  if (!check_for_node(root, "geometry")) {
1,030!
467
    fatal_error(fmt::format(
×
468
      "No <geometry> node present in the {} file.", model_filename));
469
  }
470
  read_geometry_xml(root.child("geometry"));
1,030✔
471

472
  // Final geometry setup and assign temperatures
473
  finalize_geometry();
1,030✔
474

475
  // Finalize cross sections having assigned temperatures
476
  finalize_cross_sections();
1,030✔
477

478
  // Compute cell density multipliers now that material densities
479
  // have been finalized (from geometry_aux.h)
480
  finalize_cell_densities();
1,030✔
481

482
  if (check_for_node(root, "tallies"))
1,030✔
483
    read_tallies_xml(root.child("tallies"));
584✔
484

485
  // Initialize distribcell_filters
486
  prepare_distribcell();
1,026✔
487

488
  if (check_for_node(root, "plots")) {
1,026✔
489
    read_plots_xml(root.child("plots"));
56✔
490
  } else {
491
    // When no <plots> element is present in the model.xml file, check for a
492
    // regular plots.xml file
493
    std::string filename = settings::path_input + "plots.xml";
970✔
494
    if (file_exists(filename)) {
970!
495
      read_plots_xml();
×
496
    }
497
  }
970✔
498

499
  finalize_variance_reduction();
1,024✔
500

501
  return true;
1,024✔
502
}
1,232✔
503

504
void read_separate_xml_files()
208✔
505
{
506
  read_settings_xml();
208✔
507
  if (settings::run_mode != RunMode::PLOTTING) {
206✔
508
    read_cross_sections_xml();
200✔
509
  }
510

511
  // Read data from chain file
512
  read_chain_file_xml();
206✔
513

514
  read_materials_xml();
206✔
515
  read_geometry_xml();
206✔
516

517
  // Final geometry setup and assign temperatures
518
  finalize_geometry();
206✔
519

520
  // Finalize cross sections having assigned temperatures
521
  finalize_cross_sections();
206✔
522

523
  // Compute cell density multipliers now that material densities
524
  // have been finalized (from geometry_aux.h)
525
  finalize_cell_densities();
206✔
526

527
  read_tallies_xml();
206✔
528

529
  // Initialize distribcell_filters
530
  prepare_distribcell();
206✔
531

532
  // Read the plots.xml regardless of plot mode in case plots are requested
533
  // via the API
534
  read_plots_xml();
206✔
535

536
  finalize_variance_reduction();
206✔
537
}
206✔
538

539
void initial_output()
1,230✔
540
{
541
  // write initial output
542
  if (settings::run_mode == RunMode::PLOTTING) {
1,230✔
543
    // Read plots.xml if it exists
544
    if (mpi::master && settings::verbosity >= 5)
20!
545
      print_plot();
16✔
546

547
  } else {
548
    // Write summary information
549
    if (mpi::master && settings::output_summary)
1,210!
550
      write_summary();
1,152✔
551

552
    // Warn if overlap checking is on
553
    if (mpi::master && settings::check_overlaps) {
1,210!
554
      warning("Cell overlap checking is ON.");
×
555
    }
556
  }
557
}
1,230✔
558

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