def test_imitating_lift_coefficient(self): # Try to set lift coefficient so that thrust force is equal to # expected value from momentum theory. This should mean the # calculation is already converged. # Choose some values U = 5.4 # wind speed w = 1.2 # rotor speed r = 15.3 # radius a = 0.14 # induction factor Nb = 3 # number of blades c = 1.9 # chord def lift_drag(alpha): # Thrust should be 2 rho A U^2 a (1-a)` # for annulus, A = 2 pi r dr # If no drag, then # thrust on blade = 0.5 rho c (U(1-a)/sin(phi))^2 CL dr cos(phi) # So CL = (8 pi r a sin^2 phi) / ((1-a) Nb c cos(phi)) CL = 8*pi*r * a * sin(alpha)**2 / ((1-a) * Nb * c * cos(alpha)) return [CL[0], 0] LSR = w * r / U solidity = Nb * c / (2*pi*r) factors = np.array([[a, 0]]) Wnorm, phi = inflow(LSR, factors) cphi, sphi = np.cos(phi[0]), np.sin(phi[0]) A = array([[cphi, sphi], [-sphi, cphi]]) force_coeffs = np.array([np.dot(A, lift_drag(phi))]) factors = iterate_induction_factors(LSR, force_coeffs, solidity, 0, factors) assert_aae(a, factors[0, 0]) assert np.all(factors[:, 1] > 0)
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_forces_with_one_annulus(self): args = (12.2, 12*pi/30, 0) factors = self.model.solve(*args) all_forces = self.model.forces(*args, rho=1.225, factors=factors) one_forces = self.model.forces(*args, rho=1.225, factors=factors[2:3], annuli=slice(2, 3)) assert_aae(one_forces, all_forces[2:3, :])
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_lift_drag_one_annulus(self): alpha = np.random.rand(len(self.model.radii)) * pi all_lift_drag = self.model.lift_drag(alpha) one_lift_drag = self.model.lift_drag(alpha[2:3], annuli=slice(2, 3)) another_lift_drag = self.model.lift_drag(alpha[2:3], annuli=[2]) self.assertIsInstance(one_lift_drag, np.ndarray) assert_aae(one_lift_drag, all_lift_drag[2:3, :]) assert_aae(another_lift_drag, all_lift_drag[2:3, :])
def test_inflow_derivatives_with_one_annulus(self): args = (12.2, 12*pi/30, 0) factors = self.model.solve(*args) all_derivs = self.model.inflow_derivatives(*args, factors=factors*1.1) one_derivs = self.model.inflow_derivatives(*args, factors=factors[2:3]*1.1, annuli=slice(2, 3)) assert_aae(one_derivs, all_derivs[2:3, :])
def rotate(lon0, lat0, x, y, z): x2 = cos(lat0)*cos(lon0)*x - cos(lat0)*sin(lon0)*y + sin(lat0)*z y2 = sin(lon0)*x + cos(lon0)*y z2 = -sin(lat0)*cos(lon0)*x + sin(lat0)*sin(lon0)*y + cos(lat0)*z x3, y3, z3 = rotate_z(lon0, x, y, z) x4, y4, z4 = rotate_y(lat0, x3, y3, z3) assert_aae([x2,y2,z2], [x4,y4,z4], 15) return (x2,y2,z2)
def test_distal_node_position_with_zero_strain(self): NTESTS = 5 for i in range(NTESTS): el = _make_random_element(self.rdm) set_random_state(el, self.rdm, 1) el.xstrain[:] = 0 el.calc_distal_pos() last_node_x0 = [el._params['length'], 0, 0] assert_aae(el.rd, el.rp + dot(el.Rp, last_node_x0)) assert_aae(el.Rd, el.Rp)
def test_solve_wake_is_same_as_solve_with_extra_factors(self): args = (12.2, 12*pi/30, 0) wake = self.model.solve_wake(*args) extra_velocities = wake * 0.43 extra_velocity_factors = extra_velocities / args[0] factors = self.model.solve( *args, extra_velocity_factors=extra_velocity_factors) wake = self.model.solve_wake(*args, extra_velocities=extra_velocities) assert_aae(factors[:, 0], wake[:, 0] / args[0]) assert_aae(factors[:, 1], wake[:, 1] / args[1] / self.model.radii)
def test_solve_wake_is_same_as_solve_with_extra_factors(self): args = (12.2, 12 * pi / 30, 0) wake = self.model.solve_wake(*args) extra_velocities = wake * 0.43 extra_velocity_factors = extra_velocities / args[0] factors = self.model.solve( *args, extra_velocity_factors=extra_velocity_factors) wake = self.model.solve_wake(*args, extra_velocities=extra_velocities) assert_aae(factors[:, 0], wake[:, 0] / args[0]) assert_aae(factors[:, 1], wake[:, 1] / args[1] / self.model.radii)
def test_point_equal(): p1 = Point(0,0.001234567890123456786) p2 = Point(0,0.001234567890123456987) eq = p1.equals(p2) ok_(eq==False, eq) aeq = p1.almost_equals(p2,18) ok_(aeq==True, aeq) assert_aae(p1.coords[:][0], p2.coords[:][0],18) assert_ape(p1.coords[:][0][1], p2.coords[:][0][1],16)
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_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_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_reaction_on_hinged_beam(self): """Reaction force should balance applied force""" # Applied force and moment (+ve in +Z and +Y) Fa = -self.force * self.length Ma = -Fa * self.length / 2 # Acceleration inertia = self.density * (self.length ** 3) / 3 ang_acc = Ma / inertia assert_aae(self.hinge.astrain[0], ang_acc) # Inertial d'Alembert force and moment Mi = -Ma # moments balance initially Fi = self.density * self.length**2 * ang_acc / 2 # Check equilibrium. No applied forces on hinge, so both nodes # should be the same. assert_aae(self.system.joint_reactions['node-0'], [0, 0, -(Fa + Fi), 0, -(Ma + Mi), 0]) assert_aae(self.system.joint_reactions['ground'], self.system.joint_reactions['node-0']) # The base of the beam should have zero linear acceleration # but should have an angular acceleration: assert_aae(self.beam.ap, [0, 0, 0, 0, ang_acc, 0])
def test_distal_node_position(self): NTESTS = 5 for i in range(NTESTS): el = _make_random_element(self.rdm) set_random_state(el, self.rdm, 1) last_node_x0 = [el._params['length'], 0, 0] Xd = dot(el.shapes[-6:], el.xstrain) last_node_defl = Xd[0:3] last_node_rotn = Xd[3:6] el.calc_distal_pos() assert_aae(el.rd, el.rp + dot(el.Rp, last_node_x0 + last_node_defl)) assert_aae(el.Rd, dot(el.Rp, np.eye(3) + skewmat(last_node_rotn)))
def test_first_mode_frequency(self): # From Reddy1993, p. 160 x = np.linspace(0, 1, 16) # Using the z axis as the transverse direction gives the same # sign convention as Reddy uses in 2D, namely that rotations # are positive clockwise. fe = BeamFE(x, density=1, EA=0, EIy=1, EIz=0) fe.set_boundary_conditions('C', 'F') fe.set_dofs([False, False, True, False, True, False]) element = ModalElementFromFE('elem', fe) Mmodal = element.mass_ee Kmodal = element.K w = np.sqrt(np.diag(Kmodal) / np.diag(Mmodal)) assert_aae(w[0], 3.5160, decimal=4)
def test_coordinate_frames(self): """Beam loading is in local frame, while reaction forces are global. So by rotating the beam, the reaction forces should change. """ self.hinge.xstrain[0] = pi / 2 self.recalc() # Applied force and moment Fx = -self.force * self.length # in -X direction My = -Fx * self.length / 2 # in +Y direction # Check equilibrium. No applied forces on hinge, so both nodes # should be the same. assert_aae(self.system.joint_reactions['node-0'], [-Fx, 0, 0, 0, -My, 0]) assert_aae(self.system.joint_reactions['ground'], self.system.joint_reactions['node-0'])
def test_rotations(self): assert_aae(rotations(('z', pi / 2)), [[0, -1, 0], [1, 0, 0], [0, 0, 1]]) # Work these two examples out by hand: apply the rotations in # turn about the updated axes, and see where the body axes end # up pointing. assert_aae(rotations(('z', pi / 2), ('x', pi / 2)), [[0, 0, 1], [1, 0, 0], [0, 1, 0]]) assert_aae(rotations(('z', pi / 2), ('x', pi / 2), ('z', pi / 2)), [[0, 0, 1], [0, -1, 0], [1, 0, 0]]) assert_aae(rotations(('x', pi / 2), ('z', pi / 2)), [[0, -1, 0], [0, 0, -1], [1, 0, 0]])
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))
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 test_static_deflection(self): x = array([0.0, 4.0, 10.0]) EI = 144.0 # Using the z axis as the transverse direction gives the same # sign convention as Reddy uses in 2D, namely that rotations # are positive clockwise. fe = BeamFE(x, density=10, EA=0, EIy=EI, EIz=0) fe.set_boundary_conditions('C', 'F') fe.set_dofs([False, False, True, False, True, False]) element = ModalElementFromFE('elem', fe) # Distributed load, linearly interpolated load = np.zeros((3, 3)) load[-1, 2] = -100 # Load in z direction at tip element.apply_distributed_loading(load) defl = -element.applied_stress / np.diag(element.K) # Check against directly calculating static deflection from FE Q = fe.distribute_load(interleave(load, 6)) defl_fe, reactions_fe = fe.static_deflection(Q) assert_aae(dot(element.shapes, defl), defl_fe, decimal=2)
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_step_response(self): # Calculate system parameters zeta = self.damping_coeff wn = np.sqrt(self.K / self.M) wd = wn * np.sqrt(1 - zeta**2) psi = np.arcsin(zeta) # Forcing - step function force_amp = 3.4 t0 = 2.3 force_func = lambda t: (force_amp if t >= t0 else 0) def callback(system, time, q_struct, q_other): self.body.nodal_load = [0, 0, force_func(time)] return [] t, solution = integrate(self.system, callback) decay = np.exp(-zeta * wn * (t - t0)) expected_defl = 1 - decay * np.cos(wd * (t - t0) - psi) / np.cos(psi) X = force_amp / self.K expected_defl[t < t0] = 0 assert_aae(solution[:, 0], X * expected_defl)
def test_distributed_loading_uniform_loading_aligned_with_mode(self): P = self._uniform_force(1, 3.4) Qr, Qw, Qe = self.modes.distributed_loading(P, [0]) assert_aae(Qr, [0, 3.4 * 10, 0]) assert_aae(Qw, [0, 0, 3.4 * self.L**2 / 2]) # Generalised force should be integral of applied # force and deflection. Integral of x^2 if L^3 / 3 assert_aae(Qe, [3.4 * trapz(self.x**2, self.x)])
def test_rotmats(self): assert_array_equal(rotmat_x(0), eye(3)) assert_array_equal(rotmat_y(0), eye(3)) assert_array_equal(rotmat_z(0), eye(3)) # Rotate 45 deg about each axis assert_aae(dot(rotmat_x(pi / 4), [1, 1, 1]), [1, 0, sqrt(2)]) assert_aae(dot(rotmat_y(pi / 4), [1, 1, 1]), [sqrt(2), 1, 0]) assert_aae(dot(rotmat_z(pi / 4), [1, 1, 1]), [0, sqrt(2), 1])
def test_rigid_element_reference_loading(self): # Make an element with no modes (rigid) fe = BeamFE(np.linspace(0, 1, 11), density=1, EA=0, EIy=1, EIz=0) fe.set_boundary_conditions('C', 'F') fe.set_dofs([False, False, True, False, True, False]) element = ModalElementFromFE('elem', fe, 0) # Distributed load, linearly interpolated load = np.zeros((11, 3)) load[:, 2] = -100 # Uniform load in z direction element.apply_distributed_loading(load) assert_aae(element.applied_stress, []) assert_aae(element.applied_forces[0:6], [0, 0, -100 * 1, 0, 50, 0]) assert_aae(element.applied_forces[6:12], 0) # Distributed load, triangle at tip load = np.zeros((11, 3)) load[-1, 2] = -100 # tapered load in z direction element.applied_forces[:] = 0 # reset element.apply_distributed_loading(load) F = -100 * 0.1 / 2 assert_aae(element.applied_forces[0:6], [0, 0, F, 0, -F * (0.9 + 0.1 * 2 / 3), 0]) assert_aae(element.applied_forces[6:12], 0)
def test_rotations(self): assert_aae(rotations(('z', pi/2)), [[0, -1, 0], [1, 0, 0], [0, 0, 1]]) # Work these two examples out by hand: apply the rotations in # turn about the updated axes, and see where the body axes end # up pointing. assert_aae(rotations(('z', pi/2), ('x', pi/2)), [[0, 0, 1], [1, 0, 0], [0, 1, 0]]) assert_aae(rotations(('z', pi/2), ('x', pi/2), ('z', pi/2)), [[0, 0, 1], [0, -1, 0], [1, 0, 0]]) assert_aae(rotations(('x', pi/2), ('z', pi/2)), [[0, -1, 0], [0, 0, -1], [1, 0, 0]])
def test_reaction_on_fixed_beam(self): """Reaction force should balance applied force""" # Applied force and moment (+ve in +Z and +Y) Fa = -self.force * self.length Ma = -Fa * self.length / 2 # Check equilibrium. No applied forces on hinge, so both nodes # should be the same. assert_aae(self.system.joint_reactions['node-0'], [0, 0, -Fa, 0, -Ma, 0]) assert_aae(self.system.joint_reactions['ground'], self.system.joint_reactions['node-0']) # The base of the beam should have zero acceleration: assert_aae(self.beam.ap, 0)
def test_reaction_on_forced_beam(self): # Acceleration inertia = self.density * (self.length ** 3) / 3 ang_acc = self.hinge_torque / inertia assert_aae(self.hinge.astrain[0], ang_acc) # Inertial d'Alembert force and moment Mi = -inertia * ang_acc Fi = self.density * self.length**2 * ang_acc / 2 # Check equilibrium. assert_aae(self.system.joint_reactions['node-0'], [0, 0, -Fi, 0, -Mi, 0]) assert_aae(self.system.joint_reactions['ground'], [0, 0, -Fi, 0, -Mi, 0])
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_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 interpolated_by_thickness(self): # Interpolate between 13% and 17% data lift_drag_values = self.db.for_thickness(0.15) lift_drag = interp1d(self.db.alpha, lift_drag_values, axis=0) assert_aae(lift_drag(10 * pi / 180), [(1.460 + 1.500) / 2, (0.016 + 0.014) / 2])
def test_thirteen_percent_foil_interpolated_by_alpha(self): # Interpolate between 4 and 6 deg alpha lift_drag_values = self.db.for_thickness(0.13) lift_drag = interp1d(self.db.alpha, lift_drag_values, axis=0) assert_aae(lift_drag(5 * pi / 180), [(0.890 + 1.100) / 2, (0.009 + 0.012) / 2])