jiahuimbzuai/CoQuIR huggingface dataset:
//  [python]
def _one_component_kilonova_rosswog_heatingrate(time, mej, vej, electron_fraction, **kwargs):
    tdays = time/day_to_s
    # set up kilonova physics
    av, bv, dv = interpolated_barnes_and_kasen_thermalisation_efficiency(mej, vej)
    # thermalisation from Barnes+16
    e_th = 0.36 * (np.exp(-av * tdays) + np.log1p(2.0 * bv * tdays ** dv) / (2.0 * bv * tdays ** dv))
    temperature_floor = kwargs.get('temperature_floor', 4000)  # kelvin
    beta = 13.7

    v0 = vej * speed_of_light
    m0 = mej * solar_mass
    kappa = kappa_from_electron_fraction(electron_fraction)
    tdiff = np.sqrt(2.0 * kappa * (m0) / (beta * v0 * speed_of_light))

    lum_in = _calc_new_heating_rate(time, mej, electron_fraction, vej, **kwargs)
    integrand = lum_in * e_th * (time / tdiff) * np.exp(time ** 2 / tdiff ** 2)

    bolometric_luminosity = np.zeros(len(time))
    bolometric_luminosity[1:] = cumulative_trapezoid(integrand, time)
    bolometric_luminosity[0] = bolometric_luminosity[1]
    bolometric_luminosity = bolometric_luminosity * np.exp(-time ** 2 / tdiff ** 2) / tdiff

    temperature = (bolometric_luminosity / (4.0 * np.pi * sigma_sb * v0 ** 2 * time ** 2)) ** 0.25
    r_photosphere = (bolometric_luminosity / (4.0 * np.pi * sigma_sb * temperature_floor ** 4)) ** 0.5

    # check temperature floor conditions
    mask = temperature <= temperature_floor
    temperature[mask] = temperature_floor
    mask = np.logical_not(mask)
    r_photosphere[mask] = v0 * time[mask]
    return bolometric_luminosity, temperature, r_photosphere
//  [python]
def run(x,jarr,aarr,rhoarr,sphstokes=-1,atol=1e-8,rtol=1e-6,max_tau=10):
    if sphstokes==-1:
        method=0
    else:
        method=3
    radtrans_integrate.init_radtrans_integrate_data(method,4,len(x),len(x),max_tau,0.1,atol,rtol,1e-2,100000)
    Karr = (np.append(aarr,rhoarr,axis=1))
    tau = np.append(0.,scipy.integrate.cumtrapz(Karr[:,0],x))
    radtrans_integrate.integrate(x[::-1],jarr[:,:],Karr[:,:],tau,4)
    i = radtrans_integrate.intensity.copy()
    radtrans_integrate.del_radtrans_integrate_data()
    return i
//  [python]
def run(x,jarr,aarr,rhoarr,sphstokes=-1,atol=1e-8,rtol=1e-6,max_tau=10):
    if sphstokes==-1:
        method=0
    else:
        method=3
    radtrans_integrate.init_radtrans_integrate_data(method,4,len(x),len(x),max_tau,0.1,atol,rtol,1e-2,100000)
    Karr = (np.append(aarr,rhoarr,axis=1))
    tau = np.append(0.,scipy.integrate.cumulative_trapezoid(Karr[:,0],x))
    radtrans_integrate.integrate(x[::-1],jarr[:,:],Karr[:,:],tau,4)
    i = radtrans_integrate.intensity.copy()
    radtrans_integrate.del_radtrans_integrate_data()
    return i
//  [python]
def firstpassagetime_avg_theory2(self, x0, *args, **kwargs):
        r"""
        Compute the mean first-passage time using the theoretical formula:

        :math:`\mathbb{E}[\tau_M] = \frac{1}{D} \int_{x_0}^{M} dx e^{V(x)/D} \int_{-\infty}^x e^{-V(y)/D}dy.`

        This formula is valid for a homogeneous process, conditioned on the initial position :math:`x_0`.

        Parameters
        ----------
        x0 : float
            Initial position
        """
        t0 = kwargs.pop('t0', 0.0)
        inf = kwargs.pop('inf', -10.0)
        # args have to be sorted in increasing order:
        args = np.sort(args)
        # remove the values of args which are <= x0:
        badargs, args = (args[args <= x0], args[args > x0])

        def exppot(y, sign=-1, fun=lambda z: 1):
            return np.exp(sign*self.model.potential(y, t0)/self.model.D0)*fun(y)
        # compute the inner integral and interpolate:
        z = np.linspace(inf, args[-1], **kwargs)
        iarr = integrate.cumtrapz(exppot(z), z, initial=0)
        ifun = interp1d(z, iarr)
        # now compute the outer integral by chunks
        y = np.linspace(x0, args[-1], **kwargs)
        oarr = integrate.cumtrapz(exppot(y, sign=1, fun=ifun), y, initial=0)/self.model.D0
        ofun = interp1d(y, oarr)
        return np.concatenate((badargs, args)), np.concatenate((np.zeros_like(badargs), ofun(args)))
//  [python]
def firstpassagetime_avg_theory2(self, x0, *args, **kwargs):
        r"""
        Compute the mean first-passage time using the theoretical formula:

        :math:`\mathbb{E}[\tau_M] = \frac{1}{D} \int_{x_0}^{M} dx e^{V(x)/D} \int_{-\infty}^x e^{-V(y)/D}dy.`

        This formula is valid for a homogeneous process, conditioned on the initial position :math:`x_0`.

        Parameters
        ----------
        x0 : float
            Initial position
        """
        t0 = kwargs.pop('t0', 0.0)
        inf = kwargs.pop('inf', -10.0)
        # args have to be sorted in increasing order:
        args = np.sort(args)
        # remove the values of args which are <= x0:
        badargs, args = (args[args <= x0], args[args > x0])

        def exppot(y, sign=-1, fun=lambda z: 1):
            return np.exp(sign*self.model.potential(y, t0)/self.model.D0)*fun(y)
        # compute the inner integral and interpolate:
        z = np.linspace(inf, args[-1], **kwargs)
        iarr = integrate.cumulative_trapezoid(exppot(z), z, initial=0)
        ifun = interp1d(z, iarr)
        # now compute the outer integral by chunks
        y = np.linspace(x0, args[-1], **kwargs)
        oarr = integrate.cumulative_trapezoid(exppot(y, sign=1, fun=ifun), y, initial=0)/self.model.D0
        ofun = interp1d(y, oarr)
        return np.concatenate((badargs, args)), np.concatenate((np.zeros_like(badargs), ofun(args)))
//  [python]
def _one_component_kilonova_model(time, mej, vej, kappa, **kwargs):
    """
    :param time: source frame time in seconds
    :param redshift: redshift
    :param mej: ejecta mass in solar masses
    :param vej: minimum initial velocity
    :param kappa_r: gray opacity
    :param kwargs: temperature_floor
    :return: bolometric_luminosity, temperature, r_photosphere
    """
    tdays = time/day_to_s

    # set up kilonova physics
    av, bv, dv = interpolated_barnes_and_kasen_thermalisation_efficiency(mej, vej)
    # thermalisation from Barnes+16
    e_th = 0.36 * (np.exp(-av * tdays) + np.log1p(2.0 * bv * tdays ** dv) / (2.0 * bv * tdays ** dv))
    t0 = 1.3 #seconds
    sig = 0.11  #seconds
    temperature_floor = kwargs.get('temperature_floor', 4000) #kelvin

    beta = 13.7

    v0 = vej * speed_of_light
    m0 = mej * solar_mass
    tdiff = np.sqrt(2.0 * kappa * (m0) / (beta * v0 * speed_of_light))
    lum_in = 4.0e18 * (m0) * (0.5 - np.arctan((time - t0) / sig) / np.pi)**1.3
    integrand = lum_in * e_th * (time/tdiff) * np.exp(time**2/tdiff**2)
    bolometric_luminosity = np.zeros(len(time))
    bolometric_luminosity[1:] = cumtrapz(integrand, time)
    bolometric_luminosity[0] = bolometric_luminosity[1]
    bolometric_luminosity = bolometric_luminosity * np.exp(-time**2/tdiff**2) / tdiff

    temperature = (bolometric_luminosity / (4.0 * np.pi * sigma_sb * v0**2 * time**2))**0.25
    r_photosphere = (bolometric_luminosity / (4.0 * np.pi * sigma_sb * temperature_floor**4))**0.5

    # check temperature floor conditions
    mask = temperature <= temperature_floor
    temperature[mask] = temperature_floor
    mask = np.logical_not(mask)
    r_photosphere[mask] = v0 * time[mask]
    return bolometric_luminosity, temperature, r_photosphere
//  [python]
def _one_component_kilonova_model(time, mej, vej, kappa, **kwargs):
    """
    :param time: source frame time in seconds
    :param redshift: redshift
    :param mej: ejecta mass in solar masses
    :param vej: minimum initial velocity
    :param kappa_r: gray opacity
    :param kwargs: temperature_floor
    :return: bolometric_luminosity, temperature, r_photosphere
    """
    tdays = time/day_to_s

    # set up kilonova physics
    av, bv, dv = interpolated_barnes_and_kasen_thermalisation_efficiency(mej, vej)
    # thermalisation from Barnes+16
    e_th = 0.36 * (np.exp(-av * tdays) + np.log1p(2.0 * bv * tdays ** dv) / (2.0 * bv * tdays ** dv))
    t0 = 1.3 #seconds
    sig = 0.11  #seconds
    temperature_floor = kwargs.get('temperature_floor', 4000) #kelvin

    beta = 13.7

    v0 = vej * speed_of_light
    m0 = mej * solar_mass
    tdiff = np.sqrt(2.0 * kappa * (m0) / (beta * v0 * speed_of_light))
    lum_in = 4.0e18 * (m0) * (0.5 - np.arctan((time - t0) / sig) / np.pi)**1.3
    integrand = lum_in * e_th * (time/tdiff) * np.exp(time**2/tdiff**2)
    bolometric_luminosity = np.zeros(len(time))
    bolometric_luminosity[1:] = cumulative_trapezoid(integrand, time)
    bolometric_luminosity[0] = bolometric_luminosity[1]
    bolometric_luminosity = bolometric_luminosity * np.exp(-time**2/tdiff**2) / tdiff

    temperature = (bolometric_luminosity / (4.0 * np.pi * sigma_sb * v0**2 * time**2))**0.25
    r_photosphere = (bolometric_luminosity / (4.0 * np.pi * sigma_sb * temperature_floor**4))**0.5

    # check temperature floor conditions
    mask = temperature <= temperature_floor
    temperature[mask] = temperature_floor
    mask = np.logical_not(mask)
    r_photosphere[mask] = v0 * time[mask]
    return bolometric_luminosity, temperature, r_photosphere
//  [python]
def dist_for_params(z, params):
    """
    Get comoving distance and expansion rate for a given set of parameters.
    """
    a = 1. / (1. + z)
    zz = np.linspace(0., 6., 200)
    aa = 1. / (1. + zz)
    
    # Set parameter values
    h = params['h']
    w0 = params['w0']
    wa = params['wa']
    oc = params['oc']
    ob = params['ob']
    onu = params['Mnu'] / (93. * h**2.) # Neutrino density scale: 93 eV
    om = oc + ob + onu
    
    ok = params['ok']
    ol = 1. - om - ok
    H0 = 100. * h
    
    # Calculate Hubble rate H(z)
    omegaDE = lambda aa: ol * np.exp(3.*wa*(aa - 1.)) / aa**(3.*(1. + w0 + wa))
    E = lambda aa: np.sqrt( om * aa**(-3.) + ok * aa**(-2.) + omegaDE(aa) )
    H = H0 * E(a)
    
    # Calculate comoving dist.
    r_c = np.concatenate( ([0.], scipy.integrate.cumtrapz(1./E(aa), zz)) )
    if ok > 0.:
        _r = C/(H0*np.sqrt(ok)) * np.sinh(r_c * np.sqrt(ok))
    elif ok < 0.:
        _r = C/(H0*np.sqrt(-ok)) * np.sin(r_c * np.sqrt(-ok))
    else:
        _r = (C/H0) * r_c
    r = scipy.interpolate.interp1d(zz, _r, kind='linear', bounds_error=False)(z)
    return H, r
//  [python]
def dist_for_params(z, params):
    """
    Get comoving distance and expansion rate for a given set of parameters.
    """
    a = 1. / (1. + z)
    zz = np.linspace(0., 6., 200)
    aa = 1. / (1. + zz)
    
    # Set parameter values
    h = params['h']
    w0 = params['w0']
    wa = params['wa']
    oc = params['oc']
    ob = params['ob']
    onu = params['Mnu'] / (93. * h**2.) # Neutrino density scale: 93 eV
    om = oc + ob + onu
    
    ok = params['ok']
    ol = 1. - om - ok
    H0 = 100. * h
    
    # Calculate Hubble rate H(z)
    omegaDE = lambda aa: ol * np.exp(3.*wa*(aa - 1.)) / aa**(3.*(1. + w0 + wa))
    E = lambda aa: np.sqrt( om * aa**(-3.) + ok * aa**(-2.) + omegaDE(aa) )
    H = H0 * E(a)
    
    # Calculate comoving dist.
    r_c = np.concatenate( ([0.], scipy.integrate.cumulative_trapezoid(1./E(aa), zz)) )
    if ok > 0.:
        _r = C/(H0*np.sqrt(ok)) * np.sinh(r_c * np.sqrt(ok))
    elif ok < 0.:
        _r = C/(H0*np.sqrt(-ok)) * np.sin(r_c * np.sqrt(-ok))
    else:
        _r = (C/H0) * r_c
    r = scipy.interpolate.interp1d(zz, _r, kind='linear', bounds_error=False)(z)
    return H, r
//  [python]
def running_ave(kappa, time):
    """
    Gets running average. Reads and returns the structure input file from GPUMD.

    Args:
        kappa (ndarray): Raw thermal conductivity
        time (ndarray): Time vector that kappa was sampled at

    Returns:
        ndarray: Running average of kappa input
    """
    return cumtrapz(kappa, time, initial=0)/time
//  [python]
def running_ave(kappa, time):
    """
    Gets running average. Reads and returns the structure input file from GPUMD.

    Args:
        kappa (ndarray): Raw thermal conductivity
        time (ndarray): Time vector that kappa was sampled at

    Returns:
        ndarray: Running average of kappa input
    """
    return cumulative_trapezoid(kappa, time, initial=0)/time
//  [python]
def BMX_Magnetic_Fieldd(time, Bzdot, Brdot, Btdot, mean_Cutoff, max_Range,
                       rprobe_Dia = 0.003175, ztprobe_Dia = 0.00158755,
                       hole_Sep = 0.001016):
    """
        Outputs the magnetic field strengths, time and monitor value.
    """
   
    #Setting the area variables
    rprobe_Area = np.pi*(rprobe_Dia/2)**2
    tzprobe_Area = ztprobe_Dia*hole_Sep
    
    #Setting the time variables
    time_Microsec = time
    time_Sec = time_Microsec*1e-6
    timeB_Sec = time_Sec[1:]
    
    # My standard is {Z, R, Theta}
    Bzdot -= np.mean(Bzdot[0:mean_Cutoff])
    Brdot -= np.mean(Brdot[0:mean_Cutoff])
    Btdot -= np.mean(Btdot[0:mean_Cutoff])
    
    Bzdot = BMX_Clear_Infs(Bzdot, max_Range)
    Brdot = BMX_Clear_Infs(Brdot, max_Range)
    Btdot = BMX_Clear_Infs(Btdot, max_Range)
    
    # Performing the integration using the trap-rule (Guass units)
    Bz = sp.cumtrapz(Bzdot/(tzprobe_Area),time_Sec)*1e4
    Br = sp.cumtrapz(Brdot/(rprobe_Area),time_Sec)*1e4
    Bt = sp.cumtrapz(Btdot/(tzprobe_Area),time_Sec)*1e4
    
    Bz = sps.detrend(Bz)
    Br = sps.detrend(Br)
    Bt = sps.detrend(Bt)
    
    return  timeB_Sec, Bz, Br, Bt
//  [python]
def BMX_Magnetic_Fieldd(time, Bzdot, Brdot, Btdot, mean_Cutoff, max_Range,
                       rprobe_Dia = 0.003175, ztprobe_Dia = 0.00158755,
                       hole_Sep = 0.001016):
    """
        Outputs the magnetic field strengths, time and monitor value.
    """
   
    #Setting the area variables
    rprobe_Area = np.pi*(rprobe_Dia/2)**2
    tzprobe_Area = ztprobe_Dia*hole_Sep
    
    #Setting the time variables
    time_Microsec = time
    time_Sec = time_Microsec*1e-6
    timeB_Sec = time_Sec[1:]
    
    # My standard is {Z, R, Theta}
    Bzdot -= np.mean(Bzdot[0:mean_Cutoff])
    Brdot -= np.mean(Brdot[0:mean_Cutoff])
    Btdot -= np.mean(Btdot[0:mean_Cutoff])
    
    Bzdot = BMX_Clear_Infs(Bzdot, max_Range)
    Brdot = BMX_Clear_Infs(Brdot, max_Range)
    Btdot = BMX_Clear_Infs(Btdot, max_Range)
    
    # Performing the integration using the trap-rule (Guass units)
    Bz = sp.cumulative_trapezoid(Bzdot/(tzprobe_Area),time_Sec)*1e4
    Br = sp.cumulative_trapezoid(Brdot/(rprobe_Area),time_Sec)*1e4
    Bt = sp.cumulative_trapezoid(Btdot/(tzprobe_Area),time_Sec)*1e4
    
    Bz = sps.detrend(Bz)
    Br = sps.detrend(Br)
    Bt = sps.detrend(Bt)
    
    return  timeB_Sec, Bz, Br, Bt
//  [python]
def numeric_cdf_grid(self, kde, N=None, cut=None):
        """
        Compute the CDF on a grid using a trivial, but fast, numeric
        integration of the pdf.
        """
        pts, pdf = self.grid(kde, N, cut)
        cdf = integrate.cumtrapz(pdf, pts, initial=0)
        return pts, cdf
//  [python]
def numeric_cdf_grid(self, kde, N=None, cut=None):
        """
        Compute the CDF on a grid using a trivial, but fast, numeric
        integration of the pdf.
        """
        pts, pdf = self.grid(kde, N, cut)
        cdf = integrate.cumulative_trapezoid(pdf, pts, initial=0)
        return pts, cdf
//  [python]
def integrate_signal(self, acc_sig, fs, int_type="time"):
        """
        Performs either time of frequency domain integration
        Parameters
        ----------
        acc_sig
        fs
        int_type

        Returns
        -------

        """
        if int_type == "time":
            vel = inter.cumtrapz(acc_sig, None, dx=1 / fs)
            disp = inter.cumtrapz(vel, None, dx=1 / fs)

            return vel, disp

        if int_type == "frequency":
            vel = self.freq_int(acc_sig, fs, 1)
            disp = self.freq_int(acc_sig, fs, 2)
            return vel, disp
//  [python]
def integrate_signal(self, acc_sig, fs, int_type="time"):
        """
        Performs either time of frequency domain integration
        Parameters
        ----------
        acc_sig
        fs
        int_type

        Returns
        -------

        """
        if int_type == "time":
            vel = inter.cumulative_trapezoid(acc_sig, None, dx=1 / fs)
            disp = inter.cumulative_trapezoid(vel, None, dx=1 / fs)

            return vel, disp

        if int_type == "frequency":
            vel = self.freq_int(acc_sig, fs, 1)
            disp = self.freq_int(acc_sig, fs, 2)
            return vel, disp
//  [python]
def opticalDepth(ds, ab, int_c):
    """
    Calculates the optical depth of material given the integration grid and the absorption coefficients.

    :param ds: The regular spacing of the interpolated grid (integration distances , ds)
    :param ab: grid of absorption coefficients calculated from absorptionBody()
    :param int_c: integration constant calculated from integrationConstant()

    :return: array of cumulative optical depth (tau)
    """
    tau = (intg.cumtrapz(ab, x=ds, initial=0, axis=1)) * int_c
    #note that axis=1 is here to ensure integration occurs along y axis.
    #Python denotes the arrays as [z,y,x] in Tecplot axes terms.(x and z axes notation are swapped)

    return tau
//  [python]
def opticalDepth(ds, ab, int_c):
    """
    Calculates the optical depth of material given the integration grid and the absorption coefficients.

    :param ds: The regular spacing of the interpolated grid (integration distances , ds)
    :param ab: grid of absorption coefficients calculated from absorptionBody()
    :param int_c: integration constant calculated from integrationConstant()

    :return: array of cumulative optical depth (tau)
    """
    tau = (scipy.integrate.cumulative_trapezoid(ab, x=ds, initial=0, axis=1)) * int_c
    #note that axis=1 is here to ensure integration occurs along y axis.
    #Python denotes the arrays as [z,y,x] in Tecplot axes terms.(x and z axes notation are swapped)

    return tau
//  [python]
def _expand_E_grid(eedges, n,Econt_in_full, cont_in_full):

  """
  Code to expand the compressed continuum onto a series of bins.

  Parameters
  ----------
  eedges : float(array)
    The bin edges for the spectrum to be calculated on, in units of keV
  n : int
    The number of good data points in the continuum array
  Econt_in_full: float(array)
    The compressed continuum energies
  cont_in_full: float(array)
    The compressed continuum emissivities

  Returns
  -------
  float(array)
    len(bins)-1 array of continuum emission, in units of \
    photons cm^3 s^-1 bin^-1
  """

#  History
#  -------
#  Version 0.1 - initial release
#    Adam Foster July 17th 2015

  import scipy.integrate
  cont_in = cont_in_full[:n]
  Econt_in = Econt_in_full[:n]


  # ok. So. Sort this.
  E_all = numpy.append(Econt_in, eedges)

  cont_tmp = numpy.interp(eedges, Econt_in, cont_in)
  C_all = numpy.append(cont_in, cont_tmp)

  iord = numpy.argsort(E_all)

  # order the arrays
  E_all = E_all[iord]
  C_all = C_all[iord]

  ihi = numpy.where(iord>=n)[0]
  cum_cont = scipy.integrate.cumtrapz(C_all, E_all, initial=0)
  C_out = numpy.zeros(len(eedges))
#  for i in range(len(eedges)):
#    arg  = numpy.argwhere(iord==n+i)[0][0]
#    C_out[i] = cum_cont[ihi[i]]

  C_out = cum_cont[ihi]

  cont = C_out[1:]-C_out[:-1]
  return cont
//  [python]
def _expand_E_grid(eedges, n,Econt_in_full, cont_in_full):

  """
  Code to expand the compressed continuum onto a series of bins.

  Parameters
  ----------
  eedges : float(array)
    The bin edges for the spectrum to be calculated on, in units of keV
  n : int
    The number of good data points in the continuum array
  Econt_in_full: float(array)
    The compressed continuum energies
  cont_in_full: float(array)
    The compressed continuum emissivities

  Returns
  -------
  float(array)
    len(bins)-1 array of continuum emission, in units of \
    photons cm^3 s^-1 bin^-1
  """

#  History
#  -------
#  Version 0.1 - initial release
#    Adam Foster July 17th 2015

  import scipy.integrate
  cont_in = cont_in_full[:n]
  Econt_in = Econt_in_full[:n]


  # ok. So. Sort this.
  E_all = numpy.append(Econt_in, eedges)

  cont_tmp = numpy.interp(eedges, Econt_in, cont_in)
  C_all = numpy.append(cont_in, cont_tmp)

  iord = numpy.argsort(E_all)

  # order the arrays
  E_all = E_all[iord]
  C_all = C_all[iord]

  ihi = numpy.where(iord>=n)[0]
  cum_cont = scipy.integrate.cumulative_trapezoid(C_all, E_all, initial=0)
  C_out = numpy.zeros(len(eedges))
#  for i in range(len(eedges)):
#    arg  = numpy.argwhere(iord==n+i)[0][0]
#    C_out[i] = cum_cont[ihi[i]]

  C_out = cum_cont[ihi]

  cont = C_out[1:]-C_out[:-1]
  return cont
//  [python]
def mgtm(self, m):
        cdf_by_m = cumtrapz(self.tab_imf * self.Ms**2, x=np.log(self.Ms), initial=0.) \
            / np.trapz(self.tab_imf * self.Ms**2, x=np.log(self.Ms))
        
        return 1. - np.interp(m, self.Ms, cdf_by_m)
//  [python]
def mgtm(self, m):
        cdf_by_m = cumulative_trapezoid(self.tab_imf * self.Ms**2, x=np.log(self.Ms), initial=0.) \
            / np.trapz(self.tab_imf * self.Ms**2, x=np.log(self.Ms))
        
        return 1. - np.interp(m, self.Ms, cdf_by_m)
//  [python]
def chern_density(h,nk=10,operator=None,delta=0.02,dk=0.02,
        es=np.linspace(-1.0,1.0,40)):
  """Compute the Chern density as a function of the energy"""
  ks = klist.kmesh(h.dimensionality,nk=nk)
  cs = np.zeros(es.shape[0]) # initialize
  f = h.get_gk_gen(delta=delta,canonical_phase=True) # green function generator
  tr = timing.Testimator("CHERN DENSITY",maxite=len(ks))
  from . import parallel
  def fp(k): # compute berry curvatures
    if parallel.cores==1: tr.iterate()
    else: print(k)
#    k = np.random.random(3)
    fgreen = berry_green_generator(f,k=k,dk=dk,operator=operator,full=False) 
    return np.array([fgreen(e).real for e in es])
  out = parallel.pcall(fp,ks) # compute everything
  for o in out: cs += o # add contributions
  cs = cs/(len(ks)*np.pi*2) # normalize
  from scipy.integrate import cumtrapz
  csi = cumtrapz(cs,x=es,initial=0) # integrate
  np.savetxt("CHERN_DENSITY.OUT",np.matrix([es,cs]).T)
  np.savetxt("CHERN_DENSITY_INTEGRATED.OUT",np.matrix([es,csi]).T)
//  [python]
def chern_density(h,nk=10,operator=None,delta=0.02,dk=0.02,
        es=np.linspace(-1.0,1.0,40)):
  """Compute the Chern density as a function of the energy"""
  ks = klist.kmesh(h.dimensionality,nk=nk)
  cs = np.zeros(es.shape[0]) # initialize
  f = h.get_gk_gen(delta=delta,canonical_phase=True) # green function generator
  tr = timing.Testimator("CHERN DENSITY",maxite=len(ks))
  from . import parallel
  def fp(k): # compute berry curvatures
    if parallel.cores==1: tr.iterate()
    else: print(k)
#    k = np.random.random(3)
    fgreen = berry_green_generator(f,k=k,dk=dk,operator=operator,full=False) 
    return np.array([fgreen(e).real for e in es])
  out = parallel.pcall(fp,ks) # compute everything
  for o in out: cs += o # add contributions
  cs = cs/(len(ks)*np.pi*2) # normalize
  from scipy.integrate import cumulative_trapezoid
  csi = cumulative_trapezoid(cs,x=es,initial=0) # integrate
  np.savetxt("CHERN_DENSITY.OUT",np.matrix([es,cs]).T)
  np.savetxt("CHERN_DENSITY_INTEGRATED.OUT",np.matrix([es,csi]).T)
//  [python]
def tab_dtd_cdf(self):
        if not hasattr(self, '_tab_dtd_cdf'):
            ok = self.Ms >= 8.
            top = cumtrapz(self.tab_life[ok==1] * self.tab_imf[ok==1] \
                * self.Ms[ok==1], x=np.log(self.Ms[ok==1]), initial=0.0)
                
            bot = np.trapz(self.tab_life[ok==1] * self.tab_imf[ok==1] \
                * self.Ms[ok==1], x=np.log(self.Ms[ok==1]))
            
            self._tab_dtd_cdf = top / bot
            
        return self._tab_dtd_cdf
//  [python]
def tab_dtd_cdf(self):
        if not hasattr(self, '_tab_dtd_cdf'):
            ok = self.Ms >= 8.
            top = cumulative_trapezoid(self.tab_life[ok==1] * self.tab_imf[ok==1] \
                * self.Ms[ok==1], x=np.log(self.Ms[ok==1]), initial=0.0)
                
            bot = np.trapz(self.tab_life[ok==1] * self.tab_imf[ok==1] \
                * self.Ms[ok==1], x=np.log(self.Ms[ok==1]))
            
            self._tab_dtd_cdf = top / bot
            
        return self._tab_dtd_cdf
//  [python]
def plot_density_fit(self):

		Ds = np.asarray(list(self.density(th) for th in self.get_theta()))
		Dslow = np.percentile(Ds,2.5,axis=0)
		Dshigh = np.percentile(Ds,97.5,axis=0)
		Dmean = self.density(self.theta)

		fig = plt.figure()
		plt.plot(self.t,Dmean,'r-',color='red',lw=3)
		plt.plot(self.t,Dslow,'r--',lw=1)
		plt.plot(self.t,Dshigh,'r--',lw=1)
		plt.axvline(x=self.T,color='b',linestyle='-')

		mirrored_data = (2*self.T-self.df['infection'].values).tolist()
		combined_data = self.df['infection'].values.tolist() + mirrored_data
		dense = gaussian_kde(combined_data)
		denseval = list(dense(x)*2 for x in self.t)
		plt.plot(self.t,denseval,'b-',color='blue',lw=3)
		plt.fill_between(self.t, Dslow, Dshigh, alpha=.3, color='red')
		plt.legend()
		plt.ylabel('$-S_t/(1-S_T)$')
		plt.xlabel('t')
		c = cumtrapz(Dmean,self.t)
		ind = np.argmax(c>=0.001)
		plt.xlim((self.t[ind],self.t[-1]+1))

		return fig
//  [python]
def plot_density_fit(self):

		Ds = np.asarray(list(self.density(th) for th in self.get_theta()))
		Dslow = np.percentile(Ds,2.5,axis=0)
		Dshigh = np.percentile(Ds,97.5,axis=0)
		Dmean = self.density(self.theta)

		fig = plt.figure()
		plt.plot(self.t,Dmean,'r-',color='red',lw=3)
		plt.plot(self.t,Dslow,'r--',lw=1)
		plt.plot(self.t,Dshigh,'r--',lw=1)
		plt.axvline(x=self.T,color='b',linestyle='-')

		mirrored_data = (2*self.T-self.df['infection'].values).tolist()
		combined_data = self.df['infection'].values.tolist() + mirrored_data
		dense = gaussian_kde(combined_data)
		denseval = list(dense(x)*2 for x in self.t)
		plt.plot(self.t,denseval,'b-',color='blue',lw=3)
		plt.fill_between(self.t, Dslow, Dshigh, alpha=.3, color='red')
		plt.legend()
		plt.ylabel('$-S_t/(1-S_T)$')
		plt.xlabel('t')
		c = cumulative_trapezoid(Dmean,self.t)
		ind = np.argmax(c>=0.001)
		plt.xlim((self.t[ind],self.t[-1]+1))

		return fig
//  [python]
def compute_integral(src: SignalObj) -> SignalObj:
    """Compute integral

    Args:
        src: source signal

    Returns:
        Result signal object
    """
    dst = dst_11(src, "integral")
    x, y = src.get_data()
    dst.set_xydata(x, spt.cumtrapz(y, x, initial=0.0))
    return dst
//  [python]
def compute_integral(src: SignalObj) -> SignalObj:
    """Compute integral

    Args:
        src: source signal

    Returns:
        Result signal object
    """
    dst = dst_11(src, "integral")
    x, y = src.get_data()
    dst.set_xydata(x, spt.cumulative_trapezoid(y, x, initial=0.0))
    return dst
//  [python]
def find_cutoff_wavenumber(P, m, integration_limit, lambda_min=5):
    """
    Find cutoff wavenumber for spectrum integration.

    Parameters
    ----------
    P : array-like
        Spectrum
    m : array-like
        Vertical wavenumber vector
    integration_limit : float
        Variance limit for integration
    lambda_min : float, optional
        Minimum vertical wavelength lambda_z=2pi/m, by default 5

    Returns
    -------
    iim : array-like
        Integration range as indexer to `P`.
    """
    specsum = cumtrapz(P, m)
    specsum = np.insert(specsum, 0, 0)
    iim = np.flatnonzero(
        np.less(specsum, integration_limit, where=np.isfinite(specsum))
    )
    if iim.size == 1:  # need at least two data points for integration
        iim = np.append(iim, 1)
    if iim.size > 1:
        # Do not go to wavenumbers corresponding to lambda_z < lambda_min meter
        if np.max(m[iim] / 2 / np.pi) > 1.0 / lambda_min:
            iim = np.where(m / 2 / np.pi < 1.0 / lambda_min)[0]
        return iim, np.max(m[iim])
    else:
        return iim, np.nan
//  [python]
def find_cutoff_wavenumber(P, m, integration_limit, lambda_min=5):
    """
    Find cutoff wavenumber for spectrum integration.

    Parameters
    ----------
    P : array-like
        Spectrum
    m : array-like
        Vertical wavenumber vector
    integration_limit : float
        Variance limit for integration
    lambda_min : float, optional
        Minimum vertical wavelength lambda_z=2pi/m, by default 5

    Returns
    -------
    iim : array-like
        Integration range as indexer to `P`.
    """
    specsum = cumulative_trapezoid(P, m)
    specsum = np.insert(specsum, 0, 0)
    iim = np.flatnonzero(
        np.less(specsum, integration_limit, where=np.isfinite(specsum))
    )
    if iim.size == 1:  # need at least two data points for integration
        iim = np.append(iim, 1)
    if iim.size > 1:
        # Do not go to wavenumbers corresponding to lambda_z < lambda_min meter
        if np.max(m[iim] / 2 / np.pi) > 1.0 / lambda_min:
            iim = np.where(m / 2 / np.pi < 1.0 / lambda_min)[0]
        return iim, np.max(m[iim])
    else:
        return iim, np.nan
//  [python]
def get_husid(acceleration, time_step):
    """
    Returns the Husid vector, defined as \int{acceleration ** 2.}
    :param numpy.ndarray acceleration:
        Vector of acceleration values
    :param float time_step:
        Time-step of record (s)
    """
    time_vector = get_time_vector(time_step, len(acceleration))
    husid = np.hstack([0., cumtrapz(acceleration ** 2., time_vector)])
    return husid, time_vector
//  [python]
def get_husid(acceleration, time_step):
    """
    Returns the Husid vector, defined as \int{acceleration ** 2.}
    :param numpy.ndarray acceleration:
        Vector of acceleration values
    :param float time_step:
        Time-step of record (s)
    """
    time_vector = get_time_vector(time_step, len(acceleration))
    husid = np.hstack([0., cumulative_trapezoid(acceleration ** 2., time_vector)])
    return husid, time_vector
//  [python]
def get_peak_measures(time_step, acceleration, get_vel=False, get_disp=False):
    """
    Returns the peak measures from acceleration, velocity and displacement
    time-series
    :param float time_step:
        Time step of acceleration time series in s
    :param numpy.ndarray acceleration:
        Acceleration time series
    :param bool get_vel:
        Choose to return (and therefore calculate) velocity (True) or otherwise
        (false)
    :returns:
        * pga - Peak Ground Acceleration
        * pgv - Peak Ground Velocity
        * pgd - Peak Ground Displacement
        * velocity - Velocity Time Series
        * dispalcement - Displacement Time series
    """
    pga = np.max(np.fabs(acceleration))
    velocity = None
    displacement = None
    # If displacement is not required then do not integrate to get
    # displacement time series
    if get_disp:
        get_vel = True
    if get_vel:
        velocity = time_step * cumtrapz(acceleration, initial=0.)
        pgv = np.max(np.fabs(velocity))
    else:
        pgv = None
    if get_disp:
        displacement = time_step * cumtrapz(velocity, initial=0.)
        pgd = np.max(np.fabs(displacement))
    else:
        pgd = None
    return pga, pgv, pgd, velocity, displacement
//  [python]
def get_peak_measures(time_step, acceleration, get_vel=False, get_disp=False):
    """
    Returns the peak measures from acceleration, velocity and displacement
    time-series
    :param float time_step:
        Time step of acceleration time series in s
    :param numpy.ndarray acceleration:
        Acceleration time series
    :param bool get_vel:
        Choose to return (and therefore calculate) velocity (True) or otherwise
        (false)
    :returns:
        * pga - Peak Ground Acceleration
        * pgv - Peak Ground Velocity
        * pgd - Peak Ground Displacement
        * velocity - Velocity Time Series
        * dispalcement - Displacement Time series
    """
    pga = np.max(np.fabs(acceleration))
    velocity = None
    displacement = None
    # If displacement is not required then do not integrate to get
    # displacement time series
    if get_disp:
        get_vel = True
    if get_vel:
        velocity = time_step * cumulative_trapezoid(acceleration, initial=0.)
        pgv = np.max(np.fabs(velocity))
    else:
        pgv = None
    if get_disp:
        displacement = time_step * cumulative_trapezoid(velocity, initial=0.)
        pgd = np.max(np.fabs(displacement))
    else:
        pgd = None
    return pga, pgv, pgd, velocity, displacement
//  [python]
def rebin_old(x, xp, fp):
  ''' Resample a function fp(xp) over the new grid x, rebinning if necessary, 
    otherwise interpolates
    Parameters
    ----------
    x	: 	array like
	New coordinates
    fp 	:	array like
	y-coordinates to be resampled
    xp 	:	array like
	x-coordinates at which fp are sampled
	
    Returns
    -------
    out	: 	array like
	new samples  
  '''
  
  if (x.unit != xp.unit):
    print (x.unit, xp.unit)
    jexosim_error('Units mismatch')
  
  idx = np.where(np.logical_and(xp > 0.9*x.min(), xp < 1.1*x.max()))[0]

  xp = xp[idx]
  fp = fp[idx]
  
  print (abs(np.diff(xp)).min() ,  abs(np.diff(x)).min())

  if abs(np.diff(xp)).min() < abs(np.diff(x)).min():

    print ('**************  binning')    # Binning!
    # old method
    # c = cumtrapz(fp, x=xp)
    # xpc = xp[1:]    
    # delta = np.gradient(x)
    # new_c_1 = np.interp(x-0.5*delta, xpc, c, 
    #                     left=0.0, right=0.0)
    # new_c_2 = np.interp(x+0.5*delta, xpc, c, 
    #                     left=0.0, right=0.0)
    # new_f = (new_c_2 - new_c_1)/delta
    
    # I think this is a bit more correct - not much difference in reality
    c = cumtrapz(fp, x=xp)
    xpc = xp[1:].value
    x = x.value
    diff = np.diff(x)
    diff_pos = np.array((diff/2).tolist() +[diff[-1]/2]) # edge bins fix
    diff_neg = np.array([diff[0]/2] +(diff/2).tolist()) # edge bins fix
    delta = diff_pos+diff_neg
    new_c_1 = np.interp(x-diff_neg, xpc, c, 
                        left=0.0, right=0.0)
    new_c_2 = np.interp(x+diff_pos, xpc, c, 
                        left=0.0, right=0.0)
    new_f = ((new_c_2 - new_c_1)/delta)*fp.unit
    x = x*xp.unit
        
  else:
    print ('**************  interpolating') # Interpolate !
    new_f = np.interp(x, xp, fp, left=0.0, right=0.0)
    
  new_f = (new_f.value)*fp.unit
 
#    func = interpolate.interp1d(xp, fp, kind='quadratic', bounds_error=None, fill_value=0.0)
#    new_f  = func(x)*fp.unit
  '''
  import matplotlib.pyplot as plt
  plt.plot(xp, fp, '-')
  plt.plot(x, new_f, '.-')
  plt.show()
  # check
  print np.trapz(new_f, x)
  idx = np.where(np.logical_and(xp>= x.min(), xp <= x.max()))
  print np.trapz(fp[idx], xp[idx])
  '''
  return x, new_f
//  [python]
def rebin_old(x, xp, fp):
  ''' Resample a function fp(xp) over the new grid x, rebinning if necessary, 
    otherwise interpolates
    Parameters
    ----------
    x	: 	array like
	New coordinates
    fp 	:	array like
	y-coordinates to be resampled
    xp 	:	array like
	x-coordinates at which fp are sampled
	
    Returns
    -------
    out	: 	array like
	new samples  
  '''
  
  if (x.unit != xp.unit):
    print (x.unit, xp.unit)
    jexosim_error('Units mismatch')
  
  idx = np.where(np.logical_and(xp > 0.9*x.min(), xp < 1.1*x.max()))[0]

  xp = xp[idx]
  fp = fp[idx]
  
  print (abs(np.diff(xp)).min() ,  abs(np.diff(x)).min())

  if abs(np.diff(xp)).min() < abs(np.diff(x)).min():

    print ('**************  binning')    # Binning!
    # old method
    # c = cumtrapz(fp, x=xp)
    # xpc = xp[1:]    
    # delta = np.gradient(x)
    # new_c_1 = np.interp(x-0.5*delta, xpc, c, 
    #                     left=0.0, right=0.0)
    # new_c_2 = np.interp(x+0.5*delta, xpc, c, 
    #                     left=0.0, right=0.0)
    # new_f = (new_c_2 - new_c_1)/delta
    
    # I think this is a bit more correct - not much difference in reality
    c = scipy.integrate.cumulative_trapezoid(fp, x=xp)
    xpc = xp[1:].value
    x = x.value
    diff = np.diff(x)
    diff_pos = np.array((diff/2).tolist() +[diff[-1]/2]) # edge bins fix
    diff_neg = np.array([diff[0]/2] +(diff/2).tolist()) # edge bins fix
    delta = diff_pos+diff_neg
    new_c_1 = np.interp(x-diff_neg, xpc, c, 
                        left=0.0, right=0.0)
    new_c_2 = np.interp(x+diff_pos, xpc, c, 
                        left=0.0, right=0.0)
    new_f = ((new_c_2 - new_c_1)/delta)*fp.unit
    x = x*xp.unit
        
  else:
    print ('**************  interpolating') # Interpolate !
    new_f = np.interp(x, xp, fp, left=0.0, right=0.0)
    
  new_f = (new_f.value)*fp.unit
 
#    func = interpolate.interp1d(xp, fp, kind='quadratic', bounds_error=None, fill_value=0.0)
#    new_f  = func(x)*fp.unit
  '''
  import matplotlib.pyplot as plt
  plt.plot(xp, fp, '-')
  plt.plot(x, new_f, '.-')
  plt.show()
  # check
  print np.trapz(new_f, x)
  idx = np.where(np.logical_and(xp>= x.min(), xp <= x.max()))
  print np.trapz(fp[idx], xp[idx])
  '''
  return x, new_f
//  [python]
def rebin(x, xp, fp):
  ''' Resample a function fp(xp) over the new grid x, rebinning if necessary, 
    otherwise interpolates
    Parameters
    ----------
    x	: 	array like
	New coordinates
    fp 	:	array like
	y-coordinates to be resampled
    xp 	:	array like
	x-coordinates at which fp are sampled
	
    Returns
    -------
    out	: 	array like
	new samples  
  '''
  
  if (x.unit != xp.unit):
    print (x.unit, xp.unit)
    jexosim_error('Units mismatch')
    
    
# ============================================================================
  fp[np.isnan(fp)] = 0
  # first select non-zero elements
  idx_x = np.argwhere(x>0).T[0]
  idx_xp = np.argwhere(xp>0).T[0]

  x_ = x[idx_x]
  xp_ = xp[idx_xp]
  fp_ = fp[idx_xp]

  # select elements in input grid within wl range of new grid
  idx = np.where(np.logical_and(xp_ > 0.9*x_.min(), xp_ < 1.1*x_.max()))[0]
  xp = xp_[idx]
  fp = fp_[idx]

  #To check that the binning or interp is applied correctly uncomment following
  # print (abs(np.diff(xp_)).min() ,  abs(np.diff(x_)).min())

  # compare the resolutions of the non-zero arrays and thus bin or interpolate
  if abs(np.diff(xp_).min()) < abs(np.diff(x_).min()):
      
    # print ('**************  binning')    # Binning!
  
    c = cumtrapz(fp, x=xp)
    xpc = xp[1:].value
    x = x.value
    diff = np.diff(x)
    diff_pos = np.array((diff/2).tolist() +[diff[-1]/2]) # edge bins fix
    diff_neg = np.array([diff[0]/2] +(diff/2).tolist()) # edge bins fix
    delta = diff_pos+diff_neg
    new_c_1 = np.interp(x-diff_neg, xpc, c, 
                       left=0.0, right=0.0)
    new_c_2 = np.interp(x+diff_pos, xpc, c, 
                       left=0.0, right=0.0)
    new_f = ((new_c_2 - new_c_1)/delta)*fp.unit
    x = x*xp.unit
       
  else:
    # Interpolate !
    # print ('************** interpolating')

    new_f = np.interp(x, xp, fp, left=0.0, right=0.0)

  new_f[np.isnan(new_f)] = 0
  new_f = (new_f.value)*fp.unit
 
#    func = interpolate.interp1d(xp, fp, kind='quadratic', bounds_error=None, fill_value=0.0)
#    new_f  = func(x)*fp.unit
  '''
  import matplotlib.pyplot as plt
  plt.plot(xp, fp, '-')
  plt.plot(x, new_f, '.-')
  plt.show()
  # check
  print np.trapz(new_f, x)
  idx = np.where(np.logical_and(xp>= x.min(), xp <= x.max()))
  print np.trapz(fp[idx], xp[idx])
  '''
  return x, new_f
//  [python]
def rebin(x, xp, fp):
  ''' Resample a function fp(xp) over the new grid x, rebinning if necessary, 
    otherwise interpolates
    Parameters
    ----------
    x	: 	array like
	New coordinates
    fp 	:	array like
	y-coordinates to be resampled
    xp 	:	array like
	x-coordinates at which fp are sampled
	
    Returns
    -------
    out	: 	array like
	new samples  
  '''
  
  if (x.unit != xp.unit):
    print (x.unit, xp.unit)
    jexosim_error('Units mismatch')
    
    
# ============================================================================
  fp[np.isnan(fp)] = 0
  # first select non-zero elements
  idx_x = np.argwhere(x>0).T[0]
  idx_xp = np.argwhere(xp>0).T[0]

  x_ = x[idx_x]
  xp_ = xp[idx_xp]
  fp_ = fp[idx_xp]

  # select elements in input grid within wl range of new grid
  idx = np.where(np.logical_and(xp_ > 0.9*x_.min(), xp_ < 1.1*x_.max()))[0]
  xp = xp_[idx]
  fp = fp_[idx]

  #To check that the binning or interp is applied correctly uncomment following
  # print (abs(np.diff(xp_)).min() ,  abs(np.diff(x_)).min())

  # compare the resolutions of the non-zero arrays and thus bin or interpolate
  if abs(np.diff(xp_).min()) < abs(np.diff(x_).min()):
      
    # print ('**************  binning')    # Binning!
  
    c = cumulative_trapezoid(fp, x=xp)
    xpc = xp[1:].value
    x = x.value
    diff = np.diff(x)
    diff_pos = np.array((diff/2).tolist() +[diff[-1]/2]) # edge bins fix
    diff_neg = np.array([diff[0]/2] +(diff/2).tolist()) # edge bins fix
    delta = diff_pos+diff_neg
    new_c_1 = np.interp(x-diff_neg, xpc, c, 
                       left=0.0, right=0.0)
    new_c_2 = np.interp(x+diff_pos, xpc, c, 
                       left=0.0, right=0.0)
    new_f = ((new_c_2 - new_c_1)/delta)*fp.unit
    x = x*xp.unit
       
  else:
    # Interpolate !
    # print ('************** interpolating')

    new_f = np.interp(x, xp, fp, left=0.0, right=0.0)

  new_f[np.isnan(new_f)] = 0
  new_f = (new_f.value)*fp.unit
 
#    func = interpolate.interp1d(xp, fp, kind='quadratic', bounds_error=None, fill_value=0.0)
#    new_f  = func(x)*fp.unit
  '''
  import matplotlib.pyplot as plt
  plt.plot(xp, fp, '-')
  plt.plot(x, new_f, '.-')
  plt.show()
  # check
  print np.trapz(new_f, x)
  idx = np.where(np.logical_and(xp>= x.min(), xp <= x.max()))
  print np.trapz(fp[idx], xp[idx])
  '''
  return x, new_f
//  [python]
def __init__(self, h, A):
        self.h = np.asarray(h)
        self.A = np.asarray(A)
        self.V = scipy.integrate.cumtrapz(h, A, initial=0.)
        self.hmax = self.h.max()
        self.hmin = self.h.min()
        self.Amax = self.A.max()
        self.A_interpolator = scipy.interpolate.interp1d(self.h, self.A)
        self.V_interpolator = scipy.interpolate.interp1d(self.h, self.V)
//  [python]
def __init__(self, h, A):
        self.h = np.asarray(h)
        self.A = np.asarray(A)
        self.V = scipy.integrate.cumulative_trapezoid(h, A, initial=0.)
        self.hmax = self.h.max()
        self.hmin = self.h.min()
        self.Amax = self.A.max()
        self.A_interpolator = scipy.interpolate.interp1d(self.h, self.A)
        self.V_interpolator = scipy.interpolate.interp1d(self.h, self.V)
//  [python]
def integrateMomentTS(r, F):
    r"""
    Integrate moment from time series of forces at nr radial stations

    Compute 
        M_j =  \int_{r_j}^(r_n) f(r) * (r-r_j) dr  for j=1,nr
        M_j =  \int_{r_j}^(r_n) f(r) *r*dr  - r_j * \int_(r_j}^{r_n} f(r) dr
      j are the columns of M

    NOTE: simply trapezoidal integration is used. 
    The "r" term is not integrated analytically. This can be improved!

    INPUTS:
      - r: array of size nr, of radial stations (ordered)
      - F: array nt x nr of time series of forces at each radial stations
    OUTPUTS:
      - M: array nt x nr of integrated moment at each radial station

    """
    import scipy.integrate as si
    # Compute \int_{r_j}^{r_n} f(r) dr, with "j" each column 
    IF = np.fliplr(-si.cumtrapz(np.fliplr(F), r[-1::-1]))
    # Compute \int_{r_j}^{r_n} f(r)*r dr, with "j" each column 
    FR  = F * r 
    IFR = np.fliplr(-si.cumtrapz(np.fliplr(FR), r[-1::-1]))
    # Compute x_j * \int_{r_j}^(r_n) f(r) * r dr
    R_IF = IF * r[:-1]
    # \int_{r_j}^(r_n) f(r) * (r-r_j) dr  = IF + IFR
    M = IFR - R_IF


    # --- Sanity checks
    M0  = integrateMoment(r, F[0,:])
    Mm1 = integrateMoment(r, F[-1,:])
    if np.max(np.abs(M0-M[0,:]))>1e-8:
        raise Exception('>>> Inaccuracies in integrateMomentTS')
    if np.max(np.abs(Mm1-M[-1,:]))>1e-8:
        raise Exception('>>> Inaccuracies in integrateMomentTS')

    return M
//  [python]
def integrateMomentTS(r, F):
    r"""
    Integrate moment from time series of forces at nr radial stations

    Compute 
        M_j =  \int_{r_j}^(r_n) f(r) * (r-r_j) dr  for j=1,nr
        M_j =  \int_{r_j}^(r_n) f(r) *r*dr  - r_j * \int_(r_j}^{r_n} f(r) dr
      j are the columns of M

    NOTE: simply trapezoidal integration is used. 
    The "r" term is not integrated analytically. This can be improved!

    INPUTS:
      - r: array of size nr, of radial stations (ordered)
      - F: array nt x nr of time series of forces at each radial stations
    OUTPUTS:
      - M: array nt x nr of integrated moment at each radial station

    """
    import scipy.integrate as si
    # Compute \int_{r_j}^{r_n} f(r) dr, with "j" each column 
    IF = np.fliplr(-si.cumulative_trapezoid(np.fliplr(F), r[-1::-1]))
    # Compute \int_{r_j}^{r_n} f(r)*r dr, with "j" each column 
    FR  = F * r 
    IFR = np.fliplr(-si.cumulative_trapezoid(np.fliplr(FR), r[-1::-1]))
    # Compute x_j * \int_{r_j}^(r_n) f(r) * r dr
    R_IF = IF * r[:-1]
    # \int_{r_j}^(r_n) f(r) * (r-r_j) dr  = IF + IFR
    M = IFR - R_IF


    # --- Sanity checks
    M0  = integrateMoment(r, F[0,:])
    Mm1 = integrateMoment(r, F[-1,:])
    if np.max(np.abs(M0-M[0,:]))>1e-8:
        raise Exception('>>> Inaccuracies in integrateMomentTS')
    if np.max(np.abs(Mm1-M[-1,:]))>1e-8:
        raise Exception('>>> Inaccuracies in integrateMomentTS')

    return M
//  [python]
def integrate_time_series(time, data, fs):
    """Integrate time series data with given frequency

    :param data: x values (m/s^2 or m/s)
    :param fs: sampling frequency (Hz)
    :return: x_int, y_int, z_int
    """
    from scipy import integrate

    y = integrate.cumtrapz(data, time, initial=data[0])

    return y
//  [python]
def integrate_time_series(time, data, fs):
    """Integrate time series data with given frequency

    :param data: x values (m/s^2 or m/s)
    :param fs: sampling frequency (Hz)
    :return: x_int, y_int, z_int
    """
    from scipy import integrate

    y = integrate.cumulative_trapezoid(data, time, initial=data[0])

    return y
//  [python]
def __fill_area_between(self, ax, ev_dfs: typ.Iterable[pd.DataFrame],
                            ev_names: typ.List[str], color, MY_DPI: int = 96):
        """
        Plot graphs of multiple battery output data for a given simulation and
        fill area between them. Plot area on the axis given by `ax`.
        """
        for ev_df in ev_dfs:
            ev_df['timestep_time'] = ev_df['timestep_time'].dt.time.map(
                lambda x: dt.datetime(1900,1,1,x.hour,x.minute,x.second))

        # TODO: Make `rolling_window` function argument
        rolling_window = 3600  # seconds

        power_dfs = []
        dist_dfs = []
        time_dfs = []
        for ev_df in ev_dfs:
            power_df = ev_df['vehicle_energyConsumed'] * 3.6 * 1000

            t = ev_df['timestep_time'] - ev_df['timestep_time'][0]
            t = t.map(lambda t: t.total_seconds())
            dist = integrate.cumtrapz(ev_df['vehicle_speed'], t)/1000
            dist_df = pd.Series(dist / 1000)
            dist_dfs.append(dist_df)

            time_df = ev_df['timestep_time']
            time_dfs.append(time_df)

            power_df = power_df.rolling(rolling_window, center=True).mean()
            # power_df = power_df.tolist()
            power_dfs.append(power_df)
        # power_min_df = np.nanmin(power_dfs, axis=0)
        # power_max_df = np.nanmax(power_dfs, axis=0)
        power_min_df = pd.concat(power_dfs, axis=1).min(axis=1)
        power_max_df = pd.concat(power_dfs, axis=1).max(axis=1)
        # Plot
        # time = np.linspace(
        #     dt.time(hour=0), dt.time(hour=23, minute=59, second=59),
        #     len(power_max_df)
        # )
        # min_time = np.nanmin(time_dfs)
        # max_time = np.nanmax(time_dfs)
        min_time = pd.concat(time_dfs, axis=1).min(axis=1).min(axis=0)
        max_time = pd.concat(time_dfs, axis=1).max(axis=1).max(axis=0)
        time = pd.date_range(min_time, max_time, freq='S')
        # ax.plot(time, power_min_df, color=color, alpha=0.6)
        # ax.plot(time, power_max_df, color=color, alpha=0.6)
        ax.fill_between(time, power_min_df, power_max_df, color=color,
                        facecolor='#00000000', label="Power distribution",
                        hatch='.....')
        ax.legend()