Architecture Overview
=====================

.. note:: For detailed explanations of individual components (e.g., surfaces, ray tracing, optimization), see the corresponding sections in this guide.

Core Components
---------------

The relationships between components are illustrated in the following diagram:

.. figure:: ../images/class_diagram.svg
   :alt: Architectural Diagram of Optiland
   :align: center
   :figwidth: 100%

   Architectural diagram showing relationships between the `Optic` class, which is the core class for optical systems, and other key components like fields,
   surfaces, rays, optimization, analysis, and visualization frameworks.

This diagram is not meant to be exhaustive but provides a high-level overview of the core components and their interactions.

The `Optic` Class
------------------

The architecture is centered around the `Optic` class, which acts as the primary container for optical system definitions and
coordinates interactions between key components. It encapsulates:

- **Field Group**: A collection of defined field points for ray tracing and analysis. These define the extent of the object.
- **Wavelength Group**: The set of wavelengths used in calculations and ray tracing.
- **Surface Group**: A collection of surfaces that define the optical system.
- **RealRayTracer**: A module for tracing real rays through the system.
- **Paraxial**: A helper class for performing paraxial calculations.
- **Aberrations**: A helper class for computing various aberration metrics.
- **Pickup and Solve Managers**: Tools for linking parameters between surfaces and solving specific system constraints.

Overview of Key Modules
-----------------------

- **Optic Class**: The central container for an optical system. It manages fields, wavelengths, surfaces, and utility tools like paraxial calculations and aberration helpers. The `Optic` class serves as the interface for operations such as optimization, analysis, and tolerancing.
- **Surface Group**: A collection of surfaces within the `Optic` class. Each surface defines an optical interface with attributes like geometry, materials, coatings, and optional apertures.
- **Ray Tracing Framework**: Manages ray generation and propagation through the optical system, supporting real, paraxial, and polarized rays.
- **Analysis Framework**: Provides tools to evaluate system performance, such as spot diagrams, ray fans, and wavefront aberrations.
- **Optimization Framework**: Allows for system refinement based on user-defined objectives, combining operands, variables, and optimization algorithms.
- **Visualization Framework**: Offers 2D and 3D visualizations of optical components, ray paths, and system performance metrics.

Component Interactions
----------------------

The components of Optiland are designed to work seamlessly together, with the `Optic` class orchestrating most interactions:

- Rays are generated via the `RealRayTracer` and traced through the system via the surface group.
- Optimization relies on surface attributes (e.g., radius of curvature) as variables and ray tracing results for evaluating operands.
- Analysis tools, such as spot diagrams and ray fans, operate on data generated by the ray tracing framework.
- Visualization modules query the state of the `Optic` class, rendering components and simulation results.
- Pickups manage the relationships between surfaces and can modify surface properties.

Sequential vs Nonsequential Engines
-----------------------------------

Optiland provides two distinct ray tracing engines tailored for different optical modeling needs:

- **Sequential Engine**: Light propagates through a predefined, ordered sequence of surfaces. This is the standard model for most imaging systems (lenses, telescopes, etc.). It is extremely fast and supports differentiable optimization and wavefront-based analysis. The `Optic` class is the primary interface for this engine.
- **Nonsequential Engine**: Light can interact with any surface in any order, including multiple interactions with the same surface or escaping the system entirely. This model is essential for illumination design, stray light analysis, and systems with complex geometries (baffles, prisms, etc.). The `NSQScene` class is the primary interface for this engine.

Both engines share common underlying logic for geometry solvers, material models, and coordinate transformations, ensuring consistency across the entire package.

Extensibility
-------------

Optiland's design prioritizes extensibility:

- **New Components**: Developers can easily add new surface types, coating types, ray definitions, or analysis tools, etc. by following existing patterns.
- **Custom Optimizations**: The optimization framework supports user-defined variables and operands.
- **Visualization Customization**: Modular visualization classes allow for tailored rendering of components or results.

.. note::
   For details on the architecture of the PySide6-based Graphical User Interface (GUI), please refer to the :ref:`developers_guide_gui` section.

A Note on Circular References
------------------------------

``Optic`` holds a ``Paraxial``, an ``Aberrations`` helper, and a ``RealRayTracer``, and each of
those in turn keeps a back-reference to the owning ``Optic`` (e.g. ``Paraxial.optic``) so it can
read the current surfaces, fields, and wavelengths without every call site threading that state
through as arguments. This is a deliberate, accepted design choice, not an oversight:

- These helper classes are not meant to be used standalone — they exist to decompose ``Optic``'s
  behavior into focused pieces, and always operate against exactly one ``Optic`` instance for
  their whole lifetime.
- The back-reference avoids a much larger refactor (passing ``Optic`` state into every paraxial,
  aberration, and ray-tracing method call) for no practical benefit, since none of these helpers
  are ever detached from their owner or shared across systems.
- Python's garbage collector handles reference cycles like this without special-casing (there is
  no manual memory management concern here as there might be in a language without cyclic GC).

If you find yourself adding a new helper class of this shape, following the same pattern
(store the owning ``Optic`` once in ``__init__``, keep the helper single-purpose) is expected and
does not need to be redesigned away.

With this high-level understanding of Optiland's backend architecture, we can now dive deeper into individual components in their dedicated sections.
