0
\$\begingroup\$

I am modeling a capacitor which has its capacitance varying according to the DC Bias. The DC Bias is computed by taking the mean of the voltage across the capacitance.

class CAP:
    """
    Represents the Capacitor.
    """
    def __init__(self, nominal_capacitance):
        # Theoretical capacitance without DC Bias (i.e. voltage mean at 0)
        self.nominal_capacitance = nominal_capacitance # uF
        # Actual capacitance, initialized at the nominal value
        self.capacitance = nominal_capacitance # uF

        # Voltage control loop settings
        self.set_voltage_value = None # Value set in mV
        self.set_lower_voltage_window_bound = None # Window upper bound in mV
        self.set_upper_voltage_window_bound = None # Window lower bound in mV
        self.voltage = 0 # Voltage across the capacitor
        self.being_charge = False # Is it being charged/discharged by the control loop?

        # Measurements
        self.charge_counter = 0
        self.discharge_counter = 0

        self.voltage_charged = 0
        self.voltage_discharged = 0

        # List of the voltage across the capacitance
        self.timeline = [] # mV

    def update_capacitance(self):
        """
        Value according to the DC Bias characterstic approximation.
        """
        global timelines_resolution # time step between 2 voltages measurements in the list self.timeline

        DC_bias = np.mean(self.timeline) / 1000
        change_perc = (-0.0008*(DC_bias**4)+0.0474*(DC_bias**3)-0.7953*(DC_bias**2)-0.5447*DC_bias-0.4847)/100
        self.capacitance = self.nominal_capacitance * (1+change_perc)

The voltage across the capacitance is controlled via a control loop which will recharge or discharge the capacitor. The goal is to keep its voltage within boundaries (window).

The attribute self.timeline will have one measure point (voltage) added every timelines_resolution. I can get between 200 000 points to a few millions.

The conversion to a numpy array and the mean computation become quite long. On the otherhand, it is handy to work with a list and its append method since I do not know beforehand the number of point that will be measured.

Do you see any other way to compute this DC Bias or to make this faster? At the moment, I call the function update_capacitance every 25000 points. I would like to increase this resolution.

\$\endgroup\$
2
  • \$\begingroup\$ I find it unclear how this code is intended to be used. Please include the "control loop" of which you speak. \$\endgroup\$ – 200_success Sep 27 '18 at 2:12
  • \$\begingroup\$ @200_success The control loop is irrelevant, the problem can be resumed as follow: how to compute efficiently the mean of the list, knowing that the list is growing and that we want to compute the mean every N (e.g. 25000) points. \$\endgroup\$ – Mathieu Sep 27 '18 at 9:09
1
\$\begingroup\$

Computing the mean of 25000 values is summing up the values and dividing by 25000. Computing the mean of 50000 values (the original 25000 plus 25000 new values) is summing up the values and dividing by 50000. This means (no pun intended) you are adding up the first 25000 values over and over again each time you compute the mean. And adding up 25000 values takes time. As the length of the list grows, the time it takes to sum it grows as well. Total complexity: \$O(n^2)\$

You could create a running total, and compute the mean yourself.

class CAP:

    def __init__(self, ...):
        ....
        self._sum = 0
        self._count = 0

    def update_capacitance(self):
        ...
        self._sum += sum(self.timeline[self._count:])   # Only add in the new samples
        self._count = len(self.timeline)
        DC_bias = (self._sum / self._count) / 1000

Feel free to use np.sum() if it is faster, or to ensure the required precision in the sum.


Appending values one-at-a-time to timeline is itself a time consuming process. If you know you are accumulating samples in blocks of 25000 points, you could pre-allocate a buffer of 25000 points with np.empty(), and fill that in. When it is full, after summing this small block into the running total, you could np.concatenate() to a larger timeline array. Or append it to a list of buffers, and created a new buffer for the next block, and concatenate all the blocks together at the end.

You could also create a list of the means of these smaller buffers, and compute the mean of that list. This may help avoid precision issues you would encounter in totaling a few million points.

\$\endgroup\$
1
  • \$\begingroup\$ I like the idea, I'll have a look, thanks :) \$\endgroup\$ – Mathieu Sep 27 '18 at 9:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.