mdl.add_nodes(nodes=[[5., -5., 0.], [5., 5., 0.], [-5., 5., 0.], [0., 0., 5.]]) # print('Node number 3:', mdl.nodes[3]) # print('Node number 3 xyz:', mdl.node_xyz(3)) # print('Node count: ', mdl.node_count()) # print('Node index: ', mdl.node_index) # print('Check node at [0, 0, 0]: ', mdl.check_node_exists([0, 0, 0])) # print('Check node at [5, 5, 0]: ', mdl.check_node_exists([5, 5, 0])) # print('Node bounds: ', mdl.node_bounds()) # Add elements mdl.add_elements(elements=[[0, 4], [1, 4], [2, 4], [3, 4]], type='BeamElement', axes={'ex': [1, 0, 0]}) mdl.add_element(nodes=[0, 1, 4], type='ShellElement') # print('Element 3 nodes: ', mdl.elements[3].nodes) # print('Element count: ', mdl.element_count()) # print('Element index: ', mdl.element_index) # print('Check element with nodes 1-4: ', mdl.check_element_exists([1, 4])) # print('Check element with nodes 0-1: ', mdl.check_element_exists([0, 1])) # Add sets mdl.add_set(name='nset_base', type='node', selection=[0, 1, 2, 3]) mdl.add_set(name='nset_top', type='node', selection=[4]) mdl.add_set(name='elset_beams', type='element', selection=[0, 1, 2, 3]) mdl.add_set(name='elset_shell', type='element', selection=[4]) # print('Set: nset_base: ', mdl.sets['nset_base'])
def compute_compas_fea(file_path, load_path, fea_engine='abaqus', recompute=True): """ Use abaqus (via compas_fea) to perform elastic FEA on the given frame under a given load case. If no load path is specified, elemental gravity will be assumbed to be applied. Parameters ---------- file_path : string full path to the frame shape's json file. load_path : type full path to the load case's json file. Returns ------- nD: dict Reactional nodal displacement key is the node id. value is (nodal_id, dx, dy, dz, theta_x, theta_y, theta_z). fR: dict Fixities reaction force, moment. key is the nodal id. value is [Fxyz, Mxyz] in the global axes. eR: dict Element-wise reaction force, moment (two ends). key is the element id. (Fxyz_1, Mxyz_1, Fxyz_2, Mxyz_2) """ root_dir = os.path.dirname(os.path.abspath(__file__)) temp_dir = os.path.join(root_dir, 'compas_fea-temp') if not os.path.exists(temp_dir): os.makedirs(temp_dir) file_json_name = file_path.split(os.sep)[-1] file_name = file_json_name.split('.')[0] print('compas_fea initing: file name {}'.format(file_name)) if not recompute: nD, fR, eR = parse_abaqus_result_json(file_name, temp_dir) return nD, fR, eR with open(file_path, 'r') as f: json_data = json.loads(f.read()) load_json_data = {} if load_path: with open(load_path, 'r') as f: load_json_data = json.loads(f.read()) # init an empty structure mdl = Structure(name=file_name, path=os.path.join(temp_dir, '')) # nodes mdl.add_nodes(nodes=parse_frame_nodes(json_data)) # elements elements = parse_elements(json_data) # align local axes with conmech sc = stiffness_checker(json_file_path=file_path, verbose=False) e_rot_mats = sc.get_element_local2global_rot_matrices() assert len(e_rot_mats) == len(elements) for e, mat in zip(elements, e_rot_mats): # compas_fea local axis convention is differrent to the one used in conmech: # in compas_fea # 'ex' axis represents the cross-section’s major axis # 'ey' is the cross-section’s minor axis # 'ez' is the axis along the element # TODO: this numpy array to list conversion # is essential to make compas_fea work... ez = list(mat[0][0:3]) # conmech longitude axis ex = list(mat[1][0:3]) # conmech cross sec major axis ey = list(mat[2][0:3]) # conmech cross sec minor axis mdl.add_element(nodes=e, type='BeamElement', axes={'ex': ex, 'ey': ey, 'ez': ez}) # print(mdl.elements[mdl.check_element_exists(nodes=e)]) assert_equal(mdl.element_count(), len(elements)) # Sets # just convenient aliases for referring to a group of elements mdl.add_set(name='elset_all', type='element', selection=list(range(mdl.element_count()))) mdl.add_set(name='nset_all', type='node', selection=list(range(mdl.node_count()))) fixities = parse_fixties(json_data) mdl.add_set(name='nset_fix', type='node', selection=[f[0] for f in fixities]) if load_json_data: pt_loads, include_sw = parse_load_case(load_json_data) # mdl.add_set(name='nset_pt_load', type='node', selection=[l[0] for l in pt_loads]) else: pt_loads = [] include_sw = True if pt_loads: mdl.add_set(name='nset_v_load_all', type='node', selection=[pl[0] for pl in pt_loads]) # Materials # Young’s modulus E [in units of Pa] # Poisson’s ratio v and density p [kg per cubic metre]. mat_json = json_data['material_properties'] mat_name = 'mat_' + mat_json['material_name'] E_scale = parse_pressure_scale_conversion(mat_json['youngs_modulus_unit']) p_scale = parse_density_scale_conversion(mat_json['density_unit']) mdl.add(ElasticIsotropic(name=mat_name, E=E_scale * mat_json['youngs_modulus'], v=mat_json['poisson_ratio'], p=p_scale * mat_json['density'])) # G_scale = parse_pressure_scale_conversion(mat_json['shear_modulus_unit']) # print('{}, {}'.format(mdl.materials['mat_' + mat_json['material_name']].G, G_scale * mat_json['shear_modulus'])) # assert_almost_equal(mdl.materials['mat_' + mat_json['material_name']].G['G'], G_scale * mat_json['shear_modulus']) # print('-----------material') # print(mdl.materials[mat_name]) # Sections # SI units should be used, this includes the use of metres m for cross-section dimensions, not millimetres mm. sec_name = 'sec_circ' mdl.add(CircularSection(name=sec_name, r=parse_circular_cross_sec_radius(json_data))) # print('-----------cross section') # print(mdl.sections[sec_name]) # Properties, associate material & cross sec w/ element sets mdl.add(Properties(name='ep_all', material=mat_name, section=sec_name, elset='elset_all')) # Displacements # pin supports for i, fix in enumerate(fixities): f_dof = [] for j in range(6): if fix[j+1] == 1: f_dof.append(0) else: f_dof.append(None) mdl.add(GeneralDisplacement(name='disp_fix_'+str(i), nodes=[fix[0]], x=f_dof[0], y=f_dof[1], z=f_dof[2], xx=f_dof[3], yy=f_dof[4], zz=f_dof[5])) # print('-----------fixities') # for i in range(len(fixities)): # print(mdl.displacements['disp_fix_'+str(i)]) # Loads if pt_loads: mdl.add([PointLoad(name='load_v_'+str(i), nodes=[pl[0]], x=pl[1], y=pl[2], z=pl[3], xx=pl[4], yy=pl[5], zz=pl[6]) for i, pl in enumerate(pt_loads)]) if include_sw: mdl.add(GravityLoad(name='load_gravity', elements='elset_all')) else: mdl.add(GravityLoad(name='load_gravity', elements='elset_all')) # print('-----------loads') # print(mdl.loads['load_gravity']) # for i in range(len(pt_loads)): # print(mdl.loads['load_v_'+str(i)]) # Steps loads_names = [] if pt_loads: loads_names.extend(['load_v_'+str(i) for i in range(len(pt_loads))]) if include_sw: loads_names.append('load_gravity') mdl.add([ GeneralStep(name='step_bc', displacements=['disp_fix_'+str(i) for i in range(len(fixities))]), GeneralStep(name='step_loads', loads=loads_names) ]) # a boundary condition step such as 'step_bc' above, should always be applied as the first step to prevent rigid body motion mdl.steps_order = ['step_bc', 'step_loads'] # Summary mdl.summary() # Run # node # 'u': nodal displacement: ux, uy, uz, um (magnitude) # 'ur': nodal rotation # 'rf': reaction force # 'cf': concentrated force (external load) # 'cm': concentrated moment (external load) # element # 's': beam stress (conmech cannot compute this at # version 0.1.1) # For beam, the following values are evaluated # at the "integration point" 'ip1' (middle point) # and pts along the axis: 'sp3, sp7, sp11, sp15' # sxx: axial # syy: hoop # sxy: torsion # smises: Von Mises # smaxp: max principal # sminp: min principal # 'sf': beam section force # sf1: axial # sf2: shear x # sf3: shear y if fea_engine == 'abaqus': mdl.analyse_and_extract(software='abaqus', fields=['u', 'ur', 'rf', 'rm', 'sf'], ndof=6, output=True) nD, fR, eR = parse_abaqus_result_json(file_name, temp_dir) elif fea_engine == 'opensees': mdl.analyse_and_extract(software='opensees', fields=['u'], exe=OPENSEES_PATH, ndof=6, output=True, save=True) raise NotImplementedError('opensees from compas_fea is not fully supported at this moment...') nD = {} fR = {} eR = {} # nD = mdl.get_nodal_results(step='step_load', field='ux', nodes='nset_all') print(mdl.results) else: raise NotImplementedError('FEA engine not supported!') return nD, fR, eR
__license__ = 'MIT License' __email__ = '*****@*****.**' # Structure mdl = Structure(name='shell_bench', path=compas_fea.TEMP) # Nodes and Elements xyz = [[0, 0, 0], [1, 0, 0], [2, 0, 0], [0, 1, 0], [1, 1, 0], [2, 1, 0], [0, 2, 0], [1, 2, 0], [2, 2, 0]] nodes = mdl.add_nodes(xyz) shells = [[0, 1, 4, 3], [1, 2, 5, 4], [3, 4, 7, 6], [4, 5, 8, 7]] shells = [mdl.add_element(shell, 'ShellElement') for shell in shells] # Sets elset_shells = mdl.add_set('elset_shells', 'element', shells) # Materials mdl.add(Steel(name='mat_steel')) # Sections mdl.add(ShellSection(name='sec_shell', t=0.01)) # Properties
from compas_fea.structure import RectangularSection from compas_fea.structure import PointLoad from compas_fea.structure import Steel from compas_fea.structure import Structure # Author(s): Tomás Méndez Echenagucia (github.com/tmsmendez) # Structure mdl = Structure(name='beam_bench', path=compas_fea.TEMP) # Nodes and Elements xyz = [[0, 0, 0], [1, 0, 0]] nodes = mdl.add_nodes(xyz) beam = mdl.add_element(nodes, 'BeamElement', axes={'ex': [0, 1, 0]}) # Sets elset_beams = mdl.add_set('elset_beams', 'element', [beam]) # Materials mdl.add(Steel(name='mat_steel')) # Sections mdl.add(RectangularSection(name='sec_pipe', b=0.05, h=0.1)) # Properties