File: orego.py

package info (click to toggle)
petsc4py 3.23.1-1exp2
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 3,448 kB
  • sloc: python: 12,503; ansic: 1,697; makefile: 343; f90: 313; sh: 14
file content (135 lines) | stat: -rw-r--r-- 3,700 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# Oregonator: stiff 3-variable oscillatory ODE system from chemical reactions,
# problem OREGO in Hairer&Wanner volume 2
# See also http://www.scholarpedia.org/article/Oregonator

import sys
import petsc4py

petsc4py.init(sys.argv)

from petsc4py import PETSc


class Orego:
    n = 3
    comm = PETSc.COMM_SELF

    def evalSolution(self, t, x):
        if t != 0.0:
            raise ValueError('Only for t=0')
        x.setArray([1, 2, 3])

    def evalFunction(self, ts, t, x, xdot, f):
        f.setArray(
            [
                xdot[0] - 77.27 * (x[1] + x[0] * (1 - 8.375e-6 * x[0] - x[1])),
                xdot[1] - 1 / 77.27 * (x[2] - (1 + x[0]) * x[1]),
                xdot[2] - 0.161 * (x[0] - x[2]),
            ]
        )

    def evalJacobian(self, ts, t, x, xdot, a, A, B):
        B[:, :] = [
            [
                a - 77.27 * ((1 - 8.375e-6 * x[0] - x[1]) - 8.375e-6 * x[0]),
                -77.27 * (1 - x[0]),
                0,
            ],
            [1 / 77.27 * x[1], a + 1 / 77.27 * (1 + x[0]), -1 / 77.27],
            [-0.161, 0, a + 0.161],
        ]
        B.assemble()
        if A != B:
            A.assemble()


OptDB = PETSc.Options()
ode = Orego()

J = PETSc.Mat().createDense([ode.n, ode.n], comm=ode.comm)
J.setUp()
x = PETSc.Vec().createSeq(ode.n, comm=ode.comm)
f = x.duplicate()

ts = PETSc.TS().create(comm=ode.comm)
ts.setType(ts.Type.ROSW)  # Rosenbrock-W. ARKIMEX is a nonlinearly implicit alternative.

ts.setIFunction(ode.evalFunction, f)
ts.setIJacobian(ode.evalJacobian, J)

history = []


def monitor(ts, i, t, x):
    xx = x[:].tolist()
    history.append((i, t, xx))


ts.setMonitor(monitor)

ts.setTime(0.0)
ts.setTimeStep(0.1)
ts.setMaxTime(360)
ts.setMaxSteps(2000)
ts.setExactFinalTime(PETSc.TS.ExactFinalTime.INTERPOLATE)
ts.setMaxSNESFailures(
    -1
)  # allow an unlimited number of failures (step will be rejected and retried)

# Set a different tolerance on each variable. Can use a scalar or a vector for either or both atol and rtol.
vatol = x.duplicate(array=[1e-2, 1e-1, 1e-4])
ts.setTolerances(
    atol=vatol, rtol=1e-3
)  # adaptive controller attempts to match this tolerance

snes = ts.getSNES()  # Nonlinear solver
snes.setTolerances(
    max_it=10
)  # Stop nonlinear solve after 10 iterations (TS will retry with shorter step)
ksp = snes.getKSP()  # Linear solver
ksp.setType(ksp.Type.PREONLY)  # Just use the preconditioner without a Krylov method
pc = ksp.getPC()  # Preconditioner
pc.setType(pc.Type.LU)  # Use a direct solve

ts.setFromOptions()  # Apply run-time options, e.g. -ts_adapt_monitor -ts_type arkimex -snes_converged_reason
ode.evalSolution(0.0, x)
ts.solve(x)
print(
    'steps %d (%d rejected, %d SNES fails), nonlinear its %d, linear its %d'
    % (
        ts.getStepNumber(),
        ts.getStepRejections(),
        ts.getSNESFailures(),
        ts.getSNESIterations(),
        ts.getKSPIterations(),
    )
)

if OptDB.getBool('plot_history', True):
    from matplotlib import pylab
    from matplotlib import rc
    import numpy as np

    ii = np.asarray([v[0] for v in history])
    tt = np.asarray([v[1] for v in history])
    xx = np.asarray([v[2] for v in history])

    rc('text', usetex=True)
    pylab.suptitle('Oregonator: TS \\texttt{%s}' % ts.getType())
    pylab.subplot(2, 2, 1)
    pylab.subplots_adjust(wspace=0.3)
    pylab.semilogy(
        ii[:-1],
        np.diff(tt),
    )
    pylab.xlabel('step number')
    pylab.ylabel('timestep')

    for i in range(3):
        pylab.subplot(2, 2, i + 2)
        pylab.semilogy(tt, xx[:, i], 'rgb'[i])
        pylab.xlabel('time')
        pylab.ylabel('$x_%d$' % i)

    # pylab.savefig('orego-history.png')
    pylab.show()