OGS
AssemblyMixin.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
4#include "AssemblyMixin.h"
5
6#include <range/v3/algorithm/sort.hpp>
7#include <range/v3/view/enumerate.hpp>
8#include <range/v3/view/for_each.hpp>
9#include <range/v3/view/transform.hpp>
10#include <range/v3/view/zip.hpp>
11
14#include "Process.h"
15
16namespace
17{
18
20 std::vector<std::vector<std::string>> const& per_process_residuum_names,
21 std::vector<
22 std::vector<std::reference_wrapper<ProcessLib::ProcessVariable>>> const&
23 per_process_pvs)
24{
25 if (per_process_pvs.size() != per_process_residuum_names.size())
26 {
28 "The number of passed residuum names ({}) does not match the "
29 "number of processes ({}).",
30 per_process_residuum_names.size(), per_process_pvs.size());
31 }
33 auto check_sizes = [](std::size_t const process_id, auto const& rns_pvs)
34 {
35 auto const& [rns, pvs] = rns_pvs;
36
37 if (rns.size() != pvs.size())
38 {
40 "The number of passed residuum names ({}) does not match the "
41 "number of process variables ({}) for process {}.",
42 rns.size(), pvs.size(), process_id);
43 }
44 };
45 for (auto const& [process_id, rns_pvs] :
46 ranges::views::zip(per_process_residuum_names, per_process_pvs) |
47 ranges::views::enumerate)
48 {
49 check_sizes(process_id, rns_pvs);
50 }
51}
52
53std::vector<
54 std::vector<std::reference_wrapper<MeshLib::PropertyVector<double>>>>
56 MeshLib::Mesh& mesh,
57 std::vector<std::vector<std::string>> const& per_process_residuum_names,
58 std::vector<
59 std::vector<std::reference_wrapper<ProcessLib::ProcessVariable>>> const&
60 per_process_pvs)
61{
62 checkResiduumNamesVsProcessVariables(per_process_residuum_names,
63 per_process_pvs);
64
65 auto create_mesh_property_for_residuum =
66 [&mesh](std::pair<std::string const&,
67 ProcessLib::ProcessVariable const&> const&
68 residuum_name_process_variable)
69 -> std::reference_wrapper<MeshLib::PropertyVector<double>>
70 {
71 auto const& [rn, pv] = residuum_name_process_variable;
74 pv.getNumberOfGlobalComponents());
75 };
76
77 auto create_mesh_properties =
78 [&create_mesh_property_for_residuum](auto const& rns_pvs)
79 {
80 auto const& [rns, pvs] = rns_pvs;
81 return ranges::views::zip(rns, pvs) |
82 ranges::views::transform(create_mesh_property_for_residuum) |
83 ranges::to<std::vector<
84 std::reference_wrapper<MeshLib::PropertyVector<double>>>>;
85 };
86
87 return ranges::views::zip(per_process_residuum_names, per_process_pvs) |
88 ranges::views::transform(create_mesh_properties) |
89 ranges::to<std::vector<std::vector<
90 std::reference_wrapper<MeshLib::PropertyVector<double>>>>>;
91}
92} // namespace
93
94namespace ProcessLib
95{
96
98 AbstractJacobianAssembler& jacobian_assembler,
99 bool const is_linear,
100 bool const use_monolithic_scheme)
101 : pvma_{jacobian_assembler}, is_linear_{is_linear}
102{
103 if (is_linear && !use_monolithic_scheme)
104 {
105 OGS_FATAL(
106 "You requested to assemble only once in combination with staggered "
107 "coupling. This use case is not yet implemented.");
108 }
109
110 if (is_linear_)
111 {
112 WARN(
113 "You specified that the process simulated by OGS is linear. With "
114 "this optimization, the system is assembled only once, and the "
115 "non-linear solver performs a single iteration per time step. No "
116 "non-linearities will be resolved, and OGS will not detect whether "
117 "any exist. It is your responsibility to ensure that the assembled "
118 "systems are indeed linear; there is no safety net.\n"
119 "\n"
120 "Be aware that non-linearities may arise in subtle ways. For "
121 "example, in an HT problem with a steady-state velocity field, if "
122 "the initial pressure field does not match the steady-state "
123 "condition (e.g., initialized to zero), then the steady-state "
124 "velocity field only emerges during the first iteration—making the "
125 "system effectively non-linear under this optimization.");
126 }
127}
128
130 MeshLib::Mesh& bulk_mesh,
131 std::vector<std::reference_wrapper<MeshLib::Mesh>> const& submeshes,
132 std::vector<std::vector<std::string>> const& residuum_names,
133 std::vector<std::vector<std::reference_wrapper<ProcessVariable>>> const&
134 pvs)
135{
136 DBUG("AssemblyMixinBase initializeSubmeshOutput().");
137
138 auto const num_submeshes = submeshes.size();
139
140 if (num_submeshes == 0)
141 {
143 createResiduumVectors(bulk_mesh, residuum_names, pvs));
144 return;
145 }
146
147 submesh_assembly_data_.reserve(num_submeshes);
148 for (auto& submesh_ref : submeshes)
149 {
150 auto& submesh = submesh_ref.get();
151
152 submesh_assembly_data_.emplace_back(
153 submesh, createResiduumVectors(submesh, residuum_names, pvs));
154 }
155
156 submesh_matrix_cache_.resize(num_submeshes);
157}
158
160{
161 DBUG("AssemblyMixin updateActiveElements().");
162
164 {
166 return;
167 }
168
169 ActiveElementIDsState const new_state =
170 process.getActiveElementIDs().empty()
173
176 {
177 // no update necessary
178 return;
179 }
180
181 // updating the active elements is necessary in all remaining cases, because
182 // of the following transitions between old and new states (deactivated
183 // subdomains present? yes/no; old state -> new state):
184 // * no -> yes - now there are deactivated subdomains
185 // * yes -> no - no deactivated subdomains anymore
186 // * yes -> yes - deactivated subdomains might have changed
188}
189
191{
192 DBUG("AssemblyMixinBase updateActiveElementsImpl().");
193
194 auto const& active_element_ids = process.getActiveElementIDs();
195
196 ActiveElementIDsState const new_state =
197 active_element_ids.empty()
200
202 {
203 // no deactivated subdomains, assemble on all elements as specified
204 // in the bulk_element_ids of the submeshes
205 for (auto& sad : submesh_assembly_data_)
206 {
207 sad.setAllElementsActive();
208 }
210 {
211 bulk_mesh_assembly_data_->setAllElementsActive();
212 }
213 }
214 else
215 {
216 auto active_element_ids_copy{active_element_ids};
217 // assemble each submesh on the intersection of the "global" active
218 // elements and the submesh
219 ranges::sort(active_element_ids_copy);
220
221 for (auto& sad : submesh_assembly_data_)
222 {
223 sad.setElementSelectionActive(active_element_ids_copy);
224 }
226 {
227 bulk_mesh_assembly_data_->setElementSelectionActive(
228 active_element_ids_copy);
229 }
230 }
231
232 ids_state_ = new_state;
233}
234
236 GlobalVector const& rhs,
237 NumLib::LocalToGlobalIndexMap const& local_to_global_index_map,
238 std::vector<std::reference_wrapper<MeshLib::PropertyVector<double>>>
239 residuum_vectors)
240{
241 for (std::size_t variable_id = 0; variable_id < residuum_vectors.size();
242 ++variable_id)
243 {
244 transformVariableFromGlobalVector(
245 rhs, variable_id, local_to_global_index_map,
246 residuum_vectors[variable_id].get(), std::negate<double>());
247 }
248}
249
251 int const process_id,
252 GlobalVector const& rhs,
253 NumLib::LocalToGlobalIndexMap const& local_to_global_index_map,
255{
256 auto const& residuum_vectors = sad.residuum_vectors[process_id];
257 for (std::size_t variable_id = 0; variable_id < residuum_vectors.size();
258 ++variable_id)
259 {
260 transformVariableFromGlobalVector(
261 rhs, variable_id, local_to_global_index_map,
262 residuum_vectors[variable_id].get(),
263 std::span<std::size_t const>{sad.bulk_node_ids},
264 std::negate<double>());
265 }
266}
267
268} // namespace ProcessLib
#define OGS_FATAL(...)
Definition Error.h:19
MathLib::EigenVector GlobalVector
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
Base class for Jacobian assemblers.
void initializeAssemblyOnSubmeshes(MeshLib::Mesh &bulk_mesh, std::vector< std::reference_wrapper< MeshLib::Mesh > > const &submeshes, std::vector< std::vector< std::string > > const &residuum_names, std::vector< std::vector< std::reference_wrapper< ProcessVariable > > > const &pvs)
AssemblyMixinBase(AbstractJacobianAssembler &jacobian_assembler, bool const is_linear, bool const use_monolithic_scheme)
Assembly::ParallelVectorMatrixAssembler pvma_
static void copyResiduumVectorsToBulkMesh(GlobalVector const &rhs, NumLib::LocalToGlobalIndexMap const &local_to_global_index_map, std::vector< std::reference_wrapper< MeshLib::PropertyVector< double > > > residuum_vectors)
void updateActiveElementsImpl(Process const &process)
std::vector< AssembledMatrixCache > submesh_matrix_cache_
AssembledMatrixCache for each submesh.
static void copyResiduumVectorsToSubmesh(int const process_id, GlobalVector const &rhs, NumLib::LocalToGlobalIndexMap const &local_to_global_index_map, ProcessLib::Assembly::SubmeshAssemblyData const &sad)
ActiveElementIDsState ids_state_
void updateActiveElements(ProcessLib::Process const &process)
std::vector< ProcessLib::Assembly::SubmeshAssemblyData > submesh_assembly_data_
SubmeshAssemblyData for each submesh.
std::optional< ProcessLib::Assembly::BulkMeshAssemblyData > bulk_mesh_assembly_data_
Empty if submesh assembly is used.
std::vector< std::size_t > const & getActiveElementIDs() const
Definition Process.h:160
PropertyVector< T > * getOrCreateMeshProperty(Mesh &mesh, std::string const &property_name, MeshItemType const item_type, int const number_of_components)
void checkResiduumNamesVsProcessVariables(std::vector< std::vector< std::string > > const &per_process_residuum_names, std::vector< std::vector< std::reference_wrapper< ProcessLib::ProcessVariable > > > const &per_process_pvs)
std::vector< std::vector< std::reference_wrapper< MeshLib::PropertyVector< double > > > > createResiduumVectors(MeshLib::Mesh &mesh, std::vector< std::vector< std::string > > const &per_process_residuum_names, std::vector< std::vector< std::reference_wrapper< ProcessLib::ProcessVariable > > > const &per_process_pvs)
std::vector< std::vector< std::reference_wrapper< MeshLib::PropertyVector< double > > > > residuum_vectors
Residuum vectors for each process ID.
MeshLib::PropertyVector< std::size_t > const & bulk_node_ids