def _init_operators(self, current_stage="online"): # The difference between this method and the parent one is that non-hierarchical affine # expansion storage is requested during the online stage and during the postprocessing # of the offline stage. if current_stage == "online": # Initialize all terms using a non-hierarchical affine expansion storage, and then loading from file for term in self.terms: self.operator[term] = OnlineNonHierarchicalAffineExpansionStorage(0) # it will be resized by assemble_operator self.assemble_operator(term, "online") self.Q[term] = len(self.operator[term]) # Initialize additional reduced operator related to vanishing viscosity self.operator["vanishing_viscosity"] = OnlineNonHierarchicalAffineExpansionStorage(1) self.assemble_operator("vanishing_viscosity", "online") elif current_stage == "offline": # Call Parent EllipticCoerciveReducedProblem_DerivedClass._init_operators(self, current_stage) elif current_stage == "offline_vanishing_viscosity_postprocessing": # Initialize additional truth operators self.truth_problem.operator["k"] = AffineExpansionStorage(self.truth_problem.assemble_operator("k")) self.truth_problem.operator["m"] = AffineExpansionStorage(self.truth_problem.assemble_operator("m")) # Initialize all terms using a non-hierarchical affine expansion storage for term in self.terms: self.Q[term] = self.truth_problem.Q[term] self.operator[term] = OnlineNonHierarchicalAffineExpansionStorage(self.Q[term]) # Initialize additional reduced operator related to vanishing viscosity self.operator["vanishing_viscosity"] = OnlineNonHierarchicalAffineExpansionStorage(1) else: # Call Parent, which may eventually raise an error EllipticCoerciveReducedProblem_DerivedClass._init_operators(self, current_stage)
def _init_inner_products(self, current_stage="online"): # The difference between this method and the parent one is that non-hierarchical affine # expansion storage is requested during the online stage and during the postprocessing # of the offline stage. if current_stage == "online": self.inner_product = OnlineNonHierarchicalAffineExpansionStorage( 1) self.projection_inner_product = OnlineNonHierarchicalAffineExpansionStorage( 1) self.assemble_operator("inner_product", "online") self.assemble_operator("projection_inner_product", "online") self._disable_inner_product_combination = True elif current_stage == "offline": EllipticCoerciveReducedProblem_DerivedClass._init_inner_products( self, current_stage) elif current_stage == "offline_vanishing_viscosity_postprocessing": self.inner_product = OnlineNonHierarchicalAffineExpansionStorage( 1) self.projection_inner_product = OnlineNonHierarchicalAffineExpansionStorage( 1) self._disable_inner_product_combination = True else: # Call Parent, which may eventually raise an error EllipticCoerciveReducedProblem_DerivedClass._init_inner_products( self, current_stage)
def _init_operators(self, current_stage="online"): # Initialize additional reduced operators related to rectification. Note that these operators # are not hierarchical because: # * the basis is possibly non-hierarchical # * the coefficients of the reduced solution for different reduced sizes are definitely not hierarchical if current_stage == "online": self.operator[ "projection_truth_snapshots"] = OnlineNonHierarchicalAffineExpansionStorage( 1) assert len(self.online_solve_kwargs_with_rectification) is len( self.online_solve_kwargs_without_rectification) self.operator[ "projection_reduced_snapshots"] = OnlineNonHierarchicalAffineExpansionStorage( len(self.online_solve_kwargs_with_rectification)) self.assemble_operator("projection_truth_snapshots", "online") self.assemble_operator("projection_reduced_snapshots", "online") # Call Parent EllipticCoerciveReducedProblem_DerivedClass._init_operators( self, current_stage) elif current_stage == "offline": # Call Parent EllipticCoerciveReducedProblem_DerivedClass._init_operators( self, current_stage) elif current_stage == "offline_rectification_postprocessing": self.operator[ "projection_truth_snapshots"] = OnlineNonHierarchicalAffineExpansionStorage( 1) assert len(self.online_solve_kwargs_with_rectification) is len( self.online_solve_kwargs_without_rectification) self.operator[ "projection_reduced_snapshots"] = OnlineNonHierarchicalAffineExpansionStorage( len(self.online_solve_kwargs_with_rectification)) # We do not call Parent method as there is no need to re-initialize offline operators else: # Call Parent, which may eventually raise an error EllipticCoerciveReducedProblem_DerivedClass._init_operators( self, current_stage)
class OnlineVanishingViscosityDecoratedReducedProblem_Class(EllipticCoerciveReducedProblem_DerivedClass): def __init__(self, truth_problem, **kwargs): # Call to parent EllipticCoerciveReducedProblem_DerivedClass.__init__(self, truth_problem, **kwargs) # Store vanishing viscosity data self._viscosity = truth_problem._viscosity self._N_threshold_min = truth_problem._N_threshold_min self._N_threshold_max = truth_problem._N_threshold_max # Temporary storage for vanishing viscosity eigenvalues self.vanishing_viscosity_eigenvalues = list() # Default values for keyword arguments in solve self._online_solve_default_kwargs = OrderedDict() self._online_solve_default_kwargs["online_stabilization"] = False self._online_solve_default_kwargs["online_vanishing_viscosity"] = True self.OnlineSolveKwargs = OnlineSolveKwargsGenerator(**self._online_solve_default_kwargs) # Flag to disable inner product combination after vanishing viscosity operator has been setup self._disable_inner_product_combination = False # Flag to disable error estimation after vanishing viscosity operator has been setup self._disable_error_estimation = False def _init_operators(self, current_stage="online"): # The difference between this method and the parent one is that non-hierarchical affine # expansion storage is requested during the online stage and during the postprocessing # of the offline stage. if current_stage == "online": # Initialize all terms using a non-hierarchical affine expansion storage, and then loading from file for term in self.terms: self.operator[term] = OnlineNonHierarchicalAffineExpansionStorage(0) # it will be resized by assemble_operator self.assemble_operator(term, "online") self.Q[term] = len(self.operator[term]) # Initialize additional reduced operator related to vanishing viscosity self.operator["vanishing_viscosity"] = OnlineNonHierarchicalAffineExpansionStorage(1) self.assemble_operator("vanishing_viscosity", "online") elif current_stage == "offline": # Call Parent EllipticCoerciveReducedProblem_DerivedClass._init_operators(self, current_stage) elif current_stage == "offline_vanishing_viscosity_postprocessing": # Initialize additional truth operators self.truth_problem.operator["k"] = AffineExpansionStorage(self.truth_problem.assemble_operator("k")) self.truth_problem.operator["m"] = AffineExpansionStorage(self.truth_problem.assemble_operator("m")) # Initialize all terms using a non-hierarchical affine expansion storage for term in self.terms: self.Q[term] = self.truth_problem.Q[term] self.operator[term] = OnlineNonHierarchicalAffineExpansionStorage(self.Q[term]) # Initialize additional reduced operator related to vanishing viscosity self.operator["vanishing_viscosity"] = OnlineNonHierarchicalAffineExpansionStorage(1) else: # Call Parent, which may eventually raise an error EllipticCoerciveReducedProblem_DerivedClass._init_operators(self, current_stage) def _init_inner_products(self, current_stage="online"): # The difference between this method and the parent one is that non-hierarchical affine # expansion storage is requested during the online stage and during the postprocessing # of the offline stage. if current_stage == "online": self.inner_product = OnlineNonHierarchicalAffineExpansionStorage(1) self.projection_inner_product = OnlineNonHierarchicalAffineExpansionStorage(1) self.assemble_operator("inner_product", "online") self.assemble_operator("projection_inner_product", "online") self._disable_inner_product_combination = True elif current_stage == "offline": EllipticCoerciveReducedProblem_DerivedClass._init_inner_products(self, current_stage) elif current_stage == "offline_vanishing_viscosity_postprocessing": self.inner_product = OnlineNonHierarchicalAffineExpansionStorage(1) self.projection_inner_product = OnlineNonHierarchicalAffineExpansionStorage(1) self._disable_inner_product_combination = True else: # Call Parent, which may eventually raise an error EllipticCoerciveReducedProblem_DerivedClass._init_inner_products(self, current_stage) def _combine_all_inner_products(self): if self._disable_inner_product_combination: return NotImplemented else: return EllipticCoerciveReducedProblem_DerivedClass._combine_all_inner_products(self) def _combine_all_projection_inner_products(self): if self._disable_inner_product_combination: return NotImplemented else: return EllipticCoerciveReducedProblem_DerivedClass._combine_all_projection_inner_products(self) def _init_basis_functions(self, current_stage="online"): if current_stage == "online": if self.basis_functions is None: # avoid re-initializing basis functions matrix multiple times self.basis_functions = NonHierarchicalBasisFunctionsMatrix(self.truth_problem.V) self.basis_functions.init(self.truth_problem.components) basis_functions_loaded = self.basis_functions.load(self.folder["basis"], "basis") if basis_functions_loaded: self.N = len(self.basis_functions) self.N_bc = 0 # TODO handle inhomogeneous bcs elif current_stage == "offline": EllipticCoerciveReducedProblem_DerivedClass._init_basis_functions(self, current_stage) elif current_stage == "offline_vanishing_viscosity_postprocessing": self.basis_functions = NonHierarchicalBasisFunctionsMatrix(self.truth_problem.V) self.basis_functions.init(self.truth_problem.components) else: # Call Parent, which may eventually raise an error EllipticCoerciveReducedProblem_DerivedClass._init_basis_functions(self, current_stage) def _init_error_estimation_operators(self, current_stage="online"): if current_stage in ("online", "offline_vanishing_viscosity_postprocessing"): # Disable error estimation, which would not take into account the additional vanishing viscosity operator self._disable_error_estimation = True elif current_stage == "offline": # Call Parent EllipticCoerciveReducedProblem_DerivedClass._init_error_estimation_operators(self, current_stage) else: # Call Parent, which may eventually raise an error EllipticCoerciveReducedProblem_DerivedClass._init_error_estimation_operators(self, current_stage) def build_reduced_operators(self, current_stage="offline"): if current_stage == "offline_vanishing_viscosity_postprocessing": EllipticCoerciveReducedProblem_DerivedClass.build_reduced_operators(self, "offline_vanishing_viscosity_postprocessing") # Compute vanishing viscosity reduced operator print("build vanishing viscosity reduced operator") self.operator["vanishing_viscosity"] = self.assemble_operator("vanishing_viscosity", "offline_vanishing_viscosity_postprocessing") else: # Call Parent, which may eventually raise an error EllipticCoerciveReducedProblem_DerivedClass.build_reduced_operators(self, current_stage) def assemble_operator(self, term, current_stage="online"): if term == "vanishing_viscosity": assert current_stage in ("online", "offline_vanishing_viscosity_postprocessing") if current_stage == "online": # load from file self.operator["vanishing_viscosity"].load(self.folder["reduced_operators"], "operator_vanishing_viscosity") return self.operator["vanishing_viscosity"] elif current_stage == "offline_vanishing_viscosity_postprocessing": assert len(self.vanishing_viscosity_eigenvalues) is self.N assert all([len(vanishing_viscosity_eigenvalues_n) is n + 1 for (n, vanishing_viscosity_eigenvalues_n) in enumerate(self.vanishing_viscosity_eigenvalues)]) print("build reduced vanishing viscosity operator") for n in range(1, self.N + 1): vanishing_viscosity_expansion = OnlineAffineExpansionStorage(1) vanishing_viscosity_eigenvalues = self.vanishing_viscosity_eigenvalues[n - 1] vanishing_viscosity_operator = OnlineMatrix(n, n) n_min = int(n*self._N_threshold_min) n_max = int(n*self._N_threshold_max) lambda_n_min = vanishing_viscosity_eigenvalues[n_min] lambda_n_max = vanishing_viscosity_eigenvalues[n_max] for i in range(n): lambda_i = vanishing_viscosity_eigenvalues[i] if i < n_min: viscosity_i = 0. elif i < n_max: viscosity_i = ( self._viscosity * (lambda_i - lambda_n_min)**2/(lambda_n_max - lambda_n_min)**3 * (2*lambda_n_max**2 - (lambda_n_min + lambda_n_max)*lambda_i) ) else: viscosity_i = self._viscosity*lambda_i vanishing_viscosity_operator[i, i] = viscosity_i*lambda_i vanishing_viscosity_expansion[0] = vanishing_viscosity_operator self.operator["vanishing_viscosity"][:n, :n] = vanishing_viscosity_expansion # Save to file self.operator["vanishing_viscosity"].save(self.folder["reduced_operators"], "operator_vanishing_viscosity") return self.operator["vanishing_viscosity"] else: raise ValueError("Invalid stage in assemble_operator().") else: if current_stage == "offline_vanishing_viscosity_postprocessing": if term in self.terms: for n in range(1, self.N + 1): assert self.Q[term] == self.truth_problem.Q[term] term_expansion = OnlineAffineExpansionStorage(self.Q[term]) assert self.terms_order[term] in (1, 2) if self.terms_order[term] == 2: for q in range(self.Q[term]): term_expansion[q] = transpose(self.basis_functions[:n])*self.truth_problem.operator[term][q]*self.basis_functions[:n] self.operator[term][:n, :n] = term_expansion elif self.terms_order[term] == 1: for q in range(self.Q[term]): term_expansion[q] = transpose(self.basis_functions[:n])*self.truth_problem.operator[term][q] self.operator[term][:n] = term_expansion else: raise ValueError("Invalid value for order of term " + term) self.operator[term].save(self.folder["reduced_operators"], "operator_" + term) return self.operator[term] elif term.startswith("inner_product"): assert len(self.inner_product) == 1 # the affine expansion storage contains only the inner product matrix assert len(self.truth_problem.inner_product) == 1 # the affine expansion storage contains only the inner product matrix for n in range(1, self.N + 1): inner_product_expansion = OnlineAffineExpansionStorage(1) inner_product_expansion[0] = transpose(self.basis_functions[:n])*self.truth_problem.inner_product[0]*self.basis_functions[:n] self.inner_product[:n, :n] = inner_product_expansion self.inner_product.save(self.folder["reduced_operators"], term) return self.inner_product elif term.startswith("projection_inner_product"): assert len(self.projection_inner_product) == 1 # the affine expansion storage contains only the inner product matrix assert len(self.truth_problem.projection_inner_product) == 1 # the affine expansion storage contains only the inner product matrix for n in range(1, self.N + 1): projection_inner_product_expansion = OnlineAffineExpansionStorage(1) projection_inner_product_expansion[0] = transpose(self.basis_functions[:n])*self.truth_problem.projection_inner_product[0]*self.basis_functions[:n] self.projection_inner_product[:n, :n] = projection_inner_product_expansion self.projection_inner_product.save(self.folder["reduced_operators"], term) return self.projection_inner_product elif term.startswith("dirichlet_bc"): raise ValueError("There should be no need to assemble Dirichlet BCs when querying the offline vanishing viscosity postprocessing stage.") else: raise ValueError("Invalid term for assemble_operator().") else: return EllipticCoerciveReducedProblem_DerivedClass.assemble_operator(self, term, current_stage) def _online_size_from_kwargs(self, N, **kwargs): N, kwargs = EllipticCoerciveReducedProblem_DerivedClass._online_size_from_kwargs(self, N, **kwargs) kwargs = self.OnlineSolveKwargs(**kwargs) return N, kwargs def _solve(self, N, **kwargs): # Temporarily change value of stabilized attribute in truth problem bak_stabilized = self.truth_problem.stabilized self.truth_problem.stabilized = kwargs["online_stabilization"] # Solve reduced problem if kwargs["online_vanishing_viscosity"]: assembled_operator = dict() assembled_operator["a"] = ( sum(product(self.compute_theta("a"), self.operator["a"][:N, :N])) + self.operator["vanishing_viscosity"][:N, :N][0] ) assembled_operator["f"] = sum(product(self.compute_theta("f"), self.operator["f"][:N])) self._solution = OnlineFunction(N) solver = LinearSolver(assembled_operator["a"], self._solution, assembled_operator["f"]) solver.set_parameters(self._linear_solver_parameters) solver.solve() else: EllipticCoerciveReducedProblem_DerivedClass._solve(self, N, **kwargs) # Restore original value of stabilized attribute in truth problem self.truth_problem.stabilized = bak_stabilized def estimate_error(self): if self._disable_error_estimation: return NotImplemented else: return EllipticCoerciveReducedProblem_DerivedClass.estimate_error(self)