deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
manifold_lib.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2014 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13
14#include <deal.II/base/config.h>
15
17
18#ifdef DEAL_II_WITH_OPENCASCADE
19
20
21# include <BRepAdaptor_CompCurve.hxx>
22# include <BRepAdaptor_Curve.hxx>
23# if !DEAL_II_OPENCASCADE_VERSION_GTE(7, 6, 0)
24# include <BRepAdaptor_HCompCurve.hxx>
25# include <BRepAdaptor_HCurve.hxx>
26# endif
27# include <BRepTools.hxx>
28# include <BRep_Tool.hxx>
29# include <GCPnts_AbscissaPoint.hxx>
30# include <ShapeAnalysis_Curve.hxx>
31# include <ShapeAnalysis_Surface.hxx>
32# include <TopoDS.hxx>
33# if !DEAL_II_OPENCASCADE_VERSION_GTE(7, 0, 0)
34# include <Handle_Adaptor3d_HCurve.hxx>
35# endif
36
37// OpenCASCADE defines a macro that creates class names. When building
38// modules, we don't #include any of the OpenCASCADE header files, and
39// we can't export macros, so the macro is not available. Re-declare
40// it here if it is not defined. (And if it is defined, we have a
41// problem: Apparently some header #include above is left in the file
42// by accident. Error out in that case so we get alerted to the
43// problem.)
44# ifdef DEAL_II_BUILDING_CXX20_MODULE
45# ifndef HANDLE
46# define Handle(ClassName) Handle_##ClassName
47# else
48# error \
49 "Some OpenCASCADE header file is apparently included even when building modules!"
50# endif
51# endif
52
53#endif
54
56
57#ifdef DEAL_II_WITH_OPENCASCADE
58
59
60namespace OpenCASCADE
61{
62 namespace
63 {
69# if DEAL_II_OPENCASCADE_VERSION_GTE(7, 6, 0)
70 Handle_Adaptor3d_Curve
71 curve_adaptor(const TopoDS_Shape &shape)
72 {
73 Assert((shape.ShapeType() == TopAbs_WIRE) ||
74 (shape.ShapeType() == TopAbs_EDGE),
76 if (shape.ShapeType() == TopAbs_WIRE)
77 return Handle(BRepAdaptor_CompCurve)(
78 new BRepAdaptor_CompCurve(TopoDS::Wire(shape)));
79 else if (shape.ShapeType() == TopAbs_EDGE)
80 return Handle(BRepAdaptor_Curve)(
81 new BRepAdaptor_Curve(TopoDS::Edge(shape)));
82
84 return Handle(BRepAdaptor_Curve)(new BRepAdaptor_Curve());
85 }
86# else
87 Handle_Adaptor3d_HCurve
88 curve_adaptor(const TopoDS_Shape &shape)
89 {
90 Assert((shape.ShapeType() == TopAbs_WIRE) ||
91 (shape.ShapeType() == TopAbs_EDGE),
93 if (shape.ShapeType() == TopAbs_WIRE)
94 return Handle(BRepAdaptor_HCompCurve)(
95 new BRepAdaptor_HCompCurve(TopoDS::Wire(shape)));
96 else if (shape.ShapeType() == TopAbs_EDGE)
97 return Handle(BRepAdaptor_HCurve)(
98 new BRepAdaptor_HCurve(TopoDS::Edge(shape)));
99
101 return Handle(BRepAdaptor_HCurve)(new BRepAdaptor_HCurve());
102 }
103# endif
104
105
106
107 // Helper internal functions.
108 double
109 shape_length(const TopoDS_Shape &sh)
110 {
111# if DEAL_II_OPENCASCADE_VERSION_GTE(7, 6, 0)
112 Handle_Adaptor3d_Curve adapt = curve_adaptor(sh);
113 return GCPnts_AbscissaPoint::Length(*adapt);
114# else
115 Handle_Adaptor3d_HCurve adapt = curve_adaptor(sh);
116 return GCPnts_AbscissaPoint::Length(adapt->GetCurve());
117# endif
118 }
119 } // namespace
120
121 /*======================= NormalProjectionManifold =========================*/
122 template <int dim, int spacedim>
124 const TopoDS_Shape &sh,
125 const double tolerance)
126 : sh(sh)
127 , tolerance(tolerance)
128 {
129 Assert(spacedim == 3, ExcNotImplemented());
130 }
131
132
133
134 template <int dim, int spacedim>
135 std::unique_ptr<Manifold<dim, spacedim>>
137 {
138 return std::unique_ptr<Manifold<dim, spacedim>>(
139 new NormalProjectionManifold(sh, tolerance));
140 }
141
142
143
144 template <int dim, int spacedim>
147 const ArrayView<const Point<spacedim>> &surrounding_points,
148 const Point<spacedim> &candidate) const
149 {
150 (void)surrounding_points;
151 if constexpr (running_in_debug_mode())
152 {
153 for (unsigned int i = 0; i < surrounding_points.size(); ++i)
154 Assert(closest_point(sh, surrounding_points[i], tolerance)
155 .distance(surrounding_points[i]) <
156 std::max(tolerance * surrounding_points[i].norm(),
157 tolerance),
158 ExcPointNotOnManifold<spacedim>(surrounding_points[i]));
159 }
160 return closest_point(sh, candidate, tolerance);
161 }
162
163
164 /*===================== DirectionalProjectionManifold ======================*/
165 template <int dim, int spacedim>
167 const TopoDS_Shape &sh,
168 const Tensor<1, spacedim> &direction,
169 const double tolerance)
170 : sh(sh)
171 , direction(direction)
172 , tolerance(tolerance)
173 {
174 Assert(spacedim == 3, ExcNotImplemented());
175 }
176
177
178
179 template <int dim, int spacedim>
180 std::unique_ptr<Manifold<dim, spacedim>>
182 {
183 return std::unique_ptr<Manifold<dim, spacedim>>(
184 new DirectionalProjectionManifold(sh, direction, tolerance));
185 }
186
187
188
189 template <int dim, int spacedim>
192 const ArrayView<const Point<spacedim>> &surrounding_points,
193 const Point<spacedim> &candidate) const
194 {
195 (void)surrounding_points;
196 if constexpr (running_in_debug_mode())
197 {
198 for (unsigned int i = 0; i < surrounding_points.size(); ++i)
199 Assert(closest_point(sh, surrounding_points[i], tolerance)
200 .distance(surrounding_points[i]) <
201 std::max(tolerance * surrounding_points[i].norm(),
202 tolerance),
203 ExcPointNotOnManifold<spacedim>(surrounding_points[i]));
204 }
205 return line_intersection(sh, candidate, direction, tolerance);
206 }
207
208
209
210 /*===================== NormalToMeshProjectionManifold =====================*/
211 template <int dim, int spacedim>
213 const TopoDS_Shape &sh,
214 const double tolerance)
215 : sh(sh)
216 , tolerance(tolerance)
217 {
218 Assert(spacedim == 3, ExcNotImplemented());
219 Assert(
220 std::get<0>(count_elements(sh)) > 0,
222 "NormalToMeshProjectionManifold needs a shape containing faces to operate."));
223 }
224
225 template <int dim, int spacedim>
226 std::unique_ptr<Manifold<dim, spacedim>>
228 {
229 return std::unique_ptr<Manifold<dim, spacedim>>(
231 }
232
233
234 namespace
235 {
236 template <int spacedim>
238 internal_project_to_manifold(const TopoDS_Shape &,
239 const double,
240 const ArrayView<const Point<spacedim>> &,
241 const Point<spacedim> &)
242 {
244 return {};
245 }
246
247 template <>
249 internal_project_to_manifold(
250 const TopoDS_Shape &sh,
251 const double tolerance,
252 const ArrayView<const Point<3>> &surrounding_points,
253 const Point<3> &candidate)
254 {
255 constexpr int spacedim = 3;
256 TopoDS_Shape out_shape;
257 Tensor<1, spacedim> average_normal;
258 if constexpr (running_in_debug_mode())
259 {
260 for (const auto &point : surrounding_points)
261 {
262 Assert(closest_point(sh, point, tolerance).distance(point) <
263 std::max(tolerance * point.norm(), tolerance),
264 ExcPointNotOnManifold<spacedim>(point));
265 }
266 }
267
268 switch (surrounding_points.size())
269 {
270 case 2:
271 {
272 for (const auto &point : surrounding_points)
273 {
274 std::tuple<Point<3>, Tensor<1, 3>, double, double>
275 p_and_diff_forms =
277 point,
278 tolerance);
279 average_normal += std::get<1>(p_and_diff_forms);
280 }
281
282 average_normal /= 2.0;
283
284 Assert(
285 average_normal.norm() > 1e-4,
287 "Failed to refine cell: the average of the surface normals at the surrounding edge turns out to be a null vector, making the projection direction undetermined."));
288
289 Tensor<1, 3> T = surrounding_points[0] - surrounding_points[1];
290 T /= T.norm();
291 average_normal = average_normal - (average_normal * T) * T;
292 average_normal /= average_normal.norm();
293 break;
294 }
295 case 4:
296 {
297 Tensor<1, 3> u = surrounding_points[1] - surrounding_points[0];
298 Tensor<1, 3> v = surrounding_points[2] - surrounding_points[0];
299 const double n1_coords[3] = {u[1] * v[2] - u[2] * v[1],
300 u[2] * v[0] - u[0] * v[2],
301 u[0] * v[1] - u[1] * v[0]};
302 Tensor<1, 3> n1(n1_coords);
303 n1 = n1 / n1.norm();
304 u = surrounding_points[2] - surrounding_points[3];
305 v = surrounding_points[1] - surrounding_points[3];
306 const double n2_coords[3] = {u[1] * v[2] - u[2] * v[1],
307 u[2] * v[0] - u[0] * v[2],
308 u[0] * v[1] - u[1] * v[0]};
309 Tensor<1, 3> n2(n2_coords);
310 n2 = n2 / n2.norm();
311
312 average_normal = (n1 + n2) / 2.0;
313
314 Assert(
315 average_normal.norm() > tolerance,
317 "Failed to refine cell: the normal estimated via the surrounding points turns out to be a null vector, making the projection direction undetermined."));
318
319 average_normal /= average_normal.norm();
320 break;
321 }
322 case 8:
323 {
324 Tensor<1, 3> u = surrounding_points[1] - surrounding_points[0];
325 Tensor<1, 3> v = surrounding_points[2] - surrounding_points[0];
326 const double n1_coords[3] = {u[1] * v[2] - u[2] * v[1],
327 u[2] * v[0] - u[0] * v[2],
328 u[0] * v[1] - u[1] * v[0]};
329 Tensor<1, 3> n1(n1_coords);
330 n1 = n1 / n1.norm();
331 u = surrounding_points[2] - surrounding_points[3];
332 v = surrounding_points[1] - surrounding_points[3];
333 const double n2_coords[3] = {u[1] * v[2] - u[2] * v[1],
334 u[2] * v[0] - u[0] * v[2],
335 u[0] * v[1] - u[1] * v[0]};
336 Tensor<1, 3> n2(n2_coords);
337 n2 = n2 / n2.norm();
338 u = surrounding_points[4] - surrounding_points[7];
339 v = surrounding_points[6] - surrounding_points[7];
340 const double n3_coords[3] = {u[1] * v[2] - u[2] * v[1],
341 u[2] * v[0] - u[0] * v[2],
342 u[0] * v[1] - u[1] * v[0]};
343 Tensor<1, 3> n3(n3_coords);
344 n3 = n3 / n3.norm();
345 u = surrounding_points[6] - surrounding_points[7];
346 v = surrounding_points[5] - surrounding_points[7];
347 const double n4_coords[3] = {u[1] * v[2] - u[2] * v[1],
348 u[2] * v[0] - u[0] * v[2],
349 u[0] * v[1] - u[1] * v[0]};
350 Tensor<1, 3> n4(n4_coords);
351 n4 = n4 / n4.norm();
352
353 average_normal = (n1 + n2 + n3 + n4) / 4.0;
354
355 Assert(
356 average_normal.norm() > tolerance,
358 "Failed to refine cell: the normal estimated via the surrounding points turns out to be a null vector, making the projection direction undetermined."));
359
360 average_normal /= average_normal.norm();
361 break;
362 }
363 default:
364 {
365 // Given an arbitrary number of points we compute all the possible
366 // normal vectors
367 for (unsigned int i = 0; i < surrounding_points.size(); ++i)
368 for (unsigned int j = 0; j < surrounding_points.size(); ++j)
369 if (j != i)
370 for (unsigned int k = 0; k < surrounding_points.size(); ++k)
371 if (k != j && k != i)
372 {
373 Tensor<1, 3> u =
374 surrounding_points[i] - surrounding_points[j];
375 Tensor<1, 3> v =
376 surrounding_points[i] - surrounding_points[k];
377 const double n_coords[3] = {u[1] * v[2] - u[2] * v[1],
378 u[2] * v[0] - u[0] * v[2],
379 u[0] * v[1] -
380 u[1] * v[0]};
381 Tensor<1, 3> n1(n_coords);
382 if (n1.norm() > tolerance)
383 {
384 n1 = n1 / n1.norm();
385 if (average_normal.norm() < tolerance)
386 average_normal = n1;
387 else
388 {
389 auto dot_prod = n1 * average_normal;
390 // We check that the direction of the normal
391 // vector w.r.t the current average, and make
392 // sure we flip it if it is opposite
393 if (dot_prod > 0)
394 average_normal += n1;
395 else
396 average_normal -= n1;
397 }
398 }
399 }
400 Assert(
401 average_normal.norm() > tolerance,
403 "Failed to compute a normal: the normal estimated via the surrounding points turns out to be a null vector, making the projection direction undetermined."));
404 average_normal = average_normal / average_normal.norm();
405 break;
406 }
407 }
408
409 return line_intersection(sh, candidate, average_normal, tolerance);
410 }
411 } // namespace
412
413
414 template <int dim, int spacedim>
417 const ArrayView<const Point<spacedim>> &surrounding_points,
418 const Point<spacedim> &candidate) const
419 {
420 return internal_project_to_manifold(sh,
421 tolerance,
422 surrounding_points,
423 candidate);
424 }
425
426
427 /*==================== ArclengthProjectionLineManifold =====================*/
428 template <int dim, int spacedim>
430 ArclengthProjectionLineManifold(const TopoDS_Shape &sh,
431 const double tolerance)
432 :
433
434 ChartManifold<dim, spacedim, 1>(sh.Closed() ? Point<1>(shape_length(sh)) :
435 Point<1>())
436 , sh(sh)
437 , curve(curve_adaptor(sh))
438 , tolerance(tolerance)
439 , length(shape_length(sh))
440 {
441 Assert(spacedim >= 2, ExcImpossibleInDimSpacedim(dim, spacedim));
442 }
443
444
445
446 template <int dim, int spacedim>
447 std::unique_ptr<Manifold<dim, spacedim>>
449 {
450 return std::unique_ptr<Manifold<dim, spacedim>>(
451 new ArclengthProjectionLineManifold(sh, tolerance));
452 }
453
454
455
456 template <int dim, int spacedim>
459 const Point<spacedim> &space_point) const
460 {
461 double t(0.0);
462 ShapeAnalysis_Curve curve_analysis;
463 gp_Pnt proj;
464
465 const double dist = curve_analysis.Project(
467 *curve, point(space_point), tolerance, proj, t, true);
468# else
469 curve->GetCurve(), point(space_point), tolerance, proj, t, true);
470# endif
471
472 (void)dist;
473 Assert(dist < tolerance * length,
474 ExcPointNotOnManifold<spacedim>(space_point));
475
476 return Point<1>(GCPnts_AbscissaPoint::Length(
478 *curve, curve->FirstParameter(), t));
479# else
480 curve->GetCurve(), curve->GetCurve().FirstParameter(), t));
481# endif
482 }
483
484
485
486 template <int dim, int spacedim>
489 const Point<1> &chart_point) const
490 {
491# if DEAL_II_OPENCASCADE_VERSION_GTE(7, 6, 0)
492 GCPnts_AbscissaPoint AP(*curve, chart_point[0], curve->FirstParameter());
493 gp_Pnt P = curve->Value(AP.Parameter());
494# else
495 GCPnts_AbscissaPoint AP(curve->GetCurve(),
496 chart_point[0],
497 curve->GetCurve().FirstParameter());
498 gp_Pnt P = curve->GetCurve().Value(AP.Parameter());
499# endif
500
501 return point<spacedim>(P);
502 }
503
504 template <int dim, int spacedim>
506 const double tolerance)
507 : face(face)
508 , tolerance(tolerance)
509 {}
510
511
512
513 template <int dim, int spacedim>
514 std::unique_ptr<Manifold<dim, spacedim>>
516 {
517 return std::unique_ptr<Manifold<dim, spacedim>>(
518 new NURBSPatchManifold<dim, spacedim>(face, tolerance));
519 }
520
521
522
523 template <int dim, int spacedim>
526 const Point<spacedim> &space_point) const
527 {
528 Handle(Geom_Surface) SurfToProj = BRep_Tool::Surface(face);
529
530 ShapeAnalysis_Surface projector(SurfToProj);
531 gp_Pnt2d proj_params = projector.ValueOfUV(point(space_point), tolerance);
532
533 double u = proj_params.X();
534 double v = proj_params.Y();
535
536 return {u, v};
537 }
538
539 template <int dim, int spacedim>
542 const Point<2> &chart_point) const
543 {
544 return ::OpenCASCADE::push_forward<spacedim>(face,
545 chart_point[0],
546 chart_point[1]);
547 }
548
549 template <int dim, int spacedim>
552 const Point<2> &chart_point) const
553 {
555 Handle(Geom_Surface) surf = BRep_Tool::Surface(face);
556
557 gp_Pnt q;
558 gp_Vec Du, Dv;
559 surf->D1(chart_point[0], chart_point[1], q, Du, Dv);
560
561 DX[0][0] = Du.X();
562 DX[1][0] = Du.Y();
563 if (spacedim > 2)
564 DX[2][0] = Du.Z();
565 else
566 Assert(std::abs(Du.Z()) < tolerance,
568 "Expecting derivative along Z to be zero! Bailing out."));
569 DX[0][1] = Dv.X();
570 DX[1][1] = Dv.Y();
571 if (spacedim > 2)
572 DX[2][1] = Dv.Z();
573 else
574 Assert(std::abs(Dv.Z()) < tolerance,
576 "Expecting derivative along Z to be zero! Bailing out."));
577 return DX;
578 }
579
580 template <int dim, int spacedim>
581 std::tuple<double, double, double, double>
583 {
584 Standard_Real umin, umax, vmin, vmax;
585 BRepTools::UVBounds(face, umin, umax, vmin, vmax);
586 return std::make_tuple(umin, umax, vmin, vmax);
587 }
588
589// We don't build the .inst file if deal.II isn't configured
590// with GMSH, but doxygen doesn't know that and tries to find that
591// file anyway for parsing -- which then of course it fails on. So
592// exclude the following from doxygen consideration.
593# ifndef DOXYGEN
594# include "opencascade/manifold_lib.inst"
595# endif
596} // end namespace OpenCASCADE
597
598
599#endif
600
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
virtual Point< spacedim > push_forward(const Point< 1 > &chart_point) const override
ArclengthProjectionLineManifold(const TopoDS_Shape &sh, const double tolerance=1e-7)
virtual Point< 1 > pull_back(const Point< spacedim > &space_point) const override
virtual Point< spacedim > project_to_manifold(const ArrayView< const Point< spacedim > > &surrounding_points, const Point< spacedim > &candidate) const override
DirectionalProjectionManifold(const TopoDS_Shape &sh, const Tensor< 1, spacedim > &direction, const double tolerance=1e-7)
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
std::tuple< double, double, double, double > get_uv_bounds() const
virtual Point< 2 > pull_back(const Point< spacedim > &space_point) const override
virtual Point< spacedim > push_forward(const Point< 2 > &chart_point) const override
NURBSPatchManifold(const TopoDS_Face &face, const double tolerance=1e-7)
virtual DerivativeForm< 1, 2, spacedim > push_forward_gradient(const Point< 2 > &chart_point) const override
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
virtual Point< spacedim > project_to_manifold(const ArrayView< const Point< spacedim > > &surrounding_points, const Point< spacedim > &candidate) const override
NormalProjectionManifold(const TopoDS_Shape &sh, const double tolerance=1e-7)
virtual Point< spacedim > project_to_manifold(const ArrayView< const Point< spacedim > > &surrounding_points, const Point< spacedim > &candidate) const override
virtual std::unique_ptr< Manifold< dim, spacedim > > clone() const override
NormalToMeshProjectionManifold(const TopoDS_Shape &sh, const double tolerance=1e-7)
Definition point.h:111
numbers::NumberTraits< Number >::real_type norm() const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_OPENCASCADE_VERSION_GTE(major, minor, subminor)
Definition config.h:496
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
static ::ExceptionBase & ExcImpossibleInDimSpacedim(int arg1, int arg2)
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
static ::ExceptionBase & ExcUnsupportedShape()
static ::ExceptionBase & ExcMessage(std::string arg1)
constexpr char T
std::tuple< unsigned int, unsigned int, unsigned int > count_elements(const TopoDS_Shape &shape)
Definition utilities.cc:110
std::tuple< Point< 3 >, Tensor< 1, 3 >, double, double > closest_point_and_differential_forms(const TopoDS_Shape &in_shape, const Point< 3 > &origin, const double tolerance=1e-7)
Definition utilities.cc:817
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
Definition utilities.cc:210
Point< dim > closest_point(const TopoDS_Shape &in_shape, const Point< dim > &origin, const double tolerance=1e-7)
Definition utilities.cc:807
Point< dim > line_intersection(const TopoDS_Shape &in_shape, const Point< dim > &origin, const Tensor< 1, dim > &direction, const double tolerance=1e-7)
Definition utilities.cc:532
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)