1use super::*;
2
3pub struct StructuralAnalyzer {
5 pub(super) finite_element_solver: FiniteElementSolver,
6 structural_dynamics: StructuralDynamics,
7 buckling_analysis: BucklingAnalysis,
8 vibration_analysis: VibrationAnalysis,
9 model_store: HashMap<String, EngineeringModel>,
10 linear_algebra: Option<Arc<Mutex<LinearAlgebraLibrary>>>,
12}
13
14pub struct FiniteElementSolver {
16 mesh_generator: MeshGenerator,
17 element_library: ElementLibrary,
18 solver_engine: SolverEngine,
19 post_processor: PostProcessor,
20 zns_manager: Option<Arc<Mutex<ZnsZoneManager>>>,
22}
23
24pub struct MeshGenerator {
26 mesh_types: HashMap<String, MeshType>,
27 mesh_algorithms: HashMap<String, MeshAlgorithm>,
28 mesh_quality: MeshQuality,
29}
30
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
33pub enum MeshType {
34 Triangular,
36 Quadrilateral,
38 Tetrahedral,
40 Hexahedral,
42 Mixed,
44 Structured,
46 Unstructured,
48}
49
50#[derive(Debug, Clone)]
52pub struct MeshAlgorithm {
53 pub algorithm_id: String,
54 pub algorithm_name: String,
55 pub algorithm_type: MeshAlgorithmType,
56 pub parameters: MeshAlgorithmParameters,
57}
58
59#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
61pub enum MeshAlgorithmType {
62 Delaunay,
63 AdvancingFront,
64 Octree,
65 Cartesian,
66 Custom(String),
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct MeshAlgorithmParameters {
72 pub element_size: f64,
73 pub refinement_level: u32,
74 pub quality_criteria: Vec<QualityCriterion>,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct QualityCriterion {
80 pub criterion_name: String,
81 pub minimum_value: f64,
82 pub maximum_value: f64,
83}
84
85pub struct MeshQuality {
87 pub quality_metrics: HashMap<String, QualityMetric>,
88 pub quality_assessment: QualityAssessment,
89}
90
91#[derive(Debug, Clone)]
93pub struct QualityMetric {
94 pub metric_name: String,
95 pub metric_value: f64,
96 pub metric_type: MetricType,
97}
98
99#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
101pub enum MetricType {
102 AspectRatio,
103 Skewness,
104 Orthogonality,
105 Jacobian,
106}
107
108#[derive(Debug, Clone)]
110pub struct QualityAssessment {
111 pub overall_quality: f64,
112 pub quality_grade: QualityGrade,
113 pub recommendations: Vec<String>,
114}
115
116#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
118pub enum QualityGrade {
119 Excellent,
120 Good,
121 Fair,
122 Poor,
123}
124
125pub struct ElementLibrary {
127 elements: HashMap<String, Element>,
128 element_properties: HashMap<String, ElementProperties>,
129}
130
131#[derive(Debug, Clone)]
133pub struct Element {
134 pub element_id: String,
135 pub element_name: String,
136 pub element_type: ElementType,
137 pub nodes: Vec<Node>,
138 pub properties: ElementProperties,
139}
140
141#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
143pub enum ElementType {
144 Truss,
146 Beam,
147 Frame,
148 Shell,
150 Plate,
151 Membrane,
152 Solid,
154 Tetrahedron,
155 Hexahedron,
156 Mass,
158 Spring,
159 Damper,
160}
161
162#[derive(Debug, Clone)]
164pub struct Node {
165 pub node_id: String,
166 pub coordinates: Vec<f64>,
167 pub degrees_of_freedom: Vec<DOF>,
168 pub constraints: Vec<Constraint>,
169}
170
171#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
173pub enum DOF {
174 UX,
175 UY,
176 UZ,
177 ROTX,
178 ROTY,
179 ROTZ,
180 Temperature,
181 Pressure,
182}
183
184#[derive(Debug, Clone)]
186pub struct Constraint {
187 pub constraint_id: String,
188 pub constraint_type: ConstraintType,
189 pub constraint_value: f64,
190}
191
192#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
194pub enum ConstraintType {
195 Fixed,
196 Pinned,
197 Roller,
198 Displacement,
199 Rotation,
200 Temperature,
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct ElementProperties {
206 pub material_properties: MaterialProperties,
207 pub geometric_properties: GeometricProperties,
208 pub section_properties: SectionProperties,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct MaterialProperties {
214 pub youngs_modulus: f64,
215 pub poissons_ratio: f64,
216 pub density: f64,
217 pub thermal_expansion: f64,
218 pub thermal_conductivity: f64,
219 pub specific_heat: f64,
220 pub yield_strength: f64,
221 pub ultimate_strength: f64,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct GeometricProperties {
227 pub area: f64,
228 pub volume: f64,
229 pub perimeter: f64,
230 pub surface_area: f64,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct SectionProperties {
236 pub moment_of_inertia: Vec<f64>,
237 pub torsional_constant: f64,
238 pub section_modulus: Vec<f64>,
239 pub shear_center: Vec<f64>,
240}
241
242pub struct SolverEngine {
244 solvers: HashMap<String, Solver>,
245 solver_parameters: SolverParameters,
246 convergence_criteria: ConvergenceCriteria,
247}
248
249#[derive(Debug, Clone)]
251pub struct Solver {
252 pub solver_id: String,
253 pub solver_name: String,
254 pub solver_type: SolverType,
255 pub capabilities: SolverCapabilities,
256}
257
258#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
260pub enum SolverType {
261 Direct,
262 Iterative,
263 Eigenvalue,
264 Transient,
265 Nonlinear,
266}
267
268#[derive(Debug, Clone)]
270pub struct SolverCapabilities {
271 pub max_dof: u64,
272 pub supported_element_types: Vec<ElementType>,
273 pub analysis_types: Vec<AnalysisType>,
274}
275
276#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
278pub enum AnalysisType {
279 LinearStatic,
280 NonlinearStatic,
281 LinearDynamic,
282 NonlinearDynamic,
283 Thermal,
284 Buckling,
285 Vibration,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize)]
290pub struct SolverParameters {
291 pub tolerance: f64,
292 pub max_iterations: u32,
293 pub convergence_acceleration: ConvergenceAcceleration,
294}
295
296#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
298pub enum ConvergenceAcceleration {
299 None,
300 Jacobi,
301 GaussSeidel,
302 SOR,
303 Multigrid,
304}
305
306pub struct ConvergenceCriteria {
308 pub criteria_type: ConvergenceType,
309 pub tolerance: f64,
310 pub max_iterations: u32,
311}
312
313#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
315pub enum ConvergenceType {
316 Residual,
317 Energy,
318 Displacement,
319 Force,
320}
321
322pub struct PostProcessor {
324 result_extractors: HashMap<String, ResultExtractor>,
325 visualization_engine: VisualizationEngine,
326 report_generator: ReportGenerator,
327}
328
329#[derive(Debug, Clone)]
331pub struct ResultExtractor {
332 pub extractor_id: String,
333 pub extractor_name: String,
334 pub result_type: ResultType,
335 pub extraction_method: ExtractionMethod,
336}
337
338#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
340pub enum ResultType {
341 Displacement,
342 Stress,
343 Strain,
344 Force,
345 Reaction,
346 Energy,
347 Temperature,
348 HeatFlux,
349}
350
351#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
353pub enum ExtractionMethod {
354 Nodal,
355 Elemental,
356 Gaussian,
357 Custom(String),
358}
359
360#[derive(Debug, Clone)]
362pub struct VisualizationEngine {
363 visualization_types: HashMap<String, VisualizationType>,
364 rendering_engine: RenderingEngine,
365}
366
367#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
369pub enum VisualizationType {
370 Contour,
371 Vector,
372 Deformed,
373 Animation,
374 Custom(String),
375}
376
377#[derive(Debug, Clone)]
379pub struct RenderingEngine {
380 pub engine_type: RenderingEngineType,
381 pub rendering_options: RenderingOptions,
382}
383
384#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
386pub enum RenderingEngineType {
387 OpenGL,
388 Vulkan,
389 DirectX,
390 Software,
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize)]
395pub struct RenderingOptions {
396 pub color_map: String,
397 pub scale_factor: f64,
398 pub line_width: f64,
399 pub transparency: f64,
400}
401
402pub struct ReportGenerator {
404 report_templates: HashMap<String, ReportTemplate>,
405 export_formats: Vec<ExportFormat>,
406}
407
408#[derive(Debug, Clone)]
410pub struct ReportTemplate {
411 pub template_id: String,
412 pub template_name: String,
413 pub template_type: TemplateType,
414 pub sections: Vec<ReportSection>,
415}
416
417#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
419pub enum TemplateType {
420 Summary,
421 Detailed,
422 Technical,
423 Executive,
424}
425
426#[derive(Debug, Clone)]
428pub struct ReportSection {
429 pub section_id: String,
430 pub section_name: String,
431 pub section_content: SectionContent,
432}
433
434#[derive(Debug, Clone)]
436pub struct SectionContent {
437 pub content_type: ContentType,
438 pub data: Vec<u8>,
439 pub format: ContentFormat,
440}
441
442#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
444pub enum ContentType {
445 Text,
446 Table,
447 Chart,
448 Image,
449}
450
451#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
453pub enum ContentFormat {
454 Text,
455 HTML,
456 PDF,
457 CSV,
458}
459
460#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
462pub enum ExportFormat {
463 PDF,
464 HTML,
465 CSV,
466 JSON,
467 XML,
468}
469impl StructuralAnalyzer {
472 pub fn new() -> Self {
473 Self {
474 finite_element_solver: FiniteElementSolver::new(),
475 structural_dynamics: StructuralDynamics::new(),
476 buckling_analysis: BucklingAnalysis::new(),
477 vibration_analysis: VibrationAnalysis::new(),
478 model_store: HashMap::new(),
479 linear_algebra: None,
480 }
481 }
482
483 pub fn attach_linear_algebra(&mut self, lib: Option<Arc<Mutex<LinearAlgebraLibrary>>>) {
485 self.linear_algebra = lib;
486 }
487
488 pub fn store_model(&mut self, model: EngineeringModel) {
489 self.model_store.insert(model.model_id.clone(), model);
490 }
491
492 pub fn initialize(&mut self) -> Result<(), EngineeringError> {
493 self.finite_element_solver.initialize()?;
494 self.structural_dynamics.initialize()?;
495 Ok(())
496 }
497
498 pub fn validate_model(&self, model: &EngineeringModel) -> Result<(), EngineeringError> {
499 if model.geometry.dimensions.is_empty() {
500 return Err(EngineeringError::ValidationError(
501 "Model must have dimensions".to_string(),
502 ));
503 }
504 Ok(())
505 }
506
507 pub fn analyze(
508 &mut self,
509 model: &EngineeringModel,
510 analysis_type: AnalysisType,
511 ) -> Result<AnalysisResults, EngineeringError> {
512 let material = model.materials.values().next().ok_or_else(|| {
519 EngineeringError::InsufficientData(
520 "model has no material; cannot compute stress / factor of safety".to_string(),
521 )
522 })?;
523 let mp = &material.material_properties;
524 let e = mp.youngs_modulus;
525 let sy = mp.yield_strength;
526 let density = mp.density;
527
528 let dims = &model.geometry.dimensions;
529 if dims.len() < 2 || dims.iter().take(2).any(|&d| !(d > 0.0)) {
530 return Err(EngineeringError::InsufficientData(
531 "geometry needs at least two positive cross-section dimensions to form an area"
532 .to_string(),
533 ));
534 }
535 let area = dims[0] * dims[1]; let length = dims.get(2).copied().filter(|&l| l > 0.0).unwrap_or(dims[0]); if model.loads.is_empty() {
539 return Err(EngineeringError::InsufficientData(
540 "model has no loads; cannot compute stress".to_string(),
541 ));
542 }
543 let force: f64 = model.loads.iter().map(|l| l.load_magnitude).sum(); let stress = force / area; let strain = if e > 0.0 { stress / e } else { 0.0 };
547 let displacement = if e > 0.0 {
548 force * length / (area * e)
549 } else {
550 f64::INFINITY
551 };
552 let safety_factor = if stress.abs() > 0.0 && sy > 0.0 {
553 sy / stress.abs()
554 } else if stress.abs() == 0.0 {
555 f64::INFINITY } else {
557 0.0 };
559
560 match analysis_type {
561 AnalysisType::LinearStatic => Ok(AnalysisResults {
562 results_id: "structural_axial".to_string(),
563 analysis_type,
564 displacement_field: vec![displacement],
565 stress_field: vec![stress],
566 strain_field: vec![strain],
567 reaction_forces: vec![-force], safety_factor,
569 temperature_field: Vec::new(), heat_flux_field: Vec::new(),
571 }),
572 AnalysisType::Buckling => {
573 let b = dims[0];
584 let h = dims[1];
585 let i_weak = (b * h * h * h).min(h * b * b * b) / 12.0;
586 let k_factor = 1.0_f64;
587 let le = k_factor * length;
588 let p_cr = std::f64::consts::PI.powi(2) * e * i_weak / (le * le);
589 let load_factor = if force.abs() > 0.0 {
590 p_cr / force.abs()
591 } else {
592 f64::INFINITY
593 };
594 Ok(AnalysisResults {
595 results_id: "structural_buckling_euler".to_string(),
596 analysis_type,
597 displacement_field: vec![displacement],
598 stress_field: vec![stress],
599 strain_field: vec![strain],
600 reaction_forces: vec![-force],
601 safety_factor: load_factor,
602 temperature_field: Vec::new(),
603 heat_flux_field: Vec::new(),
604 })
605 }
606 AnalysisType::NonlinearStatic => {
607 let ea = e * area;
612 let bar = fem::GeoNonlinearBar { ea, length };
613 let u = bar.solve_static(force, 1e-10, 200)?;
614 let eps = bar.strain(u); let sigma = e * eps;
616 let sf = if sigma.abs() > 0.0 && sy > 0.0 {
617 sy / sigma.abs()
618 } else if sigma.abs() == 0.0 {
619 f64::INFINITY
620 } else {
621 0.0
622 };
623 Ok(AnalysisResults {
624 results_id: "structural_nonlinear_static_geom_bar".to_string(),
625 analysis_type,
626 displacement_field: vec![u],
627 stress_field: vec![sigma],
628 strain_field: vec![eps],
629 reaction_forces: vec![-bar.internal_force(u)],
630 safety_factor: sf,
631 temperature_field: Vec::new(),
632 heat_flux_field: Vec::new(),
633 })
634 }
635 AnalysisType::LinearDynamic => {
636 if !(density > 0.0) {
642 return Err(EngineeringError::InsufficientData(
643 "dynamic analysis needs a positive material density".to_string(),
644 ));
645 }
646 let k = e * area / length;
647 let m = density * area * length / 2.0;
648 let omega = (k / m).sqrt();
649 let period = 2.0 * std::f64::consts::PI / omega;
650 let dt = period / 200.0;
651 let nsteps = 260; let res = fem::newmark_linear(
653 &[m],
654 &[0.0],
655 &[k],
656 1,
657 |_t| vec![force],
658 &[0.0],
659 &[0.0],
660 dt,
661 nsteps,
662 0.25,
663 0.5,
664 )?;
665 let u_peak = res.peak_abs(0);
666 let sigma = e * (u_peak / length); let sf = if sigma.abs() > 0.0 && sy > 0.0 {
668 sy / sigma.abs()
669 } else if sigma.abs() == 0.0 {
670 f64::INFINITY
671 } else {
672 0.0
673 };
674 Ok(AnalysisResults {
675 results_id: "structural_linear_dynamic_newmark".to_string(),
676 analysis_type,
677 displacement_field: vec![u_peak],
678 stress_field: vec![sigma],
679 strain_field: vec![u_peak / length],
680 reaction_forces: vec![-k * u_peak], safety_factor: sf,
682 temperature_field: Vec::new(),
683 heat_flux_field: Vec::new(),
684 })
685 }
686 AnalysisType::NonlinearDynamic => {
687 if !(density > 0.0) {
691 return Err(EngineeringError::InsufficientData(
692 "dynamic analysis needs a positive material density".to_string(),
693 ));
694 }
695 let ea = e * area;
696 let bar = fem::GeoNonlinearBar { ea, length };
697 let k0 = ea / length; let m = density * area * length / 2.0;
699 let omega = (k0 / m).sqrt();
700 let period = 2.0 * std::f64::consts::PI / omega;
701 let dt = period / 200.0;
702 let nsteps = 260;
703 let res = fem::newmark_nonlinear(
704 &[m],
705 &[0.0],
706 1,
707 |u| vec![bar.internal_force(u[0])],
708 |u| vec![bar.tangent(u[0])],
709 |_t| vec![force],
710 &[0.0],
711 &[0.0],
712 dt,
713 nsteps,
714 0.25,
715 0.5,
716 1e-10,
717 100,
718 )?;
719 let u_peak = res.peak_abs(0);
720 let eps = bar.strain(u_peak);
721 let sigma = e * eps;
722 let sf = if sigma.abs() > 0.0 && sy > 0.0 {
723 sy / sigma.abs()
724 } else if sigma.abs() == 0.0 {
725 f64::INFINITY
726 } else {
727 0.0
728 };
729 Ok(AnalysisResults {
730 results_id: "structural_nonlinear_dynamic_newmark_newton".to_string(),
731 analysis_type,
732 displacement_field: vec![u_peak],
733 stress_field: vec![sigma],
734 strain_field: vec![eps],
735 reaction_forces: vec![-bar.internal_force(u_peak)],
736 safety_factor: sf,
737 temperature_field: Vec::new(),
738 heat_flux_field: Vec::new(),
739 })
740 }
741 AnalysisType::Vibration | AnalysisType::Thermal => {
748 Err(EngineeringError::NotImplemented(format!(
749 "structural {:?} is not available through the AnalysisResults facade; \
750 use VibrationAnalysis::analyze_free / ModalAnalysis::analyze_modal for \
751 modal & free-vibration results and ThermalAnalyzer for thermal response",
752 analysis_type
753 )))
754 }
755 }
756 }
757
758 pub fn fem_static(
763 &self,
764 model: &fem::FeModel,
765 ) -> Result<fem::FeStaticResult, EngineeringError> {
766 fem::solve_static(model)
767 }
768
769 pub fn list_analysis_types(&self) -> Vec<String> {
770 vec![
771 "LinearStatic".to_string(),
772 "NonlinearStatic".to_string(),
773 "LinearDynamic".to_string(),
774 "NonlinearDynamic".to_string(),
775 "Buckling".to_string(),
776 ]
777 }
778
779 pub fn get_model(&self, model_id: &str) -> Option<EngineeringModel> {
780 self.model_store.get(model_id).cloned()
781 }
782
783 pub fn get_performance_metrics(&self) -> EngineeringPerformanceMetrics {
784 EngineeringPerformanceMetrics::new()
785 }
786
787 pub fn buckling_analysis(&self) -> &BucklingAnalysis {
789 &self.buckling_analysis
790 }
791
792 pub fn buckling_analysis_mut(&mut self) -> &mut BucklingAnalysis {
794 &mut self.buckling_analysis
795 }
796
797 pub fn vibration_analysis(&self) -> &VibrationAnalysis {
799 &self.vibration_analysis
800 }
801
802 pub fn vibration_analysis_mut(&mut self) -> &mut VibrationAnalysis {
804 &mut self.vibration_analysis
805 }
806}
807
808impl FiniteElementSolver {
809 pub fn new() -> Self {
810 Self {
811 mesh_generator: MeshGenerator::new(),
812 element_library: ElementLibrary::new(),
813 solver_engine: SolverEngine::new(),
814 post_processor: PostProcessor::new(),
815 zns_manager: None,
816 }
817 }
818
819 pub fn attach_zns_manager(&mut self, manager: Option<Arc<Mutex<ZnsZoneManager>>>) {
821 self.zns_manager = manager;
822 }
823
824 pub fn initialize(&mut self) -> Result<(), EngineeringError> {
825 self.mesh_generator.initialize()?;
826 self.element_library.initialize()?;
827 self.solver_engine.initialize()?;
828 self.post_processor.initialize()?;
829 Ok(())
830 }
831}
832
833impl MeshGenerator {
834 pub fn new() -> Self {
835 Self {
836 mesh_types: HashMap::new(),
837 mesh_algorithms: HashMap::new(),
838 mesh_quality: MeshQuality::new(),
839 }
840 }
841
842 pub fn initialize(&mut self) -> Result<(), EngineeringError> {
849 self.mesh_types
850 .insert("triangular".to_string(), MeshType::Triangular);
851 self.mesh_types
852 .insert("quadrilateral".to_string(), MeshType::Quadrilateral);
853 self.mesh_types
854 .insert("tetrahedral".to_string(), MeshType::Tetrahedral);
855 self.mesh_types
856 .insert("hexahedral".to_string(), MeshType::Hexahedral);
857 self.mesh_types.insert("prism".to_string(), MeshType::Mixed);
858 self.mesh_types
859 .insert("pyramid".to_string(), MeshType::Mixed);
860 self.mesh_types.insert("mixed".to_string(), MeshType::Mixed);
861 self.mesh_types
862 .insert("structured".to_string(), MeshType::Structured);
863 self.mesh_types
864 .insert("unstructured".to_string(), MeshType::Unstructured);
865
866 let default_params = MeshAlgorithmParameters {
867 element_size: 1.0,
868 refinement_level: 1,
869 quality_criteria: Vec::new(),
870 };
871 self.mesh_algorithms.insert(
872 "delaunay".to_string(),
873 MeshAlgorithm {
874 algorithm_id: "algo_delaunay".to_string(),
875 algorithm_name: "Delaunay Triangulation".to_string(),
876 algorithm_type: MeshAlgorithmType::Delaunay,
877 parameters: default_params.clone(),
878 },
879 );
880 self.mesh_algorithms.insert(
881 "advancing_front".to_string(),
882 MeshAlgorithm {
883 algorithm_id: "algo_advancing_front".to_string(),
884 algorithm_name: "Advancing Front".to_string(),
885 algorithm_type: MeshAlgorithmType::AdvancingFront,
886 parameters: default_params.clone(),
887 },
888 );
889 self.mesh_algorithms.insert(
890 "octree".to_string(),
891 MeshAlgorithm {
892 algorithm_id: "algo_octree".to_string(),
893 algorithm_name: "Octree Decomposition".to_string(),
894 algorithm_type: MeshAlgorithmType::Octree,
895 parameters: default_params.clone(),
896 },
897 );
898 self.mesh_algorithms.insert(
899 "structured".to_string(),
900 MeshAlgorithm {
901 algorithm_id: "algo_structured".to_string(),
902 algorithm_name: "Structured Grid".to_string(),
903 algorithm_type: MeshAlgorithmType::Custom("Structured".to_string()),
904 parameters: default_params.clone(),
905 },
906 );
907 self.mesh_algorithms.insert(
908 "unstructured".to_string(),
909 MeshAlgorithm {
910 algorithm_id: "algo_unstructured".to_string(),
911 algorithm_name: "Unstructured Mesh".to_string(),
912 algorithm_type: MeshAlgorithmType::Custom("Unstructured".to_string()),
913 parameters: default_params,
914 },
915 );
916
917 Ok(())
918 }
919
920 pub fn get_mesh_type(&self, name: &str) -> Option<&MeshType> {
922 self.mesh_types.get(name)
923 }
924
925 pub fn get_algorithm(&self, name: &str) -> Option<&MeshAlgorithm> {
927 self.mesh_algorithms.get(name)
928 }
929
930 pub fn list_mesh_types(&self) -> Vec<String> {
932 let mut names: Vec<String> = self.mesh_types.keys().cloned().collect();
933 names.sort();
934 names
935 }
936
937 pub fn list_algorithms(&self) -> Vec<String> {
939 let mut names: Vec<String> = self.mesh_algorithms.keys().cloned().collect();
940 names.sort();
941 names
942 }
943
944 pub fn mesh_quality(&self) -> &MeshQuality {
946 &self.mesh_quality
947 }
948
949 pub fn mesh_quality_mut(&mut self) -> &mut MeshQuality {
951 &mut self.mesh_quality
952 }
953}
954
955impl MeshQuality {
956 pub fn new() -> Self {
957 Self {
958 quality_metrics: HashMap::new(),
959 quality_assessment: QualityAssessment::new(),
960 }
961 }
962
963 pub fn add_metric(&mut self, metric: QualityMetric) {
965 self.quality_metrics
966 .insert(metric.metric_name.clone(), metric);
967 }
968
969 pub fn get_metric(&self, name: &str) -> Option<&QualityMetric> {
971 self.quality_metrics.get(name)
972 }
973
974 pub fn list_metrics(&self) -> Vec<String> {
976 let mut names: Vec<String> = self.quality_metrics.keys().cloned().collect();
977 names.sort();
978 names
979 }
980
981 pub fn quality_assessment(&self) -> &QualityAssessment {
983 &self.quality_assessment
984 }
985}
986
987impl QualityAssessment {
988 pub fn new() -> Self {
989 Self {
990 overall_quality: 0.95,
991 quality_grade: QualityGrade::Excellent,
992 recommendations: Vec::new(),
993 }
994 }
995}
996
997impl ElementLibrary {
998 pub fn new() -> Self {
999 Self {
1000 elements: HashMap::new(),
1001 element_properties: HashMap::new(),
1002 }
1003 }
1004
1005 pub fn initialize(&mut self) -> Result<(), EngineeringError> {
1010 let default_props = ElementProperties {
1012 material_properties: MaterialProperties {
1013 youngs_modulus: 200_000.0,
1014 poissons_ratio: 0.3,
1015 density: 7850.0,
1016 thermal_expansion: 1.2e-5,
1017 thermal_conductivity: 50.0,
1018 specific_heat: 500.0,
1019 yield_strength: 250.0,
1020 ultimate_strength: 400.0,
1021 },
1022 geometric_properties: GeometricProperties {
1023 area: 1.0,
1024 volume: 1.0,
1025 perimeter: 4.0,
1026 surface_area: 6.0,
1027 },
1028 section_properties: SectionProperties {
1029 moment_of_inertia: vec![1.0 / 12.0, 1.0 / 12.0, 1.0 / 12.0],
1030 torsional_constant: 1.0 / 12.0,
1031 section_modulus: vec![1.0 / 6.0, 1.0 / 6.0, 1.0 / 6.0],
1032 shear_center: vec![0.0, 0.0, 0.0],
1033 },
1034 };
1035
1036 let make_nodes = |count: usize, dofs: &[DOF]| -> Vec<Node> {
1038 (0..count)
1039 .map(|i| Node {
1040 node_id: format!("n{i}"),
1041 coordinates: vec![i as f64, 0.0, 0.0],
1042 degrees_of_freedom: dofs.to_vec(),
1043 constraints: Vec::new(),
1044 })
1045 .collect()
1046 };
1047
1048 let truss = Element {
1050 element_id: "truss_2node".to_string(),
1051 element_name: "2-Node Truss".to_string(),
1052 element_type: ElementType::Truss,
1053 nodes: make_nodes(2, &[DOF::UX, DOF::UY]),
1054 properties: default_props.clone(),
1055 };
1056 self.elements.insert("truss_2node".to_string(), truss);
1057 self.element_properties
1058 .insert("truss_2node".to_string(), default_props.clone());
1059
1060 let beam = Element {
1062 element_id: "beam_2node".to_string(),
1063 element_name: "2-Node Beam".to_string(),
1064 element_type: ElementType::Beam,
1065 nodes: make_nodes(2, &[DOF::UX, DOF::UY, DOF::ROTZ]),
1066 properties: default_props.clone(),
1067 };
1068 self.elements.insert("beam_2node".to_string(), beam);
1069 self.element_properties
1070 .insert("beam_2node".to_string(), default_props.clone());
1071
1072 let quad = Element {
1074 element_id: "quad_4node".to_string(),
1075 element_name: "4-Node Quadrilateral Shell".to_string(),
1076 element_type: ElementType::Shell,
1077 nodes: make_nodes(4, &[DOF::UX, DOF::UY]),
1078 properties: default_props.clone(),
1079 };
1080 self.elements.insert("quad_4node".to_string(), quad);
1081 self.element_properties
1082 .insert("quad_4node".to_string(), default_props.clone());
1083
1084 let hex = Element {
1086 element_id: "hex_8node".to_string(),
1087 element_name: "8-Node Hexahedral Solid".to_string(),
1088 element_type: ElementType::Hexahedron,
1089 nodes: make_nodes(8, &[DOF::UX, DOF::UY, DOF::UZ]),
1090 properties: default_props.clone(),
1091 };
1092 self.elements.insert("hex_8node".to_string(), hex);
1093 self.element_properties
1094 .insert("hex_8node".to_string(), default_props.clone());
1095
1096 let tet = Element {
1098 element_id: "tet_4node".to_string(),
1099 element_name: "4-Node Tetrahedral Solid".to_string(),
1100 element_type: ElementType::Tetrahedron,
1101 nodes: make_nodes(4, &[DOF::UX, DOF::UY, DOF::UZ]),
1102 properties: default_props.clone(),
1103 };
1104 self.elements.insert("tet_4node".to_string(), tet);
1105 self.element_properties
1106 .insert("tet_4node".to_string(), default_props.clone());
1107
1108 let shell = Element {
1110 element_id: "shell_8node".to_string(),
1111 element_name: "8-Node Shell".to_string(),
1112 element_type: ElementType::Shell,
1113 nodes: make_nodes(
1114 8,
1115 &[DOF::UX, DOF::UY, DOF::UZ, DOF::ROTX, DOF::ROTY, DOF::ROTZ],
1116 ),
1117 properties: default_props.clone(),
1118 };
1119 self.elements.insert("shell_8node".to_string(), shell);
1120 self.element_properties
1121 .insert("shell_8node".to_string(), default_props);
1122
1123 Ok(())
1124 }
1125
1126 pub fn get_element(&self, name: &str) -> Option<&Element> {
1128 self.elements.get(name)
1129 }
1130
1131 pub fn get_properties(&self, name: &str) -> Option<&ElementProperties> {
1133 self.element_properties.get(name)
1134 }
1135
1136 pub fn list_elements(&self) -> Vec<String> {
1138 let mut names: Vec<String> = self.elements.keys().cloned().collect();
1139 names.sort();
1140 names
1141 }
1142}
1143
1144impl SolverEngine {
1145 pub fn new() -> Self {
1146 Self {
1147 solvers: HashMap::new(),
1148 solver_parameters: SolverParameters::new(),
1149 convergence_criteria: ConvergenceCriteria::new(),
1150 }
1151 }
1152
1153 pub fn initialize(&mut self) -> Result<(), EngineeringError> {
1154 Ok(())
1155 }
1156
1157 pub fn add_solver(&mut self, solver: Solver) {
1159 self.solvers.insert(solver.solver_id.clone(), solver);
1160 }
1161
1162 pub fn get_solver(&self, id: &str) -> Option<&Solver> {
1164 self.solvers.get(id)
1165 }
1166
1167 pub fn list_solvers(&self) -> Vec<String> {
1169 let mut ids: Vec<String> = self.solvers.keys().cloned().collect();
1170 ids.sort();
1171 ids
1172 }
1173
1174 pub fn solver_parameters(&self) -> &SolverParameters {
1176 &self.solver_parameters
1177 }
1178
1179 pub fn solver_parameters_mut(&mut self) -> &mut SolverParameters {
1181 &mut self.solver_parameters
1182 }
1183
1184 pub fn convergence_criteria(&self) -> &ConvergenceCriteria {
1186 &self.convergence_criteria
1187 }
1188
1189 pub fn convergence_criteria_mut(&mut self) -> &mut ConvergenceCriteria {
1191 &mut self.convergence_criteria
1192 }
1193}
1194
1195impl SolverParameters {
1196 pub fn new() -> Self {
1197 Self {
1198 tolerance: 1e-6,
1199 max_iterations: 1000,
1200 convergence_acceleration: ConvergenceAcceleration::None,
1201 }
1202 }
1203}
1204
1205impl ConvergenceCriteria {
1206 pub fn new() -> Self {
1207 Self {
1208 criteria_type: ConvergenceType::Residual,
1209 tolerance: 1e-6,
1210 max_iterations: 1000,
1211 }
1212 }
1213}
1214
1215impl PostProcessor {
1216 pub fn new() -> Self {
1217 Self {
1218 result_extractors: HashMap::new(),
1219 visualization_engine: VisualizationEngine::new(),
1220 report_generator: ReportGenerator::new(),
1221 }
1222 }
1223
1224 pub fn initialize(&mut self) -> Result<(), EngineeringError> {
1225 self.visualization_engine.initialize()?;
1226 self.report_generator.initialize()?;
1227 Ok(())
1228 }
1229
1230 pub fn add_extractor(&mut self, extractor: ResultExtractor) {
1232 self.result_extractors
1233 .insert(extractor.extractor_id.clone(), extractor);
1234 }
1235
1236 pub fn get_extractor(&self, id: &str) -> Option<&ResultExtractor> {
1238 self.result_extractors.get(id)
1239 }
1240
1241 pub fn list_extractors(&self) -> Vec<String> {
1243 let mut ids: Vec<String> = self.result_extractors.keys().cloned().collect();
1244 ids.sort();
1245 ids
1246 }
1247}
1248
1249impl VisualizationEngine {
1250 pub fn new() -> Self {
1251 Self {
1252 visualization_types: HashMap::new(),
1253 rendering_engine: RenderingEngine::new(),
1254 }
1255 }
1256
1257 pub fn initialize(&mut self) -> Result<(), EngineeringError> {
1258 Ok(())
1259 }
1260
1261 pub fn add_visualization_type(&mut self, name: impl Into<String>, vtype: VisualizationType) {
1263 self.visualization_types.insert(name.into(), vtype);
1264 }
1265
1266 pub fn get_visualization_type(&self, name: &str) -> Option<&VisualizationType> {
1268 self.visualization_types.get(name)
1269 }
1270
1271 pub fn list_visualization_types(&self) -> Vec<String> {
1273 let mut names: Vec<String> = self.visualization_types.keys().cloned().collect();
1274 names.sort();
1275 names
1276 }
1277
1278 pub fn rendering_engine(&self) -> &RenderingEngine {
1280 &self.rendering_engine
1281 }
1282
1283 pub fn rendering_engine_mut(&mut self) -> &mut RenderingEngine {
1285 &mut self.rendering_engine
1286 }
1287}
1288
1289impl RenderingEngine {
1290 pub fn new() -> Self {
1291 Self {
1292 engine_type: RenderingEngineType::OpenGL,
1293 rendering_options: RenderingOptions::new(),
1294 }
1295 }
1296}
1297
1298impl RenderingOptions {
1299 pub fn new() -> Self {
1300 Self {
1301 color_map: "jet".to_string(),
1302 scale_factor: 1.0,
1303 line_width: 1.0,
1304 transparency: 0.0,
1305 }
1306 }
1307}
1308
1309impl ReportGenerator {
1310 pub fn new() -> Self {
1311 Self {
1312 report_templates: HashMap::new(),
1313 export_formats: vec![ExportFormat::PDF, ExportFormat::HTML],
1314 }
1315 }
1316
1317 pub fn initialize(&mut self) -> Result<(), EngineeringError> {
1318 Ok(())
1319 }
1320
1321 pub fn add_template(&mut self, template: ReportTemplate) {
1323 self.report_templates
1324 .insert(template.template_id.clone(), template);
1325 }
1326
1327 pub fn get_template(&self, id: &str) -> Option<&ReportTemplate> {
1329 self.report_templates.get(id)
1330 }
1331
1332 pub fn list_templates(&self) -> Vec<String> {
1334 let mut ids: Vec<String> = self.report_templates.keys().cloned().collect();
1335 ids.sort();
1336 ids
1337 }
1338
1339 pub fn export_formats(&self) -> &[ExportFormat] {
1341 &self.export_formats
1342 }
1343
1344 pub fn add_export_format(&mut self, format: ExportFormat) {
1346 if !self.export_formats.contains(&format) {
1347 self.export_formats.push(format);
1348 }
1349 }
1350}