taco_format/header.rs
1//! Header definition and operations for TACO file format
2//!
3//! The header contains critical metadata about the trajectory including:
4//! - Format version
5//! - Number of atoms and frames
6//! - Compression settings
7//! - Simulation parameters
8//! - Atom metadata
9
10use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
11use serde::{Deserialize, Serialize};
12use std::io::{Read, Write};
13
14use crate::compression::CompressionSettings;
15use crate::metadata::{AtomMetadata, SimulationMetadata};
16use crate::{Error, MAGIC, Result, VERSION};
17
18/// File format header containing metadata about the trajectory
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct Header {
21 /// The TACO format version
22 pub version: String,
23
24 /// Number of atoms in the trajectory
25 pub num_atoms: u32,
26
27 /// Total number of frames in the trajectory
28 pub num_frames: u64,
29
30 /// Time step between frames in picoseconds
31 pub time_step: f64,
32
33 /// Periodic boundary conditions along [x, y, z]
34 pub pbc: [bool; 3],
35
36 /// Interval between full (non-delta) frames
37 pub full_frame_interval: u32,
38
39 /// Simulation metadata (temperature, ensemble, etc.)
40 pub simulation_metadata: SimulationMetadata,
41
42 /// Atom-specific metadata (masses, names, elements, etc.)
43 pub atom_metadata: AtomMetadata,
44
45 /// Compression settings used for the trajectory
46 pub compression_settings: CompressionSettings,
47
48 /// Offset to the start of the frame index table (in bytes)
49 pub frame_index_offset: u64,
50
51 /// Flags indicating which data components are present
52 /// Bit 0: Positions
53 /// Bit 1: Velocities
54 /// Bit 2: Forces
55 /// Bit 3: Box dimensions
56 /// Bits 4-31: Reserved for future use
57 pub data_flags: u32,
58}
59
60impl Header {
61 /// Create a new header with default values
62 pub fn new(
63 num_atoms: u32,
64 time_step: f64,
65 simulation_metadata: SimulationMetadata,
66 atom_metadata: AtomMetadata,
67 compression_settings: CompressionSettings,
68 ) -> Self {
69 Header {
70 version: VERSION.to_string(),
71 num_atoms,
72 num_frames: 0, // Will be updated during writing
73 time_step,
74 pbc: [true; 3], // Default: periodic in all directions
75 full_frame_interval: 100, // Default: store a full frame every 100 frames
76 simulation_metadata,
77 atom_metadata,
78 compression_settings,
79 frame_index_offset: 0, // Will be updated during writing
80 data_flags: 0x0F, // Default: include all data types
81 }
82 }
83
84 /// Write the header to a writer
85 pub fn write<W: Write>(&self, writer: &mut W) -> Result<()> {
86 // Write magic number to identify the file format
87 writer.write_all(&MAGIC)?;
88
89 // Serialize header to JSON for maximum compatibility and extensibility
90 let header_json = serde_json::to_vec(self)?;
91
92 // Write length of header data as u32
93 writer.write_u32::<LittleEndian>(header_json.len() as u32)?;
94
95 // Write the serialized header
96 writer.write_all(&header_json)?;
97
98 Ok(())
99 }
100
101 /// Read the header from a reader
102 pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
103 // Check magic number
104 let mut magic = [0u8; 4];
105 reader.read_exact(&mut magic)?;
106
107 if magic != MAGIC {
108 return Err(Error::InvalidFormat(format!(
109 "Invalid file format. Expected TACO magic number, found: {magic:?}"
110 )));
111 }
112
113 // Read the length of the header data
114 let header_length = reader.read_u32::<LittleEndian>()?;
115
116 // Read the serialized header
117 let mut header_data = vec![0u8; header_length as usize];
118 reader.read_exact(&mut header_data)?;
119
120 // Deserialize the header
121 let header: Header = serde_json::from_slice(&header_data)
122 .map_err(|e| Error::Deserialization(format!("Failed to deserialize header: {e}")))?;
123
124 // Validate version
125 if !is_compatible_version(&header.version) {
126 return Err(Error::InvalidFormat(format!(
127 "Incompatible format version: {}. This library supports: {}",
128 header.version, VERSION
129 )));
130 }
131
132 Ok(header)
133 }
134}
135
136/// Check if the given version is compatible with the current version
137fn is_compatible_version(version: &str) -> bool {
138 // For now, only exact version matches are supported
139 // In the future, this could be more sophisticated with major/minor versioning
140 version == VERSION
141}