Skip to content

Commit

Permalink
#1129 add more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
valentinsulzer committed Mar 8, 2022
1 parent 2079ded commit 2e80558
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 7 deletions.
5 changes: 2 additions & 3 deletions pybamm/expression_tree/operations/evaluate_julia.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,14 +596,13 @@ def get_julia_function(
else:
func_def = f"{funcname}_with_consts!"

# add function def and sparse arrays to first line
imports = "begin\n\n" # using SparseArrays, LinearAlgebra\n\n"
# add function def
if typ == "ode":
function_def = f"\nfunction {func_def}(dy, y, p, t)\n"
elif typ == "dae":
function_def = f"\nfunction {func_def}(out, dy, y, p, t)\n"
julia_str = (
imports
"begin\n"
+ const_and_cache_str
+ function_def
+ input_parameter_extraction
Expand Down
6 changes: 3 additions & 3 deletions pybamm/models/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -945,7 +945,7 @@ def generate_julia_diffeq(
self,
input_parameter_order=None,
get_consistent_ics_solver=None,
dae_type="implicit",
dae_type="semi-explicit",
**kwargs,
):
"""
Expand All @@ -960,7 +960,7 @@ def generate_julia_diffeq(
Solver to use to get consistent initial conditions. If None, the initial
guesses for boundary conditions (non-consistent) are used.
dae_type : str, optional
How to write the DAEs. Options are "explicit" or "implicit".
How to write the DAEs. Options are "semi-explicit" (default) or "implicit".
Returns
-------
Expand All @@ -984,7 +984,7 @@ def generate_julia_diffeq(
**kwargs,
)
else:
if dae_type == "explicit":
if dae_type == "semi-explicit":
len_rhs = None
else:
len_rhs = self.concatenated_rhs.size
Expand Down
2 changes: 1 addition & 1 deletion pybamm/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ def have_julia():
try:
subprocess.call(["julia", "--version"], stdout=FNULL, stderr=subprocess.STDOUT)
return True
except subprocess.CalledProcessError:
except subprocess.CalledProcessError: # pragma: no cover
return False


Expand Down
4 changes: 4 additions & 0 deletions tests/unit/test_expression_tree/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ def test_exceptions(self):
with self.assertRaises(pybamm.DomainError):
pybamm.Function(test_multi_var_function, a, b)

fun = pybamm.Function(np.cos, pybamm.t)
with self.assertRaisesRegex(NotImplementedError, "No julia name"):
fun.julia_name

def test_function_unnamed(self):
fun = pybamm.Function(np.cos, pybamm.t)
self.assertEqual(fun.name, "function (cos)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
Julia(compiled_modules=False)
from julia import Main

# load julia libraries required for evaluating the strings
Main.eval("using SparseArrays, LinearAlgebra")


@unittest.skipIf(not have_julia, "Julia not installed")
@unittest.skipIf(system() == "Windows", "Julia not supported on windows")
Expand Down
132 changes: 132 additions & 0 deletions tests/unit/test_models/test_base_model_generate_julia_diffeq.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#
# Tests for the base model class
#
import platform
import unittest
import pybamm

have_julia = True # pybamm.have_julia()
if have_julia and platform.system() != "Windows":
from julia.api import Julia

Julia(compiled_modules=False)
from julia import Main

# load julia libraries required for evaluating the strings
Main.eval("using SparseArrays, LinearAlgebra")


@unittest.skipIf(not have_julia, "Julia not installed")
@unittest.skipIf(platform.system() == "Windows", "Skipped for Windows")
class TestBaseModelGenerateJuliaDiffEq(unittest.TestCase):
def test_generate_ode(self):
# ODE model with no input parameters
model = pybamm.BaseModel(name="ode test model")
a = pybamm.Variable("a")
b = pybamm.Variable("b")
model.rhs = {a: -a, b: a - b}
model.initial_conditions = {a: 1, b: 2}

# Generate rhs and ics for the Julia model
rhs_str, ics_str = model.generate_julia_diffeq()
self.assertIsInstance(rhs_str, str)
self.assertIn("ode_test_model", rhs_str)
self.assertIn("(dy, y, p, t)", rhs_str)
self.assertIsInstance(ics_str, str)
self.assertIn("ode_test_model_u0", ics_str)
self.assertIn("(u0, p)", ics_str)

# ODE model with input parameters
model = pybamm.BaseModel(name="ode test model 2")
a = pybamm.Variable("a")
b = pybamm.Variable("b")
p = pybamm.InputParameter("p")
q = pybamm.InputParameter("q")
model.rhs = {a: -a * p, b: a - b}
model.initial_conditions = {a: q, b: 2}

# Generate rhs and ics for the Julia model
rhs_str, ics_str = model.generate_julia_diffeq(input_parameter_order=["p", "q"])
self.assertIsInstance(rhs_str, str)
self.assertIn("ode_test_model_2", rhs_str)
self.assertIn("p, q = p", rhs_str)

self.assertIsInstance(ics_str, str)
self.assertIn("ode_test_model_2_u0", ics_str)
self.assertIn("p, q = p", ics_str)

def test_generate_dae(self):
# ODE model with no input parameters
model = pybamm.BaseModel(name="dae test model")
a = pybamm.Variable("a")
b = pybamm.Variable("b")
model.rhs = {a: -a}
model.algebraic = {b: a - b}
model.initial_conditions = {a: 1, b: 2}

# Generate eqn and ics for the Julia model (semi-explicit)
eqn_str, ics_str = model.generate_julia_diffeq()
self.assertIsInstance(eqn_str, str)
self.assertIn("dae_test_model", eqn_str)
self.assertIn("(dy, y, p, t)", eqn_str)
self.assertIsInstance(ics_str, str)
self.assertIn("dae_test_model_u0", ics_str)
self.assertIn("(u0, p)", ics_str)
self.assertIn("[1.,2.]", ics_str)

# Generate eqn and ics for the Julia model (implicit)
eqn_str, ics_str = model.generate_julia_diffeq(dae_type="implicit")
self.assertIsInstance(eqn_str, str)
self.assertIn("dae_test_model", eqn_str)
self.assertIn("(out, dy, y, p, t)", eqn_str)
self.assertIsInstance(ics_str, str)
self.assertIn("dae_test_model_u0", ics_str)
self.assertIn("(u0, p)", ics_str)

# Calculate initial conditions in python
eqn_str, ics_str = model.generate_julia_diffeq(
get_consistent_ics_solver=pybamm.CasadiSolver()
)
# Check that the initial conditions are consistent
self.assertIn("[1.,1.]", ics_str)

def test_generate_pde(self):
# ODE model with no input parameters
model = pybamm.BaseModel(name="pde test model")
a = pybamm.Variable("a", domain="line")
b = pybamm.Variable("b", domain="line")
model.rhs = {a: pybamm.div(pybamm.grad(a)) + b, b: a - b}
model.boundary_conditions = {
a: {"left": (-1, "Dirichlet"), "right": (1, "Neumann")}
}
model.initial_conditions = {a: 1, b: 2}

# Discretize
x = pybamm.SpatialVariable("x", domain=["line"])
geometry = pybamm.Geometry(
{"line": {x: {"min": pybamm.Scalar(0), "max": pybamm.Scalar(1)}}}
)
submesh_types = {"line": pybamm.Uniform1DSubMesh}
var_pts = {x: 10}
mesh = pybamm.Mesh(geometry, submesh_types, var_pts)
disc = pybamm.Discretisation(mesh, {"line": pybamm.FiniteVolume()})
disc.process_model(model)

# Generate rhs and ics for the Julia model
rhs_str, ics_str = model.generate_julia_diffeq()
self.assertIsInstance(rhs_str, str)
self.assertIn("pde_test_model", rhs_str)
self.assertIn("(dy, y, p, t)", rhs_str)
self.assertIsInstance(ics_str, str)
self.assertIn("pde_test_model_u0", ics_str)
self.assertIn("(u0, p)", ics_str)


if __name__ == "__main__":
print("Add -v for more debug output")
import sys

if "-v" in sys.argv:
debug = True
pybamm.settings.debug_mode = True
unittest.main()
19 changes: 19 additions & 0 deletions tests/unit/test_parameters/test_parameter_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,25 @@ def D(a, b):
processed_func = parameter_values.process_symbol(func)
self.assertEqual(processed_func.evaluate(), 3)

def test_function_parameter_replace_callable(self):
# This functionality is used for generating a model in Julia's MTK
def D(a, b):
return a * pybamm.exp(b)

parameter_values = pybamm.ParameterValues({"a": 3, "Diffusivity": D})
parameter_values._replace_callable_function_parameters = False

a = pybamm.Parameter("a")
b = pybamm.Variable("b")
func = pybamm.FunctionParameter("Diffusivity", {"a": a, "b": b})
func.print_name = "D"

processed_func = parameter_values.process_symbol(func)
self.assertIsInstance(processed_func, pybamm.FunctionParameter)
self.assertEqual(processed_func.name, "D")
self.assertEqual(processed_func.arg_names, ["a", "b"])
self.assertIsInstance(processed_func.callable, pybamm.Multiplication)

def test_process_interpolant(self):
x = np.linspace(0, 10)[:, np.newaxis]
data = np.hstack([x, 2 * x])
Expand Down

0 comments on commit 2e80558

Please sign in to comment.