def test_name(self): a = pybamm.Symbol("a") x = np.linspace(0, 1, 200) interp = pybamm.Interpolant(x, x, a, "name") self.assertEqual(interp.name, "name") interp = pybamm.Interpolant(x, x, a) self.assertEqual(interp.name, "interpolating_function")
def test_interpolation(self): x = np.linspace(0, 1) y = pybamm.StateVector(slice(0, 2)) casadi_y = casadi.MX.sym("y", 2) # linear y_test = np.array([0.4, 0.6]) for interpolator in ["linear", "pchip", "cubic spline"]: interp = pybamm.Interpolant(x, 2 * x, y, interpolator=interpolator) interp_casadi = interp.to_casadi(y=casadi_y) f = casadi.Function("f", [casadi_y], [interp_casadi]) np.testing.assert_array_almost_equal(interp.evaluate(y=y_test), f(y_test)) # square y = pybamm.StateVector(slice(0, 1)) for interpolator in ["pchip", "cubic spline"]: interp = pybamm.Interpolant(x, x**2, y, interpolator=interpolator) interp_casadi = interp.to_casadi(y=casadi_y) f = casadi.Function("f", [casadi_y], [interp_casadi]) np.testing.assert_array_almost_equal(interp.evaluate(y=y_test), f(y_test)) # len(x)=1 but y is 2d y = pybamm.StateVector(slice(0, 1)) casadi_y = casadi.MX.sym("y", 1) data = np.tile(2 * x, (10, 1)).T y_test = np.array([0.4]) for interpolator in ["linear", "pchip", "cubic spline"]: interp = pybamm.Interpolant(x, data, y, interpolator=interpolator) interp_casadi = interp.to_casadi(y=casadi_y) f = casadi.Function("f", [casadi_y], [interp_casadi]) np.testing.assert_array_almost_equal(interp.evaluate(y=y_test), f(y_test))
def test_errors(self): with self.assertRaisesRegex(ValueError, "data should have exactly two columns"): pybamm.Interpolant(np.ones(10), None) with self.assertRaisesRegex(ValueError, "interpolator 'bla' not recognised"): pybamm.Interpolant(np.ones((10, 2)), None, interpolator="bla")
def test_interpolation(self): x = np.linspace(0, 1)[:, np.newaxis] y = pybamm.StateVector(slice(0, 2)) # linear linear = np.hstack([x, 2 * x]) for interpolator in ["pchip", "cubic spline"]: interp = pybamm.Interpolant(linear, y, interpolator=interpolator) np.testing.assert_array_almost_equal( interp.evaluate(y=np.array([0.397, 1.5]))[:, 0], np.array([0.794, 3])) # square square = np.hstack([x, x**2]) y = pybamm.StateVector(slice(0, 1)) for interpolator in ["pchip", "cubic spline"]: interp = pybamm.Interpolant(square, y, interpolator=interpolator) np.testing.assert_array_almost_equal( interp.evaluate(y=np.array([0.397]))[:, 0], np.array([0.397**2])) # with extrapolation set to False for interpolator in ["pchip", "cubic spline"]: interp = pybamm.Interpolant(square, y, interpolator=interpolator, extrapolate=False) np.testing.assert_array_equal( interp.evaluate(y=np.array([2]))[:, 0], np.array([np.nan]))
def test_process_interpolant(self): x = np.linspace(0, 10)[:, np.newaxis] data = np.hstack([x, 2 * x]) parameter_values = pybamm.ParameterValues( {"a": 3.01, "Times two": ("times two", data)} ) a = pybamm.Parameter("a") func = pybamm.FunctionParameter("Times two", {"a": a}) processed_func = parameter_values.process_symbol(func) self.assertIsInstance(processed_func, pybamm.Interpolant) self.assertEqual(processed_func.evaluate(), 6.02) # process differentiated function parameter diff_func = func.diff(a) processed_diff_func = parameter_values.process_symbol(diff_func) self.assertEqual(processed_diff_func.evaluate(), 2) # interpolant defined up front interp2 = pybamm.Interpolant(data[:, 0], data[:, 1], a) processed_interp2 = parameter_values.process_symbol(interp2) self.assertEqual(processed_interp2.evaluate(), 6.02) data3 = np.hstack([x, 3 * x]) interp3 = pybamm.Interpolant(data3[:, 0], data3[:, 1], a) processed_interp3 = parameter_values.process_symbol(interp3) self.assertEqual(processed_interp3.evaluate(), 9.03)
def get_interp_fun(variable_name, domain): """ Create a :class:`pybamm.Function` object using the variable, to allow plotting with :class:`pybamm.QuickPlot` (interpolate in space to match edges, and then create function to interpolate in time) """ variable = comsol_variables[variable_name] if domain == ["negative electrode"]: comsol_x = comsol_variables["x_n"] elif domain == ["positive electrode"]: comsol_x = comsol_variables["x_p"] elif domain == whole_cell: comsol_x = comsol_variables["x"] # Make sure to use dimensional space pybamm_x = mesh.combine_submeshes(*domain).nodes * L_x variable = interp.interp1d(comsol_x, variable, axis=0)(pybamm_x) fun = pybamm.Interpolant( comsol_t, variable.T, pybamm.t * pybamm_model.timescale.evaluate(), ) fun.domain = domain fun.mesh = mesh.combine_submeshes(*domain) fun.secondary_mesh = None return fun
def test_processing(self): x = np.linspace(0, 1)[:, np.newaxis] y = pybamm.StateVector(slice(0, 2)) linear = np.hstack([x, 2 * x]) interp = pybamm.Interpolant(linear, y) self.assertEqual(interp.id, interp.new_copy().id) self.assertEqual(interp.id, interp.simplify().id)
def _function_new_copy(self, children): """ See :meth:`Function._function_new_copy()` """ return pybamm.Interpolant(self.data, *children, name=self.name, interpolator=self.interpolator, extrapolate=self.extrapolate, entries_string=self.entries_string)
def test_errors(self): with self.assertRaisesRegex(ValueError, "x1"): pybamm.Interpolant(np.ones(10), np.ones(11), pybamm.Symbol("a")) with self.assertRaisesRegex(ValueError, "x2"): pybamm.Interpolant((np.ones(10), np.ones(11)), np.ones((10, 12)), pybamm.Symbol("a")) with self.assertRaisesRegex(ValueError, "y should"): pybamm.Interpolant((np.ones(10), np.ones(11)), np.ones(10), pybamm.Symbol("a")) with self.assertRaisesRegex(ValueError, "interpolator 'bla' not recognised"): pybamm.Interpolant(np.ones(10), np.ones(10), pybamm.Symbol("a"), interpolator="bla") with self.assertRaisesRegex(ValueError, "child should have size 1"): pybamm.Interpolant(np.ones(10), np.ones((10, 11)), pybamm.StateVector(slice(0, 2))) with self.assertRaisesRegex(ValueError, "should equal"): pybamm.Interpolant((np.ones(10), np.ones(12)), np.ones((10, 12)), pybamm.Symbol("a")) with self.assertRaisesRegex(ValueError, "interpolator should be 'linear'"): pybamm.Interpolant( (np.ones(10), np.ones(12)), np.ones((10, 12)), (pybamm.Symbol("a"), pybamm.Symbol("b")), interpolator="cubic spline", )
def test_interpolation_1_x_2d_y(self): x = np.linspace(0, 1, 200) y = np.tile(2 * x, (10, 1)).T var = pybamm.StateVector(slice(0, 1)) # linear for interpolator in ["linear", "pchip", "cubic spline"]: interp = pybamm.Interpolant(x, y, var, interpolator=interpolator) np.testing.assert_array_almost_equal( interp.evaluate(y=np.array([0.397])), 0.794 * np.ones((10, 1)))
def test_interpolation_2_x_2d_y(self): x = (np.arange(-5.01, 5.01, 0.05), np.arange(-5.01, 5.01, 0.05)) xx, yy = np.meshgrid(x[0], x[1]) z = np.sin(xx**2 + yy**2) var1 = pybamm.StateVector(slice(0, 1)) var2 = pybamm.StateVector(slice(1, 2)) # linear interp = pybamm.Interpolant(x, z, (var1, var2), interpolator="linear") np.testing.assert_array_almost_equal( interp.evaluate(y=np.array([0, 0])), 0, decimal=3)
def test_interpolation(self): x = np.linspace(0, 1)[:, np.newaxis] y = pybamm.StateVector(slice(0, 2)) casadi_y = casadi.MX.sym("y", 2) # linear linear = np.hstack([x, 2 * x]) y_test = np.array([0.4, 0.6]) for interpolator in ["pchip", "cubic spline"]: interp = pybamm.Interpolant(linear, y, interpolator=interpolator) interp_casadi = interp.to_casadi(y=casadi_y) f = casadi.Function("f", [casadi_y], [interp_casadi]) np.testing.assert_array_almost_equal(interp.evaluate(y=y_test), f(y_test)) # square square = np.hstack([x, x ** 2]) y = pybamm.StateVector(slice(0, 1)) for interpolator in ["pchip", "cubic spline"]: interp = pybamm.Interpolant(square, y, interpolator=interpolator) interp_casadi = interp.to_casadi(y=casadi_y) f = casadi.Function("f", [casadi_y], [interp_casadi]) np.testing.assert_array_almost_equal(interp.evaluate(y=y_test), f(y_test))
def test_diff(self): x = np.linspace(0, 1)[:, np.newaxis] y = pybamm.StateVector(slice(0, 2)) # linear (derivative should be 2) linear = np.hstack([x, 2 * x]) for interpolator in ["pchip", "cubic spline"]: interp_diff = pybamm.Interpolant(linear, y, interpolator=interpolator).diff(y) np.testing.assert_array_almost_equal( interp_diff.evaluate(y=np.array([0.397, 1.5]))[:, 0], np.array([2, 2])) # square (derivative should be 2*x) square = np.hstack([x, x**2]) for interpolator in ["pchip", "cubic spline"]: interp_diff = pybamm.Interpolant(square, y, interpolator=interpolator).diff(y) np.testing.assert_array_almost_equal( interp_diff.evaluate(y=np.array([0.397, 0.806]))[:, 0], np.array([0.794, 1.612]), decimal=3, )
def test_diff(self): x = np.linspace(0, 1, 200) y = pybamm.StateVector(slice(0, 2)) # linear (derivative should be 2) # linear interpolator cannot be differentiated for interpolator in ["pchip", "cubic spline"]: interp_diff = pybamm.Interpolant(x, 2 * x, y, interpolator=interpolator).diff(y) np.testing.assert_array_almost_equal( interp_diff.evaluate(y=np.array([0.397, 1.5]))[:, 0], np.array([2, 2])) # square (derivative should be 2*x) for interpolator in ["pchip", "cubic spline"]: interp_diff = pybamm.Interpolant(x, x**2, y, interpolator=interpolator).diff(y) np.testing.assert_array_almost_equal( interp_diff.evaluate(y=np.array([0.397, 0.806]))[:, 0], np.array([0.794, 1.612]), decimal=3, )
def test_drive_cycle_interpolant(self): model = pybamm.lithium_ion.SPM() param = model.default_parameter_values # Import drive cycle from file drive_cycle = pd.read_csv( pybamm.get_parameters_filepath( os.path.join("input", "drive_cycles", "US06.csv") ), comment="#", skip_blank_lines=True, header=None, ).to_numpy() timescale = param.evaluate(model.timescale) current_interpolant = pybamm.Interpolant( drive_cycle[:, 0], drive_cycle[:, 1], timescale * pybamm.t ) param["Current function [A]"] = current_interpolant time_data = drive_cycle[:, 0] sim = pybamm.Simulation(model, parameter_values=param) # check solution is returned at the times in the data sim.solve() tau = sim.model.timescale.evaluate() np.testing.assert_array_almost_equal(sim.solution.t, time_data / tau) # check warning raised if the largest gap in t_eval is bigger than the # smallest gap in the data with self.assertWarns(pybamm.SolverWarning): sim.solve(t_eval=np.linspace(0, 1, 100)) # check warning raised if t_eval doesnt contain time_data , but has a finer # resolution (can still solve, but good for users to know they dont have # the solution returned at the data points) with self.assertWarns(pybamm.SolverWarning): sim.solve(t_eval=np.linspace(0, time_data[-1], 800))
os.chdir(pybamm.__path__[0] + "/..") pybamm.set_logging_level("INFO") # load model and update parameters so the input current is the US06 drive cycle model = pybamm.lithium_ion.SPMe({"thermal": "lumped"}) param = model.default_parameter_values # import drive cycle from file drive_cycle = pd.read_csv("pybamm/input/drive_cycles/US06.csv", comment="#", header=None).to_numpy() # create interpolant timescale = param.evaluate(model.timescale) current_interpolant = pybamm.Interpolant(drive_cycle[:, 0], drive_cycle[:, 1], timescale * pybamm.t) # set drive cycle param["Current function [A]"] = current_interpolant # create and run simulation using the CasadiSolver in "fast" mode, remembering to # pass in the updated parameters sim = pybamm.Simulation(model, parameter_values=param, solver=pybamm.CasadiSolver(mode="fast")) sim.solve() sim.plot([ "Negative particle surface concentration [mol.m-3]", "Electrolyte concentration [mol.m-3]", "Positive particle surface concentration [mol.m-3]", "Current [A]",
pybamm.set_logging_level("INFO") # load model and update parameters so the input current is the US06 drive cycle model = pybamm.lithium_ion.SPMe({"thermal": "lumped"}) param = model.default_parameter_values # import drive cycle from file drive_cycle = pd.read_csv( "pybamm/input/drive_cycles/US06.csv", comment="#", header=None ).to_numpy() # create interpolant timescale = param.evaluate(model.timescale) current_interpolant = pybamm.Interpolant(drive_cycle, timescale * pybamm.t) # set drive cycle param["Current function [A]"] = current_interpolant # create and run simulation using the CasadiSolver in "fast" mode, remembering to # pass in the updated parameters sim = pybamm.Simulation( model, parameter_values=param, solver=pybamm.CasadiSolver(mode="fast") ) sim.solve() sim.plot( [ "Negative particle surface concentration [mol.m-3]", "Electrolyte concentration [mol.m-3]",
def test_name(self): a = pybamm.Symbol("a") x = np.linspace(0, 1)[:, np.newaxis] interp = pybamm.Interpolant(np.hstack([x, x]), a, "name") self.assertEqual(interp.name, "interpolating function (name)")
def _process_symbol(self, symbol): """ See :meth:`ParameterValues.process_symbol()`. """ if isinstance(symbol, pybamm.Parameter): value = self[symbol.name] # Scalar inherits name (for updating parameters) and domain (for Broadcast) return pybamm.Scalar(value, name=symbol.name, domain=symbol.domain) elif isinstance(symbol, pybamm.FunctionParameter): new_children = [self.process_symbol(child) for child in symbol.children] function_name = self[symbol.name] # if current setter, process any parameters that are symbols and # store the evaluated symbol in the parameters_eval dict if isinstance(function_name, pybamm.GetCurrent): for param, sym in function_name.parameters.items(): if isinstance(sym, pybamm.Symbol): new_sym = self.process_symbol(sym) function_name.parameters[param] = new_sym function_name.parameters_eval[param] = new_sym.evaluate() # If loading data, need to update interpolant with # evaluated parameters if isinstance(function_name, pybamm.GetCurrentData): function_name.interpolate() # Create Function or Interpolant objec if isinstance(function_name, tuple): # If function_name is a tuple then it should be (name, data) and we need # to create an Interpolant name, data = function_name function = pybamm.Interpolant(data, *new_children, name=name) else: # otherwise create standard function function = pybamm.Function(function_name, *new_children) # Differentiate if necessary if symbol.diff_variable is None: return function else: # return differentiated function new_diff_variable = self.process_symbol(symbol.diff_variable) return function.diff(new_diff_variable) elif isinstance(symbol, pybamm.BinaryOperator): # process children new_left = self.process_symbol(symbol.left) new_right = self.process_symbol(symbol.right) # make new symbol, ensure domain remains the same new_symbol = symbol.__class__(new_left, new_right) new_symbol.domain = symbol.domain return new_symbol # Unary operators elif isinstance(symbol, pybamm.UnaryOperator): new_child = self.process_symbol(symbol.child) new_symbol = symbol._unary_new_copy(new_child) # ensure domain remains the same new_symbol.domain = symbol.domain return new_symbol # Functions elif isinstance(symbol, pybamm.Function): new_children = [self.process_symbol(child) for child in symbol.children] return symbol._function_new_copy(new_children) # Concatenations elif isinstance(symbol, pybamm.Concatenation): new_children = [self.process_symbol(child) for child in symbol.children] return symbol._concatenation_new_copy(new_children) else: # Backup option: return new copy of the object try: return symbol.new_copy() except NotImplementedError: raise NotImplementedError( "Cannot process parameters for symbol of type '{}'".format( type(symbol) ) )
fun.domain = domain fun.mesh = mesh.combine_submeshes(*domain) fun.secondary_mesh = None return fun comsol_c_n_surf = get_interp_fun("c_n_surf", ["negative electrode"]) comsol_c_e = get_interp_fun("c_e", whole_cell) comsol_c_p_surf = get_interp_fun("c_p_surf", ["positive electrode"]) comsol_phi_n = get_interp_fun("phi_n", ["negative electrode"]) comsol_phi_e = get_interp_fun("phi_e", whole_cell) comsol_phi_p = get_interp_fun("phi_p", ["positive electrode"]) comsol_voltage = pybamm.Interpolant( comsol_t, comsol_variables["voltage"], pybamm.t * pybamm_model.timescale.evaluate(), ) comsol_voltage.mesh = None comsol_voltage.secondary_mesh = None # Create comsol model with dictionary of Matrix variables comsol_model = pybamm.lithium_ion.BaseModel() comsol_model.variables = { "Negative particle surface concentration [mol.m-3]": comsol_c_n_surf, "Electrolyte concentration [mol.m-3]": comsol_c_e, "Positive particle surface concentration [mol.m-3]": comsol_c_p_surf, "Current [A]": pybamm_model.variables["Current [A]"], "Negative electrode potential [V]": comsol_phi_n, "Electrolyte potential [V]": comsol_phi_e,
def _process_symbol(self, symbol): """ See :meth:`ParameterValues.process_symbol()`. """ if isinstance(symbol, pybamm.Parameter): value = self[symbol.name] if isinstance(value, numbers.Number): # Scalar inherits name (for updating parameters) and domain (for # Broadcast) return pybamm.Scalar(value, name=symbol.name, domain=symbol.domain) elif isinstance(value, pybamm.Symbol): new_value = self.process_symbol(value) new_value.domain = symbol.domain return new_value else: raise TypeError("Cannot process parameter '{}'".format(value)) elif isinstance(symbol, pybamm.FunctionParameter): new_children = [] for child in symbol.children: if symbol.diff_variable is not None and any( x.id == symbol.diff_variable.id for x in child.pre_order()): # Wrap with NotConstant to avoid simplification, # which would stop symbolic diff from working properly new_child = pybamm.NotConstant(child.new_copy()) new_children.append(self.process_symbol(new_child)) else: new_children.append(self.process_symbol(child)) function_name = self[symbol.name] # Create Function or Interpolant or Scalar object if isinstance(function_name, tuple): # If function_name is a tuple then it should be (name, data) and we need # to create an Interpolant name, data = function_name function = pybamm.Interpolant(data[:, 0], data[:, 1], *new_children, name=name) # Define event to catch extrapolation. In these events the sign is # important: it should be positive inside of the range and negative # outside of it self.parameter_events.append( pybamm.Event( "Interpolant {} lower bound".format(name), pybamm.min(new_children[0] - min(data[:, 0])), pybamm.EventType.INTERPOLANT_EXTRAPOLATION, )) self.parameter_events.append( pybamm.Event( "Interpolant {} upper bound".format(name), pybamm.min(max(data[:, 0]) - new_children[0]), pybamm.EventType.INTERPOLANT_EXTRAPOLATION, )) elif isinstance(function_name, numbers.Number): # If the "function" is provided is actually a scalar, return a Scalar # object instead of throwing an error. # Also use ones_like so that we get the right shapes function = pybamm.Scalar( function_name, name=symbol.name) * pybamm.ones_like(*new_children) elif (isinstance(function_name, pybamm.Symbol) and function_name.evaluates_to_number()): # If the "function" provided is a pybamm scalar-like, use ones_like to # get the right shape # This also catches input parameters function = function_name * pybamm.ones_like(*new_children) elif callable(function_name): # otherwise evaluate the function to create a new PyBaMM object function = function_name(*new_children) elif isinstance(function_name, pybamm.Interpolant): function = function_name else: raise TypeError( "Parameter provided for '{}' ".format(symbol.name) + "is of the wrong type (should either be scalar-like or callable)" ) # Differentiate if necessary if symbol.diff_variable is None: function_out = function else: # return differentiated function new_diff_variable = self.process_symbol(symbol.diff_variable) function_out = function.diff(new_diff_variable) # Convert possible float output to a pybamm scalar if isinstance(function_out, numbers.Number): return pybamm.Scalar(function_out) # Process again just to be sure return self.process_symbol(function_out) elif isinstance(symbol, pybamm.BinaryOperator): # process children new_left = self.process_symbol(symbol.left) new_right = self.process_symbol(symbol.right) # Special case for averages, which can appear as "integral of a broadcast" # divided by "integral of a broadcast" # this construction seems very specific but can appear often when averaging if (isinstance(symbol, pybamm.Division) # right is integral(Broadcast(1)) and (isinstance(new_right, pybamm.Integral) and isinstance(new_right.child, pybamm.Broadcast) and new_right.child.child.id == pybamm.Scalar(1).id) # left is integral and isinstance(new_left, pybamm.Integral)): # left is integral(Broadcast) if (isinstance(new_left.child, pybamm.Broadcast) and new_left.child.child.domain == []): integrand = new_left.child if integrand.auxiliary_domains == {}: return integrand.orphans[0] else: domain = integrand.auxiliary_domains["secondary"] if "tertiary" not in integrand.auxiliary_domains: return pybamm.PrimaryBroadcast( integrand.orphans[0], domain) else: auxiliary_domains = { "secondary": integrand.auxiliary_domains["tertiary"] } return pybamm.FullBroadcast( integrand.orphans[0], domain, auxiliary_domains) # left is "integral of concatenation of broadcasts" elif isinstance(new_left.child, pybamm.Concatenation) and all( isinstance(child, pybamm.Broadcast) for child in new_left.child.children): return self.process_symbol(pybamm.x_average( new_left.child)) # make new symbol, ensure domain remains the same new_symbol = symbol._binary_new_copy(new_left, new_right) new_symbol.domain = symbol.domain return new_symbol # Unary operators elif isinstance(symbol, pybamm.UnaryOperator): new_child = self.process_symbol(symbol.child) new_symbol = symbol._unary_new_copy(new_child) # ensure domain remains the same new_symbol.domain = symbol.domain return new_symbol # Functions elif isinstance(symbol, pybamm.Function): new_children = [ self.process_symbol(child) for child in symbol.children ] return symbol._function_new_copy(new_children) # Concatenations elif isinstance(symbol, pybamm.Concatenation): new_children = [ self.process_symbol(child) for child in symbol.children ] return symbol._concatenation_new_copy(new_children) else: # Backup option: return new copy of the object try: return symbol.new_copy() except NotImplementedError: raise NotImplementedError( "Cannot process parameters for symbol of type '{}'".format( type(symbol)))
def _process_symbol(self, symbol): """ See :meth:`ParameterValues.process_symbol()`. """ if isinstance(symbol, pybamm.Parameter): value = self[symbol.name] if isinstance(value, numbers.Number): # Scalar inherits name (for updating parameters) and domain (for # Broadcast) return pybamm.Scalar(value, name=symbol.name, domain=symbol.domain) elif isinstance(value, pybamm.InputParameter): value.domain = symbol.domain return value elif isinstance(symbol, pybamm.FunctionParameter): new_children = [ self.process_symbol(child) for child in symbol.children ] function_name = self[symbol.name] # Create Function or Interpolant or Scalar object if isinstance(function_name, tuple): # If function_name is a tuple then it should be (name, data) and we need # to create an Interpolant name, data = function_name function = pybamm.Interpolant(data, *new_children, name=name) elif isinstance(function_name, numbers.Number): # If the "function" is provided is actually a scalar, return a Scalar # object instead of throwing an error. # Also use ones_like so that we get the right shapes function = pybamm.Scalar( function_name, name=symbol.name) * pybamm.ones_like(*new_children) elif isinstance(function_name, pybamm.InputParameter): # Replace the function with an input parameter function = function_name elif (isinstance(function_name, pybamm.Symbol) and function_name.evaluates_to_number()): # If the "function" provided is a pybamm scalar-like, use ones_like to # get the right shape function = function_name * pybamm.ones_like(*new_children) elif callable(function_name): # otherwise evaluate the function to create a new PyBaMM object function = function_name(*new_children) else: raise TypeError( "Parameter provided for '{}' ".format(symbol.name) + "is of the wrong type (should either be scalar-like or callable)" ) # Differentiate if necessary if symbol.diff_variable is None: function_out = function else: # return differentiated function new_diff_variable = self.process_symbol(symbol.diff_variable) function_out = function.diff(new_diff_variable) # Convert possible float output to a pybamm scalar if isinstance(function_out, numbers.Number): return pybamm.Scalar(function_out) # Process again just to be sure return self.process_symbol(function_out) elif isinstance(symbol, pybamm.BinaryOperator): # process children new_left = self.process_symbol(symbol.left) new_right = self.process_symbol(symbol.right) # make new symbol, ensure domain remains the same new_symbol = symbol._binary_new_copy(new_left, new_right) new_symbol.domain = symbol.domain return new_symbol # Unary operators elif isinstance(symbol, pybamm.UnaryOperator): new_child = self.process_symbol(symbol.child) new_symbol = symbol._unary_new_copy(new_child) # ensure domain remains the same new_symbol.domain = symbol.domain return new_symbol # Functions elif isinstance(symbol, pybamm.Function): new_children = [ self.process_symbol(child) for child in symbol.children ] return symbol._function_new_copy(new_children) # Concatenations elif isinstance(symbol, pybamm.Concatenation): new_children = [ self.process_symbol(child) for child in symbol.children ] return symbol._concatenation_new_copy(new_children) else: # Backup option: return new copy of the object try: return symbol.new_copy() except NotImplementedError: raise NotImplementedError( "Cannot process parameters for symbol of type '{}'".format( type(symbol)))
def test_processing(self): x = np.linspace(0, 1, 200) y = pybamm.StateVector(slice(0, 2)) interp = pybamm.Interpolant(x, 2 * x, y) self.assertEqual(interp.id, interp.new_copy().id)
def US06_experiment(reply=False): model = pybamm.lithium_ion.DFN() # import drive cycle from file if reply: drive_cycle = pd.read_csv("drive_cycle.csv", comment="#", header=None).to_numpy() elif not reply: drive_cycle = pd.read_csv("US06.csv", comment="#", header=None).to_numpy() # create interpolant param = model.default_parameter_values timescale = param.evaluate(model.timescale) current_interpolant = pybamm.Interpolant( drive_cycle[:, 0], drive_cycle[:, 1], timescale * pybamm.t ) # set drive cycle param["Current function [A]"] = current_interpolant sim_US06_1 = pybamm.Simulation( model, parameter_values=param, solver=pybamm.CasadiSolver(mode="fast") ) sol_US06_1 = sim_US06_1.solve() if reply: time = foo1.plot_graph(sol_US06_1, sim_US06_1, reply=reply) return time solved = False print("REACHED") while not solved: try: cycle = US06_experiment_cycle() # cycle = ["Charge at 1 A until 4.1 V", "Hold at 4.1 V until 50 mA"] experiment = pybamm.Experiment(cycle) sim_cccv = pybamm.Simulation(model, experiment=experiment) sol_cccv = sim_cccv.solve() new_model = model.set_initial_conditions_from(sol_cccv, inplace=False) sim_US06_2 = pybamm.Simulation( new_model, parameter_values=param, solver=pybamm.CasadiSolver(mode="fast"), ) sol_US06_2 = sim_US06_2.solve() # pybamm.dynamic_plot( # [sol_US06_1, sol_US06_2], # labels=["Default initial conditions", "Fully charged"], # ) solution = [sol_US06_1, sol_US06_2] sim = [sim_US06_1, sim_US06_2] t = solution[0]["Time [s]"] final_time = int(t.entries[len(t.entries) - 1]) time = random.randint(0, final_time) plot = pybamm.QuickPlot( sim, labels=["Default initial conditions", "Fully charged"], time_unit="seconds", ) plot.plot(time) plot.fig.savefig("foo.png", dpi=300) print(sol_US06_1) solved = True except: pass # print("Exception") return time, cycle