@@ -149,16 +149,31 @@ def __new__(cls, expr, *dims, **kwargs):
149149 else :
150150 dcounter [d ] += o
151151
152- # Default finite difference orders depending on input dimension (.dt or .dx)
153152 # It's possible that the expr is a `sympy.Number` at this point, which
154153 # has derivative 0, unless we're taking a 0th derivative.
155154 if isinstance (expr , sympy .Number ):
156155 if any (dcounter .values ()):
157156 return 0
158157 else :
159158 return expr
159+
160+ # Use `fd_order` if specified
161+ fd_order = kwargs .get ('fd_order' )
162+ if fd_order is not None :
163+ _fd_order_specified = True
164+ # If `fd_order` is specified collect these together
165+ fcounter = defaultdict (int )
166+ for d , o in zip (dims , as_tuple (fd_order )):
167+ fcounter [d ] += o
168+ for d , o in dcounter .items ():
169+ order = expr .time_order if getattr (d , 'is_Time' , False ) else expr .space_order
170+ if o > order :
171+ raise ValueError (f'Function does not support { d } derivative of order { o } ' )
172+ fd_order = fcounter .values ()
160173 else :
161- default_fdo = tuple ([
174+ _fd_order_specified = False
175+ # Default finite difference orders depending on input dimension (.dt or .dx)
176+ fd_order = tuple ([
162177 expr .time_order
163178 if getattr (d , 'is_Time' , False )
164179 else expr .space_order
@@ -174,8 +189,9 @@ def __new__(cls, expr, *dims, **kwargs):
174189 obj = Differentiable .__new__ (cls , expr , * derivatives )
175190 obj ._dims = tuple (dcounter .keys ())
176191
192+ obj ._fd_order_specified = _fd_order_specified
177193 obj ._fd_order = DimensionTuple (
178- * as_tuple (kwargs . get ( ' fd_order' , default_fdo ) ),
194+ * as_tuple (fd_order ),
179195 getters = obj ._dims
180196 )
181197 obj ._deriv_order = DimensionTuple (
@@ -533,7 +549,11 @@ def _eval_expand_nest(self, **hints):
533549 # all kwargs from the self object
534550 # TODO: This dictionary merge needs to be a lot better
535551 # EG: Don't actually expand if derivatives are incompatible
536- new_kwargs = {'deriv_order' : tuple (chain (self .deriv_order , expr .deriv_order ))}
552+ new_kwargs = {
553+ 'deriv_order' : tuple (chain (self .deriv_order , expr .deriv_order ))
554+ }
555+ if self ._fd_order_specified or expr ._fd_order_specified :
556+ new_kwargs .update ({'fd_order' : tuple (chain (self .fd_order , expr .fd_order ))})
537557 return self .func (new_expr , * new_dims , ** new_kwargs )
538558 else :
539559 return self
@@ -564,3 +584,16 @@ def _eval_expand_add(self, **hints):
564584 return self .func (dep , * self .args [1 :])
565585 else :
566586 return self
587+
588+ def _eval_expand_product_rule (self , ** hints ):
589+ ''' Expands products, of functions of the dependent variable
590+ `Derivative(f(x)·g(x), x)
591+ --> Derivative(f(x), x)·g(x) + f(x)·Derivative(g(x), x)`
592+ This is only implemented for first derivatives with an arbitrary number
593+ of multiplicands and second derivatives with two multiplicands. The
594+ resultant expression for higher derivatives and mixed derivatives is much
595+ more difficult to implement.
596+ '''
597+ # expr = self.args[0]
598+ # if isinstance(expr, sympy.Mul):
599+ raise NotImplementedError ('Product rule expansion has not been written' )
0 commit comments