Note: These notebooks follow the writing conventions for unit symbols and names recommanded by the International Bureau of Weights and Measures (BIPM 2019) and National Institute of Standards and Technology (Thomson A. et al. 2008).
This notebook:
Imports standard modules such as numpy, pandas, and matplotlib.pyplot, and a dedicated module, dm4bem. It then reads a file in EnergyPlus Weather Format (epw) containing weather data for Lyon, France.
Selects three columns from the weather data, namely air temperature, direct radiation on a normal surface, and diffuse radiation on an horizontal surface, and replaces the year in the index with 2000.
Defines a start date and an end date and filters the weather data based on these dates.
Creates three plots using the filtered weather data:
Calculates the solar radiation on a tilted surface by computing the direct radiation, diffuse radiation, and reflected radiation. It then stores the calculated solar radiation as a new column in the filtered weather data.
EnergyPlus™
format¶import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from dm4bem import read_epw, sol_rad_tilt_surf
Download the weather file with extension .epw
from:
For example, for the airport Lyon-Bron, France (N45.73, E5.08), download the files:
FRA_Lyon.074810_IWEC.epw
orFRA_AR_Lyon-Bron.AP.074800_TMYx.2004-2018
and place them in the ./weather_data
folder.
filename = './weather_data/FRA_Lyon.074810_IWEC.epw'
# filename = './weather_data/FRA_AR_Lyon-Bron.AP.074800_TMYx.2004-2018.epw'
The weather file .epw
contains hourly data for one year. For the description of the structure and the meaning of the fileds of the .epw
file, see read_epw
function in dm4bem.py module and the documentation for pvlib.iotools.read_epw function of pvlib python.
[data, meta] = read_epw(filename, coerce_year=None)
data
year | month | day | hour | minute | data_source_unct | temp_air | temp_dew | relative_humidity | atmospheric_pressure | ... | ceiling_height | present_weather_observation | present_weather_codes | precipitable_water | aerosol_optical_depth | snow_depth | days_since_last_snowfall | albedo | liquid_precipitation_depth | liquid_precipitation_quantity | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1983-01-01 00:00:00+01:00 | 1983 | 1 | 1 | 1 | 60 | C9C9C9C9*0?9?9?9?9?9?9?9A7A7A7A7A7A7*0E8*0*0 | 0.8 | 0.3 | 96 | 100100 | ... | 15 | 0 | 999999599 | 0 | 0.204 | 0 | 88 | 0.0 | 0.0 | 0.0 |
1983-01-01 01:00:00+01:00 | 1983 | 1 | 1 | 2 | 60 | C9C9C9C9*0?9?9?9?9?9?9?9A7A7B8B8A7A7*0E8*0*0 | -0.6 | -0.9 | 97 | 100300 | ... | 30 | 0 | 999999599 | 0 | 0.204 | 0 | 88 | 0.0 | 0.0 | 0.0 |
1983-01-01 02:00:00+01:00 | 1983 | 1 | 1 | 3 | 60 | C9C9C9C9*0?9?9?9?9?9?9?9A7A7B8B8A7A7*0E8*0*0 | -1.5 | -1.7 | 98 | 100400 | ... | 30 | 0 | 999999599 | 0 | 0.204 | 0 | 88 | 0.0 | 0.0 | 0.0 |
1983-01-01 03:00:00+01:00 | 1983 | 1 | 1 | 4 | 60 | C9C9C9C9*0?9?9?9?9?9?9?9A7A7A7A7A7A7*0E8*0*0 | -1.9 | -2.0 | 99 | 100500 | ... | 30 | 0 | 999999599 | 0 | 0.204 | 0 | 88 | 0.0 | 0.0 | 0.0 |
1983-01-01 04:00:00+01:00 | 1983 | 1 | 1 | 5 | 60 | C9C9C9C9*0?9?9?9?9?9?9?9A7A7B8B8A7A7*0E8*0*0 | -2.1 | -2.2 | 100 | 100500 | ... | 30 | 0 | 999999599 | 0 | 0.204 | 0 | 88 | 0.0 | 0.0 | 0.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
1986-12-31 19:00:00+01:00 | 1986 | 12 | 31 | 20 | 60 | C9C9C9C9*0?9?9?9?9?9?9?9A7A7B8B8A7A7*0E8*0*0 | 5.9 | 4.7 | 92 | 99400 | ... | 22000 | 9 | 999999999 | 0 | 0.117 | 2 | 88 | 0.0 | 0.0 | 0.0 |
1986-12-31 20:00:00+01:00 | 1986 | 12 | 31 | 21 | 60 | C9C9C9C9*0?9?9?9?9?9?9?9A7A7B8B8A7A7*0E8*0*0 | 5.5 | 4.5 | 93 | 99400 | ... | 22000 | 9 | 999999999 | 0 | 0.117 | 2 | 88 | 0.0 | 0.0 | 0.0 |
1986-12-31 21:00:00+01:00 | 1986 | 12 | 31 | 22 | 60 | C9C9C9C9*0?9?9?9?9?9?9?9A7A7A7A7A7A7*0E8*0*0 | 4.9 | 3.9 | 94 | 99500 | ... | 22000 | 9 | 999999999 | 0 | 0.117 | 2 | 88 | 0.0 | 0.0 | 0.0 |
1986-12-31 22:00:00+01:00 | 1986 | 12 | 31 | 23 | 60 | C9C9C9C9*0?9?9?9?9?9?9?9A7A7B8B8A7A7*0E8*0*0 | 3.7 | 2.9 | 94 | 99600 | ... | 22000 | 9 | 999999999 | 0 | 0.117 | 2 | 88 | 0.0 | 0.0 | 0.0 |
1986-12-31 23:00:00+01:00 | 1986 | 12 | 31 | 24 | 60 | C9C9C9C9*0?9?9?9?9?9?9?9A7A7B8B8A7A7*0E8*0*0 | 2.3 | 1.6 | 95 | 99800 | ... | 1320 | 9 | 999999999 | 0 | 0.117 | 2 | 88 | 0.0 | 0.0 | 0.0 |
8760 rows × 35 columns
Data for each month may be from different years.
# Extract the month and year from the DataFrame index with the format 'MM-YYYY'
month_year = data.index.strftime('%m-%Y')
# Create a set of unique month-year combinations
unique_month_years = sorted(set(month_year))
# Create a DataFrame from the unique month-year combinations
pd.DataFrame(unique_month_years, columns=['Month-Year'])
Month-Year | |
---|---|
0 | 01-1983 |
1 | 02-1985 |
2 | 03-1998 |
3 | 04-1995 |
4 | 05-1986 |
5 | 06-1993 |
6 | 07-1982 |
7 | 08-1993 |
8 | 09-1988 |
9 | 10-1999 |
10 | 11-1991 |
11 | 12-1986 |
From the dataset, select:
EPWData field | Description | Unit |
---|---|---|
temp_air |
Dry bulb air temperature at timestamp | °C |
dir_n_rad |
Direct normal radiation received during 1 h prior to timestamp | Wh/m² |
dif_h_rad |
Diffuse horizontal radiation received during 1 h prior to timestamp | Wh/m² |
Note: For the description of .EPW
file, see pvlib.iotools.epw.
Since in the dataset the values for each month are from different years, we will replace all year with the same year, e.g. 2000
.
# select columns of interest
weather_data = data[["temp_air", "dir_n_rad", "dif_h_rad"]]
# replace year of the index with 2000
weather_data.index = weather_data.index.map(
lambda t: t.replace(year=2000))
Select a period for:
# Define start and end dates
start_date = '2000-06-29'
end_date = '2000-07-02'
# Filter the data based on the start and end dates
weather_data = weather_data.loc[start_date:end_date]
del data
weather_data.head()
temp_air | dir_n_rad | dif_h_rad | |
---|---|---|---|
2000-06-29 00:00:00+01:00 | 16.7 | 0 | 0 |
2000-06-29 01:00:00+01:00 | 17.0 | 0 | 0 |
2000-06-29 02:00:00+01:00 | 16.0 | 0 | 0 |
2000-06-29 03:00:00+01:00 | 15.9 | 0 | 0 |
2000-06-29 04:00:00+01:00 | 16.0 | 0 | 0 |
weather_data['temp_air'].plot()
plt.xlabel("Time")
plt.ylabel("Dry-bulb air temperature, θ / °C")
plt.legend([])
plt.show()
Figure 1. Hourly dry bulb air temperature at timestamp.
weather_data[['dir_n_rad', 'dif_h_rad']].plot()
plt.xlabel("Time")
plt.ylabel("Solar radiation, Φ / (W/m²)")
plt.legend(['$Φ_{direct}$', '$Φ_{diffuse}$'])
plt.show()
Figure 2. Hourly-mean direct normal and difuse horizontal solar radiation.
For a tilted wall and knowing the albedo of the surface which is in front of it, the diffuse and reflected radiation incident on the wall can be calculated from weather data for each hour.
Let's consider a wall at latitude ϕ with the orientation given by its slope angle, β, and azimuth angle, γ.
surface_orientation = {'slope': 90, # 90° is vertical; > 90° downward
'azimuth': 0, # 0° South, positive westward
'latitude': 45} # °, North Pole 90° positive
pd.Series(surface_orientation)
slope 90 azimuth 0 latitude 45 dtype: int64
Figure 3. Orientation of a surface:
- β: slope angle: β=90° is vertical; β>90° is downward facing.
- γ: azimuth angle: 0° South, positive westward, negative eastward.
- θ: incidence angle: angle between the solar beam on the surface and the normal to the surface.
N, S, E, W: North, South, East and West, respectively. →n is the vector normal to the surface. ∟ represents right angles.
Let's consider that the albedo of the surface in front of the wall is:
albedo = 0.2
rad_surf = sol_rad_tilt_surf(
weather_data, surface_orientation, albedo)
rad_surf.plot()
plt.xlabel("Time")
plt.ylabel("Solar radiation, Φ / (W/m²)")
plt.show()
Figure 4. Hourly-mean direct, diffuse and reflected radiation on a tilted surface.
Let’s consider a tilted surface having another surface (e.g., ground) in front of it. Given the weather data, the surface orientation, and the albedo of the ground in front of the surface, find the direct, diffuse and reflected solar radiation for this surface. The algoritm is implemented in the function sol_rad_tilt_surf
of the dm4bem.py
module.
The orientation of the surface is given by the following angles:
β = surface_orientation['slope']
γ = surface_orientation['azimuth']
ϕ = surface_orientation['latitude']
# Transform degrees in radians
β = β * np.pi / 180
γ = γ * np.pi / 180
ϕ = ϕ * np.pi / 180
n = weather_data.index.dayofyear
Total solar radiation is the amount of radiation received on a surface during the number of minutes preceding the time indicated:
Gsr=Gdir+Gdif+Grwhere:
Gdir direct normal or beam radiation: amount of solar radiation received directly from the solar disk on a surface perpendicular to the sun’s rays, during the number of minutes preceding the time indicated, W/m².
Gdif diffuse radiation: amount of solar radiation received after scattering by the atmosphere, W/m². Note: it does not include the diffuse infrared radiation emitted by the atmosphere.
Gr total solar radiation coming by reflection from the surface in front of the wall (usually, ground), W/m².
The direct radiation on the surface, Gdir, depends on the direct normal (or beam) radiation, Gn, and the incidence angle, θ, between the solar beam and the normal to the wall [2] (§11.2.1).
In order to calculate the incidence angle, θ, we need:
ϕ latitude, the angle between the position and the Equator, ranging from 0° at the Equator to 90° at the North Pole and -90° at the South Pole. −90∘≤ϕ≤90∘
β slope, the angle between the plane of the surface and the horizontal. 0≤β≤180∘; β≤90∘: the surface is upward facing.
γ azimuth, the angle between the projection on a horizontal plane of the normal to the surface and the local meridian; south is zero, east negative, and west positive. −180∘≤γ≤180∘.
δ declination angle, the angle between the sun at noon (i.e., when the sun is on the local meridian) and the plane of the equator, north positive [1](eq. 1.6.1a), [2](§11.2.1.1, eq. (78)):
declination_angle = 23.45 * np.sin(360 * (284 + n) / 365 * np.pi / 180)
δ = declination_angle * np.pi / 180
where hour and minute is the solar time.
−180∘≤ω≤180∘. ω<0 in the morning, ω=0 at noon, and ω>0 in the afternoon. Hour angle is used with the declination to give the direction of a point on the celestial sphere.
hour = weather_data.index.hour
minute = weather_data.index.minute + 60
hour_angle = 15 * ((hour + minute / 60) - 12) # deg
ω = hour_angle * np.pi / 180 # rad
The [incidence angle](https://en.m.wikipedia.org/wiki/Angle_of_incidence_(optics), θ, is the angle between the solar beam on the surface and the normal to the surface [1](eq. 1.6.2), [2] (eq. 78):
θ=arccos(sinδsinϕcosβ−sinδcosϕsinβcosγ+cosδcosϕcosβcosω+cosδsinϕsinβcosγcosω+cosδsinβsinγsinω)If β≤90∘, then the sun is behind the surface. Therefore, if θ>π/2, then θ=π/2.
theta = np.sin(δ) * np.sin(ϕ) * np.cos(β) \
- np.sin(δ) * np.cos(ϕ) * np.sin(β) * np.cos(γ) \
+ np.cos(δ) * np.cos(ϕ) * np.cos(β) * np.cos(ω) \
+ np.cos(δ) * np.sin(ϕ) * np.sin(β) * np.cos(γ) * np.cos(ω) \
+ np.cos(δ) * np.sin(β) * np.sin(γ) * np.sin(ω)
theta = np.array(np.arccos(theta))
theta = np.minimum(theta, np.pi / 2)
The direct radiation, Gdir on the surface is:
Gdir=Gdir,ncosθwhere direct normal radiation or beam radiation, Gn, is the amount of solar radiation (in W/m²) received directly from the solar disk on the surface perpendicular to the sun’s rays, during the number of minutes preceding the time indicated. It is given by weather data.
dir_rad = weather_data["dir_n_rad"] * np.cos(theta)
dir_rad[dir_rad < 0] = 0
dif_rad = weather_data["dif_h_rad"] * (1 + np.cos(β)) / 2
gamma = np.cos(δ) * np.cos(ϕ) * np.cos(ω) \
+ np.sin(δ) * np.sin(ϕ)
gamma = np.array(np.arcsin(gamma))
gamma[gamma < 1e-5] = 1e-5
dir_h_rad = weather_data["dir_n_rad"] * np.sin(gamma)
The total radiation received by reflection is:
Gr=(Gdir,h+Gdif,h)ρ1−cosβ2where ρ is the albedo (or the reflection coefficient) of the surface.
ref_rad = (dir_h_rad + weather_data["dif_h_rad"]) * albedo \
* (1 - np.cos(β) / 2)
Gdir,n Direct normal or beam radiation. Amount of solar radiation in Wh/m² received directly from the solar disk on a surface perpendicular to the sun’s rays, during the number of minutes preceding the time indicated.
Gdif,h Diffuse horizontal radiation. Amount of solar radiation in Wh/m² received after scattering by the atmosphere. This definition distinguishes the diffuse solar radiation from infrared radiation emitted by the atmosphere.
Total Solar Radiation. Total amount of direct and diffuse solar radiation in Wh/m² received on a surface during the number of minutes preceding the time indicated.
Global radiation. Total solar radiation given on a horizontal surface.
Solar Time. Time based on the apparent position of the sun in the sky with noon the time when the sun crosses the observer meridian.
ϕ Latitude. Angle between the position and the Equator, ranging from 0° at the Equator to 90° at the North Pole and -90° at the South Pole. −90∘≤ϕ≤90∘
β Slope. Angle between the plane of the surface and the horizontal. 0≤β≤180∘. β<90∘ means that the surface is upward facing.
γ Azimuth. Angle between the projection on a horizontal plane of the normal to the surface and the local meridian; south is zero, east negative, and west positive. −180∘≤γ≤180∘.
δ Declination. Angle between the sun at noon (i.e., when the sun is on the local meridian) and the plane of the equator, north positive [1, eq. 1.6.1a):
δ=23.45sin(360284+n365)where n is the day of the year. −23.45∘≤δ≤23.45∘. Declination is used with hour angle to give the direction of a point on the celestial sphere.
ω Hour angle. Angle between the sun and the local meridian due to rotation of the earth around its axis at 15° per hour [1]:
ω=15(h+min60−12)where h and min is the solar time in hours and in minutes, respectively. The hour angle is −180∘≤ω≤180∘
with ω<0 in the morning, ω=0 at noon, and ω>0 in the afternoon. Hour angle is used with the declination to give the direction of a point on the celestial sphere.
θ Incidence. Angle between the solar beam on the surface and the normal to the surface [1, eq. 1.6.2]:
θ=arccos(sinδsinϕcosβ−sinδcosϕsinβcosγ+cosδcosϕcosβcosω+cosδsinϕsinβcosγcosω+cosδsinβsinγsinω)Duffie, J.A., Beckman, W. A., Blair, N. (2020) Solar Engineering of Thermal Processes, 5th ed. John Wiley & Sons, Inc. ISBN 9781119540281
Réglementation Thermique 2005. Méthode de calcul Th-CE. Annexe à l’arrêté du 19 juillet 2006
US Department of Energy (2022) EnergyPlus™ Engineering Reference, version 22.1.0, © 1996-2022
BIPM (2019) The International System of Units (SI), 9th edition, licence CC-BY-3.0
Gőbel, E., Mills, I., Wallard, A. (2006). A concise summary of the International System of Units, the SI
Thomson, A., Taylor, B. N. (2008). Guide for the use of the international System of units (NIST Special Publication 811․ 2008 Edition). National Institute of Standards and Technology, US Government Printing Office.