midas-civil 0.0.9__py3-none-any.whl → 0.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 midas-civil might be problematic. Click here for more details.

@@ -0,0 +1,415 @@
1
+ from ._mapi import *
2
+
3
+ class CS:
4
+
5
+ CSA = []
6
+
7
+ def __init__(self,
8
+ name: str,
9
+ duration: float = 0,
10
+ s_group: str = None,
11
+ s_age: float = None,
12
+ s_type: str= None,
13
+ b_group: str = None,
14
+ b_pos: str = None,
15
+ b_type: str = None,
16
+ l_group: str = None,
17
+ l_day: str = None,
18
+ l_type: str = None,
19
+ id: int = None,
20
+ sr_stage: bool = True,
21
+ ad_stage: bool = False,
22
+ load_in: bool = False,
23
+ nl: int = 5,
24
+ addstp: list = None):
25
+ """
26
+ Construction Stage define.
27
+
28
+ Parameters:
29
+ name: Name of Construction Stage
30
+ duration: Duration of Construction Stage in days (default 0)
31
+ s_group: Structure group name or list of group names (default None)
32
+ s_age: Age of structure group in days and Redistribution value(%) win case of Deactivation (default 0)
33
+ s_type: Structure activation type - "A" to activate, "D" to deactivate(default A)
34
+ b_group: Boundary group name or list of group names (default None)
35
+ b_pos: Boundary position type - "ORIGINAL" or "DEFORMED", or list (default DEFORMED)
36
+ b_type: Boundary activation type - "A" to activate, "D" to deactivate (default A)
37
+ l_group: Load group name or list of group names (default None)
38
+ l_day: Load activation day - "FIRST" or "LAST" (default "FIRST")
39
+ l_type: Load activation type - "A" to activate, "D" to deactivate (default A)
40
+ id: The construction stage ID (optional)
41
+ sr_stage: Save results of this stage (default True)
42
+ ad_stage: Add additional step results (default False)
43
+ load_in: Load incremental steps for material nonlinear analysis (default False)
44
+ nl: Number of load incremental steps (default 5)
45
+ addstp: List of additional steps (default None)
46
+
47
+ Examples:
48
+ ```python
49
+ # Single group activation
50
+ CS("CS1", 7, "S1", 7, "A", "B1", "DEFORMED", "A", "L1", "FIRST", "A")
51
+
52
+ # Multiple group activation
53
+ CS("CS1", 7, ["S1", "S2"], [7, 10], ["A", "A"], ["B1", "B2"],
54
+ ["DEFORMED", "DEFORMED"], ["A", "A"], ["L1", "L2"], ["FIRST", "FIRST"], ["A", "A"])
55
+
56
+ # Mixed activation and deactivation
57
+ CS("CS1", 7, ["S1", "S2"], [7, 10], ["A", "D"], ["B1", "B2"],
58
+ ["DEFORMED", "DEFORMED"], ["A", "D"], "L1", "FIRST", "A")
59
+
60
+ # With additional options
61
+ CS("CS1", 7, "S1", 7, "A", "B1", "DEFORMED", "A", "L1", "FIRST", "A",
62
+ sr_stage=True, ad_stage=True, load_in=True, nl=6, addstp=[1, 2, 3])
63
+ ```
64
+ """
65
+
66
+ self.NAME = name
67
+ self.DURATION = duration
68
+ self.SR_stage = sr_stage
69
+ self.Ad_stage = ad_stage
70
+ self.Load_IN = load_in
71
+ self.NL = nl
72
+ self.addstp = [] if addstp is None else addstp
73
+
74
+ # Initialize group containers
75
+ self.act_structure_groups = []
76
+ self.deact_structure_groups = []
77
+ self.act_boundary_groups = []
78
+ self.deact_boundary_groups = []
79
+ self.act_load_groups = []
80
+ self.deact_load_groups = []
81
+
82
+ # Set ID
83
+ if id is None:
84
+ self.ID = len(CS.CSA) + 1
85
+ else:
86
+ self.ID = id
87
+
88
+ # Process structure groups
89
+ if s_group:
90
+ # Convert single values to lists for uniform processing
91
+ if not isinstance(s_group, list):
92
+ s_group = [s_group]
93
+ s_age = [s_age if s_age is not None else 0]
94
+ s_type = [s_type if s_type is not None else "A"]
95
+ else:
96
+ # Ensure other parameters are lists too
97
+ if s_age is None:
98
+ s_age = [0] * len(s_group)
99
+ elif not isinstance(s_age, list):
100
+ s_age = [s_age] * len(s_group)
101
+
102
+ if s_type is None:
103
+ s_type = ["A"] * len(s_group)
104
+ elif not isinstance(s_type, list):
105
+ s_type = [s_type] * len(s_group)
106
+
107
+ # Process each structure group
108
+ for i, group in enumerate(s_group):
109
+ if i < len(s_type) and s_type[i] == "A":
110
+ # Activation: Check if already activated in previous stages
111
+ for stage in CS.CSA:
112
+ for existing_group in stage.act_structure_groups:
113
+ if existing_group["name"] == group:
114
+ raise ValueError(f"Structure group '{group}' has already been activated in stage '{stage.NAME}' (ID: {stage.ID})")
115
+
116
+ age = s_age[i] if i < len(s_age) else 0
117
+ self.act_structure_groups.append({"name": group, "age": age})
118
+ else:
119
+ # Deactivation: Check if activated in previous stages
120
+ activated = False
121
+ for stage in CS.CSA:
122
+ for existing_group in stage.act_structure_groups:
123
+ if existing_group["name"] == group:
124
+ activated = True
125
+ break
126
+ if activated:
127
+ break
128
+
129
+ if not activated:
130
+ raise ValueError(f"Structure group '{group}' cannot be deactivated as it has not been activated in any previous stage")
131
+
132
+ # For deactivation, s_age value is used as redist percentage
133
+ redist = s_age[i] if i < len(s_age) else 100
134
+ self.deact_structure_groups.append({"name": group, "redist": redist})
135
+
136
+ # Process boundary groups
137
+ if b_group:
138
+ # Convert single values to lists for uniform processing
139
+ if not isinstance(b_group, list):
140
+ b_group = [b_group]
141
+ b_pos = [b_pos if b_pos is not None else "DEFORMED"]
142
+ b_type = [b_type if b_type is not None else "A"]
143
+ else:
144
+ # Ensure other parameters are lists too
145
+ if b_pos is None:
146
+ b_pos = ["DEFORMED"] * len(b_group)
147
+ elif not isinstance(b_pos, list):
148
+ b_pos = [b_pos] * len(b_group)
149
+
150
+ if b_type is None:
151
+ b_type = ["A"] * len(b_group)
152
+ elif not isinstance(b_type, list):
153
+ b_type = [b_type] * len(b_group)
154
+
155
+ # Process each boundary group
156
+ for i, group in enumerate(b_group):
157
+ if i < len(b_type) and b_type[i] == "A":
158
+ # Activation: Check if already activated in previous stages
159
+ for stage in CS.CSA:
160
+ for existing_group in stage.act_boundary_groups:
161
+ if existing_group["name"] == group:
162
+ raise ValueError(f"Boundary group '{group}' has already been activated in stage '{stage.NAME}' (ID: {stage.ID})")
163
+
164
+ pos = b_pos[i] if i < len(b_pos) else "DEFORMED"
165
+ self.act_boundary_groups.append({"name": group, "pos": pos})
166
+ else:
167
+ # Deactivation: Check if activated in previous stages
168
+ activated = False
169
+ for stage in CS.CSA:
170
+ for existing_group in stage.act_boundary_groups:
171
+ if existing_group["name"] == group:
172
+ activated = True
173
+ break
174
+ if activated:
175
+ break
176
+
177
+ if not activated:
178
+ raise ValueError(f"Boundary group '{group}' cannot be deactivated as it has not been activated in any previous stage")
179
+
180
+ self.deact_boundary_groups.append(group)
181
+
182
+ # Process load groups
183
+ if l_group:
184
+ # Convert single values to lists for uniform processing
185
+ if not isinstance(l_group, list):
186
+ l_group = [l_group]
187
+ l_day = [l_day if l_day is not None else "FIRST"]
188
+ l_type = [l_type if l_type is not None else "A"]
189
+ else:
190
+ # Ensure other parameters are lists too
191
+ if l_day is None:
192
+ l_day = ["FIRST"] * len(l_group)
193
+ elif not isinstance(l_day, list):
194
+ l_day = [l_day] * len(l_group)
195
+
196
+ if l_type is None:
197
+ l_type = ["A"] * len(l_group)
198
+ elif not isinstance(l_type, list):
199
+ l_type = [l_type] * len(l_group)
200
+
201
+ # Process each load group
202
+ for i, group in enumerate(l_group):
203
+ if i < len(l_type) and l_type[i] == "A":
204
+ # Activation: Check if already activated in previous stages
205
+ for stage in CS.CSA:
206
+ for existing_group in stage.act_load_groups:
207
+ if existing_group["name"] == group:
208
+ raise ValueError(f"Load group '{group}' has already been activated in stage '{stage.NAME}' (ID: {stage.ID})")
209
+
210
+ day = l_day[i] if i < len(l_day) else "FIRST"
211
+ self.act_load_groups.append({"name": group, "day": day})
212
+ else:
213
+ # Deactivation: Check if activated in previous stages
214
+ activated = False
215
+ for stage in CS.CSA:
216
+ for existing_group in stage.act_load_groups:
217
+ if existing_group["name"] == group:
218
+ activated = True
219
+ break
220
+ if activated:
221
+ break
222
+
223
+ if not activated:
224
+ raise ValueError(f"Load group '{group}' cannot be deactivated as it has not been activated in any previous stage")
225
+
226
+ day = l_day[i] if i < len(l_day) else "FIRST"
227
+ self.deact_load_groups.append({"name": group, "day": day})
228
+
229
+ CS.CSA.append(self)
230
+
231
+ @classmethod
232
+ def json(cls):
233
+ """
234
+ Converts Construction Stage data to JSON format
235
+ Example:
236
+ # Get the JSON data for all construction stages
237
+ json_data = CS.json()
238
+ print(json_data)
239
+ """
240
+ json = {"Assign": {}}
241
+
242
+ for csa in cls.CSA:
243
+ stage_data = {
244
+ "NAME": csa.NAME,
245
+ "DURATION": csa.DURATION,
246
+ "bSV_RSLT": csa.SR_stage,
247
+ "bSV_STEP": csa.Ad_stage,
248
+ "bLOAD_STEP": csa.Load_IN
249
+ }
250
+
251
+ # Add incremental steps if load step is enabled
252
+ if csa.Load_IN:
253
+ stage_data["INCRE_STEP"] = csa.NL
254
+
255
+ # Add additional steps if specified
256
+ if csa.addstp:
257
+ stage_data["ADD_STEP"] = csa.addstp
258
+ else:
259
+ stage_data["ADD_STEP"] = []
260
+
261
+ # Handle structure group activation
262
+ if csa.act_structure_groups:
263
+ stage_data["ACT_ELEM"] = []
264
+ for group in csa.act_structure_groups:
265
+ stage_data["ACT_ELEM"].append({
266
+ "GRUP_NAME": group["name"],
267
+ "AGE": group["age"]
268
+ })
269
+
270
+ # Handle structure group deactivation
271
+ if csa.deact_structure_groups:
272
+ stage_data["DACT_ELEM"] = []
273
+ for group in csa.deact_structure_groups:
274
+ stage_data["DACT_ELEM"].append({
275
+ "GRUP_NAME": group["name"],
276
+ "REDIST": group["redist"]
277
+ })
278
+
279
+ # Handle boundary group activation
280
+ if csa.act_boundary_groups:
281
+ stage_data["ACT_BNGR"] = []
282
+ for group in csa.act_boundary_groups:
283
+ stage_data["ACT_BNGR"].append({
284
+ "BNGR_NAME": group["name"],
285
+ "POS": group["pos"]
286
+ })
287
+
288
+ # Handle boundary group deactivation
289
+ if csa.deact_boundary_groups:
290
+ stage_data["DACT_BNGR"] = []
291
+ for group_name in csa.deact_boundary_groups:
292
+ stage_data["DACT_BNGR"].append(group_name)
293
+
294
+ # Handle load group activation
295
+ if csa.act_load_groups:
296
+ stage_data["ACT_LOAD"] = []
297
+ for group in csa.act_load_groups:
298
+ stage_data["ACT_LOAD"].append({
299
+ "LOAD_NAME": group["name"],
300
+ "DAY": group["day"]
301
+ })
302
+
303
+ # Handle load group deactivation
304
+ if csa.deact_load_groups:
305
+ stage_data["DACT_LOAD"] = []
306
+ for group in csa.deact_load_groups:
307
+ stage_data["DACT_LOAD"].append({
308
+ "LOAD_NAME": group["name"],
309
+ "DAY": group["day"]
310
+ })
311
+
312
+ json["Assign"][str(csa.ID)] = stage_data
313
+
314
+ return json
315
+
316
+ @classmethod
317
+ def create(cls):
318
+ """Creates construction stages in the database"""
319
+ MidasAPI("PUT", "/db/stag", CS.json())
320
+
321
+ @classmethod
322
+ def get(cls):
323
+ """Gets construction stage data from the database"""
324
+ return MidasAPI("GET", "/db/stag")
325
+
326
+ @classmethod
327
+ def sync(cls):
328
+ """Updates the CS class with data from the database"""
329
+ cls.CSA = []
330
+ a = CS.get()
331
+ if a != {'message': ''}:
332
+ if "STAG" in a:
333
+ stag_data_dict = a["STAG"]
334
+ else:
335
+ return
336
+
337
+ for stag_id, stag_data in stag_data_dict.items():
338
+ # Basic stage data
339
+ name = stag_data.get("NAME")
340
+ duration = stag_data.get("DURATION")
341
+ sr_stage = stag_data.get("bSV_RSLT")
342
+ ad_stage = stag_data.get("bSV_STEP")
343
+ load_in = stag_data.get("bLOAD_STEP")
344
+ nl = stag_data.get("INCRE_STEP")
345
+ addstp = stag_data.get("ADD_STEP")
346
+
347
+ # Create a new CS object with basic properties
348
+ new_cs = CS(
349
+ name=name,
350
+ duration=duration,
351
+ id=int(stag_id),
352
+ sr_stage=sr_stage,
353
+ ad_stage=ad_stage,
354
+ load_in=load_in,
355
+ nl=nl,
356
+ addstp=addstp
357
+ )
358
+
359
+ CS.CSA.pop()
360
+
361
+ # Process activation elements
362
+ if "ACT_ELEM" in stag_data and stag_data["ACT_ELEM"]:
363
+ for elem in stag_data["ACT_ELEM"]:
364
+ group_name = elem.get("GRUP_NAME")
365
+ age = elem.get("AGE")
366
+ new_cs.act_structure_groups.append({"name": group_name, "age": age})
367
+
368
+ # Process deactivation elements
369
+ if "DACT_ELEM" in stag_data and stag_data["DACT_ELEM"]:
370
+ for elem in stag_data["DACT_ELEM"]:
371
+ if isinstance(elem, dict):
372
+ group_name = elem.get("GRUP_NAME")
373
+ redist = elem.get("REDIST")
374
+ else:
375
+ group_name = elem
376
+ redist = 0
377
+ new_cs.deact_structure_groups.append({"name": group_name, "redist": redist})
378
+
379
+ # Process activation boundary groups
380
+ if "ACT_BNGR" in stag_data and stag_data["ACT_BNGR"]:
381
+ for bngr in stag_data["ACT_BNGR"]:
382
+ group_name = bngr.get("BNGR_NAME")
383
+ pos = bngr.get("POS")
384
+ new_cs.act_boundary_groups.append({"name": group_name, "pos": pos})
385
+
386
+ # Process deactivation boundary groups
387
+ if "DACT_BNGR" in stag_data and stag_data["DACT_BNGR"]:
388
+ for bngr in stag_data["DACT_BNGR"]:
389
+ new_cs.deact_boundary_groups.append(bngr)
390
+
391
+ # Process activation loads
392
+ if "ACT_LOAD" in stag_data and stag_data["ACT_LOAD"]:
393
+ for load in stag_data["ACT_LOAD"]:
394
+ group_name = load.get("LOAD_NAME")
395
+ day = load.get("DAY")
396
+ new_cs.act_load_groups.append({"name": group_name, "day": day})
397
+
398
+ # Process deactivation loads
399
+ if "DACT_LOAD" in stag_data and stag_data["DACT_LOAD"]:
400
+ for load in stag_data["DACT_LOAD"]:
401
+ if isinstance(load, dict):
402
+ group_name = load.get("LOAD_NAME")
403
+ day = load.get("DAY")
404
+ else:
405
+ group_name = load
406
+ day = "FIRST"
407
+ new_cs.deact_load_groups.append({"name": group_name, "day": day})
408
+
409
+ CS.CSA.append(new_cs)
410
+
411
+ @classmethod
412
+ def delete(cls):
413
+ """Deletes all construction stages from the database and resets the class"""
414
+ cls.CSA = []
415
+ return MidasAPI("DELETE", "/db/stag")
midas_civil/_element.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from ._mapi import *
2
2
  from ._node import *
3
+ from ._group import _add_elem_2_stGroup
3
4
 
4
5
  import numpy as np
5
6
 
@@ -8,6 +9,8 @@ def _ADD(self):
8
9
  Adds an element to the main list. If the ID is 0, it auto-increments.
9
10
  If the ID already exists, it replaces the existing element.
10
11
  """
12
+
13
+ # ------------ ID assignment -----------------------
11
14
  id = int(self.ID)
12
15
  if not Element.ids:
13
16
  count = 1
@@ -27,6 +30,20 @@ def _ADD(self):
27
30
  self.ID = id
28
31
  Element.elements.append(self)
29
32
  Element.ids.append(int(self.ID))
33
+
34
+ # ------------ Group assignment -----------------------
35
+ if self._GROUP == '' :
36
+ pass
37
+ elif isinstance(self._GROUP, list):
38
+ for gpName in self._GROUP:
39
+ _add_elem_2_stGroup(self.ID,gpName)
40
+ elif isinstance(self._GROUP, str):
41
+ _add_elem_2_stGroup(self.ID,self._GROUP)
42
+
43
+
44
+
45
+
46
+
30
47
 
31
48
  def _updateElem(self):
32
49
  """Sends a PUT request to update a single element in Midas."""
@@ -166,7 +183,7 @@ class Element:
166
183
 
167
184
  class Beam(_common):
168
185
 
169
- def __init__(self, i: int, j: int, mat: int = 1, sect: int = 1, angle: float = 0, id: int = 0):
186
+ def __init__(self, i: int, j: int, mat: int = 1, sect: int = 1, angle: float = 0, group = "" , id: int = 0):
170
187
  """
171
188
  Creates a BEAM element for frame analysis.
172
189
 
@@ -176,6 +193,7 @@ class Element:
176
193
  mat: Material property number (default 1)
177
194
  sect: Section property number (default 1)
178
195
  angle: Beta angle for section orientation in degrees (default 0.0)
196
+ group: Structure group of the element (str or list; 'SG1' or ['SG1','SG2'])
179
197
  id: Element ID (default 0 for auto-increment)
180
198
 
181
199
  Examples:
@@ -199,10 +217,11 @@ class Element:
199
217
  self.SECT = sect
200
218
  self.NODE = [i, j]
201
219
  self.ANGLE = angle
220
+ self._GROUP = group
202
221
  _ADD(self)
203
222
 
204
223
  @staticmethod
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
224
+ def SDL(s_loc:list,dir:list,l:float,n:int=1,mat:int=1,sect:int=1,angle:float=0, group = "" , id: int = 0): #CHANGE TO TUPLE
206
225
  beam_nodes =[]
207
226
  beam_obj = []
208
227
  s_locc = np.array(s_loc)
@@ -216,13 +235,13 @@ class Element:
216
235
  for i in range(n):
217
236
  if id == 0 : id_new = 0
218
237
  else: id_new = id+i
219
- beam_obj.append(Element.Beam(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,id_new))
238
+ beam_obj.append(Element.Beam(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,group,id_new))
220
239
 
221
240
  return beam_obj
222
241
 
223
242
 
224
243
  @staticmethod
225
- def SE(s_loc:list,e_loc:list,n:int=1,mat:int=1,sect:int=1,angle:float=0,id:int=0):
244
+ def SE(s_loc:list,e_loc:list,n:int=1,mat:int=1,sect:int=1,angle:float=0, group = "" , id: int = 0):
226
245
  beam_nodes =[]
227
246
  beam_obj = []
228
247
  i_loc = np.linspace(s_loc,e_loc,n+1)
@@ -233,12 +252,12 @@ class Element:
233
252
  for i in range(n):
234
253
  if id == 0 : id_new = 0
235
254
  else: id_new = id+i
236
- beam_obj.append(Element.Beam(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,id_new))
255
+ beam_obj.append(Element.Beam(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,group,id_new))
237
256
 
238
257
  return beam_obj
239
258
 
240
259
  class Truss(_common):
241
- def __init__(self, i: int, j: int, mat: int = 1, sect: int = 1, angle: float = 0, id: int = 0):
260
+ def __init__(self, i: int, j: int, mat: int = 1, sect: int = 1, angle: float = 0, group = "" , id: int = 0):
242
261
  """
243
262
  Creates a TRUSS element
244
263
 
@@ -248,6 +267,7 @@ class Element:
248
267
  mat: Material property number (default 1)
249
268
  sect: Section property number (default 1)
250
269
  angle: Beta angle for section orientation in degrees (default 0.0)
270
+ group: Structure group of the element (str or list; 'SG1' or ['SG1','SG2'])
251
271
  id: Element ID (default 0 for auto-increment)
252
272
 
253
273
  Examples:
@@ -268,10 +288,11 @@ class Element:
268
288
  self.SECT = sect
269
289
  self.NODE = [i, j]
270
290
  self.ANGLE = angle
291
+ self._GROUP = group
271
292
  _ADD(self)
272
293
 
273
294
  @staticmethod
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):
295
+ def SDL(s_loc:list,dir:list,l:float,n:int=1,mat:int=1,sect:int=1,angle:float=0, group = "" , id: int = 0):
275
296
  beam_nodes =[]
276
297
  beam_obj =[]
277
298
  s_locc = np.array(s_loc)
@@ -285,13 +306,13 @@ class Element:
285
306
  for i in range(n):
286
307
  if id == 0 : id_new = 0
287
308
  else: id_new = id+i
288
- beam_obj.append(Element.Truss(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,id_new))
309
+ beam_obj.append(Element.Truss(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,group,id_new))
289
310
 
290
311
  return beam_obj
291
312
 
292
313
 
293
314
  @staticmethod
294
- def SE(s_loc:list,e_loc:list,n:int=1,mat:int=1,sect:int=1,angle:float=0,id:int=0):
315
+ def SE(s_loc:list,e_loc:list,n:int=1,mat:int=1,sect:int=1,angle:float=0, group = "" , id: int = 0):
295
316
  beam_nodes =[]
296
317
  beam_obj = []
297
318
  i_loc = np.linspace(s_loc,e_loc,n+1)
@@ -302,12 +323,12 @@ class Element:
302
323
  for i in range(n):
303
324
  if id == 0 : id_new = 0
304
325
  else: id_new = id+i
305
- beam_obj.append(Element.Truss(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,id_new))
326
+ beam_obj.append(Element.Truss(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,group,id_new))
306
327
 
307
328
  return beam_obj
308
329
 
309
330
  class Plate(_common):
310
- def __init__(self, nodes: list, stype: int = 1, mat: int = 1, sect: int = 1, angle: float = 0, id: int = 0):
331
+ def __init__(self, nodes: list, stype: int = 1, mat: int = 1, sect: int = 1, angle: float = 0, group = "" , id: int = 0):
311
332
  """
312
333
  Creates a PLATE element.
313
334
 
@@ -317,6 +338,7 @@ class Element:
317
338
  mat: Material property number (default 1)
318
339
  sect: Section (thickness) property number (default 1)
319
340
  angle: Material angle for orthotropic materials in degrees (default 0.0)
341
+ group: Structure group of the element (str or list; 'SG1' or ['SG1','SG2'])
320
342
  id: Element ID (default 0 for auto-increment)
321
343
 
322
344
  Examples:
@@ -338,10 +360,11 @@ class Element:
338
360
  self.NODE = nodes
339
361
  self.ANGLE = angle
340
362
  self.STYPE = stype
363
+ self._GROUP = group
341
364
  _ADD(self)
342
365
 
343
366
  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):
367
+ def __init__(self, i: int, j: int, stype: int, mat: int = 1, sect: int = 1, angle: float = 0, group = "" , id: int = 0, non_len: float = None, cable_type: int = None, tens: float = None, t_limit: float = None):
345
368
  """
346
369
  Creates a TENSTR (Tension-only) element.
347
370
 
@@ -352,6 +375,7 @@ class Element:
352
375
  mat: Material property number (default 1)
353
376
  sect: Section property number (default 1)
354
377
  angle: Beta angle for section orientation in degrees (default 0.0)
378
+ group: Structure group of the element (str or list; 'SG1' or ['SG1','SG2'])
355
379
  id: Element ID (default 0 for auto-increment)
356
380
  non_len: Non-linear length parameter for Hook/Cable (default None)
357
381
  cable_type: Cable type for stype=3 (1=Pretension, 2=Horizontal, 3=Lu) (default None)
@@ -380,6 +404,7 @@ class Element:
380
404
  self.NODE = [i, j]
381
405
  self.ANGLE = angle
382
406
  self.STYPE = stype
407
+ self._GROUP = group
383
408
 
384
409
  # Handle subtype-specific parameters
385
410
  if stype == 1: # Tension-only specific
@@ -403,7 +428,7 @@ class Element:
403
428
  _ADD(self)
404
429
 
405
430
  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):
431
+ def __init__(self, i: int, j: int, stype: int, mat: int = 1, sect: int = 1, angle: float = 0, group = "" , id: int = 0, tens: float = None, t_limit: float = None, non_len: float = None):
407
432
  """
408
433
  Creates a COMPTR (Compression-only) element.
409
434
 
@@ -414,6 +439,7 @@ class Element:
414
439
  mat: Material property number (default 1)
415
440
  sect: Section property number (default 1)
416
441
  angle: Beta angle for section orientation in degrees (default 0.0)
442
+ group: Structure group of the element (str or list; 'SG1' or ['SG1','SG2'])
417
443
  id: Element ID (default 0 for auto-increment)
418
444
  tens: Allowable tension or initial compression force (default None)
419
445
  t_limit: Compression limit value. If provided, the compression limit flag is set to True. (default None)
@@ -438,6 +464,7 @@ class Element:
438
464
  self.NODE = [i, j]
439
465
  self.ANGLE = angle
440
466
  self.STYPE = stype
467
+ self._GROUP = group
441
468
 
442
469
  # Handle subtype-specific parameters
443
470
  if stype == 1: # Compression-only specific
@@ -453,7 +480,7 @@ class Element:
453
480
  _ADD(self)
454
481
 
455
482
  class Solid(_common):
456
- def __init__(self, nodes: list, mat: int = 1, sect: int = 0, id: int = 0):
483
+ def __init__(self, nodes: list, mat: int = 1, sect: int = 0, group = "" , id: int = 0):
457
484
  """
458
485
  Creates a SOLID element for 3D analysis.
459
486
 
@@ -463,6 +490,7 @@ class Element:
463
490
  - 6 nodes: Pentahedral element
464
491
  - 8 nodes: Hexahedral element
465
492
  mat: Material property number (default 1)
493
+ group: Structure group of the element (str or list; 'SG1' or ['SG1','SG2'])
466
494
  id: Element ID (default 0 for auto-increment)
467
495
 
468
496
  Examples:
@@ -484,6 +512,7 @@ class Element:
484
512
  self.MATL = mat
485
513
  self.SECT = sect # Solid elements don't use section properties
486
514
  self.NODE = nodes
515
+ self._GROUP = group
487
516
  _ADD(self)
488
517
 
489
518