midas-civil 1.4.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.
- midas_civil/_BoundaryChangeAssignment.py +278 -0
- midas_civil/__init__.py +51 -0
- midas_civil/_analysiscontrol.py +585 -0
- midas_civil/_boundary.py +888 -0
- midas_civil/_construction.py +1004 -0
- midas_civil/_element.py +1346 -0
- midas_civil/_group.py +337 -0
- midas_civil/_load.py +967 -0
- midas_civil/_loadcomb.py +159 -0
- midas_civil/_mapi.py +249 -0
- midas_civil/_material.py +1692 -0
- midas_civil/_model.py +522 -0
- midas_civil/_movingload.py +1479 -0
- midas_civil/_node.py +532 -0
- midas_civil/_result_table.py +929 -0
- midas_civil/_result_test.py +5455 -0
- midas_civil/_section/_TapdbSecSS.py +175 -0
- midas_civil/_section/__init__.py +413 -0
- midas_civil/_section/_compositeSS.py +283 -0
- midas_civil/_section/_dbSecSS.py +164 -0
- midas_civil/_section/_offsetSS.py +53 -0
- midas_civil/_section/_pscSS copy.py +455 -0
- midas_civil/_section/_pscSS.py +822 -0
- midas_civil/_section/_tapPSC12CellSS.py +565 -0
- midas_civil/_section/_unSupp.py +58 -0
- midas_civil/_settlement.py +161 -0
- midas_civil/_temperature.py +677 -0
- midas_civil/_tendon.py +1016 -0
- midas_civil/_thickness.py +147 -0
- midas_civil/_utils.py +529 -0
- midas_civil/_utilsFunc/__init__.py +0 -0
- midas_civil/_utilsFunc/_line2plate.py +636 -0
- midas_civil/_view.py +891 -0
- midas_civil/_view_trial.py +430 -0
- midas_civil/_visualise.py +347 -0
- midas_civil-1.4.1.dist-info/METADATA +74 -0
- midas_civil-1.4.1.dist-info/RECORD +40 -0
- midas_civil-1.4.1.dist-info/WHEEL +5 -0
- midas_civil-1.4.1.dist-info/licenses/LICENSE +21 -0
- midas_civil-1.4.1.dist-info/top_level.txt +1 -0
midas_civil/_boundary.py
ADDED
|
@@ -0,0 +1,888 @@
|
|
|
1
|
+
from ._mapi import MidasAPI
|
|
2
|
+
# from ._model import *
|
|
3
|
+
from ._node import Node
|
|
4
|
+
from ._group import Group
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def convList(item):
|
|
10
|
+
if type(item)!=list:
|
|
11
|
+
return [item]
|
|
12
|
+
else:
|
|
13
|
+
return item
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# ----- Extend for list of nodes/elems -----
|
|
17
|
+
|
|
18
|
+
def _ADD_Support(self):
|
|
19
|
+
if isinstance(self.NODE,int):
|
|
20
|
+
Boundary.Support.sups.append(self)
|
|
21
|
+
elif isinstance(self.NODE,list):
|
|
22
|
+
for nID in self.NODE:
|
|
23
|
+
Boundary.Support(nID,self.CONST,self.GROUP)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Boundary:
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def create(cls):
|
|
30
|
+
"""Creates Boundary elements in MIDAS Civil NX"""
|
|
31
|
+
if cls.Support.sups!=[]: cls.Support.create()
|
|
32
|
+
if cls.ElasticLink.links!=[]: cls.ElasticLink.create()
|
|
33
|
+
if cls.RigidLink.links!=[]: cls.RigidLink.create()
|
|
34
|
+
if cls.MLFC.func!=[]: cls.RigidLink.create()
|
|
35
|
+
if cls.PointSpring.springs!=[]: cls.PointSpring.create()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def delete(cls):
|
|
40
|
+
"""Delets Boundary elements from MIDAS Civil NX and Python"""
|
|
41
|
+
cls.Support.delete()
|
|
42
|
+
cls.ElasticLink.delete()
|
|
43
|
+
cls.RigidLink.delete()
|
|
44
|
+
cls.MLFC.delete()
|
|
45
|
+
cls.PointSpring.delete()
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def clear(cls):
|
|
49
|
+
"""Clear Boundary elements from Python"""
|
|
50
|
+
cls.Support.clear()
|
|
51
|
+
cls.ElasticLink.clear()
|
|
52
|
+
cls.RigidLink.clear()
|
|
53
|
+
cls.MLFC.clear()
|
|
54
|
+
cls.PointSpring.clear()
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def sync(cls):
|
|
58
|
+
"""Sync Boundary elements from MIDAS Civil NX to Python"""
|
|
59
|
+
cls.Support.sync()
|
|
60
|
+
cls.ElasticLink.sync()
|
|
61
|
+
cls.RigidLink.sync()
|
|
62
|
+
cls.MLFC.sync()
|
|
63
|
+
cls.PointSpring.sync()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Support:
|
|
67
|
+
"""Create Support Object in Python \n\nNode ID, Constraint, Boundary Group. Sample: Support(3, "1110000") or Support(3, "pin"). \nValid inputs for DOF are 1s and 0s or "pin", "fix", "free" (no capital letters).
|
|
68
|
+
\nIf more than 7 characters are entered, then only first 7 characters will be considered to define constraint."""
|
|
69
|
+
sups = []
|
|
70
|
+
def __init__(self, nodeID:int, constraint:str, group:str = ""):
|
|
71
|
+
if not isinstance(constraint, str): constraint = str(constraint)
|
|
72
|
+
if constraint == "pin": constraint = "111"
|
|
73
|
+
if constraint == "fix": constraint = "1111111"
|
|
74
|
+
if constraint == "roller": constraint = "001"
|
|
75
|
+
if len(constraint) < 7: constraint = constraint + '0' * (7-len(constraint))
|
|
76
|
+
if len(constraint) > 7: constraint = constraint[:7]
|
|
77
|
+
string = ''.join(['1' if char != '0' else '0' for char in constraint])
|
|
78
|
+
|
|
79
|
+
# Check if group exists, create if not
|
|
80
|
+
if group != "":
|
|
81
|
+
chk = 0
|
|
82
|
+
a = [v['NAME'] for v in Group.Boundary.json()["Assign"].values()]
|
|
83
|
+
if group in a:
|
|
84
|
+
chk = 1
|
|
85
|
+
if chk == 0:
|
|
86
|
+
Group.Boundary(group)
|
|
87
|
+
|
|
88
|
+
self.NODE = nodeID
|
|
89
|
+
self.CONST = string
|
|
90
|
+
self.GROUP = group
|
|
91
|
+
self.ID = len(Boundary.Support.sups) + 1
|
|
92
|
+
_ADD_Support(self)
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
def json(cls):
|
|
96
|
+
"""Creates JSON from Supports objects defined in Python"""
|
|
97
|
+
json = {"Assign":{}}
|
|
98
|
+
ng = []
|
|
99
|
+
for i in Boundary.Support.sups:
|
|
100
|
+
if i.NODE in Node.ids:
|
|
101
|
+
json["Assign"][i.NODE] = {"ITEMS":
|
|
102
|
+
[{"ID": i.ID,
|
|
103
|
+
"CONSTRAINT":i.CONST,
|
|
104
|
+
"GROUP_NAME": i.GROUP}]
|
|
105
|
+
}
|
|
106
|
+
if i.NODE not in Node.ids: ng.append(i.NODE)
|
|
107
|
+
if len(ng) > 0: print("These nodes are not defined: ", ng)
|
|
108
|
+
return json
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def create():
|
|
112
|
+
"""Creates Supports in MIDAS Civil NX"""
|
|
113
|
+
MidasAPI("PUT","/db/cons",Boundary.Support.json())
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def get():
|
|
117
|
+
"""Get the JSON of Supports from MIDAS Civil NX"""
|
|
118
|
+
return MidasAPI("GET","/db/cons")
|
|
119
|
+
|
|
120
|
+
@staticmethod
|
|
121
|
+
def sync():
|
|
122
|
+
"""Sync Supports from MIDAS Civil NX to Python"""
|
|
123
|
+
a = Boundary.Support.get()
|
|
124
|
+
if a != {'message': ''}:
|
|
125
|
+
if list(a['CONS'].keys()) != []:
|
|
126
|
+
Boundary.Support.sups = []
|
|
127
|
+
for j in a['CONS'].keys():
|
|
128
|
+
Boundary.Support(int(j),a['CONS'][j]['ITEMS'][0]['CONSTRAINT'])
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def delete():
|
|
132
|
+
"""Delete Supports from MIDAS Civil NX and Python"""
|
|
133
|
+
Boundary.Support.clear()
|
|
134
|
+
return MidasAPI("DELETE","/db/cons")
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def clear():
|
|
138
|
+
"""Delete Supports from Python"""
|
|
139
|
+
Boundary.Support.sups=[]
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
#---------------------------------------------------------------------------------------------------------------
|
|
145
|
+
#Class to define Elastic Links:
|
|
146
|
+
class ElasticLink:
|
|
147
|
+
|
|
148
|
+
# list to store all link instances
|
|
149
|
+
links = []
|
|
150
|
+
|
|
151
|
+
def __init__(self,
|
|
152
|
+
i_node: int,
|
|
153
|
+
j_node: int,
|
|
154
|
+
group: str = "",
|
|
155
|
+
link_type: str = "GEN",
|
|
156
|
+
sdx: float = 0,
|
|
157
|
+
sdy: float = 0,
|
|
158
|
+
sdz: float = 0,
|
|
159
|
+
srx: float = 0,
|
|
160
|
+
sry: float = 0,
|
|
161
|
+
srz: float = 0,
|
|
162
|
+
shear: bool = False,
|
|
163
|
+
dr_y: float = 0.5,
|
|
164
|
+
dr_z: float = 0.5,
|
|
165
|
+
beta_angle: float = 0,
|
|
166
|
+
dir: str = "Dy",
|
|
167
|
+
func_id: int = 1,
|
|
168
|
+
distance_ratio: float = 0,
|
|
169
|
+
id: int = None, ):
|
|
170
|
+
"""
|
|
171
|
+
Elastic link.
|
|
172
|
+
Parameters:
|
|
173
|
+
i_node: The first node ID
|
|
174
|
+
j_node: The second node ID
|
|
175
|
+
group: The group name (default "")
|
|
176
|
+
link_type: Type of link (GEN, RIGID, TENS, COMP, MULTI LINEAR, SADDLE, RAIL INTERACT) (default "GEN")
|
|
177
|
+
sdx: Spring stiffness in X direction (default 0)
|
|
178
|
+
sdy: Spring stiffness in Y direction (default 0)
|
|
179
|
+
sdz: Spring stiffness in Z direction (default 0)
|
|
180
|
+
srx: Rotational stiffness around X axis (default 0)
|
|
181
|
+
sry: Rotational stiffness around Y axis (default 0)
|
|
182
|
+
srz: Rotational stiffness around Z axis (default 0)
|
|
183
|
+
shear: Consider shear effects (default False)
|
|
184
|
+
dr_y: Distance ratio for Y direction (default 0.5)
|
|
185
|
+
dr_z: Distance ratio for Z direction (default 0.5)
|
|
186
|
+
beta_angle: Rotation angle in degrees (default 0)
|
|
187
|
+
dir: Direction for MULTI LINEAR or RAIL INTERACT links (default "Dy")
|
|
188
|
+
func_id: Function ID for MULTI LINEAR or RAIL INTERACT links (default 1)
|
|
189
|
+
distance_ratio: Distance ratio for MULTI LINEAR or RAIL INTERACT links (default 0)
|
|
190
|
+
id: The link ID (optional)
|
|
191
|
+
|
|
192
|
+
Examples:
|
|
193
|
+
```python
|
|
194
|
+
# General link with all stiffness parameters
|
|
195
|
+
ElasticLink(1, 2, "Group1", "GEN", 1000, 1000, 1000, 100, 100, 100)
|
|
196
|
+
# Rigid link
|
|
197
|
+
ElasticLink(3, 4, "Group2", "RIGID")
|
|
198
|
+
# Tension-only link
|
|
199
|
+
ElasticLink(5, 6, "Group3", "TENS", 500)
|
|
200
|
+
# Compression-only link
|
|
201
|
+
ElasticLink(7, 8, "Group4", "COMP", 500)
|
|
202
|
+
# Rail Track Type link
|
|
203
|
+
ElasticLink(9, 10, "Group5", "RAIL INTERACT", dir="Dy", func_id=1)
|
|
204
|
+
# Multi Linear Link
|
|
205
|
+
ElasticLink(11, 12, "Group6", "MULTI LINEAR", dir="Dy", func_id=1)
|
|
206
|
+
# Saddle type link
|
|
207
|
+
ElasticLink(13, 14, "Group7", "SADDLE")
|
|
208
|
+
```
|
|
209
|
+
"""
|
|
210
|
+
# Check if group exists, create if not
|
|
211
|
+
if group != "":
|
|
212
|
+
chk = 0
|
|
213
|
+
a = [v['NAME'] for v in Group.Boundary.json()["Assign"].values()]
|
|
214
|
+
if group in a:
|
|
215
|
+
chk = 1
|
|
216
|
+
if chk == 0:
|
|
217
|
+
Group.Boundary(group)
|
|
218
|
+
|
|
219
|
+
# Validate link type
|
|
220
|
+
valid_types = ["GEN", "RIGID", "TENS", "COMP", "MULTI LINEAR", "SADDLE", "RAIL INTERACT"]
|
|
221
|
+
if link_type not in valid_types:
|
|
222
|
+
link_type = "GEN"
|
|
223
|
+
|
|
224
|
+
# Validate direction for MULTI LINEAR
|
|
225
|
+
if link_type == "MULTI LINEAR":
|
|
226
|
+
valid_directions = ["Dx", "Dy", "Dz", "Rx", "Ry", "Rz"]
|
|
227
|
+
if dir not in valid_directions:
|
|
228
|
+
dir = "Dy"
|
|
229
|
+
|
|
230
|
+
# Validate direction for RAIL INTERACT
|
|
231
|
+
if link_type == "RAIL INTERACT":
|
|
232
|
+
valid_directions = ["Dy", "Dz"]
|
|
233
|
+
if dir not in valid_directions:
|
|
234
|
+
dir = "Dy"
|
|
235
|
+
|
|
236
|
+
self.I_NODE = i_node
|
|
237
|
+
self.J_NODE = j_node
|
|
238
|
+
self.GROUP_NAME = group
|
|
239
|
+
self.LINK_TYPE = link_type
|
|
240
|
+
self.ANGLE = beta_angle
|
|
241
|
+
|
|
242
|
+
# Parameters for all link types
|
|
243
|
+
self.SDx = sdx
|
|
244
|
+
self.SDy = sdy
|
|
245
|
+
self.SDz = sdz
|
|
246
|
+
self.SRx = srx
|
|
247
|
+
self.SRy = sry
|
|
248
|
+
self.SRz = srz
|
|
249
|
+
self.bSHEAR = shear
|
|
250
|
+
self.DR_Y = dr_y
|
|
251
|
+
self.DR_Z = dr_z
|
|
252
|
+
|
|
253
|
+
# Parameters for MULTI LINEAR and RAIL INTERACT
|
|
254
|
+
self.Direction = dir
|
|
255
|
+
self.Function_ID = func_id
|
|
256
|
+
self.Distance_ratio = distance_ratio
|
|
257
|
+
|
|
258
|
+
# Auto-assign ID if not provided
|
|
259
|
+
if id is None:
|
|
260
|
+
self.ID = len(Boundary.ElasticLink.links) + 1
|
|
261
|
+
else:
|
|
262
|
+
self.ID = id
|
|
263
|
+
|
|
264
|
+
# Add to static list
|
|
265
|
+
Boundary.ElasticLink.links.append(self)
|
|
266
|
+
|
|
267
|
+
@classmethod
|
|
268
|
+
def json(cls):
|
|
269
|
+
"""
|
|
270
|
+
Converts ElasticLink data to JSON format for API submission.
|
|
271
|
+
Example:
|
|
272
|
+
# Get the JSON data for all links
|
|
273
|
+
json_data = ElasticLink.json()
|
|
274
|
+
print(json_data)
|
|
275
|
+
"""
|
|
276
|
+
data = {}
|
|
277
|
+
|
|
278
|
+
for link in cls.links:
|
|
279
|
+
link_data = {
|
|
280
|
+
"NODE": [link.I_NODE, link.J_NODE],
|
|
281
|
+
"LINK": link.LINK_TYPE,
|
|
282
|
+
"ANGLE": link.ANGLE,
|
|
283
|
+
"BNGR_NAME": link.GROUP_NAME
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
# Add type-specific parameters
|
|
287
|
+
if link.LINK_TYPE == "GEN":
|
|
288
|
+
link_data["R_S"] = [False] * 6
|
|
289
|
+
link_data["SDR"] = [
|
|
290
|
+
link.SDx,
|
|
291
|
+
link.SDy,
|
|
292
|
+
link.SDz,
|
|
293
|
+
link.SRx,
|
|
294
|
+
link.SRy,
|
|
295
|
+
link.SRz
|
|
296
|
+
]
|
|
297
|
+
link_data["bSHEAR"] = link.bSHEAR
|
|
298
|
+
if link.bSHEAR:
|
|
299
|
+
link_data["DR"] = [link.DR_Y, link.DR_Z]
|
|
300
|
+
else:
|
|
301
|
+
link_data["DR"] = [0.5, 0.5]
|
|
302
|
+
|
|
303
|
+
elif link.LINK_TYPE in ["TENS", "COMP"]:
|
|
304
|
+
link_data["SDR"] = [link.SDx, 0, 0, 0, 0, 0]
|
|
305
|
+
link_data["bSHEAR"] = link.bSHEAR
|
|
306
|
+
if link.bSHEAR:
|
|
307
|
+
link_data["DR"] = [link.DR_Y, link.DR_Z]
|
|
308
|
+
else:
|
|
309
|
+
link_data["DR"] = [0.5, 0.5]
|
|
310
|
+
|
|
311
|
+
elif link.LINK_TYPE == "MULTI LINEAR":
|
|
312
|
+
direction_mapping = {
|
|
313
|
+
"Dx": 0, "Dy": 1, "Dz": 2, "Rx": 3, "Ry": 4, "Rz": 5
|
|
314
|
+
}
|
|
315
|
+
link_data["DIR"] = direction_mapping.get(link.Direction, 0)
|
|
316
|
+
link_data["MLFC"] = link.Function_ID
|
|
317
|
+
link_data["DRENDI"] = link.Distance_ratio
|
|
318
|
+
|
|
319
|
+
elif link.LINK_TYPE == "RAIL INTERACT":
|
|
320
|
+
direction_mapping = {"Dy": 1, "Dz": 2}
|
|
321
|
+
link_data["DIR"] = direction_mapping.get(link.Direction, 0)
|
|
322
|
+
link_data["RLFC"] = link.Function_ID
|
|
323
|
+
link_data["bSHEAR"] = link.bSHEAR
|
|
324
|
+
if link.bSHEAR:
|
|
325
|
+
link_data["DEENDI"] = link.Distance_ratio
|
|
326
|
+
else:
|
|
327
|
+
link_data["DR"] = [0.5, 0.5]
|
|
328
|
+
|
|
329
|
+
data[link.ID] = link_data
|
|
330
|
+
|
|
331
|
+
return {"Assign": data}
|
|
332
|
+
|
|
333
|
+
@classmethod
|
|
334
|
+
def create(cls):
|
|
335
|
+
"""
|
|
336
|
+
Sends all ElasticLink data to Midas API.
|
|
337
|
+
Example:
|
|
338
|
+
ElasticLink(1, 2, "Group1", "GEN", 1000, 1000, 1000, 100, 100, 100)
|
|
339
|
+
# Send to the API
|
|
340
|
+
ElasticLink.create()
|
|
341
|
+
"""
|
|
342
|
+
MidasAPI("PUT", "/db/elnk", cls.json())
|
|
343
|
+
|
|
344
|
+
@classmethod
|
|
345
|
+
def get(cls):
|
|
346
|
+
"""
|
|
347
|
+
Retrieves ElasticLink data from Midas API.
|
|
348
|
+
Example:
|
|
349
|
+
api_data = ElasticLink.get()
|
|
350
|
+
print(api_data)
|
|
351
|
+
"""
|
|
352
|
+
return MidasAPI("GET", "/db/elnk")
|
|
353
|
+
|
|
354
|
+
@classmethod
|
|
355
|
+
def sync(cls):
|
|
356
|
+
"""
|
|
357
|
+
Updates the ElasticLink class with data from the Midas API.
|
|
358
|
+
Example:
|
|
359
|
+
ElasticLink.sync()
|
|
360
|
+
"""
|
|
361
|
+
cls.links = []
|
|
362
|
+
a = cls.get()
|
|
363
|
+
|
|
364
|
+
if a != {'message': ''}:
|
|
365
|
+
for link_id, link_data in a.get("ELNK", {}).items():
|
|
366
|
+
sdx = sdy = sdz = srx = sry = srz = 0
|
|
367
|
+
shear = False
|
|
368
|
+
dr_y = dr_z = 0.5
|
|
369
|
+
direction = "Dy"
|
|
370
|
+
func_id = 1
|
|
371
|
+
distance_ratio = 0
|
|
372
|
+
|
|
373
|
+
if link_data["LINK"] == "GEN" and "SDR" in link_data:
|
|
374
|
+
sdx, sdy, sdz, srx, sry, srz = link_data["SDR"]
|
|
375
|
+
shear = link_data.get("bSHEAR")
|
|
376
|
+
if shear and "DR" in link_data:
|
|
377
|
+
dr_y, dr_z = link_data["DR"]
|
|
378
|
+
|
|
379
|
+
elif link_data["LINK"] in ["TENS", "COMP"] and "SDR" in link_data:
|
|
380
|
+
sdx = link_data["SDR"][0]
|
|
381
|
+
shear = link_data.get("bSHEAR")
|
|
382
|
+
if shear and "DR" in link_data:
|
|
383
|
+
dr_y, dr_z = link_data["DR"]
|
|
384
|
+
|
|
385
|
+
elif link_data["LINK"] == "MULTI LINEAR":
|
|
386
|
+
dir_mapping = {0: "Dx", 1: "Dy", 2: "Dz", 3: "Rx", 4: "Ry", 5: "Rz"}
|
|
387
|
+
direction = dir_mapping.get(link_data.get("DIR"), "Dy")
|
|
388
|
+
func_id = link_data.get("MLFC")
|
|
389
|
+
distance_ratio = link_data.get("DRENDI")
|
|
390
|
+
|
|
391
|
+
elif link_data["LINK"] == "RAIL INTERACT":
|
|
392
|
+
dir_mapping = {1: "Dy", 2: "Dz"}
|
|
393
|
+
direction = dir_mapping.get(link_data.get("DIR"), "Dy")
|
|
394
|
+
func_id = link_data.get("RLFC")
|
|
395
|
+
shear = link_data.get("bSHEAR")
|
|
396
|
+
if shear and "DEENDI" in link_data:
|
|
397
|
+
distance_ratio = link_data["DEENDI"]
|
|
398
|
+
|
|
399
|
+
Boundary.ElasticLink(
|
|
400
|
+
link_data["NODE"][0],
|
|
401
|
+
link_data["NODE"][1],
|
|
402
|
+
link_data.get("BNGR_NAME"),
|
|
403
|
+
link_data["LINK"],
|
|
404
|
+
sdx, sdy, sdz, srx, sry, srz,
|
|
405
|
+
shear, dr_y, dr_z,
|
|
406
|
+
link_data.get("ANGLE"),
|
|
407
|
+
direction, func_id, distance_ratio,
|
|
408
|
+
int(link_id)
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
@classmethod
|
|
412
|
+
def delete(cls):
|
|
413
|
+
"""
|
|
414
|
+
Deletes all elastic links from the database and resets the class.
|
|
415
|
+
Example:sss
|
|
416
|
+
ElasticLink.delete()
|
|
417
|
+
"""
|
|
418
|
+
cls.clear()
|
|
419
|
+
|
|
420
|
+
@classmethod
|
|
421
|
+
def clear(cls):
|
|
422
|
+
"""
|
|
423
|
+
Deletes all elastic links from the database and resets the class.
|
|
424
|
+
Example:sss
|
|
425
|
+
ElasticLink.delete()
|
|
426
|
+
"""
|
|
427
|
+
cls.links = []
|
|
428
|
+
#---------------------------------------------------------------------------------------------------------------
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
#Class to define Rigid Links:
|
|
432
|
+
class RigidLink:
|
|
433
|
+
|
|
434
|
+
links = []
|
|
435
|
+
ids = [0]
|
|
436
|
+
|
|
437
|
+
def __init__(self,
|
|
438
|
+
master_node: int,
|
|
439
|
+
slave_nodes: list,
|
|
440
|
+
group: str = "",
|
|
441
|
+
dof: int = 111111,
|
|
442
|
+
id: int = None):
|
|
443
|
+
"""
|
|
444
|
+
Rigid link.
|
|
445
|
+
Parameters:
|
|
446
|
+
master_node: The first node ID
|
|
447
|
+
slave_nodes: The second node ID
|
|
448
|
+
group: The group name (default "")
|
|
449
|
+
dof: Fixity of link (default 111111)
|
|
450
|
+
id: The link ID (optional)
|
|
451
|
+
|
|
452
|
+
Examples:
|
|
453
|
+
```python
|
|
454
|
+
# General link with all stiffness parameters
|
|
455
|
+
RigidLink(1, [2,3], "Group1", 111000,1)
|
|
456
|
+
```
|
|
457
|
+
"""
|
|
458
|
+
|
|
459
|
+
# Check if group exists, create if not
|
|
460
|
+
if group != "":
|
|
461
|
+
chk = 0
|
|
462
|
+
a = [v['NAME'] for v in Group.Boundary.json()["Assign"].values()]
|
|
463
|
+
if group in a:
|
|
464
|
+
chk = 1
|
|
465
|
+
if chk == 0:
|
|
466
|
+
Group.Boundary(group)
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
self.M_NODE = master_node
|
|
470
|
+
self.S_NODE = convList(slave_nodes)
|
|
471
|
+
self.GROUP_NAME = group
|
|
472
|
+
self.DOF = dof
|
|
473
|
+
|
|
474
|
+
# Auto-assign ID if not provided
|
|
475
|
+
if id is None:
|
|
476
|
+
self.ID = max(Boundary.RigidLink.ids) + 1
|
|
477
|
+
else:
|
|
478
|
+
self.ID = id
|
|
479
|
+
|
|
480
|
+
# Add to static list
|
|
481
|
+
Boundary.RigidLink.links.append(self)
|
|
482
|
+
Boundary.RigidLink.ids.append(self.ID)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
@classmethod
|
|
486
|
+
def json(cls):
|
|
487
|
+
"""
|
|
488
|
+
Converts RigidLink data to JSON format for API submission.
|
|
489
|
+
Example:
|
|
490
|
+
# Get the JSON data for all links
|
|
491
|
+
json_data = RigidLink.json()
|
|
492
|
+
print(json_data)
|
|
493
|
+
"""
|
|
494
|
+
json = {"Assign": {}}
|
|
495
|
+
for link in cls.links:
|
|
496
|
+
if link.M_NODE not in list(json["Assign"].keys()):
|
|
497
|
+
json["Assign"][link.M_NODE] = {"ITEMS": []}
|
|
498
|
+
|
|
499
|
+
json["Assign"][link.M_NODE]["ITEMS"].append({
|
|
500
|
+
"ID": link.ID,
|
|
501
|
+
"GROUP_NAME": link.GROUP_NAME,
|
|
502
|
+
"DOF": link.DOF,
|
|
503
|
+
"S_NODE": convList(link.S_NODE),
|
|
504
|
+
})
|
|
505
|
+
return json
|
|
506
|
+
|
|
507
|
+
@classmethod
|
|
508
|
+
def create(cls):
|
|
509
|
+
"""
|
|
510
|
+
Sends all RigidLink data to Midas API.
|
|
511
|
+
RigidLink.create()
|
|
512
|
+
"""
|
|
513
|
+
MidasAPI("PUT", "/db/RIGD", cls.json())
|
|
514
|
+
|
|
515
|
+
@classmethod
|
|
516
|
+
def get(cls):
|
|
517
|
+
"""
|
|
518
|
+
Retrieves Rigid Link data from Midas API.
|
|
519
|
+
Example:
|
|
520
|
+
api_data = RigidLink.get()
|
|
521
|
+
print(api_data)
|
|
522
|
+
"""
|
|
523
|
+
return MidasAPI("GET", "/db/RIGD")
|
|
524
|
+
|
|
525
|
+
@classmethod
|
|
526
|
+
def sync(cls):
|
|
527
|
+
"""
|
|
528
|
+
Updates the RigidLink class with data from the Midas API.
|
|
529
|
+
Example:
|
|
530
|
+
RigidLink.sync()
|
|
531
|
+
"""
|
|
532
|
+
cls.links = []
|
|
533
|
+
a = cls.get()
|
|
534
|
+
if a != {'message': ''}:
|
|
535
|
+
for i in a['RIGD'].keys():
|
|
536
|
+
for j in range(len(a['RIGD'][i]['ITEMS'])):
|
|
537
|
+
itm = a['RIGD'][i]['ITEMS'][j]
|
|
538
|
+
Boundary.RigidLink(int(i),itm['S_NODE'],itm['GROUP_NAME'],itm['DOF'],itm['ID'])
|
|
539
|
+
|
|
540
|
+
@classmethod
|
|
541
|
+
def delete(cls):
|
|
542
|
+
"""
|
|
543
|
+
Deletes all rigid links from the database and resets the class.
|
|
544
|
+
Example:
|
|
545
|
+
ElasticLink.delete()
|
|
546
|
+
"""
|
|
547
|
+
cls.clear()
|
|
548
|
+
return MidasAPI("DELETE", "/db/RIGD")
|
|
549
|
+
|
|
550
|
+
@classmethod
|
|
551
|
+
def clear(cls):
|
|
552
|
+
"""
|
|
553
|
+
Deletes all rigid links from the database and resets the class.
|
|
554
|
+
Example:
|
|
555
|
+
ElasticLink.delete()
|
|
556
|
+
"""
|
|
557
|
+
cls.links = []
|
|
558
|
+
#---------------------------------------------------------------------------------------------------------------
|
|
559
|
+
|
|
560
|
+
class MLFC:
|
|
561
|
+
|
|
562
|
+
func = []
|
|
563
|
+
_id = []
|
|
564
|
+
|
|
565
|
+
def __init__(self, name:str, type:str='FORCE', symm:bool=True, data:list=[[0,0],[1,1]], id:int=None):
|
|
566
|
+
"""
|
|
567
|
+
Force-Deformation Function constructor.
|
|
568
|
+
|
|
569
|
+
Parameters:
|
|
570
|
+
name (str): The name for the Force-Deformation Function.
|
|
571
|
+
type (str, optional): Type of function, either "FORCE" or "MOMENT". Defaults to "FORCE".
|
|
572
|
+
symm (bool, optional): Defines if the function is symmetric (True) or unsymmetric (False). Defaults to True.
|
|
573
|
+
data (list[list[float]], optional): A list of [X, Y] coordinate pairs defining the function curve. Required.
|
|
574
|
+
- For "FORCE" type: X is displacement, Y is force.
|
|
575
|
+
- For "MOMENT" type: X is rotation in radians, Y is moment.
|
|
576
|
+
Defaults to [[0,0],[1,1]].
|
|
577
|
+
id (int, optional): The function ID. If not provided, it will be auto-assigned.
|
|
578
|
+
|
|
579
|
+
Examples:
|
|
580
|
+
```python
|
|
581
|
+
# Create a symmetric force vs. displacement function
|
|
582
|
+
Boundary.MLFC(name="MyForceFunction", type="FORCE", symm=True, data=[[0,0], [0.1, 100], [0.2, 150]])
|
|
583
|
+
|
|
584
|
+
# Create an unsymmetric moment vs. rotation function with a specific ID
|
|
585
|
+
Boundary.MLFC(name="MyMomentFunction", type="MOMENT", symm=False, data=[[0,0], [0.01, 500], [0.02, 750]], id=5)
|
|
586
|
+
```
|
|
587
|
+
"""
|
|
588
|
+
self.NAME = name
|
|
589
|
+
self.TYPE = type
|
|
590
|
+
self.SYMM = symm
|
|
591
|
+
self.DATA = data
|
|
592
|
+
|
|
593
|
+
self.X = [dat[0] for dat in self.DATA]
|
|
594
|
+
self.Y = [dat[1] for dat in self.DATA]
|
|
595
|
+
|
|
596
|
+
# Auto-assign ID if not provided
|
|
597
|
+
if id is None:
|
|
598
|
+
if __class__._id == []:
|
|
599
|
+
self.ID = 1
|
|
600
|
+
else:
|
|
601
|
+
self.ID = max(__class__._id) + 1
|
|
602
|
+
else:
|
|
603
|
+
self.ID = id
|
|
604
|
+
|
|
605
|
+
__class__._id.append(self.ID)
|
|
606
|
+
__class__.func.append(self)
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
@classmethod
|
|
611
|
+
def json(cls):
|
|
612
|
+
|
|
613
|
+
json = {"Assign": {}}
|
|
614
|
+
for fn in cls.func:
|
|
615
|
+
json["Assign"][fn.ID]={
|
|
616
|
+
"NAME": fn.NAME,
|
|
617
|
+
"TYPE": fn.TYPE,
|
|
618
|
+
"SYMM": fn.SYMM,
|
|
619
|
+
"FUNC_ID": 0,
|
|
620
|
+
"ITEMS": []
|
|
621
|
+
}
|
|
622
|
+
for i in range(len(fn.X)):
|
|
623
|
+
json["Assign"][fn.ID]["ITEMS"].append({"X":fn.X[i],"Y":fn.Y[i]})
|
|
624
|
+
return json
|
|
625
|
+
|
|
626
|
+
@classmethod
|
|
627
|
+
def create(cls):
|
|
628
|
+
"""
|
|
629
|
+
Sends all FUNC data.
|
|
630
|
+
"""
|
|
631
|
+
MidasAPI("PUT", "/db/MLFC", cls.json())
|
|
632
|
+
|
|
633
|
+
@classmethod
|
|
634
|
+
def get(cls):
|
|
635
|
+
"""
|
|
636
|
+
Retrieves data.
|
|
637
|
+
"""
|
|
638
|
+
return MidasAPI("GET", "/db/MLFC")
|
|
639
|
+
|
|
640
|
+
@classmethod
|
|
641
|
+
def sync(cls):
|
|
642
|
+
|
|
643
|
+
cls.links = []
|
|
644
|
+
a = cls.get()
|
|
645
|
+
if a != {'message': ''}:
|
|
646
|
+
for i in a['MLFC'].keys():
|
|
647
|
+
name = a['MLFC'][i]["NAME"]
|
|
648
|
+
type = a['MLFC'][i]["TYPE"]
|
|
649
|
+
symm = a['MLFC'][i]["SYMM"]
|
|
650
|
+
data = []
|
|
651
|
+
for j in (a['MLFC'][i]['ITEMS']):
|
|
652
|
+
data.append([j["X"],j["Y"]])
|
|
653
|
+
Boundary.MLFC(name,type,symm,data,int(i))
|
|
654
|
+
|
|
655
|
+
@classmethod
|
|
656
|
+
def delete(cls):
|
|
657
|
+
"""
|
|
658
|
+
Deletes all func from the database and resets the class.
|
|
659
|
+
"""
|
|
660
|
+
cls.clear()
|
|
661
|
+
return MidasAPI("DELETE", "/db/MLFC")
|
|
662
|
+
|
|
663
|
+
@classmethod
|
|
664
|
+
def clear(cls):
|
|
665
|
+
"""
|
|
666
|
+
Deletes all func from the database and resets the class.
|
|
667
|
+
"""
|
|
668
|
+
cls.links = []
|
|
669
|
+
|
|
670
|
+
#--------------------------------------------------------------------------------------------------------------------------------------
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
class PointSpring:
|
|
674
|
+
"""Create Point Spring Object in Python"""
|
|
675
|
+
springs = []
|
|
676
|
+
|
|
677
|
+
def __init__(self,
|
|
678
|
+
node: int,
|
|
679
|
+
spring_type: str = "LINEAR",
|
|
680
|
+
group: str = "",
|
|
681
|
+
stiffness: list = None,
|
|
682
|
+
fixed_option: list = None,
|
|
683
|
+
damping: list = None,
|
|
684
|
+
direction: str = "Dx+",
|
|
685
|
+
normal_vector: list = None,
|
|
686
|
+
function_id: int = 1,
|
|
687
|
+
id: int = None):
|
|
688
|
+
"""
|
|
689
|
+
Point Spring constructor.
|
|
690
|
+
|
|
691
|
+
Parameters:
|
|
692
|
+
node: Node ID where spring is applied
|
|
693
|
+
spring_type: Type of spring ("LINEAR", "COMP", "TENS", "MULTI")
|
|
694
|
+
group: Group name (default "")
|
|
695
|
+
stiffness: Spring stiffness values [SDx, SDy, SDz, SRx, SRy, SRz] or single value for COMP/TENS
|
|
696
|
+
fixed_option: Fixed option array [Boolean, 6] for LINEAR type
|
|
697
|
+
damping: Damping values [Cx, Cy, Cz, CRx, CRy, CRz] (if provided, damping is enabled)
|
|
698
|
+
direction: Direction string ("Dx+", "Dx-", "Dy+", "Dy-", "Dz+", "Dz-", "Vector")
|
|
699
|
+
normal_vector: Normal vector [x, y, z] when direction is "Vector"
|
|
700
|
+
function_id: Function ID for MULTI type
|
|
701
|
+
id: Spring ID (optional, auto-assigned if None)
|
|
702
|
+
|
|
703
|
+
Examples:
|
|
704
|
+
# Linear spring
|
|
705
|
+
PointSpring(1, "LINEAR", "Group1", stiffness=[1000, 1000, 1000, 100, 100, 100])
|
|
706
|
+
|
|
707
|
+
# Compression only spring
|
|
708
|
+
PointSpring(2, "COMP", "Group2", stiffness=5000, direction="Dz+")
|
|
709
|
+
|
|
710
|
+
# Tension only spring with vector direction
|
|
711
|
+
PointSpring(3, "TENS", "Group3", stiffness=3000, direction="Vector", normal_vector=[0, -1, -1])
|
|
712
|
+
|
|
713
|
+
# Multi-linear spring
|
|
714
|
+
PointSpring(4, "MULTI", "Group4", direction="Dz+", function_id=1)
|
|
715
|
+
"""
|
|
716
|
+
|
|
717
|
+
# Check if group exists, create if not
|
|
718
|
+
if group != "":
|
|
719
|
+
chk = 0
|
|
720
|
+
a = [v['NAME'] for v in Group.Boundary.json()["Assign"].values()]
|
|
721
|
+
if group in a:
|
|
722
|
+
chk = 1
|
|
723
|
+
if chk == 0:
|
|
724
|
+
Group.Boundary(group)
|
|
725
|
+
|
|
726
|
+
# Validate spring type
|
|
727
|
+
valid_types = ["LINEAR", "COMP", "TENS", "MULTI"]
|
|
728
|
+
if spring_type not in valid_types:
|
|
729
|
+
spring_type = "LINEAR"
|
|
730
|
+
|
|
731
|
+
self.NODE = node
|
|
732
|
+
self.TYPE = spring_type
|
|
733
|
+
self.GROUP_NAME = group
|
|
734
|
+
|
|
735
|
+
# Convert direction string to integer
|
|
736
|
+
direction_map = {
|
|
737
|
+
"Dx+": 0, "Dx-": 1, "Dy+": 2, "Dy-": 3,
|
|
738
|
+
"Dz+": 4, "Dz-": 5, "Vector": 6
|
|
739
|
+
}
|
|
740
|
+
self.DIR = direction_map.get(direction, 0)
|
|
741
|
+
|
|
742
|
+
# Auto-assign ID if not provided
|
|
743
|
+
if id is None:
|
|
744
|
+
self.ID = len(Boundary.PointSpring.springs) + 1
|
|
745
|
+
else:
|
|
746
|
+
self.ID = id
|
|
747
|
+
|
|
748
|
+
# Type-specific parameters
|
|
749
|
+
if spring_type == "LINEAR":
|
|
750
|
+
self.SDR = stiffness if stiffness else [0, 0, 0, 0, 0, 0]
|
|
751
|
+
self.F_S = fixed_option if fixed_option else [False] * 6
|
|
752
|
+
# Damping logic: if damping values provided, enable damping
|
|
753
|
+
self.DAMPING = damping is not None and any(d != 0 for d in damping) if damping else False
|
|
754
|
+
self.Cr = damping if damping else [0, 0, 0, 0, 0, 0]
|
|
755
|
+
|
|
756
|
+
elif spring_type in ["COMP", "TENS"]:
|
|
757
|
+
self.STIFF = stiffness if stiffness else 0
|
|
758
|
+
self.DV = normal_vector if normal_vector else [0, 0, 0]
|
|
759
|
+
|
|
760
|
+
elif spring_type == "MULTI":
|
|
761
|
+
self.DV = normal_vector if normal_vector else [0, 0, 0]
|
|
762
|
+
self.FUNCTION = function_id
|
|
763
|
+
|
|
764
|
+
# Add to static list
|
|
765
|
+
Boundary.PointSpring.springs.append(self)
|
|
766
|
+
|
|
767
|
+
@classmethod
|
|
768
|
+
def json(cls):
|
|
769
|
+
"""
|
|
770
|
+
Converts PointSpring data to JSON format for API submission.
|
|
771
|
+
"""
|
|
772
|
+
json_data = {"Assign": {}}
|
|
773
|
+
|
|
774
|
+
for spring in cls.springs:
|
|
775
|
+
spring_data = {
|
|
776
|
+
"ID": spring.ID,
|
|
777
|
+
"TYPE": spring.TYPE,
|
|
778
|
+
"GROUP_NAME": spring.GROUP_NAME
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
# Add type-specific parameters
|
|
782
|
+
if spring.TYPE == "LINEAR":
|
|
783
|
+
spring_data["SDR"] = spring.SDR
|
|
784
|
+
spring_data["F_S"] = spring.F_S
|
|
785
|
+
spring_data["DAMPING"] = spring.DAMPING
|
|
786
|
+
if spring.DAMPING:
|
|
787
|
+
spring_data["Cr"] = spring.Cr
|
|
788
|
+
|
|
789
|
+
elif spring.TYPE in ["COMP", "TENS"]:
|
|
790
|
+
spring_data["STIFF"] = spring.STIFF
|
|
791
|
+
spring_data["DIR"] = spring.DIR
|
|
792
|
+
spring_data["DV"] = spring.DV
|
|
793
|
+
|
|
794
|
+
elif spring.TYPE == "MULTI":
|
|
795
|
+
spring_data["DIR"] = spring.DIR
|
|
796
|
+
spring_data["DV"] = spring.DV
|
|
797
|
+
spring_data["FUNCTION"] = spring.FUNCTION
|
|
798
|
+
|
|
799
|
+
json_data["Assign"][spring.NODE] = {"ITEMS": [spring_data]}
|
|
800
|
+
|
|
801
|
+
return json_data
|
|
802
|
+
|
|
803
|
+
@classmethod
|
|
804
|
+
def create(cls):
|
|
805
|
+
"""
|
|
806
|
+
Sends all PointSpring data
|
|
807
|
+
"""
|
|
808
|
+
MidasAPI("PUT", "/db/nspr", cls.json())
|
|
809
|
+
|
|
810
|
+
@classmethod
|
|
811
|
+
def get(cls):
|
|
812
|
+
"""
|
|
813
|
+
Retrieves PointSpring data
|
|
814
|
+
"""
|
|
815
|
+
return MidasAPI("GET", "/db/nspr")
|
|
816
|
+
|
|
817
|
+
@classmethod
|
|
818
|
+
def sync(cls):
|
|
819
|
+
"""
|
|
820
|
+
Updates the PointSpring class with data
|
|
821
|
+
"""
|
|
822
|
+
cls.springs = []
|
|
823
|
+
a = cls.get()
|
|
824
|
+
|
|
825
|
+
if a != {'message': ''}:
|
|
826
|
+
for node_id, node_data in a.get("NSPR", {}).items():
|
|
827
|
+
for item in node_data.get("ITEMS"):
|
|
828
|
+
spring_type = item.get("TYPE")
|
|
829
|
+
group_name = item.get("GROUP_NAME")
|
|
830
|
+
spring_id = item.get("ID", 1)
|
|
831
|
+
|
|
832
|
+
# Extract type-specific parameters
|
|
833
|
+
if spring_type == "LINEAR":
|
|
834
|
+
stiffness = item.get("SDR")
|
|
835
|
+
fixed_option = item.get("F_S")
|
|
836
|
+
damping = item.get("Cr") if item.get("DAMPING", False) else None
|
|
837
|
+
|
|
838
|
+
# Convert direction back to string
|
|
839
|
+
dir_map = {0: "Dx+", 1: "Dx-", 2: "Dy+", 3: "Dy-", 4: "Dz+", 5: "Dz-", 6: "Vector"}
|
|
840
|
+
direction_str = dir_map.get(0, "Dx+") # Default for LINEAR
|
|
841
|
+
|
|
842
|
+
Boundary.PointSpring(
|
|
843
|
+
int(node_id), spring_type, group_name,
|
|
844
|
+
stiffness, fixed_option, damping, direction_str,spring_id
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
elif spring_type in ["COMP", "TENS"]:
|
|
848
|
+
stiffness = item.get("STIFF")
|
|
849
|
+
direction_int = item.get("DIR")
|
|
850
|
+
normal_vector = item.get("DV")
|
|
851
|
+
|
|
852
|
+
# Convert direction back to string
|
|
853
|
+
dir_map = {0: "Dx+", 1: "Dx-", 2: "Dy+", 3: "Dy-", 4: "Dz+", 5: "Dz-", 6: "Vector"}
|
|
854
|
+
direction_str = dir_map.get(direction_int, "Dx+")
|
|
855
|
+
|
|
856
|
+
Boundary.PointSpring(
|
|
857
|
+
int(node_id), spring_type, group_name,
|
|
858
|
+
stiffness, None, None, direction_str, normal_vector,spring_id
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
elif spring_type == "MULTI":
|
|
862
|
+
direction_int = item.get("DIR")
|
|
863
|
+
normal_vector = item.get("DV")
|
|
864
|
+
function_id = item.get("FUNCTION")
|
|
865
|
+
|
|
866
|
+
# Convert direction back to string
|
|
867
|
+
dir_map = {0: "Dx+", 1: "Dx-", 2: "Dy+", 3: "Dy-", 4: "Dz+", 5: "Dz-", 6: "Vector"}
|
|
868
|
+
direction_str = dir_map.get(direction_int, "Dx+")
|
|
869
|
+
|
|
870
|
+
Boundary.PointSpring(
|
|
871
|
+
int(node_id), spring_type, group_name,
|
|
872
|
+
None, None, None, direction_str, normal_vector, function_id,spring_id
|
|
873
|
+
)
|
|
874
|
+
|
|
875
|
+
@classmethod
|
|
876
|
+
def delete(cls):
|
|
877
|
+
"""
|
|
878
|
+
Deletes all point springs from the database and resets the class.
|
|
879
|
+
"""
|
|
880
|
+
cls.clear()
|
|
881
|
+
return MidasAPI("DELETE", "/db/nspr")
|
|
882
|
+
|
|
883
|
+
@classmethod
|
|
884
|
+
def clear(cls):
|
|
885
|
+
"""
|
|
886
|
+
Deletes all point springs from the database and resets the class.
|
|
887
|
+
"""
|
|
888
|
+
cls.springs = []
|