def transit_model(theta, t, exp_time=0.002): params = TransitParams() t0, p, k, r, b, q1, q2 = theta[:7] u1, u2 = q_to_u(q1, q2) a = arstar(p, r) i = inclination(a, b) params.t0 = t0 #time of inferior conjunction params.per = p #orbital period params.rp = k #planet radius (in units of stellar radii) params.a = a #semi-major axis (in units of stellar radii) params.inc = i #orbital inclination (in degrees) params.ecc = 0. #eccentricity params.w = 90. #longitude of periastron (in degrees) params.u = [u1, u2] #limb darkening coefficients params.limb_dark = "quadratic" m = TransitModel(params, t, supersample_factor=1, exp_time=exp_time) # m = TransitModel(params, t) return m.light_curve(params)
def light_curve(self, spot_lons, spot_lats, spot_radii, inc_stellar, planet=None, times=None, fast=False, time_ref=None, return_spots_occulted=False, transit_model_kwargs={}): """ Generate a(n ensemble of) light curve(s). Light curve output will have shape ``(n_phases, len(inc_stellar))`` or ``(len(times), len(inc_stellar))``. Parameters ---------- spot_lons : `~numpy.ndarray` Spot longitudes spot_lats : `~numpy.ndarray` Spot latitudes spot_radii : `~numpy.ndarray` Spot radii inc_stellar : `~numpy.ndarray` Stellar inclinations planet : `~batman.TransitParams`, optional Transiting planet parameters times : `~numpy.ndarray`, optional Times at which to compute the light curve fast : bool, optional When `True`, use approximation that spots are fixed on the star during a transit event. When `False`, account for motion of starspots on stellar surface due to rotation during transit event. Default is `False`. time_ref : float, optional Reference time used as the initial rotational phase of the star, such that the sub-observer point is at zero longitude at ``time_ref``. return_spots_occulted : bool, optional Return whether or not spots have been occulted. Returns ------- light_curves : `~numpy.ndarray` Stellar light curves of shape ``(n_phases, len(inc_stellar))`` or ``(len(times), len(inc_stellar))`` """ if time_ref is None: if times is not None: time_ref = 0 else: time_ref = self.phases[0] # Compute the spot positions in cartesian coordinates: tilted_spots = self.spherical_to_cartesian(spot_lons, spot_lats, inc_stellar, times=times, planet=planet, time_ref=time_ref) # Compute the distance of each spot from the stellar centroid, mask # any spots that are "behind" the star, in other words, x < 0 r = np.ma.masked_array(np.hypot(tilted_spots.y.value, tilted_spots.z.value), mask=tilted_spots.x.value < 0) ld = limb_darkening_normed(self.u_ld, r) # Compute the out-of-transit flux missing due to each spot f_spots = (np.pi * spot_radii**2 * (1 - self.spot_contrast) * ld * np.sqrt(1 - r**2)) if planet is None: # If there is no transiting planet, skip the transit routine: lambda_e = np.zeros((len(self.phases), 1)) else: if not inc_stellar.isscalar: raise ValueError('Transiting exoplanets are implemented for ' 'planets transiting single stars only, but ' '``inc_stellar`` has multiple values. ') # Compute a transit model from batman import TransitModel n_spots = len(spot_lons) m = TransitModel(planet, times, **transit_model_kwargs) lambda_e = 1 - m.light_curve(planet)[:, np.newaxis] # Compute the true anomaly of the planet at each time, f: f = m.get_true_anomaly() # Compute the position of the planet in cartesian coordinates using # Equations 53-55 of Murray & Correia (2010). Note that these # coordinates are different from the cartesian coordinates used for # the spot positions. In this system, the observer is at X-> -inf. I = np.radians(90 - planet.inc) Omega = np.radians(planet.w) # this is 90 deg by default omega = np.pi / 2 X = planet.a * (np.cos(Omega) * np.cos(omega + f) - np.sin(Omega) * np.sin(omega + f) * np.cos(I)) Y = planet.a * (np.sin(Omega) * np.cos(omega + f) + np.cos(Omega) * np.sin(omega + f) * np.cos(I)) Z = planet.a * np.sin(omega + f) * np.sin(I) # Create a shapely circle object for the planet's silhouette only # when the planet is in front of the star, otherwise append `None` planet_disk = [ circle([-Y[i], -Z[i]], planet.rp) if (np.abs(Y[i]) < 1 + planet.rp) and (X[i] < 0) else None for i in range(len(f)) ] if fast: spots_occulted = self._planet_spot_overlap_fast( planet, planet_disk, tilted_spots, spot_radii, n_spots, X, Y, lambda_e) else: spots_occulted = self._planet_spot_overlap_slow( planet, planet_disk, tilted_spots, spot_radii, n_spots, X, Y, lambda_e) # Return the flux missing from the star at each time due to spots # (f_spots/self.f0) and due to the transit (lambda_e): if return_spots_occulted: return (1 - np.sum(f_spots.filled(0) / self.f0, axis=1) - lambda_e, spots_occulted) else: return 1 - np.sum(f_spots.filled(0) / self.f0, axis=1) - lambda_e