def main(): """ A simple test program to setup a full step hierarchy """ # initialize level parameters level_params = dict() level_params['restol'] = 1E-10 level_params['dt'] = 0.1 # initialize sweeper parameters sweeper_params = dict() sweeper_params['collocation_class'] = CollGaussRadau_Right sweeper_params['num_nodes'] = [5, 3] # initialize problem parameters problem_params = dict() problem_params['nu'] = 0.1 # diffusion coefficient problem_params['freq'] = 4 # frequency for the test value problem_params['nvars'] = [31, 15, 7 ] # number of degrees of freedom for each level # initialize step parameters step_params = dict() step_params['maxiter'] = 20 # initialize space transfer parameters space_transfer_params = dict() space_transfer_params['rorder'] = 2 space_transfer_params['iorder'] = 2 # fill description dictionary for easy step instantiation description = dict() description['problem_class'] = heat1d # pass problem class description['problem_params'] = problem_params # pass problem parameters description['sweeper_class'] = generic_LU # pass sweeper (see part B) description['sweeper_params'] = sweeper_params # pass sweeper parameters description['level_params'] = level_params # pass level parameters description['step_params'] = step_params # pass step parameters description[ 'space_transfer_class'] = mesh_to_mesh # pass spatial transfer class description[ 'space_transfer_params'] = space_transfer_params # pass paramters for spatial transfer # now the description contains more or less everything we need to create a step with multiple levels S = step(description=description) # print out and check f = open('step_4_B_out.txt', 'w') for l in range(len(S.levels)): L = S.levels[l] out = 'Level %2i: nvars = %4i -- nnodes = %2i' % ( l, L.prob.params.nvars, L.sweep.coll.num_nodes) f.write(out + '\n') print(out) assert L.prob.params.nvars == problem_params['nvars'][min(l, len(problem_params['nvars']) - 1)], \ "ERROR: number of DOFs is not correct on this level, got %s" % L.prob.params.nvars assert L.sweep.coll.num_nodes == sweeper_params['num_nodes'][min(l, len(sweeper_params['num_nodes']) - 1)], \ "ERROR: number of nodes is not correct on this level, got %s" % L.sweep.coll.num_nodes f.close()
def main(): """ A simple test program to setup a full step instance """ # initialize level parameters level_params = dict() level_params['restol'] = 1E-10 level_params['dt'] = 0.1 # initialize sweeper parameters sweeper_params = dict() sweeper_params['collocation_class'] = CollGaussRadau_Right sweeper_params['num_nodes'] = 3 # initialize problem parameters problem_params = dict() problem_params['nu'] = 0.1 # diffusion coefficient problem_params['freq'] = 4 # frequency for the test value problem_params['nvars'] = 1023 # number of degrees of freedom # initialize step parameters step_params = dict() step_params['maxiter'] = 20 # fill description dictionary for easy step instantiation description = dict() description['problem_class'] = heat1d # pass problem class description['problem_params'] = problem_params # pass problem parameters description['dtype_u'] = mesh # pass data type for u description['dtype_f'] = mesh # pass data type for f description['sweeper_class'] = generic_LU # pass sweeper (see part B) description['sweeper_params'] = sweeper_params # pass sweeper parameters description['level_params'] = level_params # pass level parameters description['step_params'] = step_params # pass step parameters # now the description contains more or less everything we need to create a step S = step(description=description) # we only have a single level, make a shortcut L = S.levels[0] # one of the integral parts of each level is the problem class, make a shortcut P = L.prob # now we can do e.g. what we did before with the problem err = run_accuracy_check(P) f = open('step_2_A_out.txt', 'w') out = 'Error of the spatial accuracy test: %8.6e' % err f.write(out) print(out) f.close() assert err <= 2E-04, "ERROR: the spatial accuracy is higher than expected, got %s" % err
def __init__(self, controller_params, description, comm): """ Initialization routine for PFASST controller Args: controller_params: parameter set for the controller and the step class description: all the parameters to set up the rest (levels, problems, transfer, ...) comm: MPI communicator """ # call parent's initialization routine super(controller_MPI, self).__init__(controller_params) # create single step per processor self.S = step(description) # pass communicator for future use self.comm = comm num_procs = self.comm.Get_size() rank = self.comm.Get_rank() # insert data on time communicator to the steps (helpful here and there) self.S.status.time_size = num_procs if self.params.dump_setup and rank == 0: self.dump_setup(step=self.S, controller_params=controller_params, description=description) num_levels = len(self.S.levels) # add request handler for status send self.req_status = None # add request handle container for isend self.req_send = [None] * num_levels self.req_ibcast = None self.req_diff = None if num_procs > 1 and num_levels > 1: for L in self.S.levels: if not L.sweep.coll.right_is_node or L.sweep.params.do_coll_update: raise ControllerError( "For PFASST to work, we assume uend^k = u_M^k") if num_levels == 1 and self.params.predict_type is not None: self.logger.warning( 'you have specified a predictor type but only a single level.. ' 'predictor will be ignored')
def main(): """ A simple test program to run IMEX SDC for a single time step """ # initialize level parameters level_params = dict() level_params['restol'] = 1E-10 level_params['dt'] = 0.1 # initialize sweeper parameters sweeper_params = dict() sweeper_params['collocation_class'] = CollGaussRadau_Right sweeper_params['num_nodes'] = 3 # initialize problem parameters problem_params = dict() problem_params['nu'] = 0.1 # diffusion coefficient problem_params['freq'] = 4 # frequency for the test value problem_params['nvars'] = 1023 # number of degrees of freedom # initialize step parameters step_params = dict() step_params['maxiter'] = 20 # Fill description dictionary for easy hierarchy creation description = dict() description['problem_class'] = heat1d_forced description['problem_params'] = problem_params description['dtype_u'] = mesh description['dtype_f'] = rhs_imex_mesh description['sweeper_class'] = imex_1st_order description['sweeper_params'] = sweeper_params description['level_params'] = level_params description['step_params'] = step_params # instantiate the step we are going to work on S = step(description=description) # run IMEX SDC test and check error, residual and number of iterations err, res, niter = run_imex_sdc(S) print('Error and residual: %12.8e -- %12.8e' % (err, res)) assert err <= 1E-5, "ERROR: IMEX SDC iteration did not reduce the error enough, got %s" % err assert res <= level_params[ 'restol'], "ERROR: IMEX SDC iteration did not reduce the residual enough, got %s" % res assert niter <= 12, "ERROR: IMEX SDC took too many iterations, got %s" % niter
def __init__(self, controller_params, description, comm): """ Initialization routine for PFASST controller Args: controller_params: parameter set for the controller and the step class description: all the parameters to set up the rest (levels, problems, transfer, ...) comm: MPI communicator """ # call parent's initialization routine super(allinclusive_classic_MPI, self).__init__(controller_params) self.logger.warning( 'classic controller is about to become deprecated, use multigrid controller instead' ) # create single step per processor self.S = step(description) # pass communicator for future use self.comm = comm # add request handle container for isend self.req_send = [] # add request handler for status send self.req_status = None num_procs = self.comm.Get_size() rank = self.comm.Get_rank() if self.params.dump_setup and rank == 0: self.dump_setup(step=self.S, controller_params=controller_params, description=description) num_levels = len(self.S.levels) assert not (num_procs > 1 and num_levels == 1), "ERROR: classic cannot do MSSDC" if num_procs > 1 and num_levels > 1: for L in self.S.levels: if not L.sweep.coll.right_is_node or L.sweep.params.do_coll_update: raise ControllerError( "For PFASST to work, we assume uend^k = u_M^k in this controller" )
def compute_specrad(): """ Routine to compute spectral radius and norm of the error propagation matrix E Returns: numpy.nparray: list of number of nodes numpy.nparray: list of fast lambdas numpy.nparray: list of spectral radii numpy.nparray: list of norms """ problem_params = dict() # SET VALUE FOR lambda_slow AND VALUES FOR lambda_fast ### problem_params['lambda_s'] = np.array([1.0 * 1j], dtype='complex') problem_params['lambda_f'] = np.array([50.0 * 1j, 100.0 * 1j], dtype='complex') problem_params['u0'] = 1.0 # initialize sweeper parameters sweeper_params = dict() # SET TYPE OF QUADRATURE NODES ### sweeper_params['collocation_class'] = CollGaussRadau_Right # initialize level parameters level_params = dict() level_params['dt'] = 1.0 t0 = 0.0 # fill description dictionary for easy step instantiation description = dict() description['problem_class'] = swfw_scalar # pass problem class description['problem_params'] = problem_params # pass problem parameters description['sweeper_class'] = imex_1st_order # pass sweeper (see part B) description['level_params'] = level_params # pass level parameters description['step_params'] = dict() # pass step parameters nodes_v = np.arange(2, 10) specrad = np.zeros((3, np.size(nodes_v))) norm = np.zeros((3, np.size(nodes_v))) for i in range(0, np.size(nodes_v)): sweeper_params['num_nodes'] = nodes_v[i] description[ 'sweeper_params'] = sweeper_params # pass sweeper parameters # now the description contains more or less everything we need to create a step S = step(description=description) L = S.levels[0] P = L.prob u0 = S.levels[0].prob.u_exact(t0) S.init_step(u0) QE = L.sweep.QE[1:, 1:] QI = L.sweep.QI[1:, 1:] Q = L.sweep.coll.Qmat[1:, 1:] nnodes = L.sweep.coll.num_nodes dt = L.params.dt assert nnodes == nodes_v[ i], 'Something went wrong during instantiation, nnodes is not correct, got %s' % nnodes for j in range(0, 2): LHS = np.eye(nnodes) - dt * (P.params.lambda_f[j] * QI + P.params.lambda_s[0] * QE) RHS = dt * ( (P.params.lambda_f[j] + P.params.lambda_s[0]) * Q - (P.params.lambda_f[j] * QI + P.params.lambda_s[0] * QE)) evals, evecs = np.linalg.eig(np.linalg.inv(LHS).dot(RHS)) specrad[j + 1, i] = np.linalg.norm(evals, np.inf) norm[j + 1, i] = np.linalg.norm(np.linalg.inv(LHS).dot(RHS), np.inf) if L.sweep.coll.left_is_node: # For Lobatto nodes, first column and row are all zeros, since q_1 = q_0; hence remove them QI = QI[1:, 1:] Q = Q[1:, 1:] # Eigenvalue of error propagation matrix in stiff limit: E = I - inv(QI)*Q evals, evecs = np.linalg.eig( np.eye(nnodes - 1) - np.linalg.inv(QI).dot(Q)) norm[0, i] = np.linalg.norm( np.eye(nnodes - 1) - np.linalg.inv(QI).dot(Q), np.inf) else: evals, evecs = np.linalg.eig( np.eye(nnodes) - np.linalg.inv(QI).dot(Q)) norm[0, i] = np.linalg.norm( np.eye(nnodes) - np.linalg.inv(QI).dot(Q), np.inf) specrad[0, i] = np.linalg.norm(evals, np.inf) print("Spectral radius of infinitely fast wave case > 1.0 for M=%2i" % nodes_v[np.argmax(specrad[0, :] > 1.0)]) print("Spectral radius of > 1.0 for M=%2i" % nodes_v[np.argmax(specrad[1, :] > 1.0)]) return nodes_v, problem_params['lambda_f'], specrad, norm
def compute_stability(): """ Routine to compute the stability domains of different configurations of fwsw-SDC Returns: numpy.ndarray: lambda_slow numpy.ndarray: lambda_fast int: number of collocation nodes int: number of iterations numpy.ndarray: stability numbers """ N_s = 100 N_f = 400 lam_s_max = 5.0 lam_f_max = 12.0 lambda_s = 1j * np.linspace(0.0, lam_s_max, N_s) lambda_f = 1j * np.linspace(0.0, lam_f_max, N_f) problem_params = dict() # SET VALUE FOR lambda_slow AND VALUES FOR lambda_fast ### problem_params['lambda_s'] = np.array([0.0]) problem_params['lambda_f'] = np.array([0.0]) problem_params['u0'] = 1.0 # initialize sweeper parameters sweeper_params = dict() # SET TYPE AND NUMBER OF QUADRATURE NODES ### sweeper_params['collocation_class'] = CollGaussRadau_Right sweeper_params['num_nodes'] = 3 sweeper_params['do_coll_update'] = True # initialize level parameters level_params = dict() level_params['dt'] = 1.0 # fill description dictionary for easy step instantiation description = dict() description['problem_class'] = swfw_scalar # pass problem class description['problem_params'] = problem_params # pass problem parameters description['sweeper_class'] = imex_1st_order # pass sweeper (see part B) description['sweeper_params'] = sweeper_params # pass sweeper parameters description['level_params'] = level_params # pass level parameters description['step_params'] = dict() # pass step parameters # SET NUMBER OF ITERATIONS - SET K=0 FOR COLLOCATION SOLUTION ### K = 3 # now the description contains more or less everything we need to create a step S = step(description=description) L = S.levels[0] Q = L.sweep.coll.Qmat[1:, 1:] nnodes = L.sweep.coll.num_nodes dt = L.params.dt stab = np.zeros((N_f, N_s), dtype='complex') for i in range(0, N_s): for j in range(0, N_f): lambda_fast = lambda_f[j] lambda_slow = lambda_s[i] if K is not 0: lambdas = [lambda_fast, lambda_slow] # LHS, RHS = L.sweep.get_scalar_problems_sweeper_mats(lambdas=lambdas) Mat_sweep = L.sweep.get_scalar_problems_manysweep_mat(nsweeps=K, lambdas=lambdas) else: # Compute stability function of collocation solution Mat_sweep = np.linalg.inv(np.eye(nnodes) - dt * (lambda_fast + lambda_slow) * Q) if L.sweep.params.do_coll_update: stab_fh = 1.0 + (lambda_fast + lambda_slow) * L.sweep.coll.weights.dot( Mat_sweep.dot(np.ones(nnodes))) else: q = np.zeros(nnodes) q[nnodes - 1] = 1.0 stab_fh = q.dot(Mat_sweep.dot(np.ones(nnodes))) stab[j, i] = stab_fh return lambda_s, lambda_f, sweeper_params['num_nodes'], K, stab
def compute_stab_vs_k(slow_resolved): """ Routine to compute modulus of the stability function Args: slow_resolved (bool): switch to compute lambda_slow = 1 or lambda_slow = 4 Returns: numpy.ndarray: number of nodes numpy.ndarray: number of iterations numpy.ndarray: moduli """ mvals = [2, 3, 4] kvals = np.arange(1, 10) lambda_fast = 10j # PLOT EITHER FOR lambda_slow = 1 (resolved) OR lambda_slow = 4 (unresolved) if slow_resolved: lambda_slow = 1j else: lambda_slow = 4j stabval = np.zeros((np.size(mvals), np.size(kvals))) problem_params = dict() # SET VALUE FOR lambda_slow AND VALUES FOR lambda_fast ### problem_params['lambda_s'] = np.array([0.0]) problem_params['lambda_f'] = np.array([0.0]) problem_params['u0'] = 1.0 # initialize sweeper parameters sweeper_params = dict() # SET TYPE AND NUMBER OF QUADRATURE NODES ### sweeper_params['collocation_class'] = CollGaussRadau_Right sweeper_params['do_coll_update'] = True # initialize level parameters level_params = dict() level_params['dt'] = 1.0 # fill description dictionary for easy step instantiation description = dict() description['problem_class'] = swfw_scalar # pass problem class description['problem_params'] = problem_params # pass problem parameters description['sweeper_class'] = imex_1st_order # pass sweeper (see part B) description['level_params'] = level_params # pass level parameters description['step_params'] = dict() # pass step parameters for i in range(0, np.size(mvals)): sweeper_params['num_nodes'] = mvals[i] description[ 'sweeper_params'] = sweeper_params # pass sweeper parameters # now the description contains more or less everything we need to create a step S = step(description=description) L = S.levels[0] nnodes = L.sweep.coll.num_nodes for k in range(0, np.size(kvals)): Kmax = kvals[k] Mat_sweep = L.sweep.get_scalar_problems_manysweep_mat( nsweeps=Kmax, lambdas=[lambda_fast, lambda_slow]) if L.sweep.params.do_coll_update: stab_fh = 1.0 + (lambda_fast + lambda_slow) * L.sweep.coll.weights.dot( Mat_sweep.dot(np.ones(nnodes))) else: q = np.zeros(nnodes) q[nnodes - 1] = 1.0 stab_fh = q.dot(Mat_sweep.dot(np.ones(nnodes))) stabval[i, k] = np.absolute(stab_fh) return mvals, kvals, stabval
def compute_and_plot_dispersion(): problem_params = dict() # SET VALUE FOR lambda_slow AND VALUES FOR lambda_fast ### problem_params['lambda_s'] = np.array([0.0]) problem_params['lambda_f'] = np.array([0.0]) problem_params['u0'] = 1.0 # initialize sweeper parameters sweeper_params = dict() # SET TYPE AND NUMBER OF QUADRATURE NODES ### sweeper_params['collocation_class'] = CollGaussRadau_Right sweeper_params['do_coll_update'] = True sweeper_params['num_nodes'] = 3 # initialize level parameters level_params = dict() level_params['dt'] = 1.0 # fill description dictionary for easy step instantiation description = dict() description['problem_class'] = swfw_scalar # pass problem class description['problem_params'] = problem_params # pass problem parameters description['dtype_u'] = mesh # pass data type for u description['dtype_f'] = rhs_imex_mesh # pass data type for f description['sweeper_class'] = imex_1st_order # pass sweeper description['sweeper_params'] = sweeper_params # pass sweeper parameters description['level_params'] = level_params # pass level parameters description['step_params'] = dict() # pass step parameters # SET NUMBER OF ITERATIONS ### K = 3 # ORDER OF DIRK/IMEX IS EQUAL TO NUMBER OF ITERATIONS AND THUS ORDER OF SDC ### dirk_order = K c_speed = 1.0 U_speed = 0.05 # now the description contains more or less everything we need to create a step S = step(description=description) L = S.levels[0] # u0 = S.levels[0].prob.u_exact(t0) # S.init_step(u0) QE = L.sweep.QE[1:, 1:] QI = L.sweep.QI[1:, 1:] Q = L.sweep.coll.Qmat[1:, 1:] nnodes = L.sweep.coll.num_nodes dt = L.params.dt Nsamples = 15 k_vec = np.linspace(0, np.pi, Nsamples + 1, endpoint=False) k_vec = k_vec[1:] phase = np.zeros((3, Nsamples)) amp_factor = np.zeros((3, Nsamples)) for i in range(0, np.size(k_vec)): Cs = -1j * k_vec[i] * np.array([[0.0, c_speed], [c_speed, 0.0]], dtype='complex') Uadv = -1j * k_vec[i] * np.array([[U_speed, 0.0], [0.0, U_speed]], dtype='complex') LHS = np.eye(2 * nnodes) - dt * (np.kron(QI, Cs) + np.kron(QE, Uadv)) RHS = dt * (np.kron(Q, Uadv + Cs) - np.kron(QI, Cs) - np.kron(QE, Uadv)) LHSinv = np.linalg.inv(LHS) Mat_sweep = np.linalg.matrix_power(LHSinv.dot(RHS), K) for k in range(0, K): Mat_sweep = Mat_sweep + np.linalg.matrix_power(LHSinv.dot(RHS), k).dot(LHSinv) ## # ---> The update formula for this case need verification!! update = dt * np.kron(L.sweep.coll.weights, Uadv + Cs) y1 = np.array([1, 0], dtype='complex') y2 = np.array([0, 1], dtype='complex') e1 = np.kron(np.ones(nnodes), y1) stab_fh_1 = y1 + update.dot(Mat_sweep.dot(e1)) e2 = np.kron(np.ones(nnodes), y2) stab_fh_2 = y2 + update.dot(Mat_sweep.dot(e2)) stab_sdc = np.column_stack((stab_fh_1, stab_fh_2)) # Stability function of backward Euler is 1/(1-z); system is y' = (Cs+Uadv)*y # stab_ie = np.linalg.inv( np.eye(2) - step.status.dt*(Cs+Uadv) ) # For testing, insert exact stability function exp(-dt*i*k*(Cs+Uadv) # stab_fh = la.expm(Cs+Uadv) dirkts = dirk(Cs + Uadv, dirk_order) stab_fh1 = dirkts.timestep(y1, 1.0) stab_fh2 = dirkts.timestep(y2, 1.0) stab_dirk = np.column_stack((stab_fh1, stab_fh2)) rkimex = rk_imex(M_fast=Cs, M_slow=Uadv, order=K) stab_fh1 = rkimex.timestep(y1, 1.0) stab_fh2 = rkimex.timestep(y2, 1.0) stab_rk_imex = np.column_stack((stab_fh1, stab_fh2)) sol_sdc = findomega(stab_sdc) sol_dirk = findomega(stab_dirk) sol_rk_imex = findomega(stab_rk_imex) # Now solve for discrete phase phase[0, i] = sol_sdc.real / k_vec[i] amp_factor[0, i] = np.exp(sol_sdc.imag) phase[1, i] = sol_dirk.real / k_vec[i] amp_factor[1, i] = np.exp(sol_dirk.imag) phase[2, i] = sol_rk_imex.real / k_vec[i] amp_factor[2, i] = np.exp(sol_rk_imex.imag) rcParams['figure.figsize'] = 1.5, 1.5 fs = 8 fig = plt.figure() plt.plot(k_vec, (U_speed + c_speed) + np.zeros(np.size(k_vec)), '--', color='k', linewidth=1.5, label='Exact') plt.plot(k_vec, phase[1, :], '-', color='g', linewidth=1.5, label='DIRK(' + str(dirkts.order) + ')') plt.plot(k_vec, phase[2, :], '-+', color='r', linewidth=1.5, label='IMEX(' + str(rkimex.order) + ')', markevery=(2, 3), mew=1.0) plt.plot(k_vec, phase[0, :], '-o', color='b', linewidth=1.5, label='SDC(' + str(K) + ')', markevery=(1, 3), markersize=fs / 2) plt.xlabel('Wave number', fontsize=fs, labelpad=0.25) plt.ylabel('Phase speed', fontsize=fs, labelpad=0.5) plt.xlim([k_vec[0], k_vec[-1:]]) plt.ylim([0.0, 1.1 * (U_speed + c_speed)]) fig.gca().tick_params(axis='both', labelsize=fs) plt.legend(loc='lower left', fontsize=fs, prop={'size': fs - 2}) plt.xticks([0, 1, 2, 3], fontsize=fs) filename = 'data/phase-K' + str(K) + '-M' + str( sweeper_params['num_nodes']) + '.png' plt.gcf().savefig(filename, bbox_inches='tight') fig = plt.figure() plt.plot(k_vec, 1.0 + np.zeros(np.size(k_vec)), '--', color='k', linewidth=1.5, label='Exact') plt.plot(k_vec, amp_factor[1, :], '-', color='g', linewidth=1.5, label='DIRK(' + str(dirkts.order) + ')') plt.plot(k_vec, amp_factor[2, :], '-+', color='r', linewidth=1.5, label='IMEX(' + str(rkimex.order) + ')', markevery=(2, 3), mew=1.0) plt.plot(k_vec, amp_factor[0, :], '-o', color='b', linewidth=1.5, label='SDC(' + str(K) + ')', markevery=(1, 3), markersize=fs / 2) plt.xlabel('Wave number', fontsize=fs, labelpad=0.25) plt.ylabel('Amplification factor', fontsize=fs, labelpad=0.5) fig.gca().tick_params(axis='both', labelsize=fs) plt.xlim([k_vec[0], k_vec[-1:]]) plt.ylim([k_vec[0], k_vec[-1:]]) plt.legend(loc='lower left', fontsize=fs, prop={'size': fs - 2}) plt.gca().set_ylim([0.0, 1.1]) plt.xticks([0, 1, 2, 3], fontsize=fs) filename = 'data/ampfactor-K' + str(K) + '-M' + str( sweeper_params['num_nodes']) + '.png' plt.gcf().savefig(filename, bbox_inches='tight')