midas-civil 0.0.6__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.6 → midas_civil-0.0.8}/PKG-INFO +1 -1
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/_boundary.py +323 -2
- midas_civil-0.0.8/midas_civil/_element.py +496 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/_mapi.py +7 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil.egg-info/PKG-INFO +1 -1
- {midas_civil-0.0.6 → midas_civil-0.0.8}/setup.py +1 -1
- midas_civil-0.0.6/midas_civil/_element.py +0 -275
- {midas_civil-0.0.6 → midas_civil-0.0.8}/LICENSE +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/README.md +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/__init__.py +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/_construction.py +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/_group.py +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/_load.py +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/_material.py +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/_model.py +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/_node.py +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/_result.py +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/_result_extract.py +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/_section.py +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/_thickness.py +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil/_utils.py +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil.egg-info/SOURCES.txt +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil.egg-info/dependency_links.txt +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil.egg-info/requires.txt +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/midas_civil.egg-info/top_level.txt +0 -0
- {midas_civil-0.0.6 → midas_civil-0.0.8}/setup.cfg +0 -0
|
@@ -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")
|
|
@@ -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,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,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
|