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.
Files changed (40) hide show
  1. midas_civil/_BoundaryChangeAssignment.py +278 -0
  2. midas_civil/__init__.py +51 -0
  3. midas_civil/_analysiscontrol.py +585 -0
  4. midas_civil/_boundary.py +888 -0
  5. midas_civil/_construction.py +1004 -0
  6. midas_civil/_element.py +1346 -0
  7. midas_civil/_group.py +337 -0
  8. midas_civil/_load.py +967 -0
  9. midas_civil/_loadcomb.py +159 -0
  10. midas_civil/_mapi.py +249 -0
  11. midas_civil/_material.py +1692 -0
  12. midas_civil/_model.py +522 -0
  13. midas_civil/_movingload.py +1479 -0
  14. midas_civil/_node.py +532 -0
  15. midas_civil/_result_table.py +929 -0
  16. midas_civil/_result_test.py +5455 -0
  17. midas_civil/_section/_TapdbSecSS.py +175 -0
  18. midas_civil/_section/__init__.py +413 -0
  19. midas_civil/_section/_compositeSS.py +283 -0
  20. midas_civil/_section/_dbSecSS.py +164 -0
  21. midas_civil/_section/_offsetSS.py +53 -0
  22. midas_civil/_section/_pscSS copy.py +455 -0
  23. midas_civil/_section/_pscSS.py +822 -0
  24. midas_civil/_section/_tapPSC12CellSS.py +565 -0
  25. midas_civil/_section/_unSupp.py +58 -0
  26. midas_civil/_settlement.py +161 -0
  27. midas_civil/_temperature.py +677 -0
  28. midas_civil/_tendon.py +1016 -0
  29. midas_civil/_thickness.py +147 -0
  30. midas_civil/_utils.py +529 -0
  31. midas_civil/_utilsFunc/__init__.py +0 -0
  32. midas_civil/_utilsFunc/_line2plate.py +636 -0
  33. midas_civil/_view.py +891 -0
  34. midas_civil/_view_trial.py +430 -0
  35. midas_civil/_visualise.py +347 -0
  36. midas_civil-1.4.1.dist-info/METADATA +74 -0
  37. midas_civil-1.4.1.dist-info/RECORD +40 -0
  38. midas_civil-1.4.1.dist-info/WHEEL +5 -0
  39. midas_civil-1.4.1.dist-info/licenses/LICENSE +21 -0
  40. midas_civil-1.4.1.dist-info/top_level.txt +1 -0
midas_civil/_load.py ADDED
@@ -0,0 +1,967 @@
1
+ from ._mapi import MidasAPI
2
+ from ._group import Group
3
+ from ._element import elemByID
4
+ import numpy as np
5
+ from typing import Literal
6
+
7
+ _presDir = Literal['LX','LY','LZ','GX','GY','GZ','VECTOR']
8
+ _beamLoadDir = Literal['LX','LY','LZ','GX','GY','GZ']
9
+ _beamLoadType = Literal['CONLOAD','CONMOMENT','UNILOAD','UNIMOMENT']
10
+ _lineDistType = Literal['Abs','Rel']
11
+ # ----- Extend for list of nodes/elems -----
12
+
13
+ def _ADD_NodalLoad(self):
14
+ if isinstance(self.NODE,int):
15
+ Load.Nodal.data.append(self)
16
+ elif isinstance(self.NODE,list):
17
+ for nID in self.NODE:
18
+ Load.Nodal(nID,self.LCN,self.LDGR,self.FX,self.FY,self.FZ,self.MX,self.MY,self.MZ,self.ID)
19
+
20
+ def _ADD_PressureLoad(self):
21
+ if isinstance(self.ELEM,int):
22
+ Load.Pressure.data.append(self)
23
+ elif isinstance(self.ELEM,list):
24
+ for eID in self.ELEM:
25
+ Load.Pressure(eID,self.LCN,self.LDGR,self.DIR,self.PRES,self.VECTOR,self.bPROJ,self.ID)
26
+
27
+
28
+ def _ADD_BeamLoad(self):
29
+ if isinstance(self.ELEMENT,int):
30
+ Load.Beam.data.append(self)
31
+ elif isinstance(self.ELEMENT,list):
32
+ for eID in self.ELEMENT:
33
+ Load.Beam(eID,self.LCN,self.LDGR,self.VALUE,self.DIRECTION,self.D,self.P,self.CMD,self.TYPE,self.USE_ECCEN,self.USE_PROJECTION,
34
+ self.ECCEN_DIR,self.ECCEN_TYPE,self.IECC,self.JECC,self.USE_H,self.I_H,self.J_H,self.ID)
35
+
36
+
37
+
38
+ #11 Class to define Load Cases:
39
+ class Load_Case:
40
+ """Type symbol (Refer Static Load Case section in the Onine API Manual, Load Case names.
41
+ \nSample: Load_Case("USER", "Case 1", "Case 2", ..., "Case n")"""
42
+ maxID = 0
43
+ maxNO = 0
44
+ cases = []
45
+ types = ["USER", "D", "DC", "DW", "DD", "EP", "EANN", "EANC", "EAMN", "EAMC", "EPNN", "EPNC", "EPMN", "EPMC", "EH", "EV", "ES", "EL", "LS", "LSC",
46
+ "L", "LC", "LP", "IL", "ILP", "CF", "BRK", "BK", "CRL", "PS", "B", "WP", "FP", "SF", "WPR", "W", "WL", "STL", "CR", "SH", "T", "TPG", "CO",
47
+ "CT", "CV", "E", "FR", "IP", "CS", "ER", "RS", "GE", "LR", "S", "R", "LF", "RF", "GD", "SHV", "DRL", "WA", "WT", "EVT", "EEP", "EX", "I", "EE"]
48
+ def __init__(self, type, *name):
49
+ self.TYPE = type
50
+ self.NAME = name
51
+ self.ID = []
52
+ self.NO = []
53
+ for i in range(len(self.NAME)):
54
+ if Load_Case.cases == []:
55
+ self.ID.append(i+1)
56
+ self.NO.append(i+1)
57
+ if Load_Case.cases != []:
58
+ self.ID.append(Load_Case.maxID + i + 1)
59
+ self.NO.append(Load_Case.maxNO + i + 1)
60
+ Load_Case.cases.append(self)
61
+ Load_Case.maxID = max(max(self.ID),Load_Case.maxID)
62
+ Load_Case.maxNO = max(max(self.NO),Load_Case.maxNO)
63
+
64
+ @classmethod
65
+ def json(cls):
66
+ ng = []
67
+ json = {"Assign":{}}
68
+ for i in cls.cases:
69
+ if i.TYPE in cls.types:
70
+ for j in i.ID:
71
+ json['Assign'][j] = {
72
+ "NO": i.NO[i.ID.index(j)],
73
+ "NAME": i.NAME[i.ID.index(j)],
74
+ "TYPE": i.TYPE}
75
+ else:
76
+ ng.append(i.TYPE)
77
+ if ng != []: print(f"These load case types are incorrect: {ng}.\nPlease check API Manual.")
78
+ return json
79
+
80
+ @staticmethod
81
+ def create():
82
+ MidasAPI("PUT","/db/stld",Load_Case.json())
83
+
84
+ @staticmethod
85
+ def get():
86
+ return MidasAPI("GET","/db/stld")
87
+
88
+ @staticmethod
89
+ def sync():
90
+ Load_Case.maxID = 0
91
+ Load_Case.maxNO = 0
92
+ a = Load_Case.get()
93
+ if a != {'message': ''}:
94
+ if list(a['STLD'].keys()) != []:
95
+ Load_Case.cases = []
96
+ for j in a['STLD'].keys():
97
+ lc = Load_Case(a['STLD'][j]['TYPE'], a['STLD'][j]['NAME'])
98
+ lcID = int(j)
99
+ lCNO = int(a['STLD'][j]['NO'])
100
+ lc.ID = [lcID]
101
+ lc.NO = [lCNO]
102
+
103
+ Load_Case.maxID = max(Load_Case.maxID ,lcID )
104
+ Load_Case.maxNO = max(Load_Case.maxNO ,lCNO )
105
+
106
+ @classmethod
107
+ def delete(cls):
108
+ cls.clear()
109
+ return MidasAPI("DELETE","/db/stld")
110
+
111
+ @classmethod
112
+ def clear(cls):
113
+ cls.maxID = 0
114
+ cls.maxNO = 0
115
+ cls.cases=[]
116
+ #---------------------------------------------------------------------------------------------------------------
117
+
118
+
119
+
120
+ class Load:
121
+
122
+ @classmethod
123
+ def create(cls):
124
+ if Load_Case.cases!=[]: Load_Case.create()
125
+ if cls.SW.data!=[]: cls.SW.create()
126
+ if cls.Nodal.data!=[]: cls.Nodal.create()
127
+ if cls.Beam.data!=[]: cls.Beam.create()
128
+ if cls.Pressure.data!=[]: cls.Pressure.create()
129
+
130
+ @classmethod
131
+ def clear(cls):
132
+ Load_Case.clear()
133
+ cls.SW.clear()
134
+ cls.Nodal.clear()
135
+ cls.Beam.clear()
136
+ cls.Pressure.clear()
137
+
138
+ class SW:
139
+ """Load Case Name, direction, Value, Load Group.\n
140
+ Sample: Load_SW("Self-Weight", "Z", -1, "DL")"""
141
+ data = []
142
+ def __init__(self, load_case, dir = "Z", value = -1, load_group = ""):
143
+
144
+ chk = 0
145
+ for i in Load_Case.cases:
146
+ if load_case in i.NAME: chk = 1
147
+ if chk == 0: Load_Case("D", load_case)
148
+
149
+ if load_group != "":
150
+ chk = 0
151
+ a = [v['NAME'] for v in Group.Load.json()["Assign"].values()]
152
+ if load_group in a: chk = 1
153
+ if chk == 0: Group.Load(load_group)
154
+
155
+ if type(value)==int:
156
+ if dir == "X":
157
+ fv = [value, 0, 0]
158
+ elif dir == "Y":
159
+ fv = [0, value, 0]
160
+ else:
161
+ fv = [0, 0, value]
162
+ elif type(value)==list:
163
+ fv = value
164
+ else: fv = [0,0,-1]
165
+
166
+
167
+ self.LC = load_case
168
+ self.DIR = dir
169
+ self.FV = fv
170
+ self.LG = load_group
171
+ self.ID = len(Load.SW.data) + 1
172
+ Load.SW.data.append(self)
173
+
174
+ @classmethod
175
+ def json(cls):
176
+ json = {"Assign":{}}
177
+ for i in cls.data:
178
+ json["Assign"][i.ID] = {
179
+ "LCNAME": i.LC,
180
+ "GROUP_NAME": i.LG,
181
+ "FV": i.FV
182
+ }
183
+ return json
184
+
185
+ @staticmethod
186
+ def create():
187
+ MidasAPI("PUT","/db/BODF",Load.SW.json())
188
+
189
+ @staticmethod
190
+ def get():
191
+ return MidasAPI("GET","/db/BODF")
192
+
193
+ @classmethod
194
+ def delete(cls):
195
+ cls.clear()
196
+ return MidasAPI("DELETE","/db/BODF")
197
+
198
+ @classmethod
199
+ def clear(cls):
200
+ cls.data=[]
201
+
202
+ @staticmethod
203
+ def sync():
204
+ a = Load.SW.get()
205
+ if a != {'message': ''}:
206
+ for i in list(a['BODF'].keys()):
207
+ if a['BODF'][i]['FV'][0] != 0:
208
+ di = "X"
209
+ va = a['BODF'][i]['FV'][0]
210
+ elif a['BODF'][i]['FV'][1] != 0:
211
+ di = "Y"
212
+ va = a['BODF'][i]['FV'][1]
213
+ else:
214
+ di = "Z"
215
+ va = a['BODF'][i]['FV'][2]
216
+ Load.SW(a['BODF'][i]['LCNAME'], di, va, a['BODF'][i]['GROUP_NAME'])
217
+
218
+
219
+ #-------------------------------- NODAL LOADS ------------------------------------------------------------
220
+
221
+ #18 Class to add Nodal Loads:
222
+ class Nodal:
223
+ """Creates node loads and converts to JSON format.
224
+ Example: Load_Node(101, "LC1", "Group1", FZ = 10)
225
+ """
226
+ data = []
227
+ def __init__(self, node, load_case, load_group = "", FX:float = 0, FY:float = 0, FZ:float= 0, MX:float =0, MY:float =0, MZ:float=0, id = None):
228
+
229
+
230
+ chk = 0
231
+ for i in Load_Case.cases:
232
+ if load_case in i.NAME: chk = 1
233
+ if chk == 0: Load_Case("D", load_case)
234
+ if load_group != "":
235
+ chk = 0
236
+ a = [v['NAME'] for v in Group.Load.json()["Assign"].values()]
237
+ if load_group in a: chk = 1
238
+ if chk == 0: Group.Load(load_group)
239
+
240
+
241
+ self.NODE = node
242
+ self.LCN = load_case
243
+ self.LDGR = load_group
244
+ self.FX = FX
245
+ self.FY = FY
246
+ self.FZ = FZ
247
+ self.MX = MX
248
+ self.MY = MY
249
+ self.MZ = MZ
250
+
251
+ if id is None:
252
+ self.ID = len(Load.Nodal.data) + 1
253
+ else:
254
+ self.ID = id
255
+
256
+ _ADD_NodalLoad(self)
257
+ # Load.Nodal.data.append(self)
258
+
259
+ @classmethod
260
+ def json(cls):
261
+ json = {"Assign": {}}
262
+ for i in cls.data:
263
+ if i.NODE not in list(json["Assign"].keys()):
264
+ json["Assign"][i.NODE] = {"ITEMS": []}
265
+
266
+ json["Assign"][i.NODE]["ITEMS"].append({
267
+ "ID": i.ID,
268
+ "LCNAME": i.LCN,
269
+ "GROUP_NAME": i.LDGR,
270
+ "FX": i.FX,
271
+ "FY": i.FY,
272
+ "FZ": i.FZ,
273
+ "MX": i.MX,
274
+ "MY": i.MY,
275
+ "MZ": i.MZ
276
+ })
277
+ return json
278
+
279
+ @classmethod
280
+ def create(cls):
281
+ MidasAPI("PUT", "/db/CNLD",cls.json())
282
+
283
+ @classmethod
284
+ def get(cls):
285
+ return MidasAPI("GET", "/db/CNLD")
286
+
287
+ @classmethod
288
+ def delete(cls):
289
+ cls.clear()
290
+ return MidasAPI("DELETE", "/db/CNLD")
291
+
292
+ @classmethod
293
+ def clear(cls):
294
+ cls.data=[]
295
+
296
+ @classmethod
297
+ def sync(cls):
298
+ cls.data = []
299
+ a = cls.get()
300
+ if a != {'message': ''}:
301
+ for i in a['CNLD'].keys():
302
+ for j in range(len(a['CNLD'][i]['ITEMS'])):
303
+ Load.Nodal(int(i),a['CNLD'][i]['ITEMS'][j]['LCNAME'], a['CNLD'][i]['ITEMS'][j]['GROUP_NAME'],
304
+ a['CNLD'][i]['ITEMS'][j]['FX'], a['CNLD'][i]['ITEMS'][j]['FY'], a['CNLD'][i]['ITEMS'][j]['FZ'],
305
+ a['CNLD'][i]['ITEMS'][j]['MX'], a['CNLD'][i]['ITEMS'][j]['MY'], a['CNLD'][i]['ITEMS'][j]['MZ'],
306
+ a['CNLD'][i]['ITEMS'][j]['ID'])
307
+ #---------------------------------------------------------------------------------------------------------------
308
+
309
+ #19 Class to define Beam Loads:
310
+ class Beam:
311
+ data = []
312
+ def __init__(self, element:int, load_case: str, load_group: str = "", value: float=0, direction:_beamLoadDir = "GZ",
313
+ D:list = [0, 1, 0, 0], P = [0, 0, 0, 0], cmd = "BEAM", typ:_beamLoadType = "UNILOAD", use_ecc = False, use_proj = False,
314
+ eccn_dir = "LY", eccn_type = 1, ieccn = 0, jeccn = 0, adnl_h = False, adnl_h_i = 0, adnl_h_j = 0,id = None):
315
+ """
316
+ element: Element ID or list of Element IDs
317
+ load_case (str): Load case name
318
+ load_group (str, optional): Load group name. Defaults to ""
319
+ value (float): Load value
320
+ direction (str): Load direction (e.g., "GX", "GY", "GZ", "LX", "LY", "LZ"). Defaults to "GZ"
321
+ D: Relative distance (list with 4 values, optional) based on length of element. Defaults to [0, 1, 0, 0]
322
+ P: Magnitude of UDL at corresponding position of D (list with 4 values, optional). Defaults to [value, value, 0, 0]
323
+ cmd: Load command (e.g., "BEAM", "LINE", "TYPICAL")
324
+ typ: Load type (e.g., "CONLOAD", "CONMOMENT", "UNITLOAD", "UNIMOMENT", "PRESSURE")
325
+ use_ecc: Use eccentricity (True or False). Defaults to False.
326
+ use_proj: Use projection (True or False). Defaults to False.
327
+ eccn_dir: Eccentricity direction (e.g., "GX", "GY", "GZ", "LX", "LY", "LZ"). Defaults to "LZ"
328
+ eccn_type: Eccentricity from offset (1) or centroid (0). Defaults to 1.
329
+ ieccn, jeccn: Eccentricity values at i-end and j-end of the element
330
+ adnl_h: Consider additional H when applying pressure on beam (True or False). Defaults to False.
331
+ adnl_h_i, adnl_h_j: Additional H values at i-end and j-end of the beam. Defaults to 0.
332
+ id (default=None): Load ID. Defaults to auto-generated\n
333
+ Example:
334
+ - Load_Beam(115, "UDL_Case", "", -50.0, "GZ") # No eccentricity
335
+ - Load_Beam(115, "UDL_Case", "", -50.0, "GZ", ieccn=2.5) # With eccentricity
336
+ """
337
+
338
+ chk = 0
339
+ for i in Load_Case.cases:
340
+ if load_case in i.NAME: chk = 1
341
+ if chk == 0: Load_Case("D", load_case)
342
+ if load_group != "":
343
+ chk = 0
344
+ a = [v['NAME'] for v in Group.Load.json()["Assign"].values()]
345
+ if load_group in a: chk = 1
346
+ if chk == 0: Group.Load(load_group)
347
+ D = (D + [0] * 4)[:4]
348
+ P = (P + [0] * 4)[:4]
349
+ if P == [0, 0, 0, 0]: P = [value, value, 0, 0]
350
+ if eccn_type not in (0, 1):
351
+ eccn_type = 1
352
+ if direction not in ("GX", "GY", "GZ", "LX", "LY", "LZ"): direction = "GZ"
353
+ if eccn_dir not in ("GX", "GY", "GZ", "LX", "LY", "LZ"): eccn_dir = "LY"
354
+ if cmd not in ("BEAM", "LINE", "TYPICAL"): cmd = "BEAM"
355
+ if typ not in ("CONLOAD", "CONMOMENT", "UNILOAD", "UNIMOMENT","PRESSURE"): typ = "UNILOAD"
356
+ if use_ecc == False:
357
+ if ieccn != 0 or jeccn != 0: use_ecc = True
358
+ self.ELEMENT = element
359
+ self.LCN = load_case
360
+ self.LDGR = load_group
361
+ self.VALUE = value
362
+ self.DIRECTION = direction
363
+ self.CMD = cmd
364
+ self.TYPE = typ
365
+ self.USE_PROJECTION = use_proj
366
+ self.USE_ECCEN = use_ecc
367
+ self.ECCEN_TYPE = eccn_type
368
+ self.ECCEN_DIR = eccn_dir
369
+ self.IECC = ieccn
370
+ if jeccn == 0:
371
+ self.JECC = 0
372
+ self.USE_JECC = False
373
+ else:
374
+ self.JECC = jeccn
375
+ self.USE_JECC = True
376
+ self.D = D
377
+ self.P = P
378
+ self.USE_H = adnl_h
379
+ self.I_H = adnl_h_i
380
+ if adnl_h == 0:
381
+ self.USE_JH = False
382
+ self.J_H = 0
383
+ else:
384
+ self.USE_JH = True
385
+ self.J_H = adnl_h_j
386
+
387
+ if id is None:
388
+ self.ID = len(Load.Beam.data) + 1
389
+ else:
390
+ self.ID = id
391
+
392
+ _ADD_BeamLoad(self)
393
+ # Load.Beam.data.append(self)
394
+
395
+ @classmethod
396
+ def json(cls):
397
+ json = {"Assign": {}}
398
+ for i in cls.data:
399
+ item_data = {
400
+ "ID": i.ID,
401
+ "LCNAME": i.LCN,
402
+ "GROUP_NAME": i.LDGR,
403
+ "CMD": i.CMD,
404
+ "TYPE": i.TYPE,
405
+ "DIRECTION": i.DIRECTION,
406
+ "USE_PROJECTION": i.USE_PROJECTION,
407
+ "USE_ECCEN": i.USE_ECCEN,
408
+ "D": i.D,
409
+ "P": i.P
410
+ }
411
+ if i.USE_ECCEN == True:
412
+ item_data.update({
413
+ "ECCEN_TYPE": i.ECCEN_TYPE,
414
+ "ECCEN_DIR": i.ECCEN_DIR,
415
+ "I_END": i.IECC,
416
+ "J_END": i.JECC,
417
+ "USE_J_END": i.USE_JECC
418
+ })
419
+ if i.TYPE == "PRESSURE":
420
+ item_data.update({
421
+ "USE_ADDITIONAL": i.USE_H,
422
+ "ADDITIONAL_I_END": i.I_H,
423
+ "ADDITIONAL_J_END": i.J_H,
424
+ "USE_ADDITIONAL_J_END": i.J_H
425
+ })
426
+ if i.ELEMENT not in json["Assign"]:
427
+ json["Assign"][i.ELEMENT] = {"ITEMS": []}
428
+ json["Assign"][i.ELEMENT]["ITEMS"].append(item_data)
429
+ return json
430
+
431
+ @classmethod
432
+ def create(cls):
433
+ MidasAPI("PUT", "/db/bmld", cls.json())
434
+
435
+ @classmethod
436
+ def get(cls):
437
+ return MidasAPI("GET", "/db/bmld")
438
+
439
+ @classmethod
440
+ def delete(cls):
441
+ cls.clear()
442
+ return MidasAPI("DELETE", "/db/bmld")
443
+
444
+ @classmethod
445
+ def clear(cls):
446
+ cls.data=[]
447
+
448
+ @classmethod
449
+ def sync(cls):
450
+ cls.data = []
451
+ a = cls.get()
452
+ if a != {'message': ''}:
453
+ for i in a['BMLD'].keys():
454
+ for j in range(len(a['BMLD'][i]['ITEMS'])):
455
+ if a['BMLD'][i]['ITEMS'][j]['USE_ECCEN'] == True and a['BMLD'][i]['ITEMS'][j]['USE_ADDITIONAL'] == True:
456
+ Load.Beam(int(i),a['BMLD'][i]['ITEMS'][j]['LCNAME'], a['BMLD'][i]['ITEMS'][j]['GROUP_NAME'], a['BMLD'][i]['ITEMS'][j]['P'],
457
+ a['BMLD'][i]['ITEMS'][j]['DIRECTION'], a['BMLD'][i]['ITEMS'][j]['D'], a['BMLD'][i]['ITEMS'][j]['P'],
458
+ a['BMLD'][i]['ITEMS'][j]['CMD'], a['BMLD'][i]['ITEMS'][j]['TYPE'], a['BMLD'][i]['ITEMS'][j]['USE_ECCEN'], a['BMLD'][i]['ITEMS'][j]['USE_PROJECTION'],
459
+ a['BMLD'][i]['ITEMS'][j]['ECCEN_DIR'], a['BMLD'][i]['ITEMS'][j]['ECCEN_TYPE'], a['BMLD'][i]['ITEMS'][j]['I_END'], a['BMLD'][i]['ITEMS'][j]['J_END'],
460
+ a['BMLD'][i]['ITEMS'][j]['USE_ADDITIONAL'], a['BMLD'][i]['ITEMS'][j]['ADDITIONAL_I_END'], a['BMLD'][i]['ITEMS'][j]['ADDITIONAL_J_END'], a['BMLD'][i]['ITEMS'][j]['ID'])
461
+ elif a['BMLD'][i]['ITEMS'][j]['USE_ECCEN'] == False and a['BMLD'][i]['ITEMS'][j]['USE_ADDITIONAL'] == True:
462
+ Load.Beam(int(i),a['BMLD'][i]['ITEMS'][j]['LCNAME'], a['BMLD'][i]['ITEMS'][j]['GROUP_NAME'], a['BMLD'][i]['ITEMS'][j]['P'],
463
+ a['BMLD'][i]['ITEMS'][j]['DIRECTION'], a['BMLD'][i]['ITEMS'][j]['D'], a['BMLD'][i]['ITEMS'][j]['P'],
464
+ a['BMLD'][i]['ITEMS'][j]['CMD'], a['BMLD'][i]['ITEMS'][j]['TYPE'], a['BMLD'][i]['ITEMS'][j]['USE_ECCEN'], a['BMLD'][i]['ITEMS'][j]['USE_PROJECTION'],
465
+ adnl_h = a['BMLD'][i]['ITEMS'][j]['USE_ADDITIONAL'], adnl_h_i = a['BMLD'][i]['ITEMS'][j]['ADDITIONAL_I_END'], adnl_h_j = a['BMLD'][i]['ITEMS'][j]['ADDITIONAL_J_END'],id= a['BMLD'][i]['ITEMS'][j]['ID'])
466
+ elif a['BMLD'][i]['ITEMS'][j]['USE_ECCEN'] == True and a['BMLD'][i]['ITEMS'][j]['USE_ADDITIONAL'] == False:
467
+ Load.Beam(int(i),a['BMLD'][i]['ITEMS'][j]['LCNAME'], a['BMLD'][i]['ITEMS'][j]['GROUP_NAME'], a['BMLD'][i]['ITEMS'][j]['P'],
468
+ a['BMLD'][i]['ITEMS'][j]['DIRECTION'], a['BMLD'][i]['ITEMS'][j]['D'], a['BMLD'][i]['ITEMS'][j]['P'],
469
+ a['BMLD'][i]['ITEMS'][j]['CMD'], a['BMLD'][i]['ITEMS'][j]['TYPE'], a['BMLD'][i]['ITEMS'][j]['USE_ECCEN'], a['BMLD'][i]['ITEMS'][j]['USE_PROJECTION'],
470
+ a['BMLD'][i]['ITEMS'][j]['ECCEN_DIR'], a['BMLD'][i]['ITEMS'][j]['ECCEN_TYPE'], a['BMLD'][i]['ITEMS'][j]['I_END'], a['BMLD'][i]['ITEMS'][j]['J_END'],id=a['BMLD'][i]['ITEMS'][j]['ID'])
471
+ else:
472
+ Load.Beam(int(i),a['BMLD'][i]['ITEMS'][j]['LCNAME'], a['BMLD'][i]['ITEMS'][j]['GROUP_NAME'],a['BMLD'][i]['ITEMS'][j]['P'],
473
+ a['BMLD'][i]['ITEMS'][j]['DIRECTION'], a['BMLD'][i]['ITEMS'][j]['D'], a['BMLD'][i]['ITEMS'][j]['P'],
474
+ a['BMLD'][i]['ITEMS'][j]['CMD'], a['BMLD'][i]['ITEMS'][j]['TYPE'], a['BMLD'][i]['ITEMS'][j]['USE_ECCEN'], a['BMLD'][i]['ITEMS'][j]['USE_PROJECTION'],id= a['BMLD'][i]['ITEMS'][j]['ID'])
475
+
476
+
477
+ #-------------------------------- Load to Mass ------------------------------------------------------------
478
+
479
+ #20 Class to add Load to Mass:
480
+ class LoadToMass:
481
+ """
482
+ Creates load-to-mass conversion entries and converts them to JSON format.
483
+
484
+ Example:
485
+ Load.LoadToMass("Z", ["DL", "LL"], [1.0, 0.5])
486
+
487
+ Args:
488
+ dir (str):
489
+ Mass Direction - "X", "Y", "Z", "XY", "YZ", "XZ", "XYZ".
490
+ If invalid, defaults to "XYZ".
491
+ load_case (list | str):
492
+ List of load case names or a single case name as string.
493
+ load_factor (list | float, optional):
494
+ List of scale factors corresponding to `load_case`.
495
+ If None or shorter than `load_case`, remaining factors default to 1.0.
496
+ nodal_load (bool, optional):
497
+ Include nodal loads. Defaults to True.
498
+ beam_load (bool, optional):
499
+ Include beam loads. Defaults to True.
500
+ floor_load (bool, optional):
501
+ Include floor loads. Defaults to True.
502
+ pressure (bool, optional):
503
+ Include pressure loads. Defaults to True.
504
+ gravity (float, optional):
505
+ Gravity acceleration. Defaults to 9.806.
506
+ """
507
+ data = []
508
+
509
+ def __init__(self, dir, load_case, load_factor=None, nodal_load=True, beam_load=True,
510
+ floor_load=True, pressure=True, gravity=9.806):
511
+
512
+ valid_directions = ["X", "Y", "Z", "XY", "YZ", "XZ", "XYZ"]
513
+ if dir not in valid_directions:
514
+ dir = "XYZ"
515
+
516
+ if not isinstance(load_case, list):
517
+ load_case = [load_case]
518
+
519
+ if load_factor is None:
520
+ load_factor = [1.0] * len(load_case)
521
+ elif not isinstance(load_factor, list):
522
+ load_factor = [load_factor]
523
+
524
+ while len(load_factor) < len(load_case):
525
+ load_factor.append(1.0)
526
+
527
+ for case in load_case:
528
+ chk = 0
529
+ for i in Load_Case.cases:
530
+ if case in i.NAME:
531
+ chk = 1
532
+ if chk == 0:
533
+ print(f"Warning: Load case '{case}' does not exist!")
534
+
535
+ self.DIR = dir
536
+ self.LOAD_CASE = load_case
537
+ self.LOAD_FACTOR = load_factor
538
+ self.NODAL = nodal_load
539
+ self.BEAM = beam_load
540
+ self.FLOOR = floor_load
541
+ self.PRESSURE = pressure
542
+ self.GRAVITY = gravity
543
+
544
+ Load.LoadToMass.data.append(self)
545
+
546
+ @classmethod
547
+ def json(cls):
548
+ json_data = {"Assign": {}}
549
+
550
+ for idx, load_obj in enumerate(cls.data, start=1):
551
+ vlc_array = []
552
+ for i, case_name in enumerate(load_obj.LOAD_CASE):
553
+ vlc_array.append({
554
+ "LCNAME": case_name,
555
+ "FACTOR": load_obj.LOAD_FACTOR[i]
556
+ })
557
+
558
+ json_data["Assign"][str(idx)] = {
559
+ "DIR": load_obj.DIR,
560
+ "bNODAL": load_obj.NODAL,
561
+ "bBEAM": load_obj.BEAM,
562
+ "bFLOOR": load_obj.FLOOR,
563
+ "bPRES": load_obj.PRESSURE,
564
+ "GRAV": load_obj.GRAVITY,
565
+ "vLC": vlc_array
566
+ }
567
+
568
+ return json_data
569
+
570
+ @classmethod
571
+ def create(cls):
572
+ return MidasAPI("PUT", "/db/ltom", cls.json())
573
+
574
+ @classmethod
575
+ def get(cls):
576
+ return MidasAPI("GET", "/db/ltom")
577
+
578
+ @classmethod
579
+ def delete(cls):
580
+ cls.data = []
581
+ return MidasAPI("DELETE", "/db/ltom")
582
+
583
+ @classmethod
584
+ def sync(cls):
585
+ cls.data = []
586
+ response = cls.get()
587
+
588
+ if response != {'message': ''}:
589
+ for key, item_data in response.get('LTOM', {}).items():
590
+ load_cases = []
591
+ load_factors = []
592
+
593
+ for lc_item in item_data.get('vLC', []):
594
+ load_cases.append(lc_item.get('LCNAME'))
595
+ load_factors.append(lc_item.get('FACTOR'))
596
+
597
+ Load.LoadToMass(
598
+ dir=item_data.get('DIR'),
599
+ load_case=load_cases,
600
+ load_factor=load_factors,
601
+ nodal_load=item_data.get('bNODAL'),
602
+ beam_load=item_data.get('bBEAM'),
603
+ floor_load=item_data.get('bFLOOR'),
604
+ pressure=item_data.get('bPRES'),
605
+ gravity=item_data.get('GRAV')
606
+ )
607
+
608
+
609
+ #-----------------------------------------------------------NodalMass-----------------
610
+ #21NodalMass
611
+
612
+ class NodalMass:
613
+ """Creates nodal mass and converts to JSON format.
614
+ Example: NodalMass(1, 1.5, 2.0, 3.0, 0.1, 0.2, 0.3)
615
+ """
616
+ data = []
617
+
618
+ def __init__(self, node_id, mX, mY=0, mZ=0, rmX=0, rmY=0, rmZ=0):
619
+ """
620
+ node_id (int): Node ID where the mass is applied (Required)
621
+ mX (float): Translational Lumped Mass in GCS X-direction (Required)
622
+ mY (float): Translational Lumped Mass in GCS Y-direction. Defaults to 0
623
+ mZ (float): Translational Lumped Mass in GCS Z-direction. Defaults to 0
624
+ rmX (float): Rotational Mass Moment of Inertia about GCS X-axis. Defaults to 0
625
+ rmY (float): Rotational Mass Moment of Inertia about GCS Y-axis. Defaults to 0
626
+ rmZ (float): Rotational Mass Moment of Inertia about GCS Z-axis. Defaults to 0
627
+ """
628
+ self.NODE_ID = node_id
629
+ self.MX = mX
630
+ self.MY = mY
631
+ self.MZ = mZ
632
+ self.RMX = rmX
633
+ self.RMY = rmY
634
+ self.RMZ = rmZ
635
+
636
+ Load.NodalMass.data.append(self)
637
+
638
+ @classmethod
639
+ def json(cls):
640
+ json_data = {"Assign": {}}
641
+
642
+ for mass_obj in cls.data:
643
+ json_data["Assign"][mass_obj.NODE_ID] = {
644
+ "mX": mass_obj.MX,
645
+ "mY": mass_obj.MY,
646
+ "mZ": mass_obj.MZ,
647
+ "rmX": mass_obj.RMX,
648
+ "rmY": mass_obj.RMY,
649
+ "rmZ": mass_obj.RMZ
650
+ }
651
+
652
+ return json_data
653
+
654
+ @classmethod
655
+ def create(cls):
656
+ return MidasAPI("PUT", "/db/nmas", cls.json())
657
+
658
+ @classmethod
659
+ def get(cls):
660
+ MidasAPI("GET", "/db/nmas")
661
+
662
+ @classmethod
663
+ def delete(cls):
664
+ cls.data = []
665
+ MidasAPI("DELETE", "/db/nmas")
666
+
667
+ @classmethod
668
+ def sync(cls):
669
+ cls.data = []
670
+ response = cls.get()
671
+
672
+ if response and response != {'message': ''}:
673
+ nmas_data = response.get('NMAS', {})
674
+
675
+ for node_id, item_data in nmas_data.items():
676
+ Load.NodalMass(
677
+ node_id=int(node_id),
678
+ mX=item_data.get('mX'),
679
+ mY=item_data.get('mY'),
680
+ mZ=item_data.get('mZ'),
681
+ rmX=item_data.get('rmX'),
682
+ rmY=item_data.get('rmY'),
683
+ rmZ=item_data.get('rmZ')
684
+ )
685
+
686
+ #-----------------------------------------------------------Specified Displacement-------------------------------------------------
687
+ class SpDisp:
688
+ """Creates specified displacement loads and converts to JSON format.
689
+ Example: SpDisp(10, "LL", "Group1", [1.5, 1.5, 1.5, 1.5, 0.5, 0.5])
690
+ """
691
+ data = []
692
+
693
+ def __init__(self, node, load_case, load_group="", values=[0, 0, 0, 0, 0, 0], id=None):
694
+ """
695
+ node (int): Node number (Required)
696
+ load_case (str): Load case name (Required)
697
+ load_group (str, optional): Load group name. Defaults to ""
698
+ values (list): Displacement values [Dx, Dy, Dz, Rx, Ry, Rz]. Defaults to [0, 0, 0, 0, 0, 0]
699
+ id (default=None): Load ID. Defaults to auto-generated
700
+ """
701
+
702
+ # Check if load case exists - give warning if not
703
+ chk = 0
704
+ for i in Load_Case.cases:
705
+ if load_case in i.NAME:
706
+ chk = 1
707
+ if chk == 0:
708
+ print(f"Warning: Load case '{load_case}' does not exist!")
709
+
710
+ # Check if load group exists and create if specified
711
+ if load_group != "":
712
+ chk = 0
713
+ a = [v['NAME'] for v in Group.Load.json()["Assign"].values()]
714
+ if load_group in a:
715
+ chk = 1
716
+ if chk == 0:
717
+ print(f"Warning: Load group '{load_group}' does not exist!")
718
+
719
+ # Ensure values is a list of 6 elements [Dx, Dy, Dz, Rx, Ry, Rz]
720
+ if not isinstance(values, list):
721
+ values = [0, 0, 0, 0, 0, 0]
722
+
723
+ # Pad or truncate to exactly 6 values
724
+ values = (values + [0] * 6)[:6]
725
+
726
+ self.NODE = node
727
+ self.LCN = load_case
728
+ self.LDGR = load_group
729
+ self.VALUES = values
730
+
731
+ if id is None:
732
+ self.ID = len(Load.SpDisp.data) + 1
733
+ else:
734
+ self.ID = id
735
+
736
+ Load.SpDisp.data.append(self)
737
+
738
+ @classmethod
739
+ def json(cls):
740
+ json_data = {"Assign": {}}
741
+
742
+ for i in cls.data:
743
+ if i.NODE not in list(json_data["Assign"].keys()):
744
+ json_data["Assign"][i.NODE] = {"ITEMS": []}
745
+
746
+ # Create VALUES array with OPT_FLAG logic
747
+ values_array = []
748
+ displacement_labels = ["Dx", "Dy", "Dz", "Rx", "Ry", "Rz"]
749
+
750
+ for idx, value in enumerate(i.VALUES):
751
+ values_array.append({
752
+ "OPT_FLAG": value != 0, # True if value > 0, False if value = 0
753
+ "DISPLACEMENT": float(value)
754
+ })
755
+
756
+ json_data["Assign"][i.NODE]["ITEMS"].append({
757
+ "ID": i.ID,
758
+ "LCNAME": i.LCN,
759
+ "GROUP_NAME": i.LDGR,
760
+ "VALUES": values_array
761
+ })
762
+
763
+ return json_data
764
+
765
+ @classmethod
766
+ def create(cls):
767
+ return MidasAPI("PUT", "/db/sdsp", cls.json())
768
+
769
+ @classmethod
770
+ def get(cls):
771
+ return MidasAPI("GET", "/db/sdsp")
772
+
773
+ @classmethod
774
+ def delete(cls):
775
+ cls.data = []
776
+ return MidasAPI("DELETE", "/db/sdsp")
777
+
778
+ @classmethod
779
+ def sync(cls):
780
+ cls.data = []
781
+ response = cls.get()
782
+
783
+ if response != {'message': ''}:
784
+ for node_key in response['SDSP'].keys():
785
+ for j in range(len(response['SDSP'][node_key]['ITEMS'])):
786
+ item = response['SDSP'][node_key]['ITEMS'][j]
787
+
788
+ # Extract displacement values from VALUES array
789
+ values = []
790
+ for val_item in item.get('VALUES', []):
791
+ values.append(val_item.get('DISPLACEMENT', 0))
792
+
793
+ Load.SpDisp(
794
+ int(node_key),
795
+ item['LCNAME'],
796
+ item['GROUP_NAME'],
797
+ values,
798
+ item['ID']
799
+ )
800
+ class Line:
801
+ def __init__(self, element_ids, load_case: str, load_group: str = "", D = [0, 1], P = [0, 0], direction:_beamLoadDir = "GZ",
802
+ type:_beamLoadType = "UNILOAD", distType:_lineDistType='Abs',use_ecc = False, use_proj = False,
803
+ eccn_dir:_beamLoadDir = "LY", eccn_type = 1, ieccn = 0, jeccn = 0, adnl_h = False, adnl_h_i = 0, adnl_h_j = 0,id = None) :
804
+
805
+ elem_IDS = []
806
+ elem_LEN = []
807
+
808
+ for eID in element_ids:
809
+ try:
810
+ elm_len = elemByID(eID).LENGTH
811
+ elem_IDS.append(eID)
812
+ elem_LEN.append(elm_len)
813
+ # print(f"ID = {eID} LEN = {elm_len}")
814
+ except: pass
815
+ cum_LEN = np.insert(np.cumsum(elem_LEN),0,0)
816
+ tot_LEN = cum_LEN[-1]
817
+
818
+ if distType == 'Rel':
819
+ D = np.array(D)*tot_LEN
820
+
821
+ if type == 'CONLOAD':
822
+ for i in range(len(D)):
823
+ for q in range(len(cum_LEN)):
824
+ if D[i] >= 0:
825
+ if D[i] < cum_LEN[q] :
826
+ # print(f'LOADING ELEMENT at {D[i]}m = {elem_IDS[q-1]}')
827
+ rel_loc = (D[i] - cum_LEN[q-1]) / elem_LEN[q-1]
828
+ # print(f"Relative location = {rel_loc}")
829
+ Load.Beam(element=elem_IDS[q-1],load_case=load_case,load_group=load_group,D=[rel_loc],P=[P[i]],direction=direction,
830
+ typ = "CONLOAD", use_ecc = use_ecc, use_proj = use_proj,
831
+ eccn_dir = eccn_dir, eccn_type = eccn_type, ieccn = ieccn, jeccn = jeccn, adnl_h = adnl_h, adnl_h_i = adnl_h_i, adnl_h_j = adnl_h_j,id=id)
832
+ break
833
+ if D[-1] == tot_LEN:
834
+ Load.Beam(element=elem_IDS[-1],load_case=load_case,load_group=load_group,D=[1,0,0,0],P=[P[-1]],direction=direction,
835
+ typ = "CONLOAD", use_ecc = use_ecc, use_proj = use_proj,
836
+ eccn_dir = eccn_dir, eccn_type = eccn_type, ieccn = ieccn, jeccn = jeccn, adnl_h = adnl_h, adnl_h_i = adnl_h_i, adnl_h_j = adnl_h_j,id=id)
837
+
838
+ if type == 'UNILOAD':
839
+ n_req = len(D)-1
840
+ D_orig = D
841
+ P_orig = P
842
+ for k in range(n_req):
843
+ D = D_orig[0+k:2+k]
844
+ P = P_orig[0+k:2+k]
845
+ elms_indx = []
846
+ for i in range(2):
847
+ for q in range(len(cum_LEN)):
848
+ if D[i] < cum_LEN[q] :
849
+ # print(f'LOADING ELEMENT at {D[i]}m = {elem_IDS[q-1]}')
850
+ elms_indx.append(q-1)
851
+ # rel_loc = (D[i] - cum_LEN[q-1]) / elem_LEN[q-1]
852
+ break
853
+ if len(elms_indx)==1: elms_indx.append(len(cum_LEN)-2)
854
+ if elms_indx != []:
855
+ for i in range(elms_indx[0],elms_indx[1]+1):
856
+ rel1 = float((max(D[0],cum_LEN[i]) - cum_LEN[i]) / elem_LEN[i])
857
+ rel2 = float((min(D[1],cum_LEN[i+1]) - cum_LEN[i]) / elem_LEN[i])
858
+
859
+ p1 = float(P[0]+(max(D[0],cum_LEN[i])-D[0])*(P[1]-P[0])/(D[1]-D[0]))
860
+ p2 = float(P[0]+(min(D[1],cum_LEN[i+1])-D[0])*(P[1]-P[0])/(D[1]-D[0]))
861
+ if rel2-rel1 == 0: continue
862
+
863
+
864
+ # print(f"Loading ELEM -> {elem_IDS[i]} , D1 = {rel1} , P1 = {p1} | D2 = {rel2} , P2 = {p2}")
865
+ # Load.Beam(elem_IDS[i],load_case,load_group,D=[rel1,rel2],P=[p1,p2],typ=typ,direction=direction)
866
+ Load.Beam(element=elem_IDS[i],load_case=load_case,load_group=load_group,D=[rel1,rel2],P=[p1,p2],direction=direction,
867
+ typ = "UNILOAD", use_ecc = use_ecc, use_proj = use_proj,
868
+ eccn_dir = eccn_dir, eccn_type = eccn_type, ieccn = ieccn, jeccn = jeccn, adnl_h = adnl_h, adnl_h_i = adnl_h_i, adnl_h_j = adnl_h_j,id = id)
869
+
870
+
871
+
872
+ class Pressure:
873
+ """ Assign Pressure load to plates faces.
874
+
875
+ """
876
+ data = []
877
+ def __init__(self, element:list, load_case:str, load_group:str = "", D:_presDir='LZ', P:list=0, VectorDir:list = [1,0,0],bProjection:bool = False,id:int = None):
878
+
879
+
880
+ chk = 0
881
+ for i in Load_Case.cases:
882
+ if load_case in i.NAME: chk = 1
883
+ if chk == 0: Load_Case("D", load_case)
884
+ if load_group != "":
885
+ chk = 0
886
+ a = [v['NAME'] for v in Group.Load.json()["Assign"].values()]
887
+ if load_group in a: chk = 1
888
+ if chk == 0: Group.Load(load_group)
889
+
890
+
891
+ self.ELEM = element
892
+ self.LCN = load_case
893
+ self.LDGR = load_group
894
+ self.DIR = D
895
+ self.VECTOR = VectorDir
896
+ self.PRES = P
897
+
898
+ if D in ['GX','GY','GZ']: self.bPROJ = bProjection
899
+ else: self.bPROJ = False
900
+
901
+ if id is None:
902
+ self.ID = len(Load.Pressure.data) + 1
903
+ else:
904
+ self.ID = id
905
+
906
+ _ADD_PressureLoad(self)
907
+
908
+
909
+ @classmethod
910
+ def json(cls):
911
+ json = {"Assign": {}}
912
+ for i in cls.data:
913
+ if i.ELEM not in list(json["Assign"].keys()):
914
+ json["Assign"][i.ELEM] = {"ITEMS": []}
915
+
916
+ js = {
917
+ "ID": i.ID,
918
+ "LCNAME": i.LCN,
919
+ "GROUP_NAME": i.LDGR,
920
+ "CMD": "PRES",
921
+ "ELEM_TYPE": "PLATE",
922
+ "FACE_EDGE_TYPE": "FACE",
923
+ "DIRECTION": i.DIR,
924
+ "VECTORS" : i.VECTOR,
925
+ "FORCES": i.PRES
926
+ }
927
+ if isinstance(i.PRES,float): newP = [i.PRES,0,0,0,0]
928
+ elif isinstance(i.PRES,list):
929
+ trimP = i.PRES[:4]
930
+ newP = [0] + trimP
931
+ js["FORCES"] = newP
932
+ if i.bPROJ:
933
+ js["OPT_PROJECTION"] = True
934
+
935
+ json["Assign"][i.ELEM]["ITEMS"].append(js)
936
+
937
+ return json
938
+
939
+ @classmethod
940
+ def create(cls):
941
+ MidasAPI("PUT", "/db/PRES",cls.json())
942
+
943
+ @classmethod
944
+ def get(cls):
945
+ return MidasAPI("GET", "/db/PRES")
946
+
947
+ @classmethod
948
+ def delete(cls):
949
+ cls.clear()
950
+ return MidasAPI("DELETE", "/db/PRES")
951
+
952
+ @classmethod
953
+ def clear(cls):
954
+ cls.data=[]
955
+
956
+ # @classmethod
957
+ # def sync(cls):
958
+ # cls.data = []
959
+ # a = cls.get()
960
+ # if a != {'message': ''}:
961
+ # for i in a['PRES'].keys():
962
+ # for j in range(len(a['CNLD'][i]['ITEMS'])):
963
+ # Load.Nodal(int(i),a['CNLD'][i]['ITEMS'][j]['LCNAME'], a['CNLD'][i]['ITEMS'][j]['GROUP_NAME'],
964
+ # a['CNLD'][i]['ITEMS'][j]['FX'], a['CNLD'][i]['ITEMS'][j]['FY'], a['CNLD'][i]['ITEMS'][j]['FZ'],
965
+ # a['CNLD'][i]['ITEMS'][j]['MX'], a['CNLD'][i]['ITEMS'][j]['MY'], a['CNLD'][i]['ITEMS'][j]['MZ'],
966
+ # a['CNLD'][i]['ITEMS'][j]['ID'])
967
+