public static double[] ModifiedTakagi(FaultLocationDataSet faultDataSet, string parameters) { ComplexNumber z; ComplexNumber[] voltages; ComplexNumber[] currents; ComplexNumber[] zeros; z = GetNominalImpedance(faultDataSet); voltages = faultDataSet.Cycles.Select(cycle => GetFaultVoltage(cycle, faultDataSet.FaultType)).ToArray(); currents = faultDataSet.Cycles.Select(cycle => GetFaultCurrent(cycle, faultDataSet.FaultType)).ToArray(); zeros = faultDataSet.Cycles.Select(cycle => 3 * CycleData.CalculateSequenceComponents(cycle.AN.I, cycle.BN.I, cycle.CN.I)[0]).ToArray(); return(voltages.Zip(currents, (v, i) => new { V = v, I = i }) .Zip(zeros, (vi, zero) => { ComplexNumber v = vi.V; ComplexNumber i = vi.I; Angle t = (i / zero).Angle; ComplexNumber ejt = new ComplexNumber(t, 1.0D); return (v * zero.Conjugate * ejt).Imaginary / (z * i * zero.Conjugate * ejt).Imaginary; }) .Select(m => m * faultDataSet.LineDistance) .ToArray()); }
private static FaultType Simple(FaultLocationDataSet faultDataSet, string parameters) { Dictionary<string, string> parameterLookup; string parameterValue; int largestCurrentIndex; CycleData largestCurrentCycle; FaultType faultType; double anCurrentRMS; double bnCurrentRMS; double cnCurrentRMS; double tolerance; double largestCurrentRMS; double faultCurrentRMS; bool anFault; bool bnFault; bool cnFault; parameterLookup = parameters.ParseKeyValuePairs(); largestCurrentIndex = faultDataSet.Cycles.GetLargestCurrentIndex(); largestCurrentCycle = faultDataSet.Cycles[largestCurrentIndex]; if ((object)largestCurrentCycle == null) throw new InvalidOperationException("No cycles found in fault data set. Cannot calculate fault type."); if (!parameterLookup.TryGetValue("tolerance", out parameterValue) || !double.TryParse(parameterValue, out tolerance)) tolerance = 10.0D; anCurrentRMS = largestCurrentCycle.AN.I.RMS; bnCurrentRMS = largestCurrentCycle.BN.I.RMS; cnCurrentRMS = largestCurrentCycle.CN.I.RMS; largestCurrentRMS = Math.Max(Math.Max(anCurrentRMS, bnCurrentRMS), cnCurrentRMS); faultCurrentRMS = largestCurrentRMS * tolerance * 0.01D; anFault = (anCurrentRMS >= faultCurrentRMS); bnFault = (bnCurrentRMS >= faultCurrentRMS); cnFault = (cnCurrentRMS >= faultCurrentRMS); if (anFault && bnFault && cnFault) faultType = FaultType.ABC; else if (anFault && bnFault) faultType = FaultType.AB; else if (bnFault && cnFault) faultType = FaultType.BC; else if (cnFault && anFault) faultType = FaultType.CA; else if (anFault) faultType = FaultType.AN; else if (bnFault) faultType = FaultType.BN; else faultType = FaultType.CN; return faultType; }
private static bool RatedCurrentTrigger(FaultLocationDataSet faultDataSet, string parameters) { Dictionary <string, string> parameterLookup; string parameterValue; double ratingMultiplier; double anFaultLimit; double bnFaultLimit; double cnFaultLimit; List <int> faultedCycles; bool anFaultCycle; bool bnFaultCycle; bool cnFaultCycle; // If no cycles exist in the data set, there is no fault if (faultDataSet.Cycles.Count <= 0) { return(false); } // Get parameters required for determining the existence of the fault parameterLookup = parameters.ParseKeyValuePairs(); if (!parameterLookup.TryGetValue("ratingMultiplier", out parameterValue) || !double.TryParse(parameterValue, out ratingMultiplier)) { ratingMultiplier = 2.0D; } // Determine the upper limit of the current during normal conditions anFaultLimit = faultDataSet.RatedCurrent * ratingMultiplier; bnFaultLimit = faultDataSet.RatedCurrent * ratingMultiplier; cnFaultLimit = faultDataSet.RatedCurrent * ratingMultiplier; // Build a list of faulted cycles faultedCycles = new List <int>(); for (int i = 0; i < faultDataSet.Cycles.Count; i++) { CycleData cycle = faultDataSet.Cycles[i]; anFaultCycle = (cycle.AN.I.RMS >= anFaultLimit); bnFaultCycle = (cycle.BN.I.RMS >= bnFaultLimit); cnFaultCycle = (cycle.CN.I.RMS >= cnFaultLimit); if (anFaultCycle || bnFaultCycle || cnFaultCycle) { faultedCycles.Add(i); } } // Provide additional information about which // cycles were found to contain fault conditions faultDataSet.FaultedCycles = faultedCycles; return(faultedCycles.Count > 0); }
private static bool PrefaultMultiplierAndRatedCurrentTrigger(FaultLocationDataSet faultDataSet, string parameters) { Dictionary<string, string> parameterLookup; string parameterValue; double prefaultMultiplier; double ratingMultiplier; double anFaultLimit; double bnFaultLimit; double cnFaultLimit; List<int> faultedCycles; bool anFaultCycle; bool bnFaultCycle; bool cnFaultCycle; // If no cycles exist in the data set, there is no fault if (faultDataSet.Cycles.Count <= 0) return false; // Get parameters required for determining the existence of the fault parameterLookup = parameters.ParseKeyValuePairs(); if (!parameterLookup.TryGetValue("prefaultMultiplier", out parameterValue) || !double.TryParse(parameterValue, out prefaultMultiplier)) prefaultMultiplier = 5.0D; if (!parameterLookup.TryGetValue("ratingMultiplier", out parameterValue) || !double.TryParse(parameterValue, out ratingMultiplier)) ratingMultiplier = 2.0D; // Determine the upper limit of the current during normal conditions anFaultLimit = Math.Max(faultDataSet.Cycles[0].AN.I.RMS * prefaultMultiplier, faultDataSet.RatedCurrent * ratingMultiplier); bnFaultLimit = Math.Max(faultDataSet.Cycles[0].BN.I.RMS * prefaultMultiplier, faultDataSet.RatedCurrent * ratingMultiplier); cnFaultLimit = Math.Max(faultDataSet.Cycles[0].CN.I.RMS * prefaultMultiplier, faultDataSet.RatedCurrent * ratingMultiplier); // Build a list of faulted cycles faultedCycles = new List<int>(); for (int i = 0; i < faultDataSet.Cycles.Count; i++) { CycleData cycle = faultDataSet.Cycles[i]; anFaultCycle = (cycle.AN.I.RMS >= anFaultLimit); bnFaultCycle = (cycle.BN.I.RMS >= bnFaultLimit); cnFaultCycle = (cycle.CN.I.RMS >= cnFaultLimit); if (anFaultCycle || bnFaultCycle || cnFaultCycle) faultedCycles.Add(i); } // Provide additional information about which // cycles were found to contain fault conditions faultDataSet.FaultedCycles = faultedCycles; return faultedCycles.Count > 0; }
public static double[] Novosel(FaultLocationDataSet faultDataSet, string parameters) { ComplexNumber z; ComplexNumber[] voltages; ComplexNumber[] currents; ComplexNumber vPre; ComplexNumber iPre; ComplexNumber loadImpedance; z = GetNominalImpedance(faultDataSet); voltages = faultDataSet.Cycles.Select(cycle => GetFaultVoltage(cycle, faultDataSet.FaultType)).ToArray(); currents = faultDataSet.Cycles.Select(cycle => GetFaultCurrent(cycle, faultDataSet.FaultType)).ToArray(); vPre = GetFaultVoltage(faultDataSet.PrefaultCycle, faultDataSet.FaultType); iPre = GetFaultCurrent(faultDataSet.PrefaultCycle, faultDataSet.FaultType); loadImpedance = (vPre / iPre) - z; return(voltages.Zip(currents, (v, i) => { ComplexNumber sourceImpedance = faultDataSet.ZSrc; // TODO: Test to determine the effect of using -(v - vPre) / (i - iPre) instead if (IsNaN(sourceImpedance)) { sourceImpedance = (v - vPre) / (i - iPre); } ComplexNumber ab = (v / (z * i)) + (loadImpedance / z) + 1; ComplexNumber cd = (v / (z * i)) * (1 + (loadImpedance / z)); ComplexNumber ef = ((i - iPre) / (z * i)) * (1 + ((loadImpedance + sourceImpedance) / z)); double a = ab.Real, b = ab.Imaginary; double c = cd.Real, d = cd.Imaginary; double e = ef.Real, f = ef.Imaginary; double left = (a - ((e * b) / f)); double right = Math.Sqrt(left * left - 4.0D * (c - ((e * d) / f))); double m1 = (left + right) / 2.0D; double m2 = (left - right) / 2.0D; if (MinDistance(m1, 0.0D, 1.0D) < MinDistance(m2, 0.0D, 1.0D)) { return m1; } return m2; }) .Select(m => m * faultDataSet.LineDistance) .ToArray()); }
// True if current spikes upward by 300amps within a cycle while in a range between the //rated current and a user defined maximum current. private static bool RangeSpikeTrigger(FaultLocationDataSet faultDataSet, string parameters) { Dictionary <string, string> parameterLookup; string parameterValue; double ratingMultiplier; double anFaultLimit; double bnFaultLimit; double cnFaultLimit; List <int> faultedCycles; bool anFaultCycle; bool bnFaultCycle; bool cnFaultCycle; // If no cycles exist in the data set, there is no fault if (faultDataSet.Cycles.Count <= 0) { return(false); } // Get parameters required for determining the existence of the fault parameterLookup = parameters.ParseKeyValuePairs(); if (!parameterLookup.TryGetValue("ratingMultiplier", out parameterValue) || !double.TryParse(parameterValue, out ratingMultiplier)) { ratingMultiplier = 0.50D; } // Determine the upper limit of the current range during normal conditions anFaultLimit = faultDataSet.RatedCurrent * ratingMultiplier; bnFaultLimit = faultDataSet.RatedCurrent * ratingMultiplier; cnFaultLimit = faultDataSet.RatedCurrent * ratingMultiplier; for (int i = 0; i < faultDataSet.Cycles.Count; i++) { CycleData cycle = faultDataSet.Cycles[i]; if ((cycle.AN.I.RMS >= faultDataSet.RatedCurrent && cycle.AN.I.RMS <= anFaultLimit || cycle.BN.I.RMS >= faultDataSet.RatedCurrent && cycle.BN.I.RMS <= bnFaultLimit || cycle.CN.I.RMS >= faultDataSet.RatedCurrent && cycle.CN.I.RMS <= cnFaultLimit) && faultDataSet.Cycles[i + 1].AN.I.RMS > faultDataSet.Cycles[i].AN.I.RMS + 300D || faultDataSet.Cycles[i + 1].BN.I.RMS > faultDataSet.Cycles[i].BN.I.RMS + 300D || faultDataSet.Cycles[i + 1].CN.I.RMS > faultDataSet.Cycles[i].CN.I.RMS + 300D) { return(true); } } return(false); }
public static double[] Eriksson(FaultLocationDataSet faultDataSet, string parameters) { ComplexNumber z; ComplexNumber[] voltages; ComplexNumber[] currents; ComplexNumber iPre; ComplexNumber sourceImpedance; ComplexNumber remoteImpedance; sourceImpedance = faultDataSet.ZSrc; remoteImpedance = faultDataSet.ZRem; if (IsNaN(sourceImpedance) || IsNaN(remoteImpedance)) { return(null); } z = GetNominalImpedance(faultDataSet); voltages = faultDataSet.Cycles.Select(cycle => GetFaultVoltage(cycle, faultDataSet.FaultType)).ToArray(); currents = faultDataSet.Cycles.Select(cycle => GetFaultCurrent(cycle, faultDataSet.FaultType)).ToArray(); iPre = GetFaultCurrent(faultDataSet.PrefaultCycle, faultDataSet.FaultType); return(voltages.Zip(currents, (v, i) => { ComplexNumber ab = (v / (i * z)) + 1 + (remoteImpedance / z); ComplexNumber cd = (v / (i * z)) * ((remoteImpedance / z) + 1); ComplexNumber ef = ((i - iPre) / (i * z)) * (((sourceImpedance + remoteImpedance) / z) + 1); double a = ab.Real, b = ab.Imaginary; double c = cd.Real, d = cd.Imaginary; double e = ef.Real, f = ef.Imaginary; double left = (a - ((e * b) / f)); double right = Math.Sqrt(left * left - 4.0D * (c - ((e * d) / f))); double m1 = (left + right) / 2.0D; double m2 = (left - right) / 2.0D; if (MinDistance(m1, 0.0D, 1.0D) < MinDistance(m2, 0.0D, 1.0D)) { return m1; } return m2; }) .Select(m => m * faultDataSet.LineDistance) .ToArray()); }
public static double[] Reactance(FaultLocationDataSet faultDataSet, string parameters) { ComplexNumber nominalImpedance; nominalImpedance = GetNominalImpedance(faultDataSet); return(faultDataSet.Cycles .Select(cycleData => new { V = GetFaultVoltage(cycleData, faultDataSet.FaultType), I = GetFaultCurrent(cycleData, faultDataSet.FaultType), Z = nominalImpedance }) .Select(cycle => (cycle.V / cycle.I).Imaginary / cycle.Z.Imaginary) .Select(m => m * faultDataSet.LineDistance) .ToArray()); }
/// <summary> /// Double-ended algorithm for calculating the distance to a fault that was found in the <see cref="FaultLocationDataSet"/>. /// </summary> /// <param name="localFaultDataSet">The data set used to find the distance to the fault.</param> /// <param name="remoteFaultCycle">The cycle of data from the remote station used in the double-ended distance algorithm.</param> /// <param name="parameters">Extra parameters to the algorithm.</param> /// <returns>A set of distance calculations, one for each cycle of data in <paramref name="localFaultDataSet"/>.</returns> public static ComplexNumber[] DoubleEnded(FaultLocationDataSet localFaultDataSet, CycleData remoteFaultCycle, string parameters) { FaultType faultType; ComplexNumber vfs; ComplexNumber ifs; ComplexNumber z; faultType = localFaultDataSet.FaultType; vfs = GetDoubleEndedFaultVoltage(remoteFaultCycle, faultType); ifs = GetDoubleEndedFaultCurrent(remoteFaultCycle, faultType); z = localFaultDataSet.Z1; return(localFaultDataSet.Cycles .Select(cycleData => new { Vns = GetDoubleEndedFaultVoltage(cycleData, faultType), Ins = GetDoubleEndedFaultCurrent(cycleData, faultType) }) .Select(cycle => (cycle.Vns - vfs + z * ifs) / (z * (cycle.Ins + ifs))) .Select(m => m * localFaultDataSet.LineDistance) .ToArray()); }
public static double[] Takagi(FaultLocationDataSet faultDataSet, string parameters) { ComplexNumber z; ComplexNumber[] voltages; ComplexNumber[] currents; ComplexNumber iPre; z = GetNominalImpedance(faultDataSet); voltages = faultDataSet.Cycles.Select(cycle => GetFaultVoltage(cycle, faultDataSet.FaultType)).ToArray(); currents = faultDataSet.Cycles.Select(cycle => GetFaultCurrent(cycle, faultDataSet.FaultType)).ToArray(); iPre = GetFaultCurrent(faultDataSet.PrefaultCycle, faultDataSet.FaultType); return(voltages.Zip(currents, (v, i) => { ComplexNumber iSupConjugate = (i - iPre).Conjugate; return (v * iSupConjugate).Imaginary / (z * i * iSupConjugate).Imaginary; }) .Select(m => m * faultDataSet.LineDistance) .ToArray()); }
// Get the nominal impedance value to use in fault calculations, based on the fault type. private static ComplexNumber GetNominalImpedance(FaultLocationDataSet faultDataSet) { switch (faultDataSet.FaultType) { case FaultType.AN: case FaultType.BN: case FaultType.CN: return(faultDataSet.Zs); case FaultType.AB: case FaultType.BC: case FaultType.CA: case FaultType.ABG: case FaultType.BCG: case FaultType.CAG: case FaultType.ABC: return(faultDataSet.Z1); default: throw new ArgumentOutOfRangeException("faultDataSet", string.Format("Unknown fault type: {0}", faultDataSet.FaultType)); } }
/// <summary> /// Double-ended algorithm for calculating the distance to a fault that was found in the <see cref="FaultLocationDataSet"/>. /// </summary> /// <param name="localFaultDataSet">The data set used to find the distance to the fault.</param> /// <param name="remoteFaultCycle">The cycle of data from the remote station used in the double-ended distance algorithm.</param> /// <param name="parameters">Extra parameters to the algorithm.</param> /// <returns>A set of distance calculations, one for each cycle of data in <paramref name="localFaultDataSet"/>.</returns> public static ComplexNumber[] DoubleEnded(FaultLocationDataSet localFaultDataSet, CycleData remoteFaultCycle, string parameters) { FaultType faultType; ComplexNumber vfs; ComplexNumber ifs; ComplexNumber z; faultType = localFaultDataSet.FaultType; vfs = GetDoubleEndedFaultVoltage(remoteFaultCycle, faultType); ifs = GetDoubleEndedFaultCurrent(remoteFaultCycle, faultType); z = localFaultDataSet.Z1; return localFaultDataSet.Cycles .Select(cycleData => new { Vns = GetDoubleEndedFaultVoltage(cycleData, faultType), Ins = GetDoubleEndedFaultCurrent(cycleData, faultType) }) .Select(cycle => (cycle.Vns - vfs + z * ifs) / (z * (cycle.Ins + ifs))) .Select(m => m * localFaultDataSet.LineDistance) .ToArray(); }
public static double[] Takagi(FaultLocationDataSet faultDataSet, string parameters) { ComplexNumber z; ComplexNumber[] voltages; ComplexNumber[] currents; ComplexNumber iPre; z = GetNominalImpedance(faultDataSet); voltages = faultDataSet.Cycles.Select(cycle => GetFaultVoltage(cycle, faultDataSet.FaultType)).ToArray(); currents = faultDataSet.Cycles.Select(cycle => GetFaultCurrent(cycle, faultDataSet.FaultType)).ToArray(); iPre = GetFaultCurrent(faultDataSet.PrefaultCycle, faultDataSet.FaultType); return voltages.Zip(currents, (v, i) => { ComplexNumber iSupConjugate = (i - iPre).Conjugate; return (v * iSupConjugate).Imaginary / (z * i * iSupConjugate).Imaginary; }) .Select(m => m * faultDataSet.LineDistance) .ToArray(); }
public static double[] Novosel(FaultLocationDataSet faultDataSet, string parameters) { ComplexNumber z; ComplexNumber[] voltages; ComplexNumber[] currents; ComplexNumber vPre; ComplexNumber iPre; ComplexNumber loadImpedance; z = GetNominalImpedance(faultDataSet); voltages = faultDataSet.Cycles.Select(cycle => GetFaultVoltage(cycle, faultDataSet.FaultType)).ToArray(); currents = faultDataSet.Cycles.Select(cycle => GetFaultCurrent(cycle, faultDataSet.FaultType)).ToArray(); vPre = GetFaultVoltage(faultDataSet.PrefaultCycle, faultDataSet.FaultType); iPre = GetFaultCurrent(faultDataSet.PrefaultCycle, faultDataSet.FaultType); loadImpedance = (vPre / iPre) - z; return voltages.Zip(currents, (v, i) => { ComplexNumber sourceImpedance = faultDataSet.ZSrc; // TODO: Test to determine the effect of using -(v - vPre) / (i - iPre) instead if (IsNaN(sourceImpedance)) sourceImpedance = (v - vPre) / (i - iPre); ComplexNumber ab = (v / (z * i)) + (loadImpedance / z) + 1; ComplexNumber cd = (v / (z * i)) * (1 + (loadImpedance / z)); ComplexNumber ef = ((i - iPre) / (z * i)) * (1 + ((loadImpedance + sourceImpedance) / z)); double a = ab.Real, b = ab.Imaginary; double c = cd.Real, d = cd.Imaginary; double e = ef.Real, f = ef.Imaginary; double left = (a - ((e * b) / f)); double right = Math.Sqrt(left * left - 4.0D * (c - ((e * d) / f))); double m1 = (left + right) / 2.0D; double m2 = (left - right) / 2.0D; if (MinDistance(m1, 0.0D, 1.0D) < MinDistance(m2, 0.0D, 1.0D)) return m1; return m2; }) .Select(m => m * faultDataSet.LineDistance) .ToArray(); }
public static double[] Simple(FaultLocationDataSet faultDataSet, string parameters) { ComplexNumber nominalImpedance; nominalImpedance = GetNominalImpedance(faultDataSet); return faultDataSet.Cycles .Select(cycleData => new { V = GetFaultVoltage(cycleData, faultDataSet.FaultType), I = GetFaultCurrent(cycleData, faultDataSet.FaultType), Z = nominalImpedance }) .Select(cycle => (cycle.V.Magnitude / cycle.I.Magnitude) / cycle.Z.Magnitude) .Select(m => m * faultDataSet.LineDistance) .ToArray(); }
public static double[] Eriksson(FaultLocationDataSet faultDataSet, string parameters) { ComplexNumber z; ComplexNumber[] voltages; ComplexNumber[] currents; ComplexNumber iPre; ComplexNumber sourceImpedance; ComplexNumber remoteImpedance; sourceImpedance = faultDataSet.ZSrc; remoteImpedance = faultDataSet.ZRem; if (IsNaN(sourceImpedance) || IsNaN(remoteImpedance)) return null; z = GetNominalImpedance(faultDataSet); voltages = faultDataSet.Cycles.Select(cycle => GetFaultVoltage(cycle, faultDataSet.FaultType)).ToArray(); currents = faultDataSet.Cycles.Select(cycle => GetFaultCurrent(cycle, faultDataSet.FaultType)).ToArray(); iPre = GetFaultCurrent(faultDataSet.PrefaultCycle, faultDataSet.FaultType); return voltages.Zip(currents, (v, i) => { ComplexNumber ab = (v / (i * z)) + 1 + (remoteImpedance / z); ComplexNumber cd = (v / (i * z)) * ((remoteImpedance / z) + 1); ComplexNumber ef = ((i - iPre) / (i * z)) * (((sourceImpedance + remoteImpedance) / z) + 1); double a = ab.Real, b = ab.Imaginary; double c = cd.Real, d = cd.Imaginary; double e = ef.Real, f = ef.Imaginary; double left = (a - ((e * b) / f)); double right = Math.Sqrt(left * left - 4.0D * (c - ((e * d) / f))); double m1 = (left + right) / 2.0D; double m2 = (left - right) / 2.0D; if (MinDistance(m1, 0.0D, 1.0D) < MinDistance(m2, 0.0D, 1.0D)) return m1; return m2; }) .Select(m => m * faultDataSet.LineDistance) .ToArray(); }
public static double[] ModifiedTakagi(FaultLocationDataSet faultDataSet, string parameters) { ComplexNumber z; ComplexNumber[] voltages; ComplexNumber[] currents; ComplexNumber[] zeros; z = GetNominalImpedance(faultDataSet); voltages = faultDataSet.Cycles.Select(cycle => GetFaultVoltage(cycle, faultDataSet.FaultType)).ToArray(); currents = faultDataSet.Cycles.Select(cycle => GetFaultCurrent(cycle, faultDataSet.FaultType)).ToArray(); zeros = faultDataSet.Cycles.Select(cycle => 3 * CycleData.CalculateSequenceComponents(cycle.AN.I, cycle.BN.I, cycle.CN.I)[0]).ToArray(); return voltages.Zip(currents, (v, i) => new { V = v, I = i }) .Zip(zeros, (vi, zero) => { ComplexNumber v = vi.V; ComplexNumber i = vi.I; Angle t = (i / zero).Angle; ComplexNumber ejt = new ComplexNumber(t, 1.0D); return (v * zero.Conjugate * ejt).Imaginary / (z * i * zero.Conjugate * ejt).Imaginary; }) .Select(m => m * faultDataSet.LineDistance) .ToArray(); }
/// <summary> /// Populate known voltage and current data from PQDIF file. /// </summary> /// <param name="faultDataSet">Fault data set to be populated.</param> /// <param name="settings">Source parameters.</param> /// <param name="line">Associated XML event file definition.</param> public static void PopulateDataSet(FaultLocationDataSet faultDataSet, Dictionary<string, string> settings, Line line) { string fileName; if ((object)line == null) throw new ArgumentNullException("line"); if (!settings.TryGetValue("fileName", out fileName) || !File.Exists(fileName)) throw new ArgumentException("Parameters must define a valid \"fileName\" setting."); // Comtrade parsing will require a CFG file, make sure this exists... string directory = Path.GetDirectoryName(fileName) ?? string.Empty; string rootFileName = FilePath.GetFileNameWithoutExtension(fileName); string configurationFileName = Path.Combine(directory, rootFileName + ".cfg"); if (!File.Exists(configurationFileName)) throw new FileNotFoundException(string.Format("Associated CFG file \"{0}\" for COMTRADE data file does not exist - cannot parse COMTRADE file.", configurationFileName)); // Parse configuration file Schema schema = new Schema(configurationFileName); // Find <Channels> element in XML line definition XElement channels = line.ChannelsElement; if ((object)channels == null) throw new NullReferenceException("No \"<channels>\" element was found in event file definition - cannot load COMTRADE data file."); // Extract COMTRADE channel ID's for desired voltage and current elements IEnumerable<Tuple<int, int>> vaIndexes = GetValueIndex(schema, channels, "VA").ToList(); IEnumerable<Tuple<int, int>> vbIndexes = GetValueIndex(schema, channels, "VB").ToList(); IEnumerable<Tuple<int, int>> vcIndexes = GetValueIndex(schema, channels, "VC").ToList(); IEnumerable<Tuple<int, int>> iaIndexes = GetValueIndex(schema, channels, "IA").ToList(); IEnumerable<Tuple<int, int>> ibIndexes = GetValueIndex(schema, channels, "IB").ToList(); IEnumerable<Tuple<int, int>> icIndexes = GetValueIndex(schema, channels, "IC").ToList(); List<long> times = new List<long>(); List<double> vaValues = new List<double>(); List<double> vbValues = new List<double>(); List<double> vcValues = new List<double>(); List<double> iaValues = new List<double>(); List<double> ibValues = new List<double>(); List<double> icValues = new List<double>(); SampleRate sampleRate; ValidateIndexes("VA", vaIndexes); ValidateIndexes("VB", vbIndexes); ValidateIndexes("VC", vcIndexes); ValidateIndexes("IA", iaIndexes); ValidateIndexes("IB", ibIndexes); ValidateIndexes("IC", icIndexes); // Create a new COMTRADE file parser using (Parser parser = new Parser() { Schema = schema, FileName = fileName, InferTimeFromSampleRates = true }) { // Open COMTRADE data files parser.OpenFiles(); faultDataSet.Frequency = schema.NominalFrequency; sampleRate = schema.SampleRates.First(); if (sampleRate.Rate != 0) faultDataSet.SetSampleRates((int)(sampleRate.Rate / faultDataSet.Frequency)); // Read all COMTRADE records while (parser.ReadNext()) { times.Add(parser.Timestamp.Ticks); vaValues.Add(GetValue(parser, vaIndexes)); vbValues.Add(GetValue(parser, vbIndexes)); vcValues.Add(GetValue(parser, vcIndexes)); iaValues.Add(GetValue(parser, iaIndexes)); ibValues.Add(GetValue(parser, ibIndexes)); icValues.Add(GetValue(parser, icIndexes)); } } // Populate voltage data set faultDataSet.Voltages.AN.Times = times.ToArray(); faultDataSet.Voltages.AN.Measurements = vaValues.ToArray(); faultDataSet.Voltages.BN.Times = times.ToArray(); faultDataSet.Voltages.BN.Measurements = vbValues.ToArray(); faultDataSet.Voltages.CN.Times = times.ToArray(); faultDataSet.Voltages.CN.Measurements = vcValues.ToArray(); // Populate current data set faultDataSet.Currents.AN.Times = times.ToArray(); faultDataSet.Currents.AN.Measurements = iaValues.ToArray(); faultDataSet.Currents.BN.Times = times.ToArray(); faultDataSet.Currents.BN.Measurements = ibValues.ToArray(); faultDataSet.Currents.CN.Times = times.ToArray(); faultDataSet.Currents.CN.Measurements = icValues.ToArray(); }
private static FaultType Simple(FaultLocationDataSet faultDataSet, string parameters) { Dictionary <string, string> parameterLookup; string parameterValue; int largestCurrentIndex; CycleData largestCurrentCycle; FaultType faultType; double anCurrentRMS; double bnCurrentRMS; double cnCurrentRMS; double tolerance; double largestCurrentRMS; double faultCurrentRMS; bool anFault; bool bnFault; bool cnFault; parameterLookup = parameters.ParseKeyValuePairs(); largestCurrentIndex = faultDataSet.Cycles.GetLargestCurrentIndex(); largestCurrentCycle = faultDataSet.Cycles[largestCurrentIndex]; if ((object)largestCurrentCycle == null) { throw new InvalidOperationException("No cycles found in fault data set. Cannot calculate fault type."); } if (!parameterLookup.TryGetValue("tolerance", out parameterValue) || !double.TryParse(parameterValue, out tolerance)) { tolerance = 10.0D; } anCurrentRMS = largestCurrentCycle.AN.I.RMS; bnCurrentRMS = largestCurrentCycle.BN.I.RMS; cnCurrentRMS = largestCurrentCycle.CN.I.RMS; largestCurrentRMS = Math.Max(Math.Max(anCurrentRMS, bnCurrentRMS), cnCurrentRMS); faultCurrentRMS = largestCurrentRMS * tolerance * 0.01D; anFault = (anCurrentRMS >= faultCurrentRMS); bnFault = (bnCurrentRMS >= faultCurrentRMS); cnFault = (cnCurrentRMS >= faultCurrentRMS); if (anFault && bnFault && cnFault) { faultType = FaultType.ABC; } else if (anFault && bnFault) { faultType = FaultType.AB; } else if (bnFault && cnFault) { faultType = FaultType.BC; } else if (cnFault && anFault) { faultType = FaultType.CA; } else if (anFault) { faultType = FaultType.AN; } else if (bnFault) { faultType = FaultType.BN; } else { faultType = FaultType.CN; } return(faultType); }
private void InsertFaultDistances(FaultLocationDataSet faultDataSet, SqlCommand command, int cycleIndex, int cycleID) { IDbDataParameter algorithmParameter = command.CreateParameter(); IDbDataParameter distanceParameter = command.CreateParameter(); double distance; command.CommandText = "INSERT INTO FaultDistance (CycleID, Algorithm, Distance) VALUES (@cycleID, @algorithm, @distance)"; command.Parameters.AddWithValue("@cycleID", cycleID); command.Parameters.Add(algorithmParameter); command.Parameters.Add(distanceParameter); algorithmParameter.ParameterName = "@algorithm"; distanceParameter.ParameterName = "@distance"; foreach (KeyValuePair<string, double[]> faultDistances in faultDataSet.FaultDistances) { distance = faultDistances.Value[cycleIndex]; if (!double.IsNaN(distance)) { algorithmParameter.Value = faultDistances.Key; distanceParameter.Value = distance; command.ExecuteNonQuery(); } } command.Parameters.Clear(); }
private void InsertFaultData(Line line, FaultLocationDataSet faultDataSet, SqlCommand command, int fileGroupID) { int lineID; int lineDisturbanceID; int largestCurrentIndex; double faultDistance; // Get largest current index and median fault distance for that cycle largestCurrentIndex = faultDataSet.Cycles.GetLargestCurrentIndex(); faultDistance = faultDataSet.FaultDistances.Values .Select(distances => distances[largestCurrentIndex]) .OrderBy(distance => distance) .ToArray()[faultDataSet.FaultDistances.Count / 2]; // Get ID of the line associated with this fault data command.CommandText = "SELECT ID FROM Line WHERE FLEID = @fleID"; command.Parameters.AddWithValue("@fleID", line.ID); lineID = Convert.ToInt32(command.ExecuteScalar()); command.Parameters.Clear(); // Insert a disturbance record for this line command.CommandText = "INSERT INTO LineDisturbance (LineID, FileGroupID, FaultType, LargestCurrentIndex, " + "FaultDistance, IAMax, IBMax, ICMax, VAMin, VBMin, VCMin) VALUES (@lineID, @fileGroupID, " + "@faultType, @largestCurrentIndex, @faultDistance, @iaMax, @ibMax, @icMax, @vaMin, @vbMin, @vcMin)"; command.Parameters.AddWithValue("@lineID", lineID); command.Parameters.AddWithValue("@fileGroupID", fileGroupID); command.Parameters.AddWithValue("@faultType", faultDataSet.FaultType.ToString()); command.Parameters.AddWithValue("@largestCurrentIndex", largestCurrentIndex); command.Parameters.AddWithValue("@faultDistance", faultDistance); command.Parameters.AddWithValue("@iaMax", faultDataSet.Cycles.Max(cycle => cycle.AN.I.RMS)); command.Parameters.AddWithValue("@ibMax", faultDataSet.Cycles.Max(cycle => cycle.BN.I.RMS)); command.Parameters.AddWithValue("@icMax", faultDataSet.Cycles.Max(cycle => cycle.CN.I.RMS)); command.Parameters.AddWithValue("@vaMin", faultDataSet.Cycles.Min(cycle => cycle.AN.V.RMS)); command.Parameters.AddWithValue("@vbMin", faultDataSet.Cycles.Min(cycle => cycle.BN.V.RMS)); command.Parameters.AddWithValue("@vcMin", faultDataSet.Cycles.Min(cycle => cycle.CN.V.RMS)); command.ExecuteNonQuery(); command.Parameters.Clear(); // Get the ID of the disturbance record command.CommandText = "SELECT @@IDENTITY"; lineDisturbanceID = Convert.ToInt32(command.ExecuteScalar()); // Insert data for each cycle into the database for (int i = 0; i < faultDataSet.Cycles.Count; i++) InsertCycleData(faultDataSet, command, lineDisturbanceID, i); }
// Get the nominal impedance value to use in fault calculations, based on the fault type. private static ComplexNumber GetNominalImpedance(FaultLocationDataSet faultDataSet) { switch (faultDataSet.FaultType) { case FaultType.AN: case FaultType.BN: case FaultType.CN: return faultDataSet.Zs; case FaultType.AB: case FaultType.BC: case FaultType.CA: case FaultType.ABG: case FaultType.BCG: case FaultType.CAG: case FaultType.ABC: return faultDataSet.Z1; default: throw new ArgumentOutOfRangeException("faultDataSet", string.Format("Unknown fault type: {0}", faultDataSet.FaultType)); } }
private void InsertCycleData(FaultLocationDataSet faultDataSet, SqlCommand command, int lineDisturbanceID, int cycleIndex) { int cycleID; CycleData cycle; ComplexNumber[] sequenceVoltages; ComplexNumber[] sequenceCurrents; // Get the cycle data and sequence // components from the fault data set cycle = faultDataSet.Cycles[cycleIndex]; sequenceVoltages = CycleData.CalculateSequenceComponents(cycle.AN.V, cycle.BN.V, cycle.CN.V); sequenceCurrents = CycleData.CalculateSequenceComponents(cycle.AN.I, cycle.BN.I, cycle.CN.I); // Insert the cycle data into the database command.CommandText = "INSERT INTO Cycle (LineDisturbanceID, TimeStart, CycleIndex," + "ANVoltagePeak, ANVoltageRMS, ANVoltagePhase, ANCurrentPeak, ANCurrentRMS, ANCurrentPhase, " + "BNVoltagePeak, BNVoltageRMS, BNVoltagePhase, BNCurrentPeak, BNCurrentRMS, BNCurrentPhase, " + "CNVoltagePeak, CNVoltageRMS, CNVoltagePhase, CNCurrentPeak, CNCurrentRMS, CNCurrentPhase, " + "PositiveVoltageMagnitude, PositiveVoltageAngle, PositiveCurrentMagnitude, PositiveCurrentAngle, " + "NegativeVoltageMagnitude, NegativeVoltageAngle, NegativeCurrentMagnitude, NegativeCurrentAngle, " + "ZeroVoltageMagnitude, ZeroVoltageAngle, ZeroCurrentMagnitude, ZeroCurrentAngle) " + "VALUES (@lineDisturbanceID, @timeStart, @cycleIndex, " + "@anVoltagePeak, @anVoltageRMS, @anVoltagePhase, @anCurrentPeak, @anCurrentRMS, @anCurrentPhase, " + "@bnVoltagePeak, @bnVoltageRMS, @bnVoltagePhase, @bnCurrentPeak, @bnCurrentRMS, @bnCurrentPhase, " + "@cnVoltagePeak, @cnVoltageRMS, @cnVoltagePhase, @cnCurrentPeak, @cnCurrentRMS, @cnCurrentPhase, " + "@positiveVoltageMagnitude, @positiveVoltageAngle, @positiveCurrentMagnitude, @positiveCurrentAngle, " + "@negativeVoltageMagnitude, @negativeVoltageAngle, @negativeCurrentMagnitude, @negativeCurrentAngle, " + "@zeroVoltageMagnitude, @zeroVoltageAngle, @zeroCurrentMagnitude, @zeroCurrentAngle)"; command.Parameters.AddWithValue("@lineDisturbanceID", lineDisturbanceID); command.Parameters.AddWithValue("@timeStart", cycle.StartTime); command.Parameters.AddWithValue("@cycleIndex", cycleIndex); command.Parameters.AddWithValue("@anVoltagePeak", cycle.AN.V.Peak); command.Parameters.AddWithValue("@anVoltageRMS", cycle.AN.V.RMS); command.Parameters.AddWithValue("@anVoltagePhase", cycle.AN.V.Phase.ToDegrees()); command.Parameters.AddWithValue("@anCurrentPeak", cycle.AN.I.Peak); command.Parameters.AddWithValue("@anCurrentRMS", cycle.AN.I.RMS); command.Parameters.AddWithValue("@anCurrentPhase", cycle.AN.I.Phase.ToDegrees()); command.Parameters.AddWithValue("@bnVoltagePeak", cycle.BN.V.Peak); command.Parameters.AddWithValue("@bnVoltageRMS", cycle.BN.V.RMS); command.Parameters.AddWithValue("@bnVoltagePhase", cycle.BN.V.Phase.ToDegrees()); command.Parameters.AddWithValue("@bnCurrentPeak", cycle.BN.I.Peak); command.Parameters.AddWithValue("@bnCurrentRMS", cycle.BN.I.RMS); command.Parameters.AddWithValue("@bnCurrentPhase", cycle.BN.I.Phase.ToDegrees()); command.Parameters.AddWithValue("@cnVoltagePeak", cycle.CN.V.Peak); command.Parameters.AddWithValue("@cnVoltageRMS", cycle.CN.V.RMS); command.Parameters.AddWithValue("@cnVoltagePhase", cycle.CN.V.Phase.ToDegrees()); command.Parameters.AddWithValue("@cnCurrentPeak", cycle.CN.I.Peak); command.Parameters.AddWithValue("@cnCurrentRMS", cycle.CN.I.RMS); command.Parameters.AddWithValue("@cnCurrentPhase", cycle.CN.I.Phase.ToDegrees()); command.Parameters.AddWithValue("@positiveVoltageMagnitude", sequenceVoltages[1].Magnitude); command.Parameters.AddWithValue("@positiveVoltageAngle", sequenceVoltages[1].Angle.ToDegrees()); command.Parameters.AddWithValue("@negativeVoltageMagnitude", sequenceVoltages[2].Magnitude); command.Parameters.AddWithValue("@negativeVoltageAngle", sequenceVoltages[2].Angle.ToDegrees()); command.Parameters.AddWithValue("@zeroVoltageMagnitude", sequenceVoltages[0].Magnitude); command.Parameters.AddWithValue("@zeroVoltageAngle", sequenceVoltages[0].Angle.ToDegrees()); command.Parameters.AddWithValue("@positiveCurrentMagnitude", sequenceCurrents[1].Magnitude); command.Parameters.AddWithValue("@positiveCurrentAngle", sequenceCurrents[1].Angle.ToDegrees()); command.Parameters.AddWithValue("@negativeCurrentMagnitude", sequenceCurrents[2].Magnitude); command.Parameters.AddWithValue("@negativeCurrentAngle", sequenceCurrents[2].Angle.ToDegrees()); command.Parameters.AddWithValue("@zeroCurrentMagnitude", sequenceCurrents[0].Magnitude); command.Parameters.AddWithValue("@zeroCurrentAngle", sequenceCurrents[0].Angle.ToDegrees()); command.ExecuteNonQuery(); command.Parameters.Clear(); // Get the ID of the cycle data that was entered command.CommandText = "SELECT @@IDENTITY"; cycleID = Convert.ToInt32(command.ExecuteScalar()); // Insert fault distances calculated for this cycle InsertFaultDistances(faultDataSet, command, cycleIndex, cycleID); }
// True if current spikes upward by 300amps within a cycle while in a range between the //rated current and a user defined maximum current. private static bool RangeSpikeTrigger(FaultLocationDataSet faultDataSet, string parameters) { Dictionary<string, string> parameterLookup; string parameterValue; double ratingMultiplier; double anFaultLimit; double bnFaultLimit; double cnFaultLimit; List<int> faultedCycles; bool anFaultCycle; bool bnFaultCycle; bool cnFaultCycle; // If no cycles exist in the data set, there is no fault if (faultDataSet.Cycles.Count <= 0) return false; // Get parameters required for determining the existence of the fault parameterLookup = parameters.ParseKeyValuePairs(); if (!parameterLookup.TryGetValue("ratingMultiplier", out parameterValue) || !double.TryParse(parameterValue, out ratingMultiplier)) ratingMultiplier = 0.50D; // Determine the upper limit of the current range during normal conditions anFaultLimit = faultDataSet.RatedCurrent * ratingMultiplier; bnFaultLimit = faultDataSet.RatedCurrent * ratingMultiplier; cnFaultLimit = faultDataSet.RatedCurrent * ratingMultiplier; for (int i = 0; i < faultDataSet.Cycles.Count; i++) { CycleData cycle = faultDataSet.Cycles[i]; if ((cycle.AN.I.RMS >= faultDataSet.RatedCurrent && cycle.AN.I.RMS <= anFaultLimit || cycle.BN.I.RMS >= faultDataSet.RatedCurrent && cycle.BN.I.RMS <= bnFaultLimit || cycle.CN.I.RMS >= faultDataSet.RatedCurrent && cycle.CN.I.RMS <= cnFaultLimit) && faultDataSet.Cycles[i + 1].AN.I.RMS > faultDataSet.Cycles[i].AN.I.RMS + 300D || faultDataSet.Cycles[i + 1].BN.I.RMS > faultDataSet.Cycles[i].BN.I.RMS + 300D || faultDataSet.Cycles[i + 1].CN.I.RMS > faultDataSet.Cycles[i].CN.I.RMS + 300D) return true; } return false; }