diff --git a/binarycpython/utils/grid.py b/binarycpython/utils/grid.py index 992e9f97c69a88eb0dca5931b2f6b0257b499de5..7a18c516be4102223fa4293461e7005753ac3139 100644 --- a/binarycpython/utils/grid.py +++ b/binarycpython/utils/grid.py @@ -23,6 +23,8 @@ Tasks: import os import gc import sys +from colorama import Fore,Back,Style,init as colorama_init + import copy import json import time @@ -93,7 +95,7 @@ from binarycpython.utils.distribution_functions import ( raghavan2010_binary_fraction, Moe_di_Stefano_2017_multiplicity_fractions, ) - +colorama_init() from binarycpython import _binary_c_bindings secs_per_day = 86400 # probably needs to go somewhere more sensible @@ -154,6 +156,18 @@ class Population: # shared memory used for logging self.shared_memory = {} + # variable to test if we're running in a shell + if sys.stdin and sys.stdin.isatty(): + self.in_shell = True + else: + self.in_shell = False + + # ANSI colours: use them if in a shell + self.ANSI_colours = self._ANSI_colours() + if self.in_shell == False: + for c in self.ANSI_colours: + self.ANSI_colours[c] = "" + # Set global (OS) process id self.grid_options["_main_pid"] = os.getpid() @@ -255,7 +269,8 @@ class Population: def parse_cmdline(self) -> None: """ - Function to handle settings values via the command line. + Function to handle settings values via the command line in the form x=y, w=z, etc. + Best to be called after all the .set(..) lines, and just before the .evolve() is called If you input any known parameter (i.e. contained in grid_options, defaults/bse_options @@ -266,21 +281,14 @@ class Population: can change to ints. The value of any new parameter (which will go to custom_options) will be a string. - - Tasks: - - TODO: remove the need for --cmdline """ - parser = argparse.ArgumentParser() - parser.add_argument( - "--cmdline", - help='Setting values via the commandline. Input like --cmdline "metallicity=0.02"', - ) - args = parser.parse_args() + # get the cmd-line args in the form x=y + cmdline_args = sys.argv[1:] # How its set up now is that as input you need to give --cmdline "metallicity=0.002" # Its checked if this exists and handled accordingly. - if args.cmdline: + if cmdline_args: verbose_print( "Found cmdline args. Parsing them now", self.grid_options["verbosity"], @@ -288,17 +296,12 @@ class Population: ) # Grab the input and split them up, while accepting only non-empty entries - cmdline_args = args.cmdline + #cmdline_args = args self.grid_options["_commandline_input"] = cmdline_args - split_args = [ - cmdline_arg - for cmdline_arg in cmdline_args.split(" ") - if not cmdline_arg == "" - ] # Make dict and fill it cmdline_dict = {} - for cmdline_arg in split_args: + for cmdline_arg in cmdline_args: split = cmdline_arg.split("=") parameter = split[0] value = split[1] @@ -852,15 +855,20 @@ class Population: # Reset population ID: self.grid_options["_population_id"] = uuid.uuid4().hex + # save number of stored log stats + self.shared_memory["n_saved_log_stats"] = multiprocessing.Value('i', 0) + # set previous logging time - self.shared_memory["prev_log_time"] = multiprocessing.Value('d', time.time()) + _t = time.time() + + self.shared_memory["prev_log_time"] = multiprocessing.Array('d', [_t] * self.grid_options['n_logging_stats']) # set previous logging system number to 0 - self.shared_memory["prev_log_system_number"] = multiprocessing.Value('i', 0) + self.shared_memory["prev_log_system_number"] = multiprocessing.Array('i', [0] * self.grid_options['n_logging_stats']) # array to store memory use per-thread - mem = mem_use() - self.shared_memory["memory_use_per_thread"] = multiprocessing.Array('d', [1.0 * mem] * self.grid_options["amt_cores"]) + mem = 1.0 * mem_use() + self.shared_memory["memory_use_per_thread"] = multiprocessing.Array('d', [mem] * self.grid_options["amt_cores"]) def clean( self @@ -1333,7 +1341,7 @@ class Population: # variables for the statu bar prints start_grid_time = time.time() - next_log_time = self.shared_memory["prev_log_time"].value + self.grid_options["log_dt"] + next_log_time = self.shared_memory["prev_log_time"][0] + self.grid_options["log_dt"] next_mem_update_time = start_grid_time + self.grid_options["log_dt"] ############################################################ @@ -1406,7 +1414,7 @@ class Population: next_mem_update_time = now + self.grid_options["log_dt"] # calculate the next logging time - next_log_time = self.shared_memory["prev_log_time"].value + self.grid_options["log_dt"] + next_log_time = self.shared_memory["prev_log_time"][0] + self.grid_options["log_dt"] # Check if we need to log info again # TODO: Check if we can put this functionality elsewhere @@ -1420,8 +1428,23 @@ class Population: # Set some values for next time next_log_time = now + self.grid_options["log_dt"] - self.shared_memory["prev_log_time"].value = now - self.shared_memory["prev_log_system_number"].value = system_number + + #print("PREV ",self.shared_memory["prev_log_time"]) + #print("N LOG STATS",self.shared_memory["n_saved_log_stats"].value) + + # shift the arrays + self.shared_memory["prev_log_time"][-(self.grid_options['n_logging_stats'] - 1):] = self.shared_memory["prev_log_time"][:(self.grid_options['n_logging_stats'] - 1)] + self.shared_memory["prev_log_system_number"][-(self.grid_options['n_logging_stats'] - 1):] = self.shared_memory["prev_log_system_number"][:(self.grid_options['n_logging_stats'] - 1)] + + # set the current time and system number + self.shared_memory["prev_log_time"][0] = now + self.shared_memory["prev_log_system_number"][0] = system_number + + # increase the number of stats + self.shared_memory["n_saved_log_stats"].value = min(self.shared_memory["n_saved_log_stats"].value+1,self.grid_options['n_logging_stats']) + + #print("FIRST (0) ",self.shared_memory["prev_log_time"][0]) + #print("LAST (",self.shared_memory["n_saved_log_stats"].value-1,")",self.shared_memory["prev_log_time"][self.shared_memory["n_saved_log_stats"].value-1]) ############### # Log current system info @@ -4285,8 +4308,17 @@ eccentricity3=0 # calculate estimated time of arrive (eta and eta_secs), time per run (tpr) localtime = time.localtime(now) - dt = now - self.shared_memory["prev_log_time"].value - dn = system_number - self.shared_memory["prev_log_system_number"].value + # calculate stats + n = self.shared_memory["n_saved_log_stats"].value + if n < 2: + # simple 1-system calculation: inaccurate + # but best for small n + dt = now - self.shared_memory["prev_log_time"][0] + dn = system_number - self.shared_memory["prev_log_system_number"][0] + else: + # average over n_saved_log_stats + dt = self.shared_memory["prev_log_time"][0] - self.shared_memory["prev_log_time"][n-1] + dn = self.shared_memory["prev_log_system_number"][0] - self.shared_memory["prev_log_system_number"][n-1] eta, units, tpr, eta_secs = trem( dt, @@ -4336,21 +4368,60 @@ eccentricity3=0 # do the print verbose_print( - "{system_number}/{total_starcount}{modulo} {complete:5.1f}% complete {hours:02d}:{minutes:02d}:{seconds:02d} ETA={eta:7.1f}{units} tpr={tpr:2.2e} ETF={etf} mem:{mem_use:.1f}MB {system_string}".format( + "{opening_colour}{system_number}/{total_starcount}{modulo} {pc_colour}{pc_complete:5.1f}% complete {time_colour}{hours:02d}:{minutes:02d}:{seconds:02d} {ETA_colour}ETA={ETA:7.1f}{units} tpr={tpr:2.2e} {ETF_colour}ETF={ETF} {mem_use_colour}mem:{mem_use:.1f}MB {system_string_colour}{system_string}{closing_colour}".format( + opening_colour = self.ANSI_colours['reset'] + self.ANSI_colours['yellow on black'], system_number = system_number, total_starcount = self.grid_options["_total_starcount"], - complete=(100.0*system_number)/(1.0*self.grid_options["_total_starcount"]) if self.grid_options["_total_starcount"] else -1, modulo = modulo, + pc_colour = self.ANSI_colours['blue on black'], + pc_complete=(100.0*system_number)/(1.0*self.grid_options["_total_starcount"]) if self.grid_options["_total_starcount"] else -1, + time_colour = self.ANSI_colours['green on black'], hours = localtime.tm_hour, minutes = localtime.tm_min, seconds = localtime.tm_sec, - eta = eta, + ETA_colour = self.ANSI_colours['red on black'], + ETA = eta, units = units, tpr = tpr, - etf = etf, + ETF_colour = self.ANSI_colours['blue'], + ETF = etf, + mem_use_colour = self.ANSI_colours['magenta'], mem_use = total_mem_use, - system_string=system_string + system_string_colour = self.ANSI_colours['yellow'], + system_string=system_string, + closing_colour = self.ANSI_colours['reset'] ), self.grid_options["verbosity"], 1 ) + + def _ANSI_colours(self): + # ANSI colours dictionary + foreground_colours = { + 'red':Fore.RED, + 'yellow':Fore.YELLOW, + 'blue':Fore.BLUE, + 'cyan':Fore.CYAN, + 'green':Fore.GREEN, + 'magenta':Fore.MAGENTA, + 'white':Fore.WHITE, + 'black':Fore.BLACK, + } + background_colours = { + 'red':Back.RED, + 'yellow':Back.YELLOW, + 'blue':Back.BLUE, + 'cyan':Back.CYAN, + 'green':Back.GREEN, + 'magenta':Back.MAGENTA, + 'white':Back.WHITE, + 'black':Back.BLACK, + } + default_style = Style.BRIGHT + colours = {} + for c in foreground_colours: + colours[c] = default_style + foreground_colours[c] + for d in background_colours: + colours[c + ' on ' + d] = foreground_colours[c] + background_colours[d] + colours['reset'] = Style.RESET_ALL + return colours diff --git a/binarycpython/utils/grid_options_defaults.py b/binarycpython/utils/grid_options_defaults.py index 67c845abcfa93144b4b6f377da3bd10e1d3a80e9..140ea2e25e8d24597b3142b11da9d327ba28d07d 100644 --- a/binarycpython/utils/grid_options_defaults.py +++ b/binarycpython/utils/grid_options_defaults.py @@ -53,6 +53,7 @@ grid_options_defaults_dict = { temp_dir(), "binary_c_python.log" ), # Set to None to not log to file. The directory will be created "log_dt" : 5, # time between vb=1 logging outputs + "n_logging_stats" : 50, # number of logging stats used to calculate time remaining (etc.) default = 50 ########################## # binary_c files ########################## diff --git a/requirements.txt b/requirements.txt index e9d511142ece93bf128fa836e852761c6e265938..849cea1674d09ee33183259a71103715edd32b9c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ backcall==0.1.0 certifi==2019.9.11 chardet==3.0.4 clang==6.0.0.2 +colorama==0.4.4 cycler==0.10.0 decorator==4.4.1 dill==0.3.1.1 diff --git a/setup.py b/setup.py index cac092170d2f73d37d656cf7e94031e81e8716c4..291e0793c129a196bad03462deeba44fbda05d79 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ def license(): def check_version(installed_binary_c_version, required_binary_c_versions): """Function to check the installed version and compare it to the required version""" message = """ - Something went wrong. Make sure that binary_c config exists. + Something went wrong. Make sure that binary_c config exists. Possibly the binary_c version that is installed ({}) does not match the binary_c versions ({}) that this release of the binary_c python module requires. """.format( @@ -259,7 +259,8 @@ setup( "seaborn", "py_rinterpolate", "setproctitle", - "psutil" + "psutil", + "colorama" ], include_package_data=True, ext_modules=[BINARY_C_PYTHON_API_MODULE], # binary_c must be loaded