midas-civil 0.0.7__py3-none-any.whl → 0.0.9__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 midas-civil might be problematic. Click here for more details.
- midas_civil/_boundary.py +248 -10
- midas_civil/_element.py +367 -146
- midas_civil/_material.py +1269 -29
- {midas_civil-0.0.7.dist-info → midas_civil-0.0.9.dist-info}/METADATA +1 -1
- {midas_civil-0.0.7.dist-info → midas_civil-0.0.9.dist-info}/RECORD +8 -8
- {midas_civil-0.0.7.dist-info → midas_civil-0.0.9.dist-info}/WHEEL +0 -0
- {midas_civil-0.0.7.dist-info → midas_civil-0.0.9.dist-info}/licenses/LICENSE +0 -0
- {midas_civil-0.0.7.dist-info → midas_civil-0.0.9.dist-info}/top_level.txt +0 -0
midas_civil/_element.py
CHANGED
|
@@ -4,214 +4,271 @@ from ._node import *
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
|
|
6
6
|
def _ADD(self):
|
|
7
|
-
|
|
7
|
+
"""
|
|
8
|
+
Adds an element to the main list. If the ID is 0, it auto-increments.
|
|
9
|
+
If the ID already exists, it replaces the existing element.
|
|
10
|
+
"""
|
|
8
11
|
id = int(self.ID)
|
|
9
|
-
if Element.ids
|
|
12
|
+
if not Element.ids:
|
|
10
13
|
count = 1
|
|
11
14
|
else:
|
|
12
|
-
count = max(Element.ids)+1
|
|
15
|
+
count = max(Element.ids) + 1
|
|
13
16
|
|
|
14
|
-
if id==0
|
|
17
|
+
if id == 0:
|
|
15
18
|
self.ID = count
|
|
16
19
|
Element.elements.append(self)
|
|
17
20
|
Element.ids.append(int(self.ID))
|
|
18
21
|
elif id in Element.ids:
|
|
19
|
-
self.ID=int(id)
|
|
20
|
-
print(f'⚠️ Element with ID {id} already
|
|
21
|
-
index=Element.ids.index(id)
|
|
22
|
-
Element.elements[index]=self
|
|
22
|
+
self.ID = int(id)
|
|
23
|
+
print(f'⚠️ Element with ID {id} already exists! It will be replaced.')
|
|
24
|
+
index = Element.ids.index(id)
|
|
25
|
+
Element.elements[index] = self
|
|
23
26
|
else:
|
|
24
|
-
self.ID=id
|
|
27
|
+
self.ID = id
|
|
25
28
|
Element.elements.append(self)
|
|
26
29
|
Element.ids.append(int(self.ID))
|
|
27
|
-
# Common END -------------------------------------------------------
|
|
28
|
-
|
|
29
30
|
|
|
30
31
|
def _updateElem(self):
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
"""Sends a PUT request to update a single element in Midas."""
|
|
33
|
+
js2s = {'Assign': {self.ID: _Obj2JS(self)}}
|
|
34
|
+
MidasAPI('PUT', '/db/elem', js2s)
|
|
33
35
|
return js2s
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
37
|
def _Obj2JS(obj):
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"SECT": obj.SECT,
|
|
48
|
-
"NODE": [
|
|
49
|
-
obj.NODE[0],
|
|
50
|
-
obj.NODE[1]
|
|
51
|
-
],
|
|
52
|
-
"ANGLE": obj.ANGLE
|
|
53
|
-
}
|
|
54
|
-
elif obj.TYPE == 'TRUSS':
|
|
55
|
-
#--- TRUSS ---------------------------------------
|
|
56
|
-
js = {
|
|
57
|
-
"TYPE": obj.TYPE,
|
|
58
|
-
"MATL": obj.MATL,
|
|
59
|
-
"SECT": obj.SECT,
|
|
60
|
-
"NODE": [
|
|
61
|
-
obj.NODE[0],
|
|
62
|
-
obj.NODE[1]
|
|
63
|
-
],
|
|
64
|
-
"ANGLE": obj.ANGLE
|
|
65
|
-
}
|
|
38
|
+
"""Converts a Python element object to its JSON dictionary representation."""
|
|
39
|
+
# Base attributes common to many elements
|
|
40
|
+
js = {
|
|
41
|
+
"TYPE": obj.TYPE,
|
|
42
|
+
"MATL": obj.MATL,
|
|
43
|
+
"SECT": obj.SECT,
|
|
44
|
+
"NODE": obj.NODE,
|
|
45
|
+
}
|
|
66
46
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"
|
|
76
|
-
|
|
77
|
-
|
|
47
|
+
# Add optional attributes if they exist on the object
|
|
48
|
+
if hasattr(obj, 'ANGLE'): js["ANGLE"] = obj.ANGLE
|
|
49
|
+
if hasattr(obj, 'STYPE'): js["STYPE"] = obj.STYPE
|
|
50
|
+
|
|
51
|
+
# Handle type-specific and subtype-specific attributes
|
|
52
|
+
if obj.TYPE == 'TENSTR': # Tension/Hook/Cable
|
|
53
|
+
# Tension-only (stype=1) - can have TENS parameter
|
|
54
|
+
if obj.STYPE == 1:
|
|
55
|
+
if hasattr(obj, 'TENS'): js["TENS"] = obj.TENS
|
|
56
|
+
if hasattr(obj, 'T_LIMIT'): js["T_LIMIT"] = obj.T_LIMIT
|
|
57
|
+
if hasattr(obj, 'T_bLMT'): js["T_bLMT"] = obj.T_bLMT
|
|
58
|
+
|
|
59
|
+
# Hook (stype=2) - has NON_LEN parameter
|
|
60
|
+
elif obj.STYPE == 2:
|
|
61
|
+
if hasattr(obj, 'NON_LEN'): js["NON_LEN"] = obj.NON_LEN
|
|
62
|
+
|
|
63
|
+
# Cable (stype=3) - has CABLE, NON_LEN, and TENS parameters
|
|
64
|
+
elif obj.STYPE == 3:
|
|
65
|
+
if hasattr(obj, 'CABLE'): js["CABLE"] = obj.CABLE
|
|
66
|
+
if hasattr(obj, 'NON_LEN'): js["NON_LEN"] = obj.NON_LEN
|
|
67
|
+
if hasattr(obj, 'TENS'): js["TENS"] = obj.TENS
|
|
68
|
+
|
|
69
|
+
elif obj.TYPE == 'COMPTR': # Compression/Gap
|
|
70
|
+
# Compression-only (stype=1) - can have TENS, T_LIMIT, T_bLMT
|
|
71
|
+
if obj.STYPE == 1:
|
|
72
|
+
if hasattr(obj, 'TENS'): js["TENS"] = obj.TENS
|
|
73
|
+
if hasattr(obj, 'T_LIMIT'): js["T_LIMIT"] = obj.T_LIMIT
|
|
74
|
+
if hasattr(obj, 'T_bLMT'): js["T_bLMT"] = obj.T_bLMT
|
|
75
|
+
|
|
76
|
+
# Gap (stype=2) - has NON_LEN parameter
|
|
77
|
+
elif obj.STYPE == 2:
|
|
78
|
+
if hasattr(obj, 'NON_LEN'): js["NON_LEN"] = obj.NON_LEN
|
|
79
|
+
|
|
78
80
|
return js
|
|
79
81
|
|
|
80
|
-
def _JS2Obj(id,js):
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
sect = js['SECT']
|
|
84
|
-
node = js['NODE']
|
|
85
|
-
angle = js['ANGLE']
|
|
86
|
-
|
|
87
|
-
if type == 'BEAM':
|
|
88
|
-
Element.Beam(node[0],node[1],matl,sect,angle,id)
|
|
89
|
-
elif type == 'TRUSS':
|
|
90
|
-
Element.Truss(node[0],node[1],matl,sect,angle,id)
|
|
91
|
-
elif type == 'PLATE':
|
|
92
|
-
stype = js['STYPE']
|
|
93
|
-
Element.Plate(node,stype,matl,sect,angle,id)
|
|
82
|
+
def _JS2Obj(id, js):
|
|
83
|
+
"""Converts a JSON dictionary back into a Python element object during sync."""
|
|
84
|
+
elem_type = js.get('TYPE')
|
|
94
85
|
|
|
86
|
+
# Prepare arguments for constructors
|
|
87
|
+
args = {
|
|
88
|
+
'id': int(id),
|
|
89
|
+
'mat': js.get('MATL'),
|
|
90
|
+
'sect': js.get('SECT'),
|
|
91
|
+
'node': js.get('NODE'),
|
|
92
|
+
'angle': js.get('ANGLE'),
|
|
93
|
+
'stype': js.get('STYPE')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# Prepare individual parameters for optional/subtype-specific parameters
|
|
97
|
+
non_len = js.get('NON_LEN')
|
|
98
|
+
cable_type = js.get('CABLE')
|
|
99
|
+
tens = js.get('TENS')
|
|
100
|
+
t_limit = js.get('T_LIMIT')
|
|
101
|
+
|
|
102
|
+
if elem_type == 'BEAM':
|
|
103
|
+
Element.Beam(args['node'][0], args['node'][1], args['mat'], args['sect'], args['angle'], args['id'])
|
|
104
|
+
elif elem_type == 'TRUSS':
|
|
105
|
+
Element.Truss(args['node'][0], args['node'][1], args['mat'], args['sect'], args['angle'], args['id'])
|
|
106
|
+
elif elem_type == 'PLATE':
|
|
107
|
+
Element.Plate(args['node'], args['stype'], args['mat'], args['sect'], args['angle'], args['id'])
|
|
108
|
+
elif elem_type == 'TENSTR':
|
|
109
|
+
Element.Tension(args['node'][0], args['node'][1], args['stype'], args['mat'], args['sect'], args['angle'], args['id'], non_len, cable_type, tens, t_limit)
|
|
110
|
+
elif elem_type == 'COMPTR':
|
|
111
|
+
Element.Compression(args['node'][0], args['node'][1], args['stype'], args['mat'], args['sect'], args['angle'], args['id'], tens, t_limit, non_len)
|
|
112
|
+
elif elem_type == 'SOLID':
|
|
113
|
+
Element.Solid(nodes=args['node'], mat=args['mat'], sect=args['sect'], id=args['id'])
|
|
95
114
|
|
|
96
115
|
|
|
97
116
|
class _common:
|
|
117
|
+
"""Common base class for all element types."""
|
|
98
118
|
def __str__(self):
|
|
99
|
-
return str(f'ID = {self.ID}
|
|
119
|
+
return str(f'ID = {self.ID} \nJSON : {_Obj2JS(self)}\n')
|
|
100
120
|
|
|
101
121
|
def update(self):
|
|
102
122
|
return _updateElem(self)
|
|
103
123
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
#6 Class to create elements
|
|
124
|
+
# --- Main Element Class ---
|
|
110
125
|
class Element:
|
|
111
|
-
"""
|
|
126
|
+
"""
|
|
127
|
+
Main class to create and manage structural elements like Beams, Trusses,
|
|
128
|
+
Plates, Tension/Compression-only elements, and Solids.
|
|
129
|
+
"""
|
|
112
130
|
elements = []
|
|
113
131
|
ids = []
|
|
114
132
|
|
|
115
133
|
@classmethod
|
|
116
134
|
def json(cls):
|
|
117
|
-
|
|
135
|
+
json_data = {"Assign": {}}
|
|
118
136
|
for elem in cls.elements:
|
|
119
137
|
js = _Obj2JS(elem)
|
|
120
|
-
|
|
121
|
-
return
|
|
122
|
-
|
|
138
|
+
json_data["Assign"][elem.ID] = js
|
|
139
|
+
return json_data
|
|
140
|
+
|
|
123
141
|
@classmethod
|
|
124
142
|
def create(cls):
|
|
125
|
-
if cls.elements
|
|
126
|
-
MidasAPI("PUT","/db/ELEM",Element.json())
|
|
127
|
-
|
|
143
|
+
if cls.elements:
|
|
144
|
+
MidasAPI("PUT", "/db/ELEM", Element.json())
|
|
145
|
+
|
|
128
146
|
@staticmethod
|
|
129
147
|
def get():
|
|
130
|
-
return MidasAPI("GET","/db/ELEM")
|
|
131
|
-
|
|
148
|
+
return MidasAPI("GET", "/db/ELEM")
|
|
149
|
+
|
|
132
150
|
@staticmethod
|
|
133
151
|
def sync():
|
|
134
152
|
a = Element.get()
|
|
135
|
-
if a
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
153
|
+
if a and 'ELEM' in a and a['ELEM']:
|
|
154
|
+
Element.elements = []
|
|
155
|
+
Element.ids = []
|
|
156
|
+
for elem_id, data in a['ELEM'].items():
|
|
157
|
+
_JS2Obj(elem_id, data)
|
|
158
|
+
|
|
142
159
|
@staticmethod
|
|
143
160
|
def delete():
|
|
144
|
-
MidasAPI("DELETE","/db/ELEM")
|
|
145
|
-
Element.elements=[]
|
|
146
|
-
Element.ids=[]
|
|
147
|
-
|
|
161
|
+
MidasAPI("DELETE", "/db/ELEM")
|
|
162
|
+
Element.elements = []
|
|
163
|
+
Element.ids = []
|
|
148
164
|
|
|
165
|
+
# --- Element Type Subclasses ---
|
|
149
166
|
|
|
150
167
|
class Beam(_common):
|
|
151
168
|
|
|
152
|
-
def __init__(self,i:int,j:int,mat:int=1,sect:int=1,angle:float=0,id:int=0):
|
|
169
|
+
def __init__(self, i: int, j: int, mat: int = 1, sect: int = 1, angle: float = 0, id: int = 0):
|
|
170
|
+
"""
|
|
171
|
+
Creates a BEAM element for frame analysis.
|
|
172
|
+
|
|
173
|
+
Parameters:
|
|
174
|
+
i: Start node ID
|
|
175
|
+
j: End node ID
|
|
176
|
+
mat: Material property number (default 1)
|
|
177
|
+
sect: Section property number (default 1)
|
|
178
|
+
angle: Beta angle for section orientation in degrees (default 0.0)
|
|
179
|
+
id: Element ID (default 0 for auto-increment)
|
|
180
|
+
|
|
181
|
+
Examples:
|
|
182
|
+
```python
|
|
183
|
+
# Simple beam with default properties
|
|
184
|
+
Element.Beam(1, 2)
|
|
185
|
+
|
|
186
|
+
# Beam with specific material and section
|
|
187
|
+
Element.Beam(1, 2, mat=2, sect=3)
|
|
188
|
+
|
|
189
|
+
# Beam with 90° rotation (strong axis vertical)
|
|
190
|
+
Element.Beam(1, 2, mat=1, sect=1, angle=90.0)
|
|
191
|
+
|
|
192
|
+
# Beam with specific ID
|
|
193
|
+
Element.Beam(1, 2, mat=1, sect=1, angle=0.0, id=100)
|
|
194
|
+
```
|
|
195
|
+
"""
|
|
153
196
|
self.ID = id
|
|
154
197
|
self.TYPE = 'BEAM'
|
|
155
198
|
self.MATL = mat
|
|
156
199
|
self.SECT = sect
|
|
157
|
-
self.NODE=[i,j]
|
|
200
|
+
self.NODE = [i, j]
|
|
158
201
|
self.ANGLE = angle
|
|
159
|
-
|
|
160
202
|
_ADD(self)
|
|
161
203
|
|
|
162
204
|
@staticmethod
|
|
163
205
|
def SDL(s_loc:list,dir:list,l:float,n:int=1,mat:int=1,sect:int=1,angle:float=0,id:int=0): #CHANGE TO TUPLE
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
return beam_obj
|
|
206
|
+
beam_nodes =[]
|
|
207
|
+
beam_obj = []
|
|
208
|
+
s_locc = np.array(s_loc)
|
|
209
|
+
unit_vec = np.array(dir)/np.linalg.norm(dir)
|
|
210
|
+
|
|
211
|
+
for i in range(n+1):
|
|
212
|
+
locc = s_locc+i*l*unit_vec/n
|
|
213
|
+
Enode=Node(locc[0].item(),locc[1].item(),locc[2].item())
|
|
214
|
+
beam_nodes.append(Enode.ID)
|
|
215
|
+
|
|
216
|
+
for i in range(n):
|
|
217
|
+
if id == 0 : id_new = 0
|
|
218
|
+
else: id_new = id+i
|
|
219
|
+
beam_obj.append(Element.Beam(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,id_new))
|
|
180
220
|
|
|
221
|
+
return beam_obj
|
|
222
|
+
|
|
181
223
|
|
|
182
224
|
@staticmethod
|
|
183
225
|
def SE(s_loc:list,e_loc:list,n:int=1,mat:int=1,sect:int=1,angle:float=0,id:int=0):
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
226
|
+
beam_nodes =[]
|
|
227
|
+
beam_obj = []
|
|
228
|
+
i_loc = np.linspace(s_loc,e_loc,n+1)
|
|
229
|
+
for i in range(n+1):
|
|
230
|
+
Enode=Node(i_loc[i][0].item(),i_loc[i][1].item(),i_loc[i][2].item())
|
|
231
|
+
beam_nodes.append(Enode.ID)
|
|
232
|
+
|
|
233
|
+
for i in range(n):
|
|
234
|
+
if id == 0 : id_new = 0
|
|
235
|
+
else: id_new = id+i
|
|
236
|
+
beam_obj.append(Element.Beam(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,id_new))
|
|
237
|
+
|
|
238
|
+
return beam_obj
|
|
201
239
|
|
|
202
240
|
class Truss(_common):
|
|
203
|
-
|
|
204
|
-
|
|
241
|
+
def __init__(self, i: int, j: int, mat: int = 1, sect: int = 1, angle: float = 0, id: int = 0):
|
|
242
|
+
"""
|
|
243
|
+
Creates a TRUSS element
|
|
244
|
+
|
|
245
|
+
Parameters:
|
|
246
|
+
i: Start node ID
|
|
247
|
+
j: End node ID
|
|
248
|
+
mat: Material property number (default 1)
|
|
249
|
+
sect: Section property number (default 1)
|
|
250
|
+
angle: Beta angle for section orientation in degrees (default 0.0)
|
|
251
|
+
id: Element ID (default 0 for auto-increment)
|
|
252
|
+
|
|
253
|
+
Examples:
|
|
254
|
+
```python
|
|
255
|
+
# Simple truss member
|
|
256
|
+
Element.Truss(1, 2)
|
|
257
|
+
|
|
258
|
+
# Truss with specific material and section
|
|
259
|
+
Element.Truss(1, 2, mat=3, sect=2)
|
|
260
|
+
|
|
261
|
+
# Diagonal truss member
|
|
262
|
+
Element.Truss(3, 4, mat=1, sect=1, id=50)
|
|
263
|
+
```
|
|
264
|
+
"""
|
|
205
265
|
self.ID = id
|
|
206
266
|
self.TYPE = 'TRUSS'
|
|
207
267
|
self.MATL = mat
|
|
208
268
|
self.SECT = sect
|
|
209
|
-
self.NODE=[i,j]
|
|
269
|
+
self.NODE = [i, j]
|
|
210
270
|
self.ANGLE = angle
|
|
211
|
-
|
|
212
271
|
_ADD(self)
|
|
213
|
-
|
|
214
|
-
|
|
215
272
|
|
|
216
273
|
@staticmethod
|
|
217
274
|
def SDL(s_loc:list,dir:list,l:float,n:int=1,mat:int=1,sect:int=1,angle:float=0,id:int=0):
|
|
@@ -247,23 +304,187 @@ class Element:
|
|
|
247
304
|
else: id_new = id+i
|
|
248
305
|
beam_obj.append(Element.Truss(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,id_new))
|
|
249
306
|
|
|
250
|
-
return beam_obj
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
307
|
+
return beam_obj
|
|
308
|
+
|
|
254
309
|
class Plate(_common):
|
|
255
|
-
|
|
256
|
-
|
|
310
|
+
def __init__(self, nodes: list, stype: int = 1, mat: int = 1, sect: int = 1, angle: float = 0, id: int = 0):
|
|
311
|
+
"""
|
|
312
|
+
Creates a PLATE element.
|
|
313
|
+
|
|
314
|
+
Parameters:
|
|
315
|
+
nodes: List of node IDs [n1, n2, n3] for triangular or [n1, n2, n3, n4] for quadrilateral
|
|
316
|
+
stype: Plate subtype (1=Thick plate, 2=Thin plate, 3=With drilling DOF) (default 1)
|
|
317
|
+
mat: Material property number (default 1)
|
|
318
|
+
sect: Section (thickness) property number (default 1)
|
|
319
|
+
angle: Material angle for orthotropic materials in degrees (default 0.0)
|
|
320
|
+
id: Element ID (default 0 for auto-increment)
|
|
321
|
+
|
|
322
|
+
Examples:
|
|
323
|
+
```python
|
|
324
|
+
# Triangular thick plate
|
|
325
|
+
Element.Plate([1, 2, 3], stype=1, mat=1, sect=1)
|
|
326
|
+
|
|
327
|
+
# Quadrilateral thin plate
|
|
328
|
+
Element.Plate([1, 2, 3, 4], stype=2, mat=2, sect=1)
|
|
329
|
+
|
|
330
|
+
# Plate with drilling DOF for shell analysis
|
|
331
|
+
Element.Plate([5, 6, 7, 8], stype=3, mat=1, sect=2, angle=45.0)
|
|
332
|
+
```
|
|
333
|
+
"""
|
|
257
334
|
self.ID = id
|
|
258
335
|
self.TYPE = 'PLATE'
|
|
259
336
|
self.MATL = mat
|
|
260
337
|
self.SECT = sect
|
|
261
|
-
self.NODE=nodes
|
|
338
|
+
self.NODE = nodes
|
|
262
339
|
self.ANGLE = angle
|
|
263
340
|
self.STYPE = stype
|
|
264
|
-
|
|
341
|
+
_ADD(self)
|
|
342
|
+
|
|
343
|
+
class Tension(_common):
|
|
344
|
+
def __init__(self, i: int, j: int, stype: int, mat: int = 1, sect: int = 1, angle: float = 0, id: int = 0, non_len: float = None, cable_type: int = None, tens: float = None, t_limit: float = None):
|
|
345
|
+
"""
|
|
346
|
+
Creates a TENSTR (Tension-only) element.
|
|
347
|
+
|
|
348
|
+
Parameters:
|
|
349
|
+
i: Start node ID
|
|
350
|
+
j: End node ID
|
|
351
|
+
stype: Tension element subtype (1=Tension-only, 2=Hook, 3=Cable)
|
|
352
|
+
mat: Material property number (default 1)
|
|
353
|
+
sect: Section property number (default 1)
|
|
354
|
+
angle: Beta angle for section orientation in degrees (default 0.0)
|
|
355
|
+
id: Element ID (default 0 for auto-increment)
|
|
356
|
+
non_len: Non-linear length parameter for Hook/Cable (default None)
|
|
357
|
+
cable_type: Cable type for stype=3 (1=Pretension, 2=Horizontal, 3=Lu) (default None)
|
|
358
|
+
tens: Initial tension force or allowable compression (default None)
|
|
359
|
+
t_limit: Tension limit value. If provided, the tension limit flag is set to True. (default None)
|
|
360
|
+
|
|
361
|
+
Examples:
|
|
362
|
+
```python
|
|
363
|
+
# Simple tension-only member
|
|
364
|
+
Element.Tension(1, 2, stype=1)
|
|
365
|
+
|
|
366
|
+
# Tension-only with allowable compression and a tension limit
|
|
367
|
+
Element.Tension(1, 2, stype=1, tens=0.5, t_limit=-15)
|
|
368
|
+
|
|
369
|
+
# Hook element with slack length
|
|
370
|
+
Element.Tension(3, 4, stype=2, non_len=0.5)
|
|
371
|
+
|
|
372
|
+
# Cable with initial tension and catenary effects
|
|
373
|
+
Element.Tension(5, 6, stype=3, cable_type=3, tens=1000.0, non_len=0.1)
|
|
374
|
+
```
|
|
375
|
+
"""
|
|
376
|
+
self.ID = id
|
|
377
|
+
self.TYPE = 'TENSTR'
|
|
378
|
+
self.MATL = mat
|
|
379
|
+
self.SECT = sect
|
|
380
|
+
self.NODE = [i, j]
|
|
381
|
+
self.ANGLE = angle
|
|
382
|
+
self.STYPE = stype
|
|
383
|
+
|
|
384
|
+
# Handle subtype-specific parameters
|
|
385
|
+
if stype == 1: # Tension-only specific
|
|
386
|
+
if tens is not None:
|
|
387
|
+
self.TENS = tens
|
|
388
|
+
if t_limit is not None:
|
|
389
|
+
self.T_LIMIT = t_limit
|
|
390
|
+
self.T_bLMT = True
|
|
391
|
+
|
|
392
|
+
elif stype == 2: # Hook specific
|
|
393
|
+
if non_len is not None:
|
|
394
|
+
self.NON_LEN = non_len
|
|
395
|
+
|
|
396
|
+
elif stype == 3: # Cable specific
|
|
397
|
+
if cable_type is not None:
|
|
398
|
+
self.CABLE = cable_type
|
|
399
|
+
if non_len is not None:
|
|
400
|
+
self.NON_LEN = non_len
|
|
401
|
+
if tens is not None:
|
|
402
|
+
self.TENS = tens
|
|
403
|
+
_ADD(self)
|
|
404
|
+
|
|
405
|
+
class Compression(_common):
|
|
406
|
+
def __init__(self, i: int, j: int, stype: int, mat: int = 1, sect: int = 1, angle: float = 0, id: int = 0, tens: float = None, t_limit: float = None, non_len: float = None):
|
|
407
|
+
"""
|
|
408
|
+
Creates a COMPTR (Compression-only) element.
|
|
409
|
+
|
|
410
|
+
Parameters:
|
|
411
|
+
i: Start node ID
|
|
412
|
+
j: End node ID
|
|
413
|
+
stype: Compression element subtype (1=Compression-only, 2=Gap)
|
|
414
|
+
mat: Material property number (default 1)
|
|
415
|
+
sect: Section property number (default 1)
|
|
416
|
+
angle: Beta angle for section orientation in degrees (default 0.0)
|
|
417
|
+
id: Element ID (default 0 for auto-increment)
|
|
418
|
+
tens: Allowable tension or initial compression force (default None)
|
|
419
|
+
t_limit: Compression limit value. If provided, the compression limit flag is set to True. (default None)
|
|
420
|
+
non_len: Non-linear length parameter for gap (default None)
|
|
421
|
+
|
|
422
|
+
Examples:
|
|
423
|
+
```python
|
|
424
|
+
# Simple compression-only member
|
|
425
|
+
Element.Compression(1, 2, stype=1)
|
|
426
|
+
|
|
427
|
+
# Compression-only with tension limit and buckling limit
|
|
428
|
+
Element.Compression(1, 2, stype=1, tens=27, t_limit=-15)
|
|
429
|
+
|
|
430
|
+
# Gap element with initial gap
|
|
431
|
+
Element.Compression(3, 4, stype=2, non_len=0.25)
|
|
432
|
+
```
|
|
433
|
+
"""
|
|
434
|
+
self.ID = id
|
|
435
|
+
self.TYPE = 'COMPTR'
|
|
436
|
+
self.MATL = mat
|
|
437
|
+
self.SECT = sect
|
|
438
|
+
self.NODE = [i, j]
|
|
439
|
+
self.ANGLE = angle
|
|
440
|
+
self.STYPE = stype
|
|
441
|
+
|
|
442
|
+
# Handle subtype-specific parameters
|
|
443
|
+
if stype == 1: # Compression-only specific
|
|
444
|
+
if tens is not None:
|
|
445
|
+
self.TENS = tens
|
|
446
|
+
if t_limit is not None:
|
|
447
|
+
self.T_LIMIT = t_limit
|
|
448
|
+
self.T_bLMT = True
|
|
449
|
+
|
|
450
|
+
elif stype == 2: # Gap specific
|
|
451
|
+
if non_len is not None:
|
|
452
|
+
self.NON_LEN = non_len
|
|
265
453
|
_ADD(self)
|
|
266
454
|
|
|
455
|
+
class Solid(_common):
|
|
456
|
+
def __init__(self, nodes: list, mat: int = 1, sect: int = 0, id: int = 0):
|
|
457
|
+
"""
|
|
458
|
+
Creates a SOLID element for 3D analysis.
|
|
459
|
+
|
|
460
|
+
Parameters:
|
|
461
|
+
nodes: List of node IDs defining the solid element
|
|
462
|
+
- 4 nodes: Tetrahedral element
|
|
463
|
+
- 6 nodes: Pentahedral element
|
|
464
|
+
- 8 nodes: Hexahedral element
|
|
465
|
+
mat: Material property number (default 1)
|
|
466
|
+
id: Element ID (default 0 for auto-increment)
|
|
467
|
+
|
|
468
|
+
Examples:
|
|
469
|
+
```python
|
|
470
|
+
# Tetrahedral solid element
|
|
471
|
+
Element.Solid([1, 2, 3, 4], mat=1)
|
|
472
|
+
|
|
473
|
+
# Wedge solid element
|
|
474
|
+
Element.Solid([1, 2, 3, 4, 5, 6], mat=2)
|
|
475
|
+
|
|
476
|
+
# Hexahedral solid element
|
|
477
|
+
Element.Solid([1, 2, 3, 4, 5, 6, 7, 8], mat=1, id=200)
|
|
478
|
+
```
|
|
479
|
+
"""
|
|
480
|
+
if len(nodes) not in [4, 6, 8]:
|
|
481
|
+
raise ValueError("Solid element must have 4, 6, or 8 nodes.")
|
|
482
|
+
self.ID = id
|
|
483
|
+
self.TYPE = 'SOLID'
|
|
484
|
+
self.MATL = mat
|
|
485
|
+
self.SECT = sect # Solid elements don't use section properties
|
|
486
|
+
self.NODE = nodes
|
|
487
|
+
_ADD(self)
|
|
267
488
|
|
|
268
489
|
|
|
269
490
|
|