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.
- morphopt/__init__.py +32 -0
- morphopt/opt_runner.py +130 -0
- morphopt/optcore/baseobject.py +48 -0
- morphopt/optcore/controller.py +404 -0
- morphopt/optcore/history.py +440 -0
- morphopt/optcore/modelparams/__init__.py +4 -0
- morphopt/optcore/modelparams/base_params.py +97 -0
- morphopt/optcore/modelparams/feamodel/__init__.py +0 -0
- morphopt/optcore/modelparams/feamodel/feainterface/__init__.py +10 -0
- morphopt/optcore/modelparams/feamodel/feainterface/basefeainterface.py +50 -0
- morphopt/optcore/modelparams/feamodel/feainterface/bodyforceinterface.py +71 -0
- morphopt/optcore/modelparams/feamodel/feainterface/boundaryconditioninterface.py +59 -0
- morphopt/optcore/modelparams/feamodel/feainterface/contactinterface.py +113 -0
- morphopt/optcore/modelparams/feamodel/feainterface/coupleinterface.py +31 -0
- morphopt/optcore/modelparams/feamodel/feainterface/pointinterface.py +79 -0
- morphopt/optcore/modelparams/feamodel/feainterface/pressureinterface.py +68 -0
- morphopt/optcore/modelparams/feamodel/feainterface/referencepointinterface.py +29 -0
- morphopt/optcore/modelparams/feamodel/feainterface/springinterface.py +104 -0
- morphopt/optcore/modelparams/feamodel/feaparams.py +264 -0
- morphopt/optcore/modelparams/geometry/__init__.py +3 -0
- morphopt/optcore/modelparams/geometry/geometryinterfaces/__init__.py +1 -0
- morphopt/optcore/modelparams/geometry/geometryinterfaces/basesurfaceinterface.py +763 -0
- morphopt/optcore/modelparams/geometry/geometryinterfaces/bspsurfaceinterface.py +723 -0
- morphopt/optcore/modelparams/geometry/geometryinterfaces/cpgeosurfaceinterface.py +374 -0
- morphopt/optcore/modelparams/geometry/geometryparams.py +715 -0
- morphopt/optcore/modelparams/materials/__init__.py +1 -0
- morphopt/optcore/modelparams/materials/materialparams.py +110 -0
- morphopt/optcore/modelparams/params.py +88 -0
- morphopt/optcore/objfunc.py +248 -0
- morphopt/optcore/solver.py +159 -0
- morphopt/optcore/updaters/__init__.py +2 -0
- morphopt/optcore/updaters/base_updater.py +110 -0
- morphopt/optcore/updaters/geometry/__init__.py +2 -0
- morphopt/optcore/updaters/geometry/objectivefuncs/__init__.py +5 -0
- morphopt/optcore/updaters/geometry/objectivefuncs/basefuncs.py +117 -0
- morphopt/optcore/updaters/geometry/objectivefuncs/boundarys.py +91 -0
- morphopt/optcore/updaters/geometry/objectivefuncs/distancesurface.py +123 -0
- morphopt/optcore/updaters/geometry/objectivefuncs/shapederivative.py +179 -0
- morphopt/optcore/updaters/geometry/objectivefuncs/surfacefairness.py +40 -0
- morphopt/optcore/updaters/geometry/update_geometry.py +347 -0
- morphopt/optcore/updaters/optimizer.py +177 -0
- morphopt/optcore/updaters/updaters.py +82 -0
- morphopt/optcore/utils/plot_history_surface.py +239 -0
- morphopt/taskoptmization.py +75 -0
- morphopt/taskui.py +458 -0
- morphopt-3.1.1.dist-info/METADATA +242 -0
- morphopt-3.1.1.dist-info/RECORD +49 -0
- morphopt-3.1.1.dist-info/WHEEL +5 -0
- 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
|