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())
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)
40 throw std::runtime_error(
"Periodic FEM pairing requires external boundary simplices.");
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)
46 std::array<size_t,dim> indices{};
50 for(
size_t j=0;
j<
dim+1;++
j)
52 if(face->xID(
i)==element.simplex.xID(
j))
61 throw std::runtime_error(
"Boundary vertex missing from parent element.");
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)
76 key[
i]=Element::baryStarsAndBars(n,ib[
i]);
79 if(sum==Element::order)
81 if(!targetNodes.emplace(
key,
b.node(n).gID).second)
83 throw std::runtime_error(
"Duplicate FEM boundary barycentric node.");
88 for(
size_t n=0;n<Element::nodesPerElement;++n)
94 const int weight=Element::baryStarsAndBars(n,ia[
i]);
95 key[relation.vertexPermutation[
i]]=weight;
98 if(sum!=Element::order)
102 const auto found=targetNodes.find(
key);
103 if(found==targetNodes.end())
105 throw std::runtime_error(
"Missing periodic higher-order FEM node.");
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)
112 throw std::runtime_error(
"Periodic FEM node translation mismatch.");
119 const auto inserted=unique.emplace(std::make_pair(
u,
v),shift);
120 if(!inserted.second && (inserted.first->second-shift).norm()>tolerance)
122 throw std::runtime_error(
"Conflicting periodic FEM node translations.");
125 if(matched!=targetNodes.size())
127 throw std::runtime_error(
"Incomplete periodic FEM face pairing.");
130 std::vector<std::vector<std::pair<size_t,Vector>>> adjacency(fe.nodeSize());
131 for(
const auto& entry : unique)
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);
139 shifts.assign(fe.nodeSize(),Vector::Zero());
140 for(
size_t root=0;root<fe.nodeSize();++root)
147 std::vector<size_t> queue{root};
148 for(
size_t k=0;
k<queue.size();++
k)
150 const size_t u=queue[
k];
151 for(
const auto& edge : adjacency[
u])
153 const size_t v=edge.first;
163 throw std::runtime_error(
"Inconsistent periodic FEM edge/corner cycle.");