def test_consistency_pycap_simulation(self): # # weak run test; simply ensures that Dualfoil object # can be run with pycap.Charge # df1 = Dualfoil(path=path) # will use pycap df2 = Dualfoil(path=path) # manual runs im = df_manip.InputManager(path=path) # testing a charge-to-hold-const-voltage # manual # use InputManager to set the input file c = -12.0 # constant current im.add_new_leg(c, 5.0, 1) df1.run() df1.outbot.update_output() v = 4.54 # constant voltage im.add_new_leg(v, 5.0, 0) df1.run() df1.outbot.update_output() # pycap simulation # build a ptree input ptree = PropertyTree() ptree.put_double('time_step', 300.0) # 5 minutes ptree.put_string('charge_mode', 'constant_current') ptree.put_double('charge_current', 12.0) ptree.put_string('charge_stop_at_1', 'voltage_greater_than') ptree.put_double('charge_voltage_limit', 4.54) ptree.put_bool('charge_voltage_finish', True) # hold end voltage after either 5 minutes have passed # OR current falls under 1 ampere ptree.put_double('charge_voltage_finish_max_time', 300.0) ptree.put_double('charge_voltage_finish_current_limit', 1.0) const_current_const_voltage = Charge(ptree) const_current_const_voltage.run(df2) # check the output lists of both devices o1 = df1.outbot.output o2 = df2.outbot.output self.assertEqual(len(o1['time']), len(o2['time'])) for i in range(len(o1['time'])): self.assertAlmostEqual(o1['time'][i], o2['time'][i]) # BELOW: relaxed delta for voltage # REASON: dualfoil cuts off its voltages at 5 # decimal places, meaning that this end-digit # is subject to roundoff errors error = 1e-5 self.assertAlmostEqual(o1['voltage'][i], o2['voltage'][i], delta=error) self.assertAlmostEqual(o1['current'][i], o2['current'][i])
def test_charge_constant_current(self): ptree = PropertyTree() ptree.put_string('charge_mode', 'constant_current') ptree.put_double('charge_current', 10e-3) ptree.put_string('charge_stop_at_1', 'voltage_greater_than') ptree.put_double('charge_voltage_limit', 1.4) ptree.put_double('time_step', 0.2) charge = Charge(ptree) data = initialize_data() charge.run(device, data) self.assertAlmostEqual(data['current'][0], 10e-3) self.assertAlmostEqual(data['current'][-1], 10e-3) self.assertGreaterEqual(data['voltage'][-1], 1.4) self.assertAlmostEqual(data['time'][1] - data['time'][0], 0.2)
def test_charge_constant_voltage(self): ptree = PropertyTree() ptree.put_string('charge_mode', 'constant_voltage') ptree.put_double('charge_voltage', 1.4) ptree.put_string('charge_stop_at_1', 'current_less_than') ptree.put_double('charge_current_limit', 1e-6) ptree.put_string('charge_stop_at_2', 'time') ptree.put_double('charge_max_duration', 60) ptree.put_double('time_step', 0.2) charge = Charge(ptree) data = initialize_data() charge.run(device, data) self.assertTrue(data['time'][-1] >= 60 or abs(data['current'][-1]) <= 1e-6) self.assertAlmostEqual(data['voltage'][-1], 1.4)
def run_discharge(device, ptree): data = initialize_data() # (re)charge the device initial_voltage = ptree.get_double('initial_voltage') charge_database = PropertyTree() charge_database.put_string('charge_mode', 'constant_current') charge_database.put_double('charge_current', 10.0) charge_database.put_string('charge_stop_at_1', 'voltage_greater_than') charge_database.put_double('charge_voltage_limit', initial_voltage) charge_database.put_bool('charge_voltage_finish', True) charge_database.put_double('charge_voltage_finish_current_limit', 1e-2) charge_database.put_double('charge_voltage_finish_max_time', 600) charge_database.put_double('charge_rest_time', 0) charge_database.put_double('time_step', 10.0) charge = Charge(charge_database) start = time() charge.run(device, data) end = time() # used for tracking time of this substep print('Charge: %s min' % ((end-start) / 60)) data['time'] -= data['time'][-1] # discharge at constant power discharge_power = ptree.get_double('discharge_power') final_voltage = ptree.get_double('final_voltage') time_step = ptree.get_double('time_step') discharge_database = PropertyTree() discharge_database.put_string('discharge_mode', 'constant_power') discharge_database.put_double('discharge_power', discharge_power) discharge_database.put_string('discharge_stop_at_1', 'voltage_less_than') discharge_database.put_double('discharge_voltage_limit', final_voltage) discharge_database.put_double('discharge_rest_time', 10 * time_step) discharge_database.put_double('time_step', time_step) discharge = Discharge(discharge_database) start = time() discharge.run(device, data) end = time() # used for tracking time of this substep print('Discharge: %s min' % ((end-start) / 60)) return data
def run_discharge(device, ptree): data = initialize_data() # (re)charge the device initial_voltage = ptree.get_double('initial_voltage') charge_database = PropertyTree() charge_database.put_string('charge_mode', 'constant_current') charge_database.put_double('charge_current', 10.0) charge_database.put_string('charge_stop_at_1', 'voltage_greater_than') charge_database.put_double('charge_voltage_limit', initial_voltage) charge_database.put_bool('charge_voltage_finish', True) charge_database.put_double('charge_voltage_finish_current_limit', 1e-2) charge_database.put_double('charge_voltage_finish_max_time', 600) charge_database.put_double('charge_rest_time', 0) charge_database.put_double('time_step', 10.0) charge = Charge(charge_database) start = time() charge.run(device, data) end = time() # used for tracking time of this substep print('Charge: %s min' % ((end - start) / 60)) data['time'] -= data['time'][-1] # discharge at constant power discharge_power = ptree.get_double('discharge_power') final_voltage = ptree.get_double('final_voltage') time_step = ptree.get_double('time_step') discharge_database = PropertyTree() discharge_database.put_string('discharge_mode', 'constant_power') discharge_database.put_double('discharge_power', discharge_power) discharge_database.put_string('discharge_stop_at_1', 'voltage_less_than') discharge_database.put_double('discharge_voltage_limit', final_voltage) discharge_database.put_double('discharge_rest_time', 10 * time_step) discharge_database.put_double('time_step', time_step) discharge = Discharge(discharge_database) start = time() discharge.run(device, data) end = time() # used for tracking time of this substep print('Discharge: %s min' % ((end - start) / 60)) return data
def test_accuracy_pycap_simulation(self): # # tests the accuracy of a pycap simulation against a # straight run dualfoil sim with different timesteps # df1 = Dualfoil(path=path) # manual runs df2 = Dualfoil(path=path) # pycap simulation im = df_manip.InputManager(path=path) # testing a charge-to-hold-const-voltage # manual # use InputManager to set the input file c = -10.0 # constant charge current # charge for 5 minutes straight im.add_new_leg(c, 5, 1) df1.run() df1.outbot.update_output() v = 4.539 # expected voltage after 5 minutes # hold constant voltage for 3 minutes straight im.add_new_leg(v, 3.0, 0) df1.run() df1.outbot.update_output() # pycap simulation # build a ptree input ptree = PropertyTree() ptree.put_double('time_step', 30.0) # 30 second time step ptree.put_string('charge_mode', 'constant_current') ptree.put_double('charge_current', 10.0) ptree.put_string('charge_stop_at_1', 'voltage_greater_than') ptree.put_double('charge_voltage_limit', v) ptree.put_bool('charge_voltage_finish', True) # hold end voltage after either 3 minutes have passed # OR current falls under 1 ampere ptree.put_double('charge_voltage_finish_max_time', 180.0) ptree.put_double('charge_voltage_finish_current_limit', 1.0) const_current_const_voltage = Charge(ptree) const_current_const_voltage.run(df2) o1 = df1.outbot.output # contains sim1 output o2 = df2.outbot.output # contains sim2 output # affirm we make it this far and have usable data self.assertTrue(len(o1['time']) > 0) self.assertTrue(len(o2['time']) > 0) # lengths of data should be different self.assertFalse(len(o1['time']) == len(o2['time'])) # TEST LOGIC: # -Merge the two outputs into one, sorted by # increasing time stamps. # -Compare the consistency of the two simulations # by checking for smooth changes within the curves # of the combined output lists o1['time'].extend(o2['time']) time = ar(o1['time']) # nparray o1['voltage'].extend(o2['voltage']) voltage = ar(o1['voltage']) # nparray o1['current'].extend(o2['current']) current = ar(o1['current']) # np array # create a dictionary with the combined output lists output = {'time': time, 'voltage': voltage, 'current': current} # sort based on time, keeping the three types aligned key = argsort(output['time']) # for using the key to sort the list tmp = {'time': [], 'voltage': [], 'current': []} for i in key: tmp['time'].append(output['time'][i]) tmp['voltage'].append(output['voltage'][i]) tmp['current'].append(output['current'][i]) # reassign ordered set to `output` as nparrays output['time'] = ar(tmp['time']) output['voltage'] = ar(tmp['voltage']) output['current'] = ar(tmp['current']) # BELOW: first 20 seconds are identical time stamps; # skip these to avoid errors from incorrect sorting # REASON FOR ERROR: Dualfoil only prints time data as # precice as minutes to three decimal places. So when # the following is generated.... # Manual Run | Pycap Simulation # (min) (V) (amp) | (min) (V) (amp) # .001 4.52345 10.0 | .001 4.52345 10.0 # .001 4.52349 10.0 | .001 4.52349 10.0 # ... ... # ...python's `sorted()` function has no way of # distinguishing entries; it instead returns this: # [ # (.001, 4.52345, 10.0), # (.001, 4.52349, 10.0), <- these two should # (.001, 4.52345, 10.0), <- be switched # (.001, 4.52349, 10.0) # ] # SOLUTION: consistency test affirms that the exact same # time step will produce same current and voltage, so # skip ahead to first instance where time stamps will # be out of alignment i = 0 while output['time'][i] <= 0.4: # 24 seconds i = i + 1 index_limit = len(output['time']) - 1 # go through and affirm smoothness of curve while i < index_limit: # Check if time values are the same to 3 decimal places. # If so, current and voltage are not guarunteed # to also be exactly the same, but should be close if output['time'][i] == output['time'][i - 1]: # affirm that current is virtually the same self.assertAlmostEqual(output['current'][i], output['current'][i - 1]) # BELOW: delta is eased slightly # REASON: `sorted()` can't tell which entry came # first from same time-stamp if from different # simulations; allow for this with error error = 3e-5 self.assertAlmostEqual(output['voltage'][i], output['voltage'][i - 1], delta=error) else: # Time values are different # Check to affirm that the variable NOT being held # constant is steadily increasing / decreasing # First part happens in first 4 minutes if output['time'][i] <= 5.0: # part 1, const currrent # current should be equal self.assertEqual(output['current'][i], output['current'][i - 1]) # voltage should not have decreased self.assertTrue(output['voltage'][i], output['voltage'][i - 1]) else: # part 2, const voltage # current should be getting less positive self.assertTrue( output['current'][i] <= output['current'][i - 1], msg=(output['current'][i - 2:i + 10], output['time'][i - 2:i + 10])) # voltage should decrease, then stay at 4.54 if output['voltage'][i - 1] == 4.54: self.assertEqual(output['voltage'][i], output['voltage'][i - 1]) else: self.assertTrue( output['voltage'][i] <= output['voltage'][i - 1]) # update index i = i + 1
def test_accuracy_pycap_simulation(self): # # tests the accuracy of a pycap simulation against a # straight run dualfoil sim with different timesteps # df1 = Dualfoil(path=path) # manual runs df2 = Dualfoil(path=path) # pycap simulation im = df_manip.InputManager(path=path) # testing a charge-to-hold-const-voltage # manual # use InputManager to set the input file c = -10.0 # constant charge current # charge for 5 minutes straight im.add_new_leg(c, 5, 1) df1.run() df1.outbot.update_output() v = 4.539 # expected voltage after 5 minutes # hold constant voltage for 3 minutes straight im.add_new_leg(v, 3.0, 0) df1.run() df1.outbot.update_output() # pycap simulation # build a ptree input ptree = PropertyTree() ptree.put_double('time_step', 30.0) # 30 second time step ptree.put_string('charge_mode', 'constant_current') ptree.put_double('charge_current', 10.0) ptree.put_string('charge_stop_at_1', 'voltage_greater_than') ptree.put_double('charge_voltage_limit', v) ptree.put_bool('charge_voltage_finish', True) # hold end voltage after either 3 minutes have passed # OR current falls under 1 ampere ptree.put_double('charge_voltage_finish_max_time', 180.0) ptree.put_double('charge_voltage_finish_current_limit', 1.0) const_current_const_voltage = Charge(ptree) const_current_const_voltage.run(df2) o1 = df1.outbot.output # contains sim1 output o2 = df2.outbot.output # contains sim2 output # affirm we make it this far and have usable data self.assertTrue(len(o1['time']) > 0) self.assertTrue(len(o2['time']) > 0) # lengths of data should be different self.assertFalse(len(o1['time']) == len(o2['time'])) # TEST LOGIC: # -Merge the two outputs into one, sorted by # increasing time stamps. # -Compare the consistency of the two simulations # by checking for smooth changes within the curves # of the combined output lists o1['time'].extend(o2['time']) time = ar(o1['time']) # nparray o1['voltage'].extend(o2['voltage']) voltage = ar(o1['voltage']) # nparray o1['current'].extend(o2['current']) current = ar(o1['current']) # np array # create a dictionary with the combined output lists output = {'time': time, 'voltage': voltage, 'current': current } # sort based on time, keeping the three types aligned key = argsort(output['time']) # for using the key to sort the list tmp = {'time': [], 'voltage': [], 'current': []} for i in key: tmp['time'].append(output['time'][i]) tmp['voltage'].append(output['voltage'][i]) tmp['current'].append(output['current'][i]) # reassign ordered set to `output` as nparrays output['time'] = ar(tmp['time']) output['voltage'] = ar(tmp['voltage']) output['current'] = ar(tmp['current']) # BELOW: first 20 seconds are identical time stamps; # skip these to avoid errors from incorrect sorting # REASON FOR ERROR: Dualfoil only prints time data as # precice as minutes to three decimal places. So when # the following is generated.... # Manual Run | Pycap Simulation # (min) (V) (amp) | (min) (V) (amp) # .001 4.52345 10.0 | .001 4.52345 10.0 # .001 4.52349 10.0 | .001 4.52349 10.0 # ... ... # ...python's `sorted()` function has no way of # distinguishing entries; it instead returns this: # [ # (.001, 4.52345, 10.0), # (.001, 4.52349, 10.0), <- these two should # (.001, 4.52345, 10.0), <- be switched # (.001, 4.52349, 10.0) # ] # SOLUTION: consistency test affirms that the exact same # time step will produce same current and voltage, so # skip ahead to first instance where time stamps will # be out of alignment i = 0 while output['time'][i] <= 0.4: # 24 seconds i = i + 1 index_limit = len(output['time'])-1 # go through and affirm smoothness of curve while i < index_limit: # Check if time values are the same to 3 decimal places. # If so, current and voltage are not guarunteed # to also be exactly the same, but should be close if output['time'][i] == output['time'][i-1]: # affirm that current is virtually the same self.assertAlmostEqual(output['current'][i], output['current'][i-1]) # BELOW: delta is eased slightly # REASON: `sorted()` can't tell which entry came # first from same time-stamp if from different # simulations; allow for this with error error = 3e-5 self.assertAlmostEqual(output['voltage'][i], output['voltage'][i-1], delta=error) else: # Time values are different # Check to affirm that the variable NOT being held # constant is steadily increasing / decreasing # First part happens in first 4 minutes if output['time'][i] <= 5.0: # part 1, const currrent # current should be equal self.assertEqual(output['current'][i], output['current'][i-1]) # voltage should not have decreased self.assertTrue(output['voltage'][i], output['voltage'][i-1]) else: # part 2, const voltage # current should be getting less positive self.assertTrue(output['current'][i] <= output['current'][i-1], msg=(output['current'][i-2:i+10], output['time'][i-2:i+10])) # voltage should decrease, then stay at 4.54 if output['voltage'][i-1] == 4.54: self.assertEqual(output['voltage'][i], output['voltage'][i-1]) else: self.assertTrue(output['voltage'][i] <= output['voltage'][i-1]) # update index i = i + 1