Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
PeriodicVelocityConstraints.h
Go to the documentation of this file.
1#ifndef model_PeriodicVelocityConstraints_H_
2#define model_PeriodicVelocityConstraints_H_
3
4#include <algorithm>
5#include <cmath>
6#include <cstddef>
7#include <utility>
8#include <limits>
9#include <map>
10#include <vector>
11#include <stdexcept>
12
13namespace model
14{
15 // Homogeneous scalar constraints. Each pivot expresses one velocity in
16 // terms of the remaining velocities. Repeated constraints are redundant;
17 // different endpoint pairs impose compatibility, rather than adding two
18 // interpolants to the same row. The three Cartesian components share Z.
20 {
21 using Row = std::map<size_t, double>;
22 struct Pivot
23 {
24 size_t node;
25 Row row;
26 };
27 size_t count;
28 std::vector<Pivot> pivots;
29 std::map<size_t, size_t> pivotIndex;
30
31 public:
32 explicit PeriodicVelocityConstraints(size_t countIn) : count(countIn)
33 {
34 }
35
36 void interpolate(size_t node, size_t a, size_t b, double wa, double wb)
37 {
38 if (node >= count || a >= count || b >= count || !std::isfinite(wa) || !std::isfinite(wb))
39 {
40 throw std::runtime_error("Invalid periodic velocity interpolation.");
41 }
42 Row row;
43 row[node] += 1.0;
44 row[a] -= wa;
45 row[b] -= wb;
46 constrain(std::move(row), true, node);
47 }
48
49 // General homogeneous relation, also used when scalar climb speeds
50 // have different vector directions at the participating nodes.
51 void constrain(Row row, bool preserveTranslation = false,
52 size_t preferredNode = std::numeric_limits<size_t>::max())
53 {
54 for (const auto &value : row)
55 {
56 if (value.first >= count || !std::isfinite(value.second))
57 {
58 throw std::runtime_error("Invalid velocity constraint.");
59 }
60 }
61 const double tolerance = 128 * std::numeric_limits<double>::epsilon();
62 // Substitute earlier pivots in their creation order. Their rows can
63 // reference later pivots, but never earlier ones.
64 std::map<size_t, size_t> pending;
65 for (const auto &value : row)
66 {
67 const auto found = pivotIndex.find(value.first);
68 if (found != pivotIndex.end())
69 {
70 pending.emplace(found->second, value.first);
71 }
72 }
73 while (!pending.empty())
74 {
75 const auto item = *pending.begin();
76 pending.erase(pending.begin());
77 const double factor = row[item.second];
78 row.erase(item.second);
79 for (const auto &value : pivots[item.first].row)
80 {
81 row[value.first] += factor * value.second;
82 const auto found = pivotIndex.find(value.first);
83 if (found != pivotIndex.end())
84 {
85 pending.emplace(found->second, value.first);
86 }
87 }
88 }
89 for (auto it = row.begin(); it != row.end();)
90 {
91 if (std::abs(it->second) <= tolerance)
92 {
93 it = row.erase(it);
94 }
95 else
96 {
97 ++it;
98 }
99 }
100 if (row.empty())
101 {
102 return;
103 }
104 auto pivot = std::max_element(row.begin(), row.end(), [](const auto &x, const auto &y)
105 { return std::abs(x.second) < std::abs(y.second); });
106 // Every interpolation preserves rigid translation. Cancellation
107 // between almost identical relations must not create an artificial
108 // absolute-velocity constraint. Restore the zero coefficient sum
109 // before normalizing a small residual row.
110 if (preserveTranslation)
111 {
112 long double otherSum = 0.0L;
113 for (const auto &value : row)
114 {
115 if (value.first != pivot->first)
116 {
117 otherSum += value.second;
118 }
119 }
120 pivot->second = -static_cast<double>(otherSum);
121 if (std::abs(pivot->second) <= tolerance)
122 {
123 row.erase(pivot);
124 if (row.empty())
125 {
126 return;
127 }
128 }
129 }
130 pivot = std::max_element(row.begin(), row.end(), [](const auto &x, const auto &y)
131 { return std::abs(x.second) < std::abs(y.second); });
132 const auto preferred = row.find(preferredNode);
133 if (preferred != row.end() && std::abs(preferred->second) >= 0.5 * std::abs(pivot->second))
134 {
135 pivot = preferred;
136 }
137 const size_t id = pivot->first;
138 const double coefficient = pivot->second;
139 row.erase(pivot);
140 for (auto &value : row)
141 {
142 value.second /= -coefficient;
143 }
144 pivotIndex.emplace(id, pivots.size());
145 pivots.push_back({id, std::move(row)});
146 }
147
148 template <int dim, class Triplets> size_t append(Triplets &triplets) const
149 {
150 std::vector<Row> expressions(count);
151 size_t freeCount = 0;
152 for (size_t i = 0; i < count; ++i)
153 {
154 if (!pivotIndex.count(i))
155 {
156 expressions[i].emplace(freeCount++, 1.0);
157 }
158 }
159 for (auto it = pivots.rbegin(); it != pivots.rend(); ++it)
160 {
161 for (const auto &dependency : it->row)
162 {
163 for (const auto &value : expressions[dependency.first])
164 {
165 expressions[it->node][value.first] += dependency.second * value.second;
166 }
167 }
168 }
169 for (size_t i = 0; i < count; ++i)
170 {
171 for (const auto &value : expressions[i])
172 {
173 for (int d = 0; d < dim; ++d)
174 {
175 triplets.emplace_back(dim * i + d, dim * value.first + d, value.second);
176 }
177 }
178 }
179 return freeCount;
180 }
181 };
182
183 template <class Network, class Callback>
184 void forEachPeriodicVelocityRelation(const Network &network, Callback callback)
185 {
186 // networkID() walks the ordered container. Cache the identical ordering
187 // once rather than repeating that linear walk for every occurrence.
188 std::map<size_t, size_t> indices;
189 for (const auto &entry : network.networkNodes())
190 {
191 indices.emplace(entry.second.lock()->sID, indices.size());
192 }
193 for (const auto &entry : network.networkNodes())
194 {
195 const auto node = entry.second.lock();
196 const bool subdivision =
197 !node->loopNodes().empty() && std::all_of(node->loopNodes().begin(), node->loopNodes().end(),
198 [](const auto *ln) { return ln->isPeriodicSubdivision(); });
199 if (!subdivision)
200 {
201 continue;
202 }
203 for (const auto *ln : node->loopNodes())
204 {
205 const auto a = ln->periodicPrev();
206 const auto b = ln->periodicNext();
207 if (a && b)
208 {
209 const double da = (ln->get_P() - a->get_P()).norm();
210 const double db = (ln->get_P() - b->get_P()).norm();
211 if (da + db <= std::numeric_limits<double>::epsilon())
212 {
213 throw std::runtime_error("Degenerate periodic velocity interpolation endpoints.");
214 }
215 callback(indices.at(node->sID), indices.at(a->networkNode->sID), indices.at(b->networkNode->sID),
216 db / (da + db), da / (da + db));
217 }
218 }
219 }
220 }
221
222 template <int dim, class Network, class Triplets>
223 size_t periodicVelocityConstraints(const Network &network, Triplets &triplets)
224 {
225 PeriodicVelocityConstraints constraints(network.networkNodes().size());
226 forEachPeriodicVelocityRelation(network, [&](size_t node, size_t a, size_t b, double wa, double wb)
227 { constraints.interpolate(node, a, b, wa, wb); });
228 return constraints.template append<dim>(triplets);
229 }
230} // namespace model
231#endif
const NetworkNodeContainerType & networkNodes() const
size_t append(Triplets &triplets) const
void interpolate(size_t node, size_t a, size_t b, double wa, double wb)
void constrain(Row row, bool preserveTranslation=false, size_t preferredNode=std::numeric_limits< size_t >::max())
a
Definition int_R.m:14
b
Definition int_R.m:16
x
Definition int_R.m:12
void forEachPeriodicVelocityRelation(const Network &network, Callback callback)
size_t periodicVelocityConstraints(const Network &network, Triplets &triplets)
model::DislocationNetwork< 3 > Network
std::deque< Eigen::Triplet< double > > Triplets
int pivot
int i