From 3c444a1b8f49cbebc9dd723652018960d527abdb Mon Sep 17 00:00:00 2001 From: David Hendriks <davidhendriks93@gmail.com> Date: Wed, 18 Dec 2019 16:31:22 +0000 Subject: [PATCH] added help_all functionality and began writing the grid script --- binarycpython/utils/functions.py | 132 ++++++++++++++++-- binarycpython/utils/grid.py | 41 ++++++ .../example_run_binary_with_custom_logging.py | 6 +- include/binary_c_python.h | 4 +- src/binary_c_python.c | 84 +++++++++-- src/binary_c_python_api.c | 76 ++++++++-- tests/python_API_test.py | 18 ++- 7 files changed, 326 insertions(+), 35 deletions(-) create mode 100644 binarycpython/utils/grid.py diff --git a/binarycpython/utils/functions.py b/binarycpython/utils/functions.py index c65ca92db..df087d4e2 100644 --- a/binarycpython/utils/functions.py +++ b/binarycpython/utils/functions.py @@ -1,3 +1,5 @@ +import copy +import json from collections import defaultdict import binary_c_python_api @@ -5,6 +7,110 @@ from binarycpython.utils.custom_logging_functions import ( create_and_load_logging_function, ) +def get_help_all(print_help=True, return_dict=False): + """ + Function that reads out the output of the help_all api call to binary_c + + prints all the parameters and their descriptions. + + return_dict: returns a dictionary + """ + + # Call function + help_all = binary_c_python_api.return_help_all() + + # String manipulation + split = help_all.split( + "############################################################\n" + ) + cleaned = [el for el in split if not el == "\n"] + + section_nums = [i for i in range(len(cleaned)) if cleaned[i].startswith("#####")] + + # Create dicts + help_all_dict = {} + + # Select the section name and the contents of that section. Note, not all sections have content! + for i in range(len(section_nums)): + if not i == len(section_nums) - 1: + params = cleaned[section_nums[i] + 1 : section_nums[i + 1]] + else: + params = cleaned[section_nums[i] + 1 : len(cleaned)] + section_name = ( + cleaned[section_nums[i]] + .lstrip("#####") + .strip() + .replace("Section ", "") + .lower() + ) + + # + params_dict = {} + + if params: + + # Clean it, replace in-text newlines with a space and then split on newlines. + split_params = params[0].strip().replace("\n ", " ").split("\n") + + # Process params and descriptions per section + for el in split_params: + split_param_info = el.split(" : ") + if not len(split_param_info) == 3: + # there are ocassions where the semicolon is used in the description text itself. + if len(split_param_info) == 4: + split_param_info = [ + split_param_info[0], + ": ".join([split_param_info[1], split_param_info[2]]), + split_param_info[3], + ] + + # other occassions? + + # Put the information in a dict + param_name = split_param_info[0] + param_description = split_param_info[1] + rest = split_param_info[2] + + params_dict[param_name] = { + "param_name": param_name, + "description": param_description, + "rest": rest, + } + + # make section_dict + section_dict = {"section_name": section_name, "parameters": params_dict.copy()} + + # Put in the total dict + help_all_dict[section_name] = section_dict.copy() + + # Print things + if print_help: + for section in sorted(help_all_dict.keys()): + print( + "##################\n###### Section {}\n##################".format( + section + ) + ) + section_dict = help_all_dict[section] + for param_name in sorted(section_dict["parameters"].keys()): + param = section_dict["parameters"][param_name] + print( + "\n{}:\n\t{}: {}".format( + param["param_name"], param["description"], param["rest"] + ) + ) + + # Loop over all the parameters an call the help() function on it. Takes a long time but this is for testing + # for section in help_all_dict.keys(): + # section_dict = help_all_dict[section] + # for param in section_dict['parameters'].keys(): + # get_help(param) + + if return_dict: + return help_all_dict + else: + return None + def create_arg_string(arg_dict): """ @@ -17,11 +123,14 @@ def create_arg_string(arg_dict): return arg_string -def get_defaults(): +def get_defaults(filter_values=False): """ Function that calls the binaryc get args function and cast it into a dictionary All the values are strings + + filter_values: whether to filter out NULL and Function defaults. """ + default_output = binary_c_python_api.return_arglines() default_dict = {} @@ -30,9 +139,15 @@ def get_defaults(): key, value = default.split(" = ") # Filter out NULLS (not compiled anyway) - if not value in ["NULL", "Function"]: - if not value == "": - default_dict[key] = value + if filter_values: + if not value in ["NULL", "Function"]: + if not value == "": + default_dict[key] = value + + # On default, just show everything + else: + default_dict[key] = value + return default_dict @@ -118,8 +233,8 @@ def get_help(param_name, return_dict=False): macros = cleaned[macros_line_nr[0] + 1 :] help_info_dict["macros"] = macros - for key in help_info_dict.keys(): - print("{}:\n\t{}".format(key, help_info_dict[key])) + # for key in help_info_dict.keys(): + # print("{}:\n\t{}".format(key, help_info_dict[key])) if return_dict: return help_info_dict @@ -133,7 +248,7 @@ def get_help(param_name, return_dict=False): return None -get_help("RLOF_method") +# get_help("RLOF_method") def run_system(**kwargs): @@ -223,7 +338,7 @@ def run_system_with_log(**kwargs): output = binary_c_python_api.run_binary_with_logfile(arg_string) return output - +# run_system_with_log() def parse_output(output, selected_header): """ @@ -323,3 +438,4 @@ def load_logfile(logfile): event_list.append(" ".join(split_line[9:])) print(event_list) +# load_logfile() \ No newline at end of file diff --git a/binarycpython/utils/grid.py b/binarycpython/utils/grid.py new file mode 100644 index 000000000..57937bb40 --- /dev/null +++ b/binarycpython/utils/grid.py @@ -0,0 +1,41 @@ +import binary_c_python_api + +from binarycpython.utils.functions import ( + get_defaults, +) + +import binarycpython + + +class Population(object): + def __init__(self): + """ + Lots of initialisation + """ + + self.defaults = get_defaults() + pass + + def evolve(self): + pass + + def evolve_single(self): + arg_string = "binary_c " + for param in self.defaults.keys(): + print(param, self.defaults[param]) + if self.defaults[param]=='': + print(self.defaults[param]) + + # arg_string += "{} {} ".format(param, self.defaults[param]) + + # arg_string = arg_string.strip() + out = binary_c_python_api.run_binary(arg_string) + print(out) + # print( arg_string) + + +test_pop = Population() + +# print(test_pop.defaults) + +test_pop.evolve_single() \ No newline at end of file diff --git a/examples/example_run_binary_with_custom_logging.py b/examples/example_run_binary_with_custom_logging.py index 83ab013a0..ac6cfc7b9 100644 --- a/examples/example_run_binary_with_custom_logging.py +++ b/examples/example_run_binary_with_custom_logging.py @@ -2,14 +2,14 @@ import ctypes import tempfile import os -from binaryc_python_utils.custom_logging_functions import ( +from binarycpython.utils.custom_logging_functions import ( autogen_C_logging_code, binary_c_log_code, compile_shared_lib, temp_custom_logging_dir, create_and_load_logging_function, ) -import binary_c +import binary_c_python_api # generate logging lines logging_line = autogen_C_logging_code( @@ -36,5 +36,5 @@ 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, separation, orbital_period, eccentricity, metallicity, max_evolution_time ) -output = binary_c.run_binary_custom_logging(argstring, func_memaddr) +output = binary_c_python_api.run_binary_custom_logging(argstring, func_memaddr) print(output) diff --git a/include/binary_c_python.h b/include/binary_c_python.h index e2a48e16f..01aca72ea 100644 --- a/include/binary_c_python.h +++ b/include/binary_c_python.h @@ -34,7 +34,9 @@ int return_help_info(char * argstring, char ** const errorstring, size_t * const nbytes); - +int return_help_all_info(char ** const outstring, + char ** const errorstring, + size_t * const nbytes); /* C macros */ #define BINARY_C_APITEST_VERSION 0.1 diff --git a/src/binary_c_python.c b/src/binary_c_python.c index bd0ffd741..726752d97 100644 --- a/src/binary_c_python.c +++ b/src/binary_c_python.c @@ -29,20 +29,26 @@ static char module_docstring[] MAYBE_UNUSED = static char create_binary_docstring[] = "Allocate memory for a binary"; #endif +static char function_prototype_docstring[] = + "The prototype for a binary_c python function"; +static char new_binary_system_docstring[] = + "Return an object containing a binary, ready for evolution"; + +// Evolution function docstrings static char run_binary_docstring[] = "Run one binary using binary_c"; static char run_binary_with_logdocstring[] = "Run one binary using binary_c and allow the logfile to be written. Do not use for populations!"; static char run_binary_custom_loggingdocstring[] = "TODO"; -static char new_binary_system_docstring[] = - "Return an object containing a binary, ready for evolution"; -static char function_prototype_docstring[] = - "The prototype for a binary_c python function"; + +// Utility function docstrings static char return_arglines_docstring[] = "Return the default args for a binary_c system"; static char return_help_info_docstring[] = "Return the help info for a given parameter"; +static char return_help_all_info_docstring[] = + "Return an overview of all the parameters, their description, categorized in sections"; static struct libbinary_c_store_t *store = NULL; @@ -50,14 +56,18 @@ static struct libbinary_c_store_t *store = NULL; #ifdef __DEPRECATED static PyObject* binary_c_create_binary(PyObject *self, PyObject *args); #endif +static PyObject* binary_c_function_prototype(PyObject *self, PyObject *args); +static PyObject* binary_c_new_binary_system(PyObject *self, PyObject *args); + +// Evolution function headers static PyObject* binary_c_run_binary(PyObject *self, PyObject *args); static PyObject* binary_c_run_binary_with_logfile(PyObject *self, PyObject *args); static PyObject* binary_c_run_binary_custom_logging(PyObject *self, PyObject *args); -static PyObject* binary_c_function_prototype(PyObject *self, PyObject *args); -static PyObject* binary_c_new_binary_system(PyObject *self, PyObject *args); + +// Utility function headers static PyObject* binary_c_return_arglines(PyObject *self, PyObject *args); static PyObject* binary_c_return_help_info(PyObject *self, PyObject *args); - +static PyObject* binary_c_return_help_all_info(PyObject *self, PyObject *args); /* * Python 3 interface is described at @@ -80,8 +90,10 @@ static PyMethodDef module_methods[] = { {"run_binary", binary_c_run_binary, METH_VARARGS, run_binary_docstring}, {"run_binary_with_logfile", binary_c_run_binary_with_logfile, METH_VARARGS, run_binary_with_logdocstring}, {"run_binary_custom_logging", binary_c_run_binary_custom_logging, METH_VARARGS, run_binary_custom_loggingdocstring}, + {"return_arglines", binary_c_return_arglines, METH_VARARGS, return_arglines_docstring}, {"return_help", binary_c_return_help_info, METH_VARARGS, return_help_info_docstring}, + {"return_help_all", binary_c_return_help_all_info, METH_VARARGS, return_help_all_info_docstring}, {NULL, NULL, 0, NULL} }; @@ -92,8 +104,8 @@ static PyMethodDef module_methods[] = { static struct PyModuleDef Py_binary_c_python_api = { PyModuleDef_HEAD_INIT, - "binary_c", /* name of module */ - "binary_c docs", /* module documentation, may be NULL */ + "binary_c_python_api", /* name of module */ + "binary_c_python_api docs", /* module documentation, may be NULL */ -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ module_methods }; @@ -187,6 +199,22 @@ static PyObject* binary_c_function_prototype(PyObject *self, PyObject *args) } } +/* + +Below are the real funtions: +binary_c_run_binary +binary_c_run_binary_custom_logging +binary_c_run_binary_with_logfile + +binary_c_return_arglines +binary_c_return_help_info +binary_c_return_help_all_info + + +*/ + +/* Wrappers to functions that evolve binary systems. */ + static PyObject* binary_c_run_binary(PyObject *self, PyObject *args) { /* Parse the input tuple */ @@ -310,6 +338,8 @@ static PyObject* binary_c_run_binary_with_logfile(PyObject *self, PyObject *args } } +/* Wrappers to functions that call other API functionality like help and arglines*/ + static PyObject* binary_c_return_arglines(PyObject *self, PyObject *args) { char * buffer; @@ -375,4 +405,40 @@ static PyObject* binary_c_return_help_info(PyObject *self, PyObject *args) return return_string; } +} + +static PyObject* binary_c_return_help_all_info(PyObject *self, PyObject *args) +{ + /* Parse the input tuple */ + // char *argstring; + + // if(!PyArg_ParseTuple(args, "s", &argstring)) + // { + // return NULL; + // } + // else + // { + char * buffer; + char * error_buffer; + size_t nbytes; + int out MAYBE_UNUSED = return_help_all_info(&buffer, + &error_buffer, + &nbytes); + + /* copy the buffer to a python string */ + PyObject * return_string = Py_BuildValue("s", buffer); + PyObject * return_error_string MAYBE_UNUSED = Py_BuildValue("s", error_buffer); + + if(error_buffer != NULL && strlen(error_buffer)>0) + { + fprintf(stderr, + "Error in binary_c run : %s\n", + error_buffer); + } + + Safe_free(buffer); + Safe_free(error_buffer); + + return return_string; + // } } \ No newline at end of file diff --git a/src/binary_c_python_api.c b/src/binary_c_python_api.c index f8e1d11bf..10ab6147a 100644 --- a/src/binary_c_python_api.c +++ b/src/binary_c_python_api.c @@ -45,6 +45,23 @@ static void capture_stdout(void); int out_pipe[2]; int stdoutwas; +/* +Below are the real calls to the API of binary_c. Currently these are the functions: + +run_binary +run_binary_custom_logging +return_arglines +run_binary_with_logfile +return_help_info +return_help_all_info + + +*/ + + + +/* Functions to evolve binary systems */ + int run_binary(char * argstring, char ** const buffer, char ** const error_buffer, @@ -149,6 +166,48 @@ int run_binary_custom_logging(char * argstring, return 0; } +int run_binary_with_logfile(char * argstring, + char ** const buffer, + char ** const error_buffer, + size_t * const nbytes) +{ + /* memory for N binary systems */ + struct libbinary_c_stardata_t *stardata; + struct libbinary_c_store_t * store = NULL; + + /* make new stardata */ + stardata = NULL; + binary_c_new_system(&stardata, + NULL, + NULL, + &store, + &argstring, + -1); + + /* output to strings */ + stardata->preferences->internal_buffering = INTERNAL_BUFFERING_STORE; + stardata->preferences->batchmode = BATCHMODE_LIBRARY; + + /* do binary evolution */ + binary_c_evolve_for_dt(stardata, + stardata->model.max_evolution_time); + + /* get buffer pointer */ + binary_c_buffer_info(stardata,buffer,nbytes); + + /* get error buffer pointer */ + binary_c_error_buffer(stardata,error_buffer); + + /* set raw_buffer_size = -1 to prevent it being freed */ + stardata->tmpstore->raw_buffer_size = -1; + + /* free stardata (except the buffer) */ + binary_c_free_memory(&stardata,TRUE,TRUE,FALSE,FALSE); + binary_c_free_store_contents(store); + return 0; +} + +/* Functions to call other API functionality like help and arglines */ int return_arglines(char ** const buffer, char ** const error_buffer, size_t * const nbytes) @@ -200,7 +259,8 @@ int return_arglines(char ** const buffer, return 0; } -int run_binary_with_logfile(char * argstring, + +int return_help_info(char * argstring, char ** const buffer, char ** const error_buffer, size_t * const nbytes) @@ -222,9 +282,8 @@ int run_binary_with_logfile(char * argstring, stardata->preferences->internal_buffering = INTERNAL_BUFFERING_STORE; stardata->preferences->batchmode = BATCHMODE_LIBRARY; - /* do binary evolution */ - binary_c_evolve_for_dt(stardata, - stardata->model.max_evolution_time); + /* Ask the help api */ + binary_c_help(stardata, argstring); /* get buffer pointer */ binary_c_buffer_info(stardata,buffer,nbytes); @@ -241,9 +300,7 @@ int run_binary_with_logfile(char * argstring, return 0; } - -int return_help_info(char * argstring, - char ** const buffer, +int return_help_all_info(char ** const buffer, char ** const error_buffer, size_t * const nbytes) { @@ -253,11 +310,12 @@ int return_help_info(char * argstring, /* make new stardata */ stardata = NULL; + char * empty_str = ""; binary_c_new_system(&stardata, NULL, NULL, &store, - &argstring, + &empty_str, -1); /* output to strings */ @@ -265,7 +323,7 @@ int return_help_info(char * argstring, stardata->preferences->batchmode = BATCHMODE_LIBRARY; /* Ask the help api */ - binary_c_help(stardata, argstring); + binary_c_help_all(stardata); /* get buffer pointer */ binary_c_buffer_info(stardata,buffer,nbytes); diff --git a/tests/python_API_test.py b/tests/python_API_test.py index e24a7906e..cc373d2a4 100755 --- a/tests/python_API_test.py +++ b/tests/python_API_test.py @@ -117,13 +117,21 @@ def test_run_binary_with_custom_logging(): print(out) +def test_return_help_all(): + out = binary_c_python_api.return_help_all("M_1") + print(out) + + #### -# test_run_binary() +if __name__ == "__main__": + test_run_binary() + + test_run_binary_with_log() -# test_run_binary_with_log() + test_run_binary_with_custom_logging() -# test_return_help() + test_return_help() -# test_return_arglines() + test_return_arglines() -# test_run_binary_with_custom_logging() + test_return_help_all() -- GitLab