Source code for Pabq.modelers.rhs_t_joint

from material import *
from part import *
from section import *
from interaction import *
from step import *
from job import *
from mesh import *


import logging
import sys
import os
import Pabq.common_tools.abq_tools as at
import Pabq.common_tools.factorial_exec as fe
import Pabq.common_tools.steel_design as sd
import Pabq.common_tools.design_of_joints as dj
import numpy as np
import copy

logging.basicConfig(
    level=logging.DEBUG,
    filename='fruits/logging.log',
    filemode='w',
    #format='%(name)s - %(levelname)s - %(message)s'
)


[docs]class TjointRHS(at.GeneralModel):
[docs] def __init__( self, column=None, beam=None, l_column=None, l_lever=None, a_weld=None, moment_percent=None, size_factor=None, step_props=None, imperfections=None, mdl_id=None, targets=None, s_gauges=None, node20=None, measure_weld=None, **kargs ): # Default values if moment_percent is None: self.m_prc = 0.5 else: self.m_prc = moment_percent / 100. if imperfections is None: self.imperfections = False else: self.imperfections = imperfections if node20 is None: self.node20 = False else: self.node20 = True if measure_weld is None: self.measure_weld = False else: self.measure_weld = True self.targets = targets self.s_gauges = s_gauges # Report info on the current model logging.info( "\n============\nC%sB%02dM%02d\n============" % ( "xx", 10*beam[0]/column[0], moment_percent ) ) logging.info("Current model input values") logging.info("Column: %s" % (column,)) logging.info("Beam: %s" % (beam,)) logging.info("Moment percent: %.3f" % moment_percent) # Give a default basename for the model, if not specified if mdl_id is None: mdl_id = 'rhs_t_joint' # Inherit properties and default values from the general_model class super(TjointRHS, self).__init__(mdl_id=mdl_id, **kargs) # Add model specific names to the name dictionary self.names.update( { "bpart": "beam_part", "cpart": "column_part", "cshellpart": "column_shell_part", "bshellpart": "beam_shell_part", "mat_col_opp": "S355_column_opp", "mat_col_adj": "S355_column_adj", "mat_beam_opp": "S355_beam_opp", "mat_beam_adj": "S355_beam_adj", "mat_cf_col": "S355_cf_column", "mat_cf_beam": "S355_cf_beam", "col_solid_section_opp": "column_solid_opp", "col_solid_section_adj": "column_solid_adj", "col_cf_solid_section": "column_cf_solid", "beam_solid_section_opp": "beam_solid_opp", "beam_solid_section_adj": "beam_solid_adj", "beam_cf_solid_section": "beam_cf_solid", "init_load_step": "init_load_step", "b_load_step": "b_load_step", "c_load_step": "c_load_step", "b_instance": "beam_inst", "c_instance": "column_inst", "bs_inst": "beam_shell_inst", "cs_low_inst": "column_low_shell_inst", "cs_high_inst": "column_high_shell_inst", "velo_bc": "velo_bc" } ) self.set_names = { "test_targer_rp": "test_target_r", "b_front_face": "b_front_face_set", "c_low_face": "c_low_face_set", "c_high_face": "c_high_face_set", "y_symm": "y_symm_set", "b_rp": "b_rp_set", "c_low_rp": "c_low_rp_set", "c_high_rp": "c_high_rp_set" } # By default, the weld size is equal to the beam thickness. if a_weld is None: a_weld = beam[2] # Gather cross-section characteristics self.h_column = float(column[0]) self.b_column = float(column[1]) self.t_column = float(column[2]) self.h_beam = float(beam[0]) self.b_beam = float(beam[1]) self.t_beam = float(beam[2]) self.l_column = l_column self.l_lever = l_lever # Beta self.beta = self.b_beam/self.b_column # General weld props self.a_weld = a_weld # Weld foot size self.w_foot = self.a_weld * sqrt(2) # Gap between the column and the beam self.b_gap = self.w_foot/10. # General column properties ( self.ch_flat, self.cb_flat, self.r_o_column, self.r_i_column ) = sd.CsRHS.calc_flat_width_radius( self.h_column, self.b_column, self.t_column ) self.r_m_column = self.r_o_column - self.t_column/2. # Half-symmetric self.cht2 = self.h_column/2. self.cbt2 = self.b_column/2. self.chf2 = self.ch_flat/2. self.cbf2 = self.cb_flat/2. self.b_start = self.cht2 + self.b_gap # General beam properties ( self.bh_flat, self.bb_flat, self.r_o_beam, self.r_i_beam ) = sd.CsRHS.calc_flat_width_radius( self.h_beam, self.b_beam, self.t_beam ) self.r_m_beam = self.r_o_beam - self.t_beam/2. # Half-symmetric self.bht2 = self.h_beam/2. self.bbt2 = self.b_beam/2. self.bhf2 = self.bh_flat/2. self.bbf2 = self.bb_flat/2. logging.info( "Flat portions of column flange and web: %.3f, %.3f" % ( self.cb_flat, self.ch_flat ) ) # Set material characteristics # The material dictionary is constructed so that it fits Abaqus. self.mat_dict_col_opp = self.set_material(mdl_id+"_column_opp") self.mat_dict_col_adj = self.set_material(mdl_id+"_column_adj") self.mat_dict_beam_opp = self.set_material(mdl_id+"_beam_opp") self.mat_dict_beam_adj = self.set_material(mdl_id+"_beam_adj") # Create new material dictionaries for material for the cold formed # regions of the profiles by increasing the parent material by 22%. # This is based on literature (Hancock) temp_stresses = np.array( self.mat_dict_col_opp['plasticity']['table'] ) temp_stresses[:, 0] = temp_stresses[:, 0]*1.22 temp_stresses = tuple(tuple(i) for i in temp_stresses) self.material_dict_cf_column = copy.deepcopy( self.mat_dict_col_opp ) self.material_dict_cf_column['plasticity']['table'] = temp_stresses temp_stresses = np.array( self.mat_dict_beam_opp['plasticity']['table'] ) temp_stresses[:, 0] = temp_stresses[:, 0]*1.22 temp_stresses = tuple(tuple(i) for i in temp_stresses) self.material_dict_cf_beam = copy.deepcopy( self.mat_dict_col_opp ) self.material_dict_cf_beam['plasticity']['table'] = temp_stresses # Grab a couple of useful values to shorter variables. self.elasticity = self.mat_dict_col_opp["elasticity"]["table"][0][0] self.poisson = self.mat_dict_col_opp["elasticity"]["table"][0][1] self.density = self.mat_dict_col_opp["density"]["table"][0][0] logging.info("Poisson ratio: %.3f" % self.poisson) # Apart from the material description for Abaqus, I have my own # material class which is used in steel_design module. This object must # also be defined because it will be needed throughout the resistance # calculations. # First, find the average yield stress for the entire cross section, # based on the notion that the face opposite to the weld has an # elevated yield stress than the other faces. fy_col_avg = ( ( 3*self.mat_dict_col_adj["f_yield"] )+( self.mat_dict_col_opp["f_yield"] ) )/4 fy_beam_avg = ( ( 3*self.mat_dict_beam_adj["f_yield"] )+( self.mat_dict_beam_opp["f_yield"] ) )/4 material_sd_col = sd.Material( 210000., 0.3, fy_col_avg ) # The ultimate stress is taken as the last value on the stress-strain # curve from the coupon test. material_sd_col.f_u_nominal = self.mat_dict_col_adj[ "plasticity" ]["table"][-1][0] material_sd_beam = sd.Material( 210000., 0.3, self.mat_dict_beam_opp["f_yield"] ) material_sd_beam.f_u_nominal = self.mat_dict_beam_adj[ "plasticity" ]["table"][-1][0] # Create an T-joint object for the model. This object performs all the # calculations, geometrical-engineering-Euroocde, from which we derive # the loads for the model and other characteristics. logging.info( ( "\nBeginning of design resistance and design load" " calculations\n-+-+-+-+-+-+-+-+-+-+-+-+-" ) ) nd_width_col = self.ch_flat/(self.t_column*material_sd_col.epsilon) nd_width_beam = self.bh_flat/(self.t_beam*material_sd_beam.epsilon) print( nd_width_col, self.b_column, nd_width_beam, self.beta, self.m_prc, 0., material_sd_col.f_y_nominal, material_sd_col.f_u_nominal, material_sd_beam.f_y_nominal, material_sd_beam.f_u_nominal, self.l_column ) self.joint_props = dj.TjointRHS.from_slend_beta_m_prc_jj( nd_width_col, self.b_column, nd_width_beam, self.beta, self.m_prc, n_prc_beam=0., material_col=material_sd_col, material_beam=material_sd_beam, length_c=self.l_column, ) logging.info( ( "\nEnd of design resistance and design load" " calculations\n-+-+-+-+-+-+-+-+-+-+-+-+-+-\n" ) ) # Calculate the beam concentrated load according to the lever arm and # the moment utilisation self.beam_load = self.joint_props.struct_props[ "m_ip_rd_draft" ]/self.l_lever logging.info("Concentrated load: %.3f" % (2*self.beam_load)) # Calculate the point load applied on the column head. The full plastic # resistance of the column is used, instead of the interaction # resistance, to make sure that we reach failure load. On top, it is # also increased by a factor of 1.2. self.column_load = 1.2*self.joint_props.cs_props["column"].area*\ self.joint_props.material["column"].f_y_nominal logging.info("Load applied directly on the column head: %.3f" % self.column_load) # Calculate the initial compression (used for stability during the # experiment. The factor used results to exactly 50kN. That's what we # had during the experiments. self.init_column_compression = 0.0717473345865201*self.column_load # Member lengths # The length of the pin, as constructed on the lab for the beam loading self.l_pin = 80. # The beam length is calculated based on the requested lever arm from # the end of the beam (load application) to the neutral axis of the # column (theoretical centre of the joints) logging.info("column length: %.3f" % self.l_column) self.l_beam = self.l_lever - self.cht2 - self.b_gap - self.l_pin logging.info("beam length: %.3f" % self.l_beam) # Lengths of the solid portions of the members # Calculate the max length of the solid part of the column self.lc_solid2 = self.bht2 + 2*self.cht2 # If the max solid length is larger than the total requested length of # the column, substitute self.no_column_shell = False if self.lc_solid2 > self.l_column/2.: self.no_column_shell = True self.lc_solid2 = self.l_column/2. # Calculate the max length of the solid part of the beam self.lb_solid = self.h_beam # If the max solid length is larger than the total requested length of # the column, substitute self.no_beam_shell = False if self.lb_solid > self.l_beam: self.no_beam_shell = True self.lb_solid = self.l_beam # Seeding size factor for the mesh (how many elements through the # thickness) if size_factor is None: self.size_factor = 3. else: self.size_factor = size_factor # Step properties # Calculate the time it takes for a wave to travel from the one end of # the column to the other. The step time for the column displacement # will be n times that (defined later, under step properties). dilat_vel = sqrt( self.elasticity*(1-self.poisson) / \ (self.density*(1+self.poisson)*(1-2*self.poisson)) ) logging.info("Dilatational velocity, %.3f: " % dilat_vel) # Simplified dilatational wave speed (in case of thin rods). Only for # cross-checking the calculation. dilat_vel_simpl = sqrt(self.elasticity/self.density) logging.info( "Simplified dilatational velocity, %.3f: " % dilat_vel_simpl ) column_time = self.l_column / dilat_vel n_times = 50. # Estimation of the bulk vicousity parameters. The estimation comes # from a suggestion that the viscous parameter should be 1-2% pf the # density*dilatetional wave speed product. # NOTE: The above comment should be checked by introducing alternative # values and checking for vibrations, reaction forces, energy balance # etc. visc_prc = 0.02 c_v = visc_prc*dilat_vel*self.density logging.info("Bulk viscousity factor, c_v: %.5f" % c_v) # Well, the aformentioned method is not used, I just go # trial-and-error... Here I define values to experiment with. self.lin_visc = 0.06 self.quad_visc = 1.2 # TODO: There should be a better way to define the step time for the # beam loading. Currently, a single value is used for all the models # which was concluded by performing a frequency analysis on just one # arbitary model. This cannot reflect equally to all the models because # the beam length is varying and so does the eigenvalue. Maybe it would # be better to perform one frequency analysis per geometry and # automatically calculate the step value... if step_props is None: # ((step1 time, step2 time), (step1 massscale, step2 massscale)) # the following line is working, I increased the time just to test # out... # step_props = ((1.7e-2, 50 * column_time), (1, 1)) step_props = ((3.0e-2, n_times * column_time), (1, 1)) self.stp_time = step_props[0] self.stp_masscale = step_props[1] # Column compression velocity target_disp = 4. self.disp_vel = target_disp / step_props[0][1] # Create the column part self.cprt = self.mdl.Part( dimensionality=THREE_D, name=self.names["cpart"], type=DEFORMABLE_BODY ) # Create the beam part self.bprt = self.mdl.Part( dimensionality=THREE_D, name=self.names["bpart"], type=DEFORMABLE_BODY ) # Create the column shell extension part if not self.no_column_shell: self.cshell_prt = self.mdl.Part( dimensionality=THREE_D, name=self.names["cshellpart"], type=DEFORMABLE_BODY ) # Create the beam shell extension part if not self.no_beam_shell: self.bshell_prt = self.mdl.Part( dimensionality=THREE_D, name=self.names["bshellpart"], type=DEFORMABLE_BODY )
[docs] def calc_axial_disp(self): # TODO: change so that it accounts for effective area (reduced f_y) delta_l = 355. * self.l_column / 210000. return delta_l
[docs] def face_lofter( self, find_point1=None, find_point2=None, find_path=None ): # Pick up all the edges around face 1 edges_f1 = self.bprt.faces.findAt(find_point1).getEdges() section1 = tuple([self.bprt.edges[i] for i in edges_f1]) # Pick up all the edges around face 2 edges_f2 = self.bprt.faces.findAt(find_point2).getEdges() section2 = tuple([self.bprt.edges[i] for i in edges_f2]) self.bprt.SolidLoft( globalSmoothing=ON, loftsections=( section1, section2 ), paths=((self.bprt.edges.findAt(find_path),),) )
[docs] def fillet_welder( self, depth=None, sketchPlane=None, sketchUpEdge=None, origin=None, orientations=(0, 0, 0, 0), name=None ): if any((depth, sketchPlane, sketchUpEdge, origin)) is None: print("Missing arguments for fillet weld") return if name is None: name = "weld_cs" sketchOrientation = RIGHT sketchPlaneSide = SIDE1 flipExtrudeDirection = OFF if orientations[0]: sketchOrientation = LEFT if orientations[1]: sketchPlaneSide = SIDE2 if orientations[2]: flipExtrudeDirection = ON x = (self.w_foot, 0., 0., 0.) y = (0., 0., self.b_gap, self.w_foot) flip = False if orientations[3]: x, y = y, x flip = True transform = self.bprt.MakeSketchTransform( sketchPlane=sketchPlane, sketchUpEdge=sketchUpEdge, sketchPlaneSide=sketchPlaneSide, sketchOrientation=sketchOrientation, origin=origin ) weld = self.mdl.ConstrainedSketch( name=name, sheetSize=2 * self.w_foot, transform=transform ) weld.Line( point1=(x[0], y[0]), point2=(x[1], y[1]) ) weld.Arc3Points( point1=(x[1], y[1]), point2=(x[2], y[2]), point3=self.weld_arc_point( (x[2], y[2]), (x[1], y[1]), curvature=0.1, flip=not (flip) ) ) weld.Line( point1=(x[2], y[2]), point2=(x[3], y[3]) ) weld.Arc3Points( point1=(x[3], y[3]), point2=(x[0], y[0]), point3=self.weld_arc_point( (x[3], y[3]), (x[0], y[0]), curvature=0.1, flip=flip ) ) self.bprt.SolidExtrude( depth=depth, flipExtrudeDirection=flipExtrudeDirection, sketch=weld, sketchPlane=sketchPlane, sketchUpEdge=sketchUpEdge, sketchPlaneSide=sketchPlaneSide, sketchOrientation=sketchOrientation, )
[docs] @staticmethod def pl_datum(prt, plane, offset): return ( prt.datums[ prt.DatumPlaneByPrincipalPlane( offset=offset, principalPlane=plane ).id ] )
[docs] @staticmethod def cross_cut_shell( prt, coords, norm_vect ): n=0 pln_datums = [] principal_plns = (YZPLANE, XZPLANE, XYPLANE) for v, i in enumerate(norm_vect): if not(i): pln_datums.append( prt.datums[ prt.DatumPlaneByPrincipalPlane( offset=coords[n], principalPlane=principal_plns[v] ).id ] ) try: prt.PartitionFaceByDatumPlane( datumPlane=pln_datums[n], faces=prt.faces[:] ) except: print("No partitioning performed") n += 1 return(pln_datums)
[docs] def rhs_sketcher( self, h_rhs, b_rhs, t_rhs, r_rhs, name=None, half=False, **kargs ): """ Make the necessary sketches for creating a half-symmetric 3D RHS part by sweeping. """ if name is None: name = "sweep" path = self.mdl.ConstrainedSketch(name=name, sheetSize=200.0, **kargs) path.Line( point1=(-(h_rhs - t_rhs)/2., 0.), point2=(-(h_rhs - t_rhs)/2., ((b_rhs - t_rhs)/2. - r_rhs)) ) path.ArcByCenterEnds( center=(-(h_rhs - t_rhs)/2. + r_rhs, ((b_rhs - t_rhs)/2. - r_rhs)), direction=CLOCKWISE, point1=(-(h_rhs - t_rhs)/2., ((b_rhs - t_rhs)/2. - r_rhs)), point2=(-(h_rhs - t_rhs)/2. + r_rhs, ((b_rhs - t_rhs)/2.)) ) path.Line( point1=(-(h_rhs - t_rhs) / 2. + r_rhs, ((b_rhs - t_rhs) / 2.)), point2=(0., (b_rhs - t_rhs) / 2.) ) if not half: path.Line( point1=(0., (b_rhs - t_rhs) / 2.), point2=((h_rhs - t_rhs) / 2. - r_rhs, (b_rhs - t_rhs) / 2.) ) path.ArcByCenterEnds( center=( (h_rhs - t_rhs)/2. - r_rhs, (b_rhs - t_rhs)/2. - r_rhs ), direction=CLOCKWISE, point1=((h_rhs - t_rhs)/2. - r_rhs, (b_rhs - t_rhs)/2.), point2=((h_rhs - t_rhs)/2., ((b_rhs - t_rhs)/2. - r_rhs)) ) path.Line( point1=((h_rhs - t_rhs)/2., ((b_rhs - t_rhs)/2. - r_rhs)), point2=((h_rhs - t_rhs)/2., 0.) ) return path
[docs] def common_edge(self, point1, point2): edges1 = self.bprt.vertices.findAt(point1, ).getEdges() edges2 = self.bprt.vertices.findAt(point2, ).getEdges() common = None if len(set(edges1).intersection(edges2)) > 0: common = self.bprt.edges[ int(tuple(set(edges1).intersection(edges2))[0]) ] return common
[docs] def get_connected_vrtcs(self, point): pnt_idx = self.bprt.vertices.findAt(point, ).index edge_list = self.bprt.vertices[pnt_idx].getEdges() pnt_list = [] for edge in edge_list: new_pnt = self.bprt.edges[edge].getVertices()[0] if new_pnt == pnt_idx: new_pnt = self.bprt.edges[edge].getVertices()[1] pnt_list.append(new_pnt) return(pnt_list)
[docs] def common_path(self, point1, point2): direct_path = self.common_edge(point1, point2) if direct_path: return (direct_path,) pnt1_idx = self.bprt.vertices.findAt(point1, ).index pnt2_idx = self.bprt.vertices.findAt(point2, ).index pnt_list = self.get_connected_vrtcs(point1) for pnt in pnt_list: edge1 = self.common_edge( point1, self.bprt.vertices[pnt].pointOn[0] ) edge2 = self.common_edge( self.bprt.vertices[pnt].pointOn[0], point2 ) if edge2: break return(edge1, edge2)
[docs] def column_weld_separation(self): edge1 = self.common_edge( (self.cht2, 0, self.l_column / 2), (self.cht2, self.cbf2, self.l_column / 2.) ) edge2 = self.common_edge( (self.cht2, self.cbf2, self.l_column / 2), (self.chf2, self.cbt2, self.l_column / 2.) ) self.bprt.PartitionCellByExtrudeEdge( cells=self.bprt.cells[:], edges=( edge1, edge2 ), line=self.bprt.edges.findAt( (self.cht2, 0, self.l_column/2 - 1.), ), sense=FORWARD )
[docs] def beam_weld_separation(self, theta=None): if theta is None: self.bprt.PartitionCellBySweepEdge( cells=self.bprt.cells[:], edges=( self.common_edge( (self.b_start, self.bbf2, self.bht2), (self.b_start, self.bbt2, self.bhf2)), self.common_edge( (self.b_start, self.bbt2, self.bhf2), (self.b_start, self.bbt2, 0.)), ), sweepPath=self.bprt.edges.findAt( (self.cht2 + self.w_foot/2., self.bbf2, self.bht2), ) ) else: self.bprt.PartitionCellBySweepEdge( cells=self.bprt.cells[:], edges=( self.common_edge( (self.b_start, self.bbf2, self.bht2), ( self.b_start, self.bbf2 + self.r_o_beam * sin(theta), self.bhf2 + self.r_o_beam * cos(theta) ) ), ), sweepPath=self.bprt.edges.findAt( ( self.cht2 + self.w_foot/2., self.bbf2, self.bht2 ), ) )
[docs] def create_history(self, name, step, region, variables): self.mdl.HistoryOutputRequest( createStepName=step, name=name, # numIntervals=30, rebar=EXCLUDE, region=region, sectionPoints=DEFAULT, variables=variables )
[docs] def fetch_maxload(self): odb = self.open_odb() stp = odb.steps[self.names["c_load_step"]] data = stp.historyRegions['Node ASSEMBLY.3'].historyOutputs['CF3'].data maxpos = data.index(max(data, key=lambda x: abs(x[1]))) load = data[maxpos][1] return load
[docs] @staticmethod def weld_arc_point( point1, point2, curvature=1., flip=False ): x1, y1 = point1 x2, y2 = point2 xm = (x1 + x2) / 2. ym = (y1 + y2) / 2. d0 = sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) d1 = curvature * d0 / 2. theta = asin((y2 - y1) / d0) if (x2 - x1) < 0: theta = pi - theta if flip: phi = theta - pi / 2. else: phi = theta + pi / 2. x = xm + d1 * cos(phi) y = ym + d1 * sin(phi) return x, y
[docs] @staticmethod def export_results(job_name): """ Fetch history outputs. Works only with results from the static solver """ # Open the odb database odb = at.odbAccess.openOdb(job_name + ".odb") session.viewports['Viewport: 1'].setValues(displayedObject=odb) # Write all the existing history output data to file at.write_all_history(odb, job_name+".hou") # Fetch strain data from field output. session.xyDataListFromField( odb=odb, outputPosition=NODAL, variable=( ( 'LE', INTEGRATION_POINT, ( (COMPONENT, 'LE11'), ) ), ), nodeSets=('SG_SET', ) ) # Write the sg data to a report file session.writeXYReport( job_name + ".rpt", tuple(session.xyDataObjects.values()) ) return(odb)
[docs] def modeler(self): """Create a model of a T-joint of RHS profiles.""" # Rebuild the namespace (to avoid repeating the "self") # Grab the model and the part objects from the self cprt = self.cprt bprt = self.bprt if not self.no_column_shell: csprt = self.cshell_prt if not self.no_beam_shell: bsprt = self.bshell_prt mdl = self.mdl # General column properties r_o_column = self.r_o_column r_m_column = self.r_m_column # Half-symmetric cht2 = self.cht2 cbt2 = self.cbt2 chf2 = self.chf2 cbf2 = self.cbf2 # General beam properties r_o_beam = self.r_o_beam r_i_beam = self.r_i_beam r_m_beam = self.r_m_beam # Half-symmetric bht2 = self.bht2 bbt2 = self.bbt2 bhf2 = self.bhf2 bbf2 = self.bbf2 # Thicknesses t_beam = self.t_beam t_column = self.t_column # Lengths l_beam = self.l_beam l_column = self.l_column l_pin = self.l_pin # Solid portion lengths lc_solid2 = self.lc_solid2 lb_solid = self.lb_solid # General weld props w_foot = self.w_foot # Weld foot size b_gap = self.b_gap # Gap between the column and the beam b_start = self.b_start # x-position on the GCSYS where the beam starts # Start building the model. We start by building the beam part, which # is the most complicated. # Datums # Datum planes for the beam part mid_datxy = self.pl_datum(bprt, XYPLANE, 0.) mid_datxz = self.pl_datum(bprt, XZPLANE, 0.) beam_start_datyz = self.pl_datum(bprt, YZPLANE, b_start) beam_r_datxy = self.pl_datum(bprt, XYPLANE, bhf2) if self.measure_weld: beam_w_top_datxy = self.pl_datum(bprt, XYPLANE, 30) beam_w_bot_datxy = self.pl_datum(bprt, XYPLANE, -30) beam_w_datyz = self.pl_datum(bprt, YZPLANE, 55) # Datum plane for the beam and column shell parts. if not self.no_beam_shell: mid_datxy_sbeam = bsprt.datums[ bsprt.DatumPlaneByPrincipalPlane( offset=0., principalPlane=XYPLANE ).id ] mid_datxz_sbeam = bsprt.datums[ bsprt.DatumPlaneByPrincipalPlane( offset=0., principalPlane=XZPLANE ).id ] mid_datxz_scolumn = csprt.datums[ csprt.DatumPlaneByPrincipalPlane( offset=0., principalPlane=XZPLANE ).id ] # Datum plane for the column part. mid_datxy_col = cprt.datums[ self.cprt.DatumPlaneByPrincipalPlane( offset=0., principalPlane=XYPLANE ).id ] mid_datxz_col = cprt.datums[ self.cprt.DatumPlaneByPrincipalPlane( offset=0., principalPlane=XZPLANE ).id ] if self.measure_weld: col_w_top_datxy = self.pl_datum(cprt, XYPLANE, 30) col_w_bot_datxy = self.pl_datum(cprt, XYPLANE, -30) col_w_datyz = self.pl_datum(cprt, YZPLANE, 40) # Datum axes beam_start_daty = bprt.datums[ bprt.DatumAxisByTwoPlane( plane1=mid_datxy, plane2=beam_start_datyz ).id ] mid_datx = bprt.datums[ bprt.DatumAxisByTwoPlane( plane1=mid_datxy, plane2=mid_datxz ).id ] # Create column surface. The column face upon which the beam is welded # is needed to help with the creation of the weld geometry later on. column_sweep_path = self.rhs_sketcher( self.h_column, self.b_column, 0., r_o_column, name="cface_sweep_path", half=True, transform=( -1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0 ) ) column_sweep_cs = mdl.ConstrainedSketch( name='cface_sweep_cs', sheetSize=200.0, transform=( 0, -1.0, 0.0, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0 ) ) mdl.sketches['cface_sweep_cs'].Line( point1=(0.0, 0.0), point2=(0.0, l_column) ) bprt.BaseShellSweep( path=column_sweep_path, sketch=column_sweep_cs ) # Create the beam beam_sweep_path_transform = bprt.MakeSketchTransform( sketchPlane=beam_start_datyz, sketchUpEdge=beam_start_daty, sketchPlaneSide=SIDE1, sketchOrientation=RIGHT, origin=(b_start, 0.0, 0.0) ) beam_sweep_path = self.rhs_sketcher( self.h_beam, self.b_beam, self.t_beam, r_m_beam, name="beam_sweep_path", transform=beam_sweep_path_transform, half=True ) beam_sweep_cs = mdl.ConstrainedSketch( name='beam_sweep_cs', sheetSize=200.0, ) beam_sweep_cs.rectangle( point1=(0, -self.t_beam / 2.), point2=(self.lb_solid, self.t_beam / 2.) ) bprt.BaseSolidSweep( path=beam_sweep_path, sketch=beam_sweep_cs ) # Partition the beam bprt.PartitionCellByPlaneThreePoints( cells=bprt.cells.findAt((( b_start+self.lb_solid/2., bbt2-self.t_beam/2., 0.0 ),),), point1=(0., bbf2, 0.), point2=(1., bbf2, 0.), point3=(0., bbf2, 1.) ) bprt.PartitionCellByPlaneThreePoints( cells=bprt.cells.findAt((( b_start+self.lb_solid/2., bbt2-self.t_beam/2., 0.0 ),),), point1=(0., 0., bhf2), point2=(1., 0., bhf2), point3=(0., 1., bhf2) ) # Create the welds: Flange weld self.fillet_welder( depth=bbf2, sketchPlane=mid_datxz, sketchUpEdge=mid_datx, origin=(cht2, 0.0, bht2), orientations=(1, 0, 0, 0), name='weld1_cs' ) bprt.PartitionCellBySweepEdge( cells=bprt.cells.findAt(((b_start, bbt2/2., bht2 + b_gap),)), edges=(bprt.edges.findAt((cht2 + w_foot, b_gap, bht2), ),), sweepPath=bprt.edges.findAt((b_start+b_gap, bbf2, bht2), ) ) # Create the welds: Web weld cat_w = cbt2 - r_o_column - bbt2 # From this point, the model generation is separated in 3 different # cases depending on the relative position of the web weld to the # column face and corner curvature: # 1) The weld foot fits entirely in the flat portion of the column face # 2) The weld foot partially enters the curved corner part of the # column # 3) The foot of the web's weld lands entirely on the curved portion of # the column corner. # The differentiation between the three cases is based on the value of # the variable 'cat_w'. # First case: The weld foot entirely on the flat portion of the column # face. if cat_w >= w_foot: bprt.PartitionCellByPlaneThreePoints( cells=bprt.cells, point1=(cht2 + w_foot, 0., 0.), point2=(cht2 + w_foot, 1., 0.), point3=(cht2 + w_foot, 1., 1.) ) self.fillet_welder( depth=bhf2, sketchPlane=beam_r_datxy, sketchUpEdge=beam_start_daty, origin=(cht2, bbt2, -bhf2), orientations=(0, 0, 1, 1), name='weld3_cs' ) # Loft around the rounded corners # First corner self.face_lofter( find_point1=(cht2 + w_foot / 2., bbf2, bht2 + w_foot / 2.), find_point2=(cht2 + w_foot / 2., bbt2 + w_foot / 2., bhf2), find_path=( cht2 + b_gap, bbf2 + r_o_beam * sin(pi / 4.), bhf2 + r_o_beam * cos(pi / 4.) ), ) bprt.RemoveFaces( deleteCells=False, faceList=( bprt.faces.findAt((cht2, cbf2/2., 0.9*l_column), ), bprt.faces.findAt(( chf2+r_o_column*cos(pi/4.), cbf2+r_o_column*sin(pi/4.), 0.9*l_column ), ), bprt.faces.findAt((chf2/2., cbt2, 0.9*l_column), ), bprt.faces.findAt((cht2, cbf2/2., bhf2), )) ) # Extra partitioning to aid the mesh generation # Separate the weld from the column and the beam self.beam_weld_separation() # Mirror everything to create the other half symmetric part. bprt.Mirror( keepOriginal=ON, mirrorPlane=mid_datxy ) # Assign meshing techniques and element types bprt.setMeshControls( elemShape=TET, regions=bprt.cells.findAt( ((b_start, bbf2/2., bht2+w_foot/2.),), ((b_start, bbf2/2., -bht2-w_foot/2.),), ), technique=FREE ) bprt.setElementType( elemTypes=( ElemType( lemCode=C3D4, elemLibrary=EXPLICIT ), ), regions=( bprt.cells.findAt( ((b_start, bbf2/2., bht2+w_foot/2.),), ((b_start, bbf2/2., -bht2-w_foot/2.),), ), ) ) # From here starts the modelling of the 2 other cases (case b) # and c)) regarding the relative position of the weld to the column # curved corner portion. # Initially, some properties and geometries common to both cases are # calculated. else: """ The weld around the corner arc is performed in 2 steps: 1) Sweep around the beam corner arc until the furthest point of the weld foot meets the begining of the column corner arc. Along this part, the weld maintains the sam cross-section 2) Loft around the remaining part of the beam corner arc. The weld's cross-sections changes shape along this step. """ # Angle of the 1st step if bbf2<chf2: theta_1 = asin((r_o_beam + cat_w) / (r_o_beam + w_foot)) else: theta_1 = 0. # Angle of the second step theta_2 = pi / 2 - theta_1 # Useful points # Point where the weld foot meets the beginning of the column # corner arc wa_pnt = ( cht2, cbf2, bhf2 + (r_o_beam + w_foot) * cos(theta_1) ) # Point on the 1st part of the beam corner arc c1_pnt = ( b_start, bbf2 + r_o_beam * sin(theta_1 / 3.), bhf2 + r_o_beam * cos(theta_1 / 3.) ) # Point on the 2nd part of the beam corner arc c2_pnt = ( b_start, bbf2 + r_o_beam * sin(theta_1 + theta_2 / 2.), bhf2 + r_o_beam * cos(theta_1 + theta_2 / 2.) ) # Create a datum plane to to assist partitioning between the two # parts of the corner weld. sweep_datum_f = bprt.DatumPlaneByThreePoints( point1=wa_pnt, point2=(0., bbf2, bhf2), point3=(1., bbf2, bhf2), ) cp_datum = bprt.datums[sweep_datum_f.id] # Partition the beginning of the beam to split the corner to the # two aforementioned parts. # Dont partirion if the beam corner is shorter than the columnh # corner (the corner weld is entirely on the # column corner, no part on the flat face of the column) if bbf2 < cbf2: bprt.PartitionCellByDatumPlane( cells=bprt.cells.findAt( (( b_start + b_gap, bbf2 + r_m_beam * sin(theta_1 / 2.), bhf2 + r_m_beam * cos(theta_1 / 2.) ),) ), datumPlane=cp_datum ) # Here starts the generation of the weld specificlaly for case b), # where the web's weld enters partially in the curved portion of # the column corner. if cat_w >= 0: # Partition the beam at the height of the weld-foot bprt.PartitionCellByPlaneThreePoints( cells=bprt.cells, point1=(cht2 + w_foot, 0., 0.), point2=(cht2 + w_foot, 1., 0.), point3=(cht2 + w_foot, 1., 1.) ) # Creation of the web part of the weld. Constant cross-section # swept along a straight path. # Some useful quantities l_arc = w_foot - cat_w x_arc = r_o_column * sin(l_arc / r_o_column) y_arc = r_o_column * (cos(l_arc / r_o_column) - 1) # Make the transformation object for the cross section. Origin # at the beam edge point. weld3_cs_transform = bprt.MakeSketchTransform( sketchPlane=mid_datxy, sketchUpEdge=beam_start_daty, sketchPlaneSide=SIDE1, sketchOrientation=RIGHT, origin=(cht2, bbt2, bhf2) ) # Create the sketch object and draw the weld cs. weld3_cs_sktch = mdl.ConstrainedSketch( name='weld3_cs', sheetSize=2 * w_foot, transform=weld3_cs_transform ) # This first line becomes a point for cat_w = 0. Check if it # causes problems, if yes put it under # condition. weld3_cs_sktch.Line( point1=(0., cat_w), point2=(0., 0.) ) weld3_cs_sktch.Arc3Points( point1=(0., 0.), point2=(b_gap, 0.), point3=self.weld_arc_point( (0., 0.), (b_gap, 0.), curvature=0.1, flip=True ) ) weld3_cs_sktch.Line( point1=(b_gap, 0.), point2=(w_foot, 0.) ) weld3_cs_sktch.Arc3Points( point1=(y_arc, cat_w + x_arc), point2=(w_foot, 0.), point3=self.weld_arc_point( (y_arc, cat_w + x_arc), (w_foot, 0.), curvature=0.1, flip=False ) ) weld3_cs_sktch.ArcByCenterEnds( center=(-r_o_column, cat_w), direction=COUNTERCLOCKWISE, point1=(0., cat_w), point2=(y_arc, cat_w + x_arc) ) bprt.SolidExtrude( depth=bhf2, flipExtrudeDirection=OFF, sketch=weld3_cs_sktch, sketchPlane=mid_datxy, sketchUpEdge=beam_start_daty, sketchPlaneSide=SIDE1, sketchOrientation=RIGHT, ) # Weld along the beam curved corner. In 2 parts, as described # previously. # 1st Part # Sweep the cross the fillet weld cross-section around the # first part of the corner weld (constant cs). sweep_edge = self.common_edge( ( b_start, bbf2 + r_o_beam * sin(theta_1), bhf2 + r_o_beam * cos(theta_1) ), ( b_start, bbf2, bht2 ) ) # The following checks the default direction of the sweep path # and if its wrong, the flipSweepDirection argument is given to # the sweeping tool if sweep_edge.getVertices()[0] > sweep_edge.getVertices()[1]: flip_sweep=ON else: flip_sweep=OFF bprt.SolidSweep( flipSweepDirection=flip_sweep, path=bprt.edges.findAt(sweep_edge.pointOn), profile=bprt.faces.findAt(( b_start, bbf2, bht2 + w_foot/2. ), ) ) # 2nd Part # This part is made as a loft between the start and the end # shape of the weld cross-section (fillet-weld to the semi-butt # shape of the web weld). The faces are lofted and then the # solid is created from the separate faces. First, partitioning # of the column faces is necessary to create the lofting paths. # Partition the flat part of the column face. bprt.PartitionFaceByProjectingEdges( edges=bprt.edges.findAt((c2_pnt,)), extendEdges=False, faces=bprt.faces.findAt((( cht2, bbf2, bhf2 ),)) ) # Partition the curved part of the column face. wl_edge1 = bprt.edges.getClosest( coordinates=( ( (cht2+y_arc + cht2+w_foot)/2., (cbf2+x_arc + bbf2+r_o_beam)/2., bhf2 ), ) )[0][0] prj_vrtx_idx = wl_edge1.getVertices() prj_vrtx = bprt.vertices[prj_vrtx_idx[0]].pointOn[0] prj_vrtx2 = bprt.vertices[prj_vrtx_idx[1]].pointOn[0] if prj_vrtx[1] < prj_vrtx2[1]: prj_vrtx = prj_vrtx2 partition_transform = bprt.MakeSketchTransform( sketchPlane=bprt.faces.findAt((cht2, cbf2/2., bhf2/2.),), sketchPlaneSide=SIDE2, sketchUpEdge=bprt.edges.findAt((cht2, cbf2/2., 0.), ), sketchOrientation=RIGHT, origin=(cht2, bbf2, bhf2) ) partition_sktch = mdl.ConstrainedSketch( name='partition_sktch', sheetSize=cht2, transform=partition_transform ) bprt.projectReferencesOntoSketch( filter=COPLANAR_EDGES, sketch=partition_sktch ) bprt.projectReferencesOntoSketch( sketch=partition_sktch, vertices=(bprt.vertices.findAt((prj_vrtx), ),) ) h_const1 = partition_sktch.ConstructionLine( angle=0.0, point1=(0, prj_vrtx[1] - bbf2) ) partition_sktch.HorizontalConstraint( addUndoState=False, entity=h_const1 ) r_wfoot = r_o_beam + w_foot spline1 = partition_sktch.Spline( points=( ( -r_wfoot * cos(theta_1), r_wfoot * sin(theta_1) ), ( -r_wfoot * cos(theta_1 + theta_2/2.), r_wfoot * sin(theta_1 + theta_2/2.) ), ( 0, r_o_beam + cat_w + x_arc ), ) ) partition_sktch.TangentConstraint( entity1=partition_sktch.geometry.findAt( ( -r_wfoot * cos(theta_1/2.), r_wfoot * sin(theta_1/2.) ), ), entity2=spline1 ) partition_sktch.TangentConstraint( entity1=partition_sktch.geometry.findAt( (1., r_o_beam + cat_w + x_arc), ), entity2=spline1 ) # Make the partitioning bprt.PartitionFaceBySketchThruAll( faces=bprt.faces.findAt((( chf2 + r_o_column*sin(pi/4.), cbf2 + r_o_column*cos(pi/4.), bhf2 ),)), sketch=partition_sktch, sketchPlane=bprt.faces.findAt((cht2, cbf2/2., bhf2/2.),), sketchPlaneSide=SIDE2, sketchUpEdge=bprt.edges.findAt((cht2, cbf2/2., 0.), ) ) # Loft a shell face for the small weld part that fills the gap # under the beam. gap_edge1 = bprt.edges.getByBoundingSphere( ( cht2+b_gap/2., bbf2 + r_o_beam * sin(theta_1), bhf2 + r_o_beam * cos(theta_1) ), b_gap / 2. + 0.1 ) gap_edge2 = self.common_edge( (cht2, bbt2, bhf2), (b_start, bbt2, bhf2) ) bprt.ShellLoft( globalSmoothing=ON, loftsections=( gap_edge1, (gap_edge2,) ), paths=( (bprt.edges.findAt((cht2, c2_pnt[1], c2_pnt[2]), ),), (bprt.edges.findAt((b_start, c2_pnt[1], c2_pnt[2]), ),) ) ) # Loft a shell face for front arched face of the weld. wl_edge1 = bprt.edges.getClosest(coordinates=(( (cht2+y_arc + cht2+w_foot)/2., (cbf2+x_arc + bbf2+r_o_beam)/2., bhf2 ),))[0][0] wl_edge2 = bprt.edges.getClosest(coordinates=(( (wa_pnt[0] + cht2+w_foot)/2., (wa_pnt[1] + bbf2 + r_o_beam*sin(theta_1))/2., (wa_pnt[2] + bhf2 + r_o_beam*cos(theta_1))/2. ),))[0][0] wl_path1 = bprt.edges.getClosest(coordinates=(( cht2+w_foot, c2_pnt[1], c2_pnt[2], ),))[0][0] wl_path2 = bprt.edges.getClosest(coordinates=(( cht2+y_arc, bbf2 + r_wfoot * sin(theta_1 + theta_2 / 2.), bhf2 + r_wfoot * cos(theta_1 + theta_2 / 2.) ),))[0][0] bprt.ShellLoft( globalSmoothing=ON, loftsections=((wl_edge1,),(wl_edge2,)), paths=((wl_path1,),(wl_path2,)) ) # Convert the faces that form the last part of the weld corner # to a solid bprt.AddCells( faceList=bprt.faces.getByBoundingBox( prj_vrtx[0]-0.1, bbf2+r_o_beam*sin(theta_1)-0.1, prj_vrtx[2]-0.1, cht2+w_foot+0.1, prj_vrtx[1]+0.1, wa_pnt[2]+0.1 ) ) # Extra partitioning to aid the mesh generation self.beam_weld_separation(theta=theta_1) # Remove auxiliary column faces bprt.RemoveFaces( deleteCells=False, faceList=( bprt.faces.findAt((cht2, cbf2/2., 0.9*l_column), ), bprt.faces.findAt( ( chf2+r_o_column*cos(pi/4.), cbf2+r_o_column*sin(pi/4.), 0.9*l_column ), ), bprt.faces.findAt((chf2/2., cbt2, 0.9*l_column), ), bprt.faces.findAt((cht2, cbf2/2., bhf2), )) ) # Mirror everything to create the other half symmetric part. bprt.Mirror( keepOriginal=ON, mirrorPlane=mid_datxy ) # Assign meshing techniques and element types bprt.setMeshControls( elemShape=TET, regions=bprt.cells.findAt( ((b_start, bbf2 / 2., bht2 + w_foot / 2.),), ((b_start, bbf2 / 2., -bht2 - w_foot / 2.),), ((b_start, bbt2 + w_foot/2., bhf2/2),), ((b_start, bbt2 + w_foot/2., -bhf2/2),), ((b_start, bbt2 + w_foot/2., bhf2-0.1),), ((b_start, bbt2 + w_foot/2., -bhf2+0.1),), ( ( b_start, bbf2 + (r_o_beam+w_foot/2.)*cos(theta_2/2.), bhf2 + (r_o_beam+w_foot/2.)*sin(theta_2/2.) ), ), ( ( b_start, bbf2 + (r_o_beam+w_foot/2.)*cos(theta_2/2.), -bhf2 - (r_o_beam+w_foot/2.)*sin(theta_2/2.) ), ), ), technique=FREE ) bprt.setElementType( elemTypes=( ElemType( lemCode=C3D4, elemLibrary=EXPLICIT ), ), regions=(bprt.cells.findAt( ((b_start, bbf2 / 2., bht2 + w_foot / 2.),), ((b_start, bbf2 / 2., -bht2 - w_foot / 2.),), ((b_start, bbt2 + w_foot / 2., bhf2 / 2),), ((b_start, bbt2 + w_foot / 2., -bhf2 / 2),), ((b_start, bbf2 + (r_o_beam + w_foot/2.) * cos(theta_2 / 2.), bhf2 + (r_o_beam + w_foot/2.) * sin(theta_2 / 2.)),), ((b_start, bbf2 + (r_o_beam + w_foot/2.) * cos(theta_2 / 2.), -bhf2 - (r_o_beam + w_foot/2.) * sin(theta_2 / 2.)),), ),), ) # Here starts the modelling of the web's weld for case c), where # the weld foot is landing entirely on the curved portion of the # column corner. else: # Construct the straight part of the web fillet weld. # The geometry of the weld cross-section is described on a csys # with origin on the centre of the column radious. # Coordinates of the beam edge point x_edge = -cat_w y_edge = r_o_column + b_gap # Percentage of fillet-butt shape depending on how far in the # column curvature is the beam b_prc = x_edge / r_o_column f_prc = 1 - b_prc # length of the weld footprint under the beam and on the side # of the beam w_unde = b_prc * self.t_beam w_side = (w_foot - b_gap) * f_prc # Auxiliary length on the x-axis for the calculation of the # weld foot on the arc x_extra = f_prc * (r_o_column * sin(w_foot/r_o_column) - (w_foot - b_gap)/2.) # Coordinates of the arc's start-stop points x_arc_start = x_edge - w_unde y_arc_start = sqrt(r_o_column**2 - x_arc_start**2) x_arc_stop = x_edge + w_side/2. + x_extra # Check if the calculated arc end-point is further then the # column arc and correct it if x_arc_stop > r_o_column: x_arc_stop = r_o_column y_arc_stop = sqrt(r_o_column**2 - x_arc_stop**2) # draw the sketch of the cs of the web's portion of the weld. weld3_cs_transform = bprt.MakeSketchTransform( sketchPlane=beam_r_datxy, sketchUpEdge=beam_start_daty, sketchPlaneSide=SIDE1, sketchOrientation=RIGHT, origin=(chf2, cbf2, bhf2) ) weld3_cs_sktch = mdl.ConstrainedSketch( name='weld3_cs', sheetSize=2*w_foot, transform=weld3_cs_transform ) weld3_cs_sktch.Arc3Points( point1=(y_arc_start, x_arc_start), point2=(y_edge, x_arc_start), point3=self.weld_arc_point( (y_arc_start, x_arc_start), (y_edge, x_arc_start), curvature=0.1, flip=True ) ) weld3_cs_sktch.Line( point1=(y_edge, x_arc_start), point2=(y_edge, x_edge) ) weld3_cs_sktch.Line( point1=(y_edge, x_edge), point2=(y_edge + w_side, x_edge) ) weld3_cs_sktch.Arc3Points( point1=(y_arc_stop, x_arc_stop), point2=(y_edge + w_side, x_edge), point3=self.weld_arc_point( (y_arc_stop, x_arc_stop), (y_edge + w_side, x_edge), curvature=0.1, flip=False ) ) weld3_cs_sktch.ArcByCenterEnds( center=(0., 0.), direction=CLOCKWISE, point1=(y_arc_stop, x_arc_stop), point2=(y_arc_start, x_arc_start) ) # Extrude the weld cs sketch bprt.SolidExtrude( depth=bhf2, flipExtrudeDirection=ON, sketch=weld3_cs_sktch, sketchPlane=beam_r_datxy, sketchUpEdge=beam_start_daty, sketchPlaneSide=SIDE1, sketchOrientation=RIGHT, ) # Weld along the beam curved corner. In 2 parts, as described # previously. # 1st Part # Sweep the fillet weld cross-section around the first part of # the corner weld (constant cs), if it exists. if bbf2 < cbf2: bprt.SolidSweep( flipSweepDirection=ON, path=bprt.edges.findAt((c1_pnt,)), profile=bprt.faces.findAt(( b_start, bbf2, bht2 + w_foot/2. ), ) ) # Partition the corner part to create additional edges, # useful for sweping paths. # First part of the corner, parallel to th YZ plane bprt.PartitionCellByPlaneThreePoints( cells=bprt.cells.findAt((c1_pnt,)), point1=(cht2+w_foot, 0., 0.), point2=(cht2+w_foot, 1., 0.), point3=(cht2+w_foot, 0., 1.) ) # 2nd Part # This part is made as a loft between the start and the end # shape of the weld cross-section (fillet-weld to the # semi-butt shape of the web weld). The faces are lofted # and then the solid is created from the separate # faces. First, partitioning of the column faces is # necessary to create the lofting paths. # Partition the flat part of the column face. bprt.PartitionFaceByProjectingEdges( edges=bprt.edges.findAt(( c2_pnt, )), extendEdges=False, faces=bprt.faces.findAt((( cht2, bbf2 + r_o_beam * sin(theta_1), bhf2 + r_o_beam * cos(theta_1) ),)) ) # Partition the beginning of the beam web, up to the weld foot. bprt.PartitionCellByPlaneThreePoints( cells=bprt.cells.findAt( ( ( cht2 + w_foot, bbt2 - t_beam / 2., bhf2 / 2. ), ) ), point1=(b_start + w_side, 0., 0.), point2=(b_start + w_side, 1., 0.), point3=(b_start + w_side, 0., 1.) ) # Partition the external face of the second part of the beam corner curve. bprt.PartitionFaceByShortestPath( faces=bprt.faces.findAt(((cht2 + w_foot / 2., c2_pnt[1], c2_pnt[2]),)), point1=bprt.vertices.findAt( (cht2 + w_foot, bbf2 + r_o_beam * sin(theta_1), bhf2 + r_o_beam * cos(theta_1)), ), point2=bprt.vertices.findAt((b_start + w_side, bbf2 + r_o_beam, bhf2), ) ) # Partition the beginning of the beam flange bprt.PartitionCellByPlaneThreePoints( cells=bprt.cells.findAt(((cht2+w_foot, bbf2/2., bht2-t_beam/2),)), point1=(cht2+w_foot, 0., 0.), point2=(cht2+w_foot, 1., 0.), point3=(cht2+w_foot, 0., 1.) ) # Partition the internal face of the second part of the beam corner curve. bprt.PartitionFaceByShortestPath( faces=bprt.faces.findAt((( cht2 + w_foot / 2., bbf2 + r_i_beam * sin(theta_1 + theta_2 / 2.), bhf2 + r_i_beam * cos(theta_1 + theta_2 / 2.) ),)), point1=bprt.vertices.findAt( ( cht2 + w_foot, bbf2 + r_i_beam * sin(theta_1), bhf2 + r_i_beam * cos(theta_1) ), ), point2=bprt.vertices.findAt((b_start + w_side, bbf2 + r_i_beam, bhf2), ) ) # Partition the curved part of the column face. # Get two points on the bottom of the beam face to aid the partitioning bbf_pnt1 = b_start, bbf2+r_o_beam*sin(theta_1), bhf2+r_o_beam*cos(theta_1) bbf_pnt2 = b_start, cbf2+x_arc_start, bhf2 # Furthest point on the cs of the web's straight weld. wf_pnt = (chf2 + y_arc_stop, cbf2 + x_arc_stop, bhf2) # Innermost point on the surface of the column curvature the cs # of the web's straight weld wi_pnt = (chf2 + y_arc_start, cbf2 + x_arc_start, bhf2) # Create the sketch for partitioning the curved part of the column partition_transform = bprt.MakeSketchTransform( sketchPlane=bprt.faces.findAt((cht2, cbf2/2., bhf2/2.),), sketchPlaneSide=SIDE2, sketchUpEdge=bprt.edges.findAt((cht2, cbf2/2., 0.), ), sketchOrientation=RIGHT, origin=(cht2, bbf2, bhf2) ) partition_sktch = mdl.ConstrainedSketch( name='partition_sktch', sheetSize=cht2, transform=partition_transform ) bprt.projectReferencesOntoSketch( filter=COPLANAR_EDGES, sketch=partition_sktch ) bprt.projectReferencesOntoSketch( sketch=partition_sktch, vertices=(bprt.vertices.findAt((wf_pnt), ),) ) bprt.projectReferencesOntoSketch( sketch=partition_sktch, vertices=(bprt.vertices.findAt((bbf_pnt2), ),) ) h_const1 = partition_sktch.ConstructionLine( angle=-5, point1=(0, wf_pnt[1] - bbf2) ) h_const2 = partition_sktch.ConstructionLine( angle=0.0, point1=(0, bbf_pnt2[1] - bbf2) ) # Fix construction lines partition_sktch.FixedConstraint( entity=h_const1) partition_sktch.HorizontalConstraint( addUndoState=False, entity=h_const2 ) # Useful quantities and points for the splines r_wfoot = r_o_beam + w_foot x_sp1 = -r_wfoot * cos(theta_1) y_sp1 = r_wfoot * sin(theta_1) y_sp3 = cbf2 - bbf2 + x_arc_stop y_sp2 = (3*y_sp1 + y_sp3)/4. x_sp2 = -sqrt(r_wfoot**2 - y_sp2**2) ## Draw outer spline ## KEEP THIS, IN CASE IT HAS TO BE USED!! ## This method of partitioning the column's curved face did not ## work for all the cases of beta=1 so a different strategy was ## followed (using PartitionFaceByCurvedPathEdgePoints) #spline1 = partition_sktch.Spline( # points=( # (x_sp1, y_sp1), # (x_sp2, y_sp2), # (0, y_sp3), # ) #) # Draw inner spline spline2 = partition_sktch.Spline( points=( (-r_o_beam * cos(theta_1), r_o_beam * sin(theta_1)), (-sqrt(r_o_beam**2 - y_sp1**2), y_sp1), (0, cbf2 - bbf2 + x_arc_start), ) ) ## Create tangent contraints to get better curvature ## Horizontal constraints partition_sktch.TangentConstraint( entity1=h_const2, entity2=spline2 ) # Constraints on the end of the curved path. If the corner weld # is located entirely on the column curvature, this becomes a # vertical constraint. if bbf2 < cbf2: partition_sktch.TangentConstraint( entity1=partition_sktch.geometry.findAt( (-r_o_beam * cos(theta_1/2.), r_o_beam * sin(theta_1/2.)), ), entity2=spline2 ) else: partition_sktch.TangentConstraint( entity1=partition_sktch.geometry.findAt( (-r_o_beam, -bbf2/2.), ), entity2=spline2 ) # Perform the partitioning. # The partitioning is performed in 3 steps: first, the column # curved face is sliced at the angle that the weld of the flat # part meets the start of the curvature. # The outer line is partitioned with "CurvedPathEdgePoints" # while the second is performed using the sketch that was just # created. # Partition the inner curve using the sketch bprt.PartitionFaceBySketchThruAll( faces=bprt.faces.findAt((( chf2 + r_o_column*sin(pi/4.), cbf2 + r_o_column*cos(pi/4.), (bhf2+bht2)/2. ),)), sketch=partition_sktch, sketchPlane=bprt.faces.findAt((cht2, cbf2/2., l_column/2.),), sketchPlaneSide=SIDE2, sketchUpEdge=bprt.edges.findAt((cht2, cbf2/2., 0.), ) ) # Slice at the correct angle. if not theta_1==0: bprt.PartitionFaceByExtendFace( extendFace=bprt.faces.findAt(( cht2 + b_gap/2., bbf2 + r_o_beam * sin(theta_1), bhf2 + r_o_beam * cos(theta_1) )), faces=bprt.faces.findAt(( chf2 + r_o_column*sin(pi/4), cbf2 + r_o_column*sin(pi/4), 2*bhf2 )), ) # Getting this newly created edge from the previous # partitioning angle_path = self.common_edge( ( cht2, cbf2, bhf2 + (r_o_beam + w_foot) * cos(theta_1) ), ( chf2, cbt2, bhf2 + (cbt2-bbf2)/tan(theta_1) ) ) bprt.PartitionFaceByCurvedPathEdgePoints( face=bprt.faces.findAt(( chf2 + r_o_column*sin(pi/4), cbf2 + r_o_column*sin(pi/4), bhf2 + r_o_beam/2. )), edge1=angle_path, edge2=self.common_edge( wf_pnt, wi_pnt ), point1=bprt.vertices.findAt(( cht2, bbf2 + r_wfoot*sin(theta_1), bhf2 + r_wfoot*cos(theta_1) )), point2=bprt.vertices.findAt(wf_pnt) ) else: bprt.PartitionFaceByCurvedPathEdgePoints( face=bprt.faces.findAt(( chf2 + r_o_column*sin(pi/4), cbf2 + r_o_column*sin(pi/4), bhf2 + r_o_beam/2. )), edge1=bprt.edges.findAt(( cht2, cbf2, 2*bht2, )), edge2=bprt.edges.findAt(( chf2 + r_o_column*sin(pi/4), cbf2 + r_o_column*sin(pi/4), bhf2 )), point1=bprt.vertices.findAt(( cht2, bbf2 + r_wfoot*sin(theta_1), bhf2 + r_wfoot*cos(theta_1) )), point2=bprt.vertices.findAt(wf_pnt) ) # Partition the bottom face (on the thickness) of the beam. if bbf2 < cbf2: #if not bbt2 == cbt2: bprt.PartitionFaceByCurvedPathEdgePoints( edge1=bprt.edges.findAt( ( b_start, bbf2+r_m_beam*sin(theta_1), bhf2+r_m_beam*cos(theta_1) ), ), edge2=bprt.edges.findAt((b_start, cbf2+(x_arc_start+x_edge)/2., bhf2), ), face=bprt.faces.findAt(( b_start, bbf2+r_m_beam*sin(theta_1+theta_2/2.), bhf2+r_m_beam*cos(theta_1+theta_2/2.)), ), point1=bprt.vertices.findAt((bbf_pnt1), ), point2=bprt.vertices.findAt((bbf_pnt2), ) ) else: # Create the sketch for partitioning the curved part of the column partition_transform = bprt.MakeSketchTransform( sketchPlane=bprt.faces.findAt( ( b_start, bbf2+r_m_beam*sin(pi/4), bhf2+r_m_beam*cos(pi/4) ), ), sketchPlaneSide=SIDE1, sketchUpEdge=bprt.edges.findAt((b_start, bbf2 + r_m_beam, bhf2), ), sketchOrientation=RIGHT, origin=(b_start, bbf2, bhf2) ) partition_sktch = mdl.ConstrainedSketch( name='partition_sktch2', sheetSize=cht2, transform=partition_transform ) bprt.projectReferencesOntoSketch( filter=COPLANAR_EDGES, sketch=partition_sktch ) spline1 = partition_sktch.Spline( points=( (-r_o_beam, 0), (-r_m_beam*cos(pi/4), -r_m_beam*sin(pi/4)), (0, -r_i_beam), ) ) partition_sktch.TangentConstraint( entity1=partition_sktch.geometry.findAt( (-r_o_beam, 1.), ), entity2=spline1 ) partition_sktch.TangentConstraint( entity1=partition_sktch.geometry.findAt( (1., -r_i_beam), ), entity2=spline1 ) # Perform the partitioning bprt.PartitionFaceBySketchThruAll( faces=bprt.faces.findAt( ( b_start, bbf2+r_m_beam*sin(pi/4), bhf2+r_m_beam*cos(pi/4) ), ), sketch=partition_sktch, sketchPlane=bprt.faces.findAt( ( b_start, bbf2+r_m_beam*sin(pi/4), bhf2+r_m_beam*cos(pi/4) ), ), sketchPlaneSide=SIDE1, sketchUpEdge=bprt.edges.findAt( ( b_start, bbf2 + r_m_beam, bhf2 ), ) ) # Get the newly created edges. First, collect useful points. # Three endpoints of the two consecutive edges of the inner weld-root path on the # column (counterclockwise) pnt1 = (chf2+y_arc_start, cbf2+x_arc_start, bhf2) pnt2 = (cht2, cbf2, bhf2+sqrt(r_o_beam**2-(cbf2-bbf2)**2)) if bbf2 < cbf2: pnt3 = (cht2, bbf2+r_o_beam*sin(theta_1), bhf2+r_o_beam*cos(theta_1)) else: pnt3 = pnt2 # Endpoints of the path between the two weld cross-sections on # the curved corner of the beam bc1_pnt = (cht2 + w_foot, bbf2+r_o_beam*sin(theta_1), bhf2+r_o_beam*cos(theta_1)) bc2_pnt = (chf2 + y_edge+w_side, bbf2+r_o_beam, bhf2) # Path at the bottom of the beam bbf_path = self.common_edge(bbf_pnt1, bbf_pnt2) # Inner path on the column curved face (weld root path). Consists of 2 parts. cc1_inner = [self.common_edge(pnt1, pnt2), ] if bbf2 < cbf2: cc1_inner = cc1_inner + [self.common_edge(pnt2, pnt3)] cc1_inner = tuple(cc1_inner) # Get the two weld paths that live in the gap between the beam and column gp1_path = self.common_edge(pnt1, bbf_pnt2) gp2_path = self.common_edge(pnt3, bbf_pnt1) # Loft the weld root face that lives in the beam-column gap bprt.ShellLoft( globalSmoothing=ON, loftsections=( (bbf_path,), cc1_inner ), paths=( (gp2_path,), (gp1_path,) ) ) # Get the path between the two welds on the curved corner of # the beam bc_path = self.common_edge(bc1_pnt, bc2_pnt) # Outer path on the column curved face (weld foot path) cc_outer = self.common_path( wf_pnt, (cht2, bbf2+r_wfoot*sin(theta_1), bhf2+r_wfoot*cos(theta_1)) ) # Get the edges of the two weld cross-sections (flange and web # welds) to be used as loft paths. wf1_path = self.common_edge( bc1_pnt, (cht2, bbf2+r_wfoot*sin(theta_1), bhf2+r_wfoot*cos(theta_1)) ) wf2_path = self.common_edge(bc2_pnt, wf_pnt) # Loft the weld front face. bprt.ShellLoft( loftsections=( (bc_path,), cc_outer ), paths=( (wf1_path,), (wf2_path,) ) ) # Convert the faces that form the last part of the weld corner # to a solid bprt.AddCells( faceList=bprt.faces.getByBoundingBox( min(wf_pnt[0], bc1_pnt[0])-0.1, min(wf_pnt[1], bc1_pnt[1])-0.1, min(wf_pnt[2], bc1_pnt[2])-0.1, max(wf_pnt[0], bc1_pnt[0])+0.1, max(wf_pnt[1], bc1_pnt[1])+0.1, max(wf_pnt[2], bc1_pnt[2])+0.1, ) ) bprt.PartitionCellByPatchNEdges( cell=bprt.cells.findAt(( b_start+w_foot, bbf2+r_i_beam*sin(theta_1 + theta_2/2.), bhf2 + r_i_beam * cos(theta_1 + theta_2 / 2.) ), ), edges=( self.common_edge( (cht2+w_foot, bbf2+r_o_beam*sin(theta_1), bhf2+r_o_beam*cos(theta_1)), (cht2+w_foot, bbf2+r_i_beam*sin(theta_1), bhf2+r_i_beam*cos(theta_1)) ), self.common_edge( (cht2+w_foot, bbf2+r_i_beam*sin(theta_1), bhf2+r_i_beam*cos(theta_1)), (b_start+w_side, bbt2-t_beam, bhf2) ), self.common_edge( (b_start+w_side, bbt2-t_beam, bhf2), (b_start+w_side, bbt2, bhf2) ), self.common_edge( (b_start+w_side, bbt2, bhf2), (cht2+w_foot, bbf2+r_o_beam*sin(theta_1), bhf2+r_o_beam*cos(theta_1)) ), ) ) if not cbt2==bbt2: bprt.PartitionCellBySweepEdge( cells=bprt.cells.findAt((( b_start, bbf2+(r_o_beam+w_foot/2.)*sin(theta_1/2.), bhf2+(r_o_beam+w_foot/2.)*cos(theta_1/2.) ),)), edges=( bprt.edges.findAt(( cht2 + w_foot, bbf2+r_o_beam*sin(theta_1/2.), bhf2+r_o_beam*cos(theta_1/2.) ), ), ), sweepPath=bprt.edges.findAt((cht2 + w_foot/2., bbf2, bht2), ) ) bprt.PartitionCellBySweepEdge( cells=bprt.cells.findAt(((b_start, bbt2, bhf2/2.),)), edges=( bprt.edges.findAt(( b_start+w_side/2., bbt2, bhf2 ), ), bprt.edges.findAt(( b_start, bbt2-w_unde/2., bhf2 ), ), ), sweepPath=bprt.edges.findAt((b_start+w_side, bbt2, bhf2/2.), ) ) # Delete the auxiliary column face that was created to assist # the creation of the weld x_aux = x_arc_start / 2. y_aux = sqrt(r_o_column**2 - x_aux**2) # Remove auxiliary column faces bprt.RemoveFaces( deleteCells=False, faceList=( bprt.faces.findAt((cht2, cbf2/2., 0.9*l_column), ), bprt.faces.findAt( ( chf2+r_o_column*cos(pi/4.), cbf2+r_o_column*sin(pi/4.), 0.9*l_column ), ), bprt.faces.findAt( ( chf2+r_o_column*cos(pi/4.), cbf2+r_o_column*sin(pi/4.), bht2 + w_foot ), ), bprt.faces.findAt((chf2+y_aux, cbf2+x_aux, bhf2/2), ), bprt.faces.findAt((chf2/2., cbt2, 0.9*l_column), ), bprt.faces.findAt((cht2, cbf2/2., bhf2), ), bprt.faces.findAt((cht2, cbf2/2., bhf2/2), ), bprt.faces.findAt((chf2/2., cbt2, bhf2/2), ), ) ) # Mirror everything to create the other half symmetric part. bprt.Mirror( keepOriginal=ON, mirrorPlane=mid_datxy ) # Partition everything at the symmmetry plane bprt.PartitionCellByPlaneThreePoints( cells=bprt.cells[:], point1=(0., 0., 0.), point2=(1., 0., 0.), point3=(0., 1., 0.) ) # Mirror parts to create the other half symmetric bprt.Mirror( keepOriginal=ON, mirrorPlane=mid_datxz ) # Partition everything at the symmmetry plane bprt.PartitionCellByPlaneThreePoints( cells=bprt.cells[:], point1=(0., 0., 0.), point2=(1., 0., 0.), point3=(0., 0., 1.) ) # Extra partitioning for creating nodes at the exact locations # of the weld-measuring DIC targets if self.measure_weld: bprt.PartitionCellByDatumPlane( datumPlane=beam_w_top_datxy, cells=bprt.cells[:] ) bprt.PartitionCellByDatumPlane( datumPlane=beam_w_bot_datxy, cells=bprt.cells[:] ) bprt.PartitionCellByDatumPlane( datumPlane=beam_w_datyz, cells=bprt.cells[:] ) # Assign meshing techniques and element types # Assign tetrahedrons for the weld seam bprt.setMeshControls( elemShape=TET, regions=bprt.cells.findAt( ((b_start, bbf2 / 2., bht2 + w_foot / 2.),), ((b_start, bbf2 / 2., -bht2 - w_foot / 2.),), ((b_start-b_gap/2., bbt2, bhf2/2),), ((b_start-b_gap/2., bbt2, -bhf2/2),), ((b_start-b_gap/2., bbt2, bhf2-0.1),), ((b_start-b_gap/2., bbt2, -bhf2+0.1),), ( ( b_start-b_gap/2., bbf2 + r_o_beam*cos(theta_2/2.), bhf2 + r_o_beam*sin(theta_2/2.) ), ), ( ( b_start-b_gap/2., bbf2 + r_o_beam*cos(theta_2/2.), -bhf2 - r_o_beam*sin(theta_2/2.) ), ), ( ( b_start+w_foot/4., bbf2 + r_m_beam*cos(theta_2/2.), bhf2 + r_m_beam*sin(theta_2/2.) ), ), ( ( b_start+w_foot/4., bbf2 + r_m_beam*cos(theta_2/2.), -bhf2 - r_m_beam*sin(theta_2/2.) ), ), ((b_start, -bbf2 / 2., bht2 + w_foot / 2.),), ((b_start, -bbf2 / 2., -bht2 - w_foot / 2.),), ((b_start-b_gap/2., -bbt2, bhf2/2),), ((b_start-b_gap/2., -bbt2, -bhf2/2),), ( ( b_start-b_gap/2., -bbf2 - r_o_beam*cos(theta_2/2.), bhf2 + r_o_beam*sin(theta_2/2.) ), ), ( ( b_start-b_gap/2., -bbf2 - r_o_beam*cos(theta_2/2.), -bhf2 - r_o_beam*sin(theta_2/2.) ), ), ( ( b_start+w_foot/4., -bbf2 - r_m_beam*cos(theta_2/2.), bhf2 + r_m_beam*sin(theta_2/2.) ), ), ( ( b_start+w_foot/4., -bbf2 - r_m_beam*cos(theta_2/2.), -bhf2 - r_m_beam*sin(theta_2/2.) ), ), ), technique=FREE ) bprt.setElementType( elemTypes=( ElemType( lemCode=C3D4, elemLibrary=EXPLICIT ), ), regions=(bprt.cells.findAt( ((b_start, bbf2 / 2., bht2 + w_foot / 2.),), ((b_start, bbf2 / 2., -bht2 - w_foot / 2.),), ((b_start-b_gap/2., bbt2, bhf2/2),), ((b_start-b_gap/2., bbt2, -bhf2/2),), (( b_start-b_gap/2., bbf2 + r_o_beam*cos(theta_2/2.), bhf2 + r_o_beam*sin(theta_2/2.) ),), (( b_start-b_gap/2., bbf2 + r_o_beam*cos(theta_2/2.), -bhf2 - r_o_beam*sin(theta_2/2.) ),), (( b_start+w_side/4., bbf2 + r_m_beam*cos(theta_2/2.), bhf2 + r_m_beam*sin(theta_2/2.) ),), (( b_start+w_side/4., bbf2 + r_m_beam*cos(theta_2/2.), -bhf2 - r_m_beam*sin(theta_2/2.) ),), ),), ) # Having modelled and meshed the beam which contained the complicated # geometry of the weld, the column part is created column_sweep_path = self.rhs_sketcher( self.h_column, self.b_column, self.t_column, r_m_column, name="cface_sweep_path", ) column_sweep_cs = mdl.ConstrainedSketch( name='cface_sweep_cs', sheetSize=200.0 ) column_sweep_cs.rectangle( point1=(-self.t_column / 2., 0.), point2=(self.t_column / 2., lc_solid2) ) cprt.BaseSolidSweep( path=column_sweep_path, sketch=column_sweep_cs ) # Mirror everything to create the other half symmetric part of the # column. cprt.Mirror( keepOriginal=ON, mirrorPlane=mid_datxy_col ) # Mirror parts to create the other half symmetric cprt.Mirror( keepOriginal=ON, mirrorPlane=mid_datxz_col ) # Partition everything at the symmmetry plane cprt.PartitionCellByPlaneThreePoints( cells=cprt.cells[:], point1=(0., 0., 0.), point2=(1., 0., 0.), point3=(0., 0., 1.) ) # Extra partitioning for creating nodes at the exact locations # of the weld-measuring DIC targets if self.measure_weld: cprt.PartitionCellByDatumPlane( datumPlane=col_w_top_datxy, cells=cprt.cells[:] ) cprt.PartitionCellByDatumPlane( datumPlane=col_w_bot_datxy, cells=cprt.cells[:] ) cprt.PartitionCellByDatumPlane( datumPlane=col_w_datyz, cells=cprt.cells[:] ) # Partition to separate the cold formed corners from the flat faces cprt.PartitionCellByPlaneThreePoints( cells=cprt.cells[:], point1=(chf2, 0., 0.), point2=(chf2, 1., 0.), point3=(chf2, 0., 1.) ) cprt.PartitionCellByPlaneThreePoints( cells=cprt.cells[:], point1=(-chf2, 0., 0.), point2=(-chf2, 1., 0.), point3=(-chf2, 0., 1.) ) cprt.PartitionCellByPlaneThreePoints( cells=cprt.cells[:], point1=(0., cbf2, 0.), point2=(1., cbf2, 0.), point3=(1., cbf2, 1.) ) cprt.PartitionCellByPlaneThreePoints( cells=cprt.cells[:], point1=(0., -cbf2, 0.), point2=(1., -cbf2, 0.), point3=(1., -cbf2, 1.) ) # Create shell parts to extend the column and beam parts with # inexpensive elements. if not self.no_column_shell: lc_shell = (l_column/2.) - lc_solid2 - 45 csprt.BaseShellExtrude( depth=lc_shell, sketch=column_sweep_path ) # Perform cuts at poi's for i in self.s_gauges["column"].values(): if i[0]>0: self.cross_cut_shell( csprt, (i[1], i[0]-lc_solid2), (1, 0, 0) ) else: self.cross_cut_shell( csprt, (-i[1], -i[0]-lc_solid2), (1, 0, 0) ) for i in self.targets["column"].values(): if i[0]>0: self.cross_cut_shell( csprt, (i[1], i[0]-lc_solid2), (0, 1, 0) ) else: self.cross_cut_shell( csprt, (i[1], -i[0]-lc_solid2), (0, 1, 0) ) csprt.Mirror( keepOriginal=ON, mirrorPlane=mid_datxz_scolumn ) if not self.no_beam_shell: bsprt.BaseShellExtrude( depth=l_beam - lb_solid, sketch=beam_sweep_path ) # Mirror everything to create the other half symmetric part. bsprt.Mirror( keepOriginal=ON, mirrorPlane=mid_datxy_sbeam ) # Perform cuts at poi's for i in self.s_gauges["beam"].values(): self.cross_cut_shell( bsprt, (i[1]-lb_solid, i[0]), (0, 0, 1) ) for i in self.targets["beam"].values(): self.cross_cut_shell( bsprt, (i[1]-lb_solid, i[0]), (0, 1, 0) ) bsprt.Mirror( keepOriginal=ON, mirrorPlane=mid_datxz_sbeam ) ### SEEDING AND MESHING # Do the seeding and generate the mesh for all parts. # Set mesh seeding bprt.seedPart( deviationFactor=0.1, minSizeFactor=0.1, size=t_column / self.size_factor ) # Generate the mesh bprt.generateMesh() # Set elements for the solid beam part if self.node20: bprt.setElementType( elemTypes=( ElemType( elemCode=C3D20R, elemLibrary=STANDARD ), ElemType( elemCode=C3D15, elemLibrary=STANDARD ), ElemType( elemCode=C3D10, elemLibrary=STANDARD ) ), regions=( bprt.cells[:], ) ) # Set mesh seeding cprt.seedPart( deviationFactor=0.1, minSizeFactor=0.1, size=t_column / self.size_factor ) # Generate the mesh cprt.generateMesh() # Set elements for the solid beam part if self.node20: cprt.setElementType( elemTypes=( ElemType( elemCode=C3D20R, elemLibrary=STANDARD ), ElemType( elemCode=C3D15, elemLibrary=STANDARD ), ElemType( elemCode=C3D10, elemLibrary=STANDARD ) ), regions=( cprt.cells[:], ) ) # Set mesh seeding. The shell parts have a coarser mesh than the solid. if not self.no_column_shell: csprt.seedPart( deviationFactor=0.1, minSizeFactor=0.1, size=8*t_column / self.size_factor ) # Generate the mesh csprt.generateMesh() # Set mesh seeding if not self.no_beam_shell: bsprt.seedPart( deviationFactor=0.1, minSizeFactor=0.1, size=8*t_column / self.size_factor ) # Generate the mesh bsprt.generateMesh() ### MATERIALS AND SECTIONS # Create a material # column, face opposite to the weld mat_col_opp = mdl.Material(name=self.names["mat_col_opp"]) mat_col_opp.Elastic(**self.mat_dict_col_opp["elasticity"]) mat_col_opp.Plastic(**self.mat_dict_col_opp["plasticity"]) mat_col_opp.Density(**self.mat_dict_col_opp["density"]) # column, face adjacent to the weld mat_col_adj = mdl.Material(name=self.names["mat_col_adj"]) mat_col_adj.Elastic(**self.mat_dict_col_adj["elasticity"]) mat_col_adj.Plastic(**self.mat_dict_col_adj["plasticity"]) mat_col_adj.Density(**self.mat_dict_col_adj["density"]) # cold formed column mat_cf_col = mdl.Material(name=self.names["mat_cf_col"]) mat_cf_col.Elastic(**self.material_dict_cf_column["elasticity"]) mat_cf_col.Plastic(**self.material_dict_cf_column["plasticity"]) mat_cf_col.Density(**self.material_dict_cf_column["density"]) # Beam, face opposite to the weld mat_beam_opp = mdl.Material(name=self.names["mat_beam_opp"]) mat_beam_opp.Elastic(**self.mat_dict_beam_opp["elasticity"]) mat_beam_opp.Plastic(**self.mat_dict_beam_opp["plasticity"]) mat_beam_opp.Density(**self.mat_dict_beam_opp["density"]) # Beam, face adjacent to the weld mat_beam_adj = mdl.Material(name=self.names["mat_beam_adj"]) mat_beam_adj.Elastic(**self.mat_dict_beam_adj["elasticity"]) mat_beam_adj.Plastic(**self.mat_dict_beam_adj["plasticity"]) mat_beam_adj.Density(**self.mat_dict_beam_adj["density"]) # Cold formed Beam mat_cf_beam = mdl.Material(name=self.names["mat_cf_beam"]) mat_cf_beam.Elastic(**self.material_dict_cf_beam["elasticity"]) mat_cf_beam.Plastic(**self.material_dict_cf_beam["plasticity"]) mat_cf_beam.Density(**self.material_dict_cf_beam["density"]) # Create the sections # Solid section mdl.HomogeneousSolidSection( material=self.names["mat_col_opp"], name=self.names["col_solid_section_opp"], thickness=None ) mdl.HomogeneousSolidSection( material=self.names["mat_col_adj"], name=self.names["col_solid_section_adj"], thickness=None ) mdl.HomogeneousSolidSection( material=self.names["mat_cf_col"], name=self.names["col_cf_solid_section"], thickness=None ) mdl.HomogeneousSolidSection( material=self.names["mat_beam_opp"], name=self.names["beam_solid_section_opp"], thickness=None ) mdl.HomogeneousSolidSection( material=self.names["mat_beam_adj"], name=self.names["beam_solid_section_adj"], thickness=None ) mdl.HomogeneousSolidSection( material=self.names["mat_cf_beam"], name=self.names["beam_cf_solid_section"], thickness=None ) # Shell sections mdl.HomogeneousShellSection( idealization=NO_IDEALIZATION, integrationRule=SIMPSON, material=self.names["mat_col_opp"], name='col_shell_section_opp', numIntPts=5, poissonDefinition=DEFAULT, preIntegrate=OFF, temperature= GRADIENT, thickness=t_column, thicknessField='', thicknessModulus=None, thicknessType=UNIFORM, useDensity=OFF ) mdl.HomogeneousShellSection( idealization=NO_IDEALIZATION, integrationRule=SIMPSON, material=self.names["mat_col_adj"], name='col_shell_section_adj', numIntPts=5, poissonDefinition=DEFAULT, preIntegrate=OFF, temperature= GRADIENT, thickness=t_column, thicknessField='', thicknessModulus=None, thicknessType=UNIFORM, useDensity=OFF ) mdl.HomogeneousShellSection( idealization=NO_IDEALIZATION, integrationRule=SIMPSON, material=self.names["mat_cf_col"], name='col_cf_shell_section', numIntPts=5, poissonDefinition=DEFAULT, preIntegrate=OFF, temperature= GRADIENT, thickness=t_column, thicknessField='', thicknessModulus=None, thicknessType=UNIFORM, useDensity=OFF ) mdl.HomogeneousShellSection( idealization=NO_IDEALIZATION, integrationRule=SIMPSON, material=self.names["mat_beam_opp"], name='beam_shell_section_opp' , numIntPts=5, poissonDefinition=DEFAULT, preIntegrate=OFF, temperature= GRADIENT, thickness=t_beam, thicknessField='', thicknessModulus=None, thicknessType=UNIFORM, useDensity=OFF ) mdl.HomogeneousShellSection( idealization=NO_IDEALIZATION, integrationRule=SIMPSON, material=self.names["mat_beam_adj"], name='beam_shell_section_adj' , numIntPts=5, poissonDefinition=DEFAULT, preIntegrate=OFF, temperature= GRADIENT, thickness=t_beam, thicknessField='', thicknessModulus=None, thicknessType=UNIFORM, useDensity=OFF ) mdl.HomogeneousShellSection( idealization=NO_IDEALIZATION, integrationRule=SIMPSON, material=self.names["mat_cf_beam"], name='beam_cf_shell_section' , numIntPts=5, poissonDefinition=DEFAULT, preIntegrate=OFF, temperature= GRADIENT, thickness=t_beam, thicknessField='', thicknessModulus=None, thicknessType=UNIFORM, useDensity=OFF ) # Assign the sections # The entire solid part of the beam and the weld are assigned one # section. For more detailed modelling (different sections for the # adjacent and the opposite faces etc.) they have to be created # afterwards from the gui. bprt.Set( cells=bprt.cells[:], name='all_Set' ) bprt.SectionAssignment( offset=0.0, offsetField='', offsetType=MIDDLE_SURFACE, region=bprt.sets['all_Set'], sectionName=self.names["beam_solid_section_opp"], thicknessAssignment=FROM_SECTION ) # Create sets and assign sections to the solid column part cprt.Set( cells=cprt.cells.getByBoundingBox( cht2-t_column-0.1, -cbf2-0.1, -lc_solid2-0.1, cht2+0.1, cbf2+0.1, lc_solid2+0.1 ), name='col_solid_opp_set' ) cprt.Set( cells=cprt.cells.getByBoundingBox( -cht2-0.1, -cbf2-0.1, -lc_solid2-0.1, -cht2+t_column+0.1, cbf2+0.1, lc_solid2+0.1 )+cprt.cells.getByBoundingBox( -chf2-0.1, cbt2-t_column-0.1, -lc_solid2-0.1, chf2+0.1, cbt2+0.1, lc_solid2+0.1 )+cprt.cells.getByBoundingBox( -chf2-0.1, -cbt2-0.1, -lc_solid2-0.1, chf2+0.1, -cbt2+t_column+0.1, lc_solid2+0.1 ), name='col_solid_adj_set' ) cprt.Set( cells=cprt.cells.getByBoundingBox( -cht2, cbf2, -lc_solid2, -chf2, cbt2, lc_solid2 )+cprt.cells.getByBoundingBox( chf2, cbf2, -lc_solid2, cht2, cbt2, lc_solid2 )+cprt.cells.getByBoundingBox( chf2, -cbt2, -lc_solid2, cht2, -cbf2, lc_solid2 )+cprt.cells.getByBoundingBox( -cht2, -cbt2, -lc_solid2, -chf2, -cbf2, lc_solid2 ), name='col_solid_cf_set' ) cprt.SectionAssignment( offset=0.0, offsetField='', offsetType=MIDDLE_SURFACE, region=cprt.sets['col_solid_adj_set'], sectionName=self.names["col_solid_section_adj"], thicknessAssignment=FROM_SECTION ) cprt.SectionAssignment( offset=0.0, offsetField='', offsetType=MIDDLE_SURFACE, region=cprt.sets['col_solid_opp_set'], sectionName=self.names["col_solid_section_opp"], thicknessAssignment=FROM_SECTION ) cprt.SectionAssignment( offset=0.0, offsetField='', offsetType=MIDDLE_SURFACE, region=cprt.sets['col_solid_cf_set'], sectionName=self.names["col_cf_solid_section"], thicknessAssignment=FROM_SECTION ) # Assign section to the shell parts if not self.no_column_shell: csprt.Set( faces=csprt.faces.getByBoundingBox( cht2-t_column-0.1, -cbf2-0.1, 0, cht2+0.1, cbf2+0.1, lc_shell ), name='col_shell_opp_set' ) csprt.Set( faces=csprt.faces.getByBoundingBox( -cht2-0.1, -cbf2-0.1, 0, -cht2+t_column+0.1, cbf2+0.1, lc_shell )+csprt.faces.getByBoundingBox( -chf2-0.1, cbt2-t_column-0.1, 0, chf2+0.1, cbt2+0.1, lc_shell )+csprt.faces.getByBoundingBox( -chf2-0.1, -cbt2-0.1, 0, chf2+0.1, -cbt2+t_column+0.1, lc_shell ), name='col_shell_adj_set' ) csprt.Set( faces=csprt.faces.getByBoundingBox( -cht2, cbf2, 0, -chf2, cbt2, lc_shell )+csprt.faces.getByBoundingBox( chf2, cbf2, 0, cht2, cbt2, lc_shell )+csprt.faces.getByBoundingBox( chf2, -cbt2, 0, cht2, -cbf2, lc_shell )+csprt.faces.getByBoundingBox( -cht2, -cbt2, 0, -chf2, -cbf2, lc_shell ), name='col_shell_cf_set' ) csprt.SectionAssignment( offset=0.0, offsetField='', offsetType=MIDDLE_SURFACE, region=csprt.sets['col_shell_opp_set'], sectionName='col_shell_section_opp', thicknessAssignment=FROM_SECTION ) csprt.SectionAssignment( offset=0.0, offsetField='', offsetType=MIDDLE_SURFACE, region=csprt.sets['col_shell_adj_set'], sectionName='col_shell_section_adj', thicknessAssignment=FROM_SECTION ) csprt.SectionAssignment( offset=0.0, offsetField='', offsetType=MIDDLE_SURFACE, region=csprt.sets['col_shell_cf_set'], sectionName='col_cf_shell_section', thicknessAssignment=FROM_SECTION ) if not self.no_beam_shell: bsprt.Set( faces=bsprt.faces[:], name='shell_set' ) bsprt.SectionAssignment( offset=0.0, offsetField='', offsetType=MIDDLE_SURFACE, region=bsprt.sets['shell_set'], sectionName='beam_shell_section_opp', thicknessAssignment=FROM_SECTION ) ### ASSEMBLY asmbl = mdl.rootAssembly asmbl.DatumCsysByDefault(CARTESIAN) # Create the part instances inst = asmbl.Instance( dependent=ON, name=self.names["b_instance"], part=bprt ) inst2 = asmbl.Instance( dependent=ON, name=self.names["c_instance"], part=cprt ) if not self.no_beam_shell: inst_bs = asmbl.Instance( dependent=ON, name=self.names["bs_inst"], part=bsprt ) if not self.no_column_shell: inst_cs_low = asmbl.Instance( dependent=ON, name=self.names["cs_low_inst"], part=csprt ) if not self.no_column_shell: inst_cs_high = asmbl.Instance( dependent=ON, name=self.names["cs_high_inst"], part=csprt ) # Translate and rotate the shell parts to the right position asmbl.translate( instanceList=(self.names["cs_high_inst"], ), vector=(0.0, 0.0, lc_solid2) ) asmbl.translate( instanceList=(self.names["cs_low_inst"], ), vector=(0.0, 0.0, lc_solid2) ) asmbl.rotate( angle=180.0, axisDirection=(0.0, 1., 0.0), axisPoint=(0.0, 0.0, 0.0), instanceList=(self.names["cs_low_inst"], ) ) asmbl.rotate( angle=180.0, axisDirection=(0.0, 0.0, 1.), axisPoint=(0.0, 0.0, 0.0), instanceList=(self.names["cs_low_inst"], ) ) asmbl.translate( instanceList=(self.names["bs_inst"], ), vector=(lb_solid, 0.0, 0.0) ) # Get the contact surface of the column asmbl.Surface( name='c_weld_surf', side1Faces=asmbl.instances['column_inst'].faces.findAt( ((cht2, cbf2/2., bhf2), ), ((cht2, cbf2/2., 0.), ), ((cht2, cbf2/2., -bhf2), ), ((cht2, -cbf2/2., bhf2), ), (( chf2+r_o_column*cos(pi/4.), cbf2+r_o_column*sin(pi/4.), bhf2 ), ), (( chf2+r_o_column*cos(pi/4.), cbf2+r_o_column*sin(pi/4.), 0. ), ), (( chf2+r_o_column*cos(pi/4.), cbf2+r_o_column*sin(pi/4.), -bhf2 ), ), (( chf2+r_o_column*cos(pi/4.), -cbf2-r_o_column*sin(pi/4.), bhf2 ), ), (( chf2+r_o_column*cos(pi/4.), -cbf2-r_o_column*sin(pi/4.), 0. ), ), (( chf2+r_o_column*cos(pi/4.), -cbf2-r_o_column*sin(pi/4.), -bhf2 ), ), ) ) # Get the faces of the weld that are in contact with the column using # the "getByAngle" method. w_contact_faces = asmbl.instances['beam_inst'].faces.findAt( ((cht2, bbf2/2., bht2+w_foot/2.), ) )[0].getFacesByFaceAngle(0) asmbl.Surface( name='b_weld_surf', side1Faces=w_contact_faces ) mdl.Tie( adjust=ON, master=asmbl.surfaces['c_weld_surf'], name='weld_tie', positionToleranceMethod=COMPUTED, slave=asmbl.surfaces['b_weld_surf'], thickness=ON, tieRotations=ON ) # Create sets for the cross sections to apply boundary conditions if self.no_beam_shell: b_front_face_set = asmbl.Set( faces=inst.faces.getByBoundingBox( cht2+b_gap+lb_solid-0.1, -bbt2-0.1, -bht2-0.1, cht2+b_gap+lb_solid+0.1, bbt2+0.1, bht2+0.1 ), name=self.set_names["b_front_face"] ) else: b_front_face_set = asmbl.Set( edges=inst_bs.edges.getByBoundingBox( self.l_lever-l_pin-0.1, -bbt2-0.1, -bht2-0.1, self.l_lever-l_pin+0.1, bbt2+0.1, bht2+0.1 ), name=self.set_names["b_front_face"] ) if self.no_column_shell: c_low_face_set = asmbl.Set( faces=inst2.faces.getByBoundingBox( -cht2-0.1, -cbt2-0.1, -l_column/2.-0.1, cht2+0.1, cbt2+0.1, -l_column/2.+0.1 ), name=self.set_names["c_low_face"] ) c_high_face_set = asmbl.Set( faces=inst2.faces.getByBoundingBox( -cht2-0.1, -cbt2-0.1, l_column/2.-0.1, cht2+0.1, cbt2+0.1, l_column/2.+0.1 ), name=self.set_names["c_high_face"] ) else: c_low_face_set = asmbl.Set( edges=inst_cs_low.edges.getByBoundingBox( -cht2-0.1, -cbt2-0.1, -l_column/2.+45-0.1, cht2+0.1, cbt2+0.1, -l_column/2.+45+0.1 ), name=self.set_names["c_low_face"] ) c_high_face_set = asmbl.Set( edges=inst_cs_high.edges.getByBoundingBox( -cht2-0.1, -cbt2-0.1, l_column/2.-45-0.1, cht2+0.1, cbt2+0.1, l_column/2.-45+0.1 ), name=self.set_names["c_high_face"] ) # Create reference points and their sets for the bcs b_rp = asmbl.referencePoints[asmbl.ReferencePoint( point=(self.l_lever, 0., 0, ) ).id] b_rp_set = asmbl.Set( name=self.set_names["b_rp"], referencePoints=(b_rp,) ) c_low_rp = asmbl.referencePoints[asmbl.ReferencePoint( point=(0., 0., -l_column/2.+6) ).id] c_low_rp_set = asmbl.Set( name=self.set_names["c_low_rp"], referencePoints=(c_low_rp,) ) c_high_rp = asmbl.referencePoints[asmbl.ReferencePoint( point=(0., 0., l_column/2.+6) ).id] c_high_rp_set = asmbl.Set( name=self.set_names["c_high_rp"], referencePoints=(c_high_rp,) ) # find the indices of all nodes we need history outputs for the DIC # targets. ho_nodes = [] sg_nodes = [] for i in self.targets["beam"].values(): ho_nodes.append( inst_bs.nodes.getByBoundingSphere( ( i[1], -bbt2+t_beam/2., i[0] ), (0.1) ) ) for i in self.targets["column"].values(): ho_nodes.append( inst_cs_high.nodes.getByBoundingSphere( ( i[1], -cbt2+t_column/2., i[0] ), (0.1) ) ) ho_nodes.append( inst_cs_low.nodes.getByBoundingSphere( ( i[1], -cbt2+t_column/2., i[0] ), (0.1) ) ) # Add extra nodes for history output nodes for the weld measurement if self.measure_weld: for i in ([55., -30.], [55., 30.]): ho_nodes.append( inst.nodes.getByBoundingSphere( ( i[0], -bbt2, i[1] ), (0.1) ) ) for i in ([40., -30.], [40., 30.]): ho_nodes.append( inst2.nodes.getByBoundingSphere( ( i[0], -cbt2, i[1] ), (0.1) ) ) # Do the same for the strain gauges. for i in self.s_gauges["beam"].values(): sg_nodes.append( inst_bs.nodes.getByBoundingSphere( ( i[1], -i[0], -bht2+t_beam/2. ), (0.1) ) ) sg_nodes.append( inst_bs.nodes.getByBoundingSphere( ( i[1], -i[0], bht2-t_beam/2. ), (0.1) ) ) for i in self.s_gauges["column"].values(): sg_nodes.append( inst_cs_high.nodes.getByBoundingSphere( ( -cht2+t_column/2, -i[1], i[0] ), (0.1) ) ) sg_nodes.append( inst_cs_low.nodes.getByBoundingSphere( ( -cht2+t_column/2, -i[1], i[0] ), (0.1) ) ) sg_nodes.append( inst_cs_high.nodes.getByBoundingSphere( ( cht2-t_column/2, -i[1], i[0] ), (0.1) ) ) sg_nodes.append( inst_cs_low.nodes.getByBoundingSphere( ( cht2-t_column/2, -i[1], i[0] ), (0.1) ) ) targets_set = asmbl.Set( name='targets_set', nodes=ho_nodes ) sg_set = asmbl.Set( name='sg_set', nodes=sg_nodes ) ## LOCAL AND GLOBAL BUCKLING IMPERFECTION # The following calculation if based on the proposed newer version of # the Eurocode. It is overwriten by the value of the current Eurocode. # According to prEN1993-1-1 beta_imp = 1/68. alfa_imp = sd.imp_factor("c") flex_imp = 1/( alfa_imp*beta_imp/self.joint_props.material["column"].epsilon ) # According to EN1993-1-1 # The flex_imp is the denominator of the imperfection amplitude, as # described in table 5.1 of EN1993-1-1 (e_0/L = 1/'flex_imp') # The first value produces one millimeter of displacement on the middle # of the specimen. This value is chosen as a realistic representation # of the experimens. No exact imperfection measurements were taken but # looking at the specimen with the laser, a rough estimation shows that # global bowing did not exceed 1mm. flex_imp = 750 # Alternatively, the 1/100 value can be used, which is the recommendid # value from EN1993-1-1 tb. 5.1 for buckling curve 'c' (cold formed # tubes). This prpoduces unrealistically large imperfections. #flex_imp = 150 flex_imperfection_amp = l_column / flex_imp asmbl.makeIndependent( instances = ( asmbl.instances['beam_inst'], asmbl.instances['column_inst'], asmbl.instances['beam_shell_inst'], asmbl.instances['column_low_shell_inst'], asmbl.instances['column_high_shell_inst'], ) ) if self.imperfections: for instance in asmbl.instances.items(): for j in range(len(instance[1].nodes)): xi = instance[1].nodes[j].coordinates[0] yi = instance[1].nodes[j].coordinates[1] zi = instance[1].nodes[j].coordinates[2] bow_imp = \ flex_imperfection_amp *\ cos(pi * zi / l_column) #(1 - abs(xi / l_beam)) asmbl.editNode( nodes = instance[1].nodes[j], offset1 = bow_imp, offset2 = bow_imp ) ### STEPS # In case the general static solver is requested, both beam loading and # column compression steps are performed as general static. if self.solver == "static": mdl.StaticStep( initialInc=1., name=self.names["init_load_step"], nlgeom=ON, previous='Initial', ) mdl.StaticStep( initialInc=0.5, name=self.names["b_load_step"], nlgeom=ON, previous=self.names["init_load_step"] ) mdl.StaticStep( initialInc=0.5, name=self.names["c_load_step"], nlgeom=ON, previous=self.names["b_load_step"], minInc=0.005 ) # Create the loading steps. First the column compression and then the # beam bending # If the RIKS solver is requested, it's used only fort he compression # of the column. elif self.solver == "riks": mdl.StaticStep( initialInc=1, name=self.names["init_load_step"], nlgeom=ON, previous='Initial' ) mdl.StaticStep( initialInc=1, name=self.names["b_load_step"], nlgeom=ON, previous='Initial' ) mdl.StaticRiksStep( initialArcInc=0.5, maxLPF=1, name=self.names["c_load_step"], previous=self.names["b_load_step"] ) elif self.solver == "explicit": # Fetch the average stable time increment from the mesh statistics. # This is used as target time increment for mass scaling. Elements # with smaller stable time increment will be mass-scaled to reach # this average target value. avg_stbl_inc = asmbl.verifyMeshQuality( STABLE_TIME_INCREMENT )["average"] # Create the step for the load application on the beam. mdl.ExplicitDynamicsStep( name=self.names["b_load_step"], previous='Initial', timePeriod=self.stp_time[0], linearBulkViscosity=self.lin_visc, quadBulkViscosity=self.quad_visc, massScaling=( ( SEMI_AUTOMATIC, MODEL, AT_BEGINNING, 0.0, avg_stbl_inc, BELOW_MIN, 0, 0, 0.0, 0.0, 0, None ), ) ) mdl.ExplicitDynamicsStep( name=self.names["c_load_step"], previous=self.names["b_load_step"], timePeriod=self.stp_time[1], linearBulkViscosity=self.lin_visc, quadBulkViscosity=self.quad_visc, massScaling= PREVIOUS_STEP, ) elif self.solver == "buckling": mdl.FrequencyStep( name=self.names["b_load_step"], numEigen=8, previous= 'Initial' ) # mdl.BuckleStep( # name = self.names["b_load_step"], # numEigen = 1, # previous = 'Initial', # vectors = 8, # maxIterations = 1000 # ) else: print("Wrong solver specifier") # Create a smooth amplitude for the load applications if self.solver == "explicit": mdl.SmoothStepAmplitude( data=( (0.0, 0.0), (self.stp_time[0]*0.8, 1.0), (self.stp_time[0], 1.0) ), name='ramp', timeSpan=STEP ) mdl.SmoothStepAmplitude( data=( (0.0, 0.0), (self.stp_time[1]/3., 1.), (2*self.stp_time[1]/3, 1.), (self.stp_time[1], 0.) ), name='smooth', timeSpan=STEP ) ## BCs AND LOADS # Create general boundary conditions. #mdl.YsymmBC( # createStepName='Initial', # localCsys=None, # name='y_symm_bc', # region=y_symm_set #) mdl.DisplacementBC( amplitude=UNSET, createStepName='Initial', distributionType=UNIFORM, fieldName='', localCsys=None, name='c_low_bc', region=c_low_rp_set, u1=SET, u2=SET, u3=SET, ur1=UNSET, ur2=UNSET, ur3=SET ) mdl.DisplacementBC( amplitude=UNSET, createStepName='Initial', distributionType=UNIFORM, fieldName='', localCsys=None, name='c_high_bc', region=c_high_rp_set, u1=SET, u2=SET, u3=UNSET, ur1=UNSET, ur2=UNSET, ur3=SET ) mdl.DisplacementBC( amplitude=UNSET, createStepName='Initial', distributionType=UNIFORM, fieldName='', localCsys=None, name='b_bc', region=b_rp_set, u1=UNSET, u2=UNSET, u3=UNSET, ur1=UNSET, ur2=UNSET, ur3=UNSET ) # Create coupling constraints between the reference control points and the cross sections mdl.Coupling( controlPoint=b_rp_set, couplingType=KINEMATIC, influenceRadius=WHOLE_SURFACE, localCsys=None, name='b_cplg', surface=b_front_face_set, u1=ON, u2=ON, u3=ON, ur1=ON, ur2=ON, ur3=ON ) mdl.Coupling( controlPoint=c_low_rp_set, couplingType=KINEMATIC, influenceRadius=WHOLE_SURFACE, localCsys=None, name='c_low_cplg', surface=c_low_face_set, u1=ON, u2=ON, u3=ON, ur1=ON, ur2=ON, ur3=ON ) mdl.Coupling( controlPoint=c_high_rp_set, couplingType=KINEMATIC, influenceRadius=WHOLE_SURFACE, localCsys=None, name='c_high_cplg', surface=c_high_face_set, u1=ON, u2=ON, u3=ON, ur1=ON, ur2=ON, ur3=ON ) # Apply sell to solid couplings between the extension shells and the # solid instances (if shell extensions exist) if not self.no_beam_shell: asmbl.Surface( name='b_contact_edge', side1Edges=inst_bs.edges.getByBoundingBox( cht2+b_gap+lb_solid-0.1, -bbt2-0.1, -bht2-0.1, cht2+b_gap+lb_solid+0.1, bbt2+0.1, bht2+0.1 ) ) asmbl.Surface( name='b_contact_face', side1Faces=inst.faces.getByBoundingBox( cht2+b_gap+lb_solid-0.1, -bbt2-0.1, -bht2-0.1, cht2+b_gap+lb_solid+0.1, bbt2+0.1, bht2+0.1 ) ) mdl.ShellSolidCoupling( name='b_contact_constraint', positionToleranceMethod=COMPUTED, shellEdge=asmbl.surfaces['b_contact_edge'], solidFace=asmbl.surfaces['b_contact_face']) if not self.no_column_shell: asmbl.Surface( name='c_contact_edge_low', side1Edges=inst_cs_low.edges.getByBoundingBox( -cht2-0.1, -cbt2-0.1, -lc_solid2-0.1, cht2+0.1, cbt2+0.1, -lc_solid2+0.1 ) ) asmbl.Surface( name='c_contact_face_low', side1Faces=inst2.faces.getByBoundingBox( -cht2-0.1, -cbt2-0.1, -lc_solid2-0.1, cht2+0.1, cbt2+0.1, -lc_solid2+0.1 ) ) mdl.ShellSolidCoupling( name='c_contact_constraint_low', positionToleranceMethod=COMPUTED, shellEdge=asmbl.surfaces['c_contact_edge_low'], solidFace=asmbl.surfaces['c_contact_face_low']) asmbl.Surface( name='c_contact_edge_high', side1Edges=inst_cs_high.edges.getByBoundingBox( -cht2-0.1, -cbt2-0.1, lc_solid2-0.1, cht2+0.1, cbt2+0.1, lc_solid2+0.1 ) ) asmbl.Surface( name='c_contact_face_high', side1Faces=inst2.faces.getByBoundingBox( -cht2-0.1, -cbt2-0.1, lc_solid2-0.1, cht2+0.1, cbt2+0.1, lc_solid2+0.1 ) ) mdl.ShellSolidCoupling( name='c_contact_constraint_high', positionToleranceMethod=COMPUTED, shellEdge=asmbl.surfaces['c_contact_edge_high'], solidFace=asmbl.surfaces['c_contact_face_high']) # Apply the loads if self.solver == "explicit": if self.column_load: mdl.ConcentratedForce( amplitude='ramp', cf3=self.beam_load, createStepName=self.names["b_load_step"], distributionType=UNIFORM, field='', localCsys=None, name='moment_load', region=b_rp_set ) # if self.axial: # mdl.ConcentratedForce( # amplitude='smooth', # cf3=self.compression, # createStepName=self.names["c_load_step"], # distributionType=UNIFORM, # field='', # localCsys=None, # name='axial_load', # region=c_high_rp_set # ) # Apply displacements. The compression displacement applied on the column is calculated based on the elastic # limit of the cross section in pure compression. Twice the displacement required to reach the theoretical # elastic limit is applied to ensure failure. # mdl.boundaryConditions['c_high_bc'].setValuesInStep( # amplitude='smooth', # stepName=self.names["c_load_step"], # u3=-2*self.calc_axial_disp() # ) mdl.VelocityBC( amplitude='smooth', createStepName= 'c_load_step', distributionType=UNIFORM, fieldName='', localCsys=None, name=self.names["velo_bc"], region= c_high_rp_set, v1=UNSET, v2= UNSET, v3=-self.disp_vel, vr1=UNSET, vr2=UNSET, vr3=UNSET ) elif (self.solver == "riks") or (self.solver == "static"): mdl.ConcentratedForce( cf3=-self.init_column_compression, createStepName=self.names["init_load_step"], distributionType=UNIFORM, field='', localCsys=None, name='init_compression', region=c_high_rp_set ) mdl.ConcentratedForce( cf3=-self.beam_load, createStepName=self.names["b_load_step"], distributionType=UNIFORM, field='', localCsys=None, name='beam_load', region=b_rp_set ) # The predicted resistance is multiplied by 1.3 to make sure that # we are going to reach some ultimate load mdl.ConcentratedForce( cf3=-self.column_load, createStepName=self.names["c_load_step"], distributionType=UNIFORM, field='', localCsys=None, name='axial_load', region=c_high_rp_set ) elif self.solver == "buckling": pass #mdl.ConcentratedForce( # cf3=1, # createStepName=self.names["b_load_step"], # distributionType=UNIFORM, # field='', # localCsys=None, # name='beam_load', # region=b_rp_set #) else: print("invalid solver definition") # Create outputs # History output for the axial loading step if not self.solver == "buckling": self.create_history( 'b_hist', self.names["init_load_step"], b_rp_set, ('CF3',) ) self.create_history( 'c_high_hist', self.names["init_load_step"], c_high_rp_set, ('U3', 'CF3') ) self.create_history( 'c_low_hist', self.names["init_load_step"], c_low_rp_set, ('RF3',) ) self.create_history( 'targets_hist', self.names["init_load_step"], targets_set, ('U1', 'U3') ) #self.create_history( # 'sg_hist', # self.names["init_load_step"], # sg_set, # ('S11', 'S22', 'S33', 'S12', 'S13', 'S23') #) if self.solver == "explicit": mdl.HistoryOutputRequest( createStepName='init_load_step', name= 'energies', variables=('ALLIE', 'ALLKE') ) # Delete the default outputs. del mdl.historyOutputRequests['H-Output-1'] del mdl.fieldOutputRequests['F-Output-1'] # Field Output for the axial loading step mdl.FieldOutputRequest( createStepName=self.names["init_load_step"], name='all_fields', variables=('S', 'MISES', 'E', 'PE', 'PEEQ', 'U') ) ## Create a set to act as a control point for the solver killer subroutine #killer_cp_name = 'RIKS_NODE' #asmbl.Set( # name=killer_cp_name, # nodes=(inst.nodes[1:2],) # ) ## Output the RIKS_NODE for GN_killer to work #self.mdl.keywordBlock.synchVersions(storeNodesAndElements=False) #self.mdl.keywordBlock.insert( # at.get_block_position(self.mdl, '*End Step')[-1] - 1, # '\n*NODE FILE, GLOBAL=YES, NSET='+killer_cp_name+'\nU' # ) # Look for the user subruting GN_killer sbrtn = None if self.solver=="riks": sbrtn = at.find("riks_terminator.f", "../../") logging.info("The subroutine file is: %s" %sbrtn) # Create the analysis job if (self.solver=="riks") or (self.solver=="static"): mdb.Job( atTime=None, contactPrint=OFF, description='', echoPrint=OFF, explicitPrecision=SINGLE, getMemoryFromAnalysis=True, historyPrint=OFF, memory=90, memoryUnits=PERCENTAGE, model=self.names["model"], modelPrint=OFF, multiprocessingMode=MPI, name=self.names["job"], nodalOutputPrecision=SINGLE, numCpus=self.ncpus, numDomains=self.ncpus, numGPUs=0, queue=None, resultsFormat=ODB, scratch='', type=ANALYSIS, userSubroutine=sbrtn, waitHours=0, waitMinutes=0 ) elif self.solver=="explicit": mdb.Job( activateLoadBalancing=False, atTime=None, contactPrint=OFF, description='', echoPrint=OFF, explicitPrecision=DOUBLE, historyPrint=OFF, memory=90, memoryUnits=PERCENTAGE, model=self.names["model"], modelPrint=OFF, multiprocessingMode=DEFAULT, name=self.names["job"], nodalOutputPrecision=SINGLE, numCpus=self.ncpus, numDomains=self.ncpus, parallelizationMethodExplicit=DOMAIN, queue=None, resultsFormat=ODB, scratch='', type=ANALYSIS, userSubroutine='', waitHours= 0, waitMinutes=0 ) elif self.solver == "buckling": mdb.Job( atTime=None, contactPrint=OFF, description='', echoPrint=OFF, explicitPrecision=SINGLE, getMemoryFromAnalysis=True, historyPrint=OFF, memory=90, memoryUnits=PERCENTAGE, model=self.names["model"], modelPrint=OFF, multiprocessingMode=MPI, name=self.names["job"], nodalOutputPrecision=SINGLE, numCpus=1, numDomains=1, numGPUs=0, queue=None, resultsFormat=ODB, scratch='', type=ANALYSIS, userSubroutine='', waitHours=0, waitMinutes=0 ) # Save, write inp and submit if self.save_cae: self.save() #if self.write_input: self.w_input() if self.direct_submit: logging.debug("Submitting") self.submit() logging.debug("Solver finished!") # Export results if (self.solver=="static") or (self.solver=="riks"): hist_results = self.export_results(self.names["job"]) logging.info(hist_results) return
[docs]def make_run_model( pl_class, beta_percent, moment_percent, l_column=None, c_width=None, f_yield=None, bl_ratio=None, calcs_only=None, ncpus=None, solver=None, direct_submit=None, id_string=None, **kargs ): # Defaults if l_column is None: l_column = 1102. if bl_ratio is None: bl_ratio = 0.015115 if ncpus is None: npcus=4 if solver is None: solver = "static" if f_yield is None: f_yield = 355. if c_width is None: c_width = 100. if calcs_only is None: calcs_only = False if direct_submit is None: direct_submit = False if id_string is None: id_string = "NA" epsilon = sqrt(235./f_yield) b_width = c_width * beta_percent/100. # An iterative procedure is needed to calculate the thickness # because the flat width depends on the corner radius which depends # on the thickness. c_thick = c_width/(epsilon*pl_class + 2.*2.) c_thick_new = 0 logging.info("Initial thickness: %.3f" % c_thick) tol = 1e-6 while abs(c_thick-c_thick_new) > tol: c_thick_new = c_thick r_out = sd.CsRHS.radius_from_thickness(c_thick_new) c_thick = (c_width - 2*r_out[0])/(epsilon*pl_class) logging.info("New thickness: %.3f" % c_thick) # Set the thickness of the beam independently # NOTE: Change this so that it is assigned automatically for our # experiments. b_thick = 4.7 column = (c_width, c_width, c_thick) beam = (b_width, b_width, b_thick) joint = TjointRHS( column=column, beam=beam, l_column=l_column, moment_percent=moment_percent, bl_ratio=bl_ratio, direct_submit=direct_submit, ncpus=ncpus, solver=solver, mdl_id=id_string, **kargs ) if not calcs_only: mdl = joint.modeler() return joint
[docs]def experiment(test_id): # Move to the directory for the job execution os.chdir("./fruits") catalogue = { "C1B10M70":{ "column": (100.1, 100.1, 3.58), "beam": (100.1, 100.1, 3.58), "l_column": 1102., "l_lever": 511., "a_weld": 3.5, "moment_percent": 70., }, "C1B10M90": { "column": (100.1, 100.1, 4.096), "beam": (100.1, 100.1, 4.096), "l_column": 800., "l_lever": 511., "a_weld": 3.5, "moment_percent": 90., "targets": { "beam": { "s06": (25., 331.), "s07": (-25., 331.), "s08": (25., 165.), "s09": (-25., 165.), }, "column": { "s00": (460, -30), "s01": (400, 30), "s02": (400, -30), "s12": (-400, -30), "s13": (-400, 30), "s14": (-460, 30), } }, "s_gauges": { "beam": { "B1": (15, 152), #"B2": (152, 15) }, "column": { "C1": (400, 16.7), #"C2": (), "C3": (-400, 16.7), #"C4": (), } } }, "C1B09M70": { "column": (100.1, 100.1, 4.096), "beam": (90.1, 90.1, 3.844), "l_column": 800., "l_lever": 511., "a_weld": 3.5, "moment_percent": 70., "targets": { "beam": { "s06": (25., 331.), "s07": (-25., 331.), "s08": (25., 165.), "s09": (-25., 165.), }, "column": { "s00": (460, -30), "s01": (400, 30), "s02": (400, -30), "s12": (-400, -30), "s13": (-400, 30), "s14": (-460, 30), } }, "s_gauges": { "beam": { "B1": (15, 152), #"B2": (152, 15) }, "column": { "C1": (400, 16.7), #"C2": (), "C3": (-400, 16.7), #"C4": (), } } }, "C1B09M90":{ "column": (100.1, 100.1, 3.73), "beam": (90.1, 90.1, 4.7), "l_column": 1102., "l_lever": 511., "a_weld": 3.0, "moment_percent": 93.295, "measure_weld": True, "targets": { "beam": { "s06": (25., 331.), "s07": (-25., 331.), "s08": (25., 165.), "s09": (-25., 165.), }, "column": { "s00": (460, -30), "s01": (400, 30), "s02": (400, -30), "s12": (-400, -30), "s13": (-400, 30), "s14": (-460, 30), } }, "s_gauges": { "beam": { "B1": (15, 152), #"B2": (152, 15) }, "column": { "C1": (400, 16.7), #"C2": (), "C3": (-400, 16.7), #"C4": (), } } }, "C2B10M70": { "column": (100., 100., 2.966), "beam": (100., 100., 2.966), "l_column": 800., "l_lever": 511., "a_weld": 3.5, "moment_percent": 70., "targets": { "beam": { "s06": (25., 331.), "s07": (-25., 331.), "s08": (25., 165.), "s09": (-25., 165.), }, "column": { "s00": (460, -30), "s01": (400, 30), "s02": (400, -30), "s12": (-400, -30), "s13": (-400, 30), "s14": (-460, 30), } }, "s_gauges": { "beam": { "B1": (15, 152), #"B2": (152, 15) }, "column": { "C1": (400, 16.7), #"C2": (), "C3": (-400, 16.7), #"C4": (), } } }, "C2B10M90": { "column": (100., 100., 2.966), "beam": (100., 100., 2.966), "l_column": 800., "l_lever": 511., "a_weld": 3.5, "moment_percent": 90., "targets": { "beam": { "s06": (25., 331.), "s07": (-25., 331.), "s08": (25., 165.), "s09": (-25., 165.), }, "column": { "s00": (460, -30), "s01": (400, 30), "s02": (400, -30), "s12": (-400, -30), "s13": (-400, 30), "s14": (-460, 30), } }, "s_gauges": { "beam": { "B1": (15, 152), #"B2": (152, 15) }, "column": { "C1": (400, 16.7), #"C2": (), "C3": (-400, 16.7), #"C4": (), } } }, "C2B09M70": { "column": (100., 100., 2.966), "beam": (90.1, 90.1, 3.844), "l_column": 800., "l_lever": 511., "a_weld": 3.5, "moment_percent": 70., "targets": { "beam": { "s06": (25., 331.), "s07": (-25., 331.), "s08": (25., 165.), "s09": (-25., 165.), }, "column": { "s00": (460, -30), "s01": (400, 30), "s02": (400, -30), "s12": (-400, -30), "s13": (-400, 30), "s14": (-460, 30), } }, "s_gauges": { "beam": { "B1": (15, 152), #"B2": (152, 15) }, "column": { "C1": (400, 16.7), #"C2": (), "C3": (-400, 16.7), #"C4": (), } } }, "C2B09M90": { "column": (100., 100., 2.966), "beam": (90.1, 90.1, 3.844), "l_column": 800., "l_lever": 511., "a_weld": 3.5, "moment_percent": 90., "targets": { "beam": { "s06": (25., 331.), "s07": (-25., 331.), "s08": (25., 165.), "s09": (-25., 165.), }, "column": { "s00": (460, -30), "s01": (400, 30), "s02": (400, -30), "s12": (-400, -30), "s13": (-400, 30), "s14": (-460, 30), } }, "s_gauges": { "beam": { "B1": (15, 152), #"B2": (152, 15) }, "column": { "C1": (400, 16.7), #"C2": (), "C3": (-400, 16.7), #"C4": (), } } } } specimen = TjointRHS( size_factor=3, imperfections=True, mdl_id=test_id, direct_submit=False, solver="static", **catalogue[test_id] ) mdl = specimen.modeler() # Return to the parent directory os.chdir("..") return(specimen)
[docs]def main(): """ The main function. It either executes the modeler parametrically for a range of values or just executes a single time for specific values. The parametric execution is activated if "parametric" is given as the last argument from the system. """ # Move to the directory for the job execution os.chdir("./fruits") # 2 types of execution, either parametric or single run. if sys.argv[-1]=="parametric": fe.run_factorial( sys.argv[-2], make_run_model, func_args=[ [20, 30, 40], #[20, 25, 30, 35, 40, 45, 50], [100, 90], #[100, 95, 90, 85], [10] #[10., 20., 30., 40.], ], func_kargs={ "ncpus": 1, "direct_submit": False, }, mk_subdirs=True, p_args=[0, 1, 2] ) else: # If the argument "parametric" is not found, execute a single model. pl_class = 27.25 beta_percent = 90. moment_percent = 10. joint = make_run_model( pl_class, # b/t beta_percent, # beta*100 moment_percent, # m_col*100, column util. ratio #id_string="%d-%d-%d" % (pl_class, beta_percent, moment_percent), imperfections=False, direct_submit=False, #(100., 100., 3.42857142857143), #Column #(85., 85., 4.), #Beam #1000., #Column length #10., #moment percent #0.0151157, #beam load permil ##0.043174, #beam load permil #direct_submit=False, #solver="static", #ncpus=4, calcs_only=False, #id_string="debuggui" bl_ratio=0.01515, id_string="C1B09M90" #id_string="C1B10M70" ) os.chdir("..")
#main() """ Things that I need to manually change after the generation of a model and before submitting. - Regions of the weld tie coupling (c_weld_surf). - Add moment for eccentricity at the column ends - Decrease the min inc step - Set parallelization """