def GeostrophicOptimalControlReducedProblem(ParametrizedReducedDifferentialProblem_DerivedClass): GeostrophicOptimalControlReducedProblem_Base = LinearReducedProblem( ParametrizedReducedDifferentialProblem_DerivedClass) class GeostrophicOptimalControlReducedProblem_Class(GeostrophicOptimalControlReducedProblem_Base): class ProblemSolver(GeostrophicOptimalControlReducedProblem_Base.ProblemSolver): def matrix_eval(self): problem = self.problem N = self.N assembled_operator = dict() for term in ("a", "a*", "c", "c*", "m", "n"): assembled_operator[term] = sum(product(problem.compute_theta(term), problem.operator[term][:N, :N])) return (assembled_operator["m"] + assembled_operator["a*"] + assembled_operator["n"] - assembled_operator["c*"] + assembled_operator["a"] - assembled_operator["c"]) def vector_eval(self): problem = self.problem N = self.N assembled_operator = dict() for term in ("f", "g"): assembled_operator[term] = sum(product(problem.compute_theta(term), problem.operator[term][:N])) return (assembled_operator["g"] + assembled_operator["f"]) # Perform an online evaluation of the cost functional 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 _online_size_from_kwargs(self, N, **kwargs): if N is None: # then either, # * the user has passed kwargs, so we trust that he/she has doubled y and p for us # * or self.N was copied, which already stores the correct count of basis functions return GeostrophicOptimalControlReducedProblem_Base._online_size_from_kwargs(self, N, **kwargs) else: # then the integer value provided to N would be used for all components: need to double # it for y and p N, kwargs = GeostrophicOptimalControlReducedProblem_Base._online_size_from_kwargs(self, N, **kwargs) for component in ("ypsi", "yq", "ppsi", "pq"): N[component] *= 2 return N, kwargs return GeostrophicOptimalControlReducedProblem_Class
def EllipticReducedProblem(ParametrizedReducedDifferentialProblem_DerivedClass): EllipticReducedProblem_Base = LinearReducedProblem(ParametrizedReducedDifferentialProblem_DerivedClass) # Base class containing the interface of a projection based ROM # for elliptic problems. class EllipticReducedProblem_Class(EllipticReducedProblem_Base): class ProblemSolver(EllipticReducedProblem_Base.ProblemSolver): def matrix_eval(self): problem = self.problem N = self.N return sum(product(problem.compute_theta("a"), problem.operator["a"][:N, :N])) def vector_eval(self): problem = self.problem N = self.N return sum(product(problem.compute_theta("f"), problem.operator["f"][:N])) # Perform an online evaluation of the output def _compute_output(self, N): self._output = transpose(self._solution) * sum(product(self.compute_theta("s"), self.operator["s"][:N])) # return value (a class) for the decorator return EllipticReducedProblem_Class
def GeostrophicReducedProblem(ParametrizedReducedDifferentialProblem_DerivedClass): GeostrophicReducedProblem_Base = LinearReducedProblem(ParametrizedReducedDifferentialProblem_DerivedClass) class GeostrophicReducedProblem_Class(GeostrophicReducedProblem_Base): class ProblemSolver(GeostrophicReducedProblem_Base.ProblemSolver): def matrix_eval(self): problem = self.problem N = self.N return sum(product(problem.compute_theta("a"), problem.operator["a"][:N, :N])) def vector_eval(self): problem = self.problem N = self.N return sum(product(problem.compute_theta("f"), problem.operator["f"][:N])) # return value (a class) for the decorator return GeostrophicReducedProblem_Class
def StokesOptimalControlReducedProblem( ParametrizedReducedDifferentialProblem_DerivedClass): StokesOptimalControlReducedProblem_Base = LinearReducedProblem( ParametrizedReducedDifferentialProblem_DerivedClass) class StokesOptimalControlReducedProblem_Class( StokesOptimalControlReducedProblem_Base): class ProblemSolver( StokesOptimalControlReducedProblem_Base.ProblemSolver): def matrix_eval(self): problem = self.problem N = self.N assembled_operator = dict() for term in ("a", "a*", "b", "b*", "bt", "bt*", "c", "c*", "m", "n"): assembled_operator[term] = sum( product(problem.compute_theta(term), problem.operator[term][:N, :N])) return (assembled_operator["m"] + assembled_operator["a*"] + assembled_operator["bt*"] + assembled_operator["b*"] + assembled_operator["n"] - assembled_operator["c*"] + assembled_operator["a"] + assembled_operator["bt"] - assembled_operator["c"] + assembled_operator["b"]) def vector_eval(self): problem = self.problem N = self.N assembled_operator = dict() for term in ("f", "g", "l"): assembled_operator[term] = sum( product(problem.compute_theta(term), problem.operator[term][:N])) return (assembled_operator["g"] + assembled_operator["f"] + assembled_operator["l"]) # Custom combination of boundary conditions *not* to add BCs of supremizers def bc_eval(self): problem = self.problem # Temporarily change problem.components components_bak = problem.components problem.components = ["v", "p", "w", "q"] # Call Parent bcs = StokesOptimalControlReducedProblem_Base.ProblemSolver.bc_eval( self) # Restore and return problem.components = components_bak return bcs # Perform an online evaluation of the cost functional 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"]) # If a value of N was provided, make sure to double it when dealing with y and p, due to # the aggregated component approach def _online_size_from_kwargs(self, N, **kwargs): if N is None: # then either, # * the user has passed kwargs, so we trust that he/she has doubled velocities, # supremizers and pressures for us # * or self.N was copied, which already stores the correct count of basis functions return StokesOptimalControlReducedProblem_Base._online_size_from_kwargs( self, N, **kwargs) else: # then the integer value provided to N would be used for all components: need to double # it for velocities, supremizers and pressures N, kwargs = StokesOptimalControlReducedProblem_Base._online_size_from_kwargs( self, N, **kwargs) for component in ("v", "s", "p", "w", "r", "q"): N[component] *= 2 return N, kwargs # Internal method for error computation def _compute_error(self, **kwargs): components = ["v", "p", "u", "w", "q"] # but not supremizers if "components" not in kwargs: kwargs["components"] = components else: assert kwargs["components"] == components return StokesOptimalControlReducedProblem_Base._compute_error( self, **kwargs) # Internal method for relative error computation def _compute_relative_error(self, absolute_error, **kwargs): components = ["v", "p", "u", "w", "q"] # but not supremizers if "components" not in kwargs: kwargs["components"] = components else: assert kwargs["components"] == components return StokesOptimalControlReducedProblem_Base._compute_relative_error( self, absolute_error, **kwargs) # Assemble the reduced order affine expansion def assemble_operator(self, term, current_stage="online"): if term == "bt_restricted": self.operator["bt_restricted"] = self.operator["bt"] return self.operator["bt_restricted"] elif term == "bt*_restricted": self.operator["bt*_restricted"] = self.operator["bt*"] return self.operator["bt*_restricted"] elif term == "inner_product_s": self.inner_product["s"] = self.inner_product["v"] return self.inner_product["s"] elif term == "inner_product_r": self.inner_product["r"] = self.inner_product["w"] return self.inner_product["r"] elif term == "projection_inner_product_s": self.projection_inner_product[ "s"] = self.projection_inner_product["v"] return self.projection_inner_product["s"] elif term == "projection_inner_product_r": self.projection_inner_product[ "r"] = self.projection_inner_product["w"] return self.projection_inner_product["r"] else: return StokesOptimalControlReducedProblem_Base.assemble_operator( self, term, current_stage) # Custom combination of inner products *not* to add inner product corresponding to supremizers def _combine_all_inner_products(self): # Temporarily change self.components components_bak = self.components self.components = ["v", "p", "u", "w", "q"] # Call Parent combined_inner_products = StokesOptimalControlReducedProblem_Base._combine_all_inner_products( self) # Restore and return self.components = components_bak return combined_inner_products # Custom combination of inner products *not* to add projection inner product corresponding to supremizers def _combine_all_projection_inner_products(self): # Temporarily change self.components components_bak = self.components self.components = ["v", "p", "u", "w", "q"] # Call Parent combined_projection_inner_products = ( StokesOptimalControlReducedProblem_Base. _combine_all_projection_inner_products(self)) # Restore and return self.components = components_bak return combined_projection_inner_products # return value (a class) for the decorator return StokesOptimalControlReducedProblem_Class
def EllipticOptimalControlReducedProblem(ParametrizedReducedDifferentialProblem_DerivedClass): EllipticOptimalControlReducedProblem_Base = LinearReducedProblem( ParametrizedReducedDifferentialProblem_DerivedClass) # Base class containing the interface of a projection based ROM # for saddle point problems. class EllipticOptimalControlReducedProblem_Class(EllipticOptimalControlReducedProblem_Base): class ProblemSolver(EllipticOptimalControlReducedProblem_Base.ProblemSolver): def matrix_eval(self): problem = self.problem N = self.N assembled_operator = dict() for term in ("a", "a*", "c", "c*", "m", "n"): assembled_operator[term] = sum(product(problem.compute_theta(term), problem.operator[term][:N, :N])) return (assembled_operator["m"] + assembled_operator["a*"] + assembled_operator["n"] - assembled_operator["c*"] + assembled_operator["a"] - assembled_operator["c"]) def vector_eval(self): problem = self.problem N = self.N assembled_operator = dict() for term in ("f", "g"): assembled_operator[term] = sum(product(problem.compute_theta(term), problem.operator[term][:N])) return (assembled_operator["g"] + assembled_operator["f"]) # Perform an online evaluation of the cost functional def _compute_output(self, N): assembled_operator = dict() for term in ("g", "h", "m", "n"): 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"] ) # If a value of N was provided, make sure to double it when dealing with y and p, due to # the aggregated component approach def _online_size_from_kwargs(self, N, **kwargs): if N is None: # then either, # * the user has passed kwargs, so we trust that he/she has doubled y and p for us # * or self.N was copied, which already stores the correct count of basis functions return EllipticOptimalControlReducedProblem_Base._online_size_from_kwargs(self, N, **kwargs) else: # then the integer value provided to N would be used for all components: need to double # it for y and p N, kwargs = EllipticOptimalControlReducedProblem_Base._online_size_from_kwargs(self, N, **kwargs) for component in ("y", "p"): N[component] *= 2 return N, kwargs # return value (a class) for the decorator return EllipticOptimalControlReducedProblem_Class
def StokesReducedProblem(ParametrizedReducedDifferentialProblem_DerivedClass): StokesReducedProblem_Base = LinearReducedProblem( ParametrizedReducedDifferentialProblem_DerivedClass) # Base class containing the interface of a projection based ROM # for saddle point problems. class StokesReducedProblem_Class(StokesReducedProblem_Base): def __init__(self, truth_problem, **kwargs): StokesReducedProblem_Base.__init__(self, truth_problem, **kwargs) # Auxiliary storage for solution of reduced order supremizer problem (if requested through solve_supremizer) self._supremizer = None # OnlineFunction # I/O def _supremizer_cache_key_generator(*args, **kwargs): assert len(args) is 2 assert args[0] == self.mu return self._supremizer_cache_key_from_N_and_kwargs( args[1], **kwargs) self._supremizer_cache = Cache( "reduced problems", key_generator=_supremizer_cache_key_generator) class ProblemSolver(StokesReducedProblem_Base.ProblemSolver): def matrix_eval(self): problem = self.problem N = self.N assembled_operator = dict() for term in ("a", "b", "bt"): assembled_operator[term] = sum( product(problem.compute_theta(term), problem.operator[term][:N, :N])) return assembled_operator["a"] + assembled_operator[ "b"] + assembled_operator["bt"] def vector_eval(self): problem = self.problem N = self.N assembled_operator = dict() for term in ("f", "g"): assembled_operator[term] = sum( product(problem.compute_theta(term), problem.operator[term][:N])) return assembled_operator["f"] + assembled_operator["g"] # Custom combination of boundary conditions *not* to add BCs of supremizers def bc_eval(self): problem = self.problem # Temporarily change problem.components components_bak = problem.components problem.components = ["u", "p"] # Call Parent bcs = StokesReducedProblem_Base.ProblemSolver.bc_eval(self) # Restore and return problem.components = components_bak return bcs def solve_supremizer(self, solution): N_us = OnlineSizeDict(solution.N) # create a copy del N_us["p"] kwargs = self._latest_solve_kwargs self._supremizer = OnlineFunction(N_us) try: assign(self._supremizer, self._supremizer_cache[ self.mu, N_us, kwargs]) # **kwargs is not supported by __getitem__ except KeyError: self._solve_supremizer(solution) self._supremizer_cache[self.mu, N_us, kwargs] = copy(self._supremizer) return self._supremizer def _solve_supremizer(self, solution): N_us = self._supremizer.N N_usp = solution.N assert len( self.inner_product["s"] ) == 1 # the affine expansion storage contains only the inner product matrix assembled_operator_lhs = self.inner_product["s"][0][:N_us, :N_us] assembled_operator_bt = sum( product(self.compute_theta("bt_restricted"), self.operator["bt_restricted"][:N_us, :N_usp])) assembled_operator_rhs = assembled_operator_bt * solution if self.dirichlet_bc[ "u"] and not self.dirichlet_bc_are_homogeneous["u"]: assembled_dirichlet_bc = dict() assert self.dirichlet_bc["s"] assert self.dirichlet_bc_are_homogeneous["s"] assembled_dirichlet_bc["u"] = self.compute_theta( "dirichlet_bc_s") else: assembled_dirichlet_bc = None solver = OnlineLinearSolver(assembled_operator_lhs, self._supremizer, assembled_operator_rhs, assembled_dirichlet_bc) solver.set_parameters(self._linear_solver_parameters) solver.solve() def _supremizer_cache_key_from_N_and_kwargs(self, N, **kwargs): return self._cache_key_from_N_and_kwargs(N, **kwargs) # Internal method for error computation def _compute_error(self, **kwargs): components = ["u", "p"] # but not "s" if "components" not in kwargs: kwargs["components"] = components else: assert kwargs["components"] == components return StokesReducedProblem_Base._compute_error(self, **kwargs) # Internal method for relative error computation def _compute_relative_error(self, absolute_error, **kwargs): components = ["u", "p"] # but not "s" if "components" not in kwargs: kwargs["components"] = components else: assert kwargs["components"] == components return StokesReducedProblem_Base._compute_relative_error( self, absolute_error, **kwargs) def export_supremizer(self, folder=None, filename=None, supremizer=None, component=None, suffix=None): if supremizer is None: supremizer = self._supremizer N_us = supremizer.N basis_functions_us = self.basis_functions[["u", "s"]] self.truth_problem.export_supremizer( folder, filename, basis_functions_us[:N_us] * supremizer, component, suffix) # Assemble the reduced order affine expansion def assemble_operator(self, term, current_stage="online"): if current_stage == "offline": if term == "bt_restricted": basis_functions_us = self.basis_functions[["u", "s"]] assert self.Q["bt_restricted"] == self.truth_problem.Q[ "bt_restricted"] for q in range(self.Q["bt_restricted"]): self.operator["bt_restricted"][q] = transpose( basis_functions_us) * self.truth_problem.operator[ "bt_restricted"][q] * self.basis_functions self.operator["bt_restricted"].save( self.folder["reduced_operators"], "operator_bt_restricted") return self.operator["bt_restricted"] elif term == "inner_product_s": basis_functions_us = self.basis_functions[["u", "s"]] assert len( self.inner_product["s"] ) == 1 # the affine expansion storage contains only the inner product matrix assert len( self.truth_problem.inner_product["s"] ) == 1 # the affine expansion storage contains only the inner product matrix self.inner_product["s"][0] = transpose( basis_functions_us) * self.truth_problem.inner_product[ "s"][0] * basis_functions_us self.inner_product["s"].save( self.folder["reduced_operators"], "inner_product_s") return self.inner_product["s"] elif term == "projection_inner_product_s": basis_functions_us = self.basis_functions[["u", "s"]] assert len( self.projection_inner_product["s"] ) == 1 # the affine expansion storage contains only the inner product matrix assert len( self.truth_problem.projection_inner_product["s"] ) == 1 # the affine expansion storage contains only the inner product matrix self.projection_inner_product["s"][0] = transpose( basis_functions_us ) * self.truth_problem.projection_inner_product["s"][ 0] * basis_functions_us self.projection_inner_product["s"].save( self.folder["reduced_operators"], "projection_inner_product_s") return self.projection_inner_product["s"] else: return StokesReducedProblem_Base.assemble_operator( self, term, current_stage) else: return StokesReducedProblem_Base.assemble_operator( self, term, current_stage) # Custom combination of inner products *not* to add inner product corresponding to supremizers def _combine_all_inner_products(self): # Temporarily change self.components components_bak = self.components self.components = ["u", "p"] # Call Parent combined_inner_products = StokesReducedProblem_Base._combine_all_inner_products( self) # Restore and return self.components = components_bak return combined_inner_products # Custom combination of inner products *not* to add projection inner product corresponding to supremizers def _combine_all_projection_inner_products(self): # Temporarily change self.components components_bak = self.components self.components = ["u", "p"] # Call Parent combined_projection_inner_products = StokesReducedProblem_Base._combine_all_projection_inner_products( self) # Restore and return self.components = components_bak return combined_projection_inner_products # return value (a class) for the decorator return StokesReducedProblem_Class