use super::functions::terzaghi_bearing_factors;
#[allow(unused_imports)]
use super::functions::*;
use std::f64::consts::PI;
pub struct CoverRequirement;
impl CoverRequirement {
pub fn minimum_cover(exposure: &ExposureClass, bar_dia: f64) -> f64 {
match exposure {
ExposureClass::Interior => {
if bar_dia <= 16.0 {
20.0
} else {
40.0
}
}
ExposureClass::Moderate => 50.0,
ExposureClass::Severe => 65.0,
ExposureClass::Submerged => 75.0,
}
}
pub fn effective_depth(h: f64, cover: f64, bar_dia: f64) -> f64 {
h - cover - bar_dia / 2.0
}
}
#[derive(Debug, Clone)]
pub struct StirrupDesign {
pub bw: f64,
pub d: f64,
pub fc: f64,
pub fy: f64,
pub av: f64,
pub legs: u32,
pub spacing: f64,
}
impl StirrupDesign {
pub fn new(bw: f64, d: f64, fc: f64, fy: f64, av: f64, legs: u32, spacing: f64) -> Self {
StirrupDesign {
bw,
d,
fc,
fy,
av,
legs,
spacing,
}
}
pub fn vc(&self) -> f64 {
0.17 * self.fc.sqrt() * self.bw * self.d
}
pub fn vs(&self) -> f64 {
let total_av = self.av * self.legs as f64;
total_av * self.fy * self.d / self.spacing
}
pub fn vn(&self) -> f64 {
self.vc() + self.vs()
}
pub fn phi_vn(&self) -> f64 {
0.75 * self.vn()
}
pub fn max_spacing(&self) -> f64 {
(self.d / 2.0).min(600.0)
}
pub fn min_av_per_s(&self) -> f64 {
let t1 = 0.062 * self.fc.sqrt() * self.bw / self.fy;
let t2 = 0.35 * self.bw / self.fy;
t1.max(t2)
}
pub fn is_adequate(&self, vu: f64) -> bool {
self.phi_vn() >= vu
}
}
#[derive(Debug, Clone)]
pub struct AngleSection {
pub l: f64,
pub t: f64,
pub fy: f64,
}
impl AngleSection {
pub fn equal_leg(l: f64, t: f64, fy: f64) -> Self {
AngleSection { l, t, fy }
}
pub fn area(&self) -> f64 {
2.0 * self.l * self.t - self.t.powi(2)
}
pub fn centroid(&self) -> f64 {
let a = self.area();
if a < 1e-10 {
return 0.0;
}
let a1 = self.l * self.t;
let x1 = self.t / 2.0;
let a2 = (self.l - self.t) * self.t;
let x2 = self.t + (self.l - self.t) / 2.0;
(a1 * x1 + a2 * x2) / a
}
pub fn ixx(&self) -> f64 {
let t = self.t;
let l = self.l;
let i_horiz = l * t.powi(3) / 12.0 + l * t * (t / 2.0).powi(2);
let i_vert = t * (l - t).powi(3) / 12.0 + t * (l - t) * ((l - t) / 2.0 + t).powi(2);
i_horiz + i_vert
}
pub fn rz(&self) -> f64 {
(self.ixx() / self.area()).sqrt()
}
pub fn axial_capacity(&self) -> f64 {
self.area() * self.fy
}
}
pub struct RetainingWall {
pub height: f64,
pub gamma: f64,
pub phi_deg: f64,
pub delta_deg: f64,
pub beta_deg: f64,
}
impl RetainingWall {
pub fn new(height: f64, gamma: f64, phi_deg: f64) -> Self {
RetainingWall {
height,
gamma,
phi_deg,
delta_deg: phi_deg * 2.0 / 3.0,
beta_deg: 0.0,
}
}
pub fn rankine_ka(&self) -> f64 {
let phi = self.phi_deg.to_radians();
((PI / 4.0 - phi / 2.0).tan()).powi(2)
}
pub fn rankine_kp(&self) -> f64 {
let phi = self.phi_deg.to_radians();
((PI / 4.0 + phi / 2.0).tan()).powi(2)
}
pub fn active_thrust_rankine(&self) -> f64 {
0.5 * self.gamma * self.height.powi(2) * self.rankine_ka()
}
pub fn passive_resistance_rankine(&self) -> f64 {
0.5 * self.gamma * self.height.powi(2) * self.rankine_kp()
}
pub fn coulomb_ka(&self) -> f64 {
let phi = self.phi_deg.to_radians();
let delta = self.delta_deg.to_radians();
let beta = self.beta_deg.to_radians();
let alpha = PI / 2.0;
let num = (phi - beta).sin().powi(2);
let term = 1.0
+ ((phi + delta).sin() * (phi - beta).sin()
/ ((alpha + delta).sin() * (alpha - beta).sin()))
.sqrt();
num / ((alpha).sin().powi(2) * (alpha - delta).sin() * term.powi(2))
}
pub fn overturning_factor(&self, footing_width: f64) -> f64 {
let pa = self.active_thrust_rankine();
let overturning_moment = pa * self.height / 3.0;
let restoring_moment = self.gamma * self.height * footing_width * footing_width / 2.0;
if overturning_moment < 1e-10 {
return f64::INFINITY;
}
restoring_moment / overturning_moment
}
}
#[derive(Debug, Clone)]
pub struct MasonryPrism {
pub unit_strength: f64,
pub mortar_type: String,
pub h_t_ratio: f64,
pub fp_measured: f64,
}
impl MasonryPrism {
pub fn new(unit_strength: f64, mortar_type: &str, h_t_ratio: f64, fp_measured: f64) -> Self {
MasonryPrism {
unit_strength,
mortar_type: mortar_type.to_string(),
h_t_ratio,
fp_measured,
}
}
pub fn ht_correction_factor(&self) -> f64 {
if self.h_t_ratio >= 5.0 {
1.0
} else if self.h_t_ratio >= 2.0 {
0.75 + 0.05 * (self.h_t_ratio - 2.0)
} else {
0.75
}
}
pub fn fm_corrected(&self) -> f64 {
self.fp_measured * self.ht_correction_factor()
}
pub fn em(&self) -> f64 {
900.0 * self.fm_corrected()
}
pub fn allowable_compressive_stress(&self) -> f64 {
0.25 * self.fm_corrected()
}
}
#[derive(Debug, Clone)]
pub struct ConcreteMixDesign {
pub wc_ratio: f64,
pub cement: f64,
pub water: f64,
pub fine_agg: f64,
pub coarse_agg: f64,
pub superplasticiser: f64,
pub air_content: f64,
pub slump: f64,
}
impl ConcreteMixDesign {
pub fn new(
wc_ratio: f64,
cement: f64,
fine_agg: f64,
coarse_agg: f64,
air_content: f64,
) -> Self {
let water = wc_ratio * cement;
ConcreteMixDesign {
wc_ratio,
cement,
water,
fine_agg,
coarse_agg,
superplasticiser: 0.0,
air_content,
slump: 75.0,
}
}
pub fn c30_normal_weight() -> Self {
ConcreteMixDesign {
wc_ratio: 0.50,
cement: 350.0,
water: 175.0,
fine_agg: 720.0,
coarse_agg: 1080.0,
superplasticiser: 0.0,
air_content: 2.0,
slump: 75.0,
}
}
pub fn high_performance() -> Self {
ConcreteMixDesign {
wc_ratio: 0.30,
cement: 500.0,
water: 150.0,
fine_agg: 700.0,
coarse_agg: 1050.0,
superplasticiser: 1.5,
air_content: 1.5,
slump: 180.0,
}
}
pub fn fresh_unit_weight(&self) -> f64 {
self.cement + self.water + self.fine_agg + self.coarse_agg
}
pub fn abrams_strength(&self) -> f64 {
let a = 96.0_f64;
let b = 8.6_f64;
a / b.powf(self.wc_ratio)
}
pub fn paste_volume(&self) -> f64 {
let rho_c = 3150.0;
let rho_w = 1000.0;
self.cement / rho_c + self.water / rho_w
}
pub fn aggregate_cement_ratio(&self) -> f64 {
(self.fine_agg + self.coarse_agg) / self.cement
}
pub fn meets_durability_wc_limit(&self, max_wc: f64) -> bool {
self.wc_ratio <= max_wc
}
pub fn combined_fineness_modulus(&self, fm_fine: f64, fm_coarse: f64) -> f64 {
let total = self.fine_agg + self.coarse_agg;
if total < 1e-6 {
return 0.0;
}
(self.fine_agg * fm_fine + self.coarse_agg * fm_coarse) / total
}
}
#[derive(Debug, Clone)]
pub struct TerzaghiSettlement {
pub cc: f64,
pub cr: f64,
pub e0: f64,
pub h: f64,
pub sigma_v0: f64,
pub sigma_p: f64,
pub cv: f64,
}
impl TerzaghiSettlement {
pub fn normally_consolidated(cc: f64, e0: f64, h: f64, sigma_v0: f64, cv: f64) -> Self {
TerzaghiSettlement {
cc,
cr: cc / 5.0,
e0,
h,
sigma_v0,
sigma_p: sigma_v0,
cv,
}
}
pub fn over_consolidated(
cc: f64,
cr: f64,
e0: f64,
h: f64,
sigma_v0: f64,
sigma_p: f64,
cv: f64,
) -> Self {
TerzaghiSettlement {
cc,
cr,
e0,
h,
sigma_v0,
sigma_p,
cv,
}
}
pub fn primary_settlement(&self, delta_sigma: f64) -> f64 {
let sigma_f = self.sigma_v0 + delta_sigma;
if self.sigma_v0 >= self.sigma_p {
self.h * self.cc / (1.0 + self.e0) * (sigma_f / self.sigma_v0).log10()
} else if sigma_f <= self.sigma_p {
self.h * self.cr / (1.0 + self.e0) * (sigma_f / self.sigma_v0).log10()
} else {
let s1 = self.h * self.cr / (1.0 + self.e0) * (self.sigma_p / self.sigma_v0).log10();
let s2 = self.h * self.cc / (1.0 + self.e0) * (sigma_f / self.sigma_p).log10();
s1 + s2
}
}
pub fn time_factor(&self, uv: f64) -> f64 {
if uv <= 0.6 {
PI / 4.0 * uv.powi(2)
} else {
-(1.0 - uv).ln() * 1.781 - 0.933
}
}
pub fn consolidation_time(&self, uv: f64) -> f64 {
let tv = self.time_factor(uv);
let hdr = self.h / 2.0;
tv * hdr.powi(2) / self.cv
}
pub fn ocr(&self) -> f64 {
self.sigma_p / self.sigma_v0
}
}
#[derive(Debug, Clone)]
pub struct AggregateGrading {
pub sieve_sizes: Vec<f64>,
pub percent_passing: Vec<f64>,
}
impl AggregateGrading {
pub fn new(sieve_sizes: Vec<f64>, percent_passing: Vec<f64>) -> Self {
AggregateGrading {
sieve_sizes,
percent_passing,
}
}
pub fn astm_c33_coarse() -> Self {
AggregateGrading {
sieve_sizes: vec![25.0, 19.0, 12.5, 9.5, 4.75, 2.36],
percent_passing: vec![100.0, 90.0, 55.0, 35.0, 5.0, 0.0],
}
}
pub fn astm_c33_fine() -> Self {
AggregateGrading {
sieve_sizes: vec![9.5, 4.75, 2.36, 1.18, 0.60, 0.30, 0.15],
percent_passing: vec![100.0, 95.0, 80.0, 65.0, 45.0, 20.0, 5.0],
}
}
pub fn fineness_modulus(&self) -> f64 {
let sum_retained: f64 = self.percent_passing.iter().map(|p| 100.0 - p).sum();
sum_retained / 100.0
}
pub fn maximum_size(&self) -> f64 {
for (i, &p) in self.percent_passing.iter().enumerate() {
if p >= 100.0 {
return self.sieve_sizes[i];
}
}
*self.sieve_sizes.last().unwrap_or(&0.0)
}
pub fn nominal_max_size(&self) -> f64 {
for (i, &p) in self.percent_passing.iter().enumerate() {
if p < 100.0 {
if i > 0 {
return self.sieve_sizes[i - 1];
}
return self.sieve_sizes[0];
}
}
*self.sieve_sizes.last().unwrap_or(&0.0)
}
}
pub struct TimberMaterial {
pub species_grade: String,
pub moe: f64,
pub mor: f64,
pub e_longitudinal: f64,
pub e_radial: f64,
pub e_tangential: f64,
pub density: f64,
}
impl TimberMaterial {
pub fn douglas_fir_ss() -> Self {
TimberMaterial {
species_grade: "Douglas Fir - Select Structural".to_string(),
moe: 12_400.0,
mor: 62.0,
e_longitudinal: 12_400.0,
e_radial: 930.0,
e_tangential: 620.0,
density: 500.0,
}
}
pub fn adjusted_fb(&self, cd: f64, cm: f64) -> f64 {
self.mor * cd * cm
}
pub fn adjusted_e(&self, cm: f64) -> f64 {
self.moe * cm
}
pub fn shear_modulus_lr(&self) -> f64 {
self.e_longitudinal / 16.0
}
}
#[derive(Debug, Clone)]
pub struct LaminatedVeneerLumber {
pub b: f64,
pub d: f64,
pub fb: f64,
pub fc: f64,
pub ft: f64,
pub fv: f64,
pub e: f64,
}
impl LaminatedVeneerLumber {
pub fn microllam_1_9e(b: f64, d: f64) -> Self {
LaminatedVeneerLumber {
b,
d,
fb: 19.3,
fc: 19.3,
ft: 11.7,
fv: 2.07,
e: 13_100.0,
}
}
pub fn area(&self) -> f64 {
self.b * self.d
}
pub fn ix(&self) -> f64 {
self.b * self.d.powi(3) / 12.0
}
pub fn sx(&self) -> f64 {
self.b * self.d.powi(2) / 6.0
}
pub fn allowable_moment(&self) -> f64 {
self.fb * self.sx()
}
pub fn allowable_compression(&self) -> f64 {
self.fc * self.area()
}
pub fn euler_buckling_load(&self, le: f64) -> f64 {
PI.powi(2) * self.e * self.ix() / le.powi(2)
}
pub fn d_to_b_ratio(&self) -> f64 {
self.d / self.b
}
}
#[derive(Debug, Clone)]
pub struct GeosyntheticReinforcement {
pub geosyn_type: String,
pub ta: f64,
pub sv: f64,
pub phi_fill: f64,
pub gamma_fill: f64,
pub rc: f64,
}
impl GeosyntheticReinforcement {
pub fn new(
geosyn_type: &str,
ta: f64,
sv: f64,
phi_fill: f64,
gamma_fill: f64,
rc: f64,
) -> Self {
GeosyntheticReinforcement {
geosyn_type: geosyn_type.to_string(),
ta,
sv,
phi_fill,
gamma_fill,
rc,
}
}
pub fn horizontal_stress(&self, z: f64) -> f64 {
let ka = ((PI / 4.0 - self.phi_fill.to_radians() / 2.0).tan()).powi(2);
ka * self.gamma_fill * z
}
pub fn required_strength(&self, z: f64) -> f64 {
self.horizontal_stress(z) * self.sv
}
pub fn tension_fos(&self, z: f64) -> f64 {
let t_req = self.required_strength(z);
if t_req < 1e-10 {
return f64::INFINITY;
}
self.ta / t_req
}
pub fn pullout_length(&self, z: f64, fos: f64) -> f64 {
let t_max = self.required_strength(z);
let sigma_v = self.gamma_fill * z;
let f_star = 0.67 * self.phi_fill.to_radians().tan();
let denom = 2.0 * self.rc * f_star * sigma_v;
if denom < 1e-10 {
return 1.0;
}
(t_max * fos / denom).max(1.0)
}
}
pub struct SteelRebarBond {
pub db: f64,
pub fy: f64,
pub fc: f64,
pub cover: f64,
}
impl SteelRebarBond {
pub fn new(db: f64, fy: f64, fc: f64, cover: f64) -> Self {
SteelRebarBond { db, fy, fc, cover }
}
pub fn development_length(&self) -> f64 {
let psi_t = 1.0;
let psi_e = 1.0;
let lambda = 1.0;
(self.fy * psi_t * psi_e) / (1.1 * lambda * self.fc.sqrt()) * self.db
}
pub fn hook_development_length(&self) -> f64 {
let ldh_basic = 0.24 * self.fy * self.db / (self.fc.sqrt());
ldh_basic.max(8.0 * self.db).max(150.0)
}
pub fn average_bond_stress(&self) -> f64 {
let ld = self.development_length();
self.fy / (PI * ld / self.db)
}
}
#[derive(Debug, Clone)]
pub struct Admixture {
pub admixture_type: AdmixtureType,
pub dosage: f64,
pub water_reduction_pct: f64,
pub setting_time_delta: f64,
}
impl Admixture {
pub fn new(
admixture_type: AdmixtureType,
dosage: f64,
water_reduction_pct: f64,
setting_time_delta: f64,
) -> Self {
Admixture {
admixture_type,
dosage,
water_reduction_pct,
setting_time_delta,
}
}
pub fn adjusted_water(&self, base_water: f64) -> f64 {
base_water * (1.0 - self.water_reduction_pct / 100.0)
}
pub fn adjusted_wc_ratio(&self, base_wc: f64) -> f64 {
base_wc * (1.0 - self.water_reduction_pct / 100.0)
}
pub fn is_accelerator(&self) -> bool {
self.admixture_type == AdmixtureType::Accelerator
}
}
#[derive(Debug, Clone)]
pub struct CrossLaminatedTimber {
pub width: f64,
pub thickness: f64,
pub n_layers: u32,
pub layer_thickness: f64,
pub e0: f64,
pub e90: f64,
pub fb: f64,
pub g_rolling: f64,
}
impl CrossLaminatedTimber {
pub fn five_layer(width: f64, total_thickness: f64) -> Self {
let n_layers = 5u32;
let layer_t = total_thickness / n_layers as f64;
CrossLaminatedTimber {
width,
thickness: total_thickness,
n_layers,
layer_thickness: layer_t,
e0: 11_000.0,
e90: 370.0,
fb: 24.0,
g_rolling: 65.0,
}
}
pub fn effective_bending_stiffness(&self) -> f64 {
let n_par = self.n_layers.div_ceil(2);
let h = self.layer_thickness;
let mut ei = 0.0;
for i in 0..n_par {
let yi = (self.thickness / 2.0) - h / 2.0 - i as f64 * 2.0 * h;
ei += self.e0 * self.width * h.powi(3) / 12.0 + self.e0 * self.width * h * yi.powi(2);
}
ei
}
pub fn effective_axial_stiffness(&self) -> f64 {
let n_par = self.n_layers.div_ceil(2);
self.e0 * self.width * self.layer_thickness * n_par as f64
}
pub fn rolling_shear_capacity(&self) -> f64 {
self.g_rolling * self.layer_thickness / self.width
}
pub fn area_per_m(&self) -> f64 {
self.thickness * 1000.0
}
}
#[derive(Debug, Clone)]
pub struct MasonryShearWall {
pub length: f64,
pub thickness: f64,
pub height: f64,
pub fm: f64,
pub av: f64,
pub fy: f64,
pub sigma_v: f64,
}
impl MasonryShearWall {
pub fn new(
length: f64,
thickness: f64,
height: f64,
fm: f64,
av: f64,
fy: f64,
sigma_v: f64,
) -> Self {
MasonryShearWall {
length,
thickness,
height,
fm,
av,
fy,
sigma_v,
}
}
pub fn net_area(&self) -> f64 {
self.length * self.thickness
}
pub fn in_plane_shear_capacity(&self) -> f64 {
let m_v_d = (self.height / self.length).min(1.0);
let an = self.net_area();
let p = self.sigma_v * an;
let vnm = (4.0 - 1.75 * m_v_d) * an * self.fm.sqrt() + 0.25 * p;
let vns = 0.5 * self.av * self.fy * self.length / 1000.0;
(vnm + vns).min(6.0 * an * self.fm.sqrt())
}
pub fn out_of_plane_moment_capacity(&self) -> f64 {
let d = self.thickness / 2.0;
self.av * self.fy * d / 1000.0
}
pub fn aspect_ratio(&self) -> f64 {
self.height / self.length
}
}
#[derive(Debug, Clone)]
pub struct PileFoundation {
pub diameter: f64,
pub length: f64,
pub pile_type: String,
pub qs: f64,
pub qb: f64,
pub cu: f64,
pub alpha: f64,
}
impl PileFoundation {
pub fn concrete_pile(diameter: f64, length: f64, qs: f64, qb: f64) -> Self {
PileFoundation {
diameter,
length,
pile_type: "concrete".to_string(),
qs,
qb,
cu: qs,
alpha: 0.5,
}
}
pub fn perimeter(&self) -> f64 {
PI * self.diameter
}
pub fn tip_area(&self) -> f64 {
PI * (self.diameter / 2.0).powi(2)
}
pub fn skin_friction(&self) -> f64 {
self.qs * self.perimeter() * self.length
}
pub fn end_bearing(&self) -> f64 {
self.qb * self.tip_area()
}
pub fn ultimate_capacity(&self) -> f64 {
self.skin_friction() + self.end_bearing()
}
pub fn allowable_capacity(&self) -> f64 {
self.ultimate_capacity() / 2.5
}
pub fn alpha_skin_friction(&self) -> f64 {
self.alpha * self.cu * self.perimeter() * self.length
}
pub fn elastic_compression(&self, load_kn: f64) -> f64 {
let e_pile = if self.pile_type == "concrete" {
25_000.0
} else {
200_000.0
};
let area_mm2 = self.tip_area() * 1e6;
let load_n = load_kn * 1000.0;
let length_mm = self.length * 1000.0;
load_n * length_mm / (e_pile * area_mm2)
}
}
pub struct ConcreteMaterial {
pub fc: f64,
pub ft: f64,
pub ec: f64,
pub poisson: f64,
pub shrinkage_strain: f64,
pub creep_coefficient: f64,
pub density: f64,
}
impl ConcreteMaterial {
pub fn new(fc: f64) -> Self {
let ec = 4700.0 * fc.sqrt();
let ft = 0.1 * fc;
ConcreteMaterial {
fc,
ft,
ec,
poisson: 0.2,
shrinkage_strain: 3e-4,
creep_coefficient: 2.0,
density: 2400.0,
}
}
pub fn shear_modulus(&self) -> f64 {
self.ec / (2.0 * (1.0 + self.poisson))
}
pub fn splitting_tensile_strength(&self) -> f64 {
0.56 * self.fc.sqrt()
}
pub fn ultimate_compressive_strain(&self) -> f64 {
0.003
}
pub fn modulus_of_rupture(&self) -> f64 {
0.62 * self.fc.sqrt()
}
}
pub struct ReinforcedConcrete {
pub concrete: ConcreteMaterial,
pub b: f64,
pub d: f64,
pub as_t: f64,
pub fy: f64,
pub as_comp: f64,
pub d_prime: f64,
}
impl ReinforcedConcrete {
#[allow(clippy::too_many_arguments)]
pub fn new(
concrete: ConcreteMaterial,
b: f64,
d: f64,
as_t: f64,
fy: f64,
as_comp: f64,
d_prime: f64,
) -> Self {
ReinforcedConcrete {
concrete,
b,
d,
as_t,
fy,
as_comp,
d_prime,
}
}
pub fn moment_capacity(&self) -> f64 {
let fc = self.concrete.fc;
let beta1 = if fc <= 28.0 {
0.85
} else {
(0.85 - 0.05 * (fc - 28.0) / 7.0).max(0.65)
};
let a = self.as_t * self.fy / (0.85 * fc * self.b);
let c = a / beta1;
let eps_cu = 0.003;
let eps_prime = eps_cu * (c - self.d_prime) / c;
let fs_prime = if eps_prime >= self.fy / 200_000.0 {
self.fy
} else {
eps_prime * 200_000.0
};
self.as_t * self.fy * (self.d - a / 2.0) + self.as_comp * fs_prime * (self.d - self.d_prime)
}
pub fn design_moment_capacity(&self) -> f64 {
0.9 * self.moment_capacity()
}
pub fn shear_capacity(&self) -> f64 {
0.17 * self.concrete.fc.sqrt() * self.b * self.d
}
pub fn reinforcement_ratio(&self) -> f64 {
self.as_t / (self.b * self.d)
}
pub fn rho_min(&self) -> f64 {
let term1 = 0.25 * self.concrete.fc.sqrt() / self.fy;
let term2 = 1.4 / self.fy;
term1.max(term2)
}
}
#[derive(Debug, Clone)]
pub struct HssSection {
pub b: f64,
pub h: f64,
pub t: f64,
pub fy: f64,
pub e: f64,
}
impl HssSection {
pub fn rectangular(b: f64, h: f64, t: f64, fy: f64) -> Self {
HssSection {
b,
h,
t,
fy,
e: 200_000.0,
}
}
pub fn square(b: f64, t: f64, fy: f64) -> Self {
HssSection {
b,
h: b,
t,
fy,
e: 200_000.0,
}
}
pub fn area(&self) -> f64 {
self.b * self.h - (self.b - 2.0 * self.t) * (self.h - 2.0 * self.t)
}
pub fn ix(&self) -> f64 {
(self.b * self.h.powi(3) - (self.b - 2.0 * self.t) * (self.h - 2.0 * self.t).powi(3)) / 12.0
}
pub fn iy(&self) -> f64 {
(self.h * self.b.powi(3) - (self.h - 2.0 * self.t) * (self.b - 2.0 * self.t).powi(3)) / 12.0
}
pub fn sx(&self) -> f64 {
self.ix() / (self.h / 2.0)
}
pub fn zx(&self) -> f64 {
let outer = self.b * self.h.powi(2) / 4.0;
let inner = (self.b - 2.0 * self.t) * (self.h - 2.0 * self.t).powi(2) / 4.0;
outer - inner
}
pub fn plastic_moment(&self) -> f64 {
self.zx() * self.fy
}
pub fn torsional_constant(&self) -> f64 {
let h_mid = self.h - self.t;
let b_mid = self.b - self.t;
2.0 * self.t * b_mid * h_mid * (b_mid * h_mid) / (b_mid + h_mid)
}
pub fn warping_constant(&self) -> f64 {
0.0
}
pub fn web_slenderness(&self) -> f64 {
(self.h - 2.0 * self.t) / self.t
}
pub fn flange_slenderness(&self) -> f64 {
(self.b - 2.0 * self.t) / self.t
}
pub fn compact_flange_limit(&self) -> f64 {
1.12 * (self.e / self.fy).sqrt()
}
pub fn flange_is_compact(&self) -> bool {
self.flange_slenderness() <= self.compact_flange_limit()
}
}
#[derive(Debug, Clone)]
pub struct EurocodeLoad {
pub gk: f64,
pub qk1: f64,
pub qk2: f64,
pub wk: f64,
pub sk: f64,
pub ed: f64,
}
impl EurocodeLoad {
pub fn new(gk: f64, qk1: f64, qk2: f64, wk: f64, sk: f64, ed: f64) -> Self {
EurocodeLoad {
gk,
qk1,
qk2,
wk,
sk,
ed,
}
}
pub fn uls_combo_610(&self) -> f64 {
1.35 * self.gk + 1.50 * self.qk1 + 1.50 * 0.7 * self.qk2
}
pub fn uls_combo_610a(&self) -> f64 {
1.35 * self.gk + 1.50 * 0.7 * self.qk1
}
pub fn uls_combo_610b(&self) -> f64 {
0.85 * 1.35 * self.gk + 1.50 * self.qk1
}
pub fn uls_seismic(&self) -> f64 {
self.gk + self.ed + 0.3 * self.qk1
}
pub fn sls_characteristic(&self) -> f64 {
self.gk + self.qk1 + 0.7 * self.qk2
}
pub fn sls_frequent(&self) -> f64 {
self.gk + 0.5 * self.qk1 + 0.3 * self.qk2
}
pub fn sls_quasi_permanent(&self) -> f64 {
self.gk + 0.3 * self.qk1
}
pub fn governing_uls(&self) -> f64 {
[
self.uls_combo_610(),
self.uls_combo_610a(),
self.uls_combo_610b(),
]
.iter()
.cloned()
.fold(f64::NEG_INFINITY, f64::max)
}
pub fn uls_wind_uplift(&self) -> f64 {
0.9 * self.gk + 1.50 * self.wk
}
pub fn uls_snow(&self) -> f64 {
1.35 * self.gk + 1.50 * self.sk
}
}
#[derive(Debug, Clone)]
pub struct ChannelSection {
pub d: f64,
pub bf: f64,
pub tf: f64,
pub tw: f64,
pub fy: f64,
}
impl ChannelSection {
pub fn new(d: f64, bf: f64, tf: f64, tw: f64, fy: f64) -> Self {
ChannelSection { d, bf, tf, tw, fy }
}
pub fn area(&self) -> f64 {
2.0 * self.bf * self.tf + (self.d - 2.0 * self.tf) * self.tw
}
pub fn shear_center_x(&self) -> f64 {
let bf = self.bf;
let tf = self.tf;
let d = self.d;
let tw = self.tw;
let hw = d - 2.0 * tf;
let ix = self.ix();
let sx = if d > 0.0 { ix / (d / 2.0) } else { 1.0 };
bf.powi(2) * tf / (2.0 * sx / d) / (1.0 + (hw * tw) / (6.0 * bf * tf))
}
pub fn ix(&self) -> f64 {
let hw = self.d - 2.0 * self.tf;
self.bf * self.d.powi(3) / 12.0 - (self.bf - self.tw) * hw.powi(3) / 12.0
}
pub fn sx(&self) -> f64 {
self.ix() / (self.d / 2.0)
}
pub fn moment_capacity(&self) -> f64 {
self.sx() * self.fy
}
pub fn shear_capacity(&self) -> f64 {
let hw = self.d - 2.0 * self.tf;
0.6 * self.fy * hw * self.tw
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum AdmixtureType {
WaterReducer,
Superplasticiser,
Accelerator,
Retarder,
AirEntrainer,
ShrinkageReducer,
CorrosionInhibitor,
}
#[derive(Debug, Clone)]
pub struct MoistureCorrectionTimber {
pub mc_service: f64,
pub mc_fsp: f64,
pub e_green: f64,
pub fb_green: f64,
}
impl MoistureCorrectionTimber {
pub fn douglas_fir() -> Self {
MoistureCorrectionTimber {
mc_service: 15.0,
mc_fsp: 28.0,
e_green: 11_000.0,
fb_green: 40.0,
}
}
pub fn cm_factor_e(&self) -> f64 {
if self.mc_service >= self.mc_fsp {
return 1.0;
}
if self.mc_service <= 19.0 {
1.0
} else {
1.0 - 0.1 * (self.mc_service - 19.0) / (self.mc_fsp - 19.0)
}
}
pub fn cm_factor_fb(&self) -> f64 {
if self.mc_service >= self.mc_fsp {
return 0.85;
}
if self.mc_service <= 19.0 {
1.0
} else {
0.85 + 0.15 * (self.mc_fsp - self.mc_service) / (self.mc_fsp - 19.0)
}
}
pub fn adjusted_e(&self) -> f64 {
self.e_green * self.cm_factor_e()
}
pub fn adjusted_fb(&self) -> f64 {
self.fb_green * self.cm_factor_fb()
}
pub fn tangential_shrinkage_per_pct_mc(&self) -> f64 {
0.29
}
pub fn dimensional_change(&self, dimension_mm: f64) -> f64 {
let delta_mc = (self.mc_fsp - self.mc_service).max(0.0);
dimension_mm * self.tangential_shrinkage_per_pct_mc() * delta_mc / 100.0
}
}
#[derive(Debug, Clone)]
pub struct GeotextileFilter {
pub aos: f64,
pub permittivity: f64,
pub transmissivity: f64,
pub tensile_strength: f64,
pub elongation: f64,
pub soil_d85: f64,
pub soil_d15: f64,
pub kp: f64,
pub kn: f64,
}
impl GeotextileFilter {
pub fn woven_road_drainage() -> Self {
GeotextileFilter {
aos: 0.212,
permittivity: 0.5,
transmissivity: 1e-4,
tensile_strength: 40.0,
elongation: 15.0,
soil_d85: 0.3,
soil_d15: 0.05,
kp: 1e-3,
kn: 1e-4,
}
}
pub fn retention_criterion_met(&self, b: f64) -> bool {
self.aos <= b * self.soil_d85
}
pub fn permeability_criterion_met(&self, k_soil: f64) -> bool {
self.kn >= k_soil
}
pub fn gradient_ratio(&self) -> f64 {
if self.soil_d15 < 1e-10 {
return f64::INFINITY;
}
self.aos / self.soil_d15
}
pub fn filter_ratio(&self) -> f64 {
if self.soil_d85 < 1e-10 {
return f64::INFINITY;
}
self.aos / self.soil_d85
}
pub fn overlap_width(&self, trench_depth_m: f64) -> f64 {
(0.3_f64).max(trench_depth_m * 0.5)
}
}
#[derive(Debug, Clone)]
pub struct Geogrid {
pub tult_md: f64,
pub tult_cmd: f64,
pub junction_efficiency: f64,
pub aperture_size: f64,
pub ltds: f64,
pub coverage_ratio: f64,
}
impl Geogrid {
pub fn bx1100() -> Self {
Geogrid {
tult_md: 15.0,
tult_cmd: 20.0,
junction_efficiency: 93.0,
aperture_size: 33.0,
ltds: 8.0,
coverage_ratio: 0.35,
}
}
pub fn ux1500hs() -> Self {
Geogrid {
tult_md: 150.0,
tult_cmd: 25.0,
junction_efficiency: 91.0,
aperture_size: 16.0,
ltds: 80.0,
coverage_ratio: 0.70,
}
}
pub fn allowable_strength(&self, rf_id: f64, rf_cr: f64) -> f64 {
self.ltds / (rf_id * rf_cr)
}
pub fn interaction_coefficient(&self, phi_soil: f64) -> f64 {
let base_ci = 0.8;
let phi = phi_soil.to_radians();
base_ci * phi.tan() / phi.tan()
}
pub fn passive_resistance(&self, sigma_v: f64) -> f64 {
self.ltds * sigma_v / 100.0
}
}
#[derive(Debug, Clone)]
pub struct StructuralLoad {
pub dead: f64,
pub live: f64,
pub wind: f64,
pub seismic: f64,
pub snow: f64,
}
impl StructuralLoad {
pub fn new(dead: f64, live: f64, wind: f64, seismic: f64, snow: f64) -> Self {
StructuralLoad {
dead,
live,
wind,
seismic,
snow,
}
}
pub fn lrfd_combo1(&self) -> f64 {
1.4 * self.dead
}
pub fn lrfd_combo2(&self) -> f64 {
1.2 * self.dead + 1.6 * self.live + 0.5 * self.snow
}
pub fn lrfd_combo3(&self) -> f64 {
1.2 * self.dead + 1.0 * self.wind + 1.0 * self.live + 0.5 * self.snow
}
pub fn lrfd_combo4(&self) -> f64 {
0.9 * self.dead + 1.0 * self.wind
}
pub fn lrfd_seismic(&self) -> f64 {
1.2 * self.dead + 1.0 * self.seismic + 1.0 * self.live + 0.2 * self.snow
}
pub fn asd_combo_dl(&self) -> f64 {
self.dead + self.live
}
pub fn governing_lrfd(&self) -> f64 {
[
self.lrfd_combo1(),
self.lrfd_combo2(),
self.lrfd_combo3(),
self.lrfd_combo4(),
self.lrfd_seismic(),
]
.iter()
.cloned()
.fold(f64::NEG_INFINITY, f64::max)
}
}
pub struct AsphaltMixture {
pub bitumen_content: f64,
pub vma: f64,
pub vfa: f64,
pub air_voids: f64,
pub dynamic_stability: f64,
pub stability: f64,
pub flow: f64,
}
impl AsphaltMixture {
pub fn superpave_12_5() -> Self {
AsphaltMixture {
bitumen_content: 5.0,
vma: 14.0,
vfa: 72.0,
air_voids: 4.0,
dynamic_stability: 1000.0,
stability: 8000.0,
flow: 3.0,
}
}
pub fn bulk_density(&self, gmm: f64) -> f64 {
(100.0 - self.air_voids) / 100.0 * gmm
}
pub fn meets_air_voids_criterion(&self) -> bool {
(self.air_voids - 4.0).abs() < 0.5
}
}
#[derive(Debug, Clone)]
pub struct SteelSection {
pub area: f64,
pub ix: f64,
pub iy: f64,
pub zx: f64,
pub zy: f64,
pub fy: f64,
pub e: f64,
}
impl SteelSection {
pub fn i_section(bf: f64, tf: f64, d: f64, tw: f64, fy: f64) -> Self {
let hw = d - 2.0 * tf;
let area = 2.0 * bf * tf + hw * tw;
let ix = bf * d.powi(3) / 12.0 - (bf - tw) * hw.powi(3) / 12.0;
let iy = 2.0 * tf * bf.powi(3) / 12.0 + hw * tw.powi(3) / 12.0;
let zx = bf * tf * (d / 2.0 - tf / 2.0) * 2.0 + tw * hw.powi(2) / 4.0;
let zy = bf.powi(2) * tf / 2.0 + tw.powi(2) * hw / 4.0;
SteelSection {
area,
ix,
iy,
zx,
zy,
fy,
e: 200_000.0,
}
}
pub fn sx(&self, d: f64) -> f64 {
self.ix / (d / 2.0)
}
pub fn plastic_moment(&self) -> f64 {
self.zx * self.fy
}
pub fn axial_capacity(&self) -> f64 {
self.area * self.fy
}
pub fn rx(&self) -> f64 {
(self.ix / self.area).sqrt()
}
pub fn ry(&self) -> f64 {
(self.iy / self.area).sqrt()
}
pub fn elastic_buckling_stress(&self, kl_over_r: f64) -> f64 {
PI.powi(2) * self.e / kl_over_r.powi(2)
}
}
pub struct FoundationSoil {
pub cu: f64,
pub phi_deg: f64,
pub c_prime: f64,
pub gamma: f64,
pub spt_n: u32,
pub constrained_modulus: f64,
}
impl FoundationSoil {
pub fn medium_clay() -> Self {
FoundationSoil {
cu: 50.0,
phi_deg: 0.0,
c_prime: 50.0,
gamma: 18.0,
spt_n: 10,
constrained_modulus: 5.0,
}
}
pub fn ultimate_bearing_capacity(&self, b: f64, df: f64) -> f64 {
let phi = self.phi_deg.to_radians();
let (nc, nq, ng) = terzaghi_bearing_factors(phi);
self.c_prime * nc + self.gamma * df * nq + 0.5 * self.gamma * b * ng
}
pub fn allowable_bearing_capacity(&self, b: f64, df: f64) -> f64 {
self.ultimate_bearing_capacity(b, df) / 3.0
}
pub fn immediate_settlement(&self, q: f64, b: f64, es_mpa: f64, nu: f64) -> f64 {
let is = 0.82;
q * b * (1.0 - nu * nu) * is / (es_mpa * 1000.0)
}
}
#[derive(Debug, Clone)]
pub struct PrestressedConcrete {
pub ag: f64,
pub ig: f64,
pub yb: f64,
pub yt: f64,
pub aps: f64,
pub fpu: f64,
pub fpi: f64,
pub eccentricity: f64,
pub fc: f64,
pub span: f64,
}
impl PrestressedConcrete {
#[allow(clippy::too_many_arguments)]
pub fn new(
ag: f64,
ig: f64,
yb: f64,
yt: f64,
aps: f64,
fpu: f64,
fpi: f64,
eccentricity: f64,
fc: f64,
span: f64,
) -> Self {
PrestressedConcrete {
ag,
ig,
yb,
yt,
aps,
fpu,
fpi,
eccentricity,
fc,
span,
}
}
pub fn initial_prestress_force(&self) -> f64 {
self.aps * self.fpi
}
pub fn effective_prestress_force(&self, loss_fraction: f64) -> f64 {
self.initial_prestress_force() * (1.0 - loss_fraction)
}
pub fn upper_kern(&self) -> f64 {
self.ig / (self.ag * self.yb)
}
pub fn lower_kern(&self) -> f64 {
self.ig / (self.ag * self.yt)
}
pub fn bottom_fiber_stress(&self, pe: f64, m: f64) -> f64 {
let p_term = -pe / self.ag;
let e_term = -pe * self.eccentricity * self.yb / self.ig;
let m_term = m * self.yb / self.ig;
p_term + e_term + m_term
}
pub fn top_fiber_stress(&self, pe: f64, m: f64) -> f64 {
let p_term = -pe / self.ag;
let e_term = pe * self.eccentricity * self.yt / self.ig;
let m_term = -m * self.yt / self.ig;
p_term + e_term + m_term
}
pub fn cracking_moment(&self, pe: f64) -> f64 {
let fr = 0.62 * self.fc.sqrt();
let sb = self.ig / self.yb;
(pe / self.ag + pe * self.eccentricity / sb + fr) * sb
}
pub fn elastic_shortening_loss(&self) -> f64 {
let es_steel = 197_000.0;
let ec = 4700.0 * self.fc.sqrt();
let n = es_steel / ec;
let pe = self.initial_prestress_force();
let fc_cgs = pe / self.ag + pe * self.eccentricity.powi(2) / self.ig;
n * fc_cgs
}
pub fn shrinkage_loss(&self) -> f64 {
70.0
}
pub fn creep_loss(&self) -> f64 {
let ccr = 2.0;
let n = 197_000.0 / (4700.0 * self.fc.sqrt());
let pe = self.effective_prestress_force(0.0);
let fc_cgs = pe / self.ag + pe * self.eccentricity.powi(2) / self.ig;
ccr * n * fc_cgs
}
pub fn total_losses(&self) -> f64 {
self.elastic_shortening_loss() + self.shrinkage_loss() + self.creep_loss()
}
pub fn nominal_flexural_strength(&self) -> f64 {
let rho_p = self.aps / (self.ag * 0.8);
let fps = self.fpu * (1.0 - 0.5 * rho_p * self.fpu / self.fc);
let dp = self.yb;
let a = fps * self.aps / (0.85 * self.fc * (self.ag / self.yb));
fps * self.aps * (dp - a / 2.0)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ExposureClass {
Interior,
Moderate,
Severe,
Submerged,
}
pub struct MasonryUnit {
pub fm: f64,
pub mortar_type: String,
pub bond_pattern: String,
pub em: f64,
pub density: f64,
}
impl MasonryUnit {
pub fn clay_brick(fm: f64) -> Self {
let em = 700.0 * fm;
MasonryUnit {
fm,
mortar_type: "S".to_string(),
bond_pattern: "Running".to_string(),
em,
density: 1900.0,
}
}
pub fn shear_modulus(&self) -> f64 {
0.4 * self.em
}
pub fn modulus_of_rupture(&self) -> f64 {
0.064 * self.fm
}
}
#[derive(Debug, Clone)]
pub struct GlulamSection {
pub b: f64,
pub d: f64,
pub n_lam: u32,
pub lam_thickness: f64,
pub fb: f64,
pub fv: f64,
pub e: f64,
pub cm: f64,
}
impl GlulamSection {
pub fn new(b: f64, d: f64, n_lam: u32, fb: f64, fv: f64, e: f64) -> Self {
let lam_thickness = d / n_lam as f64;
GlulamSection {
b,
d,
n_lam,
lam_thickness,
fb,
fv,
e,
cm: 1.0,
}
}
pub fn df_24f_v4() -> Self {
GlulamSection {
b: 275.0,
d: 570.0,
n_lam: 19,
lam_thickness: 30.0,
fb: 16.5,
fv: 2.4,
e: 12_400.0,
cm: 1.0,
}
}
pub fn area(&self) -> f64 {
self.b * self.d
}
pub fn ix(&self) -> f64 {
self.b * self.d.powi(3) / 12.0
}
pub fn sx(&self) -> f64 {
self.b * self.d.powi(2) / 6.0
}
pub fn volume_factor(&self, l_m: f64) -> f64 {
let kl = 1.09;
let b_ft = self.b / 25.4 / 12.0;
let d_ft = self.d / 25.4 / 12.0;
let l_ft = l_m * 3.28084;
(kl * 21.0 / l_ft).powf(0.1)
* (12.0 / (d_ft * 12.0)).powf(0.1)
* (5.125 / (b_ft * 12.0)).powf(0.1).min(1.0)
}
pub fn adjusted_fb(&self, cd: f64, cv: f64) -> f64 {
self.fb * cd * self.cm * cv
}
pub fn allowable_moment(&self, cd: f64, cv: f64) -> f64 {
self.adjusted_fb(cd, cv) * self.sx()
}
pub fn allowable_shear(&self) -> f64 {
let fv_prime = self.fv * self.cm;
fv_prime * (2.0 / 3.0) * self.b * self.d
}
pub fn midspan_deflection(&self, w_n_per_mm: f64, span_mm: f64) -> f64 {
5.0 * w_n_per_mm * span_mm.powi(4) / (384.0 * self.e * self.ix())
}
}