Пример #1
0
 def save(self, directory, filename):
     # Get full directory name
     full_directory = Folders.Folder(os.path.join(str(directory), filename))
     full_directory.create()
     # Save problem corresponding to self._lhs
     assert self._lhs is not None
     LHSIO.save_file(
         get_reduced_problem_from_riesz_solve_inner_product(
             self._lhs).truth_problem.name(), full_directory,
         "lhs_problem_name")
     # Save problem corresponding to self._solution
     assert self._solution is not None
     SolutionIO.save_file(
         get_reduced_problem_from_riesz_solve_storage(
             self._solution).truth_problem.name(), full_directory,
         "solution_problem_name")
     # Save problem and operator corresponding to self._rhs
     assert self._rhs is not None
     assert isinstance(self._rhs,
                       (AbstractParametrizedTensorFactory, DelayedProduct))
     if isinstance(self._rhs, AbstractParametrizedTensorFactory):
         RHSIO.save_file("ParametrizedTensorFactory", full_directory,
                         "rhs_type")
         rhs_arg_0 = self._rhs
         rhs_problem_name_0 = get_problem_from_parametrized_operator(
             rhs_arg_0).name()
         (rhs_term_0, rhs_index_0
          ) = get_term_and_index_from_parametrized_operator(rhs_arg_0)
         RHSIO.save_file((rhs_problem_name_0, rhs_term_0, rhs_index_0),
                         full_directory, "rhs_arg_0")
     elif isinstance(self._rhs, DelayedProduct):
         RHSIO.save_file("DelayedProduct", full_directory, "rhs_type")
         assert len(self._rhs._args) is 3
         rhs_arg_0 = self._rhs._args[0]
         assert rhs_arg_0 == -1.0
         RHSIO.save_file(rhs_arg_0, full_directory, "rhs_arg_0")
         assert isinstance(self._rhs._args[1],
                           AbstractParametrizedTensorFactory)
         rhs_arg_1 = self._rhs._args[1]
         rhs_problem_name_1 = get_problem_from_parametrized_operator(
             rhs_arg_1).name()
         (rhs_term_1, rhs_index_1
          ) = get_term_and_index_from_parametrized_operator(rhs_arg_1)
         RHSIO.save_file((rhs_problem_name_1, rhs_term_1, rhs_index_1),
                         full_directory, "rhs_arg_1")
         rhs_arg_2 = self._rhs._args[2]
         rhs_problem_name_2 = rhs_problem_name_1
         (rhs_component_2, rhs_index_2
          ) = get_component_and_index_from_basis_function(rhs_arg_2)
         RHSIO.save_file((rhs_problem_name_2, rhs_component_2, rhs_index_2),
                         full_directory, "rhs_arg_2")
     else:
         raise TypeError("Invalid rhs")
     # Save problem corresponding to self._bcs
     BCsIO.save_file(
         get_reduced_problem_from_riesz_solve_homogeneous_dirichlet_bc(
             self._bcs).truth_problem.name(), full_directory,
         "bcs_problem_name")
     # Save parameters
     ParametersIO.save_file(self._parameters, full_directory, "parameters")
Пример #2
0
def _product(thetas: ThetaType,
             operators: tuple_of(ParametrizedTensorFactory)):
    operators_as_forms = tuple(operator._form for operator in operators)
    try:
        output = _product_parametrized_tensor_factories_output_cache[
            operators_as_forms]
    except KeyError:
        # Keep the operators as ParametrizedTensorFactories and delay assembly as long as possible
        output = _product(thetas, operators_as_forms)
        output = ParametrizedTensorFactory(output.sum_product_return_value)
        problems = [
            get_problem_from_parametrized_operator(operator)
            for operator in operators
        ]
        assert all([problem is problems[0] for problem in problems])
        add_to_map_from_parametrized_operator_to_problem(output, problems[0])
        output = ProductOutput(output)
        _product_parametrized_tensor_factories_output_cache[
            operators_as_forms] = output
        _product_parametrized_tensor_factories_constants_cache[
            operators_as_forms] = _product_forms_constants_cache[
                operators_as_forms]
        return output
    else:
        constants = _product_parametrized_tensor_factories_constants_cache[
            operators_as_forms]
        for (theta, constant) in zip(thetas, constants):
            theta = float(theta)
            constant.assign(theta)
        return output
 def __rmul__(self, other):
     form_mul = other * self._form
     output = _ParametrizedTensorFactory.__new__(type(self), form_mul)
     output.__init__(form_mul)
     add_to_map_from_parametrized_operator_to_problem(
         output, get_problem_from_parametrized_operator(self))
     return output
Пример #4
0
 def save(self, directory, filename):
     # Get full directory name
     full_directory = Folders.Folder(os.path.join(str(directory), filename))
     full_directory.create()
     # Export depending on type
     TypeIO.save_file(self._type, full_directory, "type")
     assert self._type in ("basis_functions_matrix", "empty", "error_estimation_operators_11", "error_estimation_operators_21", "error_estimation_operators_22", "functions_list", "operators")
     if self._type in ("basis_functions_matrix", "functions_list"):
         # Save delayed functions
         delayed_functions = self._content[self._type]
         it = NonAffineExpansionStorageContent_Iterator(delayed_functions, flags=["c_index", "multi_index", "refs_ok"], op_flags=["readonly"])
         while not it.finished:
             delayed_function = delayed_functions[it.multi_index]
             delayed_function.save(full_directory, "delayed_functions_" + str(it.index))
             it.iternext()
     elif self._type == "empty":
         pass
     elif self._type in ("error_estimation_operators_11", "error_estimation_operators_21", "error_estimation_operators_22"):
         # Save delayed functions
         delayed_function_type = {
             DelayedBasisFunctionsMatrix: "DelayedBasisFunctionsMatrix",
             DelayedLinearSolver: "DelayedLinearSolver"
         }
         assert len(self._content["delayed_functions"]) is 2
         for (index, delayed_functions) in enumerate(self._content["delayed_functions"]):
             it = NonAffineExpansionStorageContent_Iterator(delayed_functions, flags=["c_index", "refs_ok"], op_flags=["readonly"])
             while not it.finished:
                 delayed_function = delayed_functions[it.index]
                 DelayedFunctionsTypeIO.save_file(delayed_function_type[type(delayed_function)], full_directory, "delayed_functions_" + str(index) + "_" + str(it.index) + "_type")
                 DelayedFunctionsProblemNameIO.save_file(delayed_function.get_problem_name(), full_directory, "delayed_functions_" + str(index) + "_" + str(it.index) + "_problem_name")
                 delayed_function.save(full_directory, "delayed_functions_" + str(index) + "_" + str(it.index) + "_content")
                 it.iternext()
         ErrorEstimationInnerProductIO.save_file(get_reduced_problem_from_error_estimation_inner_product(self._content["inner_product_matrix"]).truth_problem.name(), full_directory, "inner_product_matrix_problem_name")
     elif self._type == "operators":
         # Save truth content
         it = NonAffineExpansionStorageContent_Iterator(self._content["truth_operators"], flags=["c_index", "multi_index", "refs_ok"], op_flags=["readonly"])
         while not it.finished:
             operator = self._content["truth_operators"][it.multi_index]
             assert isinstance(operator, (AbstractParametrizedTensorFactory, NumericForm))
             if isinstance(operator, AbstractParametrizedTensorFactory):
                 problem_name = get_problem_from_parametrized_operator(operator).name()
                 (term, index) = get_term_and_index_from_parametrized_operator(operator)
                 TruthContentItemIO.save_file("ParametrizedTensorFactory", full_directory, "truth_operator_" + str(it.index) + "_type")
                 TruthContentItemIO.save_file((problem_name, term, index), full_directory, "truth_operator_" + str(it.index))
             elif isinstance(operator, NumericForm):
                 TruthContentItemIO.save_file("NumericForm", full_directory, "truth_operator_" + str(it.index) + "_type")
                 TruthContentItemIO.save_file(operator, full_directory, "truth_operator_" + str(it.index))
             else:
                 raise TypeError("Invalid operator type")
             it.iternext()
         assert "truth_operators_as_expansion_storage" in self._content
         # Save basis functions content
         assert len(self._content["basis_functions"]) in (0, 1, 2)
         BasisFunctionsContentLengthIO.save_file(len(self._content["basis_functions"]), full_directory, "basis_functions_length")
         for (index, basis_functions) in enumerate(self._content["basis_functions"]):
             BasisFunctionsProblemNameIO.save_file(get_reduced_problem_from_basis_functions(basis_functions).truth_problem.name(), full_directory, "basis_functions_" + str(index) + "_problem_name")
             BasisFunctionsProblemNameIO.save_file(basis_functions._components_name, full_directory, "basis_functions_" + str(index) + "_components_name")
     else:
         raise ValueError("Invalid type")
Пример #5
0
 def _prepare_truth_operators_as_expansion_storage(self):
     from rbnics.backends import NonAffineExpansionStorage
     assert self._type == "operators"
     assert self.order() is 1
     extracted_operators = tuple(op._form for op in self._content["truth_operators"])
     assert "truth_operators_as_expansion_storage" not in self._content
     self._content["truth_operators_as_expansion_storage"] = NonAffineExpansionStorage(extracted_operators)
     problems = [get_problem_from_parametrized_operator(op) for op in self._content["truth_operators"]]
     assert all([problem is problems[0] for problem in problems])
     for extracted_operator in self._content["truth_operators_as_expansion_storage"]:
         add_to_map_from_parametrized_operator_to_problem(extracted_operator, problems[0])
 def __add__(self, other):
     form_sum = self._form + other._form
     output = _ParametrizedTensorFactory.__new__(type(self), form_sum)
     output.__init__(form_sum)
     problems = [
         get_problem_from_parametrized_operator(operator)
         for operator in (self, other)
     ]
     assert all([problem is problems[0] for problem in problems])
     add_to_map_from_parametrized_operator_to_problem(
         output, problems[0])
     return output
 def __rmul__(self, other):
     form_mul = other * self._form
     output = _ParametrizedTensorFactory.__new__(type(self), form_mul)
     output.__init__(form_mul)
     # Set corresponding problem
     add_to_map_from_parametrized_operator_to_problem(
         output, get_problem_from_parametrized_operator(self))
     # Automatically compute name starting from current name.
     output._name = _ParametrizedTensorFactory._hash_name(
         str(other) + " * " + self.name())
     # This method is only used by exact parametrized operator evaluations, and not by DEIM.
     # Thus, description (which is called by DEIM during the offline phase) must never be used,
     # and the code should give an error if it is used by mistake.
     del output._description
     # Return
     return output
 def __mul__(self, other):
     form_mul = self._form * other
     output = _ParametrizedTensorFactory.__new__(type(self), form_mul)
     output.__init__(form_mul)
     # Set corresponding problem
     add_to_map_from_parametrized_operator_to_problem(
         output, get_problem_from_parametrized_operator(self))
     # Do not recompute name, as the computed name would:
     # * account that other is a solution (or its derivative) while called from an high fidelity solve, because of
     #   known mapping from truth solution to truth problem.
     # * not be able account that other is the high fidelity representation of a reduced solution, because mapping
     #   from (high fidelity representation of) reduced solution to truth problem is not known, as a new
     #   reduced solution (and corresponding representation) is generated at every reduced solve
     # Simply re-use the existing name with a custom suffix.
     output._name = _ParametrizedTensorFactory._hash_name(
         self.name() + "_mul_function")
     # This method is only used by exact parametrized operator evaluations, and not by DEIM.
     # Thus, description (which is called by DEIM during the offline phase) must never be used,
     # and the code should give an error if it is used by mistake.
     del output._description
     # Return
     return output
 def __add__(self, other):
     form_sum = self._form + other._form
     output = _ParametrizedTensorFactory.__new__(type(self), form_sum)
     output.__init__(form_sum)
     # Set corresponding problem
     problems = [
         get_problem_from_parametrized_operator(operator)
         for operator in (self, other)
     ]
     assert all([problem is problems[0] for problem in problems])
     add_to_map_from_parametrized_operator_to_problem(
         output, problems[0])
     # Automatically compute name starting from names of addends
     output._name = _ParametrizedTensorFactory._hash_name(self.name() +
                                                          " + " +
                                                          other.name())
     # This method is only used by exact parametrized operator evaluations, and not by DEIM.
     # Thus, description (which is called by DEIM during the offline phase) must never be used,
     # and the code should give an error if it is used by mistake.
     del output._description
     # Return
     return output
Пример #10
0
    def _basic_form_on_truth_function_space(form_wrapper, tensor=None):
        form = form_wrapper._form
        form_name = form_wrapper.name()
        mu = get_problem_from_parametrized_operator(form_wrapper).mu

        if form_name not in form_on_truth_function_space__reduced_problem_to_truth_solution_cache:
            visited = set()
            truth_problems = list()
            truth_problem_to_components = dict()
            truth_problem_to_exact_truth_problem = dict()
            truth_problem_to_truth_solution = dict()
            reduced_problem_to_components = dict()
            reduced_problem_to_truth_solution = dict()

            # Look for terminals on truth mesh
            for node in wrapping.form_iterator(form):
                if node in visited:
                    continue
                # ... problem solutions related to nonlinear terms
                elif wrapping.is_problem_solution_or_problem_solution_component_type(
                        node):
                    if wrapping.is_problem_solution_or_problem_solution_component(
                            node):
                        (preprocessed_node, component, truth_solution
                         ) = wrapping.solution_identify_component(node)
                        truth_problem = get_problem_from_solution(
                            truth_solution)
                        truth_problems.append(truth_problem)
                        # Store the solution
                        truth_problem_to_truth_solution[
                            truth_problem] = truth_solution
                        # Store the component
                        if truth_problem not in truth_problem_to_components:
                            truth_problem_to_components[truth_problem] = list()
                        truth_problem_to_components[truth_problem].append(
                            component)
                    else:
                        preprocessed_node = node
                    # Make sure to skip any parent solution related to this one
                    visited.add(node)
                    visited.add(preprocessed_node)
                    for parent_node in wrapping.solution_iterator(
                            preprocessed_node):
                        visited.add(parent_node)

            # Cache the resulting dicts
            form_on_truth_function_space__truth_problems_cache[
                form_name] = truth_problems
            form_on_truth_function_space__truth_problem_to_components_cache[
                form_name] = truth_problem_to_components
            form_on_truth_function_space__truth_problem_to_exact_truth_problem_cache[
                form_name] = truth_problem_to_exact_truth_problem
            form_on_truth_function_space__truth_problem_to_truth_solution_cache[
                form_name] = truth_problem_to_truth_solution
            form_on_truth_function_space__reduced_problem_to_components_cache[
                form_name] = reduced_problem_to_components
            form_on_truth_function_space__reduced_problem_to_truth_solution_cache[
                form_name] = reduced_problem_to_truth_solution

        # Extract from cache
        truth_problems = form_on_truth_function_space__truth_problems_cache[
            form_name]
        truth_problem_to_components = form_on_truth_function_space__truth_problem_to_components_cache[
            form_name]
        truth_problem_to_exact_truth_problem = form_on_truth_function_space__truth_problem_to_exact_truth_problem_cache[
            form_name]
        truth_problem_to_truth_solution = form_on_truth_function_space__truth_problem_to_truth_solution_cache[
            form_name]
        reduced_problem_to_components = form_on_truth_function_space__reduced_problem_to_components_cache[
            form_name]
        reduced_problem_to_truth_solution = form_on_truth_function_space__reduced_problem_to_truth_solution_cache[
            form_name]

        # Get list of truth and reduced problems that need to be solved, possibly updating cache
        required_truth_problems = list()
        required_reduced_problems = list()
        for truth_problem in truth_problems:
            truth_problem_is_solving = hasattr(truth_problem, "_is_solving")
            if is_training_started(truth_problem):
                reduced_problem = get_reduced_problem_from_problem(
                    truth_problem)
                reduced_problem_is_solving = hasattr(reduced_problem,
                                                     "_is_solving")
            else:
                reduced_problem = None
                reduced_problem_is_solving = False
            if not truth_problem_is_solving:
                if is_training_finished(truth_problem):
                    # Store the component
                    if reduced_problem not in reduced_problem_to_components:
                        reduced_problem_to_components[
                            reduced_problem] = truth_problem_to_components[
                                truth_problem]
                    # Store the solution
                    if reduced_problem not in reduced_problem_to_truth_solution:
                        reduced_problem_to_truth_solution[
                            reduced_problem] = truth_problem_to_truth_solution[
                                truth_problem]
                    # Append to list of required reduced problems
                    required_reduced_problems.append(
                        (reduced_problem, reduced_problem_is_solving))
                else:
                    if (hasattr(truth_problem,
                                "_apply_exact_evaluation_at_stages") and
                            not hasattr(truth_problem, "_apply_EIM_at_stages")
                            and not hasattr(truth_problem,
                                            "_apply_DEIM_at_stages")):
                        # Init truth problem (if required), as it may not have been initialized
                        truth_problem.init()
                        # Append to list of required truth problems which are not currently solving
                        required_truth_problems.append(
                            (truth_problem, False, reduced_problem_is_solving))
                    else:
                        # Store the corresponding exact truth problem
                        if truth_problem not in truth_problem_to_exact_truth_problem:
                            exact_truth_problem = exact_problem(truth_problem)
                            truth_problem_to_exact_truth_problem[
                                truth_problem] = exact_truth_problem
                            # Init exact truth problem (if required), as it may not have been initialized
                            exact_truth_problem.init()
                        else:
                            exact_truth_problem = truth_problem_to_exact_truth_problem[
                                truth_problem]
                        # Store the component
                        if exact_truth_problem not in truth_problem_to_components:
                            truth_problem_to_components[
                                exact_truth_problem] = truth_problem_to_components[
                                    truth_problem]
                        # Store the solution
                        if exact_truth_problem not in truth_problem_to_truth_solution:
                            truth_problem_to_truth_solution[
                                exact_truth_problem] = truth_problem_to_truth_solution[
                                    truth_problem]
                        # Append to list of required truth problems which are not currently solving
                        required_truth_problems.append(
                            (exact_truth_problem, False,
                             reduced_problem_is_solving))
            else:
                assert not reduced_problem_is_solving
                # Append to list of required truth problems which are currently solving
                required_truth_problems.append((truth_problem, True, False))

        # Solve truth problems (which have not been reduced yet) associated to nonlinear terms
        truth_problem_to_truth_solution_copy = dict()
        for (truth_problem, truth_problem_is_solving,
             reduced_problem_is_solving) in required_truth_problems:
            if not reduced_problem_is_solving:
                # Solve (if necessary) ...
                truth_problem.set_mu(mu)
                if not truth_problem_is_solving:
                    log(
                        PROGRESS,
                        "In form_on_truth_function_space, requiring truth problem solve for problem "
                        + truth_problem.name())
                    truth_problem.solve()
                else:
                    log(
                        PROGRESS,
                        "In form_on_truth_function_space, loading current truth problem solution for problem "
                        + truth_problem.name())
            else:
                reduced_problem = get_reduced_problem_from_problem(
                    truth_problem)
                log(
                    PROGRESS,
                    "In form_on_truth_function_space, replacing current truth problem solution with reduced solution for problem "
                    + reduced_problem.truth_problem.name())
            # ... and assign to truth_solution
            truth_solution = truth_problem_to_truth_solution[truth_problem]
            truth_problem_to_truth_solution_copy[truth_problem] = backend.copy(
                truth_solution)
            for component in truth_problem_to_components[truth_problem]:
                solution_to = _sub_from_tuple(truth_solution, component)
                if not reduced_problem_is_solving:
                    solution_from = _sub_from_tuple(truth_problem._solution,
                                                    component)
                else:
                    solution_from = _sub_from_tuple(
                        reduced_problem.basis_functions[:reduced_problem.
                                                        _solution.N] *
                        reduced_problem._solution, component)
                backend.assign(solution_to, solution_from)

        # Solve reduced problems associated to nonlinear terms
        reduced_problem_to_truth_solution_copy = dict()
        for (reduced_problem, is_solving) in required_reduced_problems:
            # Solve (if necessary) ...
            reduced_problem.set_mu(mu)
            if not is_solving:
                log(
                    PROGRESS,
                    "In form_on_truth_function_space, requiring reduced problem solve for problem "
                    + reduced_problem.truth_problem.name())
                reduced_problem.solve()
            else:
                log(
                    PROGRESS,
                    "In form_on_truth_function_space, loading current reduced problem solution for problem "
                    + reduced_problem.truth_problem.name())
            # ... and assign to truth_solution
            truth_solution = reduced_problem_to_truth_solution[reduced_problem]
            reduced_problem_to_truth_solution_copy[
                reduced_problem] = backend.copy(truth_solution)
            for component in reduced_problem_to_components[reduced_problem]:
                solution_to = _sub_from_tuple(truth_solution, component)
                solution_from = _sub_from_tuple(
                    reduced_problem.basis_functions[:reduced_problem._solution.
                                                    N] *
                    reduced_problem._solution, component)
                backend.assign(solution_to, solution_from)

        # Assemble
        assembled_form = wrapping.assemble(form, tensor)
        assembled_form.generator = form_wrapper  # for I/O
        form_rank = assembled_form.rank()

        # Undo any side effect of truth problem solves
        for (truth_problem, _, _) in required_truth_problems:
            truth_solution = truth_problem_to_truth_solution[truth_problem]
            truth_solution_copy = truth_problem_to_truth_solution_copy[
                truth_problem]
            for component in truth_problem_to_components[truth_problem]:
                solution_to = _sub_from_tuple(truth_solution, component)
                solution_from = _sub_from_tuple(truth_solution_copy, component)
                backend.assign(solution_to, solution_from)

        # Undo any side effect of reduced problem solves
        for (reduced_problem, _) in required_reduced_problems:
            truth_solution = reduced_problem_to_truth_solution[reduced_problem]
            truth_solution_copy = reduced_problem_to_truth_solution_copy[
                reduced_problem]
            for component in reduced_problem_to_components[reduced_problem]:
                solution_to = _sub_from_tuple(truth_solution, component)
                solution_from = _sub_from_tuple(truth_solution_copy, component)
                backend.assign(solution_to, solution_from)

        # Return
        return (assembled_form, form_rank)
    def _basic_form_on_reduced_function_space(form_wrapper, at):
        form = form_wrapper._form
        form_name = form_wrapper.name()
        form_problem = get_problem_from_parametrized_operator(form_wrapper)
        reduced_V = at.get_reduced_function_spaces()
        reduced_subdomain_data = at.get_reduced_subdomain_data()
        mu = form_problem.mu
        if hasattr(form_problem, "set_time"):
            t = form_problem.t
        else:
            t = None

        if (form_name, reduced_V) not in form_cache:
            visited = set()
            replacements = dict()
            truth_problems = list()
            truth_problem_to_components = {  # outer dict index over time derivative
                0: dict(),
                1: dict()
            }
            truth_problem_to_exact_truth_problem = dict()
            truth_problem_to_reduced_mesh_solution = dict()
            truth_problem_to_reduced_mesh_solution_dot = dict()
            truth_problem_to_reduced_mesh_interpolator = {  # outer dict index over time derivative
                0: dict(),
                1: dict()
            }
            reduced_problem_to_components = {  # outer dict index over time derivative
                0: dict(),
                1: dict()
            }
            reduced_problem_to_reduced_mesh_solution = dict()
            reduced_problem_to_reduced_mesh_solution_dot = dict()
            reduced_problem_to_reduced_basis_functions = {  # outer dict index over time derivative
                0: dict(),
                1: dict()
            }

            # Look for terminals on truth mesh
            logger.log(DEBUG, "Traversing terminals of form " + form_name)
            for node in wrapping.form_iterator(form, "nodes"):
                if node in visited:
                    continue
                # ... test and trial functions
                elif isinstance(node, Argument):
                    logger.log(
                        DEBUG, "\tFound argument, number: " +
                        str(node.number()) + ", part: " + str(node.part()))
                    replacements[node] = wrapping.form_argument_replace(
                        node, reduced_V)
                    visited.add(node)
                # ... problem solutions related to nonlinear terms
                elif wrapping.is_problem_solution_type(node):
                    node_is_problem_solution = wrapping.is_problem_solution(
                        node)
                    node_is_problem_solution_dot = wrapping.is_problem_solution_dot(
                        node)
                    if node_is_problem_solution or node_is_problem_solution_dot:
                        if node_is_problem_solution:
                            (preprocessed_node, component, truth_solution
                             ) = wrapping.solution_identify_component(node)
                            truth_problem = get_problem_from_solution(
                                truth_solution)
                            logger.log(
                                DEBUG,
                                "\tFound problem solution of truth problem " +
                                truth_problem.name() +
                                " (exact problem decorator: " +
                                str(hasattr(truth_problem, "__is_exact__")) +
                                ", component: " + str(component) + ")")
                            # Time derivative key for components and interpolator dicts
                            time_derivative = 0
                        elif node_is_problem_solution_dot:
                            (preprocessed_node, component, truth_solution_dot
                             ) = wrapping.solution_dot_identify_component(node)
                            truth_problem = get_problem_from_solution_dot(
                                truth_solution_dot)
                            logger.log(
                                DEBUG,
                                "\tFound problem solution dot of truth problem "
                                + truth_problem.name() +
                                " (exact problem decorator: " +
                                str(hasattr(truth_problem, "__is_exact__")) +
                                ", component: " + str(component) + ")")
                            # Time derivative key for components and interpolator dicts
                            time_derivative = 1
                        # Store truth problem
                        if truth_problem not in truth_problems:
                            truth_problems.append(truth_problem)
                        # Store the component
                        if truth_problem not in truth_problem_to_components[
                                time_derivative]:
                            truth_problem_to_components[time_derivative][
                                truth_problem] = list()
                        if component not in truth_problem_to_components[
                                time_derivative][truth_problem]:
                            truth_problem_to_components[time_derivative][
                                truth_problem].append(component)
                            # Get the function space corresponding to preprocessed_node on the reduced mesh
                            auxiliary_reduced_V = at.get_auxiliary_reduced_function_space(
                                truth_problem, component)
                            # Define and store the replacement
                            assert preprocessed_node not in replacements  # as it is related to a new truth solution component
                            replacements[preprocessed_node] = backend.Function(
                                auxiliary_reduced_V)
                            if time_derivative == 0:
                                if truth_problem not in truth_problem_to_reduced_mesh_solution:
                                    truth_problem_to_reduced_mesh_solution[
                                        truth_problem] = list()
                                truth_problem_to_reduced_mesh_solution[
                                    truth_problem].append(
                                        replacements[preprocessed_node])
                            elif time_derivative == 1:
                                if truth_problem not in truth_problem_to_reduced_mesh_solution_dot:
                                    truth_problem_to_reduced_mesh_solution_dot[
                                        truth_problem] = list()
                                truth_problem_to_reduced_mesh_solution_dot[
                                    truth_problem].append(
                                        replacements[preprocessed_node])
                            # Get interpolator on reduced mesh
                            if truth_problem not in truth_problem_to_reduced_mesh_interpolator[
                                    time_derivative]:
                                truth_problem_to_reduced_mesh_interpolator[
                                    time_derivative][truth_problem] = list()
                            truth_problem_to_reduced_mesh_interpolator[
                                time_derivative][truth_problem].append(
                                    at.get_auxiliary_function_interpolator(
                                        truth_problem, component))
                    else:
                        (
                            preprocessed_node, component, auxiliary_problem
                        ) = wrapping.get_auxiliary_problem_for_non_parametrized_function(
                            node)
                        logger.log(
                            DEBUG, "\tFound non parametrized function " +
                            str(preprocessed_node) +
                            " associated to auxiliary problem " +
                            str(auxiliary_problem.name()) + ", component: " +
                            str(component))
                        if preprocessed_node not in replacements:
                            # Get interpolator on reduced mesh
                            auxiliary_truth_problem_to_reduced_mesh_interpolator = at.get_auxiliary_function_interpolator(
                                auxiliary_problem, component)
                            # Define and store the replacement
                            replacements[
                                preprocessed_node] = auxiliary_truth_problem_to_reduced_mesh_interpolator(
                                    preprocessed_node)
                    # Make sure to skip any parent solution related to this one
                    visited.add(node)
                    visited.add(preprocessed_node)
                    for parent_node in wrapping.solution_iterator(
                            preprocessed_node):
                        visited.add(parent_node)
                # ... geometric quantities
                elif isinstance(node, GeometricQuantity):
                    logger.log(DEBUG,
                               "\tFound geometric quantity " + str(node))
                    if len(reduced_V) == 2:
                        assert reduced_V[0].mesh().ufl_domain(
                        ) == reduced_V[1].mesh().ufl_domain()
                    replacements[node] = type(node)(reduced_V[0].mesh())
                    visited.add(node)
                else:
                    visited.add(node)
            # ... and replace them
            replaced_form = wrapping.form_replace(form, replacements, "nodes")

            # Look for measures ...
            if len(reduced_V) == 2:
                assert reduced_V[0].mesh().ufl_domain() == reduced_V[1].mesh(
                ).ufl_domain()
            measure_reduced_domain = reduced_V[0].mesh().ufl_domain()
            replacements_measures = dict()
            for integral in wrapping.form_iterator(replaced_form, "integrals"):
                # Prepare measure for the new form (from firedrake/mg/ufl_utils.py)
                integral_subdomain_data = integral.subdomain_data()
                if integral_subdomain_data is not None:
                    integral_reduced_subdomain_data = reduced_subdomain_data[
                        integral_subdomain_data]
                else:
                    integral_reduced_subdomain_data = None
                measure = Measure(
                    integral.integral_type(),
                    domain=measure_reduced_domain,
                    subdomain_id=integral.subdomain_id(),
                    subdomain_data=integral_reduced_subdomain_data,
                    metadata=integral.metadata())
                replacements_measures[integral.integrand(),
                                      integral.integral_type(),
                                      integral.subdomain_id()] = measure
            # ... and replace them
            replaced_form_with_replaced_measures = wrapping.form_replace(
                replaced_form, replacements_measures, "measures")

            # Cache the resulting dicts
            form_cache[(form_name,
                        reduced_V)] = replaced_form_with_replaced_measures
            truth_problems_cache[(form_name, reduced_V)] = truth_problems
            truth_problem_to_components_cache[(
                form_name, reduced_V)] = truth_problem_to_components
            truth_problem_to_exact_truth_problem_cache[(
                form_name, reduced_V)] = truth_problem_to_exact_truth_problem
            truth_problem_to_reduced_mesh_solution_cache[(
                form_name, reduced_V)] = truth_problem_to_reduced_mesh_solution
            truth_problem_to_reduced_mesh_solution_dot_cache[(
                form_name,
                reduced_V)] = truth_problem_to_reduced_mesh_solution_dot
            truth_problem_to_reduced_mesh_interpolator_cache[(
                form_name,
                reduced_V)] = truth_problem_to_reduced_mesh_interpolator
            reduced_problem_to_components_cache[(
                form_name, reduced_V)] = reduced_problem_to_components
            reduced_problem_to_reduced_mesh_solution_cache[(
                form_name,
                reduced_V)] = reduced_problem_to_reduced_mesh_solution
            reduced_problem_to_reduced_mesh_solution_dot_cache[(
                form_name,
                reduced_V)] = reduced_problem_to_reduced_mesh_solution_dot
            reduced_problem_to_reduced_basis_functions_cache[(
                form_name,
                reduced_V)] = reduced_problem_to_reduced_basis_functions

        # Extract from cache
        replaced_form_with_replaced_measures = form_cache[(form_name,
                                                           reduced_V)]
        truth_problems = truth_problems_cache[(form_name, reduced_V)]
        truth_problem_to_components = truth_problem_to_components_cache[(
            form_name, reduced_V)]
        truth_problem_to_exact_truth_problem = truth_problem_to_exact_truth_problem_cache[
            (form_name, reduced_V)]
        truth_problem_to_reduced_mesh_solution = truth_problem_to_reduced_mesh_solution_cache[
            (form_name, reduced_V)]
        truth_problem_to_reduced_mesh_solution_dot = truth_problem_to_reduced_mesh_solution_dot_cache[
            (form_name, reduced_V)]
        truth_problem_to_reduced_mesh_interpolator = truth_problem_to_reduced_mesh_interpolator_cache[
            (form_name, reduced_V)]
        reduced_problem_to_components = reduced_problem_to_components_cache[(
            form_name, reduced_V)]
        reduced_problem_to_reduced_mesh_solution = reduced_problem_to_reduced_mesh_solution_cache[
            (form_name, reduced_V)]
        reduced_problem_to_reduced_mesh_solution_dot = reduced_problem_to_reduced_mesh_solution_dot_cache[
            (form_name, reduced_V)]
        reduced_problem_to_reduced_basis_functions = reduced_problem_to_reduced_basis_functions_cache[
            (form_name, reduced_V)]

        # Get list of truth and reduced problems that need to be solved, possibly updating cache
        required_truth_problems = list()
        required_reduced_problems = list()
        for truth_problem in truth_problems:
            truth_problem_is_solving = hasattr(truth_problem, "_is_solving")
            if is_training_started(truth_problem):
                reduced_problem = get_reduced_problem_from_problem(
                    truth_problem)
                reduced_problem_is_solving = hasattr(reduced_problem,
                                                     "_is_solving")
            else:
                reduced_problem = None
                reduced_problem_is_solving = False
            if not truth_problem_is_solving:
                if is_training_finished(truth_problem):
                    logger.log(
                        DEBUG, "Truth problem " + truth_problem.name() +
                        " (exact problem decorator: " +
                        str(hasattr(truth_problem, "__is_exact__")) +
                        ") is not currently solving, and its offline stage has finished: truth problem will be replaced by reduced problem"
                    )
                    # Store the replacement for solution
                    if (reduced_problem
                            not in reduced_problem_to_reduced_mesh_solution
                            and truth_problem
                            in truth_problem_to_reduced_mesh_solution):
                        reduced_problem_to_reduced_mesh_solution[
                            reduced_problem] = truth_problem_to_reduced_mesh_solution[
                                truth_problem]
                        # Store the component
                        assert reduced_problem not in reduced_problem_to_components[
                            0]
                        assert truth_problem in truth_problem_to_components[0]
                        reduced_problem_to_components[0][
                            reduced_problem] = truth_problem_to_components[0][
                                truth_problem]
                        # Get reduced problem basis functions on reduced mesh
                        assert reduced_problem not in reduced_problem_to_reduced_basis_functions[
                            0]
                        reduced_problem_to_reduced_basis_functions[0][
                            reduced_problem] = [
                                at.get_auxiliary_basis_functions_matrix(
                                    truth_problem, component) for component in
                                reduced_problem_to_components[0]
                                [reduced_problem]
                            ]
                    # Store the replacement for solution_dot
                    if (reduced_problem
                            not in reduced_problem_to_reduced_mesh_solution_dot
                            and truth_problem
                            in truth_problem_to_reduced_mesh_solution_dot):
                        reduced_problem_to_reduced_mesh_solution_dot[
                            reduced_problem] = truth_problem_to_reduced_mesh_solution_dot[
                                truth_problem]
                        # Store the component
                        assert reduced_problem not in reduced_problem_to_components[
                            1]
                        assert truth_problem in truth_problem_to_components[1]
                        reduced_problem_to_components[1][
                            reduced_problem] = truth_problem_to_components[1][
                                truth_problem]
                        # Get reduced problem basis functions on reduced mesh
                        assert reduced_problem not in reduced_problem_to_reduced_basis_functions[
                            1]
                        reduced_problem_to_reduced_basis_functions[1][
                            reduced_problem] = [
                                at.get_auxiliary_basis_functions_matrix(
                                    truth_problem, component) for component in
                                reduced_problem_to_components[1]
                                [reduced_problem]
                            ]
                    # Append to list of required reduced problems
                    required_reduced_problems.append(
                        (reduced_problem, reduced_problem_is_solving))
                else:
                    if (hasattr(truth_problem,
                                "_apply_exact_evaluation_at_stages") and
                            not hasattr(truth_problem, "_apply_EIM_at_stages")
                            and not hasattr(truth_problem,
                                            "_apply_DEIM_at_stages")):
                        logger.log(
                            DEBUG, "Truth problem " + truth_problem.name() +
                            " (exact problem decorator: " +
                            str(hasattr(truth_problem, "__is_exact__")) +
                            ") is not currently solving, its offline stage has not finished, and only @ExactParametrizedFunctions has been used: truth solve of this truth problem instance will be called"
                        )
                        # Init truth problem (if required), as it may not have been initialized
                        truth_problem.init()
                        # Append to list of required truth problems which are not currently solving
                        required_truth_problems.append(
                            (truth_problem, False, reduced_problem_is_solving))
                    else:
                        logger.log(
                            DEBUG, "Truth problem " + truth_problem.name() +
                            " (exact problem decorator: " +
                            str(hasattr(truth_problem, "__is_exact__")) +
                            ") is not currently solving, its offline stage has not finished, and either @ExactParametrizedFunctions has not been used or it has been used in combination with @DEIM or @EIM: truth solve on an auxiliary instance (with exact problem decorator) will be called, to prevent early initialization of DEIM/EIM data structures"
                        )
                        # Store the corresponding exact truth problem
                        if truth_problem not in truth_problem_to_exact_truth_problem:
                            exact_truth_problem = exact_problem(truth_problem)
                            truth_problem_to_exact_truth_problem[
                                truth_problem] = exact_truth_problem
                            # Init exact truth problem (if required), as it may not have been initialized
                            exact_truth_problem.init()
                        else:
                            exact_truth_problem = truth_problem_to_exact_truth_problem[
                                truth_problem]
                        # Store the replacement for solution
                        if (exact_truth_problem
                                not in truth_problem_to_reduced_mesh_solution
                                and truth_problem
                                in truth_problem_to_reduced_mesh_solution):
                            truth_problem_to_reduced_mesh_solution[
                                exact_truth_problem] = truth_problem_to_reduced_mesh_solution[
                                    truth_problem]
                            # Store the component
                            assert exact_truth_problem not in truth_problem_to_components[
                                0]
                            assert truth_problem in truth_problem_to_components[
                                0]
                            truth_problem_to_components[0][
                                exact_truth_problem] = truth_problem_to_components[
                                    0][truth_problem]
                            # Get interpolator on reduced mesh
                            assert exact_truth_problem not in truth_problem_to_reduced_mesh_interpolator[
                                0]
                            assert truth_problem in truth_problem_to_reduced_mesh_interpolator[
                                0]
                            truth_problem_to_reduced_mesh_interpolator[0][
                                exact_truth_problem] = truth_problem_to_reduced_mesh_interpolator[
                                    0][truth_problem]
                        # Store the replacement for solution_dot
                        if (exact_truth_problem not in
                                truth_problem_to_reduced_mesh_solution_dot
                                and truth_problem
                                in truth_problem_to_reduced_mesh_solution_dot):
                            truth_problem_to_reduced_mesh_solution_dot[
                                exact_truth_problem] = truth_problem_to_reduced_mesh_solution_dot[
                                    truth_problem]
                            # Store the component
                            assert exact_truth_problem not in truth_problem_to_components[
                                1]
                            assert truth_problem in truth_problem_to_components[
                                1]
                            truth_problem_to_components[1][
                                exact_truth_problem] = truth_problem_to_components[
                                    1][truth_problem]
                            # Get interpolator on reduced mesh
                            assert exact_truth_problem not in truth_problem_to_reduced_mesh_interpolator[
                                1]
                            assert truth_problem in truth_problem_to_reduced_mesh_interpolator[
                                1]
                            truth_problem_to_reduced_mesh_interpolator[1][
                                exact_truth_problem] = truth_problem_to_reduced_mesh_interpolator[
                                    1][truth_problem]
                        # Append to list of required truth problems which are not currently solving
                        required_truth_problems.append(
                            (exact_truth_problem, False,
                             reduced_problem_is_solving))
            else:
                logger.log(
                    DEBUG, "Truth problem " + truth_problem.name() +
                    " (exact problem decorator: " +
                    str(hasattr(truth_problem, "__is_exact__")) +
                    ") is currently solving: current truth solution will be loaded"
                )
                assert not reduced_problem_is_solving
                # Append to list of required truth problems which are currently solving
                required_truth_problems.append((truth_problem, True, False))

        # Solve truth problems (which have not been reduced yet) associated to nonlinear terms
        for (truth_problem, truth_problem_is_solving,
             reduced_problem_is_solving) in required_truth_problems:
            if not reduced_problem_is_solving:
                # Solve (if necessary)
                truth_problem.set_mu(mu)
                if not truth_problem_is_solving:
                    logger.log(
                        DEBUG, "Requiring truth problem solve for problem " +
                        truth_problem.name() + " (exact problem decorator: " +
                        str(hasattr(truth_problem, "__is_exact__")) + ")")
                    truth_problem.solve()
                else:
                    logger.log(
                        DEBUG,
                        "Loading current truth problem solution for problem " +
                        truth_problem.name() + " (exact problem decorator: " +
                        str(hasattr(truth_problem, "__is_exact__")) + ")")
            else:
                reduced_problem = get_reduced_problem_from_problem(
                    truth_problem)
                logger.log(
                    DEBUG,
                    "Replacing current truth problem solution with reduced solution for problem "
                    + reduced_problem.truth_problem.name())
            # Assign to reduced_mesh_solution
            if truth_problem in truth_problem_to_reduced_mesh_solution:
                for (reduced_mesh_solution, reduced_mesh_interpolator) in zip(
                        truth_problem_to_reduced_mesh_solution[truth_problem],
                        truth_problem_to_reduced_mesh_interpolator[0]
                    [truth_problem]):
                    solution_to = reduced_mesh_solution
                    if t is None:
                        if not reduced_problem_is_solving:
                            solution_from = reduced_mesh_interpolator(
                                truth_problem._solution)
                        else:
                            solution_from = reduced_mesh_interpolator(
                                reduced_problem.
                                basis_functions[:reduced_problem._solution.N] *
                                reduced_problem._solution)
                    else:
                        if not reduced_problem_is_solving:
                            if not truth_problem_is_solving:
                                solution_from = reduced_mesh_interpolator(
                                    truth_problem._solution_over_time.at(t))
                            else:
                                solution_from = reduced_mesh_interpolator(
                                    truth_problem._solution)
                        else:
                            solution_from = reduced_mesh_interpolator(
                                reduced_problem.
                                basis_functions[:reduced_problem._solution.N] *
                                reduced_problem._solution)
                    backend.assign(solution_to, solution_from)
            # Assign to reduced_mesh_solution_dot
            if truth_problem in truth_problem_to_reduced_mesh_solution_dot:
                for (reduced_mesh_solution_dot,
                     reduced_mesh_interpolator) in zip(
                         truth_problem_to_reduced_mesh_solution_dot[
                             truth_problem],
                         truth_problem_to_reduced_mesh_interpolator[1]
                         [truth_problem]):
                    solution_dot_to = reduced_mesh_solution_dot
                    assert t is not None
                    if not reduced_problem_is_solving:
                        if not truth_problem_is_solving:
                            solution_dot_from = reduced_mesh_interpolator(
                                truth_problem._solution_dot_over_time.at(t))
                        else:
                            solution_dot_from = reduced_mesh_interpolator(
                                truth_problem._solution_dot)
                    else:
                        solution_dot_from = reduced_mesh_interpolator(
                            reduced_problem.basis_functions[:reduced_problem.
                                                            _solution_dot.N] *
                            reduced_problem._solution_dot)
                    backend.assign(solution_dot_to, solution_dot_from)

        # Solve reduced problems associated to nonlinear terms
        for (reduced_problem, is_solving) in required_reduced_problems:
            # Solve (if necessary)
            reduced_problem.set_mu(mu)
            if not is_solving:
                logger.log(
                    DEBUG, "Requiring reduced problem solve for problem " +
                    reduced_problem.truth_problem.name())
                reduced_problem.solve()
            else:
                logger.log(
                    DEBUG,
                    "Loading current reduced problem solution for problem " +
                    reduced_problem.truth_problem.name())
            # Assign to reduced_mesh_solution
            if reduced_problem in reduced_problem_to_reduced_mesh_solution:
                for (reduced_mesh_solution, reduced_basis_functions) in zip(
                        reduced_problem_to_reduced_mesh_solution[
                            reduced_problem],
                        reduced_problem_to_reduced_basis_functions[0]
                    [reduced_problem]):
                    solution_to = reduced_mesh_solution
                    solution_from_N = OnlineSizeDict()
                    for c, v in reduced_problem._solution.N.items():
                        if c in reduced_basis_functions._components_name:
                            solution_from_N[c] = v
                    solution_from = online_backend.OnlineFunction(
                        solution_from_N)
                    if t is None or is_solving:
                        online_backend.online_assign(solution_from,
                                                     reduced_problem._solution)
                    else:
                        online_backend.online_assign(
                            solution_from,
                            reduced_problem._solution_over_time.at(t))
                    solution_from = reduced_basis_functions[:solution_from_N] * solution_from
                    backend.assign(solution_to, solution_from)
            # Assign to reduced_mesh_solution_dot
            if reduced_problem in reduced_problem_to_reduced_mesh_solution_dot:
                for (reduced_mesh_solution_dot,
                     reduced_basis_functions) in zip(
                         reduced_problem_to_reduced_mesh_solution_dot[
                             reduced_problem],
                         reduced_problem_to_reduced_basis_functions[1]
                         [reduced_problem]):
                    solution_dot_to = reduced_mesh_solution_dot
                    solution_dot_from_N = OnlineSizeDict()
                    for c, v in reduced_problem._solution_dot.N.items():
                        if c in reduced_basis_functions._components_name:
                            solution_dot_from_N[c] = v
                    solution_dot_from = online_backend.OnlineFunction(
                        solution_dot_from_N)
                    assert t is not None
                    if is_solving:
                        online_backend.online_assign(
                            solution_dot_from, reduced_problem._solution_dot)
                    else:
                        online_backend.online_assign(
                            solution_dot_from,
                            reduced_problem._solution_dot_over_time.at(t))
                    solution_dot_from = reduced_basis_functions[:
                                                                solution_dot_from_N] * solution_dot_from
                    backend.assign(solution_dot_to, solution_dot_from)

        # Assemble and return
        assembled_replaced_form = wrapping.assemble(
            replaced_form_with_replaced_measures)
        if not isinstance(assembled_replaced_form, Number):
            form_rank = assembled_replaced_form.rank()
        else:
            form_rank = 0
        return (assembled_replaced_form, form_rank)
Пример #12
0
    def _basic_form_on_truth_function_space(form_wrapper, tensor=None):
        form = form_wrapper._form
        form_name = form_wrapper.name()
        form_problem = get_problem_from_parametrized_operator(form_wrapper)
        mu = form_problem.mu
        if hasattr(form_problem, "set_time"):
            t = form_problem.t
        else:
            t = None

        if form_name not in reduced_problem_to_truth_solution_cache:
            visited = set()
            truth_problems = list()
            truth_problem_to_components = {  # outer dict index over time derivative
                0: dict(),
                1: dict()
            }
            truth_problem_to_exact_truth_problem = dict()
            truth_problem_to_truth_solution = dict()
            truth_problem_to_truth_solution_copy = dict()
            truth_problem_to_truth_solution_dot = dict()
            truth_problem_to_truth_solution_dot_copy = dict()
            reduced_problem_to_components = {  # outer dict index over time derivative
                0: dict(),
                1: dict()
            }
            reduced_problem_to_truth_solution = dict()
            reduced_problem_to_truth_solution_copy = dict()
            reduced_problem_to_truth_solution_dot = dict()
            reduced_problem_to_truth_solution_dot_copy = dict()

            # Look for terminals on truth mesh
            for node in wrapping.form_iterator(form):
                if node in visited:
                    continue
                # ... problem solutions related to nonlinear terms
                elif wrapping.is_problem_solution_type(node):
                    node_is_problem_solution = wrapping.is_problem_solution(
                        node)
                    node_is_problem_solution_dot = wrapping.is_problem_solution_dot(
                        node)
                    if node_is_problem_solution or node_is_problem_solution_dot:
                        if node_is_problem_solution:
                            (preprocessed_node, component, truth_solution
                             ) = wrapping.solution_identify_component(node)
                            truth_problem = get_problem_from_solution(
                                truth_solution)
                            if truth_problem not in truth_problems:
                                truth_problems.append(truth_problem)
                            # Store the solution
                            if truth_problem not in truth_problem_to_truth_solution:
                                truth_problem_to_truth_solution[
                                    truth_problem] = truth_solution
                                truth_problem_to_truth_solution_copy[
                                    truth_problem] = backend.copy(
                                        truth_solution)
                            else:
                                assert truth_problem_to_truth_solution[
                                    truth_problem] is truth_solution
                                assert truth_problem in truth_problem_to_truth_solution_copy
                            # Time derivative key for components dict
                            time_derivative = 0
                        elif node_is_problem_solution_dot:
                            (preprocessed_node, component, truth_solution_dot
                             ) = wrapping.solution_dot_identify_component(node)
                            truth_problem = get_problem_from_solution_dot(
                                truth_solution_dot)
                            if truth_problem not in truth_problems:
                                truth_problems.append(truth_problem)
                            # Store the solution_dot
                            if truth_problem not in truth_problem_to_truth_solution_dot:
                                truth_problem_to_truth_solution_dot[
                                    truth_problem] = truth_solution_dot
                                truth_problem_to_truth_solution_dot_copy[
                                    truth_problem] = backend.copy(
                                        truth_solution_dot)
                            else:
                                assert truth_problem_to_truth_solution_dot[
                                    truth_problem] is truth_solution_dot
                                assert truth_problem in truth_problem_to_truth_solution_dot_copy
                            # Time derivative key for components dict
                            time_derivative = 1
                        # Store truth problem
                        if truth_problem not in truth_problems:
                            truth_problems.append(truth_problem)
                        # Store the component
                        if truth_problem not in truth_problem_to_components[
                                time_derivative]:
                            truth_problem_to_components[time_derivative][
                                truth_problem] = list()
                        if component not in truth_problem_to_components[
                                time_derivative][truth_problem]:
                            truth_problem_to_components[time_derivative][
                                truth_problem].append(component)
                    else:
                        (
                            preprocessed_node, _, _
                        ) = wrapping.get_auxiliary_problem_for_non_parametrized_function(
                            node)
                    # Make sure to skip any parent solution related to this one
                    visited.add(node)
                    visited.add(preprocessed_node)
                    for parent_node in wrapping.solution_iterator(
                            preprocessed_node):
                        visited.add(parent_node)
                else:
                    visited.add(node)

            # Cache the resulting dicts
            truth_problems_cache[form_name] = truth_problems
            truth_problem_to_components_cache[
                form_name] = truth_problem_to_components
            truth_problem_to_exact_truth_problem_cache[
                form_name] = truth_problem_to_exact_truth_problem
            truth_problem_to_truth_solution_cache[
                form_name] = truth_problem_to_truth_solution
            truth_problem_to_truth_solution_copy_cache[
                form_name] = truth_problem_to_truth_solution_copy
            truth_problem_to_truth_solution_dot_cache[
                form_name] = truth_problem_to_truth_solution_dot
            truth_problem_to_truth_solution_dot_copy_cache[
                form_name] = truth_problem_to_truth_solution_dot_copy
            reduced_problem_to_components_cache[
                form_name] = reduced_problem_to_components
            reduced_problem_to_truth_solution_cache[
                form_name] = reduced_problem_to_truth_solution
            reduced_problem_to_truth_solution_copy_cache[
                form_name] = reduced_problem_to_truth_solution_copy
            reduced_problem_to_truth_solution_dot_cache[
                form_name] = reduced_problem_to_truth_solution_dot
            reduced_problem_to_truth_solution_dot_copy_cache[
                form_name] = reduced_problem_to_truth_solution_dot_copy

        # Extract from cache
        truth_problems = truth_problems_cache[form_name]
        truth_problem_to_components = truth_problem_to_components_cache[
            form_name]
        truth_problem_to_exact_truth_problem = truth_problem_to_exact_truth_problem_cache[
            form_name]
        truth_problem_to_truth_solution = truth_problem_to_truth_solution_cache[
            form_name]
        truth_problem_to_truth_solution_copy = truth_problem_to_truth_solution_copy_cache[
            form_name]
        truth_problem_to_truth_solution_dot = truth_problem_to_truth_solution_dot_cache[
            form_name]
        truth_problem_to_truth_solution_dot_copy = truth_problem_to_truth_solution_dot_copy_cache[
            form_name]
        reduced_problem_to_components = reduced_problem_to_components_cache[
            form_name]
        reduced_problem_to_truth_solution = reduced_problem_to_truth_solution_cache[
            form_name]
        reduced_problem_to_truth_solution_copy = reduced_problem_to_truth_solution_copy_cache[
            form_name]
        reduced_problem_to_truth_solution_dot = reduced_problem_to_truth_solution_dot_cache[
            form_name]
        reduced_problem_to_truth_solution_dot_copy = reduced_problem_to_truth_solution_dot_copy_cache[
            form_name]

        # Get list of truth and reduced problems that need to be solved, possibly updating cache
        required_truth_problems = list()
        required_reduced_problems = list()
        for truth_problem in truth_problems:
            truth_problem_is_solving = hasattr(truth_problem, "_is_solving")
            if is_training_started(truth_problem):
                reduced_problem = get_reduced_problem_from_problem(
                    truth_problem)
                reduced_problem_is_solving = hasattr(reduced_problem,
                                                     "_is_solving")
            else:
                reduced_problem = None
                reduced_problem_is_solving = False
            if not truth_problem_is_solving:
                if is_training_finished(truth_problem):
                    # Store the solution
                    if (reduced_problem
                            not in reduced_problem_to_truth_solution and
                            truth_problem in truth_problem_to_truth_solution):
                        reduced_problem_to_truth_solution[
                            reduced_problem] = truth_problem_to_truth_solution[
                                truth_problem]
                        assert reduced_problem not in reduced_problem_to_truth_solution_copy
                        assert truth_problem in truth_problem_to_truth_solution_copy
                        reduced_problem_to_truth_solution_copy[
                            reduced_problem] = truth_problem_to_truth_solution_copy[
                                truth_problem]
                        # Store the component
                        assert reduced_problem not in reduced_problem_to_components
                        assert truth_problem in truth_problem_to_components[0]
                        reduced_problem_to_components[0][
                            reduced_problem] = truth_problem_to_components[0][
                                truth_problem]
                    # Store the solution_dot
                    if (reduced_problem
                            not in reduced_problem_to_truth_solution_dot
                            and truth_problem
                            in truth_problem_to_truth_solution_dot):
                        reduced_problem_to_truth_solution_dot[
                            reduced_problem] = truth_problem_to_truth_solution_dot[
                                truth_problem]
                        assert reduced_problem not in reduced_problem_to_truth_solution_dot_copy
                        assert truth_problem in truth_problem_to_truth_solution_dot_copy
                        reduced_problem_to_truth_solution_dot_copy[
                            reduced_problem] = truth_problem_to_truth_solution_dot_copy[
                                truth_problem]
                        # Store the component
                        assert reduced_problem not in reduced_problem_to_components
                        assert truth_problem in truth_problem_to_components[1]
                        reduced_problem_to_components[1][
                            reduced_problem] = truth_problem_to_components[1][
                                truth_problem]
                    # Append to list of required reduced problems
                    required_reduced_problems.append(
                        (reduced_problem, reduced_problem_is_solving))
                else:
                    if (hasattr(truth_problem,
                                "_apply_exact_evaluation_at_stages") and
                            not hasattr(truth_problem, "_apply_EIM_at_stages")
                            and not hasattr(truth_problem,
                                            "_apply_DEIM_at_stages")):
                        # Init truth problem (if required), as it may not have been initialized
                        truth_problem.init()
                        # Append to list of required truth problems which are not currently solving
                        required_truth_problems.append(
                            (truth_problem, False, reduced_problem_is_solving))
                    else:
                        # Store the corresponding exact truth problem
                        if truth_problem not in truth_problem_to_exact_truth_problem:
                            exact_truth_problem = exact_problem(truth_problem)
                            truth_problem_to_exact_truth_problem[
                                truth_problem] = exact_truth_problem
                            # Init exact truth problem (if required), as it may not have been initialized
                            exact_truth_problem.init()
                        else:
                            exact_truth_problem = truth_problem_to_exact_truth_problem[
                                truth_problem]
                        # Store the solution
                        if (exact_truth_problem
                                not in truth_problem_to_truth_solution
                                and truth_problem
                                in truth_problem_to_truth_solution):
                            truth_problem_to_truth_solution[
                                exact_truth_problem] = truth_problem_to_truth_solution[
                                    truth_problem]
                            assert exact_truth_problem not in truth_problem_to_truth_solution_copy
                            assert truth_problem in truth_problem_to_truth_solution_copy
                            truth_problem_to_truth_solution_copy[
                                exact_truth_problem] = truth_problem_to_truth_solution_copy[
                                    truth_problem]
                            # Store the component
                            assert exact_truth_problem not in truth_problem_to_components[
                                0]
                            assert truth_problem in truth_problem_to_components[
                                0]
                            truth_problem_to_components[0][
                                exact_truth_problem] = truth_problem_to_components[
                                    0][truth_problem]
                        # Store the solution_dot
                        if (exact_truth_problem
                                not in truth_problem_to_truth_solution_dot
                                and truth_problem
                                in truth_problem_to_truth_solution_dot):
                            truth_problem_to_truth_solution_dot[
                                exact_truth_problem] = truth_problem_to_truth_solution_dot[
                                    truth_problem]
                            assert exact_truth_problem not in truth_problem_to_truth_solution_dot_copy
                            assert truth_problem in truth_problem_to_truth_solution_dot_copy
                            truth_problem_to_truth_solution_dot_copy[
                                exact_truth_problem] = truth_problem_to_truth_solution_dot_copy[
                                    truth_problem]
                            # Store the component
                            assert exact_truth_problem not in truth_problem_to_components[
                                1]
                            assert truth_problem in truth_problem_to_components[
                                1]
                            truth_problem_to_components[1][
                                exact_truth_problem] = truth_problem_to_components[
                                    1][truth_problem]
                        # Append to list of required truth problems which are not currently solving
                        required_truth_problems.append(
                            (exact_truth_problem, False,
                             reduced_problem_is_solving))
            else:
                assert not reduced_problem_is_solving
                # Append to list of required truth problems which are currently solving
                required_truth_problems.append((truth_problem, True, False))

        # Solve truth problems (which have not been reduced yet) associated to nonlinear terms
        for (truth_problem, truth_problem_is_solving,
             reduced_problem_is_solving) in required_truth_problems:
            if not reduced_problem_is_solving:
                # Solve (if necessary)
                truth_problem.set_mu(mu)
                if not truth_problem_is_solving:
                    log(
                        PROGRESS,
                        "In form_on_truth_function_space, requiring truth problem solve for problem "
                        + truth_problem.name())
                    truth_problem.solve()
                else:
                    log(
                        PROGRESS,
                        "In form_on_truth_function_space, loading current truth problem solution for problem "
                        + truth_problem.name())
            else:
                reduced_problem = get_reduced_problem_from_problem(
                    truth_problem)
                log(
                    PROGRESS,
                    "In form_on_truth_function_space, replacing current truth problem solution with reduced solution for problem "
                    + reduced_problem.truth_problem.name())
            # Assign to truth_solution
            if truth_problem in truth_problem_to_truth_solution:
                truth_solution = truth_problem_to_truth_solution[truth_problem]
                backend.assign(
                    truth_problem_to_truth_solution_copy[truth_problem],
                    truth_solution)
                for component in truth_problem_to_components[0][truth_problem]:
                    solution_to = _sub_from_tuple(truth_solution, component)
                    if t is None:
                        if not reduced_problem_is_solving:
                            solution_from = _sub_from_tuple(
                                truth_problem._solution, component)
                        else:
                            solution_from = _sub_from_tuple(
                                reduced_problem.
                                basis_functions[:reduced_problem._solution.N] *
                                reduced_problem._solution, component)
                    else:
                        if not reduced_problem_is_solving:
                            if not truth_problem_is_solving:
                                solution_from = _sub_from_tuple(
                                    truth_problem._solution_over_time.at(t),
                                    component)
                            else:
                                solution_from = _sub_from_tuple(
                                    truth_problem._solution, component)
                        else:
                            solution_from = _sub_from_tuple(
                                reduced_problem.
                                basis_functions[:reduced_problem._solution.N] *
                                reduced_problem._solution, component)
                    backend.assign(solution_to, solution_from)
            # Assign to truth_solution_dot
            if truth_problem in truth_problem_to_truth_solution_dot:
                truth_solution_dot = truth_problem_to_truth_solution_dot[
                    truth_problem]
                backend.assign(
                    truth_problem_to_truth_solution_dot_copy[truth_problem],
                    truth_solution_dot)
                for component in truth_problem_to_components[1][truth_problem]:
                    solution_dot_to = _sub_from_tuple(truth_solution_dot,
                                                      component)
                    assert t is not None
                    if not reduced_problem_is_solving:
                        if not truth_problem_is_solving:
                            solution_dot_from = _sub_from_tuple(
                                truth_problem._solution_dot_over_time.at(t),
                                component)
                        else:
                            solution_dot_from = _sub_from_tuple(
                                truth_problem._solution_dot, component)
                    else:
                        solution_dot_from = _sub_from_tuple(
                            reduced_problem.basis_functions[:reduced_problem.
                                                            _solution_dot.N] *
                            reduced_problem._solution_dot, component)
                    backend.assign(solution_dot_to, solution_dot_from)

        # Solve reduced problems associated to nonlinear terms
        for (reduced_problem, is_solving) in required_reduced_problems:
            # Solve (if necessary)
            reduced_problem.set_mu(mu)
            if not is_solving:
                log(
                    PROGRESS,
                    "In form_on_truth_function_space, requiring reduced problem solve for problem "
                    + reduced_problem.truth_problem.name())
                reduced_problem.solve()
            else:
                log(
                    PROGRESS,
                    "In form_on_truth_function_space, loading current reduced problem solution for problem "
                    + reduced_problem.truth_problem.name())
            # Assign to truth_solution
            if reduced_problem in reduced_problem_to_truth_solution:
                truth_solution = reduced_problem_to_truth_solution[
                    reduced_problem]
                backend.assign(
                    reduced_problem_to_truth_solution_copy[reduced_problem],
                    truth_solution)
                for component in reduced_problem_to_components[0][
                        reduced_problem]:
                    solution_to = _sub_from_tuple(truth_solution, component)
                    if t is None or is_solving:
                        solution_from = _sub_from_tuple(
                            reduced_problem.basis_functions[:reduced_problem.
                                                            _solution.N] *
                            reduced_problem._solution, component)
                    else:
                        solution_from = _sub_from_tuple(
                            reduced_problem.basis_functions[:reduced_problem.
                                                            _solution.N] *
                            reduced_problem._solution_over_time.at(t),
                            component)
                    backend.assign(solution_to, solution_from)
            # Assign to truth_solution_dot
            if reduced_problem in reduced_problem_to_truth_solution_dot:
                truth_solution_dot = reduced_problem_to_truth_solution_dot[
                    reduced_problem]
                backend.assign(
                    reduced_problem_to_truth_solution_dot_copy[
                        reduced_problem], truth_solution_dot)
                for component in reduced_problem_to_components[1][
                        reduced_problem]:
                    solution_dot_to = _sub_from_tuple(truth_solution_dot,
                                                      component)
                    assert t is not None
                    if is_solving:
                        solution_dot_from = _sub_from_tuple(
                            reduced_problem.basis_functions[:reduced_problem.
                                                            _solution_dot.N] *
                            reduced_problem._solution_dot, component)
                    else:
                        solution_dot_from = _sub_from_tuple(
                            reduced_problem.basis_functions[:reduced_problem.
                                                            _solution_dot.N] *
                            reduced_problem._solution_dot_over_time.at(t),
                            component)
                    backend.assign(solution_dot_to, solution_dot_from)

        # Assemble
        assembled_form = wrapping.assemble(form, tensor)
        if not isinstance(assembled_form, Number):
            assembled_form.generator = form_wrapper  # for I/O
            form_rank = assembled_form.rank()
        else:
            form_rank = 0

        # Undo any side effect of truth problem solves
        for (truth_problem, _, _) in required_truth_problems:
            if truth_problem in truth_problem_to_truth_solution:
                truth_solution = truth_problem_to_truth_solution[truth_problem]
                truth_solution_copy = truth_problem_to_truth_solution_copy[
                    truth_problem]
                for component in truth_problem_to_components[0][truth_problem]:
                    solution_to = _sub_from_tuple(truth_solution, component)
                    solution_from = _sub_from_tuple(truth_solution_copy,
                                                    component)
                    backend.assign(solution_to, solution_from)
            if truth_problem in truth_problem_to_truth_solution_dot:
                truth_solution_dot = truth_problem_to_truth_solution_dot[
                    truth_problem]
                truth_solution_dot_copy = truth_problem_to_truth_solution_dot_copy[
                    truth_problem]
                for component in truth_problem_to_components[1][truth_problem]:
                    solution_dot_to = _sub_from_tuple(truth_solution_dot,
                                                      component)
                    solution_dot_from = _sub_from_tuple(
                        truth_solution_dot_copy, component)
                    backend.assign(solution_dot_to, solution_dot_from)

        # Undo any side effect of reduced problem solves
        for (reduced_problem, _) in required_reduced_problems:
            if reduced_problem in reduced_problem_to_truth_solution:
                truth_solution = reduced_problem_to_truth_solution[
                    reduced_problem]
                truth_solution_copy = reduced_problem_to_truth_solution_copy[
                    reduced_problem]
                for component in reduced_problem_to_components[0][
                        reduced_problem]:
                    solution_to = _sub_from_tuple(truth_solution, component)
                    solution_from = _sub_from_tuple(truth_solution_copy,
                                                    component)
                    backend.assign(solution_to, solution_from)
            if reduced_problem in reduced_problem_to_truth_solution_dot:
                truth_solution_dot = reduced_problem_to_truth_solution_dot[
                    reduced_problem]
                truth_solution_dot_copy = reduced_problem_to_truth_solution_dot_copy[
                    reduced_problem]
                for component in reduced_problem_to_components[1][
                        reduced_problem]:
                    solution_dot_to = _sub_from_tuple(truth_solution_dot,
                                                      component)
                    solution_dot_from = _sub_from_tuple(
                        truth_solution_dot_copy, component)
                    backend.assign(solution_dot_to, solution_dot_from)

        # Return
        return (assembled_form, form_rank)
Пример #13
0
    def _basic_form_on_reduced_function_space(form_wrapper, at):
        form = form_wrapper._form
        form_name = form_wrapper.name()
        mu = get_problem_from_parametrized_operator(form_wrapper).mu
        reduced_V = at.get_reduced_function_spaces()
        reduced_subdomain_data = at.get_reduced_subdomain_data()

        if (form_name,
                reduced_V) not in form_on_reduced_function_space__form_cache:
            visited = set()
            replacements = dict()
            truth_problems = list()
            truth_problem_to_components = dict()
            truth_problem_to_exact_truth_problem = dict()
            truth_problem_to_reduced_mesh_solution = dict()
            truth_problem_to_reduced_mesh_interpolator = dict()
            reduced_problem_to_components = dict()
            reduced_problem_to_reduced_mesh_solution = dict()
            reduced_problem_to_reduced_basis_functions = dict()

            # Look for terminals on truth mesh
            for node in wrapping.form_iterator(form, "nodes"):
                if node in visited:
                    continue
                # ... test and trial functions
                elif isinstance(node, Argument):
                    replacements[node] = wrapping.form_argument_replace(
                        node, reduced_V)
                    visited.add(node)
                # ... problem solutions related to nonlinear terms
                elif wrapping.is_problem_solution_or_problem_solution_component_type(
                        node):
                    if wrapping.is_problem_solution_or_problem_solution_component(
                            node):
                        (preprocessed_node, component, truth_solution
                         ) = wrapping.solution_identify_component(node)
                        truth_problem = get_problem_from_solution(
                            truth_solution)
                        truth_problems.append(truth_problem)
                        # Store the component
                        if truth_problem not in truth_problem_to_components:
                            truth_problem_to_components[truth_problem] = list()
                        truth_problem_to_components[truth_problem].append(
                            component)
                        # Get the function space corresponding to preprocessed_node on the reduced mesh
                        auxiliary_reduced_V = at.get_auxiliary_reduced_function_space(
                            truth_problem, component)
                        # Define and store the replacement
                        if truth_problem not in truth_problem_to_reduced_mesh_solution:
                            truth_problem_to_reduced_mesh_solution[
                                truth_problem] = list()
                        replacements[preprocessed_node] = backend.Function(
                            auxiliary_reduced_V)
                        truth_problem_to_reduced_mesh_solution[
                            truth_problem].append(
                                replacements[preprocessed_node])
                        # Get interpolator on reduced mesh
                        if truth_problem not in truth_problem_to_reduced_mesh_interpolator:
                            truth_problem_to_reduced_mesh_interpolator[
                                truth_problem] = list()
                        truth_problem_to_reduced_mesh_interpolator[
                            truth_problem].append(
                                at.get_auxiliary_function_interpolator(
                                    truth_problem, component))
                    else:
                        (
                            auxiliary_problem, component
                        ) = wrapping.get_auxiliary_problem_for_non_parametrized_function(
                            node)
                        preprocessed_node = node
                        # Get the function space corresponding to preprocessed_node on the reduced mesh
                        auxiliary_reduced_V = at.get_auxiliary_reduced_function_space(
                            auxiliary_problem, component)
                        # Get interpolator on reduced mesh
                        auxiliary_truth_problem_to_reduced_mesh_interpolator = at.get_auxiliary_function_interpolator(
                            auxiliary_problem, component)
                        # Define and store the replacement
                        replacements[
                            preprocessed_node] = auxiliary_truth_problem_to_reduced_mesh_interpolator(
                                preprocessed_node)
                    # Make sure to skip any parent solution related to this one
                    visited.add(node)
                    visited.add(preprocessed_node)
                    for parent_node in wrapping.solution_iterator(
                            preprocessed_node):
                        visited.add(parent_node)
                # ... geometric quantities
                elif isinstance(node, GeometricQuantity):
                    if len(reduced_V) == 2:
                        assert reduced_V[0].mesh().ufl_domain(
                        ) == reduced_V[1].mesh().ufl_domain()
                    replacements[node] = type(node)(reduced_V[0].mesh())
                    visited.add(node)
            # ... and replace them
            replaced_form = wrapping.form_replace(form, replacements, "nodes")

            # Look for measures ...
            if len(reduced_V) == 2:
                assert reduced_V[0].mesh().ufl_domain() == reduced_V[1].mesh(
                ).ufl_domain()
            measure_reduced_domain = reduced_V[0].mesh().ufl_domain()
            replacements_measures = dict()
            for integral in wrapping.form_iterator(replaced_form, "integrals"):
                # Prepare measure for the new form (from firedrake/mg/ufl_utils.py)
                integral_subdomain_data = integral.subdomain_data()
                if integral_subdomain_data is not None:
                    integral_reduced_subdomain_data = reduced_subdomain_data[
                        integral_subdomain_data]
                else:
                    integral_reduced_subdomain_data = None
                measure = Measure(
                    integral.integral_type(),
                    domain=measure_reduced_domain,
                    subdomain_id=integral.subdomain_id(),
                    subdomain_data=integral_reduced_subdomain_data,
                    metadata=integral.metadata())
                replacements_measures[integral.integrand(),
                                      integral.integral_type(),
                                      integral.subdomain_id()] = measure
            # ... and replace them
            replaced_form_with_replaced_measures = wrapping.form_replace(
                replaced_form, replacements_measures, "measures")

            # Cache the resulting dicts
            form_on_reduced_function_space__form_cache[(
                form_name, reduced_V)] = replaced_form_with_replaced_measures
            form_on_reduced_function_space__truth_problems_cache[(
                form_name, reduced_V)] = truth_problems
            form_on_reduced_function_space__truth_problem_to_components_cache[(
                form_name, reduced_V)] = truth_problem_to_components
            form_on_reduced_function_space__truth_problem_to_exact_truth_problem_cache[
                (form_name, reduced_V)] = truth_problem_to_exact_truth_problem
            form_on_reduced_function_space__truth_problem_to_reduced_mesh_solution_cache[
                (form_name,
                 reduced_V)] = truth_problem_to_reduced_mesh_solution
            form_on_reduced_function_space__truth_problem_to_reduced_mesh_interpolator_cache[
                (form_name,
                 reduced_V)] = truth_problem_to_reduced_mesh_interpolator
            form_on_reduced_function_space__reduced_problem_to_components_cache[
                (form_name, reduced_V)] = reduced_problem_to_components
            form_on_reduced_function_space__reduced_problem_to_reduced_mesh_solution_cache[
                (form_name,
                 reduced_V)] = reduced_problem_to_reduced_mesh_solution
            form_on_reduced_function_space__reduced_problem_to_reduced_basis_functions_cache[
                (form_name,
                 reduced_V)] = reduced_problem_to_reduced_basis_functions

        # Extract from cache
        replaced_form_with_replaced_measures = form_on_reduced_function_space__form_cache[
            (form_name, reduced_V)]
        truth_problems = form_on_reduced_function_space__truth_problems_cache[(
            form_name, reduced_V)]
        truth_problem_to_components = form_on_reduced_function_space__truth_problem_to_components_cache[
            (form_name, reduced_V)]
        truth_problem_to_exact_truth_problem = form_on_reduced_function_space__truth_problem_to_exact_truth_problem_cache[
            (form_name, reduced_V)]
        truth_problem_to_reduced_mesh_solution = form_on_reduced_function_space__truth_problem_to_reduced_mesh_solution_cache[
            (form_name, reduced_V)]
        truth_problem_to_reduced_mesh_interpolator = form_on_reduced_function_space__truth_problem_to_reduced_mesh_interpolator_cache[
            (form_name, reduced_V)]
        reduced_problem_to_components = form_on_reduced_function_space__reduced_problem_to_components_cache[
            (form_name, reduced_V)]
        reduced_problem_to_reduced_mesh_solution = form_on_reduced_function_space__reduced_problem_to_reduced_mesh_solution_cache[
            (form_name, reduced_V)]
        reduced_problem_to_reduced_basis_functions = form_on_reduced_function_space__reduced_problem_to_reduced_basis_functions_cache[
            (form_name, reduced_V)]

        # Get list of truth and reduced problems that need to be solved, possibly updating cache
        required_truth_problems = list()
        required_reduced_problems = list()
        for truth_problem in truth_problems:
            truth_problem_is_solving = hasattr(truth_problem, "_is_solving")
            if is_training_started(truth_problem):
                reduced_problem = get_reduced_problem_from_problem(
                    truth_problem)
                reduced_problem_is_solving = hasattr(reduced_problem,
                                                     "_is_solving")
            else:
                reduced_problem = None
                reduced_problem_is_solving = False
            if not truth_problem_is_solving:
                if is_training_finished(truth_problem):
                    # Store the component
                    if reduced_problem not in reduced_problem_to_components:
                        reduced_problem_to_components[
                            reduced_problem] = truth_problem_to_components[
                                truth_problem]
                    # Store the replacement
                    if reduced_problem not in reduced_problem_to_reduced_mesh_solution:
                        reduced_problem_to_reduced_mesh_solution[
                            reduced_problem] = truth_problem_to_reduced_mesh_solution[
                                truth_problem]
                    # Get reduced problem basis functions on reduced mesh
                    if reduced_problem not in reduced_problem_to_reduced_basis_functions:
                        reduced_problem_to_reduced_basis_functions[
                            reduced_problem] = list()
                        for component in reduced_problem_to_components[
                                reduced_problem]:
                            reduced_problem_to_reduced_basis_functions[
                                reduced_problem].append(
                                    at.get_auxiliary_basis_functions_matrix(
                                        truth_problem, reduced_problem,
                                        component))
                    # Append to list of required reduced problems
                    required_reduced_problems.append(
                        (reduced_problem, reduced_problem_is_solving))
                else:
                    if (hasattr(truth_problem,
                                "_apply_exact_evaluation_at_stages") and
                            not hasattr(truth_problem, "_apply_EIM_at_stages")
                            and not hasattr(truth_problem,
                                            "_apply_DEIM_at_stages")):
                        # Init truth problem (if required), as it may not have been initialized
                        truth_problem.init()
                        # Append to list of required truth problems which are not currently solving
                        required_truth_problems.append(
                            (truth_problem, False, reduced_problem_is_solving))
                    else:
                        # Store the corresponding exact truth problem
                        if truth_problem not in truth_problem_to_exact_truth_problem:
                            exact_truth_problem = exact_problem(truth_problem)
                            truth_problem_to_exact_truth_problem[
                                truth_problem] = exact_truth_problem
                            # Init exact truth problem (if required), as it may not have been initialized
                            exact_truth_problem.init()
                        else:
                            exact_truth_problem = truth_problem_to_exact_truth_problem[
                                truth_problem]
                        # Store the component
                        if exact_truth_problem not in truth_problem_to_components:
                            truth_problem_to_components[
                                exact_truth_problem] = truth_problem_to_components[
                                    truth_problem]
                        # Store the replacement
                        if exact_truth_problem not in truth_problem_to_reduced_mesh_solution:
                            truth_problem_to_reduced_mesh_solution[
                                exact_truth_problem] = truth_problem_to_reduced_mesh_solution[
                                    truth_problem]
                        # Get interpolator on reduced mesh
                        if exact_truth_problem not in truth_problem_to_reduced_mesh_interpolator:
                            truth_problem_to_reduced_mesh_interpolator[
                                exact_truth_problem] = list()
                            for component in truth_problem_to_components[
                                    exact_truth_problem]:
                                truth_problem_to_reduced_mesh_interpolator[
                                    exact_truth_problem].append(
                                        at.get_auxiliary_function_interpolator(
                                            exact_truth_problem, component))
                        # Append to list of required truth problems which are not currently solving
                        required_truth_problems.append(
                            (exact_truth_problem, False,
                             reduced_problem_is_solving))
            else:
                assert not reduced_problem_is_solving
                # Append to list of required truth problems which are currently solving
                required_truth_problems.append((truth_problem, True, False))

        # Solve truth problems (which have not been reduced yet) associated to nonlinear terms
        for (truth_problem, truth_problem_is_solving,
             reduced_problem_is_solving) in required_truth_problems:
            if not reduced_problem_is_solving:
                # Solve (if necessary) ...
                truth_problem.set_mu(mu)
                if not truth_problem_is_solving:
                    log(
                        PROGRESS,
                        "In form_on_reduced_function_space, requiring truth problem solve for problem "
                        + truth_problem.name())
                    truth_problem.solve()
                else:
                    log(
                        PROGRESS,
                        "In form_on_reduced_function_space, loading current truth problem solution for problem "
                        + truth_problem.name())
            else:
                reduced_problem = get_reduced_problem_from_problem(
                    truth_problem)
                log(
                    PROGRESS,
                    "In form_on_reduced_function_space, replacing current truth problem solution with reduced solution for problem "
                    + reduced_problem.truth_problem.name())
            # ... and assign to reduced_mesh_solution
            for (reduced_mesh_solution, reduced_mesh_interpolator) in zip(
                    truth_problem_to_reduced_mesh_solution[truth_problem],
                    truth_problem_to_reduced_mesh_interpolator[truth_problem]):
                solution_to = reduced_mesh_solution
                if not reduced_problem_is_solving:
                    solution_from = reduced_mesh_interpolator(
                        truth_problem._solution)
                else:
                    solution_from = reduced_mesh_interpolator(
                        reduced_problem.basis_functions[:reduced_problem.
                                                        _solution.N] *
                        reduced_problem._solution)
                backend.assign(solution_to, solution_from)

        # Solve reduced problems associated to nonlinear terms
        for (reduced_problem, is_solving) in required_reduced_problems:
            # Solve (if necessary) ...
            reduced_problem.set_mu(mu)
            if not is_solving:
                log(
                    PROGRESS,
                    "In form_on_reduced_function_space, requiring reduced problem solve for problem "
                    + reduced_problem.truth_problem.name())
                reduced_problem.solve()
            else:
                log(
                    PROGRESS,
                    "In form_on_reduced_function_space, loading current reduced problem solution for problem "
                    + reduced_problem.truth_problem.name())
            # ... and assign to reduced_mesh_solution
            for (reduced_mesh_solution, reduced_basis_functions) in zip(
                    reduced_problem_to_reduced_mesh_solution[reduced_problem],
                    reduced_problem_to_reduced_basis_functions[reduced_problem]
            ):
                solution_to = reduced_mesh_solution
                solution_from_N = OnlineSizeDict()
                for c, v in reduced_problem._solution.N.items():
                    if c in reduced_basis_functions._components_name:
                        solution_from_N[c] = v
                solution_from = online_backend.OnlineFunction(solution_from_N)
                online_backend.online_assign(solution_from,
                                             reduced_problem._solution)
                solution_from = reduced_basis_functions[:solution_from_N] * solution_from
                backend.assign(solution_to, solution_from)

        # Assemble and return
        assembled_replaced_form = wrapping.assemble(
            replaced_form_with_replaced_measures)
        form_rank = assembled_replaced_form.rank()
        return (assembled_replaced_form, form_rank)