Skip to content

Seeming issue with orientation.py #464

@somberostrich

Description

@somberostrich

Version Checks (indicate both or one)

  • I have confirmed this bug exists on the lastest release of atlite.

  • I have confirmed this bug exists on the current master branch of atlite.

Issue Description

Hi, we've been modelling solar PV output using the latest version of atlite, and we are encountering what appears to be a bug for the 'horizontal' and 'tilted horizontal' tracking options.

For tilted horizontal tracking when the azimuth is set to below 180 degrees (and tilt is set to above 0 degrees), there is a sudden drop in the power output at around noon when the solar zenith approaches 0. This drop is particularly large for the southern hemisphere. In orientation.py, when lines 156 - 160 are commented out:

rotation = np.where(
            logical_and(rotation < 0, azimuth_difference > 0),
            rotation + pi,
            rotation,
        )

the issue appears to disappear and the curves seem to be reasonable.

For horizontal tracking, we also noticed that in orientation.py lines 127 - 129, there might be a bracketing error. It is currently:

surface_azimuth = axis_azimuth + arcsin(
            sin(rotation / sin(surface_slope))
        )

I believe it should be:

surface_azimuth = axis_azimuth + arcsin(
            sin(rotation) / sin(surface_slope)
        )

Reproducible Example

import numpy as np
import pickle
import matplotlib.pyplot as plt
import atlite as at
from datetime import datetime, timedelta
import pytz

class AtlitePV:
    def __init__(self,start_date,end_date,Lon,Lat,tz):
        local_tz = pytz.timezone(tz)
        self.tz=tz
        gmt = pytz.timezone("UTC")

        start_date = local_tz.localize(start_date)
        start_date = start_date.astimezone(gmt)

        end_date = local_tz.localize(end_date)
        end_date = end_date.astimezone(gmt)



        #start_date = start_date.tz_localize(tz).tz_convert(tz='GMT')
        #end_date = end_date.tz_localize(tz).tz_convert(tz='GMT')

        self.cutout = at.Cutout(
            path="ERA-AusSummer-data.nc",
            module="era5",
            x=slice(Lon,Lon+0.75),  # Longitude range
            y=slice(Lat,Lat+0.75),  # Latitude range
            time=slice(str(start_date)[:10],str(end_date)[:10])  # Month or use slice("2025-01-01", "2025-01-31")
        )

        self.cf = None

        self.cutout.prepare(features=["influx", "temperature"])

    def get_solar_power(self,panel:str='CSi',tracking=None,slope:float=0.0,azimuth:float=90.0):
        self.cf = self.cutout.pv(
            panel=panel,
            orientation={"slope": slope, "azimuth": azimuth},
            tracking=tracking,
            shapes=self.cutout.grid,
            #shapes=None,
            per_unit=True
        )

        return self.cf

    def plot_solar_power(self,lon,lat):
        time_index = self.cf.coords['time'].to_index()
        time_local = time_index.tz_localize('UTC').tz_convert('Australia/Melbourne')

        plt.figure(figsize=(12, 4))
        plt.plot(time_local,self.cf)
        plt.title("Solar Capacity Factors")
        plt.ylabel("CF")
        plt.xlabel("Date")
        plt.grid(True)
        plt.legend()
        plt.tight_layout()
        plt.show()

    def compare_solar_power(self,lon,lat,panel:str='CSi',slope:float=0,azimuth:float=90):
        cf_notrack = self.cutout.pv(
            panel=panel,
            orientation={"slope": slope, "azimuth": azimuth},
            tracking=None,
            shapes=self.cutout.grid,
            # shapes=None,
            per_unit=True
        )

        cf_horizontal = self.cutout.pv(
            panel=panel,
            orientation={"slope": slope, "azimuth": azimuth},
            tracking='horizontal',
            shapes=self.cutout.grid,
            # shapes=None,
            per_unit=True
        )

        cf_horizontal_tilt = self.cutout.pv(
            panel=panel,
            orientation={"slope": slope, "azimuth": azimuth},
            tracking='tilted_horizontal',
            shapes=self.cutout.grid,
            # shapes=None,
            per_unit=True
        )

        cf_vertical = self.cutout.pv(
            panel=panel,
            orientation={"slope": slope, "azimuth": azimuth},
            tracking='vertical',
            shapes=self.cutout.grid,
            # shapes=None,
            per_unit=True
        )

        cf_dual = self.cutout.pv(
            panel=panel,
            orientation={"slope": slope, "azimuth": azimuth},
            tracking='dual',
            shapes=self.cutout.grid,
            # shapes=None,
            per_unit=True
        )

        time_index = self.cf.coords['time'].to_index()
        #time_index = cf_notrack.coords['time'].to_index()
        time_local = time_index.tz_localize('UTC').tz_convert(self.tz)

        fig, ax = plt.subplots(figsize=(12, 4))
        #plt.figure(figsize=(12, 4))
        ax.plot(time_local,np.mean(cf_dual, axis=1),color='black',label="dual tracking")
        ax.plot(time_local, np.mean(cf_vertical, axis=1),color='red',label="vertical tracking")
        ax.plot(time_local, np.mean(cf_horizontal_tilt, axis=1),color='orange',label="tilted horizontal tracking")
        ax.plot(time_local, np.mean(cf_horizontal, axis=1),color='green',label="horizontal tracking")
        ax.plot(time_local, np.mean(cf_notrack, axis=1),color='blue',label="no tracking")

        ax.set_title(f"Atlite output at lat: {lat}, and lon: {lon}")
        ax.set_ylabel("CF")
        ax.set_xlabel("Date")
        ax.grid(True)
        ax.legend()
        plt.show()

        with open(f"track_comp_{Lat}_{Lon}.pkl", "wb") as f:
            pickle.dump(fig, f)

    def plot_tilted(self, lon, lat, panel: str = 'CSi', slope: float = 0, azimuth: float = 90):

        cf_horizontal_tilt = self.cutout.pv(
            panel=panel,
            orientation={"slope": slope, "azimuth": azimuth},
            tracking='tilted_horizontal',
            shapes=self.cutout.grid,
            # shapes=None,
            per_unit=True
        )


        time_index = self.cf.coords['time'].to_index()
        # time_index = cf_notrack.coords['time'].to_index()
        time_local = time_index.tz_localize('UTC').tz_convert(self.tz)

        plt.figure(figsize=(12, 4))
        plt.plot(time_local, np.mean(cf_horizontal_tilt, axis=1), color='orange',
                label="tilted horizontal tracking")

        plt.title(f"Atlite output at lat: {lat}, and lon: {lon}")
        plt.ylabel("CF")
        plt.xlabel("Date")
        plt.grid(True)
        plt.tight_layout()
        plt.show()



if __name__ == "__main__":
    start_date=datetime(2022, 1, 1,hour=1)
    end_date=datetime(2022, 1, 5,hour=23)

    tz = 'Australia/Perth'
    Lon = 115.7
    Lat = -32.2
    azimuth = 0
    slope = 30


    atlite_pv = AtlitePV(start_date=start_date,end_date=end_date,Lon=Lon,Lat=Lat,tz=tz)
    atlite_pv.get_solar_power(azimuth=azimuth,slope=slope)
    atlite_pv.compare_solar_power(lon=Lon, lat=Lat,azimuth=azimuth,slope=slope)

Expected Behavior

No response

Installed Versions

Replace this line.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    Status

    Quick wins

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions