diff --git a/.all-contributorsrc b/.all-contributorsrc
index 2456085b32..405ff36569 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -442,7 +442,7 @@
"login": "wigging",
"name": "Gavin Wiggins",
"avatar_url": "https://avatars.githubusercontent.com/u/6828967?v=4",
- "profile": "https://wigging.me",
+ "profile": "https://gavinw.me",
"contributions": [
"bug",
"code"
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
new file mode 100644
index 0000000000..c3575a50f8
--- /dev/null
+++ b/.github/workflows/docker.yml
@@ -0,0 +1,75 @@
+name: Build & Push Docker Images
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - develop
+
+jobs:
+ pre_job:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Login to DockerHub
+ uses: docker/login-action@v3
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+
+ - name: List built images
+ run: docker images
+
+ - name: Build and Push Docker Image (Without Solvers)
+ uses: docker/build-push-action@v5
+ with:
+ context: .
+ file: scripts/Dockerfile
+ tags: pybamm/pybamm:latest
+ push: true
+
+ - name: Build and Push Docker Image (With JAX Solver)
+ uses: docker/build-push-action@v5
+ with:
+ context: .
+ file: scripts/Dockerfile
+ tags: pybamm/pybamm:jax
+ push: true
+ build-args: |
+ JAX=true
+
+ - name: Build and Push Docker Image (With ODES & DAE Solver)
+ uses: docker/build-push-action@v5
+ with:
+ context: .
+ file: scripts/Dockerfile
+ tags: pybamm/pybamm:odes
+ push: true
+ build-args: |
+ ODES=true
+
+ - name: Build and Push Docker Image (With IDAKLU Solver)
+ uses: docker/build-push-action@v5
+ with:
+ context: .
+ file: scripts/Dockerfile
+ tags: pybamm/pybamm:idaklu
+ push: true
+ build-args: |
+ IDAKLU=true
+
+ - name: Build and Push Docker Image (With All Solvers)
+ uses: docker/build-push-action@v5
+ with:
+ context: .
+ file: scripts/Dockerfile
+ tags: pybamm/pybamm:latest
+ push: true
+ build-args: |
+ ALL=true
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a6aa6987c2..bec0fee02a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -44,7 +44,7 @@ You now have everything you need to start making changes!
### B. Writing your code
-6. PyBaMM is developed in [Python](https://en.wikipedia.org/wiki/Python_(programming_language)), and makes heavy use of [NumPy](https://en.wikipedia.org/wiki/NumPy) (see also [NumPy for MatLab users](https://numpy.org/doc/stable/user/numpy-for-matlab-users.html) and [Python for R users](http://blog.hackerearth.com/how-can-r-users-learn-python-for-data-science)).
+6. PyBaMM is developed in [Python](https://en.wikipedia.org/wiki/Python_(programming_language)), and makes heavy use of [NumPy](https://en.wikipedia.org/wiki/NumPy) (see also [NumPy for MatLab users](https://numpy.org/doc/stable/user/numpy-for-matlab-users.html) and [Python for R users](https://www.rebeccabarter.com/blog/2023-09-11-from_r_to_python)).
7. Make sure to follow our [coding style guidelines](#coding-style-guidelines).
8. Commit your changes to your branch with [useful, descriptive commit messages](https://chris.beams.io/posts/git-commit/): Remember these are publicly visible and should still make sense a few months ahead in time. While developing, you can keep using the GitHub issue you're working on as a place for discussion. [Refer to your commits](https://stackoverflow.com/questions/8910271/how-can-i-reference-a-commit-in-an-issue-comment-on-github) when discussing specific lines of code.
9. If you want to add a dependency on another library, or re-use code you found somewhere else, have a look at [these guidelines](#dependencies-and-reusing-code).
@@ -185,7 +185,7 @@ You may also test multiple notebooks this way. Passing the path to a folder will
nox -s examples -- docs/source/examples/notebooks/models/
```
-You may also use an appropriate [glob pattern](https://www.malikbrowne.com/blog/a-beginners-guide-glob-patterns) to run all notebooks matching a particular folder or name pattern.
+You may also use an appropriate [glob pattern](https://docs.python.org/3/library/glob.html) to run all notebooks matching a particular folder or name pattern.
To edit the structure and how the Jupyter notebooks get rendered in the Sphinx documentation (using `nbsphinx`), install [Pandoc](https://pandoc.org/installing.html) on your system, either using `conda` (through the `conda-forge` channel)
diff --git a/README.md b/README.md
index a4bb69ac7c..9f45d34327 100644
--- a/README.md
+++ b/README.md
@@ -233,7 +233,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Chuck Liu 🐛 💻 |
partben 📖 |
- Gavin Wiggins 🐛 💻 |
+ Gavin Wiggins 🐛 💻 |
Dion Wilde 🐛 💻 |
Elias Hohl 💻 |
KAschad 🐛 |
diff --git a/tests/unit/test_batch_study.py b/tests/unit/test_batch_study.py
index c04e71b5cb..f8762133a6 100644
--- a/tests/unit/test_batch_study.py
+++ b/tests/unit/test_batch_study.py
@@ -5,6 +5,7 @@
import os
import pybamm
import unittest
+from tempfile import TemporaryDirectory
spm = pybamm.lithium_ion.SPM()
spm_uniform = pybamm.lithium_ion.SPM({"particle": "uniform profile"})
@@ -90,21 +91,19 @@ def test_solve(self):
self.assertIn(output_experiment, experiments_list)
def test_create_gif(self):
- bs = pybamm.BatchStudy({"spm": pybamm.lithium_ion.SPM()})
- bs.solve([0, 10])
+ with TemporaryDirectory() as dir_name:
+ bs = pybamm.BatchStudy({"spm": pybamm.lithium_ion.SPM()})
+ bs.solve([0, 10])
- # Create a temporary file name
- test_stub = "batch_study_test"
- test_file = f"{test_stub}.gif"
+ # Create a temporary file name
+ test_file = os.path.join(dir_name, "batch_study_test.gif")
- # create a GIF before calling the plot method
- bs.create_gif(number_of_images=3, duration=1, output_filename=test_file)
+ # create a GIF before calling the plot method
+ bs.create_gif(number_of_images=3, duration=1, output_filename=test_file)
- # create a GIF after calling the plot method
- bs.plot(testing=True)
- bs.create_gif(number_of_images=3, duration=1, output_filename=test_file)
-
- os.remove(test_file)
+ # create a GIF after calling the plot method
+ bs.plot(testing=True)
+ bs.create_gif(number_of_images=3, duration=1, output_filename=test_file)
if __name__ == "__main__":
diff --git a/tests/unit/test_expression_tree/test_symbol.py b/tests/unit/test_expression_tree/test_symbol.py
index 3a74375ce7..3f91633fbe 100644
--- a/tests/unit/test_expression_tree/test_symbol.py
+++ b/tests/unit/test_expression_tree/test_symbol.py
@@ -4,6 +4,7 @@
from tests import TestCase
import os
import unittest
+from tempfile import TemporaryDirectory
import numpy as np
from scipy.sparse import csr_matrix, coo_matrix
@@ -386,13 +387,16 @@ def test_symbol_repr(self):
)
def test_symbol_visualise(self):
- c = pybamm.Variable("c", "negative electrode")
- d = pybamm.Variable("d", "negative electrode")
- sym = pybamm.div(c * pybamm.grad(c)) + (c / d + c - d) ** 5
- sym.visualise("test_visualize.png")
- self.assertTrue(os.path.exists("test_visualize.png"))
- with self.assertRaises(ValueError):
- sym.visualise("test_visualize")
+ with TemporaryDirectory() as dir_name:
+ test_stub = os.path.join(dir_name, "test_visualize")
+ test_name = f"{test_stub}.png"
+ c = pybamm.Variable("c", "negative electrode")
+ d = pybamm.Variable("d", "negative electrode")
+ sym = pybamm.div(c * pybamm.grad(c)) + (c / d + c - d) ** 5
+ sym.visualise(test_name)
+ self.assertTrue(os.path.exists(test_name))
+ with self.assertRaises(ValueError):
+ sym.visualise(test_stub)
def test_has_spatial_derivatives(self):
var = pybamm.Variable("var", domain="test")
diff --git a/tests/unit/test_parameters/test_lead_acid_parameters.py b/tests/unit/test_parameters/test_lead_acid_parameters.py
index e0151f0e7b..ddc73f61ee 100644
--- a/tests/unit/test_parameters/test_lead_acid_parameters.py
+++ b/tests/unit/test_parameters/test_lead_acid_parameters.py
@@ -1,10 +1,11 @@
#
# Test for the standard lead acid parameters
#
+import os
from tests import TestCase
import pybamm
from tests import get_discretisation_for_testing
-
+from tempfile import TemporaryDirectory
import unittest
@@ -15,10 +16,11 @@ def test_scipy_constants(self):
self.assertAlmostEqual(constants.F.evaluate(), 96485, places=0)
def test_print_parameters(self):
- parameters = pybamm.LeadAcidParameters()
- parameter_values = pybamm.lead_acid.BaseModel().default_parameter_values
- output_file = "lead_acid_parameters.txt"
- parameter_values.print_parameters(parameters, output_file)
+ with TemporaryDirectory() as dir_name:
+ parameters = pybamm.LeadAcidParameters()
+ parameter_values = pybamm.lead_acid.BaseModel().default_parameter_values
+ output_file = os.path.join(dir_name, "lead_acid_parameters.txt")
+ parameter_values.print_parameters(parameters, output_file)
def test_parameters_defaults_lead_acid(self):
# Load parameters to be tested
diff --git a/tests/unit/test_parameters/test_lithium_ion_parameters.py b/tests/unit/test_parameters/test_lithium_ion_parameters.py
index 9d9d892300..0c46eec16e 100644
--- a/tests/unit/test_parameters/test_lithium_ion_parameters.py
+++ b/tests/unit/test_parameters/test_lithium_ion_parameters.py
@@ -1,19 +1,21 @@
#
-# Tests lithium ion parameters load and give expected values
+# Tests lithium-ion parameters load and give expected values
#
+import os
from tests import TestCase
import pybamm
-
+from tempfile import TemporaryDirectory
import unittest
import numpy as np
class TestLithiumIonParameterValues(TestCase):
def test_print_parameters(self):
- parameters = pybamm.LithiumIonParameters()
- parameter_values = pybamm.lithium_ion.BaseModel().default_parameter_values
- output_file = "lithium_ion_parameters.txt"
- parameter_values.print_parameters(parameters, output_file)
+ with TemporaryDirectory() as dir_name:
+ parameters = pybamm.LithiumIonParameters()
+ parameter_values = pybamm.lithium_ion.BaseModel().default_parameter_values
+ output_file = os.path.join(dir_name, "lithium_ion_parameters.txt")
+ parameter_values.print_parameters(parameters, output_file)
def test_lithium_ion(self):
"""This test checks that all the parameters are being calculated
diff --git a/tests/unit/test_plotting/test_quick_plot.py b/tests/unit/test_plotting/test_quick_plot.py
index 8304e89583..f569f00152 100644
--- a/tests/unit/test_plotting/test_quick_plot.py
+++ b/tests/unit/test_plotting/test_quick_plot.py
@@ -3,6 +3,7 @@
import unittest
from tests import TestCase
import numpy as np
+from tempfile import TemporaryDirectory
class TestQuickPlot(TestCase):
@@ -290,12 +291,13 @@ def test_spm_simulation(self):
quick_plot.plot(0)
# test creating a GIF
- test_stub = "spm_sim_test"
- test_file = f"{test_stub}.gif"
- quick_plot.create_gif(number_of_images=3, duration=3, output_filename=test_file)
- assert not os.path.exists(f"{test_stub}*.png")
- assert os.path.exists(test_file)
- os.remove(test_file)
+ with TemporaryDirectory() as dir_name:
+ test_stub = os.path.join(dir_name, "spm_sim_test")
+ test_file = f"{test_stub}.gif"
+ quick_plot.create_gif(number_of_images=3, duration=3,
+ output_filename=test_file)
+ assert not os.path.exists(f"{test_stub}*.png")
+ assert os.path.exists(test_file)
pybamm.close_plots()
def test_loqs_spme(self):
diff --git a/tests/unit/test_simulation.py b/tests/unit/test_simulation.py
index f008c7ff2a..c98586ee59 100644
--- a/tests/unit/test_simulation.py
+++ b/tests/unit/test_simulation.py
@@ -6,6 +6,7 @@
import sys
import unittest
import uuid
+from tempfile import TemporaryDirectory
class TestSimulation(TestCase):
@@ -248,32 +249,37 @@ def test_step_with_inputs(self):
)
def test_save_load(self):
- model = pybamm.lead_acid.LOQS()
- model.use_jacobian = True
- sim = pybamm.Simulation(model)
-
- sim.save("test.pickle")
- sim_load = pybamm.load_sim("test.pickle")
- self.assertEqual(sim.model.name, sim_load.model.name)
-
- # save after solving
- sim.solve([0, 600])
- sim.save("test.pickle")
- sim_load = pybamm.load_sim("test.pickle")
- self.assertEqual(sim.model.name, sim_load.model.name)
+ with TemporaryDirectory() as dir_name:
+ test_name = os.path.join(dir_name, "tests.pickle")
+
+ model = pybamm.lead_acid.LOQS()
+ model.use_jacobian = True
+ sim = pybamm.Simulation(model)
+
+ sim.save(test_name)
+ sim_load = pybamm.load_sim(test_name)
+ self.assertEqual(sim.model.name, sim_load.model.name)
+
+ # save after solving
+ sim.solve([0, 600])
+ sim.save(test_name)
+ sim_load = pybamm.load_sim(test_name)
+ self.assertEqual(sim.model.name, sim_load.model.name)
+
+ # with python formats
+ model.convert_to_format = None
+ sim = pybamm.Simulation(model)
+ sim.solve([0, 600])
+ sim.save(test_name)
+ model.convert_to_format = "python"
+ sim = pybamm.Simulation(model)
+ sim.solve([0, 600])
+ with self.assertRaisesRegex(
+ NotImplementedError,
+ "Cannot save simulation if model format is python"
+ ):
+ sim.save(test_name)
- # with python formats
- model.convert_to_format = None
- sim = pybamm.Simulation(model)
- sim.solve([0, 600])
- sim.save("test.pickle")
- model.convert_to_format = "python"
- sim = pybamm.Simulation(model)
- sim.solve([0, 600])
- with self.assertRaisesRegex(
- NotImplementedError, "Cannot save simulation if model format is python"
- ):
- sim.save("test.pickle")
def test_load_param(self):
# Test load_sim for parameters imports
@@ -299,33 +305,36 @@ def test_load_param(self):
os.remove(filename)
def test_save_load_dae(self):
- model = pybamm.lead_acid.LOQS({"surface form": "algebraic"})
- model.use_jacobian = True
- sim = pybamm.Simulation(model)
-
- # save after solving
- sim.solve([0, 600])
- sim.save("test.pickle")
- sim_load = pybamm.load_sim("test.pickle")
- self.assertEqual(sim.model.name, sim_load.model.name)
-
- # with python format
- model.convert_to_format = None
- sim = pybamm.Simulation(model)
- sim.solve([0, 600])
- sim.save("test.pickle")
-
- # with Casadi solver & experiment
- model.convert_to_format = "casadi"
- sim = pybamm.Simulation(
- model,
- experiment="Discharge at 1C for 20 minutes",
- solver=pybamm.CasadiSolver(),
- )
- sim.solve([0, 600])
- sim.save("test.pickle")
- sim_load = pybamm.load_sim("test.pickle")
- self.assertEqual(sim.model.name, sim_load.model.name)
+ with TemporaryDirectory() as dir_name:
+ test_name = os.path.join(dir_name, "test.pickle")
+
+ model = pybamm.lead_acid.LOQS({"surface form": "algebraic"})
+ model.use_jacobian = True
+ sim = pybamm.Simulation(model)
+
+ # save after solving
+ sim.solve([0, 600])
+ sim.save(test_name)
+ sim_load = pybamm.load_sim(test_name)
+ self.assertEqual(sim.model.name, sim_load.model.name)
+
+ # with python format
+ model.convert_to_format = None
+ sim = pybamm.Simulation(model)
+ sim.solve([0, 600])
+ sim.save(test_name)
+
+ # with Casadi solver & experiment
+ model.convert_to_format = "casadi"
+ sim = pybamm.Simulation(
+ model,
+ experiment="Discharge at 1C for 20 minutes",
+ solver=pybamm.CasadiSolver(),
+ )
+ sim.solve([0, 600])
+ sim.save(test_name)
+ sim_load = pybamm.load_sim(test_name)
+ self.assertEqual(sim.model.name, sim_load.model.name)
def test_plot(self):
sim = pybamm.Simulation(pybamm.lithium_ion.SPM())
@@ -340,21 +349,19 @@ def test_plot(self):
sim.plot(testing=True)
def test_create_gif(self):
- sim = pybamm.Simulation(pybamm.lithium_ion.SPM())
- sim.solve(t_eval=[0, 10])
+ with TemporaryDirectory() as dir_name:
+ sim = pybamm.Simulation(pybamm.lithium_ion.SPM())
+ sim.solve(t_eval=[0, 10])
- # Create a temporary file name
- test_stub = "test_sim"
- test_file = f"{test_stub}.gif"
+ # Create a temporary file name
+ test_file = os.path.join(dir_name, "test_sim.gif")
- # create a GIF without calling the plot method
- sim.create_gif(number_of_images=3, duration=1, output_filename=test_file)
-
- # call the plot method before creating the GIF
- sim.plot(testing=True)
- sim.create_gif(number_of_images=3, duration=1, output_filename=test_file)
+ # create a GIF without calling the plot method
+ sim.create_gif(number_of_images=3, duration=1, output_filename=test_file)
- os.remove(test_file)
+ # call the plot method before creating the GIF
+ sim.plot(testing=True)
+ sim.create_gif(number_of_images=3, duration=1, output_filename=test_file)
def test_drive_cycle_interpolant(self):
model = pybamm.lithium_ion.SPM()
diff --git a/tests/unit/test_solvers/test_solution.py b/tests/unit/test_solvers/test_solution.py
index 2ef01d7434..9fc93dfb26 100644
--- a/tests/unit/test_solvers/test_solution.py
+++ b/tests/unit/test_solvers/test_solution.py
@@ -1,6 +1,7 @@
#
# Tests for the Solution class
#
+import os
from tests import TestCase
import json
import pybamm
@@ -9,6 +10,7 @@
import pandas as pd
from scipy.io import loadmat
from tests import get_discretisation_for_testing
+from tempfile import TemporaryDirectory
class TestSolution(TestCase):
@@ -233,95 +235,103 @@ def test_plot(self):
solution.plot(["c", "2c"], testing=True)
def test_save(self):
- model = pybamm.BaseModel()
- # create both 1D and 2D variables
- c = pybamm.Variable("c")
- d = pybamm.Variable("d", domain="negative electrode")
- model.rhs = {c: -c, d: 1}
- model.initial_conditions = {c: 1, d: 2}
- model.variables = {"c": c, "d": d, "2c": 2 * c, "c + d": c + d}
-
- disc = get_discretisation_for_testing()
- disc.process_model(model)
- solution = pybamm.ScipySolver().solve(model, np.linspace(0, 1))
-
- # test save data
- with self.assertRaises(ValueError):
- solution.save_data("test.pickle")
-
- # set variables first then save
- solution.update(["c", "d"])
- with self.assertRaisesRegex(ValueError, "pickle"):
- solution.save_data(to_format="pickle")
- solution.save_data("test.pickle")
-
- data_load = pybamm.load("test.pickle")
- np.testing.assert_array_equal(solution.data["c"], data_load["c"])
- np.testing.assert_array_equal(solution.data["d"], data_load["d"])
-
- # to matlab
- solution.save_data("test.mat", to_format="matlab")
- data_load = loadmat("test.mat")
- np.testing.assert_array_equal(solution.data["c"], data_load["c"].flatten())
- np.testing.assert_array_equal(solution.data["d"], data_load["d"])
-
- with self.assertRaisesRegex(ValueError, "matlab"):
- solution.save_data(to_format="matlab")
-
- # to matlab with bad variables name fails
- solution.update(["c + d"])
- with self.assertRaisesRegex(ValueError, "Invalid character"):
- solution.save_data("test.mat", to_format="matlab")
- # Works if providing alternative name
- solution.save_data(
- "test.mat", to_format="matlab", short_names={"c + d": "c_plus_d"}
- )
- data_load = loadmat("test.mat")
- np.testing.assert_array_equal(solution.data["c + d"], data_load["c_plus_d"])
-
- # to csv
- with self.assertRaisesRegex(
- ValueError, "only 0D variables can be saved to csv"
- ):
- solution.save_data("test.csv", to_format="csv")
- # only save "c" and "2c"
- solution.save_data("test.csv", ["c", "2c"], to_format="csv")
- csv_str = solution.save_data(variables=["c", "2c"], to_format="csv")
-
- # check string is the same as the file
- with open("test.csv") as f:
- # need to strip \r chars for windows
- self.assertEqual(csv_str.replace("\r", ""), f.read())
-
- # read csv
- df = pd.read_csv("test.csv")
- np.testing.assert_array_almost_equal(df["c"], solution.data["c"])
- np.testing.assert_array_almost_equal(df["2c"], solution.data["2c"])
-
- # to json
- solution.save_data("test.json", to_format="json")
- json_str = solution.save_data(to_format="json")
-
- # check string is the same as the file
- with open("test.json") as f:
- # need to strip \r chars for windows
- self.assertEqual(json_str.replace("\r", ""), f.read())
-
- # check if string has the right values
- json_data = json.loads(json_str)
- np.testing.assert_array_almost_equal(json_data["c"], solution.data["c"])
- np.testing.assert_array_almost_equal(json_data["d"], solution.data["d"])
-
- # raise error if format is unknown
- with self.assertRaisesRegex(ValueError, "format 'wrong_format' not recognised"):
- solution.save_data("test.csv", to_format="wrong_format")
-
- # test save whole solution
- solution.save("test.pickle")
- solution_load = pybamm.load("test.pickle")
- self.assertEqual(solution.all_models[0].name, solution_load.all_models[0].name)
- np.testing.assert_array_equal(solution["c"].entries, solution_load["c"].entries)
- np.testing.assert_array_equal(solution["d"].entries, solution_load["d"].entries)
+ with TemporaryDirectory() as dir_name:
+ test_stub = os.path.join(dir_name, "test")
+
+ model = pybamm.BaseModel()
+ # create both 1D and 2D variables
+ c = pybamm.Variable("c")
+ d = pybamm.Variable("d", domain="negative electrode")
+ model.rhs = {c: -c, d: 1}
+ model.initial_conditions = {c: 1, d: 2}
+ model.variables = {"c": c, "d": d, "2c": 2 * c, "c + d": c + d}
+
+ disc = get_discretisation_for_testing()
+ disc.process_model(model)
+ solution = pybamm.ScipySolver().solve(model, np.linspace(0, 1))
+
+ # test save data
+ with self.assertRaises(ValueError):
+ solution.save_data(f"{test_stub}.pickle")
+
+ # set variables first then save
+ solution.update(["c", "d"])
+ with self.assertRaisesRegex(ValueError, "pickle"):
+ solution.save_data(to_format="pickle")
+ solution.save_data(f"{test_stub}.pickle")
+
+ data_load = pybamm.load(f"{test_stub}.pickle")
+ np.testing.assert_array_equal(solution.data["c"], data_load["c"])
+ np.testing.assert_array_equal(solution.data["d"], data_load["d"])
+
+ # to matlab
+ solution.save_data(f"{test_stub}.mat", to_format="matlab")
+ data_load = loadmat(f"{test_stub}.mat")
+ np.testing.assert_array_equal(solution.data["c"], data_load["c"].flatten())
+ np.testing.assert_array_equal(solution.data["d"], data_load["d"])
+
+ with self.assertRaisesRegex(ValueError, "matlab"):
+ solution.save_data(to_format="matlab")
+
+ # to matlab with bad variables name fails
+ solution.update(["c + d"])
+ with self.assertRaisesRegex(ValueError, "Invalid character"):
+ solution.save_data(f"{test_stub}.mat", to_format="matlab")
+ # Works if providing alternative name
+ solution.save_data(
+ f"{test_stub}.mat", to_format="matlab",
+ short_names={"c + d": "c_plus_d"}
+ )
+ data_load = loadmat(f"{test_stub}.mat")
+ np.testing.assert_array_equal(solution.data["c + d"], data_load["c_plus_d"])
+
+ # to csv
+ with self.assertRaisesRegex(
+ ValueError, "only 0D variables can be saved to csv"
+ ):
+ solution.save_data(f"{test_stub}.csv", to_format="csv")
+ # only save "c" and "2c"
+ solution.save_data(f"{test_stub}.csv", ["c", "2c"], to_format="csv")
+ csv_str = solution.save_data(variables=["c", "2c"], to_format="csv")
+
+ # check string is the same as the file
+ with open(f"{test_stub}.csv") as f:
+ # need to strip \r chars for windows
+ self.assertEqual(csv_str.replace("\r", ""), f.read())
+
+ # read csv
+ df = pd.read_csv(f"{test_stub}.csv")
+ np.testing.assert_array_almost_equal(df["c"], solution.data["c"])
+ np.testing.assert_array_almost_equal(df["2c"], solution.data["2c"])
+
+ # to json
+ solution.save_data(f"{test_stub}.json", to_format="json")
+ json_str = solution.save_data(to_format="json")
+
+ # check string is the same as the file
+ with open(f"{test_stub}.json") as f:
+ # need to strip \r chars for windows
+ self.assertEqual(json_str.replace("\r", ""), f.read())
+
+ # check if string has the right values
+ json_data = json.loads(json_str)
+ np.testing.assert_array_almost_equal(json_data["c"], solution.data["c"])
+ np.testing.assert_array_almost_equal(json_data["d"], solution.data["d"])
+
+ # raise error if format is unknown
+ with self.assertRaisesRegex(ValueError,
+ "format 'wrong_format' not recognised"):
+ solution.save_data(f"{test_stub}.csv", to_format="wrong_format")
+
+ # test save whole solution
+ solution.save(f"{test_stub}.pickle")
+ solution_load = pybamm.load(f"{test_stub}.pickle")
+ self.assertEqual(solution.all_models[0].name,
+ solution_load.all_models[0].name)
+ np.testing.assert_array_equal(solution["c"].entries,
+ solution_load["c"].entries)
+ np.testing.assert_array_equal(solution["d"].entries,
+ solution_load["d"].entries)
def test_get_data_cycles_steps(self):
model = pybamm.BaseModel()