Skip to main content

zmatrix/physics/
basic.rs

1//! 物理量系统:25 种物理量及其量纲安全运算。
2//!
3//! # 物理量与裸 `f64` 的运算
4//!
5//! `X + f64`、`X - f64`、`X * f64`、`X / f64` 这类实现,语义都是:
6//! **裸数直接作用于物理量的内部存储值,按其当前单位理解,不做任何单位换算。**
7//!
8//! 这是**有意设计**,不是缺陷。`f64` 自身不带单位,库无从判断它代表 1 m 还是 1 km;
9//! 若擅自按基准单位换算,等于替调用方做了一个它并未表达的假设。
10//!
11//! ```
12//! use zmatrix::physics::basic::Distance;
13//!
14//! // 由 from_km 构造:内部单位是 km,+ 1.0 即在 km 上加 1
15//! let a = Distance::from_km(1.0) + 1.0;
16//! // 由 from_m 构造:内部单位是 m,+ 1.0 即在 m 上加 1
17//! let b = Distance::from_m(1000.0) + 1.0;
18//!
19//! assert_eq!(a.as_km(), 2.0);
20//! assert_eq!(b.as_m(), 1001.0);
21//! ```
22//!
23//! 注意 `a` 与 `b` 在加 `1.0` **之前**是相等的(都是 1000 m),加完之后不等。
24//! 这就是"裸数与物理量混算"必须付出的代价 —— 单位信息在运算符里丢失了。
25//!
26//! **需要单位安全时,请让两个物理量直接运算**,把单位写进类型:
27//!
28//! ```
29//! use zmatrix::physics::basic::Distance;
30//!
31//! let a = Distance::from_km(1.0);
32//! let b = Distance::from_m(1000.0);
33//! let c = a + b;                  // 由编译器统一换算到 m,再相加
34//!
35//! assert_eq!(c.as_m(), 2000.0);
36//! ```
37//!
38//! 判定口径:只要参与运算的两侧有一侧是裸数,就没有单位保证;
39//! 两侧都是物理量时,单位由类型系统负责。
40
41use std::any::Any;
42use std::fmt::{Display, Formatter};
43use std::ops::Div;
44
45mod acceleration;
46mod angular;
47mod angular_acceleration;
48mod angular_momentum;
49mod angular_velocity;
50mod area;
51mod coef;
52mod distance;
53mod electric_capacitance;
54mod electric_charge;
55mod electric_conductance;
56mod electric_current;
57mod electric_potential;
58mod electric_resistance;
59mod energy;
60mod force;
61pub mod heat_capacity;
62pub mod heat_flux;
63pub mod inertia;
64mod magnetic_angular_velocity;
65mod magnetic_induction;
66mod magnetic_moment;
67pub mod mass;
68mod momentum;
69mod power;
70mod temperature;
71mod temperature_difference;
72pub mod thermal_conductance;
73mod thermal_conductivity;
74mod torque;
75pub mod vector;
76mod velocity;
77mod volume;
78
79//支持向量的物理量
80pub trait VectorQuantity: PhysicalQuantity {}
81
82pub trait PhysicalQuantity: Any {
83    fn as_any(&self) -> &dyn Any;
84
85    fn is_zero(&self) -> bool;
86
87    fn default_unit_value(&self) -> f64;
88
89    fn set_value(&mut self, value: f64);
90
91    /// 返回物理量名称和单位,如 "Distance (m)", "Velocity (m/s)"
92    fn unit_display_name(&self) -> String;
93}
94
95#[derive(Clone, Copy, Debug, PartialEq, Eq)]
96pub enum DistanceType {
97    M,
98    KM,
99    LightYear,
100}
101#[derive(Clone, Copy, Debug, PartialEq)]
102pub enum VelocityType {
103    MPerSecond,
104    KmPerHour,
105    KmPerSecond,
106    LightSpeed,
107}
108/// 距离物理量结构体
109///
110/// 表示空间中的距离,支持多种单位制
111///
112/// # 示例
113/// ```
114/// use zmatrix::physics::basic::Distance;
115///
116/// let distance = Distance::from_m(100.0);
117/// println!("距离: {} 米", distance.as_m());
118/// ```
119#[derive(Clone, Debug, PartialEq, Copy)]
120pub struct Distance {
121    default_type: DistanceType,
122    v: f64,
123}
124
125/// 速度物理量结构体
126///
127/// 表示物体运动的快慢和方向,支持多种单位制
128///
129/// # 示例
130/// ```
131/// use zmatrix::physics::basic::Velocity;
132///
133/// let velocity = Velocity::from_m_per_sec(10.0);
134/// println!("速度: {} m/s", velocity.as_m_per_sec());
135/// ```
136#[derive(Clone, Debug, PartialEq, Copy)]
137pub struct Velocity {
138    default_type: VelocityType,
139    v: f64,
140}
141
142
143/// 加速度物理量结构体
144///
145/// 表示速度变化的快慢,支持多种单位制
146///
147/// # 示例
148/// ```
149/// use zmatrix::physics::basic::Acceleration;
150///
151/// let acceleration = Acceleration::from_g(1.0);
152/// println!("加速度: {} g", acceleration.as_g());
153/// ```
154#[derive(Clone, Debug, PartialEq, Copy)]
155pub struct Acceleration {
156    default_type: AccelerationType,
157    v: f64,
158}
159
160#[derive(Clone, Debug, PartialEq, Copy)]
161pub enum AccelerationType {
162    MPerSecond2,
163    KmPerHour2,
164    G,
165}
166
167#[derive(Clone, Debug, PartialEq, Copy)]
168pub enum AngularType {
169    Rad,
170    Deg,
171}
172
173/// 角度物理量结构体
174///
175/// 表示角度大小,支持弧度和度数单位
176///
177/// # 示例
178/// ```
179/// use zmatrix::physics::basic::Angular;
180///
181/// let angle = Angular::from_rad(std::f64::consts::PI);
182/// println!("角度: {} 弧度", angle.as_rad());
183/// ```
184#[derive(Clone, Debug, PartialEq, Copy)]
185pub struct Angular {
186    default_type: AngularType,
187    v: f64,
188}
189
190#[derive(Clone, Debug, PartialEq, Copy, Default)]
191pub enum AngularVelocityType {
192    #[default]
193    RadperSecond,
194    DegPerSecond,
195    RadperHour,
196    DegperHour,
197}
198
199/// 角速度物理量结构体
200///
201/// 表示角度变化的快慢,支持多种单位制
202///
203/// # 示例
204/// ```
205/// use zmatrix::physics::basic::AngularVelocity;
206///
207/// let angular_velocity = AngularVelocity::from_rad_per_second(1.0);
208/// println!("角速度: {} rad/s", angular_velocity.as_rad_per_second());
209/// ```
210#[derive(Clone, Debug, PartialEq, Copy)]
211pub struct AngularVelocity {
212    default_type: AngularVelocityType,
213    v: f64,
214}
215
216
217#[derive(Clone, Debug, PartialEq, Copy)]
218pub enum AngularAccelerationType {
219    RadperSecond2,
220    DegPerSecond2,
221}
222
223/// 角加速度物理量结构体
224///
225/// 表示角速度变化的快慢,支持多种单位制
226///
227/// # 示例
228/// ```
229/// use zmatrix::physics::basic::AngularAcceleration;
230///
231/// let angular_acceleration = AngularAcceleration::from_rad_per_second2(1.0);
232/// println!("角加速度: {} rad/s²", angular_acceleration.as_rad_per_second2());
233/// ```
234#[derive(Clone, Debug, PartialEq, Copy)]
235pub struct AngularAcceleration {
236    default_type: AngularAccelerationType,
237    v: f64,
238}
239
240/// 系数物理量结构体
241///
242/// 表示无量纲的系数或比例因子
243///
244/// # 示例
245/// ```
246/// use zmatrix::physics::basic::Coef;
247///
248/// let coefficient = Coef::new(0.5);
249/// println!("系数: {}", coefficient.get_value());
250/// ```
251#[derive(Clone, Debug, PartialEq, PartialOrd, Copy)]
252pub struct Coef {
253    v: f64,
254}
255
256#[derive(Clone, Debug, PartialEq, Copy)]
257pub enum MassType {
258    Kg,
259    g,
260}
261
262/// 质量物理量结构体
263///
264/// 表示物体的质量,支持千克和克单位
265///
266/// # 示例
267/// ```
268/// use zmatrix::physics::basic::Mass;
269///
270/// let mass = Mass::from_kg(1.0);
271/// println!("质量: {} kg", mass.as_kg());
272/// ```
273#[derive(Clone, Debug, PartialEq, Copy)]
274pub struct Mass {
275    default_type: MassType,
276    pub v: f64,
277}
278
279#[derive(Clone, Debug, PartialEq, Copy)]
280pub enum InertiaType {
281    /// 千克·平方米(SI 基本单位)
282    KgM2,
283    /// 千克·平方毫米(工程图纸与 CAD 常用,也是 aods 的 J_kgmm2 单位)
284    KgMm2,
285}
286
287/// 转动惯量(标量)
288///
289/// 描述刚体绕**某一指定轴**的转动惯性,量纲 kg·m²。
290/// 这是标量形态:飞轮转子惯量、单轴转动惯量都属于它。
291///
292/// 完整描述刚体在任意轴向上的转动惯性需要 3×3 的转动惯量张量,
293/// 标量是该张量沿某条主轴的投影。区分两者很重要:只有标量值时,
294/// 姿态动力学只能按"惯量各向同性"处理。
295///
296/// 与 [`AngularMomentum`] / [`AngularVelocity`] 的关系:
297/// `L = J·ω`,故 `AngularMomentum / AngularVelocity = Inertia`。
298///
299/// # 示例
300/// ```
301/// use zmatrix::physics::basic::Inertia;
302///
303/// let j = Inertia::from_kg_m2(0.5);
304/// println!("转动惯量: {} kg·m²", j.as_kg_m2());
305///
306/// // 工程图纸给的 kg·mm² 需除以 10^6
307/// let j2 = Inertia::from_kg_mm2(500_000.0);
308/// assert_eq!(j2.as_kg_m2(), 0.5);
309/// ```
310#[derive(Clone, Debug, PartialEq, Copy)]
311pub struct Inertia {
312    default_type: InertiaType,
313    pub v: f64,
314}
315
316#[derive(Clone, Debug, PartialEq, Copy)]
317pub enum AngularMomentumType {
318    KgM2perSecond,  // 每秒1千克1平方米
319    KgKm2perSecond, // 每秒1千克1平方公里
320    Nms,            // 牛顿·米·秒 (N·m·s)
321    MillNms,        // 毫牛顿·米·秒 (mN·m·s)
322    MicroNms,       // 微牛顿·米·秒 (μN·m·s)
323    NanoNms,        // 纳牛顿·米·秒 (nN·m·s)
324}
325/// 角动量物理量结构体
326///
327/// 表示物体绕轴旋转的动量,是质量、速度和距离的乘积
328///
329/// # 示例
330/// ```
331/// use zmatrix::physics::basic::AngularMomentum;
332///
333/// let angular_momentum = AngularMomentum::from_kg_m2_per_second(1.0);
334/// println!("角动量: {} kg·m²/s", angular_momentum.as_kg_m2_per_second());
335/// ```
336#[derive(Clone, Debug, PartialEq, Copy)]
337pub struct AngularMomentum {
338    default_type: AngularMomentumType,
339    pub v: f64,
340}
341
342#[derive(Clone, Debug, PartialEq, Copy)]
343pub enum MomentumType {
344    KgMperSecond,  // 每秒1千克1米
345    KgKmperSecond, // 每秒1千克1千米
346}
347/// 动量物理量结构体
348///
349/// 表示物体运动的动量,是质量和速度的乘积
350///
351/// # 示例
352/// ```
353/// use zmatrix::physics::basic::Momentum;
354///
355/// let momentum = Momentum::from_kg_m_s(1.0);
356/// println!("动量: {} kg·m/s", momentum.as_kg_m_s());
357/// ```
358#[derive(Clone, Debug, PartialEq, Copy)]
359pub struct Momentum {
360    default_type: MomentumType,
361    pub v: f64,
362}
363
364
365/// 三维向量结构体
366///
367/// 表示三维空间中的向量,包含x、y、z三个分量
368///
369/// # 类型参数
370/// * `T` - 向量分量的物理量类型,必须实现 `VectorQuantity` 和 `Default` trait
371///
372/// # 示例
373/// ```
374/// use zmatrix::physics::basic::{Vector3, Distance};
375///
376/// let position = Vector3 {
377///     x: Distance::from_m(1.0),
378///     y: Distance::from_m(2.0),
379///     z: Distance::from_m(3.0),
380/// };
381/// println!("位置: ({}, {}, {})", position.x.as_m(), position.y.as_m(), position.z.as_m());
382/// ```
383#[derive(Clone, Debug, PartialEq, Copy)]
384pub struct Vector3<T: VectorQuantity + Default> {
385    pub x: T,
386    pub y: T,
387    pub z: T,
388}
389
390impl<T: VectorQuantity + Default + Display> Display for Vector3<T> {
391    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
392        write!(f, "({}, {}, {})", self.x, self.y, self.z)
393    }
394}
395
396#[derive(Clone, Copy, Debug, PartialEq)]
397pub enum AreaType {
398    M2,
399    KM2,
400}
401
402/// 面积物理量结构体
403///
404/// 表示二维空间的面积大小,支持平方米和平方千米单位
405///
406/// # 示例
407/// ```
408/// use zmatrix::physics::basic::Area;
409///
410/// let area = Area::from_m2(100.0);
411/// println!("面积: {} m²", area.as_m2());
412/// ```
413#[derive(Clone, Copy, Debug, PartialEq)]
414pub struct Area {
415    default_type: AreaType,
416    pub v: f64,
417}
418
419#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
420pub enum MagneticInductionType {
421    Gauss,
422    #[default]
423    Tesla,
424    MillTesla,  // 毫
425    MicroTesla, // 微
426    NanoTesla,  //; 纳
427    MillGauss,  // 毫
428    KiloGauss,  // 千
429}
430/// 磁感应强度物理量结构体
431///
432/// 表示磁场强度,单位是特斯拉或高斯
433///
434/// # 示例
435/// ```
436/// use zmatrix::physics::basic::MagneticInduction;
437///
438/// let magnetic_field = MagneticInduction::from_tesla(1.0);
439/// println!("磁感应强度: {} T", magnetic_field.as_tesla());
440/// ```
441#[derive(Clone, Copy, Debug, PartialEq)]
442pub struct MagneticInduction {
443    default_type: MagneticInductionType,
444    pub v: f64,
445}
446
447
448#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
449pub enum MagneticMomentType {
450    AM2,            // 安培·平方米 (A·m²)
451    MillAM2,        // 毫安培·平方米 (mA·m²)
452    MicroAM2,       // 微安培·平方米 (μA·m²)
453    NanoAM2,        // 纳安培·平方米 (nA·m²)
454    JPerTesla,      // 焦耳/特斯拉 (J/T)
455    MillJPerTesla,  // 毫焦耳/特斯拉 (mJ/T)
456    MicroJPerTesla, // 微焦耳/特斯拉 (μJ/T)
457    NanoJPerTesla,  // 纳焦耳/特斯拉 (nJ/T)
458}
459
460/// 磁矩物理量结构体
461///
462/// 表示磁偶极子的磁矩,单位是安培·平方米或焦耳/特斯拉
463///
464/// # 示例
465/// ```
466/// use zmatrix::physics::basic::MagneticMoment;
467///
468/// let magnetic_moment = MagneticMoment::from_am2(1.0);
469/// println!("磁矩: {} A·m²", magnetic_moment.as_am2());
470/// ```
471#[derive(Clone, Copy, Debug, PartialEq)]
472pub struct MagneticMoment {
473    default_type: MagneticMomentType,
474    pub v: f64,
475}
476
477
478#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
479pub enum TorqueType {
480    NM,      // 牛顿·米 (N·m)
481    MillNM,  // 毫牛顿·米 (mN·m)
482    MicroNM, // 微牛顿·米 (μN·m)
483    NanoNM,  // 纳牛顿·米 (nN·m)
484    KNM,     // 千牛顿·米 (kN·m)
485    MNM,     // 兆牛顿·米 (MN·m)
486}
487
488/// 力矩物理量结构体
489///
490/// 表示力对物体产生的转动效应,单位是牛顿·米
491///
492/// # 示例
493/// ```
494/// use zmatrix::physics::basic::Torque;
495///
496/// let torque = Torque::from_nm(10.0);
497/// println!("力矩: {} N·m", torque.as_nm());
498/// ```
499#[derive(Clone, Copy, Debug, PartialEq)]
500pub struct Torque {
501    default_type: TorqueType,
502    pub v: f64,
503}
504
505
506#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
507pub enum EnergyType {
508    Joule,            // 焦耳 (J)
509    MillJoule,        // 毫焦耳 (mJ)
510    MicroJoule,       // 微焦耳 (μJ)
511    NanoJoule,        // 纳焦耳 (nJ)
512    KiloJoule,        // 千焦耳 (kJ)
513    MegaJoule,        // 兆焦耳 (MJ)
514    ElectronVolt,     // 电子伏特 (eV)
515    KiloElectronVolt, // 千电子伏特 (keV)
516    MegaElectronVolt, // 兆电子伏特 (MeV)
517}
518
519/// 能量物理量结构体
520///
521/// 表示物体做功的能力,单位是焦耳
522///
523/// # 示例
524/// ```
525/// use zmatrix::physics::basic::Energy;
526///
527/// let energy = Energy::from_joule(100.0);
528/// println!("能量: {} J", energy.as_joule());
529/// ```
530#[derive(Clone, Copy, Debug, PartialEq)]
531pub struct Energy {
532    default_type: EnergyType,
533    pub v: f64,
534}
535
536
537#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
538pub enum ForceType {
539    Newton,      // 牛顿 (N)
540    MillNewton,  // 毫牛顿 (mN)
541    MicroNewton, // 微牛顿 (μN)
542    NanoNewton,  // 纳牛顿 (nN)
543    KiloNewton,  // 千牛顿 (kN)
544    MegaNewton,  // 兆牛顿 (MN)
545}
546
547/// 力物理量结构体
548///
549/// 表示物体间的相互作用,单位是牛顿
550///
551/// # 示例
552/// ```
553/// use zmatrix::physics::basic::Force;
554///
555/// let force = Force::from_newton(10.0);
556/// println!("力: {} N", force.as_newton());
557/// ```
558#[derive(Clone, Copy, Debug, PartialEq)]
559pub struct Force {
560    default_type: ForceType,
561    pub v: f64,
562}
563
564
565#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
566pub enum PowerType {
567    Watt,       // 瓦特 (W)
568    MillWatt,   // 毫瓦特 (mW)
569    MicroWatt,  // 微瓦特 (μW)
570    NanoWatt,   // 纳瓦特 (nW)
571    KiloWatt,   // 千瓦特 (kW)
572    MegaWatt,   // 兆瓦特 (MW)
573    HorsePower, // 马力 (hp)
574}
575
576/// 功率物理量结构体
577///
578/// 表示单位时间内做功的多少,单位是瓦特
579///
580/// # 示例
581/// ```
582/// use zmatrix::physics::basic::Power;
583///
584/// let power = Power::from_watt(100.0);
585/// println!("功率: {} W", power.as_watt());
586/// ```
587#[derive(Clone, Copy, Debug, PartialEq)]
588pub struct Power {
589    default_type: PowerType,
590    pub v: f64,
591}
592
593
594#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
595pub enum MagneticAngularVelocityType {
596    TeslaRadPerSecond,      // 特斯拉·弧度/秒 (T·rad/s)
597    MillTeslaRadPerSecond,  // 毫特斯拉·弧度/秒 (mT·rad/s)
598    MicroTeslaRadPerSecond, // 微特斯拉·弧度/秒 (μT·rad/s)
599    NanoTeslaRadPerSecond,  // 纳特斯拉·弧度/秒 (nT·rad/s)
600    GaussRadPerSecond,      // 高斯·弧度/秒 (G·rad/s)
601    MillGaussRadPerSecond,  // 毫高斯·弧度/秒 (mG·rad/s)
602    KiloGaussRadPerSecond,  // 千高斯·弧度/秒 (kG·rad/s)
603}
604
605/// 磁角速度物理量结构体
606///
607/// 表示磁场中的角速度,单位是特斯拉·弧度/秒或高斯·弧度/秒
608///
609/// # 示例
610/// ```
611/// use zmatrix::physics::basic::MagneticAngularVelocity;
612///
613/// let magnetic_angular_velocity = MagneticAngularVelocity::from_tesla_rad_per_second(1.0);
614/// println!("磁角速度: {} T·rad/s", magnetic_angular_velocity.as_tesla_rad_per_second());
615/// ```
616#[derive(Clone, Copy, Debug, PartialEq)]
617pub struct MagneticAngularVelocity {
618    default_type: MagneticAngularVelocityType,
619    pub v: f64,
620}
621
622
623#[derive(Clone, Debug, PartialEq, Copy)]
624pub enum VolumeType {
625    M3,
626    KM3,
627}
628/// 体积物理量结构体
629///
630/// 表示三维空间的体积大小,支持立方米和立方千米单位
631///
632/// # 示例
633/// ```
634/// use zmatrix::physics::basic::Volume;
635///
636/// let volume = Volume::from_m3(1000.0);
637/// println!("体积: {} m³", volume.as_m3());
638/// ```
639#[derive(Clone, Copy, Debug, PartialEq)]
640pub struct Volume {
641    default_type: VolumeType,
642    pub v: f64,
643}
644
645// ── 电学物理量 ───────────────────────────────────────────────────────────────
646
647#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
648pub enum ElectricCurrentType {
649    A,
650    MilliA,
651    MicroA,
652    KiloA,
653}
654
655/// 电流物理量结构体
656///
657/// 表示电荷流动的速率,单位是安培 (A)
658#[derive(Clone, Copy, Debug, PartialEq)]
659pub struct ElectricCurrent {
660    default_type: ElectricCurrentType,
661    pub v: f64,
662}
663
664#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
665pub enum ElectricPotentialType {
666    V,
667    MilliV,
668    MicroV,
669    KiloV,
670}
671
672/// 电压(电势差)物理量结构体
673///
674/// 表示两点间的电势差,单位是伏特 (V)
675#[derive(Clone, Copy, Debug, PartialEq)]
676pub struct ElectricPotential {
677    default_type: ElectricPotentialType,
678    pub v: f64,
679}
680
681#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
682pub enum ElectricResistanceType {
683    Ohm,
684    MillOhm,
685    KiloOhm,
686    MegaOhm,
687}
688
689/// 电阻物理量结构体
690///
691/// 表示导体对电流的阻碍能力,单位是欧姆 (Ω)
692#[derive(Clone, Copy, Debug, PartialEq)]
693pub struct ElectricResistance {
694    default_type: ElectricResistanceType,
695    pub v: f64,
696}
697
698#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
699pub enum ElectricChargeType {
700    Coulomb,
701    MilliCoulomb,
702    MicroCoulomb,
703    NanoCoulomb,
704}
705
706/// 电荷量物理量结构体
707///
708/// 表示物体所带的电荷量,单位是库仑 (C)
709#[derive(Clone, Copy, Debug, PartialEq)]
710pub struct ElectricCharge {
711    default_type: ElectricChargeType,
712    pub v: f64,
713}
714
715#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
716pub enum ElectricCapacitanceType {
717    Farad,
718    MilliFarad,
719    MicroFarad,
720    NanoFarad,
721    PicoFarad,
722}
723
724/// 电容物理量结构体
725///
726/// 表示存储电荷的能力,单位是法拉 (F)
727#[derive(Clone, Copy, Debug, PartialEq)]
728pub struct ElectricCapacitance {
729    default_type: ElectricCapacitanceType,
730    pub v: f64,
731}
732
733#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
734pub enum ElectricConductanceType {
735    Siemens,
736    MilliSiemens,
737    MicroSiemens,
738}
739
740/// 电导物理量结构体
741///
742/// 表示导体导电的能力(电阻的倒数),单位是西门子 (S)
743#[derive(Clone, Copy, Debug, PartialEq)]
744pub struct ElectricConductance {
745    default_type: ElectricConductanceType,
746    pub v: f64,
747}
748
749/// 温度(绝对温度)
750///
751/// 内部统一以开尔文存储,不保留构造时使用的单位。这是与库内其他物理量的
752/// 一处**有意差异**:摄氏/华氏与开尔文之间是带偏移的仿射换算
753/// (`T_K = T_C + 273.15`),若沿用"原始单位下的值 + 单位标记"的范式,
754/// 每个 `as_*` 都要重复一遍偏移逻辑,极易漏。统一存 K 后,偏移只在构造
755/// 和取值两处出现,内部运算不需要 `match`。
756///
757/// # 允许的运算
758///
759/// | 运算 | 结果 | 含义 |
760/// |---|---|---|
761/// | `T - T` | `TemperatureDifference` | 求温差,唯一有意义的减法 |
762/// | `T + ΔT` | `Temperature` | 加热 |
763/// | `T - ΔT` | `Temperature` | 冷却 |
764/// | `T / T` | `Coef` | 温度比,如 Carnot 效率 `1 - Tc/Th` |
765///
766/// # 刻意不实现的运算
767///
768/// - `T + T`:两个位置温度之和不是温度,物理上无意义。
769/// - `T * f64`:从摄氏构造时会产生严重违背直觉的结果 ——
770///   `20°C` 内部是 `293.15 K`,乘 2 得 `586.3 K` 即 `313.15°C`,
771///   而不是直觉上的 `40°C`。真需要时请显式写
772///   `Temperature::from_kelvin(t.as_kelvin() * 2.0)`。
773/// - `T * T`:输出应是 K²,库中没有也不应有这个量纲。
774///
775/// # 注意
776///
777/// 温度是标量量,**刻意不实现 [`VectorQuantity`]**,因此 `Vector3<Temperature>`
778/// 在编译期即不可构造。空间温度梯度 ∇T 虽然是向量,但单位是 K/m,
779/// 其元素类型不是温度本身。
780///
781/// # 示例
782/// ```
783/// use zmatrix::physics::basic::Temperature;
784///
785/// let t = Temperature::from_celsius(20.0);
786/// assert!((t.as_kelvin() - 293.15).abs() < 1e-9);
787/// assert!((t.as_celsius() - 20.0).abs() < 1e-9);
788///
789/// // 与华氏互转
790/// let f = Temperature::from_fahrenheit(212.0);
791/// assert!((f.as_celsius() - 100.0).abs() < 1e-9);
792/// ```
793#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
794pub struct Temperature {
795    /// 绝对温度,单位 K。私有字段:外部只能通过 `from_*` / `as_*` 访问,
796    /// 保证偏移换算不会绕过。
797    kelvin: f64,
798}
799
800/// 温度差(相对温度)
801///
802/// 与 [`Temperature`] 的唯一区别是**零点**:温度有偏移,温差没有。
803///
804/// - `Δ°C` 与 `ΔK` 数值**完全相同**(换算系数 1、偏移 0)
805/// - `Δ°F` 与 `ΔK` 相差 `9/5`
806///
807/// # 为什么必须是独立类型
808///
809/// 若只保留一个温度类型,`G * T`(热导 × 温度)与 `G * ΔT`(热导 × 温差)
810/// 在量纲上**都成立**(`W/K × K = W`),但数值可相差几十倍:
811///
812/// ```
813/// # use zmatrix::physics::basic::{Temperature, TemperatureDifference};
814/// let t1 = Temperature::from_celsius(30.0);   // 303.15 K
815/// let t2 = Temperature::from_celsius(20.0);   // 293.15 K
816///
817/// // 正确:温差 10 K
818/// let dt: TemperatureDifference = t1 - t2;
819/// assert!((dt.as_kelvin() - 10.0).abs() < 1e-9);
820///
821/// // 若误把绝对温度当温差用,数值会是 303.15 而非 10,差 30 倍。
822/// // 量纲检查对此无能为力 —— 只有类型系统能挡。
823/// ```
824///
825/// 同样**刻意不实现 [`VectorQuantity`]**:梯度 ∇T 的单位是 K/m,不是 K。
826#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
827pub struct TemperatureDifference {
828    /// 温度差,单位 K。私有字段,理由同上。
829    kelvin: f64,
830}
831
832#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
833pub enum HeatFluxType {
834    /// 瓦特每平方米(SI 导出单位)
835    WattPerSquareMeter,
836    /// 千瓦每平方米
837    KiloWattPerSquareMeter,
838    /// 毫瓦每平方厘米。文献与涂层数据手册常用;
839    /// `1 mW/cm² = 10 W/m²`(注意不是 1e-3 的关系,分母是 cm² 不是 m²)
840    MilliWattPerSquareCentiMeter,
841}
842
843/// 热流密度
844///
845/// **单位面积上的热功率**,量纲 W/m²。中文也叫「热通量」,但严格说 *flux*
846/// 指通过某个面的总流量(W),*flux density* 才是每平方米的值(W/m²);
847/// 工程上 `heat flux` 几乎一律指后者,本类型采用这一约定。
848///
849/// # 与热量、热功率的区别
850///
851/// | 量 | 单位 | 含义 | 本库类型 |
852/// |---|---|---|---|
853/// | 热量 Q | J | 累计能量 | [`Energy`] |
854/// | 热功率 Q̇ | W | 单位时间的能量 | [`Power`] |
855/// | 热流密度 q | W/m² | 单位时间、单位面积 | `HeatFlux` |
856///
857/// 密度与总量无关:两块各 1 m² 的板共承受 2000 W 热功率,但每块的
858/// 热流密度仍是 1000 W/m²。热控设计关心后者 —— 它与材料耐受温度和
859/// 涂层退化直接挂钩,与板子多大无关。
860///
861/// # 典型量级
862///
863/// - 太阳常数:1361 W/m²
864/// - 地球红外辐照:约 230 W/m²
865/// - 地球反照:0–400 W/m²,随轨道位置变化
866/// - 多层隔热材料漏热:几 W/m²
867///
868/// # 为什么它是热学量里唯一的向量量
869///
870/// 热流有方向。太阳照到倾斜面上,实际吸收量为 `q · n̂`(点乘),
871/// 与入射角余弦成正比。因此 `Vector3<HeatFlux>` 有意义,而
872/// [`Temperature`] 与 [`TemperatureDifference`] 都不实现
873/// [`VectorQuantity`] —— 温度是标量场,梯度 ∇T 虽是向量但单位是 K/m。
874///
875/// # 示例
876/// ```
877/// use zmatrix::physics::basic::{Area, HeatFlux, Power};
878///
879/// // 太阳常数
880/// let q = HeatFlux::from_w_per_m2(1361.0);
881/// assert_eq!(q.as_w_per_m2(), 1361.0);
882///
883/// // 换算:涂层手册常给 mW/cm²
884/// let q2 = HeatFlux::from_mw_per_cm2(1.0);
885/// assert_eq!(q2.as_w_per_m2(), 10.0);
886///
887/// // 热流密度 × 面积 = 热功率
888/// let area = Area::from_m2(2.0);
889/// let power: Power = q * area;
890/// assert_eq!(power.as_watt(), 2722.0);
891/// ```
892#[derive(Clone, Copy, Debug, PartialEq)]
893pub struct HeatFlux {
894    default_type: HeatFluxType,
895    pub v: f64,
896}
897
898#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
899pub enum ThermalConductivityType {
900    /// 瓦特每米开尔文(SI 导出单位)
901    WattPerMeterKelvin,
902    /// 千瓦每米开尔文
903    KiloWattPerMeterKelvin,
904    /// 毫瓦每厘米开尔文。隔热材料数据手册常用:
905    /// 气凝胶、隔热毡的 k 常在 0.02–0.04 W/(m·K),用 W/(m·K) 写全是零。
906    /// `1 mW/(cm·K) = 0.1 W/(m·K)`
907    MilliWattPerCentiMeterKelvin,
908}
909
910/// 导热系数(也称热导率)
911///
912/// **材料本身的属性**:单位长度、单位温差下通过的热流密度,量纲 W/(m·K)。
913/// 傅里叶定律的一维形式:`q = k · ΔT / L`。
914///
915/// # 与两个近名量的区分(中文术语极易混淆)
916///
917/// | 英文 | 中文 | 单位 | 层级 |
918/// |---|---|---|---|
919/// | `ThermalConductivity` | **导热系数**(热导率) | W/(m·K) | 材料属性 |
920/// | `HeatTransferCoefficient` | 传热系数(表面传热系数) | W/(m²·K) | 表面对流 |
921/// | `ThermalConductance` | 热导 | W/K | 构件级 |
922///
923/// - 与「传热系数」只差一个字,但单位多一个 m²:前者是材料内部导热的本领,
924///   后者是表面对流换热的强弱,两者不可互换。
925/// - 与「热导」也不同:热导是构件级的整体值,
926///   等于 `导热系数 × 面积 ÷ 长度`。
927///
928/// # 与比热容的区别
929///
930/// 导热系数管「热传得多快」,比热容(`SpecificHeatCapacity`,尚未实现)管
931/// 「热存得住多少」。两者量纲完全不同、可自由组合:隔热毡的 k 比铝小 6000 倍,
932/// c 反而更大 —— 阻断传导的同时靠自身热容缓冲温度波动。
933///
934/// # 典型量级
935///
936/// | 材料 | k (W/(m·K)) |
937/// |---|---|
938/// | 铝 | 237 |
939/// | 钛 | 22 |
940/// | 隔热毡 | 0.04 |
941///
942/// # 示例
943/// ```
944/// use zmatrix::physics::basic::{Distance, HeatFlux, TemperatureDifference, ThermalConductivity};
945///
946/// // 10 mm 厚的铝板,两面温差 20 K
947/// let k = ThermalConductivity::from_w_per_m_k(237.0);
948/// let thickness = Distance::from_m(0.01);
949/// let dt = TemperatureDifference::from_celsius(20.0);
950///
951/// // 傅里叶定律:q = k·ΔT/L
952/// let q = k.heat_flux(dt, thickness);
953/// assert!((q.as_w_per_m2() - 474_000.0).abs() < 1e-6);
954/// ```
955#[derive(Clone, Copy, Debug, PartialEq)]
956pub struct ThermalConductivity {
957    default_type: ThermalConductivityType,
958    pub v: f64,
959}
960
961#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
962pub enum HeatCapacityType {
963    /// 焦耳每开尔文(SI 导出单位)
964    JoulePerKelvin,
965    /// 千焦每开尔文
966    KiloJoulePerKelvin,
967}
968
969/// 热容
970///
971/// **整个物体**温度升高 1 K 所需的热量,量纲 J/K。
972/// 显热公式:`Q = C · ΔT`。
973///
974/// # 与比热容的区别(中文只差一个「比」字)
975///
976/// | 英文 | 中文 | 单位 | 层级 |
977/// |---|---|---|---|
978/// | `HeatCapacity` | **热容** | J/K | 整个物体 |
979/// | `SpecificHeatCapacity` | 比热容 | J/(kg·K) | 材料属性 |
980///
981/// 关系:`C = c · m`。热容是「这个物体存多少热」,比热容是「这种材料每千克存多少热」。
982/// 比热容尚未实现。
983///
984/// # 与热导的区别
985///
986/// 热容管**储热**(`C · dT/dt`),热导 [`ThermalConductance`] 管**传热**(`G · ΔT`)。
987/// 两者在热网络方程里分别占据左右两侧,量纲不同:
988///
989/// ```text
990/// C · dT/dt = G · ΔT + Q̇
991/// J/K · K/s   W/K · K     W        ← 三项都是 W
992/// ```
993///
994/// # 为什么要用热容而不是比热容
995///
996/// 集总参数热网络里,每个节点是一个等温体,需要的是「这个节点整体存多少热」,
997/// 而不是材料的单位质量属性。TD/SINDA 输出的 `CAPACITANCE` 就是本类型。
998///
999/// # 示例
1000/// ```
1001/// use zmatrix::physics::basic::{HeatCapacity, TemperatureDifference, Energy};
1002///
1003/// // 10 kg 铝件,比热容 900 J/(kg·K)
1004/// let c = HeatCapacity::from_j_per_k(9000.0);
1005///
1006/// // 升温 20 K 需要多少热量
1007/// let q: Energy = c * TemperatureDifference::from_celsius(20.0);
1008/// assert_eq!(q.as_joule(), 180_000.0);
1009/// ```
1010#[derive(Clone, Copy, Debug, PartialEq)]
1011pub struct HeatCapacity {
1012    default_type: HeatCapacityType,
1013    pub v: f64,
1014}
1015
1016#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1017pub enum ThermalConductanceType {
1018    /// 瓦特每开尔文(SI 导出单位)
1019    WattPerKelvin,
1020    /// 毫瓦每开尔文
1021    MilliWattPerKelvin,
1022}
1023
1024/// 热导
1025///
1026/// **整条连接**两端温差 1 K 时通过的热功率,量纲 W/K。
1027/// 热网络公式:`Q̇ = G · ΔT`。
1028///
1029/// # 与导热系数的区别(名字相近,层级不同)
1030///
1031/// | 英文 | 中文 | 单位 | 层级 |
1032/// |---|---|---|---|
1033/// | `ThermalConductance` | **热导** | W/K | 整条构件 |
1034/// | `ThermalConductivity` | 导热系数 | W/(m·K) | 材料属性 |
1035///
1036/// 关系:`G = k · A / L`。热导已经包含了面积和长度,是「这根构件能过多少热」;
1037/// 导热系数只是材料本身的属性。
1038///
1039/// # 热导 vs 热阻
1040///
1041/// `R = 1/G`,量纲 K/W。热阻尚未实现,但热网络里两种写法等价:
1042/// 串联用热阻相加更直观,并联用热导相加更直观。
1043///
1044/// # 与热容的区别
1045///
1046/// 热导管**传热**(`G · ΔT`),热容 [`HeatCapacity`] 管**储热**(`C · dT/dt`)。
1047/// 前者决定节点之间耦合多强,后者决定节点温度变化多慢。
1048///
1049/// # 示例
1050/// ```
1051/// use zmatrix::physics::basic::{Power, TemperatureDifference, ThermalConductance};
1052///
1053/// // 一条 12 W/K 的导热路径,两端温差 20 K
1054/// let g = ThermalConductance::from_w_per_k(12.0);
1055/// let q: Power = g * TemperatureDifference::from_celsius(20.0);
1056/// assert_eq!(q.as_watt(), 240.0);
1057/// ```
1058#[derive(Clone, Copy, Debug, PartialEq)]
1059pub struct ThermalConductance {
1060    default_type: ThermalConductanceType,
1061    pub v: f64,
1062}
1063
1064
1065impl Default for Distance {
1066    fn default() -> Self {
1067        Distance::from_m(0.0)
1068    }
1069}
1070
1071impl PhysicalQuantity for Distance {
1072    fn as_any(&self) -> &dyn Any {
1073        self
1074}
1075    fn is_zero(&self) -> bool {
1076        self.v == 0.0
1077    }
1078
1079    fn default_unit_value(&self) -> f64 {
1080        self.as_m()
1081    }
1082
1083    fn set_value(&mut self, value: f64) {
1084        self.v = value;
1085    }
1086    fn unit_display_name(&self) -> String {
1087        "Distance (m)".to_string()
1088    }
1089}
1090
1091impl Default for Angular {
1092    fn default() -> Self {
1093        Angular::from_rad(0.0)
1094    }
1095}
1096
1097impl PhysicalQuantity for Angular {
1098    fn as_any(&self) -> &dyn Any {
1099        self
1100}
1101    fn is_zero(&self) -> bool {
1102        self.v == 0.0
1103    }
1104    fn default_unit_value(&self) -> f64 {
1105        self.as_rad()
1106    }
1107
1108    fn set_value(&mut self, value: f64) {
1109        self.v = value;
1110    }
1111    fn unit_display_name(&self) -> String {
1112        "Angular (rad)".to_string()
1113    }
1114}
1115
1116impl Default for AngularVelocity {
1117    fn default() -> Self {
1118        AngularVelocity::from_rad_per_second(0.0)
1119    }
1120}
1121
1122impl Div for AngularMomentum {
1123    type Output = Coef;
1124    fn div(self, rhs: Self) -> Self::Output {
1125        let v = self.default_unit_value() / rhs.default_unit_value();
1126        Coef::new(v)
1127    }
1128}
1129
1130impl Default for Coef {
1131    fn default() -> Self {
1132        Coef::new(1.0)
1133    }
1134}
1135
1136impl Default for AngularAcceleration {
1137    fn default() -> Self {
1138        AngularAcceleration::from_rad_per_second2(0.0)
1139    }
1140}
1141
1142impl<T: VectorQuantity + Default> Default for Vector3<T> {
1143    fn default() -> Self {
1144        Vector3 {
1145            x: T::default(),
1146            y: T::default(),
1147            z: T::default(),
1148        }
1149    }
1150}
1151
1152/// 从 f64 秒数创建 chrono::TimeDelta(支持负数)
1153pub fn time_delta_from_secs_f64(secs: f64) -> chrono::TimeDelta {
1154    let nanos = (secs * 1_000_000_000.0) as i64;
1155    chrono::TimeDelta::nanoseconds(nanos)
1156}
1157
1158/// 将 chrono::TimeDelta 转换为 f64 秒数
1159pub fn time_delta_to_secs_f64(td: &chrono::TimeDelta) -> f64 {
1160    td.num_seconds() as f64 + td.subsec_nanos() as f64 * 1e-9
1161}
1162
1163
1164// Display impls for physical quantities — show f64 value only (unit is available via unit_display_name)
1165
1166// Display impls for physical quantities — show f64 value (unit available via unit_display_name)
1167macro_rules! impl_display_physics {
1168    ($type:ty) => {
1169        impl std::fmt::Display for $type {
1170            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1171                write!(f, "{}", self.default_unit_value())
1172            }
1173        }
1174    };
1175}
1176
1177impl_display_physics!(Distance);
1178impl_display_physics!(Velocity);
1179impl_display_physics!(Acceleration);
1180impl_display_physics!(Angular);
1181impl_display_physics!(AngularVelocity);
1182impl_display_physics!(AngularAcceleration);
1183impl_display_physics!(Coef);
1184impl_display_physics!(Mass);
1185impl_display_physics!(Momentum);
1186impl_display_physics!(AngularMomentum);
1187impl_display_physics!(Area);
1188impl_display_physics!(Volume);
1189impl_display_physics!(MagneticInduction);
1190impl_display_physics!(MagneticMoment);
1191impl_display_physics!(Torque);
1192impl_display_physics!(Energy);
1193impl_display_physics!(Force);
1194impl_display_physics!(Power);
1195impl_display_physics!(MagneticAngularVelocity);
1196impl_display_physics!(ElectricCurrent);
1197impl_display_physics!(ElectricPotential);
1198impl_display_physics!(ElectricResistance);
1199impl_display_physics!(ElectricCharge);
1200impl_display_physics!(ElectricCapacitance);
1201impl_display_physics!(ElectricConductance);
1202#[cfg(test)]
1203mod tests {
1204    use super::*;
1205    use approx::assert_relative_eq;
1206    #[test]
1207    fn test_as_any() {
1208        let g: &dyn PhysicalQuantity = &Acceleration::from_g(1.23);
1209        let _d = g.as_any().downcast_ref::<Acceleration>().unwrap();
1210
1211        let g: &dyn PhysicalQuantity = &Area::from_m2(10.0);
1212        let _d = g.as_any().downcast_ref::<Area>().unwrap();
1213
1214        let g: &dyn PhysicalQuantity = &Distance::from_m(10.0);
1215        let d = g.as_any().downcast_ref::<Distance>().unwrap();
1216        assert_eq!(d.default_type, DistanceType::M);
1217
1218        let g: &dyn PhysicalQuantity = &Velocity::from_m_per_sec(10.0);
1219        let d = g.as_any().downcast_ref::<Velocity>().unwrap();
1220        assert_eq!(d.default_type, VelocityType::MPerSecond);
1221
1222        let g: &dyn PhysicalQuantity = &Acceleration::from_g(1.23);
1223        let d = g.as_any().downcast_ref::<Acceleration>().unwrap();
1224        assert_eq!(d.default_type, AccelerationType::G);
1225
1226        let g: &dyn PhysicalQuantity = &Angular::from_rad(1.23);
1227        let d = g.as_any().downcast_ref::<Angular>().unwrap();
1228        assert_eq!(d.default_type, AngularType::Rad);
1229
1230        let g: &dyn PhysicalQuantity = &AngularVelocity::from_rad_per_hour(1.23);
1231        let d = g.as_any().downcast_ref::<AngularVelocity>().unwrap();
1232        assert_eq!(d.default_type, AngularVelocityType::RadperHour);
1233
1234        let g: &dyn PhysicalQuantity = &Coef::new(1.23);
1235        let d = g.as_any().downcast_ref::<Coef>().unwrap();
1236        assert_eq!(d.v, 1.23);
1237
1238        let g: &dyn PhysicalQuantity = &AngularAcceleration::from_rad_per_second2(1.23);
1239        let d = g.as_any().downcast_ref::<AngularAcceleration>().unwrap();
1240        assert_eq!(d.default_type, AngularAccelerationType::RadperSecond2);
1241    }
1242
1243    #[test]
1244    fn test_acceleration_default() {
1245        let a = Acceleration::default();
1246        assert_eq!(0.0, a.as_m_per_s2());
1247    }
1248
1249    #[test]
1250    fn test_angular_velocity_default() {
1251        let a = AngularVelocity::default();
1252        assert_eq!(0.0, a.as_rad_per_hour());
1253
1254        let a = AngularAcceleration::default();
1255        assert_eq!(0.0, a.as_rad_per_second2());
1256    }
1257
1258    #[test]
1259    fn test_angular_is_zero() {
1260        let a = Angular::from_rad(0.0);
1261        assert_eq!(true, a.is_zero());
1262        let a = Angular::from_rad(1.0);
1263        assert_eq!(false, a.is_zero());
1264    }
1265
1266    #[test]
1267    fn test_volume_clone() {
1268        let a = Volume::from_m3(3.8);
1269        let b = a.clone();
1270        assert_relative_eq!(a.as_km3(), b.as_km3(), epsilon = 1e-8);
1271    }
1272
1273    #[test]
1274    fn test_magnetic_moment_as_any() {
1275        let g: &dyn PhysicalQuantity = &MagneticMoment::from_am2(1.23);
1276        let d = g.as_any().downcast_ref::<MagneticMoment>().unwrap();
1277        assert_eq!(d.default_type, MagneticMomentType::AM2);
1278    }
1279
1280    #[test]
1281    fn test_magnetic_moment_default() {
1282        let mm = MagneticMoment::default();
1283        assert_eq!(0.0, mm.as_am2());
1284    }
1285
1286    #[test]
1287    fn test_magnetic_moment_is_zero() {
1288        let mm = MagneticMoment::from_am2(0.0);
1289        assert_eq!(true, mm.is_zero());
1290        let mm = MagneticMoment::from_am2(1.0);
1291        assert_eq!(false, mm.is_zero());
1292    }
1293
1294    #[test]
1295    fn test_torque_as_any() {
1296        let g: &dyn PhysicalQuantity = &Torque::from_nm(1.23);
1297        let d = g.as_any().downcast_ref::<Torque>().unwrap();
1298        assert_eq!(d.default_type, TorqueType::NM);
1299    }
1300
1301    #[test]
1302    fn test_torque_default() {
1303        let t = Torque::default();
1304        assert_eq!(0.0, t.as_nm());
1305    }
1306
1307    #[test]
1308    fn test_torque_is_zero() {
1309        let t = Torque::from_nm(0.0);
1310        assert_eq!(true, t.is_zero());
1311        let t = Torque::from_nm(1.0);
1312        assert_eq!(false, t.is_zero());
1313    }
1314
1315    #[test]
1316    fn test_energy_as_any() {
1317        let g: &dyn PhysicalQuantity = &Energy::from_joule(1.23);
1318        let d = g.as_any().downcast_ref::<Energy>().unwrap();
1319        assert_eq!(d.default_type, EnergyType::Joule);
1320    }
1321
1322    #[test]
1323    fn test_energy_default() {
1324        let e = Energy::default();
1325        assert_eq!(0.0, e.as_joule());
1326    }
1327
1328    #[test]
1329    fn test_energy_is_zero() {
1330        let e = Energy::from_joule(0.0);
1331        assert_eq!(true, e.is_zero());
1332        let e = Energy::from_joule(1.0);
1333        assert_eq!(false, e.is_zero());
1334    }
1335
1336    #[test]
1337    fn test_angular_default_unit_value_is_rad() {
1338        // 默认单位为弧度时,default_unit_value 应等于 as_rad()
1339        let a_rad = Angular::from_rad(std::f64::consts::PI);
1340        assert_relative_eq!(a_rad.default_unit_value(), a_rad.as_rad(), epsilon = 1e-12);
1341
1342        // 默认单位为度时,default_unit_value 也应返回弧度
1343        let a_deg = Angular::from_deg(180.0);
1344        assert_relative_eq!(a_deg.default_unit_value(), a_deg.as_rad(), epsilon = 1e-12);
1345
1346        // 随机值覆盖(包括负值和小数),确保统一弧度输出
1347        let cases = vec![
1348            -720.0, -360.0, -45.5, 0.0, 12.34, 90.0, 123.456, 360.0, 720.0,
1349        ];
1350        for deg in cases {
1351            let a = Angular::from_deg(deg);
1352            assert_relative_eq!(a.default_unit_value(), a.as_rad(), epsilon = 1e-12);
1353        }
1354    }
1355
1356    #[test]
1357    fn test_unit_display_name_and_display() {
1358        // 验证所有物理量的 unit_display_name 与 Display 实现
1359
1360        assert_eq!(Distance::from_m(1.5).unit_display_name(), "Distance (m)");
1361        assert_eq!(format!("{}", Distance::from_m(2.5)), "2.5");
1362
1363        assert_eq!(Velocity::from_m_per_sec(3.0).unit_display_name(), "Velocity (m/s)");
1364        assert_eq!(format!("{}", Velocity::from_km_per_h(36.0)), "10");
1365
1366        assert_eq!(Acceleration::from_m_per_s2(9.8).unit_display_name(), "Acceleration (m/s²)");
1367        assert_eq!(format!("{}", Acceleration::from_g(1.0)), format!("{}", 9.80665));
1368
1369        assert_eq!(Angular::from_rad(1.0).unit_display_name(), "Angular (rad)");
1370        assert_eq!(format!("{}", Angular::from_deg(180.0)), format!("{}", std::f64::consts::PI));
1371
1372        assert_eq!(AngularVelocity::from_rad_per_second(2.0).unit_display_name(), "AngularVelocity (rad/s)");
1373        assert_eq!(format!("{}", AngularVelocity::from_rad_per_second(2.0)), "2");
1374
1375        assert_eq!(AngularAcceleration::from_rad_per_second2(1.0).unit_display_name(), "AngularAcceleration (rad/s²)");
1376        assert_eq!(format!("{}", AngularAcceleration::from_rad_per_second2(3.0)), "3");
1377
1378        assert_eq!(Coef::new(3.14).unit_display_name(), "Coef");
1379        assert_eq!(format!("{}", Coef::new(3.14)), "3.14");
1380
1381        assert_eq!(Mass::from_kg(1.0).unit_display_name(), "Mass (kg)");
1382        assert_eq!(format!("{}", Mass::from_kg(5.0)), "5");
1383
1384        assert_eq!(Momentum::from_kg_m_s(10.0).unit_display_name(), "Momentum (kg·m/s)");
1385        assert_eq!(format!("{}", Momentum::from_kg_m_s(10.0)), "10");
1386
1387        assert_eq!(AngularMomentum::from_kg_m2_per_second(100.0).unit_display_name(), "AngularMomentum (kg·m²/s)");
1388        assert_eq!(format!("{}", AngularMomentum::from_nms(50.0)), "50");
1389
1390        assert_eq!(Area::from_m2(4.0).unit_display_name(), "Area (m²)");
1391        assert_eq!(format!("{}", Area::from_m2(12.0)), "12");
1392
1393        assert_eq!(Volume::from_m3(8.0).unit_display_name(), "Volume (m³)");
1394        assert_eq!(format!("{}", Volume::from_m3(8.0)), "8");
1395
1396        assert_eq!(MagneticInduction::from_tesla(1.0).unit_display_name(), "MagneticInduction (T)");
1397        assert_eq!(format!("{}", MagneticInduction::from_gauss(10000.0)), "1");
1398
1399        assert_eq!(MagneticMoment::from_am2(1.0).unit_display_name(), "MagneticMoment (A·m²)");
1400        assert_eq!(format!("{}", MagneticMoment::from_am2(2.0)), "2");
1401
1402        assert_eq!(Torque::from_nm(10.0).unit_display_name(), "Torque (N·m)");
1403        assert_eq!(format!("{}", Torque::from_nm(0.0)), "0");
1404
1405        assert_eq!(Energy::from_joule(42.0).unit_display_name(), "Energy (J)");
1406        assert_eq!(format!("{}", Energy::from_joule(0.0)), "0");
1407
1408        assert_eq!(Force::from_newton(100.0).unit_display_name(), "Force (N)");
1409        assert_eq!(format!("{}", Force::from_newton(100.0)), "100");
1410
1411        assert_eq!(Power::from_watt(60.0).unit_display_name(), "Power (W)");
1412        assert_eq!(format!("{}", Power::from_watt(60.0)), "60");
1413
1414        assert_eq!(MagneticAngularVelocity::from_tesla_rad_per_second(1.0).unit_display_name(), "MagneticAngularVelocity (T·rad/s)");
1415        assert_eq!(format!("{}", MagneticAngularVelocity::from_tesla_rad_per_second(1.0)), "1");
1416
1417        // 电学物理量
1418        assert_eq!(ElectricCurrent::from_a(2.5).unit_display_name(), "ElectricCurrent (A)");
1419        assert_eq!(format!("{}", ElectricCurrent::from_a(2.5)), "2.5");
1420
1421        assert_eq!(ElectricPotential::from_v(12.0).unit_display_name(), "ElectricPotential (V)");
1422        assert_eq!(format!("{}", ElectricPotential::from_v(12.0)), "12");
1423
1424        assert_eq!(ElectricResistance::from_ohm(100.0).unit_display_name(), "ElectricResistance (Ohm)");
1425        assert_eq!(format!("{}", ElectricResistance::from_ohm(100.0)), "100");
1426
1427        assert_eq!(ElectricCharge::from_coulomb(5.0).unit_display_name(), "ElectricCharge (C)");
1428        assert_eq!(format!("{}", ElectricCharge::from_coulomb(5.0)), "5");
1429
1430        assert_eq!(ElectricCapacitance::from_farad(0.001).unit_display_name(), "ElectricCapacitance (F)");
1431        assert_eq!(format!("{}", ElectricCapacitance::from_farad(0.001)), "0.001");
1432
1433        assert_eq!(ElectricConductance::from_siemens(0.01).unit_display_name(), "ElectricConductance (S)");
1434        assert_eq!(format!("{}", ElectricConductance::from_siemens(0.01)), "0.01");
1435    }
1436
1437    #[test]
1438    fn test_vector3_display() {
1439        let v: Vector3<Distance> = Vector3::new(
1440            Distance::from_m(1.0),
1441            Distance::from_m(2.0),
1442            Distance::from_m(3.0),
1443        );
1444        assert_eq!(format!("{}", v), "(1, 2, 3)");
1445    }
1446}
1447
1448// 手动实现 VectorQuantity trait 给所有支持向量的物理量
1449impl VectorQuantity for Distance {}
1450impl VectorQuantity for Velocity {}
1451impl VectorQuantity for Acceleration {}
1452impl VectorQuantity for Angular {}
1453impl VectorQuantity for AngularVelocity {}
1454impl VectorQuantity for AngularAcceleration {}
1455impl VectorQuantity for Coef {}
1456impl VectorQuantity for AngularMomentum {}
1457impl VectorQuantity for Momentum {}
1458impl VectorQuantity for MagneticInduction {}
1459impl VectorQuantity for MagneticMoment {}
1460impl VectorQuantity for Torque {}
1461impl VectorQuantity for Force {}
1462impl VectorQuantity for MagneticAngularVelocity {}
1463impl VectorQuantity for HeatFlux {}