def keplerplanet(self, currentTime, bodyname): """Finds position vector for solar system objects This method uses algorithms 2 and 10 from Vallado 2013 to find heliocentric equatorial position vectors (astropy Quantity in km) for solar system objects. Args: currentTime (astropy Time): Current absolute mission time in MJD bodyname (string): Solar system object name Returns: r_body (astropy Quantity nx3 array): Heliocentric equatorial position vector in units of km """ if bodyname == 'Moon': r_Earth = self.keplerplanet(currentTime, 'Earth') return r_Earth + self.moon_earth(currentTime) assert self.planets.has_key(bodyname),\ "%s is not a recognized body name."%(bodyname) planet = self.planets[bodyname] # find Julian centuries from J2000 TDB = self.cent(currentTime) # update ephemeride data a = self.propeph(planet.a, TDB) e = self.propeph(planet.e, TDB) I = np.radians(self.propeph(planet.I, TDB)) O = np.radians(self.propeph(planet.O, TDB)) w = np.radians(self.propeph(planet.w, TDB)) lM = np.radians(self.propeph(planet.lM, TDB)) # Find mean anomaly and argument of perigee M = np.mod(lM - w,2*np.pi) wp = np.mod(w - O,2*np.pi) # Find eccentric anomaly E = eccanom(M,e)[0] # Find true anomaly nu = np.arctan2(np.sin(E) * np.sqrt(1 - e**2), np.cos(E) - e) # Find semiparameter p = a*(1 - e**2) # position vector (km) in orbital plane rx = p*np.cos(nu)/(1 + e*np.cos(nu)) ry = p*np.sin(nu)/(1 + e*np.cos(nu)) rz = np.zeros(currentTime.size) r_body = np.vstack((rx,ry,rz)) # position vector (km) in ecliptic plane r_body = np.array([np.dot(np.dot(self.rot(-O[x],3),self.rot(-I[x],1)),\ np.dot(self.rot(-wp[x],3),r_body[:,x])) for x in range(currentTime.size)]).T # find obliquity of the ecliptic obe = np.array(np.radians(self.obe(TDB)),ndmin=1) # position vector (km) in heliocentric equatorial frame r_body = np.array([np.dot(self.rot(-obe[x],1),r_body[:,x])\ for x in range(currentTime.size)])*u.km return r_body.to('km')
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
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
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
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
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
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)
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
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'
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
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'
def test_eccanom(self): r"""Test eccentric anomaly computation. Approach: Reference to pre-computed results from Matlab, plus additional check that solutions satisfy Kepler's equation. Tests restricted to eccentricity between 0.0 and 0.4.""" # eccanom() appears to be a specialized version of newtonm.m from # Vallado's m-files. eccanom() implements the case in the m-file # marked "elliptical", because it works for planets in elliptical orbits. print('eccanom()') # precomputed from newtonm.m in Vallado's Matlab source code tabulation = { # a few systematically-chosen values, a few random ones # (eccentricity in [0,0.4] and mean anomaly in [0,2 pi] # label eccentricity mean anomaly ecc-anomaly true-anomaly "syst-0": (0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000), "syst-1": (0.025000000000, 0.400000000000, 0.409964417354, 0.420045901819), "syst-2": (0.050000000000, 0.800000000000, 0.837136437398, 0.874924655639), "syst-3": (0.075000000000, 1.200000000000, 1.271669563802, 1.344211492229), "syst-4": (0.100000000000, 1.600000000000, 1.699177050713, 1.797889059320), "syst-5": (0.125000000000, 2.000000000000, 2.107429361800, 2.211834518809), "syst-6": (0.150000000000, 2.400000000000, 2.490864846892, 2.577019718783), "syst-7": (0.175000000000, 2.800000000000, 2.850264344358, 2.896965705233), "syst-8": (0.200000000000, 3.200000000000, 3.190268645286, -3.101846256861), "syst-9": (0.225000000000, 3.600000000000, 3.517416263407, -2.841371075544), "syst-10": (0.250000000000, 4.000000000000, 3.839370814447, -2.592284710937), "syst-11": (0.275000000000, 4.400000000000, 4.165158556909, -2.340284012237), "syst-12": (0.300000000000, 4.800000000000, 4.506345584831, -2.066225859687), "syst-13": (0.325000000000, 5.200000000000, 4.879529007461, -1.739297079458), "syst-14": (0.350000000000, 5.600000000000, 5.310823513733, -1.301816690532), "syst-15": (0.375000000000, 6.000000000000, 5.838779417089, -0.646704214263), "rand-1": (0.021228232647, 5.868452651315, 5.859729688192, -0.432264760951), "rand-2": (0.016968378871, 4.761021655110, 4.744061786583, -1.556088761716), "rand-3": (0.018578311703, 2.464435046216, 2.475908955821, 2.487300499232), "rand-4": (0.016386947254, 1.075597681642, 1.090127758250, 1.104713814823), "rand-5": (0.017651152200, 0.200011672644, 0.203580329847, 0.207180380010), "rand-6": (0.006923074624, 0.290103403226, 0.292096978801, 0.294097208267), "rand-7": (0.002428294531, 5.173938128028, 5.171761572640, -1.113601464490), "rand-8": (0.017370715574, 1.992394794033, 2.008130645020, 2.023809681520), "rand-9": (0.023755551221, 0.216431106906, 0.221653600172, 0.226938058952), "rand-10": (0.010968608991, 2.397402491437, 2.404772696274, 2.412113273400), "rand-11": (0.019137919704, 4.996388335095, 4.977921138154, -1.323779018431), "rand-12": (0.004671815114, 3.077280455596, 3.077579312617, 3.077877470783), "rand-13": (0.011139655018, 4.060904408970, 4.052106100441, -2.239847775747), "rand-14": (0.017734120771, 4.741836271756, 4.724103367769, -1.576817615338), "rand-15": (0.006900626925, 4.270697872458, 4.264477964900, -2.024918020450), "rand-16": (0.016377450099, 1.021719665350, 1.035808775845, 1.049957669774), "rand-17": (0.002974942039, 3.131313689041, 3.131344177237, 3.131374620007), "rand-18": (0.023993598963, 2.138706596562, 2.158672164638, 2.178507958606), "rand-19": (0.014631693774, 1.406251889782, 1.420719116738, 1.435202708137), "rand-20": (0.018781676483, 1.602809881387, 1.621567356335, 1.640316999728), } for (_, value) in tabulation.items(): (ref_eccentricity, ref_mean_anomaly, ref_ecc_anomaly, _ref_true_anomaly) = value exo_ecc_anomaly = eccanom(ref_mean_anomaly, ref_eccentricity) # 1: ensure the output agrees with the tabulation self.assertAlmostEqual(exo_ecc_anomaly, ref_ecc_anomaly, delta=1e-6) # 2: additionally, ensure the Kepler relation: # M = E - e sin E # is satisfied for the output of eccanom(). # Here, e is the given eccentricity, E is the EXOSIMS eccentric # anomaly, and M is the mean anomaly. est_mean_anomaly = exo_ecc_anomaly - ref_eccentricity * np.sin(exo_ecc_anomaly) self.assertAlmostEqual(ref_mean_anomaly, est_mean_anomaly, delta=1e-6)
def test_eccanom(self): r"""Test eccentric anomaly computation. Approach: Reference to pre-computed results from Matlab, plus additional check that solutions satisfy Kepler's equation. Tests restricted to eccentricity between 0.0 and 0.4.""" # eccanom() appears to be a specialized version of newtonm.m from # Vallado's m-files. eccanom() implements the case in the m-file # marked "elliptical", because it works for planets in elliptical orbits. print 'eccanom()' # precomputed from newtonm.m in Vallado's Matlab source code tabulation = { # a few systematically-chosen values, a few random ones # (eccentricity in [0,0.4] and mean anomaly in [0,2 pi] # label eccentricity mean anomaly ecc-anomaly true-anomaly "syst-0": (0.000000000000, 0.000000000000, 0.000000000000, 0.000000000000), "syst-1": (0.025000000000, 0.400000000000, 0.409964417354, 0.420045901819), "syst-2": (0.050000000000, 0.800000000000, 0.837136437398, 0.874924655639), "syst-3": (0.075000000000, 1.200000000000, 1.271669563802, 1.344211492229), "syst-4": (0.100000000000, 1.600000000000, 1.699177050713, 1.797889059320), "syst-5": (0.125000000000, 2.000000000000, 2.107429361800, 2.211834518809), "syst-6": (0.150000000000, 2.400000000000, 2.490864846892, 2.577019718783), "syst-7": (0.175000000000, 2.800000000000, 2.850264344358, 2.896965705233), "syst-8": (0.200000000000, 3.200000000000, 3.190268645286, -3.101846256861), "syst-9": (0.225000000000, 3.600000000000, 3.517416263407, -2.841371075544), "syst-10": (0.250000000000, 4.000000000000, 3.839370814447, -2.592284710937), "syst-11": (0.275000000000, 4.400000000000, 4.165158556909, -2.340284012237), "syst-12": (0.300000000000, 4.800000000000, 4.506345584831, -2.066225859687), "syst-13": (0.325000000000, 5.200000000000, 4.879529007461, -1.739297079458), "syst-14": (0.350000000000, 5.600000000000, 5.310823513733, -1.301816690532), "syst-15": (0.375000000000, 6.000000000000, 5.838779417089, -0.646704214263), "rand-1": (0.021228232647, 5.868452651315, 5.859729688192, -0.432264760951), "rand-2": (0.016968378871, 4.761021655110, 4.744061786583, -1.556088761716), "rand-3": (0.018578311703, 2.464435046216, 2.475908955821, 2.487300499232), "rand-4": (0.016386947254, 1.075597681642, 1.090127758250, 1.104713814823), "rand-5": (0.017651152200, 0.200011672644, 0.203580329847, 0.207180380010), "rand-6": (0.006923074624, 0.290103403226, 0.292096978801, 0.294097208267), "rand-7": (0.002428294531, 5.173938128028, 5.171761572640, -1.113601464490), "rand-8": (0.017370715574, 1.992394794033, 2.008130645020, 2.023809681520), "rand-9": (0.023755551221, 0.216431106906, 0.221653600172, 0.226938058952), "rand-10": (0.010968608991, 2.397402491437, 2.404772696274, 2.412113273400), "rand-11": (0.019137919704, 4.996388335095, 4.977921138154, -1.323779018431), "rand-12": (0.004671815114, 3.077280455596, 3.077579312617, 3.077877470783), "rand-13": (0.011139655018, 4.060904408970, 4.052106100441, -2.239847775747), "rand-14": (0.017734120771, 4.741836271756, 4.724103367769, -1.576817615338), "rand-15": (0.006900626925, 4.270697872458, 4.264477964900, -2.024918020450), "rand-16": (0.016377450099, 1.021719665350, 1.035808775845, 1.049957669774), "rand-17": (0.002974942039, 3.131313689041, 3.131344177237, 3.131374620007), "rand-18": (0.023993598963, 2.138706596562, 2.158672164638, 2.178507958606), "rand-19": (0.014631693774, 1.406251889782, 1.420719116738, 1.435202708137), "rand-20": (0.018781676483, 1.602809881387, 1.621567356335, 1.640316999728), } for (_, value) in tabulation.iteritems(): (ref_eccentricity, ref_mean_anomaly, ref_ecc_anomaly, _ref_true_anomaly) = value exo_ecc_anomaly = eccanom(ref_mean_anomaly, ref_eccentricity) # 1: ensure the output agrees with the tabulation self.assertAlmostEqual(exo_ecc_anomaly, ref_ecc_anomaly, delta=1e-6) # 2: additionally, ensure the Kepler relation: # M = E - e sin E # is satisfied for the output of eccanom(). # Here, e is the given eccentricity, E is the EXOSIMS eccentric # anomaly, and M is the mean anomaly. est_mean_anomaly = exo_ecc_anomaly - ref_eccentricity * np.sin( exo_ecc_anomaly) self.assertAlmostEqual(ref_mean_anomaly, est_mean_anomaly, delta=1e-6)
def keplerplanet(self, currentTime, bodyname, eclip=False): """Finds solar system body positions vector in heliocentric equatorial (default) or ecliptic frame for current time (MJD). This method uses algorithms 2 and 10 from Vallado 2013 to find heliocentric equatorial position vectors for solar system objects. Args: currentTime (astropy Time array): Current absolute mission time in MJD bodyname (string): Solar system object name eclip (boolean): Boolean used to switch to heliocentric ecliptic frame. Defaults to False, corresponding to heliocentric equatorial frame. Returns: r_body (astropy Quantity nx3 array): Solar system body positions in heliocentric equatorial (default) or ecliptic frame in units of AU Note: Use eclip=True to get ecliptic coordinates. """ # Moon positions based on Earth positions if bodyname == 'Moon': r_Earth = self.keplerplanet(currentTime, 'Earth') return r_Earth + self.moon_earth(currentTime) assert self.planets.has_key(bodyname),\ "%s is not a recognized body name."%(bodyname) # find Julian centuries from J2000 TDB = self.cent(currentTime) # update ephemerides data (convert sma from km to AU) planet = self.planets[bodyname] a = (self.propeph(planet.a, TDB) * u.km).to('AU').value e = self.propeph(planet.e, TDB) I = np.radians(self.propeph(planet.I, TDB)) O = np.radians(self.propeph(planet.O, TDB)) w = np.radians(self.propeph(planet.w, TDB)) lM = np.radians(self.propeph(planet.lM, TDB)) # find mean anomaly and argument of perigee M = (lM - w) % (2 * np.pi) wp = (w - O) % (2 * np.pi) # find eccentric anomaly E = eccanom(M, e)[0] # find true anomaly nu = np.arctan2(np.sin(E) * np.sqrt(1 - e**2), np.cos(E) - e) # find semiparameter p = a * (1 - e**2) # body positions vector in orbital plane rx = p * np.cos(nu) / (1 + e * np.cos(nu)) ry = p * np.sin(nu) / (1 + e * np.cos(nu)) rz = np.zeros(currentTime.size) r_orb = np.array([rx, ry, rz]) # body positions vector in heliocentric ecliptic plane r_body = np.array([ np.dot(np.dot(self.rot(-O[x], 3), self.rot(-I[x], 1)), np.dot(self.rot(-wp[x], 3), r_orb[:, x])) for x in range(currentTime.size) ]) * u.AU if not eclip: # body positions vector in heliocentric equatorial frame r_body = self.eclip2equat(r_body, currentTime) return r_body
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)
Bmag = 5.66 #(mag) radius = 1.23 #r sun star_d = 13.802083302115193 #distance (pc) ±0.028708172014593 star_mass = 1.03 #0.05 #### Randomly Generate 47 UMa c planet parameters n = 10**5 inc, W, w = PPop.gen_angles(n, None) inc = inc.to('rad').value inc[np.where(inc > np.pi / 2)[0]] = np.pi - inc[np.where(inc > np.pi / 2)[0]] W = W.to('rad').value w = w.to('rad').value a, e, p, Rp = PPop.gen_plan_params(n) a = a.to('AU').value M0 = rand.uniform(low=0., high=2 * np.pi, size=n) #rand.random(360, size=n) E = eccanom(M0, e) # eccentric anomaly a = rand.uniform(low=3.5, high=3.7, size=n) * u.AU # (3.7-3.5)*rand.random(n)+3.5 #uniform random e = rand.uniform(low=0.002, high=0.145, size=n) #(0.145-0.002)*rand.random(n)+0.02 #uniform random Msini = rand.uniform(low=0.467, high=0.606, size=n) #(0.606-0.467)*rand.random(n)+0.467 Mp = (Msini / np.sin(inc) * u.M_jup).to('M_earth') #TODO CHECK FOR INF/TOO LARGE print('Done Generating planets 1') Rp = PPM.calc_radius_from_mass(Mp) indsTooBig = np.where( Rp < 12 * u.earthRad)[0] #throws out planets with radius 12x larger than Earth
def keplerplanet(self, currentTime, bodyname): """Finds position vector for solar system objects This method uses algorithms 2 and 10 from Vallado 2013 to find heliocentric equatorial position vectors (astropy Quantity in km) for solar system objects. Args: currentTime (astropy Time): Current absolute mission time in MJD bodyname (string): Solar system object name Returns: r_body (astropy Quantity nx3 array): Heliocentric equatorial position vector in units of km """ # reshape currentTime currentTime = currentTime.reshape(currentTime.size) if bodyname == 'Moon': r_Earth = self.keplerplanet(currentTime, 'Earth') return r_Earth + self.moon_earth(currentTime) assert self.planets.has_key(bodyname),\ "%s is not a recognized body name."%(bodyname) planet = self.planets[bodyname] # find Julian centuries from J2000 TDB = self.cent(currentTime) # update ephemeride data a = self.propeph(planet.a, TDB) e = self.propeph(planet.e, TDB) I = np.radians(self.propeph(planet.I, TDB)) O = np.radians(self.propeph(planet.O, TDB)) w = np.radians(self.propeph(planet.w, TDB)) lM = np.radians(self.propeph(planet.lM, TDB)) # Find mean anomaly and argument of perigee M = np.mod(lM - w, 2 * np.pi) wp = np.mod(w - O, 2 * np.pi) # Find eccentric anomaly E = eccanom(M, e)[0] # Find true anomaly nu = np.arctan2(np.sin(E) * np.sqrt(1 - e**2), np.cos(E) - e) # Find semiparameter p = a * (1 - e**2) # position vector (km) in orbital plane rx = p * np.cos(nu) / (1 + e * np.cos(nu)) ry = p * np.sin(nu) / (1 + e * np.cos(nu)) rz = np.zeros(currentTime.size) r_body = np.vstack((rx, ry, rz)).T # position vector (km) in ecliptic plane r_body = np.array([np.dot(np.dot(self.rot(-O[x],3),self.rot(-I[x],1)),\ np.dot(self.rot(-wp[x],3),r_body[x,:])) for x in range(currentTime.size)]) # find obliquity of the ecliptic obe = np.radians(self.obe(TDB)) # position vector (km) in heliocentric equatorial frame r_body = np.array([np.dot(self.rot(-obe[x],1),r_body[x,:])\ for x in range(currentTime.size)])*u.km return r_body.to('km').reshape(currentTime.size, 3)