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 _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 _apply_bcs(self, bcs: DictOfThetaType): # Auxiliary dicts should have been stored in lhs and rhs, and should be consistent assert self.rhs._component_name_to_basis_component_index == self.lhs._component_name_to_basis_component_index[0] assert self.rhs._component_name_to_basis_component_length == self.lhs._component_name_to_basis_component_length[0] # Provide auxiliary dicts to DirichletBC constructor, and apply bcs = DirichletBC(bcs, self.rhs._component_name_to_basis_component_index, self.rhs.N) bcs.apply_to_vector(self.rhs) bcs.apply_to_matrix(self.lhs)
def _init_bcs(self, bcs: DictOfThetaType): self.bcs = DirichletBC( bcs, self.residual_vector._component_name_to_basis_component_index, self.solution.vector().N)
def _init_bcs(self, bcs: ThetaType): self.bcs = DirichletBC(bcs)
def _apply_bcs(self, bcs: ThetaType): bcs = DirichletBC(bcs) bcs.apply_to_vector(self.rhs) bcs.apply_to_matrix(self.lhs)
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)
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