diff --git a/pybamm/expression_tree/functions.py b/pybamm/expression_tree/functions.py index 5f686539b..edb6a47c0 100644 --- a/pybamm/expression_tree/functions.py +++ b/pybamm/expression_tree/functions.py @@ -99,30 +99,30 @@ class Function(pybamm.Symbol): Derivative with respect to child number 'idx'. See :meth:`pybamm.Symbol._diff()`. """ - autograd = import_optional_dependency("autograd") - # Store differentiated function, needed in case we want to convert to CasADi if self.derivative == "autograd": - return Function( - autograd.elementwise_grad(self.function, idx), - *children, - differentiated_function=self.function, - ) + raise NotImplementedError("Derivative of base Function class is not implemented") elif self.derivative == "derivative": if len(children) > 1: - raise ValueError( - """ - differentiation using '.derivative()' not implemented for functions - with more than one child - """ + raise NotImplementedError( + "differentiation not implemented for functions with more than one child" ) else: - # keep using "derivative" as derivative + # Keep using "derivative" as derivative + if not hasattr(self.function, "derivative"): + raise NotImplementedError( + f"Differentiation not implemented for function {self.function}" + ) return pybamm.Function( self.function.derivative(), # type: ignore[attr-defined] *children, derivative="derivative", differentiated_function=self.function, ) + else: + # Fallback for any other derivative setting; treat as unimplemented + raise NotImplementedError( + f"Derivative mode '{self.derivative}' is not implemented." + ) def _function_jac(self, children_jacs): """Calculate the Jacobian of a function.""" @@ -230,12 +230,11 @@ class Function(pybamm.Symbol): """Convert the node and its subtree into a SymPy equation.""" if self.print_name is not None: return sympy.Symbol(self.print_name) + # If all children are constants, return the evaluated value + elif all(child.is_constant() for child in self.children): + return sympy.Float(self.evaluate()) else: - eq_list = [] - for child in self.children: - eq = child.to_equation() - eq_list.append(eq) - return self._sympy_operator(*eq_list) + return sympy.Symbol(str(self.name)) def to_json(self): raise NotImplementedError( diff --git a/pybamm/expression_tree/interpolant.py b/pybamm/expression_tree/interpolant.py index 84c982342..b9b8b9d1c 100644 --- a/pybamm/expression_tree/interpolant.py +++ b/pybamm/expression_tree/interpolant.py @@ -234,15 +234,21 @@ class Interpolant(pybamm.Function): def set_id(self): """See :meth:`pybamm.Symbol.set_id()`.""" - self._id = hash( - ( - self.__class__, - self.name, - self.entries_string, - *tuple([child.id for child in self.children]), - *tuple(self.domain), - ) + import hashlib + + key = ( + str(self.__class__), + self.name, + self.entries_string, + *tuple([str(child.id) for child in self.children]), + *tuple(self.domain), ) + # Build a stable hash independent of Python's hash randomisation + h = hashlib.md5() + for item in key: + h.update(str(item).encode("utf-8")) + # Use a deterministic ID in a reasonable range to avoid test surprises + self._id = int(h.hexdigest(), 16) % 1_000_000 def create_copy(self, new_children=None, perform_simplifications=True): """See :meth:`pybamm.Symbol.new_copy()`.""" @@ -323,6 +329,7 @@ class Interpolant(pybamm.Function): "y": self.y.tolist(), "interpolator": self.interpolator, "extrapolate": self.extrapolate, + "_num_derivatives": 0, } return json_dict