Commit be3544d1 authored by Lukas Riedel's avatar Lukas Riedel 📝 Committed by Santiago Ospina De Los Ríos

Add warnings on non-collective classes/methods

Use warning paragraphs in doxygen docs to inform about non-collective
methods of BoundaryConditionManager and TimeStepController.
parent 7250c44c
......@@ -40,6 +40,7 @@
* Outflow boundary condition for Richards model !191
* Specify water content as initial condition in Richards model !187
* Documentation about model solver loop and time step adaptation schemes !190
* Documentation about the parallel features of several classes and methods !197
### Changed
* Data structures for storing and accessing parameter information !55
......@@ -81,6 +82,7 @@
* Use unsafe loader of PyYAML v5.2 for loading parameter scraper data !177
* CFL condition in explicit Transport model serves as time step upper limit !179
* Use apparent solute velocity instead of water flux in CFL condition !180
* Steps were not synchronized when timestep suggestions differed in parallel !197
### Deprecated
......
......@@ -19,6 +19,8 @@ namespace Dorie{
/** Define the virtual public functions queried by the Dune::Dorie::FlowBoundary and
* Dune::Dorie::CalculationController classes, as well as the BC Side struct for convenience.
* This class should be inherited by all classes implementing a BC file readout interface.
*
* @ingroup NonCollective
*/
template<typename Traits>
class BCReadoutInterface{
......
......@@ -58,6 +58,14 @@ namespace Dorie {
* 4. Retrieve a shared pointer to the actual boundary condition for any
* boundary intersection by calling bc().
*
* @ingroup NonCollective
*
* @warning
* Methods of this class are @ref NonCollective. Objects of this class only
* store boundary conditions for the boundary indices given in the index map
* they are constructed with. This may lead to max_time_step() returning
* different values for each process when executed in a parallel application.
*
* @ingroup Boundary
*/
template<typename Traits, BCMMode mode=BCMMode::none>
......@@ -129,6 +137,8 @@ public:
* \throw InvalidStateException if the current time 'overtook' the
* applied time interval (i.e., the time step returned by this
* function would be negative).
* \warning This algorithm is @ref NonCollective and may return different
* values for each (parallel) process it is invoked on.
*/
RF max_time_step (const RF current_time) const
{
......
......@@ -9,34 +9,37 @@ namespace Dune{
namespace Dorie{
/**
* @brief Computes the CFL-condition (\f$\mathcal{CFL}\f$) for a grid
* function representing a velocity field. In particular, the
* return value is the suggested timestep for the lowest CFL
* @brief Computes the CFL-condition (\f$\mathcal{CFL}\f$) for a grid
* function representing a velocity field. In particular, the
* return value is the suggested timestep for the lowest CFL
* ratio for each cell:
*
*
* @f{eqnarray*}{
* \mathcal{CFL} = \min_{T\in\mathcal{T}_h} ||\beta||^{-1}_{[L^\infty(T)]^d}h_T
* @f}
* where \f$\mathcal{T}_h\f$ is a triangulation, \f$\beta\f$ the
* velocity field, and \f$h_T\f$ the diameter of the cell
* \f$T\in \mathcal{T}_h\f$.
* where \f$\mathcal{T}_h\f$ is a triangulation, \f$\beta\f$ the
* velocity field, and \f$h_T\f$ the diameter of the cell
* \f$T\in \mathcal{T}_h\f$.
* As is usual with the CFL-condition, one can restrict the time
* step (\f$\Delta t\f$) for explicit schemes using a given courant
* step (\f$\Delta t\f$) for explicit schemes using a given courant
* number \f$\varrho \le 1\f$:
*
*
* @f{eqnarray*}{
* \Delta t \le \varrho \cdot \mathcal{CFL}
* @f}
*
* @param[in] gf Grid function representing the veolcity field
*
* @param[in] gf Grid function representing the veolcity field
* \f$\beta\f$.
* @param[in] intorder The integration order. This value determines the
* quadrature points to evaluate
* @param[in] intorder The integration order. This value determines the
* quadrature points to evaluate
* \f$||\beta||_{[L^\infty(T)]^d}h_T\f$.
*
* @tparam GF The grid function type.
* @tparam RF The range field. Type to represent CFL values.
*
* @ingroup Common
* @remark @ref Collective
*
* @return The CFL-condition (\f$\mathcal{CFL}\f$).
*/
template<class VectorGF, class ScalarGF, class RF = double>
......
......@@ -88,6 +88,7 @@ constexpr bool supports_flux_reconstr_v
* @author Santiago Ospina De Los Ríos
* @date 2018
* @ingroup FluxReconstruction
* @ingroup Collective
*
* @tparam GO The GridOperator type.
* @tparam gt Geometry type of the grid.
......
......@@ -188,6 +188,72 @@
respective logging header inside DORiE.
@}
@defgroup Parallelism Parallelism
@{
@ingroup Common
@brief How parallelism is handled
## Overview
Parallelism in DORiE, unsurprisingly, relies on the parallelism constructs
of DUNE. The main characteristization of parallelism in DUNE is thorugh its
grid. Grids, once constructed, get distributed among the available processes
and algorithms try to stick to the local processor grid as much as possible.
When a processor requires information from other processors, data is mainly
communicated between boundaries of the grid using the grid communicator.
In our case, this data exchange is automatically done within the PDELab
algorithms for grid operations and equation system solvers.
### About documentation
If parallelism is hidden within the DUNE interfaces, how does a DORiE
developer knows whether an algorithm has to be called in parallel?
If the class or method is documented with a parallel group (@ref Collective
or @ref NonCollective), answer is easy, otherwise, it is safe to assume that
whenever an algorithm requires a grid or a PDELab algorithm, the method has
to be called in every processor where the grid lives. Notice that compile
time information (e.g. traits) can be understood as @ref Collective since
compilation cannot distinguish between different processors.
When documenting classes and functions bear in mind that functions cannot
belong to several Doxygen groups, therefore, use the command \@`remark` and
\@`ref` instead of \@`ingroup` to describe the group a function belongs to.
If a class holds @ref Collective objects (e.g. a parallel vector), then the
class must be included in the @ref Collective group, however, if one of its
functions does not rely on this @ref Collective object, the function itself
can marked as @ref NonCollective.
@defgroup Collective Collective
@{
@ingroup Parallelism
@brief Parallelism-aware classes and methods
## Overview
The class or method is _collective_ on the grid. This means that algorithm
only yiels meaningful results when the method is invoked on every processor
where the grid lives. Results of this type of methods do not nessesarily
translate to the same value for every processor (e.g. reduction), instead,
resulting side effects could include creation or modification data that is
distributed among the different processors and only make sense when treated
collectively (e.g. parallel vector).
@}
@defgroup NonCollective Non-Collective
@{
@ingroup Parallelism
@brief Purely sequential classes and methods
## Overview
The class or method is _non-collective_. This means that algorithms are
unaware of other processors computations. Methods can be called
independelty on each processor and each of them will have a different
effect regarding to the local data available to the algorithm performed.
@}
@}
@defgroup Interpolators Interpolators
@{
@ingroup Common
......@@ -209,7 +275,7 @@
@brief Initial state for models
## Overview
Dune::Dorie::InitialCondition is the base class used to map input settings
(e.g. HDF and VTK data files, analytic functions) to the initial state of
models. It is the polymorphic version of the PDELab GridFunctionBase so that
......
......@@ -41,6 +41,12 @@ namespace Dorie{
* retrieve it via get_time_step(), granting the option of adjusting
* the time step to the simulation end time.
*
* @ingroup NonCollective
*
* \warning The methods of this class are @ref NonCollective. The variables
* stored and processed by this class are not communicated between
* parallel processes!
*
* \tparam TF Time data type
* \ingroup Common
* \author Lukas Riedel
......
......@@ -20,6 +20,7 @@ namespace Dorie{
* @todo Dune::Dorie::OutputPolicy can handle specific intermediate times
* of the stepping.
* @ingroup Models
* @ingroup Collective
*/
class ModelBase
{
......@@ -53,6 +54,7 @@ public:
/*-----------------------------------------------------------------------*//**
* @brief Sets the output policy.
*
* @remark @ref NonCollective
* @param[in] output_policy The output policy,
*/
void set_policy(OutputPolicy output_policy) {_output_policy = output_policy;}
......@@ -70,6 +72,7 @@ public:
/*-----------------------------------------------------------------------*//**
* @brief Returns the current output policy.
*
* @remark @ref NonCollective
* @return The current output policy.
*/
OutputPolicy output_policy() const {return _output_policy;}
......
......@@ -80,6 +80,7 @@ public:
* @date 2018-2019
* @ingroup ModelTransport
* @ingroup ModelRichards
* @ingroup Collective
*
* @todo Implement source term.
*
......@@ -231,6 +232,8 @@ private:
#ifdef GTEST
FRIEND_TEST(ModelCoupling, TimeSteps);
FRIEND_TEST(ModelCouplingParallel, Richards);
FRIEND_TEST(ModelCouplingParallel, Transport);
#endif
};
......
......@@ -190,8 +190,11 @@ void ModelRichards<Traits>::step()
// iterate until break or throw
while (true)
{
// Fetch current time step
// NOTE: Communicate minimal time step between processors!
time_step_ctrl.suggest_time_step(dt_suggestion);
const auto dt = time_step_ctrl.get_time_step(this->_time);
auto dt = time_step_ctrl.get_time_step(this->_time);
dt = gv.comm().min(dt);
try
{
......
......@@ -198,6 +198,7 @@ struct ModelRichardsTraits : public BaseTraits
* the solution
*
* @ingroup ModelRichards
* @ingroup Collective
*
* @tparam ModelRichardsTraits Traits containing the type definitions
* for Richards Model
......
......@@ -255,8 +255,10 @@ void ModelTransport<Traits>::step()
auto unext = std::make_shared<U>(*u);
// Obtain time variables
// NOTE: Communicate minimal time step between processors!
const auto time = this->_time;
auto dt = time_step_ctrl.get_time_step(time);
dt = gv.comm().min(dt);
// Solver loop
// NOTE: Runs infinitely until solution is computed and valid (causes break)
......@@ -313,8 +315,10 @@ void ModelTransport<Traits>::step()
}
// Reduce time step and try again
// NOTE: Communicate minimal time step between processes!
time_step_ctrl.decrease_time_step();
dt = time_step_ctrl.get_time_step(time);
dt = gv.comm().min(dt);
} // while (true)
// Short time step log for level 'info' (after success)
......
......@@ -237,6 +237,7 @@ struct ModelTransportTraits : public BaseTraits
* @author Santiago Ospina De Los Ríos
* @date 2018
* @ingroup TransportModel
* @ingroup Collective
*
* @todo Implement source term.
* @todo Implement more complex initial conditions.
......
......@@ -42,6 +42,17 @@ target_compile_definitions(${mc_target}
DORIE_TORDER=0
)
# Model coupling in parallel
foreach (ini_name implicit explicit)
dune_add_test(
NAME ut-test-model-coupling-par_${ini_name}
TARGET ${mc_target}
COMMAND --allow-run-as-root ${mc_target}
MPI_RANKS 2
TIMEOUT 30
CMD_ARGS ${CMAKE_CURRENT_BINARY_DIR}/${ini_name}.ini
)
endforeach()
# time controller test
dorie_add_metaini_test(
......
#include <gtest/gtest.h>
#include <random>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
......@@ -63,6 +65,8 @@ protected:
}
};
// --- Model Coupling Tests --- //
TEST_P(ModelCoupling, TimeSteps)
{
// Get a pointer to the actual model class
......@@ -93,4 +97,82 @@ INSTANTIATE_TEST_SUITE_P(ModelCouplingTimeSteps,
std::make_tuple(100.0, 4, 6)
));
// --- Parallel Model Coupling Tests --- //
class ModelCouplingParallel :
public ::testing::Test
{
protected:
/// A pointer to the model
std::shared_ptr<ModelBase> _model;
/// Time step for this process
double dt;
/// Minimum time step across all processes
double dt_min;
void SetUp () override
{
// Initialize ALL the things!
auto [inifile, log, helper] = Dune::Dorie::Setup::init(_argc, _argv);
log.reset();
_model = TransportFactory::create(inifile, helper);
// Create random time steps for every process, and find minimum
auto comm = helper.getCollectiveCommunication();
std::mt19937 gen(comm.rank());
std::uniform_real_distribution<double> dist;
dt = dist(gen);
dt_min = comm.min(dt);
}
void TearDown () override
{
spdlog::drop_all();
}
};
/// Check that models communicate minimal time step between processes
TEST_F(ModelCouplingParallel, Richards)
{
// Get a pointer to the actual model class
auto &model = dynamic_cast<Model&>(*_model);
// Set time steps on each process, then run
model.suggest_timestep(dt);
model.step();
EXPECT_DOUBLE_EQ(model.current_time(), dt_min);
}
TEST_F(ModelCouplingParallel, Transport)
{
// Get a pointer to the actual model class
auto &model = dynamic_cast<Model&>(*_model);
// Create instationary grid function containers
using GFWaterContentContainer = typename Traits::GFWaterContentContainer;
using GFWaterFluxContainer = typename Traits::GFWaterFluxContainer;
auto igf_water_content = std::make_shared<GFWaterContentContainer>();
auto igf_water_flux = std::make_shared<GFWaterFluxContainer>();
// Set initial state of the water content to container
auto gf_water_content_begin = model._richards->get_water_content();
igf_water_content->push(gf_water_content_begin, 0.0);
igf_water_content->push(gf_water_content_begin, 1.0);
// Set initial state of the water flux to container
auto gf_water_flux_begin = model._richards->get_water_flux_reconstructed();
igf_water_flux->push(gf_water_flux_begin, 0.0);
igf_water_flux->push(gf_water_flux_begin, 1.0);
// Set data in transport solver
model._transport->set_water_flux(igf_water_flux);
model._transport->set_water_content(igf_water_content);
// Perform step in transport solver
model._transport->suggest_timestep(dt);
model._transport->step();
EXPECT_DOUBLE_EQ(model._transport->current_time(), dt_min);
}
} // namespace Dune::Dorie
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment