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

openmc-dev / openmc / 22694846817

04 Mar 2026 11:42PM UTC coverage: 81.537% (-0.2%) from 81.69%
22694846817

Pull #3808

github

web-flow
Merge 41317cfa2 into 2bd06660c
Pull Request #3808: Add properties to settings w/ documentation, c++ loading of filename, and python round-trip test

17549 of 25285 branches covered (69.4%)

Branch coverage included in aggregate %.

82 of 92 new or added lines in 8 files covered. (89.13%)

1498 existing lines in 55 files now uncovered.

57990 of 67359 relevant lines covered (86.09%)

44766605.57 hits per line

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

74.95
/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/weight_windows.h"
40

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

45
int openmc_init(int argc, char* argv[], const void* intracomm)
8,251✔
46
{
47
  using namespace openmc;
8,251✔
48

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

58
  // Initialize MPI for C++
59
  initialize_mpi(comm);
3,631✔
60
#endif
61

62
  // Parse command-line arguments
63
  int err = parse_command_line(argc, argv);
8,251✔
64
  if (err)
8,218✔
65
    return err;
66

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

85
    settings::libmesh_comm = &(settings::libmesh_init->comm());
1,540✔
86
  }
87

88
#endif
89

90
  // Start total and initialization timer
91
  simulation::time_total.start();
8,207✔
92
  simulation::time_initialize.start();
8,207✔
93

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

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

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

117
  // Read XML input files
118
  if (!read_model_xml())
8,207✔
119
    read_separate_xml_files();
1,349✔
120

121
  if (!settings::properties_file.empty()) {
8,114!
NEW
122
    openmc_properties_import(settings::properties_file.c_str(),
×
123
      settings::read_temperatures, settings::read_densities);
124
  }
125

126
  // Reset locale to previous state
127
  if (std::setlocale(LC_ALL, prev_locale.c_str()) == NULL) {
8,114!
128
    fatal_error("Cannot reset locale.");
×
129
  }
130

131
  // Write some initial output under the header if needed
132
  initial_output();
8,114✔
133

134
  // Check for particle restart run
135
  if (settings::particle_restart_run)
8,114✔
136
    settings::run_mode = RunMode::PARTICLE;
41✔
137

138
  // Stop initialization timer
139
  simulation::time_initialize.stop();
8,114✔
140
  simulation::time_total.stop();
8,114✔
141

142
  return 0;
8,114✔
143
}
8,125✔
144

145
namespace openmc {
146

147
#ifdef OPENMC_MPI
148
void initialize_mpi(MPI_Comm intracomm)
3,631✔
149
{
150
  mpi::intracomm = intracomm;
3,631✔
151

152
  // Initialize MPI
153
  int flag;
3,631✔
154
  MPI_Initialized(&flag);
3,631✔
155
  if (!flag)
3,631✔
156
    MPI_Init(nullptr, nullptr);
2,987✔
157

158
  // Determine number of processes and rank for each
159
  MPI_Comm_size(intracomm, &mpi::n_procs);
3,631✔
160
  MPI_Comm_rank(intracomm, &mpi::rank);
3,631✔
161
  mpi::master = (mpi::rank == 0);
3,631✔
162

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

181
  int blocks[] {3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1};
3,631✔
182
  MPI_Datatype types[] {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE,
3,631✔
183
    MPI_DOUBLE, MPI_INT, MPI_INT, MPI_INT, MPI_INT, MPI_LONG, MPI_LONG};
184
  MPI_Type_create_struct(11, blocks, disp, types, &mpi::source_site);
3,631✔
185
  MPI_Type_commit(&mpi::source_site);
3,631✔
186

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

209
  int blocksc[] = {3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
3,631✔
210
  MPI_Datatype typesc[] = {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE,
3,631✔
211
    MPI_DOUBLE, MPI_DOUBLE, MPI_INT, MPI_INT, MPI_INT, MPI_INT, MPI_INT,
212
    MPI_INT, MPI_INT, MPI_INT, MPI_INT64_T, MPI_INT64_T};
213

214
  MPI_Type_create_struct(
3,631✔
215
    16, blocksc, dispc, typesc, &mpi::collision_track_site);
216
  MPI_Type_commit(&mpi::collision_track_site);
3,631✔
217
}
3,631✔
218
#endif // OPENMC_MPI
219

220
int parse_command_line(int argc, char* argv[])
8,251✔
221
{
222
  int last_flag = 0;
8,251✔
223
  for (int i = 1; i < argc; ++i) {
9,540✔
224
    std::string arg {argv[i]};
1,300✔
225
    if (arg[0] == '-') {
1,300✔
226
      if (arg == "-p" || arg == "--plot") {
2,275!
227
        settings::run_mode = RunMode::PLOTTING;
119✔
228
        settings::check_overlaps = true;
119✔
229

230
      } else if (arg == "-n" || arg == "--particles") {
2,156!
231
        i += 1;
×
232
        settings::n_particles = std::stoll(argv[i]);
×
233

234
      } else if (arg == "-q" || arg == "--verbosity") {
2,156!
235
        i += 1;
×
236
        settings::verbosity = std::stoi(argv[i]);
×
237
        if (settings::verbosity > 10 || settings::verbosity < 1) {
×
238
          auto msg = fmt::format("Invalid verbosity: {}.", settings::verbosity);
×
239
          strcpy(openmc_err_msg, msg.c_str());
×
240
          return OPENMC_E_INVALID_ARGUMENT;
×
241
        }
×
242

243
      } else if (arg == "-e" || arg == "--event") {
1,952!
244
        settings::event_based = true;
204✔
245
      } else if (arg == "-r" || arg == "--restart") {
1,644!
246
        i += 1;
104✔
247
        // Check what type of file this is
248
        hid_t file_id = file_open(argv[i], 'r', true);
104✔
249
        std::string filetype;
104✔
250
        read_attribute(file_id, "filetype", filetype);
104✔
251
        file_close(file_id);
104✔
252

253
        // Set path and flag for type of run
254
        if (filetype == "statepoint") {
104✔
255
          settings::path_statepoint = argv[i];
63✔
256
          settings::path_statepoint_c = settings::path_statepoint.c_str();
63✔
257
          settings::restart_run = true;
63✔
258
        } else if (filetype == "particle restart") {
41!
259
          settings::path_particle_restart = argv[i];
41✔
260
          settings::particle_restart_run = true;
41✔
261
        } else {
UNCOV
262
          auto msg =
×
263
            fmt::format("Unrecognized file after restart flag: {}.", filetype);
×
264
          strcpy(openmc_err_msg, msg.c_str());
×
265
          return OPENMC_E_INVALID_ARGUMENT;
×
266
        }
×
267

268
        // If its a restart run check for additional source file
269
        if (settings::restart_run && i + 1 < argc) {
104!
270
          // Check if it has extension we can read
271
          if (ends_with(argv[i + 1], ".h5")) {
×
272

273
            // Check file type is a source file
274
            file_id = file_open(argv[i + 1], 'r', true);
×
275
            read_attribute(file_id, "filetype", filetype);
×
276
            file_close(file_id);
×
277
            if (filetype != "source") {
×
UNCOV
278
              std::string msg {
×
279
                "Second file after restart flag must be a source file"};
×
280
              strcpy(openmc_err_msg, msg.c_str());
×
281
              return OPENMC_E_INVALID_ARGUMENT;
×
282
            }
×
283

284
            // It is a source file
285
            settings::path_sourcepoint = argv[i + 1];
104!
286
            i += 1;
287

288
          } else {
289
            // Source is in statepoint file
290
            settings::path_sourcepoint = settings::path_statepoint;
×
291
          }
292

293
        } else {
294
          // Source is assumed to be in statepoint file
295
          settings::path_sourcepoint = settings::path_statepoint;
208✔
296
        }
297

298
      } else if (arg == "-g" || arg == "--geometry-debug") {
1,644!
299
        settings::check_overlaps = true;
×
300
      } else if (arg == "-c" || arg == "--volume") {
943✔
301
        settings::run_mode = RunMode::VOLUME;
679✔
302
      } else if (arg == "-s" || arg == "--threads") {
158!
303
        // Read number of threads
304
        i += 1;
24✔
305

306
#ifdef _OPENMP
307
        // Read and set number of OpenMP threads
308
        int n_threads = std::stoi(argv[i]);
26✔
309
        if (n_threads < 1) {
13!
310
          std::string msg {"Number of threads must be positive."};
×
311
          strcpy(openmc_err_msg, msg.c_str());
312
          return OPENMC_E_INVALID_ARGUMENT;
313
        }
314
        omp_set_num_threads(n_threads);
13✔
315
#else
316
        if (mpi::master) {
11✔
317
          warning("Ignoring number of threads specified on command line.");
20✔
318
        }
319
#endif
320

321
      } else if (arg == "-?" || arg == "-h" || arg == "--help") {
201!
322
        print_usage();
×
323
        return OPENMC_E_UNASSIGNED;
×
324

325
      } else if (arg == "-v" || arg == "--version") {
123!
326
        print_version();
11✔
327
        print_build_info();
11✔
328
        return OPENMC_E_UNASSIGNED;
11✔
329

330
      } else if (arg == "-t" || arg == "--track") {
56!
331
        settings::write_all_tracks = true;
56✔
332

333
      } else {
334
        fmt::print(stderr, "Unknown option: {}\n", argv[i]);
×
335
        print_usage();
×
336
        return OPENMC_E_UNASSIGNED;
×
337
      }
338

339
      last_flag = i;
340
    }
341
  }
1,300✔
342

343
  // Determine directory where XML input files are
344
  if (argc > 1 && last_flag < argc - 1) {
8,240✔
345
    settings::path_input = std::string(argv[last_flag + 1]);
103✔
346

347
    // check that the path is either a valid directory or file
348
    if (!dir_exists(settings::path_input) &&
193✔
349
        !file_exists(settings::path_input)) {
90✔
350
      fatal_error(fmt::format(
39✔
351
        "The path specified to the OpenMC executable '{}' does not exist.",
352
        settings::path_input));
353
    }
354

355
    // Add slash at end of directory if it isn't there
356
    if (!ends_with(settings::path_input, "/") &&
210!
357
        dir_exists(settings::path_input)) {
70✔
358
      settings::path_input += "/";
8,218✔
359
    }
360
  }
361

362
  return 0;
363
}
364

365
bool read_model_xml()
8,207✔
366
{
367
  std::string model_filename = settings::path_input;
8,207✔
368

369
  // if the current filename is a directory, append the default model filename
370
  if (model_filename.empty() || dir_exists(model_filename))
8,207✔
371
    model_filename += "model.xml";
8,150✔
372

373
  // if this file doesn't exist, stop here
374
  if (!file_exists(model_filename))
8,207✔
375
    return false;
376

377
  // try to process the path input as an XML file
378
  pugi::xml_document doc;
6,858✔
379
  if (!doc.load_file(model_filename.c_str())) {
6,858!
380
    fatal_error(fmt::format(
×
381
      "Error reading from single XML input file '{}'", model_filename));
382
  }
383

384
  pugi::xml_node root = doc.document_element();
6,858✔
385

386
  // Read settings
387
  if (!check_for_node(root, "settings")) {
6,858!
388
    fatal_error("No <settings> node present in the model.xml file.");
×
389
  }
390
  auto settings_root = root.child("settings");
6,858✔
391

392
  // Verbosity
393
  if (check_for_node(settings_root, "verbosity") && settings::verbosity == -1) {
6,858!
394
    settings::verbosity = std::stoi(get_node_value(settings_root, "verbosity"));
60✔
395
  } else if (settings::verbosity == -1) {
6,828!
396
    settings::verbosity = 7;
6,828✔
397
  }
398

399
  // To this point, we haven't displayed any output since we didn't know what
400
  // the verbosity is. Now that we checked for it, show the title if necessary
401
  if (mpi::master) {
6,858✔
402
    if (settings::verbosity >= 2)
6,102✔
403
      title();
6,080✔
404
  }
405

406
  write_message(
6,858✔
407
    fmt::format("Reading model XML file '{}' ...", model_filename), 5);
8,142✔
408

409
  read_settings_xml(settings_root);
6,858✔
410

411
  // If other XML files are present, display warning
412
  // that they will be ignored
413
  auto other_inputs = {"materials.xml", "geometry.xml", "settings.xml",
6,804✔
414
    "tallies.xml", "plots.xml"};
6,804✔
415
  for (const auto& input : other_inputs) {
40,030✔
416
    if (file_exists(settings::path_input + input)) {
33,416✔
417
      warning((fmt::format("Other XML file input(s) are present. These files "
225✔
418
                           "may be ignored in favor of the {} file.",
419
        model_filename)));
420
      break;
190✔
421
    }
422
  }
423

424
  // Read data from chain file
425
  read_chain_file_xml();
6,804✔
426

427
  // Read materials and cross sections
428
  if (!check_for_node(root, "materials")) {
6,804!
429
    fatal_error(fmt::format(
×
430
      "No <materials> node present in the {} file.", model_filename));
431
  }
432

433
  if (settings::run_mode != RunMode::PLOTTING) {
6,804✔
434
    read_cross_sections_xml(root.child("materials"));
6,718✔
435
  }
436
  read_materials_xml(root.child("materials"));
6,804✔
437

438
  // Read geometry
439
  if (!check_for_node(root, "geometry")) {
6,804!
440
    fatal_error(fmt::format(
×
441
      "No <geometry> node present in the {} file.", model_filename));
442
  }
443
  read_geometry_xml(root.child("geometry"));
6,804✔
444

445
  // Final geometry setup and assign temperatures
446
  finalize_geometry();
6,802✔
447

448
  // Finalize cross sections having assigned temperatures
449
  finalize_cross_sections();
6,802✔
450

451
  // Compute cell density multipliers now that material densities
452
  // have been finalized (from geometry_aux.h)
453
  finalize_cell_densities();
6,802✔
454

455
  if (check_for_node(root, "tallies"))
6,802✔
456
    read_tallies_xml(root.child("tallies"));
3,956✔
457

458
  // Initialize distribcell_filters
459
  prepare_distribcell();
6,784✔
460

461
  if (check_for_node(root, "plots")) {
6,784✔
462
    read_plots_xml(root.child("plots"));
343✔
463
  } else {
464
    // When no <plots> element is present in the model.xml file, check for a
465
    // regular plots.xml file
466
    std::string filename = settings::path_input + "plots.xml";
6,441✔
467
    if (file_exists(filename)) {
6,441!
468
      read_plots_xml();
×
469
    }
470
  }
6,441✔
471

472
  finalize_variance_reduction();
6,775✔
473

474
  return true;
6,775✔
475
}
14,899✔
476

477
void read_separate_xml_files()
1,349✔
478
{
479
  read_settings_xml();
1,349✔
480
  if (settings::run_mode != RunMode::PLOTTING) {
1,339✔
481
    read_cross_sections_xml();
1,306✔
482
  }
483

484
  // Read data from chain file
485
  read_chain_file_xml();
1,339✔
486

487
  read_materials_xml();
1,339✔
488
  read_geometry_xml();
1,339✔
489

490
  // Final geometry setup and assign temperatures
491
  finalize_geometry();
1,339✔
492

493
  // Finalize cross sections having assigned temperatures
494
  finalize_cross_sections();
1,339✔
495

496
  // Compute cell density multipliers now that material densities
497
  // have been finalized (from geometry_aux.h)
498
  finalize_cell_densities();
1,339✔
499

500
  read_tallies_xml();
1,339✔
501

502
  // Initialize distribcell_filters
503
  prepare_distribcell();
1,339✔
504

505
  // Read the plots.xml regardless of plot mode in case plots are requested
506
  // via the API
507
  read_plots_xml();
1,339✔
508

509
  finalize_variance_reduction();
1,339✔
510
}
1,339✔
511

512
void initial_output()
8,114✔
513
{
514
  // write initial output
515
  if (settings::run_mode == RunMode::PLOTTING) {
8,114✔
516
    // Read plots.xml if it exists
517
    if (mpi::master && settings::verbosity >= 5)
110!
518
      print_plot();
88✔
519

520
  } else {
521
    // Write summary information
522
    if (mpi::master && settings::output_summary)
8,004✔
523
      write_summary();
6,735✔
524

525
    // Warn if overlap checking is on
526
    if (mpi::master && settings::check_overlaps) {
8,004!
527
      warning("Cell overlap checking is ON.");
×
528
    }
529
  }
530
}
8,114✔
531

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