Dragonfly/Ladybug: Potential Bugs and Question

@annasong

I’ll need some time to look into questions 1 and 2, or @chris will respond when he has time, but I can take a stab at answering question 3.

So for some reason the IronPython interpreter is choosing the path to your Desktop UWG, rather then using your Rhino scripts folder exclusively. I believe the way the sys.path works in Python, it selects the first UWG folder it finds in the list of all directories in sys.path. And in this case Rhino adds the scripts folder to the list of directories for IronPython to search in the sys.path when it starts. At some point, I assume you are adding your desktop directory to the sys.path, which is why the interpreter is going there to find the UWG, before going to the Rhino scripts folder.

You can confirm this by printing your sys.path, and you should see the desktop directory there before (i.e at a lower index) then the Rhino scripts directory.

I’m not sure how the UWG from your desktop is getting added there but a couple of things to check out:

  • If you are running the desktop UWG at any point, while you have Rhino open, you might be using the same interpreter, which is therefore inserting it’s path before the Rhino scripts path.
  • You may have added your desktop or related directory to the top of your to the environment variable PYTHONPATH i.e like so:
    .
    (Caveat, this is for Cpython, but I think similiar for IronPython.

So making sure to insert the correct path to the Rhino scripts version at index 0, should fix this.

Is this what you tried with the sys library?

sys.path.insert(0, 'C:\\Users\\%User%\\AppData\\Roaming\\McNeel\\Rhinoceros\\6.0\\scripts')

You can also try this just to make sure:

sys.path.insert(0, 'C:\\Users\\%User%\\AppData\\Roaming\\McNeel\\Rhinoceros\\6.0\\scripts\\UWG')

A common mistake is to use append rather then insert, and not fix the order problem. If you are using insert, I think that should work… As long as you run that at the beginning of any Dragonfly session, it should force the IronPython editor to search the Rhino scripts directory first. It may not be good coding practice, because you are adding redundant paths, but it should work.

Let me know if you’ve tried that. Alternatively, print the sys.path before and after this is occurring, and we can at least confirm if path order is the cause of this problem.

Saeran

1 Like

Regarding question 2.

I remember now that the absence of the roof albedo and roof vegetation fractions are due to an earlier question we were having about the contribution of the roof parameters to the urban canyon.

Essentially, one of Joseph’s contribution to the UWG was to bound the volume of the urban canyon to the top of the building heights. and have the the Urban boundary layer extend vertically from the top of the representative building. (In the previous model the canyon height was twice the building height). In this way, all the heat from the roof is contributed to the boundary layer, and not the canyon layer. Arguably this underestimates the heat contribution from roofs to the urban canyon, but it made UWG more consistent with the Town Energy Balance model.

As such, originally we were going to remove the roof albedo, and roof vegetation fraction in inputs from the user inputs, since it was reasoned that it didn’t have an impact on the urban canyon. (Especially as the boundary layer didn’t take into account radiation from the roof). However, Joseph pointed out that the sensible heat flux from the roof to the boundary layer, which impacts the canyon conditions through advective exchange should be represented, so we kept those inputs.

Anyway, all of this is to say, I think in removing, then re-adding these inputs, it looks like the roof characteristics weren’t fully added back into the GH side of things. While it is true that the roof characteristics have less of an impact on the urban canyon then the wall surfaces in this model, I think we should be taking the area-weighted average of the different building typology roof characteristics during the merging process.

S

2 Likes

Thanks @SaeranVasanthakumar , for answering the majority of the questions. Especially the ones about the path, which seems pretty complex and I am at a bit of a loss to explain.

I can confirm that points 1. and 2. are bugs that we should fix. They are both relics from early points in the design of Dragonfly (as Saeran points out about the roof parameters). The _bldg_types is also a relic from when I was playing with a different way of linking the building typologies and the disctionary that stores the ratios of the typologies on the city object.

@annasong , you are welcome to send another pull request with fixes and I’ll metge it in (thank you again for the last one you sent!) Otherwise, I can take care of it in a day or two.

1 Like

@SaeranVasanthakumar,

Thanks so much for your explanation! I think you’re right, in that the Desktop\urbanWeatherGen-master UWG version was being imported because I was running other python files also residing in the urbanWeatherGen-master folder.

Because I often cannot replicate this issue of the incorrect UWG file being imported, I just now tried to import the incorrect UWG file (since DF is importing the correct version) to test my control of the import process. Below is an image of the code I added to the DF Run UWG component and the test results.

Unfortunately, the results show that while I’ve changed sys.path accordingly to begin with the Desktop location, the UWG version imported is still from the Rhino scripts folder. I also tried inserting this piece of code into the DF Dragonfly component with the same results. When the incorrect version was being imported, I also tried to insert to sys.path the Rhino scripts folder location in this way to no avail. I’m thinking Rhino might have its own paths that it searches through before it goes through sys.path.

Regardless, I think I understand now why the wrong import was happening. Thanks once again for your detailed explanation!

Also, thanks for your response to my second question! It all makes sense now.

@chris,

Thanks for the explanation! I created a pull request with proposed edits for these two issues. To calculate the new roof albedo and roof veg fraction for the merged typology, I averaged the old roof albedo and roof veg fractions by the footprint areas of the old typologies. As for the second issue, I deleted that line of code appending the program string to self._bldg._types, but I’m not sure if that’s the optimal solution you had in mind.

Thanks.

Thank you, @annasong ! I merged in your fixes. You’ve saved us from a lot of bugs that would have caused us some headaches down the line :slight_smile:

1 Like

I think I know why the sys.path insert() didn’t work. I forgot that the python package defines and loads all local modules in its package during it’s initialization in the init.py file. So the “UWG” variable in this case is already likely already attached to the scripts directory UWG right at the beginning, and changing the sys.path downstream won’t change that. I say “likely” because, I don’t know how GHPython initializes the python in it’s components, but I assume it’s working in a similar fashion.

So rather then printing your sys.path, print your globals(). globals() is a method in python that returns a dictionary of all the variables in your module. I believe in this case you should see that the ‘UWG’ key is attached the module path to your desktop UWG (or whatever the wrong path is).

Unfortunately I’m not sure how to fix this. Stackoverflow seems to suggest not start clearing or messing around with the globals() dictionary. Maybe you could just redefine the UWG key with the correct module path? Anyway, I hope this at least provides some more context on the problem, and I think posting on Stackoverflow would be the best way of resolving it.

1 Like

@SaeranVasanthakumar,

Yes, this really helps explain why changing sys.path didn’t affect the address from which UWG was being imported. Thank you so much for your insight and suggestions!

For what it’s worth, see this post by Giulio:

1 Like

@SaeranVasanthakumar and @annasong ,

I believe that I have found a solution, which I arrived at through a test I was doing that had nothing to do with the UWG. It seems that, if a folder containing python libraries is in the same directory as the grasshopper script that is importing those libraries, the grasshopper script will load the libraries from that folder instead of the Rhino scripts folder.

Based on this logic, I would venture a guess that the grasshopper script that you were working with @annasong is also on your desktop. If so, then we have found the solution, which is simply to move the uwg libraries into a different directory than your grasshopper script.

If not, then we may have to keep searching for the answer.

1 Like

I should also say that, if you ever want to purposefully load a python library from a given location, @mostapha 's response on this post is a good way to do it:

2 Likes

@chris and @SaeranVasanthakumar,

Thanks for looking into this issue, Chris! Just to clarify, when you refer to the “grasshopper script”, do you mean the script behind the Dragonfly components used in the Grasshopper Definition file (UWG Workflow)? If so, the source code for these components are located in the Dragonfly-master folder I downloaded from GitHub, and unfortunately they are not located in the Desktop folder.

Thanks again.

@chris and @SaeranVasanthakumar,

I think I’ve found a piece of code that will import the desired version of UWG that only uses Python libraries. It’s a slight modification off of the solution for Python 2 posted here: https://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path.

The code is:

import imp

filepath = "C:\\Users\\Anna Song\\AppData\\Roaming\\McNeel\\Rhinoceros\\6.0\\scripts\\UWG\\__init__.py"
#filepath = "C:\\Users\\Anna Song\\Desktop\\urbanWeatherGen-master\\UWG\\__init__.py"

foo = imp.load_source('all', filepath)
from all import UWG

Filepath is the path to the file we want to import, which in our case is the init.py file in the UWG folder provided in the Rhino 6 scripts folder (although we could choose to import the init.py in the Desktop UWG folder). I believe we should choose to import the init.py instead of UWG.py itself since UWG.py imports other files in the UWG folder, and so errors are raised if we just import UWG.py because the files it imports cannot be found.

The first argument to imp.load_source is the name we give the module that filepath leads us to. By importing init.py first, we can successfully import UWG from init.py (now named all).

Thank you for all of your help! The previous suggestions and solutions were really constructive!

Hi @SaeranVasanthakumar,

In response to one of my previous questions, we discussed how when typologies are merged via create_merged_typology, the roof_albedo and roof_veg_fraction parameter values of the individual typologies weren’t preserved in the new merged typology when their values were not the default values (in other words, they were user entered).

I’m wondering if the same applies to the shgc parameter? The shgc parameter value for the new merged typology doesn’t seem to take into account the shgc values of the original individual typologies.

In the case that at least one of the two shgc values was set by the user, should the other shgc value that’s equal to None be initialized to its default value (or both shgc values could have been set by the user) and the two resulting shgc values be averaged by façade area?

Thanks.

Sorry for the late response, but yes, the SHGC should definitely be area-weighted as well. I will take a look at the code when I have some time.

1 Like

@SaeranVasanthakumar,

Not late at all! Thanks!

@SaeranVasanthakumar,

After looking at the DF Dragonfly component code some more, I have a few more questions.

  1. Through the DF Typology component, users can enter a value for the average height between floors, which is then passed into the Typology class’s from_geometry method under the argument floor_to_floor. I was wondering if this input is expected to be in meters or match the units that the Rhino file is in?

If floor_to_floor is expected to be in meters, in the geometry parameter calculation process, should we first convert floor_to_floor to match the units the Rhino file is in? I say this because in the Geometry class’s getFloorBreps method, where floor_to_floor has been renamed floorHeight, there exists the lines

fullRange = massBB.Max.Z - massBB.Min.Z
numCrvs = int(math.floor(fullRange / floorHeight))

In this case, I assume that fullRange is in the units that the Rhino file is in and floorHeight is in meters, but we want them to be in the same units. numCrvs will affect the floor breps extraction process, which will change our values for floor area and footprint area.

On the other hand, if floor_to_floor is expected to be given in the same units as the Rhino file, in the case that no floor_to_floor is entered, the default value of 3.05 is given to floor_to_floor. However, the units behind 3.05 is meters, so would we have to adjust 3.05 be a conversion factor to get it to be in the same units as the Rhino file once again?

  1. Instances of both the City and Terrain classes have the attribute characteristic_length. When a City object is created, its characteristic_length attribute is the same as its Terrain object’s characteristic_length attribute.

In the Terrain class’s area attribute’s setter method, characteristic_length is calculated as follows

self._characteristic_length = math.sqrt(self._area)

In this case, characteristic_length is the length of one side of a square with the same area as the actual terrain. However, in the City class’s building_typologies and update_geo_from_typologies methods, the site area is recovered by assuming that the characteristic_length is the radius of a circle that has area matching the terrain area.

site_area = math.pow(self.characteristic_length,2) * math.pi

Does this mean that when calculating characteristic_length in the Terrain class, the terrain area should be divided by pi before the square root is taken?

  1. In the City class’s update_geo_from_typologies method, there is the code
totalWeight = sum(floorAreas)
typologyRatios = [x/totalWeight for x in floorAreas]
self._building_typologies = {}
for i, key in enumerate(fullTypeNames):
      self._building_typologies[key] = typologyRatios[i]

In this case, _building_typologies is a dictionary that matches string descriptions of typology program, age to that typology’s floor area coverage fraction. However, in other references to _building_typologies, it is a list of Typology objects in the current City object (for example, in the building_typologies and from_typologies methods in the City class). Was self._bldg._type_ratios meant here instead?

Thanks.

@annasong good finds as usual!

For #1: I think you’re right. I actually assumed that Ladybug always just expects the Rhino doc to be in meters, but I did a search of the code and it looks like Chris has included a linear conversion factor here which means that’s not the case:

The conversion factor is for converting everything into meters, so in this case we just have to use the self.linearConversionFac on the fullRange variable.

Do you want to send this correction in as a PR to the DF repo? Otherwise I can do it as well.

S

For # 2: I looked into this, and the problem here is slightly more complicated then it appears. There seems to be a discrepancy from how the characteristic_length is described and how it’s used in the uwg, that we need to investigate further. More details here:

For #3: I think Chris would be the best one to take a look at this one, as I’m not that familiar with this method. Thoughts @chris?

S

@annasong and @SaeranVasanthakumar ,

Thanks for all of the detective work here. For 1, I tried to make it so that all core Dragonfly objects have properties in SI (meters) and all conversion to meters happens in the geometry class with all if the Rhino API calls. It seems like I might have missed this for the floor-to-floor parameter so we should fix this soon.

For 2. I put a response on the github issue, @SaeranVasanthakumar.

For 3., It might be best to see if this issue still persists after I clean up the DF code into it’s own pure python library. While I’ll be a bit busy in the next 3-4 weeks, I have managed to secure a good excuse of investing more time in the Dragonfly core so I should be able to finish cleaning it up soon after.