111 const std::vector<basis::ElementBases> &bases,
112 const std::vector<mesh::LocalBoundary> &local_boundary,
113 const std::array<int, 2> &boundary_ids,
114 const double relative_tolerance)
116 if (boundary_ids[0] < 0 || boundary_ids[1] < 0 || boundary_ids[0] == boundary_ids[1])
118 if (relative_tolerance <= 0)
121 const BoundaryNodes first = collect_boundary_nodes(boundary_ids[0], mesh, bases, local_boundary);
122 const BoundaryNodes second = collect_boundary_nodes(boundary_ids[1], mesh, bases, local_boundary);
123 if (first.empty() || second.empty())
125 "Unable to find DoFs for periodic boundary pair ({}, {}): found {} and {} DoFs",
126 boundary_ids[0], boundary_ids[1], first.size(), second.size());
127 if (first.size() != second.size())
129 "Periodic boundary pair ({}, {}) has different DoF counts: {} and {}",
130 boundary_ids[0], boundary_ids[1], first.size(), second.size());
134 for (
const BoundaryNode &node : first)
135 first_centroid += node.point;
136 for (
const BoundaryNode &node : second)
137 second_centroid += node.point;
138 first_centroid /= double(first.size());
139 second_centroid /= double(second.size());
145 const RowVectorNd translation = second_centroid - first_centroid;
149 const double tolerance = relative_tolerance * (bbox_max - bbox_min).maxCoeff();
151 std::vector<std::pair<int, int>> pairs;
152 pairs.reserve(first.size());
153 std::set<int> used_second;
154 for (
int first_index = 0; first_index < int(first.size()); ++first_index)
156 int matched_index = -1;
157 double matched_distance = std::numeric_limits<double>::infinity();
158 for (
int second_index = 0; second_index < int(second.size()); ++second_index)
160 if (used_second.count(second_index) > 0)
163 const double distance = (first[first_index].point + translation - second[second_index].point).norm();
164 if (distance < matched_distance)
166 matched_distance = distance;
167 matched_index = second_index;
171 if (matched_index < 0 || matched_distance > tolerance)
173 "No matching DoF found on periodic boundary {} for trace DoF {} on boundary {} (distance {}, tolerance {})",
174 boundary_ids[1], first_index, boundary_ids[0], matched_distance, tolerance);
175 if (!used_second.insert(matched_index).second)
177 "Periodic boundary pair ({}, {}) does not have a bijective DoF correspondence; DoF {} was matched more than once",
178 boundary_ids[0], boundary_ids[1], matched_index);
179 pairs.emplace_back(first_index, matched_index);
182 std::vector<Eigen::Triplet<double>>
entries;
183 for (
int i = 0; i < int(pairs.size()); ++i)
185 for (
int d = 0; d < value_dim; ++d)
187 const int row = i * value_dim + d;
188 for (
const auto &[index,
weight] : first[pairs[i].first].weights)
190 const int dof = index * value_dim + d;
191 if (dof < 0 || dof >= ndof)
195 for (
const auto &[index,
weight] : second[pairs[i].second].weights)
197 const int dof = index * value_dim + d;
198 if (dof < 0 || dof >= ndof)
207 std::set<int> boundary_dofs;
208 for (
const BoundaryNode &node : first)
209 for (
const auto &[index,
weight] : node.weights)
210 boundary_dofs.insert(index);
211 for (
const BoundaryNode &node : second)
212 for (
const auto &[index,
weight] : node.weights)
213 boundary_dofs.insert(index);
214 data.
boundary_dofs.assign(boundary_dofs.begin(), boundary_dofs.end());
215 data.
A.resize(
int(pairs.size()) * value_dim, ndof);
217 data.
A.makeCompressed();
218 data.
b.setZero(data.
A.rows(), 1);