22#include <janus/core/Function.hpp>
23#include <janus/math/RootFinding.hpp>
24#include <vulcan/io/HDF5Reader.hpp>
32#include <unordered_set>
108 const std::vector<std::string> &zero_derivatives,
double t);
112 const std::vector<std::string> &control_signals,
113 const std::vector<std::string> &zero_derivatives,
double t);
116 void ApplyBounds(Eigen::VectorXd &u,
const std::vector<std::string> &control_signals,
117 const std::unordered_map<std::string, std::pair<double, double>> &bounds);
163 const vulcan::io::HDF5Reader &reader);
166 size_t FindFrameIndex(
const std::vector<double> ×,
double target_time);
169 void RestoreState(
::icarus::Simulator &sim, vulcan::io::HDF5Reader &reader,
size_t frame_idx);
185 bool symbolic_enabled) {
188 return std::make_unique<WarmstartSolver>();
192 if (symbolic_enabled && config.
method ==
"newton") {
193 return std::make_unique<SymbolicTrim>();
199 return std::make_unique<FiniteDifferenceTrim>(opts);
220 const int n_controls =
static_cast<int>(controls.size());
221 const int n_residuals =
static_cast<int>(derivs.size());
223 if (n_controls == 0) {
224 result.
message =
"No control signals specified";
227 if (n_residuals == 0) {
228 result.
message =
"No zero_derivatives specified";
233 std::vector<std::pair<std::string, double>> targets;
234 targets.reserve(n_residuals);
235 for (
const auto &name : derivs) {
236 targets.emplace_back(name, 0.0);
241 Eigen::VectorXd u(n_controls);
242 for (
int i = 0; i < n_controls; ++i) {
247 u(i) = sim.
Peek(controls[i]);
251 const double t = sim.
Time();
254 for (
int iter = 0; iter < opts_.max_iterations; ++iter) {
256 for (
int i = 0; i < n_controls; ++i) {
257 sim.
Poke(controls[i], u(i));
261 Eigen::VectorXd F = EvaluateResidual(sim, derivs, t);
262 double norm = F.norm();
269 if (norm < opts_.tolerance) {
278 Eigen::MatrixXd J = ComputeJacobian(sim, controls, derivs, t);
287 Eigen::JacobiSVD<Eigen::MatrixXd> svd(J, Eigen::ComputeThinU | Eigen::ComputeThinV);
291 constexpr double svd_tolerance = 1e-10;
292 svd.setThreshold(svd_tolerance);
298 Eigen::VectorXd du = svd.solve(-F);
301 u += opts_.damping * du;
312 "Max iterations reached (residual = " + std::to_string(result.
residual_norm) +
")";
316 for (
int i = 0; i < n_controls; ++i) {
317 result.
controls[controls[i]] = u(i);
318 sim.
Poke(controls[i], u(i));
322 Eigen::VectorXd F_final = EvaluateResidual(sim, derivs, t);
323 for (
int i = 0; i < n_residuals; ++i) {
324 result.
residuals[derivs[i]] = F_final(i);
336inline Eigen::VectorXd
338 const std::vector<std::string> &zero_derivatives,
double t) {
343 const int n =
static_cast<int>(zero_derivatives.size());
344 Eigen::VectorXd F(n);
345 for (
int i = 0; i < n; ++i) {
346 F(i) = sim.
Peek(zero_derivatives[i]);
351inline Eigen::MatrixXd
353 const std::vector<std::string> &control_signals,
354 const std::vector<std::string> &zero_derivatives,
double t) {
355 const int n_controls =
static_cast<int>(control_signals.size());
356 const int n_residuals =
static_cast<int>(zero_derivatives.size());
359 Eigen::MatrixXd J(n_residuals, n_controls);
361 for (
int j = 0; j < n_controls; ++j) {
362 double u0 = sim.
Peek(control_signals[j]);
365 sim.
Poke(control_signals[j], u0 + h);
366 Eigen::VectorXd F_plus = EvaluateResidual(sim, zero_derivatives, t);
369 sim.
Poke(control_signals[j], u0 - h);
370 Eigen::VectorXd F_minus = EvaluateResidual(sim, zero_derivatives, t);
373 J.col(j) = (F_plus - F_minus) / (2.0 * h);
376 sim.
Poke(control_signals[j], u0);
382inline void FiniteDifferenceTrim::ApplyBounds(
383 Eigen::VectorXd &u,
const std::vector<std::string> &control_signals,
384 const std::unordered_map<std::string, std::pair<double, double>> &bounds) {
385 for (
int i = 0; i < static_cast<int>(control_signals.size()); ++i) {
386 auto it = bounds.find(control_signals[i]);
387 if (it != bounds.end()) {
388 const auto &[lo, hi] = it->second;
389 u(i) = std::clamp(u(i), lo, hi);
404 const int n_controls =
static_cast<int>(controls.size());
405 const int n_residuals =
static_cast<int>(derivs.size());
407 if (n_controls == 0) {
408 result.
message =
"No control signals specified";
411 if (n_residuals == 0) {
412 result.
message =
"No zero_derivatives specified";
417 std::vector<std::pair<std::string, double>> targets;
418 targets.reserve(n_residuals);
419 for (
const auto &name : derivs) {
420 targets.emplace_back(name, 0.0);
429 janus::Function F = BuildResidualFunction(sym_sim, config);
432 janus::RootFinderOptions opts;
435 opts.line_search =
true;
436 opts.verbose =
false;
438 janus::NewtonSolver solver(F, opts);
441 Eigen::VectorXd u0(n_controls);
442 for (
int i = 0; i < n_controls; ++i) {
447 u0(i) = sim.
Peek(controls[i]);
452 auto root_result = solver.solve(u0);
455 result.
converged = root_result.converged;
457 result.
message = root_result.message;
461 for (
int i = 0; i < n_controls; ++i) {
462 double val = root_result.x(i);
464 sim.
Poke(controls[i], val);
468 double t = sim.
Time();
470 for (
int i = 0; i < n_residuals; ++i) {
476 for (
const auto &[name, val] : result.
residuals) {
486 }
catch (
const Error &e) {
488 result.
message = std::string(
"Symbolic trim failed: ") + e.what();
490 }
catch (
const std::exception &e) {
492 result.
message = std::string(
"Symbolic trim failed: ") + e.what();
501 using Scalar = janus::SymbolicScalar;
503 const int n_controls =
static_cast<int>(config.
control_signals.size());
508 auto [u_vec, u_mx] = janus::sym_vec_pair(
"u", n_controls);
511 auto [x_vec, x_mx] = janus::sym_vec_pair(
"x",
static_cast<int>(n_states));
512 Scalar t_sym = janus::sym(
"t");
519 for (
int i = 0; i < n_controls; ++i) {
527 std::vector<Scalar> residual_elements;
528 residual_elements.reserve(n_residuals);
531 throw ConfigError(
"SymbolicTrim: Unknown derivative signal '" + deriv_name +
"'");
533 residual_elements.push_back(sym_sim.
GetSignal(deriv_name));
537 Scalar residuals = Scalar::vertcat(residual_elements);
541 return janus::Function(
"trim_residual", {u_mx}, {residuals});
555 result.
message =
"No recording_path specified for warmstart";
568 auto validation = ValidateRecording(sim, reader);
569 if (!validation.IsValid()) {
571 result.
message =
"Recording validation failed: " + validation.GetErrors()[0];
576 for (
const auto &warning : validation.GetWarnings()) {
582 auto times = reader.times();
585 result.
message =
"Recording contains no frames";
589 size_t frame_idx = FindFrameIndex(times, config.
resume_time);
590 double actual_time = times[frame_idx];
593 std::to_string(frame_idx) +
594 " at t=" + std::to_string(actual_time));
597 RestoreState(sim, reader, frame_idx);
606 " at t=" + std::to_string(actual_time);
610 }
catch (
const std::exception &e) {
612 result.
message = std::string(
"Warmstart failed: ") + e.what();
619inline ValidationResult WarmstartSolver::ValidateRecording(const ::icarus::Simulator &sim,
620 const vulcan::io::HDF5Reader &reader) {
624 auto recorded_signals = reader.signal_names();
625 std::unordered_set<std::string> recorded_set(recorded_signals.begin(), recorded_signals.end());
628 const auto ®istry = sim.Registry();
629 const auto &state_pairs = registry.get_state_pairs();
632 for (
const auto &pair : state_pairs) {
633 if (!recorded_set.contains(pair.value_name)) {
634 result.
AddError(
"State signal missing from recording", pair.value_name);
639 auto current_signals = registry.get_all_signal_names();
640 std::unordered_set<std::string> current_set(current_signals.begin(), current_signals.end());
642 for (
const auto &sig : recorded_signals) {
643 if (!current_set.contains(sig)) {
644 result.
AddInfo(
"Extra signal in recording (will be ignored)", sig);
651inline size_t WarmstartSolver::FindFrameIndex(
const std::vector<double> ×,
652 double target_time) {
658 auto it = std::lower_bound(times.begin(), times.end(), target_time);
660 if (it == times.end()) {
662 return times.size() - 1;
665 if (it == times.begin()) {
671 if (std::abs(*it - target_time) < std::abs(*prev - target_time)) {
672 return static_cast<size_t>(std::distance(times.begin(), it));
674 return static_cast<size_t>(std::distance(times.begin(), prev));
677inline void WarmstartSolver::RestoreState(::icarus::Simulator &sim, vulcan::io::HDF5Reader &reader,
679 const auto ®istry = sim.
Registry();
683 Eigen::VectorXd X(
static_cast<Eigen::Index
>(state_pairs.size()));
685 for (
size_t i = 0; i < state_pairs.size(); ++i) {
686 const auto &pair = state_pairs[i];
689 auto values = reader.read_double(pair.value_name, frame_idx, 1);
690 if (!values.empty()) {
691 X(
static_cast<Eigen::Index
>(i)) = values[0];
693 X(
static_cast<Eigen::Index
>(i)) = 0.0;
695 }
catch (
const std::exception &e) {
697 X(
static_cast<Eigen::Index
>(i)) = 0.0;
Consolidated error handling for Icarus.
Simulator and subsystem configuration structs.
Top-level simulation coordinator.
Core types for staging subsystem (trim, linearization, symbolic).
Lightweight symbolic simulator for graph extraction.
Structured validation result for warmstart and configuration validation.
Configuration/parsing errors with optional file context.
Definition Error.hpp:185
Base class for all Icarus exceptions.
Definition Error.hpp:52
void Log(LogLevel level, const std::string &message)
Log raw message.
Definition MissionLogger.hpp:584
void LogTrimStart(const std::string &mode, const std::vector< std::pair< std::string, double > > &targets)
Log trim solver progress.
Definition MissionLogger.hpp:350
void LogTrimConverged(int iterations)
Definition MissionLogger.hpp:366
void LogTrimIteration(int iteration, double residual)
Definition MissionLogger.hpp:360
void LogTrimFailed(const std::string &reason)
Definition MissionLogger.hpp:372
const auto & get_state_pairs() const
Get all integrable state pairs.
Definition Registry.hpp:268
Top-level simulation coordinator.
Definition Simulator.hpp:70
double Peek(const std::string &name) const
Read a signal value by name.
Definition Simulator.hpp:190
const SimulatorConfig & GetConfig() const
Get simulator configuration.
Definition Simulator.hpp:300
void Poke(const std::string &name, double value)
Write a signal value by name.
Definition Simulator.hpp:211
Eigen::VectorXd ComputeDerivatives(double t)
Compute derivatives for current state at time t.
Definition Simulator.hpp:1101
void SetState(const Eigen::VectorXd &X)
Set state vector.
Definition Simulator.hpp:1099
MissionLogger & GetLogger()
Get the mission logger.
Definition Simulator.hpp:216
void SetTime(double met)
Set simulation time (MET).
Definition Simulator.hpp:1073
double Time() const
Get current simulation time (MET - derived from epoch).
Definition Simulator.hpp:143
const SignalRegistry< double > & Registry() const
Get signal registry (for recording, introspection).
Definition Simulator.hpp:199
Structured validation result with errors and warnings.
Definition ValidationResult.hpp:58
void AddInfo(const std::string &message, const std::string &context="")
Add an info message.
Definition ValidationResult.hpp:119
void AddError(const std::string &message, const std::string &context="")
Add an error.
Definition ValidationResult.hpp:109
FiniteDifferenceTrim()
Definition TrimSolver.hpp:97
::icarus::staging::TrimResult Solve(::icarus::Simulator &sim, const TrimConfig &config) override
Solve trim problem.
Definition TrimSolver.hpp:214
FiniteDifferenceTrim(Options opts)
Definition TrimSolver.hpp:98
Lightweight symbolic simulator for graph extraction.
Definition SymbolicSimulatorCore.hpp:48
JanusVector< Scalar > ComputeDerivatives()
Compute derivatives symbolically.
Definition SymbolicSimulatorCore.hpp:101
void SetTime(Scalar t)
Set time (symbolic).
Definition SymbolicSimulatorCore.hpp:90
bool HasSignal(const std::string &name) const
Check if signal exists.
Definition SymbolicSimulatorCore.hpp:182
void SetState(const JanusVector< Scalar > &x)
Set state vector (symbolic).
Definition SymbolicSimulatorCore.hpp:80
void SetSignal(const std::string &name, Scalar value)
Write signal value (symbolic).
Definition SymbolicSimulatorCore.hpp:177
Scalar GetSignal(const std::string &name) const
Read signal value (symbolic).
Definition SymbolicSimulatorCore.hpp:170
std::size_t GetStateSize() const
Get total state size.
Definition SymbolicSimulatorCore.hpp:149
Symbolic trim using janus::NewtonSolver.
Definition TrimSolver.hpp:132
::icarus::staging::TrimResult Solve(::icarus::Simulator &sim, const TrimConfig &config) override
Solve trim problem.
Definition TrimSolver.hpp:398
Abstract trim solver interface.
Definition TrimSolver.hpp:52
virtual::icarus::staging::TrimResult Solve(::icarus::Simulator &sim, const TrimConfig &config)=0
Solve trim problem.
virtual ~TrimSolver()=default
Warmstart solver - restores state from HDF5 recording.
Definition TrimSolver.hpp:155
::icarus::staging::TrimResult Solve(::icarus::Simulator &sim, const TrimConfig &config) override
Solve trim problem.
Definition TrimSolver.hpp:548
Definition Simulator.hpp:53
std::unique_ptr< TrimSolver > CreateTrimSolver(const TrimConfig &config, bool symbolic_enabled)
Create appropriate trim solver based on configuration.
Definition TrimSolver.hpp:184
Definition AggregationTypes.hpp:13
@ Warning
Potential issues.
Definition Console.hpp:40
@ Info
Normal operation.
Definition Console.hpp:38
@ Error
Recoverable errors.
Definition Console.hpp:41
@ Debug
Debugging info.
Definition Console.hpp:37
Trim configuration for state initialization.
Definition SimulatorConfig.hpp:111
std::unordered_map< std::string, std::pair< double, double > > control_bounds
Control bounds (optional): key -> (min, max).
Definition SimulatorConfig.hpp:136
std::vector< std::string > zero_derivatives
Trim targets: which derivatives should be zero?
Definition SimulatorConfig.hpp:122
std::string method
"newton" or "ipopt"
Definition SimulatorConfig.hpp:130
int max_iterations
Definition SimulatorConfig.hpp:129
double resume_time
MET to resume from (for warmstart mode).
Definition SimulatorConfig.hpp:146
double tolerance
Optimization settings.
Definition SimulatorConfig.hpp:128
bool validate_schema
Validate recording schema before loading (for warmstart mode).
Definition SimulatorConfig.hpp:149
std::string recording_path
Path to recording file (for warmstart mode).
Definition SimulatorConfig.hpp:143
std::unordered_map< std::string, double > initial_guesses
Initial guesses for controls (optional).
Definition SimulatorConfig.hpp:133
bool IsWarmstart() const
Check if this is warmstart mode.
Definition SimulatorConfig.hpp:159
std::vector< std::string > control_signals
Trim controls: which signals can be adjusted?
Definition SimulatorConfig.hpp:125
Definition TrimSolver.hpp:89
double step_size
Finite difference step size.
Definition TrimSolver.hpp:90
double damping
Newton step damping factor (0, 1].
Definition TrimSolver.hpp:93
int max_iterations
Maximum Newton iterations.
Definition TrimSolver.hpp:92
double tolerance
Convergence tolerance on residual norm.
Definition TrimSolver.hpp:91
bool verbose
Print iteration progress.
Definition TrimSolver.hpp:94
Result of trim optimization.
Definition StagingTypes.hpp:31
std::unordered_map< std::string, double > controls
Final control values (control_name -> value).
Definition StagingTypes.hpp:38
double residual_norm
Definition StagingTypes.hpp:34
int iterations
Definition StagingTypes.hpp:33
std::unordered_map< std::string, double > residuals
Final derivative residuals (derivative_name -> value).
Definition StagingTypes.hpp:41
std::string message
Definition StagingTypes.hpp:35
bool converged
Definition StagingTypes.hpp:32