Coverage for gpkit/tests/t_tools.py: 100%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

83 statements  

1"""Tests for tools module""" 

2import unittest 

3import numpy as np 

4from numpy import log 

5from gpkit import Variable, VectorVariable, Model, NomialArray 

6from gpkit.tools.autosweep import BinarySweepTree 

7from gpkit.tools.tools import te_exp_minus1, te_secant, te_tangent 

8from gpkit.small_scripts import mag 

9from gpkit import parse_variables 

10 

11 

12def assert_logtol(first, second, logtol=1e-6): 

13 "Asserts that the logs of two arrays have a given abstol" 

14 np.testing.assert_allclose(log(mag(first)), log(mag(second)), 

15 atol=logtol, rtol=0) 

16 

17 

18class OnlyVectorParse(Model): 

19 """ 

20 Variables of length 3 

21 --------------------- 

22 x [-] just another variable 

23 """ 

24 @parse_variables(__doc__, globals()) 

25 def setup(self): 

26 pass 

27 

28 

29class Fuselage(Model): 

30 """The thing that carries the fuel, engine, and payload 

31 

32 Variables 

33 --------- 

34 f [-] Fineness 

35 g 9.81 [m/s^2] Standard gravity 

36 k [-] Form factor 

37 l [ft] Length 

38 mfac 2.0 [-] Weight margin factor 

39 R [ft] Radius 

40 rhocfrp 1.6 [g/cm^3] Density of CFRP 

41 rhofuel 6.01 [lbf/gallon] Density of 100LL fuel 

42 S [ft^2] Wetted area 

43 t 0.024 [in] Minimum skin thickness 

44 Vol [ft^3] Volume 

45 W [lbf] Weight 

46 

47 Upper Unbounded 

48 --------------- 

49 k, W 

50 

51 """ 

52 

53 # pylint: disable=undefined-variable, invalid-name 

54 @parse_variables(__doc__, globals()) 

55 def setup(self, Wfueltot): 

56 return [ 

57 f == l/R/2, 

58 k >= 1 + 60/f**3 + f/400, 

59 3*(S/np.pi)**1.6075 >= 2*(l*R*2)**1.6075 + (2*R)**(2*1.6075), 

60 Vol <= 4*np.pi/3*(l/2)*R**2, 

61 Vol >= Wfueltot/rhofuel, 

62 W/mfac >= S*rhocfrp*t*g, 

63 ] 

64 

65 

66class TestTools(unittest.TestCase): 

67 """TestCase for math models""" 

68 

69 def test_vector_only_parse(self): 

70 # pylint: disable=no-member 

71 m = OnlyVectorParse() 

72 self.assertTrue(hasattr(m, "x")) 

73 self.assertIsInstance(m.x, NomialArray) 

74 self.assertEqual(len(m.x), 3) 

75 

76 def test_parse_variables(self): 

77 Fuselage(Variable("Wfueltot", 5, "lbf")) 

78 

79 def test_binary_sweep_tree(self): 

80 bst0 = BinarySweepTree([1, 2], [{"cost": 1}, {"cost": 8}], None, None) 

81 assert_logtol(bst0.sample_at([1, 1.5, 2])["cost"], [1, 3.375, 8], 1e-3) 

82 bst0.add_split(1.5, {"cost": 4}) 

83 assert_logtol(bst0.sample_at([1, 1.25, 1.5, 1.75, 2])["cost"], 

84 [1, 2.144, 4, 5.799, 8], 1e-3) 

85 

86 def test_dual_objective(self): 

87 L = Variable("L") 

88 W = Variable("W") 

89 eqns = [L >= 1, W >= 1, 

90 L*W == 10] 

91 N = 4 

92 ws = Variable("w_{CO}", ("sweep", np.linspace(1/N, 1-1/N, N)), "-") 

93 w_s = Variable("v_{CO}", lambda c: 1-c[ws], "-") 

94 obj = ws*(L+W) + w_s*(W**-1 * L**-3) 

95 m = Model(obj, eqns) 

96 sol = m.solve(verbosity=0) 

97 a = sol["cost"] 

98 b = np.array([1.58856898, 2.6410391, 3.69348122, 4.74591386]) 

99 self.assertTrue((abs(a-b)/(a+b+1e-7) < 1e-7).all()) 

100 

101 def test_te_exp_minus1(self): 

102 """Test Taylor expansion of e^x - 1""" 

103 x = Variable('x') 

104 self.assertEqual(te_exp_minus1(x, 1), x) 

105 self.assertEqual(te_exp_minus1(x, 3), x + x**2/2 + x**3/6) 

106 self.assertEqual(te_exp_minus1(x, 0), 0) 

107 # make sure x was not modified 

108 self.assertEqual(x, Variable('x')) 

109 # try for VectorVariable too 

110 y = VectorVariable(3, 'y') 

111 self.assertEqual(te_exp_minus1(y, 1), y) 

112 self.assertEqual(te_exp_minus1(y, 3), y + y**2/2 + y**3/6) 

113 self.assertEqual(te_exp_minus1(y, 0), 0) 

114 # make sure y was not modified 

115 self.assertEqual(y, VectorVariable(3, 'y')) 

116 

117 def test_te_secant(self): 

118 "Test Taylor expansion of secant(var)" 

119 x = Variable('x') 

120 self.assertEqual(te_secant(x, 1), 1 + x**2/2) 

121 a = te_secant(x, 2) 

122 b = 1 + x**2/2 + 5*x**4/24 

123 self.assertTrue(all([abs(val) <= 1e-10 

124 for val in (a.hmap - b.hmap).values()])) # pylint:disable=no-member 

125 self.assertEqual(te_secant(x, 0), 1) 

126 # make sure x was not modified 

127 self.assertEqual(x, Variable('x')) 

128 # try for VectorVariable too 

129 y = VectorVariable(3, 'y') 

130 self.assertTrue(te_secant(y, 0) == 1) # truthy bc monomial constraint 

131 self.assertTrue(all(te_secant(y, 1) == 1 + y**2/2)) 

132 self.assertTrue(all(te_secant(y, 2) == 1 + y**2/2 + 5*y**4/24)) 

133 # make sure y was not modified 

134 self.assertEqual(y, VectorVariable(3, 'y')) 

135 _ = te_secant(x, 13) # to trigger the extension 

136 

137 def test_te_tangent(self): 

138 "Test Taylor expansion of tangent(var)" 

139 x = Variable('x') 

140 self.assertEqual(te_tangent(x, 1), x) 

141 self.assertEqual(te_tangent(x, 3), x + x**3/3 + 2*x**5/15) 

142 self.assertEqual(te_tangent(x, 0), 0) 

143 # make sure x was not modified 

144 self.assertEqual(x, Variable('x')) 

145 # try for VectorVariable too 

146 y = VectorVariable(3, 'y') 

147 self.assertEqual(te_tangent(y, 1), y) 

148 self.assertEqual(te_tangent(y, 3), y + y**3/3 + 2*y**5/15) 

149 self.assertEqual(te_tangent(y, 0), 0) 

150 # make sure y was not modified 

151 self.assertEqual(y, VectorVariable(3, 'y')) 

152 with self.assertRaises(NotImplementedError): 

153 _ = te_tangent(x, 16) 

154 

155 

156TESTS = [TestTools] 

157 

158 

159if __name__ == "__main__": # pragma: no cover 

160 # pylint: disable=wrong-import-position 

161 from gpkit.tests.helpers import run_tests 

162 run_tests(TESTS)