308 lines · 14.6 KB · C#
RawDownload
//******************************************************************************************************//  PowerStability.cs - Gbtc////  Copyright © 2012, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://www.opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  11/08/2006 - J. Ritchie Carroll//      Initial version of source generated//  12/22/2009 - Jian R. Zuo//       Converted code to C#;//  04/12/2010 - J. Ritchie Carroll//       Performed full code review, optimization and further abstracted code for stability calculator.//  12/13/2012 - Starlynn Danyelle Gilliam//       Modified Header.////****************************************************************************************************** using System;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using System.Text;using GSF.Collections;using GSF.NumericalAnalysis;using GSF.TimeSeries;using GSF.TimeSeries.Adapters;using GSF.Units;using GSF.Units.EE;using PhasorProtocolAdapters; namespace PowerCalculations{    /// <summary>    /// Represents an algorithm that calculates power and stability from a synchrophasor device.    /// </summary>    /// <remarks>    /// <para>    /// This algorithm calculates power and its standard deviation in real-time that can be used to    /// determine if there is an oscillatory signature in the power output.    /// </para>    /// <para>    /// If multiple voltage phasors are provided as inputs to this algorithm, then they are assumed to be    /// redundant values on the same bus, the first energized value will be the voltage phasor that is    /// used in the calculation.<br/>    /// If multiple current phasors are provided as inputs to this algorithm, then they are assumed to be    /// cumulative inputs representing the desired power output summation of the generation source.    /// </para>    /// <para>    /// Individual phase angle and magnitude phasor elements are expected to be defined consecutively.    /// That is the definition order of angles and magnitudes must match so that the angle / magnitude    /// pair can be matched up appropriately. For example: angle1;mag1;  angle2;mag2;  angle3;mag3.    /// </para>    /// </remarks>    [Description("Power Stability: Calculates power and stability for a synchrophasor device")]    public class PowerStability : CalculatedMeasurementBase    {        #region [ Members ]         // Fields        private int m_minimumSamples;        private double m_energizedThreshold;        private List<double> m_powerDataSample;        private double m_lastStdev;        private MeasurementKey[] m_voltageAngles;        private MeasurementKey[] m_voltageMagnitudes;        private MeasurementKey[] m_currentAngles;        private MeasurementKey[] m_currentMagnitudes;         // Important: Make sure output definition defines points in the following order        private enum Output        {            Power,            StDev        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets the sample size, in seconds, of the data to be monitored.        /// </summary>        [ConnectionStringParameter,        Description("Define the sample size, in seconds, of the data to be monitored."),        DefaultValue(15)]        public int SampleSize        {            get            {                return m_minimumSamples / FramesPerSecond;            }            set            {                m_minimumSamples = value * FramesPerSecond;            }        }         /// <summary>        /// Gets or sets the energized bus threshold, in volts. The recommended value is 20% of nominal line-to-neutral voltage.        /// </summary>        [ConnectionStringParameter,        Description("Define the energized bus threshold, in volts. The recommended value is 20% of nominal line-to-neutral voltage."),        DefaultValue(58000.0D)]        public double EnergizedThreshold        {            get            {                return m_energizedThreshold;            }            set            {                m_energizedThreshold = value;            }        }         /// <summary>        /// Returns the detailed status of the <see cref="PowerStability"/> monitor.        /// </summary>        public override string Status        {            get            {                const int ValuesToShow = 3;                 StringBuilder status = new StringBuilder();                 status.AppendFormat("          Data sample size: {0} seconds", (int)(m_minimumSamples / FramesPerSecond));                status.AppendLine();                status.AppendFormat("   Energized bus threshold: {0} volts", m_energizedThreshold.ToString("0.00"));                status.AppendLine();                status.AppendFormat("     Total voltage phasors: {0}", m_voltageMagnitudes.Length);                status.AppendLine();                status.AppendFormat("     Total current phasors: {0}", m_currentMagnitudes.Length);                status.AppendLine();                status.Append("         Last power values: ");                 lock (m_powerDataSample)                {                    // Display last several values                    if (m_powerDataSample.Count > ValuesToShow)                        status.Append(m_powerDataSample.GetRange(m_powerDataSample.Count - ValuesToShow - 1, ValuesToShow).Select(v => v.ToString("0.00MW")).ToDelimitedString(", "));                    else                        status.Append("Not enough values calculated yet...");                }                 status.AppendLine();                status.AppendFormat("     Latest stdev of power: {0}", m_lastStdev);                status.AppendLine();                status.Append(base.Status);                 return status.ToString();            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Initializes the <see cref="PowerStability"/> monitor.        /// </summary>        public override void Initialize()        {            base.Initialize();             Dictionary<string, string> settings = Settings;            string setting;             // Load parameters            if (settings.TryGetValue("sampleSize", out setting))            // Data sample size to monitor, in seconds                m_minimumSamples = int.Parse(setting) * FramesPerSecond;            else                m_minimumSamples = 15 * FramesPerSecond;             if (settings.TryGetValue("energizedThreshold", out setting))    // Energized bus threshold, in volts, recommended value                m_energizedThreshold = double.Parse(setting);               // is 20% of nominal line-to-neutral voltage            else                m_energizedThreshold = 58000.0D;             // Load needed phase angle and magnitude measurement keys from defined InputMeasurementKeys            m_voltageAngles = InputMeasurementKeys.Where((key, index) => InputMeasurementKeyTypes[index] == SignalType.VPHA).ToArray();            m_voltageMagnitudes = InputMeasurementKeys.Where((key, index) => InputMeasurementKeyTypes[index] == SignalType.VPHM).ToArray();            m_currentAngles = InputMeasurementKeys.Where((key, index) => InputMeasurementKeyTypes[index] == SignalType.IPHA).ToArray();            m_currentMagnitudes = InputMeasurementKeys.Where((key, index) => InputMeasurementKeyTypes[index] == SignalType.IPHM).ToArray();             if (m_voltageAngles.Length < 1)                throw new InvalidOperationException("No voltage angle input measurement keys were not found - at least one voltage angle input measurement is required for the power stability monitor.");             if (m_voltageMagnitudes.Length < 1)                throw new InvalidOperationException("No voltage magnitude input measurement keys were not found - at least one voltage magnitude input measurement is required for the power stability monitor.");             if (m_currentAngles.Length < 1)                throw new InvalidOperationException("No current angle input measurement keys were not found - at least one current angle input measurement is required for the power stability monitor.");             if (m_currentMagnitudes.Length < 1)                throw new InvalidOperationException("No current magnitude input measurement keys were not found - at least one current magnitude input measurement is required for the power stability monitor.");             if (m_voltageAngles.Length != m_voltageMagnitudes.Length)                throw new InvalidOperationException("A different number of voltage magnitude and angle input measurement keys were supplied - the angles and magnitudes must be supplied in pairs, i.e., one voltage magnitude input measurement must be supplied for each voltage angle input measurement in a consecutive sequence (e.g., VA1;VM1;  VA2;VM2; ... VAn;VMn)");             if (m_currentAngles.Length != m_currentMagnitudes.Length)                throw new InvalidOperationException("A different number of current magnitude and angle input measurement keys were supplied - the angles and magnitudes must be supplied in pairs, i.e., one current magnitude input measurement must be supplied for each current angle input measurement in a consecutive sequence (e.g., IA1;IM1;  IA2;IM2; ... IAn;IMn)");             // Make sure only these phasor measurements are used as input            InputMeasurementKeys = m_voltageAngles.Concat(m_voltageMagnitudes).Concat(m_currentAngles).Concat(m_currentMagnitudes).ToArray();             // Validate output measurements            if (OutputMeasurements.Length < Enum.GetValues(typeof(Output)).Length)                throw new InvalidOperationException("Not enough output measurements were specified for the power stability monitor, expecting measurements for the \"Calculated Power\", and the \"Standard Deviation of Power\" - in this order.");             m_powerDataSample = new List<double>();        }         /// <summary>        /// Publishes the <see cref="IFrame"/> of time-aligned collection of <see cref="IMeasurement"/> values that arrived within the        /// adapter's defined <see cref="ConcentratorBase.LagTime"/>.        /// </summary>        /// <param name="frame"><see cref="IFrame"/> of measurements with the same timestamp that arrived within <see cref="ConcentratorBase.LagTime"/> that are ready for processing.</param>        /// <param name="index">Index of <see cref="IFrame"/> within a second ranging from zero to <c><see cref="ConcentratorBase.FramesPerSecond"/> - 1</c>.</param>        protected override void PublishFrame(IFrame frame, int index)        {            IDictionary<MeasurementKey, IMeasurement> measurements = frame.Measurements;            IMeasurement magnitude, angle;            double voltageMagnitude = double.NaN, voltageAngle = double.NaN, power = 0.0D;            int i;             // Get first voltage magnitude and angle value pair that is above the energized threshold            for (i = 0; i < m_voltageMagnitudes.Length; i++)            {                if (measurements.TryGetValue(m_voltageMagnitudes[i], out magnitude) && measurements.TryGetValue(m_voltageAngles[i], out angle))                {                    if (magnitude.AdjustedValue > m_energizedThreshold)                    {                        voltageMagnitude = magnitude.AdjustedValue;                        voltageAngle = angle.AdjustedValue;                        break;                    }                }            }             // Exit if bus voltage measurements were not available for calculation            if (double.IsNaN(voltageMagnitude))                return;             // Calculate the sum of the current phasors            for (i = 0; i < m_currentMagnitudes.Length; i++)            {                // Retrieve current magnitude and angle measurements as consecutive pairs                if (measurements.TryGetValue(m_currentMagnitudes[i], out magnitude) && measurements.TryGetValue(m_currentAngles[i], out angle))                    power += magnitude.AdjustedValue * Math.Cos(Angle.FromDegrees(angle.AdjustedValue - voltageAngle));                else                    return; // Exit if current measurements were not available for calculation            }             // Apply bus voltage and convert to 3-phase megawatts            power = power * voltageMagnitude / (SI.Mega / 3.0D);             // Add latest calculated power to data sample            lock (m_powerDataSample)            {                m_powerDataSample.Add(power);                 // Maintain sample size                while (m_powerDataSample.Count > m_minimumSamples)                    m_powerDataSample.RemoveAt(0);            }             IMeasurement[] outputMeasurements = OutputMeasurements;            Measurement powerMeasurement = Measurement.Clone(outputMeasurements[(int)Output.Power], power, frame.Timestamp);             // Check to see if the needed number of samples are available to begin producing the standard deviation output measurement            if (m_powerDataSample.Count >= m_minimumSamples)            {                Measurement stdevMeasurement = Measurement.Clone(outputMeasurements[(int)Output.StDev], frame.Timestamp);                 lock (m_powerDataSample)                {                    stdevMeasurement.Value = m_powerDataSample.StandardDeviation();                }                 // Provide calculated measurements for external consumption                OnNewMeasurements(new IMeasurement[] { powerMeasurement, stdevMeasurement });                 // Track last standard deviation...                m_lastStdev = stdevMeasurement.AdjustedValue;            }            else if (power > 0.0D)            {                // If not, we can still start publishing power calculation as soon as we have one...                OnNewMeasurements(new IMeasurement[] { powerMeasurement });            }        }         #endregion    }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t