Skip to content

Commit a24d594

Browse files
committed
added FPUT
1 parent 79cd0d5 commit a24d594

File tree

4 files changed

+450
-3
lines changed

4 files changed

+450
-3
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
from __future__ import division
2+
from numba import jit
3+
import numpy as np
4+
5+
from pySDC.core.Problem import ptype
6+
from pySDC.implementations.datatype_classes.particles import particles, acceleration
7+
from pySDC.core.Errors import ParameterError
8+
9+
10+
# noinspection PyUnusedLocal
11+
class fermi_pasta_ulam_tsingou(ptype):
12+
"""
13+
Example implementing the outer solar system problem
14+
"""
15+
16+
def __init__(self, problem_params, dtype_u, dtype_f):
17+
"""
18+
Initialization routine
19+
20+
Args:
21+
problem_params (dict): custom parameters for the example
22+
dtype_u: particle data type (will be passed parent class)
23+
dtype_f: acceleration data type (will be passed parent class)
24+
"""
25+
26+
# these parameters will be used later, so assert their existence
27+
essential_keys = ['npart', 'alpha', 'k', 'energy_modes']
28+
for key in essential_keys:
29+
if key not in problem_params:
30+
msg = 'need %s to instantiate problem, only got %s' % (key, str(problem_params.keys()))
31+
raise ParameterError(msg)
32+
33+
# invoke super init, passing nparts, dtype_u and dtype_f
34+
super(fermi_pasta_ulam_tsingou, self).__init__(problem_params['npart'], dtype_u, dtype_f, problem_params)
35+
36+
@staticmethod
37+
@jit
38+
def fast_acceleration(N, alpha, pos, accel):
39+
for n in range(1, N - 1):
40+
accel[n - 1] = pos[n - 1] - 2.0 * pos[n] + pos[n + 1] + \
41+
alpha * ((pos[n + 1] - pos[n]) ** 2 - (pos[n] - pos[n - 1]) ** 2)
42+
43+
def eval_f(self, u, t):
44+
"""
45+
Routine to compute the RHS
46+
47+
Args:
48+
u (dtype_u): the particles
49+
t (float): current time (not used here)
50+
Returns:
51+
dtype_f: RHS
52+
"""
53+
me = acceleration(self.init, val=0.0)
54+
55+
self.fast_acceleration(self.params.npart, self.params.alpha, u.pos.values, me.values[1:self.params.npart - 1])
56+
57+
return me
58+
59+
def u_exact(self, t):
60+
"""
61+
Routine to compute the exact/initial trajectory at time t
62+
63+
Args:
64+
t (float): current time
65+
Returns:
66+
dtype_u: exact/initial position and velocity
67+
"""
68+
assert t == 0.0, 'error, u_exact only works for the initial time t0=0'
69+
70+
me = particles(self.init)
71+
72+
for n in range(self.params.npart):
73+
me.pos.values[n] = np.sin(self.params.k * np.pi * n / (self.params.npart - 1))
74+
me.vel.values[n] = 0.0
75+
76+
return me
77+
78+
@staticmethod
79+
@jit
80+
def fast_hamiltonian(N, alpha, pos, vel):
81+
ham = 0.0
82+
for n in range(N - 1):
83+
ham += 0.5 * vel[n] ** 2 + 0.5 * (pos[n + 1] - pos[n]) ** 2 + alpha / 3.0 * (pos[n + 1] - pos[n]) ** 3
84+
return ham
85+
86+
def eval_hamiltonian(self, u):
87+
"""
88+
Routine to compute the Hamiltonian
89+
90+
Args:
91+
u (dtype_u): the particles
92+
Returns:
93+
float: hamiltonian
94+
"""
95+
return self.fast_hamiltonian(self.params.npart, self.params.alpha, u.pos.values, u.vel.values)
96+
97+
def eval_mode_energy(self, u):
98+
"""
99+
Routine to compute the energy following
100+
http://www.scholarpedia.org/article/Fermi-Pasta-Ulam_nonlinear_lattice_oscillations
101+
102+
Args:
103+
u (dtype_u): the particles
104+
Returns:
105+
dict: energies
106+
"""
107+
108+
energy = {}
109+
110+
for k in self.params.energy_modes:
111+
112+
Qk = np.sqrt(2.0 / (self.params.npart - 1)) * \
113+
sum([u.pos.values[n] * np.sin(np.pi * k * n / (self.params.npart - 1))
114+
for n in range(1, self.params.npart)])
115+
Qkdot = np.sqrt(2.0 / (self.params.npart - 1)) * \
116+
sum([u.vel.values[n] * np.sin(np.pi * k * n / (self.params.npart - 1))
117+
for n in range(1, self.params.npart)])
118+
119+
omegak2 = 4.0 * np.sin(k * np.pi / (2.0 * (self.params.npart - 1))) ** 2
120+
energy[k] = 0.5 * (Qkdot ** 2 + omegak2 * Qk ** 2)
121+
122+
return energy

pySDC/projects/Hamiltonian/fput.py

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
from __future__ import division
2+
from collections import defaultdict
3+
import os
4+
import dill
5+
import numpy as np
6+
7+
import pySDC.helpers.plot_helper as plt_helper
8+
9+
from pySDC.implementations.collocation_classes.gauss_lobatto import CollGaussLobatto
10+
from pySDC.implementations.controller_classes.allinclusive_classic_nonMPI import allinclusive_classic_nonMPI
11+
from pySDC.implementations.sweeper_classes.verlet import verlet
12+
from pySDC.implementations.datatype_classes.particles import particles, acceleration
13+
from pySDC.implementations.problem_classes.FermiPastaUlamTsingou import fermi_pasta_ulam_tsingou
14+
from pySDC.implementations.transfer_classes.TransferParticles_NoCoarse import particles_to_particles
15+
16+
from pySDC.helpers.stats_helper import filter_stats, sort_stats
17+
from pySDC.projects.Hamiltonian.hamiltonian_and_energy_output import hamiltonian_and_energy_output
18+
19+
20+
def setup_fput():
21+
"""
22+
Helper routine for setting up everything for the Fermi-Pasta-Ulam-Tsingou problem
23+
24+
Returns:
25+
description (dict): description of the controller
26+
controller_params (dict): controller parameters
27+
"""
28+
29+
# initialize level parameters
30+
level_params = dict()
31+
level_params['restol'] = 1E-08
32+
level_params['dt'] = 2.5
33+
34+
# initialize sweeper parameters
35+
sweeper_params = dict()
36+
sweeper_params['collocation_class'] = CollGaussLobatto
37+
sweeper_params['num_nodes'] = [5]
38+
sweeper_params['spread'] = False
39+
40+
# initialize problem parameters for the Penning trap
41+
problem_params = dict()
42+
problem_params['npart'] = 34
43+
problem_params['alpha'] = 0.25
44+
problem_params['k'] = 1.0
45+
problem_params['energy_modes'] = [[1, 2, 3, 4]]
46+
47+
# initialize step parameters
48+
step_params = dict()
49+
step_params['maxiter'] = 50
50+
51+
# initialize controller parameters
52+
controller_params = dict()
53+
controller_params['hook_class'] = hamiltonian_and_energy_output
54+
controller_params['logger_level'] = 30
55+
controller_params['predict'] = False
56+
57+
# Fill description dictionary for easy hierarchy creation
58+
description = dict()
59+
description['problem_class'] = fermi_pasta_ulam_tsingou
60+
description['problem_params'] = problem_params
61+
description['dtype_u'] = particles
62+
description['dtype_f'] = acceleration
63+
description['sweeper_class'] = verlet
64+
description['sweeper_params'] = sweeper_params
65+
description['level_params'] = level_params
66+
description['step_params'] = step_params
67+
description['space_transfer_class'] = particles_to_particles
68+
69+
return description, controller_params
70+
71+
72+
def run_simulation():
73+
"""
74+
Routine to run the simulation of a second order problem
75+
76+
"""
77+
78+
description, controller_params = setup_fput()
79+
# set time parameters
80+
t0 = 0.0
81+
Tend = 5000.0
82+
num_procs = 1
83+
84+
f = open('fput_out.txt', 'w')
85+
out = 'Running fput problem with %s processors...' % num_procs
86+
f.write(out + '\n')
87+
print(out)
88+
89+
# instantiate the controller
90+
controller = allinclusive_classic_nonMPI(num_procs=num_procs, controller_params=controller_params,
91+
description=description)
92+
93+
# get initial values on finest level
94+
P = controller.MS[0].levels[0].prob
95+
uinit = P.u_exact(t=t0)
96+
97+
# call main function to get things done...
98+
uend, stats = controller.run(u0=uinit, t0=t0, Tend=Tend)
99+
100+
# filter statistics by type (number of iterations)
101+
filtered_stats = filter_stats(stats, type='niter')
102+
103+
# convert filtered statistics to list of iterations count, sorted by process
104+
iter_counts = sort_stats(filtered_stats, sortby='time')
105+
106+
# compute and print statistics
107+
for item in iter_counts:
108+
out = 'Number of iterations for time %4.2f: %2i' % item
109+
f.write(out)
110+
print(out)
111+
112+
niters = np.array([item[1] for item in iter_counts])
113+
out = ' Mean number of iterations: %4.2f' % np.mean(niters)
114+
f.write(out + '\n')
115+
print(out)
116+
out = ' Range of values for number of iterations: %2i ' % np.ptp(niters)
117+
f.write(out + '\n')
118+
print(out)
119+
out = ' Position of max/min number of iterations: %2i -- %2i' % \
120+
(int(np.argmax(niters)), int(np.argmin(niters)))
121+
f.write(out + '\n')
122+
print(out)
123+
out = ' Std and var for number of iterations: %4.2f -- %4.2f' % (float(np.std(niters)), float(np.var(niters)))
124+
f.write(out + '\n')
125+
print(out)
126+
127+
# get runtime
128+
timing_run = sort_stats(filter_stats(stats, type='timing_run'), sortby='time')[0][1]
129+
out = '... took %s seconds to run this.' % timing_run
130+
f.write(out + '\n')
131+
print(out)
132+
f.close()
133+
134+
# assert np.mean(niters) <= maxmeaniter, 'Mean number of iterations is too high, got %s' % np.mean(niters)
135+
136+
fname = 'data/fput.dat'
137+
f = open(fname, 'wb')
138+
dill.dump(stats, f)
139+
f.close()
140+
141+
assert os.path.isfile(fname), 'Run for %s did not create stats file' % prob
142+
143+
144+
def show_results(cwd=''):
145+
"""
146+
Helper function to plot the error of the Hamiltonian
147+
148+
Args:
149+
cwd (str): current working directory
150+
"""
151+
152+
# read in the dill data
153+
f = open(cwd + 'data/fput.dat', 'rb')
154+
stats = dill.load(f)
155+
f.close()
156+
157+
plt_helper.mpl.style.use('classic')
158+
plt_helper.setup_mpl()
159+
160+
# HAMILTONIAN PLOTTING #
161+
# extract error in hamiltonian and prepare for plotting
162+
extract_stats = filter_stats(stats, type='err_hamiltonian')
163+
result = defaultdict(list)
164+
for k, v in extract_stats.items():
165+
result[k.iter].append((k.time, v))
166+
for k, v in result.items():
167+
result[k] = sorted(result[k], key=lambda x: x[0])
168+
169+
plt_helper.newfig(textwidth=238.96, scale=0.89)
170+
171+
# Rearrange data for easy plotting
172+
err_ham = 1
173+
for k, v in result.items():
174+
time = [item[0] for item in v]
175+
ham = [item[1] for item in v]
176+
err_ham = ham[-1]
177+
plt_helper.plt.semilogy(time, ham, '-', lw=1, label='Iter ' + str(k))
178+
print(err_ham)
179+
# assert err_ham < 7.5E-15, 'Error in the Hamiltonian is too large for %s, got %s' % (prob, err_ham)
180+
181+
plt_helper.plt.xlabel('Time')
182+
plt_helper.plt.ylabel('Error in Hamiltonian')
183+
plt_helper.plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
184+
185+
fname = 'data/fput_hamiltonian'
186+
plt_helper.savefig(fname)
187+
188+
assert os.path.isfile(fname + '.pdf'), 'ERROR: plotting did not create PDF file'
189+
assert os.path.isfile(fname + '.pgf'), 'ERROR: plotting did not create PGF file'
190+
assert os.path.isfile(fname + '.png'), 'ERROR: plotting did not create PNG file'
191+
192+
# ENERGY PLOTTING #
193+
# extract error in hamiltonian and prepare for plotting
194+
extract_stats = filter_stats(stats, type='energy_step')
195+
result = sort_stats(extract_stats, sortby='time')
196+
197+
plt_helper.newfig(textwidth=238.96, scale=0.89)
198+
199+
# Rearrange data for easy plotting
200+
201+
for mode in result[0][1].keys():
202+
time = [item[0] for item in result]
203+
energy = [item[1][mode] for item in result]
204+
plt_helper.plt.plot(time, energy, label=str(mode) + 'th mode')
205+
206+
plt_helper.plt.xlabel('Time')
207+
plt_helper.plt.ylabel('Energy')
208+
plt_helper.plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
209+
210+
fname = 'data/fput_energy'
211+
plt_helper.savefig(fname)
212+
213+
assert os.path.isfile(fname + '.pdf'), 'ERROR: plotting did not create PDF file'
214+
assert os.path.isfile(fname + '.pgf'), 'ERROR: plotting did not create PGF file'
215+
assert os.path.isfile(fname + '.png'), 'ERROR: plotting did not create PNG file'
216+
217+
# POSITION PLOTTING #
218+
# extract positions and prepare for plotting
219+
extract_stats = filter_stats(stats, type='position')
220+
result = sort_stats(extract_stats, sortby='time')
221+
222+
plt_helper.newfig(textwidth=238.96, scale=0.89)
223+
224+
# Rearrange data for easy plotting
225+
nparts = len(result[0][1])
226+
nsteps = len(result)
227+
pos = np.zeros((nparts, nsteps))
228+
time = np.zeros(nsteps)
229+
for idx, item in enumerate(result):
230+
time[idx] = item[0]
231+
for n in range(nparts):
232+
pos[n, idx] = item[1][n]
233+
234+
for n in range(nparts):
235+
plt_helper.plt.plot(time, pos[n, :])
236+
237+
fname = 'data/fput_positions'
238+
plt_helper.savefig(fname)
239+
240+
assert os.path.isfile(fname + '.pdf'), 'ERROR: plotting did not create PDF file'
241+
assert os.path.isfile(fname + '.pgf'), 'ERROR: plotting did not create PGF file'
242+
assert os.path.isfile(fname + '.png'), 'ERROR: plotting did not create PNG file'
243+
244+
245+
def main():
246+
run_simulation()
247+
show_results()
248+
249+
if __name__ == "__main__":
250+
main()

0 commit comments

Comments
 (0)