Wouldnât it make sense to just give the Blueprint a condition where the user can set âbetween x and y do cover hight z%â + the option to recheck it every x minutes?
x, y should then maybe be the results out of your calculation to make it fit for summer and winter?
That could work. However my experience with such rule based systems is that they solve the problem for a specific location and time of the year. What may work well in the summer may not work in the winter because the sun will be at a different elevation for the same azimuth.
So in my case if I rotate it just clockwise around the corner, Iâd need to fill in âalphaâ = 0° and âbetaâ = 90°. Maybe to script it that way is easier?
Your drawing is correct. There are basically two ways to define the tracking FOV. Reference North (0°) or the window (win_azi
). I actually already do the second one to come up with valid angles in the first place:
# all angles above the horizon and within the half circle of the window facade
valid = np.logical_and((np.abs(gamma) < np.pi / 2), (alpha > 0))
# filter out sun azimuth outside azi_min and azi_max
valid = np.logical_and(valid, ((sol_azi > azi_min) & (sol_azi < azi_max)))
So the final tracking range is thus the intersection of the half circle (as in your drawing) with the upper and lower limits defined by the user.
Here is the python code if you want to try the simulation yourself:
from pvlib import solarposition
import pandas as pd
import numpy as np
np.set_printoptions(suppress=True)
import matplotlib.pyplot as plt
tz = 'CET'
lat, lon = 52.35843822282988, 4.881058028007153
# summer
start_date = '2022-06-05'
end_date = '2022-06-06'
# winter
# start_date = '2022-12-05'
# end_date = '2022-12-06'
times = pd.date_range(start=start_date, end=end_date, freq='5min', tz=tz)
solpos = solarposition.get_solarposition(times, lat, lon)
# plot of elevation and azimuth
plt.figure(figsize=(15, 5))
solpos['elevation'].plot(label='elevation')
solpos['azimuth'].plot(label='azimuth')
plt.ylabel('Angle [°]')
plt.xlabel('Time [HH:MM]')
plt.legend()
plt.show()
from numpy import cos, tan, radians
def calculate_blind_height(sol_elev: float, sol_azi: float, win_azi, azi_min, azi_max, d: float) -> np.ndarray:
"""
Calculate the height of the blind based on the sun position and the panel tilt and azimuth.
:param sol_elev: elevation of the sun in degrees
:param sol_azi: azimuth of the sun in degrees
:param win_azi: azimuth of the panel in degrees from north
:param win_tilt: tilt of the panel in degrees
:param d: distance between the working area and window facade in meters
"""
# surface solar azimuth
gamma = radians(sol_azi - win_azi)
# solar elevation
alpha = radians(sol_elev)
# all angles above the horizon and within the half circle of the window facade
valid = np.logical_and((np.abs(gamma) < np.pi / 2), (alpha > 0))
# filter out sun azimuth outside azi_min and azi_max
valid = np.logical_and(valid, ((sol_azi > azi_min) & (sol_azi < azi_max)))
# calculate blind height
return np.where(valid, (d / cos(gamma)) * tan(alpha), 0)
# variables
win_azi = 310
win_tilt = 0
h_max = 1.96
dis_workplane = 1
time = solpos.index
# azi min and max
azi_min = 90
azi_max = 284
# plot of blind height
ax, fig = plt.subplots(figsize=(12, 5))
plt.title(f"Window azimuth: {win_azi}°, azi_min: {azi_min}°, azi_max: {azi_max}°")
plt.plot(solpos['elevation'].values, label='elevation')
plt.plot(solpos['azimuth'].values, label='azimuth')
# two vlines, one at azi_min and one at azi_max
pos = np.where(np.logical_and(solpos['azimuth'].values > azi_min, solpos['azimuth'].values < azi_max))[0]
plt.vlines(pos[0], -10, 360, color='black', linestyle='--', linewidth=2, label='azi_min/azi_max')
plt.vlines(pos[-1], -10, 360, color='black', linestyle='--', linewidth=2 )
plt.xticks(np.arange(0, len(time), 12), time[::12].strftime('%H:%M'), rotation=90)
plt.ylabel('Angle [°]')
plt.xlabel('Time [HH:MM]')
plt.legend()
# plot of blind height
ax2 = fig.twinx()
blind_height = np.clip(calculate_blind_height(solpos['elevation'], solpos['azimuth'], win_azi, azi_min=azi_min, azi_max=azi_max, d=dis_workplane), 0, h_max)
ax2.plot(blind_height, label='blind height', color='red')
plt.ylabel('Blind height [m]')
plt.legend()
plt.show()