1#[allow(unused_imports)]
6use super::functions::*;
7#[derive(Debug, Clone)]
11pub struct InterfacialZone {
12 pub fracture_energy: f64,
14 pub strength: f64,
16 pub vdw_energy: f64,
18 pub thickness: f64,
20 pub debonded: bool,
22}
23impl InterfacialZone {
24 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 pub fn check_debond(&mut self, traction: f64) {
36 if traction >= self.strength {
37 self.debonded = true;
38 }
39 }
40 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 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#[derive(Debug, Clone)]
58pub struct CompositeLaminate {
59 pub plies: Vec<Ply>,
61}
62impl CompositeLaminate {
63 pub fn new(plies: Vec<Ply>) -> Self {
65 Self { plies }
66 }
67 pub fn total_thickness(&self) -> f64 {
69 self.plies.iter().map(|p| p.thickness).sum()
70 }
71 pub fn a11(&self) -> f64 {
75 self.plies
76 .iter()
77 .map(|p| p.transformed_q11() * p.thickness)
78 .sum()
79 }
80 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 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}
103pub struct VirialStress {
105 pub volume_m3: f64,
107 pub temperature: f64,
109 pub n_atoms: usize,
111 pub atom_mass: f64,
113}
114impl VirialStress {
115 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 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 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 #[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 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}
181pub struct QuantumDotConfinement {
183 pub radius_nm: f64,
185 pub bulk_bandgap_ev: f64,
187 pub eff_mass_electron: f64,
189 pub eff_mass_hole: f64,
191 pub dielectric_constant: f64,
193 pub designation: String,
195}
196impl QuantumDotConfinement {
197 #[allow(dead_code)]
199 const KB_EV: f64 = 8.617e-5;
200 #[allow(dead_code)]
202 const H_EV_S: f64 = 4.136e-15;
203 const M0: f64 = 9.109e-31;
205 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 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 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 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 pub fn effective_bandgap_ev(&self) -> f64 {
246 self.bulk_bandgap_ev + self.confinement_energy_ev() + self.coulomb_correction_ev()
247 }
248 pub fn emission_wavelength_nm(&self) -> f64 {
250 let eg = self.effective_bandgap_ev().max(0.01);
251 1239.8 / eg
252 }
253 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#[derive(Debug, Clone)]
264pub struct NanoparticleDispersion {
265 pub volume_fraction: f64,
267 pub ep: f64,
269 pub em: f64,
271 pub nu_p: f64,
273 pub nu_m: f64,
275}
276impl NanoparticleDispersion {
277 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 fn bulk_modulus(e: f64, nu: f64) -> f64 {
289 e / (3.0 * (1.0 - 2.0 * nu))
290 }
291 fn shear_modulus(e: f64, nu: f64) -> f64 {
293 e / (2.0 * (1.0 + nu))
294 }
295 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 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}
319pub struct GrapheneElasticStiffness {
321 pub c11: f64,
323 pub c12: f64,
325 pub c44: f64,
327 pub layer_thickness_nm: f64,
329}
330impl GrapheneElasticStiffness {
331 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 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 pub fn youngs_modulus_2d(&self) -> f64 {
351 self.c11 - self.c12 * self.c12 / self.c11
352 }
353 pub fn poisson_ratio(&self) -> f64 {
355 self.c12 / self.c11
356 }
357 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 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#[derive(Debug, Clone)]
374pub struct NanotubeProperties {
375 pub chirality: CntChirality,
377 pub n: u32,
379 pub m: u32,
381 pub diameter_nm: f64,
383 pub youngs_modulus_tpa: f64,
385 pub tensile_strength_gpa: f64,
387 pub thermal_conductivity: f64,
389}
390impl NanotubeProperties {
391 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 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 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 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 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#[derive(Debug, Clone, Copy, PartialEq)]
451pub enum CntChirality {
452 Armchair,
454 Zigzag,
456 Chiral,
458}
459#[derive(Debug, Clone)]
461pub struct ThermoelectricMaterial {
462 pub seebeck: f64,
464 pub electrical_conductivity: f64,
466 pub thermal_conductivity: f64,
468 pub temperature: f64,
470}
471impl ThermoelectricMaterial {
472 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 pub fn power_factor(&self) -> f64 {
483 self.seebeck * self.seebeck * self.electrical_conductivity
484 }
485 pub fn zt(&self) -> f64 {
487 self.power_factor() * self.temperature / self.thermal_conductivity
488 }
489 pub fn peltier_coefficient(&self) -> f64 {
491 self.seebeck * self.temperature
492 }
493 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}
503pub struct DislocationMechanics {
505 pub shear_modulus: f64,
507 pub burgers_nm: f64,
509 pub nu: f64,
511 pub lattice_parameter_nm: f64,
513 pub misfit: f64,
515}
516impl DislocationMechanics {
517 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 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 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 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 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 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 pub fn schmid_crss(&self, sigma_applied: f64, phi: f64, lambda: f64) -> f64 {
571 sigma_applied * phi.cos() * lambda.cos()
572 }
573}
574pub struct NanoGrapheneThermal {
576 pub k_bulk: f64,
578 pub substrate_coupling: f64,
580 pub defect_mfp_nm: f64,
582 pub grain_size_nm: f64,
584}
585impl NanoGrapheneThermal {
586 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 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 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 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 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#[derive(Debug, Clone)]
624pub struct MolecularCrystal {
625 pub lattice: [f64; 3],
627 pub bond_stiffness: f64,
629 pub bonds_per_cell: u32,
631 pub cell_volume_m3: f64,
633}
634impl MolecularCrystal {
635 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 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 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#[derive(Debug, Clone)]
663pub struct SizeEffect {
664 pub hall_petch_k: f64,
666 pub friction_stress: f64,
668 pub weibull_m: f64,
670 pub reference_volume: f64,
672 pub reference_strength: f64,
674}
675impl SizeEffect {
676 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 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 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 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#[derive(Debug, Clone)]
711pub struct PolymerChain {
712 pub n_segments: u32,
714 pub segment_length: f64,
716 pub persistence_length: f64,
718 pub temperature: f64,
720}
721impl PolymerChain {
722 const KB: f64 = 1.380_649e-23;
724 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 pub fn contour_length(&self) -> f64 {
740 self.n_segments as f64 * self.segment_length
741 }
742 pub fn rms_end_to_end(&self) -> f64 {
744 self.segment_length * (self.n_segments as f64).sqrt()
745 }
746 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 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#[derive(Debug, Clone)]
770pub struct FiberMatrix {
771 pub vf: f64,
773 pub ef: f64,
775 pub em: f64,
777 pub nu_f: f64,
779 pub nu_m: f64,
781}
782impl FiberMatrix {
783 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 pub fn vm(&self) -> f64 {
796 1.0 - self.vf
797 }
798 pub fn e1(&self) -> f64 {
800 self.ef * self.vf + self.em * self.vm()
801 }
802 pub fn e2_rom(&self) -> f64 {
804 self.ef * self.em / (self.ef * self.vm() + self.em * self.vf)
805 }
806 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 pub fn nu12(&self) -> f64 {
816 self.nu_f * self.vf + self.nu_m * self.vm()
817 }
818 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}
827pub struct CntBuckling {
829 pub diameter_nm: f64,
831 pub wall_thickness_nm: f64,
833 pub youngs_modulus_tpa: f64,
835 pub length_nm: f64,
837}
838impl CntBuckling {
839 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 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 pub fn cross_section_area(&self) -> f64 {
856 std::f64::consts::PI * self.diameter_nm * self.wall_thickness_nm
857 }
858 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 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 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 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#[derive(Debug, Clone)]
889pub struct BioMaterial {
890 pub collagen_fraction: f64,
892 pub mineral_fraction: f64,
894 pub collagen_modulus: f64,
896 pub mineral_modulus: f64,
898 pub c1: f64,
900 pub c2: f64,
902 pub remodeling_rate: f64,
904}
905impl BioMaterial {
906 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 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 pub fn effective_modulus(&self) -> f64 {
932 self.collagen_fraction * self.collagen_modulus
933 + self.mineral_fraction * self.mineral_modulus
934 }
935 pub fn mooney_rivlin_energy(&self, i1: f64, i2: f64) -> f64 {
939 self.c1 * (i1 - 3.0) + self.c2 * (i2 - 3.0)
940 }
941 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}
950pub struct SurfaceToVolumeEffects {
952 pub k_hp: f64,
954 pub sigma_0: f64,
956 pub d_breakdown_nm: f64,
958 pub surface_energy: f64,
960 pub surface_stress: f64,
962 pub d_atom_nm: f64,
964}
965impl SurfaceToVolumeEffects {
966 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 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 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 pub fn surface_to_volume_ratio(&self, diameter_nm: f64) -> f64 {
999 6.0 / diameter_nm
1000 }
1001 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 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 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}
1015pub struct GrainBoundaryMechanics {
1017 pub e0: f64,
1019 pub theta_m: f64,
1021 pub d0_gb: f64,
1023 pub q_gb: f64,
1025 pub delta_nm: f64,
1027}
1028impl GrainBoundaryMechanics {
1029 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 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 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 pub fn high_angle_energy(&self) -> f64 {
1062 self.e0
1063 }
1064 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 pub fn gb_diffusion_flux(&self, t_kelvin: f64) -> f64 {
1071 self.gb_diffusivity(t_kelvin) * self.delta_nm * 1.0e-9
1072 }
1073 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}
1081pub struct ThinFilmMechanics {
1083 pub ef: f64,
1085 pub nu_f: f64,
1087 pub hf_nm: f64,
1089 pub cte_film: f64,
1091 pub es: f64,
1093 pub nu_s: f64,
1095 pub hs_mm: f64,
1097 pub cte_substrate: f64,
1099 pub gc: f64,
1101}
1102impl ThinFilmMechanics {
1103 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 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 pub fn biaxial_modulus(&self) -> f64 {
1133 self.ef / (1.0 - self.nu_f)
1134 }
1135 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 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 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 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 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}
1171pub struct NanoindentationOliverPharr {
1173 pub tip_radius_nm: f64,
1175 pub e_indenter: f64,
1177 pub nu_indenter: f64,
1179 pub c_geom: f64,
1181}
1182impl NanoindentationOliverPharr {
1183 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 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 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 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 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 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 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#[derive(Debug, Clone)]
1237pub struct GrapheneSheet {
1238 pub n_layers: u32,
1240 pub youngs_modulus_tpa: f64,
1242 pub tensile_strength_gpa: f64,
1244 pub thermal_conductivity: f64,
1246 pub vacancy_fraction: f64,
1248}
1249impl GrapheneSheet {
1250 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 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 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 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 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#[derive(Debug, Clone)]
1290pub struct Ply {
1291 pub thickness: f64,
1293 pub angle: f64,
1295 pub e1: f64,
1297 pub e2: f64,
1299 pub g12: f64,
1301 pub nu12: f64,
1303}
1304impl Ply {
1305 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 pub fn nu21(&self) -> f64 {
1318 self.nu12 * self.e2 / self.e1
1319 }
1320 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 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}
1341pub struct PhononTransport {
1343 pub mean_free_path_nm: f64,
1345 pub debye_temp: f64,
1347 pub k_bulk: f64,
1349 pub v_group: f64,
1351 pub rho_cv: f64,
1353}
1354impl PhononTransport {
1355 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 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 pub fn knudsen_number(&self, length_nm: f64) -> f64 {
1377 self.mean_free_path_nm / length_nm
1378 }
1379 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 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 pub fn ballistic_heat_flux(&self, delta_t: f64) -> f64 {
1393 0.5 * self.rho_cv * self.v_group * delta_t
1394 }
1395 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}