midas-civil 0.0.6__py3-none-any.whl → 0.0.8__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 CHANGED
@@ -10,6 +10,7 @@ def convList(item):
10
10
  return item
11
11
 
12
12
 
13
+
13
14
  class Boundary:
14
15
 
15
16
  @classmethod
@@ -18,6 +19,8 @@ class Boundary:
18
19
  if cls.Support.sups!=[]: cls.Support.create()
19
20
  if cls.ElasticLink.links!=[]: cls.ElasticLink.create()
20
21
  if cls.RigidLink.links!=[]: cls.RigidLink.create()
22
+ if cls.MLFC.func!=[]: cls.RigidLink.create()
23
+ if cls.PointSpring.springs!=[]: cls.PointSpring.create()
21
24
 
22
25
 
23
26
  @classmethod
@@ -26,6 +29,8 @@ class Boundary:
26
29
  cls.Support.delete()
27
30
  cls.ElasticLink.delete()
28
31
  cls.RigidLink.delete()
32
+ cls.MLFC.delete()
33
+ cls.PointSpring.delete()
29
34
 
30
35
  @classmethod
31
36
  def sync(cls):
@@ -33,8 +38,8 @@ class Boundary:
33
38
  cls.Support.sync()
34
39
  cls.ElasticLink.sync()
35
40
  cls.RigidLink.sync()
36
-
37
-
41
+ cls.MLFC.sync()
42
+ cls.PointSpring.delete()
38
43
 
39
44
 
40
45
  class Support:
@@ -502,3 +507,319 @@ class Boundary:
502
507
  cls.links = []
503
508
  return MidasAPI("DELETE", "/db/RIGD")
504
509
  #---------------------------------------------------------------------------------------------------------------
510
+
511
+ class MLFC:
512
+
513
+ func = []
514
+ _id = []
515
+
516
+ def __init__(self, name:str, type:str='FORCE', symm:bool=True, data:list=[[0,0],[1,1]], id:int=None):
517
+ """
518
+ Force-Deformation Function constructor.
519
+
520
+ Parameters:
521
+ name (str): The name for the Force-Deformation Function.
522
+ type (str, optional): Type of function, either "FORCE" or "MOMENT". Defaults to "FORCE".
523
+ symm (bool, optional): Defines if the function is symmetric (True) or unsymmetric (False). Defaults to True.
524
+ data (list[list[float]], optional): A list of [X, Y] coordinate pairs defining the function curve. Required.
525
+ - For "FORCE" type: X is displacement, Y is force.
526
+ - For "MOMENT" type: X is rotation in radians, Y is moment.
527
+ Defaults to [[0,0],[1,1]].
528
+ id (int, optional): The function ID. If not provided, it will be auto-assigned.
529
+
530
+ Examples:
531
+ ```python
532
+ # Create a symmetric force vs. displacement function
533
+ Boundary.MLFC(name="MyForceFunction", type="FORCE", symm=True, data=[[0,0], [0.1, 100], [0.2, 150]])
534
+
535
+ # Create an unsymmetric moment vs. rotation function with a specific ID
536
+ Boundary.MLFC(name="MyMomentFunction", type="MOMENT", symm=False, data=[[0,0], [0.01, 500], [0.02, 750]], id=5)
537
+ ```
538
+ """
539
+ self.NAME = name
540
+ self.TYPE = type
541
+ self.SYMM = symm
542
+ self.DATA = data
543
+
544
+ self.X = [dat[0] for dat in self.DATA]
545
+ self.Y = [dat[1] for dat in self.DATA]
546
+
547
+ # Auto-assign ID if not provided
548
+ if id is None:
549
+ if __class__._id == []:
550
+ self.ID = 1
551
+ else:
552
+ self.ID = max(__class__._id) + 1
553
+ else:
554
+ self.ID = id
555
+
556
+ __class__._id.append(self.ID)
557
+ __class__.func.append(self)
558
+
559
+
560
+
561
+ @classmethod
562
+ def json(cls):
563
+
564
+ json = {"Assign": {}}
565
+ for fn in cls.func:
566
+ json["Assign"][fn.ID]={
567
+ "NAME": fn.NAME,
568
+ "TYPE": fn.TYPE,
569
+ "SYMM": fn.SYMM,
570
+ "FUNC_ID": 0,
571
+ "ITEMS": []
572
+ }
573
+ for i in range(len(fn.X)):
574
+ json["Assign"][fn.ID]["ITEMS"].append({"X":fn.X[i],"Y":fn.Y[i]})
575
+ return json
576
+
577
+ @classmethod
578
+ def create(cls):
579
+ """
580
+ Sends all FUNC data.
581
+ """
582
+ MidasAPI("PUT", "/db/MLFC", cls.json())
583
+
584
+ @classmethod
585
+ def get(cls):
586
+ """
587
+ Retrieves data.
588
+ """
589
+ return MidasAPI("GET", "/db/MLFC")
590
+
591
+ @classmethod
592
+ def sync(cls):
593
+
594
+ cls.links = []
595
+ a = cls.get()
596
+ if a != {'message': ''}:
597
+ for i in a['MLFC'].keys():
598
+ name = a['MLFC'][i]["NAME"]
599
+ type = a['MLFC'][i]["TYPE"]
600
+ symm = a['MLFC'][i]["SYMM"]
601
+ data = []
602
+ for j in (a['MLFC'][i]['ITEMS']):
603
+ data.append([j["X"],j["Y"]])
604
+ Boundary.MLFC(name,type,symm,data,int(i))
605
+
606
+ @classmethod
607
+ def delete(cls):
608
+ """
609
+ Deletes all func from the database and resets the class.
610
+ """
611
+ cls.links = []
612
+ return MidasAPI("DELETE", "/db/MLFC")
613
+
614
+ #--------------------------------------------------------------------------------------------------------------------------------------
615
+
616
+
617
+ class PointSpring:
618
+ """Create Point Spring Object in Python"""
619
+ springs = []
620
+
621
+ def __init__(self,
622
+ node: int,
623
+ spring_type: str = "LINEAR",
624
+ group: str = "",
625
+ id: int = None,
626
+ stiffness: list = None,
627
+ fixed_option: list = None,
628
+ damping: list = None,
629
+ direction: str = "Dx+",
630
+ normal_vector: list = None,
631
+ function_id: int = 1):
632
+ """
633
+ Point Spring constructor.
634
+
635
+ Parameters:
636
+ node: Node ID where spring is applied
637
+ spring_type: Type of spring ("LINEAR", "COMP", "TENS", "MULTI")
638
+ group: Group name (default "")
639
+ id: Spring ID (optional, auto-assigned if None)
640
+ stiffness: Spring stiffness values [SDx, SDy, SDz, SRx, SRy, SRz] or single value for COMP/TENS
641
+ fixed_option: Fixed option array [Boolean, 6] for LINEAR type
642
+ damping: Damping values [Cx, Cy, Cz, CRx, CRy, CRz] (if provided, damping is enabled)
643
+ direction: Direction string ("Dx+", "Dx-", "Dy+", "Dy-", "Dz+", "Dz-", "Vector")
644
+ normal_vector: Normal vector [x, y, z] when direction is "Vector"
645
+ function_id: Function ID for MULTI type
646
+
647
+ Examples:
648
+ # Linear spring
649
+ PointSpring(1, "LINEAR", "Group1", stiffness=[1000, 1000, 1000, 100, 100, 100])
650
+
651
+ # Compression only spring
652
+ PointSpring(2, "COMP", "Group2", stiffness=5000, direction="Dz+")
653
+
654
+ # Tension only spring with vector direction
655
+ PointSpring(3, "TENS", "Group3", stiffness=3000, direction="Vector", normal_vector=[0, -1, -1])
656
+
657
+ # Multi-linear spring
658
+ PointSpring(4, "MULTI", "Group4", direction="Dz+", function_id=1)
659
+ """
660
+
661
+ # Check if group exists, create if not
662
+ if group != "":
663
+ chk = 0
664
+ a = [v['NAME'] for v in Group.Boundary.json()["Assign"].values()]
665
+ if group in a:
666
+ chk = 1
667
+ if chk == 0:
668
+ Group.Boundary(group)
669
+
670
+ # Validate spring type
671
+ valid_types = ["LINEAR", "COMP", "TENS", "MULTI"]
672
+ if spring_type not in valid_types:
673
+ spring_type = "LINEAR"
674
+
675
+ self.NODE = node
676
+ self.TYPE = spring_type
677
+ self.GROUP_NAME = group
678
+
679
+ # Convert direction string to integer
680
+ direction_map = {
681
+ "Dx+": 0, "Dx-": 1, "Dy+": 2, "Dy-": 3,
682
+ "Dz+": 4, "Dz-": 5, "Vector": 6
683
+ }
684
+ self.DIR = direction_map.get(direction, 0)
685
+
686
+ # Auto-assign ID if not provided
687
+ if id is None:
688
+ self.ID = len(Boundary.PointSpring.springs) + 1
689
+ else:
690
+ self.ID = id
691
+
692
+ # Type-specific parameters
693
+ if spring_type == "LINEAR":
694
+ self.SDR = stiffness if stiffness else [0, 0, 0, 0, 0, 0]
695
+ self.F_S = fixed_option if fixed_option else [False] * 6
696
+ # Damping logic: if damping values provided, enable damping
697
+ self.DAMPING = damping is not None and any(d != 0 for d in damping) if damping else False
698
+ self.Cr = damping if damping else [0, 0, 0, 0, 0, 0]
699
+
700
+ elif spring_type in ["COMP", "TENS"]:
701
+ self.STIFF = stiffness if stiffness else 0
702
+ self.DV = normal_vector if normal_vector else [0, 0, 0]
703
+
704
+ elif spring_type == "MULTI":
705
+ self.DV = normal_vector if normal_vector else [0, 0, 0]
706
+ self.FUNCTION = function_id
707
+
708
+ # Add to static list
709
+ Boundary.PointSpring.springs.append(self)
710
+
711
+ @classmethod
712
+ def json(cls):
713
+ """
714
+ Converts PointSpring data to JSON format for API submission.
715
+ """
716
+ json_data = {"Assign": {}}
717
+
718
+ for spring in cls.springs:
719
+ spring_data = {
720
+ "ID": spring.ID,
721
+ "TYPE": spring.TYPE,
722
+ "GROUP_NAME": spring.GROUP_NAME
723
+ }
724
+
725
+ # Add type-specific parameters
726
+ if spring.TYPE == "LINEAR":
727
+ spring_data["SDR"] = spring.SDR
728
+ spring_data["F_S"] = spring.F_S
729
+ spring_data["DAMPING"] = spring.DAMPING
730
+ if spring.DAMPING:
731
+ spring_data["Cr"] = spring.Cr
732
+
733
+ elif spring.TYPE in ["COMP", "TENS"]:
734
+ spring_data["STIFF"] = spring.STIFF
735
+ spring_data["DIR"] = spring.DIR
736
+ spring_data["DV"] = spring.DV
737
+
738
+ elif spring.TYPE == "MULTI":
739
+ spring_data["DIR"] = spring.DIR
740
+ spring_data["DV"] = spring.DV
741
+ spring_data["FUNCTION"] = spring.FUNCTION
742
+
743
+ json_data["Assign"][spring.NODE] = {"ITEMS": [spring_data]}
744
+
745
+ return json_data
746
+
747
+ @classmethod
748
+ def create(cls):
749
+ """
750
+ Sends all PointSpring data
751
+ """
752
+ MidasAPI("PUT", "/db/nspr", cls.json())
753
+
754
+ @classmethod
755
+ def get(cls):
756
+ """
757
+ Retrieves PointSpring data
758
+ """
759
+ return MidasAPI("GET", "/db/nspr")
760
+
761
+ @classmethod
762
+ def sync(cls):
763
+ """
764
+ Updates the PointSpring class with data
765
+ """
766
+ cls.springs = []
767
+ a = cls.get()
768
+
769
+ if a != {'message': ''}:
770
+ for node_id, node_data in a.get("NSPR", {}).items():
771
+ for item in node_data.get("ITEMS"):
772
+ spring_type = item.get("TYPE")
773
+ group_name = item.get("GROUP_NAME")
774
+ spring_id = item.get("ID", 1)
775
+
776
+ # Extract type-specific parameters
777
+ if spring_type == "LINEAR":
778
+ stiffness = item.get("SDR")
779
+ fixed_option = item.get("F_S")
780
+ damping = item.get("Cr") if item.get("DAMPING", False) else None
781
+
782
+ # Convert direction back to string
783
+ dir_map = {0: "Dx+", 1: "Dx-", 2: "Dy+", 3: "Dy-", 4: "Dz+", 5: "Dz-", 6: "Vector"}
784
+ direction_str = dir_map.get(0, "Dx+") # Default for LINEAR
785
+
786
+ Boundary.PointSpring(
787
+ int(node_id), spring_type, group_name, spring_id,
788
+ stiffness, fixed_option, damping, direction_str
789
+ )
790
+
791
+ elif spring_type in ["COMP", "TENS"]:
792
+ stiffness = item.get("STIFF")
793
+ direction_int = item.get("DIR")
794
+ normal_vector = item.get("DV")
795
+
796
+ # Convert direction back to string
797
+ dir_map = {0: "Dx+", 1: "Dx-", 2: "Dy+", 3: "Dy-", 4: "Dz+", 5: "Dz-", 6: "Vector"}
798
+ direction_str = dir_map.get(direction_int, "Dx+")
799
+
800
+ Boundary.PointSpring(
801
+ int(node_id), spring_type, group_name, spring_id,
802
+ stiffness, None, None, direction_str, normal_vector
803
+ )
804
+
805
+ elif spring_type == "MULTI":
806
+ direction_int = item.get("DIR")
807
+ normal_vector = item.get("DV")
808
+ function_id = item.get("FUNCTION")
809
+
810
+ # Convert direction back to string
811
+ dir_map = {0: "Dx+", 1: "Dx-", 2: "Dy+", 3: "Dy-", 4: "Dz+", 5: "Dz-", 6: "Vector"}
812
+ direction_str = dir_map.get(direction_int, "Dx+")
813
+
814
+ Boundary.PointSpring(
815
+ int(node_id), spring_type, group_name, spring_id,
816
+ None, None, None, direction_str, normal_vector, function_id
817
+ )
818
+
819
+ @classmethod
820
+ def delete(cls):
821
+ """
822
+ Deletes all point springs from the database and resets the class.
823
+ """
824
+ cls.springs = []
825
+ return MidasAPI("DELETE", "/db/nspr")
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
- # Commom HERE ---------------------------------------------
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 exist! It will be replaced.')
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
- js2s = {'Assign':{self.ID : _Obj2JS(self)}}
32
- MidasAPI('PUT','/db/elem',js2s)
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
- js={}
41
-
42
- if obj.TYPE == 'BEAM':
43
- #--- BEAM ---------------------------------------
44
- js = {
45
- "TYPE": obj.TYPE,
46
- "MATL": obj.MATL,
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
- elif obj.TYPE == 'PLATE':
68
- #--- PLATE ---------------------------------------
69
- js = {
70
- "TYPE": obj.TYPE,
71
- "MATL": obj.MATL,
72
- "SECT": obj.SECT,
73
- "NODE": obj.NODE,
74
- "ANGLE": obj.ANGLE,
75
- "STYPE": obj.STYPE
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
- type = js['TYPE']
82
- matl = js['MATL']
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} \nJSON : {_Obj2JS(self)}\n')
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
- """ Use Beam or Truss function"""
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
- json = {"Assign":{}}
135
+ json_data = {"Assign": {}}
118
136
  for elem in cls.elements:
119
137
  js = _Obj2JS(elem)
120
- json["Assign"][elem.ID] = js
121
- return json
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 != {'message': ''}:
136
- if list(a['ELEM'].keys()) != []:
137
- Element.elements = []
138
- Element.ids=[]
139
- for elem_id in a['ELEM'].keys():
140
- _JS2Obj(elem_id,a['ELEM'][elem_id])
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
- beam_nodes =[]
165
- beam_obj = []
166
- s_locc = np.array(s_loc)
167
- unit_vec = np.array(dir)/np.linalg.norm(dir)
168
-
169
- for i in range(n+1):
170
- locc = s_locc+i*l*unit_vec/n
171
- Enode=Node(locc[0].item(),locc[1].item(),locc[2].item())
172
- beam_nodes.append(Enode.ID)
173
-
174
- for i in range(n):
175
- if id == 0 : id_new = 0
176
- else: id_new = id+i
177
- beam_obj.append(Element.Beam(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,id_new))
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
- beam_nodes =[]
185
- beam_obj = []
186
- i_loc = np.linspace(s_loc,e_loc,n+1)
187
- for i in range(n+1):
188
- Enode=Node(i_loc[i][0].item(),i_loc[i][1].item(),i_loc[i][2].item())
189
- beam_nodes.append(Enode.ID)
190
-
191
- for i in range(n):
192
- if id == 0 : id_new = 0
193
- else: id_new = id+i
194
- beam_obj.append(Element.Beam(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,id_new))
195
-
196
- return beam_obj
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
- def __init__(self,i:int,j:int,mat:int=1,sect:int=1,angle:float=0,id:int=0):
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
- def __init__(self,nodes:list,stype:int=1,mat:int=1,sect:int=1,angle:float=0,id:int=0):
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
 
midas_civil/_mapi.py CHANGED
@@ -1,6 +1,13 @@
1
1
  import requests
2
2
  import sys
3
3
 
4
+
5
+ def Midas_help():
6
+ """MIDAS Documnetation : https://midas-rnd.github.io/midasapi-python """
7
+ print("---"*22)
8
+ print("| HELP MANUAL : https://midas-rnd.github.io/midasapi-python/ |")
9
+ print("---"*22,"\n")
10
+
4
11
  class MAPI_PRODUCT:
5
12
  product = "civil"
6
13
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: midas_civil
3
- Version: 0.0.6
3
+ Version: 0.0.8
4
4
  Summary: Python library for MIDAS Civil NX
5
5
  Author: Sumit Shekhar
6
6
  Author-email: sumit.midasit@gmail.com
@@ -1,10 +1,10 @@
1
1
  midas_civil/__init__.py,sha256=mq1dqMgS92chPxRbKzoA7X33jpgz5y-8a_CpBvQUmJI,510
2
- midas_civil/_boundary.py,sha256=gGZtVtYk7ey68KOK8aNk7wGrb4OSDLqo69ynorfiJ58,19138
2
+ midas_civil/_boundary.py,sha256=VwDgysWvcwlu2gTfEyxPuXqCp4IXKs2NTn3jUz7Brhg,32390
3
3
  midas_civil/_construction.py,sha256=Pj7V-NYCkkT-aMjKXfs1jKa9klsGh48UXDLwn3BLYTY,18225
4
- midas_civil/_element.py,sha256=RyuJN2XMCrZoBBJ5a0CDbRISocPL0WyuQvYdKes6vMc,7237
4
+ midas_civil/_element.py,sha256=UbXgad_D_yNgTXWRO1zMP2hQB82BaDdMWvGymrg0L1g,18508
5
5
  midas_civil/_group.py,sha256=8iZCIB6XEgFz4Z7QEDU3wd7t18xAtNnqetw5Ch1UOeY,10074
6
6
  midas_civil/_load.py,sha256=__qVQgbyfAILyoLv8eDGZ_1uMunzzDG1tUu5zWWD-oA,17213
7
- midas_civil/_mapi.py,sha256=5B6m4csZiERswaVfgI8pQLopfHXmfwaNlJyLsi204Pw,2132
7
+ midas_civil/_mapi.py,sha256=NImeBj8L7rwrYuaeXbaU3-UnCL4Fyp6tB1wf0UpdQp8,2354
8
8
  midas_civil/_material.py,sha256=hlgBGL5JTN1-aDL5hXwU9lalAhzoD0pPP-8HE2jCfAs,10711
9
9
  midas_civil/_model.py,sha256=wbf3gmFgv0ZB4rphxr-3xLspH-WD7p6aB7PQCNGHA40,14923
10
10
  midas_civil/_node.py,sha256=yj17RT3Ei7RafWQVHThjwPGUHe3DTQbuv0MwuDjCJro,3513
@@ -13,8 +13,8 @@ midas_civil/_result_extract.py,sha256=nYR3cYYZVogVje-Y4Y1Lh1S-lCDs_n-1MCwvkmWk6q
13
13
  midas_civil/_section.py,sha256=56RWJdyvDr7Es7Is8Fxq3jPCPP57WW8ECCYZzhm6bf0,26375
14
14
  midas_civil/_thickness.py,sha256=Mungooyfo0_JJUmjPCr25h0PSSW_TtEfcMa-hz3YGZA,3146
15
15
  midas_civil/_utils.py,sha256=eymiqO8KaTKdhVY3saebqNS0BbUUmGmgw3-ELKqew0A,2611
16
- midas_civil-0.0.6.dist-info/licenses/LICENSE,sha256=zrL4RwZC4rb-by_ZHKXwKdIwcs6ATy59TPZ9HxPHCrs,1071
17
- midas_civil-0.0.6.dist-info/METADATA,sha256=2LRiSrSpRr3xu9aKBNRAoP-VUCvt1GhonYhWQBDPcRo,1709
18
- midas_civil-0.0.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
- midas_civil-0.0.6.dist-info/top_level.txt,sha256=_NFmrlN5V9OxJ-PAO4s_om8OA8uupXho3QqZcSsnbuI,12
20
- midas_civil-0.0.6.dist-info/RECORD,,
16
+ midas_civil-0.0.8.dist-info/licenses/LICENSE,sha256=zrL4RwZC4rb-by_ZHKXwKdIwcs6ATy59TPZ9HxPHCrs,1071
17
+ midas_civil-0.0.8.dist-info/METADATA,sha256=Xfg4B9gsHuRxpVwwbV-xX7mwnrVe1zAHD4noTzCZXzA,1709
18
+ midas_civil-0.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
+ midas_civil-0.0.8.dist-info/top_level.txt,sha256=_NFmrlN5V9OxJ-PAO4s_om8OA8uupXho3QqZcSsnbuI,12
20
+ midas_civil-0.0.8.dist-info/RECORD,,