diff --git a/binarycpython/utils/functions.py b/binarycpython/utils/functions.py index da2c5bb5ec3888b441286e8b70c8ad4b60ed137f..675441579f7d9638ea3ed5e5a91f912bcc824d39 100644 --- a/binarycpython/utils/functions.py +++ b/binarycpython/utils/functions.py @@ -134,8 +134,8 @@ def create_hdf5(data_dir, name): [file for file in content_data_dir if file.endswith("_settings.json")][0], ) - with open(settings_file, "r") as f: - settings_json = json.load(f) + with open(settings_file, "r") as settings_file: + settings_json = json.load(settings_file) # Create settings group settings_grp = f.create_group("settings") @@ -380,7 +380,7 @@ def get_arg_keys(): return get_defaults().keys() -def get_help(param_name, print_help=True, return_dict=False, fail_silently=False): +def get_help(param_name="", print_help=True, return_dict=False, fail_silently=False): """ Function that returns the help info for a given parameter. @@ -397,78 +397,82 @@ def get_help(param_name, print_help=True, return_dict=False, fail_silently=False return_dict: wether to return the help info dictionary """ - + available_arg_keys = get_arg_keys() - if param_name in available_arg_keys: - help_info = binary_c_python_api.return_help(param_name) - cleaned = [el for el in help_info.split("\n") if not el == ""] - - # Get line numbers - did_you_mean_nr = [ - i for i, el in enumerate(cleaned) if el.startswith("Did you mean") - ] - parameter_line_nr = [ - i for i, el in enumerate(cleaned) if el.startswith("binary_c help") - ] - default_line_nr = [ - i for i, el in enumerate(cleaned) if el.startswith("Default") - ] - macros_line_nr = [ - i for i, el in enumerate(cleaned) if el.startswith("Available") - ] - - help_info_dict = {} - - # Get alternatives - if did_you_mean_nr: - alternatives = cleaned[did_you_mean_nr[0] + 1 : parameter_line_nr[0]] - alternatives = [el.strip() for el in alternatives] - help_info_dict["alternatives"] = alternatives - - # Information about the parameter - parameter_line = cleaned[parameter_line_nr[0]] - parameter_name = parameter_line.split(":")[1].strip().split(" ")[0] - parameter_value_input_type = ( - " ".join(parameter_line.split(":")[1].strip().split(" ")[1:]) - .replace("<", "") - .replace(">", "") - ) + if not param_name: + print("Please set the param_name to any of the following:\n {}".format(sorted(available_arg_keys))) + return None + else: + if param_name in available_arg_keys: + help_info = binary_c_python_api.return_help(param_name) + cleaned = [el for el in help_info.split("\n") if not el == ""] + + # Get line numbers + did_you_mean_nr = [ + i for i, el in enumerate(cleaned) if el.startswith("Did you mean") + ] + parameter_line_nr = [ + i for i, el in enumerate(cleaned) if el.startswith("binary_c help") + ] + default_line_nr = [ + i for i, el in enumerate(cleaned) if el.startswith("Default") + ] + macros_line_nr = [ + i for i, el in enumerate(cleaned) if el.startswith("Available") + ] + + help_info_dict = {} + + # Get alternatives + if did_you_mean_nr: + alternatives = cleaned[did_you_mean_nr[0] + 1 : parameter_line_nr[0]] + alternatives = [el.strip() for el in alternatives] + help_info_dict["alternatives"] = alternatives + + # Information about the parameter + parameter_line = cleaned[parameter_line_nr[0]] + parameter_name = parameter_line.split(":")[1].strip().split(" ")[0] + parameter_value_input_type = ( + " ".join(parameter_line.split(":")[1].strip().split(" ")[1:]) + .replace("<", "") + .replace(">", "") + ) - help_info_dict["parameter_name"] = parameter_name - help_info_dict["parameter_value_input_type"] = parameter_value_input_type + help_info_dict["parameter_name"] = parameter_name + help_info_dict["parameter_value_input_type"] = parameter_value_input_type - description_line = " ".join( - cleaned[parameter_line_nr[0] + 1 : default_line_nr[0]] - ) - help_info_dict["description"] = description_line + description_line = " ".join( + cleaned[parameter_line_nr[0] + 1 : default_line_nr[0]] + ) + help_info_dict["description"] = description_line - # Default: - default_line = cleaned[default_line_nr[0]] - default_value = default_line.split(":")[-1].strip() + # Default: + default_line = cleaned[default_line_nr[0]] + default_value = default_line.split(":")[-1].strip() - help_info_dict["default"] = default_value + help_info_dict["default"] = default_value - # Get Macros: - if macros_line_nr: - macros = cleaned[macros_line_nr[0] + 1 :] - help_info_dict["macros"] = macros + # Get Macros: + if macros_line_nr: + macros = cleaned[macros_line_nr[0] + 1 :] + help_info_dict["macros"] = macros - if print_help: - for key in help_info_dict.keys(): - print("{}:\n\t{}".format(key, help_info_dict[key])) + if print_help: + for key in help_info_dict.keys(): + print("{}:\n\t{}".format(key, help_info_dict[key])) - if return_dict: - return help_info_dict + if return_dict: + return help_info_dict - else: - if not fail_silently: - print( - "{} is not a valid parameter name. Please choose from the following parameters:\n\t{}".format( - param_name, list(available_arg_keys) + else: + if not fail_silently: + print( + "{} is not a valid parameter name. Please choose from the following parameters:\n\t{}".format( + param_name, list(available_arg_keys) + ) ) - ) - return None + return None def run_system(**kwargs): diff --git a/binarycpython/utils/grid.py b/binarycpython/utils/grid.py index 7f9b6754a0dade92699ce4b78478f2dd6461b88c..3c3a9c4167b88eba3aab5735de55c650f5f74243 100644 --- a/binarycpython/utils/grid.py +++ b/binarycpython/utils/grid.py @@ -351,6 +351,16 @@ class Population(object): include_binary_c_help_all=include_binary_c_help_all, ) + # Copy dict + all_info_cleaned = copy.deepcopy(all_info) + + + # Clean the all_info_dict: (i.e. transform the function objects to strings) + if all_info_cleaned.get('population_settings', None): + if all_info_cleaned['population_settings']['grid_options']['parse_function']: + all_info_cleaned['population_settings']['grid_options']['parse_function'] = str(all_info_cleaned['population_settings']['grid_options']['parse_function']) + + if use_datadir: base_name = os.path.splitext(self.custom_options["base_filename"])[0] settings_name = base_name + "_settings.json" @@ -366,14 +376,14 @@ class Population(object): print("Writing settings to {}".format(settings_fullname)) # if not outfile.endswith('json'): with open(settings_fullname, "w") as f: - f.write(json.dumps(all_info, indent=4)) + f.write(json.dumps(all_info_cleaned, indent=4)) else: if self.grid_options["verbose"] > 0: print("Writing settings to {}".format(outfile)) # if not outfile.endswith('json'): with open(outfile, "w") as f: - f.write(json.dumps(all_info, indent=4)) + f.write(json.dumps(all_info_cleaned, indent=4)) def set_custom_logging(self): """ @@ -978,6 +988,7 @@ class Population(object): code_string += "import numpy as np\n" code_string += "from binarycpython.utils.distribution_functions import *\n" code_string += "from binarycpython.utils.spacing_functions import *\n" + code_string += "from binarycpython.utils.useful_funcs import *\n" code_string += "\n\n" # Make the function @@ -1123,7 +1134,7 @@ class Population(object): code_string += ( indent * (depth + 1) + "{}".format( - grid_variable["precode"].replace("\n", "\n" + indent * (depth + 1)) + grid_variable["precode"].replace("\n", "\n" + indent * (depth)) ) + "\n" ) @@ -1440,9 +1451,9 @@ class Population(object): # Define frequency if self.grid_options['verbose'] == 1: - print_freq = 10 - else: print_freq = 1 + else: + print_freq = 10 # Calculate amount of time left diff --git a/examples/example_population.py b/examples/example_population.py index 4ee06d7d7bd669dd4b9f19bb02a3180059597723..e3a9cab26203fdec33131e16cfaa895439c9f71a 100644 --- a/examples/example_population.py +++ b/examples/example_population.py @@ -6,13 +6,53 @@ import os from binarycpython.utils.grid import Population from binarycpython.utils.functions import get_help_all, get_help, create_hdf5 - +from binarycpython.utils.custom_logging_functions import temp_dir ######################################################### # This file serves as an example for running a population. # The use of help(<function>) is a good way to inspect what parameters are there to use ######################################################### + +## Quick script to get some output +def output_lines(output): + """ + Function that outputs the lines that were recieved from the binary_c run. + """ + return output.splitlines() + +def parse_function(self, output): + # extract info from the population instance + # TODO: think about whether this is smart. Passing around this object might be an overkill + + # Get some information from the + data_dir = self.custom_options['data_dir'] + base_filename = self.custom_options['base_filename'] + + # Check directory, make if necessary + os.makedirs(data_dir, exist_ok=True) + + seperator = ' ' + + # Create filename + outfilename = os.path.join(data_dir, base_filename) + + # Go over the output. + for el in output_lines(output): + headerline = el.split()[0] + + # CHeck the header and act accordingly + if (headerline=='MY_STELLAR_DATA'): + parameters = ['time', 'mass', 'zams_mass', 'probability', 'radius'] + values = el.split()[1:] + + if not os.path.exists(outfilename): + with open(outfilename, 'w') as f: + f.write(seperator.join(parameters)+'\n') + + with open(outfilename, 'a') as f: + f.write(seperator.join(values)+'\n') + # Create population object example_pop = Population() @@ -24,48 +64,72 @@ example_pop.set(verbose=1) # Those that are present in the default grid_options are set in grid_options # All other values that you set are put in a custom_options dict example_pop.set( - # + # binary_c physics options M_1=10, # bse_options separation=0, # bse_options - orbital_period=4580, # bse_options + orbital_period=45000000080, # bse_options max_evolution_time=15000, # bse_options eccentricity=0.02, # bse_options - # + + # grid_options amt_cores=1, # grid_options - # + verbose=1, # verbosity. Not fully configured correctly yet but having it value of 1 prints alot of stuff + parse_function=parse_function, # Setting the parse function thats used in the evolve_population + + # Custom options # TODO: need to be set in grid_options probably data_dir=os.path.join( - os.environ["BINARYC_DATA_ROOT"], "example_python" + temp_dir(), "example_python_population_result" ), # custom_options base_filename="example_pop.dat", # custom_options ) -# Custom logging -# TODO: show different ways -example_pop.set( - C_auto_logging={ - "MY_HEADER_LINE": ["star[0].mass", "star[1].mass", "model.probability"] - } -) +### Custom logging + +## Below example requires changing the parse function +## very simple example of custom logging. Will work but need to change the parse function to handle that nicely. +# example_pop.set( +# C_auto_logging={ +# "MY_HEADER_LINE": ["star[0].mass", "star[1].mass", "model.probability"] +# } +# ) + + +# Log the moment when the star turns into a hertzsprung-gap example_pop.set( - C_logging_code='Printf("MY_STELLAR_DATA time=%g mass=%g radius=%g\\n", stardata->model.time, stardata->star[0].mass, stardata->star[0].radius);' -) + C_logging_code=""" +if(stardata->star[0].stellar_type >= 2) +{ + if (stardata->model.time < stardata->model.max_evolution_time) + { + Printf("MY_STELLAR_DATA %30.12e %g %g %g %g\\n", + // + stardata->model.time, // 1 + stardata->star[0].mass, //2 + stardata->star[0].pms_mass, //4 + stardata->model.probability, //5 + stardata->star[0].radius // 6 + ); + }; + /* Kill the simulation to save time */ + stardata->model.max_evolution_time = stardata->model.time - stardata->model.dtm; +}; +""") -# TODO: show when reading from file -example_pop.set_custom_logging() # TODO: remove this one +# Add grid variables +resolution = {'M_1': 100} -# Adding grid variables: +# Mass example_pop.add_grid_variable( name="lnm1", - longname="log primary mass", - valuerange=[10, 20], - resolution="10", - spacingfunc="np.linspace(0.213, 10.2, 10)", + longname="Primary mass", + valuerange=[2, 150], + resolution="{}".format(resolution['M_1']), + spacingfunc="const(math.log(2), math.log(150), {})".format(resolution['M_1']), precode="M_1=math.exp(lnm1)", - probdist="flat(M_1)", - # probdist='self.custom_options["extra_prob_function"](M_1)', - dphasevol="", + probdist="three_part_powerlaw(M_1, 0.1, 0.5, 1.0, 150, -1.3, -2.3, -2.3)*M_1", + dphasevol="dlnm1", parameter_name="M_1", - condition="", + condition="", # Impose a condition on this grid variable. Mostly for a check for yourself ) # Exporting of all the settings can be done with .export_all_info() @@ -80,13 +144,16 @@ example_pop.export_all_info() # Creating a parsing function # TODO: add example of setting up a parsing function -# Executing a single system -# TODO: add example of running a single system +## Executing a single system +## This uses the M_1 orbital period etc set with the set function +# output = example_pop.evolve_single() +# print(output) -# Executing a population -# TODO: add example of running a population +## Executing a population +## This uses the values generated by the grid_variables +example_pop.evolve_population_mp_chunks() # TODO: update this function call # Wrapping up the results to an hdf5 file can be done by using the create_hdf5(<directory containing data and settings>) # This function takes the settings file (ending in _settings.json) and the data files (ending in .dat) from the data_dir # and packing them into an hdf5 file, which is then written into the same data_dir directory -create_hdf5(example_pop.custom_options["data_dir"]) +create_hdf5(data_dir=example_pop.custom_options["data_dir"], name='example_pop.hdf5') \ No newline at end of file