Source code for implementations.sweeper_classes.imex_1st_order_mass

from pySDC.implementations.sweeper_classes.imex_1st_order import imex_1st_order


[docs] class imex_1st_order_mass(imex_1st_order): """ Custom sweeper class, implements Sweeper.py First-order IMEX sweeper using implicit/explicit Euler as base integrator, with mass or weighting matrix """
[docs] def update_nodes(self): """ Update the u- and f-values at the collocation nodes -> corresponds to a single sweep over all nodes Returns: None """ # get current level and problem description L = self.level P = L.prob # only if the level has been touched before assert L.status.unlocked # get number of collocation nodes for easier access M = self.coll.num_nodes # gather all terms which are known already (e.g. from the previous iteration) # this corresponds to u0 + QF(u^k) - QIFI(u^k) - QEFE(u^k) + tau # get QF(u^k) integral = self.integrate() # This is somewhat ugly, but we have to apply the mass matrix on u0 only on the finest level if L.level_index == 0: u0 = P.apply_mass_matrix(L.u[0]) else: u0 = L.u[0] for m in range(M): # subtract QIFI(u^k)_m + QEFE(u^k)_m for j in range(M + 1): integral[m] -= L.dt * (self.QI[m + 1, j] * L.f[j].impl + self.QE[m + 1, j] * L.f[j].expl) # add initial value integral[m] += u0 # add tau if associated if L.tau[m] is not None: integral[m] += L.tau[m] # do the sweep for m in range(0, M): # build rhs, consisting of the known values from above and new values from previous nodes (at k+1) rhs = P.dtype_u(integral[m]) for j in range(m + 1): rhs += L.dt * (self.QI[m + 1, j] * L.f[j].impl + self.QE[m + 1, j] * L.f[j].expl) # implicit solve with prefactor stemming from QI L.u[m + 1] = P.solve_system( rhs, L.dt * self.QI[m + 1, m + 1], L.u[m + 1], L.time + L.dt * self.coll.nodes[m] ) # update function values L.f[m + 1] = P.eval_f(L.u[m + 1], L.time + L.dt * self.coll.nodes[m]) # indicate presence of new values at this level L.status.updated = True return None
[docs] def compute_end_point(self): """ Compute u at the right point of the interval The value uend computed here is a full evaluation of the Picard formulation unless do_full_update==False Returns: None """ # get current level and problem description L = self.level P = L.prob # check if Mth node is equal to right point and do_coll_update is false, perform a simple copy if self.coll.right_is_node and not self.params.do_coll_update: # a copy is sufficient L.uend = P.dtype_u(L.u[-1]) else: raise NotImplementedError('Mass matrix sweeper expect u_M = u_end') return None
[docs] def compute_residual(self, stage=None): """ Computation of the residual using the collocation matrix Q Args: stage (str): The current stage of the step the level belongs to """ # get current level and problem description L = self.level P = L.prob # Check if we want to skip the residual computation to gain performance # Keep in mind that skipping any residual computation is likely to give incorrect outputs of the residual! if stage in self.params.skip_residual_computation: L.status.residual = 0.0 if L.status.residual is None else L.status.residual return None # check if there are new values (e.g. from a sweep) # assert L.status.updated # compute the residual for each node # build QF(u) res_norm = [] res = self.integrate() for m in range(self.coll.num_nodes): # This is somewhat ugly, but we have to apply the mass matrix on u0 only on the finest level if L.level_index == 0: res[m] += P.apply_mass_matrix(L.u[0] - L.u[m + 1]) else: res[m] += L.u[0] - P.apply_mass_matrix(L.u[m + 1]) # add tau if associated if L.tau[m] is not None: res[m] += L.tau[m] # Due to different boundary conditions we might have to fix the residual if L.prob.fix_bc_for_residual: L.prob.fix_residual(res[m]) # use abs function from data type here res_norm.append(abs(res[m])) # find maximal residual over the nodes L.status.residual = max(res_norm) # indicate that the residual has seen the new values L.status.updated = False return None