midas-civil 1.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. midas_civil/_BoundaryChangeAssignment.py +278 -0
  2. midas_civil/__init__.py +51 -0
  3. midas_civil/_analysiscontrol.py +585 -0
  4. midas_civil/_boundary.py +888 -0
  5. midas_civil/_construction.py +1004 -0
  6. midas_civil/_element.py +1346 -0
  7. midas_civil/_group.py +337 -0
  8. midas_civil/_load.py +967 -0
  9. midas_civil/_loadcomb.py +159 -0
  10. midas_civil/_mapi.py +249 -0
  11. midas_civil/_material.py +1692 -0
  12. midas_civil/_model.py +522 -0
  13. midas_civil/_movingload.py +1479 -0
  14. midas_civil/_node.py +532 -0
  15. midas_civil/_result_table.py +929 -0
  16. midas_civil/_result_test.py +5455 -0
  17. midas_civil/_section/_TapdbSecSS.py +175 -0
  18. midas_civil/_section/__init__.py +413 -0
  19. midas_civil/_section/_compositeSS.py +283 -0
  20. midas_civil/_section/_dbSecSS.py +164 -0
  21. midas_civil/_section/_offsetSS.py +53 -0
  22. midas_civil/_section/_pscSS copy.py +455 -0
  23. midas_civil/_section/_pscSS.py +822 -0
  24. midas_civil/_section/_tapPSC12CellSS.py +565 -0
  25. midas_civil/_section/_unSupp.py +58 -0
  26. midas_civil/_settlement.py +161 -0
  27. midas_civil/_temperature.py +677 -0
  28. midas_civil/_tendon.py +1016 -0
  29. midas_civil/_thickness.py +147 -0
  30. midas_civil/_utils.py +529 -0
  31. midas_civil/_utilsFunc/__init__.py +0 -0
  32. midas_civil/_utilsFunc/_line2plate.py +636 -0
  33. midas_civil/_view.py +891 -0
  34. midas_civil/_view_trial.py +430 -0
  35. midas_civil/_visualise.py +347 -0
  36. midas_civil-1.4.1.dist-info/METADATA +74 -0
  37. midas_civil-1.4.1.dist-info/RECORD +40 -0
  38. midas_civil-1.4.1.dist-info/WHEEL +5 -0
  39. midas_civil-1.4.1.dist-info/licenses/LICENSE +21 -0
  40. midas_civil-1.4.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,147 @@
1
+ from ._mapi import MidasAPI
2
+
3
+
4
+
5
+ def _ThikADD(self):
6
+ # Commom HERE ---------------------------------------------
7
+ id = int(self.ID)
8
+ if Thickness.ids == []:
9
+ count = 1
10
+ else:
11
+ count = max(Thickness.ids)+1
12
+
13
+ if id==0 :
14
+ self.ID = count
15
+ Thickness.thick.append(self)
16
+ Thickness.ids.append(int(self.ID))
17
+ elif id in Thickness.ids:
18
+ self.ID=int(id)
19
+ print(f'⚠️ Thickness with ID {id} already exist! It will be replaced.')
20
+ index=Thickness.ids.index(id)
21
+ Thickness.thick[index]=self
22
+ else:
23
+ self.ID=id
24
+ Thickness.thick.append(self)
25
+ Thickness.ids.append(int(self.ID))
26
+ # Common END -------------------------------------------------------
27
+
28
+
29
+ def _updateThik(self):
30
+ js2s = {'Assign':{self.ID : _Obj2JS(self)}}
31
+ MidasAPI('PUT','/db/THIK',js2s)
32
+ return js2s
33
+
34
+
35
+ def _Obj2JS(obj):
36
+
37
+ js={
38
+ "NAME": obj.NAME,
39
+ "TYPE": obj.TYPE,
40
+ "bINOUT": obj.bINOUT,
41
+ "T_IN": obj.T_IN,
42
+ "T_OUT": obj.T_OUT,
43
+ "OFFSET": obj.OFF_TYPE,
44
+ "O_VALUE": obj.OFFSET
45
+ }
46
+ return js
47
+
48
+
49
+
50
+ def _JS2Obj(id,js):
51
+ name = js['NAME']
52
+ type = js['TYPE']
53
+ binout = js['bINOUT']
54
+ t_in = js['T_IN']
55
+ t_out = js['T_OUT']
56
+ try : offset = js['OFFSET']
57
+ except: offset = 0
58
+ off_value = js['O_VALUE']
59
+
60
+ t_out2=-1
61
+ if binout:t_out2 = t_out
62
+
63
+ if type == 'VALUE':
64
+ Thickness(t_in,t_out2,off_value,offset,name,id)
65
+
66
+
67
+
68
+ class _common:
69
+ def __str__(self):
70
+ return str(f'ID = {self.ID} \nJSON : {_Obj2JS(self)}\n')
71
+
72
+ def update(self):
73
+ return _updateThik(self)
74
+
75
+
76
+ class Thickness(_common):
77
+ """Create Thicknes"""
78
+ thick = []
79
+ ids = []
80
+
81
+ def __init__(self,thick=0.0,thick_out=-1,offset=0,off_type='rat',name="",id=None):
82
+ if id == None: id = 0
83
+ self.ID = id
84
+ if name == "":
85
+ self.NAME = str(thick)
86
+ else: self.NAME = name
87
+ self.TYPE = 'VALUE'
88
+ self.T_IN = thick
89
+ self.bINOUT = True
90
+
91
+ if thick_out==-1:
92
+ self.T_OUT = thick
93
+ self.bINOUT = False
94
+ else: self.T_OUT = thick_out
95
+
96
+ self.OFFSET = offset
97
+
98
+ if off_type=='rat':
99
+ self.OFF_TYPE = 1
100
+ else: self.OFF_TYPE = 2
101
+
102
+ if offset==0:
103
+ self.OFF_TYPE =0
104
+
105
+ _ThikADD(self)
106
+
107
+
108
+ @classmethod
109
+ def json(cls):
110
+ json = {"Assign":{}}
111
+ for sect in cls.thick:
112
+ js = _Obj2JS(sect)
113
+ json["Assign"][sect.ID] = js
114
+ return json
115
+
116
+ @staticmethod
117
+ def create():
118
+ MidasAPI("PUT","/db/THIK",Thickness.json())
119
+
120
+ @staticmethod
121
+ def get():
122
+ return MidasAPI("GET","/db/THIK")
123
+
124
+
125
+ @staticmethod
126
+ def delete():
127
+ MidasAPI("DELETE","/db/THIK")
128
+ Thickness.thick=[]
129
+ Thickness.ids=[]
130
+
131
+ @staticmethod
132
+ def clear():
133
+ Thickness.thick=[]
134
+ Thickness.ids=[]
135
+
136
+
137
+ @staticmethod
138
+ def sync():
139
+ a = Thickness.get()
140
+ if a != {'message': ''}:
141
+ if list(a['THIK'].keys()) != []:
142
+ Thickness.thick = []
143
+ Thickness.ids=[]
144
+ for sect_id in a['THIK'].keys():
145
+ _JS2Obj(sect_id,a['THIK'][sect_id])
146
+
147
+
midas_civil/_utils.py ADDED
@@ -0,0 +1,529 @@
1
+ # from ._model import *
2
+ # from ._mapi import *
3
+ from __future__ import annotations
4
+ from math import hypot,sqrt
5
+ import numpy as np
6
+ from typing import Literal
7
+
8
+ #Function to remove duplicate set of values from 2 lists
9
+ # def unique_lists(li1, li2):
10
+ # if type (li1) == list and type (li2) == list:
11
+ # if len(li1) == len(li2):
12
+ # indices_to_remove = []
13
+ # for i in range(len(li1)):
14
+ # for j in range(i+1,len(li1)):
15
+ # if li1[i] == li1[j] and li2[i] == li2[j]:
16
+ # indices_to_remove.append(j)
17
+ # for index in sorted(indices_to_remove, reverse = True):
18
+ # del li1[index]
19
+ # del li2[index]
20
+
21
+
22
+ # def sect_inp(sec):
23
+ # """Section ID. Enter one section id or list of section IDs. Sample: sect_inp(1) OR sect_inp([3,2,5])"""
24
+ # Model.units()
25
+ # a = MidasAPI("GET","/db/SECT",{"Assign":{}})
26
+ # if type(sec)==int: sec = [sec]
27
+ # b={}
28
+ # for s in sec:
29
+ # if str(s) in a['SECT'].keys() : b.update({s : a['SECT'][str(s)]})
30
+ # # if elem = [0] and sec!=0: b.update({sec : })
31
+ # if b == {}: b = "The required section ID is not defined in connected model file."
32
+ # return(b)
33
+ #---------------------------------------------------------------------------------------------------------------
34
+
35
+
36
+
37
+ def sFlatten(list_of_list):
38
+ # list_of_list = [list_of_list]
39
+ return [item for elem in list_of_list for item in (elem if isinstance(elem, (list,np.ndarray)) else [elem])]
40
+
41
+ # def getID_orig(element_list):
42
+ # """Return ID of Node and Element"""
43
+ # return [beam.ID for beam in sFlatten(element_list)]
44
+
45
+ def getID(*objects):
46
+ objects = list(objects)
47
+ _getID2(objects)
48
+ return objects
49
+
50
+ def _getID2(objects):
51
+ for i in range(len(objects)):
52
+ if isinstance(objects[i], list):
53
+ _getID2(objects[i]) # Recursive call for sublist
54
+ else:
55
+ objects[i] = objects[i].ID
56
+
57
+ def getLOC(objects):
58
+ ''' Get location for multiple node objects'''
59
+ _getLOC2(objects)
60
+ return objects
61
+
62
+ def _getLOC2(objects):
63
+ for i in range(len(objects)):
64
+ if isinstance(objects[i], list):
65
+ _getLOC2(objects[i]) # Recursive call for sublist
66
+ else:
67
+ objects[i] = objects[i].LOC
68
+
69
+ def getNodeID(*objects):
70
+ objects = list(objects)
71
+ _getNodeID2(objects)
72
+ return objects
73
+
74
+ def _getNodeID2(objects):
75
+ for i in range(len(objects)):
76
+ if isinstance(objects[i], list):
77
+ _getNodeID2(objects[i]) # Recursive call for sublist
78
+ else:
79
+ objects[i] = objects[i].NODES
80
+
81
+
82
+
83
+
84
+ # def getNodeID_orig(element_list):
85
+ # """Return Node IDs of Element"""
86
+ # # return list(sFlatten([beam.NODES for beam in sFlatten(element_list)]))
87
+ # return list(sFlatten([beam.NODES for beam in sFlatten(element_list)]))
88
+
89
+
90
+ def arr2csv(nlist):
91
+ strinff = ",".join(map(str,nlist))
92
+ return strinff
93
+
94
+ def zz_add_to_dict(dictionary, key, value):
95
+ if key in dictionary:
96
+ dictionary[key].append(value)
97
+ else:
98
+ dictionary[key] = [value]
99
+
100
+
101
+ def _convItem2List(item):
102
+ if isinstance(item,(list,np.ndarray)):
103
+ return item
104
+ return [item]
105
+
106
+ def _matchArray(A,B):
107
+ '''Matches B to length of A
108
+ Return B'''
109
+ A = _convItem2List(A)
110
+ B = _convItem2List(B)
111
+ n = len(A)
112
+ if len(B) >= n:
113
+ return B[:n]
114
+ return B + [B[-1]] * (n - len(B))
115
+
116
+ def _longestList(A,B):
117
+ """ Matches A , B list and returns the list with longest length with last element repeated """
118
+ A = _convItem2List(A)
119
+ B = _convItem2List(B)
120
+ nA = len(A)
121
+ nB = len(B)
122
+
123
+ if nA >= nB:
124
+ return (A , B + [B[-1]] * (nA - nB))
125
+ return (A + [A[-1]] * (nB - nA),B)
126
+
127
+
128
+ _alignType = Literal['cubic','akima','makima','pchip']
129
+
130
+ class utils:
131
+ ''' Contains helper function and utilities'''
132
+ __RC_Grillage_nSpan = 1
133
+ class Alignment:
134
+ '''Defines alignment object passing through the points
135
+ X -> monotonous increasing'''
136
+
137
+ def __init__(self,points,type: _alignType = 'cubic'):
138
+ '''
139
+ **POINTS** -> Points on the alignment [[x,y] , [x,y] , [x,y] ....]
140
+ **TYPE** -> Type of interpolating curve
141
+ cubic , akima , makima , pchip
142
+ '''
143
+ from scipy.interpolate import CubicSpline , Akima1DInterpolator , PchipInterpolator
144
+
145
+ _pt_x = [pt[0] for pt in points]
146
+ _pt_y = [pt[1] for pt in points]
147
+
148
+ # _alignment = splrep(_pt_x, _pt_y)
149
+ if type == 'akima':
150
+ _alignment = Akima1DInterpolator(_pt_x, _pt_y,method='akima')
151
+ elif type == 'makima':
152
+ _alignment = Akima1DInterpolator(_pt_x, _pt_y,method='makima')
153
+ elif type == 'pchip':
154
+ _alignment = PchipInterpolator(_pt_x, _pt_y)
155
+ else :
156
+ _alignment = CubicSpline(_pt_x, _pt_y)
157
+
158
+ # _alignSlope = Akima1DInterpolator(_pt_x, _pt_y,method='akima') # Used for slope calculation
159
+
160
+ _n=100
161
+ # INITIAL ALGINMENT - Mapping U parameter to X (based on Distance)
162
+ _x_fine = np.linspace(_pt_x[0],_pt_x[-1],_n)
163
+
164
+ _y_fine = _alignment(_x_fine)
165
+
166
+ _dx = np.diff(_x_fine)
167
+ _dy = np.diff(_y_fine)
168
+
169
+ _dl=[]
170
+ for i in range(len(_dx)):
171
+ _dl.append(hypot(_dx[i],_dy[i]))
172
+
173
+ _cumLength = np.insert(np.cumsum(_dl),0,0)
174
+ _totalLength = _cumLength[-1]
175
+
176
+ _u_fine = _cumLength/_totalLength
177
+
178
+ self.ALIGNMENT = _alignment
179
+ # self.ALIGNSLOPE = _alignSlope
180
+ self.TOTALLENGTH = _totalLength
181
+ self.CUMLENGTH = _cumLength
182
+ self.PT_X = _pt_x
183
+ self.PT_Y = _pt_y
184
+ self.X_FINE = _x_fine
185
+ self.Y_FINE = _y_fine
186
+ self.U_FINE = _u_fine
187
+
188
+ def getPoint(self,distance):
189
+ x_interp = np.interp(distance,self.CUMLENGTH,self.X_FINE)
190
+ y_interp = np.interp(distance,self.CUMLENGTH,self.Y_FINE)
191
+ return x_interp , y_interp
192
+
193
+ def getSlope(self,distance):
194
+ 'Returns theta in radians (-pi/2 to pi/2)'
195
+ x_interp = np.interp(distance,self.CUMLENGTH,self.X_FINE)
196
+ slope = self.ALIGNMENT(x_interp,1) # Tan theta
197
+ angle = np.atan(slope)
198
+ return angle
199
+
200
+
201
+ @staticmethod
202
+ def transformPoint(point:tuple,initial_align:utils.Alignment,final_align:utils.Alignment) -> list :
203
+ '''
204
+ Transforms a point (x,y) => [X , Y]
205
+ Maps a point (x,y) wrt Initial alignment curve to a new Final alignment (X,Y)
206
+ '''
207
+ ptx = point[0]
208
+ pty = point[1]
209
+ distx = 100000 #Initial high distance
210
+ idx = 0
211
+ y_ref = 0
212
+ fact = 10000
213
+ for q in range(101):
214
+ x_onLine1 = ptx+initial_align.TOTALLENGTH*(q-50)/fact
215
+ if x_onLine1 < initial_align.PT_X[0]:
216
+ continue
217
+ if x_onLine1 > initial_align.PT_X[-1]:
218
+ break
219
+ # y_onLine1 = splev(x_onLine1, initial_align.ALIGNMENT)
220
+ y_onLine1 = initial_align.ALIGNMENT(x_onLine1)
221
+ dist = hypot(ptx-x_onLine1,pty-y_onLine1)
222
+ if dist <= distx:
223
+ distx = dist
224
+ idx = q
225
+ y_ref = y_onLine1
226
+ # print(f" > X location of line = {x_onLine1} Y on Line = {y_onLine1}| Distance = {dist} | Index = {q}")
227
+
228
+ final_u = np.interp(ptx+initial_align.TOTALLENGTH*(idx-50)/fact,initial_align.X_FINE,initial_align.U_FINE)
229
+ off = np.sign(pty-y_ref)*distx
230
+ x2_interp = np.interp(final_u,final_align.U_FINE,final_align.X_FINE)
231
+
232
+ # y2_interp = splev(x2_interp, final_align.ALIGNMENT)
233
+ y2_interp = final_align.ALIGNMENT(x2_interp)
234
+
235
+ slope = final_align.ALIGNMENT(x2_interp,1) # Tan theta
236
+
237
+ norm = sqrt(1+slope*slope)
238
+ x_off = -slope/norm
239
+ y_off = 1/norm
240
+
241
+ # print(f"Point loc = [{point}] , Index match = {idx} , Point X on Initial = {ptx+initial_align.TOTALLENGTH*(idx-50)/8000} , Point Y = {y_ref} , Distance = {off} , Xoff = {slope}")
242
+
243
+ return (round(x2_interp+x_off*off,5),round(y2_interp+y_off*off,5))
244
+
245
+ @staticmethod
246
+ def modifyNXModel(initial_align:utils.Alignment,final_align:utils.Alignment,bElement:bool=True,bUpdateModel=True,bSync=True):
247
+ '''
248
+ Modifies CIVIL NX model as per new alignment.
249
+ Meant for **standalone** use
250
+ Use transformPoint in other cases
251
+
252
+ :param initial_align: Intitial alignment of the model
253
+ :type initial_align: utils.Alignment
254
+ :param final_align: Final alignment of the model
255
+ :type final_align: utils.Alignment
256
+ :param bElement: If beta angle of element should be modified
257
+ :type bElement: bool
258
+ '''
259
+ from midas_civil import Node,Element,MidasAPI,nodeByID
260
+ if bSync:
261
+ Node.sync()
262
+ if bElement: Element.sync()
263
+
264
+ ptsXY = [(nd.X , nd.Y , nd.ID ) for nd in Node.nodes]
265
+ dist_range = 0.25*initial_align.TOTALLENGTH # 0.1 * total length
266
+
267
+ dist_array = [dist_range*0.5-i*dist_range/50 for i in range(51)] # 50 divisions
268
+
269
+ finalXY = []
270
+ for pt in ptsXY:
271
+ ptx = pt[0]
272
+ pty = pt[1]
273
+
274
+ if ptx < initial_align.PT_X[0]:
275
+ x_onLine1 = initial_align.PT_X[0]
276
+ y_onLine1 = initial_align.PT_Y[0]
277
+ slope_onLine1 = initial_align.ALIGNMENT(x_onLine1,1)
278
+ angle_onLine1 = np.atan(slope_onLine1)
279
+
280
+ dist = hypot(ptx-x_onLine1,pty-y_onLine1)
281
+ tangent_angleO = np.atan((pty-y_onLine1)/(ptx-x_onLine1)) # Radian theta
282
+
283
+ x_onLine2 = final_align.PT_X[0]
284
+ y_onLine2 = final_align.PT_Y[0]
285
+ slope_onLine2 = final_align.ALIGNMENT(x_onLine2,1)
286
+ angle_onLine2 = np.atan(slope_onLine2)
287
+
288
+ totalAngle = angle_onLine2-angle_onLine1+tangent_angleO
289
+ x_off = np.cos(totalAngle)
290
+ y_off = np.sin(totalAngle)
291
+ angle = np.degrees(angle_onLine2-angle_onLine1)
292
+
293
+ finalXY.append([x_onLine2-x_off*dist,y_onLine2-y_off*dist,angle])
294
+
295
+ elif ptx > initial_align.PT_X[-1]:
296
+ x_onLine1 = initial_align.PT_X[-1]
297
+ y_onLine1 = initial_align.PT_Y[-1]
298
+ slope_onLine1 = initial_align.ALIGNMENT(x_onLine1,1)
299
+ angle_onLine1 = np.atan(slope_onLine1)
300
+
301
+ dist = hypot(ptx-x_onLine1,pty-y_onLine1)
302
+ off = np.sign(pty-y_onLine1)*dist
303
+ tangent_angleO = np.atan((pty-y_onLine1)/(ptx-x_onLine1)) # Radian theta
304
+
305
+ x_onLine2 = final_align.PT_X[-1]
306
+ y_onLine2 = final_align.PT_Y[-1]
307
+ slope_onLine2 = final_align.ALIGNMENT(x_onLine2,1)
308
+ angle_onLine2 = np.atan(slope_onLine2)
309
+
310
+ totalAngle = angle_onLine2-angle_onLine1+tangent_angleO
311
+
312
+ x_off = np.cos(totalAngle)
313
+ y_off = np.sin(totalAngle)
314
+ angle = np.degrees(angle_onLine2-angle_onLine1)
315
+
316
+ finalXY.append([x_onLine2+x_off*dist,y_onLine2+y_off*dist,angle])
317
+
318
+ else:
319
+ x_onLine1 = np.add(ptx,dist_array)
320
+ y_onLine1 = initial_align.ALIGNMENT(x_onLine1)
321
+
322
+ sqDist = np.sum((np.array([ptx,pty]) - np.array(list(zip(x_onLine1,y_onLine1)))) ** 2, axis=1)
323
+ min_index = np.argmin(sqDist)
324
+
325
+ x_ref = x_onLine1[min_index]
326
+ y_ref = y_onLine1[min_index]
327
+ dist_ref = sqrt(sqDist[min_index])
328
+
329
+ final_u = np.interp(x_ref,initial_align.X_FINE,initial_align.U_FINE)
330
+ off = np.sign(pty-y_ref)*dist_ref
331
+ x2_interp = np.interp(final_u,final_align.U_FINE,final_align.X_FINE)
332
+ y2_interp = final_align.ALIGNMENT(x2_interp)
333
+
334
+ slope = final_align.ALIGNMENT(x2_interp,1) # Tan theta
335
+ norm = sqrt(1+slope*slope)
336
+ x_off = -slope/norm
337
+ y_off = 1/norm
338
+
339
+ angle = np.degrees(np.atan(slope))
340
+
341
+ finalXY.append([x2_interp+x_off*off,y2_interp+y_off*off,angle])
342
+
343
+ for i,nod in enumerate(Node.nodes):
344
+ nod.X , nod.Y , nod.TEMP_ANG = float(finalXY[i][0]),float(finalXY[i][1]),float(finalXY[i][2])
345
+
346
+ if bUpdateModel: Node.create()
347
+
348
+ #---------------- BLOCK START FOR VERTICAL ELEMENT -------------------------------------------
349
+ if bElement:
350
+ editedElemsJS = {"Assign":{}}
351
+ for elm in Element.elements:
352
+ if elm.TYPE in ['BEAM','TRUSS','TENSTR','COMPTR']:
353
+ if elm.LOCALX[0]==0 and elm.LOCALX[1]==0 :
354
+ elm.ANGLE += np.sign(elm.LOCALX[2])*nodeByID(elm.NODE[0]).TEMP_ANG
355
+
356
+ editedElemsJS["Assign"][elm.ID] = {"ANGLE":elm.ANGLE}
357
+ if bUpdateModel: MidasAPI("PUT","/db/ELEM",editedElemsJS)
358
+
359
+
360
+
361
+ @staticmethod
362
+ def LineToPlate(nDiv:int = 10 , mSizeDiv:float = 0, bRigdLnk:bool=True , meshSize:float=0.5, elemList:list=None):
363
+ '''
364
+ Converts selected/entered line element to Shell elements
365
+ **nDiv** - No. of Division along the length of span
366
+ **mSizeDiv** - Division based on mesh size(in meter) along the length of span
367
+ division based on number -> **mSizeDiv = 0**
368
+ division based on meshSize(in meter) -> **nDiv = 0**
369
+ **bRigdLnk** - Whether to create Rigid links at the span ends
370
+ **meshSize** - Mesh size(in meter) of the plate elements
371
+ **elemList** - Element list which are to be converted . If None is passed, element are taken from selected elements in CIVIL NX
372
+
373
+ '''
374
+ from ._utilsFunc._line2plate import SS_create
375
+ SS_create(nDiv , mSizeDiv , bRigdLnk , meshSize ,elemList)
376
+
377
+ @staticmethod
378
+ def RC_Grillage(span_length = 20, width = 8, support:Literal['fix','pin']='fix', dia_no=2,start_loc = [0,0,0], girder_depth = 0, girder_width = 0, girder_no = 0,
379
+ web_thk = 0, slab_thk = 0, dia_depth = 0, dia_width = 0, overhang = 0, skew = 0, mat_E = 30_000_000):
380
+
381
+ """
382
+ RC Grillage Utility wizard
383
+ Use Model.create() to create model in CIVIL NX
384
+
385
+ Parameters
386
+ ----------
387
+ span_length : float, optional
388
+ Span length of the structure (default is 20).
389
+ width : float, optional
390
+ Overall deck width (default is 8).
391
+ support : {'fix', 'pin'}, optional
392
+ Support condition at the ends of the span.
393
+ 'fix' for fixed support, 'pin' for pinned support (default is 'fix').
394
+ dia_no : int, optional
395
+ No of diaphragms (default is 2).
396
+ start_loc : list, optional
397
+ Start location for Grillage placement (default is [0,0,0]).
398
+ girder_depth : float, optional
399
+ Depth of the girder section (default is 0).
400
+ girder_width : float, optional
401
+ Width of the girder section (default is 0).
402
+ girder_no : int, optional
403
+ Number of girders in the system (default is 0).
404
+ web_thk : float, optional
405
+ Thickness of the girder web (default is 0).
406
+ slab_thk : float, optional
407
+ Thickness of the deck slab (default is 0).
408
+ dia_depth : float, optional
409
+ Depth of the diaphragm (default is 0).
410
+ dia_width : float, optional
411
+ Width of the diaphragm (default is 0).
412
+ overhang : float, optional
413
+ Overhang length beyond the outer girder (default is 0).
414
+ skew : float, optional
415
+ Skew angle of the structure in degrees (default is 0).
416
+ mat_E : float, optional
417
+ Modulus of elasticity of the material (default is 30,000,000).
418
+ """
419
+ from midas_civil import Model,Material,Section,Offset,nodesInGroup,Element,Boundary,Load,elemsInGroup,Node
420
+ import math
421
+
422
+ Model.units()
423
+
424
+ cos_theta = math.cos(math.radians(skew))
425
+ tan_theta = math.tan(math.radians(skew))
426
+ nSec = len(Section.sect)
427
+ nMat = len(Material.mats)
428
+
429
+ # dia_no = 2
430
+ if span_length > 0 and width > 0:
431
+ #Data proofing and initial calcs:
432
+ if girder_depth == 0: girder_depth = max(1, round(span_length/20,3))
433
+ if girder_no == 0: girder_no = int(width/2)
434
+ if girder_width == 0: girder_width = width/girder_no
435
+ if slab_thk == 0: slab_thk = round(span_length/100,1)+0.05
436
+ if web_thk == 0: web_thk = round(girder_width/8,3)
437
+ if dia_depth == 0: dia_depth = girder_depth - slab_thk
438
+ if dia_width == 0: dia_width = web_thk
439
+ if dia_no <=1:
440
+ print("At least 2 diaphragms are required. No. of diaphragm is changed to 2.")
441
+ dia_no = 2
442
+ if dia_no >= 2:
443
+ overhang = max(overhang, dia_width/2)
444
+ cc_diaph = span_length / (dia_no - 1)
445
+ elem_len = round(cc_diaph / (round(cc_diaph,0)), 6)
446
+ if overhang > elem_len/2:
447
+ o_div = int(round(overhang/elem_len + 1, 0))
448
+ o_elem_len = overhang / o_div
449
+ if overhang > 0 and overhang <= elem_len/2:
450
+ o_div = 1
451
+ o_elem_len = overhang
452
+ if overhang == 0:
453
+ o_div = 0
454
+ o_elem_len = 0
455
+
456
+ Material.CONC.User('Concrete',mat_E,0.2,25,0,1.2e-5,id=nMat+1)
457
+ Material.CONC.User('Dummy',mat_E,0.2,0,0,1.2e-5,id=nMat+2)
458
+
459
+ if overhang > 0:
460
+ if o_div > 1:
461
+ Section.DBUSER(f"Overhang_Span{utils.__RC_Grillage_nSpan}",'SB',[slab_thk,o_elem_len*cos_theta],Offset('CT'),id=nSec+1)
462
+ Section.DBUSER(f"Start_Span{utils.__RC_Grillage_nSpan}",'SB',[slab_thk,o_elem_len*cos_theta/2],Offset('RT'),id=nSec+2)
463
+ Section.DBUSER(f"End_Span{utils.__RC_Grillage_nSpan}",'SB',[slab_thk,o_elem_len*cos_theta/2],Offset('LT'),id=nSec+3)
464
+ if dia_no >=2:
465
+ Section.DBUSER(f"Diap_Span{utils.__RC_Grillage_nSpan}",'SB',[dia_depth,dia_width*cos_theta],Offset('CT',UsrOffOpt=1,VOffOpt=1,VOffset=-slab_thk),id=nSec+4)
466
+ Section.DBUSER(f"T Beam_Span{utils.__RC_Grillage_nSpan}",'T',[girder_depth,girder_width,web_thk,slab_thk],Offset('CT'),id=nSec+5)
467
+ Section.DBUSER(f"Slab_Span{utils.__RC_Grillage_nSpan}",'SB',[slab_thk,elem_len*cos_theta],Offset('CT'),id=nSec+6)
468
+ Section.DBUSER(f"Slab_sup_st_Span{utils.__RC_Grillage_nSpan}",'SB',[slab_thk,(elem_len + o_elem_len)*cos_theta / 2],Offset('RT',UsrOffOpt=1,HOffOpt=1,HOffset=cos_theta*o_elem_len/2),id=nSec+7)
469
+ Section.DBUSER(f"Slab_sup_en_Span{utils.__RC_Grillage_nSpan}",'SB',[slab_thk,(elem_len + o_elem_len)*cos_theta / 2],Offset('LT',UsrOffOpt=1,HOffOpt=1,HOffset=cos_theta*o_elem_len/2),id=nSec+8)
470
+
471
+ Section.DBUSER("Dummy CB",'SB',[0.1,0.1],Offset('CC'),id=9)
472
+
473
+ offTrans = 0.5*width/girder_no
474
+ # Longitudinal
475
+ Element.Beam.SDL(np.add([0,0,0],start_loc),[1,0,0],span_length,int(span_length),nMat+2,9,group=f'CrashBarrier_R')
476
+ for i in range(girder_no):
477
+ Element.Beam.SDL(np.add([(2*i*offTrans+offTrans)*tan_theta,2*i*offTrans+offTrans,0],start_loc),[1,0,0],span_length,int(span_length),nMat+1,nSec+5,group=f'Girder {i+1} Span {utils.__RC_Grillage_nSpan}')
478
+ Boundary.Support(nodesInGroup(f'Girder {i+1} Span {utils.__RC_Grillage_nSpan}')[0],support,'Support')
479
+ Boundary.Support(nodesInGroup(f'Girder {i+1} Span {utils.__RC_Grillage_nSpan}')[-1],support,'Support')
480
+ Element.Beam.SDL(np.add([width*tan_theta,width,0],start_loc),[1,0,0],span_length,int(span_length),nMat+2,9,group=f'CrashBarrier_L')
481
+
482
+
483
+ spacing = span_length/int(span_length)
484
+ # Cross
485
+ Element.Beam.SE(start_loc,np.add([width*tan_theta,width,0],start_loc),2*girder_no,nMat+1,nSec+4,group='Diaphragm')
486
+ Element.Beam.SE(start_loc,np.add([width*tan_theta,width,0],start_loc),2*girder_no,nMat+2,nSec+7,group='CrossEnd')
487
+ for i in range(int(span_length)-1):
488
+ Element.Beam.SE(np.add([(i+1)*spacing,0,0],start_loc),np.add([(i+1)*spacing+width*tan_theta,width,0],start_loc),2*girder_no,nMat+2,nSec+6,group='Cross Slab')
489
+
490
+ Element.Beam.SE(np.add([span_length,0,0],start_loc),np.add([span_length+width*tan_theta,width,0],start_loc),2*girder_no,nMat+2,nSec+8,group='CrossEnd')
491
+ Element.Beam.SE(np.add([span_length,0,0],start_loc),np.add([span_length+width*tan_theta,width,0],start_loc),2*girder_no,nMat+1,nSec+4,group='Diaphragm')
492
+
493
+
494
+ # # Overhang
495
+ if o_elem_len!=0:
496
+ Element.Beam.SE(np.add([-o_elem_len,0,0],start_loc),np.add([width*tan_theta-o_elem_len,width,0],start_loc),2*girder_no,nMat+2,nSec+2)
497
+ Element.Beam.SE(np.add([span_length+o_elem_len,0,0],start_loc),np.add([width*tan_theta+span_length+o_elem_len,width,0],start_loc),2*girder_no,nMat+2,nSec+3)
498
+
499
+ Element.Beam.SDL(np.add([-o_elem_len,0,0],start_loc),[1,0,0],o_elem_len,1,nMat+2,9,group=f'CrashBarrier_R')
500
+ Element.Beam.SDL(np.add([span_length,0,0],start_loc),[1,0,0],o_elem_len,1,nMat+2,9,group=f'CrashBarrier_R')
501
+ for i in range(girder_no):
502
+ Element.Beam.SDL(np.add([-o_elem_len+(2*i*offTrans+offTrans)*tan_theta,2*i*offTrans+offTrans,0],start_loc),[1,0,0],o_elem_len,1,nMat+1,nSec+5,group=f'Girder {i+1}')
503
+ Element.Beam.SDL(np.add([span_length+(2*i*offTrans+offTrans)*tan_theta,2*i*offTrans+offTrans,0],start_loc),[1,0,0],o_elem_len,1,nMat+1,nSec+5,group=f'Girder {i+1}')
504
+ Element.Beam.SDL(np.add([-o_elem_len+width*tan_theta,width,0],start_loc),[1,0,0],o_elem_len,1,nMat+2,9,group=f'CrashBarrier_L')
505
+ Element.Beam.SDL(np.add([span_length+width*tan_theta,width,0],start_loc),[1,0,0],o_elem_len,1,nMat+2,9,group=f'CrashBarrier_L')
506
+
507
+ # extra diaphragm
508
+ span_idx = list(range(int(span_length+1)))
509
+
510
+ if dia_no > 2:
511
+ for i in range(dia_no-2):
512
+ Element.Beam.SE(np.add([span_idx[int((i+1)*span_length/(dia_no-1))]*spacing,0,0],start_loc),np.add([span_idx[int((i+1)*span_length/(dia_no-1))]*spacing+width*tan_theta,width,0],start_loc),2*girder_no,nMat+2,nSec+4,group='Diaphragm')
513
+
514
+
515
+
516
+ Load.SW('Self Weight',load_group='Self Weight')
517
+
518
+ # WCloading = -1.9
519
+ WCloading = -22 * 0.075 * cos_theta
520
+ Load.Beam(elemsInGroup('Cross Slab'),'Wearing Course','Wearing Course',WCloading)
521
+ Load.Beam(elemsInGroup('CrossEnd'),'Wearing Course','Wearing Course',WCloading*0.5)
522
+
523
+
524
+ CBloading = -22 * 0.5
525
+ Load.Beam(elemsInGroup(['CrashBarrier_R','CrashBarrier_L']),'Crash Barrier','Crash Barrier',CBloading)
526
+
527
+ utils.__RC_Grillage_nSpan+=1
528
+ #---------------------------------------------------------------------------------------
529
+ # Model.create()
File without changes