artist.optim ============ .. py:module:: artist.optim Submodules ---------- .. toctree:: :maxdepth: 1 /autoapi/artist/optim/kinematics_reconstructor/index /autoapi/artist/optim/loss/index /autoapi/artist/optim/motor_position_optimizer/index /autoapi/artist/optim/regularizers/index /autoapi/artist/optim/surface_reconstructor/index /autoapi/artist/optim/training/index Classes ------- .. autoapisummary:: artist.optim.KinematicsReconstructor artist.optim.AngleLoss artist.optim.FocalSpotLoss artist.optim.KLDivergenceLoss artist.optim.Loss artist.optim.PixelLoss artist.optim.VectorLoss artist.optim.MotorPositionsOptimizer artist.optim.IdealSurfaceRegularizer artist.optim.SmoothnessRegularizer artist.optim.SurfaceReconstructor artist.optim.EarlyStopping Functions --------- .. autoapisummary:: artist.optim.mean_loss_per_heliostat artist.optim.cyclic artist.optim.exponential artist.optim.reduce_on_plateau Package Contents ---------------- .. py:class:: KinematicsReconstructor(ddp_setup: artist.util.env.DdpSetup, scenario: artist.scenario.scenario.Scenario, data: dict[str, artist.io.calibration_parser.CalibrationDataParser | list[tuple[str, list[pathlib.Path], list[pathlib.Path]]]], optimization_configuration: dict[str, Any], dni: float | None = None, reconstruction_method: str = constants.kinematics_reconstruction_raytracing, bitmap_resolution: torch.Tensor = torch.tensor([256, 256])) Initialize the kinematics optimizer. Parameters ---------- ddp_setup : DdpSetup Information about the distributed environment, process groups, devices, ranks, world size, and heliostat-group-to-ranks mapping. scenario : Scenario The scenario. data : dict[str, CalibrationDataParser | list[tuple[str, list[pathlib.Path], list[pathlib.Path]]]] The data parser and the mapping of heliostat name and calibration data. optimization_configuration : dict[str, Any] Parameters for the optimizer, learning rate scheduler, regularizers, and early stopping. dni : float | None Direct normal irradiance in W/m^2 (default is None which leads to a ray magnitude of 1.0). reconstruction_method : str The reconstruction method. Currently, only reconstruction via ray tracing is implemented. bitmap_resolution : torch.Tensor The resolution of all bitmaps during reconstruction (default is ``torch.tensor([256, 256])``). Shape is ``[2]``. .. py:attribute:: ddp_setup .. py:attribute:: scenario .. py:attribute:: data .. py:attribute:: optimizer_dict .. py:attribute:: scheduler_dict .. py:attribute:: dni :value: None .. py:attribute:: bitmap_resolution .. py:method:: reconstruct_kinematics(loss_definition: artist.optim.loss.Loss, device: torch.device | None = None) -> tuple[torch.Tensor, list[Any]] Reconstruct the kinematic parameters. Parameters ---------- loss_definition : Loss The definition of the loss function and pre-processing of the prediction. device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ARTIST will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. Returns ------- torch.Tensor The final loss of the kinematics reconstruction for each heliostat in each group. Shape is ``[total_number_of_heliostats_in_scenario]``. list[list[dict[str, list[float]]]] Loss histories over epochs grouped by rank. Outer list: one entry per rank. Inner list: one entry per heliostat group processed on that rank. Each group entry is a dict with key ``"total_loss"`` mapping to a list of per-epoch scalar loss values. In non-distributed mode, this is a single-rank container: ``[local_group_histories]``. .. py:method:: _reconstruct_kinematics_parameters_with_raytracing(loss_definition: artist.optim.loss.Loss, device: torch.device | None = None) -> tuple[torch.Tensor, list[list[dict[str, list[float]]]]] Reconstruct the kinematics parameters using ray tracing. This reconstruction method optimizes the kinematics parameters by extracting the focal points of calibration images and using heliostat-tracing. Parameters ---------- loss_definition : Loss Definition of the loss function and pre-processing of the prediction. device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ARTIST will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. Returns ------- torch.Tensor The final loss of the kinematics reconstruction for each heliostat in each group. Shape is ``[total_number_of_heliostats_in_scenario]``. list[list[dict[str, list[float]]]] Loss histories over epochs grouped by rank. Outer list: one entry per rank. Inner list: one entry per heliostat group processed on that rank. Each group entry is a dict with key ``"total_loss"`` mapping to a list of per-epoch scalar loss values. In non-distributed mode, this is a single-rank container: ``[local_group_histories]``. .. py:class:: AngleLoss Bases: :py:obj:`Loss` Initialize the angle loss. .. py:method:: __call__(prediction: torch.Tensor, ground_truth: torch.Tensor, **kwargs: Any) -> torch.Tensor Compute the cosine similarity between the prediction and ground truth. Parameters ---------- prediction : torch.Tensor The predicted values. Shape is ``[number_of_samples, 4]``. ground_truth : torch.Tensor The ground truth. Shape is ``[number_of_samples, 4]``. \*\*kwargs : Any Keyword arguments. Returns ------- torch.Tensor The summed loss reduced along the specified dimensions. Shape is ``[number_of_samples]``. .. py:class:: FocalSpotLoss(scenario: artist.scenario.scenario.Scenario) Bases: :py:obj:`Loss` Initialize the focal spot loss. Parameters ---------- scenario : Scenario The scenario. .. py:attribute:: scenario .. py:method:: __call__(prediction: torch.Tensor, ground_truth: torch.Tensor, **kwargs: Any) -> torch.Tensor Compute the focal spot loss. First the focal spots of the prediction are computed, then the loss is computed and reduced along the specified dimensions. Parameters ---------- prediction : torch.Tensor The predicted values. Shape is ``[number_of_samples, bitmap_resolution_e, bitmap_resolution_u]``. ground_truth : torch.Tensor The ground truth. Shape is ``[number_of_samples, 4]``. \*\*kwargs : Any Keyword arguments. ``target_area_indices`` and ``device`` are expected keyword arguments for the focal spot loss. Raises ------ ValueError If expected keyword arguments are not passed. Returns ------- torch.Tensor The focal spot loss. Shape is ``[number_of_samples]``. .. py:class:: KLDivergenceLoss Bases: :py:obj:`Loss` Initialize the Kullback-Leibler divergence loss. .. py:method:: __call__(prediction: torch.Tensor, ground_truth: torch.Tensor, **kwargs: Any) -> torch.Tensor Compute the Kullback-Leibler divergence loss :math:`D_{\mathrm{KL}}(P \parallel Q)`. The elements in the prediction and ground truth are normalized and shifted, to be greater or equal to zero. The KL-divergence is defined by: .. math:: D_{\mathrm{KL}}(P \parallel Q) = \sum_{x} P(x) \log \frac{P(x)}{Q(x)}, where :math:`P` is the ground truth distribution and :math:`Q` is the approximation or prediction of :math:`Q`. The KL-divergence is an asymmetric function. Switching :math:`P` and :math:`Q` has the following effect: :math:`P \parallel Q` Penalizes extra mass in the prediction where the ground truth has none. :math:`Q \parallel P` Penalizes missing mass in the prediction where the ground truth has mass. Parameters ---------- prediction : torch.Tensor The predicted values. Shape is ``[number_of_samples, bitmap_resolution_e, bitmap_resolution_u]``. ground_truth : torch.Tensor The ground truth. Shape is ``[number_of_samples, bitmap_resolution_e, bitmap_resolution_u]``. \*\*kwargs : Any Keyword arguments. ``reduction_dimensions`` is an expected keyword argument for the KL-divergence loss. Raises ------ ValueError If expected keyword arguments are not passed. Returns ------- torch.Tensor The summed KL-divergence loss reduced along the specified dimensions. Shape is ``[number_of_samples]``. .. py:class:: Loss(loss_function: torch.nn.Module) Initialize the base loss. Parameters ---------- loss_function : torch.nn.Module A torch module implementing a loss. .. py:attribute:: loss_function .. py:method:: __call__(prediction: torch.Tensor, ground_truth: torch.Tensor, **kwargs: Any) -> torch.Tensor :abstractmethod: Compute the loss. Parameters ---------- prediction : torch.Tensor The predicted values. Shape is variable. ground_truth : torch.Tensor The ground truth. Shape is variable. \*\*kwargs : Any Keyword arguments. Raises ------ NotImplementedError This abstract method must be overridden. .. py:class:: PixelLoss(scenario: artist.scenario.scenario.Scenario) Bases: :py:obj:`Loss` Initialize the pixel loss. Parameters ---------- scenario : Scenario The scenario. .. py:attribute:: scenario .. py:method:: __call__(prediction: torch.Tensor, ground_truth: torch.Tensor, **kwargs: Any) -> torch.Tensor Compute the pixel loss. First the predicted bitmaps and the ground truth are normalized, then the loss is computed and reduced along the specified dimensions. Parameters ---------- prediction : torch.Tensor The predicted values. Shape is ``[number_of_samples, bitmap_resolution_e, bitmap_resolution_u]``. ground_truth : torch.Tensor The ground truth. Shape is ``[number_of_samples, bitmap_resolution_e, bitmap_resolution_u]``. \*\*kwargs : Any Keyword arguments. ``reduction_dimensions``, ``target_area_indices``, and ``device`` are expected keyword arguments for the pixel loss. Raises ------ ValueError If expected keyword arguments are not passed. Returns ------- torch.Tensor The summed MSE pixel loss reduced along the specified dimensions. Shape is ``[number_of_samples]``. .. py:class:: VectorLoss Bases: :py:obj:`Loss` Initialize the vector loss. .. py:method:: __call__(prediction: torch.Tensor, ground_truth: torch.Tensor, **kwargs: Any) -> torch.Tensor Compute the vector loss. Parameters ---------- prediction : torch.Tensor The predicted values. Shape is ``[number_of_samples, ...]``. ground_truth : torch.Tensor The ground truth. Shape is ``[number_of_samples, ...]``. \*\*kwargs : Any Keyword arguments. ``reduction_dimensions`` is an expected keyword argument for the vector loss. Raises ------ ValueError If expected keyword arguments are not passed. Returns ------- torch.Tensor The summed MSE vector loss reduced along the specified dimensions. Shape is ``[number_of_samples]``. .. py:function:: mean_loss_per_heliostat(loss_per_sample: torch.Tensor, number_of_samples_per_heliostat: int) -> torch.Tensor Calculate the mean loss per heliostat from a loss per sample. Parameters ---------- loss_per_sample : torch.Tensor Loss per sample. Shape is ``[number_of_samples]``. number_of_samples_per_heliostat : int Number of samples per heliostat. Returns ------- torch.Tensor Loss per heliostat. Shape is ``[number_of_heliostats]``. .. py:class:: MotorPositionsOptimizer(ddp_setup: artist.util.env.DdpSetup, scenario: artist.scenario.scenario.Scenario, optimization_configuration: dict[str, Any], incident_ray_direction: torch.Tensor, target_area_index: int, ground_truth: torch.Tensor, dni: float, bitmap_resolution: torch.Tensor = torch.tensor([256, 256]), epsilon: float = 1e-12, device: torch.device | None = None) Initialize the motor-positions optimizer. Parameters ---------- ddp_setup : DdpSetup Information about the distributed environment, process groups, devices, ranks, world size, and heliostat-group-to-ranks mapping. scenario : Scenario The scenario. optimization_configuration : dict[str, Any] Parameters for the optimizer, learning rate scheduler, regularizers, and early stopping. incident_ray_direction : torch.Tensor Incident ray direction during the optimization. Shape is ``[4]``. target_area_index : int Index of the target used for the optimization. ground_truth : torch.Tensor Desired focal spot or distribution. Shape is ``[4]`` or ``[bitmap_resolution_e, bitmap_resolution_u]``. dni : float Direct normal irradiance in W/m^2. bitmap_resolution : torch.Tensor Resolution of all bitmaps during optimization (default is ``torch.tensor([256,256])``). Shape is ``[2]``. epsilon : float A small value to avoid division by zero (default is 1e-12). device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ``ARTIST`` will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. .. py:attribute:: ddp_setup .. py:attribute:: scenario .. py:attribute:: optimizer_dict .. py:attribute:: scheduler_dict .. py:attribute:: constraint_dict .. py:attribute:: incident_ray_direction .. py:attribute:: target_area_index .. py:attribute:: ground_truth .. py:attribute:: dni .. py:attribute:: bitmap_resolution .. py:attribute:: epsilon :value: 1e-12 .. py:method:: optimize(loss_definition: artist.optim.loss.Loss, device: torch.device | None = None) -> tuple[torch.Tensor, dict[str, list], torch.Tensor, torch.Tensor, torch.Tensor] Optimize the motor positions. The motor positions are optimized through a reparameterization to ensure stable training across different heliostats with widely varying initial motor positions and ranges. Motor positions can range from 0 to up to ~80000. Instead of directly optimizing the absolute motor positions, which can differ in magnitudes, an unconstrained parameter is optimized. Directly optimizing the absolute motor positions would have very different effects depending on the scale of the motors. For small initial motor positions (e.g. ~100), a gradient update of size 10 may cause a ~10% relative change, drastically altering the motor positions of this heliostat. For large initial motor positions (e.g. ~50000), the same optimizer step would correspond to only a 0.02% relative change in motor positions, effectively freezing the optimization of this heliostat. This mismatch makes it impossible to choose a single learning rate that works robustly across all heliostats. Reparameterizing the motor positions to be optimized defines the optimizable parameter as: .. math:: \text{motor\_positions\_optimized} = \tanh( \text{torch.nn.Parameter(optimizable\_parameter)} ) The true motor positions can be reconstructed by: .. math:: \text{motor\_positions} = \text{initial\_motor\_positions} + \text{motor\_positions\_normalized} \cdot \text{scale} where scale defines the range (e.g. up to ~80000) for adjustments. By optimizing reparameterized instead of raw motor positions, every heliostat sees updates of comparable relative magnitude, regardless of the absolute size of its motors positions. Parameters ---------- loss_definition : Loss The definition of the loss function and pre-processing of the prediction. device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ``ARTIST`` will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. Returns ------- torch.Tensor Final loss of the motor position optimization. dict[str, list] Loss history over epochs, with keys ``"total_loss"``, ``"flux_loss"``, ``"local_flux_constraint"``, ``"intercept_constraint"``, ``"flux_integral_constraint"``, and ``"flux_integral"``. Each value is a list of per-epoch scalar floats. torch.Tensor Final intercept factors for each heliostat. torch.Tensor Final fraction of rays hitting the target, neglecting blocking effects, for each heliostat. torch.Tensor Final fraction of rays not being blocked, for each heliostat. .. py:class:: IdealSurfaceRegularizer(reduction_dimensions: tuple[int, Ellipsis]) Bases: :py:obj:`Regularizer` Initialize the regularizer. Parameters ---------- reduction_dimensions : tuple[int, ...] Dimensions along which to reduce the loss. .. py:method:: __call__(current_control_points: torch.Tensor, original_control_points: torch.Tensor, device: torch.device | None = None, **kwargs: Any) -> torch.Tensor Compute the L2 loss between current control points and original control points. Parameters ---------- current_control_points : torch.Tensor The current control points. Shape is ``[number_of_heliostats, number_of_facets_per_surface, number_of_control_points_u_direction, number_of_control_points_v_direction, 3]``. original_control_points : torch.Tensor The current control points. Shape is ``[number_of_heliostats, number_of_facets_per_surface, number_of_control_points_u_direction, number_of_control_points_v_direction, 3]``. device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ``ARTIST`` will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. Returns ------- torch.Tensor L2 deviation loss per surface. .. py:class:: SmoothnessRegularizer(reduction_dimensions: tuple[int, Ellipsis]) Bases: :py:obj:`Regularizer` Initialize the regularizer. Parameters ---------- reduction_dimensions : tuple[int, ...] Dimensions along which to reduce the loss. .. py:method:: __call__(current_control_points: torch.Tensor, original_control_points: torch.Tensor, device: torch.device | None = None, **kwargs: Any) -> torch.Tensor Compute the Laplacian regularization loss. The loss measures how much each control-point displacement differs from the average of its four immediate neighbors, thereby penalizing localized, non-smooth deformations. Parameters ---------- current_control_points : torch.Tensor The current control points. Shape is ``[number_of_heliostats, number_of_facets_per_surface, number_of_control_points_u_direction, number_of_control_points_v_direction, 3]``. original_control_points : torch.Tensor The original control points. Shape is ``[number_of_heliostats, number_of_facets_per_surface, number_of_control_points_u_direction, number_of_control_points_v_direction, 3]``. device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ``ARTIST`` will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. Returns ------- torch.Tensor Laplacian regularization loss per surface. .. py:class:: SurfaceReconstructor(ddp_setup: artist.util.env.DdpSetup, scenario: artist.scenario.scenario.Scenario, data: dict[str, artist.io.calibration_parser.CalibrationDataParser | list[tuple[str, list[pathlib.Path], list[pathlib.Path]]]], optimization_configuration: dict[str, Any], dni: float | None = None, number_of_surface_points: torch.Tensor = torch.tensor([50, 50]), bitmap_resolution: torch.Tensor = torch.tensor([256, 256]), epsilon: float | None = 1e-12, device: torch.device | None = None) Initialize the surface reconstructor. Parameters ---------- ddp_setup : DdpSetup Information about the distributed environment, process groups, devices, ranks, world size, and heliostat-group-to-ranks mapping. scenario : Scenario The scenario. data : dict[str, CalibrationDataParser | list[tuple[str, list[pathlib.Path], list[pathlib.Path]]]] The data parser and the mapping of heliostat name and calibration data. optimization_configuration : dict[str, Any] The parameters for the optimizer, learning rate scheduler, early stopping, and constraints. dni : float | None Direct normal irradiance in W/m² used to scale the ray-traced flux (default is None). If None, the ``HeliostatRayTracer`` uses its own default. number_of_surface_points : torch.Tensor The number of surface points of the reconstructed surfaces (default is ``torch.tensor([50, 50])``). Shape is ``[2]``. bitmap_resolution : torch.Tensor The resolution of all bitmaps during reconstruction (default is ``torch.tensor([256, 256])``). Shape is ``[2]``. epsilon : float | None Small numerical offset used to avoid division by zero in the energy constraint (default is 1e-12). device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ``ARTIST`` will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. .. py:attribute:: ddp_setup .. py:attribute:: scenario .. py:attribute:: data .. py:attribute:: optimizer_dict .. py:attribute:: scheduler_dict .. py:attribute:: constraint_dict .. py:attribute:: number_of_surface_points .. py:attribute:: dni :value: None .. py:attribute:: bitmap_resolution .. py:attribute:: epsilon :value: 1e-12 .. py:method:: reconstruct_surfaces(loss_definition: artist.optim.loss.Loss, device: torch.device | None = None) -> tuple[torch.Tensor, list[list[dict[str, list[float]]]]] Reconstruct NURBS surfaces from bitmaps. Parameters ---------- loss_definition : Loss The definition of the loss function and pre-processing of the prediction. device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ``ARTIST`` will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. Returns ------- torch.Tensor The final reconstruction loss per heliostat, one entry per heliostat in the scenario. Shape is ``[total_number_of_heliostats_in_scenario]``. list[list[dict[str, list[float]]]] Loss histories over epochs grouped by rank. - Outer list: one entry per rank. - Inner list: one entry per heliostat group processed on that rank. - Each group entry is a dict with keys: ``"total_loss"``, ``"flux_loss"``, ``"smoothness_regularizer"``, ``"ideal_regularizer"``, ``"flux_integral"``, and ``"flux_integral_constraint"``. Each value is a list of per-epoch scalar floats. In non-distributed mode, this is a single-rank container: ``[local_group_histories]``. .. py:method:: lock_control_points_on_outer_edges(gradients: torch.Tensor, device: torch.device | None = None) -> torch.Tensor :staticmethod: Lock the u and v values of the control points on the outer edges of each facet. As the knots of the first and last knots on each facet have full multiplicity, the NURBS surfaces all start and end in control points. If the outer control points are not fixed in their u and v values, the reconstructed surfaces may not be rectangular anymore. To keep them rectangular, this function zeros the gradients of the u and v coordinates of all outer control points. Parameters ---------- gradients : torch.Tensor The full control point gradient tensor for all active heliostats. Gradients on the outer edges will be zeroed; interior gradients are returned unchanged. Shape is ``[number_of_active_heliostats, number_of_facets_per_surface, number_of_control_points_u_direction, number_of_control_points_v_direction, 3]``. device : torch.device | None The device on which to perform computations or load tensors and models (default is None). If None, ``ARTIST`` will automatically select the most appropriate device (CUDA or CPU) based on availability and OS. Returns ------- torch.Tensor The updated gradients. Shape is ``[number_of_active_heliostats, number_of_facets_per_surface, number_of_control_points_u_direction, number_of_control_points_v_direction, 3]``. .. py:class:: EarlyStopping(window_size: int = 10, patience: int = 20, min_improvement: float = 0.0001, relative: bool = True, eps: float = 1e-08) Initialize the early stopping. Parameters ---------- window_size : int Number of epochs used to estimate loss trend (default is 10). patience : int Number of consecutive non-improving windows before stopping (default is 20). min_improvement : float Minimum required improvement over the window to reset patience (default is 1e-4). relative : bool Indicates whether improvement is normalized by loss magnitude (default is True). eps : float Small value for stability (default is 1e-8). .. py:attribute:: window_size :value: 10 .. py:attribute:: patience :value: 20 .. py:attribute:: min_improvement :value: 0.0001 .. py:attribute:: relative :value: True .. py:attribute:: eps :value: 1e-08 .. py:attribute:: loss_history :type: Deque[float] .. py:attribute:: counter :value: 0 .. py:method:: step(loss: float) -> bool Update stopping state. Parameters ---------- loss : float Current loss value. Returns ------- bool True if optimization should stop, otherwise False. .. py:function:: cyclic(optimizer: torch.optim.Optimizer, parameters: dict[str, float]) -> torch.optim.lr_scheduler.LRScheduler Create a cyclic learning rate scheduler. Parameters ---------- optimizer : Optimizer The optimizer. parameters : dict[str, float] The scheduler parameters. Returns ------- LRScheduler A cyclic learning rate scheduler. .. py:function:: exponential(optimizer: torch.optim.Optimizer, parameters: dict[str, float]) -> torch.optim.lr_scheduler.LRScheduler Create an exponential learning rate scheduler. Parameters ---------- optimizer : Optimizer The optimizer. parameters : dict[str, float] The scheduler parameters. Returns ------- LRScheduler An exponential learning rate scheduler. .. py:function:: reduce_on_plateau(optimizer: torch.optim.Optimizer, parameters: dict[str, float]) -> torch.optim.lr_scheduler.LRScheduler Create learning rate scheduler that reduces on plateaus. Parameters ---------- optimizer : Optimizer The optimizer. parameters : dict[str, float] The scheduler parameters. Returns ------- LRScheduler A learning rate scheduler that reduces on plateaus.