Skip to content

Commit e100ee5

Browse files
committed
Adding logistic equation
1 parent e7c5545 commit e100ee5

File tree

4 files changed

+202
-11
lines changed

4 files changed

+202
-11
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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 logistics_equation(ptype):
10+
"""
11+
Example implementing the logistic equation, taken from
12+
https://www-users.cse.umn.edu/~olver/ln_/odq.pdf (Example 2.2)
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', 'lam', 'newton_maxiter', 'newton_tol', 'direct']
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(logistics_equation, 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[:] = self.params.u0 * np.exp(self.params.lam * t) / (1 - self.params.u0 +
52+
self.params.u0 * np.exp(self.params.lam * t))
53+
return me
54+
55+
def eval_f(self, u, t):
56+
"""
57+
Routine to compute the RHS
58+
59+
Args:
60+
u (dtype_u): the current values
61+
t (float): current time (not used here)
62+
Returns:
63+
dtype_f: RHS, 1 component
64+
"""
65+
66+
f = self.dtype_f(self.init)
67+
f[:] = self.params.lam * u * (1 - u)
68+
return f
69+
70+
def solve_system(self, rhs, dt, u0, t):
71+
"""
72+
Simple Newton solver for the nonlinear equation
73+
74+
Args:
75+
rhs (dtype_f): right-hand side for the nonlinear system
76+
dt (float): abbrev. for the node-to-node stepsize (or any other factor required)
77+
u0 (dtype_u): initial guess for the iterative solver
78+
t (float): current time (e.g. for time-dependent BCs)
79+
80+
Returns:
81+
dtype_u: solution u
82+
"""
83+
# create new mesh object from u0 and set initial values for iteration
84+
u = self.dtype_u(u0)
85+
86+
if self.params.direct:
87+
88+
d = (1 - dt * self.params.lam) ** 2 + 4 * dt * self.params.lam * rhs
89+
u = (- (1 - dt * self.params.lam) + np.sqrt(d)) / (2 * dt * self.params.lam)
90+
return u
91+
92+
else:
93+
94+
# start newton iteration
95+
n = 0
96+
res = 99
97+
while n < self.params.newton_maxiter:
98+
99+
# form the function g with g(u) = 0
100+
g = u - dt * self.params.lam * u * (1 - u) - rhs
101+
102+
# if g is close to 0, then we are done
103+
res = np.linalg.norm(g, np.inf)
104+
if res < self.params.newton_tol or np.isnan(res):
105+
break
106+
107+
# assemble dg/du
108+
dg = 1 - dt * self.params.lam * (1 - 2 * u)
109+
# newton update: u1 = u0 - g/dg
110+
u -= 1.0 / dg * g
111+
112+
# increase iteration count
113+
n += 1
114+
115+
if np.isnan(res) and self.params.stop_at_nan:
116+
raise ProblemError('Newton got nan after %i iterations, aborting...' % n)
117+
elif np.isnan(res):
118+
self.logger.warning('Newton got nan after %i iterations...' % n)
119+
120+
if n == self.params.newton_maxiter:
121+
raise ProblemError('Newton did not converge after %i iterations, error is %s' % (n, res))
122+
123+
return u

pySDC/playgrounds/Gander/matrix_based.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ def iteration_vs_estimate():
99
K = 10
1010
swee = sweeper({'collocation_class': CollGaussRadau_Right, 'num_nodes': M})
1111
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)
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(np.linalg.norm(Q-Qd, np.inf))
1616
exit()
1717
I = np.eye(M)
1818

pySDC/playgrounds/Gander/testequation.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ def testequation_setup(prec_type=None, maxiter=None):
1818
# initialize level parameters
1919
level_params = dict()
2020
level_params['restol'] = 0.0
21-
level_params['dt'] = 0.25
21+
level_params['dt'] = 1.0
2222
level_params['nsweeps'] = [1]
2323

2424
# initialize sweeper parameters
2525
sweeper_params = dict()
2626
sweeper_params['collocation_class'] = CollGaussRadau_Right
27-
sweeper_params['num_nodes'] = [3]
27+
sweeper_params['num_nodes'] = [5]
2828
sweeper_params['QI'] = prec_type
2929
sweeper_params['initial_guess'] = 'spread'
3030

@@ -35,7 +35,7 @@ def testequation_setup(prec_type=None, maxiter=None):
3535
# problem_params['lambdas'] = [[-1.0]]
3636
# .. or a list of values like this ...
3737
# problem_params['lambdas'] = [[-1.0, -2.0, 1j, -1j]]
38-
problem_params['lambdas'] = [[-1000]]
38+
problem_params['lambdas'] = [[-1.0 + 0j]]
3939
# note: PFASST will do all of those at once, but without interaction (realized via diagonal matrix).
4040
# The propagation matrix will be diagonal too, corresponding to the respective lambda value.
4141

@@ -45,7 +45,7 @@ def testequation_setup(prec_type=None, maxiter=None):
4545

4646
# initialize controller parameters
4747
controller_params = dict()
48-
controller_params['logger_level'] = 30
48+
controller_params['logger_level'] = 20
4949
controller_params['hook_class'] = error_output
5050

5151
# fill description dictionary for easy step instantiation
@@ -64,11 +64,11 @@ def compare_preconditioners(f=None, list_of_k=None):
6464

6565
# set time parameters
6666
t0 = 0.0
67-
Tend = 1.0
67+
Tend = 2.0
6868

6969
for k in list_of_k:
7070

71-
description_IE, controller_params_IE = testequation_setup(prec_type='IE', maxiter=k)
71+
description_IE, controller_params_IE = testequation_setup(prec_type='MIN3', maxiter=k)
7272
description_LU, controller_params_LU = testequation_setup(prec_type='LU', maxiter=k)
7373

7474
out = f'\nWorking with maxiter = {k}'
@@ -114,7 +114,7 @@ def compare_preconditioners(f=None, list_of_k=None):
114114
def main():
115115

116116
f = open('comparison_IE_vs_LU.txt', 'w')
117-
compare_preconditioners(f=f, list_of_k=[1, 2, 3, 4])
117+
compare_preconditioners(f=f, list_of_k=[1])
118118
f.close()
119119

120120

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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.LogisticEquation import logistics_equation
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'] = 1.0
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'] = 1E-12
26+
problem_params['newton_maxiter'] = 50
27+
problem_params['lam'] = 20
28+
problem_params['direct'] = True
29+
problem_params['u0'] = 0.5
30+
31+
# initialize step parameters
32+
step_params = dict()
33+
step_params['maxiter'] = 20
34+
35+
# initialize controller parameters
36+
controller_params = dict()
37+
# controller_params['hook_class'] = trajectories
38+
controller_params['logger_level'] = 20
39+
40+
# Fill description dictionary for easy hierarchy creation
41+
description = dict()
42+
description['problem_class'] = logistics_equation
43+
description['problem_params'] = problem_params
44+
description['sweeper_class'] = generic_implicit
45+
description['sweeper_params'] = sweeper_params
46+
description['level_params'] = level_params
47+
description['step_params'] = step_params
48+
49+
# instantiate the controller
50+
controller = controller_nonMPI(num_procs=1, controller_params=controller_params, description=description)
51+
52+
# set time parameters
53+
t0 = 0.0
54+
Tend = level_params['dt']
55+
56+
# get initial values on finest level
57+
P = controller.MS[0].levels[0].prob
58+
uinit = P.u_exact(t0)
59+
60+
# call main function to get things done...
61+
uend, stats = controller.run(u0=uinit, t0=t0, Tend=Tend)
62+
63+
uex = P.u_exact(Tend)
64+
print(abs(uend - uex))
65+
66+
67+
if __name__ == "__main__":
68+
main()

0 commit comments

Comments
 (0)