예제 #1
0
 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
예제 #3
0
 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
예제 #7
0
 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
     )
예제 #9
0
 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
예제 #11
0
 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)
예제 #14
0
 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]))
예제 #16
0
 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)
예제 #18
0
 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)
예제 #19
0
 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().")
예제 #20
0
    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().")