Skip to content

update building radiation utils for external radiative heat transfer#170

Open
ecosang wants to merge 1 commit into
google:copybara_pushfrom
ecosang:ext_radiation_util
Open

update building radiation utils for external radiative heat transfer#170
ecosang wants to merge 1 commit into
google:copybara_pushfrom
ecosang:ext_radiation_util

Conversation

@ecosang

@ecosang ecosang commented May 17, 2026

Copy link
Copy Markdown
Contributor

Adding several external radiative heat transfer-related utils.

New functions in building_radiation_utils.py:

  • _ensure_irradiance_components() — Normalizes irradiance input (dict or dataclass) to a guaranteed IrradianceComponents instance with validation of required keys (ghi, dni, dhi, solar_zenith, solar_azimuth)

  • validate_fenestration_connectivity() — Validates that fenestration nodes in a floor plan properly bridge exterior space to interior air (checks air adjacency, exterior exposure, surrounded-by-air detection, and chain connectivity)

  • _validate_fenestration_chain_connectivity() — Ensures each fenestration node can reach both exterior and interior air through the fenestration chain via BFS, and validates interior-facing direction is not blocked

  • _determine_interior_direction() — Infers the direction vector pointing from exterior toward interior air based on node positions relative to grid boundaries or adjacent exterior space

  • _find_connected_groups() — Finds all 4-connected groups of cells with a given value in the floor plan using BFS

  • mark_fenestration_positions() — Classifies fenestration nodes into exterior, interior, or in-between categories based on adjacency

  • group_fenestrations() — Groups adjacent fenestration nodes and computes surface properties (azimuth, tilt, view factors F_sky/F_gnd/F_air, beta)

  • _determine_fenestration_azimuth() — Determines fenestration azimuth from exterior adjacency using atan2, with floor_plan_orientation offset support

  • group_air_nodes() — Groups connected interior air nodes and annotates which fenestration groups are adjacent to each air group

  • calculate_solar_absorbed_for_fenestration_group() — Computes absorbed solar flux per node for a fenestration group using POA irradiance

  • net_solar_absorbed_heatflux_fenestration() — Returns array of absorbed solar heat flux at each fenestration node across all groups

  • calculate_solar_transmitted_for_fenestration_group() — Computes total transmitted solar radiation for a fenestration group

  • net_solar_transmitted_heatflux_fenestration() — Distributes transmitted solar heat flux to air nodes connected to windows

  • mark_interior_surface_adjacent_to_air() — Marks interior surfaces (walls + fenestration) adjacent to air with boolean mask

  • calculate_exterior_lwr_for_fenestration_group() — Computes net exterior longwave radiative heat flux for a fenestration group (sky + ground + air exchange)

  • net_exterior_radiative_heatflux() — Computes net exterior LWR heat flux array for all fenestration nodes

  • get_exterior_wall_boundary_mask() — Identifies exterior wall nodes forming the building boundary (excluding walls facing enclosed interior air)

  • determine_exterior_wall_azimuth_array() — Determines azimuth for each exterior wall boundary node based on adjacent exterior space direction

New tests in building_radiation_utils_test.py:

  • 6 dedicated unit tests for _validate_fenestration_chain_connectivity() — Testing valid chains, blocked-from-exterior, blocked-from-air, interior-direction blockage, thick fenestration, and L-shaped fenestration
  • new tests for the above added functions.

Code cleanup:

  • Removed unused interior_wall_value parameter from _validate_fenestration_chain_connectivity() and all callers

@ecosang

ecosang commented May 17, 2026

Copy link
Copy Markdown
Contributor Author

@s2t2
Several building radiation utility functions are added.
We may want to have a single class to handle all radiative heat transfer, but I only use those functions within the current simulator/building framework, so I think this structure is ok to maintain.
It can be debugged while testing simulator for other developers.

@s2t2

s2t2 commented May 18, 2026

Copy link
Copy Markdown
Collaborator

Nice @ecosang , thank you! There is a lot of code - I will work on reviewing by later this week / early next week.

@s2t2

s2t2 commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

@gemini-code-assist please review this pull request

@s2t2

s2t2 commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

@gemini-code-assist are there any opportunities to refactor the function-oriented code in "smart_control/simulator/building_radiation_utils.py" to use class and be object oriented?

@s2t2 s2t2 self-requested a review June 1, 2026 16:33

@s2t2 s2t2 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ecosang thanks for the PR!

Overall this looks really great.

I just had some minor comments about type hints, and refactoring a variable, otherwise LGTM.

Also I made some separate comments to test the capabilities of the Gemini code assistant - I'm still learning how to best incorporate it.

from collections import deque
import math
from typing import Optional, Tuple
from typing import Any, Dict, List, Mapping, Optional, Sequence, Set, Tuple, Union

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Google Python style guide recommends prefers using X | None style instead of typing.Optional.

They also prefer using normal list and dict instead of typing.List and typing.Dict.

solar_radiation.IrradianceComponents,
Mapping[str, Any],
],
solar_zenith: Optional[float] = None,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's use float | None = None (here and in related places below / throughout)



def _ensure_irradiance_components(
irradiance_components: Union[

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Google style guide prefers This | That instead of Union[This, That]

if col == cols - 1:
return (0, -1)

directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this directions variable duplicated in a few places - let's move to a shared constant DIRECTIONS at the top of the file, and refactor all references

- 180° = South (down/increasing row)
- 270° = West (left/decreasing col)

The computed azimuth is then offset by floor_plan_orientation.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice docstrings!

-43,
-43,
-1,
], # Interior fenestration (adj. to air)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI if you find it easier to read this list of numbers if they are on the same line, and to be consistent with the other lines, feel free to use #pylint:disable=line-too-long whenever you feel it is helpful.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate the detailed, thorough, and well documented tests!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants