diff --git a/docs/notebooks/monte_carlo_analysis/monte_carlo_class_usage.ipynb b/docs/notebooks/monte_carlo_analysis/monte_carlo_class_usage.ipynb index 2fb46fa86..e2ac41747 100644 --- a/docs/notebooks/monte_carlo_analysis/monte_carlo_class_usage.ipynb +++ b/docs/notebooks/monte_carlo_analysis/monte_carlo_class_usage.ipynb @@ -800,6 +800,34 @@ ")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also export our Monte Carlo results into .csv and .json files using the method `MonteCarlo.export_results()`.\n", + "\n", + "Choose a name for the output file and select a format to output." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Results saved to monte_carlo_analysis_outputs/monte_carlo_csv_output_example as .csv file\n", + "Results saved to monte_carlo_analysis_outputs/monte_carlo_json_output_example as .json file\n" + ] + } + ], + "source": [ + "test_dispersion.export_results(\"monte_carlo_analysis_outputs/monte_carlo_csv_output_example\", \"csv\")\n", + "test_dispersion.export_results(\"monte_carlo_analysis_outputs/monte_carlo_json_output_example\", \"json\")" + ] + }, { "attachments": {}, "cell_type": "markdown", diff --git a/rocketpy/simulation/monte_carlo.py b/rocketpy/simulation/monte_carlo.py index e10789a7d..c0b05634b 100644 --- a/rocketpy/simulation/monte_carlo.py +++ b/rocketpy/simulation/monte_carlo.py @@ -14,6 +14,7 @@ """ import json +import csv import os import traceback import warnings @@ -588,6 +589,41 @@ def __evaluate_flight_outputs(self, flight, sim_idx): json.dumps(outputs_dict, cls=RocketPyEncoder, **self._export_config) + "\n" ) + def export_results(self, output_filename, output_format): + """Converts the default Monte Carlo .txt output to .cvs or .json file + depending on the user's choice + + Parameters + ---------- + output_filename : str + Name of the file in which the converted data will be saved + output_format : str + Format of the output file + + Returns + ------- + None + """ + txt_data = [] + with open(f"{self.filename}.outputs.txt", "r", encoding="utf-8") as f: + for line in f: + line = line.strip() + data = json.loads(line) + txt_data.append(data) + + output_format = output_format.strip().lower() + if output_format == "json": + with open(f"{output_filename}.json", "w", encoding="utf-8") as f: + json.dump(txt_data, f, indent=4) + _SimMonitor.reprint(f"Results saved to {Path(output_filename)} as .json file") + elif output_format == "csv": + output_csv_header = txt_data[0].keys() + with open(f"{output_filename}.csv", "w", newline= "") as f: + output_writer = csv.DictWriter(f, fieldnames=output_csv_header) + output_writer.writeheader() + output_writer.writerows(txt_data) + _SimMonitor.reprint(f"Results saved to {Path(output_filename)} as .csv file") + def __terminate_simulation(self): """ Terminates the simulation, closes the files and prints the results. diff --git a/tests/unit/simulation/test_monte_carlo.py b/tests/unit/simulation/test_monte_carlo.py index 5595e46bb..8398d4b3e 100644 --- a/tests/unit/simulation/test_monte_carlo.py +++ b/tests/unit/simulation/test_monte_carlo.py @@ -1,6 +1,8 @@ import matplotlib as plt import numpy as np import pytest +import json +import os from rocketpy.simulation import MonteCarlo @@ -185,3 +187,31 @@ def test_estimate_confidence_interval_raises_type_error_for_invalid_statistic(): with pytest.raises(TypeError): mc.estimate_confidence_interval("apogee", statistic="not_a_function") + + +def test_monte_carlo_export_results_to_csv_and_json_files(monte_carlo_calisto, tmp_path): + """Checks that the export_results create .csv and .json files + + Parameters + ---------- + monte_carlo_calisto : MonteCarlo + Fixture that has the .txt files necessary for the export_results + """ + try: + mc = monte_carlo_calisto + mc.filename = tmp_path / "mock_output" + mock_data = {"apogee": 100, "max_velocity": 255} + with open(tmp_path / "mock_output.outputs.txt", "w") as f: + f.write(json.dumps(mock_data) + "\n") + + mc.export_results(tmp_path / "mock_outputs_in_csv", "csv") + expected_file_in_csv = tmp_path / f"{"mock_outputs_in_csv"}.csv" + assert expected_file_in_csv.exists() + + mc.export_results(tmp_path / "mock_output_in_json", "json") + expected_file_in_json = tmp_path / f"{"mock_output_in_json"}.json" + assert expected_file_in_json.exists() + finally: + os.remove("monte_carlo_test.errors.txt") + os.remove("monte_carlo_test.inputs.txt") + os.remove("monte_carlo_test.outputs.txt")