#[allow(unused_imports)]
use super::functions::*;
#[allow(unused_imports)]
use super::functions_2::*;
use std::io::Write;
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct NetCdfDimension {
pub name: String,
pub size: Option<usize>,
}
impl NetCdfDimension {
#[allow(dead_code)]
pub fn is_unlimited(&self) -> bool {
self.size.is_none()
}
#[allow(dead_code)]
pub fn effective_size(&self) -> usize {
self.size.unwrap_or(0)
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct NetCdfVariable {
pub name: String,
pub dims: Vec<String>,
pub data: Vec<f64>,
pub attributes: Vec<VariableAttribute>,
}
impl NetCdfVariable {
#[allow(dead_code)]
pub fn new(name: &str, dims: Vec<String>, data: Vec<f64>) -> Self {
NetCdfVariable {
name: name.to_string(),
dims,
data,
attributes: Vec::new(),
}
}
#[allow(dead_code)]
pub fn add_attribute(&mut self, key: &str, value: &str) {
self.attributes.push(VariableAttribute {
key: key.to_string(),
value: value.to_string(),
});
}
#[allow(dead_code)]
pub fn get_attribute(&self, key: &str) -> Option<&str> {
self.attributes
.iter()
.find(|a| a.key == key)
.map(|a| a.value.as_str())
}
#[allow(dead_code)]
pub fn len(&self) -> usize {
self.data.len()
}
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct NetcdfVariableStats {
pub name: String,
pub min: f64,
pub max: f64,
pub mean: f64,
pub std_dev: f64,
pub count: usize,
}
#[allow(dead_code)]
impl NetcdfVariableStats {
pub fn range(&self) -> f64 {
self.max - self.min
}
pub fn cv(&self) -> f64 {
if self.mean.abs() < f64::EPSILON {
0.0
} else {
self.std_dev / self.mean
}
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct Nc4File {
pub root: Nc4Group,
pub global_attributes: Vec<(String, String)>,
pub dimensions: Vec<(String, Option<usize>)>,
pub unlimited_size: usize,
}
impl Nc4File {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
root: Nc4Group::new("/"),
global_attributes: Vec::new(),
dimensions: Vec::new(),
unlimited_size: 0,
}
}
#[allow(dead_code)]
pub fn add_dimension(&mut self, name: &str, size: usize) {
self.dimensions.push((name.to_string(), Some(size)));
}
#[allow(dead_code)]
pub fn add_unlimited_dimension(&mut self, name: &str) {
self.dimensions.push((name.to_string(), None));
}
#[allow(dead_code)]
pub fn extend_unlimited(&mut self) {
self.unlimited_size += 1;
for entry in self.dimensions.iter_mut() {
if entry.1.is_none() || entry.1 == Some(self.unlimited_size - 1) {
entry.1 = Some(self.unlimited_size);
break;
}
}
}
#[allow(dead_code)]
pub fn add_global_attribute(&mut self, key: &str, value: &str) {
self.global_attributes
.push((key.to_string(), value.to_string()));
}
#[allow(dead_code)]
pub fn get_global_attribute(&self, key: &str) -> Option<&str> {
self.global_attributes
.iter()
.find(|(k, _)| k == key)
.map(|(_, v)| v.as_str())
}
#[allow(dead_code)]
pub fn get_dimension_size(&self, name: &str) -> Option<usize> {
self.dimensions
.iter()
.find(|(n, _)| n == name)
.and_then(|(_, s)| *s)
}
#[allow(dead_code)]
pub fn is_unlimited(&self, name: &str) -> bool {
self.dimensions.iter().any(|(n, s)| {
n == name && s.is_none()
|| (n == name && *s == Some(self.unlimited_size) && self.unlimited_size > 0)
})
}
#[allow(dead_code)]
pub fn add_variable(&mut self, var: Nc4Variable) {
self.root.add_variable(var);
}
#[allow(dead_code)]
pub fn get_variable(&self, name: &str) -> Option<&Nc4Variable> {
self.root.get_variable(name)
}
#[allow(dead_code)]
pub fn add_group(&mut self, group: Nc4Group) {
self.root.add_subgroup(group);
}
#[allow(dead_code)]
pub fn get_group(&self, name: &str) -> Option<&Nc4Group> {
self.root.subgroups.iter().find(|g| g.name == name)
}
#[allow(dead_code)]
pub fn total_variable_count(&self) -> usize {
self.root.total_variable_count()
}
#[allow(dead_code)]
pub fn to_bytes(&self) -> Vec<u8> {
let mut lines = Vec::new();
lines.push(format!("unlimited_size:{}", self.unlimited_size));
for (k, v) in &self.global_attributes {
lines.push(format!("attr:{}={}", k, v));
}
for (name, size) in &self.dimensions {
match size {
Some(s) => lines.push(format!("dim:{}={}", name, s)),
None => lines.push(format!("dim:{}=UNLIMITED", name)),
}
}
for var in &self.root.variables {
lines.push(format!(
"var:{}:{}:{}",
var.name,
var.dims.join(","),
var.data_type.type_name()
));
}
let payload = lines.join("\n");
let payload_bytes = payload.as_bytes();
let mut buf = Vec::with_capacity(9 + payload_bytes.len());
buf.extend_from_slice(b"OXNC4");
buf.extend_from_slice(&(payload_bytes.len() as u32).to_le_bytes());
buf.extend_from_slice(payload_bytes);
buf
}
}
impl Default for Nc4File {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(dead_code)]
pub enum Nc4DataType {
Float64,
Float32,
Int32,
UInt8,
String,
}
impl Nc4DataType {
#[allow(dead_code)]
pub fn byte_width(&self) -> usize {
match self {
Nc4DataType::Float64 => 8,
Nc4DataType::Float32 => 4,
Nc4DataType::Int32 => 4,
Nc4DataType::UInt8 => 1,
Nc4DataType::String => 0,
}
}
#[allow(dead_code)]
pub fn type_name(&self) -> &'static str {
match self {
Nc4DataType::Float64 => "double",
Nc4DataType::Float32 => "float",
Nc4DataType::Int32 => "int",
Nc4DataType::UInt8 => "ubyte",
Nc4DataType::String => "string",
}
}
}
#[derive(Debug, Clone, Default)]
#[allow(dead_code)]
pub struct NetcdfTrajectoryBuilder {
pub title: String,
pub application: String,
pub frames: Vec<TrajectoryFrame>,
pub use_angstroms: bool,
}
impl NetcdfTrajectoryBuilder {
#[allow(dead_code)]
pub fn new() -> Self {
NetcdfTrajectoryBuilder {
title: String::new(),
application: "OxiPhysics".to_string(),
frames: Vec::new(),
use_angstroms: true,
}
}
#[allow(dead_code)]
pub fn with_title(mut self, title: &str) -> Self {
self.title = title.to_string();
self
}
#[allow(dead_code)]
pub fn with_application(mut self, app: &str) -> Self {
self.application = app.to_string();
self
}
#[allow(dead_code)]
pub fn in_angstroms(mut self) -> Self {
self.use_angstroms = true;
self
}
#[allow(dead_code)]
pub fn in_nanometres(mut self) -> Self {
self.use_angstroms = false;
self
}
#[allow(dead_code)]
pub fn add_frame(&mut self, frame: TrajectoryFrame) {
self.frames.push(frame);
}
#[allow(dead_code)]
pub fn frame_count(&self) -> usize {
self.frames.len()
}
#[allow(dead_code)]
pub fn n_atoms(&self) -> usize {
self.frames.first().map(|f| f.n_atoms()).unwrap_or(0)
}
#[allow(dead_code)]
pub fn rmsd_series(&self) -> Vec<f64> {
if self.frames.is_empty() {
return vec![];
}
let ref_frame = &self.frames[0];
self.frames.iter().map(|f| f.rmsd_from(ref_frame)).collect()
}
#[allow(dead_code)]
pub fn write_cdl<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
let n_atoms = self.n_atoms();
let n_frames = self.frame_count();
writeln!(writer, "netcdf trajectory {{")?;
writeln!(writer, "dimensions:")?;
writeln!(writer, "\tatom = {n_atoms} ;")?;
writeln!(writer, "\tframe = UNLIMITED ; // currently {n_frames}")?;
writeln!(writer, "\tspatial = 3 ;")?;
writeln!(writer, "\tlabel = 5 ;")?;
writeln!(writer, "variables:")?;
let unit = if self.use_angstroms {
"angstrom"
} else {
"nanometer"
};
writeln!(writer, "\tdouble coordinates(frame, atom, spatial) ;")?;
writeln!(writer, "\t\tcoordinates:units = \"{unit}\" ;")?;
writeln!(writer, "\tdouble time(frame) ;")?;
writeln!(writer, "\t\ttime:units = \"picosecond\" ;")?;
writeln!(writer, "\tdouble cell_lengths(frame, spatial) ;")?;
writeln!(writer, "\t\tcell_lengths:units = \"{unit}\" ;")?;
writeln!(writer, "// global attributes:")?;
writeln!(writer, "\t:title = \"{}\" ;", self.title)?;
writeln!(writer, "\t:application = \"{}\" ;", self.application)?;
writeln!(writer, "\t:Conventions = \"AMBER\" ;")?;
writeln!(writer, "\t:ConventionVersion = \"1.0\" ;")?;
writeln!(writer, "}}")?;
Ok(())
}
#[allow(dead_code)]
pub fn time_series(&self) -> Vec<f64> {
self.frames.iter().map(|f| f.time_ps).collect()
}
#[allow(dead_code)]
pub fn com_trajectory(&self) -> Vec<[f64; 3]> {
self.frames.iter().map(|f| f.centre_of_mass()).collect()
}
#[allow(dead_code)]
pub fn frame(&self, idx: usize) -> &TrajectoryFrame {
&self.frames[idx]
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct NetCdfFile {
pub dimensions: Vec<(String, usize)>,
pub variables: Vec<NetCdfVariable>,
pub attributes: Vec<(String, String)>,
pub unlimited_dim: Option<String>,
}
impl NetCdfFile {
#[allow(dead_code)]
pub fn new() -> Self {
NetCdfFile {
dimensions: Vec::new(),
variables: Vec::new(),
attributes: Vec::new(),
unlimited_dim: None,
}
}
#[allow(dead_code)]
pub fn add_dimension(&mut self, name: &str, size: usize) {
self.dimensions.push((name.to_string(), size));
}
#[allow(dead_code)]
pub fn add_unlimited_dimension(&mut self, name: &str, current_size: usize) {
self.dimensions.push((name.to_string(), current_size));
self.unlimited_dim = Some(name.to_string());
}
#[allow(dead_code)]
pub fn add_variable(&mut self, var: NetCdfVariable) {
self.variables.push(var);
}
#[allow(dead_code)]
pub fn add_attribute(&mut self, key: &str, value: &str) {
self.attributes.push((key.to_string(), value.to_string()));
}
#[allow(dead_code)]
pub fn get_variable(&self, name: &str) -> Option<&NetCdfVariable> {
self.variables.iter().find(|v| v.name == name)
}
#[allow(dead_code)]
pub fn get_variable_mut(&mut self, name: &str) -> Option<&mut NetCdfVariable> {
self.variables.iter_mut().find(|v| v.name == name)
}
#[allow(dead_code)]
pub fn get_attribute(&self, key: &str) -> Option<&str> {
self.attributes
.iter()
.find(|(k, _)| k == key)
.map(|(_, v)| v.as_str())
}
#[allow(dead_code)]
pub fn get_dimension_size(&self, name: &str) -> Option<usize> {
self.dimensions
.iter()
.find(|(n, _)| n == name)
.map(|(_, s)| *s)
}
#[allow(dead_code)]
pub fn is_unlimited_dimension(&self, name: &str) -> bool {
self.unlimited_dim.as_deref() == Some(name)
}
#[allow(dead_code)]
pub fn variable_names(&self) -> Vec<&str> {
self.variables.iter().map(|v| v.name.as_str()).collect()
}
#[allow(dead_code)]
pub fn dimension_names(&self) -> Vec<&str> {
self.dimensions.iter().map(|(n, _)| n.as_str()).collect()
}
}
impl Default for NetCdfFile {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct NetcdfFile {
pub dimensions: std::collections::HashMap<String, usize>,
pub variables: Vec<NetcdfVariable>,
pub global_attrs: Vec<(String, String)>,
}
#[allow(dead_code)]
impl NetcdfFile {
pub fn new() -> Self {
Self {
dimensions: std::collections::HashMap::new(),
variables: Vec::new(),
global_attrs: Vec::new(),
}
}
pub fn add_dimension(&mut self, name: &str, size: usize) {
self.dimensions.insert(name.to_string(), size);
}
pub fn add_variable(&mut self, var: NetcdfVariable) {
self.variables.push(var);
}
pub fn add_global_attr(&mut self, key: &str, value: &str) {
self.global_attrs.push((key.to_string(), value.to_string()));
}
pub fn write_cdl(&self) -> String {
let mut out = String::new();
out.push_str("netcdf data {\n");
out.push_str("dimensions:\n");
let mut dims: Vec<(&String, &usize)> = self.dimensions.iter().collect();
dims.sort_by_key(|(k, _)| k.as_str());
for (name, size) in &dims {
out.push_str(&format!("\t{} = {} ;\n", name, size));
}
out.push_str("variables:\n");
for var in &self.variables {
let dims_str = var.dimensions.join(", ");
out.push_str(&format!("\tdouble {}({}) ;\n", var.name, dims_str));
if !var.units.is_empty() {
out.push_str(&format!("\t\t{}:units = \"{}\" ;\n", var.name, var.units));
}
if !var.long_name.is_empty() {
out.push_str(&format!(
"\t\t{}:long_name = \"{}\" ;\n",
var.name, var.long_name
));
}
}
if !self.global_attrs.is_empty() {
out.push_str("// global attributes:\n");
for (key, value) in &self.global_attrs {
out.push_str(&format!("\t\t:{} = \"{}\" ;\n", key, value));
}
}
out.push_str("data:\n");
for var in &self.variables {
let vals: Vec<String> = var.data.iter().map(|v| format!("{}", v)).collect();
out.push_str(&format!("\t{} = {} ;\n", var.name, vals.join(", ")));
}
out.push_str("}\n");
out
}
pub fn trajectory_write(
path: &str,
times: &[f64],
positions: &[Vec<[f64; 3]>],
) -> std::io::Result<()> {
use std::io::Write;
let n_frames = times.len();
let n_atoms = positions.first().map(|f| f.len()).unwrap_or(0);
let mut f = std::fs::File::create(path)?;
writeln!(f, "netcdf trajectory {{")?;
writeln!(f, "dimensions:")?;
writeln!(f, "\tframe = {} ;", n_frames)?;
writeln!(f, "\tatom = {} ;", n_atoms)?;
writeln!(f, "\tspatial = 3 ;")?;
writeln!(f, "variables:")?;
writeln!(f, "\tdouble time(frame) ;")?;
writeln!(f, "\t\ttime:units = \"ps\" ;")?;
writeln!(f, "\tdouble coordinates(frame, atom, spatial) ;")?;
writeln!(f, "\t\tcoordinates:units = \"angstrom\" ;")?;
writeln!(f, "data:")?;
let time_vals: Vec<String> = times.iter().map(|t| format!("{}", t)).collect();
writeln!(f, "\ttime = {} ;", time_vals.join(", "))?;
write!(f, "\tcoordinates = ")?;
let mut first = true;
for frame in positions {
for pos in frame {
for &coord in pos {
if !first {
write!(f, ", ")?;
}
write!(f, "{}", coord)?;
first = false;
}
}
}
writeln!(f, " ;")?;
writeln!(f, "}}")?;
Ok(())
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct NetcdfWriter {
pub(super) name: String,
pub(super) dimensions: Vec<(String, usize)>,
pub(super) unlimited_dim: Option<String>,
pub(super) variables: Vec<NetcdfWriterVariable>,
pub(super) global_attrs: Vec<(String, String)>,
}
#[allow(dead_code)]
impl NetcdfWriter {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
dimensions: Vec::new(),
unlimited_dim: None,
variables: Vec::new(),
global_attrs: Vec::new(),
}
}
pub fn add_dimension(&mut self, name: &str, size: usize) -> &mut Self {
self.dimensions.push((name.to_string(), size));
self
}
pub fn add_unlimited_dimension(&mut self, name: &str, current_size: usize) -> &mut Self {
self.dimensions.push((name.to_string(), current_size));
self.unlimited_dim = Some(name.to_string());
self
}
pub fn add_global_attribute(&mut self, key: &str, value: &str) -> &mut Self {
self.global_attrs.push((key.to_string(), value.to_string()));
self
}
pub fn add_variable(&mut self, name: &str, dims: &[&str], data: Vec<f64>) -> &mut Self {
self.variables.push(NetcdfWriterVariable {
name: name.to_string(),
dims: dims.iter().map(|s| s.to_string()).collect(),
data,
attrs: Vec::new(),
});
self
}
pub fn add_variable_attribute(&mut self, key: &str, value: &str) -> &mut Self {
if let Some(v) = self.variables.last_mut() {
v.attrs.push((key.to_string(), value.to_string()));
}
self
}
pub fn add_coordinate(&mut self, dim_name: &str, data: Vec<f64>, units: &str) -> &mut Self {
self.add_variable(dim_name, &[dim_name], data);
self.add_variable_attribute("units", units);
self.add_variable_attribute("axis", dim_name);
self
}
pub fn n_dimensions(&self) -> usize {
self.dimensions.len()
}
pub fn n_variables(&self) -> usize {
self.variables.len()
}
pub fn to_cdl(&self) -> String {
let mut out = String::new();
out.push_str(&format!("netcdf {} {{\n", self.name));
out.push_str("dimensions:\n");
for (name, size) in &self.dimensions {
if self.unlimited_dim.as_deref() == Some(name.as_str()) {
out.push_str(&format!(
"\t{} = UNLIMITED ; // ({} currently)\n",
name, size
));
} else {
out.push_str(&format!("\t{} = {} ;\n", name, size));
}
}
out.push_str("variables:\n");
for var in &self.variables {
let dims = var.dims.join(", ");
out.push_str(&format!("\tdouble {}({}) ;\n", var.name, dims));
for (k, v) in &var.attrs {
out.push_str(&format!("\t\t{}:{} = \"{}\" ;\n", var.name, k, v));
}
}
if !self.global_attrs.is_empty() {
out.push_str("// global attributes:\n");
for (k, v) in &self.global_attrs {
out.push_str(&format!("\t:{} = \"{}\" ;\n", k, v));
}
}
out.push_str("data:\n");
for var in &self.variables {
let vals: Vec<String> = var.data.iter().map(|v| format!("{}", v)).collect();
out.push_str(&format!("\t{} = {} ;\n", var.name, vals.join(", ")));
}
out.push_str("}\n");
out
}
pub fn finish(self) -> String {
self.to_cdl()
}
pub fn write_to_file(&self, path: &str) -> std::io::Result<()> {
let mut f = std::fs::File::create(path)?;
f.write_all(self.to_cdl().as_bytes())
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct NetcdfVariable {
pub name: String,
pub dimensions: Vec<String>,
pub data: Vec<f64>,
pub units: String,
pub long_name: String,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct TrajectoryFrame {
pub time_ps: f64,
pub positions: Vec<[f64; 3]>,
pub velocities: Option<Vec<[f64; 3]>>,
pub box_lengths: Option<[f64; 3]>,
}
impl TrajectoryFrame {
#[allow(dead_code)]
pub fn n_atoms(&self) -> usize {
self.positions.len()
}
#[allow(dead_code)]
pub fn centre_of_mass(&self) -> [f64; 3] {
if self.positions.is_empty() {
return [0.0; 3];
}
let mut s = [0.0_f64; 3];
for p in &self.positions {
s[0] += p[0];
s[1] += p[1];
s[2] += p[2];
}
let inv = 1.0 / self.positions.len() as f64;
[s[0] * inv, s[1] * inv, s[2] * inv]
}
#[allow(dead_code)]
pub fn rmsd_from(&self, reference: &TrajectoryFrame) -> f64 {
let n = self.positions.len().min(reference.positions.len());
if n == 0 {
return 0.0;
}
let sum: f64 = (0..n)
.map(|i| {
let dx = self.positions[i][0] - reference.positions[i][0];
let dy = self.positions[i][1] - reference.positions[i][1];
let dz = self.positions[i][2] - reference.positions[i][2];
dx * dx + dy * dy + dz * dz
})
.sum();
(sum / n as f64).sqrt()
}
#[allow(dead_code)]
pub fn kinetic_energy(&self, mass_amu: f64) -> f64 {
let vels = match &self.velocities {
Some(v) => v,
None => return 0.0,
};
let sum: f64 = vels
.iter()
.map(|v| v[0] * v[0] + v[1] * v[1] + v[2] * v[2])
.sum();
0.5 * mass_amu * sum
}
#[allow(dead_code)]
pub fn translate(&mut self, delta: [f64; 3]) {
for p in &mut self.positions {
p[0] += delta[0];
p[1] += delta[1];
p[2] += delta[2];
}
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct NetcdfReader {
pub dimensions: std::collections::HashMap<String, usize>,
pub variables: Vec<NetcdfVariable>,
pub global_attrs: Vec<(String, String)>,
pub unlimited_dim: Option<String>,
}
#[allow(dead_code)]
impl NetcdfReader {
pub fn from_cdl(cdl: &str) -> Result<Self, String> {
let file = parse_ncf_text(cdl)?;
let variables: Vec<NetcdfVariable> = file
.variables
.into_iter()
.map(|v| {
let units = v.get_attribute("units").unwrap_or("").to_string();
let long_name = v.get_attribute("long_name").unwrap_or("").to_string();
NetcdfVariable {
name: v.name,
dimensions: v.dims,
data: v.data,
units,
long_name,
}
})
.collect();
Ok(Self {
dimensions: file.dimensions.into_iter().collect(),
variables,
global_attrs: file.attributes,
unlimited_dim: file.unlimited_dim,
})
}
pub fn get_dimension(&self, name: &str) -> Option<usize> {
self.dimensions.get(name).copied()
}
pub fn get_variable(&self, name: &str) -> Option<&NetcdfVariable> {
self.variables.iter().find(|v| v.name == name)
}
pub fn get_data(&self, var_name: &str) -> Option<&[f64]> {
self.get_variable(var_name).map(|v| v.data.as_slice())
}
pub fn variable_names(&self) -> Vec<&str> {
self.variables.iter().map(|v| v.name.as_str()).collect()
}
pub fn dimension_names(&self) -> Vec<&str> {
let mut names: Vec<&str> = self.dimensions.keys().map(String::as_str).collect();
names.sort();
names
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct VariableAttribute {
pub key: String,
pub value: String,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct Nc4Group {
pub name: String,
pub variables: Vec<Nc4Variable>,
pub subgroups: Vec<Nc4Group>,
pub attributes: Vec<(String, String)>,
}
impl Nc4Group {
#[allow(dead_code)]
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
variables: Vec::new(),
subgroups: Vec::new(),
attributes: Vec::new(),
}
}
#[allow(dead_code)]
pub fn add_variable(&mut self, var: Nc4Variable) {
self.variables.push(var);
}
#[allow(dead_code)]
pub fn add_subgroup(&mut self, group: Nc4Group) {
self.subgroups.push(group);
}
#[allow(dead_code)]
pub fn add_attribute(&mut self, key: &str, value: &str) {
self.attributes.push((key.to_string(), value.to_string()));
}
#[allow(dead_code)]
pub fn get_variable(&self, name: &str) -> Option<&Nc4Variable> {
self.variables.iter().find(|v| v.name == name)
}
#[allow(dead_code)]
pub fn get_attribute(&self, key: &str) -> Option<&str> {
self.attributes
.iter()
.find(|(k, _)| k == key)
.map(|(_, v)| v.as_str())
}
#[allow(dead_code)]
pub fn all_variables(&self) -> Vec<&Nc4Variable> {
let mut out: Vec<&Nc4Variable> = self.variables.iter().collect();
for sg in &self.subgroups {
out.extend(sg.all_variables());
}
out
}
#[allow(dead_code)]
pub fn total_variable_count(&self) -> usize {
self.variables.len()
+ self
.subgroups
.iter()
.map(|g| g.total_variable_count())
.sum::<usize>()
}
}
#[derive(Debug, Clone, PartialEq)]
#[allow(dead_code)]
pub struct NetcdfDimSpec {
pub name: String,
pub size: usize,
pub unlimited: bool,
}
impl NetcdfDimSpec {
#[allow(dead_code)]
pub fn fixed(name: &str, size: usize) -> Self {
NetcdfDimSpec {
name: name.to_string(),
size,
unlimited: false,
}
}
#[allow(dead_code)]
pub fn unlimited(name: &str, current_size: usize) -> Self {
NetcdfDimSpec {
name: name.to_string(),
size: current_size,
unlimited: true,
}
}
#[allow(dead_code)]
pub fn to_cdl(&self) -> String {
if self.unlimited {
format!("\t{} = UNLIMITED ; // currently {}", self.name, self.size)
} else {
format!("\t{} = {} ;", self.name, self.size)
}
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct Nc4Variable {
pub name: String,
pub dims: Vec<String>,
pub float_data: Vec<f64>,
pub string_data: Vec<String>,
pub int_data: Vec<i64>,
pub data_type: Nc4DataType,
pub attributes: Vec<VariableAttribute>,
}
impl Nc4Variable {
#[allow(dead_code)]
pub fn float64(name: &str, dims: Vec<String>, data: Vec<f64>) -> Self {
Self {
name: name.to_string(),
dims,
float_data: data,
string_data: Vec::new(),
int_data: Vec::new(),
data_type: Nc4DataType::Float64,
attributes: Vec::new(),
}
}
#[allow(dead_code)]
pub fn string_var(name: &str, dims: Vec<String>, data: Vec<String>) -> Self {
Self {
name: name.to_string(),
dims,
float_data: Vec::new(),
string_data: data,
int_data: Vec::new(),
data_type: Nc4DataType::String,
attributes: Vec::new(),
}
}
#[allow(dead_code)]
pub fn int32(name: &str, dims: Vec<String>, data: Vec<i64>) -> Self {
Self {
name: name.to_string(),
dims,
float_data: Vec::new(),
string_data: Vec::new(),
int_data: data,
data_type: Nc4DataType::Int32,
attributes: Vec::new(),
}
}
#[allow(dead_code)]
pub fn add_attribute(&mut self, key: &str, value: &str) {
self.attributes.push(VariableAttribute {
key: key.to_string(),
value: value.to_string(),
});
}
#[allow(dead_code)]
pub fn get_attribute(&self, key: &str) -> Option<&str> {
self.attributes
.iter()
.find(|a| a.key == key)
.map(|a| a.value.as_str())
}
#[allow(dead_code)]
pub fn len(&self) -> usize {
match self.data_type {
Nc4DataType::String => self.string_data.len(),
Nc4DataType::Int32 | Nc4DataType::UInt8 => self.int_data.len(),
_ => self.float_data.len(),
}
}
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub(super) struct NetcdfWriterVariable {
pub(super) name: String,
pub(super) dims: Vec<String>,
pub(super) data: Vec<f64>,
pub(super) attrs: Vec<(String, String)>,
}