Loading [MathJax]/extensions/tex2jax.js
OGS
ParallelVectorMatrixAssembler.cpp
Go to the documentation of this file.
1
12
13#include <cstdlib>
14#include <fstream>
15#include <range/v3/view/iota.hpp>
16#include <vector>
17
18#include "BaseLib/StringTools.h"
25
26namespace
27{
29 const std::size_t mesh_item_id,
31 const NumLib::LocalToGlobalIndexMap& dof_table, const double t,
32 const double dt, const GlobalVector& x, const GlobalVector& x_prev,
33 std::vector<double>& local_b_data, std::vector<double>& local_Jac_data,
34 ProcessLib::AbstractJacobianAssembler& jacobian_assembler,
36{
37 std::vector<GlobalIndexType> const& indices =
38 NumLib::getIndices(mesh_item_id, dof_table);
39
40 local_b_data.clear();
41 local_Jac_data.clear();
42
43 auto const local_x = x.get(indices);
44 auto const local_x_prev = x_prev.get(indices);
45 jacobian_assembler.assembleWithJacobian(local_assembler, t, dt, local_x,
46 local_x_prev, local_b_data,
47 local_Jac_data);
48
49 if (local_Jac_data.empty())
50 {
52 "No Jacobian has been assembled! This might be due to "
53 "programming errors in the local assembler of the "
54 "current process.");
55 }
56
57 cache.add(local_b_data, local_Jac_data, indices);
58}
59
61 const std::size_t mesh_item_id,
63 std::vector<NumLib::LocalToGlobalIndexMap const*> const& dof_tables,
64 const double t, const double dt, std::vector<GlobalVector*> const& x,
65 std::vector<GlobalVector*> const& x_prev, int const process_id,
66 std::vector<double>& local_b_data, std::vector<double>& local_Jac_data,
67 ProcessLib::AbstractJacobianAssembler& jacobian_assembler,
69{
70 std::vector<std::vector<GlobalIndexType>> indices_of_processes;
71 indices_of_processes.reserve(dof_tables.size());
72 transform(cbegin(dof_tables), cend(dof_tables),
73 back_inserter(indices_of_processes),
74 [&](auto const* dof_table)
75 { return NumLib::getIndices(mesh_item_id, *dof_table); });
76
77 auto local_coupled_xs =
78 ProcessLib::getCoupledLocalSolutions(x, indices_of_processes);
79 auto const local_x = MathLib::toVector(local_coupled_xs);
80
81 auto local_coupled_x_prevs =
82 ProcessLib::getCoupledLocalSolutions(x_prev, indices_of_processes);
83 auto const local_x_prev = MathLib::toVector(local_coupled_x_prevs);
84
85 std::vector<GlobalIndexType> const& indices =
86 indices_of_processes[process_id];
87
88 local_b_data.clear();
89 local_Jac_data.clear();
90
92 local_assembler, t, dt, local_x, local_x_prev, process_id, local_b_data,
93 local_Jac_data);
94
95 if (local_Jac_data.empty())
96 {
98 "No Jacobian has been assembled! This might be due to "
99 "programming errors in the local assembler of the "
100 "current process.");
101 }
102
103 cache.add(local_b_data, local_Jac_data, indices);
104}
105
107std::vector<
108 std::tuple<std::ptrdiff_t,
109 std::reference_wrapper<ProcessLib::LocalAssemblerInterface>>>
112 ProcessLib::LocalAssemblerInterface> const& local_assemblers,
113 std::vector<std::size_t> const& active_elements)
114{
115 auto create_ids_asm_pairs = [&](auto const& element_ids)
116 {
117 std::vector<std::tuple<
118 std::ptrdiff_t,
119 std::reference_wrapper<ProcessLib::LocalAssemblerInterface>>>
120 result;
121 result.reserve(static_cast<std::size_t>(element_ids.size()));
122 for (auto const id : element_ids)
123 {
124 result.push_back({id, local_assemblers[id]});
125 }
126 return result;
127 };
128
129 if (active_elements.empty())
130 {
131 return create_ids_asm_pairs(ranges::views::iota(
132 static_cast<std::size_t>(0), local_assemblers.size()));
133 }
134 return create_ids_asm_pairs(active_elements);
135}
136
138 std::vector<std::tuple<
139 std::ptrdiff_t,
140 std::reference_wrapper<ProcessLib::LocalAssemblerInterface>>> const&
141 ids_local_assemblers,
142 ThreadException& exception,
143 auto local_matrix_output,
144 auto assemble)
145{
146 std::ptrdiff_t n_elements =
147 static_cast<std::ptrdiff_t>(ids_local_assemblers.size());
148
149#pragma omp for nowait
150 for (std::ptrdiff_t i = 0; i < n_elements; ++i)
151 {
152 if (exception)
153 {
154 continue;
155 }
156 auto [element_id, loc_asm] = ids_local_assemblers[i];
157
158 try
159 {
160 assemble(element_id, loc_asm);
161 }
162 catch (...)
163 {
164 exception.capture();
165 continue;
166 }
167
168 local_matrix_output(element_id);
169 }
170}
171
173{
174 char const* const num_threads_env = std::getenv("OGS_ASM_THREADS");
175
176 if (!num_threads_env)
177 {
178 INFO(
179 "Threads used for ParallelVectorMatrixAssembler: 1. This is the "
180 "default when OGS_ASM_THREADS environment variable is not set.");
181 return 1;
182 }
183
184 if (std::strlen(num_threads_env) == 0)
185 {
186 OGS_FATAL("The environment variable OGS_ASM_THREADS is set but empty.");
187 }
188
189 std::string num_threads_str{num_threads_env};
190 BaseLib::trim(num_threads_str);
191
192 std::istringstream num_threads_iss{num_threads_str};
193 int num_threads = -1;
194
195 num_threads_iss >> num_threads;
196
197 if (!num_threads_iss)
198 {
199 OGS_FATAL("Error parsing OGS_ASM_THREADS (= \"{}\").", num_threads_env);
200 }
201
202 if (!num_threads_iss.eof())
203 {
204 OGS_FATAL(
205 "Error parsing OGS_ASM_THREADS (= \"{}\"): not read entirely, the "
206 "remainder is \"{}\"",
207 num_threads_env,
208 num_threads_iss.str().substr(num_threads_iss.tellg()));
209 }
210
211 if (num_threads < 1)
212 {
213 OGS_FATAL(
214 "You asked (via OGS_ASM_THREADS) to assemble with {} < 1 thread.",
215 num_threads);
216 }
217
218 INFO("Threads used for ParallelVectorMatrixAssembler: {}.", num_threads);
219 return num_threads;
220}
221} // namespace
222
223namespace ProcessLib::Assembly
224{
226 AbstractJacobianAssembler& jacobian_assembler)
227 : jacobian_assembler_{jacobian_assembler},
228 num_threads_(getNumberOfThreads())
229{
230}
231
234 LocalAssemblerInterface> const& local_assemblers,
235 std::vector<std::size_t> const& active_elements,
236 std::vector<NumLib::LocalToGlobalIndexMap const*> const& dof_tables,
237 const double t, double const dt, std::vector<GlobalVector*> const& xs,
238 std::vector<GlobalVector*> const& x_prevs, int const process_id,
239 GlobalVector& b, GlobalMatrix& Jac)
240{
241 // checks //////////////////////////////////////////////////////////////////
242 if (dof_tables.size() != xs.size())
243 {
244 OGS_FATAL("Different number of DOF tables and solution vectors.");
245 }
246
247 std::size_t const number_of_processes = xs.size();
248 // algorithm ///////////////////////////////////////////////////////////////
249
251 ConcurrentMatrixView b_view(b);
252 ConcurrentMatrixView Jac_view(Jac);
253
254 ThreadException exception;
255#pragma omp parallel num_threads(num_threads_)
256 {
257#ifdef _OPENMP
258#pragma omp single nowait
259 {
260 INFO("Number of threads: {}", omp_get_num_threads());
261 }
262#endif
263
264 // temporary data only stored here in order to avoid frequent memory
265 // reallocations.
266 std::vector<double> local_b_data;
267 std::vector<double> local_Jac_data;
268
269 // copy to avoid concurrent access
270 auto const jac_asm = jacobian_assembler_.copy();
271 auto stats_this_thread = stats->clone();
272
273 MultiMatrixElementCache cache{b_view, Jac_view,
274 stats_this_thread->data};
275
276 auto local_matrix_output = [&](std::ptrdiff_t element_id)
277 {
278 local_matrix_output_(t, process_id, element_id, local_b_data,
279 local_Jac_data);
280 };
281
282 // TODO corner case: what if all elements on a submesh are deactivated?
283
284 // Monolithic scheme
285 if (number_of_processes == 1)
286 {
287 assert(process_id == 0);
288 auto const& dof_table = *dof_tables[0];
289 auto const& x = *xs[0];
290 auto const& x_prev = *x_prevs[0];
291
292 runAssembly(
293 collectActiveLocalAssemblers(local_assemblers, active_elements),
294 exception, local_matrix_output,
295 [&](auto element_id, auto& loc_asm)
296 {
297 assembleWithJacobianOneElement(
298 element_id, loc_asm, dof_table, t, dt, x, x_prev,
299 local_b_data, local_Jac_data, *jac_asm, cache);
300 });
301 }
302 else // Staggered scheme
303 {
304 runAssembly(
305 collectActiveLocalAssemblers(local_assemblers, active_elements),
306 exception, local_matrix_output,
307 [&](auto element_id, auto& loc_asm)
308 {
309 assembleWithJacobianForStaggeredSchemeOneElement(
310 element_id, loc_asm, dof_tables, t, dt, xs, x_prevs,
311 process_id, local_b_data, local_Jac_data, *jac_asm,
312 cache);
313 });
314 }
315 }
316
317 stats->print();
318
319 global_matrix_output_(t, process_id, b, Jac);
320 exception.rethrow();
321}
322} // namespace ProcessLib::Assembly
#define OGS_FATAL(...)
Definition Error.h:26
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:35
Definition of string helper functions.
Global vector based on Eigen vector.
Definition EigenVector.h:25
double get(IndexType rowId) const
get entry
Definition EigenVector.h:58
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 std::unique_ptr< AbstractJacobianAssembler > copy() const =0
virtual void assembleWithJacobianForStaggeredScheme(LocalAssemblerInterface &, double const, double const, Eigen::VectorXd const &, Eigen::VectorXd const &, int const, std::vector< double > &, std::vector< double > &)
static std::shared_ptr< CumulativeStats< Data > > create()
void add(std::vector< double > const &local_b_data, std::vector< double > const &local_Jac_data, std::vector< GlobalIndexType > const &indices)
ParallelVectorMatrixAssembler(AbstractJacobianAssembler &jacobian_assembler)
void assembleWithJacobian(BaseLib::PolymorphicRandomAccessContainerView< LocalAssemblerInterface > const &local_assemblers, std::vector< std::size_t > 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)
void trim(std::string &str, char ch)
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)
std::vector< std::tuple< std::ptrdiff_t, std::reference_wrapper< ProcessLib::LocalAssemblerInterface > > > collectActiveLocalAssemblers(BaseLib::PolymorphicRandomAccessContainerView< ProcessLib::LocalAssemblerInterface > const &local_assemblers, std::vector< std::size_t > const &active_elements)
Returns a vector of active element ids with corresponding local assemblers.
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 &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 &cache)
void runAssembly(std::vector< std::tuple< std::ptrdiff_t, std::reference_wrapper< ProcessLib::LocalAssemblerInterface > > > const &ids_local_assemblers, ThreadException &exception, auto local_matrix_output, auto assemble)