use std::collections::HashMap;
use std::fs::File;
use std::io::{BufWriter, Result, Write};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::vector3::Vector3;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SimulationData {
pub name: String,
pub version: String,
pub metadata: HashMap<String, String>,
pub parameters: HashMap<String, f64>,
pub time_series: Vec<(f64, Vec<f64>)>,
pub vector_snapshots: Vec<(f64, Vec<Vector3<f64>>)>,
pub scalar_snapshots: Vec<(f64, String, Vec<f64>)>,
}
impl SimulationData {
pub fn new(name: &str, version: &str) -> Self {
Self {
name: name.to_string(),
version: version.to_string(),
metadata: HashMap::new(),
parameters: HashMap::new(),
time_series: Vec::new(),
vector_snapshots: Vec::new(),
scalar_snapshots: Vec::new(),
}
}
pub fn add_metadata(&mut self, key: &str, value: &str) {
self.metadata.insert(key.to_string(), value.to_string());
}
pub fn add_parameter(&mut self, key: &str, value: f64) {
self.parameters.insert(key.to_string(), value);
}
pub fn add_time_point(&mut self, time: f64, values: Vec<f64>) {
self.time_series.push((time, values));
}
pub fn add_vector_snapshot(&mut self, time: f64, vectors: Vec<Vector3<f64>>) {
self.vector_snapshots.push((time, vectors));
}
pub fn add_scalar_snapshot(&mut self, time: f64, field_name: &str, values: Vec<f64>) {
self.scalar_snapshots
.push((time, field_name.to_string(), values));
}
}
pub struct JsonWriter;
impl JsonWriter {
pub fn write(filename: &str, data: &SimulationData) -> Result<()> {
let file = File::create(filename)?;
let mut writer = BufWriter::new(file);
writeln!(writer, "{{")?;
writeln!(writer, " \"name\": \"{}\",", data.name)?;
writeln!(writer, " \"version\": \"{}\",", data.version)?;
writeln!(writer, " \"metadata\": {{")?;
let metadata_entries: Vec<_> = data.metadata.iter().collect();
for (i, (key, value)) in metadata_entries.iter().enumerate() {
let comma = if i < metadata_entries.len() - 1 {
","
} else {
""
};
writeln!(writer, " \"{}\": \"{}\"{}", key, value, comma)?;
}
writeln!(writer, " }},")?;
writeln!(writer, " \"parameters\": {{")?;
let param_entries: Vec<_> = data.parameters.iter().collect();
for (i, (key, value)) in param_entries.iter().enumerate() {
let comma = if i < param_entries.len() - 1 { "," } else { "" };
writeln!(writer, " \"{}\": {}{}", key, value, comma)?;
}
writeln!(writer, " }},")?;
writeln!(writer, " \"time_series\": [")?;
for (i, (time, values)) in data.time_series.iter().enumerate() {
let comma = if i < data.time_series.len() - 1 {
","
} else {
""
};
let values_str = values
.iter()
.map(|v| format!("{:.10e}", v))
.collect::<Vec<_>>()
.join(", ");
writeln!(
writer,
" {{\"time\": {:.10e}, \"values\": [{}]}}{}",
time, values_str, comma
)?;
}
writeln!(writer, " ],")?;
writeln!(writer, " \"vector_snapshots\": [")?;
for (i, (time, vectors)) in data.vector_snapshots.iter().enumerate() {
let comma = if i < data.vector_snapshots.len() - 1 {
","
} else {
""
};
writeln!(writer, " {{\"time\": {:.10e}, \"vectors\": [", time)?;
for (j, v) in vectors.iter().enumerate() {
let vec_comma = if j < vectors.len() - 1 { "," } else { "" };
writeln!(
writer,
" [{:.10e}, {:.10e}, {:.10e}]{}",
v.x, v.y, v.z, vec_comma
)?;
}
writeln!(writer, " ]}}{}", comma)?;
}
writeln!(writer, " ],")?;
writeln!(writer, " \"scalar_snapshots\": [")?;
for (i, (time, name, values)) in data.scalar_snapshots.iter().enumerate() {
let comma = if i < data.scalar_snapshots.len() - 1 {
","
} else {
""
};
let values_str = values
.iter()
.map(|v| format!("{:.10e}", v))
.collect::<Vec<_>>()
.join(", ");
writeln!(
writer,
" {{\"time\": {:.10e}, \"field\": \"{}\", \"values\": [{}]}}{}",
time, name, values_str, comma
)?;
}
writeln!(writer, " ]")?;
writeln!(writer, "}}")?;
writer.flush()?;
Ok(())
}
pub fn write_simple(filename: &str, times: &[f64], data: &[Vec<f64>]) -> Result<()> {
let file = File::create(filename)?;
let mut writer = BufWriter::new(file);
writeln!(writer, "{{")?;
writeln!(writer, " \"data\": [")?;
for (i, (time, values)) in times.iter().zip(data.iter()).enumerate() {
let comma = if i < times.len() - 1 { "," } else { "" };
let values_str = values
.iter()
.map(|v| format!("{:.10e}", v))
.collect::<Vec<_>>()
.join(", ");
writeln!(
writer,
" {{\"t\": {:.10e}, \"v\": [{}]}}{}",
time, values_str, comma
)?;
}
writeln!(writer, " ]")?;
writeln!(writer, "}}")?;
writer.flush()?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simulation_data_creation() {
let data = SimulationData::new("test", "1.0");
assert_eq!(data.name, "test");
assert_eq!(data.version, "1.0");
}
#[test]
fn test_add_metadata() {
let mut data = SimulationData::new("test", "1.0");
data.add_metadata("material", "YIG");
assert_eq!(data.metadata.get("material"), Some(&"YIG".to_string()));
}
#[test]
fn test_add_parameter() {
let mut data = SimulationData::new("test", "1.0");
data.add_parameter("alpha", 0.001);
assert_eq!(data.parameters.get("alpha"), Some(&0.001));
}
#[test]
fn test_add_time_point() {
let mut data = SimulationData::new("test", "1.0");
data.add_time_point(0.0, vec![1.0, 0.0, 0.0]);
assert_eq!(data.time_series.len(), 1);
}
#[test]
fn test_add_vector_snapshot() {
let mut data = SimulationData::new("test", "1.0");
let vectors = vec![Vector3::new(1.0, 0.0, 0.0)];
data.add_vector_snapshot(0.0, vectors);
assert_eq!(data.vector_snapshots.len(), 1);
}
#[test]
fn test_add_scalar_snapshot() {
let mut data = SimulationData::new("test", "1.0");
data.add_scalar_snapshot(0.0, "energy", vec![1.5, 2.3]);
assert_eq!(data.scalar_snapshots.len(), 1);
}
#[test]
fn test_json_write() {
let mut data = SimulationData::new("llg_test", "0.1.0");
data.add_metadata("material", "YIG");
data.add_parameter("alpha", 0.001);
data.add_time_point(0.0, vec![1.0, 0.0, 0.0]);
let result = JsonWriter::write("/tmp/test_sim.json", &data);
assert!(result.is_ok());
}
#[test]
fn test_json_write_simple() {
let times = vec![0.0, 1.0, 2.0];
let data = vec![vec![1.0, 0.0], vec![0.5, 0.5], vec![0.0, 1.0]];
let result = JsonWriter::write_simple("/tmp/test_simple.json", ×, &data);
assert!(result.is_ok());
}
#[test]
fn test_full_simulation_export() {
let mut data = SimulationData::new("skyrmion_dynamics", "0.1.0");
data.add_metadata("material", "Pt/CoFeB");
data.add_metadata("notes", "DMI-stabilized skyrmion");
data.add_parameter("alpha", 0.3);
data.add_parameter("temperature", 300.0);
data.add_parameter("dmi_d", 3.5e-3);
data.add_time_point(0.0, vec![1.0, 0.0, 0.0]);
data.add_time_point(1e-12, vec![0.99, 0.01, 0.0]);
let vectors = vec![Vector3::new(1.0, 0.0, 0.0), Vector3::new(0.0, 1.0, 0.0)];
data.add_vector_snapshot(0.0, vectors);
data.add_scalar_snapshot(0.0, "energy_density", vec![1.5, 2.3]);
let result = JsonWriter::write("/tmp/test_full.json", &data);
assert!(result.is_ok());
}
}