def test_no_name(self): ptree = PropertyTree() ptree.put_double('initial_voltage', 0) ptree.put_double('final_voltage', 0) ptree.put_double('scan_limit_1', 1) ptree.put_double('scan_limit_2', 0) ptree.put_double('step_size', 0.1) ptree.put_double('scan_rate', 1) ptree.put_int('cycles', 1) cv = CyclicVoltammetry(ptree) try: cv.run(device) except: self.fail('calling run without data should not raise') data = initialize_data() cv.run(device, data) voltage = array([0., 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1., 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.], dtype=float) time = linspace(0, 2, 21) try: testing.assert_array_almost_equal(data['voltage'], voltage) testing.assert_array_almost_equal(data['time'], time) except AssertionError as e: print(e) self.fail()
def test_get_with_default_value(self): ptree = PropertyTree() # double self.assertEqual( ptree.get_double_with_default_value('missing_double', 3.14), 3.14) ptree.put_double('present_double', 1.41) self.assertEqual( ptree.get_double_with_default_value('present_double', 3.14), 1.41) # string self.assertEqual( ptree.get_string_with_default_value('missing_string', 'missing'), 'missing') ptree.put_string('present_string', 'present') self.assertEqual( ptree.get_string_with_default_value('present_string', 'missing'), 'present') # int self.assertEqual(ptree.get_int_with_default_value('missing_int', 255), 255) ptree.put_int('present_int', 0) self.assertEqual(ptree.get_int_with_default_value('present_int', 255), 0) # bool self.assertEqual( ptree.get_bool_with_default_value('missing_bool', True), True) ptree.put_bool('present_bool', False) self.assertEqual( ptree.get_bool_with_default_value('present_bool', True), False)
def test_verification_with_equivalent_circuit(self): R = 50e-3 # ohm R_L = 500 # ohm C = 3 # farad U_i = 2.7 # volt U_f = 1.2 # volt # setup experiment ptree = PropertyTree() ptree.put_double('discharge_power_lower_limit', 1e-2) ptree.put_double('discharge_power_upper_limit', 1e+2) ptree.put_int('steps_per_decade', 5) ptree.put_double('initial_voltage', U_i) ptree.put_double('final_voltage', U_f) ptree.put_double('time_step', 15) ptree.put_int('min_steps_per_discharge', 2000) ptree.put_int('max_steps_per_discharge', 3000) ragone = RagoneAnalysis(ptree) # setup equivalent circuit database device_database = PropertyTree() device_database.put_double('series_resistance', R) device_database.put_double('parallel_resistance', R_L) device_database.put_double('capacitance', C) # analytical solutions E = {} def E_SeriesRC(P): U_0 = U_i / 2 + sqrt(U_i**2 / 4 - R * P) return C / 2 * (-R * P * log(U_0**2 / U_f**2) + U_0**2 - U_f**2) E['SeriesRC'] = E_SeriesRC def E_ParallelRC(P): U_0 = U_i / 2 + sqrt(U_i**2 / 4 - R * P) tmp = (U_f**2 / R_L + P * (1 + R / R_L)) / \ (U_0**2 / R_L + P * (1 + R / R_L)) return C / 2 * (-R_L * P * log(tmp) - R * R_L / (R + R_L) * P * log(tmp * U_0**2 / U_f**2)) E['ParallelRC'] = E_ParallelRC for device_type in ['SeriesRC', 'ParallelRC']: # create a device device_database.put_string('type', device_type) device = EnergyStorageDevice(device_database) # setup experiment and measure ragone.reset() ragone.run(device) P = ragone._data['power'] E_computed = ragone._data['energy'] # compute the exact solution E_exact = E[device_type](P) # ensure the error is small max_percent_error = 100 * linalg.norm( (E_computed - E_exact) / E_computed, inf) self.assertLess(max_percent_error, 0.1)
def test_property_tree(self): # ptree as container to store int, double, string, and bool ptree = PropertyTree() ptree.put_int('dim', 3) self.assertEqual(ptree.get_int('dim'), 3) ptree.put_double('path.to.pi', 3.14) self.assertEqual(ptree.get_double('path.to.pi'), 3.14) ptree.put_string('good.news', 'it works') self.assertEqual(ptree.get_string('good.news'), 'it works') ptree.put_bool('is.that.a.good.idea', False) self.assertEqual(ptree.get_bool('is.that.a.good.idea'), False)
def test_get_children(self): ptree = PropertyTree() # put child child = PropertyTree() child.put_double('prune', 6.10) ptree.put_child('a.g', child) self.assertEqual(ptree.get_double('a.g.prune'), 6.10) # get child ptree.put_string('child.name', 'clement') ptree.put_int('child.age', -2) child = ptree.get_child('child') self.assertEqual(child.get_string('name'), 'clement') self.assertEqual(child.get_int('age'), -2)
def test_retrieve_data(self): ptree = PropertyTree() ptree.put_string('type', 'SeriesRC') ptree.put_double('series_resistance', 50e-3) ptree.put_double('capacitance', 3) device = EnergyStorageDevice(ptree) ptree = PropertyTree() ptree.put_string('type', 'RagoneAnalysis') ptree.put_double('discharge_power_lower_limit', 1e-1) ptree.put_double('discharge_power_upper_limit', 1e+1) ptree.put_int('steps_per_decade', 1) ptree.put_double('initial_voltage', 2.1) ptree.put_double('final_voltage', 0.7) ptree.put_double('time_step', 1.5) ptree.put_int('min_steps_per_discharge', 20) ptree.put_int('max_steps_per_discharge', 30) ragone = Experiment(ptree) with File('trash.hdf5', 'w') as fout: ragone.run(device, fout) performance_data = ragone._data fin = File('trash.hdf5', 'r') retrieved_data = retrieve_performance_data(fin) fin.close() # a few digits are lost when power is converted to string self.assertLess( linalg.norm(performance_data['power'] - retrieved_data['power'], inf), 1e-12) self.assertEqual( linalg.norm(performance_data['energy'] - retrieved_data['energy'], inf), 0.0) # TODO: probably want to move this into its own test ragoneplot = RagonePlot("ragone.png") ragoneplot.update(ragone) # check reset reinitialize the time step and empty the data ragone.reset() self.assertEqual(ragone._ptree.get_double('time_step'), 1.5) self.assertFalse(ragone._data['power']) self.assertFalse(ragone._data['energy'])
def test_verification_with_equivalent_circuit(self): R = 50e-3 # ohm R_L = 500 # ohm C = 3 # farad # setup EIS experiment ptree = PropertyTree() ptree.put_string('type', 'ElectrochemicalImpedanceSpectroscopy') ptree.put_double('frequency_upper_limit', 1e+4) ptree.put_double('frequency_lower_limit', 1e-6) ptree.put_int('steps_per_decade', 3) ptree.put_int('steps_per_cycle', 1024) ptree.put_int('cycles', 2) ptree.put_int('ignore_cycles', 1) ptree.put_double('dc_voltage', 0) ptree.put_string('harmonics', '3') ptree.put_string('amplitudes', '5e-3') ptree.put_string('phases', '0') eis = Experiment(ptree) # setup equivalent circuit database device_database = PropertyTree() device_database.put_double('series_resistance', R) device_database.put_double('parallel_resistance', R_L) device_database.put_double('capacitance', C) # analytical solutions Z = {} Z['SeriesRC'] = lambda f: R + 1 / (1j * C * 2 * pi * f) Z['ParallelRC'] = lambda f: R + R_L / (1 + 1j * R_L * C * 2 * pi * f) for device_type in ['SeriesRC', 'ParallelRC']: # create a device device_database.put_string('type', device_type) device = EnergyStorageDevice(device_database) # setup experiment and measure eis.reset() eis.run(device) f = eis._data['frequency'] Z_computed = eis._data['impedance'] # compute the exact solution Z_exact = Z[device_type](f) # ensure the error is small max_phase_error_in_degree = linalg.norm( angle(Z_computed) * 180 / pi - angle(Z_exact) * 180 / pi, inf) max_magniture_error_in_decibel = linalg.norm( 20 * log10(absolute(Z_exact)) - 20 * log10(absolute(Z_computed)), inf) print(device_type) print( '-- max_phase_error_in_degree = {0}'.format(max_phase_error_in_degree)) print( '-- max_magniture_error_in_decibel = {0}'.format(max_magniture_error_in_decibel)) self.assertLessEqual(max_phase_error_in_degree, 1) self.assertLessEqual(max_magniture_error_in_decibel, 0.2)
def test_time_steps(self): ptree = PropertyTree() ptree.put_int('stages', 2) ptree.put_int('cycles', 1) ptree.put_double('time_step', 1.0) ptree.put_string('stage_0.mode', 'hold') ptree.put_string('stage_0.end_criterion', 'time') ptree.put_double('stage_0.duration', 2.0) ptree.put_string('stage_1.mode', 'rest') ptree.put_string('stage_1.end_criterion', 'time') ptree.put_double('stage_1.duration', 1.0) ptree.put_double('stage_1.time_step', 0.1) multi = MultiStage(ptree) data = initialize_data() steps = multi.run(device, data) self.assertEqual(steps, 12) self.assertEqual(steps, len(data['time'])) self.assertAlmostEqual(data['time'][5], 2.4) self.assertAlmostEqual(data['voltage'][0], data['voltage'][1]) self.assertAlmostEqual(data['current'][3], 0.0)
def test_setup_frequency_range(self): ptree = PropertyTree() ptree.put_string('type', 'ElectrochemicalImpedanceSpectroscopy') # specify the upper and lower bounds of the range # the number of points per decades controls the spacing on the log # scale ptree.put_double('frequency_upper_limit', 1e+2) ptree.put_double('frequency_lower_limit', 1e-1) ptree.put_int('steps_per_decade', 3) eis = Experiment(ptree) print(eis._frequencies) f = eis._frequencies self.assertEqual(len(f), 10) self.assertAlmostEqual(f[0], 1e+2) self.assertAlmostEqual(f[3], 1e+1) self.assertAlmostEqual(f[9], 1e-1) # or directly specify the frequencies frequencies = [3, 2e3, 0.1] eis = Experiment(ptree, frequencies) self.assertTrue(all(equal(frequencies, eis._frequencies)))
def test_retrieve_data(self): ptree = PropertyTree() ptree.put_string('type', 'SeriesRC') ptree.put_double('series_resistance', 50e-3) ptree.put_double('capacitance', 3) device = EnergyStorageDevice(ptree) ptree = PropertyTree() ptree.put_string('type', 'RagoneAnalysis') ptree.put_double('discharge_power_lower_limit', 1e-1) ptree.put_double('discharge_power_upper_limit', 1e+1) ptree.put_int('steps_per_decade', 1) ptree.put_double('initial_voltage', 2.1) ptree.put_double('final_voltage', 0.7) ptree.put_double('time_step', 1.5) ptree.put_int('min_steps_per_discharge', 20) ptree.put_int('max_steps_per_discharge', 30) ragone = Experiment(ptree) with File('trash.hdf5', 'w') as fout: ragone.run(device, fout) performance_data = ragone._data fin = File('trash.hdf5', 'r') retrieved_data = retrieve_performance_data(fin) fin.close() # a few digits are lost when power is converted to string self.assertLess(linalg.norm(performance_data['power'] - retrieved_data['power'], inf), 1e-12) self.assertEqual(linalg.norm(performance_data['energy'] - retrieved_data['energy'], inf), 0.0) # TODO: probably want to move this into its own test ragoneplot = RagonePlot("ragone.png") ragoneplot.update(ragone) # check reset reinitialize the time step and empty the data ragone.reset() self.assertEqual(ragone._ptree.get_double('time_step'), 1.5) self.assertFalse(ragone._data['power']) self.assertFalse(ragone._data['energy'])
def test_retrieve_data(self): ptree = PropertyTree() ptree.put_string('type', 'SeriesRC') ptree.put_double('series_resistance', 100e-3) ptree.put_double('capacitance', 2.5) device = EnergyStorageDevice(ptree) ptree = PropertyTree() ptree.put_string('type', 'ElectrochemicalImpedanceSpectroscopy') ptree.put_double('frequency_upper_limit', 1e+2) ptree.put_double('frequency_lower_limit', 1e-1) ptree.put_int('steps_per_decade', 1) ptree.put_int('steps_per_cycle', 64) ptree.put_int('cycles', 2) ptree.put_int('ignore_cycles', 1) ptree.put_double('dc_voltage', 0) ptree.put_string('harmonics', '3') ptree.put_string('amplitudes', '5e-3') ptree.put_string('phases', '0') eis = Experiment(ptree) with File('trash.hdf5', 'w') as fout: eis.run(device, fout) spectrum_data = eis._data with File('trash.hdf5', 'r') as fin: retrieved_data = retrieve_impedance_spectrum(fin) print(spectrum_data['impedance'] - retrieved_data['impedance']) print(retrieved_data) self.assertEqual(linalg.norm(spectrum_data['frequency'] - retrieved_data['frequency'], inf), 0.0) # not sure why we don't get equality for the impedance self.assertLess(linalg.norm(spectrum_data['impedance'] - retrieved_data['impedance'], inf), 1e-10)
def setup_expertiment(): eis_database = PropertyTree() eis_database.put_double('frequency_upper_limit', 1e+4) eis_database.put_double('frequency_lower_limit', 1e-6) eis_database.put_int('steps_per_decade', 3) eis_database.put_int('steps_per_cycle', 1024) eis_database.put_int('cycles', 2) eis_database.put_int('ignore_cycles', 1) eis_database.put_double('dc_voltage', 0) eis_database.put_string('harmonics', ' 3') eis_database.put_string('amplitudes', ' 5e-3') eis_database.put_string('phases', '0') return eis_database
def test_fourier_analysis(self): ptree = PropertyTree() ptree.put_int('steps_per_cycle', 3) ptree.put_int('cycles', 1) ptree.put_int('ignore_cycles', 0) ptree.put_string('harmonics', '1') # uninitialized data data = {} self.assertRaises(KeyError, fourier_analysis, data, ptree) # empty data data = initialize_data() self.assertRaises(IndexError, fourier_analysis, data, ptree) # bad data data['time'] = array([1, 2, 3], dtype=float) data['current'] = array([4, 5, 6], dtype=float) data['voltage'] = array([7, 8], dtype=float) self.assertRaises(AssertionError, fourier_analysis, data, ptree) # poor data (size not a power of 2) data['voltage'] = array([7, 8, 9], dtype=float) with catch_warnings(): simplefilter("error") self.assertRaises(RuntimeWarning, fourier_analysis, data, ptree) # data unchanged after analyze dummy = array([1, 2, 3, 4, 5, 6, 7, 8], dtype=float) data['time'] = dummy data['current'] = dummy data['voltage'] = dummy # ptree needs to be updated self.assertRaises(AssertionError, fourier_analysis, data, ptree) ptree.put_int('steps_per_cycle', 4) ptree.put_int('cycles', 2) ptree.put_int('ignore_cycles', 0) fourier_analysis(data, ptree) self.assertTrue(all(equal(data['time'], dummy))) self.assertTrue(all(equal(data['current'], dummy))) self.assertTrue(all(equal(data['voltage'], dummy)))