1*55a74a43SLisandro Dalcin# Oregonator: stiff 3-variable oscillatory ODE system from chemical reactions, 2*55a74a43SLisandro Dalcin# problem OREGO in Hairer&Wanner volume 2 3*55a74a43SLisandro Dalcin# See also http://www.scholarpedia.org/article/Oregonator 4*55a74a43SLisandro Dalcin 5*55a74a43SLisandro Dalcinimport sys, petsc4py 6*55a74a43SLisandro Dalcinpetsc4py.init(sys.argv) 7*55a74a43SLisandro Dalcin 8*55a74a43SLisandro Dalcinfrom petsc4py import PETSc 9*55a74a43SLisandro Dalcin 10*55a74a43SLisandro Dalcinclass Orego(object): 11*55a74a43SLisandro Dalcin n = 3 12*55a74a43SLisandro Dalcin comm = PETSc.COMM_SELF 13*55a74a43SLisandro Dalcin def evalSolution(self, t, x): 14*55a74a43SLisandro Dalcin assert t == 0.0, "only for t=0.0" 15*55a74a43SLisandro Dalcin x.setArray([1, 2, 3]) 16*55a74a43SLisandro Dalcin def evalFunction(self, ts, t, x, xdot, f): 17*55a74a43SLisandro Dalcin f.setArray([xdot[0] - 77.27*(x[1] + x[0]*(1 - 8.375e-6*x[0] - x[1])), 18*55a74a43SLisandro Dalcin xdot[1] - 1/77.27*(x[2] - (1 + x[0])*x[1]), 19*55a74a43SLisandro Dalcin xdot[2] - 0.161*(x[0] - x[2])]) 20*55a74a43SLisandro Dalcin def evalJacobian(self, ts, t, x, xdot, a, A, B): 21*55a74a43SLisandro Dalcin B[:,:] = [[a - 77.27*((1 - 8.375e-6*x[0] - x[1]) - 8.375e-6*x[0]), -77.27*(1 - x[0]), 0], 22*55a74a43SLisandro Dalcin [1/77.27*x[1], a + 1/77.27*(1 + x[0]), -1/77.27], 23*55a74a43SLisandro Dalcin [-0.161, 0, a + 0.161]] 24*55a74a43SLisandro Dalcin B.assemble() 25*55a74a43SLisandro Dalcin if A != B: A.assemble() 26*55a74a43SLisandro Dalcin return True # same nonzero pattern 27*55a74a43SLisandro Dalcin 28*55a74a43SLisandro DalcinOptDB = PETSc.Options() 29*55a74a43SLisandro Dalcinode = Orego() 30*55a74a43SLisandro Dalcin 31*55a74a43SLisandro DalcinJ = PETSc.Mat().createDense([ode.n, ode.n], comm=ode.comm) 32*55a74a43SLisandro DalcinJ.setUp() 33*55a74a43SLisandro Dalcinx = PETSc.Vec().createSeq(ode.n, comm=ode.comm) 34*55a74a43SLisandro Dalcinf = x.duplicate() 35*55a74a43SLisandro Dalcin 36*55a74a43SLisandro Dalcints = PETSc.TS().create(comm=ode.comm) 37*55a74a43SLisandro Dalcints.setType(ts.Type.ROSW) # Rosenbrock-W. ARKIMEX is a nonlinearly implicit alternative. 38*55a74a43SLisandro Dalcin 39*55a74a43SLisandro Dalcints.setIFunction(ode.evalFunction, f) 40*55a74a43SLisandro Dalcints.setIJacobian(ode.evalJacobian, J) 41*55a74a43SLisandro Dalcin 42*55a74a43SLisandro Dalcinhistory = [] 43*55a74a43SLisandro Dalcindef monitor(ts, i, t, x): 44*55a74a43SLisandro Dalcin xx = x[:].tolist() 45*55a74a43SLisandro Dalcin history.append((i, t, xx)) 46*55a74a43SLisandro Dalcints.setMonitor(monitor) 47*55a74a43SLisandro Dalcin 48*55a74a43SLisandro Dalcints.setTime(0.0) 49*55a74a43SLisandro Dalcints.setTimeStep(0.1) 50*55a74a43SLisandro Dalcints.setMaxTime(360) 51*55a74a43SLisandro Dalcints.setMaxSteps(2000) 52*55a74a43SLisandro Dalcints.setExactFinalTime(PETSc.TS.ExactFinalTime.INTERPOLATE) 53*55a74a43SLisandro Dalcints.setMaxSNESFailures(-1) # allow an unlimited number of failures (step will be rejected and retried) 54*55a74a43SLisandro Dalcin 55*55a74a43SLisandro Dalcin# Set a different tolerance on each variable. Can use a scalar or a vector for either or both atol and rtol. 56*55a74a43SLisandro Dalcinvatol = x.duplicate(array=[1e-2, 1e-1, 1e-4]) 57*55a74a43SLisandro Dalcints.setTolerances(atol=vatol,rtol=1e-3) # adaptive controller attempts to match this tolerance 58*55a74a43SLisandro Dalcin 59*55a74a43SLisandro Dalcinsnes = ts.getSNES() # Nonlinear solver 60*55a74a43SLisandro Dalcinsnes.setTolerances(max_it=10) # Stop nonlinear solve after 10 iterations (TS will retry with shorter step) 61*55a74a43SLisandro Dalcinksp = snes.getKSP() # Linear solver 62*55a74a43SLisandro Dalcinksp.setType(ksp.Type.PREONLY) # Just use the preconditioner without a Krylov method 63*55a74a43SLisandro Dalcinpc = ksp.getPC() # Preconditioner 64*55a74a43SLisandro Dalcinpc.setType(pc.Type.LU) # Use a direct solve 65*55a74a43SLisandro Dalcin 66*55a74a43SLisandro Dalcints.setFromOptions() # Apply run-time options, e.g. -ts_adapt_monitor -ts_type arkimex -snes_converged_reason 67*55a74a43SLisandro Dalcinode.evalSolution(0.0, x) 68*55a74a43SLisandro Dalcints.solve(x) 69*55a74a43SLisandro Dalcinprint('steps %d (%d rejected, %d SNES fails), nonlinear its %d, linear its %d' 70*55a74a43SLisandro Dalcin % (ts.getStepNumber(), ts.getStepRejections(), ts.getSNESFailures(), 71*55a74a43SLisandro Dalcin ts.getSNESIterations(), ts.getKSPIterations())) 72*55a74a43SLisandro Dalcin 73*55a74a43SLisandro Dalcinif OptDB.getBool('plot_history', True): 74*55a74a43SLisandro Dalcin try: 75*55a74a43SLisandro Dalcin from matplotlib import pylab 76*55a74a43SLisandro Dalcin from matplotlib import rc 77*55a74a43SLisandro Dalcin except ImportError: 78*55a74a43SLisandro Dalcin print("matplotlib not available") 79*55a74a43SLisandro Dalcin raise SystemExit 80*55a74a43SLisandro Dalcin 81*55a74a43SLisandro Dalcin import numpy as np 82*55a74a43SLisandro Dalcin ii = np.asarray([v[0] for v in history]) 83*55a74a43SLisandro Dalcin tt = np.asarray([v[1] for v in history]) 84*55a74a43SLisandro Dalcin xx = np.asarray([v[2] for v in history]) 85*55a74a43SLisandro Dalcin 86*55a74a43SLisandro Dalcin rc('text', usetex=True) 87*55a74a43SLisandro Dalcin pylab.suptitle('Oregonator: TS \\texttt{%s}' % ts.getType()) 88*55a74a43SLisandro Dalcin pylab.subplot(2,2,1) 89*55a74a43SLisandro Dalcin pylab.subplots_adjust(wspace=0.3) 90*55a74a43SLisandro Dalcin pylab.semilogy(ii[:-1], np.diff(tt), ) 91*55a74a43SLisandro Dalcin pylab.xlabel('step number') 92*55a74a43SLisandro Dalcin pylab.ylabel('timestep') 93*55a74a43SLisandro Dalcin 94*55a74a43SLisandro Dalcin for i in range(0,3): 95*55a74a43SLisandro Dalcin pylab.subplot(2,2,i+2) 96*55a74a43SLisandro Dalcin pylab.semilogy(tt, xx[:,i], "rgb"[i]) 97*55a74a43SLisandro Dalcin pylab.xlabel('time') 98*55a74a43SLisandro Dalcin pylab.ylabel('$x_%d$' % i) 99*55a74a43SLisandro Dalcin 100*55a74a43SLisandro Dalcin # pylab.savefig('orego-history.png') 101*55a74a43SLisandro Dalcin pylab.show() 102