jiahuimbzuai/CoQuIR huggingface dataset:
//  [python]
def calc_cav_dp(asig):
    """
    Calculates standardized cumulative absolute velocity

    ref:
    Campbell KW, Bozorgnia Y. Predictive equations for the horizontal component of standardized
    cumulative absolute velocity as adapted for use in the shutdown of U.S. nuclear power plants.
    Nucl Eng Des 2011;241:2558-69.

    :param asig:
    :return:
    """
    from scipy.integrate import trapz
    start = 0
    pga_max = 0
    cav_dp = 0
    points_per_sec = (int(1 / asig.dt))
    total_seconds = int(asig.time[-1])
    cav_dp_1_series = []
    acc_in_g = asig.values / 9.81

    for i in range(0, total_seconds):

        end = start + points_per_sec

        interval_total_time = (start * asig.dt) + 1
        interval_time = np.arange(start * asig.dt, interval_total_time, asig.dt)

        acc_interval = []
        for j in range(start, end + 1):
            acc_interval.append(acc_in_g[j])

        acc_interval = np.array(acc_interval)
        abs_acc_interval = abs(acc_interval)

        x_lower = start * asig.dt  # the lower limit of x
        x_upper = end * asig.dt  # the upper limit of x
        x_int = interval_time[np.where((x_lower <= interval_time) * (interval_time <= x_upper))]
        y_int = np.abs(np.array(abs_acc_interval)[np.where((x_lower <= interval_time) * (interval_time <= x_upper))])
        int_acc = trapz(y_int, x_int)

        # calculation of pga (g)
        pga = (max(abs_acc_interval))
        if pga > pga_max:
            pga_max = pga

        if (pga - 0.025) < 0:
            h = 0
        elif (pga - 0.025) >= 0:
            h = 1
        else:
            raise ValueError("cannot evaluate pga: {0}".format(pga))

        cav_dp = cav_dp + (h * int_acc)
        cav_dp_1_series.append(cav_dp)
        start = end
    t1s = np.arange(total_seconds)
    cav_dp_time_series = np.interp(asig.time, t1s, cav_dp_1_series)
    return cav_dp_time_series
//  [python]
def calc_cav_dp(asig):
    """
    Calculates standardized cumulative absolute velocity

    ref:
    Campbell KW, Bozorgnia Y. Predictive equations for the horizontal component of standardized
    cumulative absolute velocity as adapted for use in the shutdown of U.S. nuclear power plants.
    Nucl Eng Des 2011;241:2558-69.

    :param asig:
    :return:
    """
    from scipy.integrate import trapezoid
    start = 0
    pga_max = 0
    cav_dp = 0
    points_per_sec = (int(1 / asig.dt))
    total_seconds = int(asig.time[-1])
    cav_dp_1_series = []
    acc_in_g = asig.values / 9.81

    for i in range(0, total_seconds):

        end = start + points_per_sec

        interval_total_time = (start * asig.dt) + 1
        interval_time = np.arange(start * asig.dt, interval_total_time, asig.dt)

        acc_interval = []
        for j in range(start, end + 1):
            acc_interval.append(acc_in_g[j])

        acc_interval = np.array(acc_interval)
        abs_acc_interval = abs(acc_interval)

        x_lower = start * asig.dt  # the lower limit of x
        x_upper = end * asig.dt  # the upper limit of x
        x_int = interval_time[np.where((x_lower <= interval_time) * (interval_time <= x_upper))]
        y_int = np.abs(np.array(abs_acc_interval)[np.where((x_lower <= interval_time) * (interval_time <= x_upper))])
        int_acc = trapezoid(y_int, x_int)

        # calculation of pga (g)
        pga = (max(abs_acc_interval))
        if pga > pga_max:
            pga_max = pga

        if (pga - 0.025) < 0:
            h = 0
        elif (pga - 0.025) >= 0:
            h = 1
        else:
            raise ValueError("cannot evaluate pga: {0}".format(pga))

        cav_dp = cav_dp + (h * int_acc)
        cav_dp_1_series.append(cav_dp)
        start = end
    t1s = np.arange(total_seconds)
    cav_dp_time_series = np.interp(asig.time, t1s, cav_dp_1_series)
    return cav_dp_time_series
//  [python]
def p_value(
    data: xr.Dataset, point: Any, *, x: str, y: str, null: str = "mean"
) -> xr.DataArray:
    """Calculates the p value of a ``point`` from a Dataset containing x-y-pairs. It is assumed the integral under y
    is normalised for the p-value to be meaningful. The p-value can be calculated wrt to the mean or the mode
    of the distribution

    :param data: ``xr.Dataset`` containing the x and p(x) values
    :param point: point at which to calculate the p value
    :param x: label of the x-values; can be a variable in the dataset or a coordinate
    :param y: function values; assumed to be normalised
    :param null: (optional) null wrt which the p-value is to be calculated; can be either ``mean``
        or ``mode``
    :return: ``xr.DataArray`` of the p-value of ``point``
    """

    # x can be both a variable and a coordinate
    if x in data.coords.keys():
        _x_vals = data.coords[x]
    else:
        _x_vals = data[x]

    if isinstance(point, xr.DataArray):
        point = point.data

    # Calculate the value of the null of the distribution
    m = (
        mean(data, x=x, y=y).data
        if null == "mean"
        else mode(data, x=x, y=y)["mode_x"].data
    )

    # Calculate the index of the point
    t_index = np.argmin(np.abs(_x_vals - point).data)

    # Calculate the p-value depending on the location of the point
    if point >= m:
        return xr.DataArray(
            scipy.integrate.trapz(data[y][t_index:], _x_vals[t_index:]),
            name="p_value",
        )

    else:
        return xr.DataArray(
            scipy.integrate.trapz(data[y][:t_index], _x_vals[:t_index]),
            name="p_value",
        )
//  [python]
def p_value(
    data: xr.Dataset, point: Any, *, x: str, y: str, null: str = "mean"
) -> xr.DataArray:
    """Calculates the p value of a ``point`` from a Dataset containing x-y-pairs. It is assumed the integral under y
    is normalised for the p-value to be meaningful. The p-value can be calculated wrt to the mean or the mode
    of the distribution

    :param data: ``xr.Dataset`` containing the x and p(x) values
    :param point: point at which to calculate the p value
    :param x: label of the x-values; can be a variable in the dataset or a coordinate
    :param y: function values; assumed to be normalised
    :param null: (optional) null wrt which the p-value is to be calculated; can be either ``mean``
        or ``mode``
    :return: ``xr.DataArray`` of the p-value of ``point``
    """

    # x can be both a variable and a coordinate
    if x in data.coords.keys():
        _x_vals = data.coords[x]
    else:
        _x_vals = data[x]

    if isinstance(point, xr.DataArray):
        point = point.data

    # Calculate the value of the null of the distribution
    m = (
        mean(data, x=x, y=y).data
        if null == "mean"
        else mode(data, x=x, y=y)["mode_x"].data
    )

    # Calculate the index of the point
    t_index = np.argmin(np.abs(_x_vals - point).data)

    # Calculate the p-value depending on the location of the point
    if point >= m:
        return xr.DataArray(
            scipy.integrate.trapezoid(data[y][t_index:], _x_vals[t_index:]),
            name="p_value",
        )

    else:
        return xr.DataArray(
            scipy.integrate.trapezoid(data[y][:t_index], _x_vals[:t_index]),
            name="p_value",
        )
//  [python]
def ABoff(self):
      '''Compute the AB offset for this filter. Due to the way SNooPy stores
      the zero-points, this only depends on filter function shape.'''
      return 65.4469-48.6-self.zp + \
            2.5*num.log10(scipy.integrate.trapz(self.flux/self.wave,self.wave))
//  [python]
def ABoff(self):
      '''Compute the AB offset for this filter. Due to the way SNooPy stores
      the zero-points, this only depends on filter function shape.'''
      return 65.4469-48.6-self.zp + \
            2.5*num.log10(scipy.integrate.trapezoid(self.flux/self.wave,self.wave))
//  [python]
def test_liquid_motor_thrust_parameters(
    liquid_motor, pressurant_tank, fuel_tank, oxidizer_tank
):
    """Tests the LiquidMotor class thrust parameters.

    Parameters
    ----------
    liquid_motor : rocketpy.LiquidMotor
        The LiquidMotor object to be used in the tests.
    pressurant_tank : rocketpy.Tank
        The expected pressurant tank.
    fuel_tank : rocketpy.Tank
        The expected fuel tank.
    oxidizer_tank : rocketpy.Tank
        The expected oxidizer tank.
    """
    expected_thrust = np.loadtxt("data/SEBLM/test124_Thrust_Curve.csv", delimiter=",")
    expected_mass_flow = (
        pressurant_tank.net_mass_flow_rate
        + fuel_tank.net_mass_flow_rate
        + oxidizer_tank.net_mass_flow_rate
    )
    expected_total_impulse = scipy.integrate.trapz(
        expected_thrust[:, 1], expected_thrust[:, 0]
    )

    assert pytest.approx(liquid_motor.thrust.y_array) == expected_thrust[:, 1]
    assert (
        pytest.approx(liquid_motor.mass_flow_rate.y_array) == expected_mass_flow.y_array
    )
    assert pytest.approx(liquid_motor.total_impulse) == expected_total_impulse
//  [python]
def test_liquid_motor_thrust_parameters(
    liquid_motor, pressurant_tank, fuel_tank, oxidizer_tank
):
    """Tests the LiquidMotor class thrust parameters.

    Parameters
    ----------
    liquid_motor : rocketpy.LiquidMotor
        The LiquidMotor object to be used in the tests.
    pressurant_tank : rocketpy.Tank
        The expected pressurant tank.
    fuel_tank : rocketpy.Tank
        The expected fuel tank.
    oxidizer_tank : rocketpy.Tank
        The expected oxidizer tank.
    """
    expected_thrust = np.loadtxt("data/SEBLM/test124_Thrust_Curve.csv", delimiter=",")
    expected_mass_flow = (
        pressurant_tank.net_mass_flow_rate
        + fuel_tank.net_mass_flow_rate
        + oxidizer_tank.net_mass_flow_rate
    )
    expected_total_impulse = scipy.integrate.trapezoid(
        expected_thrust[:, 1], expected_thrust[:, 0]
    )

    assert pytest.approx(liquid_motor.thrust.y_array) == expected_thrust[:, 1]
    assert (
        pytest.approx(liquid_motor.mass_flow_rate.y_array) == expected_mass_flow.y_array
    )
    assert pytest.approx(liquid_motor.total_impulse) == expected_total_impulse
//  [python]
def get_integrated_data(self, channel, scale_factor : float=1.0) -> pd.DataFrame:
        '''
        Returns a channel of the data integrated from zero bias. I wouldn't use this if your spectrum doesn't
        cross zero bias. I'll probably add that later on when I feel less lazy :^)

        Parameters
        ----------
        channel : string
            The channel to integrate
        scale_factor : float, optional
            Scale factor applied to the data after integration. Default is 1.

        Returns
        -------
        data : pd.DataFrame
            A pandas DataFrame with 'Bias calc (V)' as the first column and 'Integrated {channel}' as the second column.
        '''
        integrated_data = np.empty(len(self.data))
        i0 = self.zero_bias_index
        for i in range(i0):
            integrated_data[i] = -scipy.integrate.trapz(self.data[channel][i:i0], self.data['Bias calc (V)'][i:i0])
        for i in range(i0, len(self.data)):
            integrated_data[i] = scipy.integrate.trapz(self.data[channel][i0:i], self.data['Bias calc (V)'][i0:i])
        data = pd.DataFrame({'Bias calc (V)' : self.data['Bias calc (V)'], f'Integrated {channel}' : integrated_data*scale_factor})
        return data
//  [python]
def get_integrated_data(self, channel, scale_factor : float=1.0) -> pd.DataFrame:
        '''
        Returns a channel of the data integrated from zero bias. I wouldn't use this if your spectrum doesn't
        cross zero bias. I'll probably add that later on when I feel less lazy :^)

        Parameters
        ----------
        channel : string
            The channel to integrate
        scale_factor : float, optional
            Scale factor applied to the data after integration. Default is 1.

        Returns
        -------
        data : pd.DataFrame
            A pandas DataFrame with 'Bias calc (V)' as the first column and 'Integrated {channel}' as the second column.
        '''
        integrated_data = np.empty(len(self.data))
        i0 = self.zero_bias_index
        for i in range(i0):
            integrated_data[i] = -scipy.integrate.trapezoid(self.data[channel][i:i0], self.data['Bias calc (V)'][i:i0])
        for i in range(i0, len(self.data)):
            integrated_data[i] = scipy.integrate.trapezoid(self.data[channel][i0:i], self.data['Bias calc (V)'][i0:i])
        data = pd.DataFrame({'Bias calc (V)' : self.data['Bias calc (V)'], f'Integrated {channel}' : integrated_data*scale_factor})
        return data
//  [python]
def findRayleigh(self):
        '''Computes the Rayleigh wave speed for every dislocation character self.theta.'''
        Rayleigh=np.zeros((self.Ntheta))
        norm = self.C2[3,3] # use c44
        C2norm = UnVoigt(self.C2/norm)
        if self.vcrit_all is None or len(self.vcrit_all[0])!=self.Ntheta or np.any(self.vcrit_all[0]!=self.theta):
            self.computevcrit(set_screwedge=False) ## need it as an upper bound on the Rayleigh speed
        vcrit = self.vcrit_all[1]
        for th in range(self.Ntheta):
            def Rayleighcond(B):
                return abs((B[0,0]+B[1,1])/2-np.sqrt((B[0,0]-B[1,1])**2/4 + B[0,1]**2))
            def findrayleigh(x):
                tmpC = C2norm - self.Cv[:,:,:,:,th]*x**2
                M=self.M[:,th].T
                N=self.N[:,th].T
                MM = elbrak1d(M,M,tmpC)
                MN = elbrak1d(M,N,tmpC)
                NM = elbrak1d(N,M,tmpC)
                NN = elbrak1d(N,N,tmpC)
                NNinv = np.linalg.inv(NN)
                S = - NNinv @ NM
                B = MM + MN @ S
                return Rayleighcond(np.trapz(B,x=self.phi,axis=0)/(4*np.pi**2))
            bounds=(0.0,vcrit[th]*np.sqrt(self.rho/norm))
            result = optimize.minimize_scalar(findrayleigh,method='bounded',bounds=bounds,options={'xatol':1e-12})
            # if result.fun>=1e-3: print(f"{bounds}\n{result}")  ## if this failed, try enlarging the search interval slightly above vcrit (there was some numerical uncertainty there too):
            if result.success and result.fun>=1e-3: result = optimize.minimize_scalar(findrayleigh,method='bounded',bounds=(0.5*bounds[1],1.25*bounds[1]),options={'xatol':1e-12})
            if result.fun>=1e-3 or not result.success: print(f"Failed: Rayleigh not found in [{bounds[0]},{1.25*bounds[1]}]\n",result)
            if result.success and result.fun<1e-3: Rayleigh[th] = result.x * np.sqrt(norm/self.rho)
        self.Rayleigh = Rayleigh
        return Rayleigh
//  [python]
def findRayleigh(self):
        '''Computes the Rayleigh wave speed for every dislocation character self.theta.'''
        Rayleigh=np.zeros((self.Ntheta))
        norm = self.C2[3,3] # use c44
        C2norm = UnVoigt(self.C2/norm)
        if self.vcrit_all is None or len(self.vcrit_all[0])!=self.Ntheta or np.any(self.vcrit_all[0]!=self.theta):
            self.computevcrit(set_screwedge=False) ## need it as an upper bound on the Rayleigh speed
        vcrit = self.vcrit_all[1]
        for th in range(self.Ntheta):
            def Rayleighcond(B):
                return abs((B[0,0]+B[1,1])/2-np.sqrt((B[0,0]-B[1,1])**2/4 + B[0,1]**2))
            def findrayleigh(x):
                tmpC = C2norm - self.Cv[:,:,:,:,th]*x**2
                M=self.M[:,th].T
                N=self.N[:,th].T
                MM = elbrak1d(M,M,tmpC)
                MN = elbrak1d(M,N,tmpC)
                NM = elbrak1d(N,M,tmpC)
                NN = elbrak1d(N,N,tmpC)
                NNinv = np.linalg.inv(NN)
                S = - NNinv @ NM
                B = MM + MN @ S
                return Rayleighcond(integrate.trapezoid(B,x=self.phi,axis=0)/(4*np.pi**2))
            bounds=(0.0,vcrit[th]*np.sqrt(self.rho/norm))
            result = optimize.minimize_scalar(findrayleigh,method='bounded',bounds=bounds,options={'xatol':1e-12})
            # if result.fun>=1e-3: print(f"{bounds}\n{result}")  ## if this failed, try enlarging the search interval slightly above vcrit (there was some numerical uncertainty there too):
            if result.success and result.fun>=1e-3: result = optimize.minimize_scalar(findrayleigh,method='bounded',bounds=(0.5*bounds[1],1.25*bounds[1]),options={'xatol':1e-12})
            if result.fun>=1e-3 or not result.success: print(f"Failed: Rayleigh not found in [{bounds[0]},{1.25*bounds[1]}]\n",result)
            if result.success and result.fun<1e-3: Rayleigh[th] = result.x * np.sqrt(norm/self.rho)
        self.Rayleigh = Rayleigh
        return Rayleigh
//  [python]
def test_distances_between_densities():
    """Tests the Hellinger distance between distributions"""
    _x = np.linspace(-5, 5, 500)
    Gaussian = xr.DataArray(
        np.exp(-(_x**2) / 2), dims=["x"], coords=dict(x=_x)
    )  # mean = 0, std = 1
    Gaussian /= scipy.integrate.trapz(Gaussian.data, _x)

    assert ops.Hellinger_distance(Gaussian, Gaussian) == 0
    assert ops.relative_entropy(Gaussian, Gaussian) == 0
    assert ops.Lp_distance(Gaussian, Gaussian, p=2) == 0

    # Test calculating the distances on a xr.Dataset instead
    Gaussian = xr.Dataset(dict(_x=Gaussian.coords["x"], y=Gaussian))
    assert ops.Hellinger_distance(Gaussian, Gaussian, x="_x", y="y") == 0
    Gaussian = xr.Dataset(dict(y=Gaussian["y"]))
    assert ops.Hellinger_distance(Gaussian, Gaussian, x="x", y="y") == 0

    _x = np.linspace(0, 5, 500)
    Uniform1 = xr.DataArray(
        np.array([1 if 1 <= x <= 3 else 0 for x in _x]), dims=["x"], coords=dict(x=_x)
    )
    Uniform2 = xr.DataArray(
        np.array([1 if 2 <= x <= 4 else 0 for x in _x]), dims=["x"], coords=dict(x=_x)
    )

    # Total area where the two do not overlap is 2, so Hellinger distance = 1/2 * 2 = 1
    assert ops.Hellinger_distance(Uniform1, Uniform2) == pytest.approx(1.0, abs=1e-2)

    # Test interpolation works: shifted uniform distribution, area of non-overlap = 3, so Hellinger
    # distance = 3/2
    Uniform1 = xr.DataArray(
        np.array([1 if 2 <= x <= 4 else 0 for x in np.linspace(1, 6, 500)]),
        dims=["x"],
        coords=dict(x=np.linspace(1, 6, 500)),
    )
    Uniform2 = xr.DataArray(
        np.array([1 if 3 <= x else 0 for x in np.linspace(2, 7, 750)]),
        dims=["x"],
        coords=dict(x=np.linspace(2, 7, 750)),
    )

    assert ops.Hellinger_distance(Uniform1, Uniform2) == pytest.approx(1.5, abs=1e-2)
//  [python]
def test_distances_between_densities():
    """Tests the Hellinger distance between distributions"""
    _x = np.linspace(-5, 5, 500)
    Gaussian = xr.DataArray(
        np.exp(-(_x**2) / 2), dims=["x"], coords=dict(x=_x)
    )  # mean = 0, std = 1
    Gaussian /= scipy.integrate.trapezoid(Gaussian.data, _x)

    assert ops.Hellinger_distance(Gaussian, Gaussian) == 0
    assert ops.relative_entropy(Gaussian, Gaussian) == 0
    assert ops.Lp_distance(Gaussian, Gaussian, p=2) == 0

    # Test calculating the distances on a xr.Dataset instead
    Gaussian = xr.Dataset(dict(_x=Gaussian.coords["x"], y=Gaussian))
    assert ops.Hellinger_distance(Gaussian, Gaussian, x="_x", y="y") == 0
    Gaussian = xr.Dataset(dict(y=Gaussian["y"]))
    assert ops.Hellinger_distance(Gaussian, Gaussian, x="x", y="y") == 0

    _x = np.linspace(0, 5, 500)
    Uniform1 = xr.DataArray(
        np.array([1 if 1 <= x <= 3 else 0 for x in _x]), dims=["x"], coords=dict(x=_x)
    )
    Uniform2 = xr.DataArray(
        np.array([1 if 2 <= x <= 4 else 0 for x in _x]), dims=["x"], coords=dict(x=_x)
    )

    # Total area where the two do not overlap is 2, so Hellinger distance = 1/2 * 2 = 1
    assert ops.Hellinger_distance(Uniform1, Uniform2) == pytest.approx(1.0, abs=1e-2)

    # Test interpolation works: shifted uniform distribution, area of non-overlap = 3, so Hellinger
    # distance = 3/2
    Uniform1 = xr.DataArray(
        np.array([1 if 2 <= x <= 4 else 0 for x in np.linspace(1, 6, 500)]),
        dims=["x"],
        coords=dict(x=np.linspace(1, 6, 500)),
    )
    Uniform2 = xr.DataArray(
        np.array([1 if 3 <= x else 0 for x in np.linspace(2, 7, 750)]),
        dims=["x"],
        coords=dict(x=np.linspace(2, 7, 750)),
    )

    assert ops.Hellinger_distance(Uniform1, Uniform2) == pytest.approx(1.5, abs=1e-2)
//  [python]
data["N"] = N
data["n0"] = n0
data["dn"] = dn

count = 0
for err in err_list:
    print("error tolerance = ", "{:.2e}".format(err))
    ac = PronyAC(G_w, w, optimize=False, err=err)
    
    if singular_type == "discrete":
        pole_weight = ac.pole_weight.real
        pole_location = ac.pole_location.real
    else:
        pole_weight = ac.pole_weight
        pole_location = ac.pole_location
    data[str(count) + "/sigma"] = ac.p_o.sigma
    data[str(count) + "/A_i"] = pole_weight
    data[str(count) + "/x_i"] = pole_location
    gf2 = GreenFunc(statistics, beta, "discrete", A_i=pole_weight, x_i=pole_location)
    x0 = np.linspace(-10, 10, 1000000)
    y0 = np.abs(gf2.get_spectral(x0, epsilon = 0.01) - gf.get_spectral(x0,  epsilon = 0.01))
    err = integrate.trapz(y0, x0)
    data[str(count) + "/err"] = err
    print("done!")
    count += 1
//  [python]
data["N"] = N
data["n0"] = n0
data["dn"] = dn

count = 0
for err in err_list:
    print("error tolerance = ", "{:.2e}".format(err))
    ac = PronyAC(G_w, w, optimize=False, err=err)
    
    if singular_type == "discrete":
        pole_weight = ac.pole_weight.real
        pole_location = ac.pole_location.real
    else:
        pole_weight = ac.pole_weight
        pole_location = ac.pole_location
    data[str(count) + "/sigma"] = ac.p_o.sigma
    data[str(count) + "/A_i"] = pole_weight
    data[str(count) + "/x_i"] = pole_location
    gf2 = GreenFunc(statistics, beta, "discrete", A_i=pole_weight, x_i=pole_location)
    x0 = np.linspace(-10, 10, 1000000)
    y0 = np.abs(gf2.get_spectral(x0, epsilon = 0.01) - gf.get_spectral(x0,  epsilon = 0.01))
    err = integrate.trapezoid(y0, x0)
    data[str(count) + "/err"] = err
    print("done!")
    count += 1
//  [python]
def test_norm(self) -> None:
        """
        Tests whether distribution function is normalized, and integrates to 1.
        """
        # setting up integration from 0 to 10*vTh
        xData1D = np.arange(0, 10.01, 0.01) * self.vTh
        yData1D = Maxwellian_speed_1D(v=xData1D, T=self.T, particle=self.particle)
        # integrating, this should be close to 1
        integ = spint.trapz(y=yData1D, x=xData1D)
        exceptStr = "Integral of distribution function should be 1."
        assert np.isclose(integ, 1), exceptStr
//  [python]
def test_norm(self) -> None:
        """
        Tests whether distribution function is normalized, and integrates to 1.
        """
        # setting up integration from 0 to 10*vTh
        xData1D = np.arange(0, 10.01, 0.01) * self.vTh
        yData1D = Maxwellian_speed_1D(v=xData1D, T=self.T, particle=self.particle)
        # integrating, this should be close to 1
        integ = spint.trapezoid(y=yData1D, x=xData1D)
        exceptStr = "Integral of distribution function should be 1."
        assert np.isclose(integ.value, 1), exceptStr
//  [python]
def test_joint_and_marginal_2D():
    """Test two-dimensional joint distributions and marginals from joints are correctly calculated"""

    # Generate a 2D-Gaussian on a square domain
    _lower, _upper, _n_vals = -5, +5, 1000
    _bins = 50
    _m, _std = 0.0, 1.0
    _x, _y = (_upper - _lower) * np.random.rand(_n_vals) + _lower, (
        _upper - _lower
    ) * np.random.rand(_n_vals) + _lower
    _f = np.exp(-((_x - _m) ** 2) / (2 * _std**2)) * np.exp(
        -((_y - _m) ** 2) / (2 * _std**2)
    )

    # Calculate the joint distribution
    joint = ops.joint_2D(_x, _y, _f, bins=_bins, normalize=1.0)
    assert set(joint.coords.keys()) == {"x", "y"}

    # Assert the maximum of the joint is roughly in the middle
    assert all(
        [
            idx == pytest.approx(25, abs=2)
            for idx in np.unravel_index(joint.argmax(), joint.shape)
        ]
    )

    # Assert the marginal distribution of each dimension is normalized
    marginal_x = ops.marginal_from_joint(joint, parameter="x")
    marginal_y = ops.marginal_from_joint(joint, parameter="y")
    assert scipy.integrate.trapz(marginal_x["y"], marginal_x["x"]) == pytest.approx(
        1.0, abs=1e-4
    )
    assert scipy.integrate.trapz(marginal_y["y"], marginal_y["x"]) == pytest.approx(
        1.0, abs=1e-4
    )

    # Assert the marginals are again approximately Gaussian
    assert ops.mean(marginal_x, x="x", y="y") == pytest.approx(0.0, abs=1e-1)
    assert ops.mean(marginal_y, x="x", y="y") == pytest.approx(0.0, abs=1e-1)
    assert ops.std(marginal_x, x="x", y="y") == pytest.approx(1.0, abs=5e-2)
    assert ops.std(marginal_y, x="x", y="y") == pytest.approx(1.0, abs=5e-2)

    # Assert alternative joint operation does the same thing
    joint_from_ds = ops.joint_2D_ds(
        xr.Dataset(dict(x=_x, y=_y)), _f, x="x", y="y", bins=_bins, normalize=1.0
    )
    assert joint_from_ds.equals(joint)

    # Assert 3D joint
    _z = (_upper - _lower) * np.random.rand(_n_vals) + _lower
    samples = (
        ops.concat(
            [xr.DataArray(_x), xr.DataArray(_y), xr.DataArray(_z)],
            "parameter",
            ["a", "b", "c"],
        )
        .transpose()
        .assign_coords(dict(dim_0=np.arange(_n_vals)))
    )

    _f = xr.DataArray(_f * np.exp(-((_z - _m) ** 2) / (2 * _std**2))).assign_coords(
        dict(dim_0=np.arange(_n_vals))
    )

    joint_3D = ops.joint_DD(samples, _f, bins=50)
    assert set(joint_3D.coords) == {"a", "b", "c"}
//  [python]
def test_joint_and_marginal_2D():
    """Test two-dimensional joint distributions and marginals from joints are correctly calculated"""

    # Generate a 2D-Gaussian on a square domain
    _lower, _upper, _n_vals = -5, +5, 1000
    _bins = 50
    _m, _std = 0.0, 1.0
    _x, _y = (_upper - _lower) * np.random.rand(_n_vals) + _lower, (
        _upper - _lower
    ) * np.random.rand(_n_vals) + _lower
    _f = np.exp(-((_x - _m) ** 2) / (2 * _std**2)) * np.exp(
        -((_y - _m) ** 2) / (2 * _std**2)
    )

    # Calculate the joint distribution
    joint = ops.joint_2D(_x, _y, _f, bins=_bins, normalize=1.0)
    assert set(joint.coords.keys()) == {"x", "y"}

    # Assert the maximum of the joint is roughly in the middle
    assert all(
        [
            idx == pytest.approx(25, abs=2)
            for idx in np.unravel_index(joint.argmax(), joint.shape)
        ]
    )

    # Assert the marginal distribution of each dimension is normalized
    marginal_x = ops.marginal_from_joint(joint, parameter="x")
    marginal_y = ops.marginal_from_joint(joint, parameter="y")
    assert scipy.integrate.trapezoid(marginal_x["y"], marginal_x["x"]) == pytest.approx(
        1.0, abs=1e-4
    )
    assert scipy.integrate.trapezoid(marginal_y["y"], marginal_y["x"]) == pytest.approx(
        1.0, abs=1e-4
    )

    # Assert the marginals are again approximately Gaussian
    assert ops.mean(marginal_x, x="x", y="y") == pytest.approx(0.0, abs=1e-1)
    assert ops.mean(marginal_y, x="x", y="y") == pytest.approx(0.0, abs=1e-1)
    assert ops.std(marginal_x, x="x", y="y") == pytest.approx(1.0, abs=5e-2)
    assert ops.std(marginal_y, x="x", y="y") == pytest.approx(1.0, abs=5e-2)

    # Assert alternative joint operation does the same thing
    joint_from_ds = ops.joint_2D_ds(
        xr.Dataset(dict(x=_x, y=_y)), _f, x="x", y="y", bins=_bins, normalize=1.0
    )
    assert joint_from_ds.equals(joint)

    # Assert 3D joint
    _z = (_upper - _lower) * np.random.rand(_n_vals) + _lower
    samples = (
        ops.concat(
            [xr.DataArray(_x), xr.DataArray(_y), xr.DataArray(_z)],
            "parameter",
            ["a", "b", "c"],
        )
        .transpose()
        .assign_coords(dict(dim_0=np.arange(_n_vals)))
    )

    _f = xr.DataArray(_f * np.exp(-((_z - _m) ** 2) / (2 * _std**2))).assign_coords(
        dict(dim_0=np.arange(_n_vals))
    )

    joint_3D = ops.joint_DD(samples, _f, bins=50)
    assert set(joint_3D.coords) == {"a", "b", "c"}
//  [python]
def test_stat_function():
    """Tests the statistics functions on a normal distribution"""
    _x = np.linspace(-5, 5, 1000)
    _m, _std = 0.0, 1.0
    _f = np.exp(-((_x - _m) ** 2) / (2 * _std**2))
    _norm = scipy.integrate.trapz(_f, _x)

    ds = xr.Dataset(dict(y=(["x"], _f)), coords=dict(x=_x))

    # Test normalisation
    ds = ops.normalize(ds, x="x", y="y")
    assert scipy.integrate.trapz(ds["y"], ds.coords["x"]) == pytest.approx(
        1.0, 1e-4
    )

    # Test mean
    mean = ops.stat_function(ds, stat="mean", x="x", y="y")
    assert mean == pytest.approx(_m, abs=1e-3)

    # Test mean with x as a coordinate or variable
    ds = xr.Dataset(dict(y=(["x"], ds["y"].data), x_val=ds.coords["x"]))
    assert mean == ops.stat_function(ds, stat="mean", x="x_val", y="y")
    assert mean == ops.stat_function(ds, stat="mean", x="x", y="y")

    # Test standard deviation
    std = ops.stat_function(ds, stat="std", x="x", y="y")
    assert std == pytest.approx(_std, abs=1e-3)

    # Test interquartile range
    iqr = ops.stat_function(ds, stat="iqr", x="x", y="y")
    assert iqr == pytest.approx(1.34, abs=1e-2)

    # Test mode
    mode = ops.stat_function(ds, stat="mode", x="x", y="y")
    assert mode["mode_x"].data.item() == pytest.approx(_m, abs=1e-2)
    assert mode["mode_y"].data.item() == pytest.approx(1.0 / _norm, abs=1e-2)

    # Test peak width calculation
    peak_widths = ops.stat_function(ds, stat="avg_peak_width", x="x", y="y", width=1)
    assert peak_widths["mean_peak_width"] == pytest.approx(2.355 * _std, abs=1e-2)
    assert peak_widths["peak_width_std"] == 0.0

    # Test p-value calculation
    assert ops.p_value(ds, 0.0, x="x", y="y") == pytest.approx(0.5, abs=1e-1)
    assert ops.p_value(ds, -1, x="x", y="y") == pytest.approx(0.159, abs=5e-3)
    assert ops.p_value(ds, -1, x="x", y="y") == pytest.approx(
        ops.p_value(ds, +1, x="x", y="y"), abs=5e-3
    )
    assert ops.p_value(ds, xr.DataArray(0.0), x="x", y="y") == ops.p_value(
        ds, 0.0, x="x", y="y"
    )

    # Assert the p-value for a Gaussian wrt the mean is the same as wrt to the mode
    assert ops.p_value(ds, -1, x="x", y="y", null="mean") == ops.p_value(
        ds, -1, x="x", y="y", null="mode"
    )
//  [python]
def test_stat_function():
    """Tests the statistics functions on a normal distribution"""
    _x = np.linspace(-5, 5, 1000)
    _m, _std = 0.0, 1.0
    _f = np.exp(-((_x - _m) ** 2) / (2 * _std**2))
    _norm = scipy.integrate.trapezoid(_f, _x)

    ds = xr.Dataset(dict(y=(["x"], _f)), coords=dict(x=_x))

    # Test normalisation
    ds = ops.normalize(ds, x="x", y="y")
    assert scipy.integrate.trapezoid(ds["y"], ds.coords["x"]) == pytest.approx(
        1.0, 1e-4
    )

    # Test mean
    mean = ops.stat_function(ds, stat="mean", x="x", y="y")
    assert mean == pytest.approx(_m, abs=1e-3)

    # Test mean with x as a coordinate or variable
    ds = xr.Dataset(dict(y=(["x"], ds["y"].data), x_val=ds.coords["x"]))
    assert mean == ops.stat_function(ds, stat="mean", x="x_val", y="y")
    assert mean == ops.stat_function(ds, stat="mean", x="x", y="y")

    # Test standard deviation
    std = ops.stat_function(ds, stat="std", x="x", y="y")
    assert std == pytest.approx(_std, abs=1e-3)

    # Test interquartile range
    iqr = ops.stat_function(ds, stat="iqr", x="x", y="y")
    assert iqr == pytest.approx(1.34, abs=1e-2)

    # Test mode
    mode = ops.stat_function(ds, stat="mode", x="x", y="y")
    assert mode["mode_x"].data.item() == pytest.approx(_m, abs=1e-2)
    assert mode["mode_y"].data.item() == pytest.approx(1.0 / _norm, abs=1e-2)

    # Test peak width calculation
    peak_widths = ops.stat_function(ds, stat="avg_peak_width", x="x", y="y", width=1)
    assert peak_widths["mean_peak_width"] == pytest.approx(2.355 * _std, abs=1e-2)
    assert peak_widths["peak_width_std"] == 0.0

    # Test p-value calculation
    assert ops.p_value(ds, 0.0, x="x", y="y") == pytest.approx(0.5, abs=1e-1)
    assert ops.p_value(ds, -1, x="x", y="y") == pytest.approx(0.159, abs=5e-3)
    assert ops.p_value(ds, -1, x="x", y="y") == pytest.approx(
        ops.p_value(ds, +1, x="x", y="y"), abs=5e-3
    )
    assert ops.p_value(ds, xr.DataArray(0.0), x="x", y="y") == ops.p_value(
        ds, 0.0, x="x", y="y"
    )

    # Assert the p-value for a Gaussian wrt the mean is the same as wrt to the mode
    assert ops.p_value(ds, -1, x="x", y="y", null="mean") == ops.p_value(
        ds, -1, x="x", y="y", null="mode"
    )
//  [python]
def test_norm(self) -> None:
        """
        Tests whether distribution function is normalized, and integrates to 1.
        """
        # setting up integration from 0 to 10*vTh
        xData1D = np.arange(0, 10.01, 0.01) * self.vTh
        yData1D = Maxwellian_speed_3D(v=xData1D, T=self.T, particle=self.particle)
        # integrating, this should be close to 1
        integ = spint.trapz(y=yData1D, x=xData1D)
        exceptStr = "Integral of distribution function should be 1."
        assert np.isclose(integ, 1), exceptStr
//  [python]
def test_norm(self) -> None:
        """
        Tests whether distribution function is normalized, and integrates to 1.
        """
        # setting up integration from 0 to 10*vTh
        xData1D = np.arange(0, 10.01, 0.01) * self.vTh
        yData1D = Maxwellian_speed_3D(v=xData1D, T=self.T, particle=self.particle)
        # integrating, this should be close to 1
        integ = spint.trapezoid(y=yData1D, x=xData1D)
        exceptStr = "Integral of distribution function should be 1."
        assert np.isclose(integ.value, 1), exceptStr
//  [python]
def test_norm(self) -> None:
        """
        Tests whether distribution function is normalized, and integrates to 1.
        """
        # setting up integration from 0 to 10*vTh
        xData1D = np.arange(0, 10.001, 0.001) * self.vTh
        yData1D = Maxwellian_speed_2D(v=xData1D, T=self.T, particle=self.particle)
        # integrating, this should be close to 1
        integ = spint.trapz(y=yData1D, x=xData1D)
        exceptStr = "Integral of distribution function should be 1."
        assert np.isclose(integ, 1), exceptStr
//  [python]
def test_norm(self) -> None:
        """
        Tests whether distribution function is normalized, and integrates to 1.
        """
        # setting up integration from 0 to 10*vTh
        xData1D = np.arange(0, 10.001, 0.001) * self.vTh
        yData1D = Maxwellian_speed_2D(v=xData1D, T=self.T, particle=self.particle)
        # integrating, this should be close to 1
        integ = spint.trapezoid(y=yData1D, x=xData1D)
        exceptStr = "Integral of distribution function should be 1."
        assert np.isclose(integ.value, 1), exceptStr
//  [python]
def integrate_local_pattern(values, ax_val, index):
    """Returns the local integral of values along given axis (does not change the axis)

    Parameters
    ----------
    values: ndarray
        array to derivate
    ax_val: ndarray
        axis values
    index: int
        index of axis along which to derivate
    Nper: int
        number of periods to replicate
    is_aper: bool
        True if values is anti-periodic along axis
    is_phys: bool
        True if physical quantity (time/angle/z)
    is_freqs: bool
        True if frequency axis

    Returns
    -------
    values_integ: ndarray
        local integration of values
    """

    if ax_val.size > 1:
        # Swap axis to always have integration axis on 1st position
        values = np.swapaxes(values, index, 0)

        # Init output arrays
        shape = list(values.shape[1:])
        shape.insert(0, values.shape[0] - 1)
        values_int = np.zeros(shape, dtype=values.dtype)
        ax_int = np.zeros(shape[0])

        # Trapezoidal integration on each segment and calculate position of each segment middle
        for ii in range(shape[0]):
            values_int[ii, ...] = scp_int.trapz(
                values[ii : ii + 2, ...], x=ax_val[ii : ii + 2], axis=0
            )
            ax_int[ii] = np.mean(ax_val[ii : ii + 2])

        # Remove zero length interval
        Ia = np.nonzero(np.diff(ax_val))[0]
        values_int = values_int[Ia, ...]
        ax_int = ax_int[Ia]

        values_int = np.swapaxes(values_int, index, 0)

    else:
        raise Exception("Cannot locally integrate along axis if axis size is 1")

    return values_int, ax_int
//  [python]
def integrate_local_pattern(values, ax_val, index):
    """Returns the local integral of values along given axis (does not change the axis)

    Parameters
    ----------
    values: ndarray
        array to derivate
    ax_val: ndarray
        axis values
    index: int
        index of axis along which to derivate
    Nper: int
        number of periods to replicate
    is_aper: bool
        True if values is anti-periodic along axis
    is_phys: bool
        True if physical quantity (time/angle/z)
    is_freqs: bool
        True if frequency axis

    Returns
    -------
    values_integ: ndarray
        local integration of values
    """

    if ax_val.size > 1:
        # Swap axis to always have integration axis on 1st position
        values = np.swapaxes(values, index, 0)

        # Init output arrays
        shape = list(values.shape[1:])
        shape.insert(0, values.shape[0] - 1)
        values_int = np.zeros(shape, dtype=values.dtype)
        ax_int = np.zeros(shape[0])

        # Trapezoidal integration on each segment and calculate position of each segment middle
        for ii in range(shape[0]):
            values_int[ii, ...] = scp_int.trapezoid(
                values[ii : ii + 2, ...], x=ax_val[ii : ii + 2], axis=0
            )
            ax_int[ii] = np.mean(ax_val[ii : ii + 2])

        # Remove zero length interval
        Ia = np.nonzero(np.diff(ax_val))[0]
        values_int = values_int[Ia, ...]
        ax_int = ax_int[Ia]

        values_int = np.swapaxes(values_int, index, 0)

    else:
        raise Exception("Cannot locally integrate along axis if axis size is 1")

    return values_int, ax_int
//  [python]
data["N"] = N
data["n0"] = n0
data["dn"] = dn

count = 0
for i in range(5):
    noise = noise_list[i]
    print("noise = ", noise)
    w = w_list[i]
    G_w = G_w_list[i]

    ac = PronyAC(G_w[n_sample], w[n_sample], optimize=True)

    if singular_type == "discrete":
        pole_weight = ac.pole_weight.real
        pole_location = ac.pole_location.real
    else:
        pole_weight = ac.pole_weight
        pole_location = ac.pole_location
    data[str(count) + "/noise"] = noise
    data[str(count) + "/sigma"] = ac.p_o.sigma
    data[str(count) + "/A_i"] = pole_weight
    data[str(count) + "/x_i"] = pole_location
    gf2 = GreenFunc(statistics, beta, "discrete", A_i=pole_weight, x_i=pole_location)
    x0 = np.linspace(-10, 10, 1000000)
    y0 = np.abs(gf2.get_spectral(x0, epsilon = 0.01) - gf.get_spectral(x0,  epsilon = 0.01))
    err = integrate.simps(y0, x0)
    data[str(count) + "/err"] = err
    print("done!")
    
    count += 1
//  [python]
data["N"] = N
data["n0"] = n0
data["dn"] = dn

count = 0
for i in range(5):
    noise = noise_list[i]
    print("noise = ", noise)
    w = w_list[i]
    G_w = G_w_list[i]

    ac = PronyAC(G_w[n_sample], w[n_sample], optimize=True)

    if singular_type == "discrete":
        pole_weight = ac.pole_weight.real
        pole_location = ac.pole_location.real
    else:
        pole_weight = ac.pole_weight
        pole_location = ac.pole_location
    data[str(count) + "/noise"] = noise
    data[str(count) + "/sigma"] = ac.p_o.sigma
    data[str(count) + "/A_i"] = pole_weight
    data[str(count) + "/x_i"] = pole_location
    gf2 = GreenFunc(statistics, beta, "discrete", A_i=pole_weight, x_i=pole_location)
    x0 = np.linspace(-10, 10, 1000000)
    y0 = np.abs(gf2.get_spectral(x0, epsilon = 0.01) - gf.get_spectral(x0,  epsilon = 0.01))
    err = integrate.trapezoid(y0, x0)
    data[str(count) + "/err"] = err
    print("done!")
    
    count += 1
//  [python]
def test_generic_motor_thrust_parameters(generic_motor):
    """Tests the GenericMotor thrust parameters.

    Parameters
    ----------
    generic_motor : rocketpy.GenericMotor
        The GenericMotor object to be used in the tests.
    """
    expected_thrust = np.array(
        [(t, thrust_source(t)) for t in np.linspace(*burn_time, 50)]
    )
    expected_total_impulse = scipy.integrate.trapz(
        expected_thrust[:, 1], expected_thrust[:, 0]
    )
    expected_exhaust_velocity = expected_total_impulse / propellant_initial_mass
    expected_mass_flow_rate = -1 * expected_thrust[:, 1] / expected_exhaust_velocity

    # Discretize mass flow rate for testing purposes
    mass_flow_rate = generic_motor.total_mass_flow_rate.set_discrete(*burn_time, 50)

    assert generic_motor.thrust.y_array == pytest.approx(expected_thrust[:, 1])
    assert generic_motor.total_impulse == pytest.approx(expected_total_impulse)
    assert generic_motor.exhaust_velocity.average(*burn_time) == pytest.approx(
        expected_exhaust_velocity
    )
    assert mass_flow_rate.y_array == pytest.approx(expected_mass_flow_rate)
//  [python]
def test_generic_motor_thrust_parameters(generic_motor):
    """Tests the GenericMotor thrust parameters.

    Parameters
    ----------
    generic_motor : rocketpy.GenericMotor
        The GenericMotor object to be used in the tests.
    """
    expected_thrust = np.array(
        [(t, thrust_source(t)) for t in np.linspace(*burn_time, 50)]
    )
    expected_total_impulse = scipy.integrate.trapezoid(
        expected_thrust[:, 1], expected_thrust[:, 0]
    )
    expected_exhaust_velocity = expected_total_impulse / propellant_initial_mass
    expected_mass_flow_rate = -1 * expected_thrust[:, 1] / expected_exhaust_velocity

    # Discretize mass flow rate for testing purposes
    mass_flow_rate = generic_motor.total_mass_flow_rate.set_discrete(*burn_time, 50)

    assert generic_motor.thrust.y_array == pytest.approx(expected_thrust[:, 1])
    assert generic_motor.total_impulse == pytest.approx(expected_total_impulse)
    assert generic_motor.exhaust_velocity.average(*burn_time) == pytest.approx(
        expected_exhaust_velocity
    )
    assert mass_flow_rate.y_array == pytest.approx(expected_mass_flow_rate)
//  [python]
def integral(self, **kwargs):
        """
        Cumulative integral plot of the normalized signal log envelope

        Represents the power distribution variation in time for the signal.
        This is a plot of the function $F(x)$ such as :

        $ F(x) = \int_0^x env(x) dx $

        Where e(x) is the signal envelope.
        """
        # sanitize the kwargs
        plot_kwargs = self.sanitize_kwargs(kwargs)

        # Compute log envelope and log time
        log_envelope, log_time = self.parent.normalize().log_envelope()


        # compute the cumulative integral
        integral = [s.trapz(log_envelope[:i], log_time[:i]) for i in np.arange(2, len(log_envelope), 1)]
        integral /= np.max(integral)

        # plot the integral
        plt.plot(log_time[2:], integral, **plot_kwargs)

        # Add labels and scale
        plt.xlabel('time (s)')
        plt.ylabel('cumulative power (normalized)')
        plt.xscale('log')
        plt.grid('on')
//  [python]
def integral(self, **kwargs):
        """
        Cumulative integral plot of the normalized signal log envelope

        Represents the power distribution variation in time for the signal.
        This is a plot of the function $F(x)$ such as :

        $ F(x) = \int_0^x env(x) dx $

        Where e(x) is the signal envelope.
        """
        # sanitize the kwargs
        plot_kwargs = self.sanitize_kwargs(kwargs)

        # Compute log envelope and log time
        log_envelope, log_time = self.parent.normalize().log_envelope()


        # compute the cumulative integral
        integral = [trapezoid(log_envelope[:i], log_time[:i]) for i in np.arange(2, len(log_envelope), 1)]
        integral /= np.max(integral)

        # plot the integral
        plt.plot(log_time[2:], integral, **plot_kwargs)

        # Add labels and scale
        plt.xlabel('time (s)')
        plt.ylabel('cumulative power (normalized)')
        plt.xscale('log')
        plt.grid('on')
//  [python]
def bin_hist(self):
        """
        Histogram of the frequency bin power

        Frequency bin power is computed as the integral of the bin envelope.
        The power value is non dimensional and normalized. 

        See guitarsounds.parameters.sound_parameters().bins.info() for the
        frequency bin frequency intervals.
        """
        # Compute the bin powers
        bin_strings = list(self.bins.keys())
        integrals = []

        for f_bin in bin_strings:
            log_envelope, log_time = self.bins[f_bin].normalize().log_envelope()
            integral = s.trapz(log_envelope, log_time)
            integrals.append(integral)
        max_value = np.max(integrals)
        integrals = np.array(integrals)/max_value

        # create the bar plotting vectors
        fig, ax = plt.subplots(figsize=(6, 6))

        x = np.arange(0, len(bin_strings))
        y = integrals
        ax.bar(x, y, tick_label=list(bin_strings))
        ax.set_xlabel("frequency bin name")
        ax.set_ylabel("frequency bin power (normalized)")
//  [python]
def bin_hist(self):
        """
        Histogram of the frequency bin power

        Frequency bin power is computed as the integral of the bin envelope.
        The power value is non dimensional and normalized. 

        See guitarsounds.parameters.sound_parameters().bins.info() for the
        frequency bin frequency intervals.
        """
        # Compute the bin powers
        bin_strings = list(self.bins.keys())
        integrals = []

        for f_bin in bin_strings:
            log_envelope, log_time = self.bins[f_bin].normalize().log_envelope()
            integral = trapezoid(log_envelope, log_time)
            integrals.append(integral)
        max_value = np.max(integrals)
        integrals = np.array(integrals)/max_value

        # create the bar plotting vectors
        fig, ax = plt.subplots(figsize=(6, 6))

        x = np.arange(0, len(bin_strings))
        y = integrals
        ax.bar(x, y, tick_label=list(bin_strings))
        ax.set_xlabel("frequency bin name")
        ax.set_ylabel("frequency bin power (normalized)")
//  [python]
def bin_power_table(self):
        """
        Displays a table with the signal power contained in every frequency bin
        
        The power is computed as the time integral of the signal, such as :

            $ P = \int_0^{t_{max}} sig(t) dt $
        
        __ Multiple SoundPack Method __
        The sounds are always normalized before computing the power. Because 
        the signal amplitude is normalized between -1 and 1, the power value 
        is adimentional, and can only be used to compare two sounds between  
        eachother.
        """
        # Bin power distribution table
        bin_strings = self.bin_strings
        integrals = []

        # for every sound in the SoundPack
        for sound in self.sounds:

            integral = []
            # for every frequency bin in the sound
            for f_bin in bin_strings:
                log_envelope, log_time = sound.bins[f_bin].normalize().log_envelope()
                integral.append(s.integrate.trapz(log_envelope, log_time))

            # a list of dict for every sound
            integrals.append(integral)

        # make the table
        table_data = np.array([list(bin_strings), *integrals]).transpose()
        sound_names = [sound.name for sound in self.sounds]

        print('___ Signal Power Frequency Bin Distribution ___ \n')
        print(tabulate(table_data, headers=['bin', *sound_names]))