Coverage for pySDC/projects/Second_orderSDC/penningtrap_Simulation.py: 76%
133 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 14:51 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 14:51 +0000
1import numpy as np
3from pySDC.helpers.stats_helper import get_sorted
4from pySDC.implementations.controller_classes.controller_nonMPI import controller_nonMPI
5from pySDC.projects.Second_orderSDC.penningtrap_HookClass import particles_output
6from pySDC.implementations.sweeper_classes.Runge_Kutta_Nystrom import RKN, Velocity_Verlet
7from pySDC.projects.Second_orderSDC.plot_helper import PlotManager
10class ComputeError(PlotManager):
11 """
12 This class generates data for plots and computations for Second-order SDC
13 """
15 def __init__(self, controller_params, description, time_iter=3, K_iter=(1, 2, 3), Tend=2, axes=(1,), cwd=''):
16 super().__init__(
17 controller_params, description, time_iter=time_iter, K_iter=K_iter, Tend=Tend, axes=axes, cwd=''
18 )
20 def run_local_error(self):
21 """
22 Controls everything to generate local convergence rate
23 """
24 self.compute_local_error_data()
25 self.plot_convergence()
27 def run_global_error(self):
28 """
29 Computes global convergence order and finds approximate order
30 """
31 self.error_type = 'global'
32 self.compute_global_error_data()
33 self.find_approximate_order(filename='data/dt_vs_global_errorSDC.csv')
34 self.plot_convergence()
36 def run_work_precision(self, RK=True, VV=False, dt_cont=1):
37 """
38 Implements work-precision of Second-order SDC
39 Args:
40 RK: True or False to include RKN method
41 VV: True or False to include Velocity-Verlet Scheme
42 dt_cont: moves RK and VV left to right
43 """
44 self.RK = RK
45 self.VV = VV
46 self.compute_global_error_data(work_counter=True)
47 self.compute_global_error_data(Picard=True, work_counter=True)
48 if self.RK:
49 self.compute_global_error_data(RK=RK, work_counter=True, dt_cont=dt_cont)
50 if self.VV:
51 self.compute_global_error_data(VV=VV, work_counter=True, dt_cont=dt_cont)
52 self.plot_work_precision()
54 def compute_local_error_data(self):
55 """
56 Computes local convergence rate and saves the data
57 """
58 step_params = dict()
59 dt_val = self.description['level_params']['dt']
61 for order in self.K_iter:
62 step_params['maxiter'] = order
63 self.description['step_params'] = step_params
65 file_path = self.cwd + 'data/dt_vs_local_errorSDC.csv'
66 mode = 'w' if order == self.K_iter[0] else 'a'
68 with open(file_path, mode) as file:
69 if order == self.K_iter[0]:
70 file.write("Time_steps | Order_pos | Abs_error_position | Order_vel | Abs_error_velocity\n")
72 for ii in range(0, self.time_iter):
73 dt = dt_val / 2**ii
75 self.description['level_params']['dt'] = dt
76 self.description['level_params'] = self.description['level_params']
78 controller = controller_nonMPI(
79 num_procs=1, controller_params=self.controller_params, description=self.description
80 )
82 t0, Tend = 0.0, dt
83 P = controller.MS[0].levels[0].prob
84 uinit = P.u_init()
86 uend, stats = controller.run(u0=uinit, t0=t0, Tend=Tend)
88 uex = P.u_exact(Tend)
89 coll_order = controller.MS[0].levels[0].sweep.coll.order
90 order_pos = list(self.local_order_pos(order, coll_order))
91 order_vel = list(self.local_order_vel(order, coll_order))
92 error_pos = list(np.abs((uex - uend).pos).T[0])
93 error_vel = list(np.abs((uex - uend).vel).T[0])
95 dt_omega = dt * self.description['problem_params']['omega_B']
96 file.write(
97 f"{dt_omega}, {', '.join(map(str, order_pos))}, {', '.join(map(str, error_pos))},"
98 f" {', '.join(map(str, order_vel))}, {', '.join(map(str, error_vel))}\n"
99 )
101 def compute_global_error_data(self, Picard=False, RK=False, VV=False, work_counter=False, dt_cont=1):
102 """
103 Computes global convergence data and saves it into the data folder
104 Args:
105 Picard: bool, Picard iteration computation
106 RK: bool, RKN method
107 VV: bool, Velocity-Verlet scheme
108 work_counter: bool, compute rhs for work precision
109 dt_cont: moves the data left to right for RK and VV method
110 """
111 K_iter = self.K_iter
112 name, description = '', self.description
114 if Picard:
115 name = 'Picard'
116 description['sweeper_params']['QI'] = 'PIC'
117 description['sweeper_params']['QE'] = 'PIC'
118 elif RK:
119 K_iter, name, description['sweeper_class'] = (1,), 'RKN', RKN
120 elif VV:
121 K_iter, name, description['sweeper_class'] = (1,), 'VV', Velocity_Verlet
122 else:
123 name = 'SDC'
125 self.controller_params['hook_class'] = particles_output
126 step_params, dt_val = dict(), self.description['level_params']['dt']
127 values, error = ['position', 'velocity'], dict()
129 filename = f"data/{'rhs_eval_vs_global_error' if work_counter else 'dt_vs_global_error'}{name}.csv"
131 for order in K_iter:
132 u_val, uex_val = dict(), dict()
133 step_params['maxiter'], description['step_params'] = order, step_params
135 file_path = self.cwd + filename
136 mode = 'w' if order == K_iter[0] else 'a'
138 with open(file_path, mode) as file:
139 if order == K_iter[0]:
140 file.write(
141 "Time_steps/Work_counter | Order_pos | Abs_error_position | Order_vel | Abs_error_velocity\n"
142 )
144 cont = 2 if self.time_iter == 3 else 2 ** abs(3 - self.time_iter)
145 cont = cont if not Picard else dt_cont
147 for ii in range(0, self.time_iter):
148 dt = (dt_val * cont) / 2**ii
150 description['level_params']['dt'] = dt
151 description['level_params'] = self.description['level_params']
153 controller = controller_nonMPI(
154 num_procs=1, controller_params=self.controller_params, description=description
155 )
157 t0, Tend = 0.0, self.Tend
158 P = controller.MS[0].levels[0].prob
159 uinit = P.u_init()
160 uend, stats = controller.run(u0=uinit, t0=t0, Tend=Tend)
162 func_eval = P.work_counters['Boris_solver'].niter + P.work_counters['rhs'].niter
164 for nn in values:
165 u_val[nn] = get_sorted(stats, type=nn, sortby='time')
166 uex_val[nn] = get_sorted(stats, type=nn + '_exact', sortby='time')
167 error[nn] = self.relative_error(uex_val[nn], u_val[nn])
168 error[nn] = list(error[nn].T[0])
170 if RK or VV:
171 global_order = np.array([4, 4])
172 else:
173 coll_order = controller.MS[0].levels[0].sweep.coll.order
174 global_order = list(self.global_order(order, coll_order))
175 global_order = np.array([4, 4]) if RK or VV else list(self.global_order(order, coll_order))
177 dt_omega = dt * self.description['problem_params']['omega_B']
178 save = func_eval if work_counter else dt_omega
180 file.write(
181 f"{save}, {', '.join(map(str, global_order))}, {', '.join(map(str, error['position']))},"
182 f" {', '.join(map(str, global_order))}, {', '.join(map(str, error['velocity']))}\n"
183 )
185 def local_order_pos(self, order_K, order_quad):
186 if self.description['sweeper_params']['initial_guess'] == 'spread':
187 if self.quad_type == 'GAUSS' or self.quad_type == 'RADAU-RIGHT':
188 return np.array([np.min([order_K + 2 + 2, order_quad]), np.min([2 * order_K + 3, order_quad])])
189 elif self.quad_type == 'LOBATTO' or self.quad_type == 'RADAU-LEFT':
190 return np.array([np.min([order_K + 2 + 2, order_quad]), np.min([2 * order_K + 3, order_quad])])
191 else:
192 raise NotImplementedError('Order of convergence explicitly not implemented')
193 else:
194 if self.quad_type == 'GAUSS' or self.quad_type == 'RADAU-RIGHT':
195 return np.array([np.min([order_K + 2, order_quad]), np.min([2 * order_K + 3, order_quad])])
196 elif self.quad_type == 'LOBATTO' or self.quad_type == 'RADAU-LEFT':
197 return np.array([np.min([order_K + 2, order_quad]), np.min([2 * order_K + 3, order_quad])])
198 else:
199 raise NotImplementedError('Order of convergence explicitly not implemented')
201 def local_order_vel(self, order_K, order_quad):
202 if self.description['sweeper_params']['initial_guess'] == 'spread':
203 if self.quad_type == 'GAUSS' or self.quad_type == 'RADAU-RIGHT':
204 return np.array([np.min([order_K + 1 + 2, order_quad]), np.min([2 * order_K + 2, order_quad])])
205 elif self.quad_type == 'LOBATTO' or self.quad_type == 'RADAU-LEFT':
206 return np.array([np.min([order_K + 1 + 2, order_quad]), np.min([2 * order_K + 2, order_quad])])
207 else:
208 raise NotImplementedError('Order of convergence explicitly not implemented')
209 else:
210 if self.quad_type == 'GAUSS' or self.quad_type == 'RADAU-RIGHT':
211 return np.array([np.min([order_K + 1, order_quad]), np.min([2 * order_K + 2, order_quad])])
212 elif self.quad_type == 'LOBATTO' or self.quad_type == 'RADAU-LEFT':
213 return np.array([np.min([order_K + 1, order_quad]), np.min([2 * order_K + 2, order_quad])])
214 else:
215 raise NotImplementedError('Order of convergence explicitly not implemented')
217 def global_order(self, order_K, order_quad):
218 if self.quad_type == 'GAUSS' or self.quad_type == 'RADAU-RIGHT':
219 return np.array([np.min([order_K, order_quad]), np.min([2 * order_K, order_quad])])
220 elif self.quad_type == 'LOBATTO' or self.quad_type == 'RADAU-LEFT':
221 return np.array([np.min([order_K, order_quad]), np.min([2 * order_K, order_quad])]) + 2
222 else:
223 raise NotImplementedError('Order of convergence explicitly not implemented')
225 def relative_error(self, uex_data, u_data):
226 u_ex = np.array([entry[1] for entry in uex_data])
227 u = np.array([entry[1] for entry in u_data])
228 return np.linalg.norm(np.abs((u_ex - u)), np.inf, 0) / np.linalg.norm(u_ex, np.inf, 0)