Spaces:
Sleeping
Sleeping
| """Kirchhoff-Love rectangular plate solvers with Navier series solutions. | |
| Two configurations: | |
| - Simply supported on all edges + uniform pressure | |
| - Clamped on all edges + uniform pressure | |
| The simply supported case uses the exact Navier double Fourier series. | |
| The clamped case uses tabulated coefficients from Timoshenko & Woinowsky-Krieger. | |
| Assumptions (Kirchhoff-Love theory): | |
| - Thin plate (t << a, b) | |
| - Small deflections (w << t) | |
| - Linear elasticity | |
| - Homogeneous, isotropic material | |
| Reference: Timoshenko, S.P. & Woinowsky-Krieger, S., | |
| "Theory of Plates and Shells", 2nd Ed. | |
| """ | |
| import math | |
| from typing import Any | |
| import numpy as np | |
| from src.data.schema import SolutionResult | |
| from src.data.solvers.base import AnalyticalSolver | |
| class SimplySupported_UniformPressure(AnalyticalSolver): | |
| """Simply supported rectangular plate under uniform pressure q. | |
| Uses Navier's double Fourier series solution (exact): | |
| w(x,y) = sum_m sum_n [a_mn * sin(m*pi*x/a) * sin(n*pi*y/b)] | |
| where a_mn = 16*q / (pi^6 * D * m*n * (m^2/a^2 + n^2/b^2)^2) | |
| for odd m, n only. | |
| Max deflection at center (a/2, b/2): | |
| w_max = sum of a_mn * sin(m*pi/2) * sin(n*pi/2) | |
| Max bending stress from max moment: | |
| M_x_max at center, sigma = 6*M_x / t^2 | |
| """ | |
| N_TERMS = 50 # Fourier series terms (converges well by ~20) | |
| def config_id(self) -> str: | |
| return "plate_ss_uniform" | |
| def problem_family(self) -> str: | |
| return "plate" | |
| def solve(self, params: dict[str, Any]) -> SolutionResult: | |
| a = params["length_a"] | |
| b = params["length_b"] | |
| t = params["thickness"] | |
| E = params["elastic_modulus"] | |
| nu = params["poisson_ratio"] | |
| sigma_y = params["yield_strength"] | |
| q = params["pressure"] | |
| D = E * t**3 / (12.0 * (1.0 - nu**2)) | |
| w_max = 0.0 | |
| Mx_max = 0.0 | |
| My_max = 0.0 | |
| for m in range(1, self.N_TERMS + 1, 2): # odd m only | |
| for n in range(1, self.N_TERMS + 1, 2): # odd n only | |
| denom = (m**2 / a**2 + n**2 / b**2) ** 2 | |
| # Fourier coefficient | |
| a_mn = 16.0 * q / (math.pi**6 * D * m * n * denom) | |
| # Deflection at center: sin(m*pi/2) * sin(n*pi/2) | |
| sign_w = math.sin(m * math.pi / 2) * math.sin(n * math.pi / 2) | |
| w_max += a_mn * sign_w | |
| # Bending moments at center | |
| # M_x = -D * (d2w/dx2 + nu * d2w/dy2) | |
| factor_x = (m * math.pi / a) ** 2 + nu * (n * math.pi / b) ** 2 | |
| factor_y = nu * (m * math.pi / a) ** 2 + (n * math.pi / b) ** 2 | |
| Mx_max += a_mn * factor_x * sign_w | |
| My_max += a_mn * factor_y * sign_w | |
| # Convert moment to stress: sigma = 6*M / t^2 (plate bending formula) | |
| Mx_max *= D # a_mn already divided by D, so Mx = D * sum(a_mn * factor * sign) | |
| My_max *= D | |
| sigma_x = 6.0 * abs(Mx_max) / t**2 | |
| sigma_y_stress = 6.0 * abs(My_max) / t**2 | |
| max_stress = max(sigma_x, sigma_y_stress) | |
| return SolutionResult.from_stress(max_stress, abs(w_max), sigma_y) | |
| class Clamped_UniformPressure(AnalyticalSolver): | |
| """Clamped rectangular plate under uniform pressure q. | |
| No exact Fourier series exists for clamped edges. Uses interpolated | |
| coefficients from Timoshenko Table 35 (pp. 202): | |
| w_max = alpha * q * a^4 / D | |
| M_max = beta * q * a^2 | |
| where alpha and beta depend on the aspect ratio b/a. | |
| For b/a >= 1 (a is the shorter side), standard tabulated values apply. | |
| Reference values (b/a -> alpha, beta_center, beta_edge): | |
| 1.0 -> 0.00126, 0.0231, 0.0513 | |
| 1.2 -> 0.00172, 0.0299, 0.0639 | |
| 1.4 -> 0.00207, 0.0349, 0.0726 | |
| 1.6 -> 0.00230, 0.0381, 0.0780 | |
| 1.8 -> 0.00245, 0.0401, 0.0812 | |
| 2.0 -> 0.00254, 0.0412, 0.0829 | |
| inf -> 0.00260, 0.0417, 0.0833 | |
| """ | |
| # Tabulated coefficients: (b/a, alpha, beta_edge) | |
| # beta_edge gives max stress at mid-edge (clamped), which exceeds center stress | |
| _TABLE = np.array([ | |
| [1.0, 0.00126, 0.0513], | |
| [1.2, 0.00172, 0.0639], | |
| [1.4, 0.00207, 0.0726], | |
| [1.6, 0.00230, 0.0780], | |
| [1.8, 0.00245, 0.0812], | |
| [2.0, 0.00254, 0.0829], | |
| [3.0, 0.00260, 0.0833], # approaches infinite strip | |
| [5.0, 0.00260, 0.0833], | |
| ]) | |
| def config_id(self) -> str: | |
| return "plate_fixed_uniform" | |
| def problem_family(self) -> str: | |
| return "plate" | |
| def solve(self, params: dict[str, Any]) -> SolutionResult: | |
| a_dim = params["length_a"] | |
| b_dim = params["length_b"] | |
| t = params["thickness"] | |
| E = params["elastic_modulus"] | |
| nu = params["poisson_ratio"] | |
| sigma_y = params["yield_strength"] | |
| q = params["pressure"] | |
| # Ensure a <= b (a is the shorter side for table lookup) | |
| if a_dim > b_dim: | |
| a, b = b_dim, a_dim | |
| else: | |
| a, b = a_dim, b_dim | |
| D = E * t**3 / (12.0 * (1.0 - nu**2)) | |
| ratio = b / a | |
| # Interpolate coefficients from table | |
| alpha = float(np.interp(ratio, self._TABLE[:, 0], self._TABLE[:, 1])) | |
| beta = float(np.interp(ratio, self._TABLE[:, 0], self._TABLE[:, 2])) | |
| max_deflection = alpha * q * a**4 / D | |
| # beta is the moment coefficient: M_max = beta * q * a^2 | |
| # Bending stress: sigma = 6*M / t^2 | |
| max_stress = 6.0 * beta * q * a**2 / t**2 | |
| return SolutionResult.from_stress(max_stress, abs(max_deflection), sigma_y) | |
| PLATE_SOLVERS: dict[str, type[AnalyticalSolver]] = { | |
| "plate_ss_uniform": SimplySupported_UniformPressure, | |
| "plate_fixed_uniform": Clamped_UniformPressure, | |
| } | |