noether_guard/
conservation.rs1use crate::symmetry::Symmetry;
2
3#[derive(Debug, Clone)]
5pub struct ConservationLaw {
6 pub symmetry: Symmetry,
7 pub name: String,
8 pub initial_value: f64,
9 pub tolerance: f64,
10}
11
12impl ConservationLaw {
13 pub fn new(symmetry: Symmetry, initial_value: f64, tolerance: f64) -> Self {
15 Self {
16 name: symmetry.conserved_quantity().to_string(),
17 symmetry,
18 initial_value,
19 tolerance,
20 }
21 }
22
23 pub fn is_conserved(&self, value: f64) -> bool {
25 (value - self.initial_value).abs() <= self.tolerance
26 }
27
28 pub fn drift(&self, value: f64) -> f64 {
30 (value - self.initial_value).abs()
31 }
32
33 pub fn drift_rate(&self, previous_drift: f64, dt: f64) -> f64 {
35 if dt > 0.0 {
36 (self.initial_value - previous_drift).abs() / dt
37 } else {
38 0.0
39 }
40 }
41}
42
43#[derive(Debug, Clone)]
45pub struct ConservedQuantity {
46 pub name: String,
47 pub values: Vec<(f64, f64)>,
48 pub drift_rate: f64,
49}
50
51impl ConservedQuantity {
52 pub fn new(name: &str) -> Self {
53 Self {
54 name: name.to_string(),
55 values: Vec::new(),
56 drift_rate: 0.0,
57 }
58 }
59
60 pub fn record(&mut self, time: f64, value: f64) {
62 self.values.push((time, value));
63 if self.values.len() >= 2 {
64 let (t0, v0) = self.values[self.values.len() - 2];
65 let (t1, v1) = self.values[self.values.len() - 1];
66 let dt = t1 - t0;
67 if dt > 0.0 {
68 self.drift_rate = (v1 - v0) / dt;
69 }
70 }
71 }
72
73 pub fn current_drift_rate(&self) -> f64 {
75 self.drift_rate
76 }
77
78 pub fn total_drift(&self) -> f64 {
80 if self.values.len() < 2 {
81 return 0.0;
82 }
83 let first = self.values.first().unwrap().1;
84 let last = self.values.last().unwrap().1;
85 (last - first).abs()
86 }
87
88 pub fn len(&self) -> usize {
90 self.values.len()
91 }
92
93 pub fn is_empty(&self) -> bool {
95 self.values.is_empty()
96 }
97}