From 1b95c0eada5d7c420f661dd3e04673000192fcec Mon Sep 17 00:00:00 2001 From: Robert Izzard <r.izzard@surrey.ac.uk> Date: Wed, 24 Nov 2021 14:51:31 +0000 Subject: [PATCH] fix cmdline parsing of bool types update _const_dt to include a wrapper just before it's cached, allowing output to the screen for debugging other minor fixes --- binarycpython/utils/Moe_di_Stefano_2017.py | 6 +- binarycpython/utils/distribution_functions.py | 20 +++- binarycpython/utils/grid.py | 27 +++-- binarycpython/utils/grid_options_defaults.py | 3 + binarycpython/utils/gridcode.py | 43 ++++---- binarycpython/utils/spacing_functions.py | 99 ++++++++++++++++--- 6 files changed, 146 insertions(+), 52 deletions(-) diff --git a/binarycpython/utils/Moe_di_Stefano_2017.py b/binarycpython/utils/Moe_di_Stefano_2017.py index fdf507a64..0cfb63604 100644 --- a/binarycpython/utils/Moe_di_Stefano_2017.py +++ b/binarycpython/utils/Moe_di_Stefano_2017.py @@ -404,7 +404,7 @@ class Moe_di_Stefano_2017(): # # log-spaced m1 with given resolution self.add_grid_variable( - name="lnm1", + name="lnM_1", parameter_name="M_1", longname="Primary mass", samplerfunc=self.grid_options["Moe2017_options"]["samplerfuncs"]["M"][0] @@ -422,8 +422,8 @@ class Moe_di_Stefano_2017(): ), ], gridtype="centred", - dphasevol="dlnm1", - precode='M_1 = np.exp(lnm1); options["M_1"]=M_1', + dphasevol="dlnM_1", + precode='M_1 = np.exp(lnM_1); options["M_1"]=M_1', probdist="self.Moe_di_Stefano_2017_pdf({{{}, {}, {}}}, verbosity=self.grid_options['verbosity'])['total_probdens'] if multiplicity == 1 else 1".format( str(dict(self.grid_options["Moe2017_options"]))[1:-1], "'multiplicity': multiplicity", diff --git a/binarycpython/utils/distribution_functions.py b/binarycpython/utils/distribution_functions.py index 08f0178a4..89abfde88 100644 --- a/binarycpython/utils/distribution_functions.py +++ b/binarycpython/utils/distribution_functions.py @@ -401,10 +401,20 @@ class distribution_functions(): ##### def Kroupa2001(self, - m: Union[int, float], newopts: dict = None) -> Union[int, float]: + m: Union[int, float], + newopts: dict = None) -> Union[int, float]: """ - Probability distribution function for Kroupa 2001 IMF, where the default values to the - three_part_powerlaw are: default = {"m0": 0.1, "m1": 0.5, "m2": 1, "mmax": 100, "p1": -1.3, "p2": -2.3,"p3": -2.3} + Probability distribution function for Kroupa 2001 IMF, + where the default values to the three_part_powerlaw are: + default = { + "m0": 0.1, + "m1": 0.5, + "m2": 1, + "mmax": 100, + "p1": -1.3, + "p2": -2.3, + "p3": -2.3 + } Args: m: mass to evaluate the distribution at @@ -1799,7 +1809,9 @@ class distribution_functions(): prob_dict["M_1"] = M1_probability verbose_print( "\tMoe_di_Stefano_2017_pdf: Appended Mass (m={}) probability ({}) to the prob dict ({})".format( - options["M_1"], prob_dict["M_1"], prob_dict + options["M_1"], + prob_dict["M_1"], + prob_dict ), verbosity, _MOE2017_VERBOSITY_LEVEL, diff --git a/binarycpython/utils/grid.py b/binarycpython/utils/grid.py index f42357b68..d2ebfea79 100644 --- a/binarycpython/utils/grid.py +++ b/binarycpython/utils/grid.py @@ -33,6 +33,7 @@ import psutil import queue import setproctitle import signal +import str2bool import subprocess import sys import time @@ -247,7 +248,7 @@ class Population(analytics, print("exit from binary_c-python Population with code {}".format(self.grid_options['exit_code'])) if flush: sys.stdout.flush() - if stacktrace: + if stacktrace or self.grid_options['print_stack_on_exit']: traceback.print_stack() sys.exit(self.grid_options['exit_code']) @@ -373,7 +374,6 @@ class Population(analytics, split = cmdline_arg.split("=") if len(split)==2: - parameter = split[0] value = split[1] old_value_found = False @@ -391,7 +391,7 @@ class Population(analytics, old_value = self.custom_options[parameter] old_value_found = True - # (attempt to) convert + # (attempt to) convert type if old_value_found: if old_value != None: try: @@ -400,10 +400,13 @@ class Population(analytics, parameter, type(value), type(old_value) ), self.grid_options["verbosity"], - 2, + 1, ) try: - value = type(old_value)(value) + if isinstance(old_value, bool): + value = str2bool.str2bool(value) + else: + value = type(old_value)(value) self.verbose_print("Success!", self.grid_options["verbosity"], 2) except Exception as e: print("Failed to convert {param} value with type {type}: old_value is '{old}', new value is '{new}', {e}".format( @@ -434,6 +437,13 @@ class Population(analytics, 0, ) # Add to dict + self.verbose_print( + "setting {} = {} ".format( + parameter, value + ), + self.grid_options["verbosity"], + 1, + ) cmdline_dict[parameter] = value else: @@ -612,7 +622,7 @@ class Population(analytics, # open locked settings file, then output if we get the lock (f,lock) = self.locked_open_for_write(settings_fullname) - + if lock and f: self.verbose_print( "Writing settings to {}".format(settings_fullname), @@ -1945,11 +1955,9 @@ class Population(analytics, self._generate_grid_code(dry_run=True) # Load the grid code - print("load grid") self._load_grid_function() # Do a dry run - print("Doing dry run") self._dry_run() self.verbose_print( @@ -1966,6 +1974,7 @@ class Population(analytics, 0, ) if self.grid_options["exit_after_dry_run"]: + print("Exiting after dry run {}".format(self.grid_options["exit_after_dry_run"])) self.exit(code=0) ####################### @@ -2054,7 +2063,7 @@ class Population(analytics, Requires the grid to be built as a dry run grid """ - self.verbose_print("Dry run of the grid", self.grid_options["verbosity"], 1) + self.verbose_print("Doing a dry run of the grid.", self.grid_options["verbosity"], 1) system_generator = self.grid_options["_system_generator"] total_starcount = system_generator(self) self.grid_options["_total_starcount"] = total_starcount diff --git a/binarycpython/utils/grid_options_defaults.py b/binarycpython/utils/grid_options_defaults.py index 1b3327cc1..33dee5f0f 100644 --- a/binarycpython/utils/grid_options_defaults.py +++ b/binarycpython/utils/grid_options_defaults.py @@ -57,6 +57,7 @@ class grid_options_defaults(): "do_dry_run": True, # Whether to do a dry run to calculate the total probability for this run "custom_generator": None, # Place for the custom system generator "exit_after_dry_run": False, # Exit after dry run? + "print_stack_on_exit" : False, # print the stack trace on exit calls? ##################### # System information @@ -389,6 +390,8 @@ class grid_options_defaults(): "m&s_options": "Internal variable that holds the Moe and di Stefano (2017) options. Don't write to this your self", "_loaded_Moe2017_data": "Internal variable storing whether the Moe and di Stefano (2017) data has been loaded into memory", "do_dry_run": "Whether to do a dry run to calculate the total probability for this run", + "exit_after_dry_run": "If True, exits after a dry run. Default is False.", + "print_stack_on_exit" : "If True, prints a stack trace when the population's exit method is called.", "_Moe2017_JSON_data": "Location to store the loaded Moe&diStefano2017 dataset", # Stores the data } diff --git a/binarycpython/utils/gridcode.py b/binarycpython/utils/gridcode.py index edf4c6457..c6afbc3b3 100644 --- a/binarycpython/utils/gridcode.py +++ b/binarycpython/utils/gridcode.py @@ -117,7 +117,7 @@ class gridcode(): "# Grid code generated on {}\n".format(datetime.datetime.now().isoformat()), "# This function generates the systems that will be evolved with binary_c\n\n" # Set some values in the generated code: - "# Setting initial values\n", + "# Set initial values\n", "_total_starcount = 0\n", "starcounts = [0 for i in range({})]\n".format(total_grid_variables + 1), "probabilities = {}\n", @@ -145,7 +145,7 @@ class gridcode(): "eccentricity3 = None\n", "\n", # Prepare the probability - "# setting probability lists\n", + "# set probability lists\n", ) for grid_variable_el in sorted( @@ -184,7 +184,7 @@ class gridcode(): self._add_code(grid_variable["topcode"]) ######################### - # Setting up the for loop + # Set up the for loop # Add comment for for loop self._add_code( "# for loop for variable {name} gridtype {gridtype}".format( @@ -406,7 +406,7 @@ class gridcode(): self._add_code("\n") ######################### - # Setting up pre-code and value in some cases + # Set up pre-code and value in some cases # Add pre-code if grid_variable["precode"]: self._add_code( @@ -430,14 +430,14 @@ class gridcode(): # Calculate probability self._add_code( "\n", - "# Setting probabilities\n", - "d{name} = dphasevol_{name} * ({probdist})".format( + "# Set probabilities\n", + "dprob_{name} = dphasevol_{name} * ({probdist})".format( name=grid_variable["name"], probdist=grid_variable["probdist"], ) + "\n", # Save probability sum - "probabilities_sum[{n}] += d{name}".format( + "probabilities_sum[{n}] += dprob_{name}".format( n=grid_variable["grid_variable_number"], name=grid_variable["name"] ) @@ -446,11 +446,11 @@ class gridcode(): if grid_variable["grid_variable_number"] == 0: self._add_code( - "probabilities_list[0] = d{name}".format(name=grid_variable["name"]) + "\n" + "probabilities_list[0] = dprob_{name}".format(name=grid_variable["name"]) + "\n" ) else: self._add_code( - "probabilities_list[{this}] = probabilities_list[{prev}] * d{name}".format( + "probabilities_list[{this}] = probabilities_list[{prev}] * dprob_{name}".format( this=grid_variable["grid_variable_number"], prev=grid_variable["grid_variable_number"] - 1, name=grid_variable["name"], @@ -568,7 +568,7 @@ class gridcode(): self._add_code("\n") ############################### - # Finalising print statements + # Finalise print statements # self._increment_indent_depth(+1) self._add_code("\n", "#" * 40 + "\n", "if print_results:\n") @@ -578,7 +578,7 @@ class gridcode(): ) ################ - # Finalising return statement for dry run. + # Finalise return statement for dry run. # if dry_run: self._add_code("return _total_starcount\n") @@ -589,7 +589,7 @@ class gridcode(): # Save the grid code to the grid_options self.verbose_print( - "Saving grid code to grid_options", self.grid_options["verbosity"], 1 + "Save grid code to grid_options", self.grid_options["verbosity"], 1 ) self.grid_options["code_string"] = self.code_string @@ -600,7 +600,7 @@ class gridcode(): self.grid_options["gridcode_filename"] = gridcode_filename self.verbose_print( - "{blue}Writing grid code to {file} [dry_run = {dry}]{reset}".format( + "{blue}Write grid code to {file} [dry_run = {dry}]{reset}".format( blue=self.ANSI_colours["blue"], file=gridcode_filename, dry=dry_run, @@ -643,8 +643,9 @@ class gridcode(): self, grid_variable, dry_run, branchpoint, branchcode ): ################################################################################# - # Here are the calls to the queuing or other solution. this part is for every system - # Add comment + # Here are the calls to the queue or other solution. + # this part is for every system + # self._increment_indent_depth(+1) self._add_code("#" * 40 + "\n") @@ -695,7 +696,7 @@ class gridcode(): ) if not dry_run: - # Handling of what is returned, or what is not. + # Handle what is returned, or what is not. self._add_code("yield(parameter_dict)\n", indent=1) # If its a dry run, dont do anything with it @@ -718,7 +719,7 @@ class gridcode(): # Code to load the self.verbose_print( - message="Loading grid code function from {file}".format( + message="Load grid code function from {file}".format( file=self.grid_options["gridcode_filename"] ), verbosity=self.grid_options["verbosity"], @@ -877,7 +878,7 @@ class gridcode(): the rest of the function Examples: - name = 'lnm1' + name = 'lnM_1' parameter_name: name of the parameter in binary_c @@ -909,10 +910,10 @@ class gridcode(): precode: Extra room for some code. This code will be evaluated within the loop of the - sampling function (i.e. a value for lnm1 is chosen already) + sampling function (i.e. a value for lnM_1 is chosen already) Examples: - precode = 'M_1=math.exp(lnm1);' + precode = 'M_1=math.exp(lnM_1);' postcode: Code executed after the probability is calculated. probdist: @@ -924,7 +925,7 @@ class gridcode(): part of the parameter space that the total probability is calculated with. Put to -1 if you want to ignore any dphasevol calculations and set the value to 1 Examples: - dphasevol = 'dlnm1' + dphasevol = 'dlnM_1' condition: condition that has to be met in order for the grid generation to continue Examples: diff --git a/binarycpython/utils/spacing_functions.py b/binarycpython/utils/spacing_functions.py index bc3d53288..4b7fe6465 100644 --- a/binarycpython/utils/spacing_functions.py +++ b/binarycpython/utils/spacing_functions.py @@ -67,9 +67,9 @@ class spacing_functions(): where step is int((int(max_bound)-int(min_bound))/steps) """ - step = int((int(max_bound)-int(min_bound))/(steps-1)) + step = int((int(max_bound)-int(min_bound))/max(1,steps-1)) if steps <= 1: - return int(min_bound) + return [int(min_bound)] else: return range(int(min_bound),int(max_bound+step),step) @@ -196,6 +196,7 @@ class spacing_functions(): logspacing: whether to use log-spaced time, in which case dt is actually d(log10(t)) tmin: the minimum time to consider (Myr, default 3.0 Myr) tmax: the maximum time to consider (Myr, default None which means we use the grid option 'max_evolution_time') + max_evolution_time: overrides bse_options['max_evolution_time'] if set mindm: a tuple of tuples containing a mass range and minimum mass spacing in that range. The default is ((0.07,1.0,0.1),(1.0,300.0,1.0)) allocated a minimum dm of 0.1Msun in the mass range 0.07 to 1.0 Msun and 1.0Msun in the range 1.0 to 300.0 Msun. Anything you set overrides this. Note, if you use only one tuple, you must set it with a trailing comma, thus, e.g. ((0.07,1.0,0.1),). (default None) maxdm: a list of tuples similar to mindm but specifying a maximum mass spacing. In the case of maxdm, if the third option in each tuple is negative it is treated as a log step (its absolute value is used as the step). (default None) fsample: a global sampling (Shannon-like) factor (<1) to improve resolution (default 1.0, set to smaller to improve resolution) @@ -225,10 +226,70 @@ class spacing_functions(): if cachedir == None: cachedir = self.grid_options['cache_dir'] + '/const_dt_cache' cache = Cache(cachedir) + + def _const_dt_wrapper(cachedir=None, + num_cores=None, + bse_options=None, + dt=1000.0, + dlogt=0.1, + mmin=0.07, + mmax=100.0, + nres=1000, + logspacing=False, + tmin=3.0, # start at 3Myr + tmax=None, # use max_evolution_time by default + max_evolution_time=None, + mindm=None, # tuple of tuples + maxdm=((0.07, 1.0, 0.1), (1.0, 300.0, 1.0)), # tuple of tuples + fsample=1.0, + factor=1.0, + logmasses=False, + log10masses=False, + showlist=False, + showtable=False, + usecache=True, + ): + print("call _const_dt num_cores={} dt={} dlogt={} mmin={} mmax={} nres={} logspacing={} tmin={} mindm={} maxdm={} fsample={} factor={} logmasses={} log10masses={}".format( + num_cores,dt,dlogt,mmin,mmax,nres,logspacing,tmin,mindm,maxdm,fsample,factor,logmasses,log10masses)) + + + # strip bse_options of options that will not affect + # _const_dt + bse_stripped = bse_options.copy() + for x in ['multiplicity']: + del bse_stripped[x] + + # make a JSON string of the options (this can be + # used to check the cache) + bse_options_json = json.dumps(bse_stripped, + sort_keys=True, + ensure_ascii=False) + + return _const_dt(cachedir=cachedir, + num_cores=num_cores, + bse_options_json=bse_options_json, + dt=dt, + dlogt=dlogt, + mmin=mmin, + mmax=mmax, + nres=nres, + logspacing=logspacing, + tmin=tmin, + tmax=tmax, + max_evolution_time=max_evolution_time, + mindm=mindm, + maxdm=maxdm, + fsample=fsample, + logmasses=logmasses, + log10masses=log10masses, + showlist=showlist, + showtable=showtable, + usecache=usecache) + @cache.memoize() # memoize to disc - def _const_dt(cachedir, - num_cores, - bse_options, + def _const_dt(cachedir=None, + num_cores=None, + bse_options_json=None, # JSON string dt=1000.0, dlogt=0.1, mmin=0.07, @@ -237,6 +298,7 @@ class spacing_functions(): logspacing=False, tmin=3.0, # start at 3Myr tmax=None, # use max_evolution_time by default + max_evolution_time=None, mindm=None, # tuple of tuples maxdm=((0.07, 1.0, 0.1), (1.0, 300.0, 1.0)), # tuple of tuples fsample=1.0, @@ -247,16 +309,18 @@ class spacing_functions(): showtable=False, usecache=True, ): - print("_const_dt num_cores={} dt={} dlogt={} mmin={} mmax={} nres={} logscpaing={} tmin={} mindm={} maxdm={} fsample={} factor={} logmasses={} log10masses={}".format( - num_cores,dt,dlogt,mmin,mmax,nres,logspacing,tmin,mindm,maxdm,fsample,factor,logmasses,log10masses)) # first thing to do is make a stellar lifetime table # - # we should use the bse_options passed in + # we should use the bse_options_json passed in # so our lifetime_population uses the same physics # as the main grid # convert bse_options to dict - bse_options = json.loads(bse_options) + bse_options = json.loads(bse_options_json) + + # perhaps override max_evolution_time + if max_evolution_time: + bse_options['max_evolution_time'] = max_evolution_time # make new population object from binarycpython.utils.grid import Population @@ -277,6 +341,12 @@ class spacing_functions(): slurm=0, condor=0, multiplicity=1, + ensemble=0, + ensemble_dt=1e3, + ensemble_logdt=0.1, + # for debugging + verbosity=1, + log_dt=1, ) # make a grid in M1 @@ -481,12 +551,11 @@ class spacing_functions(): # # Note: we send a sorted JSON string instead of the # bse_options dict to make sure the order is preserved - mass_list = _const_dt(cachedir, - self.grid_options['num_cores'], - json.dumps(self.bse_options, - sort_keys=True, - ensure_ascii=False), - **kwargs + + mass_list = _const_dt_wrapper(cachedir=cachedir, + num_cores=self.grid_options['num_cores'], + bse_options=self.bse_options, + **kwargs ) cache.close() -- GitLab