Source code for lightwin.optimisation.objective.minimize_difference_with_ref

"""Define a simple optimization objective.

It is a simple difference over a given quantity between the reference linac and
the linac under tuning.

"""

import logging
from typing import Any

from lightwin.beam_calculation.simulation_output.simulation_output import (
    SimulationOutput,
)
from lightwin.optimisation.objective.objective import Objective
from lightwin.util.typing import GETTABLE_SIMULATION_OUTPUT_T


[docs] class MinimizeDifferenceWithRef(Objective): """A simple difference at a given point between ref and fix."""
[docs] def __init__( self, name: str, weight: float, get_key: GETTABLE_SIMULATION_OUTPUT_T, get_kwargs: dict[str, Any], reference: SimulationOutput, descriptor: str | None = None, ) -> None: """ Set complementary :meth:`.SimulationOutput.get` flags, reference value. Parameters ---------- name : A short string to describe the objective and access to it. weight : A scaling constant to set the weight of current objective. get_key : Name of the quantity to get. get_kwargs : Keyword arguments for the :meth:`.SimulationOutput.get` method. We do not check its validity, but in general you will want to define the keys ``elt`` and ``pos``. If objective concerns a phase, you may want to precise the ``to_deg`` key. You also should explicit the ``to_numpy`` key. reference : The reference simulation output from which the ideal value will be taken. descriptor : A longer string to explain the objective. """ self._check_get_arguments(get_key, get_kwargs) self.get_key: GETTABLE_SIMULATION_OUTPUT_T = get_key self.get_kwargs = get_kwargs self.ideal_value: float super().__init__( name, weight, descriptor=descriptor, ideal_value=self._value_getter(reference, handle_missing_elt=True), ) self._check_ideal_value()
[docs] def base_str(self) -> str: """Tell nature and position of objective.""" message = f"{self.get_key:>23}" elt = str(self.get_kwargs.get("elt", "NA")) message += f" @elt {elt:>5}" pos = str(self.get_kwargs.get("pos", "NA")) message += f" ({pos:>3}) | {self.weight:>5} | " return message
[docs] def __str__(self) -> str: """Give objective information value.""" message = self.base_str() if isinstance(self.ideal_value, float): message += f"{self.ideal_value:+.14e}" return message if isinstance(self.ideal_value, tuple): message += ( f"{self.ideal_value[0]:+.2e} ~ {self.ideal_value[1]:+.2e}" ) return message if self.ideal_value is None: message += f"{'None': ^21}" return message return message
[docs] def _value_getter( self, simulation_output: SimulationOutput, handle_missing_elt: bool = False, ) -> float: """Get desired value using :meth:`.SimulationOutput.get` method. .. seealso:: :func:`.simulation_output.factory._element_to_index` Parameters ---------- simulation_output : Object to ``get`` ``self.get_key`` from. handle_missing_elt : Automatically look for an equivalent :class:`.Element` when the current one is not in :class:`.SimulationOutput`. Set it to ``True`` when calculating reference value (reference :class:`.Element` is not in compensating list of elements). """ return simulation_output.get( self.get_key, **self.get_kwargs, handle_missing_elt=handle_missing_elt, )
[docs] def _check_ideal_value(self) -> None: """Assert the the reference value is a float.""" if not isinstance(self.ideal_value, float): logging.warning( f"Tried to get {self.get_key} with {self.get_kwargs}, which " f"returned {self.ideal_value} instead of a float." )
[docs] def evaluate(self, simulation_output: SimulationOutput | float) -> float: assert isinstance(simulation_output, SimulationOutput) value = self._value_getter(simulation_output) return self._compute_residuals(value)
[docs] def _compute_residuals(self, value: float) -> float: """Compute residuals, that we want to minimize.""" return self.weight * abs(value - self.ideal_value)