From 77cef2b18ce12f80b00b9290b77dfc1858125b52 Mon Sep 17 00:00:00 2001
From: David Hendriks <davidhendriks93@gmail.com>
Date: Wed, 20 Nov 2019 23:56:34 +0000
Subject: [PATCH] made the parse output a bit more functional

---
 .../custom_logging_functions.py               | 30 ++++++----
 binaryc_python_utils/functions.py             | 38 ++++++++----
 examples/examples.py                          | 28 ++++++---
 setup.py                                      | 59 ++++++++++---------
 4 files changed, 93 insertions(+), 62 deletions(-)

diff --git a/binaryc_python_utils/custom_logging_functions.py b/binaryc_python_utils/custom_logging_functions.py
index 3bb4d7f48..cd37988bc 100644
--- a/binaryc_python_utils/custom_logging_functions.py
+++ b/binaryc_python_utils/custom_logging_functions.py
@@ -122,7 +122,7 @@ def from_binary_c_config(config_file, flag):
     return res
 
 
-def return_compilation_dict():
+def return_compilation_dict(verbose=False):
     """
     Function to build the compile command for the shared library
 
@@ -206,21 +206,23 @@ def return_compilation_dict():
     ]
     libs = "{} {}".format(" ".join(library_paths), " ".join(non_library_paths))
 
-    print(
-        "Building shared library for custom logging with (binary_c.h) at {} on {}\n".format(
-            BINARY_C_SRC_DIR, socket.gethostname()
+
+    if verbose:
+        print(
+            "Building shared library for custom logging with (binary_c.h) at {} on {}\n".format(
+                BINARY_C_SRC_DIR, socket.gethostname()
+            )
         )
-    )
-    print(
-        "With options:\n\tcc = {cc}\n\tccflags = {ccflags}\n\tld = {ld}\n\tlibs = {libs}\n\tinc = {inc}\n\n".format(
-            cc=cc, ccflags=ccflags, ld=ld, libs=libs, inc=inc
+        print(
+            "With options:\n\tcc = {cc}\n\tccflags = {ccflags}\n\tld = {ld}\n\tlibs = {libs}\n\tinc = {inc}\n\n".format(
+                cc=cc, ccflags=ccflags, ld=ld, libs=libs, inc=inc
+            )
         )
-    )
 
     return {"cc": cc, "ld": ld, "ccflags": ccflags, "libs": libs, "inc": inc}
 
 
-def compile_shared_lib(code, sourcefile_name, outfile_name):
+def compile_shared_lib(code, sourcefile_name, outfile_name, verbose=False):
     """
     Function to write the custom logging code to a file and then compile it.
     """
@@ -245,11 +247,13 @@ def compile_shared_lib(code, sourcefile_name, outfile_name):
     command = " ".join(command.split())
 
     # Execute compilation
-    print("Executing following command:\n{command}".format(command=command))
+    if verbose:
+        print("Executing following command:\n{command}".format(command=command))
     res = subprocess.check_output("{command}".format(command=command), shell=True)
 
-    if res:
-        print("Output of compilation command:\n{}".format(res))
+    if verbose:
+        if res:
+            print("Output of compilation command:\n{}".format(res))
 
 
 def temp_custom_logging_dir():
diff --git a/binaryc_python_utils/functions.py b/binaryc_python_utils/functions.py
index c0b9ee155..cc7e3b954 100644
--- a/binaryc_python_utils/functions.py
+++ b/binaryc_python_utils/functions.py
@@ -136,16 +136,20 @@ def run_system_with_log(**kwargs):
 
 def parse_output(output, selected_header):
     """
-    Function that parses output of binaryc when it is construction like this:
-    DAVID_SINGLE_ANALYSIS t=0 mass=20 
+    Function that parses output of binary_c:
+
+    This function works in two cases:
+    if the caught line contains output like 'example_header time=12.32 mass=0.94 ..'
+    or if the line contains output like 'example_header 12.32 0.94'
 
     You can give a 'selected_header' to catch any line that starts with that. 
     Then the values will be put into a dictionary.
-    TODO: Think about exporting to numpy array or pandas
-
+    
+    TODO: Think about exporting to numpy array or pandas instead of a defaultdict
     """
-    value_dicts = []
-    val_lists = []
+
+    value_dicts     = []
+    val_lists       = []
 
     # split output on newlines
     for i, line in enumerate(output.split("\n")):
@@ -155,17 +159,25 @@ def parse_output(output, selected_header):
 
             # Select parts
             header = split_line[0]
-            value_array = split_line[1:]
+            values_list = split_line[1:]
 
+            # print(values_list)
             # Catch line starting with selected header
             if header == selected_header:
-                # print(value_array)
-                # Make a dict
+                # Check if the line contains '=' symbols:
                 value_dict = {}
-                for el in value_array:
-                    key, val = el.split("=")
-                    value_dict[key] = val
-                value_dicts.append(value_dict)
+                if all('=' in el for el in values_list):
+                    for el in values_list:
+                        key, val = el.split("=")
+                        value_dict[key.strip()] = val.strip()
+                    value_dicts.append(value_dict)
+                else:
+                    if any('=' in el for el in values_list):
+                        raise ValueError('Caught line contains some = symbols but not all of them do. aborting run')
+                    else:
+                        for i, val in enumerate(values_list):
+                            value_dict[i] = val
+                        value_dicts.append(value_dict)
 
     if len(value_dicts) == 0:
         print(
diff --git a/examples/examples.py b/examples/examples.py
index a8609771c..7df0e0287 100644
--- a/examples/examples.py
+++ b/examples/examples.py
@@ -20,7 +20,6 @@ Use these as inspiration/base.
 def run_example_binary():
     """
     Function to run a binary system. Very basic approach which directly adresses the run_binary(..) python-c wrapper function. 
-
     """
 
     m1 = 15.0  # Msun
@@ -46,7 +45,6 @@ def run_example_binary():
     output = binary_c.run_binary(argstring)
     print(output)
 
-
 # run_example_binary()
 
 
@@ -60,6 +58,8 @@ def run_example_binary_with_run_system():
     run_system: mostly just makes passing arguments to the function easier. It also loads all the necessary defaults in the background
     parse_output: Takes the raw output of binary_c and selects those lines that start with the given header. 
     Note, if you dont use the custom_logging functionality binary_c should be configured to have output that starts with that given header
+
+    The parsing of the output only works correctly if either all of the values are described inline like `mass=<number>' or none of them are.    
     """
 
     import pandas as pd
@@ -68,16 +68,31 @@ def run_example_binary_with_run_system():
     # Run system. all arguments can be given as optional arguments.
     output = run_system(M_1=10, M_2=20, separation=0, orbital_period=100000000000)
 
+    # print(output)
+
     # Catch results that start with a given header. (Mind that binary_c has to be configured to print them if your not using a custom logging function)
-    result_example_header = parse_output(output, "example_header")
+    result_example_header_1 = parse_output(output, selected_header="example_header_1")
+    result_example_header_2 = parse_output(output, selected_header="example_header_2")
+
+    # print(result_example_header_1)
 
     #### Now do whatever you want with it:
     # Put it in numpy arrays
     # t_res = np.asarray(result_example_header['t'], dtype=np.float64, order='C')
     # m_res = np.asarray(result_example_header['mass'], dtype=np.float64, order='C')
 
+    # Or put them into a pandas array
+
+
     # Cast the data into a dataframe.
-    df = pd.DataFrame.from_dict(result_example_header, dtype=np.float64)
+    # This example automatically catches the column names because the binary_c output line is constructed as 'example_header_1 time=<number>..'
+    df = pd.DataFrame.from_dict(result_example_header_1, dtype=np.float64)
+    print(df)
+
+    # This example has column headers which are numbered, but we can override that with custom headers.
+    df2 = pd.DataFrame.from_dict(result_example_header_2, dtype=np.float64)
+    df2.columns=['time', 'mass_1', 'mass_2', 'st1', 'st2', 'sep', 'ecc']
+    print(df2)
 
     # print(df)
     # sliced_df = df[df.t < 1000] # Cut off late parts of evolution
@@ -85,8 +100,7 @@ def run_example_binary_with_run_system():
 
     # Some routine to plot.
 
-# run_example_binary_with_run_system()
-
+run_example_binary_with_run_system()
 
 def run_example_binary_with_custom_logging():
     """
@@ -166,4 +180,4 @@ def run_example_binary_with_writing_logfile():
 
     # Some routine to plot.
 
-# run_example_binary_with_writing_logfile()
+# run_example_binary_with_writing_logfile()
\ No newline at end of file
diff --git a/setup.py b/setup.py
index c2e18e378..88648bed4 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
-# from distutils.core import setup, Extension
+from distutils.core import setup, Extension
 # from setuptools import find_packages
-from setuptools import setup, find_packages, Extension
+# from setuptools import setup, find_packages, Extension
 import os
 import subprocess
 import re
@@ -57,11 +57,37 @@ API_h = os.environ["BINARY_C"] + "/src/API/binary_c_API.h"
 binary_c_define_macros.extend([("BINARY_C_API_H", API_h)])
 
 
+binary_c_python_api_module = Extension(
+    "binary_c",
+    ["src/binary_c_python.c"],
+    include_dirs=[
+        os.environ["BINARY_C"] + "/src",
+        os.environ["BINARY_C"] + "/src/API",
+        "include",
+    ]
+    + binary_c_incdirs,
+    libraries=["binary_c"] + binary_c_libs + ["binary_c_api"],
+    library_dirs=[
+        os.environ["BINARY_C"] + "/src",
+        "./",
+        os.path.join(CWD, "lib/"),
+    ]
+    + binary_c_libdirs,
+    runtime_library_dirs=[
+        os.environ["BINARY_C"] + "/src",
+        "./",
+        os.path.join(CWD, "lib/"),
+    ]
+    + binary_c_libdirs,
+    define_macros=[] + binary_c_define_macros,
+    extra_objects=[],
+    extra_compile_args=[],
+)
+
 def readme():
     with open('README.md') as f:
         return f.read()
 
-
 setup(
     name="binary_c",
     version="1.0",
@@ -72,31 +98,6 @@ setup(
     url="https://gitlab.eps.surrey.ac.uk/ri0005/binary_c-python",
     license="",
     ext_modules=[
-        Extension(
-            "binary_c",
-            ["src/binary_c_python.c"],
-            include_dirs=[
-                os.environ["BINARY_C"] + "/src",
-                os.environ["BINARY_C"] + "/src/API",
-                "include",
-            ]
-            + binary_c_incdirs,
-            libraries=["binary_c"] + binary_c_libs + ["binary_c_api"],
-            library_dirs=[
-                os.environ["BINARY_C"] + "/src",
-                "./",
-                os.path.join(CWD, "lib/"),
-            ]
-            + binary_c_libdirs,
-            runtime_library_dirs=[
-                os.environ["BINARY_C"] + "/src",
-                "./",
-                os.path.join(CWD, "lib/"),
-            ]
-            + binary_c_libdirs,
-            define_macros=[] + binary_c_define_macros,
-            extra_objects=[],
-            extra_compile_args=[],
-        )
+        binary_c_python_api_module
     ],  # binary_c must be loaded
 )
-- 
GitLab