compas-cem 0.7.0__py2.py3-none-any.whl → 0.8.2__py2.py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- compas_cem/__init__.py +1 -1
- compas_cem/diagrams/diagram.py +16 -0
- compas_cem/diagrams/topology/topology.py +6 -3
- compas_cem/equilibrium/__init__.py +1 -0
- compas_cem/equilibrium/force.py +9 -3
- compas_cem/equilibrium/force_numpy.py +9 -3
- compas_cem/ghpython/components/CompasCem_ConstraintEdgeDirection/code.py +17 -0
- compas_cem/ghpython/components/CompasCem_ConstraintEdgeDirection/icon.png +0 -0
- compas_cem/ghpython/components/CompasCem_ConstraintEdgeDirection/metadata.json +35 -0
- compas_cem/ghpython/components/CompasCem_ConstraintNodePolyline/code.py +16 -0
- compas_cem/ghpython/components/CompasCem_ConstraintNodePolyline/icon.png +0 -0
- compas_cem/ghpython/components/CompasCem_ConstraintNodePolyline/metadata.json +36 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ArtistColors.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ArtistForm.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ArtistTopology.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ConstrainedFormFinding.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ConstraintDeviationEdgeLength.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ConstraintEdgeDirection.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ConstraintNodeLine.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ConstraintNodePlane.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ConstraintNodePoint.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ConstraintNodePolyline.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ConstraintReactionForce.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ConstraintTrailEdgeForce.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_DisassembleForm.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_DisassembleTopology.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_EdgeDeviation.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_EdgeTrail.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_FormFinding.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_Info.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_JSONExportDiagram.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_JSONImportForm.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_JSONImportTopology.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_NodeLoad.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_NodeSupport.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_OriginNodesMove.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ParameterDeviationEdge.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ParameterNodeLoadX.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ParameterNodeLoadY.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ParameterNodeLoadZ.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ParameterNodeOriginX.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ParameterNodeOriginY.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ParameterNodeOriginZ.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ParameterTrailEdge.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_Proxy.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ResultsEdges.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_ResultsSupportNodes.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_SearchEdgeKey.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_SearchNodeKey.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_TopologyDiagram.ghuser +0 -0
- compas_cem/ghpython/components/ghuser/CompasCem_TrailsShift.ghuser +0 -0
- compas_cem/optimization/constraints/__init__.py +2 -0
- compas_cem/optimization/constraints/direction.py +191 -0
- compas_cem/optimization/constraints/polyline.py +149 -0
- compas_cem/optimization/optimizer.py +2 -2
- compas_cem/optimization/parameters/load.py +4 -2
- compas_cem/viewers/diagramobject.py +1 -0
- compas_cem-0.8.2.dist-info/AUTHORS.rst +41 -0
- {compas_cem-0.7.0.dist-info → compas_cem-0.8.2.dist-info}/METADATA +183 -189
- {compas_cem-0.7.0.dist-info → compas_cem-0.8.2.dist-info}/RECORD +63 -54
- {compas_cem-0.7.0.dist-info → compas_cem-0.8.2.dist-info}/WHEEL +1 -1
- compas_cem-0.7.0.dist-info/AUTHORS.rst +0 -41
- compas_cem-0.7.0.dist-info/entry_points.txt +0 -3
- {compas_cem-0.7.0.dist-info → compas_cem-0.8.2.dist-info}/LICENSE +0 -0
- {compas_cem-0.7.0.dist-info → compas_cem-0.8.2.dist-info}/top_level.txt +0 -0
compas_cem/__init__.py
CHANGED
compas_cem/diagrams/diagram.py
CHANGED
@@ -255,6 +255,22 @@ class Diagram(Data, NodeMixins, EdgeMixins, Network):
|
|
255
255
|
"""
|
256
256
|
return self.edge_attribute(key=edge, name="length")
|
257
257
|
|
258
|
+
def edge_plane(self, edge):
|
259
|
+
"""
|
260
|
+
Gets the projection plane at an edge.
|
261
|
+
|
262
|
+
Parameters
|
263
|
+
----------
|
264
|
+
edge : ``tuple``
|
265
|
+
The u, v edge key.
|
266
|
+
|
267
|
+
Return
|
268
|
+
------
|
269
|
+
plane : ``tuple``
|
270
|
+
The projection plane of the edge.
|
271
|
+
"""
|
272
|
+
return self.edge_attribute(key=edge, name="plane")
|
273
|
+
|
258
274
|
# ==============================================================================
|
259
275
|
# Magic methods
|
260
276
|
# ==============================================================================
|
@@ -737,7 +737,8 @@ class TopologyDiagram(Diagram, MeshMixins):
|
|
737
737
|
flag : ``bool``
|
738
738
|
``True``if the edge is in an auxiliary trail. ``False`` otherwise.
|
739
739
|
"""
|
740
|
-
|
740
|
+
aux_trails = set([tuple(edge) for edge in self.auxiliary_trails()])
|
741
|
+
if edge in set(aux_trails):
|
741
742
|
return True
|
742
743
|
return False
|
743
744
|
|
@@ -910,7 +911,7 @@ class TopologyDiagram(Diagram, MeshMixins):
|
|
910
911
|
|
911
912
|
def trail_sequences(self, key):
|
912
913
|
"""
|
913
|
-
Create a mapping between
|
914
|
+
Create a mapping between topological sequences and the nodes in a trail.
|
914
915
|
|
915
916
|
Parameters
|
916
917
|
----------
|
@@ -930,7 +931,9 @@ class TopologyDiagram(Diagram, MeshMixins):
|
|
930
931
|
|
931
932
|
def trails_sequences(self):
|
932
933
|
"""
|
933
|
-
Creates a mapping between the nodes
|
934
|
+
Creates a mapping of mappings between the nodes of the trails and the sequences.
|
935
|
+
|
936
|
+
The mapping has the form ``{trail_key: {sequence: node}}``.
|
934
937
|
|
935
938
|
Returns
|
936
939
|
-------
|
compas_cem/equilibrium/force.py
CHANGED
@@ -69,6 +69,7 @@ def equilibrium_state(topology, kmax=None, tmax=100, eta=1e-6, verbose=False, ca
|
|
69
69
|
# create data containers that describe equilibrium state
|
70
70
|
reaction_forces = {}
|
71
71
|
trail_forces = {}
|
72
|
+
trail_directions = {}
|
72
73
|
residual_vectors = {node: topology.reaction_force(node) for node in topology.nodes()}
|
73
74
|
node_xyz = {node: topology.node_coordinates(node) for node in topology.nodes()}
|
74
75
|
|
@@ -84,7 +85,7 @@ def equilibrium_state(topology, kmax=None, tmax=100, eta=1e-6, verbose=False, ca
|
|
84
85
|
# store last positions for residual
|
85
86
|
last_xyz = {k: v for k, v in node_xyz.items()}
|
86
87
|
|
87
|
-
for k in range(
|
88
|
+
for k in range(topology.number_of_sequences()): # sequences
|
88
89
|
|
89
90
|
for key, trail in topology.trails(keys=True):
|
90
91
|
|
@@ -144,12 +145,16 @@ def equilibrium_state(topology, kmax=None, tmax=100, eta=1e-6, verbose=False, ca
|
|
144
145
|
length = plength
|
145
146
|
|
146
147
|
# store next node position
|
147
|
-
|
148
|
+
nrvec = normalize_vector(rvec)
|
149
|
+
next_pos = add_vectors(pos, scale_vector(nrvec, length))
|
148
150
|
node_xyz[next_node] = next_pos
|
149
151
|
|
150
152
|
# store trail force
|
151
153
|
trail_forces[edge] = copysign(length_vector(rvec), length)
|
152
154
|
|
155
|
+
# store trail direction
|
156
|
+
trail_directions[edge] = nrvec
|
157
|
+
|
153
158
|
# store residual vector
|
154
159
|
residual_vectors[next_node] = rvec
|
155
160
|
|
@@ -185,11 +190,12 @@ def equilibrium_state(topology, kmax=None, tmax=100, eta=1e-6, verbose=False, ca
|
|
185
190
|
eq_state["node_xyz"] = node_xyz
|
186
191
|
eq_state["trail_forces"] = trail_forces
|
187
192
|
eq_state["reaction_forces"] = reaction_forces
|
193
|
+
eq_state["trail_directions"] = trail_directions
|
188
194
|
|
189
195
|
return eq_state
|
190
196
|
|
191
197
|
|
192
|
-
def form_update(form, node_xyz, trail_forces, reaction_forces):
|
198
|
+
def form_update(form, node_xyz, trail_forces, reaction_forces, **kwargs):
|
193
199
|
"""
|
194
200
|
Update the node and edge attributes of a form after equilibrating it.
|
195
201
|
"""
|
@@ -57,7 +57,7 @@ def equilibrium_state_numpy(topology, tmax=100, eta=1e-6, verbose=False, callbac
|
|
57
57
|
|
58
58
|
# output, mutable
|
59
59
|
edge_forces = {e: np.array(topology.edge_force(e)) for e in topology.edges()}
|
60
|
-
edge_lengths = {e: np.array(topology.
|
60
|
+
edge_lengths = {e: np.array(topology.edge_length_2(e)) for e in topology.edges()}
|
61
61
|
|
62
62
|
# input, immutable
|
63
63
|
# numpy
|
@@ -70,7 +70,7 @@ def equilibrium_state_numpy(topology, tmax=100, eta=1e-6, verbose=False, callbac
|
|
70
70
|
# edge planes
|
71
71
|
edge_planes = {}
|
72
72
|
for edge in topology.trail_edges():
|
73
|
-
plane = topology.
|
73
|
+
plane = topology.edge_plane(edge)
|
74
74
|
if not plane:
|
75
75
|
continue
|
76
76
|
plane = [np.array(vector) for vector in plane]
|
@@ -82,6 +82,7 @@ def equilibrium_state_numpy(topology, tmax=100, eta=1e-6, verbose=False, callbac
|
|
82
82
|
# output
|
83
83
|
reaction_forces = {}
|
84
84
|
trail_forces = {}
|
85
|
+
trail_directions = {}
|
85
86
|
|
86
87
|
for t in range(tmax): # max iterations
|
87
88
|
|
@@ -152,11 +153,15 @@ def equilibrium_state_numpy(topology, tmax=100, eta=1e-6, verbose=False, callbac
|
|
152
153
|
# compute trail force
|
153
154
|
trail_force = length_vector_numpy(rvec) # always positive
|
154
155
|
|
156
|
+
# compute trail direction by normalizing residual vector
|
155
157
|
# NOTE: to avoid NaNs, do not normalize residual vector if it is zero length
|
156
158
|
nrvec = rvec / trail_force
|
157
159
|
if np.isnan(length_vector_numpy(nrvec)):
|
158
160
|
nrvec = rvec
|
159
161
|
|
162
|
+
# store trail direction
|
163
|
+
trail_directions[edge] = nrvec
|
164
|
+
|
160
165
|
# store next node position
|
161
166
|
next_pos = pos + length * nrvec
|
162
167
|
node_xyz[next_node] = next_pos
|
@@ -203,13 +208,14 @@ def equilibrium_state_numpy(topology, tmax=100, eta=1e-6, verbose=False, callbac
|
|
203
208
|
eq_state = {}
|
204
209
|
eq_state["node_xyz"] = node_xyz
|
205
210
|
eq_state["trail_forces"] = trail_forces
|
211
|
+
eq_state["trail_directions"] = trail_directions
|
206
212
|
eq_state["reaction_forces"] = reaction_forces
|
207
213
|
|
208
214
|
# return node_xyz, trail_forces, reaction_forces
|
209
215
|
return eq_state
|
210
216
|
|
211
217
|
|
212
|
-
def form_update(form, node_xyz, trail_forces, reaction_forces):
|
218
|
+
def form_update(form, node_xyz, trail_forces, reaction_forces, **kwargs):
|
213
219
|
"""
|
214
220
|
Update the node and edge attributes of a form after equilibrating it.
|
215
221
|
"""
|
@@ -0,0 +1,17 @@
|
|
1
|
+
"""
|
2
|
+
Align the direction of a trail or a deviation edge with a target vector.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from ghpythonlib.componentbase import executingcomponent as component
|
6
|
+
|
7
|
+
from compas_cem.optimization import EdgeDirectionConstraint
|
8
|
+
from compas_rhino.geometry import RhinoVector
|
9
|
+
|
10
|
+
|
11
|
+
class EdgeDirectionConstraintComponent(component):
|
12
|
+
def RunScript(self, edge_key, vector, weight):
|
13
|
+
weight = weight or 1.0
|
14
|
+
if not edge_key or not vector:
|
15
|
+
return
|
16
|
+
vector = RhinoVector.from_geometry(vector).to_compas()
|
17
|
+
return EdgeDirectionConstraint(edge_key, vector, weight)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
{
|
2
|
+
"name": "Edge Direction Constraint",
|
3
|
+
"nickname": "EdgeDirectionConstraint",
|
4
|
+
"category": "COMPAS CEM",
|
5
|
+
"subcategory": "06_Optimization",
|
6
|
+
"description": "Align the direction of a trail or a deviation edge with a target vector.",
|
7
|
+
"exposure": 4,
|
8
|
+
|
9
|
+
"ghpython": {
|
10
|
+
"isAdvancedMode": true,
|
11
|
+
"inputParameters": [
|
12
|
+
{
|
13
|
+
"name": "edge_key",
|
14
|
+
"description": "The key of a COMPAS CEM trail or deviation edge."
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"name": "vector",
|
18
|
+
"description": "The target vector.",
|
19
|
+
"typeHintID": "vector"
|
20
|
+
},
|
21
|
+
{
|
22
|
+
"name": "weight",
|
23
|
+
"description": "The weight of the constraint in the optimization problem. Defaults to one.",
|
24
|
+
"typeHintID": "float"
|
25
|
+
}
|
26
|
+
],
|
27
|
+
"outputParameters": [
|
28
|
+
{
|
29
|
+
"name": "constraint",
|
30
|
+
"description": "A COMPAS CEM edge direction constraint.",
|
31
|
+
"optional": false
|
32
|
+
}
|
33
|
+
]
|
34
|
+
}
|
35
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
"""
|
2
|
+
Pull the position of a node to a target polyline.
|
3
|
+
"""
|
4
|
+
from ghpythonlib.componentbase import executingcomponent as component
|
5
|
+
|
6
|
+
from compas_cem.optimization import PolylineConstraint
|
7
|
+
from compas_rhino.geometry import RhinoPolyline
|
8
|
+
|
9
|
+
|
10
|
+
class PolylineConstraintComponent(component):
|
11
|
+
def RunScript(self, node_key, polyline, weight):
|
12
|
+
weight = weight or 1.0
|
13
|
+
if node_key is None or not polyline:
|
14
|
+
return
|
15
|
+
polyline = RhinoPolyline.from_geometry(polyline).to_compas()
|
16
|
+
return PolylineConstraint(node_key, polyline, weight)
|
Binary file
|
@@ -0,0 +1,36 @@
|
|
1
|
+
{
|
2
|
+
"name": "Polyline Constraint",
|
3
|
+
"nickname": "PolylineConstraint",
|
4
|
+
"category": "COMPAS CEM",
|
5
|
+
"subcategory": "06_Optimization",
|
6
|
+
"description": "Pull the position of a node to a target polyline.",
|
7
|
+
"exposure": 4,
|
8
|
+
|
9
|
+
"ghpython": {
|
10
|
+
"isAdvancedMode": true,
|
11
|
+
"inputParameters": [
|
12
|
+
{
|
13
|
+
"name": "node_key",
|
14
|
+
"description": "The key of a COMPAS CEM node.",
|
15
|
+
"typeHintID": "int"
|
16
|
+
},
|
17
|
+
{
|
18
|
+
"name": "polyline",
|
19
|
+
"description": "The target polyline.",
|
20
|
+
"typeHintID": "polyline"
|
21
|
+
},
|
22
|
+
{
|
23
|
+
"name": "weight",
|
24
|
+
"description": "The weight of the constraint in the optimization problem. Defaults to one.",
|
25
|
+
"typeHintID": "float"
|
26
|
+
}
|
27
|
+
],
|
28
|
+
"outputParameters": [
|
29
|
+
{
|
30
|
+
"name": "constraint",
|
31
|
+
"description": "A COMPAS CEM node polyline constraint.",
|
32
|
+
"optional": false
|
33
|
+
}
|
34
|
+
]
|
35
|
+
}
|
36
|
+
}
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -34,6 +34,8 @@ from .plane import * # noqa F403
|
|
34
34
|
from .line import * # noqa F403
|
35
35
|
from .force import * # noqa F403
|
36
36
|
from .length import * # noqa F403
|
37
|
+
from .direction import * # noqa F403
|
38
|
+
from .polyline import * # noqa F403
|
37
39
|
|
38
40
|
# import compas
|
39
41
|
# if not compas.IPY:
|
@@ -0,0 +1,191 @@
|
|
1
|
+
from compas.geometry import dot_vectors
|
2
|
+
from compas.geometry import normalize_vector
|
3
|
+
from compas.geometry import subtract_vectors
|
4
|
+
from compas.geometry import length_vector_sqrd
|
5
|
+
from compas.geometry import scale_vector
|
6
|
+
|
7
|
+
from compas_cem.optimization.constraints import VectorConstraint
|
8
|
+
|
9
|
+
|
10
|
+
__all__ = ["EdgeDirectionConstraint"]
|
11
|
+
|
12
|
+
|
13
|
+
class EdgeDirectionConstraint(VectorConstraint):
|
14
|
+
"""
|
15
|
+
Align the direction of a trail or a deviation edge with a target vector.
|
16
|
+
|
17
|
+
Note that the ordering of the nodes of the edge to constrain matters.
|
18
|
+
The reference direction of the edge is a vector pointing from its starting node (u),
|
19
|
+
towards its end node (v).
|
20
|
+
"""
|
21
|
+
def __init__(self, edge=None, vector=None, weight=1.0):
|
22
|
+
super(EdgeDirectionConstraint, self).__init__(edge, vector, weight)
|
23
|
+
|
24
|
+
def target(self, reference):
|
25
|
+
"""
|
26
|
+
The target, unit-length vector.
|
27
|
+
"""
|
28
|
+
target = normalize_vector(self._target)
|
29
|
+
return self._aligned_vector(target, reference)
|
30
|
+
|
31
|
+
def reference(self, data):
|
32
|
+
"""
|
33
|
+
The unitized edge direction.
|
34
|
+
"""
|
35
|
+
vector = self._vector_two_points(self.key(), data)
|
36
|
+
return self._unitized_vector(vector)
|
37
|
+
|
38
|
+
@staticmethod
|
39
|
+
def _vector_two_points(edge, data):
|
40
|
+
"""
|
41
|
+
Create a vector from the XYZ coordinates of two nodes.
|
42
|
+
"""
|
43
|
+
u, v = edge
|
44
|
+
return subtract_vectors(data["node_xyz"][v], data["node_xyz"][u])
|
45
|
+
|
46
|
+
@staticmethod
|
47
|
+
def _unitized_vector(vector):
|
48
|
+
"""
|
49
|
+
Scale a copy of a vector such that its length is equal to one.
|
50
|
+
"""
|
51
|
+
return scale_vector(vector, 1.0 / (length_vector_sqrd(vector) ** 0.5))
|
52
|
+
|
53
|
+
@staticmethod
|
54
|
+
def _aligned_vector(vector, vector_ref):
|
55
|
+
"""
|
56
|
+
Align a vector to another such that their dot product is non-negative.
|
57
|
+
"""
|
58
|
+
if dot_vectors(vector, vector_ref) < 0.0:
|
59
|
+
return scale_vector(vector, -1.0)
|
60
|
+
return vector
|
61
|
+
|
62
|
+
|
63
|
+
if __name__ == "__main__":
|
64
|
+
|
65
|
+
from math import fabs
|
66
|
+
|
67
|
+
from compas.geometry import Translation
|
68
|
+
|
69
|
+
from compas_cem.diagrams import TopologyDiagram
|
70
|
+
|
71
|
+
from compas_cem.elements import Node
|
72
|
+
from compas_cem.elements import TrailEdge
|
73
|
+
from compas_cem.elements import DeviationEdge
|
74
|
+
|
75
|
+
from compas_cem.loads import NodeLoad
|
76
|
+
from compas_cem.supports import NodeSupport
|
77
|
+
|
78
|
+
from compas_cem.equilibrium import static_equilibrium
|
79
|
+
|
80
|
+
from compas_cem.optimization import Optimizer
|
81
|
+
from compas_cem.optimization import DeviationEdgeParameter
|
82
|
+
|
83
|
+
from compas_cem.plotters import Plotter
|
84
|
+
|
85
|
+
# ------------------------------------------------------------------------------
|
86
|
+
# Instantiate a topology diagram
|
87
|
+
# ------------------------------------------------------------------------------
|
88
|
+
|
89
|
+
topology = TopologyDiagram()
|
90
|
+
|
91
|
+
# ------------------------------------------------------------------------------
|
92
|
+
# Add nodes
|
93
|
+
# ------------------------------------------------------------------------------
|
94
|
+
|
95
|
+
topology.add_node(Node(0, [0.0, 0.0, 0.0]))
|
96
|
+
topology.add_node(Node(1, [1.0, 0.0, 0.0]))
|
97
|
+
topology.add_node(Node(2, [2.5, 0.0, 0.0]))
|
98
|
+
topology.add_node(Node(3, [3.5, 0.0, 0.0]))
|
99
|
+
|
100
|
+
# ------------------------------------------------------------------------------
|
101
|
+
# Add edges
|
102
|
+
# ------------------------------------------------------------------------------
|
103
|
+
|
104
|
+
topology.add_edge(TrailEdge(1, 0, length=-1.0))
|
105
|
+
topology.add_edge(DeviationEdge(1, 2, force=-1.0))
|
106
|
+
topology.add_edge(TrailEdge(2, 3, length=-1.0))
|
107
|
+
|
108
|
+
# ------------------------------------------------------------------------------
|
109
|
+
# Add supports
|
110
|
+
# ------------------------------------------------------------------------------
|
111
|
+
|
112
|
+
topology.add_support(NodeSupport(0))
|
113
|
+
topology.add_support(NodeSupport(3))
|
114
|
+
|
115
|
+
# ------------------------------------------------------------------------------
|
116
|
+
# Add loads
|
117
|
+
# ------------------------------------------------------------------------------
|
118
|
+
|
119
|
+
topology.add_load(NodeLoad(1, [0.0, -1.0, 0.0]))
|
120
|
+
topology.add_load(NodeLoad(2, [0.0, -1.0, 0.0]))
|
121
|
+
|
122
|
+
# ------------------------------------------------------------------------------
|
123
|
+
# Build trails automatically
|
124
|
+
# ------------------------------------------------------------------------------
|
125
|
+
|
126
|
+
topology.build_trails()
|
127
|
+
|
128
|
+
# ------------------------------------------------------------------------------
|
129
|
+
# Compute a state of static equilibrium
|
130
|
+
# ------------------------------------------------------------------------------
|
131
|
+
|
132
|
+
form = static_equilibrium(topology, eta=1e-6, tmax=100, verbose=True)
|
133
|
+
|
134
|
+
# ------------------------------------------------------------------------------
|
135
|
+
# Initialize optimizer
|
136
|
+
# ------------------------------------------------------------------------------
|
137
|
+
|
138
|
+
opt = Optimizer()
|
139
|
+
|
140
|
+
# ------------------------------------------------------------------------------
|
141
|
+
# Define constraints
|
142
|
+
# ------------------------------------------------------------------------------
|
143
|
+
|
144
|
+
vector = [0.0, -2.0, 0.0]
|
145
|
+
for edge in topology.trail_edges():
|
146
|
+
constraint = EdgeDirectionConstraint(edge, vector)
|
147
|
+
opt.add_constraint(constraint)
|
148
|
+
|
149
|
+
# ------------------------------------------------------------------------------
|
150
|
+
# Define optimization parameters
|
151
|
+
# ------------------------------------------------------------------------------
|
152
|
+
|
153
|
+
for edge in topology.deviation_edges():
|
154
|
+
opt.add_parameter(DeviationEdgeParameter(edge, bound_low=10.0, bound_up=10.0))
|
155
|
+
|
156
|
+
# ------------------------------------------------------------------------------
|
157
|
+
# Optimization
|
158
|
+
# ------------------------------------------------------------------------------
|
159
|
+
|
160
|
+
form_opt = opt.solve(topology=topology,
|
161
|
+
algorithm="SLSQP",
|
162
|
+
verbose=True)
|
163
|
+
|
164
|
+
# ------------------------------------------------------------------------------
|
165
|
+
# Test
|
166
|
+
# ------------------------------------------------------------------------------
|
167
|
+
|
168
|
+
for edge in topology.deviation_edges():
|
169
|
+
force = fabs(topology.edge_force(edge))
|
170
|
+
assert force <= 1e-3
|
171
|
+
|
172
|
+
# ------------------------------------------------------------------------------
|
173
|
+
# Plot results
|
174
|
+
# ------------------------------------------------------------------------------
|
175
|
+
|
176
|
+
plotter = Plotter()
|
177
|
+
|
178
|
+
# add topology diagram to scene
|
179
|
+
plotter.add(topology, show_nodetext=True, nodesize=0.2)
|
180
|
+
|
181
|
+
# add shifted form diagram to the scene
|
182
|
+
form = form.transformed(Translation.from_vector([0.0, -1.0, 0.0]))
|
183
|
+
plotter.add(form, nodesize=0.2, show_edgetext=True, edgetext="force")
|
184
|
+
|
185
|
+
# add shifted form diagram to the scene
|
186
|
+
form_opt = form_opt.transformed(Translation.from_vector([0.0, -2.5, 0.0]))
|
187
|
+
plotter.add(form_opt, nodesize=0.2, show_edgetext=True, edgetext="force")
|
188
|
+
|
189
|
+
# show plotter contents
|
190
|
+
plotter.zoom_extents()
|
191
|
+
plotter.show()
|