1use super::functions::terzaghi_bearing_factors;
6#[allow(unused_imports)]
7use super::functions::*;
8use std::f64::consts::PI;
9
10pub struct CoverRequirement;
12impl CoverRequirement {
13 pub fn minimum_cover(exposure: &ExposureClass, bar_dia: f64) -> f64 {
15 match exposure {
16 ExposureClass::Interior => {
17 if bar_dia <= 16.0 {
18 20.0
19 } else {
20 40.0
21 }
22 }
23 ExposureClass::Moderate => 50.0,
24 ExposureClass::Severe => 65.0,
25 ExposureClass::Submerged => 75.0,
26 }
27 }
28 pub fn effective_depth(h: f64, cover: f64, bar_dia: f64) -> f64 {
30 h - cover - bar_dia / 2.0
31 }
32}
33#[derive(Debug, Clone)]
35pub struct StirrupDesign {
36 pub bw: f64,
38 pub d: f64,
40 pub fc: f64,
42 pub fy: f64,
44 pub av: f64,
46 pub legs: u32,
48 pub spacing: f64,
50}
51impl StirrupDesign {
52 pub fn new(bw: f64, d: f64, fc: f64, fy: f64, av: f64, legs: u32, spacing: f64) -> Self {
54 StirrupDesign {
55 bw,
56 d,
57 fc,
58 fy,
59 av,
60 legs,
61 spacing,
62 }
63 }
64 pub fn vc(&self) -> f64 {
66 0.17 * self.fc.sqrt() * self.bw * self.d
67 }
68 pub fn vs(&self) -> f64 {
70 let total_av = self.av * self.legs as f64;
71 total_av * self.fy * self.d / self.spacing
72 }
73 pub fn vn(&self) -> f64 {
75 self.vc() + self.vs()
76 }
77 pub fn phi_vn(&self) -> f64 {
79 0.75 * self.vn()
80 }
81 pub fn max_spacing(&self) -> f64 {
83 (self.d / 2.0).min(600.0)
84 }
85 pub fn min_av_per_s(&self) -> f64 {
87 let t1 = 0.062 * self.fc.sqrt() * self.bw / self.fy;
88 let t2 = 0.35 * self.bw / self.fy;
89 t1.max(t2)
90 }
91 pub fn is_adequate(&self, vu: f64) -> bool {
93 self.phi_vn() >= vu
94 }
95}
96#[derive(Debug, Clone)]
98pub struct AngleSection {
99 pub l: f64,
101 pub t: f64,
103 pub fy: f64,
105}
106impl AngleSection {
107 pub fn equal_leg(l: f64, t: f64, fy: f64) -> Self {
109 AngleSection { l, t, fy }
110 }
111 pub fn area(&self) -> f64 {
113 2.0 * self.l * self.t - self.t.powi(2)
114 }
115 pub fn centroid(&self) -> f64 {
117 let a = self.area();
118 if a < 1e-10 {
119 return 0.0;
120 }
121 let a1 = self.l * self.t;
122 let x1 = self.t / 2.0;
123 let a2 = (self.l - self.t) * self.t;
124 let x2 = self.t + (self.l - self.t) / 2.0;
125 (a1 * x1 + a2 * x2) / a
126 }
127 pub fn ixx(&self) -> f64 {
129 let t = self.t;
130 let l = self.l;
131 let i_horiz = l * t.powi(3) / 12.0 + l * t * (t / 2.0).powi(2);
132 let i_vert = t * (l - t).powi(3) / 12.0 + t * (l - t) * ((l - t) / 2.0 + t).powi(2);
133 i_horiz + i_vert
134 }
135 pub fn rz(&self) -> f64 {
137 (self.ixx() / self.area()).sqrt()
138 }
139 pub fn axial_capacity(&self) -> f64 {
141 self.area() * self.fy
142 }
143}
144pub struct RetainingWall {
146 pub height: f64,
148 pub gamma: f64,
150 pub phi_deg: f64,
152 pub delta_deg: f64,
154 pub beta_deg: f64,
156}
157impl RetainingWall {
158 pub fn new(height: f64, gamma: f64, phi_deg: f64) -> Self {
160 RetainingWall {
161 height,
162 gamma,
163 phi_deg,
164 delta_deg: phi_deg * 2.0 / 3.0,
165 beta_deg: 0.0,
166 }
167 }
168 pub fn rankine_ka(&self) -> f64 {
170 let phi = self.phi_deg.to_radians();
171 ((PI / 4.0 - phi / 2.0).tan()).powi(2)
172 }
173 pub fn rankine_kp(&self) -> f64 {
175 let phi = self.phi_deg.to_radians();
176 ((PI / 4.0 + phi / 2.0).tan()).powi(2)
177 }
178 pub fn active_thrust_rankine(&self) -> f64 {
180 0.5 * self.gamma * self.height.powi(2) * self.rankine_ka()
181 }
182 pub fn passive_resistance_rankine(&self) -> f64 {
184 0.5 * self.gamma * self.height.powi(2) * self.rankine_kp()
185 }
186 pub fn coulomb_ka(&self) -> f64 {
188 let phi = self.phi_deg.to_radians();
189 let delta = self.delta_deg.to_radians();
190 let beta = self.beta_deg.to_radians();
191 let alpha = PI / 2.0;
192 let num = (phi - beta).sin().powi(2);
193 let term = 1.0
194 + ((phi + delta).sin() * (phi - beta).sin()
195 / ((alpha + delta).sin() * (alpha - beta).sin()))
196 .sqrt();
197 num / ((alpha).sin().powi(2) * (alpha - delta).sin() * term.powi(2))
198 }
199 pub fn overturning_factor(&self, footing_width: f64) -> f64 {
201 let pa = self.active_thrust_rankine();
202 let overturning_moment = pa * self.height / 3.0;
203 let restoring_moment = self.gamma * self.height * footing_width * footing_width / 2.0;
204 if overturning_moment < 1e-10 {
205 return f64::INFINITY;
206 }
207 restoring_moment / overturning_moment
208 }
209}
210#[derive(Debug, Clone)]
212pub struct MasonryPrism {
213 pub unit_strength: f64,
215 pub mortar_type: String,
217 pub h_t_ratio: f64,
219 pub fp_measured: f64,
221}
222impl MasonryPrism {
223 pub fn new(unit_strength: f64, mortar_type: &str, h_t_ratio: f64, fp_measured: f64) -> Self {
225 MasonryPrism {
226 unit_strength,
227 mortar_type: mortar_type.to_string(),
228 h_t_ratio,
229 fp_measured,
230 }
231 }
232 pub fn ht_correction_factor(&self) -> f64 {
234 if self.h_t_ratio >= 5.0 {
235 1.0
236 } else if self.h_t_ratio >= 2.0 {
237 0.75 + 0.05 * (self.h_t_ratio - 2.0)
238 } else {
239 0.75
240 }
241 }
242 pub fn fm_corrected(&self) -> f64 {
244 self.fp_measured * self.ht_correction_factor()
245 }
246 pub fn em(&self) -> f64 {
248 900.0 * self.fm_corrected()
249 }
250 pub fn allowable_compressive_stress(&self) -> f64 {
252 0.25 * self.fm_corrected()
253 }
254}
255#[derive(Debug, Clone)]
257pub struct ConcreteMixDesign {
258 pub wc_ratio: f64,
260 pub cement: f64,
262 pub water: f64,
264 pub fine_agg: f64,
266 pub coarse_agg: f64,
268 pub superplasticiser: f64,
270 pub air_content: f64,
272 pub slump: f64,
274}
275impl ConcreteMixDesign {
276 pub fn new(
278 wc_ratio: f64,
279 cement: f64,
280 fine_agg: f64,
281 coarse_agg: f64,
282 air_content: f64,
283 ) -> Self {
284 let water = wc_ratio * cement;
285 ConcreteMixDesign {
286 wc_ratio,
287 cement,
288 water,
289 fine_agg,
290 coarse_agg,
291 superplasticiser: 0.0,
292 air_content,
293 slump: 75.0,
294 }
295 }
296 pub fn c30_normal_weight() -> Self {
298 ConcreteMixDesign {
299 wc_ratio: 0.50,
300 cement: 350.0,
301 water: 175.0,
302 fine_agg: 720.0,
303 coarse_agg: 1080.0,
304 superplasticiser: 0.0,
305 air_content: 2.0,
306 slump: 75.0,
307 }
308 }
309 pub fn high_performance() -> Self {
311 ConcreteMixDesign {
312 wc_ratio: 0.30,
313 cement: 500.0,
314 water: 150.0,
315 fine_agg: 700.0,
316 coarse_agg: 1050.0,
317 superplasticiser: 1.5,
318 air_content: 1.5,
319 slump: 180.0,
320 }
321 }
322 pub fn fresh_unit_weight(&self) -> f64 {
324 self.cement + self.water + self.fine_agg + self.coarse_agg
325 }
326 pub fn abrams_strength(&self) -> f64 {
329 let a = 96.0_f64;
330 let b = 8.6_f64;
331 a / b.powf(self.wc_ratio)
332 }
333 pub fn paste_volume(&self) -> f64 {
335 let rho_c = 3150.0;
336 let rho_w = 1000.0;
337 self.cement / rho_c + self.water / rho_w
338 }
339 pub fn aggregate_cement_ratio(&self) -> f64 {
341 (self.fine_agg + self.coarse_agg) / self.cement
342 }
343 pub fn meets_durability_wc_limit(&self, max_wc: f64) -> bool {
345 self.wc_ratio <= max_wc
346 }
347 pub fn combined_fineness_modulus(&self, fm_fine: f64, fm_coarse: f64) -> f64 {
350 let total = self.fine_agg + self.coarse_agg;
351 if total < 1e-6 {
352 return 0.0;
353 }
354 (self.fine_agg * fm_fine + self.coarse_agg * fm_coarse) / total
355 }
356}
357#[derive(Debug, Clone)]
359pub struct TerzaghiSettlement {
360 pub cc: f64,
362 pub cr: f64,
364 pub e0: f64,
366 pub h: f64,
368 pub sigma_v0: f64,
370 pub sigma_p: f64,
372 pub cv: f64,
374}
375impl TerzaghiSettlement {
376 pub fn normally_consolidated(cc: f64, e0: f64, h: f64, sigma_v0: f64, cv: f64) -> Self {
378 TerzaghiSettlement {
379 cc,
380 cr: cc / 5.0,
381 e0,
382 h,
383 sigma_v0,
384 sigma_p: sigma_v0,
385 cv,
386 }
387 }
388 pub fn over_consolidated(
390 cc: f64,
391 cr: f64,
392 e0: f64,
393 h: f64,
394 sigma_v0: f64,
395 sigma_p: f64,
396 cv: f64,
397 ) -> Self {
398 TerzaghiSettlement {
399 cc,
400 cr,
401 e0,
402 h,
403 sigma_v0,
404 sigma_p,
405 cv,
406 }
407 }
408 pub fn primary_settlement(&self, delta_sigma: f64) -> f64 {
410 let sigma_f = self.sigma_v0 + delta_sigma;
411 if self.sigma_v0 >= self.sigma_p {
412 self.h * self.cc / (1.0 + self.e0) * (sigma_f / self.sigma_v0).log10()
413 } else if sigma_f <= self.sigma_p {
414 self.h * self.cr / (1.0 + self.e0) * (sigma_f / self.sigma_v0).log10()
415 } else {
416 let s1 = self.h * self.cr / (1.0 + self.e0) * (self.sigma_p / self.sigma_v0).log10();
417 let s2 = self.h * self.cc / (1.0 + self.e0) * (sigma_f / self.sigma_p).log10();
418 s1 + s2
419 }
420 }
421 pub fn time_factor(&self, uv: f64) -> f64 {
424 if uv <= 0.6 {
425 PI / 4.0 * uv.powi(2)
426 } else {
427 -(1.0 - uv).ln() * 1.781 - 0.933
428 }
429 }
430 pub fn consolidation_time(&self, uv: f64) -> f64 {
432 let tv = self.time_factor(uv);
433 let hdr = self.h / 2.0;
434 tv * hdr.powi(2) / self.cv
435 }
436 pub fn ocr(&self) -> f64 {
438 self.sigma_p / self.sigma_v0
439 }
440}
441#[derive(Debug, Clone)]
443pub struct AggregateGrading {
444 pub sieve_sizes: Vec<f64>,
446 pub percent_passing: Vec<f64>,
448}
449impl AggregateGrading {
450 pub fn new(sieve_sizes: Vec<f64>, percent_passing: Vec<f64>) -> Self {
452 AggregateGrading {
453 sieve_sizes,
454 percent_passing,
455 }
456 }
457 pub fn astm_c33_coarse() -> Self {
459 AggregateGrading {
460 sieve_sizes: vec![25.0, 19.0, 12.5, 9.5, 4.75, 2.36],
461 percent_passing: vec![100.0, 90.0, 55.0, 35.0, 5.0, 0.0],
462 }
463 }
464 pub fn astm_c33_fine() -> Self {
466 AggregateGrading {
467 sieve_sizes: vec![9.5, 4.75, 2.36, 1.18, 0.60, 0.30, 0.15],
468 percent_passing: vec![100.0, 95.0, 80.0, 65.0, 45.0, 20.0, 5.0],
469 }
470 }
471 pub fn fineness_modulus(&self) -> f64 {
473 let sum_retained: f64 = self.percent_passing.iter().map(|p| 100.0 - p).sum();
474 sum_retained / 100.0
475 }
476 pub fn maximum_size(&self) -> f64 {
478 for (i, &p) in self.percent_passing.iter().enumerate() {
479 if p >= 100.0 {
480 return self.sieve_sizes[i];
481 }
482 }
483 *self.sieve_sizes.last().unwrap_or(&0.0)
484 }
485 pub fn nominal_max_size(&self) -> f64 {
487 for (i, &p) in self.percent_passing.iter().enumerate() {
488 if p < 100.0 {
489 if i > 0 {
490 return self.sieve_sizes[i - 1];
491 }
492 return self.sieve_sizes[0];
493 }
494 }
495 *self.sieve_sizes.last().unwrap_or(&0.0)
496 }
497}
498pub struct TimberMaterial {
500 pub species_grade: String,
502 pub moe: f64,
504 pub mor: f64,
506 pub e_longitudinal: f64,
508 pub e_radial: f64,
510 pub e_tangential: f64,
512 pub density: f64,
514}
515impl TimberMaterial {
516 pub fn douglas_fir_ss() -> Self {
518 TimberMaterial {
519 species_grade: "Douglas Fir - Select Structural".to_string(),
520 moe: 12_400.0,
521 mor: 62.0,
522 e_longitudinal: 12_400.0,
523 e_radial: 930.0,
524 e_tangential: 620.0,
525 density: 500.0,
526 }
527 }
528 pub fn adjusted_fb(&self, cd: f64, cm: f64) -> f64 {
530 self.mor * cd * cm
531 }
532 pub fn adjusted_e(&self, cm: f64) -> f64 {
534 self.moe * cm
535 }
536 pub fn shear_modulus_lr(&self) -> f64 {
538 self.e_longitudinal / 16.0
539 }
540}
541#[derive(Debug, Clone)]
543pub struct LaminatedVeneerLumber {
544 pub b: f64,
546 pub d: f64,
548 pub fb: f64,
550 pub fc: f64,
552 pub ft: f64,
554 pub fv: f64,
556 pub e: f64,
558}
559impl LaminatedVeneerLumber {
560 pub fn microllam_1_9e(b: f64, d: f64) -> Self {
562 LaminatedVeneerLumber {
563 b,
564 d,
565 fb: 19.3,
566 fc: 19.3,
567 ft: 11.7,
568 fv: 2.07,
569 e: 13_100.0,
570 }
571 }
572 pub fn area(&self) -> f64 {
574 self.b * self.d
575 }
576 pub fn ix(&self) -> f64 {
578 self.b * self.d.powi(3) / 12.0
579 }
580 pub fn sx(&self) -> f64 {
582 self.b * self.d.powi(2) / 6.0
583 }
584 pub fn allowable_moment(&self) -> f64 {
586 self.fb * self.sx()
587 }
588 pub fn allowable_compression(&self) -> f64 {
590 self.fc * self.area()
591 }
592 pub fn euler_buckling_load(&self, le: f64) -> f64 {
594 PI.powi(2) * self.e * self.ix() / le.powi(2)
595 }
596 pub fn d_to_b_ratio(&self) -> f64 {
598 self.d / self.b
599 }
600}
601#[derive(Debug, Clone)]
603pub struct GeosyntheticReinforcement {
604 pub geosyn_type: String,
606 pub ta: f64,
608 pub sv: f64,
610 pub phi_fill: f64,
612 pub gamma_fill: f64,
614 pub rc: f64,
616}
617impl GeosyntheticReinforcement {
618 pub fn new(
620 geosyn_type: &str,
621 ta: f64,
622 sv: f64,
623 phi_fill: f64,
624 gamma_fill: f64,
625 rc: f64,
626 ) -> Self {
627 GeosyntheticReinforcement {
628 geosyn_type: geosyn_type.to_string(),
629 ta,
630 sv,
631 phi_fill,
632 gamma_fill,
633 rc,
634 }
635 }
636 pub fn horizontal_stress(&self, z: f64) -> f64 {
638 let ka = ((PI / 4.0 - self.phi_fill.to_radians() / 2.0).tan()).powi(2);
639 ka * self.gamma_fill * z
640 }
641 pub fn required_strength(&self, z: f64) -> f64 {
643 self.horizontal_stress(z) * self.sv
644 }
645 pub fn tension_fos(&self, z: f64) -> f64 {
647 let t_req = self.required_strength(z);
648 if t_req < 1e-10 {
649 return f64::INFINITY;
650 }
651 self.ta / t_req
652 }
653 pub fn pullout_length(&self, z: f64, fos: f64) -> f64 {
655 let t_max = self.required_strength(z);
656 let sigma_v = self.gamma_fill * z;
657 let f_star = 0.67 * self.phi_fill.to_radians().tan();
658 let denom = 2.0 * self.rc * f_star * sigma_v;
659 if denom < 1e-10 {
660 return 1.0;
661 }
662 (t_max * fos / denom).max(1.0)
663 }
664}
665pub struct SteelRebarBond {
667 pub db: f64,
669 pub fy: f64,
671 pub fc: f64,
673 pub cover: f64,
675}
676impl SteelRebarBond {
677 pub fn new(db: f64, fy: f64, fc: f64, cover: f64) -> Self {
679 SteelRebarBond { db, fy, fc, cover }
680 }
681 pub fn development_length(&self) -> f64 {
683 let psi_t = 1.0;
684 let psi_e = 1.0;
685 let lambda = 1.0;
686 (self.fy * psi_t * psi_e) / (1.1 * lambda * self.fc.sqrt()) * self.db
687 }
688 pub fn hook_development_length(&self) -> f64 {
690 let ldh_basic = 0.24 * self.fy * self.db / (self.fc.sqrt());
691 ldh_basic.max(8.0 * self.db).max(150.0)
692 }
693 pub fn average_bond_stress(&self) -> f64 {
695 let ld = self.development_length();
696 self.fy / (PI * ld / self.db)
697 }
698}
699#[derive(Debug, Clone)]
701pub struct Admixture {
702 pub admixture_type: AdmixtureType,
704 pub dosage: f64,
706 pub water_reduction_pct: f64,
708 pub setting_time_delta: f64,
710}
711impl Admixture {
712 pub fn new(
714 admixture_type: AdmixtureType,
715 dosage: f64,
716 water_reduction_pct: f64,
717 setting_time_delta: f64,
718 ) -> Self {
719 Admixture {
720 admixture_type,
721 dosage,
722 water_reduction_pct,
723 setting_time_delta,
724 }
725 }
726 pub fn adjusted_water(&self, base_water: f64) -> f64 {
728 base_water * (1.0 - self.water_reduction_pct / 100.0)
729 }
730 pub fn adjusted_wc_ratio(&self, base_wc: f64) -> f64 {
732 base_wc * (1.0 - self.water_reduction_pct / 100.0)
733 }
734 pub fn is_accelerator(&self) -> bool {
736 self.admixture_type == AdmixtureType::Accelerator
737 }
738}
739#[derive(Debug, Clone)]
741pub struct CrossLaminatedTimber {
742 pub width: f64,
744 pub thickness: f64,
746 pub n_layers: u32,
748 pub layer_thickness: f64,
750 pub e0: f64,
752 pub e90: f64,
754 pub fb: f64,
756 pub g_rolling: f64,
758}
759impl CrossLaminatedTimber {
760 pub fn five_layer(width: f64, total_thickness: f64) -> Self {
762 let n_layers = 5u32;
763 let layer_t = total_thickness / n_layers as f64;
764 CrossLaminatedTimber {
765 width,
766 thickness: total_thickness,
767 n_layers,
768 layer_thickness: layer_t,
769 e0: 11_000.0,
770 e90: 370.0,
771 fb: 24.0,
772 g_rolling: 65.0,
773 }
774 }
775 pub fn effective_bending_stiffness(&self) -> f64 {
777 let n_par = self.n_layers.div_ceil(2);
778 let h = self.layer_thickness;
779 let mut ei = 0.0;
780 for i in 0..n_par {
781 let yi = (self.thickness / 2.0) - h / 2.0 - i as f64 * 2.0 * h;
782 ei += self.e0 * self.width * h.powi(3) / 12.0 + self.e0 * self.width * h * yi.powi(2);
783 }
784 ei
785 }
786 pub fn effective_axial_stiffness(&self) -> f64 {
788 let n_par = self.n_layers.div_ceil(2);
789 self.e0 * self.width * self.layer_thickness * n_par as f64
790 }
791 pub fn rolling_shear_capacity(&self) -> f64 {
793 self.g_rolling * self.layer_thickness / self.width
794 }
795 pub fn area_per_m(&self) -> f64 {
797 self.thickness * 1000.0
798 }
799}
800#[derive(Debug, Clone)]
802pub struct MasonryShearWall {
803 pub length: f64,
805 pub thickness: f64,
807 pub height: f64,
809 pub fm: f64,
811 pub av: f64,
813 pub fy: f64,
815 pub sigma_v: f64,
817}
818impl MasonryShearWall {
819 pub fn new(
821 length: f64,
822 thickness: f64,
823 height: f64,
824 fm: f64,
825 av: f64,
826 fy: f64,
827 sigma_v: f64,
828 ) -> Self {
829 MasonryShearWall {
830 length,
831 thickness,
832 height,
833 fm,
834 av,
835 fy,
836 sigma_v,
837 }
838 }
839 pub fn net_area(&self) -> f64 {
841 self.length * self.thickness
842 }
843 pub fn in_plane_shear_capacity(&self) -> f64 {
845 let m_v_d = (self.height / self.length).min(1.0);
846 let an = self.net_area();
847 let p = self.sigma_v * an;
848 let vnm = (4.0 - 1.75 * m_v_d) * an * self.fm.sqrt() + 0.25 * p;
849 let vns = 0.5 * self.av * self.fy * self.length / 1000.0;
850 (vnm + vns).min(6.0 * an * self.fm.sqrt())
851 }
852 pub fn out_of_plane_moment_capacity(&self) -> f64 {
854 let d = self.thickness / 2.0;
855 self.av * self.fy * d / 1000.0
856 }
857 pub fn aspect_ratio(&self) -> f64 {
859 self.height / self.length
860 }
861}
862#[derive(Debug, Clone)]
864pub struct PileFoundation {
865 pub diameter: f64,
867 pub length: f64,
869 pub pile_type: String,
871 pub qs: f64,
873 pub qb: f64,
875 pub cu: f64,
877 pub alpha: f64,
879}
880impl PileFoundation {
881 pub fn concrete_pile(diameter: f64, length: f64, qs: f64, qb: f64) -> Self {
883 PileFoundation {
884 diameter,
885 length,
886 pile_type: "concrete".to_string(),
887 qs,
888 qb,
889 cu: qs,
890 alpha: 0.5,
891 }
892 }
893 pub fn perimeter(&self) -> f64 {
895 PI * self.diameter
896 }
897 pub fn tip_area(&self) -> f64 {
899 PI * (self.diameter / 2.0).powi(2)
900 }
901 pub fn skin_friction(&self) -> f64 {
903 self.qs * self.perimeter() * self.length
904 }
905 pub fn end_bearing(&self) -> f64 {
907 self.qb * self.tip_area()
908 }
909 pub fn ultimate_capacity(&self) -> f64 {
911 self.skin_friction() + self.end_bearing()
912 }
913 pub fn allowable_capacity(&self) -> f64 {
915 self.ultimate_capacity() / 2.5
916 }
917 pub fn alpha_skin_friction(&self) -> f64 {
919 self.alpha * self.cu * self.perimeter() * self.length
920 }
921 pub fn elastic_compression(&self, load_kn: f64) -> f64 {
923 let e_pile = if self.pile_type == "concrete" {
924 25_000.0
925 } else {
926 200_000.0
927 };
928 let area_mm2 = self.tip_area() * 1e6;
929 let load_n = load_kn * 1000.0;
930 let length_mm = self.length * 1000.0;
931 load_n * length_mm / (e_pile * area_mm2)
932 }
933}
934pub struct ConcreteMaterial {
936 pub fc: f64,
938 pub ft: f64,
940 pub ec: f64,
942 pub poisson: f64,
944 pub shrinkage_strain: f64,
946 pub creep_coefficient: f64,
948 pub density: f64,
950}
951impl ConcreteMaterial {
952 pub fn new(fc: f64) -> Self {
956 let ec = 4700.0 * fc.sqrt();
957 let ft = 0.1 * fc;
958 ConcreteMaterial {
959 fc,
960 ft,
961 ec,
962 poisson: 0.2,
963 shrinkage_strain: 3e-4,
964 creep_coefficient: 2.0,
965 density: 2400.0,
966 }
967 }
968 pub fn shear_modulus(&self) -> f64 {
970 self.ec / (2.0 * (1.0 + self.poisson))
971 }
972 pub fn splitting_tensile_strength(&self) -> f64 {
974 0.56 * self.fc.sqrt()
975 }
976 pub fn ultimate_compressive_strain(&self) -> f64 {
978 0.003
979 }
980 pub fn modulus_of_rupture(&self) -> f64 {
982 0.62 * self.fc.sqrt()
983 }
984}
985pub struct ReinforcedConcrete {
987 pub concrete: ConcreteMaterial,
989 pub b: f64,
991 pub d: f64,
993 pub as_t: f64,
995 pub fy: f64,
997 pub as_comp: f64,
999 pub d_prime: f64,
1001}
1002impl ReinforcedConcrete {
1003 #[allow(clippy::too_many_arguments)]
1005 pub fn new(
1006 concrete: ConcreteMaterial,
1007 b: f64,
1008 d: f64,
1009 as_t: f64,
1010 fy: f64,
1011 as_comp: f64,
1012 d_prime: f64,
1013 ) -> Self {
1014 ReinforcedConcrete {
1015 concrete,
1016 b,
1017 d,
1018 as_t,
1019 fy,
1020 as_comp,
1021 d_prime,
1022 }
1023 }
1024 pub fn moment_capacity(&self) -> f64 {
1026 let fc = self.concrete.fc;
1027 let beta1 = if fc <= 28.0 {
1028 0.85
1029 } else {
1030 (0.85 - 0.05 * (fc - 28.0) / 7.0).max(0.65)
1031 };
1032 let a = self.as_t * self.fy / (0.85 * fc * self.b);
1033 let c = a / beta1;
1034 let eps_cu = 0.003;
1035 let eps_prime = eps_cu * (c - self.d_prime) / c;
1036 let fs_prime = if eps_prime >= self.fy / 200_000.0 {
1037 self.fy
1038 } else {
1039 eps_prime * 200_000.0
1040 };
1041
1042 self.as_t * self.fy * (self.d - a / 2.0) + self.as_comp * fs_prime * (self.d - self.d_prime)
1043 }
1044 pub fn design_moment_capacity(&self) -> f64 {
1046 0.9 * self.moment_capacity()
1047 }
1048 pub fn shear_capacity(&self) -> f64 {
1051 0.17 * self.concrete.fc.sqrt() * self.b * self.d
1052 }
1053 pub fn reinforcement_ratio(&self) -> f64 {
1055 self.as_t / (self.b * self.d)
1056 }
1057 pub fn rho_min(&self) -> f64 {
1059 let term1 = 0.25 * self.concrete.fc.sqrt() / self.fy;
1060 let term2 = 1.4 / self.fy;
1061 term1.max(term2)
1062 }
1063}
1064#[derive(Debug, Clone)]
1066pub struct HssSection {
1067 pub b: f64,
1069 pub h: f64,
1071 pub t: f64,
1073 pub fy: f64,
1075 pub e: f64,
1077}
1078impl HssSection {
1079 pub fn rectangular(b: f64, h: f64, t: f64, fy: f64) -> Self {
1081 HssSection {
1082 b,
1083 h,
1084 t,
1085 fy,
1086 e: 200_000.0,
1087 }
1088 }
1089 pub fn square(b: f64, t: f64, fy: f64) -> Self {
1091 HssSection {
1092 b,
1093 h: b,
1094 t,
1095 fy,
1096 e: 200_000.0,
1097 }
1098 }
1099 pub fn area(&self) -> f64 {
1101 self.b * self.h - (self.b - 2.0 * self.t) * (self.h - 2.0 * self.t)
1102 }
1103 pub fn ix(&self) -> f64 {
1105 (self.b * self.h.powi(3) - (self.b - 2.0 * self.t) * (self.h - 2.0 * self.t).powi(3)) / 12.0
1106 }
1107 pub fn iy(&self) -> f64 {
1109 (self.h * self.b.powi(3) - (self.h - 2.0 * self.t) * (self.b - 2.0 * self.t).powi(3)) / 12.0
1110 }
1111 pub fn sx(&self) -> f64 {
1113 self.ix() / (self.h / 2.0)
1114 }
1115 pub fn zx(&self) -> f64 {
1117 let outer = self.b * self.h.powi(2) / 4.0;
1118 let inner = (self.b - 2.0 * self.t) * (self.h - 2.0 * self.t).powi(2) / 4.0;
1119 outer - inner
1120 }
1121 pub fn plastic_moment(&self) -> f64 {
1123 self.zx() * self.fy
1124 }
1125 pub fn torsional_constant(&self) -> f64 {
1127 let h_mid = self.h - self.t;
1128 let b_mid = self.b - self.t;
1129 2.0 * self.t * b_mid * h_mid * (b_mid * h_mid) / (b_mid + h_mid)
1130 }
1131 pub fn warping_constant(&self) -> f64 {
1133 0.0
1134 }
1135 pub fn web_slenderness(&self) -> f64 {
1137 (self.h - 2.0 * self.t) / self.t
1138 }
1139 pub fn flange_slenderness(&self) -> f64 {
1141 (self.b - 2.0 * self.t) / self.t
1142 }
1143 pub fn compact_flange_limit(&self) -> f64 {
1145 1.12 * (self.e / self.fy).sqrt()
1146 }
1147 pub fn flange_is_compact(&self) -> bool {
1149 self.flange_slenderness() <= self.compact_flange_limit()
1150 }
1151}
1152#[derive(Debug, Clone)]
1154pub struct EurocodeLoad {
1155 pub gk: f64,
1157 pub qk1: f64,
1159 pub qk2: f64,
1161 pub wk: f64,
1163 pub sk: f64,
1165 pub ed: f64,
1167}
1168impl EurocodeLoad {
1169 pub fn new(gk: f64, qk1: f64, qk2: f64, wk: f64, sk: f64, ed: f64) -> Self {
1171 EurocodeLoad {
1172 gk,
1173 qk1,
1174 qk2,
1175 wk,
1176 sk,
1177 ed,
1178 }
1179 }
1180 pub fn uls_combo_610(&self) -> f64 {
1183 1.35 * self.gk + 1.50 * self.qk1 + 1.50 * 0.7 * self.qk2
1184 }
1185 pub fn uls_combo_610a(&self) -> f64 {
1187 1.35 * self.gk + 1.50 * 0.7 * self.qk1
1188 }
1189 pub fn uls_combo_610b(&self) -> f64 {
1191 0.85 * 1.35 * self.gk + 1.50 * self.qk1
1192 }
1193 pub fn uls_seismic(&self) -> f64 {
1196 self.gk + self.ed + 0.3 * self.qk1
1197 }
1198 pub fn sls_characteristic(&self) -> f64 {
1200 self.gk + self.qk1 + 0.7 * self.qk2
1201 }
1202 pub fn sls_frequent(&self) -> f64 {
1204 self.gk + 0.5 * self.qk1 + 0.3 * self.qk2
1205 }
1206 pub fn sls_quasi_permanent(&self) -> f64 {
1208 self.gk + 0.3 * self.qk1
1209 }
1210 pub fn governing_uls(&self) -> f64 {
1212 [
1213 self.uls_combo_610(),
1214 self.uls_combo_610a(),
1215 self.uls_combo_610b(),
1216 ]
1217 .iter()
1218 .cloned()
1219 .fold(f64::NEG_INFINITY, f64::max)
1220 }
1221 pub fn uls_wind_uplift(&self) -> f64 {
1223 0.9 * self.gk + 1.50 * self.wk
1224 }
1225 pub fn uls_snow(&self) -> f64 {
1227 1.35 * self.gk + 1.50 * self.sk
1228 }
1229}
1230#[derive(Debug, Clone)]
1232pub struct ChannelSection {
1233 pub d: f64,
1235 pub bf: f64,
1237 pub tf: f64,
1239 pub tw: f64,
1241 pub fy: f64,
1243}
1244impl ChannelSection {
1245 pub fn new(d: f64, bf: f64, tf: f64, tw: f64, fy: f64) -> Self {
1247 ChannelSection { d, bf, tf, tw, fy }
1248 }
1249 pub fn area(&self) -> f64 {
1251 2.0 * self.bf * self.tf + (self.d - 2.0 * self.tf) * self.tw
1252 }
1253 pub fn shear_center_x(&self) -> f64 {
1255 let bf = self.bf;
1256 let tf = self.tf;
1257 let d = self.d;
1258 let tw = self.tw;
1259 let hw = d - 2.0 * tf;
1260 let ix = self.ix();
1261 let sx = if d > 0.0 { ix / (d / 2.0) } else { 1.0 };
1262 bf.powi(2) * tf / (2.0 * sx / d) / (1.0 + (hw * tw) / (6.0 * bf * tf))
1263 }
1264 pub fn ix(&self) -> f64 {
1266 let hw = self.d - 2.0 * self.tf;
1267 self.bf * self.d.powi(3) / 12.0 - (self.bf - self.tw) * hw.powi(3) / 12.0
1268 }
1269 pub fn sx(&self) -> f64 {
1271 self.ix() / (self.d / 2.0)
1272 }
1273 pub fn moment_capacity(&self) -> f64 {
1275 self.sx() * self.fy
1276 }
1277 pub fn shear_capacity(&self) -> f64 {
1279 let hw = self.d - 2.0 * self.tf;
1280 0.6 * self.fy * hw * self.tw
1281 }
1282}
1283#[derive(Debug, Clone, PartialEq)]
1285pub enum AdmixtureType {
1286 WaterReducer,
1288 Superplasticiser,
1290 Accelerator,
1292 Retarder,
1294 AirEntrainer,
1296 ShrinkageReducer,
1298 CorrosionInhibitor,
1300}
1301#[derive(Debug, Clone)]
1303pub struct MoistureCorrectionTimber {
1304 pub mc_service: f64,
1306 pub mc_fsp: f64,
1308 pub e_green: f64,
1310 pub fb_green: f64,
1312}
1313impl MoistureCorrectionTimber {
1314 pub fn douglas_fir() -> Self {
1316 MoistureCorrectionTimber {
1317 mc_service: 15.0,
1318 mc_fsp: 28.0,
1319 e_green: 11_000.0,
1320 fb_green: 40.0,
1321 }
1322 }
1323 pub fn cm_factor_e(&self) -> f64 {
1325 if self.mc_service >= self.mc_fsp {
1326 return 1.0;
1327 }
1328 if self.mc_service <= 19.0 {
1329 1.0
1330 } else {
1331 1.0 - 0.1 * (self.mc_service - 19.0) / (self.mc_fsp - 19.0)
1332 }
1333 }
1334 pub fn cm_factor_fb(&self) -> f64 {
1336 if self.mc_service >= self.mc_fsp {
1337 return 0.85;
1338 }
1339 if self.mc_service <= 19.0 {
1340 1.0
1341 } else {
1342 0.85 + 0.15 * (self.mc_fsp - self.mc_service) / (self.mc_fsp - 19.0)
1343 }
1344 }
1345 pub fn adjusted_e(&self) -> f64 {
1347 self.e_green * self.cm_factor_e()
1348 }
1349 pub fn adjusted_fb(&self) -> f64 {
1351 self.fb_green * self.cm_factor_fb()
1352 }
1353 pub fn tangential_shrinkage_per_pct_mc(&self) -> f64 {
1355 0.29
1356 }
1357 pub fn dimensional_change(&self, dimension_mm: f64) -> f64 {
1359 let delta_mc = (self.mc_fsp - self.mc_service).max(0.0);
1360 dimension_mm * self.tangential_shrinkage_per_pct_mc() * delta_mc / 100.0
1361 }
1362}
1363#[derive(Debug, Clone)]
1365pub struct GeotextileFilter {
1366 pub aos: f64,
1368 pub permittivity: f64,
1370 pub transmissivity: f64,
1372 pub tensile_strength: f64,
1374 pub elongation: f64,
1376 pub soil_d85: f64,
1378 pub soil_d15: f64,
1380 pub kp: f64,
1382 pub kn: f64,
1384}
1385impl GeotextileFilter {
1386 pub fn woven_road_drainage() -> Self {
1388 GeotextileFilter {
1389 aos: 0.212,
1390 permittivity: 0.5,
1391 transmissivity: 1e-4,
1392 tensile_strength: 40.0,
1393 elongation: 15.0,
1394 soil_d85: 0.3,
1395 soil_d15: 0.05,
1396 kp: 1e-3,
1397 kn: 1e-4,
1398 }
1399 }
1400 pub fn retention_criterion_met(&self, b: f64) -> bool {
1403 self.aos <= b * self.soil_d85
1404 }
1405 pub fn permeability_criterion_met(&self, k_soil: f64) -> bool {
1407 self.kn >= k_soil
1408 }
1409 pub fn gradient_ratio(&self) -> f64 {
1411 if self.soil_d15 < 1e-10 {
1412 return f64::INFINITY;
1413 }
1414 self.aos / self.soil_d15
1415 }
1416 pub fn filter_ratio(&self) -> f64 {
1418 if self.soil_d85 < 1e-10 {
1419 return f64::INFINITY;
1420 }
1421 self.aos / self.soil_d85
1422 }
1423 pub fn overlap_width(&self, trench_depth_m: f64) -> f64 {
1425 (0.3_f64).max(trench_depth_m * 0.5)
1426 }
1427}
1428#[derive(Debug, Clone)]
1430pub struct Geogrid {
1431 pub tult_md: f64,
1433 pub tult_cmd: f64,
1435 pub junction_efficiency: f64,
1437 pub aperture_size: f64,
1439 pub ltds: f64,
1441 pub coverage_ratio: f64,
1443}
1444impl Geogrid {
1445 pub fn bx1100() -> Self {
1447 Geogrid {
1448 tult_md: 15.0,
1449 tult_cmd: 20.0,
1450 junction_efficiency: 93.0,
1451 aperture_size: 33.0,
1452 ltds: 8.0,
1453 coverage_ratio: 0.35,
1454 }
1455 }
1456 pub fn ux1500hs() -> Self {
1458 Geogrid {
1459 tult_md: 150.0,
1460 tult_cmd: 25.0,
1461 junction_efficiency: 91.0,
1462 aperture_size: 16.0,
1463 ltds: 80.0,
1464 coverage_ratio: 0.70,
1465 }
1466 }
1467 pub fn allowable_strength(&self, rf_id: f64, rf_cr: f64) -> f64 {
1470 self.ltds / (rf_id * rf_cr)
1471 }
1472 pub fn interaction_coefficient(&self, phi_soil: f64) -> f64 {
1475 let base_ci = 0.8;
1476 let phi = phi_soil.to_radians();
1477 base_ci * phi.tan() / phi.tan()
1478 }
1479 pub fn passive_resistance(&self, sigma_v: f64) -> f64 {
1481 self.ltds * sigma_v / 100.0
1482 }
1483}
1484#[derive(Debug, Clone)]
1486pub struct StructuralLoad {
1487 pub dead: f64,
1489 pub live: f64,
1491 pub wind: f64,
1493 pub seismic: f64,
1495 pub snow: f64,
1497}
1498impl StructuralLoad {
1499 pub fn new(dead: f64, live: f64, wind: f64, seismic: f64, snow: f64) -> Self {
1501 StructuralLoad {
1502 dead,
1503 live,
1504 wind,
1505 seismic,
1506 snow,
1507 }
1508 }
1509 pub fn lrfd_combo1(&self) -> f64 {
1511 1.4 * self.dead
1512 }
1513 pub fn lrfd_combo2(&self) -> f64 {
1515 1.2 * self.dead + 1.6 * self.live + 0.5 * self.snow
1516 }
1517 pub fn lrfd_combo3(&self) -> f64 {
1519 1.2 * self.dead + 1.0 * self.wind + 1.0 * self.live + 0.5 * self.snow
1520 }
1521 pub fn lrfd_combo4(&self) -> f64 {
1523 0.9 * self.dead + 1.0 * self.wind
1524 }
1525 pub fn lrfd_seismic(&self) -> f64 {
1527 1.2 * self.dead + 1.0 * self.seismic + 1.0 * self.live + 0.2 * self.snow
1528 }
1529 pub fn asd_combo_dl(&self) -> f64 {
1531 self.dead + self.live
1532 }
1533 pub fn governing_lrfd(&self) -> f64 {
1535 [
1536 self.lrfd_combo1(),
1537 self.lrfd_combo2(),
1538 self.lrfd_combo3(),
1539 self.lrfd_combo4(),
1540 self.lrfd_seismic(),
1541 ]
1542 .iter()
1543 .cloned()
1544 .fold(f64::NEG_INFINITY, f64::max)
1545 }
1546}
1547pub struct AsphaltMixture {
1549 pub bitumen_content: f64,
1551 pub vma: f64,
1553 pub vfa: f64,
1555 pub air_voids: f64,
1557 pub dynamic_stability: f64,
1559 pub stability: f64,
1561 pub flow: f64,
1563}
1564impl AsphaltMixture {
1565 pub fn superpave_12_5() -> Self {
1567 AsphaltMixture {
1568 bitumen_content: 5.0,
1569 vma: 14.0,
1570 vfa: 72.0,
1571 air_voids: 4.0,
1572 dynamic_stability: 1000.0,
1573 stability: 8000.0,
1574 flow: 3.0,
1575 }
1576 }
1577 pub fn bulk_density(&self, gmm: f64) -> f64 {
1580 (100.0 - self.air_voids) / 100.0 * gmm
1581 }
1582 pub fn meets_air_voids_criterion(&self) -> bool {
1584 (self.air_voids - 4.0).abs() < 0.5
1585 }
1586}
1587#[derive(Debug, Clone)]
1589pub struct SteelSection {
1590 pub area: f64,
1592 pub ix: f64,
1594 pub iy: f64,
1596 pub zx: f64,
1598 pub zy: f64,
1600 pub fy: f64,
1602 pub e: f64,
1604}
1605impl SteelSection {
1606 pub fn i_section(bf: f64, tf: f64, d: f64, tw: f64, fy: f64) -> Self {
1611 let hw = d - 2.0 * tf;
1612 let area = 2.0 * bf * tf + hw * tw;
1613 let ix = bf * d.powi(3) / 12.0 - (bf - tw) * hw.powi(3) / 12.0;
1614 let iy = 2.0 * tf * bf.powi(3) / 12.0 + hw * tw.powi(3) / 12.0;
1615 let zx = bf * tf * (d / 2.0 - tf / 2.0) * 2.0 + tw * hw.powi(2) / 4.0;
1616 let zy = bf.powi(2) * tf / 2.0 + tw.powi(2) * hw / 4.0;
1617 SteelSection {
1618 area,
1619 ix,
1620 iy,
1621 zx,
1622 zy,
1623 fy,
1624 e: 200_000.0,
1625 }
1626 }
1627 pub fn sx(&self, d: f64) -> f64 {
1629 self.ix / (d / 2.0)
1630 }
1631 pub fn plastic_moment(&self) -> f64 {
1633 self.zx * self.fy
1634 }
1635 pub fn axial_capacity(&self) -> f64 {
1637 self.area * self.fy
1638 }
1639 pub fn rx(&self) -> f64 {
1641 (self.ix / self.area).sqrt()
1642 }
1643 pub fn ry(&self) -> f64 {
1645 (self.iy / self.area).sqrt()
1646 }
1647 pub fn elastic_buckling_stress(&self, kl_over_r: f64) -> f64 {
1649 PI.powi(2) * self.e / kl_over_r.powi(2)
1650 }
1651}
1652pub struct FoundationSoil {
1654 pub cu: f64,
1656 pub phi_deg: f64,
1658 pub c_prime: f64,
1660 pub gamma: f64,
1662 pub spt_n: u32,
1664 pub constrained_modulus: f64,
1666}
1667impl FoundationSoil {
1668 pub fn medium_clay() -> Self {
1670 FoundationSoil {
1671 cu: 50.0,
1672 phi_deg: 0.0,
1673 c_prime: 50.0,
1674 gamma: 18.0,
1675 spt_n: 10,
1676 constrained_modulus: 5.0,
1677 }
1678 }
1679 pub fn ultimate_bearing_capacity(&self, b: f64, df: f64) -> f64 {
1683 let phi = self.phi_deg.to_radians();
1684 let (nc, nq, ng) = terzaghi_bearing_factors(phi);
1685 self.c_prime * nc + self.gamma * df * nq + 0.5 * self.gamma * b * ng
1686 }
1687 pub fn allowable_bearing_capacity(&self, b: f64, df: f64) -> f64 {
1689 self.ultimate_bearing_capacity(b, df) / 3.0
1690 }
1691 pub fn immediate_settlement(&self, q: f64, b: f64, es_mpa: f64, nu: f64) -> f64 {
1696 let is = 0.82;
1697 q * b * (1.0 - nu * nu) * is / (es_mpa * 1000.0)
1698 }
1699}
1700#[derive(Debug, Clone)]
1702pub struct PrestressedConcrete {
1703 pub ag: f64,
1705 pub ig: f64,
1707 pub yb: f64,
1709 pub yt: f64,
1711 pub aps: f64,
1713 pub fpu: f64,
1715 pub fpi: f64,
1717 pub eccentricity: f64,
1719 pub fc: f64,
1721 pub span: f64,
1723}
1724impl PrestressedConcrete {
1725 #[allow(clippy::too_many_arguments)]
1727 pub fn new(
1728 ag: f64,
1729 ig: f64,
1730 yb: f64,
1731 yt: f64,
1732 aps: f64,
1733 fpu: f64,
1734 fpi: f64,
1735 eccentricity: f64,
1736 fc: f64,
1737 span: f64,
1738 ) -> Self {
1739 PrestressedConcrete {
1740 ag,
1741 ig,
1742 yb,
1743 yt,
1744 aps,
1745 fpu,
1746 fpi,
1747 eccentricity,
1748 fc,
1749 span,
1750 }
1751 }
1752 pub fn initial_prestress_force(&self) -> f64 {
1754 self.aps * self.fpi
1755 }
1756 pub fn effective_prestress_force(&self, loss_fraction: f64) -> f64 {
1758 self.initial_prestress_force() * (1.0 - loss_fraction)
1759 }
1760 pub fn upper_kern(&self) -> f64 {
1762 self.ig / (self.ag * self.yb)
1763 }
1764 pub fn lower_kern(&self) -> f64 {
1766 self.ig / (self.ag * self.yt)
1767 }
1768 pub fn bottom_fiber_stress(&self, pe: f64, m: f64) -> f64 {
1771 let p_term = -pe / self.ag;
1772 let e_term = -pe * self.eccentricity * self.yb / self.ig;
1773 let m_term = m * self.yb / self.ig;
1774 p_term + e_term + m_term
1775 }
1776 pub fn top_fiber_stress(&self, pe: f64, m: f64) -> f64 {
1778 let p_term = -pe / self.ag;
1779 let e_term = pe * self.eccentricity * self.yt / self.ig;
1780 let m_term = -m * self.yt / self.ig;
1781 p_term + e_term + m_term
1782 }
1783 pub fn cracking_moment(&self, pe: f64) -> f64 {
1785 let fr = 0.62 * self.fc.sqrt();
1786 let sb = self.ig / self.yb;
1787 (pe / self.ag + pe * self.eccentricity / sb + fr) * sb
1788 }
1789 pub fn elastic_shortening_loss(&self) -> f64 {
1791 let es_steel = 197_000.0;
1792 let ec = 4700.0 * self.fc.sqrt();
1793 let n = es_steel / ec;
1794 let pe = self.initial_prestress_force();
1795 let fc_cgs = pe / self.ag + pe * self.eccentricity.powi(2) / self.ig;
1796 n * fc_cgs
1797 }
1798 pub fn shrinkage_loss(&self) -> f64 {
1800 70.0
1801 }
1802 pub fn creep_loss(&self) -> f64 {
1804 let ccr = 2.0;
1805 let n = 197_000.0 / (4700.0 * self.fc.sqrt());
1806 let pe = self.effective_prestress_force(0.0);
1807 let fc_cgs = pe / self.ag + pe * self.eccentricity.powi(2) / self.ig;
1808 ccr * n * fc_cgs
1809 }
1810 pub fn total_losses(&self) -> f64 {
1812 self.elastic_shortening_loss() + self.shrinkage_loss() + self.creep_loss()
1813 }
1814 pub fn nominal_flexural_strength(&self) -> f64 {
1816 let rho_p = self.aps / (self.ag * 0.8);
1817 let fps = self.fpu * (1.0 - 0.5 * rho_p * self.fpu / self.fc);
1818 let dp = self.yb;
1819 let a = fps * self.aps / (0.85 * self.fc * (self.ag / self.yb));
1820 fps * self.aps * (dp - a / 2.0)
1821 }
1822}
1823#[derive(Debug, Clone, PartialEq)]
1825pub enum ExposureClass {
1826 Interior,
1828 Moderate,
1830 Severe,
1832 Submerged,
1834}
1835pub struct MasonryUnit {
1837 pub fm: f64,
1839 pub mortar_type: String,
1841 pub bond_pattern: String,
1843 pub em: f64,
1845 pub density: f64,
1847}
1848impl MasonryUnit {
1849 pub fn clay_brick(fm: f64) -> Self {
1851 let em = 700.0 * fm;
1852 MasonryUnit {
1853 fm,
1854 mortar_type: "S".to_string(),
1855 bond_pattern: "Running".to_string(),
1856 em,
1857 density: 1900.0,
1858 }
1859 }
1860 pub fn shear_modulus(&self) -> f64 {
1862 0.4 * self.em
1863 }
1864 pub fn modulus_of_rupture(&self) -> f64 {
1866 0.064 * self.fm
1867 }
1868}
1869#[derive(Debug, Clone)]
1871pub struct GlulamSection {
1872 pub b: f64,
1874 pub d: f64,
1876 pub n_lam: u32,
1878 pub lam_thickness: f64,
1880 pub fb: f64,
1882 pub fv: f64,
1884 pub e: f64,
1886 pub cm: f64,
1888}
1889impl GlulamSection {
1890 pub fn new(b: f64, d: f64, n_lam: u32, fb: f64, fv: f64, e: f64) -> Self {
1892 let lam_thickness = d / n_lam as f64;
1893 GlulamSection {
1894 b,
1895 d,
1896 n_lam,
1897 lam_thickness,
1898 fb,
1899 fv,
1900 e,
1901 cm: 1.0,
1902 }
1903 }
1904 pub fn df_24f_v4() -> Self {
1906 GlulamSection {
1907 b: 275.0,
1908 d: 570.0,
1909 n_lam: 19,
1910 lam_thickness: 30.0,
1911 fb: 16.5,
1912 fv: 2.4,
1913 e: 12_400.0,
1914 cm: 1.0,
1915 }
1916 }
1917 pub fn area(&self) -> f64 {
1919 self.b * self.d
1920 }
1921 pub fn ix(&self) -> f64 {
1923 self.b * self.d.powi(3) / 12.0
1924 }
1925 pub fn sx(&self) -> f64 {
1927 self.b * self.d.powi(2) / 6.0
1928 }
1929 pub fn volume_factor(&self, l_m: f64) -> f64 {
1932 let kl = 1.09;
1933 let b_ft = self.b / 25.4 / 12.0;
1934 let d_ft = self.d / 25.4 / 12.0;
1935 let l_ft = l_m * 3.28084;
1936 (kl * 21.0 / l_ft).powf(0.1)
1937 * (12.0 / (d_ft * 12.0)).powf(0.1)
1938 * (5.125 / (b_ft * 12.0)).powf(0.1).min(1.0)
1939 }
1940 pub fn adjusted_fb(&self, cd: f64, cv: f64) -> f64 {
1942 self.fb * cd * self.cm * cv
1943 }
1944 pub fn allowable_moment(&self, cd: f64, cv: f64) -> f64 {
1946 self.adjusted_fb(cd, cv) * self.sx()
1947 }
1948 pub fn allowable_shear(&self) -> f64 {
1950 let fv_prime = self.fv * self.cm;
1951 fv_prime * (2.0 / 3.0) * self.b * self.d
1952 }
1953 pub fn midspan_deflection(&self, w_n_per_mm: f64, span_mm: f64) -> f64 {
1955 5.0 * w_n_per_mm * span_mm.powi(4) / (384.0 * self.e * self.ix())
1956 }
1957}