def plotdmagvss(sma,eccen,inc,omega,Omega,ax=None,num=None):
    #need a mechanism for calculating evenly spaced distribution of nu
    circ = circ_from_E(sma,eccen)
    b = np.sqrt(sma**2.*(1.-eccen**2.)) #semi-minor axis
    area = np.pi*sma*b #area inside the ellipse

    #Note: We only need to evenly distribute over area bc equal area
    #in equal time is one of Kepler's laws
    numPts=200 #number of points to get
    dA = area/numPts #each dA aiming to achieve
    rs = np.zeros(numPts)#All the r to use
    nus = np.zeros(numPts)#All the nu to use
    xs = np.zeros(numPts)
    ys = np.zeros(numPts)
    zs = np.zeros(numPts)
    rs[0] = r_koe(sma,eccen,0.) #Set initial r
    nus[0] = 0.
    #EXAMPLE nus[1] = nus[0] + 2.*dA/(rs[0]*(rs[0]-dr_koe(a,e,nus[0])))
    xs[0] = x_koe(rs[0],inc,omega,Omega,nus[0])
    ys[0] = y_koe(rs[0],inc,omega,Omega,nus[0])
    zs[0] = z_koe(rs[0],inc,omega,Omega,nus[0])
    #EXAMPLE nus[1] = nus[0] + 2.*dA/(rs[0]*(rs[0]-dr_koe(a,e,nus[0])))
    for i in np.arange(numPts-1)+1:
        nus[i] = nus[i-1] + 2.*dA/(rs[i-1]*(rs[i-1]-dr_koe(sma,eccen,nus[i-1])))
        rs[i] = r_koe(sma,eccen,nus[i])
        xs[i] = x_koe(rs[i],inc,omega,Omega,nus[i])
        ys[i] = y_koe(rs[i],inc,omega,Omega,nus[i])
        zs[i] = z_koe(rs[i],inc,omega,Omega,nus[i])

    ss = s_koe(sma,eccen,nus,inc,omega)
    betas = np.arcsin(zs/rs) #From my paper
    Phis = phiLambert(betas*u.rad)
    dmags = deltaMag(p=1.,Rp=1.*u.earthRad,d=rs*u.AU,Phi=Phis)
    if ax == None:
        plt.figure()
        plt.plot(ss,dmags)
        plt.xlabel('s')
        plt.ylabel('dmag')
        plt.show(block=False)
        return None
    else:
        ax.plot(ss,dmags)
        #ax.scatter(ss,dmags,s=4)
        #E = np.pi/2.-omega#-omega/2.#-omega
        #nu = np.arccos((np.cos(E)-eccen)/(1.-eccen*np.cos(E))) + omega/2.*np.cos(inc)
        nu = np.arccos(-eccen)
        s = s_koe(sma,eccen,nu,inc,omega)
        r = r_koe(sma,eccen,nu)
        z = z_koe(r,inc,omega,Omega,nu)
        beta = np.arcsin(z/r) #From my paper
        Phi = phiLambert(beta*u.rad)
        dmag = deltaMag(p=1.,Rp=1.*u.earthRad,d=r*u.AU,Phi=Phi)
        ax.scatter(s,dmag)

        ra = sma*(1.+eccen)
        #DELETEax.plot([ra*np.sin(inc),ra*np.sin(inc)],[20.,23])
        ax.set_xlim([0.,ra])
        plt.show(block=False)
        return ax
コード例 #2
0
ファイル: TargetList.py プロジェクト: turmon/EXOSIMS
    def max_dmag_filter(self):
        """Includes stars if maximum delta mag is in the allowed orbital range
        
        Removed from prototype filters. Prototype is already calling the 
        int_cutoff_filter with OS.dMag0 and the completeness_filter with Comp.dMagLim
        
        """

        PPop = self.PlanetPopulation
        PPMod = self.PlanetPhysicalModel
        Comp = self.Completeness

        # s and beta arrays
        s = np.tan(self.OpticalSystem.WA0) * self.dist
        if PPop.scaleOrbits:
            s /= np.sqrt(self.L)
        beta = np.array([1.10472881476178] * len(s)) * u.rad

        # fix out of range values
        below = np.where(s < np.min(PPop.rrange) * np.sin(beta))[0]
        above = np.where(s > np.max(PPop.rrange) * np.sin(beta))[0]
        s[below] = np.sin(beta[below]) * np.min(PPop.rrange)
        beta[above] = np.arcsin(s[above] / np.max(PPop.rrange))

        # calculate delta mag
        p = np.max(PPop.prange)
        Rp = np.max(PPop.Rprange)
        d = s / np.sin(beta)
        Phi = PPMod.calc_Phi(beta)
        i = np.where(deltaMag(p, Rp, d, Phi) < Comp.dMagLim)[0]
        self.revise_lists(i)
コード例 #3
0
ファイル: TargetList.py プロジェクト: dsavransky/EXOSIMS
 def max_dmag_filter(self):
     """Includes stars if maximum delta mag is in the allowed orbital range
     
     Removed from prototype filters. Prototype is already calling the 
     int_cutoff_filter with OS.dMag0 and the completeness_filter with Comp.dMagLim
     
     """
     
     PPop = self.PlanetPopulation
     PPMod = self.PlanetPhysicalModel
     Comp = self.Completeness
     
     # s and beta arrays
     s = np.tan(self.OpticalSystem.WA0)*self.dist
     if PPop.scaleOrbits:
         s /= np.sqrt(self.L)
     beta = np.array([1.10472881476178]*len(s))*u.rad
     
     # fix out of range values
     below = np.where(s < np.min(PPop.rrange)*np.sin(beta))[0]
     above = np.where(s > np.max(PPop.rrange)*np.sin(beta))[0]
     s[below] = np.sin(beta[below])*np.min(PPop.rrange)
     beta[above] = np.arcsin(s[above]/np.max(PPop.rrange))
     
     # calculate delta mag
     p = np.max(PPop.prange)
     Rp = np.max(PPop.Rprange)
     d = s/np.sin(beta)
     Phi = PPMod.calc_Phi(beta)
     i = np.where(deltaMag(p, Rp, d, Phi) < Comp.dMagLim)[0]
     self.revise_lists(i)
コード例 #4
0
 def max_dmag_filter(self):
     """Includes stars if maximum delta mag is in the allowed orbital range
     
     """
     
     PPop = self.PlanetPopulation
     PPMod = self.PlanetPhysicalModel
     OS = self.OpticalSystem
     
     # s and beta arrays
     s = np.tan(OS.IWA)*self.dist
     if PPop.scaleOrbits:
         s /= np.sqrt(self.L)
     beta = np.array([1.10472881476178]*len(s))*u.rad
     
     # fix out of range values
     below = np.where(s < np.min(PPop.rrange)*np.sin(beta))[0]
     above = np.where(s > np.max(PPop.rrange)*np.sin(beta))[0]
     s[below] = np.sin(beta[below])*np.min(PPop.rrange)
     beta[above] = np.arcsin(s[above]/np.max(PPop.rrange))
     
     # calculate delta mag
     p = np.max(PPop.prange)
     Rp = np.max(PPop.Rprange)
     d = s/np.sin(beta)
     Phi = PPMod.calc_Phi(beta)
     i = np.where(deltaMag(p,Rp,d,Phi) < OS.dMagLim)[0]
     self.revise_lists(i)
コード例 #5
0
ファイル: test_deltaMag.py プロジェクト: walker-dula/EXOSIMS
 def test2(self):
     r"""Testing a couple of specific cases."""
     p = np.array([0.1, 0.2])
     Rp = np.array([1.0, 2.0]) * const.R_earth
     d = np.array([1.0, 2.0]) * u.au
     Phi = np.array([0.1, 0.5])
     result = deltaMag(p, Rp, d, Phi)
     expected = np.array([26.85, 24.35])
     np.testing.assert_allclose(expected, result, rtol=1e-2, atol=0.)
コード例 #6
0
    def init_systems(self):
        """Finds initial time-dependant parameters. Assigns each planet an 
        initial position, velocity, planet-star distance, apparent separation, 
        phase function, surface brightness of exo-zodiacal light, delta 
        magnitude, and working angle. 
        
        This method makes use of the systems' physical properties (masses, 
        distances) and their orbital elements (a, e, I, O, w, M0).
        
        """

        PPMod = self.PlanetPhysicalModel
        ZL = self.ZodiacalLight
        TL = self.TargetList

        a = self.a.to('AU').value  # semi-major axis
        e = self.e  # eccentricity
        I = self.I.to('rad').value  # inclinations
        O = self.O.to('rad').value  # right ascension of the ascending node
        w = self.w.to('rad').value  # argument of perigee
        M0 = self.M0.to('rad').value  # initial mean anomany
        E = eccanom(M0, e)  # eccentric anomaly
        Mp = self.Mp  # planet masses

        #This is the a1 a2 a3 and b1 b2 b3 are the euler angle transformation from  the I,J,K refernece frame
        # to an x,y,z frame see https://en.wikipedia.org/wiki/Orbital_elements#Keplerian_elements
        a1 = np.cos(O) * np.cos(w) - np.sin(O) * np.cos(I) * np.sin(w)
        a2 = np.sin(O) * np.cos(w) + np.cos(O) * np.cos(I) * np.sin(w)
        a3 = np.sin(I) * np.sin(w)
        A = a * np.vstack((a1, a2, a3)) * u.AU
        b1 = -np.sqrt(1 - e**2) * (np.cos(O) * np.sin(w) +
                                   np.sin(O) * np.cos(I) * np.cos(w))
        b2 = np.sqrt(1 - e**2) * (-np.sin(O) * np.sin(w) +
                                  np.cos(O) * np.cos(I) * np.cos(w))
        b3 = np.sqrt(1 - e**2) * np.sin(I) * np.cos(w)
        B = a * np.vstack((b1, b2, b3)) * u.AU
        r1 = np.cos(E) - e
        r2 = np.sin(E)

        mu = const.G * (Mp + TL.MsTrue[self.plan2star])
        v1 = np.sqrt(mu / self.a**3) / (1 - e * np.cos(E))
        v2 = np.cos(E)

        self.r = (A * r1 + B * r2).T.to('AU')  # position
        self.v = (v1 * (-A * r2 + B * v2)).T.to('AU/day')  # velocity
        self.d = np.linalg.norm(self.r,
                                axis=1) * self.r.unit  # planet-star distance
        self.s = np.linalg.norm(self.r[:, 0:2],
                                axis=1) * self.r.unit  # apparent separation
        self.phi = PPMod.calc_Phi(np.arccos(self.r[:, 2] /
                                            self.d))  # planet phase
        self.fEZ = ZL.fEZ(TL.MV[self.plan2star], self.I,
                          self.d)  # exozodi brightness
        self.dMag = deltaMag(self.p, self.Rp, self.d,
                             self.phi)  # delta magnitude
        self.WA = np.arctan(self.s / TL.dist[self.plan2star]).to(
            'arcsec')  # working angle
コード例 #7
0
ファイル: test_deltaMag.py プロジェクト: walker-dula/EXOSIMS
 def test1(self):
     r"""Testing some limiting cases."""
     p = np.array([0.0, 1.0, 1.0, 1.0, 1.0])
     Rp = np.array([1.0, 0.0, 1.0, 1.0, 1.0]) * u.kilometer
     d = np.array([1.0, 1.0, 0.0, 1.0, np.inf]) * u.kilometer
     Phi = np.array([1.0, 1.0, 1.0, 0.0, 1.0])
     # suppress division-by-zero warnings
     with np.errstate(divide='ignore'):
         result = deltaMag(p, Rp, d, Phi)
     expected = np.array([np.inf, np.inf, -np.inf, np.inf, np.inf])
     np.testing.assert_allclose(expected, result, rtol=1e-1, atol=0)
コード例 #8
0
def dmagErrorFromdmagInt(nus, inds, inc, w, a, e, Rp, p, dmag):
    Phi = (1. +
           np.sin(np.tile(inc[inds],
                          (2, 1)).T) * np.sin(nus + np.tile(w[inds], (2, 1)).T)
           )**2. / 4.  #TRYING THIS TO CIRCUMVENT POTENTIAL ARCCOS
    d = np.tile(a[inds].to('AU'),
                (2, 1)).T * (1. - np.tile(e[inds], (2, 1)).T**2.) / (
                    np.tile(e[inds], (2, 1)).T * np.cos(nus) + 1.)
    dmags = deltaMag(
        np.tile(p[inds], (2, 1)).T,
        np.tile(Rp[inds].to('AU'), (2, 1)).T, d,
        Phi)  #calculate dmag of the specified x-value
    return np.sum(np.abs(dmags - dmag))
コード例 #9
0
 def init_systems(self):
     """Finds initial time-dependant parameters. Assigns each planet an 
     initial position, velocity, planet-star distance, apparent separation, 
     phase function, surface brightness of exo-zodiacal light, delta magnitude, 
     working angle, and initializes the planet current times to zero. 
     This method makes us of the systems' physical properties (masses, distances)
      and their orbital elements (a, e, I, O, w, M0).
     """
     
     PPMod = self.PlanetPhysicalModel
     ZL = self.ZodiacalLight
     TL = self.TargetList
     
     sInds = self.plan2star                  # indices of target stars
     sDist = TL.dist[sInds]                  # distances to target stars
     Ms = TL.MsTrue[sInds]*const.M_sun       # masses of target stars
     
     a = self.a.to('AU').value               # semi-major axis
     e = self.e                              # eccentricity
     I = self.I.to('rad').value              # inclinations
     O = self.O.to('rad').value              # right ascension of the ascending node
     w = self.w.to('rad').value              # argument of perigee
     M0 = self.M0.to('rad').value            # initial mean anomany
     E = eccanom(M0, e)                      # eccentric anomaly
     Mp = self.Mp                            # planet masses
     
     a1 = np.cos(O)*np.cos(w) - np.sin(O)*np.cos(I)*np.sin(w)
     a2 = np.sin(O)*np.cos(w) + np.cos(O)*np.cos(I)*np.sin(w)
     a3 = np.sin(I)*np.sin(w)
     A = a*np.vstack((a1,a2,a3))*u.AU
     b1 = -np.sqrt(1.-e**2)*(np.cos(O)*np.sin(w) + np.sin(O)*np.cos(I)*np.cos(w))
     b2 = np.sqrt(1.-e**2)*(-np.sin(O)*np.sin(w) + np.cos(O)*np.cos(I)*np.cos(w))
     b3 = np.sqrt(1.-e**2)*np.sin(I)*np.cos(w)
     B = a*np.vstack((b1,b2,b3))*u.AU
     
     r1 = np.cos(E) - e
     r2 = np.sin(E)
     mu = const.G*(Mp + Ms)
     v1 = np.sqrt(mu/self.a**3)/(1. - e*np.cos(E))
     v2 = np.cos(E)
     
     self.r = (A*r1 + B*r2).T.to('AU')                       # position
     self.v = (v1*(-A*r2 + B*v2)).T.to('AU/day')             # velocity
     self.d = np.sqrt(np.sum(self.r**2, axis=1))             # planet-star distance
     self.s = np.sqrt(np.sum(self.r[:,0:2]**2, axis=1))      # apparent separation
     self.phi = PPMod.calc_Phi(np.arcsin(self.s/self.d))     # planet phase
     self.fEZ = ZL.fEZ(TL, sInds, self.I, self.d)            # exozodi brightness
     self.dMag = deltaMag(self.p, self.Rp, self.d, self.phi) # delta magnitude
     self.WA = np.arctan(self.s/sDist).to('mas')             # working angle
     # current time (normalized to zero at mission start) of planet positions
     self.planTime = np.zeros(self.nPlans)*u.day
コード例 #10
0
 def init_systems(self):
     """Finds initial time-dependant parameters. Assigns each planet an 
     initial position, velocity, planet-star distance, apparent separation, 
     phase function, surface brightness of exo-zodiacal light, delta 
     magnitude, and working angle. 
     
     This method makes use of the systems' physical properties (masses, 
     distances) and their orbital elements (a, e, I, O, w, M0).
     
     """
     
     PPMod = self.PlanetPhysicalModel
     ZL = self.ZodiacalLight
     TL = self.TargetList
     
     a = self.a.to('AU').value               # semi-major axis
     e = self.e                              # eccentricity
     I = self.I.to('rad').value              # inclinations
     O = self.O.to('rad').value              # right ascension of the ascending node
     w = self.w.to('rad').value              # argument of perigee
     M0 = self.M0.to('rad').value            # initial mean anomany
     E = eccanom(M0, e)                      # eccentric anomaly
     Mp = self.Mp                            # planet masses
     
     a1 = np.cos(O)*np.cos(w) - np.sin(O)*np.cos(I)*np.sin(w)
     a2 = np.sin(O)*np.cos(w) + np.cos(O)*np.cos(I)*np.sin(w)
     a3 = np.sin(I)*np.sin(w)
     A = a*np.vstack((a1, a2, a3))*u.AU
     b1 = -np.sqrt(1 - e**2)*(np.cos(O)*np.sin(w) + np.sin(O)*np.cos(I)*np.cos(w))
     b2 = np.sqrt(1 - e**2)*(-np.sin(O)*np.sin(w) + np.cos(O)*np.cos(I)*np.cos(w))
     b3 = np.sqrt(1 - e**2)*np.sin(I)*np.cos(w)
     B = a*np.vstack((b1, b2, b3))*u.AU
     r1 = np.cos(E) - e
     r2 = np.sin(E)
     
     mu = const.G*(Mp + TL.MsTrue[self.plan2star])
     v1 = np.sqrt(mu/self.a**3)/(1 - e*np.cos(E))
     v2 = np.cos(E)
     
     self.r = (A*r1 + B*r2).T.to('AU')                           # position
     self.v = (v1*(-A*r2 + B*v2)).T.to('AU/day')                 # velocity
     self.d = np.linalg.norm(self.r, axis=1)*self.r.unit         # planet-star distance
     self.s = np.linalg.norm(self.r[:,0:2], axis=1)*self.r.unit  # apparent separation
     self.phi = PPMod.calc_Phi(np.arccos(self.r[:,2]/self.d))    # planet phase
     self.fEZ = ZL.fEZ(TL.MV[self.plan2star], self.I, self.d)    # exozodi brightness
     self.dMag = deltaMag(self.p, self.Rp, self.d, self.phi)     # delta magnitude
     self.WA = np.arctan(self.s/TL.dist[self.plan2star]).to('arcsec')# working angle
コード例 #11
0
ファイル: BrownCompleteness.py プロジェクト: jrenrut/EXOSIMS
    def genplans(self, nplan):
        """Generates planet data needed for Monte Carlo simulation
        
        Args:
            nplan (integer):
                Number of planets
                
        Returns:
            tuple:
            s (astropy Quantity array):
                Planet apparent separations in units of AU
            dMag (ndarray):
                Difference in brightness
        
        """

        PPop = self.PlanetPopulation

        nplan = int(nplan)

        # sample uniform distribution of mean anomaly
        M = np.random.uniform(high=2.0 * np.pi, size=nplan)
        # sample quantities
        a, e, p, Rp = PPop.gen_plan_params(nplan)
        # check if circular orbits
        if np.sum(PPop.erange) == 0:
            r = a
            e = 0.0
            E = M
        else:
            E = eccanom(M, e)
            # orbital radius
            r = a * (1.0 - e * np.cos(E))

        beta = np.arccos(1.0 - 2.0 * np.random.uniform(size=nplan)) * u.rad
        s = r * np.sin(beta)
        # phase function
        Phi = self.PlanetPhysicalModel.calc_Phi(beta)
        # calculate dMag
        dMag = deltaMag(p, Rp, r, Phi)

        return s, dMag
コード例 #12
0
    def genplans(self, nplan):
        """Generates planet data needed for Monte Carlo simulation
        
        Args:
            nplan (integer):
                Number of planets
                
        Returns:
            tuple:
            s (astropy Quantity array):
                Planet apparent separations in units of AU
            dMag (ndarray):
                Difference in brightness
        
        """
        
        PPop = self.PlanetPopulation
        
        nplan = int(nplan)
        
        # sample uniform distribution of mean anomaly
        M = np.random.uniform(high=2.0*np.pi,size=nplan)
        # sample quantities
        a, e, p, Rp = PPop.gen_plan_params(nplan)
        # check if circular orbits
        if np.sum(PPop.erange) == 0:
            r = a
            e = 0.0
            E = M
        else:
            E = eccanom(M,e)
            # orbital radius
            r = a*(1.0-e*np.cos(E))

        beta = np.arccos(1.0-2.0*np.random.uniform(size=nplan))*u.rad
        s = r*np.sin(beta)
        # phase function
        Phi = self.PlanetPhysicalModel.calc_Phi(beta)
        # calculate dMag
        dMag = deltaMag(p,Rp,r,Phi)
        
        return s, dMag
コード例 #13
0
ファイル: KnownRVSurvey.py プロジェクト: walker-dula/EXOSIMS
 def __init__(self, **specs):
     
     # call prototype constructor
     SurveySimulation.__init__(self, **specs)
     
     TL = self.TargetList
     SU = self.SimulatedUniverse
     
     # reinitialize working angles and delta magnitudes used for integration
     self.WAint = np.zeros(TL.nStars)*u.arcsec
     self.dMagint = np.zeros(TL.nStars)
     
     # calculate estimates of shortest WAint and largest dMagint for each target
     for sInd in range(TL.nStars):
         pInds = np.where(SU.plan2star == sInd)[0]
         self.WAint[sInd] = np.arctan(np.min(SU.a[pInds])/TL.dist[sInd]).to('arcsec')
         phis = np.array([np.pi/2]*pInds.size)
         dMags = deltaMag(SU.p[pInds], SU.Rp[pInds], SU.a[pInds], phis)
         self.dMagint[sInd] = np.min(dMags)
     
     # populate outspec with arrays
     self._outspec['WAint'] = self.WAint.value
     self._outspec['dMagint'] = self.dMagint
コード例 #14
0
ファイル: KnownRVSurvey.py プロジェクト: dsavransky/EXOSIMS
 def __init__(self, **specs):
     
     # call prototype constructor
     SurveySimulation.__init__(self, **specs)
     
     TL = self.TargetList
     SU = self.SimulatedUniverse
     
     # reinitialize working angles and delta magnitudes used for integration
     self.WAint = np.zeros(TL.nStars)*u.arcsec
     self.dMagint = np.zeros(TL.nStars)
     
     # calculate estimates of shortest WAint and largest dMagint for each target
     for sInd in range(TL.nStars):
         pInds = np.where(SU.plan2star == sInd)[0]
         self.WAint[sInd] = np.arctan(np.min(SU.a[pInds])/TL.dist[sInd]).to('arcsec')
         phis = np.array([np.pi/2]*pInds.size)
         dMags = deltaMag(SU.p[pInds], SU.Rp[pInds], SU.a[pInds], phis)
         self.dMagint[sInd] = np.min(dMags)
     
     # populate outspec with arrays
     self._outspec['WAint'] = self.WAint.value
     self._outspec['dMagint'] = self.dMagint
コード例 #15
0
    def run_sim(self):
        """Performs the survey simulation 
        
        This method has access to the following:
            Obs:
                Observatory class object
            TK:
                TimeKeeping class object
            SU:
                SimulatedUniverse class object
            TL:
                TargetList class object
            PPro:
                PostProcessing class object
            OS:
                OpticalSystem class object
            PPop:
                PlanetPopulation class object
        
        Returns:
            string (str):
                String 'Simulation results in .DRM'
        
        """
        
        Obs = self.Observatory
        TK = self.TimeKeeping
        SU = self.SimulatedUniverse
        TL = self.TargetList
        PPro = self.PostProcessing
        OS = self.OpticalSystem
        PPop = self.PlanetPopulation
        PPMod = self.PlanetPhysicalModel
        
        Logger.info('run_sim beginning')
        # initialize values updated later
        # number of visits to target star
        visited = np.zeros(TL.nStars,dtype=int)
        # target revisit list
        revisit_list = np.array([])
        extended_list = np.array([])
        # current time (normalized to zero at mission start) of planet positions
        planPosTime = np.array([TK.currentTimeNorm.to('day').value]*SU.nPlans)*u.day
        # number of planet observations
        observed = np.zeros((SU.nPlans,), dtype=int)
        # set occulter separation if haveOcculter
        if OS.haveOcculter:
            Obs.currentSep = Obs.occulterSep
        
        # initialize run options
        # keep track of spectral characterizations, 0 is no characterization
        spectra = np.zeros(SU.nPlans, dtype=int)
        # get index of first target star
        sInd,_ = self.next_target()
        
        # loop until mission is finished
        while not TK.mission_is_over():
            Logger.info('current time is %r' % TK.currentTimeNorm)
            print 'Current mission time: ', TK.currentTimeNorm
            obsbegin = TK.currentTimeNorm.to('day')

            # dictionary containing results
            DRM = {}
            DRM['target_ind'] = sInd                                 # target star index
            DRM['arrival_time'] = TK.currentTimeNorm.to('day').value # arrival time
            if OS.haveOcculter:
                DRM['scMass'] = Obs.scMass.to('kg').value            # spacecraft mass

            # get target list star index of detections for extended_list 
            if TK.currentTimeNorm > TK.missionLife and extended_list.shape[0] == 0:
                for i in xrange(len(self.DRM)):
                    if self.DRM[i]['det'] == 1:
                        extended_list = np.hstack((extended_list, self.DRM[i]['target_ind']))
                        extended_list = np.unique(extended_list)
            
            # filter planet indices
            pInds = np.where(SU.plan2star == sInd)[0]           # belonging to target star
            nPlans = len(pInds)
            WA = SU.get_current_WA(pInds)
            pInds = pInds[(WA>OS.IWA) * (WA<OS.OWA)]            # inside [IWA-OWA]
            Phi = PPMod.calc_Phi(np.arcsin(SU.s[pInds]/SU.d[pInds]))
            dMag = deltaMag(SU.p[pInds],SU.Rp[pInds],SU.d[pInds],Phi)
            pInds = pInds[dMag < OS.dMagLim]                    # bright enough
            Logger.info('Observing %r/%r planets around star #%r/%r.'%(len(pInds),\
                    nPlans,sInd+1,TL.nStars))
            
            # update visited list for current star
            visited[sInd] += 1
            # find out if observations are possible and get relevant data
            observationPossible, t_int, DRM = self.observation_detection(pInds, sInd, DRM, planPosTime)
            t_int += Obs.settlingTime
            # store detection integration time
            DRM['det_int_time'] = t_int.to('day').value
            if not TK.allocate_time(t_int):
                # time too large: skip it and allocate default value dtAlloc
                observationPossible = False
                TK.allocate_time(TK.dtAlloc)
            if pInds.shape[0] != 0:
                Logger.info('Imaging possible: %s', observationPossible)
            
            # determine detection, missed detection, false alarm booleans
            FA, DET, MD, NULL = PPro.det_occur(observationPossible)
            
            # encode detection status
            s, DRM, observed = self.det_data(DRM, FA, DET, MD, sInd, pInds, \
                    observationPossible, observed)
            
            # perform characterization if SNchar defined
            if PPro.SNchar > 0:
                DRM, FA, spectra = self.observation_characterization(observationPossible, \
                        pInds, sInd, spectra, DRM, FA, t_int)
            if pInds.shape[0] != 0:
                Logger.info('Characterization possible: %s', observationPossible)
            
            # schedule a revisit
            if pInds.shape[0] != 0 and (DET or FA):
                # if there are planets, revisit based on planet with minimum separation
                sp = np.min(s)
                Mp = SU.Mp[pInds[np.argmin(s)]]
                mu = const.G*(Mp + TL.MsTrue[sInd]*const.M_sun)
                T = 2.*np.pi*np.sqrt(sp**3/mu)
                t_rev = TK.currentTimeNorm + T/2.
            else:
                # revisit based on average of population semi-major axis and mass
                sp = SU.s.mean()
                Mp = SU.Mp.mean()
                mu = const.G*(Mp + TL.MsTrue[sInd]*const.M_sun)
                T = 2.*np.pi*np.sqrt(sp**3/mu)
                t_rev = TK.currentTimeNorm + 0.75*T
            
            # populate revisit list (sInd is converted to float)
            revisit = np.array([sInd, t_rev.to('day').value])
            if revisit_list.size == 0:
                revisit_list = np.array([revisit])
            else:
                revisit_list = np.vstack((revisit_list, revisit))
            
            # update completeness values
            obsend = TK.currentTimeNorm.to('day')
            nexttime = TK.currentTimeNorm
            TL.comp0 = self.Completeness.completeness_update(sInd, TL, obsbegin, \
                    obsend, nexttime)
            
            # append result values to self.DRM
            self.DRM.append(DRM)
            
            # with occulter if spacecraft fuel is depleted, exit loop
            if OS.haveOcculter and Obs.scMass < Obs.dryMass:
                print 'Total fuel mass exceeded at %r' % TK.currentTimeNorm
                break
            
            # acquire a new target star index
            sInd, DRM = self.next_target(sInd, DRM)
        
        Logger.info('run_sim finishing OK')
        print 'Survey simulation: finishing OK'
        return 'Simulation results in .DRM'
コード例 #16
0
 def genplans(self, nplan):
     """Generates planet data needed for Monte Carlo simulation
     
     Args:
         nplan (integer):
             Number of planets
             
     Returns:
         s (astropy Quantity array):
             Planet apparent separations in units of AU
         dMag (ndarray):
             Difference in brightness
     
     """
     nplan = int(nplan)
     
     # sample uniform distribution of mean anomaly
     M = np.random.uniform(high=2.*np.pi,size=nplan)
     # sample semi-major axis        
     a = self.PlanetPopulation.gen_sma(nplan).to('AU').value
     
     # sample other necessary orbital parameters
     if np.sum(self.PlanetPopulation.erange) == 0:
         # all circular orbits
         r = a
         e = 0.
         E = M
     else:
         # sample eccentricity
         if self.PlanetPopulation.constrainOrbits:
             e = self.PlanetPopulation.gen_eccen_from_sma(nplan,a*u.AU)
         else:
             e = self.PlanetPopulation.gen_eccen(nplan)   
         # Newton-Raphson to find E
         E = eccanom(M,e)
         # orbital radius
         r = a*(1-e*np.cos(E))
     
     # orbit angle sampling
     O = self.PlanetPopulation.gen_O(nplan).to('rad').value
     w = self.PlanetPopulation.gen_w(nplan).to('rad').value
     I = self.PlanetPopulation.gen_I(nplan).to('rad').value
     
     r1 = r*(np.cos(E) - e)
     r1 = np.hstack((r1.reshape(len(r1),1), r1.reshape(len(r1),1), r1.reshape(len(r1),1)))
     r2 = r*np.sin(E)*np.sqrt(1. -  e**2)
     r2 = np.hstack((r2.reshape(len(r2),1), r2.reshape(len(r2),1), r2.reshape(len(r2),1)))
     
     a1 = np.cos(O)*np.cos(w) - np.sin(O)*np.sin(w)*np.cos(I)
     a2 = np.sin(O)*np.cos(w) + np.cos(O)*np.sin(w)*np.cos(I)
     a3 = np.sin(w)*np.sin(I)
     A = np.hstack((a1.reshape(len(a1),1), a2.reshape(len(a2),1), a3.reshape(len(a3),1)))
     
     b1 = -np.cos(O)*np.sin(w) - np.sin(O)*np.cos(w)*np.cos(I)
     b2 = -np.sin(O)*np.sin(w) + np.cos(O)*np.cos(w)*np.cos(I)
     b3 = np.cos(w)*np.sin(I)
     B = np.hstack((b1.reshape(len(b1),1), b2.reshape(len(b2),1), b3.reshape(len(b3),1)))
     
     # planet position, planet-star distance, apparent separation
     r = (A*r1 + B*r2)*u.AU
     d = np.sqrt(np.sum(r**2, axis=1))
     s = np.sqrt(np.sum(r[:,0:2]**2, axis=1))
     
     # sample albedo, planetary radius, phase function
     p = self.PlanetPopulation.gen_albedo(nplan)
     Rp = self.PlanetPopulation.gen_radius(nplan)
     beta = np.arccos(r[:,2]/d)
     Phi = self.PlanetPhysicalModel.calc_Phi(beta)
     
     # calculate dMag
     dMag = deltaMag(p,Rp,d,Phi)
     
     return s, dMag
コード例 #17
0
ファイル: BrownCompleteness.py プロジェクト: jrenrut/EXOSIMS
    def gen_update(self, TL):
        """Generates dynamic completeness values for multiple visits of each 
        star in the target list
        
        Args:
            TL (TargetList):
                TargetList class object
        
        """

        OS = TL.OpticalSystem
        PPop = TL.PlanetPopulation

        # limiting planet delta magnitude for completeness
        dMagMax = self.dMagLim

        # get name for stored dynamic completeness updates array
        # inner and outer working angles for detection mode
        mode = list(
            filter(lambda mode: mode['detectionMode'] == True,
                   OS.observingModes))[0]
        IWA = mode['IWA']
        OWA = mode['OWA']
        extstr = self.extstr + 'IWA: ' + str(IWA) + ' OWA: ' + str(OWA) + \
                ' dMagMax: ' + str(dMagMax) + ' nStars: ' + str(TL.nStars)
        ext = hashlib.md5(extstr.encode('utf-8')).hexdigest()
        self.dfilename += ext
        self.dfilename += '.dcomp'

        path = os.path.join(self.cachedir, self.dfilename)
        # if the 2D completeness update array exists as a .dcomp file load it
        if os.path.exists(path):
            self.vprint(
                'Loading cached dynamic completeness array from "%s".' % path)
            try:
                with open(path, "rb") as ff:
                    self.updates = pickle.load(ff)
            except UnicodeDecodeError:
                with open(path, "rb") as ff:
                    self.updates = pickle.load(ff, encoding='latin1')
            self.vprint('Dynamic completeness array loaded from cache.')
        else:
            # run Monte Carlo simulation and pickle the resulting array
            self.vprint(
                'Cached dynamic completeness array not found at "%s".' % path)
            self.vprint('Beginning dynamic completeness calculations')
            # dynamic completeness values: rows are stars, columns are number of visits
            self.updates = np.zeros((TL.nStars, 5))
            # number of planets to simulate
            nplan = int(2e4)
            # sample quantities which do not change in time
            a, e, p, Rp = PPop.gen_plan_params(nplan)
            a = a.to('AU').value
            # sample angles
            I, O, w = PPop.gen_angles(nplan)
            I = I.to('rad').value
            O = O.to('rad').value
            w = w.to('rad').value
            Mp = PPop.gen_mass(nplan)  # M_earth
            rmax = a * (1. + e)  # AU
            # sample quantity which will be updated
            M = np.random.uniform(high=2. * np.pi, size=nplan)
            newM = np.zeros((nplan, ))
            # population values
            smin = (np.tan(IWA) * TL.dist).to('AU').value
            if np.isfinite(OWA):
                smax = (np.tan(OWA) * TL.dist).to('AU').value
            else:
                smax = np.array([np.max(PPop.arange.to('AU').value)*\
                        (1.+np.max(PPop.erange))]*TL.nStars)
            # fill dynamic completeness values
            for sInd in xrange(TL.nStars):
                mu = (const.G * (Mp + TL.MsTrue[sInd])).to('AU3/day2').value
                n = np.sqrt(mu / a**3)  # in 1/day
                # normalization time equation from Brown 2015
                dt = 58.0 * (TL.L[sInd] / 0.83)**(3.0 / 4.0) * (
                    TL.MsTrue[sInd] / (0.91 * u.M_sun))**(1.0 / 2.0)  # days
                # remove rmax < smin
                pInds = np.where(rmax > smin[sInd])[0]
                # calculate for 5 successive observations
                for num in xrange(5):
                    if num == 0:
                        self.updates[sInd, num] = TL.comp0[sInd]
                    if not pInds.any():
                        break
                    # find Eccentric anomaly
                    if num == 0:
                        E = eccanom(M[pInds], e[pInds])
                        newM[pInds] = M[pInds]
                    else:
                        E = eccanom(newM[pInds], e[pInds])

                    r1 = a[pInds] * (np.cos(E) - e[pInds])
                    r1 = np.hstack(
                        (r1.reshape(len(r1),
                                    1), r1.reshape(len(r1),
                                                   1), r1.reshape(len(r1), 1)))
                    r2 = (a[pInds] * np.sin(E) * np.sqrt(1. - e[pInds]**2))
                    r2 = np.hstack(
                        (r2.reshape(len(r2),
                                    1), r2.reshape(len(r2),
                                                   1), r2.reshape(len(r2), 1)))

                    a1 = np.cos(O[pInds]) * np.cos(w[pInds]) - np.sin(
                        O[pInds]) * np.sin(w[pInds]) * np.cos(I[pInds])
                    a2 = np.sin(O[pInds]) * np.cos(w[pInds]) + np.cos(
                        O[pInds]) * np.sin(w[pInds]) * np.cos(I[pInds])
                    a3 = np.sin(w[pInds]) * np.sin(I[pInds])
                    A = np.hstack(
                        (a1.reshape(len(a1),
                                    1), a2.reshape(len(a2),
                                                   1), a3.reshape(len(a3), 1)))

                    b1 = -np.cos(O[pInds]) * np.sin(w[pInds]) - np.sin(
                        O[pInds]) * np.cos(w[pInds]) * np.cos(I[pInds])
                    b2 = -np.sin(O[pInds]) * np.sin(w[pInds]) + np.cos(
                        O[pInds]) * np.cos(w[pInds]) * np.cos(I[pInds])
                    b3 = np.cos(w[pInds]) * np.sin(I[pInds])
                    B = np.hstack(
                        (b1.reshape(len(b1),
                                    1), b2.reshape(len(b2),
                                                   1), b3.reshape(len(b3), 1)))

                    # planet position, planet-star distance, apparent separation
                    r = (A * r1 + B * r2)  # position vector (AU)
                    d = np.linalg.norm(r, axis=1)  # planet-star distance
                    s = np.linalg.norm(r[:, 0:2],
                                       axis=1)  # apparent separation
                    beta = np.arccos(r[:, 2] / d)  # phase angle
                    Phi = self.PlanetPhysicalModel.calc_Phi(
                        beta * u.rad)  # phase function
                    dMag = deltaMag(p[pInds], Rp[pInds], d * u.AU,
                                    Phi)  # difference in magnitude

                    toremoves = np.where((s > smin[sInd])
                                         & (s < smax[sInd]))[0]
                    toremovedmag = np.where(dMag < dMagMax)[0]
                    toremove = np.intersect1d(toremoves, toremovedmag)

                    pInds = np.delete(pInds, toremove)

                    if num == 0:
                        self.updates[sInd, num] = TL.comp0[sInd]
                    else:
                        self.updates[sInd, num] = float(len(toremove)) / nplan

                    # update M
                    newM[pInds] = (newM[pInds] + n[pInds] * dt) / (
                        2 * np.pi) % 1 * 2. * np.pi

                if (sInd + 1) % 50 == 0:
                    self.vprint('stars: %r / %r' % (sInd + 1, TL.nStars))
            # ensure that completeness values are between 0 and 1
            self.updates = np.clip(self.updates, 0., 1.)
            # store dynamic completeness array as .dcomp file
            with open(path, 'wb') as ff:
                pickle.dump(self.updates, ff)
            self.vprint('Dynamic completeness calculations finished')
            self.vprint('Dynamic completeness array stored in %r' % path)
コード例 #18
0
    def propag_system(self, sInd, dt):
        """Propagates planet time-dependant parameters: position, velocity, 
        planet-star distance, apparent separation, phase function, surface brightness 
        of exo-zodiacal light, delta magnitude, and working angle.
        
        This method uses the Kepler state transition matrix to propagate a 
        planet's state (position and velocity vectors) forward in time using 
        the Kepler state transition matrix.
        
        Args:
            sInd (integer):
                Index of the target system of interest
            dt (astropy Quantity):
                Time increment in units of day, for planet position propagation
        
        """

        PPMod = self.PlanetPhysicalModel
        ZL = self.ZodiacalLight
        TL = self.TargetList

        assert np.isscalar(sInd), \
                "Can only propagate one system at a time, sInd must be scalar."
        # check for planets around this target
        pInds = np.where(self.plan2star == sInd)[0]
        if len(pInds) == 0:
            return
        # check for positive time increment
        assert dt >= 0, "Time increment (dt) to propagate a planet must be positive."
        if dt == 0:
            return

        # Calculate initial positions in AU and velocities in AU/day
        r0 = self.r[pInds].to('AU').value
        v0 = self.v[pInds].to('AU/day').value
        # stack dimensionless positions and velocities
        nPlans = pInds.size
        x0 = np.reshape(np.concatenate((r0, v0), axis=1), nPlans * 6)

        # Calculate vector of gravitational parameter in AU3/day2
        Ms = TL.MsTrue[[sInd]]
        Mp = self.Mp[pInds]
        mu = (const.G * (Mp + Ms)).to('AU3/day2').value

        # use keplerSTM.py to propagate the system
        prop = planSys(x0, mu, epsmult=10.)
        try:
            prop.takeStep(dt.to('day').value)
        except ValueError:
            #try again with larger epsmult and two steps to force convergence
            prop = planSys(x0, mu, epsmult=100.)
            try:
                prop.takeStep(dt.to('day').value / 2.)
                prop.takeStep(dt.to('day').value / 2.)
            except ValueError:
                raise ValueError('planSys error')

        # split off position and velocity vectors
        x1 = np.array(np.hsplit(prop.x0, 2 * nPlans))
        rind = np.array(range(0, len(x1), 2))  # even indices
        vind = np.array(range(1, len(x1), 2))  # odd indices

        # update planets' position, velocity, planet-star distance, apparent, separation,
        # phase function, exozodi surface brightness, delta magnitude and working angle
        self.r[pInds] = x1[rind] * u.AU
        self.v[pInds] = x1[vind] * u.AU / u.day
        # if sys.version_info[0] > 2:
        #     self.d[pInds] = np.linalg.norm(self.r[pInds], axis=1)
        #     self.s[pInds] = np.linalg.norm(self.r[pInds,0:2], axis=1)
        # else:
        #     self.d[pInds] = np.linalg.norm(self.r[pInds], axis=1)*self.r.unit
        #     self.s[pInds] = np.linalg.norm(self.r[pInds,0:2], axis=1)*self.r.unit

        try:
            self.d[pInds] = np.linalg.norm(self.r[pInds], axis=1)
            self.phi[pInds] = PPMod.calc_Phi(
                np.arccos(self.r[pInds, 2] / self.d[pInds]))
        except:
            self.d[pInds] = np.linalg.norm(self.r[pInds], axis=1) * self.r.unit
            self.phi[pInds] = PPMod.calc_Phi(
                np.arccos(self.r[pInds, 2] / self.d[pInds]))

        # self.fEZ[pInds] = ZL.fEZ(TL.MV[sInd], self.I[pInds], self.d[pInds])
        self.dMag[pInds] = deltaMag(self.p[pInds], self.Rp[pInds],
                                    self.d[pInds], self.phi[pInds])
        try:
            self.s[pInds] = np.linalg.norm(self.r[pInds, 0:2], axis=1)
            self.WA[pInds] = np.arctan(self.s[pInds] /
                                       TL.dist[sInd]).to('arcsec')
        except:
            self.s[pInds] = np.linalg.norm(self.r[pInds, 0:2],
                                           axis=1) * self.r.unit
            self.WA[pInds] = np.arctan(self.s[pInds] /
                                       TL.dist[sInd]).to('arcsec')
コード例 #19
0
 def set_planet_phase(self, beta = np.pi/2):
     """Positions all planets at input star-planet-observer phase angle
     where possible. For systems where the input phase angle is not achieved,
     planets are positioned at quadrature (phase angle of 90 deg).
     
     The position found here is not unique. The desired phase angle will be
     achieved at two points on the planet's orbit (for non-face on orbits).
     
     Args:
         beta (float):
             star-planet-observer phase angle in radians.
     
     """
     
     PPMod = self.PlanetPhysicalModel
     ZL = self.ZodiacalLight
     TL = self.TargetList
     
     a = self.a.to('AU').value               # semi-major axis
     e = self.e                              # eccentricity
     I = self.I.to('rad').value              # inclinations
     O = self.O.to('rad').value              # right ascension of the ascending node
     w = self.w.to('rad').value              # argument of perigee
     Mp = self.Mp                            # planet masses
     
     # make list of betas
     betas = beta*np.ones(w.shape)
     mask = np.cos(betas)/np.sin(I) > 1.
     num = len(np.where(mask == True)[0])
     betas[mask] = np.pi/2.
     mask = np.cos(betas)/np.sin(I) < -1.
     num += len(np.where(mask == True)[0])
     betas[mask] = np.pi/2.
     if num > 0:
         self.vprint('***Warning***')
         self.vprint('{} planets out of {} could not be set to phase angle {} radians.'.format(num,self.nPlans,beta))
         self.vprint('These planets are set to quadrature (phase angle pi/2)')
     
     # solve for true anomaly
     nu = np.arcsin(np.cos(betas)/np.sin(I)) - w
     
     # setup for position and velocity
     a1 = np.cos(O)*np.cos(w) - np.sin(O)*np.cos(I)*np.sin(w)
     a2 = np.sin(O)*np.cos(w) + np.cos(O)*np.cos(I)*np.sin(w)
     a3 = np.sin(I)*np.sin(w)
     A = np.vstack((a1, a2, a3))
     
     b1 = -(np.cos(O)*np.sin(w) + np.sin(O)*np.cos(I)*np.cos(w))
     b2 = (-np.sin(O)*np.sin(w) + np.cos(O)*np.cos(I)*np.cos(w))
     b3 = np.sin(I)*np.cos(w)
     B = np.vstack((b1, b2, b3))
     
     r = a*(1.-e**2)/(1.-e*np.cos(nu))
     mu = const.G*(Mp + TL.MsTrue[self.plan2star])
     v1 = -np.sqrt(mu/(self.a*(1.-self.e**2)))*np.sin(nu)
     v2 = np.sqrt(mu/(self.a*(1.-self.e**2)))*(self.e + np.cos(nu))
     
     self.r = (A*r*np.cos(nu) + B*r*np.sin(nu)).T*u.AU           # position
     self.v = (A*v1 + B*v2).T.to('AU/day')                       # velocity
     self.d = np.linalg.norm(self.r, axis=1)*self.r.unit         # planet-star distance
     self.s = np.linalg.norm(self.r[:,0:2], axis=1)*self.r.unit  # apparent separation
     self.phi = PPMod.calc_Phi(np.arccos(self.r[:,2]/self.d))    # planet phase
     self.fEZ = ZL.fEZ(TL.MV[self.plan2star], self.I, self.d)    # exozodi brightness
     self.dMag = deltaMag(self.p, self.Rp, self.d, self.phi)     # delta magnitude
     self.WA = np.arctan(self.s/TL.dist[self.plan2star]).to('arcsec')# working angle
コード例 #20
0
 def check_visible_end(self, observationPossible, t_int, t_trueint, sInd, pInds, t_char_calc):
     """Determines if planets are visible at the end of the observation time
     
     Args:
         observationPossible (ndarray):
             1D numpy ndarray of booleans indicating if an observation of 
             each planet is possible
         t_int (Quantity):
             integration time (units of time)
         t_trueint (Quantity):
             1D numpy ndarray of calculated integration times for planets
             (units of time)
         sInd (int):
             target star index
         pInds (ndarray):
             1D numpy ndarray of planet indices
         t_char_calc (bool):
             boolean where True is for characterization calculations
             
     Returns:
         t_int, observationPossible, chargo (Quantity, ndarray, bool):
             true integration time for planetary system (units of day) 
             (t_char_calc = False) or maximum characterization time for 
             planetary system (t_char_calc = True), updated 1D numpy ndarray 
             of booleans indicating if an observation or characterization of 
             each planet is possible, boolean where True is to encode 
             characterization data (t_char_calc = True only)
     
     """
     
     TL = self.TargetList
     Obs = self.Observatory
     SU = self.SimulatedUniverse
     OS = self.OpticalSystem
     PPop = self.PlanetPopulation
     PPMod = self.PlanetPhysicalModel
     
     # set chargo to False initially
     chargo = False
     for i in xrange(len(pInds)):
         if observationPossible[i]:
             # is planet visible at the end of the observation time?
             if Obs.kogood[sInd]:
                 # propagate planet to observational period end, and find dMagend
                 dt = t_int[i] if t_char_calc else t_trueint[i] + Obs.settlingTime
                 j = pInds[[i]] # must be an array of size 1
                 rend, vend, send, dend = SU.prop_system(SU.r[j],SU.v[j],\
                         SU.Mp[j],TL.MsTrue[sInd], dt)
                 Phi = PPMod.calc_Phi(np.arcsin(send/dend))
                 dMagend = deltaMag(SU.p[j],SU.Rp[j],dend,Phi)[0]
                 WAend = np.arctan(send/TL.dist[sInd])[0]
                 if (dMagend <= OS.dMagLim) & (WAend >= OS.IWA):
                     obsRes = 1
                     # planet visible at the end of observation
                     if not t_char_calc:
                         # update integration time
                         t_int = max(t_trueint[i],t_int) if t_int != TL.maxintTime[sInd] else t_trueint[i]
                 # if kogood, do characterization
                 if t_char_calc:
                     chargo = True
     
     if t_char_calc:
         if np.any(observationPossible):
             t_int = np.max(t_int[observationPossible])
         return t_int, observationPossible, chargo
     else:
         return t_int, observationPossible
コード例 #21
0
 def propag_system(self, sInd, dt):
     """Propagates planet time-dependant parameters: position, velocity, 
     planet-star distance, apparent separation, phase function, surface brightness 
     of exo-zodiacal light, delta magnitude, and working angle.
     
     This method uses the Kepler state transition matrix to propagate a 
     planet's state (position and velocity vectors) forward in time using 
     the Kepler state transition matrix.
     
     Args:
         sInd (integer):
             Index of the target system of interest
         dt (astropy Quantity):
             Time increment in units of day, for planet position propagation
     
     """
     
     PPMod = self.PlanetPhysicalModel
     ZL = self.ZodiacalLight
     TL = self.TargetList
     
     assert np.isscalar(sInd), \
             "Can only propagate one system at a time, sInd must be scalar."
     # check for planets around this target
     pInds = np.where(self.plan2star == sInd)[0]
     if len(pInds) == 0:
         return
     # check for positive time increment
     assert dt >= 0, "Time increment (dt) to propagate a planet must be positive."
     if dt == 0:
         return
     
     # Calculate initial positions in AU and velocities in AU/day
     r0 = self.r[pInds].to('AU').value
     v0 = self.v[pInds].to('AU/day').value
     # stack dimensionless positions and velocities
     nPlans = pInds.size
     x0 = np.reshape(np.concatenate((r0, v0), axis=1), nPlans*6)
     
     # Calculate vector of gravitational parameter in AU3/day2
     Ms = TL.MsTrue[[sInd]]
     Mp = self.Mp[pInds]
     mu = (const.G*(Mp + Ms)).to('AU3/day2').value
     
     # use keplerSTM.py to propagate the system
     prop = planSys(x0, mu, epsmult=10.)
     try:
         prop.takeStep(dt.to('day').value)
     except ValueError:
         #try again with larger epsmult and two steps to force convergence 
         prop = planSys(x0, mu, epsmult=100.)
         try:
             prop.takeStep(dt.to('day').value/2.)
             prop.takeStep(dt.to('day').value/2.)
         except ValueError:
             raise ValueError('planSys error')
     
     # split off position and velocity vectors
     x1 = np.array(np.hsplit(prop.x0, 2*nPlans))
     rind = np.array(range(0, len(x1), 2)) # even indices
     vind = np.array(range(1, len(x1), 2)) # odd indices
     
     # update planets' position, velocity, planet-star distance, apparent, separation,
     # phase function, exozodi surface brightness, delta magnitude and working angle
     self.r[pInds] = x1[rind]*u.AU
     self.v[pInds] = x1[vind]*u.AU/u.day
     self.d[pInds] = np.linalg.norm(self.r[pInds], axis=1)*self.r.unit
     self.s[pInds] = np.linalg.norm(self.r[pInds,0:2], axis=1)*self.r.unit
     self.phi[pInds] = PPMod.calc_Phi(np.arccos(self.r[pInds,2]/self.d[pInds]))
     self.fEZ[pInds] = ZL.fEZ(TL.MV[sInd], self.I[pInds], self.d[pInds])
     self.dMag[pInds] = deltaMag(self.p[pInds], self.Rp[pInds], self.d[pInds],
             self.phi[pInds])
     self.WA[pInds] = np.arctan(self.s[pInds]/TL.dist[sInd]).to('arcsec')
def cij(a, e, M0, I, w, O, Rp, p, t, mu, potential_planets, d, IWA, OWA,
        dMag0):
    '''
    Calculates the dynamic completeness value of the second visit to a star
    
    Args:
        a (ndarray of Astropy quantities):
            Semi-major axis
        e (ndarray):
            Eccentricity
        M0 (ndarray of Astropy quantities):
            Mean anomaly
        I (Astropy quantity):
            Inclination (rad)
        w (Astropy quantity):
            Argument of periastron (rad)
        O (Astropy quantity):
            Longitude of the ascending node (rad)
        Rp (Astropy quantity):
            Planetary radius
        p (float):
            Geometric albedo of planets
        t (float):
            Time progressed in seconds
        mu (Astropy quantity):
            Gravitational parameter
        potential_planets (ndarray of booleans):
            A true value indicates that the planet has not been eliminated from the search around this planet
        d (astropy quantity):
            Distance to star
        IWA (astropy quantity):
            Telescope's inner working angle
        OWA (astropy quantity):
            Telescope's outer working angle
        dMag0 (float):
            Telescope's limiting difference in brightness between the star and planet
    Returns:
        c2j (ndarray):
            Dynamic completeness value
    '''
    total_planets = len(a)  #total number of planets

    # Get indices for which planets are to be propagated
    planet_indices = np.arange(
        total_planets
    )  #np.linspace(0, total_planets-1, total_planets).astype(int)
    potential_planet_indices = planet_indices[potential_planets]

    # Get the values for the propagated planets
    a_p = a[potential_planets]
    e_p = e[potential_planets]
    M0_p = M0[potential_planets]
    I_p = I[potential_planets]
    w_p = w[potential_planets]
    Rp_p = Rp[potential_planets]
    p_p = p[potential_planets]

    # Calculate the mean anomaly for the planet population after the time period
    M1 = meananom(mu, a_p, t, M0_p)

    # Calculate the anomalies for each planet after the time period passes
    E = fun.eccanom(M1.value, e_p)
    nu = fun.trueanom(E, e_p)

    theta = nu + w_p.value
    r = a_p * (1. - e_p**2.) / (1. + e_p * np.cos(nu))
    s = (r.value/4.)*np.sqrt(4.*np.cos(2.*I_p.value) + 4.*np.cos(2.*theta)-2.*np.cos(2.*I_p.value-2.*theta) \
         - 2.*np.cos(2.*I_p.value+2.*theta) + 12.) #From
    beta = np.arccos(-np.sin(I_p) * np.sin(theta))
    phi = (np.sin(beta.value) +
           (np.pi - beta.value) * np.cos(beta.value)) / np.pi
    dMag = deltaMag(p_p, Rp_p.to(u.km), r.to(u.AU), phi)

    min_separation = IWA.to(u.arcsec).value * dist_to_star.to(u.pc).value
    max_separation = OWA.to(u.arcsec).value * dist_to_star.to(u.pc).value

    # Right now visible planets is an array with the size of the number of potential_planets
    # I need to convert it back to size of the total number of planets with each
    visible_planets = (s > min_separation) & (s < max_separation) & (dMag <
                                                                     dMag0)

    # Calculate the completeness
    cij = np.sum(visible_planets) / float(np.sum(potential_planets))

    # Create an array with all of the visible planets with their original indices
    visible_planet_indices = potential_planet_indices[visible_planets]
    full_visible_planets = np.zeros(total_planets, dtype=bool)
    full_visible_planets[visible_planet_indices] = True

    return [cij, full_visible_planets]
コード例 #23
0
    def gen_update(self, TL):
        """Generates dynamic completeness values for multiple visits of each 
        star in the target list
        
        Args:
            TL (TargetList):
                TargetList class object
        
        """
        
        OS = TL.OpticalSystem
        PPop = TL.PlanetPopulation
        
        # limiting planet delta magnitude for completeness
        dMagMax = self.dMagLim
        
        # get name for stored dynamic completeness updates array
        # inner and outer working angles for detection mode
        mode = list(filter(lambda mode: mode['detectionMode'] == True, OS.observingModes))[0]
        IWA = mode['IWA']
        OWA = mode['OWA']
        extstr = self.extstr + 'IWA: ' + str(IWA) + ' OWA: ' + str(OWA) + \
                ' dMagMax: ' + str(dMagMax) + ' nStars: ' + str(TL.nStars)
        ext = hashlib.md5(extstr.encode('utf-8')).hexdigest()
        self.dfilename += ext 
        self.dfilename += '.dcomp'

        path = os.path.join(self.cachedir, self.dfilename)
        # if the 2D completeness update array exists as a .dcomp file load it
        if os.path.exists(path):
            self.vprint('Loading cached dynamic completeness array from "%s".' % path)
            try:
                with open(path, "rb") as ff:
                    self.updates = pickle.load(ff)
            except UnicodeDecodeError:
                with open(path, "rb") as ff:
                    self.updates = pickle.load(ff,encoding='latin1')
            self.vprint('Dynamic completeness array loaded from cache.')
        else:
            # run Monte Carlo simulation and pickle the resulting array
            self.vprint('Cached dynamic completeness array not found at "%s".' % path)
            self.vprint('Beginning dynamic completeness calculations')
            # dynamic completeness values: rows are stars, columns are number of visits
            self.updates = np.zeros((TL.nStars, 5))
            # number of planets to simulate
            nplan = int(2e4)
            # sample quantities which do not change in time
            a, e, p, Rp = PPop.gen_plan_params(nplan)
            a = a.to('AU').value
            # sample angles
            I, O, w = PPop.gen_angles(nplan)
            I = I.to('rad').value
            O = O.to('rad').value
            w = w.to('rad').value
            Mp = PPop.gen_mass(nplan) # M_earth
            rmax = a*(1.+e) # AU
            # sample quantity which will be updated
            M = np.random.uniform(high=2.*np.pi,size=nplan)
            newM = np.zeros((nplan,))
            # population values
            smin = (np.tan(IWA)*TL.dist).to('AU').value
            if np.isfinite(OWA):
                smax = (np.tan(OWA)*TL.dist).to('AU').value
            else:
                smax = np.array([np.max(PPop.arange.to('AU').value)*\
                        (1.+np.max(PPop.erange))]*TL.nStars)
            # fill dynamic completeness values
            for sInd in xrange(TL.nStars):
                mu = (const.G*(Mp + TL.MsTrue[sInd])).to('AU3/day2').value
                n = np.sqrt(mu/a**3) # in 1/day
                # normalization time equation from Brown 2015
                dt = 58.0*(TL.L[sInd]/0.83)**(3.0/4.0)*(TL.MsTrue[sInd]/(0.91*u.M_sun))**(1.0/2.0) # days
                # remove rmax < smin 
                pInds = np.where(rmax > smin[sInd])[0]
                # calculate for 5 successive observations
                for num in xrange(5):
                    if num == 0:
                        self.updates[sInd, num] = TL.comp0[sInd]
                    if not pInds.any():
                        break
                    # find Eccentric anomaly
                    if num == 0:
                        E = eccanom(M[pInds],e[pInds])
                        newM[pInds] = M[pInds]
                    else:
                        E = eccanom(newM[pInds],e[pInds])
                    
                    r1 = a[pInds]*(np.cos(E) - e[pInds])
                    r1 = np.hstack((r1.reshape(len(r1),1), r1.reshape(len(r1),1), r1.reshape(len(r1),1)))
                    r2 = (a[pInds]*np.sin(E)*np.sqrt(1. -  e[pInds]**2))
                    r2 = np.hstack((r2.reshape(len(r2),1), r2.reshape(len(r2),1), r2.reshape(len(r2),1)))
                    
                    a1 = np.cos(O[pInds])*np.cos(w[pInds]) - np.sin(O[pInds])*np.sin(w[pInds])*np.cos(I[pInds])
                    a2 = np.sin(O[pInds])*np.cos(w[pInds]) + np.cos(O[pInds])*np.sin(w[pInds])*np.cos(I[pInds])
                    a3 = np.sin(w[pInds])*np.sin(I[pInds])
                    A = np.hstack((a1.reshape(len(a1),1), a2.reshape(len(a2),1), a3.reshape(len(a3),1)))
                    
                    b1 = -np.cos(O[pInds])*np.sin(w[pInds]) - np.sin(O[pInds])*np.cos(w[pInds])*np.cos(I[pInds])
                    b2 = -np.sin(O[pInds])*np.sin(w[pInds]) + np.cos(O[pInds])*np.cos(w[pInds])*np.cos(I[pInds])
                    b3 = np.cos(w[pInds])*np.sin(I[pInds])
                    B = np.hstack((b1.reshape(len(b1),1), b2.reshape(len(b2),1), b3.reshape(len(b3),1)))
                    
                    # planet position, planet-star distance, apparent separation
                    r = (A*r1 + B*r2) # position vector (AU)
                    d = np.linalg.norm(r,axis=1) # planet-star distance
                    s = np.linalg.norm(r[:,0:2],axis=1) # apparent separation
                    beta = np.arccos(r[:,2]/d) # phase angle
                    Phi = self.PlanetPhysicalModel.calc_Phi(beta*u.rad) # phase function
                    dMag = deltaMag(p[pInds],Rp[pInds],d*u.AU,Phi) # difference in magnitude
                    
                    toremoves = np.where((s > smin[sInd]) & (s < smax[sInd]))[0]
                    toremovedmag = np.where(dMag < dMagMax)[0]
                    toremove = np.intersect1d(toremoves, toremovedmag)
                    
                    pInds = np.delete(pInds, toremove)
                    
                    if num == 0:
                        self.updates[sInd, num] = TL.comp0[sInd]
                    else:
                        self.updates[sInd, num] = float(len(toremove))/nplan
                    
                    # update M
                    newM[pInds] = (newM[pInds] + n[pInds]*dt)/(2*np.pi) % 1 * 2.*np.pi
                    
                if (sInd+1) % 50 == 0:
                    self.vprint('stars: %r / %r' % (sInd+1,TL.nStars))
            # ensure that completeness values are between 0 and 1
            self.updates = np.clip(self.updates, 0., 1.)
            # store dynamic completeness array as .dcomp file
            with open(path, 'wb') as ff:
                pickle.dump(self.updates, ff)
            self.vprint('Dynamic completeness calculations finished')
            self.vprint('Dynamic completeness array stored in %r' % path)
if PPop.scaleOrbits:
    s /= np.sqrt(TL.L)
beta = np.array([1.10472881476178] * len(s)) * u.rad

# fix out of range values
below = np.where(s < np.min(PPop.rrange) * np.sin(beta))[0]
above = np.where(s > np.max(PPop.rrange) * np.sin(beta))[0]
s[below] = np.sin(beta[below]) * np.min(PPop.rrange)
beta[above] = np.arcsin(s[above] / np.max(PPop.rrange))

# calculate delta mag
p = np.max(PPop.prange)
Rp = np.max(PPop.Rprange)
d = s / np.sin(beta)
Phi = PPMod.calc_Phi(beta)
i = np.where(deltaMag(p, Rp, d, Phi) < Comp.dMagLim)[0]
postmax_dmag_filter = len(i)
#################################################

#### int_cutoff_filter ##########################
nanVmagInds = np.argwhere(np.isnan(TL.Vmag))
nanBVInds = np.argwhere(np.isnan(TL.BV))
sInds = list(np.arange(len(TL.Vmag)))
sInds = np.asarray(
    [ind for ind in sInds if not ind in nanVmagInds and not ind in nanBVInds])
#DELETE mV = TL.starMag(sInds,565.0*u.nm)
#DELETE Cp, Cb, Csp = OS.Cp_Cb_Csp(TL, sInds, )
mode = list(
    filter(lambda mode: mode['detectionMode'] == True,
           TL.OpticalSystem.observingModes))[0]
fZ = 0. / u.arcsec**2
コード例 #25
0
 def propag_system(self, sInd, currentTimeNorm):
     """Propagates planet time-dependant parameters: position, velocity, 
     planet-star distance, apparent separation, phase function, surface brightness 
     of exo-zodiacal light, delta magnitude, working angle, and the planet 
     current time array.
     
     This method uses the Kepler state transition matrix to propagate a 
     planet's state (position and velocity vectors) forward in time using 
     the Kepler state transition matrix.
     
     Args:
         sInd (integer):
             Index of the target system of interest
         currentTimeNorm (astropy Quantity):
             Current mission time normalized to zero at mission start in units of day
     
     """
     
     PPMod = self.PlanetPhysicalModel
     ZL = self.ZodiacalLight
     TL = self.TargetList
     
     assert np.isscalar(sInd), "Can only propagate one system at a time, \
             sInd must be scalar."
     # check for planets around this target
     pInds = np.where(self.plan2star == sInd)[0]
     if not np.any(pInds):
         return
     # check for positive time increment
     dt = currentTimeNorm - self.planTime[pInds][0]
     assert dt >= 0, "Time increment (dt) to propagate a planet must be positive."
     if dt == 0:
         return
     
     # Initial positions in AU and velocities in AU/day
     rold = self.r[pInds].to('AU').value
     vold = self.v[pInds].to('AU/day').value
     # stack dimensionless positions and velocities
     x0 = np.array([])
     for i in xrange(len(rold)):
         x0 = np.hstack((x0, rold[i], vold[i]))
     
     # calculate system's distance and masses
     sDist = TL.dist[[sInd]]
     Ms = TL.MsTrue[[sInd]]*const.M_sun
     Mp = self.Mp[pInds]
     # calculate vector of gravitational parameter
     mu = (const.G*(Mp + Ms)).to('AU3/day2').value
     
     # use keplerSTM.py to propagate the system
     prop = planSys(x0, mu, epsmult=10.)
     try:
         prop.takeStep(dt.to('day').value)
     except ValueError:
         #try again with larger epsmult and two steps to force convergence 
         prop = planSys(x0, mu, epsmult=100.)
         try:
             prop.takeStep(dt.to('day').value/2.)
             prop.takeStep(dt.to('day').value/2.)
         except ValueError:
             raise ValueError('planSys error')
     
     # split off position and velocity vectors
     x1 = np.array(np.hsplit(prop.x0, 2*len(rold)))
     rind = np.array(range(0,len(x1),2)) # even indices
     vind = np.array(range(1,len(x1),2)) # odd indices
     
     # update planets' position, velocity, planet-star distance, apparent 
     # separation, phase function, exozodi surface brightness, delta magnitude, 
     # working angle, and current time
     self.r[pInds] = x1[rind]*u.AU
     self.v[pInds] = x1[vind]*u.AU/u.day
     self.d[pInds] = np.sqrt(np.sum(self.r[pInds]**2, axis=1))
     self.s[pInds] = np.sqrt(np.sum(self.r[pInds,0:2]**2, axis=1))
     self.phi[pInds] = PPMod.calc_Phi(np.arcsin(self.s[pInds]/self.d[pInds]))
     self.fEZ[pInds] = ZL.fEZ(TL, sInd, self.I[pInds],self.d[pInds])
     self.dMag[pInds] = deltaMag(self.p[pInds],self.Rp[pInds],self.d[pInds],self.phi[pInds])
     self.WA[pInds] = np.arctan(self.s[pInds]/sDist).to('mas')
     self.planTime[pInds] = currentTimeNorm
コード例 #26
0
 def observation_detection(self, pInds, sInd, DRM, planPosTime):
     """Finds if planet observations are possible and relevant information
     
     This method makes use of the following inherited class objects:
     
     Args:
         pInds (ndarray):
             1D numpy ndarray of planet indices
         sInd (int):
             target star index
         DRM (dict):
             dictionary containing simulation results
         planPosTime (Quantity):
             1D numpy ndarray containing times of planet positions (units of
             time)
     
     Returns:
         observationPossible, t_int, DRM (ndarray, Quantity, dict, Quantity, ndarray, Quantity):
             1D numpy ndarray of booleans indicating if an observation of 
             each planet is possible, integration time (units of time), 
             dictionary containing survey simulation results, apparent 
             separation (units of distance), 1D numpy ndarray of delta 
             magnitude, difference in magnitude between planet and star,
             irradiance (units of :math:`1/(m^2*nm*s)`)
     
     """
     
     Obs = self.Observatory
     TK = self.TimeKeeping
     SU = self.SimulatedUniverse
     TL = self.TargetList
     OS = self.OpticalSystem
     ZL = self.ZodiacalLight
     PPop = self.PlanetPopulation
     PPMod = self.PlanetPhysicalModel
     
     # initialize with True if planets are present at the target star 
     observationPossible = np.ones(len(pInds),bool) if len(pInds) else False
     # propagate the system if a planet position time do not match up with current time
     for i,pInd in enumerate(pInds):
         if planPosTime[pInd] != TK.currentTimeNorm:
             # propagate planet positions and velocities
             try:
                 dt = TK.currentTimeNorm - planPosTime[pInd]
                 j = np.array([pInd])
                 SU.r[j],SU.v[j],SU.s[j],SU.d[j] = SU.prop_system(SU.r[j],\
                         SU.v[j],SU.Mp[j],TL.MsTrue[sInd],dt)
                 # update planet position times
                 planPosTime[pInd] += dt
             except ValueError:
                 observationPossible[i] = False
     
     # set integration time to max integration time as a default
     t_int = TL.maxintTime[sInd]
     
     # determine true integration time and update observationPossible
     if np.any(observationPossible):
         sInds = np.array([sInd]*len(pInds))
         Phi = PPMod.calc_Phi(np.arcsin(SU.s[pInds]/SU.d[pInds]))
         dMag = deltaMag(SU.p[pInds],SU.Rp[pInds],SU.d[pInds],Phi)
         WA = SU.get_current_WA(pInds)
         fEZ = SU.fEZ[pInds]
         fZ = ZL.fZ(TL,sInds,OS.Imager['lam'],Obs.r_sc)
         t_trueint = OS.calc_intTime(TL,sInds,dMag,WA,fEZ,fZ)
         observationPossible = observationPossible & (t_trueint <= OS.intCutoff)
     
     # determine if planets are observable at the end of observation
     # and update integration time
     if np.any(observationPossible):
         try:
             t_int, observationPossible = self.check_visible_end(observationPossible,\
                     t_int, t_trueint, sInd, pInds, False)
         except ValueError:
             observationPossible = False
     
     if OS.haveOcculter:
         # find disturbance forces on occulter
         dF_lateral, dF_axial = Obs.distForces(TK, TL, sInd)
         # store these values
         DRM['dF_lateral'] = dF_lateral.to('N').value
         DRM['dF_axial'] = dF_axial.to('N').value
         # decrement mass for station-keeping
         intMdot, mass_used, deltaV = Obs.mass_dec(dF_lateral, t_int)
         # store these values
         DRM['det_dV'] = deltaV.to('m/s').value
         DRM['det_mass_used'] = mass_used.to('kg').value
         Obs.scMass -= mass_used
         
         # patch negative t_int
         if np.any(t_int < 0):
             Logger.warning('correcting negative t_int to arbitrary value')
             t_int = (1.0+np.random.rand())*u.day
     
     return observationPossible, t_int, DRM
コード例 #27
0
 def observation_characterization(self, observationPossible, pInds, sInd, spectra, DRM, FA, t_int):
     """Finds if characterizations are possible and relevant information
     
     Args:
         observationPossible (ndarray):
             1D numpy ndarray of booleans indicating if an observation of 
             each planet is possible
         pInds (ndarray):
             1D numpy ndarray of planet indices
         sInd (int):
             target star index
         spectra (ndarray):
             numpy ndarray of values indicating if planet spectra has been
             captured
         DRM (dict):
             dictionary containing survey simulation results
         FA (bool):
             False Alarm boolean
         t_int (Quantity):
             integration time (units of time)
     
     Returns:
         DRM, FA, spectra (dict, bool, ndarray):
             dictionary containing survey simulation results, False Alarm 
             boolean, numpy ndarray of values indicating if planet spectra 
             has been captured
             
     """
     
     Obs = self.Observatory
     SU = self.SimulatedUniverse
     TL = self.TargetList
     TK = self.TimeKeeping
     OS = self.OpticalSystem
     ZL = self.ZodiacalLight
     PPop = self.PlanetPopulation
     PPMod = self.PlanetPhysicalModel
     
     # check if characterization has been done
     if pInds.shape[0] != 0:
         if np.any(observationPossible):
             if np.any(spectra[pInds[observationPossible]] == 0):
                 # perform first characterization
                 # find characterization time
                 sInds = np.array([sInd]*len(pInds))
                 Phi = PPMod.calc_Phi(np.arcsin(SU.s[pInds]/SU.d[pInds]))
                 dMag = deltaMag(SU.p[pInds],SU.Rp[pInds],SU.d[pInds],Phi)
                 WA = SU.get_current_WA(pInds)
                 fEZ = SU.fEZ[pInds]
                 fZ = ZL.fZ(TL,sInds,OS.Spectro['lam'],Obs.r_sc)
                 t_char = OS.calc_charTime(TL,sInds,dMag,WA,fZ,fEZ)
                 # account for 5 bands and one coronagraph
                 t_char *= 4
                 # patch negative t_char
                 if np.any(t_char < 0):
                     Logger.warning('correcting negative t_char to arb. value')
                     t_char_value = (4+2*np.random.rand())*u.day
                     t_char[t_char < 0] = t_char_value
                 
                 # determine which planets will be observable at the end of observation
                 charPossible = observationPossible & (t_char <= OS.intCutoff)
                 
                 try:
                     t_char, charPossible, chargo = self.check_visible_end(charPossible, \
                             t_char, t_char, sInd, pInds, True)
                 except ValueError:
                     chargo = False
                 
                 if chargo:
                     # encode relevant first characterization data
                     if OS.haveOcculter:
                         # decrement sc mass
                         # find disturbance forces on occulter
                         dF_lateral, dF_axial = Obs.distForces(TK, TL, sInd)
                         # decrement mass for station-keeping
                         intMdot, mass_used, deltaV = Obs.mass_dec(dF_lateral, t_int)
                         mass_used_char = t_char*intMdot
                         deltaV_char = dF_lateral/Obs.scMass*t_char
                         Obs.scMass -= mass_used_char
                         # encode information in DRM
                         DRM['char_1_time'] = t_char.to('day').value
                         DRM['char_1_dV'] = deltaV_char.to('m/s').value
                         DRM['char_1_mass_used'] = mass_used_char.to('kg').value
                     else:
                         DRM['char_1_time'] = t_char.to('day').value
                     
                     # if integration time goes beyond observation duration, set quantities
                     if not TK.allocate_time(t_char.max()):
                         charPossible = False
                     
                     # if this was a false alarm, it has been noted, update FA
                     if FA:
                         FA = False
                     
                     # if planet is visible at end of characterization,
                     # spectrum is captured
                     if np.any(charPossible):
                         if OS.haveOcculter:
                             spectra[pInds[charPossible]] = 1
                             # encode success
                             DRM['char_1_success'] = 1
                         else:
                             lamEff = np.arctan(SU.s[pInds]/TL.dist[sInd]) / OS.IWA.to('rad')
                             lamEff *= OS.Spectro['lam']/OS.pupilDiam*np.sqrt(OS.pupilArea/OS.shapeFac)
                             charPossible = charPossible & (lamEff >= 800.*u.nm)
                             # encode results
                             if np.any(charPossible):
                                 spectra[pInds[charPossible]] = 1
                                 DRM['char_1_success'] = 1
                             else:
                                 DRM['char_1_success'] = lamEff.max().to('nm').value
     
     return DRM, FA, spectra
s_circle = np.asarray([s_inner.to('AU').value])

#NEED TO MAKE GOOD HANDLING FOR E=0 ORBITS. SPECIFICALLY FOR MIN AND MAX SOLVING
# dmajorp,dminorp,_,_,Op,x,y,Phi,xreal,only2RealInds,yrealAllRealInds,fourIntInds,twoIntOppositeXInds,twoIntSameYInds,nu_minSepPoints,nu_maxSepPoints,\
#     nu_lminSepPoints,nu_lmaxSepPoints,nu_fourInt,nu_twoIntSameY,nu_twoIntOppositeX,nu_IntersectionsOnly2, yrealImagInds,\
#     t_minSep,t_maxSep,t_lminSep,t_lmaxSep,t_fourInt0,t_fourInt1,t_fourInt2,t_fourInt3,t_twoIntSameY0,\
#     t_twoIntSameY1,t_twoIntOppositeX0,t_twoIntOppositeX1,t_IntersectionOnly20,t_IntersectionOnly21,\
#     _,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_, periods = calcMasterIntersections(smavenus,e,W,w,inc,s_circle,starMass,False)\

#need beta from true anomaly function
betas = betaFunc(inc, nus, w)
beta_smax = betaFunc(inc, 0., w)
Phis = quasiLambertPhaseFunction(betas)
Phi_smax = quasiLambertPhaseFunction(beta_smax)
rsvenus = smavenus * (1. - e**2.) / (1. + e * np.cos(nus))
dmags_venus = deltaMag(pvenus, Rpvenus, rsvenus[0], Phis)
dmag_venus_smax = deltaMag(pvenus, Rpvenus, rsvenus[0], Phi_smax)
seps_venus = planet_star_separation(smavenus, e, nus, w, inc)
WA_venus_smax = (smavenus.to('AU').value /
                 (15. * u.pc.to('AU'))) * u.rad.to('arcsec') * u.arcsec

#Calculate integration time at WA_venus_smax

venus_intTime = OS.calc_intTime(TL, [0], ZL.fZ0 * 100000., ZL.fEZ0 * 100000.,
                                dmag_venus_smax, WA_venus_smax, mode)
mean_anomalyvenus = venus_intTime.to(
    'year').value * 2. * np.pi / periods_venus  #total angle moved by planet
eccentric_anomalyvenus = mean_anomalyvenus  #solve eccentric anomaly from mean anomaly

nus_venus = trueAnomalyFromEccentricAnomaly(
    e, eccentric_anomalyvenus
コード例 #29
0
 def det_data(self, DRM, FA, DET, MD, sInd, pInds, observationPossible, observed):
     """Determines detection status
     
     This method encodes detection status (FA, DET, MD) values in the DRM 
     dictionary.
     
     Args:
         DRM (dict):
             dictionary containing simulation results
         FA (bool):
             Boolean signifying False Alarm
         DET (bool):
             Boolean signifying DETection
         MD (bool):
             Boolean signifying Missed Detection
         sInd (int):
             index of star in target list
         pInds (ndarray):
             idices of planets belonging to target star
         observationPossible (ndarray):
             1D numpy ndarray of booleans indicating if an observation of 
             each planet is possible
         observed (ndarray):
             1D numpy ndarray indicating number of observations for each
             planet
     
     Returns:
         s, DRM, observed (Quantity, dict, ndarray):
             apparent separation (units of distance), delta magnitude, 
             irradiance (units of flux per time), dictionary containing 
             simulation results, 1D numpy ndarray indicating number of 
             observations for each planet
     
     """
     
     SU = self.SimulatedUniverse
     TL = self.TargetList
     PPop = self.PlanetPopulation
     PPMod = self.PlanetPhysicalModel
     
     # planet indexes
     DRM['plan_inds'] = pInds
     # default DRM detection status to null detection
     DRM['det_status'] = 0
     # apparent separation placeholders
     s = SU.s[pInds] if pInds.size else np.array([1.])*u.AU
     
     if FA: # false alarm
         DRM['det_status'] = -2
         ds = np.random.rand()*(PPop.arange.max() - PPop.arange.min())
         s += ds*np.sqrt(TL.L[sInd]) if PPop.scaleOrbits else ds
     elif MD: # missed detection
         DRM['det_status'] = -1
     elif DET: # detection
         observed[pInds[observationPossible]] += 1
         DRM['det_status'] = observationPossible.astype(int).tolist()
         DRM['det_WA'] = np.arctan(s/TL.dist[sInd]).min().to('mas').value
         Phi = PPMod.calc_Phi(np.arcsin(SU.s[pInds]/SU.d[pInds]))
         DRM['det_dMag'] = deltaMag(SU.p[pInds], SU.Rp[pInds], SU.d[pInds],Phi).max()
     
     return s, DRM, observed
コード例 #30
0
#### dmag vs nu extrema and intersection Verification plot
#ind = indsWith4Int[0]
for ind in indsWith4Int:
    num = ind
    plt.figure(num=num)
    plt.rc('axes', linewidth=2)
    plt.rc('lines', linewidth=2)
    plt.rcParams['axes.linewidth'] = 2
    plt.rc('font', weight='bold')

    nus = np.linspace(start=0, stop=2. * np.pi, num=300)
    phis = (1. + np.sin(inc[ind]) * np.sin(nus + w[ind])
            )**2. / 4.  #TRYING THIS TO CIRCUMVENT POTENTIAL ARCCOS
    ds = a[ind] * (1. - e[ind]**2.) / (e[ind] * np.cos(nus) + 1.)
    dmags = deltaMag(p[ind], Rp[ind].to('AU'), ds,
                     phis)  #calculate dmag of the specified x-value

    plt.plot(nus, dmags, color='black', zorder=10)
    #plt.plot([0.,2.*np.pi],[dmag,dmag],color='blue')
    plt.scatter(nuMinDmag[ind],
                mindmag[ind],
                color='cyan',
                marker='d',
                zorder=20)
    plt.scatter(nuMaxDmag[ind],
                maxdmag[ind],
                color='red',
                marker='d',
                zorder=20)
    lind = np.where(ind == indsWith4)[0]
    if ind in indsWith2Int:
    compMethod2 = rawdata['compMethod2']
    total_time_visible_error = rawdata['total_time_visible_error']
    visbools = rawdata['visbools']
else:
    start = time.time()
    for i in np.arange(numPlans):#len(inc)):
        print('Working on: ' + str(i) + '/' + str(numPlans))
        #period = periods[i]
        #np.linspace(start=0.,stop=periods[i])
        M = np.linspace(start=0.,stop=2.*np.pi,num=numPoints) #Even distribution across mean anomaly
        E = np.asarray([eccanom(M[j],e[i]) for j in np.arange(len(M))])
        nus_range = trueAnomalyFromEccentricAnomaly(e[i],E)
        betas = betaFunc(inc[i],nus_range,w[i])
        Phis = quasiLambertPhaseFunction(betas)
        rs = sma[i]*u.AU*(1.-e[i]**2.)/(1.+e[i]*np.cos(nus_range))
        dmags = deltaMag(p[i],Rp[i],rs,Phis)
        seps = planet_star_separation(sma[i],e[i],nus_range,w[i],inc[i])
        visibleBool = (seps > s_inner)*(seps < s_outer)*(dmags < dmag_upper)
        visbools.append(visibleBool)

        compMethod2.append(np.sum(visibleBool.astype('int'))/numPoints)

        total_time_visible_error.append(np.abs(compMethod2[i]-totalCompleteness[i]))
    stop = time.time()
    print('Execution Time (s): ' + str(stop-start))

    compMethod2 = np.asarray(compMethod2)
    print(compMethod2)
    print(totalCompleteness[0:len(compMethod2)])
    print(total_time_visible_error)
    print(np.max(total_time_visible_error))
コード例 #32
0
    def gen_update(self, TL):
        """Generates dynamic completeness values for multiple visits of each 
        star in the target list
        
        Args:
            TL (TargetList module):
                TargetList class object
        
        """

        OS = TL.OpticalSystem
        PPop = self.PlanetPopulation

        print 'Beginning completeness update calculations'
        # initialize number of visits
        self.visits = np.array([0] * TL.nStars)
        # dynamic completeness values: rows are stars, columns are number of visits
        self.updates = np.zeros((TL.nStars, 5))
        # number of planets to simulate
        nplan = int(2e4)
        # normalization time
        dt = 1e9 * u.day
        # sample quantities which do not change in time
        a = PPop.gen_sma(nplan)  # AU
        e = PPop.gen_eccen(nplan)
        I = PPop.gen_I(nplan)  # deg
        O = PPop.gen_O(nplan)  # deg
        w = PPop.gen_w(nplan)  # deg
        p = PPop.gen_albedo(nplan)
        Rp = PPop.gen_radius(nplan)  # km
        Mp = PPop.gen_mass(nplan)  # kg
        rmax = a * (1. + e)
        # sample quantity which will be updated
        M = np.random.uniform(high=2. * np.pi, size=nplan)
        newM = np.zeros((nplan, ))
        # population values
        smin = (np.tan(OS.IWA) * TL.dist).to('AU')
        if np.isfinite(OS.OWA):
            smax = (np.tan(OS.OWA) * TL.dist).to('AU')
        else:
            smax = np.array([np.max(PPop.arange.to('AU').value)*\
                    (1.+np.max(PPop.erange))]*TL.nStars)*u.AU
        # fill dynamic completeness values
        for sInd in xrange(TL.nStars):
            Mstar = TL.MsTrue[sInd] * const.M_sun
            # remove rmax < smin
            pInds = np.where(rmax > smin[sInd])[0]
            # calculate for 5 successive observations
            for num in xrange(5):
                if num == 0:
                    self.updates[sInd, num] = TL.comp0[sInd]
                if not pInds.any():
                    break
                # find Eccentric anomaly
                if num == 0:
                    E = eccanom(M[pInds], e[pInds])
                    newM[pInds] = M[pInds]
                else:
                    E = eccanom(newM[pInds], e[pInds])

                r1 = a[pInds] * (np.cos(E) - e[pInds])
                r1 = np.hstack((r1.reshape(len(r1), 1), r1.reshape(len(r1), 1),
                                r1.reshape(len(r1), 1)))
                r2 = (a[pInds] * np.sin(E) * np.sqrt(1. - e[pInds]**2))
                r2 = np.hstack((r2.reshape(len(r2), 1), r2.reshape(len(r2), 1),
                                r2.reshape(len(r2), 1)))

                a1 = np.cos(O[pInds]) * np.cos(w[pInds]) - np.sin(
                    O[pInds]) * np.sin(w[pInds]) * np.cos(I[pInds])
                a2 = np.sin(O[pInds]) * np.cos(w[pInds]) + np.cos(
                    O[pInds]) * np.sin(w[pInds]) * np.cos(I[pInds])
                a3 = np.sin(w[pInds]) * np.sin(I[pInds])
                A = np.hstack((a1.reshape(len(a1), 1), a2.reshape(len(a2), 1),
                               a3.reshape(len(a3), 1)))

                b1 = -np.cos(O[pInds]) * np.sin(w[pInds]) - np.sin(
                    O[pInds]) * np.cos(w[pInds]) * np.cos(I[pInds])
                b2 = -np.sin(O[pInds]) * np.sin(w[pInds]) + np.cos(
                    O[pInds]) * np.cos(w[pInds]) * np.cos(I[pInds])
                b3 = np.cos(w[pInds]) * np.sin(I[pInds])
                B = np.hstack((b1.reshape(len(b1), 1), b2.reshape(len(b2), 1),
                               b3.reshape(len(b3), 1)))

                # planet position, planet-star distance, apparent separation
                r = (A * r1 + B * r2) * u.AU  # position vector
                d = np.sqrt(np.sum(r**2, axis=1))  # planet-star distance
                s = np.sqrt(np.sum(r[:, 0:2]**2,
                                   axis=1))  # apparent separation
                beta = np.arccos(r[:, 2] / d)  # phase angle
                Phi = self.PlanetPhysicalModel.calc_Phi(beta)  # phase function
                dMag = deltaMag(p[pInds], Rp[pInds], d,
                                Phi)  # difference in magnitude

                toremoves = np.where((s > smin[sInd]) & (s < smax[sInd]))[0]
                toremovedmag = np.where(dMag < OS.dMagLim)[0]
                toremove = np.intersect1d(toremoves, toremovedmag)

                pInds = np.delete(pInds, toremove)

                if num == 0:
                    self.updates[sInd, num] = TL.comp0[sInd]
                else:
                    self.updates[sInd, num] = float(len(toremove)) / nplan

                # update M
                mu = const.G * (Mstar + Mp[pInds])
                n = np.sqrt(mu / a[pInds]**3)
                newM[pInds] = (newM[pInds] + n * dt) / (2 *
                                                        np.pi) % 1 * 2. * np.pi

            if (sInd + 1) % 50 == 0:
                print 'stars: %r / %r' % (sInd + 1, TL.nStars)

        print 'Completeness update calculations finished'
コード例 #33
0
    def set_planet_phase(self, beta=np.pi / 2):
        """Positions all planets at input star-planet-observer phase angle
        where possible. For systems where the input phase angle is not achieved,
        planets are positioned at quadrature (phase angle of 90 deg).
        
        The position found here is not unique. The desired phase angle will be
        achieved at two points on the planet's orbit (for non-face on orbits).
        
        Args:
            beta (float):
                star-planet-observer phase angle in radians.
        
        """

        PPMod = self.PlanetPhysicalModel
        ZL = self.ZodiacalLight
        TL = self.TargetList

        a = self.a.to('AU').value  # semi-major axis
        e = self.e  # eccentricity
        I = self.I.to('rad').value  # inclinations
        O = self.O.to('rad').value  # right ascension of the ascending node
        w = self.w.to('rad').value  # argument of perigee
        Mp = self.Mp  # planet masses

        # make list of betas
        betas = beta * np.ones(w.shape)
        mask = np.cos(betas) / np.sin(I) > 1.
        num = len(np.where(mask == True)[0])
        betas[mask] = np.pi / 2.
        mask = np.cos(betas) / np.sin(I) < -1.
        num += len(np.where(mask == True)[0])
        betas[mask] = np.pi / 2.
        if num > 0:
            self.vprint('***Warning***')
            self.vprint(
                '{} planets out of {} could not be set to phase angle {} radians.'
                .format(num, self.nPlans, beta))
            self.vprint(
                'These planets are set to quadrature (phase angle pi/2)')

        # solve for true anomaly
        nu = np.arcsin(np.cos(betas) / np.sin(I)) - w

        # setup for position and velocity
        a1 = np.cos(O) * np.cos(w) - np.sin(O) * np.cos(I) * np.sin(w)
        a2 = np.sin(O) * np.cos(w) + np.cos(O) * np.cos(I) * np.sin(w)
        a3 = np.sin(I) * np.sin(w)
        A = np.vstack((a1, a2, a3))

        b1 = -(np.cos(O) * np.sin(w) + np.sin(O) * np.cos(I) * np.cos(w))
        b2 = (-np.sin(O) * np.sin(w) + np.cos(O) * np.cos(I) * np.cos(w))
        b3 = np.sin(I) * np.cos(w)
        B = np.vstack((b1, b2, b3))

        r = a * (1. - e**2) / (1. - e * np.cos(nu))
        mu = const.G * (Mp + TL.MsTrue[self.plan2star])
        v1 = -np.sqrt(mu / (self.a * (1. - self.e**2))) * np.sin(nu)
        v2 = np.sqrt(mu / (self.a * (1. - self.e**2))) * (self.e + np.cos(nu))

        self.r = (A * r * np.cos(nu) + B * r * np.sin(nu)).T * u.AU  # position
        self.v = (A * v1 + B * v2).T.to('AU/day')  # velocity
        # if sys.version_info[0] > 2:
        #     self.d = np.linalg.norm(self.r, axis=1)    # planet-star distance
        #     self.s = np.linalg.norm(self.r[:,0:2], axis=1)  # apparent separation
        # else:
        #     self.d = np.linalg.norm(self.r, axis=1)*self.r.unit  # planet-star distance
        #     self.s = np.linalg.norm(self.r[:,0:2], axis=1)*self.r.unit  # apparent separation

        try:
            self.d = np.linalg.norm(self.r, axis=1)  # planet-star distance
            self.phi = PPMod.calc_Phi(
                np.arccos(self.r[:, 2].to('AU').value / self.d.to('AU').value)
                * u.rad)  # planet phase
        except:
            self.d = np.linalg.norm(
                self.r, axis=1) * self.r.unit  # planet-star distance
            self.phi = PPMod.calc_Phi(
                np.arccos(self.r[:, 2].to('AU').value / self.d.to('AU').value)
                * u.rad)  # planet phase

        self.fEZ = ZL.fEZ(TL.MV[self.plan2star], self.I,
                          self.d)  # exozodi brightness
        self.dMag = deltaMag(self.p, self.Rp, self.d,
                             self.phi)  # delta magnitude

        try:
            self.s = np.linalg.norm(self.r[:, 0:2],
                                    axis=1)  # apparent separation
            self.WA = np.arctan(self.s / TL.dist[self.plan2star]).to(
                'arcsec')  # working angle
        except:
            self.s = np.linalg.norm(
                self.r[:, 0:2], axis=1) * self.r.unit  # apparent separation
            self.WA = np.arctan(self.s / TL.dist[self.plan2star]).to(
                'arcsec')  # working angle
コード例 #34
0
    def genplans(self, nplan):
        """Generates planet data needed for Monte Carlo simulation
        
        Args:
            nplan (integer):
                Number of planets
                
        Returns:
            s (astropy Quantity array):
                Planet apparent separations in units of AU
            dMag (ndarray):
                Difference in brightness
        
        """

        PPop = self.PlanetPopulation

        nplan = int(nplan)

        # sample uniform distribution of mean anomaly
        M = np.random.uniform(high=2. * np.pi, size=nplan)
        # sample semi-major axis
        a = PPop.gen_sma(nplan).to('AU').value

        # sample other necessary orbital parameters
        if np.sum(PPop.erange) == 0:
            # all circular orbits
            r = a
            e = 0.
            E = M
        else:
            # sample eccentricity
            if PPop.constrainOrbits:
                e = PPop.gen_eccen_from_sma(nplan, a * u.AU)
            else:
                e = PPop.gen_eccen(nplan)
            # Newton-Raphson to find E
            E = eccanom(M, e)
            # orbital radius
            r = a * (1 - e * np.cos(E))

        # orbit angle sampling
        O = PPop.gen_O(nplan).to('rad').value
        w = PPop.gen_w(nplan).to('rad').value
        I = PPop.gen_I(nplan).to('rad').value

        r1 = a * (np.cos(E) - e)
        r1 = np.hstack((r1.reshape(len(r1),
                                   1), r1.reshape(len(r1),
                                                  1), r1.reshape(len(r1), 1)))
        r2 = a * np.sin(E) * np.sqrt(1. - e**2)
        r2 = np.hstack((r2.reshape(len(r2),
                                   1), r2.reshape(len(r2),
                                                  1), r2.reshape(len(r2), 1)))

        a1 = np.cos(O) * np.cos(w) - np.sin(O) * np.sin(w) * np.cos(I)
        a2 = np.sin(O) * np.cos(w) + np.cos(O) * np.sin(w) * np.cos(I)
        a3 = np.sin(w) * np.sin(I)
        A = np.hstack((a1.reshape(len(a1),
                                  1), a2.reshape(len(a2),
                                                 1), a3.reshape(len(a3), 1)))

        b1 = -np.cos(O) * np.sin(w) - np.sin(O) * np.cos(w) * np.cos(I)
        b2 = -np.sin(O) * np.sin(w) + np.cos(O) * np.cos(w) * np.cos(I)
        b3 = np.cos(w) * np.sin(I)
        B = np.hstack((b1.reshape(len(b1),
                                  1), b2.reshape(len(b2),
                                                 1), b3.reshape(len(b3), 1)))

        # planet position, planet-star distance, apparent separation
        r = (A * r1 + B * r2) * u.AU
        d = np.sqrt(np.sum(r**2, axis=1))
        s = np.sqrt(np.sum(r[:, 0:2]**2, axis=1))

        # sample albedo, planetary radius, phase function
        p = PPop.gen_albedo(nplan)
        Rp = PPop.gen_radius(nplan)
        beta = np.arccos(r[:, 2] / d)
        Phi = self.PlanetPhysicalModel.calc_Phi(beta)

        # calculate dMag
        dMag = deltaMag(p, Rp, d, Phi)

        return s, dMag
コード例 #35
0
#NEED TO BE ABLE TO PUT BOUNDS INTO BOX WITH 4 SIDES
#AND BOX WITH 3 SIDES

#### dmag vs nu extrema and intersection Verification plot
num = 88833543453218
plt.figure(num=num)
plt.rc('axes', linewidth=2)
plt.rc('lines', linewidth=2)
plt.rcParams['axes.linewidth'] = 2
plt.rc('font', weight='bold')
ind = fourIntersectionInd  #indsWith4Int[0]
nus = np.linspace(start=0, stop=2. * np.pi, num=100)
phis = (1. + np.sin(inc[ind]) * np.sin(nus + w[ind])
        )**2. / 4.  #TRYING THIS TO CIRCUMVENT POTENTIAL ARCCOS
ds = sma[ind] * (1. - e[ind]**2.) / (e[ind] * np.cos(nus) + 1.)
dmags = deltaMag(p[ind], Rp[ind].to('AU'), ds * u.AU,
                 phis)  #calculate dmag of the specified x-value

plt.plot(nus, dmags, color='black', zorder=10)
#plt.plot([0.,2.*np.pi],[dmag,dmag],color='blue')
plt.scatter(nuMinDmag[ind], mindmag[ind], color='teal', marker='D', zorder=20)
plt.plot([0., 2. * np.pi], [mindmag[ind], mindmag[ind]],
         color='teal',
         zorder=20)
plt.scatter(nuMaxDmag[ind], maxdmag[ind], color='red', marker='D', zorder=20)
plt.plot([0., 2. * np.pi], [maxdmag[ind], maxdmag[ind]],
         color='red',
         zorder=20)
lind = np.where(ind == indsWith4)[0]
if ind in indsWith2Int:
    mind = np.where(ind == indsWith2Int)[0][0]
    plt.scatter(nus2Int[mind],
コード例 #36
0
 def gen_update(self, targlist):
     """Generates dynamic completeness values for multiple visits of each 
     star in the target list
     
     Args:
         targlist (TargetList):
             TargetList module
     
     """
     
     print 'Beginning completeness update calculations'
     self.visits = np.array([0]*targlist.nStars)
     self.updates = []
     # number of planets to simulate
     nplan = int(2e4)
     # normalization time
     dt = 1e9*u.day
     # sample quantities which do not change in time
     a = self.PlanetPopulation.gen_sma(nplan) # AU
     e = self.PlanetPopulation.gen_eccen(nplan)
     I = self.PlanetPopulation.gen_I(nplan) # deg
     O = self.PlanetPopulation.gen_O(nplan) # deg
     w = self.PlanetPopulation.gen_w(nplan) # deg
     p = self.PlanetPopulation.gen_albedo(nplan)
     Rp = self.PlanetPopulation.gen_radius(nplan) # km
     Mp = self.PlanetPopulation.gen_mass(nplan) # kg
     rmax = a*(1.+e)
     rmin = a*(1.-e)
     # sample quantity which will be updated
     M = np.random.uniform(high=2.*np.pi,size=nplan)
     newM = np.zeros((nplan,))
     # population values
     smin = (np.tan(targlist.OpticalSystem.IWA)*targlist.dist).to('AU')
     if np.isfinite(targlist.OpticalSystem.OWA):
         smax = (np.tan(targlist.OpticalSystem.OWA)*targlist.dist).to('AU')
     else:
         smax = np.array([np.max(self.PlanetPopulation.arange.to('AU').value)*\
                 (1.+np.max(self.PlanetPopulation.erange))]*targlist.nStars)*u.AU
     # fill dynamic completeness values
     for sInd in xrange(targlist.nStars):
         Mstar = targlist.MsTrue[sInd]*const.M_sun
         # remove rmax < smin and rmin > smax
         inside = np.where(rmax > smin[sInd])[0]
         outside = np.where(rmin < smax[sInd])[0]
         pInds = np.intersect1d(inside,outside)
         dynamic = []
         # calculate for 5 successive observations
         for num in xrange(5):
             if not pInds.any():
                 dynamic.append(0.)
                 break
             # find Eccentric anomaly
             if num == 0:
                 E = eccanom(M[pInds],e[pInds])
                 newM[pInds] = M[pInds]
             else:
                 E = eccanom(newM[pInds],e[pInds])
             
             r = a[pInds]*(1.-e[pInds]*np.cos(E))
             r1 = r*(np.cos(E) - e[pInds])
             r1 = np.hstack((r1.reshape(len(r1),1), r1.reshape(len(r1),1), r1.reshape(len(r1),1)))
             r2 = (r*np.sin(E)*np.sqrt(1. -  e[pInds]**2))
             r2 = np.hstack((r2.reshape(len(r2),1), r2.reshape(len(r2),1), r2.reshape(len(r2),1)))
             
             a1 = np.cos(O[pInds])*np.cos(w[pInds]) - np.sin(O[pInds])*np.sin(w[pInds])*np.cos(I[pInds])
             a2 = np.sin(O[pInds])*np.cos(w[pInds]) + np.cos(O[pInds])*np.sin(w[pInds])*np.cos(I[pInds])
             a3 = np.sin(w[pInds])*np.sin(I[pInds])
             A = np.hstack((a1.reshape(len(a1),1), a2.reshape(len(a2),1), a3.reshape(len(a3),1)))
             
             b1 = -np.cos(O[pInds])*np.sin(w[pInds]) - np.sin(O[pInds])*np.cos(w[pInds])*np.cos(I[pInds])
             b2 = -np.sin(O[pInds])*np.sin(w[pInds]) + np.cos(O[pInds])*np.cos(w[pInds])*np.cos(I[pInds])
             b3 = np.cos(w[pInds])*np.sin(I[pInds])
             B = np.hstack((b1.reshape(len(b1),1), b2.reshape(len(b2),1), b3.reshape(len(b3),1)))
             
             # planet position, planet-star distance, apparent separation
             r = (A*r1 + B*r2)*u.AU # position vector
             d = np.sqrt(np.sum(r**2, axis=1)) # planet-star distance
             s = np.sqrt(np.sum(r[:,0:2]**2, axis=1)) # apparent separation
             beta = np.arccos(r[:,2]/d) # phase angle
             Phi = self.PlanetPhysicalModel.calc_Phi(beta) # phase function
             dMag = deltaMag(p[pInds],Rp[pInds],d,Phi) # difference in magnitude
             
             toremoves = np.where((s > smin[sInd]) & (s < smax[sInd]))[0]
             toremovedmag = np.where(dMag < targlist.OpticalSystem.dMagLim)[0]
             toremove = np.intersect1d(toremoves, toremovedmag)
             
             pInds = np.delete(pInds, toremove)
             
             if num == 0:
                 dynamic.append(targlist.comp0[sInd])
             else:
                 dynamic.append(float(len(toremove))/nplan)
             
             # update M
             mu = const.G*(Mstar+Mp[pInds])
             n = np.sqrt(mu/a[pInds]**3)
             newM[pInds] = (newM[pInds] + n*dt)/(2*np.pi) % 1 * 2.*np.pi
         
         self.updates.append(dynamic)
             
         if (sInd+1) % 50 == 0:
             print 'stars: %r / %r' % (sInd+1,targlist.nStars)
     
     self.updates = np.array(self.updates)
     print 'Completeness update calculations finished'