[petsc-users] TS Solver stops working when including ts.setDM
Eirik Jaccheri Høydalsvik
eirik.hoydalsvik at sintef.no
Mon Feb 24 07:41:09 CST 2025
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?
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?
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]
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mcs.anl.gov/pipermail/petsc-users/attachments/20250224/f07ee069/attachment-0001.html>
More information about the petsc-users
mailing list