18 if (diagonal.size() != Z.rows() || force.size() != Z.rows() || !diagonal.allFinite() || !force.allFinite())
20 throw std::runtime_error(
"Invalid constrained velocity balance.");
22 if (Z.rows() == Z.cols())
25 Eigen::VectorXd
result = Eigen::VectorXd::Zero(force.size());
26 for (Eigen::Index
k = 0;
k < force.size(); ++
k)
28 if (std::abs(diagonal(
k)) > FLT_EPSILON)
35 std::vector<Eigen::Triplet<double>> triplets;
36 for (Eigen::Index
k = 0;
k < diagonal.size(); ++
k)
38 if (std::abs(diagonal(
k)) > FLT_EPSILON)
40 triplets.emplace_back(
k,
k, diagonal(
k));
45 return Eigen::VectorXd::Zero(Z.rows());
48 D.setFromTriplets(triplets.begin(), triplets.end());
50 const Eigen::VectorXd rhs = Z.transpose() * force;
53 return Eigen::VectorXd::Zero(Z.rows());
55 Eigen::SparseQR<VelocityMatrix, Eigen::COLAMDOrdering<int>> solver;
56 solver.compute(reduced);
57 if (solver.info() != Eigen::Success)
59 throw std::runtime_error(
"Constrained climb factorization failed.");
61 const Eigen::VectorXd solution = solver.solve(rhs);
62 if (!solution.allFinite() || (reduced * solution - rhs).norm() > 1.e-9 * rhs.norm())
64 throw std::runtime_error(
"Inconsistent constrained climb balance.");
71 std::vector<Eigen::Matrix<double, dim, 1>> directions;
74 directions.push_back(entry.second.lock()->climbDirection());
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)
84 expression.emplace(
a, wa * directions[
a]);
85 const auto found = expression.find(
b);
86 if (found == expression.end())
88 expression.emplace(
b, wb * directions[
b]);
92 found->second += wb * directions[
b];
94 const auto first = interpolants.emplace(node, expression);
100 std::map<size_t, double> row{{node, 1.0}};
101 for (
const auto &term : expression)
103 row[term.first] -= directions[node].dot(term.second);
105 constraints.
constrain(std::move(row),
false, node);
111 for (
int d = 0; d < dim; ++d)
113 std::map<size_t, double> row;
114 for (
const auto &term :
first.first->second)
116 row[term.first] += term.second(d);
118 for (
const auto &term : expression)
120 row[term.first] -= term.second(d);
126 std::vector<Eigen::Triplet<double>> triplets;
127 const auto freeCount = constraints.template append<1>(triplets);
129 Z.setFromTriplets(triplets.begin(), triplets.end());
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);
141 [&](
size_t node,
size_t a,
size_t b,
double wa,
double wb)
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)
147 result.template segment<dim>(dim * node) = value;
149 else if ((
result.template segment<dim>(dim * node) - value).norm() > tolerance)
151 throw std::runtime_error(
"Incompatible shared climb subdivision velocity.");
160 if (prediction.size() != dim * Eigen::Index(network.
networkNodes().size()) || !prediction.allFinite())
162 throw std::runtime_error(
"Invalid Python glide velocity shape or values.");
164 std::vector<Eigen::Triplet<double>> triplets;
171 Z.setFromTriplets(triplets.begin(), triplets.end());
172 Eigen::SparseQR<VelocityMatrix, Eigen::COLAMDOrdering<int>> solver;
174 if (solver.info() != Eigen::Success)
176 throw std::runtime_error(
"Periodic Python velocity projection failed.");
178 const Eigen::VectorXd free = solver.solve(prediction);
179 if (!free.allFinite())
181 throw std::runtime_error(
"Nonfinite periodic Python velocity projection.");