From 0df4d869c17d4114f9c80be106d2e85355f7e170 Mon Sep 17 00:00:00 2001 From: David Hendriks <davidhendriks93@gmail.com> Date: Thu, 15 Jul 2021 01:20:08 +0100 Subject: [PATCH] updated the grid by splitting the ms functinality and making it available for other functions --- binarycpython/utils/grid.py | 789 +++++++++++-------- binarycpython/utils/grid_options_defaults.py | 4 + 2 files changed, 465 insertions(+), 328 deletions(-) diff --git a/binarycpython/utils/grid.py b/binarycpython/utils/grid.py index bfb90e887..b4d138ff8 100644 --- a/binarycpython/utils/grid.py +++ b/binarycpython/utils/grid.py @@ -86,6 +86,9 @@ from binarycpython.utils.distribution_functions import ( LOG_LN_CONVERTER, fill_data, get_max_multiplicity, + Arenou2010_binary_fraction, + raghavan2010_binary_fraction, + Moe_de_Stefano_2017_multiplicity_fractions, ) from binarycpython import _binary_c_bindings @@ -122,6 +125,14 @@ class Population: # Custom options self.custom_options = {} + # Load M&s options + self.grid_options['m&s_options'] = copy.deepcopy(moe_distefano_default_options) + + # Write M&S options to a file + os.makedirs(os.path.join(self.grid_options["tmp_dir"], "moe_distefano"), exist_ok=True) + with open(os.path.join(os.path.join(self.grid_options["tmp_dir"], "moe_distefano"), "moeopts.dat"), "w") as f: + f.write(json.dumps(self.grid_options['m&s_options'], indent=4)) + # Argline dict self.argline_dict = {} @@ -1054,7 +1065,7 @@ class Population: include_binary_c_version_info=True, include_binary_c_help_all=True, ) - self.grid_ensemble_results["metadata"]["settings"] = copy.deepcopy(all_info) + self.grid_ensemble_results["metadata"]["settings"] = json.loads(json.dumps(all_info, default=binaryc_json_serializer)) ############################## # Update grid options @@ -2099,7 +2110,7 @@ class Population: code_string += indent * (depth + 1) + "#" * 40 + "\n" code_string += ( indent * (depth + 1) - + "# Code below is for finalising the handling of this iteration of the parameter\n" + + "# Code below is for finalising the handling of this iteration of the parameter {}\n".format(grid_variable["name"]) ) # Set phasevol @@ -2215,7 +2226,15 @@ class Population: + "# Code below will get evaluated for every generated system\n" ) - # Calculate value + # Factor in the custom weight input + code_string += ( + indent * (depth + 1) + + "\n" + ) + code_string += ( + indent * (depth + 1) + + "# Weigh the probability by a custom weighting factor\n" + ) code_string += ( indent * (depth + 1) + 'probability = self.grid_options["weight"] * probabilities_list[{}]'.format( @@ -2223,22 +2242,54 @@ class Population: ) + "\n" ) - # TODO: ask rob if just replacing this with probability is enough + + # Take into account the multiplicity fraction: + code_string += ( + indent * (depth + 1) + + "\n" + ) + code_string += ( + indent * (depth + 1) + + "# Factor the multiplicity fraction into the probability\n" + ) + code_string += ( + indent * (depth + 1) + + 'probability = probability * self._calculate_multiplicity_fraction(parameter_dict)' + + "\n" + ) + + # Add division by amount of repeats + code_string += ( + indent * (depth + 1) + + "\n" + ) + code_string += ( + indent * (depth + 1) + + "# Divide the probability by the amount of repeats\n" + ) + code_string += ( indent * (depth + 1) - # + 'repeat_probability = probability / self.grid_options["repeat"]' + 'probability = probability / self.grid_options["repeat"]' + "\n" ) - # For each repeat of the system this has to be done yes. + # Now we yield the system self.grid_options["repeat"] times. + code_string += ( + indent * (depth + 1) + + "\n" + ) + code_string += ( + indent * (depth + 1) + + "# Loop over the repeats\n" + ) code_string += ( indent * (depth + 1) + 'for _ in range(self.grid_options["repeat"]):' + "\n" ) code_string += indent * (depth + 2) + "_total_starcount += 1\n" - # set probability and phasevol values + # set probability and phasevol values into the system dict code_string += ( indent * (depth + 2) + 'parameter_dict["{}"] = {}'.format("probability", "probability") @@ -2265,16 +2316,11 @@ class Population: # Handling of what is returned, or what is not. # TODO: think of whether this is a good method # code_string += indent * (depth + 2) + "if (self.grid_options['multiplicity'] >= 2): print('phasevol_q: ',phasevol_q); print('phasevol_log10per: ',phasevol_log10per);\n" - # code_string += indent * (depth + 2) + "print('phasevol_lnm1: ',phasevol_lnm1); print('phasevol_multiplicity: ',phasevol_multiplicity);\n" - # code_string += indent * (depth + 2) + "print(probabilities_list)\n" - # code_string += indent * (depth + 2) + "print(parameter_dict)\n" - # code_string += indent * (depth + 2) + "print('YOO IK GA LEKKER NOG EEN RONDJE')\n" code_string += indent * (depth + 2) + "yield(parameter_dict)\n" # If its a dry run, dont do anything with it else: # code_string += indent * (depth + 2) + "if (self.grid_options['multiplicity'] >= 2): print(phasevol_q)\n" - # code_string += indent * (depth + 2) + "print(parameter_dict)\n" code_string += indent * (depth + 2) + "pass\n" code_string += indent * (depth + 1) + "#" * 40 + "\n" @@ -3137,11 +3183,6 @@ class Population: self.logger.addHandler(handler_file) self.logger.addHandler(handler_stdout) - # def join_result_dicts(self): - # """ - # Function to join the result dictionaries - # """ - def _check_binary_c_error(self, binary_c_output, system_dict): """ Function to check whether binary_c throws an error and handle accordingly. @@ -3216,77 +3257,45 @@ class Population: f.write(argstring + "\n") else: verbose_print( - "binary_c had 0 output. Which is strange", + "binary_c had 0 output. Which is strange. Make sure you mean to do this. If there is actually ensemble output being generated then its fine.", self.grid_options["verbosity"], 2, ) - ################################################################################################ - def Moe_de_Stefano_2017(self, options=None): + def set_moe_distefano_settings(self, options=None): """ - Class method of the population class - - Takes a dictionary as its only argument - - The dictionary should contain a file to + Function to set user input configurations for the moe & distefano methods + + If nothing is passed then we just use the default options """ - # Require input options - if not options: - msg = "Please provide a options dictionary." - verbose_print( - "\tMoe_de_Stefano_2017: {}".format(msg), - self.grid_options["verbosity"], - 0, - ) - raise ValueError(msg) - - # Create the tmp dir - ms_tmp_dir = os.path.join(self.grid_options["tmp_dir"], "moe_distefano") - os.makedirs(ms_tmp_dir, exist_ok=True) - - # TODO: put this in defaults - default_options = copy.deepcopy(moe_distefano_default_options) - - # Take the option dictionary that was given and override. - options = update_dicts(default_options, options) + if options: + # Take the option dictionary that was given and override. + options = update_dicts(self.grid_options['m&s_options'], options) + self.grid_options['m&s_options'] = copy.deepcopy(dict(options)) - # Write options to a file - with open(os.path.join(ms_tmp_dir, "moeopts.dat"), "w") as f: - f.write(json.dumps(options, indent=4)) + # Write options to a file + os.makedirs(os.path.join(self.grid_options["tmp_dir"], "moe_distefano"), exist_ok=True) + with open(os.path.join(os.path.join(self.grid_options["tmp_dir"], "moe_distefano"), "moeopts.dat"), "w") as f: + f.write(json.dumps(self.grid_options['m&s_options'], indent=4)) - json_data = get_moe_distefano_dataset(options) - - # entry of log10M1 is a list containing 1 dict. We can take the dict out of the list - - json_data["log10M1"] = json_data["log10M1"][0] - - # Get all the masses - logmasses = sorted(json_data["log10M1"].keys()) - if not logmasses: - msg = "The table does not contain masses." - verbose_print( - "\tMoe_de_Stefano_2017: {}".format(msg), - self.grid_options["verbosity"], - 0, - ) - raise ValueError(msg) - - # Write to file - with open(os.path.join(ms_tmp_dir, "moe.log"), "w") as logfile: - logfile.write("logâ‚â‚€Masses(M☉) {}\n".format(logmasses)) + def _load_moe_distefano_data(self): + """ + Function to load the moe & distefano data + """ - # Get all the periods and see if they are all consistently present - logperiods = [] - for logmass in logmasses: - if not logperiods: - logperiods = sorted(json_data["log10M1"][logmass]["logP"]) - dlog10P = float(logperiods[1]) - float(logperiods[0]) + # Only if the grid is loaded and moecache contains information + if (not self.grid_options['_loaded_ms_data']) and Moecache: + # Load the json data + json_data = get_moe_distefano_dataset(self.grid_options['m&s_options']) - current_logperiods = sorted(json_data["log10M1"][logmass]["logP"]) + # entry of log10M1 is a list containing 1 dict. We can take the dict out of the list + json_data["log10M1"] = json_data["log10M1"][0] - if not (logperiods == current_logperiods): - msg = "Period values are not consistent throughout the dataset" + # Get all the masses + logmasses = sorted(json_data["log10M1"].keys()) + if not logmasses: + msg = "The table does not contain masses." verbose_print( "\tMoe_de_Stefano_2017: {}".format(msg), self.grid_options["verbosity"], @@ -3294,15 +3303,22 @@ class Population: ) raise ValueError(msg) - ############################################################ - # log10period binwidth : of course this assumes a fixed - # binwidth, so we check for this too. + # Write to file + os.makedirs(os.path.join(self.grid_options["tmp_dir"], "moe_distefano"), exist_ok=True) + with open(os.path.join(os.path.join(self.grid_options["tmp_dir"], "moe_distefano"), "moe.log"), "w") as logfile: + logfile.write("logâ‚â‚€Masses(M☉) {}\n".format(logmasses)) + + # Get all the periods and see if they are all consistently present + logperiods = [] + for logmass in logmasses: + if not logperiods: + logperiods = sorted(json_data["log10M1"][logmass]["logP"]) + dlog10P = float(logperiods[1]) - float(logperiods[0]) - for i in range(len(current_logperiods) - 1): - if not dlog10P == ( - float(current_logperiods[i + 1]) - float(current_logperiods[i]) - ): - msg = "Period spacing is not consistent throughout the dataset" + current_logperiods = sorted(json_data["log10M1"][logmass]["logP"]) + + if not (logperiods == current_logperiods): + msg = "Period values are not consistent throughout the dataset" verbose_print( "\tMoe_de_Stefano_2017: {}".format(msg), self.grid_options["verbosity"], @@ -3310,69 +3326,100 @@ class Population: ) raise ValueError(msg) - # Write to file - with open( - os.path.join(self.grid_options["tmp_dir"], "moe_distefano", "moe.log"), "a" - ) as logfile: - logfile.write("logâ‚â‚€Periods(days) {}\n".format(logperiods)) - - # Fill the global dict - for logmass in logmasses: - # Create the multiplicity table - if not Moecache.get("multiplicity_table", None): - Moecache["multiplicity_table"] = [] - - # multiplicity as a function of primary mass - Moecache["multiplicity_table"].append( - [ - float(logmass), - json_data["log10M1"][logmass]["f_multi"], - json_data["log10M1"][logmass]["single star fraction"], - json_data["log10M1"][logmass]["binary star fraction"], - json_data["log10M1"][logmass]["triple/quad star fraction"], - ] - ) + ############################################################ + # log10period binwidth : of course this assumes a fixed + # binwidth, so we check for this too. - ############################################################ - # a small log10period which we can shift just outside the - # table to force integration out there to zero - epslog10P = 1e-8 * dlog10P + for i in range(len(current_logperiods) - 1): + if not dlog10P == ( + float(current_logperiods[i + 1]) - float(current_logperiods[i]) + ): + msg = "Period spacing is not consistent throughout the dataset" + verbose_print( + "\tMoe_de_Stefano_2017: {}".format(msg), + self.grid_options["verbosity"], + 0, + ) + raise ValueError(msg) - ############################################################ - # loop over either binary or triple-outer periods - first = 1 + # Write to file + os.makedirs(os.path.join(self.grid_options["tmp_dir"], "moe_distefano"), exist_ok=True) + with open( + os.path.join(self.grid_options["tmp_dir"], "moe_distefano", "moe.log"), "a" + ) as logfile: + logfile.write("logâ‚â‚€Periods(days) {}\n".format(logperiods)) - # Go over the periods - for logperiod in logperiods: - # print("logperiod: {}".format(logperiod)) - ############################################################ - # distributions of binary and triple star fractions - # as a function of mass, period. - # - # Note: these should be per unit log10P, hence we - # divide by $dlog10P + # Fill the global dict + for logmass in logmasses: + # Create the multiplicity table + if not Moecache.get("multiplicity_table", None): + Moecache["multiplicity_table"] = [] - if first: - first = 0 + # multiplicity as a function of primary mass + Moecache["multiplicity_table"].append( + [ + float(logmass), + json_data["log10M1"][logmass]["f_multi"], + json_data["log10M1"][logmass]["single star fraction"], + json_data["log10M1"][logmass]["binary star fraction"], + json_data["log10M1"][logmass]["triple/quad star fraction"], + ] + ) - # Create the multiplicity table - if not Moecache.get("period_distributions", None): - Moecache["period_distributions"] = [] + ############################################################ + # a small log10period which we can shift just outside the + # table to force integration out there to zero + epslog10P = 1e-8 * dlog10P + + ############################################################ + # loop over either binary or triple-outer periods + first = 1 + # Go over the periods + for logperiod in logperiods: + # print("logperiod: {}".format(logperiod)) ############################################################ - # lower bound the period distributions to zero probability - Moecache["period_distributions"].append( - [ - float(logmass), - float(logperiod) - 0.5 * dlog10P - epslog10P, - 0.0, - 0.0, - ] - ) + # distributions of binary and triple star fractions + # as a function of mass, period. + # + # Note: these should be per unit log10P, hence we + # divide by $dlog10P + + if first: + first = 0 + + # Create the multiplicity table + if not Moecache.get("period_distributions", None): + Moecache["period_distributions"] = [] + + ############################################################ + # lower bound the period distributions to zero probability + Moecache["period_distributions"].append( + [ + float(logmass), + float(logperiod) - 0.5 * dlog10P - epslog10P, + 0.0, + 0.0, + ] + ) + Moecache["period_distributions"].append( + [ + float(logmass), + float(logperiod) - 0.5 * dlog10P, + json_data["log10M1"][logmass]["logP"][logperiod][ + "normed_bin_frac_p_dist" + ] + / dlog10P, + json_data["log10M1"][logmass]["logP"][logperiod][ + "normed_tripquad_frac_p_dist" + ] + / dlog10P, + ] + ) Moecache["period_distributions"].append( [ float(logmass), - float(logperiod) - 0.5 * dlog10P, + float(logperiod), json_data["log10M1"][logmass]["logP"][logperiod][ "normed_bin_frac_p_dist" ] @@ -3383,132 +3430,118 @@ class Population: / dlog10P, ] ) + + ############################################################ + # distributions as a function of mass, period, q + # + # First, get a list of the qs given by Moe + # + qs = sorted(json_data["log10M1"][logmass]["logP"][logperiod]["q"]) + + # Fill the data and 'normalize' + qdata = fill_data( + qs, json_data["log10M1"][logmass]["logP"][logperiod]["q"] + ) + + # Create the multiplicity table + if not Moecache.get("q_distributions", None): + Moecache["q_distributions"] = [] + + for q in qs: + Moecache["q_distributions"].append( + [float(logmass), float(logperiod), float(q), qdata[q]] + ) + + ############################################################ + # eccentricity distributions as a function of mass, period, ecc + eccs = sorted(json_data["log10M1"][logmass]["logP"][logperiod]["e"]) + + # Fill the data and 'normalize' + ecc_data = fill_data( + eccs, json_data["log10M1"][logmass]["logP"][logperiod]["e"] + ) + + # Create the multiplicity table + if not Moecache.get("ecc_distributions", None): + Moecache["ecc_distributions"] = [] + + for ecc in eccs: + Moecache["ecc_distributions"].append( + [float(logmass), float(logperiod), float(ecc), ecc_data[ecc]] + ) + + ############################################################ + # upper bound the period distributions to zero probability Moecache["period_distributions"].append( [ float(logmass), - float(logperiod), - json_data["log10M1"][logmass]["logP"][logperiod][ + float(logperiods[-1]) + 0.5 * dlog10P, # TODO: why this shift? + json_data["log10M1"][logmass]["logP"][logperiods[-1]][ "normed_bin_frac_p_dist" ] / dlog10P, - json_data["log10M1"][logmass]["logP"][logperiod][ + json_data["log10M1"][logmass]["logP"][logperiods[-1]][ "normed_tripquad_frac_p_dist" ] / dlog10P, ] ) - - ############################################################ - # distributions as a function of mass, period, q - # - # First, get a list of the qs given by Moe - # - qs = sorted(json_data["log10M1"][logmass]["logP"][logperiod]["q"]) - - # Fill the data and 'normalize' - qdata = fill_data( - qs, json_data["log10M1"][logmass]["logP"][logperiod]["q"] - ) - - # Create the multiplicity table - if not Moecache.get("q_distributions", None): - Moecache["q_distributions"] = [] - - for q in qs: - Moecache["q_distributions"].append( - [float(logmass), float(logperiod), float(q), qdata[q]] - ) - - ############################################################ - # eccentricity distributions as a function of mass, period, ecc - eccs = sorted(json_data["log10M1"][logmass]["logP"][logperiod]["e"]) - - # Fill the data and 'normalize' - ecc_data = fill_data( - eccs, json_data["log10M1"][logmass]["logP"][logperiod]["e"] + Moecache["period_distributions"].append( + [ + float(logmass), + float(logperiods[-1]) + 0.5 * dlog10P + epslog10P, + 0.0, + 0.0, + ] ) - # Create the multiplicity table - if not Moecache.get("ecc_distributions", None): - Moecache["ecc_distributions"] = [] - - for ecc in eccs: - Moecache["ecc_distributions"].append( - [float(logmass), float(logperiod), float(ecc), ecc_data[ecc]] - ) - - ############################################################ - # upper bound the period distributions to zero probability - Moecache["period_distributions"].append( - [ - float(logmass), - float(logperiods[-1]) + 0.5 * dlog10P, # TODO: why this shift? - json_data["log10M1"][logmass]["logP"][logperiods[-1]][ - "normed_bin_frac_p_dist" - ] - / dlog10P, - json_data["log10M1"][logmass]["logP"][logperiods[-1]][ - "normed_tripquad_frac_p_dist" - ] - / dlog10P, - ] + verbose_print( + "\tMoe_de_Stefano_2017: Length period_distributions table: {}".format( + len(Moecache["period_distributions"]) + ), + self.grid_options["verbosity"], + _MS_VERBOSITY_LEVEL, ) - Moecache["period_distributions"].append( - [ - float(logmass), - float(logperiods[-1]) + 0.5 * dlog10P + epslog10P, - 0.0, - 0.0, - ] + verbose_print( + "\tMoe_de_Stefano_2017: Length multiplicity table: {}".format( + len(Moecache["multiplicity_table"]) + ), + self.grid_options["verbosity"], + _MS_VERBOSITY_LEVEL, + ) + verbose_print( + "\tMoe_de_Stefano_2017: Length q table: {}".format( + len(Moecache["q_distributions"]) + ), + self.grid_options["verbosity"], + _MS_VERBOSITY_LEVEL, + ) + verbose_print( + "\tMoe_de_Stefano_2017: Length ecc table: {}".format( + len(Moecache["ecc_distributions"]) + ), + self.grid_options["verbosity"], + _MS_VERBOSITY_LEVEL, ) - verbose_print( - "\tMoe_de_Stefano_2017: Length period_distributions table: {}".format( - len(Moecache["period_distributions"]) - ), - self.grid_options["verbosity"], - _MS_VERBOSITY_LEVEL, - ) - verbose_print( - "\tMoe_de_Stefano_2017: Length multiplicity table: {}".format( - len(Moecache["multiplicity_table"]) - ), - self.grid_options["verbosity"], - _MS_VERBOSITY_LEVEL, - ) - verbose_print( - "\tMoe_de_Stefano_2017: Length q table: {}".format( - len(Moecache["q_distributions"]) - ), - self.grid_options["verbosity"], - _MS_VERBOSITY_LEVEL, - ) - verbose_print( - "\tMoe_de_Stefano_2017: Length ecc table: {}".format( - len(Moecache["ecc_distributions"]) - ), - self.grid_options["verbosity"], - _MS_VERBOSITY_LEVEL, - ) - - # Write to logfile - with open(os.path.join(ms_tmp_dir, "moecache.json"), "w") as cache_filehandle: - cache_filehandle.write(json.dumps(Moecache, indent=4)) - + # Write to logfile + os.makedirs(os.path.join(self.grid_options["tmp_dir"], "moe_distefano"), exist_ok=True) + with open(os.path.join(os.path.join(self.grid_options["tmp_dir"], "moe_distefano"), "moecache.json"), "w") as cache_filehandle: + cache_filehandle.write(json.dumps(Moecache, indent=4)) - ############################################################ - # construct the grid here - # + # Signal that the data has been loaded + self.grid_options['_loaded_ms_data'] = True - ############################################################ - # make a version of the options which can be passed to - # the probability density function in the gridcode + def _set_moe_distefano_distributions(self): + """ + Function to set the moe & distefano distribution + """ ############################################################ # first, the multiplicity, this is 1,2,3,4, ... # for singles, binaries, triples, quadruples, ... - max_multiplicity = get_max_multiplicity(options["multiplicity_modulator"]) + max_multiplicity = get_max_multiplicity(self.grid_options['m&s_options']["multiplicity_modulator"]) verbose_print( "\tMoe_de_Stefano_2017: Max multiplicity = {}".format(max_multiplicity), self.grid_options["verbosity"], @@ -3517,9 +3550,6 @@ class Population: ###### # Setting up the grid variables - # Make a normal dict of the options again - options = dict(options) - # Multiplicity self.add_grid_variable( name="multiplicity", @@ -3528,11 +3558,11 @@ class Population: valuerange=[1, max_multiplicity], resolution=1, spacingfunc="range(1, 5)", - precode='self.grid_options["multiplicity"] = multiplicity; options={}'.format( - options + precode='self.grid_options["multiplicity"] = multiplicity; self.bse_options["multiplicity"] = multiplicity; options={}'.format( + self.grid_options['m&s_options'] ), condition="({}[multiplicity-1] > 0)".format( - str(options["multiplicity_modulator"]) + str(self.grid_options['m&s_options']["multiplicity_modulator"]) ), gridtype="edge", dphasevol=-1, @@ -3552,19 +3582,19 @@ class Population: longname="Primary mass", resolution="options['resolutions']['M'][0]", spacingfunc="const(np.log({}), np.log({}), {})".format( - options["ranges"]["M"][0], - options["ranges"]["M"][1], - options["resolutions"]["M"][0], + self.grid_options['m&s_options']["ranges"]["M"][0], + self.grid_options['m&s_options']["ranges"]["M"][1], + self.grid_options['m&s_options']["resolutions"]["M"][0], ), valuerange=[ - "np.log({})".format(options["ranges"]["M"][0]), - "np.log({})".format(options["ranges"]["M"][1]), + "np.log({})".format(self.grid_options['m&s_options']["ranges"]["M"][0]), + "np.log({})".format(self.grid_options['m&s_options']["ranges"]["M"][1]), ], gridtype="centred", dphasevol="dlnm1", precode='M_1 = np.exp(lnm1); options["M1"]=M_1', probdist="Moe_de_Stefano_2017_pdf({{{}, {}, {}}}, verbosity=self.grid_options['verbosity'])['total_probdens'] if multiplicity == 1 else 1".format( - str(dict(options))[1:-1], "'multiplicity': multiplicity", "'M1': M_1" + str(dict(self.grid_options['m&s_options']))[1:-1], "'multiplicity': multiplicity", "'M1': M_1" ), ) @@ -3575,23 +3605,23 @@ class Population: name="log10per", parameter_name="orbital_period", longname="log10(Orbital_Period)", - resolution=options["resolutions"]["logP"][0], + resolution=self.grid_options['m&s_options']["resolutions"]["logP"][0], probdist=1.0, condition='(self.grid_options["multiplicity"] >= 2)', branchpoint=1 if max_multiplicity > 1 else 0, # Signal here to put a branchpoint if we have a max multiplicity higher than 1. gridtype="centred", dphasevol="({} * dlog10per)".format(LOG_LN_CONVERTER), - valuerange=[options["ranges"]["logP"][0], options["ranges"]["logP"][1]], + valuerange=[self.grid_options['m&s_options']["ranges"]["logP"][0], self.grid_options['m&s_options']["ranges"]["logP"][1]], spacingfunc="const({}, {}, {})".format( - options["ranges"]["logP"][0], - options["ranges"]["logP"][1], - options["resolutions"]["logP"][0], + self.grid_options['m&s_options']["ranges"]["logP"][0], + self.grid_options['m&s_options']["ranges"]["logP"][1], + self.grid_options['m&s_options']["resolutions"]["logP"][0], ), precode="""orbital_period = 10.0**log10per qmin={}/M_1 qmax=maximum_mass_ratio_for_RLOF(M_1, orbital_period) """.format( - options.get("Mmin", 0.07) + self.grid_options['m&s_options'].get("Mmin", 0.07) ), ) # TODO: change the maximum_mass_ratio_for_RLOF @@ -3601,14 +3631,14 @@ qmax=maximum_mass_ratio_for_RLOF(M_1, orbital_period) parameter_name="M_2", longname="Mass ratio", valuerange=[ - options["ranges"]["q"][0] - if options.get("ranges", {}).get("q", None) + self.grid_options['m&s_options']["ranges"]["q"][0] + if self.grid_options['m&s_options'].get("ranges", {}).get("q", None) else "options.get('Mmin', 0.07)/M_1", - options["ranges"]["q"][1] - if options.get("ranges", {}).get("q", None) + self.grid_options['m&s_options']["ranges"]["q"][1] + if self.grid_options['m&s_options'].get("ranges", {}).get("q", None) else "qmax", ], - resolution=options["resolutions"]["M"][1], + resolution=self.grid_options['m&s_options']["resolutions"]["M"][1], probdist=1, gridtype="centred", dphasevol="dq", @@ -3617,35 +3647,35 @@ M_2 = q * M_1 sep = calc_sep_from_period(M_1, M_2, orbital_period) """, spacingfunc="const({}, {}, {})".format( - options["ranges"]["q"][0] - if options.get("ranges", {}).get("q", [None, None])[0] - else "{}/M_1".format(options.get("Mmin", 0.07)), - options["ranges"]["q"][1] - if options.get("ranges", {}).get("q", [None, None])[1] + self.grid_options['m&s_options']["ranges"]["q"][0] + if self.grid_options['m&s_options'].get("ranges", {}).get("q", [None, None])[0] + else "{}/M_1".format(self.grid_options['m&s_options'].get("Mmin", 0.07)), + self.grid_options['m&s_options']["ranges"]["q"][1] + if self.grid_options['m&s_options'].get("ranges", {}).get("q", [None, None])[1] else "qmax", - options["resolutions"]["M"][1], + self.grid_options['m&s_options']["resolutions"]["M"][1], ), ) # (optional) binaries: eccentricity - if options["resolutions"]["ecc"][0] > 0: + if self.grid_options['m&s_options']["resolutions"]["ecc"][0] > 0: self.add_grid_variable( name="ecc", parameter_name="eccentricity", longname="Eccentricity", - resolution=options["resolutions"]["ecc"][0], + resolution=self.grid_options['m&s_options']["resolutions"]["ecc"][0], probdist=1, gridtype="centred", dphasevol="decc", precode="eccentricity=ecc", valuerange=[ - options["ranges"]["ecc"][0], # Just fail if not defined. - options["ranges"]["ecc"][1], + self.grid_options['m&s_options']["ranges"]["ecc"][0], # Just fail if not defined. + self.grid_options['m&s_options']["ranges"]["ecc"][1], ], spacingfunc="const({}, {}, {})".format( - options["ranges"]["ecc"][0], # Just fail if not defined. - options["ranges"]["ecc"][1], - options["resolutions"]["ecc"][0], + self.grid_options['m&s_options']["ranges"]["ecc"][0], # Just fail if not defined. + self.grid_options['m&s_options']["ranges"]["ecc"][1], + self.grid_options['m&s_options']["resolutions"]["ecc"][0], ), ) @@ -3656,45 +3686,45 @@ sep = calc_sep_from_period(M_1, M_2, orbital_period) name="log10per2", parameter_name="orbital_period_triple", longname="log10(Orbital_Period2)", - resolution=options["resolutions"]["logP"][1], + resolution=self.grid_options['m&s_options']["resolutions"]["logP"][1], probdist=1.0, condition='(self.grid_options["multiplicity"] >= 3)', branchpoint=2 if max_multiplicity > 2 else 0, # Signal here to put a branchpoint if we have a max multiplicity higher than 1. gridtype="centred", dphasevol="({} * dlog10per2)".format(LOG_LN_CONVERTER), valuerange=[ - options["ranges"]["logP"][0], - options["ranges"]["logP"][1], + self.grid_options['m&s_options']["ranges"]["logP"][0], + self.grid_options['m&s_options']["ranges"]["logP"][1], ], spacingfunc="const({}, {}, {})".format( - options["ranges"]["logP"][0], - options["ranges"]["logP"][1], - options["resolutions"]["logP"][1], + self.grid_options['m&s_options']["ranges"]["logP"][0], + self.grid_options['m&s_options']["ranges"]["logP"][1], + self.grid_options['m&s_options']["resolutions"]["logP"][1], ), precode="""orbital_period_triple = 10.0**log10per2 q2min={}/(M_1+M_2) q2max=maximum_mass_ratio_for_RLOF(M_1+M_2, orbital_period_triple) """.format( - options.get("Mmin", 0.07) + self.grid_options['m&s_options'].get("Mmin", 0.07) ), ) # Triples: mass ratio # Note, the mass ratio is M_outer/M_inner - print("M2 resolution {}".format(options["resolutions"]["M"][2])) + print("M2 resolution {}".format(self.grid_options['m&s_options']["resolutions"]["M"][2])) self.add_grid_variable( name="q2", parameter_name="M_3", longname="Mass ratio outer/inner", valuerange=[ - options["ranges"]["q"][0] - if options.get("ranges", {}).get("q", None) + self.grid_options['m&s_options']["ranges"]["q"][0] + if self.grid_options['m&s_options'].get("ranges", {}).get("q", None) else "options.get('Mmin', 0.07)/(M_1+M_2)", - options["ranges"]["q"][1] - if options.get("ranges", {}).get("q", None) + self.grid_options['m&s_options']["ranges"]["q"][1] + if self.grid_options['m&s_options'].get("ranges", {}).get("q", None) else "q2max", ], - resolution=options["resolutions"]["M"][2], + resolution=self.grid_options['m&s_options']["resolutions"]["M"][2], probdist=1, gridtype="centred", dphasevol="dq2", @@ -3704,35 +3734,35 @@ sep2 = calc_sep_from_period((M_1+M_2), M_3, orbital_period_triple) eccentricity2=0 """, spacingfunc="const({}, {}, {})".format( - options["ranges"]["q"][0] - if options.get("ranges", {}).get("q", None) + self.grid_options['m&s_options']["ranges"]["q"][0] + if self.grid_options['m&s_options'].get("ranges", {}).get("q", None) else "options.get('Mmin', 0.07)/(M_1+M_2)", - options["ranges"]["q"][1] - if options.get("ranges", {}).get("q", None) + self.grid_options['m&s_options']["ranges"]["q"][1] + if self.grid_options['m&s_options'].get("ranges", {}).get("q", None) else "q2max", - options["resolutions"]["M"][2], + self.grid_options['m&s_options']["resolutions"]["M"][2], ), ) # (optional) triples: eccentricity - if options["resolutions"]["ecc"][1] > 0: + if self.grid_options['m&s_options']["resolutions"]["ecc"][1] > 0: self.add_grid_variable( name="ecc2", parameter_name="eccentricity2", longname="Eccentricity of the triple", - resolution=options["resolutions"]["ecc"][1], + resolution=self.grid_options['m&s_options']["resolutions"]["ecc"][1], probdist=1, gridtype="centred", dphasevol="decc2", precode="eccentricity2=ecc2", valuerange=[ - options["ranges"]["ecc"][0], # Just fail if not defined. - options["ranges"]["ecc"][1], + self.grid_options['m&s_options']["ranges"]["ecc"][0], # Just fail if not defined. + self.grid_options['m&s_options']["ranges"]["ecc"][1], ], spacingfunc="const({}, {}, {})".format( - options["ranges"]["ecc"][0], # Just fail if not defined. - options["ranges"]["ecc"][1], - options["resolutions"]["ecc"][1], + self.grid_options['m&s_options']["ranges"]["ecc"][0], # Just fail if not defined. + self.grid_options['m&s_options']["ranges"]["ecc"][1], + self.grid_options['m&s_options']["resolutions"]["ecc"][1], ), ) @@ -3742,44 +3772,44 @@ eccentricity2=0 name="log10per3", parameter_name="orbital_period_quadruple", longname="log10(Orbital_Period3)", - resolution=options["resolutions"]["logP"][2], + resolution=self.grid_options['m&s_options']["resolutions"]["logP"][2], probdist=1.0, condition='(self.grid_options["multiplicity"] >= 4)', branchpoint=3 if max_multiplicity > 3 else 0, # Signal here to put a branchpoint if we have a max multiplicity higher than 1. gridtype="centred", dphasevol="({} * dlog10per3)".format(LOG_LN_CONVERTER), valuerange=[ - options["ranges"]["logP"][0], - options["ranges"]["logP"][1], + self.grid_options['m&s_options']["ranges"]["logP"][0], + self.grid_options['m&s_options']["ranges"]["logP"][1], ], spacingfunc="const({}, {}, {})".format( - options["ranges"]["logP"][0], - options["ranges"]["logP"][1], - options["resolutions"]["logP"][2], + self.grid_options['m&s_options']["ranges"]["logP"][0], + self.grid_options['m&s_options']["ranges"]["logP"][1], + self.grid_options['m&s_options']["resolutions"]["logP"][2], ), precode="""orbital_period_quadruple = 10.0**log10per3 q3min={}/(M_3) q3max=maximum_mass_ratio_for_RLOF(M_3, orbital_period_quadruple) """.format( - options.get("Mmin", 0.07) + self.grid_options['m&s_options'].get("Mmin", 0.07) ), ) # Quadruple: mass ratio : M_outer / M_inner - print("M3 resolution {}".format(options["resolutions"]["M"][3])) + print("M3 resolution {}".format(self.grid_options['m&s_options']["resolutions"]["M"][3])) self.add_grid_variable( name="q3", parameter_name="M_4", longname="Mass ratio outer low/outer high", valuerange=[ - options["ranges"]["q"][0] - if options.get("ranges", {}).get("q", None) + self.grid_options['m&s_options']["ranges"]["q"][0] + if self.grid_options['m&s_options'].get("ranges", {}).get("q", None) else "options.get('Mmin', 0.07)/(m3)", - options["ranges"]["q"][1] - if options.get("ranges", {}).get("q", None) + self.grid_options['m&s_options']["ranges"]["q"][1] + if self.grid_options['m&s_options'].get("ranges", {}).get("q", None) else "q3max", ], - resolution=options["resolutions"]["M"][3], + resolution=self.grid_options['m&s_options']["resolutions"]["M"][3], probdist=1, gridtype="centred", dphasevol="dq3", @@ -3789,40 +3819,40 @@ sep3 = calc_sep_from_period((M_3), M_4, orbital_period_quadruple) eccentricity3=0 """, spacingfunc="const({}, {}, {})".format( - options["ranges"]["q"][0] - if options.get("ranges", {}).get("q", None) + self.grid_options['m&s_options']["ranges"]["q"][0] + if self.grid_options['m&s_options'].get("ranges", {}).get("q", None) else "options.get('Mmin', 0.07)/(m3)", - options["ranges"]["q"][1] - if options.get("ranges", {}).get("q", None) + self.grid_options['m&s_options']["ranges"]["q"][1] + if self.grid_options['m&s_options'].get("ranges", {}).get("q", None) else "q3max", - options["resolutions"]["M"][2], + self.grid_options['m&s_options']["resolutions"]["M"][2], ), ) # TODO: Ask about which periods should be used for the calc_sep_from_period # (optional) triples: eccentricity - if options["resolutions"]["ecc"][2] > 0: + if self.grid_options['m&s_options']["resolutions"]["ecc"][2] > 0: self.add_grid_variable( name="ecc3", parameter_name="eccentricity3", longname="Eccentricity of the triple+quadruple/outer binary", - resolution=options["resolutions"]["ecc"][2], + resolution=self.grid_options['m&s_options']["resolutions"]["ecc"][2], probdist=1, gridtype="centred", dphasevol="decc3", precode="eccentricity3=ecc3", valuerange=[ - options["ranges"]["ecc"][ + self.grid_options['m&s_options']["ranges"]["ecc"][ 0 ], # Just fail if not defined. - options["ranges"]["ecc"][1], + self.grid_options['m&s_options']["ranges"]["ecc"][1], ], spacingfunc="const({}, {}, {})".format( - options["ranges"]["ecc"][ + self.grid_options['m&s_options']["ranges"]["ecc"][ 0 ], # Just fail if not defined. - options["ranges"]["ecc"][1], - options["resolutions"]["ecc"][2], + self.grid_options['m&s_options']["ranges"]["ecc"][1], + self.grid_options['m&s_options']["resolutions"]["ecc"][2], ), ) @@ -3832,8 +3862,8 @@ eccentricity3=0 # the real probability. The trick we use is to strip the options_dict as a string # and add some keys to it: - probdist_addition = "Moe_de_Stefano_2017_pdf({{{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}}, verbosity=self.grid_options['verbosity'])['total_probdens']".format( - str(dict(options))[1:-1], + updated_options = "{{{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}}".format( + str(dict(self.grid_options['m&s_options']))[1:-1], '"multiplicity": multiplicity', '"M1": M_1', '"M2": M_2', @@ -3847,11 +3877,20 @@ eccentricity3=0 '"ecc3": eccentricity3', ) + probdist_addition = "Moe_de_Stefano_2017_pdf({}, verbosity=self.grid_options['verbosity'])['total_probdens']".format( + updated_options + ) + # and finally the probability calculator self.grid_options["_grid_variables"][self.last_grid_variable()][ "probdist" ] = probdist_addition + # Add the options to the grid + # self.grid_options["_grid_variables"][self.last_grid_variable()][ + # "precode" + # ] += "; self.grid_options['m&s_ensemble_options'] = {}".format(o) + verbose_print( "\tMoe_de_Stefano_2017: Added final call to the pdf function".format( max_multiplicity @@ -3863,6 +3902,25 @@ eccentricity3=0 # Signal that the M&S grid has been set self.grid_options['_set_ms_grid'] = True + ################################################################################################ + def Moe_de_Stefano_2017(self, options=None): + """ + Function to handle setting the user input settings, + set up the data and load that into interpolators and + and then set the distribution functions + + Takes a dictionary as its only argument + """ + + # Set the userinput + self.set_moe_distefano_settings(options=options) + + # Load the data + self._load_moe_distefano_data() + + # construct the grid here + self._set_moe_distefano_distributions() + def _clean_interpolators(self): """ Function to clean up the interpolators after a run @@ -3880,3 +3938,78 @@ eccentricity3=0 Moecache[key].destroy() del Moecache[key] gc.collect() + + ##### Unsorted functions + def _calculate_multiplicity_fraction(self, system_dict): + """ + Function to calculate multiplicity fraction + + Makes use of the self.bse_options['multiplicity'] value. If its not set, it will raise an error + + grid_options['multiplicity_fraction_function'] will be checked for the choice + """ + + + # Just return 1 if no option has been chosen + if self.grid_options['multiplicity_fraction_function'] == 0: + print("Chosen not to use any multiplicity fraction.") + return 1 + + # Raise an error if the multiplicity is not set + if not system_dict.get('multiplicity', None): + msg = "Multiplicity value has not been set. When using a specific multiplicity fraction function please set the multiplicity" + raise ValueError(msg) + + # Go over the chosen options + if self.grid_options['multiplicity_fraction_function'] == 1: + # Arenou 2010 will be used + # TODO: add verbose logging + print("Using Arenou 2010 to calculate multiplicity fractions") + binary_fraction = Arenou2010_binary_fraction(system_dict['M_1']) + multiplicity_fraction_dict = {1: 1-binary_fraction, 2: binary_fraction, 3: 0, 4: 0} + + elif self.grid_options['multiplicity_fraction_function'] == 2: + # Raghavan 2010 will be used + # TODO: add verbose logging + print("Using Rhagavan 2010 to calculate multiplicity fractions") + binary_fraction = raghavan2010_binary_fraction(system_dict['M_1']) + multiplicity_fraction_dict = {1: 1-binary_fraction, 2: binary_fraction, 3: 0, 4: 0} + + elif self.grid_options['multiplicity_fraction_function'] == 3: + # We need to check several things now here: + + # First, are the options for the M&S grid set? On start it is filled with the default settings + if not self.grid_options['m&s_options']: + msg = "The M&S options do not seem to be set properly. The value is {}".format(self.grid_options['m&s_options']) + raise ValueError(msg) + + # Second: is the moecache filled. + if not Moecache: + msg = "Moecache is empty. It needs to be filled with the data for the interpolators" + raise ValueError(msg) + + # Load the data + self._load_moe_distefano_data() + + # record the prev value + prev_M1_value_ms = self.grid_options['m&s_options'].get('M_1', None) + + # Set value of M1 of the current system + self.grid_options['m&s_options']['M_1'] = system_dict['M_1'] + + # Calculate the multiplicity fraction + ding = Moe_de_Stefano_2017_multiplicity_fractions(self.grid_options['m&s_options'], self.grid_options["verbosity"]) + print(ding) + + # Set the prev value back + self.grid_options['m&s_options'][prev_M1_value_ms] + + # we don't know what to do next + else: + msg = "Chosen value for the multiplicity fraction function is not known." + raise ValueError(msg) + + # TODO: add extra verbose logging + print("Multiplicity: {} multiplicity_fraction: {}".format(system_dict['multiplicity'], multiplicity_fraction_dict[system_dict['multiplicity']])) + + return multiplicity_fraction_dict[system_dict['multiplicity']] \ No newline at end of file diff --git a/binarycpython/utils/grid_options_defaults.py b/binarycpython/utils/grid_options_defaults.py index 707e0714d..52e8c7cf6 100644 --- a/binarycpython/utils/grid_options_defaults.py +++ b/binarycpython/utils/grid_options_defaults.py @@ -46,6 +46,8 @@ grid_options_defaults_dict = { "_zero_prob_stars_skipped": 0, "ensemble_factor_in_probability_weighted_mass": False, # Whether to multiply the ensemble results by 1/probability_weighted_mass "multiplicity_fraction_function": 0, # Which multiplicity fraction function to use. 0: None, 1: Arenou 2010, 2: Rhagavan 2010, 3: M&S 2017 + "_m&s_options": None, # Holds the M&S options. + "_loaded_ms_data": False, # Holds flag whether the M&S data is loaded into memory ########################## # Execution log: ########################## @@ -489,6 +491,8 @@ grid_options_descriptions = { "_zero_prob_stars_skipped": "Internal counter to track how many systems are skipped because they have 0 probability", "ensemble_factor_in_probability_weighted_mass": "Flag to multiply all the ensemble results with 1/probability_weighted_mass", "multiplicity_fraction_function": "Which multiplicity fraction function to use. 0: None, 1: Arenou 2010, 2: Rhagavan 2010, 3: M&S 2017", + "_m&s_options": "Internal variable that holds the M&S options. Don't write to this your self", + "_loaded_ms_data": "Internal variable storing whether the M&S data has been loaded into memory", } ### -- GitLab