/// <summary> /// Gets information on voltage drop across a <see cref="ITwoTerminal"/> component /// </summary> /// <param name="component"></param> /// <param name="voltageBA">If true, voltage drop is calculated from <see cref="ITwoTerminal.TerminalB"/> to /// <returns></returns> public ISignalInformation Get(ITwoTerminal component, bool voltageBA = true) => // Depending on requested voltage drop direction voltageBA? // Get voltage drop from node A to node B Get(component.TerminalA.NodeIndex, component.TerminalB.NodeIndex) : // Get voltage drop from node B to node A Get(component.TerminalB.NodeIndex, component.TerminalA.NodeIndex);
/// <summary> /// Gets voltage drop across a <see cref="ITwoTerminal"/> component or null if unsuccessful and assigns it to /// <paramref name="voltage"/>. Returns true on success, false otherwise. /// </summary> /// <param name="component"></param> /// <param name="voltageBA">If true, voltage drop is calculated from <see cref="ITwoTerminal.TerminalB"/> to /// <param name="voltage"></param> /// <returns></returns> public bool TryGet(ITwoTerminal component, out IPhasorDomainSignal voltage, bool voltageBA = true) => // Depending on requested voltage drop direction voltageBA? // Get voltage drop from node A to node B TryGet(component.TerminalA.NodeIndex, component.TerminalB.NodeIndex, out voltage) : // Get voltage drop from node B to node A TryGet(component.TerminalB.NodeIndex, component.TerminalA.NodeIndex, out voltage);
/// <summary> /// Caches the <paramref name="signal"/> as well as its negated copy (with inverted indexes) /// </summary> /// <param name="signal"></param> /// <param name="component"></param> /// <param name="voltageBA">If true, it means that current was calculated for voltage drop from /// <see cref="ITwoTerminal.TerminalA"/> (reference) to <see cref="ITwoTerminal.TerminalB"/>, if false it means that /// that direction was reversed</param> protected void CacheCurrent(TSignal signal, ITwoTerminal component, bool voltageBA) { // Cache the original CacheHelper(signal, component, voltageBA); // And cache the reversed one CacheHelper(CopyAndNegate(signal), component, !voltageBA); }
/// <summary> /// If cache does not contain an entry with key given by <paramref name="component"/> and /// <paramref name="voltageBA"/>, caches <paramref name="signal"/>, otherwise doesn't do anything /// </summary> /// <param name="signal"></param> /// <param name="component"></param> /// <param name="voltageBA">If true, it means that current was calculated for voltage drop from /// <see cref="ITwoTerminal.TerminalA"/> (reference) to <see cref="ITwoTerminal.TerminalB"/>, if false it means that /// that direction was reversed</param> private void CacheHelper(TSignal signal, ITwoTerminal component, bool voltageBA) { if (!_Cache.ContainsKey(new Tuple <ITwoTerminal, bool>(component, voltageBA))) { _Cache.Add(new Tuple <ITwoTerminal, bool>(component, voltageBA), Tuple.Create( signal, IoC.Resolve <ISignalInformation>(signal, IoC.Resolve <ICommonSignalDescriptions>().Current))); } }
/// <summary> /// Attempts to obtain a current for some <see cref="ITwoTerminal"/> <paramref name="component"/>, if not successful /// returns null /// </summary> /// <param name="component"></param> /// <param name="voltageBA">If true, voltage used to calculate the current is taken from <see cref="ITwoTerminal.TerminalA"/> /// (reference node) to <see cref="ITwoTerminal.TerminalB"/>, if false the direction is reversed</param> /// <returns></returns> private ISignalInformation GetStandardTwoTerminalCurrent(ITwoTerminal component, bool voltageBA) { // Check if the current may be obtained from cache if (TryEnableCurrent(component, voltageBA) && // If so, try to get it from the cache (this condition should always be true if the previous one is true) _Cache.TryGetValue(new Tuple <ITwoTerminal, bool>(component, voltageBA), out var currentPackage)) { return(currentPackage.Item2); } else { return(null); } }
/// <summary> /// Checks if current through <paramref name="component"/> can be obtained from cache, if not performs /// all possible actions to create it and cache it. Returns true if, at the end of the method call, the current may be /// obtained from <see cref="_PassiveCurrentsCache"/>, false otherwise. /// </summary> /// <param name="component">Element for which the current is considered</param> /// <param name="voltageBA">If true, voltage used to calculate the current is taken from <see cref="ITwoTerminal.TerminalA"/> /// (reference node) to <see cref="ITwoTerminal.TerminalB"/>, if false the direction is reversed</param> /// <returns></returns> protected bool TryEnableCurrent(ITwoTerminal component, bool voltageBA) { // If there was a cached entry already return it if (_Cache.ContainsKey(new Tuple <ITwoTerminal, bool>(component, voltageBA))) { return(true); } // Try to construct an entry else if (TryConstructCurrent(component, voltageBA, out var current)) { CacheCurrent(current, component, voltageBA); return(true); } return(false); }
/// <summary> /// Attempts to obtain a current for some <see cref="ITwoTerminal"/> <paramref name="component"/> /// </summary> /// <param name="component"></param> /// <param name="voltageBA">If true, voltage used to calculate the current is taken from <see cref="ITwoTerminal.TerminalA"/> /// (reference node) to <see cref="ITwoTerminal.TerminalB"/>, if false the direction is reversed</param> /// <param name="current"></param> /// <returns></returns> private bool TryGetStandardTwoTerminalCurrent(ITwoTerminal component, bool voltageBA, out ITimeDomainSignal current) { // Check if the current may be obtained from cache if (TryEnableCurrent(component, voltageBA) && // If so, try to get it from the cache (this condition should always be true if the previous one is true) _Cache.TryGetValue(new Tuple <ITwoTerminal, bool>(component, voltageBA), out var currentPackage)) { current = currentPackage.Item1; return(true); } else { current = null; return(false); } }
/// <summary> /// Tries to construct a current for <paramref name="element"/>, returns true on success /// </summary> /// <param name="element"></param> /// <param name="voltageBA">If true, it means that current was calculated for voltage drop from /// <see cref="ITwoTerminal.TerminalA"/> (reference) to <see cref="ITwoTerminal.TerminalB"/>, if false it means that /// that direction was reversed</param> /// <param name="current">Current constructed if successful, null otherwise</param> /// <returns></returns> protected override bool TryConstructCurrent(ITwoTerminal element, bool voltageBA, out IPhasorDomainSignal current) { // Try to get voltage drop across the element if (_VoltageDrops.TryGet(element, out var voltageDrop, voltageBA)) { // If successful, create a new current signal based on it, cache it //current = IoC.Resolve<IPhasorDomainSignal>( // GetPassiveTwoTerminalDCCurrent(voltageDrop, element), // GetPassiveTwoTerminalACCurrentPhasors(voltageDrop, element)); current = IoC.Resolve <IPhasorDomainSignal>( voltageDrop.Phasors.Select((x) => x.Value * element.GetAdmittance(x.Key.Frequency))); // And return success return(true); }
/// <summary> /// Tries to construct a current for <paramref name="element"/>, returns true on success /// </summary> /// <param name="element"></param> /// <param name="voltageBA">If true, it means that current was calculated for voltage drop from /// <see cref="ITwoTerminal.TerminalA"/> (reference) to <see cref="ITwoTerminal.TerminalB"/>, if false it means that /// that direction was reversed</param> /// <param name="current">Current constructed if successful, null otherwise</param> /// <returns></returns> protected override bool TryConstructCurrent(ITwoTerminal element, bool voltageBA, out ITimeDomainSignal current) { // Try to get voltage drop across the element if (_VoltageDrops.TryGet(element, out var voltageDrop, voltageBA)) { // If successful, create a new current signal based on it, cache it var result = IoC.Resolve <ITimeDomainSignalMutable>(voltageDrop.Samples, voltageDrop.TimeStep, voltageDrop.StartTime); // Get the minimum frequency - it is needed for capacitor waveform shifting, check if there are any waveforms, if not // just assign 0 (technically no waveforms result in a zero wave, which is DC) var minACFrequency = voltageDrop.Waveforms.Count > 0 ? voltageDrop.Waveforms.Keys.Min((x) => x.Frequency) : 0; // Current is composed of each voltage waveform times admittance of the element foreach (var waveform in voltageDrop.Waveforms) { // Get magnitude of element's admittance var admittanceMagnitude = element.GetAdmittance(waveform.Key.Frequency).Magnitude; // Current waveform is the product of voltage waveforrm and magnitude var finalWaveform = waveform.Value.Select((x) => x * admittanceMagnitude); // Introduce phase shift for capacitors - but only if minimum AC frequency is greater than 0, if it's not then there were // no AC voltage sources and so no current will flow through any capacitor if (minACFrequency > 0 && element is ICapacitor) { // Each wave has to be shifted by pi / 2 but only in its period. // For example, a wave with frequency 2 times the lowest frequency has to be shifted by total of pi / 4 - because // there are 2 periods of it in the full waveform. This relation is given by minimum frequency / wave frequency finalWaveform = WaveformBuilder.ShiftWaveform(finalWaveform, minACFrequency / waveform.Key.Frequency * Math.PI / 2); } // Add the waveform to the final waveform result.AddWaveform(waveform.Key, finalWaveform); } current = result; // And return success return(true); }
/// <summary> /// Tries to constract a power for an <see cref="ITwoTerminal"/> /// </summary> /// <param name="twoTerminal"></param> /// <param name="power"></param> /// <returns></returns> private bool TryConstructPower(ITwoTerminal twoTerminal, out ITimeDomainSignal power, bool voltageBA) { power = null; if (_VoltageDrops.TryGet(twoTerminal, out var voltage, voltageBA)) { ITimeDomainSignal current = null; // Call different method depending on the type of the two terminal TypeSwitch.Construct(). LazyCase <IResistor>((x) => _Currents.TryGet(x, out current)). LazyCase <ICapacitor>((x) => _Currents.TryGet(x, out current)). LazyCase <IActiveComponent>((x) => _Currents.TryGet(x.Index, out current)). Switch(twoTerminal); // If both voltage and current were obtained if (current != null) { var result = IoC.Resolve <ITimeDomainSignalMutable>(voltage.Samples, voltage.TimeStep, voltage.StartTime); // The result is a product of voltage and current waveforms, calculate power generated by each source separately foreach (var waveform in voltage.Waveforms) { // Add new power waveform, use voltage drop's source as its source and use the final current waveform. // If only the waveform for the considered source would be used then the result wouldbe incorrect. // Power is a product of voltage and current, we can't superposition both quantities because power is not // a linear relationship in that case. However if we take the total current then only superimposing voltage is correct // since it's just a linear relationship P = U * I (I is independent of the source - it can be considered as a scalar). result.AddWaveform(waveform.Key, waveform.Value.MergeSelect(current.FinalWaveform, (x, y) => x * y)); } power = result; } } // If power is not null it means that it was successfully constructed return(power != null); }
/// <summary> /// Tries to construct a current for <paramref name="component"/>, returns true on success /// </summary> /// <param name="component"></param> /// <param name="voltageBA">If true, it means that current was calculated for voltage drop from /// <see cref="ITwoTerminal.TerminalA"/> (reference) to <see cref="ITwoTerminal.TerminalB"/>, if false it means that /// that direction was reversed</param> /// <param name="current">Current constructed if successful, null otherwise</param> /// <returns></returns> protected abstract bool TryConstructCurrent(ITwoTerminal component, bool voltageBA, out TSignal current);
/// <summary> /// Returns null /// </summary> /// <param name="component"></param> /// <param name="voltageBA">If true, voltage drop is calculated from <see cref="ITwoTerminal.TerminalB"/> to /// <returns></returns> public ISignalInformation Get(ITwoTerminal component, bool voltageBA = true) => null;
/// <summary> /// Gets information on voltage drop across a <see cref="ITwoTerminal"/> component /// </summary> /// <param name="component"></param> /// <param name="voltageBA">If true, voltage drop is calculated from <see cref="ITwoTerminal.TerminalB"/> to /// <returns></returns> public ISignalInformation Get(ITwoTerminal component, bool voltageBA = true) => voltageBA? Get(component.TerminalA.NodeIndex, component.TerminalB.NodeIndex) : Get(component.TerminalB.NodeIndex, component.TerminalA.NodeIndex);
/// <summary> /// Gets voltage drop across a <see cref="ITwoTerminal"/> component or null if unsuccessful and assigns it to /// <paramref name="voltage"/>. Returns true on success, false otherwise. /// </summary> /// <param name="component"></param> /// <param name="voltageBA">If true, voltage drop is calculated from <see cref="ITwoTerminal.TerminalB"/> to /// <param name="voltage"></param> /// <returns></returns> public bool TryGet(ITwoTerminal component, out ITimeDomainSignal voltage, bool voltageBA = true) => voltageBA? TryGet(component.TerminalA.NodeIndex, component.TerminalB.NodeIndex, out voltage) : TryGet(component.TerminalB.NodeIndex, component.TerminalA.NodeIndex, out voltage);