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