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 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(); }
// Attempts to execute fault type algorithm and processes errors if they occur. private FaultType ExecuteFaultTypeAlgorithm(FaultTypeAlgorithm algorithm, FaultLocationDataSet faultDataSet, string parameters) { try { return(algorithm(faultDataSet, parameters)); } catch (Exception ex) { OnProcessException(new Exception(string.Format("Error executing fault type algorithm: {0}", ex.Message), ex)); return(FaultType.None); } }
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()); }
private bool TryExecute(FaultLocationAlgorithm faultLocationAlgorithm, FaultLocationDataSet faultLocationDataSet, out double[] distances) { try { distances = faultLocationAlgorithm(faultLocationDataSet, null); } catch { distances = null; } return((object)distances != null); }
/// <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> public static void PopulateDataSet(FaultLocationDataSet faultDataSet, Dictionary <string, string> settings) { string fileName; List <ObservationRecord> observationRecords; ObservationRecord faultObservation; ChannelInstance[] anChannels; ChannelInstance[] bnChannels; ChannelInstance[] cnChannels; if (!settings.TryGetValue("fileName", out fileName) || !File.Exists(fileName)) { throw new ArgumentException("Parameters must define a valid \"fileName\" setting."); } // Parse PQDif File Data using (LogicalParser logicalParser = new LogicalParser(fileName)) { observationRecords = new List <ObservationRecord>(); logicalParser.Open(); while (logicalParser.HasNextObservationRecord()) { observationRecords.Add(logicalParser.NextObservationRecord()); } } // Get the first observation record that contains six wave forms. // Assume that this observation record will has both voltage and // current wave forms for each of the three phases faultObservation = observationRecords.FirstOrDefault(observation => observation.ChannelInstances.Count(channel => channel.Definition.QuantityTypeID == QuantityType.WaveForm) >= 6); if ((object)faultObservation != null) { // Get the voltage and current wave forms for each of the six phase types IEnumerable <ChannelInstance> waveForms = faultObservation.ChannelInstances.Where(channel => channel.Definition.QuantityTypeID == QuantityType.WaveForm).ToList(); anChannels = waveForms.Where(channel => channel.Definition.Phase == Phase.AN).ToArray(); bnChannels = waveForms.Where(channel => channel.Definition.Phase == Phase.BN).ToArray(); cnChannels = waveForms.Where(channel => channel.Definition.Phase == Phase.CN).ToArray(); // Attempt to fill in fault data for each of the three phase types FillFaultData(faultDataSet.Voltages.AN, faultDataSet.Currents.AN, anChannels); FillFaultData(faultDataSet.Voltages.BN, faultDataSet.Currents.BN, bnChannels); FillFaultData(faultDataSet.Voltages.CN, faultDataSet.Currents.CN, cnChannels); // Set the frequency in the data set to the nominal frequency of the system faultDataSet.Frequency = faultObservation.Settings.NominalFrequency; } }
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); } }
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()); }
private void ExecuteFaultLocationAlgorithm(double lineLength, ComplexNumber nominalImpedance, MappingNode local, MappingNode remote) { FaultLocationDataSet faultLocationDataSet; CycleData remoteCycle; ComplexNumber[] curve; faultLocationDataSet = new FaultLocationDataSet(); faultLocationDataSet.FaultType = local.FaultType; faultLocationDataSet.LineDistance = lineLength; faultLocationDataSet.Z1 = nominalImpedance; local.CycleDataGroup.PushDataTo(faultLocationDataSet.Cycles); remoteCycle = GetCycleAt(remote.CycleDataGroup, remote.Fault.CalculationCycle - remote.StartSample); curve = FaultLocationAlgorithms.DoubleEnded(faultLocationDataSet, remoteCycle, string.Empty); ExtractDistanceVectors(local, curve); }
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()); }
/// <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()); }
// 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 FaultLocationDataSet GetFaultDataSet(string fileName, Line line) { string extension = FilePath.GetExtension(fileName).ToLowerInvariant().Trim(); FaultLocationDataSet faultDataSet = new FaultLocationDataSet() { PositiveImpedance = new ComplexNumber(line.R1, line.X1), ZeroImpedance = new ComplexNumber(line.R0, line.X0), LineDistance = line.Length }; StringBuilder parameters = new StringBuilder(); // TODO: Load other needed specific associated parameters based on file / line information once all meta-data is known and defined parameters.AppendFormat("fileName={0}; ", fileName); parameters.Append("dataSourceType="); // Process files based on file extension switch (extension) { case ".pqd": parameters.Append("PQDIF"); break; case ".d00": case ".dat": parameters.Append("Comtrade"); break; default: throw new InvalidOperationException(string.Format("Unknown file extension encountered: \"{0}\" - cannot parse file.", extension)); } // Load data sets based on specified parameters LoadDataSets(faultDataSet, parameters.ToString(), line); return(faultDataSet); }
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); }
/// <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(); }
// Static Methods // Load voltage and current data set based on connection string parameters private static void LoadDataSets(FaultLocationDataSet faultDataSet, string parameters, Line line) { if (string.IsNullOrEmpty(parameters)) { throw new ArgumentNullException("parameters"); } Dictionary <string, string> settings = parameters.ParseKeyValuePairs(); string dataSourceType; if (!settings.TryGetValue("dataSourceType", out dataSourceType) || string.IsNullOrWhiteSpace(dataSourceType)) { throw new ArgumentException("Parameters must define a \"dataSourceType\" setting."); } switch (dataSourceType.ToLowerInvariant().Trim()) { case "pqdif": PQDIFLoader.PopulateDataSet(faultDataSet, settings); break; case "comtrade": COMTRADELoader.PopulateDataSet(faultDataSet, settings, line); break; default: throw new ArgumentOutOfRangeException("parameters", string.Format("Cannot parse \"{0}\" data source type - format is undefined.", dataSourceType)); } if ((object)faultDataSet.Voltages.AN.Measurements == null || faultDataSet.Voltages.AN.Measurements.Length == 0) { throw new InvalidOperationException("Missing AN voltage. Cannot calculate fault location without line-to-neutral voltages."); } if ((object)faultDataSet.Voltages.BN.Measurements == null || faultDataSet.Voltages.BN.Measurements.Length == 0) { throw new InvalidOperationException("Missing BN voltage. Cannot calculate fault location without line-to-neutral voltages."); } if ((object)faultDataSet.Voltages.CN.Measurements == null || faultDataSet.Voltages.CN.Measurements.Length == 0) { throw new InvalidOperationException("Missing CN voltage. Cannot calculate fault location without line-to-neutral voltages."); } if ((object)faultDataSet.Currents.AN.Measurements == null || faultDataSet.Currents.AN.Measurements.Length == 0) { throw new InvalidOperationException("Missing AN current. Cannot calculate fault location without line-to-neutral currents."); } if ((object)faultDataSet.Currents.BN.Measurements == null || faultDataSet.Currents.BN.Measurements.Length == 0) { throw new InvalidOperationException("Missing BN current. Cannot calculate fault location without line-to-neutral currents."); } if ((object)faultDataSet.Currents.CN.Measurements == null || faultDataSet.Currents.CN.Measurements.Length == 0) { throw new InvalidOperationException("Missing CN current. Cannot calculate fault location without line-to-neutral currents."); } ValidateSampleRates(faultDataSet.Frequency, faultDataSet.Voltages); ValidateSampleRates(faultDataSet.Frequency, faultDataSet.Currents); faultDataSet.Cycles.Populate(faultDataSet.Frequency, faultDataSet.Voltages, faultDataSet.Currents); }
public override void Initialize(MeterDataSet meterDataSet) { DataGroup dataGroup; VIDataGroup viDataGroup; VICycleDataGroup viCycleDataGroup; List <Fault> faults; List <FaultLocationAlgorithm> faultLocationAlgorithms; FaultLocationDataSet faultLocationDataSet; ImpedanceExtractor impedanceExtractor; FaultCurveGenerator faultCurveGenerator; CycleDataResource cycleDataResource; bool?faultDetectionLogicResult; bool defaultFaultDetectionLogicResult; bool faultValidationLogicResult; Stopwatch stopwatch; stopwatch = new Stopwatch(); cycleDataResource = meterDataSet.GetResource <CycleDataResource>(); faultLocationAlgorithms = GetFaultLocationAlgorithms(m_dbAdapterContainer.GetAdapter <FaultLocationInfoDataContext>()); Log.Info(string.Format("Executing fault location analysis on {0} events.", cycleDataResource.DataGroups.Count)); for (int i = 0; i < cycleDataResource.DataGroups.Count; i++) { dataGroup = cycleDataResource.DataGroups[i]; viDataGroup = cycleDataResource.VIDataGroups[i]; viCycleDataGroup = cycleDataResource.VICycleDataGroups[i]; // Engineering reasonableness checks Log.Debug("Checking for engineering reasonableness..."); try { stopwatch.Restart(); if (!IsReasonable(dataGroup, viCycleDataGroup)) { continue; } } finally { Log.Debug(stopwatch.Elapsed); } // Break into faults and segments Log.Debug("Classifying data into faults and segments..."); try { stopwatch.Restart(); faults = DetectFaults(viDataGroup, viCycleDataGroup); if (faults.Count > 0) { ClassifyFaults(faults, dataGroup, viCycleDataGroup); } } finally { Log.Debug(stopwatch.Elapsed); } // Check the fault detection logic and the default fault detection logic faultDetectionLogicResult = CheckFaultDetectionLogic(meterDataSet, dataGroup); defaultFaultDetectionLogicResult = CheckDefaultFaultDetectionLogic(faults); // If the fault detection logic detects a fault and the default // logic does not agree, treat the whole waveform as a fault if (faultDetectionLogicResult == true && !defaultFaultDetectionLogicResult) { faults.Add(new Fault() { StartSample = 0, EndSample = dataGroup[0].DataPoints.Count - 1 }); ClassifyFaults(faults, dataGroup, viCycleDataGroup); } // Create the fault location data set and begin populating // the properties necessary for calculating fault location faultLocationDataSet = new FaultLocationDataSet(); faultLocationDataSet.LineDistance = dataGroup.Line.Length; faultLocationDataSet.PrefaultCycle = FirstCycle(viCycleDataGroup); // Extract impedances from the database // and into the fault location data set impedanceExtractor = new ImpedanceExtractor(); impedanceExtractor.FaultLocationDataSet = faultLocationDataSet; impedanceExtractor.FaultLocationInfo = m_dbAdapterContainer.GetAdapter <FaultLocationInfoDataContext>(); impedanceExtractor.Meter = meterDataSet.Meter; impedanceExtractor.Line = dataGroup.Line; if (impedanceExtractor.TryExtractImpedances()) { // Generate fault curves for fault analysis Log.Debug("Generating fault curves..."); stopwatch.Restart(); faultCurveGenerator = new FaultCurveGenerator(); faultCurveGenerator.SamplesPerCycle = (int)(viDataGroup.VA.SampleRate / m_systemFrequency); faultCurveGenerator.CycleDataGroup = viCycleDataGroup; faultCurveGenerator.Faults = faults; faultCurveGenerator.FaultLocationDataSet = faultLocationDataSet; faultCurveGenerator.FaultLocationAlgorithms = faultLocationAlgorithms; faultCurveGenerator.GenerateFaultCurves(); Log.Debug(stopwatch.Elapsed); // Gather additional info about each fault // based on the results of the above analysis foreach (Fault fault in faults) { PopulateFaultInfo(fault, dataGroup, viCycleDataGroup); } } // Create a fault group and add it to the lookup table faultValidationLogicResult = CheckFaultValidationLogic(faults); m_faultLookup.Add(dataGroup, new FaultGroup(faults, faultDetectionLogicResult, defaultFaultDetectionLogicResult, faultValidationLogicResult)); } }