diff --git a/binarycpython/utils/grid.py b/binarycpython/utils/grid.py index 3c3a9c4167b88eba3aab5735de55c650f5f74243..fdab898bc12d963a52903b7019b2705004786c01 100644 --- a/binarycpython/utils/grid.py +++ b/binarycpython/utils/grid.py @@ -433,105 +433,14 @@ class Population(object): # Evolution functions ################################################### - def evolve_single(self, parse_function=None, clean_up_custom_logging_files=True): - """ - Function to run a single system - - The output of the run gets returned, unless a parse function is given to this function. + def setup(self): """ + Function to set up the necessary stuff for the population evolution: + # TODO: Make other kinds of populations possible. i.e, read out type of grid, and set up accordingly - ### Custom logging code: - self.set_custom_logging() - - # Get argument line - argline = self.return_argline(self.bse_options) - print('Running {}'.format(argline)) - # Run system - out = binary_c_python_api.run_system( - argline, - self.grid_options["custom_logging_func_memaddr"], - self.grid_options["store_memaddr"], - ) # Todo: change this to run_binary again but then build in checks in binary + # TODO: make this function more general. Have it explicitly set the system_generator function - # TODO: add call to function that cleans up the temp customlogging dir, and unloads the loaded libraries. - # TODO: make a switch to turn this off - if clean_up_custom_logging_files: - self.clean_up_custom_logging(evol_type="single") - - # Parse - if parse_function: - parse_function(self, out) - else: - return out - - def evolve_population(self, parse_function, custom_arg_file=None): """ - The function that will evolve the population. This function contains many steps - - TODO: fix the verbosity in this function when this function is finished - """ - - ### Custom logging code: - self.set_custom_logging() - - ### Load store - if self.grid_options["verbose"] > 0: - print("loading binary_c store information") - self.grid_options["store_memaddr"] = binary_c_python_api.return_store("") - - # Execute. - # TODO: CHange this part alot. This is not finished whatsoever - out = binary_c_python_api.run_population( - self.return_argline(), - self.grid_options["custom_logging_func_memaddr"], - self.grid_options["store_memaddr"], - ) - - print(out) - - ### Arguments - # If user inputs a file containing arg lines then use that - if custom_arg_file: - # check if file exists - if os.path.isfile(custom_arg_file): - # load file - with open(custom_arg_file) as f: - # Load lines into list - temp = f.read().splitlines() - - # Filter out all the lines that dont start with binary_c - population_arglines = [ - line for line in temp if line.startswith("binary_c") - ] - - else: - # generate population from options - - pass - - quit() - - ####### - # Do stuff - for line in population_arglines: - print(line) - - pass - - # TODO: add call to function that cleans up the temp customlogging dir, and unloads the loaded libraries. - - ################################################### - # Testing functions - ################################################### - - def test_evolve_population_lin(self): - """ - Test function to evolve a population in a linear way. - - returns total time spent on the actual interfacing with binaryc - """ - - import time ####################### ### Custom logging code: @@ -550,304 +459,170 @@ class Population(object): self.dry_run() - total_starcount_run = self.grid_options['total_starcount'] - print("Total starcount for this run will be: {}".format(total_starcount_run)) + print("Total starcount for this run will be: {}".format(self.grid_options['total_starcount'])) ####################### - # Linear run - start_lin = time.time() - + # Reset values and prepare the grid function self.grid_options['probtot'] = 0 # To make sure that the values are reset. TODO: fix this in a cleaner way - + self.grid_options['start_time_grid'] = time.time() # Setting start time of grid + + # self.generate_grid_code(dry_run=False) + # self.load_grid_function() - for i, system in enumerate(self.grid_options["system_generator"](self)): - full_system_dict = self.bse_options.copy() - full_system_dict.update(system) - - binary_cmdline_string = self.return_argline(full_system_dict) - out = binary_c_python_api.run_population( - binary_cmdline_string, - self.grid_options["custom_logging_func_memaddr"], - self.grid_options["store_memaddr"], - ) - print("{}/{}".format(i+1, total_starcount_run), binary_cmdline_string) - - stop_lin = time.time() - print( - "Without mp: {} systems took {}s".format(total_starcount_run, stop_lin-start_lin)) - - return stop_lin-start_lin - - - def test_evolve_population_mp(self): + def cleanup(self): """ - Test function to evolve a population in a parallel way. - - returns total time spent on the actual interfacing with binaryc + Function that handles all the cleaning up after the grid has been generated and/or run + + - reset values to 0 + - remove grid file + - unload grid function/module + - remove dry grid file + - unload dry grid function/module """ - import time - import multiprocessing as mp - from pathos.multiprocessing import ProcessingPool as Pool - - ####################### - ### Custom logging code: - self.set_custom_logging() - - ### Load store - self.grid_options["store_memaddr"] = binary_c_python_api.return_store("") - - ####################### - # Dry run and getting starcount + # Reset values + self.grid_options['count'] = 0 self.grid_options['probtot'] = 0 + self.grid_options['system_generator'] = None - self.generate_grid_code(dry_run=True) + # Remove files + # Unload functions - self.load_grid_function() - self.dry_run() - - total_starcount_run = self.grid_options['total_starcount'] - print("Total starcount for this run will be: {}".format(total_starcount_run)) - - ####################### - # MP run - self.grid_options['probtot'] = 0 # To make sure that the values are reset. TODO: fix this in a cleaner way - - start_mp = time.time() - - self.generate_grid_code(dry_run=False) - - self.load_grid_function() + def evolve_system_mp(self, binary_cmdline_string): + """ + Function that the multiprocessing evolution method calls to evolve a system + """ - def evolve_system(binary_cmdline_string): - # print(binary_cmdline_string) - # pass - # print('next') - # self.set_bse_option("M_1", mass) - # out = binary_c_python_api.run_population( - # binary_cmdline_string, - # self.grid_options["custom_logging_func_memaddr"], - # self.grid_options["store_memaddr"], - # ) - pass - # # parse_function(self, out) + out = binary_c_python_api.run_population( + binary_cmdline_string, + self.grid_options["custom_logging_func_memaddr"], + self.grid_options["store_memaddr"], + ) + if self.grid_options['parse_function']: + self.grid_options['parse_function'](self, out) - def yield_system(): - for i, system in enumerate(self.grid_options["system_generator"](self)): - full_system_dict = self.bse_options.copy() - full_system_dict.update(system) - binary_cmdline_string = self.return_argline(full_system_dict) - # print("{}/{}".format(i+1, total_starcount_run), binary_cmdline_string) - yield binary_cmdline_string - # yield i - print("generator done") + def yield_system_mp(self): + """ + Function that the multiprocessing evolution method calls to yield systems + """ - # Create pool - p = Pool(nodes=self.grid_options["amt_cores"]) + for i, system in enumerate(self.grid_options["system_generator"](self)): + full_system_dict = self.bse_options.copy() + full_system_dict.update(system) - # Execute - r = list(p.imap(evolve_system, yield_system())) + binary_cmdline_string = self.return_argline(full_system_dict) - stop_mp = time.time() + self.print_info(i+1, self.grid_options['total_starcount'], full_system_dict) + yield binary_cmdline_string - # Give feedback - print( - "with mp: {} systems took {}s using {} cores".format( - self.grid_options['total_starcount'], - stop_mp - start_mp, - self.grid_options["amt_cores"], - ) - ) + print("generator done") - return stop_mp - start_mp - def test_evolve_population_mp_chunks(self): + def evolve_single(self, parse_function=None, clean_up_custom_logging_files=True): """ - Test function to evolve a population in a parallel way. - - returns total time spent on the actual interfacing with binaryc + Function to run a single system + + The output of the run gets returned, unless a parse function is given to this function. """ - import time - import multiprocessing as mp - # from pathos.multiprocessing import ProcessingPool as Pool - from pathos.pools import _ProcessPool as Pool - ####################### ### Custom logging code: self.set_custom_logging() - ### Load store - self.grid_options["store_memaddr"] = binary_c_python_api.return_store("") - - ####################### - # Dry run and getting starcount - self.grid_options['probtot'] = 0 - - self.generate_grid_code(dry_run=True) - - self.load_grid_function() - - self.dry_run() - - total_starcount_run = self.grid_options['total_starcount'] - print("Total starcount for this run will be: {}".format(total_starcount_run)) - - ####################### - # MP run - self.grid_options['probtot'] = 0 # To make sure that the values are reset. TODO: fix this in a cleaner way - - start_mp = time.time() - - self.generate_grid_code(dry_run=False) - - self.load_grid_function() - - def evolve_system(binary_cmdline_string): + # Get argument line + argline = self.return_argline(self.bse_options) + print('Running {}'.format(argline)) + # Run system + out = binary_c_python_api.run_system( + argline, + self.grid_options["custom_logging_func_memaddr"], + self.grid_options["store_memaddr"], + ) # Todo: change this to run_binary again but then build in checks in binary + # TODO: add call to function that cleans up the temp customlogging dir, and unloads the loaded libraries. + # TODO: make a switch to turn this off + if clean_up_custom_logging_files: + self.clean_up_custom_logging(evol_type="single") - # print(binary_cmdline_string) - # pass - # print('next') - # self.set_bse_option("M_1", mass) - out = binary_c_python_api.run_population( - binary_cmdline_string, - self.grid_options["custom_logging_func_memaddr"], - self.grid_options["store_memaddr"], - ) + # Parse + if parse_function: + return parse_function(self, out) + else: + return out - parse_function(self, out) - # pass - def yield_system(): - for i, system in enumerate(self.grid_options["system_generator"](self)): - full_system_dict = self.bse_options.copy() - full_system_dict.update(system) + def evolve_population_mp(self): + """ + Function to evolve the population with multiprocessing approach. Using pathos to be able to include class-owned functions. + """ - binary_cmdline_string = self.return_argline(full_system_dict) - # print("{}/{}".format(i+1, total_starcount_run), binary_cmdline_string) - yield binary_cmdline_string - # yield i - print("generator done") + import multiprocessing as mp + # from pathos.multiprocessing import ProcessingPool as Pool + from pathos.pools import _ProcessPool as Pool # Create pool p = Pool(processes=self.grid_options["amt_cores"]) # Execute # TODO: calculate the chunksize value based on: total starcount and cores used. - r = list(p.imap_unordered(evolve_system, yield_system(), chunksize=1000)) + r = list(p.imap_unordered(self.evolve_system_mp, self.yield_system_mp(), chunksize=20)) - stop_mp = time.time() - - # Give feedback - print( - "with mp: {} systems took {}s using {} cores".format( - self.grid_options['total_starcount'], - stop_mp - start_mp, - self.grid_options["amt_cores"], - ) - ) + # Handle clean termination of the whole multiprocessing (making sure there are no zombie processes (https://en.wikipedia.org/wiki/Zombie_process)) + p.close() + p.join() - return stop_mp - start_mp - def evolve_population_mp_chunks(self): + def evolve_population_lin(self): """ - Test function to evolve a population in a parallel way. - - returns total time spent on the actual interfacing with binaryc + Function to evolve the population linearly (i.e. 1 core, no multiprocessing) """ - import time - import multiprocessing as mp - # from pathos.multiprocessing import ProcessingPool as Pool - from pathos.pools import _ProcessPool as Pool - ####################### - ### Custom logging code: - self.set_custom_logging() - - ### Load store - self.grid_options["store_memaddr"] = binary_c_python_api.return_store("") - - ####################### - # Dry run and getting starcount - self.grid_options['probtot'] = 0 - - self.generate_grid_code(dry_run=True) - - self.load_grid_function() - - self.dry_run() - - total_starcount_run = self.grid_options['total_starcount'] - print("Total starcount for this run will be: {}".format(total_starcount_run)) - - ####################### - # MP run - self.grid_options['probtot'] = 0 # To make sure that the values are reset. TODO: fix this in a cleaner way - self.grid_options['start_time_grid'] = time.time() # Setting start time of grid - - - start_mp = time.time() - - self.generate_grid_code(dry_run=False) - - self.load_grid_function() + for i, system in enumerate(self.grid_options["system_generator"](self)): + full_system_dict = self.bse_options.copy() + full_system_dict.update(system) - # def evolve_system(binary_cmdline_string): - def evolve_system(binary_cmdline_string): + binary_cmdline_string = self.return_argline(full_system_dict) out = binary_c_python_api.run_population( binary_cmdline_string, self.grid_options["custom_logging_func_memaddr"], self.grid_options["store_memaddr"], ) - if self.grid_options['parse_function']: - self.grid_options['parse_function'](self, out) + self.print_info(i+1, self.grid_options['total_starcount'], full_system_dict) - def yield_system(): - for i, system in enumerate(self.grid_options["system_generator"](self)): - full_system_dict = self.bse_options.copy() - full_system_dict.update(system) - - binary_cmdline_string = self.return_argline(full_system_dict) - - self.print_info(i+1, total_starcount_run, full_system_dict) - # print("{}/{}".format(i+1, total_starcount_run), binary_cmdline_string) - yield binary_cmdline_string - - print("generator done") - - # Create pool - p = Pool(processes=self.grid_options["amt_cores"]) - - # Execute - # TODO: calculate the chunksize value based on: total starcount and cores used. - r = list(p.imap_unordered(evolve_system, yield_system(), chunksize=20)) - - # Handle clean termination of the whole multiprocessing (making sure there are no zombie processes (https://en.wikipedia.org/wiki/Zombie_process)) - p.close() - p.join() - - stop_mp = time.time() - self.grid_options['start_time_grid'] = 0 + def evolve_population(self): + """ + Function to evolve populations. This is the main function. Handles the setting up, evolving and cleaning up of a population of stars. + """ - # Give feedback - print( - "with mp: {} systems took {}s using {} cores".format( - self.grid_options['total_starcount'], - stop_mp - start_mp, - self.grid_options["amt_cores"], - ) - ) + ## + # Prepare code/initialise grid. + # set custom logging, set up store_memaddr, build grid code. dry run grid code. + self.setup() + + ## + # Evolve systems: via grid_options one can choose to do this linearly, or multiprocessing method. + if self.grid_options['evolution_type'] in self.grid_options['evolution_type_options']: + if self.grid_options['evolution_type'] == 'mp': + self.evolve_population_mp() + elif self.grid_options['evolution_type'] == 'linear': + self.evolve_population_lin() + else: + print("Warning. you chose a wrong option for the grid evolution types. Please choose from the following: {}.".format(self.grid_options['evolution_type_options'])) - return stop_mp - start_mp + ## + # Clean up code: remove files, unset values. + self.cleanup() + ################################################### + # Function to test evolution algorithms + ################################################### def test_evolve_single(self): """ @@ -898,6 +673,7 @@ class Population(object): print("Error while deleting file {}".format(file)) raise FileNotFoundError + def clean_up_custom_logging(self, evol_type): """ Function to clean up the custom logging. @@ -932,7 +708,6 @@ class Population(object): # TODO: make sure that these also work. not fully sure if necessary tho. whether its a single file, or a dict of files/memaddresses - def increment_probtot(self, prob): """ Function to add to the total probability @@ -940,6 +715,7 @@ class Population(object): self.grid_options["probtot"] += prob + def increment_count(self): """ Function to add to the total amount of stars @@ -1346,18 +1122,6 @@ class Population(object): with open(gridcode_filename, "w") as f: f.write(code_string) - def cleanup_grid(self): - """ - Function that handles all the cleaning up after the grid has been generated and/or run - - - reset values to 0 - - remove grid file - - unload grid function/module - - remove dry grid file - - unload dry grid function/module - """ - - pass def load_grid_function(self): """ @@ -1387,6 +1151,7 @@ class Population(object): if self.grid_options["verbose"] > 0: print("Grid code loaded") + def dry_run(self): """ Function to dry run the grid and know how many stars it will run diff --git a/binarycpython/utils/grid_options_defaults.py b/binarycpython/utils/grid_options_defaults.py index d37d6a1bd89784920d72c63d18273d60c6d6a345..529e9e23dd2decd9da3e93ff4435b94f8fd98200 100644 --- a/binarycpython/utils/grid_options_defaults.py +++ b/binarycpython/utils/grid_options_defaults.py @@ -7,6 +7,8 @@ grid_options_defaults_dict = { # general "amt_cores": 1, # total amount of cores used to evolve the population "verbose": 0, # Level of verbosity of the simulation + "evolution_type": 'mp', # Flag for type of population evolution + "evolution_type_options": ['mp', 'linear'], # available choices for type of population evolution # binary_c files "binary_c_executable": os.path.join( os.environ["BINARY_C"], "binary_c-config" diff --git a/examples/example_population.py b/examples/example_population.py index e3a9cab26203fdec33131e16cfaa895439c9f71a..63f88c62f1a88dd2ff2f03d0a7c7308c00bc75f2 100644 --- a/examples/example_population.py +++ b/examples/example_population.py @@ -146,12 +146,12 @@ example_pop.export_all_info() ## Executing a single system ## This uses the M_1 orbital period etc set with the set function -# output = example_pop.evolve_single() -# print(output) +output = example_pop.evolve_single() +print(output) ## Executing a population ## This uses the values generated by the grid_variables -example_pop.evolve_population_mp_chunks() # TODO: update this function call +# 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 diff --git a/src/binary_c_python_api.c b/src/binary_c_python_api.c index 903e60257c6ad610391f46721d7e36d6f14e03d8..68be4e74c8e83f7737f0ab1fd1f6ed06d968ada8 100644 --- a/src/binary_c_python_api.c +++ b/src/binary_c_python_api.c @@ -395,7 +395,7 @@ int return_arglines(char ** const buffer, stardata->preferences->batchmode = BATCHMODE_LIBRARY; /* List available arguments */ - // binary_c_list_args(stardata); + binary_c_list_args(stardata); /* get buffer pointer */ binary_c_buffer_info(stardata,buffer,nbytes); diff --git a/tests/population/grid_tests.py b/tests/population/grid_tests.py index e7e63f37c23b19f17aa6a05b22cd26736eec10b2..44c3e1f202416acd74a4ee5f04607fb09b301acf 100644 --- a/tests/population/grid_tests.py +++ b/tests/population/grid_tests.py @@ -290,7 +290,7 @@ test_pop.set( # ### # testing population: test_pop.set(verbose=1, - amt_cores=1, + amt_cores=2, binary=0, ) @@ -326,10 +326,12 @@ test_pop.add_grid_variable( ) test_pop.set(verbose=1, - amt_cores=1, + amt_cores=2, binary=0, + evolution_type='linear' ) - # test_pop.test_evolve_population_mp() -test_pop.evolve_population_mp_chunks() +# test_pop.evolve_population_mp_chunks() + +test_pop.evolve_population() \ No newline at end of file