Example #1
0
    class _AssimuloIDA(object):
        def __init__(self, residual_eval, solution, solution_dot, bc_eval,
                     jacobian_eval, set_time):
            self.residual_eval = residual_eval
            self.solution = solution
            self.solution_dot = solution_dot
            self.bc_eval = bc_eval
            self.jacobian_eval = jacobian_eval
            self.set_time = set_time
            # We should be solving a square system
            self.sample_residual = residual_eval(0, self.solution,
                                                 self.solution_dot)
            self.sample_jacobian = jacobian_eval(0, self.solution,
                                                 self.solution_dot, 0.)
            assert self.sample_jacobian.M == self.sample_jacobian.N
            assert self.sample_jacobian.N == self.sample_residual.N
            # Storage for current BC
            self.current_bc = None
            # Additional storage which will be setup by set_parameters
            self._absolute_tolerance = None
            self._final_time = None
            self._initial_time = 0.
            self._max_time_steps = None
            self._monitor_callback = None
            self._monitor_initial_time = None
            self._monitor_time_step_size = None
            self._relative_tolerance = None
            self._report = False
            self._time_step_size = None

        def _update_bcs(self, t):
            # Update current bc
            bcs_t = self.bc_eval(t)
            assert isinstance(bcs_t, (tuple, dict))
            if isinstance(bcs_t, tuple):
                self.current_bc = DirichletBC(bcs_t)
            elif isinstance(bcs_t, dict):
                self.current_bc = DirichletBC(
                    bcs_t, self.sample_residual.
                    _component_name_to_basis_component_index,
                    self.solution.vector().N)
            else:
                raise TypeError("Invalid bc in _LinearSolver.__init__().")

        def _residual_vector_eval(self, t, solution, solution_dot):
            # Store current time
            self.set_time(t)
            # Store solution and solution_dot
            self.solution.vector()[:] = solution
            self.solution_dot.vector()[:] = solution_dot
            # Compute residual
            residual_vector = self.residual_eval(t, self.solution,
                                                 self.solution_dot)
            # Apply BCs, if necessary
            if self.bc_eval is not None:
                self._update_bcs(t)
                self.current_bc.apply_to_vector(residual_vector,
                                                self.solution.vector())
            # Convert to an array, rather than a matrix with one column, and return
            return residual_vector.__array__()

        def _jacobian_matrix_eval(self, solution_dot_coefficient, t, solution,
                                  solution_dot):
            # Store current time
            self.set_time(t)
            # Store solution and solution_dot
            self.solution.vector()[:] = solution
            self.solution_dot.vector()[:] = solution_dot
            # Compute jacobian
            jacobian_matrix = self.jacobian_eval(t, self.solution,
                                                 self.solution_dot,
                                                 solution_dot_coefficient)
            # Apply BCs, if necessary
            if self.bc_eval is not None:
                self._update_bcs(t)
                self.current_bc.apply_to_matrix(jacobian_matrix)
            # Return
            return jacobian_matrix.__array__()

        def _monitor(self, solver, t, solution, solution_dot):
            # Store solution and solution_dot
            self.solution.vector()[:] = solution
            self.solution_dot.vector()[:] = solution_dot
            # Call monitor
            if self._monitor_callback is not None:
                self._monitor_callback(t, self.solution, self.solution_dot)

        def set_parameters(self, parameters):
            for (key, value) in parameters.items():
                if key == "absolute_tolerance":
                    self._absolute_tolerance = value
                elif key == "final_time":
                    self._final_time = value
                elif key == "initial_time":
                    self._initial_time = value
                elif key == "integrator_type":
                    assert value == "ida"
                elif key == "max_time_steps":
                    self._max_time_steps = value
                elif key == "monitor":
                    assert isinstance(value, dict)
                    assert all(key_monitor in ("initial_time",
                                               "time_step_size")
                               for key_monitor in value)
                    if "initial_time" in value:
                        self._monitor_initial_time = value["initial_time"]
                    if "time_step_size" in value:
                        self._monitor_time_step_size = value["time_step_size"]
                elif key == "nonlinear_solver":
                    for (key_nonlinear, value_nonlinear) in value.items():
                        if key_nonlinear == "absolute_tolerance":
                            raise NotImplementedError(
                                "This feature has not been implemented in IDA."
                            )
                        elif key_nonlinear == "line_search":
                            raise NotImplementedError(
                                "This feature has not been implemented in IDA."
                            )
                        elif key_nonlinear == "maximum_iterations":
                            raise NotImplementedError(
                                "This feature has not been implemented in IDA."
                            )
                        elif key_nonlinear == "relative_tolerance":
                            raise NotImplementedError(
                                "This feature has not been implemented in IDA."
                            )
                        elif key_nonlinear == "report":
                            raise NotImplementedError(
                                "This feature has not been implemented in IDA."
                            )
                        elif key_nonlinear == "solution_tolerance":
                            raise NotImplementedError(
                                "This feature has not been implemented in IDA."
                            )
                        else:
                            raise ValueError(
                                "Invalid paramater passed to _AssimuloIDA object."
                            )
                elif key == "problem_type":
                    pass
                elif key == "relative_tolerance":
                    self._relative_tolerance = value
                elif key == "report":
                    self._report = True
                elif key == "time_step_size":
                    self._time_step_size = value
                else:
                    raise ValueError(
                        "Invalid paramater passed to _AssimuloIDA object.")

        def solve(self):
            # Setup IDA
            assert self._initial_time is not None
            problem = Implicit_Problem(self._residual_vector_eval,
                                       self.solution.vector(),
                                       self.solution_dot.vector(),
                                       self._initial_time)
            problem.jac = self._jacobian_matrix_eval
            problem.handle_result = self._monitor
            # Define an Assimulo IDA solver
            solver = IDA(problem)
            # Setup options
            assert self._time_step_size is not None
            solver.inith = self._time_step_size
            if self._absolute_tolerance is not None:
                solver.atol = self._absolute_tolerance
            if self._max_time_steps is not None:
                solver.maxsteps = self._max_time_steps
            if self._relative_tolerance is not None:
                solver.rtol = self._relative_tolerance
            if self._report:
                solver.verbosity = 10
                solver.display_progress = True
                solver.report_continuously = True
            else:
                solver.display_progress = False
                solver.verbosity = 50
            # Assert consistency of final time and time step size
            assert self._final_time is not None
            final_time_consistency = (
                self._final_time - self._initial_time) / self._time_step_size
            assert isclose(
                round(final_time_consistency), final_time_consistency
            ), ("Final time should be occuring after an integer number of time steps"
                )
            # Prepare monitor computation if not provided by parameters
            if self._monitor_initial_time is None:
                self._monitor_initial_time = self._initial_time
            assert isclose(
                round(self._monitor_initial_time / self._time_step_size),
                self._monitor_initial_time / self._time_step_size
            ), ("Monitor initial time should be a multiple of the time step size"
                )
            if self._monitor_time_step_size is None:
                self._monitor_time_step_size = self._time_step_size
            assert isclose(
                round(self._monitor_time_step_size / self._time_step_size),
                self._monitor_time_step_size / self._time_step_size
            ), ("Monitor time step size should be a multiple of the time step size"
                )
            monitor_t = arange(
                self._monitor_initial_time,
                self._final_time + self._monitor_time_step_size / 2.,
                self._monitor_time_step_size)
            # Solve
            solver.simulate(self._final_time, ncp_list=monitor_t)
Example #2
0
 class _AssimuloIDA(object):
     def __init__(self, residual_eval, solution, solution_dot, bc_eval, jacobian_eval, set_time):
         self.residual_eval = residual_eval
         self.solution = solution
         self.solution_dot = solution_dot
         self.bc_eval = bc_eval
         self.jacobian_eval = jacobian_eval
         self.set_time = set_time
         # We should be solving a square system
         self.sample_residual = residual_eval(0, self.solution, self.solution_dot)
         self.sample_jacobian = jacobian_eval(0, self.solution, self.solution_dot, 0.)
         assert self.sample_jacobian.M == self.sample_jacobian.N
         assert self.sample_jacobian.N == self.sample_residual.N
         # Storage for current BC
         self.current_bc = None
         # Define an Assimulo Implicit problem
         def _store_solution_and_solution_dot(t, solution, solution_dot):
             self.solution.vector()[:] = solution
             self.solution_dot.vector()[:] = solution_dot
             # Update current bc
             if self.bc_eval is not None:
                 bcs_t = self.bc_eval(t)
                 assert isinstance(bcs_t, (tuple, dict))
                 if isinstance(bcs_t, tuple):
                     self.current_bc = DirichletBC(bcs_t)
                 elif isinstance(bcs_t, dict):
                     self.current_bc = DirichletBC(bcs_t, self.sample_residual._component_name_to_basis_component_index, self.solution.vector().N)
                 else:
                     raise TypeError("Invalid bc in _LinearSolver.__init__().")
         def _assimulo_residual_eval(t, solution, solution_dot):
             # Store current time
             self.set_time(t)
             # Convert to a matrix with one column, rather than an array
             _store_solution_and_solution_dot(t, solution, solution_dot)
             # Compute residual
             residual_vector = self.residual_eval(t, self.solution, self.solution_dot)
             # Apply BCs, if necessary
             if self.bc_eval is not None:
                 self.current_bc.apply_to_vector(residual_vector, self.solution.vector())
             # Convert to an array, rather than a matrix with one column, and return
             return residual_vector.__array__()
         def _assimulo_jacobian_eval(solution_dot_coefficient, t, solution, solution_dot):
             # Store current time
             self.set_time(t)
             # Convert to a matrix with one column, rather than an array
             _store_solution_and_solution_dot(t, solution, solution_dot)
             # Compute jacobian
             jacobian_matrix = self.jacobian_eval(t, self.solution, self.solution_dot, solution_dot_coefficient)
             # Apply BCs, if necessary
             if self.bc_eval is not None:
                 self.current_bc.apply_to_matrix(jacobian_matrix)
             # Return
             return jacobian_matrix.__array__()
         self.problem = Implicit_Problem(_assimulo_residual_eval, self.solution.vector(), self.solution_dot.vector())
         self.problem.jac = _assimulo_jacobian_eval
         # Define an Assimulo IDA solver
         self.solver = IDA(self.problem)
         self.solver.display_progress = False
         self.solver.verbosity = 50
         # Additional storage which will be setup by set_parameters
         self._final_time = None
         self._initial_time = 0.
         self._max_time_steps = None
         self._monitor = None
         self._time_step_size = None
         
     def set_parameters(self, parameters):
         for (key, value) in parameters.items():
             if key == "absolute_tolerance":
                 self.solver.atol = value
             elif key == "final_time":
                 self._final_time = value
             elif key == "initial_time":
                 self._initial_time = value
             elif key == "integrator_type":
                 assert value == "ida"
             elif key == "max_time_steps":
                 self.solver.maxsteps = value
                 self._max_time_steps = value
             elif key == "monitor":
                 self._monitor = value
             elif key == "nonlinear_solver":
                 for (key_nonlinear, value_nonlinear) in value.items():
                     if key_nonlinear == "absolute_tolerance":
                         self.solver.atol = value_nonlinear
                     elif key_nonlinear == "line_search":
                         raise NotImplementedError("This feature has not been implemented in IDA.")
                     elif key_nonlinear == "maximum_iterations":
                         raise NotImplementedError("This feature has not been implemented in IDA.")
                     elif key_nonlinear == "relative_tolerance":
                         self.solver.rtol = value
                     elif key_nonlinear == "report":
                         raise NotImplementedError("This feature has not been implemented in IDA.")
                     elif key_nonlinear == "solution_tolerance":
                         raise NotImplementedError("This feature has not been implemented in IDA.")
                     else:
                         raise ValueError("Invalid paramater passed to _AssimuloIDA object.")
             elif key == "problem_type":
                 pass
             elif key == "relative_tolerance":
                 self.solver.rtol = value
             elif key == "report":
                 self.solver.verbosity = 10
                 self.solver.display_progress = True
                 self.solver.report_continuously = True
             elif key == "time_step_size":
                 self.solver.inith = value
                 self._time_step_size = value
             else:
                 raise ValueError("Invalid paramater passed to _AssimuloIDA object.")
         
     def solve(self):
         assert self._max_time_steps is not None or self._time_step_size is not None
         if self._time_step_size is not None:
             all_t = [0]
             all_t_arange = self._time_step_size + arange(self._initial_time, self._final_time, self._time_step_size)
             all_t.extend(all_t_arange.tolist())
         elif self._max_time_steps is not None:
             all_t = linspace(self._initial_time, self._final_time, num=self._max_time_steps+1)
             all_t = all_t.tolist()
         for ida_trial in range(5):
             try:
                 all_times, all_solutions, all_solutions_dot = self.solver.simulate(self._final_time, ncp_list=all_t)
             except IDAError as error:
                 if str(error) == "'Error test failures occurred too many times during one internal time step or minimum step size was reached. At time 0.000000.'":
                     # There is no way to increase the number of error test failures in the assimulo interface, try again with smaller inith
                     self.solver.inith /= 10.
                 else:
                     # There was an error, but we cannot handle it. Raise it again
                     raise
             else:
                 break
         # Convert all_solutions to a list of Function
         all_solutions_as_functions = list()
         all_solutions_dot_as_functions = list()
         for (t, solution) in zip(all_times, all_solutions):
             self.solution.vector()[:] = solution
             all_solutions_as_functions.append(function_copy(self.solution))
             if len(all_solutions_as_functions) > 1: # monitor is being called at t > 0.
                 self.solution_dot.vector()[:] = (all_solutions_as_functions[-1].vector() - all_solutions_as_functions[-2].vector())/self._time_step_size
             else:
                 self.solution_dot.vector()[:] = all_solutions_dot[0]
             all_solutions_dot_as_functions.append(function_copy(self.solution_dot))
             if self._monitor is not None:
                 self._monitor(t, self.solution, self.solution_dot)
         self.solution.vector()[:] = all_solutions_as_functions[-1].vector()
         self.solution_dot.vector()[:] = all_solutions_dot_as_functions[-1].vector()
         return all_times, all_solutions_as_functions, all_solutions_dot_as_functions