From 9da557dab7fe8982b7a3b5469d5f2fbc52ad9675 Mon Sep 17 00:00:00 2001
From: David Hendriks <davidhendriks93@gmail.com>
Date: Sat, 28 Mar 2020 14:40:27 +0000
Subject: [PATCH] adding plot_system routine to easily plot some of the
 standard things of the binary evolution

---
 binarycpython/utils/functions.py          |  42 +++---
 binarycpython/utils/grid.py               |   9 +-
 binarycpython/utils/plot_functions.py     | 162 ++++++++++++++++++++++
 binarycpython/utils/run_system_wrapper.py |   2 -
 requirements.txt                          |  83 ++++++-----
 5 files changed, 234 insertions(+), 64 deletions(-)
 create mode 100644 binarycpython/utils/plot_functions.py

diff --git a/binarycpython/utils/functions.py b/binarycpython/utils/functions.py
index b15dcc5d9..5e401986f 100644
--- a/binarycpython/utils/functions.py
+++ b/binarycpython/utils/functions.py
@@ -363,6 +363,22 @@ def get_help_all(print_help=True, return_dict=False):
     else:
         return None
 
+def filter_arg_dict(arg_dict):
+    """
+    Function to filter out keys that contain values included in ['NULL', 'Function', '']
+    """
+
+    old_dict = arg_dict.copy()
+    new_dict = {}
+    
+    for key in old_dict.keys():
+        if not old_dict[key] in ["NULL", "Function"]:
+            if not old_dict[key] == "":
+                new_dict[key] = old_dict[key]
+
+    return new_dict
+
+
 
 def create_arg_string(arg_dict, sort=False, filter_values=False):
     """
@@ -375,18 +391,12 @@ def create_arg_string(arg_dict, sort=False, filter_values=False):
     """
     arg_string = ""
 
-    # 
-    keys = sorted(arg_dict.keys()) if sort else arg_dict.keys()
+    if filter_values:
+        arg_dict = filter_values(arg_dict)
 
-    # 
+    keys = sorted(arg_dict.keys()) if sort else arg_dict.keys()
     for key in keys:
-        # Filter out NULLS (not compiled anyway)
-        if filter_values:   
-            if not arg_dict[key] in ["NULL", "Function"]:
-                if not arg_dict[key] == "":
-                    arg_string += "{key} {value} ".format(key=key, value=arg_dict[key])
-        else:
-            arg_string += "{key} {value} ".format(key=key, value=arg_dict[key])
+        arg_string += "{key} {value} ".format(key=key, value=arg_dict[key])
     arg_string = arg_string.strip()
     return arg_string
 
@@ -405,16 +415,10 @@ def get_defaults(filter_values=False):
     for default in default_output.split("\n"):
         if not default in ["__ARG_BEGIN", "__ARG_END", ""]:
             key, value = default.split(" = ")
+            default_dict[key] = value
 
-            # Filter out NULLS (not compiled anyway)
-            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
+    if filter_values:
+        default_dict = filter_arg_dict(default_dict)
 
     return default_dict
 
diff --git a/binarycpython/utils/grid.py b/binarycpython/utils/grid.py
index 41e77311d..5a3953a22 100644
--- a/binarycpython/utils/grid.py
+++ b/binarycpython/utils/grid.py
@@ -27,6 +27,7 @@ from binarycpython.utils.functions import (
     parse_binary_c_version_info,
     output_lines,
     remove_file,
+    filter_arg_dict,
 )
 
 
@@ -1258,14 +1259,12 @@ class Population(object):
         - "NULL" 
         - ""
         - "Function"
-
+        
+        Uses the function from utils.functions 
         """
 
-        cleaned_dict = {}
         binary_c_defaults = self.return_binary_c_defaults().copy()
-        for key in binary_c_defaults:
-            if not binary_c_defaults[key] in ["NULL", "", "Function"]:
-                cleaned_dict[key] = binary_c_defaults[key]
+        cleaned_dict = filter_arg_dict(binary_c_defaults)
 
         return cleaned_dict
 
diff --git a/binarycpython/utils/plot_functions.py b/binarycpython/utils/plot_functions.py
new file mode 100644
index 000000000..052970293
--- /dev/null
+++ b/binarycpython/utils/plot_functions.py
@@ -0,0 +1,162 @@
+"""
+Module that contains plotting routines for single systems.
+
+The idea to do this is to provide the user with some quick 
+commands to plot the evolution of a system
+
+There is no preloaded matplotlib rc, you should do that yourself
+"""
+
+import pandas as pd
+import numpy as np
+
+import matplotlib.pyplot as plt
+from david_phd_functions.plotting.plot_functions import plot_orbit, plot_masses, plot_HR_diagram
+
+from binarycpython.utils.functions import get_arg_keys, output_lines
+from binarycpython.utils.run_system_wrapper import run_system
+from binarycpython.utils.custom_logging_functions import binary_c_log_code
+
+# Define the custom_logging_strings. These are kept to the minimum necessary for each plotting routine. 
+
+custom_logging_string_masses = ""
+custom_logging_string_orbit = ""
+
+custom_logging_string_HR_diagram = """
+Printf("HR_PLOTTING %30.12e %d %d %g %g %g %g %g %g\\n",
+    //
+    stardata->model.time, // 1
+    
+    // stellar types
+    stardata->star[0].stellar_type, //2
+    stardata->star[1].stellar_type, //3
+
+    // luminosity and radii
+    stardata->star[0].luminosity, // 4
+    stardata->star[1].luminosity, // 5
+    stardata->star[0].radius,     // 6
+    stardata->star[1].radius,     // 7
+
+    // masses
+    stardata->star[0].pms_mass, // 8
+    stardata->star[1].pms_mass  // 9
+    );
+"""
+
+
+
+
+
+
+
+
+# Define the parse functions for the plotting routines
+def dummy():
+    pass
+
+
+
+def parse_function_hr_diagram(output):
+    """
+    Parsing function for the HR plotting routine
+    """
+
+    # extract info from the single evolution
+
+    values_list = []
+
+    parameters = ['time',
+        'stellar_type_1', 'stellar_type_2',  
+        'luminosity_1', 'luminosity_2', 
+        'radius_1', 'radius_2', 'pms_mass_1', 'pms_mass_2']
+
+    # Go over the output.
+    for el in output_lines(output):
+        headerline = el.split()[0]
+
+        # Check the header and act accordingly
+        if (headerline=='HR_PLOTTING'):
+            values = el.split()[1:]
+            values_list.append(values)
+
+    df = pd.DataFrame(values_list)
+    df.columns = parameters
+
+
+    df = df.astype(np.float64)
+    df['stellar_type_1'] = df['stellar_type_1'].astype(np.int64)
+    df['stellar_type_2'] = df['stellar_type_2'].astype(np.int64)
+
+    return df
+
+def plot_system(plot_type, **kwargs):
+    """
+    TODO: Complex Function!
+    TODO: make sure this way of passing args works correctly.
+    TODO: make the plotting specific keywords available via the inspect stuff
+    Function to plot the evolution of the system.  
+
+    This goes (in general) via the following steps:
+        - a preset custom logging for a specific plotting routine is loaded.
+        - This is used for the run_system call
+        - The output of this run_system is loaded into a dataframe by parsing it with a corresponding parsing function
+        - The dataframe is passed to the plotting routine
+        - plot is shown or returned.
+
+    There are several pre-set plots to choose from
+
+    All keywords are considered kwargs, except for plot_type
+    input:
+        plot_type: string input should be one of the following types: ['mass_evolution', 'orbit_evolution', 'hr_diagram'].
+            Input will be matched against this, and then go through a dictionary to pick the correct plotting function. 
+        return_fig: boolean whether to return the fig object instead of plotting the plot (makes so that you can customize it)
+        show_stellar_types: whether to plot the stellar type evolution on a second pane. This is not included in all the plotting routines.
+        Other input: other kwargs that are passed to run_system (inspect the docstring of run_system for more info)
+    """
+
+    # set defaults and options
+    return_fig = False
+    show_stellar_types = False
+
+    plot_types_dict = {
+            'mass_evolution': {'plot_function': plot_masses, 'custom_logging_string': custom_logging_string_masses, 'parse_function': dummy},
+            'orbit_evolution': {'plot_function': plot_orbit, 'custom_logging_string': custom_logging_string_orbit, 'parse_function': dummy},
+            'hr_diagram': {'plot_function': plot_HR_diagram, 'custom_logging_string': custom_logging_string_HR_diagram, 'parse_function': parse_function_hr_diagram},
+        }
+    plot_system_specific_keywords = ['plot_type', 'show_plot', 'show_stellar_types']
+
+    # First check on the plot_type input
+    if not plot_type in plot_types_dict.keys():
+        print("Warning, the provided plot type is not known. Please choose one from the following:\n\t{}".format(plot_types_dict.keys()))
+        raise ValueError
+
+    # First: check all the arguments. Chosen to not check all the keywords for run_system and binary_c specifically, but just to pick out the ones needed for this routine. run_system will handle the rest
+    run_system_arg_dict = {}
+
+    for key in kwargs.keys():
+        if key == 'show_plot':
+            show_plot = kwargs[key]
+        elif key == 'show_stellar_types':
+            show_stellar_types = kwargs[key]
+
+        # The rest will be passed to run_system
+        else:
+            run_system_arg_dict[key] = kwargs[key]
+
+    # TODO: When a list of plot_types is passed, make it so that the strings are chained, and that the output of the binary_c call is handled by multiple parsers
+    custom_logging_code = binary_c_log_code(plot_types_dict[plot_type]['custom_logging_string'])
+    run_system_arg_dict['custom_logging_code'] = custom_logging_code
+
+    run_system_arg_dict['parse_function'] = plot_types_dict[plot_type]['parse_function']
+
+    # Run and get the output of the parse function
+    binary_c_output_df = run_system(**run_system_arg_dict)
+
+
+    fig = plot_types_dict[plot_type]['plot_function'](binary_c_output_df, show_plot=show_plot, show_stellar_types=show_stellar_types)
+
+    if not show_plot:
+        return fig
+
+
+plot_system(plot_type='hr_diagram', M_1=10, M_2=5, orbital_period=100000, max_evolution_time=15000, show_plot=True)
\ No newline at end of file
diff --git a/binarycpython/utils/run_system_wrapper.py b/binarycpython/utils/run_system_wrapper.py
index 7166b0318..0a247f1f0 100644
--- a/binarycpython/utils/run_system_wrapper.py
+++ b/binarycpython/utils/run_system_wrapper.py
@@ -40,8 +40,6 @@ def run_system(**kwargs):
 
     other_keywords = ['custom_logging_code', 'log_filename', 'parse_function']
 
-
-
     # Set default values
     func_memaddr = -1
     write_logfile = 0
diff --git a/requirements.txt b/requirements.txt
index 6329c5c64..1c79312a3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,57 +1,64 @@
-astropy==3.2.1
-attrs==19.1.0
+alabaster==0.7.12
+astropy==4.0.1
+Babel==2.7.0
 backcall==0.1.0
-bleach==3.1.0
+binarycpython==0.1
+certifi==2019.9.11
+chardet==3.0.4
+clang==6.0.0.2
 cycler==0.10.0
-decorator==4.4.0
-defusedxml==0.6.0
+decorator==4.4.1
 dill==0.3.1.1
-entrypoints==0.3
+docutils==0.15.2
 h5py==2.10.0
-ipykernel==5.1.2
-ipython==7.7.0
+hawkmoth==0.4
+idna==2.8
+imagesize==1.1.0
+ipython==7.9.0
 ipython-genutils==0.2.0
-ipywidgets==7.5.1
-ivs-sse==0.0
 jedi==0.15.1
-Jinja2==2.10.1
-jsonschema==3.0.2
-jupyter==1.0.0
-jupyter-client==5.3.1
-jupyter-console==6.0.0
-jupyter-core==4.5.0
+Jinja2==2.10.3
 kiwisolver==1.1.0
+lxml==4.5.0
+m2r==0.2.1
 MarkupSafe==1.1.1
+matplotlib==3.1.2
 mistune==0.8.4
 multiprocess==0.70.9
-nbconvert==5.6.0
-nbformat==4.4.0
-notebook==6.0.0
-numpy==1.17.0
-#pandas==0.25.0
-pandocfilters==1.4.2
+mypy==0.770
+mypy-extensions==0.4.3
+numpy==1.17.4
+packaging==19.2
+pandas==0.25.3
 parso==0.5.1
 pathos==0.2.5
 pexpect==4.7.0
 pickleshare==0.7.5
 pox==0.2.7
 ppft==1.6.6.1
-prometheus-client==0.7.1
-prompt-toolkit==2.0.9
+prompt-toolkit==2.0.10
+psutil==5.6.7
 ptyprocess==0.6.0
 Pygments==2.4.2
-pyparsing==2.4.2
-pyrsistent==0.15.4
-python-dateutil==2.8.0
-pytz==2019.2
-pyzmq==18.1.0
-qtconsole==4.5.2
-Send2Trash==1.5.0
-six==1.12.0
-terminado==0.8.2
-testpath==0.4.2
-tornado==6.0.3
-traitlets==4.3.2
+pyparsing==2.4.5
+python-dateutil==2.8.1
+pytz==2019.3
+requests==2.22.0
+scipy==1.4.1
+seaborn==0.10.0
+setproctitle==1.1.10
+six==1.13.0
+snowballstemmer==2.0.0
+Sphinx==2.2.1
+sphinx-rtd-theme==0.4.3
+sphinxcontrib-applehelp==1.0.1
+sphinxcontrib-devhelp==1.0.1
+sphinxcontrib-htmlhelp==1.0.2
+sphinxcontrib-jsmath==1.0.1
+sphinxcontrib-qthelp==1.0.2
+sphinxcontrib-serializinghtml==1.1.3
+traitlets==4.3.3
+typed-ast==1.4.1
+typing-extensions==3.7.4.1
+urllib3==1.25.7
 wcwidth==0.1.7
-webencodings==0.5.1
-widgetsnbextension==3.5.1
-- 
GitLab