21#ifndef B2TYPED_SOLVER_H_
22#define B2TYPED_SOLVER_H_
28#include "b2ppconfig.h"
30#include "io/b2000_db/b2000_db_v3/b2solution_database.H"
33#include "model_imp/b2monolithic_boundary_conditions.H"
34#include "time_integration/b2time_integrator.H"
37#include "utils/b2timing.H"
40#include "utils/b2threading.H"
43namespace b2000::solver {
45template <
typename T,
typename MATRIX_FORMAT>
46class TypedSolver :
public Solver {
49 using EssentialBoundaryConditionPointer =
50 std::unique_ptr<MonolithicEssentialBoundaryConditions<T>>;
51 using NaturalBoundaryConditionPointer =
52 std::unique_ptr<MonolithicNaturalBoundaryConditions<T, MATRIX_FORMAT>>;
53 using type_t = ObjectTypeIncomplete<TypedSolver, Solver::type_t>;
56 using type_scalar = T;
57 using type_matrix = MATRIX_FORMAT;
59 void solve()
override = 0;
61 const auto& GetDof()
const {
return dof_; }
63 const auto& GetKeff()
const {
return K_eff_; }
65 const auto& GetFeff()
const {
return F_eff_; }
67 auto GetTimeIntegrator()
const {
return time_integrator_; }
77 assemble_effective_system,
78 assemble_effective_matrix,
79 assemble_effective_vector,
82 update_internal_variables,
89 virtual void InitializeSolverOnCase()
override;
91 bool solve_iteration()
override = 0;
95 void MainLoopOverElements(
const b2Task task);
98 void SetSolutionFieldsToZero();
101 void SolveEffectiveSystem();
112 b2linalg::Matrix<T, b2linalg::Mrectangle> dof_{};
120 b2linalg::Matrix<T, b2linalg::Mrectangle> dU_{};
122 b2linalg::Matrix<T, typename MATRIX_FORMAT::sparse_inversible>
124 b2linalg::Vector<T, b2linalg::Vdense> F_eff_{};
126 EssentialBoundaryConditionPointer essential_boundary_conditions_{};
127 NaturalBoundaryConditionPointer natural_boundary_conditions_{};
131 std::shared_ptr<TimeIntegrator<T>> time_integrator_{};
132 std::shared_ptr<TimeIntegrator<T>> corrector_{};
138 std::vector<int> fixed_dof_{};
140 T reduced_scalar_value{};
143 SolutionMonolithic<T>* solution_{
nullptr};
147 enum class NewtonMethod { conventional, modified, delayed_modified, modified_postponed };
157 void IterateOverElements(
auto& elements,
auto func,
auto timer, std::string_view log);
169 void SeriallyIterateOverElements(
auto& elements,
auto func,
auto timer, std::string_view log);
183 void IterateOverElementsParallelReduction(
184 auto& elements,
auto func,
auto reduction, T identity,
auto timer, std::string_view log);
191 void AllocateAndAssembleEffectiveSystem(std::vector<std::reference_wrapper<Element>>& elements);
196 void AssembleEffectiveSystem(TypedElement<T>& element);
200 void AssembleEffectiveMatrix(TypedElement<T>& element);
204 void AssembleEffectiveVector(TypedElement<T>& element);
209 void ComputeGradients(TypedElement<T>& element, GradientContainer*
const gradient_container);
216 std::vector<std::reference_wrapper<Element>>& elements,
auto timer, std::string_view log);
219template <
typename T,
typename MATRIX_FORMAT>
220void TypedSolver<T, MATRIX_FORMAT>::InitializeSolverOnCase() {
235 solution_ = &
dynamic_cast<SolutionMonolithic<T>&
>(model_->get_solution());
240 essential_boundary_conditions_ = std::make_unique<MonolithicEssentialBoundaryConditions<T>>();
241 natural_boundary_conditions_ =
242 std::make_unique<MonolithicNaturalBoundaryConditions<T, MATRIX_FORMAT>>();
245 number_of_fixed_dof_ =
246 essential_boundary_conditions_->InitializeEssentialBoundaryConditions(*model_);
247 natural_boundary_conditions_->InitializeNaturalBoundaryConditions(*model_, *case_);
261 effective_num_dof_ = total_num_dof_ - number_of_fixed_dof_;
264 fixed_dof_.resize(total_num_dof_);
265 std::iota(fixed_dof_.begin(), fixed_dof_.end(), 0);
276 F_eff_.reset(effective_num_dof_);
278 dof_.resize(total_num_dof_, columns);
279 dU_.resize(total_num_dof_, 2);
287template <
typename T,
typename MATRIX_FORMAT>
288void TypedSolver<T, MATRIX_FORMAT>::IterateOverElements(
289 auto& elements,
auto func,
auto timer, std::string_view log) {
295 (*timer).second.start();
301 tbb::parallel_for(
static_cast<size_t>(0), elements.size(), func);
307 for (
size_t i{}; i < elements.size(); ++i) { func(i); }
309 (*timer).second.stop();
314template <
typename T,
typename MATRIX_FORMAT>
315void TypedSolver<T, MATRIX_FORMAT>::SeriallyIterateOverElements(
316 auto& elements,
auto func,
auto timer, std::string_view log) {
319 logger <<
logging::info <<
"Serially Starting to " <<
log << logging::LOGFLUSH;
321 (*timer).second.start();
322 for (
size_t i{}; i < elements.size(); ++i) { func(i); }
323 (*timer).second.stop();
325 logger <<
logging::info <<
"Serially Finished to " <<
log << logging::LOGFLUSH;
329template <
typename T,
typename MATRIX_FORMAT>
330void TypedSolver<T, MATRIX_FORMAT>::IterateOverElementsParallelReduction(
331 auto& elements,
auto func,
auto reduction, T identity,
auto timer, std::string_view log) {
338 (*timer).second.start();
339 reduced_scalar_value = tbb::parallel_reduce(
340 tbb::blocked_range<size_t>(0, elements.size()), identity, func, reduction);
341 (*timer).second.stop();
347template <
typename T,
typename MATRIX_FORMAT>
348void TypedSolver<T, MATRIX_FORMAT>::MainLoopOverElements(
const b2Task task) {
353 auto& elements = model_->get_domain().GetElementContainer();
375 case (b2Task::assemble_mass_matrix): {
376 auto time_assemble_mass_mat =
obtain_timer(
"solver_assemble_mass_mat");
381 [[likely]]
case (b2Task::assemble_effective_system):
383 if (K_eff_.size1() == 0) {
384 AllocateAndAssembleEffectiveSystem(elements);
389 [
this, &elements](
const size_t i) {
390 AssembleEffectiveSystem(
dynamic_cast<TypedElement<T>&
>(elements[i].get()));
392 obtain_timer(
"solver_assemble_eff_sys"),
"assemble the effective system");
398 case (b2Task::assemble_effective_matrix):
401 [
this, &elements](
const size_t i) {
402 AssembleEffectiveMatrix(
dynamic_cast<TypedElement<T>&
>(elements[i].get()));
404 obtain_timer(
"solver_assemble_eff_mat"),
"assemble the effective matrix");
408 case (b2Task::assemble_effective_vector):
411 [
this, &elements](
const size_t i) {
412 AssembleEffectiveVector(
dynamic_cast<TypedElement<T>&
>(elements[i].get()));
414 obtain_timer(
"solver_assemble_eff_vec"),
"assemble the effective vector");
418 case (b2Task::compute_gradients):
421 auto container = solution_->get_gradient_container();
422 GradientContainer* gradients = container.get();
426 [
this, &elements, gradients](
const size_t i) {
428 dynamic_cast<TypedElement<T>&
>(elements[i].get()), gradients);
430 obtain_timer(
"compute_gradients"),
"compute the gradients");
435 case (b2Task::compute_J_integral): {
440 auto time_comp_j_integral =
obtain_timer(
"solver_comp_j_integral");
445 case (b2Task::update_internal_variables): {
450 auto time_update_inter_var =
obtain_timer(
"solver_update_inter_var");
455 case (b2Task::compute_error):
456 ComputeError(elements,
obtain_timer(
"solver_comp_err"),
"update the error");
465template <
typename T,
typename MATRIX_FORMAT>
466void TypedSolver<T, MATRIX_FORMAT>::AllocateAndAssembleEffectiveSystem(
467 std::vector<std::reference_wrapper<Element>>& elements) {
469 K_eff_.resize(effective_num_dof_, model_->get_domain().get_dof_connectivity_type(), *case_);
472 using my_mutex_t = tbb::spin_mutex;
473 std::vector<my_mutex_t> row_blocker(effective_num_dof_);
476 std::vector<std::map<size_t, T>> row_contributions(effective_num_dof_);
481 auto ElementLoop = [&](
size_t i) {
482 auto& element{
dynamic_cast<TypedElement<T>&
>(elements[i].get())};
485 b2linalg::Matrix<T, typename MATRIX_FORMAT::dense> k_eff;
486 b2linalg::Vector<T, b2linalg::Vdense> f_eff;
487 b2linalg::Index index;
489 element.AssembleElementEffectiveSystem(*
this, k_eff, f_eff, index);
495 size_t N{index.size()};
497 int global_index_i{};
498 int global_index_j{};
499 for (
size_t i{}; i < N; ++i) {
500 global_index_i = fixed_dof_[index[i]];
503 if (global_index_i < 0) {
continue; }
510 for (
size_t j{}; j < N; ++j) {
511 global_index_j = fixed_dof_[index[j]];
513 if (global_index_j < 0) {
515 if (k_eff(i, j) != T{}) { f_eff[i] -= k_eff(i, j) * dU_(index[j], 0); }
517 if constexpr (std::is_same_v<MATRIX_FORMAT, b2linalg::Msymmetric>) {
518 if (j < i) {
continue; }
521 if (global_index_j < global_index_i) {
522 std::swap(global_index_i, global_index_j);
528 my_mutex_t::scoped_lock row_lock{row_blocker[global_index_i]};
530 row_contributions[global_index_i][global_index_j] += k_eff(i, j);
533 if constexpr (std::is_same_v<MATRIX_FORMAT, b2linalg::Msymmetric>) {
535 std::swap(global_index_i, global_index_j);
543 std::atomic_ref<T> atomic_T{F_eff_[global_index_i]};
544 atomic_T += f_eff[i];
546 F_eff_[global_index_i] += f_eff[i];
552 logger <<
logging::info <<
"Starting to allocate the system matrix" << logging::LOGFLUSH;
553 (*timer).second.start();
555 tbb::parallel_for(
static_cast<size_t>(0), elements.size(), ElementLoop);
557 for (
size_t i{}; i != elements.size(); ++i) { ElementLoop(i); }
559 (*timer).second.stop();
560 logger <<
logging::info <<
"Finished to allocate the system matrix" << logging::LOGFLUSH;
564 auto RowLoop = [&](
size_t row) { K_eff_.InitializeRow(row, row_contributions[row]); };
567 logger <<
logging::info <<
"Starting to assemble the system matrix" << logging::LOGFLUSH;
568 (*timer).second.start();
570 tbb::parallel_for(
static_cast<size_t>(0), effective_num_dof_, RowLoop);
572 for (
size_t row{}; row < effective_num_dof_; ++row) { RowLoop(row); }
574 (*timer).second.stop();
575 logger <<
logging::info <<
"Finished to assemble the system matrix" << logging::LOGFLUSH;
578template <
typename T,
typename MATRIX_FORMAT>
579void TypedSolver<T, MATRIX_FORMAT>::AssembleEffectiveSystem(TypedElement<T>& element) {
583 b2linalg::Matrix<T, typename MATRIX_FORMAT::dense> k_eff;
584 b2linalg::Vector<T, b2linalg::Vdense> f_eff;
585 b2linalg::Index index;
587 element.AssembleElementEffectiveSystem(*
this, k_eff, f_eff, index);
593 size_t N{index.size()};
594 int global_index_i{};
595 int global_index_j{};
596 for (
size_t i{}; i != N; ++i) {
597 global_index_i = fixed_dof_[index[i]];
598 if (global_index_i < 0) {
603 for (
size_t j{}; j < N; ++j) {
604 global_index_j = fixed_dof_[index[j]];
606 if (global_index_j < 0) {
607 if (k_eff(i, j) != T{}) { f_eff[i] -= k_eff(i, j) * dU_(index[j], 0); }
609 if constexpr (std::is_same_v<MATRIX_FORMAT, b2linalg::Msymmetric>) {
610 if (j < i) {
continue; }
614 std::atomic_ref<T> atomic_U{K_eff_(global_index_i, global_index_j)};
615 atomic_U += k_eff(i, j);
618 K_eff_(global_index_i, global_index_j) += k_eff(i, j);
624 std::atomic_ref<T> atomic_T{F_eff_[global_index_i]};
625 atomic_T += f_eff[i];
627 F_eff_[global_index_i] += f_eff[i];
632template <
typename T,
typename MATRIX_FORMAT>
633void TypedSolver<T, MATRIX_FORMAT>::AssembleEffectiveMatrix(TypedElement<T>& element) {
637 b2linalg::Matrix<T, typename MATRIX_FORMAT::dense>
639 b2linalg::Index index;
641 k_eff = element.AssembleElementEffectiveMatrix(*
this, index);
647template <
typename T,
typename MATRIX_FORMAT>
648void TypedSolver<T, MATRIX_FORMAT>::AssembleEffectiveVector(TypedElement<T>& element) {
653 b2linalg::Vector<T, b2linalg::Vdense> f_eff;
654 b2linalg::Index index;
656 f_eff = element.AssembleElementEffectiveVector(*
this, index);
662template <
typename T,
typename MATRIX_FORMAT>
663inline void TypedSolver<T, MATRIX_FORMAT>::ComputeGradients(
664 TypedElement<T>& element, GradientContainer*
const gradient_container) {
665 gradient_container->set_current_element(element);
667 element.ComputeElementGradient(*
this, gradient_container);
670template <
typename T,
typename MATRIX_FORMAT>
671inline void TypedSolver<T, MATRIX_FORMAT>::ComputeError(
672 std::vector<std::reference_wrapper<Element>>& elements,
auto timer, std::string_view log) {
674 IterateOverElementsParallelReduction(
679 [
this, &elements](
const tbb::blocked_range<size_t>& r, T init) -> T {
680 for (
size_t i{r.begin()}; i != r.end(); ++i) {
682 init, (
dynamic_cast<TypedElement<T>&
>(elements[i].get()))
683 .ComputeElementError(*
this));
688 [](T x, T y) {
return std::max(x, y); },
695 [
this, &elements](
const size_t i) {
696 reduced_scalar_value = std::max(
697 reduced_scalar_value,
698 dynamic_cast<TypedElement<T>&
>(elements[i].get()).ComputeElementError(*
this));
704template <
typename T,
typename MATRIX_FORMAT>
705inline void TypedSolver<T, MATRIX_FORMAT>::SetSolutionFieldsToZero() {
706 auto SetdUToZero{[&](
size_t i) { dU_(i, 0) = 0; }};
709 tbb::parallel_for(
static_cast<size_t>(0), dof_.size1(), SetdUToZero);
711 for (
size_t i{}; i < dof_.size1(); ++i) { SetdUToZero(i); }
714 K_eff_.set_zero_same_struct();
718template <
typename T,
typename MATRIX_FORMAT>
719void TypedSolver<T, MATRIX_FORMAT>::SolveEffectiveSystem() {
720 b2linalg::Matrix<T, b2linalg::Mrectangle> nks_r;
721 b2linalg::SingularMatrixError ee;
722 bool is_singular =
false;
724 const int nbnsv = case_->get_int(
"COMPUTE_NULL_SPACE_VECTOR", 0);
726 K_eff_.remove_zero();
730 logger <<
logging::info <<
"Starting to solve the effective system" << logging::LOGFLUSH;
734 F_eff_ = inverse(K_eff_, nks_r, nbnsv) * F_eff_;
736 logger <<
logging::info <<
"Finished to solve the effective system" << logging::LOGFLUSH;
737 }
catch (b2linalg::SingularMatrixError& e) {
741 if (!e.singular_dofs.empty()) {
742 if (nks_r.size2() == 0) {
743 nks_r.resize(effective_num_dof_, 1);
746 for (
auto i : e.singular_dofs) {
747 assert(i < nks_r.size1());
748 for (
size_t j{}; j != nks_r.size2(); ++j) { nks_r(i, j) = T(1); }
754template <
typename T,
typename MATRIX_FORMAT>
755typename TypedSolver<T, MATRIX_FORMAT>::type_t TypedSolver<T, MATRIX_FORMAT>::type(
756 "TypedSolver", type_name<T, MATRIX_FORMAT>(), StringList(
"TypedSolver"),
757 b2000::solver::module, &Solver::type);
csda< T > log(const csda< T > &a)
Natural logarithm of a csda number.
Definition b2csda.H:366
Interface to C++ representations of FE solvers.
virtual void InitializeSolverOnCase()
Initializes the member variables of class Solver for the case at hand.
Definition b2solver.H:116
Logger & get_logger(const std::string &logger_name="")
Definition b2logging.H:829
timer_map_t::iterator obtain_timer(std::string name)
Definition b2timing.H:139