Paquete instalable para EnerHabitat
Project description
EnerHabitat
EnerHabitat is a Python package for the thermal simulation of opaque constructive systems (walls and roofs) driven by EPW weather data. It solves the one-dimensional, time-dependent heat conduction equation across multi-layer systems and produces hourly indoor temperatures and air-conditioning energy demands for an average day of a chosen month.
Contents
- Overview
- Theoretical background
- Installation
- Recommended folder structure
- Key concepts
- Quickstart
- Workflow
- Examples
- API reference
- Config (global)
- Materials
- Dependencies
- Authors
- License
Overview
EnerHabitat models the heat transfer through opaque constructive systems without windows, ventilation or infiltration. Each layer of the system is described by a material name and three thermal properties:
- thermal conductivity
k(W/m·K) - density
rho(kg/m³) - specific heat
c(J/kg·K) — writtenc_pin the heat equation below
These three names (k, rho, c) are the exact keys expected in
materials.ini; they are case-sensitive and Greek letters are not accepted.
Given an EPW file and a constructive system, EnerHabitat computes:
| Symbol | Description |
|---|---|
Ta |
Outdoor ambient temperature |
Tsa |
Sun-air temperature |
Ti |
Indoor temperature |
Tn |
Adaptive comfort (neutral) temperature |
Ig |
Global horizontal irradiance |
Ib |
Direct normal irradiance |
Id |
Diffuse horizontal irradiance |
Is |
Solar irradiance on the tilted surface |
Theoretical background
EnerHabitat solves the 1-D, time-dependent heat conduction equation across the constructive system:
∂T ∂²T
ρ c_p ── = k ────
∂t ∂x²
The exterior boundary condition uses the sun-air temperature, which combines convection, short-wave solar gain and long-wave radiative losses:
T_sa = T_o + (I_s a) / h_o + RF
where:
T_o— outdoor ambient temperatureI_s— solar irradiance incident on the surfacea— external solar absorptanceh_o— outdoor convective heat transfer coefficientRF— long-wave radiative loss factor (°C). EnerHabitat usesRF = -3.9°C for horizontal surfaces (tilt = 0, e.g. a roof, where the surface sees the cold sky) andRF = 0°C for vertical walls (tilt = 90).
The equation is discretised with finite control volumes and solved with the TDMA (Tri-Diagonal Matrix Algorithm). The simulation runs over an average day of a selected month — built from the EPW — and is iterated until a periodic (oscillatory) steady state is reached.
Two solution modes are available:
- Free-running —
solve(): no air conditioning is applied; the indoor temperature follows the dynamics of the constructive system. - Air-conditioned —
solveAC(): the indoor temperature is held at a comfort setpoint derived from the Humphreys & Nicol adaptive comfort model combined with Morillón's comfort-zone amplitude proposal. EnerHabitat then applies the cooling or heating needed at every time step to keepTiat that setpoint, and reports the resultingcooling_energyandheating_energydemands.
Installation
pip install enerhabitat
With uv (we love it and warmly encourage its use — it is fast, reproducible, and our recommended way to install EnerHabitat):
uv add enerhabitat
EnerHabitat requires Python ≥ 3.10.
Recommended folder structure
materials.ini is required — EnerHabitat ships with no default materials,
so you must provide this file (see Materials for its format).
project/
├── main.py
├── materials.ini # Material properties (REQUIRED — user-provided)
└── epw/
├── ...
└── example.epw
Key concepts
Locationreads an EPW file and computes the average day withmeanDay().Systemcombines aLocationand a list of layers and computesTsa(),solve()andsolveAC().configis a global instance whose attributes (materials file, discretisation, convection coefficients, time step) affect every subsequent computation.
Quickstart
EnerHabitat does not ship with pre-loaded materials. Before running anything,
create a materials.ini file (see Materials) in your working
directory or point eh.config.file to its location.
import enerhabitat as eh
# 1) Materials file (required — no defaults are bundled)
eh.config.file = "./materials.ini"
# 2) Location from an EPW file
loc = eh.Location("./epw/example.epw")
# 3) Define the constructive system
wall = eh.System(location=loc)
wall.azimuth = 90
wall.absortance = 0.3
wall.layers = [("Adobe", 0.20)] # outside → inside
# 4) Average day and solar inputs
loc.meanDay(month=5, year=2025)
wall.Tsa()
# 5) Solve
ti = wall.solve()
Workflow
To simulate a wall (or roof) you need to:
- Geolocate it — pass an EPW file to
Location. - Orient it — set
azimuth(andtiltif needed). - Define its color — set
absortance. - Define its layers — set
layersfrom outside to inside. - Choose the period — call
location.meanDay(month, year). - Compute
Tsa(), then choose one solver:solve()— without air conditioning (free-running): the indoor temperatureTievolves freely with the dynamics of the constructive system.solveAC()— with air conditioning: the indoor temperature is held at a comfort setpoint and the cooling/heating energy required is reported.
Both solve() and solveAC() return pandas DataFrames indexed by time of day.
Examples
Two-layer system without air conditioning
import enerhabitat as eh
import pandas as pd
epw_file = "epw/MEX_CAM_Campeche-Ignacio.766961_TMYx.epw"
wall = eh.System(eh.Location(epw_file))
wall.azimuth = 90
wall.absortance = 0.3
wall.layers = [("Mortero", 0.025), ("Ladrillo", 0.10)]
wall.location.meanDay(month=5, year=2025)
wall.Tsa()
# Free-running solution
data = wall.solve()
# Attach Tsa to the result. Note that Tsa is a function of color, tilt,
# orientation, month and location, so it must be recomputed whenever any of
# those inputs change.
data = pd.concat([data, wall.Tsa().asfreq("10min")], axis=1)
Two-layer system with air conditioning
import enerhabitat as eh
import pandas as pd
epw_file = "epw/MEX_CAM_Campeche-Ignacio.766961_TMYx.epw"
wall = eh.System(eh.Location(epw_file))
wall.azimuth = 90
wall.absortance = 0.3
wall.layers = [("Mortero", 0.025), ("Ladrillo", 0.10)]
wall.location.meanDay(month=5, year=2025)
wall.Tsa()
# Air-conditioned solution: setpoint at the upper comfort bound
data = wall.solveAC()
data = pd.concat([data, wall.Tsa().asfreq("10min")], axis=1)
# Cooling and heating energy demands, in J/(m²·day) over one average day
print(wall.cooling_energy, wall.heating_energy)
API reference
import enerhabitat as eh
Location
loc = eh.Location("./epw/example.epw")
Attributes
The EPW path is stored in file. The following are read-only and recovered
from the EPW header — change file to update them:
city—str, city from the EPW headerlatitude—float, degreeslongitude—float, degreesaltitude—float, metrestimezone—pytz.timezone
loc.file = "./epw/other.epw"
Methods
meanDay(month, year)— average-day DataFrame (Ta,Ig,Ib,Id,Tn)copy()— returns a copy of the instanceinfo()— prints instance attributesflag()—dictwith metadata of the lastmeanDay()call
loc.meanDay(month=6, year=2020).info()
loc.info()
print(loc.flag()["date"])
System
loc = eh.Location("./epw/example.epw")
wall = eh.System(location=loc)
Attributes
-
location— associatedLocation -
tilt—float, degrees from horizontal (0= roof,90= vertical wall) -
azimuth—float, surface azimuth in degrees (pvlib convention, clockwise from north):Direction Azimuth North 0East 90South 180West 270 -
absortance—floatin[0, 1] -
layers—list[tuple[str, float]]of(material, thickness_m), ordered from outside to inside
wall.location = loc_2
wall.tilt = 0
wall.azimuth = 45
wall.absortance = 0.3
wall.layers = [("Adobe", 0.10), ("Acero", 0.05), ("Ladrillo", 0.02)]
wall.add_layer("Mortero", 0.20) # appended at the inside
wall.remove_layer(2) # removes layer at index 2
Read-only result attributes (all expressed in J/(m²·day) — energy per unit surface area, accumulated over one converged average day):
energy_transfer— total energy transferred to the indoor side fromsolve()heating_energy— heating demand fromsolveAC()cooling_energy— cooling demand fromsolveAC()
Units:
hi · Δt · ΔTwithhiin W/(m²·K),Δtin seconds andΔTin K yields J/m², and the loop accumulates these contributions over the 24 h of the average day, so the reported value is J/(m²·day). Divide by3600to get Wh/(m²·day) or by3.6e6to get kWh/(m²·day).
Methods
Tsa()— sun-air temperature andIsfromLocation.meanDay()solve()— indoor temperatureTi(free-running)solveAC()— cooling and heating energy with constant indoor setpointcopy()— returns a copy of the instanceinfo()— prints attributesflag()— reports whether the cached value was recomputed
wall.Tsa().info()
ti = wall.solve()
energy = wall.energy_transfer
wall.solveAC()
c_energy = wall.cooling_energy
h_energy = wall.heating_energy
Note:
Tsadepends onabsortance(color),tilt,azimuth(orientation),meanDay(month) andLocation. It must be recomputed whenever any of these inputs change. Attach it to a result DataFrame withdata = pd.concat([data, wall.Tsa().asfreq("10min")], axis=1).
Config (global)
config is a global singleton that stores parameters shared by every
Location and System. Changing it affects all subsequent computations.
Attributes (defaults shown; all are writable)
| Attribute | Default | Description |
|---|---|---|
file |
"materials.ini" |
Path to the .ini file with material properties |
La |
2.5 m |
Length of the fictional indoor space |
Nx |
200 |
Number of control volumes used to discretise the system |
ho |
13 W/(m²·K) |
Outdoor convective heat transfer coefficient |
hi |
8.6 W/(m²·K) |
Indoor convective heat transfer coefficient |
dt |
600 s |
Time step |
The default values for ho and hi are those prescribed by the Mexican
energy efficiency standards NOM-020-ENER and NOM-008-ENER for the
thermal envelope of buildings — both norms specify the same coefficients.
They can be overridden at any time:
eh.config.file = "./materials.ini"
# Inspect current values
eh.config.ho # 13.0
eh.config.hi # 8.6
# Other configuration parameters
eh.config.La = 2.0
eh.config.Nx = 300
eh.config.dt = 60
# Override the NOM-prescribed coefficients (NOM defaults are not enforced)
eh.config.ho = 12
eh.config.hi = 8.3
# Restore all defaults at any time (the NOM values for ho/hi included)
eh.config.reset()
config.materials is a read-only dict keyed by material name:
adobe = eh.config.materials["Adobe"]
adobe.k # W/m·K
adobe.rho # kg/m³
adobe.c # J/kg·K
Methods
info()— prints currentconfigvaluesto_dict()— returns parameters as adictreset()— restores default valuesmaterials_list()— list of material names defined infilematerials_dict()— dict of material properties
Materials
EnerHabitat does not bundle any default materials. You must supply a
materials.ini file — by default the package looks for materials.ini in the
current working directory; otherwise set eh.config.file to the path you want
to use.
Material properties are declared in an .ini file, with the material name as
the section header and k, rho and c as keys:
[concrete]
k = 1.35 # Thermal conductivity, W/m·K
rho = 1800 # Density, kg/m³
c = 1000 # Specific heat, J/kg·K
[adobe]
k = 0.58
rho = 1500
c = 1480
Point config.file to a different file when you need to switch material sets:
eh.config.file = "./config/new_materials.ini"
If config.file points at a missing file, EnerHabitat will report
Error: <path> not found and materials will be empty — System.solve() will
fail because the layer materials cannot be resolved.
Dependencies
Direct dependencies (declared in pyproject.toml):
Pulled in transitively and used internally:
Authors
Developed at the Instituto de Energías Renovables, UNAM.
- Guillermo Barrios del Valle — gbv@ier.unam.mx
- Fernando Rodríguez Calderón — ferrodriguez2509@gmail.com
Source code: https://github.com/Ener-Habitat/EnerHabitat
Issues: https://github.com/Ener-Habitat/EnerHabitat/issues
License
Released under the MIT License.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file enerhabitat-0.1.9.tar.gz.
File metadata
- Download URL: enerhabitat-0.1.9.tar.gz
- Upload date:
- Size: 66.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b10cf196cc15d1202a6cd76d8299e7ff469f5cbd339019839b21a17c5ed1d58
|
|
| MD5 |
2a1b5277a3190cc92ddfae2de61d643d
|
|
| BLAKE2b-256 |
086b605233e3a416b4dd0271783a0ee8e978b035759c6ef74f1d2f79f6048b05
|
File details
Details for the file enerhabitat-0.1.9-py3-none-any.whl.
File metadata
- Download URL: enerhabitat-0.1.9-py3-none-any.whl
- Upload date:
- Size: 19.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
df06a92a697a7876f267945ae6183483ee44831197e047a18485194167dd35f0
|
|
| MD5 |
cc95ad2d87d8f742f5050548c0799880
|
|
| BLAKE2b-256 |
5c51485315663347ae1a4340cf9a370a6ef10838c3df08a79385472e74649172
|