Coverage for gpkit/tests/t_tools.py: 100%
83 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-07 22:15 -0500
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-07 22:15 -0500
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
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)
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
29class Fuselage(Model):
30 """The thing that carries the fuel, engine, and payload
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
47 Upper Unbounded
48 ---------------
49 k, W
51 """
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 ]
66class TestTools(unittest.TestCase):
67 """TestCase for math models"""
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)
76 def test_parse_variables(self):
77 Fuselage(Variable("Wfueltot", 5, "lbf"))
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)
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())
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'))
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
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)
156TESTS = [TestTools]
159if __name__ == "__main__": # pragma: no cover
160 # pylint: disable=wrong-import-position
161 from gpkit.tests.helpers import run_tests
162 run_tests(TESTS)