[petsc-users] TS Solver stops working when including ts.setDM
Matthew Knepley
knepley at gmail.com
Mon Feb 24 07:53:41 CST 2025
On Mon, Feb 24, 2025 at 8:41 AM Eirik Jaccheri Høydalsvik via petsc-users <
petsc-users at mcs.anl.gov> wrote:
> Hi,
>
> I am using the petsc4py.ts timestepper to solve a PDE. It is not easy to
> obtain the jacobian for my equations, so I do not provide a jacobian
> function. The code is given at the end of the email.
>
> When I comment out the function call “ts.setDM(da)”, the code runs and
> gives reasonable results.
>
> However, when I add this line of code, the program crashes with the error
> message provided at the end of the email.
>
> Questions:
>
> 1. Do you know why adding this line of code can make the SNES solver
> diverge? Any suggestions for how to debug the issue?
>
I will not know until I run it, but here is my guess. When the DMDA is
specified, PETSc uses coloring to produce the Jacobian. When it is not, it
just brute-forces the entire J. My guess is that your residual does not
respect the stencil in the DMDA, so the coloring is wrong, making a wrong
Jacobian.
> 2. What is the advantage of adding the DMDA object to the ts solver? Will
> this speed up the calculation of the finite difference jacobian?
>
Yes, it speeds up the computation of the FD Jacobian.
Thanks,
Matt
> Best regards,
>
> Eirik Høydalsvik
>
> SINTEF ER/NTNU
>
> Error message:
>
> [Eiriks-MacBook-Pro.local:26384] shmem: mmap: an error occurred while
> determining whether or not
> /var/folders/w1/35jw9y4n7lsbw0dhjqdhgzz80000gn/T//ompi.Eiriks-MacBook-Pro.501/jf.0/2046361600/sm_segment.Eiriks-MacBook-Pro.501.79f90000.0 could
> be created.
>
> t 0 of 1 with dt = 0.2
>
> 0 TS dt 0.2 time 0.
>
> TSAdapt none step 0 stage rejected (SNES reason
> DIVERGED_LINEAR_SOLVE) t=0 + 2.000e-01 retrying with dt=5.000e-02
>
> TSAdapt none step 0 stage rejected (SNES reason
> DIVERGED_LINEAR_SOLVE) t=0 + 5.000e-02 retrying with dt=1.250e-02
>
> TSAdapt none step 0 stage rejected (SNES reason
> DIVERGED_LINEAR_SOLVE) t=0 + 1.250e-02 retrying with dt=3.125e-03
>
> TSAdapt none step 0 stage rejected (SNES reason
> DIVERGED_LINEAR_SOLVE) t=0 + 3.125e-03 retrying with dt=7.813e-04
>
> TSAdapt none step 0 stage rejected (SNES reason
> DIVERGED_LINEAR_SOLVE) t=0 + 7.813e-04 retrying with dt=1.953e-04
>
> TSAdapt none step 0 stage rejected (SNES reason
> DIVERGED_LINEAR_SOLVE) t=0 + 1.953e-04 retrying with dt=4.883e-05
>
> TSAdapt none step 0 stage rejected (SNES reason
> DIVERGED_LINEAR_SOLVE) t=0 + 4.883e-05 retrying with dt=1.221e-05
>
> TSAdapt none step 0 stage rejected (SNES reason
> DIVERGED_LINEAR_SOLVE) t=0 + 1.221e-05 retrying with dt=3.052e-06
>
> TSAdapt none step 0 stage rejected (SNES reason
> DIVERGED_LINEAR_SOLVE) t=0 + 3.052e-06 retrying with dt=7.629e-07
>
> TSAdapt none step 0 stage rejected (SNES reason
> DIVERGED_LINEAR_SOLVE) t=0 + 7.629e-07 retrying with dt=1.907e-07
>
> TSAdapt none step 0 stage rejected (SNES reason
> DIVERGED_LINEAR_SOLVE) t=0 + 1.907e-07 retrying with dt=4.768e-08
>
> Traceback (most recent call last):
>
> File "/Users/iept1445/Code/tank_model/closed_tank.py", line 200, in
> <module>
>
> return_dict1d = get_tank_composition_1d(tank_params)
>
> File "/Users/iept1445/Code/tank_model/src/tank_model1d.py", line 223, in
> get_tank_composition_1d
>
> ts.solve(u=x)
>
> File "petsc4py/PETSc/TS.pyx", line 2478, in petsc4py.PETSc.TS.solve
>
> petsc4py.PETSc.Error: error code 91
>
> [0] TSSolve() at
> /Users/iept1445/.cache/uv/sdists-v6/pypi/petsc/3.22.1/svV0XnlR4s3LB4lmsaKjR/src/src/ts/interface/ts.c:4072
>
> [0] TSStep() at
> /Users/iept1445/.cache/uv/sdists-v6/pypi/petsc/3.22.1/svV0XnlR4s3LB4lmsaKjR/src/src/ts/interface/ts.c:3440
>
> [0] TSStep has failed due to DIVERGED_STEP_REJECTED
>
> Options for solver:
>
> COMM = PETSc.COMM_WORLD
>
>
>
> da = PETSc.DMDA().create(
>
> dim=(N_vertical,),
>
> dof=3,
>
> stencil_type=PETSc.DMDA().StencilType.STAR,
>
> stencil_width=1,
>
> # boundary_type=PETSc.DMDA.BoundaryType.GHOSTED,
>
> )
>
> x = da.createGlobalVec()
>
> x_old = da.createGlobalVec()
>
> f = da.createGlobalVec()
>
> J = da.createMat()
>
> rho_ref = rho_m[0] # kg/m3
>
> e_ref = e_m[0] # J/mol
>
> p_ref = p0 # Pa
>
> x.setArray(np.array([rho_m / rho_ref, e_m / e_ref, ux_m]).T.flatten())
>
> x_old.setArray(np.array([rho_m / rho_ref, e_m / e_ref,
> ux_m]).T.flatten())
>
>
>
> optsDB = PETSc.Options()
>
> optsDB["snes_lag_preconditioner_persists"] = False
>
> optsDB["snes_lag_jacobian"] = 1
>
> optsDB["snes_lag_jacobian_persists"] = False
>
> optsDB["snes_lag_preconditioner"] = 1
>
> optsDB["ksp_type"] = "gmres" # "gmres" # gmres"
>
> optsDB["pc_type"] = "ilu" # "lu" # "ilu"
>
> optsDB["snes_type"] = "newtonls"
>
> optsDB["ksp_rtol"] = 1e-7
>
> optsDB["ksp_atol"] = 1e-7
>
> optsDB["ksp_max_it"] = 100
>
> optsDB["snes_rtol"] = 1e-5
>
> optsDB["snes_atol"] = 1e-5
>
> optsDB["snes_stol"] = 1e-5
>
> optsDB["snes_max_it"] = 100
>
> optsDB["snes_mf"] = False
>
> optsDB["ts_max_time"] = t_end
>
> optsDB["ts_type"] = "beuler" # "bdf" #
>
> optsDB["ts_max_snes_failures"] = -1
>
> optsDB["ts_monitor"] = ""
>
> optsDB["ts_adapt_monitor"] = ""
>
> # optsDB["snes_monitor"] = ""
>
> # optsDB["ksp_monitor"] = ""
>
> optsDB["ts_atol"] = 1e-4
>
>
>
> x0 = x_old
>
> residual_wrap = residual_ts(
>
> eos,
>
> x0,
>
> N_vertical,
>
> g,
>
> pos,
>
> z,
>
> mw,
>
> dt,
>
> dx,
>
> p_amb,
>
> A_nozzle,
>
> r_tank_inner,
>
> mph_uv_flsh_L,
>
> rho_ref,
>
> e_ref,
>
> p_ref,
>
> closed_tank,
>
> J,
>
> f,
>
> da,
>
> drift_func,
>
> T_wall,
>
> tank_params,
>
> )
>
>
>
> # optsDB["ts_adapt_type"] = "none"
>
>
>
> ts = PETSc.TS().create(comm=COMM)
>
> # TODO: Figure out why DM crashes the code
>
> # ts.setDM(residual_wrap.da)
>
> ts.setIFunction(residual_wrap.residual_ts, None)
>
> ts.setTimeStep(dt)
>
> ts.setMaxSteps(-1)
>
> ts.setTime(t_start) # s
>
> ts.setMaxTime(t_end) # s
>
> ts.setMaxSteps(1e5)
>
> ts.setStepLimits(1e-3, 1e5)
>
> ts.setFromOptions()
>
> ts.solve(u=x)
>
>
>
> Residual function:
>
> class residual_ts:
>
> def __init__(
>
> self,
>
> eos,
>
> x0,
>
> N,
>
> g,
>
> pos,
>
> z,
>
> mw,
>
> dt,
>
> dx,
>
> p_amb,
>
> A_nozzle,
>
> r_tank_inner,
>
> mph_uv_flsh_l,
>
> rho_ref,
>
> e_ref,
>
> p_ref,
>
> closed_tank,
>
> J,
>
> f,
>
> da,
>
> drift_func,
>
> T_wall,
>
> tank_params,
>
> ):
>
> self.eos = eos
>
> self.x0 = x0
>
> self.N = N
>
> self.g = g
>
> self.pos = pos
>
> self.z = z
>
> self.mw = mw
>
> self.dt = dt
>
> self.dx = dx
>
> self.p_amb = p_amb
>
> self.A_nozzle = A_nozzle
>
> self.r_tank_inner = r_tank_inner
>
> self.mph_uv_flsh_L = mph_uv_flsh_l
>
> self.rho_ref = rho_ref
>
> self.e_ref = e_ref
>
> self.p_ref = p_ref
>
> self.closed_tank = closed_tank
>
> self.J = J
>
> self.f = f
>
> self.da = da
>
> self.drift_func = drift_func
>
> self.T_wall = T_wall
>
> self.tank_params = tank_params
>
> self.Q_wall = np.zeros(N)
>
> self.n_iter = 0
>
> self.t_current = [0.0]
>
> self.s_top = [0.0]
>
> self.p_choke = [0.0]
>
>
>
> # setting interp func # TODO figure out how to generalize this
> method
>
> self._interp_func = _jalla_upwind
>
>
>
> # allocate space for new params
>
> self.p = np.zeros(N) # Pa
>
> self.T = np.zeros(N) # K
>
> self.alpha = np.zeros((2, N))
>
> self.rho = np.zeros((2, N))
>
> self.e = np.zeros((2, N))
>
>
>
> # allocate space for ghost cells
>
> self.alpha_ghost = np.zeros((2, N + 2))
>
> self.rho_ghost = np.zeros((2, N + 2))
>
> self.rho_m_ghost = np.zeros(N + 2)
>
> self.u_m_ghost = np.zeros(N + 1)
>
> self.u_ghost = np.zeros((2, N + 1))
>
> self.e_ghost = np.zeros((2, N + 2))
>
> self.pos_ghost = np.zeros(N + 2)
>
> self.h_ghost = np.zeros((2, N + 2))
>
>
>
> # allocate soace for local X and Xdot
>
> self.X_LOCAL = da.createLocalVec()
>
> self.XDOT_LOCAL = da.createLocalVec()
>
>
>
> def residual_ts(self, ts, t, X, XDOT, F):
>
> self.n_iter += 1
>
> # TODO: Estimate time use
>
> """
>
> Caculate residuals for equations
>
> (rho, rho (e + gx))_t + (rho u, rho u (h + gx))_x = 0
>
> P_x = - g \rho
>
> """
>
> n_phase = 2
>
> self.da.globalToLocal(X, self.X_LOCAL)
>
> self.da.globalToLocal(XDOT, self.XDOT_LOCAL)
>
> x = self.da.getVecArray(self.X_LOCAL)
>
> xdot = self.da.getVecArray(self.XDOT_LOCAL)
>
> f = self.da.getVecArray(F)
>
>
>
> T_c, v_c, p_c = self.eos.critical(self.z) # K, m3/mol, Pa
>
> rho_m = x[:, 0] * self.rho_ref # kg/m3
>
> e_m = x[:, 1] * self.e_ref # J/mol
>
> u_m = x[:-1, 2] # m/s
>
>
>
> # derivatives
>
> rho_m_dot = xdot[:, 0] * self.rho_ref # kg/m3
>
> e_m_dot = xdot[:, 1] * self.e_ref # kg/m3
>
> dt = ts.getTimeStep() # s
>
>
>
> for i in range(self.N):
>
> # get new parameters
>
> self.mph_uv_flsh_L[i] = self.eos.multi_phase_uvflash(
>
> self.z, e_m[i], self.mw / rho_m[i], self.mph_uv_flsh_L[i]
>
> )
>
>
>
> betaL, betaV, betaS = _get_betas(self.mph_uv_flsh_L[i]) #
> mol/mol
>
> beta = [betaL, betaV]
>
> if betaS != 0.0:
>
> print("there is a solid phase which is not accounted for")
>
> self.T[i], self.p[i] = _get_tank_temperature_pressure(
>
> self.mph_uv_flsh_L[i]
>
> ) # K, Pa)
>
> for j, phase in enumerate([self.eos.LIQPH, self.eos.VAPPH]):
>
> # new parameters
>
> self.rho_ghost[:, 1:-1][j][i] = (
>
> self.mw
>
> / self.eos.specific_volume(self.T[i], self.p[i],
> self.z, phase)[0]
>
> ) # kg/m3
>
> self.e_ghost[:, 1:-1][j][i] = self.eos.internal_energy_tv(
>
> self.T[i], self.mw / self.rho_ghost[:, 1:-1][j][i],
> self.z, phase
>
> )[
>
> 0
>
> ] # J/mol
>
> self.h_ghost[:, 1:-1][j][i] = (
>
> self.e_ghost[:, 1:-1][j][i]
>
> + self.p[i] * self.mw / self.rho_ghost[:, 1:-1][j][i]
>
> ) # J/mol
>
> self.alpha_ghost[:, 1:-1][j][i] = (
>
> beta[j] / self.rho_ghost[:, 1:-1][j][i] * rho_m[i]
>
> ) # m3/m3
>
>
>
> # calculate drift velocity
>
> for i in range(self.N - 1):
>
> self.u_ghost[:, 1:-1][0][i], self.u_ghost[:, 1:-1][1][i] = (
>
> calc_drift_velocity(
>
> u_m[i],
>
> self._interp_func(
>
> self.rho_ghost[:, 1:-1][0][i],
>
> self.rho_ghost[:, 1:-1][0][i + 1],
>
> u_m[i],
>
> ),
>
> self._interp_func(
>
> self.rho_ghost[:, 1:-1][1][i],
>
> self.rho_ghost[:, 1:-1][1][i + 1],
>
> u_m[i],
>
> ),
>
> self.g,
>
> self._interp_func(self.T[i], self.T[i + 1], u_m[i]),
>
> T_c,
>
> self.r_tank_inner,
>
> self._interp_func(
>
> self.alpha_ghost[:, 1:-1][0][i],
>
> self.alpha_ghost[:, 1:-1][0][i + 1],
>
> u_m[i],
>
> ),
>
> self._interp_func(
>
> self.alpha_ghost[:, 1:-1][1][i],
>
> self.alpha_ghost[:, 1:-1][1][i + 1],
>
> u_m[i],
>
> ),
>
> self.drift_func,
>
> )
>
> ) # liq m / s , vapour m / s
>
>
>
> u_bottom = 0
>
> if self.closed_tank:
>
> u_top = 0.0 # m/s
>
> else:
>
> # calc phase to skip env_isentrope_cross
>
> if (
>
> self.mph_uv_flsh_L[-1].liquid != None
>
> and self.mph_uv_flsh_L[-1].vapour == None
>
> and self.mph_uv_flsh_L[-1].solid == None
>
> ):
>
> phase_env = self.eos.LIQPH
>
> else:
>
> phase_env = self.eos.TWOPH
>
>
>
> self.h_m = e_m + self.p * self.mw / rho_m # J / mol
>
> self.s_top[0] = _get_s_tank(self.eos, self.mph_uv_flsh_L[-1])
> # J / mol / K
>
> mdot, self.p_choke[0] = calc_mass_outflow(
>
> self.eos,
>
> self.z,
>
> self.h_m[-1],
>
> self.s_top[0],
>
> self.p[-1],
>
> self.p_amb,
>
> self.A_nozzle,
>
> self.mw,
>
> phase_env,
>
> debug_plot=False,
>
> ) # mol / s , Pa
>
> u_top = -mdot * self.mw / rho_m[-1] / (np.pi *
> self.r_tank_inner**2) # m/s
>
>
>
> # assemble vectors with ghost cells
>
> self.alpha_ghost[:, 0] = self.alpha_ghost[:, 1:-1][:, 0] # m3/m3
>
> self.alpha_ghost[:, -1] = self.alpha_ghost[:, 1:-1][:, -1] # m3/m3
>
> self.rho_ghost[:, 0] = self.rho_ghost[:, 1:-1][:, 0] # kg/m3
>
> self.rho_ghost[:, -1] = self.rho_ghost[:, 1:-1][:, -1] # kg/m3
>
> self.rho_m_ghost[0] = rho_m[0] # kg/m3
>
> self.rho_m_ghost[1:-1] = rho_m # kg/m3
>
> self.rho_m_ghost[-1] = rho_m[-1] # kg/m3
>
> # u_ghost[:, 1:-1] = u # m/s
>
> self.u_ghost[:, 0] = u_bottom # m/s
>
> self.u_ghost[:, -1] = u_top # m/s
>
> self.u_m_ghost[0] = u_bottom # m/s
>
> self.u_m_ghost[1:-1] = u_m # m/s
>
> self.u_m_ghost[-1] = u_top # m/s
>
> self.e_ghost[:, 0] = self.e_ghost[:, 1:-1][:, 0] # J/mol
>
> self.e_ghost[:, -1] = self.e_ghost[:, 1:-1][:, -1] # J/mol
>
> self.pos_ghost[1:-1] = self.pos # m
>
> self.pos_ghost[0] = self.pos[0] # m
>
> self.pos_ghost[-1] = self.pos[-1] # m
>
> self.h_ghost[:, 0] = self.h_ghost[:, 1] # J/mol
>
> self.h_ghost[:, -1] = self.h_ghost[:, -2] # J/mol
>
>
>
> # recalculate wall temperature and heat flux
>
> # TODO ARE WE DOING THE STAGGERING CORRECTLY?
>
> lz = self.tank_params["lz_tank"] / self.N # m
>
> if ts.getTime() != self.t_current[0] and
> self.tank_params["heat_transfer"]:
>
> self.t_current[0] = ts.getTime()
>
> for i in range(self.N):
>
> self.T_wall[i], self.Q_wall[i], h_ht = (
>
> solve_radial_heat_conduction_implicit(
>
> self.tank_params,
>
> self.T[i],
>
> self.T_wall[i],
>
> (self.u_m_ghost[i] + self.u_m_ghost[i + 1]) / 2,
>
> self.rho_m_ghost[i + 1],
>
> self.mph_uv_flsh_L[i],
>
> lz,
>
> dt,
>
> )
>
> ) # K, J/s, W/m2K
>
>
>
> # Calculate residuals
>
> f[:, :] = 0.0
>
> f[:, 0] = dt * rho_m_dot # kg/m3
>
> f[:-1, 1] = self.p[1:] - self.p[:-1] + self.dx * self.g *
> rho_m[0:-1] # Pa/m
>
> f[:, 2] = (
>
> dt
>
> * (
>
> rho_m_dot * (e_m / self.mw + self.g * self.pos)
>
> + rho_m * e_m_dot / self.mw
>
> )
>
> - rho_m_dot * e_m_dot / self.mw * dt**2
>
> - self.Q_wall / (np.pi * self.r_tank_inner**2 * lz) * dt
>
> ) # J / m3
>
>
>
> # add contribution from space
>
> for i in range(n_phase):
>
> e_flux_i = np.zeros_like(self.u_ghost[i]) # J/m3 m/s
>
> rho_flux_i = np.zeros_like(self.u_ghost[i]) # kg/m2/s
>
> for j in range(1, self.N + 1):
>
> if self.u_ghost[i][j] >= 0.0:
>
> rho_flux_new = _rho_flux(
>
> self.alpha_ghost[i][j], self.rho_ghost[i][j],
> self.u_ghost[i][j]
>
> )
>
> e_flux_new = _e_flux(
>
> self.alpha_ghost[i][j],
>
> self.rho_ghost[i][j],
>
> self.h_ghost[i][j],
>
> self.mw,
>
> self.g,
>
> self.pos_ghost[j],
>
> self.u_ghost[i][j],
>
> )
>
>
>
> # backward euler
>
> rho_flux_i[j] = rho_flux_new # kg/m2/s
>
> e_flux_i[j] = e_flux_new # J/m3 m/s
>
>
>
> else:
>
> rho_flux_new = _rho_flux(
>
> self.alpha_ghost[i][j + 1],
>
> self.rho_ghost[i][j + 1],
>
> self.u_ghost[i][j],
>
> )
>
>
>
> e_flux_new = _e_flux(
>
> self.alpha_ghost[i][j + 1],
>
> self.rho_ghost[i][j + 1],
>
> self.h_ghost[i][j + 1],
>
> self.mw,
>
> self.g,
>
> self.pos_ghost[j + 1],
>
> self.u_ghost[i][j],
>
> )
>
>
>
> # backward euler
>
> rho_flux_i[j] = rho_flux_new
>
> e_flux_i[j] = e_flux_new
>
>
>
> # mass eq
>
> f[:, 0] += (dt / self.dx) * (rho_flux_i[1:] -
> rho_flux_i[:-1]) # kg/m3
>
>
>
> # energy eq
>
> f[:, 2] += (dt / self.dx) * (e_flux_i[1:] - e_flux_i[:-1]) #
> J/m3
>
>
>
> f1_ref, f2_ref, f3_ref = self.rho_ref, self.p_ref, self.e_ref
>
> f[:, 0] /= f1_ref
>
> f[:-1, 1] /= f2_ref
>
> f[:, 2] /= f3_ref
>
> # dummy eq
>
> f[-1, 1] = x[-1, 2]
>
>
>
>
>
--
What most experimenters take for granted before they begin their
experiments is infinitely more interesting than any results to which their
experiments lead.
-- Norbert Wiener
https://urldefense.us/v3/__https://www.cse.buffalo.edu/*knepley/__;fg!!G_uCfscf7eWS!ZxDCDT2_bjnnDIclXM4ZGttKBfwEZhFamWy_uuk1tJpgQYOZv6UzsOeafuUZ_Zln7sTsEo0uKwot1T29bxWQ$ <https://urldefense.us/v3/__http://www.cse.buffalo.edu/*knepley/__;fg!!G_uCfscf7eWS!ZxDCDT2_bjnnDIclXM4ZGttKBfwEZhFamWy_uuk1tJpgQYOZv6UzsOeafuUZ_Zln7sTsEo0uKwot1dAkbMHw$ >
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mcs.anl.gov/pipermail/petsc-users/attachments/20250224/6b220a32/attachment-0001.html>
More information about the petsc-users
mailing list