def test_parameters_init(self): with self.assertRaises(NotImplementedError): pybamm.Parameters(tests="not a test") with self.assertRaises(NotImplementedError): param = pybamm.Parameters(chemistry="not a chemistry") for chemistry in ["lead-acid"]: # pybamm.KNOWN_CHEMISTRIES: param = pybamm.Parameters(chemistry=chemistry) self.assertEqual(param._raw["R"], 8.314) self.assertEqual(param._func.D_eff(param, 1, 1), 1)
def test_grad_div_1D_FV_basic(self): param = pybamm.Parameters() mesh = pybamm.Mesh(param, target_npts=50) y = np.ones_like(mesh.x.centres) N = np.ones_like(mesh.x.edges) yn = np.ones_like(mesh.xn.centres) Nn = np.ones_like(mesh.xn.edges) yp = np.ones_like(mesh.xp.centres) Np = np.ones_like(mesh.xp.edges) # Get all operators operators = pybamm.Operators("Finite Volumes", mesh) # Check output shape self.assertEqual(operators.x.grad(y).shape[0], y.shape[0] - 1) self.assertEqual(operators.x.div(N).shape[0], N.shape[0] - 1) self.assertEqual(operators.xn.grad(yn).shape[0], yn.shape[0] - 1) self.assertEqual(operators.xn.div(Nn).shape[0], Nn.shape[0] - 1) self.assertEqual(operators.xp.grad(yp).shape[0], yp.shape[0] - 1) self.assertEqual(operators.xp.div(Np).shape[0], Np.shape[0] - 1) # Check grad and div are both zero self.assertEqual(np.linalg.norm(operators.x.grad(y)), 0) self.assertEqual(np.linalg.norm(operators.x.div(N)), 0) self.assertEqual(np.linalg.norm(operators.xn.grad(yn)), 0) self.assertEqual(np.linalg.norm(operators.xn.div(Nn)), 0) self.assertEqual(np.linalg.norm(operators.xp.grad(yp)), 0) self.assertEqual(np.linalg.norm(operators.xp.div(Np)), 0)
def test_parameters_defaults_lead_acid(self): # Tests on how the parameters interact param = pybamm.Parameters(chemistry="lead-acid") mesh = pybamm.Mesh(param, 10) param.set_mesh(mesh) # Dimensionless lengths sum to 1 self.assertAlmostEqual( param.geometric.ln + param.geometric.ls + param.geometric.lp, 1, places=10 ) # Diffusional C-rate should be smaller than C-rate self.assertLess(param.electrolyte.Cd, param.electrical.Crate) # # Dimensionless electrode conductivities should be large self.assertGreater(param.neg_electrode.iota_s, 10) self.assertGreater(param.pos_electrode.iota_s, 10) # # Dimensionless double-layer capacity should be small self.assertLess(param.neg_reactions.gamma_dl, 1e-3) self.assertLess(param.pos_reactions.gamma_dl, 1e-3) # # Volume change positive in negative electrode and negative in positive # # electrode self.assertGreater(param.neg_volume_changes.DeltaVsurf, 0) self.assertLess(param.pos_volume_changes.DeltaVsurf, 0) # # Excluded volume fraction should be less than 1 self.assertLess(param.lead_acid_misc.pi_os, 1e-4)
def test_parameters_update_raw(self): param = pybamm.Parameters(chemistry="lead-acid") param.update_raw({"Ln": 0.5}) self.assertEqual(param._raw["Ln"], 0.5) self.assertAlmostEqual( param.geometric.ln + param.geometric.ls + param.geometric.lp, 1, places=10 )
def __init__(self, model, param=None, mesh=None, solver=None, name="unnamed"): # Defaults if param is None: param = pybamm.Parameters() if mesh is None: mesh = pybamm.Mesh(param) if solver is None: solver = pybamm.Solver() # Assign attributes self.model = model self.param = param self.mesh = mesh self.solver = solver self.name = name # Initialise simulation to prepare for solving # Set mesh dependent parameters self.param.set_mesh(self.mesh) # Create operators from solver self.operators = self.solver.operators(self.mesh) # Assign param, operators and mesh as model attributes self.model.set_simulation(self.param, self.operators, self.mesh)
def setUp(self): self.model = pybamm.ElectrolyteCurrentModel() self.param = pybamm.Parameters() target_npts = 3 tsteps = 10 tend = 1 self.mesh = pybamm.Mesh(self.param, target_npts, tsteps=tsteps, tend=tend) self.param.set_mesh(self.mesh)
def setUp(self): # Set up with a model and default simulation param = pybamm.Parameters() self.mesh = pybamm.Mesh(param) model = ModelForTesting(param, self.mesh) self.vars = pybamm.Variables(model) self.mesh = self.mesh y = np.ones_like(self.mesh.x.centres) self.vars.update(self.mesh.time, y)
def setUp(self): self.model = pybamm.ReactionDiffusionModel() self.param = pybamm.Parameters() target_npts = 10 tsteps = 10 tend = 1 self.mesh = pybamm.Mesh(self.param, target_npts, tsteps=tsteps, tend=tend) self.param.set_mesh(self.mesh)
def test_parameters_options(self): # test dictionary input param = pybamm.Parameters( chemistry="lead-acid", optional_parameters={"Ln": 1 / 3, "Ls": 0.25, "Lp": 0.25}, ) self.assertEqual(param._raw["Ln"], 1 / 3) self.assertEqual(param._raw["R"], 8.314) self.assertAlmostEqual( param.geometric.ln + param.geometric.ls + param.geometric.lp, 1, places=10 ) # Test file input param = pybamm.Parameters( chemistry="lead-acid", optional_parameters="optional_test.csv" ) self.assertEqual(param._raw["Ln"], 0.5) self.assertEqual(param._raw["R"], 8.314) self.assertAlmostEqual( param.geometric.ln + param.geometric.ls + param.geometric.lp, 1, places=10 )
def test_mesh_creation(self): param = pybamm.Parameters() mesh = pybamm.Mesh(param, 50) self.assertEqual(mesh.x.edges[-1], 1) self.assertEqual(len(mesh.x.edges), len(mesh.x.centres) + 1) self.assertAlmostEqual( np.linalg.norm( mesh.x.centres - np.concatenate([mesh.xn.centres, mesh.xs.centres, mesh.xp.centres]) ), 0, )
def test_mesh_sizes(self): param = pybamm.Parameters() mesh = pybamm.Mesh(param, 50) self.assertEqual(mesh.nn + (mesh.ns - 2) + mesh.np, mesh.n) self.assertEqual(mesh.xn.npts, mesh.nn - 1) self.assertEqual(mesh.xs.npts, mesh.ns - 1) self.assertEqual(mesh.xp.npts, mesh.np - 1) self.assertEqual(mesh.x.npts, mesh.n - 1) self.assertEqual(len(mesh.xn.edges), mesh.nn) self.assertEqual(len(mesh.xs.edges), mesh.ns) self.assertEqual(len(mesh.xp.edges), mesh.np) self.assertEqual(len(mesh.x.edges), mesh.n)
def test_model_convergence(self): """ Exact solution: c = exp(-4*pi**2*t) * cos(2*pi*x) Initial conditions: c0 = cos(2*pi*x) Boundary conditions: Zero flux Source terms: 0 Can achieve "convergence" in time by changing the integrator tolerance Can't get h**2 convergence in space """ param = pybamm.Parameters(tests="convergence") param.set_mesh(self.mesh) def c_exact(t): return np.exp(-4 * np.pi**2 * t) * np.cos( 2 * np.pi * self.mesh.x.centres) inits = {"concentration": c_exact(0)} def bcs(t): return {"concentration": (np.array([0]), np.array([0]))} def sources(t): return {"concentration": 0} tests = {"inits": inits, "bcs": bcs, "sources": sources} model = pybamm.ReactionDiffusionModel(tests=tests) ns = [1, 2, 3] errs = [0] * len(ns) for i, n in enumerate(ns): solver = pybamm.Solver( integrator="BDF", spatial_discretisation="Finite Volumes", tol=10**(-n), ) sim = pybamm.Simulation(model, param=param, mesh=self.mesh, solver=solver) sim.run() errs[i] = norm(sim.vars.c.T - c_exact(self.mesh.time[:, np.newaxis])) / norm( c_exact(self.mesh.time[:, np.newaxis])) [ self.assertLess(errs[i + 1] / errs[i], 0.14) for i in range(len(errs) - 1) ]
def test_current_conservation_finite_volumes_convergence(self): electrolyte = pybamm.Electrolyte() # Finite volume only has h**2 convergence if the mesh is uniform? uniform_lengths = {"Ln": 1e-3, "Ls": 1e-3, "Lp": 1e-3} param = pybamm.Parameters(optional_parameters=uniform_lengths, tests="convergence") # Test convergence ns = [100, 200, 400] errn = [0] * len(ns) errp = [0] * len(ns) for i, n in enumerate(ns): # Set up mesh = pybamm.Mesh(param, n) param.set_mesh(mesh) cn = np.cos(2 * np.pi * mesh.xcn) cp = np.cos(2 * np.pi * mesh.xcp) en = mesh.xcn**2 ep = mesh.xcp**2 operators = { "xcn": pybamm.Operators("Finite Volumes", "xcn", mesh), "xcp": pybamm.Operators("Finite Volumes", "xcp", mesh), } in_exact = (-2 * np.pi * np.sin(2 * np.pi * mesh.xn)) + 2 * mesh.xn ip_exact = (-2 * np.pi * np.sin(2 * np.pi * mesh.xp / param.lp)) + 2 * mesh.xp current_bcs_n = (in_exact[0, None], in_exact[-1, None]) current_bcs_p = (ip_exact[0, None], ip_exact[-1, None]) # Exact solutions dendt_exact = 1 / param.gamma_dl_n * (-4 * np.pi**2 * cn + 2) depdt_exact = 1 / param.gamma_dl_p * (-4 * np.pi**2 * cp + 2) # Calculate solution and errors electrolyte.set_simulation(param, operators, mesh) dendt = electrolyte.current_conservation("xcn", cn, en, 0, current_bcs_n) depdt = electrolyte.current_conservation("xcp", cp, ep, 0, current_bcs_p) errn[i] = norm( (dendt - dendt_exact)[1:-1]) / norm(dendt_exact[1:-1]) errp[i] = norm( (depdt - depdt_exact)[1:-1]) / norm(depdt_exact[1:-1]) # Expect h**2 convergence for i in range(len(errn) - 1): self.assertLess(errn[i + 1] / errn[i], 0.26) self.assertLess(errp[i + 1] / errp[i], 0.26)
def test_model_shape(self): for spatial_discretisation in pybamm.KNOWN_SPATIAL_DISCRETISATIONS: # Set up param = pybamm.Parameters() mesh = pybamm.Mesh(param) param.set_mesh(mesh) operators = pybamm.Operators(spatial_discretisation, mesh) electrolyte = pybamm.electrolyte.StefanMaxwellDiffusion( param.electrolyte, operators.x, mesh.x, {}) # Test c0 = electrolyte.initial_conditions() vars = VarsForTesting(c=c0, j=c0) dcdt = electrolyte.pdes_rhs(vars) self.assertEqual(c0.shape, dcdt.shape)
def test_functions_lead_acid(self): # Tests on how the parameters interact param = pybamm.Parameters(chemistry="lead-acid") mesh = pybamm.Mesh(param, 10) param.set_mesh(mesh) # Known values for dimensionless functions self.assertEqual(param.electrolyte.D_eff(1, 1), 1) self.assertEqual(param.electrolyte.kappa_eff(0, 1), 0) self.assertEqual(param.electrolyte.kappa_eff(1, 0), 0) self.assertEqual(param.neg_reactions.j0(0), 0) self.assertEqual(param.pos_reactions.j0(0), 0) # Known monotonicity for dimensionless functions self.assertLess(param.neg_reactions.j0(1), param.neg_reactions.j0(2)) self.assertLess(param.pos_reactions.j0(1), param.pos_reactions.j0(2)) self.assertGreater(param.lead_acid_misc.chi(1), param.lead_acid_misc.chi(0.5)) self.assertLess(param.neg_reactions.U(1), param.neg_reactions.U(0.5)) self.assertGreater(param.pos_reactions.U(1), param.pos_reactions.U(0.5))
def test_simulation_name(self): model = pybamm.ReactionDiffusionModel() param = pybamm.Parameters() mesh = pybamm.Mesh(param, target_npts=50) solver = pybamm.Solver() sim = pybamm.Simulation(model, param=param, mesh=mesh, solver=solver, name="test name") self.assertEqual(sim.param.electrolyte.s.shape, sim.mesh.x.centres.shape) np.testing.assert_array_equal(sim.operators.x.div(mesh.x.edges), np.ones_like(mesh.x.centres)) self.assertEqual(param, sim.model.param) self.assertEqual(mesh, sim.model.mesh) self.assertEqual(str(sim), "test name")
def test_macinnes_finite_volumes_convergence(self): electrolyte = pybamm.Electrolyte() # Finite volume only has h**2 convergence if the mesh is uniform? uniform_lengths = {"Ln": 1e-3, "Ls": 1e-3, "Lp": 1e-3} param = pybamm.Parameters(optional_parameters=uniform_lengths, tests="convergence") # Test convergence ns = [100, 200, 400] errn = [0] * len(ns) errp = [0] * len(ns) for i, n in enumerate(ns): # Set up mesh = pybamm.Mesh(param, n) param.set_mesh(mesh) cn = np.cos(2 * np.pi * mesh.xcn) cp = np.sin(2 * np.pi * mesh.xcp) en = mesh.xcn**2 ep = mesh.xcp**2 operators = { "xcn": pybamm.Operators("Finite Volumes", "xcn", mesh), "xcp": pybamm.Operators("Finite Volumes", "xcp", mesh), } in_exact = -2 * np.pi * np.sin(2 * np.pi * mesh.xn) + 2 * mesh.xn ip_exact = 2 * np.pi * np.cos(2 * np.pi * mesh.xp) + 2 * mesh.xp current_bcs_n = (in_exact[0, None], in_exact[-1, None]) current_bcs_p = (ip_exact[0, None], ip_exact[-1, None]) # Calculate solution and errors electrolyte.set_simulation(param, operators, mesh) i_n = electrolyte.macinnes("xcn", cn, en, current_bcs_n) i_p = electrolyte.macinnes("xcp", cp, ep, current_bcs_p) errn[i] = norm(i_n - in_exact) / norm(in_exact) errp[i] = norm(i_p - ip_exact) / norm(ip_exact) # Expect h**2 convergence for i in range(len(errn) - 1): self.assertLess(errn[i + 1] / errn[i], 0.26) self.assertLess(errp[i + 1] / errp[i], 0.26)
def test_finite_volumes_convergence(self): # Finite volume only has h**2 convergence if the mesh is uniform? uniform_lengths = {"Ln": 1e-3, "Ls": 1e-3, "Lp": 1e-3} param = pybamm.Parameters(optional_parameters=uniform_lengths, tests="convergence") # Test convergence ns = [100, 200, 400] errs = [0] * len(ns) for i, n in enumerate(ns): # Set up mesh = pybamm.Mesh(param, target_npts=n) param.set_mesh(mesh) operators = pybamm.Operators("Finite Volumes", mesh) # Exact solution c = np.cos(2 * np.pi * mesh.x.centres) dcdt_exact = -4 * np.pi**2 * c def bcs(t): return {"concentration": (np.array([0]), np.array([0]))} def sources(t): return {"concentration": 0} tests = {"bcs": bcs, "sources": sources} electrolyte = pybamm.electrolyte.StefanMaxwellDiffusion( param.electrolyte, operators.x, mesh.x, tests) # Calculate solution and errors vars = VarsForTesting(c=c) dcdt = electrolyte.pdes_rhs(vars) errs[i] = norm(dcdt - dcdt_exact) / norm(dcdt_exact) # Expect h**2 convergence [ self.assertLess(errs[i + 1] / errs[i], 0.26) for i in range(len(errs) - 1) ]
def test_grad_div_1D_FV_convergence(self): # Convergence param = pybamm.Parameters() ns = [50, 100, 200] grad_errs = [0] * len(ns) div_errs = [0] * len(ns) for i, n in enumerate(ns): # Define problem and exact solutions mesh = pybamm.Mesh(param, target_npts=n) y = np.sin(mesh.x.centres) grad_y_exact = np.cos(mesh.x.edges[1:-1]) div_exact = -np.sin(mesh.x.centres) # Get operators and flux operators = pybamm.operators.CartesianFiniteVolumes(mesh.x) grad_y_approx = operators.grad(y) # Calculate divergence of exact flux to avoid double errors # (test for those separately) N_exact = np.cos(mesh.x.edges) div_approx = operators.div(N_exact) # Calculate errors grad_errs[i] = np.linalg.norm( grad_y_approx - grad_y_exact ) / np.linalg.norm(grad_y_exact) div_errs[i] = np.linalg.norm(div_approx - div_exact) / np.linalg.norm( div_exact ) # Expect h**2 convergence [ self.assertLess(grad_errs[i + 1] / grad_errs[i], 0.26) for i in range(len(grad_errs) - 1) ] [ self.assertLess(div_errs[i + 1] / div_errs[i], 0.26) for i in range(len(div_errs) - 1) ]
def test_variables_average_convergence(self): # Set up param = pybamm.Parameters() ns = [50, 100, 200] err = [0] * len(ns) errn = [0] * len(ns) errs = [0] * len(ns) errp = [0] * len(ns) for i, n in enumerate(ns): # Set up mesh = pybamm.Mesh(param, n) model = ModelForTesting(param, mesh) y = mesh.x.centres**2 vars = pybamm.Variables(model) vars.update(mesh.time, y) vars.average() # Exact solution ln, ls, lp = [ param.geometric.__dict__[l] for l in ["ln", "ls", "lp"] ] c_avg_exact = 1 / 3 cn_avg_exact = ln**2 / 3 cs_avg_exact = ((1 - lp)**3 - ln**3) / (3 * ls) cp_avg_exact = (1 - (1 - lp)**3) / (3 * lp) # Compare err[i] = norm(vars.c_avg - c_avg_exact) / norm(c_avg_exact) errn[i] = norm(vars.cn_avg - cn_avg_exact) / norm(cn_avg_exact) errs[i] = norm(vars.cs_avg - cs_avg_exact) / norm(cs_avg_exact) errp[i] = norm(vars.cp_avg - cp_avg_exact) / norm(cp_avg_exact) for i in range(len(err) - 1): self.assertLess(err[i + 1] / err[i], 0.26) self.assertLess(errn[i + 1] / errn[i], 0.26) self.assertLess(errs[i + 1] / errs[i], 0.26) self.assertLess(errp[i + 1] / errp[i], 0.26)
def test_mesh_dependent_parameters(self): param = pybamm.Parameters(chemistry="lead-acid") mesh = pybamm.Mesh(param, 10) param.set_mesh(mesh) self.assertEqual(param.electrolyte.s.shape, mesh.x.centres.shape)
def setUp(self): self.param = pybamm.Parameters() self.param_n = self.param.neg_reactions self.param_p = self.param.pos_reactions self.mesh = pybamm.Mesh(self.param, target_npts=50)