def current_rms(current, period_length=PERIOD_LENGTH): """Calculates the Current RMS (CRMS). Let \\(I_{W(k)}\\) denote the \\(k\\)th of \\(N\\) periods of current measurements, \\(\\langle t_1, t_2, ..., t_n \\rangle\\) the concatenation of tuples \\(t_1, ..., t_n\\), and \\(\\nu = N \\mod 10\\). Then: \\[CRMS = \\begin{pmatrix}\\text{rms}(\\langle I_{W(1)}, I_{W(2)}, ..., I_{W(10)}\\rangle) \\\\ \\text{rms}(\\langle I_{W(11)}, I_{W(12)}, ..., I_{W(20)}\\rangle) \\\\ ... \\\\ \\text{rms}(\\langle I_{W(N - \\nu + 1)}, I_{W(N - \\nu + 2)}, ..., I_{W(N)}\\rangle)\\end{pmatrix}\\] Args: current (numpy.ndarray): (n_samples, window_size)-dimensional array of current measurements. n_periods (int): Length of transient and steady state in periods Returns: numpy.ndarray: Transient steady states ratio as a (n_samples, 1)-dimensional array. """ n = int(math.floor(current.shape[1] / period_length / 10)) cutoff = n * 10 * period_length first = rms(current[:, :cutoff].reshape(-1, n, 10 * period_length), axis=2) # Handle window_size is not multiple of 10*period_length if cutoff != current.shape[1]: return np.hstack((first, rms(current[:, cutoff:]))) return first
def positive_negative_half_cycle_ratio(current, period_length=PERIOD_LENGTH): """Calculates Positive-negative half cycle ratio (PNR). Let \\(I_{P_\\text{pos}}\\) and \\(I_{P_\\text{neg}}\\) be the RMS of 10 averaged positive and negative current half cycles. Then \\[PNR = \\frac{\\min\\{I_{P_\\text{pos}}, I_{P_\\text{neg}}\\}} {\\max\\{I_{P_\\text{pos}}, I_{P_\\text{neg}}\\}}\\] Args: current (numpy.ndarray): (n_samples, window_size)-dimensional array of current measurements. Returns: numpy.ndarray: PNR as a (n_samples, 1)-dimensional array. """ # Create indices for positive and negative half cycles of sin wave idcs = np.arange(0, period_length * 10) p_idcs = idcs[idcs % period_length < period_length / 2] n_idcs = idcs[idcs % period_length >= period_length / 2] p_current = current[:, p_idcs].reshape(-1, 10, period_length // 2) n_current = current[:, n_idcs].reshape(-1, 10, period_length // 2) * -1 # Calculate RMS of averaged half cycles p_n = np.hstack((rms(np.mean(p_current, axis=1)), rms(np.mean(n_current, axis=1)))) return (np.min(p_n, axis=1) / np.max(p_n, axis=1)).reshape(-1, 1)
def apparent_power(voltage, current): """Calculates Apparent power (S). \\[S = \\text{rms}(V) \\times \\text{rms}(I)\\] Args: voltage (numpy.ndarray): (n_samples, window_size)-dimensional array of voltage measurements. current (numpy.ndarray): (n_samples, window_size)-dimensional array of current measurements. Returns: numpy.ndarray: Apparent power as a (n_samples, 1)-dimensional array. """ return rms(voltage) * rms(current)
def resistance_mean(voltage, current): """Calculates Resistance \\(R_\\text{mean}\\) (mean version) from voltage and current arrays. \\[R_\\text{mean} = \\frac{\\sqrt{\\text{mean}(V^2)}}{\\sqrt{\\text{mean}(I^2)}}\\] Args: voltage (numpy.ndarray): (n_samples, window_size)-dimensional array of voltage measurements. current (numpy.ndarray): (n_samples, window_size)-dimensional array of current measurements. Returns: numpy.ndarray: Resistance as a (n_samples, 1)-dimensional array. """ return rms(voltage) / rms(current)
def inrush_current_ratio(current, period_length=PERIOD_LENGTH): """Calculates the Inrush current ratio (ICR). Let \\(I_{W(k)}\\) denote the \\(k\\)th of \\(N\\) periods of current measurements. Then \\(I_{P(k)} = \\text{rms}(I_{W(k)})\\) and \\[ICR = \\frac{I_{P(1)}}{I_{P(N)}}\\] Args: current (numpy.ndarray): (n_samples, window_size)-dimensional array of current measurements. Returns: numpy.ndarray: Inrush current ratio as a (n_samples, 1)-dimensional array. """ return rms(current[:, :period_length]) / rms(current[:, -period_length:])
def temporal_centroid(current, mains_frequency=POWER_FREQUENCY, period_length=PERIOD_LENGTH): """Calculates the Temporal centroid \\(C_t\\). Let \\(I_{W(k)}\\) denote the \\(k\\)th of \\(N\\) periods of current measurements. Then \\(I_{P(k)} = \\text{rms}(I_{W(k)})\\) and \\[C_t = \\frac{1}{f_0} \\cdot \\frac{\\sum_{k=1}^N I_{P(k)} \\cdot k} {\\sum_{k=1}^N I_{P(k)}} \\] Args: current (numpy.ndarray): (n_samples, window_size)-dimensional array of current measurements. mains_frequency (int): Mains frequency, defaults to power frequency. Returns: numpy.ndarray: Temporal centroid as a (n_samples, 1)-dimensional array. """ # Calculate RMS of each period # TODO: Use apply_to_periods? ip = rms(current.reshape(current.shape[0], -1, period_length), axis=2) # Calculate numerator and denominator and put together numerator = np.sum(ip * np.arange(1, ip.shape[1] + 1), axis=1) denominator = np.sum(ip, axis=1) return mains_frequency * (numerator / denominator).reshape(-1, 1)
def crest_factor(current): """Calculates Crest factor (CF). \\[CF = \\frac{\\max(|I|)}{\\text{rms}(I)}\\] Args: current (numpy.ndarray): (n_samples, window_size)-dimensional array of current measurements. Returns: numpy.ndarray: Crest factor as a (n_samples, 1)-dimensional array. """ return np.max(np.abs(current), axis=1).reshape(-1, 1) / rms(current)
def form_factor(current): """Calculates Form factor (FF). \\[FF = \\frac{\\text{rms}(I)}{\\text{mean}(|I|)}\\] Args: current (numpy.ndarray): (n_samples, window_size)-dimensional array of current measurements. Returns: numpy.ndarray: Form factor as a (n_samples, 1)-dimensional array. """ return rms(current) / np.mean(np.abs(current), axis=1).reshape(-1, 1)
def transient_steady_states_ratio(current, n_periods=5, period_length=PERIOD_LENGTH): """Calculates the Transient steady states ratio (TSSR). Let \\(I_{W(k)}\\) denote the \\(k\\)th of \\(N\\) periods of current measurements and \\(\\langle t_1, t_2, ..., t_n \\rangle\\) the concatenation of tuples \\(t_1, ..., t_n\\). Then: \\[TSSR = \\frac{\\text{rms}(\\langle I_{W(1)}, I_{W(2)}, ..., I_{W(\\text{n_periods})}\\rangle)} {\\text{rms}(\\langle I_{W(N - \\text{n_periods})}, I_{W(N - \\text{n_periods} + 1)}, ..., I_{W(N)}\\rangle)}\\] Args: current (numpy.ndarray): (n_samples, window_size)-dimensional array of current measurements. n_periods (int): Length of transient and steady state in periods Returns: numpy.ndarray: Transient steady states ratio as a (n_samples, 1)-dimensional array. """ return (rms(current[:, :n_periods * period_length]) / rms(current[:, -n_periods * period_length:])).reshape(-1, 1)
def reactive_power(voltage, current, phase_shift=None, period_length=PERIOD_LENGTH): """Calculates Reactive power (Q). If phase shift is None, it is calculated. \\[Q = \\text{rms}(V) \\times \\text{rms}(I) \\times \\sin(\\phi)\\] Args: voltage (numpy.ndarray): (n_samples, window_size)-dimensional array of voltage measurements. current (numpy.ndarray): (n_samples, window_size)-dimensional array of current measurements. phase_shift (numpy.ndarray): (n_samples, 1)-dimensional array of phase shifts. Returns: numpy.ndarray: Reactive power as a (n_samples, 1)-dimensional array. """ if phase_shift is None: phase_shift = phase_shift(voltage, current, period_length=period_length) return np.abs(rms(voltage) * rms(current) * np.sin(phase_shift))
def max_inrush_ratio(current, period_length=PERIOD_LENGTH): """Calculates the Max inrush ratio (MIR). Let \\(I_{W(k)}\\) be the current measurements of the \\(k\\)th period and \\(I_{P(k)} = \\text{rms}(I_{W(k)})\\). Then \\[MIR = \\frac{I_{P(1)}}{\\max(|I_{W(1)}|)}\\] Args: current (numpy.ndarray): (n_samples, window_size)-dimensional array of current measurements. Returns: numpy.ndarray: Max inrush ratio as a (n_samples, 1)-dimensional array. """ first_period_rms = rms(current[:, :period_length]) first_period_max = np.max(np.abs(current[:, :period_length]), axis=1) return first_period_rms / first_period_max.reshape(-1, 1)
def total_harmonic_distortion(harmonics_amp, spectrum_amp): """Calculates the Total harmonic distortion (THD). Let \\(x_{f_1}, ..., x_{f_{20}}\\) be the amplitudes of the first 20 harmonics of the current and \\(x_{f_0}\\) the mains frequency amplitude. Then: \\[THD = \\frac{\\text{rms}([x_{f_1}, x_{f_2}, x_{f_3}, ..., x_{f_{20}}])} {x_{f_0}}\\] Args: harmonics_amp (numpy.ndarray): Harmonic amplitudes as a (n_samples, n)-dimensional array. spectrum_amp (numpy.ndarray): Spectral amplitudes as a (n_samples, window)-dimensional array. Returns: numpy.ndarray: Total harmonic distortion as a (n_samples, 1)-dimensional array. """ return (rms(harmonics_amp) / _mains_frequency_amplitude(spectrum_amp))