diff --git a/Makefile b/Makefile index 7647859a7..4c9f5771c 100644 --- a/Makefile +++ b/Makefile @@ -285,6 +285,9 @@ endif ifeq ($(ENABLE_DAGMC), yes) libmesh_CXXFLAGS += -DENABLE_DAGMC + + # this flag is used in OpenMC + libmesh_CXXFLAGS += -DDAGMC endif # ====================================================================================== diff --git a/doc/content/media/dagmc_model.png b/doc/content/media/dagmc_model.png new file mode 100644 index 000000000..59df4de31 Binary files /dev/null and b/doc/content/media/dagmc_model.png differ diff --git a/doc/content/media/skinning.png b/doc/content/media/skinning.png new file mode 100644 index 000000000..5c012215f Binary files /dev/null and b/doc/content/media/skinning.png differ diff --git a/doc/content/source/problems/OpenMCCellAverageProblem.md b/doc/content/source/problems/OpenMCCellAverageProblem.md index 60fc8dfed..1d21308ea 100644 --- a/doc/content/source/problems/OpenMCCellAverageProblem.md +++ b/doc/content/source/problems/OpenMCCellAverageProblem.md @@ -432,7 +432,76 @@ element in the `[Mesh]`. While this class mainly facilitates data transfers to and from OpenMC, a number of other features are implemented in order to enable more convenient input file -setup and achieve better iterative performance. These are described in this section. +setup, achieve better iterative performance, and iteratively update the geometry +(for CAD tracking only). These are described in this section. + +#### CAD Geometry Skinning + id=skinning + +For all cell-based OpenMC models, OpenMC is currently limited to settings a single +constant temperature, and a single constant density, in each cell. For CSG geometries, +the user needs to manually set up sub-divisions in the geometry in order to capture +spatial variation in temperature and density (computed by some other MOOSE application). +For DAGMC geometries, you can instead optionally re-generate the OpenMC cells after +each Picard iteration according to contours in temperature and/or density - in other +words, automatically setting up the OpenMC model so that it can receive spatially-varying +temperature and density without the user needing to manually subdivide regions of space +*a priori*. + +!alert note +This skinning feature is only available for DAGMC geometries for which the `[Mesh]` +obeys the same material boundaries in the starting `.h5m` file. + +To use this feature, provide the `skinner` user +object parameter, of type [MoabSkinner](/userobjects/MoabSkinner.md). After each OpenMC +run, this object will group elements in the `[Mesh]` into "bins" for temperature, density, +and/or subdomain (mesh block). A new DAGMC cell will then be created by "skinning" the mesh +elements according to the boundaries between the unique bin regions. +For example, suppose the starting DAGMC model consists of two +cells, each with a different material, as shown in [dagmc_model]. + +!media dagmc_model.png + id=dagmc_model + style=width:50%;margin-left:auto;margin-right:auto + caption=Example illustration of a DAGMC model before a skinning operation is applied. + +Suppose this geometry is skinned with 4 temperature bins between 500 K and 700 K. +The input syntax would look something like + +!listing test/tests/neutronics/dagmc/mesh_tallies/openmc.i + start=Problem + end=Postprocessors + +Elements will then be categorized as falling into one of these bins: + +- Bin 0, for $500\ K \leq T< 550\ K$. +- Bin 1, for $550\ K \leq T< 600\ K$. +- Bin 2, for $600\ K \leq T< 650\ K$. +- Bin 3, for $650\ K \leq T\leq 700\ K$. + +The geometry will also be skinned according to the subdomain IDs, which align with the +materials in the original DAGMC geometry. For this problem with two materials, elements +will be categorized according to falling into one of two subdomain bins: + +- Bin 0, for elements in material A +- Bin 1, for elements in material B + +Then, if the following temperature distribution is sent to OpenMC, the DAGMC geometry +will be re-skinned into 8 unique cells, as shown in [skinning]. Depending on the +temperature and material definitions, the problem would be skinned into more or less +cells, depending on which temperatures are observed and whether those element "clumps" +are contiguous or disjoint from one another. +A "graveyard" volume is automatically built around the new DAGMC model between two bounding boxes. This +region is used to apply vacuum boundary conditions by killing any particles that +enter this region. + +!media skinning.png + id=skinning + caption=Example illustration of a DAGMC model after skinning + +Once the geometry has been re-created, the rest of the coupling proceeds as normal - +the average temperature and density of each new DAGMC cell is set as a volume-average +of the elements corresponding to each cell. #### Mesh Scaling id=scaling diff --git a/include/base/OpenMCCellAverageProblem.h b/include/base/OpenMCCellAverageProblem.h index 1e76337af..8d06267de 100644 --- a/include/base/OpenMCCellAverageProblem.h +++ b/include/base/OpenMCCellAverageProblem.h @@ -25,6 +25,11 @@ #include "openmc/tallies/filter_mesh.h" #include "openmc/mesh.h" +#ifdef ENABLE_DAGMC +#include "MoabSkinner.h" +#include "DagMC.hpp" +#endif + /** * Mapping of OpenMC to a collection of MOOSE elements, with temperature feedback * on solid cells and both temperature and density feedback on fluid cells. The @@ -307,10 +312,16 @@ class OpenMCCellAverageProblem : public OpenMCProblemBase */ double cellMappedVolume(const cellInfo & cell_info); + /// Reconstruct the DAGMC geometry after skinning + void reloadDAGMC(); + /// Constant flag to indicate that a cell/element was unmapped static constexpr int32_t UNMAPPED{-1}; protected: + /// Calculate the number of unique OpenMC cells (each with a unique ID & instance) + void calculateNumCells(); + /// Loop over the mapped cells, and build a map between subdomains to OpenMC materials void subdomainsToMaterials(); @@ -935,7 +946,7 @@ class OpenMCCellAverageProblem : public OpenMCProblemBase std::map> _cell_to_elem_subdomain; /// Mapping of elem subdomains to materials - std::map> _subdomain_to_material; + std::map> _subdomain_to_material; /** * A point inside the cell, taken simply as the centroid of the first global @@ -1117,6 +1128,20 @@ class OpenMCCellAverageProblem : public OpenMCProblemBase /// Index in tally_score pointing to the score used for normalizing flux tallies in eigenvalue mode unsigned int _source_rate_index; +#ifdef ENABLE_DAGMC + /// Optional skinner to re-generate the OpenMC geometry on-the-fly for DAGMC models + MoabSkinner * _skinner; + + /// Pointer to DAGMC + std::shared_ptr _dagmc; +#endif + + /// Total number of unique OpenMC cell IDs + instances combinations + long unsigned int _n_openmc_cells; + + /// Index in the OpenMC universes corresponding to the DAGMC universe + int32_t _dagmc_universe_index; + /// Conversion rate from eV to Joule static constexpr Real EV_TO_JOULE = 1.6022e-19; diff --git a/include/base/OpenMCProblemBase.h b/include/base/OpenMCProblemBase.h index 3098ac1f8..fb3542f78 100644 --- a/include/base/OpenMCProblemBase.h +++ b/include/base/OpenMCProblemBase.h @@ -326,6 +326,12 @@ class OpenMCProblemBase : public CardinalProblem, public PostprocessorInterface /// Total number of unique OpenMC cell IDs + instances combinations long unsigned int _n_openmc_cells; + /** + * Whether the OpenMC model consists of a single coordinate level; this can + * in some cases be used for more verbose error messages + */ + const bool _single_coord_level; + /** * Fixed point iteration index used in relaxation; because we sometimes run OpenMC * in a pseudo-transient coupling with NekRS, we simply increment this by 1 each diff --git a/include/userobjects/MoabSkinner.h b/include/userobjects/MoabSkinner.h index 2fc3a9ac4..93bcd20c1 100644 --- a/include/userobjects/MoabSkinner.h +++ b/include/userobjects/MoabSkinner.h @@ -37,6 +37,8 @@ class MoabSkinner : public GeneralUserObject */ virtual moab::ErrorCode check(const moab::ErrorCode input) const; + std::string materialName(const unsigned int & block, const unsigned int & density, const unsigned int & temp) const; + /// Perform the skinning operation virtual void update(); @@ -118,6 +120,31 @@ class MoabSkinner : public GeneralUserObject const unsigned int & density_bin, const unsigned int & subdomain_bin) const; + /** + * Whether the skinner builds a graveyard + * @return whether a graveyard is built + */ + virtual const bool & hasGraveyard() const { return _build_graveyard; } + + /** + * Set the graveyard setting + * @param[in] build whether to build a graveyard + */ + void setGraveyard(bool build) { _build_graveyard = build; } + + /** + * Number of density bins; if greater than 1, this means we must be re-generating + * OpenMC materials during the course of the simulation. + * @return number of density bins + */ + virtual unsigned int nDensityBins() const { return _n_density_bins; } + + /** + * Get pointer to underlying moab interface + * @return pointer to moab interface + */ + const std::shared_ptr & moabPtr() const { return _moab; } + protected: std::unique_ptr> _serialized_solution; @@ -127,12 +154,6 @@ class MoabSkinner : public GeneralUserObject /// Whether to print diagnostic information bool _verbose; - /** - * Whether to build a graveyard as two additional cube surfaces surrounding the mesh. - * This is only needed if the skinned geometry is fed into a Monte Carlo code. - */ - const bool & _build_graveyard; - /// Name of the temperature variable const std::string & _temperature_name; @@ -173,6 +194,12 @@ class MoabSkinner : public GeneralUserObject /// Whether to output the MOAB mesh to a .h5m file const bool & _output_full; + /** + * Whether to build a graveyard as two additional cube surfaces surrounding the mesh. + * This is only needed if the skinned geometry is fed into a Monte Carlo code. + */ + bool _build_graveyard; + /// Length multiplier to get from [Mesh] units into OpenMC's centimeters Real _scaling; @@ -309,14 +336,6 @@ class MoabSkinner : public GeneralUserObject /// NB elems in param is a copy, localElems is a reference void groupLocalElems(std::set elems, std::vector & localElems); - /** - * Get a unique index for an OpenMC material (which we can simply take as the density bin, - * because in OpenMC you can set a unique temperature on different cells filled with the - * same material, but this is not the case for density). - * @return OpenMC material index - */ - unsigned int getMatBin(const unsigned int & iDenBin) const; - /// Clear MOAB entity sets bool resetMOAB(); diff --git a/src/base/OpenMCCellAverageProblem.C b/src/base/OpenMCCellAverageProblem.C index d50a03106..130389186 100644 --- a/src/base/OpenMCCellAverageProblem.C +++ b/src/base/OpenMCCellAverageProblem.C @@ -32,16 +32,21 @@ #include "openmc/capi.h" #include "openmc/cell.h" #include "openmc/constants.h" +#include "openmc/cross_sections.h" +#include "openmc/dagmc.h" #include "openmc/error.h" #include "openmc/particle.h" #include "openmc/geometry.h" #include "openmc/geometry_aux.h" +#include "openmc/material.h" #include "openmc/message_passing.h" +#include "openmc/nuclide.h" #include "openmc/random_lcg.h" #include "openmc/settings.h" #include "openmc/summary.h" #include "openmc/tallies/trigger.h" #include "openmc/volume_calc.h" +#include "openmc/universe.h" #include "xtensor/xarray.hpp" #include "xtensor/xview.hpp" @@ -82,7 +87,7 @@ OpenMCCellAverageProblem::validParams() "export_properties", false, "Whether to export OpenMC's temperature and density properties after updating " - "them in the syncSolutions call."); + "them from MOOSE."); params.addRangeCheckedParam( "scaling", 1.0, @@ -238,6 +243,8 @@ OpenMCCellAverageProblem::validParams() "An optional user object that will perform a stochastic volume calculation to get the OpenMC " "cell volumes. This can be used to check that the MOOSE regions to which the cells map are " "of approximately the same volume."); + params.addParam("skinner", "When using DAGMC geometries, an optional skinner that will " + "regenerate the OpenMC geometry on-the-fly according to iso-contours of temperature and density"); return params; } @@ -273,7 +280,11 @@ OpenMCCellAverageProblem::OpenMCCellAverageProblem(const InputParameters & param _temperature_vars(nullptr), _temperature_blocks(nullptr), _volume_calc(nullptr), - _symmetry(nullptr) + _symmetry(nullptr), +#ifdef ENABLE_DAGMC + _skinner(nullptr), +#endif + _n_openmc_cells(0) { // We need to clear and re-initialize the OpenMC tallies if // fixed_mesh is false, which indicates at least one of the following: @@ -409,6 +420,46 @@ OpenMCCellAverageProblem::OpenMCCellAverageProblem(const InputParameters & param else checkUnusedParam(params, "first_iteration_particles", "not using Dufek-Gudowski relaxation"); + // OpenMC will throw an error if the geometry contains DAG universes but OpenMC wasn't compiled with DAGMC. + // So we can assume that if we have a DAGMC geometry, that we will also by this point have ENABLE_DAGMC. + +#ifdef ENABLE_DAGMC + bool has_csg; + bool has_dag; + geometryType(has_csg, has_dag); + + if (!has_dag) + checkUnusedParam(params, "skinner", "the OpenMC model does not contain any DagMC universes"); + else + { + if (isParamValid("skinner")) + { + // TODO: we currently delete the entire OpenMC geometry, and only re-build the cells + // bounded by the skins. We can generalize this later to only regenerate DAGMC universes, + // so that CSG cells are untouched by the skinner. We'd also need to be careful with the + // relationships between the DAGMC universes and the CSG cells, because the DAGMC universes + // could be filled inside of the CSG cells. + if (has_csg && has_dag) + mooseError("The 'skinner' can only be used with OpenMC geometries that are entirely DAGMC based.\n" + "Your model contains a combination of both CSG and DAG cells."); + + // We know there will be a single DAGMC universe, because we already impose + // above that there cannot be CSG cells (and the only way to get >1 DAGMC + // universe is to fill it inside a CSG cell). + for(const auto& universe: openmc::model::universes) + if (universe->geom_type() == openmc::GeometryType::DAG) + _dagmc_universe_index = openmc::model::universe_map[universe->id_]; + + const openmc::Universe * u = openmc::model::universes[_dagmc_universe_index].get(); + const openmc::DAGUniverse * dag = dynamic_cast(u); + if (dag->uses_uwuw()) // TODO: test + mooseError("The 'skinner' does not currently support the UWUW workflow."); + } + } +#else + checkUnusedParam(params, "skinner", "the OpenMC model does not contain any DagMC universes"); +#endif + _n_particles_1 = nParticles(); // set the parameters needed for tally triggers @@ -558,6 +609,80 @@ OpenMCCellAverageProblem::initialSetup() } setupProblem(); + +#ifdef ENABLE_DAGMC + if (isParamValid("skinner")) + { + auto name = getParam("skinner"); + _skinner = &getUserObject(name); + + if (!_skinner) + paramError("skinner", "The 'skinner' user object must be of type MoabSkinner!"); + + if (_tally_type == tally::cell) + mooseError("'skinner' does not yet support cell tallies. This will be relaxed soon."); + + if (_scaling != 1.0) + mooseError("'skinner' currently requires 'scaling = 1.0' (with a [Mesh] in units of centimeters). This will be relaxed soon."); + + if (_has_fluid_blocks) + mooseError("'skinner' currently can only skin solid-only models, because we are not re-creating OpenMC materials for the newly-created cells. This will be relaxed soon."); + + if (_symmetry) + mooseError("Cannot combine the 'skinner' with 'symmetry_mapper'!\n\nWhen using a skinner, " + "the [Mesh] must exactly match the underlying OpenMC model, so there is\n" + "no need to transform spatial coordinates to map between OpenMC and the [Mesh]."); + + // If the density bins are > 1, we need to re-init the OpenMC materials once (between + // the first time we read the materials.xml and when we run the first skinned OpenMC + // geometry) so that we have unique materials that can be set to individual densities. + // This is just not yet implemented. I think it'd be worthwhile to just allow OpenMC + // to set densities of cells filled by the same material, i.e. have the "density" + // construct attached to the cell, not material. + if (_skinner->nDensityBins() > 1) + paramError("skinner", "Density binning is not currently supported for the OpenMC wrapping!"); + + if (!_skinner->hasGraveyard()) + { + mooseWarning("Overriding graveyard setting on '", Moose::stringify(name), "' user object " + "because a graveyard must be present for OpenMC.\nYou can hide this warning by setting " + "'build_graveyard = true' for the '", Moose::stringify(name), "' user object."); + _skinner->setGraveyard(true); + } + + _skinner->setScaling(_scaling); + _skinner->setVerbosity(_verbose); + _skinner->makeDependentOnExternalAction(); + + // the skinner expects that there is one OpenMC material per subdomain (otherwise this + // indicates that our [Mesh] doesn't match the .h5m model, because DAGMC itself imposes + // the one-material-per-cell case. In the future, if we generate DAGMC models directly + // from the [Mesh] (bypassing the .h5m), we would not need this error check. + std::vector mats; + for (const auto & s : _subdomain_to_material) + { + if (s.second.size() > 1) + { + std::stringstream msg; + msg << "The 'skinner' expects to find one OpenMC material mapped to each [Mesh] subdomain, but " << + Moose::stringify(s.second.size()) << " materials\nmapped to subdomain " << s.first << + ". This indicates your [Mesh] is not " << + "consistent with the .h5m model.\n\nThe materials which mapped to subdomain " << s.first << " are:\n"; + + for (const auto & m : s.second) + msg << "\n" << materialName(m); + + mooseError(msg.str()); + } + + mats.push_back(materialName(*(s.second.begin()))); + } + + _skinner->setMaterialNames(mats); + + _skinner->initialize(); + } +#endif } void @@ -574,6 +699,8 @@ OpenMCCellAverageProblem::setupProblem() _local_to_global_elem.push_back(e); } + calculateNumCells(); + initializeElementToCellMapping(); getMaterialFills(); @@ -1384,6 +1511,10 @@ OpenMCCellAverageProblem::initializeElementToCellMapping() " MOOSE elements, " + "which occupy a volume of (cm3): " + Moose::stringify(_uncoupled_volume * _scaling * _scaling * _scaling)); + if (_n_openmc_cells < _cell_to_elem.size()) + mooseError("Internal error: _cell_to_elem has length ", _cell_to_elem.size(), " which should\n" + "not exceed the number of OpenMC cells, ", _n_openmc_cells); + // If there is a single coordinate level, we can print a helpful message if there are uncoupled // cells in the domain auto n_uncoupled_cells = _n_openmc_cells - _cell_to_elem.size(); @@ -2063,7 +2194,7 @@ OpenMCCellAverageProblem::initializeTallies() int n_translations = _mesh_translations.size(); std::string name = _tally_mesh_from_moose ? "the MOOSE [Mesh]" : _mesh_template_filename; - _console << "\nAdding mesh tally based on " + name + " at " + + _console << "Adding mesh tally based on " + name + " at " + Moose::stringify(n_translations) + " locations" << std::endl; @@ -2635,6 +2766,8 @@ OpenMCCellAverageProblem::syncSolutions(ExternalProblem::Direction direction) { case coupling::hdf5: { + // if we're reading temperature and density from an existing HDF5 file, + // we don't need to send anything in to OpenMC, so we can leave. importProperties(); return; } @@ -2645,6 +2778,8 @@ OpenMCCellAverageProblem::syncSolutions(ExternalProblem::Direction direction) } case coupling::xml: { + // if we're just using whatever temperature and density are already in the XML + // files, we don't need to send anything in to OpenMC, so we can leave. std::string incoming_transfer = _has_fluid_blocks ? "temperature and density" : "temperature"; _console << "Skipping " << incoming_transfer << " transfer into OpenMC" << std::endl; @@ -2655,6 +2790,43 @@ OpenMCCellAverageProblem::syncSolutions(ExternalProblem::Direction direction) } } +#ifdef ENABLE_DAGMC + if (_skinner) + { + // skin the mesh geometry according to contours in temperature, density, and subdomain + _skinner->update(); + + openmc::model::universe_cell_counts.clear(); + openmc::model::universe_level_counts.clear(); + + // Clear nuclides, these will get reset in read_ce_cross_sections + // Horrible circular logic means that clearing nuclides clears nuclide_map, but + // which is needed before nuclides gets reset + std::unordered_map nuclide_map_copy = openmc::data::nuclide_map; + openmc::data::nuclides.clear(); + openmc::data::nuclide_map = nuclide_map_copy; + + // Clear existing cell data + openmc::model::cells.clear(); + openmc::model::cell_map.clear(); + + // Clear existing surface data + openmc::model::surfaces.clear(); + openmc::model::surface_map.clear(); + + // regenerate the DAGMC geometry + reloadDAGMC(); + + // we need to then re-establish the data structures that map from OpenMC cells to the [Mesh] + // (because the cells changed) + setupProblem(); + } +#else + // re-establish the mapping from the OpenMC cells to the [Mesh] (because the mesh changed) + if (!_fixed_mesh) + setupProblem(); +#endif + // Because we require at least one of fluid_blocks and solid_blocks, we are guaranteed // to be setting the temperature of all of the cells in cell_to_elem - only for the density // transfer do we need to filter for the fluid cells @@ -2835,4 +3007,56 @@ OpenMCCellAverageProblem::cellTemperature(const cellInfo & cell_info) return T; } +void +OpenMCCellAverageProblem::reloadDAGMC() +{ +#ifdef ENABLE_DAGMC + _dagmc.reset(new moab::DagMC(_skinner->moabPtr())); + + // Set up geometry in DagMC from already-loaded mesh + _dagmc->load_existing_contents(); + + // Initialize acceleration data structures + _dagmc->init_OBBTree(); + + // Get an iterator to the DAGMC universe unique ptr + auto univ_it = openmc::model::universes.begin() + _dagmc_universe_index; + + // Remove the old universe + openmc::model::universes.erase(univ_it); + + // Create new DAGMC universe + openmc::DAGUniverse* dag_univ_ptr = new openmc::DAGUniverse(_dagmc); + openmc::model::universes.push_back(std::unique_ptr(dag_univ_ptr)); + + _console << "Re-generating OpenMC model with " << openmc::model::cells.size() << " cells... "; + + // Add cells to universes + openmc::populate_universes(); + + // Set the root universe + openmc::model::root_universe = openmc::find_root_universe(); + openmc::check_dagmc_root_univ(); + + // Final geometry setup + openmc::finalize_geometry(); + + // Finalize cross sections + openmc::finalize_cross_sections(); + + // Needed to obtain correct cell instances + openmc::prepare_distribcell(); + + _console << "done" << std::endl; +#endif +} + +void +OpenMCCellAverageProblem::calculateNumCells() +{ + _n_openmc_cells = 0.0; + for (const auto & c : openmc::model::cells) + _n_openmc_cells += c->n_instances_; +} + #endif diff --git a/src/base/OpenMCProblemBase.C b/src/base/OpenMCProblemBase.C index 5c6f4affb..80afb803b 100644 --- a/src/base/OpenMCProblemBase.C +++ b/src/base/OpenMCProblemBase.C @@ -176,10 +176,6 @@ OpenMCProblemBase::OpenMCProblemBase(const InputParameters & params) // The OpenMC wrapping doesn't require material properties itself, but we might // define them on some blocks of the domain for other auxiliary kernel purposes setMaterialCoverageCheck(false); - - _n_openmc_cells = 0.0; - for (const auto & c : openmc::model::cells) - _n_openmc_cells += c->n_instances_; } OpenMCProblemBase::~OpenMCProblemBase() { openmc_finalize(); } diff --git a/src/userobjects/MoabSkinner.C b/src/userobjects/MoabSkinner.C index 01f461d1e..44a6a04f1 100644 --- a/src/userobjects/MoabSkinner.C +++ b/src/userobjects/MoabSkinner.C @@ -78,7 +78,6 @@ MoabSkinner::MoabSkinner(const InputParameters & parameters) : GeneralUserObject(parameters), _serialized_solution(NumericVector::build(_communicator).release()), _verbose(getParam("verbose")), - _build_graveyard(getParam("build_graveyard")), _temperature_name(getParam("temperature")), _temperature_min(getParam("temperature_min")), _temperature_max(getParam("temperature_max")), @@ -95,6 +94,8 @@ MoabSkinner::MoabSkinner(const InputParameters & parameters) _n_write(0), _standalone(true) { + _build_graveyard = getParam("build_graveyard"); + // we can probably support this in the future, it's just not implemented yet if (!mesh().is_serial()) mooseError("MoabSkinner does not yet support distributed meshes!"); @@ -255,6 +256,9 @@ MoabSkinner::execute() void MoabSkinner::update() { + _console << "Skinning geometry into " << _n_temperature_bins << " temperature bins, " << + _n_density_bins << " density bins, and " << _n_block_bins << " block bins... "; + // Clear MOAB mesh data from last timestep reset(); @@ -269,6 +273,8 @@ MoabSkinner::update() // Find the surfaces of local temperature regions findSurfaces(); + + _console << "done" << std::endl; } void @@ -564,12 +570,12 @@ MoabSkinner::sortElemsByResults() std::to_string(_density_bin_bounds[i + 1]), n_density_hits[i]); - _console << "Mapping of Elements to Temperature Bins:" << std::endl; + _console << "\nMapping of Elements to Temperature Bins:" << std::endl; vtt.print(_console); if (_bin_by_density) { - _console << "\nMapping of Elements to Density Bins:" << std::endl; + _console << "\n\nMapping of Elements to Density Bins:" << std::endl; vtd.print(_console); } } @@ -582,7 +588,7 @@ MoabSkinner::getTemperatureBin(const Elem * const elem) const auto value = (*_serialized_solution)(dof); // TODO: add option to truncate instead - if (value < _temperature_min) + if ((_temperature_min - value) > 1e-6) mooseError("Variable '", _temperature_name, "' has value below minimum range of bins. " @@ -592,7 +598,7 @@ MoabSkinner::getTemperatureBin(const Elem * const elem) const "\n temperature_min: ", _temperature_min); - if (value > _temperature_max) + if ((value - _temperature_max) > 1e-6) mooseError("Variable '", _temperature_name, "' has value above maximum range of bins. " @@ -615,7 +621,7 @@ MoabSkinner::getDensityBin(const Elem * const elem) const auto value = (*_serialized_solution)(dof); // TODO: add option to truncate instead - if (value < _density_min) + if ((_density_min - value) > 1e-6) mooseError("Variable '", _density_name, "' has value below minimum range of bins. " @@ -625,7 +631,7 @@ MoabSkinner::getDensityBin(const Elem * const elem) const "\n density_min: ", _density_min); - if (value > _density_max) + if ((value - _density_max) > 1e-6) mooseError("Variable '", _density_name, "' has value above maximum range of bins. " @@ -638,6 +644,15 @@ MoabSkinner::getDensityBin(const Elem * const elem) const return bin_utility::linearBin(value, _density_bin_bounds); } +std::string +MoabSkinner::materialName(const unsigned int & block, const unsigned int & density, const unsigned int & temp) const +{ + if (_n_density_bins > 1) + return "mat:" + _material_names.at(block) + "_" + std::to_string(density); + else + return "mat:" + _material_names.at(block); +} + void MoabSkinner::findSurfaces() { @@ -653,9 +668,6 @@ MoabSkinner::findSurfaces() // Loop over material bins for (unsigned int iMat = 0; iMat < _n_block_bins; iMat++) { - // Get the base material name: - std::string mat_name = "mat:" + _material_names.at(iMat); - // Loop over density bins for (unsigned int iDen = 0; iDen < _n_density_bins; iDen++) { @@ -663,11 +675,13 @@ MoabSkinner::findSurfaces() for (unsigned int iVar = 0; iVar < _n_temperature_bins; iVar++) { // Update material name - auto updated_mat_name = mat_name + "_" + std::to_string(getMatBin(iDen)); + auto updated_mat_name = materialName(iMat, iDen, iVar); // Create a material group int iSortBin = getBin(iVar, iDen, iMat); + // For DagMC to fill a cell with a material, we first create a group + // with that name, and then assign it with createVol (called inside findSurface) moab::EntityHandle group_set; unsigned int group_id = iSortBin + 1; createGroup(group_id, updated_mat_name, group_set); @@ -818,17 +832,11 @@ MoabSkinner::reset() } unsigned int -MoabSkinner::getBin(const unsigned int & iVarBin, - const unsigned int & iDenBin, - const unsigned int & iMat) const -{ - return _n_temperature_bins * (_n_density_bins * iMat + iDenBin) + iVarBin; -} - -unsigned int -MoabSkinner::getMatBin(const unsigned int & iDenBin) const +MoabSkinner::getBin(const unsigned int & i_temp, + const unsigned int & i_density, + const unsigned int & i_block) const { - return iDenBin; + return _n_temperature_bins * (_n_density_bins * i_block + i_density) + i_temp; } void diff --git a/test/tests/neutronics/dagmc/density_bins.i b/test/tests/neutronics/dagmc/density_bins.i new file mode 100644 index 000000000..609d0bb11 --- /dev/null +++ b/test/tests/neutronics/dagmc/density_bins.i @@ -0,0 +1,46 @@ +[Mesh] + [file] + type = FileMeshGenerator + file = ../meshes/tet_cube.e + [] + + allow_renumbering = false + parallel_type = replicated +[] + +[Problem] + type = OpenMCCellAverageProblem + tally_type = mesh + mesh_template = ../meshes/tet_cube.e + solid_cell_level = 0 + solid_blocks = '1' + power = 1000.0 + skinner = moab +[] + +[AuxVariables] + [density] + family = MONOMIAL + order = CONSTANT + [] +[] + +[UserObjects] + [moab] + type = MoabSkinner + temperature = temp + temperature_min = 0.0 + temperature_max = 900.0 + n_temperature_bins = 1 + build_graveyard = true + + density = density + density_min = 0.0 + density_max = 1000.0 + n_density_bins = 4 + [] +[] + +[Executioner] + type = Steady +[] diff --git a/test/tests/neutronics/dagmc/geometry.xml b/test/tests/neutronics/dagmc/geometry.xml new file mode 100644 index 000000000..ffea8a070 --- /dev/null +++ b/test/tests/neutronics/dagmc/geometry.xml @@ -0,0 +1,4 @@ + + + + diff --git a/test/tests/neutronics/dagmc/incompatible_geom/has_csg/geometry.xml b/test/tests/neutronics/dagmc/incompatible_geom/has_csg/geometry.xml new file mode 100644 index 000000000..6760e07e9 --- /dev/null +++ b/test/tests/neutronics/dagmc/incompatible_geom/has_csg/geometry.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/test/tests/neutronics/dagmc/incompatible_geom/has_csg/make_openmc_model.py b/test/tests/neutronics/dagmc/incompatible_geom/has_csg/make_openmc_model.py new file mode 100644 index 000000000..24747f3ea --- /dev/null +++ b/test/tests/neutronics/dagmc/incompatible_geom/has_csg/make_openmc_model.py @@ -0,0 +1,40 @@ +#********************************************************************/ +#* SOFTWARE COPYRIGHT NOTIFICATION */ +#* Cardinal */ +#* */ +#* (c) 2021 UChicago Argonne, LLC */ +#* ALL RIGHTS RESERVED */ +#* */ +#* Prepared by UChicago Argonne, LLC */ +#* Under Contract No. DE-AC02-06CH11357 */ +#* With the U. S. Department of Energy */ +#* */ +#* Prepared by Battelle Energy Alliance, LLC */ +#* Under Contract No. DE-AC07-05ID14517 */ +#* With the U. S. Department of Energy */ +#* */ +#* See LICENSE for full restrictions */ +#********************************************************************/ + +import openmc + +a = openmc.Material() +a.set_density('g/cc', 11.0) +a.add_nuclide('U235', 1.0) + +b = openmc.Material() +b.set_density('g/cc', 11.0) +b.add_nuclide('U235', 0.5) +b.add_nuclide('U238', 0.5) + +mats = openmc.Materials([a, b]) +mats.export_to_xml() + +dagmc_univ = openmc.DAGMCUniverse(filename="../../mesh_tallies/slab.h5m", auto_geom_ids=True) + +sphere = openmc.Sphere(r=10.0, boundary_type='vacuum') +csg = openmc.Cell(region=-sphere) +csg.fill = dagmc_univ + +geometry = openmc.Geometry([csg]) +geometry.export_to_xml() diff --git a/test/tests/neutronics/dagmc/incompatible_geom/has_csg/materials.xml b/test/tests/neutronics/dagmc/incompatible_geom/has_csg/materials.xml new file mode 100644 index 000000000..cf75dbd14 --- /dev/null +++ b/test/tests/neutronics/dagmc/incompatible_geom/has_csg/materials.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/test/tests/neutronics/dagmc/incompatible_geom/has_csg/openmc.i b/test/tests/neutronics/dagmc/incompatible_geom/has_csg/openmc.i new file mode 100644 index 000000000..4ac6a46be --- /dev/null +++ b/test/tests/neutronics/dagmc/incompatible_geom/has_csg/openmc.i @@ -0,0 +1,32 @@ +[Mesh] + type = FileMesh + file = ../../mesh_tallies/slab.e + allow_renumbering = false + parallel_type = replicated +[] + +[Problem] + type = OpenMCCellAverageProblem + initial_properties = xml + power = 100.0 + tally_type = mesh + solid_cell_level = 0 + solid_blocks = '1 2' + mesh_template = ../../mesh_tallies/slab.e + + skinner = moab +[] + +[UserObjects] + [moab] + type = MoabSkinner + temperature = temp + n_temperature_bins = 2 + temperature_min = 0.0 + temperature_max = 1000.0 + [] +[] + +[Executioner] + type = Steady +[] diff --git a/test/tests/neutronics/dagmc/incompatible_geom/has_csg/settings.xml b/test/tests/neutronics/dagmc/incompatible_geom/has_csg/settings.xml new file mode 100644 index 000000000..daec8f6a0 --- /dev/null +++ b/test/tests/neutronics/dagmc/incompatible_geom/has_csg/settings.xml @@ -0,0 +1,16 @@ + + + eigenvalue + 1000 + 100 + 50 + + + -12.5 -12.5 -12.5 87.5 37.5 12.5 + + + 500.0 + interpolation + 294.0 3000.0 + 1000.0 + diff --git a/test/tests/neutronics/dagmc/incompatible_geom/has_csg/tests b/test/tests/neutronics/dagmc/incompatible_geom/has_csg/tests new file mode 100644 index 000000000..5ae6ad00f --- /dev/null +++ b/test/tests/neutronics/dagmc/incompatible_geom/has_csg/tests @@ -0,0 +1,11 @@ +[Tests] + [includes_csg] + type = RunException + input = openmc.i + expect_err = "The 'skinner' can only be used with OpenMC geometries that are entirely DAGMC based.\n" + "Your model contains a combination of both CSG and DAG cells." + requirement = "The system shall error if attempting to skin an OpenMC geometry that is a combination " + "of DAG and CSG cells." + required_objects = 'MoabSkinner' + [] +[] diff --git a/test/tests/neutronics/dagmc/incompatible_geom/mismatch/geometry.xml b/test/tests/neutronics/dagmc/incompatible_geom/mismatch/geometry.xml new file mode 100644 index 000000000..4888e583d --- /dev/null +++ b/test/tests/neutronics/dagmc/incompatible_geom/mismatch/geometry.xml @@ -0,0 +1,4 @@ + + + + diff --git a/test/tests/neutronics/dagmc/incompatible_geom/mismatch/materials.xml b/test/tests/neutronics/dagmc/incompatible_geom/mismatch/materials.xml new file mode 100644 index 000000000..cf75dbd14 --- /dev/null +++ b/test/tests/neutronics/dagmc/incompatible_geom/mismatch/materials.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/test/tests/neutronics/dagmc/incompatible_geom/mismatch/openmc.i b/test/tests/neutronics/dagmc/incompatible_geom/mismatch/openmc.i new file mode 100644 index 000000000..568c4521a --- /dev/null +++ b/test/tests/neutronics/dagmc/incompatible_geom/mismatch/openmc.i @@ -0,0 +1,43 @@ +[Mesh] + [load] + type = FileMeshGenerator + file = ../../mesh_tallies/slab.e + [] + [merge] + type = RenameBlockGenerator + input = load + old_block = '2' + new_block = '1' + [] + + allow_renumbering = false + parallel_type = replicated +[] + +[Problem] + type = OpenMCCellAverageProblem + tally_type = mesh + mesh_template = ../../mesh_tallies/slab.e + + solid_blocks = '1' + solid_cell_level = 0 + power = 100.0 + + initial_properties = xml + skinner = moab +[] + +[UserObjects] + [moab] + type = MoabSkinner + temperature_min = 0.0 + temperature_max = 1000.0 + n_temperature_bins = 4 + temperature = temp + build_graveyard = true + [] +[] + +[Executioner] + type = Steady +[] diff --git a/test/tests/neutronics/dagmc/incompatible_geom/mismatch/settings.xml b/test/tests/neutronics/dagmc/incompatible_geom/mismatch/settings.xml new file mode 100644 index 000000000..daec8f6a0 --- /dev/null +++ b/test/tests/neutronics/dagmc/incompatible_geom/mismatch/settings.xml @@ -0,0 +1,16 @@ + + + eigenvalue + 1000 + 100 + 50 + + + -12.5 -12.5 -12.5 87.5 37.5 12.5 + + + 500.0 + interpolation + 294.0 3000.0 + 1000.0 + diff --git a/test/tests/neutronics/dagmc/incompatible_geom/mismatch/tests b/test/tests/neutronics/dagmc/incompatible_geom/mismatch/tests new file mode 100644 index 000000000..f09e5eed1 --- /dev/null +++ b/test/tests/neutronics/dagmc/incompatible_geom/mismatch/tests @@ -0,0 +1,13 @@ +[Tests] + [mismatch] + type = RunException + input = openmc.i + expect_err = "The 'skinner' expects to find one OpenMC material mapped to each \[Mesh\] subdomain, but 2 materials\n" + "mapped to subdomain 1. This indicates your \[Mesh\] is not consistent with the .h5m model.\n\n" + "The materials which mapped to subdomain 1 are:\n\n1\n2" + requirement = "The system shall error if there is an obvious mismatch between the [Mesh] and " + "DAGMC model for the case where the number of DAGMC materials which map to each " + "[Mesh] subdomain do not match." + required_objects = 'MoabSkinner' + [] +[] diff --git a/test/tests/neutronics/dagmc/materials.xml b/test/tests/neutronics/dagmc/materials.xml new file mode 100644 index 000000000..b231edcd5 --- /dev/null +++ b/test/tests/neutronics/dagmc/materials.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/geometry.xml b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/geometry.xml new file mode 100644 index 000000000..c42734a45 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/geometry.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/make_openmc_model.py b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/make_openmc_model.py new file mode 100644 index 000000000..eaddd8a98 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/make_openmc_model.py @@ -0,0 +1,45 @@ +#********************************************************************/ +#* SOFTWARE COPYRIGHT NOTIFICATION */ +#* Cardinal */ +#* */ +#* (c) 2021 UChicago Argonne, LLC */ +#* ALL RIGHTS RESERVED */ +#* */ +#* Prepared by UChicago Argonne, LLC */ +#* Under Contract No. DE-AC02-06CH11357 */ +#* With the U. S. Department of Energy */ +#* */ +#* Prepared by Battelle Energy Alliance, LLC */ +#* Under Contract No. DE-AC07-05ID14517 */ +#* With the U. S. Department of Energy */ +#* */ +#* See LICENSE for full restrictions */ +#********************************************************************/ + +import openmc + +a = openmc.Material() +a.set_density('g/cc', 11.0) +a.add_nuclide('U235', 1.0) + +b = openmc.Material() +b.set_density('g/cc', 11.0) +b.add_nuclide('U235', 0.5) +b.add_nuclide('U238', 0.5) + +mats = openmc.Materials([a, b]) +mats.export_to_xml() + +xmin = openmc.XPlane(x0=-12.5, boundary_type='vacuum') +xmax = openmc.XPlane(x0=87.5, boundary_type='vacuum') +ymin = openmc.YPlane(y0=-12.5, boundary_type='vacuum') +ymax = openmc.YPlane(y0=37.5, boundary_type='vacuum') +ymid = openmc.YPlane(y0=(-12.5+25.0)) +zmin = openmc.ZPlane(z0=-12.5, boundary_type='vacuum') +zmax = openmc.ZPlane(z0=12.5, boundary_type='vacuum') + +cell1 = openmc.Cell(region=+xmin & -xmax & +ymin & -ymid & +zmin & -zmax, fill=b) +cell2 = openmc.Cell(region=+xmin & -xmax & +ymid & -ymax & +zmin & -zmax, fill=a) + +geom = openmc.Geometry([cell1, cell2]) +geom.export_to_xml() diff --git a/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/materials.xml b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/materials.xml new file mode 120000 index 000000000..c344223b6 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/materials.xml @@ -0,0 +1 @@ +../materials.xml \ No newline at end of file diff --git a/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/openmc.i b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/openmc.i new file mode 100644 index 000000000..937d0c4c1 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/openmc.i @@ -0,0 +1,77 @@ +[Mesh] + type = FileMesh + file = ../slab.e +[] + +[AuxVariables] + [cell_id] + family = MONOMIAL + order = CONSTANT + [] + [cell_instance] + family = MONOMIAL + order = CONSTANT + [] + [cell_temperature] + family = MONOMIAL + order = CONSTANT + [] + [material_id] + family = MONOMIAL + order = CONSTANT + [] +[] + +[AuxKernels] + [cell_id] + type = CellIDAux + variable = cell_id + [] + [cell_instance] + type = CellInstanceAux + variable = cell_instance + [] + [cell_temperature] + type = CellTemperatureAux + variable = cell_temperature + [] + [material_id] + type = CellMaterialIDAux + variable = material_id + [] +[] + +[AuxKernels] + [temp] + type = ConstantAux + variable = temp + value = 500.0 + execute_on = timestep_begin + [] +[] + +[Problem] + type = OpenMCCellAverageProblem + verbose = true + tally_type = mesh + mesh_template = ../slab.e + + solid_blocks = '1 2' + solid_cell_level = 0 + power = 100.0 +[] + +[Postprocessors] + [k] + type = KEigenvalue + [] +[] + +[Executioner] + type = Transient + num_steps = 2 +[] + +[Outputs] + exodus = true +[] diff --git a/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/settings.xml b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/settings.xml new file mode 120000 index 000000000..05fa269cb --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_1/settings.xml @@ -0,0 +1 @@ +../settings.xml \ No newline at end of file diff --git a/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/geometry.xml b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/geometry.xml new file mode 100644 index 000000000..b28cd1716 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/geometry.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/make_openmc_model.py b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/make_openmc_model.py new file mode 100644 index 000000000..3a170fcc7 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/make_openmc_model.py @@ -0,0 +1,70 @@ +#********************************************************************/ +#* SOFTWARE COPYRIGHT NOTIFICATION */ +#* Cardinal */ +#* */ +#* (c) 2021 UChicago Argonne, LLC */ +#* ALL RIGHTS RESERVED */ +#* */ +#* Prepared by UChicago Argonne, LLC */ +#* Under Contract No. DE-AC02-06CH11357 */ +#* With the U. S. Department of Energy */ +#* */ +#* Prepared by Battelle Energy Alliance, LLC */ +#* Under Contract No. DE-AC07-05ID14517 */ +#* With the U. S. Department of Energy */ +#* */ +#* See LICENSE for full restrictions */ +#********************************************************************/ + +import openmc + +a = openmc.Material() +a.set_density('g/cc', 11.0) +a.add_nuclide('U235', 1.0) + +b = openmc.Material() +b.set_density('g/cc', 11.0) +b.add_nuclide('U235', 0.5) +b.add_nuclide('U238', 0.5) + +mats = openmc.Materials([a, b]) +mats.export_to_xml() + +p1 = 12.5 +p2 = 37.5 +p3 = 62.5 + +xmin = openmc.XPlane(x0=-12.5, boundary_type='vacuum') +xmax = openmc.XPlane(x0=87.5, boundary_type='vacuum') +ymin = openmc.YPlane(y0=-12.5, boundary_type='vacuum') +ymax = openmc.YPlane(y0=37.5, boundary_type='vacuum') +ymid = openmc.YPlane(y0=(-12.5+25.0)) +zmin = openmc.ZPlane(z0=-12.5, boundary_type='vacuum') +zmax = openmc.ZPlane(z0=12.5, boundary_type='vacuum') + +x1 = openmc.XPlane(x0=p1) +x2 = openmc.XPlane(x0=p2) +x3 = openmc.XPlane(x0=p3) + +# bottom row of cells +cell1 = openmc.Cell(region=+xmin & -x1 & +ymin & -ymid & +zmin & -zmax, fill=b) +cell2 = openmc.Cell(region=+x1 & -x2 & +ymin & -ymid & +zmin & -zmax, fill=b) +cell3 = openmc.Cell(region=+x2 & -x3 & +ymin & -ymid & +zmin & -zmax, fill=b) +cell4 = openmc.Cell(region=+x3 & -xmax & +ymin & -ymid & +zmin & -zmax, fill=b) +cell1.temperature = 550.0 +cell2.temperature = 600.0 +cell3.temperature = 650.0 +cell4.temperature = 700.0 + +# top row of cells +cell5 = openmc.Cell(region=+xmin & -x1 & +ymid & -ymax & +zmin & -zmax, fill=a) +cell6 = openmc.Cell(region=+x1 & -x2 & +ymid & -ymax & +zmin & -zmax, fill=a) +cell7 = openmc.Cell(region=+x2 & -x3 & +ymid & -ymax & +zmin & -zmax, fill=a) +cell8 = openmc.Cell(region=+x3 & -xmax & +ymid & -ymax & +zmin & -zmax, fill=a) +cell5.temperature = 550.0 +cell6.temperature = 600.0 +cell7.temperature = 650.0 +cell8.temperature = 700.0 + +geom = openmc.Geometry([cell1, cell2, cell3, cell4, cell5, cell6, cell7, cell8]) +geom.export_to_xml() diff --git a/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/materials.xml b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/materials.xml new file mode 120000 index 000000000..c344223b6 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/materials.xml @@ -0,0 +1 @@ +../materials.xml \ No newline at end of file diff --git a/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/openmc.i b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/openmc.i new file mode 100644 index 000000000..02e47ab80 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/openmc.i @@ -0,0 +1,75 @@ +[Mesh] + type = FileMesh + file = ../slab.e +[] + +x0 = 12.5 +x1 = 37.5 +x2 = 62.5 + +[AuxVariables] + [cell_id] + family = MONOMIAL + order = CONSTANT + [] + [cell_temperature] + family = MONOMIAL + order = CONSTANT + [] +[] + +[AuxKernels] + [cell_id] + type = CellIDAux + variable = cell_id + [] + [cell_temperature] + type = CellTemperatureAux + variable = cell_temperature + [] +[] + +T0 = 600.0 +dT = 50.0 + +[Functions] + [temp] + type = ParsedFunction + expression = 'if (x <= ${x0}, ${fparse T0 - dT}, if (x <= ${x1}, ${T0}, if (x <= ${x2}, ${fparse T0 + dT}, ${fparse T0 + 2 * dT})))' + [] +[] + +[AuxKernels] + [temp] + type = FunctionAux + variable = temp + function = temp + execute_on = timestep_begin + [] +[] + +[Problem] + type = OpenMCCellAverageProblem + verbose = true + tally_type = mesh + mesh_template = ../slab.e + + solid_blocks = '1 2' + solid_cell_level = 0 + power = 100.0 +[] + +[Postprocessors] + [k] + type = KEigenvalue + [] +[] + +[Executioner] + type = Transient + num_steps = 2 +[] + +[Outputs] + exodus = true +[] diff --git a/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/settings.xml b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/settings.xml new file mode 120000 index 000000000..05fa269cb --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/csg_step_2/settings.xml @@ -0,0 +1 @@ +../settings.xml \ No newline at end of file diff --git a/test/tests/neutronics/dagmc/mesh_tallies/direct_tally.i b/test/tests/neutronics/dagmc/mesh_tallies/direct_tally.i new file mode 100644 index 000000000..53baebc93 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/direct_tally.i @@ -0,0 +1,89 @@ +[Mesh] + type = FileMesh + file = slab.e + allow_renumbering = false + parallel_type = replicated +[] + +x0 = 12.5 +x1 = 37.5 +x2 = 62.5 + +[AuxVariables] + [cell_id] + family = MONOMIAL + order = CONSTANT + [] + [cell_temperature] + family = MONOMIAL + order = CONSTANT + [] +[] + +[AuxKernels] + [cell_id] + type = CellIDAux + variable = cell_id + [] + [cell_temperature] + type = CellTemperatureAux + variable = cell_temperature + [] +[] + +T0 = 600.0 +dT = 50.0 + +[Functions] + [temp] + type = ParsedFunction + expression = 'if (x <= ${x0}, ${fparse T0 - dT}, if (x <= ${x1}, ${T0}, if (x <= ${x2}, ${fparse T0 + dT}, ${fparse T0 + 2 * dT})))' + [] +[] + +[AuxKernels] + [temp] + type = FunctionAux + variable = temp + function = temp + execute_on = timestep_begin + [] +[] + +[Problem] + type = OpenMCCellAverageProblem + verbose = true + tally_type = mesh + + solid_blocks = '1 2' + solid_cell_level = 0 + power = 100.0 + + skinner = moab +[] + +[UserObjects] + [moab] + type = MoabSkinner + temperature_min = ${fparse T0 - dT} + temperature_max = ${fparse T0 + 2 * dT} + n_temperature_bins = 4 + temperature = temp + build_graveyard = true + [] +[] + +[Postprocessors] + [k] + type = KEigenvalue + [] +[] + +[Executioner] + type = Transient + num_steps = 2 +[] + +[Outputs] + exodus = true +[] diff --git a/test/tests/neutronics/dagmc/mesh_tallies/disjoint_bins.i b/test/tests/neutronics/dagmc/mesh_tallies/disjoint_bins.i new file mode 100644 index 000000000..2f81af258 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/disjoint_bins.i @@ -0,0 +1,69 @@ +[Mesh] + type = FileMesh + file = slab.e + allow_renumbering = false + parallel_type = replicated +[] + +x0 = 12.5 +x1 = 37.5 +x2 = 62.5 + +T0 = 600.0 +dT = 50.0 + +[Functions] + [temp] + type = ParsedFunction + expression = 'if (x <= ${x0}, ${fparse T0 - dT}, if (x <= ${x1}, ${T0}, if (x <= ${x2}, ${fparse T0 - dT}, ${fparse T0 + 2 * dT})))' + [] +[] + +[AuxKernels] + [temp] + type = FunctionAux + variable = temp + function = temp + execute_on = timestep_begin + [] +[] + +[Problem] + type = OpenMCCellAverageProblem + verbose = true + tally_type = mesh + mesh_template = slab.e + + solid_blocks = '1 2' + solid_cell_level = 0 + power = 100.0 + + skinner = moab +[] + +[UserObjects] + [moab] + type = MoabSkinner + temperature_min = ${fparse T0 - dT} + temperature_max = ${fparse T0 + 2 * dT} + n_temperature_bins = 4 + temperature = temp + build_graveyard = true + output_skins = true + [] +[] + +[Postprocessors] + [k] + type = KEigenvalue + [] +[] + +[Executioner] + type = Transient + num_steps = 2 +[] + +[Outputs] + exodus = true +[] diff --git a/test/tests/neutronics/dagmc/mesh_tallies/geometry.xml b/test/tests/neutronics/dagmc/mesh_tallies/geometry.xml new file mode 100644 index 000000000..f34d68a48 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/geometry.xml @@ -0,0 +1,4 @@ + + + + diff --git a/test/tests/neutronics/dagmc/mesh_tallies/gold/disjoint_bins_out.e b/test/tests/neutronics/dagmc/mesh_tallies/gold/disjoint_bins_out.e new file mode 100644 index 000000000..6452254ec Binary files /dev/null and b/test/tests/neutronics/dagmc/mesh_tallies/gold/disjoint_bins_out.e differ diff --git a/test/tests/neutronics/dagmc/mesh_tallies/gold/one_bin_out.e b/test/tests/neutronics/dagmc/mesh_tallies/gold/one_bin_out.e new file mode 100644 index 000000000..e6d034a6f Binary files /dev/null and b/test/tests/neutronics/dagmc/mesh_tallies/gold/one_bin_out.e differ diff --git a/test/tests/neutronics/dagmc/mesh_tallies/gold/openmc_out.e b/test/tests/neutronics/dagmc/mesh_tallies/gold/openmc_out.e new file mode 100644 index 000000000..6fe6d70c9 Binary files /dev/null and b/test/tests/neutronics/dagmc/mesh_tallies/gold/openmc_out.e differ diff --git a/test/tests/neutronics/dagmc/mesh_tallies/materials.xml b/test/tests/neutronics/dagmc/mesh_tallies/materials.xml new file mode 100644 index 000000000..cf75dbd14 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/materials.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/test/tests/neutronics/dagmc/mesh_tallies/one_bin.i b/test/tests/neutronics/dagmc/mesh_tallies/one_bin.i new file mode 100644 index 000000000..1a341dadb --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/one_bin.i @@ -0,0 +1,75 @@ +[Mesh] + type = FileMesh + file = slab.e + allow_renumbering = false +[] + +[AuxVariables] + [cell_id] + family = MONOMIAL + order = CONSTANT + [] + [cell_temperature] + family = MONOMIAL + order = CONSTANT + [] +[] + +[AuxKernels] + [cell_id] + type = CellIDAux + variable = cell_id + [] + [cell_temperature] + type = CellTemperatureAux + variable = cell_temperature + [] +[] + +[AuxKernels] + [temp] + type = ConstantAux + variable = temp + value = 500.0 + execute_on = timestep_begin + [] +[] + +[Problem] + type = OpenMCCellAverageProblem + tally_type = mesh + mesh_template = slab.e + + solid_blocks = '1 2' + solid_cell_level = 0 + power = 100.0 + + skinner = moab +[] + +[UserObjects] + [moab] + type = MoabSkinner + temperature_min = 0.0 + temperature_max = 1000.0 + n_temperature_bins = 1 + temperature = temp + build_graveyard = true + [] +[] + +[Postprocessors] + [k] + type = KEigenvalue + [] +[] + +[Executioner] + type = Transient + num_steps = 2 +[] + +[Outputs] + exodus = true + hide = 'temp' +[] diff --git a/test/tests/neutronics/dagmc/mesh_tallies/openmc.i b/test/tests/neutronics/dagmc/mesh_tallies/openmc.i new file mode 100644 index 000000000..8e5ee04ff --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/openmc.i @@ -0,0 +1,89 @@ +[Mesh] + type = FileMesh + file = slab.e + allow_renumbering = false +[] + +x0 = 12.5 +x1 = 37.5 +x2 = 62.5 + +[AuxVariables] + [cell_id] + family = MONOMIAL + order = CONSTANT + [] + [cell_temperature] + family = MONOMIAL + order = CONSTANT + [] +[] + +[AuxKernels] + [cell_id] + type = CellIDAux + variable = cell_id + [] + [cell_temperature] + type = CellTemperatureAux + variable = cell_temperature + [] +[] + +T0 = 600.0 +dT = 50.0 + +[Functions] + [temp] + type = ParsedFunction + expression = 'if (x <= ${x0}, ${fparse T0 - dT}, if (x <= ${x1}, ${T0}, if (x <= ${x2}, ${fparse T0 + dT}, ${fparse T0 + 2 * dT})))' + [] +[] + +[AuxKernels] + [temp] + type = FunctionAux + variable = temp + function = temp + execute_on = timestep_begin + [] +[] + +[Problem] + type = OpenMCCellAverageProblem + verbose = true + tally_type = mesh + mesh_template = slab.e + + solid_blocks = '1 2' + solid_cell_level = 0 + power = 100.0 + + skinner = moab +[] + +[UserObjects] + [moab] + type = MoabSkinner + temperature_min = ${fparse T0 - dT} + temperature_max = ${fparse T0 + 2 * dT} + n_temperature_bins = 4 + temperature = temp + build_graveyard = true + [] +[] + +[Postprocessors] + [k] + type = KEigenvalue + [] +[] + +[Executioner] + type = Transient + num_steps = 2 +[] + +[Outputs] + exodus = true +[] diff --git a/test/tests/neutronics/dagmc/mesh_tallies/settings.xml b/test/tests/neutronics/dagmc/mesh_tallies/settings.xml new file mode 100644 index 000000000..daec8f6a0 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/settings.xml @@ -0,0 +1,16 @@ + + + eigenvalue + 1000 + 100 + 50 + + + -12.5 -12.5 -12.5 87.5 37.5 12.5 + + + 500.0 + interpolation + 294.0 3000.0 + 1000.0 + diff --git a/test/tests/neutronics/dagmc/mesh_tallies/slab.e b/test/tests/neutronics/dagmc/mesh_tallies/slab.e new file mode 100644 index 000000000..b7c279117 Binary files /dev/null and b/test/tests/neutronics/dagmc/mesh_tallies/slab.e differ diff --git a/test/tests/neutronics/dagmc/mesh_tallies/slab.h5m b/test/tests/neutronics/dagmc/mesh_tallies/slab.h5m new file mode 100644 index 000000000..168809cfe Binary files /dev/null and b/test/tests/neutronics/dagmc/mesh_tallies/slab.h5m differ diff --git a/test/tests/neutronics/dagmc/mesh_tallies/tests b/test/tests/neutronics/dagmc/mesh_tallies/tests new file mode 100644 index 000000000..704c76382 --- /dev/null +++ b/test/tests/neutronics/dagmc/mesh_tallies/tests @@ -0,0 +1,44 @@ +[Tests] + [null_skin] + type = Exodiff + input = one_bin.i + exodiff = one_bin_out.e + requirement = "The system shall give identical Monte Carlo solution (file mesh tallies and k) when skinning " + "as compared to both (i) a CSG-equivalent version of the geometry and (ii) the same " + "input file run with the skinner disabled. The CSG file used for comparison is in " + "the csg_step_1 directory." + mesh_mode = 'replicated' + required_objects = 'MoabSkinner' + [] + [skin] + type = Exodiff + input = openmc.i + exodiff = openmc_out.e + requirement = "The system shall give identical Monte Carlo solution (file mesh tallies and k) when skinning " + "as compared to a CSG-equivalent version of the geometry, which is in the csg_step_2 " + "directory." + mesh_mode = 'replicated' + required_objects = 'MoabSkinner' + [] + [skin_direct] + type = Exodiff + input = direct_tally.i + cli_args = 'Outputs/file_base=openmc_out' + exodiff = openmc_out.e + requirement = "The system shall give identical Monte Carlo solution (direct mesh tallies and k) when skinning " + "as compared to a CSG-equivalent version of the geometry, which is in the csg_step_2 " + "directory." + mesh_mode = 'replicated' + required_objects = 'MoabSkinner' + [] + [disjoint_bins] + type = Exodiff + input = disjoint_bins.i + exodiff = disjoint_bins_out.e + requirement = "The system shall give identical Monte Carlo solution (file mesh tallies and k) when skinning " + "as compared to a CSG-equivalent version of the geometry, which is in the csg_step_2 " + "directory. For this case, a bin is split across disjoint elements." + mesh_mode = 'replicated' + required_objects = 'MoabSkinner' + [] +[] diff --git a/test/tests/neutronics/dagmc/missing_graveyard.i b/test/tests/neutronics/dagmc/missing_graveyard.i new file mode 100644 index 000000000..6c08a2dd7 --- /dev/null +++ b/test/tests/neutronics/dagmc/missing_graveyard.i @@ -0,0 +1,33 @@ +[Mesh] + [file] + type = FileMeshGenerator + file = ../meshes/tet_cube.e + [] + + allow_renumbering = false + parallel_type = replicated +[] + +[Problem] + type = OpenMCCellAverageProblem + tally_type = mesh + check_zero_tallies = false + solid_cell_level = 0 + solid_blocks = '1' + power = 1000.0 + skinner = moab +[] + +[UserObjects] + [moab] + type = MoabSkinner + temperature = temp + temperature_min = 0.0 + temperature_max = 900.0 + n_temperature_bins = 1 + [] +[] + +[Executioner] + type = Steady +[] diff --git a/test/tests/neutronics/dagmc/no_symmetry.i b/test/tests/neutronics/dagmc/no_symmetry.i new file mode 100644 index 000000000..ae2b02788 --- /dev/null +++ b/test/tests/neutronics/dagmc/no_symmetry.i @@ -0,0 +1,39 @@ +[Mesh] + [file] + type = FileMeshGenerator + file = ../meshes/tet_cube.e + [] + + allow_renumbering = false + parallel_type = replicated +[] + +[Problem] + type = OpenMCCellAverageProblem + tally_type = mesh + mesh_template = ../meshes/tet_cube.e + solid_cell_level = 0 + solid_blocks = '1' + power = 1000.0 + skinner = moab + symmetry_mapper = sym +[] + +[UserObjects] + [moab] + type = MoabSkinner + temperature = temp + temperature_min = 0.0 + temperature_max = 900.0 + n_temperature_bins = 1 + build_graveyard = true + [] + [sym] + type = SymmetryPointGenerator + normal = '1.0 0.0 0.0' + [] +[] + +[Executioner] + type = Steady +[] diff --git a/test/tests/neutronics/dagmc/settings.xml b/test/tests/neutronics/dagmc/settings.xml new file mode 100644 index 000000000..a32f2af0d --- /dev/null +++ b/test/tests/neutronics/dagmc/settings.xml @@ -0,0 +1,16 @@ + + + eigenvalue + 5000 + 10 + 2 + + + -0.5 -0.5 -0.5 0.5 0.5 0.5 + + + 500.0 + interpolation + 294.0 3000.0 + 1000.0 + diff --git a/test/tests/neutronics/dagmc/tests b/test/tests/neutronics/dagmc/tests new file mode 100644 index 000000000..6f89a751d --- /dev/null +++ b/test/tests/neutronics/dagmc/tests @@ -0,0 +1,32 @@ +[Tests] + [wrong_uo] + type = RunException + input = wrong_uo.i + expect_err = "The 'skinner' user object must be of type MoabSkinner!" + requirement = "The system shall error if the skinner user object is not the correct type" + required_objects = 'MoabSkinner' + [] + [missing_graveyard] + type = RunException + input = missing_graveyard.i + cli_args = '--error' + expect_err = "Overriding graveyard setting on 'moab' user object because a graveyard must be present for OpenMC." + requirement = "The system shall warn if the graveyard is missing for OpenMC skinned models" + required_objects = 'MoabSkinner' + [] + [no_symmetry] + type = RunException + input = no_symmetry.i + expect_err = "Cannot combine the 'skinner' with 'symmetry_mapper'!" + requirement = "The system shall error if applying a symmetry mapping to an OpenMC model which must " + "already exactly match the mesh." + required_objects = 'MoabSkinner' + [] + [density_bins] + type = RunException + input = density_bins.i + expect_err = "Density binning is not currently supported for the OpenMC wrapping!" + requirement = "The system shall error if using density bins, which are not yet supported." + required_objects = 'MoabSkinner' + [] +[] diff --git a/test/tests/neutronics/dagmc/tet_cube.h5m b/test/tests/neutronics/dagmc/tet_cube.h5m new file mode 100644 index 000000000..19d75c464 Binary files /dev/null and b/test/tests/neutronics/dagmc/tet_cube.h5m differ diff --git a/test/tests/neutronics/dagmc/wrong_uo.i b/test/tests/neutronics/dagmc/wrong_uo.i new file mode 100644 index 000000000..bdfac6809 --- /dev/null +++ b/test/tests/neutronics/dagmc/wrong_uo.i @@ -0,0 +1,28 @@ +[Mesh] + [file] + type = FileMeshGenerator + file = ../meshes/tet_cube.e + [] + parallel_type = replicated +[] + +[Problem] + type = OpenMCCellAverageProblem + tally_type = cell + solid_cell_level = 0 + tally_blocks = '1' + solid_blocks = '1' + power = 1000.0 + skinner = moab +[] + +[UserObjects] + [moab] + type = NearestNodeNumberUO + point = '0.0 0.0 0.0' + [] +[] + +[Executioner] + type = Steady +[] diff --git a/test/tests/openmc_errors/input_params/no_dag.i b/test/tests/openmc_errors/input_params/no_dag.i new file mode 100644 index 000000000..4d9741c67 --- /dev/null +++ b/test/tests/openmc_errors/input_params/no_dag.i @@ -0,0 +1,39 @@ +[Mesh] + [sphere] + type = FileMeshGenerator + file = ../../neutronics/meshes/sphere.e + [] + [solid_ids] + type = SubdomainIDGenerator + input = sphere + subdomain_id = '100' + [] +[] + +[Problem] + type = OpenMCCellAverageProblem + power = 100.0 + solid_blocks = '100' + solid_cell_level = 0 + tally_type = cell + normalize_by_global_tally = false + initial_properties = xml + skinner = moab +[] + +[UserObjects] + [moab] + type = MoabSkinner + temperature = "temp" + temperature_max = 1000.0 + n_temperature_bins = 5 + [] +[] + +[Executioner] + type = Transient +[] + +[Outputs] + csv = true +[] diff --git a/test/tests/openmc_errors/input_params/tests b/test/tests/openmc_errors/input_params/tests index 8d20597cf..91e0e88be 100644 --- a/test/tests/openmc_errors/input_params/tests +++ b/test/tests/openmc_errors/input_params/tests @@ -22,4 +22,12 @@ requirement = "The system shall error if a properties file is loaded but does not exist" required_objects = 'OpenMCCellAverageProblem' [] + [no_dag] + type = RunException + input = no_dag.i + cli_args = '--error' + expect_err = "When the OpenMC model does not contain any DagMC universes, the 'skinner' parameter is unused!" + requirement = "The system shall warn the user if using a skinner without a DagMC geometry" + required_objects = 'MoabSkinner' + [] []