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

openmc-dev / openmc / 10586562087

27 Aug 2024 10:05PM UTC coverage: 84.707% (-0.2%) from 84.9%
10586562087

Pull #3112

github

web-flow
Merge f7f32bf18 into 5bc04b5d7
Pull Request #3112: Revamp CI with dependency and Python caching for efficient installs

49553 of 58499 relevant lines covered (84.71%)

34324762.08 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)
5,036,130✔
45
{
46
  // Get type of library
47
  if (check_for_node(node, "type")) {
5,036,130✔
48
    auto type = get_node_value(node, "type");
5,036,130✔
49
    if (type == "neutron") {
5,036,130✔
50
      type_ = Type::neutron;
2,205,279✔
51
    } else if (type == "thermal") {
2,830,851✔
52
      type_ = Type::thermal;
104,452✔
53
    } else if (type == "photon") {
2,726,399✔
54
      type_ = Type::photon;
521,300✔
55
    } else if (type == "wmp") {
2,205,099✔
56
      type_ = Type::wmp;
2,205,099✔
57
    } else {
58
      fatal_error("Unrecognized library type: " + type);
×
59
    }
60
  } else {
5,036,130✔
61
    fatal_error("Missing library type");
×
62
  }
63

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

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

75
  if (starts_with(path, "/")) {
5,036,130✔
76
    path_ = path;
×
77
  } else if (ends_with(directory, "/")) {
5,036,130✔
78
    path_ = directory + path;
×
79
  } else if (!directory.empty()) {
5,036,130✔
80
    path_ = directory + "/" + path;
5,024,526✔
81
  } else {
82
    path_ = path;
11,604✔
83
  }
84

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

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

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

105
  auto root = doc.document_element();
1,561✔
106

107
  read_cross_sections_xml(root);
1,561✔
108
}
1,561✔
109

110
void read_cross_sections_xml(pugi::xml_node root)
6,260✔
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")) {
6,260✔
116
    // No cross_sections.xml file specified in settings.xml, check
117
    // environment variable
118
    if (settings::run_CE) {
5,201✔
119
      char* envvar = std::getenv("OPENMC_CROSS_SECTIONS");
5,201✔
120
      if (!envvar) {
5,201✔
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;
5,201✔
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,059✔
145

146
    // If no '/' found, the file is probably in the input directory
147
    auto pos = settings::path_cross_sections.rfind("/");
1,059✔
148
    if (pos == std::string::npos && !settings::path_input.empty()) {
1,059✔
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) {
6,260✔
156
    read_ce_cross_sections_xml();
5,393✔
157
  } else {
158
    data::mg.read_header(settings::path_cross_sections);
867✔
159
    put_mgxs_header_data_to_globals();
867✔
160
  }
161

162
  // Establish mapping between (type, material) and index in libraries
163
  int i = 0;
6,260✔
164
  for (const auto& lib : data::libraries) {
5,044,617✔
165
    for (const auto& name : lib.materials_) {
10,076,714✔
166
      LibraryKey key {lib.type_, name};
5,038,357✔
167
      data::library_map.insert({key, i});
5,038,357✔
168
    }
5,038,357✔
169
    ++i;
5,038,357✔
170
  }
171

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

182
void read_ce_cross_sections(const vector<vector<double>>& nuc_temps,
5,393✔
183
  const vector<vector<double>>& thermal_temps)
184
{
185
  std::unordered_set<std::string> already_read;
5,393✔
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());
5,393✔
190
  vector<std::string> thermal_names(data::thermal_scatt_map.size());
5,393✔
191
  for (const auto& kv : data::nuclide_map) {
32,561✔
192
    nuclide_names[kv.second] = kv.first;
27,168✔
193
  }
194
  for (const auto& kv : data::thermal_scatt_map) {
6,505✔
195
    thermal_names[kv.second] = kv.first;
1,112✔
196
  }
197

198
  // Read cross sections
199
  for (const auto& mat : model::materials) {
17,829✔
200
    for (int i_nuc : mat->nuclide_) {
72,200✔
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];
59,764✔
205

206
      // If we've already read this nuclide, skip it
207
      if (already_read.find(name) != already_read.end())
59,764✔
208
        continue;
32,596✔
209

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

215
      already_read.insert(name);
27,168✔
216
    }
217
  }
218

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

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

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

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

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

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

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

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

259
  // Show minimum/maximum temperature
260
  write_message(
5,393✔
261
    4, "Minimum neutron data temperature: {} K", data::temperature_min);
262
  write_message(
5,393✔
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) {
5,393✔
267
    bool mp_found = false;
34✔
268
    for (const auto& nuc : data::nuclides) {
34✔
269
      if (nuc->multipole_) {
34✔
270
        mp_found = true;
34✔
271
        break;
34✔
272
      }
273
    }
274
    if (mpi::master && !mp_found) {
34✔
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
}
5,393✔
281

282
void read_ce_cross_sections_xml()
5,393✔
283
{
284
  // Check if cross_sections.xml exists
285
  const auto& filename = settings::path_cross_sections;
5,393✔
286
  if (dir_exists(filename)) {
5,393✔
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)) {
5,393✔
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);
5,393✔
296

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

305
  std::string directory;
5,393✔
306
  if (check_for_node(root, "directory")) {
5,393✔
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("/");
5,393✔
315
    if (pos == std::string::npos) {
5,393✔
316
      // No '\\' found, so the file must be in the same directory as
317
      // materials.xml
318
      directory = settings::path_input;
12✔
319
    } else {
320
      directory = filename.substr(0, pos);
5,381✔
321
    }
322
  }
323

324
  for (const auto& node_library : root.children("library")) {
5,041,523✔
325
    data::libraries.emplace_back(node_library, directory);
5,036,130✔
326
  }
327

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

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

345
      // Read continuous-energy cross sections from HDF5
346
      read_ce_cross_sections(nuc_temps, thermal_temps);
5,393✔
347
    } else {
5,393✔
348
      // Create material macroscopic data for MGXS
349
      set_mg_interface_nuclides_and_temps();
867✔
350
      data::mg.init();
867✔
351
      mark_fissionable_mgxs_materials();
867✔
352
    }
353
    simulation::time_read_xs.stop();
6,260✔
354
  }
355
}
6,498✔
356

357
void library_clear()
6,597✔
358
{
359
  data::libraries.clear();
6,597✔
360
  data::library_map.clear();
6,597✔
361
}
6,597✔
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