morphopt 3.1.1__tar.gz
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.
- morphopt-3.1.1/PKG-INFO +242 -0
- morphopt-3.1.1/README.md +220 -0
- morphopt-3.1.1/pyproject.toml +23 -0
- morphopt-3.1.1/setup.cfg +4 -0
- morphopt-3.1.1/src/morphopt/__init__.py +32 -0
- morphopt-3.1.1/src/morphopt/opt_runner.py +130 -0
- morphopt-3.1.1/src/morphopt/optcore/baseobject.py +48 -0
- morphopt-3.1.1/src/morphopt/optcore/controller.py +404 -0
- morphopt-3.1.1/src/morphopt/optcore/history.py +440 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/__init__.py +4 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/base_params.py +97 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/feamodel/__init__.py +0 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/feamodel/feainterface/__init__.py +10 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/feamodel/feainterface/basefeainterface.py +50 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/feamodel/feainterface/bodyforceinterface.py +71 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/feamodel/feainterface/boundaryconditioninterface.py +59 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/feamodel/feainterface/contactinterface.py +113 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/feamodel/feainterface/coupleinterface.py +31 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/feamodel/feainterface/pointinterface.py +79 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/feamodel/feainterface/pressureinterface.py +68 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/feamodel/feainterface/referencepointinterface.py +29 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/feamodel/feainterface/springinterface.py +104 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/feamodel/feaparams.py +264 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/geometry/__init__.py +3 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/geometry/geometryinterfaces/__init__.py +1 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/geometry/geometryinterfaces/basesurfaceinterface.py +763 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/geometry/geometryinterfaces/bspsurfaceinterface.py +723 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/geometry/geometryinterfaces/cpgeosurfaceinterface.py +374 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/geometry/geometryparams.py +715 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/materials/__init__.py +1 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/materials/materialparams.py +110 -0
- morphopt-3.1.1/src/morphopt/optcore/modelparams/params.py +88 -0
- morphopt-3.1.1/src/morphopt/optcore/objfunc.py +248 -0
- morphopt-3.1.1/src/morphopt/optcore/solver.py +159 -0
- morphopt-3.1.1/src/morphopt/optcore/updaters/__init__.py +2 -0
- morphopt-3.1.1/src/morphopt/optcore/updaters/base_updater.py +110 -0
- morphopt-3.1.1/src/morphopt/optcore/updaters/geometry/__init__.py +2 -0
- morphopt-3.1.1/src/morphopt/optcore/updaters/geometry/objectivefuncs/__init__.py +5 -0
- morphopt-3.1.1/src/morphopt/optcore/updaters/geometry/objectivefuncs/basefuncs.py +117 -0
- morphopt-3.1.1/src/morphopt/optcore/updaters/geometry/objectivefuncs/boundarys.py +91 -0
- morphopt-3.1.1/src/morphopt/optcore/updaters/geometry/objectivefuncs/distancesurface.py +123 -0
- morphopt-3.1.1/src/morphopt/optcore/updaters/geometry/objectivefuncs/shapederivative.py +179 -0
- morphopt-3.1.1/src/morphopt/optcore/updaters/geometry/objectivefuncs/surfacefairness.py +40 -0
- morphopt-3.1.1/src/morphopt/optcore/updaters/geometry/update_geometry.py +347 -0
- morphopt-3.1.1/src/morphopt/optcore/updaters/optimizer.py +177 -0
- morphopt-3.1.1/src/morphopt/optcore/updaters/updaters.py +82 -0
- morphopt-3.1.1/src/morphopt/optcore/utils/plot_history_surface.py +239 -0
- morphopt-3.1.1/src/morphopt/taskoptmization.py +75 -0
- morphopt-3.1.1/src/morphopt/taskui.py +458 -0
- morphopt-3.1.1/src/morphopt.egg-info/PKG-INFO +242 -0
- morphopt-3.1.1/src/morphopt.egg-info/SOURCES.txt +52 -0
- morphopt-3.1.1/src/morphopt.egg-info/dependency_links.txt +1 -0
- morphopt-3.1.1/src/morphopt.egg-info/requires.txt +15 -0
- morphopt-3.1.1/src/morphopt.egg-info/top_level.txt +1 -0
morphopt-3.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: morphopt
|
|
3
|
+
Version: 3.1.1
|
|
4
|
+
Summary: A morphing optimization framework using PyTorch and Abaqus.
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: torch>=2.0.0
|
|
8
|
+
Requires-Dist: torchvision>=0.15.1
|
|
9
|
+
Requires-Dist: numpy>=2.0.0
|
|
10
|
+
Requires-Dist: pypardiso>=0.4.7
|
|
11
|
+
Requires-Dist: pyqt6>=6.10.2
|
|
12
|
+
Requires-Dist: pyvista>=0.46.4
|
|
13
|
+
Requires-Dist: pyvistaqt>=0.11.3
|
|
14
|
+
Requires-Dist: vtk>=9.5.2
|
|
15
|
+
Requires-Dist: gmsh>=4.15.0
|
|
16
|
+
Requires-Dist: tabulate>=0.9.0
|
|
17
|
+
Requires-Dist: cpgeo>=1.0.2
|
|
18
|
+
Requires-Dist: imageio>=2.37.2
|
|
19
|
+
Requires-Dist: bspmap>=1.0.0
|
|
20
|
+
Requires-Dist: torchfea>=1.0.4
|
|
21
|
+
Requires-Dist: trimesh>=4.11.1
|
|
22
|
+
|
|
23
|
+
# 项目说明与使用指南
|
|
24
|
+
|
|
25
|
+
本仓库提供基于形状优化的工作流,集成了表面参数化、有限元(FEA)求解、载荷管理与目标函数组合。你可以复用示例快速搭建新任务,或扩展接口以支持更多场景。
|
|
26
|
+
|
|
27
|
+
目录:
|
|
28
|
+
- 安装与环境
|
|
29
|
+
- 项目结构
|
|
30
|
+
- 快速开始(运行示例)
|
|
31
|
+
- 如何定义一个优化问题(标准化工作流)
|
|
32
|
+
- 目标与更新器(Objective/Updater)
|
|
33
|
+
- 载荷接口一览(Pressure/Contact/Point)
|
|
34
|
+
- 日志与历史(History)
|
|
35
|
+
- GPU 与精度设置
|
|
36
|
+
- 性能建议
|
|
37
|
+
- 故障排查(FAQ)
|
|
38
|
+
- 贡献与许可证
|
|
39
|
+
|
|
40
|
+
## 安装与环境
|
|
41
|
+
|
|
42
|
+
前置条件:
|
|
43
|
+
- Python 3.10+(建议使用 conda 环境)
|
|
44
|
+
- Windows(已在 Windows+PowerShell 下验证),Linux 也可按需适配
|
|
45
|
+
|
|
46
|
+
安装依赖:
|
|
47
|
+
```powershell
|
|
48
|
+
# 可选:创建虚拟环境
|
|
49
|
+
conda create -n MorphOpt python=3.10 -y; conda activate MorphOpt
|
|
50
|
+
|
|
51
|
+
# 安装 Python 依赖
|
|
52
|
+
pip install -r requirements.txt
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
若使用 GPU,请确保已安装匹配的 PyTorch CUDA 版本。
|
|
56
|
+
|
|
57
|
+
## 项目结构
|
|
58
|
+
|
|
59
|
+
参考主要目录:
|
|
60
|
+
- `Jobs/`:示例与具体任务脚本(建议以 `Jobs/examples/displacement.py` 为模板)。
|
|
61
|
+
- `MorphOpt/`:核心库,包括参数、接口、求解器、更新器等。
|
|
62
|
+
- `Tests/`:简单的可视化/单元测试脚本。
|
|
63
|
+
|
|
64
|
+
你可以在工作区面板查看完整结构。
|
|
65
|
+
|
|
66
|
+
## 快速开始(运行示例)
|
|
67
|
+
|
|
68
|
+
运行位移示例(displacement):
|
|
69
|
+
```powershell
|
|
70
|
+
python Jobs/examples/displacement.py
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
运行其他示例(如接触/抓取/运动学等),进入对应子目录执行相应脚本。
|
|
74
|
+
|
|
75
|
+
## 如何定义一个优化问题(标准化工作流)
|
|
76
|
+
|
|
77
|
+
下面介绍如何在本仓库中定义一个新的优化任务。请参考并对照示例脚本 `Jobs/examples/displacement.py`。
|
|
78
|
+
|
|
79
|
+
### 总体结构
|
|
80
|
+
|
|
81
|
+
每个任务脚本遵循统一结构:
|
|
82
|
+
|
|
83
|
+
1) 定义目标函数 ObjectiveFunction
|
|
84
|
+
- 继承自 `GLOBAL.ObjectiveFunction`,实现 `get_objective(self)`,并设置 `GLOBAL.obj_fun = ObjectiveFunction()`。
|
|
85
|
+
|
|
86
|
+
2) 定义参数容器 Params(_Params)
|
|
87
|
+
- 内含三个子类:
|
|
88
|
+
- `SurfaceParams(_SurfacesParams)`:构建可优化的几何曲面(使用 `self.BSP.*` 初始化几何)。
|
|
89
|
+
- `LoadParams(_LoadsParams)`:定义“所有需要的载荷接口”和“每个加载步的幅值参数”。
|
|
90
|
+
- `MaterialParams(_Materials)`:设置材料参数(如 `mu`, `kappa`, `density`)。
|
|
91
|
+
|
|
92
|
+
3) 定义 Generator、Solver、Updater 与 Controller
|
|
93
|
+
- `Generator(_Generator)`:生成网格/中间数据(可设置 `seed_size`, `mesh_order` 等)。
|
|
94
|
+
- `Solver(_MorphSolver)`:负责调用 FEA;内部会一次性将所有载荷添加到 FEA 中,并在每个加载步切换载荷“幅值”,无需重复初始化模型。
|
|
95
|
+
- `Updater(_Updaters)`:包含 `UpdaterSurfaces(_UpdaterSurfaces)`,在其中添加/组合优化目标项(如形状导数、平滑、边界约束等)。
|
|
96
|
+
- `Controller(_Controller)`:组织整个优化循环。
|
|
97
|
+
|
|
98
|
+
4) 在 `__main__` 中初始化路径与历史、实例化各组件并执行 `controller.opt_loop()`。
|
|
99
|
+
|
|
100
|
+
### 曲面定义 SurfaceParams
|
|
101
|
+
|
|
102
|
+
在 `SurfaceParams` 中:
|
|
103
|
+
- 使用 `self.BSP.*` 工具函数构建一个或多个初始曲面,并通过 `self.add_surface(...)` 注册。
|
|
104
|
+
- 通过 `self.if_update = [...]` 指定哪些曲面参与更新。
|
|
105
|
+
|
|
106
|
+
示例(略化):
|
|
107
|
+
```python
|
|
108
|
+
class SurfaceParams(_SurfacesParams):
|
|
109
|
+
def __init__(self):
|
|
110
|
+
super().__init__(max_step_length=[0.4, 0.4])
|
|
111
|
+
self.add_surface(self.BSP.initialize_cylinder(r0=8., length=80., seed_size=1.0,
|
|
112
|
+
symmetric=[1, [1]], flip=False,
|
|
113
|
+
maxR=0.1, maxC=1.0, maxFF=0.2, perturbation_L=12.))
|
|
114
|
+
self.add_surface(self.BSP.initialize_cylinder(r0=4., length=74., seed_size=1.0,
|
|
115
|
+
symmetric=[1, [1]], init_location=[0, 0, 3],
|
|
116
|
+
flip=True, maxR=0.1, maxC=1.0, maxFF=0.2, perturbation_L=12.))
|
|
117
|
+
self.if_update = [True, True]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 载荷定义 LoadParams(关键:一次定义,分步调幅)
|
|
121
|
+
|
|
122
|
+
新的载荷定义方式遵循两个阶段:
|
|
123
|
+
- 阶段 A:注册“载荷接口”(仅定义类型/关联对象,不写死幅值)。
|
|
124
|
+
- 阶段 B:设置“步数”和“每个步的幅值”。求解时,FEA 只创建一次载荷对象,各步仅切换幅值,避免频繁重建/初始化。
|
|
125
|
+
|
|
126
|
+
LoadParams 提供以下方法:
|
|
127
|
+
- `add_load_interface(load_interface, name: str | None) -> str`
|
|
128
|
+
- 注册一个载荷接口,返回其唯一名称(若未提供 name 会自动生成)。
|
|
129
|
+
- `set_step_num(num_steps: int)`
|
|
130
|
+
- 设置加载步数量,并初始化各步的参数字典。
|
|
131
|
+
- `set_step_params(step_index: int, load_name: str, values: list[float])`
|
|
132
|
+
- 为某个加载步的某个载荷设置幅值(如压力标量,力/力矩三分量等)。
|
|
133
|
+
|
|
134
|
+
常用载荷接口(均在 `MorphOpt/modelparams/loads/LoadInterface/` 下):
|
|
135
|
+
- `PressureInterface(instance_name='final_model', surface_name='...')`
|
|
136
|
+
- 幅值参数:`[pressure]`(单个浮点数)。
|
|
137
|
+
- `ConcentratedForceInterface(rp_name: str)`
|
|
138
|
+
- 幅值参数:`[Fx, Fy, Fz]`(三个浮点数)。
|
|
139
|
+
- 注意:类成员变量只存 Python list[float],不使用 torch;仅在参数导出时转为张量。
|
|
140
|
+
- `ConcentratedMomentInterface(rp_name: str)`
|
|
141
|
+
- 幅值参数:`[Mx, My, Mz]`。
|
|
142
|
+
- `ContactInterface(instance_name1, surface_name1, instance_name2, surface_name2, ...)`
|
|
143
|
+
- 接触约束,一般不需要幅值;只需注册接口即可。
|
|
144
|
+
- `ContactSelfInterface(instance_name, surface_name, ...)`
|
|
145
|
+
- 自接触约束,同样通常无幅值参数。
|
|
146
|
+
|
|
147
|
+
示例(等价于 `Jobs/examples/displacement.py` 的写法):
|
|
148
|
+
```python
|
|
149
|
+
class LoadParams(_LoadsParams):
|
|
150
|
+
def __init__(self):
|
|
151
|
+
super().__init__()
|
|
152
|
+
# A. 注册载荷接口(只定义对象,不写死幅值)
|
|
153
|
+
self.add_load_interface(
|
|
154
|
+
self.PressureInterface(instance_name='final_model', surface_name='surface_1_All'),
|
|
155
|
+
name='pressure_1'
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# B. 定义步数与每步幅值
|
|
159
|
+
self.set_step_num(1)
|
|
160
|
+
self.set_step_params(0, 'pressure_1', [0.06])
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Solver 会在 solve 时:
|
|
164
|
+
- 一次性用 `get_loads_fea()` 将全部载荷添加到 FEA。
|
|
165
|
+
- 每个步调用 `process_fea(fea, step_index)` 应用当前步的幅值;模型不需要每步重建、仅切换数值,效率更高。
|
|
166
|
+
|
|
167
|
+
### 材料参数 MaterialParams
|
|
168
|
+
|
|
169
|
+
示例:
|
|
170
|
+
```python
|
|
171
|
+
class MaterialParams(_Materials):
|
|
172
|
+
def __init__(self):
|
|
173
|
+
super().__init__(mu=0.482, kappa=4.8, density=1.08e-9)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 生成器与求解器
|
|
177
|
+
|
|
178
|
+
- `Generator(_Generator)`:根据曲面生成中间数据;常见参数有 `seed_size`、`mesh_order` 等。
|
|
179
|
+
- `Solver(_MorphSolver)`:无需手动管理每步载荷添加/删除;内部已经采用“预定义载荷 + 分步调幅”的机制。
|
|
180
|
+
|
|
181
|
+
## 目标与更新器(Objective/Updater)
|
|
182
|
+
|
|
183
|
+
在 `Updater(_Updaters).UpdaterSurfaces(_UpdaterSurfaces)` 中:
|
|
184
|
+
- 创建形状导数(如 `ShapeDerivativeDirect(reset_per_iter=5)`)并注册:
|
|
185
|
+
- `self.add_objective_function(shape_derivative)`
|
|
186
|
+
- 可叠加其它目标/正则(如 `Fairness`, `Distance`, `boundarys.Cylinder` 等)。
|
|
187
|
+
|
|
188
|
+
常见用法:
|
|
189
|
+
- 目标函数统一通过 `self.add_objective_function(...)` 注册。
|
|
190
|
+
- 多目标时,可通过权重在对应目标构造参数中体现。
|
|
191
|
+
|
|
192
|
+
## 载荷接口一览(Pressure/Contact/Point)
|
|
193
|
+
|
|
194
|
+
位置:`MorphOpt/modelparams/loads/LoadInterface/`
|
|
195
|
+
|
|
196
|
+
- PressureInterface:面压力,单一标量幅值。
|
|
197
|
+
- ContactInterface:外部接触;一般无幅值。
|
|
198
|
+
- ContactSelfInterface:自接触;一般无幅值。
|
|
199
|
+
- ConcentratedForceInterface:集中力,基于参考点名 `rp_name`,幅值为 `[Fx,Fy,Fz]`。
|
|
200
|
+
- ConcentratedMomentInterface:集中力矩,`rp_name`,幅值为 `[Mx,My,Mz]`。
|
|
201
|
+
- SpringToGroundInterface:弹簧(RP-地面点),参数顺序为 `[k, L0, Px, Py, Pz]`。
|
|
202
|
+
- SpringBetweenRPsInterface:弹簧(RP-RP),参数顺序为 `[k, L0]`。
|
|
203
|
+
|
|
204
|
+
注意:接口类内部仅存 Python list[float];参数导出阶段再转为张量,便于优化器统一打包。
|
|
205
|
+
|
|
206
|
+
## 日志与历史(History)
|
|
207
|
+
|
|
208
|
+
- `initializer.initialize_path(...)` 会创建输出目录结构(如 `Results/`)。
|
|
209
|
+
- `initializer.initialize_history()` 管理迭代历史(`GLOBAL.History`)。
|
|
210
|
+
- 你可以在 `Jobs/...` 的脚本中设置任务名与输出路径,以便归档每次试验结果。
|
|
211
|
+
|
|
212
|
+
## GPU 与精度设置
|
|
213
|
+
|
|
214
|
+
- 默认使用 `torch.float64`;可在入口脚本中通过 `torch.set_default_dtype` 调整。
|
|
215
|
+
- 若可用 GPU,则内部会选择合适设备;也可在脚本中手动设置或屏蔽 CUDA(调试时可启用 `CUDA_LAUNCH_BLOCKING=1`)。
|
|
216
|
+
|
|
217
|
+
## 性能建议
|
|
218
|
+
|
|
219
|
+
- 预先“注册所有载荷”,并仅在步间切换幅值(已在 `LoadParams`/`Solver` 内实现)。
|
|
220
|
+
- 合理设置网格密度与曲面参数,避免过大规模导致 FEA 迭代缓慢。
|
|
221
|
+
- 多步求解时,确保每步仅修改必要的幅值,减少 FE 内部对象的重复构建。
|
|
222
|
+
|
|
223
|
+
## 故障排查(FAQ)
|
|
224
|
+
|
|
225
|
+
- 载荷无效/不生效?
|
|
226
|
+
- 确认已通过 `add_load_interface` 注册;对有幅值的载荷在每个步都正确调用了 `set_step_params`。
|
|
227
|
+
- 接触相关错误?
|
|
228
|
+
- 检查实例名与表面名是否与 INP/几何一致;必要时在 `Solver.init_FEA` 中自定义接触参数。
|
|
229
|
+
- GPU/精度导致的数值不稳定?
|
|
230
|
+
- 可改回 CPU 或降低步长;确保所有张量 dtype 一致为 float64。
|
|
231
|
+
|
|
232
|
+
## 贡献与许可证
|
|
233
|
+
|
|
234
|
+
- 欢迎通过 PR/Issue 贡献新的载荷接口、目标函数或示例任务。
|
|
235
|
+
- 许可证与版权信息请参考仓库根目录中的相关文件(若缺失,请在提交 PR 时补充)。
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
更多参考:
|
|
240
|
+
- 示例:`Jobs/examples/displacement.py`
|
|
241
|
+
- 接触/集中载荷用法:`Jobs/locomotion/front.py`、`Jobs/ral2025contact/*`
|
|
242
|
+
- 载荷接口实现:`MorphOpt/modelparams/loads/LoadInterface/`
|
morphopt-3.1.1/README.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# 项目说明与使用指南
|
|
2
|
+
|
|
3
|
+
本仓库提供基于形状优化的工作流,集成了表面参数化、有限元(FEA)求解、载荷管理与目标函数组合。你可以复用示例快速搭建新任务,或扩展接口以支持更多场景。
|
|
4
|
+
|
|
5
|
+
目录:
|
|
6
|
+
- 安装与环境
|
|
7
|
+
- 项目结构
|
|
8
|
+
- 快速开始(运行示例)
|
|
9
|
+
- 如何定义一个优化问题(标准化工作流)
|
|
10
|
+
- 目标与更新器(Objective/Updater)
|
|
11
|
+
- 载荷接口一览(Pressure/Contact/Point)
|
|
12
|
+
- 日志与历史(History)
|
|
13
|
+
- GPU 与精度设置
|
|
14
|
+
- 性能建议
|
|
15
|
+
- 故障排查(FAQ)
|
|
16
|
+
- 贡献与许可证
|
|
17
|
+
|
|
18
|
+
## 安装与环境
|
|
19
|
+
|
|
20
|
+
前置条件:
|
|
21
|
+
- Python 3.10+(建议使用 conda 环境)
|
|
22
|
+
- Windows(已在 Windows+PowerShell 下验证),Linux 也可按需适配
|
|
23
|
+
|
|
24
|
+
安装依赖:
|
|
25
|
+
```powershell
|
|
26
|
+
# 可选:创建虚拟环境
|
|
27
|
+
conda create -n MorphOpt python=3.10 -y; conda activate MorphOpt
|
|
28
|
+
|
|
29
|
+
# 安装 Python 依赖
|
|
30
|
+
pip install -r requirements.txt
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
若使用 GPU,请确保已安装匹配的 PyTorch CUDA 版本。
|
|
34
|
+
|
|
35
|
+
## 项目结构
|
|
36
|
+
|
|
37
|
+
参考主要目录:
|
|
38
|
+
- `Jobs/`:示例与具体任务脚本(建议以 `Jobs/examples/displacement.py` 为模板)。
|
|
39
|
+
- `MorphOpt/`:核心库,包括参数、接口、求解器、更新器等。
|
|
40
|
+
- `Tests/`:简单的可视化/单元测试脚本。
|
|
41
|
+
|
|
42
|
+
你可以在工作区面板查看完整结构。
|
|
43
|
+
|
|
44
|
+
## 快速开始(运行示例)
|
|
45
|
+
|
|
46
|
+
运行位移示例(displacement):
|
|
47
|
+
```powershell
|
|
48
|
+
python Jobs/examples/displacement.py
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
运行其他示例(如接触/抓取/运动学等),进入对应子目录执行相应脚本。
|
|
52
|
+
|
|
53
|
+
## 如何定义一个优化问题(标准化工作流)
|
|
54
|
+
|
|
55
|
+
下面介绍如何在本仓库中定义一个新的优化任务。请参考并对照示例脚本 `Jobs/examples/displacement.py`。
|
|
56
|
+
|
|
57
|
+
### 总体结构
|
|
58
|
+
|
|
59
|
+
每个任务脚本遵循统一结构:
|
|
60
|
+
|
|
61
|
+
1) 定义目标函数 ObjectiveFunction
|
|
62
|
+
- 继承自 `GLOBAL.ObjectiveFunction`,实现 `get_objective(self)`,并设置 `GLOBAL.obj_fun = ObjectiveFunction()`。
|
|
63
|
+
|
|
64
|
+
2) 定义参数容器 Params(_Params)
|
|
65
|
+
- 内含三个子类:
|
|
66
|
+
- `SurfaceParams(_SurfacesParams)`:构建可优化的几何曲面(使用 `self.BSP.*` 初始化几何)。
|
|
67
|
+
- `LoadParams(_LoadsParams)`:定义“所有需要的载荷接口”和“每个加载步的幅值参数”。
|
|
68
|
+
- `MaterialParams(_Materials)`:设置材料参数(如 `mu`, `kappa`, `density`)。
|
|
69
|
+
|
|
70
|
+
3) 定义 Generator、Solver、Updater 与 Controller
|
|
71
|
+
- `Generator(_Generator)`:生成网格/中间数据(可设置 `seed_size`, `mesh_order` 等)。
|
|
72
|
+
- `Solver(_MorphSolver)`:负责调用 FEA;内部会一次性将所有载荷添加到 FEA 中,并在每个加载步切换载荷“幅值”,无需重复初始化模型。
|
|
73
|
+
- `Updater(_Updaters)`:包含 `UpdaterSurfaces(_UpdaterSurfaces)`,在其中添加/组合优化目标项(如形状导数、平滑、边界约束等)。
|
|
74
|
+
- `Controller(_Controller)`:组织整个优化循环。
|
|
75
|
+
|
|
76
|
+
4) 在 `__main__` 中初始化路径与历史、实例化各组件并执行 `controller.opt_loop()`。
|
|
77
|
+
|
|
78
|
+
### 曲面定义 SurfaceParams
|
|
79
|
+
|
|
80
|
+
在 `SurfaceParams` 中:
|
|
81
|
+
- 使用 `self.BSP.*` 工具函数构建一个或多个初始曲面,并通过 `self.add_surface(...)` 注册。
|
|
82
|
+
- 通过 `self.if_update = [...]` 指定哪些曲面参与更新。
|
|
83
|
+
|
|
84
|
+
示例(略化):
|
|
85
|
+
```python
|
|
86
|
+
class SurfaceParams(_SurfacesParams):
|
|
87
|
+
def __init__(self):
|
|
88
|
+
super().__init__(max_step_length=[0.4, 0.4])
|
|
89
|
+
self.add_surface(self.BSP.initialize_cylinder(r0=8., length=80., seed_size=1.0,
|
|
90
|
+
symmetric=[1, [1]], flip=False,
|
|
91
|
+
maxR=0.1, maxC=1.0, maxFF=0.2, perturbation_L=12.))
|
|
92
|
+
self.add_surface(self.BSP.initialize_cylinder(r0=4., length=74., seed_size=1.0,
|
|
93
|
+
symmetric=[1, [1]], init_location=[0, 0, 3],
|
|
94
|
+
flip=True, maxR=0.1, maxC=1.0, maxFF=0.2, perturbation_L=12.))
|
|
95
|
+
self.if_update = [True, True]
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 载荷定义 LoadParams(关键:一次定义,分步调幅)
|
|
99
|
+
|
|
100
|
+
新的载荷定义方式遵循两个阶段:
|
|
101
|
+
- 阶段 A:注册“载荷接口”(仅定义类型/关联对象,不写死幅值)。
|
|
102
|
+
- 阶段 B:设置“步数”和“每个步的幅值”。求解时,FEA 只创建一次载荷对象,各步仅切换幅值,避免频繁重建/初始化。
|
|
103
|
+
|
|
104
|
+
LoadParams 提供以下方法:
|
|
105
|
+
- `add_load_interface(load_interface, name: str | None) -> str`
|
|
106
|
+
- 注册一个载荷接口,返回其唯一名称(若未提供 name 会自动生成)。
|
|
107
|
+
- `set_step_num(num_steps: int)`
|
|
108
|
+
- 设置加载步数量,并初始化各步的参数字典。
|
|
109
|
+
- `set_step_params(step_index: int, load_name: str, values: list[float])`
|
|
110
|
+
- 为某个加载步的某个载荷设置幅值(如压力标量,力/力矩三分量等)。
|
|
111
|
+
|
|
112
|
+
常用载荷接口(均在 `MorphOpt/modelparams/loads/LoadInterface/` 下):
|
|
113
|
+
- `PressureInterface(instance_name='final_model', surface_name='...')`
|
|
114
|
+
- 幅值参数:`[pressure]`(单个浮点数)。
|
|
115
|
+
- `ConcentratedForceInterface(rp_name: str)`
|
|
116
|
+
- 幅值参数:`[Fx, Fy, Fz]`(三个浮点数)。
|
|
117
|
+
- 注意:类成员变量只存 Python list[float],不使用 torch;仅在参数导出时转为张量。
|
|
118
|
+
- `ConcentratedMomentInterface(rp_name: str)`
|
|
119
|
+
- 幅值参数:`[Mx, My, Mz]`。
|
|
120
|
+
- `ContactInterface(instance_name1, surface_name1, instance_name2, surface_name2, ...)`
|
|
121
|
+
- 接触约束,一般不需要幅值;只需注册接口即可。
|
|
122
|
+
- `ContactSelfInterface(instance_name, surface_name, ...)`
|
|
123
|
+
- 自接触约束,同样通常无幅值参数。
|
|
124
|
+
|
|
125
|
+
示例(等价于 `Jobs/examples/displacement.py` 的写法):
|
|
126
|
+
```python
|
|
127
|
+
class LoadParams(_LoadsParams):
|
|
128
|
+
def __init__(self):
|
|
129
|
+
super().__init__()
|
|
130
|
+
# A. 注册载荷接口(只定义对象,不写死幅值)
|
|
131
|
+
self.add_load_interface(
|
|
132
|
+
self.PressureInterface(instance_name='final_model', surface_name='surface_1_All'),
|
|
133
|
+
name='pressure_1'
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# B. 定义步数与每步幅值
|
|
137
|
+
self.set_step_num(1)
|
|
138
|
+
self.set_step_params(0, 'pressure_1', [0.06])
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Solver 会在 solve 时:
|
|
142
|
+
- 一次性用 `get_loads_fea()` 将全部载荷添加到 FEA。
|
|
143
|
+
- 每个步调用 `process_fea(fea, step_index)` 应用当前步的幅值;模型不需要每步重建、仅切换数值,效率更高。
|
|
144
|
+
|
|
145
|
+
### 材料参数 MaterialParams
|
|
146
|
+
|
|
147
|
+
示例:
|
|
148
|
+
```python
|
|
149
|
+
class MaterialParams(_Materials):
|
|
150
|
+
def __init__(self):
|
|
151
|
+
super().__init__(mu=0.482, kappa=4.8, density=1.08e-9)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 生成器与求解器
|
|
155
|
+
|
|
156
|
+
- `Generator(_Generator)`:根据曲面生成中间数据;常见参数有 `seed_size`、`mesh_order` 等。
|
|
157
|
+
- `Solver(_MorphSolver)`:无需手动管理每步载荷添加/删除;内部已经采用“预定义载荷 + 分步调幅”的机制。
|
|
158
|
+
|
|
159
|
+
## 目标与更新器(Objective/Updater)
|
|
160
|
+
|
|
161
|
+
在 `Updater(_Updaters).UpdaterSurfaces(_UpdaterSurfaces)` 中:
|
|
162
|
+
- 创建形状导数(如 `ShapeDerivativeDirect(reset_per_iter=5)`)并注册:
|
|
163
|
+
- `self.add_objective_function(shape_derivative)`
|
|
164
|
+
- 可叠加其它目标/正则(如 `Fairness`, `Distance`, `boundarys.Cylinder` 等)。
|
|
165
|
+
|
|
166
|
+
常见用法:
|
|
167
|
+
- 目标函数统一通过 `self.add_objective_function(...)` 注册。
|
|
168
|
+
- 多目标时,可通过权重在对应目标构造参数中体现。
|
|
169
|
+
|
|
170
|
+
## 载荷接口一览(Pressure/Contact/Point)
|
|
171
|
+
|
|
172
|
+
位置:`MorphOpt/modelparams/loads/LoadInterface/`
|
|
173
|
+
|
|
174
|
+
- PressureInterface:面压力,单一标量幅值。
|
|
175
|
+
- ContactInterface:外部接触;一般无幅值。
|
|
176
|
+
- ContactSelfInterface:自接触;一般无幅值。
|
|
177
|
+
- ConcentratedForceInterface:集中力,基于参考点名 `rp_name`,幅值为 `[Fx,Fy,Fz]`。
|
|
178
|
+
- ConcentratedMomentInterface:集中力矩,`rp_name`,幅值为 `[Mx,My,Mz]`。
|
|
179
|
+
- SpringToGroundInterface:弹簧(RP-地面点),参数顺序为 `[k, L0, Px, Py, Pz]`。
|
|
180
|
+
- SpringBetweenRPsInterface:弹簧(RP-RP),参数顺序为 `[k, L0]`。
|
|
181
|
+
|
|
182
|
+
注意:接口类内部仅存 Python list[float];参数导出阶段再转为张量,便于优化器统一打包。
|
|
183
|
+
|
|
184
|
+
## 日志与历史(History)
|
|
185
|
+
|
|
186
|
+
- `initializer.initialize_path(...)` 会创建输出目录结构(如 `Results/`)。
|
|
187
|
+
- `initializer.initialize_history()` 管理迭代历史(`GLOBAL.History`)。
|
|
188
|
+
- 你可以在 `Jobs/...` 的脚本中设置任务名与输出路径,以便归档每次试验结果。
|
|
189
|
+
|
|
190
|
+
## GPU 与精度设置
|
|
191
|
+
|
|
192
|
+
- 默认使用 `torch.float64`;可在入口脚本中通过 `torch.set_default_dtype` 调整。
|
|
193
|
+
- 若可用 GPU,则内部会选择合适设备;也可在脚本中手动设置或屏蔽 CUDA(调试时可启用 `CUDA_LAUNCH_BLOCKING=1`)。
|
|
194
|
+
|
|
195
|
+
## 性能建议
|
|
196
|
+
|
|
197
|
+
- 预先“注册所有载荷”,并仅在步间切换幅值(已在 `LoadParams`/`Solver` 内实现)。
|
|
198
|
+
- 合理设置网格密度与曲面参数,避免过大规模导致 FEA 迭代缓慢。
|
|
199
|
+
- 多步求解时,确保每步仅修改必要的幅值,减少 FE 内部对象的重复构建。
|
|
200
|
+
|
|
201
|
+
## 故障排查(FAQ)
|
|
202
|
+
|
|
203
|
+
- 载荷无效/不生效?
|
|
204
|
+
- 确认已通过 `add_load_interface` 注册;对有幅值的载荷在每个步都正确调用了 `set_step_params`。
|
|
205
|
+
- 接触相关错误?
|
|
206
|
+
- 检查实例名与表面名是否与 INP/几何一致;必要时在 `Solver.init_FEA` 中自定义接触参数。
|
|
207
|
+
- GPU/精度导致的数值不稳定?
|
|
208
|
+
- 可改回 CPU 或降低步长;确保所有张量 dtype 一致为 float64。
|
|
209
|
+
|
|
210
|
+
## 贡献与许可证
|
|
211
|
+
|
|
212
|
+
- 欢迎通过 PR/Issue 贡献新的载荷接口、目标函数或示例任务。
|
|
213
|
+
- 许可证与版权信息请参考仓库根目录中的相关文件(若缺失,请在提交 PR 时补充)。
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
更多参考:
|
|
218
|
+
- 示例:`Jobs/examples/displacement.py`
|
|
219
|
+
- 接触/集中载荷用法:`Jobs/locomotion/front.py`、`Jobs/ral2025contact/*`
|
|
220
|
+
- 载荷接口实现:`MorphOpt/modelparams/loads/LoadInterface/`
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "morphopt"
|
|
3
|
+
version = "3.1.1"
|
|
4
|
+
description = "A morphing optimization framework using PyTorch and Abaqus."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"torch>=2.0.0",
|
|
9
|
+
"torchvision>=0.15.1",
|
|
10
|
+
"numpy>=2.0.0",
|
|
11
|
+
"pypardiso>=0.4.7",
|
|
12
|
+
"pyqt6>=6.10.2",
|
|
13
|
+
"pyvista>=0.46.4",
|
|
14
|
+
"pyvistaqt>=0.11.3",
|
|
15
|
+
"vtk>=9.5.2",
|
|
16
|
+
"gmsh>=4.15.0",
|
|
17
|
+
"tabulate>=0.9.0",
|
|
18
|
+
"cpgeo>=1.0.2",
|
|
19
|
+
"imageio>=2.37.2",
|
|
20
|
+
"bspmap>=1.0.0",
|
|
21
|
+
"torchfea>=1.0.4",
|
|
22
|
+
"trimesh>=4.11.1",
|
|
23
|
+
]
|
morphopt-3.1.1/setup.cfg
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# region optcore imports
|
|
2
|
+
from .optcore.controller import Controller
|
|
3
|
+
from .optcore.modelparams import GeometryParams, FEAParams, Materials
|
|
4
|
+
from .optcore.solver import MorphSolver
|
|
5
|
+
from .optcore.updaters.geometry import UpdaterGeometries
|
|
6
|
+
from .optcore.updaters.updaters import Updaters
|
|
7
|
+
from .optcore.modelparams import Params
|
|
8
|
+
from .optcore.objfunc import ObjectiveFunction
|
|
9
|
+
from .optcore.history import History
|
|
10
|
+
from .optcore.baseobject import BaseObject
|
|
11
|
+
from .optcore.utils.plot_history_surface import SurfacesFigurePlotter
|
|
12
|
+
# endregion
|
|
13
|
+
|
|
14
|
+
from .opt_runner import start_optimization, debug_optimization, view_optimization_result
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
controller: Controller = None
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"Controller",
|
|
23
|
+
"GeometryParams",
|
|
24
|
+
"FEAParams",
|
|
25
|
+
"Materials",
|
|
26
|
+
"MorphSolver",
|
|
27
|
+
"UpdaterGeometries",
|
|
28
|
+
"Updaters",
|
|
29
|
+
"Params",
|
|
30
|
+
"ObjectiveFunction",
|
|
31
|
+
]
|
|
32
|
+
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def start_optimization(device='cpu', restart_per_iteration: int = 20, path_result: str=None, target_iteration=None, no_gui: bool=False):
|
|
5
|
+
"""
|
|
6
|
+
Start a new optimization process.
|
|
7
|
+
|
|
8
|
+
Args:
|
|
9
|
+
Controller (type): The controller class to use for the optimization.
|
|
10
|
+
device (str, optional): The device to use for computation. Defaults to 'cpu'.
|
|
11
|
+
restart_per_iteration (int, optional): The number of iterations between restarts. Defaults to 20.
|
|
12
|
+
"""
|
|
13
|
+
if path_result is None:
|
|
14
|
+
import __main__
|
|
15
|
+
main_filepath = __main__.__file__
|
|
16
|
+
else:
|
|
17
|
+
main_filepath = path_result + '/scripts/' + 'MAIN_SCRIPT_FOR_RESTART.py'
|
|
18
|
+
|
|
19
|
+
import time
|
|
20
|
+
import multiprocessing as mp
|
|
21
|
+
from .taskoptmization import TaskOptimization
|
|
22
|
+
from .taskui import run_ui
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
dataqueue = mp.Queue()
|
|
26
|
+
process_optimization = mp.Process(target=TaskOptimization.task_optimization, kwargs={'path_result': path_result,
|
|
27
|
+
'main_filepath': main_filepath,
|
|
28
|
+
'device': device,
|
|
29
|
+
'target_iteration': target_iteration,
|
|
30
|
+
'restart_per_iteration': restart_per_iteration,
|
|
31
|
+
'dataqueue': dataqueue})
|
|
32
|
+
process_optimization.start()
|
|
33
|
+
|
|
34
|
+
if no_gui:
|
|
35
|
+
process_optimization.join()
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
process_ui = mp.Process(target=run_ui, args=(dataqueue, main_filepath))
|
|
39
|
+
process_ui.start()
|
|
40
|
+
|
|
41
|
+
process_optimization.join()
|
|
42
|
+
process_ui.join()
|
|
43
|
+
|
|
44
|
+
def view_optimization_result(path_result: str = None):
|
|
45
|
+
"""
|
|
46
|
+
View the result of an optimization process.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
path_result (str): The folder path of the optimization result.
|
|
50
|
+
"""
|
|
51
|
+
import os
|
|
52
|
+
import multiprocessing as mp
|
|
53
|
+
from .taskui import run_ui
|
|
54
|
+
from .optcore.history import History
|
|
55
|
+
|
|
56
|
+
if path_result is None or not os.path.exists(path_result):
|
|
57
|
+
# Use file dialog to select directory if path is not provided
|
|
58
|
+
# We need a temporary app to show the dialog
|
|
59
|
+
from PyQt6.QtWidgets import QApplication, QFileDialog
|
|
60
|
+
import sys
|
|
61
|
+
|
|
62
|
+
# Check if an app instance already exists
|
|
63
|
+
app = QApplication.instance()
|
|
64
|
+
if not app:
|
|
65
|
+
app = QApplication(sys.argv)
|
|
66
|
+
|
|
67
|
+
path_result = QFileDialog.getExistingDirectory(None, "Select Optimization Result Folder", os.getcwd())
|
|
68
|
+
if not path_result:
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
main_filepath = os.path.join(path_result, 'scripts', 'MAIN_SCRIPT_FOR_RESTART.py')
|
|
72
|
+
if not os.path.exists(main_filepath):
|
|
73
|
+
print(f"Error: {main_filepath} not found.")
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
# Determine the last iteration
|
|
77
|
+
history = History()
|
|
78
|
+
try:
|
|
79
|
+
history.load(foldpath=os.path.join(path_result, 'log'))
|
|
80
|
+
last_iteration = history.iteration
|
|
81
|
+
except:
|
|
82
|
+
last_iteration = 0
|
|
83
|
+
|
|
84
|
+
dataqueue = mp.Queue()
|
|
85
|
+
# Send the initial data to load the result
|
|
86
|
+
dataqueue.put({'iteration': last_iteration, 'path_result': path_result})
|
|
87
|
+
|
|
88
|
+
run_ui(dataqueue, main_filepath)
|
|
89
|
+
|
|
90
|
+
def debug_optimization(device='cpu', restart_per_iteration: int = 20, path_result: str=None, target_iteration=None, ):
|
|
91
|
+
"""
|
|
92
|
+
Start a new optimization process.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
Controller (type): The controller class to use for the optimization.
|
|
96
|
+
device (str, optional): The device to use for computation. Defaults to 'cpu'.
|
|
97
|
+
restart_per_iteration (int, optional): The number of iterations between restarts. Defaults to 20.
|
|
98
|
+
"""
|
|
99
|
+
import __main__
|
|
100
|
+
from .taskoptmization import TaskOptimization
|
|
101
|
+
import os
|
|
102
|
+
import morphopt
|
|
103
|
+
|
|
104
|
+
if path_result is None:
|
|
105
|
+
import __main__
|
|
106
|
+
main_filepath = __main__.__file__
|
|
107
|
+
else:
|
|
108
|
+
main_filepath = path_result + '/scripts/' + 'MAIN_SCRIPT_FOR_RESTART.py'
|
|
109
|
+
|
|
110
|
+
filename = os.path.splitext(os.path.basename(main_filepath))[0]
|
|
111
|
+
filepath = os.path.dirname(main_filepath)
|
|
112
|
+
os.chdir(filepath)
|
|
113
|
+
import sys
|
|
114
|
+
sys.path.append(os.getcwd())
|
|
115
|
+
|
|
116
|
+
Controller: morphopt.Controller = getattr(__import__(filename), 'ThisController')
|
|
117
|
+
|
|
118
|
+
controller: morphopt.Controller = Controller()
|
|
119
|
+
controller.restart_per_iteration = restart_per_iteration
|
|
120
|
+
controller.optdevice = device
|
|
121
|
+
|
|
122
|
+
TaskOptimization.optmain(device=device,
|
|
123
|
+
path_result=path_result,
|
|
124
|
+
target_iteration=target_iteration,
|
|
125
|
+
restart_per_iteration=restart_per_iteration,
|
|
126
|
+
main_filepath=main_filepath)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|