diff --git a/.gitignore b/.gitignore index cda7e3a37b6607b3b9360a049121d38774cecebe..ac7199b53782ed72cb611b38524cbc18216dfea4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +build +dist + # ignore results *.csv *.mat @@ -31,4 +34,6 @@ dymosim* __pycache__ # ignore jupyter notebook files -.ipynb_checkpoints \ No newline at end of file +.ipynb_checkpoints + +.eggs \ No newline at end of file diff --git a/dataprocessing/__init__.py b/dataprocessing/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ffc05b2550467dbaaf3036d94a2536a0ff1d9de2 100644 --- a/dataprocessing/__init__.py +++ b/dataprocessing/__init__.py @@ -0,0 +1 @@ +__all__ = ["readtools", "plottools", "timeseries"] \ No newline at end of file diff --git a/dataprocessing/dpsim.py b/dataprocessing/dpsim.py index e56cb5ff54e9f7f5cf8332474d241f008d72a269..67d0f82b4a0878b470f84b6f7bf2fcb249f6668c 100644 --- a/dataprocessing/dpsim.py +++ b/dataprocessing/dpsim.py @@ -1,5 +1,6 @@ from dataprocessing.readtools import * from dataprocessing.timeseries import * +from dataprocessing.plottools import * def get_node_voltage_phasors(dpsim_timeseries_list): """Calculate voltage phasors of all nodes @@ -24,7 +25,7 @@ def get_node_emt_voltages(timeseries_list, freq): """ voltages_list = {} for name, ts in timeseries_list.items(): - ts_emt = ts.dynphasor_shift_to_emt(ts.name, freq) + ts_emt = ts.frequency_shift(ts.name, freq) voltages_list[ts.name] = ts_emt return voltages_list diff --git a/dataprocessing/readtools.py b/dataprocessing/readtools.py index 99eba9f9b0ebddc793bf7ee58ecd895bb5d4aa9c..9226ff6e751782972e6a27977d3aaa9e10f0fbfb 100644 --- a/dataprocessing/readtools.py +++ b/dataprocessing/readtools.py @@ -56,24 +56,51 @@ def read_timeseries_PLECS(filename, timeseries_names=None): def read_timeseries_simulink(filename, timeseries_names=None): pd_df = pd.read_csv(filename) - timeseries_list = [] + timeseries_list = {} + cmpl_result_columns = [] + real_result_columns = [] + if timeseries_names is None: - # No trajectory names specified, thus read in all - timeseries_names = list(pd_df.columns.values) - timeseries_names.remove('time') - for name in timeseries_names: - timeseries_list.append(TimeSeries(name, pd_df['time'].values, pd_df[name].values)) + # No column names specified, thus read in all and strip off spaces + pd_df.rename(columns=lambda x: x.strip(), inplace=True) + column_names = list(pd_df.columns.values) + + # Remove timestamps column name and store separately + column_names.remove('time') + timestamps = pd_df.iloc[:, 0] + + # Find real and complex variable names + real_string = '.real' + imaginary_string = '.imag' + for column in column_names: + if real_string in column: + tmp = column.replace(real_string, '') + cmpl_result_columns.append(tmp) + #print("Found complex variable: " + tmp) + elif not imaginary_string in column: + real_result_columns.append(column) + #print("Found real variable: " + column) + + for column in real_result_columns: + timeseries_list[column] = TimeSeries(column, timestamps, pd_df[column]) + + for column in cmpl_result_columns: + timeseries_list[column] = TimeSeries(column, timestamps, + np.vectorize(complex)(pd_df[column + real_string], + pd_df[column + imaginary_string])) + else: # Read in specified time series - for name in timeseries_names: - timeseries_list.append(TimeSeries(name, pd_df['time'].values, pd_df[name].values)) + print('cannot read specified columns yet') - print('Simulink results column names: ' + str(timeseries_names)) - print('Simulink results number: ' + str(len(timeseries_list))) + print('Simulink results real column names: ' + str(real_result_columns)) + print('Simulink results complex column names: ' + str(cmpl_result_columns)) + print('Simulink results variable number: ' + str(len(timeseries_list))) + print('Simulink results length: ' + str(len(timestamps))) return timeseries_list -def read_timeseries_dpsim(filename, timeseries_names=None): +def read_timeseries_dpsim(filename, timeseries_names=None, print_status=True): """Reads complex time series data from DPsim log file. Real and imaginary part are stored in one complex variable. :param filename: name of the csv file that has the data @@ -132,9 +159,39 @@ def read_timeseries_dpsim(filename, timeseries_names=None): # Read in specified time series print('cannot read specified columns yet') - print('DPsim results real column names: ' + str(real_result_columns)) - print('DPsim results complex column names: ' + str(cmpl_result_columns)) - print('DPsim results variable number: ' + str(len(timeseries_list))) - print('DPsim results length: ' + str(len(timestamps))) + if print_status : + print('DPsim results real column names: ' + str(real_result_columns)) + print('DPsim results complex column names: ' + str(cmpl_result_columns)) + print('DPsim results variable number: ' + str(len(timeseries_list))) + print('DPsim results length: ' + str(len(timestamps))) return timeseries_list + +def read_dpsim_log(log_path): + log_file = open(log_path, "r") + log_lines = [line for line in log_file] + log_file.close() + + # Sectionize + log_sections = {'init':[], 'none':[], 'sysmat_stamp':[], 'sysmat_final':[], 'sourcevec_stamp':[], 'sourcevec_final':[], 'ludecomp':[]} + section = 'init' + for line_pos in range(len(log_lines)): + if re.search('DEBUG: Stamping .+ into system matrix:', log_lines[line_pos]): + section = 'sysmat_stamp' + elif re.search('INFO: System matrix:', log_lines[line_pos]): + section = 'sysmat_final' + elif re.search('DEBUG: Stamping .+ into source vector:', log_lines[line_pos]): + section = 'sourcevec_stamp' + elif re.search('INFO: Right side vector:', log_lines[line_pos]): + section = 'sourcevec_final' + elif re.search('INFO: LU decomposition:', log_lines[line_pos]): + section = 'ludecomp' + elif re.search('INFO: Number of network simulation nodes:', log_lines[line_pos]): + section = 'none' + elif re.search('INFO: Added .+ to simulation.', log_lines[line_pos]): + section = 'none' + elif re.search('INFO: Initial switch status:', log_lines[line_pos]): + section = 'none' + log_sections[section].append(line_pos) + + return log_lines, log_sections diff --git a/dataprocessing/timeseries.py b/dataprocessing/timeseries.py index 300c0b5c8759e679a84b73299df0e0c281e0fa93..d94ca4131aa165c18def7c276507d0810fa05e7f 100644 --- a/dataprocessing/timeseries.py +++ b/dataprocessing/timeseries.py @@ -37,6 +37,31 @@ class TimeSeries: ts_phase = TimeSeries(name, self.time, phase_values) return ts_phase + def phasor(self, name): + """Calculate phasor of complex time series and return dict with abs and phase. + """ + ts_abs = ts.abs(ts.name + '_abs') + ts_phase = ts.phase(ts.name + '_phase') + ts_phasor = {} + ts_phasor['abs'] = ts_abs + ts_phasor['phase'] = ts_phase + + return ts_phasor + + @staticmethod + def frequency_shift_list(timeseries_list, freq): + """Calculate shifted frequency results of all time series + :param timeseries_list: timeseries list retrieved from dpsim results + :param freq: frequency by which the timeseries should be shifted + :return: list of shifted time series + """ + result_list = {} + for name, ts in timeseries_list.items(): + ts_emt = ts.frequency_shift(ts.name, freq) + result_list[ts.name] = ts_emt + + return result_list + @staticmethod def rmse(ts1, ts2): """ Calculate root mean square error between two time series @@ -69,7 +94,7 @@ class TimeSeries: ts_diff = TimeSeries(name, time, (interp_vals_ts2 - interp_vals_ts1)) return ts_diff - def dynphasor_shift_to_emt(self, name, freq): + def frequency_shift(self, name, freq): """ Shift dynamic phasor values to EMT by frequency freq. Assumes the same time steps for both timeseries. :param name: name of returned time series