def _run_mass_properties(model2, nnodes, nelements, run_mass_properties=True): """helper method""" if not (run_mass_properties and nelements): return if nelements > 1 and nnodes == 0: # pragma: no cover raise RuntimeError('no nodes exist') mass1, cg1, inertia1 = mass_properties(model2, reference_point=None, sym_axis=None) mass2, cg2, inertia2 = mass_properties_nsm(model2, reference_point=None, sym_axis=None) #if not quiet: #if model2.wtmass != 1.0: #print('weight = %s' % (mass1 / model2.wtmass)) #print('mass = %s' % mass1) #print('cg = %s' % cg1) #print('Ixx=%s, Iyy=%s, Izz=%s \nIxy=%s, Ixz=%s, Iyz=%s' % tuple(inertia1)) assert np.allclose(mass1, mass2), 'mass1=%s mass2=%s' % (mass1, mass2) assert np.allclose(cg1, cg2), 'mass=%s\ncg1=%s cg2=%s' % (mass1, cg1, cg2) if not np.allclose(inertia1, inertia2, atol=1e-5): # pragma: no cover raise ValueError( 'mass=%s cg=%s\ninertia1=%s\ninertia2=%s\ndinertia=%s' % (mass1, cg1, inertia1, inertia2, inertia1 - inertia2)) unused_mass3, unused_cg3, unused_inertia3 = mass_properties_breakdown( model2)[:3]
def test_nsmadd(self): """tests the NSMADD and all NSM cards""" eid_quad = 1 unused_eid_tri = 2 unused_eid_conrod = 3 unused_eid_crod = 4 unused_eid_pbeaml = 5 unused_eid_pbarl = 6 unused_pid_pbeaml = 40 pid_pshell = 10 unused_pid_pbeaml = 21 unused_pid_pbarl = 31 unused_pid_prod = 41 mid = 100 E = 3.0e7 G = None nu = 0.3 nids = [1, 2, 3, 4] model = BDF(debug=False) model.add_grid(1, [0., 0., 0.]) model.add_grid(2, [1., 0., 0.]) model.add_grid(3, [1., 1., 0.]) model.add_grid(4, [0., 1., 0.]) model.add_cquad4(eid_quad, pid_pshell, nids) # area=1.0 model.add_mat1(mid, E, G, nu, rho=0.0) model.add_pshell(pid_pshell, mid1=mid, t=0.1) #, nsm=None) model.add_nsm1(1000, 'PSHELL', 1.0, pid_pshell, comment='nsm1') # correct; 1.0 model.add_nsml1(2000, 'PSHELL', 1.0, pid_pshell, comment='nsml1') # correct; 1.0 model.add_nsml(3000, 'PSHELL', pid_pshell, 1.0, comment='nsml') # correct; 1.0 model.add_nsml(4000, 'PSHELL', pid_pshell, 1.0, comment='nsml') # correct; 1.0 model.add_nsmadd(5000, [1000, 2000, 3000, 4000], comment='nsmadd') model.add_nsmadd(5000, [1000, 2000, 3000, 4000], comment='nsmadd') model.cross_reference() model.pop_xref_errors() mass, unused_cg, unused_I = mass_properties_nsm(model, nsm_id=5000) self.assertAlmostEqual(mass, 8.0) model2 = save_load_deck(model) mass, unused_cg, unused_I = mass_properties_nsm(model2, nsm_id=5000)
def check_solid(self, model, eid, etype, pid, ptype, mid, mtype, nsm, rho, V): """checks that various solid methods work""" mass = rho * V element = model.elements[eid] assert max(element.node_ids) > 0 assert pid in model.properties, 'pid is missing for\n%s' % str(element) self.assertEqual(element.type, etype) self.assertEqual(element.eid, eid) self.assertEqual(element.pid_ref.type, ptype) self.assertEqual(element.Pid(), pid) self.assertEqual(element.pid_ref.mid_ref.type, mtype) self.assertEqual(element.Mid(), mid) self.assertEqual(element.Volume(), V) self.assertEqual(element.Mass(), mass) mass_mp = mass_properties(model, element_ids=eid)[0] mass_mp_nsm = mass_properties_nsm(model, element_ids=eid)[0] assert np.allclose(mass, mass_mp) assert np.allclose(mass, mass_mp_nsm)
def check_solid(self, model, eid, etype, pid, ptype, mid, mtype, nsm, rho, volume): """checks that various solid methods work""" mass = rho * volume element = model.elements[eid] assert max(element.node_ids) > 0 assert pid in model.properties, 'pid is missing for\n%s' % str(element) self.assertEqual(element.type, etype) self.assertEqual(element.eid, eid) self.assertEqual(element.pid_ref.type, ptype) self.assertEqual(element.Pid(), pid) self.assertEqual(element.pid_ref.mid_ref.type, mtype) self.assertEqual(element.Mid(), mid) self.assertEqual(element.Volume(), volume) self.assertEqual(element.Mass(), mass) mass_mp = mass_properties(model, element_ids=eid)[0] mass_mp_nsm = mass_properties_nsm(model, element_ids=eid)[0] unused_centroid, unused_xe, unused_ye, unused_ze = element.material_coordinate_system( ) assert np.allclose(mass, mass_mp) assert np.allclose(mass, mass_mp_nsm)
def mass_properties_nsm(self, element_ids=None, mass_ids=None, nsm_id=None, reference_point=None, sym_axis=None, scale=None, inertia_reference='cg', xyz_cid0_dict=None, debug=False): """ Calculates mass properties in the global system about the reference point. Considers NSM, NSM1, NSML, NSML1. Parameters ---------- model : BDF() a BDF object element_ids : list[int]; (n, ) ndarray, optional An array of element ids. mass_ids : list[int]; (n, ) ndarray, optional An array of mass ids. nsm_id : int the NSM id to consider reference_point : ndarray/int, optional type : ndarray An array that defines the origin of the frame. default = <0,0,0>. type : int the node id sym_axis : str, optional The axis to which the model is symmetric. If AERO cards are used, this can be left blank. allowed_values = 'no', x', 'y', 'z', 'xy', 'yz', 'xz', 'xyz' scale : float, optional The WTMASS scaling value. default=None -> PARAM, WTMASS is used float > 0.0 inertia_reference : str; default='cg' 'cg' : inertia is taken about the cg 'ref' : inertia is about the reference point xyz_cid0_dict : dict[nid] : xyz; default=None -> auto-calculate mapping of the node id to the global position debug : bool; default=False developer debug; may be removed in the future Returns ------- mass : float The mass of the model. cg : ndarray The cg of the model as an array. inertia : ndarray Moment of inertia array([Ixx, Iyy, Izz, Ixy, Ixz, Iyz]). I = mass * centroid * centroid .. math:: I_{xx} = m (dy^2 + dz^2) .. math:: I_{yz} = -m * dy * dz where: .. math:: dx = x_{element} - x_{ref} .. seealso:: http://en.wikipedia.org/wiki/Moment_of_inertia#Moment_of_inertia_tensor .. note:: This doesn't use the mass matrix formulation like Nastran. It assumes m*r^2 is the dominant term. If you're trying to get the mass of a single element, it will be wrong, but for real models will be correct. Examples -------- **mass properties of entire structure** >>> mass, cg, inertia = model.mass_properties() >>> Ixx, Iyy, Izz, Ixy, Ixz, Iyz = inertia **mass properties of model based on Property ID** >>> pids = list(model.pids.keys()) >>> pid_eids = model.get_element_ids_dict_with_pids(pids) >>> for pid, eids in sorted(pid_eids.items()): >>> mass, cg, inertia = mass_properties(model, element_ids=eids) Warnings -------- - If eids are requested, but don't exist, no warning is thrown. Decide if this is the desired behavior. - If the NSMx ALL option is used, the mass from all elements will be considered, even if not included in the element set """ self.deprecated( 'mass, cg, inertia = model.mass_properties_nsm(...)', 'from pyNastran.bdf.mesh_utils.mass_properties import mass_properties_nsm\n' 'mass, cg, inertia = mass_properties_nsm(model, ...)', '1.3') mass, cg, inertia = mass_properties_nsm( self, element_ids=element_ids, mass_ids=mass_ids, nsm_id=nsm_id, reference_point=reference_point, sym_axis=sym_axis, scale=scale, inertia_reference=inertia_reference, xyz_cid0_dict=xyz_cid0_dict, debug=debug) return (mass, cg, inertia)
def test_nsm_cquad4(self): eid_quad = 1 eid_tri = 2 eid_conrod = 3 eid_crod = 4 eid_pbeaml = 5 eid_pbarl = 6 pid_pbeaml = 40 pid_pshell = 10 pid_pbeaml = 21 pid_pbarl = 31 pid_prod = 41 mid = 100 E = 3.0e7 G = None nu = 0.3 nids = [1, 2, 3, 4] model = BDF(debug=False) model.add_grid(1, [0., 0., 0.]) model.add_grid(2, [1., 0., 0.]) model.add_grid(3, [1., 1., 0.]) model.add_grid(4, [0., 1., 0.]) model.add_cquad4(eid_quad, pid_pshell, nids) # area=1.0 model.add_ctria3(eid_tri, pid_pshell, nids[:-1]) # area=0.5 model.add_conrod(eid_conrod, mid, [1, 2], A=1.0, j=0.0, c=0.0, nsm=0.0, comment='') x = [0., 0., 1.] g0 = None model.add_cbar(eid_pbarl, pid_pbarl, [1, 2], x, g0, offt='GGG', pa=0, pb=0, wa=None, wb=None, comment='') model.add_cbeam(eid_pbeaml, pid_pbeaml, nids, x, g0, offt='GGG', bit=None, pa=0, pb=0, wa=None, wb=None, sa=0, sb=0, comment='') model.add_crod(eid_crod, pid_prod, [1, 2]) model.add_prod(pid_prod, mid, A=0.1) model.add_pshell(pid_pshell, mid1=mid, t=0.1) #, nsm=None) bar_type = 'BAR' dims = [1., 2.] xxb = [0.] model.add_pbarl(pid_pbarl, mid, bar_type, dims, group='MSCBML0', nsm=0., comment='') beam_type = 'BAR' dims = [[1., 2.]] nsm = [0.0] model.add_pbeaml(pid_pbeaml, mid, beam_type, xxb, dims, so=None, nsm=nsm, group='MSCBML0', comment='') model.add_mat1(mid, E, G, nu, rho=0.0) # TODO: these are correct barring incorrect formulas model.add_nsm1(1000, 'PSHELL', 1.0, pid_pshell, comment='nsm1') # correct; 1.5 model.add_nsm1(1001, 'ELEMENT', 1.0, eid_quad) # correct; 1.0 model.add_nsm1(1002, 'ELEMENT', 1.0, [eid_quad, eid_tri]) # correct; 1.5 model.add_nsm1(1003, 'ELEMENT', 1.0, [eid_pbeaml]) # correct; 1.0 model.add_nsm1(1004, 'ELEMENT', 1.0, eid_pbarl) # correct; 1.0 model.add_nsm1(1005, 'ELEMENT', 1.0, 'ALL') # crash according to QRG b/c mixed type; 2.5 model.add_nsm1(1006, 'PSHELL', 1.0, 'ALL') # correct; 1.5 model.add_nsm1(1007, 'PSHELL', 1.0, [10, 'THRU', 12]) # correct; 1.5 model.add_nsm1(1008, 'PSHELL', 1.0, [10, 'THRU', 12, 'BY', 2]) # correct; 1.5 model.add_nsm1(1009, 'PBARL', 1.0, pid_pbarl) # correct; 1.0 model.add_nsm1(1010, 'PBEAML', 1.0, pid_pbeaml) # correct; 1.0 model.add_nsm1(1011, 'PROD', 1.0, pid_prod) # correct; 1.0 model.add_nsm1(1012, 'CONROD', 1.0, eid_conrod) # correct; 1.0 #model.add_nsml1(sid, nsm_type, value, ids) model.add_nsml1(2000, 'PSHELL', 1.0, pid_pshell, comment='nsml1') # correct; 1.0 model.add_nsml1(2001, 'ELEMENT', 1.0, eid_quad) # correct; 1.0 model.add_nsml1(2002, 'ELEMENT', 1.0, [eid_quad, eid_tri]) # correct; 1.0 model.add_nsml1(2003, 'ELEMENT', 1.0, [eid_pbeaml]) # correct; 1.0 model.add_nsml1(2004, 'ELEMENT', 1.0, eid_pbarl) # correct; 1.0 model.add_nsml1(2005, 'ELEMENT', 1.0, 'ALL') # crash according to QRG b/c mixed type; 1.0 model.add_nsml1(2006, 'PSHELL', 1.0, 'ALL') # correct; 1.0 model.add_nsml1(2007, 'PSHELL', 1.0, [10, 'THRU', 12]) # correct; 1.0 model.add_nsml1(2008, 'PSHELL', 1.0, [10, 'THRU', 12, 'BY', 2]) # correct; 1.0 model.add_nsml1(2009, 'PBARL', 1.0, pid_pbarl) # correct; 1.0 model.add_nsml1(2010, 'PBEAML', 1.0, pid_pbeaml) # correct; 1.0 model.add_nsml1(2011, 'PROD', 1.0, pid_prod) # correct; 1.0 model.add_nsml1(2012, 'CONROD', 1.0, eid_conrod) # correct; 1.0 #model.add_nsml1(2011, 'PSHELL', 1.0, ['1240', 'THRU', '1250', None, None, # correct; 0.0 #'2567', 'THRU', '2575', #'35689', 'THRU', '35700', None, None, #'76', 'THRU', '85',]) #print(model.nsms[2011]) model.add_nsm(3000, 'PSHELL', pid_pshell, 1.0, comment='nsm') # correct; 1.5 model.add_nsm(3001, 'ELEMENT', eid_quad, 1.0) # correct; 1.0 model.add_nsm(3003, 'ELEMENT', [eid_pbeaml], 1.0) # correct; 1.0 model.add_nsm(3004, 'ELEMENT', eid_pbarl, 1.0) # correct; 1.0 model.add_nsm(3009, 'PBARL', pid_pbarl, 1.0) # correct; 1.0 model.add_nsm(3010, 'PBEAML', pid_pbeaml, 1.0) # correct; 1.0 model.add_nsm(3011, 'PROD', pid_prod, 1.0) # correct; 1.0 model.add_nsm(3012, 'CONROD', eid_conrod, 1.0) # correct; 1.0 model.add_nsml(4000, 'PSHELL', pid_pshell, 1.0, comment='nsml') # correct; 1.0 model.add_nsml(4001, 'ELEMENT', eid_quad, 1.0) # correct; 1.0 model.add_nsml(4003, 'ELEMENT', [eid_pbeaml], 1.0) # correct; 1.0 model.add_nsml(4004, 'ELEMENT', eid_pbarl, 1.0) # correct; 1.0 model.add_nsml(4009, 'PBARL', pid_pbarl, 1.0) # correct; 1.0 model.add_nsml(4010, 'PBEAML', pid_pbeaml, 1.0) # correct; 1.0 model.add_nsml(4011, 'PROD', pid_prod, 1.0) # correct; 1.0 model.add_nsml(4012, 'CONROD', eid_conrod, 1.0) # correct; 1.0 model.pop_parse_errors() model.cross_reference() model.pop_xref_errors() expected_dict = { # NSM1 1000: 1.5, 1001: 1.0, 1002: 1.5, 1003: 1.0, 1004: 1.0, 1005: -1.0, # crash 1006: 1.5, 1007: 1.5, 1008: 1.5, 1009: 1.0, 1010: 1.0, 1011: 1.0, 1012: 1.0, #model.add_nsml1(sid, nsm_type, value, ids) # NSML1 2000: 1.0, 2001: 1.0, 2002: 1.0, 2003: 1.0, 2004: 1.0, 2005: -1.0, # crash 2006: 1.0, 2007: 1.0, 2008: 1.0, 2009: 1.0, 2010: 1.0, 2011: 1.0, 2012: 1.0, # NSM 3000: 1.5, 3001: 1.0, 3003: 1.0, 3004: 1.0, 3009: 1.0, 3010: 1.0, 3011: 1.0, 3012: 1.0, # NSM1 4000: 1.0, 4001: 1.0, 4003: 1.0, 4004: 1.0, 4009: 1.0, 4010: 1.0, 4011: 1.0, 4012: 1.0, } for nsm_id in sorted(model.nsms): mass1_expected = expected_dict[nsm_id] if mass1_expected == -1.0: with self.assertRaises(RuntimeError): mass1, unused_cg, unused_I = mass_properties_nsm( model, nsm_id=nsm_id, debug=False) else: mass1, unused_cg, unused_I = mass_properties_nsm(model, nsm_id=nsm_id, debug=False) if mass1 != mass1_expected: unused_mass2 = mass_properties_nsm(model, nsm_id=nsm_id, debug=True)[0] raise RuntimeError('nsm_id=%s mass != %s; mass1=%s' % (nsm_id, mass1_expected, mass1)) #print('mass[%s] = %s' % (nsm_id, mass)) #print('----------------------------------------------') model2 = save_load_deck(model, run_test_bdf=False) model2.reset_rslot_map() #print(model2._type_to_slot_map) model2.elements = {} type_to_id_map = {} for card_type, ids in model2._type_to_id_map.items(): if card_type in [ 'CQUAD4', 'CTRIA3', 'CBEAM', 'CONROD', 'CBAR', 'CROD' ]: pass elif card_type in [ 'NSM', 'NSM1', 'NSML', 'NSML1', 'MAT1', 'PBARL', 'PBEAM', 'PSHELL', 'PCOMP', 'PROD', 'PBEAML', 'GRID' ]: type_to_id_map[card_type] = ids else: raise NotImplementedError(str((card_type, ids))) model2._type_to_id_map = type_to_id_map model2.log = SimpleLogger(level='error') # don't crash on the null case for nsm_id in sorted(model2.nsms): mass, unused_cg, unused_I = mass_properties_nsm(model2, nsm_id=nsm_id, debug=False) self.assertEqual(mass, 0.0)
def test_shear(self): """tests a CSHEAR, PSHEAR""" pid = 10 pid_pshell = 11 mid = 100 log = get_logger(level='warning') model = BDF(log=log) model.add_grid(1, [0., 0., 0.]) model.add_grid(2, [1., 0., 0.]) model.add_grid(3, [1., 1., 0.]) model.add_grid(4, [0., 1., 0.]) nsm = 10.0 t = 1.0 rho = 1.0 cshear = model.add_cshear(10, pid, [1, 2, 3, 4], comment='cshear') cquad4 = model.add_cquad4(14, pid_pshell, [1, 2, 3, 4], comment='cquad4') model.add_pshear(pid, mid, t=t, nsm=nsm, f1=0., f2=0., comment='pshear') model.add_pshell(pid_pshell, mid1=mid, t=t, mid2=None, twelveIt3=1.0, mid3=None, tst=0.833333, nsm=nsm, z1=None, z2=None, mid4=None, comment='') E = 3.0e7 G = None nu = 0.3 model.add_mat1(mid, E, G, nu, rho=rho) model.validate() model.cross_reference() model.pop_xref_errors() area = 1.0 mass_expected = area * (rho * t + nsm) mass = model.mass_properties()[0] assert np.allclose(mass, mass_expected*2), 'mass_properties all: mass=%s mass_expected=%s' % (mass, mass_expected*2) mass = model.mass_properties(element_ids=10)[0] assert np.allclose(mass, mass_expected), 'mass_properties reduced: mass=%s mass_expected=%s' % (mass, mass_expected) mass = mass_properties_nsm(model)[0] assert np.allclose(mass, mass_expected*2), 'mass_properties_nsm all: mass=%s mass_expected=%s' % (mass, mass_expected*2) mass = mass_properties_nsm(model, element_ids=10)[0] assert np.allclose(mass, mass_expected), 'mass_properties_nsm reduced: mass=%s mass_expected=%s' % (mass, mass_expected) bdf_file = StringIO() model.write_bdf(bdf_file) model.uncross_reference() model.cross_reference() model.pop_xref_errors() model.get_area_breakdown(property_ids=None, stop_if_no_area=True) model.get_mass_breakdown(property_ids=None, stop_if_no_mass=True, detailed=False) model.get_mass_breakdown(property_ids=None, stop_if_no_mass=True, detailed=True) model.get_volume_breakdown(property_ids=None, stop_if_no_volume=True) assert np.allclose(cshear.Mass(), mass_expected), cshear.Mass() model.uncross_reference() model.safe_cross_reference() model.uncross_reference() #bdf_file = model.write_bdf(bdf_file) save_load_deck(model)