1 #include "openmc/dagmc.h"
2
3 #include "openmc/cell.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.h"
9 #include "openmc/geometry_aux.h"
10 #include "openmc/material.h"
11 #include "openmc/string_utils.h"
12 #include "openmc/settings.h"
13 #include "openmc/surface.h"
14
15 #ifdef DAGMC
16 #include "uwuw.hpp"
17 #include "dagmcmetadata.hpp"
18 #endif
19 #include <fmt/core.h>
20
21 #include <string>
22 #include <sstream>
23 #include <algorithm>
24 #include <fstream>
25
26 namespace openmc {
27
28 #ifdef DAGMC
29 const bool DAGMC_ENABLED = true;
30 #else
31 const bool DAGMC_ENABLED = false;
32 #endif
33
34 }
35
36 #ifdef DAGMC
37
38 namespace openmc {
39
40 const std::string DAGMC_FILENAME = "dagmc.h5m";
41
42 namespace model {
43
44 moab::DagMC* DAG;
45
46 } // namespace model
47
48
dagmc_file()49 std::string dagmc_file() {
50 std::string filename = settings::path_input + DAGMC_FILENAME;
51 if (!file_exists(filename)) {
52 fatal_error("Geometry DAGMC file '" + filename + "' does not exist!");
53 }
54 return filename;
55 }
56
get_uwuw_materials_xml(std::string & s)57 bool get_uwuw_materials_xml(std::string& s) {
58 std::string filename = dagmc_file();
59 UWUW uwuw(filename.c_str());
60
61 std::stringstream ss;
62 bool uwuw_mats_present = false;
63 if (uwuw.material_library.size() != 0) {
64 uwuw_mats_present = true;
65 // write header
66 ss << "<?xml version=\"1.0\"?>\n";
67 ss << "<materials>\n";
68 const auto& mat_lib = uwuw.material_library;
69 // write materials
70 for (auto mat : mat_lib) { ss << mat.second->openmc("atom"); }
71 // write footer
72 ss << "</materials>";
73 s = ss.str();
74 }
75
76 return uwuw_mats_present;
77 }
78
read_uwuw_materials(pugi::xml_document & doc)79 bool read_uwuw_materials(pugi::xml_document& doc) {
80 std::string s;
81 bool found_uwuw_mats = get_uwuw_materials_xml(s);
82 if (found_uwuw_mats) {
83 pugi::xml_parse_result result = doc.load_string(s.c_str());
84 if (!result) {
85 throw std::runtime_error{"Error reading UWUW materials"};
86 }
87 }
88 return found_uwuw_mats;
89 }
90
write_uwuw_materials_xml()91 bool write_uwuw_materials_xml() {
92 std::string s;
93 bool found_uwuw_mats = get_uwuw_materials_xml(s);
94 // if there is a material library in the file
95 if (found_uwuw_mats) {
96 // write a material.xml file
97 std::ofstream mats_xml("materials.xml");
98 mats_xml << s;
99 mats_xml.close();
100 }
101
102 return found_uwuw_mats;
103 }
104
legacy_assign_material(std::string mat_string,DAGCell * c)105 void legacy_assign_material(std::string mat_string, DAGCell* c)
106 {
107 bool mat_found_by_name = false;
108 // attempt to find a material with a matching name
109 to_lower(mat_string);
110 for (const auto& m : model::materials) {
111 std::string m_name = m->name();
112 to_lower(m_name);
113 if (mat_string == m_name) {
114 // assign the material with that name
115 if (!mat_found_by_name) {
116 mat_found_by_name = true;
117 c->material_.push_back(m->id_);
118 // report error if more than one material is found
119 } else {
120 fatal_error(fmt::format(
121 "More than one material found with name {}. Please ensure materials "
122 "have unique names if using this property to assign materials.",
123 mat_string));
124 }
125 }
126 }
127
128 // if no material was set using a name, assign by id
129 if (!mat_found_by_name) {
130 try {
131 auto id = std::stoi(mat_string);
132 c->material_.emplace_back(id);
133 } catch (const std::invalid_argument&) {
134 fatal_error(fmt::format(
135 "No material {} found for volume (cell) {}", mat_string, c->id_));
136 }
137 }
138
139 if (settings::verbosity >= 10) {
140 const auto& m = model::materials[model::material_map.at(c->material_[0])];
141 std::stringstream msg;
142 msg << "DAGMC material " << mat_string << " was assigned";
143 if (mat_found_by_name) {
144 msg << " using material name: " << m->name_;
145 } else {
146 msg << " using material id: " << m->id_;
147 }
148 write_message(msg.str(), 10);
149 }
150 }
151
load_dagmc_geometry()152 void load_dagmc_geometry()
153 {
154 if (!model::DAG) {
155 model::DAG = new moab::DagMC();
156 }
157
158 // --- Materials ---
159
160 // create uwuw instance
161 auto filename = dagmc_file();
162 UWUW uwuw(filename.c_str());
163
164 // check for uwuw material definitions
165 bool using_uwuw = !uwuw.material_library.empty();
166
167 // notify user if UWUW materials are going to be used
168 if (using_uwuw) {
169 write_message("Found UWUW Materials in the DAGMC geometry file.", 6);
170 }
171
172 int32_t dagmc_univ_id = 0; // universe is always 0 for DAGMC runs
173
174 // load the DAGMC geometry
175 moab::ErrorCode rval = model::DAG->load_file(filename.c_str());
176 MB_CHK_ERR_CONT(rval);
177
178 // initialize acceleration data structures
179 rval = model::DAG->init_OBBTree();
180 MB_CHK_ERR_CONT(rval);
181
182 // parse model metadata
183 dagmcMetaData DMD(model::DAG, false, false);
184 DMD.load_property_data();
185
186 vector<std::string> keywords {"temp"};
187 std::map<std::string, std::string> dum;
188 std::string delimiters = ":/";
189 rval = model::DAG->parse_properties(keywords, dum, delimiters.c_str());
190 MB_CHK_ERR_CONT(rval);
191
192 // --- Cells (Volumes) ---
193
194 // initialize cell objects
195 int n_cells = model::DAG->num_entities(3);
196 moab::EntityHandle graveyard = 0;
197 for (int i = 0; i < n_cells; i++) {
198 moab::EntityHandle vol_handle = model::DAG->entity_by_index(3, i+1);
199
200 // set cell ids using global IDs
201 DAGCell* c = new DAGCell();
202 c->dag_index_ = i+1;
203 c->id_ = model::DAG->id_by_index(3, c->dag_index_);
204 c->dagmc_ptr_ = model::DAG;
205 c->universe_ = dagmc_univ_id; // set to zero for now
206 c->fill_ = C_NONE; // no fill, single universe
207
208 model::cells.emplace_back(c);
209 model::cell_map[c->id_] = i;
210
211 // Populate the Universe vector and dict
212 auto it = model::universe_map.find(dagmc_univ_id);
213 if (it == model::universe_map.end()) {
214 model::universes.push_back(make_unique<Universe>());
215 model::universes.back()->id_ = dagmc_univ_id;
216 model::universes.back()->cells_.push_back(i);
217 model::universe_map[dagmc_univ_id] = model::universes.size() - 1;
218 } else {
219 model::universes[it->second]->cells_.push_back(i);
220 }
221
222 // MATERIALS
223
224 // determine volume material assignment
225 std::string mat_str = DMD.get_volume_property("material", vol_handle);
226
227 if (mat_str.empty()) {
228 fatal_error(fmt::format("Volume {} has no material assignment.", c->id_));
229 }
230
231 to_lower(mat_str);
232
233 if (mat_str == "graveyard") {
234 graveyard = vol_handle;
235 }
236
237 // material void checks
238 if (mat_str == "void" || mat_str == "vacuum" || mat_str == "graveyard") {
239 c->material_.push_back(MATERIAL_VOID);
240 } else {
241 if (using_uwuw) {
242 // lookup material in uwuw if present
243 std::string uwuw_mat = DMD.volume_material_property_data_eh[vol_handle];
244 if (uwuw.material_library.count(uwuw_mat) != 0) {
245 // Note: material numbers are set by UWUW
246 int mat_number = uwuw.material_library.get_material(uwuw_mat).metadata["mat_number"].asInt();
247 c->material_.push_back(mat_number);
248 } else {
249 fatal_error(fmt::format("Material with value {} not found in the "
250 "UWUW material library", mat_str));
251 }
252 } else {
253 legacy_assign_material(mat_str, c);
254 }
255 }
256
257 // check for temperature assignment
258 std::string temp_value;
259
260 // no temperature if void
261 if (c->material_[0] == MATERIAL_VOID) continue;
262
263 // assign cell temperature
264 const auto& mat = model::materials[model::material_map.at(c->material_[0])];
265 if (model::DAG->has_prop(vol_handle, "temp")) {
266 rval = model::DAG->prop_value(vol_handle, "temp", temp_value);
267 MB_CHK_ERR_CONT(rval);
268 double temp = std::stod(temp_value);
269 c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * temp));
270 } else {
271 c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * mat->temperature()));
272 }
273
274 }
275
276 // allocate the cell overlap count if necessary
277 if (settings::check_overlaps) {
278 model::overlap_check_count.resize(model::cells.size(), 0);
279 }
280
281 if (!graveyard) {
282 warning("No graveyard volume found in the DagMC model."
283 "This may result in lost particles and rapid simulation failure.");
284 }
285
286 // --- Surfaces ---
287
288 // initialize surface objects
289 int n_surfaces = model::DAG->num_entities(2);
290
291 for (int i = 0; i < n_surfaces; i++) {
292 moab::EntityHandle surf_handle = model::DAG->entity_by_index(2, i+1);
293
294 // set cell ids using global IDs
295 DAGSurface* s = new DAGSurface();
296 s->dag_index_ = i+1;
297 s->id_ = model::DAG->id_by_index(2, s->dag_index_);
298 s->dagmc_ptr_ = model::DAG;
299
300 if (contains(settings::source_write_surf_id, s->id_)) {
301 s->surf_source_ = true;
302 }
303
304 // set BCs
305 std::string bc_value = DMD.get_surface_property("boundary", surf_handle);
306 to_lower(bc_value);
307 if (bc_value.empty() || bc_value == "transmit" || bc_value == "transmission") {
308 // Leave the bc_ a nullptr
309 } else if (bc_value == "vacuum") {
310 s->bc_ = std::make_shared<VacuumBC>();
311 } else if (bc_value == "reflective" || bc_value == "reflect" || bc_value == "reflecting") {
312 s->bc_ = std::make_shared<ReflectiveBC>();
313 } else if (bc_value == "white") {
314 fatal_error("White boundary condition not supported in DAGMC.");
315 } else if (bc_value == "periodic") {
316 fatal_error("Periodic boundary condition not supported in DAGMC.");
317 } else {
318 fatal_error(fmt::format("Unknown boundary condition \"{}\" specified "
319 "on surface {}", bc_value, s->id_));
320 }
321
322 // graveyard check
323 moab::Range parent_vols;
324 rval = model::DAG->moab_instance()->get_parent_meshsets(surf_handle, parent_vols);
325 MB_CHK_ERR_CONT(rval);
326
327 // if this surface belongs to the graveyard
328 if (graveyard && parent_vols.find(graveyard) != parent_vols.end()) {
329 // set graveyard surface BC's to vacuum
330 s->bc_ = std::make_shared<VacuumBC>();
331 }
332
333 // add to global array and map
334 model::surfaces.emplace_back(s);
335 model::surface_map[s->id_] = i;
336 }
337
338 return;
339 }
340
read_geometry_dagmc()341 void read_geometry_dagmc()
342 {
343 write_message("Reading DAGMC geometry...", 5);
344 load_dagmc_geometry();
345
346 model::root_universe = find_root_universe();
347 }
348
free_memory_dagmc()349 void free_memory_dagmc()
350 {
351 delete model::DAG;
352 }
353
354
355 }
356 #endif
357