Icarus
Vehicle Simulation as a Transformable Computational Graph, built on Vulcan and Janus
Loading...
Searching...
No Matches
Simulator.hpp
Go to the documentation of this file.
1#pragma once
2
13
14#include <functional>
34#include <memory>
35#include <optional>
36#include <regex>
37#include <string>
38#include <unordered_map>
39#include <unordered_set>
40#include <vector>
41
42// Janus includes for symbolic graph generation
43#include <janus/core/Function.hpp>
44#include <janus/core/JanusTypes.hpp>
45
46// Vulcan time infrastructure
47#include <vulcan/time/Epoch.hpp>
48
49// Recording
51
52// Forward declarations for staging
53namespace icarus::staging {
54class TrimSolver;
55class Linearizer;
56} // namespace icarus::staging
57
58namespace icarus {
59
70class Simulator {
71 public:
72 // =========================================================================
73 // CORE LIFECYCLE (The 4 Operations)
74 // =========================================================================
75
85 [[nodiscard]] static std::unique_ptr<Simulator> FromConfig(const std::string &config_path);
86
90 [[nodiscard]] static std::unique_ptr<Simulator> FromConfig(const SimulatorConfig &config);
91
95 Simulator();
96
98 ~Simulator();
99
100 // Non-copyable, non-movable (Backplane holds reference to registry_)
101 Simulator(const Simulator &) = delete;
102 Simulator &operator=(const Simulator &) = delete;
103 Simulator(Simulator &&) = delete;
105
115 void Stage();
116 void Stage(const StageConfig &config);
117
126 void Step(double dt);
127 void Step(); // Uses config dt
128
129 // =========================================================================
130 // QUERY INTERFACE (Read-only)
131 // =========================================================================
132
134 [[nodiscard]] const std::string &Name() const { return config_.name; }
135
137 [[nodiscard]] double Dt() const { return config_.dt; }
138
140 [[nodiscard]] double EndTime() const { return config_.t_end; }
141
143 [[nodiscard]] double Time() const { return epoch_ - epoch_start_; }
144
146 [[nodiscard]] const vulcan::time::NumericEpoch &Epoch() const { return epoch_; }
147
149 [[nodiscard]] double JD_UTC() const { return epoch_.jd_utc(); }
150
152 [[nodiscard]] double JD_TAI() const { return epoch_.jd_tai(); }
153
155 [[nodiscard]] double JD_TT() const { return epoch_.jd_tt(); }
156
158 [[nodiscard]] double JD_GPS() const { return epoch_.jd_gps(); }
159
161 [[nodiscard]] int GPSWeek() const { return epoch_.gps_week(); }
162
164 [[nodiscard]] double GPSSecondsOfWeek() const { return epoch_.gps_seconds_of_week(); }
165
167 [[nodiscard]] std::string ISO8601() const { return epoch_.to_iso_string(); }
168
170 [[nodiscard]] Lifecycle GetLifecycle() const { return lifecycle_; }
171
173 [[nodiscard]] int32_t GetFlightPhase() const { return phase_manager_.CurrentPhase(); }
174
176 [[nodiscard]] std::string GetFlightPhaseName() const {
177 return phase_manager_.CurrentPhaseName();
178 }
179
181 [[nodiscard]] bool IsInitialized() const { return lifecycle_ >= Lifecycle::Staged; }
182
190 [[nodiscard]] double Peek(const std::string &name) const { return registry_.GetByName(name); }
191
193 [[nodiscard]] DataDictionary GetDataDictionary() const;
194
196 [[nodiscard]] IntrospectionGraph GetIntrospectionGraph() const;
197
199 [[nodiscard]] const SignalRegistry<double> &Registry() const { return registry_; }
200
201 // =========================================================================
202 // CONTROL INTERFACE (Runtime modification)
203 // =========================================================================
204
211 void Poke(const std::string &name, double value) { registry_.SetByName(name, value); }
212
216 [[nodiscard]] MissionLogger &GetLogger() { return logger_; }
217 [[nodiscard]] const MissionLogger &GetLogger() const { return logger_; }
218
223 void Reset();
224
233 void SetTime(double met);
234
236 using InputSourceCallback = std::function<double(const std::string &)>;
237
239 using OutputObserverCallback = std::function<void(const std::string &, double)>;
240
242 void SetInputSource(const std::string &signal_name, InputSourceCallback callback);
243
245 void SetOutputObserver(const std::string &signal_name, OutputObserverCallback callback);
246
248 void ClearInputSource(const std::string &signal_name);
249
251 void ClearOutputObserver(const std::string &signal_name);
252
253 // =========================================================================
254 // EXPERT INTERFACE (Advanced users)
255 // =========================================================================
256
258 [[nodiscard]] Eigen::VectorXd GetState() const;
259
261 void SetState(const Eigen::VectorXd &X);
262
264 Eigen::VectorXd ComputeDerivatives(double t);
265
267 AdaptiveStepResult<double> AdaptiveStep(double dt_request);
268
273 [[nodiscard]] std::optional<janus::Function> GetDynamicsGraph() const;
274
279 [[nodiscard]] std::optional<janus::Function> GetJacobian() const;
280
285 [[nodiscard]] const std::optional<staging::TrimResult> &GetTrimResult() const {
286 return trim_result_;
287 }
288
293 [[nodiscard]] const std::optional<staging::LinearModel> &GetLinearModel() const {
294 return linear_model_;
295 }
296
300 [[nodiscard]] const SimulatorConfig &GetConfig() const { return config_; }
301
302 // =========================================================================
303 // PROGRAMMATIC SETUP (Alternative to FromConfig)
304 // =========================================================================
305
307 void AddComponent(std::unique_ptr<Component<double>> component);
308
310 void AddComponent(const ComponentConfig &config);
311
313 void Configure(const SimulatorConfig &config);
314
316 void AddRoutes(const std::vector<signal::SignalRoute> &routes);
317
319 [[nodiscard]] std::size_t NumComponents() const { return components_.size(); }
320
322 [[nodiscard]] Backplane<double> &GetBackplane() { return backplane_; }
323 [[nodiscard]] const Backplane<double> &GetBackplane() const { return backplane_; }
324
325 private:
326 // =========================================================================
327 // PRIVATE METHODS
328 // =========================================================================
329
331 void Provision();
332
334 void ApplyRouting();
335
337 void AllocateAndBindState();
338
340 void ApplyInitialConditions();
341
343 void ValidateWiring();
344
346 void InvokeInputSources();
347
349 void InvokeOutputObservers();
350
352 void InitializeEpoch();
353
354 // =========================================================================
355 // PRIVATE DATA
356 // =========================================================================
357
358 SimulatorConfig config_;
359
360 // Components
361 std::vector<std::unique_ptr<Component<double>>> components_;
362
363 // Signal system
364 SignalRegistry<double> registry_;
365 Backplane<double> backplane_;
367
368 // Managers
369 StateManager<double> state_manager_;
370 IntegrationManager<double> integration_manager_;
371 Scheduler scheduler_;
372 PhaseManager<double> phase_manager_;
373
374 // Logging
375 MissionLogger logger_;
376
377 // Time and lifecycle
378 vulcan::time::NumericEpoch epoch_;
379 vulcan::time::NumericEpoch epoch_start_;
381 int frame_count_ = 0;
382
383 // Symbolic results (optional, after Stage)
384 std::optional<janus::Function> dynamics_graph_;
385 std::optional<janus::Function> jacobian_;
386
387 // Recording (optional, when enabled in config)
388 std::unique_ptr<HDF5Recorder> recorder_;
389
390 // Staging results (optional, after Stage)
391 std::optional<staging::TrimResult> trim_result_;
392 std::optional<staging::LinearModel> linear_model_;
393
394 // External callbacks
395 std::unordered_map<std::string, InputSourceCallback> input_sources_;
396 std::unordered_map<std::string, OutputObserverCallback> output_observers_;
397
398 // Dependency tracking
399 std::unordered_map<Component<double> *, std::vector<std::string>> outputs_;
400 std::unordered_map<Component<double> *, std::vector<std::string>> inputs_;
401
402 // =========================================================================
403 // FRAME CONTEXT (Scheduler filtering helper)
404 // =========================================================================
405
412 struct FrameContext {
413 std::unordered_set<std::string> active_components;
414 std::unordered_set<std::string> all_scheduled_components;
415
417 [[nodiscard]] bool ShouldRun(const std::string &name) const {
418 // If component isn't registered with scheduler, run every frame
419 if (!all_scheduled_components.contains(name)) {
420 return true;
421 }
422 // Otherwise, only run if its group is active this frame
423 return active_components.contains(name);
424 }
425 };
426
428 [[nodiscard]] FrameContext BuildFrameContext() const;
429
431 void ExecComponent(Component<double> &comp, void (Component<double>::*method)(double, double),
432 double t, double dt);
433};
434
435} // namespace icarus
436
437// =============================================================================
438// INLINE IMPLEMENTATIONS
439// =============================================================================
440
441// Include staging solvers for Stage() implementation
442// (Must be outside namespace icarus to avoid double-nesting)
446
447namespace icarus {
448
450 // Close recorder and log summary
451 if (recorder_) {
452 size_t frames = recorder_->FrameCount();
453 recorder_->Close();
454 logger_.Log(LogLevel::Info, "[REC] Recording complete: " + std::to_string(frames) +
455 " frames written to " + config_.recording.path);
456 recorder_.reset();
457 }
458
459 // If we ever ran, log the debrief on destruction (RAII)
460 if (lifecycle_ == Lifecycle::Running) {
461 logger_.BeginLifecycle(LifecycleStrings::Shutdown);
462 auto wall_time = std::chrono::duration<double>(logger_.WallElapsed()).count();
463 logger_.LogDebrief(Time(), wall_time);
464 }
465}
466
467inline Simulator::Simulator() : backplane_(registry_) { integration_manager_.ConfigureDefault(); }
468
469inline std::unique_ptr<Simulator> Simulator::FromConfig(const std::string &config_path) {
470 // Load YAML configuration
471 SimulatorConfig config = io::SimulationLoader::Load(config_path);
472 auto sim = FromConfig(config);
473 if (sim) {
474 sim->logger_.LogConfigFile(config_path);
475 }
476 return sim;
477}
478
479inline std::unique_ptr<Simulator> Simulator::FromConfig(const SimulatorConfig &config) {
480 auto sim = std::make_unique<Simulator>();
481 sim->Configure(config);
482
483 // Log startup banner (Icarus engine version)
484 sim->logger_.LogStartup();
485
486 // Log simulation configuration (from YAML)
487 sim->logger_.LogSimulationConfig(config.name, config.version, config.description);
488
489 // Log time and integrator settings
490 sim->logger_.LogTimeConfig(config.t_start, config.t_end, config.dt);
491 sim->logger_.LogIntegrator(config.integrator.type);
492
493 // Log phase configuration
494 sim->logger_.LogPhaseConfig(config.phases);
495
496 // Begin Provision phase
497 sim->logger_.BeginLifecycle(LifecycleStrings::Provision);
498
499 // Create components from factory
500 auto &factory = ComponentFactory<double>::Instance();
501 std::size_t comp_count = config.components.size();
502 for (std::size_t i = 0; i < comp_count; ++i) {
503 const auto &comp_cfg = config.components[i];
504 auto component = factory.Create(comp_cfg);
505
506 // Log component addition
507 bool is_last = (i == comp_count - 1);
508 sim->logger_.LogComponentAdd(comp_cfg.name, comp_cfg.type, "YAML", is_last);
509
510 sim->AddComponent(std::move(component));
511 }
512
513 // Add routes from config
514 sim->AddRoutes(config.routes);
515
516 // Provision all components
517 sim->Provision();
518
519 // Log state allocation
520 sim->logger_.LogStateAllocation(sim->state_manager_.TotalSize());
521
522 // Generate and log manifest (signal dictionary)
523 auto dict = sim->GetDataDictionary();
524 sim->logger_.LogManifest(dict);
525
526 sim->logger_.EndLifecycle();
527
528 return sim;
529}
530
531inline void Simulator::InitializeEpoch() {
532 // Parse epoch configuration - always initialize epoch (single source of truth)
533 if (config_.epoch.system == "UTC" && !config_.epoch.reference.empty()) {
534 // Robust ISO 8601 parser with timezone offset and fractional seconds support
535 // Format: YYYY-MM-DDTHH:MM:SS[.fractional][Z|+HH:MM|-HH:MM|+HHMM|-HHMM]
536 static const std::regex iso8601_regex(
537 R"(^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|([+-])(\d{2}):?(\d{2}))?$)");
538 // Groups: 1=year, 2=month, 3=day, 4=hour, 5=minute, 6=whole_seconds
539 // 7=fractional_seconds (optional, includes leading '.')
540 // 8=timezone block (optional)
541 // 9=tz_sign (+ or -), 10=tz_hours, 11=tz_minutes
542
543 std::smatch match;
544 if (!std::regex_match(config_.epoch.reference, match, iso8601_regex)) {
545 throw ConfigError("Invalid epoch reference format: " + config_.epoch.reference +
546 " (expected ISO 8601: YYYY-MM-DDTHH:MM:SS[.sss][Z|±HH:MM])");
547 }
548
549 // Extract date/time components
550 int year = std::stoi(match[1].str());
551 int month = std::stoi(match[2].str());
552 int day = std::stoi(match[3].str());
553 int hour = std::stoi(match[4].str());
554 int min = std::stoi(match[5].str());
555 double sec = std::stod(match[6].str());
556
557 // Add fractional seconds if present
558 if (match[7].matched) {
559 sec += std::stod(match[7].str());
560 }
561
562 // Parse timezone offset and convert to UTC
563 if (match[8].matched && match[8].str() != "Z") {
564 // Has explicit offset (not Z)
565 int tz_sign = (match[9].str() == "-") ? -1 : 1;
566 int tz_hours = std::stoi(match[10].str());
567 int tz_minutes = std::stoi(match[11].str());
568 int offset_minutes = tz_sign * (tz_hours * 60 + tz_minutes);
569
570 // Convert local time to UTC by subtracting the offset
571 // (positive offset = ahead of UTC, so subtract to get UTC)
572 int total_minutes = hour * 60 + min - offset_minutes;
573
574 // Handle day rollover
575 while (total_minutes < 0) {
576 total_minutes += 24 * 60;
577 day -= 1;
578 }
579 while (total_minutes >= 24 * 60) {
580 total_minutes -= 24 * 60;
581 day += 1;
582 }
583
584 hour = total_minutes / 60;
585 min = total_minutes % 60;
586
587 // Handle month/year rollover (simplified - use days in month)
588 auto days_in_month = [](int y, int m) -> int {
589 static constexpr int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
590 if (m == 2) {
591 bool leap = (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0);
592 return leap ? 29 : 28;
593 }
594 return days[m - 1];
595 };
596
597 while (day < 1) {
598 month -= 1;
599 if (month < 1) {
600 month = 12;
601 year -= 1;
602 }
603 day += days_in_month(year, month);
604 }
605 while (day > days_in_month(year, month)) {
606 day -= days_in_month(year, month);
607 month += 1;
608 if (month > 12) {
609 month = 1;
610 year += 1;
611 }
612 }
613 }
614 // else: Z suffix or no suffix means UTC - no conversion needed
615
616 epoch_start_ = vulcan::time::NumericEpoch::from_utc(year, month, day, hour, min, sec);
617 } else if (config_.epoch.system == "TAI" && config_.epoch.jd > 0.0) {
618 epoch_start_ = vulcan::time::NumericEpoch::from_jd_tai(config_.epoch.jd);
619 } else if (config_.epoch.system == "GPS" && config_.epoch.gps_configured) {
620 epoch_start_ = vulcan::time::NumericEpoch::from_gps_week(config_.epoch.gps_week,
621 config_.epoch.gps_seconds);
622 } else {
623 // MET-only mode or default: use J2000.0 as arbitrary reference
624 epoch_start_ = vulcan::time::NumericEpoch();
625 }
626
627 // Current epoch starts at epoch_start_ (MET = 0)
628 epoch_ = epoch_start_;
629}
630
631inline void Simulator::Configure(const SimulatorConfig &config) {
632 config_ = config;
633
634 // Configure logger from YAML settings
635 if (!config_.logging.file_path.empty() && config_.logging.file_enabled) {
636 logger_.SetLogFile(config_.logging.file_path);
637 }
638 logger_.SetConsoleLevel(config_.logging.console_level);
639 logger_.SetFileLevel(config_.logging.file_level);
640 logger_.SetProgressEnabled(config_.logging.progress_enabled);
641 logger_.SetProfilingEnabled(config_.logging.profiling_enabled);
642 logger_.SetVersion(config_.version);
643
644 // Configure integrator
645 integration_manager_.Configure(config_.integrator.type);
646
647 // Configure scheduler
648 double sim_rate = config_.scheduler.MaxRate();
649 auto timing_errors = config_.scheduler.ValidateGlobalTiming(sim_rate);
650 if (!timing_errors.empty()) {
651 throw ConfigError("Scheduler timing validation failed: " + timing_errors[0]);
652 }
653 scheduler_.Configure(config_.scheduler, sim_rate);
654
655 // Configure phase manager (if phases defined)
656 if (!config_.phases.definitions.empty()) {
657 phase_manager_.Configure(config_.phases);
658 }
659
660 // Initialize epoch from configuration
661 InitializeEpoch();
662}
663
664inline void Simulator::AddComponent(std::unique_ptr<Component<double>> component) {
665 components_.push_back(std::move(component));
666}
667
668inline void Simulator::AddComponent(const ComponentConfig &config) {
669 auto &factory = ComponentFactory<double>::Instance();
670 auto component = factory.Create(config);
671 components_.push_back(std::move(component));
672}
673
674inline void Simulator::AddRoutes(const std::vector<signal::SignalRoute> &routes) {
675 for (const auto &route : routes) {
676 router_.AddRoute(route);
677 }
678}
679
680inline void Simulator::Provision() {
681 if (lifecycle_ != Lifecycle::Uninitialized) {
682 throw LifecycleError(LifecyclePhase::Provision, "can only be called once");
683 }
684
685 // Provision each component
686 for (auto &comp : components_) {
687 backplane_.set_context(comp->Entity(), comp->Name());
688 backplane_.clear_tracking();
689
690 try {
691 comp->Provision(backplane_);
692 } catch (const Error &e) {
693 backplane_.clear_context();
694 LogError(e, 0.0, comp->FullName());
695 throw;
696 }
697 comp->MarkProvisioned();
698
699 outputs_[comp.get()] = backplane_.registered_outputs();
700 backplane_.clear_context();
701 }
702
703 // Apply routing
704 ApplyRouting();
705
706 // Allocate and bind state
707 AllocateAndBindState();
708
709 lifecycle_ = Lifecycle::Provisioned;
710}
711
712inline void Simulator::ApplyRouting() { router_.ApplyRoutes(backplane_); }
713
714inline void Simulator::AllocateAndBindState() {
715 // Phase 6: Discover states from registry instead of scanning components
716 state_manager_.DiscoverStates(registry_);
717}
718
719inline void Simulator::ApplyInitialConditions() {
720 // ICs are typically applied in component Stage() methods
721 // Config-driven ICs could be applied here in the future
722}
723
724inline void Simulator::ValidateWiring() {
725 auto unwired = registry_.get_unwired_inputs();
726 if (!unwired.empty()) {
727 std::string msg;
728 for (const auto &name : unwired) {
729 msg += name + ", ";
730 }
731 // Remove trailing ", "
732 if (msg.size() >= 2) {
733 msg.resize(msg.size() - 2);
734 }
735
736 if (config_.staging.validate_wiring) {
737 throw WiringError("Unwired inputs: " + msg);
738 }
739 // Warn only - unwired inputs default to 0, can be poked externally
740 logger_.Log(LogLevel::Warning, "[WIRE] Unwired inputs (defaulting to 0): " + msg);
741 }
742}
743
744inline void Simulator::Stage() {
745 // Auto-provision if components were added programmatically
746 if (lifecycle_ == Lifecycle::Uninitialized && !components_.empty()) {
747 Provision();
748 }
749
750 if (lifecycle_ != Lifecycle::Provisioned) {
751 throw LifecycleError(LifecyclePhase::Stage, "requires components to be added first");
752 }
753
754 // Begin Stage phase logging
755 logger_.BeginLifecycle(LifecycleStrings::Stage);
756
757 // =========================================================================
758 // Automatic Topology Ordering (if enabled)
759 // =========================================================================
760 if (config_.scheduler.topology.mode == SchedulingMode::Automatic) {
761 logger_.Log(LogLevel::Debug, "[TOPO] Computing execution order from signal dependencies");
762
763 // Build dependency graph and compute topological order
765 config_, config_.scheduler.topology.cycle_detection);
766
767 if (result.has_cycles) {
768 // Log cycle warnings
769 for (const auto &cycle : result.cycles) {
770 logger_.Log(LogLevel::Warning, "[TOPO] " + cycle.ToString());
771 }
772 }
773
774 // Apply topology order to scheduler
775 TopologyAnalyzer::ApplyTopologyOrder(config_.scheduler, result);
776
777 // Reconfigure scheduler with updated order
778 scheduler_.Configure(config_.scheduler, config_.scheduler.MaxRate());
779
780 logger_.Log(
781 LogLevel::Info, "[TOPO] Computed execution order: " + [&]() {
782 std::string s;
783 for (size_t i = 0; i < result.execution_order.size(); ++i) {
784 s += result.execution_order[i];
785 if (i < result.execution_order.size() - 1)
786 s += " -> ";
787 }
788 return s;
789 }());
790 }
791
792 // Log wiring info (Debug level)
793 for (const auto &route : router_.GetRoutes()) {
794 std::string from = route.output_path;
795 std::string to = route.input_path;
796 logger_.LogWiring(from, to);
797 }
798
799 // Log scheduler execution order (Debug level)
800 scheduler_.LogExecutionOrder(&logger_);
801
802 // Set epoch reference on backplane for component binding
803 backplane_.set_epoch(&epoch_);
804
805 // Stage each component (config loading, input wiring)
806 for (auto &comp : components_) {
807 backplane_.set_context(comp->Entity(), comp->Name());
808 backplane_.clear_tracking();
809
810 // Bind epoch to component for time-dependent calculations
811 backplane_.bind_epoch_to(*comp);
812
813 try {
814 comp->Stage(backplane_);
815 } catch (const Error &e) {
816 backplane_.clear_context();
817 LogError(e, 0.0, comp->FullName());
818 throw;
819 }
820 comp->MarkStaged();
821
822 inputs_[comp.get()] = backplane_.resolved_inputs();
823 backplane_.clear_context();
824 }
825
826 // Validate wiring
827 ValidateWiring();
828
829 // =========================================================================
830 // Trim Optimization (if enabled)
831 // =========================================================================
832 if (config_.staging.trim.enabled) {
833 // Build targets list for logging
834 auto solver =
835 staging::CreateTrimSolver(config_.staging.trim, config_.staging.symbolics.enabled);
836 auto result = solver->Solve(*this, config_.staging.trim);
837
838 if (config_.staging.trim.tolerance > 0 && !result.converged) {
839 // Only throw if strict tolerance was specified
840 throw StageError("Trim optimization failed: " + result.message);
841 }
842
843 trim_result_ = std::move(result);
844 }
845
846 // =========================================================================
847 // Linearization (if enabled)
848 // =========================================================================
849 if (config_.staging.linearization.enabled) {
850 std::ostringstream oss;
851 oss << "[LIN] Computing linear model (" << config_.staging.linearization.states.size()
852 << " states, " << config_.staging.linearization.inputs.size() << " inputs, "
853 << config_.staging.linearization.outputs.size() << " outputs)";
854 logger_.Log(LogLevel::Debug, oss.str());
855
856 auto linearizer = staging::CreateLinearizer(config_.staging.symbolics.enabled);
857 auto model = linearizer->Compute(*this, config_.staging.linearization);
858
859 // Log full linear model analysis
860 logger_.LogLinearModel(model);
861
862 // Export if configured
863 const auto &lin = config_.staging.linearization;
864 if (!lin.output_dir.empty()) {
865 if (lin.export_matlab) {
866 model.ExportMatlab(lin.output_dir + "/linear_model.m");
867 logger_.Log(LogLevel::Debug,
868 "[LIN] Exported MATLAB to " + lin.output_dir + "/linear_model.m");
869 }
870 if (lin.export_numpy) {
871 model.ExportNumPy(lin.output_dir + "/linear_model.py");
872 logger_.Log(LogLevel::Debug,
873 "[LIN] Exported NumPy to " + lin.output_dir + "/linear_model.py");
874 }
875 if (lin.export_json) {
876 model.ExportJSON(lin.output_dir + "/linear_model.json");
877 logger_.Log(LogLevel::Debug,
878 "[LIN] Exported JSON to " + lin.output_dir + "/linear_model.json");
879 }
880 }
881
882 linear_model_ = std::move(model);
883 }
884
885 // =========================================================================
886 // Symbolic Graph Generation (if enabled)
887 // =========================================================================
888 if (config_.staging.symbolics.enabled) {
889 try {
890 staging::SymbolicSimulatorCore sym_sim(config_);
891 staging::SymbolicStager stager(sym_sim);
892
893 staging::SymbolicStagerConfig stager_config;
894 stager_config.generate_dynamics = true;
895 stager_config.generate_jacobian = config_.staging.symbolics.generate_jacobian;
896 stager_config.include_time = true;
897
898 auto sym_dynamics = stager.GenerateDynamics(stager_config);
899
900 dynamics_graph_ = sym_dynamics.dynamics;
901 if (config_.staging.symbolics.generate_jacobian) {
902 jacobian_ = sym_dynamics.jacobian_x;
903 }
904
905 logger_.Log(LogLevel::Info, "[SYM] Symbolic graphs generated: " +
906 std::to_string(sym_sim.GetStateSize()) + " states");
907 } catch (const Error &e) {
908 logger_.Log(LogLevel::Warning,
909 "[SYM] Symbolic generation failed: " + std::string(e.what()));
910 // Continue - symbolic is optional
911 } catch (const std::exception &e) {
912 logger_.Log(LogLevel::Warning,
913 "[SYM] Symbolic generation failed: " + std::string(e.what()));
914 // Continue - symbolic is optional
915 }
916 }
917
918 logger_.EndLifecycle();
919
920 // Initialize recording if enabled
921 if (config_.recording.IsActive()) {
922 recorder_ = std::make_unique<HDF5Recorder>(registry_, config_.recording);
923 recorder_->Open("");
924 logger_.Log(LogLevel::Info, "[REC] Recording " +
925 std::to_string(recorder_->RecordedSignals().size()) +
926 " signals to " + config_.recording.path);
927 }
928
929 lifecycle_ = Lifecycle::Staged;
930
931 // Reset to t=0 unless warmstart mode restored time from recording
932 if (!config_.staging.trim.IsWarmstart()) {
933 epoch_ = epoch_start_;
934 }
935}
936
937inline void Simulator::Stage(const StageConfig &config) {
938 config_.staging = config;
939 Stage();
940}
941
942inline void Simulator::Step(double dt) {
943 if (lifecycle_ != Lifecycle::Staged && lifecycle_ != Lifecycle::Running) {
944 throw LifecycleError(LifecyclePhase::Step, "requires prior Stage()");
945 }
946
947 // Auto-log RUN phase on first step
948 if (lifecycle_ == Lifecycle::Staged) {
949 lifecycle_ = Lifecycle::Running;
950 logger_.BeginLifecycle(LifecycleStrings::Run);
951 logger_.LogRunStart(Time(), config_.t_end, config_.dt);
952 }
953
954 // Invoke input sources
955 InvokeInputSources();
956
957 // Build frame context (scheduler filtering)
958 auto ctx = BuildFrameContext();
959
960 // Cache current time for this step
961 double t = Time();
962
963 // If no state, just call components directly
964 if (state_manager_.TotalSize() == 0) {
965 for (auto &comp : components_) {
966 if (ctx.ShouldRun(comp->FullName())) {
967 ExecComponent(*comp, &Component<double>::PreStep, t, dt);
968 }
969 }
970 for (auto &comp : components_) {
971 if (ctx.ShouldRun(comp->FullName())) {
972 ExecComponent(*comp, &Component<double>::Step, t, dt);
973 }
974 }
975 for (auto &comp : components_) {
976 if (ctx.ShouldRun(comp->FullName())) {
977 ExecComponent(*comp, &Component<double>::PostStep, t, dt);
978 }
979 }
980 } else {
981 // Create derivative function for integrator
982 auto deriv_func = [this, &ctx, dt](double t,
983 const JanusVector<double> &x) -> JanusVector<double> {
984 state_manager_.SetState(x);
985 state_manager_.ZeroDerivatives();
986
987 for (auto &comp : components_) {
988 if (ctx.ShouldRun(comp->FullName())) {
989 ExecComponent(*comp, &Component<double>::PreStep, t, dt);
990 ExecComponent(*comp, &Component<double>::Step, t, dt);
991 ExecComponent(*comp, &Component<double>::PostStep, t, dt);
992 }
993 }
994
995 return state_manager_.GetDerivatives(ctx.active_components);
996 };
997
998 // Integrate
999 try {
1000 JanusVector<double> X = state_manager_.GetState();
1001 JanusVector<double> X_new = integration_manager_.Step(deriv_func, X, t, dt);
1002 state_manager_.SetState(X_new);
1003 } catch (const Error &e) {
1004 LogError(e, t, "Integrator");
1005 throw;
1006 }
1007 }
1008
1009 // Compute time from frame count to avoid floating-point drift
1010 // (accumulating dt compounds rounding errors; multiplying has only one)
1011 frame_count_++;
1012 epoch_ = epoch_start_ + frame_count_ * dt;
1013
1014 // Evaluate phase transitions
1015 phase_manager_.EvaluateTransitions(registry_);
1016 if (phase_manager_.PhaseChangedThisStep()) {
1017 logger_.Log(LogLevel::Info,
1018 "[PHASE] Transition: " +
1019 phase_manager_.GetConfig().GetPhaseName(phase_manager_.PreviousPhase()) +
1020 " → " + phase_manager_.CurrentPhaseName() +
1021 " at t=" + std::to_string(Time()) + "s");
1022 }
1023
1024 // Periodic progress logging (e.g., every 100 frames)
1025 if (logger_.IsProgressEnabled() && (frame_count_ % 100 == 0)) {
1026 logger_.LogRunProgress(Time(), config_.t_end);
1027 }
1028
1029 // Invoke output observers
1030 InvokeOutputObservers();
1031
1032 // Record frame if enabled
1033 if (recorder_) {
1034 recorder_->Record(Time());
1035 }
1036}
1037
1038inline void Simulator::Step() { Step(config_.dt); }
1039
1040inline void Simulator::Reset() {
1041 if (lifecycle_ < Lifecycle::Staged) {
1042 throw LifecycleError(LifecyclePhase::Reset, "requires prior Stage()");
1043 }
1044
1045 epoch_ = epoch_start_; // Reset to t=0 (MET derived via Time())
1046 frame_count_ = 0;
1047
1048 // Zero state
1049 if (state_manager_.TotalSize() > 0) {
1050 JanusVector<double> zero =
1051 JanusVector<double>::Zero(static_cast<Eigen::Index>(state_manager_.TotalSize()));
1052 state_manager_.SetState(zero);
1053 }
1054
1055 // Re-run Stage on components
1056 for (auto &comp : components_) {
1057 backplane_.set_context(comp->Entity(), comp->Name());
1058 backplane_.clear_tracking();
1059 try {
1060 comp->Stage(backplane_);
1061 } catch (const Error &e) {
1062 backplane_.clear_context();
1063 LogError(e, 0.0, comp->FullName());
1064 throw;
1065 }
1066 inputs_[comp.get()] = backplane_.resolved_inputs();
1067 backplane_.clear_context();
1068 }
1069
1070 lifecycle_ = Lifecycle::Staged;
1071}
1072
1073inline void Simulator::SetTime(double met) {
1074 epoch_ = epoch_start_ + met;
1075 // Estimate frame count from MET and dt
1076 frame_count_ = static_cast<int>(met / config_.dt);
1077}
1078
1079inline void Simulator::SetInputSource(const std::string &signal_name,
1080 InputSourceCallback callback) {
1081 input_sources_[signal_name] = std::move(callback);
1082}
1083
1084inline void Simulator::SetOutputObserver(const std::string &signal_name,
1085 OutputObserverCallback callback) {
1086 output_observers_[signal_name] = std::move(callback);
1087}
1088
1089inline void Simulator::ClearInputSource(const std::string &signal_name) {
1090 input_sources_.erase(signal_name);
1091}
1092
1093inline void Simulator::ClearOutputObserver(const std::string &signal_name) {
1094 output_observers_.erase(signal_name);
1095}
1096
1097inline Eigen::VectorXd Simulator::GetState() const { return state_manager_.GetState(); }
1098
1099inline void Simulator::SetState(const Eigen::VectorXd &X) { state_manager_.SetState(X); }
1100
1101inline Eigen::VectorXd Simulator::ComputeDerivatives(double t) {
1102 state_manager_.ZeroDerivatives();
1103
1104 double dt = config_.dt;
1105 auto ctx = BuildFrameContext();
1106
1107 for (auto &comp : components_) {
1108 if (ctx.ShouldRun(comp->FullName())) {
1109 ExecComponent(*comp, &Component<double>::PreStep, t, dt);
1110 }
1111 }
1112 for (auto &comp : components_) {
1113 if (ctx.ShouldRun(comp->FullName())) {
1114 ExecComponent(*comp, &Component<double>::Step, t, dt);
1115 }
1116 }
1117 for (auto &comp : components_) {
1118 if (ctx.ShouldRun(comp->FullName())) {
1119 ExecComponent(*comp, &Component<double>::PostStep, t, dt);
1120 }
1121 }
1122
1123 return state_manager_.GetDerivatives(ctx.active_components);
1124}
1125
1127 if (lifecycle_ != Lifecycle::Staged && lifecycle_ != Lifecycle::Running) {
1128 throw LifecycleError(LifecyclePhase::Step, "AdaptiveStep() requires prior Stage()");
1129 }
1130
1131 // Auto-log RUN phase on first step
1132 if (lifecycle_ == Lifecycle::Staged) {
1133 lifecycle_ = Lifecycle::Running;
1134 logger_.BeginLifecycle(LifecycleStrings::Run);
1135 logger_.LogRunStart(Time(), config_.t_end, config_.dt);
1136 }
1137
1138 // Invoke input sources
1139 InvokeInputSources();
1140
1141 // Build frame context (scheduler filtering)
1142 auto ctx = BuildFrameContext();
1143
1144 // Cache current time
1145 double t = Time();
1146
1147 // Derivative function with scheduler filtering
1148 auto deriv_func = [this, &ctx,
1149 dt_request](double t, const JanusVector<double> &x) -> JanusVector<double> {
1150 state_manager_.SetState(x);
1151 state_manager_.ZeroDerivatives();
1152
1153 for (auto &comp : components_) {
1154 if (ctx.ShouldRun(comp->FullName())) {
1155 ExecComponent(*comp, &Component<double>::PreStep, t, dt_request);
1156 ExecComponent(*comp, &Component<double>::Step, t, dt_request);
1157 ExecComponent(*comp, &Component<double>::PostStep, t, dt_request);
1158 }
1159 }
1160
1161 return state_manager_.GetDerivatives(ctx.active_components);
1162 };
1163
1164 JanusVector<double> X = state_manager_.GetState();
1166 try {
1167 result = integration_manager_.AdaptiveStep(deriv_func, X, t, dt_request);
1168 } catch (const Error &e) {
1169 LogError(e, t, "Integrator");
1170 throw;
1171 }
1172
1173 if (result.accepted) {
1174 state_manager_.SetState(result.state);
1175 epoch_ += result.dt_actual; // Single source of truth
1176 frame_count_++;
1177
1178 // Invoke output observers
1179 InvokeOutputObservers();
1180 }
1181
1182 return result;
1183}
1184
1185inline std::optional<janus::Function> Simulator::GetDynamicsGraph() const {
1186 return dynamics_graph_;
1187}
1188
1189inline std::optional<janus::Function> Simulator::GetJacobian() const { return jacobian_; }
1190
1192 DataDictionary dict;
1193
1194 for (const auto &comp : components_) {
1196 entry.name = comp->FullName();
1197 entry.type = comp->TypeName();
1198 entry.outputs = registry_.get_outputs_for_component(comp->FullName());
1199 entry.inputs = registry_.get_inputs_for_component(comp->FullName());
1200 entry.parameters = registry_.get_params_for_component(comp->FullName());
1201 entry.config = registry_.get_config_for_component(comp->FullName());
1202 dict.components.push_back(entry);
1203 }
1204
1205 dict.ComputeStats();
1206 return dict;
1207}
1208
1210 IntrospectionGraph graph;
1211 graph.dictionary = GetDataDictionary();
1212
1213 // 1. Explicit route edges (from SignalRouter)
1214 for (const auto &route : router_.GetRoutes()) {
1215 graph.edges.push_back({route.output_path, route.input_path, EdgeKind::Route});
1216 }
1217
1218 // 2. Resolve-based edges (implicit source bindings)
1219 // The inputs_ map is populated during Stage() from Backplane::resolved_inputs().
1220 // Any component that calls bp.resolve() to read another component's output
1221 // gets its dependency captured here automatically.
1222 for (const auto &comp : components_) {
1223 auto it = inputs_.find(comp.get());
1224 if (it != inputs_.end()) {
1225 for (const auto &resolved_signal : it->second) {
1226 graph.edges.push_back({resolved_signal, comp->FullName(), EdgeKind::Resolve});
1227 }
1228 }
1229 }
1230
1231 return graph;
1232}
1233
1234inline void Simulator::InvokeInputSources() {
1235 for (const auto &[name, callback] : input_sources_) {
1236 double value = callback(name);
1237 registry_.SetByName(name, value);
1238 }
1239}
1240
1241inline void Simulator::InvokeOutputObservers() {
1242 for (const auto &[name, callback] : output_observers_) {
1243 double value = registry_.GetByName(name);
1244 callback(name, value);
1245 }
1246}
1247
1248inline Simulator::FrameContext Simulator::BuildFrameContext() const {
1249 FrameContext ctx;
1250
1251 // Get active groups for this frame AND current flight phase
1252 int32_t current_phase = phase_manager_.CurrentPhase();
1253 auto active_groups = scheduler_.GetGroupsForFrame(frame_count_, current_phase);
1254
1255 // Build set of active component names for O(1) lookup
1256 for (const auto &group_name : active_groups) {
1257 for (const auto &comp_name : scheduler_.GetMembersForGroup(group_name)) {
1258 ctx.active_components.insert(comp_name);
1259 }
1260 }
1261
1262 // Build set of all scheduled component names (to detect unscheduled components)
1263 for (const auto &group : scheduler_.GetGroups()) {
1264 for (const auto &member : group.members) {
1265 ctx.all_scheduled_components.insert(member.component);
1266 }
1267 }
1268
1269 return ctx;
1270}
1271
1272inline void Simulator::ExecComponent(Component<double> &comp,
1273 void (Component<double>::*method)(double, double), double t,
1274 double dt) {
1275 try {
1276 logger_.BeginComponentTiming(comp.Name());
1277 (comp.*method)(t, dt);
1278 logger_.EndComponentTiming();
1279 } catch (const Error &e) {
1280 logger_.EndComponentTiming();
1281 LogError(e, t, comp.FullName());
1282 throw;
1283 }
1284}
1285
1286} // namespace icarus
Component-facing facade for signal registration and resolution.
Configuration container for components with typed accessors.
Factory for creating components from configuration.
Base class for all simulation components.
Core type definitions, concepts, and configuration for Icarus.
Complete catalog of simulation interface.
Integration between Error types and LogService.
HDF5 recorder wrapping Vulcan's telemetry system.
Manages integrator lifecycle and stepping.
Full topology graph for block diagram visualization.
Linearization of dynamics around operating point.
Flight Recorder style logging service.
Flight phase transition management.
Signal Registry (Backplane) for Icarus.
Group-based execution scheduler for components.
Centralized signal routing configuration.
Loads complete simulation configuration from YAML.
Simulator and subsystem configuration structs.
Core types for staging subsystem (trim, linearization, symbolic).
Manages state integration via unified signal model.
Symbolic graph generation during Stage().
Dependency graph analysis and topological sorting for component scheduling.
Trim/initialization solvers for state setup during Stage().
Component-facing facade for signal registration and resolution.
Definition Backplane.hpp:32
void set_context(const std::string &entity, const std::string &component)
Set current component context.
Definition Backplane.hpp:45
void clear_tracking()
Clear tracking for next component.
Definition Backplane.hpp:480
static ComponentFactory & Instance()
Get singleton instance.
Definition ComponentFactory.hpp:109
Base class for all simulation components.
Definition Component.hpp:47
virtual void PostStep(Scalar, Scalar)
Called after all component Steps (for post-processing).
Definition Component.hpp:97
virtual void Step(Scalar t, Scalar dt)=0
Step phase - called every time step (hot path!).
virtual void PreStep(Scalar, Scalar)
Called before any component Steps (for pre-processing).
Definition Component.hpp:92
Configuration/parsing errors with optional file context.
Definition Error.hpp:185
Base class for all Icarus exceptions.
Definition Error.hpp:52
Manages integrator lifecycle and stepping.
Definition IntegrationManager.hpp:40
Lifecycle ordering/state errors.
Definition Error.hpp:236
Mission Logger - Flight Recorder style logging.
Definition MissionLogger.hpp:53
Manages flight phase transitions.
Definition PhaseManager.hpp:100
Group-based execution scheduler.
Definition Scheduler.hpp:35
Central registry for all simulation signals.
Definition Registry.hpp:37
void SetByName(const std::string &name, const Scalar &value)
Set signal value by name (slow path, for initialization).
Definition Registry.hpp:445
Simulator & operator=(Simulator &&)=delete
Lifecycle GetLifecycle() const
Get current simulation lifecycle state.
Definition Simulator.hpp:170
const std::optional< staging::LinearModel > & GetLinearModel() const
Get linear model Available after Stage() if linearization.enabled = true.
Definition Simulator.hpp:293
void ClearOutputObserver(const std::string &signal_name)
Remove an output observer.
Definition Simulator.hpp:1093
double Peek(const std::string &name) const
Read a signal value by name.
Definition Simulator.hpp:190
void AddComponent(std::unique_ptr< Component< double > > component)
Add a component to the simulation.
Definition Simulator.hpp:664
std::string ISO8601() const
Get current time as ISO 8601 string.
Definition Simulator.hpp:167
const std::string & Name() const
Get simulation name (from config).
Definition Simulator.hpp:134
bool IsInitialized() const
Check if simulation is initialized (Staged or later).
Definition Simulator.hpp:181
Eigen::VectorXd GetState() const
Get current state vector.
Definition Simulator.hpp:1097
void Configure(const SimulatorConfig &config)
Configure simulator with full config struct.
Definition Simulator.hpp:631
const SimulatorConfig & GetConfig() const
Get simulator configuration.
Definition Simulator.hpp:300
void ClearInputSource(const std::string &signal_name)
Remove an input source.
Definition Simulator.hpp:1089
~Simulator()
Destructor.
Definition Simulator.hpp:449
double Dt() const
Get configured timestep.
Definition Simulator.hpp:137
void Poke(const std::string &name, double value)
Write a signal value by name.
Definition Simulator.hpp:211
double EndTime() const
Get configured end time.
Definition Simulator.hpp:140
const std::optional< staging::TrimResult > & GetTrimResult() const
Get trim result Available after Stage() if trim.enabled = true.
Definition Simulator.hpp:285
double GPSSecondsOfWeek() const
Get GPS seconds of week.
Definition Simulator.hpp:164
void AddRoutes(const std::vector< signal::SignalRoute > &routes)
Add signal routes.
Definition Simulator.hpp:674
Simulator(Simulator &&)=delete
Eigen::VectorXd ComputeDerivatives(double t)
Compute derivatives for current state at time t.
Definition Simulator.hpp:1101
int GPSWeek() const
Get GPS week number.
Definition Simulator.hpp:161
AdaptiveStepResult< double > AdaptiveStep(double dt_request)
Execute adaptive step (requires RK45 integrator).
Definition Simulator.hpp:1126
void SetState(const Eigen::VectorXd &X)
Set state vector.
Definition Simulator.hpp:1099
IntrospectionGraph GetIntrospectionGraph() const
Get introspection graph (data dictionary + topology edges).
Definition Simulator.hpp:1209
double JD_TAI() const
Get Julian Date in TAI scale.
Definition Simulator.hpp:152
void SetOutputObserver(const std::string &signal_name, OutputObserverCallback callback)
Register an output observer for a signal.
Definition Simulator.hpp:1084
std::optional< janus::Function > GetJacobian() const
Get symbolic Jacobian Available after Stage() if symbolics.generate_jacobian = true.
Definition Simulator.hpp:1189
MissionLogger & GetLogger()
Get the mission logger.
Definition Simulator.hpp:216
double JD_GPS() const
Get Julian Date in GPS scale.
Definition Simulator.hpp:158
std::function< double(const std::string &)> InputSourceCallback
Input source callback: (signal_name) -> value.
Definition Simulator.hpp:236
Simulator & operator=(const Simulator &)=delete
void Reset()
Reset simulation to initial state Re-applies ICs, resets time to 0.
Definition Simulator.hpp:1040
std::size_t NumComponents() const
Get number of components.
Definition Simulator.hpp:319
std::function< void(const std::string &, double)> OutputObserverCallback
Output observer callback: (signal_name, value) -> void.
Definition Simulator.hpp:239
void SetTime(double met)
Set simulation time (MET).
Definition Simulator.hpp:1073
void Stage()
Stage the simulation.
Definition Simulator.hpp:744
std::optional< janus::Function > GetDynamicsGraph() const
Get symbolic dynamics graph Available after Stage() if symbolics.enabled = true.
Definition Simulator.hpp:1185
double Time() const
Get current simulation time (MET - derived from epoch).
Definition Simulator.hpp:143
static std::unique_ptr< Simulator > FromConfig(const std::string &config_path)
Create simulator from configuration file.
Definition Simulator.hpp:469
double JD_UTC() const
Get Julian Date in UTC scale.
Definition Simulator.hpp:149
double JD_TT() const
Get Julian Date in TT scale.
Definition Simulator.hpp:155
void Step()
Definition Simulator.hpp:1038
Simulator(const Simulator &)=delete
const MissionLogger & GetLogger() const
Definition Simulator.hpp:217
void SetInputSource(const std::string &signal_name, InputSourceCallback callback)
Register an external input source for a signal.
Definition Simulator.hpp:1079
const SignalRegistry< double > & Registry() const
Get signal registry (for recording, introspection).
Definition Simulator.hpp:199
const vulcan::time::NumericEpoch & Epoch() const
Get current epoch (single source of truth for time).
Definition Simulator.hpp:146
Backplane< double > & GetBackplane()
Get backplane for signal introspection (expert).
Definition Simulator.hpp:322
const Backplane< double > & GetBackplane() const
Definition Simulator.hpp:323
std::string GetFlightPhaseName() const
Get current flight phase name (from PhaseManager).
Definition Simulator.hpp:176
int32_t GetFlightPhase() const
Get current flight phase value (from PhaseManager).
Definition Simulator.hpp:173
DataDictionary GetDataDictionary() const
Get data dictionary for the simulation.
Definition Simulator.hpp:1191
Simulator()
Default constructor for programmatic setup.
Definition Simulator.hpp:467
Definition Error.hpp:420
Manages state integration via signal discovery.
Definition StateManager.hpp:55
static TopologyResult ComputeExecutionOrder(const SimulatorConfig &config, TopologyConfig::CycleHandling cycle_handling=TopologyConfig::CycleHandling::Error)
Compute execution order from configuration.
Definition TopologyAnalyzer.hpp:368
static void ApplyTopologyOrder(SchedulerConfig &config, const TopologyResult &result)
Merge topology order into existing scheduler config.
Definition TopologyAnalyzer.hpp:445
static SimulatorConfig Load(const std::string &path)
Load complete simulation config from file.
Definition SimulationLoader.hpp:60
Centralized signal routing configuration.
Definition SignalRouter.hpp:103
Abstract linearizer interface.
Definition Linearizer.hpp:46
Lightweight symbolic simulator for graph extraction.
Definition SymbolicSimulatorCore.hpp:48
std::size_t GetStateSize() const
Get total state size.
Definition SymbolicSimulatorCore.hpp:149
Symbolic graph generator.
Definition SymbolicStager.hpp:50
SymbolicDynamics GenerateDynamics(const SymbolicStagerConfig &config={})
Generate symbolic dynamics representation.
Definition SymbolicStager.hpp:69
Abstract trim solver interface.
Definition TrimSolver.hpp:52
constexpr const char * Run
Definition MissionLogger.hpp:43
constexpr const char * Provision
Definition MissionLogger.hpp:41
constexpr const char * Stage
Definition MissionLogger.hpp:42
constexpr const char * Shutdown
Definition MissionLogger.hpp:44
Definition Simulator.hpp:53
std::unique_ptr< Linearizer > CreateLinearizer(bool symbolic_enabled)
Create appropriate linearizer based on configuration.
Definition Linearizer.hpp:114
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
@ Step
Definition Error.hpp:231
@ Reset
Definition Error.hpp:231
@ Stage
Definition Error.hpp:231
@ Provision
Definition Error.hpp:231
@ Automatic
Topological sort from signal dependencies (Future TODO).
Definition SimulatorConfig.hpp:292
Lifecycle
Simulation lifecycle phases.
Definition CoreTypes.hpp:98
@ Provisioned
After Provision, before Stage.
Definition CoreTypes.hpp:100
@ Staged
After Stage, ready to run.
Definition CoreTypes.hpp:101
@ Running
During Step loop.
Definition CoreTypes.hpp:102
@ Error
Error state.
Definition CoreTypes.hpp:105
@ Uninitialized
Before Provision.
Definition CoreTypes.hpp:99
@ Resolve
Implicit dependency via Backplane::resolve() (source binding).
Definition IntrospectionGraph.hpp:31
@ Route
Explicit SignalRouter connection (input_path <- output_path).
Definition IntrospectionGraph.hpp:30
void LogError(const Error &error, double time=0.0, const std::string &component="")
Log an error to the global LogService.
Definition ErrorLogging.hpp:36
@ Warning
Potential issues.
Definition Console.hpp:40
@ Info
Normal operation.
Definition Console.hpp:38
@ Debug
Debugging info.
Definition Console.hpp:37
Result from adaptive step integrators.
Definition Integrator.hpp:26
JanusVector< Scalar > state
New state at t + dt_actual.
Definition Integrator.hpp:27
bool accepted
Whether step was accepted.
Definition Integrator.hpp:30
Scalar dt_actual
Actual step taken (may differ from requested).
Definition Integrator.hpp:28
Configuration container for components.
Definition ComponentConfig.hpp:37
Entry for a single component.
Definition DataDictionary.hpp:49
std::string name
Full component name (e.g., "X15.MainEngine").
Definition DataDictionary.hpp:50
std::vector< SignalDescriptor > outputs
Output signals.
Definition DataDictionary.hpp:53
std::vector< SignalDescriptor > config
Discrete config (not optimizable).
Definition DataDictionary.hpp:56
std::vector< SignalDescriptor > parameters
Scalar parameters (optimizable).
Definition DataDictionary.hpp:55
std::vector< SignalDescriptor > inputs
Input ports.
Definition DataDictionary.hpp:54
std::string type
Component type (e.g., "JetEngine").
Definition DataDictionary.hpp:51
Complete catalog of simulation interface.
Definition DataDictionary.hpp:45
void ComputeStats()
Compute summary statistics from components.
Definition DataDictionary.hpp:156
std::vector< ComponentEntry > components
All registered components.
Definition DataDictionary.hpp:60
std::string reference
Definition SimulatorConfig.hpp:65
std::string system
Time system: "MET" (default), "UTC", "TAI", "GPS".
Definition SimulatorConfig.hpp:61
IntegratorType type
Method to use.
Definition IntegratorTypes.hpp:84
Complete introspection graph: nodes (components) + typed edges.
Definition IntrospectionGraph.hpp:69
DataDictionary dictionary
The existing data dictionary (component nodes with signal lists).
Definition IntrospectionGraph.hpp:71
std::vector< IntrospectionEdge > edges
All topology edges (routes + resolves).
Definition IntrospectionGraph.hpp:74
double MaxRate() const
Get maximum rate across all groups.
Definition SimulatorConfig.hpp:367
Complete simulation configuration.
Definition SimulatorConfig.hpp:673
double t_end
Definition SimulatorConfig.hpp:686
std::string version
Definition SimulatorConfig.hpp:678
EpochConfig epoch
Definition SimulatorConfig.hpp:691
std::string name
Definition SimulatorConfig.hpp:677
SchedulerConfig scheduler
Definition SimulatorConfig.hpp:706
double t_start
Definition SimulatorConfig.hpp:685
std::vector< signal::SignalRoute > routes
Definition SimulatorConfig.hpp:701
double dt
Note: may be auto-derived from scheduler.
Definition SimulatorConfig.hpp:687
PhaseConfig phases
Definition SimulatorConfig.hpp:716
std::string description
Definition SimulatorConfig.hpp:679
std::vector< ComponentConfig > components
Definition SimulatorConfig.hpp:696
IntegratorConfig< double > integrator
Definition SimulatorConfig.hpp:721
Staging configuration.
Definition SimulatorConfig.hpp:261
std::optional< janus::Function > dynamics
f(t, x) -> xdot
Definition StagingTypes.hpp:55
Configuration for symbolic graph generation.
Definition SymbolicStager.hpp:30
bool generate_dynamics
Generate dynamics function f(t, x) -> xdot.
Definition SymbolicStager.hpp:31
bool include_time
Include time as explicit input.
Definition SymbolicStager.hpp:36
bool generate_jacobian
Generate state Jacobian df/dx.
Definition SymbolicStager.hpp:32