File size: 6,398 Bytes
8e5ba9e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
"""Data schemas for structural mechanics dataset.

Pydantic models enforce type safety and validation across the entire pipeline.
All quantities use SI units (meters, Pascals, Newtons).
"""

from enum import Enum
from typing import Optional

from pydantic import BaseModel, Field, field_validator


class ProblemFamily(str, Enum):
    BEAM = "beam"
    PLATE = "plate"
    VESSEL = "vessel"


class BeamConfig(str, Enum):
    SS_POINT = "beam_ss_point"
    SS_UDL = "beam_ss_udl"
    CANTILEVER_POINT = "beam_cantilever_point"
    CANTILEVER_UDL = "beam_cantilever_udl"
    FIXED_POINT = "beam_fixed_point"
    FIXED_UDL = "beam_fixed_udl"


class PlateConfig(str, Enum):
    SS_UNIFORM = "plate_ss_uniform"
    FIXED_UNIFORM = "plate_fixed_uniform"


class VesselConfig(str, Enum):
    CYLINDER = "vessel_cylinder"
    SPHERE = "vessel_sphere"


class SafetyCategory(str, Enum):
    SAFE = "safe"          # SF >= 2.0
    MARGINAL = "marginal"  # 1.0 <= SF < 2.0
    FAILURE = "failure"    # SF < 1.0


class MaterialProperties(BaseModel):
    """Material properties in SI units."""

    elastic_modulus: float = Field(..., gt=0, description="Young's modulus E [Pa]")
    poisson_ratio: float = Field(..., gt=0, lt=0.5, description="Poisson's ratio nu [-]")
    yield_strength: float = Field(..., gt=0, description="Yield strength sigma_y [Pa]")
    density: float = Field(..., gt=0, description="Density rho [kg/m^3]")


class BeamParameters(BaseModel):
    """Input parameters for Euler-Bernoulli beam analysis."""

    length: float = Field(..., gt=0, description="Beam span L [m]")
    width: float = Field(..., gt=0, description="Cross-section width b [m]")
    height: float = Field(..., gt=0, description="Cross-section height h [m]")
    material: MaterialProperties
    point_load: float = Field(default=0.0, ge=0, description="Point load P [N]")
    distributed_load: float = Field(default=0.0, ge=0, description="Distributed load w [N/m]")

    @property
    def moment_of_inertia(self) -> float:
        """Second moment of area I = bh^3/12 [m^4]."""
        return self.width * self.height**3 / 12.0

    @property
    def section_modulus(self) -> float:
        """Section modulus S = bh^2/6 [m^3]."""
        return self.width * self.height**2 / 6.0

    @property
    def cross_section_area(self) -> float:
        """Cross-section area A = bh [m^2]."""
        return self.width * self.height


class PlateParameters(BaseModel):
    """Input parameters for Kirchhoff-Love plate analysis."""

    length_a: float = Field(..., gt=0, description="Plate dimension a [m]")
    length_b: float = Field(..., gt=0, description="Plate dimension b [m]")
    thickness: float = Field(..., gt=0, description="Plate thickness t [m]")
    material: MaterialProperties
    pressure: float = Field(..., gt=0, description="Uniform pressure q [Pa]")

    @property
    def flexural_rigidity(self) -> float:
        """Flexural rigidity D = Et^3 / (12(1 - nu^2)) [N*m]."""
        E = self.material.elastic_modulus
        nu = self.material.poisson_ratio
        t = self.thickness
        return E * t**3 / (12.0 * (1.0 - nu**2))

    @property
    def aspect_ratio(self) -> float:
        """Aspect ratio a/b [-]."""
        return self.length_a / self.length_b


class VesselParameters(BaseModel):
    """Input parameters for thick-walled pressure vessel analysis (Lame equations)."""

    inner_radius: float = Field(..., gt=0, description="Inner radius r_i [m]")
    outer_radius: float = Field(..., gt=0, description="Outer radius r_o [m]")
    length: Optional[float] = Field(default=None, gt=0, description="Cylinder length L [m]")
    material: MaterialProperties
    internal_pressure: float = Field(..., gt=0, description="Internal pressure p [Pa]")

    @field_validator("outer_radius")
    @classmethod
    def outer_must_exceed_inner(cls, v: float, info: dict) -> float:
        if "inner_radius" in info.data and v <= info.data["inner_radius"]:
            raise ValueError("outer_radius must be greater than inner_radius")
        return v

    @property
    def radius_ratio(self) -> float:
        """Radius ratio r_o/r_i [-]."""
        return self.outer_radius / self.inner_radius

    @property
    def wall_thickness(self) -> float:
        """Wall thickness t = r_o - r_i [m]."""
        return self.outer_radius - self.inner_radius


class SolutionResult(BaseModel):
    """Output from an analytical solver."""

    max_stress: float = Field(..., ge=0, description="Maximum stress [Pa]")
    max_deflection: float = Field(..., ge=0, description="Maximum deflection [m]")
    safety_factor: float = Field(..., ge=0, description="Safety factor = sigma_y / max_stress [-]")
    safety_category: SafetyCategory

    @classmethod
    def from_stress(cls, max_stress: float, max_deflection: float, yield_strength: float) -> "SolutionResult":
        """Construct result and auto-classify safety category."""
        sf = yield_strength / max_stress if max_stress > 0 else float("inf")

        if sf >= 2.0:
            category = SafetyCategory.SAFE
        elif sf >= 1.0:
            category = SafetyCategory.MARGINAL
        else:
            category = SafetyCategory.FAILURE

        return cls(
            max_stress=max_stress,
            max_deflection=abs(max_deflection),
            safety_factor=sf,
            safety_category=category,
        )


class DatasetSample(BaseModel):
    """A single row in the dataset. Flat structure for Parquet compatibility."""

    sample_id: str
    problem_family: ProblemFamily
    config_id: str

    # Geometry (SI: meters)
    length: float
    width: Optional[float] = None
    height: Optional[float] = None
    inner_radius: Optional[float] = None
    outer_radius: Optional[float] = None
    thickness: Optional[float] = None   # plate thickness

    # Material (SI)
    elastic_modulus: float
    poisson_ratio: float
    yield_strength: float
    density: float

    # Loading (SI)
    point_load: float = 0.0
    distributed_load: float = 0.0
    internal_pressure: float = 0.0
    pressure: float = 0.0  # plate uniform pressure

    # Derived geometry
    moment_of_inertia: Optional[float] = None
    section_modulus: Optional[float] = None
    cross_section_area: Optional[float] = None

    # Outputs
    max_stress: float
    max_deflection: float
    safety_factor: float
    safety_category: SafetyCategory