def _getNodesandElements(): """ This function returns the nodes and elments for an active model, in a standardized format. The OpenSees model must be active in order for the function to work. Returns ------- nodes : 2dArray An array of all nodes in the model. Returns nodes in the shape: [Nodes, 3] in 2d and [Nodes, 4] For each node the information is tored as follows: [NodeID, x, y] or [NodeID, x, y, z] elements : Array An list of all elements in. Each entry in the list is it's own' [element1, element2,...], element1 = [element#, node1, node2,...] """ # Get nodes and elements nodeList = ops.getNodeTags() eleList = ops.getEleTags() # Check Number of dimensions and intialize variables ndm = len(ops.nodeCoord(nodeList[0])) Nnodes = len(nodeList) nodes = np.zeros([Nnodes, ndm + 1]) # Get Node list for ii, node in enumerate(nodeList): nodes[ii, 0] = node nodes[ii, 1:] = ops.nodeCoord(nodeList[ii]) Nele = len(eleList) elements = [None] * Nele # Generate the element list by looping through all emenemts for ii, ele in enumerate(eleList): tempNodes = ops.eleNodes(ele) tempNnodes = len(tempNodes) tempEle = np.zeros(tempNnodes + 1) tempEle[0] = int(ele) tempEle[1:] = tempNodes elements[ii] = tempEle return nodes, elements
def get_node_coords_and_disp(): node_coords = dict() node_disp = dict() node_tags = ops.getNodeTags() for i in node_tags: node_coords[i] = ops.nodeCoord(i) node_disp[i] = ops.nodeDisp(i) return (node_coords,node_disp)
def drawModel(): plt.figure() etags = ops.getEleTags() if etags is None: return if isinstance(etags, int): etags = [etags] for e in etags: elenodes = ops.eleNodes(e) for i in range(0, len(elenodes)): [xi, yi] = ops.nodeCoord(elenodes[i - 1]) [xj, yj] = ops.nodeCoord(elenodes[i]) plt.plot([xi, xj], [yi, yj], 'k') plt.show()
def plot_deformed_shape(self, xlim, ylim, scale=1, arrow_len=10, arrow_width=2, save=''): ''' Plot deformed shape of the model. Args: xlim: A list of left and right limits of the x axis. ylim: A list of bottom and top limits of the y axis. scale: A float scale of the displayed deformations (default=1). arrow_len: An integer length of the load arrows displayed (default=10). arrow_width: An integer head width of the load arrows displayed (default=2). save: A string indicating save path for the figure (default='', meaning that the figure will NOT be saved by default). ''' fig, ax = plt.subplots(dpi=75) ax.set_axis_off() ax.grid(True, which='both', alpha=0.5) ax.axhline(y=0, color='k', lw=1) opsv.plot_defo(scale, fmt_undefo='k-', fmt_interp='k--') ax.axis('equal') ax.set(xlim=xlim, ylim=ylim) node_list = ops.getNodeTags() node_disp = np.array([ops.nodeDisp(n) for n in node_list]) node_coord = np.array([ops.nodeCoord(n) for n in node_list]) new_coord = node_disp[:, :-1] * scale + node_coord for node, Px, Py, M in self.loads: c = new_coord[node_list.index(node), :] ax.annotate('', xytext=(c[0] + abs(Px) * arrow_len, c[1] + abs(Py) * arrow_len), xy=(c[0], c[1]), arrowprops=dict(arrowstyle=f'-|>, \ head_width={arrow_width/5},\ head_length={arrow_width/2}', lw=arrow_width, fc='orangered', ec='orangered')) if save: fig.savefig(save, transparent=True) plt.show()
def DiscretizeMember(ndI,ndJ,numEle,eleType,integrTag,transfTag,nodeTag,eleTag): nodeList = [] eleList = [] if numEle <= 1: ops.element(eleType,eleTag,ndI,ndJ,transfTag,integrTag) eleList.append(eleTag) return eleList,nodeList Xi = ops.nodeCoord(ndI,'X') Yi = ops.nodeCoord(ndI,'Y') Xj = ops.nodeCoord(ndJ,'X') Yj = ops.nodeCoord(ndJ,'Y') dX = (Xj-Xi)/numEle dY = (Yj-Yi)/numEle threeD = True if len(ops.nodeCoord(ndI)) < 3: threeD = False else: Zi = ops.nodeCoord(ndI,'Z') Zj = ops.nodeCoord(ndJ,'Z') dZ = (Zj-Zi)/numEle nodes = [None]*(numEle+1) nodes[0] = ndI nodes[numEle] = ndJ for i in range(1,numEle): if threeD: ops.node(nodeTag,Xi+i*dX,Yi+i*dY,Zi+i*dZ) else: ops.node(nodeTag,Xi+i*dX,Yi+i*dY) nodes[i] = nodeTag nodeList.append(nodeTag) nodeTag = nodeTag+1 #print(eleType,eleTag,ndI,nodes[1],transfTag,integrTag) ops.element(eleType,eleTag,ndI,nodes[1],transfTag,integrTag) eleList.append(eleTag) eleTag = eleTag + 1 for i in range(1,numEle-1): ops.element(eleType,eleTag,nodes[i],nodes[i+1],transfTag,integrTag) eleList.append(eleTag) eleTag = eleTag + 1 ops.element(eleType,eleTag,nodes[numEle-1],ndJ,transfTag,integrTag) eleList.append(eleTag) eleTag = eleTag + 1 return eleList,nodeList
def _getModeShapeData(modeNumber): # Get nodes and elements nodeList = ops.getNodeTags() # Check Number of dimensions and intialize variables ndm = len(ops.nodeCoord(nodeList[0])) Nnodes = len(nodeList) nodes_modeshape = np.zeros([Nnodes, ndm + 1]) for ii, node in enumerate(nodeList): nodes_modeshape[ii, 0] = node tempData = ops.nodeEigenvector(nodeList[ii], modeNumber) nodes_modeshape[ii, 1:] = tempData[0:ndm] return nodes_modeshape
def get_multi_pile_m( pile_layout, cap_edge=0, cap_thickness=2, pile_z0=-2.5, pile_z1=-30, pile_d=2, m0=7500000, top_f=0.0, top_h=0.0, top_m=0.0 ): if cap_edge == 0: if pile_d <= 1: cap_edge = max(0.25, 0.5 * pile_d) else: cap_edge = max(0.5, 0.3 * pile_d) cap_w = max(pile_layout[0]) - min(pile_layout[0]) + pile_d + cap_edge * 2 cap_l = max(pile_layout[1]) - min(pile_layout[1]) + pile_d + cap_edge * 2 top_f += cap_w * cap_l * cap_thickness * 26e3 # 承台自重 top_f += (cap_w * cap_l) * (-pile_z0 - cap_thickness) * 15e3 # 盖梁重量 pile_rows = len(pile_layout[1]) # 桩排数 top_f /= pile_rows # 桩顶力分配 top_h /= pile_rows # 桩顶水平力分配 top_m /= pile_rows # 桩顶弯矩分配 cap_i = cap_l * cap_thickness ** 3 / 12 / pile_rows # 承台横向刚度 pile_h = pile_z0 - pile_z1 pile_a = np.pi * (pile_d / 2) ** 2 pile_i = np.pi * pile_d ** 4 / 64 pile_b1 = 0.9 * (1.5 + 0.5 / pile_d) * 1 * pile_d # 建立模型 ops.wipe() ops.model('basic', '-ndm', 2, '-ndf', 3) # 建立节点 cap_bot = pile_z0 # ops.node(1, 0, cap_top) # 承台竖向节点 if 0 not in pile_layout[0]: ops.node(2, 0, cap_bot) # 建立桩基节点 node_z = np.linspace(pile_z0, pile_z1, elem_num + 1) for i, j in enumerate(pile_layout[0]): node_start = 100 + i * 300 for m, n in enumerate(node_z): ops.node(node_start + m + 1, j, n) ops.node(node_start + m + 151, j, n) nodes = {} for i in ops.getNodeTags(): nodes[i] = ops.nodeCoord(i) # 建立约束 for i, j in enumerate(pile_layout[0]): node_start = 100 + i * 300 for m, n in enumerate(node_z): ops.fix(node_start + m + 151, 1, 1, 1) if n == node_z[-1]: ops.fix(node_start + m + 1, 1, 1, 1) # 建立材料 for i in range(len(node_z)): pile_depth = i * (pile_h / elem_num) pile_depth_nominal = 10 if pile_depth <= 10 else pile_depth soil_k = m0 * pile_depth_nominal * pile_b1 * (pile_h / elem_num) if i == 0: ops.uniaxialMaterial('Elastic', 1 + i, soil_k / 2) continue ops.uniaxialMaterial('Elastic', 1 + i, soil_k) # 装配 ops.geomTransf('Linear', 1) # 建立单元 if len(pile_layout[0]) > 1: # 承台横向单元 cap_nodes = [] for i in nodes: if nodes[i][1] == cap_bot: if len(cap_nodes) == 0: cap_nodes.append(i) elif nodes[i][0] != nodes[cap_nodes[-1]][0]: cap_nodes.append(i) cap_nodes = sorted(cap_nodes, key=lambda x: nodes[x][0]) for i, j in enumerate(cap_nodes[:-1]): ops.element('elasticBeamColumn', 10 + i, j, cap_nodes[i+1], cap_l * cap_thickness, 3e10, cap_i, 1) pile_elem = [] for i, j in enumerate(pile_layout[0]): # 桩基单元 node_start = 100 + i * 300 pile_elem_i = [] for m, n in enumerate(node_z): if n != pile_z1: ops.element('elasticBeamColumn', node_start + m + 1, node_start + m + 1, node_start + m + 2, pile_a, 3e10, pile_i, 1) pile_elem_i.append(node_start + m + 1) ops.element('zeroLength', node_start + m + 151, node_start + m + 151, node_start + m + 1, '-mat', 1 + m, '-dir', 1) pile_elem.append(pile_elem_i) ops.timeSeries('Linear', 1) ops.pattern('Plain', 1, 1) for i in nodes: if nodes[i] == [0, pile_z0]: ops.load(i, -top_h, -top_f, top_m) # 加载 ops.system('BandGeneral') ops.numberer('Plain') ops.constraints('Plain') ops.integrator('LoadControl', 0.01) ops.test('EnergyIncr', 1e-6, 200) ops.algorithm('Newton') ops.analysis('Static') ops.analyze(100) node_disp = {} for i in ops.getNodeTags(): node_disp[i] = [j * 1000 for j in ops.nodeDisp(i)] elem_m = {} for i in pile_elem: for j in i: elem_m[j] = [k / 1000 for k in ops.eleForce(j)] plt.figure() for i, j in enumerate(pile_elem): plt.subplot(f'1{len(pile_elem)}{i+1}') if i == 0: plt.ylabel('Pile Depth(m)') node_disp_x = [] for m, n in enumerate(j): node_1 = ops.eleNodes(n)[0] if m == 0: plt.plot([0, node_disp[node_1][0]], [nodes[node_1][1], nodes[node_1][1]], linewidth=1.5, color='grey') else: plt.plot([0, node_disp[node_1][0]], [nodes[node_1][1], nodes[node_1][1]], linewidth=0.7, color='grey') node_disp_x.append(node_disp[node_1][0]) for m, n in enumerate(j): node_1 = ops.eleNodes(n)[0] if abs(node_disp[node_1][0]) == max([abs(i) for i in node_disp_x]): side = 1 if node_disp[node_1][0] > 0 else -1 plt.annotate(f'{node_disp[node_1][0]:.1f} mm', xy=(node_disp[node_1][0], nodes[node_1][1]), xytext=(0.4 + 0.1 * side, 0.5), textcoords='axes fraction', bbox=dict(boxstyle="round", fc="0.8"), arrowprops=dict(arrowstyle='->', connectionstyle=f"arc3,rad={side * 0.3}")) break plt.plot([0, 0], [node_z[0], node_z[-1]], linewidth=1.5, color='dimgray') plt.plot(node_disp_x, node_z[:-1], linewidth=1.5, color='midnightblue') plt.xlabel(f'Displacement_{i+1} (mm)') plt.show() plt.figure() for i, j in enumerate(pile_elem): plt.subplot(f'1{len(pile_elem)}{i + 1}') if i == 0: plt.ylabel('Pile Depth(m)') elem_mi = [] for m, n in enumerate(j): node_1 = ops.eleNodes(n)[0] if m == 0: plt.plot([0, elem_m[n][2]], [nodes[node_1][1], nodes[node_1][1]], linewidth=1.5, color='grey') else: plt.plot([0, elem_m[n][2]], [nodes[node_1][1], nodes[node_1][1]], linewidth=0.7, color='grey') elem_mi.append(elem_m[n][2]) for m, n in enumerate(j): node_1 = ops.eleNodes(n)[0] if abs(elem_m[n][2]) == max([abs(i) for i in elem_mi]): side = 1 if elem_m[n][2] > 0 else -1 plt.annotate(f'{elem_m[n][2]:.1f} kN.m', xy=(elem_m[n][2], nodes[node_1][1]), xytext=(0.4 + 0.1 * side, 0.5), textcoords='axes fraction', bbox=dict(boxstyle="round", fc="0.8"), arrowprops=dict(arrowstyle='->', connectionstyle=f"arc3,rad={side * 0.3}")) break plt.plot([0, 0], [node_z[0], node_z[-1]], linewidth=1.5, color='dimgray') plt.plot(elem_mi, node_z[:-1], linewidth=1.5, color='brown') plt.xlabel(f'Moment_{i + 1} (kN.m)') plt.show() return pile_elem, elem_m
def get_node_coords(): node_coords = dict() node_tags = ops.getNodeTags() for i in node_tags: node_coords[i] = ops.nodeCoord(i) return node_coords
ops.fix(2, 1, 1) ops.fix(3, 1, 1) ops.element('Truss', 2, 2, 4, 5.0, 1) ops.element('Truss', 3, 3, 4, 5.0, 1) ops.constraints('Transformation') ops.numberer('ParallelPlain') ops.system('Mumps') ops.test('NormDispIncr', 1e-6, 6, 2) ops.algorithm('Newton') ops.integrator('LoadControl', 0.1) ops.analysis('Static') ops.analyze(10) print('Node 4: ', [ops.nodeCoord(4), ops.nodeDisp(4)]) ops.loadConst('-time', 0.0) if pid == 0: ops.pattern('Plain', 2, 1) ops.load(4, 1.0, 0.0) ops.domainChange() ops.integrator('ParallelDisplacementControl', 4, 1, 0.1) ops.analyze(10) print('Node 4: ', [ops.nodeCoord(4), ops.nodeDisp(4)]) ops.stop()
def run_analysis(GM_dt, GM_npts, TS_List, EDP_specs, model_params): """ Run dynamic analysis for a time history and return a dictionary of envelope EDPs. Assumes that length is measured in inches and acceleration in in/s2 Parameters ---------- GM_dt: float Time step of time series GM_npts: float Number of points in time series TS_List: float 1x2 list where first component is a list of accelerations in the X direction, the second component is a list of accelerations in the Y direction. EDP_specs: dict """ stories = model_params["NumberOfStories"] node_tags = list(range(stories + 1)) height = ops.nodeCoord(node_tags[-1], 3) - ops.nodeCoord(node_tags[0], 3) # define parameters for dynamic analysis dt = GM_dt # time increment for analysis GMX = TS_List[0] # list of GM accelerations in X direction GMY = TS_List[1] # list of GM accelerations in Y direction driftLimit = 0.20 # interstory drift limit indicating collapse tol = 1.e-08 # tolerance criteria to check for convergence maxiter = 30 # max number of iterations to check subSteps = 2 # number of subdivisions in cases of ill convergence # pad shorter record with zeros (free vibration) such that two horizontal records are the same length nsteps = max(len(GMX), len(GMY)) for GM in [GMX, GMY]: if len(GM) < nsteps: diff = nsteps - len(GM) GM.extend(np.zeros(diff)) # initialize dictionary of envelope EDPs envelopeDict = {} for edp in EDP_specs: envelopeDict[edp] = {} for loc in EDP_specs[edp]: envelopeDict[edp][loc] = np.zeros(len( EDP_specs[edp][loc])).tolist() #print(envelopeDict) # initialize dictionary of time history EDPs time_analysis = np.zeros(nsteps * 5) acc_history = {} for floor in range(stories + 1): acc_history.update( {floor: { 1: time_analysis.copy(), 2: time_analysis.copy() }}) ops.wipeAnalysis() ops.constraints( 'Transformation' ) # handles boundary conditions based on transformation equation method ops.numberer('RCM') # renumber dof's to minimize band-width (optimization) ops.system('UmfPack' ) # constructs sparse system of equations using UmfPack solver ops.test( 'NormDispIncr', tol, maxiter ) # tests for convergence using norm of left-hand side of matrix equation ops.algorithm( 'NewtonLineSearch' ) # use Newton's solution algorithm: updates tangent stiffness at every iteration ops.integrator( 'Newmark', 0.5, 0.25) # Newmark average acceleration method for numerical integration ops.analysis('Transient') # define type of analysis: time-dependent # initialize variables maxDiv = 1024 minDiv = subSteps step = 0 ok = 0 breaker = 0 count = 0 while step < nsteps and ok == 0 and breaker == 0: step = step + 1 # take 1 step ok = 2 div = minDiv length = maxDiv while div <= maxDiv and length > 0 and breaker == 0: stepSize = dt / div # perform analysis for one increment; will return 0 if no convergence issues ok = ops.analyze(1, stepSize) if ok == 0: count = count + 1 length = length - maxDiv / div floor = 1 while floor <= stories: # check if drift limits are satisfied # check X direction drifts (direction 1) drift_x = abs( ops.nodeDisp(node_tags[1], 1) - ops.nodeDisp(node_tags[0], 1)) / height if drift_x >= driftLimit: breaker = 1 # check Y direction drifts (direction 2) drift_y = abs( ops.nodeDisp(node_tags[1], 2) - ops.nodeDisp(node_tags[0], 2)) / height if drift_y >= driftLimit: breaker = 1 # save parameter values in recording dictionaries at every step time_analysis[count] = time_analysis[count - 1] + stepSize envelopeDict['PID'][floor][0] = max( drift_x, envelopeDict['PID'][floor][0]) envelopeDict['PID'][floor][1] = max( drift_y, envelopeDict['PID'][floor][1]) floor = floor + 1 for floor in range(stories + 1): for dof in [1, 2]: acc_history[floor][dof][count] = ops.nodeAccel( node_tags[floor], dof) else: div = div * 2 print("Number of increments increased to ", str(div)) # end analysis once drift limit has been reached if breaker == 1: ok = 1 print("Collapse drift has been reached") print("Number of analysis steps completed: {}".format(count)) # remove extra zeros from the end of the time history time_analysis = time_analysis[1:count + 1] # generate time array from recording time_record = np.linspace(0, nsteps * dt, num=nsteps, endpoint=False) # remove extra zeros from accel time history, add GM to obtain absolute a # acceleration, and record envelope value GMX_interp = np.interp(time_analysis, time_record, GMX) GMY_interp = np.interp(time_analysis, time_record, GMY) for floor in range(0, stories + 1): # X direction envelopeDict['PFA'][floor][0] = max( abs(np.asarray(acc_history[floor][1][1:count + 1]) + GMX_interp)) # Y direction envelopeDict['PFA'][floor][1] = max( abs(np.asarray(acc_history[floor][2][1:count + 1]) + GMY_interp)) return envelopeDict
def createOutputDatabase(self, Nmodes=0, deltaT=0.0, recorders=[]): """ This function creates a directory to save all the output data. Command: createODB("ModelName",<"LoadCase Name">, <Nmodes=Nmodes(int)>, <recorders=*recorder(list)>) ModelName : (string) Name of the model. The main output folder will be named "ModelName_ODB" in the current directory. LoadCase Name: (string), Optional. Name of the load case forder to be created inside the ModelName_ODB folder. If not provided, no load case data will be read. Nmodes : (int) Optional key argument to save modeshape data. Default is 0, no modeshape data is saved. deltaT : (float) Optional time interval for recording. will record when next step is deltaT greater than last recorder step. (default: records at every time step) recorders : (string) A list of additional quantities a users would like to record in the output database. The arguments for these additional inputs match the standard OpenSees arguments to avoid any confusion. 'localForce','basicDeformation', 'plasticDeformation','stresses','strains' The recorders for node displacement and reactions are saved by default to help plot the deformed shape. Example: createODB(TwoSpanBridge, Pushover, Nmodes=3, recorders=['stresses', 'strains']) Future: The integrationPoints output works only for nonlinear beam column elements. If a model has a combination of elastic and nonlienar elements, we need to create a method distinguish. """ ODBdir = self.ODBdir # ODB Dir name if not os.path.exists(ODBdir): os.makedirs(ODBdir) nodeList = op.getNodeTags() eleList = op.getEleTags() dofList = [int(ii + 1) for ii in range(len(op.nodeCoord(nodeList[0])))] # Save node and element data in the main Output folder self.saveNodesandElements() ######################### ## Create mode shape dir ######################### if Nmodes > 0: ModeShapeDir = os.path.join(ODBdir, "ModeShapes") if not os.path.exists(ModeShapeDir): os.makedirs(ModeShapeDir) ## Run eigen analysis internally and get information to print Tarray = np.zeros([1, Nmodes]) # To save all the periods of vibration op.wipeAnalysis() eigenVal = op.eigen(Nmodes + 1) for mm in range(1, Nmodes + 1): Tarray[0, mm - 1] = 4 * asin(1.0) / (eigenVal[mm - 1])**0.5 modeTFile = os.path.join(ModeShapeDir, "ModalPeriods.out") np.savetxt(modeTFile, Tarray, delimiter=self.delim, fmt=self.fmt) ### Save mode shape data for ii in range(1, Nmodes + 1): self.saveModeShapeData(ii) op.wipeAnalysis() LoadCaseDir = self.LoadCaseDir if not os.path.exists(LoadCaseDir): os.makedirs(LoadCaseDir) NodeDispFile = os.path.join(LoadCaseDir, "NodeDisp_All.out") EleForceFile = os.path.join(LoadCaseDir, "EleForce_All.out") ReactionFile = os.path.join(LoadCaseDir, "Reaction_All.out") EleStressFile = os.path.join(LoadCaseDir, "EleStress_All.out") EleStrainFile = os.path.join(LoadCaseDir, "EleStrain_All.out") EleBasicDefFile = os.path.join(LoadCaseDir, "EleBasicDef_All.out") ElePlasticDefFile = os.path.join(LoadCaseDir, "ElePlasticDef_All.out") # EleIntPointsFile = os.path.join(LoadCaseDir,"EleIntPoints_All.out") # Save recorders in the ODB folder op.recorder('Node', '-file', NodeDispFile, '-time', '-dT', deltaT, '-node', *nodeList, '-dof', *dofList, 'disp') op.recorder('Node', '-file', ReactionFile, '-time', '-dT', deltaT, '-node', *nodeList, '-dof', *dofList, 'reaction') if 'localForce' in recorders: op.recorder('Element', '-file', EleForceFile, '-time', '-dT', deltaT, '-ele', *eleList, '-dof', *dofList, 'localForce') if 'basicDeformation' in recorders: op.recorder('Element', '-file', EleBasicDefFile, '-time', '-dT', deltaT, '-ele', *eleList, '-dof', *dofList, 'basicDeformation') if 'plasticDeformation' in recorders: op.recorder('Element', '-file', ElePlasticDefFile, '-time', '-dT', deltaT, '-ele', *eleList, '-dof', *dofList, 'plasticDeformation') if 'stresses' in recorders: op.recorder('Element', '-file', EleStressFile, '-time', '-dT', deltaT, '-ele', *eleList, 'stresses') if 'strains' in recorders: op.recorder('Element', '-file', EleStrainFile, '-time', '-dT', deltaT, '-ele', *eleList, 'strains')
storyHeight = storyHeights[k] zLoc += storyHeight # add column element ops.geomTransf(coordTransf, 1, 1, 0, 0) ops.geomTransf(coordTransf, 2, 0, 0, 1) eleTag = 1 nodeTag1 = 1 for k in range(0, numFloor): for i in range(0, numBayX + 1): for j in range(0, numBayY + 1): nodeTag2 = nodeTag1 + (numBayX + 1) * (numBayY + 1) iNode = ops.nodeCoord(nodeTag1) jNode = ops.nodeCoord(nodeTag2) ops.element('elasticBeamColumn', eleTag, nodeTag1, nodeTag2, 50., E, 1000., 1000., 2150., 2150., 1, '-mass', M, massType) eleTag += 1 nodeTag1 += 1 nodeTag1 = 1 + (numBayX + 1) * (numBayY + 1) #add beam elements for j in range(1, numFloor + 1): for i in range(0, numBayX): for k in range(0, numBayY + 1): nodeTag2 = nodeTag1 + (numBayY + 1) iNode = ops.nodeCoord(nodeTag1) jNode = ops.nodeCoord(nodeTag2) ops.element('elasticBeamColumn', eleTag, nodeTag1, nodeTag2, 50.,