xref: /petsc/src/binding/petsc4py/demo/legacy/ode/orego.py (revision d53c7dcfa6b151504f6ca0aeb7a446ad68fe4d68)
155a74a43SLisandro Dalcin# Oregonator: stiff 3-variable oscillatory ODE system from chemical reactions,
255a74a43SLisandro Dalcin# problem OREGO in Hairer&Wanner volume 2
355a74a43SLisandro Dalcin# See also http://www.scholarpedia.org/article/Oregonator
455a74a43SLisandro Dalcin
5*69777137SStefano Zampiniimport sys
6*69777137SStefano Zampiniimport petsc4py
7*69777137SStefano Zampini
855a74a43SLisandro Dalcinpetsc4py.init(sys.argv)
955a74a43SLisandro Dalcin
1055a74a43SLisandro Dalcinfrom petsc4py import PETSc
1155a74a43SLisandro Dalcin
12*69777137SStefano Zampini
13*69777137SStefano Zampiniclass Orego:
1455a74a43SLisandro Dalcin    n = 3
1555a74a43SLisandro Dalcin    comm = PETSc.COMM_SELF
16*69777137SStefano Zampini
1755a74a43SLisandro Dalcin    def evalSolution(self, t, x):
18*69777137SStefano Zampini        if t != 0.0:
19*69777137SStefano Zampini            raise ValueError('Only for t=0')
2055a74a43SLisandro Dalcin        x.setArray([1, 2, 3])
21*69777137SStefano Zampini
2255a74a43SLisandro Dalcin    def evalFunction(self, ts, t, x, xdot, f):
23*69777137SStefano Zampini        f.setArray(
24*69777137SStefano Zampini            [
25*69777137SStefano Zampini                xdot[0] - 77.27 * (x[1] + x[0] * (1 - 8.375e-6 * x[0] - x[1])),
2655a74a43SLisandro Dalcin                xdot[1] - 1 / 77.27 * (x[2] - (1 + x[0]) * x[1]),
27*69777137SStefano Zampini                xdot[2] - 0.161 * (x[0] - x[2]),
28*69777137SStefano Zampini            ]
29*69777137SStefano Zampini        )
30*69777137SStefano Zampini
3155a74a43SLisandro Dalcin    def evalJacobian(self, ts, t, x, xdot, a, A, B):
32*69777137SStefano Zampini        B[:, :] = [
33*69777137SStefano Zampini            [
34*69777137SStefano Zampini                a - 77.27 * ((1 - 8.375e-6 * x[0] - x[1]) - 8.375e-6 * x[0]),
35*69777137SStefano Zampini                -77.27 * (1 - x[0]),
36*69777137SStefano Zampini                0,
37*69777137SStefano Zampini            ],
3855a74a43SLisandro Dalcin            [1 / 77.27 * x[1], a + 1 / 77.27 * (1 + x[0]), -1 / 77.27],
39*69777137SStefano Zampini            [-0.161, 0, a + 0.161],
40*69777137SStefano Zampini        ]
4155a74a43SLisandro Dalcin        B.assemble()
42*69777137SStefano Zampini        if A != B:
43*69777137SStefano Zampini            A.assemble()
44*69777137SStefano Zampini
4555a74a43SLisandro Dalcin
4655a74a43SLisandro DalcinOptDB = PETSc.Options()
4755a74a43SLisandro Dalcinode = Orego()
4855a74a43SLisandro Dalcin
4955a74a43SLisandro DalcinJ = PETSc.Mat().createDense([ode.n, ode.n], comm=ode.comm)
5055a74a43SLisandro DalcinJ.setUp()
5155a74a43SLisandro Dalcinx = PETSc.Vec().createSeq(ode.n, comm=ode.comm)
5255a74a43SLisandro Dalcinf = x.duplicate()
5355a74a43SLisandro Dalcin
5455a74a43SLisandro Dalcints = PETSc.TS().create(comm=ode.comm)
5555a74a43SLisandro Dalcints.setType(ts.Type.ROSW)  # Rosenbrock-W. ARKIMEX is a nonlinearly implicit alternative.
5655a74a43SLisandro Dalcin
5755a74a43SLisandro Dalcints.setIFunction(ode.evalFunction, f)
5855a74a43SLisandro Dalcints.setIJacobian(ode.evalJacobian, J)
5955a74a43SLisandro Dalcin
6055a74a43SLisandro Dalcinhistory = []
61*69777137SStefano Zampini
62*69777137SStefano Zampini
6355a74a43SLisandro Dalcindef monitor(ts, i, t, x):
6455a74a43SLisandro Dalcin    xx = x[:].tolist()
6555a74a43SLisandro Dalcin    history.append((i, t, xx))
66*69777137SStefano Zampini
67*69777137SStefano Zampini
6855a74a43SLisandro Dalcints.setMonitor(monitor)
6955a74a43SLisandro Dalcin
7055a74a43SLisandro Dalcints.setTime(0.0)
7155a74a43SLisandro Dalcints.setTimeStep(0.1)
7255a74a43SLisandro Dalcints.setMaxTime(360)
7355a74a43SLisandro Dalcints.setMaxSteps(2000)
7455a74a43SLisandro Dalcints.setExactFinalTime(PETSc.TS.ExactFinalTime.INTERPOLATE)
75*69777137SStefano Zampinits.setMaxSNESFailures(
76*69777137SStefano Zampini    -1
77*69777137SStefano Zampini)  # allow an unlimited number of failures (step will be rejected and retried)
7855a74a43SLisandro Dalcin
7955a74a43SLisandro Dalcin# Set a different tolerance on each variable. Can use a scalar or a vector for either or both atol and rtol.
8055a74a43SLisandro Dalcinvatol = x.duplicate(array=[1e-2, 1e-1, 1e-4])
81*69777137SStefano Zampinits.setTolerances(
82*69777137SStefano Zampini    atol=vatol, rtol=1e-3
83*69777137SStefano Zampini)  # adaptive controller attempts to match this tolerance
8455a74a43SLisandro Dalcin
8555a74a43SLisandro Dalcinsnes = ts.getSNES()  # Nonlinear solver
86*69777137SStefano Zampinisnes.setTolerances(
87*69777137SStefano Zampini    max_it=10
88*69777137SStefano Zampini)  # Stop nonlinear solve after 10 iterations (TS will retry with shorter step)
8955a74a43SLisandro Dalcinksp = snes.getKSP()  # Linear solver
9055a74a43SLisandro Dalcinksp.setType(ksp.Type.PREONLY)  # Just use the preconditioner without a Krylov method
9155a74a43SLisandro Dalcinpc = ksp.getPC()  # Preconditioner
9255a74a43SLisandro Dalcinpc.setType(pc.Type.LU)  # Use a direct solve
9355a74a43SLisandro Dalcin
9455a74a43SLisandro Dalcints.setFromOptions()  # Apply run-time options, e.g. -ts_adapt_monitor -ts_type arkimex -snes_converged_reason
9555a74a43SLisandro Dalcinode.evalSolution(0.0, x)
9655a74a43SLisandro Dalcints.solve(x)
97*69777137SStefano Zampiniprint(
98*69777137SStefano Zampini    'steps %d (%d rejected, %d SNES fails), nonlinear its %d, linear its %d'
99*69777137SStefano Zampini    % (
100*69777137SStefano Zampini        ts.getStepNumber(),
101*69777137SStefano Zampini        ts.getStepRejections(),
102*69777137SStefano Zampini        ts.getSNESFailures(),
103*69777137SStefano Zampini        ts.getSNESIterations(),
104*69777137SStefano Zampini        ts.getKSPIterations(),
105*69777137SStefano Zampini    )
106*69777137SStefano Zampini)
10755a74a43SLisandro Dalcin
10855a74a43SLisandro Dalcinif OptDB.getBool('plot_history', True):
10955a74a43SLisandro Dalcin    from matplotlib import pylab
11055a74a43SLisandro Dalcin    from matplotlib import rc
11155a74a43SLisandro Dalcin    import numpy as np
112*69777137SStefano Zampini
11355a74a43SLisandro Dalcin    ii = np.asarray([v[0] for v in history])
11455a74a43SLisandro Dalcin    tt = np.asarray([v[1] for v in history])
11555a74a43SLisandro Dalcin    xx = np.asarray([v[2] for v in history])
11655a74a43SLisandro Dalcin
11755a74a43SLisandro Dalcin    rc('text', usetex=True)
11855a74a43SLisandro Dalcin    pylab.suptitle('Oregonator: TS \\texttt{%s}' % ts.getType())
11955a74a43SLisandro Dalcin    pylab.subplot(2, 2, 1)
12055a74a43SLisandro Dalcin    pylab.subplots_adjust(wspace=0.3)
121*69777137SStefano Zampini    pylab.semilogy(
122*69777137SStefano Zampini        ii[:-1],
123*69777137SStefano Zampini        np.diff(tt),
124*69777137SStefano Zampini    )
12555a74a43SLisandro Dalcin    pylab.xlabel('step number')
12655a74a43SLisandro Dalcin    pylab.ylabel('timestep')
12755a74a43SLisandro Dalcin
128*69777137SStefano Zampini    for i in range(3):
12955a74a43SLisandro Dalcin        pylab.subplot(2, 2, i + 2)
130*69777137SStefano Zampini        pylab.semilogy(tt, xx[:, i], 'rgb'[i])
13155a74a43SLisandro Dalcin        pylab.xlabel('time')
13255a74a43SLisandro Dalcin        pylab.ylabel('$x_%d$' % i)
13355a74a43SLisandro Dalcin
13455a74a43SLisandro Dalcin    # pylab.savefig('orego-history.png')
13555a74a43SLisandro Dalcin    pylab.show()
136