Esempio n. 1
0
    def __init__(self,
                 config,
                 scale=1.0,
                 forward_model=sw_model.sw_solve,
                 plot=False):
        ''' If plot is True, the functional values will be automatically saved in a plot.
            scale is ignored if automatic_scaling is active. '''
        # Hide the configuration since changes would break the memoize algorithm.
        self.__config__ = config
        self.scale = scale
        self.automatic_scaling_factor = None
        self.plot = plot
        # Caching variables that store which controls the last forward run was performed
        self.last_m = None
        self.last_state = None
        if self.__config__.params["dump_period"] > 0:
            self.turbine_file = File("turbines.pvd", "compressed")

            if config.params['output_turbine_power']:
                self.power_file = File("power.pvd", "compressed")

        class Parameter:
            def data(self):
                m = []
                if config.params["turbine_parametrisation"] == "smooth":
                    m = numpy.zeros(config.turbine_function_space.dim())
                else:
                    if 'turbine_friction' in config.params["controls"]:
                        m += list(config.params['turbine_friction'])
                    if 'turbine_pos' in config.params["controls"]:
                        m += numpy.reshape(config.params['turbine_pos'],
                                           -1).tolist()
                return numpy.array(m)

        self.parameter = [Parameter()]

        if plot:
            self.plotter = AnimatedPlot(xlabel="Iteration",
                                        ylabel="Functional value")

        def compute_functional(m, return_final_state=False):
            ''' Takes in the turbine positions/frictions values and computes the resulting functional of interest. '''

            self.last_m = m

            self.update_turbine_cache(m)
            tf = config.turbine_cache.cache["turbine_field"]
            #info_green("Turbine integral: %f ", assemble(tf*dx))
            #info_green("The correct integral should be: %f ",  25.2771) # computed with wolfram alpha using:
            # int 0.17353373* (exp(-1.0/(1-(x/10)**2)) * exp(-1.0/(1-(y/10)**2)) * exp(2)) dx dy, x=-10..10, y=-10..10
            #info_red("relative error: %f", (assemble(tf*dx)-25.2771)/25.2771)

            return compute_functional_from_tf(tf, return_final_state)

        def compute_functional_from_tf(tf, return_final_state):
            ''' Takes in the turbine friction field and computes the resulting functional of interest. '''
            adj_reset()
            parameters["adjoint"]["record_all"] = True

            # Get initial conditions
            if config.params["implicit_turbine_thrust_parametrisation"]:
                state = Function(config.function_space_2enriched,
                                 name="Current_state")
            elif config.params["turbine_thrust_parametrisation"]:
                state = Function(config.function_space_enriched,
                                 name="Current_state")
            else:
                state = Function(config.function_space, name="Current_state")

            if config.params["steady_state"] and self.last_state != None:
                # Speed up the nonlinear solves by starting the Newton solve with the most recent state solution
                state.assign(self.last_state, annotate=False)
            else:
                ic = config.params['initial_condition']
                state.assign(ic, annotate=False)

            # Solve the shallow water system
            functional = config.functional(config)
            j = forward_model(config,
                              state,
                              functional=functional,
                              turbine_field=tf)
            self.last_state = state

            if return_final_state:
                return j, state
            else:
                return j

        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

        def compute_hessian_action(m, m_dot):
            if numpy.any(m != self.last_m):
                self.run_adjoint_model_mem(m, forget=False)

            state = self.last_state

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

            H = drivers.hessian(J,
                                InitialConditionParameter("friction"),
                                warn=False)
            m_dot = project(Constant(1), config.turbine_function_space)
            return H(m_dot)

        self.compute_functional_mem = memoize.MemoizeMutable(
            compute_functional)
        self.compute_gradient_mem = memoize.MemoizeMutable(compute_gradient)
        self.compute_hessian_action_mem = memoize.MemoizeMutable(
            compute_hessian_action)