diff --git a/Documentation/ProjectFile/prj/time_loop/processes/process/t_compensate_initial_out_of_balance_forces.md b/Documentation/ProjectFile/prj/time_loop/processes/process/t_compensate_initial_out_of_balance_forces.md new file mode 100644 index 0000000000000000000000000000000000000000..96a72b7a0b46e99d4abc8229254c406f4363ca1b --- /dev/null +++ b/Documentation/ProjectFile/prj/time_loop/processes/process/t_compensate_initial_out_of_balance_forces.md @@ -0,0 +1 @@ +\copydoc NumLib::NonlinearSolver<NonlinearSolverTag::Newton>::_compensate_initial_out_of_balance_forces diff --git a/NumLib/ODESolver/NonlinearSolver.cpp b/NumLib/ODESolver/NonlinearSolver.cpp index c3216b1cc56db1714c0b3ab62148e3a73840d122..bf258e3d263e93eb4059f5a098fd5f3916121633 100644 --- a/NumLib/ODESolver/NonlinearSolver.cpp +++ b/NumLib/ODESolver/NonlinearSolver.cpp @@ -191,6 +191,17 @@ void NonlinearSolver<NonlinearSolverTag::Newton>::assemble( // equation every time and could not forget it. } +void NonlinearSolver<NonlinearSolverTag::Newton>::calculateOutOfBalanceForces( + std::vector<GlobalVector*> const& x, int const process_id) +{ + if (!_compensate_initial_out_of_balance_forces) + return; + + _equation_system->assemble(x, process_id); + _f_oob = &NumLib::GlobalVectorProvider::provider.getVector(); + _equation_system->getResidual(*x[process_id], *_f_oob); +} + NonlinearSolverStatus NonlinearSolver<NonlinearSolverTag::Newton>::solve( std::vector<GlobalVector*>& x, std::function<void(int, GlobalVector const&)> const& postIterationCallback, @@ -239,6 +250,10 @@ NonlinearSolverStatus NonlinearSolver<NonlinearSolverTag::Newton>::solve( sys.getJacobian(J); INFO("[time] Assembly took %g s.", time_assembly.elapsed()); + // Apply out-of-balance forces if set. + if (_f_oob != nullptr) + LinAlg::axpy(res, -1, *_f_oob); + minus_delta_x.setZero(); timer_dirichlet.start(); diff --git a/NumLib/ODESolver/NonlinearSolver.h b/NumLib/ODESolver/NonlinearSolver.h index bcffe0a4a476e2cbd6a2b108d461396ec2847c57..04c5815372afd60dc334dfca4541cd5f74718c9a 100644 --- a/NumLib/ODESolver/NonlinearSolver.h +++ b/NumLib/ODESolver/NonlinearSolver.h @@ -49,6 +49,9 @@ public: virtual void assemble(std::vector<GlobalVector*> const& x, int const process_id) const = 0; + virtual void calculateOutOfBalanceForces( + std::vector<GlobalVector*> const& x, int const process_id) = 0; + /*! Assemble and solve the equation system. * * \param x in: the initial guess, out: the solution. @@ -113,12 +116,20 @@ public: void assemble(std::vector<GlobalVector*> const& x, int const process_id) const override; + void calculateOutOfBalanceForces(std::vector<GlobalVector*> const& x, + int const process_id) override; + NonlinearSolverStatus solve( std::vector<GlobalVector*>& x, std::function<void(int, GlobalVector const&)> const& postIterationCallback, int const process_id) override; + void compensateInitialOutOfBalanceForces(bool const value) + { + _compensate_initial_out_of_balance_forces = value; + } + private: GlobalLinearSolver& _linear_solver; System* _equation_system = nullptr; @@ -133,11 +144,18 @@ private: //! conservative approach. double const _damping; + GlobalVector* _f_oob = nullptr; //!< Out-of-balance forces vector. std::size_t _res_id = 0u; //!< ID of the residual vector. std::size_t _J_id = 0u; //!< ID of the Jacobian matrix. std::size_t _minus_delta_x_id = 0u; //!< ID of the \f$ -\Delta x\f$ vector. std::size_t _x_new_id = 0u; //!< ID of the vector storing \f$ x - (-\Delta x) \f$. + + /// Enables computation of the out-of-balance forces \f$ F_{\rm oob} \f$ + /// before the first time step. The forces are zero if the external forces + /// are in equilibrium with the initial state/initial conditions. During the + /// simulation the new residuum reads \f$ \tilde r = r - F_{\rm oob} \f$. + bool _compensate_initial_out_of_balance_forces = false; }; /*! Find a solution to a nonlinear equation using the Picard fixpoint iteration @@ -175,12 +193,25 @@ public: void assemble(std::vector<GlobalVector*> const& x, int const process_id) const override; + void calculateOutOfBalanceForces(GlobalVector const& /*x*/) override + { + if (!_compensate_initial_out_of_balance_forces) + return; + + OGS_FATAL("Calculation of out-of-balance forces is not implemented."); + } + NonlinearSolverStatus solve( std::vector<GlobalVector*>& x, std::function<void(int, GlobalVector const&)> const& postIterationCallback, int const process_id) override; + void compensateInitialOutOfBalanceForces(bool const value) + { + _compensate_initial_out_of_balance_forces = value; + } + private: GlobalLinearSolver& _linear_solver; System* _equation_system = nullptr; @@ -193,6 +224,10 @@ private: std::size_t _rhs_id = 0u; //!< ID of the right-hand side vector. std::size_t _x_new_id = 0u; //!< ID of the vector storing the solution of //! the linearized equation. + + /// \copydoc + /// NumLib::NonlinearSolver<NonlinearSolverTag::Newton>::_compensate_initial_out_of_balance_forces + bool _compensate_initial_out_of_balance_forces = false; }; /*! Creates a new nonlinear solver from the given configuration. diff --git a/ProcessLib/CreateProcessData.cpp b/ProcessLib/CreateProcessData.cpp index 4f46cae448f2593ebcf75a016966015269dabdbe..0c7955c5198b11fc056610e50ec5829ea5e30a52 100644 --- a/ProcessLib/CreateProcessData.cpp +++ b/ProcessLib/CreateProcessData.cpp @@ -23,7 +23,8 @@ static std::unique_ptr<ProcessData> makeProcessData( int const process_id, Process& process, std::unique_ptr<NumLib::TimeDiscretization>&& time_disc, - std::unique_ptr<NumLib::ConvergenceCriterion>&& conv_crit) + std::unique_ptr<NumLib::ConvergenceCriterion>&& conv_crit, + bool const compensate_initial_out_of_balance_forces) { using Tag = NumLib::NonlinearSolverTag; @@ -31,6 +32,8 @@ static std::unique_ptr<ProcessData> makeProcessData( dynamic_cast<NumLib::NonlinearSolver<Tag::Picard>*>( &nonlinear_solver)) { + nonlinear_solver_picard->compensateInitialOutOfBalanceForces( + compensate_initial_out_of_balance_forces); return std::make_unique<ProcessData>( std::move(timestepper), *nonlinear_solver_picard, std::move(conv_crit), std::move(time_disc), process_id, process); @@ -39,6 +42,8 @@ static std::unique_ptr<ProcessData> makeProcessData( dynamic_cast<NumLib::NonlinearSolver<Tag::Newton>*>( &nonlinear_solver)) { + nonlinear_solver_newton->compensateInitialOutOfBalanceForces( + compensate_initial_out_of_balance_forces); return std::make_unique<ProcessData>( std::move(timestepper), *nonlinear_solver_newton, std::move(conv_crit), std::move(time_disc), process_id, process); @@ -87,6 +92,11 @@ std::vector<std::unique_ptr<ProcessData>> createPerProcessData( //! \ogs_file_param{prj__time_loop__processes__process__convergence_criterion} pcs_config.getConfigSubtree("convergence_criterion")); + auto const compensate_initial_out_of_balance_forces = + //! \ogs_file_param{prj__time_loop__processes__process__compensate_initial_out_of_balance_forces} + pcs_config.getConfigParameter<bool>( + "compensate_initial_out_of_balance_forces", false); + //! \ogs_file_param{prj__time_loop__processes__process__output} auto output = pcs_config.getConfigSubtreeOptional("output"); if (output) @@ -103,7 +113,8 @@ std::vector<std::unique_ptr<ProcessData>> createPerProcessData( per_process_data.emplace_back( makeProcessData(std::move(timestepper), nl_slv, process_id, pcs, - std::move(time_disc), std::move(conv_crit))); + std::move(time_disc), std::move(conv_crit), + compensate_initial_out_of_balance_forces)); ++process_id; } diff --git a/ProcessLib/TimeLoop.cpp b/ProcessLib/TimeLoop.cpp index e26104074fa8ed8264243d2668e216cc291ab6f5..af939682793e5554f421ef6ac39f376220bf5b2f 100644 --- a/ProcessLib/TimeLoop.cpp +++ b/ProcessLib/TimeLoop.cpp @@ -167,6 +167,31 @@ std::vector<GlobalVector*> setInitialConditions( return process_solutions; } +void calculateOutOfBalanceForces( + std::vector<std::unique_ptr<ProcessData>> const& per_process_data, + std::vector<GlobalVector*> process_solutions) +{ + INFO("Calculate out-of-balance forces."); + for (auto& process_data : per_process_data) + { + auto& ode_sys = *process_data->tdisc_ode_sys; + + auto& time_disc = *process_data->time_disc; + auto& nonlinear_solver = process_data->nonlinear_solver; + auto& conv_crit = *process_data->conv_crit; + + auto const nl_tag = process_data->nonlinear_solver_tag; + setEquationSystem(nonlinear_solver, ode_sys, conv_crit, nl_tag); + // dummy values to handle the time derivative terms more or less + // correctly, i.e. to ignore them. + double const t = 0; + double const dt = 1; + time_disc.nextTimestep(t, dt); + nonlinear_solver.calculateOutOfBalanceForces(process_solutions, + process_data->process_id); + } +} + NumLib::NonlinearSolverStatus solveOneTimeStepOneProcess( std::vector<GlobalVector*>& x, std::size_t const timestep, double const t, double const delta_t, ProcessData const& process_data, @@ -445,6 +470,7 @@ bool TimeLoop::loop() bool const is_staggered_coupling = !isMonolithicProcess(*_per_process_data[0]); + bool out_of_balance_forces_computed = false; double t = _start_time; std::size_t accepted_steps = 0; std::size_t rejected_steps = 0; @@ -472,6 +498,12 @@ bool TimeLoop::loop() t, process_data->process_id); } + if (!out_of_balance_forces_computed) + { + calculateOutOfBalanceForces(_per_process_data, _process_solutions); + out_of_balance_forces_computed = true; + } + if (is_staggered_coupling) { nonlinear_solver_status =