1#[allow(unused_imports)]
6use super::functions::*;
7#[allow(unused_imports)]
8use super::functions_2::*;
9use std::io::Write;
10
11#[derive(Debug, Clone)]
13#[allow(dead_code)]
14pub struct NetCdfDimension {
15 pub name: String,
17 pub size: Option<usize>,
19}
20impl NetCdfDimension {
21 #[allow(dead_code)]
23 pub fn is_unlimited(&self) -> bool {
24 self.size.is_none()
25 }
26 #[allow(dead_code)]
28 pub fn effective_size(&self) -> usize {
29 self.size.unwrap_or(0)
30 }
31}
32#[derive(Debug, Clone)]
34#[allow(dead_code)]
35pub struct NetCdfVariable {
36 pub name: String,
38 pub dims: Vec<String>,
40 pub data: Vec<f64>,
42 pub attributes: Vec<VariableAttribute>,
44}
45impl NetCdfVariable {
46 #[allow(dead_code)]
48 pub fn new(name: &str, dims: Vec<String>, data: Vec<f64>) -> Self {
49 NetCdfVariable {
50 name: name.to_string(),
51 dims,
52 data,
53 attributes: Vec::new(),
54 }
55 }
56 #[allow(dead_code)]
58 pub fn add_attribute(&mut self, key: &str, value: &str) {
59 self.attributes.push(VariableAttribute {
60 key: key.to_string(),
61 value: value.to_string(),
62 });
63 }
64 #[allow(dead_code)]
66 pub fn get_attribute(&self, key: &str) -> Option<&str> {
67 self.attributes
68 .iter()
69 .find(|a| a.key == key)
70 .map(|a| a.value.as_str())
71 }
72 #[allow(dead_code)]
74 pub fn len(&self) -> usize {
75 self.data.len()
76 }
77 #[allow(dead_code)]
79 pub fn is_empty(&self) -> bool {
80 self.data.is_empty()
81 }
82}
83#[derive(Debug, Clone)]
85#[allow(dead_code)]
86pub struct NetcdfVariableStats {
87 pub name: String,
89 pub min: f64,
91 pub max: f64,
93 pub mean: f64,
95 pub std_dev: f64,
97 pub count: usize,
99}
100#[allow(dead_code)]
101impl NetcdfVariableStats {
102 pub fn range(&self) -> f64 {
104 self.max - self.min
105 }
106 pub fn cv(&self) -> f64 {
108 if self.mean.abs() < f64::EPSILON {
109 0.0
110 } else {
111 self.std_dev / self.mean
112 }
113 }
114}
115#[derive(Debug, Clone)]
117#[allow(dead_code)]
118pub struct Nc4File {
119 pub root: Nc4Group,
121 pub global_attributes: Vec<(String, String)>,
123 pub dimensions: Vec<(String, Option<usize>)>,
125 pub unlimited_size: usize,
127}
128impl Nc4File {
129 #[allow(dead_code)]
131 pub fn new() -> Self {
132 Self {
133 root: Nc4Group::new("/"),
134 global_attributes: Vec::new(),
135 dimensions: Vec::new(),
136 unlimited_size: 0,
137 }
138 }
139 #[allow(dead_code)]
141 pub fn add_dimension(&mut self, name: &str, size: usize) {
142 self.dimensions.push((name.to_string(), Some(size)));
143 }
144 #[allow(dead_code)]
146 pub fn add_unlimited_dimension(&mut self, name: &str) {
147 self.dimensions.push((name.to_string(), None));
148 }
149 #[allow(dead_code)]
151 pub fn extend_unlimited(&mut self) {
152 self.unlimited_size += 1;
153 for entry in self.dimensions.iter_mut() {
154 if entry.1.is_none() || entry.1 == Some(self.unlimited_size - 1) {
155 entry.1 = Some(self.unlimited_size);
156 break;
157 }
158 }
159 }
160 #[allow(dead_code)]
162 pub fn add_global_attribute(&mut self, key: &str, value: &str) {
163 self.global_attributes
164 .push((key.to_string(), value.to_string()));
165 }
166 #[allow(dead_code)]
168 pub fn get_global_attribute(&self, key: &str) -> Option<&str> {
169 self.global_attributes
170 .iter()
171 .find(|(k, _)| k == key)
172 .map(|(_, v)| v.as_str())
173 }
174 #[allow(dead_code)]
176 pub fn get_dimension_size(&self, name: &str) -> Option<usize> {
177 self.dimensions
178 .iter()
179 .find(|(n, _)| n == name)
180 .and_then(|(_, s)| *s)
181 }
182 #[allow(dead_code)]
184 pub fn is_unlimited(&self, name: &str) -> bool {
185 self.dimensions.iter().any(|(n, s)| {
186 n == name && s.is_none()
187 || (n == name && *s == Some(self.unlimited_size) && self.unlimited_size > 0)
188 })
189 }
190 #[allow(dead_code)]
192 pub fn add_variable(&mut self, var: Nc4Variable) {
193 self.root.add_variable(var);
194 }
195 #[allow(dead_code)]
197 pub fn get_variable(&self, name: &str) -> Option<&Nc4Variable> {
198 self.root.get_variable(name)
199 }
200 #[allow(dead_code)]
202 pub fn add_group(&mut self, group: Nc4Group) {
203 self.root.add_subgroup(group);
204 }
205 #[allow(dead_code)]
207 pub fn get_group(&self, name: &str) -> Option<&Nc4Group> {
208 self.root.subgroups.iter().find(|g| g.name == name)
209 }
210 #[allow(dead_code)]
212 pub fn total_variable_count(&self) -> usize {
213 self.root.total_variable_count()
214 }
215 #[allow(dead_code)]
222 pub fn to_bytes(&self) -> Vec<u8> {
223 let mut lines = Vec::new();
224 lines.push(format!("unlimited_size:{}", self.unlimited_size));
225 for (k, v) in &self.global_attributes {
226 lines.push(format!("attr:{}={}", k, v));
227 }
228 for (name, size) in &self.dimensions {
229 match size {
230 Some(s) => lines.push(format!("dim:{}={}", name, s)),
231 None => lines.push(format!("dim:{}=UNLIMITED", name)),
232 }
233 }
234 for var in &self.root.variables {
235 lines.push(format!(
236 "var:{}:{}:{}",
237 var.name,
238 var.dims.join(","),
239 var.data_type.type_name()
240 ));
241 }
242 let payload = lines.join("\n");
243 let payload_bytes = payload.as_bytes();
244 let mut buf = Vec::with_capacity(9 + payload_bytes.len());
245 buf.extend_from_slice(b"OXNC4");
246 buf.extend_from_slice(&(payload_bytes.len() as u32).to_le_bytes());
247 buf.extend_from_slice(payload_bytes);
248 buf
249 }
250}
251impl Default for Nc4File {
252 fn default() -> Self {
253 Self::new()
254 }
255}
256#[derive(Debug, Clone, PartialEq, Eq)]
258#[allow(dead_code)]
259pub enum Nc4DataType {
260 Float64,
262 Float32,
264 Int32,
266 UInt8,
268 String,
270}
271impl Nc4DataType {
272 #[allow(dead_code)]
274 pub fn byte_width(&self) -> usize {
275 match self {
276 Nc4DataType::Float64 => 8,
277 Nc4DataType::Float32 => 4,
278 Nc4DataType::Int32 => 4,
279 Nc4DataType::UInt8 => 1,
280 Nc4DataType::String => 0,
281 }
282 }
283 #[allow(dead_code)]
285 pub fn type_name(&self) -> &'static str {
286 match self {
287 Nc4DataType::Float64 => "double",
288 Nc4DataType::Float32 => "float",
289 Nc4DataType::Int32 => "int",
290 Nc4DataType::UInt8 => "ubyte",
291 Nc4DataType::String => "string",
292 }
293 }
294}
295#[derive(Debug, Clone, Default)]
297#[allow(dead_code)]
298pub struct NetcdfTrajectoryBuilder {
299 pub title: String,
301 pub application: String,
303 pub frames: Vec<TrajectoryFrame>,
305 pub use_angstroms: bool,
307}
308impl NetcdfTrajectoryBuilder {
309 #[allow(dead_code)]
311 pub fn new() -> Self {
312 NetcdfTrajectoryBuilder {
313 title: String::new(),
314 application: "OxiPhysics".to_string(),
315 frames: Vec::new(),
316 use_angstroms: true,
317 }
318 }
319 #[allow(dead_code)]
321 pub fn with_title(mut self, title: &str) -> Self {
322 self.title = title.to_string();
323 self
324 }
325 #[allow(dead_code)]
327 pub fn with_application(mut self, app: &str) -> Self {
328 self.application = app.to_string();
329 self
330 }
331 #[allow(dead_code)]
333 pub fn in_angstroms(mut self) -> Self {
334 self.use_angstroms = true;
335 self
336 }
337 #[allow(dead_code)]
339 pub fn in_nanometres(mut self) -> Self {
340 self.use_angstroms = false;
341 self
342 }
343 #[allow(dead_code)]
345 pub fn add_frame(&mut self, frame: TrajectoryFrame) {
346 self.frames.push(frame);
347 }
348 #[allow(dead_code)]
350 pub fn frame_count(&self) -> usize {
351 self.frames.len()
352 }
353 #[allow(dead_code)]
355 pub fn n_atoms(&self) -> usize {
356 self.frames.first().map(|f| f.n_atoms()).unwrap_or(0)
357 }
358 #[allow(dead_code)]
360 pub fn rmsd_series(&self) -> Vec<f64> {
361 if self.frames.is_empty() {
362 return vec![];
363 }
364 let ref_frame = &self.frames[0];
365 self.frames.iter().map(|f| f.rmsd_from(ref_frame)).collect()
366 }
367 #[allow(dead_code)]
369 pub fn write_cdl<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
370 let n_atoms = self.n_atoms();
371 let n_frames = self.frame_count();
372 writeln!(writer, "netcdf trajectory {{")?;
373 writeln!(writer, "dimensions:")?;
374 writeln!(writer, "\tatom = {n_atoms} ;")?;
375 writeln!(writer, "\tframe = UNLIMITED ; // currently {n_frames}")?;
376 writeln!(writer, "\tspatial = 3 ;")?;
377 writeln!(writer, "\tlabel = 5 ;")?;
378 writeln!(writer, "variables:")?;
379 let unit = if self.use_angstroms {
380 "angstrom"
381 } else {
382 "nanometer"
383 };
384 writeln!(writer, "\tdouble coordinates(frame, atom, spatial) ;")?;
385 writeln!(writer, "\t\tcoordinates:units = \"{unit}\" ;")?;
386 writeln!(writer, "\tdouble time(frame) ;")?;
387 writeln!(writer, "\t\ttime:units = \"picosecond\" ;")?;
388 writeln!(writer, "\tdouble cell_lengths(frame, spatial) ;")?;
389 writeln!(writer, "\t\tcell_lengths:units = \"{unit}\" ;")?;
390 writeln!(writer, "// global attributes:")?;
391 writeln!(writer, "\t:title = \"{}\" ;", self.title)?;
392 writeln!(writer, "\t:application = \"{}\" ;", self.application)?;
393 writeln!(writer, "\t:Conventions = \"AMBER\" ;")?;
394 writeln!(writer, "\t:ConventionVersion = \"1.0\" ;")?;
395 writeln!(writer, "}}")?;
396 Ok(())
397 }
398 #[allow(dead_code)]
400 pub fn time_series(&self) -> Vec<f64> {
401 self.frames.iter().map(|f| f.time_ps).collect()
402 }
403 #[allow(dead_code)]
405 pub fn com_trajectory(&self) -> Vec<[f64; 3]> {
406 self.frames.iter().map(|f| f.centre_of_mass()).collect()
407 }
408 #[allow(dead_code)]
410 pub fn frame(&self, idx: usize) -> &TrajectoryFrame {
411 &self.frames[idx]
412 }
413}
414#[derive(Debug, Clone)]
416#[allow(dead_code)]
417pub struct NetCdfFile {
418 pub dimensions: Vec<(String, usize)>,
420 pub variables: Vec<NetCdfVariable>,
422 pub attributes: Vec<(String, String)>,
424 pub unlimited_dim: Option<String>,
426}
427impl NetCdfFile {
428 #[allow(dead_code)]
430 pub fn new() -> Self {
431 NetCdfFile {
432 dimensions: Vec::new(),
433 variables: Vec::new(),
434 attributes: Vec::new(),
435 unlimited_dim: None,
436 }
437 }
438 #[allow(dead_code)]
440 pub fn add_dimension(&mut self, name: &str, size: usize) {
441 self.dimensions.push((name.to_string(), size));
442 }
443 #[allow(dead_code)]
445 pub fn add_unlimited_dimension(&mut self, name: &str, current_size: usize) {
446 self.dimensions.push((name.to_string(), current_size));
447 self.unlimited_dim = Some(name.to_string());
448 }
449 #[allow(dead_code)]
451 pub fn add_variable(&mut self, var: NetCdfVariable) {
452 self.variables.push(var);
453 }
454 #[allow(dead_code)]
456 pub fn add_attribute(&mut self, key: &str, value: &str) {
457 self.attributes.push((key.to_string(), value.to_string()));
458 }
459 #[allow(dead_code)]
461 pub fn get_variable(&self, name: &str) -> Option<&NetCdfVariable> {
462 self.variables.iter().find(|v| v.name == name)
463 }
464 #[allow(dead_code)]
466 pub fn get_variable_mut(&mut self, name: &str) -> Option<&mut NetCdfVariable> {
467 self.variables.iter_mut().find(|v| v.name == name)
468 }
469 #[allow(dead_code)]
471 pub fn get_attribute(&self, key: &str) -> Option<&str> {
472 self.attributes
473 .iter()
474 .find(|(k, _)| k == key)
475 .map(|(_, v)| v.as_str())
476 }
477 #[allow(dead_code)]
479 pub fn get_dimension_size(&self, name: &str) -> Option<usize> {
480 self.dimensions
481 .iter()
482 .find(|(n, _)| n == name)
483 .map(|(_, s)| *s)
484 }
485 #[allow(dead_code)]
487 pub fn is_unlimited_dimension(&self, name: &str) -> bool {
488 self.unlimited_dim.as_deref() == Some(name)
489 }
490 #[allow(dead_code)]
492 pub fn variable_names(&self) -> Vec<&str> {
493 self.variables.iter().map(|v| v.name.as_str()).collect()
494 }
495 #[allow(dead_code)]
497 pub fn dimension_names(&self) -> Vec<&str> {
498 self.dimensions.iter().map(|(n, _)| n.as_str()).collect()
499 }
500}
501impl Default for NetCdfFile {
502 fn default() -> Self {
503 Self::new()
504 }
505}
506#[derive(Debug, Clone)]
508#[allow(dead_code)]
509pub struct NetcdfFile {
510 pub dimensions: std::collections::HashMap<String, usize>,
512 pub variables: Vec<NetcdfVariable>,
514 pub global_attrs: Vec<(String, String)>,
516}
517#[allow(dead_code)]
518impl NetcdfFile {
519 pub fn new() -> Self {
521 Self {
522 dimensions: std::collections::HashMap::new(),
523 variables: Vec::new(),
524 global_attrs: Vec::new(),
525 }
526 }
527 pub fn add_dimension(&mut self, name: &str, size: usize) {
529 self.dimensions.insert(name.to_string(), size);
530 }
531 pub fn add_variable(&mut self, var: NetcdfVariable) {
533 self.variables.push(var);
534 }
535 pub fn add_global_attr(&mut self, key: &str, value: &str) {
537 self.global_attrs.push((key.to_string(), value.to_string()));
538 }
539 pub fn write_cdl(&self) -> String {
541 let mut out = String::new();
542 out.push_str("netcdf data {\n");
543 out.push_str("dimensions:\n");
544 let mut dims: Vec<(&String, &usize)> = self.dimensions.iter().collect();
545 dims.sort_by_key(|(k, _)| k.as_str());
546 for (name, size) in &dims {
547 out.push_str(&format!("\t{} = {} ;\n", name, size));
548 }
549 out.push_str("variables:\n");
550 for var in &self.variables {
551 let dims_str = var.dimensions.join(", ");
552 out.push_str(&format!("\tdouble {}({}) ;\n", var.name, dims_str));
553 if !var.units.is_empty() {
554 out.push_str(&format!("\t\t{}:units = \"{}\" ;\n", var.name, var.units));
555 }
556 if !var.long_name.is_empty() {
557 out.push_str(&format!(
558 "\t\t{}:long_name = \"{}\" ;\n",
559 var.name, var.long_name
560 ));
561 }
562 }
563 if !self.global_attrs.is_empty() {
564 out.push_str("// global attributes:\n");
565 for (key, value) in &self.global_attrs {
566 out.push_str(&format!("\t\t:{} = \"{}\" ;\n", key, value));
567 }
568 }
569 out.push_str("data:\n");
570 for var in &self.variables {
571 let vals: Vec<String> = var.data.iter().map(|v| format!("{}", v)).collect();
572 out.push_str(&format!("\t{} = {} ;\n", var.name, vals.join(", ")));
573 }
574 out.push_str("}\n");
575 out
576 }
577 pub fn trajectory_write(
582 path: &str,
583 times: &[f64],
584 positions: &[Vec<[f64; 3]>],
585 ) -> std::io::Result<()> {
586 use std::io::Write;
587 let n_frames = times.len();
588 let n_atoms = positions.first().map(|f| f.len()).unwrap_or(0);
589 let mut f = std::fs::File::create(path)?;
590 writeln!(f, "netcdf trajectory {{")?;
591 writeln!(f, "dimensions:")?;
592 writeln!(f, "\tframe = {} ;", n_frames)?;
593 writeln!(f, "\tatom = {} ;", n_atoms)?;
594 writeln!(f, "\tspatial = 3 ;")?;
595 writeln!(f, "variables:")?;
596 writeln!(f, "\tdouble time(frame) ;")?;
597 writeln!(f, "\t\ttime:units = \"ps\" ;")?;
598 writeln!(f, "\tdouble coordinates(frame, atom, spatial) ;")?;
599 writeln!(f, "\t\tcoordinates:units = \"angstrom\" ;")?;
600 writeln!(f, "data:")?;
601 let time_vals: Vec<String> = times.iter().map(|t| format!("{}", t)).collect();
602 writeln!(f, "\ttime = {} ;", time_vals.join(", "))?;
603 write!(f, "\tcoordinates = ")?;
604 let mut first = true;
605 for frame in positions {
606 for pos in frame {
607 for &coord in pos {
608 if !first {
609 write!(f, ", ")?;
610 }
611 write!(f, "{}", coord)?;
612 first = false;
613 }
614 }
615 }
616 writeln!(f, " ;")?;
617 writeln!(f, "}}")?;
618 Ok(())
619 }
620}
621#[derive(Debug, Clone)]
626#[allow(dead_code)]
627pub struct NetcdfWriter {
628 pub(super) name: String,
629 pub(super) dimensions: Vec<(String, usize)>,
630 pub(super) unlimited_dim: Option<String>,
631 pub(super) variables: Vec<NetcdfWriterVariable>,
632 pub(super) global_attrs: Vec<(String, String)>,
633}
634#[allow(dead_code)]
635impl NetcdfWriter {
636 pub fn new(name: &str) -> Self {
638 Self {
639 name: name.to_string(),
640 dimensions: Vec::new(),
641 unlimited_dim: None,
642 variables: Vec::new(),
643 global_attrs: Vec::new(),
644 }
645 }
646 pub fn add_dimension(&mut self, name: &str, size: usize) -> &mut Self {
648 self.dimensions.push((name.to_string(), size));
649 self
650 }
651 pub fn add_unlimited_dimension(&mut self, name: &str, current_size: usize) -> &mut Self {
653 self.dimensions.push((name.to_string(), current_size));
654 self.unlimited_dim = Some(name.to_string());
655 self
656 }
657 pub fn add_global_attribute(&mut self, key: &str, value: &str) -> &mut Self {
659 self.global_attrs.push((key.to_string(), value.to_string()));
660 self
661 }
662 pub fn add_variable(&mut self, name: &str, dims: &[&str], data: Vec<f64>) -> &mut Self {
664 self.variables.push(NetcdfWriterVariable {
665 name: name.to_string(),
666 dims: dims.iter().map(|s| s.to_string()).collect(),
667 data,
668 attrs: Vec::new(),
669 });
670 self
671 }
672 pub fn add_variable_attribute(&mut self, key: &str, value: &str) -> &mut Self {
674 if let Some(v) = self.variables.last_mut() {
675 v.attrs.push((key.to_string(), value.to_string()));
676 }
677 self
678 }
679 pub fn add_coordinate(&mut self, dim_name: &str, data: Vec<f64>, units: &str) -> &mut Self {
681 self.add_variable(dim_name, &[dim_name], data);
682 self.add_variable_attribute("units", units);
683 self.add_variable_attribute("axis", dim_name);
684 self
685 }
686 pub fn n_dimensions(&self) -> usize {
688 self.dimensions.len()
689 }
690 pub fn n_variables(&self) -> usize {
692 self.variables.len()
693 }
694 pub fn to_cdl(&self) -> String {
696 let mut out = String::new();
697 out.push_str(&format!("netcdf {} {{\n", self.name));
698 out.push_str("dimensions:\n");
699 for (name, size) in &self.dimensions {
700 if self.unlimited_dim.as_deref() == Some(name.as_str()) {
701 out.push_str(&format!(
702 "\t{} = UNLIMITED ; // ({} currently)\n",
703 name, size
704 ));
705 } else {
706 out.push_str(&format!("\t{} = {} ;\n", name, size));
707 }
708 }
709 out.push_str("variables:\n");
710 for var in &self.variables {
711 let dims = var.dims.join(", ");
712 out.push_str(&format!("\tdouble {}({}) ;\n", var.name, dims));
713 for (k, v) in &var.attrs {
714 out.push_str(&format!("\t\t{}:{} = \"{}\" ;\n", var.name, k, v));
715 }
716 }
717 if !self.global_attrs.is_empty() {
718 out.push_str("// global attributes:\n");
719 for (k, v) in &self.global_attrs {
720 out.push_str(&format!("\t:{} = \"{}\" ;\n", k, v));
721 }
722 }
723 out.push_str("data:\n");
724 for var in &self.variables {
725 let vals: Vec<String> = var.data.iter().map(|v| format!("{}", v)).collect();
726 out.push_str(&format!("\t{} = {} ;\n", var.name, vals.join(", ")));
727 }
728 out.push_str("}\n");
729 out
730 }
731 pub fn finish(self) -> String {
733 self.to_cdl()
734 }
735 pub fn write_to_file(&self, path: &str) -> std::io::Result<()> {
737 let mut f = std::fs::File::create(path)?;
738 f.write_all(self.to_cdl().as_bytes())
739 }
740}
741#[derive(Debug, Clone)]
743#[allow(dead_code)]
744pub struct NetcdfVariable {
745 pub name: String,
747 pub dimensions: Vec<String>,
749 pub data: Vec<f64>,
751 pub units: String,
753 pub long_name: String,
755}
756#[derive(Debug, Clone)]
758#[allow(dead_code)]
759pub struct TrajectoryFrame {
760 pub time_ps: f64,
762 pub positions: Vec<[f64; 3]>,
764 pub velocities: Option<Vec<[f64; 3]>>,
766 pub box_lengths: Option<[f64; 3]>,
768}
769impl TrajectoryFrame {
770 #[allow(dead_code)]
772 pub fn n_atoms(&self) -> usize {
773 self.positions.len()
774 }
775 #[allow(dead_code)]
777 pub fn centre_of_mass(&self) -> [f64; 3] {
778 if self.positions.is_empty() {
779 return [0.0; 3];
780 }
781 let mut s = [0.0_f64; 3];
782 for p in &self.positions {
783 s[0] += p[0];
784 s[1] += p[1];
785 s[2] += p[2];
786 }
787 let inv = 1.0 / self.positions.len() as f64;
788 [s[0] * inv, s[1] * inv, s[2] * inv]
789 }
790 #[allow(dead_code)]
792 pub fn rmsd_from(&self, reference: &TrajectoryFrame) -> f64 {
793 let n = self.positions.len().min(reference.positions.len());
794 if n == 0 {
795 return 0.0;
796 }
797 let sum: f64 = (0..n)
798 .map(|i| {
799 let dx = self.positions[i][0] - reference.positions[i][0];
800 let dy = self.positions[i][1] - reference.positions[i][1];
801 let dz = self.positions[i][2] - reference.positions[i][2];
802 dx * dx + dy * dy + dz * dz
803 })
804 .sum();
805 (sum / n as f64).sqrt()
806 }
807 #[allow(dead_code)]
810 pub fn kinetic_energy(&self, mass_amu: f64) -> f64 {
811 let vels = match &self.velocities {
812 Some(v) => v,
813 None => return 0.0,
814 };
815 let sum: f64 = vels
816 .iter()
817 .map(|v| v[0] * v[0] + v[1] * v[1] + v[2] * v[2])
818 .sum();
819 0.5 * mass_amu * sum
820 }
821 #[allow(dead_code)]
823 pub fn translate(&mut self, delta: [f64; 3]) {
824 for p in &mut self.positions {
825 p[0] += delta[0];
826 p[1] += delta[1];
827 p[2] += delta[2];
828 }
829 }
830}
831#[derive(Debug, Clone)]
836#[allow(dead_code)]
837pub struct NetcdfReader {
838 pub dimensions: std::collections::HashMap<String, usize>,
840 pub variables: Vec<NetcdfVariable>,
842 pub global_attrs: Vec<(String, String)>,
844 pub unlimited_dim: Option<String>,
846}
847#[allow(dead_code)]
848impl NetcdfReader {
849 pub fn from_cdl(cdl: &str) -> Result<Self, String> {
851 let file = parse_ncf_text(cdl)?;
852 let variables: Vec<NetcdfVariable> = file
853 .variables
854 .into_iter()
855 .map(|v| {
856 let units = v.get_attribute("units").unwrap_or("").to_string();
857 let long_name = v.get_attribute("long_name").unwrap_or("").to_string();
858 NetcdfVariable {
859 name: v.name,
860 dimensions: v.dims,
861 data: v.data,
862 units,
863 long_name,
864 }
865 })
866 .collect();
867 Ok(Self {
868 dimensions: file.dimensions.into_iter().collect(),
869 variables,
870 global_attrs: file.attributes,
871 unlimited_dim: file.unlimited_dim,
872 })
873 }
874 pub fn get_dimension(&self, name: &str) -> Option<usize> {
876 self.dimensions.get(name).copied()
877 }
878 pub fn get_variable(&self, name: &str) -> Option<&NetcdfVariable> {
880 self.variables.iter().find(|v| v.name == name)
881 }
882 pub fn get_data(&self, var_name: &str) -> Option<&[f64]> {
884 self.get_variable(var_name).map(|v| v.data.as_slice())
885 }
886 pub fn variable_names(&self) -> Vec<&str> {
888 self.variables.iter().map(|v| v.name.as_str()).collect()
889 }
890 pub fn dimension_names(&self) -> Vec<&str> {
892 let mut names: Vec<&str> = self.dimensions.keys().map(String::as_str).collect();
893 names.sort();
894 names
895 }
896}
897#[derive(Debug, Clone)]
899#[allow(dead_code)]
900pub struct VariableAttribute {
901 pub key: String,
903 pub value: String,
905}
906#[derive(Debug, Clone)]
910#[allow(dead_code)]
911pub struct Nc4Group {
912 pub name: String,
914 pub variables: Vec<Nc4Variable>,
916 pub subgroups: Vec<Nc4Group>,
918 pub attributes: Vec<(String, String)>,
920}
921impl Nc4Group {
922 #[allow(dead_code)]
924 pub fn new(name: &str) -> Self {
925 Self {
926 name: name.to_string(),
927 variables: Vec::new(),
928 subgroups: Vec::new(),
929 attributes: Vec::new(),
930 }
931 }
932 #[allow(dead_code)]
934 pub fn add_variable(&mut self, var: Nc4Variable) {
935 self.variables.push(var);
936 }
937 #[allow(dead_code)]
939 pub fn add_subgroup(&mut self, group: Nc4Group) {
940 self.subgroups.push(group);
941 }
942 #[allow(dead_code)]
944 pub fn add_attribute(&mut self, key: &str, value: &str) {
945 self.attributes.push((key.to_string(), value.to_string()));
946 }
947 #[allow(dead_code)]
949 pub fn get_variable(&self, name: &str) -> Option<&Nc4Variable> {
950 self.variables.iter().find(|v| v.name == name)
951 }
952 #[allow(dead_code)]
954 pub fn get_attribute(&self, key: &str) -> Option<&str> {
955 self.attributes
956 .iter()
957 .find(|(k, _)| k == key)
958 .map(|(_, v)| v.as_str())
959 }
960 #[allow(dead_code)]
962 pub fn all_variables(&self) -> Vec<&Nc4Variable> {
963 let mut out: Vec<&Nc4Variable> = self.variables.iter().collect();
964 for sg in &self.subgroups {
965 out.extend(sg.all_variables());
966 }
967 out
968 }
969 #[allow(dead_code)]
971 pub fn total_variable_count(&self) -> usize {
972 self.variables.len()
973 + self
974 .subgroups
975 .iter()
976 .map(|g| g.total_variable_count())
977 .sum::<usize>()
978 }
979}
980#[derive(Debug, Clone, PartialEq)]
982#[allow(dead_code)]
983pub struct NetcdfDimSpec {
984 pub name: String,
986 pub size: usize,
988 pub unlimited: bool,
990}
991impl NetcdfDimSpec {
992 #[allow(dead_code)]
994 pub fn fixed(name: &str, size: usize) -> Self {
995 NetcdfDimSpec {
996 name: name.to_string(),
997 size,
998 unlimited: false,
999 }
1000 }
1001 #[allow(dead_code)]
1003 pub fn unlimited(name: &str, current_size: usize) -> Self {
1004 NetcdfDimSpec {
1005 name: name.to_string(),
1006 size: current_size,
1007 unlimited: true,
1008 }
1009 }
1010 #[allow(dead_code)]
1012 pub fn to_cdl(&self) -> String {
1013 if self.unlimited {
1014 format!("\t{} = UNLIMITED ; // currently {}", self.name, self.size)
1015 } else {
1016 format!("\t{} = {} ;", self.name, self.size)
1017 }
1018 }
1019}
1020#[derive(Debug, Clone)]
1022#[allow(dead_code)]
1023pub struct Nc4Variable {
1024 pub name: String,
1026 pub dims: Vec<String>,
1028 pub float_data: Vec<f64>,
1030 pub string_data: Vec<String>,
1032 pub int_data: Vec<i64>,
1034 pub data_type: Nc4DataType,
1036 pub attributes: Vec<VariableAttribute>,
1038}
1039impl Nc4Variable {
1040 #[allow(dead_code)]
1042 pub fn float64(name: &str, dims: Vec<String>, data: Vec<f64>) -> Self {
1043 Self {
1044 name: name.to_string(),
1045 dims,
1046 float_data: data,
1047 string_data: Vec::new(),
1048 int_data: Vec::new(),
1049 data_type: Nc4DataType::Float64,
1050 attributes: Vec::new(),
1051 }
1052 }
1053 #[allow(dead_code)]
1055 pub fn string_var(name: &str, dims: Vec<String>, data: Vec<String>) -> Self {
1056 Self {
1057 name: name.to_string(),
1058 dims,
1059 float_data: Vec::new(),
1060 string_data: data,
1061 int_data: Vec::new(),
1062 data_type: Nc4DataType::String,
1063 attributes: Vec::new(),
1064 }
1065 }
1066 #[allow(dead_code)]
1068 pub fn int32(name: &str, dims: Vec<String>, data: Vec<i64>) -> Self {
1069 Self {
1070 name: name.to_string(),
1071 dims,
1072 float_data: Vec::new(),
1073 string_data: Vec::new(),
1074 int_data: data,
1075 data_type: Nc4DataType::Int32,
1076 attributes: Vec::new(),
1077 }
1078 }
1079 #[allow(dead_code)]
1081 pub fn add_attribute(&mut self, key: &str, value: &str) {
1082 self.attributes.push(VariableAttribute {
1083 key: key.to_string(),
1084 value: value.to_string(),
1085 });
1086 }
1087 #[allow(dead_code)]
1089 pub fn get_attribute(&self, key: &str) -> Option<&str> {
1090 self.attributes
1091 .iter()
1092 .find(|a| a.key == key)
1093 .map(|a| a.value.as_str())
1094 }
1095 #[allow(dead_code)]
1097 pub fn len(&self) -> usize {
1098 match self.data_type {
1099 Nc4DataType::String => self.string_data.len(),
1100 Nc4DataType::Int32 | Nc4DataType::UInt8 => self.int_data.len(),
1101 _ => self.float_data.len(),
1102 }
1103 }
1104 #[allow(dead_code)]
1106 pub fn is_empty(&self) -> bool {
1107 self.len() == 0
1108 }
1109}
1110#[derive(Debug, Clone)]
1112#[allow(dead_code)]
1113pub(super) struct NetcdfWriterVariable {
1114 pub(super) name: String,
1115 pub(super) dims: Vec<String>,
1116 pub(super) data: Vec<f64>,
1117 pub(super) attrs: Vec<(String, String)>,
1118}