DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
DirichletBC.h
1// Copyright (C) 2007-2026 Michal Habera, Anders Logg, Garth N. Wells, Jørgen
2// S.Dokken and Paul T. Kühner
3//
4// This file is part of DOLFINx (https://www.fenicsproject.org)
5//
6// SPDX-License-Identifier: LGPL-3.0-or-later
7
8#pragma once
9
10#include "Constant.h"
11#include "DofMap.h"
12#include "Function.h"
13#include "FunctionSpace.h"
14#include <algorithm>
15#include <array>
16#include <concepts>
17#include <cstdint>
18#include <dolfinx/common/types.h>
19#include <format>
20#include <functional>
21#include <memory>
22#include <optional>
23#include <span>
24#include <stdexcept>
25#include <type_traits>
26#include <utility>
27#include <variant>
28#include <vector>
29
30namespace dolfinx::fem
31{
32
58std::vector<std::int32_t>
59locate_dofs_topological(const mesh::Topology& topology, const DofMap& dofmap,
60 int dim, std::span<const std::int32_t> entities,
61 bool remote = true);
62
89std::array<std::vector<std::int32_t>, 2> locate_dofs_topological(
90 const mesh::Topology& topology,
91 std::array<std::reference_wrapper<const DofMap>, 2> dofmaps, int dim,
92 std::span<const std::int32_t> entities, bool remote = true);
93
105template <std::floating_point T, typename U>
106std::vector<std::int32_t> locate_dofs_geometrical(const FunctionSpace<T>& V,
107 U marker_fn)
108{
109 // FIXME: Calling V.tabulate_dof_coordinates() is very expensive,
110 // especially when we usually want the boundary dofs only. Add
111 // interface that computes dofs coordinates only for specified cell.
112
113 for (std::size_t i = 0; i < V.mesh()->topology()->cell_types().size(); ++i)
114 {
115 assert(V.elements(i));
116 if (V.elements(i)->is_mixed())
117 {
118 throw std::invalid_argument(
119 "Cannot locate dofs geometrically for mixed space. Use subspaces.");
120 }
121 }
122
123 // Compute dof coordinates
124 const std::vector<T> dof_coordinates = V.tabulate_dof_coordinates(true);
125
126 using cmdspan3x_t
127 = md::mdspan<const T, md::extents<std::size_t, 3, md::dynamic_extent>>;
128
129 // Compute marker for each dof coordinate
130 cmdspan3x_t x(dof_coordinates.data(), 3, dof_coordinates.size() / 3);
131 const std::vector<std::int8_t> marked_dofs = marker_fn(x);
132
133 std::vector<std::int32_t> dofs;
134 dofs.reserve(std::count(marked_dofs.begin(), marked_dofs.end(), true));
135 for (std::size_t i = 0; i < marked_dofs.size(); ++i)
136 {
137 if (marked_dofs[i])
138 dofs.push_back(i);
139 }
140
141 return dofs;
142}
143
157template <std::floating_point T, typename U>
158std::array<std::vector<std::int32_t>, 2> locate_dofs_geometrical(
159 std::array<std::reference_wrapper<const FunctionSpace<T>>, 2> V,
160 U marker_fn)
161{
162 // FIXME: Calling V.tabulate_dof_coordinates() is very expensive,
163 // especially when we usually want the boundary dofs only. Add
164 // interface that computes dofs coordinates only for specified cell.
165
166 // Get function spaces
167 const FunctionSpace<T>& V0 = V.at(0).get();
168 const FunctionSpace<T>& V1 = V.at(1).get();
169
170 // Get mesh
171 auto mesh = V0.mesh();
172 assert(mesh);
173 assert(V1.mesh());
174 if (mesh != V1.mesh())
175 throw std::invalid_argument("Meshes are not the same.");
176 const int tdim = mesh->topology()->dim();
177
178 assert(V0.element());
179 assert(V1.element());
180 if (*V0.element() != *V1.element())
181 throw std::invalid_argument("Function spaces must have the same element.");
182
183 // Compute dof coordinates
184 const std::vector<T> dof_coordinates = V1.tabulate_dof_coordinates(true);
185
186 using cmdspan3x_t
187 = md::mdspan<const T, md::extents<std::size_t, 3, md::dynamic_extent>>;
188
189 // Evaluate marker for each dof coordinate
190 cmdspan3x_t x(dof_coordinates.data(), 3, dof_coordinates.size() / 3);
191 const std::vector<std::int8_t> marked_dofs = marker_fn(x);
192
193 // Get dofmaps
194 std::shared_ptr<const DofMap> dofmap0 = V0.dofmap();
195 assert(dofmap0);
196 const int bs0 = dofmap0->bs();
197 std::shared_ptr<const DofMap> dofmap1 = V1.dofmap();
198 assert(dofmap1);
199 const int bs1 = dofmap1->bs();
200
201 const int element_bs = dofmap0->element_dof_layout().block_size();
202 assert(element_bs == dofmap1->element_dof_layout().block_size());
203
204 // Iterate over cells
205 auto topology = mesh->topology();
206 assert(topology);
207 std::vector<std::array<std::int32_t, 2>> bc_dofs;
208 for (int c = 0; c < topology->connectivity(tdim, 0)->num_nodes(); ++c)
209 {
210 // Get cell dofmaps
211 auto cell_dofs0 = dofmap0->cell_dofs(c);
212 auto cell_dofs1 = dofmap1->cell_dofs(c);
213
214 // Loop over cell dofs and add to bc_dofs if marked.
215 for (std::size_t i = 0; i < cell_dofs1.size(); ++i)
216 {
217 if (marked_dofs[cell_dofs1[i]])
218 {
219 // Unroll over blocks
220 for (int k = 0; k < element_bs; ++k)
221 {
222 const int local_pos = element_bs * i + k;
223 const std::div_t pos0 = std::div(local_pos, bs0);
224 const std::div_t pos1 = std::div(local_pos, bs1);
225 const std::int32_t dof_index0
226 = bs0 * cell_dofs0[pos0.quot] + pos0.rem;
227 const std::int32_t dof_index1
228 = bs1 * cell_dofs1[pos1.quot] + pos1.rem;
229 bc_dofs.push_back({dof_index0, dof_index1});
230 }
231 }
232 }
233 }
234
235 // Remove duplicates
236 std::ranges::sort(bc_dofs);
237 auto [unique_end, range_end] = std::ranges::unique(bc_dofs);
238 bc_dofs.erase(unique_end, range_end);
239
240 // Copy to separate array
241 std::array dofs = {std::vector<std::int32_t>(bc_dofs.size()),
242 std::vector<std::int32_t>(bc_dofs.size())};
243 std::ranges::transform(bc_dofs, dofs[0].begin(),
244 [](auto dof) { return dof[0]; });
245 std::ranges::transform(bc_dofs, dofs[1].begin(),
246 [](auto dof) { return dof[1]; });
247
248 return dofs;
249}
250
259template <dolfinx::scalar T, std::floating_point U = dolfinx::scalar_value_t<T>>
261{
262private:
265 std::size_t num_owned(const DofMap& dofmap,
266 std::span<const std::int32_t> dofs)
267 {
268 int bs = dofmap.index_map_bs();
269 std::int32_t map_size = dofmap.index_map->size_local();
270 std::int32_t owned_size = bs * map_size;
271 auto it = std::ranges::lower_bound(dofs, owned_size);
272 return std::ranges::distance(dofs.begin(), it);
273 }
274
275 // Throw if dofs are not strictly increasing, i.e. are unsorted or
276 // hold duplicates. O(dofs.size()), so compiled away in Release.
277 static void check_dofs_strictly_increasing(
278 [[maybe_unused]] std::span<const std::int32_t> dofs)
279 {
280#ifndef NDEBUG
281 auto it = std::ranges::adjacent_find(dofs, std::ranges::greater_equal());
282 if (it != dofs.end())
283 {
284 throw std::invalid_argument(std::format(
285 "DirichletBC dofs must be sorted and free of duplicates, but entry "
286 "{} ({}) does not exceed the preceding entry ({}).",
287 std::ranges::distance(dofs.begin(), it) + 1, *(it + 1), *it));
288 }
289#endif
290 }
291
293 static std::vector<std::int32_t>
294 unroll_dofs(std::span<const std::int32_t> dofs, int bs)
295 {
296 std::vector<std::int32_t> dofs_unrolled(bs * dofs.size());
297 for (std::size_t i = 0; i < dofs.size(); ++i)
298 for (int k = 0; k < bs; ++k)
299 dofs_unrolled[bs * i + k] = bs * dofs[i] + k;
300 return dofs_unrolled;
301 }
302
303public:
322 template <typename S, typename X>
323 requires(std::is_convertible_v<S, T>
324 || std::is_convertible_v<S, std::span<const T>>)
325 && std::is_convertible_v<std::remove_cvref_t<X>,
326 std::vector<std::int32_t>>
327 DirichletBC(const S& g, X&& dofs, std::shared_ptr<const FunctionSpace<U>> V)
328 : DirichletBC(std::make_shared<Constant<T>>(g), dofs, V)
329 {
330 }
331
348 template <typename X>
349 requires std::is_convertible_v<std::remove_cvref_t<X>,
350 std::vector<std::int32_t>>
351 DirichletBC(std::shared_ptr<const Constant<T>> g, X&& dofs,
352 std::shared_ptr<const FunctionSpace<U>> V)
353 : _function_space(V), _g(g), _dofs0(std::forward<X>(dofs))
354 {
355 assert(g);
356 assert(V);
357 assert(V->elements(0));
358 if (g->shape.size() != V->elements(0)->value_shape().size())
359 {
360 throw std::invalid_argument(
361 "Rank mismatch between Constant and function space in DirichletBC");
362 }
363
364 if (g->value.size()
365 != (std::size_t)_function_space->dofmaps().front()->bs())
366 {
367 throw std::invalid_argument(
368 "Creating a DirichletBC using a Constant is not supported when the "
369 "Constant size is not equal to the block size of the constrained "
370 "(sub-)space. Use a fem::Function to create the fem::DirichletBC.");
371 }
372
373 if (!V->elements(0)->interpolation_ident())
374 {
375 throw std::invalid_argument(
376 "Constant can be used only with point-evaluation elements");
377 }
378
379 // Unroll _dofs0 if dofmap block size > 1
380 if (const int bs = V->dofmaps().front()->bs(); bs > 1)
381 _dofs0 = unroll_dofs(_dofs0, bs);
382
383 check_dofs_strictly_increasing(_dofs0);
384
385 _owned_indices0 = num_owned(*_function_space->dofmaps().front(), _dofs0);
386 }
387
401 template <typename X>
402 requires std::is_convertible_v<std::remove_cvref_t<X>,
403 std::vector<std::int32_t>>
404 DirichletBC(std::shared_ptr<const Function<T, U>> g, X&& dofs)
405 : _function_space(g->function_space()), _g(g),
406 _dofs0(std::forward<X>(dofs))
407 {
408 assert(_function_space);
409
410 // Unroll _dofs0 if dofmap block size > 1
411 if (const int bs = _function_space->dofmaps().front()->bs(); bs > 1)
412 _dofs0 = unroll_dofs(_dofs0, bs);
413
414 check_dofs_strictly_increasing(_dofs0);
415
416 _owned_indices0 = num_owned(*_function_space->dofmaps().front(), _dofs0);
417 }
418
441 template <typename X>
442 DirichletBC(std::shared_ptr<const Function<T, U>> g, X&& V_g_dofs,
443 std::shared_ptr<const FunctionSpace<U>> V)
444 : _function_space(V), _g(g),
445 _dofs0(std::forward<typename std::remove_reference_t<X>::value_type>(
446 V_g_dofs[0])),
447 _dofs1_g(std::forward<typename std::remove_reference_t<X>::value_type>(
448 V_g_dofs[1])),
449 _owned_indices0(num_owned(*_function_space->dofmap(), _dofs0))
450 {
451 check_dofs_strictly_increasing(_dofs0);
452 }
453
456 DirichletBC(const DirichletBC& bc) = default;
457
460 DirichletBC(DirichletBC&& bc) = default;
461
463 ~DirichletBC() = default;
464
467 DirichletBC& operator=(const DirichletBC& bc) = default;
468
471
474 std::shared_ptr<const FunctionSpace<U>> function_space() const
475 {
476 return _function_space;
477 }
478
481 std::variant<std::shared_ptr<const Function<T, U>>,
482 std::shared_ptr<const Constant<T>>>
483 value() const
484 {
485 return _g;
486 }
487
504 std::pair<std::span<const std::int32_t>, std::int32_t> dof_indices() const
505 {
506 return {_dofs0, _owned_indices0};
507 }
508
534 void set(std::span<T> x, std::optional<std::span<const T>> x0,
535 T alpha = 1) const
536 {
537 // set_fn is a lambda which gets evaluated for every index in [0,
538 // _dofs0.size()) and its result is assigned to x[_dofs0[i]].
539 auto apply = [this, &x](std::invocable<std::int32_t> auto set_fn)
540 {
541 static_assert(
542 std::is_same_v<std::invoke_result_t<decltype(set_fn), std::int32_t>,
543 T>);
544
545 std::int32_t x_size = x.size();
546 for (std::size_t i = 0; i < _dofs0.size(); ++i)
547 {
548 if (_dofs0[i] < x_size)
549 x[_dofs0[i]] = set_fn(i);
550 }
551 };
552
553 if (alpha == T(0)) // Optimisation for when alpha == 0
554 {
555 apply([](std::int32_t) -> T { return 0; });
556 return;
557 }
558
559 if (std::holds_alternative<std::shared_ptr<const Function<T, U>>>(_g))
560 {
561 auto g = std::get<std::shared_ptr<const Function<T, U>>>(_g);
562 assert(g);
563 std::span<const T> values = g->x()->array();
564
565 // Extract degrees of freedom associated with g. If g is in a collapsed
566 // sub-space, get the dofs in this space, otherwise the degrees of g is
567 // the same as for x
568 auto dofs_g = _dofs1_g.empty() ? std::span(_dofs0) : std::span(_dofs1_g);
569
570 if (x0)
571 {
572 assert(x.size() <= x0->size());
573 apply(
574 [dofs_g, x0 = *x0, alpha, values,
575 &dofs0 = this->_dofs0](std::int32_t i) -> T
576 {
577 assert(dofs_g[i] < static_cast<std::int32_t>(values.size()));
578 return alpha * (values[dofs_g[i]] - x0[dofs0[i]]);
579 });
580 }
581 else
582 {
583 apply(
584 [dofs_g, values, alpha](std::int32_t i) -> T
585 {
586 assert(dofs_g[i] < static_cast<std::int32_t>(values.size()));
587 return alpha * values[dofs_g[i]];
588 });
589 }
590 }
591 else if (std::holds_alternative<std::shared_ptr<const Constant<T>>>(_g))
592 {
593 auto g = std::get<std::shared_ptr<const Constant<T>>>(_g);
594 const std::vector<T>& value = g->value;
595 std::int32_t bs = _function_space->dofmaps().front()->bs();
596 if (x0)
597 {
598 assert(x.size() <= x0->size());
599 apply(
600 [x0 = *x0, alpha, bs, &value, &dofs0 = _dofs0](std::int32_t i) -> T
601 {
602 auto dof = dofs0[i];
603 return alpha * (value[dof % bs] - x0[dof]);
604 });
605 }
606 else
607 {
608 apply([alpha, bs, &value, &dofs0 = _dofs0](std::int32_t i) -> T
609 { return alpha * value[dofs0[i] % bs]; });
610 }
611 }
612 else
613 {
614 // replace with std::unreachable once C++23 is supported
615 assert(false);
616 }
617 }
618
628 void mark_dofs(std::span<std::int8_t> markers) const
629 {
630#ifndef NDEBUG
631 if (!_dofs0.empty()
632 and *std::ranges::max_element(_dofs0) >= (std::int32_t)markers.size())
633 {
634 throw std::invalid_argument("Marker array is too short for the boundary "
635 "condition dofs.");
636 }
637#endif
638 for (std::int32_t idx : _dofs0)
639 markers[idx] = true;
640 }
641
642private:
643 // The function space (possibly a sub function space)
644 std::shared_ptr<const FunctionSpace<U>> _function_space;
645
646 // The function
647 std::variant<std::shared_ptr<const Function<T, U>>,
648 std::shared_ptr<const Constant<T>>>
649 _g;
650
651 // Dof indices (_dofs0) in _function_space and (_dofs1_g) in the space
652 // of _g. _dofs1_g may be empty if _dofs0 can be re-used
653 std::vector<std::int32_t> _dofs0, _dofs1_g;
654
655 // The first _owned_indices in _dofs are owned by this process
656 std::int32_t _owned_indices0 = -1;
657};
658} // namespace dolfinx::fem
Degree-of-freedom map representations and tools.
Constant (in space) value which can be attached to a Form.
Definition Constant.h:22
DirichletBC(const S &g, X &&dofs, std::shared_ptr< const FunctionSpace< U > > V)
Create a representation of a Dirichlet boundary condition constrained by a scalar- or vector-valued c...
Definition DirichletBC.h:327
DirichletBC & operator=(const DirichletBC &bc)=default
DirichletBC(std::shared_ptr< const Constant< T > > g, X &&dofs, std::shared_ptr< const FunctionSpace< U > > V)
Create a representation of a Dirichlet boundary condition constrained by a fem::Constant.
Definition DirichletBC.h:351
void set(std::span< T > x, std::optional< std::span< const T > > x0, T alpha=1) const
Set entries in an array that are constrained by Dirichlet boundary conditions.
Definition DirichletBC.h:534
std::variant< std::shared_ptr< const Function< T, U > >, std::shared_ptr< const Constant< T > > > value() const
Definition DirichletBC.h:483
DirichletBC(const DirichletBC &bc)=default
DirichletBC(DirichletBC &&bc)=default
std::pair< std::span< const std::int32_t >, std::int32_t > dof_indices() const
Dof indices constrained by this condition, and the position of the first ghost index.
Definition DirichletBC.h:504
std::shared_ptr< const FunctionSpace< U > > function_space() const
Definition DirichletBC.h:474
~DirichletBC()=default
Destructor.
DirichletBC(std::shared_ptr< const Function< T, U > > g, X &&dofs)
Create a representation of a Dirichlet boundary condition where the space being constrained is the sa...
Definition DirichletBC.h:404
DirichletBC & operator=(DirichletBC &&bc)=default
Move assignment operator.
void mark_dofs(std::span< std::int8_t > markers) const
Set markers[i] = true if dof i has a boundary condition applied.
Definition DirichletBC.h:628
DirichletBC(std::shared_ptr< const Function< T, U > > g, X &&V_g_dofs, std::shared_ptr< const FunctionSpace< U > > V)
Create a representation of a Dirichlet boundary condition where the space being constrained and the f...
Definition DirichletBC.h:442
Degree-of-freedom map.
Definition DofMap.h:73
std::shared_ptr< const common::IndexMap > index_map
Index map that describes the parallel distribution of the dofmap.
Definition DofMap.h:164
int index_map_bs() const
Block size associated with the index_map.
Definition DofMap.cpp:307
This class represents a finite element function space defined by a mesh, a finite element,...
Definition FunctionSpace.h:36
std::shared_ptr< const FiniteElement< geometry_type > > elements(int cell_type_idx) const
The finite elements.
Definition FunctionSpace.h:402
std::shared_ptr< const DofMap > dofmap() const
The dofmap.
Definition FunctionSpace.h:408
std::shared_ptr< const mesh::Mesh< geometry_type > > mesh() const
The mesh.
Definition FunctionSpace.h:383
std::shared_ptr< const FiniteElement< geometry_type > > element() const
The finite element.
Definition FunctionSpace.h:389
std::vector< geometry_type > tabulate_dof_coordinates(bool transpose) const
Tabulate the physical coordinates of all dofs on this process.
Definition FunctionSpace.h:242
Definition Function.h:44
Finite element method functionality.
Definition assemble_expression_impl.h:28
std::vector< std::int32_t > locate_dofs_geometrical(const FunctionSpace< T > &V, U marker_fn)
Find degrees of freedom whose geometric coordinate is true for the provided marking function.
Definition DirichletBC.h:106
std::vector< std::int32_t > locate_dofs_topological(const mesh::Topology &topology, const DofMap &dofmap, int dim, std::span< const std::int32_t > entities, bool remote=true)
Find degrees-of-freedom which belong to the provided mesh entities (topological).
Definition DirichletBC.cpp:202
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32