Beispiel #1
0
    def _compute_gradient(self, m, forget=True):
        """ Compute the functional gradient for the binary variables control array """
        farm = self.solver.problem.parameters.tidal_farm

        # If any of the parameters changed, the forward model needs to be re-run
        if self.last_m is None or numpy.any(m != self.last_m):
            self._compute_functional(m, annotate=True)

        J = self.time_integrator.dolfin_adjoint_functional(self.solver.state)

        # Output power
        if self.solver.parameters.dump_period > 0:

            if self._solver_params.output_turbine_power:
                turbines = farm.turbine_cache["turbine_field"]
                power = self.functional.power(self.solver.state, turbines)
                self.power_file << project(
                    power, farm._turbine_function_space, annotate=False)

        parameters = FunctionControl("turbine_friction_cache")
        djdtf = dolfin_adjoint.compute_gradient(J, parameters, forget=forget)
        dolfin.parameters["adjoint"]["stop_annotating"] = False
        dj = []

        for tfd in farm.turbine_cache["turbine_derivative_binary"]:
            farm.update()
            dj.append(djdtf.vector().inner(tfd.vector()))

        dj = numpy.array(dj)

        return dj
Beispiel #2
0
    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)
    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)
        def compute_gradient(m, forget=True):
            ''' Takes in the turbine positions/frictions values and computes the resulting functional gradient. '''
            # If the last forward run was performed with the same parameters, then all recorded values by dolfin-adjoint are still valid for this adjoint run
            # and we do not have to rerun the forward model.
            if numpy.any(m != self.last_m):
                compute_functional(m, annotate=True)

            state = self.last_state
            functional = config.functional(config)

            # Produce power plot
            if config.params['output_turbine_power']:
                if config.params['turbine_thrust_parametrisation'] or config.params["implicit_turbine_thrust_parametrisation"] or "dynamic_turbine_friction" in config.params["controls"]:
                    info_red("Turbine power VTU's is not yet implemented with thrust based turbines parameterisations and dynamic turbine friction control.")
                else:
                    turbines = self.__config__.turbine_cache.cache["turbine_field"]
                    self.power_file << project(functional.power(state, turbines), config.turbine_function_space, annotate=False)

            # The functional depends on the turbine friction function which we do not have on scope here.
            # But dolfin-adjoint only cares about the name, so we can just create a dummy function with the desired name.
            dummy_tf = Function(FunctionSpace(state.function_space().mesh(), "R", 0), name="turbine_friction")

            if config.params['steady_state'] or config.params["functional_final_time_only"]:
                J = Functional(functional.Jt(state, dummy_tf) * dt[FINISH_TIME])

            elif config.params['functional_quadrature_degree'] == 0:
                # Pseudo-redo the time loop to collect the necessary timestep information
                t = config.params["start_time"]
                timesteps = [t]
                while (t < config.params["finish_time"]):
                    t += config.params["dt"]
                    timesteps.append(t)

                if not config.params["include_time_term"]:
                    # Remove the initial condition. I think this is a bug in dolfin-adjoint, since really I expected pop(0) here - but the Taylor tests pass only with pop(1)!
                    timesteps.pop(1)

                # Construct the functional
                J = Functional(sum(functional.Jt(state, dummy_tf) * dt[t] for t in timesteps))

            else:
                if not config.params["include_time_term"]:
                    raise NotImplementedError, "Multi-steady state simulations only work with 'functional_quadrature_degree=0' or 'functional_final_time_only=True'" 
                J = Functional(functional.Jt(state, dummy_tf) * dt)

            if 'dynamic_turbine_friction' in config.params["controls"]:
                parameters = [InitialConditionParameter("turbine_friction_cache_t_%i" % i) for i in range(len(config.params["turbine_friction"]))]

            else:
                parameters = InitialConditionParameter("turbine_friction_cache")

            djdtf = dolfin_adjoint.compute_gradient(J, parameters, forget=forget)
            dolfin.parameters["adjoint"]["stop_annotating"] = False

            # Decide if we need to apply the chain rule to get the gradient of interest
            if config.params['turbine_parametrisation'] == 'smeared':
                # We are looking for the gradient with respect to the friction
                dj = dolfin_adjoint.optimization.get_global(djdtf)

            else:
                # Let J be the functional, m the parameter and u the solution of the PDE equation F(u) = 0.
                # Then we have
                # dJ/dm = (\partial J)/(\partial u) * (d u) / d m + \partial J / \partial m
                #               = adj_state * \partial F / \partial u + \partial J / \partial m
                # In this particular case m = turbine_friction, J = \sum_t(ft)
                dj = []

                if 'turbine_friction' in config.params["controls"]:
                    # Compute the derivatives with respect to the turbine friction
                    for tfd in config.turbine_cache.cache["turbine_derivative_friction"]:
                        config.turbine_cache.update(config)
                        dj.append(djdtf.vector().inner(tfd.vector()))

                elif 'dynamic_turbine_friction' in config.params["controls"]:
                    # Compute the derivatives with respect to the turbine friction
                    for djdtf_arr, t in zip(djdtf, config.turbine_cache.cache["turbine_derivative_friction"]):
                        for tfd in t:
                            config.turbine_cache.update(config)
                            dj.append(djdtf_arr.vector().inner(tfd.vector()))

                if 'turbine_pos' in config.params["controls"]:
                    # Compute the derivatives with respect to the turbine position
                    for d in config.turbine_cache.cache["turbine_derivative_pos"]:
                        for var in ('turbine_pos_x', 'turbine_pos_y'):
                            config.turbine_cache.update(config)
                            tfd = d[var]
                            dj.append(djdtf.vector().inner(tfd.vector()))

                dj = numpy.array(dj)

            return dj
Beispiel #5
0
    def _compute_gradient(self, m, forget=True):
        """ Compute the functional gradient for the turbine positions/frictions array """

        farm = self.solver.problem.parameters.tidal_farm

        # If any of the parameters changed, the forward model needs to be re-run
        if self.last_m is None or numpy.any(m != self.last_m):
            self._compute_functional(m, annotate=True)

        J = self.time_integrator.dolfin_adjoint_functional(self.solver.state)

        # Output power
        if self.solver.parameters.dump_period > 0:

            if self._solver_params.output_turbine_power:
                turbines = farm.turbine_cache["turbine_field"]
                power = self.functional.power(self.solver.state, turbines)
                self.power_file << project(
                    power, farm._turbine_function_space, annotate=False)

        if farm.turbine_specification.controls.dynamic_friction:
            parameters = []
            for i in range(len(farm._parameters["friction"])):
                parameters.append(
                    FunctionControl("turbine_friction_cache_t_%i" % i))

        else:
            parameters = FunctionControl("turbine_friction_cache")

        djdtf = dolfin_adjoint.compute_gradient(J, parameters, forget=forget)
        dolfin.parameters["adjoint"]["stop_annotating"] = False

        # Decide if we need to apply the chain rule to get the gradient of
        # interest.
        if farm.turbine_specification.smeared:
            # We are looking for the gradient with respect to the friction
            dj = dolfin_adjoint.optimization.get_global(djdtf)

        else:
            # Let J be the functional, m the parameter and u the solution of the
            # PDE equation F(u) = 0.
            # Then we have
            # dJ/dm = (\partial J)/(\partial u) * (d u) / d m + \partial J / \partial m
            #               = adj_state * \partial F / \partial u + \partial J / \partial m
            # In this particular case m = turbine_friction, J = \sum_t(ft)
            dj = []

            if farm.turbine_specification.controls.friction:
                # Compute the derivatives with respect to the turbine friction
                for tfd in farm.turbine_cache["turbine_derivative_friction"]:
                    farm.update()
                    dj.append(djdtf.vector().inner(tfd.vector()))

            elif farm.turbine_specification.controls.dynamic_friction:
                # Compute the derivatives with respect to the turbine friction
                for djdtf_arr, t in zip(
                        djdtf,
                        farm.turbine_cache["turbine_derivative_friction"]):
                    for tfd in t:
                        farm.update()
                        dj.append(djdtf_arr.vector().inner(tfd.vector()))

            if (farm.turbine_specification.controls.position):
                if (farm.turbine_specification.controls.dynamic_friction):
                    # Compute the derivatives with respect to the turbine position
                    farm.update()
                    turb_deriv_pos = farm.turbine_cache[
                        "turbine_derivative_pos"]
                    n_time_steps = len(turb_deriv_pos)
                    for n in range(farm.number_of_turbines):
                        for var in ('turbine_pos_x', 'turbine_pos_y'):
                            dj_t = 0
                            for t in range(n_time_steps):
                                tfd_t = turb_deriv_pos[t][n][var]
                                dj_t += djdtf_arr.vector().inner(
                                    tfd_t.vector())
                            dj.append(dj_t)

                else:
                    # Compute the derivatives with respect to the turbine position
                    for d in farm.turbine_cache["turbine_derivative_pos"]:
                        for var in ('turbine_pos_x', 'turbine_pos_y'):
                            farm.update()
                            tfd = d[var]
                            dj.append(djdtf.vector().inner(tfd.vector()))

            dj = numpy.array(dj)

        return dj
    def _compute_gradient(self, m, forget=True):
        """ Compute the functional gradient for the turbine positions/frictions array """
        farm = self.solver.problem.parameters.tidal_farm

        # If any of the parameters changed, the forward model needs to be re-run
        if numpy.any(m != self.last_m):
            self._compute_functional(m, annotate=True)

        J = self.time_integrator.dolfin_adjoint_functional()

        # Output power
        if self.solver.parameters.dump_period > 0:

            if self._solver_params.output_turbine_power:
                turbines = farm.turbine_cache["turbine_field"]
                power = self.functional.power(self.solver.current_state, turbines)
                self.power_file << project(power,
                                           farm._turbine_function_space,
                                           annotate=False)

        if farm.turbine_specification.controls.dynamic_friction:
            parameters = []
            for i in xrange(len(farm._parameters["friction"])):
                parameters.append(
                    FunctionControl("turbine_friction_cache_t_%i" % i))

        else:
            parameters = FunctionControl("turbine_friction_cache")

        djdtf = dolfin_adjoint.compute_gradient(J, parameters, forget=forget)
        dolfin.parameters["adjoint"]["stop_annotating"] = False

        # Decide if we need to apply the chain rule to get the gradient of
        # interest.
        if farm.turbine_specification.smeared:
            # We are looking for the gradient with respect to the friction
            dj = dolfin_adjoint.optimization.get_global(djdtf)

        else:
            # Let J be the functional, m the parameter and u the solution of the
            # PDE equation F(u) = 0.
            # Then we have
            # dJ/dm = (\partial J)/(\partial u) * (d u) / d m + \partial J / \partial m
            #               = adj_state * \partial F / \partial u + \partial J / \partial m
            # In this particular case m = turbine_friction, J = \sum_t(ft)
            dj = []

            if farm.turbine_specification.controls.friction:
                # Compute the derivatives with respect to the turbine friction
                for tfd in farm.turbine_cache["turbine_derivative_friction"]:
                    farm.update()
                    dj.append(djdtf.vector().inner(tfd.vector()))

            elif farm.turbine_specification.controls.dynamic_friction:
                # Compute the derivatives with respect to the turbine friction
                for djdtf_arr, t in zip(djdtf, farm.turbine_cache["turbine_derivative_friction"]):
                    for tfd in t:
                        farm.update()
                        dj.append(djdtf_arr.vector().inner(tfd.vector()))

            if farm.turbine_specification.controls.position:
                # Compute the derivatives with respect to the turbine position
                for d in farm.turbine_cache["turbine_derivative_pos"]:
                    for var in ('turbine_pos_x', 'turbine_pos_y'):
                        farm.update()
                        tfd = d[var]
                        dj.append(djdtf.vector().inner(tfd.vector()))

            dj = numpy.array(dj)

        return dj
Beispiel #7
0
        def compute_gradient(m, forget=True):
            ''' Takes in the turbine positions/frictions values and computes the resulting functional gradient. '''
            myt = Timer("full compute_gradient")
            # If the last forward run was performed with the same parameters, then all recorded values by dolfin-adjoint are still valid for this adjoint run
            # and we do not have to rerun the forward model.
            if numpy.any(m != self.last_m):
                compute_functional(m)

            state = self.last_state
            functional = config.functional(config)

            # Produce power plot
            if config.params['output_turbine_power']:
                if config.params['turbine_thrust_parametrisation'] or config.params[
                        "implicit_turbine_thrust_parametrisation"] or "dynamic_turbine_friction" in config.params[
                            "controls"]:
                    info_red(
                        "Turbine power VTU's is not yet implemented with thrust based turbines parameterisations and dynamic turbine friction control."
                    )
                else:
                    turbines = self.__config__.turbine_cache.cache[
                        "turbine_field"]
                    self.power_file << project(functional.expr(
                        state, turbines),
                                               config.turbine_function_space,
                                               annotate=False)

            # The functional depends on the turbine friction function which we do not have on scope here.
            # But dolfin-adjoint only cares about the name, so we can just create a dummy function with the desired name.
            dummy_tf = Function(FunctionSpace(state.function_space().mesh(),
                                              "R", 0),
                                name="turbine_friction")

            if config.params['steady_state'] or config.params[
                    "functional_final_time_only"]:
                J = Functional(
                    functional.Jt(state, dummy_tf) * dt[FINISH_TIME])
            else:
                J = Functional(functional.Jt(state, dummy_tf) * dt)

            if 'dynamic_turbine_friction' in config.params["controls"]:
                parameters = [
                    InitialConditionParameter("turbine_friction_cache_t_%i" %
                                              i)
                    for i in range(len(config.params["turbine_friction"]))
                ]

            else:
                parameters = InitialConditionParameter(
                    "turbine_friction_cache")

            djdtf = dolfin_adjoint.compute_gradient(J,
                                                    parameters,
                                                    forget=forget)
            dolfin.parameters["adjoint"]["stop_annotating"] = False

            # Decide if we need to apply the chain rule to get the gradient of interest
            if config.params['turbine_parametrisation'] == 'smooth':
                # We are looking for the gradient with respect to the friction
                dj = dolfin_adjoint.optimization.get_global(djdtf)

            else:
                # Let J be the functional, m the parameter and u the solution of the PDE equation F(u) = 0.
                # Then we have
                # dJ/dm = (\partial J)/(\partial u) * (d u) / d m + \partial J / \partial m
                #               = adj_state * \partial F / \partial u + \partial J / \partial m
                # In this particular case m = turbine_friction, J = \sum_t(ft)
                dj = []

                if 'turbine_friction' in config.params["controls"]:
                    # Compute the derivatives with respect to the turbine friction
                    for tfd in config.turbine_cache.cache[
                            "turbine_derivative_friction"]:
                        config.turbine_cache.update(config)
                        dj.append(djdtf.vector().inner(tfd.vector()))

                elif 'dynamic_turbine_friction' in config.params["controls"]:
                    # Compute the derivatives with respect to the turbine friction
                    for djdtf_arr, t in zip(
                            djdtf, config.turbine_cache.
                            cache["turbine_derivative_friction"]):
                        for tfd in t:
                            config.turbine_cache.update(config)
                            dj.append(djdtf_arr.vector().inner(tfd.vector()))

                if 'turbine_pos' in config.params["controls"]:
                    # Compute the derivatives with respect to the turbine position
                    for d in config.turbine_cache.cache[
                            "turbine_derivative_pos"]:
                        for var in ('turbine_pos_x', 'turbine_pos_y'):
                            config.turbine_cache.update(config)
                            tfd = d[var]
                            dj.append(djdtf.vector().inner(tfd.vector()))

                dj = numpy.array(dj)

            return dj
Beispiel #8
0
 def _derivative(self, x):
     _, L_tape, f = self._obj(x)
     control = da.Control(f)
     J_tape = da.compute_gradient(L_tape, control)
     J = np.array(J_tape.vector()[:])
     return J