def test_from_maccor_insufficient_interpolation_length(self): md = MaccorDatapath.from_file(self.broken_file) vrange, num_points, nominal_capacity, fast_charge, diag = md.determine_structuring_parameters() self.assertEqual(diag['parameter_set'], 'Tesla21700') diag_interp = md.interpolate_diagnostic_cycles(diag, resolution=1000, v_resolution=0.0005) self.assertEqual(np.around(diag_interp[diag_interp.cycle_index == 1].charge_capacity.median(), 3), np.around(0.6371558214610992, 3))
def test_step_is_waveform(self): md = MaccorDatapath.from_file(self.waveform_file) df = md.raw_data self.assertTrue(df.loc[df.cycle_index == 6]. groupby("step_index").apply(step_is_waveform_dchg).any()) self.assertFalse(df.loc[df.cycle_index == 6]. groupby("step_index").apply(step_is_waveform_chg).any()) self.assertFalse(df.loc[df.cycle_index == 3]. groupby("step_index").apply(step_is_waveform_dchg).any())
def test_waveform_charge_discharge_capacity(self): md = MaccorDatapath.from_file(self.waveform_file) df = md.raw_data cycle_sign = np.sign(np.diff(df["cycle_index"])) capacity_sign = np.sign(np.diff(df["charge_capacity"])) self.assertTrue( np.all(capacity_sign >= -cycle_sign) ) # Capacity increases throughout cycle capacity_sign = np.sign(np.diff(df["discharge_capacity"])) self.assertTrue( np.all(capacity_sign >= -cycle_sign) )
def test_interpolate_waveform_discharge_cycles(self): md = MaccorDatapath.from_file(self.waveform_file) all_interpolated = md.interpolate_cycles() all_interpolated = all_interpolated[(all_interpolated.step_type == "discharge")] self.assertTrue(all_interpolated.columns[0] == 'test_time') subset_interpolated = all_interpolated[all_interpolated.cycle_index==6] df = md.raw_data self.assertEqual(subset_interpolated.test_time.min(), df.loc[(df.cycle_index == 6) & (df.step_index == 33)].test_time.min()) self.assertEqual(subset_interpolated[subset_interpolated.cycle_index == 6].shape[0], 1000)
def test_get_quantity_sum(self): md = MaccorDatapath.from_file(self.diagnostics_file) cycle_sign = np.sign(np.diff(md.raw_data["cycle_index"])) capacity_sign = np.sign(np.diff(md.raw_data["charge_capacity"])) self.assertTrue( np.all(capacity_sign >= -cycle_sign) ) # Capacity increases throughout cycle capacity_sign = np.sign(np.diff(md.raw_data["discharge_capacity"])) self.assertTrue( np.all(capacity_sign >= -cycle_sign) ) # Capacity increases throughout cycle
def test_from_file(self): for file in (self.good_file, self.timestamp_file, self.timezone_file): md = MaccorDatapath.from_file(file) self.assertEqual(md.paths.get("raw"), file) self.assertEqual(md.paths.get("metadata"), file) if file == self.good_file: self.assertTupleEqual(md.raw_data.shape, (11669, 40)) self.assertEqual(70, md.metadata.channel_id) elif file == self.timestamp_file: self.assertTupleEqual(md.raw_data.shape, (333, 44)) self.assertEqual(52, md.metadata.channel_id) else: self.assertTupleEqual(md.raw_data.shape, (2165, 44)) self.assertEqual(10, md.metadata.channel_id) self.assertEqual( set(md.metadata.raw.keys()), { "barcode", "_today_datetime", "start_datetime", "filename", "protocol", "channel_id", }, ) # Quick test to see whether columns get recasted self.assertTrue( { "data_point", "cycle_index", "step_index", "voltage", "current", "charge_capacity", "discharge_capacity", } < set(md.raw_data.columns) )
def auto_load(filename): """Load any supported raw battery cycler file to the correct Datapath automatically. Matches raw file patterns to the correct datapath and returns the datapath object. Example: auto_load("2017-05-09_test-TC-contact_CH33.csv") >>> <ArbinDatapath object> auto_load("PreDiag_000287_000128short.092") >>> <MaccorDatapath object> Args: filename (str, Pathlike): string corresponding to battery cycler file filename. Returns: (beep.structure.base.BEEPDatapath): The datapath child class corresponding to this file. """ if re.match(ARBIN_CONFIG["file_pattern"], filename) or re.match( FastCharge_CONFIG["file_pattern"], filename): return ArbinDatapath.from_file(filename) elif re.match(MACCOR_CONFIG["file_pattern"], filename) or re.match( xTesladiag_CONFIG["file_pattern"], filename): return MaccorDatapath.from_file(filename) elif re.match(INDIGO_CONFIG["file_pattern"], filename): return IndigoDatapath.from_file(filename) elif re.match(BIOLOGIC_CONFIG["file_pattern"], filename): return BiologicDatapath.from_file(filename) elif re.match(NEWARE_CONFIG["file_pattern"], filename): return NewareDatapath.from_file(filename) elif re.match(BatteryArchiveDatapath.FILE_PATTERN, filename): return BatteryArchiveDatapath.from_file(filename) else: raise ValueError( "{} does not match any known file pattern".format(filename))
def test_raw_to_features(self): download_s3_object(bucket=self.maccor_file_w_parameters_s3["bucket"], key=self.maccor_file_w_parameters_s3["key"], destination_path=self.maccor_file_w_parameters) dp = MaccorDatapath.from_file(self.maccor_file_w_parameters) dp.autostructure() processed_run_path = os.path.join(TEST_FILE_DIR, "processed_diagnostic.json") # Dump to the structured file and check the file size dumpfn(dp, processed_run_path) dp = loadfn(processed_run_path) for fclass in ( DeltaQFastCharge, CycleSummaryStats, TrajectoryFastCharge, DiagnosticSummaryStats, HPPCResistanceVoltageFeatures, ): f = fclass(dp) self.assertTrue(f.validate()[0]) f.create_features() if fclass.__class__.__name__ == "HPPCResistanceVoltageFeatures": self.assertAlmostEqual(f.features['r_c_0s_00'].iloc[0], -0.159771397, 5) self.assertAlmostEqual(f.features['r_c_0s_10'].iloc[0], -0.143679, 5) self.assertAlmostEqual(f.features['r_c_0s_20'].iloc[0], -0.146345, 5) self.assertAlmostEqual(f.features['D_6'].iloc[0], -0.167919, 5) self.assertAlmostEqual(f.features['D_7'].iloc[0], 0.094136, 5) self.assertAlmostEqual(f.features['D_8'].iloc[0], 0.172496, 5)
def test_raw_to_features(self): os.environ["BEEP_PROCESSING_DIR"] = TEST_FILE_DIR download_s3_object(bucket=self.maccor_file_w_parameters_s3["bucket"], key=self.maccor_file_w_parameters_s3["key"], destination_path=self.maccor_file_w_parameters) with ScratchDir("."): os.environ["BEEP_PROCESSING_DIR"] = TEST_FILE_DIR # os.environ['BEEP_PROCESSING_DIR'] = os.getcwd() dp = MaccorDatapath.from_file(self.maccor_file_w_parameters) dp.autostructure() processed_run_path = os.path.join(TEST_FILE_DIR, "processed_diagnostic.json") # Dump to the structured file and check the file size dumpfn(dp, processed_run_path) # Create dummy json obj json_obj = { "file_list": [processed_run_path], "run_list": [0], } json_string = json.dumps(json_obj) newjsonpaths = process_file_list_from_json( json_string, processed_dir=os.getcwd()) reloaded = json.loads(newjsonpaths) import pprint pprint.pprint(reloaded) result_list = ['success'] * 7 self.assertEqual(reloaded['result_list'], result_list) rpt_df = loadfn(reloaded['file_list'][0]) self.assertEqual( np.round(rpt_df.X['m0_Amp_rpt_0.2C_1'].iloc[0], 6), 0.867371)
def test_interpolate_cycles(self): md = MaccorDatapath.from_file(self.good_file) all_interpolated = md.interpolate_cycles( v_range=[3.0, 4.2], resolution=10000 ) self.assertSetEqual(set(all_interpolated.columns.tolist()), {'voltage', 'test_time', 'discharge_capacity', 'discharge_energy', 'current', 'charge_capacity', 'charge_energy', 'internal_resistance', 'temperature', 'cycle_index', 'step_type'} ) interp2 = all_interpolated[ (all_interpolated.cycle_index == 2) & (all_interpolated.step_type == "discharge") ].sort_values("discharge_capacity") interp3 = all_interpolated[ (all_interpolated.cycle_index == 1) & (all_interpolated.step_type == "charge") ].sort_values("charge_capacity") self.assertTrue(interp3.current.mean() > 0) self.assertEqual(len(interp3.voltage), 10000) self.assertEqual(interp3.voltage.max(), np.float32(4.100838)) # self.assertEqual(interp3.voltage.min(), np.float32(3.3334765)) # 3.437705 in python3.9 np.testing.assert_almost_equal( interp3[ interp3.charge_capacity <= interp3.charge_capacity.median() ].current.iloc[0], 2.423209, decimal=6, ) df = md.raw_data cycle_2 = df[df["cycle_index"] == 2] discharge = cycle_2[cycle_2.step_index == 12] discharge = discharge.sort_values("discharge_capacity") acceptable_error = 0.01 acceptable_error_offest = 0.001 voltages_to_check = [3.3, 3.2, 3.1] columns_to_check = [ "voltage", "current", "discharge_capacity", "charge_capacity", ] for voltage_check in voltages_to_check: closest_interp2_index = interp2.index[ (interp2["voltage"] - voltage_check).abs().min() == (interp2["voltage"] - voltage_check).abs() ] closest_interp2_match = interp2.loc[closest_interp2_index] closest_discharge_index = discharge.index[ (discharge["voltage"] - voltage_check).abs().min() == (discharge["voltage"] - voltage_check).abs() ] closest_discharge_match = discharge.loc[closest_discharge_index] for column_check in columns_to_check: off_by = ( closest_interp2_match.iloc[0][column_check] - closest_discharge_match.iloc[0][column_check] ) self.assertLessEqual(np.abs(off_by), np.abs(closest_interp2_match.iloc[0][column_check]) * acceptable_error + acceptable_error_offest)
def test_get_diagnostic(self): maccor_file_w_parameters_s3 = { "bucket": "beep-sync-test-stage", "key": "big_file_tests/PreDiag_000287_000128.092" } maccor_file_w_parameters = os.path.join(TEST_FILE_DIR, "PreDiag_000287_000128.092") download_s3_object(bucket=maccor_file_w_parameters_s3["bucket"], key=maccor_file_w_parameters_s3["key"], destination_path=maccor_file_w_parameters) md = MaccorDatapath.from_file(maccor_file_w_parameters) ( v_range, resolution, nominal_capacity, full_fast_charge, diagnostic_available, ) = md.determine_structuring_parameters() self.assertEqual(nominal_capacity, 4.84) # self.assertEqual(v_range, [2.7, 4.2]) # This is an older assertion, value changed when # different cell types were added self.assertEqual(v_range, [2.5, 4.2]) self.assertEqual( diagnostic_available["cycle_type"], ["reset", "hppc", "rpt_0.2C", "rpt_1C", "rpt_2C"], ) diag_summary = md.summarize_diagnostic(diagnostic_available) reg_summary = md.summarize_cycles(diagnostic_available) self.assertEqual(len(reg_summary.cycle_index.tolist()), 230) self.assertEqual(reg_summary.cycle_index.tolist()[:10], [0, 6, 7, 8, 9, 10, 11, 12, 13, 14]) # Check data types are being set correctly for diagnostic summary diag_dyptes = diag_summary.dtypes.tolist() diag_columns = diag_summary.columns.tolist() diag_dyptes = [str(dtyp) for dtyp in diag_dyptes] for indx, col in enumerate(diag_columns): self.assertEqual(diag_dyptes[indx], STRUCTURE_DTYPES["diagnostic_summary"][col]) self.assertEqual( diag_summary.cycle_index.tolist(), [ 1, 2, 3, 4, 5, 36, 37, 38, 39, 40, 141, 142, 143, 144, 145, 246, 247 ], ) self.assertEqual( diag_summary.cycle_type.tolist(), [ "reset", "hppc", "rpt_0.2C", "rpt_1C", "rpt_2C", "reset", "hppc", "rpt_0.2C", "rpt_1C", "rpt_2C", "reset", "hppc", "rpt_0.2C", "rpt_1C", "rpt_2C", "reset", "hppc", ], ) self.assertEqual(diag_summary.paused.max(), 0) diag_interpolated = md.interpolate_diagnostic_cycles( diagnostic_available, resolution=1000) # Check data types are being set correctly for interpolated data diag_dyptes = diag_interpolated.dtypes.tolist() diag_columns = diag_interpolated.columns.tolist() diag_dyptes = [str(dtyp) for dtyp in diag_dyptes] for indx, col in enumerate(diag_columns): self.assertEqual(diag_dyptes[indx], STRUCTURE_DTYPES["diagnostic_interpolated"][col]) # Provide visual inspection to ensure that diagnostic interpolation is being done correctly diag_cycle = diag_interpolated[ (diag_interpolated.cycle_type == "rpt_0.2C") & (diag_interpolated.step_type == 1)] self.assertEqual(diag_cycle.cycle_index.unique().tolist(), [3, 38, 143]) plt.figure() plt.plot(diag_cycle.discharge_capacity, diag_cycle.voltage) plt.savefig( os.path.join(TEST_FILE_DIR, "discharge_capacity_interpolation.png")) plt.figure() plt.plot(diag_cycle.voltage, diag_cycle.discharge_dQdV) plt.savefig( os.path.join(TEST_FILE_DIR, "discharge_dQdV_interpolation.png")) self.assertEqual(len(diag_cycle.index), 3000) hppcs = diag_interpolated[(diag_interpolated.cycle_type == "hppc") & pd.isnull(diag_interpolated.current)] self.assertEqual(len(hppcs), 0) hppc_dischg1 = diag_interpolated[ (diag_interpolated.cycle_index == 37) & (diag_interpolated.step_type == 2) & (diag_interpolated.step_index_counter == 3) & ~pd.isnull(diag_interpolated.current)] plt.figure() plt.plot(hppc_dischg1.test_time, hppc_dischg1.voltage) plt.savefig(os.path.join(TEST_FILE_DIR, "hppc_discharge_pulse_1.png")) self.assertEqual(len(hppc_dischg1), 176) # processed_cycler_run = cycler_run.to_processed_cycler_run() md.autostructure() self.assertNotIn( diag_summary.cycle_index.tolist(), md.structured_data.cycle_index.unique(), ) self.assertEqual( reg_summary.cycle_index.tolist(), md.structured_summary.cycle_index.tolist(), ) processed_cycler_run_loc = os.path.join(TEST_FILE_DIR, "processed_diagnostic.json") # Dump to the structured file and check the file size # File size had to be incteased as datapath dump includes ALL data now dumpfn(md, processed_cycler_run_loc) proc_size = os.path.getsize(processed_cycler_run_loc) self.assertLess(proc_size, 260000000) # Reload the structured file and check for errors test = loadfn(processed_cycler_run_loc) self.assertIsInstance(test.diagnostic_summary, pd.DataFrame) diag_dyptes = test.diagnostic_summary.dtypes.tolist() diag_columns = test.diagnostic_summary.columns.tolist() diag_dyptes = [str(dtyp) for dtyp in diag_dyptes] for indx, col in enumerate(diag_columns): self.assertEqual(diag_dyptes[indx], STRUCTURE_DTYPES["diagnostic_summary"][col]) diag_dyptes = test.diagnostic_data.dtypes.tolist() diag_columns = test.diagnostic_data.columns.tolist() diag_dyptes = [str(dtyp) for dtyp in diag_dyptes] for indx, col in enumerate(diag_columns): self.assertEqual(diag_dyptes[indx], STRUCTURE_DTYPES["diagnostic_interpolated"][col]) self.assertEqual(test.structured_summary.cycle_index.tolist()[:10], [0, 6, 7, 8, 9, 10, 11, 12, 13, 14]) plt.figure() single_charge = test.structured_data[ (test.structured_data.step_type == "charge") & (test.structured_data.cycle_index == 25)] self.assertEqual(len(single_charge.index), 1000) plt.plot(single_charge.charge_capacity, single_charge.voltage) plt.savefig( os.path.join(TEST_FILE_DIR, "charge_capacity_interpolation_regular_cycle.png")) os.remove(processed_cycler_run_loc)