Skip to content

Commit

Permalink
Set intersection for block check (idaholab#22563)
Browse files Browse the repository at this point in the history
  • Loading branch information
dschwen committed Mar 2, 2023
1 parent 134a61c commit 799f56c
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 55 deletions.
3 changes: 1 addition & 2 deletions framework/doc/content/source/interfaces/Coupleable.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,7 @@ element user objects and elemental AuxKernels may only obtain references to elem

The block restrictins of the variables are also checked not to exceed the block restrictions of the calling object.
MOOSE keeps track of all variables to which a reference was obtained through `Coupleable::writableVariable`. Each
variable in the system may at most be written to by a single object (this might be relaxed in the future to permit
multiple object with non overlapping block restrictions to obtain a writable reference).
variable in the system may at most be written to by a single object on any given subdomain.

The user object and aux kernel thread loops check if an executed object has any writable variable references, and
if so, will insert those variables into the aux solution vector. This obviates the need for using the
Expand Down
5 changes: 5 additions & 0 deletions framework/include/interfaces/Coupleable.h
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,11 @@ class Coupleable
*/
virtual VariableValue & writableCoupledValue(const std::string & var_name, unsigned int comp = 0);

/**
* Checks that the passed in variable is only accessed writable by one object in a given subdomain
*/
void checkWritableVar(MooseVariable * var);

/**
* Returns an old value from previous time step of a coupled variable
* @param var_name Name of coupled variable
Expand Down
28 changes: 28 additions & 0 deletions framework/include/utils/MooseUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,34 @@ get(const std::shared_ptr<T> & s)
return s.get();
}

/**
* This method detects whether two sets intersect without building a result set.
* It exits as soon as any intersection is detected.
*/
template <class InputIterator>
bool
setsIntersect(InputIterator first1, InputIterator last1, InputIterator first2, InputIterator last2)
{
while (first1 != last1 && first2 != last2)
{
if (*first1 == *first2)
return true;

if (*first1 < *first2)
++first1;
else if (*first1 > *first2)
++first2;
}
return false;
}

template <class T>
bool
setsIntersect(const T & s1, const T & s2)
{
return setsIntersect(s1.begin(), s1.end(), s2.begin(), s2.end());
}

} // MooseUtils namespace

/**
Expand Down
52 changes: 30 additions & 22 deletions framework/src/interfaces/Coupleable.C
Original file line number Diff line number Diff line change
Expand Up @@ -861,28 +861,9 @@ Coupleable::writableVariable(const std::string & var_name, unsigned int comp)
"'.");
// if (nuo && !var->isNodal()) is handled by checkVar already

// check block restrictions for compatibility
const auto * br = dynamic_cast<const BlockRestrictable *>(this);
if (!var->hasBlocks(br->blocks()))
mooseError("The variable '",
var->name(),
"' must be defined on all blocks '",
_obj->name(),
"' is defined on");

// make sure only one object can access a variable
for (const auto & ci : _obj->getMooseApp().getInterfaceObjects<Coupleable>())
if (ci != this && ci->_writable_coupled_variables[_c_tid].count(var))
mooseError("'",
ci->_obj->name(),
"' already obtained a writable reference to '",
var->name(),
"'. Only one object can obtain such a reference per variable in a simulation.");
checkWritableVar(var);

// var is unique across threads, so we could forego having a separate set per thread, but we
// need qucik access to the list of all variables that need to be inserted into the solution
// vector by a given thread.
_writable_coupled_variables[_c_tid].insert(var);
return *var;
}

Expand Down Expand Up @@ -918,20 +899,47 @@ Coupleable::writableCoupledValue(const std::string & var_name, unsigned int comp
var->name(),
"' must both either be nodal or elemental.");

// make sure only one object can access a variable
checkWritableVar(var);

return const_cast<VariableValue &>(coupledValue(var_name, comp));
}

void
Coupleable::checkWritableVar(MooseVariable * var)
{
// check block restrictions for compatibility
const auto * br = dynamic_cast<const BlockRestrictable *>(this);
if (!var->hasBlocks(br->blockIDs()))
mooseError("The variable '",
var->name(),
"' must be defined on all blocks '",
_obj->name(),
"' is defined on");

// make sure only one object can access a variable
for (const auto & ci : _obj->getMooseApp().getInterfaceObjects<Coupleable>())
if (ci != this && ci->_writable_coupled_variables[_c_tid].count(var))
{
// if both this and ci are block restrictable then we check if the block restrictions
// are not overlapping. If they don't we permit the call.
const auto * br_other = dynamic_cast<const BlockRestrictable *>(ci);
if (br && br_other && br->blockRestricted() && br_other->blockRestricted() &&
!MooseUtils::setsIntersect(br->blockIDs(), br_other->blockIDs()))
continue;

mooseError("'",
ci->_obj->name(),
"' already obtained a writable reference to '",
var->name(),
"'. Only one object can obtain such a reference per variable in a simulation.");
"'. Only one object can obtain such a reference per variable and subdomain in a "
"simulation.");
}

// var is unique across threads, so we could forego having a separate set per thread, but we
// need qucik access to the list of all variables that need to be inserted into the solution
// vector by a given thread.
_writable_coupled_variables[_c_tid].insert(var);
return const_cast<VariableValue &>(coupledValue(var_name, comp));
}

const VariableValue &
Expand Down
23 changes: 0 additions & 23 deletions modules/phase_field/include/postprocessors/FeatureFloodCount.h
Original file line number Diff line number Diff line change
Expand Up @@ -541,29 +541,6 @@ class FeatureFloodCount : public GeneralPostprocessor,
*/
void updateRegionOffsets();

/**
* This method detects whether two sets intersect without building a result set.
* It exits as soon as any intersection is detected.
*/
template <class InputIterator>
static bool setsIntersect(InputIterator first1,
InputIterator last1,
InputIterator first2,
InputIterator last2)
{
while (first1 != last1 && first2 != last2)
{
if (*first1 == *first2)
return true;

if (*first1 < *first2)
++first1;
else if (*first1 > *first2)
++first2;
}
return false;
}

/*************************************************
*************** Data Structures *****************
************************************************/
Expand Down
11 changes: 3 additions & 8 deletions modules/phase_field/src/postprocessors/FeatureFloodCount.C
Original file line number Diff line number Diff line change
Expand Up @@ -1964,24 +1964,19 @@ FeatureFloodCount::FeatureData::boundingBoxesIntersect(const FeatureData & rhs)
bool
FeatureFloodCount::FeatureData::halosIntersect(const FeatureData & rhs) const
{
return setsIntersect(
_halo_ids.begin(), _halo_ids.end(), rhs._halo_ids.begin(), rhs._halo_ids.end());
return MooseUtils::setsIntersect(_halo_ids, rhs._halo_ids);
}

bool
FeatureFloodCount::FeatureData::periodicBoundariesIntersect(const FeatureData & rhs) const
{
return setsIntersect(_periodic_nodes.begin(),
_periodic_nodes.end(),
rhs._periodic_nodes.begin(),
rhs._periodic_nodes.end());
return MooseUtils::setsIntersect(_periodic_nodes, rhs._periodic_nodes);
}

bool
FeatureFloodCount::FeatureData::ghostedIntersect(const FeatureData & rhs) const
{
return setsIntersect(
_ghosted_ids.begin(), _ghosted_ids.end(), rhs._ghosted_ids.begin(), rhs._ghosted_ids.end());
return MooseUtils::setsIntersect(_ghosted_ids, rhs._ghosted_ids);
}

bool
Expand Down
40 changes: 40 additions & 0 deletions test/tests/userobjects/writable_variable/block2.i
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[Mesh]
[gen]
type = GeneratedMeshGenerator
dim = 2
nx = 2
ny = 1
subdomain_ids = '1 2'
[]
[]

[AuxVariables]
[v]
family = MONOMIAL
order = CONSTANT
[]
[]

[UserObjects]
[elemental1]
type = MultiUpdateElementalUO
v = v
[]
[elemental2]
type = MultiUpdateElementalUO
v = v
[]
[]

[Problem]
kernel_coverage_check = false
solve = false
[]

[Executioner]
type = Steady
[]

[Outputs]
exodus = true
[]
Binary file not shown.
22 changes: 22 additions & 0 deletions test/tests/userobjects/writable_variable/tests
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,26 @@
expect_err = "The variable 'v' must be defined on all blocks 'elemental' is defined on"
requirement = "The system shall enforce that all variables an object obtains a writable reference to are defined on all of the object's blocks"
[]

[block2]
type = Exodiff
input = block2.i
exodiff = block2_out.e
cli_args = 'UserObjects/elemental1/block=1 UserObjects/elemental2/block=2'
requirement = "The system shall permit multiple objects to access a variable for writing if the objects have non-overlapping block restrictions"
[]
[block2_error]
type = RunException
input = block2.i
expect_err = "'elemental1' already obtained a writable reference to 'v'. Only one object can obtain such a reference per variable and subdomain in a simulation."
cli_args = 'UserObjects/elemental1/block=1 UserObjects/elemental2/block="1 2"'
requirement = "The system shall error out if multiple objects with overlapping block restrictions try to access a variable for writing"
[]
[block2_error2]
type = RunException
input = block2.i
expect_err = "'elemental1' already obtained a writable reference to 'v'. Only one object can obtain such a reference per variable and subdomain in a simulation."
cli_args = 'UserObjects/elemental1/block=1'
requirement = "The system shall error out if multiple objects, of which at least one is not block restricted, try to access a variable for writing"
[]
[]

0 comments on commit 799f56c

Please sign in to comment.