Building physics: catalogue¶
Multi-zone airflow, heat and contaminants (CONTAM's physics; CONTAM .prj/.wth, EPW and Modelica Buildings Library readers). The airflow constitutive laws and drives a building uses come from the core library and are listed here too. How the pieces fit together: Building physics.
Every application is set up the same way; see Using noodl.
Import from noodl.apps.building_physics and the modules named in each table. The full reference, with every parameter, is in the API reference.
Network attributes¶
What the builders, constitutive laws and drives read off net.add_node(...) and net.add_edge(...), by name. Defined in noodl.apps.building_physics.attributes.
Nodes
| Attribute | Meaning | Read by |
|---|---|---|
volume |
air volume of the zone (m3); 0 for a wall node | thermal_layer, species_layer |
T0 |
initial temperature (K), also the boundary temperature of a boundary node | initial_state, initial_drivers, project_to_model |
z_ref |
height of the node's reference pressure (m) | Stack.from_network |
position |
absolute (x, y, z) of the node's component (m), set by Component.flatten; for drawing only | noodl.components |
heat_capacity |
extra lumped heat capacity of the node (J/K); a wall node's capacity | thermal_layer |
in_building_volume |
1 if the zone counts in the building's conditioned space, 0 if not (CONTAM's zone flag 16, set by the CONTAM reader); absent means 1 | building_air_change_rate |
Edges of kind airpath
| Attribute | Meaning | Read by |
|---|---|---|
z_path |
height of the opening (m) | Stack.from_network |
Cd |
discharge coefficient (dimensionless) | orifice_law_from_edges |
area |
opening area (m2) | orifice_law_from_edges |
name |
label of the edge in model.refs and in named results (optional) | noodl.refs |
Edges of kind airpath, to or from the ambient node
| Attribute | Meaning | Read by |
|---|---|---|
azimuth |
facade direction, in the unit of the theta_w driver (degrees from CONTAM) | Wind.from_network |
Cp |
constant wind pressure coefficient when the edge has no profile | Wind.from_network |
facade |
key of the building's inner_table('facades', ...); flatten writes the facade's azimuth | Component.flatten |
Ch |
wind speed modifier (dimensionless, 1 by default) | Wind.from_network |
profile |
number of the WindProfile to use; 0 means the constant Cp | Wind.from_network |
Edges of kind duct, a segment between two junctions (made by DuctNetwork)
| Attribute | Meaning | Read by |
|---|---|---|
length |
length of the segment (m) | duct_laws_from_edges |
diameter |
hydraulic diameter of the duct (m) | duct_laws_from_edges |
roughness |
roughness of the duct wall (m) | duct_laws_from_edges |
loss |
sum of the segment's dynamic loss coefficients (dimensionless) | duct_laws_from_edges |
area |
cross-section of the duct (m2); the circle of the diameter by default | duct_laws_from_edges |
Edges of kind duct_terminal, a junction to a zone (made by DuctNetwork)
| Attribute | Meaning | Read by |
|---|---|---|
area |
area of the duct at the terminal (m2) | duct_laws_from_edges |
loss |
loss coefficient of the terminal (dimensionless) | duct_laws_from_edges |
z_path |
height of the terminal, the junction's height (m) | Stack.from_network |
profile |
number of the WindProfile on a terminal in the ambient; absent or 0 for none | duct_laws_from_edges, Wind.from_network |
Cp |
constant wind pressure coefficient of a terminal in the ambient without a profile | duct_laws_from_edges, Wind.from_network |
Ch |
wind speed modifier (dimensionless, 1 by default) | Wind.from_network |
azimuth |
wall direction of a terminal with a profile (degrees from CONTAM) | Wind.from_network |
Edges of kind air_handler, an air handler's edges (made by add_air_handler)
| Attribute | Meaning | Read by |
|---|---|---|
role |
what the edge is: 0 supply point (plenum -> zone), 1 return point (zone -> plenum), 2 recirculation, 3 outdoor air, 4 exhaust | air_handler_from_edges |
design_flow |
design mass flow of a supply or return point (kg/s); 0 on the other edges | air_handler_from_edges |
outdoor_air_fraction |
on the recirculation edge: the outdoor-air fraction of the supply when no input sets it (dimensionless, 1 by default) | air_handler_from_edges |
min_outdoor_air |
on the recirculation edge: the minimum outdoor-air flow (kg/s, 0 by default) | air_handler_from_edges |
Edges of kind fan, a constant-mass-flow fan (made by the fan component)
| Attribute | Meaning | Read by |
|---|---|---|
flow |
mass flow delivered from source to target whatever the pressures (kg/s) | fan_law_from_edges |
Edges of kind wall
| Attribute | Meaning | Read by |
|---|---|---|
ua |
conductance zone -> wall or wall -> ambient (W/K) | thermal_layer |
Set-up¶
Build the model and its starting state and inputs.
| Name | What it does | How to call it |
|---|---|---|
build_modelnoodl.apps.building_physics |
Layers "air" (+ "thermal", + "species"), the density closure, one Model. | build_model(net, *, air_laws, drives, ambient='ambient', thermal=True, species=0, density='ideal_gas', density_kwargs=None, coupling='pingpong', iterate_tol=None, iterate_max=20, thermal_scheme=None, species_scheme='implicit', flow_kinds=None, closures=(), flow_balanced=(), storage_free=(), controls=None, return_inputs=False) |
initial_driversnoodl.apps.building_physics |
A complete driver set: zero boundary pressures, boundary temperatures from each boundary node's T0 attribute (T_REF where it has none), zero boundary mass fractions; then values, by name (see noodl.refs.drivers_from). |
initial_drivers(model, *, values=None) |
initial_statenoodl.apps.building_physics |
"<layer>.x" for every transport layer: temperatures from the node attribute T0 (interior order), mass fractions zero; then values. |
initial_state(model, *, values=None) |
project_to_modelnoodl.apps.building_physics |
Air layer + species layer (no thermal layer: a .prj carries no thermal data), the initial state and the drivers for the project's ambient conditions (or ambient). |
project_to_model(project, *, ambient=None, species=True, scheme='implicit') |
Network components¶
Descriptions of the physical objects a network is built from.
| Name | What it does | How to call it |
|---|---|---|
AirThroughFilternoodl.apps.building_physics |
The air passing a filter: what a flow-dependent filter's efficiency reads. | AirThroughFilter(mass_flow, temperature, density, time=0.0) |
AmbientConcentrationsnoodl.apps.building_physics |
Ambient (outdoor) mass fractions over time: t (s from 00:00 of start_day, a day of the year), x (len(t), K) (kg/kg) for the species named, in file order. |
AmbientConcentrations(t, x, species, start_day=1) |
Actuatornoodl.apps.building_physics |
Writes block block's output into driver key at positions index on its last axis (the whole driver when None); mode="add" adds it to those entries instead (a heat flow into "thermal.sources", W). |
Actuator(block, key, index=None, mode='set', name='') |
Evaluationnoodl.apps.building_physics |
What a block sees: the state the evaluation reads, the drivers, and the step length in s (None: an algebraic evaluation that advances nothing). |
Evaluation(state, drivers, dt) |
Projectnoodl.apps.building_physics |
A CONTAM project as read by read_prj: the network, its airflow laws and drives, zones and their temperatures and volumes, species, initial mass fractions, sources and ambient conditions, the run control, and the schedules as inputs. project_to_model builds the model from it. |
Project(net, laws, drives, zones, ambient, paths, species, T_zone, zone_volumes, g, ambient_conditions, profiles, x0, sources, kinds, zone_nr_to_name, density_uses_zone_pressure=False, run_control=None, schedules=<factory>, inputs=<factory>, ambient_mass_fractions=None, multiplier_edges=<factory>, speed_edges=<factory>, constant_wind_pressure=<factory>, zone_flags=<factory>, source_path=None, species_records=<factory>, zone_reactions=<factory>, edge_filters=<factory>, junctions=<factory>, junction_x0=None, terminals=<factory>, segments=<factory>, schedule_units=<factory>, terminal_openings=0, flow_balanced=<factory>, storage_free=<factory>, closures=<factory>, air_handler_edges=<factory>, junction_nr_to_name=<factory>, control_nodes=<factory>, control_plan=None, occupants=<factory>) |
RunControlnoodl.apps.building_physics |
A project's run control: what to simulate and when. | RunControl(transient_airflow, contaminants, integration, integration_weight, steady_day, steady_time, start_day, start_time, end_day, end_time, time_step, output_step, day_type, weather_file, ambient_file, zone_mass_change) |
Speciesnoodl.apps.building_physics |
One contaminant species: CONTAM's species record. | Species(name, molar_mass=0.0, mean_diameter=0.0, effective_density=0.0, decay=0.0, diffusivity=0.0, default_mass_fraction=0.0, non_trace=False, uv_susceptibility=0.0, description='') |
WallMassnoodl.apps.building_physics |
A lumped wall node: capacity [J/K], conductances to the zone and to ambient [W/K]. | WallMass(name, capacity, ua_zone, ua_ambient) |
Weathernoodl.apps.building_physics |
A weather series: time t (s from 00:00 of the file's start day start_day, a day of the year), ambient temperature Ta (K), barometric pressure Pb (Pa), wind speed Ws (m/s) and direction Wd (degrees from north, blowing from), and optionally the humidity ratio humidity_ratio (kg water / kg dry air) and each day's CONTAM day type day_types {day of year: 1-12}. at(t) interpolates; signal(name) and inputs(model) feed simulate. |
Weather(t, Ta, Pb, Ws, Wd, start_day=1, day_types=<factory>, humidity_ratio=None) |
Zonenoodl.apps.building_physics |
A room: its air volume (m3), initial temperature T0 (K), reference height z_ref (m) and an optional lumped wall (WallMass). Add it with add_zone. |
Zone(name, volume, T0=293.15, z_ref=0.0, wall=None) |
Constitutive laws¶
The relation between the flow through an edge and the potential difference across it.
| Name | What it does | How to call it |
|---|---|---|
AffinityFannoodl.elements |
A fan curve run at another speed and air density, by the fan laws (ContamX's performance-curve fan, measured against ContamX 3.4.1.7). | AffinityFan(curve, *, src, tgt, rho_ref, speed_key=None, off=0.0, multiplier=1.0, shutoff=None, rho_key='rho', kind=None) |
AirDuctnoodl.elements |
A duct segment, F (kg/s) from dp (Pa): Darcy-Weisbach with ContamX's friction factor. | AirDuct(length, diameter, roughness, loss=0.0, *, area=None, src, tgt, rho_key='rho', temperature_key=None, kind='duct', learnable=False) |
Conductancenoodl.elements |
q = g dp, the linear branch law used for thermal conduction and passive exchange; g is in the flow unit per unit of potential (W/K for heat, kg/s per Pa for air). |
Conductance(g, *, kind='conduction', learnable=False) |
CubicSplineFlownoodl.elements |
An airflow element given by measured points, fitted as ContamX fits them (SplineCurve): CONTAM's cubic-spline elements. |
CubicSplineFlow(x, y, *, independent='pressure', volume=False, multiplier=1.0, src, tgt, rho_key='rho', kind='airpath') |
Dampernoodl.elements |
Separate power-law coefficient and exponent for each flow direction: C_pos/n_pos for dp > 0 and C_neg/n_neg for dp < 0, each as in PowerLaw (C in the flow unit per Pa^n, n dimensionless). |
Damper(C_pos, n_pos, C_neg, n_neg, *, dp_transition=0.001, src=None, tgt=None, m=0.0, rho_ref=1.2041, rho_key='rho', kind='damper', learnable=False) |
DoorCompartmentalso MBLDoorCompartmentnoodl.elements |
One compartment of Buildings.Airflow.Multizone.DoorDiscretizedOpen per edge. |
DoorCompartment(*, src, tgt, medium, dA, CD=0.65, m=0.5, dp_turbulent=0.01, vZer=0.001, T_key='T', Xw_key='X_w', p_key='p_abs', kind='door', learnable=False) |
DoorPortStreamalso MBLDoorPortStreamnoodl.elements |
One of the two port streams of a whole discretised door, as ONE edge from side A to side B: direction="ab" returns port_a1.m_flow = mAB_flow, "ba" returns port_b2.m_flow = -port_a2.m_flow = -mBA_flow (kg/s, both positive from A to B), from the compartment law comp (:class:DoorCompartment or :class:OperableDoorCompartment, one door) at the compartment pressure differences dp + head(drivers) (:class:DoorCompartmentHead). The edge's own dp is the zones' potential difference phi_A - phi_B. |
DoorPortStream(comp, head, direction, kind) |
Ductnoodl.elements |
Colebrook duct: F(dp) with friction from the Colebrook equation, laminar below Re_t. Length L, diameter D and roughness eps (m); sum_C the dimensionless sum of dynamic loss coefficients; A (m2) defaults to the circular area; rho (kg/m3) and mu (Pa s); the flow is a mass flow in kg/s. |
Duct(L, D, eps, sum_C=0.0, *, A=None, Re_t=2000.0, rho=1.2041, mu=1.81625e-05, n_iter=4, kind='duct', learnable=False) |
DuctTerminalnoodl.elements |
A duct terminal, the zero-length duct between a junction and its zone, F (kg/s) from dp (Pa), as ContamX 3.4.1.7 computes it (measured). | DuctTerminal(area, loss, *, src, tgt, rho_key='rho', temperature_key=None, opening_key=None, kind='duct_terminal', learnable=False) |
FanCurvenoodl.elements |
Cubic pressure-flow curve inverted for q given dp on [0, q_max]: coeffs are (a0, a1, a2, a3) of P(q) = a0 + a1 q + a2 q^2 + a3 q^3 in Pa for q in the flow unit, and q_max is the largest flow the curve covers, in that unit. |
FanCurve(coeffs, q_max, *, kind='airpath', learnable=False) |
FixedFlownoodl.elements |
q = q0 regardless of dp (q0 in the flow unit); dflow = 0; linear_init = (q0, 0). |
FixedFlow(q0, *, kind='airpath', learnable=False) |
FixedVolumeFlownoodl.elements |
F = Q rho_up whatever dp: a constant volume flow volume_flow Q (m3/s, one per edge) delivered as mass flow at the density of the node it draws from (src when Q >= 0, tgt otherwise), read from drivers[rho_key] (full node order). ContamX's constant-volume fan in a duct (measured). |
FixedVolumeFlow(volume_flow, *, src, tgt, rho_key='rho', kind='fan', learnable=False) |
OpenDooralso MBLDoorOpennoodl.elements |
Buildings.Airflow.Multizone.DoorOpen: one direction of the always-open door. |
OpenDoor(*, direction, src, tgt, medium, wOpe=0.9, hOpe=2.1, CD=0.65, m=0.5, dp_turbulent=0.01, T_key='T', kind='door', learnable=False) |
OperableDooralso MBLDoorOperablenoodl.elements |
Buildings.Airflow.Multizone.DoorOperable: the door blended between open and closed by the opening signal y read from drivers[y_key]. |
OperableDoor(*, direction, src, tgt, medium, y_key, LClo, wOpe=0.9, hOpe=2.1, CDOpe=0.65, mOpe=0.5, mClo=0.65, dpCloRat=4.0, CDCloRat=1.0, dp_turbulent=0.01, T_key='T', kind='door', learnable=False) |
OperableDoorCompartmentalso MBLDoorCompartmentOperablenoodl.elements |
One compartment of Buildings.Airflow.Multizone.DoorDiscretizedOperable per edge, blended between the open door and the closed crack by y = drivers[y_key]. |
OperableDoorCompartment(*, src, tgt, medium, y_key, AOpe, LClo, nCom, CDOpe=0.65, CDClo=0.65, CDCloRat=1.0, dpCloRat=4.0, mOpe=0.5, mClo=0.65, dp_turbulent=0.01, vZer=0.001, T_key='T', Xw_key='X_w', p_key='p_abs', kind='door', learnable=False) |
PowerLawnoodl.elements |
q = C sign(dp) |dp|^n, laminar-blended or smoothly regularised near dp = 0. dp is in Pa; C is in the flow unit per Pa^n (kg/s for a mass-flow coefficient, m3/s for a volume-flow one), so the flow comes out in whatever unit C carries; n is dimensionless (0.5 for an orifice, towards 1 for a laminar crack). |
PowerLaw(C, n, *, dp_transition=0.001, regularised=None, kind='airpath', learnable=False) |
PrescribedFlownoodl.elements |
F = drivers[key] whatever dp: a flow (kg/s, one value per edge of the kind) that a closure or an input prescribes every step, e.g. an air handler's supply flows. dtype is the layer's: linear_init, which has no dp to take it from, returns it. |
PrescribedFlow(key, *, kind, dtype=torch.float64) |
Quadraticnoodl.elements |
q = sign(dp) * 2|dp| / (sqrt(a^2 + 4 b |dp|) + a): the inverse of dp = a q + b |q| q, with dp in Pa, a in Pa per flow unit and b in Pa per flow unit squared. |
Quadratic(a, b, *, kind='airpath', learnable=False) |
RegularizedPowerLawalso MBLPowerLawnoodl.elements |
MBL's regularised power law, q = C sign(dp) \|dp\|^m, volume or mass form. |
RegularizedPowerLaw(C, m, *, dp_turbulent=0.1, form, rho_default, kind='airpath', learnable=False) |
Scalednoodl.elements |
F = s F_law: law's flow times the dimensionless multiplier drivers[key]. |
Scaled(law, key) |
SplineFlowTablealso MBLTablenoodl.elements |
MBL's tabulated flow law: a monotone cubic Hermite spline through (dp, flow) knots, linearly extrapolated outside them; flow(dp) always returns MASS flow in kg/s. |
SplineFlowTable(dp_points, flow_points, *, form, rho_default=None, kind='airpath') |
UpstreamDensityPowerLawnoodl.elements |
PowerLaw with C scaled by (rho_upstream / rho_ref) ** m, upstream by sign(dp). |
UpstreamDensityPowerLaw(C, n, *, src, tgt, m, rho_ref=1.2041, rho_key='rho', dp_transition=0.001, regularised=None, kind='airpath', learnable=False) |
Drives¶
Terms added to an edge's potential difference (stack, wind, fans).
| Name | What it does | How to call it |
|---|---|---|
DoorCompartmentHeadnoodl.elements |
The hydrostatic part of DoorDiscretized.mo:64-66, as a noodl drive: rho_A hAg[i] - rho_B hBg[i] per compartment edge, shape (..., n_edges). |
DoorCompartmentHead(kind, *, src, tgt, hAg, hBg, medium, T_key='T', Xw_key='X_w', p_key='p_abs') |
ConstantDrivenoodl.drives |
A drive that reads a pre-computed, already batched value from drivers. |
ConstantDrive(kind, key) |
Stacknoodl.drives |
Hydrostatic stack term (CONTAM TN 1887r1 eq. 17 with eq. 64-65 at the path elevation). | Stack(kind, *, src, tgt, z_path, z_ref, rho_key='rho', g=9.80665) |
DuctStacknoodl.drives |
Hydrostatic term of a duct segment between two junctions: the air column at the density of the junctions' MEAN TEMPERATURE (ContamX 3.4.1.7, measured), which at one pressure is the harmonic mean of the two node densities. | DuctStack(kind, *, src, tgt, z_ref, rho_key='rho', g=9.80665) |
WindProfilenoodl.drives |
CONTAM wind pressure profile: Cp versus relative wind angle, periodic in the angle. | WindProfile(angles_deg, cp, *, curve='linear') |
Windnoodl.drives |
Wind pressure on envelope paths (CONTAM TN 1887r1 section 3.15). Drivers: rho_amb in kg/m3, V_met in m/s and theta_w in the unit the azimuth edge attribute uses (degrees for networks read from CONTAM). |
Wind(kind, *, sign, envelope, azimuth, ch, cp_const, profile_index, profiles=(), rho_key='rho_amb', speed_key='V_met', direction_key='theta_w') |
Closures¶
Functions of the state that compute drivers each step (densities, flows, levels).
| Name | What it does | How to call it |
|---|---|---|
AirHandlerFlowsnoodl.apps.building_physics |
The closure that prescribes every flow of one air handler each step, by CONTAM's algorithm (see the module) from its design flows and its outdoor-air fraction. | AirHandlerFlows(kind, *, roles, design_flows, outdoor_air_fraction=1.0, min_outdoor_air=0.0, name='') |
ControlNetworknoodl.apps.building_physics |
Blocks evaluated in order and the actuators that write their outputs into drivers: a model closure that keeps the block outputs in the state. Blocks without a name are named "block <i>". |
ControlNetwork(blocks, actuators=(), *, sampled=True, name='controls') |
DriverReadingnoodl.apps.building_physics |
factor * drivers[key][..., *index]. In a sampled network, the value the previous step used (ContamX's sensors see a scheduled temperature one step late); the first evaluation reads the current value. |
DriverReading(key, index, *, factor=1.0) |
FunctionReadingnoodl.apps.building_physics |
fn(state, drivers), for any other reading (the Modelica route's zone temperature). key names the state key it reads, when it reads one (see ControlNetwork.reads_state). |
FunctionReading(fn, *, previous_step=False, key=None) |
StateReadingnoodl.apps.building_physics |
factor * state[key][..., *index], or default while the state has no key (a flow or a pressure before the first airflow solve). |
StateReading(key, index, *, factor=1.0, default=0.0) |
FilterTransmissionnoodl.apps.building_physics |
Closure: the species layer's per-step transmission "<layer>.transmission", (..., b_flow, K), for filters that include flow-dependent ones (UVGIFilter). |
FilterTransmission(net, flow_kinds, species, filters, *, airflow, layer='species', on_reverse=None) |
IdealGasDensitynoodl.apps.building_physics |
rho = P_ref / (R T); P_ref from drivers["P_ref"] if present (CONTAM section 3.18). | IdealGasDensity(thermal, *, ambient_index, P_ref=101325.0, R=287.055) |
LinearDensitynoodl.apps.building_physics |
Boussinesq: rho = rho_0 (1 - (T - T_0) / T_0), the form the analytical natural- ventilation solutions (Li and Delsante 2001) assume. | LinearDensity(thermal, *, ambient_index, rho_0=1.2041, T_0=293.15) |
OccupantGenerationnoodl.apps.building_physics |
Closure: the occupants' generation added to "<layer>.sources" (kg/s, full node order) of the species layer (a TransportLayer). |
OccupantGeneration(occupants, *, layer, species=None) |
SpeciesSourcesnoodl.apps.building_physics |
Closure: every source's generation summed into "<layer>.sources" (kg/s, full node order), added to what an earlier closure or a driver put there, and every removal coefficient into "<layer>.removal" (1/s, active interior order), each times its entry of "<layer>.source_multiplier", on its own species. |
SpeciesSources(sources, *, species, layer, names=None, heights=None, g=9.80665, pressure_key='air.phi') |
Sources¶
Nodal sources and withdrawals.
| Name | What it does | How to call it |
|---|---|---|
BurstSourcenoodl.apps.building_physics |
mass delivered over the step(s) covering [t_burst, t_burst + dt). |
BurstSource(node, mass, t_burst, dt) |
ConstantSourcenoodl.apps.building_physics |
S = G - R x (constant coefficient model): G kg/s, R kg/s per unit mass fraction (CONTAM's kg_air/s). Under SpeciesSources the term R x is implicit in the species step, as ContamX holds it; called directly the source is evaluated at the x given. |
ConstantSource(node, G, R=0.0) |
CutoffSourcenoodl.apps.building_physics |
S = G (1 - x / x_cut) (cutoff concentration model): G kg/s, x_cut kg/kg. | CutoffSource(node, G, x_cut) |
DecayingSourcenoodl.apps.building_physics |
S = G0 exp(-(t - t0) / tau) for t >= t0, else 0. | DecayingSource(node, G0, tau, t0=0.0) |
DepositionRateSinknoodl.apps.building_physics |
First-order removal at a deposition rate (CONTAM's deposition rate sink): R = k_d rho V, the rate k_d (1/s) on the zone's mass fraction; rate a float or tensor. It is also how to give a species a first-order decay in a zone. |
DepositionRateSink(node, rate) |
DepositionVelocitySinknoodl.apps.building_physics |
Deposition onto a surface at a deposition velocity (CONTAM's deposition velocity sink): R = v_d A rho, a first-order rate v_d A / V on the zone's mass fraction. velocity v_d in m/s, area A in m2 (floats or tensors). Its node needs a positive volume: the density is the zone's air mass over it. |
DepositionVelocitySink(node, velocity, area) |
PeakEmissionSourcenoodl.apps.building_physics |
Emission rising to a peak and decaying (the NRCC peak model): S = a exp(-(ln(t / t_p) / b)^2 / 2), t the time since the start (s), nothing at t <= 0. peak a kg/s, width b, t_peak t_p s. |
PeakEmissionSource(node, peak, width, t_peak) |
PowerLawDecaySourcenoodl.apps.building_physics |
Emission decaying as a power of time (the NRCC power-law model): S = a t_h^(-b) after t_p and a t_ph^(-b) before it, t_h being the time since the start in HOURS (measured: ContamX takes the power of the time in hours, although its file stores t_p in s). coefficient a kg/s, exponent b, t_p s (> 0: the power has no value at t = 0). |
PowerLawDecaySource(node, coefficient, exponent, t_p) |
PressureDrivenSourcenoodl.apps.building_physics |
Entry driven by the pressure difference across the zone's envelope (CONTAM's pressure-driven model: radon, soil gas): S = G max(dP, 0)^n, dP = P_ambient - P_zone at the zone's reference height (Pa). coefficient G in kg/(s Pa^n), exponent n. Nothing enters while the zone is at or above the ambient pressure (measured against ContamX 3.4.1.7, which evaluates it at the step's own pressures). |
PressureDrivenSource(node, coefficient, exponent) |
Files¶
Readers and writers of other tools' file formats.
| Name | What it does | How to call it |
|---|---|---|
read_ctmnoodl.apps.building_physics |
A CONTAM .ctm ambient-concentration file as AmbientConcentrations. |
read_ctm(path) |
read_epwnoodl.apps.building_physics |
An EnergyPlus .epw weather file as a Weather series (ambient temperature, pressure, wind speed and direction), from start_day for n_hours hours. |
read_epw(path, *, start_day=1, n_hours=None) |
read_modelicanoodl.apps.building_physics |
Read one noodl-modelica/1 JSON file into (model, state, drivers). |
read_modelica(path, *, return_names=False, substeps=1, mass_storage=None) |
read_prjnoodl.apps.building_physics |
A CONTAM .prj project as a Project: zones, airflow paths and their elements, species and sources; project_to_model turns it into a model. |
read_prj(path) |
read_wthnoodl.apps.building_physics |
A CONTAM .wth weather file as a Weather series (ambient temperature, pressure, wind speed and direction, humidity ratio, and each day's day type). |
read_wth(path) |
write_wthnoodl.apps.building_physics |
WeatherFile ContamW 2.0, one row per sample, the columns read_wth reads. |
write_wth(weather, path, *, start_date='1/1') |
Other classes¶
Supporting types.
| Name | What it does | How to call it |
|---|---|---|
ContamCompatibilityWarningnoodl.apps.building_physics |
The reader follows ContamX where ContamX ignores data the file carries. | ContamCompatibilityWarning(...) |
AboveLimitnoodl.apps.building_physics |
1 where u > limit (strict), else 0. |
AboveLimit(a, limit, *, name='') |
Absolutenoodl.apps.building_physics |
\|u\|. |
Absolute(a, *, name='') |
Affinenoodl.apps.building_physics |
(u - offset) * gain: a unit conversion or a gain. |
Affine(a, *, offset=0.0, gain=1.0, name='') |
Andnoodl.apps.building_physics |
1 where both inputs are > 0. | And(a, b, *, name='') |
Averagenoodl.apps.building_physics |
The mean of its inputs. | Average(*inputs, name='') |
BelowLimitnoodl.apps.building_physics |
1 where u < limit (strict), else 0. |
BelowLimit(a, limit, *, name='') |
Blocknoodl.apps.building_physics |
One block. inputs are the positions, in the network's block list, of the blocks whose outputs it reads. step returns its output and its new memory, (..., memory_size). A sampled_only block keeps a previous step's value; a continuous_only block integrates in continuous time (rate). |
Block(...) |
Complementnoodl.apps.building_physics |
1 - u (CONTAM's "inverse" node; not a logical negation). |
Complement(a, *, name='') |
Constantnoodl.apps.building_physics |
A constant value; a block earlier in the order reads it before its first evaluation too (ContamX presets its constant nodes, measured). | Constant(value, *, name='') |
Dividenoodl.apps.building_physics |
numerator / denominator; a zero denominator raises naming the block (ContamX stops on it too). |
Divide(numerator, denominator, *, name='') |
DriverValuenoodl.apps.building_physics |
drivers[key][..., index] at this evaluation: a value an Input writes (a schedule into "controls.input"), or a driver such as "time". |
DriverValue(key, index=None, *, name='') |
Excessnoodl.apps.building_physics |
max(0, u - limit): how far the input is above the limit. |
Excess(a, limit, *, name='') |
ExclusiveOrnoodl.apps.building_physics |
1 where exactly one input is > 0. | ExclusiveOr(a, b, *, name='') |
Hysteresisnoodl.apps.building_physics |
ContamX's hysteresis: with x the input clipped to 0..1, the output follows the rising line max(0, (x - slack) slope) and the falling line min(1, x slope) and stays between them: y = min(falling, max(rising, y_prev)). The previous output starts at initial * slope. ContamX's files give the slope; the default 1 / (1 - slack), the slope that makes the falling line reach 1 where the rising line does, is a convenience for networks written in Python. |
Hysteresis(a, *, slack, slope=None, initial=0.0, name='') |
Identitynoodl.apps.building_physics |
Passes its input on (a signal split, a reported value). | Identity(a, *, name='') |
IncrementalPInoodl.apps.building_physics |
ContamX's PI controller on an error e, evaluated once per step of length dt (s). It is incremental: the output changes by a proportional part on the change of the error and an integral part on the trapezoid of the error over the step, y = clip(y_prev + kp (e - e_prev) + ki dt (e + e_prev) / 2, 0, 1), ki in 1/s. The stored previous output is the clipped one (which limits windup); the first evaluation updates from initial_output and initial_error. |
IncrementalPI(a, *, kp, ki, initial_output=0.0, initial_error=0.0, name='') |
LimitedPIDnoodl.apps.building_physics |
A P or PI controller with a limited output and anti-windup, in continuous time, as MSL 4.1.0's LimPID without its derivative part (Modelica/Blocks/Continuous.mo, LimPID): v = gain (setpoint_weight u_s - u_m + xi), y = clamp(v, y_min, y_max) and, with an integral_time Ti (s), d xi/dt = (u_s - u_m + (y - v)/(gain windup_ratio)) / Ti, xi(0) = initial_integral; without one, xi = 0 (a P controller). y_min defaults to -y_max. Its switching values, (v - y_max)/w and (v - y_min)/w with w = y_max - y_min, cross zero at the limiter's corners. |
LimitedPID(setpoint, measurement, *, gain, y_max, y_min=None, integral_time=None, setpoint_weight=1.0, windup_ratio=0.9, initial_integral=0.0, name='') |
LowerBandSwitchnoodl.apps.building_physics |
On (1) once u > limit + band, off (0) once u < limit, unchanged in between; off at the start (CONTAM's lower band switch). |
LowerBandSwitch(a, limit, *, band, name='') |
Maximumnoodl.apps.building_physics |
The largest of its inputs. | Maximum(*inputs, name='') |
Minimumnoodl.apps.building_physics |
The smallest of its inputs. | Minimum(*inputs, name='') |
Ornoodl.apps.building_physics |
1 where either input is > 0. | Or(a, b, *, name='') |
Positivenoodl.apps.building_physics |
1 where u > 0, else 0. |
Positive(a, *, name='') |
Productnoodl.apps.building_physics |
The product of its inputs. | Product(*inputs, name='') |
ProportionalControlnoodl.apps.building_physics |
clip(gain u, 0, 1) (CONTAM's proportional controller: the input is an error). |
ProportionalControl(a, *, gain, name='') |
Sensornoodl.apps.building_physics |
A reading converted to x = (value - offset) * scale and, with a time_constant (s), lagged: y = x + (y_prev - x) exp(-dt / time_constant), y = x at the first evaluation. A reading with previous_step gives, in a sampled network, the value the previous evaluation's step used (after stores it once the actuators have written). |
Sensor(reading, *, offset=0.0, scale=1.0, time_constant=0.0, name='') |
Shortfallnoodl.apps.building_physics |
max(0, limit - u): how far the input is below the limit. |
Shortfall(a, limit, *, name='') |
Sumnoodl.apps.building_physics |
sum_i w_i u_i (weights default 1): CONTAM's add, subtract and sum nodes, and Modelica's Add, Add3, Sum, MultiSum and Feedback. |
Sum(*inputs, weights=None, name='') |
UpperBandSwitchnoodl.apps.building_physics |
On (1) once u < limit - band, off (0) once u > limit, unchanged in between; off at the start (CONTAM's upper band switch). |
UpperBandSwitch(a, limit, *, band, name='') |
Calendarnoodl.apps.building_physics |
Where t = 0 sits in the year, and the CONTAM day type (1-12) of every day. | Calendar(*, start_day=1, start_time=0.0, start_day_type=1, day_types=None) |
ConstantEfficiencyFilternoodl.apps.building_physics |
Removes a fixed fraction of each species it lists, nothing of the others. | ConstantEfficiencyFilter(efficiencies, *, name='') |
DaySchedulenoodl.apps.building_physics |
One day of values at times of day: CONTAM's day schedule. | DaySchedule(times, values, *, shape='rectangular', name='') |
DuctNetworknoodl.apps.building_physics |
A duct system, described first and then added to a Network or a Component. |
DuctNetwork(*, kind='duct', terminal_kind='duct_terminal', ambient='ambient') |
FilterSeriesnoodl.apps.building_physics |
Filters in series (CONTAM's super filter): the air passes each stage in turn, so the pass fraction is the product of the stages' pass fractions. | FilterSeries(stages, *, name='') |
Inputnoodl.apps.building_physics |
signal's value written into drivers[key] at each step. |
Input(signal, key, *, index=None, at=None, name='') |
KineticReactionnoodl.apps.building_physics |
First-order reactions between species: dx_p/dt += k[p, r] x_r, x in kg/kg. | KineticReaction(coefficients, *, name='') |
Occupancynoodl.apps.building_physics |
An occupant's movements: one OccupancyDay per CONTAM day type (1-12). |
Occupancy(day_schedules, day_types=(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), *, name='') |
OccupancyDaynoodl.apps.building_physics |
An occupant's zone over one day: zones[i] from times[i] (s after midnight) until the next time. times run from 0 (00:00:00) to 86400 (24:00:00), strictly increasing. A zone is a node name, or None for out of the building. ContamX needs the 24:00:00 point too: without it the run stops. |
OccupancyDay(times, zones, *, name='') |
Occupantnoodl.apps.building_physics |
A person (or multiplier people) following an Occupancy, generating each species in generation (kg/s per person; quanta/s for a quanta species) while in the building. |
Occupant(occupancy, *, generation=None, multiplier=1.0, schedules=None, name='occupant', description='') |
ParticleFilternoodl.apps.building_physics |
Efficiency as a function of particle diameter (CONTAM's simple particle filter, e.g. a MERV curve); zero for a gas. | ParticleFilter(diameters, efficiencies, *, name='') |
UVGIFilternoodl.apps.building_physics |
Ultraviolet germicidal irradiation (CONTAM's Penn State UVGI filter element, TN 1887r1 equations uv-1 to uv-9): each species is inactivated with the single-pass efficiency | UVGIFilter(*, design_survival, design_velocity, design_mass_flow, design_rate_constant, output_coefficients, age_coefficients=None, name='') |
Presencenoodl.apps.building_physics |
An occupant's location as a signal over net's nodes (full node order, float64, dimensionless). |
Presence(occupancy, net, *, name='') |
Signalnoodl.apps.building_physics |
A time-varying input: value(t, calendar), a float64 tensor at t s since the simulation start (left limit at a jump for t > 0, right value at t == 0), and breakpoints(lo, hi, calendar), the times in (lo, hi) where it jumps or kinks. |
Signal(...) |
TimeSeriesnoodl.apps.building_physics |
Values at times since the simulation start (s): a measured or computed input. | TimeSeries(times, values, *, interpolation='linear', name='') |
WeekSchedulenoodl.apps.building_physics |
CONTAM's week schedule: one DaySchedule per day type (1-12). |
WeekSchedule(day_schedules, day_types=(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), *, name='') |
AirMediumalso MBLMediumnoodl.elements |
An air medium's default state and the two densities its flow elements need. | AirMedium(name, p_default, T_default, X_default, rho_default, has_moisture, _buoyancy, _density=None, _cp=None) |
LawContextnoodl.elements |
What a dynamic or implicit constitutive law needs besides its own edge values. | LawContext(mode, dt=None, t=None, integration='trapezoidal', dp_prev=None, q_prev=None, omega=None) |
SplineCurvenoodl.elements |
A measured curve y(x) fitted as ContamX fits it (see the module docstring). | SplineCurve(x, y, *, name='') |
Functions¶
Calculations and helpers.
| Name | What it does | How to call it |
|---|---|---|
add_air_handlernoodl.apps.building_physics |
Add an air handler to target (a Network or a Component): its two plenums (volumes in m3, temperature T0 in K) and its edges of kind. supplies maps each supplied zone to its design flow (kg/s), returns each zone it draws from. outdoor_air_fraction and min_outdoor_air (kg/s) are the defaults the closure uses when no input sets them. In a component the outdoor air and the exhaust reach the shared ambient through outer(ambient). Give each air handler of a building its own kind. |
add_air_handler(target, *, supplies, returns, kind='air_handler', supply_plenum='ahs_supply', return_plenum='ahs_return', outdoor_air_fraction=1.0, min_outdoor_air=0.0, supply_volume=0.0, return_volume=0.0, T0=293.15, ambient='ambient') |
add_large_openingnoodl.apps.building_physics |
CONTAM's two-opening doorway (DR_PL2, TN 1887r1 eq. 69-70): two orifices of area W H / 2 at z_mid -/+ 2H/9, both oriented a -> b. Exact for a mid-height neutral plane. Returns the two edge keys (low, high). Edge attributes: z_path, area, Cd, opening=1. names gives the two edges a name (as component factories do). net may be a Network or a noodl.components.Component. |
add_large_opening(net, a, b, *, H, W, z_mid, Cd=0.78, kind='airpath', names=None) |
add_zonenoodl.apps.building_physics |
Add the zone's air node (attributes volume, T0, z_ref, heat_capacity=0) and, if it has a wall, the wall node (heat_capacity) with 'wall' edges zone -> wall -> ambient (ua). | add_zone(net, zone, *, ambient='ambient') |
air_handlernoodl.apps.building_physics |
CONTAM's simple air-handling system: plenums supply and return (volumes in m3, temperature T0 in K), supply points to the ports supplies[i] and return points from returns[j] (design flows in kg/s), the recirculation, and the outdoor air and exhaust to the shared ambient; outdoor_air_fraction and min_outdoor_air (kg/s) as in add_air_handler. After flatten, air_handler_from_edges(net, kind) gives its law and closure, and air_handler_plenums(net, kind) the nodes for build_model(flow_balanced=...). Zero plenum volumes (CONTAM's default) also need storage_free=... and an implicit heat layer; the 1 m3 default here needs neither. Give each air handler of a building its own kind. Ports: supplies[i], returns[j]. |
air_handler(name, *, supplies, returns, outdoor_air_fraction=1.0, min_outdoor_air=0.0, supply_volume=1.0, return_volume=1.0, T0=293.15, kind='air_handler') |
air_handler_from_edgesnoodl.apps.building_physics |
The law and the closure of the air handler whose edges are of kind (as add_air_handler, or the .prj reader, made them). |
air_handler_from_edges(net, kind) |
air_handler_plenumsnoodl.apps.building_physics |
(supply plenum, return plenum) of the air handler of kind: the recirculation edge's target and source. |
air_handler_plenums(net, kind) |
apply_inputsnoodl.apps.building_physics |
New drivers with every input's value at t written in (inputs bound first). |
apply_inputs(drivers, inputs, t, calendar, *, t_start=None) |
assemble_sourcesnoodl.apps.building_physics |
Sum of every source's explicit rate source(t, x_full) (kg/s), full-node order, shape like x_full; there is no separate node-count argument. |
assemble_sources(sources, t, x_full) |
bind_inputsnoodl.apps.building_physics |
inputs with every at resolved to positions through model.refs, checked for overlap: two inputs may not write the same entry of the same driver (raises naming both). A key given with at must be one the model declares (model.refs.field(key)). |
bind_inputs(model, inputs) |
building_air_change_ratenoodl.apps.building_physics |
The building air change rate (1/h), as ContamX writes it to its .ach file: every flow entering the conditioned space from outside it, from the ambient or from a zone left out of it, over density (1.2041 kg/m3, ContamX's) times the space's volume (the nodes' "volume"), times 3600. Shape: the history's leading dimensions. |
building_air_change_rate(history, model, *, conditioned=None, layer='species', density=1.2041) |
cracknoodl.apps.building_physics |
A small orifice (m2, m). Interior: between terminals a and b. Exterior: from the shared ambient to a, carrying the wind attributes Wind.from_network reads; give the facade direction as azimuth (degrees from north) or as facade, a key of the building's inner_table("facades", ...). Edge: path. |
crack(name, *, area, z_path, Cd=0.6, exterior=False, azimuth=None, facade=None, Cp=None, Ch=None, profile=None) |
cumulative_exposurenoodl.apps.building_physics |
The time integral of occupant_exposure from the start: shape (N, ..., K), row 0 zero, in the exposure's unit times s (kg/kg s for mass fractions). |
cumulative_exposure(exposure, times) |
duct_laws_from_edgesnoodl.apps.building_physics |
The laws and drives of the duct edges DuctNetwork.add_to made: an AirDuct and a DuctStack for the segments, a DuctTerminal, a Stack (at the junction's height) and, when a terminal has a wind profile or a nonzero Cp, a Wind for the terminals (profiles numbers the WindProfiles from 1, as for Wind.from_network; an edge with neither feels no wind). Returns (laws, drives); a kind the network lacks contributes nothing. temperature_key names the full-node temperature driver for the viscosity (the viscosity at 293.15 K when None); opening_key the terminals' opening driver. |
duct_laws_from_edges(net, *, kind='duct', terminal_kind='duct_terminal', temperature_key=None, opening_key=None, g=9.80665, ambient='ambient', profiles=()) |
doornoodl.apps.building_physics |
A doorway between two zones (CONTAM's two-opening DR_PL2): terminals a, b and edges low, high. z_mid (m) is relative to the door's placement. Ports: a, b. |
door(name, *, H, W, z_mid, Cd=0.78) |
ductnoodl.apps.building_physics |
A duct run with a terminal at each end: junctions ends[0] at height z_a and ends[1] at z_b (m, relative to the placement), each holding half the duct's air, joined by the segment segment (length, hydraulic diameter and roughness in m, loss its dynamic loss coefficients), with terminals terminal_a and terminal_b (area terminal_area in m2, the duct's own by default; loss coefficient terminal_loss). Interior: from port a to port b. Exterior: terminal_a opens to the shared ambient and takes the wind from the WindProfile numbered profile (with azimuth or facade, and Ch); azimuth, facade or Ch without a profile are refused (a constant Cp is offered by DuctNetwork.terminal). Invalid lengths, diameters or losses are refused naming the duct. Build it with duct_laws_from_edges. Ports: a, b (only b when exterior). |
duct(name, *, length, diameter, roughness=9e-05, loss=0.0, terminal_area=None, terminal_loss=0.125, z_a=0.0, z_b=0.0, T0=293.15, exterior=False, azimuth=None, facade=None, Ch=None, profile=None) |
edge_flownoodl.apps.building_physics |
The mass flow (kg/s) of edge position of kind, along the edge. |
edge_flow(kind, position=0) |
edge_pressure_differencenoodl.apps.building_physics |
The driving pressure difference (Pa) of an edge, from-node minus to-node with the edge's drives (KirchhoffLayer.dp), which simulate records as "air.dp". |
edge_pressure_difference(kind, position=0) |
fannoodl.apps.building_physics |
A fan delivering the mass flow flow (kg/s, finite, not negative) whatever the pressures and the height (CONTAM's constant-mass-flow fan): from terminal a to b, or with exterior="supply" from the shared ambient into a, or with exterior="exhaust" from a to the ambient. Edge: fan, kind "fan"; build it with fan_law_from_edges. Ports: a, b (only a when exterior). |
fan(name, *, flow, exterior=None) |
fan_law_from_edgesnoodl.apps.building_physics |
One FixedFlow over every edge of kind, each edge's mass flow from its flow attribute (kg/s), as the fan component sets it. |
fan_law_from_edges(net, kind='fan') |
filter_transmissionnoodl.apps.building_physics |
The species layer's transmission, (b_flow, K): 1 - efficiency on each filtered edge, 1 elsewhere. Rows follow the layer's flow order: the edges of each kind in flow_kinds, kind by kind, each kind in network order; pass the layer's own flow_kinds. An edge is named by the (source, target, key) add_edge returned. A filter whose efficiency depends on the air passing (UVGIFilter, or a FilterSeries holding one) has no fixed transmission and is refused by name: FilterTransmission writes it every step. |
filter_transmission(net, flow_kinds, species, filters) |
infection_probabilitynoodl.apps.building_physics |
Wells-Riley: P = 1 - exp(-dose), the dose in quanta (accurate for small doses); dimensionless. | infection_probability(dose) |
inhaled_dosenoodl.apps.building_physics |
What was breathed in: breathing rate (m3/s) x air density (kg/m3) x the time integral of the mass fraction (kg/kg s; cumulative_exposure, not the per-step occupant_exposure), in kg, or quanta for a quanta species. The density defaults to 1.2041 kg/m3, CONTAM's standard air density; pass the density the species layer's mass fractions were computed with (its capacity over the zone volume) to turn them back into the inhaled amount exactly. |
inhaled_dose(integrated_exposure, breathing_rate, *, density=1.2041) |
local_mean_agenoodl.apps.building_physics |
The local mean age of air of every interior row of the layer (s; ContamX's .age file gives it in hours): the steady age equation sum_in q tau_upstream - sum_out q tau + M = 0 at every row, tau = 0 at the boundary (the outdoor air), with M the row's air mass: a transport problem with a uniform source of one second per second in every kilogram of air (Sandberg's definition; the method of AIVC Technical Note 34, which ContamX cites). Shape (..., n_i) over the history's leading dimensions, in the layer's interior order (model.transport[layer].interior_idx); evaluated at every row, with that row's flows and air mass. |
local_mean_age(history, model, *, layer='species', capacity=None) |
mass_orificenoodl.apps.building_physics |
F = Cd A sqrt(2 rho dp) [kg/s]: PowerLaw(C = Cd A sqrt(2 rho), n = 0.5). |
mass_orifice(Cd, A, *, rho=1.2041, dp_transition=0.001, kind='airpath', learnable=False) |
node_mass_fractionnoodl.apps.building_physics |
A zone's mass fraction (kg/kg) of species (position or name) in the species layer's state, read through the layer's own layout ((..., n_interior) for one species, (..., n_interior, K) for more). |
node_mass_fraction(node, species=0) |
node_pressurenoodl.apps.building_physics |
A node's gauge pressure (Pa): the air layer's potential. | node_pressure(node) |
node_temperaturenoodl.apps.building_physics |
A node's temperature (K). With a thermal layer: its state at an interior node, its boundary temperature (the driver "thermal.x_boundary") at a boundary node. Without one: the prescribed temperature "T", which the model must declare. A driver is read one step late in a sampled network. |
node_temperature(node) |
occupant_exposurenoodl.apps.building_physics |
The occupant's exposure over each step of history (from simulate): the step mean of the occupied zone's "<layer>.x", linearly interpolated within the step and split at every move; zero out of the building. Shape (N - 1, ..., K), in the layer's unit (kg/kg for mass fractions), row k for the step from history["time"][k] to [k + 1]. |
occupant_exposure(history, occupant, model, *, calendar, layer='species') |
species_historynoodl.apps.building_physics |
x (a history "<layer>.x" from simulate) in the stacked layout (N, ..., n_i, K), and whether it came in reduced. |
species_history(x, layer, name='x') |
occupant_inputsnoodl.apps.building_physics |
The inputs simulate needs for the occupants that generate something: each one's presence on "presence.<name>" (a step mean, the fraction of the step at each node of net) and, when it has schedules, the schedule values (1 for an unscheduled species, in the order of its generation) on "generation_multiplier.<name>". Occupants whose names repeat are refused: their drivers would collide. |
occupant_inputs(occupants, net) |
orifice_law_from_edgesnoodl.apps.building_physics |
One PowerLaw over every edge of kind, C_e = Cd_e A_e sqrt(2 rho), from the edge attributes Cd and area (KeyError names a missing one). |
orifice_law_from_edges(net, kind, *, rho=1.2041, dp_transition=0.001, learnable=False) |
project_calendarnoodl.apps.building_physics |
The calendar of a project's transient run: its run control's start day, time and day type, and the day types of its weather file when it has one. weather (from read_wth) overrides that file; by default the project's own is read, next to its .prj, and a weather file that cannot be found there is refused by name. simulate_project runs on this calendar; pass it where a function places times in the year (occupant_exposure). |
project_calendar(project, weather=None) |
reaction_kineticsnoodl.apps.building_physics |
The species layer's kinetics, (n_i, K, K): each node's reaction matrix in the layer's active interior order (active_interior(net, flow_kinds, [ambient])), zero where a node has none. Pass the same ambient and flow_kinds as to species_layer. A node that is not an active interior node of that layer is refused by name: the layer holds no concentration there to react. |
reaction_kinetics(net, *, species, reactions, ambient='ambient', flow_kinds=('airpath',)) |
roomnoodl.apps.building_physics |
A zone: air node air (m3, K, m) and, with wall, a lumped wall node conducting to the shared ambient. Port: air. |
room(name, *, volume, T0=293.15, z_ref=0.0, wall=None) |
run_projectnoodl.apps.building_physics |
Run the project AS WRITTEN: its own run control (start, end, time step), weather and ambient-concentration files and schedules; no ambient value is set through the API. Returns the initial state and every step: time (N,) s since the start, flow (N, n_paths) kg/s, mf (N, n_zones, K), terminal_flow (N, n_terminals): flow out of each duct terminal into its zone, kg/s, in junction-number order, dt, and the path and zone numbering of run_transient, and control (N, n_reports): the project's named pass (split) nodes, in node order, as getOutputControlValue reports them: the value the network computed for that step ((N, 0) when the project has none). airflow_convergence as in run_steady. When the project's output flags ask for them, the occupants' exposure, the age of air and the building air change rate ContamX writes are returned too, one row per step (_result_files: exposure, age, air_change_rate, building_volume); a key is absent when its file is. |
run_project(prj_path, *, airflow_convergence=None) |
run_steadynoodl.apps.building_physics |
Path net flows [kg/s], zone mass fractions and duct terminal flows (terminal_flow, kg/s, out of each terminal into its zone, in junction-number order) after the initial steady-state solve. |
run_steady(prj_path, *, ambient, airflow_convergence=None) |
run_transientnoodl.apps.building_physics |
steps steps of the project's own time step; results stacked with the initial state first: flow (steps+1, n_paths), mf (steps+1, n_zones, K), terminal_flow (steps+1, n_terminals). airflow_convergence as in run_steady. |
run_transient(prj_path, *, steps, ambient, airflow_convergence=None) |
scatternoodl.apps.building_physics |
A new tensor: base with value written at index on the last axis (all of it when index is None), batch dimensions broadcast. Differentiable in value; base is never modified. The result has base's floating dtype (so a model moved to float32 by Model.to stays float32); with base None, index must be None and value is returned as float64. |
scatter(base, index, value) |
shaftnoodl.apps.building_physics |
A tall space (stairwell, atrium, lift shaft) as a stack of zones, one per level, joined by openings (how stairwells are usually represented in multizone models): node levels[i] at z_ref = i * level_height, joined by an opening slab[i] (area m2) at the slab between levels i and i+1. Ports: levels[0], levels[1], ... |
shaft(name, *, levels, level_height, volume, area, T0=293.15, Cd=0.6) |
simulatenoodl.apps.building_physics |
Time histories at times (s since the start, increasing, times[0] == 0; the calendar places that start in the year). |
simulate(model, state, drivers, times, *, inputs=(), calendar=None, air_mass_change=None, land_on_breakpoints=True, zone_pressure_rtol=1e-12, zone_pressure_max_iter=10, diagnostics=None, **step_kwargs) |
simulate_projectnoodl.apps.building_physics |
Run a .prj project the way ContamX does: its run control (start, end, time step), its schedules, its weather and ambient-concentration files (resolved next to the .prj), with the time step fixed (no step split at a schedule's jump, as in ContamX) and the zone air-mass change when the run control asks for it. inputs are extra inputs of your own. Returns simulate's histories plus "path_flow" (N, n_paths) in path-number order and, for a project with a duct network, "terminal_flow" (N, n_terminals): the flow out of each duct terminal into its zone, in junction-number order, and, for a project with control references, "control" (N, n_reports): the named report nodes' values in node order (ContamX's control outputs; "controls.outputs" holds every block's), and, for a project with occupants, "exposure" (N - 1, ..., n_occupants, K): each occupant's exposure over each step, in project.occupants order, as ContamX's exposure file (occupant_exposure on the run's project_calendar; zero while out of the building). The species histories carry the zones' rows first, then the junctions' (project.zone_mass_fractions selects the zones). diagnostics is simulate's run-level dict, not Model.step's: "airflow_retries", the steps whose airflow solve was retried from the linear initial guess (none for a steady run). |
simulate_project(project, *, inputs=(), diagnostics=None, **step_kwargs) |
sources_from_projectnoodl.apps.building_physics |
The project's PrjSource records (from read_prj) -> source objects. |
sources_from_project(project, *, dt=60.0) |
species_layernoodl.apps.building_physics |
Species as mass fractions with zone air mass rho V as capacity (CONTAM convention). | species_layer(net, *, ambient='ambient', name='species', flow_kinds=('airpath',), rho=1.2041, n_species=1, scheme='implicit', species_names=None, transmission=None, kinetics=None, storage_free=()) |
thermal_layernoodl.apps.building_physics |
The heat layer: capacity rho c_p V + heat_capacity per active interior node, carrier c_p on the flow kinds, conduction UA on conduction_kind edges (if any exist). |
thermal_layer(net, *, ambient='ambient', name='thermal', flow_kinds=('airpath',), conduction_kind='wall', c_p=1005.0, rho=1.2041, scheme=None, fixed_temperature=(), storage_free=()) |
windownoodl.apps.building_physics |
An opening in the envelope: a crack that is exterior unless told otherwise. |
window(name, *, area, z_path, Cd=0.6, exterior=True, azimuth=None, facade=None, Cp=None, Ch=None, profile=None) |
wells_rileynoodl.apps.building_physics |
The classic steady single-zone form P = 1 - exp(-I q p t / Q): infectors I, quanta_rate q (quanta/s per infector), breathing_rate p (m3/s), ventilation_rate Q (m3/s of clean air), duration t (s); dimensionless. |
wells_riley(infectors, quanta_rate, breathing_rate, ventilation_rate, duration) |
zone_air_change_ratenoodl.apps.building_physics |
Each zone's air change rate (1/h): all the air flowing into it, from the ambient and from other zones alike, over its air mass, times 3600; the inverse of its nominal time constant, not an outdoor air change rate. An addition of noodl: ContamX writes no zone rate. Returns (rate, zones): rate (..., n_z) over the history's leading dimensions, and zones the node names of its last axis, the layer's interior rows in their order without the storage-free ones (a zero-volume plenum holds no air to change). A duct junction with volume is a row like a zone. |
zone_air_change_rate(history, model, *, layer='species', capacity=None) |
Orificenoodl.elements |
PowerLaw(C = Cd * A * sqrt(2 / rho), n = 0.5): the sharp-edged orifice equation, as a VOLUME flow in m3/s from Cd (dimensionless discharge coefficient), A (m2) and rho (kg/m3). |
Orifice(Cd, A, *, rho=1.2, dp_transition=0.001, regularised=None, kind='airpath', learnable=False) |
air_viscositynoodl.elements |
Dynamic viscosity of air (Pa s) at temperature T (K): ContamX's linear law, 1.81625e-5 + 4.92859e-8 (T - 293.15), measured between 253 and 333 K. |
air_viscosity(T) |
discretized_dooralso mbl_discretized_doornoodl.elements |
One DoorDiscretizedOpen as nCom edges of kind: the compartment element and its head drive. Defaults are MBL's (DoorDiscretized.mo:6-11, TwoWayFlowElementBuoyancy.mo:6-14, DoorDiscretizedOpen.mo:6, TwoWayFlowElement.mo:19). dA = wOpe hOpe / nCom on every edge (DoorDiscretizedOpen.mo:23, DoorDiscretized.mo:60). |
discretized_door(*, src, tgt, medium, nCom=10, wOpe=0.9, hOpe=2.1, hA=1.35, hB=1.35, CD=0.65, m=0.5, dp_turbulent=0.01, vZer=0.001, kind='door', T_key='T', Xw_key='X_w', p_key='p_abs') |
discretized_operable_dooralso mbl_discretized_operable_doornoodl.elements |
One DoorDiscretizedOperable as nCom edges of kind; see :func:discretized_door. AOpe = wOpe hOpe (DoorDiscretizedOperable.mo:35); LClo has no MBL default. |
discretized_operable_door(*, src, tgt, medium, y_key, LClo, nCom=10, wOpe=0.9, hOpe=2.1, hA=1.35, hB=1.35, CDOpe=0.65, CDClo=0.65, CDCloRat=1.0, dpCloRat=4.0, mOpe=0.5, mClo=0.65, dp_turbulent=0.01, vZer=0.001, kind='door', T_key='T', Xw_key='X_w', p_key='p_abs') |
effective_leakage_areaalso mbl_elanoodl.elements |
EffectiveAirLeakageArea.mo:3-5,7-14,16: C = L * CDRat * sqrt(2 / rho_default) * dpRat ** (0.5 - m), m = 0.65 by default (:4), dpRat = 4 Pa (:7-9), CDRat = 1 (:11-14). |
effective_leakage_area(L, dpRat=4.0, CDRat=1.0, m=0.65, dp_turbulent=0.1, *, rho_default, kind='airpath', learnable=False) |
mediumnoodl.elements |
Look up one of the three air media, by physical name or by Modelica class path. | medium(name) |
open_door_pairalso mbl_door_pairnoodl.elements |
Both edges of one DoorOpen (or of several, with per-door src/tgt): the "ab" edge (path 1) with kind f"{kind}_ab" and the "ba" edge (path 2) with kind f"{kind}_ba", sharing every keyword argument of :class:OpenDoor. |
open_door_pair(*, kind='door', **kwargs) |
operable_door_pairalso mbl_operable_door_pairnoodl.elements |
Both edges of one DoorOperable; see :func:open_door_pair. |
operable_door_pair(*, kind='door', **kwargs) |
power_law_coefficientalso mbl_coefficientnoodl.elements |
Coefficient_V_flow.mo/Coefficient_m_flow.mo: C/k and m given directly, with no further formula -- a direct pass-through to :class:RegularizedPowerLaw. |
power_law_coefficient(C, m, form, dp_turbulent=0.1, *, rho_default, kind='airpath', learnable=False) |
power_law_from_pointalso mbl_pointnoodl.elements |
Point_m_flow.mo:4-5: k = mMea_flow_nominal / dpMea_nominal ** m, fit from one measured (dp, m_flow) pair; m stays a free parameter (default 0.5, inherited from Coefficient_m_flow's PowerLawResistanceParameters(m=0.5), Coefficient_m_flow.mo:27-28). |
power_law_from_point(dpMea, mMea_flow, m=0.5, dp_turbulent=0.1, *, rho_default, kind='airpath', learnable=False) |
power_law_from_pointsalso mbl_pointsnoodl.elements |
Points_m_flow.mo:4-6,15-16: the flow exponent itself is derived from two measured (dp, m_flow) pairs, m = (ln(mMea[0]) - ln(mMea[1])) / (ln(dpMea[0]) - ln(dpMea[1])), then k = mMea[0] / dpMea[0] ** m. dpMea/mMea_flow are each a 2-element sequence. |
power_law_from_points(dpMea, mMea_flow, dp_turbulent=0.1, *, rho_default, kind='airpath', learnable=False) |
regularized_orificealso mbl_orificenoodl.elements |
Orifice.mo:3-5,9: C = CD * A * sqrt(2 / rho_default), m = 0.5 by default (overridable, per Orifice.mo:3's non-final m=0.5 modifier); C itself is final in MBL (does not depend on m). CD defaults to 0.65 (Orifice.mo:9, the sharp-edged-orifice discharge coefficient). |
regularized_orifice(A, CD=0.65, m=0.5, dp_turbulent=0.1, *, rho_default, kind='airpath', learnable=False) |
check_drive_signaturenoodl.drives |
Raise TypeError unless drive is callable as drive(drivers). |
check_drive_signature(drive, *, where) |
Constants and tables¶
Named values and option tables.
| Name | Value |
|---|---|
DEFAULT_ROUGHNESS |
9e-05 |
DEFAULT_TERMINAL_LOSS |
0.125 |
EXHAUST |
4 |
OUTDOOR_AIR |
3 |
RECIRCULATION |
2 |
RETURN |
1 |
SUPPLY |
0 |
Base classes (for extending)¶
Subclass these to add a constitutive law, drive, closure, reaction or node source; see Extending noodl.
| Name | What it does | How to call it |
|---|---|---|
ConstitutiveLawnoodl.elements |
Abstract branch law: flow as a function of potential difference. | ConstitutiveLaw(kind) |
ImplicitLawnoodl.elements |
A constitutive law written as a relation residual(dp, q, drivers, ctx) = 0. |
ImplicitLaw(kind) |
Drivenoodl.drives |
A drive must read every differentiable quantity from drivers, never own one. |
Drive(...) |