morphopt 3.2.1__tar.gz → 4.0.0__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.2.1 → morphopt-4.0.0}/PKG-INFO +5 -3
- {morphopt-3.2.1 → morphopt-4.0.0}/README.md +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/pyproject.toml +17 -3
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/__init__.py +50 -28
- morphopt-4.0.0/src/morphopt/codesign/__init__.py +23 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/codesign/constraints.py +157 -51
- morphopt-4.0.0/src/morphopt/codesign/feaparams.py +66 -0
- morphopt-4.0.0/src/morphopt/codesign/geometry.py +265 -0
- morphopt-4.0.0/src/morphopt/codesign/geometryparams.py +47 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/opt_runner.py +36 -19
- morphopt-4.0.0/src/morphopt/optcore/__init__.py +21 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/controller.py +181 -120
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/history.py +174 -141
- morphopt-4.0.0/src/morphopt/optcore/modelparams/__init__.py +14 -0
- morphopt-4.0.0/src/morphopt/optcore/modelparams/baseparam.py +170 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/__init__.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/basefeainterface.py +23 -2
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/bodyforceinterface.py +24 -14
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/boundaryconditioninterface.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/contactinterface.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/coupleinterface.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/pointinterface.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/pressureinterface.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/referencepointinterface.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/springinterface.py +0 -0
- morphopt-4.0.0/src/morphopt/optcore/modelparams/feaparams.py +134 -0
- morphopt-4.0.0/src/morphopt/optcore/modelparams/geometry.py +64 -0
- morphopt-4.0.0/src/morphopt/optcore/modelparams/materialinterface/__init__.py +11 -0
- morphopt-4.0.0/src/morphopt/optcore/modelparams/materialinterface/basematerialinterface.py +91 -0
- morphopt-4.0.0/src/morphopt/optcore/modelparams/materialinterface/homogeneousmaterial.py +21 -0
- morphopt-4.0.0/src/morphopt/optcore/modelparams/materialinterface/materialmodels.py +111 -0
- morphopt-4.0.0/src/morphopt/optcore/modelparams/materials.py +25 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/params.py +62 -51
- morphopt-4.0.0/src/morphopt/optcore/modelparams/partinterface/__init__.py +17 -0
- morphopt-4.0.0/src/morphopt/optcore/modelparams/partinterface/basepartinterface.py +205 -0
- morphopt-4.0.0/src/morphopt/optcore/modelparams/partinterface/inppartinterface.py +72 -0
- morphopt-4.0.0/src/morphopt/optcore/modelparams/partinterface/torchfeapartinterface.py +177 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/objfunc.py +14 -10
- morphopt-4.0.0/src/morphopt/optcore/protocal.py +122 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/optcore/solver.py +107 -49
- morphopt-4.0.0/src/morphopt/optcore/updaters.py +779 -0
- morphopt-4.0.0/src/morphopt/shapeopt/__init__.py +12 -0
- morphopt-4.0.0/src/morphopt/shapeopt/boundarypartinterface.py +502 -0
- morphopt-4.0.0/src/morphopt/shapeopt/meshgenerator.py +309 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/shapeopt/objectivefuncs/__init__.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/shapeopt/objectivefuncs/basefuncs.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/shapeopt/objectivefuncs/boundarys.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/shapeopt/objectivefuncs/shapederivative.py +88 -63
- morphopt-4.0.0/src/morphopt/shapeopt/objectivefuncs/surfacefairness.py +66 -0
- morphopt-4.0.0/src/morphopt/shapeopt/objectivefuncs/volumemaximization.py +58 -0
- morphopt-4.0.0/src/morphopt/shapeopt/surfaceinterfaces/__init__.py +21 -0
- {morphopt-3.2.1/src/morphopt/shapeopt/geometryinterfaces → morphopt-4.0.0/src/morphopt/shapeopt/surfaceinterfaces}/basesurfaceinterface.py +80 -76
- {morphopt-3.2.1/src/morphopt/shapeopt/geometryinterfaces → morphopt-4.0.0/src/morphopt/shapeopt/surfaceinterfaces}/bspsurfaceinterface.py +14 -67
- {morphopt-3.2.1/src/morphopt/shapeopt/geometryinterfaces → morphopt-4.0.0/src/morphopt/shapeopt/surfaceinterfaces}/cpgeosurfaceinterface.py +9 -32
- morphopt-4.0.0/src/morphopt/shapeopt/update_boundarypart.py +452 -0
- morphopt-4.0.0/src/morphopt/simp/__init__.py +10 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/simp/objectivefuncs/__init__.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/simp/objectivefuncs/basefuncs.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/simp/objectivefuncs/boundarys.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/simp/objectivefuncs/densityfield.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/simp/objectivefuncs/sensitivity.py +0 -0
- morphopt-4.0.0/src/morphopt/simp/objectivefuncs/volfrac.py +83 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/simp/simpmaterial.py +587 -321
- morphopt-4.0.0/src/morphopt/simp/update_simpmaterial.py +403 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/taskoptmization.py +44 -17
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/__init__.py +3 -1
- morphopt-4.0.0/src/morphopt/ui/application/__init__.py +31 -0
- morphopt-4.0.0/src/morphopt/ui/application/definitions.py +171 -0
- morphopt-4.0.0/src/morphopt/ui/application/editor_routing.py +52 -0
- morphopt-4.0.0/src/morphopt/ui/application/results.py +128 -0
- morphopt-4.0.0/src/morphopt/ui/application/runs.py +138 -0
- morphopt-4.0.0/src/morphopt/ui/codegen/generator.py +816 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/launcher.py +3 -2
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/mainwindow.py +30 -34
- morphopt-4.0.0/src/morphopt/ui/model/__init__.py +20 -0
- morphopt-4.0.0/src/morphopt/ui/model/modelinfo.py +118 -0
- morphopt-4.0.0/src/morphopt/ui/model/problem.py +2487 -0
- morphopt-4.0.0/src/morphopt/ui/model/schemas.py +1343 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/observe_panel.py +127 -307
- morphopt-4.0.0/src/morphopt/ui/schemes/__init__.py +16 -0
- morphopt-4.0.0/src/morphopt/ui/schemes/base.py +353 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/schemes/snippets.py +3 -3
- morphopt-4.0.0/src/morphopt/ui/templates/cantilever_beam.npz +0 -0
- morphopt-4.0.0/src/morphopt/ui/templates/multidesign.morph +443 -0
- morphopt-4.0.0/src/morphopt/ui/templates/shapeopt.morph +202 -0
- morphopt-4.0.0/src/morphopt/ui/templates/simp.morph +262 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/widgets/code_completion.py +14 -20
- morphopt-4.0.0/src/morphopt/ui/widgets/editor.py +565 -0
- morphopt-4.0.0/src/morphopt/ui/widgets/model_tree.py +868 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/widgets/objective_editor.py +6 -3
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/widgets/observation_pages.py +148 -14
- morphopt-4.0.0/src/morphopt/ui/widgets/run_dialogs.py +149 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/widgets/stepmatrix.py +22 -23
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/widgets/template_insert_dialog.py +23 -17
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/widgets/torchfea_model_editor.py +182 -102
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/widgets/updater_editor.py +277 -150
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/widgets/values.py +1 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/widgets/viewer.py +235 -83
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/workbench.py +274 -184
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/utils/__init__.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/utils/gradient_check.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/utils/history_read.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt.egg-info/PKG-INFO +5 -3
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt.egg-info/SOURCES.txt +30 -18
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt.egg-info/dependency_links.txt +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt.egg-info/requires.txt +4 -1
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt.egg-info/top_level.txt +0 -0
- morphopt-3.2.1/src/morphopt/codesign/__init__.py +0 -18
- morphopt-3.2.1/src/morphopt/codesign/feaparams.py +0 -75
- morphopt-3.2.1/src/morphopt/codesign/geometry.py +0 -359
- morphopt-3.2.1/src/morphopt/codesign/material.py +0 -53
- morphopt-3.2.1/src/morphopt/optcore/__init__.py +0 -4
- morphopt-3.2.1/src/morphopt/optcore/baseobject.py +0 -48
- morphopt-3.2.1/src/morphopt/optcore/modelparams/__init__.py +0 -5
- morphopt-3.2.1/src/morphopt/optcore/modelparams/baseparam.py +0 -133
- morphopt-3.2.1/src/morphopt/optcore/modelparams/feaparams.py +0 -239
- morphopt-3.2.1/src/morphopt/optcore/modelparams/geometry.py +0 -251
- morphopt-3.2.1/src/morphopt/optcore/modelparams/materials.py +0 -119
- morphopt-3.2.1/src/morphopt/optcore/updaters.py +0 -384
- morphopt-3.2.1/src/morphopt/shapeopt/__init__.py +0 -8
- morphopt-3.2.1/src/morphopt/shapeopt/geometryinterfaces/__init__.py +0 -3
- morphopt-3.2.1/src/morphopt/shapeopt/geometryparams.py +0 -780
- morphopt-3.2.1/src/morphopt/shapeopt/objectivefuncs/surfacefairness.py +0 -38
- morphopt-3.2.1/src/morphopt/shapeopt/objectivefuncs/volumemaximization.py +0 -25
- morphopt-3.2.1/src/morphopt/shapeopt/objfunc.py +0 -13
- morphopt-3.2.1/src/morphopt/shapeopt/update_geometry.py +0 -335
- morphopt-3.2.1/src/morphopt/simp/__init__.py +0 -9
- morphopt-3.2.1/src/morphopt/simp/objectivefuncs/volfrac.py +0 -47
- morphopt-3.2.1/src/morphopt/simp/solver.py +0 -41
- morphopt-3.2.1/src/morphopt/simp/update_material.py +0 -313
- morphopt-3.2.1/src/morphopt/ui/codegen/generator.py +0 -498
- morphopt-3.2.1/src/morphopt/ui/model/__init__.py +0 -16
- morphopt-3.2.1/src/morphopt/ui/model/problem.py +0 -1119
- morphopt-3.2.1/src/morphopt/ui/model/schemas.py +0 -801
- morphopt-3.2.1/src/morphopt/ui/schemes/__init__.py +0 -18
- morphopt-3.2.1/src/morphopt/ui/schemes/base.py +0 -186
- morphopt-3.2.1/src/morphopt/ui/schemes/codesign.py +0 -143
- morphopt-3.2.1/src/morphopt/ui/schemes/shapeopt.py +0 -100
- morphopt-3.2.1/src/morphopt/ui/schemes/simp.py +0 -107
- morphopt-3.2.1/src/morphopt/ui/widgets/editor.py +0 -344
- morphopt-3.2.1/src/morphopt/ui/widgets/model_tree.py +0 -485
- morphopt-3.2.1/tests/test_fixedGeometry.py +0 -24
- morphopt-3.2.1/tests/test_path.py +0 -12
- morphopt-3.2.1/tests/test_ui.py +0 -62
- {morphopt-3.2.1 → morphopt-4.0.0}/setup.cfg +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/codesign/params.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/shapeopt/objectivefuncs/distancesurface.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/__main__.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/app.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/codegen/__init__.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/i18n.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/model/loaders.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/widgets/__init__.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/widgets/codeeditor.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/widgets/param_form.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt/ui/widgets/solver_editor.py +0 -0
- {morphopt-3.2.1 → morphopt-4.0.0}/src/morphopt.egg-info/entry_points.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: morphopt
|
|
3
|
-
Version:
|
|
4
|
-
Summary: A morphing optimization framework using PyTorch and
|
|
3
|
+
Version: 4.0.0
|
|
4
|
+
Summary: A morphing optimization framework using PyTorch and torchfea.
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
Requires-Dist: torch>=2.9.1
|
|
@@ -16,11 +16,13 @@ Requires-Dist: gmsh>=4.15.0
|
|
|
16
16
|
Requires-Dist: tabulate>=0.9.0
|
|
17
17
|
Requires-Dist: imageio>=2.37.3
|
|
18
18
|
Requires-Dist: bspmap>=1.0.3
|
|
19
|
-
Requires-Dist: torchfea>=1.1.
|
|
19
|
+
Requires-Dist: torchfea>=1.1.2
|
|
20
20
|
Requires-Dist: trimesh>=4.11.5
|
|
21
21
|
Requires-Dist: networkx>=3.6.1
|
|
22
22
|
Requires-Dist: scipy>=1.17.1
|
|
23
23
|
Requires-Dist: cpgeo>=1.0.12
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest>=8.0; extra == "test"
|
|
24
26
|
|
|
25
27
|
# MorphOpt — A Differentiable Optimization Framework for Soft Structure & Morphology Design
|
|
26
28
|
|
|
File without changes
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "morphopt"
|
|
3
|
-
version = "
|
|
4
|
-
description = "A morphing optimization framework using PyTorch and
|
|
3
|
+
version = "4.0.0"
|
|
4
|
+
description = "A morphing optimization framework using PyTorch and torchfea."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
7
7
|
dependencies = [
|
|
@@ -17,7 +17,7 @@ dependencies = [
|
|
|
17
17
|
"tabulate>=0.9.0",
|
|
18
18
|
"imageio>=2.37.3",
|
|
19
19
|
"bspmap>=1.0.3",
|
|
20
|
-
"torchfea>=1.1.
|
|
20
|
+
"torchfea>=1.1.2",
|
|
21
21
|
"trimesh>=4.11.5",
|
|
22
22
|
"networkx>=3.6.1",
|
|
23
23
|
"scipy>=1.17.1",
|
|
@@ -27,3 +27,17 @@ dependencies = [
|
|
|
27
27
|
[project.scripts]
|
|
28
28
|
# console entry for the graphical UI (after `pip install morphopt`)
|
|
29
29
|
morphopt-ui = "morphopt.ui:run_app"
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
# Install with: python -m pip install -e ".[test]"
|
|
33
|
+
test = [
|
|
34
|
+
"pytest>=8.0",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[tool.pytest.ini_options]
|
|
38
|
+
testpaths = ["tests"]
|
|
39
|
+
pythonpath = ["src"]
|
|
40
|
+
addopts = "-ra"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.package-data]
|
|
43
|
+
"morphopt.ui" = ["templates/*.morph", "templates/*.npz"]
|
|
@@ -2,10 +2,18 @@
|
|
|
2
2
|
A differentiable structural optimization framework that eliminates manual sensitivity derivation by fusing the adjoint method with automatic differentiation (AD). The sensitivity corresponds to the virtual work of residual force derivatives on the adjoint displacement field, computed via a single backpropagation through the residual graph. MorphOpt provides a unified interface for geometry, load, and material definition, calls torchfea for GPU-accelerated differentiable nonlinear FEA and adjoint-AD sensitivity, and employs a trust-region optimizer with L-BFGS for design updates. Validated on SIMP topology optimization with B-spline density fields and pneumatic soft robot shape optimization with deformation-dependent follower loads.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
# Torch and TorchFEA use GNU OpenMP in this environment. Set MKL's matching
|
|
6
|
+
# threading layer before importing any module that can load NumPy or Torch.
|
|
7
|
+
import os as __os
|
|
8
|
+
import sys as __sys
|
|
9
|
+
|
|
10
|
+
if __sys.platform.startswith("linux"):
|
|
11
|
+
__os.environ["MKL_THREADING_LAYER"] = "GNU"
|
|
5
12
|
|
|
6
13
|
# region: Logging Configuration
|
|
7
14
|
import logging as __logging
|
|
8
15
|
|
|
16
|
+
|
|
9
17
|
def enable_logging(level=__logging.INFO, log_file=None, file_log_level=__logging.DEBUG):
|
|
10
18
|
"""
|
|
11
19
|
Enable logging for the FEA package.
|
|
@@ -18,7 +26,7 @@ def enable_logging(level=__logging.INFO, log_file=None, file_log_level=__logging
|
|
|
18
26
|
the path to a log file where logs will be written. If None, logs will only be printed to the console.
|
|
19
27
|
file_log_level : int
|
|
20
28
|
the logging level for the log file. Default is logging.INFO.
|
|
21
|
-
|
|
29
|
+
|
|
22
30
|
Examples
|
|
23
31
|
--------
|
|
24
32
|
>>> import torchfea
|
|
@@ -26,56 +34,70 @@ def enable_logging(level=__logging.INFO, log_file=None, file_log_level=__logging
|
|
|
26
34
|
"""
|
|
27
35
|
logger = __logging.getLogger(__name__)
|
|
28
36
|
logger.setLevel(min(level, file_log_level))
|
|
29
|
-
|
|
37
|
+
|
|
30
38
|
# clear existing handlers to avoid duplicate logs
|
|
31
39
|
logger.handlers.clear()
|
|
32
|
-
|
|
40
|
+
|
|
33
41
|
# logging to console
|
|
34
42
|
console = __logging.StreamHandler()
|
|
35
43
|
console.setLevel(level)
|
|
36
|
-
console.setFormatter(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
console.setFormatter(
|
|
45
|
+
__logging.Formatter(
|
|
46
|
+
"%(asctime)s | %(levelname)-7s | %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
|
|
47
|
+
)
|
|
48
|
+
)
|
|
40
49
|
logger.addHandler(console)
|
|
41
|
-
|
|
50
|
+
|
|
42
51
|
# logging to file if log_file is provided
|
|
43
52
|
if log_file:
|
|
44
|
-
file_handler = __logging.FileHandler(log_file, encoding=
|
|
53
|
+
file_handler = __logging.FileHandler(log_file, encoding="utf-8")
|
|
45
54
|
file_handler.setLevel(file_log_level)
|
|
46
|
-
file_handler.setFormatter(
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
file_handler.setFormatter(
|
|
56
|
+
__logging.Formatter(
|
|
57
|
+
"%(asctime)s | %(levelname)-7s | %(name)s | %(message)s"
|
|
58
|
+
)
|
|
59
|
+
)
|
|
49
60
|
logger.addHandler(file_handler)
|
|
50
61
|
|
|
51
62
|
return logger
|
|
52
|
-
# endregion
|
|
53
|
-
|
|
54
63
|
|
|
55
64
|
|
|
65
|
+
# endregion
|
|
56
66
|
|
|
57
67
|
|
|
58
|
-
# region
|
|
68
|
+
# region modules (loaded after the core classes they subclass)
|
|
69
|
+
from . import codesign, shapeopt, simp
|
|
70
|
+
from .opt_runner import debug_optimization, start_optimization
|
|
59
71
|
from .optcore.controller import Controller
|
|
60
|
-
from .optcore.
|
|
72
|
+
from .optcore.history import History
|
|
73
|
+
from .optcore.modelparams import (
|
|
74
|
+
BasePartInterface,
|
|
75
|
+
FEAParams,
|
|
76
|
+
GeometryParams,
|
|
77
|
+
INPPartInterface,
|
|
78
|
+
MaterialsParams,
|
|
79
|
+
Params,
|
|
80
|
+
TorchFEAPartInterface,
|
|
81
|
+
load_model_assembly,
|
|
82
|
+
resolve_model_path,
|
|
83
|
+
)
|
|
84
|
+
from .optcore.modelparams.materialinterface import HomogeneousMaterial
|
|
85
|
+
from .optcore.objfunc import ObjectiveFunction
|
|
86
|
+
from .optcore.protocal import (
|
|
87
|
+
ProtocalInitializable,
|
|
88
|
+
ProtocalSavable,
|
|
89
|
+
ProtocalUpdatable,
|
|
90
|
+
ProtocalVisualizable,
|
|
91
|
+
)
|
|
61
92
|
from .optcore.solver import Solver
|
|
62
|
-
from .optcore.modelparams import Params
|
|
63
93
|
from .optcore.updaters import Updaters
|
|
64
|
-
from .
|
|
65
|
-
from .
|
|
66
|
-
from .optcore.baseobject import BaseObject
|
|
67
|
-
# endregion
|
|
68
|
-
|
|
69
|
-
from .opt_runner import start_optimization, debug_optimization
|
|
94
|
+
from .shapeopt import BoundaryPartInterface, UpdaterBoundaryPart
|
|
95
|
+
from .simp import SIMP_BSPFieldMaterials, UpdaterSIMPMaterial
|
|
70
96
|
|
|
71
|
-
# region modules
|
|
72
|
-
from . import shapeopt
|
|
73
|
-
from . import codesign
|
|
74
|
-
from . import simp
|
|
75
97
|
# endregion
|
|
76
|
-
|
|
77
98
|
# region scripts
|
|
78
99
|
from .utils import check_gradients, get_controller
|
|
100
|
+
|
|
79
101
|
# endregion
|
|
80
102
|
|
|
81
103
|
controller: Controller = None
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Co-design (codesign) scheme: shape optimization coupled with SIMP materials."""
|
|
2
|
+
|
|
3
|
+
from ..optcore import MaterialsParams, ObjectiveFunction, Solver, Updaters
|
|
4
|
+
from ..shapeopt import UpdaterBoundaryPart
|
|
5
|
+
from ..simp import SIMP_BSPFieldMaterials, UpdaterSIMPMaterial
|
|
6
|
+
from .constraints import InwardCurvatureRadius, OffsetSurfaceMinThickness
|
|
7
|
+
from .feaparams import CodesignFEAParams
|
|
8
|
+
from .geometry import CodesignBoundaryPartInterface
|
|
9
|
+
from .geometryparams import GeometryParams
|
|
10
|
+
from .params import Params
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def plot_geometry_and_materials(geometry: GeometryParams, materials: MaterialsParams):
|
|
14
|
+
"""Plot every geometry interface together with the material fields."""
|
|
15
|
+
import pyvista as pv
|
|
16
|
+
|
|
17
|
+
plotter = pv.Plotter(window_size=(1400, 900))
|
|
18
|
+
plotter.set_background("white")
|
|
19
|
+
|
|
20
|
+
plotter = materials.plot(plotter=plotter)
|
|
21
|
+
geometry.plot(plotter=plotter)
|
|
22
|
+
|
|
23
|
+
plotter.show()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import torch
|
|
2
2
|
|
|
3
3
|
from ..shapeopt.objectivefuncs.basefuncs import BaseConstraints
|
|
4
|
-
from .geometry import
|
|
4
|
+
from .geometry import CodesignBoundaryPartInterface
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class InwardCurvatureRadius(BaseConstraints):
|
|
@@ -15,7 +15,6 @@ class InwardCurvatureRadius(BaseConstraints):
|
|
|
15
15
|
|
|
16
16
|
def __init__(
|
|
17
17
|
self,
|
|
18
|
-
geometry: CodesignGeometry,
|
|
19
18
|
surface_start: int = 1,
|
|
20
19
|
surface_ids: list[int] | None = None,
|
|
21
20
|
margin: float = 0.4,
|
|
@@ -31,7 +30,6 @@ class InwardCurvatureRadius(BaseConstraints):
|
|
|
31
30
|
Initialize the inward curvature radius constraint.
|
|
32
31
|
|
|
33
32
|
Args:
|
|
34
|
-
geometry (CodesignGeometry): The geometry parameters containing the surfaces.
|
|
35
33
|
surface_start (int, optional): The starting index of surfaces to apply the constraint. Defaults to 1.
|
|
36
34
|
surface_ids (list[int] | None, optional): The specific surface indices to apply the constraint. If None, applies to all surfaces starting from surface_start. Defaults to None.
|
|
37
35
|
margin (float, optional): The absolute margin added to thickness for curvature limit. Defaults to 0.4.
|
|
@@ -42,27 +40,39 @@ class InwardCurvatureRadius(BaseConstraints):
|
|
|
42
40
|
penalty_scale (float, optional): Global multiplier for this constraint penalty. Defaults to 1e3.
|
|
43
41
|
hard_violation_beta (float, optional): Exponential amplification factor for large violations. Defaults to 8.0.
|
|
44
42
|
normal_sign (float, optional): The sign to determine inward direction. Use 1.0 if normals point outward, -1.0 if normals point inward. Defaults to 1.0.
|
|
45
|
-
|
|
46
|
-
"""
|
|
47
43
|
|
|
44
|
+
"""
|
|
48
45
|
|
|
49
46
|
super().__init__()
|
|
50
47
|
|
|
51
|
-
self.geometry = geometry
|
|
52
48
|
self.surface_start = int(surface_start)
|
|
53
|
-
|
|
49
|
+
"""Stores the surface start used by InwardCurvatureRadius."""
|
|
50
|
+
self.surface_ids = (
|
|
51
|
+
None if surface_ids is None else [int(i) for i in surface_ids]
|
|
52
|
+
)
|
|
53
|
+
"""Stores the surface ids used by InwardCurvatureRadius."""
|
|
54
54
|
|
|
55
55
|
self.margin = margin
|
|
56
|
+
"""Stores the margin used by InwardCurvatureRadius."""
|
|
56
57
|
self.margin_ratio = float(margin_ratio)
|
|
58
|
+
"""Stores the margin ratio used by InwardCurvatureRadius."""
|
|
57
59
|
self.barrier_thre_ratio = float(barrier_thre_ratio)
|
|
60
|
+
"""Stores the barrier thre ratio used by InwardCurvatureRadius."""
|
|
58
61
|
self.barrier_ratio = float(barrier_ratio)
|
|
62
|
+
"""Stores the barrier ratio used by InwardCurvatureRadius."""
|
|
59
63
|
self.p = int(p)
|
|
64
|
+
"""Stores the p used by InwardCurvatureRadius."""
|
|
60
65
|
self.penalty_scale = float(penalty_scale)
|
|
66
|
+
"""Stores the penalty scale used by InwardCurvatureRadius."""
|
|
61
67
|
self.hard_violation_beta = float(hard_violation_beta)
|
|
68
|
+
"""Stores the hard violation beta used by InwardCurvatureRadius."""
|
|
62
69
|
self.normal_sign = 1.0 if normal_sign >= 0 else -1.0
|
|
70
|
+
"""Stores the normal sign used by InwardCurvatureRadius."""
|
|
63
71
|
|
|
64
72
|
@staticmethod
|
|
65
|
-
def _principal_curvatures(
|
|
73
|
+
def _principal_curvatures(
|
|
74
|
+
rdu: torch.Tensor, rdu2: torch.Tensor, eps: float = 1e-12
|
|
75
|
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
|
66
76
|
normal0 = torch.cross(rdu[:, :, 1], rdu[:, :, 0], dim=1)
|
|
67
77
|
normal = normal0 / (normal0.norm(dim=1, keepdim=True) + eps)
|
|
68
78
|
|
|
@@ -82,26 +92,45 @@ class InwardCurvatureRadius(BaseConstraints):
|
|
|
82
92
|
k2 = 0.5 * (tr - root)
|
|
83
93
|
return k1, k2
|
|
84
94
|
|
|
95
|
+
def initialize(self, r0, rdu0, *args, part_interface=None, **kwargs) -> None:
|
|
96
|
+
"""Take the Part this constraint is evaluated for.
|
|
97
|
+
|
|
98
|
+
The geometry updater hands over the Part it evaluates; only a
|
|
99
|
+
:class:`~morphopt.codesign.CodesignBoundaryPartInterface` carries a
|
|
100
|
+
shell, so anything else is rejected here.
|
|
101
|
+
"""
|
|
102
|
+
super().initialize(r0=r0, rdu0=rdu0, *args, **kwargs)
|
|
103
|
+
if not isinstance(part_interface, CodesignBoundaryPartInterface):
|
|
104
|
+
raise TypeError(
|
|
105
|
+
f"{type(self).__name__} is evaluated for one co-design Part "
|
|
106
|
+
f"(got {type(part_interface).__name__})."
|
|
107
|
+
)
|
|
108
|
+
self.shell_thickness = float(part_interface.shell_thickness)
|
|
109
|
+
|
|
85
110
|
def _selected_surface_ids(self, n_surface: int) -> list[int]:
|
|
86
111
|
if self.surface_ids is None:
|
|
87
112
|
return [i for i in range(max(self.surface_start, 0), n_surface)]
|
|
88
113
|
return [i for i in self.surface_ids if 0 <= i < n_surface]
|
|
89
114
|
|
|
90
|
-
def __call__(
|
|
115
|
+
def __call__(
|
|
116
|
+
self,
|
|
117
|
+
r: list[torch.Tensor],
|
|
118
|
+
rdu: list[torch.Tensor],
|
|
119
|
+
rdu2: list[torch.Tensor],
|
|
120
|
+
*args,
|
|
121
|
+
**kwargs,
|
|
122
|
+
) -> torch.Tensor:
|
|
91
123
|
loss = torch.zeros(1, device=r[0].device, dtype=r[0].dtype)
|
|
92
124
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
margin_abs = (self.margin_ratio * t) + self.margin
|
|
98
|
-
margin_abs = max(float(margin_abs), 1e-12)
|
|
99
|
-
t_eff = t + margin_abs
|
|
125
|
+
for i in self._selected_surface_ids(len(r)):
|
|
126
|
+
t = self.shell_thickness
|
|
127
|
+
if t <= 0:
|
|
128
|
+
continue
|
|
100
129
|
|
|
101
|
-
|
|
102
|
-
|
|
130
|
+
margin_abs = max(float((self.margin_ratio * t) + self.margin), 1e-12)
|
|
131
|
+
k_limit = 1.0 / (t + margin_abs)
|
|
132
|
+
barrier_thre = max(k_limit * self.barrier_thre_ratio, 1e-12)
|
|
103
133
|
|
|
104
|
-
for i in self._selected_surface_ids(len(r)):
|
|
105
134
|
k1, k2 = self._principal_curvatures(rdu[i], rdu2[i])
|
|
106
135
|
|
|
107
136
|
k1_in = torch.relu(self.normal_sign * k1)
|
|
@@ -109,11 +138,17 @@ class InwardCurvatureRadius(BaseConstraints):
|
|
|
109
138
|
k_inward = torch.maximum(k1_in, k2_in)
|
|
110
139
|
|
|
111
140
|
violation = k_inward - k_limit
|
|
112
|
-
index, penalty = self.barrier_function(
|
|
141
|
+
index, penalty = self.barrier_function(
|
|
142
|
+
violation, barrier_thre, self.barrier_ratio, self.p
|
|
143
|
+
)
|
|
113
144
|
if index.numel() > 0:
|
|
114
145
|
# Strong amplification: base polynomial barrier + exponential hard-violation boost.
|
|
115
146
|
rel_violation = (violation[index] / barrier_thre).clamp(min=0.0)
|
|
116
|
-
hard_boost = torch.exp(
|
|
147
|
+
hard_boost = torch.exp(
|
|
148
|
+
(self.hard_violation_beta * (rel_violation - 1.0)).clamp(
|
|
149
|
+
min=0.0, max=20.0
|
|
150
|
+
)
|
|
151
|
+
)
|
|
117
152
|
penalty_strong = self.penalty_scale * penalty * hard_boost
|
|
118
153
|
loss = loss + (self.scaler[i][index] * penalty_strong).sum()
|
|
119
154
|
|
|
@@ -130,7 +165,6 @@ class OffsetSurfaceMinThickness(BaseConstraints):
|
|
|
130
165
|
|
|
131
166
|
def __init__(
|
|
132
167
|
self,
|
|
133
|
-
geometry: CodesignGeometry,
|
|
134
168
|
surface_start: int = 1,
|
|
135
169
|
surface_ids: list[int] | None = None,
|
|
136
170
|
min_distance: float | None = None,
|
|
@@ -145,26 +179,51 @@ class OffsetSurfaceMinThickness(BaseConstraints):
|
|
|
145
179
|
normal_min: float = -0.7,
|
|
146
180
|
normal_max: float = -0.5,
|
|
147
181
|
) -> None:
|
|
182
|
+
"""
|
|
183
|
+
Initialize the minimum thickness constraint for the offset surface.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
surface_start (int, optional): The starting index of surfaces to apply the constraint. Defaults to 1.
|
|
187
|
+
surface_ids (list[int] | None, optional): The specific surface indices to apply the constraint. If None, applies to all surfaces starting from surface_start. Defaults to None.
|
|
188
|
+
min_distance (float | None, optional): The required minimum distance.
|
|
189
|
+
Defaults to twice the shell thickness.
|
|
190
|
+
"""
|
|
148
191
|
super().__init__()
|
|
149
192
|
|
|
150
|
-
self.geometry = geometry
|
|
151
193
|
self.surface_start = int(surface_start)
|
|
152
|
-
|
|
194
|
+
"""Stores the surface start used by OffsetSurfaceMinThickness."""
|
|
195
|
+
self.surface_ids = (
|
|
196
|
+
None if surface_ids is None else [int(i) for i in surface_ids]
|
|
197
|
+
)
|
|
198
|
+
"""Stores the surface ids used by OffsetSurfaceMinThickness."""
|
|
153
199
|
|
|
154
200
|
self.min_distance = min_distance
|
|
201
|
+
"""Stores the min distance used by OffsetSurfaceMinThickness."""
|
|
155
202
|
self.search_ratio = float(search_ratio)
|
|
203
|
+
"""Stores the search ratio used by OffsetSurfaceMinThickness."""
|
|
156
204
|
self.barrier_thre_ratio = float(barrier_thre_ratio)
|
|
205
|
+
"""Stores the barrier thre ratio used by OffsetSurfaceMinThickness."""
|
|
157
206
|
self.barrier_ratio = float(barrier_ratio)
|
|
207
|
+
"""Stores the barrier ratio used by OffsetSurfaceMinThickness."""
|
|
158
208
|
self.p = int(p)
|
|
209
|
+
"""Stores the p used by OffsetSurfaceMinThickness."""
|
|
159
210
|
self.penalty_scale = float(penalty_scale)
|
|
211
|
+
"""Stores the penalty scale used by OffsetSurfaceMinThickness."""
|
|
160
212
|
self.hard_violation_beta = float(hard_violation_beta)
|
|
213
|
+
"""Stores the hard violation beta used by OffsetSurfaceMinThickness."""
|
|
161
214
|
self.normal_sign = 1.0 if normal_sign >= 0 else -1.0
|
|
215
|
+
"""Stores the normal sign used by OffsetSurfaceMinThickness."""
|
|
162
216
|
self.normal_opposition_thre = float(normal_opposition_thre)
|
|
217
|
+
"""Stores the normal opposition thre used by OffsetSurfaceMinThickness."""
|
|
163
218
|
self.normal_min = float(normal_min)
|
|
219
|
+
"""Stores the normal min used by OffsetSurfaceMinThickness."""
|
|
164
220
|
self.normal_max = float(normal_max)
|
|
221
|
+
"""Stores the normal max used by OffsetSurfaceMinThickness."""
|
|
165
222
|
|
|
166
223
|
self._neighbor_pairs: dict[int, torch.Tensor] = {}
|
|
224
|
+
"""Stores the neighbor pairs used by OffsetSurfaceMinThickness."""
|
|
167
225
|
self._neighbor_mindist: dict[int, torch.Tensor] = {}
|
|
226
|
+
"""Stores the neighbor mindist used by OffsetSurfaceMinThickness."""
|
|
168
227
|
|
|
169
228
|
def _selected_surface_ids(self, n_surface: int) -> list[int]:
|
|
170
229
|
if self.surface_ids is None:
|
|
@@ -177,33 +236,60 @@ class OffsetSurfaceMinThickness(BaseConstraints):
|
|
|
177
236
|
normals = normal0 / (normal0.norm(dim=1, keepdim=True) + 1e-12)
|
|
178
237
|
return normals
|
|
179
238
|
|
|
180
|
-
def _offset_points(
|
|
239
|
+
def _offset_points(
|
|
240
|
+
self, r_sf: torch.Tensor, rdu_sf: torch.Tensor, thickness: float
|
|
241
|
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
|
242
|
+
"""Offset surface points and the normals they were offset along."""
|
|
181
243
|
normals = self._surface_normals(rdu_sf=rdu_sf)
|
|
182
|
-
thickness = float(self.geometry.shell_thickness)
|
|
183
244
|
return r_sf + self.normal_sign * thickness * normals, normals
|
|
184
245
|
|
|
185
|
-
def
|
|
246
|
+
def _min_distance(self, thickness: float) -> float:
|
|
247
|
+
dmin = (
|
|
248
|
+
float(self.min_distance)
|
|
249
|
+
if self.min_distance is not None
|
|
250
|
+
else (2.0 * thickness)
|
|
251
|
+
)
|
|
252
|
+
return max(dmin, 1e-12)
|
|
253
|
+
|
|
254
|
+
def initialize(self, r0, rdu0, *args, part_interface=None, **kwargs) -> None:
|
|
255
|
+
"""Take the Part this constraint is evaluated for and pair its points.
|
|
256
|
+
|
|
257
|
+
The geometry updater hands over the Part it evaluates; only a
|
|
258
|
+
:class:`~morphopt.codesign.CodesignBoundaryPartInterface` carries a
|
|
259
|
+
shell, so anything else is rejected here.
|
|
260
|
+
"""
|
|
186
261
|
super().initialize(r0=r0, rdu0=rdu0, *args, **kwargs)
|
|
262
|
+
if not isinstance(part_interface, CodesignBoundaryPartInterface):
|
|
263
|
+
raise TypeError(
|
|
264
|
+
f"{type(self).__name__} is evaluated for one co-design Part "
|
|
265
|
+
f"(got {type(part_interface).__name__})."
|
|
266
|
+
)
|
|
267
|
+
self.shell_thickness = float(part_interface.shell_thickness)
|
|
187
268
|
|
|
188
269
|
self._neighbor_pairs = {}
|
|
189
270
|
self._neighbor_mindist = {}
|
|
190
271
|
|
|
191
|
-
|
|
272
|
+
if self.shell_thickness <= 0:
|
|
273
|
+
return
|
|
192
274
|
|
|
193
|
-
|
|
194
|
-
dmin = float(self.min_distance) if self.min_distance is not None else (2.0 * thickness)
|
|
195
|
-
dmin = max(dmin, 1e-12)
|
|
196
|
-
search_radius = max(dmin * self.search_ratio, dmin)
|
|
275
|
+
import scipy.spatial
|
|
197
276
|
|
|
198
277
|
for sf_idx in self._selected_surface_ids(len(r0)):
|
|
199
|
-
|
|
278
|
+
dmin = self._min_distance(self.shell_thickness)
|
|
279
|
+
search_radius = max(dmin * self.search_ratio, dmin)
|
|
280
|
+
|
|
281
|
+
roff, normals = self._offset_points(
|
|
282
|
+
r_sf=r0[sf_idx], rdu_sf=rdu0[sf_idx], thickness=self.shell_thickness
|
|
283
|
+
)
|
|
200
284
|
|
|
201
285
|
tree = scipy.spatial.KDTree(roff.detach().cpu().numpy())
|
|
202
|
-
pairs_np = tree.query_pairs(r=search_radius, output_type=
|
|
286
|
+
pairs_np = tree.query_pairs(r=search_radius, output_type="ndarray")
|
|
203
287
|
if pairs_np.size == 0:
|
|
204
288
|
continue
|
|
205
289
|
|
|
206
|
-
pairs = torch.from_numpy(pairs_np.T).to(
|
|
290
|
+
pairs = torch.from_numpy(pairs_np.T).to(
|
|
291
|
+
dtype=torch.int64, device=r0[sf_idx].device
|
|
292
|
+
)
|
|
207
293
|
|
|
208
294
|
dotn = (normals[pairs[0]] * normals[pairs[1]]).sum(dim=1)
|
|
209
295
|
opposition = (-dotn).clamp(min=0.0)
|
|
@@ -213,22 +299,28 @@ class OffsetSurfaceMinThickness(BaseConstraints):
|
|
|
213
299
|
self._neighbor_pairs[sf_idx] = pairs
|
|
214
300
|
self._neighbor_mindist[sf_idx] = dmin * opposition[keep]
|
|
215
301
|
|
|
216
|
-
def __call__(
|
|
302
|
+
def __call__(
|
|
303
|
+
self,
|
|
304
|
+
r: list[torch.Tensor],
|
|
305
|
+
rdu: list[torch.Tensor],
|
|
306
|
+
rdu2: list[torch.Tensor],
|
|
307
|
+
*args,
|
|
308
|
+
**kwargs,
|
|
309
|
+
) -> torch.Tensor:
|
|
217
310
|
loss = torch.zeros(1, device=r[0].device, dtype=r[0].dtype)
|
|
218
311
|
|
|
219
|
-
if float(self.geometry.shell_thickness) <= 0:
|
|
220
|
-
return loss
|
|
221
|
-
|
|
222
|
-
thickness = float(self.geometry.shell_thickness)
|
|
223
|
-
dmin = float(self.min_distance) if self.min_distance is not None else (2.0 * thickness)
|
|
224
|
-
dmin = max(dmin, 1e-12)
|
|
225
|
-
barrier_thre = max(dmin * self.barrier_thre_ratio, 1e-12)
|
|
226
|
-
|
|
227
312
|
for sf_idx in self._selected_surface_ids(len(r)):
|
|
228
313
|
pairs = self._neighbor_pairs.get(sf_idx, None)
|
|
229
314
|
if pairs is None or pairs.numel() == 0:
|
|
230
315
|
continue
|
|
231
316
|
|
|
317
|
+
thickness = self.shell_thickness
|
|
318
|
+
if thickness <= 0:
|
|
319
|
+
continue
|
|
320
|
+
barrier_thre = max(
|
|
321
|
+
self._min_distance(thickness) * self.barrier_thre_ratio, 1e-12
|
|
322
|
+
)
|
|
323
|
+
|
|
232
324
|
# Base surface normals (offset surface shares same normal direction)
|
|
233
325
|
normals = self._surface_normals(rdu_sf=rdu[sf_idx])
|
|
234
326
|
dotn = (normals[pairs[0]] * normals[pairs[1]]).sum(dim=1)
|
|
@@ -241,28 +333,42 @@ class OffsetSurfaceMinThickness(BaseConstraints):
|
|
|
241
333
|
pairs_n = pairs[:, mask_n]
|
|
242
334
|
dotn_n = dotn[mask_n]
|
|
243
335
|
mindist_n = self._neighbor_mindist[sf_idx][mask_n].to(
|
|
244
|
-
device=r[sf_idx].device, dtype=r[sf_idx].dtype
|
|
336
|
+
device=r[sf_idx].device, dtype=r[sf_idx].dtype
|
|
337
|
+
)
|
|
245
338
|
scaler_n0 = self.scaler[sf_idx][pairs_n[0]]
|
|
246
339
|
scaler_n1 = self.scaler[sf_idx][pairs_n[1]]
|
|
247
340
|
|
|
248
341
|
# Offset points and distance
|
|
249
|
-
roff, _ = self._offset_points(
|
|
342
|
+
roff, _ = self._offset_points(
|
|
343
|
+
r_sf=r[sf_idx], rdu_sf=rdu[sf_idx], thickness=thickness
|
|
344
|
+
)
|
|
250
345
|
delta = roff[pairs_n[0]] - roff[pairs_n[1]]
|
|
251
346
|
dist = torch.sqrt((delta * delta).sum(dim=1) + 1e-18)
|
|
252
347
|
|
|
253
348
|
violation = mindist_n - dist + barrier_thre
|
|
254
|
-
indexl, l = self.barrier_function(
|
|
349
|
+
indexl, l = self.barrier_function(
|
|
350
|
+
violation, barrier_thre, self.barrier_ratio, self.p
|
|
351
|
+
)
|
|
255
352
|
|
|
256
353
|
if indexl.numel() > 0:
|
|
257
354
|
# Smooth normal penalty (same as Distance class)
|
|
258
|
-
np_val = (
|
|
355
|
+
np_val = (
|
|
356
|
+
self.normal_max
|
|
357
|
+
- torch.clamp(dotn_n[indexl], self.normal_min, self.normal_max)
|
|
358
|
+
) / (self.normal_max - self.normal_min)
|
|
259
359
|
np_smooth = (6 * np_val**2 - 15 * np_val + 10) * np_val**3
|
|
260
360
|
|
|
261
361
|
rel_violation = (violation[indexl] / barrier_thre).clamp(min=0.0)
|
|
262
|
-
hard_boost = torch.exp(
|
|
362
|
+
hard_boost = torch.exp(
|
|
363
|
+
(self.hard_violation_beta * (rel_violation - 1.0)).clamp(
|
|
364
|
+
min=0.0, max=20.0
|
|
365
|
+
)
|
|
366
|
+
)
|
|
263
367
|
penalty_strong = self.penalty_scale * l * hard_boost * np_smooth
|
|
264
368
|
|
|
265
|
-
loss =
|
|
369
|
+
loss = (
|
|
370
|
+
loss
|
|
371
|
+
+ (penalty_strong * scaler_n0[indexl] * scaler_n1[indexl]).sum()
|
|
372
|
+
)
|
|
266
373
|
|
|
267
374
|
return loss
|
|
268
|
-
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""FEA parameter handling for the co-design (``codesign``) scheme.
|
|
2
|
+
|
|
3
|
+
Co-design builds extra ``C3D6``/``C3D15`` shell elements on the cavity surfaces
|
|
4
|
+
of every shell-carrying geometry interface. Those elements need to be
|
|
5
|
+
initialized (integration points and weights) against their Part before the
|
|
6
|
+
analysis runs, which is what this specialization of ``FEAParams`` does.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import torchfea
|
|
10
|
+
|
|
11
|
+
import morphopt
|
|
12
|
+
|
|
13
|
+
from ..optcore.modelparams.feaparams import FEAParams
|
|
14
|
+
from ..optcore.modelparams.geometry import GeometryParams
|
|
15
|
+
from .geometry import CodesignBoundaryPartInterface
|
|
16
|
+
|
|
17
|
+
__all__ = ["CodesignFEAParams"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CodesignFEAParams(FEAParams):
|
|
21
|
+
"""FEA parameters that initialize the co-design offset shells."""
|
|
22
|
+
|
|
23
|
+
def shell_interfaces(
|
|
24
|
+
self, geometry: GeometryParams | None = None
|
|
25
|
+
) -> list[CodesignBoundaryPartInterface]:
|
|
26
|
+
"""Geometry interfaces carrying an offset shell."""
|
|
27
|
+
if geometry is None:
|
|
28
|
+
controller = morphopt.controller
|
|
29
|
+
geometry = controller.params.geometry if controller is not None else None
|
|
30
|
+
if geometry is None:
|
|
31
|
+
return []
|
|
32
|
+
return [
|
|
33
|
+
interface
|
|
34
|
+
for interface in geometry.interfaces.values()
|
|
35
|
+
if isinstance(interface, CodesignBoundaryPartInterface)
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
def create_fea(self, assembly: torchfea.Assembly) -> torchfea.FEAController:
|
|
39
|
+
"""Create the FEA controller and initialize every offset shell."""
|
|
40
|
+
fe = super().create_fea(assembly=assembly)
|
|
41
|
+
|
|
42
|
+
for interface in self.shell_interfaces():
|
|
43
|
+
if float(interface.shell_thickness) <= 0:
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
part = fe.assembly.get_part(interface.part_name)
|
|
47
|
+
element_shell = None
|
|
48
|
+
for name in ("C3D6", "C3D15"):
|
|
49
|
+
if name in part.elems:
|
|
50
|
+
element_shell = part.elems[name]
|
|
51
|
+
break
|
|
52
|
+
if element_shell is None:
|
|
53
|
+
raise KeyError(
|
|
54
|
+
f"Shell wedge elements not found on Part "
|
|
55
|
+
f"{interface.part_name!r} (expected 'C3D6' or 'C3D15')."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
element_shell.initialize(nodes=part.nodes)
|
|
59
|
+
if element_shell.gaussian_weight.min() < 0:
|
|
60
|
+
raise ValueError(
|
|
61
|
+
f"The Gaussian weights of the shell elements on Part "
|
|
62
|
+
f"{interface.part_name!r} are negative. Check the mesh "
|
|
63
|
+
"quality of the offset shell."
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return fe
|