From 0fc01325ce2cd1ac97a4b5173974b13e3a19e672 Mon Sep 17 00:00:00 2001 From: Ben Knight <55677727+bknight1@users.noreply.github.com> Date: Wed, 18 Mar 2026 14:09:57 +0800 Subject: [PATCH 1/2] Fix for NS solver Fix for the solver #https://github.com/underworldcode/underworld3/issues/82 - Change from SL to Eulerian history manager for flux & unknown in NS solver. - Does NOT fix the SL issue. Further investigation required. - Does NOT fix the callback error in the Eulerian history manager. --- src/underworld3/systems/solvers.py | 62 +++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/src/underworld3/systems/solvers.py b/src/underworld3/systems/solvers.py index 25c85b16..96c1e6e9 100644 --- a/src/underworld3/systems/solvers.py +++ b/src/underworld3/systems/solvers.py @@ -2739,18 +2739,30 @@ def __init__( # If DuDt is not provided, then we can build a SLCN version if self.Unknowns.DuDt is None: - self.Unknowns.DuDt = uw.systems.ddt.SemiLagrangian( + # self.Unknowns.DuDt = uw.systems.ddt.SemiLagrangian( + # self.mesh, + # self.u.sym, # Symbolic expression - SemiLagrangian evaluates this at each update + # self.u.sym, + # vtype=uw.VarType.VECTOR, + # degree=self.u.degree, + # continuous=self.u.continuous, + # varsymbol=self.u.symbol, + # verbose=self.verbose, + # bcs=self.essential_bcs, + # order=self._order, + # smoothing=0.0001, + # ) + self.Unknowns.DuDt = uw.systems.Eulerian_DDt( self.mesh, - self.u.sym, # Symbolic expression - SemiLagrangian evaluates this at each update self.u.sym, - vtype=uw.VarType.VECTOR, + vtype=self.u.vtype, degree=self.u.degree, continuous=self.u.continuous, + V_fn=self.u.sym, varsymbol=self.u.symbol, - verbose=self.verbose, - bcs=self.essential_bcs, order=self._order, - smoothing=0.0001, + smoothing=0.0, + verbose=self.verbose, ) # F (at least for N-S) is a nodal point variable so there is no benefit @@ -2760,19 +2772,33 @@ def __init__( # Maybe u.degree-1. The scalar equivalent seems to show # little benefit from specific choices here other than # discontinuous flux variables amplifying instabilities. + if self.Unknowns.DFDt is None: + # self.Unknowns.DFDt = uw.systems.ddt.SemiLagrangian( + # self.mesh, + # sympy.Matrix.zeros(self.mesh.dim, self.mesh.dim), + # self.u.sym, + # vtype=uw.VarType.SYM_TENSOR, + # degree=self.u.degree, + # continuous=self.u.continuous, + # varsymbol=rf"{{ F[ {self.u.symbol} ] }}", + # verbose=self.verbose, + # bcs=None, + # order=self._order, + # ) + self.Unknowns.DFDt = uw.systems.ddt.Eulerian( + self.mesh, + sympy.Matrix.zeros(self.mesh.dim, self.mesh.dim), + vtype=uw.VarType.SYM_TENSOR, + degree=self.u.degree, + continuous=self.u.continuous, + varsymbol=rf"{{ F[ {self.u.symbol} ] }}", + bcs=None, + order=self._order, + smoothing=0.0, + verbose=self.verbose, + ) + - self.Unknowns.DFDt = uw.systems.ddt.SemiLagrangian( - self.mesh, - sympy.Matrix.zeros(self.mesh.dim, self.mesh.dim), - self.u.sym, - vtype=uw.VarType.SYM_TENSOR, - degree=self.u.degree, - continuous=self.u.continuous, - varsymbol=rf"{{ F[ {self.u.symbol} ] }}", - verbose=self.verbose, - bcs=None, - order=self._order, - ) ## Add in the history terms provided ... From 1ac1279a156b14826f054501f75ae9588148669b Mon Sep 17 00:00:00 2001 From: Ben Knight <55677727+bknight1@users.noreply.github.com> Date: Wed, 18 Mar 2026 16:10:42 +0800 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/underworld3/systems/solvers.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/underworld3/systems/solvers.py b/src/underworld3/systems/solvers.py index 96c1e6e9..56058ce1 100644 --- a/src/underworld3/systems/solvers.py +++ b/src/underworld3/systems/solvers.py @@ -2737,29 +2737,19 @@ def __init__( ### sets up DuDt and DFDt ## ._setup_history_terms() - # If DuDt is not provided, then we can build a SLCN version + # If DuDt is not provided, build it here. We prefer the Eulerian_DDt + # formulation over the SemiLagrangian alternative to keep the solver + # initialisation simpler and easier to maintain. if self.Unknowns.DuDt is None: - # self.Unknowns.DuDt = uw.systems.ddt.SemiLagrangian( - # self.mesh, - # self.u.sym, # Symbolic expression - SemiLagrangian evaluates this at each update - # self.u.sym, - # vtype=uw.VarType.VECTOR, - # degree=self.u.degree, - # continuous=self.u.continuous, - # varsymbol=self.u.symbol, - # verbose=self.verbose, - # bcs=self.essential_bcs, - # order=self._order, - # smoothing=0.0001, - # ) self.Unknowns.DuDt = uw.systems.Eulerian_DDt( self.mesh, - self.u.sym, + self.u, vtype=self.u.vtype, degree=self.u.degree, continuous=self.u.continuous, V_fn=self.u.sym, varsymbol=self.u.symbol, + bcs=self.essential_bcs, order=self._order, smoothing=0.0, verbose=self.verbose,