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

openmc-dev / openmc / 10424279536

16 Aug 2024 06:08PM UTC coverage: 84.718% (-0.2%) from 84.9%
10424279536

Pull #3112

github

web-flow
Merge 6ef06d3b8 into 4ef1faf76
Pull Request #3112: Revamp CI with dependency and Python caching for efficient installs

49450 of 58370 relevant lines covered (84.72%)

45353796.23 hits per line

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

87.12
/src/cross_sections.cpp
1
#include "openmc/cross_sections.h"
2

3
#include "openmc/capi.h"
4
#include "openmc/constants.h"
5
#include "openmc/container_util.h"
6
#include "openmc/error.h"
7
#include "openmc/file_utils.h"
8
#include "openmc/geometry_aux.h"
9
#include "openmc/hdf5_interface.h"
10
#include "openmc/material.h"
11
#include "openmc/message_passing.h"
12
#include "openmc/mgxs_interface.h"
13
#include "openmc/nuclide.h"
14
#include "openmc/photon.h"
15
#include "openmc/settings.h"
16
#include "openmc/simulation.h"
17
#include "openmc/string_utils.h"
18
#include "openmc/thermal.h"
19
#include "openmc/timer.h"
20
#include "openmc/wmp.h"
21
#include "openmc/xml_interface.h"
22

23
#include "pugixml.hpp"
24

25
#include <cstdlib> // for getenv
26
#include <unordered_set>
27

28
namespace openmc {
29

30
//==============================================================================
31
// Global variable declarations
32
//==============================================================================
33

34
namespace data {
35

36
std::map<LibraryKey, std::size_t> library_map;
37
vector<Library> libraries;
38
} // namespace data
39

40
//==============================================================================
41
// Library methods
42
//==============================================================================
43

44
Library::Library(pugi::xml_node node, const std::string& directory)
10,007,538✔
45
{
46
  // Get type of library
47
  if (check_for_node(node, "type")) {
10,007,538✔
48
    auto type = get_node_value(node, "type");
10,007,538✔
49
    if (type == "neutron") {
10,007,538✔
50
      type_ = Type::neutron;
4,382,217✔
51
    } else if (type == "thermal") {
5,625,321✔
52
      type_ = Type::thermal;
207,564✔
53
    } else if (type == "photon") {
5,417,757✔
54
      type_ = Type::photon;
1,035,900✔
55
    } else if (type == "wmp") {
4,381,857✔
56
      type_ = Type::wmp;
4,381,857✔
57
    } else {
58
      fatal_error("Unrecognized library type: " + type);
×
59
    }
60
  } else {
10,007,538✔
61
    fatal_error("Missing library type");
×
62
  }
63

64
  // Get list of materials
65
  if (check_for_node(node, "materials")) {
10,007,538✔
66
    materials_ = get_node_array<std::string>(node, "materials");
10,007,538✔
67
  }
68

69
  // determine path of cross section table
70
  if (!check_for_node(node, "path")) {
10,007,538✔
71
    fatal_error("Missing library path");
×
72
  }
73
  std::string path = get_node_value(node, "path");
10,007,538✔
74

75
  if (starts_with(path, "/")) {
10,007,538✔
76
    path_ = path;
×
77
  } else if (ends_with(directory, "/")) {
10,007,538✔
78
    path_ = directory + path;
×
79
  } else if (!directory.empty()) {
10,007,538✔
80
    path_ = directory + "/" + path;
9,984,330✔
81
  } else {
82
    path_ = path;
23,208✔
83
  }
84

85
  if (!file_exists(path_)) {
10,007,538✔
86
    warning("Cross section library " + path_ + " does not exist.");
×
87
  }
88
}
10,007,538✔
89

90
//==============================================================================
91
// Non-member functions
92
//==============================================================================
93

94
void read_cross_sections_xml()
3,081✔
95
{
96
  pugi::xml_document doc;
3,081✔
97
  std::string filename = settings::path_input + "materials.xml";
3,081✔
98
  // Check if materials.xml exists
99
  if (!file_exists(filename)) {
3,081✔
100
    fatal_error("Material XML file '" + filename + "' does not exist.");
×
101
  }
102
  // Parse materials.xml file
103
  doc.load_file(filename.c_str());
3,081✔
104

105
  auto root = doc.document_element();
3,081✔
106

107
  read_cross_sections_xml(root);
3,081✔
108
}
3,081✔
109

110
void read_cross_sections_xml(pugi::xml_node root)
12,249✔
111
{
112
  // Find cross_sections.xml file -- the first place to look is the
113
  // materials.xml file. If no file is found there, then we check the
114
  // OPENMC_CROSS_SECTIONS environment variable
115
  if (!check_for_node(root, "cross_sections")) {
12,249✔
116
    // No cross_sections.xml file specified in settings.xml, check
117
    // environment variable
118
    if (settings::run_CE) {
10,335✔
119
      char* envvar = std::getenv("OPENMC_CROSS_SECTIONS");
10,335✔
120
      if (!envvar) {
10,335✔
121
        fatal_error(
×
122
          "No cross_sections.xml file was specified in "
123
          "materials.xml or in the OPENMC_CROSS_SECTIONS"
124
          " environment variable. OpenMC needs such a file to identify "
125
          "where to find data libraries. Please consult the"
126
          " user's guide at https://docs.openmc.org/ for "
127
          "information on how to set up data libraries.");
128
      }
129
      settings::path_cross_sections = envvar;
10,335✔
130
    } else {
131
      char* envvar = std::getenv("OPENMC_MG_CROSS_SECTIONS");
×
132
      if (!envvar) {
×
133
        fatal_error(
×
134
          "No mgxs.h5 file was specified in "
135
          "materials.xml or in the OPENMC_MG_CROSS_SECTIONS environment "
136
          "variable. OpenMC needs such a file to identify where to "
137
          "find MG cross section libraries. Please consult the user's "
138
          "guide at https://docs.openmc.org for information on "
139
          "how to set up MG cross section libraries.");
140
      }
141
      settings::path_cross_sections = envvar;
×
142
    }
143
  } else {
144
    settings::path_cross_sections = get_node_value(root, "cross_sections");
1,914✔
145

146
    // If no '/' found, the file is probably in the input directory
147
    auto pos = settings::path_cross_sections.rfind("/");
1,914✔
148
    if (pos == std::string::npos && !settings::path_input.empty()) {
1,914✔
149
      settings::path_cross_sections =
150
        settings::path_input + "/" + settings::path_cross_sections;
×
151
    }
152
  }
153

154
  // Now that the cross_sections.xml or mgxs.h5 has been located, read it in
155
  if (settings::run_CE) {
12,249✔
156
    read_ce_cross_sections_xml();
10,719✔
157
  } else {
158
    data::mg.read_header(settings::path_cross_sections);
1,530✔
159
    put_mgxs_header_data_to_globals();
1,530✔
160
  }
161

162
  // Establish mapping between (type, material) and index in libraries
163
  int i = 0;
12,249✔
164
  for (const auto& lib : data::libraries) {
10,023,629✔
165
    for (const auto& name : lib.materials_) {
20,022,760✔
166
      LibraryKey key {lib.type_, name};
10,011,380✔
167
      data::library_map.insert({key, i});
10,011,380✔
168
    }
10,011,380✔
169
    ++i;
10,011,380✔
170
  }
171

172
  // Check that 0K nuclides are listed in the cross_sections.xml file
173
  for (const auto& name : settings::res_scat_nuclides) {
12,351✔
174
    LibraryKey key {Library::Type::neutron, name};
102✔
175
    if (data::library_map.find(key) == data::library_map.end()) {
102✔
176
      fatal_error("Could not find resonant scatterer " + name +
×
177
                  " in cross_sections.xml file!");
178
    }
179
  }
102✔
180
}
12,249✔
181

182
void read_ce_cross_sections(const vector<vector<double>>& nuc_temps,
10,719✔
183
  const vector<vector<double>>& thermal_temps)
184
{
185
  std::unordered_set<std::string> already_read;
10,719✔
186

187
  // Construct a vector of nuclide names because we haven't loaded nuclide data
188
  // yet, but we need to know the name of the i-th nuclide
189
  vector<std::string> nuclide_names(data::nuclide_map.size());
10,719✔
190
  vector<std::string> thermal_names(data::thermal_scatt_map.size());
10,719✔
191
  for (const auto& kv : data::nuclide_map) {
64,371✔
192
    nuclide_names[kv.second] = kv.first;
53,652✔
193
  }
194
  for (const auto& kv : data::thermal_scatt_map) {
12,932✔
195
    thermal_names[kv.second] = kv.first;
2,213✔
196
  }
197

198
  // Read cross sections
199
  for (const auto& mat : model::materials) {
35,103✔
200
    for (int i_nuc : mat->nuclide_) {
140,068✔
201
      // Find name of corresponding nuclide. Because we haven't actually loaded
202
      // data, we don't have the name available, so instead we search through
203
      // all key/value pairs in nuclide_map
204
      std::string& name = nuclide_names[i_nuc];
115,684✔
205

206
      // If we've already read this nuclide, skip it
207
      if (already_read.find(name) != already_read.end())
115,684✔
208
        continue;
62,032✔
209

210
      const auto& temps = nuc_temps[i_nuc];
53,652✔
211
      int err = openmc_load_nuclide(name.c_str(), temps.data(), temps.size());
53,652✔
212
      if (err < 0)
53,652✔
213
        throw std::runtime_error {openmc_err_msg};
×
214

215
      already_read.insert(name);
53,652✔
216
    }
217
  }
218

219
  // Perform final tasks -- reading S(a,b) tables, normalizing densities
220
  for (auto& mat : model::materials) {
35,103✔
221
    for (const auto& table : mat->thermal_tables_) {
28,128✔
222
      // Get name of S(a,b) table
223
      int i_table = table.index_table;
3,744✔
224
      std::string& name = thermal_names[i_table];
3,744✔
225

226
      if (already_read.find(name) == already_read.end()) {
3,744✔
227
        LibraryKey key {Library::Type::thermal, name};
2,213✔
228
        int idx = data::library_map[key];
2,213✔
229
        std::string& filename = data::libraries[idx].path_;
2,213✔
230

231
        write_message(6, "Reading {} from {}", name, filename);
2,213✔
232

233
        // Open file and make sure version matches
234
        hid_t file_id = file_open(filename, 'r');
2,213✔
235
        check_data_version(file_id);
2,213✔
236

237
        // Read thermal scattering data from HDF5
238
        hid_t group = open_group(file_id, name.c_str());
2,213✔
239
        data::thermal_scatt.push_back(
2,213✔
240
          make_unique<ThermalScattering>(group, thermal_temps[i_table]));
4,426✔
241
        close_group(group);
2,213✔
242
        file_close(file_id);
2,213✔
243

244
        // Add name to dictionary
245
        already_read.insert(name);
2,213✔
246
      }
2,213✔
247
    } // thermal_tables_
248

249
    // Finish setting up materials (normalizing densities, etc.)
250
    mat->finalize();
24,384✔
251
  } // materials
252

253
  if (settings::photon_transport &&
10,719✔
254
      settings::electron_treatment == ElectronTreatment::TTB) {
508✔
255
    // Take logarithm of energies since they are log-log interpolated
256
    data::ttb_e_grid = xt::log(data::ttb_e_grid);
484✔
257
  }
258

259
  // Show minimum/maximum temperature
260
  write_message(
10,719✔
261
    4, "Minimum neutron data temperature: {} K", data::temperature_min);
262
  write_message(
10,719✔
263
    4, "Maximum neutron data temperature: {} K", data::temperature_max);
264

265
  // If the user wants multipole, make sure we found a multipole library.
266
  if (settings::temperature_multipole) {
10,719✔
267
    bool mp_found = false;
68✔
268
    for (const auto& nuc : data::nuclides) {
68✔
269
      if (nuc->multipole_) {
68✔
270
        mp_found = true;
68✔
271
        break;
68✔
272
      }
273
    }
274
    if (mpi::master && !mp_found) {
68✔
275
      warning("Windowed multipole functionality is turned on, but no multipole "
×
276
              "libraries were found. Make sure that windowed multipole data is "
277
              "present in your cross_sections.xml file.");
278
    }
279
  }
280
}
10,719✔
281

282
void read_ce_cross_sections_xml()
10,719✔
283
{
284
  // Check if cross_sections.xml exists
285
  const auto& filename = settings::path_cross_sections;
10,719✔
286
  if (dir_exists(filename)) {
10,719✔
287
    fatal_error("OPENMC_CROSS_SECTIONS is set to a directory. "
×
288
                "It should be set to an XML file.");
289
  }
290
  if (!file_exists(filename)) {
10,719✔
291
    // Could not find cross_sections.xml file
292
    fatal_error("Cross sections XML file '" + filename + "' does not exist.");
×
293
  }
294

295
  write_message("Reading cross sections XML file...", 5);
10,719✔
296

297
  // Parse cross_sections.xml file
298
  pugi::xml_document doc;
10,719✔
299
  auto result = doc.load_file(filename.c_str());
10,719✔
300
  if (!result) {
10,719✔
301
    fatal_error("Error processing cross_sections.xml file.");
×
302
  }
303
  auto root = doc.document_element();
10,719✔
304

305
  std::string directory;
10,719✔
306
  if (check_for_node(root, "directory")) {
10,719✔
307
    // Copy directory information if present
308
    directory = get_node_value(root, "directory");
×
309
  } else {
310
    // If no directory is listed in cross_sections.xml, by default select the
311
    // directory in which the cross_sections.xml file resides
312

313
    // TODO: Use std::filesystem functionality when C++17 is adopted
314
    auto pos = filename.rfind("/");
10,719✔
315
    if (pos == std::string::npos) {
10,719✔
316
      // No '\\' found, so the file must be in the same directory as
317
      // materials.xml
318
      directory = settings::path_input;
24✔
319
    } else {
320
      directory = filename.substr(0, pos);
10,695✔
321
    }
322
  }
323

324
  for (const auto& node_library : root.children("library")) {
10,018,257✔
325
    data::libraries.emplace_back(node_library, directory);
10,007,538✔
326
  }
327

328
  // Make sure file was not empty
329
  if (data::libraries.empty()) {
10,719✔
330
    fatal_error(
×
331
      "No cross section libraries present in cross_sections.xml file.");
332
  }
333
}
10,719✔
334

335
void finalize_cross_sections()
12,725✔
336
{
337
  if (settings::run_mode != RunMode::PLOTTING) {
12,725✔
338
    simulation::time_read_xs.start();
12,249✔
339
    if (settings::run_CE) {
12,249✔
340
      // Determine desired temperatures for each nuclide and S(a,b) table
341
      double_2dvec nuc_temps(data::nuclide_map.size());
10,719✔
342
      double_2dvec thermal_temps(data::thermal_scatt_map.size());
10,719✔
343
      get_temperatures(nuc_temps, thermal_temps);
10,719✔
344

345
      // Read continuous-energy cross sections from HDF5
346
      read_ce_cross_sections(nuc_temps, thermal_temps);
10,719✔
347
    } else {
10,719✔
348
      // Create material macroscopic data for MGXS
349
      set_mg_interface_nuclides_and_temps();
1,530✔
350
      data::mg.init();
1,530✔
351
      mark_fissionable_mgxs_materials();
1,530✔
352
    }
353
    simulation::time_read_xs.stop();
12,249✔
354
  }
355
}
12,725✔
356

357
void library_clear()
12,928✔
358
{
359
  data::libraries.clear();
12,928✔
360
  data::library_map.clear();
12,928✔
361
}
12,928✔
362

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