Skip to main content

oxiphysics_materials/nano/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5#[allow(unused_imports)]
6use super::functions::*;
7/// Models the matrix-fiber interfacial transition zone (ITZ).
8///
9/// Accounts for debonding criterion and van der Waals adhesion forces.
10#[derive(Debug, Clone)]
11pub struct InterfacialZone {
12    /// Interface fracture energy Gc (J/m²).
13    pub fracture_energy: f64,
14    /// Interface strength (Pa).
15    pub strength: f64,
16    /// van der Waals adhesion energy (J/m²).
17    pub vdw_energy: f64,
18    /// Interface thickness (m).
19    pub thickness: f64,
20    /// Debonding flag.
21    pub debonded: bool,
22}
23impl InterfacialZone {
24    /// Creates a new interfacial zone model.
25    pub fn new(fracture_energy: f64, strength: f64, vdw_energy: f64, thickness: f64) -> Self {
26        Self {
27            fracture_energy,
28            strength,
29            vdw_energy,
30            thickness,
31            debonded: false,
32        }
33    }
34    /// Check debonding: debond if applied traction exceeds strength.
35    pub fn check_debond(&mut self, traction: f64) {
36        if traction >= self.strength {
37            self.debonded = true;
38        }
39    }
40    /// van der Waals force per unit area (Lennard-Jones approximation).
41    ///
42    /// F/A = A_H / (6 * pi * z^3) where A_H is Hamaker constant.
43    pub fn vdw_force_per_area(&self, separation_nm: f64) -> f64 {
44        let a_h = 1e-19;
45        let z = separation_nm * 1e-9;
46        a_h / (6.0 * std::f64::consts::PI * z.powi(3))
47    }
48    /// Cohesive zone traction for bilinear law.
49    pub fn cohesive_traction(&self, separation: f64, delta_c: f64) -> f64 {
50        let delta_ratio = (separation / delta_c).clamp(0.0, 1.0);
51        self.strength * (1.0 - delta_ratio)
52    }
53}
54/// Classical Laminate Theory: computes the ABD stiffness matrix.
55///
56/// The ABD matrix relates mid-plane strains/curvatures to in-plane forces/moments.
57#[derive(Debug, Clone)]
58pub struct CompositeLaminate {
59    /// Stack of plies (bottom to top).
60    pub plies: Vec<Ply>,
61}
62impl CompositeLaminate {
63    /// Creates a new composite laminate from a list of plies.
64    pub fn new(plies: Vec<Ply>) -> Self {
65        Self { plies }
66    }
67    /// Total laminate thickness.
68    pub fn total_thickness(&self) -> f64 {
69        self.plies.iter().map(|p| p.thickness).sum()
70    }
71    /// Computes the A11 membrane stiffness component.
72    ///
73    /// A11 = Σ Qbar11_k * (z_k - z_{k-1}).
74    pub fn a11(&self) -> f64 {
75        self.plies
76            .iter()
77            .map(|p| p.transformed_q11() * p.thickness)
78            .sum()
79    }
80    /// Computes the D11 bending stiffness component (simplified: assumes symmetric laminate).
81    ///
82    /// D11 = (1/12) * Σ Qbar11_k * t_k^3.
83    pub fn d11(&self) -> f64 {
84        self.plies
85            .iter()
86            .map(|p| p.transformed_q11() * p.thickness.powi(3) / 12.0)
87            .sum()
88    }
89    /// Checks Tsai-Wu failure criterion for the first ply.
90    ///
91    /// Returns the safety factor (>1 = safe).
92    pub fn tsai_wu_safety_factor(&self, n11: f64, xt: f64, xc: f64) -> f64 {
93        let f1 = 1.0 / xt - 1.0 / xc;
94        let f11 = 1.0 / (xt * xc);
95        let a_coef = f11 * n11 * n11;
96        let b_coef = f1 * n11;
97        if a_coef + b_coef <= 0.0 {
98            return f64::INFINITY;
99        }
100        1.0 / (a_coef + b_coef)
101    }
102}
103/// Virial stress tensor computation bridging molecular to continuum mechanics.
104pub struct VirialStress {
105    /// Volume of the representative volume element (m³).
106    pub volume_m3: f64,
107    /// Temperature (K) for kinetic contribution.
108    pub temperature: f64,
109    /// Number of atoms in the RVE.
110    pub n_atoms: usize,
111    /// Atom mass (kg).
112    pub atom_mass: f64,
113}
114impl VirialStress {
115    /// Silicon RVE for nanoscale stress calculation.
116    pub fn silicon_rve(side_nm: f64) -> Self {
117        let side_m = side_nm * 1.0e-9;
118        VirialStress {
119            volume_m3: side_m.powi(3),
120            temperature: 300.0,
121            n_atoms: (side_nm / 0.543 * 8.0).round() as usize,
122            atom_mass: 28.0 * 1.660e-27,
123        }
124    }
125    /// Virial kinetic stress (Pa) = N*m*kT / V per component.
126    pub fn kinetic_stress_pa(&self) -> f64 {
127        const KB: f64 = 1.380649e-23;
128        self.n_atoms as f64 * self.atom_mass * KB * self.temperature / self.volume_m3
129    }
130    /// Virial potential stress (Pa) from pair force-distance list.
131    /// `pairs`: list of (force_x: f64, force_y: f64, force_z: f64, dx: f64, dy: f64, dz: f64).
132    pub fn potential_stress_xx(&self, pairs: &[(f64, f64, f64, f64, f64, f64)]) -> f64 {
133        let sum: f64 = pairs
134            .iter()
135            .map(|(fx, _fy, _fz, rx, _ry, _rz)| fx * rx)
136            .sum();
137        -sum / self.volume_m3
138    }
139    /// Cauchy-Born rule: mapping atomistic strain to continuum deformation gradient.
140    /// Given deformation gradient F\[3x3\] as flat array \[F11,F12,F13,F21,...\],
141    /// returns the Green-Lagrange strain E = 0.5*(F^T*F - I) as 6-component Voigt.
142    #[allow(clippy::too_many_arguments)]
143    pub fn cauchy_born_strain(f: &[f64; 9]) -> [f64; 6] {
144        let ftf11 = f[0] * f[0] + f[3] * f[3] + f[6] * f[6];
145        let ftf22 = f[1] * f[1] + f[4] * f[4] + f[7] * f[7];
146        let ftf33 = f[2] * f[2] + f[5] * f[5] + f[8] * f[8];
147        let ftf12 = f[0] * f[1] + f[3] * f[4] + f[6] * f[7];
148        let ftf13 = f[0] * f[2] + f[3] * f[5] + f[6] * f[8];
149        let ftf23 = f[1] * f[2] + f[4] * f[5] + f[7] * f[8];
150        [
151            0.5 * (ftf11 - 1.0),
152            0.5 * (ftf22 - 1.0),
153            0.5 * (ftf33 - 1.0),
154            ftf12,
155            ftf13,
156            ftf23,
157        ]
158    }
159    /// Atomistic-to-continuum stress via Hardy formulation (simplified).
160    pub fn hardy_stress_xx(&self, velocities: &[[f64; 3]], forces: &[[f64; 3]]) -> f64 {
161        const KB: f64 = 1.380649e-23;
162        let kinetic: f64 = velocities
163            .iter()
164            .map(|v| self.atom_mass * v[0] * v[0])
165            .sum();
166        let potential: f64 = forces
167            .iter()
168            .enumerate()
169            .map(|(i, f_arr)| {
170                if i < velocities.len() {
171                    f_arr[0] * velocities[i][0] * 1.0e-9
172                } else {
173                    0.0
174                }
175            })
176            .sum();
177        let _ = KB;
178        -(kinetic + potential) / self.volume_m3
179    }
180}
181/// Quantum dot confinement energy using particle-in-a-box model.
182pub struct QuantumDotConfinement {
183    /// Dot radius (nm).
184    pub radius_nm: f64,
185    /// Bulk bandgap Eg (eV).
186    pub bulk_bandgap_ev: f64,
187    /// Effective electron mass ratio m*_e/m_0.
188    pub eff_mass_electron: f64,
189    /// Effective hole mass ratio m*_h/m_0.
190    pub eff_mass_hole: f64,
191    /// Dielectric constant ε_r.
192    pub dielectric_constant: f64,
193    /// Material designation.
194    pub designation: String,
195}
196impl QuantumDotConfinement {
197    /// Boltzmann constant (eV/K).
198    #[allow(dead_code)]
199    const KB_EV: f64 = 8.617e-5;
200    /// Planck constant (eV·s).
201    #[allow(dead_code)]
202    const H_EV_S: f64 = 4.136e-15;
203    /// Free electron mass (kg).
204    const M0: f64 = 9.109e-31;
205    /// CdSe quantum dot.
206    pub fn cdse(radius_nm: f64) -> Self {
207        QuantumDotConfinement {
208            radius_nm,
209            bulk_bandgap_ev: 1.74,
210            eff_mass_electron: 0.13,
211            eff_mass_hole: 0.45,
212            dielectric_constant: 9.4,
213            designation: "CdSe".to_string(),
214        }
215    }
216    /// InP quantum dot.
217    pub fn inp(radius_nm: f64) -> Self {
218        QuantumDotConfinement {
219            radius_nm,
220            bulk_bandgap_ev: 1.35,
221            eff_mass_electron: 0.077,
222            eff_mass_hole: 0.64,
223            dielectric_constant: 12.4,
224            designation: "InP".to_string(),
225        }
226    }
227    /// Confinement energy (eV) from effective mass approximation.
228    /// ΔE = ℏ²π²/(2 * μ * R²) where 1/μ = 1/m*_e + 1/m*_h.
229    pub fn confinement_energy_ev(&self) -> f64 {
230        const HBAR_J_S: f64 = 1.0546e-34;
231        const EV_TO_J: f64 = 1.602e-19;
232        let r = self.radius_nm * 1.0e-9;
233        let mu = 1.0 / (1.0 / self.eff_mass_electron + 1.0 / self.eff_mass_hole);
234        let mu_kg = mu * Self::M0;
235        let e_j = std::f64::consts::PI.powi(2) * HBAR_J_S.powi(2) / (2.0 * mu_kg * r.powi(2));
236        e_j / EV_TO_J
237    }
238    /// Coulomb interaction correction (eV) — first-order electrostatic.
239    pub fn coulomb_correction_ev(&self) -> f64 {
240        const E_SQ_OVER_4PI_EPS0: f64 = 14.4;
241        let r_angstrom = self.radius_nm * 10.0;
242        -1.786 * E_SQ_OVER_4PI_EPS0 / (self.dielectric_constant * r_angstrom)
243    }
244    /// Size-dependent bandgap Eg(R) = Eg_bulk + ΔE_confinement + ΔE_coulomb.
245    pub fn effective_bandgap_ev(&self) -> f64 {
246        self.bulk_bandgap_ev + self.confinement_energy_ev() + self.coulomb_correction_ev()
247    }
248    /// Emission wavelength (nm) corresponding to the bandgap.
249    pub fn emission_wavelength_nm(&self) -> f64 {
250        let eg = self.effective_bandgap_ev().max(0.01);
251        1239.8 / eg
252    }
253    /// Bohr radius aB (nm) = ε_r * a0 / μ.
254    pub fn bohr_radius_nm(&self) -> f64 {
255        let a0 = 0.0529;
256        let mu = 1.0 / (1.0 / self.eff_mass_electron + 1.0 / self.eff_mass_hole);
257        self.dielectric_constant * a0 / mu
258    }
259}
260/// Effective stiffness of a matrix with dispersed nanoparticles.
261///
262/// Uses Mori-Tanaka homogenization for spherical inclusions.
263#[derive(Debug, Clone)]
264pub struct NanoparticleDispersion {
265    /// Particle volume fraction (0 to 1).
266    pub volume_fraction: f64,
267    /// Particle Young's modulus (Pa).
268    pub ep: f64,
269    /// Matrix Young's modulus (Pa).
270    pub em: f64,
271    /// Particle Poisson's ratio.
272    pub nu_p: f64,
273    /// Matrix Poisson's ratio.
274    pub nu_m: f64,
275}
276impl NanoparticleDispersion {
277    /// Creates a new nanoparticle dispersion.
278    pub fn new(volume_fraction: f64, ep: f64, em: f64, nu_p: f64, nu_m: f64) -> Self {
279        Self {
280            volume_fraction: volume_fraction.clamp(0.0, 1.0),
281            ep,
282            em,
283            nu_p,
284            nu_m,
285        }
286    }
287    /// Bulk modulus K from E and ν.
288    fn bulk_modulus(e: f64, nu: f64) -> f64 {
289        e / (3.0 * (1.0 - 2.0 * nu))
290    }
291    /// Shear modulus G from E and ν.
292    fn shear_modulus(e: f64, nu: f64) -> f64 {
293        e / (2.0 * (1.0 + nu))
294    }
295    /// Effective bulk modulus from Mori-Tanaka.
296    pub fn effective_bulk_modulus(&self) -> f64 {
297        let km = Self::bulk_modulus(self.em, self.nu_m);
298        let kp = Self::bulk_modulus(self.ep, self.nu_p);
299        let gm = Self::shear_modulus(self.em, self.nu_m);
300        let f = self.volume_fraction;
301        let alpha = 3.0 * km / (3.0 * km + 4.0 * gm);
302        let dk = kp - km;
303        km + f * dk / (1.0 + (1.0 - f) * dk / (km + 4.0 * gm / 3.0) * alpha)
304    }
305    /// Effective Young's modulus from Mori-Tanaka bulk and shear moduli.
306    pub fn effective_youngs_modulus(&self) -> f64 {
307        let km = Self::bulk_modulus(self.em, self.nu_m);
308        let kp = Self::bulk_modulus(self.ep, self.nu_p);
309        let gm = Self::shear_modulus(self.em, self.nu_m);
310        let gp = Self::shear_modulus(self.ep, self.nu_p);
311        let f = self.volume_fraction;
312        let alpha_k = 3.0 * km / (3.0 * km + 4.0 * gm);
313        let beta_g = 6.0 * (km + 2.0 * gm) / (5.0 * (3.0 * km + 4.0 * gm));
314        let k_eff = km + f * (kp - km) / (1.0 + (1.0 - f) * (kp - km) / km * alpha_k);
315        let g_eff = gm + f * (gp - gm) / (1.0 + (1.0 - f) * (gp - gm) / gm * beta_g);
316        9.0 * k_eff * g_eff / (3.0 * k_eff + g_eff)
317    }
318}
319/// Graphene in-plane elastic stiffness tensor and derived properties.
320pub struct GrapheneElasticStiffness {
321    /// In-plane stiffness C11 (N/m, 2D modulus × thickness).
322    pub c11: f64,
323    /// In-plane stiffness C12 (N/m).
324    pub c12: f64,
325    /// In-plane stiffness C44 (N/m).
326    pub c44: f64,
327    /// Graphene layer thickness (nm, typically 0.335 nm for bulk conversion).
328    pub layer_thickness_nm: f64,
329}
330impl GrapheneElasticStiffness {
331    /// Perfect monolayer graphene elastic constants (from DFT/experiments).
332    pub fn monolayer() -> Self {
333        GrapheneElasticStiffness {
334            c11: 352.0,
335            c12: 60.0,
336            c44: 146.0,
337            layer_thickness_nm: 0.335,
338        }
339    }
340    /// Graphene oxide (reduced GO) with ~15% defect density.
341    pub fn reduced_graphene_oxide() -> Self {
342        GrapheneElasticStiffness {
343            c11: 250.0,
344            c12: 45.0,
345            c44: 100.0,
346            layer_thickness_nm: 0.335,
347        }
348    }
349    /// In-plane Young's modulus E2D (N/m) = C11 - C12²/C11.
350    pub fn youngs_modulus_2d(&self) -> f64 {
351        self.c11 - self.c12 * self.c12 / self.c11
352    }
353    /// In-plane Poisson's ratio ν = C12 / C11.
354    pub fn poisson_ratio(&self) -> f64 {
355        self.c12 / self.c11
356    }
357    /// 3D Young's modulus (GPa) using layer thickness for bulk conversion.
358    pub fn youngs_modulus_3d_gpa(&self) -> f64 {
359        let t = self.layer_thickness_nm * 1.0e-9;
360        (self.youngs_modulus_2d() / t) / 1.0e9
361    }
362    /// Bending stiffness κ (eV) ≈ 1.4 eV for monolayer graphene.
363    pub fn bending_stiffness_ev(&self) -> f64 {
364        let t = self.layer_thickness_nm * 1.0e-9;
365        let nu = self.poisson_ratio();
366        let kappa_nm = self.c11 * 1e9 * t.powi(3) / (12.0 * (1.0 - nu * nu));
367        kappa_nm * 6.241e18
368    }
369}
370/// Carbon nanotube (CNT) mechanical and thermal properties.
371///
372/// Models single-walled CNTs with armchair, zigzag, or chiral geometry.
373#[derive(Debug, Clone)]
374pub struct NanotubeProperties {
375    /// Chirality type.
376    pub chirality: CntChirality,
377    /// Chiral indices (n, m).
378    pub n: u32,
379    /// Chiral index m.
380    pub m: u32,
381    /// Tube diameter (nm).
382    pub diameter_nm: f64,
383    /// Young's modulus (TPa, typically ~1 TPa for SWCNTs).
384    pub youngs_modulus_tpa: f64,
385    /// Tensile strength (GPa).
386    pub tensile_strength_gpa: f64,
387    /// Thermal conductivity (W/m/K).
388    pub thermal_conductivity: f64,
389}
390impl NanotubeProperties {
391    /// Creates an armchair (n,n) CNT.
392    pub fn armchair(n: u32) -> Self {
393        let a_cc = 0.142;
394        let diameter = a_cc * (3.0_f64.sqrt()) * n as f64 / std::f64::consts::PI;
395        Self {
396            chirality: CntChirality::Armchair,
397            n,
398            m: n,
399            diameter_nm: diameter,
400            youngs_modulus_tpa: 1.0,
401            tensile_strength_gpa: 130.0,
402            thermal_conductivity: 3500.0,
403        }
404    }
405    /// Creates a zigzag (n, 0) CNT.
406    pub fn zigzag(n: u32) -> Self {
407        let a_cc = 0.142;
408        let diameter = a_cc * (3.0_f64.sqrt()) * n as f64 / std::f64::consts::PI;
409        Self {
410            chirality: CntChirality::Zigzag,
411            n,
412            m: 0,
413            diameter_nm: diameter,
414            youngs_modulus_tpa: 0.97,
415            tensile_strength_gpa: 120.0,
416            thermal_conductivity: 2000.0,
417        }
418    }
419    /// Creates a chiral (n, m) CNT.
420    pub fn chiral(n: u32, m: u32) -> Self {
421        let a_cc = 0.142;
422        let diameter = a_cc * ((n * n + n * m + m * m) as f64).sqrt() / std::f64::consts::PI;
423        Self {
424            chirality: CntChirality::Chiral,
425            n,
426            m,
427            diameter_nm: diameter,
428            youngs_modulus_tpa: 1.0,
429            tensile_strength_gpa: 100.0,
430            thermal_conductivity: 1500.0,
431        }
432    }
433    /// Axial stiffness: EA = E * A, where A = π * d * t (t = 0.34 nm wall thickness).
434    pub fn axial_stiffness_n(&self) -> f64 {
435        let wall_thickness_nm = 0.34;
436        let area_nm2 = std::f64::consts::PI * self.diameter_nm * wall_thickness_nm;
437        let area_m2 = area_nm2 * 1e-18;
438        let e_pa = self.youngs_modulus_tpa * 1e12;
439        e_pa * area_m2
440    }
441    /// Returns true if the CNT is metallic (armchair or n-m divisible by 3).
442    pub fn is_metallic(&self) -> bool {
443        match self.chirality {
444            CntChirality::Armchair => true,
445            _ => (self.n as i32 - self.m as i32).abs() % 3 == 0,
446        }
447    }
448}
449/// Type of carbon nanotube chirality.
450#[derive(Debug, Clone, Copy, PartialEq)]
451pub enum CntChirality {
452    /// Armchair (n, n) — metallic.
453    Armchair,
454    /// Zigzag (n, 0) — can be semiconducting or metallic.
455    Zigzag,
456    /// Chiral (n, m) — general case.
457    Chiral,
458}
459/// Thermoelectric material: Seebeck, Peltier, and ZT.
460#[derive(Debug, Clone)]
461pub struct ThermoelectricMaterial {
462    /// Seebeck coefficient S (V/K).
463    pub seebeck: f64,
464    /// Electrical conductivity σ (S/m).
465    pub electrical_conductivity: f64,
466    /// Thermal conductivity κ (W/m/K).
467    pub thermal_conductivity: f64,
468    /// Operating temperature T (K).
469    pub temperature: f64,
470}
471impl ThermoelectricMaterial {
472    /// Creates a Bi2Te3-like thermoelectric (typical room-temperature TE).
473    pub fn bismuth_telluride() -> Self {
474        Self {
475            seebeck: 200e-6,
476            electrical_conductivity: 1e5,
477            thermal_conductivity: 1.5,
478            temperature: 300.0,
479        }
480    }
481    /// Power factor PF = S² * σ (W/m/K²).
482    pub fn power_factor(&self) -> f64 {
483        self.seebeck * self.seebeck * self.electrical_conductivity
484    }
485    /// Figure of merit ZT = S² * σ * T / κ (dimensionless).
486    pub fn zt(&self) -> f64 {
487        self.power_factor() * self.temperature / self.thermal_conductivity
488    }
489    /// Peltier coefficient Π = S * T (V).
490    pub fn peltier_coefficient(&self) -> f64 {
491        self.seebeck * self.temperature
492    }
493    /// Carnot efficiency at temperature difference ΔT.
494    pub fn device_efficiency(&self, delta_t: f64) -> f64 {
495        let zt = self.zt();
496        let t_h = self.temperature + delta_t;
497        let carnot = delta_t / t_h;
498        let sqrt_factor = (1.0 + zt).sqrt();
499        let t_ratio = self.temperature / t_h;
500        carnot * (sqrt_factor - 1.0) / (sqrt_factor + t_ratio)
501    }
502}
503/// Dislocation mechanics: Peierls stress, Orowan strengthening, dislocation density.
504pub struct DislocationMechanics {
505    /// Shear modulus G (GPa).
506    pub shear_modulus: f64,
507    /// Burgers vector magnitude b (nm).
508    pub burgers_nm: f64,
509    /// Poisson's ratio ν.
510    pub nu: f64,
511    /// Lattice parameter a (nm).
512    pub lattice_parameter_nm: f64,
513    /// Misfit parameter δ for precipitate.
514    pub misfit: f64,
515}
516impl DislocationMechanics {
517    /// FCC copper dislocation mechanics.
518    pub fn fcc_copper() -> Self {
519        DislocationMechanics {
520            shear_modulus: 42.0,
521            burgers_nm: 0.256,
522            nu: 0.34,
523            lattice_parameter_nm: 0.362,
524            misfit: 0.005,
525        }
526    }
527    /// BCC iron dislocation mechanics.
528    pub fn bcc_iron() -> Self {
529        DislocationMechanics {
530            shear_modulus: 82.0,
531            burgers_nm: 0.248,
532            nu: 0.29,
533            lattice_parameter_nm: 0.286,
534            misfit: 0.01,
535        }
536    }
537    /// Peierls-Nabarro stress τ_P (GPa).
538    /// τ_P = 2G/(1-ν) * exp(-2π*w/b) where w = a/(2*(1-ν)).
539    pub fn peierls_nabarro_stress(&self) -> f64 {
540        let a = self.lattice_parameter_nm;
541        let b = self.burgers_nm;
542        let w = a / (2.0 * (1.0 - self.nu));
543
544        2.0 * self.shear_modulus / (1.0 - self.nu) * (-2.0 * std::f64::consts::PI * w / b).exp()
545    }
546    /// Orowan strengthening stress Δτ (MPa) for obstacles of spacing λ.
547    /// Δτ = M * G * b / λ where M = Taylor factor.
548    pub fn orowan_stress_mpa(&self, obstacle_spacing_nm: f64) -> f64 {
549        const M: f64 = 3.06;
550        M * self.shear_modulus * self.burgers_nm / obstacle_spacing_nm * 1000.0
551    }
552    /// Taylor hardening: Δσ = M * α * G * b * sqrt(ρ).
553    pub fn taylor_hardening_mpa(&self, dislocation_density_m2: f64) -> f64 {
554        const M: f64 = 3.06;
555        const ALPHA: f64 = 0.3;
556        M * ALPHA
557            * self.shear_modulus
558            * self.burgers_nm
559            * 1.0e-9
560            * dislocation_density_m2.sqrt()
561            * 1.0e9
562    }
563    /// Dislocation line energy per unit length (J/m).
564    pub fn line_energy(&self) -> f64 {
565        let b = self.burgers_nm * 1.0e-9;
566        let g = self.shear_modulus * 1.0e9;
567        g * b.powi(2) / 2.0
568    }
569    /// Critical resolved shear stress (Schmid's law).
570    pub fn schmid_crss(&self, sigma_applied: f64, phi: f64, lambda: f64) -> f64 {
571        sigma_applied * phi.cos() * lambda.cos()
572    }
573}
574/// Graphene thermal conductivity model: size, defects, substrate coupling.
575pub struct NanoGrapheneThermal {
576    /// Ideal bulk in-plane conductivity (W/m·K), ≈5000 for suspended.
577    pub k_bulk: f64,
578    /// Substrate coupling parameter Γ (GW/m³·K) per unit area.
579    pub substrate_coupling: f64,
580    /// Defect scattering mean free path (nm).
581    pub defect_mfp_nm: f64,
582    /// Grain size in polycrystalline graphene (nm).
583    pub grain_size_nm: f64,
584}
585impl NanoGrapheneThermal {
586    /// Suspended graphene thermal model.
587    pub fn suspended() -> Self {
588        NanoGrapheneThermal {
589            k_bulk: 5000.0,
590            substrate_coupling: 0.0,
591            defect_mfp_nm: 1000.0,
592            grain_size_nm: 1000.0,
593        }
594    }
595    /// Graphene on SiO₂ substrate.
596    pub fn on_sio2() -> Self {
597        NanoGrapheneThermal {
598            k_bulk: 5000.0,
599            substrate_coupling: 50.0,
600            defect_mfp_nm: 500.0,
601            grain_size_nm: 500.0,
602        }
603    }
604    /// Effective phonon mean free path (nm) with defect and grain boundary scattering.
605    pub fn effective_mfp_nm(&self, intrinsic_mfp: f64) -> f64 {
606        1.0 / (1.0 / intrinsic_mfp + 1.0 / self.defect_mfp_nm + 1.0 / self.grain_size_nm)
607    }
608    /// Effective in-plane thermal conductivity (W/m·K).
609    pub fn effective_conductivity(&self, intrinsic_mfp: f64) -> f64 {
610        let mfp_eff = self.effective_mfp_nm(intrinsic_mfp);
611        self.k_bulk * mfp_eff / intrinsic_mfp
612    }
613    /// Out-of-plane thermal resistance due to substrate (K·m²/W) for film thickness hf (nm).
614    pub fn substrate_thermal_resistance(&self, hf_nm: f64) -> f64 {
615        if self.substrate_coupling < 1.0e-15 {
616            return f64::INFINITY;
617        }
618        let hf = hf_nm * 1.0e-9;
619        1.0 / (self.substrate_coupling * 1.0e9 * hf)
620    }
621}
622/// Molecular crystal elastic properties from bond stiffness.
623#[derive(Debug, Clone)]
624pub struct MolecularCrystal {
625    /// Unit cell lattice parameters \[a, b, c\] in Angstroms.
626    pub lattice: [f64; 3],
627    /// Bond stiffness k (N/m).
628    pub bond_stiffness: f64,
629    /// Number of bonds per unit cell.
630    pub bonds_per_cell: u32,
631    /// Unit cell volume (m³).
632    pub cell_volume_m3: f64,
633}
634impl MolecularCrystal {
635    /// Creates a new molecular crystal model.
636    pub fn new(lattice: [f64; 3], bond_stiffness: f64, bonds_per_cell: u32) -> Self {
637        let vol = lattice[0] * lattice[1] * lattice[2] * 1e-30;
638        Self {
639            lattice,
640            bond_stiffness,
641            bonds_per_cell,
642            cell_volume_m3: vol,
643        }
644    }
645    /// Estimated Young's modulus from bond stiffness: E ≈ k * n / V^(1/3).
646    pub fn youngs_modulus(&self) -> f64 {
647        let vol_13 = self.cell_volume_m3.cbrt();
648        self.bond_stiffness * self.bonds_per_cell as f64 / vol_13
649    }
650    /// Compressibility β = V / (dV/dp) ≈ 1 / (n * k / V).
651    pub fn compressibility(&self) -> f64 {
652        let e = self.youngs_modulus();
653        if e < 1e-15 {
654            return f64::INFINITY;
655        }
656        1.0 / e
657    }
658}
659/// Size-dependent material strengthening and scaling models.
660///
661/// Covers Hall-Petch grain size strengthening and Weibull size scaling.
662#[derive(Debug, Clone)]
663pub struct SizeEffect {
664    /// Hall-Petch coefficient k_y (Pa·m^0.5).
665    pub hall_petch_k: f64,
666    /// Friction stress σ0 (Pa).
667    pub friction_stress: f64,
668    /// Weibull modulus m (dimensionless).
669    pub weibull_m: f64,
670    /// Reference volume V0 (m³).
671    pub reference_volume: f64,
672    /// Reference strength σ0_w at V0 (Pa).
673    pub reference_strength: f64,
674}
675impl SizeEffect {
676    /// Creates a new size effect model.
677    pub fn new(
678        hall_petch_k: f64,
679        friction_stress: f64,
680        weibull_m: f64,
681        reference_volume: f64,
682        reference_strength: f64,
683    ) -> Self {
684        Self {
685            hall_petch_k,
686            friction_stress,
687            weibull_m,
688            reference_volume,
689            reference_strength,
690        }
691    }
692    /// Hall-Petch yield strength: σ_y = σ0 + k_y / sqrt(d).
693    pub fn hall_petch_strength(&self, grain_diameter_m: f64) -> f64 {
694        self.friction_stress + self.hall_petch_k / grain_diameter_m.sqrt()
695    }
696    /// Weibull size-scaled strength: σ_v = σ0 * (V0/V)^(1/m).
697    pub fn weibull_strength(&self, volume_m3: f64) -> f64 {
698        self.reference_strength * (self.reference_volume / volume_m3).powf(1.0 / self.weibull_m)
699    }
700    /// Weibull survival probability P_s = exp(-(V/V0) * (σ/σ0)^m).
701    pub fn weibull_survival_probability(&self, stress: f64, volume: f64) -> f64 {
702        let ratio = stress / self.reference_strength;
703        (-(volume / self.reference_volume) * ratio.powf(self.weibull_m)).exp()
704    }
705}
706/// Freely jointed chain (FJC) model for polymer elasticity.
707///
708/// Provides the Langevin-based force-extension relation and Flory-Huggins
709/// mixing free energy.
710#[derive(Debug, Clone)]
711pub struct PolymerChain {
712    /// Number of statistical segments N.
713    pub n_segments: u32,
714    /// Kuhn segment length b (m).
715    pub segment_length: f64,
716    /// Persistence length l_p (m).
717    pub persistence_length: f64,
718    /// Temperature (K).
719    pub temperature: f64,
720}
721impl PolymerChain {
722    /// Boltzmann constant (J/K).
723    const KB: f64 = 1.380_649e-23;
724    /// Creates a new polymer chain model.
725    pub fn new(
726        n_segments: u32,
727        segment_length: f64,
728        persistence_length: f64,
729        temperature: f64,
730    ) -> Self {
731        Self {
732            n_segments,
733            segment_length,
734            persistence_length,
735            temperature,
736        }
737    }
738    /// Contour length L0 = N * b.
739    pub fn contour_length(&self) -> f64 {
740        self.n_segments as f64 * self.segment_length
741    }
742    /// Root-mean-square end-to-end distance R_rms = b * sqrt(N).
743    pub fn rms_end_to_end(&self) -> f64 {
744        self.segment_length * (self.n_segments as f64).sqrt()
745    }
746    /// Langevin entropic force-extension relation F(r) ≈ (kT/b) * L^{-1}(r/L0).
747    ///
748    /// Uses Pade approximation for inverse Langevin: L^{-1}(x) ≈ x(3-x^2)/(1-x^2).
749    pub fn entropic_force(&self, extension: f64) -> f64 {
750        let l0 = self.contour_length();
751        let x = (extension / l0).clamp(-0.999, 0.999);
752        let inv_lang = x * (3.0 - x * x) / (1.0 - x * x);
753        Self::KB * self.temperature / self.segment_length * inv_lang
754    }
755    /// Flory-Huggins free energy of mixing per lattice site.
756    ///
757    /// ΔF_mix = kT * \[φ/N * ln(φ) + (1-φ) * ln(1-φ) + χ * φ * (1-φ)\].
758    pub fn flory_huggins_free_energy(&self, phi: f64, chi: f64) -> f64 {
759        let phi = phi.clamp(1e-10, 1.0 - 1e-10);
760        let n = self.n_segments as f64;
761        Self::KB
762            * self.temperature
763            * (phi / n * phi.ln() + (1.0 - phi) * (1.0 - phi).ln() + chi * phi * (1.0 - phi))
764    }
765}
766/// Fiber-matrix composite properties via micromechanics.
767///
768/// Implements the rule of mixtures and Halpin-Tsai equations.
769#[derive(Debug, Clone)]
770pub struct FiberMatrix {
771    /// Fiber volume fraction Vf (0 to 1).
772    pub vf: f64,
773    /// Fiber longitudinal modulus Ef (Pa).
774    pub ef: f64,
775    /// Matrix modulus Em (Pa).
776    pub em: f64,
777    /// Fiber Poisson's ratio νf.
778    pub nu_f: f64,
779    /// Matrix Poisson's ratio νm.
780    pub nu_m: f64,
781}
782impl FiberMatrix {
783    /// Creates a new fiber-matrix system.
784    pub fn new(vf: f64, ef: f64, em: f64, nu_f: f64, nu_m: f64) -> Self {
785        let vf = vf.clamp(0.0, 1.0);
786        Self {
787            vf,
788            ef,
789            em,
790            nu_f,
791            nu_m,
792        }
793    }
794    /// Matrix volume fraction Vm = 1 - Vf.
795    pub fn vm(&self) -> f64 {
796        1.0 - self.vf
797    }
798    /// Longitudinal modulus E1 (rule of mixtures).
799    pub fn e1(&self) -> f64 {
800        self.ef * self.vf + self.em * self.vm()
801    }
802    /// Transverse modulus E2 (inverse rule of mixtures).
803    pub fn e2_rom(&self) -> f64 {
804        self.ef * self.em / (self.ef * self.vm() + self.em * self.vf)
805    }
806    /// Transverse modulus E2 from Halpin-Tsai equations.
807    ///
808    /// Uses ξ = 2 for circular fibers.
809    pub fn e2_halpin_tsai(&self) -> f64 {
810        let xi = 2.0;
811        let eta = (self.ef / self.em - 1.0) / (self.ef / self.em + xi);
812        self.em * (1.0 + xi * eta * self.vf) / (1.0 - eta * self.vf)
813    }
814    /// Longitudinal Poisson's ratio ν12 (rule of mixtures).
815    pub fn nu12(&self) -> f64 {
816        self.nu_f * self.vf + self.nu_m * self.vm()
817    }
818    /// Shear modulus G12 (Halpin-Tsai).
819    pub fn g12_halpin_tsai(&self) -> f64 {
820        let gf = self.ef / (2.0 * (1.0 + self.nu_f));
821        let gm = self.em / (2.0 * (1.0 + self.nu_m));
822        let xi = 1.0;
823        let eta = (gf / gm - 1.0) / (gf / gm + xi);
824        gm * (1.0 + xi * eta * self.vf) / (1.0 - eta * self.vf)
825    }
826}
827/// Carbon nanotube buckling analysis under axial and bending loads.
828pub struct CntBuckling {
829    /// CNT diameter d (nm).
830    pub diameter_nm: f64,
831    /// Wall thickness t (nm), typically 0.34 nm.
832    pub wall_thickness_nm: f64,
833    /// Young's modulus E (TPa).
834    pub youngs_modulus_tpa: f64,
835    /// CNT length L (nm).
836    pub length_nm: f64,
837}
838impl CntBuckling {
839    /// Create a standard SWCNT buckling model.
840    pub fn swcnt(diameter_nm: f64, length_nm: f64) -> Self {
841        CntBuckling {
842            diameter_nm,
843            wall_thickness_nm: 0.34,
844            youngs_modulus_tpa: 1.0,
845            length_nm,
846        }
847    }
848    /// Moment of inertia I (nm⁴) for thin-walled cylinder.
849    pub fn moment_of_inertia(&self) -> f64 {
850        let r = self.diameter_nm / 2.0;
851        let t = self.wall_thickness_nm;
852        std::f64::consts::PI * r.powi(3) * t
853    }
854    /// Cross-sectional area A (nm²).
855    pub fn cross_section_area(&self) -> f64 {
856        std::f64::consts::PI * self.diameter_nm * self.wall_thickness_nm
857    }
858    /// Euler critical buckling load P_cr (nN) for pin-pin boundary conditions.
859    pub fn euler_buckling_load(&self) -> f64 {
860        let e_gpa = self.youngs_modulus_tpa * 1000.0;
861        let i_nm4 = self.moment_of_inertia();
862        let l = self.length_nm;
863        std::f64::consts::PI.powi(2) * e_gpa * i_nm4 / (l * l)
864    }
865    /// Shell buckling critical stress (MPa) for thin cylindrical shell.
866    /// σ_cr = E*t / (r * sqrt(3*(1-ν²)))
867    pub fn shell_buckling_stress(&self) -> f64 {
868        let r = self.diameter_nm / 2.0;
869        let t = self.wall_thickness_nm;
870        let e_gpa = self.youngs_modulus_tpa * 1000.0;
871        let nu: f64 = 0.19;
872        e_gpa * t / (r * (3.0 * (1.0 - nu * nu)).sqrt())
873    }
874    /// Slenderness ratio L/r for the CNT.
875    pub fn slenderness_ratio(&self) -> f64 {
876        let r_gyration = (self.moment_of_inertia() / self.cross_section_area()).sqrt();
877        self.length_nm / r_gyration
878    }
879    /// Critical strain ε_cr for axial buckling.
880    pub fn critical_strain(&self) -> f64 {
881        let r = self.diameter_nm / 2.0;
882        let t = self.wall_thickness_nm;
883        let c = (3.0 * (1.0 - 0.19_f64.powi(2))).sqrt();
884        t / (r * c)
885    }
886}
887/// Biological material model: collagen fibers, bone composite, tissue.
888#[derive(Debug, Clone)]
889pub struct BioMaterial {
890    /// Collagen fiber volume fraction (0–1).
891    pub collagen_fraction: f64,
892    /// Mineral (hydroxyapatite) volume fraction (0–1).
893    pub mineral_fraction: f64,
894    /// Collagen fiber modulus (Pa).
895    pub collagen_modulus: f64,
896    /// Mineral modulus (Pa).
897    pub mineral_modulus: f64,
898    /// Tissue Mooney-Rivlin parameter C1 (Pa).
899    pub c1: f64,
900    /// Tissue Mooney-Rivlin parameter C2 (Pa).
901    pub c2: f64,
902    /// Remodeling rate constant (s^-1).
903    pub remodeling_rate: f64,
904}
905impl BioMaterial {
906    /// Creates a cortical bone material model.
907    pub fn cortical_bone() -> Self {
908        Self {
909            collagen_fraction: 0.35,
910            mineral_fraction: 0.45,
911            collagen_modulus: 1.5e9,
912            mineral_modulus: 114e9,
913            c1: 1e5,
914            c2: 1e4,
915            remodeling_rate: 1e-8,
916        }
917    }
918    /// Creates a soft tissue (cartilage-like) model.
919    pub fn soft_tissue() -> Self {
920        Self {
921            collagen_fraction: 0.15,
922            mineral_fraction: 0.0,
923            collagen_modulus: 0.5e9,
924            mineral_modulus: 0.0,
925            c1: 1e4,
926            c2: 5e3,
927            remodeling_rate: 1e-7,
928        }
929    }
930    /// Effective modulus via Voigt rule of mixtures.
931    pub fn effective_modulus(&self) -> f64 {
932        self.collagen_fraction * self.collagen_modulus
933            + self.mineral_fraction * self.mineral_modulus
934    }
935    /// Mooney-Rivlin strain energy W = C1*(I1-3) + C2*(I2-3).
936    ///
937    /// I1 = λ1² + λ2² + λ3² (first invariant).
938    pub fn mooney_rivlin_energy(&self, i1: f64, i2: f64) -> f64 {
939        self.c1 * (i1 - 3.0) + self.c2 * (i2 - 3.0)
940    }
941    /// Remodeling stimulus: if strain energy W exceeds threshold, increase rate.
942    pub fn remodeling_stimulus(&self, strain_energy: f64, threshold: f64) -> f64 {
943        if strain_energy > threshold {
944            self.remodeling_rate * (strain_energy - threshold)
945        } else {
946            0.0
947        }
948    }
949}
950/// Surface-to-volume ratio effects: Hall-Petch breakdown and surface stress.
951pub struct SurfaceToVolumeEffects {
952    /// Hall-Petch slope k_HP (MPa·μm^0.5).
953    pub k_hp: f64,
954    /// Friction stress σ_0 (MPa).
955    pub sigma_0: f64,
956    /// Hall-Petch breakdown grain size d_0 (nm).
957    pub d_breakdown_nm: f64,
958    /// Surface energy γ (J/m²).
959    pub surface_energy: f64,
960    /// Surface stress τ_s (N/m).
961    pub surface_stress: f64,
962    /// Atom diameter d_atom (nm).
963    pub d_atom_nm: f64,
964}
965impl SurfaceToVolumeEffects {
966    /// Nanocrystalline copper model.
967    pub fn nc_copper() -> Self {
968        SurfaceToVolumeEffects {
969            k_hp: 145.0,
970            sigma_0: 25.0,
971            d_breakdown_nm: 15.0,
972            surface_energy: 1.7,
973            surface_stress: 1.5,
974            d_atom_nm: 0.256,
975        }
976    }
977    /// Nanocrystalline iron model.
978    pub fn nc_iron() -> Self {
979        SurfaceToVolumeEffects {
980            k_hp: 600.0,
981            sigma_0: 100.0,
982            d_breakdown_nm: 20.0,
983            surface_energy: 2.4,
984            surface_stress: 2.0,
985            d_atom_nm: 0.248,
986        }
987    }
988    /// Hall-Petch yield strength (MPa). Below d_breakdown, returns softening estimate.
989    pub fn yield_strength_mpa(&self, grain_size_nm: f64) -> f64 {
990        if grain_size_nm >= self.d_breakdown_nm {
991            self.sigma_0 + self.k_hp / grain_size_nm.sqrt()
992        } else {
993            let sigma_peak = self.sigma_0 + self.k_hp / self.d_breakdown_nm.sqrt();
994            sigma_peak * (grain_size_nm / self.d_breakdown_nm)
995        }
996    }
997    /// Surface-to-volume ratio for a sphere of diameter d (nm): S/V = 6/d.
998    pub fn surface_to_volume_ratio(&self, diameter_nm: f64) -> f64 {
999        6.0 / diameter_nm
1000    }
1001    /// Fraction of surface atoms in a spherical nanoparticle.
1002    pub fn surface_atom_fraction(&self, diameter_nm: f64) -> f64 {
1003        let n_surface_fraction = self.d_atom_nm / diameter_nm * 6.0;
1004        n_surface_fraction.min(1.0)
1005    }
1006    /// Capillary pressure (MPa) inside a spherical nanoparticle.
1007    pub fn capillary_pressure_mpa(&self, radius_nm: f64) -> f64 {
1008        2.0 * self.surface_stress / (radius_nm * 1.0e-9) / 1.0e6
1009    }
1010    /// Melting point depression ΔTm (K) via Gibbs-Thomson: ΔTm = 4*γ*Tm/(ΔHf*ρ*d).
1011    pub fn melting_point_depression(&self, t_m_bulk: f64, h_f: f64, rho: f64, d_nm: f64) -> f64 {
1012        4.0 * self.surface_energy * t_m_bulk / (h_f * rho * d_nm * 1.0e-9)
1013    }
1014}
1015/// Grain boundary mechanics: Read-Shockley energy, misorientation-dependent strength.
1016pub struct GrainBoundaryMechanics {
1017    /// Surface energy scale factor E_0 (J/m²).
1018    pub e0: f64,
1019    /// Critical misorientation angle θ_m (degrees) — typically 15°.
1020    pub theta_m: f64,
1021    /// Grain boundary diffusivity pre-exponential D_0gb (m²/s).
1022    pub d0_gb: f64,
1023    /// GB diffusion activation energy Q_gb (J/mol).
1024    pub q_gb: f64,
1025    /// Grain boundary width δ (nm).
1026    pub delta_nm: f64,
1027}
1028impl GrainBoundaryMechanics {
1029    /// Aluminum grain boundary model.
1030    pub fn aluminum_gb() -> Self {
1031        GrainBoundaryMechanics {
1032            e0: 0.32,
1033            theta_m: 15.0,
1034            d0_gb: 2.0e-7,
1035            q_gb: 84_000.0,
1036            delta_nm: 0.5,
1037        }
1038    }
1039    /// Nickel grain boundary model.
1040    pub fn nickel_gb() -> Self {
1041        GrainBoundaryMechanics {
1042            e0: 0.69,
1043            theta_m: 15.0,
1044            d0_gb: 1.7e-9,
1045            q_gb: 115_000.0,
1046            delta_nm: 0.5,
1047        }
1048    }
1049    /// Read-Shockley grain boundary energy (J/m²).
1050    /// E_gb = E_0 * θ/θ_m * (1 - ln(θ/θ_m)) for θ < θ_m.
1051    pub fn read_shockley_energy(&self, misorientation_deg: f64) -> f64 {
1052        let theta_m_rad = self.theta_m.to_radians();
1053        let theta_rad = misorientation_deg.to_radians().min(theta_m_rad);
1054        let ratio = theta_rad / theta_m_rad;
1055        if ratio < 1.0e-10 {
1056            return 0.0;
1057        }
1058        self.e0 * ratio * (1.0 - ratio.ln())
1059    }
1060    /// High-angle GB energy (≈ constant for θ > θ_m) (J/m²).
1061    pub fn high_angle_energy(&self) -> f64 {
1062        self.e0
1063    }
1064    /// Grain boundary diffusivity at temperature T (K).
1065    pub fn gb_diffusivity(&self, t_kelvin: f64) -> f64 {
1066        const R: f64 = 8.314;
1067        self.d0_gb * (-self.q_gb / (R * t_kelvin)).exp()
1068    }
1069    /// Effective GB diffusion flux (m²/s·m) = D_gb * δ.
1070    pub fn gb_diffusion_flux(&self, t_kelvin: f64) -> f64 {
1071        self.gb_diffusivity(t_kelvin) * self.delta_nm * 1.0e-9
1072    }
1073    /// Coble creep rate ε̇ (s⁻¹) for grain size d (m).
1074    pub fn coble_creep_rate(&self, sigma: f64, d_m: f64, t_kelvin: f64, omega: f64) -> f64 {
1075        const R: f64 = 8.314;
1076        let d_gb = self.gb_diffusivity(t_kelvin);
1077        let delta = self.delta_nm * 1.0e-9;
1078        148.0 * sigma * d_gb * delta * omega / (R * t_kelvin * d_m.powi(3))
1079    }
1080}
1081/// Thin film mechanics: biaxial stress, Stoney equation, delamination.
1082pub struct ThinFilmMechanics {
1083    /// Film Young's modulus Ef (GPa).
1084    pub ef: f64,
1085    /// Film Poisson's ratio νf.
1086    pub nu_f: f64,
1087    /// Film thickness hf (nm).
1088    pub hf_nm: f64,
1089    /// Film CTE αf (1/K).
1090    pub cte_film: f64,
1091    /// Substrate Young's modulus Es (GPa).
1092    pub es: f64,
1093    /// Substrate Poisson's ratio νs.
1094    pub nu_s: f64,
1095    /// Substrate thickness hs (mm).
1096    pub hs_mm: f64,
1097    /// Substrate CTE αs (1/K).
1098    pub cte_substrate: f64,
1099    /// Film-substrate interface fracture toughness Gc (J/m²).
1100    pub gc: f64,
1101}
1102impl ThinFilmMechanics {
1103    /// TiN film on silicon substrate.
1104    pub fn tin_on_silicon(hf_nm: f64) -> Self {
1105        ThinFilmMechanics {
1106            ef: 450.0,
1107            nu_f: 0.25,
1108            hf_nm,
1109            cte_film: 9.4e-6,
1110            es: 130.0,
1111            nu_s: 0.28,
1112            hs_mm: 0.725,
1113            cte_substrate: 2.6e-6,
1114            gc: 5.0,
1115        }
1116    }
1117    /// Cu film on SiO₂/Si for interconnect.
1118    pub fn cu_on_sio2(hf_nm: f64) -> Self {
1119        ThinFilmMechanics {
1120            ef: 130.0,
1121            nu_f: 0.34,
1122            hf_nm,
1123            cte_film: 16.5e-6,
1124            es: 73.0,
1125            nu_s: 0.17,
1126            hs_mm: 0.725,
1127            cte_substrate: 0.55e-6,
1128            gc: 2.0,
1129        }
1130    }
1131    /// Biaxial modulus M_f = Ef / (1 - νf).
1132    pub fn biaxial_modulus(&self) -> f64 {
1133        self.ef / (1.0 - self.nu_f)
1134    }
1135    /// Biaxial thermal mismatch stress σ_f (MPa) for temperature change ΔT.
1136    pub fn thermal_mismatch_stress_mpa(&self, delta_t: f64) -> f64 {
1137        let delta_cte = self.cte_film - self.cte_substrate;
1138        -self.biaxial_modulus() * delta_cte * delta_t * 1000.0
1139    }
1140    /// Stoney equation: substrate curvature κ (1/m) from film stress.
1141    pub fn stoney_curvature(&self, sigma_f_mpa: f64) -> f64 {
1142        let hf = self.hf_nm * 1.0e-9;
1143        let hs = self.hs_mm * 1.0e-3;
1144        let m_s = self.es / (1.0 - self.nu_s);
1145        let sigma_f_pa = sigma_f_mpa * 1.0e6;
1146        6.0 * sigma_f_pa * hf / (m_s * 1.0e9 * hs.powi(2))
1147    }
1148    /// Wafer bow (μm) from Stoney curvature for wafer radius r_wafer (mm).
1149    pub fn wafer_bow_um(&self, sigma_f_mpa: f64, r_wafer_mm: f64) -> f64 {
1150        let kappa = self.stoney_curvature(sigma_f_mpa);
1151        let r = r_wafer_mm * 1.0e-3;
1152        kappa * r.powi(2) / 2.0 * 1.0e6
1153    }
1154    /// Energy release rate G for channel cracking (Beuth solution).
1155    /// G ≈ Z * σ² * hf / Ef where Z ≈ 2 for channel crack.
1156    pub fn channel_crack_erg(&self, sigma_mpa: f64) -> f64 {
1157        const Z: f64 = 2.0;
1158        let sigma_pa = sigma_mpa * 1.0e6;
1159        let hf = self.hf_nm * 1.0e-9;
1160        let ef_pa = self.ef * 1.0e9;
1161        Z * sigma_pa.powi(2) * hf / ef_pa
1162    }
1163    /// Delamination driving force G_del (J/m²) for thin film buckling.
1164    pub fn delamination_energy(&self, sigma_mpa: f64) -> f64 {
1165        let sigma_pa = sigma_mpa * 1.0e6;
1166        let hf = self.hf_nm * 1.0e-9;
1167        let ef_pa = self.ef * 1.0e9;
1168        (1.0 - self.nu_f) * sigma_pa.powi(2) * hf / ef_pa
1169    }
1170}
1171/// Nanoindentation analysis using Oliver-Pharr method with pile-up correction.
1172pub struct NanoindentationOliverPharr {
1173    /// Indenter tip radius R (nm) for Berkovich tip.
1174    pub tip_radius_nm: f64,
1175    /// Indenter elastic modulus E_i (GPa), diamond = 1141 GPa.
1176    pub e_indenter: f64,
1177    /// Indenter Poisson's ratio ν_i.
1178    pub nu_indenter: f64,
1179    /// Berkovich geometric constant C = 24.5.
1180    pub c_geom: f64,
1181}
1182impl NanoindentationOliverPharr {
1183    /// Standard Berkovich diamond indenter.
1184    pub fn berkovich_diamond() -> Self {
1185        NanoindentationOliverPharr {
1186            tip_radius_nm: 50.0,
1187            e_indenter: 1141.0,
1188            nu_indenter: 0.07,
1189            c_geom: 24.5,
1190        }
1191    }
1192    /// Cube-corner indenter.
1193    pub fn cube_corner() -> Self {
1194        NanoindentationOliverPharr {
1195            tip_radius_nm: 40.0,
1196            e_indenter: 1141.0,
1197            nu_indenter: 0.07,
1198            c_geom: 2.598,
1199        }
1200    }
1201    /// Reduced modulus E_r from measured stiffness S and contact area A.
1202    /// E_r = S * sqrt(π) / (2 * sqrt(A)).
1203    pub fn reduced_modulus(&self, stiffness_n_per_nm: f64, contact_area_nm2: f64) -> f64 {
1204        stiffness_n_per_nm * std::f64::consts::PI.sqrt() / (2.0 * contact_area_nm2.sqrt())
1205    }
1206    /// Sample Young's modulus from reduced modulus.
1207    /// 1/E_r = (1-ν²)/E + (1-ν_i²)/E_i
1208    pub fn sample_youngs_modulus(&self, e_r_gpa: f64, nu_sample: f64) -> f64 {
1209        let inv_er = 1.0 / e_r_gpa;
1210        let inv_ei = (1.0 - self.nu_indenter.powi(2)) / self.e_indenter;
1211        (1.0 - nu_sample.powi(2)) / (inv_er - inv_ei)
1212    }
1213    /// Projected contact area from displacement (Oliver-Pharr).
1214    /// A = C * hc² where hc = hmax - 0.75 * Pmax / S.
1215    pub fn contact_area_nm2(&self, h_max_nm: f64, p_max_n: f64, stiffness_n_per_nm: f64) -> f64 {
1216        let h_c = h_max_nm - 0.75 * p_max_n / stiffness_n_per_nm;
1217        self.c_geom * h_c.max(0.0).powi(2)
1218    }
1219    /// Hardness H (GPa) = Pmax / A.
1220    pub fn hardness_gpa(&self, p_max_n: f64, contact_area_nm2: f64) -> f64 {
1221        if contact_area_nm2 < 1.0e-30 {
1222            return 0.0;
1223        }
1224        p_max_n / (contact_area_nm2 * 1.0e-18) / 1.0e9
1225    }
1226    /// Pile-up correction factor f_pu using elastic-perfectly-plastic assumption.
1227    /// f_pu = 1 + (E_r / H * 0.1) (simplified).
1228    pub fn pile_up_correction(&self, e_r_gpa: f64, h_gpa: f64) -> f64 {
1229        if h_gpa < 1.0e-10 {
1230            return 1.0;
1231        }
1232        1.0 + 0.1 * e_r_gpa / h_gpa
1233    }
1234}
1235/// Graphene sheet properties (monolayer or bilayer).
1236#[derive(Debug, Clone)]
1237pub struct GrapheneSheet {
1238    /// Number of layers (1 = monolayer, 2 = bilayer, etc.).
1239    pub n_layers: u32,
1240    /// In-plane Young's modulus (TPa).
1241    pub youngs_modulus_tpa: f64,
1242    /// In-plane tensile strength (GPa).
1243    pub tensile_strength_gpa: f64,
1244    /// Thermal conductivity (W/m/K).
1245    pub thermal_conductivity: f64,
1246    /// Vacancy defect fraction (0 = perfect, 1 = fully defected).
1247    pub vacancy_fraction: f64,
1248}
1249impl GrapheneSheet {
1250    /// Creates a perfect monolayer graphene sheet.
1251    pub fn monolayer() -> Self {
1252        Self {
1253            n_layers: 1,
1254            youngs_modulus_tpa: 1.0,
1255            tensile_strength_gpa: 130.0,
1256            thermal_conductivity: 5000.0,
1257            vacancy_fraction: 0.0,
1258        }
1259    }
1260    /// Creates a bilayer graphene sheet (AB stacking).
1261    pub fn bilayer() -> Self {
1262        Self {
1263            n_layers: 2,
1264            youngs_modulus_tpa: 0.98,
1265            tensile_strength_gpa: 120.0,
1266            thermal_conductivity: 4000.0,
1267            vacancy_fraction: 0.0,
1268        }
1269    }
1270    /// Effective Young's modulus accounting for vacancy defects.
1271    ///
1272    /// Uses a simple rule: E_eff = E0 * (1 - alpha * vacancy_fraction)^2.
1273    pub fn effective_youngs_modulus(&self) -> f64 {
1274        let alpha = 2.5;
1275        let frac = self.vacancy_fraction.clamp(0.0, 1.0);
1276        self.youngs_modulus_tpa * (1.0 - alpha * frac).max(0.0).powi(2)
1277    }
1278    /// Fracture strain reduction from vacancies (empirical model).
1279    pub fn fracture_strain(&self) -> f64 {
1280        let base_strain = 0.25;
1281        base_strain * (1.0 - self.vacancy_fraction.clamp(0.0, 1.0)).powi(2)
1282    }
1283    /// Thermal conductivity (simple layer scaling).
1284    pub fn effective_thermal_conductivity(&self) -> f64 {
1285        self.thermal_conductivity / (1.0 + 0.1 * (self.n_layers as f64 - 1.0))
1286    }
1287}
1288/// A single ply in a composite laminate.
1289#[derive(Debug, Clone)]
1290pub struct Ply {
1291    /// Ply thickness (m).
1292    pub thickness: f64,
1293    /// Fiber orientation angle (radians).
1294    pub angle: f64,
1295    /// Longitudinal Young's modulus E1 (Pa).
1296    pub e1: f64,
1297    /// Transverse Young's modulus E2 (Pa).
1298    pub e2: f64,
1299    /// In-plane shear modulus G12 (Pa).
1300    pub g12: f64,
1301    /// Major Poisson's ratio ν12.
1302    pub nu12: f64,
1303}
1304impl Ply {
1305    /// Creates a new ply.
1306    pub fn new(thickness: f64, angle_deg: f64, e1: f64, e2: f64, g12: f64, nu12: f64) -> Self {
1307        Self {
1308            thickness,
1309            angle: angle_deg.to_radians(),
1310            e1,
1311            e2,
1312            g12,
1313            nu12,
1314        }
1315    }
1316    /// Minor Poisson's ratio ν21 = ν12 * E2 / E1.
1317    pub fn nu21(&self) -> f64 {
1318        self.nu12 * self.e2 / self.e1
1319    }
1320    /// Reduced stiffness matrix Q11, Q12, Q22, Q66 in principal axes.
1321    pub fn reduced_stiffness(&self) -> [f64; 4] {
1322        let denom = 1.0 - self.nu12 * self.nu21();
1323        let q11 = self.e1 / denom;
1324        let q12 = self.nu12 * self.e2 / denom;
1325        let q22 = self.e2 / denom;
1326        let q66 = self.g12;
1327        [q11, q12, q22, q66]
1328    }
1329    /// Transformed stiffness Qbar11 in the laminate frame.
1330    pub fn transformed_q11(&self) -> f64 {
1331        let [q11, q12, q22, q66] = self.reduced_stiffness();
1332        let c = self.angle.cos();
1333        let s = self.angle.sin();
1334        let c2 = c * c;
1335        let s2 = s * s;
1336        let c4 = c2 * c2;
1337        let s4 = s2 * s2;
1338        q11 * c4 + 2.0 * (q12 + 2.0 * q66) * s2 * c2 + q22 * s4
1339    }
1340}
1341/// Nanoscale phonon heat transport: Fourier breakdown, Boltzmann transport.
1342pub struct PhononTransport {
1343    /// Phonon mean free path Λ (nm) at 300 K.
1344    pub mean_free_path_nm: f64,
1345    /// Debye temperature θ_D (K).
1346    pub debye_temp: f64,
1347    /// Room-temperature thermal conductivity k_bulk (W/m·K).
1348    pub k_bulk: f64,
1349    /// Phonon group velocity v_g (m/s).
1350    pub v_group: f64,
1351    /// Volumetric heat capacity ρ*Cv (J/m³·K).
1352    pub rho_cv: f64,
1353}
1354impl PhononTransport {
1355    /// Silicon phonon transport properties.
1356    pub fn silicon() -> Self {
1357        PhononTransport {
1358            mean_free_path_nm: 300.0,
1359            debye_temp: 645.0,
1360            k_bulk: 148.0,
1361            v_group: 6400.0,
1362            rho_cv: 1.63e6,
1363        }
1364    }
1365    /// Germanium phonon transport properties.
1366    pub fn germanium() -> Self {
1367        PhononTransport {
1368            mean_free_path_nm: 200.0,
1369            debye_temp: 360.0,
1370            k_bulk: 60.0,
1371            v_group: 3900.0,
1372            rho_cv: 1.66e6,
1373        }
1374    }
1375    /// Knudsen number Kn = Λ / L for characteristic length L (nm).
1376    pub fn knudsen_number(&self, length_nm: f64) -> f64 {
1377        self.mean_free_path_nm / length_nm
1378    }
1379    /// Effective thermal conductivity accounting for ballistic-diffusive crossover.
1380    /// k_eff = k_bulk / (1 + Kn) (Matthiessen's rule approximation).
1381    pub fn effective_conductivity(&self, length_nm: f64) -> f64 {
1382        let kn = self.knudsen_number(length_nm);
1383        self.k_bulk / (1.0 + kn)
1384    }
1385    /// Interface thermal resistance (Kapitza resistance) R_K (m²·K/W).
1386    /// Using diffuse mismatch model approximation.
1387    pub fn kapitza_resistance_dmm(&self, k2: f64) -> f64 {
1388        4.0 * (1.0 / self.k_bulk + 1.0 / k2) / self.v_group
1389    }
1390    /// Ballistic phonon heat flux (W/m²) in quasi-ballistic regime.
1391    /// q_bal = 0.5 * ρ*Cv * v_g * ΔT.
1392    pub fn ballistic_heat_flux(&self, delta_t: f64) -> f64 {
1393        0.5 * self.rho_cv * self.v_group * delta_t
1394    }
1395    /// Temperature-dependent conductivity k(T) ∝ 1/T above Debye temperature.
1396    pub fn conductivity_at_temp(&self, t_k: f64) -> f64 {
1397        if t_k < self.debye_temp {
1398            self.k_bulk * (self.debye_temp / t_k).powf(0.5)
1399        } else {
1400            self.k_bulk * self.debye_temp / t_k
1401        }
1402    }
1403}