OGS
ConstraintDirichletBoundaryCondition.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 <algorithm>
7#include <vector>
8
9#include "BaseLib/Logging.h"
10#include "MeshLib/MeshSearch/NodeSearch.h" // for getUniqueNodes
11#include "MeshLib/Node.h"
12#include "ParameterLib/Utils.h"
14
15namespace ProcessLib
16{
18 ParameterLib::Parameter<double> const& parameter,
19 NumLib::LocalToGlobalIndexMap const& dof_table_bulk, int const variable_id,
20 int const component_id, MeshLib::Mesh const& bc_mesh,
21 unsigned const integration_order, MeshLib::Mesh const& bulk_mesh,
22 double const constraint_threshold, bool const lower,
23 std::function<Eigen::Vector3d(std::size_t const, MathLib::Point3d const&,
24 double const,
25 std::vector<GlobalVector*> const&)>
26 getFlux)
27 : _parameter(parameter),
28 _variable_id(variable_id),
29 _component_id(component_id),
30 _bc_mesh(bc_mesh),
31 _integration_order(integration_order),
32 _constraint_threshold(constraint_threshold),
33 _lower(lower),
34 _bulk_mesh(bulk_mesh),
35 _getFlux(getFlux)
36{
37 if (variable_id >=
38 static_cast<int>(dof_table_bulk.getNumberOfVariables()) ||
39 component_id >=
40 dof_table_bulk.getNumberOfVariableComponents(variable_id))
41 {
42 OGS_FATAL(
43 "Variable id or component id too high. Actual values: ({:d}, "
44 "{:d}), maximum values: ({:d}, {:d}).",
45 variable_id, component_id, dof_table_bulk.getNumberOfVariables(),
46 dof_table_bulk.getNumberOfVariableComponents(variable_id));
47 }
48
49 std::vector<MeshLib::Node*> const& bc_nodes = _bc_mesh.getNodes();
50 DBUG(
51 "Found {:d} nodes for constraint Dirichlet BCs for the variable {:d} "
52 "and component {:d}",
53 bc_nodes.size(), variable_id, component_id);
54
55 MeshLib::MeshSubset bc_mesh_subset{_bc_mesh, bc_nodes};
56
57 // Create local DOF table from intersected mesh subsets for the given
58 // variable and component ids.
60 variable_id, {component_id}, std::move(bc_mesh_subset));
61
62 auto const& bc_elements(_bc_mesh.getElements());
63 _local_assemblers.resize(bc_elements.size());
64 _flux_values.resize(bc_elements.size());
65 // create _bulk_ids vector
66 auto const* bulk_element_ids = MeshLib::bulkElementIDs(_bc_mesh);
67 auto const* bulk_node_ids = MeshLib::bulkNodeIDs(_bc_mesh);
68 auto const& bulk_nodes = bulk_mesh.getNodes();
69
70 auto get_bulk_element_face_id =
71 [&](auto const bulk_element_id, MeshLib::Element const* bc_elem)
72 {
73 auto const* bulk_elem = _bulk_mesh.getElement(bulk_element_id);
74 std::array<MeshLib::Node const*, 3> nodes{
75 {bulk_nodes[(*bulk_node_ids)[bc_elem->getNode(0)->getID()]],
76 bulk_nodes[(*bulk_node_ids)[bc_elem->getNode(1)->getID()]],
77 bulk_nodes[(*bulk_node_ids)[bc_elem->getNode(2)->getID()]]}};
78 return bulk_elem->identifyFace(nodes.data());
79 };
80
81 _bulk_ids.reserve(bc_elements.size());
82 std::transform(
83 begin(bc_elements), end(bc_elements), std::back_inserter(_bulk_ids),
84 [&](auto const* bc_element)
85 {
86 auto const bulk_element_id =
87 (*bulk_element_ids)[bc_element->getID()];
88 return std::make_pair(
89 bulk_element_id,
90 get_bulk_element_face_id(bulk_element_id, bc_element));
91 });
92
93 const int shape_function_order = 1;
94
96 ConstraintDirichletBoundaryConditionLocalAssembler>(
97 _bulk_mesh.getDimension(), _bc_mesh.getElements(), *_dof_table_boundary,
98 shape_function_order, _local_assemblers,
99 NumLib::IntegrationOrder{_integration_order},
100 _bc_mesh.isAxiallySymmetric(), bulk_mesh, _bulk_ids);
101}
102
104 double const t, std::vector<GlobalVector*> const& x,
105 int const /*process_id*/)
106{
107 DBUG(
108 "ConstraintDirichletBoundaryCondition::preTimestep: computing flux "
109 "constraints");
110 for (auto const id : _bc_mesh.getElements() | MeshLib::views::ids)
111 {
112 _flux_values[id] = _local_assemblers[id]->integrate(
113 x, t,
114 [this](std::size_t const element_id, MathLib::Point3d const& pnt,
115 double const t, std::vector<GlobalVector*> const& x)
116 { return _getFlux(element_id, pnt, t, x); });
117 }
118}
119
121 const double t, const GlobalVector& /*x*/,
123{
124 bc_values.ids.clear();
125 bc_values.values.clear();
126
127 std::vector<std::pair<GlobalIndexType, double>> tmp_bc_values;
128
129 auto isFlux = [&](const std::size_t element_id)
130 {
131 return _lower ? _flux_values[element_id] < _constraint_threshold
132 : _flux_values[element_id] > _constraint_threshold;
133 };
134
135 for (auto const* boundary_element : _bc_mesh.getElements())
136 {
137 // check if the boundary element is active
138 if (isFlux(boundary_element->getID()))
139 {
140 continue;
141 }
142
143 // loop over the boundary element nodes and set the dirichlet value
144 unsigned const number_nodes = boundary_element->getNumberOfNodes();
145 for (unsigned i = 0; i < number_nodes; ++i)
146 {
147 auto const& node = *boundary_element->getNode(i);
148 auto const id = node.getID();
150 id, boundary_element->getID(), node};
151
153 id);
154 // TODO: that might be slow, but only done once
155 const auto g_idx = _dof_table_boundary->getGlobalIndex(
158 {
159 continue;
160 }
161 // For the DDC approach (e.g. with PETSc option), the negative
162 // index of g_idx means that the entry by that index is a ghost one,
163 // which should be dropped. Especially for PETSc routines
164 // MatZeroRows and MatZeroRowsColumns, which are called to apply the
165 // Dirichlet BC, the negative index is not accepted like other
166 // matrix or vector PETSc routines. Therefore, the following
167 // if-condition is applied.
168 if (g_idx >= 0)
169 {
170 tmp_bc_values.emplace_back(g_idx, _parameter(t, pos).front());
171 }
172 }
173 }
174
175 if (tmp_bc_values.empty())
176 {
177 DBUG("The domain on which the boundary is defined is empty");
178 return;
179 }
180
181 // store unique pairs of node id and value with respect to the node id in
182 // the bc_values vector. The values related to the same node id are
183 // averaged.
184 // first: sort the (node id, value) pairs according to the node id
185 std::sort(tmp_bc_values.begin(), tmp_bc_values.end(),
186 [](std::pair<GlobalIndexType, double> const& a,
187 std::pair<GlobalIndexType, double> const& b)
188 { return a.first < b.first; });
189 // second: average the values over equal node id ranges
190 unsigned cnt = 1;
191 GlobalIndexType current_id = tmp_bc_values.begin()->first;
192 double sum = tmp_bc_values.begin()->second;
193 for (auto const& tmp_bc_value : tmp_bc_values)
194 {
195 if (tmp_bc_value.first == current_id)
196 {
197 cnt++;
198 sum += tmp_bc_value.second;
199 }
200 else
201 {
202 bc_values.ids.emplace_back(current_id);
203 bc_values.values.emplace_back(sum / cnt);
204 cnt = 1;
205 sum = tmp_bc_value.second;
206 current_id = tmp_bc_value.first;
207 }
208 }
209 bc_values.ids.emplace_back(current_id);
210 bc_values.values.emplace_back(sum / cnt);
211
212 DBUG("Found {:d} constraint dirichlet boundary condition values.",
213 bc_values.ids.size());
214}
215
218{
219 DBUG("Parsing ConstraintDirichletBoundaryCondition from config.");
221 config.checkConfigParameter("type", "ConstraintDirichlet");
222
223 auto const type =
225 config.getConfigParameter<std::string>("constraint_type");
226
227 // Todo (TF) Open question: How to specify which getFlux function should be
228 // used for the constraint calculation?
229 auto const process_variable =
231 config.getConfigParameter<std::string>("constraining_process_variable");
232
233 auto const threshold =
235 config.getConfigParameter<double>("constraint_threshold");
236
237 auto const direction_string =
239 config.getConfigParameter<std::string>("constraint_direction");
240
242 auto const name = config.getConfigParameter<std::string>("parameter");
243 DBUG("parameter name {:s}", name);
244
245 return {type, process_variable, threshold, direction_string, name};
246}
247
248std::unique_ptr<ConstraintDirichletBoundaryCondition>
251 MeshLib::Mesh const& bc_mesh,
252 NumLib::LocalToGlobalIndexMap const& dof_table_bulk, int const variable_id,
253 unsigned const integration_order, int const component_id,
254 std::vector<std::unique_ptr<ParameterLib::ParameterBase>> const& parameters,
255 Process const& constraining_process)
256{
257 DBUG("Constructing ConstraintDirichletBoundaryCondition");
258
259 if (config.constraint_type != "Flux")
260 {
261 OGS_FATAL("The constraint type is '{:s}', but has to be 'Flux'.",
262 config.constraint_type);
263 }
264
265 if (!constraining_process.isMonolithicSchemeUsed())
266 {
267 OGS_FATAL(
268 "The constraint dirichlet boundary condition is implemented only "
269 "for monolithic implemented processes.");
270 }
271 const int process_id = 0;
272 auto process_variables =
273 constraining_process.getProcessVariables(process_id);
274 auto constraining_pv = std::find_if(
275 process_variables.cbegin(), process_variables.cend(),
276 [&config](ProcessVariable const& pv)
277 { return pv.getName() == config.constraining_process_variable; });
278 if (constraining_pv == std::end(process_variables))
279 {
280 auto const& constraining_process_variable_name =
281 process_variables[variable_id].get().getName();
282 OGS_FATAL(
283 "<constraining_process_variable> in process variable name '{:s}' "
284 "at geometry 'TODO' : The constraining process variable is set as "
285 "'{:s}', but this is not specified in the project file.",
286 constraining_process_variable_name,
288 }
289
290 if (config.constraint_direction != "greater" &&
291 config.constraint_direction != "lower")
292 {
293 OGS_FATAL(
294 "The constraint direction is '{:s}', but has to be either "
295 "'greater' or 'lower'.",
296 config.constraint_direction);
297 }
298 bool const lower = config.constraint_direction == "lower";
299
301 parameters, 1, &bc_mesh);
302
303 // In case of partitioned mesh the boundary could be empty, i.e. there is no
304 // boundary condition.
305#ifdef USE_PETSC
306 // This can be extracted to createBoundaryCondition() but then the config
307 // parameters are not read and will cause an error.
308 // TODO (naumov): Add a function to ConfigTree for skipping the tags of the
309 // subtree and move the code up in createBoundaryCondition().
310 if (bc_mesh.getDimension() == 0 && bc_mesh.getNumberOfNodes() == 0 &&
311 bc_mesh.getNumberOfElements() == 0)
312 {
313 return nullptr;
314 }
315#endif // USE_PETSC
316
317 return std::make_unique<ConstraintDirichletBoundaryCondition>(
318 param, dof_table_bulk, variable_id, component_id, bc_mesh,
319 integration_order, constraining_process.getMesh(),
320 config.constraint_threshold, lower,
321 [&constraining_process](std::size_t const element_id,
322 MathLib::Point3d const& pnt, double const t,
323 std::vector<GlobalVector*> const& x)
324 { return constraining_process.getFlux(element_id, pnt, t, x); });
325}
326
327} // namespace ProcessLib
#define OGS_FATAL(...)
Definition Error.h:19
MathLib::EigenVector GlobalVector
GlobalMatrix::IndexType GlobalIndexType
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
T getConfigParameter(std::string const &param) const
void checkConfigParameter(std::string const &param, std::string_view const value) const
A subset of nodes on a single mesh.
Definition MeshSubset.h:17
std::vector< Node * > const & getNodes() const
Get the nodes-vector for the mesh.
Definition Mesh.h:97
unsigned getDimension() const
Returns the dimension of the mesh (determined by the maximum dimension over all elements).
Definition Mesh.h:79
std::size_t getNumberOfNodes() const
Get the number of nodes.
Definition Mesh.h:91
std::size_t getNumberOfElements() const
Get the number of elements.
Definition Mesh.h:88
int getNumberOfVariableComponents(int variable_id) const
std::unique_ptr< LocalToGlobalIndexMap > deriveBoundaryConstrainedMap(int const variable_id, std::vector< int > const &component_ids, MeshLib::MeshSubset &&new_mesh_subset) const
static constexpr NUMLIB_EXPORT GlobalIndexType const nop
void getEssentialBCValues(const double t, const GlobalVector &x, NumLib::IndexValueVector< GlobalIndexType > &bc_values) const override
Writes the values of essential BCs to bc_values.
std::function< Eigen::Vector3d(std::size_t const, MathLib::Point3d const &, double const, std::vector< GlobalVector * > const &)> _getFlux
The function _getFlux calculates the flux through the boundary element.
std::vector< std::unique_ptr< ConstraintDirichletBoundaryConditionLocalAssemblerInterface > > _local_assemblers
Local assemblers for each boundary element.
std::unique_ptr< NumLib::LocalToGlobalIndexMap > _dof_table_boundary
void preTimestep(double const t, std::vector< GlobalVector * > const &x, int const process_id) override
std::vector< double > _flux_values
Stores the results of the flux computations per boundary element.
ConstraintDirichletBoundaryCondition(ParameterLib::Parameter< double > const &parameter, NumLib::LocalToGlobalIndexMap const &dof_table_bulk, int const variable_id, int const component_id, MeshLib::Mesh const &bc_mesh, unsigned const integration_order, MeshLib::Mesh const &bulk_mesh, double const constraint_threshold, bool const lower, std::function< Eigen::Vector3d(std::size_t const, MathLib::Point3d const &, double const, std::vector< GlobalVector * > const &)> getFlux)
unsigned const _integration_order
Integration order for integration over the lower-dimensional elements.
MeshLib::Mesh & getMesh() const
Definition Process.h:146
virtual bool isMonolithicSchemeUsed() const
Definition Process.h:95
std::vector< std::vector< std::reference_wrapper< ProcessVariable > > > const & getProcessVariables() const
Definition Process.h:149
constexpr ranges::views::view_closure ids
For an element of a range view return its id.
Definition Mesh.h:216
PropertyVector< std::size_t > const * bulkElementIDs(Mesh const &mesh)
Definition Mesh.cpp:290
PropertyVector< std::size_t > const * bulkNodeIDs(Mesh const &mesh)
Definition Mesh.cpp:282
OGS_NO_DANGLING Parameter< ParameterDataType > & findParameter(std::string const &parameter_name, std::vector< std::unique_ptr< ParameterBase > > const &parameters, int const num_components, MeshLib::Mesh const *const mesh=nullptr)
void createLocalAssemblers(const unsigned dimension, std::vector< MeshLib::Element * > const &mesh_elements, NumLib::LocalToGlobalIndexMap const &dof_table, const unsigned shapefunction_order, std::vector< std::unique_ptr< LocalAssemblerInterface > > &local_assemblers, NumLib::IntegrationOrder const integration_order, ExtraCtorArgs &&... extra_ctor_args)
std::unique_ptr< ConstraintDirichletBoundaryCondition > createConstraintDirichletBoundaryCondition(ConstraintDirichletBoundaryConditionConfig const &config, MeshLib::Mesh const &bc_mesh, NumLib::LocalToGlobalIndexMap const &dof_table_bulk, int const variable_id, unsigned const integration_order, int const component_id, std::vector< std::unique_ptr< ParameterLib::ParameterBase > > const &parameters, Process const &constraining_process)
ConstraintDirichletBoundaryConditionConfig parseConstraintDirichletBoundaryCondition(BaseLib::ConfigTree const &config)
std::vector< IndexType > ids
std::vector< double > values