Sizing Lab

8.5. TD1/2 - Hydrid storage architecture & specification [Student version]#

Written by Marc Budinger, INSA Toulouse, France

We will consider here an hybrid solution with super capacitors and traction batteries packs. This notebook is dedicated to :

  • understand main operating limits of super capacitors and batteries;

  • understand a control architecture enabling to split power between super capacitors and batteries;

  • specify energy storage requirements of the different energy sources.

The storage element selection approach developed here is inspired by the following publication:

Jaafar, A., Sareni, B., Roboam, X., & Thiounn-Guermeur, M. (2010, September). Sizing of a hybrid locomotive based on accumulators and ultracapacitors. In 2010 IEEE Vehicle Power and Propulsion Conference (pp. 1-6). IEEE [pdf].

8.5.1. Main operating limits of energy storage components#

To select the energy storage means, it is necessary to understand their main operational limits which can be representative of:

  • rapid/quick deterioration that can develop over an short operating cycle (minutes/hours).

  • gradual degradation linked to the lifespan of the component over multiple cycles (months/years), where the accumulation of degradation leads to an irreversible loss of performance.

Questions 1: Examine the following Figures extracted from a datasheet of elementary supercapacitors cell or battery (LiFeSO4) cell. Propose selection criteria representative of the main operational limits. Explain how to take into account an high number of discharge cycles within the sizing process of a battery.

Battery Cell

Ultracapacitor Cell

8.5.2. Reflexion on the Watt System architecture (capacitors)#

The WATT system includes:

  • In the bus: a battery pack and an ultra capacitors pack integrated to the electric power train, a connection system with an automatic arm installed on the roof of the vehicle;

  • On the sidewalk: a charging station or “totem” with the connection, as well as a pack of super capacitors, all integrated in the urban landscape.

Each totem (or charging station) is connected to the household electricity distribution network and allows charging between the stop of each bus.

In operation the bus is powered by supercapacitors, it travels the necessary distance to the next stop, thanks to the energy stored. At each stop, the arm deploys and realizes in 10 seconds the transfer of energy between the super capacitors of the totem and the onboard super capacitors. An adapted Lithium-ion battery pack is installed in the bus in case of a connection or recharge problem and trips without passengers to return to the central station (maintenance).

Ultracapacitor Pack and Totem PositionPacks

In order to estimate the dimensions of the ultracapacities pack, we will be interested in the energy to be supplied to the motorization of the bus on a typical path:

  • Top speed \(v_{max}\) = 30 km/h

  • Distance between 2 totems \(d\) = 800 m

  • Maximum height difference \(h\) = 3 m

The bus has the following characteristics:

Aerodynamic effect are assume here to be negligeable due to low speed.

Exercice 1: Write the general equation enabling to calculate the maximum energy to supply (to be stored). Apply it with considered simplification (aerodynamics neglected) for defined use case scenario.

# Parameters
v_max=30*1000/3600 # [m/s] max speed
m=20e3 # [kg] bus mass
d=800. # [m] distance
h=3. # [m] rise height
C_rr= 0.01 # [-] tyre rolling coef
g=9.81 # [m/s²] gravity acceleration

# Energies & works calculations
# TO BE COMPLETED

We will take as ultra-capacitor reference the following module from Maxwell Technologies.

Maxwell module characteristics MaxwellModule

Exercice 2: We will assume that maximum discharge is reached when capacitor voltage reaches half its naximum voltage. Explain and calculate the minimum number of modules.

import math

# Maxwell module characteristics
V_max=125. # [V] Max voltage
C=63. # [F] Module capacitance

# Modules number calculation
V_min=V_max/2
# TO BE COMPLETED

8.5.3. Simulation of a complete line#

The objective of this section is to propose an evolution of the previous python codes to be able to:

  • simulate the power profile necessary for a complete line comprising several sections.

  • We will define in particular the type of vehicle, the different lengths of sections between 2 stations (Distances vector), the average speed to be ensured (Speeds vector), the presence of charger in station (Chargers vector), the stopping duration at station (StopDuration vector), the ratio between the maximum braking power and the maximum acceleration power (RatioBrakeMax).

  • provide the information necessary for sizing the battery/supercapacity packs that could be added.

  • We assume here an efficiency of the motorization chain of 100%.

  • Each line section will be optimized in order to meet the requirements defined previously and minimize the energy consumed. -The energy flow or the resulting power demand will be shared between battery and supercapacity with control based on frequency sharing of demands: the low frequency power will be provided by the batteries while the high frequency part will be provided by supercapacitors.

  • Indicators useful for sizing will then be generated from these power profiles.

  • take into account the energy that could come from charging stations or catenaries.

  • This version will only implement the consideration of charging stations.

  • Each charging station will provide the power to compensate for the energy of the travel from the last station.

Let us first load the classes developed within TP1 notebook.

%%capture
%run ./01b_CaseStudy_Specification.ipynb

Here we define a line class with all the functionalities described just before.

from scipy import signal

class line():
    def __init__(self, Vehicle, Distances, Speeds, Chargers, StopDuration, RatioBrakeMax):
        
        # Save attributes
        self.Chargers =  Chargers # Boolean vector (True = Charger, False = No Charger , at the end of the section)
        self.StopDuration = StopDuration # [s] station stop duration (scalar)
        self.RatioBrakeMax = RatioBrakeMax # [-] Ratio between max braking power / max acceleration power
        
        # Initialization of  transient evolution (vectors)
        self.PowerStorage = [] # Transient evolution of requested power
        self.GlobalTime = [] # Time vector for plot and energy integration
        self.GlobalNRJStorage = [] # Transient evolution of energy
        self.PowerLF = [] # Transient evolution of power  (Low Frequency)
        self.PowerHF = [] # Transient evolution of power  (high Frequency)
        self.LFNRJStorage = [] # Time vector for plot and energy integration (Low Frequency)
        self.HFNRJStorage  =[] # Time vector for plot and energy integration (High Frequency)
        self.TotalLineDistance = sum(Distances)
        self.dt=0.25 # Time step for numerical integration
        
        # Print characteristics of each section
        i=0
        self.Section=[]
        for d,s,c in zip(Distances, Speeds, Chargers):
            print("Section %.i: %.i m at %.2f m/s %s charger"%(i+1,d,s, "whith" if c else "without"))
            self.Section=self.Section+[OptimSection(Vehicle,d,s,self.RatioBrakeMax,self.dt)]
            i=i+1
    
    # Optimization loop of each section
    def optimLine(self):
        X=[0.1, 1.0, 0.9]
        for i in range(len(self.Section)):
            self.Section[i].optimizeConso_genetic(X)
    
    # Power vector concatenation 
    def CalculPowerStorage(self):
        NRJ = 0
        self.PowerStorage= []
        self.GlobalTime= []
        dt=self.dt # [s] pas de temps pour l'integration
        
        # Power vector build thanks concatenation 
        for i in range(len(self.Section)):
            NRJ=NRJ+self.Section[i].NRJsection[-1] # we add here the energy consummed on the section
            
            self.PowerStorage = self.PowerStorage + self.Section[i].psection
            
            # Chargers effect
            if (self.Chargers[i] == True and i<(len(self.Section)-1)):
                tcharge=NRJ/self.Section[i].Vehicle.Pmax # Charging time caculation function of energy  
            else:
                tcharge=0
            if (tcharge>=self.StopDuration):
                self.PowerStorage = self.PowerStorage + [-self.Section[i].Vehicle.Pmax]*int(tcharge/dt)
                NRJ=0
            else:
                if (i<(len(self.Section)-1)):
                    self.PowerStorage = self.PowerStorage + [-self.Section[i].Vehicle.Pmax]*int(tcharge/dt)
                    self.PowerStorage = self.PowerStorage + [0]*int((self.StopDuration-tcharge)/dt)
                if (self.Chargers[i] == True) :
                    NRJ=0  
        
        # Time vector
        t=0
        for i in range(len(self.PowerStorage)):
            self.GlobalTime = self.GlobalTime + [t]
            t = t + dt
            
    # Filtering of total power in order to generate LF (battery) and HF (capacitor) powers
    def FilterPower(self, omega):
        TF=signal.TransferFunction([1], [1/omega**2, 2*1/omega, 1])
        time, self.PowerLF, state = signal.lsim(TF, self.PowerStorage , self.GlobalTime)
        self.PowerHF = self.PowerStorage -  self.PowerLF
        
    # NRJ vector integration from power vectors
    def IntegrateNRJ(self):
        t=0
        NRJtotal=0
        NRJHF=0
        NRJLF=0
        #NRJTotalAging=0
        #NRJLFAging=0
        self.HFNRJStorage = []
        self.GlobalNRJStorage = []
        self.LFNRJStorage = []
        dt=self.dt
        
        for i in range(len(self.PowerStorage)):
            self.GlobalNRJStorage = self.GlobalNRJStorage + [NRJtotal]
            self.HFNRJStorage = self.HFNRJStorage + [NRJHF]
            self.LFNRJStorage = self.LFNRJStorage + [NRJLF]
            #self.TotalNRJAging = self.TotalNRJAging + [NRJTotalAging]
            #self.LFNRJAging = self.LFNRJAging + [NRJLFAging]
        
            t = t + dt
            NRJtotal = NRJtotal+(self.PowerStorage[i])*dt
            NRJHF = NRJHF+(self.PowerHF[i])*dt
            NRJLF = NRJLF+(self.PowerLF[i])*dt
        
        PmaxHF = max(abs(min(self.PowerHF)),max(self.PowerHF))/1e3 # kW
        PmaxLF = max(self.PowerLF)/1e3 # kW 
        PmaxBrakeLF = abs(min(self.PowerLF))/1e3 # kW, Max power braking
        NRJHF = (max(self.HFNRJStorage) - min(self.HFNRJStorage))/3600/1e3 # NRJ en kWh
        NRJLF = (max(self.LFNRJStorage) - min(self.LFNRJStorage))/3600/1e3 # NRJ en kWh
        
        return PmaxHF, PmaxLF, PmaxBrakeLF, NRJHF, NRJLF
                      
    # Plot the results for a local section
    def plot_power_share(self):
        fig, axs = plt.subplots(2,1)
        try:
            axs[0].plot(self.GlobalTime,self.PowerStorage,'b-',label='Total')
            axs[0].plot(self.GlobalTime,self.PowerLF,'r-.',label='LF')
            axs[0].plot(self.GlobalTime,self.PowerHF,'g-.',label='HF')
        except:
            pass
        axs[0].set_ylabel("Power (W)")
        axs[0].legend(bbox_to_anchor=(1.05, 1.0), loc='upper left')
        axs[0].grid()
        try:
            axs[1].plot(self.GlobalTime,self.GlobalNRJStorage,'b-',label='Total')
            axs[1].plot(self.GlobalTime,self.LFNRJStorage,'r-.',label='LF')
            axs[1].plot(self.GlobalTime,self.HFNRJStorage,'g-.',label='HF')
            axs[1].set_ylabel("Energy (J)")
            axs[1].legend(bbox_to_anchor=(1.05, 1.0), loc='upper left')
            axs[1].grid()   
            axs[1].set_xlabel('Time (s)')
        except:
            fig.delaxes(axs[1])
        fig.tight_layout() 

    # Plot the results for the complete line
    def plot_sections(self):
        for i in range(len(self.Section)):
            self.Section[i].plot()  
              
            

Exercice 3: Read previous code and explain what it does method by method.

8.5.4. Example of a line definition#

We can now use this new class to define a bus transport line with the following requirements:

  • distances: 700, 500, 400, 700, 300, 300, 300, 300, 300, 300 m/s

  • mean speed: 7, 7, 7, 5, 5, 5, 5, 5 m/s

  • one final charger

Exercice 4: Define the line and launch an optimization process. Then plot the results on all the profiles.

Each speed profil section can be optimized.

A time vector of evolution of the power required at each section or supplied to each charger is constructed.

ToulouseC.CalculPowerStorage()
ToulouseC.plot_power_share()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[5], line 1
----> 1 ToulouseC.CalculPowerStorage()
      2 ToulouseC.plot_power_share()

NameError: name 'ToulouseC' is not defined

8.5.4.1. Hybrid storage system sizing#

The energy flow or the resulting power demand will be shared between battery and supercapacity with control based on frequency sharing of demands. The Figure below show how the low frequency power will be provided by the batteries while the high frequency part will be provided by supercapacitors.

Sizing Stategy

The cutoff frequency defines the power sharing and has a strong influence on the sizing of the storage elements. The following code analyzes this power sharing by varying this cutoff frequency.

Exercice 5: Implement the different sizing criteria (power, energy, lifetime…) to evaluate the mass or CO2 impact of batteries and supercapacitors using emission densities (ratio between CO2 to produce components an their own embedded mass).

omegaV=np.logspace(-5,2,50)
MassStorageV=[]
MassLFPAgeing=[]
MassLFPNRJ=[]
MassLFPPow=[]
MassSCNRJ=[]
MassSCPow=[]
CO2Total=[]

# Lifetime of the vehicle
Targetkm = 250e3 # [km]

# Supercapacitors energy density
# https://1188159.fs1.hubspotusercontent-na1.net/hubfs/1188159/02-DS-220909-SKELCAP-CELLS-1F.pdf
# chez Skeleton
WmassSC=6.8*0.75 # [Wh/kg] 75% of the fully-charge energy can be used (voltage variation from Vmax to Vmax/2)
PmassSC=860/4.3*6.8*0.75 # [W/kg]

# Batteries energy density (LFP technology)
WmassLFP= 100 # [Wh/kg] Depth Of Discharge (DOD) of 100% does not decrease that much the lifetime
PmassLFP=3*100 # [W/kg]  Power density (3C acceptable discharge)
PBmassLFP=1*100 # [W/kg]  Power density (1C acceptable charge)
Ncycle = 3000 # [-] cycles number at 100% DOD

# Bilan carbone
CO2SC = 39 # kgCO2eq/kg d'ecoInvent
CO2LFP = 11 # kgCO2eq/kg d'ecoInvent

# For each LF/HF repartition size batteries/supercapacitors
for omega in omegaV:
    
    ToulouseC.FilterPower(omega)
    PmaxHF, PmaxLF, PmaxBrakeLF, NRJHF, NRJLF = ToulouseC.IntegrateNRJ()

    # Calculate batteries mass depending on criteria (Ageing, energy, power)
    Nc=Targetkm*1000/ToulouseC.TotalLineDistance # Number of cycles for global lifetime 
    DoD=(Nc/3365)**(-1/2.26) # DoD calculation for Target Km
    # TO BE COMPLETED
    # MassLFPAgeing = 
    # MassLFPNRJ = 
    # MassLFPPow = 

    # Calculate capacitors mass depending on criteria (Energy, power)
    # TO BE COMPLETED
    # MassSCNRJ = MassSCNRJ + []
    # MassSCPow = MassSCPow + []

    # Calculate worst case total hybrid storage mass
    MassStorageV = MassStorageV + [max(MassLFPAgeing[-1], MassLFPNRJ[-1], MassLFPPow[-1]) + max(MassSCNRJ[-1], MassSCPow[-1])]
     
    # Calculate CO2 emission for hybrid mix
    CO2Total = CO2Total + [max(MassSCNRJ[-1], MassSCPow[-1])*CO2SC + max(MassLFPAgeing[-1], MassLFPNRJ[-1], MassLFPPow[-1])*CO2LFP]
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[6], line 32
     29 # For each LF/HF repartition size batteries/supercapacitors
     30 for omega in omegaV:
---> 32     ToulouseC.FilterPower(omega)
     33     PmaxHF, PmaxLF, PmaxBrakeLF, NRJHF, NRJLF = ToulouseC.IntegrateNRJ()
     35     # Calculate batteries mass depending on criteria (Ageing, energy, power)

NameError: name 'ToulouseC' is not defined

The following figures represent the overall mass of the solutions according to the choosen power share. A simple CO2 impact is also estimated.

plt.plot(omegaV, MassStorageV, 'g^', label='Total')
plt.plot(omegaV, MassSCNRJ, 'yo', label='SuperCap NRJ')
plt.plot(omegaV, MassSCPow, 'yx', label='SuperCap Power')
plt.plot(omegaV, MassLFPNRJ, 'bo', label='LFP NRJ')
plt.plot(omegaV, MassLFPPow, 'bx', label='LFP Power (Brake)')
plt.plot(omegaV, MassLFPAgeing, 'ro', label='LFP Aging')
plt.xscale('log') 
plt.ylabel('Weight (kg)')
plt.xlabel('Cut off angular frequency (rad/s)')
plt.legend()
plt.grid()
plt.show()

plt.plot(omegaV, CO2Total, 'g^', label='Total')
plt.xscale('log') 
plt.ylabel('CO2  (kgCO2eq)')
plt.xlabel('Cut off angular frequency (rad/s)')
plt.legend()
plt.grid()
plt.show()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[7], line 1
----> 1 plt.plot(omegaV, MassStorageV, 'g^', label='Total')
      2 plt.plot(omegaV, MassSCNRJ, 'yo', label='SuperCap NRJ')
      3 plt.plot(omegaV, MassSCPow, 'yx', label='SuperCap Power')

File /opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/matplotlib/pyplot.py:3794, in plot(scalex, scaley, data, *args, **kwargs)
   3786 @_copy_docstring_and_deprecators(Axes.plot)
   3787 def plot(
   3788     *args: float | ArrayLike | str,
   (...)
   3792     **kwargs,
   3793 ) -> list[Line2D]:
-> 3794     return gca().plot(
   3795         *args,
   3796         scalex=scalex,
   3797         scaley=scaley,
   3798         **({"data": data} if data is not None else {}),
   3799         **kwargs,
   3800     )

File /opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/matplotlib/axes/_axes.py:1779, in Axes.plot(self, scalex, scaley, data, *args, **kwargs)
   1536 """
   1537 Plot y versus x as lines and/or markers.
   1538 
   (...)
   1776 (``'green'``) or hex strings (``'#008000'``).
   1777 """
   1778 kwargs = cbook.normalize_kwargs(kwargs, mlines.Line2D)
-> 1779 lines = [*self._get_lines(self, *args, data=data, **kwargs)]
   1780 for line in lines:
   1781     self.add_line(line)

File /opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/matplotlib/axes/_base.py:296, in _process_plot_var_args.__call__(self, axes, data, *args, **kwargs)
    294     this += args[0],
    295     args = args[1:]
--> 296 yield from self._plot_args(
    297     axes, this, kwargs, ambiguous_fmt_datakey=ambiguous_fmt_datakey)

File /opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/matplotlib/axes/_base.py:486, in _process_plot_var_args._plot_args(self, axes, tup, kwargs, return_kwargs, ambiguous_fmt_datakey)
    483     axes.yaxis.update_units(y)
    485 if x.shape[0] != y.shape[0]:
--> 486     raise ValueError(f"x and y must have same first dimension, but "
    487                      f"have shapes {x.shape} and {y.shape}")
    488 if x.ndim > 2 or y.ndim > 2:
    489     raise ValueError(f"x and y can be no greater than 2D, but have "
    490                      f"shapes {x.shape} and {y.shape}")

ValueError: x and y must have same first dimension, but have shapes (50,) and (0,)
../../_images/45816d171484af6f5b3de210bb3ae3d5c2ef9ecbcb6905647d8f0d99ce65e1a7.png

A Pareto front can help find a solution achieving a compromise between 2 objectives.

# Pareto Front

plt.scatter(MassStorageV, CO2Total, c=np.log10(omegaV))
plt.xlabel('Weight (kg)')
plt.ylabel('CO2  (kgCO2eq)')
plt.colorbar()
plt.title('Cut off angular frequency influence on Pareto Front')
plt.show()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/matplotlib/axes/_axes.py:4618, in Axes._parse_scatter_color_args(c, edgecolors, kwargs, xsize, get_next_color_func)
   4617 try:  # Is 'c' acceptable as PathCollection facecolors?
-> 4618     colors = mcolors.to_rgba_array(c)
   4619 except (TypeError, ValueError) as err:

File /opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/matplotlib/colors.py:512, in to_rgba_array(c, alpha)
    511 else:
--> 512     rgba = np.array([to_rgba(cc) for cc in c])
    514 if alpha is not None:

File /opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/matplotlib/colors.py:512, in <listcomp>(.0)
    511 else:
--> 512     rgba = np.array([to_rgba(cc) for cc in c])
    514 if alpha is not None:

File /opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/matplotlib/colors.py:314, in to_rgba(c, alpha)
    313 if rgba is None:  # Suppress exception chaining of cache lookup failure.
--> 314     rgba = _to_rgba_no_colorcycle(c, alpha)
    315     try:

File /opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/matplotlib/colors.py:398, in _to_rgba_no_colorcycle(c, alpha)
    397 if not np.iterable(c):
--> 398     raise ValueError(f"Invalid RGBA argument: {orig_c!r}")
    399 if len(c) not in [3, 4]:

ValueError: Invalid RGBA argument: -5.0

The above exception was the direct cause of the following exception:

ValueError                                Traceback (most recent call last)
Cell In[8], line 3
      1 # Pareto Front
----> 3 plt.scatter(MassStorageV, CO2Total, c=np.log10(omegaV))
      4 plt.xlabel('Weight (kg)')
      5 plt.ylabel('CO2  (kgCO2eq)')

File /opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/matplotlib/pyplot.py:3903, in scatter(x, y, s, c, marker, cmap, norm, vmin, vmax, alpha, linewidths, edgecolors, plotnonfinite, data, **kwargs)
   3884 @_copy_docstring_and_deprecators(Axes.scatter)
   3885 def scatter(
   3886     x: float | ArrayLike,
   (...)
   3901     **kwargs,
   3902 ) -> PathCollection:
-> 3903     __ret = gca().scatter(
   3904         x,
   3905         y,
   3906         s=s,
   3907         c=c,
   3908         marker=marker,
   3909         cmap=cmap,
   3910         norm=norm,
   3911         vmin=vmin,
   3912         vmax=vmax,
   3913         alpha=alpha,
   3914         linewidths=linewidths,
   3915         edgecolors=edgecolors,
   3916         plotnonfinite=plotnonfinite,
   3917         **({"data": data} if data is not None else {}),
   3918         **kwargs,
   3919     )
   3920     sci(__ret)
   3921     return __ret

File /opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/matplotlib/__init__.py:1473, in _preprocess_data.<locals>.inner(ax, data, *args, **kwargs)
   1470 @functools.wraps(func)
   1471 def inner(ax, *args, data=None, **kwargs):
   1472     if data is None:
-> 1473         return func(
   1474             ax,
   1475             *map(sanitize_sequence, args),
   1476             **{k: sanitize_sequence(v) for k, v in kwargs.items()})
   1478     bound = new_sig.bind(ax, *args, **kwargs)
   1479     auto_label = (bound.arguments.get(label_namer)
   1480                   or bound.kwargs.get(label_namer))

File /opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/matplotlib/axes/_axes.py:4805, in Axes.scatter(self, x, y, s, c, marker, cmap, norm, vmin, vmax, alpha, linewidths, edgecolors, plotnonfinite, **kwargs)
   4802 if edgecolors is None:
   4803     orig_edgecolor = kwargs.get('edgecolor', None)
   4804 c, colors, edgecolors = \
-> 4805     self._parse_scatter_color_args(
   4806         c, edgecolors, kwargs, x.size,
   4807         get_next_color_func=self._get_patches_for_fill.get_next_color)
   4809 if plotnonfinite and colors is None:
   4810     c = np.ma.masked_invalid(c)

File /opt/hostedtoolcache/Python/3.9.23/x64/lib/python3.9/site-packages/matplotlib/axes/_axes.py:4624, in Axes._parse_scatter_color_args(c, edgecolors, kwargs, xsize, get_next_color_func)
   4622 else:
   4623     if not valid_shape:
-> 4624         raise invalid_shape_exception(c.size, xsize) from err
   4625     # Both the mapping *and* the RGBA conversion failed: pretty
   4626     # severe failure => one may appreciate a verbose feedback.
   4627     raise ValueError(
   4628         f"'c' argument must be a color, a sequence of colors, "
   4629         f"or a sequence of numbers, not {c!r}") from err

ValueError: 'c' argument has 50 elements, which is inconsistent with 'x' and 'y' with size 0.
../../_images/45816d171484af6f5b3de210bb3ae3d5c2ef9ecbcb6905647d8f0d99ce65e1a7.png
ToulouseC.FilterPower(0.4)
PmaxHF, PmaxLF, PmaxBrakeLF, NRJHF, NRJLF=ToulouseC.IntegrateNRJ()

Nc=Targetkm*1000/ToulouseC.TotalLineDistance # Number of cycles for global lifetime 
DoD=(Nc/3365)**(-1/2.26) # DoD calculation for Target Km

print("Super Capacitor:")
print("Pmax: %.2f kW"%PmaxHF)
print("NRJ:  %.2f kWh"%NRJHF)
print("Mass: % .1f kg"%(max(NRJHF/WmassSC*1e3, PmaxHF/PmassSC*1e3)))
      
print("---")
print("Traction battery:")
print("Pmax discharge: %.2f kW"%PmaxLF)
print("Pmax charge: %.2f kW"%PmaxBrakeLF)
print("NRJ: %.2f kWh"%(NRJLF/DoD))
print("NRJ one travel: %.2f kWh"%(NRJLF))
print("Mass: % .1f kg"%(max(NRJLF/DoD/WmassLFP*1e3,
                                       PmaxLF/PmassLFP*1e3, PmaxBrakeLF/PBmassLFP*1e3)))  
print("Mass (brake criteria): % .1f kg"%(PmaxLF/PmassLFP*1e3))  


ToulouseC.plot()
print("---")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[9], line 1
----> 1 ToulouseC.FilterPower(0.4)
      2 PmaxHF, PmaxLF, PmaxBrakeLF, NRJHF, NRJLF=ToulouseC.IntegrateNRJ()
      4 Nc=Targetkm*1000/ToulouseC.TotalLineDistance # Number of cycles for global lifetime 

NameError: name 'ToulouseC' is not defined

8.5.4.2. Labwork and homework#

Your objective is to specify the hybrid storage system of an electric bus for doubling line 78 between the IUT Rangueil and MFJA stations. The characteristics of the bus are here
BlueBus A example of time table of the line 78 is here
L78 We will assume a round trip in 20 min, charge at the ends of the lines included.

Modify the present notebooks in order to set up a technical justification report : starting from the need (journey, vehicle size, frequency of journeys), setting up the effort/speed/power profiles, the power distribution in the hybrid storage system, the preliminary sizing and the specification of the main components.

Adapt and complete the sizing process in order to take into account the global efficiency of the converters and storage elments (assumed to be equal to 80%).

Sizing process

Propose compatible technological storage packs and specify the DC/DC converters (DC bus of 400 V).

Provide an electrical architecture diagram (possible software) summarizing your main choices:

  • representing the different sources and load of the network.

  • making it possible to standardize the DC/DC converters used.

  • allowing reduced functionality to be maintained in the event of a fault on part of the storage elements