morphopt 3.2.2__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.2 → morphopt-4.0.0}/PKG-INFO +5 -3
- {morphopt-3.2.2 → morphopt-4.0.0}/pyproject.toml +17 -3
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/__init__.py +41 -29
- morphopt-4.0.0/src/morphopt/codesign/__init__.py +23 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/codesign/constraints.py +133 -52
- 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.2 → 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.2 → morphopt-4.0.0}/src/morphopt/optcore/controller.py +181 -120
- {morphopt-3.2.2 → 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.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/basefeainterface.py +23 -2
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/bodyforceinterface.py +24 -14
- 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-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/materialinterface/basematerialinterface.py +31 -9
- morphopt-4.0.0/src/morphopt/optcore/modelparams/materials.py +25 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/params.py +60 -47
- 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.2 → 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.2 → 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.2 → 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.2/src/morphopt/shapeopt/geometryinterfaces → morphopt-4.0.0/src/morphopt/shapeopt/surfaceinterfaces}/basesurfaceinterface.py +80 -76
- {morphopt-3.2.2/src/morphopt/shapeopt/geometryinterfaces → morphopt-4.0.0/src/morphopt/shapeopt/surfaceinterfaces}/bspsurfaceinterface.py +14 -67
- {morphopt-3.2.2/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.2 → morphopt-4.0.0}/src/morphopt/simp/objectivefuncs/volfrac.py +13 -17
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/simp/simpmaterial.py +471 -257
- morphopt-4.0.0/src/morphopt/simp/update_simpmaterial.py +403 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/taskoptmization.py +44 -17
- {morphopt-3.2.2 → 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.2 → morphopt-4.0.0}/src/morphopt/ui/launcher.py +3 -2
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/mainwindow.py +30 -34
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/model/__init__.py +6 -6
- 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-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/model/schemas.py +700 -204
- {morphopt-3.2.2 → 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.2 → 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.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/code_completion.py +14 -20
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/editor.py +265 -70
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/model_tree.py +362 -59
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/objective_editor.py +6 -3
- {morphopt-3.2.2 → 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.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/stepmatrix.py +22 -23
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/template_insert_dialog.py +23 -17
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/torchfea_model_editor.py +182 -102
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/updater_editor.py +277 -150
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/viewer.py +234 -81
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/workbench.py +274 -188
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt.egg-info/PKG-INFO +5 -3
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt.egg-info/SOURCES.txt +26 -17
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt.egg-info/requires.txt +4 -1
- morphopt-3.2.2/src/morphopt/codesign/__init__.py +0 -20
- morphopt-3.2.2/src/morphopt/codesign/feaparams.py +0 -80
- morphopt-3.2.2/src/morphopt/codesign/geometry.py +0 -362
- morphopt-3.2.2/src/morphopt/optcore/__init__.py +0 -8
- morphopt-3.2.2/src/morphopt/optcore/baseobject.py +0 -48
- morphopt-3.2.2/src/morphopt/optcore/modelparams/__init__.py +0 -5
- morphopt-3.2.2/src/morphopt/optcore/modelparams/baseparam.py +0 -133
- morphopt-3.2.2/src/morphopt/optcore/modelparams/feaparams.py +0 -239
- morphopt-3.2.2/src/morphopt/optcore/modelparams/geometry.py +0 -251
- morphopt-3.2.2/src/morphopt/optcore/modelparams/materials.py +0 -175
- morphopt-3.2.2/src/morphopt/optcore/updaters.py +0 -384
- morphopt-3.2.2/src/morphopt/shapeopt/__init__.py +0 -10
- morphopt-3.2.2/src/morphopt/shapeopt/geometryinterfaces/__init__.py +0 -3
- morphopt-3.2.2/src/morphopt/shapeopt/geometryparams.py +0 -794
- morphopt-3.2.2/src/morphopt/shapeopt/objectivefuncs/surfacefairness.py +0 -38
- morphopt-3.2.2/src/morphopt/shapeopt/objectivefuncs/volumemaximization.py +0 -25
- morphopt-3.2.2/src/morphopt/shapeopt/objfunc.py +0 -13
- morphopt-3.2.2/src/morphopt/shapeopt/update_geometry.py +0 -335
- morphopt-3.2.2/src/morphopt/simp/__init__.py +0 -12
- morphopt-3.2.2/src/morphopt/simp/solver.py +0 -41
- morphopt-3.2.2/src/morphopt/simp/update_material.py +0 -324
- morphopt-3.2.2/src/morphopt/ui/codegen/generator.py +0 -586
- morphopt-3.2.2/src/morphopt/ui/model/problem.py +0 -1220
- morphopt-3.2.2/src/morphopt/ui/schemes/__init__.py +0 -18
- morphopt-3.2.2/src/morphopt/ui/schemes/base.py +0 -206
- morphopt-3.2.2/src/morphopt/ui/schemes/codesign.py +0 -155
- morphopt-3.2.2/src/morphopt/ui/schemes/shapeopt.py +0 -104
- morphopt-3.2.2/src/morphopt/ui/schemes/simp.py +0 -112
- morphopt-3.2.2/tests/test_fixedGeometry.py +0 -24
- morphopt-3.2.2/tests/test_path.py +0 -12
- morphopt-3.2.2/tests/test_ui.py +0 -62
- {morphopt-3.2.2 → morphopt-4.0.0}/README.md +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/setup.cfg +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/codesign/params.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/__init__.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/boundaryconditioninterface.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/contactinterface.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/coupleinterface.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/pointinterface.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/pressureinterface.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/referencepointinterface.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/feainterface/springinterface.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/materialinterface/__init__.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/materialinterface/homogeneousmaterial.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/optcore/modelparams/materialinterface/materialmodels.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/shapeopt/objectivefuncs/__init__.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/shapeopt/objectivefuncs/basefuncs.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/shapeopt/objectivefuncs/boundarys.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/shapeopt/objectivefuncs/distancesurface.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/simp/objectivefuncs/__init__.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/simp/objectivefuncs/basefuncs.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/simp/objectivefuncs/boundarys.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/simp/objectivefuncs/densityfield.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/simp/objectivefuncs/sensitivity.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/__main__.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/app.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/codegen/__init__.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/i18n.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/model/loaders.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/__init__.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/codeeditor.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/param_form.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/solver_editor.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/ui/widgets/values.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/utils/__init__.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/utils/gradient_check.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt/utils/history_read.py +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt.egg-info/dependency_links.txt +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt.egg-info/entry_points.txt +0 -0
- {morphopt-3.2.2 → morphopt-4.0.0}/src/morphopt.egg-info/top_level.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
|
|
|
@@ -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"]
|
|
@@ -13,6 +13,7 @@ if __sys.platform.startswith("linux"):
|
|
|
13
13
|
# region: Logging Configuration
|
|
14
14
|
import logging as __logging
|
|
15
15
|
|
|
16
|
+
|
|
16
17
|
def enable_logging(level=__logging.INFO, log_file=None, file_log_level=__logging.DEBUG):
|
|
17
18
|
"""
|
|
18
19
|
Enable logging for the FEA package.
|
|
@@ -25,7 +26,7 @@ def enable_logging(level=__logging.INFO, log_file=None, file_log_level=__logging
|
|
|
25
26
|
the path to a log file where logs will be written. If None, logs will only be printed to the console.
|
|
26
27
|
file_log_level : int
|
|
27
28
|
the logging level for the log file. Default is logging.INFO.
|
|
28
|
-
|
|
29
|
+
|
|
29
30
|
Examples
|
|
30
31
|
--------
|
|
31
32
|
>>> import torchfea
|
|
@@ -33,59 +34,70 @@ def enable_logging(level=__logging.INFO, log_file=None, file_log_level=__logging
|
|
|
33
34
|
"""
|
|
34
35
|
logger = __logging.getLogger(__name__)
|
|
35
36
|
logger.setLevel(min(level, file_log_level))
|
|
36
|
-
|
|
37
|
+
|
|
37
38
|
# clear existing handlers to avoid duplicate logs
|
|
38
39
|
logger.handlers.clear()
|
|
39
|
-
|
|
40
|
+
|
|
40
41
|
# logging to console
|
|
41
42
|
console = __logging.StreamHandler()
|
|
42
43
|
console.setLevel(level)
|
|
43
|
-
console.setFormatter(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
console.setFormatter(
|
|
45
|
+
__logging.Formatter(
|
|
46
|
+
"%(asctime)s | %(levelname)-7s | %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
|
|
47
|
+
)
|
|
48
|
+
)
|
|
47
49
|
logger.addHandler(console)
|
|
48
|
-
|
|
50
|
+
|
|
49
51
|
# logging to file if log_file is provided
|
|
50
52
|
if log_file:
|
|
51
|
-
file_handler = __logging.FileHandler(log_file, encoding=
|
|
53
|
+
file_handler = __logging.FileHandler(log_file, encoding="utf-8")
|
|
52
54
|
file_handler.setLevel(file_log_level)
|
|
53
|
-
file_handler.setFormatter(
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
file_handler.setFormatter(
|
|
56
|
+
__logging.Formatter(
|
|
57
|
+
"%(asctime)s | %(levelname)-7s | %(name)s | %(message)s"
|
|
58
|
+
)
|
|
59
|
+
)
|
|
56
60
|
logger.addHandler(file_handler)
|
|
57
61
|
|
|
58
62
|
return logger
|
|
59
|
-
# endregion
|
|
60
|
-
|
|
61
63
|
|
|
62
64
|
|
|
65
|
+
# endregion
|
|
63
66
|
|
|
64
67
|
|
|
65
|
-
# 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
|
|
66
71
|
from .optcore.controller import Controller
|
|
72
|
+
from .optcore.history import History
|
|
67
73
|
from .optcore.modelparams import (
|
|
68
|
-
|
|
69
|
-
|
|
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,
|
|
70
91
|
)
|
|
71
92
|
from .optcore.solver import Solver
|
|
72
|
-
from .optcore.modelparams import Params
|
|
73
93
|
from .optcore.updaters import Updaters
|
|
74
|
-
from .
|
|
75
|
-
from .
|
|
76
|
-
from .optcore.baseobject import BaseObject
|
|
77
|
-
# endregion
|
|
78
|
-
|
|
79
|
-
from .opt_runner import start_optimization, debug_optimization
|
|
94
|
+
from .shapeopt import BoundaryPartInterface, UpdaterBoundaryPart
|
|
95
|
+
from .simp import SIMP_BSPFieldMaterials, UpdaterSIMPMaterial
|
|
80
96
|
|
|
81
|
-
# region modules
|
|
82
|
-
from . import shapeopt
|
|
83
|
-
from . import codesign
|
|
84
|
-
from . import simp
|
|
85
97
|
# endregion
|
|
86
|
-
|
|
87
98
|
# region scripts
|
|
88
99
|
from .utils import check_gradients, get_controller
|
|
100
|
+
|
|
89
101
|
# endregion
|
|
90
102
|
|
|
91
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,17 +40,16 @@ 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
|
-
"""Stores the geometry used by InwardCurvatureRadius."""
|
|
53
48
|
self.surface_start = int(surface_start)
|
|
54
49
|
"""Stores the surface start used by InwardCurvatureRadius."""
|
|
55
|
-
self.surface_ids =
|
|
50
|
+
self.surface_ids = (
|
|
51
|
+
None if surface_ids is None else [int(i) for i in surface_ids]
|
|
52
|
+
)
|
|
56
53
|
"""Stores the surface ids used by InwardCurvatureRadius."""
|
|
57
54
|
|
|
58
55
|
self.margin = margin
|
|
@@ -73,7 +70,9 @@ class InwardCurvatureRadius(BaseConstraints):
|
|
|
73
70
|
"""Stores the normal sign used by InwardCurvatureRadius."""
|
|
74
71
|
|
|
75
72
|
@staticmethod
|
|
76
|
-
def _principal_curvatures(
|
|
73
|
+
def _principal_curvatures(
|
|
74
|
+
rdu: torch.Tensor, rdu2: torch.Tensor, eps: float = 1e-12
|
|
75
|
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
|
77
76
|
normal0 = torch.cross(rdu[:, :, 1], rdu[:, :, 0], dim=1)
|
|
78
77
|
normal = normal0 / (normal0.norm(dim=1, keepdim=True) + eps)
|
|
79
78
|
|
|
@@ -93,26 +92,45 @@ class InwardCurvatureRadius(BaseConstraints):
|
|
|
93
92
|
k2 = 0.5 * (tr - root)
|
|
94
93
|
return k1, k2
|
|
95
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
|
+
|
|
96
110
|
def _selected_surface_ids(self, n_surface: int) -> list[int]:
|
|
97
111
|
if self.surface_ids is None:
|
|
98
112
|
return [i for i in range(max(self.surface_start, 0), n_surface)]
|
|
99
113
|
return [i for i in self.surface_ids if 0 <= i < n_surface]
|
|
100
114
|
|
|
101
|
-
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:
|
|
102
123
|
loss = torch.zeros(1, device=r[0].device, dtype=r[0].dtype)
|
|
103
124
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
margin_abs = (self.margin_ratio * t) + self.margin
|
|
109
|
-
margin_abs = max(float(margin_abs), 1e-12)
|
|
110
|
-
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
|
|
111
129
|
|
|
112
|
-
|
|
113
|
-
|
|
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)
|
|
114
133
|
|
|
115
|
-
for i in self._selected_surface_ids(len(r)):
|
|
116
134
|
k1, k2 = self._principal_curvatures(rdu[i], rdu2[i])
|
|
117
135
|
|
|
118
136
|
k1_in = torch.relu(self.normal_sign * k1)
|
|
@@ -120,16 +138,23 @@ class InwardCurvatureRadius(BaseConstraints):
|
|
|
120
138
|
k_inward = torch.maximum(k1_in, k2_in)
|
|
121
139
|
|
|
122
140
|
violation = k_inward - k_limit
|
|
123
|
-
index, penalty = self.barrier_function(
|
|
141
|
+
index, penalty = self.barrier_function(
|
|
142
|
+
violation, barrier_thre, self.barrier_ratio, self.p
|
|
143
|
+
)
|
|
124
144
|
if index.numel() > 0:
|
|
125
145
|
# Strong amplification: base polynomial barrier + exponential hard-violation boost.
|
|
126
146
|
rel_violation = (violation[index] / barrier_thre).clamp(min=0.0)
|
|
127
|
-
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
|
+
)
|
|
128
152
|
penalty_strong = self.penalty_scale * penalty * hard_boost
|
|
129
153
|
loss = loss + (self.scaler[i][index] * penalty_strong).sum()
|
|
130
154
|
|
|
131
155
|
return loss
|
|
132
156
|
|
|
157
|
+
|
|
133
158
|
class OffsetSurfaceMinThickness(BaseConstraints):
|
|
134
159
|
"""
|
|
135
160
|
Enforce a minimum distance on the offset surface to reduce self-intersection risk.
|
|
@@ -140,7 +165,6 @@ class OffsetSurfaceMinThickness(BaseConstraints):
|
|
|
140
165
|
|
|
141
166
|
def __init__(
|
|
142
167
|
self,
|
|
143
|
-
geometry: CodesignGeometry,
|
|
144
168
|
surface_start: int = 1,
|
|
145
169
|
surface_ids: list[int] | None = None,
|
|
146
170
|
min_distance: float | None = None,
|
|
@@ -155,13 +179,22 @@ class OffsetSurfaceMinThickness(BaseConstraints):
|
|
|
155
179
|
normal_min: float = -0.7,
|
|
156
180
|
normal_max: float = -0.5,
|
|
157
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
|
+
"""
|
|
158
191
|
super().__init__()
|
|
159
192
|
|
|
160
|
-
self.geometry = geometry
|
|
161
|
-
"""Stores the geometry used by OffsetSurfaceMinThickness."""
|
|
162
193
|
self.surface_start = int(surface_start)
|
|
163
194
|
"""Stores the surface start used by OffsetSurfaceMinThickness."""
|
|
164
|
-
self.surface_ids =
|
|
195
|
+
self.surface_ids = (
|
|
196
|
+
None if surface_ids is None else [int(i) for i in surface_ids]
|
|
197
|
+
)
|
|
165
198
|
"""Stores the surface ids used by OffsetSurfaceMinThickness."""
|
|
166
199
|
|
|
167
200
|
self.min_distance = min_distance
|
|
@@ -203,33 +236,60 @@ class OffsetSurfaceMinThickness(BaseConstraints):
|
|
|
203
236
|
normals = normal0 / (normal0.norm(dim=1, keepdim=True) + 1e-12)
|
|
204
237
|
return normals
|
|
205
238
|
|
|
206
|
-
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."""
|
|
207
243
|
normals = self._surface_normals(rdu_sf=rdu_sf)
|
|
208
|
-
thickness = float(self.geometry.shell_thickness)
|
|
209
244
|
return r_sf + self.normal_sign * thickness * normals, normals
|
|
210
245
|
|
|
211
|
-
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
|
+
"""
|
|
212
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)
|
|
213
268
|
|
|
214
269
|
self._neighbor_pairs = {}
|
|
215
270
|
self._neighbor_mindist = {}
|
|
216
271
|
|
|
217
|
-
|
|
272
|
+
if self.shell_thickness <= 0:
|
|
273
|
+
return
|
|
218
274
|
|
|
219
|
-
|
|
220
|
-
dmin = float(self.min_distance) if self.min_distance is not None else (2.0 * thickness)
|
|
221
|
-
dmin = max(dmin, 1e-12)
|
|
222
|
-
search_radius = max(dmin * self.search_ratio, dmin)
|
|
275
|
+
import scipy.spatial
|
|
223
276
|
|
|
224
277
|
for sf_idx in self._selected_surface_ids(len(r0)):
|
|
225
|
-
|
|
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
|
+
)
|
|
226
284
|
|
|
227
285
|
tree = scipy.spatial.KDTree(roff.detach().cpu().numpy())
|
|
228
|
-
pairs_np = tree.query_pairs(r=search_radius, output_type=
|
|
286
|
+
pairs_np = tree.query_pairs(r=search_radius, output_type="ndarray")
|
|
229
287
|
if pairs_np.size == 0:
|
|
230
288
|
continue
|
|
231
289
|
|
|
232
|
-
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
|
+
)
|
|
233
293
|
|
|
234
294
|
dotn = (normals[pairs[0]] * normals[pairs[1]]).sum(dim=1)
|
|
235
295
|
opposition = (-dotn).clamp(min=0.0)
|
|
@@ -239,22 +299,28 @@ class OffsetSurfaceMinThickness(BaseConstraints):
|
|
|
239
299
|
self._neighbor_pairs[sf_idx] = pairs
|
|
240
300
|
self._neighbor_mindist[sf_idx] = dmin * opposition[keep]
|
|
241
301
|
|
|
242
|
-
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:
|
|
243
310
|
loss = torch.zeros(1, device=r[0].device, dtype=r[0].dtype)
|
|
244
311
|
|
|
245
|
-
if float(self.geometry.shell_thickness) <= 0:
|
|
246
|
-
return loss
|
|
247
|
-
|
|
248
|
-
thickness = float(self.geometry.shell_thickness)
|
|
249
|
-
dmin = float(self.min_distance) if self.min_distance is not None else (2.0 * thickness)
|
|
250
|
-
dmin = max(dmin, 1e-12)
|
|
251
|
-
barrier_thre = max(dmin * self.barrier_thre_ratio, 1e-12)
|
|
252
|
-
|
|
253
312
|
for sf_idx in self._selected_surface_ids(len(r)):
|
|
254
313
|
pairs = self._neighbor_pairs.get(sf_idx, None)
|
|
255
314
|
if pairs is None or pairs.numel() == 0:
|
|
256
315
|
continue
|
|
257
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
|
+
|
|
258
324
|
# Base surface normals (offset surface shares same normal direction)
|
|
259
325
|
normals = self._surface_normals(rdu_sf=rdu[sf_idx])
|
|
260
326
|
dotn = (normals[pairs[0]] * normals[pairs[1]]).sum(dim=1)
|
|
@@ -267,27 +333,42 @@ class OffsetSurfaceMinThickness(BaseConstraints):
|
|
|
267
333
|
pairs_n = pairs[:, mask_n]
|
|
268
334
|
dotn_n = dotn[mask_n]
|
|
269
335
|
mindist_n = self._neighbor_mindist[sf_idx][mask_n].to(
|
|
270
|
-
device=r[sf_idx].device, dtype=r[sf_idx].dtype
|
|
336
|
+
device=r[sf_idx].device, dtype=r[sf_idx].dtype
|
|
337
|
+
)
|
|
271
338
|
scaler_n0 = self.scaler[sf_idx][pairs_n[0]]
|
|
272
339
|
scaler_n1 = self.scaler[sf_idx][pairs_n[1]]
|
|
273
340
|
|
|
274
341
|
# Offset points and distance
|
|
275
|
-
roff, _ = self._offset_points(
|
|
342
|
+
roff, _ = self._offset_points(
|
|
343
|
+
r_sf=r[sf_idx], rdu_sf=rdu[sf_idx], thickness=thickness
|
|
344
|
+
)
|
|
276
345
|
delta = roff[pairs_n[0]] - roff[pairs_n[1]]
|
|
277
346
|
dist = torch.sqrt((delta * delta).sum(dim=1) + 1e-18)
|
|
278
347
|
|
|
279
348
|
violation = mindist_n - dist + barrier_thre
|
|
280
|
-
indexl, l = self.barrier_function(
|
|
349
|
+
indexl, l = self.barrier_function(
|
|
350
|
+
violation, barrier_thre, self.barrier_ratio, self.p
|
|
351
|
+
)
|
|
281
352
|
|
|
282
353
|
if indexl.numel() > 0:
|
|
283
354
|
# Smooth normal penalty (same as Distance class)
|
|
284
|
-
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)
|
|
285
359
|
np_smooth = (6 * np_val**2 - 15 * np_val + 10) * np_val**3
|
|
286
360
|
|
|
287
361
|
rel_violation = (violation[indexl] / barrier_thre).clamp(min=0.0)
|
|
288
|
-
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
|
+
)
|
|
289
367
|
penalty_strong = self.penalty_scale * l * hard_boost * np_smooth
|
|
290
368
|
|
|
291
|
-
loss =
|
|
369
|
+
loss = (
|
|
370
|
+
loss
|
|
371
|
+
+ (penalty_strong * scaler_n0[indexl] * scaler_n1[indexl]).sum()
|
|
372
|
+
)
|
|
292
373
|
|
|
293
374
|
return loss
|
|
@@ -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
|