def solve(self): print("solving mock reduced problem at mu =", self.mu) assert not hasattr(self, "_is_solving") self._is_solving = True f = self.truth_problem.solve() f_N = transpose( self.basis_functions) * self.truth_problem.inner_product * f # Return the reduced solution self._solution = OnlineFunction(f_N) delattr(self, "_is_solving") return self._solution
def _compute_output(self, dual_N): primal_solution = self.primal_reduced_problem._solution primal_N = primal_solution.N dual_solution = self._solution assembled_output_correction_and_estimation_operator = dict() assembled_output_correction_and_estimation_operator["a"] = sum( product( self.primal_reduced_problem.compute_theta("a"), self.output_correction_and_estimation["a"] [:dual_N, :primal_N])) assembled_output_correction_and_estimation_operator["f"] = sum( product(self.primal_reduced_problem.compute_theta("f"), self.output_correction_and_estimation["f"][:dual_N])) self._output = transpose( dual_solution ) * assembled_output_correction_and_estimation_operator[ "f"] - transpose( dual_solution ) * assembled_output_correction_and_estimation_operator[ "a"] * primal_solution
def get_residual_norm_squared(self): residual_norm_squared_over_time = TimeSeries( self._solution_over_time) assert len(self._solution_over_time) == len( self._solution_dot_over_time) for (k, t) in enumerate(self._solution_over_time.stored_times()): if not isclose(t, self.t0, self.dt / 2.): # Set current time self.set_time(t) # Set current solution and solution_dot assign(self._solution, self._solution_over_time[k]) assign(self._solution_dot, self._solution_dot_over_time[k]) # Compute the numerator of the error bound at the current time, first # by computing residual of elliptic part elliptic_residual_norm_squared = AbstractParabolicRBReducedProblem_Base.get_residual_norm_squared( self) # ... and then adding terms related to time derivative N = self._solution.N theta_m = self.compute_theta("m") theta_a = self.compute_theta("a") theta_f = self.compute_theta("f") residual_norm_squared_over_time.append( elliptic_residual_norm_squared + 2.0 * (transpose(self._solution_dot) * sum( product( theta_m, self.error_estimation_operator[ "m", "f"][:N], theta_f))) + 2.0 * (transpose(self._solution_dot) * sum( product( theta_m, self.error_estimation_operator[ "m", "a"][:N, :N], theta_a)) * self._solution) + (transpose(self._solution_dot) * sum( product( theta_m, self.error_estimation_operator[ "m", "m"][:N, :N], theta_m)) * self._solution_dot)) else: # Error estimator on initial condition does not use the residual residual_norm_squared_over_time.append(0.) return residual_norm_squared_over_time
def __add__(self, other): from rbnics.backends import transpose self_args = self._arg._args other_args = other._arg._args assert len(self_args) in (2, 3) assert len(self_args) is len(other_args) assert isinstance(self_args[0], AbstractBasisFunctionsMatrix) assert isinstance(other_args[0], AbstractBasisFunctionsMatrix) assert self_args[0] is other_args[0] assert isinstance(self_args[1], AbstractParametrizedTensorFactory) assert isinstance(other_args[1], AbstractParametrizedTensorFactory) if len(self_args) is 2: output = transpose( self_args[0]) * (self_args[1] + other_args[1]) elif len(self_args) is 3: assert isinstance(self_args[2], AbstractBasisFunctionsMatrix) assert isinstance(other_args[2], AbstractBasisFunctionsMatrix) assert self_args[2] is other_args[2] output = transpose(self_args[0]) * ( self_args[1] + other_args[1]) * self_args[2] else: raise ValueError("Invalid argument") assert isinstance(output, DelayedTranspose) return DelayedTransposeWithArithmetic_Class(output)
def _compute_output(self, N): assembled_operator = dict() for term in ("m", "n", "g", "h"): assert self.terms_order[term] in (0, 1, 2) if self.terms_order[term] == 2: assembled_operator[term] = sum( product(self.compute_theta(term), self.operator[term][:N, :N])) elif self.terms_order[term] == 1: assembled_operator[term] = sum( product(self.compute_theta(term), self.operator[term][:N])) elif self.terms_order[term] == 0: assembled_operator[term] = sum( product(self.compute_theta(term), self.operator[term])) else: raise ValueError("Invalid value for order of term " + term) self._output = ( 0.5 * (transpose(self._solution) * assembled_operator["m"] * self._solution) + 0.5 * (transpose(self._solution) * assembled_operator["n"] * self._solution) - transpose(assembled_operator["g"]) * self._solution + 0.5 * assembled_operator["h"])
def get_residual_norm_squared(self): residual_norm_squared_over_time = list() # of numbers for (k, (solution, solution_dot)) in enumerate( zip(self._solution_over_time, self._solution_dot_over_time)): if k > 0: # Set current time self.set_time(k * self.dt) # Set current solution and solution_dot self._solution = solution self._solution_dot = solution_dot # Compute the numerator of the error bound at the current time, first # by computing residual of elliptic part elliptic_residual_norm_squared = ParabolicCoerciveRBReducedProblem_Base.get_residual_norm_squared( self) # ... and then adding terms related to time derivative N = self._solution.N theta_m = self.compute_theta("m") theta_a = self.compute_theta("a") theta_f = self.compute_theta("f") residual_norm_squared_over_time.append( elliptic_residual_norm_squared + 2.0 * (transpose(self._solution_dot) * sum( product(theta_m, self.error_estimation_operator[ "m", "f"][:N], theta_f))) + 2.0 * (transpose(self._solution_dot) * sum( product( theta_m, self.error_estimation_operator["m", "a"] [:N, :N], theta_a)) * self._solution) + transpose(self._solution_dot) * sum( product( theta_m, self.error_estimation_operator["m", "m"] [:N, :N], theta_m)) * self._solution_dot) else: # Error estimator on initial condition does not use the residual residual_norm_squared_over_time.append(0.) return residual_norm_squared_over_time
def assemble_operator(self, term, current_stage="online"): assert current_stage in ("online", "offline") if term.startswith("initial_condition"): component = term.replace("initial_condition", "").replace("_", "") if current_stage == "online": # load from file if component != "": initial_condition = self.initial_condition[component] else: initial_condition = self.initial_condition initial_condition.load(self.folder["reduced_operators"], term) elif current_stage == "offline": if component != "": truth_initial_condition = self.truth_problem.initial_condition[ component] initial_condition = self.initial_condition[component] truth_projection_inner_product = self.truth_problem.projection_inner_product[ component] else: truth_initial_condition = self.truth_problem.initial_condition initial_condition = self.initial_condition truth_projection_inner_product = self.truth_problem.projection_inner_product assert len( truth_projection_inner_product ) == 1 # the affine expansion storage contains only the inner product matrix for (q, truth_initial_condition_q ) in enumerate(truth_initial_condition): initial_condition[q] = transpose( self.basis_functions ) * truth_projection_inner_product[ 0] * truth_initial_condition_q initial_condition.save(self.folder["reduced_operators"], term) else: raise ValueError("Invalid stage in assemble_operator().") # Assign if component != "": assert component in self.components self.initial_condition[component] = initial_condition else: assert len(self.components) == 1 self.initial_condition = initial_condition # Return return initial_condition else: return ParametrizedReducedDifferentialProblem_DerivedClass.assemble_operator( self, term, current_stage)
def get_residual_norm_squared(self): N = self._solution.N theta_a = self.compute_theta("a") theta_b = self.compute_theta("b") theta_bt = self.compute_theta("bt") theta_f = self.compute_theta("f") theta_g = self.compute_theta("g") return ( sum(product(theta_f, self.error_estimation_operator["f", "f"], theta_f)) + sum(product(theta_g, self.error_estimation_operator["g", "g"], theta_g)) + 2.0*(transpose(self._solution)*sum(product(theta_a, self.error_estimation_operator["a", "f"][:N], theta_f))) + 2.0*(transpose(self._solution)*sum(product(theta_bt, self.error_estimation_operator["bt", "f"][:N], theta_f))) + 2.0*(transpose(self._solution)*sum(product(theta_b, self.error_estimation_operator["b", "g"][:N], theta_g))) + transpose(self._solution)*sum(product(theta_a, self.error_estimation_operator["a", "a"][:N, :N], theta_a))*self._solution + 2.0*(transpose(self._solution)*sum(product(theta_a, self.error_estimation_operator["a", "bt"][:N, :N], theta_bt))*self._solution) + transpose(self._solution)*sum(product(theta_bt, self.error_estimation_operator["bt", "bt"][:N, :N], theta_bt))*self._solution + transpose(self._solution)*sum(product(theta_b, self.error_estimation_operator["b", "b"][:N, :N], theta_b))*self._solution )
def _compute_error(self, **kwargs): """ It computes the error of the reduced order approximation with respect to the full order one for the current value of mu. """ (components, inner_product) = self._preprocess_compute_error_and_relative_error_kwargs(**kwargs) # Storage error = dict() # Compute the error on the solution if len(components) > 0: N = self._solution.N reduced_solution = self.basis_functions[:N] * self._solution truth_solution = self.truth_problem._solution error_function = truth_solution - reduced_solution for component in components: error_norm_squared_component = transpose(error_function) * inner_product[component] * error_function assert error_norm_squared_component >= 0. or isclose(error_norm_squared_component, 0.) error[component] = sqrt(abs(error_norm_squared_component)) # Simplify trivial case if len(components) == 1: error = error[components[0]] # return error
def _compute_relative_error(self, absolute_error, **kwargs): """ It computes the relative error of the reduced order approximation with respect to the full order one for the current value of mu. """ (components, inner_product ) = self._preprocess_compute_error_and_relative_error_kwargs(**kwargs) # Handle trivial case from compute_error if len(components) == 1: absolute_error_ = dict() absolute_error_[components[0]] = absolute_error absolute_error = absolute_error_ # Storage relative_error = dict() # Compute the relative error on the solution if len(components) > 0: truth_solution = self.truth_problem._solution for component in components: truth_solution_norm_squared_component = ( transpose(truth_solution) * inner_product[component] * truth_solution) assert truth_solution_norm_squared_component >= 0. or isclose( truth_solution_norm_squared_component, 0.) if truth_solution_norm_squared_component != 0.: relative_error[component] = ( absolute_error[component] / sqrt(abs(truth_solution_norm_squared_component))) else: if absolute_error[component] == 0.: relative_error[component] = 0. else: relative_error[component] = float("NaN") # Simplify trivial case if len(components) == 1: relative_error = relative_error[components[0]] # return relative_error
def assemble_error_estimation_operators(self, term, current_stage="online"): if term[0].startswith("initial_condition") and term[1].startswith("initial_condition"): component0 = term[0].replace("initial_condition", "").replace("_", "") component1 = term[1].replace("initial_condition", "").replace("_", "") assert current_stage in ("online", "offline") if current_stage == "online": # load from file assert (component0 != "") == (component1 != "") if component0 != "": assert component0 in self.components assert component1 in self.components self.initial_condition_product[component0, component1].load(self.folder["error_estimation"], "initial_condition_product_" + component0 + "_" + component1) return self.initial_condition_product[component0, component1] else: assert len(self.components) == 1 self.initial_condition_product.load(self.folder["error_estimation"], "initial_condition_product") return self.initial_condition_product elif current_stage == "offline": inner_product = self.truth_problem._combined_projection_inner_product assert (component0 != "") == (component1 != "") if component0 != "": for q0 in range(self.Q_ic[component0]): for q1 in range(self.Q_ic[component1]): self.initial_condition_product[component0, component1][q0, q1] = transpose(self.truth_problem.initial_condition[component0][q0])*inner_product*self.truth_problem.initial_condition[component1][q1] self.initial_condition_product[component0, component1].save(self.folder["error_estimation"], "initial_condition_product_" + component0 + "_" + component1) return self.initial_condition_product[component0, component1] else: assert len(self.components) == 1 for q0 in range(self.Q_ic): for q1 in range(self.Q_ic): self.initial_condition_product[q0, q1] = transpose(self.truth_problem.initial_condition[q0])*inner_product*self.truth_problem.initial_condition[q1] self.initial_condition_product.save(self.folder["error_estimation"], "initial_condition_product") return self.initial_condition_product else: raise ValueError("Invalid stage in assemble_error_estimation_operators().") else: return ParametrizedReducedDifferentialProblem_DerivedClass.assemble_error_estimation_operators(self, term, current_stage)
def get_residual_norm_squared(self): N = self._solution.N theta_a = self.compute_theta("a") theta_at = self.compute_theta("a*") theta_c = self.compute_theta("c") theta_ct = self.compute_theta("c*") theta_m = self.compute_theta("m") theta_n = self.compute_theta("n") theta_f = self.compute_theta("f") theta_g = self.compute_theta("g") return (sum(product(theta_g, self.error_estimation_operator["g", "g"], theta_g)) + sum(product(theta_f, self.error_estimation_operator["f", "f"], theta_f)) + 2.0 * (transpose(self._solution) * sum(product(theta_m, self.error_estimation_operator["m", "g"][:N], theta_g))) + 2.0 * (transpose(self._solution) * sum(product(theta_at, self.error_estimation_operator["a*", "g"][:N], theta_g))) + 2.0 * (transpose(self._solution) * sum(product(theta_a, self.error_estimation_operator["a", "f"][:N], theta_f))) - 2.0 * (transpose(self._solution) * sum(product(theta_c, self.error_estimation_operator["c", "f"][:N], theta_f))) + (transpose(self._solution) * sum(product(theta_m, self.error_estimation_operator["m", "m"][:N, :N], theta_m)) * self._solution) + (transpose(self._solution) * sum(product(theta_at, self.error_estimation_operator["a*", "a*"][:N, :N], theta_at)) * self._solution) + 2.0 * (transpose(self._solution) * sum(product(theta_m, self.error_estimation_operator["m", "a*"][:N, :N], theta_at)) * self._solution) + (transpose(self._solution) * sum(product(theta_n, self.error_estimation_operator["n", "n"][:N, :N], theta_n)) * self._solution) + (transpose(self._solution) * sum(product(theta_ct, self.error_estimation_operator["c*", "c*"][:N, :N], theta_ct)) * self._solution) - 2.0 * (transpose(self._solution) * sum(product(theta_n, self.error_estimation_operator["n", "c*"][:N, :N], theta_ct)) * self._solution) + (transpose(self._solution) * sum(product(theta_a, self.error_estimation_operator["a", "a"][:N, :N], theta_a)) * self._solution) + (transpose(self._solution) * sum(product(theta_c, self.error_estimation_operator["c", "c"][:N, :N], theta_c)) * self._solution) - 2.0 * (transpose(self._solution) * sum(product(theta_a, self.error_estimation_operator["a", "c"][:N, :N], theta_c)) * self._solution) )
def _offline(self): # Change default online solve arguments during offline stage to use online stabilization # instead of vanishing viscosity one (which will be prepared in a postprocessing stage) self.reduced_problem._online_solve_default_kwargs[ "online_stabilization"] = True self.reduced_problem._online_solve_default_kwargs[ "online_vanishing_viscosity"] = False self.reduced_problem.OnlineSolveKwargs = OnlineSolveKwargsGenerator( **self.reduced_problem._online_solve_default_kwargs) # Call standard offline phase EllipticCoerciveReductionMethod_DerivedClass._offline(self) # Start vanishing viscosity postprocessing print( TextBox( self.truth_problem.name() + " " + self.label + " offline vanishing viscosity postprocessing phase begins", fill="=")) print("") # Prepare storage for copy of lifting basis functions matrix lifting_basis_functions = BasisFunctionsMatrix( self.truth_problem.V) lifting_basis_functions.init(self.truth_problem.components) # Copy current lifting basis functions to lifting_basis_functions N_bc = self.reduced_problem.N_bc for i in range(N_bc): lifting_basis_functions.enrich( self.reduced_problem.basis_functions[i]) # Prepare storage for unrotated basis functions matrix, without lifting unrotated_basis_functions = BasisFunctionsMatrix( self.truth_problem.V) unrotated_basis_functions.init(self.truth_problem.components) # Copy current basis functions (except lifting) to unrotated_basis_functions N = self.reduced_problem.N for i in range(N_bc, N): unrotated_basis_functions.enrich( self.reduced_problem.basis_functions[i]) # Prepare new storage for non-hierarchical basis functions matrix and # corresponding affine expansions self.reduced_problem.init( "offline_vanishing_viscosity_postprocessing") # Rotated basis functions matrix are not hierarchical, i.e. a different # rotation will be applied for each basis size n. for n in range(1, N + 1): # Prepare storage for rotated basis functions matrix rotated_basis_functions = BasisFunctionsMatrix( self.truth_problem.V) rotated_basis_functions.init(self.truth_problem.components) # Rotate basis print("rotate basis functions matrix for n =", n) truth_operator_k = self.truth_problem.operator["k"] truth_operator_m = self.truth_problem.operator["m"] assert len(truth_operator_k) == 1 assert len(truth_operator_m) == 1 reduced_operator_k = ( transpose(unrotated_basis_functions[:n]) * truth_operator_k[0] * unrotated_basis_functions[:n]) reduced_operator_m = ( transpose(unrotated_basis_functions[:n]) * truth_operator_m[0] * unrotated_basis_functions[:n]) rotation_eigensolver = OnlineEigenSolver( unrotated_basis_functions[:n], reduced_operator_k, reduced_operator_m) parameters = { "problem_type": "hermitian", "spectrum": "smallest real" } rotation_eigensolver.set_parameters(parameters) rotation_eigensolver.solve() # Store and save rotated basis rotation_eigenvalues = ExportableList("text") rotation_eigenvalues.extend([ rotation_eigensolver.get_eigenvalue(i)[0] for i in range(n) ]) for i in range(0, n): print("lambda_" + str(i) + " = " + str(rotation_eigenvalues[i])) rotation_eigenvalues.save(self.folder["post_processing"], "rotation_eigs_n=" + str(n)) for i in range(N_bc): rotated_basis_functions.enrich(lifting_basis_functions[i]) for i in range(0, n): (eigenvector_i, _) = rotation_eigensolver.get_eigenvector(i) rotated_basis_functions.enrich( unrotated_basis_functions[:n] * eigenvector_i) self.reduced_problem.basis_functions[: n] = rotated_basis_functions # Attach eigenvalues to the vanishing viscosity reduced operator self.reduced_problem.vanishing_viscosity_eigenvalues.append( rotation_eigenvalues) # Save basis functions self.reduced_problem.basis_functions.save( self.reduced_problem.folder["basis"], "basis") # Re-compute all reduced operators, since the basis functions have changed print("build reduced operators") self.reduced_problem.build_reduced_operators( "offline_vanishing_viscosity_postprocessing") # Clean up reduced solution and output cache, since the basis has changed self.reduced_problem._solution_cache.clear() self.reduced_problem._output_cache.clear() print( TextBox( self.truth_problem.name() + " " + self.label + " offline vanishing viscosity postprocessing phase ends", fill="=")) print("") # Restore default online solve arguments for online stage self.reduced_problem._online_solve_default_kwargs[ "online_stabilization"] = False self.reduced_problem._online_solve_default_kwargs[ "online_vanishing_viscosity"] = True self.reduced_problem.OnlineSolveKwargs = OnlineSolveKwargsGenerator( **self.reduced_problem._online_solve_default_kwargs)
def _compute_output(self): self._output = transpose(self._solution) * sum( product(self.compute_theta("s"), self.operator["s"]))
def _compute_output(self, N): self._output = transpose(self._solution) * sum(product(self.compute_theta("f"), self.operator["f"][:N]))
def __init__(self, inner_product, basis_functions, N): self.N = N self.basis_functions = basis_functions self.inner_product = inner_product self.inner_product_N = transpose( self.basis_functions) * self.inner_product * self.basis_functions
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 assemble_operator(self, term, current_stage="online"): if term == "projection_truth_snapshots": assert current_stage in ( "online", "offline_rectification_postprocessing") if current_stage == "online": # load from file self.operator["projection_truth_snapshots"].load( self.folder["reduced_operators"], "projection_truth_snapshots") return self.operator["projection_truth_snapshots"] elif current_stage == "offline_rectification_postprocessing": assert len( self.truth_problem.inner_product ) == 1 # the affine expansion storage contains only the inner product matrix inner_product = self.truth_problem.inner_product[0] for n in range(1, self.N + 1): assert len( self.inner_product ) == 1 # the affine expansion storage contains only the inner product matrix inner_product_n = self.inner_product[:n, :n][0] basis_functions_n = self.basis_functions[:n] projection_truth_snapshots_expansion = OnlineAffineExpansionStorage( 1) projection_truth_snapshots = OnlineMatrix(n, n) for (i, snapshot_i) in enumerate(self.snapshots[:n]): projected_truth_snapshot_i = OnlineFunction(n) solver = LinearSolver( inner_product_n, projected_truth_snapshot_i, transpose(basis_functions_n) * inner_product * snapshot_i) solver.set_parameters( self._linear_solver_parameters) solver.solve() for j in range(n): projection_truth_snapshots[ j, i] = projected_truth_snapshot_i.vector()[j] projection_truth_snapshots_expansion[ 0] = projection_truth_snapshots print("\tcondition number for n = " + str(n) + ": " + str(cond(projection_truth_snapshots))) self.operator[ "projection_truth_snapshots"][:n, : n] = projection_truth_snapshots_expansion # Save self.operator["projection_truth_snapshots"].save( self.folder["reduced_operators"], "projection_truth_snapshots") return self.operator["projection_truth_snapshots"] else: raise ValueError("Invalid stage in assemble_operator().") elif term == "projection_reduced_snapshots": assert current_stage in ( "online", "offline_rectification_postprocessing") if current_stage == "online": # load from file self.operator["projection_reduced_snapshots"].load( self.folder["reduced_operators"], "projection_reduced_snapshots") return self.operator["projection_reduced_snapshots"] elif current_stage == "offline_rectification_postprocessing": # Backup mu bak_mu = self.mu # Prepare rectification for all possible online solve arguments for n in range(1, self.N + 1): print("\tcondition number for n = " + str(n)) projection_reduced_snapshots_expansion = OnlineAffineExpansionStorage( len(self.online_solve_kwargs_without_rectification) ) for (q, online_solve_kwargs) in enumerate( self.online_solve_kwargs_without_rectification ): projection_reduced_snapshots = OnlineMatrix(n, n) for (i, mu_i) in enumerate(self.snapshots_mu[:n]): self.set_mu(mu_i) projected_reduced_snapshot_i = self.solve( n, **online_solve_kwargs) for j in range(n): projection_reduced_snapshots[ j, i] = projected_reduced_snapshot_i.vector( )[j] projection_reduced_snapshots_expansion[ q] = projection_reduced_snapshots print("\t\tonline solve options " + str( dict(self. online_solve_kwargs_with_rectification[q]) ) + ": " + str(cond(projection_reduced_snapshots))) self.operator[ "projection_reduced_snapshots"][:n, : n] = projection_reduced_snapshots_expansion # Save and restore previous mu self.set_mu(bak_mu) self.operator["projection_reduced_snapshots"].save( self.folder["reduced_operators"], "projection_reduced_snapshots") return self.operator["projection_reduced_snapshots"] else: raise ValueError("Invalid stage in assemble_operator().") else: return EllipticCoerciveReducedProblem_DerivedClass.assemble_operator( self, term, current_stage)
def assemble_error_estimation_operators(self, term, current_stage="online"): """ It assembles operators for error estimation. """ assert current_stage in ("online", "offline") assert isinstance(term, tuple) assert len(term) == 2 if current_stage == "online": # load from file self.error_estimation_operator[term].load( self.folder["error_estimation"], "error_estimation_operator_" + term[0] + "_" + term[1]) return self.error_estimation_operator[term] elif current_stage == "offline": assert self.terms_order[term[0]] in (1, 2) assert self.terms_order[term[1]] in (1, 2) assert self.terms_order[term[0]] >= self.terms_order[ term[1]], "Please swap the order of " + str( term ) + " in self.error_estimation_terms" # otherwise for (term1, term2) of orders (1, 2) we would have a row vector, rather than a column one if self.terms_order[term[0]] == 2 and self.terms_order[ term[1]] == 2: for q0 in range(self.Q[term[0]]): for q1 in range(self.Q[term[1]]): self.error_estimation_operator[term][ q0, q1] = transpose( self.riesz[term[0]][q0] ) * self._error_estimation_inner_product * self.riesz[ term[1]][q1] elif self.terms_order[term[0]] == 2 and self.terms_order[ term[1]] == 1: for q0 in range(self.Q[term[0]]): for q1 in range(self.Q[term[1]]): assert len(self.riesz[term[1]][q1]) == 1 self.error_estimation_operator[term][ q0, q1] = transpose( self.riesz[term[0]][q0] ) * self._error_estimation_inner_product * self.riesz[ term[1]][q1][0] elif self.terms_order[term[0]] == 1 and self.terms_order[ term[1]] == 1: for q0 in range(self.Q[term[0]]): assert len(self.riesz[term[0]][q0]) == 1 for q1 in range(self.Q[term[1]]): assert len(self.riesz[term[1]][q1]) == 1 self.error_estimation_operator[term][ q0, q1] = transpose( self.riesz[term[0]][q0][0] ) * self._error_estimation_inner_product * self.riesz[ term[1]][q1][0] else: raise ValueError( "Invalid term order for assemble_error_estimation_operators()." ) self.error_estimation_operator[term].save( self.folder["error_estimation"], "error_estimation_operator_" + term[0] + "_" + term[1]) return self.error_estimation_operator[term] else: raise ValueError( "Invalid stage in assemble_error_estimation_operators().")
def assemble_operator(self, term, current_stage="online"): """ Terms and respective thetas are assembled. :param term: the forms of the class of the problem. :param current_stage: online or offline stage. """ assert current_stage in ("online", "offline") if current_stage == "online": # load from file # Note that it would not be needed to return the loaded operator in # init(), since it has been already modified in-place. We do this, however, # because we want this interface to be compatible with the one in # EllipticCoerciveProblem, i.e. we would like to be able to use a reduced # problem also as a truth problem for a nested reduction if term in self.terms: self.operator[term].load(self.folder["reduced_operators"], "operator_" + term) return self.operator[term] elif term.startswith("inner_product"): component = term.replace("inner_product", "").replace("_", "") if component != "": assert component in self.components self.inner_product[component].load( self.folder["reduced_operators"], term) return self.inner_product[component] else: assert len(self.components) == 1 self.inner_product.load(self.folder["reduced_operators"], term) return self.inner_product elif term.startswith("projection_inner_product"): component = term.replace("projection_inner_product", "").replace("_", "") if component != "": assert component in self.components self.projection_inner_product[component].load( self.folder["reduced_operators"], term) return self.projection_inner_product[component] else: assert len(self.components) == 1 self.projection_inner_product.load( 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 online reduced problems." ) else: raise ValueError("Invalid term for assemble_operator().") elif current_stage == "offline": # As in the previous case, there is no need to return anything because # we are still training the reduced order model, so the previous remark # (on the usage of a reduced problem as a truth one) cannot hold here. # However, in order to have a consistent interface we return the assembled # operator if term in self.terms: assert self.Q[term] == self.truth_problem.Q[term] for q in range(self.Q[term]): assert self.terms_order[term] in (0, 1, 2) if self.terms_order[term] == 2: self.operator[term][q] = transpose( self.basis_functions ) * self.truth_problem.operator[term][ q] * self.basis_functions elif self.terms_order[term] == 1: self.operator[term][q] = transpose( self.basis_functions ) * self.truth_problem.operator[term][q] elif self.terms_order[term] == 0: self.operator[term][q] = self.truth_problem.operator[ term][q] 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"): component = term.replace("inner_product", "").replace("_", "") if component != "": assert component in self.components assert len( self.inner_product[component] ) == 1 # the affine expansion storage contains only the inner product matrix assert len( self.truth_problem.inner_product[component] ) == 1 # the affine expansion storage contains only the inner product matrix self.inner_product[component][0] = transpose( self.basis_functions ) * self.truth_problem.inner_product[component][ 0] * self.basis_functions self.inner_product[component].save( self.folder["reduced_operators"], term) return self.inner_product[component] else: assert len(self.components) == 1 # single component case 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 self.inner_product[0] = transpose( self.basis_functions ) * self.truth_problem.inner_product[ 0] * self.basis_functions self.inner_product.save(self.folder["reduced_operators"], term) return self.inner_product elif term.startswith("projection_inner_product"): component = term.replace("projection_inner_product", "").replace("_", "") if component != "": assert component in self.components assert len( self.projection_inner_product[component] ) == 1 # the affine expansion storage contains only the inner product matrix assert len( self.truth_problem.projection_inner_product[component] ) == 1 # the affine expansion storage contains only the inner product matrix self.projection_inner_product[component][0] = transpose( self.basis_functions ) * self.truth_problem.projection_inner_product[component][ 0] * self.basis_functions self.projection_inner_product[component].save( self.folder["reduced_operators"], term) return self.projection_inner_product[component] else: assert len(self.components) == 1 # single component case 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 self.projection_inner_product[0] = transpose( self.basis_functions ) * self.truth_problem.projection_inner_product[ 0] * self.basis_functions self.projection_inner_product.save( self.folder["reduced_operators"], term) return self.projection_inner_product elif term.startswith("dirichlet_bc"): component = term.replace("dirichlet_bc", "").replace("_", "") if component != "": assert component in self.components has_non_homogeneous_dirichlet_bc = self.dirichlet_bc[ component] and not self.dirichlet_bc_are_homogeneous[ component] else: assert len(self.components) == 1 component = None has_non_homogeneous_dirichlet_bc = self.dirichlet_bc and not self.dirichlet_bc_are_homogeneous if has_non_homogeneous_dirichlet_bc: # Compute lifting functions for the value of mu possibly provided by the user Q_dirichlet_bcs = len(self.compute_theta(term)) # Temporarily override compute_theta method to return only one nonzero # theta term related to boundary conditions standard_compute_theta = self.truth_problem.compute_theta for i in range(Q_dirichlet_bcs): def modified_compute_theta(self, term_): if term_ == term: theta_bc = standard_compute_theta(term_) modified_theta_bc = list() for j in range(Q_dirichlet_bcs): if j != i: modified_theta_bc.append(0.) else: modified_theta_bc.append(theta_bc[i]) return tuple(modified_theta_bc) else: return standard_compute_theta(term_) PatchInstanceMethod(self.truth_problem, "compute_theta", modified_compute_theta).patch() # ... and store the solution of the truth problem corresponding to that boundary condition # as lifting function solve_message = "Computing and storing lifting function n. " + str( i) if component is not None: solve_message += " for component " + component solve_message += " (obtained for mu = " + str( self.mu) + ") in the basis matrix" print(solve_message) lifting = self._lifting_truth_solve(term, i) self.basis_functions.enrich(lifting, component=component) # Restore the standard compute_theta method self.truth_problem.compute_theta = standard_compute_theta else: raise ValueError("Invalid term for assemble_operator().") else: raise ValueError("Invalid stage in assemble_operator().")