1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct CriticalExponent {
12 pub name: String,
14 pub value: f64,
16 pub description: String,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct UniversalityClass {
23 pub name: String,
25 pub exponents: Vec<CriticalExponent>,
27 pub dimension: usize,
29 pub n_components: usize,
31 pub description: String,
33}
34
35impl UniversalityClass {
36 pub fn exponent(&self, name: &str) -> Option<f64> {
38 self.exponents.iter().find(|e| e.name == name).map(|e| e.value)
39 }
40
41 pub fn is_same_universality_class(&self, other: &UniversalityClass) -> bool {
43 if self.exponents.len() != other.exponents.len() {
44 return false;
45 }
46 for e in &self.exponents {
47 if let Some(other_val) = other.exponent(&e.name) {
48 if (e.value - other_val).abs() > 0.01 {
49 return false;
50 }
51 } else {
52 return false;
53 }
54 }
55 true
56 }
57}
58
59pub fn mean_field_class() -> UniversalityClass {
61 UniversalityClass {
62 name: "Mean Field".into(),
63 dimension: 4,
64 n_components: 1,
65 description: "Mean-field universality class valid for d ≥ 4".into(),
66 exponents: vec![
67 CriticalExponent {
68 name: "alpha".into(),
69 value: 0.0,
70 description: "Specific heat: C ~ |t|^(-α)".into(),
71 },
72 CriticalExponent {
73 name: "beta".into(),
74 value: 0.5,
75 description: "Order parameter: M ~ |t|^β".into(),
76 },
77 CriticalExponent {
78 name: "gamma".into(),
79 value: 1.0,
80 description: "Susceptibility: χ ~ |t|^(-γ)".into(),
81 },
82 CriticalExponent {
83 name: "delta".into(),
84 value: 3.0,
85 description: "Critical isotherm: M ~ h^(1/δ)".into(),
86 },
87 CriticalExponent {
88 name: "nu".into(),
89 value: 0.5,
90 description: "Correlation length: ξ ~ |t|^(-ν)".into(),
91 },
92 CriticalExponent {
93 name: "eta".into(),
94 value: 0.0,
95 description: "Correlation function decay".into(),
96 },
97 ],
98 }
99}
100
101pub fn ising_2d_class() -> UniversalityClass {
103 UniversalityClass {
104 name: "2D Ising".into(),
105 dimension: 2,
106 n_components: 1,
107 description: "2D Ising universality class (Onsager solution)".into(),
108 exponents: vec![
109 CriticalExponent {
110 name: "alpha".into(),
111 value: 0.0, description: "Specific heat: log divergence".into(),
113 },
114 CriticalExponent {
115 name: "beta".into(),
116 value: 1.0 / 8.0,
117 description: "Order parameter: M ~ |t|^(1/8)".into(),
118 },
119 CriticalExponent {
120 name: "gamma".into(),
121 value: 7.0 / 4.0,
122 description: "Susceptibility: χ ~ |t|^(-7/4)".into(),
123 },
124 CriticalExponent {
125 name: "delta".into(),
126 value: 15.0,
127 description: "Critical isotherm: M ~ h^(1/15)".into(),
128 },
129 CriticalExponent {
130 name: "nu".into(),
131 value: 1.0,
132 description: "Correlation length: ξ ~ |t|^(-1)".into(),
133 },
134 CriticalExponent {
135 name: "eta".into(),
136 value: 0.25,
137 description: "Correlation function decay".into(),
138 },
139 ],
140 }
141}
142
143pub fn ising_3d_class() -> UniversalityClass {
145 UniversalityClass {
146 name: "3D Ising".into(),
147 dimension: 3,
148 n_components: 1,
149 description: "3D Ising universality class (numerical estimates)".into(),
150 exponents: vec![
151 CriticalExponent {
152 name: "alpha".into(),
153 value: 0.110,
154 description: "Specific heat exponent".into(),
155 },
156 CriticalExponent {
157 name: "beta".into(),
158 value: 0.326,
159 description: "Order parameter exponent".into(),
160 },
161 CriticalExponent {
162 name: "gamma".into(),
163 value: 1.237,
164 description: "Susceptibility exponent".into(),
165 },
166 CriticalExponent {
167 name: "delta".into(),
168 value: 4.789,
169 description: "Critical isotherm exponent".into(),
170 },
171 CriticalExponent {
172 name: "nu".into(),
173 value: 0.630,
174 description: "Correlation length exponent".into(),
175 },
176 CriticalExponent {
177 name: "eta".into(),
178 value: 0.036,
179 description: "Anomalous dimension".into(),
180 },
181 ],
182 }
183}
184
185pub fn xy_class() -> UniversalityClass {
187 UniversalityClass {
188 name: "3D XY".into(),
189 dimension: 3,
190 n_components: 2,
191 description: "3D XY universality class (superfluid transition)".into(),
192 exponents: vec![
193 CriticalExponent {
194 name: "alpha".into(),
195 value: -0.015,
196 description: "Specific heat exponent".into(),
197 },
198 CriticalExponent {
199 name: "beta".into(),
200 value: 0.349,
201 description: "Order parameter exponent".into(),
202 },
203 CriticalExponent {
204 name: "gamma".into(),
205 value: 1.316,
206 description: "Susceptibility exponent".into(),
207 },
208 CriticalExponent {
209 name: "nu".into(),
210 value: 0.671,
211 description: "Correlation length exponent".into(),
212 },
213 CriticalExponent {
214 name: "eta".into(),
215 value: 0.038,
216 description: "Anomalous dimension".into(),
217 },
218 CriticalExponent {
219 name: "delta".into(),
220 value: 4.77,
221 description: "Critical isotherm exponent".into(),
222 },
223 ],
224 }
225}
226
227pub fn heisenberg_class() -> UniversalityClass {
229 UniversalityClass {
230 name: "3D Heisenberg".into(),
231 dimension: 3,
232 n_components: 3,
233 description: "3D Heisenberg universality class".into(),
234 exponents: vec![
235 CriticalExponent {
236 name: "alpha".into(),
237 value: -0.115,
238 description: "Specific heat exponent".into(),
239 },
240 CriticalExponent {
241 name: "beta".into(),
242 value: 0.366,
243 description: "Order parameter exponent".into(),
244 },
245 CriticalExponent {
246 name: "gamma".into(),
247 value: 1.386,
248 description: "Susceptibility exponent".into(),
249 },
250 CriticalExponent {
251 name: "nu".into(),
252 value: 0.706,
253 description: "Correlation length exponent".into(),
254 },
255 CriticalExponent {
256 name: "eta".into(),
257 value: 0.037,
258 description: "Anomalous dimension".into(),
259 },
260 CriticalExponent {
261 name: "delta".into(),
262 value: 4.78,
263 description: "Critical isotherm exponent".into(),
264 },
265 ],
266 }
267}
268
269pub fn verify_rushbrooke(class: &UniversalityClass) -> bool {
271 let alpha = class.exponent("alpha").unwrap_or(0.0);
272 let beta = class.exponent("beta").unwrap_or(0.0);
273 let gamma = class.exponent("gamma").unwrap_or(0.0);
274 (alpha + 2.0 * beta + gamma - 2.0).abs() < 0.05
275}
276
277pub fn verify_widom(class: &UniversalityClass) -> bool {
279 let gamma = class.exponent("gamma").unwrap_or(0.0);
280 let beta = class.exponent("beta").unwrap_or(0.0);
281 let delta = class.exponent("delta").unwrap_or(0.0);
282 (gamma - beta * (delta - 1.0)).abs() < 0.05
283}
284
285pub fn verify_fisher(class: &UniversalityClass) -> bool {
287 let gamma = class.exponent("gamma").unwrap_or(0.0);
288 let nu = class.exponent("nu").unwrap_or(0.0);
289 let eta = class.exponent("eta").unwrap_or(0.0);
290 (gamma - nu * (2.0 - eta)).abs() < 0.05
291}
292
293pub fn verify_josephson(class: &UniversalityClass) -> bool {
295 let alpha = class.exponent("alpha").unwrap_or(0.0);
296 let nu = class.exponent("nu").unwrap_or(0.0);
297 (2.0 - alpha - class.dimension as f64 * nu).abs() < 0.05
298}
299
300#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct FleetModel {
303 pub name: String,
305 pub parameters: Vec<f64>,
307 pub topology: String,
309}
310
311impl FleetModel {
312 pub fn new(name: &str, parameters: Vec<f64>, topology: &str) -> Self {
313 Self {
314 name: name.into(),
315 parameters,
316 topology: topology.into(),
317 }
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324
325 #[test]
326 fn test_mean_field_exponents() {
327 let mf = mean_field_class();
328 assert!((mf.exponent("alpha").unwrap() - 0.0).abs() < 1e-10);
329 assert!((mf.exponent("beta").unwrap() - 0.5).abs() < 1e-10);
330 assert!((mf.exponent("gamma").unwrap() - 1.0).abs() < 1e-10);
331 }
332
333 #[test]
334 fn test_2d_ising_exact_exponents() {
335 let ising = ising_2d_class();
336 assert!((ising.exponent("beta").unwrap() - 1.0 / 8.0).abs() < 1e-10);
337 assert!((ising.exponent("gamma").unwrap() - 7.0 / 4.0).abs() < 1e-10);
338 assert!((ising.exponent("nu").unwrap() - 1.0).abs() < 1e-10);
339 }
340
341 #[test]
342 fn test_rushbrooke_mean_field() {
343 assert!(verify_rushbrooke(&mean_field_class()));
344 }
345
346 #[test]
347 fn test_rushbrooke_2d_ising() {
348 assert!(verify_rushbrooke(&ising_2d_class()));
349 }
350
351 #[test]
352 fn test_widom_mean_field() {
353 assert!(verify_widom(&mean_field_class()));
354 }
355
356 #[test]
357 fn test_widom_2d_ising() {
358 assert!(verify_widom(&ising_2d_class()));
359 }
360
361 #[test]
362 fn test_fisher_mean_field() {
363 assert!(verify_fisher(&mean_field_class()));
364 }
365
366 #[test]
367 fn test_josephson_mean_field() {
368 assert!(verify_josephson(&mean_field_class()));
369 }
370
371 #[test]
372 fn test_universality_same_class() {
373 let class1 = ising_3d_class();
375 let mut class2 = ising_3d_class();
376 class2.name = "Fleet Ising 3D".into();
377 assert!(class1.is_same_universality_class(&class2));
378 }
379
380 #[test]
381 fn test_different_universality_classes() {
382 let ising = ising_3d_class();
383 let xy = xy_class();
384 assert!(!ising.is_same_universality_class(&xy));
385 }
386
387 #[test]
388 fn test_fleet_model_creation() {
389 let model = FleetModel::new("test", vec![1.0, 2.0], "1D chain");
390 assert_eq!(model.name, "test");
391 assert_eq!(model.parameters.len(), 2);
392 }
393}