KASKADE 7 development version
contactConstraints.hh
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the library KASKADE 7 */
4/* https://www.zib.de/research/projects/kaskade7-finite-element-toolbox */
5/* */
6/* Copyright (C) 2017-2023 Zuse Institute Berlin */
7/* */
8/* KASKADE 7 is distributed under the terms of the ZIB Academic License. */
9/* see $KASKADE/academic.txt */
10/* */
11/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12
13#ifndef CONTACTCONSTRAINTS
14#define CONTACTCONSTRAINTS
15
16#include <algorithm>
17#include <limits>
18#include <mutex>
19#include <optional>
20
21#include <dune/common/fvector.hh>
22#include "dune/istl/bvector.hh"
23
25#include "fem/gridBasics.hh"
26#include "fem/quadrature.hh"
28#include "utilities/bezier.hh"
30
31namespace Kaskade
32{
68 template <class Space, class DisplacedFace, class Displacement>
69 auto getContactConstraint(Space const& space,
71 DisplacedFace const& face,
73 double overlap)
74 {
75 using Grid = typename Space::Grid;
76 using ctype = typename Grid::ctype;
78 using EntryType = std::pair<size_t,RowEntry>;
79 using ReturnType = std::optional<std::pair<std::vector<EntryType>,ctype>>;
80
81 // Find intersections of faces with the ray starting at the given point and extending in normal direction.
82 // In a Newton loop, the current configuration need not be feasible (i.e. without overlap / interpenetration).
83 // Hence we pull back a little bit (on the order of a single element diameter) to detect a slight overlap as well.
84 auto n = face.unitOuterNormal(xi);
85
86
87 if (overlap == std::numeric_limits<double>::lowest())
88 {
89 ctype diameter = std::pow(face.volume(),1.0/DisplacedFace::facedimension);
90 overlap = 0.05*diameter;
91 }
92
93 auto const x = face.global(xi);
94 auto intersection = boundaryLocator.byRay(x-overlap*n,n);
95
96 if (!intersection) // no intersection found -> no constraint
97 return ReturnType();
98
99 auto [oface,oxi,calpha] = *intersection; // extract opposing boundary face, local point, and cos(alpha) of normals
100 auto const y = oface.global(oxi);
101
102 std::unique_lock lock(GridLocking<Grid>::mutex());
103 auto dofs = space.linearCombination( face.gridFace().inside(), face.gridFace().geometryInInside().global(xi));
104 auto odofs = space.linearCombination(oface.gridFace().inside(),oface.gridFace().geometryInInside().global(oxi));
105 lock.unlock();
106
107
108 std::vector<EntryType> entries;
109
110 RowEntry nt; nt[0] = n; // normal as a row vector
111
112 ctype const eps = std::numeric_limits<ctype>::epsilon();
113 for (auto const& p: dofs)
114 if (p.second.two_norm2() > 1e3*eps) // omit negligible contributions. Those are
115 entries.push_back(std::make_pair(p.first,p.second[0]*nt)); // typically structurally zero, but have rounding errors
116 for (auto const& p: odofs)
117 if (p.second.two_norm2() > 1e3*eps)
118 entries.push_back(std::make_pair(p.first,-p.second[0]*nt));
119
120// std::cout << "Constraint found:\n";
121// std::cout << "x = " << x << "\n";
122// std::cout << "x opposite = " << y << "\n";
123// std::cout << "n = " << n << "\n";
124// std::cout << "n opposite = " << oface.unitOuterNormal(oxi) << "\n";
125// std::cout << "cos(alpha) = " << calpha << "\n";
126// std::cout << "gap = " << (y-x)*n << "\n";
127// if ((y-x)*n < 0)
128// abort();
129
130 // compute the gap distance (can be negative in case of interpenetration)
131 return ReturnType(std::make_pair(entries,(y-x)*n));
132 }
133
149 template <class Space1, class DisplacedFace1, class Displacement1, int dimw1=Space1::GridView::Grid::dimension, class Space2, class Displacement2, int dimw2=Space2::GridView::Grid::dimension>
152 DisplacedFace1 const& face,
153 /*typename BoundaryLocator<typename Space1::GridView,Displacement1,Space1::GridView::Grid::dimension>::Face const& face,*/
155 double overlap,
156 int which=0)
157 {
158 using ctype = typename Space1::GridView::ctype;
159 using RowEntry = Dune::FieldMatrix<ctype,1,dimw1>;
160 using EntryType = std::pair<size_t,RowEntry>;
161 using ReturnType = std::optional<std::tuple<std::vector<EntryType>,std::vector<EntryType>,ctype>>;
162
163 constexpr int dim1 = Space1::GridView::Grid::dimension;
164 constexpr int dim2 = Space2::GridView::Grid::dimension;
165
166 // face on boundary belonging to boundaryLocator1
167 auto n = face.unitOuterNormal(xi);
168 if (which)
169 n = -n;
170
171 double volume;
172 if constexpr(dim1==dimw1) volume = face.gridFace().inside().geometry().volume(); //2D-2D or 3D-3D-contact: face is an intersection
173 else volume = face.gridFace().geometry().volume(); //2D grid in 3D world: face is a 2D cell
174 ctype diameter = std::pow(volume,1.0/dim1);
175 if(overlap == std::numeric_limits<double>::lowest())
176 overlap = 0.05*diameter;
177
178 // Find intersections of faces with the ray starting at the given point and extending in normal direction.
179 // In a Newton loop, the current configuration need not be feasible (i.e. without overlap / interpenetration).
180 // Hence we pull back a little bit (on the order of a single element diameter) to detect a slight overlap as well.
181 auto intersection = boundaryLocator2.byRay(face.global(xi)-overlap*n,n);
182
183 RowEntry nt; nt[0] = n; // normal as a row vector
184
185 if (!intersection) // no intersection found -> no constraint
186 return ReturnType();
187
188 // extract opposing boundary face and point
189 auto oface = std::get<0>(intersection.value());
190 auto oxi = std::get<1>(intersection.value());
191
192 std::vector<EntryType> entries1;
193 std::vector<EntryType> entries2;
194 ctype const eps = std::numeric_limits<ctype>::epsilon();
195
196
197 if constexpr(dim1==dimw1)
198 {
199 auto dofs = space1.linearCombination(face.gridFace().inside(),face.gridFace().geometryInInside().global(xi));
200 for (auto const& p: dofs)
201 if (p.second.two_norm2() > 1e3*eps) // omit negligible contributions. Those are
202 entries1.push_back(std::make_pair(p.first,p.second[0]*nt)); // typically structurally zero, but have rounding errors
203 }
204 else
205 {
206 auto dofs = space1.linearCombination(face,xi/*face.geometry().global(xi)*/);
207 for (auto const& p: dofs)
208 if (p.second.two_norm2() > 1e3*eps) // omit negligible contributions. Those are
209 entries1.push_back(std::make_pair(p.first,p.second[0]*nt)); // typically structurally zero, but have rounding errors
210 }
211
212 if constexpr(dim2==dimw2)
213 {
214 auto odofs = space2.linearCombination(oface.gridFace().inside(),oface.gridFace().geometryInInside().global(oxi));
215 for (auto const& p: odofs)
216 if (p.second.two_norm2() > 1e3*eps)
217 entries2.push_back(std::make_pair(p.first,-p.second[0]*nt));
218 }
219 else
220 {
221 auto odofs = space2.linearCombination(oface,oxi/*oface.geometry().global(oxi)*/);
222 for (auto const& p: odofs)
223 if (p.second.two_norm2() > 1e3*eps)
224 entries2.push_back(std::make_pair(p.first,-p.second[0]*nt));
225 }
226
227 auto x = face.global(xi);
228 auto y = oface.global(oxi);
229
230 // compute the gap distance (can be negative in case of interpenetration)
231 return ReturnType(std::make_tuple(entries1,entries2,(y-x)*n));
232 }
233
234 // ----------------------------------------------------------------------------------------------
235 // ----------------------------------------------------------------------------------------------
236
237
264 template <class Scalar, int dim>
265 class Mortar
266 {
267 public:
268
275 using Row = std::vector<std::pair<size_t,Entry>>;
276
277 virtual ~Mortar();
278
282 virtual int size(int n) const = 0;
283
303 Row& Gi, Scalar gi,
304 std::vector<Row>& B, std::vector<Scalar>& b) const = 0;
305 };
306
307 // ----------------------------------------------------------------------------------------------
308
316 template <class Scalar, int dim>
317 class DiracMortar: public Mortar<Scalar,dim>
318 {
319 public:
320 using typename Mortar<Scalar,dim>::Row;
321
325 virtual int size(int n) const { return n; }
326
331 Row& Gi, Scalar gi,
332 std::vector<Row>& B, std::vector<Scalar>& b) const;
333 };
334
336 namespace ContactConstraintsDetail
337 {
338 template <class Scalar, int dim>
339 constexpr DiracMortar<Scalar,dim> const& defaultMortar();
340 }
342
343 // ----------------------------------------------------------------------------------------------
344
353 template <class Scalar, int dim>
354 class BezierMortar: public Mortar<Scalar,dim>
355 {
356 public:
357 using typename Mortar<Scalar,dim>::Row;
358
365
369 virtual int size(int n) const;
370
375 Row& vic, Scalar g,
376 std::vector<Row>& rows, std::vector<Scalar>& bounds) const;
377 private:
378 int m;
379 };
380
381 // ----------------------------------------------------------------------------------------------
382
387 struct MortarB
388 {
392 enum class Type { Dirac, Bezier };
393
394
395 static constexpr MortarB dirac()
396 {
397 return MortarB{Type::Dirac,0};
398 }
399
400 static constexpr MortarB bezier(int order)
401 {
402 return MortarB{Type::Bezier,order};
403 }
404
407 };
408
409 // ----------------------------------------------------------------------------------------------
410 // ----------------------------------------------------------------------------------------------
411
412
427 template <int dim>
428 std::vector<Dune::QuadraturePoint<double,dim>> constraintPositions(Dune::GeometryType gt, int nodesPerEdge)
429 {
430 assert(nodesPerEdge>0);
431
432
433 using QP = Dune::QuadraturePoint<double,dim>;
434 std::vector<QP> pos;
435
436 if (gt.isLine())
437 {
438 if (nodesPerEdge==1)
439 {
440 Dune::FieldVector<double,dim> p{0.5}; // minimal number
441 pos.push_back(QP(p,1.0)); // -> just center position
442 }
443 else
444 {
445 for (int i=0; i<nodesPerEdge; ++i) // otherwise equidistant grid on [0,1] including
446 { // the end points
447 Dune::FieldVector<double,dim> p{i/(nodesPerEdge-1.0)}; // equidistant positions
448 double w = (i==0 || i+1==nodesPerEdge)? 0.5/(nodesPerEdge-1): 1.0/(nodesPerEdge-1); // and trapezoidal rule
449 pos.push_back(QP(p,w)); // Newton-Cotes does not appear to be really beneficial
450 }
451 }
452 }
453 else if (gt.isTriangle())
454 {
455 if (nodesPerEdge==1)
456 {
457 Dune::FieldVector<double,dim> p{1.0/3,1.0/3}; // minimal number of constraints per triangle
458 pos.push_back(QP(p,1.0)); // -> just center position
459 }
460 else
461 {
462 double w = 2.0/(nodesPerEdge*(nodesPerEdge+1));
463 for (int ix=0; ix<nodesPerEdge; ++ix) // otherwise equidistant cartesian grid of points
464 {
465 double x = ix/(nodesPerEdge-1.0);
466 for (int iy=0; iy<nodesPerEdge-ix; ++iy) // with required number of points per edge
467 {
468 double y = iy/(nodesPerEdge-1.0);
470 pos.push_back(QP(p,w)); // and including the vertices (closed formula)
471 }
472 }
473 }
474 }
475 else
476 {
477 std::cerr << "unsupported geometry type of face!\n";
478 abort();
479 }
480
481 return pos;
482 }
483
484 // ----------------------------------------------------------------------------------------------
485
503 template <class Space, class Displacement>
504 auto getContactConstraints(Space const& space,
506 int pointsPerEdge, double overlap = std::numeric_limits<double>::lowest(),
507 Mortar<typename Space::Grid::ctype,Space::Grid::dimension> const& mortar = ContactConstraintsDetail::defaultMortar<typename Space::Grid::ctype,Space::Grid::dimension>())
508 {
509 ScopedTimingSection timer("getContactConstraints");
510 using Grid = typename Space::Grid;
511 using ctype = typename Grid::ctype;
512 constexpr int dim = Grid::dimension;
513 using Entry = Dune::FieldMatrix<ctype,1,dim>;
514 using Row = std::vector<std::pair<size_t,Entry>>;
515
516 std::vector<Row> rows; // for storing
517 std::vector<ctype> bounds;
518 std::mutex mutex; // for preventing concurrent access to these buffers
519
520 auto faces = boundaryLocator.displacedFaces();
521
522 // In each quadrature point on a boundary face, look in normal direction and find the
523 // corresponding algebraic constraint.
524 parallelFor(0,faces.size(),[&](size_t i)
525 {
526 auto const& face = faces[i];
527 auto const qr = constraintPositions<dim-1>(face.type(),pointsPerEdge);
528 auto volume = face.volume();
529
530
531 std::vector<Row> tmpRows;
532 std::vector<ctype> tmpBounds;
533
534 for (auto const& qp: qr)
535 {
536 auto const& xi = qp.position();
537 auto c = getContactConstraint(space,boundaryLocator,face,xi,overlap); // find potential contact partner
538
539 if (c) // there need not be any - then skip
540 {
541 ctype w = qp.weight()*volume; // get the quadrature weight
542 auto [vic,g] = *c; // get
543 for (auto& p: vic) // and scale the constraint by this weight
544 p.second *= w;
545 g *= w;
546
547 mortar.updateConstraints(qr.size(),xi,vic,g,tmpRows,tmpBounds);
548 }
549 }
550
551 // Append the constraints of this face to the complete list of global constraints
552 std::lock_guard lock(mutex);
553
554 rows.reserve(size(rows)+size(tmpRows));
555 for (auto& tr: tmpRows)
556 rows.push_back(std::move(tr));
557
558 bounds.reserve(size(bounds)+size(tmpBounds));
559 for (auto& tb: tmpBounds)
560 bounds.push_back(std::move(tb));
561 });
562
563
564 // Now convert the data into a CSR matrix
565 ScopedTimingSection("convert to NumaBCRSMatrix");
566
567 NumaCRSPatternCreator<> creator(rows.size(),space.degreesOfFreedom());
568 for (size_t i=0; i<rows.size(); ++i)
569 for (auto const& p: rows[i])
570 creator.addElement(i,p.first);
571
572
573 NumaBCRSMatrix<Entry> B(creator);
574 for (size_t i=0; i<rows.size(); ++i)
575 for (auto const& p: rows[i])
576 B[i][p.first] += p.second;
577
578
580 std::copy(begin(bounds),end(bounds),begin(b));
581
582
583 return std::make_pair(B,b);
584 }
585
604 template <class Space1, class Displacement1, class Space2, class Displacement2, bool flattened=false>
607 int order, double overlap = std::numeric_limits<double>::lowest(), bool symmetric=true)
608 {
609 constexpr int dim1 = Space1::GridView::Grid::dimension;
610 constexpr int dim2 = Space2::GridView::Grid::dimension;
611 constexpr int dimw1= Space1::GridView::Grid::dimensionworld;
612 constexpr int dimw2= Space2::GridView::Grid::dimensionworld;
613 constexpr int dimensionworld = std::max(dimw1,dimw2);
614 constexpr int dimension = std::min(dim1, dim2);
615
616 using ctype = typename Space1::GridView::ctype;
617 constexpr int blockSize = flattened ? 1 : dimensionworld;
618 constexpr int blocksPerNode = flattened ? dimensionworld : 1;
620 using Row = std::vector<std::pair<size_t,Dune::FieldMatrix<ctype,1,dimensionworld>>>;
621 // using QR = Dune::QuadratureRule<ctype,dimw1-1>;
622
623 std::vector<Row> rows1;
624 std::vector<Row> rows2;
625 std::vector<ctype> bounds;
626
627 auto faces = boundaryLocator1.displacedFaces();
628 std::vector<typename BoundaryLocator<typename Space1::GridView,Displacement1,dimw1>::DisplacedFace> reducedFaces; //this is either an Intersection or a Cell
629
630 // TODO: parallelFor loop
631 for (auto const& face: faces)
632 {
633 auto volume = face.volume();
634 auto const qr = constraintPositions<dimw1-1>(face.type(), order);
635
636 for (auto const& qp: qr)
637 {
638 auto const& xi = qp.position();
639 auto w = qp.weight()*volume;
640 auto c = getContactConstraint(space1,boundaryLocator1,space2,boundaryLocator2,face,xi,overlap);
641 if (c)
642 {
643 auto [vic1,vic2,g] = *c; // get constraint
644 for (auto& p: vic1) // and scale it by this weight
645 p.second *= w;
646 for (auto& p: vic2) // and scale it by this weight
647 p.second *= w;
648 g *= w;
649 rows1.push_back(vic1);
650 rows2.push_back(vic2);
651 bounds.push_back(g);
652 reducedFaces.push_back(face);
653 }
654
655 if constexpr(dimension!=dimensionworld)
656 {
657 // check second normal direction (0,0,1) and (0,0,-1)
658 // for 2D-3D contact or 2D-2D in a 3D world
659 auto cc = getContactConstraint(space1,boundaryLocator1,space2,boundaryLocator2,face,xi,overlap,1);
660 if (cc)
661 {
662 auto [vic1,vic2,g] = *cc; // get constraint
663 for (auto& p: vic1) // and scale it by this weight
664 p.second *= w;
665 for (auto& p: vic2) // and scale it by this weight
666 p.second *= w;
667 g *= w;
668 rows1.push_back(vic1);
669 rows2.push_back(vic2);
670 bounds.push_back(g);
671 reducedFaces.push_back(face);
672 }
673 }
674 }
675 }
676
677 if(symmetric)
678 {
679 // loops over second geometry for overconstrained contact formulation
680 auto faces2 = boundaryLocator2.displacedFaces();
681 std::vector<typename BoundaryLocator<typename Space2::GridView,Displacement2,dimw2>::DisplacedFace> reducedFaces2; //this is either an Intersection or a Cell
682
683 // TODO: parallelFor loop
684 for (auto const& face: faces2)
685 {
686 auto volume = face.volume();
687 auto const qr = constraintPositions<dimw1-1>(face.type(), order);
688
689 for (auto const& qp: qr)
690 {
691 auto const& xi = qp.position();
692 auto w = qp.weight()*volume;
693 auto c = getContactConstraint(space2,boundaryLocator2,space1,boundaryLocator1,face,xi,overlap);
694 if (c)
695 {
696 auto [vic1,vic2,g] = *c; // get constraint
697 for (auto& p: vic1) // and scale it by this weight
698 p.second *= w;
699 for (auto& p: vic2) // and scale it by this weight
700 p.second *= w;
701 g *= w;
702 rows2.push_back(vic1);
703 rows1.push_back(vic2);
704 bounds.push_back(g);
705 reducedFaces2.push_back(face);
706 }
707
708 if constexpr(dimension!=dimensionworld)
709 {
710 // check second normal direction (0,0,1) and (0,0,-1)
711 // for 2D-3D contact or 2D-2D in a 3D world
712 auto cc = getContactConstraint(space2,boundaryLocator2,space1,boundaryLocator1,face,xi,overlap,1);
713 if (cc)
714 {
715 auto [vic1,vic2,g] = *cc; // get constraint
716 for (auto& p: vic1) // and scale it by this weight
717 p.second *= w;
718 for (auto& p: vic2) // and scale it by this weight
719 p.second *= w;
720 g *= w;
721 rows2.push_back(vic1);
722 rows1.push_back(vic2);
723 bounds.push_back(g);
724 reducedFaces.push_back(face);
725 }
726 }
727 }
728 }
729 }
730
731 NumaCRSPatternCreator<> creator1(rows1.size(),space1.degreesOfFreedom());
732 for (size_t i=0; i<rows1.size(); ++i)
733 for (auto const& p: rows1[i])
734 for (int l=0; l<blocksPerNode; ++l)
735 creator1.addElement(i,p.first*blocksPerNode+l);
736
737 NumaBCRSMatrix<Entry> B1(creator1);
738 for (size_t i=0; i<rows1.size(); ++i)
739 for (auto const& p: rows1[i]) {
740 if(!flattened) B1[i][p.first] = p.second;
741 else
742 for (int l=0; l<blocksPerNode; ++l)
743 B1[i][p.first*blocksPerNode+l] = p.second[0][l];
744 }
745
746 NumaCRSPatternCreator<> creator2(rows2.size(),space2.degreesOfFreedom());
747 for (size_t i=0; i<rows2.size(); ++i)
748 for (auto const& p: rows2[i])
749 for (int l=0; l<blocksPerNode; ++l)
750 creator2.addElement(i,p.first*blocksPerNode+l);
751
752 NumaBCRSMatrix<Entry> B2(creator2);
753 for (size_t i=0; i<rows2.size(); ++i)
754 for (auto const& p: rows2[i]) {
755 if(!flattened) B2[i][p.first] = p.second;
756 else
757 for (int l=0; l<blocksPerNode; ++l)
758 B2[i][p.first*blocksPerNode+l] = p.second[0][l];
759 }
760
762 std::copy(begin(bounds),end(bounds),begin(b));
763
764 return std::make_tuple(B1,B2,b,reducedFaces);
765 }
766
767 // ---------------------------------------------------------------------------------------------------------------
768 // ---------------------------------------------------------------------------------------------------------------
769
785 template <class GridView, class Displacement>
787 BoundaryLocator<GridView,Displacement> const& boundaryLocator2,
788 int order, double overlap = std::numeric_limits<double>::lowest(),
789 double maxGap = 1e-4)
790 {
791 using ctype = typename GridView::ctype;
792 using QR = Dune::QuadratureRule<ctype,GridView::Grid::dimension-1>;
793
794 auto faces = boundaryLocator1.displacedFaces();
795 QuadratureTraits<QR> quadratureRules;
796
797 double area = 0.0;
798
799 for (auto const& face: faces)
800 {
801 // QR const& qr = quadratureRules.rule(face.geometry().type(),order);
802 auto const qr = constraintPositions<GridView::Grid::dimension-1>(face.type(), order);
803
804
805 // usage of standard quadrature rule for integration of discontinuous contact area indicator function is maybe not the best idea, but its convenient
806 for (auto const& qp: qr)
807 {
808 auto const& xi = qp.position();
809 auto c = getContactConstraint(boundaryLocator1.displacement()->space(),boundaryLocator1,boundaryLocator1.displacement()->space(),boundaryLocator2,face,xi,overlap);
810
811 if (c && std::get<2>(*c) < maxGap)
812 {
813 auto dupi = boundaryLocator1.displacement()->derivative(face.gridFace().inside(), face.gridFace().geometryInInside().global(xi));
814 dupi += unitMatrix<ctype, GridView::Grid::dimension>();
815
816 auto jt = face.gridFace().geometry().jacobianTransposed(xi).rightmultiplyany(dupi.transposed());
817 ctype intElem = std::sqrt(std::abs(jt.rightmultiplyany(jt.transposed()).determinant()));
818 area += intElem * qp.weight();
819 }
820 }
821 }
822
823 return area;
824 }
825
826 // ---------------------------------------------------------------------------------------------------------------
827 // ---------------------------------------------------------------------------------------------------------------
828
854 template <class Displacement, class BoundaryDisplacement>
855 double contactLinesearch(Displacement const& u,
857 Displacement const& direction,
858 int pointsPerEdge, double trialStepSize=0.2,
859 double overlap = std::numeric_limits<double>::lowest(),
860 double targetOverlap = 0,
862 ContactConstraintsDetail::defaultMortar<typename Displacement::ctype,Displacement::GridView::dimension>())
863 {
864 using GridView = typename Displacement::GridView;
865 static_assert(GridView::dimensionworld==Displacement::components);
866 assert(pointsPerEdge>=0);
867 assert(targetOverlap<std::max(0.0,overlap) || (targetOverlap==0 && overlap==std::numeric_limits<double>::lowest()));
868
869 double tmin = 0; // current position on the full step [0,1].
870 double tmax = 1; // linear model predicts we can take a full step.
871
872
873 assert(boundaryLocator.displacement());
874
875
876 // Function for computing the minimal remaining gap depending on the
877 // step size t we take in direction of the given displacement "direction".
878 auto getGap = [&](double t)
879 {
880 // First get the constraints at the new trial point.
881 auto uTrial = u; // uTrial <- u + t*direction
882 uTrial.axpy(t,direction);
883 boundaryLocator.updateDisplacement(uTrial); // build spatial index for new deformation
884 auto Bb = getContactConstraints(u.space(),boundaryLocator,pointsPerEdge,overlap,mortar); // get constraints B*dx <= b
885 auto Bx(Bb.second); Bb.first.mv(direction.coefficients(),Bx);
886 auto const& b = Bb.second; // the gap
887
888 // Now step through all constraints and find, for each of them,
889 // the maximal feasible step size.
890 double gmin = std::numeric_limits<double>::max();
891 double dg, taumax = gmin;
892 for (int i=0; i<b.N(); ++i)
893 {
894 // Find the minimal gap
895 if (b[i][0] < gmin)
896 {
897 gmin = b[i][0];
898 dg = -Bx[i][0];
899 }
900
901 // Find the maximal allowed step size, i.e. max t s.t. (B*direction)[i]*t <= b[i]+targetOverlap.
902 // This limits t only if (B*direction)[i] > 0 (otherwise the gap is increased, and we obtain a
903 // (hopefully negative) minimal step size.
904 if (Bx[i][0] > 0)
905 taumax = std::min(taumax,(b[i][0]+targetOverlap)/Bx[i][0]);
906 }
907
908 return std::make_tuple(gmin,dg,taumax);
909 };
910
911 // step ahead until we find an infeasible step size.
912 // This cannot be done in one step up to tmax, because we may overlook constraints.
913 double const h = trialStepSize;
914 std::cout << "starting linesearch in [" << tmin+h << "," << tmax << "]\n";
915 while (tmin < 0.999*tmax)
916 {
917 double t = std::min(tmax,tmin+h);
918 auto [g0,dg0,tau0] = getGap(t);
919 // std::cout << "gap(t=" << t << ") = " << g0 << " [dg = " << dg0 << ", taumax = " << tau0 << "]\n";
920 if (g0 < -targetOverlap)
921 break;
922 else
923 tmin = t;
924 }
925
926 if (tmin >= 0.999*tmax)
927 {
928 // std::cout << "Found stepsize t= " << tmin << ".\n";
929 return tmin;
930 } else
931 {
932 std::cout << "No feasible stepsize found in this interval.\n";
933 tmax = std::min(tmax,tmin+h);
934 }
935
936 std::cout << "starting linesearch in [" << tmin << "," << tmax << "]\n";
937 while (tmax-tmin > std::min(1e-2,tmax/10))
938 {
939 double t = (tmax+tmin)/2;
940 if(t<1e-12)
941 break;
942 auto [g0,dg0,tau0] = getGap(t);
943 // std::cout << "gap(t=" << t << ") = " << g0 << " [dg = " << dg0 << ", taumax = " << tau0 << "]\n";
944 if (g0 < -targetOverlap)
945 tmax = t;
946 else
947 tmin = t;
948 }
949
950 std::cout << "Found stepsize t=" << tmin << ".\n";
951 return tmin;
952 }
953
986 template <class Displacement1, class BoundaryDisplacement1, class Displacement2, class BoundaryDisplacement2>
988 Displacement1 const& direction1,
989 Displacement2 const& u2, BoundaryLocator<typename Displacement2::GridView,BoundaryDisplacement2>& boundaryLocator2,
990 Displacement2 const& direction2,
991 int order, double trialStepSize=0.2,
992 double overlap = std::numeric_limits<double>::lowest(),
993 double targetOverlap = 0)
994 {
995 assert(order>=0);
996 assert(0<trialStepSize && trialStepSize<1);
997 assert(targetOverlap<overlap || (targetOverlap==0 && overlap==std::numeric_limits<double>::lowest()));
998 assert(Displacement1::GridView::Grid::dimensionworld==Displacement2::GridView::Grid::dimensionworld);
999 assert(boundaryLocator1.displacement());
1000 assert(boundaryLocator2.displacement());
1001
1002 double tmin = 0; // current position on the full step [0,1].
1003 double tmax = 1; // linear model predicts we can take a full step.
1004
1005 size_t nCoeff1 = direction1.coefficients().N();
1006 size_t nCoeff2 = direction2.coefficients().N();
1007
1008 // For a general documentation of this lambda, compare the single-grid
1009 // contactLinesearch function above.
1010 auto getGap = [&](double t)
1011 {
1012 auto uTrial1 = u1;
1013 uTrial1.axpy(t,direction1);
1014 auto uTrial2 = u2;
1015 uTrial2.axpy(t,direction2);
1016 boundaryLocator1.updateDisplacement(uTrial1); // build spatial index for new deformation
1017 boundaryLocator2.updateDisplacement(uTrial2);
1018 auto Bb = getContactConstraints(u1.space(),boundaryLocator1,u2.space(),boundaryLocator2,order,overlap); // get constraints B1*du1 + B2*du2 <= b
1019
1020 auto const& B = horzcat(std::get<0>(Bb),std::get<1>(Bb));
1021 auto const& g = std::get<2>(Bb);
1022 typename Displacement1::StorageType combinedDirection(nCoeff1+nCoeff2);
1023 for(size_t i=0; i< nCoeff1; ++i)
1024 combinedDirection[i]=direction1.coefficients()[i];
1025 for(size_t i=0; i< nCoeff2; ++i)
1026 combinedDirection[i+nCoeff1]=direction2.coefficients()[i];
1027
1028 auto Bx(g); B.mv(combinedDirection,Bx); Bx *= -1;
1029
1030 // Now step through all constraints and find, for each of them,
1031 // the maximal feasible step size.
1032 double gmin = std::numeric_limits<double>::max();
1033 double dg, taumax = gmin;
1034 for (int i=0; i<g.N(); ++i)
1035 {
1036 // find the minimal gap
1037 if (g[i][0] < gmin)
1038 {
1039 gmin = g[i][0];
1040 dg = Bx[i][0];
1041 }
1042 if (Bx[i][0] < 0)
1043 taumax = std::min(taumax,-(g[i][0]+targetOverlap)/Bx[i][0]);
1044 }
1045
1046 // std::cout << "tau max: " << taumax << std::endl;
1047
1048 return std::make_tuple(gmin,dg,taumax);
1049 };
1050
1051 // step ahead until we find an infeasible step size.
1052 // This cannot be done in one step up to tmax, because e may overlook constraints.
1053 double const h = trialStepSize;
1054 while (tmin < 0.999*tmax)
1055 {
1056 double t = std::min(tmax,tmin+h);
1057 auto [g0,dg0,tau0] = getGap(t);
1058std::cout << "gap(t=" << t << ") = " << g0 << " [dg = " << dg0 << ", taumax = " << tau0 << "]\n";
1059 if (g0 < -targetOverlap)
1060 break;
1061 else
1062 tmin = t;
1063 }
1064
1065 if (tmin >= 0.999*tmax)
1066 return tmin;
1067 else
1068 tmax = std::min(tmax,tmin+h);
1069
1070std::cout << "starting linesearch in [" << tmin << "," << tmax << "]\n";
1071 while (tmax-tmin > std::min(1e-4,tmax/10))
1072 {
1073 double t = (tmax+tmin)/2;
1074 auto [g0,dg0,tau0] = getGap(t);
1075// std::cout << "gap(t=" << t << ") = " << g0 << " [dg = " << dg0 << ", taumax = " << tau0 << "]\n";
1076 if (g0 < -targetOverlap)
1077 tmax = t;
1078 else
1079 tmin = t;
1080 }
1081
1082 return tmin;
1083 }
1084}
1085
1086#endif
Defines a constraint formulation where samples are weighted by Bezier test functions.
BezierMortar(int m)
Creates a Bezier mortar formulation of order m.
virtual int size(int n) const
The number of resulting constraints if m samples are provided.
virtual void updateConstraints(int n, Dune::FieldVector< Scalar, dim-1 > const &xi, Row &vic, Scalar g, std::vector< Row > &rows, std::vector< Scalar > &bounds) const
The.
Function const * displacement() const
Returns the current displacement of the mesh.
std::vector< DisplacedFace > displacedFaces() const
Returns a sequence of all deformed faces.
std::optional< std::tuple< typename BoundaryLocator< GridView, Function, dimw >::DisplacedFace, typename BoundaryLocator< GridView, Function, dimw >::LocalPosition, typename GridView::ctype > > byRay(Position const &from, Position const &direction, ctype minAngle=-0.5) const
Finds the first boundary face that is intersected by the given ray from outside towards inside.
void updateDisplacement(Update const &update)
Creates or updates the displacement function.
Defines a constraint formulation where each sample is taken as a single constraint of its own.
virtual void updateConstraints(int n, Dune::FieldVector< Scalar, dim-1 > const &xi, Row &Gi, Scalar gi, std::vector< Row > &B, std::vector< Scalar > &b) const
The.
virtual int size(int n) const
The number of resulting constraints if n samples are provided.
Abstract base class for computation of constraints from constraint samples.
virtual ~Mortar()
virtual int size(int n) const =0
The number of resulting constraints if n samples are provided.
virtual void updateConstraints(int n, Dune::FieldVector< Scalar, dim-1 > const &xi, Row &Gi, Scalar gi, std::vector< Row > &B, std::vector< Scalar > &b) const =0
Updates the given set of constraints (by and ) when a new constraint sample becomes available.
std::vector< std::pair< size_t, Entry > > Row
A NUMA-aware compressed row storage matrix adhering mostly to the Dune ISTL interface (to complete....
A NUMA-aware creator for matrix sparsity patterns.
void addElement(Index row, Index col)
Enters a single entry into the sparsity pattern.
A scope guard object that automatically closes a timing section on destruction.
Definition: timing.hh:181
double getContactArea(BoundaryLocator< GridView, Displacement > const &boundaryLocator1, BoundaryLocator< GridView, Displacement > const &boundaryLocator2, int order, double overlap=std::numeric_limits< double >::lowest(), double maxGap=1e-4)
Computes the contact area for the first body.
auto getContactConstraint(Space const &space, BoundaryLocator< typename Space::GridView, Displacement > const &boundaryLocator, DisplacedFace const &face, Dune::FieldVector< typename Space::GridView::ctype, Space::GridView::Grid::dimension-1 > const &xi, double overlap)
Computes a single multibody contact point constraint.
auto getContactConstraints(Space1 const &space1, BoundaryLocator< typename Space1::GridView, Displacement1, Space1::GridView::Grid::dimension > const &boundaryLocator1, Space2 const &space2, BoundaryLocator< typename Space2::GridView, Displacement2, Space1::GridView::Grid::dimension > const &boundaryLocator2, int order, double overlap=std::numeric_limits< double >::lowest(), bool symmetric=true)
Computes a complete pointwise twobody contact constraint.
auto getContactConstraints(Space const &space, BoundaryLocator< typename Space::GridView, Displacement > const &boundaryLocator, int pointsPerEdge, double overlap=std::numeric_limits< double >::lowest(), Mortar< typename Space::Grid::ctype, Space::Grid::dimension > const &mortar=ContactConstraintsDetail::defaultMortar< typename Space::Grid::ctype, Space::Grid::dimension >())
Computes a complete set of pointwise multibody contact constraints.
double contactLinesearch(Displacement1 const &u1, BoundaryLocator< typename Displacement1::GridView, BoundaryDisplacement1 > &boundaryLocator1, Displacement1 const &direction1, Displacement2 const &u2, BoundaryLocator< typename Displacement2::GridView, BoundaryDisplacement2 > &boundaryLocator2, Displacement2 const &direction2, int order, double trialStepSize=0.2, double overlap=std::numeric_limits< double >::lowest(), double targetOverlap=0)
Computes a feasible step size for contact constraints.
std::vector< Dune::QuadraturePoint< double, dim > > constraintPositions(Dune::GeometryType gt, int nodesPerEdge)
Computes contact sample points on faces.
auto getContactConstraint(Space1 const &space1, BoundaryLocator< typename Space1::GridView, Displacement1, Space1::GridView::Grid::dimension > const &boundaryLocator1, Space2 const &space2, BoundaryLocator< typename Space2::GridView, Displacement2, Space2::GridView::Grid::dimension > const &boundaryLocator2, DisplacedFace1 const &face, Dune::FieldVector< typename Space1::GridView::ctype, Space1::GridView::Grid::dimension-1 > const &xi, double overlap, int which=0)
Computes a single multibody contact point constraint.
Dune::FieldVector< T, n > max(Dune::FieldVector< T, n > x, Dune::FieldVector< T, n > const &y)
Componentwise maximum.
Definition: fixdune.hh:110
std::tuple< std::vector< typename EntryTraits< Entry >::field_type >, DynamicMatrix< typename EntryTraits< Entry >::field_type >, std::vector< int > > qr(DynamicMatrix< Entry > const &A)
Computes a rank-revealing QR decomposition of using column pivoting.
auto horzcat(DynamicMatrix< EntryA > const &A, DynamicMatrix< EntryB > const &B)
Concatenates two matrices horizontally.
Dune::FieldVector< T, n > min(Dune::FieldVector< T, n > x, Dune::FieldVector< T, n > const &y)
Componentwise minimum.
Definition: fixdune.hh:122
void parallelFor(Func const &f, int maxTasks=std::numeric_limits< int >::max())
A parallel for loop that executes the given functor in parallel on different CPUs.
Definition: threading.hh:489
Grid locking information based on grid type.
Definition: gridBasics.hh:84
Defines the sample-based contact constraint formulation.
static constexpr MortarB dirac()
Type
Defines the type of constraint formulation.
static constexpr MortarB bezier(int order)
A cache that stores suitable quadrature rules for quick retrieval.
Definition: quadrature.hh:35