midas-civil 0.0.7__tar.gz → 0.0.8__tar.gz
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-0.0.7 → midas_civil-0.0.8}/PKG-INFO +1 -1
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/_boundary.py +248 -10
- midas_civil-0.0.8/midas_civil/_element.py +496 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil.egg-info/PKG-INFO +1 -1
- {midas_civil-0.0.7 → midas_civil-0.0.8}/setup.py +1 -1
- midas_civil-0.0.7/midas_civil/_element.py +0 -275
- {midas_civil-0.0.7 → midas_civil-0.0.8}/LICENSE +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/README.md +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/__init__.py +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/_construction.py +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/_group.py +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/_load.py +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/_mapi.py +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/_material.py +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/_model.py +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/_node.py +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/_result.py +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/_result_extract.py +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/_section.py +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/_thickness.py +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil/_utils.py +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil.egg-info/SOURCES.txt +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil.egg-info/dependency_links.txt +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil.egg-info/requires.txt +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/midas_civil.egg-info/top_level.txt +0 -0
- {midas_civil-0.0.7 → midas_civil-0.0.8}/setup.cfg +0 -0
|
@@ -11,7 +11,6 @@ def convList(item):
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
|
|
15
14
|
class Boundary:
|
|
16
15
|
|
|
17
16
|
@classmethod
|
|
@@ -20,6 +19,8 @@ class Boundary:
|
|
|
20
19
|
if cls.Support.sups!=[]: cls.Support.create()
|
|
21
20
|
if cls.ElasticLink.links!=[]: cls.ElasticLink.create()
|
|
22
21
|
if cls.RigidLink.links!=[]: cls.RigidLink.create()
|
|
22
|
+
if cls.MLFC.func!=[]: cls.RigidLink.create()
|
|
23
|
+
if cls.PointSpring.springs!=[]: cls.PointSpring.create()
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
@classmethod
|
|
@@ -28,6 +29,8 @@ class Boundary:
|
|
|
28
29
|
cls.Support.delete()
|
|
29
30
|
cls.ElasticLink.delete()
|
|
30
31
|
cls.RigidLink.delete()
|
|
32
|
+
cls.MLFC.delete()
|
|
33
|
+
cls.PointSpring.delete()
|
|
31
34
|
|
|
32
35
|
@classmethod
|
|
33
36
|
def sync(cls):
|
|
@@ -35,8 +38,8 @@ class Boundary:
|
|
|
35
38
|
cls.Support.sync()
|
|
36
39
|
cls.ElasticLink.sync()
|
|
37
40
|
cls.RigidLink.sync()
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
cls.MLFC.sync()
|
|
42
|
+
cls.PointSpring.delete()
|
|
40
43
|
|
|
41
44
|
|
|
42
45
|
class Support:
|
|
@@ -506,10 +509,33 @@ class Boundary:
|
|
|
506
509
|
#---------------------------------------------------------------------------------------------------------------
|
|
507
510
|
|
|
508
511
|
class MLFC:
|
|
512
|
+
|
|
509
513
|
func = []
|
|
510
514
|
_id = []
|
|
511
515
|
|
|
512
|
-
def __init__(self,name,type:str='FORCE',symm:bool=True,data:list=[[0,0],[1,1]],id=None):
|
|
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
|
+
"""
|
|
513
539
|
self.NAME = name
|
|
514
540
|
self.TYPE = type
|
|
515
541
|
self.SYMM = symm
|
|
@@ -534,6 +560,7 @@ class Boundary:
|
|
|
534
560
|
|
|
535
561
|
@classmethod
|
|
536
562
|
def json(cls):
|
|
563
|
+
|
|
537
564
|
json = {"Assign": {}}
|
|
538
565
|
for fn in cls.func:
|
|
539
566
|
json["Assign"][fn.ID]={
|
|
@@ -550,22 +577,20 @@ class Boundary:
|
|
|
550
577
|
@classmethod
|
|
551
578
|
def create(cls):
|
|
552
579
|
"""
|
|
553
|
-
Sends all FUNC data
|
|
580
|
+
Sends all FUNC data.
|
|
554
581
|
"""
|
|
555
582
|
MidasAPI("PUT", "/db/MLFC", cls.json())
|
|
556
583
|
|
|
557
584
|
@classmethod
|
|
558
585
|
def get(cls):
|
|
559
586
|
"""
|
|
560
|
-
Retrieves data
|
|
587
|
+
Retrieves data.
|
|
561
588
|
"""
|
|
562
589
|
return MidasAPI("GET", "/db/MLFC")
|
|
563
590
|
|
|
564
591
|
@classmethod
|
|
565
592
|
def sync(cls):
|
|
566
|
-
|
|
567
|
-
Updates the class with data from the Midas API.
|
|
568
|
-
"""
|
|
593
|
+
|
|
569
594
|
cls.links = []
|
|
570
595
|
a = cls.get()
|
|
571
596
|
if a != {'message': ''}:
|
|
@@ -584,4 +609,217 @@ class Boundary:
|
|
|
584
609
|
Deletes all func from the database and resets the class.
|
|
585
610
|
"""
|
|
586
611
|
cls.links = []
|
|
587
|
-
return MidasAPI("DELETE", "/db/MLFC")
|
|
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")
|
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
from ._mapi import *
|
|
2
|
+
from ._node import *
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
def _ADD(self):
|
|
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
|
+
"""
|
|
11
|
+
id = int(self.ID)
|
|
12
|
+
if not Element.ids:
|
|
13
|
+
count = 1
|
|
14
|
+
else:
|
|
15
|
+
count = max(Element.ids) + 1
|
|
16
|
+
|
|
17
|
+
if id == 0:
|
|
18
|
+
self.ID = count
|
|
19
|
+
Element.elements.append(self)
|
|
20
|
+
Element.ids.append(int(self.ID))
|
|
21
|
+
elif id in Element.ids:
|
|
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
|
|
26
|
+
else:
|
|
27
|
+
self.ID = id
|
|
28
|
+
Element.elements.append(self)
|
|
29
|
+
Element.ids.append(int(self.ID))
|
|
30
|
+
|
|
31
|
+
def _updateElem(self):
|
|
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)
|
|
35
|
+
return js2s
|
|
36
|
+
|
|
37
|
+
def _Obj2JS(obj):
|
|
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
|
+
}
|
|
46
|
+
|
|
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
|
+
|
|
80
|
+
return js
|
|
81
|
+
|
|
82
|
+
def _JS2Obj(id, js):
|
|
83
|
+
"""Converts a JSON dictionary back into a Python element object during sync."""
|
|
84
|
+
elem_type = js.get('TYPE')
|
|
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'])
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class _common:
|
|
117
|
+
"""Common base class for all element types."""
|
|
118
|
+
def __str__(self):
|
|
119
|
+
return str(f'ID = {self.ID} \nJSON : {_Obj2JS(self)}\n')
|
|
120
|
+
|
|
121
|
+
def update(self):
|
|
122
|
+
return _updateElem(self)
|
|
123
|
+
|
|
124
|
+
# --- Main Element Class ---
|
|
125
|
+
class Element:
|
|
126
|
+
"""
|
|
127
|
+
Main class to create and manage structural elements like Beams, Trusses,
|
|
128
|
+
Plates, Tension/Compression-only elements, and Solids.
|
|
129
|
+
"""
|
|
130
|
+
elements = []
|
|
131
|
+
ids = []
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def json(cls):
|
|
135
|
+
json_data = {"Assign": {}}
|
|
136
|
+
for elem in cls.elements:
|
|
137
|
+
js = _Obj2JS(elem)
|
|
138
|
+
json_data["Assign"][elem.ID] = js
|
|
139
|
+
return json_data
|
|
140
|
+
|
|
141
|
+
@classmethod
|
|
142
|
+
def create(cls):
|
|
143
|
+
if cls.elements:
|
|
144
|
+
MidasAPI("PUT", "/db/ELEM", Element.json())
|
|
145
|
+
|
|
146
|
+
@staticmethod
|
|
147
|
+
def get():
|
|
148
|
+
return MidasAPI("GET", "/db/ELEM")
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def sync():
|
|
152
|
+
a = Element.get()
|
|
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
|
+
|
|
159
|
+
@staticmethod
|
|
160
|
+
def delete():
|
|
161
|
+
MidasAPI("DELETE", "/db/ELEM")
|
|
162
|
+
Element.elements = []
|
|
163
|
+
Element.ids = []
|
|
164
|
+
|
|
165
|
+
# --- Element Type Subclasses ---
|
|
166
|
+
|
|
167
|
+
class Beam(_common):
|
|
168
|
+
|
|
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
|
+
"""
|
|
196
|
+
self.ID = id
|
|
197
|
+
self.TYPE = 'BEAM'
|
|
198
|
+
self.MATL = mat
|
|
199
|
+
self.SECT = sect
|
|
200
|
+
self.NODE = [i, j]
|
|
201
|
+
self.ANGLE = angle
|
|
202
|
+
_ADD(self)
|
|
203
|
+
|
|
204
|
+
@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
|
|
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))
|
|
220
|
+
|
|
221
|
+
return beam_obj
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@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):
|
|
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
|
|
239
|
+
|
|
240
|
+
class Truss(_common):
|
|
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
|
+
"""
|
|
265
|
+
self.ID = id
|
|
266
|
+
self.TYPE = 'TRUSS'
|
|
267
|
+
self.MATL = mat
|
|
268
|
+
self.SECT = sect
|
|
269
|
+
self.NODE = [i, j]
|
|
270
|
+
self.ANGLE = angle
|
|
271
|
+
_ADD(self)
|
|
272
|
+
|
|
273
|
+
@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):
|
|
275
|
+
beam_nodes =[]
|
|
276
|
+
beam_obj =[]
|
|
277
|
+
s_locc = np.array(s_loc)
|
|
278
|
+
unit_vec = np.array(dir)/np.linalg.norm(dir)
|
|
279
|
+
|
|
280
|
+
for i in range(n+1):
|
|
281
|
+
locc = s_locc+i*l*unit_vec/n
|
|
282
|
+
Enode=Node(locc[0].item(),locc[1].item(),locc[2].item())
|
|
283
|
+
beam_nodes.append(Enode.ID)
|
|
284
|
+
|
|
285
|
+
for i in range(n):
|
|
286
|
+
if id == 0 : id_new = 0
|
|
287
|
+
else: id_new = id+i
|
|
288
|
+
beam_obj.append(Element.Truss(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,id_new))
|
|
289
|
+
|
|
290
|
+
return beam_obj
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
@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):
|
|
295
|
+
beam_nodes =[]
|
|
296
|
+
beam_obj = []
|
|
297
|
+
i_loc = np.linspace(s_loc,e_loc,n+1)
|
|
298
|
+
for i in range(n+1):
|
|
299
|
+
Enode=Node(i_loc[i][0].item(),i_loc[i][1].item(),i_loc[i][2].item())
|
|
300
|
+
beam_nodes.append(Enode.ID)
|
|
301
|
+
|
|
302
|
+
for i in range(n):
|
|
303
|
+
if id == 0 : id_new = 0
|
|
304
|
+
else: id_new = id+i
|
|
305
|
+
beam_obj.append(Element.Truss(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,id_new))
|
|
306
|
+
|
|
307
|
+
return beam_obj
|
|
308
|
+
|
|
309
|
+
class Plate(_common):
|
|
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
|
+
"""
|
|
334
|
+
self.ID = id
|
|
335
|
+
self.TYPE = 'PLATE'
|
|
336
|
+
self.MATL = mat
|
|
337
|
+
self.SECT = sect
|
|
338
|
+
self.NODE = nodes
|
|
339
|
+
self.ANGLE = angle
|
|
340
|
+
self.STYPE = stype
|
|
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
|
|
453
|
+
_ADD(self)
|
|
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)
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
|
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
from ._mapi import *
|
|
2
|
-
from ._node import *
|
|
3
|
-
|
|
4
|
-
import numpy as np
|
|
5
|
-
|
|
6
|
-
def _ADD(self):
|
|
7
|
-
# Commom HERE ---------------------------------------------
|
|
8
|
-
id = int(self.ID)
|
|
9
|
-
if Element.ids == []:
|
|
10
|
-
count = 1
|
|
11
|
-
else:
|
|
12
|
-
count = max(Element.ids)+1
|
|
13
|
-
|
|
14
|
-
if id==0 :
|
|
15
|
-
self.ID = count
|
|
16
|
-
Element.elements.append(self)
|
|
17
|
-
Element.ids.append(int(self.ID))
|
|
18
|
-
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
|
|
23
|
-
else:
|
|
24
|
-
self.ID=id
|
|
25
|
-
Element.elements.append(self)
|
|
26
|
-
Element.ids.append(int(self.ID))
|
|
27
|
-
# Common END -------------------------------------------------------
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def _updateElem(self):
|
|
31
|
-
js2s = {'Assign':{self.ID : _Obj2JS(self)}}
|
|
32
|
-
MidasAPI('PUT','/db/elem',js2s)
|
|
33
|
-
return js2s
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
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
|
-
}
|
|
66
|
-
|
|
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
|
-
|
|
78
|
-
return js
|
|
79
|
-
|
|
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)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
class _common:
|
|
98
|
-
def __str__(self):
|
|
99
|
-
return str(f'ID = {self.ID} \nJSON : {_Obj2JS(self)}\n')
|
|
100
|
-
|
|
101
|
-
def update(self):
|
|
102
|
-
return _updateElem(self)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
#6 Class to create elements
|
|
110
|
-
class Element:
|
|
111
|
-
""" Use Beam or Truss function"""
|
|
112
|
-
elements = []
|
|
113
|
-
ids = []
|
|
114
|
-
|
|
115
|
-
@classmethod
|
|
116
|
-
def json(cls):
|
|
117
|
-
json = {"Assign":{}}
|
|
118
|
-
for elem in cls.elements:
|
|
119
|
-
js = _Obj2JS(elem)
|
|
120
|
-
json["Assign"][elem.ID] = js
|
|
121
|
-
return json
|
|
122
|
-
|
|
123
|
-
@classmethod
|
|
124
|
-
def create(cls):
|
|
125
|
-
if cls.elements!=[]:
|
|
126
|
-
MidasAPI("PUT","/db/ELEM",Element.json())
|
|
127
|
-
|
|
128
|
-
@staticmethod
|
|
129
|
-
def get():
|
|
130
|
-
return MidasAPI("GET","/db/ELEM")
|
|
131
|
-
|
|
132
|
-
@staticmethod
|
|
133
|
-
def sync():
|
|
134
|
-
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
|
-
|
|
142
|
-
@staticmethod
|
|
143
|
-
def delete():
|
|
144
|
-
MidasAPI("DELETE","/db/ELEM")
|
|
145
|
-
Element.elements=[]
|
|
146
|
-
Element.ids=[]
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
class Beam(_common):
|
|
151
|
-
|
|
152
|
-
def __init__(self,i:int,j:int,mat:int=1,sect:int=1,angle:float=0,id:int=0):
|
|
153
|
-
self.ID = id
|
|
154
|
-
self.TYPE = 'BEAM'
|
|
155
|
-
self.MATL = mat
|
|
156
|
-
self.SECT = sect
|
|
157
|
-
self.NODE=[i,j]
|
|
158
|
-
self.ANGLE = angle
|
|
159
|
-
|
|
160
|
-
_ADD(self)
|
|
161
|
-
|
|
162
|
-
@staticmethod
|
|
163
|
-
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
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
@staticmethod
|
|
183
|
-
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
|
-
|
|
201
|
-
|
|
202
|
-
class Truss(_common):
|
|
203
|
-
|
|
204
|
-
def __init__(self,i:int,j:int,mat:int=1,sect:int=1,angle:float=0,id:int=0):
|
|
205
|
-
self.ID = id
|
|
206
|
-
self.TYPE = 'TRUSS'
|
|
207
|
-
self.MATL = mat
|
|
208
|
-
self.SECT = sect
|
|
209
|
-
self.NODE=[i,j]
|
|
210
|
-
self.ANGLE = angle
|
|
211
|
-
|
|
212
|
-
_ADD(self)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
@staticmethod
|
|
217
|
-
def SDL(s_loc:list,dir:list,l:float,n:int=1,mat:int=1,sect:int=1,angle:float=0,id:int=0):
|
|
218
|
-
beam_nodes =[]
|
|
219
|
-
beam_obj =[]
|
|
220
|
-
s_locc = np.array(s_loc)
|
|
221
|
-
unit_vec = np.array(dir)/np.linalg.norm(dir)
|
|
222
|
-
|
|
223
|
-
for i in range(n+1):
|
|
224
|
-
locc = s_locc+i*l*unit_vec/n
|
|
225
|
-
Enode=Node(locc[0].item(),locc[1].item(),locc[2].item())
|
|
226
|
-
beam_nodes.append(Enode.ID)
|
|
227
|
-
|
|
228
|
-
for i in range(n):
|
|
229
|
-
if id == 0 : id_new = 0
|
|
230
|
-
else: id_new = id+i
|
|
231
|
-
beam_obj.append(Element.Truss(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,id_new))
|
|
232
|
-
|
|
233
|
-
return beam_obj
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
@staticmethod
|
|
237
|
-
def SE(s_loc:list,e_loc:list,n:int=1,mat:int=1,sect:int=1,angle:float=0,id:int=0):
|
|
238
|
-
beam_nodes =[]
|
|
239
|
-
beam_obj = []
|
|
240
|
-
i_loc = np.linspace(s_loc,e_loc,n+1)
|
|
241
|
-
for i in range(n+1):
|
|
242
|
-
Enode=Node(i_loc[i][0].item(),i_loc[i][1].item(),i_loc[i][2].item())
|
|
243
|
-
beam_nodes.append(Enode.ID)
|
|
244
|
-
|
|
245
|
-
for i in range(n):
|
|
246
|
-
if id == 0 : id_new = 0
|
|
247
|
-
else: id_new = id+i
|
|
248
|
-
beam_obj.append(Element.Truss(beam_nodes[i],beam_nodes[i+1],mat,sect,angle,id_new))
|
|
249
|
-
|
|
250
|
-
return beam_obj
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
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):
|
|
257
|
-
self.ID = id
|
|
258
|
-
self.TYPE = 'PLATE'
|
|
259
|
-
self.MATL = mat
|
|
260
|
-
self.SECT = sect
|
|
261
|
-
self.NODE=nodes
|
|
262
|
-
self.ANGLE = angle
|
|
263
|
-
self.STYPE = stype
|
|
264
|
-
|
|
265
|
-
_ADD(self)
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|