diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eb59a98d4..cd3fd0e6c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,7 @@ - Added functionality to broadcast to edges ([#891](https://github.com/pybamm-team/PyBaMM/pull/891)) - Reformatted and cleaned up `QuickPlot` ([#886](https://github.com/pybamm-team/PyBaMM/pull/886)) - Added thermal effects to lead-acid models ([#885](https://github.com/pybamm-team/PyBaMM/pull/885)) -- Add new symbols `VariableDot`, representing the derivative of a variable wrt time, - and `StateVectorDot`, representing the derivative of a state vector wrt time - ([#858](https://github.com/pybamm-team/PyBaMM/issues/858)) +- Added a helper function for info on function parameters ([#881](https://github.com/pybamm-team/PyBaMM/pull/881)) - Added additional notebooks showing how to create and compare models ([#877](https://github.com/pybamm-team/PyBaMM/pull/877)) - Added `Minimum`, `Maximum` and `Sign` operators ([#876](https://github.com/pybamm-team/PyBaMM/pull/876)) @@ -18,6 +16,9 @@ - Add ambient temperature as a function of time ([#872](https://github.com/pybamm-team/PyBaMM/pull/872)) - Added `CasadiAlgebraicSolver` for solving algebraic systems with CasADi ([#868](https://github.com/pybamm-team/PyBaMM/pull/868)) - Added electrolyte functions from Landesfeind ([#860](https://github.com/pybamm-team/PyBaMM/pull/860)) +- Add new symbols `VariableDot`, representing the derivative of a variable wrt time, + and `StateVectorDot`, representing the derivative of a state vector wrt time + ([#858](https://github.com/pybamm-team/PyBaMM/issues/858)) ## Bug fixes diff --git a/examples/notebooks/Creating Models/1-an-ode-model.ipynb b/examples/notebooks/Creating Models/1-an-ode-model.ipynb index 56228af093..00dbabe62c 100644 --- a/examples/notebooks/Creating Models/1-an-ode-model.ipynb +++ b/examples/notebooks/Creating Models/1-an-ode-model.ipynb @@ -271,7 +271,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.9" + "version": "3.7.5" } }, "nbformat": 4, diff --git a/examples/notebooks/Creating Models/2-a-pde-model.ipynb b/examples/notebooks/Creating Models/2-a-pde-model.ipynb index 384f016072..09acd1e1c5 100644 --- a/examples/notebooks/Creating Models/2-a-pde-model.ipynb +++ b/examples/notebooks/Creating Models/2-a-pde-model.ipynb @@ -298,7 +298,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.9" + "version": "3.7.5" } }, "nbformat": 4, diff --git a/examples/notebooks/Creating Models/3-negative-particle-problem.ipynb b/examples/notebooks/Creating Models/3-negative-particle-problem.ipynb index faba0f70aa..de365eab89 100644 --- a/examples/notebooks/Creating Models/3-negative-particle-problem.ipynb +++ b/examples/notebooks/Creating Models/3-negative-particle-problem.ipynb @@ -316,7 +316,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.9" + "version": "3.7.5" } }, "nbformat": 4, diff --git a/examples/notebooks/Creating Models/4-comparing-full-and-reduced-order-models.ipynb b/examples/notebooks/Creating Models/4-comparing-full-and-reduced-order-models.ipynb index b9c3e1ab02..c7d9dccbbe 100644 --- a/examples/notebooks/Creating Models/4-comparing-full-and-reduced-order-models.ipynb +++ b/examples/notebooks/Creating Models/4-comparing-full-and-reduced-order-models.ipynb @@ -375,7 +375,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.9" + "version": "3.7.5" } }, "nbformat": 4, diff --git a/examples/notebooks/Creating Models/5-a-simple-SEI-model.ipynb b/examples/notebooks/Creating Models/5-a-simple-SEI-model.ipynb index 53388008bf..d9ae44aa71 100644 --- a/examples/notebooks/Creating Models/5-a-simple-SEI-model.ipynb +++ b/examples/notebooks/Creating Models/5-a-simple-SEI-model.ipynb @@ -249,7 +249,7 @@ "c_inf_dim = pybamm.Parameter(\"Bulk electrolyte solvent concentration\")\n", "\n", "def D_dim(cc):\n", - " return pybamm.FunctionParameter(\"Diffusivity\", cc)\n", + " return pybamm.FunctionParameter(\"Diffusivity\", {\"Solvent concentration [mol.m-3]\": cc})\n", "\n", "# dimensionless parameters\n", "k = k_dim * L_0_dim / D_dim(c_inf_dim)\n", @@ -591,7 +591,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "b2a5f649a3b64685ad9510649f130829", + "model_id": "efe1fe18458a42d88056baf689f6da80", "version_major": 2, "version_minor": 0 }, @@ -653,7 +653,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.9" + "version": "3.7.5" } }, "nbformat": 4, diff --git a/examples/notebooks/parameter-values.ipynb b/examples/notebooks/parameter-values.ipynb index b83055f649..b3ec7afe6c 100644 --- a/examples/notebooks/parameter-values.ipynb +++ b/examples/notebooks/parameter-values.ipynb @@ -47,7 +47,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "parameter values are \n" + "parameter values are \n" ] } ], @@ -73,7 +73,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "parameter values are \n" + "parameter values are \n" ] } ], @@ -109,7 +109,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Marquis2019 chemistry set is {'chemistry': 'lithium-ion', 'cell': 'kokam_Marquis2019', 'anode': 'graphite_mcmb2528_Marquis2019', 'separator': 'separator_Marquis2019', 'cathode': 'lico2_Marquis2019', 'electrolyte': 'lipf6_Marquis2019', 'experiment': '1C_discharge_from_full_Marquis2019'}\n", + "Marquis2019 chemistry set is {'chemistry': 'lithium-ion', 'cell': 'kokam_Marquis2019', 'anode': 'graphite_mcmb2528_Marquis2019', 'separator': 'separator_Marquis2019', 'cathode': 'lico2_Marquis2019', 'electrolyte': 'lipf6_Marquis2019', 'experiment': '1C_discharge_from_full_Marquis2019', 'citation': 'marquis2019asymptotic'}\n", "Negative current collector thickness is 2.5e-05 m\n" ] } @@ -138,7 +138,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "parameter values are \n" + "parameter values are \n" ] } ], @@ -165,7 +165,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "parameter values are \n" + "parameter values are \n" ] } ], @@ -218,7 +218,7 @@ "a = pybamm.Parameter(\"a\")\n", "b = pybamm.Parameter(\"b\")\n", "c = pybamm.Parameter(\"c\")\n", - "func = pybamm.FunctionParameter(\"square function\", a)\n", + "func = pybamm.FunctionParameter(\"square function\", {\"a\": a})\n", "\n", "expr = a + b * c\n", "try:\n", @@ -329,7 +329,7 @@ "outputs": [ { "data": { - "image/png": "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fTfXXnttuf5WsUrH0I7H0JgxY8jIyOChhx76dV/p6el88sknDB+uzwYiUjM99tEi/u/HLqT0uImhax4PDbtNSQ0lnxWYN5WnCu5hwB+Ab8xsdrjtBqAtgLs/BrwFDAEWAluA88N968zsNuCr8PPGuPu66IW/E2nDIW04z36+mFve+Jb39jmSjpWyYxGRqmnTpk2cdtppPPjggyWuMkLo6tWECRP46KOPgFDyUPrK47vvvssbb7zBfffdB4SWBlm6dCldunTh6aefJi0tjYsvvpjDDjvs1+ecdNJJJCcnk5yczIABA/jf//5H8+bN2dVUjDvuuIM77riDu+66i4ceeohbb721RP9tt92GmTFr1ixGjx5drvmGzZs3jzifVMqvKh1Der9FpCZ765uV/N+0+Qzr2YohZw6BHVfarDC7TEDd/VNgpzXJPXRmv7yMvgnAhD2KLgoGd2/B6Knf8p9vVnLVPvWDCkNEpFzKe5Up2goKCjjttNM455xzOPXUUyNus33YYlncnVdffZVOnTrt0LdgwQLq1au3wwf+0gmImZGcnFxiHcbzzz+fWbNm0apVqxLz+ADOOecchgwZskMCuv11txchKs/SGnl5eTsMCa2KdAzteAxFUl3ebxGR3TVzyTquenk2B7ZtyL2np1X68lO7VQW3KmreIImD2zXmrW9WBh2KiEhMcndGjBhBly5d+Mtf/rLHr3P88cfzz3/+89erjbNmzQIgJyeHkSNH8vHHH7N27VomTZr063OmTJlCXl4ea9eu5cMPP+Tggw/mgAMOYPHixb9u89RTT5UoIrNgwYISz+/cufNux1q/fn02btxYou2HH36ge/fuu/1aEvvHkN5vEZGQRdmbuPCZDFo3TOaJcw8mKTG+0mOo9gkowNAeLflh9SYWrN64641FRGqYzz77jGeffZb333+fXr160atXrx2uNJbHzTffTEFBAWlpaXTr1o2bb74ZgKuvvprLL7+cAw44gCeffJLrrruONWvWAJCWlsaAAQM45JBDuPnmm2nVqhV169alQ4cOLFy4MOJ+rrvuOrp3705aWhrvvvtumUt+7ExaWhrx8fH07Nnz1yJEH3zwAUOHDt3t15LYP4YGDBjAvHnzfi1CBHq/RaTm+XlTPuc99RVxZjx9/sE0rltr10+qAFbR63DtifT0dM/IyIja663ZkEffu6Zz5cCO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QKF3vwpYyyiclJ1nH6Zl3f8IsNy7PwHY4PWl0rs/IWzxe8sdSuYEcgvbNvCefxa5T+w/j54nlD8blvU/iD7MIHMO2sm9po8DSrPuPw2cHcL4p3a/dsgknbe87ZHX6y8MffDml5M5/2Vc2LaBtz7vBG7sZ5lIvBeGcZ1EOkcOog+hzZN5xh/aHJlvH7zllCdHL/7Q58g8+qA8OfKxhypPjsoGHMaVeS6ZUZzeABZ505aQ+RUUoAj4vffGfBaYndV2kdfuNeCckMb/KLAZeNG7LfWmnwys8t6Iq4DPh3gb/A/wshfrE8ChWW0/522bdcBnwxi/9/9i4Pu92oViG5D5la0B6CRzTP7ngcuAy7z5BvzU698q4Pgwrf88+3AbsD3rfbDSmz7bW/8vea+xRSGN/6tZ74HlZH1J8Hv9hbEP3jKfITMwTXa7sGyDU8mcY/OvrNfJuVF7L4zAeol0jsyzD6HOk3nEH+ocmU8fvP8Xozw5WvGHOkfm2QflyZGPP1R5svvXBhEREREREZERFeVzQEVERERERCRCVICKiIiIiIhIIFSAioiIiIiISCBUgIqIiIiIiEggVICKiIiIiIhIIFSAioxhZlZhZl8e7ThERETCSHlSJHgqQEXGtgpAiVVERMSf8qRIwFSAioxt3wcONLMXzeyHox2MiIhIyChPigTMnHOjHYOIjBAzmwXc75ybO8qhiIiIhI7ypEjwtAdUREREREREAqECVERERERERAKhAlRkbNsBlI52ECIiIiGlPCkSMBWgImOYc64R+IeZrdbgCiIiIj0pT4oET4MQiYiIiIiISCC0B1REREREREQCoQJUREREREREAqECVERERERERAKhAlREREREREQCoQJUREREREREAqECVERERERERAKhAlREREREREQC8f/tjayVNCsiOgAAAABJRU5ErkJggg==\n", + "image/png": "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fTfXXnttuf5WsUrH0I7H0JgxY8jIyOChhx76dV/p6el88sknDB+uzwYiUjM99tEi/u/HLqT0uImhax4PDbtNSQ0lnxWYN5WnCu5hwB+Ab8xsdrjtBqAtgLs/BrwFDAEWAluA88N968zsNuCr8PPGuPu66IW/E2nDIW04z36+mFve+Jb39jmSjpWyYxGRqmnTpk2cdtppPPjggyWuMkLo6tWECRP46KOPgFDyUPrK47vvvssbb7zBfffdB4SWBlm6dCldunTh6aefJi0tjYsvvpjDDjvs1+ecdNJJJCcnk5yczIABA/jf//5H8+bN2dVUjDvuuIM77riDu+66i4ceeohbb721RP9tt92GmTFr1ixGjx5drvmGzZs3jzifVMqvKh1Der9FpCZ765uV/N+0+Qzr2YohZw6BHVfarDC7TEDd/VNgpzXJPXRmv7yMvgnAhD2KLgoGd2/B6Knf8p9vVnLVPvWDCkNEpFzKe5Up2goKCjjttNM455xzOPXUUyNus33YYlncnVdffZVOnTrt0LdgwQLq1au3wwf+0gmImZGcnFxiHcbzzz+fWbNm0apVqxLz+ADOOecchgwZskMCuv11txchKs/SGnl5eTsMCa2KdAzteAxFUl3ebxGR3TVzyTquenk2B7ZtyL2np1X68lO7VQW3KmreIImD2zXmrW9WBh2KiEhMcndGjBhBly5d+Mtf/rLHr3P88cfzz3/+89erjbNmzQIgJyeHkSNH8vHHH7N27VomTZr063OmTJlCXl4ea9eu5cMPP+Tggw/mgAMOYPHixb9u89RTT5UoIrNgwYISz+/cufNux1q/fn02btxYou2HH36ge/fuu/1aEvvHkN5vEZGQRdmbuPCZDFo3TOaJcw8mKTG+0mOo9gkowNAeLflh9SYWrN64641FRGqYzz77jGeffZb333+fXr160atXrx2uNJbHzTffTEFBAWlpaXTr1o2bb74ZgKuvvprLL7+cAw44gCeffJLrrruONWvWAJCWlsaAAQM45JBDuPnmm2nVqhV169alQ4cOLFy4MOJ+rrvuOrp3705aWhrvvvtumUt+7ExaWhrx8fH07Nnz1yJEH3zwAUOHDt3t15LYP4YGDBjAvHnzfi1CBHq/RaTm+XlTPuc99RVxZjx9/sE0rltr10+qAFbR63DtifT0dM/IyIja663ZkEffu6Zz5cCO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QKF3vwpYyyiclJ1nH6Zl3f8IsNy7PwHY4PWl0rs/IWzxe8sdSuYEcgvbNvCefxa5T+w/j54nlD8blvU/iD7MIHMO2sm9po8DSrPuPw2cHcL4p3a/dsgknbe87ZHX6y8MffDml5M5/2Vc2LaBtz7vBG7sZ5lIvBeGcZ1EOkcOog+hzZN5xh/aHJlvH7zllCdHL/7Q58g8+qA8OfKxhypPjsoGHMaVeS6ZUZzeABZ505aQ+RUUoAj4vffGfBaYndV2kdfuNeCckMb/KLAZeNG7LfWmnwys8t6Iq4DPh3gb/A/wshfrE8ChWW0/522bdcBnwxi/9/9i4Pu92oViG5D5la0B6CRzTP7ngcuAy7z5BvzU698q4Pgwrf88+3AbsD3rfbDSmz7bW/8vea+xRSGN/6tZ74HlZH1J8Hv9hbEP3jKfITMwTXa7sGyDU8mcY/OvrNfJuVF7L4zAeol0jsyzD6HOk3nEH+ocmU8fvP8Xozw5WvGHOkfm2QflyZGPP1R5svvXBhEREREREZERFeVzQEVERERERCRCVICKiIiIiIhIIFSAioiIiIiISCBUgIqIiIiIiEggVICKiIiIiIhIIFSAioxhZlZhZl8e7ThERETCSHlSJHgqQEXGtgpAiVVERMSf8qRIwFSAioxt3wcONLMXzeyHox2MiIhIyChPigTMnHOjHYOIjBAzmwXc75ybO8qhiIiIhI7ypEjwtAdUREREREREAqECVERERERERAKhAlRkbNsBlI52ECIiIiGlPCkSMBWgImOYc64R+IeZrdbgCiIiIj0pT4oET4MQiYiIiIiISCC0B1REREREREQCoQJUREREREREAqECVERERERERAKhAlREREREREQCoQJUREREREREAqECVERERERERAKhAlREREREREQC8f/tjayVNCsiOgAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] @@ -401,7 +401,7 @@ { "data": { "text/plain": [ - "{Variable(-0x4a013e7c3cccb9f1, u, children=[], domain=[], auxiliary_domains={}): Multiplication(0x786762aded2a40a5, *, children=['-a', 'y[0:1]'], domain=[], auxiliary_domains={})}" + "{Variable(-0x2cdf9e1c15ea083, u, children=[], domain=[], auxiliary_domains={}): Multiplication(0x1d65a94f24de5058, *, children=['-a', 'y[0:1]'], domain=[], auxiliary_domains={})}" ] }, "execution_count": 12, @@ -481,7 +481,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.7.5" } }, "nbformat": 4, diff --git a/examples/scripts/create-model.py b/examples/scripts/create-model.py index 8c2d702835..73b9161351 100644 --- a/examples/scripts/create-model.py +++ b/examples/scripts/create-model.py @@ -18,7 +18,7 @@ def D_dim(cc): - return pybamm.FunctionParameter("Diffusivity", cc) + return pybamm.FunctionParameter("Diffusivity", {"Concentration [mol.m-3]": cc}) # dimensionless parameters diff --git a/pybamm/expression_tree/parameter.py b/pybamm/expression_tree/parameter.py index 9c2049ac8c..eca440ebd2 100644 --- a/pybamm/expression_tree/parameter.py +++ b/pybamm/expression_tree/parameter.py @@ -48,19 +48,23 @@ class FunctionParameter(pybamm.Symbol): name : str name of the node - child : :class:`Symbol` - child node + inputs : dict + A dictionary with string keys and :class:`pybamm.Symbol` values representing + the function inputs. The string keys should provide a reasonable description + of what the input to the function is + (e.g. "Electrolyte concentration [mol.m-3]") diff_variable : :class:`pybamm.Symbol`, optional if diff_variable is specified, the FunctionParameter node will be replaced by a :class:`pybamm.Function` and then differentiated with respect to diff_variable. Default is None. - """ - def __init__(self, name, *children, diff_variable=None): + def __init__( + self, name, inputs, diff_variable=None, + ): # assign diff variable self.diff_variable = diff_variable - children_list = list(children) + children_list = list(inputs.values()) # Turn numbers into scalars for idx, child in enumerate(children_list): @@ -76,6 +80,37 @@ def __init__(self, name, *children, diff_variable=None): auxiliary_domains=auxiliary_domains, ) + self.input_names = list(inputs.keys()) + + @property + def input_names(self): + return self._input_names + + def print_input_names(self): + if self._input_names: + for inp in self._input_names: + print(inp) + + @input_names.setter + def input_names(self, inp=None): + if inp: + if inp.__class__ is list: + for i in inp: + if i.__class__ is not str: + raise TypeError( + "Inputs must be a provided as" + + "a dictionary of the form:" + + "{{str: :class:`pybamm.Symbol`}}" + ) + else: + raise TypeError( + "Inputs must be a provided as" + + " a dictionary of the form:" + + "{{str: :class:`pybamm.Symbol`}}" + ) + + self._input_names = inp + def set_id(self): """See :meth:`pybamm.Symbol.set_id` """ self._id = hash( @@ -107,17 +142,24 @@ def diff(self, variable): """ See :meth:`pybamm.Symbol.diff()`. """ # return a new FunctionParameter, that knows it will need to be differentiated # when the parameters are set - return FunctionParameter(self.name, *self.orphans, diff_variable=variable) + children_list = self.orphans + input_names = self._input_names + + input_dict = {input_names[i]: children_list[i] for i in range(len(input_names))} + + return FunctionParameter(self.name, input_dict, diff_variable=variable) def new_copy(self): """ See :meth:`pybamm.Symbol.new_copy()`. """ - return self._function_parameter_new_copy(self.orphans) + return self._function_parameter_new_copy(self._input_names, self.orphans) - def _function_parameter_new_copy(self, children): + def _function_parameter_new_copy(self, input_names, children): """Returns a new copy of the function parameter. Inputs ------ + input_names : : list + A list of str of the names of the children/function inputs children : : list A list of the children of the function @@ -126,7 +168,12 @@ def _function_parameter_new_copy(self, children): : :pybamm.FunctionParameter A new copy of the function parameter """ - return FunctionParameter(self.name, *children, diff_variable=self.diff_variable) + + input_dict = {input_names[i]: children[i] for i in range(len(input_names))} + + return FunctionParameter( + self.name, input_dict, diff_variable=self.diff_variable + ) def _evaluate_for_shape(self): """ diff --git a/pybamm/models/base_model.py b/pybamm/models/base_model.py index 641ef347fa..410d5a9a68 100644 --- a/pybamm/models/base_model.py +++ b/pybamm/models/base_model.py @@ -398,13 +398,13 @@ def check_for_time_derivatives(self): for node in eq.pre_order(): if isinstance(node, pybamm.VariableDot): raise pybamm.ModelError( - "time derivative of variable found ({}) in rhs equation {}" - .format(node, key) + "time derivative of variable" + + " found ({}) in rhs equation {}".format(node, key) ) if isinstance(node, pybamm.StateVectorDot): raise pybamm.ModelError( - "time derivative of state vector found ({}) in rhs equation {}" - .format(node, key) + "time derivative of state vector" + + " found ({}) in rhs equation {}".format(node, key) ) # Check that no variable time derivatives exist in the algebraic equations @@ -441,8 +441,11 @@ def check_well_determined(self, post_discretisation): [x.id for x in eqn.pre_order() if isinstance(x, pybamm.Variable)] ) vars_in_eqns.update( - [x.get_variable().id for x in eqn.pre_order() - if isinstance(x, pybamm.VariableDot)] + [ + x.get_variable().id + for x in eqn.pre_order() + if isinstance(x, pybamm.VariableDot) + ] ) for var, eqn in self.algebraic.items(): vars_in_algebraic_keys.update( @@ -452,8 +455,11 @@ def check_well_determined(self, post_discretisation): [x.id for x in eqn.pre_order() if isinstance(x, pybamm.Variable)] ) vars_in_eqns.update( - [x.get_variable().id for x in eqn.pre_order() - if isinstance(x, pybamm.VariableDot)] + [ + x.get_variable().id + for x in eqn.pre_order() + if isinstance(x, pybamm.VariableDot) + ] ) for var, side_eqn in self.boundary_conditions.items(): for side, (eqn, typ) in side_eqn.items(): @@ -461,8 +467,11 @@ def check_well_determined(self, post_discretisation): [x.id for x in eqn.pre_order() if isinstance(x, pybamm.Variable)] ) vars_in_eqns.update( - [x.get_variable().id for x in eqn.pre_order() - if isinstance(x, pybamm.VariableDot)] + [ + x.get_variable().id + for x in eqn.pre_order() + if isinstance(x, pybamm.VariableDot) + ] ) # If any keys are repeated between rhs and algebraic then the model is # overdetermined @@ -614,6 +623,32 @@ def check_variables(self): ) ) + def info(self, symbol_name): + """ + Provides helpful summary information for a symbol. + + Parameters + ---------- + parameter_name : str + """ + + div = "-----------------------------------------" + symbol = find_symbol_in_model(self, symbol_name) + + if not symbol: + return None + + print(div) + print(symbol_name, "\n") + print(type(symbol)) + + if isinstance(symbol, pybamm.FunctionParameter): + print("") + print("Inputs:") + symbol.print_input_names() + + print(div) + @property def default_solver(self): "Return default solver based on whether model is ODE model or DAE model" @@ -624,3 +659,33 @@ def default_solver(self): return pybamm.IDAKLUSolver() else: return pybamm.CasadiSolver(mode="safe") + + +# helper functions for finding symbols +def find_symbol_in_tree(tree, name): + if name == tree.name: + return tree + elif len(tree.children) > 0: + for child in tree.children: + child_return = find_symbol_in_tree(child, name) + if child_return: + return child_return + + +def find_symbol_in_dict(dic, name): + for tree in dic.values(): + tree_return = find_symbol_in_tree(tree, name) + if tree_return: + return tree_return + + +def find_symbol_in_model(model, name): + dics = [ + model.rhs, + model.algebraic, + model.variables, + ] + for dic in dics: + dic_return = find_symbol_in_dict(dic, name) + if dic_return: + return dic_return diff --git a/pybamm/models/submodels/external_circuit/function_control_external_circuit.py b/pybamm/models/submodels/external_circuit/function_control_external_circuit.py index f08a12d7dd..079b0fb4c5 100644 --- a/pybamm/models/submodels/external_circuit/function_control_external_circuit.py +++ b/pybamm/models/submodels/external_circuit/function_control_external_circuit.py @@ -58,7 +58,7 @@ def __init__(self, param): def constant_voltage(variables): V = variables["Terminal voltage [V]"] - return V - pybamm.FunctionParameter("Voltage function [V]", pybamm.t) + return V - pybamm.FunctionParameter("Voltage function [V]", {"Time [s]": pybamm.t}) class PowerFunctionControl(FunctionControl): @@ -71,7 +71,9 @@ def __init__(self, param): def constant_power(variables): I = variables["Current [A]"] V = variables["Terminal voltage [V]"] - return I * V - pybamm.FunctionParameter("Power function [W]", pybamm.t) + return I * V - pybamm.FunctionParameter( + "Power function [W]", {"Time [s]": pybamm.t} + ) class LeadingOrderFunctionControl(FunctionControl, LeadingOrderBaseModel): diff --git a/pybamm/models/submodels/particle/fickian_many_particles.py b/pybamm/models/submodels/particle/fickian_many_particles.py index 343e386603..3cb2a639ac 100644 --- a/pybamm/models/submodels/particle/fickian_many_particles.py +++ b/pybamm/models/submodels/particle/fickian_many_particles.py @@ -52,12 +52,18 @@ def get_coupled_variables(self, variables): if self.domain == "Negative": x = pybamm.standard_spatial_vars.x_n - R = pybamm.FunctionParameter("Negative particle distribution in x", x) + R = pybamm.FunctionParameter( + "Negative particle distribution in x", + {"Dimensionless through-cell position (x_n)": x}, + ) variables.update({"Negative particle distribution in x": R}) elif self.domain == "Positive": x = pybamm.standard_spatial_vars.x_p - R = pybamm.FunctionParameter("Positive particle distribution in x", x) + R = pybamm.FunctionParameter( + "Positive particle distribution in x", + {"Dimensionless through-cell position (x_p)": x}, + ) variables.update({"Positive particle distribution in x": R}) return variables diff --git a/pybamm/parameters/electrical_parameters.py b/pybamm/parameters/electrical_parameters.py index c83405ca5a..b98c0d2e7f 100644 --- a/pybamm/parameters/electrical_parameters.py +++ b/pybamm/parameters/electrical_parameters.py @@ -26,7 +26,7 @@ # the user may provide the typical timescale as a parameter. timescale = pybamm.Parameter("Typical timescale [s]") dimensional_current_with_time = pybamm.FunctionParameter( - "Current function [A]", pybamm.t * timescale + "Current function [A]", {"Time[s]": pybamm.t * timescale} ) dimensional_current_density_with_time = dimensional_current_with_time / ( n_electrodes_parallel * pybamm.geometric_parameters.A_cc diff --git a/pybamm/parameters/standard_parameters_lead_acid.py b/pybamm/parameters/standard_parameters_lead_acid.py index 7e1aa86bf0..15e45d3fcd 100644 --- a/pybamm/parameters/standard_parameters_lead_acid.py +++ b/pybamm/parameters/standard_parameters_lead_acid.py @@ -171,22 +171,26 @@ def t_plus(c_e): - "Dimensionless transference number (i.e. c_e is dimensional)" - return pybamm.FunctionParameter("Cation transference number", c_e * c_e_typ) + "Dimensionless transference number (i.e. c_e is dimensionless)" + inputs = {"Electrolyte concentration [mol.m-3]": c_e * c_e_typ} + return pybamm.FunctionParameter("Cation transference number", inputs) def D_e_dimensional(c_e, T): "Dimensional diffusivity in electrolyte" - return pybamm.FunctionParameter("Electrolyte diffusivity [m2.s-1]", c_e) + inputs = {"Electrolyte concentration [mol.m-3]": c_e} + return pybamm.FunctionParameter("Electrolyte diffusivity [m2.s-1]", inputs) def kappa_e_dimensional(c_e, T): "Dimensional electrolyte conductivity" - return pybamm.FunctionParameter("Electrolyte conductivity [S.m-1]", c_e) + inputs = {"Electrolyte concentration [mol.m-3]": c_e} + return pybamm.FunctionParameter("Electrolyte conductivity [S.m-1]", inputs) def chi_dimensional(c_e): - return pybamm.FunctionParameter("Darken thermodynamic factor", c_e) + inputs = {"Electrolyte concentration [mol.m-3]": c_e} + return pybamm.FunctionParameter("Darken thermodynamic factor", inputs) def c_w_dimensional(c_e, c_ox=0, c_hy=0): @@ -227,31 +231,37 @@ def mu_dimensional(c_e): """ Dimensional viscosity of electrolyte [kg.m-1.s-1]. """ - return pybamm.FunctionParameter("Electrolyte viscosity [kg.m-1.s-1]", c_e) + inputs = {"Electrolyte concentration [mol.m-3]": c_e} + return pybamm.FunctionParameter("Electrolyte viscosity [kg.m-1.s-1]", inputs) def U_n_dimensional(c_e, T): "Dimensional open-circuit voltage in the negative electrode [V]" + inputs = {"Electrolyte molar mass [mol.kg-1]": m_dimensional(c_e)} return pybamm.FunctionParameter( - "Negative electrode open-circuit potential [V]", m_dimensional(c_e) + "Negative electrode open-circuit potential [V]", inputs ) def U_p_dimensional(c_e, T): "Dimensional open-circuit voltage in the positive electrode [V]" + inputs = {"Electrolyte molar mass [mol.kg-1]": m_dimensional(c_e)} return pybamm.FunctionParameter( - "Positive electrode open-circuit potential [V]", m_dimensional(c_e) + "Positive electrode open-circuit potential [V]", inputs ) D_e_typ = D_e_dimensional(c_e_typ, T_ref) rho_typ = rho_dimensional(c_e_typ) mu_typ = mu_dimensional(c_e_typ) + +inputs = {"Electrolyte concentration [mol.m-3]": pybamm.Scalar(1)} U_n_ref = pybamm.FunctionParameter( - "Negative electrode open-circuit potential [V]", pybamm.Scalar(1) + "Negative electrode open-circuit potential [V]", inputs ) +inputs = {"Electrolyte concentration [mol.m-3]": pybamm.Scalar(1)} U_p_ref = pybamm.FunctionParameter( - "Positive electrode open-circuit potential [V]", pybamm.Scalar(1) + "Positive electrode open-circuit potential [V]", inputs ) @@ -530,7 +540,7 @@ def U_p(c_e_p, T): # 6. Input current and voltage dimensional_current_with_time = pybamm.FunctionParameter( - "Current function [A]", pybamm.t * timescale + "Current function [A]", {"Time [s]": pybamm.t * timescale} ) dimensional_current_density_with_time = dimensional_current_with_time / ( n_electrodes_parallel * pybamm.geometric_parameters.A_cc @@ -538,3 +548,7 @@ def U_p(c_e_p, T): current_with_time = ( dimensional_current_with_time / I_typ * pybamm.Function(np.sign, I_typ) ) + + +"Remove any temporary variables" +del inputs diff --git a/pybamm/parameters/standard_parameters_lithium_ion.py b/pybamm/parameters/standard_parameters_lithium_ion.py index cf5e5dffc6..2b3a8323bc 100644 --- a/pybamm/parameters/standard_parameters_lithium_ion.py +++ b/pybamm/parameters/standard_parameters_lithium_ion.py @@ -108,15 +108,17 @@ def c_n_init_dimensional(x): "Initial concentration as a function of dimensionless position x" + inputs = {"Dimensionless through-cell position (x_n)": x} return pybamm.FunctionParameter( - "Initial concentration in negative electrode [mol.m-3]", x + "Initial concentration in negative electrode [mol.m-3]", inputs ) def c_p_init_dimensional(x): "Initial concentration as a function of dimensionless position x" + inputs = {"Dimensionless through-cell position (x_p)": x} return pybamm.FunctionParameter( - "Initial concentration in positive electrode [mol.m-3]", x + "Initial concentration in positive electrode [mol.m-3]", inputs ) @@ -140,54 +142,88 @@ def c_p_init_dimensional(x): def D_e_dimensional(c_e, T): "Dimensional diffusivity in electrolyte" - return pybamm.FunctionParameter( - "Electrolyte diffusivity [m2.s-1]", c_e, T, T_ref, E_D_e, R - ) + inputs = { + "Electrolyte concentration [mol.m-3]": c_e, + "Temperature [K]": T, + "Reference temperature [K]": T_ref, + "Activation energy [J.mol-1]": E_D_e, + "Ideal gas constant [J.mol-1.K-1]": R, + } + return pybamm.FunctionParameter("Electrolyte diffusivity [m2.s-1]", inputs) def kappa_e_dimensional(c_e, T): "Dimensional electrolyte conductivity" - return pybamm.FunctionParameter( - "Electrolyte conductivity [S.m-1]", c_e, T, T_ref, E_k_e, R - ) + inputs = { + "Electrolyte concentration [mol.m-3]": c_e, + "Temperature [K]": T, + "Reference temperature [K]": T_ref, + "Activation energy [J.mol-1]": E_k_e, + "Ideal gas constant [J.mol-1.K-1]": R, + } + return pybamm.FunctionParameter("Electrolyte conductivity [S.m-1]", inputs) def D_n_dimensional(sto, T): """Dimensional diffusivity in negative particle. Note this is defined as a function of stochiometry""" - return pybamm.FunctionParameter( - "Negative electrode diffusivity [m2.s-1]", sto, T, T_ref, E_D_s_n, R - ) + + inputs = { + "Negative particle stoichiometry": sto, + "Temperature [K]": T, + "Reference temperature [K]": T_ref, + "Activation energy [J.mol-1]": E_D_s_n, + "Ideal gas constant [J.mol-1.K-1]": R, + } + + return pybamm.FunctionParameter("Negative electrode diffusivity [m2.s-1]", inputs) def D_p_dimensional(sto, T): """Dimensional diffusivity in positive particle. Note this is defined as a function of stochiometry""" - return pybamm.FunctionParameter( - "Positive electrode diffusivity [m2.s-1]", sto, T, T_ref, E_D_s_p, R - ) + inputs = { + "Positive particle stoichiometry": sto, + "Temperature [K]": T, + "Reference temperature [K]": T_ref, + "Activation energy [J.mol-1]": E_D_s_p, + "Ideal gas constant [J.mol-1.K-1]": R, + } + return pybamm.FunctionParameter("Positive electrode diffusivity [m2.s-1]", inputs) def m_n_dimensional(T): "Dimensional negative reaction rate" - return pybamm.FunctionParameter( - "Negative electrode reaction rate", T, T_ref, E_r_n, R - ) + inputs = { + "Temperature [K]": T, + "Reference temperature [K]": T_ref, + "Activation energy [J.mol-1]": E_r_n, + "Ideal gas constant [J.mol-1.K-1]": R, + } + return pybamm.FunctionParameter("Negative electrode reaction rate", inputs) def m_p_dimensional(T): "Dimensional negative reaction rate" - return pybamm.FunctionParameter( - "Positive electrode reaction rate", T, T_ref, E_r_p, R - ) + inputs = { + "Temperature [K]": T, + "Reference temperature [K]": T_ref, + "Activation energy [J.mol-1]": E_r_p, + "Ideal gas constant [J.mol-1.K-1]": R, + } + return pybamm.FunctionParameter("Positive electrode reaction rate", inputs) def dUdT_n_dimensional(sto): """ Dimensional entropic change of the negative electrode open-circuit potential [V.K-1] """ + inputs = { + "Negative particle stoichiometry": sto, + "Max negative particle concentration [mol.m-3]": c_n_max, + } return pybamm.FunctionParameter( - "Negative electrode OCP entropic change [V.K-1]", sto, c_n_max + "Negative electrode OCP entropic change [V.K-1]", inputs ) @@ -195,20 +231,26 @@ def dUdT_p_dimensional(sto): """ Dimensional entropic change of the positive electrode open-circuit potential [V.K-1] """ + inputs = { + "Positive particle stoichiometry": sto, + "Max positive particle concentration [mol.m-3]": c_p_max, + } return pybamm.FunctionParameter( - "Positive electrode OCP entropic change [V.K-1]", sto, c_p_max + "Positive electrode OCP entropic change [V.K-1]", inputs ) def U_n_dimensional(sto, T): "Dimensional open-circuit potential in the negative electrode [V]" - u_ref = pybamm.FunctionParameter("Negative electrode OCP [V]", sto) + inputs = {"Negative particle stoichiometry": sto} + u_ref = pybamm.FunctionParameter("Negative electrode OCP [V]", inputs) return u_ref + (T - T_ref) * dUdT_n_dimensional(sto) def U_p_dimensional(sto, T): "Dimensional open-circuit potential in the positive electrode [V]" - u_ref = pybamm.FunctionParameter("Positive electrode OCP [V]", sto) + inputs = {"Positive particle stoichiometry": sto} + u_ref = pybamm.FunctionParameter("Positive electrode OCP [V]", inputs) return u_ref + (T - T_ref) * dUdT_p_dimensional(sto) @@ -292,16 +334,18 @@ def U_p_dimensional(sto, T): centre_z_tab_p = pybamm.geometric_parameters.centre_z_tab_p # Microscale geometry -epsilon_n = pybamm.FunctionParameter( - "Negative electrode porosity", pybamm.standard_spatial_vars.x_n -) -epsilon_s = pybamm.FunctionParameter( - "Separator porosity", pybamm.standard_spatial_vars.x_s -) -epsilon_p = pybamm.FunctionParameter( - "Positive electrode porosity", pybamm.standard_spatial_vars.x_p -) + +inputs = {"Through-cell distance (x_n) [m]": pybamm.standard_spatial_vars.x_n} +epsilon_n = pybamm.FunctionParameter("Negative electrode porosity", inputs) + +inputs = {"Through-cell distance (x_s) [m]": pybamm.standard_spatial_vars.x_s} +epsilon_s = pybamm.FunctionParameter("Separator porosity", inputs) + +inputs = {"Through-cell distance (x_p) [m]": pybamm.standard_spatial_vars.x_p} +epsilon_p = pybamm.FunctionParameter("Positive electrode porosity", inputs) + epsilon = pybamm.Concatenation(epsilon_n, epsilon_s, epsilon_p) + epsilon_s_n = pybamm.Parameter("Negative electrode active material volume fraction") epsilon_s_p = pybamm.Parameter("Positive electrode active material volume fraction") epsilon_inactive_n = 1 - epsilon_n - epsilon_s_n @@ -329,11 +373,14 @@ def U_p_dimensional(sto, T): def t_plus(c_e): - return pybamm.FunctionParameter("Cation transference number", c_e) + "Dimensionless transference number (i.e. c_e is dimensionless)" + inputs = {"Electrolyte concentration [mol.m-3]": c_e * c_e_typ} + return pybamm.FunctionParameter("Cation transference number", inputs) def one_plus_dlnf_dlnc(c_e): - return pybamm.FunctionParameter("1 + dlnf/dlnc", c_e) + inputs = {"Electrolyte concentration [mol.m-3]": c_e * c_e_typ} + return pybamm.FunctionParameter("1 + dlnf/dlnc", inputs) beta_surf = pybamm.Scalar(0) @@ -481,7 +528,7 @@ def dUdT_p(c_s_p): # 6. Input current and voltage dimensional_current_with_time = pybamm.FunctionParameter( - "Current function [A]", pybamm.t * timescale + "Current function [A]", {"Time [s]": pybamm.t * timescale} ) dimensional_current_density_with_time = dimensional_current_with_time / ( n_electrodes_parallel * pybamm.geometric_parameters.A_cc @@ -489,3 +536,7 @@ def dUdT_p(c_s_p): current_with_time = ( dimensional_current_with_time / I_typ * pybamm.Function(np.sign, I_typ) ) + + +"Remove any temporary variables" +del inputs diff --git a/pybamm/parameters/thermal_parameters.py b/pybamm/parameters/thermal_parameters.py index 5273d369f3..15f0f9ee8f 100644 --- a/pybamm/parameters/thermal_parameters.py +++ b/pybamm/parameters/thermal_parameters.py @@ -116,7 +116,7 @@ def T_amb_dim(t): - return pybamm.FunctionParameter("Ambient temperature [K]", t) + return pybamm.FunctionParameter("Ambient temperature [K]", {"Times [s]": t}) def T_amb(t): diff --git a/tests/unit/test_expression_tree/test_operations/test_copy.py b/tests/unit/test_expression_tree/test_operations/test_copy.py index 32afe6c273..e3fdb43c7d 100644 --- a/tests/unit/test_expression_tree/test_operations/test_copy.py +++ b/tests/unit/test_expression_tree/test_operations/test_copy.py @@ -25,7 +25,7 @@ def test_symbol_new_copy(self): -a, abs(a), pybamm.Function(np.sin, a), - pybamm.FunctionParameter("function", a), + pybamm.FunctionParameter("function", {"a": a}), pybamm.grad(a), pybamm.div(a), pybamm.Integral(a, pybamm.t), diff --git a/tests/unit/test_expression_tree/test_operations/test_simplify.py b/tests/unit/test_expression_tree/test_operations/test_simplify.py index 2cd37f7537..d898709a7f 100644 --- a/tests/unit/test_expression_tree/test_operations/test_simplify.py +++ b/tests/unit/test_expression_tree/test_operations/test_simplify.py @@ -46,11 +46,11 @@ def myfunction(x, y): self.assertEqual((f).simplify().evaluate(), 0) # FunctionParameter - f = pybamm.FunctionParameter("function", b) + f = pybamm.FunctionParameter("function", {"b": b}) self.assertIsInstance((f).simplify(), pybamm.FunctionParameter) self.assertEqual((f).simplify().children[0].id, b.id) - f = pybamm.FunctionParameter("function", a, b) + f = pybamm.FunctionParameter("function", {"a": a, "b": b}) self.assertIsInstance((f).simplify(), pybamm.FunctionParameter) self.assertEqual((f).simplify().children[0].id, a.id) self.assertEqual((f).simplify().children[1].id, b.id) diff --git a/tests/unit/test_expression_tree/test_parameter.py b/tests/unit/test_expression_tree/test_parameter.py index fc03e73f45..ee3231a0d8 100644 --- a/tests/unit/test_expression_tree/test_parameter.py +++ b/tests/unit/test_expression_tree/test_parameter.py @@ -22,7 +22,7 @@ def test_evaluate_for_shape(self): class TestFunctionParameter(unittest.TestCase): def test_function_parameter_init(self): var = pybamm.Variable("var") - func = pybamm.FunctionParameter("func", var) + func = pybamm.FunctionParameter("func", {"var": var}) self.assertEqual(func.name, "func") self.assertEqual(func.children[0].id, var.id) self.assertEqual(func.domain, []) @@ -30,14 +30,50 @@ def test_function_parameter_init(self): def test_function_parameter_diff(self): var = pybamm.Variable("var") - func = pybamm.FunctionParameter("a", var).diff(var) + func = pybamm.FunctionParameter("a", {"var": var}).diff(var) self.assertEqual(func.diff_variable, var) def test_evaluate_for_shape(self): a = pybamm.Parameter("a") - func = pybamm.FunctionParameter("func", 2 * a) + func = pybamm.FunctionParameter("func", {"2a": 2 * a}) self.assertIsInstance(func.evaluate_for_shape(), numbers.Number) + def test_copy(self): + a = pybamm.Parameter("a") + func = pybamm.FunctionParameter("func", {"2a": 2 * a}) + + new_func = func.new_copy() + self.assertEqual(func.input_names, new_func.input_names) + + def test_print_input_names(self): + var = pybamm.Variable("var") + func = pybamm.FunctionParameter("a", {"var": var}) + func.print_input_names() + + def test_get_children_domains(self): + var = pybamm.Variable("var", domain=["negative electrode"]) + var_2 = pybamm.Variable("var", domain=["positive electrode"]) + with self.assertRaises(pybamm.DomainError): + pybamm.FunctionParameter("a", {"var": var, "var 2": var_2}) + + def test_set_input_names(self): + + var = pybamm.Variable("var") + func = pybamm.FunctionParameter("a", {"var": var}) + + new_input_names = ["first", "second"] + func.input_names = new_input_names + + self.assertEqual(func.input_names, new_input_names) + + with self.assertRaises(TypeError): + new_input_names = {"wrong": "input type"} + func.input_names = new_input_names + + with self.assertRaises(TypeError): + new_input_names = [var] + func.input_names = new_input_names + if __name__ == "__main__": print("Add -v for more debug output") diff --git a/tests/unit/test_expression_tree/test_symbol.py b/tests/unit/test_expression_tree/test_symbol.py index 7faa4992c4..588796f12a 100644 --- a/tests/unit/test_expression_tree/test_symbol.py +++ b/tests/unit/test_expression_tree/test_symbol.py @@ -381,7 +381,7 @@ def test_shape_and_size_for_testing(self): param = pybamm.Parameter("a") self.assertEqual(param.shape_for_testing, ()) - func = pybamm.FunctionParameter("func", state) + func = pybamm.FunctionParameter("func", {"state": state}) self.assertEqual(func.shape_for_testing, state.shape_for_testing) concat = pybamm.Concatenation() diff --git a/tests/unit/test_models/test_base_model.py b/tests/unit/test_models/test_base_model.py index 2f2b37d05a..fe642ec57b 100644 --- a/tests/unit/test_models/test_base_model.py +++ b/tests/unit/test_models/test_base_model.py @@ -92,6 +92,7 @@ def test_variables_set_get(self): variables = {"c": "alpha", "d": "beta"} model.variables = variables self.assertEqual(variables, model.variables) + self.assertEqual(model.variable_names(), list(variables.keys())) def test_jac_set_get(self): model = pybamm.BaseModel() @@ -253,8 +254,7 @@ def test_check_well_posedness_variables(self): model.rhs = {c: d.diff(pybamm.t), d: -1} model.initial_conditions = {c: 1, d: 1} with self.assertRaisesRegex( - pybamm.ModelError, - "time derivative of variable found", + pybamm.ModelError, "time derivative of variable found", ): model.check_well_posedness() @@ -266,8 +266,7 @@ def test_check_well_posedness_variables(self): } model.initial_conditions = {c: 1, d: 1} with self.assertRaisesRegex( - pybamm.ModelError, - "time derivative of variable found", + pybamm.ModelError, "time derivative of variable found", ): model.check_well_posedness() @@ -276,8 +275,7 @@ def test_check_well_posedness_variables(self): model.rhs = {c: d.diff(pybamm.t), d: -1} model.initial_conditions = {c: 1, d: 1} with self.assertRaisesRegex( - pybamm.ModelError, - "time derivative of variable found", + pybamm.ModelError, "time derivative of variable found", ): model.check_well_posedness() @@ -285,11 +283,10 @@ def test_check_well_posedness_variables(self): model = pybamm.BaseModel() model.algebraic = { d: 5 * pybamm.StateVector(slice(0, 15)) - 1, - c: 5 * pybamm.StateVectorDot(slice(0, 15)) - 1 + c: 5 * pybamm.StateVectorDot(slice(0, 15)) - 1, } with self.assertRaisesRegex( - pybamm.ModelError, - "time derivative of state vector found", + pybamm.ModelError, "time derivative of state vector found", ): model.check_well_posedness(post_discretisation=True) @@ -298,8 +295,7 @@ def test_check_well_posedness_variables(self): model.rhs = {c: 5 * pybamm.StateVectorDot(slice(0, 15)) - 1} model.initial_conditions = {c: 1} with self.assertRaisesRegex( - pybamm.ModelError, - "time derivative of state vector found", + pybamm.ModelError, "time derivative of state vector found", ): model.check_well_posedness(post_discretisation=True) diff --git a/tests/unit/test_models/test_full_battery_models/test_lead_acid/test_loqs.py b/tests/unit/test_models/test_full_battery_models/test_lead_acid/test_loqs.py index 0c7757d17f..cb5675944b 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lead_acid/test_loqs.py +++ b/tests/unit/test_models/test_full_battery_models/test_lead_acid/test_loqs.py @@ -170,7 +170,7 @@ def test_well_posed_function(self): def external_circuit_function(variables): I = variables["Current [A]"] V = variables["Terminal voltage [V]"] - return V + I - pybamm.FunctionParameter("Function", pybamm.t) + return V + I - pybamm.FunctionParameter("Function", {"Time [s]": pybamm.t}) options = {"operating mode": external_circuit_function} model = pybamm.lead_acid.LOQS(options) diff --git a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spm.py b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spm.py index 83bd6e5cd7..9fd9fd09a7 100644 --- a/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spm.py +++ b/tests/unit/test_models/test_full_battery_models/test_lithium_ion/test_spm.py @@ -174,7 +174,7 @@ def test_well_posed_function(self): def external_circuit_function(variables): I = variables["Current [A]"] V = variables["Terminal voltage [V]"] - return V + I - pybamm.FunctionParameter("Function", pybamm.t) + return V + I - pybamm.FunctionParameter("Function", {"Time [s]": pybamm.t}) options = {"operating mode": external_circuit_function} model = pybamm.lithium_ion.SPM(options) diff --git a/tests/unit/test_models/test_model_info.py b/tests/unit/test_models/test_model_info.py new file mode 100644 index 0000000000..58c5c67c52 --- /dev/null +++ b/tests/unit/test_models/test_model_info.py @@ -0,0 +1,27 @@ +# +# Tests getting model info +# +import pybamm +import unittest + + +class TestModelInfo(unittest.TestCase): + def test_find_parameter_info(self): + model = pybamm.lithium_ion.SPM() + model.info("Negative electrode diffusivity [m2.s-1]") + model = pybamm.lithium_ion.SPMe() + model.info("Negative electrode diffusivity [m2.s-1]") + model = pybamm.lithium_ion.DFN() + model.info("Negative electrode diffusivity [m2.s-1]") + + model.info("Not a parameter") + + +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() diff --git a/tests/unit/test_models/test_submodels/test_external_circuit/test_function_control.py b/tests/unit/test_models/test_submodels/test_external_circuit/test_function_control.py index 4a06002baa..eb25f4b12a 100644 --- a/tests/unit/test_models/test_submodels/test_external_circuit/test_function_control.py +++ b/tests/unit/test_models/test_submodels/test_external_circuit/test_function_control.py @@ -9,7 +9,13 @@ def external_circuit_function(variables): I = variables["Current [A]"] V = variables["Terminal voltage [V]"] - return V + I - pybamm.FunctionParameter("Current plus voltage function", pybamm.t) + return ( + V + + I + - pybamm.FunctionParameter( + "Current plus voltage function", {"Time [s]": pybamm.t} + ) + ) class TestFunctionControl(unittest.TestCase): diff --git a/tests/unit/test_parameters/test_parameter_values.py b/tests/unit/test_parameters/test_parameter_values.py index 68c9e496e4..8596c90152 100644 --- a/tests/unit/test_parameters/test_parameter_values.py +++ b/tests/unit/test_parameters/test_parameter_values.py @@ -308,12 +308,12 @@ def test_process_function_parameter(self): a = pybamm.InputParameter("a") # process function - func = pybamm.FunctionParameter("func", a) + func = pybamm.FunctionParameter("func", {"a": a}) processed_func = parameter_values.process_symbol(func) self.assertEqual(processed_func.evaluate(inputs={"a": 3}), 369) # process constant function - const = pybamm.FunctionParameter("const", a) + const = pybamm.FunctionParameter("const", {"a": a}) processed_const = parameter_values.process_symbol(const) self.assertIsInstance(processed_const, pybamm.Scalar) self.assertEqual(processed_const.evaluate(), 254) @@ -324,14 +324,14 @@ def test_process_function_parameter(self): self.assertEqual(processed_diff_func.evaluate(inputs={"a": 3}), 123) # function parameter that returns a python float - func = pybamm.FunctionParameter("float_func", a) + func = pybamm.FunctionParameter("float_func", {"a": a}) processed_func = parameter_values.process_symbol(func) self.assertEqual(processed_func.evaluate(), 42) # function itself as input (different to the variable being an input) parameter_values = pybamm.ParameterValues({"func": "[input]"}) a = pybamm.Scalar(3) - func = pybamm.FunctionParameter("func", a) + func = pybamm.FunctionParameter("func", {"a": a}) processed_func = parameter_values.process_symbol(func) self.assertEqual(processed_func.evaluate(inputs={"func": 13}), 13) @@ -342,7 +342,7 @@ def D(c): parameter_values = pybamm.ParameterValues({"Diffusivity": D}) a = pybamm.InputParameter("a") - func = pybamm.FunctionParameter("Diffusivity", a) + func = pybamm.FunctionParameter("Diffusivity", {"a": a}) processed_func = parameter_values.process_symbol(func) self.assertEqual(processed_func.evaluate(inputs={"a": 3}), 9) @@ -373,7 +373,7 @@ def D(a, b): a = pybamm.Parameter("a") b = pybamm.Parameter("b") - func = pybamm.FunctionParameter("Diffusivity", a, b) + func = pybamm.FunctionParameter("Diffusivity", {"a": a, "b": b}) processed_func = parameter_values.process_symbol(func) self.assertEqual(processed_func.evaluate(), 3) @@ -386,7 +386,7 @@ def test_process_interpolant(self): ) a = pybamm.Parameter("a") - func = pybamm.FunctionParameter("Diffusivity", a) + func = pybamm.FunctionParameter("Diffusivity", {"a": a}) processed_func = parameter_values.process_symbol(func) self.assertIsInstance(processed_func, pybamm.Interpolant) @@ -411,8 +411,8 @@ def test_interpolant_against_function(self): ) a = pybamm.InputParameter("a") - func = pybamm.FunctionParameter("function", a) - interp = pybamm.FunctionParameter("interpolation", a) + func = pybamm.FunctionParameter("function", {"a": a}) + interp = pybamm.FunctionParameter("interpolation", {"a": a}) processed_func = parameter_values.process_symbol(func) processed_interp = parameter_values.process_symbol(interp)