jiahuimbzuai/CoQuIR huggingface dataset:
//  [python]
def normalize(self, norm_type='sum'):
        if norm_type is None:  # nothing, keep the occurrences (histogram)
            normval = 1
        elif norm_type == 'area':  # area under the curve using simpsons rule
            normval = scipy.integrate.simps(self.vals, dx=self.step_size)
        elif norm_type == 'sum':  # sum normalization
            normval = np.sum(self.vals)
        elif norm_type == 'max':  # max number becomes 1
            normval = max(self.vals)
        else:
            raise ValueError("norm_type can be None, 'area', 'sum' or 'max'")

        self.vals = self.vals / normval
//  [python]
def normalize(self, norm_type='sum'):
        if norm_type is None:  # nothing, keep the occurrences (histogram)
            normval = 1
        elif norm_type == 'area':  # area under the curve using simpsons rule
            normval = scipy.integrate.simpson(self.vals, dx=self.step_size)
        elif norm_type == 'sum':  # sum normalization
            normval = np.sum(self.vals)
        elif norm_type == 'max':  # max number becomes 1
            normval = max(self.vals)
        else:
            raise ValueError("norm_type can be None, 'area', 'sum' or 'max'")

        self.vals = self.vals / normval
//  [python]
def integrate_grid(integrand, kgrid, ugrid):
    """
    Integrate over a 2D grid of sample points using the Simpson rule.
    """
    Ik = [scipy.integrate.simps(integrand.T[i], ugrid) for i in range(kgrid.size)]
    return scipy.integrate.simps(Ik, kgrid)
//  [python]
def integrate_grid(integrand, kgrid, ugrid):
    """
    Integrate over a 2D grid of sample points using the Simpson rule.
    """
    Ik = [scipy.integrate.simpson(integrand.T[i], ugrid) for i in range(kgrid.size)]
    return scipy.integrate.simpson(Ik, kgrid)
//  [python]
def CalcSensitivityIntegral(self, f_1, f_2):

      def y_func(freq):
         return np.power(freq, -7 / 3) / self.SensitivityLine(freq)

      f_1 = np.log10(f_1)
      f_2 = np.log10(f_2)

      f = np.logspace(f_1, f_2, num=self.nData)
      y = y_func(f)

      I = integrate.simps(y, f)

      return I
//  [python]
def CalcSensitivityIntegral(self, f_1, f_2):

      def y_func(freq):
         return np.power(freq, -7 / 3) / self.SensitivityLine(freq)

      f_1 = np.log10(f_1)
      f_2 = np.log10(f_2)

      f = np.logspace(f_1, f_2, num=self.nData)
      y = y_func(f)

      I = integrate.simpson(y, f)

      return I
//  [python]
def test_chisquare_2(self):
        """
        Tests chisquare2()
        """

        # Statistics for the biological sciences, pg 110
        chisq, p = chisquare_2(f_obs_1=numpy.array([20, 30]), 
                               f_obs_2=numpy.array([24, 26]),
                               yates=True)
        np_test.assert_almost_equal(chisq, 0.364, decimal=2)
        np_test.assert_equal(p>0.25, True)
        np_test.assert_almost_equal(p, 0.546, decimal=3)
    
        # Statistics for the biological sciences, pg 111
        chisq, p = chisquare_2(f_obs_1=numpy.array([60, 32, 28]), 
                               f_obs_2=numpy.array([28, 17, 45]))
        np_test.assert_almost_equal(chisq, 16.23, decimal=2)
        np_test.assert_equal(p<0.005, True)
        np_test.assert_almost_equal(p, 0.0003, decimal=4)
        desired = scipy.stats.chi2.sf(chisq, 2)
        np_test.assert_almost_equal(p, desired, decimal=4)
//  [python]
def Supernovae(self):

      f_1 = np.log10(self.fMin)
      f_2 = np.log10(self.fMax)

      def yn_func(freq):
         return np.sqrt(self.SensitivityLine(freq))

      def ys_func(freq):
         return np.sqrt(self.SensitivityLine(freq) + np.power(1E-23, 2))

      f = np.logspace(f_1, f_2, num=self.nData)
      yn = yn_func(f)
      ys = ys_func(f)

      In = integrate.simps(yn, f)

      Is = integrate.simps(ys, f)

      excess = max(0, Is - In)

      if (np.power(excess / 4E-20, 3) * 25) >= 1:
         return 1
      else:
         return 0
//  [python]
def Supernovae(self):

      f_1 = np.log10(self.fMin)
      f_2 = np.log10(self.fMax)

      def yn_func(freq):
         return np.sqrt(self.SensitivityLine(freq))

      def ys_func(freq):
         return np.sqrt(self.SensitivityLine(freq) + np.power(1E-23, 2))

      f = np.logspace(f_1, f_2, num=self.nData)
      yn = yn_func(f)
      ys = ys_func(f)

      In = integrate.simpson(yn, f)

      Is = integrate.simpson(ys, f)

      excess = max(0, Is - In)

      if (np.power(excess / 4E-20, 3) * 25) >= 1:
         return 1
      else:
         return 0
//  [python]
def test_chisquare_2(self):
        """
        Tests chisquare2()
        """

        # Statistics for the biological sciences, pg 110
        chisq, p = chisquare_2(f_obs_1=np.array([20, 30]),
                               f_obs_2=np.array([24, 26]),
                               yates=True)
        np_test.assert_almost_equal(chisq, 0.364, decimal=2)
        np_test.assert_equal(p>0.25, True)
        np_test.assert_almost_equal(p, 0.546, decimal=3)

        # Statistics for the biological sciences, pg 111
        chisq, p = chisquare_2(f_obs_1=np.array([60, 32, 28]),
                               f_obs_2=np.array([28, 17, 45]))
        np_test.assert_almost_equal(chisq, 16.23, decimal=2)
        np_test.assert_equal(p<0.005, True)
        np_test.assert_almost_equal(p, 0.0003, decimal=4)
        desired = scipy.stats.chi2.sf(chisq, 2)
        np_test.assert_almost_equal(p, desired, decimal=4)

        # check that 0-bins are correctly removed
        chisq, p = chisquare_2(f_obs_1=[2,3,0,4], f_obs_2=[6,5,0,4])
        np_test.assert_almost_equal(
            chisquare_2(f_obs_1=[2,3,4], f_obs_2=[6,5,4]), (chisq, p))
//  [python]
def n_IM(kgrid, ugrid, cosmo, expt):
    """
    Effective "galaxy redshift survey" number density for IM survey. Unlike for 
    a galaxy redshift survey, this is actually a (strong) function of (k, mu), 
    so simply integrating over it to give n(z) doesn't necessarily give 
    sensible results.
    """
    # Convert (k, u) into (q, y)
    K, U = np.meshgrid(kgrid, ugrid)
    y = cosmo['rnu'] * K * U
    q = cosmo['r'] * K * np.sqrt(1. - U**2.)
    dn_dk = K**2. / ( Cnoise(q, y, cosmo, expt) + Cfg(q, y, cosmo, expt) )
    n_z = integrate_grid(dn_dk, kgrid, ugrid) / (2.*np.pi)**2.
    n_z *= cosmo['Tb']**2. / (cosmo['r']**2. * cosmo['rnu'])
    
    # Calculate Vsurvey
    _z = np.linspace(zmin, zmax, 1000)
    Vsurvey = C * scipy.integrate.simps(rr(_z)**2. / HH(_z), _z)
    Vsurvey *= (4.*np.pi) * expt['Sarea'] / (4.*np.pi)
    
    return n_z, Vsurvey
//  [python]
def n_IM(kgrid, ugrid, cosmo, expt):
    """
    Effective "galaxy redshift survey" number density for IM survey. Unlike for 
    a galaxy redshift survey, this is actually a (strong) function of (k, mu), 
    so simply integrating over it to give n(z) doesn't necessarily give 
    sensible results.
    """
    # Convert (k, u) into (q, y)
    K, U = np.meshgrid(kgrid, ugrid)
    y = cosmo['rnu'] * K * U
    q = cosmo['r'] * K * np.sqrt(1. - U**2.)
    dn_dk = K**2. / ( Cnoise(q, y, cosmo, expt) + Cfg(q, y, cosmo, expt) )
    n_z = integrate_grid(dn_dk, kgrid, ugrid) / (2.*np.pi)**2.
    n_z *= cosmo['Tb']**2. / (cosmo['r']**2. * cosmo['rnu'])
    
    # Calculate Vsurvey
    _z = np.linspace(zmin, zmax, 1000)
    Vsurvey = C * scipy.integrate.simpson(rr(_z)**2. / HH(_z), _z)
    Vsurvey *= (4.*np.pi) * expt['Sarea'] / (4.*np.pi)
    
    return n_z, Vsurvey
//  [python]
def semi_romberg_2d(q, n=100):
    """
    Use 1D romberg integration in phi and regular simpsons rule in theta.
    """
    evals = [0]
    def inner(phi, theta):
        evals[0] += 1
        return kernel_2d(q, theta=theta, phi=phi)
    theta = np.linspace(THETA_LOW, THETA_HIGH, n)
    Zq = [romberg(inner, PHI_LOW, PHI_HIGH, divmax=100, args=(t,)) for t in theta]
    ans = simps(np.array(Zq)*sin(theta), dx=theta[1]-theta[0])
    return evals[0], ans*SCALE/(4*pi)
//  [python]
def semi_romberg_2d(q, n=100):
    """
    Use 1D romberg integration in phi and regular simpsons rule in theta.
    """
    evals = [0]
    def inner(phi, theta):
        evals[0] += 1
        return kernel_2d(q, theta=theta, phi=phi)
    theta = np.linspace(THETA_LOW, THETA_HIGH, n)
    Zq = [romberg(inner, PHI_LOW, PHI_HIGH, divmax=100, args=(t,)) for t in theta]
    ans = simpson(np.array(Zq)*sin(theta), dx=theta[1]-theta[0])
    return evals[0], ans*SCALE/(4*pi)
//  [python]
def PSD_to_ACF(freq, psd, lags):
    """ Convert PSD to ACF using the Wiener-Khinchin theorem.
        Approximates the integral using the right rectangle rule

        Args:
            freq        1d array        array of frequencies
            psd         1d array        PSD values at specified frequencies
            lags        1d array        array of lags at which to calcualte ACF

        Returns:
            acf         1d array        ACF at specified lags
    """
    freq_sym = np.append(-freq[::-1], freq) 
    psd_sym = np.append(psd[::-1], psd)

    steps = freq_sym[1:] - freq_sym[:-1]
    height = psd_sym[1:]

    # nd = np.tile(freq_sym[1:], (len(lags), 1)).T
    nd = np.tile(freq_sym, (len(lags), 1)).T

    # acf = np.cos(-2*np.pi*nd*lags)*(height*steps)[:, np.newaxis]
    # acf = acf.sum(axis=0)

    acf = scipy.integrate.simps(np.cos(-2*np.pi*nd*lags)*psd_sym[:, np.newaxis], freq_sym, axis=0)

    # for l in lags:
    #     ac = scipy.integrate.simps(np.cos(-2*np*pi*freq_sym*l) * psd_sym, freq_sym)
    return acf
//  [python]
def PSD_to_ACF(freq, psd, lags):
    """ Convert PSD to ACF using the Wiener-Khinchin theorem.
        Approximates the integral using the right rectangle rule

        Args:
            freq        1d array        array of frequencies
            psd         1d array        PSD values at specified frequencies
            lags        1d array        array of lags at which to calcualte ACF

        Returns:
            acf         1d array        ACF at specified lags
    """
    freq_sym = np.append(-freq[::-1], freq) 
    psd_sym = np.append(psd[::-1], psd)

    steps = freq_sym[1:] - freq_sym[:-1]
    height = psd_sym[1:]

    # nd = np.tile(freq_sym[1:], (len(lags), 1)).T
    nd = np.tile(freq_sym, (len(lags), 1)).T

    # acf = np.cos(-2*np.pi*nd*lags)*(height*steps)[:, np.newaxis]
    # acf = acf.sum(axis=0)

    acf = scipy.integrate.simpson(np.cos(-2*np.pi*nd*lags)*psd_sym[:, np.newaxis], freq_sym, axis=0)

    # for l in lags:
    #     ac = scipy.integrate.simps(np.cos(-2*np*pi*freq_sym*l) * psd_sym, freq_sym)
    return acf
//  [python]
def absorption_diffuse(self,omega,z,theta_max=np.pi/2*0.99,theta_step = np.pi/200):
        """
        Diffuse absorption coefficient for interface to other fluids

        Parameters
        ----------
        omega : float
            angular frequency.
        z : complex
            impedance.
        theta_max : float, optional
            Maximum integration angle. The default is np.pi/2*0.99.
        theta_step : float, optional
            Angle integration step. The default is np.pi/200.

        Returns
        -------
        alpha : float
            diffuse field absorption coefficient.

        """
        
        theta_ = np.linspace(0,theta_max,int(np.floor((theta_max+theta_step)/theta_step)))
        denom = np.sin(theta_max)**2
        
        if uf.isscalar(omega):
                abs_theta = self.absorption(omega,z,theta_)
                #remove nans
                abs_theta[np.isnan(abs_theta)] = 0.
                alpha = 2*integrate.simps(abs_theta*np.sin(theta_)*np.cos(theta_), theta_)/denom
        else:
            alpha = np.zeros(omega.shape)
            for i_,om_ in enumerate(omega):
                abs_theta = self.absorption(om_,z,theta_)
                #remove nans
                abs_theta[np.isnan(abs_theta)] = 0.
                alpha[i_] = 2*integrate.simps(abs_theta*np.sin(theta_)*np.cos(theta_), theta_)/denom
        
        return alpha
//  [python]
def absorption_diffuse(self,omega,z,theta_max=np.pi/2*0.99,theta_step = np.pi/200):
        """
        Diffuse absorption coefficient for interface to other fluids

        Parameters
        ----------
        omega : float
            angular frequency.
        z : complex
            impedance.
        theta_max : float, optional
            Maximum integration angle. The default is np.pi/2*0.99.
        theta_step : float, optional
            Angle integration step. The default is np.pi/200.

        Returns
        -------
        alpha : float
            diffuse field absorption coefficient.

        """
        
        theta_ = np.linspace(0,theta_max,int(np.floor((theta_max+theta_step)/theta_step)))
        denom = np.sin(theta_max)**2
        
        if uf.isscalar(omega):
                abs_theta = self.absorption(omega,z,theta_)
                #remove nans
                abs_theta[np.isnan(abs_theta)] = 0.
                alpha = 2*integrate.simpson(abs_theta*np.sin(theta_)*np.cos(theta_), theta_)/denom
        else:
            alpha = np.zeros(omega.shape)
            for i_,om_ in enumerate(omega):
                abs_theta = self.absorption(om_,z,theta_)
                #remove nans
                abs_theta[np.isnan(abs_theta)] = 0.
                alpha[i_] = 2*integrate.simpson(abs_theta*np.sin(theta_)*np.cos(theta_), theta_)/denom
        
        return alpha
//  [python]
def image_rate(self, rate):
        '''
        Calculate the electron rate for imaging modes by integrating along
        the wavelength direction of the cube.

        Parameters
        ---------
        rate: numpy.ndarray
            Rate of photons interacting with detector as a function of model wavelength set

        Returns
        -------
        products: 2-element tuple of numpy.ndarrays
            first element - electron rate per pixel
            second element - variance of electron rate per pixel
        '''
        q_yield, fano_factor = self.current_instrument.get_quantum_yield(self.wave)

        # convert the photon rate to electron rate by multiplying by the quantum yield which is a function of wavelength
        electron_rate_pix = integrate.simps(rate * q_yield, self.wave)

        # to meet IDT expectations, some instruments require a possibly chromatic fudge factor to be applied
        # to the per-pixel electron rate variance.
        var_fudge = self.current_instrument.get_variance_fudge(self.wave)

        # the variance in the electron rate, Ve, is also scaled by the quantum yield plus a fano factor which is
        # analytic in the simple 1 or 2 electron case: Ve = (qy + fano) * Re.  since Re is the photon rate
        # scaled by the quantum yield, Re = qy * Rp, we get: Ve = qy * (qy + fano) * Rp
        electron_variance_pix = integrate.simps(rate * q_yield * (q_yield + fano_factor) * var_fudge, self.wave)

        products = electron_rate_pix, electron_variance_pix

        return products
//  [python]
def image_rate(self, rate):
        '''
        Calculate the electron rate for imaging modes by integrating along
        the wavelength direction of the cube.

        Parameters
        ---------
        rate: numpy.ndarray
            Rate of photons interacting with detector as a function of model wavelength set

        Returns
        -------
        products: 2-element tuple of numpy.ndarrays
            first element - electron rate per pixel
            second element - variance of electron rate per pixel
        '''
        q_yield, fano_factor = self.current_instrument.get_quantum_yield(self.wave)

        # convert the photon rate to electron rate by multiplying by the quantum yield which is a function of wavelength
        electron_rate_pix = integrate.simpson(rate * q_yield, self.wave)

        # to meet IDT expectations, some instruments require a possibly chromatic fudge factor to be applied
        # to the per-pixel electron rate variance.
        var_fudge = self.current_instrument.get_variance_fudge(self.wave)

        # the variance in the electron rate, Ve, is also scaled by the quantum yield plus a fano factor which is
        # analytic in the simple 1 or 2 electron case: Ve = (qy + fano) * Re.  since Re is the photon rate
        # scaled by the quantum yield, Re = qy * Rp, we get: Ve = qy * (qy + fano) * Rp
        electron_variance_pix = integrate.simpson(rate * q_yield * (q_yield + fano_factor) * var_fudge, self.wave)

        products = electron_rate_pix, electron_variance_pix

        return products
//  [python]
def gridded_2d(q, n=300):
    """
    Compute the integral on a regular grid using rectangular, trapezoidal,
    simpsons, and romberg integration.  Romberg integration requires that
    the grid be of size n = 2**k + 1.
    """
    theta = np.linspace(THETA_LOW, THETA_HIGH, n)
    phi = np.linspace(PHI_LOW, PHI_HIGH, n)
    Atheta, Aphi = np.meshgrid(theta, phi)
    Zq = kernel_2d(q=q, theta=Atheta, phi=Aphi)
    Zq *= abs(sin(Atheta))
    dx, dy = theta[1]-theta[0], phi[1]-phi[0]
    print("rect-%d"%n, n**2, np.sum(Zq)*dx*dy*SCALE/(4*pi))
    print("trapz-%d"%n, n**2, np.trapz(np.trapz(Zq, dx=dx), dx=dy)*SCALE/(4*pi))
    print("simpson-%d"%n, n**2, simps(simps(Zq, dx=dx), dx=dy)*SCALE/(4*pi))
    print("romb-%d"%n, n**2, romb(romb(Zq, dx=dx), dx=dy)*SCALE/(4*pi))
//  [python]
def gridded_2d(q, n=300):
    """
    Compute the integral on a regular grid using rectangular, trapezoidal,
    simpsons, and romberg integration.  Romberg integration requires that
    the grid be of size n = 2**k + 1.
    """
    theta = np.linspace(THETA_LOW, THETA_HIGH, n)
    phi = np.linspace(PHI_LOW, PHI_HIGH, n)
    Atheta, Aphi = np.meshgrid(theta, phi)
    Zq = kernel_2d(q=q, theta=Atheta, phi=Aphi)
    Zq *= abs(sin(Atheta))
    dx, dy = theta[1]-theta[0], phi[1]-phi[0]
    print("rect-%d"%n, n**2, np.sum(Zq)*dx*dy*SCALE/(4*pi))
    print("trapz-%d"%n, n**2, np.trapz(np.trapz(Zq, dx=dx), dx=dy)*SCALE/(4*pi))
    print("simpson-%d"%n, n**2, simpson(simpson(Zq, dx=dx), dx=dy)*SCALE/(4*pi))
    print("romb-%d"%n, n**2, romb(romb(Zq, dx=dx), dx=dy)*SCALE/(4*pi))
//  [python]
def scale_conduct(self, P, kappa, L, ratio, T0 = 0):
		"""
		Get scale factor q||0 (q0) for heat flux via power balance:
		(input MW = output MW)
		Ignores wall psi and just creates a profile at OMP
		Creates a dense (1000pts) grid at the midplane to get higher resolution
		integral.  Integrates q_hat with respect to psi.
		q||0 = P_div / ( 2*pi* integral(q_hat dPsi ))
		return q0		
		"""
		psiN = np.linspace(0.85, 1.2, 1000)	# this is normalized
		T = self.fT(psiN)			# this is now temperature in keV
		
		pfr = psiN < 1.0
		T[pfr] = self.fT(1.0 + ratio*(1.0-psiN[pfr]))	# treat T in PFR as if in SOL: map psi<1 to psi>1 with ratio * dpsi
		
		q_hat = 2.0/7.0 * kappa/L * (T**3.5 - T0**3.5) * (1e+3)**3.5/1e+6   # in MW/m^2
		
		psi = psiN * (self.ep.g['psiSep']-self.ep.g['psiAxis']) + self.ep.g['psiAxis']	# this is flux
		P0 = 2*np.pi * integ.simps(q_hat, psi)
		#account for nonphysical power
		if P0 < 0: P0 = -P0
		#Scale to input power
		q0 = P/P0
		return q0
//  [python]
def scale_conduct(self, P, kappa, L, ratio, T0 = 0):
		"""
		Get scale factor q||0 (q0) for heat flux via power balance:
		(input MW = output MW)
		Ignores wall psi and just creates a profile at OMP
		Creates a dense (1000pts) grid at the midplane to get higher resolution
		integral.  Integrates q_hat with respect to psi.
		q||0 = P_div / ( 2*pi* integral(q_hat dPsi ))
		return q0		
		"""
		psiN = np.linspace(0.85, 1.2, 1000)	# this is normalized
		T = self.fT(psiN)			# this is now temperature in keV
		
		pfr = psiN < 1.0
		T[pfr] = self.fT(1.0 + ratio*(1.0-psiN[pfr]))	# treat T in PFR as if in SOL: map psi<1 to psi>1 with ratio * dpsi
		
		q_hat = 2.0/7.0 * kappa/L * (T**3.5 - T0**3.5) * (1e+3)**3.5/1e+6   # in MW/m^2
		
		psi = psiN * (self.ep.g['psiSep']-self.ep.g['psiAxis']) + self.ep.g['psiAxis']	# this is flux
		P0 = 2*np.pi * integ.simpson(q_hat, psi)
		#account for nonphysical power
		if P0 < 0: P0 = -P0
		#Scale to input power
		q0 = P/P0
		return q0
//  [python]
def testBatesPDFisNormalized(self, total_count, bounds):
    low, high = tf.cast(bounds[0], tf.float64), tf.cast(bounds[1], tf.float64)
    d = bates.Bates(total_count=total_count, low=low, high=high)
    # This is about as high as JAX can go and still finish in time.
    nx = 100
    x = tf.linspace(low, high, nx)
    y = self.evaluate(d.prob(x))
    dx = self.evaluate(x[1] - x[0])
    self.assertAllClose(scipy.integrate.simps(y=y, dx=dx), 1.,
                        atol=5e-05, rtol=5e-05)
//  [python]
def testBatesPDFisNormalized(self, total_count, bounds):
    low, high = tf.cast(bounds[0], tf.float64), tf.cast(bounds[1], tf.float64)
    d = bates.Bates(total_count=total_count, low=low, high=high)
    # This is about as high as JAX can go and still finish in time.
    nx = 100
    x = tf.linspace(low, high, nx)
    y = self.evaluate(d.prob(x))
    dx = self.evaluate(x[1] - x[0])
    self.assertAllClose(scipy.integrate.simpson(y=y, dx=dx), 1.,
                        atol=5e-05, rtol=5e-05)
//  [python]
def integ_var(x, s_list, w_list, B):
    tinv = numpy.exp(-B*s_list**2/2) if B is not None else 1.
    return scipy.integrate.simps(s_list**2 * tinv * w_list * numpy.sinc(2*numpy.abs(s_list)*numpy.abs(x)),
                                 s_list)
//  [python]
def integ_var(x, s_list, w_list, B):
    tinv = numpy.exp(-B*s_list**2/2) if B is not None else 1.
    return scipy.integrate.simpson(s_list**2 * tinv * w_list * numpy.sinc(2*numpy.abs(s_list)*numpy.abs(x)),
                                 s_list)
//  [python]
def _variabledt_mean(tr):
    import scipy.integrate
    # Calculate the mean with simpsons rule:
    integral = scipy.integrate.simps(y=tr.data_pts_np, x=tr.time_pts_s)
    mean = integral / tr.get_duration().rescale('s').magnitude * tr.data_unit
    return mean
//  [python]
def _variabledt_mean(tr):
    import scipy.integrate
    # Calculate the mean with simpsons rule:
    integral = scipy.integrate.simpson(y=tr.data_pts_np, x=tr.time_pts_s)
    mean = integral / tr.get_duration().rescale('s').magnitude * tr.data_unit
    return mean
//  [python]
def blur(self, hidden_spectrum):
        """Convert hidden spectral function to spectral function."""
        if self.kind == 'freq_bosonic':
            h_interp = interp.InterpolatedUnivariateSpline(
                np.concatenate((-self.re_axis[:0:-1], self.re_axis)),
                np.concatenate((hidden_spectrum[:0:-1], hidden_spectrum)),
                ext='zeros')
        else:
            h_interp = interp.InterpolatedUnivariateSpline(
                self.re_axis,
                hidden_spectrum,
                ext='zeros')

        integrand = self.gaussian_numeric[None, :] * h_interp(self.re_axis[:, None] + self.w_integration[None, :])
        return integ.simps(integrand, x=self.w_integration, axis=-1)
//  [python]
def blur(self, hidden_spectrum):
        """Convert hidden spectral function to spectral function."""
        if self.kind == 'freq_bosonic':
            h_interp = interp.InterpolatedUnivariateSpline(
                np.concatenate((-self.re_axis[:0:-1], self.re_axis)),
                np.concatenate((hidden_spectrum[:0:-1], hidden_spectrum)),
                ext='zeros')
        else:
            h_interp = interp.InterpolatedUnivariateSpline(
                self.re_axis,
                hidden_spectrum,
                ext='zeros')

        integrand = self.gaussian_numeric[None, :] * h_interp(self.re_axis[:, None] + self.w_integration[None, :])
        return integ.simpson(integrand, x=self.w_integration, axis=-1)
//  [python]
def comoving_dist(a, cosmo):
    """
    Comoving distance. Ignores radiation, which might shift results slightly.
    """
    aa = np.logspace(np.log10(a), 0., 1000)
    zz = 1./aa - 1.
    
    # Cosmological parameters
    H0 = (100.*cosmo['h']); w0 = cosmo['w0']; wa = cosmo['wa']
    ombh2 = cosmo['omega_b_0'] * cosmo['h']**2.
    om = cosmo['omega_M_0']; ol = cosmo['omega_lambda_0']
    ogam = 2.47e-5 / cosmo['h']**2. # Rad. density fraction, from Dodelson Eq. 2.70
    Neff = 3.046
    onu = (7./8.) * (4./11.)**(4./3.) * Neff * ogam
    ok = 1. - om - ol
    
    # Omega_DE(z) and 1/E(z)
    omegaDE = ol #* np.exp(3.*wa*(aa - 1.)) / aa**(3.*(1. + w0 + wa))
    invE = 1. / np.sqrt( om*aa + ok*aa**2. + ogam + onu + omegaDE*aa**4.) # 1/(a^2 H)
    
    # Calculate r(z), with curvature-dependent parts
    r_c = scipy.integrate.simps(invE, aa)
    if ok > 0.:
        _r = C/(H0*np.sqrt(ok)) * np.sinh(r_c * np.sqrt(ok))
    elif ok < 0.:
        _r = C/(H0*np.sqrt(-ok)) * np.sin(r_c * np.sqrt(-ok))
    else:
        _r = (C/H0) * r_c
    return _r
//  [python]
def comoving_dist(a, cosmo):
    """
    Comoving distance. Ignores radiation, which might shift results slightly.
    """
    aa = np.logspace(np.log10(a), 0., 1000)
    zz = 1./aa - 1.
    
    # Cosmological parameters
    H0 = (100.*cosmo['h']); w0 = cosmo['w0']; wa = cosmo['wa']
    ombh2 = cosmo['omega_b_0'] * cosmo['h']**2.
    om = cosmo['omega_M_0']; ol = cosmo['omega_lambda_0']
    ogam = 2.47e-5 / cosmo['h']**2. # Rad. density fraction, from Dodelson Eq. 2.70
    Neff = 3.046
    onu = (7./8.) * (4./11.)**(4./3.) * Neff * ogam
    ok = 1. - om - ol
    
    # Omega_DE(z) and 1/E(z)
    omegaDE = ol #* np.exp(3.*wa*(aa - 1.)) / aa**(3.*(1. + w0 + wa))
    invE = 1. / np.sqrt( om*aa + ok*aa**2. + ogam + onu + omegaDE*aa**4.) # 1/(a^2 H)
    
    # Calculate r(z), with curvature-dependent parts
    r_c = scipy.integrate.simpson(invE, aa)
    if ok > 0.:
        _r = C/(H0*np.sqrt(ok)) * np.sinh(r_c * np.sqrt(ok))
    elif ok < 0.:
        _r = C/(H0*np.sqrt(-ok)) * np.sin(r_c * np.sqrt(-ok))
    else:
        _r = (C/H0) * r_c
    return _r
//  [python]
def multiplicity_function_individual_ProxyZ(self, z = .1, proxy = 1, 
                                                proxy_model = 1, method = 'grid'):
        r"""
        Attributes:
        -----------
        z: array
            list of redshifs
        proxy: array
            list of dark matter halo mass proxies
        proxy_model: object
            object from proxy class
        method: str
            method to use to compute multiplicity function
            "grid": use simpson integral
            "exact": use scipy.dblquad 
        Returns:
        --------       
        dN_dzdlogMdOmega : array
            multiplicity function for the corresponding redshifts and mass proxies
        """
        if method == 'grid':
            z_sort = np.sort(z)
            index_sort = np.argsort(z)
            index_z = np.arange(len(z))
            multiplicity_logm = self.dNdzdlogMdOmega_interpolation(z_sort, self.logm_grid).T
            dndproxy = scipy.integrate.simps(multiplicity_logm * self.pdf[np.argsort(z)], x = self.logm_grid, axis = 1)
            dndproxy = dndproxy[np.argsort(index_sort)]
            return dndproxy
        
        if method == 'exact':
            dndproxy = []
            for zs, proxys in zip(z, proxy):
                def __integrand__(logm):
                    res1 = proxy_model.pdf(proxys, zs, logm)
                    res2 = self.dndlog10M(logm, zs) * self.dVdzdOmega(zs)
                    return res1*res2
                dndproxy.append(scipy.integrate.quad(__integrand__, self.logm_grid[0], self.logm_grid[-1])[0])
            return np.array(dndproxy)
//  [python]
def multiplicity_function_individual_ProxyZ(self, z = .1, proxy = 1, 
                                                proxy_model = 1, method = 'grid'):
        r"""
        Attributes:
        -----------
        z: array
            list of redshifs
        proxy: array
            list of dark matter halo mass proxies
        proxy_model: object
            object from proxy class
        method: str
            method to use to compute multiplicity function
            "grid": use simpson integral
            "exact": use scipy.dblquad 
        Returns:
        --------       
        dN_dzdlogMdOmega : array
            multiplicity function for the corresponding redshifts and mass proxies
        """
        if method == 'grid':
            z_sort = np.sort(z)
            index_sort = np.argsort(z)
            index_z = np.arange(len(z))
            multiplicity_logm = self.dNdzdlogMdOmega_interpolation(z_sort, self.logm_grid).T
            dndproxy = scipy.integrate.simpson(multiplicity_logm * self.pdf[np.argsort(z)], x = self.logm_grid, axis = 1)
            dndproxy = dndproxy[np.argsort(index_sort)]
            return dndproxy
        
        if method == 'exact':
            dndproxy = []
            for zs, proxys in zip(z, proxy):
                def __integrand__(logm):
                    res1 = proxy_model.pdf(proxys, zs, logm)
                    res2 = self.dndlog10M(logm, zs) * self.dVdzdOmega(zs)
                    return res1*res2
                dndproxy.append(scipy.integrate.quad(__integrand__, self.logm_grid[0], self.logm_grid[-1])[0])
            return np.array(dndproxy)
//  [python]
def calc_beam_area(beam_profile):
	"""Calculate the beam area in steradians given a beam profile[{r,b},npoint].
	r is in radians, b should have a peak of 1.."""
	from scipy import integrate
	r, b = beam_profile
	return integrate.simps(2*np.pi*r*b,r)
//  [python]
def calc_beam_area(beam_profile):
	"""Calculate the beam area in steradians given a beam profile[{r,b},npoint].
	r is in radians, b should have a peak of 1.."""
	from scipy import integrate
	r, b = beam_profile
	return integrate.simpson(2*np.pi*r*b,r)
//  [python]
def make_filter_file(SPC, path='./EAZY/', NSTART=220):
    """
make_filter_file(SPC)
    
    Make an EAZY filter file for "filters" defined from grism spectrum
    wavelengths.
    """
    import scipy.integrate as integrate
    
    ## Number of points per "filter"
    NSPECFILT = 25
    ## Gaussian filter
    filtx = np.arange(NSPECFILT)/(NSPECFILT-1.)*8-4
    filty = np.exp(-0.5*filtx**2)/np.sqrt(2*np.pi)
    ## FWHM of line ~180 A
    filtx*=100
    ## Normalize 
    filty /= integrate.simps(filty,filtx)
    
    ## Make RES file
    spec = SPC.getSpec(SPC._ext_map[0])
    res = open(path+'grism.FILTER.RES','w')
    trans = open(path+'grism.translate','w')
    
    use = np.where((spec.LAMBDA >= XMIN) & (spec.LAMBDA < XMAX))[0]
    for i,l0 in enumerate(spec.LAMBDA[use]):
        res.write('%d fl%d\n' %(NSPECFILT, l0/10))
        for k in range(NSPECFILT):
            res.write('%4d %10.3e %10.3e\n' %(k+1, l0+filtx[k], filty[k]))
            
        trans.write('fl%d F%d\n' %(l0/10, NSTART+i))
        trans.write('el%d E%d\n' %(l0/10, NSTART+i))
        
    res.close()
//  [python]
def make_filter_file(SPC, path='./EAZY/', NSTART=220):
    """
make_filter_file(SPC)
    
    Make an EAZY filter file for "filters" defined from grism spectrum
    wavelengths.
    """
    import scipy.integrate as integrate
    
    ## Number of points per "filter"
    NSPECFILT = 25
    ## Gaussian filter
    filtx = np.arange(NSPECFILT)/(NSPECFILT-1.)*8-4
    filty = np.exp(-0.5*filtx**2)/np.sqrt(2*np.pi)
    ## FWHM of line ~180 A
    filtx*=100
    ## Normalize 
    filty /= integrate.simpson(filty,filtx)
    
    ## Make RES file
    spec = SPC.getSpec(SPC._ext_map[0])
    res = open(path+'grism.FILTER.RES','w')
    trans = open(path+'grism.translate','w')
    
    use = np.where((spec.LAMBDA >= XMIN) & (spec.LAMBDA < XMAX))[0]
    for i,l0 in enumerate(spec.LAMBDA[use]):
        res.write('%d fl%d\n' %(NSPECFILT, l0/10))
        for k in range(NSPECFILT):
            res.write('%4d %10.3e %10.3e\n' %(k+1, l0+filtx[k], filty[k]))
            
        trans.write('fl%d F%d\n' %(l0/10, NSTART+i))
        trans.write('el%d E%d\n' %(l0/10, NSTART+i))
        
    res.close()
//  [python]
def lifetime_accum(lifetime, X_s, X_t):
    """
    Compute the lifetime by integrating the correlation function
    and accumulate the result into the given lifetime object.

    Parameters
    ----------
    X_s: array-like
        Array with the correlation function.
        Works also with other decaying scores that are defined
        in the range [0,1]=[min_skill,max_skill].
    X_t: array-like
        Array with the forecast lead times in the desired unit,
        e.g. [min, hour].
    """
    if lifetime["rule"] == "trapz":
        lf = np.trapz(X_s, x=X_t)
    elif lifetime["rule"] == "simpson":
        lf = simps(X_s, x=X_t)
    elif lifetime["rule"] == "1/e":
        euler_number = 1.0 / exp(1.0)
        X_s_ = np.array(X_s)

        is_euler_reached = np.sum(X_s_ <= euler_number) > 0
        if is_euler_reached:
            idx_b = np.argmax(X_s_ <= euler_number)
            if idx_b > 0:
                idx_a = idx_b - 1
                fraction_score = (
                    (euler_number - X_s[idx_b])
                    * (X_t[idx_a] - X_t[idx_b])
                    / (X_s[idx_a] - X_s[idx_b])
                )
                lf = X_t[idx_b] + fraction_score
            else:
                # if all values are below the 1/e value, return min lead time
                lf = np.min(X_t)
        else:
            # if all values are above the 1/e value, return max lead time
            lf = np.max(X_t)

    lifetime["lifetime_sum"] += lf
    lifetime["n"] += 1
//  [python]
def lifetime_accum(lifetime, X_s, X_t):
    """
    Compute the lifetime by integrating the correlation function
    and accumulate the result into the given lifetime object.

    Parameters
    ----------
    X_s: array-like
        Array with the correlation function.
        Works also with other decaying scores that are defined
        in the range [0,1]=[min_skill,max_skill].
    X_t: array-like
        Array with the forecast lead times in the desired unit,
        e.g. [min, hour].
    """
    if lifetime["rule"] == "trapz":
        lf = np.trapz(X_s, x=X_t)
    elif lifetime["rule"] == "simpson":
        lf = simpson(X_s, x=X_t)
    elif lifetime["rule"] == "1/e":
        euler_number = 1.0 / exp(1.0)
        X_s_ = np.array(X_s)

        is_euler_reached = np.sum(X_s_ <= euler_number) > 0
        if is_euler_reached:
            idx_b = np.argmax(X_s_ <= euler_number)
            if idx_b > 0:
                idx_a = idx_b - 1
                fraction_score = (
                    (euler_number - X_s[idx_b])
                    * (X_t[idx_a] - X_t[idx_b])
                    / (X_s[idx_a] - X_s[idx_b])
                )
                lf = X_t[idx_b] + fraction_score
            else:
                # if all values are below the 1/e value, return min lead time
                lf = np.min(X_t)
        else:
            # if all values are above the 1/e value, return max lead time
            lf = np.max(X_t)

    lifetime["lifetime_sum"] += lf
    lifetime["n"] += 1
//  [python]
def calcQ(lamin0, specin0, mstar=1.0, helium=False, f_nu=True):
    '''
    Calculate the number of lyman ionizing photons for given spectrum
    Input spectrum must be in ergs/s/A if f_nu=False
    Q = int(Lnu/hnu dnu, nu_0, inf)
    '''
    from scipy.integrate import simps
    lamin = np.asarray(lamin0)
    specin = np.asarray(specin0)
    if helium:
        lam_0 = 304.0
    else:
        lam_0 = 911.6
    if f_nu:
        nu_0 = c/lam_0
        inds, = np.where(c/lamin >= nu_0)
        hlam, hflu = c/lamin[inds], specin[inds]
        nu = hlam[::-1]
        f_nu = hflu[::-1]
        integrand = f_nu/(h*nu)
        Q = simps(integrand, x=nu)
    else:
        inds, = np.nonzero(lamin <= lam_0)
        lam = lamin[inds]
        spec = specin[inds]
        integrand = lam*spec/(h*c)
        Q = simps(integrand, x=lam)*mstar
    return Q
//  [python]
def calcQ(lamin0, specin0, mstar=1.0, helium=False, f_nu=True):
    '''
    Calculate the number of lyman ionizing photons for given spectrum
    Input spectrum must be in ergs/s/A if f_nu=False
    Q = int(Lnu/hnu dnu, nu_0, inf)
    '''
    from scipy.integrate import simpson
    lamin = np.asarray(lamin0)
    specin = np.asarray(specin0)
    if helium:
        lam_0 = 304.0
    else:
        lam_0 = 911.6
    if f_nu:
        nu_0 = c/lam_0
        inds, = np.where(c/lamin >= nu_0)
        hlam, hflu = c/lamin[inds], specin[inds]
        nu = hlam[::-1]
        f_nu = hflu[::-1]
        integrand = f_nu/(h*nu)
        Q = simpson(integrand, x=nu)
    else:
        inds, = np.nonzero(lamin <= lam_0)
        lam = lamin[inds]
        spec = specin[inds]
        integrand = lam*spec/(h*c)
        Q = simpson(integrand, x=lam)*mstar
    return Q
//  [python]
def countrate(self):
        if self._countrate is None:
            # self._countrate = np.average(self._trace[:,1])
            # Take into account traces that have arbitrary sampling
            self._countrate = spintg.simps(
                self._trace[:, 1], self._trace[:, 0]) / self.duration
        return self._countrate
//  [python]
def countrate(self):
        if self._countrate is None:
            # self._countrate = np.average(self._trace[:,1])
            # Take into account traces that have arbitrary sampling
            self._countrate = spintg.simpson(
                self._trace[:, 1], self._trace[:, 0]) / self.duration
        return self._countrate
//  [python]
def abel_inversion_single(freq_samp, time_delay, current_ind, pos_antenna=1.15,
                          other_method=False):
    """
    TODO

    .. math:: R_c(\omega_{pe}) = R_{ant} - c/\omega \int_0 ^{\omega_{pe}}
        t_g \quad 1 / \sqrt{\omega_{pe}^2 - \omega^2} \\text{d} \omega


    Parameters
    ----------
    freq_samp
    time_delay
    current_ind
    pos_antenna
    other_method

    Returns
    -------

    """
    fpe = freq_samp[current_ind]
    density = plasma_density(fpe)

    if current_ind < 1:
        return pos_antenna, density

    if other_method:
        # Method: TODO

        freqs_used = freq_samp[:current_ind + 1]
        times_used = time_delay[1:current_ind + 1]

        temp_arr = np.arcsin(freqs_used[1:] / fpe) - np.arcsin(
            freqs_used[:-1] / fpe)

        integral = np.dot(temp_arr, times_used)
        r_vacuum = pos_antenna
    else:
        # Naive Abel Inversion

        freqs_used = freq_samp[:current_ind]
        times_used = time_delay[:current_ind]

        temp_arr = 1 / np.sqrt(np.power(fpe, 2) - np.power(freqs_used, 2))
        integral = simps(times_used * temp_arr, x=freqs_used)
        r_vacuum = distance_vacuum(time_delay[0]) / 2 + pos_antenna

    radius = speed_of_light * integral / np.pi + pos_antenna

    return radius, density
//  [python]
def abel_inversion_single(freq_samp, time_delay, current_ind, pos_antenna=1.15,
                          other_method=False):
    """
    TODO

    .. math:: R_c(\omega_{pe}) = R_{ant} - c/\omega \int_0 ^{\omega_{pe}}
        t_g \quad 1 / \sqrt{\omega_{pe}^2 - \omega^2} \\text{d} \omega


    Parameters
    ----------
    freq_samp
    time_delay
    current_ind
    pos_antenna
    other_method

    Returns
    -------

    """
    fpe = freq_samp[current_ind]
    density = plasma_density(fpe)

    if current_ind < 1:
        return pos_antenna, density

    if other_method:
        # Method: TODO

        freqs_used = freq_samp[:current_ind + 1]
        times_used = time_delay[1:current_ind + 1]

        temp_arr = np.arcsin(freqs_used[1:] / fpe) - np.arcsin(
            freqs_used[:-1] / fpe)

        integral = np.dot(temp_arr, times_used)
        r_vacuum = pos_antenna
    else:
        # Naive Abel Inversion

        freqs_used = freq_samp[:current_ind]
        times_used = time_delay[:current_ind]

        temp_arr = 1 / np.sqrt(np.power(fpe, 2) - np.power(freqs_used, 2))
        integral = simpson(times_used * temp_arr, x=freqs_used)
        r_vacuum = distance_vacuum(time_delay[0]) / 2 + pos_antenna

    radius = speed_of_light * integral / np.pi + pos_antenna

    return radius, density
//  [python]
def calAdjCCTTFromTrace(nt,dt,tStartIn,tEndIn,dataIn, synthIn):
    """ calculate the cross correlation traveltime adjoint sources for one seismogram
    IN:
        nt          : number of timesteps in each seismogram
        dt          : timestep of seismograms
        tStartIn      : float starting time for trace
        tEndIn        : float end time for trace
    OUT:
        fBar        : array containing the adjoint seismogram for the trace
        t           : ndarray containing the time steps
    """
    isCalculateWeights = False
    if isCalculateWeights:
        dSeism = np.zeros(nt)
        weight = 0

    # -- time vector
    t = np.ogrid[0:(nt-1)*dt:nt*1j]
    # -- the norm
    norm = 0

    # -- numpy arrays initialisation
    velSynth = np.zeros(nt)
    accSynth = np.zeros(nt)
    timeWind = np.zeros(nt)
    fBar = np.zeros(nt)

    # -- calculate time time-window
    tStart = tStartIn
    tEnd = tEndIn
    # -- the starting and ending sample numbers
    iStart = int(np.floor(tStart/dt))
    iEnd = int(np.ceil(tEnd/dt))
    # -- sample length of the window
    iWind = iEnd - iStart
    #print(iStart,iEnd,iWind)
    timeWind[iStart:iEnd]=sgnl.hann(iWind)

    # -- calculate the adjoint
    synth = synthIn
    interpTrc = interp.InterpolatedUnivariateSpline(t,synth)
    velSynth = interpTrc(t,1)
    accSynth = interpTrc(t,2)

    integrArgument = timeWind*synth*accSynth
    # -- calculating the norm
    norm = integr.simps(integrArgument,dx=dt,axis=-1,even='last')

    # -- divide every trace (row in matrices) by their norm (row in vector norm)
    fBar = timeWind*velSynth / norm
    if  isCalculateWeights:
        # -- read in the data seismograms
        data = dataIn
        # -- calculate the difference between data and synthetics (amplitude) per trace
        dSeism = data - synth
        # -- calculate the weight per trace
        integrArgument = timeWind*velSynth*dSeism
        weight = integr.simps(integrArgument,dx=dt,axis=-1,even='last')
        print("weight", weight/norm)
        # -- multiply weight with every adj trace
        fBar = fBar*weight
        print(weight)
    return [fBar,t]
//  [python]
def calAdjCCTTFromTrace(nt,dt,tStartIn,tEndIn,dataIn, synthIn):
    """ calculate the cross correlation traveltime adjoint sources for one seismogram
    IN:
        nt          : number of timesteps in each seismogram
        dt          : timestep of seismograms
        tStartIn      : float starting time for trace
        tEndIn        : float end time for trace
    OUT:
        fBar        : array containing the adjoint seismogram for the trace
        t           : ndarray containing the time steps
    """
    isCalculateWeights = False
    if isCalculateWeights:
        dSeism = np.zeros(nt)
        weight = 0

    # -- time vector
    t = np.ogrid[0:(nt-1)*dt:nt*1j]
    # -- the norm
    norm = 0

    # -- numpy arrays initialisation
    velSynth = np.zeros(nt)
    accSynth = np.zeros(nt)
    timeWind = np.zeros(nt)
    fBar = np.zeros(nt)

    # -- calculate time time-window
    tStart = tStartIn
    tEnd = tEndIn
    # -- the starting and ending sample numbers
    iStart = int(np.floor(tStart/dt))
    iEnd = int(np.ceil(tEnd/dt))
    # -- sample length of the window
    iWind = iEnd - iStart
    #print(iStart,iEnd,iWind)
    timeWind[iStart:iEnd]=sgnl.hann(iWind)

    # -- calculate the adjoint
    synth = synthIn
    interpTrc = interp.InterpolatedUnivariateSpline(t,synth)
    velSynth = interpTrc(t,1)
    accSynth = interpTrc(t,2)

    integrArgument = timeWind*synth*accSynth
    # -- calculating the norm
    norm = integr.simpson(integrArgument,dx=dt,axis=-1)

    # -- divide every trace (row in matrices) by their norm (row in vector norm)
    fBar = timeWind*velSynth / norm
    if  isCalculateWeights:
        # -- read in the data seismograms
        data = dataIn
        # -- calculate the difference between data and synthetics (amplitude) per trace
        dSeism = data - synth
        # -- calculate the weight per trace
        integrArgument = timeWind*velSynth*dSeism
        weight = integr.simpson(integrArgument,dx=dt,axis=-1)
        print("weight", weight/norm)
        # -- multiply weight with every adj trace
        fBar = fBar*weight
        print(weight)
    return [fBar,t]
//  [python]
def calcQs(lamin0, specin0, edges=[1, HeII_edge, OII_edge, HeI_edge, 911.6]):
    """
    Calculate the number of ionizing photons for the given spectrum at different segments.
    The default edge of each bin is [1, HeII_edge, OII_edge, HeI_edge, 911.6].
    Input spectrum must be in ergs/s/Hz!!
    Q = int(Lnu/hnu dnu, nu_min, nu_max)
    """
    from scipy.integrate import simps
    lamin = np.asarray(lamin0)
    specin = np.asarray(specin0)
    c = 2.9979e18 #ang/s
    h = 6.626e-27 #erg/s
    Qs = list()
    for lam_ind in range(len(edges)-1):
        nu_min = c/edges[lam_ind+1]
        nu_max = c/edges[lam_ind]
        inds, = np.where((c/lamin >= nu_min) & (c/lamin <= nu_max))
        hlam, hflu = c/lamin[inds], specin[inds]
        nu = hlam[::-1]
        f_nu = hflu[::-1]
        integrand = f_nu/(h*nu)
        #cs = scipy.interpolate.CubicSpline(nu, integrand, extrapolate=True)
        #Qs.append(scipy.integrate.quad(lambda x: cs(x), nu_min, nu_max)[0])
        Qs.append(simps(integrand, x=nu))
    return np.array(Qs)
//  [python]
def calcQs(lamin0, specin0, edges=[1, HeII_edge, OII_edge, HeI_edge, 911.6]):
    """
    Calculate the number of ionizing photons for the given spectrum at different segments.
    The default edge of each bin is [1, HeII_edge, OII_edge, HeI_edge, 911.6].
    Input spectrum must be in ergs/s/Hz!!
    Q = int(Lnu/hnu dnu, nu_min, nu_max)
    """
    from scipy.integrate import simpson
    lamin = np.asarray(lamin0)
    specin = np.asarray(specin0)
    c = 2.9979e18 #ang/s
    h = 6.626e-27 #erg/s
    Qs = list()
    for lam_ind in range(len(edges)-1):
        nu_min = c/edges[lam_ind+1]
        nu_max = c/edges[lam_ind]
        inds, = np.where((c/lamin >= nu_min) & (c/lamin <= nu_max))
        hlam, hflu = c/lamin[inds], specin[inds]
        nu = hlam[::-1]
        f_nu = hflu[::-1]
        integrand = f_nu/(h*nu)
        #cs = scipy.interpolate.CubicSpline(nu, integrand, extrapolate=True)
        #Qs.append(scipy.integrate.quad(lambda x: cs(x), nu_min, nu_max)[0])
        Qs.append(simpson(integrand, x=nu))
    return np.array(Qs)
//  [python]
def compute_rateSNIa(self, channel_switch='SNIa'):
        birthtime_grid = self.grid_picker(channel_switch, 'birthtime')
        lifetime_grid = self.grid_picker(channel_switch, 'lifetime')
        SFR_comp = np.multiply(self.SFR_component(birthtime_grid), self.IN.M_inf)
        F_SNIa = np.array([D.f_SD_Ia for D in self.Greggio05_SD(lifetime_grid)])
        integrand = np.multiply(SFR_comp, F_SNIa)
        return integr.simps(integrand, x=birthtime_grid)
//  [python]
def compute_rateSNIa(self, channel_switch='SNIa'):
        birthtime_grid = self.grid_picker(channel_switch, 'birthtime')
        lifetime_grid = self.grid_picker(channel_switch, 'lifetime')
        SFR_comp = np.multiply(self.SFR_component(birthtime_grid), self.IN.M_inf)
        F_SNIa = np.array([D.f_SD_Ia for D in self.Greggio05_SD(lifetime_grid)])
        integrand = np.multiply(SFR_comp, F_SNIa)
        return integr.simpson(integrand, x=birthtime_grid)
//  [python]
def rho(self, r, v_cut=-1):
        """DM mass density computed from f(eps).

        Parameters:
            - r : radius in pc
            - v_cut : maximum speed to include in density calculation
                     (defaults to v_max if not specified)
        """
        if v_cut < 0:
            v_cut = self.v_max(r)

        v_cut = np.clip(v_cut, 0, self.v_max(r))
        vlist = np.sqrt(np.linspace(0, v_cut ** 2, 20000))
        flist = np.interp(
            self.psi(r) - 0.5 * vlist ** 2,
            self.eps_grid[::-1],
            self.f_eps[::-1],
            left=0,
            right=0,
        )
        integ = vlist ** 2 * flist
        return 4 * np.pi * simps(integ, vlist)
//  [python]
def rho(self, r, v_cut=-1):
        """DM mass density computed from f(eps).

        Parameters:
            - r : radius in pc
            - v_cut : maximum speed to include in density calculation
                     (defaults to v_max if not specified)
        """
        if v_cut < 0:
            v_cut = self.v_max(r)

        v_cut = np.clip(v_cut, 0, self.v_max(r))
        vlist = np.sqrt(np.linspace(0, v_cut ** 2, 20000))
        flist = np.interp(
            self.psi(r) - 0.5 * vlist ** 2,
            self.eps_grid[::-1],
            self.f_eps[::-1],
            left=0,
            right=0,
        )
        integ = vlist ** 2 * flist
        return 4 * np.pi * simpson(integ, vlist)