def test_single_rigid_body(self): mass = 36.2 inertia = np.diag([75.4, 653, 234]) s = System() j = FreeJoint('joint') b = RigidBody('body', mass, inertia) s.add_leaf(j) j.add_leaf(b) s.setup() # Calculate reduced system to get rigid body matrices rsys = ReducedSystem(s) self.assertEqual(rsys.M.shape, (6, 6)) self.assertEqual(rsys.Q.shape, (6,)) assert_aae(rsys.M[:3, :3], mass * np.eye(3)) assert_aae(rsys.M[3:, 3:], inertia) assert_aae(rsys.M[3:, :3], 0) assert_aae(rsys.M[:3, 3:], 0) assert_aae(rsys.Q, 0) # Now if some freedoms are prescribed, don't appear in reduced system s.prescribe(j, part=[1, 2, 3, 4, 5]) # only x-translation is free now rsys = ReducedSystem(s) self.assertEqual(rsys.M.shape, (1, 1)) self.assertEqual(rsys.Q.shape, (1,)) assert_aae(rsys.M[0, 0], mass) assert_aae(rsys.Q, 0)
def test_nonzero_prescribed_acceleration(self): # Test reduction where a prescribed acceleration is non-zero: # two sliders in series, with a mass on the end. If the second # slider's acceleration is prescribed, the first slider's DOF # sees an inertial force corresponding to the acceleration of # the mass. mass = 36.2 s = System() s1 = PrismaticJoint('slider1', [1, 0, 0]) s2 = PrismaticJoint('slider2', [1, 0, 0]) b = RigidBody('body', mass) s.add_leaf(s1) s1.add_leaf(s2) s2.add_leaf(b) s.setup() s.prescribe(s2, acc=0) # With hinge angle = 0, no generalised inertial force rsys = ReducedSystem(s) assert_aae(rsys.M, mass) assert_aae(rsys.Q, 0) # With hinge angle = 90deg, do see generalised inertial force s.prescribe(s2, acc=2.3) rsys = ReducedSystem(s) assert_aae(rsys.M, mass) assert_aae(rsys.Q, -mass * 2.3)
def test_three_rigid_elements_as_disc_have_ends_in_right_place(self): length = 20.0 offset = 5.0 # Make 3 elements spaced by 120 deg about z axis system = System() elements = [] for i in range(3): rotmat = rotations(('z', i * 2*pi/3)) offset_vector = dot(rotmat, [offset, 0, 0]) conn = RigidConnection('offset%d' % i, offset_vector, rotmat) element = RigidConnection('element%d' % i, [length, 0, 0]) elements.append(element) system.add_leaf(conn) conn.add_leaf(element) system.setup() r = offset R = offset + length assert_aae(elements[0].rp, [r, 0, 0]) assert_aae(elements[1].rp, [-r/2, r*sqrt(3)/2, 0]) assert_aae(elements[2].rp, [-r/2, -r*sqrt(3)/2, 0]) assert_aae(elements[0].rd, [R, 0, 0]) assert_aae(elements[1].rd, [-R/2, R*sqrt(3)/2, 0]) assert_aae(elements[2].rd, [-R/2, -R*sqrt(3)/2, 0])
def test_simple_prescribed_integration(self): s = System() h = Hinge('hinge', [0, 1, 0]) s.add_leaf(h) s.setup() s.prescribe(h) w = h.vstrain[0] = 0.97 # rad/s integ = Integrator(s) t, y = integ.integrate(9.0, 0.1) # Check time vector and shape of result assert_array_equal(t, np.arange(0, 9.0, 0.1)) self.assertEqual(len(y), 1) self.assertEqual(y[0].shape, (len(t), 1)) # Result should be y = wt, but wrapped to [0, 2pi) assert_aae(y[0][:, 0], (w * t) % (2 * np.pi)) # Check asking for velocity and acceleration works h.xstrain[0] = s.time = 0.0 # reset integ = Integrator(s, ('pos', 'vel', 'acc')) t, y = integ.integrate(1.0, 0.1) assert_array_equal(t, np.arange(0, 1.0, 0.1)) self.assertEqual(len(y), 3) for yy in y: self.assertEqual(yy.shape, (len(t), 1)) assert_aae(y[0][:, 0], w * t) assert_aae(y[1][:, 0], w) assert_aae(y[2][:, 0], 0)
def test_solve_accelerations_coupling(self): # Further to test above, check that coupling between prescribed # accelerations and other dofs is correct. For example, if there # is a rigid body vertically offset from the joint, then a # prescribed horizontal acceleration should cause an angular # acceleration as well as the translational acceleration. s = System() j = FreeJoint('joint') c = RigidConnection('conn', [0, 0, 1.7]) b = RigidBody('body', mass=23.54, inertia=74.1 * np.eye(3)) s.add_leaf(j) j.add_leaf(c) c.add_leaf(b) s.setup() # Prescribe horizontal acceleration, solve other accelerations s.prescribe(j, 2.3, part=[0]) # x acceleration s.update_kinematics() # update system to show prescribed acc s.solve_accelerations() # solve free accelerations s.update_kinematics() # update system to show solution # Ground shouldn't move assert_aae(j.ap, 0) # Need angular acceleration = (m a_x L) / I0 I0 = 74.1 + (23.54 * 1.7**2) expected_angular_acc = -(23.54 * 2.3 * 1.7) / I0 assert_aae(j.ad, [2.3, 0, 0, 0, expected_angular_acc, 0]) assert_aae(j.astrain, j.ad) # not always true, but works for FreeJoint
def test_solve_reactions(self): # Check it calls the Element method in the right order: down # the tree from leaves to base. It must also reset reactions. s = System() c0 = RigidConnection('c0') c1 = RigidConnection('c1') c2 = RigidConnection('c2') b1 = RigidBody('b1', 1) b2 = RigidBody('b2', 1) s.add_leaf(c0) c0.add_leaf(c1) c0.add_leaf(c2) c1.add_leaf(b1) c2.add_leaf(b2) s.setup() # Check elements' iter_reactions() are called def mock_iter_reactions(element): calls.append(element) calls = [] import types for el in s.elements.values(): el.iter_reactions = types.MethodType(mock_iter_reactions, el) # Test s.joint_reactions[:] = 3 s.solve_reactions() self.assertEqual(calls, [b2, c2, b1, c1, c0]) assert_aae(s.joint_reactions, 0)
def test_call(self): s = System() c = RigidConnection('conn', [1, 0, 0]) h = Hinge('hinge', [0, 1, 0]) b = RigidBody('body', 1) s.add_leaf(h) h.add_leaf(c) c.add_leaf(b) s.setup() # Set hinge angle h.xstrain[0] = 0.82 h.vstrain[0] = 1.2 h.astrain[0] = -0.3 s.update_kinematics() s.solve_reactions() # Test load outputs out = LoadOutput('node-1') assert_array_equal(out(s), s.joint_reactions['node-1']) out = LoadOutput('node-1', local=True) F = s.joint_reactions['node-1'] assert_array_equal(out(s), np.r_[np.dot(b.Rp.T, F[:3]), np.dot(b.Rp.T, F[3:])])
def test_adding_elements(self): conn = RigidConnection('conn') body = RigidBody('body', mass=1.235) s = System() s.add_leaf(conn) conn.add_leaf(body) s.setup() # Should have dict of elements self.assertEqual(s.elements, {'conn': conn, 'body': body}) # Number of states: # 6 ground # + 6 constraints on conn # + 6 <node-0> between conn and body # --- # 18 self.assertEqual(s.lhs.shape, (18, 18)) for vec in (s.rhs, s.qd, s.qdd): self.assertEqual(len(vec), 18) # Check there are no dofs self.assertEqual(len(s.q.dofs), 0) self.assertEqual(len(s.qd.dofs), 0) self.assertEqual(len(s.qdd.dofs), 0)
def test_solve_accelerations(self): # solve_accelerations() should find: # (a) response of system to forces (here, gravity) # (b) include any prescribed accelerations in qdd vector g = 9.81 s = System(gravity=g) j = FreeJoint('joint') b = RigidBody('body', mass=23.54, inertia=52.1 * np.eye(3)) s.add_leaf(j) j.add_leaf(b) s.setup() # Prescribe horizontal acceleration. Vertical acceleration # should result from gravity. s.prescribe(j, 2.3, part=[0]) # x acceleration # Initially accelerations are zero assert_aae(j.ap, 0) assert_aae(j.ad, 0) assert_aae(j.astrain, 0) # Solve accelerations & check s.solve_accelerations() s.update_kinematics() assert_aae(j.ap, 0) # ground assert_aae(j.ad, [2.3, 0, -g, 0, 0, 0]) assert_aae(j.astrain, j.ad) # not always true, but works for FreeJoint
def test_dof_index(self): s = System() j1 = FreeJoint('j1') j2 = FreeJoint('j2') s.add_leaf(j1) j1.add_leaf(j2) s.setup() # Prescribe some of the strains: # _____j1____ _____j2____ # Strain: 0 1 2 3 4 5 0 1 2 3 4 5 # Prescr: * * * # Dofs: 0 1 2 3 4 5 6 7 8 s.prescribe(j1, 0, part=[1, 4]) s.prescribe(j2, 0, part=[3]) self.assertEqual(s.dof_index('j1', 0), 0) self.assertEqual(s.dof_index('j1', 2), 1) self.assertEqual(s.dof_index('j1', 5), 3) self.assertEqual(s.dof_index('j2', 5), 8) # Strain index out of range should give IndexError with self.assertRaises(IndexError): s.dof_index('j1', 6) # Asking for index of prescribed strain should give ValueError with self.assertRaises(ValueError): s.dof_index('j1', 1)
def test_prescribe_free(self): s = System() j = FreeJoint('joint') s.add_leaf(j) s.setup() s.time = 3.54 # Initially all 6 joint motions are free self.assertEqual(len(s.q.dofs), 6) # Prescribing joint to be fixed results in no dofs s.prescribe(j) self.assertEqual(len(s.q.dofs), 0) # Freeing joint results in 6 dofs again s.free(j) self.assertEqual(len(s.q.dofs), 6) # Prescribing 2 of joint motions leaves 4 dofs s.prescribe(j, lambda t: t, [0, 2]) self.assertEqual(len(s.q.dofs), 4) # Prescribing another joint motions leaves 3 dofs s.prescribe(j, 2.0, part=3) self.assertEqual(len(s.q.dofs), 3) # Check accelerations are applied to qdd assert_aae(s.qdd[j.istrain], 0) s.apply_prescribed_accelerations() assert_aae(s.qdd[j.istrain], [3.54, 0, 3.54, 2.0, 0, 0]) # Freeing joint results in 6 dofs again s.free(j) self.assertEqual(len(s.q.dofs), 6)
def test_1dof_nonlinear_system(self): s = System() j = PrismaticJoint('joint', [0, 0, 1]) k = 0.45 # quadratic stiffness coefficient j.internal_force = lambda el, t: -k * el.xstrain[0]**2 b = RigidBody('body', 10) s.add_leaf(j) j.add_leaf(b) s.setup() # Linearise around z0 = 0: stiffness should be zero linsys = LinearisedSystem.from_system(s, z0=0) assert_aae(linsys.M, [[10.0]]) assert_aae(linsys.C, [[0.0]]) assert_aae(linsys.K, [[0.0]]) # Linearise about z0 = 2: stiffness should be 2kx linsys = LinearisedSystem.from_system(s, z0=[2]) assert_aae(linsys.M, [[10.0]]) assert_aae(linsys.C, [[0.0]]) assert_aae(linsys.K, [[2 * k * 2]]) # Test setting z0 in another way linsys = LinearisedSystem.from_system(s, z0={'joint': [4.2]}) assert_aae(linsys.M, [[10.0]]) assert_aae(linsys.C, [[0.0]]) assert_aae(linsys.K, [[2 * k * 4.2]])
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_print_functions(self): # Not very good tests, but at least check they run without errors joint = FreeJoint('joint') body = RigidBody('body', mass=1.235) s = System() s.add_leaf(joint) joint.add_leaf(body) s.setup() s.print_states() s.print_info()
def test_accounts_for_offset_centre_of_mass_in_applied_force(self): # check applied force due to gravity is correct b = RigidBody("body", mass=5.6, Xc=[1.2, 3.4, 5.4]) s = System(gravity=9.81) # need a System to define gravity s.add_leaf(b) s.setup() b.calc_mass() b.calc_external_loading() assert_array_equal(b.applied_forces, b.mass * 9.81 * array([0, 0, -1, -3.4, 1.2, 0]))
def test_rigid_body_with_no_dofs(self): s = System() b = RigidBody('body', 23.7) s.add_leaf(b) s.setup() # Calculate reduced system to get rigid body matrices rsys = ReducedSystem(s) self.assertEqual(rsys.M.shape, (0, 0)) self.assertEqual(rsys.Q.shape, (0,))
def rigid_body_mass_matrix(element): joint = FreeJoint('joint') system = System() system.add_leaf(joint) joint.add_leaf(element) system.setup() for el in joint.iter_leaves(): system.prescribe(el, 0, 0) system.update_kinematics() rsys = ReducedSystem(system) return rsys.M
def test_call(self): s = System() c = RigidConnection('conn', [1, 0, 0]) h = Hinge('hinge', [0, 1, 0]) b = RigidBody('body', 1) s.add_leaf(h) h.add_leaf(c) c.add_leaf(b) s.setup() # Set hinge angle h.xstrain[0] = 0.82 h.vstrain[0] = 1.2 h.astrain[0] = -0.3 s.update_kinematics() # Test node outputs out = StateOutput('node-1') assert_array_equal(out(s), np.r_[b.rp, b.Rp.flatten()]) out = StateOutput('node-1', deriv=1) assert_array_equal(out(s), b.vp) out = StateOutput('node-1', deriv=2) assert_array_equal(out(s), b.ap) out = StateOutput('node-1', local=True) assert_array_equal(out(s), np.r_[np.dot(b.Rp.T, b.rp), np.eye(3).flatten()]) out = StateOutput('node-1', deriv=1, local=True) assert_array_equal(out(s), np.r_[np.dot(b.Rp.T, b.vp[:3]), np.dot(b.Rp.T, b.vp[3:])]) out = StateOutput('node-1', deriv=2, local=True) assert_array_equal(out(s), np.r_[np.dot(b.Rp.T, b.ap[:3]), np.dot(b.Rp.T, b.ap[3:])]) # Test strain outputs out = StateOutput('hinge-strains') assert_array_equal(out(s), 0.82) out = StateOutput('hinge-strains', deriv=1) assert_array_equal(out(s), 1.2) out = StateOutput('hinge-strains', deriv=2) assert_array_equal(out(s), -0.3) # Strains cannot be transformed to local coordinates with self.assertRaises(RuntimeError): out = StateOutput('hinge-strains', local=True) out(s)
def test_dofs_subset(self): s = System() j = FreeJoint('joint') s.add_leaf(j) s.setup() # 2 nodes, 6 constraints, 6 dofs self.assertEqual(len(s.q), 2 * 12 + 6 + 6) self.assertEqual(len(s.qd), 2 * 6 + 6 + 6) self.assertEqual(len(s.qdd), 2 * 6 + 6 + 6) self.assertEqual(len(s.q.dofs), 6) self.assertEqual(len(s.qd.dofs), 6) self.assertEqual(len(s.qdd.dofs), 6)
def setUp(self): joint = PrismaticJoint('joint', [0, 0, 1]) joint.stiffness = self.K joint.damping = 2 * self.damping_coeff * (self.K * self.M) ** 0.5 body = RigidBody('body', self.M) system = System() system.add_leaf(joint) joint.add_leaf(body) system.setup() system.update_kinematics() self.joint, self.body, self.system = joint, body, system
def setUp(self): x = linspace(0, self.L, 15) fe = BeamFE(x, density=self.m, EA=0, EIy=self.EI, EIz=0) fe.set_boundary_conditions('C', 'C') fe.set_dofs([False, False, True, False, True, False]) beam = DistalModalElementFromFE('beam', fe, num_modes=1, damping=self.damping_coeff) system = System() system.add_leaf(beam) system.setup() system.update_kinematics() self.beam, self.system = beam, system
def setUp(self): joint = PrismaticJoint('joint', [0, 0, 1]) joint.stiffness = self.K joint.damping = 2 * self.damping_coeff * (self.K * self.M)**0.5 body = RigidBody('body', self.M) system = System() system.add_leaf(joint) joint.add_leaf(body) system.setup() system.update_kinematics() self.joint, self.body, self.system = joint, body, 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_1dof_linear_system(self): s = System() j = PrismaticJoint('joint', [0, 0, 1]) j.stiffness = 5.0 j.damping = 2.3 b = RigidBody('body', 10) s.add_leaf(j) j.add_leaf(b) s.setup() linsys = LinearisedSystem.from_system(s) assert_array_equal(linsys.M, [[10.0]]) assert_array_equal(linsys.C, [[2.3]]) assert_array_equal(linsys.K, [[5.0]])
def test_get_set_state(self): s = System() j = FreeJoint('joint') s.add_leaf(j) s.setup() # State is [q_dofs, qd_dofs]. # Here we have 6 dofs: s.set_state([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) self.assertEqual(list(s.q.dofs), [1, 2, 3, 4, 5, 6]) self.assertEqual(list(s.qd.dofs), [7, 8, 9, 10, 11, 12]) s.q.dofs[2] = 100 s.qd.dofs[0] = -1 self.assertEqual(list(s.get_state()), [1, 2, 100, 4, 5, 6, -1, 8, 9, 10, 11, 12])
def setUp(self): # Parameters mass = 11.234 length = 2.54 gravity = 9.81 # Build model hinge = Hinge('hinge', [0, 1, 0]) link = RigidConnection('link', [length, 0, 0]) body = RigidBody('body', mass) system = System(gravity=gravity) system.add_leaf(hinge) hinge.add_leaf(link) link.add_leaf(body) system.setup() # Custom outputs to calculate correct answer def force_body_prox_local(s): theta = s.q[hinge.istrain][0] thetadot = s.qd[hinge.istrain][0] thetadotdot = s.qdd[hinge.istrain][0] Fx = mass * (-gravity * np.sin(theta) - length * thetadot**2) Fz = mass * (+gravity * np.cos(theta) - length * thetadotdot) return [Fx, 0, Fz, 0, 0, 0] def force_hinge_prox(s): theta = s.q[hinge.istrain][0] thetadot = s.qd[hinge.istrain][0] thetadotdot = s.qdd[hinge.istrain][0] A = np.array([[+np.cos(theta), np.sin(theta)], [-np.sin(theta), np.cos(theta)]]) Fxz = -mass * length * np.dot(A, [thetadot**2, thetadotdot]) return [Fxz[0], 0, Fxz[1] + gravity * mass, 0, 0, 0] # Solver integ = Integrator(system, ('pos', 'vel', 'acc')) integ.add_output(LoadOutput(hinge.iprox)) integ.add_output(LoadOutput(link.iprox)) integ.add_output(LoadOutput(body.iprox)) integ.add_output(LoadOutput(body.iprox, local=True)) integ.add_output(CustomOutput(force_hinge_prox, "correct ground")) integ.add_output( CustomOutput(force_body_prox_local, "correct link distal local")) self.system = system self.integ = integ
def setUp(self): # Parameters mass = 11.234 length = 2.54 gravity = 9.81 # Build model hinge = Hinge('hinge', [0, 1, 0]) link = RigidConnection('link', [length, 0, 0]) body = RigidBody('body', mass) system = System(gravity=gravity) system.add_leaf(hinge) hinge.add_leaf(link) link.add_leaf(body) system.setup() # Custom outputs to calculate correct answer def force_body_prox_local(s): theta = s.q[hinge.istrain][0] thetadot = s.qd[hinge.istrain][0] thetadotdot = s.qdd[hinge.istrain][0] Fx = mass * (-gravity*np.sin(theta) - length*thetadot**2) Fz = mass * (+gravity*np.cos(theta) - length*thetadotdot) return [Fx, 0, Fz, 0, 0, 0] def force_hinge_prox(s): theta = s.q[hinge.istrain][0] thetadot = s.qd[hinge.istrain][0] thetadotdot = s.qdd[hinge.istrain][0] A = np.array([[+np.cos(theta), np.sin(theta)], [-np.sin(theta), np.cos(theta)]]) Fxz = -mass * length * np.dot(A, [thetadot**2, thetadotdot]) return [Fxz[0], 0, Fxz[1] + gravity*mass, 0, 0, 0] # Solver integ = Integrator(system, ('pos', 'vel', 'acc')) integ.add_output(LoadOutput(hinge.iprox)) integ.add_output(LoadOutput(link.iprox)) integ.add_output(LoadOutput(body.iprox)) integ.add_output(LoadOutput(body.iprox, local=True)) integ.add_output(CustomOutput(force_hinge_prox, "correct ground")) integ.add_output(CustomOutput(force_body_prox_local, "correct link distal local")) self.system = system self.integ = integ
class TestReactionForcesForCentrifugalForce(unittest.TestCase): """ System ------ A rigid body with offset mass, attached to a spinning hinge. Tests ----- Check centrifugal force reaction on hinge is in correct direction. """ mass = 5.0 # kg offset = 3.2 # m def setUp(self): # Rigid body with offset centre of mass self.body = RigidBody("body", self.mass, Xc=[self.offset, 0, 0]) # Hinge with axis along Z axis self.hinge = Hinge("hinge", [0, 0, 1]) # Build system self.system = System() self.system.add_leaf(self.hinge) self.hinge.add_leaf(self.body) self.system.setup() self.system.update_kinematics() # Set up nodal values initially self.system.update_matrices() def test_reactions(self): # Set angular acceleration w = 5.21 # rad/s self.hinge.vstrain[0] = w self.system.update_kinematics() # Update nodal values based on DOFs self.system.update_matrices() self.system.solve_reactions() # Solve reactions incl d'Alembert # Some parameters L = self.offset m = self.mass # Check reactions at beam root Pg = self.system.joint_reactions["ground"] P0 = self.system.joint_reactions["node-0"] Fx_expected = -m * L * w ** 2 assert_aae(P0, [Fx_expected, 0, 0, 0, 0, 0]) assert_aae(Pg, P0)
class TestReactionForcesForCentrifugalForce(unittest.TestCase): """ System ------ A rigid body with offset mass, attached to a spinning hinge. Tests ----- Check centrifugal force reaction on hinge is in correct direction. """ mass = 5.0 # kg offset = 3.2 # m def setUp(self): # Rigid body with offset centre of mass self.body = RigidBody('body', self.mass, Xc=[self.offset, 0, 0]) # Hinge with axis along Z axis self.hinge = Hinge('hinge', [0, 0, 1]) # Build system self.system = System() self.system.add_leaf(self.hinge) self.hinge.add_leaf(self.body) self.system.setup() self.system.update_kinematics() # Set up nodal values initially self.system.update_matrices() def test_reactions(self): # Set angular acceleration w = 5.21 # rad/s self.hinge.vstrain[0] = w self.system.update_kinematics() # Update nodal values based on DOFs self.system.update_matrices() self.system.solve_reactions() # Solve reactions incl d'Alembert # Some parameters L = self.offset m = self.mass # Check reactions at beam root Pg = self.system.joint_reactions['ground'] P0 = self.system.joint_reactions['node-0'] Fx_expected = -m * L * w**2 assert_aae(P0, [Fx_expected, 0, 0, 0, 0, 0]) assert_aae(Pg, P0)
class Gyroscope: def __init__(self, length, radius, mass, spin): self.length = length self.radius = radius self.mass = mass self.spin = spin self.bearing = Hinge('bearing', [0, 0, 1]) self.pivot = Hinge('pivot', [0, 1, 0]) self.axis = Hinge('axis', [1, 0, 0]) self.body = self.build_body() self.pivot.damping = 200 self.system = System(gravity=9.81) self.system.add_leaf(self.bearing) self.bearing.add_leaf(self.pivot) self.pivot.add_leaf(self.axis) self.axis.add_leaf(self.body) self.system.setup() # Prescribed DOF accelerations: constant rotational speed self.system.prescribe(self.axis) def build_body(self): Jx = self.radius**2 / 2 Jyz = (3 * self.radius**2 + self.length**2) / 12 Jyz_0 = Jyz + (self.length / 2)**2 # parallel axis theorem inertia = self.mass * np.diag([Jx, Jyz_0, Jyz_0]) return RigidBody('body', self.mass, inertia, [self.length / 2, 0, 0]) def simulate(self, xpivot=0.0, vpivot=0.0, t1=10, dt=0.05): # reset self.system.q[:] = 0.0 self.system.qd[:] = 0.0 # initial conditions self.system.q[self.pivot.istrain][0] = xpivot # initial elevation #self.system.qd[self.pivot.istrain][0] = vpivot # initial elevation spd self.system.qd[self.axis.istrain][0] = self.spin # constant spin speed # simulate integ = Integrator(self.system, ('pos', 'vel')) integ.integrate(t1, dt, nprint=None) return integ
class Gyroscope: def __init__(self, length, radius, mass, spin): self.length = length self.radius = radius self.mass = mass self.spin = spin self.bearing = Hinge('bearing', [0, 0, 1]) self.pivot = Hinge('pivot', [0, 1, 0]) self.axis = Hinge('axis', [1, 0, 0]) self.body = self.build_body() self.pivot.damping = 200 self.system = System(gravity=9.81) self.system.add_leaf(self.bearing) self.bearing.add_leaf(self.pivot) self.pivot.add_leaf(self.axis) self.axis.add_leaf(self.body) self.system.setup() # Prescribed DOF accelerations: constant rotational speed self.system.prescribe(self.axis) def build_body(self): Jx = self.radius**2 / 2 Jyz = (3*self.radius**2 + self.length**2) / 12 Jyz_0 = Jyz + (self.length/2)**2 # parallel axis theorem inertia = self.mass * np.diag([Jx, Jyz_0, Jyz_0]) return RigidBody('body', self.mass, inertia, [self.length/2, 0, 0]) def simulate(self, xpivot=0.0, vpivot=0.0, t1=10, dt=0.05): # reset self.system.q[:] = 0.0 self.system.qd[:] = 0.0 # initial conditions self.system.q[self.pivot.istrain][0] = xpivot # initial elevation #self.system.qd[self.pivot.istrain][0] = vpivot # initial elevation spd self.system.qd[self.axis.istrain][0] = self.spin # constant spin speed # simulate integ = Integrator(self.system, ('pos', 'vel')) integ.integrate(t1, dt, nprint=None) return integ
def test_iter_elements(self): # /-- c2 # c1-| # \-- c3 --- c4 s = System() c1 = RigidConnection('c1') c2 = RigidConnection('c2') c3 = RigidConnection('c3') c4 = RigidConnection('c4') s.add_leaf(c1) c1.add_leaf(c2) c1.add_leaf(c3) c3.add_leaf(c4) s.setup() # Should iter elements depth-first self.assertEqual(list(s.iter_elements()), [c1, c2, c3, c4])
def test_callback(self): s = System() s.setup() # Exponential decay: qd = -A q def callback(system, ti, q_struct, q_other): self.assertIs(system, s) self.assertEqual(len(q_other), 1) return -q_other integ = Integrator(s) t, y = integ.integrate(9.0, 0.1, extra_states=np.ones(1), callback=callback) # Check time vector and shape of result assert_array_equal(t, np.arange(0, 9.0, 0.1)) self.assertEqual(len(y), 1) self.assertEqual(y[0].shape, (len(t), 1)) assert_aae(y[0][:, 0], np.exp(-t))
class hinged_beam_tests(unittest.TestCase): density = 5.0 length = 20.0 force = 34.2 # N/m hinge_torque = 0.0 free_beam = False def setUp(self): # FE model for beam x = linspace(0, self.length, 20) fe = BeamFE(x, density=self.density, EA=0, EIy=1, EIz=0) fe.set_boundary_conditions('C', 'F') self.beam = ModalElementFromFE('beam', fe, 0) # Set loading - in negative Z direction load = np.zeros((len(x), 3)) load[:, 2] = -self.force self.beam.loading = load # Hinge with axis along Y axis self.hinge = Hinge('hinge', [0, 1, 0]) self.hinge.internal_torque = self.hinge_torque # Build system self.system = System() self.system.add_leaf(self.hinge) self.hinge.add_leaf(self.beam) self.system.setup() if not self.free_beam: # Prescribe hinge to be fixed self.system.prescribe(self.hinge) # Initial calculations self.recalc() def recalc(self): self.system.update_kinematics() # Set up nodal values initially self.system.update_matrices() self.system.solve_accelerations() # Calculate accelerations of DOFs self.system.update_kinematics() # Update nodal values based on DOFs self.system.update_matrices() self.system.solve_reactions() # Solve reactions incl d'Alembert
def test_assemble(self): # Test system: # # /-- m2 # m1 -----conn----| # \-- m3 s = System() m1 = RigidBody('m1', 1.3) m2 = RigidBody('m2', 3.4) m3 = RigidBody('m3', 7.5) conn = RigidConnection('conn') s.add_leaf(conn) s.add_leaf(m1) conn.add_leaf(m2) conn.add_leaf(m3) s.setup() # Check starting mass matrices of elements are as expected assert_aae(np.diag(m1.mass_vv[:3, :3]), 1.3) assert_aae(np.diag(m2.mass_vv[:3, :3]), 3.4) assert_aae(np.diag(m3.mass_vv[:3, :3]), 7.5) # Initially make system matrix empty for testing s.lhs[:, :] = 0 assert_aae(s.lhs, 0) # After assembly, the mass matrices are put in the correct places: # 0:6 -> m1 node # 6:12 -> conn constraints # 12:18 -> m2 node # 12:18 -> m3 node s.assemble() M = s.lhs.copy() # Subtract expected mass M[0:6, 0:6] -= m1.mass_vv M[12:18, 12:18] -= m2.mass_vv + m3.mass_vv # Subtract expected constraints M[0:6, 6:12] -= conn.F_vp M[6:12, 0:6] -= conn.F_vp M[12:18, 6:12] -= conn.F_vd M[6:12, 12:18] -= conn.F_vd assert_aae(M, 0)
def build_system(): # Calculate inertia, stiffness and damping I2 = mass * length**2 k = (2 * np.pi * natfreq)**2 * I2 c = 2 * damping_factor * I2 * (2 * np.pi * natfreq) # Build model hinge = Hinge('hinge', [0, 0, 1]) hinge.stiffness = k hinge.damping = c link = RigidConnection('link', [length, 0, 0]) body = RigidBody('body', mass) system = System() system.add_leaf(hinge) hinge.add_leaf(link) link.add_leaf(body) system.setup() return system
def test_deflection_under_load(self): F = 4.2 # N/m M = (F * self.length) * (self.length / 2) expected_rotation = M / self.stiffness expected_tip_motion = expected_rotation * self.length # First, calculate directly loading = zeros((50, 3)) loading[:, 2] = F Qr, Qw, Qs = self.beam.modes.distributed_loading(loading, [0]) actual_tip_motion = Qs / self.beam.stiffness assert_aae(actual_tip_motion[0], expected_tip_motion, decimal=4) # Now check using system equilibrium self.beam.loading = lambda beam, time: loading system = System() system.add_leaf(self.beam) system.setup() system.find_equilibrium() actual_tip_motion2 = system.q[system.elements['beam'].istrain] assert_aae(actual_tip_motion2[0], expected_tip_motion, decimal=4)
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 test_applied_force(self): # Set up a hinge with a mass offset on a rigid body. The # reduced system should have 1 DOF -- the hinge rotation -- # with the associated mass being the inertia of the mass about # the hinge, and the associated generalised force being the # applied moment. mass = 36.2 zforce = -30 L = 3.2 s = System() h = Hinge('hinge', [0, 1, 0]) c = RigidConnection('conn', [L, 0, 0]) b = RigidBody('body', mass, nodal_load=[0, 0, zforce]) s.add_leaf(h) h.add_leaf(c) c.add_leaf(b) s.setup() rsys = ReducedSystem(s) self.assertEqual(rsys.M.shape, (1, 1)) self.assertEqual(rsys.Q.shape, (1, )) self.assertEqual(rsys.M[0, 0], mass * L**2) # inertial about hinge self.assertEqual(rsys.Q[0], -zforce * L) # moment about hinge
def test_find_equilibrium(self): g = 9.81 m = 23.1 k = 45.2 s = System(gravity=g) slider = PrismaticJoint('slider', [0, 0, 1]) slider.stiffness = k body = RigidBody('body', mass=m) s.add_leaf(slider) slider.add_leaf(body) s.setup() # Initially position should be zero and acceleration nonzero s.solve_accelerations() assert_aae(slider.xstrain, 0) assert_aae(slider.astrain, -g) # At equilibrium, position should be nozero and force on body zero s.find_equilibrium() s.update_matrices() # recalculate stiffness force s.solve_accelerations() assert_aae(slider.xstrain, -m * g / k) assert_aae(slider.astrain, 0)
def test_adding_elements_with_strains(self): slider = PrismaticJoint('slider', [1, 0, 0]) conn = RigidConnection('conn') body = RigidBody('body', mass=1.235) s = System() s.add_leaf(slider) slider.add_leaf(conn) conn.add_leaf(body) s.setup() # Should have dict of elements self.assertEqual(s.elements, {'slider': slider, 'conn': conn, 'body': body}) # Number of states: # 6 ground # + 6 constraints on slider # + 1 strain in slider # + 6 <node-0> between slider and conn # + 6 constraints on conn # + 6 <node-1> between conn and body # --- # 31 self.assertEqual(s.lhs.shape, (31, 31)) for vec in (s.rhs, s.qd, s.qdd): self.assertEqual(len(vec), 31) # Check there is the one slider dof self.assertEqual(len(s.q.dofs), 1) self.assertEqual(len(s.qd.dofs), 1) self.assertEqual(len(s.qdd.dofs), 1) # After prescribing the slider, there should be no dofs s.prescribe(slider) self.assertEqual(len(s.q.dofs), 0) self.assertEqual(len(s.qd.dofs), 0) self.assertEqual(len(s.qdd.dofs), 0)
class TestReactionForcesWithRotatedBeam(unittest.TestCase): """Intended to check the transformation from blade loading to rotor loading in a wind turbine rotor: the loads are applied to the beam in the local rotated coordinate system, check they work through to the ground reactions correctly. """ force = 24.1 length = 4.3 root_length = 0.0 def setUp(self): # FE model for beam - no modes, i.e. rigid self.x = x = linspace(0, self.length, 20) fe = BeamFE(x, density=2, EA=0, EIy=0, EIz=0) # Build the elements self.shaft = Hinge('shaft', [1, 0, 0]) self.roots = [] self.blades = [] self.pitch_bearings = [] for ib in range(1): R = rotations(('x', ib * 2 * pi / 3), ('y', -pi / 2)) root_offset = dot(R, [self.root_length, 0, 0]) root = RigidConnection('root%d' % (ib + 1), root_offset, R) bearing = Hinge('pitch%d' % (ib + 1), [1, 0, 0]) blade = ModalElementFromFE('blade%d' % (ib + 1), fe, 0) self.shaft.add_leaf(root) root.add_leaf(bearing) bearing.add_leaf(blade) self.roots.append(root) self.blades.append(blade) self.pitch_bearings.append(bearing) # Build system self.system = System() self.system.add_leaf(self.shaft) self.system.setup() self.system.update_kinematics() # Set up nodal values initially self.system.update_matrices() def test_reactions(self): # Some parameters L = self.length F = self.length * self.force # Set loading - in local z direction load = np.zeros((len(self.x), 3)) load[:, 2] = self.force self.blades[0].loading = load self.system.update_kinematics() self.system.update_matrices() self.system.solve_reactions() # Check reactions at ground (0, 0, 0) P = -self.system.joint_reactions['ground'] F_expected = [-F, 0, 0] M_expected = [0, -F * (L + self.root_length) / 2, 0] assert_aae(P, np.r_[F_expected, M_expected]) # Reactions on other side of hinge P2 = -self.system.joint_reactions['node-0'] assert_aae(P, P2) # Now set pitch angle to 45deg # NB: hinge rotation is opposite to wind turbine pitch convention self.pitch_bearings[0].xstrain[0] = -pi / 4 self.system.update_kinematics() self.system.update_matrices() self.system.solve_reactions() # Check reactions at ground (0, 0, 0) P = -self.system.joint_reactions['ground'] F_expected = [-F / sqrt(2), F / sqrt(2), 0] M_expected = [-F / sqrt(2) * L / 2, -F / sqrt(2) * L / 2, 0] assert_aae(P, np.r_[F_expected, M_expected]) # Reactions on other side of hinge P2 = -self.system.joint_reactions['node-0'] assert_aae(P, P2)
def setUp(self): # Parameters mass = 11.234 length = 2.54 gravity = 9.81 # Build model slider = PrismaticJoint('slider', [1, 0, 0]) link = RigidConnection('link', [0, 0, length]) body = RigidBody('body', mass) system = System(gravity=gravity) system.add_leaf(slider) slider.add_leaf(link) link.add_leaf(body) system.setup() # Prescribe motion -- sinusoidal acceleration motion_frequency = 1 # Hz motion_amplitude = 2.3 # m # x = motion_amplitude * np.cos(w*t) # v = -motion_amplitude * np.sin(w*t) * w # a = -motion_amplitude * np.cos(w*t) * w**2 def prescribed_acceleration(t): w = 2 * np.pi * motion_frequency return -w**2 * motion_amplitude * np.cos(w * t) system.prescribe(slider, prescribed_acceleration) # Set the correct initial condition system.q[slider.istrain][0] = motion_amplitude system.qd[slider.istrain][0] = 0.0 # Custom outputs to calculate correct answer def force_body_prox(s): a = prescribed_acceleration(s.time) Fx = mass * a Fz = mass * gravity return [Fx, 0, Fz, 0, 0, 0] def force_link_prox(s): a = prescribed_acceleration(s.time) Fx = mass * a Fz = mass * gravity My = length * Fx return [Fx, 0, Fz, 0, My, 0] def force_slider_prox(s): a = prescribed_acceleration(s.time) x = -a / (2 * np.pi * motion_frequency)**2 Fx = mass * a Fz = mass * gravity My = length * Fx - x * Fz return [Fx, 0, Fz, 0, My, 0] # Solver integ = Integrator(system, ('pos', 'vel', 'acc')) integ.add_output(LoadOutput(slider.iprox)) integ.add_output(LoadOutput(link.iprox)) integ.add_output(LoadOutput(body.iprox)) integ.add_output(CustomOutput(force_slider_prox, "correct ground")) integ.add_output(CustomOutput(force_link_prox, "correct slider dist")) integ.add_output(CustomOutput(force_body_prox, "correct link dist")) self.system = system self.integ = integ
class TestReactionForcesOnModalElementFromFE(unittest.TestCase): """ System ------ A triangular rigid beam, offset by a rigid link from a hinge. Tests ----- Set the angular acceleration of the hinge. Check the reaction forces at the centre and at the root of the beam. """ mass = 5.0 # kg length = 20.0 # m offset = 3.2 # m force = -34.2 # N / m def setUp(self): # FE model for beam - no modes, i.e. rigid x = linspace(0, self.length, 20) density = (2 * self.mass / self.length) * (1 - x / self.length) fe = BeamFE(x, density=density, EA=0, EIy=1, EIz=0) fe.set_boundary_conditions('C', 'F') self.beam = ModalElementFromFE('beam', fe, 0) # Set loading - in Z direction load = np.zeros((len(x), 3)) load[:, 2] = self.force self.beam.loading = load # Offset from hinge axis self.conn = RigidConnection('offset', [self.offset, 0, 0]) # Hinge with axis along Y axis self.hinge = Hinge('hinge', [0, 1, 0]) # Build system self.system = System() self.system.add_leaf(self.hinge) self.hinge.add_leaf(self.conn) self.conn.add_leaf(self.beam) self.system.setup() self.system.update_kinematics() # Set up nodal values initially def test_reactions(self): # Set angular acceleration alpha = 1.235 # rad/s2 self.hinge.astrain[0] = alpha self.system.update_kinematics() # Update nodal values based on DOFs self.system.solve_reactions() # Solve reactions incl d'Alembert # Some parameters L = self.length m = self.mass Ro = self.offset Rg = L / 3 # distance to CoM along beam IG = m * L**2 / 18 assert_aae(m, self.beam.mass_vv[0, 0]) # Check reactions at beam root P = self.system.joint_reactions['node-1'] Fz_expected = (-m * (Ro + Rg) * alpha - self.force * L) My_expected = ((IG + m * Rg * (Ro + Rg)) * alpha + self.force * L**2 / 2) assert_aae(P, [0, 0, Fz_expected, 0, My_expected, 0])
def test_update_kinematics_results(self): # Test system: (all rigid connections of length 1) # # [hinge] # (gnd)---c1---(0) # (1)---c2---(2) # | # y c3 # | | # |--> x (3) # s = System() c1 = RigidConnection('c1', [1, 0, 0]) c2 = RigidConnection('c2', [1, 0, 0]) c3 = RigidConnection('c3', [0, -1, 0]) hinge = Hinge('hinge', [0, 0, 1]) s.add_leaf(c1) c1.add_leaf(hinge) hinge.add_leaf(c2) c2.add_leaf(c3) s.setup() # All velocities and accelerations should be zero for el in [c1, c2, c3, hinge]: assert_aae(el.vp, 0) assert_aae(el.vd, 0) assert_aae(el.ap, 0) assert_aae(el.ad, 0) # (gnd) assert_aae(c1.rp, 0) assert_aae(c1.Rp, np.eye(3)) # (0) assert_aae(c1.rd, [1, 0, 0]) assert_aae(c1.Rd, np.eye(3)) # (1) assert_aae(c2.rp, [1, 0, 0]) assert_aae(c2.Rp, np.eye(3)) # (2) assert_aae(c3.rp, [2, 0, 0]) assert_aae(c3.Rp, np.eye(3)) # (3) assert_aae(c3.rd, [2, -1, 0]) assert_aae(c3.Rd, np.eye(3)) ##### now set angular velocity of hinge ##### hinge.vstrain[0] = 1.0 s.update_kinematics() # (gnd) assert_aae(c1.vp, 0) assert_aae(c1.ap, 0) # (0) assert_aae(c1.vd, 0) assert_aae(c1.ad, 0) # (1) assert_aae(c2.vp, [0, 0, 0, 0, 0, 1.0]) assert_aae(c2.ap, 0) # (2) assert_aae(c3.vp, [0, 1, 0, 0, 0, 1.0]) assert_aae(c3.ap, [-1, 0, 0, 0, 0, 0]) # centripetal acceleration # (3) assert_aae(c3.vd, [1, 1, 0, 0, 0, 1.0]) assert_aae(c3.ad, [-1, 1, 0, 0, 0, 0]) # centripetal acceleration
def _rsys_from_beam(self, beam): s = System() s.add_leaf(beam) s.setup() rsys = ReducedSystem(s) return rsys