def crank_nicolson( model: Model, gfu_0: List[ngs.GridFunction], U: List[ProxyFunction], V: List[ProxyFunction], dt: List[ngs.Parameter]) -> Tuple[ngs.BilinearForm, ngs.LinearForm]: """ Crank Nicolson (trapezoidal rule) time integration scheme. This function constructs the final bilinear and linear forms for the time integration scheme by adding the necessary time-dependent terms to the model's stationary terms. The returned bilinear and linear forms have NOT been assembled. Args: model: The model to solve. gfu_0: List of the solutions of previous time steps ordered from most recent to oldest. U: List of trial functions for the model. V: List of test (weighting) functions. dt: List of timestep sizes ordered from most recent to oldest. Returns: a: The final bilinear form (as a ngs.BilinearForm but not assembled). L: The final linear form (as a ngs.LinearForm but not assembled). """ # Separate out the various components of each gridfunction solution to a previous timestep. # gfu_lst = [[component 0, component 1...] at t^n, [component 0, component 1...] at t^n-1, ...] gfu_lst = [] for i in range(len(gfu_0)): if (len(gfu_0[i].components) > 0): gfu_lst.append([ gfu_0[i].components[j] for j in range(len(gfu_0[i].components)) ]) else: gfu_lst.append([gfu_0[i]]) # Construct the bilinear form a = ngs.BilinearForm(model.fes) a += 0.5 * model.construct_bilinear(U, V, dt[0]) # Construct the linear form L = ngs.LinearForm(model.fes) L += model.construct_linear(V, gfu_lst[0], dt[0]) L += -0.5 * model.construct_bilinear(gfu_lst[0], V, dt[0], True) a_dt, L_dt = model.time_derivative_terms(gfu_lst, 'crank nicolson') # When adding the time discretization term, multiply by the phase field if using the diffuse interface method. if model.DIM: a_dt *= model.DIM_solver.phi_gfu L_dt *= model.DIM_solver.phi_gfu a += a_dt * dx L += L_dt * dx return a, L
def adaptive_IMEX_pred( model: Model, gfu_0: List[ngs.GridFunction], U: List[ProxyFunction], V: List[ProxyFunction], dt: List[ngs.Parameter]) -> Tuple[ngs.BilinearForm, ngs.LinearForm]: """ Predictor for the adaptive time-stepping IMEX time integration scheme. This function constructs the final bilinear and linear forms for the time integration scheme by adding the necessary time-dependent terms to the model's stationary terms. The returned bilinear and linear forms have NOT been assembled. Args: model: The model to solve. gfu_0: List of the solutions of previous time steps ordered from most recent to oldest. U: List of trial functions for the model. V: List of test (weighting) functions. dt: List of timestep sizes ordered from most recent to oldest. Returns: a: The final bilinear form (as a ngs.BilinearForm but not assembled). L: The final linear form (as a ngs.LinearForm but not assembled). """ # Separate out the various components of each gridfunction solution to a previous timestep. # gfu_lst = [[component 0, component 1...] at t^n, [component 0, component 1...] at t^n-1, ...] gfu_lst = [] for i in range(len(gfu_0)): if (len(gfu_0[i].components) > 0): gfu_lst.append([ gfu_0[i].components[j] for j in range(len(gfu_0[i].components)) ]) else: gfu_lst.append([gfu_0[i]]) # Operators specific to this time integration scheme. w = dt[0] / dt[1] E = model.construct_gfu().components[0] E_expr = (1.0 + w) * gfu_lst[0][0] - w * gfu_lst[1][0] E.Set(E_expr) # Construct the bilinear form a = ngs.BilinearForm(model.fes) a += model.construct_bilinear(U, V, dt[0]) # Construct the linear form L = ngs.LinearForm(model.fes) L += model.construct_linear( V, [gfu_lst[0][0], 0.0], dt[0]) # TODO: Why doesn't using E work? Numerical error? a_dt, L_dt = model.time_derivative_terms(gfu_lst, 'crank nicolson') # When adding the time discretization term, multiply by the phase field if using the diffuse interface method. if model.DIM: a_dt *= model.DIM_solver.phi_gfu L_dt *= model.DIM_solver.phi_gfu a += a_dt * dx L += L_dt * dx return a, L