From 8c48e6ff082271f89018e48e8db6ed160ccc6732 Mon Sep 17 00:00:00 2001
From: David Hendriks <davidhendriks93@gmail.com>
Date: Mon, 9 Aug 2021 20:17:35 +0100
Subject: [PATCH] updated extra features function

---
 binarycpython/utils/functions.py       | 209 ++++++++++++-------------
 examples/notebook_extra_features.ipynb | 181 ++++++++++++++++++++-
 2 files changed, 283 insertions(+), 107 deletions(-)

diff --git a/binarycpython/utils/functions.py b/binarycpython/utils/functions.py
index f8b03c8d6..d428ddd70 100644
--- a/binarycpython/utils/functions.py
+++ b/binarycpython/utils/functions.py
@@ -492,7 +492,7 @@ def create_hdf5(data_dir: str, name: str) -> None:
 ########################################################
 
 
-def return_binary_c_version_info(parsed: bool = False) -> Union[str, dict]:
+def return_binary_c_version_info(parsed: bool = True) -> Union[str, dict]:
     """
     Function that returns the version information of binary_c. This function calls the function
     _binary_c_bindings.return_version_info()
@@ -1478,6 +1478,19 @@ def inspect_dict(
     return structure_dict
 
 
+def count_keys_recursive(input_dict):
+    """
+    Function to count the total amount of keys in a dictionary
+    """
+
+    local_count = 0
+    for key in input_dict.keys():
+        local_count += 1
+        if isinstance(input_dict[key], (dict, OrderedDict)):
+            local_count += count_keys_recursive(input_dict[key])
+    return local_count
+
+
 def merge_dicts(dict_1: dict, dict_2: dict) -> dict:
     """
     Function to merge two dictionaries in a custom way.
@@ -1603,6 +1616,7 @@ def merge_dicts(dict_1: dict, dict_2: dict) -> dict:
     #
     return new_dict
 
+
 def update_dicts(dict_1: dict, dict_2: dict) -> dict:
     """
     Function to update dict_1 with values of dict_2 in a recursive way.
@@ -1695,70 +1709,23 @@ def update_dicts(dict_1: dict, dict_2: dict) -> dict:
     return new_dict
 
 
-def count_keys_recursive(input_dict):
-    """
-    Function to count the total amount of keys in a dictionary
-    """
-
-    local_count = 0
-    for key in input_dict.keys():
-        local_count += 1
-        if isinstance(input_dict[key], (dict, OrderedDict)):
-            local_count += count_keys_recursive(input_dict[key])
-    return local_count
-
-
-def recursive_change_key_to_float(input_dict):
-    """
-    Function to recursively change the key to float
-
-    This only works if the dict contains just sub-dicts or numbers/strings.
-    Does not work with lists as values
+def multiply_values_dict(input_dict, factor):
     """
+    Function that goes over dictionary recursively and multiplies the value if possible by a factor
 
-    new_dict = OrderedDict() # TODO: check if this still works
-
-    for key in input_dict:
-        if isinstance(input_dict[key], (dict, OrderedDict)):
-            try:
-                num_key = float(key)
-                new_dict[num_key] = recursive_change_key_to_float(copy.deepcopy(input_dict[key]))
-            except ValueError:
-                new_dict[key] = recursive_change_key_to_float(copy.deepcopy(input_dict[key]))
-        else:
-            try:
-                num_key = float(key)
-                new_dict[num_key] = input_dict[key]
-            except ValueError:
-                new_dict[key] = input_dict[key]
-
-    return new_dict
-
-def recursive_change_key_to_string(input_dict):
-    """
-    Function to recursively change the key back to a string but this time in a format that we decide
+    If the key equals "general_info", the multiplication gets skipped
     """
 
-    new_dict = OrderedDict() # TODO: check if this still works
-
     for key in input_dict:
-        if isinstance(input_dict[key], (dict, OrderedDict)):
-            if isinstance(key, (int, float)):
-                string_key = "{:g}".format(key)
-                new_dict[string_key] = recursive_change_key_to_string(copy.deepcopy(input_dict[key]))
-            else:
-                new_dict[key] = recursive_change_key_to_string(copy.deepcopy(input_dict[key]))
-        else:
-            if isinstance(key, (int, float)):
-                string_key = "{:g}".format(key)
-                new_dict[string_key] = input_dict[key]
+        if not key == 'general_info':
+            if isinstance(input_dict[key], (dict, OrderedDict)):
+                input_dict[key] = multiply_values_dict(input_dict[key], factor)
             else:
-                new_dict[key] = input_dict[key]
-
-    return new_dict
+                if isinstance(input_dict[key], (int, float)):
+                    input_dict[key] = input_dict[key] * factor
 
+    return input_dict
 
-# New method to create a ordered dictionary
 def custom_sort_dict(input_dict):
     """
     Returns a dictionary that is ordered, but can handle numbers better than normal OrderedDict
@@ -1810,26 +1777,58 @@ def custom_sort_dict(input_dict):
     return input_dict
 
 
-def multiply_values_dict(input_dict, factor):
+def recursive_change_key_to_float(input_dict):
     """
-    Function that goes over dictionary recursively and multiplies the value if possible by a factor
+    Function to recursively change the key to float
 
-    If the key equals "general_info", the multiplication gets skipped
+    This only works if the dict contains just sub-dicts or numbers/strings.
+    Does not work with lists as values
     """
 
+    new_dict = OrderedDict() # TODO: check if this still works
+
     for key in input_dict:
-        if not key == 'general_info':
-            if isinstance(input_dict[key], (dict, OrderedDict)):
-                input_dict[key] = multiply_values_dict(input_dict[key], factor)
+        if isinstance(input_dict[key], (dict, OrderedDict)):
+            try:
+                num_key = float(key)
+                new_dict[num_key] = recursive_change_key_to_float(copy.deepcopy(input_dict[key]))
+            except ValueError:
+                new_dict[key] = recursive_change_key_to_float(copy.deepcopy(input_dict[key]))
+        else:
+            try:
+                num_key = float(key)
+                new_dict[num_key] = input_dict[key]
+            except ValueError:
+                new_dict[key] = input_dict[key]
+
+    return new_dict
+
+
+def recursive_change_key_to_string(input_dict):
+    """
+    Function to recursively change the key back to a string but this time in a format that we decide
+    """
+
+    new_dict = OrderedDict() # TODO: check if this still works
+
+    for key in input_dict:
+        if isinstance(input_dict[key], (dict, OrderedDict)):
+            if isinstance(key, (int, float)):
+                string_key = "{:g}".format(key)
+                new_dict[string_key] = recursive_change_key_to_string(copy.deepcopy(input_dict[key]))
             else:
-                if isinstance(input_dict[key], (int, float)):
-                    input_dict[key] = input_dict[key] * factor
+                new_dict[key] = recursive_change_key_to_string(copy.deepcopy(input_dict[key]))
+        else:
+            if isinstance(key, (int, float)):
+                string_key = "{:g}".format(key)
+                new_dict[string_key] = input_dict[key]
+            else:
+                new_dict[key] = input_dict[key]
 
-    return input_dict
+    return new_dict
 
 
 #####
-
 def extract_ensemble_json_from_string(binary_c_output: str) -> dict:
     """
     Function to extract the ensemble_json information from a raw binary_c output string
@@ -1875,6 +1874,44 @@ def extract_ensemble_json_from_string(binary_c_output: str) -> dict:
     return json_dict
 
 
+def handle_ensemble_string_to_json(raw_output):
+    """
+    Function that deals with the raw output of the ensemble and
+    creates a working JSON dictionary out of it.
+
+    Having this wrapper makes it easy to
+
+    Args:
+        raw_output: raw output of the ensemble dump by binary_c
+
+    Returns:
+        json.loads(raw_output, cls=binarycDecoder)
+
+    """
+
+    # return json.loads(json.dumps(ast.literal_eval(raw_output)), cls=binarycDecoder)
+    return json.loads(raw_output, cls=binarycDecoder)
+
+
+def binaryc_json_serializer(obj: Any) -> Any:
+    """
+    Custom serialiser 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
+
+    Args:
+        obj: The object that might not be serialisable
+
+    Returns:
+        Either string representation of object if the object is a function, or the object itself
+    """
+
+    if inspect.isfunction(obj) or isinstance(obj, py_rinterpolate.Rinterpolate):
+        return str(obj)
+    return obj
+
+
 class binarycDecoder(json.JSONDecoder):
     """
     Custom decoder to transform the numbers that are strings to actual floats
@@ -1931,41 +1968,3 @@ class BinaryCEncoder(json.JSONEncoder):
 
         # Let the base class default method raise the TypeError
         return json.JSONEncoder.default(self, o)
-
-
-def binaryc_json_serializer(obj: Any) -> Any:
-    """
-    Custom serialiser 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
-
-    Args:
-        obj: The object that might not be serialisable
-
-    Returns:
-        Either string representation of object if the object is a function, or the object itself
-    """
-
-    if inspect.isfunction(obj) or isinstance(obj, py_rinterpolate.Rinterpolate):
-        return str(obj)
-    return obj
-
-
-def handle_ensemble_string_to_json(raw_output):
-    """
-    Function that deals with the raw output of the ensemble and
-    creates a working JSON dictionary out of it.
-
-    Having this wrapper makes it easy to
-
-    Args:
-        raw_output: raw output of the ensemble dump by binary_c
-
-    Returns:
-        json.loads(raw_output, cls=binarycDecoder)
-
-    """
-
-    # return json.loads(json.dumps(ast.literal_eval(raw_output)), cls=binarycDecoder)
-    return json.loads(raw_output, cls=binarycDecoder)
diff --git a/examples/notebook_extra_features.ipynb b/examples/notebook_extra_features.ipynb
index d453a6f5b..b68864639 100644
--- a/examples/notebook_extra_features.ipynb
+++ b/examples/notebook_extra_features.ipynb
@@ -8,13 +8,190 @@
     "# Extra features and functionality of binarycpython\n",
     "In this notebook we'll go over some of the extra features and functionality that was not covered in the other notebooks.\n",
     "\n",
-    "TODO"
+    "Within the module `binarycpython.utils.functions` there are many functions that can be useful when using binarycpython. We can see which functions are in there, again by using the `help()`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 33,
+   "id": "de73a2c1-7acd-4b55-a4c4-ee6a7e0758d0",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from binarycpython.utils.functions import (\n",
+    "    get_help,\n",
+    "    get_help_all,\n",
+    "    get_help_super,\n",
+    "    return_binary_c_version_info,\n",
+    "    get_defaults\n",
+    ")\n",
+    "# help(binarycpython.utils.functions)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "88b93969-b6aa-41b7-8f4d-2eee38d7a756",
+   "metadata": {},
+   "source": [
+    "## getting extra information about binary_c parameters\n",
+    "There are several functions that can be used to get information about the parameters in binary_c: \n",
+    "- `get_help(parameter)`: Function to get information about the specific input parameter. Prints the output on default but returns a dictionary containing the information. \n",
+    "- `get_help_all(print_help=True)`: Function to get information about all the parameters. Prints the output on default but returns a dictionary containing the information. \n",
+    "- `get_help_super()`:  Function to get even more information about all the parameters. Does not print the output on default but returns a dictionary containing the information. \n",
+    "- `get_defaults()`: Function that will get all the default values for the parameters. Returns a dictionary"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "id": "7cfe1832-7fec-4817-b633-5b275c65667f",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "parameter_name:\n",
+      "\tM_1\n",
+      "parameter_value_input_type:\n",
+      "\tFloat\n",
+      "description:\n",
+      "\tThe initial mass of star one (in solar units, internally this is star index 0).\n",
+      "default:\n",
+      "\t0\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "{'parameter_name': 'M_1',\n",
+       " 'parameter_value_input_type': 'Float',\n",
+       " 'description': 'The initial mass of star one (in solar units, internally this is star index 0).',\n",
+       " 'default': '0'}"
+      ]
+     },
+     "execution_count": 23,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "get_help('M_1')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "id": "af62a066-ef70-4b59-877e-2b5a6bafcfc2",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# get_help_all()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "id": "b85f1956-ee69-444a-a212-cd7473007bf1",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# get_help_super()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 36,
+   "id": "e22b7a47-2748-406e-bba4-e92825ea9b47",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# get_defaults()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c89ef423-82b9-49ed-8cf9-94c9ce41a82a",
+   "metadata": {},
+   "source": [
+    "## Build information of binary_c\n",
+    "Sometimes we want to know with which settings binary_c has been built. We can use the function `return_binary_c_version_info` for this.\n",
+    "This function will parse the version info of binary_c and return a dictionary with all the settings."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 32,
+   "id": "4dae05bd-6a66-4b1f-be4a-d092627dfe37",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "dict_keys(['networks', 'isotopes', 'argpairs', 'ensembles', 'macros', 'elements', 'dt_limits', 'nucleosynthesis_sources', 'miscellaneous'])\n"
+     ]
+    }
+   ],
+   "source": [
+    "version_info_dict = return_binary_c_version_info(parsed=True)\n",
+    "print(version_info_dict.keys())"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "708c7253-9d9d-4705-969b-23f29695517d",
+   "metadata": {},
+   "source": [
+    "## Example parse function\n",
+    "TODO: In the functions module there is an example parse function that can be used in conjunction with run_system. "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 34,
+   "id": "8656614a-09da-486f-b299-61cc6092187c",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Help on function get_defaults in module binarycpython.utils.functions:\n",
+      "\n",
+      "get_defaults(filter_values:bool=False) -> dict\n",
+      "    Function that calls the binaryc get args function and cast it into a dictionary.\n",
+      "    \n",
+      "    All the values are strings\n",
+      "    \n",
+      "    Args:\n",
+      "        filter_values: whether to filter out NULL and Function defaults.\n",
+      "    \n",
+      "    Returns:\n",
+      "        dictionary containing the parameter name as key and the parameter default as value\n",
+      "\n"
+     ]
+    }
+   ],
+   "source": []
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6fac26d0-a0d2-40c7-915d-0883247cd24d",
+   "metadata": {},
+   "source": [
+    "## Dictionary modification\n",
+    "- merge_dicts \n",
+    "- update_dicts\n",
+    "- multiply_values_dict\n",
+    "\n",
+    "TODO:"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "0020f1bc-2a23-455c-8216-9e63e6e038ae",
+   "id": "bf3c1e28-1662-47a7-abab-aa6fb0ef0882",
    "metadata": {},
    "outputs": [],
    "source": []
-- 
GitLab