#[allow(unused_imports)]
use super::functions::*;
#[derive(Debug, Clone)]
pub struct InterfacialZone {
pub fracture_energy: f64,
pub strength: f64,
pub vdw_energy: f64,
pub thickness: f64,
pub debonded: bool,
}
impl InterfacialZone {
pub fn new(fracture_energy: f64, strength: f64, vdw_energy: f64, thickness: f64) -> Self {
Self {
fracture_energy,
strength,
vdw_energy,
thickness,
debonded: false,
}
}
pub fn check_debond(&mut self, traction: f64) {
if traction >= self.strength {
self.debonded = true;
}
}
pub fn vdw_force_per_area(&self, separation_nm: f64) -> f64 {
let a_h = 1e-19;
let z = separation_nm * 1e-9;
a_h / (6.0 * std::f64::consts::PI * z.powi(3))
}
pub fn cohesive_traction(&self, separation: f64, delta_c: f64) -> f64 {
let delta_ratio = (separation / delta_c).clamp(0.0, 1.0);
self.strength * (1.0 - delta_ratio)
}
}
#[derive(Debug, Clone)]
pub struct CompositeLaminate {
pub plies: Vec<Ply>,
}
impl CompositeLaminate {
pub fn new(plies: Vec<Ply>) -> Self {
Self { plies }
}
pub fn total_thickness(&self) -> f64 {
self.plies.iter().map(|p| p.thickness).sum()
}
pub fn a11(&self) -> f64 {
self.plies
.iter()
.map(|p| p.transformed_q11() * p.thickness)
.sum()
}
pub fn d11(&self) -> f64 {
self.plies
.iter()
.map(|p| p.transformed_q11() * p.thickness.powi(3) / 12.0)
.sum()
}
pub fn tsai_wu_safety_factor(&self, n11: f64, xt: f64, xc: f64) -> f64 {
let f1 = 1.0 / xt - 1.0 / xc;
let f11 = 1.0 / (xt * xc);
let a_coef = f11 * n11 * n11;
let b_coef = f1 * n11;
if a_coef + b_coef <= 0.0 {
return f64::INFINITY;
}
1.0 / (a_coef + b_coef)
}
}
pub struct VirialStress {
pub volume_m3: f64,
pub temperature: f64,
pub n_atoms: usize,
pub atom_mass: f64,
}
impl VirialStress {
pub fn silicon_rve(side_nm: f64) -> Self {
let side_m = side_nm * 1.0e-9;
VirialStress {
volume_m3: side_m.powi(3),
temperature: 300.0,
n_atoms: (side_nm / 0.543 * 8.0).round() as usize,
atom_mass: 28.0 * 1.660e-27,
}
}
pub fn kinetic_stress_pa(&self) -> f64 {
const KB: f64 = 1.380649e-23;
self.n_atoms as f64 * self.atom_mass * KB * self.temperature / self.volume_m3
}
pub fn potential_stress_xx(&self, pairs: &[(f64, f64, f64, f64, f64, f64)]) -> f64 {
let sum: f64 = pairs
.iter()
.map(|(fx, _fy, _fz, rx, _ry, _rz)| fx * rx)
.sum();
-sum / self.volume_m3
}
#[allow(clippy::too_many_arguments)]
pub fn cauchy_born_strain(f: &[f64; 9]) -> [f64; 6] {
let ftf11 = f[0] * f[0] + f[3] * f[3] + f[6] * f[6];
let ftf22 = f[1] * f[1] + f[4] * f[4] + f[7] * f[7];
let ftf33 = f[2] * f[2] + f[5] * f[5] + f[8] * f[8];
let ftf12 = f[0] * f[1] + f[3] * f[4] + f[6] * f[7];
let ftf13 = f[0] * f[2] + f[3] * f[5] + f[6] * f[8];
let ftf23 = f[1] * f[2] + f[4] * f[5] + f[7] * f[8];
[
0.5 * (ftf11 - 1.0),
0.5 * (ftf22 - 1.0),
0.5 * (ftf33 - 1.0),
ftf12,
ftf13,
ftf23,
]
}
pub fn hardy_stress_xx(&self, velocities: &[[f64; 3]], forces: &[[f64; 3]]) -> f64 {
const KB: f64 = 1.380649e-23;
let kinetic: f64 = velocities
.iter()
.map(|v| self.atom_mass * v[0] * v[0])
.sum();
let potential: f64 = forces
.iter()
.enumerate()
.map(|(i, f_arr)| {
if i < velocities.len() {
f_arr[0] * velocities[i][0] * 1.0e-9
} else {
0.0
}
})
.sum();
let _ = KB;
-(kinetic + potential) / self.volume_m3
}
}
pub struct QuantumDotConfinement {
pub radius_nm: f64,
pub bulk_bandgap_ev: f64,
pub eff_mass_electron: f64,
pub eff_mass_hole: f64,
pub dielectric_constant: f64,
pub designation: String,
}
impl QuantumDotConfinement {
#[allow(dead_code)]
const KB_EV: f64 = 8.617e-5;
#[allow(dead_code)]
const H_EV_S: f64 = 4.136e-15;
const M0: f64 = 9.109e-31;
pub fn cdse(radius_nm: f64) -> Self {
QuantumDotConfinement {
radius_nm,
bulk_bandgap_ev: 1.74,
eff_mass_electron: 0.13,
eff_mass_hole: 0.45,
dielectric_constant: 9.4,
designation: "CdSe".to_string(),
}
}
pub fn inp(radius_nm: f64) -> Self {
QuantumDotConfinement {
radius_nm,
bulk_bandgap_ev: 1.35,
eff_mass_electron: 0.077,
eff_mass_hole: 0.64,
dielectric_constant: 12.4,
designation: "InP".to_string(),
}
}
pub fn confinement_energy_ev(&self) -> f64 {
const HBAR_J_S: f64 = 1.0546e-34;
const EV_TO_J: f64 = 1.602e-19;
let r = self.radius_nm * 1.0e-9;
let mu = 1.0 / (1.0 / self.eff_mass_electron + 1.0 / self.eff_mass_hole);
let mu_kg = mu * Self::M0;
let e_j = std::f64::consts::PI.powi(2) * HBAR_J_S.powi(2) / (2.0 * mu_kg * r.powi(2));
e_j / EV_TO_J
}
pub fn coulomb_correction_ev(&self) -> f64 {
const E_SQ_OVER_4PI_EPS0: f64 = 14.4;
let r_angstrom = self.radius_nm * 10.0;
-1.786 * E_SQ_OVER_4PI_EPS0 / (self.dielectric_constant * r_angstrom)
}
pub fn effective_bandgap_ev(&self) -> f64 {
self.bulk_bandgap_ev + self.confinement_energy_ev() + self.coulomb_correction_ev()
}
pub fn emission_wavelength_nm(&self) -> f64 {
let eg = self.effective_bandgap_ev().max(0.01);
1239.8 / eg
}
pub fn bohr_radius_nm(&self) -> f64 {
let a0 = 0.0529;
let mu = 1.0 / (1.0 / self.eff_mass_electron + 1.0 / self.eff_mass_hole);
self.dielectric_constant * a0 / mu
}
}
#[derive(Debug, Clone)]
pub struct NanoparticleDispersion {
pub volume_fraction: f64,
pub ep: f64,
pub em: f64,
pub nu_p: f64,
pub nu_m: f64,
}
impl NanoparticleDispersion {
pub fn new(volume_fraction: f64, ep: f64, em: f64, nu_p: f64, nu_m: f64) -> Self {
Self {
volume_fraction: volume_fraction.clamp(0.0, 1.0),
ep,
em,
nu_p,
nu_m,
}
}
fn bulk_modulus(e: f64, nu: f64) -> f64 {
e / (3.0 * (1.0 - 2.0 * nu))
}
fn shear_modulus(e: f64, nu: f64) -> f64 {
e / (2.0 * (1.0 + nu))
}
pub fn effective_bulk_modulus(&self) -> f64 {
let km = Self::bulk_modulus(self.em, self.nu_m);
let kp = Self::bulk_modulus(self.ep, self.nu_p);
let gm = Self::shear_modulus(self.em, self.nu_m);
let f = self.volume_fraction;
let alpha = 3.0 * km / (3.0 * km + 4.0 * gm);
let dk = kp - km;
km + f * dk / (1.0 + (1.0 - f) * dk / (km + 4.0 * gm / 3.0) * alpha)
}
pub fn effective_youngs_modulus(&self) -> f64 {
let km = Self::bulk_modulus(self.em, self.nu_m);
let kp = Self::bulk_modulus(self.ep, self.nu_p);
let gm = Self::shear_modulus(self.em, self.nu_m);
let gp = Self::shear_modulus(self.ep, self.nu_p);
let f = self.volume_fraction;
let alpha_k = 3.0 * km / (3.0 * km + 4.0 * gm);
let beta_g = 6.0 * (km + 2.0 * gm) / (5.0 * (3.0 * km + 4.0 * gm));
let k_eff = km + f * (kp - km) / (1.0 + (1.0 - f) * (kp - km) / km * alpha_k);
let g_eff = gm + f * (gp - gm) / (1.0 + (1.0 - f) * (gp - gm) / gm * beta_g);
9.0 * k_eff * g_eff / (3.0 * k_eff + g_eff)
}
}
pub struct GrapheneElasticStiffness {
pub c11: f64,
pub c12: f64,
pub c44: f64,
pub layer_thickness_nm: f64,
}
impl GrapheneElasticStiffness {
pub fn monolayer() -> Self {
GrapheneElasticStiffness {
c11: 352.0,
c12: 60.0,
c44: 146.0,
layer_thickness_nm: 0.335,
}
}
pub fn reduced_graphene_oxide() -> Self {
GrapheneElasticStiffness {
c11: 250.0,
c12: 45.0,
c44: 100.0,
layer_thickness_nm: 0.335,
}
}
pub fn youngs_modulus_2d(&self) -> f64 {
self.c11 - self.c12 * self.c12 / self.c11
}
pub fn poisson_ratio(&self) -> f64 {
self.c12 / self.c11
}
pub fn youngs_modulus_3d_gpa(&self) -> f64 {
let t = self.layer_thickness_nm * 1.0e-9;
(self.youngs_modulus_2d() / t) / 1.0e9
}
pub fn bending_stiffness_ev(&self) -> f64 {
let t = self.layer_thickness_nm * 1.0e-9;
let nu = self.poisson_ratio();
let kappa_nm = self.c11 * 1e9 * t.powi(3) / (12.0 * (1.0 - nu * nu));
kappa_nm * 6.241e18
}
}
#[derive(Debug, Clone)]
pub struct NanotubeProperties {
pub chirality: CntChirality,
pub n: u32,
pub m: u32,
pub diameter_nm: f64,
pub youngs_modulus_tpa: f64,
pub tensile_strength_gpa: f64,
pub thermal_conductivity: f64,
}
impl NanotubeProperties {
pub fn armchair(n: u32) -> Self {
let a_cc = 0.142;
let diameter = a_cc * (3.0_f64.sqrt()) * n as f64 / std::f64::consts::PI;
Self {
chirality: CntChirality::Armchair,
n,
m: n,
diameter_nm: diameter,
youngs_modulus_tpa: 1.0,
tensile_strength_gpa: 130.0,
thermal_conductivity: 3500.0,
}
}
pub fn zigzag(n: u32) -> Self {
let a_cc = 0.142;
let diameter = a_cc * (3.0_f64.sqrt()) * n as f64 / std::f64::consts::PI;
Self {
chirality: CntChirality::Zigzag,
n,
m: 0,
diameter_nm: diameter,
youngs_modulus_tpa: 0.97,
tensile_strength_gpa: 120.0,
thermal_conductivity: 2000.0,
}
}
pub fn chiral(n: u32, m: u32) -> Self {
let a_cc = 0.142;
let diameter = a_cc * ((n * n + n * m + m * m) as f64).sqrt() / std::f64::consts::PI;
Self {
chirality: CntChirality::Chiral,
n,
m,
diameter_nm: diameter,
youngs_modulus_tpa: 1.0,
tensile_strength_gpa: 100.0,
thermal_conductivity: 1500.0,
}
}
pub fn axial_stiffness_n(&self) -> f64 {
let wall_thickness_nm = 0.34;
let area_nm2 = std::f64::consts::PI * self.diameter_nm * wall_thickness_nm;
let area_m2 = area_nm2 * 1e-18;
let e_pa = self.youngs_modulus_tpa * 1e12;
e_pa * area_m2
}
pub fn is_metallic(&self) -> bool {
match self.chirality {
CntChirality::Armchair => true,
_ => (self.n as i32 - self.m as i32).abs() % 3 == 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CntChirality {
Armchair,
Zigzag,
Chiral,
}
#[derive(Debug, Clone)]
pub struct ThermoelectricMaterial {
pub seebeck: f64,
pub electrical_conductivity: f64,
pub thermal_conductivity: f64,
pub temperature: f64,
}
impl ThermoelectricMaterial {
pub fn bismuth_telluride() -> Self {
Self {
seebeck: 200e-6,
electrical_conductivity: 1e5,
thermal_conductivity: 1.5,
temperature: 300.0,
}
}
pub fn power_factor(&self) -> f64 {
self.seebeck * self.seebeck * self.electrical_conductivity
}
pub fn zt(&self) -> f64 {
self.power_factor() * self.temperature / self.thermal_conductivity
}
pub fn peltier_coefficient(&self) -> f64 {
self.seebeck * self.temperature
}
pub fn device_efficiency(&self, delta_t: f64) -> f64 {
let zt = self.zt();
let t_h = self.temperature + delta_t;
let carnot = delta_t / t_h;
let sqrt_factor = (1.0 + zt).sqrt();
let t_ratio = self.temperature / t_h;
carnot * (sqrt_factor - 1.0) / (sqrt_factor + t_ratio)
}
}
pub struct DislocationMechanics {
pub shear_modulus: f64,
pub burgers_nm: f64,
pub nu: f64,
pub lattice_parameter_nm: f64,
pub misfit: f64,
}
impl DislocationMechanics {
pub fn fcc_copper() -> Self {
DislocationMechanics {
shear_modulus: 42.0,
burgers_nm: 0.256,
nu: 0.34,
lattice_parameter_nm: 0.362,
misfit: 0.005,
}
}
pub fn bcc_iron() -> Self {
DislocationMechanics {
shear_modulus: 82.0,
burgers_nm: 0.248,
nu: 0.29,
lattice_parameter_nm: 0.286,
misfit: 0.01,
}
}
pub fn peierls_nabarro_stress(&self) -> f64 {
let a = self.lattice_parameter_nm;
let b = self.burgers_nm;
let w = a / (2.0 * (1.0 - self.nu));
2.0 * self.shear_modulus / (1.0 - self.nu) * (-2.0 * std::f64::consts::PI * w / b).exp()
}
pub fn orowan_stress_mpa(&self, obstacle_spacing_nm: f64) -> f64 {
const M: f64 = 3.06;
M * self.shear_modulus * self.burgers_nm / obstacle_spacing_nm * 1000.0
}
pub fn taylor_hardening_mpa(&self, dislocation_density_m2: f64) -> f64 {
const M: f64 = 3.06;
const ALPHA: f64 = 0.3;
M * ALPHA
* self.shear_modulus
* self.burgers_nm
* 1.0e-9
* dislocation_density_m2.sqrt()
* 1.0e9
}
pub fn line_energy(&self) -> f64 {
let b = self.burgers_nm * 1.0e-9;
let g = self.shear_modulus * 1.0e9;
g * b.powi(2) / 2.0
}
pub fn schmid_crss(&self, sigma_applied: f64, phi: f64, lambda: f64) -> f64 {
sigma_applied * phi.cos() * lambda.cos()
}
}
pub struct NanoGrapheneThermal {
pub k_bulk: f64,
pub substrate_coupling: f64,
pub defect_mfp_nm: f64,
pub grain_size_nm: f64,
}
impl NanoGrapheneThermal {
pub fn suspended() -> Self {
NanoGrapheneThermal {
k_bulk: 5000.0,
substrate_coupling: 0.0,
defect_mfp_nm: 1000.0,
grain_size_nm: 1000.0,
}
}
pub fn on_sio2() -> Self {
NanoGrapheneThermal {
k_bulk: 5000.0,
substrate_coupling: 50.0,
defect_mfp_nm: 500.0,
grain_size_nm: 500.0,
}
}
pub fn effective_mfp_nm(&self, intrinsic_mfp: f64) -> f64 {
1.0 / (1.0 / intrinsic_mfp + 1.0 / self.defect_mfp_nm + 1.0 / self.grain_size_nm)
}
pub fn effective_conductivity(&self, intrinsic_mfp: f64) -> f64 {
let mfp_eff = self.effective_mfp_nm(intrinsic_mfp);
self.k_bulk * mfp_eff / intrinsic_mfp
}
pub fn substrate_thermal_resistance(&self, hf_nm: f64) -> f64 {
if self.substrate_coupling < 1.0e-15 {
return f64::INFINITY;
}
let hf = hf_nm * 1.0e-9;
1.0 / (self.substrate_coupling * 1.0e9 * hf)
}
}
#[derive(Debug, Clone)]
pub struct MolecularCrystal {
pub lattice: [f64; 3],
pub bond_stiffness: f64,
pub bonds_per_cell: u32,
pub cell_volume_m3: f64,
}
impl MolecularCrystal {
pub fn new(lattice: [f64; 3], bond_stiffness: f64, bonds_per_cell: u32) -> Self {
let vol = lattice[0] * lattice[1] * lattice[2] * 1e-30;
Self {
lattice,
bond_stiffness,
bonds_per_cell,
cell_volume_m3: vol,
}
}
pub fn youngs_modulus(&self) -> f64 {
let vol_13 = self.cell_volume_m3.cbrt();
self.bond_stiffness * self.bonds_per_cell as f64 / vol_13
}
pub fn compressibility(&self) -> f64 {
let e = self.youngs_modulus();
if e < 1e-15 {
return f64::INFINITY;
}
1.0 / e
}
}
#[derive(Debug, Clone)]
pub struct SizeEffect {
pub hall_petch_k: f64,
pub friction_stress: f64,
pub weibull_m: f64,
pub reference_volume: f64,
pub reference_strength: f64,
}
impl SizeEffect {
pub fn new(
hall_petch_k: f64,
friction_stress: f64,
weibull_m: f64,
reference_volume: f64,
reference_strength: f64,
) -> Self {
Self {
hall_petch_k,
friction_stress,
weibull_m,
reference_volume,
reference_strength,
}
}
pub fn hall_petch_strength(&self, grain_diameter_m: f64) -> f64 {
self.friction_stress + self.hall_petch_k / grain_diameter_m.sqrt()
}
pub fn weibull_strength(&self, volume_m3: f64) -> f64 {
self.reference_strength * (self.reference_volume / volume_m3).powf(1.0 / self.weibull_m)
}
pub fn weibull_survival_probability(&self, stress: f64, volume: f64) -> f64 {
let ratio = stress / self.reference_strength;
(-(volume / self.reference_volume) * ratio.powf(self.weibull_m)).exp()
}
}
#[derive(Debug, Clone)]
pub struct PolymerChain {
pub n_segments: u32,
pub segment_length: f64,
pub persistence_length: f64,
pub temperature: f64,
}
impl PolymerChain {
const KB: f64 = 1.380_649e-23;
pub fn new(
n_segments: u32,
segment_length: f64,
persistence_length: f64,
temperature: f64,
) -> Self {
Self {
n_segments,
segment_length,
persistence_length,
temperature,
}
}
pub fn contour_length(&self) -> f64 {
self.n_segments as f64 * self.segment_length
}
pub fn rms_end_to_end(&self) -> f64 {
self.segment_length * (self.n_segments as f64).sqrt()
}
pub fn entropic_force(&self, extension: f64) -> f64 {
let l0 = self.contour_length();
let x = (extension / l0).clamp(-0.999, 0.999);
let inv_lang = x * (3.0 - x * x) / (1.0 - x * x);
Self::KB * self.temperature / self.segment_length * inv_lang
}
pub fn flory_huggins_free_energy(&self, phi: f64, chi: f64) -> f64 {
let phi = phi.clamp(1e-10, 1.0 - 1e-10);
let n = self.n_segments as f64;
Self::KB
* self.temperature
* (phi / n * phi.ln() + (1.0 - phi) * (1.0 - phi).ln() + chi * phi * (1.0 - phi))
}
}
#[derive(Debug, Clone)]
pub struct FiberMatrix {
pub vf: f64,
pub ef: f64,
pub em: f64,
pub nu_f: f64,
pub nu_m: f64,
}
impl FiberMatrix {
pub fn new(vf: f64, ef: f64, em: f64, nu_f: f64, nu_m: f64) -> Self {
let vf = vf.clamp(0.0, 1.0);
Self {
vf,
ef,
em,
nu_f,
nu_m,
}
}
pub fn vm(&self) -> f64 {
1.0 - self.vf
}
pub fn e1(&self) -> f64 {
self.ef * self.vf + self.em * self.vm()
}
pub fn e2_rom(&self) -> f64 {
self.ef * self.em / (self.ef * self.vm() + self.em * self.vf)
}
pub fn e2_halpin_tsai(&self) -> f64 {
let xi = 2.0;
let eta = (self.ef / self.em - 1.0) / (self.ef / self.em + xi);
self.em * (1.0 + xi * eta * self.vf) / (1.0 - eta * self.vf)
}
pub fn nu12(&self) -> f64 {
self.nu_f * self.vf + self.nu_m * self.vm()
}
pub fn g12_halpin_tsai(&self) -> f64 {
let gf = self.ef / (2.0 * (1.0 + self.nu_f));
let gm = self.em / (2.0 * (1.0 + self.nu_m));
let xi = 1.0;
let eta = (gf / gm - 1.0) / (gf / gm + xi);
gm * (1.0 + xi * eta * self.vf) / (1.0 - eta * self.vf)
}
}
pub struct CntBuckling {
pub diameter_nm: f64,
pub wall_thickness_nm: f64,
pub youngs_modulus_tpa: f64,
pub length_nm: f64,
}
impl CntBuckling {
pub fn swcnt(diameter_nm: f64, length_nm: f64) -> Self {
CntBuckling {
diameter_nm,
wall_thickness_nm: 0.34,
youngs_modulus_tpa: 1.0,
length_nm,
}
}
pub fn moment_of_inertia(&self) -> f64 {
let r = self.diameter_nm / 2.0;
let t = self.wall_thickness_nm;
std::f64::consts::PI * r.powi(3) * t
}
pub fn cross_section_area(&self) -> f64 {
std::f64::consts::PI * self.diameter_nm * self.wall_thickness_nm
}
pub fn euler_buckling_load(&self) -> f64 {
let e_gpa = self.youngs_modulus_tpa * 1000.0;
let i_nm4 = self.moment_of_inertia();
let l = self.length_nm;
std::f64::consts::PI.powi(2) * e_gpa * i_nm4 / (l * l)
}
pub fn shell_buckling_stress(&self) -> f64 {
let r = self.diameter_nm / 2.0;
let t = self.wall_thickness_nm;
let e_gpa = self.youngs_modulus_tpa * 1000.0;
let nu: f64 = 0.19;
e_gpa * t / (r * (3.0 * (1.0 - nu * nu)).sqrt())
}
pub fn slenderness_ratio(&self) -> f64 {
let r_gyration = (self.moment_of_inertia() / self.cross_section_area()).sqrt();
self.length_nm / r_gyration
}
pub fn critical_strain(&self) -> f64 {
let r = self.diameter_nm / 2.0;
let t = self.wall_thickness_nm;
let c = (3.0 * (1.0 - 0.19_f64.powi(2))).sqrt();
t / (r * c)
}
}
#[derive(Debug, Clone)]
pub struct BioMaterial {
pub collagen_fraction: f64,
pub mineral_fraction: f64,
pub collagen_modulus: f64,
pub mineral_modulus: f64,
pub c1: f64,
pub c2: f64,
pub remodeling_rate: f64,
}
impl BioMaterial {
pub fn cortical_bone() -> Self {
Self {
collagen_fraction: 0.35,
mineral_fraction: 0.45,
collagen_modulus: 1.5e9,
mineral_modulus: 114e9,
c1: 1e5,
c2: 1e4,
remodeling_rate: 1e-8,
}
}
pub fn soft_tissue() -> Self {
Self {
collagen_fraction: 0.15,
mineral_fraction: 0.0,
collagen_modulus: 0.5e9,
mineral_modulus: 0.0,
c1: 1e4,
c2: 5e3,
remodeling_rate: 1e-7,
}
}
pub fn effective_modulus(&self) -> f64 {
self.collagen_fraction * self.collagen_modulus
+ self.mineral_fraction * self.mineral_modulus
}
pub fn mooney_rivlin_energy(&self, i1: f64, i2: f64) -> f64 {
self.c1 * (i1 - 3.0) + self.c2 * (i2 - 3.0)
}
pub fn remodeling_stimulus(&self, strain_energy: f64, threshold: f64) -> f64 {
if strain_energy > threshold {
self.remodeling_rate * (strain_energy - threshold)
} else {
0.0
}
}
}
pub struct SurfaceToVolumeEffects {
pub k_hp: f64,
pub sigma_0: f64,
pub d_breakdown_nm: f64,
pub surface_energy: f64,
pub surface_stress: f64,
pub d_atom_nm: f64,
}
impl SurfaceToVolumeEffects {
pub fn nc_copper() -> Self {
SurfaceToVolumeEffects {
k_hp: 145.0,
sigma_0: 25.0,
d_breakdown_nm: 15.0,
surface_energy: 1.7,
surface_stress: 1.5,
d_atom_nm: 0.256,
}
}
pub fn nc_iron() -> Self {
SurfaceToVolumeEffects {
k_hp: 600.0,
sigma_0: 100.0,
d_breakdown_nm: 20.0,
surface_energy: 2.4,
surface_stress: 2.0,
d_atom_nm: 0.248,
}
}
pub fn yield_strength_mpa(&self, grain_size_nm: f64) -> f64 {
if grain_size_nm >= self.d_breakdown_nm {
self.sigma_0 + self.k_hp / grain_size_nm.sqrt()
} else {
let sigma_peak = self.sigma_0 + self.k_hp / self.d_breakdown_nm.sqrt();
sigma_peak * (grain_size_nm / self.d_breakdown_nm)
}
}
pub fn surface_to_volume_ratio(&self, diameter_nm: f64) -> f64 {
6.0 / diameter_nm
}
pub fn surface_atom_fraction(&self, diameter_nm: f64) -> f64 {
let n_surface_fraction = self.d_atom_nm / diameter_nm * 6.0;
n_surface_fraction.min(1.0)
}
pub fn capillary_pressure_mpa(&self, radius_nm: f64) -> f64 {
2.0 * self.surface_stress / (radius_nm * 1.0e-9) / 1.0e6
}
pub fn melting_point_depression(&self, t_m_bulk: f64, h_f: f64, rho: f64, d_nm: f64) -> f64 {
4.0 * self.surface_energy * t_m_bulk / (h_f * rho * d_nm * 1.0e-9)
}
}
pub struct GrainBoundaryMechanics {
pub e0: f64,
pub theta_m: f64,
pub d0_gb: f64,
pub q_gb: f64,
pub delta_nm: f64,
}
impl GrainBoundaryMechanics {
pub fn aluminum_gb() -> Self {
GrainBoundaryMechanics {
e0: 0.32,
theta_m: 15.0,
d0_gb: 2.0e-7,
q_gb: 84_000.0,
delta_nm: 0.5,
}
}
pub fn nickel_gb() -> Self {
GrainBoundaryMechanics {
e0: 0.69,
theta_m: 15.0,
d0_gb: 1.7e-9,
q_gb: 115_000.0,
delta_nm: 0.5,
}
}
pub fn read_shockley_energy(&self, misorientation_deg: f64) -> f64 {
let theta_m_rad = self.theta_m.to_radians();
let theta_rad = misorientation_deg.to_radians().min(theta_m_rad);
let ratio = theta_rad / theta_m_rad;
if ratio < 1.0e-10 {
return 0.0;
}
self.e0 * ratio * (1.0 - ratio.ln())
}
pub fn high_angle_energy(&self) -> f64 {
self.e0
}
pub fn gb_diffusivity(&self, t_kelvin: f64) -> f64 {
const R: f64 = 8.314;
self.d0_gb * (-self.q_gb / (R * t_kelvin)).exp()
}
pub fn gb_diffusion_flux(&self, t_kelvin: f64) -> f64 {
self.gb_diffusivity(t_kelvin) * self.delta_nm * 1.0e-9
}
pub fn coble_creep_rate(&self, sigma: f64, d_m: f64, t_kelvin: f64, omega: f64) -> f64 {
const R: f64 = 8.314;
let d_gb = self.gb_diffusivity(t_kelvin);
let delta = self.delta_nm * 1.0e-9;
148.0 * sigma * d_gb * delta * omega / (R * t_kelvin * d_m.powi(3))
}
}
pub struct ThinFilmMechanics {
pub ef: f64,
pub nu_f: f64,
pub hf_nm: f64,
pub cte_film: f64,
pub es: f64,
pub nu_s: f64,
pub hs_mm: f64,
pub cte_substrate: f64,
pub gc: f64,
}
impl ThinFilmMechanics {
pub fn tin_on_silicon(hf_nm: f64) -> Self {
ThinFilmMechanics {
ef: 450.0,
nu_f: 0.25,
hf_nm,
cte_film: 9.4e-6,
es: 130.0,
nu_s: 0.28,
hs_mm: 0.725,
cte_substrate: 2.6e-6,
gc: 5.0,
}
}
pub fn cu_on_sio2(hf_nm: f64) -> Self {
ThinFilmMechanics {
ef: 130.0,
nu_f: 0.34,
hf_nm,
cte_film: 16.5e-6,
es: 73.0,
nu_s: 0.17,
hs_mm: 0.725,
cte_substrate: 0.55e-6,
gc: 2.0,
}
}
pub fn biaxial_modulus(&self) -> f64 {
self.ef / (1.0 - self.nu_f)
}
pub fn thermal_mismatch_stress_mpa(&self, delta_t: f64) -> f64 {
let delta_cte = self.cte_film - self.cte_substrate;
-self.biaxial_modulus() * delta_cte * delta_t * 1000.0
}
pub fn stoney_curvature(&self, sigma_f_mpa: f64) -> f64 {
let hf = self.hf_nm * 1.0e-9;
let hs = self.hs_mm * 1.0e-3;
let m_s = self.es / (1.0 - self.nu_s);
let sigma_f_pa = sigma_f_mpa * 1.0e6;
6.0 * sigma_f_pa * hf / (m_s * 1.0e9 * hs.powi(2))
}
pub fn wafer_bow_um(&self, sigma_f_mpa: f64, r_wafer_mm: f64) -> f64 {
let kappa = self.stoney_curvature(sigma_f_mpa);
let r = r_wafer_mm * 1.0e-3;
kappa * r.powi(2) / 2.0 * 1.0e6
}
pub fn channel_crack_erg(&self, sigma_mpa: f64) -> f64 {
const Z: f64 = 2.0;
let sigma_pa = sigma_mpa * 1.0e6;
let hf = self.hf_nm * 1.0e-9;
let ef_pa = self.ef * 1.0e9;
Z * sigma_pa.powi(2) * hf / ef_pa
}
pub fn delamination_energy(&self, sigma_mpa: f64) -> f64 {
let sigma_pa = sigma_mpa * 1.0e6;
let hf = self.hf_nm * 1.0e-9;
let ef_pa = self.ef * 1.0e9;
(1.0 - self.nu_f) * sigma_pa.powi(2) * hf / ef_pa
}
}
pub struct NanoindentationOliverPharr {
pub tip_radius_nm: f64,
pub e_indenter: f64,
pub nu_indenter: f64,
pub c_geom: f64,
}
impl NanoindentationOliverPharr {
pub fn berkovich_diamond() -> Self {
NanoindentationOliverPharr {
tip_radius_nm: 50.0,
e_indenter: 1141.0,
nu_indenter: 0.07,
c_geom: 24.5,
}
}
pub fn cube_corner() -> Self {
NanoindentationOliverPharr {
tip_radius_nm: 40.0,
e_indenter: 1141.0,
nu_indenter: 0.07,
c_geom: 2.598,
}
}
pub fn reduced_modulus(&self, stiffness_n_per_nm: f64, contact_area_nm2: f64) -> f64 {
stiffness_n_per_nm * std::f64::consts::PI.sqrt() / (2.0 * contact_area_nm2.sqrt())
}
pub fn sample_youngs_modulus(&self, e_r_gpa: f64, nu_sample: f64) -> f64 {
let inv_er = 1.0 / e_r_gpa;
let inv_ei = (1.0 - self.nu_indenter.powi(2)) / self.e_indenter;
(1.0 - nu_sample.powi(2)) / (inv_er - inv_ei)
}
pub fn contact_area_nm2(&self, h_max_nm: f64, p_max_n: f64, stiffness_n_per_nm: f64) -> f64 {
let h_c = h_max_nm - 0.75 * p_max_n / stiffness_n_per_nm;
self.c_geom * h_c.max(0.0).powi(2)
}
pub fn hardness_gpa(&self, p_max_n: f64, contact_area_nm2: f64) -> f64 {
if contact_area_nm2 < 1.0e-30 {
return 0.0;
}
p_max_n / (contact_area_nm2 * 1.0e-18) / 1.0e9
}
pub fn pile_up_correction(&self, e_r_gpa: f64, h_gpa: f64) -> f64 {
if h_gpa < 1.0e-10 {
return 1.0;
}
1.0 + 0.1 * e_r_gpa / h_gpa
}
}
#[derive(Debug, Clone)]
pub struct GrapheneSheet {
pub n_layers: u32,
pub youngs_modulus_tpa: f64,
pub tensile_strength_gpa: f64,
pub thermal_conductivity: f64,
pub vacancy_fraction: f64,
}
impl GrapheneSheet {
pub fn monolayer() -> Self {
Self {
n_layers: 1,
youngs_modulus_tpa: 1.0,
tensile_strength_gpa: 130.0,
thermal_conductivity: 5000.0,
vacancy_fraction: 0.0,
}
}
pub fn bilayer() -> Self {
Self {
n_layers: 2,
youngs_modulus_tpa: 0.98,
tensile_strength_gpa: 120.0,
thermal_conductivity: 4000.0,
vacancy_fraction: 0.0,
}
}
pub fn effective_youngs_modulus(&self) -> f64 {
let alpha = 2.5;
let frac = self.vacancy_fraction.clamp(0.0, 1.0);
self.youngs_modulus_tpa * (1.0 - alpha * frac).max(0.0).powi(2)
}
pub fn fracture_strain(&self) -> f64 {
let base_strain = 0.25;
base_strain * (1.0 - self.vacancy_fraction.clamp(0.0, 1.0)).powi(2)
}
pub fn effective_thermal_conductivity(&self) -> f64 {
self.thermal_conductivity / (1.0 + 0.1 * (self.n_layers as f64 - 1.0))
}
}
#[derive(Debug, Clone)]
pub struct Ply {
pub thickness: f64,
pub angle: f64,
pub e1: f64,
pub e2: f64,
pub g12: f64,
pub nu12: f64,
}
impl Ply {
pub fn new(thickness: f64, angle_deg: f64, e1: f64, e2: f64, g12: f64, nu12: f64) -> Self {
Self {
thickness,
angle: angle_deg.to_radians(),
e1,
e2,
g12,
nu12,
}
}
pub fn nu21(&self) -> f64 {
self.nu12 * self.e2 / self.e1
}
pub fn reduced_stiffness(&self) -> [f64; 4] {
let denom = 1.0 - self.nu12 * self.nu21();
let q11 = self.e1 / denom;
let q12 = self.nu12 * self.e2 / denom;
let q22 = self.e2 / denom;
let q66 = self.g12;
[q11, q12, q22, q66]
}
pub fn transformed_q11(&self) -> f64 {
let [q11, q12, q22, q66] = self.reduced_stiffness();
let c = self.angle.cos();
let s = self.angle.sin();
let c2 = c * c;
let s2 = s * s;
let c4 = c2 * c2;
let s4 = s2 * s2;
q11 * c4 + 2.0 * (q12 + 2.0 * q66) * s2 * c2 + q22 * s4
}
}
pub struct PhononTransport {
pub mean_free_path_nm: f64,
pub debye_temp: f64,
pub k_bulk: f64,
pub v_group: f64,
pub rho_cv: f64,
}
impl PhononTransport {
pub fn silicon() -> Self {
PhononTransport {
mean_free_path_nm: 300.0,
debye_temp: 645.0,
k_bulk: 148.0,
v_group: 6400.0,
rho_cv: 1.63e6,
}
}
pub fn germanium() -> Self {
PhononTransport {
mean_free_path_nm: 200.0,
debye_temp: 360.0,
k_bulk: 60.0,
v_group: 3900.0,
rho_cv: 1.66e6,
}
}
pub fn knudsen_number(&self, length_nm: f64) -> f64 {
self.mean_free_path_nm / length_nm
}
pub fn effective_conductivity(&self, length_nm: f64) -> f64 {
let kn = self.knudsen_number(length_nm);
self.k_bulk / (1.0 + kn)
}
pub fn kapitza_resistance_dmm(&self, k2: f64) -> f64 {
4.0 * (1.0 / self.k_bulk + 1.0 / k2) / self.v_group
}
pub fn ballistic_heat_flux(&self, delta_t: f64) -> f64 {
0.5 * self.rho_cv * self.v_group * delta_t
}
pub fn conductivity_at_temp(&self, t_k: f64) -> f64 {
if t_k < self.debye_temp {
self.k_bulk * (self.debye_temp / t_k).powf(0.5)
} else {
self.k_bulk * self.debye_temp / t_k
}
}
}