API reference
Manual reference for the public API. Everything is imported from the top level:
import enerhabitat as eh
# eh.Location, eh.System, eh.System2D, eh.HollowBlock, eh.Slab, eh.Fill,
# eh.config, eh.config2dLocation
Reads an EPW file and builds the average day of a month.
loc = eh.Location("./epw/example.epw")Attributes: the EPW path is stored in file; assigning a new path reloads the header. The rest are read-only, recovered from the EPW header:
| Attribute | Type | Description |
|---|---|---|
file |
str |
Path to the EPW file (writable) |
city |
str |
City, from the EPW header |
latitude, longitude |
float |
Degrees |
altitude |
float |
Metres |
timezone |
datetime.timezone |
Fixed offset built from the EPW decimal UTC offset (local standard time, no DST; fractional offsets like +5.5/+5.75 preserved) |
Methods:
meanDay(day="15", month="current", year="current"): average-day DataFrame (Ta;IgGHI,IbDNI,IdDHI;Tn,DeltaTn; solar position), cached; see how it is built.monthselects which EPW rows are averaged;dayandyearonly set the synthetic date used for the solar geometry and the index. In a TMY,year=2025does not select observations from 2025.info(): prints the attributes.flag(): dict with metadata of the lastmeanDay()call (e.g.flag()["date"]).
System (1D)
A multilayer wall or roof tied to a Location.
wall = eh.System(location=loc, tilt=90, azimuth=0, absortance=0.8, layers=[])Attributes:
| Attribute | Type | Description |
|---|---|---|
location |
Location |
Climate source |
tilt |
float |
Degrees from horizontal: 0 roof, 90 vertical wall. Any tilt is accepted in 1D (\(I_s\) is transposed by pvlib and \(RF\) varies linearly from 3.9 °C at 0° to 0 at 90°); the 2D elements only admit 0/90 |
azimuth |
float |
Surface azimuth, pvlib convention (N=0, E=90, S=180, W=270) |
absortance |
float |
Solar absorptance of the outdoor surface, [0, 1]; validated, so values outside the range raise ValueError |
layers |
list[tuple[str, float]] |
(material, thickness_m), outside → inside |
setpoint |
float |
AC setpoint override; None (default) → the neutrality temperature \(T_n\) (?@eq-tn in the 1D model). Ignored by solve() |
Read-only results (energy per unit surface area over the converged day, in J/(m²·day); divide by 3600 for Wh or 3.6e6 for kWh):
| Attribute | Set by | Meaning |
|---|---|---|
energy_transfer |
solve() |
Energy delivered to the indoor air |
cooling_energy |
solveAC() |
Cooling demand to hold \(T_n\) |
heating_energy |
solveAC() |
Heating demand to hold \(T_n\) |
days |
both | Day-to-day iterations used by the last solve (capped at 60) |
day_error |
both | Final day-to-day error (°C): max of the solid-field mean change and the indoor-state change |
converged |
both | True if the periodic regime was reached; otherwise a RuntimeWarning is emitted |
energy_imbalance |
solve() |
Energy-closure check \(\|Q_{in}-Q_{out}\|/\max(Q_{in},Q_{out})\) (≈ 0 when periodic); None after solveAC() |
Tso, Tsi |
both | Read-only surface temperature Series (outdoor/indoor surface, °C) of the last solve, on the Tsa() grid; None before solving. They concatenate directly with Ti and Tsa() |
Methods:
Tsa(): sun–air temperature andIsfor the current absorptance/orientation/month, on the solver’s time grid (results concatenate directly withsolve()output).solve(): free-running indoor temperatureTi(pandas.Seriesnamed"Ti"; use.to_frame("Ti")when a DataFrame is needed).solveAC():Tiheld at \(T_n\) (constantpandas.Series); setscooling_energy/heating_energy.add_layer(material, width)/remove_layer(index): edit the stack (appends at the inside).info().
The caches (in 1D and 2D alike) compare the current inputs on every call:
Tsa()recomputes whenabsortance,tilt,azimuth, themeanDayselection, theLocationor the configuration change, and the solvers additionally watchlayers, including in-place mutations (.append(...)).solve()andsolveAC()keep separate caches, so alternating modes reuses each result. A prior manualTsa()call is optional, for inspection.
System2D
Mirror of System for cross-sections that are heterogeneous across their width. Same Location, tilt, azimuth, absortance, setpoint, Tsa(), solve(), solveAC() and result attributes. Differences:
layerscontains, besides ordinary(material, thickness)tuples, exactly one 2D element: aHollowBlockor aSlab. The element’s thickness is derived from itsgeometry.- The stack accepts at most 7 layers, including the 2D element; the section builder uses the fixed seven slots (
L1…L7) inherited from the C engine. Exceeding it raisesValueError: at most 7 layers (including the 2D element).The 1DSystemhas no layer limit. - Orientation is validated:
HollowBlockrequirestilt = 90,Slabtilt = 0. - Additional outputs beyond the 1D set:
Tfield, theThuecoSeries and the inspectorspreview()/section_report(). - Extra attributes:
| Attribute | Description |
|---|---|
days |
Day-to-day iterations used by the last solve |
converged |
True if the last solve met tol_day and every inner step converged; a RuntimeWarning is emitted otherwise |
day_error |
Final day-to-day error (°C) of the last solve: max over the solid field, \(\|\Delta T_i\|\) and each cavity \(\|\Delta T_h\|\) |
inner_iterations |
Largest inner sweep count observed in the last solve |
energy_imbalance |
Energy-closure check \(\|Q_{in}-Q_{out}\|/\max(Q_{in},Q_{out})\) of the last free-running solve (≈ 0 when periodic); None after solveAC() |
Tso, Tsi |
Read-only surface temperature Series (outer/inner-surface mean, °C) of the last solve, on the Tsa() grid; None before solving. As in 1D, they concatenate directly with Ti and Tsa(). Sampling convention inherited from the C: Tso is recorded at the start of each time step, Ti/Tsi at the end |
Thueco |
Read-only cavity-air temperature Series (°C): the mean over the N cavities in Slab, NaN with Fill.SOLID; None before solving |
Tfield |
Temperature field (nx, ny) in °C at the end of the last simulated day (outside → inside along the second axis), useful for cross-section plots |
- Section inspector (before solving):
wall.section_report() # table: node types + materials (k, ρc, y-range)
wall.preview() # to-scale panels (matplotlib, extra `viz`; ASCII fallback)
wall.section() # NT / k / rhoc arrays + meshHollowBlock
Concrete hollow block for walls (tilt = 90): a shell of one material with one cavity per repeating cell.
block = eh.HollowBlock(
material = "Concreto",
fill_type = eh.Fill.AIR, # or eh.Fill.SOLID
fill_material = None, # required if SOLID
emissivity = 0.9, # cavity-wall emissivity (used if AIR)
geometry = {...}, # see below, metres
)geometry key |
Meaning |
|---|---|
web |
Half web (rib) thickness; the full web between cavities is 2·web |
block_width |
Cavity width |
cover_top |
Outer shell |
cavity |
Cavity height (through the thickness) |
cover_bottom |
Inner shell |
Cell width = 2·web + block_width; thickness = cover_top + cavity + cover_bottom.
Slab
Joist-and-block (vigueta y bovedilla) unit for roofs (tilt = 0): compression topping, L-shaped rib (web + foot) at each cell edge, filler block, and n_cavities equal cavities.
slab = eh.Slab(
rib_material = "Concreto",
block_material = "Bovedilla",
topping_material = "Concreto",
fill_type = eh.Fill.AIR, # or eh.Fill.SOLID (+ fill_material)
fill_material = None,
emissivity = 0.9,
geometry = {...},
)geometry key |
Meaning |
|---|---|
web, foot |
Rib web and foot (the L profile) |
shoulder |
Filler block between rib and cavities |
n_cavities, cavity_width |
Number and width of the equal cavities |
topping |
Compression topping thickness |
topping_cap |
Topping above the rib web (the web rises below this line) |
cover_top, cavity, cover_bottom |
Filler-block band through the thickness |
Cell width = 2·(web+foot) + (n+1)·shoulder + n·cavity_width; thickness = topping + cover_top + cavity + cover_bottom.
Fill
Enum for the cavity content of HollowBlock/Slab: Fill.AIR (cavity physics: radiation + Nusselt convection, needs emissivity) or Fill.SOLID (conduction only, needs fill_material).
config (global)
Global singleton; changing it affects all subsequent computations.
| Attribute | Default | Description |
|---|---|---|
file |
"materials.ini" |
Materials file path |
La |
2.5 m |
Indoor air depth |
Nx |
200 |
1D control volumes |
ho |
13 W/(m²·K) |
Outdoor film coefficient (NOM) |
hi |
8.1 W/(m²·K) |
Indoor film coefficient for walls (NOM, vertical-surface value) |
hi_up |
9.4 W/(m²·K) |
Indoor coefficient for roofs, upward heat flow (NOM) |
hi_down |
6.6 W/(m²·K) |
Indoor coefficient for roofs, downward heat flow (NOM) |
hi_flow |
True |
Roofs (tilt < 60°) pick hi_up/hi_down per time step; False → fixed hi everywhere |
dt |
10 s |
Fixed; assignments are ignored (why) |
AIR_DENSITY |
1.18 kg/m³ |
Indoor/cavity air density |
AIR_HEAT_CAPACITY |
1005 J/(kg·K) |
Indoor/cavity air specific heat |
Methods: info(), to_dict(), reset() (restores the numeric defaults; file/materials are user-provided and are kept), materials_list(), materials_dict(); config.materials is a read-only dict of material properties (.k, .rho, .c).
Setters validate, raising ValueError: La, ho, hi, hi_up, hi_down and the air properties must be > 0; Nx must be an integer ≥ 3; materials must have k, rho, c > 0 (checked when the .ini is loaded; on failure the previous materials are kept). Layer thicknesses must be > 0 (checked at solve time).
config2d (2D)
| Attribute | Default | Description |
|---|---|---|
nx |
80 |
Nodes across the cell width (adiabatic sides) |
ny |
160 |
Nodes through the thickness (outside → inside) |
tol_inner |
1e-8 °C |
Inner-step tolerance: max node update and max scaled equation residual must both fall below it |
tol_day |
5e-4 °C |
Day-to-day convergence tolerance |
max_days |
60 |
Cap on day-to-day iterations |
max_inner |
10000 |
Cap on inner sweeps per time step; if hit, the solve is flagged converged = False |
Methods: info(), to_dict(), reset(), validate().
The attributes are plain (assignment is not checked); validate() runs at the start of every System2D solve and raises ValueError if nx/ny are not integers ≥ 3, a tolerance is ≤ 0, or max_days/max_inner are not integers ≥ 1.