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 CycleData GetCycleAt(VICycleDataGroup viCycleDataGroup, int index) { CycleData cycle = new CycleData(); Cycle[] cycles = { cycle.AN.V, cycle.BN.V, cycle.CN.V, cycle.AN.I, cycle.BN.I, cycle.CN.I }; CycleDataGroup[] cycleDataGroups = { viCycleDataGroup.VA, viCycleDataGroup.VB, viCycleDataGroup.VC, viCycleDataGroup.IA, viCycleDataGroup.IB, viCycleDataGroup.IC }; for (int i = 0; i < cycles.Length; i++) { cycles[i].RMS = cycleDataGroups[i].RMS[index].Value; cycles[i].Phase = cycleDataGroups[i].Phase[index].Value; cycles[i].Peak = cycleDataGroups[i].Peak[index].Value; cycles[i].Error = cycleDataGroups[i].Error[index].Value; } return(cycle); }
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); }
// Get teh current to use in fault calculations for this cycle, based on the fault type. private static ComplexNumber GetDoubleEndedFaultCurrent(CycleData cycle, FaultType faultType) { ComplexNumber[] sequenceComponents; sequenceComponents = CycleData.CalculateSequenceComponents(cycle.AN.I, cycle.BN.I, cycle.CN.I); if (faultType == FaultType.ABC || faultType == FaultType.ABCG) { return(sequenceComponents[1]); } return(sequenceComponents[2]); }
// 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); }
/// <summary> /// Get the current to use in fault calculations for this cycle, based on the fault type. /// </summary> /// <param name="cycle">The cycle of data from which to derive the fault current.</param> /// <param name="viFaultType">The fault type used to determine which current to use.</param> /// <returns>The current to use in fault calculations.</returns> public static ComplexNumber GetFaultCurrent(CycleData cycle, FaultType viFaultType) { switch (viFaultType) { case FaultType.AN: return(cycle.AN.I.Complex); case FaultType.BN: return(cycle.BN.I.Complex); case FaultType.CN: return(cycle.CN.I.Complex); case FaultType.AB: case FaultType.ABG: return(cycle.AN.I.Complex - cycle.BN.I.Complex); case FaultType.BC: case FaultType.BCG: return(cycle.BN.I.Complex - cycle.CN.I.Complex); case FaultType.CA: case FaultType.CAG: return(cycle.CN.I.Complex - cycle.AN.I.Complex); case FaultType.ABC: case FaultType.ABCG: if (cycle.AN.I.Error < cycle.BN.I.Error && cycle.AN.I.Error < cycle.CN.I.Error) { return(cycle.AN.I.Complex); } if (cycle.BN.I.Error < cycle.CN.I.Error) { return(cycle.BN.I.Complex); } return(cycle.CN.I.Complex); default: throw new ArgumentOutOfRangeException("viFaultType"); } }
/// <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 void PushDataTo(CycleDataSet cycleDataSet) { FaultAlgorithms.CycleData cycleData; Cycle[] cycles; CycleDataGroup[] cycleDataGroups; cycleDataGroups = new CycleDataGroup[] { VA, VB, VC, IA, IB, IC }; cycles = new Cycle[cycleDataGroups.Length]; for (int i = 0; i < VA.ToDataGroup().Samples; i++) { cycleData = new FaultAlgorithms.CycleData(); cycles[0] = cycleData.AN.V; cycles[1] = cycleData.BN.V; cycles[2] = cycleData.CN.V; cycles[3] = cycleData.AN.I; cycles[4] = cycleData.BN.I; cycles[5] = cycleData.CN.I; for (int j = 0; j < cycles.Length; j++) { if (cycleDataGroups[j] == null) { continue; } cycles[j].RMS = cycleDataGroups[j].RMS[i].Value; cycles[j].Phase = cycleDataGroups[j].Phase[i].Value; cycles[j].Peak = cycleDataGroups[j].Peak[i].Value; cycles[j].Error = cycleDataGroups[j].Error[i].Value; } cycleDataSet[i] = cycleData; } }
public void PushDataTo(CycleDataSet cycleDataSet) { CycleData cycleData; Cycle[] cycles; CycleDataGroup[] cycleDataGroups; cycleDataGroups = new CycleDataGroup[] { VA, VB, VC, IA, IB, IC }; cycles = new Cycle[cycleDataGroups.Length]; for (int i = 0; i < VA.ToDataGroup().Samples; i++) { cycleData = new CycleData(); cycles[0] = cycleData.AN.V; cycles[1] = cycleData.BN.V; cycles[2] = cycleData.CN.V; cycles[3] = cycleData.AN.I; cycles[4] = cycleData.BN.I; cycles[5] = cycleData.CN.I; for (int j = 0; j < cycles.Length; j++) { cycles[j].RMS = cycleDataGroups[j].RMS[i].Value; cycles[j].Phase = cycleDataGroups[j].Phase[i].Value; cycles[j].Peak = cycleDataGroups[j].Peak[i].Value; cycles[j].Error = cycleDataGroups[j].Error[i].Value; } cycleDataSet[i] = cycleData; } }
// Get the voltage to use in fault calculations for this cycle, based on the fault type. private static ComplexNumber GetFaultVoltage(CycleData cycle, FaultType viFaultType) { switch (viFaultType) { case FaultType.AN: return cycle.AN.V.Complex; case FaultType.BN: return cycle.BN.V.Complex; case FaultType.CN: return cycle.CN.V.Complex; case FaultType.AB: case FaultType.ABG: return cycle.AN.V.Complex - cycle.BN.V.Complex; case FaultType.BC: case FaultType.BCG: return cycle.BN.V.Complex - cycle.CN.V.Complex; case FaultType.CA: case FaultType.CAG: return cycle.CN.V.Complex - cycle.AN.V.Complex; case FaultType.ABC: if (cycle.AN.I.Error < cycle.BN.I.Error && cycle.AN.I.Error < cycle.CN.I.Error) return cycle.AN.V.Complex; if (cycle.BN.I.Error < cycle.CN.I.Error) return cycle.BN.V.Complex; return cycle.CN.V.Complex; default: throw new ArgumentOutOfRangeException("viFaultType"); } }
// Get the voltage used in double-ended fault calculations for this cycle, based on the fault type. private static ComplexNumber GetDoubleEndedFaultVoltage(CycleData cycle, FaultType faultType) { ComplexNumber[] sequenceComponents; sequenceComponents = CycleData.CalculateSequenceComponents(cycle.AN.V, cycle.BN.V, cycle.CN.V); if (faultType == FaultType.ABC) return sequenceComponents[1]; return sequenceComponents[2]; }
/// <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()); }