qualia_core_db/specialized_libs/physics_simulation/
fields.rs1use super::*;
2
3impl PhysicsSimulationLibrary {
4 pub fn run_heat_diffusion_1d(
8 &self,
9 initial: Vec<f64>,
10 alpha: f64,
11 dx: f64,
12 total_time: f64,
13 num_samples: usize,
14 ) -> Result<HeatDiffusionResult, PhysicsError> {
15 let n = initial.len();
16 if n < 3 || !(alpha > 0.0 && dx > 0.0 && total_time > 0.0) {
17 return Err(PhysicsError::InvalidConfiguration(
18 "require grid length >= 3, alpha > 0, dx > 0, total_time > 0".to_string(),
19 ));
20 }
21 let initial_mean = initial.iter().sum::<f64>() / n as f64;
22 let inv_dx2 = 1.0 / (dx * dx);
23 let deriv = move |_t: f64, u: &[f64], du: &mut [f64]| -> Result<(), OdeError> {
24 for i in 0..n {
28 let flux_left = if i == 0 { 0.0 } else { u[i] - u[i - 1] };
29 let flux_right = if i == n - 1 { 0.0 } else { u[i + 1] - u[i] };
30 du[i] = alpha * (flux_right - flux_left) * inv_dx2;
31 }
32 Ok(())
33 };
34 let (final_temp, snapshots, accepted, rejected) =
35 self.integrate_ode_samples(initial, total_time, num_samples, deriv)?;
36 let n_pts = snapshots.len();
37 let times: Vec<f64> = (0..n_pts)
38 .map(|k| total_time * k as f64 / (n_pts - 1).max(1) as f64)
39 .collect();
40 let final_mean = final_temp.iter().sum::<f64>() / n as f64;
41 let max_deviation_from_mean = final_temp
42 .iter()
43 .map(|&v| (v - final_mean).abs())
44 .fold(0.0, f64::max);
45 Ok(HeatDiffusionResult {
46 times,
47 snapshots,
48 final_temperature: final_temp,
49 initial_mean,
50 final_mean,
51 max_deviation_from_mean,
52 steps_accepted: accepted,
53 steps_rejected: rejected,
54 })
55 }
56 pub fn run_wave_equation_1d(
60 &self,
61 initial_displacement: Vec<f64>,
62 initial_velocity: Vec<f64>,
63 c: f64,
64 dx: f64,
65 total_time: f64,
66 num_samples: usize,
67 ) -> Result<WaveResult, PhysicsError> {
68 let n = initial_displacement.len();
69 if n < 3 || initial_velocity.len() != n || !(c > 0.0 && dx > 0.0 && total_time > 0.0) {
70 return Err(PhysicsError::InvalidConfiguration(
71 "require matching grids length >= 3, c > 0, dx > 0, total_time > 0".to_string(),
72 ));
73 }
74 let c2 = c * c;
75 let inv_dx2 = 1.0 / (dx * dx);
76 let mut state = Vec::with_capacity(2 * n);
77 state.extend_from_slice(&initial_displacement);
78 state.extend_from_slice(&initial_velocity);
79 state[0] = 0.0;
81 state[n - 1] = 0.0;
82 state[n] = 0.0;
83 state[2 * n - 1] = 0.0;
84
85 let energy = |st: &[f64]| -> f64 {
86 let mut e = 0.0;
87 for i in 0..n {
88 let v = st[n + i];
89 e += 0.5 * v * v * dx;
90 }
91 for i in 0..n - 1 {
92 let grad = (st[i + 1] - st[i]) / dx;
93 e += 0.5 * c2 * grad * grad * dx;
94 }
95 e
96 };
97 let energy_initial = energy(&state);
98
99 let deriv = move |_t: f64, y: &[f64], dy: &mut [f64]| -> Result<(), OdeError> {
100 for i in 0..n {
101 if i == 0 || i == n - 1 {
102 dy[i] = 0.0; dy[n + i] = 0.0; } else {
105 dy[i] = y[n + i]; dy[n + i] = c2 * (y[i + 1] - 2.0 * y[i] + y[i - 1]) * inv_dx2;
107 }
109 }
110 Ok(())
111 };
112 let (final_state, snapshots, accepted, rejected) =
113 self.integrate_ode_samples(state, total_time, num_samples, deriv)?;
114 let energy_final = energy(&final_state);
115 let disp_snapshots: Vec<Vec<f64>> = snapshots.iter().map(|s| s[..n].to_vec()).collect();
116 let n_pts = snapshots.len();
117 let times: Vec<f64> = (0..n_pts)
118 .map(|k| total_time * k as f64 / (n_pts - 1).max(1) as f64)
119 .collect();
120 Ok(WaveResult {
121 times,
122 snapshots: disp_snapshots,
123 final_displacement: final_state[..n].to_vec(),
124 energy_initial,
125 energy_final,
126 steps_accepted: accepted,
127 steps_rejected: rejected,
128 })
129 }
130 pub fn run_advection_diffusion_1d(
136 &self,
137 initial: Vec<f64>,
138 advection_velocity: f64,
139 diffusion_coeff: f64,
140 dx: f64,
141 total_time: f64,
142 num_samples: usize,
143 ) -> Result<AdvectionDiffusionResult, PhysicsError> {
144 let n = initial.len();
145 if n < 3 || !(dx > 0.0 && total_time > 0.0) || diffusion_coeff < 0.0 {
146 return Err(PhysicsError::InvalidConfiguration(
147 "require grid length >= 3, dx > 0, total_time > 0, diffusion_coeff >= 0"
148 .to_string(),
149 ));
150 }
151 let c = advection_velocity;
152 let alpha = diffusion_coeff;
153 let inv_dx = 1.0 / dx;
154 let inv_dx2 = 1.0 / (dx * dx);
155 let initial_total = initial.iter().sum::<f64>() * dx;
156 let deriv = move |_t: f64, u: &[f64], du: &mut [f64]| -> Result<(), OdeError> {
157 for i in 0..n {
158 let ip1 = (i + 1) % n;
159 let im1 = (i + n - 1) % n;
160 let adv = if c >= 0.0 {
162 -c * (u[i] - u[im1]) * inv_dx
163 } else {
164 -c * (u[ip1] - u[i]) * inv_dx
165 };
166 let diff = alpha * (u[ip1] - 2.0 * u[i] + u[im1]) * inv_dx2;
167 du[i] = adv + diff;
168 }
169 Ok(())
170 };
171 let (final_field, snapshots, accepted, rejected) =
172 self.integrate_ode_samples(initial, total_time, num_samples, deriv)?;
173 let n_pts = snapshots.len();
174 let times: Vec<f64> = (0..n_pts)
175 .map(|k| total_time * k as f64 / (n_pts - 1).max(1) as f64)
176 .collect();
177 let final_total = final_field.iter().sum::<f64>() * dx;
178 Ok(AdvectionDiffusionResult {
179 times,
180 snapshots,
181 final_field,
182 advection_velocity: c,
183 diffusion_coeff: alpha,
184 initial_total,
185 final_total,
186 steps_accepted: accepted,
187 steps_rejected: rejected,
188 })
189 }
190}