ElectricPy icon indicating copy to clipboard operation
ElectricPy copied to clipboard

Adding `Frequency Response` for series and parallel `RLC` circuits to `electricpy.visu`

Open Lakshmikanth2001 opened this issue 2 years ago • 7 comments

Describe the solution you'd like

  • A clear and concise description of what you want to happen.
  • Computing various parameters like bandwidth, quality factor, resonating frequency, and characteristic equation
  • Plotting the frequency response using matlplotlob

Link to Formulas and Example References

Lakshmikanth2001 avatar Dec 31 '22 09:12 Lakshmikanth2001

This sounds like a marvelous idea!

Do you have some content or ideas that you'd like to contribute, or is there something I can help with?

engineerjoe440 avatar Jan 02 '23 23:01 engineerjoe440

Yes I am having a very abstract idea for Frequency Response as a class in visu.py

Which includes

  • RLC Series Frequency Response
  • Operation Amplifier Frequency Response
  • Any Second order system with a given characteristic equation
class FrequencyResponse:
    def __init__(self) -> None:
        pass

    def band_width(self):
        pass

    def quality_factor():
        pass

    def lower_cut_off_frequency():
        pass

    def upper_cut_off_frequency():
        pass

    def peak_gain():
        pass


class RLC(FrequencyResponse):
    def __init__(self) -> None:
        pass

class OpAmp(FrequencyResponse):
    def __init__(self) -> None:
        pass

Lakshmikanth2001 avatar Jan 03 '23 16:01 Lakshmikanth2001

I think this would be awesome!

Let me know how I can help!

engineerjoe440 avatar Jan 03 '23 17:01 engineerjoe440

class RLC(FrequencyResponse):
    def __init__(self, resistance: float, inductance: float, capacitance: float, frequency: float) -> None:
        self.resistance = resistance
        self.inductance = inductance
        self.capacitance = capacitance
        self.frequency = frequency

    def resonance_frequency(self):
        return 1/(_np.sqrt(self.inductance*self.capacitance)*2*_np.pi)

    def band_width(self):
        return self.resistance/(2*_np.pi*self.inductance)

    def quality_factor(self):
        return 2*_np.pi*self.frequency/self.resistance

    def lower_cut_off_frequency(self):
        x = (-self.resistance)/(2*self.inductance)
        resonance_angular_frequency = 2*_np.pi*self.resonance_frequency()

        return (x + _np.sqrt(x**2 + resonance_angular_frequency**2))/(2*_np.pi)

    def upper_cut_off_frequency(self):
        x = (self.resistance)/(2*self.inductance)
        resonance_angular_frequency = 2*_np.pi*self.resonance_frequency()

        return (x + _np.sqrt(x**2 + resonance_angular_frequency**2))/(2*_np.pi)

    def graph(self, lower_frequency_cut: float, upper_frequency_cut: float, samples = 10000):
        x = _np.linspace(lower_frequency_cut, upper_frequency_cut, samples)

        def output_gain(frequency):
            ang_frq = 2*_np.pi*frequency
            current_impedence = self.resistance**2 + (ang_frq*self.inductance - 1/(ang_frq*self.capacitance))**2
            return (self.resistance)/(_np.sqrt(current_impedence))

        y = output_gain(x)

        _plt.title("Frequency response of series RLC circuit")
        _plt.grid(visible=True)

        _plt.plot(x, y)
        _plt.ylabel("Gain")
        _plt.xlabel("Frequency (Hz)")
        f1, f2 = self.lower_cut_off_frequency(), self.upper_cut_off_frequency()
        f = self.resonance_frequency()
        _plt.scatter([f1, f2], [_np.sqrt(0.5), _np.sqrt(0.5)], marker="*", c="black", label='_nolegend_')


        half_power_gain = _np.sqrt(0.5)

        _plt.plot([f, f], [0, 1], ls='-.')
        _plt.plot([f1, f1], [half_power_gain, 0], ls='-.')
        _plt.plot([f2, f2], [half_power_gain, 0], ls='-.')
        _plt.plot([f1, f2], [half_power_gain, half_power_gain], ls='-.')

        _plt.plot([0, f], [half_power_gain, half_power_gain], label="_nolegend_", ls ="--")
        _plt.plot([0, f], [1, 1], label="_nolegend_", ls ="--")
        _plt.plot([0, 0], [half_power_gain, 1], label="Quality factor", c="black")
        _plt.scatter([0], [half_power_gain], label="_nolegend_", c="black", marker='v')
        _plt.scatter([0], [1], label="_nolegend_", c="black", marker='^')
        _plt.legend([
            'Gain',
            f'Resonance frequency ({f}Hz)',
            f'Lower cutoff frequency ({f1}Hz)',
            f'Upper cutoff frequency ({f2}Hz)',
            f'Bandwidth ({f2 - f1}Hz)',
            f'Quality factor {self.quality_factor()}'
        ])
        return _plt

Greeting,

  • can you help me with documenting each and every function of above and this methods and classes
  • should we take input in (uF, KHz) insted of (Farad and Hz)

Lakshmikanth2001 avatar May 13 '23 10:05 Lakshmikanth2001

Hi @Lakshmikanth2001! 👋

Regarding:

  • should we take input in (uF, KHz) insted of (Farad and Hz)

I think that we should probably only accept units in Farads and Hz, since those seem to be the most prolific throughout the package, and consistency is key! Converting to those units is relatively easy, so it shouldn't be too troublesome.


Regarding:

  • can you help me with documenting each and every function of above and this methods and classes

You bet! 😄 Happy to lend a hand, here!

Sample Python
class RLC(FrequencyResponse):
    """Frequency Response for a Traditional RLC (Resistive, Inductive, Capacitive) Load."""

    def __init__(self, resistance: float, inductance: float, capacitance: float, frequency: float) -> None:
        """Form the Frequency Response Analysis System."""
        self.resistance = resistance
        self.inductance = inductance
        self.capacitance = capacitance
        self.frequency = frequency

    @property
    def resonance_frequency(self):
        """Resonance Frequency (in Hz) of the Described RLC Circuit."""
        return 1/(_np.sqrt(self.inductance*self.capacitance)*2*_np.pi)

    @property
    def bandwidth(self):
        """Bandwidth of the Described RLC Circuit."""
        return self.resistance/(2*_np.pi*self.inductance)

    @property
    def quality_factor(self):
        """Quality Factor of the Described RLC Circuit."""
        return 2*_np.pi*self.frequency/self.resistance

    @property
    def lower_cutoff_frequency(self):
        """Lower Cutoff Frequency (in Hz) of the Described RLC Circuit."""
        x = (-self.resistance)/(2*self.inductance)
        resonance_angular_frequency = 2*_np.pi*self.resonance_frequency()

        return (x + _np.sqrt(x**2 + resonance_angular_frequency**2))/(2*_np.pi)

    @property
    def upper_cut_off_frequency(self):
        x = (self.resistance)/(2*self.inductance)
        resonance_angular_frequency = 2*_np.pi*self.resonance_frequency()

        return (x + _np.sqrt(x**2 + resonance_angular_frequency**2))/(2*_np.pi)

    def output_gain(self, frequency: float):
        """
        Evaluated Output Gain of the Described RLC Circuit at a Particular Frequency.
        
        Parameters
        -----------
        frequency:  float
                          Frequency (in Hz) at which the output gain should be evaluated.
        """
        ang_frq = 2*_np.pi*frequency
        current_impedence = self.resistance**2 + (ang_frq*self.inductance - 1/(ang_frq*self.capacitance))**2
        return (self.resistance)/(_np.sqrt(current_impedence))

    def graph(self, lower_frequency_cut: float, upper_frequency_cut: float, samples = 10000):
        """
        Generate a Plot to Represent all Data Respective of the RLC Circuit.
        
        Parameters
        ----------
        lower_frequency_cut:  float
                                   Minimum frequency to demonstrate as a boundary of the X-axis of the plot.
        upper_frequency_cut: float
                                   Maximum frequency to demonstrate as a boundary of the X-axis of the plot.
        samples:                   float
                                   Number of samples over which the plot should be formed.
        """
        x = _np.linspace(lower_frequency_cut, upper_frequency_cut, samples)

        y = self.output_gain(x)

        _plt.title("Frequency response of series RLC circuit")
        _plt.grid(visible=True)

        _plt.plot(x, y)
        _plt.ylabel("Gain")
        _plt.xlabel("Frequency (Hz)")
        f1, f2 = self.lower_cut_off_frequency, self.upper_cut_off_frequency
        f = self.resonance_frequency
        _plt.scatter([f1, f2], [_np.sqrt(0.5), _np.sqrt(0.5)], marker="*", c="black", label='_nolegend_')


        half_power_gain = _np.sqrt(0.5)

        _plt.plot([f, f], [0, 1], ls='-.')
        _plt.plot([f1, f1], [half_power_gain, 0], ls='-.')
        _plt.plot([f2, f2], [half_power_gain, 0], ls='-.')
        _plt.plot([f1, f2], [half_power_gain, half_power_gain], ls='-.')

        _plt.plot([0, f], [half_power_gain, half_power_gain], label="_nolegend_", ls ="--")
        _plt.plot([0, f], [1, 1], label="_nolegend_", ls ="--")
        _plt.plot([0, 0], [half_power_gain, 1], label="Quality factor", c="black")
        _plt.scatter([0], [half_power_gain], label="_nolegend_", c="black", marker='v')
        _plt.scatter([0], [1], label="_nolegend_", c="black", marker='^')
        _plt.legend([
            'Gain',
            f'Resonance frequency ({f}Hz)',
            f'Lower cutoff frequency ({f1}Hz)',
            f'Upper cutoff frequency ({f2}Hz)',
            f'Bandwidth ({f2 - f1}Hz)',
            f'Quality factor {self.quality_factor}'
        ])
        return _plt

Please note that I've also added a few general suggestions in the layout of the class (mainly, I think that some of those methods could easily be properties, and that inner-function in the graph method could be valuable by itself.

engineerjoe440 avatar May 15 '23 20:05 engineerjoe440

Thanks, @Lakshmikanth2001! 🎉

Let me know if you feel this issue is ready to close, or if you had other items you wish to add before we consider this work "done."

engineerjoe440 avatar May 18 '23 17:05 engineerjoe440

please test this changes from an end user perspective and give your valuable feedback we need to add Frequency Responses for

parallerl RLC ,OpAmp and First and second order control systems etc which can complete the Frequency Response issue

Lakshmikanth2001 avatar May 18 '23 17:05 Lakshmikanth2001