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

openmc-dev / openmc / 18537555145

15 Oct 2025 05:37PM UTC coverage: 81.983% (-3.2%) from 85.194%
18537555145

Pull #3417

github

web-flow
Merge 3615a1fcc into e9077b137
Pull Request #3417: Addition of a collision tracking feature

16802 of 23354 branches covered (71.94%)

Branch coverage included in aggregate %.

480 of 522 new or added lines in 13 files covered. (91.95%)

483 existing lines in 53 files now uncovered.

54134 of 63171 relevant lines covered (85.69%)

43199115.04 hits per line

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

77.73
/src/mgxs_interface.cpp
1
#include "openmc/mgxs_interface.h"
2

3
#include <string>
4
#include <unordered_set>
5

6
#include <fmt/format.h>
7

8
#include "openmc/cell.h"
9
#include "openmc/container_util.h"
10
#include "openmc/cross_sections.h"
11
#include "openmc/error.h"
12
#include "openmc/file_utils.h"
13
#include "openmc/geometry_aux.h"
14
#include "openmc/hdf5_interface.h"
15
#include "openmc/material.h"
16
#include "openmc/math_functions.h"
17
#include "openmc/nuclide.h"
18
#include "openmc/search.h"
19
#include "openmc/settings.h"
20

21
namespace openmc {
22

23
//==============================================================================
24
// Mgxs data loading interface methods
25
//==============================================================================
26

27
namespace data {
28
MgxsInterface mg;
29
}
30

31
MgxsInterface::MgxsInterface(const std::string& path_cross_sections,
×
32
  const vector<std::string> xs_to_read, const vector<vector<double>> xs_temps)
×
33
{
34
  read_header(path_cross_sections);
×
35
  set_nuclides_and_temperatures(xs_to_read, xs_temps);
×
36
  init();
×
UNCOV
37
}
×
38

39
void MgxsInterface::set_nuclides_and_temperatures(
×
40
  vector<std::string> xs_to_read, vector<vector<double>> xs_temps)
41
{
42
  // Check to remove all duplicates
43
  xs_to_read_ = xs_to_read;
×
44
  xs_temps_to_read_ = xs_temps;
×
45
  if (xs_to_read_.size() != xs_temps.size())
×
46
    fatal_error("The list of macro XS temperatures to read does not "
×
47
                "correspond in length to the number of XS names. ");
UNCOV
48
}
×
49

50
void MgxsInterface::init()
1,111✔
51
{
52

53
  // Check that at least some data was set to be read
54
  if (xs_to_read_.size() == 0)
1,111!
55
    warning("No MGXS nuclides were set to be read.");
×
56

57
  // Check if MGXS Library exists
58
  if (!file_exists(cross_sections_path_)) {
1,111!
59
    // Could not find MGXS Library file
60
    fatal_error(fmt::format(
×
61
      "Cross sections HDF5 file '{}' does not exist!", cross_sections_path_));
×
62
  }
63

64
  write_message("Loading cross section data...", 5);
1,111✔
65

66
  // Open file for reading
67
  hid_t file_id = file_open(cross_sections_path_, 'r');
1,111✔
68

69
  // Read filetype
70
  std::string type;
1,111✔
71
  read_attribute(file_id, "filetype", type);
1,111✔
72
  if (type != "mgxs") {
1,111!
73
    fatal_error("Provided MGXS Library is not a MGXS Library file.");
×
74
  }
75

76
  // Read revision number for the MGXS Library file and make sure it matches
77
  // with the current version
78
  array<int, 2> array;
79
  read_attribute(file_id, "version", array);
1,111✔
80
  if (array != VERSION_MGXS_LIBRARY) {
1,111!
81
    fatal_error("MGXS Library file version does not match current version "
×
82
                "supported by OpenMC.");
83
  }
84

85
  // ==========================================================================
86
  // READ ALL MGXS CROSS SECTION TABLES
87
  for (unsigned i_nuc = 0; i_nuc < xs_to_read_.size(); ++i_nuc)
3,882✔
88
    add_mgxs(file_id, xs_to_read_[i_nuc], xs_temps_to_read_[i_nuc]);
2,771✔
89

90
  file_close(file_id);
1,111✔
91

92
  create_macro_xs();
1,111✔
93
}
1,111✔
94

95
//==============================================================================
96

97
void MgxsInterface::add_mgxs(
2,771✔
98
  hid_t file_id, const std::string& name, const vector<double>& temperature)
99
{
100
  write_message(5, "Loading {} data...", name);
2,771✔
101

102
  // Check to make sure cross section set exists in the library
103
  hid_t xs_grp;
104
  if (object_exists(file_id, name.c_str())) {
2,771!
105
    xs_grp = open_group(file_id, name.c_str());
2,771✔
106
  } else {
107
    fatal_error(
×
108
      fmt::format("Data for {} does not exist in provided MGXS Library", name));
×
109
  }
110

111
  nuclides_.emplace_back(
2,771✔
112
    xs_grp, temperature, num_energy_groups_, num_delayed_groups_);
2,771✔
113
  close_group(xs_grp);
2,771✔
114
}
2,771✔
115

116
//==============================================================================
117

118
void MgxsInterface::create_macro_xs()
1,111✔
119
{
120
  // Get temperatures to read for each material
121
  auto kTs = get_mat_kTs();
1,111✔
122

123
  // Force all nuclides in a material to be the same representation.
124
  // Therefore type(nuclides[mat->nuclide_[0]]) dictates type(macroxs).
125
  // At the same time, we will find the scattering type, as that will dictate
126
  // how we allocate the scatter object within macroxs.
127

128
  for (int i = 0; i < model::materials.size(); ++i) {
3,514✔
129
    // First we have to normalize the densities as it has not been called yet
130
    // for MG mode
131
    auto& mat {model::materials[i]};
2,403✔
132
    mat->finalize();
2,403✔
133
    if (kTs[i].size() > 0) {
2,403✔
134
      // Convert atom_densities to a vector
135
      vector<double> atom_densities(
136
        mat->atom_density_.begin(), mat->atom_density_.end());
2,339✔
137

138
      // Build array of pointers to nuclides's Mgxs objects needed for this
139
      // material
140
      vector<Mgxs*> mgxs_ptr;
2,339✔
141
      for (int i_nuclide : mat->nuclide_) {
5,062✔
142
        mgxs_ptr.push_back(&nuclides_[i_nuclide]);
2,723✔
143
      }
144

145
      macro_xs_.emplace_back(mat->name_, kTs[i], mgxs_ptr, atom_densities,
2,339✔
146
        num_energy_groups_, num_delayed_groups_);
2,339✔
147
    } else {
2,339✔
148
      // Preserve the ordering of materials by including a blank entry
149
      macro_xs_.emplace_back(false);
64✔
150
    }
151
  }
152
}
1,111✔
153

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

156
vector<vector<double>> MgxsInterface::get_mat_kTs()
1,111✔
157
{
158
  vector<vector<double>> kTs(model::materials.size());
1,111✔
159

160
  for (const auto& cell : model::cells) {
6,328✔
161
    // Skip non-material cells
162
    if (cell->fill_ != C_NONE)
5,217✔
163
      continue;
2,109✔
164

165
    for (int j = 0; j < cell->material_.size(); ++j) {
6,216✔
166
      // Skip void materials
167
      int i_material = cell->material_[j];
3,108✔
168
      if (i_material == MATERIAL_VOID)
3,108✔
169
        continue;
65✔
170

171
      // Get temperature of cell (rounding to nearest integer)
172
      double sqrtkT =
173
        cell->sqrtkT_.size() == 1 ? cell->sqrtkT_[j] : cell->sqrtkT_[0];
3,043!
174
      double kT = sqrtkT * sqrtkT;
3,043✔
175

176
      // Add temperature if it hasn't already been added
177
      if (!contains(kTs[i_material], kT)) {
3,043✔
178
        kTs[i_material].push_back(kT);
2,355✔
179
      }
180
    }
181
  }
182
  return kTs;
1,111✔
183
}
×
184

185
//==============================================================================
186

187
int MgxsInterface::get_group_index(double E)
4,632✔
188
{
189
  int g =
190
    lower_bound_index(rev_energy_bins_.begin(), rev_energy_bins_.end(), E);
4,632✔
191
  return num_energy_groups_ - g - 1.;
4,632✔
192
}
193

194
//==============================================================================
195

196
void MgxsInterface::read_header(const std::string& path_cross_sections)
1,111✔
197
{
198
  // Save name of HDF5 file to be read to struct data
199
  cross_sections_path_ = path_cross_sections;
1,111✔
200

201
  // Check if MGXS Library exists
202
  if (!file_exists(cross_sections_path_)) {
1,111!
203
    // Could not find MGXS Library file
204
    fatal_error(fmt::format(
×
205
      "Cross section HDF5 file '{}' does not exist", cross_sections_path_));
×
206
  }
207
  write_message("Reading cross sections HDF5 file...", 5);
1,111✔
208

209
  // Open file for reading
210
  hid_t file_id = file_open(cross_sections_path_, 'r', true);
1,111✔
211

212
  ensure_exists(file_id, "energy_groups", true);
1,111✔
213
  read_attribute(file_id, "energy_groups", num_energy_groups_);
1,111✔
214

215
  if (attribute_exists(file_id, "delayed_groups")) {
1,111!
216
    read_attribute(file_id, "delayed_groups", num_delayed_groups_);
1,111✔
217
  } else {
218
    num_delayed_groups_ = 0;
×
219
  }
220

221
  ensure_exists(file_id, "group structure", true);
1,111✔
222
  read_attribute(file_id, "group structure", rev_energy_bins_);
1,111✔
223

224
  // Reverse energy bins
225
  std::copy(rev_energy_bins_.crbegin(), rev_energy_bins_.crend(),
1,111✔
226
    std::back_inserter(energy_bins_));
1,111✔
227

228
  // Create average energies
229
  for (int i = 0; i < energy_bins_.size() - 1; ++i) {
4,719✔
230
    energy_bin_avg_.push_back(0.5 * (energy_bins_[i] + energy_bins_[i + 1]));
3,608✔
231
  }
232

233
  // Add entries into libraries for MG data
234
  xs_names_ = group_names(file_id);
1,111✔
235
  if (xs_names_.empty()) {
1,111!
236
    fatal_error("At least one MGXS data set must be present in mgxs "
×
237
                "library file!");
238
  }
239

240
  // Close MGXS HDF5 file
241
  file_close(file_id);
1,111✔
242
}
1,111✔
243

244
void put_mgxs_header_data_to_globals()
1,111✔
245
{
246
  // Get the minimum and maximum energies
247
  int neutron = static_cast<int>(ParticleType::neutron);
1,111✔
248
  data::energy_min[neutron] = data::mg.energy_bins_.back();
1,111✔
249
  data::energy_max[neutron] = data::mg.energy_bins_.front();
1,111✔
250

251
  // Save available XS names to library list, so that when
252
  // materials are read, the specified mgxs can be confirmed
253
  // as present
254
  for (auto& name : data::mg.xs_names_) {
4,042✔
255
    Library lib {};
2,931✔
256
    lib.type_ = Library::Type::neutron;
2,931✔
257
    lib.materials_.push_back(name);
2,931✔
258
    data::libraries.push_back(lib);
2,931✔
259
  }
2,931✔
260
}
1,111✔
261

262
void set_mg_interface_nuclides_and_temps()
1,111✔
263
{
264
  // Get temperatures from global data
265
  vector<vector<double>> nuc_temps(data::nuclide_map.size());
1,111✔
266
  vector<vector<double>> dummy;
1,111✔
267
  get_temperatures(nuc_temps, dummy);
1,111✔
268

269
  // Build vector of nuclide names which are to be read
270
  vector<std::string> nuclide_names(data::nuclide_map.size());
1,111✔
271
  for (const auto& kv : data::nuclide_map) {
3,882✔
272
    nuclide_names[kv.second] = kv.first;
2,771✔
273
  }
274

275
  std::unordered_set<std::string> already_read;
1,111✔
276

277
  // Loop over materials to find xs and temperature to be read
278
  for (const auto& mat : model::materials) {
3,514✔
279
    for (int i_nuc : mat->nuclide_) {
5,190✔
280
      std::string& name = nuclide_names[i_nuc];
2,787✔
281

282
      if (already_read.find(name) == already_read.end()) {
2,787✔
283
        data::mg.xs_to_read_.push_back(name);
2,771✔
284
        data::mg.xs_temps_to_read_.push_back(nuc_temps[i_nuc]);
2,771✔
285
        already_read.insert(name);
2,771✔
286
      }
287
    }
288
  }
289
}
1,111✔
290

291
void mark_fissionable_mgxs_materials()
1,111✔
292
{
293
  // Loop over all files
294
  for (const auto& mat : model::materials) {
3,514✔
295
    for (int i_nuc : mat->nuclide_) {
5,190✔
296
      if (data::mg.nuclides_[i_nuc].fissionable) {
2,787✔
297
        mat->fissionable() = true;
992✔
298
      }
299
    }
300
  }
301
}
1,111✔
302

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