DF - Windows by surface?

Hi @chris,
I’m going to make your life hell with this one … :slight_smile: :upside_down_face: but hope not.
This is another part of some of my previous topics (i.e. here and here). All of them are part of a workflow that i developed in HB_Legacy and i’m trying to update to [+].
The issue now is how to define surfaces as windows in HB as you can in HB.
Why i need that? The process is like this:

  1. I need/want to define the window areas as a % of floor area (Not as facade surface %).
  2. Say that the total south % is 20%, i need to distribute this into all south facade surfaces).
  3. I need to disregard small facade surfaces. For that i define their minimal length.

As i said i have this process working in Legacy.

Hell or not hell?

Thanks and hope you can help.
-A.

Hey @AbrahamYezioro ,

There’s always a way to do whatever you need (especially legacy workflows) so I think the chances are low that there’s any hell involved with this one. At the most-detailed end of things, you can always translate a Dragonfly Model to Honeybee, deconstruct the Honeybee Model into Rooms and add whatever Aperture surfaces you want to the Honeybee rooms to your heart’s content.

However, it sounds like you are still trying to define the windows in terms of % so you can still use the same Dragonfly components as long as you do the translation from % of floor area to % of facade area using native GH components. You can get the gross floor area of the Dragonfly Rooms using the “DF Geometry Properties” component so this should help you do the conversion.

And, if you are looking to set the same glazing ratio for all South-facing walls, the “DF Apply Facade Parameters” accepts lists of facade parameters and it will assign different parameters based on orientation (just like the Honeybee glazing-by-ratio component).

Lastly, it’s super easy to write your own Python component that strips out the windows for wall segments below a certain length. Just copy/paste this code inside a GHPython component that has a list input for _room2ds (List access w/ no type hint) and _seg_length (Item access with float type hint):

from ladybug_rhino.grasshopper import all_required_inputs

if all_required_inputs(ghenv.Component):
    room2ds = [room.duplicate() for room in _room2ds]  # copy to avoid editing the input
    for room in room2ds:
        win_pars = list(room.window_parameters)
        for i, seg in enumerate(room.floor_segments):
            if seg.length < _seg_length:
                win_pars[i] = None  # remove any windows assigned to the segment
        room.window_parameters = win_pars

In fact, here’s a sample file that demonstrates this:
TestScriptForAbraham.gh (31.0 KB)

All of the methods you see being called on the objects in that Python script can be found either in the Room2D class of Dragonfly or the LineSegment3D class of ladybug_geometry.

Welcome to the new world of being able to write a Python component to do whatever you want!

1 Like

This is good stuff @chris. Thanks.

Don’t want to get there. If doing the effort to build the model right, so this option is not the good one to take.

This is what i’m tyring to do, and did in the Legacy version of it.

Here is the MAIN issue. I don’t want to set the same ratio for all south (or whatever orientation). For each room i need to get the length of each orientation and then set a RELATIVE size for each of them. For example, if the glazing size for the south facades is 20% of the floor, i need to distribute this 20% among the south oriented walls, so each will have its relative ratio from the 20%. Like this:


The checking of the length you suggested above needs to be done BEFORE assigning the ratio so the % of floor will be keep. And this is the challenge here now, after your script example I’m not sure how to handle it.

I’ll look further into this. I’m rusty but indeed this is a new world!!

Thanks again,
-A.

Hey @AbrahamYezioro ,

You can definitely modify the Python script that I posted to do things like get the floor area of each Room2D, get the facade area by orientation, compute the glazing ratio you want to assign, etc. And you can assign different WindowParameter objects with different glazing ratios to the various wall segments of a Dragonfly Room2D. So everything is there for you to make a Python component that does this custom editing of Dragonfly Room2Ds for you. And setting up your logic up this way with your own Python component is not only going to make your Grasshopper definitions more elegant (one component instead of spaghetti soup) but it also means that your workflow should be more reusable into the future since we’re planning to not change methods and properties in the Ladybug Tools SDK over time but the Grasshopper plugin components are definitely going to change.

With all of this said, the complexities of your logic are not clear to me from what you posted. For example, it’s not clear to me if/how you want to handle cases where the % ratio of the floor area results in something greater than what can fit on the facade. If you want to write out a list of steps with if/then and for loop logic, I can help you write the Python code for it using the methods of the Ladybug Tools SDK for Room2Ds.

I guess another route if you really want to use the Grasshopper plugin components instead of Python is just tell me what functionality you had in legacy is not available in your current workflows. If it’s something as generic as as a component that outputs the length of each Room2D wall segment, I can probably work this into one of the existing Dragonfly components or show you how to do it with the Dragonfly visualization components.

hi @chris,
Thanks a lot for your response.

Took the challenge and wrote some pseudo-code. See blue group in the attached.

At the top is your script and the blue group is close to it.
At the bottom part is what i did in Legacy to get the relative size of the windows.

Hoping the code is understandable and makes sense.
Thanks,
-A.
TestScriptForAbraham_AY.gh (85.5 KB)

Hey @AbrahamYezioro ,

It was pretty easy to write a Python script with all of your logic (assuming that interpreted it all correctly). What was originally ~50 Grasshopper components in legacy can now be done with a single component containing < 100 lines of Python:

from honeybee.boundarycondition import Outdoors
from dragonfly.windowparameter import SimpleWindowRatio


def abraham_glz_ratio(floor_area, facade_area, win_flr_area_pct):
    """Get the dragonfly WindowParameters according to Abraham's logic."""
    try:
        glz_ratio  = (win_flr_area_pct * floor_area / 100.) / facade_area
    except ZeroDivisionError:  # no facade area facing the given orientation
        return None
    if glz_ratio <= 0:  # room has no floor area or the user input a negative number
        return None
    elif glz_ratio > 0.99:  # the ratio is bigger than the wall area
        return SimpleWindowRatio(0.99)
    return SimpleWindowRatio(glz_ratio)  # the ratio is a normal number


room2ds = []  # list of Room2Ds to be output from the model
for room in _room2ds:
    # get several properties of the rooom
    floor_area = room.floor_area
    height = room.floor_to_ceiling_height
    segment_lengths = [seg.length for seg in room.floor_segments]
    orientations = room.segment_orientations()
    bcs = room.boundary_conditions
    
    # properties to be computed for with orientation-spcific properties
    north_i, north_area = [], 0
    east_i, east_area = [], 0
    south_i, south_area = [], 0
    west_i, west_area = [], 0

    # group the segments by orientation if they meet the criteria for inclusion
    for i, (length, orient, bc) in enumerate(zip(segment_lengths, orientations, bcs)):
        if isinstance(bc, Outdoors) and length >= _seg_length:
            if orient >= 315 or orient < 45:  # north-facing
                north_area += length * height
                north_i.append(i)
            elif orient < 135:  # east-facing
                east_area += length * height
                east_i.append(i)
            elif 135 <= orient < 225:  # south-facing
                south_area += length * height
                south_i.append(i)
            else:  # west-facing
                west_area += length * height
                west_i.append(i)

    # compute the glazing ratio for each orientation according to Abraham's logic
    north_win_par = abraham_glz_ratio(floor_area, north_area, _win_pct_north)
    east_win_par = abraham_glz_ratio(floor_area, east_area, _win_pct_east)
    south_win_par = abraham_glz_ratio(floor_area, south_area, _win_pct_south)
    west_win_par = abraham_glz_ratio(floor_area, west_area, _win_pct_west)
    glz_ratios = [par.window_ratio if par is not None else 0 for par in
                  (north_win_par, east_win_par, south_win_par, west_win_par)]

    # build up a list of WindowParameters to assign to the Room2D
    win_par = [None] * len(room)
    for i in north_i:
        win_par[i] = north_win_par
    for i in east_i:
        win_par[i] = east_win_par
    for i in south_i:
        win_par[i] = south_win_par
    for i in west_i:
        win_par[i] = west_win_par

    # assign the window parameters to the output room
    new_room = room.duplicate()  # duplicate to avoid editing component input
    new_room.window_parameters = win_par
    room2ds.append(new_room)



TestScriptForAbraham_AY_CWM.gh (78.4 KB)

FYI, if you think you may convert some of your other logics over to Python components, I would really recommend learning PEP8 styling. It will make your code a lot more readable and reusable.

This is great @chris. Thanks a lot. Looks so “easy”.
I think there are a couple of bugs when you have 2 rooms (or more i guess):

  1. As a previous issue the result is affected by the order of the base geometry.
  2. Windows are missing in one or both rooms or are defined on interior walls. This can be connected to the previous one but didn’t get a clear conclusion so far.

    Please see the attached. The geometries are on the top-left side of the canvas. Also added the adjacency component.
    Thanks,
    -A.
    TestScriptForAbraham_AY02_CWM.gh (87.2 KB)

Hey Abraham,

As you see in the sample files, you need to intersect the Room2Ds before you solve adjacencies between them. Otherwise the adjacent segments won’t have matching lengths. So here’s the correct way to do it:

TestScriptForAbraham_AY02_CWM02.gh (84.6 KB)

Maybe I should just automatically intersect the segments within the adjacency-setting component so that this mistake doesn’t happen again (and we can get rid of the intersection component). I guess the main reason why we pulled out the intersection in Honeybee is because the intersection calculation can be really computationally intense in 3D space. But it’s a lot quicker when it’s all in 2D space and the times where someone wants to solve Room2D adjacency but doesn’t want the intersection are pretty rare. What do you think?

Hi @chris,
This is beautiful!!

I think i should pay more attention. Don’t believe it is necessary to add the automatic intersection. In this way it preserves the “normal” workflow in DF. Also the computation intensity should be preserved low as possible. So, it is excellent as it is now.
I really appreciate your help in this one (but not only). My workflows are almost translated to [+]. This one was the most difficult to achieve.

Thanks again,
-A.

Hi @chris,
One more thing. Tried to implement the shd_par input in the code. My success is partial but i believe it is not working right. Can you take a look? It is in the yellow group.
Just copied code from the DF_ApplyFacadePar component and included some of the libraries. For sure i’m missing some things.

Thanks,
-A.
TestScriptForAbraham_AY03_CWM02.gh (56.1 KB)

Hey @AbrahamYezioro ,
Nice! You were almost there with the shading parameters. The only thing that I had to change is that you were editing the Room2Ds that were input into the component instead of the Room2Ds that are output from the component. It all really comes down to understanding what’s happening in these few lines of the code that I originally wrote:

# assign the window parameters to the output room
new_room = room.duplicate()  # duplicate to avoid editing component input
new_room.window_parameters = win_par
room2ds.append(new_room)

That duplicate() method is something that virtually all Honeybee and Dragonfly objects have and it’s pretty important if you want to build your own Grasshopper components. Essentially, it just makes a copy of the original object such that you can edit this copy (eg. assign some new window parameters or shading parameters) and the original object will not be changed. This helps preserve the fundamental logic of the Honeybee Grasshopper plugin where you input a given object to a component and get a new, edited object output from the component. Importantly, you still have the old object in it’s un-edited state if you look at the output of the previous component. What you were doing in your script, though, was editing this input object and, therefore, it breaks this Grasshopper logic.

In legacy, I think we named this process of duplicating the object “Calling from Honeybee Hive” and it was way more complicated than it needed to be. If you just remember to call this duplicate() method in [+], you should be all set to make your own Grasshopper components.

Also, here is your corrected file:
TestScriptForAbraham_AY03_CWM03.gh (50.7 KB)

Working good now. Thanks (again) @chris!!

One thing i was thinking is an error is that for options Overhang and Louver the shade will be constrained to the window size while it is taking the whole wall surface. Maybe it is better this way, it’s just the icon that confused me.

Thanks again and i’ll think on another challenge … :wink:
-A.

Glad to hear it!

And, no that’s not an error but it’s just the way that those particular ShadingParameters were written. It isn’t that hard to add a new ShadingParameter class to the core libraries that can add an overhang over each individual window instead of the parent Face. If it’s something that you’d like, feel free to open a “Wish” issue for it on the dragonfly-core repo:

Thinking about it i think is much better as it is now (shades on the wall surface) instead on a per window base. Just imagine 2 windows, different sizes with the same parameters. This will be a messy facade.
Maybe it can be a nice addition, but for now, not something i wish for.
-A.

1 Like

Hi @chris,
I was thinking about something and before i open a “wish”, wanted to see what do you think.
The shades are created on each facade (is fine). The thing is that they are created regardless they have any window on them. Here i’m not sure it is desired.
So i want to hear your comments on this and understand the logic.
My wish will be to decide whether or not to create shades on facades without windows.
Makes sense?
-A.

Hey @AbrahamYezioro ,

I thought from the way you set up your original code that you wanted to assign the shading parameters independently of the windows. But here’s a GH file with an updated component that only applies the ShadingParameters to the walls where there are windows:

TestScriptForAbraham_AY03_CWM04.gh (51.8 KB)

Hi @chris,
Please see this image:


The previous and new code can deal with the Extruded shade, but the Overhang and Louver are still created in the plain walls.
-A.

I should have known you also wanted to account for the case where the users sets the ratio to be 0 instead of just the cases where the segment is too short. Here’s the updated sample:
TestScriptForAbraham_AY03_CWM04.gh (53.5 KB)

This is beautiful!!
Charm of code.
Thanks @chris,
-A.

Hi again @chris for this discussion.
Following other topics regarding whether to work with DF or HB, i wanted to translate the code above to HB. Unfortunately i’m struggling quite some time and right now i’m kind of stuck.
I think i get some of the parts but others i can’t find the parallel HB version. See point 3. in the attached. The fie has both tracks (above-DF, below-HB).
In point 4., BTW, I am trying to “code” the different attributes of the rooms in HB.

Also wanted to ask you advice whether is better to output the zones with the windows, as in your code, or just the ratios as a tree branch per room. The advantage i see from the later is that i can connect the values with the other win_par options.

There are a couple of issues in the attached file that maybe you can help:

  1. The DF version is not outputting the roof as exterior surface. It is a ceiling. Can’t see what or where i’m doing something wrong. I have another file where the roof if ok.
  2. I believe the ‘average_floor_height’ room attribute is wrong. Instead of getting some height (3 for the example) i’m getting 0. Can be?

Hope i’m not messsing too much here … and thanks!!
-A.
TestScriptForAbraham_DF vs HB_AY04_CWM04.gh (139.4 KB)