OGS
ParallelVectorMatrixAssembler.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
5
6#include <cstdlib>
7#include <fstream>
8#include <range/v3/view/iota.hpp>
9#include <vector>
10
12#include "BaseLib/StringTools.h"
20
21namespace
22{
23void assembleOneElement(const std::size_t mesh_item_id,
25 const NumLib::LocalToGlobalIndexMap& dof_table,
26 const double t, const double dt, const GlobalVector& x,
27 const GlobalVector& x_prev,
28 std::vector<double>& local_M_data,
29 std::vector<double>& local_K_data,
30 std::vector<double>& local_b_data,
32{
33 std::vector<GlobalIndexType> indices =
34 NumLib::getIndices(mesh_item_id, dof_table);
35
36 local_M_data.clear();
37 local_K_data.clear();
38 local_b_data.clear();
39
40 auto const local_x = x.get(indices);
41 auto const local_x_prev = x_prev.get(indices);
42 local_assembler.assemble(t, dt, local_x, local_x_prev, local_M_data,
43 local_K_data, local_b_data);
44
45 cache.add(local_M_data, local_K_data, local_b_data, std::move(indices));
46}
47
49 const std::size_t mesh_item_id,
51 std::vector<NumLib::LocalToGlobalIndexMap const*> const& dof_tables,
52 const double t, const double dt, std::vector<GlobalVector*> const& x,
53 std::vector<GlobalVector*> const& x_prev, int const process_id,
54 std::vector<double>& local_M_data, std::vector<double>& local_K_data,
55 std::vector<double>& local_b_data,
57{
58 std::vector<std::vector<GlobalIndexType>> indices_of_processes;
59 indices_of_processes.reserve(dof_tables.size());
60 transform(cbegin(dof_tables), cend(dof_tables),
61 back_inserter(indices_of_processes), [&](auto const* dof_table)
62 { return NumLib::getIndices(mesh_item_id, *dof_table); });
63
64 auto local_coupled_xs =
65 ProcessLib::getCoupledLocalSolutions(x, indices_of_processes);
66 auto const local_x = MathLib::toVector(local_coupled_xs);
67
68 auto local_coupled_x_prevs =
69 ProcessLib::getCoupledLocalSolutions(x_prev, indices_of_processes);
70 auto const local_x_prev = MathLib::toVector(local_coupled_x_prevs);
71
72 local_M_data.clear();
73 local_K_data.clear();
74 local_b_data.clear();
75
76 local_assembler.assembleForStaggeredScheme(t, dt, local_x, local_x_prev,
77 process_id, local_M_data,
78 local_K_data, local_b_data);
79
80 auto const& indices = indices_of_processes[process_id];
81 cache.add(local_M_data, local_K_data, local_b_data, indices);
82}
83
85 const std::size_t mesh_item_id,
87 const NumLib::LocalToGlobalIndexMap& dof_table, const double t,
88 const double dt, const GlobalVector& x, const GlobalVector& x_prev,
89 std::vector<double>& local_b_data, std::vector<double>& local_Jac_data,
90 ProcessLib::AbstractJacobianAssembler& jacobian_assembler,
92{
93 std::vector<GlobalIndexType> const& indices =
94 NumLib::getIndices(mesh_item_id, dof_table);
95
96 local_b_data.clear();
97 local_Jac_data.clear();
98
99 auto const local_x = x.get(indices);
100 auto const local_x_prev = x_prev.get(indices);
101 jacobian_assembler.assembleWithJacobian(local_assembler, t, dt, local_x,
102 local_x_prev, local_b_data,
103 local_Jac_data);
104
105 if (local_Jac_data.empty())
106 {
107 OGS_FATAL(
108 "No Jacobian has been assembled! This might be due to "
109 "programming errors in the local assembler of the "
110 "current process.");
111 }
112
113 cache.add(local_b_data, local_Jac_data, indices);
114}
115
117 const std::size_t mesh_item_id,
119 std::vector<NumLib::LocalToGlobalIndexMap const*> const& dof_tables,
120 const double t, const double dt, std::vector<GlobalVector*> const& x,
121 std::vector<GlobalVector*> const& x_prev, int const process_id,
122 std::vector<double>& local_b_data, std::vector<double>& local_Jac_data,
123 ProcessLib::AbstractJacobianAssembler& jacobian_assembler,
125{
126 std::vector<std::vector<GlobalIndexType>> indices_of_processes;
127 indices_of_processes.reserve(dof_tables.size());
128 transform(cbegin(dof_tables), cend(dof_tables),
129 back_inserter(indices_of_processes),
130 [&](auto const* dof_table)
131 { return NumLib::getIndices(mesh_item_id, *dof_table); });
132
133 auto local_coupled_xs =
134 ProcessLib::getCoupledLocalSolutions(x, indices_of_processes);
135 auto const local_x = MathLib::toVector(local_coupled_xs);
136
137 auto local_coupled_x_prevs =
138 ProcessLib::getCoupledLocalSolutions(x_prev, indices_of_processes);
139 auto const local_x_prev = MathLib::toVector(local_coupled_x_prevs);
140
141 std::vector<GlobalIndexType> const& indices =
142 indices_of_processes[process_id];
143
144 local_b_data.clear();
145 local_Jac_data.clear();
146
148 local_assembler, t, dt, local_x, local_x_prev, process_id, local_b_data,
149 local_Jac_data);
150
151 if (local_Jac_data.empty())
152 {
153 OGS_FATAL(
154 "No Jacobian has been assembled! This might be due to "
155 "programming errors in the local assembler of the "
156 "current process.");
157 }
158
159 cache.add(local_b_data, local_Jac_data, indices);
160}
161
165 ProcessLib::LocalAssemblerInterface> const& local_assemblers,
166 ThreadException& exception,
167 auto local_matrix_output,
168 auto assemble,
169 std::ptrdiff_t const num_active_local_assemblers,
170 auto get_element_id_callback)
171{
172#pragma omp for nowait
173 for (std::ptrdiff_t i = 0; i < num_active_local_assemblers; ++i)
174 {
175 [[unlikely]] if (exception)
176 {
177 continue;
178 }
179
180 auto const element_id = get_element_id_callback(i);
181 auto& loc_asm = local_assemblers[element_id];
182
183 try
184 {
185 assemble(element_id, loc_asm);
186 }
187 catch (...)
188 {
189 exception.capture();
190 continue;
191 }
192
193 local_matrix_output(element_id);
194 }
195}
196
202 ProcessLib::LocalAssemblerInterface> const& local_assemblers,
203 std::vector<std::size_t> const* const active_elements,
204 ThreadException& exception,
205 auto local_matrix_output,
206 auto assemble)
207{
208 [[likely]] if (!active_elements)
209 {
210 std::ptrdiff_t const n_elements =
211 static_cast<std::ptrdiff_t>(local_assemblers.size());
212
213 runAssemblyImpl(local_assemblers, exception, local_matrix_output,
214 assemble, n_elements, [](auto i) { return i; });
215 }
216 else
217 {
218 std::ptrdiff_t const n_elements =
219 static_cast<std::ptrdiff_t>(active_elements->size());
220
221 runAssemblyImpl(local_assemblers, exception, local_matrix_output,
222 assemble, n_elements, [active_elements](auto i)
223 { return (*active_elements)[i]; });
224 }
225}
226} // namespace
227
228namespace ProcessLib::Assembly
229{
231 AbstractJacobianAssembler& jacobian_assembler)
232 : jacobian_assembler_{jacobian_assembler},
233 num_threads_(BaseLib::getNumberOfAssemblyThreads())
234{
235 INFO("Threads used for ParallelVectorMatrixAssembler: {}.", num_threads_);
236}
237
240 LocalAssemblerInterface> const& local_assemblers,
241 std::vector<std::size_t> const* const active_elements,
242 std::vector<NumLib::LocalToGlobalIndexMap const*> const& dof_tables,
243 const double t, double const dt, std::vector<GlobalVector*> const& xs,
244 std::vector<GlobalVector*> const& x_prevs, int const process_id,
246{
247 // checks //////////////////////////////////////////////////////////////////
248 if (dof_tables.size() != xs.size())
249 {
250 OGS_FATAL("Different number of DOF tables and solution vectors.");
251 }
252
253 std::size_t const number_of_processes = xs.size();
254
255 // algorithm ///////////////////////////////////////////////////////////////
256
257 auto stats = CumulativeStats<MultiStats<2>>::create(num_threads_);
258
259 ThreadException exception;
260#pragma omp parallel num_threads(num_threads_)
261 {
262#ifdef _OPENMP
263#pragma omp single nowait
264 {
265 INFO("Number of threads: {}", omp_get_num_threads());
266 }
267#endif
268
269 // temporary data only stored here in order to avoid frequent memory
270 // reallocations.
271 std::vector<double> local_M_data;
272 std::vector<double> local_K_data;
273 std::vector<double> local_b_data;
274
275 // copy to avoid concurrent access
276 auto const jac_asm = jacobian_assembler_.copy();
277
278 auto stats_this_thread = stats->clone();
279 MultiMatrixElementCache<2> cache{M, K, b, stats_this_thread->data,
281
282 auto local_matrix_output = [&](std::ptrdiff_t element_id)
283 {
284 local_matrix_output_(t, process_id, element_id, local_M_data,
285 local_K_data, local_b_data);
286 };
287
288 // Monolithic scheme
289 if (number_of_processes == 1)
290 {
291 assert(process_id == 0);
292 auto const& dof_table = *dof_tables[0];
293 auto const& x = *xs[0];
294 auto const& x_prev = *x_prevs[0];
295
296 runAssembly(local_assemblers, active_elements, exception,
297 local_matrix_output,
298 [&](auto element_id, auto& loc_asm)
299 {
300 assembleOneElement(element_id, loc_asm, dof_table,
301 t, dt, x, x_prev, local_M_data,
302 local_K_data, local_b_data,
303 cache);
304 });
305 }
306 else // Staggered scheme
307 {
308 runAssembly(local_assemblers, active_elements, exception,
309 local_matrix_output,
310 [&](auto element_id, auto& loc_asm)
311 {
312 assembleForStaggeredSchemeOneElement(
313 element_id, loc_asm, dof_tables, t, dt, xs,
314 x_prevs, process_id, local_M_data, local_K_data,
315 local_b_data, cache);
316 });
317 }
318 }
319
320 global_matrix_output_(t, process_id, M, K, b);
321 exception.rethrow();
322}
323
326 LocalAssemblerInterface> const& local_assemblers,
327 std::vector<std::size_t> const* const active_elements,
328 std::vector<NumLib::LocalToGlobalIndexMap const*> const& dof_tables,
329 const double t, double const dt, std::vector<GlobalVector*> const& xs,
330 std::vector<GlobalVector*> const& x_prevs, int const process_id,
331 GlobalVector& b, GlobalMatrix& Jac)
332{
333 // checks //////////////////////////////////////////////////////////////////
334 if (dof_tables.size() != xs.size())
335 {
336 OGS_FATAL("Different number of DOF tables and solution vectors.");
337 }
338
339 std::size_t const number_of_processes = xs.size();
340 // algorithm ///////////////////////////////////////////////////////////////
341
342 auto stats = CumulativeStats<MultiStats<1>>::create(num_threads_);
343
344 ThreadException exception;
345#pragma omp parallel num_threads(num_threads_)
346 {
347#ifdef _OPENMP
348#pragma omp single nowait
349 {
350 INFO("Number of threads: {}", omp_get_num_threads());
351 }
352#endif
353
354 // temporary data only stored here in order to avoid frequent memory
355 // reallocations.
356 std::vector<double> local_b_data;
357 std::vector<double> local_Jac_data;
358
359 // copy to avoid concurrent access
360 auto const jac_asm = jacobian_assembler_.copy();
361 auto stats_this_thread = stats->clone();
362
363 MultiMatrixElementCache<1> cache{b, Jac, stats_this_thread->data,
365
366 auto local_matrix_output = [&](std::ptrdiff_t element_id)
367 {
368 local_matrix_output_(t, process_id, element_id, local_b_data,
369 local_Jac_data);
370 };
371
372 // Monolithic scheme
373 if (number_of_processes == 1)
374 {
375 assert(process_id == 0);
376 auto const& dof_table = *dof_tables[0];
377 auto const& x = *xs[0];
378 auto const& x_prev = *x_prevs[0];
379
380 runAssembly(
381 local_assemblers, active_elements, exception,
382 local_matrix_output,
383 [&](auto element_id, auto& loc_asm)
384 {
385 assembleWithJacobianOneElement(
386 element_id, loc_asm, dof_table, t, dt, x, x_prev,
387 local_b_data, local_Jac_data, *jac_asm, cache);
388 });
389 }
390 else // Staggered scheme
391 {
392 runAssembly(
393 local_assemblers, active_elements, exception,
394 local_matrix_output,
395 [&](auto element_id, auto& loc_asm)
396 {
397 assembleWithJacobianForStaggeredSchemeOneElement(
398 element_id, loc_asm, dof_tables, t, dt, xs, x_prevs,
399 process_id, local_b_data, local_Jac_data, *jac_asm,
400 cache);
401 });
402 }
403 }
404
405 stats->print();
406
407 global_matrix_output_(t, process_id, b, Jac);
408 exception.rethrow();
409}
410} // namespace ProcessLib::Assembly
#define OGS_FATAL(...)
Definition Error.h:19
MathLib::EigenMatrix GlobalMatrix
MathLib::EigenVector GlobalVector
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
double get(IndexType rowId) const
get entry
Definition EigenVector.h:52
Base class for Jacobian assemblers.
virtual void assembleWithJacobian(LocalAssemblerInterface &local_assembler, double const t, double const dt, std::vector< double > const &local_x, std::vector< double > const &local_x_prev, std::vector< double > &local_b_data, std::vector< double > &local_Jac_data)=0
virtual void assembleWithJacobianForStaggeredScheme(LocalAssemblerInterface &, double const, double const, Eigen::VectorXd const &, Eigen::VectorXd const &, int const, std::vector< double > &, std::vector< double > &)
void assemble(BaseLib::PolymorphicRandomAccessContainerView< LocalAssemblerInterface > const &local_assemblers, std::vector< std::size_t > const *const active_elements, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_tables, const double t, double const dt, std::vector< GlobalVector * > const &xs, std::vector< GlobalVector * > const &x_prevs, int const process_id, GlobalMatrix &M, GlobalMatrix &K, GlobalVector &b)
ParallelVectorMatrixAssembler(AbstractJacobianAssembler &jacobian_assembler)
void assembleWithJacobian(BaseLib::PolymorphicRandomAccessContainerView< LocalAssemblerInterface > const &local_assemblers, std::vector< std::size_t > const *const active_elements, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_tables, const double t, double const dt, std::vector< GlobalVector * > const &xs, std::vector< GlobalVector * > const &x_prevs, int const process_id, GlobalVector &b, GlobalMatrix &Jac)
virtual void assembleForStaggeredScheme(double const t, double const dt, Eigen::VectorXd const &local_x, Eigen::VectorXd const &local_x_prev, int const process_id, std::vector< double > &local_M_data, std::vector< double > &local_K_data, std::vector< double > &local_b_data)
virtual void assemble(double const t, double const dt, std::vector< double > const &local_x, std::vector< double > const &local_x_prev, std::vector< double > &local_M_data, std::vector< double > &local_K_data, std::vector< double > &local_b_data)
Eigen::Map< const Vector > toVector(std::vector< double > const &data, Eigen::VectorXd::Index size)
Creates an Eigen mapped vector from the given data vector.
std::vector< GlobalIndexType > getIndices(std::size_t const mesh_item_id, NumLib::LocalToGlobalIndexMap const &dof_table)
std::vector< double > getCoupledLocalSolutions(std::vector< GlobalVector * > const &global_solutions, std::vector< std::vector< GlobalIndexType > > const &indices)
void assembleOneElement(const std::size_t mesh_item_id, ProcessLib::LocalAssemblerInterface &local_assembler, const NumLib::LocalToGlobalIndexMap &dof_table, const double t, const double dt, const GlobalVector &x, const GlobalVector &x_prev, std::vector< double > &local_M_data, std::vector< double > &local_K_data, std::vector< double > &local_b_data, ProcessLib::Assembly::MultiMatrixElementCache< 2 > &cache)
void assembleWithJacobianForStaggeredSchemeOneElement(const std::size_t mesh_item_id, ProcessLib::LocalAssemblerInterface &local_assembler, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_tables, const double t, const double dt, std::vector< GlobalVector * > const &x, std::vector< GlobalVector * > const &x_prev, int const process_id, std::vector< double > &local_b_data, std::vector< double > &local_Jac_data, ProcessLib::AbstractJacobianAssembler &jacobian_assembler, ProcessLib::Assembly::MultiMatrixElementCache< 1 > &cache)
void assembleWithJacobianOneElement(const std::size_t mesh_item_id, ProcessLib::LocalAssemblerInterface &local_assembler, const NumLib::LocalToGlobalIndexMap &dof_table, const double t, const double dt, const GlobalVector &x, const GlobalVector &x_prev, std::vector< double > &local_b_data, std::vector< double > &local_Jac_data, ProcessLib::AbstractJacobianAssembler &jacobian_assembler, ProcessLib::Assembly::MultiMatrixElementCache< 1 > &cache)
void runAssembly(BaseLib::PolymorphicRandomAccessContainerView< ProcessLib::LocalAssemblerInterface > const &local_assemblers, std::vector< std::size_t > const *const active_elements, ThreadException &exception, auto local_matrix_output, auto assemble)
void runAssemblyImpl(BaseLib::PolymorphicRandomAccessContainerView< ProcessLib::LocalAssemblerInterface > const &local_assemblers, ThreadException &exception, auto local_matrix_output, auto assemble, std::ptrdiff_t const num_active_local_assemblers, auto get_element_id_callback)
Runs the passed assemble functor for each active local assembler.
void assembleForStaggeredSchemeOneElement(const std::size_t mesh_item_id, ProcessLib::LocalAssemblerInterface &local_assembler, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_tables, const double t, const double dt, std::vector< GlobalVector * > const &x, std::vector< GlobalVector * > const &x_prev, int const process_id, std::vector< double > &local_M_data, std::vector< double > &local_K_data, std::vector< double > &local_b_data, ProcessLib::Assembly::MultiMatrixElementCache< 2 > &cache)