From 7c4d508e496ae0f8d4ea3ccb05314e20046bdb76 Mon Sep 17 00:00:00 2001 From: Wenqing Wang <wenqing.wang@ufz.de> Date: Mon, 19 Dec 2016 09:40:49 +0100 Subject: [PATCH] [Coupling] Added staggered coupling computation. --- NumLib/ODESolver/NonlinearSolver.cpp | 18 +- NumLib/ODESolver/NonlinearSolver.h | 17 +- NumLib/ODESolver/NonlinearSystem.h | 10 +- NumLib/ODESolver/ODESystem.h | 10 +- NumLib/ODESolver/TimeDiscretizedODESystem.cpp | 10 +- NumLib/ODESolver/TimeDiscretizedODESystem.h | 10 +- ProcessLib/Process.cpp | 6 +- ProcessLib/Process.h | 9 +- ProcessLib/StaggeredCouplingTerm.cpp | 26 ++ ProcessLib/StaggeredCouplingTerm.h | 42 +++ ProcessLib/UncoupledProcessesTimeLoop.cpp | 289 ++++++++++++++---- ProcessLib/UncoupledProcessesTimeLoop.h | 20 +- Tests/NumLib/ODEs.h | 31 +- Tests/NumLib/TimeLoopSingleODE.h | 8 +- 14 files changed, 416 insertions(+), 90 deletions(-) create mode 100644 ProcessLib/StaggeredCouplingTerm.cpp create mode 100644 ProcessLib/StaggeredCouplingTerm.h diff --git a/NumLib/ODESolver/NonlinearSolver.cpp b/NumLib/ODESolver/NonlinearSolver.cpp index bf55b1a5d8d..2353746d8cb 100644 --- a/NumLib/ODESolver/NonlinearSolver.cpp +++ b/NumLib/ODESolver/NonlinearSolver.cpp @@ -24,13 +24,14 @@ namespace NumLib { void NonlinearSolver<NonlinearSolverTag::Picard>::assemble( - GlobalVector const& x) const + GlobalVector const& x, + ProcessLib::StaggeredCouplingTerm const& coupled_term) const { - _equation_system->assemble(x); + _equation_system->assemble(x, coupled_term); } bool NonlinearSolver<NonlinearSolverTag::Picard>::solve( - GlobalVector& x, + GlobalVector& x, ProcessLib::StaggeredCouplingTerm const& coupled_term, std::function<void(unsigned, GlobalVector const&)> const& postIterationCallback) { namespace LinAlg = MathLib::LinAlg; @@ -61,7 +62,7 @@ bool NonlinearSolver<NonlinearSolverTag::Picard>::solve( BaseLib::RunTime time_assembly; time_assembly.start(); - sys.assemble(x); + sys.assemble(x, coupled_term); sys.getA(A); sys.getRhs(rhs); INFO("[time] Assembly took %g s.", time_assembly.elapsed()); @@ -161,16 +162,17 @@ bool NonlinearSolver<NonlinearSolverTag::Picard>::solve( } void NonlinearSolver<NonlinearSolverTag::Newton>::assemble( - GlobalVector const& x) const + GlobalVector const& x, + ProcessLib::StaggeredCouplingTerm const& coupled_term) const { - _equation_system->assemble(x); + _equation_system->assemble(x, coupled_term); // TODO if the equation system would be reset to nullptr after each // assemble() or solve() call, the user would be forced to set the // equation every time and could not forget it. } bool NonlinearSolver<NonlinearSolverTag::Newton>::solve( - GlobalVector& x, + GlobalVector& x, ProcessLib::StaggeredCouplingTerm const& coupled_term, std::function<void(unsigned, GlobalVector const&)> const& postIterationCallback) { namespace LinAlg = MathLib::LinAlg; @@ -204,7 +206,7 @@ bool NonlinearSolver<NonlinearSolverTag::Newton>::solve( BaseLib::RunTime time_assembly; time_assembly.start(); - sys.assemble(x); + sys.assemble(x, coupled_term); sys.getResidual(x, res); sys.getJacobian(J); INFO("[time] Assembly took %g s.", time_assembly.elapsed()); diff --git a/NumLib/ODESolver/NonlinearSolver.h b/NumLib/ODESolver/NonlinearSolver.h index c4a9be12b97..1eeb874bdf7 100644 --- a/NumLib/ODESolver/NonlinearSolver.h +++ b/NumLib/ODESolver/NonlinearSolver.h @@ -13,6 +13,8 @@ #include <utility> #include <logog/include/logog.hpp> +#include "ProcessLib/StaggeredCouplingTerm.h" + #include "ConvergenceCriterion.h" #include "NonlinearSystem.h" #include "Types.h" @@ -42,7 +44,9 @@ public: * * \param x the state at which the equation system will be assembled. */ - virtual void assemble(GlobalVector const& x) const = 0; + virtual void assemble(GlobalVector const& x, + ProcessLib::StaggeredCouplingTerm const& coupled_term + ) const = 0; /*! Assemble and solve the equation system. * @@ -53,6 +57,7 @@ public: * \retval false otherwise */ virtual bool solve(GlobalVector& x, + ProcessLib::StaggeredCouplingTerm const& coupled_term, std::function<void(unsigned, GlobalVector const&)> const& postIterationCallback) = 0; @@ -101,9 +106,12 @@ public: _equation_system = &eq; _convergence_criterion = &conv_crit; } - void assemble(GlobalVector const& x) const override; + void assemble(GlobalVector const& x, + ProcessLib::StaggeredCouplingTerm const& coupled_term + ) const override; bool solve(GlobalVector& x, + ProcessLib::StaggeredCouplingTerm const& coupled_term, std::function<void(unsigned, GlobalVector const&)> const& postIterationCallback) override; @@ -156,9 +164,12 @@ public: _equation_system = &eq; _convergence_criterion = &conv_crit; } - void assemble(GlobalVector const& x) const override; + void assemble(GlobalVector const& x, + ProcessLib::StaggeredCouplingTerm const& coupled_term + ) const override; bool solve(GlobalVector& x, + ProcessLib::StaggeredCouplingTerm const& coupled_term, std::function<void(unsigned, GlobalVector const&)> const& postIterationCallback) override; diff --git a/NumLib/ODESolver/NonlinearSystem.h b/NumLib/ODESolver/NonlinearSystem.h index edfa53160ee..69b1c116977 100644 --- a/NumLib/ODESolver/NonlinearSystem.h +++ b/NumLib/ODESolver/NonlinearSystem.h @@ -9,6 +9,8 @@ #pragma once +#include "ProcessLib/StaggeredCouplingTerm.h" + #include "EquationSystem.h" #include "Types.h" @@ -36,7 +38,9 @@ public: //! Assembles the linearized equation system at the point \c x. //! The linearized system is \f$A(x) \cdot x = b(x)\f$. Here the matrix //! \f$A(x)\f$ and the vector \f$b(x)\f$ are assembled. - virtual void assemble(GlobalVector const& x) = 0; + virtual void assemble(GlobalVector const& x, + ProcessLib::StaggeredCouplingTerm const& coupled_term + ) = 0; /*! Writes the residual at point \c x to \c res. * @@ -75,7 +79,9 @@ public: //! Assembles the linearized equation system at the point \c x. //! The linearized system is \f$J(x) \cdot \Delta x = (x)\f$. Here the //! residual vector \f$r(x)\f$ and its Jacobian \f$J(x)\f$ are assembled. - virtual void assemble(GlobalVector const& x) = 0; + virtual void assemble(GlobalVector const& x, + ProcessLib::StaggeredCouplingTerm const& coupled_term + ) = 0; //! Writes the linearized equation system matrix to \c A. //! \pre assemble() must have been called before. diff --git a/NumLib/ODESolver/ODESystem.h b/NumLib/ODESolver/ODESystem.h index aafe142c7a5..f1a40fa7bb9 100644 --- a/NumLib/ODESolver/ODESystem.h +++ b/NumLib/ODESolver/ODESystem.h @@ -12,6 +12,8 @@ #include "MathLib/LinAlg/MatrixVectorTraits.h" #include "NumLib/IndexValueVector.h" +#include "ProcessLib/StaggeredCouplingTerm.h" + #include "EquationSystem.h" #include "Types.h" @@ -47,7 +49,9 @@ public: //! Assemble \c M, \c K and \c b at the provided state (\c t, \c x). virtual void assemble(const double t, GlobalVector const& x, GlobalMatrix& M, GlobalMatrix& K, - GlobalVector& b) = 0; + GlobalVector& b, + ProcessLib::StaggeredCouplingTerm const& coupled_term) + = 0; using Index = MathLib::MatrixVectorTraits<GlobalMatrix>::Index; @@ -115,7 +119,9 @@ public: GlobalVector const& xdot, const double dxdot_dx, const double dx_dx, GlobalMatrix& M, GlobalMatrix& K, - GlobalVector& b, GlobalMatrix& Jac) = 0; + GlobalVector& b, GlobalMatrix& Jac, + ProcessLib::StaggeredCouplingTerm + const& coupled_term) = 0; }; //! @} diff --git a/NumLib/ODESolver/TimeDiscretizedODESystem.cpp b/NumLib/ODESolver/TimeDiscretizedODESystem.cpp index a1b6f29685c..2298e5c0f99 100644 --- a/NumLib/ODESolver/TimeDiscretizedODESystem.cpp +++ b/NumLib/ODESolver/TimeDiscretizedODESystem.cpp @@ -67,7 +67,8 @@ TimeDiscretizedODESystem< void TimeDiscretizedODESystem<ODESystemTag::FirstOrderImplicitQuasilinear, NonlinearSolverTag::Newton>:: - assemble(const GlobalVector& x_new_timestep) + assemble(const GlobalVector& x_new_timestep, + ProcessLib::StaggeredCouplingTerm const& coupled_term) { namespace LinAlg = MathLib::LinAlg; @@ -85,7 +86,7 @@ void TimeDiscretizedODESystem<ODESystemTag::FirstOrderImplicitQuasilinear, _Jac->setZero(); _ode.assembleWithJacobian(t, x_curr, xdot, dxdot_dx, dx_dx, *_M, *_K, *_b, - *_Jac); + *_Jac, coupled_term); LinAlg::finalizeAssembly(*_M); LinAlg::finalizeAssembly(*_K); @@ -175,7 +176,8 @@ TimeDiscretizedODESystem< void TimeDiscretizedODESystem<ODESystemTag::FirstOrderImplicitQuasilinear, NonlinearSolverTag::Picard>:: - assemble(const GlobalVector& x_new_timestep) + assemble(const GlobalVector& x_new_timestep, + ProcessLib::StaggeredCouplingTerm const& coupled_term) { namespace LinAlg = MathLib::LinAlg; @@ -186,7 +188,7 @@ void TimeDiscretizedODESystem<ODESystemTag::FirstOrderImplicitQuasilinear, _K->setZero(); _b->setZero(); - _ode.assemble(t, x_curr, *_M, *_K, *_b); + _ode.assemble(t, x_curr, *_M, *_K, *_b, coupled_term); LinAlg::finalizeAssembly(*_M); LinAlg::finalizeAssembly(*_K); diff --git a/NumLib/ODESolver/TimeDiscretizedODESystem.h b/NumLib/ODESolver/TimeDiscretizedODESystem.h index 4a9856d0847..2db7ddc4edb 100644 --- a/NumLib/ODESolver/TimeDiscretizedODESystem.h +++ b/NumLib/ODESolver/TimeDiscretizedODESystem.h @@ -11,6 +11,8 @@ #include <memory> +#include "ProcessLib/StaggeredCouplingTerm.h" + #include "MatrixTranslator.h" #include "NonlinearSystem.h" #include "ODESystem.h" @@ -80,7 +82,9 @@ public: ~TimeDiscretizedODESystem(); - void assemble(const GlobalVector& x_new_timestep) override; + void assemble(const GlobalVector& x_new_timestep, + ProcessLib::StaggeredCouplingTerm const& coupled_term) + override; void getResidual(GlobalVector const& x_new_timestep, GlobalVector& res) const override; @@ -172,7 +176,9 @@ public: ~TimeDiscretizedODESystem(); - void assemble(const GlobalVector& x_new_timestep) override; + void assemble(const GlobalVector& x_new_timestep, + ProcessLib::StaggeredCouplingTerm const& coupled_term) + override; void getA(GlobalMatrix& A) const override { diff --git a/ProcessLib/Process.cpp b/ProcessLib/Process.cpp index 82e75ec037b..4e69b98a00c 100644 --- a/ProcessLib/Process.cpp +++ b/ProcessLib/Process.cpp @@ -119,7 +119,8 @@ MathLib::MatrixSpecifications Process::getMatrixSpecifications() const } void Process::assemble(const double t, GlobalVector const& x, GlobalMatrix& M, - GlobalMatrix& K, GlobalVector& b) + GlobalMatrix& K, GlobalVector& b, + StaggeredCouplingTerm const& coupled_term) { assembleConcreteProcess(t, x, M, K, b); _boundary_conditions.applyNaturalBC(t, x, K, b); @@ -129,7 +130,8 @@ void Process::assembleWithJacobian(const double t, GlobalVector const& x, GlobalVector const& xdot, const double dxdot_dx, const double dx_dx, GlobalMatrix& M, GlobalMatrix& K, - GlobalVector& b, GlobalMatrix& Jac) + GlobalVector& b, GlobalMatrix& Jac, + StaggeredCouplingTerm const& coupled_term) { assembleWithJacobianConcreteProcess(t, x, xdot, dxdot_dx, dx_dx, M, K, b, Jac); diff --git a/ProcessLib/Process.h b/ProcessLib/Process.h index 874be468a4c..44df54f18ca 100644 --- a/ProcessLib/Process.h +++ b/ProcessLib/Process.h @@ -22,6 +22,7 @@ #include "CachedSecondaryVariable.h" #include "AbstractJacobianAssembler.h" #include "ProcessType.h" +#include "StaggeredCouplingTerm.h" #include "VectorMatrixAssembler.h" namespace MeshLib @@ -74,13 +75,17 @@ public: const override final; void assemble(const double t, GlobalVector const& x, GlobalMatrix& M, - GlobalMatrix& K, GlobalVector& b) override final; + GlobalMatrix& K, GlobalVector& b, + StaggeredCouplingTerm const& coupled_term) + override final; void assembleWithJacobian(const double t, GlobalVector const& x, GlobalVector const& xdot, const double dxdot_dx, const double dx_dx, GlobalMatrix& M, GlobalMatrix& K, GlobalVector& b, - GlobalMatrix& Jac) override final; + GlobalMatrix& Jac, + StaggeredCouplingTerm const& coupled_term) + override final; std::vector<NumLib::IndexValueVector<GlobalIndexType>> const* getKnownSolutions(double const t) const override final diff --git a/ProcessLib/StaggeredCouplingTerm.cpp b/ProcessLib/StaggeredCouplingTerm.cpp new file mode 100644 index 00000000000..b588710308b --- /dev/null +++ b/ProcessLib/StaggeredCouplingTerm.cpp @@ -0,0 +1,26 @@ +/** + * \copyright + * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file StaggeredCouplingTerm.cpp + * + * Created on November 7, 2016, 12:14 PM + */ + +#include "StaggeredCouplingTerm.h" +#include "Process.h" + +namespace ProcessLib +{ +const StaggeredCouplingTerm createVoidStaggeredCouplingTerm() +{ + std::map<ProcessType, Process const&> coupled_pcsss; + std::map<ProcessType, GlobalVector const*> coupled_xs; + return StaggeredCouplingTerm(coupled_pcsss, coupled_xs); +} + +} // end of ProcessLib + diff --git a/ProcessLib/StaggeredCouplingTerm.h b/ProcessLib/StaggeredCouplingTerm.h new file mode 100644 index 00000000000..c7a36166d8e --- /dev/null +++ b/ProcessLib/StaggeredCouplingTerm.h @@ -0,0 +1,42 @@ +/** + * \copyright + * Copyright (c) 2012-2016, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + * \file StaggeredCouplingTerm.h + * + * Created on November 7, 2016, 12:14 PM + */ + +#ifndef OGS_STAGGERED_COUPLING_TERM_H +#define OGS_STAGGERED_COUPLING_TERM_H + +#include <map> + +#include "MathLib/LinAlg/GlobalMatrixVectorTypes.h" + +#include "ProcessType.h" + +namespace ProcessLib +{ +class Process; +struct StaggeredCouplingTerm +{ + StaggeredCouplingTerm( + std::map<ProcessType, Process const&> const& coupled_processes_, + std::map<ProcessType, GlobalVector const*> const& coupled_xs_) + : coupled_processes(coupled_processes_), coupled_xs(coupled_xs_) + { + } + + std::map<ProcessType, Process const&> const& coupled_processes; + std::map<ProcessType, GlobalVector const*> const& coupled_xs; +}; + +const StaggeredCouplingTerm createVoidStaggeredCouplingTerm(); + +} // end of ProcessLib +#endif /* OGS_STAGGERED_COUPLING_TERM_H */ + diff --git a/ProcessLib/UncoupledProcessesTimeLoop.cpp b/ProcessLib/UncoupledProcessesTimeLoop.cpp index 4640820555b..c7489c21488 100644 --- a/ProcessLib/UncoupledProcessesTimeLoop.cpp +++ b/ProcessLib/UncoupledProcessesTimeLoop.cpp @@ -15,6 +15,8 @@ #include "NumLib/ODESolver/ConvergenceCriterionPerComponent.h" #include "NumLib/TimeStepping/Algorithms/FixedTimeStepping.h" +#include "MathLib/LinAlg/LinAlg.h" + std::unique_ptr<NumLib::ITimeStepAlgorithm> createTimeStepper( BaseLib::ConfigTree const& config) { @@ -51,7 +53,6 @@ std::unique_ptr<ProcessLib::Output> createOutput( return ProcessLib::Output::newInstance(config, output_directory); } - //! Sets the EquationSystem for the given nonlinear solver, //! which is Picard or Newton depending on the NLTag. template <NumLib::NonlinearSolverTag NLTag> @@ -82,12 +83,10 @@ static void setEquationSystem(NumLib::NonlinearSolverBase& nonlinear_solver, switch (nl_tag) { case Tag::Picard: - setEquationSystem<Tag::Picard>(nonlinear_solver, eq_sys, - conv_crit); + setEquationSystem<Tag::Picard>(nonlinear_solver, eq_sys, conv_crit); break; case Tag::Newton: - setEquationSystem<Tag::Newton>(nonlinear_solver, eq_sys, - conv_crit); + setEquationSystem<Tag::Newton>(nonlinear_solver, eq_sys, conv_crit); break; } } @@ -133,6 +132,7 @@ struct SingleProcessData std::unique_ptr<NumLib::ConvergenceCriterion>&& conv_crit_, std::unique_ptr<NumLib::TimeDiscretization>&& time_disc_, Process& process_, + std::map<ProcessType, Process const&>&& coupled_processes_, ProcessOutput&& process_output_); SingleProcessData(SingleProcessData&& spd); @@ -150,6 +150,7 @@ struct SingleProcessData NumLib::InternalMatrixStorage* mat_strg = nullptr; Process& process; + std::map<ProcessType, Process const&> const coupled_processes; ProcessOutput process_output; }; @@ -159,12 +160,14 @@ SingleProcessData::SingleProcessData( std::unique_ptr<NumLib::ConvergenceCriterion>&& conv_crit_, std::unique_ptr<NumLib::TimeDiscretization>&& time_disc_, Process& process_, + std::map<ProcessType, Process const&>&& coupled_processes_, ProcessOutput&& process_output_) : nonlinear_solver_tag(NLTag), nonlinear_solver(nonlinear_solver), conv_crit(std::move(conv_crit_)), time_disc(std::move(time_disc_)), process(process_), + coupled_processes(coupled_processes_), process_output(std::move(process_output_)) { } @@ -177,6 +180,7 @@ SingleProcessData::SingleProcessData(SingleProcessData&& spd) tdisc_ode_sys(std::move(spd.tdisc_ode_sys)), mat_strg(spd.mat_strg), process(spd.process), + coupled_processes(spd.coupled_processes), process_output(std::move(spd.process_output)) { spd.mat_strg = nullptr; @@ -240,6 +244,7 @@ std::unique_ptr<SingleProcessData> makeSingleProcessData( Process& process, std::unique_ptr<NumLib::TimeDiscretization>&& time_disc, std::unique_ptr<NumLib::ConvergenceCriterion>&& conv_crit, + std::map<ProcessType, Process const&>&& coupled_processes, ProcessOutput&& process_output) { using Tag = NumLib::NonlinearSolverTag; @@ -250,7 +255,8 @@ std::unique_ptr<SingleProcessData> makeSingleProcessData( { return std::unique_ptr<SingleProcessData>{new SingleProcessData{ *nonlinear_solver_picard, std::move(conv_crit), - std::move(time_disc), process, std::move(process_output)}}; + std::move(time_disc), process, std::move(coupled_processes), + std::move(process_output)}}; } else if (auto* nonlinear_solver_newton = dynamic_cast<NumLib::NonlinearSolver<Tag::Newton>*>( @@ -258,16 +264,18 @@ std::unique_ptr<SingleProcessData> makeSingleProcessData( { return std::unique_ptr<SingleProcessData>{new SingleProcessData{ *nonlinear_solver_newton, std::move(conv_crit), - std::move(time_disc), process, std::move(process_output)}}; - } else { + std::move(time_disc), process, std::move(coupled_processes), + std::move(process_output)}}; + } + else + { OGS_FATAL("Encountered unknown nonlinear solver type. Aborting"); } } std::vector<std::unique_ptr<SingleProcessData>> createPerProcessData( BaseLib::ConfigTree const& config, - const std::map<std::string, std::unique_ptr<Process>>& - processes, + const std::map<std::string, std::unique_ptr<Process>>& processes, std::map<std::string, std::unique_ptr<NumLib::NonlinearSolverBase>> const& nonlinear_solvers) { @@ -297,12 +305,39 @@ std::vector<std::unique_ptr<SingleProcessData>> createPerProcessData( //! \ogs_file_param{prj__time_loop__processes__process__convergence_criterion} pcs_config.getConfigSubtree("convergence_criterion")); + auto const& cpl_pcses + //! \ogs_file_param{prj__time_loop__processes__process__coupled_processes} + = pcs_config.getConfigSubtreeOptional("coupled_processes"); + std::map<ProcessType, Process const&> coupled_processes; + if (cpl_pcses) + { + for ( + auto const cpl_pcs_name : + //! \ogs_file_param{prj__time_loop__processes__process__coupled_processes__coupled_process} + cpl_pcses->getConfigParameterList<std::string>( + "coupled_process")) + { + auto& cpl_pcs_ptr = BaseLib::getOrError( + processes, cpl_pcs_name, + "A process with the given name has not been defined."); + + auto const cpl_pcs = cpl_pcs_ptr.get(); + auto const inserted = coupled_processes.emplace( + cpl_pcs_ptr->getProcessType(), *cpl_pcs); + if (!inserted.second) + { // insertion failed, i.e., key already exists + OGS_FATAL("Coupled process `%s' already exists.", + cpl_pcs_name.data()); + } + } + } + //! \ogs_file_param{prj__time_loop__processes__process__output} ProcessOutput process_output{pcs_config.getConfigSubtree("output")}; per_process_data.emplace_back(makeSingleProcessData( nl_slv, pcs, std::move(time_disc), std::move(conv_crit), - std::move(process_output))); + std::move(coupled_processes), std::move(process_output))); } if (per_process_data.size() != processes.size()) @@ -315,11 +350,26 @@ std::vector<std::unique_ptr<SingleProcessData>> createPerProcessData( std::unique_ptr<UncoupledProcessesTimeLoop> createUncoupledProcessesTimeLoop( BaseLib::ConfigTree const& config, std::string const& output_directory, - const std::map<std::string, std::unique_ptr<Process>>& - processes, + const std::map<std::string, std::unique_ptr<Process>>& processes, const std::map<std::string, std::unique_ptr<NumLib::NonlinearSolverBase>>& nonlinear_solvers) { + auto const& coupling_config + //! \ogs_file_param{prj__time_loop__global_process_coupling} + = config.getConfigSubtreeOptional("global_process_coupling"); + + std::unique_ptr<NumLib::ConvergenceCriterion> coupling_conv_crit = nullptr; + unsigned max_coupling_iterations = 1; + if (coupling_config) + { + max_coupling_iterations + //! \ogs_file_param{prj__time_loop__global_process_coupling__max_iteration} + = coupling_config->getConfigParameter<unsigned>("max_iteration"); + coupling_conv_crit = NumLib::createConvergenceCriterion( + //! \ogs_file_param{prj__time_loop__global_process_coupling__max_iteration__convergence_criterion} + coupling_config->getConfigSubtree("convergence_criterion")); + } + auto timestepper = //! \ogs_file_param{prj__time_loop__time_stepping} createTimeStepper(config.getConfigSubtree("time_stepping")); @@ -333,9 +383,10 @@ std::unique_ptr<UncoupledProcessesTimeLoop> createUncoupledProcessesTimeLoop( config.getConfigSubtree("processes"), processes, nonlinear_solvers); return std::unique_ptr<UncoupledProcessesTimeLoop>{ - new UncoupledProcessesTimeLoop{std::move(timestepper), - std::move(output), - std::move(per_process_data)}}; + new UncoupledProcessesTimeLoop{ + std::move(timestepper), std::move(output), + std::move(per_process_data), max_coupling_iterations, + std::move(coupling_conv_crit)}}; } std::vector<GlobalVector*> setInitialConditions( @@ -371,7 +422,8 @@ std::vector<GlobalVector*> setInitialConditions( auto& conv_crit = *spd->conv_crit; setEquationSystem(nonlinear_solver, ode_sys, conv_crit, nl_tag); - nonlinear_solver.assemble(x0); + nonlinear_solver.assemble( + x0, ProcessLib::createVoidStaggeredCouplingTerm()); time_disc.pushState( t0, x0, mat_strg); // TODO: that might do duplicate work } @@ -385,6 +437,7 @@ std::vector<GlobalVector*> setInitialConditions( bool solveOneTimeStepOneProcess(GlobalVector& x, std::size_t const timestep, double const t, double const delta_t, SingleProcessData& process_data, + StaggeredCouplingTerm const& coupled_term, Output const& output_control) { auto& process = process_data.process; @@ -405,35 +458,90 @@ bool solveOneTimeStepOneProcess(GlobalVector& x, std::size_t const timestep, applyKnownSolutions(ode_sys, nl_tag, x); - process.preTimestep(x, t, delta_t); - - auto const post_iteration_callback = [&]( - unsigned iteration, GlobalVector const& x) { + auto const post_iteration_callback = [&](unsigned iteration, + GlobalVector const& x) { output_control.doOutputNonlinearIteration( process, process_data.process_output, timestep, t, x, iteration); }; bool nonlinear_solver_succeeded = - nonlinear_solver.solve(x, post_iteration_callback); + nonlinear_solver.solve(x, coupled_term, post_iteration_callback); auto& mat_strg = *process_data.mat_strg; time_disc.pushState(t, x, mat_strg); - process.postTimestep(x); - return nonlinear_solver_succeeded; } UncoupledProcessesTimeLoop::UncoupledProcessesTimeLoop( std::unique_ptr<NumLib::ITimeStepAlgorithm>&& timestepper, std::unique_ptr<Output>&& output, - std::vector<std::unique_ptr<SingleProcessData>>&& per_process_data) + std::vector<std::unique_ptr<SingleProcessData>>&& per_process_data, + const unsigned global_coupling_max_iterations, + std::unique_ptr<NumLib::ConvergenceCriterion>&& glb_coupling_conv_crit) : _timestepper{std::move(timestepper)}, _output(std::move(output)), - _per_process_data(std::move(per_process_data)) + _per_process_data(std::move(per_process_data)), + _global_coupling_max_iterations(global_coupling_max_iterations), + _global_coupling_conv_crit(std::move(glb_coupling_conv_crit)) { } +bool UncoupledProcessesTimeLoop::setCoupledSolutions() +{ + if (!_global_coupling_conv_crit && _global_coupling_max_iterations == 1) + return false; + + unsigned pcs_idx = 0; + _solutions_of_coupled_processes.resize(_per_process_data.size()); + for (auto& spd : _per_process_data) + { + BaseLib::RunTime time_timestep_process; + time_timestep_process.start(); + + auto const& coupled_processes = spd->coupled_processes; + std::map<ProcessType, GlobalVector const*> coupled_xs; + auto it = coupled_processes.begin(); + while (it != coupled_processes.end()) + { + ProcessLib::Process const& coupled_process = it->second; + auto const found_item = std::find_if( + _per_process_data.begin(), + _per_process_data.end(), + [&coupled_process]( + std::unique_ptr<SingleProcessData> const& item) { + return coupled_process.getProcessType() == + item->process.getProcessType(); + }); + + if (found_item != _per_process_data.end()) + { + // Id of the coupled process: + const std::size_t c_id = + found_item - _per_process_data.begin(); + + BaseLib::insertMapIfKeyUniqueElseError(coupled_xs, it->first, + _process_solutions[c_id], + "global_coupled_x"); + } + it++; + } + _solutions_of_coupled_processes[pcs_idx] = coupled_xs; + + const auto x = _process_solutions[pcs_idx]; + + auto x1 = &NumLib::GlobalVectorProvider::provider.getVector(*x); + MathLib::LinAlg::copy(*x, *x1); + + // append a solution vector of suitable size + _solutions_of_last_cpl_iteration.emplace_back(x1); + + ++pcs_idx; + } + + return true; +} + bool UncoupledProcessesTimeLoop::loop() { // initialize output, convergence criterion, etc. @@ -448,7 +556,8 @@ bool UncoupledProcessesTimeLoop::loop() if (auto* conv_crit = dynamic_cast<NumLib::ConvergenceCriterionPerComponent*>( - spd->conv_crit.get())) { + spd->conv_crit.get())) + { conv_crit->setDOFTable(pcs.getDOFTable(), pcs.getMesh()); } @@ -475,6 +584,8 @@ bool UncoupledProcessesTimeLoop::loop() } } + const bool is_staggered_coupling = setCoupledSolutions(); + double t = t0; std::size_t timestep = 1; // the first timestep really is number one bool nonlinear_solver_succeeded = true; @@ -492,41 +603,109 @@ bool UncoupledProcessesTimeLoop::loop() INFO("=== timestep #%u (t=%gs, dt=%gs) ==============================", timestep, t, delta_t); - // TODO use process name - unsigned pcs_idx = 0; - for (auto& spd : _per_process_data) + bool coupling_iteration_converged = true; + for (unsigned i = 0; i < _global_coupling_max_iterations; i++) { - auto& pcs = spd->process; - BaseLib::RunTime time_timestep_process; - time_timestep_process.start(); - - auto& x = *_process_solutions[pcs_idx]; - - nonlinear_solver_succeeded = solveOneTimeStepOneProcess( - x, timestep, t, delta_t, *spd, *_output); - - pcs.computeSecondaryVariable(t, x); + // TODO use process name + unsigned pcs_idx = 0; + for (auto& spd : _per_process_data) + { + auto& pcs = spd->process; + BaseLib::RunTime time_timestep_process; + time_timestep_process.start(); + + auto& x = *_process_solutions[pcs_idx]; + if (i==0) + pcs.preTimestep(x, t, delta_t); + + if (is_staggered_coupling) + { + if (i == 0) + _global_coupling_conv_crit->preFirstIteration(); + else + _global_coupling_conv_crit->setNoFirstIteration(); + StaggeredCouplingTerm coupled_term( + spd->coupled_processes, + _solutions_of_coupled_processes[pcs_idx]); + + nonlinear_solver_succeeded = solveOneTimeStepOneProcess( + x, timestep, t, delta_t, *spd, coupled_term, *_output); + } + else + { + nonlinear_solver_succeeded = solveOneTimeStepOneProcess( + x, timestep, t, delta_t, *spd, + ProcessLib::createVoidStaggeredCouplingTerm(), + *_output); + pcs.postTimestep(x); + } + + pcs.computeSecondaryVariable(t, x); + + INFO( + "[time] Solving process #%u took %g s in timestep #%u " + "uncouling iteration #%u", + pcs_idx, time_timestep.elapsed(), timestep, i); + + if (!nonlinear_solver_succeeded) + { + ERR("The nonlinear solver failed in timestep #%u at t = %g " + "s" + " for process #%u.", + timestep, t, pcs_idx); + + // save unsuccessful solution + _output->doOutputAlways(pcs, spd->process_output, timestep, + t, x); + + break; + } + else + { + if (!is_staggered_coupling) + _output->doOutput(pcs, spd->process_output, timestep, t, + x); + } + + // Check the convergence of the coupling iteration + if (is_staggered_coupling) + { + auto& x_old = *_solutions_of_last_cpl_iteration[pcs_idx]; + MathLib::LinAlg::axpy(x_old, -1.0, x); + _global_coupling_conv_crit->checkResidual(x_old); + coupling_iteration_converged = + coupling_iteration_converged && + _global_coupling_conv_crit->isSatisfied(); + + if (coupling_iteration_converged) + break; + MathLib::LinAlg::copy(x, x_old); + } + + ++pcs_idx; + } - INFO("[time] Solving process #%u took %g s in timestep #%u.", - pcs_idx, time_timestep.elapsed(), timestep); + if (is_staggered_coupling && coupling_iteration_converged) + break; if (!nonlinear_solver_succeeded) { - ERR("The nonlinear solver failed in timestep #%u at t = %g s" - " for process #%u.", - timestep, t, pcs_idx); - - // save unsuccessful solution - _output->doOutputAlways(pcs, spd->process_output, timestep, t, x); - break; } - else + } + + if (is_staggered_coupling && coupling_iteration_converged) + { + unsigned pcs_idx = 0; + for (auto& spd : _per_process_data) { + auto& pcs = spd->process; + auto& x = *_process_solutions[pcs_idx]; + pcs.postTimestep(x); + _output->doOutput(pcs, spd->process_output, timestep, t, x); + ++pcs_idx; } - - ++pcs_idx; } INFO("[time] Timestep #%u took %g s.", timestep, @@ -544,7 +723,8 @@ bool UncoupledProcessesTimeLoop::loop() { auto& pcs = spd->process; auto const& x = *_process_solutions[pcs_idx]; - _output->doOutputLastTimestep(pcs, spd->process_output, timestep, t, x); + _output->doOutputLastTimestep(pcs, spd->process_output, timestep, t, + x); ++pcs_idx; } @@ -557,6 +737,9 @@ UncoupledProcessesTimeLoop::~UncoupledProcessesTimeLoop() { for (auto* x : _process_solutions) NumLib::GlobalVectorProvider::provider.releaseVector(*x); + + for (auto* x : _solutions_of_last_cpl_iteration) + NumLib::GlobalVectorProvider::provider.releaseVector(*x); } } // namespace ProcessLib diff --git a/ProcessLib/UncoupledProcessesTimeLoop.h b/ProcessLib/UncoupledProcessesTimeLoop.h index 3d6f74dde46..ef829722aeb 100644 --- a/ProcessLib/UncoupledProcessesTimeLoop.h +++ b/ProcessLib/UncoupledProcessesTimeLoop.h @@ -18,6 +18,11 @@ #include "Output.h" #include "Process.h" +namespace NumLib +{ +class ConvergenceCriterion; +} + namespace ProcessLib { struct SingleProcessData; @@ -29,17 +34,30 @@ public: explicit UncoupledProcessesTimeLoop( std::unique_ptr<NumLib::ITimeStepAlgorithm>&& timestepper, std::unique_ptr<Output>&& output, - std::vector<std::unique_ptr<SingleProcessData>>&& per_process_data); + std::vector<std::unique_ptr<SingleProcessData>>&& per_process_data, + const unsigned global_coupling_max_iterations, + std::unique_ptr<NumLib::ConvergenceCriterion>&& glb_coupling_conv_crit); bool loop(); ~UncoupledProcessesTimeLoop(); + bool setCoupledSolutions(); + private: std::vector<GlobalVector*> _process_solutions; std::unique_ptr<NumLib::ITimeStepAlgorithm> _timestepper; std::unique_ptr<Output> _output; std::vector<std::unique_ptr<SingleProcessData>> _per_process_data; + + /// Maximum iterations of the global coupling. + const unsigned _global_coupling_max_iterations; + /// Convergence criteria of the global coupling iterations. + std::unique_ptr<NumLib::ConvergenceCriterion> _global_coupling_conv_crit; + std::vector<std::map<ProcessType, GlobalVector const*>> + _solutions_of_coupled_processes; + /// Solutions of the previous coupling iteration. + std::vector<GlobalVector*> _solutions_of_last_cpl_iteration; }; //! Builds an UncoupledProcessesTimeLoop from the given configuration. diff --git a/Tests/NumLib/ODEs.h b/Tests/NumLib/ODEs.h index 848b62cda93..da01e8c8c06 100644 --- a/Tests/NumLib/ODEs.h +++ b/Tests/NumLib/ODEs.h @@ -13,6 +13,7 @@ #include "MathLib/LinAlg/LinAlg.h" #include "MathLib/LinAlg/UnifiedMatrixSetters.h" #include "NumLib/ODESolver/ODESystem.h" +#include "ProcessLib/StaggeredCouplingTerm.h" // debug //#include <iostream> @@ -27,7 +28,9 @@ class ODE1 final : public NumLib::ODESystem< { public: void assemble(const double /*t*/, GlobalVector const& /*x*/, - GlobalMatrix& M, GlobalMatrix& K, GlobalVector& b) override + GlobalMatrix& M, GlobalMatrix& K, GlobalVector& b, + ProcessLib::StaggeredCouplingTerm const& /*coupled_term*/ + ) override { MathLib::setMatrix(M, { 1.0, 0.0, 0.0, 1.0 }); MathLib::setMatrix(K, { 0.0, 1.0, -1.0, 0.0 }); @@ -39,11 +42,13 @@ public: GlobalVector const& /*xdot*/, const double dxdot_dx, const double dx_dx, GlobalMatrix& M, GlobalMatrix& K, GlobalVector& b, - GlobalMatrix& Jac) override + GlobalMatrix& Jac, + ProcessLib::StaggeredCouplingTerm + const& coupled_term) override { namespace LinAlg = MathLib::LinAlg; - assemble(t, x_curr, M, K, b); + assemble(t, x_curr, M, K, b, coupled_term); // compute Jac = M*dxdot_dx + dx_dx*K LinAlg::finalizeAssembly(M); @@ -103,7 +108,9 @@ class ODE2 final : public NumLib::ODESystem< { public: void assemble(const double /*t*/, GlobalVector const& x, GlobalMatrix& M, - GlobalMatrix& K, GlobalVector& b) override + GlobalMatrix& K, GlobalVector& b, + ProcessLib::StaggeredCouplingTerm const& /*coupled_term*/ + ) override { MathLib::setMatrix(M, {1.0}); MathLib::setMatrix(K, {x[0]}); @@ -114,9 +121,11 @@ public: GlobalVector const& /*xdot*/, const double dxdot_dx, const double dx_dx, GlobalMatrix& M, GlobalMatrix& K, GlobalVector& b, - GlobalMatrix& Jac) override + GlobalMatrix& Jac, + ProcessLib::StaggeredCouplingTerm const& + coupled_term) override { - assemble(t, x, M, K, b); + assemble(t, x, M, K, b, coupled_term); namespace LinAlg = MathLib::LinAlg; @@ -187,7 +196,9 @@ class ODE3 final : public NumLib::ODESystem< { public: void assemble(const double /*t*/, GlobalVector const& x_curr, GlobalMatrix& M, - GlobalMatrix& K, GlobalVector& b) override + GlobalMatrix& K, GlobalVector& b, + ProcessLib::StaggeredCouplingTerm const& /*coupled_term*/ + ) override { auto const u = x_curr[0]; auto const v = x_curr[1]; @@ -202,9 +213,11 @@ public: GlobalVector const& xdot, const double dxdot_dx, const double dx_dx, GlobalMatrix& M, GlobalMatrix& K, GlobalVector& b, - GlobalMatrix& Jac) override + GlobalMatrix& Jac, + ProcessLib::StaggeredCouplingTerm const& + coupled_term) override { - assemble(t, x_curr, M, K, b); + assemble(t, x_curr, M, K, b, coupled_term); auto const u = x_curr[0]; auto const v = x_curr[1]; diff --git a/Tests/NumLib/TimeLoopSingleODE.h b/Tests/NumLib/TimeLoopSingleODE.h index 1c67bfe6edc..531ef5432da 100644 --- a/Tests/NumLib/TimeLoopSingleODE.h +++ b/Tests/NumLib/TimeLoopSingleODE.h @@ -13,6 +13,7 @@ #include "NumLib/ODESolver/TimeDiscretizedODESystem.h" #include "NumLib/ODESolver/NonlinearSolver.h" +#include "ProcessLib/StaggeredCouplingTerm.h" namespace NumLib { @@ -96,7 +97,8 @@ bool TimeLoopSingleODE<NLTag>::loop(const double t0, GlobalVector const& x0, if (time_disc.needsPreload()) { - _nonlinear_solver->assemble(x); + _nonlinear_solver->assemble(x, + ProcessLib::createVoidStaggeredCouplingTerm()); time_disc.pushState(t0, x0, _ode_sys); // TODO: that might do duplicate work } @@ -112,7 +114,9 @@ bool TimeLoopSingleODE<NLTag>::loop(const double t0, GlobalVector const& x0, // INFO("time: %e, delta_t: %e", t, delta_t); time_disc.nextTimestep(t, delta_t); - nl_slv_succeeded = _nonlinear_solver->solve(x, nullptr); + nl_slv_succeeded = _nonlinear_solver->solve(x, + ProcessLib::createVoidStaggeredCouplingTerm(), + nullptr); if (!nl_slv_succeeded) break; -- GitLab