1use super::dim::*;
23use super::qty::Qty;
24use super::si::*;
25use typenum::{N1, N2, P1, P2, P3, Z0};
26
27pub fn speed_of_light(ctx: &crate::api::context::Context) -> Velocity {
29 Velocity::from_ex(ctx.physical_constant("c", ctx.int(299_792_458)))
30}
31
32pub fn standard_gravity(ctx: &crate::api::context::Context) -> Acceleration {
34 Acceleration::from_ex(ctx.physical_constant("g_0", ctx.rational(980665, 100_000)))
35}
36
37pub fn elementary_charge(ctx: &crate::api::context::Context) -> Charge {
39 Charge::from_ex(ctx.physical_constant("e_0", &ctx.int(1_602_176_634) / &ctx.int(10).powi(28)))
40}
41
42pub fn planck_constant(ctx: &crate::api::context::Context) -> AngularMomentum {
46 AngularMomentum::from_ex(
47 ctx.physical_constant("h", &ctx.int(662_607_015) / &ctx.int(10).powi(42)),
48 )
49}
50
51pub fn reduced_planck_constant(ctx: &crate::api::context::Context) -> AngularMomentum {
56 let h = ctx.physical_constant("h", &ctx.int(662_607_015) / &ctx.int(10).powi(42));
57 let two_pi = &(ctx.int(2) * &ctx.pi());
58 AngularMomentum::from_ex(ctx.physical_constant("hbar", &h / two_pi))
59}
60
61pub fn boltzmann_constant(
66 ctx: &crate::api::context::Context,
67) -> Qty<Dim<P2, P1, N2, Z0, N1, Z0, Z0>> {
68 Qty::from_ex(ctx.physical_constant("k_B", &ctx.int(1_380_649) / &ctx.int(10).powi(29)))
69}
70
71pub fn avogadro_constant(
76 ctx: &crate::api::context::Context,
77) -> Qty<Dim<Z0, Z0, Z0, Z0, Z0, N1, Z0>> {
78 let val = &ctx.int(602_214_076) * &ctx.int(10).powi(15);
80 Qty::from_ex(ctx.physical_constant("N_A", val))
81}
82
83pub fn gravitational_constant(
89 ctx: &crate::api::context::Context,
90) -> Qty<Dim<P3, N1, N2, Z0, Z0, Z0, Z0>> {
91 Qty::from_ex(ctx.physical_constant("G", &ctx.int(667_430) / &ctx.int(10).powi(16)))
92}
93
94pub fn physical_constants_dimmap() -> super::inference::DimMap {
99 use super::inference::DimMap;
100 DimMap::new()
101 .with("c", ConstDim::VELOCITY)
102 .with("g_0", ConstDim::ACCELERATION)
103 .with("e_0", ConstDim::CHARGE)
104 .with("h", ConstDim::ANGULAR_MOMENTUM)
105 .with("hbar", ConstDim::ANGULAR_MOMENTUM)
106 .with("k_B", ConstDim::new(2, 1, -2, 0, -1, 0, 0))
107 .with("N_A", ConstDim::new(0, 0, 0, 0, 0, -1, 0))
108 .with("G", ConstDim::new(3, -1, -2, 0, 0, 0, 0))
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn speed_of_light_is_velocity() {
117 let ctx = crate::api::context::Context::new();
118 let c = speed_of_light(&ctx);
119 assert!(
120 format!("{}", c.inner()).contains("c"),
121 "should display as c"
122 );
123 }
124
125 #[test]
126 fn speed_of_light_eval_f64() {
127 let ctx = crate::api::context::Context::new();
128 let c = speed_of_light(&ctx);
129 let val = c.eval_f64().unwrap();
130 assert!((val - 299_792_458.0).abs() < 1.0, "c = {val}");
131 }
132
133 #[test]
134 fn elementary_charge_eval() {
135 let ctx = crate::api::context::Context::new();
136 let e = elementary_charge(&ctx);
137 let val = e.eval_f64().unwrap();
138 assert!(
139 (val - 1.602176634e-19).abs() / 1.602176634e-19 < 1e-10,
140 "e = {val}"
141 );
142 }
143
144 #[test]
145 fn planck_constant_eval() {
146 let ctx = crate::api::context::Context::new();
147 let h = planck_constant(&ctx);
148 let val = h.eval_f64().unwrap();
149 assert!(
150 (val - 6.62607015e-34).abs() / 6.62607015e-34 < 1e-10,
151 "h = {val}"
152 );
153 }
154
155 #[test]
156 fn boltzmann_eval() {
157 let ctx = crate::api::context::Context::new();
158 let kb = boltzmann_constant(&ctx);
159 let val = kb.eval_f64().unwrap();
160 assert!(
161 (val - 1.380649e-23).abs() / 1.380649e-23 < 1e-10,
162 "k_B = {val}"
163 );
164 }
165
166 #[test]
167 fn standard_gravity_eval() {
168 let ctx = crate::api::context::Context::new();
169 let g = standard_gravity(&ctx);
170 let val = g.eval_f64().unwrap();
171 assert!((val - 9.80665).abs() < 1e-10, "g = {val}");
172 }
173
174 #[test]
175 fn e_equals_mc_squared() {
176 let ctx = crate::api::context::Context::new();
177 crate::syms!(ctx; m);
178 let c = speed_of_light(&ctx);
179 let mass = Mass::symbol(&ctx, "m");
180 let energy = Energy::from_ex(mass.inner() * c.inner() * c.inner());
182 let display = format!("{}", energy.inner());
184 assert!(
185 display.contains("c"),
186 "E=mc² should display symbolically: {display}"
187 );
188 let val = energy.subs(&m, &ctx.int(1)).eval_f64().unwrap();
190 let expected = 299_792_458.0_f64 * 299_792_458.0;
191 assert!(
192 (val - expected).abs() / expected < 1e-10,
193 "E(m=1) = {val}, expected {expected}"
194 );
195 }
196
197 #[test]
198 fn constant_diff_is_zero() {
199 let ctx = crate::api::context::Context::new();
200 crate::syms!(ctx; x);
201 let c = speed_of_light(&ctx);
202 let dc_dx = c.inner().diff(&x);
203 assert!(
204 dc_dx.is_zero().unwrap_or(false) || format!("{}", dc_dx) == "0",
205 "d/dx(c) should be 0, got {dc_dx}"
206 );
207 }
208
209 #[test]
210 fn constant_in_product_diff() {
211 let ctx = crate::api::context::Context::new();
212 crate::syms!(ctx; x);
213 let c = speed_of_light(&ctx);
214 let cx = c.inner() * &x;
215 let d = cx.diff(&x);
216 let display = format!("{}", d);
218 assert!(
219 display.contains("c"),
220 "d/dx(c*x) should contain c: {display}"
221 );
222 }
223
224 #[test]
225 fn constant_survives_simplify() {
226 let ctx = crate::api::context::Context::new();
227 crate::syms!(ctx; x, y);
228 let c = speed_of_light(&ctx);
229 let expr = c.inner() * &x + c.inner() * &y;
230 let simplified = expr.simplify();
231 let display = format!("{}", simplified);
232 assert!(
233 display.contains("c"),
234 "simplify should preserve c: {display}"
235 );
236 }
237
238 #[test]
239 fn physical_constants_dimmap_works() {
240 let ctx = crate::api::context::Context::new();
241 crate::syms!(ctx; m);
242 let dims = physical_constants_dimmap().with("m", ConstDim::MASS);
243 let c = speed_of_light(&ctx);
244 let mc2 = &(m.clone() * c.inner()) * c.inner();
245 let dim = crate::units::inference::infer_dimension(&mc2, &dims);
246 assert!(dim.is_ok(), "should infer dimension of mc²: {:?}", dim);
247 assert!(
248 dim.unwrap().eq(ConstDim::ENERGY),
249 "mc² should have Energy dimension"
250 );
251 }
252}