Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggestion: improvement of set_wwr and _has_correct_orientation from recipes.py #197

Open
JoLo90 opened this issue Aug 7, 2020 · 0 comments
Labels

Comments

@JoLo90
Copy link
Contributor

JoLo90 commented Aug 7, 2020

When working with the current set_wwr function from the recipes.py file, I noticed two opportunities for improvement:

  • when applying different WWR with the wwr_map function the case WWR = 0 adds 20% windows on the wall instead of 0.

  • the orientation of the building (North Axis parameter in E+) isn't taken in account which leads to different WWR compared to the HTML tab

Here an example with the original recipes.py with a building with EP-North Axis = 65°

OriginalRecipes_NorthAxis65deg
The WWR setted to 0 is equal to 20% in the HTML Tab and the WWR are shifted counterclockwise.

I spent some hours in the last days trying to solve these problems and came to the following lines of code which, after extensive testing, couldn't reproduce the bugs listed above.

def set_wwr(
    idf, wwr=0.2, construction=None, force=False, wwr_map=None, orientation=None
):
    # type: (IDF, Optional[float], Optional[str], Optional[bool], Optional[dict], Optional[str]) -> None
    """Set the window to wall ratio on all external walls.

    :param idf: The IDF to edit.
    :param wwr: The window to wall ratio.
    :param construction: Name of a window construction.
    :param force: True to remove all subsurfaces before setting the WWR.
    :param wwr_map: Mapping from wall orientation (azimuth) to WWR, e.g. {180: 0.25, 90: 0.2}.
    :param orientation: One of "north", "east", "south", "west". Walls within 45 degrees will be affected.

    """
    try:
        ggr = idf.idfobjects["GLOBALGEOMETRYRULES"][0]  # type: Optional[Idf_MSequence]
    except IndexError:
        ggr = None
    
    # determine EP building orientation
    building_northaxis = idf.idfobjects["BUILDING"][0].North_Axis
    
    # List ext walls
    external_walls = list(filter(
        lambda x: x.Outside_Boundary_Condition.lower() == "outdoors",
        idf.getsurfaces("wall"),
                    ))
    # Case where the "orientation" parameter is used
    if orientation != None:
        # check orientation
        orientations = {
            "north": 0.0,
            "east": 90.0,
            "south": 180.0,
            "west": 270.0,
            None: None,
                        }
        degrees = orientations.get(orientation, None)
        # filter the walls having the same orientation as the one passed to the orientation parameter
        external_walls = list(filter(
            lambda x: _has_correct_orientation(x, degrees, building_northaxis), external_walls
                        ))
        wwr_orientation = wwr
        wwr_map_final = {None: None}
    
    # When no "orientation" parameter is passed 
    else:
        wwr_orientation = None
        wwr_map_final = {0: wwr ,90: wwr, 180: wwr, 270: wwr}
        #Replaces with value from wwr_map in case not empty
        for key,value in wwr_map.items():
            wwr_map_final[key] = value
    
    subsurfaces = idf.getsubsurfaces()

    for wall in external_walls:
        # get any subsurfaces on the wall
        wall_subsurfaces = list(
            filter(lambda x: x.Building_Surface_Name == wall.Name, subsurfaces)
        )
        if not all(_is_window(wss) for wss in wall_subsurfaces) and not force:
            raise ValueError(
                'Not all subsurfaces on wall "{name}" are windows. '
                "Use `force=True` to replace all subsurfaces.".format(name=wall.Name)
            )

        if wall_subsurfaces and not construction:
            constructions = list(
                {wss.Construction_Name for wss in wall_subsurfaces if _is_window(wss)}
            )
            if len(constructions) > 1:
                raise ValueError(
                    'Not all subsurfaces on wall "{name}" have the same construction'.format(
                        name=wall.Name
                    )
                )
            construction = constructions[0]
        # remove all subsurfaces
        for ss in wall_subsurfaces:
            idf.removeidfobject(ss)
        # get the WWR for every wall taking in account the EnergyPlus North Axis
        wall_cardinalorientation = wall.azimuth + building_northaxis
        # wall_NESW result can be 0 (316° >= wall_cardinalorientation <= 45°), 90 (46° >= wall_cardinalorientation <= 135°), 180 (136° >= wall_cardinalorientation <= 225°) or 270 (226° >= wall_cardinalorientation <= 315°)
        wall_NESW = (Decimal(wall_cardinalorientation%360/90).quantize(0, ROUND_HALF_DOWN))%4*90 
        
        if wwr_orientation != None:
            wwr = wwr_orientation
        else:
            wwr = wwr_map_final.get(wall_NESW) 
        
        if wwr == 0:
            pass
        else:
            coords = window_vertices_given_wall(wall, wwr)
            window = idf.newidfobject(
                "FENESTRATIONSURFACE:DETAILED",
                Name="%s window" % wall.Name,
                Surface_Type="Window",
                Construction_Name=construction or "",
                Building_Surface_Name=wall.Name,
                View_Factor_to_Ground="autocalculate",  # from the surface angle
            )
            window.setcoords(coords, ggr)



def _has_correct_orientation(wall, orientation_degrees, building_northaxis):
    # type: (EpBunch, Optional[float]) -> bool
    """Check that the wall has an orientation which requires WWR to be set.

    :param wall: An EpBunch representing a wall.
    :param orientation_degrees: Orientation in degrees.
    :return: True if the wall is within 45 degrees of the orientation passed, or no orientation passed.
             False if the wall is not within 45 of the orientation passed.
    """
    if orientation_degrees is None:
        return True
    cardinal_orientation = wall.azimuth + building_northaxis
    if (Decimal(cardinal_orientation%360/90).quantize(0, ROUND_HALF_DOWN))%4*90 == orientation_degrees:
        return True
    return False

Here an example with the modified recipes.py with a building with EP-North Axis = 65°

LOJRecipes_NorthAxis65deg

Here the WWR of 0 is not overwritten by the standard 20%, and the WWR from the HTML Tab correspond to the inputs.

I'll create a new PR if you are happy with these modifications.

@JoLo90 JoLo90 changed the title Bug: wwr_map sets WWR to 0.2 instead of 0 Suggestion: improvement of set_wwr and _has_correct_orientation from recipes.py Aug 17, 2020
@jamiebull1 jamiebull1 added the bug label Nov 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants