midas-civil 0.1.3__py3-none-any.whl → 0.1.5__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/_node.py CHANGED
@@ -48,9 +48,9 @@ class Node:
48
48
  node_count = max(Node.ids)+1
49
49
 
50
50
 
51
- self.X = x
52
- self.Y = y
53
- self.Z = z
51
+ self.X = round(x,6)
52
+ self.Y = round(y,6)
53
+ self.Z = round(z,6)
54
54
 
55
55
  if id == 0 : self.ID = node_count
56
56
  if id != 0 : self.ID = id
@@ -94,7 +94,9 @@ class Node:
94
94
  Node.ids.append(self.ID)
95
95
  Node.Grid[cell_loc].append(self)
96
96
 
97
- _add_node_2_stGroup(self.ID,group)
97
+ if group !="":
98
+ _add_node_2_stGroup(self.ID,group)
99
+
98
100
 
99
101
 
100
102
 
@@ -115,13 +117,14 @@ class Node:
115
117
 
116
118
  @staticmethod
117
119
  def sync():
118
- Node.nodes = []
120
+ Node.nodes=[]
119
121
  Node.ids=[]
122
+ Node.Grid={}
120
123
  a = Node.get()
121
124
  if a != {'message': ''}:
122
125
  if list(a['NODE'].keys()) != []:
123
126
  for j in a['NODE'].keys():
124
- Node(round(a['NODE'][j]['X'],6), round(a['NODE'][j]['Y'],6), round(a['NODE'][j]['Z'],6),int(j),0)
127
+ Node(round(a['NODE'][j]['X'],6), round(a['NODE'][j]['Y'],6), round(a['NODE'][j]['Z'],6), id=int(j), group='', merge=0)
125
128
 
126
129
  @staticmethod
127
130
  def delete2(nodes_list):
@@ -142,6 +145,7 @@ class Node:
142
145
  MidasAPI("DELETE",f"/db/NODE/")
143
146
  Node.nodes=[]
144
147
  Node.ids=[]
148
+ Node.Grid={}
145
149
 
146
150
  @staticmethod
147
151
  def delete():
midas_civil/_result.py CHANGED
@@ -142,13 +142,13 @@ class LoadCombination:
142
142
 
143
143
  @classmethod
144
144
  def delete(cls, classification = "All", ids = []):
145
- json = LoadCombination.call_json(classification)
145
+ json = LoadCombination.json(classification)
146
146
  a = ""
147
147
  for i in range(len(ids)):
148
148
  a += str(ids[i]) + ","
149
149
  a = "/" + a[:-1]
150
150
  if json == {}:
151
- print("No load combinations are defined to delete. Def")
151
+ print("No load combinations are defined to delete.")
152
152
  for i in list(json.keys()):
153
153
  MidasAPI("DELETE",LoadCombination.com_map.get(i) + a)
154
154
  #---------------------------------------------------------------------------------------------------------------
@@ -2,6 +2,7 @@ import polars as pl
2
2
  import json
3
3
  import xlsxwriter
4
4
  from ._mapi import *
5
+ from ._model import *
5
6
  # js_file = open('JSON_Excel Parsing\\test.json','r')
6
7
 
7
8
  # print(js_file)
@@ -125,7 +126,7 @@ class Result :
125
126
 
126
127
  # ---------- User defined TABLE (Dynamic Report Table) ------------------------------
127
128
  @staticmethod
128
- def UserDefinedTable(tableName:str, summary=0):
129
+ def UserDefinedTable(tableName:str, summary=0, force_unit='KN',len_unit='M'):
129
130
  js_dat = {
130
131
  "Argument": {
131
132
  "TABLE_NAME": tableName,
@@ -135,11 +136,11 @@ class Result :
135
136
  }
136
137
  }
137
138
  }
138
-
139
+ Model.units(force=force_unit,length=len_unit)
139
140
  ss_json = MidasAPI("POST","/post/TABLE",js_dat)
140
141
  return _JSToDF_UserDefined(tableName,ss_json,summary)
141
142
 
142
- # ---------- Result TABLE ------------------------------
143
+ # ---------- LIST ALL USER DEFINED TABLE ------------------------------
143
144
  @staticmethod
144
145
  def UserDefinedTables_print():
145
146
  ''' Print all the User defined table names '''
@@ -158,18 +159,17 @@ class Result :
158
159
 
159
160
  # ---------- Result TABLE ------------------------------
160
161
  @staticmethod
161
- def ResultTable(tabletype:str,elements:list=[],loadcase:list=[],cs_stage=[],force_unit='kN',len_unit='m'):
162
+ def ResultTable(tabletype:str,keys=[],loadcase:list=[],cs_stage=[],force_unit='KN',len_unit='M'):
162
163
  '''
163
164
  TableType : REACTIONG | REACTIONL | DISPLACEMENTG | DISPLACEMENTL | TRUSSFORCE | TRUSSSTRESS
165
+ Keys : List{int} -> Element/ Node IDs | str -> Structure Group Name
166
+ Loadcase : Loadcase name followed by type. eg. DeadLoad(ST)
164
167
  '''
168
+
165
169
  js_dat = {
166
170
  "Argument": {
167
171
  "TABLE_NAME": "SS_Table",
168
172
  "TABLE_TYPE": tabletype,
169
- "UNIT": {
170
- "FORCE": force_unit,
171
- "DIST": len_unit
172
- },
173
173
  "STYLES": {
174
174
  "FORMAT": "Fixed",
175
175
  "PLACE": 12
@@ -185,9 +185,17 @@ class Result :
185
185
  js_dat["Argument"]['STAGE_STEP'] = cs_stage
186
186
 
187
187
 
188
- if elements!=[]: js_dat["Argument"]['NODE_ELEMS'] = {"KEYS": elements}
188
+ if isinstance(keys,list):
189
+ if keys!=[]:
190
+ js_dat["Argument"]['NODE_ELEMS'] = {"KEYS": keys}
191
+ elif isinstance(keys,str):
192
+ js_dat["Argument"]['NODE_ELEMS'] = {"STRUCTURE_GROUP_NAME": keys}
193
+
194
+
189
195
  if loadcase!=[]: js_dat["Argument"]['LOAD_CASE_NAMES'] = loadcase
190
196
 
197
+
198
+ Model.units(force=force_unit,length=len_unit)
191
199
  ss_json = MidasAPI("POST","/post/table",js_dat)
192
200
  return _JSToDF_ResTable(ss_json)
193
201
 
@@ -0,0 +1,160 @@
1
+ from ._mapi import *
2
+
3
+ class Settlement:
4
+
5
+ @classmethod
6
+ def create(cls):
7
+ """Creates Settlement Load in MIDAS Civil NX"""
8
+ if cls.Group.data != []: cls.Group.create()
9
+ if cls.Case.data != []: cls.Case.create()
10
+
11
+ @classmethod
12
+ def delete(cls):
13
+ """Deletes Settlement load from MIDAS Civil NX and Python"""
14
+ cls.Group.delete()
15
+ cls.Case.delete()
16
+
17
+ @classmethod
18
+ def sync(cls):
19
+ """Sync Settlement load from MIDAS Civil NX to Python"""
20
+ cls.Group.sync()
21
+ cls.Case.sync()
22
+
23
+ class Group:
24
+ """
25
+ Parameters:
26
+ name: Settlement group name (string)
27
+ displacement: Settlement displacement value (number)
28
+ node_list: List of node IDs to include in the group (array of integers)
29
+ id: Group ID (optional, auto-generated if not provided)
30
+
31
+ Examples:
32
+ ```python
33
+ Settlement.Group("SG1", 0.025, [100, 101])
34
+ Settlement.Group("SG2", 0.015, [102, 103])
35
+ ```
36
+ """
37
+ data = []
38
+
39
+ def __init__(self, name, displacement, node_list, id=""):
40
+ self.NAME = name
41
+ self.SETTLE = displacement
42
+ self.ITEMS = node_list
43
+ if id == "": id = len(Settlement.Group.data) + 1
44
+ self.ID = id
45
+
46
+ Settlement.Group.data.append(self)
47
+
48
+ @classmethod
49
+ def json(cls):
50
+ json = {"Assign": {}}
51
+ for i in cls.data:
52
+ json["Assign"][str(i.ID)] = {
53
+ "NAME": i.NAME,
54
+ "SETTLE": i.SETTLE,
55
+ "ITEMS": i.ITEMS
56
+ }
57
+ return json
58
+
59
+ @staticmethod
60
+ def create():
61
+ MidasAPI("PUT", "/db/smpt", Settlement.Group.json())
62
+
63
+ @staticmethod
64
+ def get():
65
+ return MidasAPI("GET", "/db/smpt")
66
+
67
+ @classmethod
68
+ def delete(cls):
69
+ cls.data = []
70
+ return MidasAPI("DELETE", "/db/smpt")
71
+
72
+ @classmethod
73
+ def sync(cls):
74
+ cls.data = []
75
+ a = cls.get()
76
+ if a != {'message': ''}:
77
+ for i in a['SMPT'].keys():
78
+ Settlement.Group(
79
+ a['SMPT'][i]['NAME'],
80
+ a['SMPT'][i]['SETTLE'],
81
+ a['SMPT'][i]['ITEMS'],
82
+ int(i)
83
+ )
84
+
85
+
86
+ class Case:
87
+ """
88
+
89
+ Parameters:
90
+ name: Settlement load case name (string)
91
+ settlement_groups: List of settlement group names to include (array of strings, default [])
92
+ factor: Settlement scale factor (number, default 1.0)
93
+ min_groups: Minimum number of settlement groups (integer, default 1)
94
+ max_groups: Maximum number of settlement groups (integer, default 1)
95
+ desc: Description of the settlement case (string, default "")
96
+ id: Case ID (optional, auto-generated if not provided)
97
+
98
+ Examples:
99
+ ```python
100
+ Settlement.Case("SMLC1", ["SG1"], 1.2, 1, 1, "Foundation Settlement Case")
101
+ Settlement.Case("SMLC2", ["SG1", "SG2"], 1.0, 1, 2, "Combined Settlement")
102
+ ```
103
+ """
104
+ data = []
105
+
106
+ def __init__(self, name, settlement_groups=[],factor=1.0, min_groups=1, max_groups=1, desc="", id=""):
107
+ self.NAME = name
108
+ self.DESC = desc
109
+ self.FACTOR = factor
110
+ self.MIN = min_groups
111
+ self.MAX = max_groups
112
+ self.ST_GROUPS = settlement_groups
113
+ if id == "": id = len(Settlement.Case.data) + 1
114
+ self.ID = id
115
+
116
+ Settlement.Case.data.append(self)
117
+
118
+ @classmethod
119
+ def json(cls):
120
+ json = {"Assign": {}}
121
+ for i in cls.data:
122
+ json["Assign"][str(i.ID)] = {
123
+ "NAME": i.NAME,
124
+ "DESC": i.DESC,
125
+ "FACTOR": i.FACTOR,
126
+ "MIN": i.MIN,
127
+ "MAX": i.MAX,
128
+ "ST_GROUPS": i.ST_GROUPS
129
+ }
130
+ return json
131
+
132
+ @staticmethod
133
+ def create():
134
+ MidasAPI("PUT", "/db/smlc", Settlement.Case.json())
135
+
136
+ @staticmethod
137
+ def get():
138
+ return MidasAPI("GET", "/db/smlc")
139
+
140
+ @classmethod
141
+ def delete(cls):
142
+ cls.data = []
143
+ return MidasAPI("DELETE", "/db/smlc")
144
+
145
+ @classmethod
146
+ def sync(cls):
147
+ cls.data = []
148
+ a = cls.get()
149
+ if a != {'message': ''}:
150
+ for i in a['SMLC'].keys():
151
+ Settlement.Case(
152
+ a['SMLC'][i]['NAME'],
153
+ a['SMLC'][i]['DESC'],
154
+ a['SMLC'][i]['FACTOR'],
155
+ a['SMLC'][i]['MIN'],
156
+ a['SMLC'][i]['MAX'],
157
+ a['SMLC'][i]['ST_GROUPS'],
158
+ int(i)
159
+ )
160
+
@@ -16,6 +16,8 @@ class Temperature:
16
16
  if cls.System.temps: cls.System.create()
17
17
  if cls.Element.temps: cls.Element.create()
18
18
  if cls.Gradient.temps: cls.Gradient.create()
19
+ if cls.Nodal.temps: cls.Nodal.create()
20
+ if cls.BeamSection.temps: cls.BeamSection.create()
19
21
 
20
22
  @classmethod
21
23
  def delete(cls):
@@ -23,6 +25,8 @@ class Temperature:
23
25
  cls.System.delete()
24
26
  cls.Element.delete()
25
27
  cls.Gradient.delete()
28
+ cls.Nodal.delete()
29
+ cls.BeamSection.delete()
26
30
 
27
31
 
28
32
  @classmethod
@@ -31,7 +35,9 @@ class Temperature:
31
35
  cls.System.sync()
32
36
  cls.Element.sync()
33
37
  cls.Gradient.sync()
34
-
38
+ cls.Nodal.sync()
39
+ cls.BeamSection.sync()
40
+
35
41
  # --------------------------------------------------------------------------------------------------
36
42
  # System Temperature
37
43
  # --------------------------------------------------------------------------------------------------
@@ -351,3 +357,258 @@ class Temperature:
351
357
  return MidasAPI("DELETE", "/db/gtmp")
352
358
 
353
359
  # --------------------------------------------------------------------------------------------------
360
+ # Nodal Temperature
361
+ # --------------------------------------------------------------------------------------------------
362
+ class Nodal:
363
+ """
364
+ Create Nodal Temperature
365
+
366
+ Parameters:
367
+ node (int): Node ID
368
+ temperature (float): Temperature value
369
+ lcname (str): Load case name **(Must exist in the model)**
370
+ group (str): Load group name (default "")
371
+ id (int): Temperature ID (optional)
372
+
373
+ Example:
374
+ Temperature.Nodal(6, 10, "Test")
375
+ """
376
+ temps = []
377
+
378
+ def __init__(self, node, temperature, lcname, group="", id=None):
379
+ if group:
380
+ chk = 0
381
+ try:
382
+ a = [v['NAME'] for v in Group.Load.json()["Assign"].values()]
383
+ if group in a:
384
+ chk = 1
385
+ except:
386
+ pass
387
+ if chk == 0:
388
+ Group.Load(group)
389
+
390
+ self.NODE = node
391
+ self.TEMPER = temperature
392
+ self.LCNAME = lcname
393
+ self.GROUP_NAME = group
394
+
395
+ if id is None:
396
+ existing_ids = []
397
+ for temp in Temperature.Nodal.temps:
398
+ if temp.NODE == node:
399
+ existing_ids.extend([item.get('ID', 0) for item in temp.ITEMS if hasattr(temp, 'ITEMS')])
400
+ self.ID = max(existing_ids, default=0) + 1
401
+ else:
402
+ self.ID = id
403
+
404
+ existing_temp = None
405
+ for temp in Temperature.Nodal.temps:
406
+ if temp.NODE == node:
407
+ existing_temp = temp
408
+ break
409
+
410
+ item_data = {
411
+ "ID": self.ID, "LCNAME": self.LCNAME,
412
+ "GROUP_NAME": self.GROUP_NAME, "TEMPER": self.TEMPER
413
+ }
414
+
415
+ if existing_temp:
416
+ if not hasattr(existing_temp, 'ITEMS'):
417
+ existing_temp.ITEMS = []
418
+ existing_temp.ITEMS.append(item_data)
419
+ else:
420
+ self.ITEMS = [item_data]
421
+ Temperature.Nodal.temps.append(self)
422
+
423
+ @classmethod
424
+ def json(cls):
425
+ """Creates JSON with 'Assign' key from Nodal Temperature objects defined in Python"""
426
+ json_data = {"Assign": {}}
427
+ for temp in cls.temps:
428
+ json_data["Assign"][str(temp.NODE)] = {"ITEMS": temp.ITEMS}
429
+ return json_data
430
+
431
+ @staticmethod
432
+ def create():
433
+ """Creates Nodal Temperatures in MIDAS Civil NX"""
434
+ MidasAPI("PUT", "/db/ntmp", Temperature.Nodal.json())
435
+
436
+ @staticmethod
437
+ def get():
438
+ """Get the JSON of Nodal Temperatures from MIDAS Civil NX"""
439
+ return MidasAPI("GET", "/db/ntmp")
440
+
441
+ @staticmethod
442
+ def sync():
443
+ """Sync Nodal Temperatures from MIDAS Civil NX to Python"""
444
+ Temperature.Nodal.temps = []
445
+ a = Temperature.Nodal.get()
446
+
447
+ if a and 'NTMP' in a:
448
+ temp_data = a.get('NTMP', {})
449
+ for node_id, node_data in temp_data.items():
450
+ node_obj = type('obj', (object,), {
451
+ 'NODE': int(node_id),
452
+ 'ITEMS': node_data.get('ITEMS', [])
453
+ })()
454
+ Temperature.Nodal.temps.append(node_obj)
455
+
456
+ @staticmethod
457
+ def delete():
458
+ """Delete Nodal Temperatures from MIDAS Civil NX and Python"""
459
+ Temperature.Nodal.temps = []
460
+ return MidasAPI("DELETE", "/db/ntmp")
461
+
462
+
463
+ # --------------------------------------------------------------------------------------------------
464
+ # Beam Section Temperature
465
+ # --------------------------------------------------------------------------------------------------
466
+ class BeamSection:
467
+ """
468
+ Create Beam Section Temperature Object in Python.
469
+
470
+ Parameters:
471
+ element (int): Element ID to apply the load.
472
+ lcname (str): Load Case Name (must exist in the model).
473
+ section_type (str, optional): 'General' or 'PSC'. Defaults to 'General'.
474
+ type (str, optional): 'Element' or 'Input'. Defaults to 'Element'.
475
+ group (str, optional): Load Group Name.
476
+ id (int, optional): Load ID.
477
+ dir (str, optional): Direction, 'LY' or 'LZ'. Defaults to 'LZ'.
478
+ ref_pos (str, optional): Reference Position, 'Centroid', 'Top', or 'Bot'. Defaults to 'Centroid'.
479
+ val_b (float, optional): B Value.
480
+ val_h1 (float, optional): H1 Value.
481
+ val_h2 (float, optional): H2 Value.
482
+ val_t1 (float, optional): T1 Value.
483
+ val_t2 (float, optional): T2 Value.
484
+ elast (float, optional): Modulus of Elasticity (required for 'Input' type).
485
+ thermal (float, optional): Thermal Coefficient (required for 'Input' type).
486
+ psc_ref (int, optional): Reference for PSC, 0 for Top, 1 for Bottom.
487
+ psc_opt_b (int, optional): B-Type option for PSC. (0 for Section type)
488
+ psc_opt_h1 (int, optional): H1-Type option for PSC. (0 - Z1 , 1- Z2 ,2 - Z2)
489
+ psc_opt_h2 (int, optional): H2-Type option for PSC. (0 - Z1 , 1- Z2 ,2 - Z2)
490
+ """
491
+ temps = []
492
+
493
+ def __init__(self, element, lcname, section_type='General', type='Element', group="", id=None,
494
+ dir='LZ', ref_pos='Centroid', val_b=0, val_h1=0, val_h2=0, val_t1=0, val_t2=0,
495
+ elast=None, thermal=None, psc_ref=0, psc_opt_b=1, psc_opt_h1=3, psc_opt_h2=3):
496
+
497
+ # Validate required parameters for Input type
498
+ if type.upper() == 'INPUT':
499
+ if elast is None or thermal is None:
500
+ raise ValueError("For 'Input' type, both 'elast' and 'thermal' parameters are required.")
501
+
502
+ # Handle load group creation
503
+ if group:
504
+ chk = 0
505
+ try:
506
+ a = [v['NAME'] for v in Group.Load.json()["Assign"].values()]
507
+ if group in a:
508
+ chk = 1
509
+ except:
510
+ pass
511
+ if chk == 0:
512
+ Group.Load(group)
513
+
514
+ self.ELEMENT = element
515
+
516
+ # Auto-assign ID if not provided
517
+ if id is None:
518
+ existing_ids = []
519
+ for temp in Temperature.BeamSection.temps:
520
+ if temp.ELEMENT == element:
521
+ existing_ids.extend([item.get('ID', 0) for item in temp.ITEMS if hasattr(temp, 'ITEMS')])
522
+ self.ID = max(existing_ids, default=0) + 1
523
+ else:
524
+ self.ID = id
525
+
526
+ # Construct the nested dictionary for vSECTTMP
527
+ vsec_item = {
528
+ "TYPE": type.upper(),
529
+ "VAL_B": val_b,
530
+ "VAL_H1": val_h1,
531
+ "VAL_H2": val_h2,
532
+ "VAL_T1": val_t1,
533
+ "VAL_T2": val_t2,
534
+ }
535
+
536
+ is_psc = section_type.lower() == 'psc'
537
+
538
+ # Add material properties for Input type
539
+ if type.upper() == 'INPUT':
540
+ vsec_item["ELAST"] = elast
541
+ vsec_item["THERMAL"] = thermal
542
+
543
+ # Add PSC-specific parameters
544
+ if is_psc:
545
+ vsec_item["REF"] = psc_ref
546
+ vsec_item["OPT_B"] = psc_opt_b
547
+ vsec_item["OPT_H1"] = psc_opt_h1
548
+ vsec_item["OPT_H2"] = psc_opt_h2
549
+
550
+ # Construct the main item dictionary
551
+ item_data = {
552
+ "ID": self.ID,
553
+ "LCNAME": lcname,
554
+ "GROUP_NAME": group,
555
+ "DIR": dir,
556
+ "REF": ref_pos,
557
+ "NUM": 1,
558
+ "bPSC": is_psc,
559
+ "vSECTTMP": [vsec_item]
560
+ }
561
+
562
+ # Check if an object for this element already exists
563
+ existing_temp = None
564
+ for temp in Temperature.BeamSection.temps:
565
+ if temp.ELEMENT == element:
566
+ existing_temp = temp
567
+ break
568
+
569
+ if existing_temp:
570
+ if not hasattr(existing_temp, 'ITEMS'):
571
+ existing_temp.ITEMS = []
572
+ existing_temp.ITEMS.append(item_data)
573
+ else:
574
+ self.ITEMS = [item_data]
575
+ Temperature.BeamSection.temps.append(self)
576
+
577
+ @classmethod
578
+ def json(cls):
579
+ """Creates JSON from Beam Section Temperature objects defined in Python"""
580
+ json_data = {"Assign": {}}
581
+ for temp in cls.temps:
582
+ json_data["Assign"][str(temp.ELEMENT)] = {"ITEMS": temp.ITEMS}
583
+ return json_data
584
+
585
+ @staticmethod
586
+ def create():
587
+ """Creates Beam Section Temperatures in MIDAS Civil NX"""
588
+ MidasAPI("PUT", "/db/btmp", Temperature.BeamSection.json())
589
+
590
+ @staticmethod
591
+ def get():
592
+ """Get the JSON of Beam Section Temperatures from MIDAS Civil NX"""
593
+ return MidasAPI("GET", "/db/btmp")
594
+
595
+ @staticmethod
596
+ def sync():
597
+ """Sync Beam Section Temperatures from MIDAS Civil NX to Python"""
598
+ Temperature.BeamSection.temps = []
599
+ a = Temperature.BeamSection.get()
600
+
601
+ if a and 'BTMP' in a:
602
+ temp_data = a.get('BTMP', {})
603
+ for element_id, element_data in temp_data.items():
604
+ element_obj = type('obj', (object,), {
605
+ 'ELEMENT': int(element_id),
606
+ 'ITEMS': element_data.get('ITEMS', [])
607
+ })()
608
+ Temperature.BeamSection.temps.append(element_obj)
609
+
610
+ @staticmethod
611
+ def delete():
612
+ """Delete Beam Section Temperatures from MIDAS Civil NX and Python"""
613
+ Temperature.BeamSection.temps = []
614
+ return MidasAPI("DELETE", "/db/btmp")