25 OutputMode mode{OutputMode::ONLINE};
26 std::chrono::microseconds output_interval{1000};
27 size_t buffer_size{1000};
28 bool enable_compression{
false};
29 bool enable_file_output{
false};
30 std::string output_filename;
31 bool append_to_file{
false};
34 bool validate_intervals{
true};
35 std::chrono::microseconds min_output_interval{10};
36 std::chrono::microseconds max_output_interval{std::chrono::minutes{1}};
43 if (validate_intervals) {
44 if (output_interval < min_output_interval) {
45 throw std::invalid_argument(
"output_interval below minimum " +
46 std::to_string(min_output_interval.count()) +
"μs");
48 if (output_interval > max_output_interval) {
49 throw std::invalid_argument(
"output_interval exceeds maximum " +
50 std::to_string(max_output_interval.count()) +
"μs");
54 if (buffer_size == 0) {
55 throw std::invalid_argument(
"buffer_size must be positive");
58 if (enable_file_output && output_filename.empty()) {
59 throw std::invalid_argument(
"output_filename required when file output is enabled");
100 std::function<void(
const S&,
typename IntegratorDecorator<S>::time_type,
size_t)> output_handler_;
101 std::vector<std::tuple<S, typename IntegratorDecorator<S>::time_type,
size_t>> output_buffer_;
102 std::chrono::steady_clock::time_point last_output_;
103 size_t step_count_{0};
105 std::unique_ptr<std::ofstream> output_file_;
117 std::function<void(
const S&,
typename IntegratorDecorator<S>::time_type,
size_t)> handler =
nullptr)
119 , config_(std::move(
config))
120 , output_handler_(std::move(handler))
121 , last_output_(std::chrono::steady_clock::now()) {
124 initialize_file_output();
133 if (output_file_ && output_file_->is_open()) {
134 output_file_->close();
144 void step(
typename IntegratorDecorator<S>::state_type& state,
typename IntegratorDecorator<S>::time_type dt)
override {
145 this->wrapped_integrator_->step(state, dt);
148 handle_output(state, this->current_time());
154 void integrate(
typename IntegratorDecorator<S>::state_type& state,
typename IntegratorDecorator<S>::time_type dt,
155 typename IntegratorDecorator<S>::time_type end_time)
override {
156 if (config_.mode == OutputMode::OFFLINE) {
158 this->wrapped_integrator_->integrate(state, dt, end_time);
159 buffer_output(state, this->current_time(), step_count_);
162 while (this->current_time() < end_time) {
163 typename IntegratorDecorator<S>::time_type step_size = std::min(dt, end_time - this->current_time());
164 this->
step(state, step_size);
168 if (config_.mode == OutputMode::OFFLINE || config_.mode == OutputMode::HYBRID) {
177 void set_output_handler(std::function<
void(
const S&,
typename IntegratorDecorator<S>::time_type,
size_t)> handler) {
178 output_handler_ = std::move(handler);
185 const std::vector<std::tuple<S, typename IntegratorDecorator<S>::time_type,
size_t>>&
get_buffer()
const {
186 return output_buffer_;
193 output_buffer_.clear();
194 stats_.buffered_outputs = 0;
201 if (output_handler_) {
202 auto start_time = std::chrono::high_resolution_clock::now();
204 for (
const auto& [state, time,
step] : output_buffer_) {
205 output_handler_(state, time,
step);
206 stats_.total_outputs++;
209 auto end_time = std::chrono::high_resolution_clock::now();
210 stats_.total_output_time += std::chrono::duration_cast<std::chrono::milliseconds>(
211 end_time - start_time);
214 if (config_.enable_file_output && output_file_ && output_file_->is_open()) {
215 write_buffer_to_file();
248 bool file_settings_changed = (new_config.enable_file_output != config_.enable_file_output) ||
249 (new_config.output_filename != config_.output_filename) ||
250 (new_config.append_to_file != config_.append_to_file);
252 config_ = std::move(new_config);
254 if (file_settings_changed) {
255 initialize_file_output();
263 void handle_output(
const S& state,
typename IntegratorDecorator<S>::time_type time) {
264 auto now = std::chrono::steady_clock::now();
266 if (config_.mode == OutputMode::ONLINE || config_.mode == OutputMode::HYBRID) {
267 if (now - last_output_ >= config_.output_interval) {
268 if (output_handler_) {
269 auto start_time = std::chrono::high_resolution_clock::now();
270 output_handler_(state, time, step_count_);
271 auto end_time = std::chrono::high_resolution_clock::now();
273 stats_.total_output_time += std::chrono::duration_cast<std::chrono::milliseconds>(
274 end_time - start_time);
275 stats_.total_outputs++;
276 stats_.online_outputs++;
282 if (config_.mode == OutputMode::OFFLINE || config_.mode == OutputMode::HYBRID) {
283 buffer_output(state, time, step_count_);
290 void buffer_output(
const S& state,
typename IntegratorDecorator<S>::time_type time,
size_t step) {
291 if (output_buffer_.size() >= config_.buffer_size) {
292 output_buffer_.erase(output_buffer_.begin());
293 stats_.buffer_overflows++;
295 output_buffer_.emplace_back(state, time,
step);
296 stats_.buffered_outputs++;
302 void initialize_file_output() {
303 if (config_.enable_file_output && !config_.output_filename.empty()) {
304 output_file_ = std::make_unique<std::ofstream>();
306 std::ios_base::openmode mode = std::ios_base::out;
307 if (config_.append_to_file) {
308 mode |= std::ios_base::app;
311 output_file_->open(config_.output_filename, mode);
313 if (!output_file_->is_open()) {
314 throw std::runtime_error(
"Failed to open output file: " + config_.output_filename);
318 if (!config_.append_to_file) {
319 *output_file_ <<
"# DiffEq Integration Output\n";
320 *output_file_ <<
"# Time, State, Step\n";
328 void write_buffer_to_file() {
329 if (!output_file_ || !output_file_->is_open()) {
333 for (
const auto& [state, time,
step] : output_buffer_) {
334 *output_file_ << time <<
", ";
338 for (
const auto& component : state) {
339 if (!first) *output_file_ <<
" ";
340 *output_file_ << component;
344 *output_file_ <<
", " <<
step <<
"\n";
345 stats_.file_writes++;
348 output_file_->flush();
void integrate(typename IntegratorDecorator< S >::state_type &state, typename IntegratorDecorator< S >::time_type dt, typename IntegratorDecorator< S >::time_type end_time) override
Override integrate to handle different output modes.
OutputDecorator(std::unique_ptr< AbstractIntegrator< S > > integrator, OutputConfig config={}, std::function< void(const S &, typename IntegratorDecorator< S >::time_type, size_t)> handler=nullptr)
Construct output decorator.