示例#1
0
    def GetDrag(self, h, V):
        """ Function to compute the drag force, modelled on an
            Atlas V launch vehicle as a function of Mach no."""

        #CONSTANTS:
        gamma = 1.14
        R = 287

        T, rho = atmosphere.Atmosphere(h)

        A = np.sqrt(gamma * R * T)
        M = V / A

        if M <= 1.25:

            Cd = -0.0415 * M**3 + 0.3892 * M**2 - 0.2614 * M + 0.303

        elif M > 1.25 and M <= 4:

            Cd = -0.049 * M**4 + 0.5664 * M**3 - 2.3265 * M**2 + 3.8512 * M - 1.6625

        elif M > 4 and M <= 10:

            Cd = -0.0037 * M**3 + 0.0695 * M**2 - 0.4105 * M + 0.9732

        elif M > 10:
            Cd = 0.255

        D = 0.5 * rho * V**2 * self.A * Cd

        return D
示例#2
0
    def __init__(self,X_max,N_max,d0,earth_emergence,azimuth=0,
                ckarray='gg_t_delta_theta_2020_normalized.npz',tel_area = 1):

        self.atmosphere = at.Atmosphere()
        self.gga = CherenkovPhotonArray(ckarray)
        self.tel_area = tel_area

        self.reset_shower(X_max,N_max,d0,earth_emergence,azimuth=0)
示例#3
0
    def __init__(self, time_span, rocket):
        if isinstance(rocket, Rocket.Rocket) is False:
            print(
                'Error <in ModelWorld>: "rocket" must be an object of Rocket!')
            exit(-1)
        if time_span[0] < 0 or time_span[1] < 0:
            print(
                'Error <in ModelWorld>: incorrect time interval! Time cannot be negative!'
            )
            exit(-1)

        self.t_start = time_span[0]
        self.t_end = time_span[1]
        self.atmo = Atmo.Atmosphere()
        self.main_rocket = copy.copy(rocket)
        self.rocket = copy.copy(rocket)
示例#4
0
    def initialize_run(self):
        #  ## Create atmosphere:  attributes are self.atm.gas, self.atm.cloud and self.atm.layerProperty
        self.atm = atm.Atmosphere(self.planet,
                                  mode=self.mode,
                                  config=self.config,
                                  log=self.log,
                                  **self.kwargs)
        self.atm.run()

        #  ## Read in absorption modules:  to change absorption, edit files under /constituents'
        self.alpha = alpha.Alpha(mode=self.mode,
                                 config=self.config,
                                 log=self.log,
                                 **self.kwargs)

        #  ## Next compute radiometric properties - initialize bright and return data class
        self.bright = bright.Brightness(mode=self.mode,
                                        log=self.log,
                                        **self.kwargs)
        self.data_return = data_handling.DataReturn()

        # ## Create fileIO class
        self.fIO = fileIO.FileIO(self.output_type)
示例#5
0
class Shower():
    """A class for generating extensive air shower profiles and their Cherenkov
    outputs. The shower can either be a Gaisser Hillas shower or a Griessen
    Shower.

    Parameters:
    X_max: depth at shower max (g/cm^2)
    N_max: number of charged particles at X_max
    h0: height of first interaction (meters)
    X0: Start depth
    theta: Polar angle of the shower axis with respect to vertical. Vertical
    is defined as normal to the Earth's surface at the point where the axis
    intersects with the surface.
    phi: azimuthal angle of axis intercept (radians) measured from the x
    axis. Standard physics spherical coordinate convention. Positive x axis is
    North, positive y axis is west.
    profile: Shower type, either 'GN' for Greisen, or GH for Gaisser-Hillas.
    direction: Shower direction, either 'up' for upward going showers, or 'down'
    for downward going showers.
    """
    earth_radius = 6.371e6
    Lambda = 70
    atm = at.Atmosphere()
    axis_h = np.linspace(0,atm.maximum_height,10000)
    axis_rho = atm.density(axis_h)
    axis_delta = atm.delta(axis_h)
    axis_Moliere = 96. / axis_rho
    Moliere_data = np.load('lateral.npz')
    t_Moliere = Moliere_data['t']
    AVG_Moliere = Moliere_data['avg']

    def __init__(self,X_max,N_max,h0,theta,direction,phi=0,type='GH'):
        self.type = type
        self.reset_shower(X_max,N_max,h0,theta,direction,phi,type)

    def reset_shower(self,X_max,N_max,h0,theta,direction,phi=0,type='GH'):
        '''Set necessary attributes and perform calculations
        '''
        self.input_X_max = X_max
        self.N_max = N_max
        self.h0 = h0
        self.direction = direction
        self.theta = theta
        self.axis_r, self.axis_start_r = self.set_axis(theta,h0)
        self.axis_X, self.axis_dr, self.X0 = self.set_depth(self.axis_r,
                self.axis_start_r)
        self.X_max = X_max + self.X0
        self.axis_nch = self.size(self.axis_X)
        self.axis_nch[self.axis_nch<1.e3] = 0
        self.i_ch = np.nonzero(self.axis_nch)
        self.shower_X = self.axis_X[self.i_ch]
        self.shower_r = self.axis_r[self.i_ch]
        self.shower_Moliere = self.axis_Moliere[self.i_ch]
        self.shower_t = self.stage(self.shower_X)
        self.shower_avg_M = np.interp(self.shower_t,self.t_Moliere,self.AVG_Moliere)
        self.shower_rms_w = self.shower_avg_M * self.shower_Moliere


    def h_to_axis_R_LOC(self,h,theta):
        '''Return the length along the shower axis from the point of Earth
        emergence to the height above the surface specified

        Parameters:
        h: array of heights in meters
        theta: polar angle of shower axis (radians)

        returns: r (same size as h), an array of distances along the shower
        axis_sp.
        '''
        cos_EM = np.cos(np.pi-theta)
        R = self.earth_radius
        r_CoE= h + R # distance from the center of the earth to the specified height
        r = R*cos_EM + np.sqrt(R**2*cos_EM**2-R**2+r_CoE**2)
        return r

    def set_axis(self,theta,h0):
        '''Create a table of distances along the shower axis
        '''
        axis_r = self.h_to_axis_R_LOC(self.axis_h, theta)
        axis_start_r = self.h_to_axis_R_LOC(self.h0, theta)
        return axis_r, axis_start_r

    def set_depth(self,axis_r,axis_start_r):
        '''Integrate atmospheric density over selected direction to create
        a table of depth values.
        '''
        axis_dr = axis_r[1:] - axis_r[:-1]
        axis_deltaX = np.sqrt(self.axis_rho[1:]*self.axis_rho[:-1])*axis_dr / 10# converting to g/cm^2
        if self.direction == 'up':
            axis_X = np.concatenate((np.array([0]),np.cumsum(axis_deltaX)))
        elif self.direction == 'down':
            axis_X = np.concatenate((np.cumsum(axis_deltaX[::-1])[::-1],
                    np.array([0])))
        axis_dr = np.concatenate((np.array([0]),axis_dr))
        X0 = np.interp(axis_start_r,axis_r,axis_X)
        return axis_X, axis_dr, X0

    def size(self,X):
        """Return the size of the shower at a slant-depth X

        Parameters:
            X: the slant depth at which to calculate the shower size [g/cm2]

        Returns:
            N: the shower size
        """
        if self.type == 'GH':
            value = self.GaisserHillas(X)
        elif self.type == 'GN':
            value = self.Greisen(X)
        return value

    def GaisserHillas(self,X):
        '''Return the size of a GH shower at a given depth.
        '''
        x =         (X-self.X0)/self.Lambda
        g0 = x>0.
        m = (self.X_max-self.X0)/self.Lambda
        n = np.zeros_like(x)
        n[g0] = np.exp( m*(np.log(x[g0])-np.log(m)) - (x[g0]-m) )
        return self.N_max * n

    def Greisen(self,X,p=36.62):
        '''Return the size of a Greisen shower at a given depth.
        '''
        X[X < self.X0] = self.X0
        Delta = X - self.X_max
        W = self.X_max-self.X0
        eps = Delta / W
        s = (1+eps)/(1+eps/3)
        i = np.nonzero(s)
        n = np.zeros_like(X)
        n[i] = np.exp((eps[i]*(1-1.5*np.log(s[i]))-1.5*np.log(s[i]))*(W/p))
        return self.N_max * n

    def stage(self,X,X0=36.62):
        """Return the shower stage at a given slant-depth X. This
        is after Lafebre et al.

        Parameters:
            X: atmosphering slant-depth [g/cm2]
            X0: radiation length of air [g/cm2]

        Returns:
            t: shower stage
        """
        return (X-self.X_max)/X0
示例#6
0
class UpwardShower():
    """A class for generating upward shower profiles and their Cherenkov
    outputs. The shower can either be a Gaisser Hillas shower or a Griessen
    Shower.

    Parameters:
    X_max: depth at shower max (g/cm^2)
    N_max: number of charged particles at X_max
    d0: height of first interaction (meters)
    earth_emergence: angle the primary particle makes with the tangent line of
    the Earth's surface as it emerges (radians)
    azimuth: azimuthal angle of Earth emergence (radians)
    n_stage: number of depth steps for calculating Cherenkov light
    ckarray: filename of the Cherenkov distribution table
    tel_area: surface area of the orbital telescopes (m^2)
    """
    earth_radius = 6.371e6
    Lambda = 70
    atm = at.Atmosphere()
    axis_h = np.linspace(0,atm.maximum_height,1000)
    axis_rho = atm.density(axis_h)
    axis_delta = atm.delta(axis_h)

    def __init__(self,X_max,N_max,d0,earth_emergence,azimuth=0,
                ckarray='gg_t_delta_theta_2020_normalized.npz',tel_area = 1):

        self.atmosphere = at.Atmosphere()
        self.gga = CherenkovPhotonArray(ckarray)
        self.tel_area = tel_area

        self.reset_shower(X_max,N_max,d0,earth_emergence,azimuth=0)

    def reset_shower(self,X_max,N_max,d0,earth_emergence,azimuth=0):
        '''Set necessary attributes and perform calculations
        '''
        self.X_max = X_max
        self.N_max = N_max
        self.d0   = d0
        self.earth_emergence = earth_emergence
        self.zenith = np.pi / 2 - earth_emergence
        self.azimuth = azimuth
        self.axis_cem = np.cos(np.pi - self.zenith)
        self.crunch_numbers()

    def crunch_numbers(self):
        '''This function performs all the calculations in order.
        '''
        self.set_shower_axis()
        self.select_shower_steps()
        self.calculate_gg()
        self.calculate_yield()

    def h_to_axis_R_LOC(self,h,cos_EM):
        '''Return the length along the shower axis from the point of Earth
        emergence to the height above the surface specified

        Parameters:
        h: array of heights in meters
        cos_EM: cosine of the Earth emergence angle plus 90 degrees

        returns: r (same size as h), an array of distances along the shower axis_sp.
        '''
        R = self.earth_radius
        r_CoE= h + R # distance from the center of the earth to the specified height
        r = R*cos_EM + np.sqrt(R**2*cos_EM**2-R**2+r_CoE**2)
        return r

    def flat_earth_difference(self,r,EM,CEM):
        '''Calculate the difference in height between a flat earth height and
        the corrected height as as an inclined upward shower goes through the
        atmosphere.

        Parameters:
        r: distance along the track (m)
        EM: earth emergence angle (rad)
        CEM: cosine of the earth emergence angle + pi/2 radians

        returns: the difference in atmospheric height (between flat and round 
        earth) at the given length along the track.
        '''

        calculate the height correction
        R = self.earth_radius
        h_flat = r * np.sin(EM)
        h_true = np.sqrt(r**2 + R**2 -2*r*R*CEM) - R
        return h_true - h_flat

    def set_shower_axis(self):
        '''Create a table of 10000 distances and depths along the shower axis
        to interpolate into across its whole atmospheric path
        '''
        self.axis_r = self.h_to_axis_R_LOC(self.axis_h, self.axis_cem)
        self.axis_dr = self.axis_r[1:] - self.axis_r[:-1]
        axis_deltaX = np.sqrt(self.axis_rho[1:]*self.axis_rho[:-1])*self.axis_dr / 10# converting to g/cm^2
        self.axis_X = np.concatenate((np.array([0]),np.cumsum(axis_deltaX)))
        self.axis_dr = np.concatenate((np.array([0]),self.axis_dr))


    def select_shower_steps(self):
        '''Select the depth indices steps where the shower is producing light,
        i.e. where there are charged particles, then invoke the orbital counter
        class to calculate angles and distances to hypothetical telescopes.

        '''
        self.axis_start_r = self.h_to_axis_R_LOC(self.d0, self.axis_cem)
        self.X0 = np.interp(self.axis_start_r,self.axis_r,self.axis_X)
        self.X_max += self.X0
        self.axis_nch = self.size(self.axis_X)
        self.axis_nch[self.axis_nch<1.e-1] = 0
        self.axis_t = self.stage(self.axis_X)
        self.i_ch = np.nonzero(self.axis_nch)
        self.n_stage = np.size(self.i_ch)
        axis_r = self.axis_r[self.i_ch] - self.axis_start_r
        tel_r = self.h_to_axis_R_LOC(525.e3, self.axis_cem) - self.axis_start_r
        self.OC = OrbitalCounters(axis_r,100,100.e3,tel_r)


    def size(self,X):
        """Return the size of the shower at a slant-depth X

        Parameters:
            X: the slant depth at which to calculate the shower size [g/cm2]

        Returns:
            N: the shower size
        """
        x =         (X-self.X0)/self.Lambda
        g0 = x>0.
        m = (self.X_max-self.X0)/self.Lambda
        n = np.zeros_like(x)
        n[g0] = np.exp( m*(np.log(x[g0])-np.log(m)) - (x[g0]-m) )
        return self.N_max * n

    def stage(self,X,X0=36.62):
        """Return the shower stage at a given slant-depth X. This
        is after Lafebre et al.

        Parameters:
            X: atmosphering slant-depth [g/cm2]
            X0: radiation length of air [g/cm2]

        Returns:
            t: shower stage
        """
        return (X-self.X_max)/X0

    def calculate_gg(self):
        '''This function sets the class attribute gg which is the value of
        the normalized Cherenkov distribution for each stage going to each
        telescope.
        '''

        self.gg = np.empty_like(self.OC.travel_length)
        for i in range(self.OC.travel_length.shape[0]):
            for j in range(self.OC.travel_length.shape[1]):
                if self.axis_t[self.i_ch][j]>self.gga.t[0] and self.axis_t[self.i_ch][j]<self.gga.t[-1]:
                    self.gg[i,j] = self.gga.interpolate(self.axis_t[self.i_ch][j],self.axis_delta[self.i_ch][j],self.OC.tel_q[i,j])
                else:
                    self.gg[i,j] = 0

    def calculate_yield(self):
        '''This function calculates the number of Cherenkov photons at each
        telescope using the normalized angular distribution values interpolated
        from the table.
        '''
        alpha_over_hbarc = 370.e2 # per eV per m, from PDG
        tel_dE =1.377602193180103 # Energy interval calculated from Cherenkov wavelengths
        chq = CherenkovPhoton.cherenkov_angle(1.e12,self.axis_delta[self.i_ch])
        cy = alpha_over_hbarc*np.sin(chq)**2*tel_dE
        tel_factor = self.axis_nch[self.i_ch] * self.axis_dr[self.i_ch] * cy
        self.ng = self.gg * self.OC.tel_omega * tel_factor
        self.ng_sum = self.ng.sum(axis = 1)
示例#7
0
class Shower():
    """A class for generating extensive air shower profiles and their Cherenkov
    outputs. The shower can either be a Gaisser Hillas shower or a Griessen
    Shower. The Cartesian origin is at the point where the shower axis intersects
    with the Earth's surface.

    Parameters:
    X_max: depth at shower max (g/cm^2)
    N_max: number of charged particles at X_max
    h0: height of first interaction above the ground level (meters)
    X0: Start depth
    theta: Polar angle of the shower axis with respect to vertical. Vertical
    is defined as normal to the Earth's surface at the point where the axis
    intersects with the surface.
    direction: Shower direction, either 'up' for upward going showers, or 'down'
    for downward going showers.
    phi: azimuthal angle of axis intercept (radians) measured from the x
    axis. Standard physics spherical coordinate convention. Positive x axis is
    North, positive y axis is west.
    ground_level: Height above sea level of center of shower footprint (meters)
    type: Shower type, either 'GN' for Greisen, or GH for Gaisser-Hillas.
    """
    earth_radius = 6.371e6
    c = value('speed of light in vacuum')
    Lambda = 70
    atm = at.Atmosphere()
    Moliere_data = np.load('lateral.npz')
    t_Moliere = Moliere_data['t']
    AVG_Moliere = Moliere_data['avg']
    theta_upper_limit = np.pi / 2
    theta_lower_limit = 0.

    def __init__(self,
                 X_max,
                 N_max,
                 h0,
                 theta,
                 direction,
                 phi=0.,
                 ground_level=0.,
                 type='GH'):
        if theta < self.theta_lower_limit or theta > self.theta_upper_limit:
            raise Exception("Theta value out of bounds")
        self.reset_shower(X_max, N_max, h0, theta, direction, phi,
                          ground_level, type)

    def reset_shower(self, X_max, N_max, h0, theta, direction, phi,
                     ground_level, type):
        '''Set necessary attributes and perform calculations
        '''
        self.type = type
        self.input_X_max = X_max
        self.N_max = N_max
        self.h0 = h0
        self.direction = direction
        self.theta = theta
        self.phi = phi
        self.axis_h = np.linspace(ground_level, self.atm.maximum_height, 10000)
        self.axis_rho = self.atm.density(self.axis_h)
        self.axis_delta = self.atm.delta(self.axis_h)
        self.axis_h -= ground_level
        self.axis_Moliere = 96. / self.axis_rho
        axis_midh = np.sqrt(self.axis_h[1:] * self.axis_h[:-1])
        self.axis_dh = np.empty_like(self.axis_h)
        self.axis_dh[1:-1] = np.abs(axis_midh[:-1] - axis_midh[1:])
        self.axis_dh[-1] = self.axis_dh[-2]
        self.axis_dh[0] = self.axis_dh[1]
        self.ground_level = ground_level
        self.earth_radius += ground_level  # adjust earth radius
        self.axis_r = self.h_to_axis_R_LOC(self.axis_h, theta)
        self.axis_start_r = self.h_to_axis_R_LOC(h0, theta)
        self.axis_X, self.axis_dr, self.X0 = self.set_depth(
            self.axis_r, self.axis_start_r)
        self.X_max = X_max + self.X0
        self.axis_nch = self.size(self.axis_X)
        self.axis_nch[self.axis_nch < 1.e3] = 0
        self.i_ch = np.nonzero(self.axis_nch)[0]
        self.shower_X = self.axis_X[self.i_ch]
        self.shower_r = self.axis_r[self.i_ch]
        self.shower_dr = self.axis_dr[self.i_ch]
        self.shower_nch = self.axis_nch[self.i_ch]
        self.shower_Moliere = self.axis_Moliere[self.i_ch]
        self.shower_delta = self.axis_delta[self.i_ch]
        self.shower_h = self.axis_h[self.i_ch]
        self.shower_dh = self.axis_dh[self.i_ch]
        self.shower_t = self.stage(self.shower_X, self.X_max)
        self.shower_avg_M = np.interp(self.shower_t, self.t_Moliere,
                                      self.AVG_Moliere)
        self.shower_rms_w = self.shower_avg_M * self.shower_Moliere

    @classmethod
    def h_to_axis_R_LOC(cls, h, theta):
        '''Return the length along the shower axis from the point of Earth
        emergence to the height above the surface specified

        Parameters:
        h: array of heights (m above sea level)
        theta: polar angle of shower axis (radians)

        returns: r (m) (same size as h), an array of distances along the shower
        axis_sp.
        '''
        cos_EM = np.cos(np.pi - theta)
        R = cls.earth_radius
        r_CoE = h + R  # distance from the center of the earth to the specified height
        r = R * cos_EM + np.sqrt(R**2 * cos_EM**2 - R**2 + r_CoE**2)
        return r

    @classmethod
    def theta_normal(cls, h, theta):
        ''' Convert a polar angle (at a given height) with respect to the z axis
        to a polar angle with respect to vertical in the atmosphere (at that
        height)

        Parameters:
        h: array of heights (m above sea level)
        theta: array of polar angle of shower axis (radians)

        Returns:
        The cosine(s) of the corrected angles(s)
        '''
        r = cls.h_to_axis_R_LOC(h, theta)
        cq = ((cls.earth_radius + h)**2 + r**2 -
              cls.earth_radius**2) / (2 * r * (cls.earth_radius + h))
        return cq

    def set_depth(self, axis_r, axis_start_r):
        '''Integrate atmospheric density over selected direction to create
        a table of depth values.

        Parameters:
        axis_r: distances along the shower axis
        axis_start_r: distance along the axis where the shower starts

        returns:
        axis_X: depths at each axis distances (g/cm^2)
        axis_dr: corresponding spatial distance associated with each depth (m)
        X0: start depth (g/cm^2)
        '''
        axis_dr = axis_r[1:] - axis_r[:-1]
        axis_deltaX = np.sqrt(
            self.axis_rho[1:] *
            self.axis_rho[:-1]) * axis_dr / 10  # converting to g/cm^2
        if self.direction == 'up':
            axis_X = np.concatenate((np.array([0]), np.cumsum(axis_deltaX)))
        elif self.direction == 'down':
            axis_X = np.concatenate(
                (np.cumsum(axis_deltaX[::-1])[::-1], np.array([0])))
        axis_dr = np.concatenate((np.array([0]), axis_dr))
        X0 = np.interp(axis_start_r, axis_r, axis_X)
        return axis_X, axis_dr, X0

    def size(self, X):
        """Return the size of the shower at a slant-depth X

        Parameters:
            X: the slant depth at which to calculate the shower size [g/cm2]

        Returns:
            N: the shower size (# of charged particles)
        """
        if self.type == 'GH':
            value = self.GaisserHillas(X)
        elif self.type == 'GN':
            value = self.Greisen(X)
        return value

    def GaisserHillas(self, X):
        '''Return the size of a GH shower at a given depth.
        Parameters:
        X: depth

        Returns:
        # of charged particles
        '''
        x = (self.axis_X - self.X0) / self.Lambda
        g0 = x > 0.
        m = (self.X_max - self.X0) / self.Lambda
        n = np.zeros_like(x)
        n[g0] = np.exp(m * (np.log(x[g0]) - np.log(m)) - (x[g0] - m))
        return self.N_max * n

    def Greisen(self, X_in, p=36.62):
        '''Return the size of a Greisen shower at a given depth.
        Parameters:
        X_in: depth

        Returns:
        # of charged particles
        '''
        X = [x if x > self.X0 else self.X0 for x in X_in]
        Delta = X - self.X_max
        W = self.X_max - self.X0
        eps = Delta / W
        s = (1 + eps) / (1 + eps / 3)
        i = np.nonzero(s)
        n = np.zeros_like(X)
        n[i] = np.exp(
            (eps[i] * (1 - 1.5 * np.log(s[i])) - 1.5 * np.log(s[i])) * (W / p))
        return self.N_max * n

    @classmethod
    def stage(cls, X, X_max, X0=36.62):
        """Return the shower stage at a given slant-depth X. This
        is after Lafebre et al.

        Parameters:
            X: atmosphering slant-depth [g/cm2]
            X0: radiation length of air [g/cm2]

        Returns:
            t: shower stage
        """
        return (X - X_max) / X0