class FenicsReducedFunctional(ReducedFunctional): """ This class implements a reduced functional that operators on FEniCS Functions instead of numpy.arrays for the controls. Following parameters are expected: :ivar functional: a :class:`PrototypeFunctional` class. :ivar controls: a (optionally list of) :class:`dolfin_adjoint.DolfinAdjointControl` object. :ivar solver: a :class:`Solver` object. This class has a parameter attribute for further adjustments. """ def __init__(self, functional, controls, solver): self.solver = solver if not isinstance(solver, Solver): raise ValueError, "solver argument of wrong type." self._functional = functional if not isinstance(functional, PrototypeFunctional): raise ValueError, "invalid functional argument." # Hidden attributes self._solver_params = solver.parameters self._problem_params = solver.problem.parameters self._time_integrator = None # Controls self.controls = enlisting.enlist(controls) # Conform to dolfin-adjoint API self.scale = 1 self.eval_cb_pre = lambda *args: None self.eval_cb_post = lambda *args: None self.derivative_cb_pre = lambda *args: None self.derivative_cb_post = lambda *args: None self.replay_cb = lambda *args: None self.hessian_cb = lambda *args: None self.cache = None self.current_func_value = None self.hessian = None self.functional = Functional(None) def evaluate(self, annotate=True): """ Computes the functional value by running the forward model. """ log(INFO, 'Start evaluation of j') timer = Timer("j evaluation") farm = self.solver.problem.parameters.tidal_farm # Configure dolfin-adjoint adj_reset() parameters["adjoint"]["record_all"] = True # Solve the shallow water system and integrate the functional of # interest. final_only = (not self.solver.problem._is_transient or self._problem_params.functional_final_time_only) self.time_integrator = TimeIntegrator(self.solver.problem, self._functional, final_only) for sol in self.solver.solve(annotate=annotate): self.time_integrator.add(sol["time"], sol["state"], sol["tf"], sol["is_final"]) j = self.time_integrator.integrate() timer.stop() log(INFO, 'Runtime: %f s.' % timer.elapsed()[0]) log(INFO, 'j = %e.' % float(j)) return j def __call__(self, value): """ Evaluates the reduced functional for the given control value. Args: value: The point in control space where to perform the Taylor test. Must be of the same type as the Control (e.g. Function, Constant or lists of latter). Returns: float: The functional value. """ value = enlisting.enlist(value) # Update the control values. # Note that we do not update the control values on the tape, # because OpenTidalFarm reannotates the tape in each iteration. for c, v in zip(self.controls, value): vec = c.coeff.vector() if vec.id() == v.vector().id(): continue vec.zero() vec.axpy(1, v.vector()) return self.evaluate() def derivative(self, forget=False, **kwargs): """ Computes the first derivative of the functional with respect to its controls by solving the adjoint equations. """ log(INFO, 'Start evaluation of dj') timer = Timer("dj evaluation") self.functional = self.time_integrator.dolfin_adjoint_functional(self.solver.state) dj = compute_gradient(self.functional, self.controls, forget=forget, **kwargs) parameters["adjoint"]["stop_annotating"] = False log(INFO, "Runtime: " + str(timer.stop()) + " s") return enlisting.enlist(dj)
class FenicsReducedFunctional(object): """ Following parameters are expected: :ivar functional: a :class:`PrototypeFunctional` class. :ivar controls: a (optionally list of) :class:`dolfin_adjoint.DolfinAdjointControl` object. :ivar solver: a :class:`Solver` object. This class has a parameter attribute for further adjustments. """ def __init__(self, functional, controls, solver): self.solver = solver if not isinstance(solver, Solver): raise ValueError, "solver argument of wrong type." self.functional = functional if not isinstance(functional, PrototypeFunctional): raise ValueError, "invalid functional argument." # Hidden attributes self._solver_params = solver.parameters self._problem_params = solver.problem.parameters self._time_integrator = None # Controls self.controls = enlisting.enlist(controls) def evaluate(self, annotate=True): """ Return the functional value for the given control values. """ log(INFO, 'Start evaluation of j') timer = dolfin.Timer("j evaluation") farm = self.solver.problem.parameters.tidal_farm # Configure dolfin-adjoint adj_reset() dolfin.parameters["adjoint"]["record_all"] = True # Solve the shallow water system and integrate the functional of # interest. final_only = (not self.solver.problem._is_transient or self._problem_params.functional_final_time_only) self.time_integrator = TimeIntegrator(self.solver.problem, self.functional, final_only) for sol in self.solver.solve(annotate=annotate): self.time_integrator.add(sol["time"], sol["state"], sol["tf"], sol["is_final"]) j = self.time_integrator.integrate() timer.stop() log(INFO, 'Runtime: %f s.' % timer.value()) log(INFO, 'j = %e.' % float(j)) return j def derivative(self, forget=False, **kwargs): """ Computes the first derivative of the functional with respect to its controls by solving the adjoint equations. """ log(INFO, 'Start evaluation of dj') timer = dolfin.Timer("dj evaluation") J = self.time_integrator.dolfin_adjoint_functional() dj = compute_gradient(J, self.controls, forget=forget, **kwargs) dolfin.parameters["adjoint"]["stop_annotating"] = False log(INFO, "Runtime: " + str(timer.stop()) + " s") return dj
class FenicsReducedFunctional(ReducedFunctional): """ Following parameters are expected: :ivar functional: a :class:`PrototypeFunctional` class. :ivar controls: a (optionally list of) :class:`dolfin_adjoint.DolfinAdjointControl` object. :ivar solver: a :class:`Solver` object. This class has a parameter attribute for further adjustments. """ def __init__(self, functional, controls, solver): self.tic = time.clock() self.solver = solver if not isinstance(solver, Solver): raise ValueError, "solver argument of wrong type." self.otf_functional = functional if not isinstance(functional, PrototypeFunctional): raise ValueError, "invalid functional argument." # Hidden attributes self._solver_params = solver.parameters self._problem_params = solver.problem.parameters self._time_integrator = None #Initialise self.prev with j_0 =0 self.j_prev = 0 # Controls self.controls = enlisting.enlist(controls) self.evaluate() dolfin_adjoint_functional = self.time_integrator.dolfin_adjoint_functional(self.solver.state) super(FenicsReducedFunctional, self).__init__(dolfin_adjoint_functional, controls) # Caching variables that store which controls the last forward run was # performed self.last_m = None if self.solver.parameters.dump_period > 0: turbine_filename = os.path.join(solver.parameters.output_dir, "turbines.pvd") self.turbine_file = File(turbine_filename, "compressed") def evaluate(self, annotate=True): """ Return the functional value for the given control values. """ log(INFO, 'Start evaluation of j') timer = dolfin.Timer("j evaluation") farm = self.solver.problem.parameters.tidal_farm # Configure dolfin-adjoint adj_reset() dolfin.parameters["adjoint"]["record_all"] = True # Solve the shallow water system and integrate the functional of # interest. final_only = (not self.solver.problem._is_transient or self._problem_params.functional_final_time_only) self.time_integrator = TimeIntegrator(self.solver.problem, self.otf_functional, final_only) for sol in self.solver.solve(annotate=annotate): self.time_integrator.add(sol["time"], sol["state"], sol["tf"], sol["is_final"]) j = self.time_integrator.integrate() #import ipdb; ipdb.set_trace() # relative step size convergence criteria #if np.abs(j-self.j_prev)/max(np.abs(j),np.abs(self.j_prev),1) <= 10e09: # print "LALALLALALALALALLA" # #toc = time.clock() # print "Run time:", tic - 3. #else: # self.j_prev=j timer.stop() log(INFO, 'Runtime: %f s.' % timer.value()) log(INFO, 'j = %e.' % float(j)) return j def _update_turbine_farm(self): """ Update the turbine farm from the flattened parameter array m. """ farm = self.solver.problem.parameters.tidal_farm if farm.turbine_specification.smeared: farm._parameters["friction"] = self.controls[0].data().vector() #else: # controlled_by = farm.turbine_specification.controls # shift = 0 # if controlled_by.friction: # shift = len(farm._parameters["friction"]) # farm._parameters["friction"] = m[:shift] # elif controlled_by.dynamic_friction: # shift = len(numpy.reshape(farm._parameters["friction"],-1)) # nb_turbines = len(farm._parameters["position"]) # farm._parameters["friction"] = ( # numpy.reshape(m[:shift], (-1, nb_turbines)).tolist()) # if controlled_by.position: # m_pos = m[shift:] # farm._parameters["position"] = ( # numpy.reshape(m_pos, (-1,2)).tolist()) # Update the farm cache. farm.update() def derivative(self, forget=False, new_optimisation_iteration=True, **kwargs): """ Computes the first derivative of the functional with respect to its controls by solving the adjoint equations. """ log(INFO, 'Start evaluation of dj') timer = dolfin.Timer("dj evaluation") # If any of the parameters changed, the forward model needs to be re-run #self.evaluate J = self.time_integrator.dolfin_adjoint_functional(self.solver.state) #J = self.functional dj = compute_gradient(J, self.controls, forget=forget, **kwargs) #import ipdb; ipdb.set_trace() j = self.time_integrator.integrate() print "functional value", j import ipdb; ipdb.set_trace() dolfin.parameters["adjoint"]["stop_annotating"] = False print dj log(INFO, "Runtime: " + str(timer.stop()) + " s") # We assume that the gradient is computed at and only at the beginning # of each new optimisation iteration. Hence, this is the right moment # to store the turbine friction field and to increment the optimisation # iteration counter. if new_optimisation_iteration: self.solver.optimisation_iteration += 1 farm = self.solver.problem.parameters.tidal_farm self._update_turbine_farm() print "Iteration number:", self.solver.optimisation_iteration print "Gradient norm:", np.linalg.norm(dj.vector().array(), np.inf) # Set convergence criteria eps_dj = 1 if np.linalg.norm(dj.vector().array(), np.inf) <= eps_dj: toc = time.clock() print "Run time:", toc- self.tic sys.exit(0) else: pass if (self.solver.parameters.dump_period > 0 and farm is not None): # A cache hit skips the turbine cache update, so we need # trigger it manually. #if self._compute_gradient_mem.has_cache(m, forget): # self._update_turbine_farm(m) #if farm.turbine_specification.controls.dynamic_friction: # log(WARNING, ("Turbine VTU output not yet implemented for " # " dynamic turbine control")) #else: self.turbine_file << farm.turbine_cache['turbine_field'] # Compute the total amount of friction due to turbines if farm.turbine_specification.smeared: log(INFO, "Total amount of friction: %f" % assemble(farm.turbine_cache["turbine_field"]*dx)) return dj
class FenicsReducedFunctional(ReducedFunctional): """ This class implements a reduced functional that operators on FEniCS Functions instead of numpy.arrays for the controls. Following parameters are expected: :ivar functional: a :class:`PrototypeFunctional` class. :ivar controls: a (optionally list of) :class:`dolfin_adjoint.DolfinAdjointControl` object. :ivar solver: a :class:`Solver` object. This class has a parameter attribute for further adjustments. """ def __init__(self, functional, controls, solver): self.solver = solver if not isinstance(solver, Solver): raise ValueError, "solver argument of wrong type." self._functional = functional if not isinstance(functional, PrototypeFunctional): raise ValueError, "invalid functional argument." # Hidden attributes self._solver_params = solver.parameters self._problem_params = solver.problem.parameters self._time_integrator = None # Controls self.controls = enlisting.enlist(controls) # Conform to dolfin-adjoint API self.scale = 1 self.eval_cb_pre = lambda *args: None self.eval_cb_post = lambda *args: None self.derivative_cb_pre = lambda *args: None self.derivative_cb_post = lambda *args: None self.replay_cb = lambda *args: None self.hessian_cb = lambda *args: None self.cache = None self.current_func_value = None self.hessian = None self.functional = Functional(None) def evaluate(self, annotate=True): """ Computes the functional value by running the forward model. """ log(INFO, 'Start evaluation of j') timer = Timer("j evaluation") farm = self.solver.problem.parameters.tidal_farm # Configure dolfin-adjoint adj_reset() parameters["adjoint"]["record_all"] = True # Solve the shallow water system and integrate the functional of # interest. final_only = (not self.solver.problem._is_transient or self._problem_params.functional_final_time_only) self.time_integrator = TimeIntegrator(self.solver.problem, self._functional, final_only) for sol in self.solver.solve(annotate=annotate): self.time_integrator.add(sol["time"], sol["state"], sol["tf"], sol["is_final"]) j = self.time_integrator.integrate() timer.stop() log(INFO, 'Runtime: %f s.' % timer.elapsed()[0]) log(INFO, 'j = %e.' % float(j)) return j def __call__(self, value): """ Evaluates the reduced functional for the given control value. Args: value: The point in control space where to perform the Taylor test. Must be of the same type as the Control (e.g. Function, Constant or lists of latter). Returns: float: The functional value. """ value = enlisting.enlist(value) # Update the control values. # Note that we do not update the control values on the tape, # because OpenTidalFarm reannotates the tape in each iteration. for c, v in zip(self.controls, value): vec = c.coeff.vector() if vec.id() == v.vector().id(): continue vec.zero() vec.axpy(1, v.vector()) return self.evaluate() def derivative(self, forget=False, **kwargs): """ Computes the first derivative of the functional with respect to its controls by solving the adjoint equations. """ log(INFO, 'Start evaluation of dj') timer = Timer("dj evaluation") if not hasattr(self, "time_integrator"): self.evaluate() self.functional = self.time_integrator.dolfin_adjoint_functional( self.solver.state) dj = compute_gradient(self.functional, self.controls, forget=forget, **kwargs) parameters["adjoint"]["stop_annotating"] = False log(INFO, "Runtime: " + str(timer.stop()) + " s") return enlisting.enlist(dj)
class FenicsReducedFunctional(object): """ Following parameters are expected: :ivar functional: a :class:`PrototypeFunctional` class. :ivar controls: a (optionally list of) :class:`dolfin_adjoint.DolfinAdjointControl` object. :ivar solver: a :class:`Solver` object. This class has a parameter attribute for further adjustments. """ def __init__(self, functional, controls, solver): self.solver = solver if not isinstance(solver, Solver): raise ValueError, "solver argument of wrong type." self.functional = functional if not isinstance(functional, PrototypeFunctional): raise ValueError, "invalid functional argument." # Hidden attributes self._solver_params = solver.parameters self._problem_params = solver.problem.parameters self._time_integrator = None # Controls self.controls = enlisting.enlist(controls) def evaluate(self, annotate=True): """ Return the functional value for the given control values. """ log(INFO, 'Start evaluation of j') timer = dolfin.Timer("j evaluation") farm = self.solver.problem.parameters.tidal_farm # Configure dolfin-adjoint adj_reset() dolfin.parameters["adjoint"]["record_all"] = True # Solve the shallow water system and integrate the functional of # interest. final_only = (not self.solver.problem._is_transient or self._problem_params.functional_final_time_only) self.time_integrator = TimeIntegrator(self.solver.problem, self.functional, final_only) for sol in self.solver.solve(annotate=annotate): self.time_integrator.add(sol["time"], sol["state"], sol["tf"], sol["is_final"]) j = self.time_integrator.integrate() timer.stop() log(INFO, 'Runtime: %f s.' % timer.elapsed()[0]) log(INFO, 'j = %e.' % float(j)) return j def derivative(self, forget=False, **kwargs): """ Computes the first derivative of the functional with respect to its controls by solving the adjoint equations. """ log(INFO, 'Start evaluation of dj') timer = dolfin.Timer("dj evaluation") J = self.time_integrator.dolfin_adjoint_functional() dj = compute_gradient(J, self.controls, forget=forget, **kwargs) dolfin.parameters["adjoint"]["stop_annotating"] = False log(INFO, "Runtime: " + str(timer.stop()) + " s") return dj