Icarus
Vehicle Simulation as a Transformable Computational Graph, built on Vulcan and Janus
Loading...
Searching...
No Matches
TrimSolver.hpp
Go to the documentation of this file.
1#pragma once
2
12
13#include <icarus/core/Error.hpp>
17
18// Include SymbolicSimulatorCore here (outside namespace)
19// to avoid namespace resolution issues
21
22#include <janus/core/Function.hpp>
23#include <janus/math/RootFinding.hpp>
24#include <vulcan/io/HDF5Reader.hpp>
25
26#include <Eigen/Dense>
27#include <cmath>
28
29#include <algorithm>
30#include <memory>
31#include <string>
32#include <unordered_set>
33#include <vector>
34
35namespace icarus {
36// Forward declaration
37class Simulator;
38} // namespace icarus
39
40namespace icarus::staging {
41
42// Forward declaration
44
45// =============================================================================
46// TrimSolver Interface
47// =============================================================================
48
53 public:
54 virtual ~TrimSolver() = default;
55
66 virtual ::icarus::staging::TrimResult Solve(::icarus::Simulator &sim,
67 const TrimConfig &config) = 0;
68};
69
70// =============================================================================
71// FiniteDifferenceTrim
72// =============================================================================
73
88 public:
89 struct Options {
90 double step_size{1e-7};
91 double tolerance{1e-6};
92 int max_iterations{100};
93 double damping{1.0};
94 bool verbose{false};
95 };
96
97 FiniteDifferenceTrim() : opts_{} {}
98 explicit FiniteDifferenceTrim(Options opts) : opts_(std::move(opts)) {}
99
101 const TrimConfig &config) override;
102
103 private:
104 Options opts_;
105
107 Eigen::VectorXd EvaluateResidual(::icarus::Simulator &sim,
108 const std::vector<std::string> &zero_derivatives, double t);
109
111 Eigen::MatrixXd ComputeJacobian(::icarus::Simulator &sim,
112 const std::vector<std::string> &control_signals,
113 const std::vector<std::string> &zero_derivatives, double t);
114
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);
118};
119
120// =============================================================================
121// SymbolicTrim
122// =============================================================================
123
132class SymbolicTrim : public TrimSolver {
133 public:
135 const TrimConfig &config) override;
136
137 private:
139 janus::Function BuildResidualFunction(SymbolicSimulatorCore &sym_sim, const TrimConfig &config);
140};
141
142// =============================================================================
143// WarmstartSolver
144// =============================================================================
145
156 public:
158 const TrimConfig &config) override;
159
160 private:
162 ValidationResult ValidateRecording(const ::icarus::Simulator &sim,
163 const vulcan::io::HDF5Reader &reader);
164
166 size_t FindFrameIndex(const std::vector<double> &times, double target_time);
167
169 void RestoreState(::icarus::Simulator &sim, vulcan::io::HDF5Reader &reader, size_t frame_idx);
170};
171
172// =============================================================================
173// Factory
174// =============================================================================
175
184inline std::unique_ptr<TrimSolver> CreateTrimSolver(const TrimConfig &config,
185 bool symbolic_enabled) {
186 // Warmstart mode - load from recording
187 if (config.IsWarmstart()) {
188 return std::make_unique<WarmstartSolver>();
189 }
190
191 // Equilibrium mode - solve for trim conditions
192 if (symbolic_enabled && config.method == "newton") {
193 return std::make_unique<SymbolicTrim>();
194 }
195 // Default to finite differences (works without symbolic)
197 opts.tolerance = config.tolerance;
198 opts.max_iterations = config.max_iterations;
199 return std::make_unique<FiniteDifferenceTrim>(opts);
200}
201
202} // namespace icarus::staging
203
204// =============================================================================
205// Implementation
206// =============================================================================
207// Note: Full implementation requires Simulator definition.
208// Include this header after Simulator.hpp, or implement in .cpp file.
209
211
212namespace icarus::staging {
213
214inline ::icarus::staging::TrimResult FiniteDifferenceTrim::Solve(::icarus::Simulator &sim,
215 const TrimConfig &config) {
217
218 const auto &controls = config.control_signals;
219 const auto &derivs = config.zero_derivatives;
220 const int n_controls = static_cast<int>(controls.size());
221 const int n_residuals = static_cast<int>(derivs.size());
222
223 if (n_controls == 0) {
224 result.message = "No control signals specified";
225 return result;
226 }
227 if (n_residuals == 0) {
228 result.message = "No zero_derivatives specified";
229 return result;
230 }
231
232 // Log start
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);
237 }
238 sim.GetLogger().LogTrimStart("finite-difference", targets);
239
240 // Initialize controls from config or current values
241 Eigen::VectorXd u(n_controls);
242 for (int i = 0; i < n_controls; ++i) {
243 auto it = config.initial_guesses.find(controls[i]);
244 if (it != config.initial_guesses.end()) {
245 u(i) = it->second;
246 } else {
247 u(i) = sim.Peek(controls[i]);
248 }
249 }
250
251 const double t = sim.Time();
252
253 // Newton iteration
254 for (int iter = 0; iter < opts_.max_iterations; ++iter) {
255 // Apply current controls
256 for (int i = 0; i < n_controls; ++i) {
257 sim.Poke(controls[i], u(i));
258 }
259
260 // Evaluate residual
261 Eigen::VectorXd F = EvaluateResidual(sim, derivs, t);
262 double norm = F.norm();
263
264 if (opts_.verbose) {
265 sim.GetLogger().LogTrimIteration(iter, norm);
266 }
267
268 // Check convergence
269 if (norm < opts_.tolerance) {
270 result.converged = true;
271 result.iterations = iter;
272 result.residual_norm = norm;
273 result.message = "Converged";
274 break;
275 }
276
277 // Compute Jacobian
278 Eigen::MatrixXd J = ComputeJacobian(sim, controls, derivs, t);
279
280 // Solve J * du = -F using SVD-based pseudo-inverse for robustness
281 // This handles rank-deficient, underdetermined, and overdetermined cases
282 // automatically with tolerance-based singular value thresholding.
283 //
284 // SVD tolerance: singular values below this fraction of the largest
285 // are treated as zero, providing numerical stability for near-singular
286 // Jacobians. Default threshold is O(machine_epsilon * max_dim).
287 Eigen::JacobiSVD<Eigen::MatrixXd> svd(J, Eigen::ComputeThinU | Eigen::ComputeThinV);
288
289 // Set threshold for singular values (relative to largest)
290 // Values below threshold * max_singular_value are treated as zero
291 constexpr double svd_tolerance = 1e-10;
292 svd.setThreshold(svd_tolerance);
293
294 // SVD.solve() automatically computes:
295 // - Minimum-norm solution for underdetermined systems (n_controls > n_residuals)
296 // - Least-squares solution for overdetermined systems (n_residuals > n_controls)
297 // - Exact solution for square full-rank systems
298 Eigen::VectorXd du = svd.solve(-F);
299
300 // Update with damping
301 u += opts_.damping * du;
302
303 // Apply bounds
304 ApplyBounds(u, controls, config.control_bounds);
305
306 result.iterations = iter + 1;
307 result.residual_norm = norm;
308 }
309
310 if (!result.converged) {
311 result.message =
312 "Max iterations reached (residual = " + std::to_string(result.residual_norm) + ")";
313 }
314
315 // Store final values and apply to simulator
316 for (int i = 0; i < n_controls; ++i) {
317 result.controls[controls[i]] = u(i);
318 sim.Poke(controls[i], u(i));
319 }
320
321 // Store final residuals
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);
325 }
326
327 if (result.converged) {
329 } else {
330 sim.GetLogger().LogTrimFailed(result.message);
331 }
332
333 return result;
334}
335
336inline Eigen::VectorXd
337FiniteDifferenceTrim::EvaluateResidual(::icarus::Simulator &sim,
338 const std::vector<std::string> &zero_derivatives, double t) {
339 // Compute all derivatives at current state
340 sim.ComputeDerivatives(t);
341
342 // Extract selected derivatives
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]);
347 }
348 return F;
349}
350
351inline Eigen::MatrixXd
352FiniteDifferenceTrim::ComputeJacobian(::icarus::Simulator &sim,
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());
357 const double h = opts_.step_size;
358
359 Eigen::MatrixXd J(n_residuals, n_controls);
360
361 for (int j = 0; j < n_controls; ++j) {
362 double u0 = sim.Peek(control_signals[j]);
363
364 // Forward perturbation
365 sim.Poke(control_signals[j], u0 + h);
366 Eigen::VectorXd F_plus = EvaluateResidual(sim, zero_derivatives, t);
367
368 // Backward perturbation
369 sim.Poke(control_signals[j], u0 - h);
370 Eigen::VectorXd F_minus = EvaluateResidual(sim, zero_derivatives, t);
371
372 // Central difference: dF/du ≈ (F(u+h) - F(u-h)) / (2h)
373 J.col(j) = (F_plus - F_minus) / (2.0 * h);
374
375 // Restore original value
376 sim.Poke(control_signals[j], u0);
377 }
378
379 return J;
380}
381
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);
390 }
391 }
392}
393
394// =============================================================================
395// SymbolicTrim Implementation
396// =============================================================================
397
398inline ::icarus::staging::TrimResult SymbolicTrim::Solve(::icarus::Simulator &sim,
399 const TrimConfig &config) {
401
402 const auto &controls = config.control_signals;
403 const auto &derivs = config.zero_derivatives;
404 const int n_controls = static_cast<int>(controls.size());
405 const int n_residuals = static_cast<int>(derivs.size());
406
407 if (n_controls == 0) {
408 result.message = "No control signals specified";
409 return result;
410 }
411 if (n_residuals == 0) {
412 result.message = "No zero_derivatives specified";
413 return result;
414 }
415
416 // Log start
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);
421 }
422 sim.GetLogger().LogTrimStart("symbolic-newton", targets);
423
424 // Create symbolic simulator from same config
425 try {
426 SymbolicSimulatorCore sym_sim(sim.GetConfig());
427
428 // Build symbolic residual function
429 janus::Function F = BuildResidualFunction(sym_sim, config);
430
431 // Configure Newton solver
432 janus::RootFinderOptions opts;
433 opts.abstol = config.tolerance;
434 opts.max_iter = config.max_iterations;
435 opts.line_search = true;
436 opts.verbose = false;
437
438 janus::NewtonSolver solver(F, opts);
439
440 // Get initial guess from config or current simulator values
441 Eigen::VectorXd u0(n_controls);
442 for (int i = 0; i < n_controls; ++i) {
443 auto it = config.initial_guesses.find(controls[i]);
444 if (it != config.initial_guesses.end()) {
445 u0(i) = it->second;
446 } else {
447 u0(i) = sim.Peek(controls[i]);
448 }
449 }
450
451 // Solve
452 auto root_result = solver.solve(u0);
453
454 // Convert to TrimResult
455 result.converged = root_result.converged;
456 result.iterations = root_result.iterations;
457 result.message = root_result.message;
458
459 if (result.converged) {
460 // Apply solution to numeric simulator
461 for (int i = 0; i < n_controls; ++i) {
462 double val = root_result.x(i);
463 result.controls[controls[i]] = val;
464 sim.Poke(controls[i], val);
465 }
466
467 // Evaluate final residuals
468 double t = sim.Time();
469 sim.ComputeDerivatives(t);
470 for (int i = 0; i < n_residuals; ++i) {
471 result.residuals[derivs[i]] = sim.Peek(derivs[i]);
472 }
473
474 // Compute residual norm
475 double norm = 0.0;
476 for (const auto &[name, val] : result.residuals) {
477 norm += val * val;
478 }
479 result.residual_norm = std::sqrt(norm);
480
482 } else {
483 sim.GetLogger().LogTrimFailed(result.message);
484 }
485
486 } catch (const Error &e) {
487 result.converged = false;
488 result.message = std::string("Symbolic trim failed: ") + e.what();
489 sim.GetLogger().LogTrimFailed(result.message);
490 } catch (const std::exception &e) {
491 result.converged = false;
492 result.message = std::string("Symbolic trim failed: ") + e.what();
493 sim.GetLogger().LogTrimFailed(result.message);
494 }
495
496 return result;
497}
498
499inline janus::Function SymbolicTrim::BuildResidualFunction(SymbolicSimulatorCore &sym_sim,
500 const TrimConfig &config) {
501 using Scalar = janus::SymbolicScalar;
502
503 const int n_controls = static_cast<int>(config.control_signals.size());
504 const int n_residuals = static_cast<int>(config.zero_derivatives.size());
505 const std::size_t n_states = sym_sim.GetStateSize();
506
507 // Create symbolic control variables
508 auto [u_vec, u_mx] = janus::sym_vec_pair("u", n_controls);
509
510 // Create symbolic state and time (use current numeric values as base)
511 auto [x_vec, x_mx] = janus::sym_vec_pair("x", static_cast<int>(n_states));
512 Scalar t_sym = janus::sym("t");
513
514 // Set symbolic state and time
515 sym_sim.SetState(x_vec);
516 sym_sim.SetTime(t_sym);
517
518 // Apply symbolic controls to simulator
519 for (int i = 0; i < n_controls; ++i) {
520 sym_sim.SetSignal(config.control_signals[i], u_vec(i));
521 }
522
523 // Compute derivatives symbolically (traces the graph)
524 sym_sim.ComputeDerivatives();
525
526 // Gather residuals (the derivatives we want to be zero)
527 std::vector<Scalar> residual_elements;
528 residual_elements.reserve(n_residuals);
529 for (const auto &deriv_name : config.zero_derivatives) {
530 if (!sym_sim.HasSignal(deriv_name)) {
531 throw ConfigError("SymbolicTrim: Unknown derivative signal '" + deriv_name + "'");
532 }
533 residual_elements.push_back(sym_sim.GetSignal(deriv_name));
534 }
535
536 // Concatenate into single output vector
537 Scalar residuals = Scalar::vertcat(residual_elements);
538
539 // Build function: F(u) -> residuals
540 // Note: x and t are fixed parameters (not optimized), u is the decision variable
541 return janus::Function("trim_residual", {u_mx}, {residuals});
542}
543
544// =============================================================================
545// WarmstartSolver Implementation
546// =============================================================================
547
548inline ::icarus::staging::TrimResult WarmstartSolver::Solve(::icarus::Simulator &sim,
549 const TrimConfig &config) {
551
552 // Validate config
553 if (config.recording_path.empty()) {
554 result.converged = false;
555 result.message = "No recording_path specified for warmstart";
556 return result;
557 }
558
559 sim.GetLogger().Log(LogLevel::Info, "[WARMSTART] Loading state from " + config.recording_path +
560 " at t=" + std::to_string(config.resume_time));
561
562 try {
563 // Open recording file
564 vulcan::io::HDF5Reader reader(config.recording_path);
565
566 // Validate recording if requested
567 if (config.validate_schema) {
568 auto validation = ValidateRecording(sim, reader);
569 if (!validation.IsValid()) {
570 result.converged = false;
571 result.message = "Recording validation failed: " + validation.GetErrors()[0];
572 sim.GetLogger().Log(LogLevel::Error, "[WARMSTART] " + result.message);
573 return result;
574 }
575 // Log warnings
576 for (const auto &warning : validation.GetWarnings()) {
577 sim.GetLogger().Log(LogLevel::Warning, "[WARMSTART] " + warning);
578 }
579 }
580
581 // Find frame index for resume_time
582 auto times = reader.times();
583 if (times.empty()) {
584 result.converged = false;
585 result.message = "Recording contains no frames";
586 return result;
587 }
588
589 size_t frame_idx = FindFrameIndex(times, config.resume_time);
590 double actual_time = times[frame_idx];
591
592 sim.GetLogger().Log(LogLevel::Debug, "[WARMSTART] Using frame " +
593 std::to_string(frame_idx) +
594 " at t=" + std::to_string(actual_time));
595
596 // Restore state signals
597 RestoreState(sim, reader, frame_idx);
598
599 // Set simulation time
600 sim.SetTime(actual_time);
601
602 result.converged = true;
603 result.iterations = 0;
604 result.residual_norm = 0.0;
605 result.message = "Warmstart complete from " + config.recording_path +
606 " at t=" + std::to_string(actual_time);
607
608 sim.GetLogger().Log(LogLevel::Info, "[WARMSTART] State restored successfully");
609
610 } catch (const std::exception &e) {
611 result.converged = false;
612 result.message = std::string("Warmstart failed: ") + e.what();
613 sim.GetLogger().Log(LogLevel::Error, "[WARMSTART] " + result.message);
614 }
615
616 return result;
617}
618
619inline ValidationResult WarmstartSolver::ValidateRecording(const ::icarus::Simulator &sim,
620 const vulcan::io::HDF5Reader &reader) {
621 ValidationResult result;
622
623 // Get recorded signal names
624 auto recorded_signals = reader.signal_names();
625 std::unordered_set<std::string> recorded_set(recorded_signals.begin(), recorded_signals.end());
626
627 // Get state pairs from registry
628 const auto &registry = sim.Registry();
629 const auto &state_pairs = registry.get_state_pairs();
630
631 // Check that all state signals exist in recording
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);
635 }
636 }
637
638 // Check for extra signals in recording (info only)
639 auto current_signals = registry.get_all_signal_names();
640 std::unordered_set<std::string> current_set(current_signals.begin(), current_signals.end());
641
642 for (const auto &sig : recorded_signals) {
643 if (!current_set.contains(sig)) {
644 result.AddInfo("Extra signal in recording (will be ignored)", sig);
645 }
646 }
647
648 return result;
649}
650
651inline size_t WarmstartSolver::FindFrameIndex(const std::vector<double> &times,
652 double target_time) {
653 if (times.empty()) {
654 return 0;
655 }
656
657 // Find closest frame using binary search
658 auto it = std::lower_bound(times.begin(), times.end(), target_time);
659
660 if (it == times.end()) {
661 // target_time is beyond last frame, use last frame
662 return times.size() - 1;
663 }
664
665 if (it == times.begin()) {
666 return 0;
667 }
668
669 // Check which is closer: *it or *(it-1)
670 auto prev = it - 1;
671 if (std::abs(*it - target_time) < std::abs(*prev - target_time)) {
672 return static_cast<size_t>(std::distance(times.begin(), it));
673 }
674 return static_cast<size_t>(std::distance(times.begin(), prev));
675}
676
677inline void WarmstartSolver::RestoreState(::icarus::Simulator &sim, vulcan::io::HDF5Reader &reader,
678 size_t frame_idx) {
679 const auto &registry = sim.Registry();
680 const auto &state_pairs = registry.get_state_pairs();
681
682 // Build state vector in correct order
683 Eigen::VectorXd X(static_cast<Eigen::Index>(state_pairs.size()));
684
685 for (size_t i = 0; i < state_pairs.size(); ++i) {
686 const auto &pair = state_pairs[i];
687 try {
688 // Read single value at frame_idx
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];
692 } else {
693 X(static_cast<Eigen::Index>(i)) = 0.0;
694 }
695 } catch (const std::exception &e) {
696 // Signal might not be in recording - use 0
697 X(static_cast<Eigen::Index>(i)) = 0.0;
698 }
699 }
700
701 // SetState updates both StateManager and registry signals
702 sim.SetState(X);
703}
704
705} // namespace icarus::staging
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
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