.. _arrays_and_mounting: #################################### Arrays and Mounting Configuration #################################### This page explains how collector arrays and their mounting configurations work in SunPeek, including fixed-tilt installations and single-axis tracking systems. Overview ======== In SunPeek, a **collector array** represents a group of solar thermal collectors that share the same orientation, collector type, and sensor connections. Arrays are the primary unit for Power Check analysis. The **mounting** configuration defines how collectors are oriented relative to the sun: - **Fixed mounting** (``MountingFixed``): Collectors have a constant tilt and azimuth angle. - **Single-axis tracking** (``MountingSingleAxis``): Collectors rotate around one axis to follow the sun. .. note:: Single-axis tracking support is available in the Python API. WebUI support for configuring tracking arrays is planned for a future release. Mounting Types ============== MountingFixed ------------- Use ``MountingFixed`` for fixed-tilt installations where collectors maintain a constant orientation. **Parameters:** .. list-table:: MountingFixed Parameters :header-rows: 1 :widths: 20 50 15 15 * - Parameter - Description - Unit - Range * - ``surface_tilt`` - Tilt angle from horizontal plane - degree - 0-90 * - ``surface_azimuth`` - Compass direction the collectors face (North=0, East=90, South=180, West=270) - degree - 0-360 **Example:** .. code-block:: python from sunpeek.components import Array, MountingFixed from sunpeek.common.unit_uncertainty import Q # South-facing collectors tilted at 30 degrees mounting = MountingFixed( surface_tilt=Q(30, 'deg'), surface_azimuth=Q(180, 'deg'), # South ) array = Array( name='Fixed array', plant=plant, collector=collector, mounting=mounting, area_gr=Q(500, 'm**2'), sensor_map={...}, ) MountingSingleAxis ------------------ Use ``MountingSingleAxis`` for single-axis tracking installations where collectors rotate around a fixed axis to follow the sun. The surface orientation is calculated automatically based on sun position using `pvlib `_. **Configuration Parameters:** .. list-table:: MountingSingleAxis Parameters :header-rows: 1 :widths: 20 50 15 15 * - Parameter - Description - Unit - Range * - ``axis_tilt`` - Tilt angle of the rotation axis from horizontal - degree - 0-90 * - ``axis_azimuth`` - Compass direction of the rotation axis (North=0, East=90, South=180, West=270) - degree - 0-360 * - ``max_angle`` - Maximum rotation angle from the horizontal position - degree - 0-90 **Common Configurations:** - **Horizontal N-S axis (tracks E-W)**: ``axis_tilt=0``, ``axis_azimuth=180`` - **Horizontal E-W axis (tracks N-S)**: ``axis_tilt=0``, ``axis_azimuth=90`` - **Tilted axis**: Set ``axis_tilt`` to match latitude for optimal year-round performance **Example:** .. code-block:: python from sunpeek.components import Array, MountingSingleAxis from sunpeek.common.unit_uncertainty import Q # Horizontal N-S axis tracker (tracks east-to-west) mounting = MountingSingleAxis( axis_tilt=Q(0, 'deg'), # Horizontal rotation axis axis_azimuth=Q(180, 'deg'), # N-S orientation max_angle=Q(60, 'deg'), # Maximum rotation from horizontal ) array = Array( name='Tracking array', plant=plant, collector=collector, mounting=mounting, area_gr=Q(500, 'm**2'), sensor_map={...}, ) Virtual Sensors for Tracking Arrays =================================== When using ``MountingSingleAxis``, SunPeek automatically calculates time-varying surface orientation based on the sun position and tracker configuration. These values are available as virtual sensors: .. list-table:: Tracking Virtual Sensors :header-rows: 1 :widths: 25 55 20 * - Sensor - Description - Virtual * - ``surface_tilt`` - Instantaneous tilt angle of collector surface - Always calculated * - ``surface_azimuth`` - Instantaneous azimuth angle of collector surface - Always calculated * - ``ideal_rotation_angle`` - Optimal tracker rotation angle based on sun position - Always calculated * - ``rotation_angle`` - Actual measured rotation angle (if sensor available) - Optional (measured) **Accessing Virtual Sensors:** .. code-block:: python # After loading measurement data use_csv(plant, csv_files=[...]) # Access time-varying surface orientation tracking_array = plant.arrays[0] # Get surface tilt as a time series (pint-pandas Series) surface_tilt = tracking_array.mounting.surface_tilt.data print(f"Surface tilt range: {surface_tilt.min():.1f} to {surface_tilt.max():.1f}") # Get ideal rotation angle ideal_rotation = tracking_array.mounting.ideal_rotation_angle.data The virtual sensors are calculated using `pvlib.tracking.singleaxis() `_. Array Properties ================ Arrays provide convenient properties for accessing orientation, regardless of mounting type: .. code-block:: python # For fixed mounting, tilt and azim are scalar Quantities if array.has_orientation(): print(f"Tilt: {array.tilt}") print(f"Azimuth: {array.azim}") # For tracking mounting, tilt and azim are Sensor objects # containing time-series data if not array.has_orientation(): print("Tracking array - orientation varies with time") print(f"Tilt data: {array.tilt.data.head()}") # The orientation property returns tilt/azim in degrees # Works for both fixed (scalar) and tracking (raises if time-varying) if array.has_orientation(): orient = array.orientation print(f"Orientation: tilt={orient['tilt']}°, azim={orient['azim']}°") Backward Compatibility ====================== For backward compatibility, the legacy ``tilt`` and ``azim`` parameters on ``Array`` still work. They are automatically converted to ``MountingFixed`` internally: .. code-block:: python # Legacy syntax (still works) array = Array( name='Array', plant=plant, collector=collector, tilt=Q(30, 'deg'), azim=Q(180, 'deg'), area_gr=Q(500, 'm**2'), sensor_map={...}, ) # Internally creates: MountingFixed(surface_tilt=Q(30,'deg'), surface_azimuth=Q(180,'deg')) # Recommended new syntax array = Array( name='Array', plant=plant, collector=collector, mounting=MountingFixed( surface_tilt=Q(30, 'deg'), surface_azimuth=Q(180, 'deg'), ), area_gr=Q(500, 'm**2'), sensor_map={...}, ) .. hint:: While the legacy syntax is supported, we recommend using the explicit ``mounting`` parameter for new code to make the mounting configuration clear and enable future tracking support. Additional Array Parameters =========================== Beyond mounting configuration, arrays have additional parameters for shading and layout calculations: .. list-table:: Array Parameters :header-rows: 1 :widths: 20 50 15 15 * - Parameter - Description - Unit - Required * - ``name`` - Descriptive name for the array - — - Yes * - ``collector`` - Collector type used in this array - — - Yes * - ``mounting`` - Mounting configuration (``MountingFixed`` or ``MountingSingleAxis``) - — - Yes * - ``area_gr`` - Total gross collector area - m² - Yes * - ``row_spacing`` - Distance between collector rows (for shading calculations) - m - No * - ``ground_tilt`` - Slope of the ground beneath collectors - degree - No * - ``sensor_map`` - Mapping of sensor slots to sensor objects - — - Yes For details on sensor mapping, see :ref:`sensor_mapping`. .. seealso:: - :ref:`collectors` — Solar collector types and parameters - :ref:`fluids` — Heat transfer fluid configuration - :ref:`shading` — Internal shading algorithms - :ref:`python_advanced_topics` — Advanced Python API examples including tracking arrays