11import itertools
2- import copy as cp
32import numpy as np
4- import dill
53
64from pySDC .core .controller import ParaDiagController
75from pySDC .core import step as stepclass
8- from pySDC .core .errors import ControllerError , CommunicationError
6+ from pySDC .core .errors import ControllerError
97from pySDC .implementations .convergence_controller_classes .basic_restarting import BasicRestarting
108from pySDC .helpers .ParaDiagHelper import get_G_inv_matrix
119
@@ -92,7 +90,7 @@ def ParaDiag(self, local_MS_active):
9290
9391 return all (S .status .done for S in local_MS_active )
9492
95- def apply_matrix (self , mat ):
93+ def apply_matrix (self , mat , quantity ):
9694 """
9795 Apply a matrix on the step level. Needs to be square. Puts the result back into the controller.
9896
@@ -112,29 +110,33 @@ def apply_matrix(self, mat):
112110 None ,
113111 ] * L
114112
113+ if quantity == 'residual' :
114+ me = [S .levels [0 ].residual for S in self .MS ]
115+ elif quantity == 'increment' :
116+ me = [S .levels [0 ].increment for S in self .MS ]
117+ else :
118+ raise NotImplementedError
119+
115120 # compute matrix-vector product
116121 for i in range (mat .shape [0 ]):
117- res [i ] = [prob .u_init for _ in range (M + 1 )]
122+ res [i ] = [prob .u_init for _ in range (M )]
118123 for j in range (mat .shape [1 ]):
119- for m in range (M + 1 ):
120- res [i ][m ] += mat [i , j ] * self . MS [j ]. levels [ 0 ]. u [m ]
124+ for m in range (M ):
125+ res [i ][m ] += mat [i , j ] * me [j ][m ]
121126
122127 # put the result in the "output"
123128 for i in range (mat .shape [0 ]):
124- for m in range (M + 1 ):
125- self . MS [i ]. levels [ 0 ]. u [m ] = res [i ][m ]
129+ for m in range (M ):
130+ me [i ][m ] = res [i ][m ]
126131
127- def swap_solution_for_all_at_once_residual (self , local_MS_running ):
132+ def compute_all_at_once_residual (self , local_MS_running ):
128133 """
129- Replace the solution values in the steps with the all-at-once residual.
130-
131134 This requires to communicate the solutions at the end of the steps to be the initial conditions for the next
132135 steps. Afterwards, the residual can be computed locally on the steps.
133136
134137 Args:
135138 local_MS_running (list): list of currently running steps
136139 """
137- prob = self .MS [0 ].levels [0 ].prob
138140
139141 for S in local_MS_running :
140142 # communicate initial conditions
@@ -143,9 +145,7 @@ def swap_solution_for_all_at_once_residual(self, local_MS_running):
143145 for hook in self .hooks :
144146 hook .pre_comm (step = S , level_number = 0 )
145147
146- if S .status .first :
147- S .levels [0 ].u [0 ] = prob .dtype_u (self .ParaDiag_block_u0 )
148- else :
148+ if not S .status .first :
149149 S .levels [0 ].u [0 ] = S .prev .levels [0 ].uend
150150
151151 for hook in self .hooks :
@@ -154,25 +154,16 @@ def swap_solution_for_all_at_once_residual(self, local_MS_running):
154154 # compute residuals locally
155155 S .levels [0 ].sweep .compute_residual ()
156156
157- # put residual in the solution variables
158- for m in range (S .levels [0 ].sweep .coll .num_nodes ):
159- S .levels [0 ].u [m + 1 ] = S .levels [0 ].residual [m ]
160-
161- def swap_increment_for_solution (self , local_MS_running ):
157+ def update_solution (self , local_MS_running ):
162158 """
163- After inversion of the preconditioner, the values stored in the steps are the increment. This function adds the
164- solution after the previous iteration to arrive at the solution after the current iteration.
165- Note that we also need to put in the initial conditions back in the first step because they will be perturbed by
166- the circular preconditioner.
159+ Since we solve for the increment, we need to update the solution between iterations by adding the increment.
167160
168161 Args:
169162 local_MS_running (list): list of currently running steps
170163 """
171164 for S in local_MS_running :
172- for m in range (S .levels [0 ].sweep .coll .num_nodes + 1 ):
173- S .levels [0 ].u [m ] = S .levels [0 ].uold [m ] + S .levels [0 ].u [m ]
174- if S .status .first :
175- S .levels [0 ].u [0 ] = self .ParaDiag_block_u0
165+ for m in range (S .levels [0 ].sweep .coll .num_nodes ):
166+ S .levels [0 ].u [m + 1 ] += S .levels [0 ].increment [m ]
176167
177168 def prepare_Jacobians (self , local_MS_running ):
178169 # get solutions for constructing average Jacobians
@@ -215,22 +206,22 @@ def it_ParaDiag(self, local_MS_running):
215206 # communicate average residual for setting up Jacobians for non-linear problems
216207 self .prepare_Jacobians (local_MS_running )
217208
218- # replace the values stored in the steps with the residuals in order to compute the increment
219- self .swap_solution_for_all_at_once_residual (local_MS_running )
209+ # compute the all-at-once residual to use as right hand side
210+ self .compute_all_at_once_residual (local_MS_running )
220211
221- # weighted FFT in time
222- self .FFT_in_time ()
212+ # weighted FFT of the residual in time
213+ self .FFT_in_time (quantity = 'residual' )
223214
224215 # perform local solves of "collocation problems" on the steps (can be done in parallel)
225216 for S in local_MS_running :
226217 assert len (S .levels ) == 1 , 'Multi-level SDC not implemented in ParaDiag'
227218 S .levels [0 ].sweep .update_nodes ()
228219
229- # inverse FFT in time
230- self .iFFT_in_time ()
220+ # inverse FFT of the increment in time
221+ self .iFFT_in_time (quantity = 'increment' )
231222
232- # replace the values stored in the steps with the previous solution plus the increment
233- self .swap_increment_for_solution (local_MS_running )
223+ # get the next iterate by adding increment to previous iterate
224+ self .update_solution (local_MS_running )
234225
235226 for S in local_MS_running :
236227 for hook in self .hooks :
@@ -438,7 +429,6 @@ def restart_block(self, active_slots, time, u0):
438429 u0: initial value to distribute across the steps
439430
440431 """
441- self .ParaDiag_block_u0 = u0 # need this for computing residual
442432
443433 for j in range (len (active_slots )):
444434 # get slot number
0 commit comments