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