diff --git a/src/acom_music_box/__init__.py b/src/acom_music_box/__init__.py index f7274688..8a7a0bff 100644 --- a/src/acom_music_box/__init__.py +++ b/src/acom_music_box/__init__.py @@ -19,4 +19,4 @@ from .evolving_conditions import EvolvingConditions from .music_box import MusicBox -from .examples import Examples \ No newline at end of file +from .examples import Examples diff --git a/src/acom_music_box/examples/__init__.py b/src/acom_music_box/examples/__init__.py index cccf6e33..e917f239 100644 --- a/src/acom_music_box/examples/__init__.py +++ b/src/acom_music_box/examples/__init__.py @@ -1 +1 @@ -from .examples import Examples \ No newline at end of file +from .examples import Examples diff --git a/src/acom_music_box/examples/examples.py b/src/acom_music_box/examples/examples.py index b6e1a3db..e3dbc335 100644 --- a/src/acom_music_box/examples/examples.py +++ b/src/acom_music_box/examples/examples.py @@ -1,41 +1,43 @@ import os + class Example: def __init__(self, name, short_name, description, path): self.name = name self.short_name = short_name self.description = description self.path = path - + def __str__(self): return f'{self.name}: {self.description}' - + def __repr__(self): return f'{self.name}: {self.description}' @classmethod def from_config(cls, display_name, folder_name, short_name, description): path = os.path.join(os.path.dirname(__file__), 'configs', folder_name, 'my_config.json') - return cls(name=display_name, short_name = short_name, description=description, path=path) + return cls(name=display_name, short_name=short_name, description=description, path=path) + class _Examples: CarbonBond5 = Example.from_config( - display_name='Carbon Bond IV', + display_name='Carbon Bond IV', short_name='CB5', folder_name='carbon_bond_5', description='Carbon bond 5') Chapman = Example.from_config( - display_name='Chapman', + display_name='Chapman', short_name='Chapman', folder_name='chapman', description='The Chapman cycle with conditions over Boulder, Colorado') FlowTube = Example.from_config( - display_name='Flow Tube', + display_name='Flow Tube', short_name='FlowTube', folder_name='flow_tube', description='A fictitious flow tube experiment') Analytical = Example.from_config( - display_name='Analytical', + display_name='Analytical', short_name='Analytical', folder_name='analytical', description='An example of an analytical solution to a simple chemical system') @@ -52,4 +54,5 @@ def __getattr__(self, item): return getattr(self, item) raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'") -Examples = _Examples() \ No newline at end of file + +Examples = _Examples() diff --git a/src/acom_music_box/main.py b/src/acom_music_box/main.py index e1baa9f8..83c101ed 100644 --- a/src/acom_music_box/main.py +++ b/src/acom_music_box/main.py @@ -8,9 +8,11 @@ import subprocess import tempfile + def format_examples_help(examples): return '\n'.join(f"{e.short_name}: {e.description}" for e in examples) + def parse_arguments(): parser = argparse.ArgumentParser( description='MusicBox simulation runner.', @@ -50,6 +52,7 @@ def parse_arguments(): ) return parser.parse_args() + def setup_logging(verbosity, color_output): if verbosity >= 2: log_level = logging.DEBUG @@ -58,9 +61,19 @@ def setup_logging(verbosity, color_output): else: log_level = logging.CRITICAL - formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(module)s.%(funcName)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S') + formatter = logging.Formatter( + '%(asctime)s - %(levelname)s - %(module)s.%(funcName)s - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S') if color_output: - color_formatter = colorlog.ColoredFormatter('%(log_color)s%(asctime)s - %(levelname)s - %(module)s.%(funcName)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S', log_colors={'DEBUG': 'green', 'INFO': 'cyan', 'WARNING': 'yellow', 'ERROR': 'red', 'CRITICAL': 'bold_red'}) + color_formatter = colorlog.ColoredFormatter( + '%(log_color)s%(asctime)s - %(levelname)s - %(module)s.%(funcName)s - %(message)s', + datefmt='%Y-%m-%d %H:%M:%S', + log_colors={ + 'DEBUG': 'green', + 'INFO': 'cyan', + 'WARNING': 'yellow', + 'ERROR': 'red', + 'CRITICAL': 'bold_red'}) console_handler = logging.StreamHandler() console_handler.setLevel(log_level) console_handler.setFormatter(color_formatter) @@ -71,20 +84,23 @@ def setup_logging(verbosity, color_output): console_handler.setFormatter(formatter) logging.basicConfig(level=log_level, handlers=[console_handler]) + def plot_with_gnuplot(data, species_list): # Prepare columns and data for plotting columns = ['time'] + species_list data_to_plot = data[columns] - + data_csv = data_to_plot.to_csv(index=False) - + try: with tempfile.NamedTemporaryFile(delete=False, suffix='.csv') as data_file: data_file.write(data_csv.encode()) data_file_path = data_file.name - - plot_commands = ',\n\t'.join(f"'{data_file_path}' using 1:{i+2} with lines title '{species}'" for i, species in enumerate(species_list)) - + + plot_commands = ',\n\t'.join( + f"'{data_file_path}' using 1:{i+2} with lines title '{species}'" for i, + species in enumerate(species_list)) + gnuplot_command = f""" set datafile separator ","; set terminal dumb size 120,25; @@ -93,7 +109,7 @@ def plot_with_gnuplot(data, species_list): set title 'Time vs Species'; plot {plot_commands} """ - + subprocess.run(['gnuplot', '-e', gnuplot_command], check=True) except FileNotFoundError: logging.critical("gnuplot is not installed. Skipping plotting.") @@ -104,6 +120,7 @@ def plot_with_gnuplot(data, species_list): if data_file_path: os.remove(data_file_path) + def main(): start = datetime.datetime.now() @@ -138,10 +155,10 @@ def main(): myBox.config_file) myBox.create_solver(config_path) result = myBox.solve(musicBoxOutputPath) - + if musicBoxOutputPath is None: print(result.to_csv(index=False)) - + if plot_species_list: # Prepare data for plotting plot_with_gnuplot(result, plot_species_list) @@ -152,5 +169,6 @@ def main(): sys.exit(0) + if __name__ == "__main__": main() diff --git a/src/acom_music_box/music_box.py b/src/acom_music_box/music_box.py index 14245bb7..bad0f43e 100644 --- a/src/acom_music_box/music_box.py +++ b/src/acom_music_box/music_box.py @@ -562,7 +562,7 @@ def solve(self, output_path=None): output_path = os.path.join(os.getcwd(), output_path) elif not os.path.basename(output_path): raise ValueError(f"Invalid output path: '{output_path}' does not contain a filename.") - + # Ensure the directory exists dir_path = os.path.dirname(output_path) if dir_path and not os.path.exists(dir_path): diff --git a/tests/test_analytical.py b/tests/test_analytical.py index de0fd464..b1bf43fe 100644 --- a/tests/test_analytical.py +++ b/tests/test_analytical.py @@ -3,6 +3,7 @@ import math + class TestAnalytical: def test_run(self): box_model = MusicBox()