Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
ConstrainedVelocitySolve.h
Go to the documentation of this file.
1#ifndef model_ConstrainedVelocitySolve_H_
2#define model_ConstrainedVelocitySolve_H_
4#include <Eigen/SparseQR>
5#include <cfloat>
6#include <set>
7
8namespace model
9{
10 using VelocityMatrix = Eigen::SparseMatrix<double>;
11
12 // Solve the existing lumped balance in the admissible velocity space.
13 // Reduction must precede solving; projecting unconstrained speeds would
14 // not preserve the force balance at shared periodic crossings.
15 inline Eigen::VectorXd solveConstrainedVelocityDiagonal(const Eigen::VectorXd &diagonal,
16 const Eigen::VectorXd &force, const VelocityMatrix &Z)
17 {
18 if (diagonal.size() != Z.rows() || force.size() != Z.rows() || !diagonal.allFinite() || !force.allFinite())
19 {
20 throw std::runtime_error("Invalid constrained velocity balance.");
21 }
22 if (Z.rows() == Z.cols())
23 {
24 // The topology eliminator returns identity when there are no constraints.
25 Eigen::VectorXd result = Eigen::VectorXd::Zero(force.size());
26 for (Eigen::Index k = 0; k < force.size(); ++k)
27 {
28 if (std::abs(diagonal(k)) > FLT_EPSILON)
29 {
30 result(k) = force(k) / diagonal(k);
31 }
32 }
33 return result;
34 }
35 std::vector<Eigen::Triplet<double>> triplets;
36 for (Eigen::Index k = 0; k < diagonal.size(); ++k)
37 {
38 if (std::abs(diagonal(k)) > FLT_EPSILON)
39 {
40 triplets.emplace_back(k, k, diagonal(k));
41 }
42 }
43 if (Z.cols() == 0)
44 {
45 return Eigen::VectorXd::Zero(Z.rows());
46 }
47 VelocityMatrix D(Z.rows(), Z.rows());
48 D.setFromTriplets(triplets.begin(), triplets.end());
49 const VelocityMatrix reduced = Z.transpose() * D * Z;
50 const Eigen::VectorXd rhs = Z.transpose() * force;
51 if (rhs.isZero(0.0))
52 {
53 return Eigen::VectorXd::Zero(Z.rows());
54 }
55 Eigen::SparseQR<VelocityMatrix, Eigen::COLAMDOrdering<int>> solver;
56 solver.compute(reduced);
57 if (solver.info() != Eigen::Success)
58 {
59 throw std::runtime_error("Constrained climb factorization failed.");
60 }
61 const Eigen::VectorXd solution = solver.solve(rhs);
62 if (!solution.allFinite() || (reduced * solution - rhs).norm() > 1.e-9 * rhs.norm())
63 {
64 throw std::runtime_error("Inconsistent constrained climb balance.");
65 }
66 return Z * solution;
67 }
68
69 template <int dim, class Network> VelocityMatrix periodicClimbVelocityMap(const Network &network)
70 {
71 std::vector<Eigen::Matrix<double, dim, 1>> directions;
72 for (const auto &entry : network.networkNodes())
73 {
74 directions.push_back(entry.second.lock()->climbDirection());
75 }
76 PeriodicVelocityConstraints constraints(directions.size());
77 using Vector = Eigen::Matrix<double, dim, 1>;
78 using VectorRow = std::map<size_t, Vector>;
79 std::map<size_t, VectorRow> interpolants;
81 [&](size_t node, size_t a, size_t b, double wa, double wb)
82 {
83 VectorRow expression;
84 expression.emplace(a, wa * directions[a]);
85 const auto found = expression.find(b);
86 if (found == expression.end())
87 {
88 expression.emplace(b, wb * directions[b]);
89 }
90 else
91 {
92 found->second += wb * directions[b];
93 }
94 const auto first = interpolants.emplace(node, expression);
95 if (first.second)
96 {
97 // Subdivision tangential motion is a reparameterization, not
98 // an independent climb DOF. Its source strength uses only the
99 // projection of the interpolated velocity onto the local normal.
100 std::map<size_t, double> row{{node, 1.0}};
101 for (const auto &term : expression)
102 {
103 row[term.first] -= directions[node].dot(term.second);
104 }
105 constraints.constrain(std::move(row), false, node);
106 }
107 else
108 {
109 // Every loop sharing this network node must predict the SAME
110 // vector motion, not merely the same local normal component.
111 for (int d = 0; d < dim; ++d)
112 {
113 std::map<size_t, double> row;
114 for (const auto &term : first.first->second)
115 {
116 row[term.first] += term.second(d);
117 }
118 for (const auto &term : expression)
119 {
120 row[term.first] -= term.second(d);
121 }
122 constraints.constrain(std::move(row));
123 }
124 }
125 });
126 std::vector<Eigen::Triplet<double>> triplets;
127 const auto freeCount = constraints.template append<1>(triplets);
128 VelocityMatrix Z(directions.size(), freeCount);
129 Z.setFromTriplets(triplets.begin(), triplets.end());
130 return Z;
131 }
132
133 template <int dim, class Network>
134 Eigen::VectorXd interpolateClimbSubdivisionVelocities(const Network &network, const Eigen::VectorXd &nodal)
135 {
136 Eigen::VectorXd result = nodal;
137 std::set<size_t> visited;
138 const double tolerance = 1.e-9 * std::max(nodal.norm(), 1.e-30);
140 network,
141 [&](size_t node, size_t a, size_t b, double wa, double wb)
142 {
143 const Eigen::Matrix<double, dim, 1> value =
144 wa * nodal.template segment<dim>(dim * a) + wb * nodal.template segment<dim>(dim * b);
145 if (visited.insert(node).second)
146 {
147 result.template segment<dim>(dim * node) = value;
148 }
149 else if ((result.template segment<dim>(dim * node) - value).norm() > tolerance)
150 {
151 throw std::runtime_error("Incompatible shared climb subdivision velocity.");
152 }
153 });
154 return result;
155 }
156
157 template <int dim, class Network>
158 Eigen::VectorXd compatiblePredictedVelocities(const Network &network, const Eigen::VectorXd &prediction)
159 {
160 if (prediction.size() != dim * Eigen::Index(network.networkNodes().size()) || !prediction.allFinite())
161 {
162 throw std::runtime_error("Invalid Python glide velocity shape or values.");
163 }
164 std::vector<Eigen::Triplet<double>> triplets;
165 const auto freeCount = periodicVelocityConstraints<dim>(network, triplets);
166 if (freeCount == network.networkNodes().size())
167 {
168 return prediction;
169 }
170 VelocityMatrix Z(prediction.size(), dim * freeCount);
171 Z.setFromTriplets(triplets.begin(), triplets.end());
172 Eigen::SparseQR<VelocityMatrix, Eigen::COLAMDOrdering<int>> solver;
173 solver.compute(Z);
174 if (solver.info() != Eigen::Success)
175 {
176 throw std::runtime_error("Periodic Python velocity projection failed.");
177 }
178 const Eigen::VectorXd free = solver.solve(prediction);
179 if (!free.allFinite())
180 {
181 throw std::runtime_error("Nonfinite periodic Python velocity projection.");
182 }
183 return Z * free;
184 }
185} // namespace model
186#endif
const NetworkNodeContainerType & networkNodes() const
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
Eigen::VectorXd interpolateClimbSubdivisionVelocities(const Network &network, const Eigen::VectorXd &nodal)
Eigen::VectorXd compatiblePredictedVelocities(const Network &network, const Eigen::VectorXd &prediction)
Eigen::VectorXd solveConstrainedVelocityDiagonal(const Eigen::VectorXd &diagonal, const Eigen::VectorXd &force, const VelocityMatrix &Z)
void forEachPeriodicVelocityRelation(const Network &network, Callback callback)
Eigen::SparseMatrix< double > VelocityMatrix
VelocityMatrix periodicClimbVelocityMap(const Network &network)
size_t periodicVelocityConstraints(const Network &network, Triplets &triplets)
model::DislocationNetwork< 3 > Network
REAL D[16]
int k
result
vertex first