[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