1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! In-memory collections of ordered scientific system states.
//!
//! This module provides the analysis-facing representation of a system-state
//! time series. [`StateSeries`] owns a growable array of complete
//! [`SystemState`](crate::system_state::SystemState) snapshots, while
//! [`StateSeriesView`] provides a lightweight read-only view over that array.
//!
//! # Responsibility boundary
//!
//! A time series is not the simulation's live write buffer. Simulations own
//! and evolve a live `SystemState`; the future `storage` module will
//! encode selected fields and queue completed byte records for asynchronous
//! writing. `time_series` is used when states are intentionally collected in
//! memory for analysis, including states reconstructed by a reader.
//!
//! Consequently, this module performs no JSON processing, decoding, file IO,
//! queue management, or chunking. It also has no payload codec registry.
//!
//! # Boundary
//!
//! `time_series` owns only in-memory assembly and ordering of complete sampled
//! states for analysis. It does not own schema loading, state mutation, persistence
//! format choices, or workload scheduling.
//!
//! # Collection invariants
//!
//! Every state accepted by a series must share its exact immutable
//! [`SystemStateSchema`](crate::system_state::SystemStateSchema) layout allocation and carry a
//! iteration greater than the current final iteration. Gaps between iterations
//! are valid; optional physical time does not determine ordering.
//!
//! A complete mutable state is never exposed from the collection because its
//! time could then be changed behind the ordering invariant. Use
//! [`StateSeries::payload_mut_at`] to mutate one typed payload at one position.
//!
//! # Ownership
//!
//! Appending, removing, consuming, and iterating an owned series move
//! `SystemState` owners without cloning their payloads. [`StateSeriesPushError`] returns an
//! unchanged rejected state. Explicit [`Clone`] of `StateSeries` is different:
//! it deep-clones every populated payload and should be avoided for lightweight
//! sharing. Use [`StateSeries::as_view`] or `Arc<StateSeries>` instead.
//!
//! # Example
//!
//! ```no_run
//! use scientific_workflow::system_state::{SystemStateSchema, SimulationTime};
//! use scientific_workflow::time_series::StateSeries;
//!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let spec = SystemStateSchema::load_json_template("state.json")?;
//! let mut first = spec.create_empty_state(SimulationTime::from_iteration(0));
//! drop(first.insert_payload("population", vec![10_u64, 20, 30])?);
//!
//! let mut series = StateSeries::new(spec.clone());
//! series.push_state(first)?;
//! series
//! .payload_mut_at::<Vec<u64>>(0, "population")?
//! .push(40);
//!
//! let view = series.as_view();
//! assert_eq!(view.len(), 1);
//! assert_eq!(
//! view.first_state()
//! .expect("one state was appended")
//! .payload::<Vec<u64>>("population")?,
//! &vec![10, 20, 30, 40]
//! );
//! # Ok(())
//! # }
//! ```
pub use StateSeriesError;
pub use ;