def test_get_step_index(self): structured_datapath_loc = os.path.join( TEST_FILE_DIR, "PreDiag_000240_000227_truncated_structure.json") parameters_path = os.path.join(TEST_FILE_DIR, "data-share", "raw", "parameters") structured_datapath = auto_load_processed(structured_datapath_loc) data = structured_datapath.diagnostic_data hppc_cycles = data.loc[data.cycle_type == "hppc"] # print(hppc_cycles.step_index.unique()) _, protocol_name = os.path.split(structured_datapath.metadata.protocol) parameter_row, _ = parameters_lookup.get_protocol_parameters( protocol_name, parameters_path=parameters_path) for cycle in hppc_cycles.cycle_index.unique(): hppc_cycle = hppc_cycles[hppc_cycles.cycle_index == cycle] for step in hppc_cycle.step_index.unique(): hppc_cycle_step = hppc_cycle[(hppc_cycle.step_index == step)] for step_iter in hppc_cycle_step.step_index_counter.unique(): hppc_cycle_step_iter = hppc_cycle_step[( hppc_cycle_step.step_index_counter == step_iter)] duration = hppc_cycle_step_iter.test_time.max( ) - hppc_cycle_step_iter.test_time.min() median_crate = np.round( hppc_cycle_step.current.median() / parameter_row["capacity_nominal"].iloc[0], 2) # print(step, median_crate, duration) step_ind = featurizer_helpers.get_step_index(structured_datapath, cycle_type="hppc", diag_pos=0) self.assertEqual(len(step_ind.values()), 6) print([ step_ind["hppc_long_rest"], step_ind["hppc_discharge_pulse"], step_ind["hppc_short_rest"], step_ind["hppc_charge_pulse"], step_ind["hppc_discharge_to_next_soc"] ]) self.assertEqual( step_ind, { 'hppc_charge_to_soc': 9, 'hppc_long_rest': 11, 'hppc_discharge_pulse': 12, 'hppc_short_rest': 13, 'hppc_charge_pulse': 14, 'hppc_discharge_to_next_soc': 15 }) step_ind = featurizer_helpers.get_step_index(structured_datapath, cycle_type="hppc", diag_pos=1) self.assertEqual(len(step_ind.values()), 6) self.assertEqual( step_ind, { 'hppc_charge_to_soc': 41, 'hppc_long_rest': 43, 'hppc_discharge_pulse': 44, 'hppc_short_rest': 45, 'hppc_charge_pulse': 46, 'hppc_discharge_to_next_soc': 47 })
def test_get_protocol_parameters(self): filepath = os.path.join(TEST_FILE_DIR, "PredictionDiagnostics_000109_tztest.010") parameters, _ = parameters_lookup.get_protocol_parameters( filepath, parameters_path=PROTOCOL_PARAMETERS_DIR) self.assertEqual(parameters["diagnostic_type"].iloc[0], "HPPC+RPT") self.assertEqual(parameters["diagnostic_parameter_set"].iloc[0], "Tesla21700") self.assertEqual(parameters["seq_num"].iloc[0], 109) self.assertEqual(len(parameters.index), 1) parameters_missing, project_missing = parameters_lookup.get_protocol_parameters( "Fake", parameters_path=PROTOCOL_PARAMETERS_DIR) self.assertEqual(parameters_missing, None) self.assertEqual(project_missing, None) filepath = os.path.join(TEST_FILE_DIR, "PreDiag_000292_tztest.010") parameters, _ = parameters_lookup.get_protocol_parameters( filepath, parameters_path=PROTOCOL_PARAMETERS_DIR) self.assertEqual(parameters["diagnostic_type"].iloc[0], "HPPC+RPT") self.assertEqual(parameters["seq_num"].iloc[0], 292)
def get_parameter_dict(file_list, parameters_path): """ Helper function to generate a dictionary with Args: file_list (list): List of filenames from self.filenames parameters_path (str): Root directory storing project parameter files. Returns: Dictionary with file_list as keys, and corresponding dictionary of protocol parameters as values """ d = { } # dict allows combining two different project parameter sets into the same structure for file in file_list: param_row, _ = parameters_lookup.get_protocol_parameters( file, parameters_path) d[file] = param_row.to_dict('records')[ 0] # to_dict('records') returns a list. return d
def get_fractional_quantity_remaining_nx( processed_cycler_run, metric="discharge_energy", diagnostic_cycle_type="rpt_0.2C", parameters_path="data-share/raw/parameters"): """ Similar to get_fractional_quantity_remaining() Determine relative loss of <metric> in diagnostic_cycles of type <diagnostic_cycle_type> Also returns value of 'x', the discharge throughput passed by the first diagnostic and the value 'n' at each diagnostic Args: processed_cycler_run (beep.structure.ProcessedCyclerRun): information about cycler run metric (str): column name to use for measuring degradation diagnostic_cycle_type (str): the diagnostic cycle to use for computing the amount of degradation parameters_path (str): path to the parameters file for this run Returns: a dataframe with cycle_index, corresponding degradation relative to the first measured value, 'x', i.e. the discharge throughput passed by the first diagnostic and the value 'n' at each diagnostic, i.e. the equivalent scaling factor for lifetime using n*x """ summary_diag_cycle_type = processed_cycler_run.diagnostic_summary[( processed_cycler_run.diagnostic_summary.cycle_type == diagnostic_cycle_type)].reset_index() summary_diag_cycle_type = summary_diag_cycle_type[["cycle_index", metric]] # For the nx addition if 'energy' in metric: normalize_qty = 'discharge' + '_energy' else: normalize_qty = 'discharge' + '_capacity' normalize_qty_throughput = normalize_qty + '_throughput' regular_summary = processed_cycler_run.structured_summary.copy() regular_summary = regular_summary[regular_summary.cycle_index != 0] diagnostic_summary = processed_cycler_run.diagnostic_summary.copy() # TODO the line below should become superfluous regular_summary = regular_summary[~regular_summary.cycle_index. isin(diagnostic_summary.cycle_index)] regular_summary.loc[:, normalize_qty_throughput] = regular_summary[ normalize_qty].cumsum() diagnostic_summary.loc[:, normalize_qty_throughput] = diagnostic_summary[ normalize_qty].cumsum() # Trim the cycle index in summary_diag_cycle_type to the max cycle in the regular cycles # (no partial cycles in the regular cycle summary) so that only full cycles are used for n summary_diag_cycle_type = summary_diag_cycle_type[ summary_diag_cycle_type.cycle_index <= regular_summary.cycle_index.max()] # Second gap in the regular cycles indicates the second set of diagnostics, bookending the # initial set of regular cycles. first_degradation_cycle = int(regular_summary.cycle_index[ regular_summary.cycle_index.diff() > 1].iloc[0]) last_initial_cycle = int(regular_summary.cycle_index[ regular_summary.cycle_index < first_degradation_cycle].iloc[-1]) initial_regular_throughput = regular_summary[ regular_summary.cycle_index == last_initial_cycle][normalize_qty_throughput].values[0] summary_diag_cycle_type.loc[:, 'initial_regular_throughput'] = initial_regular_throughput summary_diag_cycle_type.loc[:, 'normalized_regular_throughput'] = summary_diag_cycle_type.apply( lambda x: (1 / initial_regular_throughput) * regular_summary[regular_summary.cycle_index < x['cycle_index']] [normalize_qty_throughput].max(), axis=1) summary_diag_cycle_type['normalized_regular_throughput'].fillna( value=0, inplace=True) summary_diag_cycle_type.loc[:, 'normalized_diagnostic_throughput'] = summary_diag_cycle_type.apply( lambda x: (1 / initial_regular_throughput) * diagnostic_summary[ diagnostic_summary.cycle_index < x['cycle_index']][ normalize_qty_throughput].max(), axis=1) summary_diag_cycle_type['normalized_diagnostic_throughput'].fillna( value=0, inplace=True) # end of nx addition, calculate the fractional capacity compared to the first diagnostic cycle (reset) summary_diag_cycle_type.loc[:, metric] = ( summary_diag_cycle_type[metric] / processed_cycler_run.diagnostic_summary[metric].iloc[0]) if "\\" in processed_cycler_run.metadata.protocol: protocol_name = processed_cycler_run.metadata.protocol.split("\\")[-1] else: _, protocol_name = os.path.split( processed_cycler_run.metadata.protocol) parameter_row, _ = parameters_lookup.get_protocol_parameters( protocol_name, parameters_path=parameters_path) summary_diag_cycle_type.loc[:, 'diagnostic_start_cycle'] = parameter_row[ 'diagnostic_start_cycle'].values[0] summary_diag_cycle_type.loc[:, 'diagnostic_interval'] = parameter_row[ 'diagnostic_interval'].values[0] # TODO add number of initial regular cycles and interval to the dataframe summary_diag_cycle_type.columns = [ "cycle_index", "fractional_metric", "initial_regular_throughput", "normalized_regular_throughput", "normalized_diagnostic_throughput", "diagnostic_start_cycle", "diagnostic_interval" ] return summary_diag_cycle_type
def get_step_index(pcycler_run, cycle_type="hppc", diag_pos=0): """ Gets the step indices of the diagnostic cycle which correspond to specific attributes Args: pcycler_run (beep.structure.ProcessedCyclerRun): processed data cycle_type (str): which diagnostic cycle type to evaluate diag_pos (int): which iteration of the diagnostic cycle to use (0 for first, 1 for second, -1 for last) Returns: dict: descriptive keys with step index as values """ pulse_time = 120 # time in seconds used to decide if a current is a pulse or an soc change pulse_c_rate = 0.5 # c-rate to decide if a current is a discharge pulse rest_long_vs_short = 600 # time in seconds to decide if the rest is the long or short rest step soc_change_threshold = 0.05 parameters_path = os.path.join(os.environ.get("BEEP_PROCESSING_DIR", "/"), "data-share", "raw", "parameters") if "\\" in pcycler_run.metadata.protocol: protocol_name = pcycler_run.metadata.protocol.split("\\")[-1] else: _, protocol_name = os.path.split(pcycler_run.metadata.protocol) parameter_row, _ = parameters_lookup.get_protocol_parameters( protocol_name, parameters_path=parameters_path) step_indices_annotated = {} diag_data = pcycler_run.diagnostic_data cycles = diag_data.loc[diag_data.cycle_type == cycle_type] cycle = cycles[cycles.cycle_index == cycles.cycle_index.unique()[diag_pos]] if cycle_type == "hppc": for step in cycle.step_index.unique(): cycle_step = cycle[(cycle.step_index == step)] median_crate = np.round( cycle_step.current.median() / parameter_row["capacity_nominal"].iloc[0], 2) mean_crate = np.round( cycle_step.current.mean() / parameter_row["capacity_nominal"].iloc[0], 2) remaining_time = cycle.test_time.max() - cycle_step.test_time.max() recurring = len(cycle_step.step_index_counter.unique()) > 1 step_counter_duration = [] for step_iter in cycle_step.step_index_counter.unique(): cycle_step_iter = cycle_step[( cycle_step.step_index_counter == step_iter)] duration = cycle_step_iter.test_time.max( ) - cycle_step_iter.test_time.min() step_counter_duration.append(duration) median_duration = np.round(np.median(step_counter_duration), 0) if median_crate == 0.0: if median_duration > rest_long_vs_short: step_indices_annotated["hppc_long_rest"] = step elif rest_long_vs_short >= median_duration > 0: step_indices_annotated["hppc_short_rest"] = step else: raise ValueError elif median_crate <= -pulse_c_rate and median_duration < pulse_time: step_indices_annotated["hppc_discharge_pulse"] = step elif median_crate >= pulse_c_rate and median_duration < pulse_time: step_indices_annotated["hppc_charge_pulse"] = step elif mean_crate != median_crate < 0 and remaining_time == 0.0 and not recurring: step_indices_annotated["hppc_final_discharge"] = step elif mean_crate == median_crate < 0 and remaining_time == 0.0 and not recurring: step_indices_annotated["hppc_final_discharge"] = step elif mean_crate == median_crate < 0 and abs( mean_crate * median_duration / 3600) > soc_change_threshold: step_indices_annotated["hppc_discharge_to_next_soc"] = step elif median_crate > 0 and median_duration > pulse_time: step_indices_annotated["hppc_charge_to_soc"] = step elif cycle_type == "rpt_0.2C" or cycle_type == "rpt_1C" or cycle_type == "rpt_2C" or cycle_type == "reset": for step in cycle.step_index.unique(): cycle_step = cycle[(cycle.step_index == step)] median_crate = np.round( cycle_step.current.median() / parameter_row["capacity_nominal"].iloc[0], 2) if median_crate > 0: step_indices_annotated[cycle_type + "_charge"] = step elif median_crate < 0: step_indices_annotated[cycle_type + "_discharge"] = step else: raise ValueError else: raise NotImplementedError assert len(cycle.step_index.unique()) == len( step_indices_annotated.values()) return step_indices_annotated
def test_get_step_index_2(self): pcycler_run_loc = os.path.join( TEST_FILE_DIR, "PreDiag_000400_000084_truncated_structure.json") parameters_path = os.path.join(TEST_FILE_DIR, "data-share", "raw", "parameters") os.environ["BEEP_PROCESSING_DIR"] = TEST_FILE_DIR pcycler_run = auto_load_processed(pcycler_run_loc) _, protocol_name = os.path.split(pcycler_run.metadata.protocol) parameter_row, _ = parameters_lookup.get_protocol_parameters( protocol_name, parameters_path=parameters_path) step_ind = featurizer_helpers.get_step_index(pcycler_run, cycle_type="hppc", diag_pos=0) self.assertEqual(len(step_ind.values()), 7) self.assertEqual( step_ind, { 'hppc_charge_to_soc': 9, 'hppc_long_rest': 11, 'hppc_discharge_pulse': 12, 'hppc_short_rest': 13, 'hppc_charge_pulse': 14, 'hppc_discharge_to_next_soc': 15, 'hppc_final_discharge': 17 }) step_ind = featurizer_helpers.get_step_index(pcycler_run, cycle_type="hppc", diag_pos=1) self.assertEqual(len(step_ind.values()), 7) self.assertEqual( step_ind, { 'hppc_charge_to_soc': 41, 'hppc_long_rest': 43, 'hppc_discharge_pulse': 44, 'hppc_short_rest': 45, 'hppc_charge_pulse': 46, 'hppc_discharge_to_next_soc': 47, 'hppc_final_discharge': 49 }) step_ind = featurizer_helpers.get_step_index(pcycler_run, cycle_type="reset", diag_pos=0) self.assertEqual(step_ind, {'reset_charge': 5, 'reset_discharge': 6}) step_ind = featurizer_helpers.get_step_index(pcycler_run, cycle_type="reset", diag_pos=1) self.assertEqual(step_ind, {'reset_charge': 38, 'reset_discharge': 39}) step_ind = featurizer_helpers.get_step_index(pcycler_run, cycle_type="rpt_0.2C", diag_pos=0) self.assertEqual(step_ind, { 'rpt_0.2C_charge': 19, 'rpt_0.2C_discharge': 20 }) step_ind = featurizer_helpers.get_step_index(pcycler_run, cycle_type="rpt_0.2C", diag_pos=1) self.assertEqual(step_ind, { 'rpt_0.2C_charge': 51, 'rpt_0.2C_discharge': 52 }) step_ind = featurizer_helpers.get_step_index(pcycler_run, cycle_type="rpt_1C", diag_pos=0) self.assertEqual(step_ind, { 'rpt_1C_charge': 22, 'rpt_1C_discharge': 23 }) step_ind = featurizer_helpers.get_step_index(pcycler_run, cycle_type="rpt_1C", diag_pos=1) self.assertEqual(step_ind, { 'rpt_1C_charge': 54, 'rpt_1C_discharge': 55 }) step_ind = featurizer_helpers.get_step_index(pcycler_run, cycle_type="rpt_2C", diag_pos=0) self.assertEqual(step_ind, { 'rpt_2C_charge': 25, 'rpt_2C_discharge': 26 }) step_ind = featurizer_helpers.get_step_index(pcycler_run, cycle_type="rpt_2C", diag_pos=1) self.assertEqual(step_ind, { 'rpt_2C_charge': 57, 'rpt_2C_discharge': 58 })