def test_inertia_when_offset_axially(self): density = 230.4 length = 20.0 offset = 5.0 element = _mock_rigid_uniform_beam(density, length) conn = RigidConnection('offset', offset=[offset, 0, 0]) joint = FreeJoint('joint') system = System() system.add_leaf(joint) joint.add_leaf(conn) conn.add_leaf(element) system.setup() # Calculate reduced system to get rigid body matrices rsys = ReducedSystem(system) # Expected values: rod along x axis m = density * length Iy = m * (length**2 / 12 + (length/2 + offset)**2) expected_mass = m * eye(3) expected_inertia = diag([0, Iy, Iy]) expected_offdiag = zeros((3, 3)) # Y accel -> positive moment about Z # Z accel -> negative moment about Y expected_offdiag[2, 1] = +m * (length/2 + offset) expected_offdiag[1, 2] = -m * (length/2 + offset) assert_aae(rsys.M[:3, :3], expected_mass) assert_aae(rsys.M[3:, 3:], expected_inertia) assert_aae(rsys.M[3:, :3], expected_offdiag) assert_aae(rsys.M[:3, 3:], expected_offdiag.T)
def test_distal_node_is_transformed_by_joint_freedoms(self): j = FreeJoint('joint') j.rp = array([3.5, 9.21, 8.6]) j.Rp = eye(3) # Test distal transform -- translation j.xstrain[:3] = [2, 3, 4] j.calc_distal_pos() assert_array_equal(j.rd, j.rp + [2, 3, 4]) assert_array_equal(j.Rd, j.Rp) # Now add a rotation of 60 deg about x axis j.xstrain[3] = pi / 3 j.calc_distal_pos() assert_array_equal(j.rd, j.rp + [2, 3, 4]) # new unit vectors after rotation assert_array_almost_equal(j.Rd, c_[[1, 0, 0], [0, cos(pi/3), sin(pi/3)], [0, -sin(pi/3), cos(pi/3)]]) # Check combination of Euler angles: 90deg yaw and pitch j.xstrain[3:] = [0, pi/2, pi/2] j.calc_distal_pos() assert_array_equal(j.rd, j.rp + [2, 3, 4]) # new unit vectors after rotation. # 1) 90deg yaw -> y, -x, z # 2) 90deg pitch -> -z, -x, y assert_array_almost_equal(j.Rd, c_[[0, 0, -1], [-1, 0, 0], [0, 1, 0]])
def build_structure(rigid_body_configs): # Free joint represents the rigid-body motion of the platform free_joint = FreeJoint('base') for i, conf in enumerate(rigid_body_configs): name = conf.get('name', 'body{}'.format(i)) conn = RigidConnection('conn-' + name, offset=conf['CoM']) body = RigidBody(name, mass=conf['mass'], inertia=np.diag(conf['inertia'])) free_joint.add_leaf(conn) conn.add_leaf(body) system = System(free_joint) return system
def test_distal_forces_cause_acceleration(self): j = FreeJoint('joint') b = RigidBody('body', mass=3, inertia=np.diag([7, 7, 7])) s = System() s.add_leaf(j) j.add_leaf(b) s.setup() # Constant loading j.distal_forces = np.array([2, 0, 0, 0, 0, 0]) s.update_kinematics() s.update_matrices() s.solve_accelerations() s.update_kinematics() assert_array_equal(j.ad, [2. / 3, 0, 0, 0, 0, 0])
def test_distal_forces(self): j = FreeJoint('joint') # Constant loading j.distal_forces = np.array([2, 3.1, 2.1, 4.3, 2.5, 1.0]) j.calc_external_loading() assert_array_equal(j.applied_forces[:6], 0) assert_array_equal(j.applied_forces[6:], j.distal_forces) assert_array_equal(j.applied_stress, 0) # Loading function j.distal_forces = lambda element, t: np.ones(6) j.calc_external_loading() assert_array_equal(j.applied_forces[:6], 0) assert_array_equal(j.applied_forces[6:], 1) assert_array_equal(j.applied_stress, 0)
def test_interal_forces(self): # NB minus sign because convention for applied_stress is that # stiffness loads are positive. j = FreeJoint('joint') # Constant loading j.internal_forces = np.array([2, 3.1, 2.1, 4.3, 2.5, 1.0]) j.calc_external_loading() assert_array_equal(j.applied_forces, 0) assert_array_equal(j.applied_stress, -j.internal_forces) # Loading function j.internal_forces = lambda element, t: np.ones(6) j.calc_external_loading() assert_array_equal(j.applied_forces, 0) assert_array_equal(j.applied_stress, -1)
def test_velocity_transforms_depend_on_joint_orientation(self): j = FreeJoint('joint') j.rp = array([0, 0, 8.6]) # x-velocity: shouldn't depend on speed j.vstrain[:] = [2.3, 0, 0, 0, 0, 0] j.calc_kinematics() assert_array_equal(j.F_vp, eye(6)) # movement of p node moves d node assert_array_equal(j.F_ve, eye(6)) # with zero rotation, direct assert_array_equal(j.F_v2, 0) # now apply a 90 deg yaw angle (about z axis): now 'pitch' # causes a -ve global x-rotation, and 'roll' causes a +ve # global y-rotation j.xstrain[5] = pi / 2 j.calc_kinematics() assert_array_equal(j.F_vp, eye(6)) # movement of p node moves d node assert_array_equal(j.F_v2, 0) F_ve = eye(6) F_ve[3:, 3] = [0, 1, 0] F_ve[3:, 4] = [-1, 0, 0] assert_array_almost_equal(j.F_ve, F_ve)
def test_three_elements_forming_a_disc_about_X_have_correct_inertia(self): density = 5 length = 20.0 offset = 1.25 m = density * length # mass of one beam joint = FreeJoint('joint') # Make 3 elements spaced by 120 deg about z axis for i in range(3): # Rotation of -pi/2 about y aligns local x axis of ModalElement rotmat = rotations(('x', i * 2*pi/3), ('y', -pi/2)) offset_vector = dot(rotmat, [offset, 0, 0]) # offset // local x conn = RigidConnection('offset%d' % i, offset_vector, rotmat) element = _mock_rigid_uniform_beam(density, length, 'element%d' % i) joint.add_leaf(conn) conn.add_leaf(element) system = System() system.add_leaf(joint) system.setup() # Calculate reduced system to get rigid body matrices rsys = ReducedSystem(system) # Expected values: perp inertia using projected lengths of beams Iy = m * (length**2 / 12 + (length/2 + offset)**2) Iperp = Iy + Iy/4 + Iy/4 Iaxial = 3 * Iy expected_mass = 3 * m * eye(3) expected_inertia = diag([Iaxial, Iperp, Iperp]) expected_offdiag = zeros((3, 3)) assert_aae(rsys.M[:3, :3], expected_mass) assert_aae(rsys.M[3:, 3:], expected_inertia) assert_aae(rsys.M[3:, :3], expected_offdiag) assert_aae(rsys.M[:3, 3:], expected_offdiag.T)
def _make_random_joint(rdm): # Make free joint with random axis and transform return FreeJoint('joint', post_transform=random_rotation_matrix(rdm))
def __init__(self, structure_config): s = structure_config #### Load details of flexible elements #### if 'definition' in s['tower']: if 'mass' in s['tower']: raise ValueError("Both tower definition and explicit mass!") self.tower_definition = Tower(s['tower']['definition']) assert np.all(self.tower_definition.stn_pos[:, :2] == 0) # vert. z_tower = self.tower_definition.stn_pos[:, 2] self.tower_modes = ModesFromScratch( z_tower - z_tower[0], self.tower_definition.density, 1, self.tower_definition.EIy, self.tower_definition.EIz) else: self.tower_definition = None self.tower_modes = None if 'blade' in s: self.blade_modes = load_modes_from_Bladed(s['blade']['definition']) else: self.blade_modes = None #### Create the elements #### # Free joint represents the rigid-body motion of the platform free_joint = FreeJoint('base') # This is the rigid-body mass of the platform structure conn_platform = RigidConnection('conn-platform', offset=s['platform']['CoM']) platform = RigidBody('platform', mass=s['platform']['mass'], inertia=np.diag(s['platform']['inertia'])) free_joint.add_leaf(conn_platform) conn_platform.add_leaf(platform) # Make a rigid body to represent the added mass # (approximate to zero frequency) # XXX this is skipping the coupling matrix #A = whales_model.A(0) # added_mass = RigidBody('added-mass', mass=np.diag(A[:3, :3]), # inertia=A[3:, 3:]) # Flexible tower or equivalent rigid body if self.tower_modes: # move base of tower 10m up, and rotate so tower x-axis is vertical conn_tower = RigidConnection( 'conn-tower', offset=[0, 0, z_tower[0]], rotation=rotmat_y(-pi/2)) tower = DistalModalElementFromScratch( 'tower', self.tower_modes, s['tower']['number of normal modes']) else: # move tower to COG conn_tower = RigidConnection( 'conn-tower', offset=s['tower']['CoM']) tower = RigidBody('tower', s['tower']['mass'], np.diag(s['tower']['inertia'])) free_joint.add_leaf(conn_tower) conn_tower.add_leaf(tower) # The nacelle -- rigid body # rotate back so nacelle inertia is aligned with global coordinates if self.tower_modes: nacoff = s['nacelle']['offset from tower top'] conn_nacelle = RigidConnection('conn-nacelle', offset=dot(rotmat_y(pi/2), nacoff), rotation=rotmat_y(pi/2)) tower.add_leaf(conn_nacelle) else: conn_nacelle = RigidConnection( 'conn-nacelle', offset=np.array([0, 0, s['nacelle']['height']])) free_joint.add_leaf(conn_nacelle) nacelle = RigidBody( 'nacelle', mass=s['nacelle']['mass'], inertia=np.diag(s['nacelle'].get('inertia', np.zeros(3)))) conn_nacelle.add_leaf(nacelle) # The rotor hub -- currently just connections (no mass) # rotate so rotor centre is aligned with global coordinates if self.tower_modes: rotoff = s['rotor']['offset from tower top'] conn_rotor = RigidConnection('conn-rotor', offset=dot(rotmat_y(pi/2), rotoff), rotation=rotmat_y(pi/2)) tower.add_leaf(conn_rotor) else: conn_rotor = RigidConnection( 'conn-rotor', offset=np.array([0, 0, s['nacelle']['height']])) free_joint.add_leaf(conn_rotor) # The drive shaft rotation (rotation about x) shaft = Hinge('shaft', [1, 0, 0]) conn_rotor.add_leaf(shaft) # The blades if self.blade_modes: rtlen = s['rotor']['root length'] Ryx = dot(rotmat_y(-pi/2), rotmat_x(-pi/2)) # align blade modes for i in range(3): R = rotmat_x(i*2*pi/3) root = RigidConnection('root%d' % (i+1), offset=dot(R, [0, 0, rtlen]), rotation=dot(R, Ryx)) blade = ModalElement('blade%d' % (i+1), self.blade_modes) shaft.add_leaf(root) root.add_leaf(blade) else: rotor = RigidBody('rotor', s['rotor']['mass'], np.diag(s['rotor']['inertia'])) shaft.add_leaf(rotor) # Build system self.system = System(free_joint) # Constrain missing DOFs -- tower torsion & extension not complete if self.tower_modes: self.system.prescribe(tower, vel=0, part=[0, 3])