Skip to content

Commit e7c5545

Browse files
committed
simple nonlinear ODE test case
1 parent 3e34575 commit e7c5545

File tree

3 files changed

+302
-0
lines changed

3 files changed

+302
-0
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import numpy as np
2+
3+
from pySDC.core.Errors import ParameterError, ProblemError
4+
from pySDC.core.Problem import ptype
5+
from pySDC.implementations.datatype_classes.mesh import mesh
6+
7+
8+
# noinspection PyUnusedLocal
9+
class nonlinear_ODE_1(ptype):
10+
"""
11+
Example implementing some simple nonlinear ODE with a singularity in the derivative, taken from
12+
https://www.osti.gov/servlets/purl/6111421 (Problem E-4)
13+
"""
14+
15+
def __init__(self, problem_params, dtype_u=mesh, dtype_f=mesh):
16+
"""
17+
Initialization routine
18+
19+
Args:
20+
problem_params (dict): custom parameters for the example
21+
dtype_u: mesh data type (will be passed parent class)
22+
dtype_f: mesh data type (will be passed parent class)
23+
"""
24+
25+
# these parameters will be used later, so assert their existence
26+
essential_keys = ['u0', 'newton_maxiter', 'newton_tol']
27+
for key in essential_keys:
28+
if key not in problem_params:
29+
msg = 'need %s to instantiate problem, only got %s' % (key, str(problem_params.keys()))
30+
raise ParameterError(msg)
31+
problem_params['nvars'] = 1
32+
33+
if 'stop_at_nan' not in problem_params:
34+
problem_params['stop_at_nan'] = True
35+
36+
# invoke super init, passing dtype_u and dtype_f, plus setting number of elements to 2
37+
super(nonlinear_ODE_1, self).__init__((problem_params['nvars'], None, np.dtype('float64')),
38+
dtype_u, dtype_f, problem_params)
39+
40+
def u_exact(self, t):
41+
"""
42+
Exact solution
43+
44+
Args:
45+
t (float): current time
46+
Returns:
47+
dtype_u: mesh type containing the values
48+
"""
49+
50+
me = self.dtype_u(self.init)
51+
me[:] = t - t ** 2 / 4
52+
return me
53+
54+
def eval_f(self, u, t):
55+
"""
56+
Routine to compute the RHS
57+
58+
Args:
59+
u (dtype_u): the current values
60+
t (float): current time (not used here)
61+
Returns:
62+
dtype_f: RHS, 1 component
63+
"""
64+
65+
f = self.dtype_f(self.init)
66+
f[:] = np.sqrt(1 - u)
67+
return f
68+
69+
def solve_system(self, rhs, dt, u0, t):
70+
"""
71+
Simple Newton solver for the nonlinear equation
72+
73+
Args:
74+
rhs (dtype_f): right-hand side for the nonlinear system
75+
dt (float): abbrev. for the node-to-node stepsize (or any other factor required)
76+
u0 (dtype_u): initial guess for the iterative solver
77+
t (float): current time (e.g. for time-dependent BCs)
78+
79+
Returns:
80+
dtype_u: solution u
81+
"""
82+
# create new mesh object from u0 and set initial values for iteration
83+
u = self.dtype_u(u0)
84+
85+
# start newton iteration
86+
n = 0
87+
res = 99
88+
while n < self.params.newton_maxiter:
89+
90+
# form the function g with g(u) = 0
91+
g = u - dt * np.sqrt(1 - u) - rhs
92+
93+
# if g is close to 0, then we are done
94+
res = np.linalg.norm(g, np.inf)
95+
if res < self.params.newton_tol or np.isnan(res):
96+
break
97+
98+
# assemble dg/du
99+
dg = 1 - (-dt) / (2 * np.sqrt(1 - u))
100+
# newton update: u1 = u0 - g/dg
101+
u -= 1.0 / dg * g
102+
103+
# increase iteration count
104+
n += 1
105+
106+
if np.isnan(res) and self.params.stop_at_nan:
107+
raise ProblemError('Newton got nan after %i iterations, aborting...' % n)
108+
elif np.isnan(res):
109+
self.logger.warning('Newton got nan after %i iterations...' % n)
110+
111+
if n == self.params.newton_maxiter:
112+
raise ProblemError('Newton did not converge after %i iterations, error is %s' % (n, res))
113+
114+
return u
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
from pySDC.implementations.collocation_classes.gauss_radau_right import CollGaussRadau_Right
2+
from pySDC.core.Sweeper import sweeper
3+
import numpy as np
4+
import matplotlib.pyplot as plt
5+
6+
def iteration_vs_estimate():
7+
8+
M = 5
9+
K = 10
10+
swee = sweeper({'collocation_class': CollGaussRadau_Right, 'num_nodes': M})
11+
Q = swee.coll.Qmat[1:, 1:]
12+
# Qd = swee.get_Qdelta_implicit(swee.coll, 'IE')[1:, 1:]
13+
Qd = swee.get_Qdelta_implicit(swee.coll, 'LU')[1:, 1:]
14+
Qd = swee.get_Qdelta_explicit(swee.coll, 'EE')[1:, 1:]
15+
print(swee.coll.nodes)
16+
exit()
17+
I = np.eye(M)
18+
19+
lam = -0.7
20+
print(1/np.linalg.norm(Qd, np.inf), np.linalg.norm(np.linalg.inv(Qd), np.inf))
21+
C = I - lam * Q
22+
P = I - lam * Qd
23+
Pinv = np.linalg.inv(P)
24+
25+
R = Pinv.dot(lam * (Q - Qd))
26+
rho = max(abs(np.linalg.eigvals(R)))
27+
infnorm = np.linalg.norm(R, np.inf)
28+
twonorm = np.linalg.norm(R, 2)
29+
30+
# uex = np.exp(lam)
31+
u0 = np.ones(M, dtype=np.complex128)
32+
uex = np.linalg.inv(C).dot(u0)[-1]
33+
u = u0.copy()
34+
# u = np.random.rand(M)
35+
res = u0 - C.dot(u)
36+
err = []
37+
resnorm = []
38+
for k in range(K):
39+
u += Pinv.dot(res)
40+
res = u0 - C.dot(u)
41+
err.append(abs(u[-1] - uex))
42+
resnorm.append(np.linalg.norm(res, np.inf))
43+
print(k, resnorm[-1], err[-1])
44+
45+
plt.figure()
46+
plt.semilogy(range(K), err, 'o-', color='red', label='error')
47+
plt.semilogy(range(K), resnorm, 'd-', color='orange', label='residual')
48+
plt.semilogy(range(K), [err[0] * rho ** k for k in range(K)], '--', color='green', label='spectral')
49+
plt.semilogy(range(K), [err[0] * infnorm ** k for k in range(K)], '--', color='blue', label='infnorm')
50+
plt.semilogy(range(K), [err[0] * twonorm ** k for k in range(K)], '--', color='cyan', label='twonorm')
51+
plt.semilogy(range(K), [err[0] * ((-1/lam) ** (1/M)) ** k for k in range(K)], 'x--', color='black', label='est')
52+
plt.semilogy(range(K), [err[0] * (abs(lam) * np.linalg.norm(Q-Qd)) ** k for k in range(K)], 'x-.', color='black', label='est')
53+
# plt.semilogy(range(K), [err[0] * (1/abs(lam) + np.linalg.norm((I-np.linalg.inv(Qd).dot(Q)) ** k)) for k in range(K)], 'x-.', color='black', label='est')
54+
plt.grid()
55+
plt.legend()
56+
plt.show()
57+
58+
59+
def estimates_over_lambda():
60+
M = 5
61+
K = 10
62+
swee = sweeper({'collocation_class': CollGaussRadau_Right, 'num_nodes': M})
63+
Q = swee.coll.Qmat[1:, 1:]
64+
# Qd = swee.get_Qdelta_implicit(swee.coll, 'IE')[1:, 1:]
65+
Qd = swee.get_Qdelta_implicit(swee.coll, 'LU')[1:, 1:]
66+
67+
Qdinv = np.linalg.inv(Qd)
68+
I = np.eye(M)
69+
# lam = 1/np.linalg.eigvals(Q)[0]
70+
# print(np.linalg.inv(I - lam * Q))
71+
# print(np.linalg.norm(1/lam * Qdinv, np.inf))
72+
# exit()
73+
74+
# print(np.linalg.norm((I-Qdinv.dot(Q)) ** K))
75+
# lam_list = np.linspace(start=20j, stop=0j, num=1000, endpoint=False)
76+
lam_list = np.linspace(start=-1000, stop=0, num=100, endpoint=True)
77+
78+
rho = []
79+
est = []
80+
infnorm = []
81+
twonorm = []
82+
for lam in lam_list:
83+
# C = I - lam * Q
84+
P = I - lam * Qd
85+
Pinv = np.linalg.inv(P)
86+
87+
R = Pinv.dot(lam * (Q - Qd))
88+
# w, V = np.linalg.eig(R)
89+
# Vinv = np.linalg.inv(V)
90+
# assert np.linalg.norm(V.dot(np.diag(w)).dot(Vinv) - R) < 1E-14, np.linalg.norm(V.dot(np.diag(w)).dot(Vinv) - R)
91+
rho.append(max(abs(np.linalg.eigvals(R))))
92+
# est.append(0.62*(-1/lam) ** (1/(M-1))) # M = 3
93+
# est.append(0.57*(-1/lam) ** (1/(M-1))) # M = 5
94+
# est.append(0.71*(-1/lam) ** (1/(M-1.5))) # M = 7
95+
# est.append(0.92*(-1/lam) ** (1/(M-2.5))) # M = 9
96+
# est.append((-1/lam) ** (1/(M)))
97+
est.append(1000*(1/abs(lam)) ** (1/(1)))
98+
# est.append(abs(lam))
99+
# est.append(np.linalg.norm((I-Qdinv.dot(Q)) ** M) + 1/abs(lam))
100+
# est.append(np.linalg.norm(np.linalg.inv(I - lam * Qd), np.inf))
101+
# est.append(np.linalg.norm(np.linalg.inv(I - np.linalg.inv(I - lam * np.diag(Qd)).dot(lam*np.tril(Qd, -1))), np.inf))
102+
103+
infnorm.append(np.linalg.norm(np.linalg.matrix_power(R, M), np.inf))
104+
# infnorm.append(np.linalg.norm(Vinv.dot(R).dot(V), np.inf))
105+
twonorm.append(np.linalg.norm(np.linalg.matrix_power(R, M), 2))
106+
# twonorm.append(np.linalg.norm(Vinv.dot(R).dot(V), 2))
107+
plt.figure()
108+
plt.semilogy(lam_list, rho, '--', color='green', label='spectral')
109+
plt.semilogy(lam_list, est, '--', color='red', label='est')
110+
# plt.semilogy(lam_list, infnorm, 'x--', color='blue', label='infnorm')
111+
# plt.semilogy(lam_list, twonorm, 'd--', color='cyan', label='twonorm')
112+
# plt.semilogy(np.imag(lam_list), rho, '--', color='green', label='spectral')
113+
# plt.semilogy(np.imag(lam_list), est, '--', color='red', label='est')
114+
# plt.semilogy(np.imag(lam_list), infnorm, '--', color='blue', label='infnorm')
115+
# plt.semilogy(np.imag(lam_list), twonorm, '--', color='cyan', label='twonorm')
116+
plt.grid()
117+
plt.legend()
118+
plt.show()
119+
120+
if __name__ == '__main__':
121+
iteration_vs_estimate()
122+
# estimates_over_lambda()
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from pySDC.implementations.collocation_classes.gauss_radau_right import CollGaussRadau_Right
2+
from pySDC.implementations.controller_classes.controller_nonMPI import controller_nonMPI
3+
from pySDC.implementations.problem_classes.nonlinear_ODE_1 import nonlinear_ODE_1
4+
from pySDC.implementations.sweeper_classes.generic_implicit import generic_implicit
5+
from pySDC.playgrounds.ODEs.trajectory_HookClass import trajectories
6+
7+
8+
def main():
9+
"""
10+
Van der Pol's oscillator inc. visualization
11+
"""
12+
# initialize level parameters
13+
level_params = dict()
14+
level_params['restol'] = 1E-10
15+
level_params['dt'] = 0.01
16+
17+
# initialize sweeper parameters
18+
sweeper_params = dict()
19+
sweeper_params['collocation_class'] = CollGaussRadau_Right
20+
sweeper_params['num_nodes'] = 3
21+
sweeper_params['QI'] = 'LU'
22+
23+
# initialize problem parameters
24+
problem_params = dict()
25+
problem_params['newton_tol'] = 5E-11
26+
problem_params['newton_maxiter'] = 500
27+
problem_params['u0'] = 0.0
28+
29+
# initialize step parameters
30+
step_params = dict()
31+
step_params['maxiter'] = 20
32+
33+
# initialize controller parameters
34+
controller_params = dict()
35+
# controller_params['hook_class'] = trajectories
36+
controller_params['logger_level'] = 20
37+
38+
# Fill description dictionary for easy hierarchy creation
39+
description = dict()
40+
description['problem_class'] = nonlinear_ODE_1
41+
description['problem_params'] = problem_params
42+
description['sweeper_class'] = generic_implicit
43+
description['sweeper_params'] = sweeper_params
44+
description['level_params'] = level_params
45+
description['step_params'] = step_params
46+
47+
# instantiate the controller
48+
controller = controller_nonMPI(num_procs=1, controller_params=controller_params, description=description)
49+
50+
# set time parameters
51+
t0 = 0.0
52+
Tend = 5
53+
54+
# get initial values on finest level
55+
P = controller.MS[0].levels[0].prob
56+
uinit = P.u_exact(t0)
57+
58+
# call main function to get things done...
59+
uend, stats = controller.run(u0=uinit, t0=t0, Tend=Tend)
60+
61+
uex = P.u_exact(Tend)
62+
print(abs(uend - uex))
63+
64+
65+
if __name__ == "__main__":
66+
main()

0 commit comments

Comments
 (0)