diff --git a/.gitignore b/.gitignore index fc4b5eaa0e905f92701264354a082eb56ebfe503..a1f83a92e8e0d778f8824315aa37bdfcd4603fb5 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ obj/ binarycpython_snippets -1.log - # Standard template *.org~ *.so diff --git a/binarycpython/tests/example.ipynb b/binarycpython/tests/example.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..57ce0148138a19e0c9cffd6872673c3f44d627d7 --- /dev/null +++ b/binarycpython/tests/example.ipynb @@ -0,0 +1,95 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "fd5b1a83-7212-4aca-b317-991bf289fba8", + "metadata": {}, + "outputs": [], + "source": [ + "def add(a, b):\n", + " return a + b" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "4d842395-3e17-48e8-b613-9856365e9796", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "11" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "add(5, 6)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f2afc967-a66a-4a47-bfc5-0d6c17826794", + "metadata": {}, + "outputs": [ + { + "ename": "ZeroDivisionError", + "evalue": "division by zero", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m<ipython-input-3-bc757c3fda29>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" + ] + } + ], + "source": [ + "1 / 0" + ] + }, + { + "cell_type": "markdown", + "id": "8491b29d-375d-458f-8a46-fc822422d8f3", + "metadata": {}, + "source": [ + "hello" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "601a89e6-5ca6-4725-8834-5e975ba76726", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/binarycpython/tests/main.py b/binarycpython/tests/main.py index 9d7e00f632b5b42ac96c21ced287609a3d7f622f..abafd95b221ca038e06521ed47a79c4f7198e336 100755 --- a/binarycpython/tests/main.py +++ b/binarycpython/tests/main.py @@ -5,7 +5,6 @@ Main file for the tests. This file imports all the combined_test functions from import unittest - from binarycpython.tests.test_c_bindings import * from binarycpython.tests.test_custom_logging import * from binarycpython.tests.test_distributions import * @@ -18,6 +17,7 @@ from binarycpython.tests.test_spacing_functions import * from binarycpython.tests.test_useful_funcs import * from binarycpython.tests.test_grid_options_defaults import * from binarycpython.tests.test_stellar_types import * +from test_notebooks import * if __name__ == "__main__": unittest.main() diff --git a/binarycpython/tests/test_c_bindings.py b/binarycpython/tests/test_c_bindings.py index 662a50e4654f453519aabc887758f331d5c82842..d92d449682bdded20d9e06e2e7b4fcca394a7c37 100644 --- a/binarycpython/tests/test_c_bindings.py +++ b/binarycpython/tests/test_c_bindings.py @@ -24,9 +24,7 @@ from binarycpython.utils.functions import ( Capturing, ) -# https://docs.python.org/3/library/unittest.html -TMP_DIR = temp_dir() -os.makedirs(os.path.join(TMP_DIR, "test"), exist_ok=True) +TMP_DIR = temp_dir("tests", "test_c_bindings") #### some useful functions def return_argstring( diff --git a/binarycpython/tests/test_custom_logging.py b/binarycpython/tests/test_custom_logging.py index 62c5be37044f9eac622ed553924ff14005d0f6b3..17f54545f0941c2a5dbb8db823d6e12bafb958d0 100644 --- a/binarycpython/tests/test_custom_logging.py +++ b/binarycpython/tests/test_custom_logging.py @@ -7,8 +7,7 @@ import unittest from binarycpython.utils.custom_logging_functions import * from binarycpython.utils.functions import Capturing -binary_c_temp_dir = temp_dir() - +TMP_DIR = temp_dir("tests", "test_custom_logging") class test_custom_logging(unittest.TestCase): """ @@ -26,7 +25,7 @@ class test_custom_logging(unittest.TestCase): """ input_dict_1 = None - output_1 = autogen_C_logging_code(input_dict_1, verbose=1) + output_1 = autogen_C_logging_code(input_dict_1, verbosity=1) self.assertEqual(output_1, None, msg="Error. return value should be None") input_dict_2 = { @@ -37,7 +36,7 @@ class test_custom_logging(unittest.TestCase): "model.dt", ] } - output_2 = autogen_C_logging_code(input_dict_2, verbose=1) + output_2 = autogen_C_logging_code(input_dict_2, verbosity=1) test_output_2 = 'Printf("MY_STELLAR_DATA %g %g %g %g\\n",((double)stardata->model.time),((double)stardata->star[0].mass),((double)stardata->model.probability),((double)stardata->model.dt));' self.assertEqual( @@ -45,7 +44,7 @@ class test_custom_logging(unittest.TestCase): ) input_dict_3 = {"MY_STELLAR_DATA": 2} - output_3 = autogen_C_logging_code(input_dict_3, verbose=1) + output_3 = autogen_C_logging_code(input_dict_3, verbosity=1) self.assertEqual(output_3, None, msg="Output should be None") def test_binary_c_log_code(self): @@ -59,11 +58,11 @@ class test_custom_logging(unittest.TestCase): """ input_1 = "None" - output_1 = binary_c_log_code(input_1, verbose=1) + output_1 = binary_c_log_code(input_1, verbosity=1) self.assertEqual(output_1, None, msg="Output should be None") input_2 = 'Printf("MY_STELLAR_DATA %g %g %g %g\\n",((double)stardata->model.time),((double)stardata->star[0].mass),((double)stardata->model.probability),((double)stardata->model.dt));' - output_2 = binary_c_log_code(input_2, verbose=1) + output_2 = binary_c_log_code(input_2, verbosity=1) test_value_2 = '#pragma push_macro("Max")\n#pragma push_macro("Min")\n#undef Max\n#undef Min\n#include "binary_c.h"\n\n// add visibility __attribute__ ((visibility ("default"))) to it \nvoid binary_c_API_function custom_output_function(struct stardata_t * stardata);\nvoid binary_c_API_function custom_output_function(struct stardata_t * stardata)\n{\n // struct stardata_t * stardata = (struct stardata_t *)x;\n Printf("MY_STELLAR_DATA %g %g %g %g\\n",((double)stardata->model.time),((double)stardata->star[0].mass),((double)stardata->model.probability),((double)stardata->model.dt));;\n}\n\n#undef Max \n#undef Min\n#pragma pop_macro("Min")\n#pragma pop_macro("Max") ' self.assertEqual( @@ -86,18 +85,18 @@ class test_custom_logging(unittest.TestCase): binary_c_write_log_code( input_1, - os.path.join(binary_c_temp_dir, "test_binary_c_write_log_code.txt"), - verbose=1, + os.path.join(TMP_DIR, "test_binary_c_write_log_code.txt"), + verbosity=1, ) self.assertTrue( os.path.isfile( - os.path.join(binary_c_temp_dir, "test_binary_c_write_log_code.txt") + os.path.join(TMP_DIR, "test_binary_c_write_log_code.txt") ), msg="File not created", ) with open( - os.path.join(binary_c_temp_dir, "test_binary_c_write_log_code.txt") + os.path.join(TMP_DIR, "test_binary_c_write_log_code.txt") ) as f: content_file = repr(f.read()) self.assertEqual(repr(input_1), content_file, msg="Contents are not similar") @@ -129,7 +128,7 @@ class test_custom_logging(unittest.TestCase): input_2 = "version" output_2 = from_binary_c_config(BINARY_C_CONFIG, input_2) - self.assertIn(output_2, ["2.1.7", "2.2pre1"], msg="binary_c version doesnt match") + self.assertIn(output_2, ["2.1.7", "2.2pre1", "2.2.0", "2.2.1"], msg="binary_c version doesnt match") def test_return_compilation_dict(self): with Capturing() as output: @@ -144,7 +143,7 @@ class test_custom_logging(unittest.TestCase): # Just going to check whether the dictionary has the components it needs # TODO: check whether we need to make this better - output = return_compilation_dict(verbose=1) + output = return_compilation_dict(verbosity=1) self.assertTrue("cc" in output) self.assertTrue("ld" in output) @@ -164,7 +163,7 @@ class test_custom_logging(unittest.TestCase): # input_1 = '#pragma push_macro("MAX")\n#pragma push_macro("MIN")\n#undef MAX\n#undef MIN\n#include "binary_c.h"\n\n// add visibility __attribute__ ((visibility ("default"))) to it \nvoid binary_c_API_function custom_output_function(struct stardata_t * stardata);\nvoid binary_c_API_function custom_output_function(struct stardata_t * stardata)\n{\n // struct stardata_t * stardata = (struct stardata_t *)x;\n Printf("MY_STELLAR_DATA %g %g %g %g\\n",((double)stardata->model.time),((double)stardata->star[0].mass),((double)stardata->model.probability),((double)stardata->model.dt));;\n}\n\n#undef MAX \n#undef MIN\n#pragma pop_macro("MIN")\n#pragma pop_macro("MAX") ' - output_1 = create_and_load_logging_function(input_1, verbose=1) + output_1 = create_and_load_logging_function(input_1, verbosity=1) self.assertTrue(isinstance(output_1[0], int), msg="memaddr is not an int") self.assertTrue(output_1[0] > 0, msg="memaddr is an int but not set correctly") diff --git a/binarycpython/tests/test_distributions.py b/binarycpython/tests/test_distributions.py index caeb87ffe3119f48359edd64cc4a51088d91d3a2..ba90e5d50fb9b2b4226f0588e990bea80fb3820b 100644 --- a/binarycpython/tests/test_distributions.py +++ b/binarycpython/tests/test_distributions.py @@ -6,8 +6,12 @@ import unittest from binarycpython.utils.distribution_functions import * from binarycpython.utils.useful_funcs import calc_sep_from_period -from binarycpython.utils.functions import Capturing +from binarycpython.utils.functions import ( + Capturing, + temp_dir +) +TMP_DIR = temp_dir("tests", "test_distributions") class TestDistributions(unittest.TestCase): """ diff --git a/binarycpython/tests/test_functions.py b/binarycpython/tests/test_functions.py index b576bf7f66ea5fa98a350c7a0084ab6bf79c18ee..4c15060dc6682aa76dd0b9f78930347ac6deb87b 100644 --- a/binarycpython/tests/test_functions.py +++ b/binarycpython/tests/test_functions.py @@ -8,19 +8,7 @@ from binarycpython.utils.functions import * from binarycpython.utils.custom_logging_functions import binary_c_log_code from binarycpython.utils.run_system_wrapper import run_system -binary_c_temp_dir = temp_dir() - -############################# -# Script that contains unit tests for functions from the binarycpython.utils.functions file - -# class test_(unittest.TestCase): -# """ -# Unittests for function -# """ - -# def test_1(self): -# pass - +TMP_DIR = temp_dir("tests", "test_functions") class dummy: """ @@ -82,11 +70,11 @@ class test_remove_file(unittest.TestCase): """ with open( - os.path.join(binary_c_temp_dir, "test_remove_file_file.txt"), "w" + os.path.join(TMP_DIR, "test_remove_file_file.txt"), "w" ) as f: f.write("test") - remove_file(os.path.join(binary_c_temp_dir, "test_remove_file_file.txt")) + remove_file(os.path.join(TMP_DIR, "test_remove_file_file.txt")) def test_remove_nonexisting_file(self): with Capturing() as output: @@ -97,7 +85,7 @@ class test_remove_file(unittest.TestCase): Test to try to remove a nonexistant file """ - file = os.path.join(binary_c_temp_dir, "test_remove_nonexistingfile_file.txt") + file = os.path.join(TMP_DIR, "test_remove_nonexistingfile_file.txt") remove_file(file) @@ -141,7 +129,7 @@ class test_create_hdf5(unittest.TestCase): Test that creates files, packs them in a hdf5 file and checks the contents """ - testdir = os.path.join(binary_c_temp_dir, "test_create_hdf5") + testdir = os.path.join(TMP_DIR, "test_create_hdf5") os.makedirs(testdir, exist_ok=True) # Create dummy settings file: @@ -178,7 +166,7 @@ class test_return_binary_c_version_info(unittest.TestCase): Test for the raw version_info output """ - version_info = return_binary_c_version_info() + version_info = return_binary_c_version_info(parsed=False) self.assertTrue(isinstance(version_info, str)) self.assertIn("Build", version_info) @@ -222,7 +210,7 @@ class test_parse_binary_c_version_info(unittest.TestCase): Test for the parsed versio info, more detailed """ - info = return_binary_c_version_info() + info = return_binary_c_version_info(parsed=False) parsed_info = parse_binary_c_version_info(info) self.assertIn("isotopes", parsed_info.keys()) @@ -600,7 +588,7 @@ class test_write_binary_c_parameter_descriptions_to_rst_file(unittest.TestCase): """ output_name = os.path.join( - binary_c_temp_dir, + TMP_DIR, "test_write_binary_c_parameter_descriptions_to_rst_file_test_1.txt", ) output_1 = write_binary_c_parameter_descriptions_to_rst_file(output_name) @@ -616,7 +604,7 @@ class test_write_binary_c_parameter_descriptions_to_rst_file(unittest.TestCase): """ output_name = os.path.join( - binary_c_temp_dir, + TMP_DIR, "test_write_binary_c_parameter_descriptions_to_rst_file_test_1.rst", ) output_1 = write_binary_c_parameter_descriptions_to_rst_file(output_name) diff --git a/binarycpython/tests/test_grid.py b/binarycpython/tests/test_grid.py index 5eb146ff8962e2c275718f81e14a7b3e6a9c3349..ce1bda06b48be6b565a12f2ca9391a7971171578 100644 --- a/binarycpython/tests/test_grid.py +++ b/binarycpython/tests/test_grid.py @@ -22,8 +22,7 @@ from binarycpython.utils.functions import ( ) from binarycpython.utils.custom_logging_functions import binary_c_log_code -binary_c_temp_dir = temp_dir() - +TMP_DIR = temp_dir("tests", "test_grid") TEST_VERBOSITY = 1 def parse_function_test_grid_evolve_2_threads_with_custom_logging(self, output): @@ -257,17 +256,17 @@ class test_Population(unittest.TestCase): self.assertIn("custom_options", population_settings) self.assertTrue(population_settings["custom_options"]["data_dir"] == "/tmp") - def test__return_binary_c_version_info(self): + def test_return_binary_c_version_info(self): with Capturing() as output: - self._test__return_binary_c_version_info() + self._test_return_binary_c_version_info() - def _test__return_binary_c_version_info(self): + def _test_return_binary_c_version_info(self): """ - Unittests for the function _return_binary_c_version_info + Unittests for the function return_binary_c_version_info """ test_pop = Population() - binary_c_version_info = test_pop._return_binary_c_version_info(parsed=True) + binary_c_version_info = test_pop.return_binary_c_version_info(parsed=True) self.assertTrue(isinstance(binary_c_version_info, dict)) self.assertIn("isotopes", binary_c_version_info) @@ -290,17 +289,17 @@ class test_Population(unittest.TestCase): if binary_c_version_info["macros"]["NUCSYN_ID_SOURCES"] == "on": self.assertIsNotNone(binary_c_version_info["nucleosynthesis_sources"]) - def test__return_binary_c_defaults(self): + def test_return_binary_c_defaults(self): with Capturing() as output: - self._test__return_binary_c_defaults() + self._test_return_binary_c_defaults() - def _test__return_binary_c_defaults(self): + def _test_return_binary_c_defaults(self): """ - Unittests for the function _return_binary_c_defaults + Unittests for the function return_binary_c_defaults """ test_pop = Population() - binary_c_defaults = test_pop._return_binary_c_defaults() + binary_c_defaults = test_pop.return_binary_c_defaults() self.assertIn("probability", binary_c_defaults) self.assertIn("phasevol", binary_c_defaults) self.assertIn("metallicity", binary_c_defaults) @@ -342,7 +341,7 @@ class test_Population(unittest.TestCase): test_pop.set(metallicity=0.02, verbosity=TEST_VERBOSITY) test_pop.set(M_1=10) test_pop.set(amt_cores=2) - test_pop.set(data_dir=binary_c_temp_dir) + test_pop.set(data_dir=TMP_DIR) # datadir settings_filename = test_pop.export_all_info(use_datadir=True) @@ -366,7 +365,7 @@ class test_Population(unittest.TestCase): # datadir settings_filename = test_pop.export_all_info( use_datadir=False, - outfile=os.path.join(binary_c_temp_dir, "example_settings.json"), + outfile=os.path.join(TMP_DIR, "example_settings.json"), ) self.assertTrue(os.path.isfile(settings_filename)) with open(settings_filename, "r") as f: @@ -389,7 +388,7 @@ class test_Population(unittest.TestCase): ValueError, test_pop.export_all_info, use_datadir=False, - outfile=os.path.join(binary_c_temp_dir, "example_settings.txt"), + outfile=os.path.join(TMP_DIR, "example_settings.txt"), ) def test__cleanup_defaults(self): @@ -440,7 +439,7 @@ class test_Population(unittest.TestCase): Unittests for the function _dict_from_line_source_file """ - source_file = os.path.join(binary_c_temp_dir, "example_source_file.txt") + source_file = os.path.join(TMP_DIR, "example_source_file.txt") # write with open(source_file, "w") as f: @@ -602,7 +601,7 @@ class test_grid_evolve(unittest.TestCase): Unittests to see if multiple threads do the custom logging correctly """ - data_dir_value = os.path.join(binary_c_temp_dir, "grid_tests") + data_dir_value = os.path.join(TMP_DIR, "grid_tests") amt_cores_value = 2 custom_logging_string = 'Printf("MY_STELLAR_DATA_TEST_EXAMPLE %g %g %g %g\\n",((double)stardata->model.time),((double)stardata->star[0].mass),((double)stardata->model.probability),((double)stardata->model.dt));' @@ -784,7 +783,7 @@ Printf("TEST_CUSTOM_LOGGING_1 %30.12e %g %g %g %g\\n", Unittests to see if multiple threads output the ensemble information to files correctly """ - data_dir_value = binary_c_temp_dir + data_dir_value = TMP_DIR amt_cores_value = 2 test_pop = Population() @@ -800,7 +799,7 @@ Printf("TEST_CUSTOM_LOGGING_1 %30.12e %g %g %g %g\\n", ensemble_dt=1000, ) test_pop.set( - data_dir=binary_c_temp_dir, + data_dir=TMP_DIR, ensemble_output_name="ensemble_output.json", combine_ensemble_with_thread_joining=False, ) @@ -858,7 +857,7 @@ Printf("TEST_CUSTOM_LOGGING_1 %30.12e %g %g %g %g\\n", Unittests to see if multiple threads correclty combine the ensemble data and store them in the grid """ - data_dir_value = binary_c_temp_dir + data_dir_value = TMP_DIR amt_cores_value = 2 test_pop = Population() @@ -874,7 +873,7 @@ Printf("TEST_CUSTOM_LOGGING_1 %30.12e %g %g %g %g\\n", ensemble_dt=1000, ) test_pop.set( - data_dir=binary_c_temp_dir, + data_dir=TMP_DIR, combine_ensemble_with_thread_joining=True, ensemble_output_name="ensemble_output.json", ) @@ -915,7 +914,7 @@ Printf("TEST_CUSTOM_LOGGING_1 %30.12e %g %g %g %g\\n", Unittests to compare the method of storing the combined ensemble data in the object and writing them to files and combining them later. they have to be the same """ - data_dir_value = binary_c_temp_dir + data_dir_value = TMP_DIR amt_cores_value = 2 # First @@ -932,7 +931,7 @@ Printf("TEST_CUSTOM_LOGGING_1 %30.12e %g %g %g %g\\n", ensemble_dt=1000, ) test_pop_1.set( - data_dir=binary_c_temp_dir, + data_dir=TMP_DIR, combine_ensemble_with_thread_joining=True, ensemble_output_name="ensemble_output.json", ) @@ -971,7 +970,7 @@ Printf("TEST_CUSTOM_LOGGING_1 %30.12e %g %g %g %g\\n", ensemble_dt=1000, ) test_pop_2.set( - data_dir=binary_c_temp_dir, + data_dir=TMP_DIR, ensemble_output_name="ensemble_output.json", combine_ensemble_with_thread_joining=False, ) diff --git a/binarycpython/tests/test_grid_options_defaults.py b/binarycpython/tests/test_grid_options_defaults.py index 8986e1ada77ac6f10c18e2064b31c7b46b66dc62..ae1ccb9124db5a771800078b0328d93920dc2b29 100644 --- a/binarycpython/tests/test_grid_options_defaults.py +++ b/binarycpython/tests/test_grid_options_defaults.py @@ -14,8 +14,7 @@ from binarycpython.utils.grid_options_defaults import ( grid_options_description_checker, ) -binary_c_temp_dir = temp_dir() - +TMP_DIR = temp_dir("tests", "test_grid_options_defaults") class test_grid_options_defaults(unittest.TestCase): """ @@ -79,13 +78,13 @@ class test_grid_options_defaults(unittest.TestCase): """ input_1 = os.path.join( - binary_c_temp_dir, "test_write_grid_options_to_rst_file_1.txt" + TMP_DIR, "test_write_grid_options_to_rst_file_1.txt" ) output_1 = write_grid_options_to_rst_file(input_1) self.assertIsNone(output_1) input_2 = os.path.join( - binary_c_temp_dir, "test_write_grid_options_to_rst_file_2.rst" + TMP_DIR, "test_write_grid_options_to_rst_file_2.rst" ) output_2 = write_grid_options_to_rst_file(input_2) diff --git a/binarycpython/tests/test_notebooks.py b/binarycpython/tests/test_notebooks.py new file mode 100644 index 0000000000000000000000000000000000000000..e90d2696f7dee2e53ac3d99d2dc4882ff7fe1864 --- /dev/null +++ b/binarycpython/tests/test_notebooks.py @@ -0,0 +1,78 @@ +import unittest + + +import nbformat +import os + +from nbconvert.preprocessors import ExecutePreprocessor + +from binarycpython.utils.functions import temp_dir + +TMP_DIR = temp_dir('testing', 'test_notebooks') +NOTEBOOKS_DIR = os.path.abspath(os.path.join(os.path.abspath(__file__), '../../../examples')) + +def run_notebook(notebook_path): + """ + Function to run notebooks and get the errors + """ + + # https://www.blog.pythonlibrary.org/2018/10/16/testing-jupyter-notebooks/ + nb_name, _ = os.path.splitext(os.path.basename(notebook_path)) + dirname = os.path.dirname(notebook_path) + + with open(notebook_path) as f: + nb = nbformat.read(f, as_version=4) + + proc = ExecutePreprocessor(timeout=600, kernel_name='python3') + proc.allow_errors = True + + proc.preprocess(nb, {'metadata': {'path': '/'}}) + + output_path = os.path.join(TMP_DIR, '{}_all_output.ipynb'.format(nb_name)) + + with open(output_path, mode='wt') as f: + nbformat.write(nb, f) + errors = [] + for cell in nb.cells: + if 'outputs' in cell: + for output in cell['outputs']: + if output.output_type == 'error': + errors.append(output) + + return nb, errors + +class TestNotebook(unittest.TestCase): + """ + Class that contains the notebook test calls + """ + + def test_notebook_api_functionality(self): + notebook_name = 'notebook_api_functionality.ipynb' + full_notebook_path = os.path.join(NOTEBOOKS_DIR, notebook_name) + nb, errors = run_notebook(full_notebook_path) + msg = "\nNotebook: {}\n\n".format(notebook_name) + "\n".join(["{}: {}\n{}".format(el['ename'], el['evalue'], '\n'.join(el['traceback'])) for el in errors]) + self.assertEqual(errors, [], msg=msg) + + def test_notebook_population(self): + notebook_name = 'notebook_population.ipynb' + full_notebook_path = os.path.join(NOTEBOOKS_DIR, notebook_name) + nb, errors = run_notebook(full_notebook_path) + msg = "\nNotebook: {}\n\n".format(notebook_name) + "\n".join(["{}: {}\n{}".format(el['ename'], el['evalue'], '\n'.join(el['traceback'])) for el in errors]) + self.assertEqual(errors, [], msg=msg) + + def test_notebook_individual_systems(self): + notebook_name = 'notebook_individual_systems.ipynb' + full_notebook_path = os.path.join(NOTEBOOKS_DIR, notebook_name) + nb, errors = run_notebook(full_notebook_path) + msg = "\nNotebook: {}\n\n".format(notebook_name) + "\n".join(["{}: {}\n{}".format(el['ename'], el['evalue'], '\n'.join(el['traceback'])) for el in errors]) + self.assertEqual(errors, [], msg=msg) + + def test_notebook_extra_features(self): + notebook_name = 'notebook_extra_features.ipynb' + full_notebook_path = os.path.join(NOTEBOOKS_DIR, notebook_name) + nb, errors = run_notebook(full_notebook_path) + msg = "\nNotebook: {}\n\n".format(notebook_name) + "\n".join(["{}: {}\n{}".format(el['ename'], el['evalue'], '\n'.join(el['traceback'])) for el in errors]) + self.assertEqual(errors, [], msg=msg) + +if __name__ == '__main__': + unittest.main() diff --git a/binarycpython/tests/test_plot_functions.py b/binarycpython/tests/test_plot_functions.py index 43b716e95d811cf78d88ca8b64a4aabdb42b391f..3e103e6b732a9ee9460b072ff700a7beb47cddd6 100644 --- a/binarycpython/tests/test_plot_functions.py +++ b/binarycpython/tests/test_plot_functions.py @@ -9,13 +9,7 @@ import matplotlib.pyplot as plt from binarycpython.utils.plot_functions import * from binarycpython.utils.functions import Capturing -# class test_(unittest.TestCase): -# """ -# Unittests for function -# """ -# def test_1(self): -# pass class test_color_by_index(unittest.TestCase): diff --git a/binarycpython/utils/functions.py b/binarycpython/utils/functions.py index d428ddd70ecc93bc399727b8b0eb11c1c0c00b8b..5a1caf4cee196eed44bf03d894d5371bb0711f6b 100644 --- a/binarycpython/utils/functions.py +++ b/binarycpython/utils/functions.py @@ -10,6 +10,7 @@ Tasks: import json import os +import gc import tempfile import copy import inspect @@ -73,6 +74,35 @@ def get_size(obj, seen=None): size += sum([get_size(i, seen) for i in obj]) return size + +def format_ensemble_results(ensemble_dictionary): + """ + Function to handle all the steps of formatting the ensemble output again. + + Input: + ensemble_dictionary: dictionary containing all the ensemble results + """ + + original_ensemble_results = ensemble_dictionary + + float_format_ensemble_results = recursive_change_key_to_float(original_ensemble_results) + del original_ensemble_results + gc.collect() + + # Then sort the dictionary + sorted_ensemble_results = custom_sort_dict(float_format_ensemble_results) + del float_format_ensemble_results + gc.collect() + + # Then Change the keys back to a string but with a %g format. + reformatted_ensemble_results = recursive_change_key_to_string(sorted_ensemble_results) + del sorted_ensemble_results + gc.collect() + + # Put back in the dictionary + return reformatted_ensemble_results + + def subtract_dicts(dict_1: dict, dict_2: dict) -> dict: """ Function to subtract two dictionaries. @@ -646,11 +676,19 @@ def parse_binary_c_version_info(version_info_string: str) -> dict: cleaned = cleaned - ensembles ensemble_dict = {} + ensemble_filter_dict = {} for el in ensembles: split_info = el.split("Ensemble ")[-1].split(" is ") + if len(split_info) > 1: - ensemble_dict[int(split_info[0])] = split_info[-1] + if not split_info[0].startswith('filter'): + ensemble_dict[int(split_info[0])] = split_info[-1] + else: + filter_no = int(split_info[0].replace('filter ', '')) + ensemble_filter_dict[filter_no] = split_info[-1] + version_info_dict["ensembles"] = ensemble_dict if ensemble_dict else None + version_info_dict["ensemble_filters"] = ensemble_filter_dict if ensemble_filter_dict else None ########################## # macros: diff --git a/binarycpython/utils/grid.py b/binarycpython/utils/grid.py index a77abd8c3c216a4b712cb1d215abf53c97f6dcd0..d027b8a4cd4858ad26a06fd98fef274922f47c57 100644 --- a/binarycpython/utils/grid.py +++ b/binarycpython/utils/grid.py @@ -72,6 +72,7 @@ from binarycpython.utils.functions import ( custom_sort_dict, recursive_change_key_to_string, multiply_values_dict, + format_ensemble_results, ) @@ -961,33 +962,6 @@ class Population: for _ in range(amt_cores): job_queue.put("STOP") - def format_ensemble_results(self, ensemble_dictionary): - """ - Function to handle all the steps of formatting the ensemble output again. - - Input: - ensemble_dictionary: dictionary containing all the ensemble results - """ - - original_ensemble_results = ensemble_dictionary - - float_format_ensemble_results = recursive_change_key_to_float(original_ensemble_results) - del original_ensemble_results - gc.collect() - - # Then sort the dictionary - sorted_ensemble_results = custom_sort_dict(float_format_ensemble_results) - del float_format_ensemble_results - gc.collect() - - # Then Change the keys back to a string but with a %g format. - reformatted_ensemble_results = recursive_change_key_to_string(sorted_ensemble_results) - del sorted_ensemble_results - gc.collect() - - # Put back in the dictionary - return reformatted_ensemble_results - def _evolve_population_grid(self): """ Function that handles running the population using multiprocessing. @@ -1049,7 +1023,7 @@ class Population: break # Extra ensemble result manipulation: - combined_output_dict['ensemble_results']['ensemble'] = self.format_ensemble_results(combined_output_dict['ensemble_results'].get('ensemble', {})) + combined_output_dict['ensemble_results']['ensemble'] = format_ensemble_results(combined_output_dict['ensemble_results'].get('ensemble', {})) gc.collect() # Take into account that we run this on multiple cores @@ -1118,6 +1092,7 @@ class Population: # print("thread {}: persistent_data_memaddr: ".format(self.process_ID), persistent_data_memaddr) # Get results binary_c + # print("running: {}".format(binary_cmdline_string)) out = _binary_c_bindings.run_system( argstring=binary_cmdline_string, custom_logging_func_memaddr=self.grid_options[ @@ -1410,11 +1385,6 @@ class Population: ensemble_json["ensemble"] = extract_ensemble_json_from_string( ensemble_raw_output ) # Load this into a dict so that we can combine it later - - # Extra ensemble result manipulation: - # We need to reformat and multiply by a factor - ensemble_json["ensemble"] = self.format_ensemble_results(ensemble_json["ensemble"]) - gc.collect() else: # If we do not allow this, automatically we will export this to the data_dir, in # some formatted way @@ -1425,9 +1395,11 @@ class Population: ), ) + # TODO: consider writing this in a formatted structure # Write to file with open(output_file, "w") as f: f.write(ensemble_raw_output) + # f.write(json.dumps(self.format_ensemble_results(ensemble_output))) print( "Thread {}: Wrote ensemble results directly to file: {}".format( diff --git a/examples/notebook_individual_systems.ipynb b/examples/notebook_individual_systems.ipynb index 19c2d29fbd316324eb39a361e897be2b835955e8..21d49016f3b2826d6f9d5ec87a1f6d8bb18df7af 100644 --- a/examples/notebook_individual_systems.ipynb +++ b/examples/notebook_individual_systems.ipynb @@ -28,6 +28,18 @@ { "cell_type": "code", "execution_count": 1, + "id": "e32dcdee", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from binarycpython.utils.functions import temp_dir\n", + "TMP_DIR = temp_dir(\"notebooks\", \"notebook_individual_systems\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, "id": "425efed3-d8e3-432d-829e-41d8ebe05162", "metadata": {}, "outputs": [], @@ -38,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "id": "b2abab48-433d-4936-8434-14804c52c9f6", "metadata": {}, "outputs": [ @@ -46,7 +58,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "SINGLE_STAR_LIFETIME 1 12462\n", + "SINGLE_STAR_LIFETIME 1 12461.9\n", "\n" ] } @@ -66,7 +78,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "id": "029fc3f2-f09a-49af-a32b-248505738f2e", "metadata": {}, "outputs": [ @@ -74,24 +86,25 @@ "name": "stdout", "output_type": "stream", "text": [ - " TIME M1 M2 K1 K2 SEP ECC R1/ROL1 R2/ROL2 TYPE RANDOM_SEED=67365 RANDOM_COUNT=0\n", - " 0.0000 1.000 0.000 1 15 -1 -1.00 0.000 0.000 \"INITIAL \"\n", - " 11003.1302 1.000 0.000 2 15 -1 -1.00 0.000 0.000 \"OFF_MS\"\n", - " 11003.1302 1.000 0.000 2 15 -1 -1.00 0.000 0.000 \"TYPE_CHNGE\"\n", - " 11582.2424 1.000 0.000 3 15 -1 -1.00 0.000 0.000 \"TYPE_CHNGE\"\n", - " 12325.1085 0.817 0.000 4 15 -1 -1.00 0.000 0.000 \"TYPE_CHNGE\"\n", - " 12457.1300 0.783 0.000 5 15 -1 -1.00 0.000 0.000 \"TYPE_CHNGE\"\n", - " 12460.8955 0.774 0.000 6 15 -1 -1.00 0.000 0.000 \"TYPE_CHNGE\"\n", - " 12460.8955 0.774 0.000 6 15 -1 -1.00 0.000 0.000 \"shrinkAGB\"\n", - " 12461.9514 0.523 0.000 11 15 -1 -1.00 0.000 0.000 \"TYPE_CHNGE\"\n", - " 15000.0000 0.523 0.000 11 15 -1 -1.00 0.000 0.000 \"MAX_TIME\"\n", + " TIME M1 M2 K1 K2 SEP PER ECC R1/ROL1 R2/ROL2 TYPE RANDOM_SEED=17089 RANDOM_COUNT=0\n", + " 0.0000 1.000 0.000 1 15 -1 -1 -1.00 0.000 0.000 \"INITIAL \"\n", + " 11003.1302 1.000 0.000 2 15 -1 -1 -1.00 0.000 0.000 \"OFF_MS\"\n", + " 11003.1302 1.000 0.000 2 15 -1 -1 -1.00 0.000 0.000 \"TYPE_CHNGE\"\n", + " 11582.2424 1.000 0.000 3 15 -1 -1 -1.00 0.000 0.000 \"TYPE_CHNGE\"\n", + " 12325.1085 0.817 0.000 4 15 -1 -1 -1.00 0.000 0.000 \"TYPE_CHNGE\"\n", + " 12457.1301 0.783 0.000 5 15 -1 -1 -1.00 0.000 0.000 \"TYPE_CHNGE\"\n", + " 12460.9983 0.716 0.000 6 15 -1 -1 -1.00 0.000 0.000 \"TYPE_CHNGE\"\n", + " 12460.9983 0.716 0.000 6 15 -1 -1 -1.00 0.000 0.000 \"shrinkAGB\"\n", + " 12461.9458 0.518 0.000 11 15 -1 -1 -1.00 0.000 0.000 \"TYPE_CHNGE\"\n", + " 15000.0000 0.518 0.000 11 15 -1 -1 -1.00 0.000 0.000 \"MAX_TIME\"\n", "\n" ] } ], "source": [ - "output = run_system(M_1=1, log_filename='/tmp/test_logfile.txt')\n", - "with open('/tmp/test_logfile.txt', 'r') as f:\n", + "log_filename = os.path.join(TMP_DIR, 'test_logfile.txt')\n", + "output = run_system(M_1=1, log_filename=log_filename, api_log_filename_prefix=TMP_DIR)\n", + "with open(log_filename, 'r') as f:\n", " print(f.read())" ] }, @@ -105,7 +118,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "e6a23b55-ca42-440d-83ac-e76a24a83a67", "metadata": { "tags": [] @@ -115,7 +128,10 @@ "name": "stdout", "output_type": "stream", "text": [ - "['EXAMPLE_MASSLOSS 0.000000000000e+00 1 1 1', 'EXAMPLE_MASSLOSS 0.000000000000e+00 1 1 1', 'EXAMPLE_MASSLOSS 1.000000000000e-06 1 1 1', 'EXAMPLE_MASSLOSS 2.000000000000e-06 1 1 1']\n" + "EXAMPLE_MASSLOSS 0.000000000000e+00 1 1 1\n", + "EXAMPLE_MASSLOSS 0.000000000000e+00 1 1 1\n", + "EXAMPLE_MASSLOSS 1.000000000000e-06 1 1 1\n", + "EXAMPLE_MASSLOSS 2.000000000000e-06 1 1 1\n" ] } ], @@ -137,8 +153,8 @@ "# Generate entire shared lib code around logging lines\n", "custom_logging_code = binary_c_log_code(custom_logging_print_statement)\n", "\n", - "output = run_system(M_1=1, custom_logging_code=custom_logging_code)\n", - "print(output.splitlines()[:4])" + "output = run_system(M_1=1, custom_logging_code=custom_logging_code, api_log_filename_prefix=TMP_DIR)\n", + "print('\\n'.join(output.splitlines()[:4]))" ] }, { @@ -151,7 +167,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "id": "3822721f-217a-495b-962e-d57137b9e290", "metadata": {}, "outputs": [ @@ -201,7 +217,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "id": "654a07ed-2a88-46ff-9da0-b7759580f9f3", "metadata": {}, "outputs": [ @@ -216,13 +232,13 @@ "4 2e-06 1 1 1\n", "5 3e-06 1 1 1\n", "... ... ... ... ...\n", - "1612 12461.8 0.577754 1 6\n", - "1613 12462 0.522806 1 11\n", - "1614 13462 0.522806 1 11\n", - "1615 14462 0.522806 1 11\n", - "1616 15000 0.522806 1 11\n", + "1617 12461.8 0.546683 1 6\n", + "1618 12461.9 0.517749 1 11\n", + "1619 13461.9 0.517749 1 11\n", + "1620 14461.9 0.517749 1 11\n", + "1621 15000 0.517749 1 11\n", "\n", - "[1616 rows x 4 columns]\n" + "[1621 rows x 4 columns]\n" ] } ], @@ -252,7 +268,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "id": "4a98ffca-1b72-4bb8-8df1-3bf3187d882f", "metadata": {}, "outputs": [], @@ -271,7 +287,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "id": "bff1cc2e-6b32-4ba0-879f-879ffbabd223", "metadata": {}, "outputs": [ @@ -279,11 +295,12 @@ "name": "stdout", "output_type": "stream", "text": [ + "adding: api_log_filename_prefix=/tmp/binary_c_python/notebooks/notebook_individual_systems to BSE_options\n", "adding: M_1=10 to BSE_options\n", "Creating and loading custom logging functionality\n", - "Running binary_c M_1 10\n", + "Running binary_c M_1 10 api_log_filename_prefix /tmp/binary_c_python/notebooks/notebook_individual_systems\n", "Cleaning up the custom logging stuff. type: single\n", - "SINGLE_STAR_LIFETIME 10 27.7358\n", + "SINGLE_STAR_LIFETIME 10 28.4838\n", "\n" ] } @@ -294,7 +311,8 @@ "\n", "# Set some parameters\n", "example_pop.set(\n", - " verbosity=1\n", + " verbosity=1,\n", + " api_log_filename_prefix=TMP_DIR\n", ")\n", "example_pop.set(\n", " M_1=10\n", @@ -315,7 +333,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 10, "id": "dd748bab-b57e-4129-8350-9ea11fa179d0", "metadata": { "scrolled": true, @@ -338,10 +356,13 @@ ");\n", " to grid_options\n", "Creating and loading custom logging functionality\n", - "Running binary_c M_1 10\n", + "Running binary_c M_1 10 api_log_filename_prefix /tmp/binary_c_python/notebooks/notebook_individual_systems\n", "Cleaning up the custom logging stuff. type: single\n", - "Removed /tmp/binary_c_python/custom_logging/libcustom_logging_eac2dfc438a14e5a9f5be98b1b6b4294.so\n", - "['EXAMPLE_MASSLOSS 0.000000000000e+00 10 0 10 1', 'EXAMPLE_MASSLOSS 0.000000000000e+00 10 10 10 1', 'EXAMPLE_MASSLOSS 1.000000000000e-06 10 10 10 1', 'EXAMPLE_MASSLOSS 2.000000000000e-06 10 10 10 1']\n" + "Removed /tmp/binary_c_python/custom_logging/libcustom_logging_8967553693ac4e11a49c42d4eef773e8.so\n", + "EXAMPLE_MASSLOSS 0.000000000000e+00 10 0 10 1\n", + "EXAMPLE_MASSLOSS 0.000000000000e+00 10 10 10 1\n", + "EXAMPLE_MASSLOSS 1.000000000000e-06 10 10 10 1\n", + "EXAMPLE_MASSLOSS 2.000000000000e-06 10 10 10 1\n" ] } ], @@ -362,7 +383,7 @@ "\n", "# get output and print\n", "output = example_pop.evolve_single()\n", - "print(output.splitlines()[:4])" + "print('\\n'.join(output.splitlines()[:4]))" ] }, { @@ -377,7 +398,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 11, "id": "fec39154-cce6-438c-8c2c-509d76b00f34", "metadata": {}, "outputs": [], @@ -428,7 +449,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 12, "id": "57347512-fd4a-434b-b13c-5e6dbd3ac415", "metadata": { "scrolled": true, @@ -439,12 +460,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "adding: parse_function=<function object_parse_function at 0x7f9265091598> to grid_options\n", - "<<<< Warning: Key does not match previously known parameter: adding: output_dir=/tmp/ to custom_options >>>>\n", + "adding: parse_function=<function object_parse_function at 0x7fb4d41ebbf8> to grid_options\n", + "<<<< Warning: Key does not match previously known parameter: adding: output_dir=/tmp/binary_c_python/notebooks/notebook_individual_systems to custom_options >>>>\n", + "adding: api_log_filename_prefix=/tmp/binary_c_python/notebooks/notebook_individual_systems to BSE_options\n", "Creating and loading custom logging functionality\n", - "Running binary_c M_1 10\n", + "Running binary_c M_1 10 api_log_filename_prefix /tmp/binary_c_python/notebooks/notebook_individual_systems\n", "Cleaning up the custom logging stuff. type: single\n", - "Removed /tmp/binary_c_python/custom_logging/libcustom_logging_e9c2bec7f15541eb847fc6013e48e7ed.so\n", + "Removed /tmp/binary_c_python/custom_logging/libcustom_logging_5d7779e8190e4b79b10c7e6a44cb0e7e.so\n", "[['time', 'mass', 'initial_mass', 'stellar_type'], [0.0, 10.0, 0.0, 10.0, 1.0], [0.0, 10.0, 10.0, 10.0, 1.0], [1e-06, 10.0, 10.0, 10.0, 1.0]]\n", "dict_keys(['time', 'mass', 'initial_mass', 'stellar_type'])\n" ] @@ -453,7 +475,8 @@ "source": [ "example_pop.set(\n", " parse_function=object_parse_function,\n", - " output_dir='/tmp/'\n", + " output_dir=TMP_DIR,\n", + " api_log_filename_prefix=TMP_DIR\n", ")\n", "output = example_pop.evolve_single()\n", "print(output[:4])\n", @@ -488,7 +511,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 13, "id": "ec48125c-6bf5-48f4-9357-8261800b5d8b", "metadata": {}, "outputs": [ @@ -496,7 +519,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "SINGLE_STAR_LIFETIME 15 14.2383\n", + "SINGLE_STAR_LIFETIME 15 14.9947\n", "\n" ] } @@ -510,10 +533,11 @@ "eccentricity = 0.0\n", "metallicity = 0.02\n", "max_evolution_time = 15000 # Myr. You need to include this argument.\n", + "api_log_filename_prefix = TMP_DIR\n", "\n", "# Here we set up the argument string that is passed to the bindings\n", "argstring = \"\"\"\n", - "binary_c M_1 {M_1} M_2 {M_2} separation {separation} orbital_period {orbital_period} eccentricity {eccentricity} metallicity {metallicity} max_evolution_time {max_evolution_time}\n", + "binary_c M_1 {M_1} M_2 {M_2} separation {separation} orbital_period {orbital_period} eccentricity {eccentricity} metallicity {metallicity} max_evolution_time {max_evolution_time} api_log_filename_prefix {api_log_filename_prefix}\n", "\"\"\".format(\n", " M_1=M_1,\n", " M_2=M_2,\n", @@ -522,6 +546,7 @@ " eccentricity=eccentricity,\n", " metallicity=metallicity,\n", " max_evolution_time=max_evolution_time,\n", + " api_log_filename_prefix=TMP_DIR\n", ").strip()\n", "\n", "from binarycpython import _binary_c_bindings\n", diff --git a/examples/notebook_population.ipynb b/examples/notebook_population.ipynb index 56f3b153c23064e82863f188906d50243e795715..b6a37baa8d90a2f36c0fd19311548ed49e77f173 100644 --- a/examples/notebook_population.ipynb +++ b/examples/notebook_population.ipynb @@ -3,7 +3,9 @@ { "cell_type": "markdown", "id": "bbbaafbb-fd7d-4b73-a970-93506ba35d71", - "metadata": {}, + "metadata": { + "tags": [] + }, "source": [ "# Running populations with binarycpython\n", "This notebook will show you how to evolve a population of stars\n", @@ -27,15 +29,6 @@ "# help(Population) # Uncomment to see the public functions of this object" ] }, - { - "cell_type": "markdown", - "id": "a081ab23-7822-4971-aa82-991548534714", - "metadata": {}, - "source": [ - "- running ensemble\n", - "- using M&S grid" - ] - }, { "cell_type": "markdown", "id": "f268eff3-4e08-4f6b-8b59-f22dba4d2074", @@ -329,7 +322,7 @@ ], "source": [ "# Add grid variables\n", - "resolution = {\"M_1\": 20, \"q\": 20, \"per\": 40}\n", + "resolution = {\"M_1\": 20}\n", "\n", "# Mass\n", "example_pop.add_grid_variable(\n", @@ -1125,7 +1118,7 @@ ")\n", "\n", "# Add grid variables\n", - "resolution = {\"M_1\": 5, \"q\": 5, \"per\": 5}\n", + "resolution = {\"M_1\": 3, \"q\": 3, \"per\": 3}\n", "\n", "# Mass\n", "example_pop.add_grid_variable(\n", diff --git a/setup.py b/setup.py index f79e7108a223e6daa90f90497b67f3e407871b49..619bb01b2920a7b83d6503cb64167fe64e49c97f 100644 --- a/setup.py +++ b/setup.py @@ -64,7 +64,7 @@ def execute_make(): ### -REQUIRED_BINARY_C_VERSIONS = ["2.1.7", "2.2pre1", "2.2.0"] +REQUIRED_BINARY_C_VERSIONS = ["2.1.7", "2.2pre1", "2.2.0", "2.2.1"] #### GSL_DIR = os.getenv("GSL_DIR", None)