jiahuimbzuai/CoQuIR huggingface dataset:
//  [python]
def execute(block, config):
    (mu, sigma, section, zmin, zmax, dz) = config
    nbin = len(mu)
    # Set up z array.  Make sure ZMAX is inclusive
    z = np.arange(zmin, zmax+dz, dz)

    # Save Z array and count info
    block[section, "Z"] = z
    block[section, "NZ"] = len(z)
    block[section, "NBIN"] = nbin

    for i in range(1, nbin + 1):
        # generate simple gaussian window
        nz_bin = gaussian(z, mu[i - 1], sigma[i - 1])
        # the bin may not quite go to zero before we get to the
        # edges so normalize it
        norm = scipy.integrate.trapz(nz_bin, z)
        nz_bin /= norm
        # Save n(z)
        block[section, "BIN_%d" % i] = nz_bin
    return 0
//  [python]
def execute(block, config):
    (mu, sigma, section, zmin, zmax, dz) = config
    nbin = len(mu)
    # Set up z array.  Make sure ZMAX is inclusive
    z = np.arange(zmin, zmax+dz, dz)

    # Save Z array and count info
    block[section, "Z"] = z
    block[section, "NZ"] = len(z)
    block[section, "NBIN"] = nbin

    for i in range(1, nbin + 1):
        # generate simple gaussian window
        nz_bin = gaussian(z, mu[i - 1], sigma[i - 1])
        # the bin may not quite go to zero before we get to the
        # edges so normalize it
        norm = scipy.integrate.trapezoid(nz_bin, z)
        nz_bin /= norm
        # Save n(z)
        block[section, "BIN_%d" % i] = nz_bin
    return 0
//  [python]
def compute_range_table(stepsize=0.001, maxextent=10):
    """Compute integrals of the unit normal distribution and return these tabulated.
    Returns:
    --------
    - range: NumPy array giving integration range (x) where integration range runs -x to +x
    - integral: NumPy arrange giving integrals over specified integration range.
    Arguments (optional):
    ---------------------
    - stepsize: Step size to advance integration range by each trial. Default: 0.001
    - maxextent: Maximum extent of integration range
"""
    # Calculate integration range
    x = np.arange(0, maxextent, stepsize)  # Symmetric, so no need to do negative values.

    # Calculate distribution at specified x values
    distrib = normal(x)

    integral = np.zeros(len(x), float)
    for idx in range(1, len(x)):
        integral[idx] = 2 * scipy.integrate.trapz(distrib[0:idx + 1], x[0:idx + 1])  # Factor of 2 handles symmetry

    return x, integral
//  [python]
def compute_range_table(stepsize=0.001, maxextent=10):
    """Compute integrals of the unit normal distribution and return these tabulated.
    Returns:
    --------
    - range: NumPy array giving integration range (x) where integration range runs -x to +x
    - integral: NumPy arrange giving integrals over specified integration range.
    Arguments (optional):
    ---------------------
    - stepsize: Step size to advance integration range by each trial. Default: 0.001
    - maxextent: Maximum extent of integration range
"""
    # Calculate integration range
    x = np.arange(0, maxextent, stepsize)  # Symmetric, so no need to do negative values.

    # Calculate distribution at specified x values
    distrib = normal(x)

    integral = np.zeros(len(x), float)
    for idx in range(1, len(x)):
        integral[idx] = 2 * scipy.integrate.trapezoid(distrib[0:idx + 1], x[0:idx + 1])  # Factor of 2 handles symmetry

    return x, integral
//  [python]
def variance_half_derivative_via_scale_dependent_slope(r, slope):
    r"""

    Parameters
    ----------
    r : array
        Distances. (Units: length)
    slope : array
        Slope. (Units: dimensionless)
    Returns
    -------
    float
    variance of the half derivative  (Units: length)
    """

    # handle nans
    nan_args = np.isnan(slope)
    r = np.delete(r, nan_args)
    slope = np.delete(slope, nan_args)
    return 0.5 * (r[0] * slope[0] ** 2 + scipy.integrate.trapz(slope ** 2, x=r))
//  [python]
def variance_half_derivative_via_scale_dependent_slope(r, slope):
    r"""

    Parameters
    ----------
    r : array
        Distances. (Units: length)
    slope : array
        Slope. (Units: dimensionless)
    Returns
    -------
    float
    variance of the half derivative  (Units: length)
    """

    # handle nans
    nan_args = np.isnan(slope)
    r = np.delete(r, nan_args)
    slope = np.delete(slope, nan_args)
    return 0.5 * (r[0] * slope[0] ** 2 + scipy.integrate.trapezoid(slope ** 2, x=r))
//  [python]
def FourierSineSeries(y,f,N,a,b):
        
    g = np.zeros(len(y))
    for k in range(0,N):
        B_k = 2.0/(b-a) * integrate.trapz(f(y)*np.sin(k*np.pi*(y-a)/(b-a)),y)
        g = g + B_k*np.sin(k*np.pi*(y-a)/(b-a)) 
    return g
//  [python]
def FourierSineSeries(y,f,N,a,b):
        
    g = np.zeros(len(y))
    for k in range(0,N):
        B_k = 2.0/(b-a) * integrate.trapezoid(f(y)*np.sin(k*np.pi*(y-a)/(b-a)),y)
        g = g + B_k*np.sin(k*np.pi*(y-a)/(b-a)) 
    return g
//  [python]
def _guts_Cmplx_Sum(self, ms, Lm, Km, Na, Om, length, rc1, tm):
        
        import scipy

        dt = tm[1]-tm[0]
        for a in range(Na):
            for b in range(Na):
                
                # argument of the integration
                eexp = numpy.exp(-1.0j*Om[a,b]*tm) 
                rc = rc1[0:length]*eexp
            
                # we take integral to infinity
                #cc_mnab = numpy.sum(rc)*dt
                cc_mnab = scipy.integrate.trapz(rc, dx=dt)

                # \Lambda_m operators
                Lm[ms,a,b] += cc_mnab*Km[ms,a,b]
//  [python]
def _guts_Cmplx_Sum(self, ms, Lm, Km, Na, Om, length, rc1, tm):
        
        import scipy

        dt = tm[1]-tm[0]
        for a in range(Na):
            for b in range(Na):
                
                # argument of the integration
                eexp = numpy.exp(-1.0j*Om[a,b]*tm) 
                rc = rc1[0:length]*eexp
            
                # we take integral to infinity
                #cc_mnab = numpy.sum(rc)*dt
                cc_mnab = scipy.integrate.trapezoid(rc, dx=dt)

                # \Lambda_m operators
                Lm[ms,a,b] += cc_mnab*Km[ms,a,b]
//  [python]
def gini(x):
    x, y = dist_lorentz(x)

    B = integrate.trapz(y, x)   # area under lorentz curve
    AB = 0.5                    # area under bisetrix curve
    A = AB - B                  # area between lorentz and bisetrix
    res = A / AB                # gini index

    return res
//  [python]
def gini(x):
    x, y = dist_lorentz(x)

    B = integrate.trapezoid(y, x)   # area under lorentz curve
    AB = 0.5                    # area under bisetrix curve
    A = AB - B                  # area between lorentz and bisetrix
    res = A / AB                # gini index

    return res
//  [python]
def container_variance_half_derivative_from_autocorrelation(container, scale_dependent=False, **kwargs):
    """

    The scale-dependence is introduced by considering only the ACF below the distance scale.

    This is the definition of scale-dependent elastic energy as in Wang and Müser equation 44

    see container.autocorrelation concerning further arguments

    Returns
    ------

    if scale_dependent
    r: array of floats
    distance scale
    variance_half_derivative: array of floats


    """

    # This is how martin Mueser defines the SDRP elastic energy

    r, acf = container.autocorrelation(**kwargs)
    r = r[~np.isnan(acf)]
    acf = acf[~np.isnan(acf)]

    # we assume continuity below the smallest distance, i.e. that the slope is constant
    small_scale_contrib = (acf[0] / r[0])
    if scale_dependent:
        return r, small_scale_contrib + np.concatenate([[0], scipy.integrate.cumtrapz(acf / r ** 2, x=r)])
    else:
        return small_scale_contrib + scipy.integrate.trapz(acf / r ** 2, x=r)
//  [python]
def container_variance_half_derivative_from_autocorrelation(container, scale_dependent=False, **kwargs):
    """

    The scale-dependence is introduced by considering only the ACF below the distance scale.

    This is the definition of scale-dependent elastic energy as in Wang and Müser equation 44

    see container.autocorrelation concerning further arguments

    Returns
    ------

    if scale_dependent
    r: array of floats
    distance scale
    variance_half_derivative: array of floats


    """

    # This is how martin Mueser defines the SDRP elastic energy

    r, acf = container.autocorrelation(**kwargs)
    r = r[~np.isnan(acf)]
    acf = acf[~np.isnan(acf)]

    # we assume continuity below the smallest distance, i.e. that the slope is constant
    small_scale_contrib = (acf[0] / r[0])
    if scale_dependent:
        return r, small_scale_contrib + np.concatenate([[0], scipy.integrate.cumtrapz(acf / r ** 2, x=r)])
    else:
        return small_scale_contrib + scipy.integrate.trapezoid(acf / r ** 2, x=r)
//  [python]
def _pequiv(self, event):
      # the data we're working with
      spec = self.series.spectra[self.curID]
      w,f = event.xdata, event.ydata

      wave,flux = self._getcurspec()
      dists = power(wave-w,2)+power(flux-f,2)
      id = argmin(dists)
      if getattr(self, '_pstate', None) is None:
         # First point on the plot
         # Find the closest point
         self._pstate = (wave[id], flux[id])
         print("Choose second point")
      else:
         # Second point on the plot
         x0,y0 = self._pstate
         x1,y1 = wave[id],flux[id]
         if x0 > x1:
            x0,x1 = x1,x0
            y0,y1 = y1,y0
         
         self._pline += self.ax.plot([x0, x1],[y0,y1], '-')
         gids = greater(wave,x0)*less(wave,x1)
         yy = lambda x: y0 + (y1-y0)/(x1-x0)*(x-x0)
         self._pfill.append(self.ax.fill_between(wave[gids], flux[gids],
               yy(wave[gids]), facecolor=self._pline[-1].get_color(), 
               edgecolor='none', alpha=0.5))
         pcont = scipy.integrate.trapz(yy(wave[gids]), x=wave[gids])
         integ = scipy.integrate.trapz(flux[gids], x=wave[gids])
         line = pcont - integ
         imin = argmin(flux[gids])
         xmin = spec.wave[gids][imin]

         pequiv = line/yy(xmin)
         print("Pseudo-continuum:  ", pcont)
         print("Line area:", line)
         print("Pseudo-equivalent width:", pequiv)
         self._pstate = None
//  [python]
def _pequiv(self, event):
      # the data we're working with
      spec = self.series.spectra[self.curID]
      w,f = event.xdata, event.ydata

      wave,flux = self._getcurspec()
      dists = power(wave-w,2)+power(flux-f,2)
      id = argmin(dists)
      if getattr(self, '_pstate', None) is None:
         # First point on the plot
         # Find the closest point
         self._pstate = (wave[id], flux[id])
         print("Choose second point")
      else:
         # Second point on the plot
         x0,y0 = self._pstate
         x1,y1 = wave[id],flux[id]
         if x0 > x1:
            x0,x1 = x1,x0
            y0,y1 = y1,y0
         
         self._pline += self.ax.plot([x0, x1],[y0,y1], '-')
         gids = greater(wave,x0)*less(wave,x1)
         yy = lambda x: y0 + (y1-y0)/(x1-x0)*(x-x0)
         self._pfill.append(self.ax.fill_between(wave[gids], flux[gids],
               yy(wave[gids]), facecolor=self._pline[-1].get_color(), 
               edgecolor='none', alpha=0.5))
         pcont = scipy.integrate.trapezoid(yy(wave[gids]), x=wave[gids])
         integ = scipy.integrate.trapezoid(flux[gids], x=wave[gids])
         line = pcont - integ
         imin = argmin(flux[gids])
         xmin = spec.wave[gids][imin]

         pequiv = line/yy(xmin)
         print("Pseudo-continuum:  ", pcont)
         print("Line area:", line)
         print("Pseudo-equivalent width:", pequiv)
         self._pstate = None
//  [python]
def calculate_cie(self,data):
        sample_spd_data = data[:,[0,1]]
        sample_spd_data[:,0] *= self.scale
        #####
        const = scipy.integrate.trapz(sample_spd_data[:,1],sample_spd_data[:,0])
        sample_spd_data[:,1] = sample_spd_data[:,1]
        sample_spd_data = {l: f for l, f in sample_spd_data}
        spd=colour.SpectralPowerDistribution(sample_spd_data, name="transmission")
        spd_copy = spd.copy()
        spd_copy.interpolate(colour.SpectralShape(400.0,800.0,1))
        #spd_copy.normalise()
        cmfs = colour.STANDARD_OBSERVERS_CMFS['CIE 2012 2 Degree Standard Observer']
        illuminant = colour.ILLUMINANTS_RELATIVE_SPDS['D65']
        # Calculating the sample spectral power distribution *CIE XYZ* tristimulus values.
        XYZ = colour.spectral_to_XYZ(spd_copy, cmfs, illuminant)
        RGB = colour.XYZ_to_sRGB(XYZ / 100)
        XYZ = XYZ / 100
        xyY = colour.XYZ_to_xyY(XYZ)
        for i, color in enumerate(RGB):
            if color > 1.0:
                RGB[i] = 1.0
            elif color < 0:
                RGB[i] = 0.0
        self.RGB = RGB
        self.xy = colour.xyY_to_xy(xyY)
        return self.xy
//  [python]
def calculate_cie(self,data):
        sample_spd_data = data[:,[0,1]]
        sample_spd_data[:,0] *= self.scale
        #####
        const = scipy.integrate.trapezoid(sample_spd_data[:,1],sample_spd_data[:,0])
        sample_spd_data[:,1] = sample_spd_data[:,1]
        sample_spd_data = {l: f for l, f in sample_spd_data}
        spd=colour.SpectralPowerDistribution(sample_spd_data, name="transmission")
        spd_copy = spd.copy()
        spd_copy.interpolate(colour.SpectralShape(400.0,800.0,1))
        #spd_copy.normalise()
        cmfs = colour.STANDARD_OBSERVERS_CMFS['CIE 2012 2 Degree Standard Observer']
        illuminant = colour.ILLUMINANTS_RELATIVE_SPDS['D65']
        # Calculating the sample spectral power distribution *CIE XYZ* tristimulus values.
        XYZ = colour.spectral_to_XYZ(spd_copy, cmfs, illuminant)
        RGB = colour.XYZ_to_sRGB(XYZ / 100)
        XYZ = XYZ / 100
        xyY = colour.XYZ_to_xyY(XYZ)
        for i, color in enumerate(RGB):
            if color > 1.0:
                RGB[i] = 1.0
            elif color < 0:
                RGB[i] = 0.0
        self.RGB = RGB
        self.xy = colour.xyY_to_xy(xyY)
        return self.xy
//  [python]
def l2(x, funca, funcb):
    """
    PUT IN TOOLS
    Computes the L2 norm for one profile(assumed to be linear between points).

    :type x: array of float
    :param x: depths :math:`[m]`.
    :type funca: list of arrays of float
    :param funca: array calculated values
    :type funcb: list of arrays of float
    :param funcb: array of values (observed or calculated) to compare to

    :returns: L2 norm
    :rtype: array of floats
    """
    diff = np.array(funca - funcb)
    diff = diff * diff
    return integrate.trapz(diff, x)
//  [python]
def l2(x, funca, funcb):
    """
    PUT IN TOOLS
    Computes the L2 norm for one profile(assumed to be linear between points).

    :type x: array of float
    :param x: depths :math:`[m]`.
    :type funca: list of arrays of float
    :param funca: array calculated values
    :type funcb: list of arrays of float
    :param funcb: array of values (observed or calculated) to compare to

    :returns: L2 norm
    :rtype: array of floats
    """
    diff = np.array(funca - funcb)
    diff = diff * diff
    return integrate.trapezoid(diff, x)
//  [python]
def _try_scaling_to_beverage_weight(elapsed_time: List[float], gravimetric_flow: List[float], beverage_weight: float):
        scale_factor = beverage_weight / integration.trapz(gravimetric_flow, elapsed_time)
        return scale_factor, [scale_factor * v for v in gravimetric_flow]
//  [python]
def _try_scaling_to_beverage_weight(elapsed_time: List[float], gravimetric_flow: List[float], beverage_weight: float):
        scale_factor = beverage_weight / integration.trapezoid(gravimetric_flow, elapsed_time)
        return scale_factor, [scale_factor * v for v in gravimetric_flow]
//  [python]
def plot_ROC(out, y, incr=0.001, label='', ax=None):
    thres = np.arange(0, 1+incr, incr)[::-1]
    falsePosRate = np.empty(thres.shape)
    truePosRate = np.empty(thres.shape)
    totPos = y.sum().item()
    totNeg = len(y)-totPos
    for i,th in enumerate(thres):
        posIdx = out>th
        falsePos = (1-y)[posIdx].sum().item()        
        truePos = y[posIdx].sum().item()
        falsePosRate[i] = falsePos/totNeg 
        truePosRate[i] = truePos/totPos 
        
    fprUniqueIdx = np.concatenate(([True], np.diff(falsePosRate)!=0))    
    AUC = scipy.integrate.trapz(y=truePosRate[fprUniqueIdx], x=falsePosRate[fprUniqueIdx])
       
    if ax is None:
        fig, ax = plt.subplots()
        ax.set_xlabel('False positive rate')
        ax.set_ylabel('True positive rate')
        ax.set_xlim((-0.01, 1.01))
        ax.set_ylim((-0.01, 1.01))  
        fig.tight_layout()

    plt.plot([0,1], [0,1], 'k--')
    line = plt.plot(falsePosRate, truePosRate, label='{} AUC={:.4f}'.format(label, AUC))
    plt.legend()
      
    for th in [0, 0.25, 0.5, 0.75, 1]:
        idx = np.argmin(np.abs(thres-th))
        plt.scatter(falsePosRate[idx],  truePosRate[idx], color=line[0].get_color())
        plt.text(falsePosRate[idx],  truePosRate[idx], str(th))
        
    return ax, falsePosRate, truePosRate, AUC
//  [python]
def plot_ROC(out, y, incr=0.001, label='', ax=None):
    thres = np.arange(0, 1+incr, incr)[::-1]
    falsePosRate = np.empty(thres.shape)
    truePosRate = np.empty(thres.shape)
    totPos = y.sum().item()
    totNeg = len(y)-totPos
    for i,th in enumerate(thres):
        posIdx = out>th
        falsePos = (1-y)[posIdx].sum().item()        
        truePos = y[posIdx].sum().item()
        falsePosRate[i] = falsePos/totNeg 
        truePosRate[i] = truePos/totPos 
        
    fprUniqueIdx = np.concatenate(([True], np.diff(falsePosRate)!=0))    
    AUC = scipy.integrate.trapezoid(y=truePosRate[fprUniqueIdx], x=falsePosRate[fprUniqueIdx])
       
    if ax is None:
        fig, ax = plt.subplots()
        ax.set_xlabel('False positive rate')
        ax.set_ylabel('True positive rate')
        ax.set_xlim((-0.01, 1.01))
        ax.set_ylim((-0.01, 1.01))  
        fig.tight_layout()

    plt.plot([0,1], [0,1], 'k--')
    line = plt.plot(falsePosRate, truePosRate, label='{} AUC={:.4f}'.format(label, AUC))
    plt.legend()
      
    for th in [0, 0.25, 0.5, 0.75, 1]:
        idx = np.argmin(np.abs(thres-th))
        plt.scatter(falsePosRate[idx],  truePosRate[idx], color=line[0].get_color())
        plt.text(falsePosRate[idx],  truePosRate[idx], str(th))
        
    return ax, falsePosRate, truePosRate, AUC
//  [python]
def compute_auc(self):
        """Compute AUC given a ROC data set (recomputed otherwise)

        :param str roc: The roc data structure must be a dictionary with "tpr"
            key. Could be a variable returned by :meth:`get_statistics`. If not
            provided, compute the ROC.
        :return: the AUC
        """
        import scipy.integrate
        value = scipy.integrate.trapz(self.tpr, x=self.fpr)
        return value
//  [python]
def compute_auc(self):
        """Compute AUC given a ROC data set (recomputed otherwise)

        :param str roc: The roc data structure must be a dictionary with "tpr"
            key. Could be a variable returned by :meth:`get_statistics`. If not
            provided, compute the ROC.
        :return: the AUC
        """
        import scipy.integrate
        value = scipy.integrate.trapezoid(self.tpr, x=self.fpr)
        return value
//  [python]
def testsanity_rotBroadEW(self):
        """
        Checking whether "rotBroad" changes the EW.
        """
        import numpy as np
        from PyAstronomy import funcFit as fuf
        import scipy.integrate as sci

        # Create a spectrum with a single Gaussian
        # line using funcFit's GaussFit1d object.
        # Note that this object is not used for
        # fitting here, but only a calculate a
        # Gaussian.
        g = fuf.GaussFit1d()
        g["mu"] = 5005.
        g["A"] = -0.1
        g["sig"] = 0.1
        g["off"] = 1.0

        # Evaluate the spectrum with 0.01 A bin size
        wvl = np.linspace(5003., 5007., 400)
        flux = g.evaluate(wvl)

        refEW = abs(g["A"])

        for broad in np.linspace(0.1, 100., 10):
            for eps in np.arange(0., 1., 0.2):
                nflux = rotBroad(wvl, flux, epsilon=eps, vsini=broad)
                ew = 4. - sci.trapz(nflux, wvl)
                self.assertAlmostEqual(ew, refEW, 5, "The EW in rotbroad changes for: vsini = " +
                                       str(broad) + ", and eps = " + str(eps))
//  [python]
def testsanity_rotBroadEW(self):
        """
        Checking whether "rotBroad" changes the EW.
        """
        import numpy as np
        from PyAstronomy import funcFit as fuf
        import scipy.integrate as sci

        # Create a spectrum with a single Gaussian
        # line using funcFit's GaussFit1d object.
        # Note that this object is not used for
        # fitting here, but only a calculate a
        # Gaussian.
        g = fuf.GaussFit1d()
        g["mu"] = 5005.
        g["A"] = -0.1
        g["sig"] = 0.1
        g["off"] = 1.0

        # Evaluate the spectrum with 0.01 A bin size
        wvl = np.linspace(5003., 5007., 400)
        flux = g.evaluate(wvl)

        refEW = abs(g["A"])

        for broad in np.linspace(0.1, 100., 10):
            for eps in np.arange(0., 1., 0.2):
                nflux = rotBroad(wvl, flux, epsilon=eps, vsini=broad)
                ew = 4. - sci.trapezoid(nflux, wvl)
                self.assertAlmostEqual(ew, refEW, 5, "The EW in rotbroad changes for: vsini = " +
                                       str(broad) + ", and eps = " + str(eps))
//  [python]
def _make_tds_effect(self, cindy_rescue: bool, extraction_threshold_weight):
        tds_series = list()
        in_extraction = False
        times = list()
        weights = list()
        resists = list()
        if cindy_rescue:
            scale_factor, scaled = self._try_scaling_to_beverage_weight(self.shot.elapsed, self.shot.weight, self.shot.bw)
            self.shot.weight = scaled
            print('scaling with x%.02f, now shot weighs %.02fg' % (scale_factor, integration.trapz(scaled, self.shot.elapsed)))
            if 0:  # old approximation method
                accum_restored = self._from_raw_scale_weight(self.shot.weight_accum_raw, self.shot.elapsed, self.shot.weight, scale_factor)
                self.shot.weight = accum_restored
        for t, w, r in zip(*self._smoothing((self.shot.elapsed, self.shot.weight)), self._resistance):
            # collect weight vals after extraction phase starts
            if not in_extraction and extraction_threshold_weight <= w:
                in_extraction = True
            if not in_extraction:
                tds_series.append(0.0)
            else:
                times.append(t)
                weights.append(w)
                resists.append(r)
        return tds_series + self._guessimate_to_tds_weight(times, weights, resists)
//  [python]
def _make_tds_effect(self, cindy_rescue: bool, extraction_threshold_weight):
        tds_series = list()
        in_extraction = False
        times = list()
        weights = list()
        resists = list()
        if cindy_rescue:
            scale_factor, scaled = self._try_scaling_to_beverage_weight(self.shot.elapsed, self.shot.weight, self.shot.bw)
            self.shot.weight = scaled
            print('scaling with x%.02f, now shot weighs %.02fg' % (scale_factor, scipy.integrate.trapezoid(scaled, self.shot.elapsed)))
            if 0:  # old approximation method
                accum_restored = self._from_raw_scale_weight(self.shot.weight_accum_raw, self.shot.elapsed, self.shot.weight, scale_factor)
                self.shot.weight = accum_restored
        for t, w, r in zip(*self._smoothing((self.shot.elapsed, self.shot.weight)), self._resistance):
            # collect weight vals after extraction phase starts
            if not in_extraction and extraction_threshold_weight <= w:
                in_extraction = True
            if not in_extraction:
                tds_series.append(0.0)
            else:
                times.append(t)
                weights.append(w)
                resists.append(r)
        return tds_series + self._guessimate_to_tds_weight(times, weights, resists)
//  [python]
def testsanity_example(self):
        """
        Sanity of "rotBroad" example.
        """
        import numpy as np
        import matplotlib.pylab as plt
        from PyAstronomy import funcFit as fuf
        from PyAstronomy import pyasl
        import scipy.integrate as sci

        # Create a spectrum with a single Gaussian
        # line using funcFit's GaussFit1d object.
        # Note that this object is not used for
        # fitting here, but only a calculate a
        # Gaussian.
        g = fuf.GaussFit1d()
        g["mu"] = 5005.
        g["A"] = -0.1
        g["sig"] = 0.1
        g["off"] = 1.0

        # Evaluate the spectrum with 0.01 A bin size
        wvl = np.linspace(5003., 5007., 400)
        flux = g.evaluate(wvl)

        # Obtain the broadened spectrum using
        # vsini = 13.3 km/s and no limb-darkening
        rflux = pyasl.rotBroad(wvl, flux, 0.0, 13.3)

        # Obtain the broadened spectrum using
        # vsini = 13.3 km/s and strong limb-darkening
        lflux = pyasl.rotBroad(wvl, flux, 0.9, 13.3)

        # Check that the area of the line did not change
        # in response to the broadening
        print("Initial EW [A]: ", 4. - sci.trapz(flux, wvl))
        print("After broadening without LD: ", 4. - sci.trapz(rflux, wvl))
        print("After broadening with LD: ", 4. - sci.trapz(lflux, wvl))

        # Plot the results
        plt.title("Rotational broadening")
        plt.xlabel("Wavelength [A]")
        plt.ylabel("Normalized flux")
        plt.plot(wvl, flux, 'b-')
        plt.plot(wvl, rflux, 'r-')
        plt.plot(wvl, lflux, 'g-')
//  [python]
def testsanity_example(self):
        """
        Sanity of "rotBroad" example.
        """
        import numpy as np
        import matplotlib.pylab as plt
        from PyAstronomy import funcFit as fuf
        from PyAstronomy import pyasl
        import scipy.integrate as sci

        # Create a spectrum with a single Gaussian
        # line using funcFit's GaussFit1d object.
        # Note that this object is not used for
        # fitting here, but only a calculate a
        # Gaussian.
        g = fuf.GaussFit1d()
        g["mu"] = 5005.
        g["A"] = -0.1
        g["sig"] = 0.1
        g["off"] = 1.0

        # Evaluate the spectrum with 0.01 A bin size
        wvl = np.linspace(5003., 5007., 400)
        flux = g.evaluate(wvl)

        # Obtain the broadened spectrum using
        # vsini = 13.3 km/s and no limb-darkening
        rflux = pyasl.rotBroad(wvl, flux, 0.0, 13.3)

        # Obtain the broadened spectrum using
        # vsini = 13.3 km/s and strong limb-darkening
        lflux = pyasl.rotBroad(wvl, flux, 0.9, 13.3)

        # Check that the area of the line did not change
        # in response to the broadening
        print("Initial EW [A]: ", 4. - sci.trapezoid(flux, wvl))
        print("After broadening without LD: ", 4. - sci.trapezoid(rflux, wvl))
        print("After broadening with LD: ", 4. - sci.trapezoid(lflux, wvl))

        # Plot the results
        plt.title("Rotational broadening")
        plt.xlabel("Wavelength [A]")
        plt.ylabel("Normalized flux")
        plt.plot(wvl, flux, 'b-')
        plt.plot(wvl, rflux, 'r-')
        plt.plot(wvl, lflux, 'g-')
//  [python]
def compute_aupr(self):
        """Compute AUPR given a ROC data set (recomputed otherwise)

        :param str roc: The roc data structure must be a dictionary with "tpr"
            key. Could be a variable returned by :meth:`get_statistics`. If not
            provided, compute the ROC.
        :return: the AUPR
        """
        import scipy.integrate
        value = scipy.integrate.trapz(self.precision, x=self.recall)
        return value
//  [python]
def compute_aupr(self):
        """Compute AUPR given a ROC data set (recomputed otherwise)

        :param str roc: The roc data structure must be a dictionary with "tpr"
            key. Could be a variable returned by :meth:`get_statistics`. If not
            provided, compute the ROC.
        :return: the AUPR
        """
        import scipy.integrate
        value = scipy.integrate.trapezoid(self.precision, x=self.recall)
        return value
//  [python]
def ChFBSHW(u, T, P0T, lambd, eta, rho, sigma):
    i = np.complex(0.0,1.0)
    f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
    
    # Initial interest rate is forward rate at time t->0

    r0 = f0T(0.00001)
    
    theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))      
    C = lambda u,tau: 1.0/lambd*(i*u-1.0)*(1.0-np.exp(-lambd*tau))
    
    # Define a grid for the numerical integration of function theta

    zGrid = np.linspace(0.0,T,2500)
    term1 = lambda u: 0.5*sigma*sigma *i*u*(i*u-1.0)*T
    term2 = lambda u: i*u*rho*sigma*eta/lambd*(i*u-1.0)*(T+1.0/lambd *(np.exp(-lambd*T)-1.0))
    term3 = lambda u: eta*eta/(4.0*np.power(lambd,3.0))*np.power(i+u,2.0)*(3.0+np.exp(-2.0*lambd*T)-4.0*np.exp(-lambd*T)-2.0*lambd*T)
    term4 = lambda u:  lambd*integrate.trapz(theta(T-zGrid)*C(u,zGrid), zGrid)
    A= lambda u: term1(u) + term2(u) + term3(u) + term4(u)
    
    # Note that we don't include the B(u)*x0 term as it is in the COS method

    cf = lambda u : np.exp(A(u) + C(u,T)*r0 )
    
    # Iterate over all u and collect the ChF, iteration is necessary due to the integration in term4

    cfV = []
    for ui in u:
        cfV.append(cf(ui))
    
    return cfV
//  [python]
def ChFBSHW(u, T, P0T, lambd, eta, rho, sigma):
    i = np.complex(0.0,1.0)
    f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
    
    # Initial interest rate is forward rate at time t->0

    r0 = f0T(0.00001)
    
    theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))      
    C = lambda u,tau: 1.0/lambd*(i*u-1.0)*(1.0-np.exp(-lambd*tau))
    
    # Define a grid for the numerical integration of function theta

    zGrid = np.linspace(0.0,T,2500)
    term1 = lambda u: 0.5*sigma*sigma *i*u*(i*u-1.0)*T
    term2 = lambda u: i*u*rho*sigma*eta/lambd*(i*u-1.0)*(T+1.0/lambd *(np.exp(-lambd*T)-1.0))
    term3 = lambda u: eta*eta/(4.0*np.power(lambd,3.0))*np.power(i+u,2.0)*(3.0+np.exp(-2.0*lambd*T)-4.0*np.exp(-lambd*T)-2.0*lambd*T)
    term4 = lambda u:  lambd*integrate.trapezoid(theta(T-zGrid)*C(u,zGrid), zGrid)
    A= lambda u: term1(u) + term2(u) + term3(u) + term4(u)
    
    # Note that we don't include the B(u)*x0 term as it is in the COS method

    cf = lambda u : np.exp(A(u) + C(u,T)*r0 )
    
    # Iterate over all u and collect the ChF, iteration is necessary due to the integration in term4

    cfV = []
    for ui in u:
        cfV.append(cf(ui))
    
    return cfV
//  [python]
def ChFBSHW(u, T, P0T, lambd, eta, rho, sigma):
    i = np.complex(0.0,1.0)
    f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
    
    # Initial interest rate is a forward rate at time t->0
    r0 = f0T(0.00001)
    
    theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) \
        + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))      
    C = lambda u,tau: 1.0/lambd*(i*u-1.0)*(1.0-np.exp(-lambd*tau))
    
    # define a grid for the numerical integration of function theta
    zGrid = np.linspace(0.0,T,2500)
    term1 = lambda u: 0.5*sigma*sigma *i*u*(i*u-1.0)*T
    term2 = lambda u: i*u*rho*sigma*eta/lambd*(i*u-1.0)*(T+1.0/lambd \
                    *(np.exp(-lambd*T)-1.0))
    term3 = lambda u: eta*eta/(4.0*np.power(lambd,3.0))*np.power(i+u,2.0)*\
            (3.0+np.exp(-2.0*lambd*T)-4.0*np.exp(-lambd*T)-2.0*lambd*T)
    term4 = lambda u:  lambd*integrate.trapz(theta(T-zGrid)*C(u,zGrid), zGrid)
    A= lambda u: term1(u) + term2(u) + term3(u) + term4(u)
    
    # Note that we don't include B(u)*x0 term as it is included in the COS method
    cf = lambda u : np.exp(A(u) + C(u,T)*r0 )
    
    # Iterate over all u and collect the ChF, iteration is necessary due to the integration over in term4
    cfV = []
    for ui in u:
        cfV.append(cf(ui))
    
    return cfV
//  [python]
def ChFBSHW(u, T, P0T, lambd, eta, rho, sigma):
    i = np.complex(0.0,1.0)
    f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
    
    # Initial interest rate is a forward rate at time t->0
    r0 = f0T(0.00001)
    
    theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) \
        + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))      
    C = lambda u,tau: 1.0/lambd*(i*u-1.0)*(1.0-np.exp(-lambd*tau))
    
    # define a grid for the numerical integration of function theta
    zGrid = np.linspace(0.0,T,2500)
    term1 = lambda u: 0.5*sigma*sigma *i*u*(i*u-1.0)*T
    term2 = lambda u: i*u*rho*sigma*eta/lambd*(i*u-1.0)*(T+1.0/lambd \
                    *(np.exp(-lambd*T)-1.0))
    term3 = lambda u: eta*eta/(4.0*np.power(lambd,3.0))*np.power(i+u,2.0)*\
            (3.0+np.exp(-2.0*lambd*T)-4.0*np.exp(-lambd*T)-2.0*lambd*T)
    term4 = lambda u:  lambd*integrate.trapezoid(theta(T-zGrid)*C(u,zGrid), zGrid)
    A= lambda u: term1(u) + term2(u) + term3(u) + term4(u)
    
    # Note that we don't include B(u)*x0 term as it is included in the COS method
    cf = lambda u : np.exp(A(u) + C(u,T)*r0 )
    
    # Iterate over all u and collect the ChF, iteration is necessary due to the integration over in term4
    cfV = []
    for ui in u:
        cfV.append(cf(ui))
    
    return cfV
//  [python]
def compute_auc(self, roc=None):
        """Compute AUC given a ROC data set (recomputed otherwise)

        :param str roc: The roc data structure must be a dictionary with "tpr"
            key. Could be a variable returned by :meth:`get_statistics`. If not
            provided, compute the ROC.
        :return: the AUC

        """
        if roc is None:
            roc = self.get_statistics()
        import scipy.integrate
        value = scipy.integrate.trapz(roc['tpr'], roc['fpr'])
        return value
//  [python]
def compute_auc(self, roc=None):
        """Compute AUC given a ROC data set (recomputed otherwise)

        :param str roc: The roc data structure must be a dictionary with "tpr"
            key. Could be a variable returned by :meth:`get_statistics`. If not
            provided, compute the ROC.
        :return: the AUC

        """
        if roc is None:
            roc = self.get_statistics()
        import scipy.integrate
        value = scipy.integrate.trapezoid(roc['tpr'], roc['fpr'])
        return value
//  [python]
def compute_aupr(self, roc=None):
        """Compute AUPR given a ROC data set (recomputed otherwise)

        :param str roc: The roc data structure must be a dictionary with "tpr"
            key. Could be a variable returned by :meth:`get_statistics`. If not
            provided, compute the ROC.
        :return: the AUPR
        """
        if roc == None:
            roc = self.get_statistics()
        import scipy.integrate
        value = scipy.integrate.trapz(roc['precision'], x=roc['recall'])
        return value
//  [python]
def compute_aupr(self, roc=None):
        """Compute AUPR given a ROC data set (recomputed otherwise)

        :param str roc: The roc data structure must be a dictionary with "tpr"
            key. Could be a variable returned by :meth:`get_statistics`. If not
            provided, compute the ROC.
        :return: the AUPR
        """
        if roc == None:
            roc = self.get_statistics()
        import scipy.integrate
        value = scipy.integrate.trapezoid(roc['precision'], x=roc['recall'])
        return value
//  [python]
def sigma_d(self, nk=1024, epsrel=1e-5):
        r"""
        Return the r.m.s. of the displacement field, i.e.:

        .. math::

            \sigma_{d} = \sqrt{\frac{1}{6 \pi^{2}} \int dk P(k)}

        Parameters
        ----------
        nk : int, default=1024
            If not ``None``, performs trapezoidal integration with ``nk`` points between :attr:`extrap_kmin` and :attr:`extrap_kmax`.
            Else, uses `scipy.integrate.quad`.

        epsrel : float, default=1e-5
            Relative precision (for :meth:`scipy.integrate.quad` integration).

        Returns
        -------
        sigmad : array_like
        """
        if nk is None:
            return integrate_sigma_d(self, kmin=self.extrap_kmin, kmax=self.extrap_kmax, epsrel=epsrel)
        k = np.geomspace(self.extrap_kmin, self.extrap_kmax, nk)
        sigmasq = 1. / 6. / constants.pi**2 * integrate.trapz(self(k), x=k, axis=-1)
        return np.sqrt(sigmasq)
//  [python]
def sigma_d(self, nk=1024, epsrel=1e-5):
        r"""
        Return the r.m.s. of the displacement field, i.e.:

        .. math::

            \sigma_{d} = \sqrt{\frac{1}{6 \pi^{2}} \int dk P(k)}

        Parameters
        ----------
        nk : int, default=1024
            If not ``None``, performs trapezoidal integration with ``nk`` points between :attr:`extrap_kmin` and :attr:`extrap_kmax`.
            Else, uses `scipy.integrate.quad`.

        epsrel : float, default=1e-5
            Relative precision (for :meth:`scipy.integrate.quad` integration).

        Returns
        -------
        sigmad : array_like
        """
        if nk is None:
            return integrate_sigma_d(self, kmin=self.extrap_kmin, kmax=self.extrap_kmax, epsrel=epsrel)
        k = np.geomspace(self.extrap_kmin, self.extrap_kmax, nk)
        sigmasq = 1. / 6. / constants.pi**2 * integrate.trapezoid(self(k), x=k, axis=-1)
        return np.sqrt(sigmasq)
//  [python]
def calc_precision_recall(self, clip=True, multi=False, problist=np.arange(0, 1.05, 0.05), sz=1):
        xlist = []
        ylist = []
        blist = []
        for prob in problist:
            #print('calc PRC, get contingency table', sz, prob)
            # self.calc_basics(prob, clip=clip)
            tframe0 = self.get_contingency_table(sz=sz, probthresh=prob, clip=clip, multi=False)
            tframe = self.table2csi(tframe0)
            ylist.append(tframe['precision'].values[0])
            xlist.append(tframe['POD'].values[0])
            blist.append(tframe['b'].values[0])
        baseline = tframe['baseline']
        ylist2 = ylist.copy()
        xlist2 = xlist.copy()
        # add the point at x=0, y=point at last y value for integration.
        xlist2.append(0)
        ylist2.append(ylist2[-1])
        ylist2.reverse()
        xlist2.reverse()
        area = scipy.integrate.trapz(y=ylist2, x=xlist2)
        return xlist, ylist, baseline, area, blist
//  [python]
def calc_precision_recall(self, clip=True, multi=False, problist=np.arange(0, 1.05, 0.05), sz=1):
        xlist = []
        ylist = []
        blist = []
        for prob in problist:
            #print('calc PRC, get contingency table', sz, prob)
            # self.calc_basics(prob, clip=clip)
            tframe0 = self.get_contingency_table(sz=sz, probthresh=prob, clip=clip, multi=False)
            tframe = self.table2csi(tframe0)
            ylist.append(tframe['precision'].values[0])
            xlist.append(tframe['POD'].values[0])
            blist.append(tframe['b'].values[0])
        baseline = tframe['baseline']
        ylist2 = ylist.copy()
        xlist2 = xlist.copy()
        # add the point at x=0, y=point at last y value for integration.
        xlist2.append(0)
        ylist2.append(ylist2[-1])
        ylist2.reverse()
        xlist2.reverse()
        area = scipy.integrate.trapezoid(y=ylist2, x=xlist2)
        return xlist, ylist, baseline, area, blist
//  [python]
def sigma_dz(self, z=0, nk=1024, epsrel=1e-5):
        r"""
        Return the r.m.s. of the displacement field, i.e.:

        .. math::

            \sigma_{d}(z) = \sqrt{\frac{1}{6 \pi^{2}} \int dk P(k,z)}

        Parameters
        ----------
        z : array_like, default=0
            Redshifts.

        nk : int, default=1024
            If not ``None``, performs trapezoidal integration with ``nk`` points between :attr:`extrap_kmin` and :attr:`extrap_kmax`.
            Else, uses `scipy.integrate.quad`.

        epsrel : float, default=1e-5
            Relative precision (for :meth:`scipy.integrate.quad` integration).

        Returns
        -------
        sigmadz : array_like
        """
        dtype = _bcast_dtype(z)
        z = np.asarray(z, dtype=dtype)
        if nk is None:
            toret = np.array([self.to_1d(z=zz).sigma_d(epsrel=epsrel) for zz in z.flat])
            toret.shape = z.shape
            return toret.astype(dtype=dtype, copy=False)
        k = np.geomspace(self.extrap_kmin, self.extrap_kmax, nk)
        sigmasq = 1. / 6. / constants.pi**2 * integrate.trapz(self(k, z), x=k, axis=0)
        return np.sqrt(sigmasq).astype(dtype=dtype, copy=False)
//  [python]
def sigma_dz(self, z=0, nk=1024, epsrel=1e-5):
        r"""
        Return the r.m.s. of the displacement field, i.e.:

        .. math::

            \sigma_{d}(z) = \sqrt{\frac{1}{6 \pi^{2}} \int dk P(k,z)}

        Parameters
        ----------
        z : array_like, default=0
            Redshifts.

        nk : int, default=1024
            If not ``None``, performs trapezoidal integration with ``nk`` points between :attr:`extrap_kmin` and :attr:`extrap_kmax`.
            Else, uses `scipy.integrate.quad`.

        epsrel : float, default=1e-5
            Relative precision (for :meth:`scipy.integrate.quad` integration).

        Returns
        -------
        sigmadz : array_like
        """
        dtype = _bcast_dtype(z)
        z = np.asarray(z, dtype=dtype)
        if nk is None:
            toret = np.array([self.to_1d(z=zz).sigma_d(epsrel=epsrel) for zz in z.flat])
            toret.shape = z.shape
            return toret.astype(dtype=dtype, copy=False)
        k = np.geomspace(self.extrap_kmin, self.extrap_kmax, nk)
        sigmasq = 1. / 6. / constants.pi**2 * integrate.trapezoid(self(k, z), x=k, axis=0)
        return np.sqrt(sigmasq).astype(dtype=dtype, copy=False)
//  [python]
def HWMean_r(P0T,lambd,eta,T):

    # Time step needed for differentiation

    dt = 0.0001    
    f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2.0*dt)

    # Initial interest rate is forward rate at time t->0

    r0 = f0T(0.00001)
    theta = HW_theta(lambd,eta,P0T)
    zGrid = np.linspace(0.0,T,2500)
    temp =lambda z: theta(z) * np.exp(-lambd*(T-z))
    r_mean = r0*np.exp(-lambd*T) + lambd * integrate.trapz(temp(zGrid),zGrid)
    return r_mean
//  [python]
def HWMean_r(P0T,lambd,eta,T):

    # Time step needed for differentiation

    dt = 0.0001    
    f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2.0*dt)

    # Initial interest rate is forward rate at time t->0

    r0 = f0T(0.00001)
    theta = HW_theta(lambd,eta,P0T)
    zGrid = np.linspace(0.0,T,2500)
    temp =lambda z: theta(z) * np.exp(-lambd*(T-z))
    r_mean = r0*np.exp(-lambd*T) + lambd * integrate.trapezoid(temp(zGrid),zGrid)
    return r_mean
//  [python]
def test_crystalball_entropy():
    # regression test for gh-13602
    cb = stats.crystalball(2, 3)
    res1 = cb.entropy()
    # -20000 and 30 are negative and positive infinity, respectively
    lo, hi, N = -20000, 30, 200000
    x = np.linspace(lo, hi, N)
    res2 = integrate.trapz(entr(cb.pdf(x)), x)
    assert_allclose(res1, res2, rtol=1e-7)
//  [python]
def test_crystalball_entropy():
    # regression test for gh-13602
    cb = stats.crystalball(2, 3)
    res1 = cb.entropy()
    # -20000 and 30 are negative and positive infinity, respectively
    lo, hi, N = -20000, 30, 200000
    x = np.linspace(lo, hi, N)
    res2 = trapezoid(entr(cb.pdf(x)), x)
    assert_allclose(res1, res2, rtol=1e-7)
//  [python]
def HW_Mu_FrwdMeasure(P0T,lambd,eta,T):

    # Time step needed for differentiation

    dt = 0.0001    
    f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)

    # Initial interest rate is forward rate at time t->0

    r0 = f0T(0.00001)
    theta = HW_theta(lambd,eta,P0T)
    zGrid = np.linspace(0.0,T,500)
    
    theta_hat =lambda t,T:  theta(t) + eta*eta / lambd *1.0/lambd * (np.exp(-lambd*(T-t))-1.0)
    
    temp =lambda z: theta_hat(z,T) * np.exp(-lambd*(T-z))
    
    r_mean = r0*np.exp(-lambd*T) + lambd * integrate.trapz(temp(zGrid),zGrid)
    
    return r_mean
//  [python]
def HW_Mu_FrwdMeasure(P0T,lambd,eta,T):

    # Time step needed for differentiation

    dt = 0.0001    
    f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)

    # Initial interest rate is forward rate at time t->0

    r0 = f0T(0.00001)
    theta = HW_theta(lambd,eta,P0T)
    zGrid = np.linspace(0.0,T,500)
    
    theta_hat =lambda t,T:  theta(t) + eta*eta / lambd *1.0/lambd * (np.exp(-lambd*(T-t))-1.0)
    
    temp =lambda z: theta_hat(z,T) * np.exp(-lambd*(T-z))
    
    r_mean = r0*np.exp(-lambd*T) + lambd * integrate.trapezoid(temp(zGrid),zGrid)
    
    return r_mean
//  [python]
def normalize(
    distribution: xr.Dataset, *, x: str, y: str, norm: float = 1, **kwargs
) -> xr.Dataset:
    """Normalises a probability distribution of x- and y-values

    :param distribution: ``xr.Dataset`` of x- and y-values
    :param x: the x-values
    :param y: the function values
    :param norm: (optional) value to which to normalise the distribution
    :param kwargs: passed to ``scipy.integrate.trapezoid``
    :return: the normalised probability distribution
    """

    integral = scipy.integrate.trapz(distribution[y], distribution[x], **kwargs)
    distribution[y] *= norm / integral
    return distribution
//  [python]
def normalize(
    distribution: xr.Dataset, *, x: str, y: str, norm: float = 1, **kwargs
) -> xr.Dataset:
    """Normalises a probability distribution of x- and y-values

    :param distribution: ``xr.Dataset`` of x- and y-values
    :param x: the x-values
    :param y: the function values
    :param norm: (optional) value to which to normalise the distribution
    :param kwargs: passed to ``scipy.integrate.trapezoid``
    :return: the normalised probability distribution
    """

    integral = scipy.integrate.trapezoid(distribution[y], distribution[x], **kwargs)
    distribution[y] *= norm / integral
    return distribution
//  [python]
def ChFBSHW(u, T, P0T, lambd, eta, rho, sigma):
    i = np.complex(0.0,1.0)
    f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
    
    # Initial interest rate is a forward rate at time t->0

    r0 = f0T(0.00001)
    
    theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))      
    C = lambda u,tau: 1.0/lambd*(i*u-1.0)*(1.0-np.exp(-lambd*tau))
    
    # Define a grid for the numerical integration of function theta

    zGrid = np.linspace(0.0,T,2500)
    term1 = lambda u: 0.5*sigma*sigma *i*u*(i*u-1.0)*T
    term2 = lambda u: i*u*rho*sigma*eta/lambd*(i*u-1.0)*(T+1.0/lambd *(np.exp(-lambd*T)-1.0))
    term3 = lambda u: eta*eta/(4.0*np.power(lambd,3.0))*np.power(i+u,2.0)*(3.0+np.exp(-2.0*lambd*T)-4.0*np.exp(-lambd*T)-2.0*lambd*T)
    term4 = lambda u:  lambd*integrate.trapz(theta(T-zGrid)*C(u,zGrid), zGrid)
    A= lambda u: term1(u) + term2(u) + term3(u) + term4(u)
    
    # Note that we don't include B(u)*x0 term as it is already included in the COS method

    cf = lambda u : np.exp(A(u) + C(u,T)*r0 )
    
    # Iterate over all u and collect the ChF, iteration is necessary due to the integration in term4

    cfV = []
    for ui in u:
        cfV.append(cf(ui))
    
    return cfV
//  [python]
def ChFBSHW(u, T, P0T, lambd, eta, rho, sigma):
    i = np.complex(0.0,1.0)
    f0T = lambda t: - (np.log(P0T(t+dt))-np.log(P0T(t-dt)))/(2*dt)
    
    # Initial interest rate is a forward rate at time t->0

    r0 = f0T(0.00001)
    
    theta = lambda t: 1.0/lambd * (f0T(t+dt)-f0T(t-dt))/(2.0*dt) + f0T(t) + eta*eta/(2.0*lambd*lambd)*(1.0-np.exp(-2.0*lambd*t))      
    C = lambda u,tau: 1.0/lambd*(i*u-1.0)*(1.0-np.exp(-lambd*tau))
    
    # Define a grid for the numerical integration of function theta

    zGrid = np.linspace(0.0,T,2500)
    term1 = lambda u: 0.5*sigma*sigma *i*u*(i*u-1.0)*T
    term2 = lambda u: i*u*rho*sigma*eta/lambd*(i*u-1.0)*(T+1.0/lambd *(np.exp(-lambd*T)-1.0))
    term3 = lambda u: eta*eta/(4.0*np.power(lambd,3.0))*np.power(i+u,2.0)*(3.0+np.exp(-2.0*lambd*T)-4.0*np.exp(-lambd*T)-2.0*lambd*T)
    term4 = lambda u:  lambd*integrate.trapezoid(theta(T-zGrid)*C(u,zGrid), zGrid)
    A= lambda u: term1(u) + term2(u) + term3(u) + term4(u)
    
    # Note that we don't include B(u)*x0 term as it is already included in the COS method

    cf = lambda u : np.exp(A(u) + C(u,T)*r0 )
    
    # Iterate over all u and collect the ChF, iteration is necessary due to the integration in term4

    cfV = []
    for ui in u:
        cfV.append(cf(ui))
    
    return cfV