.. _python_advanced_topics: ############################ Advanced Topics ############################ The examples below demonstrate advanced features beyond the basic usage shown in :ref:`python_getting_started`. Each example is self-contained and shows a specific capability of the SunPeek Python API. .. note:: These examples assume you have created a plant object using the preconfigured demo approach, as shown in :ref:`demo_preconfigured`. The complete runnable script with all examples is available at ``docs/scripts/script_advanced_usage.py``. Accessing Sensor Data ---------------------- Sensor data in SunPeek is stored as pint-pandas Series objects, that is: pandas Series with physical units. They preserve their physical units throughout calculations, allowing for automatic unit conversion and ensuring dimensional consistency. .. literalinclude:: ../../../scripts/script_advanced_usage.py :language: python :start-after: # EXAMPLE_A_START :end-before: # EXAMPLE_A_END :dedent: 0 *Output:* .. code-block:: text Data type: Unit: kelvin First 3 values: ds 2017-05-01 00:00:00+01:00 282.101333333333 2017-05-01 00:01:00+01:00 282.011 2017-05-01 00:02:00+01:00 281.834833333333 Name: te_amb, dtype: pint[kelvin][Float64] The ``.pint`` accessor provides unit-aware operations. You can convert to any compatible unit using ``.pint.to('target_unit')``: .. literalinclude:: ../../../scripts/script_advanced_usage.py :language: python :start-after: # EXAMPLE_A2_START :end-before: # EXAMPLE_A2_END :dedent: 0 *Output:* .. code-block:: text Values in Celsius: ds 2017-05-01 00:00:00+01:00 8.951333333333025 2017-05-01 00:01:00+01:00 8.861000000000047 2017-05-01 00:02:00+01:00 8.684833333333017 Name: te_amb, dtype: pint[degree_Celsius][Float64] By the way, component properties are also stored unit-aware: .. literalinclude:: ../../../scripts/script_advanced_usage.py :language: python :start-after: # EXAMPLE_A3_START :end-before: # EXAMPLE_A3_END :dedent: 0 *Output:* .. code-block:: text Plant latitude: 47.05 degree Array row spacing: 3.1 meter Accessing Virtual Sensors -------------------------- Virtual sensors are automatically calculated after loading measurement data. Examples of virtual sensors include, for instance solar position, angle of incidence, or thermal power calculated from volume flow, fluid properties, and temperature difference. Virtual sensors use the same data access pattern as physical sensors. See :ref:`fluids` for details on how SunPeek models heat transfer fluids. Common virtual sensors include: - **Plant** level: ``sun_azimuth``, ``sun_elevation``, ``sun_zenith`` (solar position) - **Array** level: ``aoi`` (angle of incidence of sun on collectors), ``iam`` (incidence angle modifier). The IAM depends on collector type and optical properties, see :ref:`collectors` for details. .. literalinclude:: ../../../scripts/script_advanced_usage.py :language: python :start-after: # EXAMPLE_B_START :end-before: # EXAMPLE_B_END :dedent: 0 *Output:* .. code-block:: text At 2017-05-01 09:15:00+00:00: Solar azimuth: 138.4 degree Solar elevation: 51.9 degree Solar zenith: 38.1 degree Angle of incidence: 24.2 degree Incidence angle modifier: 0.985 dimensionless Working with Multiple Arrays ------------------------------ SunPeek plants can contain multiple collector arrays with different orientations, tilt angles, or collector types. Power Check analysis automatically handles all arrays and aggregates results at the plant level. .. code-block:: python from sunpeek.components import Array from sunpeek.common.unit_uncertainty import Q # Create additional arrays with different configurations array_1 = Array( name='Some array', plant=plant, collector=some_collector, area_gr=Q(100, 'm**2'), azim=Q(160, 'deg'), tilt=Q(30, 'deg'), sensor_map=(...) ) array_2 = Array( name='Another array', plant=plant, collector=another_collector, area_gr=Q(100, 'm**2'), azim=Q(210, 'deg'), tilt=Q(40, 'deg'), sensor_map=(...) ) # Add arrays to plant plant.add_array([array_1, array_2]) # Power Check analyzes all arrays automatically result = run_power_check(plant) print(f"Analyzed {len(plant.arrays)} arrays") .. note:: This example is conceptual. For a working multi-array example, build a custom plant with multiple arrays, as shown in :ref:`demo_custom`. For details on collector types and parameters, see :ref:`collectors`. Advanced CSV Loading Options ------------------------------ The ``use_csv()`` function accepts many keyword arguments for handling different CSV formats and data import requirements: .. code-block:: python from datetime import datetime, timezone response = use_csv( plant, csv_files=['path/to/data.csv', 'path/to/another_data.csv'], # Timezone handling timezone='Europe/Vienna', # If timestamps lack timezone info # CSV format csv_separator=';', # Column separator (default: ';') csv_decimal=',', # Decimal separator (default: '.') csv_encoding='utf-8', # File encoding (default: 'utf-8') index_col=0, # Column index for timestamps (default: 0) # Datetime parsing datetime_format='%Y-%m-%d %H:%M:%S', # Custom datetime format # Time range filtering eval_start=datetime(2017, 5, 1, tzinfo=timezone.utc), eval_end=datetime(2017, 5, 2, tzinfo=timezone.utc) ) The response returned by ``use_csv()`` contains helpful information, such as: .. code-block:: python # Overall upload statistics print(f"Uploaded {response.n_uploaded_data_rows} data rows") print(f"Found {response.n_duplicates_index} duplicate timestamps") # Per-file information for file_info in response.response_per_file: print(f"\nFile: {file_info.name}") print(f" Rows: {file_info.n_rows}") print(f" Time range: {file_info.start} to {file_info.end}") print(f" Missing columns: {file_info.missing_columns}") if file_info.error_cause: print(f" Error: {file_info.error_cause}") For common data import issues and solutions, see :ref:`troubleshooting`. *Output:* .. code-block:: text Uploaded 2880 data rows Found 0 duplicate timestamps File: data.csv Rows: 1440 Time range: 2017-05-01 00:00:00+00:00 to 2017-05-01 23:59:00+00:00 Missing columns: [] File: another_data.csv Rows: 1440 Time range: 2017-05-02 00:00:00+00:00 to 2017-05-02 23:59:00+00:00 Missing columns: ['te_amb'] Loading Data from DataFrames ------------------------------ In addition to loading CSV files with ``use_csv()``, SunPeek can load data directly from pandas DataFrames using ``use_dataframe()``. This is useful when data is already in memory, comes from a database, or requires custom preprocessing. .. code-block:: python # Load CSV into plant and apply custom preprocessing df = pd.read_csv( sunpeek.demo.DEMO_DATA_PATH_2DAYS, index_col=0 ) df.index = pd.to_datetime(df.index) df_resampled = df.resample('2min').mean() # Load DataFrame into plant use_dataframe(plant, df=df_resampled) Configuring Power Check Analysis ---------------------------------- SunPeek's Power Check analysis offers extensive configuration options to adapt the analysis to different use cases, plant configurations, data quality levels, etc. For detailed background on Power Checkology, see the `Guide to ISO 24194:2022 Power Check `_. Auto-Mode and dry-run ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ All Power Check settings are optional. If not specified, SunPeek automatically selects the best formula, averaging method, and other settings based on your plant configuration and available measurement data. .. code-block:: python result = run_power_check(plant) To inspect the auto-chosen algorithm settings (called a "strategy") without running the full analysis, use ``get_successful_strategy()`` or ``dry_run=True``. If no valid strategy is found, you can inspect the attempted strategies to get feedback: .. literalinclude:: ../../../scripts/script_advanced_usage.py :language: python :start-after: # EXAMPLE_F2_START :end-before: # EXAMPLE_F2_END :dedent: 0 *Output:* .. code-block:: text Auto-chosen settings: Formula: 2 Mode: ISO Use wind: True If no valid strategy is found, see :ref:`troubleshooting` for common sensor mapping and configuration issues. Formula, Method, and Time Range ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can explicitly configure the formula (per ISO 24194:2022), averaging method, and an optional time range for the analysis. .. code-block:: python result = run_power_check( plant, # Power Check formulas 1/2/3, as specified in ISO 24194:2022 formula=1, # Uses global tilted irradiance (GTI) # formula=2, # Uses beam and diffuse irradiance components separately # formula=3, # For highly concentrating collectors # Averaging methods method='ISO' # Fixed-interval averaging per ISO 24194:2022 # method='Extended' # Moving-average approach with extended interval search # Limit analysis to specific time range eval_start=pd.Timestamp('2017-05-01', tz='UTC'), eval_end=pd.Timestamp('2017-05-02', tz='UTC') ) .. note:: This example shows configuration options for Power Check analysis. The ``result`` object is a ``PowerCheckResult`` with the same structure as in previous examples, but using your specified formula, method, and time range. Fine-Tuning Parameters ^^^^^^^^^^^^^^^^^^^^^^^ There are more parameters to fine-tune algorithm behavior, including safety factors and numerical parameters. .. literalinclude:: ../../../scripts/script_advanced_usage.py :language: python :start-after: # EXAMPLE_F3_START :end-before: # EXAMPLE_F3_END :dedent: 0 Creating Individual Plots --------------------------- Beyond the comprehensive PDF report, SunPeek provides functions to create individual Power Check plots for custom reporting or interactive analysis. Each plotting function returns a list of matplotlib Figure objects that can be displayed, saved, or further customized. .. literalinclude:: ../../../scripts/script_advanced_usage.py :language: python :start-after: # EXAMPLE_G_START :end-before: # EXAMPLE_G_END :dedent: 0 Exporting and Importing Plant Configuration --------------------------------------------- Plant configurations can be exported to JSON files for documentation or sharing with collaborators. The exported configuration includes all plant configurations, sensor definitions, fluid and collector parameters. The exported JSON file is human-readable and can be edited manually if needed. Import the configuration later using ``make_plant_from_config_file()`` to recreate the exact same plant setup. .. code-block:: python import json from sunpeek.exporter import create_export_config from sunpeek.common.config_parser import make_plant_from_config_file # Export plant configuration and save to JSON file config_dict = create_export_config(plant) with open('plant_config.json', 'w') as f: json.dump(config_dict, f, indent=2, default=str) # Import configuration later plant_reloaded = make_plant_from_config_file('plant_config.json') Managing Operational Events ----------------------------- SunPeek supports "operational events", a concept useful to track any event that influences plant performance, such as maintenance periods, sensor calibration, or data quality issues. Events can optionally be marked as "ignored" to exclude specific time periods from analysis. .. code-block:: python from datetime import datetime, timezone # Add event that is NOT excluded from analysis plant.add_operational_event( start=datetime(2017, 5, 1, 10, 0, tzinfo=timezone.utc), end=datetime(2017, 5, 1, 14, 0, tzinfo=timezone.utc), description="Sensor calibration", ignored_range=False # Include in analysis ) # Add event that IS excluded from analysis plant.add_operational_event( start=datetime(2017, 5, 1, 20, 0, tzinfo=timezone.utc), end=datetime(2017, 5, 2, 8, 0, tzinfo=timezone.utc), description="System maintenance", ignored_range=True # Exclude from analysis ) Ignored time ranges are automatically excluded from Power Check analysis and other calculations. Use ``plant.is_ignored(timestamp)`` to check if a specific time is within an ignored range. .. literalinclude:: ../../../scripts/script_advanced_usage.py :language: python :start-after: # EXAMPLE_I_START :end-before: # EXAMPLE_I_END :dedent: 0 *Output:* .. code-block:: text Time 2017-05-02 00:00:00+00:00 is ignored: True Plant has 1 ignored time ranges.