Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
PeriodicFEMNodePairing.h
Go to the documentation of this file.
1#ifndef model_PeriodicFEMNodePairing_H_
2#define model_PeriodicFEMNodePairing_H_
3#include <Eigen/Core>
4#include <array>
5#include <map>
6#include <vector>
7#include <limits>
8#include <stdexcept>
9#include <algorithm>
10namespace model
11{
12 // Geometric relations only: no DOFs are eliminated and no field is tied.
13 template<class Element>
15 {
16 static constexpr int dim=Element::dim;
17 using Vector=Eigen::Matrix<double,dim,1>;
18 struct Pair
19 {
20 size_t source,sink;
22 };
23 std::vector<Pair> pairs; // unique undirected edges, source < sink
24 std::vector<size_t> representatives;
25 std::vector<Vector> shifts; // node position = representative + shift
26
27 template<class FE>
28 explicit PeriodicFEMNodePairing(const FE& fe)
29 {
30 const double tolerance=1.e-8*(fe.mesh.xMax()-fe.mesh.xMin()).norm()+
31 64*std::numeric_limits<double>::epsilon()*std::max(fe.mesh.xMin().norm(),fe.mesh.xMax().norm());
32 std::map<std::pair<size_t,size_t>,Vector> unique;
33 for(const auto& entry : fe.mesh.periodicSimplexPairs())
34 {
35 const auto* source=entry.first;
36 const auto& relation=entry.second;
37 const auto* target=relation.partner;
38 if(source->parents().size()!=1 || target->parents().size()!=1)
39 {
40 throw std::runtime_error("Periodic FEM pairing requires external boundary simplices.");
41 }
42 const auto& a=fe.elements().at(source->parents().begin()->first);
43 const auto& b=fe.elements().at(target->parents().begin()->first);
44 const auto localIndices=[](const auto& element,const auto* face)
45 {
46 std::array<size_t,dim> indices{};
47 for(size_t i=0;i<dim;++i)
48 {
49 bool found=false;
50 for(size_t j=0;j<dim+1;++j)
51 {
52 if(face->xID(i)==element.simplex.xID(j))
53 {
54 indices[i]=j;
55 found=true;
56 break;
57 }
58 }
59 if(!found)
60 {
61 throw std::runtime_error("Boundary vertex missing from parent element.");
62 }
63 }
64 return indices;
65 };
66 const auto ia=localIndices(a,source);
67 const auto ib=localIndices(b,target);
68 using Key=std::array<int,dim>;
69 std::map<Key,size_t> targetNodes;
70 for(size_t n=0;n<Element::nodesPerElement;++n)
71 {
72 Key key{};
73 int sum=0;
74 for(size_t i=0;i<dim;++i)
75 {
76 key[i]=Element::baryStarsAndBars(n,ib[i]);
77 sum+=key[i];
78 }
79 if(sum==Element::order)
80 {
81 if(!targetNodes.emplace(key,b.node(n).gID).second)
82 {
83 throw std::runtime_error("Duplicate FEM boundary barycentric node.");
84 }
85 }
86 }
87 size_t matched=0;
88 for(size_t n=0;n<Element::nodesPerElement;++n)
89 {
90 Key key{};
91 int sum=0;
92 for(size_t i=0;i<dim;++i)
93 {
94 const int weight=Element::baryStarsAndBars(n,ia[i]);
95 key[relation.vertexPermutation[i]]=weight;
96 sum+=weight;
97 }
98 if(sum!=Element::order)
99 {
100 continue;
101 }
102 const auto found=targetNodes.find(key);
103 if(found==targetNodes.end())
104 {
105 throw std::runtime_error("Missing periodic higher-order FEM node.");
106 }
107 ++matched;
108 size_t u=a.node(n).gID,v=found->second;
109 Vector shift=relation.shift;
110 if((fe.node(u).P0+shift-fe.node(v).P0).norm()>tolerance || u==v)
111 {
112 throw std::runtime_error("Periodic FEM node translation mismatch.");
113 }
114 if(u>v)
115 {
116 std::swap(u,v);
117 shift=-shift;
118 }
119 const auto inserted=unique.emplace(std::make_pair(u,v),shift);
120 if(!inserted.second && (inserted.first->second-shift).norm()>tolerance)
121 {
122 throw std::runtime_error("Conflicting periodic FEM node translations.");
123 }
124 }
125 if(matched!=targetNodes.size())
126 {
127 throw std::runtime_error("Incomplete periodic FEM face pairing.");
128 }
129 }
130 std::vector<std::vector<std::pair<size_t,Vector>>> adjacency(fe.nodeSize());
131 for(const auto& entry : unique)
132 {
133 const auto [u,v]=entry.first;
134 pairs.push_back({u,v,entry.second});
135 adjacency[u].emplace_back(v,entry.second);
136 adjacency[v].emplace_back(u,-entry.second);
137 }
138 representatives.assign(fe.nodeSize(),fe.nodeSize());
139 shifts.assign(fe.nodeSize(),Vector::Zero());
140 for(size_t root=0;root<fe.nodeSize();++root)
141 {
142 if(representatives[root]!=fe.nodeSize())
143 {
144 continue;
145 }
146 representatives[root]=root;
147 std::vector<size_t> queue{root};
148 for(size_t k=0;k<queue.size();++k)
149 {
150 const size_t u=queue[k];
151 for(const auto& edge : adjacency[u])
152 {
153 const size_t v=edge.first;
154 const Vector offset=shifts[u]+edge.second;
155 if(representatives[v]==fe.nodeSize())
156 {
157 representatives[v]=root;
158 shifts[v]=offset;
159 queue.push_back(v);
160 }
161 else if(representatives[v]!=root || (shifts[v]-offset).norm()>tolerance)
162 {
163 throw std::runtime_error("Inconsistent periodic FEM edge/corner cycle.");
164 }
165 }
166 }
167 }
168 }
170 };
171}
172#endif
a
Definition int_R.m:14
b
Definition int_R.m:16
Eigen::Matrix< double, dim, 1 > Vector
int j
REAL u[4]
int k
int i
newbad key
REAL v[4]