dsh-engineering-ui 0.0.0-stage → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +772 -3
- package/engineering/plugins/dsh-engineering-ui/cordis.patch.yml +5 -0
- package/engineering/plugins/dsh-engineering-ui/icon.svg +14 -0
- package/engineering/plugins/dsh-engineering-ui/lib/client.js +8259 -0
- package/engineering/plugins/dsh-engineering-ui/lib/defense-sign.js +1552 -0
- package/engineering/plugins/dsh-engineering-ui/lib/index.js +5633 -0
- package/engineering/plugins/dsh-engineering-ui/lib/job-wait-guard.js +236 -0
- package/engineering/plugins/dsh-engineering-ui/lib/mem-guard.js +331 -0
- package/engineering/plugins/dsh-engineering-ui/lib/webots.js +114 -0
- package/engineering/plugins/dsh-engineering-ui/locale/en.json +6 -0
- package/engineering/plugins/dsh-engineering-ui/locale/zh.json +6 -0
- package/engineering/plugins/dsh-engineering-ui/preset-engineering.patch.yml +601 -0
- package/engineering/skills/assembly-orchestration/SKILL.md +651 -0
- package/engineering/skills/cad-workflow/SKILL.md +132 -0
- package/engineering/skills/mode-selection/SKILL.md +660 -0
- package/engineering/skills/physics-in-loop/SKILL.md +238 -0
- package/engineering/skills/physics-in-loop/references/feadocs.md +142 -0
- package/engineering/skills/solidworks-bridge/SKILL.md +445 -0
- package/engineering/skills/sw-design/SKILL.md +112 -0
- package/engineering/skills/sw-design/references/autocad-workflow.md +81 -0
- package/engineering/skills/sw-design/references/gbt-drafting.md +71 -0
- package/engineering/skills/sw-to-cad/SKILL.md +121 -0
- package/engineering/skills/webots-simulation/SKILL.md +58 -0
- package/engineering/tools/LICENSE.Auto-SolidWorks +21 -0
- package/engineering/tools/_plugin_paths.py +93 -0
- package/engineering/tools/_root.py +330 -0
- package/engineering/tools/_store.py +446 -0
- package/engineering/tools/ac_bridge.py +459 -0
- package/engineering/tools/ac_rules.json +21 -0
- package/engineering/tools/ac_validate.py +699 -0
- package/engineering/tools/ask_user.py +887 -0
- package/engineering/tools/build_arm1.py +46 -0
- package/engineering/tools/build_assembly.py +94 -0
- package/engineering/tools/build_base.py +57 -0
- package/engineering/tools/build_housing.py +70 -0
- package/engineering/tools/build_structural.py +43 -0
- package/engineering/tools/build_transmission.py +69 -0
- package/engineering/tools/cadx.lsp +128 -0
- package/engineering/tools/choice_contract.py +61 -0
- package/engineering/tools/conn_state.py +1671 -0
- package/engineering/tools/defense_gate.py +3374 -0
- package/engineering/tools/drawing_input.py +314 -0
- package/engineering/tools/env_check.py +467 -0
- package/engineering/tools/examples/build_axis.py +142 -0
- package/engineering/tools/examples/build_cup.py +56 -0
- package/engineering/tools/examples/build_plate.py +58 -0
- package/engineering/tools/examples/build_sphere.py +30 -0
- package/engineering/tools/examples/build_transition_duct.py +381 -0
- package/engineering/tools/gate_integrity.py +401 -0
- package/engineering/tools/mode2_flow.py +3589 -0
- package/engineering/tools/mode_checker.py +77 -0
- package/engineering/tools/mode_gate.py +8013 -0
- package/engineering/tools/part_registry.py +4266 -0
- package/engineering/tools/physics/__init__.py +50 -0
- package/engineering/tools/physics/_compat.py +12 -0
- package/engineering/tools/physics/design_state.py +142 -0
- package/engineering/tools/physics/domain_validator.py +751 -0
- package/engineering/tools/physics/fatigue.py +489 -0
- package/engineering/tools/physics/fea_solver.py +1163 -0
- package/engineering/tools/physics/feapy_solver.py +314 -0
- package/engineering/tools/physics/gb_materials.json +1228 -0
- package/engineering/tools/physics/geometry_gate.py +337 -0
- package/engineering/tools/physics/load_case.py +951 -0
- package/engineering/tools/physics/material_db.py +874 -0
- package/engineering/tools/physics/mesh_adapter.py +321 -0
- package/engineering/tools/physics/refine_rules.py +272 -0
- package/engineering/tools/physics/rules/gb_base_rules.json +377 -0
- package/engineering/tools/physics/rules/gb_drafting_rules.json +57 -0
- package/engineering/tools/physics/rules/gb_electrical_rules.json +25 -0
- package/engineering/tools/physics/rules/gb_fasteners_rules.json +73 -0
- package/engineering/tools/physics/rules/gb_housing_rules.json +41 -0
- package/engineering/tools/physics/rules/gb_manufacturing_rules.json +25 -0
- package/engineering/tools/physics/rules/gb_material_verify_rules.json +72 -0
- package/engineering/tools/physics/rules/gb_materials_rules.json +377 -0
- package/engineering/tools/physics/rules/gb_safety_rules.json +89 -0
- package/engineering/tools/physics/rules/gb_springs_rules.json +41 -0
- package/engineering/tools/physics/rules/gb_standards_registry.json +1646 -0
- package/engineering/tools/physics/rules/gb_structural_rules.json +201 -0
- package/engineering/tools/physics/rules/gb_surface_rules.json +25 -0
- package/engineering/tools/physics/rules/gb_transmission_rules.json +233 -0
- package/engineering/tools/physics/rules/housing_rules.json +48 -0
- package/engineering/tools/physics/rules/manifest.json +54 -0
- package/engineering/tools/physics/rules/mold_rules.json +48 -0
- package/engineering/tools/physics/rules/structural_rules.json +56 -0
- package/engineering/tools/physics/rules/transmission_rules.json +48 -0
- package/engineering/tools/physics/simulation_report.py +829 -0
- package/engineering/tools/physics_bridge.py +2685 -0
- package/engineering/tools/profile-manifest.ps1 +72 -0
- package/engineering/tools/run_regression.py +109 -0
- package/engineering/tools/settings.py +419 -0
- package/engineering/tools/solidworks-modeling.md +519 -0
- package/engineering/tools/sw_bridge.py +8215 -0
- package/engineering/tools/sw_guard.py +1066 -0
- package/engineering/tools/sw_surface.py +87 -0
- package/engineering/tools/swapi.py +14766 -0
- package/engineering/tools/verify_sync.py +218 -0
- package/engineering/tools/visual_check.py +2589 -0
- package/engineering/tools/webots_env.py +121 -0
- package/engineering/tools/webots_mcp/__init__.py +3 -0
- package/engineering/tools/webots_mcp/__main__.py +3 -0
- package/engineering/tools/webots_mcp/assembly.py +391 -0
- package/engineering/tools/webots_mcp/bootstrap.py +55 -0
- package/engineering/tools/webots_mcp/controller_worker.py +124 -0
- package/engineering/tools/webots_mcp/evidence.py +288 -0
- package/engineering/tools/webots_mcp/fake_worker.py +69 -0
- package/engineering/tools/webots_mcp/fault_resources.py +121 -0
- package/engineering/tools/webots_mcp/host.py +45 -0
- package/engineering/tools/webots_mcp/ledger.py +73 -0
- package/engineering/tools/webots_mcp/owned_job.py +70 -0
- package/engineering/tools/webots_mcp/physical_gate.py +187 -0
- package/engineering/tools/webots_mcp/physics.py +152 -0
- package/engineering/tools/webots_mcp/pipeline.py +108 -0
- package/engineering/tools/webots_mcp/recipes.py +129 -0
- package/engineering/tools/webots_mcp/repair.py +132 -0
- package/engineering/tools/webots_mcp/repair_trial.py +375 -0
- package/engineering/tools/webots_mcp/runtime.py +311 -0
- package/engineering/tools/webots_mcp/server.py +217 -0
- package/engineering/tools/webots_mcp/sw_export.py +509 -0
- package/engineering/tools/webots_mcp/transport.py +156 -0
- package/engineering/tools/workflow_gate.py +6134 -0
- package/package.json +64 -4
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
fatigue.py — 疲劳强度与设计寿命(30 年)校核
|
|
4
|
+
==============================================
|
|
5
|
+
【BUG-02 修复】原工具链只有线性静力(2D 平面应力),完全没有疲劳/寿命校核,
|
|
6
|
+
用户"30 年使用寿命"这一核心需求无法验证。本模块补齐该能力。
|
|
7
|
+
|
|
8
|
+
方法链(工程标准做法,纯 Python + numpy,无外部依赖):
|
|
9
|
+
|
|
10
|
+
1) S-N 曲线(Basquin 幂律)
|
|
11
|
+
sigma_a = sigma_f' * (2N)^b
|
|
12
|
+
钢材:由 UTS 估算疲劳强度系数 sigma_f' 与指数 b
|
|
13
|
+
铝合金:无真正疲劳极限,取 5×10^8 次为条件疲劳极限
|
|
14
|
+
|
|
15
|
+
2) 疲劳极限修正(Marin 系数,Shigley 体系)
|
|
16
|
+
Se = ka·kb·kc·kd·ke·Se'
|
|
17
|
+
ka 表面加工 kb 尺寸 kc 载荷型式 kd 温度 ke 可靠度
|
|
18
|
+
|
|
19
|
+
3) 平均应力修正(三种准则可选)
|
|
20
|
+
Goodman : sigma_a/Se + sigma_m/Su = 1/n (保守,默认)
|
|
21
|
+
Soderberg: sigma_a/Se + sigma_m/Sy = 1/n (最保守)
|
|
22
|
+
Gerber : n·sigma_a/Se + (n·sigma_m/Su)^2 = 1(延性材料较贴合)
|
|
23
|
+
|
|
24
|
+
4) 载荷谱 → Miner 线性累积损伤
|
|
25
|
+
D = Σ (n_i / N_i)
|
|
26
|
+
30 年寿命判定:D_30y < 1 → 通过;否则给出可承受年限 T = 1 / (D/年)
|
|
27
|
+
|
|
28
|
+
5) 输出结构
|
|
29
|
+
{ok, method, endurance_limit_mpa, fatigue_sf, damage_30y,
|
|
30
|
+
life_years_allowable, verdict, gates, details, limitations}
|
|
31
|
+
|
|
32
|
+
设计原则:宁可保守也不虚高。所有输入缺失时用明确的默认值,并在结果里
|
|
33
|
+
标注 default_used,绝不静默假设。
|
|
34
|
+
"""
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
from typing import Any, Optional
|
|
38
|
+
|
|
39
|
+
# ── 可靠度系数 ke(Shigley 表,对应 90%~99.99% 可靠度)──────────────────
|
|
40
|
+
_RELIABILITY_KE = {
|
|
41
|
+
0.50: 1.000, 0.90: 0.897, 0.95: 0.868, 0.99: 0.814,
|
|
42
|
+
0.999: 0.753, 0.9999: 0.702,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# ── 表面加工系数 ka 的经验参数(ka = a * Su^b,Su 单位 MPa)──────────────
|
|
46
|
+
_SURFACE_AB = {
|
|
47
|
+
# 加工方式: (a, b)
|
|
48
|
+
"ground": (1.58, -0.085), # 磨削
|
|
49
|
+
"machined": (4.51, -0.265), # 机加工/冷拔
|
|
50
|
+
"cold_drawn": (4.51, -0.265),
|
|
51
|
+
"hot_rolled": (57.7, -0.718), # 热轧
|
|
52
|
+
"as_forged": (272.0, -0.995), # 锻造
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# ── 铝合金(无真实疲劳极限,用 5e8 次条件极限)────────────────────────────
|
|
56
|
+
_ALUMINUM_IDS = ("AL_", "ALUMINUM", "6061", "7075", "5052", "1060")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _is_aluminum(mat: dict) -> bool:
|
|
60
|
+
hay = (str(mat.get("id", "")) + " " + str(mat.get("name", ""))).upper()
|
|
61
|
+
return any(k in hay for k in _ALUMINUM_IDS)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _surface_factor(finish: str, uts_mpa: float) -> tuple[float, str]:
|
|
65
|
+
"""表面加工修正系数 ka。"""
|
|
66
|
+
key = str(finish or "machined").strip().lower().replace(" ", "_")
|
|
67
|
+
# 中文别名
|
|
68
|
+
alias = {"磨削": "ground", "机加工": "machined", "车削": "machined",
|
|
69
|
+
"热轧": "hot_rolled", "锻造": "as_forged", "铸造": "as_forged",
|
|
70
|
+
"铸件": "as_forged", "钣金": "cold_drawn"}
|
|
71
|
+
key = alias.get(key, key)
|
|
72
|
+
a, b = _SURFACE_AB.get(key, _SURFACE_AB["machined"])
|
|
73
|
+
ka = a * (max(uts_mpa, 1.0) ** b)
|
|
74
|
+
ka = min(max(ka, 0.05), 1.0) # 物理上不超过 1
|
|
75
|
+
return ka, key
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _size_factor(diameter_mm: Optional[float]) -> tuple[float, str]:
|
|
79
|
+
"""尺寸修正系数 kb(Shigley 旋转弯曲)。"""
|
|
80
|
+
if diameter_mm is None or diameter_mm <= 0:
|
|
81
|
+
return 0.85, "default(未给特征尺寸,取 0.85)"
|
|
82
|
+
d = float(diameter_mm)
|
|
83
|
+
if d <= 8.0:
|
|
84
|
+
return 1.0, "d<=8mm → 1.0"
|
|
85
|
+
if d <= 51.0:
|
|
86
|
+
return round(0.869 * (d ** -0.097), 4), "8<d<=51mm 公式"
|
|
87
|
+
if d <= 254.0:
|
|
88
|
+
return round(1.189 * (d ** -0.097), 4), "51<d<=254mm 公式"
|
|
89
|
+
return 0.60, "d>254mm → 0.60(保守)"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _load_factor(load_type: str, mode: str = "bending") -> tuple[float, str]:
|
|
93
|
+
"""载荷型式修正系数 kc。"""
|
|
94
|
+
lt = str(load_type or "static").lower()
|
|
95
|
+
if lt in ("impact", "冲击"):
|
|
96
|
+
return 1.0, "冲击/弯曲 → 1.0"
|
|
97
|
+
if mode == "axial":
|
|
98
|
+
return 0.85, "轴向 → 0.85"
|
|
99
|
+
if mode == "torsion":
|
|
100
|
+
return 0.59, "扭转 → 0.59"
|
|
101
|
+
return 1.0, "弯曲 → 1.0"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _reliability_factor(reliability: float) -> tuple[float, str]:
|
|
105
|
+
"""可靠度系数 ke(取不超过给定可靠度的档位)。"""
|
|
106
|
+
try:
|
|
107
|
+
r = float(reliability)
|
|
108
|
+
except Exception:
|
|
109
|
+
r = 0.99
|
|
110
|
+
if r <= 0 or r >= 1:
|
|
111
|
+
r = 0.99
|
|
112
|
+
best = 0.50
|
|
113
|
+
for k in sorted(_RELIABILITY_KE):
|
|
114
|
+
if k <= r + 1e-9:
|
|
115
|
+
best = k
|
|
116
|
+
return _RELIABILITY_KE[best], "可靠度 %.4g → ke=%.3f" % (r, _RELIABILITY_KE[best])
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def endurance_limit(mat: dict, surface: str = "machined",
|
|
120
|
+
diameter_mm: Optional[float] = None,
|
|
121
|
+
load_type: str = "static", mode: str = "bending",
|
|
122
|
+
reliability: float = 0.99,
|
|
123
|
+
temperature_c: float = 25.0) -> dict:
|
|
124
|
+
"""计算修正后的疲劳极限 Se(MPa)。
|
|
125
|
+
|
|
126
|
+
钢材:Se' = 0.5·Su(Su <= 1400MPa);Su > 1400 时 Se' = 700MPa
|
|
127
|
+
铝合金:无真实疲劳极限 → 以 5×10^8 次的条件强度作 Se'(保守取 0.4·Su)
|
|
128
|
+
"""
|
|
129
|
+
uts = float(mat.get("uts_mpa") or 0) or float(mat.get("yield_strength_mpa") or 0) * 1.2
|
|
130
|
+
sy = float(mat.get("yield_strength_mpa") or 0)
|
|
131
|
+
defaults = []
|
|
132
|
+
if uts <= 0:
|
|
133
|
+
uts = 400.0
|
|
134
|
+
defaults.append("uts_mpa(默认 400)")
|
|
135
|
+
if sy <= 0:
|
|
136
|
+
sy = 250.0
|
|
137
|
+
defaults.append("yield_strength_mpa(默认 250)")
|
|
138
|
+
|
|
139
|
+
alu = _is_aluminum(mat)
|
|
140
|
+
if alu:
|
|
141
|
+
se_prime = 0.4 * uts # 铝合金保守条件疲劳极限
|
|
142
|
+
basis = "铝合金:Se'=0.4·Su(无真实疲劳极限,按 5e8 次条件极限)"
|
|
143
|
+
# 铝合金对表面/尺寸更敏感,ka 使用更保守下限
|
|
144
|
+
else:
|
|
145
|
+
se_prime = 0.5 * uts if uts <= 1400.0 else 700.0
|
|
146
|
+
basis = "钢材:Se'=0.5·Su(Su<=1400MPa)" if uts <= 1400.0 else "钢材:Su>1400 → Se'=700MPa"
|
|
147
|
+
|
|
148
|
+
ka, ka_note = _surface_factor(surface, uts)
|
|
149
|
+
kb, kb_note = _size_factor(diameter_mm)
|
|
150
|
+
kc, kc_note = _load_factor(load_type, mode)
|
|
151
|
+
ke, ke_note = _reliability_factor(reliability)
|
|
152
|
+
|
|
153
|
+
# 温度系数 kd:钢在 <450°C 取 1.0;铝在 >150°C 明显下降
|
|
154
|
+
try:
|
|
155
|
+
T = float(temperature_c)
|
|
156
|
+
except Exception:
|
|
157
|
+
T = 25.0
|
|
158
|
+
if alu and T > 100.0:
|
|
159
|
+
kd = max(0.5, 1.0 - (T - 100.0) * 0.002)
|
|
160
|
+
kd_note = "铝 %.0f°C → kd=%.3f" % (T, kd)
|
|
161
|
+
elif (not alu) and T > 450.0:
|
|
162
|
+
kd = max(0.5, 1.0 - (T - 450.0) * 0.001)
|
|
163
|
+
kd_note = "钢 %.0f°C → kd=%.3f" % (T, kd)
|
|
164
|
+
else:
|
|
165
|
+
kd, kd_note = 1.0, "常温 → kd=1.0"
|
|
166
|
+
|
|
167
|
+
se = se_prime * ka * kb * kc * kd * ke
|
|
168
|
+
# 疲劳极限不应超过屈服强度(否则静强度先失效)
|
|
169
|
+
capped = False
|
|
170
|
+
if sy > 0 and se > sy:
|
|
171
|
+
se = sy * 0.95
|
|
172
|
+
capped = True
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
"Se_prime_mpa": round(se_prime, 2),
|
|
176
|
+
"Se_mpa": round(se, 2),
|
|
177
|
+
"uts_mpa": round(uts, 1),
|
|
178
|
+
"yield_mpa": round(sy, 1),
|
|
179
|
+
"is_aluminum": alu,
|
|
180
|
+
"factors": {"ka": round(ka, 4), "kb": round(kb, 4), "kc": round(kc, 4),
|
|
181
|
+
"kd": round(kd, 4), "ke": round(ke, 4)},
|
|
182
|
+
"notes": {"basis": basis, "ka": ka_note, "kb": kb_note,
|
|
183
|
+
"kc": kc_note, "kd": kd_note, "ke": ke_note},
|
|
184
|
+
"capped_by_yield": capped,
|
|
185
|
+
"default_used": defaults,
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _sn_life(sigma_a: float, mat: dict, se_info: dict) -> float:
|
|
190
|
+
"""由 Basquin 幂律求给定应力幅下的失效循环数 N。
|
|
191
|
+
|
|
192
|
+
sigma_a = sigma_f' * (2N)^b
|
|
193
|
+
→ N = 0.5 * (sigma_a / sigma_f')^(1/b)
|
|
194
|
+
sigma_a <= Se(且为钢材)→ 视为无限寿命(返回 inf)。
|
|
195
|
+
铝合金无无限寿命平台,即使低于 Se 也按 5e8 次封顶。
|
|
196
|
+
"""
|
|
197
|
+
if sigma_a <= 0:
|
|
198
|
+
return float("inf")
|
|
199
|
+
se = se_info["Se_mpa"]
|
|
200
|
+
uts = se_info["uts_mpa"]
|
|
201
|
+
is_alu = se_info["is_aluminum"]
|
|
202
|
+
# 钢材:应力幅低于疲劳极限 → 无限寿命
|
|
203
|
+
if (not is_alu) and sigma_a <= se:
|
|
204
|
+
return float("inf")
|
|
205
|
+
# 铝合金条件疲劳极限同样视为寿命上限 5e8
|
|
206
|
+
if is_alu and sigma_a <= se:
|
|
207
|
+
return 5e8
|
|
208
|
+
if sigma_a >= uts:
|
|
209
|
+
return 1e3 # 静强度即失效,给最小寿命 1e3 次
|
|
210
|
+
# sigma_f' 估算(Shigley):钢 sigma_f' ≈ Su + 345MPa;铝 ≈ 1.5·Su
|
|
211
|
+
if is_alu:
|
|
212
|
+
sigma_f_prime = 1.5 * uts
|
|
213
|
+
b = -0.12
|
|
214
|
+
else:
|
|
215
|
+
sigma_f_prime = uts + 345.0
|
|
216
|
+
b = -0.085
|
|
217
|
+
try:
|
|
218
|
+
ratio = sigma_a / sigma_f_prime
|
|
219
|
+
if ratio <= 0:
|
|
220
|
+
return float("inf")
|
|
221
|
+
n = 0.5 * (ratio ** (1.0 / b))
|
|
222
|
+
except Exception:
|
|
223
|
+
return 1e3
|
|
224
|
+
# 数值边界:循环数不应小于 1e3、不应大于 1e12
|
|
225
|
+
if n < 1e3:
|
|
226
|
+
return 1e3
|
|
227
|
+
if n > 1e12:
|
|
228
|
+
return 1e12
|
|
229
|
+
# 铝合金封顶 5e8
|
|
230
|
+
if is_alu:
|
|
231
|
+
n = min(n, 5e8)
|
|
232
|
+
return n
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _mean_stress_sf(sigma_a: float, sigma_m: float, se: float, sy: float,
|
|
236
|
+
uts: float, criterion: str = "goodman") -> tuple[float, str]:
|
|
237
|
+
"""平均应力修正后的疲劳安全系数 n。"""
|
|
238
|
+
if sigma_a <= 0:
|
|
239
|
+
# 纯静载:用屈服/强度判据
|
|
240
|
+
if sigma_m <= 0:
|
|
241
|
+
return float("inf"), "无交变应力"
|
|
242
|
+
return (sy / sigma_m if sigma_m > 0 else float("inf")), "纯静载(按屈服)"
|
|
243
|
+
crit = str(criterion or "goodman").lower()
|
|
244
|
+
try:
|
|
245
|
+
if crit == "soderberg":
|
|
246
|
+
if se <= 0 or sy <= 0:
|
|
247
|
+
return 0.0, "参数不足"
|
|
248
|
+
return 1.0 / (sigma_a / se + sigma_m / sy), "Soderberg"
|
|
249
|
+
if crit == "gerber":
|
|
250
|
+
if se <= 0 or uts <= 0:
|
|
251
|
+
return 0.0, "参数不足"
|
|
252
|
+
# n·σa/Se + (n·σm/Su)^2 = 1 → 解一元二次
|
|
253
|
+
A = (sigma_m / uts) ** 2
|
|
254
|
+
B = sigma_a / se
|
|
255
|
+
C = -1.0
|
|
256
|
+
if abs(A) < 1e-12:
|
|
257
|
+
return (1.0 / B if B > 0 else float("inf")), "Gerber(退化)"
|
|
258
|
+
disc = B * B - 4 * A * C
|
|
259
|
+
if disc < 0:
|
|
260
|
+
return 0.0, "Gerber 无实数解"
|
|
261
|
+
n = (-B + disc ** 0.5) / (2 * A)
|
|
262
|
+
return n, "Gerber"
|
|
263
|
+
# 默认 Goodman
|
|
264
|
+
if se <= 0 or uts <= 0:
|
|
265
|
+
return 0.0, "参数不足"
|
|
266
|
+
return 1.0 / (sigma_a / se + sigma_m / uts), "Goodman"
|
|
267
|
+
except Exception:
|
|
268
|
+
return 0.0, "计算异常"
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def analyze_fatigue(
|
|
272
|
+
max_von_mises_mpa: float,
|
|
273
|
+
mat: dict,
|
|
274
|
+
design_life_years: float = 30.0,
|
|
275
|
+
cycles_per_year: float = 200000.0,
|
|
276
|
+
load_type: str = "cyclic",
|
|
277
|
+
impact_factor: float = 1.0,
|
|
278
|
+
surface: str = "machined",
|
|
279
|
+
diameter_mm: Optional[float] = None,
|
|
280
|
+
reliability: float = 0.99,
|
|
281
|
+
temperature_c: float = 25.0,
|
|
282
|
+
criterion: str = "goodman",
|
|
283
|
+
required_life_years: Optional[float] = None,
|
|
284
|
+
apply_impact_to_amplitude: bool = False,
|
|
285
|
+
) -> dict[str, Any]:
|
|
286
|
+
"""疲劳强度 + 设计寿命(默认 30 年)完整校核。
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
max_von_mises_mpa: 静力 FEA 得到的最大 von Mises 应力(额定载荷下)
|
|
290
|
+
mat: 材料字典(需 uts_mpa / yield_strength_mpa)
|
|
291
|
+
design_life_years: 目标设计寿命(年),默认 30
|
|
292
|
+
cycles_per_year: 每年动作次数(载荷循环数)
|
|
293
|
+
load_type: static / cyclic / impact
|
|
294
|
+
impact_factor: 动载系数(用于载荷谱峰值)
|
|
295
|
+
criterion: goodman / soderberg / gerber
|
|
296
|
+
apply_impact_to_amplitude: True 时峰值应力已含动载系数
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
dict —— 见模块 docstring 的结构说明
|
|
300
|
+
"""
|
|
301
|
+
req_years = float(required_life_years if required_life_years is not None
|
|
302
|
+
else (design_life_years or 30.0))
|
|
303
|
+
cpy = float(cycles_per_year or 200000.0)
|
|
304
|
+
if cpy <= 0:
|
|
305
|
+
cpy = 200000.0
|
|
306
|
+
|
|
307
|
+
se_info = endurance_limit(mat, surface=surface, diameter_mm=diameter_mm,
|
|
308
|
+
load_type=load_type, reliability=reliability,
|
|
309
|
+
temperature_c=temperature_c)
|
|
310
|
+
se = se_info["Se_mpa"]
|
|
311
|
+
sy = se_info["yield_mpa"]
|
|
312
|
+
uts = se_info["uts_mpa"]
|
|
313
|
+
|
|
314
|
+
# ── 应力幅 / 平均应力分解 ────────────────────────────────────────────
|
|
315
|
+
# 工程惯例:脉动循环(0→峰值)时 σa = σm = σ_peak/2;
|
|
316
|
+
# 对称循环时 σa = σ_peak、σm = 0。
|
|
317
|
+
# 这里采用【较保守的脉动循环假设】(多数机械往复工况),并在结果中标注。
|
|
318
|
+
sigma_peak = float(max_von_mises_mpa or 0.0)
|
|
319
|
+
if apply_impact_to_amplitude and impact_factor and impact_factor > 1.0:
|
|
320
|
+
sigma_peak *= float(impact_factor)
|
|
321
|
+
sigma_a = sigma_peak / 2.0
|
|
322
|
+
sigma_m = sigma_peak / 2.0
|
|
323
|
+
assumption = ("脉动循环假设: σa=σm=σ_peak/2(0→峰值往复,工程保守取法)"
|
|
324
|
+
if load_type in ("cyclic", "impact", "动态", "循环", "冲击")
|
|
325
|
+
else "静态工况: 以 σ_peak/2 作等效交变幅做保守疲劳评估")
|
|
326
|
+
|
|
327
|
+
sf, crit_note = _mean_stress_sf(sigma_a, sigma_m, se, sy, uts, criterion)
|
|
328
|
+
|
|
329
|
+
# ── 载荷谱与 Miner 累积损伤 ──────────────────────────────────────────
|
|
330
|
+
# 谱型(工程简化,五级):各级应力幅系数 × 循环占比
|
|
331
|
+
SPECTRUM = (
|
|
332
|
+
(1.00, 0.05), # 满负荷 5%
|
|
333
|
+
(0.80, 0.15), # 80% 15%
|
|
334
|
+
(0.60, 0.30), # 60% 30%
|
|
335
|
+
(0.40, 0.30), # 40% 30%
|
|
336
|
+
(0.20, 0.20), # 20% 20%
|
|
337
|
+
)
|
|
338
|
+
levels = []
|
|
339
|
+
damage_per_year = 0.0
|
|
340
|
+
for frac, share in SPECTRUM:
|
|
341
|
+
sa_i = sigma_a * frac
|
|
342
|
+
sm_i = sigma_m * frac
|
|
343
|
+
# 平均应力修正到等效应力幅(Goodman 折算),再做 S-N
|
|
344
|
+
if uts > 0:
|
|
345
|
+
sa_eq = sa_i / max(1.0 - sm_i / uts, 1e-6) if sm_i > 0 else sa_i
|
|
346
|
+
else:
|
|
347
|
+
sa_eq = sa_i
|
|
348
|
+
n_fail = _sn_life(sa_eq, mat, se_info)
|
|
349
|
+
n_i = cpy * share
|
|
350
|
+
if n_fail == float("inf") or n_fail <= 0:
|
|
351
|
+
d_i = 0.0
|
|
352
|
+
else:
|
|
353
|
+
d_i = n_i / n_fail
|
|
354
|
+
damage_per_year += d_i
|
|
355
|
+
levels.append({
|
|
356
|
+
"amplitude_mpa": round(sa_i, 3),
|
|
357
|
+
"mean_mpa": round(sm_i, 3),
|
|
358
|
+
"equivalent_amplitude_mpa": round(sa_eq, 3),
|
|
359
|
+
"cycles_per_year": round(n_i, 1),
|
|
360
|
+
"allowable_cycles": (None if n_fail == float("inf") else round(n_fail, 1)),
|
|
361
|
+
"infinite_life": n_fail == float("inf"),
|
|
362
|
+
"damage_per_year": round(d_i, 10),
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
damage_req = damage_per_year * req_years
|
|
366
|
+
# 可承受年限:D=1 时的年限
|
|
367
|
+
if damage_per_year <= 0:
|
|
368
|
+
life_allowable = float("inf")
|
|
369
|
+
else:
|
|
370
|
+
life_allowable = 1.0 / damage_per_year
|
|
371
|
+
|
|
372
|
+
# ── 判定 ────────────────────────────────────────────────────────────
|
|
373
|
+
min_sf_fatigue = 1.5 # 疲劳安全系数下限(工程惯例 1.3~2.0)
|
|
374
|
+
d_limit = 1.0 # Miner 判据
|
|
375
|
+
sf_ok = sf >= min_sf_fatigue
|
|
376
|
+
life_ok = damage_req < d_limit
|
|
377
|
+
static_ok = (sigma_peak < sy) if sy > 0 else True
|
|
378
|
+
# ── 疲劳强度硬判据:Goodman/Soderberg/Gerber 图上 n<1 = 越过设计线 ──
|
|
379
|
+
# 注意:sf<1 与"寿命够"可以并存(应力幅略高于疲劳极限时仍可能有有限寿命),
|
|
380
|
+
# 但工程上 n<1 表示疲劳强度不足,**不得判 PASS**(宁可保守,杜绝虚高)。
|
|
381
|
+
sf_hard_fail = sf < 1.0
|
|
382
|
+
|
|
383
|
+
if not static_ok:
|
|
384
|
+
verdict = "FAIL"
|
|
385
|
+
reason = "静强度已不足:σ_peak=%.2fMPa ≥ σy=%.1fMPa" % (sigma_peak, sy)
|
|
386
|
+
elif not life_ok:
|
|
387
|
+
verdict = "FAIL"
|
|
388
|
+
reason = ("疲劳寿命不足:%d 年累积损伤 D=%.4g ≥ 1,可承受约 %.1f 年"
|
|
389
|
+
% (int(req_years), damage_req, life_allowable))
|
|
390
|
+
elif sf_hard_fail:
|
|
391
|
+
verdict = "FAIL"
|
|
392
|
+
reason = ("疲劳强度不足:安全系数 %.2f < 1.0(%s 判据下已越过设计线);"
|
|
393
|
+
"尽管 Miner 寿命 %.0f 年尚可,仍不得判为合格"
|
|
394
|
+
% (sf, crit_note, life_allowable if life_allowable != float("inf") else 0))
|
|
395
|
+
elif not sf_ok:
|
|
396
|
+
verdict = "REVIEW"
|
|
397
|
+
reason = ("疲劳安全系数 %.2f < %.1f(寿命尚可,但裕度偏低,建议加强/换材)"
|
|
398
|
+
% (sf, min_sf_fatigue))
|
|
399
|
+
else:
|
|
400
|
+
verdict = "PASS"
|
|
401
|
+
reason = ("疲劳安全系数 %.2f ≥ %.1f 且 %d 年累积损伤 D=%.4g < 1"
|
|
402
|
+
% (sf, min_sf_fatigue, int(req_years), damage_req))
|
|
403
|
+
|
|
404
|
+
gates = {
|
|
405
|
+
"FATIGUE_STRENGTH": {
|
|
406
|
+
"status": ("PASS" if sf_ok else "FAIL"),
|
|
407
|
+
"actual_sf": round(sf, 3),
|
|
408
|
+
"required_min": min_sf_fatigue,
|
|
409
|
+
"endurance_limit_mpa": round(se, 2),
|
|
410
|
+
"criterion": crit_note,
|
|
411
|
+
"below_unity": sf_hard_fail,
|
|
412
|
+
},
|
|
413
|
+
"FATIGUE_LIFE": {
|
|
414
|
+
"status": ("PASS" if life_ok else "FAIL"),
|
|
415
|
+
"required_years": req_years,
|
|
416
|
+
"damage_at_required_life": round(damage_req, 6),
|
|
417
|
+
"damage_limit": d_limit,
|
|
418
|
+
"allowable_life_years": (None if life_allowable == float("inf")
|
|
419
|
+
else round(life_allowable, 2)),
|
|
420
|
+
},
|
|
421
|
+
"STATIC_STRENGTH": {
|
|
422
|
+
"status": ("PASS" if static_ok else "FAIL"),
|
|
423
|
+
"peak_stress_mpa": round(sigma_peak, 3),
|
|
424
|
+
"yield_mpa": round(sy, 1),
|
|
425
|
+
},
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return {
|
|
429
|
+
"ok": verdict != "FAIL",
|
|
430
|
+
"method": "fatigue_sn_miner",
|
|
431
|
+
"backend": "basquin_sn_marin_miner",
|
|
432
|
+
"verdict": verdict,
|
|
433
|
+
"reason": reason,
|
|
434
|
+
"design_life_years": req_years,
|
|
435
|
+
"cycles_per_year": cpy,
|
|
436
|
+
"total_cycles_design_life": round(cpy * req_years, 1),
|
|
437
|
+
"endurance_limit_mpa": round(se, 2),
|
|
438
|
+
"fatigue_sf": round(sf, 3),
|
|
439
|
+
"min_required_fatigue_sf": min_sf_fatigue,
|
|
440
|
+
"damage_design_life": round(damage_req, 6),
|
|
441
|
+
"life_years_allowable": (None if life_allowable == float("inf")
|
|
442
|
+
else round(life_allowable, 2)),
|
|
443
|
+
"stress_amplitude_mpa": round(sigma_a, 3),
|
|
444
|
+
"mean_stress_mpa": round(sigma_m, 3),
|
|
445
|
+
"peak_stress_mpa": round(sigma_peak, 3),
|
|
446
|
+
"criterion": criterion,
|
|
447
|
+
"endurance_detail": se_info,
|
|
448
|
+
"load_spectrum": levels,
|
|
449
|
+
"assumption": assumption,
|
|
450
|
+
"gates": gates,
|
|
451
|
+
"limitations": [
|
|
452
|
+
"S-N 曲线由 UTS 经验估算(非实测材料数据)",
|
|
453
|
+
"Miner 线性累积损伤忽略载荷顺序与过载迟滞效应",
|
|
454
|
+
"未计入焊接接头/缺口应力集中系数 Kt(保守起见请另乘 Kt)",
|
|
455
|
+
"脉动循环假设(σa=σm=σpeak/2);对称循环工况偏保守",
|
|
456
|
+
"尺寸/表面/可靠度系数取自 Shigley 经验表",
|
|
457
|
+
],
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def analyze_fatigue_from_case(load_case: dict, fea_result: dict) -> dict:
|
|
462
|
+
"""从载荷工况 + FEA 结果直接做疲劳校核(供 fea_solver 统一调用)。
|
|
463
|
+
|
|
464
|
+
读取:
|
|
465
|
+
load_case.acceptance.design_life_years 设计寿命(默认 30)
|
|
466
|
+
load_case.acceptance.operating_cycles_per_year 年动作次数
|
|
467
|
+
load_case.acceptance.load_type 工况性质
|
|
468
|
+
load_case.acceptance.impact_factor 动载系数
|
|
469
|
+
"""
|
|
470
|
+
mat = load_case.get("material", {}) or {}
|
|
471
|
+
acc = load_case.get("acceptance", {}) or {}
|
|
472
|
+
stress = (fea_result.get("max_von_mises_mpa")
|
|
473
|
+
or fea_result.get("max_stress_mpa") or 0.0)
|
|
474
|
+
if not stress:
|
|
475
|
+
return {"ok": False, "error": "缺少静力应力结果,无法做疲劳校核",
|
|
476
|
+
"verdict": "NOT_EVALUATED"}
|
|
477
|
+
return analyze_fatigue(
|
|
478
|
+
max_von_mises_mpa=stress,
|
|
479
|
+
mat=mat,
|
|
480
|
+
design_life_years=acc.get("design_life_years", 30.0),
|
|
481
|
+
cycles_per_year=acc.get("operating_cycles_per_year", 200000.0),
|
|
482
|
+
load_type=acc.get("load_type", "cyclic"),
|
|
483
|
+
impact_factor=acc.get("impact_factor", 1.0),
|
|
484
|
+
surface=acc.get("surface_finish", "machined"),
|
|
485
|
+
diameter_mm=acc.get("characteristic_diameter_mm"),
|
|
486
|
+
reliability=acc.get("reliability", 0.99),
|
|
487
|
+
temperature_c=acc.get("service_temperature_c", 25.0),
|
|
488
|
+
criterion=acc.get("fatigue_criterion", "goodman"),
|
|
489
|
+
)
|