def setUpClass(cls): """Initialize some ZIP outputs with different base powers.""" # Use 120V nominal. cls.v_n = V_N # Use logarithmically varying S_n cls.s_n = [1, 10, 100, 1000] # Use four sets of ZIP coefficients that can use the default # initial parameters. cls.zip = [ZIP_CFL_42W, ZIP_LCD, ZIP_CFL_13W, ZIP_FAN] # Initialize results for the 4 coefficients. cls.results = [pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame] # Sweep voltage from 90% to 110% of nominal. cls.v = V_SWEEP # Loop and create output for i in range(len(cls.zip)): p, q = zip._zip_model(v=cls.v, v_n=cls.v_n, s_n=cls.s_n[i], zip_terms=cls.zip[i]) # Get into format for calling zip_fit. vpq = pd.DataFrame({'v': cls.v, 'p': p, 'q': q}) cls.results[i] = vpq
def setUpClass(cls): cls.v_n = V_N cls.s_n = S_N cls.v = V_SWEEP p, q = zip._zip_model(v=cls.v, v_n=cls.v_n, s_n=cls.s_n, zip_terms=zip.PAR_0) cls.vpq = pd.DataFrame({'v': cls.v, 'p': p, 'q': q})
def run_fit(self, key): """Helper to perform the fit and tests.""" # Grab attributes. vpq_bar = getattr(self, key)['vpq_bar'] p_expected = getattr(self, key)['p'] q_expected = getattr(self, key)['q'] # zip_terms = getattr(self, key)['zip_terms'] # result = zip._zip_fit_slsqp(vpq_bar=vpq_bar) with self.subTest('{}, success'.format(key)): self.assertTrue(result.success) p_actual, q_actual = zip._zip_model(v=self.v, v_n=self.v_n, s_n=self.s_n, zip_terms=result.x) with self.subTest('{}, p'.format(key)): np.testing.assert_allclose(p_actual, p_expected, rtol=R_TOL_P, atol=A_TOL) # If all the Q values are essentially 0 (like for the # incandescent bulb), we need to take a different approach. if not np.allclose(q_expected, np.zeros_like(q_expected), atol=A_TOL_0, rtol=0): rtol = R_TOL_Q atol = A_TOL else: rtol = 0 atol = 0.05 with self.subTest('{}, q'.format(key)): np.testing.assert_allclose(q_actual, q_expected, rtol=rtol, atol=atol)
def setUpClass(cls): """Initialize all our expected results.""" # Use 120V nominal. cls.v_n = V_N # We'll use a 1000VA base. cls.s_n = S_N # Sweep voltage from 90% to 110% of nominal. cls.v = V_SWEEP # Loop and assign. for key, value in ZIP_DICT.items(): # Compute P and Q for the given model. p, q = zip._zip_model(v=cls.v, v_n=cls.v_n, s_n=cls.s_n, zip_terms=value) # Normalize. vpq_bar = zip._get_vpq_bar( vpq=pd.DataFrame({'v': cls.v, 'p': p, 'q': q}), v_n=cls.v_n, s_n=cls.s_n) setattr(cls, key, {'vpq_bar': vpq_bar, 'p': p, 'q': q})
def test_zip_model(self): """Simple test of _zip_model to ensure accuracy. """ v = np.array([10]) v_n = np.array([11]) s_n = 100 zip_terms = np.array([1/3, np.pi/4, 1/3, np.pi/4, 1/3, np.pi/4]) # Get our p and q p_actual, q_actual = zip._zip_model(v=v, v_n=v_n, s_n=s_n, zip_terms=zip_terms) # sin and cos of pi/4 evaluate to sqrt(2)/2 r22 = 2 ** 0.5 / 2 # Hard-coding to make the test less maintainable but prevent # myself from copying from the function itself. p_expected = 100 * np.array([ (10 ** 2) / (11 ** 2) * (1/3) * r22 + 10/11 * (1/3) * r22 + (1/3) * r22 ]) # Since we used an angle of pi/4, p and q should be equal. q_expected = p_expected # Test. np.testing.assert_array_almost_equal(p_expected, p_actual) np.testing.assert_array_almost_equal(q_expected, q_actual)
def test_zip_obj_and_jac_zero_error(self): """Given the correct zip_terms, our objective and Jacobian should be zero (to within reasonable rounding error).""" p, q = zip._zip_model(v=V_SWEEP, v_n=V_N, s_n=S_N, zip_terms=zip.PAR_0) vpq = pd.DataFrame({'v': V_SWEEP, 'p': p, 'q': q}) vpq_bar = zip._get_vpq_bar(vpq=vpq, v_n=V_N, s_n=S_N) obj, jac = zip._zip_obj_and_jac(zip_terms=zip.PAR_0, v_s=vpq_bar['v_bar'].values**2, v_bar=vpq_bar['v_bar'].values, p_bar=vpq_bar['p_bar'].values, q_bar=vpq_bar['q_bar'].values) # Our p and q are on the order of several hundred, so matching # to within 1 decimal place is acceptable. self.assertAlmostEqual(0, obj) np.testing.assert_allclose(jac, 0, rtol=0, atol=1e-10)