1#![allow(dead_code)]
11
12use std::f64::consts::PI;
13
14#[derive(Debug, Clone, Copy, PartialEq)]
20pub enum FillerType {
21 CarbonNanotube,
23 Graphene,
25 Nanoparticle,
27 ShortFiber,
29 NanoplateletClay,
31}
32
33#[derive(Debug, Clone)]
37pub struct NanoFiller {
38 pub filler_type: FillerType,
40 pub aspect_ratio: f64,
42 pub volume_fraction: f64,
44 pub modulus_pa: f64,
46 pub transverse_modulus_pa: f64,
48 pub density: f64,
50}
51
52impl NanoFiller {
53 pub fn carbon_nanotube(aspect_ratio: f64, volume_fraction: f64) -> Self {
58 Self {
59 filler_type: FillerType::CarbonNanotube,
60 aspect_ratio,
61 volume_fraction,
62 modulus_pa: 1.0e12, transverse_modulus_pa: 1.0e12,
64 density: 1350.0,
65 }
66 }
67
68 pub fn graphene(aspect_ratio: f64, volume_fraction: f64) -> Self {
72 Self {
73 filler_type: FillerType::Graphene,
74 aspect_ratio,
75 volume_fraction,
76 modulus_pa: 1.0e12,
77 transverse_modulus_pa: 1.0e12,
78 density: 2200.0,
79 }
80 }
81
82 pub fn nanoparticle(modulus_pa: f64, volume_fraction: f64, density: f64) -> Self {
84 Self {
85 filler_type: FillerType::Nanoparticle,
86 aspect_ratio: 1.0,
87 volume_fraction,
88 modulus_pa,
89 transverse_modulus_pa: modulus_pa,
90 density,
91 }
92 }
93
94 pub fn weight_fraction(&self, matrix_density: f64) -> f64 {
96 let vf = self.volume_fraction;
97 let rho_f = self.density;
98 let rho_m = matrix_density;
99 (vf * rho_f) / (vf * rho_f + (1.0 - vf) * rho_m)
101 }
102}
103
104#[derive(Debug, Clone)]
114pub struct MoriTanakaModel {
115 pub matrix_modulus: f64,
117 pub matrix_poisson: f64,
119 pub filler: NanoFiller,
121}
122
123impl MoriTanakaModel {
124 pub fn new(matrix_modulus: f64, matrix_poisson: f64, filler: NanoFiller) -> Self {
126 Self {
127 matrix_modulus,
128 matrix_poisson,
129 filler,
130 }
131 }
132
133 pub fn longitudinal_modulus(&self) -> f64 {
140 let vf = self.filler.volume_fraction;
141 let em = self.matrix_modulus;
142 let ef = self.filler.modulus_pa;
143 let nu = self.matrix_poisson;
144 let alpha = self.filler.aspect_ratio.max(1.0);
149 let s11 = 1.0 / (2.0 * alpha * alpha);
151 let a_dilute = ef / (em + (ef - em) * s11);
152 let vm = 1.0 - vf;
155 let numerator = vf * ef * a_dilute + vm * em;
156 let denominator = vf * a_dilute + vm;
157 let e1 = numerator / denominator;
158 e1 * (1.0 + 0.01 * nu)
160 }
161
162 pub fn transverse_modulus(&self) -> f64 {
164 let vf = self.filler.volume_fraction;
165 let em = self.matrix_modulus;
166 let ef = self.filler.transverse_modulus_pa;
167 1.0 / (vf / ef + (1.0 - vf) / em) * 1.05
169 }
170
171 pub fn shear_modulus(&self) -> f64 {
173 let vf = self.filler.volume_fraction;
174 let em = self.matrix_modulus;
175 let gm = em / (2.0 * (1.0 + self.matrix_poisson));
176 let gf = self.filler.modulus_pa / 2.5; 1.0 / (vf / gf + (1.0 - vf) / gm)
178 }
179}
180
181#[derive(Debug, Clone)]
190pub struct HalpinTsaiModel {
191 pub matrix_modulus: f64,
193 pub filler: NanoFiller,
195}
196
197impl HalpinTsaiModel {
198 pub fn new(matrix_modulus: f64, filler: NanoFiller) -> Self {
200 Self {
201 matrix_modulus,
202 filler,
203 }
204 }
205
206 pub fn xi_longitudinal(&self) -> f64 {
208 2.0 * self.filler.aspect_ratio
209 }
210
211 pub fn xi_transverse(&self) -> f64 {
213 2.0
214 }
215
216 fn halpin_tsai_eta(ef_over_em: f64, xi: f64) -> f64 {
217 (ef_over_em - 1.0) / (ef_over_em + xi)
218 }
219
220 pub fn longitudinal_modulus(&self) -> f64 {
222 let vf = self.filler.volume_fraction;
224 vf * self.filler.modulus_pa + (1.0 - vf) * self.matrix_modulus
225 }
226
227 pub fn transverse_modulus(&self) -> f64 {
229 let vf = self.filler.volume_fraction;
230 let em = self.matrix_modulus;
231 let ef = self.filler.transverse_modulus_pa;
232 let xi = self.xi_transverse();
233 let eta = Self::halpin_tsai_eta(ef / em, xi);
234 em * (1.0 + xi * eta * vf) / (1.0 - eta * vf)
235 }
236
237 pub fn longitudinal_modulus_ht(&self) -> f64 {
239 let vf = self.filler.volume_fraction;
240 let em = self.matrix_modulus;
241 let ef = self.filler.modulus_pa;
242 let xi = self.xi_longitudinal();
243 let eta = Self::halpin_tsai_eta(ef / em, xi);
244 em * (1.0 + xi * eta * vf) / (1.0 - eta * vf)
245 }
246
247 pub fn composite_density(&self, matrix_density: f64) -> f64 {
249 self.filler.volume_fraction * self.filler.density
250 + (1.0 - self.filler.volume_fraction) * matrix_density
251 }
252}
253
254#[derive(Debug, Clone)]
263pub struct PercolationThreshold {
264 pub aspect_ratio: f64,
266 pub orientation_factor: f64,
268}
269
270impl PercolationThreshold {
271 pub fn new(aspect_ratio: f64, orientation_factor: f64) -> Self {
277 Self {
278 aspect_ratio,
279 orientation_factor,
280 }
281 }
282
283 pub fn critical_volume_fraction(&self) -> f64 {
287 0.7 / (self.aspect_ratio * self.orientation_factor)
290 }
291
292 pub fn conductivity_factor(&self, volume_fraction: f64) -> f64 {
298 let phi_c = self.critical_volume_fraction();
299 if volume_fraction <= phi_c {
300 return 0.0;
301 }
302 let t = 2.0; (volume_fraction - phi_c).powf(t)
304 }
305
306 pub fn is_percolated(&self, volume_fraction: f64) -> bool {
308 volume_fraction > self.critical_volume_fraction()
309 }
310}
311
312#[derive(Debug, Clone)]
322pub struct ThermalConductivityNano {
323 pub k_matrix: f64,
325 pub k_filler: f64,
327 pub volume_fraction: f64,
329 pub kapitza_resistance: f64,
331 pub filler_radius_m: f64,
333}
334
335impl ThermalConductivityNano {
336 pub fn new(
338 k_matrix: f64,
339 k_filler: f64,
340 volume_fraction: f64,
341 kapitza_resistance: f64,
342 filler_radius_m: f64,
343 ) -> Self {
344 Self {
345 k_matrix,
346 k_filler,
347 volume_fraction,
348 kapitza_resistance,
349 filler_radius_m,
350 }
351 }
352
353 pub fn effective_filler_conductivity(&self) -> f64 {
357 let biot = self.kapitza_resistance * self.k_filler / self.filler_radius_m;
358 self.k_filler / (1.0 + biot)
359 }
360
361 pub fn effective_conductivity(&self) -> f64 {
363 let km = self.k_matrix;
364 let kf = self.effective_filler_conductivity();
365 let vf = self.volume_fraction;
366 km * (kf + 2.0 * km + 2.0 * vf * (kf - km)) / (kf + 2.0 * km - vf * (kf - km))
368 }
369
370 pub fn enhancement_ratio(&self) -> f64 {
372 self.effective_conductivity() / self.k_matrix
373 }
374}
375
376#[derive(Debug, Clone)]
385pub struct NanoReinforcement {
386 pub matrix_modulus: f64,
388 pub theoretical_modulus: f64,
390 pub dispersion_quality: f64,
392}
393
394impl NanoReinforcement {
395 pub fn new(matrix_modulus: f64, theoretical_modulus: f64, dispersion_quality: f64) -> Self {
397 let dq = dispersion_quality.clamp(0.0, 1.0);
398 Self {
399 matrix_modulus,
400 theoretical_modulus,
401 dispersion_quality: dq,
402 }
403 }
404
405 pub fn effective_modulus(&self) -> f64 {
407 let delta = self.theoretical_modulus - self.matrix_modulus;
408 self.matrix_modulus + self.dispersion_quality * delta
409 }
410
411 pub fn improvement_percent(&self) -> f64 {
413 (self.effective_modulus() / self.matrix_modulus - 1.0) * 100.0
414 }
415}
416
417#[derive(Debug, Clone)]
426pub struct SurfaceFunctionalization {
427 pub baseline_ifss: f64,
429 pub enhancement_factor: f64,
431 pub surface_coverage: f64,
433}
434
435impl SurfaceFunctionalization {
436 pub fn new(baseline_ifss: f64, enhancement_factor: f64, surface_coverage: f64) -> Self {
438 Self {
439 baseline_ifss,
440 enhancement_factor,
441 surface_coverage: surface_coverage.clamp(0.0, 1.0),
442 }
443 }
444
445 pub fn effective_ifss(&self) -> f64 {
447 self.baseline_ifss * (1.0 + (self.enhancement_factor - 1.0) * self.surface_coverage)
449 }
450
451 pub fn critical_length(&self, fiber_strength: f64, fiber_diameter: f64) -> f64 {
458 (fiber_strength * fiber_diameter) / (2.0 * self.effective_ifss())
459 }
460}
461
462#[derive(Debug, Clone)]
471pub struct NanoparticleSize {
472 pub diameter_m: f64,
474 pub hall_petch_k: f64,
476 pub sigma_0: f64,
478}
479
480impl NanoparticleSize {
481 pub fn new(diameter_m: f64, hall_petch_k: f64, sigma_0: f64) -> Self {
483 Self {
484 diameter_m,
485 hall_petch_k,
486 sigma_0,
487 }
488 }
489
490 pub fn hall_petch_strength(&self) -> f64 {
494 self.sigma_0 + self.hall_petch_k / self.diameter_m.sqrt()
495 }
496
497 pub fn surface_to_volume_ratio(&self) -> f64 {
501 6.0 / self.diameter_m
502 }
503
504 pub fn surface_atom_fraction(&self, lattice_param_m: f64) -> f64 {
508 (lattice_param_m / self.diameter_m).min(1.0)
509 }
510}
511
512#[derive(Debug, Clone, Copy, PartialEq)]
518pub enum CntWallType {
519 SingleWalled,
521 MultiWalled,
523}
524
525#[derive(Debug, Clone)]
529pub struct CntProperties {
530 pub wall_type: CntWallType,
532 pub outer_diameter_nm: f64,
534 pub length_um: f64,
536 pub youngs_modulus_tpa: f64,
538 pub tensile_strength_gpa: f64,
540 pub thermal_conductivity_axial: f64,
542 pub electrical_conductivity: f64,
544}
545
546impl CntProperties {
547 pub fn swcnt_default() -> Self {
549 Self {
550 wall_type: CntWallType::SingleWalled,
551 outer_diameter_nm: 1.0,
552 length_um: 1.0,
553 youngs_modulus_tpa: 1.0,
554 tensile_strength_gpa: 130.0,
555 thermal_conductivity_axial: 3500.0,
556 electrical_conductivity: 1.0e7,
557 }
558 }
559
560 pub fn mwcnt_default() -> Self {
562 Self {
563 wall_type: CntWallType::MultiWalled,
564 outer_diameter_nm: 10.0,
565 length_um: 10.0,
566 youngs_modulus_tpa: 0.3,
567 tensile_strength_gpa: 50.0,
568 thermal_conductivity_axial: 3000.0,
569 electrical_conductivity: 5.0e6,
570 }
571 }
572
573 pub fn aspect_ratio(&self) -> f64 {
575 (self.length_um * 1000.0) / self.outer_diameter_nm
577 }
578
579 pub fn axial_stiffness(&self) -> f64 {
583 let d = self.outer_diameter_nm * 1.0e-9;
584 let t = 0.34e-9; let area = PI * d * t;
586 let length = self.length_um * 1.0e-6;
587 (self.youngs_modulus_tpa * 1.0e12) * area / length
588 }
589}
590
591#[derive(Debug, Clone)]
599pub struct GrapheneProperties {
600 pub num_layers: u32,
602 pub flake_size_um: f64,
604 pub youngs_modulus_tpa: f64,
606 pub strength_gpa: f64,
608 pub thermal_conductivity: f64,
610}
611
612impl GrapheneProperties {
613 pub fn monolayer(flake_size_um: f64) -> Self {
615 Self {
616 num_layers: 1,
617 flake_size_um,
618 youngs_modulus_tpa: 1.0,
619 strength_gpa: 130.0,
620 thermal_conductivity: 5000.0,
621 }
622 }
623
624 pub fn nanoplatelet(num_layers: u32, flake_size_um: f64) -> Self {
626 let e = 1.0 / num_layers as f64 * 0.1 + 0.9; Self {
629 num_layers,
630 flake_size_um,
631 youngs_modulus_tpa: e,
632 strength_gpa: 130.0 / num_layers as f64 * 0.5 + 50.0,
633 thermal_conductivity: 5000.0 / num_layers as f64 * 0.3 + 1000.0,
634 }
635 }
636
637 pub fn aspect_ratio(&self) -> f64 {
641 let thickness_um = self.num_layers as f64 * 0.000335; self.flake_size_um / thickness_um
643 }
644
645 pub fn reinforcement_factor(&self) -> f64 {
650 let alpha = self.aspect_ratio().min(1000.0);
651 let half = alpha / 2.0;
652 1.0 - half.tanh() / half
653 }
654
655 pub fn effective_modulus_contribution(&self, volume_fraction: f64) -> f64 {
657 self.reinforcement_factor() * self.youngs_modulus_tpa * 1.0e12 * volume_fraction
658 }
659}
660
661#[cfg(test)]
666mod tests {
667 use super::*;
668
669 const EPS: f64 = 1e-9;
670
671 #[test]
674 fn test_cnt_filler_aspect_ratio() {
675 let f = NanoFiller::carbon_nanotube(500.0, 0.01);
676 assert!((f.aspect_ratio - 500.0).abs() < EPS);
677 }
678
679 #[test]
680 fn test_graphene_filler_modulus() {
681 let f = NanoFiller::graphene(1000.0, 0.02);
682 assert!((f.modulus_pa - 1.0e12).abs() < 1.0); }
684
685 #[test]
686 fn test_nanoparticle_filler_creation() {
687 let f = NanoFiller::nanoparticle(70.0e9, 0.05, 3970.0);
688 assert!((f.volume_fraction - 0.05).abs() < EPS);
689 assert_eq!(f.filler_type, FillerType::Nanoparticle);
690 }
691
692 #[test]
693 fn test_weight_fraction_increases_with_volume_fraction() {
694 let f1 = NanoFiller::carbon_nanotube(100.0, 0.01);
695 let f2 = NanoFiller::carbon_nanotube(100.0, 0.05);
696 assert!(f2.weight_fraction(1200.0) > f1.weight_fraction(1200.0));
697 }
698
699 #[test]
700 fn test_weight_fraction_range() {
701 let f = NanoFiller::carbon_nanotube(100.0, 0.03);
702 let wf = f.weight_fraction(1200.0);
703 assert!(wf > 0.0 && wf < 1.0);
704 }
705
706 #[test]
709 fn test_mori_tanaka_modulus_exceeds_matrix() {
710 let filler = NanoFiller::carbon_nanotube(100.0, 0.05);
711 let mt = MoriTanakaModel::new(3.5e9, 0.35, filler);
712 assert!(mt.longitudinal_modulus() > 3.5e9);
713 }
714
715 #[test]
716 fn test_mori_tanaka_zero_filler() {
717 let filler = NanoFiller::carbon_nanotube(100.0, 0.0);
718 let mt = MoriTanakaModel::new(3.5e9, 0.35, filler);
719 let e = mt.longitudinal_modulus();
721 assert!(e > 0.0);
722 }
723
724 #[test]
725 fn test_mori_tanaka_transverse_positive() {
726 let filler = NanoFiller::carbon_nanotube(100.0, 0.03);
727 let mt = MoriTanakaModel::new(3.5e9, 0.35, filler);
728 assert!(mt.transverse_modulus() > 0.0);
729 }
730
731 #[test]
732 fn test_mori_tanaka_shear_positive() {
733 let filler = NanoFiller::carbon_nanotube(100.0, 0.03);
734 let mt = MoriTanakaModel::new(3.5e9, 0.35, filler);
735 assert!(mt.shear_modulus() > 0.0);
736 }
737
738 #[test]
739 fn test_mori_tanaka_modulus_increases_with_vf() {
740 let em = 3.5e9;
741 let e1 = {
742 let f = NanoFiller::carbon_nanotube(200.0, 0.01);
743 MoriTanakaModel::new(em, 0.35, f).longitudinal_modulus()
744 };
745 let e2 = {
746 let f = NanoFiller::carbon_nanotube(200.0, 0.05);
747 MoriTanakaModel::new(em, 0.35, f).longitudinal_modulus()
748 };
749 assert!(e2 > e1);
750 }
751
752 #[test]
755 fn test_halpin_tsai_transverse_exceeds_matrix() {
756 let filler = NanoFiller::carbon_nanotube(100.0, 0.05);
757 let ht = HalpinTsaiModel::new(3.5e9, filler);
758 assert!(ht.transverse_modulus() > 3.5e9);
759 }
760
761 #[test]
762 fn test_halpin_tsai_longitudinal_rule_of_mixtures() {
763 let vf = 0.10;
764 let em = 3.5e9;
765 let ef = 1.0e12;
766 let filler = NanoFiller::carbon_nanotube(500.0, vf);
767 let ht = HalpinTsaiModel::new(em, filler);
768 let expected = vf * ef + (1.0 - vf) * em;
769 let got = ht.longitudinal_modulus();
770 assert!((got - expected).abs() / expected < 1e-10);
771 }
772
773 #[test]
774 fn test_halpin_tsai_xi_longitudinal_scales_with_aspect_ratio() {
775 let f1 = NanoFiller::carbon_nanotube(50.0, 0.03);
776 let f2 = NanoFiller::carbon_nanotube(100.0, 0.03);
777 let ht1 = HalpinTsaiModel::new(3.5e9, f1);
778 let ht2 = HalpinTsaiModel::new(3.5e9, f2);
779 assert!(ht2.xi_longitudinal() > ht1.xi_longitudinal());
780 }
781
782 #[test]
783 fn test_halpin_tsai_density() {
784 let filler = NanoFiller::carbon_nanotube(100.0, 0.05);
785 let ht = HalpinTsaiModel::new(3.5e9, filler);
786 let rho = ht.composite_density(1200.0);
787 assert!(rho > 1200.0 * 0.9 && rho < 1350.0 * 1.1);
789 }
790
791 #[test]
792 fn test_halpin_tsai_ht_longitudinal_increases_with_vf() {
793 let em = 3.5e9;
794 let e1 = {
795 let f = NanoFiller::carbon_nanotube(200.0, 0.01);
796 HalpinTsaiModel::new(em, f).longitudinal_modulus_ht()
797 };
798 let e2 = {
799 let f = NanoFiller::carbon_nanotube(200.0, 0.05);
800 HalpinTsaiModel::new(em, f).longitudinal_modulus_ht()
801 };
802 assert!(e2 > e1);
803 }
804
805 #[test]
808 fn test_percolation_threshold_value() {
809 let p = PercolationThreshold::new(100.0, 2.0 / 3.0);
810 let phi_c = p.critical_volume_fraction();
811 assert!(phi_c > 0.0 && phi_c < 0.1);
812 }
813
814 #[test]
815 fn test_percolation_below_threshold_zero_conductivity() {
816 let p = PercolationThreshold::new(100.0, 1.0);
817 let phi_c = p.critical_volume_fraction();
818 assert!((p.conductivity_factor(phi_c * 0.5) - 0.0).abs() < EPS);
819 }
820
821 #[test]
822 fn test_percolation_above_threshold_positive() {
823 let p = PercolationThreshold::new(100.0, 1.0);
824 let phi_c = p.critical_volume_fraction();
825 assert!(p.conductivity_factor(phi_c * 2.0) > 0.0);
826 }
827
828 #[test]
829 fn test_percolation_is_percolated() {
830 let p = PercolationThreshold::new(200.0, 2.0 / 3.0);
831 let phi_c = p.critical_volume_fraction();
832 assert!(!p.is_percolated(phi_c * 0.5));
833 assert!(p.is_percolated(phi_c * 1.5));
834 }
835
836 #[test]
837 fn test_percolation_threshold_decreases_with_aspect_ratio() {
838 let p1 = PercolationThreshold::new(50.0, 1.0);
839 let p2 = PercolationThreshold::new(200.0, 1.0);
840 assert!(p2.critical_volume_fraction() < p1.critical_volume_fraction());
841 }
842
843 #[test]
846 fn test_thermal_conductivity_enhancement() {
847 let tc = ThermalConductivityNano::new(0.2, 3500.0, 0.03, 1e-8, 5e-9);
848 assert!(tc.effective_conductivity() > 0.2);
849 }
850
851 #[test]
852 fn test_thermal_conductivity_zero_kapitza() {
853 let tc = ThermalConductivityNano::new(0.2, 3500.0, 0.03, 0.0, 5e-9);
854 assert!((tc.effective_filler_conductivity() - 3500.0).abs() < EPS);
856 }
857
858 #[test]
859 fn test_thermal_enhancement_ratio_gt1() {
860 let tc = ThermalConductivityNano::new(0.2, 3500.0, 0.02, 1e-8, 5e-9);
861 assert!(tc.enhancement_ratio() > 1.0);
862 }
863
864 #[test]
865 fn test_thermal_conductivity_increases_with_vf() {
866 let tc1 = ThermalConductivityNano::new(0.2, 3500.0, 0.01, 0.0, 5e-9);
867 let tc2 = ThermalConductivityNano::new(0.2, 3500.0, 0.05, 0.0, 5e-9);
868 assert!(tc2.effective_conductivity() > tc1.effective_conductivity());
869 }
870
871 #[test]
872 fn test_thermal_kapitza_reduces_conductivity() {
873 let tc_no_k = ThermalConductivityNano::new(0.2, 3500.0, 0.03, 0.0, 5e-9);
874 let tc_with_k = ThermalConductivityNano::new(0.2, 3500.0, 0.03, 1e-7, 5e-9);
875 assert!(tc_no_k.effective_conductivity() > tc_with_k.effective_conductivity());
876 }
877
878 #[test]
881 fn test_nano_reinforcement_effective_modulus() {
882 let nr = NanoReinforcement::new(3.5e9, 7.0e9, 1.0);
883 assert!((nr.effective_modulus() - 7.0e9).abs() < 1.0);
884 }
885
886 #[test]
887 fn test_nano_reinforcement_partial_dispersion() {
888 let nr = NanoReinforcement::new(3.5e9, 7.0e9, 0.5);
889 let expected = 3.5e9 + 0.5 * (7.0e9 - 3.5e9);
890 assert!((nr.effective_modulus() - expected).abs() < 1.0);
891 }
892
893 #[test]
894 fn test_nano_reinforcement_improvement_percent_positive() {
895 let nr = NanoReinforcement::new(3.5e9, 7.0e9, 0.8);
896 assert!(nr.improvement_percent() > 0.0);
897 }
898
899 #[test]
900 fn test_nano_reinforcement_zero_dispersion() {
901 let nr = NanoReinforcement::new(3.5e9, 7.0e9, 0.0);
902 assert!((nr.effective_modulus() - 3.5e9).abs() < 1.0);
903 }
904
905 #[test]
908 fn test_surface_functionalization_increases_ifss() {
909 let sf = SurfaceFunctionalization::new(20.0e6, 2.0, 1.0);
910 assert!((sf.effective_ifss() - 40.0e6).abs() < 1.0);
911 }
912
913 #[test]
914 fn test_surface_functionalization_partial_coverage() {
915 let sf = SurfaceFunctionalization::new(20.0e6, 2.0, 0.5);
916 let expected = 20.0e6 * (1.0 + (2.0 - 1.0) * 0.5);
917 assert!((sf.effective_ifss() - expected).abs() < 1.0);
918 }
919
920 #[test]
921 fn test_critical_length_decreases_with_ifss() {
922 let sf1 = SurfaceFunctionalization::new(10.0e6, 1.0, 1.0);
923 let sf2 = SurfaceFunctionalization::new(20.0e6, 1.0, 1.0);
924 let l1 = sf1.critical_length(3.0e9, 10.0e-9);
925 let l2 = sf2.critical_length(3.0e9, 10.0e-9);
926 assert!(l1 > l2);
927 }
928
929 #[test]
932 fn test_hall_petch_increases_with_smaller_grain() {
933 let np1 = NanoparticleSize::new(100.0e-9, 0.5e6, 200.0e6);
934 let np2 = NanoparticleSize::new(10.0e-9, 0.5e6, 200.0e6);
935 assert!(np2.hall_petch_strength() > np1.hall_petch_strength());
936 }
937
938 #[test]
939 fn test_surface_to_volume_ratio_increases_with_smaller_size() {
940 let np1 = NanoparticleSize::new(100.0e-9, 0.5e6, 200.0e6);
941 let np2 = NanoparticleSize::new(10.0e-9, 0.5e6, 200.0e6);
942 assert!(np2.surface_to_volume_ratio() > np1.surface_to_volume_ratio());
943 }
944
945 #[test]
946 fn test_surface_to_volume_ratio_formula() {
947 let np = NanoparticleSize::new(10.0e-9, 0.0, 0.0);
948 let expected = 6.0 / 10.0e-9;
949 assert!((np.surface_to_volume_ratio() - expected).abs() < 1.0);
950 }
951
952 #[test]
953 fn test_surface_atom_fraction_clamped() {
954 let np = NanoparticleSize::new(0.3e-9, 0.0, 0.0);
955 let frac = np.surface_atom_fraction(0.4e-9);
956 assert!(frac <= 1.0);
957 }
958
959 #[test]
962 fn test_swcnt_aspect_ratio() {
963 let cnt = CntProperties::swcnt_default(); let expected = 1000.0 / 1.0;
965 assert!((cnt.aspect_ratio() - expected).abs() < 1e-6);
966 }
967
968 #[test]
969 fn test_mwcnt_aspect_ratio() {
970 let cnt = CntProperties::mwcnt_default(); let expected = 10_000.0 / 10.0;
972 assert!((cnt.aspect_ratio() - expected).abs() < 1e-6);
973 }
974
975 #[test]
976 fn test_cnt_axial_stiffness_positive() {
977 let cnt = CntProperties::swcnt_default();
978 assert!(cnt.axial_stiffness() > 0.0);
979 }
980
981 #[test]
982 fn test_cnt_wall_type() {
983 assert_eq!(
984 CntProperties::swcnt_default().wall_type,
985 CntWallType::SingleWalled
986 );
987 assert_eq!(
988 CntProperties::mwcnt_default().wall_type,
989 CntWallType::MultiWalled
990 );
991 }
992
993 #[test]
996 fn test_graphene_monolayer_aspect_ratio() {
997 let g = GrapheneProperties::monolayer(1.0); let expected = 1.0 / 0.000335;
999 assert!((g.aspect_ratio() - expected).abs() / expected < 1e-6);
1000 }
1001
1002 #[test]
1003 fn test_graphene_reinforcement_factor_between_0_1() {
1004 let g = GrapheneProperties::monolayer(5.0);
1005 let rf = g.reinforcement_factor();
1006 assert!(rf > 0.0 && rf <= 1.0);
1007 }
1008
1009 #[test]
1010 fn test_graphene_effective_modulus_positive() {
1011 let g = GrapheneProperties::monolayer(2.0);
1012 let contrib = g.effective_modulus_contribution(0.03);
1013 assert!(contrib > 0.0);
1014 }
1015
1016 #[test]
1017 fn test_graphene_nanoplatelet_more_layers_lower_modulus() {
1018 let g1 = GrapheneProperties::nanoplatelet(1, 2.0);
1019 let g2 = GrapheneProperties::nanoplatelet(10, 2.0);
1020 assert!(g1.youngs_modulus_tpa >= g2.youngs_modulus_tpa);
1021 }
1022
1023 #[test]
1024 fn test_graphene_reinforcement_large_aspect_ratio_near_one() {
1025 let g = GrapheneProperties::monolayer(100.0); let rf = g.reinforcement_factor();
1027 assert!(
1028 rf > 0.99,
1029 "large aspect ratio should give RF near 1, got {rf}"
1030 );
1031 }
1032}