Skip to content

Utilities#

smart_control.simulator.constants #

Defines constants for use in simulation code suite.

smart_control.simulator.building_utils #

Utils for computing the physical and thermal characteristics of buildings.

RoomIndicesDict module-attribute #

RoomIndicesDict = defaultdict[str, Any]

Note: The following four types all describe various aspects of the same

floorplan; i.e. all four types have the same dimensionality and encode similar variations of the same information. Here I will describe the differences between the types: (1) FileInputFloorPlan: the floorplan format as entered into the program. This specifically notes exterior space as 2's, walls as 1's, and inside space as 0's. This file type may have various "glitches" that the program will correct, such as walls that abut directly the frame of the image. (2) ConnectionReadyFloorPlan: the floorplan format as processed and ready for the opencv function connectedComponents . This format requires a very specific input type, namely 1's where there is open space and 0's everywhere else. Therefore, prior to this step, various pieces of information must be removed from the floor plan, such as the location of exterior space, and stored in other datatypes. (3) Connections: the floorplan format after opencv's connectedComponents is run on it. The Connections floorplan has each connected component, defined as the contiguous space within each component of interest (room, zone, etc.) labelled with an increasing integer indexing the component. It is an np.ndarray where 0's are walls and connected contiguous space are increasing integers. (4) ExteriorSpace: the floorplan format serves an auxiliary store of information so that the original floorplan can be stripped of a specific designation for exterior space in order to be prepared for ingestion into connectedComponents. It is an np.ndarray where 0's are walls or interior space and exterior space is marked with -1. (5) ExteriorWalls: a floorplan format that notes where exterior walls are with 1's and everywhere else with 0's. (6) InteriorWalls: a floorplan format that notes where interior walls are with -3 and everywhere else with 0's.

construct_building_data_types #

construct_building_data_types(
    floor_plan: FileInputFloorPlan,
    zone_map: FileInputFloorPlan,
    save_debugging_image: bool = False,
) -> Tuple[RoomIndicesDict, ExteriorWalls, InteriorWalls, ExteriorSpace]

Sequentially calls all preprocessing functions in building_utils.py.

This function links together the necessary helper functions in building_utils.py so that it is clear and straightforward what they do when put in sequence. Given a FileInputFloorPlan, this function breaks out the necessary pieces of information for further processing.

Parameters:

Name Type Description Default
floor_plan FileInputFloorPlan

an FileInputFloorPlan with outside air marked as constants.EXTERIOR_SPACE_VALUE_IN_FILE_INPUT, inside walls marked as constants.INTERIOR_WALL_VALUE_IN_FILE_INPUT, and inside space marked as constants.INTERIOR_SPACE_VALUE_IN_FILE_INPUT.

required
zone_map FileInputFloorPlan

a FileInputFloorPlan that has, using the same int markings as the floor_plan, the VAV zones noted instead of the physical rooms.

required
save_debugging_image bool

bool for whether we should save some debugging images to CNS.

False

Returns:

Type Description
Tuple[RoomIndicesDict, ExteriorWalls, InteriorWalls, ExteriorSpace]

connections output with exterior space set negative.

enlarge_component #

enlarge_component(
    array_with_component_nonzero: ndarray, distance_to_augment: float
) -> np.ndarray

Enlarges the component in question by CVs within a certain distance.

Note: this function is a general purpose function intended to enlarge any component by measure of "distance_to_augment".

Parameters:

Name Type Description Default
array_with_component_nonzero ndarray

array where the object to enlarge is nonzero

required
distance_to_augment float

return a new object with CVs within this distance selected.

required

Returns:

Type Description
ndarray

An array with 1 being a CV to include and 0 being a CV to exclude.

guarantee_air_padding_in_frame #

guarantee_air_padding_in_frame(
    floor_plan: FileInputFloorPlan,
) -> FileInputFloorPlan

Adds a row or column of air if a building is abuts its frame edge.

Future computation relies on buildings being surrounded by at least one layer of air CVs between them and the edge of the floor plan frame. However, due to human variation in preparing floor plans for transformation into arrays, we may have the case where a floor plan is passed in that has wall CVs at the edge of the frame. Thus, this function should check that that is the case and will add a layer of exterior space CVs if building lies against the array's edge.

If walls are touching more than

one edge of the floor plan, the function should be able to compute this -- but if it adds rows of CVs and then does not update the shape of the floor plan, it will fail. Thus, we package the dimension tracking and rows to concatenate in a convenient helper function so it can be called multiple times without repetitive code.

Parameters:

Name Type Description Default
floor_plan FileInputFloorPlan

a FileInputFloorPlan

required

Returns:

Type Description
FileInputFloorPlan

an FileInputFloorPlan that has 2's padded along whichever array edge was missing them.

process_and_run_connected_components #

process_and_run_connected_components(
    floor_plan: FileInputFloorPlan,
) -> Connections

Public function that takes in floor plan and outputs a Components.

Parameters:

Name Type Description Default
floor_plan FileInputFloorPlan

FileInputFloorPlan

required

Returns:

Type Description
Connections

connections output with exterior space set negative.

read_floor_plan_from_filepath #

read_floor_plan_from_filepath(
    filepath: str, save_debugging_image: bool = False
) -> FileInputFloorPlan

Reads a file from a disk (including CNS) and returns it.

Parameters:

Name Type Description Default
filepath str

name of the location on CNS. CSV and NPY files are supported, and they are both determined by the semantic naming of the path, i.e. the file type suffix.

required
save_debugging_image bool

boolean for whether we should save the debugging image to cns.

False

Returns:

Type Description
FileInputFloorPlan

a FileInputFloorPlan

save_images_to_cns_for_debugging #

save_images_to_cns_for_debugging(
    floor_plan: Union[
        FileInputFloorPlan,
        Connections,
        ExteriorWalls,
        InteriorWalls,
        ndarray,
        ConnectionReadyFloorPlan,
    ],
    path_ending: str,
    path_to_simulator_cns: str = "/cns/oi-d/home/smart_buildings/control/configs/simulation/",
) -> None

Saves a .png of a floorplan array to CNS for visual debugging.

Parameters:

Name Type Description Default
floor_plan Union[FileInputFloorPlan, Connections, ExteriorWalls, InteriorWalls, ndarray, ConnectionReadyFloorPlan]

one of the floor_plan types

required
path_ending str

a path suffix to end saved files

required
path_to_simulator_cns str

base path to save the files on CNS.

'/cns/oi-d/home/smart_buildings/control/configs/simulation/'

smart_control.simulator.building_radiation_utils #

Building Radiation Utility Functions

For computing the physical and thermal characteristics of buildings.

calculate_A_tilde_inv #

calculate_A_tilde_inv(epsilon: ndarray, F: ndarray) -> np.ndarray

Calculates the A-tilde matrix used in radiative heat transfer calculations.

The A-tilde matrix relates the radiosity to the blackbody emissive power in a radiative heat transfer system. It accounts for both emission and reflection.

Parameters:

Name Type Description Default
epsilon ndarray

Array of surface emissivity values (between 0 and 1)

required
F ndarray

View factor matrix

required

Returns:

Type Description
ndarray

The A-tilde matrix relating radiosity to blackbody emissive power

Raises:

Type Description
AssertionError

If emissivity vector size doesn't match view factor matrix or if emissivity values are outside [0,1]

calculate_IFAinv #

calculate_IFAinv(F: ndarray, A_inv: ndarray) -> np.ndarray

Calculates the \(IFA_{inv}\) matrix.

\[IFA_{inv} = (I - F) \tilde{A}^{-1}\]

See net_radiative_heatflux_function_of_T for more details.

Parameters:

Name Type Description Default
F ndarray

The view factor matrix.

required
A_inv ndarray

The A inverse matrix.

required

Returns:

Name Type Description
IFA_inv ndarray

The IFA inverse matrix.

net_radiative_heatflux_function_of_T #

net_radiative_heatflux_function_of_T(T: ndarray, IFAinv: ndarray) -> np.array

Calculates the net radiative heat flux and radiosity for all surfaces given surface temperatures.

Equations:#

The net radiative heat flux leaving surface \(i\) is:

\[q_i = J_i - G_i\]

where: - \(J_i\) is the radiosity (total outgoing radiative flux) from surface \(i\), - \(G_i\) is the irradiation (total incoming radiative flux) onto surface \(i\).

The radiosity is given by:

\[J_i = \epsilon_i E_{b,i} + \rho_i G_i\]

where \(\epsilon_i\) is the emissivity, \(\rho_i = 1 - \epsilon_i\) is the reflectivity (for opaque surfaces), and \(E_{b,i}\) is the blackbody emission from \(i\) surface.

The irradiation for the \(i\) surface is:

\[G_i A_i = \sum_{j=1,\, j\neq i}^n J_j A_j F_{ji}\]

where \(F_{ji}\) is the view factor from surface \(j\) to \(i\).

Combining these, the radiosity equation for all surfaces can be written in vector-matrix form as:

\[\tilde{\mathbf{A}}\, \mathbf{J} = \mathbf{E}_b\]

where \(\tilde{A}_{ij} = \delta_{ij} - \frac{(1-\epsilon_i) F_{ij}}{\epsilon_i}\).

Solving for \(\mathbf{J}\):

\[\mathbf{J} = \tilde{\mathbf{A}}^{-1} \mathbf{E}_b\]

The net heat flux vector for all surfaces is:

\[\mathbf{q}= (\mathbf{I}-\tilde{\mathbf{F}})\tilde{\mathbf{A}}^{-1}\mathbf{E}_b\]

where \(\tilde{\mathbf{F}}\) is the matrix of view factors, \(F_{ij}\) and\(\mathbf{E}_b\) is \(\sigma \mathbf{T}^4\).

Nomenclature and Units:#
  • \(q_i\) : Net radiative heat flux from surface \(i\) [\(\mathrm{W/m^2}\)]
  • \(\mathbf{q}\) : Vector of \(q_i\) for all \(i=1..n\) [\(\mathrm{W/m^2}\)]
  • \(J_i\) : Radiosity of surface \(i\) [\(\mathrm{W/m^2}\)]
  • \(\mathbf{J}\) : Vector of \(J_i\) for all \(i=1..n\) [\(\mathrm{W/m^2}\)]
  • \(G_i\) : Irradiation on surface \(i\) [\(\mathrm{W/m^2}\)]
  • \(E_{b,i}\) : Blackbody emissive power of surface \(i\) [\(\mathrm{W/m^2}\)]$
  • \(\mathbf{E}_b\): Vector of \(E_{b,i}\) for all \(i=1..n\) [\(\mathrm{W/m^2}\)]
  • \(\epsilon_i\) : Emissivity of surface \(i\) [dimensionless]
  • \(\rho_i\) : Reflectivity of surface \(i\) [dimensionless], (\(\rho_i=1-\epsilon_i\))
  • \(A_i\) : Area of surface \(i\) [\(\mathrm{m^2}\)]
  • \(F_{ij}\) : View factor from surface \(i\) to \(j\) [dimensionless]
  • \(\tilde{\mathbf{A}}\): Matrix with elements (\(\tilde{A}_{ij} = \delta_{ij} - \frac{(1-\epsilon_i) F_{ij}}{\epsilon_i}\))
  • \(\mathbf{I}\) : \(n \times n\) identity matrix
  • \(\tilde{\mathbf{F}}\): Matrix of \(F_{ij}\) (view factors)
  • \(\delta_{ij}\): Kronecker delta (\(=1\) if \(i=j\), \(=0\) otherwise)
  • \(\sigma\): Stefan-Boltzmann constant [\(\mathrm{W/m^2K^4}\)]
  • \(\mathbf{T}\): Vector of surface temperatures [K]
References:#
  • Incropera, F.P., DeWitt, D.P., "Fundamentals of Heat and Mass Transfer"

Parameters:

Name Type Description Default
T ndarray

Surface temperatures in Kelvin.

required
IFAinv ndarray

(I - F) @ A_inv.

required

Returns:

Name Type Description
q array

Net radiative heat flux [W/m^2]

mark_air_connected_interior_walls #

mark_air_connected_interior_walls(
    indexed_floor_plan: ndarray,
    start_pos: Tuple[int, int],
    interior_wall_value: int = constants.INTERIOR_WALL_VALUE_IN_FUNCTION,
    marked_value: int = TEMPORARY_MARKED_VALUE,
    air_value: int = constants.INTERIOR_SPACE_VALUE_IN_FUNCTION,
) -> Tuple[Optional[np.ndarray], Optional[np.ndarray]]

Mark all interior wall nodes that are connected to the same air space as the starting interior wall. Uses 4-directional connectivity to check wall-air adjacency. All connected walls including the starting position are marked.

Parameters:

Name Type Description Default
indexed_floor_plan ndarray

2D numpy array representing the floor plan where different values represent different types of cells (walls, air, etc.).

required
start_pos Tuple[int, int]

Starting position (row, col) of the interior wall to begin marking from.

required
interior_wall_value int

Value used to represent interior walls in the floor plan. Defaults to -3 (from constants.py).

INTERIOR_WALL_VALUE_IN_FUNCTION
marked_value int

Value used to mark connected interior walls. Only used internally. Defaults to -33.

TEMPORARY_MARKED_VALUE
air_value int

Value used to represent air spaces in the floor plan. Defaults to 0 (from constants.py).

INTERIOR_SPACE_VALUE_IN_FUNCTION

Returns:

Type Description
Tuple[Optional[ndarray], Optional[ndarray]]

A tuple containing:

  • modified_floor_plan: Copy of input floor plan with connected walls marked with marked_value. None if start_pos is invalid.

  • interior_space_array: Extracted interior space containing only air and marked walls, cropped to the bounding box of the connected region. None if start_pos is invalid or no interior space is found.

Raises:

Type Description
ValueError

If the starting position is out of bounds of the floor plan.

Note

This function is used as the first step in radiative heat transfer calculations to identify all interior wall nodes that are connected to the same air space. The marked_value (-33) indicates walls that can potentially participate in radiative heat transfer with each other.

fix_view_factors #

fix_view_factors(F: ndarray, A: ndarray = None) -> np.ndarray

Fix approximate view factors and enforce reciprocity and completeness.

Parameters:

Name Type Description Default
F ndarray

Approximate direct view factor matrix (N x N)

required
A ndarray

Area vector (N elements). Defaults to None.

None

Returns:

Type Description
ndarray

Fixed view factor matrix

References

See FixViewFactors function in EnergyPlus

get_VF #

get_VF(
    indexed_floor_plan: ndarray,
    interior_wall_mask: ndarray,
    view_factor_method: str = "ScriptF",
    marked_value: int = TEMPORARY_MARKED_VALUE,
) -> np.ndarray

Calculate view factors between interior walls in the floor plan.

Parameters:

Name Type Description Default
indexed_floor_plan ndarray

2D array representing the floor plan with indexed values.

required
view_factor_method str

Method to use for view factors. Defaults to 'ScriptF'. Either "ScriptF" or "CarrollMRT".

'ScriptF'
marked_value int

Value used to mark connected interior walls. Only used internally. Defaults to -33.

TEMPORARY_MARKED_VALUE

Returns:

Type Description
ndarray

View factor matrix where VF[i,j] represents the view factor from wall i to wall j.

mark_interior_wall_adjacent_to_air #

mark_interior_wall_adjacent_to_air(
    arr: ndarray,
    interior_wall_value: int = constants.INTERIOR_WALL_VALUE_IN_FUNCTION,
    air_value: int = constants.INTERIOR_SPACE_VALUE_IN_FUNCTION,
) -> np.ndarray

Marks interior walls that are adjacent to air spaces.

Creates a boolean mask identifying interior walls that share an edge with an air space (value of 0) in the floor plan. Checks for adjacency in four directions: up, down, left, and right.

Parameters:

Name Type Description Default
arr ndarray

2D array representing the floor plan with interior walls marked as interior_wall_value and air spaces as 0.

required
interior_wall_value int

Value used to represent interior walls in the floor plan. Defaults to -3 (constants.INTERIOR_WALL_VALUE_IN_FUNCTION).

INTERIOR_WALL_VALUE_IN_FUNCTION

Returns:

Type Description
ndarray

Boolean mask array where True indicates an interior wall that is adjacent to

ndarray

at least one air space.

get_line_points #

get_line_points(
    start: Tuple[float, float], end: Tuple[float, float]
) -> list[Tuple[float, float]]

Generate points where the line crosses integer grid lines.

This function calculates all intersection points between a line segment and the integer grid lines. It handles vertical, horizontal, and diagonal lines by finding intersections with both vertical (x = integer) and horizontal (y = integer) grid lines.

Parameters:

Name Type Description Default
start Tuple[float, float]

Starting point of the line segment as (x, y) coordinates.

required
end Tuple[float, float]

Ending point of the line segment as (x, y) coordinates.

required

Returns:

Type Description
list[Tuple[float, float]]

List of intersection points sorted by distance from the start point. Each point is a tuple of (x, y) coordinates as floats.

is_line_blocked #

is_line_blocked(
    floor_plan: ndarray,
    start: Tuple[float, float],
    end: Tuple[float, float],
    interior_wall_value: int = constants.INTERIOR_WALL_VALUE_IN_FUNCTION,
    marked_value: int = TEMPORARY_MARKED_VALUE,
    blocked_value: int = TEMPORARY_BLOCKED_VALUE,
) -> bool

Check if the line between start and end is blocked by walls.

This function determines if a line of sight between two points is blocked by walls in the floor plan. It checks all grid intersections along the line and determines if the line is blocked by examining the 4 surrounding grid cells at each intersection point.

Parameters:

Name Type Description Default
floor_plan ndarray

2D numpy array representing the floor plan where different values represent different types of cells (walls, air, etc.).

required
start Tuple[float, float]

Starting point of the line as (x, y) coordinates.

required
end Tuple[float, float]

Ending point of the line as (x, y) coordinates.

required
interior_wall_value int

Value used to represent interior walls in the floor plan. Defaults to -3 (from constants.py).

INTERIOR_WALL_VALUE_IN_FUNCTION
marked_value int

Value used to represent marked wall nodes. Only used internally. Defaults: -33. Only used internally.

TEMPORARY_MARKED_VALUE
blocked_value int

Value used to represent blocked wall nodes. Only used internally. Default: -34. Only used internally.

TEMPORARY_BLOCKED_VALUE

Returns:

Type Description
bool

True if the line is blocked by walls, False if the line of sight is clear.

Note

The function considers a line blocked if all 4 grid cells surrounding an intersection point are walls (values -3, -33, or -34).

are_neighbors #

are_neighbors(pos1: Tuple[int, int], pos2: Tuple[int, int]) -> bool

Check if two positions are physically neighboring (adjacent).

This function determines if two grid positions are adjacent to each other using 4-connectivity. Two positions are considered neighbors if they are within 1 unit distance in both x and y directions, but not the same position.

Parameters:

Name Type Description Default
pos1 Tuple[int, int]

First position as (row, col) coordinates.

required
pos2 Tuple[int, int]

Second position as (row, col) coordinates.

required

Returns:

Type Description
bool

True if the positions are neighbors, False otherwise.

mark_directly_seeing_nodes #

mark_directly_seeing_nodes(
    floor_plan: ndarray,
    base_node: Tuple[int, int],
    interior_wall_value: int = constants.INTERIOR_WALL_VALUE_IN_FUNCTION,
    marked_value: int = TEMPORARY_MARKED_VALUE,
    blocked_value: int = TEMPORARY_BLOCKED_VALUE,
) -> np.ndarray

Mark nodes that are directly seeing the base node as blocked_value.

This function identifies and marks wall nodes that have a direct line of sight to the base node. It processes all connected wall nodes (marked with marked_value) and determines which ones can directly see the base node without being blocked by other walls.

Parameters:

Name Type Description Default
floor_plan ndarray

2D numpy array representing the floor plan where different values represent different types of cells (walls, air, etc.).

required
base_node Tuple[int, int]

Position of the base node as (row, col) coordinates.

required
interior_wall_value int

Value used to represent interior walls in the floor plan. Defaults to -3 (from constants.py).

INTERIOR_WALL_VALUE_IN_FUNCTION
marked_value int

Value used to represent connected wall nodes that should be checked for line of sight. Only used internally. Defaults to -33.

TEMPORARY_MARKED_VALUE
blocked_value int

Value used to mark nodes that cannot directly see the base node. Only used internally. Defaults to -34.

TEMPORARY_BLOCKED_VALUE

Returns:

Type Description
ndarray

Copy of the floor plan with nodes marked according to their visibility to the base node. Nodes that cannot see the base node are marked with blocked_value, and the base node itself is marked with blocked_value + marked_value.

Note
  • Neighboring nodes are automatically marked as blocked (no line of sight calculation needed).
  • For non-neighboring nodes, the function checks if the line of sight is blocked by walls using is_line_blocked().
  • The base node itself is marked with a special value to distinguish it.
  • Value meanings for radiative heat transfer:
  • marked_value (-33): Interior wall nodes connected to the same air space (can participate in radiative transfer)
  • blocked_value (-34): Interior wall nodes that cannot see the base node (blocked from radiative transfer)
  • blocked_value + marked_value (-67): The starting node itself

smart_control.simulator.thermal_diffuser_utils #

Code for generating thermal diffusers in a building.

These helper functions are separated these out into their own file for extensibility: we can easily put in another function loading these from data and process this using similar function format.

diffuser_allocation_switch #

diffuser_allocation_switch(
    room_cv_indices: Collection[Coordinates2D],
    spacing: int = 10,
    interior_walls: Optional[InteriorWalls] = None,
    buffer_from_walls: int = 2,
) -> Collection[Coordinates2D]

Switches between random and even assignment of thermal diffusers.

A more in-depth explanation: here we provide a method for allocating thermal diffusers that is general for many types of rooms. At a high level, if a room is rectangular enough, we allocate diffusers evenly according to a grid determined by the input "spacing", and then only select the points that are inside the original set of room indices to ensure we don't place a diffuser outside. If a room is too insanely shaped (i.e. imagine a long snaking hallway or an entryway to a few private alcoves) we instead try to ensure that the appropriate number of diffusers are still placed, but for simplicity we place them randomly. This option is almost never taken, as most rooms are deemed rectangular enough according to our measure:

Here, from the number of CVs that define a room, extract the total number of CVs (num_CVs), the maximum and minimum x coordinates (x_max, x_min), and the maximum and minimum y coordinates (y_max, y_min), and set a threshold. If num_CVs / ((x_max - x_min) * (y_max - y_min)) > threshold, it is rectangular enough.

Parameters:

Name Type Description Default
room_cv_indices Collection[Coordinates2D]

a list of indices making up a room.

required
spacing int

how many control volumes to put between each diffuser

10
interior_walls Optional[InteriorWalls]

an InteriorWalls for determining whether thermal diffusers are allocated in walls. This measure is intended for cases in which one needs to provide a interior_walls and zone_map to the Building class, and they may not line up correctly on account of being from different photo sources.

None
buffer_from_walls int

how far to place a thermal diffuser away from a wall.

2

Returns:

Type Description
Collection[Coordinates2D]

a list of inds to place diffusers.