Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Adaptive Mesh Refinement #964

Open
wants to merge 18 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions include/base/OpenMCCellAverageProblem.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,12 @@ class OpenMCCellAverageProblem : public OpenMCProblemBase

int fixedPointIteration() const { return _fixed_point_iteration; }

/**
* Checks if the problem uses adaptivity or not.
* @return if the problem uses adaptivity.
*/
bool hasAdaptivity() const { return _has_adaptivity; }

/// Constant flag to indicate that a cell/element was unmapped
static constexpr int32_t UNMAPPED{-1};

Expand Down Expand Up @@ -702,7 +708,7 @@ class OpenMCCellAverageProblem : public OpenMCProblemBase
void compareContainedCells(std::map<cellInfo, containedCells> & reference,
std::map<cellInfo, containedCells> & compare) const;

std::unique_ptr<NumericVector<Number>> _serialized_solution;
NumericVector<Number> & _serialized_solution;

/**
* Whether to automatically compute the mapping of OpenMC cell IDs and
Expand Down Expand Up @@ -764,12 +770,16 @@ class OpenMCCellAverageProblem : public OpenMCProblemBase
const bool _normalize_by_global;

/**
* If 'fixed_mesh' is false, this indicates that the [Mesh] is changing during
* the simulation (either from adaptive refinement or from deformation).
* When the mesh changes during the simulation, the mapping from OpenMC cells to
* the [Mesh] must be re-established after each OpenMC run.
* Whether or not the problem contains mesh adaptivity.
*/
bool _has_adaptivity;

/**
* When the mesh changes during the simulation (either from adaptive mesh refinement
* or deformation), the mapping from OpenMC cells to the [Mesh] must be re-established
* after each OpenMC run.
*/
const bool _need_to_reinit_coupling;
bool _need_to_reinit_coupling;

/**
* Whether to check the tallies against the global tally;
Expand Down
22 changes: 20 additions & 2 deletions include/tallies/MeshTally.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@

#include "openmc/tallies/filter_mesh.h"

namespace libMesh
{
class ReplicatedMesh;
}

class MeshTally : public TallyBase
{
public:
Expand Down Expand Up @@ -82,7 +87,7 @@ class MeshTally : public TallyBase
Point _mesh_translation;

/// The index into an array of mesh translations.
unsigned int _instance;
const unsigned int _instance;

/// The index of the mesh added by this tally.
unsigned int _mesh_index;
Expand All @@ -91,5 +96,18 @@ class MeshTally : public TallyBase
openmc::MeshFilter * _mesh_filter;

/// OpenMC unstructured mesh instance for use with mesh tallies
const openmc::LibMesh * _mesh_template;
openmc::LibMesh * _mesh_template;

/**
* For use with AMR only. A copy of the mesh which only contains active elements.
* This removes the link between the MooseMesh that has an auxvariable equation system and the
* OpenMC mesh which has an equation system that is tallied on. The OpenMC equation system throws
* errors when attempting to project solution vectors as it has not been initialized with
* the data structures required for adaptivity.
* TODO: Fix this in OpenMC (enable adaptivity in the equation systems added by openmc::LibMesh
* meshes).
*/
std::unique_ptr<libMesh::ReplicatedMesh> _libmesh_mesh_copy;
/// A mapping between the elements in '_libmesh_mesh_copy' and the elements in the MooseMesh.
std::vector<unsigned int> _active_to_total_mapping;
};
3 changes: 3 additions & 0 deletions include/tallies/TallyBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,9 @@ class TallyBase : public MooseObject
/// Suffixes to apply to 'tally_name' in order to name the fields in the 'output'.
std::vector<std::string> _output_name;

/// Whether the problem uses adaptive mesh refinement or not.
const bool _is_adaptive;

/// Tolerance for setting zero tally
static constexpr Real ZERO_TALLY_THRESHOLD = 1e-12;
};
84 changes: 42 additions & 42 deletions src/base/OpenMCCellAverageProblem.C
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "TallyBase.h"
#include "CellTally.h"
#include "AddTallyAction.h"
#include "CreateDisplacedProblemAction.h"

#include "openmc/constants.h"
#include "openmc/cross_sections.h"
Expand Down Expand Up @@ -84,13 +85,6 @@ OpenMCCellAverageProblem::validParams()
"filled into more than one cell). If your OpenMC model has a unique material "
"in every cell you want to receive density feedback, these two options are IDENTICAL");

// TODO: would be nice to auto-detect this
params.addParam<bool>("fixed_mesh", true,
"Whether the MooseMesh is unchanging during the simulation (true), or whether there is mesh "
"movement and/or adaptivity that is changing the mesh in time (false). When the mesh changes "
"during the simulation, the mapping from OpenMC's cells to the mesh must be re-evaluated after "
"each OpenMC run.");

MooseEnum scores_heat(
"heating heating_local kappa_fission fission_q_prompt fission_q_recoverable");
params.addParam<MooseEnum>(
Expand Down Expand Up @@ -183,7 +177,7 @@ OpenMCCellAverageProblem::validParams()

OpenMCCellAverageProblem::OpenMCCellAverageProblem(const InputParameters & params)
: OpenMCProblemBase(params),
_serialized_solution(NumericVector<Number>::build(_communicator).release()),
_serialized_solution(_aux->serializedSolution()),
_output_cell_mapping(getParam<bool>("output_cell_mapping")),
_initial_condition(
getParam<MooseEnum>("initial_properties").getEnum<coupling::OpenMCInitialCondition>()),
Expand All @@ -193,7 +187,8 @@ OpenMCCellAverageProblem::OpenMCCellAverageProblem(const InputParameters & param
_normalize_by_global(_run_mode == openmc::RunMode::FIXED_SOURCE
? false
: getParam<bool>("normalize_by_global_tally")),
_need_to_reinit_coupling(!getParam<bool>("fixed_mesh")),
_has_adaptivity(getMooseApp().actionWarehouse().hasActions("set_adaptivity_options")),
_need_to_reinit_coupling(_has_adaptivity),
_check_tally_sum(
isParamValid("check_tally_sum")
? getParam<bool>("check_tally_sum")
Expand All @@ -210,12 +205,26 @@ OpenMCCellAverageProblem::OpenMCCellAverageProblem(const InputParameters & param
_volume_calc(nullptr),
_symmetry(nullptr)
{
// Check to see if a displaced problem is being initialized.
// TODO: this also needs to include a "use_displaced_mesh" parameter, alongside the ability to
// actually use the displaced mesh. See https://github.com/neams-th-coe/cardinal/pull/907 for more
// information.
const auto & dis_actions =
getMooseApp().actionWarehouse().getActions<CreateDisplacedProblemAction>();
for (const auto & act : dis_actions)
{
auto has_displaced =
act->isParamValid("displacements") && act->getParam<bool>("use_displaced_mesh");
_need_to_reinit_coupling |= has_displaced;
// Switch the above with: _need_to_reinit_coupling |= (has_displaced && _use_displaced_mesh);
}

// Look through the list of AddTallyActions to see if we have a CellTally. If so, we need to map
// cells.
const auto & actions = getMooseApp().actionWarehouse().getActions<AddTallyAction>();
for (const auto & act : actions)
_has_cell_tallies = act->getMooseObjectType() == "CellTally" || _has_cell_tallies;
_needs_to_map_cells = _needs_to_map_cells || _has_cell_tallies;
_has_cell_tallies |= act->getMooseObjectType() == "CellTally";
_needs_to_map_cells |= _has_cell_tallies;

if (!_needs_to_map_cells)
checkUnusedParam(params,
Expand Down Expand Up @@ -243,11 +252,13 @@ OpenMCCellAverageProblem::OpenMCCellAverageProblem(const InputParameters & param
// the same number of bins or to exactly the same regions of space, so we must
// disable relaxation.
if (_need_to_reinit_coupling && _relaxation != relaxation::none)
mooseError("When 'fixed_mesh' is false, the mapping from the OpenMC model to the [Mesh] may "
"vary in time. This means that we have no guarantee that the number of tally bins (or even "
"the regions of space corresponding to each bin) are fixed. Therefore, it is not "
"possible to apply relaxation to the OpenMC tallies because you might end up trying to add vectors "
"of different length (and possibly spatial mapping).");
mooseError(
"When adaptivity is requested or a displaced problem is used, the mapping from the "
"OpenMC model to the [Mesh] may vary in time. This means that we have no guarantee that "
"the "
"number of tally bins (or even the regions of space corresponding to each bin) are fixed. "
"Therefore, it is not possible to apply relaxation to the OpenMC tallies because you might "
"end up trying to add vectors of different length (and possibly spatial mapping).");

if (_run_mode == openmc::RunMode::FIXED_SOURCE)
checkUnusedParam(params, "normalize_by_global_tally", "running OpenMC in fixed source mode");
Expand Down Expand Up @@ -444,9 +455,6 @@ OpenMCCellAverageProblem::initialSetup()
"OpenMCVolumeCalculation!");
}

if (_adaptivity.isOn() && !_need_to_reinit_coupling)
mooseError("When using mesh adaptivity, 'fixed_mesh' must be false!");

if (isParamValid("symmetry_mapper"))
{
const auto & name = getParam<UserObjectName>("symmetry_mapper");
Expand Down Expand Up @@ -548,7 +556,7 @@ OpenMCCellAverageProblem::setupProblem()
for (unsigned int e = 0; e < _mesh.nElem(); ++e)
{
const auto * elem = _mesh.queryElemPtr(e);
if (!isLocalElem(elem))
if (!isLocalElem(elem) || !elem->active())
continue;

_local_to_global_elem.push_back(e);
Expand Down Expand Up @@ -742,8 +750,8 @@ OpenMCCellAverageProblem::storeElementPhase()
for (const auto & s : excl_density_blocks)
_n_moose_density_elems += numElemsInSubdomain(s);

_n_moose_none_elems =
_mesh.nElem() - _n_moose_temp_density_elems - _n_moose_temp_elems - _n_moose_density_elems;
_n_moose_none_elems = _mesh.getMesh().n_active_elem() - _n_moose_temp_density_elems -
_n_moose_temp_elems - _n_moose_density_elems;
}

void
Expand Down Expand Up @@ -1325,9 +1333,10 @@ OpenMCCellAverageProblem::initializeElementToCellMapping()
mooseError("Did not find any overlap between MOOSE elements and OpenMC cells for "
"the specified blocks!");

_console << "\nMapping between " + Moose::stringify(_mesh.nElem()) + " MOOSE elements and " +
Moose::stringify(_n_openmc_cells) + " OpenMC cells (on " +
Moose::stringify(openmc::model::n_coord_levels) + " coordinate levels):"
_console << "\nMapping between " + Moose::stringify(_mesh.getMesh().n_active_elem()) +
" MOOSE elements and " + Moose::stringify(_n_openmc_cells) +
" OpenMC cells (on " + Moose::stringify(openmc::model::n_coord_levels) +
" coordinate levels):"
<< std::endl;

VariadicTable<std::string, int, int, int, int> vt(
Expand Down Expand Up @@ -1641,7 +1650,7 @@ OpenMCCellAverageProblem::mapElemsToCells()
{
const auto * elem = _mesh.queryElemPtr(e);

if (!isLocalElem(elem))
if (!isLocalElem(elem) || !elem->active())
continue;

local_elem++;
Expand Down Expand Up @@ -1764,10 +1773,9 @@ OpenMCCellAverageProblem::mapElemsToCells()
gatherCellVector(elems, n_elems, _cell_to_elem);

// fill out the elem_to_cell structure
_elem_to_cell.resize(_mesh.nElem());
for (unsigned int e = 0; e < _mesh.nElem(); ++e)
_elem_to_cell[e] = {UNMAPPED, UNMAPPED};

// TODO: figure out how to shrink this so we only store the mapping for active
// elements as opposed to the entire element hierarchy.
_elem_to_cell.resize(_mesh.nElem(), {UNMAPPED, UNMAPPED});
for (const auto & c : _cell_to_elem)
{
for (const auto & e : c.second)
Expand Down Expand Up @@ -2052,7 +2060,7 @@ OpenMCCellAverageProblem::computeVolumeWeightedCellInput(
const auto * elem = _mesh.queryElemPtr(globalElemID(e));
auto v = var_num.at(elem->subdomain_id()).first;
auto dof_idx = elem->dof_number(sys_number, v, 0);
product += (*_serialized_solution)(dof_idx) * elem->volume();
product += _serialized_solution(dof_idx) * elem->volume();
}

volume_product.push_back(product);
Expand Down Expand Up @@ -2305,12 +2313,7 @@ OpenMCCellAverageProblem::checkNormalization(const Real & sum, unsigned int glob
void
OpenMCCellAverageProblem::syncSolutions(ExternalProblem::Direction direction)
{
auto & solution = _aux->solution();

if (_first_transfer)
_serialized_solution->init(_aux->sys().n_dofs(), false, SERIAL);

solution.localize(*_serialized_solution);
_aux->serializeSolution();

switch (direction)
{
Expand Down Expand Up @@ -2403,12 +2406,9 @@ OpenMCCellAverageProblem::syncSolutions(ExternalProblem::Direction direction)

// we need to then re-establish the data structures that map from OpenMC cells to the [Mesh]
// (because the cells changed)
resetTallies();
setupProblem();
}
#else
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain where this went?

Copy link
Contributor Author

@nuclearkevin nuclearkevin Oct 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This #else statement was the cause of the segmentation faults on CIVET, and more generally it resulted in memory leaks when Cardinal is built without DAGMC and using a moving mesh.

When the mesh is changing OpenMCCellAverageProblem needs to reset tallies on every timestep (after the first step), and so it calls resetTallies() at the beginning of the step. This is immediately followed by a call to setupProblem(). Afterwards, if the mesh is moving setupProblem() is called a second time in that #else statement resulting in an initialization of OpenMC tallies twice for each time they're reset. This causes a memory leak as we don't manage the extra OpenMC tally created on each step.

The reason why this caused a segmentation fault is a little more complicated and involves the copy of the mesh created in MeshTally containing only active elements. This mesh copy is deleted both when resetTally() and initializeTally() are called (it's a unique pointer). Since we've been creating two OpenMC tallies per timestep (by accident) the first of those tallies still thinks the mesh copy is valid, and so it tries to tally on datastructures that have had their memory freed. This behaviour is undefined and results in a segmentation fault.

This bug only occurs when Cardinal is compiled without DAGMC as resetTallies() is called again when skinning the geometry (the previous usecase for the #ifdef/#else statement), so nothing breaks.

// re-establish the mapping from the OpenMC cells to the [Mesh] (because the mesh changed)
if (_need_to_reinit_coupling)
setupProblem();
#endif

// Because we require at least one of fluid_blocks and solid_blocks, we are guaranteed
Expand Down Expand Up @@ -2521,7 +2521,7 @@ OpenMCCellAverageProblem::syncSolutions(ExternalProblem::Direction direction)
}

_first_transfer = false;
solution.close();
_aux->solution().close();
_aux->system().update();
}

Expand Down
2 changes: 1 addition & 1 deletion src/base/OpenMCProblemBase.C
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ OpenMCProblemBase::numElemsInSubdomain(const SubdomainID & id) const
{
const auto * elem = _mesh.queryElemPtr(e);

if (!isLocalElem(elem))
if (!isLocalElem(elem) || !elem->active())
continue;

const auto subdomain_id = elem->subdomain_id();
Expand Down
Loading