1#[allow(dead_code)]
16#[derive(Debug, Clone, PartialEq)]
17pub struct PhysicalConstants {
18 pub c: f64,
20 pub h: f64,
22 pub k_b: f64,
24 pub n_a: f64,
26 pub r: f64,
28 pub g: f64,
30 pub epsilon_0: f64,
32 pub mu_0: f64,
34}
35
36impl Default for PhysicalConstants {
37 fn default() -> Self {
38 Self {
39 c: 2.997_924_58e8,
40 h: 6.626_070_15e-34,
41 k_b: 1.380_649e-23,
42 n_a: 6.022_140_76e23,
43 r: 8.314_462_618,
44 g: 6.674_30e-11,
45 epsilon_0: 8.854_187_812_8e-12,
46 mu_0: 1.256_637_062_12e-6,
47 }
48 }
49}
50
51impl PhysicalConstants {
52 pub fn new() -> Self {
54 Self::default()
55 }
56}
57
58#[allow(dead_code)]
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum LengthUnit {
66 Meter,
68 Centimeter,
70 Millimeter,
72 Micrometer,
74 Nanometer,
76 Angstrom,
78 Foot,
80 Inch,
82 LightYear,
84 AstronomicalUnit,
86 Parsec,
88}
89
90impl LengthUnit {
91 fn to_meters(self) -> f64 {
93 match self {
94 LengthUnit::Meter => 1.0,
95 LengthUnit::Centimeter => 1e-2,
96 LengthUnit::Millimeter => 1e-3,
97 LengthUnit::Micrometer => 1e-6,
98 LengthUnit::Nanometer => 1e-9,
99 LengthUnit::Angstrom => 1e-10,
100 LengthUnit::Foot => 0.3048,
101 LengthUnit::Inch => 0.0254,
102 LengthUnit::LightYear => 9.460_730_472_580_8e15,
103 LengthUnit::AstronomicalUnit => 1.495_978_707e11,
104 LengthUnit::Parsec => 3.085_677_581_49e16,
105 }
106 }
107}
108
109#[allow(dead_code)]
118pub fn convert_length(value: f64, from: LengthUnit, to: LengthUnit) -> f64 {
119 value * from.to_meters() / to.to_meters()
120}
121
122#[allow(dead_code)]
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129pub enum MassUnit {
130 Kilogram,
132 Gram,
134 Milligram,
136 Microgram,
138 Pound,
140 Ounce,
142 AtomicMassUnit,
144 Dalton,
146}
147
148impl MassUnit {
149 fn to_kg(self) -> f64 {
151 match self {
152 MassUnit::Kilogram => 1.0,
153 MassUnit::Gram => 1e-3,
154 MassUnit::Milligram => 1e-6,
155 MassUnit::Microgram => 1e-9,
156 MassUnit::Pound => 0.453_592_37,
157 MassUnit::Ounce => 0.028_349_523_125,
158 MassUnit::AtomicMassUnit | MassUnit::Dalton => 1.660_539_066_60e-27,
159 }
160 }
161}
162
163#[allow(dead_code)]
172pub fn convert_mass(value: f64, from: MassUnit, to: MassUnit) -> f64 {
173 value * from.to_kg() / to.to_kg()
174}
175
176#[allow(dead_code)]
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub enum EnergyUnit {
184 Joule,
186 Kilojoule,
188 Electronvolt,
190 Kilocalorie,
192 Calorie,
194 Erg,
196 Btu,
198 Hartree,
200}
201
202impl EnergyUnit {
203 fn to_joules(self) -> f64 {
205 match self {
206 EnergyUnit::Joule => 1.0,
207 EnergyUnit::Kilojoule => 1e3,
208 EnergyUnit::Electronvolt => 1.602_176_634e-19,
209 EnergyUnit::Kilocalorie => 4184.0,
210 EnergyUnit::Calorie => 4.184,
211 EnergyUnit::Erg => 1e-7,
212 EnergyUnit::Btu => 1055.05585262,
213 EnergyUnit::Hartree => 4.359_744_650_98e-18,
214 }
215 }
216}
217
218#[allow(dead_code)]
227pub fn convert_energy(value: f64, from: EnergyUnit, to: EnergyUnit) -> f64 {
228 value * from.to_joules() / to.to_joules()
229}
230
231#[allow(dead_code)]
237#[derive(Debug, Clone, Copy, PartialEq, Eq)]
238pub enum TemperatureUnit {
239 Kelvin,
241 Celsius,
243 Fahrenheit,
245 Rankine,
247}
248
249#[allow(dead_code)]
261pub fn convert_temperature(value: f64, from: TemperatureUnit, to: TemperatureUnit) -> f64 {
262 let kelvin = match from {
264 TemperatureUnit::Kelvin => value,
265 TemperatureUnit::Celsius => value + 273.15,
266 TemperatureUnit::Fahrenheit => (value - 32.0) * 5.0 / 9.0 + 273.15,
267 TemperatureUnit::Rankine => value * 5.0 / 9.0,
268 };
269 match to {
271 TemperatureUnit::Kelvin => kelvin,
272 TemperatureUnit::Celsius => kelvin - 273.15,
273 TemperatureUnit::Fahrenheit => (kelvin - 273.15) * 9.0 / 5.0 + 32.0,
274 TemperatureUnit::Rankine => kelvin * 9.0 / 5.0,
275 }
276}
277
278#[allow(dead_code)]
284#[derive(Debug, Clone, Copy, PartialEq, Eq)]
285pub enum PressureUnit {
286 Pascal,
288 Kilopascal,
290 Megapascal,
292 Gigapascal,
294 Bar,
296 Atmosphere,
298 Psi,
300 Torr,
302}
303
304impl PressureUnit {
305 fn to_pascals(self) -> f64 {
307 match self {
308 PressureUnit::Pascal => 1.0,
309 PressureUnit::Kilopascal => 1e3,
310 PressureUnit::Megapascal => 1e6,
311 PressureUnit::Gigapascal => 1e9,
312 PressureUnit::Bar => 1e5,
313 PressureUnit::Atmosphere => 101_325.0,
314 PressureUnit::Psi => 6_894.757_293_168_361,
315 PressureUnit::Torr => 101_325.0 / 760.0,
316 }
317 }
318}
319
320#[allow(dead_code)]
329pub fn convert_pressure(value: f64, from: PressureUnit, to: PressureUnit) -> f64 {
330 value * from.to_pascals() / to.to_pascals()
331}
332
333#[cfg(test)]
338mod tests {
339 use super::*;
340
341 #[test]
344 fn test_speed_of_light_positive() {
345 let c = PhysicalConstants::new();
346 assert!(c.c > 0.0);
347 }
348
349 #[test]
350 fn test_planck_constant_order_of_magnitude() {
351 let pc = PhysicalConstants::new();
352 assert!(pc.h > 1e-35 && pc.h < 1e-33);
353 }
354
355 #[test]
356 fn test_boltzmann_constant_order() {
357 let pc = PhysicalConstants::new();
358 assert!(pc.k_b > 1e-24 && pc.k_b < 1e-22);
359 }
360
361 #[test]
362 fn test_avogadro_order() {
363 let pc = PhysicalConstants::new();
364 assert!(pc.n_a > 6.0e23 && pc.n_a < 6.1e23);
365 }
366
367 #[test]
368 fn test_gas_constant_relation() {
369 let pc = PhysicalConstants::new();
371 let computed = pc.n_a * pc.k_b;
372 assert!((computed - pc.r).abs() / pc.r < 1e-6);
373 }
374
375 #[test]
376 fn test_gravitational_constant_order() {
377 let pc = PhysicalConstants::new();
378 assert!(pc.g > 6.6e-11 && pc.g < 6.7e-11);
379 }
380
381 #[test]
384 fn test_length_identity() {
385 assert!((convert_length(5.0, LengthUnit::Meter, LengthUnit::Meter) - 5.0).abs() < 1e-12);
386 }
387
388 #[test]
389 fn test_foot_to_inches() {
390 let inches = convert_length(1.0, LengthUnit::Foot, LengthUnit::Inch);
391 assert!((inches - 12.0).abs() < 1e-10);
392 }
393
394 #[test]
395 fn test_meter_to_centimeter() {
396 let cm = convert_length(1.0, LengthUnit::Meter, LengthUnit::Centimeter);
397 assert!((cm - 100.0).abs() < 1e-10);
398 }
399
400 #[test]
401 fn test_meter_to_millimeter() {
402 let mm = convert_length(1.0, LengthUnit::Meter, LengthUnit::Millimeter);
403 assert!((mm - 1000.0).abs() < 1e-10);
404 }
405
406 #[test]
407 fn test_nanometer_to_angstrom() {
408 let ang = convert_length(1.0, LengthUnit::Nanometer, LengthUnit::Angstrom);
409 assert!((ang - 10.0).abs() < 1e-6);
410 }
411
412 #[test]
413 fn test_length_roundtrip() {
414 let val = 42.0;
415 let cm = convert_length(val, LengthUnit::Meter, LengthUnit::Centimeter);
416 let back = convert_length(cm, LengthUnit::Centimeter, LengthUnit::Meter);
417 assert!((back - val).abs() < 1e-10);
418 }
419
420 #[test]
421 fn test_au_larger_than_ly() {
422 let aus = convert_length(1.0, LengthUnit::LightYear, LengthUnit::AstronomicalUnit);
424 assert!(aus > 60_000.0);
425 }
426
427 #[test]
428 fn test_inch_to_meter() {
429 let m = convert_length(1.0, LengthUnit::Inch, LengthUnit::Meter);
430 assert!((m - 0.0254).abs() < 1e-10);
431 }
432
433 #[test]
436 fn test_mass_identity() {
437 assert!((convert_mass(3.0, MassUnit::Kilogram, MassUnit::Kilogram) - 3.0).abs() < 1e-12);
438 }
439
440 #[test]
441 fn test_kg_to_gram() {
442 let g = convert_mass(1.0, MassUnit::Kilogram, MassUnit::Gram);
443 assert!((g - 1000.0).abs() < 1e-10);
444 }
445
446 #[test]
447 fn test_pound_to_kg() {
448 let kg = convert_mass(1.0, MassUnit::Pound, MassUnit::Kilogram);
449 assert!((kg - 0.453_592_37).abs() < 1e-8);
450 }
451
452 #[test]
453 fn test_ounce_to_gram() {
454 let g = convert_mass(1.0, MassUnit::Ounce, MassUnit::Gram);
456 assert!((g - 28.349_523_125).abs() < 1e-6);
457 }
458
459 #[test]
460 fn test_amu_dalton_equal() {
461 let a = convert_mass(1.0, MassUnit::AtomicMassUnit, MassUnit::Kilogram);
462 let b = convert_mass(1.0, MassUnit::Dalton, MassUnit::Kilogram);
463 assert!((a - b).abs() < 1e-40);
464 }
465
466 #[test]
467 fn test_mass_roundtrip() {
468 let val = 7.5;
469 let lb = convert_mass(val, MassUnit::Kilogram, MassUnit::Pound);
470 let back = convert_mass(lb, MassUnit::Pound, MassUnit::Kilogram);
471 assert!((back - val).abs() < 1e-10);
472 }
473
474 #[test]
477 fn test_energy_identity() {
478 assert!((convert_energy(2.0, EnergyUnit::Joule, EnergyUnit::Joule) - 2.0).abs() < 1e-12);
479 }
480
481 #[test]
482 fn test_kj_to_j() {
483 let j = convert_energy(1.0, EnergyUnit::Kilojoule, EnergyUnit::Joule);
484 assert!((j - 1000.0).abs() < 1e-10);
485 }
486
487 #[test]
488 fn test_calorie_to_joule() {
489 let j = convert_energy(1.0, EnergyUnit::Calorie, EnergyUnit::Joule);
490 assert!((j - 4.184).abs() < 1e-10);
491 }
492
493 #[test]
494 fn test_kcal_to_cal() {
495 let cal = convert_energy(1.0, EnergyUnit::Kilocalorie, EnergyUnit::Calorie);
496 assert!((cal - 1000.0).abs() < 1e-10);
497 }
498
499 #[test]
500 fn test_erg_to_joule() {
501 let j = convert_energy(1.0, EnergyUnit::Erg, EnergyUnit::Joule);
502 assert!((j - 1e-7).abs() < 1e-18);
503 }
504
505 #[test]
506 fn test_energy_roundtrip() {
507 let val = 100.0;
508 let ev = convert_energy(val, EnergyUnit::Joule, EnergyUnit::Electronvolt);
509 let back = convert_energy(ev, EnergyUnit::Electronvolt, EnergyUnit::Joule);
510 assert!((back - val).abs() / val < 1e-10);
511 }
512
513 #[test]
516 fn test_celsius_to_kelvin_freezing() {
517 let k = convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Kelvin);
518 assert!((k - 273.15).abs() < 1e-8);
519 }
520
521 #[test]
522 fn test_celsius_to_fahrenheit_boiling() {
523 let f = convert_temperature(100.0, TemperatureUnit::Celsius, TemperatureUnit::Fahrenheit);
524 assert!((f - 212.0).abs() < 1e-8);
525 }
526
527 #[test]
528 fn test_fahrenheit_freezing_to_celsius() {
529 let c = convert_temperature(32.0, TemperatureUnit::Fahrenheit, TemperatureUnit::Celsius);
530 assert!(c.abs() < 1e-8);
531 }
532
533 #[test]
534 fn test_rankine_to_kelvin() {
535 let k = convert_temperature(0.0, TemperatureUnit::Rankine, TemperatureUnit::Kelvin);
537 assert!(k.abs() < 1e-10);
538 }
539
540 #[test]
541 fn test_rankine_459_67_is_zero_fahrenheit() {
542 let f = convert_temperature(
544 459.67,
545 TemperatureUnit::Rankine,
546 TemperatureUnit::Fahrenheit,
547 );
548 assert!(f.abs() < 1e-6);
549 }
550
551 #[test]
552 fn test_temperature_roundtrip_k_c() {
553 let val = 500.0_f64;
554 let c = convert_temperature(val, TemperatureUnit::Kelvin, TemperatureUnit::Celsius);
555 let back = convert_temperature(c, TemperatureUnit::Celsius, TemperatureUnit::Kelvin);
556 assert!((back - val).abs() < 1e-8);
557 }
558
559 #[test]
560 fn test_temperature_identity_kelvin() {
561 let val = 300.0_f64;
562 let k = convert_temperature(val, TemperatureUnit::Kelvin, TemperatureUnit::Kelvin);
563 assert!((k - val).abs() < 1e-10);
564 }
565
566 #[test]
569 fn test_pressure_identity() {
570 assert!(
571 (convert_pressure(5.0, PressureUnit::Pascal, PressureUnit::Pascal) - 5.0).abs() < 1e-12
572 );
573 }
574
575 #[test]
576 fn test_atm_to_pascal() {
577 let pa = convert_pressure(1.0, PressureUnit::Atmosphere, PressureUnit::Pascal);
578 assert!((pa - 101_325.0).abs() < 1e-4);
579 }
580
581 #[test]
582 fn test_bar_to_kpa() {
583 let kpa = convert_pressure(1.0, PressureUnit::Bar, PressureUnit::Kilopascal);
584 assert!((kpa - 100.0).abs() < 1e-8);
585 }
586
587 #[test]
588 fn test_torr_to_pa() {
589 let pa = convert_pressure(760.0, PressureUnit::Torr, PressureUnit::Pascal);
590 assert!((pa - 101_325.0).abs() < 1e-4);
591 }
592
593 #[test]
594 fn test_psi_to_pa() {
595 let pa = convert_pressure(1.0, PressureUnit::Psi, PressureUnit::Pascal);
596 assert!((pa - 6_894.757_293_168_361).abs() < 1e-4);
597 }
598
599 #[test]
600 fn test_pressure_roundtrip() {
601 let val = 3.5;
602 let atm = convert_pressure(val, PressureUnit::Megapascal, PressureUnit::Atmosphere);
603 let back = convert_pressure(atm, PressureUnit::Atmosphere, PressureUnit::Megapascal);
604 assert!((back - val).abs() / val < 1e-10);
605 }
606
607 #[test]
608 fn test_gpa_to_mpa() {
609 let mpa = convert_pressure(1.0, PressureUnit::Gigapascal, PressureUnit::Megapascal);
610 assert!((mpa - 1000.0).abs() < 1e-8);
611 }
612}