From da3dccd035e02894e31c304e2214cb7ff626e526 Mon Sep 17 00:00:00 2001 From: David Hendriks <davidhendriks93@gmail.com> Date: Sun, 30 Aug 2020 20:47:14 +0100 Subject: [PATCH] making the tests for persistent data actually useful. putting the ensemble functions in the gridls --- binarycpython/utils/functions.py | 68 +++++++-- binarycpython/utils/grid.py | 235 +++++++++++++++++++------------ tests/test_persistent_data.py | 129 +++++++++++------ 3 files changed, 287 insertions(+), 145 deletions(-) diff --git a/binarycpython/utils/functions.py b/binarycpython/utils/functions.py index 35d293cac..715dbc0b5 100644 --- a/binarycpython/utils/functions.py +++ b/binarycpython/utils/functions.py @@ -8,6 +8,9 @@ useful functions for the user import json import os import tempfile +import copy +import inspect + from collections import defaultdict import h5py @@ -19,15 +22,25 @@ import binary_c_python_api # utility functions ######################################################## -def remove_file(file, verbose=0): +def verbose_print(message, verbosity, minimal_verbosity): + """ + Function that decides whether to print a message based on the current verbosity + and its minimum verbosity + + if verbosity is equal or higher than the minimum, then we print + """ + + if verbosity >= minimal_verbosity: + print(message) + +def remove_file(file, verbosity=0): """ Function to remove files but with verbosity """ if os.path.exists(file): try: - if verbose > 0: - print("Removed {}".format(file)) + verbose_print("Removed {}".format(file), verbosity, 1) os.remove(file) except FileNotFoundError as inst: @@ -116,6 +129,18 @@ def create_hdf5(data_dir, name): # version_info functions ######################################################## +def return_binary_c_version_info(parsed=False): + """ + Function that returns the version information of binary_c + """ + + version_info = binary_c_python_api.return_version_info().strip() + + if parsed: + version_info = parse_binary_c_version_info(version_info) + + return version_info + def parse_binary_c_version_info(version_info_string): """ Function that parses the binary_c version info. Length function with a lot of branches @@ -666,7 +691,7 @@ def load_logfile(logfile): # Ensemble dict functions ######################################################## -def inspect_dict(dict_1, indent=0): +def inspect_dict(dict_1, indent=0, print_structure=True): """ Function to inspect a dict. @@ -675,10 +700,16 @@ def inspect_dict(dict_1, indent=0): Prints out keys and their value types """ + structure_dict = {} + for key, value in dict_1.items(): - print("\t"*indent, key, type(value)) + structure_dict[key] = type(value) + if print_structure: + print("\t"*indent, key, type(value)) if isinstance(value, dict): - inspect_dict(value, indent=indent+1) + structure_dict[key] = inspect_dict(value, indent=indent+1) + return structure_dict + def merge_dicts(dict_1, dict_2): """ @@ -715,24 +746,27 @@ def merge_dicts(dict_1, dict_2): if isinstance(dict_1[key], (float, int)): new_dict[key] = dict_1[key] else: - new_dict[key] = dict_1[key].deepcopy() + copy_dict = copy.deepcopy(dict_1[key]) + new_dict[key] = copy_dict for key in unique_to_dict_2: if isinstance(dict_2[key], (float, int)): new_dict[key] = dict_2[key] else: - new_dict[key] = dict_2[key].deepcopy() + copy_dict = copy.deepcopy(dict_2[key]) + new_dict[key] = copy_dict # Go over the common keys: for key in overlapping_keys: - # See whether the types are actually similar + # See whether the types are actually the same if not type(dict_1[key]) is type(dict_2[key]): print("Error {} and {} are not of the same type and cannot be merged".format( dict_1[key], dict_2[key])) raise ValueError - # TODO: Create a matrix of combinations here. - # TODO: Could maybe be more compact + # Here we check for the cases that we want to explicitly catch. Ints will be added, + # floats will be added, lists will be appended (though that might change) and dicts will be + # dealt with by calling this function again. else: # ints if isinstance(dict_1[key], int) and isinstance(dict_2[key], int): @@ -756,6 +790,7 @@ def merge_dicts(dict_1, dict_2): # return new_dict + class binarycDecoder(json.JSONDecoder): """ Custom decoder to transform the numbers that are strings to actual floats @@ -788,3 +823,14 @@ class binarycDecoder(json.JSONDecoder): else: return o +def binaryc_json_serializer(obj): + """ + Custom serializer for binary_c to use when functions are present in the dictionary + that we want to export. + + Function objects will be turned into str representations of themselves + """ + + if inspect.isfunction(obj): + return str(obj) + return obj diff --git a/binarycpython/utils/grid.py b/binarycpython/utils/grid.py index ccd9ae19c..df736bfca 100644 --- a/binarycpython/utils/grid.py +++ b/binarycpython/utils/grid.py @@ -34,12 +34,16 @@ from binarycpython.utils.functions import ( remove_file, filter_arg_dict, get_help_all, + return_binary_c_version_info, + binaryc_json_serializer, + verbose_print, + binarycDecoder, + merge_dicts ) import binary_c_python_api - -# Todo-list +# Tasks # TODO: add functionality to 'on-init' set arguments # TODO: add functionality to return the initial_abundance_hash # TODO: add functionality to return the isotope_hash @@ -81,6 +85,10 @@ class Population: # Set main process id self.grid_options["main_pid"] = os.getpid() + # Set some memory dicts + self.persistent_data_memory_dict = {} + + ################################################### # Argument functions ################################################### @@ -118,20 +126,21 @@ class Population: in the self.grid_options If neither of above is met; the key and the value get stored in a custom_options dict. + + TODO: catch those parameter names that contain an %d """ for key in kwargs: # Filter out keys for the bse_options if key in self.defaults.keys(): - if self.grid_options["verbose"] > 0: - print("adding: {}={} to BSE_options".format(key, kwargs[key])) + verbose_print("adding: {}={} to BSE_options".format(key, kwargs[key]), self.grid_options["verbose"], 1) self.bse_options[key] = kwargs[key] # Filter out keys for the grid_options elif key in self.grid_options.keys(): - if self.grid_options["verbose"] > 0: - print("adding: {}={} to grid_options".format(key, kwargs[key])) + verbose_print("adding: {}={} to grid_options".format(key, kwargs[key]), self.grid_options["verbose"], 1) self.grid_options[key] = kwargs[key] + # The of the keys go into a custom_options dict else: print( @@ -159,8 +168,8 @@ class Population: # 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 self.grid_options["verbose"] > 0: - print("Found cmdline args. Parsing them now") + verbose_print("Found cmdline args. Parsing them now", self.grid_options["verbose"], 1) + # Grab the input and split them up, while accepting only non-empty entries cmdline_args = args.cmdline split_args = [ @@ -208,17 +217,17 @@ class Population: pass def add_grid_variable( - self, - name, - longname, - valuerange, - resolution, - spacingfunc, - probdist, - dphasevol, - parameter_name, - precode=None, - condition=None, + self, + name, + longname, + valuerange, + resolution, + spacingfunc, + probdist, + dphasevol, + parameter_name, + precode=None, + condition=None, ): """spec Function to add grid variables to the grid_options. @@ -276,8 +285,7 @@ class Population: # Load it into the grid_options self.grid_options["grid_variables"][grid_variable["name"]] = grid_variable - if self.grid_options["verbose"] > 0: - print("Added grid variable: {}".format(json.dumps(grid_variable, indent=4))) + verbose_print("Added grid variable: {}".format(json.dumps(grid_variable, indent=4)), self.grid_options["verbose"], 1) ################################################### # Return functions @@ -318,11 +326,11 @@ class Population: return self.defaults def return_all_info( - self, - include_population_settings=True, - include_binary_c_defaults=True, - include_binary_c_version_info=True, - include_binary_c_help_all=True, + self, + include_population_settings=True, + include_binary_c_defaults=True, + include_binary_c_version_info=True, + include_binary_c_help_all=True, ): """ Function that returns all the information about the population and binary_c @@ -342,7 +350,7 @@ class Population: all_info["binary_c_defaults"] = binary_c_defaults if include_binary_c_version_info: - binary_c_version_info = self.return_binary_c_version_info(parsed=True) + binary_c_version_info = return_binary_c_version_info(parsed=True) all_info["binary_c_version_info"] = binary_c_version_info if include_binary_c_help_all: @@ -352,13 +360,13 @@ class Population: return all_info def export_all_info( - self, - use_datadir=True, - outfile=None, - include_population_settings=True, - include_binary_c_defaults=True, - include_binary_c_version_info=True, - include_binary_c_help_all=True, + self, + use_datadir=True, + outfile=None, + include_population_settings=True, + include_binary_c_defaults=True, + include_binary_c_version_info=True, + include_binary_c_help_all=True, ): """ Function that exports the all_info to a json file @@ -382,18 +390,18 @@ class Population: # 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" - ] - ) + # # 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: if not self.custom_options.get("base_filename", None): @@ -412,15 +420,15 @@ class Population: self.custom_options["data_dir"], settings_name ) - if self.grid_options["verbose"] > 0: - print("Writing settings to {}".format(settings_fullname)) + verbose_print("Writing settings to {}".format(settings_fullname), self.grid_options["verbose"], 1) # if not outfile.endswith('json'): with open(settings_fullname, "w") as file: - file.write(json.dumps(all_info_cleaned, indent=4)) + file.write( + json.dumps(all_info_cleaned, indent=4, default=binaryc_json_serializer) + ) else: - if self.grid_options["verbose"] > 0: - print("Writing settings to {}".format(outfile)) + verbose_print("Writing settings to {}".format(outfile), self.grid_options["verbose"], 1) # if not outfile.endswith('json'): with open(outfile, "w") as file: file.write(json.dumps(all_info_cleaned, indent=4)) @@ -432,9 +440,7 @@ class Population: """ # C_logging_code gets priority of C_autogen_code - if self.grid_options["verbose"] > 0: - print("Creating and loading custom logging functionality") - + verbose_print("Creating and loading custom logging functionality", self.grid_options["verbose"], 1) if self.grid_options["C_logging_code"]: # Generate entire shared lib code around logging lines custom_logging_code = binary_c_log_code( @@ -470,6 +476,53 @@ class Population: custom_logging_code, verbose=self.grid_options["verbose"] ) + ################################################### + # Ensemble functions + ################################################### + + def load_persistent_data_memory_dict(self): + """ + Function that loads a set amount (amt_cores) of persistent data memory adresses to + pass to binary_c. + + TODO: fix the function + """ + + for thread_nr in self.grid_options["amt_cores"]: + persistent_data_memaddr = binary_c_python_api.binary_c_return_persistent_data_memaddr() + self.persistent_data_memory_dict[thread_nr] = persistent_data_memaddr + verbose_print("Created the following dict with persistent memaddresses: {}".format(self.persistent_data_memory_dict), self.grid_options["verbosity"], 1) + + def free_persistent_data_memory_and_combine_results(self): + """ + Function that loads a set amount of persisten data memory adresses to + pass to binary_c. + + TODO: fix the function + """ + + combined_ensemble_json = {} + + for key in self.persistent_data_memory_dict: + persistent_data_memaddr = self.persistent_data_memory_dict[key] + + verbose_print("Freeing {} (thread {})and merging output to combined dict".format(persistent_data_memaddr, key), self.grid_options["verbosity"], 1) + + # Get the output and decode it correctly to get the numbers correct + ensemble_json_output = binary_c_python_api.binary_c_free_persistent_data_memaddr_and_return_json_output(persistent_data_memaddr) + parsed_json = json.loads(ensemble_json_output.splitlines()[0][len("ENSEMBLE_JSON "):], cls=binarycDecoder) + + # Combine the output with the main output + combined_ensemble_json = merge_dicts(combined_ensemble_json, parsed_json) + + # Write results to file. + # TODO: Make sure everything is checked beforehand + full_output_filename = os.path.join(self.custom_options["data_dir"], self.custom_options["ensemble_output_name"]) + verbose_print("Writing ensemble output to {}".format(full_output_filename), self.grid_options["verbosity"], 1) + + + + ################################################### # Evolution functions ################################################### @@ -697,8 +750,8 @@ class Population: # 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"] + self.grid_options["evolution_type"] + in self.grid_options["evolution_type_options"] ): if self.grid_options["evolution_type"] == "mp": self.evolve_population_mp() @@ -725,11 +778,10 @@ class Population: Function to test the evolution of a system. Calls the api binding directly. """ - if self.grid_options["verbose"] > 0: - print("running a single system as a test") + verbose_print("running a single system as a test", self.grid_options["verbose"], 1) - m1 = 15.0 # Msun - m2 = 14.0 # Msun + m_1 = 15.0 # Msun + m_2 = 14.0 # Msun separation = 0 # 0 = ignored, use period orbital_period = 4530.0 # days eccentricity = 0.0 @@ -737,8 +789,8 @@ class Population: max_evolution_time = 15000 argstring = "binary_c M_1 {0:g} M_2 {1:g} separation {2:g} orbital_period {3:g}\ eccentricity {4:g} metallicity {5:g} max_evolution_time {6:g}".format( - m1, - m2, + m_1, + m_2, separation, orbital_period, eccentricity, @@ -779,8 +831,7 @@ class Population: Results in a generated file that contains a system_generator function. """ - if self.grid_options["verbose"] > 0: - print("Generating grid code") + verbose_print("Generating grid code", self.grid_options["verbose"], 1) # Some local values code_string = "" @@ -838,8 +889,8 @@ class Population: code_string += indent * depth + "# setting probability lists\n" # Prepare the probability for grid_variable_el in sorted( - self.grid_options["grid_variables"].items(), - key=lambda x: x[1]["grid_variable_number"], + self.grid_options["grid_variables"].items(), + key=lambda x: x[1]["grid_variable_number"], ): # Make probabilities dict grid_variable = grid_variable_el[1] @@ -854,10 +905,10 @@ class Population: # Generate code print("Generating grid code") for loopnr, grid_variable_el in enumerate( - sorted( - self.grid_options["grid_variables"].items(), - key=lambda x: x[1]["grid_variable_number"], - ) + sorted( + self.grid_options["grid_variables"].items(), + key=lambda x: x[1]["grid_variable_number"], + ) ): print("Constructing/adding: {}".format(grid_variable_el[0])) grid_variable = grid_variable_el[1] @@ -1100,11 +1151,11 @@ class Population: # this has to go in a reverse order: # Here comes the stuff that is put after the deepest nested part that calls returns stuff. for loopnr, grid_variable_el in enumerate( - sorted( - self.grid_options["grid_variables"].items(), - key=lambda x: x[1]["grid_variable_number"], - reverse=True, - ) + sorted( + self.grid_options["grid_variables"].items(), + key=lambda x: x[1]["grid_variable_number"], + reverse=True, + ) ): grid_variable = grid_variable_el[1] code_string += indent * (depth + 1) + "#" * 40 + "\n" @@ -1143,8 +1194,7 @@ class Population: # Stop of code generation. Here the code is saved and written # Save the gridcode to the grid_options - if self.grid_options["verbose"] > 0: - print("Saving grid code to grid_options") + verbose_print("Saving grid code to grid_options", self.grid_options["verbose"], 1) self.grid_options["code_string"] = code_string @@ -1154,8 +1204,7 @@ class Population: ) self.grid_options["gridcode_filename"] = gridcode_filename - if self.grid_options["verbose"] > 0: - print("Writing grid code to {}".format(gridcode_filename)) + verbose_print("Writing grid code to {}".format(gridcode_filename), self.grid_options["verbose"], 1) with open(gridcode_filename, "w") as file: file.write(code_string) @@ -1167,13 +1216,11 @@ class Population: """ # Code to load the - - if self.grid_options["verbose"] > 0: - print( - "Loading grid code function from {}".format( - self.grid_options["gridcode_filename"] - ) - ) + verbose_print( + message="Loading grid code function from {}".format( + self.grid_options["gridcode_filename"] + ), + verbosity=self.grid_options["verbose"], minimal_verbosity=1) spec = importlib.util.spec_from_file_location( "binary_c_python_grid", @@ -1185,8 +1232,7 @@ class Population: self.grid_options["system_generator"] = generator - if self.grid_options["verbose"] > 0: - print("Grid code loaded") + verbose_print("Grid code loaded", self.grid_options["verbose"], 1) def dry_run(self): """ @@ -1253,7 +1299,7 @@ class Population: ################################################### def write_binary_c_calls_to_file( - self, output_dir=None, output_filename=None, include_defaults=False + self, output_dir=None, output_filename=None, include_defaults=False ): """ Function that loops over the gridcode and writes the generated parameters to a file. @@ -1349,8 +1395,8 @@ class Population: """ if evol_type == "single": - if self.grid_options["verbose"] > 0: - print("Cleaning up the custom logging stuff. type: single") + verbose_print("Cleaning up the custom logging stuff. type: single", self.grid_options["verbose"], 1) + # TODO: Unset custom logging code # TODO: Unset function memory adress @@ -1364,11 +1410,14 @@ class Population: ) if evol_type == "population": - if self.grid_options["verbose"] > 0: - print("Cleaning up the custom logging stuffs. type: population") + verbose_print("Cleaning up the custom logging stuffs. type: population", self.grid_options["verbose"], 1) + # TODO: make sure that these also work. not fully sure if necessary tho. # whether its a single file, or a dict of files/memaddresses + if evol_type == "MC": + pass + def increment_probtot(self, prob): """ Function to add to the total probability diff --git a/tests/test_persistent_data.py b/tests/test_persistent_data.py index e7c24cc02..9d20bad4c 100644 --- a/tests/test_persistent_data.py +++ b/tests/test_persistent_data.py @@ -9,7 +9,12 @@ import json import textwrap import binary_c_python_api -from binarycpython.utils.functions import binarycDecoder, temp_dir +from binarycpython.utils.functions import ( + binarycDecoder, + temp_dir, + inspect_dict, + merge_dicts, +) TMP_DIR = temp_dir() os.makedirs(os.path.join(TMP_DIR, "test"), exist_ok=True) @@ -102,32 +107,42 @@ def test_adding_ensemble_output(): m1 = 2 # Msun m2 = 0.1 # Msun + extra_mass = 10 ############################################################################################# # The 2 runs below use the ensemble but do not defer the output to anything else, so that the # results are returned directly after the run # Direct output commands - argstring_1 = return_argstring(m1=m1, m2=m2, ensemble_filter="STELLAR_TYPE_COUNTS", defer_ensemble=0) - argstring_2 = return_argstring(m1=m1+1, m2=m2, ensemble_filter="STELLAR_TYPE_COUNTS", defer_ensemble=0) + argstring_1 = return_argstring(m1=m1, m2=m2, + ensemble_filter="STELLAR_TYPE_COUNTS", defer_ensemble=0) + argstring_2 = return_argstring(m1=m1 + extra_mass, m2=m2, + ensemble_filter="STELLAR_TYPE_COUNTS", defer_ensemble=0) # Get outputs output_1 = binary_c_python_api.run_system(argstring=argstring_1) output_2 = binary_c_python_api.run_system(argstring=argstring_2) - ensemble_jsons_1 = [line for line in output_1.splitlines() if line.startswith("ENSEMBLE_JSON")] - ensemble_jsons_2 = [line for line in output_2.splitlines() if line.startswith("ENSEMBLE_JSON")] + test_1_ensemble_jsons_1 = [ + line for line in output_1.splitlines() if line.startswith("ENSEMBLE_JSON") + ] + test_1_ensemble_jsons_2 = [ + line for line in output_2.splitlines() if line.startswith("ENSEMBLE_JSON") + ] - json_1 = json.loads(ensemble_jsons_1[0][len("ENSEMBLE_JSON "):], cls=binarycDecoder) - json_2 = json.loads(ensemble_jsons_2[0][len("ENSEMBLE_JSON "):], cls=binarycDecoder) + test_1_json_1 = json.loads( + test_1_ensemble_jsons_1[0][len("ENSEMBLE_JSON "):], cls=binarycDecoder) + test_1_json_2 = json.loads( + test_1_ensemble_jsons_2[0][len("ENSEMBLE_JSON "):], cls=binarycDecoder) - # test_1_total_dict = SumDict(json_1) - # test_1_total_dict.merge(json_2) + test_1_merged_dict = merge_dicts(test_1_json_1, test_1_json_2) - with open(os.path.join(TMP_DIR, "test", "adding_json_1.json"), 'w') as f: - f.write(json.dumps(json_1, indent=4)) - with open(os.path.join(TMP_DIR, "test", "adding_json_2.json"), 'w') as f: - f.write(json.dumps(json_2, indent=4)) + with open(os.path.join(TMP_DIR, "test", "adding_json_1.json"), 'w') as file: + file.write(json.dumps(test_1_json_1, indent=4)) + with open(os.path.join(TMP_DIR, "test", "adding_json_2.json"), 'w') as file: + file.write(json.dumps(test_1_json_2, indent=4)) + with open(os.path.join(TMP_DIR, "test", "adding_json_merged.json"), 'w') as file: + file.write(json.dumps(test_1_json_2, indent=4)) print("Single runs done\n") @@ -137,31 +152,32 @@ def test_adding_ensemble_output(): # have the output returned in that way # Deferred commands - argstring_1_deferred = return_argstring(m1=m1, m2=m2, ensemble_filter="STELLAR_TYPE_COUNTS", defer_ensemble=1) - argstring_2_deferred = return_argstring(m1=m1+1, m2=m2, ensemble_filter="STELLAR_TYPE_COUNTS", defer_ensemble=1) + argstring_1_deferred = return_argstring( + m1=m1, m2=m2, ensemble_filter="STELLAR_TYPE_COUNTS", defer_ensemble=1) + argstring_2_deferred = return_argstring( + m1=m1 + extra_mass, m2=m2, ensemble_filter="STELLAR_TYPE_COUNTS", defer_ensemble=1) # Get a memory location - persistent_data_memaddr = binary_c_python_api.return_persistent_data_memaddr() + test_2_persistent_data_memaddr = binary_c_python_api.return_persistent_data_memaddr() # Run the systems and defer the output each time - output_1_deferred = binary_c_python_api.run_system( + _ = binary_c_python_api.run_system( argstring=argstring_1_deferred, - persistent_data_memaddr=persistent_data_memaddr + persistent_data_memaddr=test_2_persistent_data_memaddr ) - output_2_deferred = binary_c_python_api.run_system( + _ = binary_c_python_api.run_system( argstring=argstring_2_deferred, - persistent_data_memaddr=persistent_data_memaddr + persistent_data_memaddr=test_2_persistent_data_memaddr ) # Have the persistent_memory adress be released and have the json outputted - output_total_deferred = binary_c_python_api.free_persistent_data_memaddr_and_return_json_output(persistent_data_memaddr) - - ensemble_jsons_deferred = [line for line in output_total_deferred.splitlines() if line.startswith("ENSEMBLE_JSON")] + test_2_output = binary_c_python_api.free_persistent_data_memaddr_and_return_json_output( + test_2_persistent_data_memaddr) + test_2_ensemble_json = [line for line in test_2_output.splitlines() if line.startswith("ENSEMBLE_JSON")] + test_2_json = json.loads(test_2_ensemble_json[0][len("ENSEMBLE_JSON "):], cls=binarycDecoder) - json_deferred = json.loads(ensemble_jsons_deferred[0][len("ENSEMBLE_JSON "):], cls=binarycDecoder) - - with open(os.path.join(TMP_DIR, "test", "adding_json_deferred.json"), 'w') as f: - f.write(json.dumps(json_deferred, indent=4)) + with open(os.path.join(TMP_DIR, "test", "adding_json_deferred.json"), 'w') as file: + file.write(json.dumps(test_2_json, indent=4)) print("Double deferred done\n") @@ -170,27 +186,50 @@ def test_adding_ensemble_output(): # Then the second one uses that memory to combine its results with, but doesn't defer the # data after that, so it will print it after the second run is done - persistent_data_memaddr_2 = binary_c_python_api.return_persistent_data_memaddr() + test_3_persistent_data_memaddr = binary_c_python_api.return_persistent_data_memaddr() - # Run the systems and defer the output once and the second time not, so that the second run automatically prints out the results - output_1_deferred = binary_c_python_api.run_system( + # Run the systems and defer the output once and the second time not, so that the second run + # automatically prints out the results + _ = binary_c_python_api.run_system( argstring=argstring_1_deferred, - persistent_data_memaddr=persistent_data_memaddr_2 + persistent_data_memaddr=test_3_persistent_data_memaddr ) - output_2_deferred_and_output = binary_c_python_api.run_system( + test_3_output_2 = binary_c_python_api.run_system( argstring=argstring_2, - persistent_data_memaddr=persistent_data_memaddr_2 + persistent_data_memaddr=test_3_persistent_data_memaddr ) - - ensemble_jsons_deferred_and_output = [line for line in output_2_deferred_and_output.splitlines() if line.startswith("ENSEMBLE_JSON")] - - json_deferred_and_output = json.loads(ensemble_jsons_deferred_and_output[0][len("ENSEMBLE_JSON "):], cls=binarycDecoder) + test_3_ensemble_jsons = [ + line for line in test_3_output_2.splitlines() if line.startswith("ENSEMBLE_JSON") + ] + test_3_json = json.loads(test_3_ensemble_jsons[0][len("ENSEMBLE_JSON "):], cls=binarycDecoder) with open(os.path.join(TMP_DIR, "test", "adding_json_deferred_and_output.json"), 'w') as f: - f.write(json.dumps(json_deferred_and_output, indent=4)) + f.write(json.dumps(test_3_json, indent=4)) print("Single deferred done\n") + # + assert_message_1 = """ + The structure of the manually merged is not the same as the merged by double deferring + """ + assert_message_2 = """ + The structure of the manually merged is not the same as the merged by deferring once + and output on the second run + """ + + # + assert inspect_dict(test_1_merged_dict, print_structure=False) == inspect_dict(test_2_json, print_structure=False), assert_message_1 + # assert inspect_dict(test_1_merged_dict, print_structure=False) == inspect_dict(test_3_json, print_structure=False), assert_message_2 + +def combine_with_empty_json(): + argstring_1 = return_argstring(defer_ensemble=0) + output_1 = binary_c_python_api.run_system(argstring=argstring_1) + ensemble_jsons_1 = [line for line in output_1.splitlines() if line.startswith("ENSEMBLE_JSON")] + json_1 = json.loads(ensemble_jsons_1[0][len("ENSEMBLE_JSON "):], cls=binarycDecoder) + + assert_message = "combining output json with empty dict should give same result as initial json" + assert merge_dicts(json_1, {}) == json_1, assert_message + def test_free_and_json_output(): """ Function that tests the freeing of the memory adress and the output of the json @@ -203,7 +242,7 @@ def test_free_and_json_output(): argstring_1 = return_argstring(m1=m2, m2=m2, ensemble_filter="STELLAR_TYPE_COUNTS", defer_ensemble=1) # Get a memory adress: - persistent_data_memaddr = binary_c_python_api.return_persistent_data_memaddr("") + persistent_data_memaddr = binary_c_python_api.return_persistent_data_memaddr() # Evolve and defer output print("evolving") @@ -219,10 +258,18 @@ def test_free_and_json_output(): print("Output:") print(textwrap.indent(str(json_output_by_freeing), "\t")) + + parsed_json = json.loads(json_output_by_freeing.splitlines()[0][len("ENSEMBLE_JSON "):], cls=binarycDecoder) + print(parsed_json) + # ensemble_jsons_1 = [line for line in output_1.splitlines() if line.startswith("ENSEMBLE_JSON")] + # json_1 = json.loads(ensemble_jsons_1[0][len("ENSEMBLE_JSON "):], cls=binarycDecoder) + + #### if __name__ == "__main__": - test_return_persistent_data_memaddr() + # test_return_persistent_data_memaddr() # test_passing_persistent_data_to_run_system() # test_full_ensemble_output() # test_adding_ensemble_output() - # test_free_and_json_output() + test_free_and_json_output() + # combine_with_empty_json() -- GitLab