defijacobian(ts, t, Uin, Udot, shift, J, P): # compute: J = dF/dU + shift * dF/dUdot # F = Udot - kappa * u_xx # # F[i] = Udot[i] - kappa*(U[i-1]-2U[i]+U[i+1]) / (hx * hx) # => dF[i]/dU[i] = 2*kappa / (hx * hx) # => dF[i]/dU[i+1] = -kappa / (hx * hx) # => dF[i]/dU[i-1] = -kappa / (hx * hx) # # dF[i]/dUdot[i] = 1 # => J[i, i] = dF[i]/dU[i] + shift * dF[i]/dUdot[i] # => J[i, i+1] = dF[i]/dU[i+1] # => J[i, i-1] = dF[i]/dU[i-1] # # Boundary conditions: F = U - g # => dF/dU = 1 J.zeroEntries() (xs, xe), = dm.getRanges() for i inrange(xs, xe): if i == 0or i == nx - 1: # d(U - g)/dU = 1 J.setValue(i, i, 1.0) else: off = -kappa / (hx * hx) diag = shift + 2.0 * kappa / (hx * hx) if i - 1 != 0: J.setValue(i, i - 1, off) # BC J.setValue(i, i, diag) if i + 1 != nx - 1: J.setValue(i, i + 1, off) # BC J.assemble() # P=J, so `P.assemble()` is not needed
u_history = [] # store solution history for output last_save = -np.inf defts_monitor(ts, step, time, u): global u_history, last_save, rank flag = time - last_save - save_interval if flag >= -1e-8or step == -1: scatter, u_seq = PETSc.Scatter.toZero(u) scatter.scatter(u, u_seq, addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) if rank == 0: u_arr = u_seq.getArray(readonly=True).copy() u_history.append((time, u_arr)) last_save = time
if step == -1and rank == 0and u_history: plt.figure(figsize=(10, 6)) x_grid = np.linspace(0, 1, nx) for (t_val, u_val) in u_history: plt.plot(x_grid, u_val, label=f"t={t_val:.2f}") plt.xlabel("x") plt.ylabel("u") plt.legend() plt.grid(True) plt.savefig("heat.png") plt.close() PETSc.Sys.Print("Result saved to 'heat.png'")