def test_mpi(verbose=False, plot=False, npoints=8, turn_mpi_off_after=True): phoebe.reset_settings() phoebe.mpi_on(4) b = phoebe.Bundle.default_binary() b.add_dataset('lc', times=np.linspace(0, 1, npoints)) if verbose: print("calling compute") b.run_compute(irrad_method='none', ntriangles=1000, detach=True) if verbose: print("attaching to model") print(b['model'].status) b['model'].attach() if verbose: print("model received") if plot: b.plot(show=True) phoebe.reset_settings() if turn_mpi_off_after: # we need to turn this off for the next test in nosetests... but # if running from python or mpirun, we don't want to release all the # workers or they'll just pickup all the previous tasks phoebe.mpi_off() return b
def test_mpi(plot=False, npoints=8): phoebe.reset_settings() phoebe.mpi_on(4) b = phoebe.Bundle.default_binary() b.add_dataset('lc', times=np.linspace(0, 1, npoints)) if plot: print "calling compute" b.run_compute(irrad_method='none', model='phoebe2model') if plot: print "model received" if plot: b.plot(show=True) phoebe.reset_settings() phoebe.mpi_off() return b
def test_mpi(plot=False, npoints=8): phoebe.reset_settings() phoebe.mpi_on(4) b = phoebe.Bundle.default_binary() b.add_dataset('lc', times=np.linspace(0,1,npoints)) if plot: print "calling compute" b.run_compute(irrad_method='none', ntriangles=1000, detach=True) if plot: print "attaching to model" print b['model'].status b['model'].attach() if plot: print "model received" if plot: b.plot(show=True) phoebe.reset_settings() phoebe.mpi_off() return b
import phoebe phoebe.mpi_off() import numpy as np import multiprocessing as mp def compute_lc(values): (q, r1, r2, tratio, incl, ecc, per0) = values # b.set_value_all('atm', value='blackbody') # b.set_value_all('ld_mode', value='manual') b['q'] = q b['requiv@primary'] = r1 * b['value@sma@binary'] b['requiv@secondary'] = r2 * b['value@sma@binary'] b['teff@secondary'] = tratio * b['teff@primary'].value b['incl@binary'] = incl b['ecc'] = ecc b['per0'] = per0 try: b.run_compute() return b['value@fluxes@model'] except: return np.zeros(len(phases)) phases = np.linspace(-0.5, 0.5, 100) b = phoebe.default_binary() b.add_dataset('lc', compute_phases=phases)
def binary_star_mesh(star1_params, star2_params, binary_params, observation_times, use_blackbody_atm=False, make_mesh_plots=True, mesh_temp=False, mesh_temp_cmap=None, plot_name=None, print_diagnostics=False, par_compute=False, num_par_processes=8, num_triangles=1500): """Compute the light curve for a binary system Keyword arguments: star1_params -- Tuple of parameters for the primary star star2_params -- Tuple of parameters for the secondary star binary_params -- Tuple of parameters for the binary system configuration observation_times -- Tuple of observation times, with numpy array of MJDs in each band (kp_MJDs, h_MJDs, mesh_MJDs) = observation_times use_blackbody_atm -- Use blackbody atmosphere instead of default Castelli & Kurucz (default False) make_mesh_plots -- Make a mesh plot of the binary system (default False) plot_name print_diagnostics par_compute num_par_processes """ if par_compute: # TODO: Need to implement parallelization correctly phoebe.mpi_on(nprocs=num_par_processes) else: phoebe.mpi_off() # Read in the stellar parameters of the binary components (star1_mass, star1_rad, star1_teff, star1_logg, star1_mag_Kp, star1_mag_H, star1_pblum_Kp, star1_pblum_H) = star1_params (star2_mass, star2_rad, star2_teff, star2_logg, star2_mag_Kp, star2_mag_H, star2_pblum_Kp, star2_pblum_H) = star2_params # Read in the parameters of the binary system (binary_period, binary_ecc, binary_inc, t0) = binary_params err_out = (np.array([-1.]), np.array([-1.])) # Check for high temp ck2004 atmosphere limits if not use_blackbody_atm: # log g = 3.5, high temp bounds (above 31e3 K) if star1_teff > (31000 * u.K) and (4.0 > star1_logg > 3.5): star1_teff_round = 30995.0 * u.K if print_diagnostics: print('Star 1 out of C&K 2004 grid') print('star1_logg = {0:.4f}'.format(star1_logg)) print('Rounding down star1_teff') print('{0:.4f} -> {1:.4f}'.format(star1_teff, star1_teff_round)) star1_teff = star1_teff_round if star2_teff > (31000 * u.K) and (4.0 > star2_logg > 3.5): star2_teff_round = 30995.0 * u.K if print_diagnostics: print('Star 2 out of C&K 2004 grid') print('star2_logg = {0:.4f}'.format(star2_logg)) print('Rounding down star2_teff') print('{0:.4f} -> {1:.4f}'.format(star2_teff, star2_teff_round)) star2_teff = star2_teff_round # log g = 4.0, high temp bounds (above 40e3 K) if star1_teff > (40000 * u.K) and (4.5 > star1_logg > 4.0): star1_teff_round = 39995.0 * u.K print('Star 1 out of C&K 2004 grid') print('star1_logg = {0:.4f}'.format(star1_logg)) print('Rounding down star1_teff') print('{0:.4f} -> {1:.4f}'.format(star1_teff, star1_teff_round)) star1_teff = star1_teff_round if star2_teff > (40000 * u.K) and (4.0 > star2_logg > 3.50): star2_teff_round = 39995.0 * u.K print('Star 2 out of C&K 2004 grid') print('star2_logg = {0:.4f}'.format(star2_logg)) print('Rounding down star2_teff') print('{0:.4f} -> {1:.4f}'.format(star2_teff, star2_teff_round)) star2_teff = star2_teff_round # Set up binary model b = phoebe.default_binary() ## Set a default distance b.set_value('distance', 10 * u.pc) ## Set period, semimajor axis, and mass ratio (q) binary_sma = ((binary_period**2. * const.G * (star1_mass + star2_mass)) / (4. * np.pi**2.))**(1. / 3.) binary_q = star2_mass / star1_mass if print_diagnostics: print('\nBinary orbit checks') print('Binary SMA: {0}'.format(binary_sma.to(u.AU))) print('Binary Mass Ratio (q): {0}'.format(binary_q)) b.set_value('period@orbit', binary_period) b.set_value('sma@binary@component', binary_sma) b.set_value('q@binary@component', binary_q) ## Inclination b.set_value('incl@orbit', binary_inc) # Check for overflow ## Variables to help store the non-detached binary cases star1_semidetached = False star2_semidetached = False star1_overflow = False star2_overflow = False ## Get the max radii for both component stars star1_rad_max = b.get_value('requiv_max@primary@component') * u.solRad star2_rad_max = b.get_value('requiv_max@secondary@component') * u.solRad ## Check for semidetached cases if print_diagnostics: print('\nSemidetached checks') print('Star 1: {0}'.format( np.abs((star1_rad - star1_rad_max) / star1_rad_max))) print('Star 2: {0}'.format( np.abs((star2_rad - star2_rad_max) / star2_rad_max))) semidet_cut = 0.001 # (within 0.1% of max radii) semidet_cut = 0.015 # (within 1.5% of max radii) if np.abs((star1_rad - star1_rad_max) / star1_rad_max) < semidet_cut: star1_semidetached = True if np.abs((star2_rad - star2_rad_max) / star2_rad_max) < semidet_cut: star2_semidetached = True ## Check for overflow if (star1_rad > star1_rad_max) and not star1_semidetached: star1_overflow = True if (star2_rad > star2_rad_max) and not star2_semidetached: star2_overflow = True ### Check for if both stars are overflowing; which star overflows more? ### Choose that star to be overflowing more if star1_overflow and star2_overflow: if (star1_rad - star1_rad_max) >= (star2_rad - star2_rad_max): star2_overflow = False else: star1_overflow = False if print_diagnostics: print('\nOverflow Checks') print('Star 1 Semidetached: {0}'.format(star1_semidetached)) print('Star 2 Semidetached: {0}'.format(star2_semidetached)) print('Star 1 Overflow: {0}'.format(star1_overflow)) print('Star 2 Overflow: {0}'.format(star2_overflow)) # If star 2 is overflowing, have to re set up model: # Calling this same binary_star_lc function again, # with star 2 as primary and star 1 as secondary. # Change t0 = t0 - per/2 to make sure phase is correct, # wrt stars 1 and 2 being in same respective position if star2_overflow and not star1_overflow: redo_binary_params = (binary_period, binary_ecc, binary_inc, t0 - (binary_period.to(u.d).value / 2.)) return binary_star_lc(star2_params, star1_params, redo_binary_params, observation_times, use_blackbody_atm=use_blackbody_atm, make_mesh_plots=make_mesh_plots, mesh_temp=mesh_temp, mesh_temp_cmap=mesh_temp_cmap, plot_name=plot_name, print_diagnostics=print_diagnostics, par_compute=par_compute, num_par_processes=num_par_processes, num_triangles=num_triangles) ## If none of these overflow cases, set variable to store if binary is detached binary_detached = (not star1_semidetached) and \ (not star2_semidetached) and \ (not star1_overflow) and \ (not star2_overflow) ### Set non-zero eccentricity only if binary is detached if binary_detached: b.set_value('ecc@binary@component', binary_ecc) else: b.set_value('ecc@binary@component', 0.) ## Change set up for contact or semidetached cases if star1_overflow or star2_overflow: b = phoebe.default_binary(contact_binary=True) ### Reset all necessary binary properties for contact system b.set_value('distance', 10 * u.pc) b.set_value('period@orbit', binary_period) b.set_value('sma@binary@component', binary_sma) b.set_value('q@binary@component', binary_q) b.set_value('incl@orbit', binary_inc) if star1_semidetached and not star2_overflow: b.add_constraint('semidetached', 'primary') if star2_semidetached and not star1_overflow: b.add_constraint('semidetached', 'secondary') # Set up compute if use_blackbody_atm: b.add_compute('phoebe', compute='detailed', irrad_method='wilson', atm='blackbody') else: b.add_compute('phoebe', compute='detailed', irrad_method='wilson') # Set the parameters of the component stars of the system ## Primary b.set_value('teff@primary@component', star1_teff) # b.set_value('logg@primary@component', star1_logg) if (not star1_semidetached) and (not star2_overflow): b.set_value('requiv@primary@component', star1_rad) ## Secondary b.set_value('teff@secondary@component', star2_teff) # b.set_value('logg@secondary@component', star2_logg) if (not star2_semidetached) and (not star1_overflow) and ( not star2_overflow): try: b.set_value('requiv@secondary@component', star2_rad) except: print('\nOverflow Checks') print('Star 1 Semidetached: {0}'.format(star1_semidetached)) print('Star 2 Semidetached: {0}'.format(star2_semidetached)) print('Star 1 Overflow: {0}'.format(star1_overflow)) print('Star 2 Overflow: {0}'.format(star2_overflow)) print("Cannot set secondary radius: {0}".format(sys.exc_info()[0])) return err_out # Set the number of triangles in the mesh # Detached stars if len(b.filter('ntriangles@primary@detailed@compute')) == 1: b.set_value('ntriangles@primary@detailed@compute', num_triangles) if len(b.filter('ntriangles@secondary@detailed@compute')) == 1: b.set_value('ntriangles@secondary@detailed@compute', num_triangles) # Contact envelope if len(b.filter('ntriangles@contact_envelope@detailed@compute')) == 1: if print_diagnostics: print('Setting number of triangles for contact envelope') b.set_value('ntriangles@contact_envelope@detailed@compute', num_triangles * 2.) # Phase the observation times ## Read in observation times (kp_MJDs, h_MJDs, mesh_MJDs) = observation_times ## Phase the observation times kp_phased_days = ( (kp_MJDs - t0) % binary_period.to(u.d).value) / binary_period.to( u.d).value h_phased_days = ((h_MJDs - t0) % binary_period.to(u.d).value) / binary_period.to(u.d).value mesh_phased_days = ( (mesh_MJDs - t0) % binary_period.to(u.d).value) / binary_period.to( u.d).value # Add light curve datasets ## Kp kp_phases_sorted_inds = np.argsort(kp_phased_days) kp_model_times = (kp_phased_days) * binary_period.to(u.d).value kp_model_times = kp_model_times[kp_phases_sorted_inds] # b.add_dataset(phoebe.dataset.lc, time=kp_model_times, # dataset='mod_lc_Kp', passband='Keck_NIRC2:Kp') if use_blackbody_atm: b.add_dataset(phoebe.dataset.lc, time=kp_model_times, dataset='mod_lc_Kp', passband='Keck_NIRC2:Kp') b.set_value('ld_mode@primary@mod_lc_Kp', 'manual') b.set_value('ld_mode@secondary@mod_lc_Kp', 'manual') b.set_value('ld_func@primary@mod_lc_Kp', 'logarithmic') b.set_value('ld_func@secondary@mod_lc_Kp', 'logarithmic') else: b.add_dataset(phoebe.dataset.lc, time=kp_model_times, dataset='mod_lc_Kp', passband='Keck_NIRC2:Kp') ## H h_phases_sorted_inds = np.argsort(h_phased_days) h_model_times = (h_phased_days) * binary_period.to(u.d).value h_model_times = h_model_times[h_phases_sorted_inds] # b.add_dataset(phoebe.dataset.lc, times=h_model_times, # dataset='mod_lc_H', passband='Keck_NIRC2:H') if use_blackbody_atm: b.add_dataset(phoebe.dataset.lc, times=h_model_times, dataset='mod_lc_H', passband='Keck_NIRC2:H') b.set_value('ld_mode@primary@mod_lc_H', 'manual') b.set_value('ld_mode@secondary@mod_lc_H', 'manual') b.set_value('ld_func@primary@mod_lc_H', 'logarithmic') b.set_value('ld_func@secondary@mod_lc_H', 'logarithmic') else: b.add_dataset(phoebe.dataset.lc, times=h_model_times, dataset='mod_lc_H', passband='Keck_NIRC2:H') # Add mesh dataset if making mesh plot mesh_phases_sorted_inds = np.argsort(mesh_phased_days) mesh_model_times = (mesh_phased_days) * binary_period.to(u.d).value mesh_model_times = mesh_model_times[mesh_phases_sorted_inds] if make_mesh_plots: b.add_dataset('mesh', times=mesh_model_times, dataset='mod_mesh') if mesh_temp: b['columns@mesh'] = ['teffs', 'loggs'] # Set the passband luminosities for the stars b.set_value('pblum_mode@mod_lc_Kp', 'decoupled') b.set_value('pblum_mode@mod_lc_H', 'decoupled') b.set_value('pblum@primary@mod_lc_Kp', star1_pblum_Kp) b.set_value('pblum@primary@mod_lc_H', star1_pblum_H) b.set_value('pblum@secondary@mod_lc_Kp', star2_pblum_Kp) b.set_value('pblum@secondary@mod_lc_H', star2_pblum_H) # Run compute # if print_diagnostics: # print("Trying inital compute run") # b.run_compute(compute='detailed', model='run', # progressbar=False) try: b.run_compute(compute='detailed', model='run', progressbar=False) except: if print_diagnostics: print("Error during primary binary compute: {0}".format( sys.exc_info()[0])) return err_out # Save out mesh plot if make_mesh_plots: ## Plot Nerdery plt.rc('font', family='serif') plt.rc('font', serif='Computer Modern Roman') plt.rc('text', usetex=True) plt.rc('text.latex', preamble=r"\usepackage{gensymb}") plt.rc('xtick', direction='in') plt.rc('ytick', direction='in') # plt.rc('xtick', top = True) # plt.rc('ytick', right = True) suffix_str = '' if plot_name is not None: suffix_str = '_' + plot_name ## Mesh plot mesh_plot_out = [] if mesh_temp: b['mod_mesh@model'].plot( fc='teffs', fcmap=mesh_temp_cmap, ec='face', animate=True, save='./binary_mesh{0}.gif'.format(suffix_str), save_kwargs={'writer': 'imagemagick'}) for (mesh_model_time, mesh_index) in zip(mesh_model_times, range(len(mesh_model_times))): plt.clf() new_fig = plt.figure() (mesh_af_fig, mesh_plt_fig) = b['mod_mesh@model'].plot( fig=new_fig, time=mesh_model_time, fc='teffs', fcmap=mesh_temp_cmap, ec='face', save='./binary_mesh_{0}.pdf'.format(mesh_index)) mesh_plot_out.append(mesh_plt_fig) plt.close(new_fig) else: b['mod_mesh@model'].plot( animate=True, save='./binary_mesh{0}.gif'.format(suffix_str), save_kwargs={'writer': 'imagemagick'}) for mesh_model_time in mesh_model_times: mesh_plot_out.append(b['mod_mesh@model'].plot( time=mesh_model_time, save='./binary_mesh.pdf'.format(suffix_str))) # Get fluxes ## Kp model_fluxes_Kp = np.array( b['fluxes@lc@mod_lc_Kp@model'].value) * u.W / (u.m**2.) model_mags_Kp = -2.5 * np.log10(model_fluxes_Kp / flux_ref_Kp) + 0.03 ## H model_fluxes_H = np.array( b['fluxes@lc@mod_lc_H@model'].value) * u.W / (u.m**2.) model_mags_H = -2.5 * np.log10(model_fluxes_H / flux_ref_H) + 0.03 if print_diagnostics: print('\nFlux Checks') print('Fluxes, Kp: {0}'.format(model_fluxes_Kp)) print('Mags, Kp: {0}'.format(model_mags_Kp)) print('Fluxes, H: {0}'.format(model_fluxes_H)) print('Mags, H: {0}'.format(model_mags_H)) if make_mesh_plots: return (model_mags_Kp, model_mags_H, mesh_plot_out) else: return (model_mags_Kp, model_mags_H)
def binary_star_lc(star1_params, star2_params, binary_params, observation_times, use_blackbody_atm=False, use_compact_object=False, make_mesh_plots=False, mesh_temp=False, mesh_temp_cmap=None, plot_name=None, print_diagnostics=False, par_compute=False, num_par_processes=8, num_triangles=1500): """Compute the light curve for a binary system Keyword arguments: star1_params -- Tuple of parameters for the primary star star2_params -- Tuple of parameters for the secondary star binary_params -- Tuple of parameters for the binary system configuration observation_times -- Tuple of observation times, with numpy array of MJDs in each band and for the RVs (kp_MJDs, h_MJDs, rv_MJDs) = observation_times use_blackbody_atm -- Use blackbody atmosphere instead of default Castelli & Kurucz (default False) use_compact_object -- Set eclipse_method to 'only_horizon', necessary for compact companions without eclipses (default False) make_mesh_plots -- Make a mesh plot of the binary system (default False) plot_name print_diagnostics par_compute num_par_processes """ if par_compute: # TODO: Need to implement parallelization correctly phoebe.mpi_on(nprocs=num_par_processes) else: phoebe.mpi_off() # Read in the stellar parameters of the binary components (star1_mass, star1_rad, star1_teff, star1_logg, [star1_mag_Kp, star1_mag_H], [star1_pblum_Kp, star1_pblum_H]) = star1_params (star2_mass, star2_rad, star2_teff, star2_logg, [star2_mag_Kp, star2_mag_H], [star2_pblum_Kp, star2_pblum_H]) = star2_params # Read in the parameters of the binary system (binary_period, binary_ecc, binary_inc, t0) = binary_params err_out = (np.array([-1.]), np.array([-1.]), np.array([-1.]), np.array([-1.])) if make_mesh_plots: err_out = (np.array([-1.]), np.array([-1.]), np.array([-1.]), np.array([-1.]), np.array([-1.])) # Check for high temp ck2004 atmosphere limits if not use_blackbody_atm: # log g = 3.5, high temp bounds (above 31e3 K) if star1_teff > (31000 * u.K) and (4.0 > star1_logg > 3.5): star1_teff_round = 30995.0 * u.K if print_diagnostics: print('Star 1 out of C&K 2004 grid') print('star1_logg = {0:.4f}'.format(star1_logg)) print('Rounding down star1_teff') print('{0:.4f} -> {1:.4f}'.format(star1_teff, star1_teff_round)) star1_teff = star1_teff_round if star2_teff > (31000 * u.K) and (4.0 > star2_logg > 3.5): star2_teff_round = 30995.0 * u.K if print_diagnostics: print('Star 2 out of C&K 2004 grid') print('star2_logg = {0:.4f}'.format(star2_logg)) print('Rounding down star2_teff') print('{0:.4f} -> {1:.4f}'.format(star2_teff, star2_teff_round)) star2_teff = star2_teff_round # log g = 4.0, high temp bounds (above 40e3 K) if star1_teff > (40000 * u.K) and (4.5 > star1_logg > 4.0): star1_teff_round = 39995.0 * u.K print('Star 1 out of C&K 2004 grid') print('star1_logg = {0:.4f}'.format(star1_logg)) print('Rounding down star1_teff') print('{0:.4f} -> {1:.4f}'.format(star1_teff, star1_teff_round)) star1_teff = star1_teff_round if star2_teff > (40000 * u.K) and (4.0 > star2_logg > 3.50): star2_teff_round = 39995.0 * u.K print('Star 2 out of C&K 2004 grid') print('star2_logg = {0:.4f}'.format(star2_logg)) print('Rounding down star2_teff') print('{0:.4f} -> {1:.4f}'.format(star2_teff, star2_teff_round)) star2_teff = star2_teff_round # Set up binary model b = phoebe.default_binary() ## Set a default distance b.set_value('distance', 10 * u.pc) ## Set period, semimajor axis, and mass ratio (q) binary_sma = ((binary_period**2. * const.G * (star1_mass + star2_mass)) / (4. * np.pi**2.))**(1./3.) binary_q = star2_mass / star1_mass if print_diagnostics: print('\nBinary orbit checks') print('Binary SMA: {0}'.format(binary_sma.to(u.AU))) print('Binary Mass Ratio (q): {0}'.format(binary_q)) b.set_value('period@orbit', binary_period) b.set_value('sma@binary@component', binary_sma) b.set_value('q@binary@component', binary_q) ## Inclination b.set_value('incl@orbit', binary_inc) # Check for overflow ## Variables to help store the non-detached binary cases star1_semidetached = False star2_semidetached = False star1_overflow = False star2_overflow = False ## Get the max radii for both component stars star1_rad_max = b.get_value('requiv_max@primary@component') * u.solRad star2_rad_max = b.get_value('requiv_max@secondary@component') * u.solRad ## Check for semidetached cases if print_diagnostics: print('\nSemidetached checks') print('Star 1: {0}'.format(np.abs((star1_rad - star1_rad_max) / star1_rad_max))) print('Star 2: {0}'.format(np.abs((star2_rad - star2_rad_max) / star2_rad_max))) semidet_cut = 0.001 # (within 0.1% of max radii) semidet_cut = 0.015 # (within 1.5% of max radii) if np.abs((star1_rad - star1_rad_max) / star1_rad_max) < semidet_cut: star1_semidetached = True if np.abs((star2_rad - star2_rad_max) / star2_rad_max) < semidet_cut: star2_semidetached = True ## Check for overflow if (star1_rad > star1_rad_max) and not star1_semidetached: star1_overflow = True if (star2_rad > star2_rad_max) and not star2_semidetached: star2_overflow = True ### Check for if both stars are overflowing; which star overflows more? ### Choose that star to be overflowing more if star1_overflow and star2_overflow: if (star1_rad - star1_rad_max) >= (star2_rad - star2_rad_max): star2_overflow = False else: star1_overflow = False if print_diagnostics: print('\nOverflow Checks') print('Star 1 Semidetached: {0}'.format(star1_semidetached)) print('Star 2 Semidetached: {0}'.format(star2_semidetached)) print('Star 1 Overflow: {0}'.format(star1_overflow)) print('Star 2 Overflow: {0}'.format(star2_overflow)) # If star 2 is overflowing, have to re set up model: # Calling this same binary_star_lc function again, # with star 2 as primary and star 1 as secondary. # Change t0 = t0 - per/2 to make sure phase is correct, # wrt stars 1 and 2 being in same respective position if star2_overflow and not star1_overflow: redo_binary_params = (binary_period, binary_ecc, binary_inc, t0 - (binary_period.to(u.d).value/2.)) return binary_star_lc(star2_params, star1_params, redo_binary_params, observation_times, use_blackbody_atm=use_blackbody_atm, make_mesh_plots=make_mesh_plots, mesh_temp=mesh_temp, mesh_temp_cmap=mesh_temp_cmap, plot_name=plot_name, print_diagnostics=print_diagnostics, par_compute=par_compute, num_par_processes=num_par_processes, num_triangles=num_triangles) ## If none of these overflow cases, set variable to store if binary is detached binary_detached = (not star1_semidetached) and \ (not star2_semidetached) and \ (not star1_overflow) and \ (not star2_overflow) ### Set non-zero eccentricity only if binary is detached if binary_detached: b.set_value('ecc@binary@component', binary_ecc) else: b.set_value('ecc@binary@component', 0.) ## Change set up for contact or semidetached cases if star1_overflow or star2_overflow: b = phoebe.default_binary(contact_binary=True) ### Reset all necessary binary properties for contact system b.set_value('distance', 10 * u.pc) b.set_value('period@orbit', binary_period) b.set_value('sma@binary@component', binary_sma) b.set_value('q@binary@component', binary_q) b.set_value('incl@orbit', binary_inc) if star1_semidetached and not star2_overflow: b.add_constraint('semidetached', 'primary') if star2_semidetached and not star1_overflow: b.add_constraint('semidetached', 'secondary') # Set up compute if use_blackbody_atm: b.add_compute('phoebe', compute='detailed', irrad_method='wilson', atm='blackbody') b.set_value('atm@primary@detailed', 'blackbody') b.set_value('atm@secondary@detailed', 'blackbody') else: b.add_compute('phoebe', compute='detailed', irrad_method='wilson') # Set the parameters of the component stars of the system ## Primary b.set_value('teff@primary@component', star1_teff) # b.set_value('logg@primary@component', star1_logg) if (not star1_semidetached) and (not star2_overflow): b.set_value('requiv@primary@component', star1_rad) ## Secondary b.set_value('teff@secondary@component', star2_teff) # b.set_value('logg@secondary@component', star2_logg) if (not star2_semidetached) and (not star1_overflow) and (not star2_overflow): try: b.set_value('requiv@secondary@component', star2_rad) except: print('\nOverflow Checks') print('Star 1 Semidetached: {0}'.format(star1_semidetached)) print('Star 2 Semidetached: {0}'.format(star2_semidetached)) print('Star 1 Overflow: {0}'.format(star1_overflow)) print('Star 2 Overflow: {0}'.format(star2_overflow)) print("Cannot set secondary radius: {0}".format(sys.exc_info()[0])) return err_out # Set the number of triangles in the mesh # Detached stars if len(b.filter('ntriangles@primary@detailed@compute')) == 1: b.set_value('ntriangles@primary@detailed@compute', num_triangles) if len(b.filter('ntriangles@secondary@detailed@compute')) == 1: b.set_value('ntriangles@secondary@detailed@compute', num_triangles) # Contact envelope if len(b.filter('ntriangles@contact_envelope@detailed@compute')) == 1: if print_diagnostics: print('Setting number of triangles for contact envelope') b.set_value('ntriangles@contact_envelope@detailed@compute', num_triangles * 2.) # Phase the observation times ## Read in observation times (kp_MJDs, h_MJDs, rv_MJDs) = observation_times ## Phase the observation times kp_phased_days = ((kp_MJDs - t0) % binary_period.to(u.d).value) / binary_period.to(u.d).value h_phased_days = ((h_MJDs - t0) % binary_period.to(u.d).value) / binary_period.to(u.d).value rv_phased_days = ((rv_MJDs - t0) % binary_period.to(u.d).value) / binary_period.to(u.d).value # Add light curve datasets if use_blackbody_atm: b.set_value_all('ld_mode_bol', 'manual') b.set_value_all('ld_func_bol', 'linear') b.set_value_all('ld_coeffs_bol', [0.0]) # Check for compact companion if use_compact_object: b.set_value('irrad_method@detailed', 'none') ## Kp kp_phases_sorted_inds = np.argsort(kp_phased_days) kp_model_times = (kp_phased_days) * binary_period.to(u.d).value kp_model_times = kp_model_times[kp_phases_sorted_inds] if use_blackbody_atm: b.add_dataset(phoebe.dataset.lc, time=kp_model_times, dataset='mod_lc_Kp', passband='Keck_NIRC2:Kp') b.set_value_all('ld_mode@mod_lc_Kp', 'manual') b.set_value_all('ld_func@mod_lc_Kp', 'linear') b.set_value_all('ld_coeffs@mod_lc_Kp', [0.0]) else: b.add_dataset(phoebe.dataset.lc, time=kp_model_times, dataset='mod_lc_Kp', passband='Keck_NIRC2:Kp') ## H h_phases_sorted_inds = np.argsort(h_phased_days) h_model_times = (h_phased_days) * binary_period.to(u.d).value h_model_times = h_model_times[h_phases_sorted_inds] if use_blackbody_atm: b.add_dataset(phoebe.dataset.lc, times=h_model_times, dataset='mod_lc_H', passband='Keck_NIRC2:H') b.set_value_all('ld_mode@mod_lc_H', 'manual') b.set_value_all('ld_func@mod_lc_H', 'linear') b.set_value_all('ld_coeffs@mod_lc_H', [0.0]) else: b.add_dataset(phoebe.dataset.lc, times=h_model_times, dataset='mod_lc_H', passband='Keck_NIRC2:H') ## RV rv_phases_sorted_inds = np.argsort(rv_phased_days) rv_model_times = (rv_phased_days) * binary_period.to(u.d).value rv_model_times = rv_model_times[rv_phases_sorted_inds] if use_blackbody_atm: b.add_dataset(phoebe.dataset.rv, time=rv_model_times, dataset='mod_rv', passband='Keck_NIRC2:Kp') b.set_value_all('ld_mode@mod_rv', 'manual') b.set_value_all('ld_func@mod_rv', 'linear') b.set_value_all('ld_coeffs@mod_rv', [0.0]) else: b.add_dataset(phoebe.dataset.rv, time=rv_model_times, dataset='mod_rv', passband='Keck_NIRC2:Kp') # Add mesh dataset if making mesh plot if make_mesh_plots: b.add_dataset('mesh', times=[(binary_period/4.).to(u.d).value], dataset='mod_mesh') if mesh_temp: b['columns@mesh'] = ['teffs', 'loggs', 'areas', '*@mod_lc_Kp', '*@mod_lc_H'] # Set the passband luminosities for the stars b.set_value('pblum_mode@mod_lc_Kp', 'decoupled') b.set_value('pblum_mode@mod_lc_H', 'decoupled') b.set_value('pblum@primary@mod_lc_Kp', star1_pblum_Kp) b.set_value('pblum@primary@mod_lc_H', star1_pblum_H) b.set_value('pblum@secondary@mod_lc_Kp', star2_pblum_Kp) b.set_value('pblum@secondary@mod_lc_H', star2_pblum_H) # Run compute # Determine eclipse method if use_compact_object: eclipse_method = 'only_horizon' else: eclipse_method = 'native' if print_diagnostics: print("Trying inital compute run") b.run_compute(compute='detailed', model='run', progressbar=False, eclipse_method=eclipse_method) else: try: b.run_compute(compute='detailed', model='run', progressbar=False, eclipse_method=eclipse_method) except: if print_diagnostics: print("Error during primary binary compute: {0}".format(sys.exc_info()[0])) return err_out # Save out mesh plot if make_mesh_plots: ## Plot Nerdery plt.rc('font', family='serif') plt.rc('font', serif='Computer Modern Roman') plt.rc('text', usetex=True) plt.rc('text.latex', preamble=r"\usepackage{gensymb}") plt.rc('xtick', direction = 'in') plt.rc('ytick', direction = 'in') # plt.rc('xtick', top = True) # plt.rc('ytick', right = True) suffix_str = '' if plot_name is not None: suffix_str = '_' + plot_name ## Mesh plot if mesh_temp: mesh_plot_out = b['mod_mesh@model'].plot(save='./binary_mesh{0}.pdf'.format(suffix_str), fc='teffs', fcmap=mesh_temp_cmap, ec='none') # print(mesh_plot_out.axs) # Extract and output mesh quantities mesh_quant_names = ['teffs', 'loggs', 'areas', 'abs_intensities'] mesh_quant_do_filt = [False, False, False, True] mesh_quant_units = [u.K, 1.0, u.solRad**2, u.W / (u.m**3)] mesh_quant_filts = ['mod_lc_Kp', 'mod_lc_H'] mesh_quants_pri = {} mesh_quants_sec = {} for (quant, do_filt, quant_unit) in zip(mesh_quant_names, mesh_quant_do_filt, mesh_quant_units): if do_filt: for filt in mesh_quant_filts: quant_pri = b['{0}@primary@{1}'.format(quant, filt)].value *\ quant_unit quant_sec = b['{0}@secondary@{1}'.format(quant, filt)].value *\ quant_unit mesh_quants_pri['{0}_{1}'.format(quant, filt)] = quant_pri mesh_quants_sec['{0}_{1}'.format(quant, filt)] = quant_sec else: quant_pri = b['{0}@primary'.format(quant)].value * quant_unit quant_sec = b['{0}@secondary'.format(quant)].value * quant_unit mesh_quants_pri[quant] = quant_pri mesh_quants_sec[quant] = quant_sec # Construct mesh tables for each star and output mesh_pri_table = Table(mesh_quants_pri) mesh_pri_table.sort(['teffs'], reverse=True) with open('mesh_pri.txt', 'w') as out_file: for line in mesh_pri_table.pformat_all(): out_file.write(line + '\n') mesh_pri_table.write('mesh_pri.h5', format='hdf5', path='data', serialize_meta=True, overwrite=True) mesh_pri_table.write('mesh_pri.fits', format='fits', overwrite=True) mesh_sec_table = Table(mesh_quants_sec) mesh_sec_table.sort(['teffs'], reverse=True) with open('mesh_sec.txt', 'w') as out_file: for line in mesh_sec_table.pformat_all(): out_file.write(line + '\n') mesh_sec_table.write('mesh_sec.h5', format='hdf5', path='data', serialize_meta=True, overwrite=True) mesh_sec_table.write('mesh_sec.fits', format='fits', overwrite=True) else: mesh_plot_out = b['mod_mesh@model'].plot(save='./binary_mesh{0}.pdf'.format(suffix_str)) # Get fluxes ## Kp model_fluxes_Kp = np.array(b['fluxes@lc@mod_lc_Kp@model'].value) * u.W / (u.m**2.) model_mags_Kp = -2.5 * np.log10(model_fluxes_Kp / flux_ref_Kp) + 0.03 ## H model_fluxes_H = np.array(b['fluxes@lc@mod_lc_H@model'].value) * u.W / (u.m**2.) model_mags_H = -2.5 * np.log10(model_fluxes_H / flux_ref_H) + 0.03 # Get RVs model_RVs_pri = np.array(b['rvs@primary@run@rv@model'].value) * u.km / u.s model_RVs_sec = np.array(b['rvs@secondary@run@rv@model'].value) * u.km / u.s if print_diagnostics: print("\nFlux Checks") print("Fluxes, Kp: {0}".format(model_fluxes_Kp)) print("Mags, Kp: {0}".format(model_mags_Kp)) print("Fluxes, H: {0}".format(model_fluxes_H)) print("Mags, H: {0}".format(model_mags_H)) print("\nRV Checks") print("RVs, Primary: {0}".format(model_RVs_pri)) print("RVs, Secondary: {0}".format(model_RVs_sec)) if make_mesh_plots: return (model_mags_Kp, model_mags_H, model_RVs_pri, model_RVs_sec, mesh_plot_out) else: return (model_mags_Kp, model_mags_H, model_RVs_pri, model_RVs_sec)
def binary_star_lc(star1_params, star2_params, binary_params, observation_times, use_blackbody_atm=False, make_mesh_plots=False, plot_name=None, print_diagnostics=False, par_compute=False, num_par_processes=8, num_triangles=1500): """Compute the light curve for a binary system Keyword arguments: star1_params -- Tuple of parameters for the primary star star2_params -- Tuple of parameters for the secondary star binary_params -- Tuple of parameters for the binary system configuration observation_times -- Tuple of observation times, with numpy array of MJDs in each band (kp_MJDs, h_MJDs) = observation_times use_blackbody_atm -- Use blackbody atmosphere instead of default Castelli & Kurucz (default False) make_mesh_plots -- Make a mesh plot of the binary system (default False) plot_name print_diagnostics par_compute num_par_processes """ if par_compute: # TODO: Need to implement parallelization correctly phoebe.mpi_on(nprocs=num_par_processes) else: phoebe.mpi_off() # Read in the stellar parameters of the binary components (star1_mass, star1_rad, star1_teff, star1_mag_Kp, star1_mag_H) = star1_params (star2_mass, star2_rad, star2_teff, star2_mag_Kp, star2_mag_H) = star2_params # Read in the parameters of the binary system (binary_period, binary_ecc, binary_inc, t0) = binary_params err_out = (np.array([-1.]), np.array([-1.])) # Set up binary model b = phoebe.default_binary() ## Set period, semimajor axis, and mass ratio (q) binary_sma = ((binary_period**2. * const.G * (star1_mass + star2_mass)) / (4. * np.pi**2.))**(1. / 3.) binary_q = star2_mass / star1_mass b.set_value('period@orbit', binary_period) b.set_value('sma@binary@component', binary_sma) b.set_value('q@binary@component', binary_q) ## Inclination b.set_value('incl@orbit', binary_inc) # Check for overflow ## Variables to help store the non-detached binary cases star1_semidetached = False star2_semidetached = False star1_overflow = False star2_overflow = False ## Get the max radii for both component stars star1_rad_max = b.get_value('requiv_max@primary@component') * u.solRad star2_rad_max = b.get_value('requiv_max@secondary@component') * u.solRad ## Check for semidetached cases if print_diagnostics: print('\nSemidetached checks') print(np.abs((star1_rad - star1_rad_max) / star1_rad_max)) print(np.abs((star2_rad - star2_rad_max) / star2_rad_max)) semidet_cut = 0.001 # (within 0.1% of max radii) semidet_cut = 0.015 # (within 1.5% of max radii) if np.abs((star1_rad - star1_rad_max) / star1_rad_max) < semidet_cut: star1_semidetached = True if np.abs((star2_rad - star2_rad_max) / star2_rad_max) < semidet_cut: star2_semidetached = True ## Check for overflow if (star1_rad > star1_rad_max) and not star1_semidetached: star1_overflow = True if (star2_rad > star2_rad_max) and not star2_semidetached: star2_overflow = True ### Check for if both stars are overflowing; which star overflows more? ### Choose that star to be overflowing more if star1_overflow and star2_overflow: if (star1_rad - star1_rad_max) >= (star2_rad - star2_rad_max): star2_overflow = False else: star1_overflow = False if print_diagnostics: print('\nOverflow Checks') print(star1_semidetached) print(star2_semidetached) print(star1_overflow) print(star2_overflow) ## If none of these overflow cases, set variable to store if binary is detached binary_detached = (not star1_semidetached) and ( not star2_semidetached) and (not star1_overflow) and ( not star2_overflow) ### Set non-zero eccentricity only if binary is detached if binary_detached: b.set_value('ecc@binary@component', binary_ecc) else: b.set_value('ecc@binary@component', 0.) ## Change set up for contact or semidetached cases if star1_overflow or star2_overflow: b = phoebe.default_binary(contact_binary=True) b.set_value('period@orbit', binary_period) b.set_value('sma@binary@component', binary_sma) b.set_value('q@binary@component', binary_q) b.set_value('incl@orbit', binary_inc) if star1_semidetached and not star2_overflow: b.add_constraint('semidetached', 'primary') if star2_semidetached and not star1_overflow: b.add_constraint('semidetached', 'secondary') # Set up compute if use_blackbody_atm: b.add_compute('phoebe', compute='detailed', irrad_method='wilson', atm='blackbody') else: b.add_compute('phoebe', compute='detailed', irrad_method='wilson') # Set the parameters of the component stars of the system ## Primary b.set_value('teff@primary@component', star1_teff) if (not star1_semidetached) and (not star2_overflow): b.set_value('requiv@primary@component', star1_rad) ## Secondary b.set_value('teff@secondary@component', star2_teff) if (not star2_semidetached) and (not star1_overflow): b.set_value('requiv@secondary@component', star2_rad) # Set the number of triangles in the mesh b.set_value('ntriangles@primary@detailed@compute', num_triangles) b.set_value('ntriangles@secondary@detailed@compute', num_triangles) # Phase the observation times ## Read in observation times (kp_MJDs, h_MJDs) = observation_times ## Phase the observation times kp_phased_days = ( (kp_MJDs - t0) % binary_period.to(u.d).value) / binary_period.to( u.d).value h_phased_days = ((h_MJDs - t0) % binary_period.to(u.d).value) / binary_period.to(u.d).value # Add light curve datasets ## Kp kp_phases_sorted_inds = np.argsort(kp_phased_days) kp_model_times = (kp_phased_days) * binary_period.to(u.d).value kp_model_times = kp_model_times[kp_phases_sorted_inds] if use_blackbody_atm: b.add_dataset(phoebe.dataset.lc, time=kp_model_times, dataset='mod_lc_Kp', passband='Keck_NIRC2:Kp', ld_func='linear', ld_coeffs=[0.0]) else: b.add_dataset(phoebe.dataset.lc, time=kp_model_times, dataset='mod_lc_Kp', passband='Keck_NIRC2:Kp') ## H h_phases_sorted_inds = np.argsort(h_phased_days) h_model_times = (h_phased_days) * binary_period.to(u.d).value h_model_times = h_model_times[h_phases_sorted_inds] if use_blackbody_atm: b.add_dataset(phoebe.dataset.lc, times=h_model_times, dataset='mod_lc_H', passband='Keck_NIRC2:H', ld_func='linear', ld_coeffs=[0.0]) else: b.add_dataset(phoebe.dataset.lc, times=h_model_times, dataset='mod_lc_H', passband='Keck_NIRC2:H') # Add mesh dataset if making mesh plot if make_mesh_plots: b.add_dataset('mesh', times=[binary_period / 4.], dataset='mod_mesh') # Run compute # b.run_compute(compute='detailed', model='run') try: b.run_compute(compute='detailed', model='run') except: if print_diagnostics: print("Error during primary binary compute: {0}".format( sys.exc_info()[0])) return err_out # Save out mesh plot if make_mesh_plots: ## Plot Nerdery plt.rc('font', family='serif') plt.rc('font', serif='Computer Modern Roman') plt.rc('text', usetex=True) plt.rc('text.latex', preamble=r"\usepackage{gensymb}") plt.rc('xtick', direction='in') plt.rc('ytick', direction='in') plt.rc('xtick', top=True) plt.rc('ytick', right=True) suffix_str = '' if plot_name is not None: suffix_str = '_' + plot_name ## Mesh plot b['mod_mesh@model'].plot( save='./binary_mesh{0}.pdf'.format(suffix_str)) # Get fluxes ## Kp model_fluxes_Kp = np.array( b['fluxes@lc@mod_lc_Kp@model'].value) * u.solLum / ( 4 * np.pi * u.m**2) # * u.W / (u.m**2.) model_mags_Kp = -2.5 * np.log10(model_fluxes_Kp / flux_ref_Kp) + 0.03 ## H model_fluxes_H = np.array( b['fluxes@lc@mod_lc_H@model'].value) * u.solLum / ( 4 * np.pi * u.m**2) # * u.W / (u.m**2.) model_mags_H = -2.5 * np.log10(model_fluxes_H / flux_ref_H) + 0.03 return (model_mags_Kp, model_mags_H)