morphopt 3.1.1__py3-none-any.whl

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.

Potentially problematic release.


This version of morphopt might be problematic. Click here for more details.

Files changed (49) hide show
  1. morphopt/__init__.py +32 -0
  2. morphopt/opt_runner.py +130 -0
  3. morphopt/optcore/baseobject.py +48 -0
  4. morphopt/optcore/controller.py +404 -0
  5. morphopt/optcore/history.py +440 -0
  6. morphopt/optcore/modelparams/__init__.py +4 -0
  7. morphopt/optcore/modelparams/base_params.py +97 -0
  8. morphopt/optcore/modelparams/feamodel/__init__.py +0 -0
  9. morphopt/optcore/modelparams/feamodel/feainterface/__init__.py +10 -0
  10. morphopt/optcore/modelparams/feamodel/feainterface/basefeainterface.py +50 -0
  11. morphopt/optcore/modelparams/feamodel/feainterface/bodyforceinterface.py +71 -0
  12. morphopt/optcore/modelparams/feamodel/feainterface/boundaryconditioninterface.py +59 -0
  13. morphopt/optcore/modelparams/feamodel/feainterface/contactinterface.py +113 -0
  14. morphopt/optcore/modelparams/feamodel/feainterface/coupleinterface.py +31 -0
  15. morphopt/optcore/modelparams/feamodel/feainterface/pointinterface.py +79 -0
  16. morphopt/optcore/modelparams/feamodel/feainterface/pressureinterface.py +68 -0
  17. morphopt/optcore/modelparams/feamodel/feainterface/referencepointinterface.py +29 -0
  18. morphopt/optcore/modelparams/feamodel/feainterface/springinterface.py +104 -0
  19. morphopt/optcore/modelparams/feamodel/feaparams.py +264 -0
  20. morphopt/optcore/modelparams/geometry/__init__.py +3 -0
  21. morphopt/optcore/modelparams/geometry/geometryinterfaces/__init__.py +1 -0
  22. morphopt/optcore/modelparams/geometry/geometryinterfaces/basesurfaceinterface.py +763 -0
  23. morphopt/optcore/modelparams/geometry/geometryinterfaces/bspsurfaceinterface.py +723 -0
  24. morphopt/optcore/modelparams/geometry/geometryinterfaces/cpgeosurfaceinterface.py +374 -0
  25. morphopt/optcore/modelparams/geometry/geometryparams.py +715 -0
  26. morphopt/optcore/modelparams/materials/__init__.py +1 -0
  27. morphopt/optcore/modelparams/materials/materialparams.py +110 -0
  28. morphopt/optcore/modelparams/params.py +88 -0
  29. morphopt/optcore/objfunc.py +248 -0
  30. morphopt/optcore/solver.py +159 -0
  31. morphopt/optcore/updaters/__init__.py +2 -0
  32. morphopt/optcore/updaters/base_updater.py +110 -0
  33. morphopt/optcore/updaters/geometry/__init__.py +2 -0
  34. morphopt/optcore/updaters/geometry/objectivefuncs/__init__.py +5 -0
  35. morphopt/optcore/updaters/geometry/objectivefuncs/basefuncs.py +117 -0
  36. morphopt/optcore/updaters/geometry/objectivefuncs/boundarys.py +91 -0
  37. morphopt/optcore/updaters/geometry/objectivefuncs/distancesurface.py +123 -0
  38. morphopt/optcore/updaters/geometry/objectivefuncs/shapederivative.py +179 -0
  39. morphopt/optcore/updaters/geometry/objectivefuncs/surfacefairness.py +40 -0
  40. morphopt/optcore/updaters/geometry/update_geometry.py +347 -0
  41. morphopt/optcore/updaters/optimizer.py +177 -0
  42. morphopt/optcore/updaters/updaters.py +82 -0
  43. morphopt/optcore/utils/plot_history_surface.py +239 -0
  44. morphopt/taskoptmization.py +75 -0
  45. morphopt/taskui.py +458 -0
  46. morphopt-3.1.1.dist-info/METADATA +242 -0
  47. morphopt-3.1.1.dist-info/RECORD +49 -0
  48. morphopt-3.1.1.dist-info/WHEEL +5 -0
  49. morphopt-3.1.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,59 @@
1
+ from operator import index
2
+ import torch
3
+
4
+ from torchfea import FEAController
5
+ from .basefeainterface import BaseFEAInterface
6
+ from torchfea.model.boundarys import Boundary_Condition, Boundary_Condition_RP
7
+
8
+
9
+ class BoundaryConditionInterface(BaseFEAInterface):
10
+ """
11
+ Nonlinear axial spring connecting a reference point to a fixed ground point.
12
+ Values order (5 floats): [k, L0, Px, Py, Pz]
13
+ k: stiffness
14
+ L0: rest length
15
+ P*: ground point coordinates
16
+ """
17
+
18
+ def __init__(self, instance_name: str, set_nodes_name: str, index_dof: list[int] = [0, 1, 2]) -> None:
19
+ self.instance_name = instance_name
20
+ self.set_nodes_name = set_nodes_name
21
+ self.index_dof = index_dof
22
+ super().__init__()
23
+
24
+ @property
25
+ def num_values(self) -> int:
26
+ return 0
27
+
28
+ def modify_fea(self, fe: FEAController, name: str) -> None:
29
+ bcobj = Boundary_Condition(instance_name=self.instance_name, set_nodes_name=self.set_nodes_name, indexDoF=self.index_dof)
30
+ fe.assembly.add_boundary(bcobj, name)
31
+
32
+
33
+ class BoundaryConditionRPInterface(BaseFEAInterface):
34
+ """
35
+ Reference-point boundary condition (BCRP) interface.
36
+
37
+ This interface creates a Boundary_Condition_RP on a reference point and
38
+ allows updating the prescribed values per degree of freedom.
39
+
40
+ - index_dof: list of DoF indices to prescribe (e.g. [0,1,2] for translations)
41
+ - values: list of floats with the same length as index_dof representing the
42
+ prescribed values for each specified DoF.
43
+ """
44
+
45
+ def __init__(self, rp_name: str, index_dof: list[int] = [0, 1, 2]) -> None:
46
+ self.rp_name = rp_name
47
+ self.index_dof = index_dof
48
+ super().__init__()
49
+
50
+ @property
51
+ def num_values(self) -> int:
52
+ return 0
53
+
54
+ def modify_fea(self, fe: FEAController, name: str) -> None:
55
+ """Create and register the RP boundary condition in the FE assembly."""
56
+ bcobj = Boundary_Condition_RP(rp_name=self.rp_name, indexDoF=self.index_dof)
57
+ fe.assembly.add_boundary(bcobj, name)
58
+
59
+
@@ -0,0 +1,113 @@
1
+ from torchfea import FEAController
2
+ import torch
3
+ from .basefeainterface import BaseFEAInterface
4
+
5
+ # Prefer the same import style as PressureInterface
6
+ try:
7
+ from torchfea.model.loads.contact import Contact, ContactSelf # type: ignore
8
+ except Exception: # Fallback if package structure differs
9
+ # Defer import to get_fea_load to avoid import-time failures
10
+ Contact = None # type: ignore
11
+ ContactSelf = None # type: ignore
12
+
13
+
14
+ class ContactInterface(BaseFEAInterface):
15
+ """
16
+ Contact load interface between two instances.
17
+
18
+ Notes:
19
+ - This contact interface has no amplitude values (num_values = 0).
20
+ It is registered once and applied as a constraint; no per-step values required.
21
+ - Penalty parameters can be specified via init args.
22
+
23
+ This interface describes a contact constraint between surface1 of instance1
24
+ and surface2 of instance2. It integrates with LoadsParams so contact loads
25
+ are injected consistently during solve and sensitivity sub-steps.
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ instance_name1: str,
31
+ surface_name1: str,
32
+ instance_name2: str,
33
+ surface_name2: str,
34
+ penalty_threshold_h: float = 3.0,
35
+ penalty_start_f: float | None = None,
36
+ penalty_end_f: float | None = None,
37
+ ) -> None:
38
+ super().__init__()
39
+ self.instance_name1 = instance_name1
40
+ self.surface_name1 = surface_name1
41
+ self.instance_name2 = instance_name2
42
+ self.surface_name2 = surface_name2
43
+ self.penalty_threshold_h = float(penalty_threshold_h)
44
+ self.penalty_start_f = None if penalty_start_f is None else float(penalty_start_f)
45
+ self.penalty_end_f = None if penalty_end_f is None else float(penalty_end_f)
46
+
47
+ @property
48
+ def num_values(self) -> int:
49
+ return 0
50
+
51
+ def modify_fea(self, fe: FEAController, name: str):
52
+
53
+ # Lazy import to be robust to different package layouts
54
+ global Contact
55
+ if Contact is None:
56
+ from FEA.assemble.loads.contact import Contact as _Contact # type: ignore
57
+ Contact = _Contact # type: ignore
58
+
59
+ kwargs = dict(
60
+ instance_name1=self.instance_name1,
61
+ instance_name2=self.instance_name2,
62
+ surface_name1=self.surface_name1,
63
+ surface_name2=self.surface_name2,
64
+ penalty_threshold_h=self.penalty_threshold_h,
65
+ )
66
+ if self.penalty_start_f is not None:
67
+ kwargs["penalty_start_f"] = self.penalty_start_f
68
+ if self.penalty_end_f is not None:
69
+ kwargs["penalty_end_f"] = self.penalty_end_f
70
+ loadobj = Contact(**kwargs) # type: ignore
71
+
72
+ fe.assembly.add_load(loadobj, name)
73
+
74
+
75
+
76
+ class ContactSelfInterface(BaseFEAInterface):
77
+ """
78
+ Self-contact load interface for a single instance surface.
79
+
80
+ Notes:
81
+ - This self-contact interface has no amplitude values (num_values = 0).
82
+ It is registered once and applied as a constraint; no per-step values required.
83
+
84
+ Adds a self-contact constraint for a given surface on one instance.
85
+ """
86
+
87
+ def __init__(
88
+ self,
89
+ instance_name: str,
90
+ surface_name: str,
91
+ penalty_threshold_h: float | None = None,
92
+ ) -> None:
93
+ super().__init__()
94
+ self.instance_name = instance_name
95
+ self.surface_name = surface_name
96
+ self.penalty_threshold_h = None if penalty_threshold_h is None else float(penalty_threshold_h)
97
+
98
+ @property
99
+ def num_values(self) -> int:
100
+ return 0
101
+
102
+ def modify_fea(self, fe: FEAController, name: str):
103
+ from torchfea.model.loads.contact import ContactSelf
104
+
105
+ kwargs = dict(
106
+ instance_name=self.instance_name,
107
+ surface_name=self.surface_name,
108
+ )
109
+ if self.penalty_threshold_h is not None:
110
+ kwargs["penalty_threshold_h"] = self.penalty_threshold_h
111
+ loadobj = ContactSelf(**kwargs) # type: ignore
112
+
113
+ fe.assembly.add_load(loadobj, name)
@@ -0,0 +1,31 @@
1
+ from operator import index
2
+ import torch
3
+
4
+ from torchfea import FEAController
5
+ from .basefeainterface import BaseFEAInterface
6
+ from torchfea.model.constraints import Couple
7
+
8
+
9
+ class CoupleInterface(BaseFEAInterface):
10
+ """
11
+ Nonlinear axial spring connecting a reference point to a fixed ground point.
12
+ Values order (5 floats): [k, L0, Px, Py, Pz]
13
+ k: stiffness
14
+ L0: rest length
15
+ P*: ground point coordinates
16
+ """
17
+
18
+ def __init__(self, rp_name: str, instance_name: str, set_nodes_name: str) -> None:
19
+ self.rp_name = rp_name
20
+ self.instance_name = instance_name
21
+ self.set_nodes_name = set_nodes_name
22
+ super().__init__()
23
+
24
+ @property
25
+ def num_values(self) -> int:
26
+ return 0
27
+
28
+ def modify_fea(self, fe: FEAController, name: str) -> None:
29
+ loadobj = Couple(instance_name=self.instance_name, set_nodes_name=self.set_nodes_name, rp_name=self.rp_name)
30
+ fe.assembly.add_constraint(loadobj, name)
31
+
@@ -0,0 +1,79 @@
1
+ import numpy as np
2
+ import torch
3
+
4
+ from torchfea import FEAController
5
+ from .basefeainterface import BaseFEAInterface
6
+ from torchfea.model.loads import Concentrate_Force, Moment
7
+
8
+
9
+ class ConcentratedForceInterface(BaseFEAInterface):
10
+ """
11
+ Concentrated (point) force load interface.
12
+
13
+ Applies a force vector at a reference point (RP).
14
+
15
+ Values (list[float], length=3):
16
+ - [0] Fx, [1] Fy, [2] Fz
17
+ """
18
+
19
+ def __init__(self, rp_name: str) -> None:
20
+ self.rp_name = rp_name
21
+ super().__init__()
22
+
23
+ @property
24
+ def num_values(self) -> int:
25
+ return 3
26
+
27
+ def modify_fea(self, fe: FEAController, name: str) -> None:
28
+ loadobj = Concentrate_Force(rp_name=self.rp_name, force=self.force)
29
+ fe.assembly.add_load(loadobj, name)
30
+
31
+ def apply_fea_value(self, fe: FEAController, name: str) -> None:
32
+ fe.assembly.get_load(name).force = self.force
33
+
34
+ @property
35
+ def force(self) -> list[float]:
36
+ return self._values.copy()
37
+
38
+ @force.setter
39
+ def force(self, value: list[float]) -> None:
40
+ if len(value) != 3:
41
+ raise ValueError("force must be a list of 3 floats [Fx, Fy, Fz].")
42
+ self._values = list(map(float, value))
43
+
44
+
45
+ class ConcentratedMomentInterface(BaseFEAInterface):
46
+ """
47
+ Concentrated (point) moment load interface.
48
+
49
+ Applies a moment vector at a reference point (RP).
50
+
51
+ Values (list[float], length=3):
52
+ - [0] Mx, [1] My, [2] Mz
53
+ """
54
+
55
+ def __init__(self, rp_name: str) -> None:
56
+ super().__init__()
57
+ self.rp_name = rp_name
58
+
59
+ @property
60
+ def num_values(self) -> int:
61
+ return 3
62
+
63
+ def modify_fea(self, fe: FEAController, name: str) -> None:
64
+ loadobj = Moment(rp_name=self.rp_name, moment=self.moment)
65
+ fe.assembly.add_load(loadobj, name)
66
+
67
+ def apply_fea_value(self, fe: FEAController, name: str) -> None:
68
+ fe.assembly.get_load(name).moment = self.moment
69
+
70
+ @property
71
+ def moment(self) -> list[float]:
72
+ return self._values.copy()
73
+
74
+ @moment.setter
75
+ def moment(self, value: list[float]) -> None:
76
+ if len(value) != 3:
77
+ raise ValueError("moment must be a list of 3 floats [Mx, My, Mz].")
78
+ self._values = list(map(float, value))
79
+
@@ -0,0 +1,68 @@
1
+
2
+ from torchfea import FEAController
3
+ import numpy as np
4
+ import torch
5
+ from .basefeainterface import BaseFEAInterface
6
+ from torchfea.model.loads import Pressure
7
+ class PressureInterface(BaseFEAInterface):
8
+ """
9
+ Surface pressure load interface.
10
+
11
+ Values (list[float], length=1):
12
+ - [0] pressure (scalar)
13
+ """
14
+
15
+ def __init__(self, surface_name: str, instance_name: str = 'final_model'):
16
+ """
17
+ Initialize the PressureInterface class with default values.
18
+ """
19
+ super().__init__()
20
+ """
21
+ pressure: A 2D list to hold the pressure values.
22
+ """
23
+ self.instance_name = instance_name
24
+ """
25
+ instance_name: The name of the instance.
26
+ """
27
+ self.surface_name = surface_name
28
+ """
29
+ surface_name: The name of the surface.
30
+ """
31
+
32
+ @property
33
+ def pressure(self) -> float:
34
+ """
35
+ Get the pressure values.
36
+
37
+ Returns:
38
+ float: The pressure values.
39
+ """
40
+ return float(self._values[0])
41
+
42
+ @pressure.setter
43
+ def pressure(self, value: float) -> None:
44
+ """
45
+ Set the pressure values.
46
+
47
+ Args:
48
+ value (float): The new pressure value.
49
+ """
50
+ self._values = [float(value)]
51
+
52
+ @property
53
+ def num_values(self) -> int:
54
+ """
55
+ Get the number of pressure variables.
56
+
57
+ Returns:
58
+ int: The number of pressure variables.
59
+ """
60
+ return 1
61
+
62
+ def modify_fea(self, fe: FEAController, name: str) -> None:
63
+ pressure_load = Pressure(instance_name=self.instance_name,surface_set=self.surface_name,pressure=self.pressure)
64
+ fe.assembly.add_load(pressure_load, name)
65
+
66
+ def apply_fea_value(self, fe: FEAController, name: str) -> None:
67
+ pressure_load: Pressure = fe.assembly.get_load(name)
68
+ pressure_load.pressure = self.pressure
@@ -0,0 +1,29 @@
1
+ from operator import index
2
+ import torch
3
+
4
+ from torchfea import FEAController
5
+ from .basefeainterface import BaseFEAInterface
6
+ from torchfea.model import ReferencePoint
7
+
8
+
9
+ class ReferencePointInterface(BaseFEAInterface):
10
+ """
11
+ Nonlinear axial spring connecting a reference point to a fixed ground point.
12
+ Values order (5 floats): [k, L0, Px, Py, Pz]
13
+ k: stiffness
14
+ L0: rest length
15
+ P*: ground point coordinates
16
+ """
17
+
18
+ def __init__(self, rp_location: list[float]) -> None:
19
+ self.rp_location = rp_location
20
+ super().__init__()
21
+
22
+ @property
23
+ def num_values(self) -> int:
24
+ return 0
25
+
26
+ def modify_fea(self, fe: FEAController, name: str) -> None:
27
+ rp = ReferencePoint(node=self.rp_location)
28
+ fe.assembly.add_reference_point(rp, name)
29
+
@@ -0,0 +1,104 @@
1
+ import torch
2
+
3
+ from torchfea import FEAController
4
+ from .basefeainterface import BaseFEAInterface
5
+ from torchfea.model.loads.spring import Spring_RP_Point, Spring_RP_RP
6
+
7
+
8
+ class SpringToGroundInterface(BaseFEAInterface):
9
+ """
10
+ Nonlinear axial spring connecting a reference point to a fixed ground point.
11
+ Values order (5 floats): [k, L0, Px, Py, Pz]
12
+ k: stiffness
13
+ L0: rest length
14
+ P*: ground point coordinates
15
+ """
16
+
17
+ def __init__(self, rp_name: str) -> None:
18
+ self.rp_name = rp_name
19
+ super().__init__()
20
+
21
+ @property
22
+ def num_values(self) -> int:
23
+ return 5
24
+
25
+ # Convenience accessors
26
+ @property
27
+ def k(self) -> float:
28
+ return float(self._values[0])
29
+
30
+ @k.setter
31
+ def k(self, value: float) -> None:
32
+ self._values[0] = float(value)
33
+
34
+ @property
35
+ def rest_length(self) -> float:
36
+ return float(self._values[1])
37
+
38
+ @rest_length.setter
39
+ def rest_length(self, value: float) -> None:
40
+ self._values[1] = float(value)
41
+
42
+ @property
43
+ def point(self) -> list[float]:
44
+ return [float(self._values[2]), float(self._values[3]), float(self._values[4])]
45
+
46
+ @point.setter
47
+ def point(self, value: list[float]) -> None:
48
+ if len(value) != 3:
49
+ raise ValueError("point must be a list of 3 floats [x, y, z].")
50
+ self._values[2:5] = [float(value[0]), float(value[1]), float(value[2])]
51
+
52
+ def modify_fea(self, fe: FEAController, name: str) -> None:
53
+ loadobj = Spring_RP_Point(rp_name=self.rp_name, point=self.point, k=self.k, rest_length=self.rest_length)
54
+ fe.assembly.add_load(loadobj, name)
55
+
56
+ def apply_fea_value(self, fe: FEAController, name: str) -> None:
57
+ loadobj: Spring_RP_Point = fe.assembly.get_load(name)
58
+ loadobj.k = self.k
59
+ loadobj.rest_length = self.rest_length
60
+ loadobj.point = self.point
61
+
62
+
63
+ class SpringBetweenRPsInterface(BaseFEAInterface):
64
+ """
65
+ Nonlinear axial spring connecting two reference points (RP-RP).
66
+
67
+ Values order (2 floats): [k, L0]
68
+ - k: stiffness
69
+ - L0: rest length
70
+ """
71
+
72
+ def __init__(self, rp_name1: str, rp_name2: str) -> None:
73
+ self.rp_name1 = rp_name1
74
+ self.rp_name2 = rp_name2
75
+ super().__init__()
76
+
77
+ @property
78
+ def num_values(self) -> int:
79
+ return 2
80
+
81
+ @property
82
+ def k(self) -> float:
83
+ return float(self._values[0])
84
+
85
+ @k.setter
86
+ def k(self, value: float) -> None:
87
+ self._values[0] = float(value)
88
+
89
+ @property
90
+ def rest_length(self) -> float:
91
+ return float(self._values[1])
92
+
93
+ @rest_length.setter
94
+ def rest_length(self, value: float) -> None:
95
+ self._values[1] = float(value)
96
+
97
+ def modify_fea(self, fe: FEAController, name: str) -> None:
98
+ loadobj = Spring_RP_RP(rp_name1=self.rp_name1, rp_name2=self.rp_name2, k=self.k, rest_length=self.rest_length)
99
+ fe.assembly.add_load(loadobj, name)
100
+
101
+ def apply_fea_value(self, fe: FEAController, name: str) -> None:
102
+ loadobj: Spring_RP_RP = fe.assembly.get_load(name)
103
+ loadobj.k = self.k
104
+ loadobj.rest_length = self.rest_length