Source code for mhkit.power.quality

import pandas as pd
import numpy as np
import scipy.integrate as integrate
from scipy.optimize import fsolve
from scipy.signal import hilbert
from scipy import signal, fft, fftpack


#This group of functions are to be used for power quality assessments 

[docs]def harmonics(x,freq,grid_freq): """ Calculates the harmonics from time series of voltage or current based on IEC 61000-4-7. Parameters ----------- x: pandas Series or DataFrame Time-series of voltage [V] or current [A] freq: float or Int Frequency of the time-series data [Hz] grid_freq: int Value indicating if the power supply is 50 or 60 Hz. Options = 50 or 60 Returns -------- harmonics: pandas DataFrame Amplitude of the time-series data harmonics indexed by the harmonic frequency with signal name columns """ assert isinstance(x, (pd.Series, pd.DataFrame)), 'Provided voltage or current must be of type pd.DataFrame or pd.Series' assert isinstance(freq, (float, int)), 'freq must be of type float or integer' assert (grid_freq == 50 or grid_freq == 60), 'grid_freq must be either 50 or 60' # Check if x is a DataFrame if isinstance(x, (pd.DataFrame)) == True: cols = x.columns x = x.to_numpy() sample_spacing = 1./freq frequency_bin_centers = fftpack.fftfreq(len(x), d=sample_spacing) harmonics_amplitude = np.abs(np.fft.fft(x, axis=0)) harmonics = pd.DataFrame(harmonics_amplitude, index=frequency_bin_centers) harmonics = harmonics.sort_index() # Keep the signal name as the column name if 'cols' in locals(): harmonics.columns = cols if grid_freq == 60: hz = np.arange(0,3060,5) elif grid_freq == 50: hz = np.arange(0,2570,5) harmonics = harmonics.reindex(hz, method='nearest') harmonics = harmonics/len(x)*2 return harmonics
[docs]def harmonic_subgroups(harmonics, grid_freq): """ Calculates the harmonic subgroups based on IEC 61000-4-7 Parameters ---------- harmonics: pandas Series or DataFrame Harmonic amplitude indexed by the harmonic frequency grid_freq: int Value indicating if the power supply is 50 or 60 Hz. Options = 50 or 60 Returns -------- harmonic_subgroups: pandas DataFrame Harmonic subgroups indexed by harmonic frequency with signal name columns """ assert isinstance(harmonics, (pd.Series, pd.DataFrame)), 'harmonics must be of type pd.DataFrame or pd.Series' assert (grid_freq == 50 or grid_freq == 60), 'grid_freq must be either 50 or 60' # Check if harmonics is a DataFrame if isinstance(harmonics, (pd.DataFrame)) == True: cols = harmonics.columns if grid_freq == 60: hz = np.arange(0,3060,60) elif grid_freq == 50: hz = np.arange(0,2550,50) j=0 i=0 cols=harmonics.columns harmonic_subgroups=np.ones((np.size(hz),np.size(cols))) for n in hz: harmonics=harmonics.sort_index(axis=0) ind=pd.Index(harmonics.index) indn = ind.get_loc(n, method='nearest') for col in cols: harmonic_subgroups[i,j] = np.sqrt(np.sum([harmonics[col].iloc[indn-1]**2,harmonics[col].iloc[indn]**2,harmonics[col].iloc[indn+1]**2])) j=j+1 j=0 i=i+1 harmonic_subgroups = pd.DataFrame(harmonic_subgroups,index=hz) # Keep the signal name as the column name if 'cols' in locals(): harmonic_subgroups.columns = cols return harmonic_subgroups
[docs]def total_harmonic_current_distortion(harmonics_subgroup,rated_current): """ Calculates the total harmonic current distortion (THC) based on IEC/TS 62600-30 Parameters ---------- harmonics_subgroup: pandas DataFrame or Series Subgrouped current harmonics indexed by harmonic frequency rated_current: float Rated current of the energy device in Amps Returns -------- THCD: pd.DataFrame Total harmonic current distortion indexed by signal name with THCD column """ assert isinstance(harmonics_subgroup, (pd.Series, pd.DataFrame)), 'harmonic_subgroups must be of type pd.DataFrame or pd.Series' assert isinstance(rated_current, float), 'rated_current must be a float' harmonics_sq = harmonics_subgroup.iloc[2:50]**2 harmonics_sum=harmonics_sq.sum() THCD = (np.sqrt(harmonics_sum)/harmonics_subgroup.iloc[1])*100 THCD = pd.DataFrame(THCD) # converting to dataframe for Matlab THCD.columns = ['THCD'] THCD = THCD.T return THCD
[docs]def interharmonics(harmonics,grid_freq): """ Calculates the interharmonics from the harmonics of current Parameters ----------- harmonics: pandas Series or DataFrame Harmonic amplitude indexed by the harmonic frequency grid_freq: int Value indicating if the power supply is 50 or 60 Hz. Options = 50 or 60 Returns ------- interharmonics: pandas DataFrame Interharmonics groups """ assert isinstance(harmonics, (pd.Series, pd.DataFrame)), 'harmonics must be of type pd.DataFrame or pd.Series' assert (grid_freq == 50 or grid_freq == 60), 'grid_freq must be either 50 or 60' if grid_freq == 60: hz = np.arange(0,3060,60) elif grid_freq == 50: hz = np.arange(0,2550,50) j=0 i=0 cols=harmonics.columns interharmonics=np.ones((np.size(hz),np.size(cols))) for n in hz: harmonics=harmonics.sort_index(axis=0) ind=pd.Index(harmonics.index) indn = ind.get_loc(n, method='nearest') for col in cols: if grid_freq == 60: subset = harmonics[col].iloc[indn+1:indn+11]**2 subset = subset.squeeze() else: subset = harmonics[col].iloc[indn+1:indn+7]**2 subset = subset.squeeze() interharmonics[i,j] = np.sqrt(np.sum(subset)) j=j+1 j=0 i=i+1 interharmonics = pd.DataFrame(interharmonics,index=hz) return interharmonics