Skip to content

Instantly share code, notes, and snippets.

@yorikvanhavre
Last active July 25, 2020 11:16
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yorikvanhavre/0e7ab25d077263a25e1dac0a3c974d28 to your computer and use it in GitHub Desktop.
Save yorikvanhavre/0e7ab25d077263a25e1dac0a3c974d28 to your computer and use it in GitHub Desktop.
FreeCAD ladybug integration notes
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The ladybug-tools project features different components to integrate BIM projects with energy and climate analysis tools. Most are divided or in the process of being divided) between an independent core component, and GUI components for different BIM apps like Revit or Rhino, mostly using node-based systems like Grasshopper or Dynamo. Everything is written in Python and has a good API.\n",
"\n",
"For the FreeCAD integration, however, I'm thinking of using core funcionality directly, and creating interface panels on the fly, don't know yet. This document is mostly to explore and find interesting functionality.\n",
"\n",
"This document is hosted at https://gist.github.com/yorikvanhavre/0e7ab25d077263a25e1dac0a3c974d28\n",
"\n",
"\n",
"### Ladybug\n",
"\n",
"[Ladybug](https://github.com/ladybug-tools/ladybug) is a component to interact with weather data. Weather data is typically downloaded from the [energyplus website](https://energyplus.net/weather), maintained by the USA goverment, but has files for most parts of the world. \n",
"\n",
"Ladybug can be cloned with GIT, but it's easy to install with pip with:\n",
"\n",
"```pip3 install lbt-ladybug```\n",
"\n",
"This is how you work with it (I downloaded one epw file and renamed it \"bxl.epw\" for simplicity).\n",
"\n",
"#### Contents of ladybug module"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['analysisperiod',\n",
" 'compass',\n",
" 'datacollection',\n",
" 'datatype',\n",
" 'designday',\n",
" 'dt',\n",
" 'epw',\n",
" 'futil',\n",
" 'header',\n",
" 'location',\n",
" 'psychrometrics',\n",
" 'skymodel',\n",
" 'sunpath']"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import ladybug\n",
"[e for e in dir(ladybug) if not e.startswith(\"_\")]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Contents of ladybug.epw module:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['AnalysisPeriod',\n",
" 'DesignDay',\n",
" 'EPW',\n",
" 'EPWField',\n",
" 'EPWFields',\n",
" 'Header',\n",
" 'HourlyContinuousCollection',\n",
" 'Location',\n",
" 'MonthlyCollection',\n",
" 'angle',\n",
" 'calc_sky_temperature',\n",
" 'distance',\n",
" 'division',\n",
" 'energyflux',\n",
" 'energyintensity',\n",
" 'fraction',\n",
" 'generic',\n",
" 'illuminance',\n",
" 'luminance',\n",
" 'os',\n",
" 'pressure',\n",
" 'readmode',\n",
" 'speed',\n",
" 'temperature',\n",
" 'write_to_file',\n",
" 'xrange']"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from ladybug import epw\n",
"[e for e in dir(ladybug.epw) if not e.startswith(\"_\")]"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hourly Continuous Data Collection\n",
"1/1 to 12/31 between 0 and 23 @1\n",
"Dry Bulb Temperature (C)\n",
"...8760 values...\n"
]
},
{
"data": {
"text/plain": [
"EPW file Data for [BRUSSELS]"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"epw_data = ladybug.epw.EPW(\"bxl.epw\")\n",
"print(epw_data.dry_bulb_temperature)\n",
"epw_data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Contents of epw object:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['ToString',\n",
" 'aerosol_optical_depth',\n",
" 'albedo',\n",
" 'annual_cooling_design_day_004',\n",
" 'annual_cooling_design_day_010',\n",
" 'annual_heating_design_day_990',\n",
" 'annual_heating_design_day_996',\n",
" 'atmospheric_station_pressure',\n",
" 'ceiling_height',\n",
" 'comments_1',\n",
" 'comments_2',\n",
" 'convert_to_ip',\n",
" 'convert_to_si',\n",
" 'cooling_design_condition_dictionary',\n",
" 'daylight_savings_end',\n",
" 'daylight_savings_start',\n",
" 'days_since_last_snowfall',\n",
" 'dew_point_temperature',\n",
" 'diffuse_horizontal_illuminance',\n",
" 'diffuse_horizontal_radiation',\n",
" 'direct_normal_illuminance',\n",
" 'direct_normal_radiation',\n",
" 'dry_bulb_temperature',\n",
" 'extraterrestrial_direct_normal_radiation',\n",
" 'extraterrestrial_horizontal_radiation',\n",
" 'extreme_cold_weeks',\n",
" 'extreme_design_condition_dictionary',\n",
" 'extreme_hot_weeks',\n",
" 'file_path',\n",
" 'from_dict',\n",
" 'from_missing_values',\n",
" 'global_horizontal_illuminance',\n",
" 'global_horizontal_radiation',\n",
" 'header',\n",
" 'heating_design_condition_dictionary',\n",
" 'horizontal_infrared_radiation_intensity',\n",
" 'import_data_by_field',\n",
" 'is_data_loaded',\n",
" 'is_header_loaded',\n",
" 'is_ip',\n",
" 'is_leap_year',\n",
" 'liquid_precipitation_depth',\n",
" 'liquid_precipitation_quantity',\n",
" 'location',\n",
" 'metadata',\n",
" 'monthly_ground_temperature',\n",
" 'opaque_sky_cover',\n",
" 'precipitable_water',\n",
" 'present_weather_codes',\n",
" 'present_weather_observation',\n",
" 'relative_humidity',\n",
" 'save',\n",
" 'sky_temperature',\n",
" 'snow_depth',\n",
" 'to_dict',\n",
" 'to_wea',\n",
" 'total_sky_cover',\n",
" 'typical_weeks',\n",
" 'visibility',\n",
" 'wind_direction',\n",
" 'wind_speed',\n",
" 'years',\n",
" 'zenith_luminance']"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[e for e in dir(epw_data) if not e.startswith(\"_\")]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The ladybug project also maintains a site where you can download epw files: https://www.ladybug.tools/epwmap/\n",
"\n",
"You can also create locations or extract location from an epw file:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Site:Location,\n",
" BRUSSELS,\n",
" 50.9, !Latitude\n",
" 4.53, !Longitude\n",
" 1.0, !Time Zone\n",
" 58.0; !Elevation\n"
]
}
],
"source": [
"loc = ladybug.location.Location('Sydney', 'AUS', latitude=-33.87, longitude=151.22, time_zone=10)\n",
"loc = epw_data.location\n",
"print(loc)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ladybug can calculate sun paths:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"altitude: 18.02052748676022, azimuth: 158.46264722289163\n"
]
}
],
"source": [
"sp = ladybug.sunpath.Sunpath.from_location(loc)\n",
"sun = sp.calculate_sun(month=11, day=15, hour=11.0)\n",
"print('altitude: {}, azimuth: {}'.format(sun.altitude, sun.azimuth))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Most data in the EPW object are lists of numbers for each hour of the year, like *dry_bulb_temperature* or *relative_humidity* which are useful for energy analyses, but not really directly for the user."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hourly Continuous Data Collection\n",
"1/1 to 12/31 between 0 and 23 @1\n",
"Relative Humidity (%)\n",
"...8760 values...\n"
]
}
],
"source": [
"print(epw_data.relative_humidity)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"8760"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"365*24"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Creating a solar diagram\n",
"\n",
"Obtaining directional 3D vectors for 9h, 12h and 15h on March 30th, June 30th, September 30th and December 30th"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[(-0.9082125488882267, -0.4185092185865809, 0.9166072437014305), (-0.26937811050451205, -0.9630344924150012, 0.7424663286416248), (0.6741897527703045, -0.7385581745939285, 0.815188618704883), (-0.9820592723540658, -0.18857249423869546, 0.7795769997217051), (-0.3732549295631947, -0.9277288168192117, 0.4867536841253755), (0.8264136162967166, -0.5630635264331931, 0.615548708213317), (-0.8476997769708247, -0.5304762842235402, 0.9402295952215028), (-0.17025295274041632, -0.9854003917612218, 0.8118084839575337), (0.6783988696012553, -0.7346937958930503, 0.8867106229004053), (-0.7633756061356852, -0.6459548621668353, 0.9996954244183549), (-0.18383923938399696, -0.9829563235783741, 0.9642046328113717), (0.5207118254072426, -0.8537325078038538, 0.9839803824126283)]\n"
]
}
],
"source": [
"import math\n",
"vectors = []\n",
"for month in (3,6,9,12):\n",
" for hour in (9,12,15):\n",
" sun = sp.calculate_sun(month=month, day=30, hour=hour)\n",
" xyangle = 90 + sun.azimuth # use X direction as base\n",
" x = math.cos(math.radians(xyangle))\n",
" y = math.sin(math.radians(xyangle))\n",
" z = math.cos(math.radians(sun.altitude))\n",
" vectors.append((x,y,z))\n",
"print(vectors)\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Creating a wind rose\n",
"Getting a series of 3D vectors for each portion of a circle, representing a wind itensity (max 1.0)"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[(-0.027054519841974333, 0.2054994803574807, 0), (-0.047313588001502004, 0.11422510583775909, 0), (-0.04205988054969343, 0.054813503511030794, 0), (-0.18925083317492733, 0.14521727179262578, 0), (-0.13841395177987278, 0.05733293604887892, 0), (-0.16223643186116898, 0.021358831454190257, 0), (-0.06417352193619574, -0.008448604441879686, 0), (-0.16193088533470554, -0.06707396887271753, 0), (-0.03461905484907208, -0.026564135084016904, 0), (-0.07659325615891543, -0.09981827481482448, 0), (-0.050931685907499184, -0.12295996687241127, 0), (-0.04651478850023658, -0.3533148960532124, 0), (0.03540819614405755, -0.2689519514854046, 0), (0.2922309847151596, -0.7055080066449826, 0), (0.21738317210420494, -0.28329926551490653, 0), (0.7933533402912349, -0.6087614290087209, 0), (0.3675360758426719, -0.15223842727542108, 0), (0.5256460392301875, -0.06920261391157649, 0), (0.22424680137254913, 0.029522651476680668, 0), (0.37963049881372873, 0.15724810129910968, 0), (0.08943255836010289, 0.06862401563371032, 0), (0.14256085828422413, 0.18578892769002012, 0), (0.0325628811539749, 0.0786137493118695, 0), (0.01708706516335223, 0.1297891454889352, 0)]\n"
]
}
],
"source": [
"sectors = 24 # the number of sectors to divide the circle into\n",
"\n",
"baseangle = 360/sectors\n",
"sectorangles = [i * baseangle for i in range(sectors)] # the divider angles between each sector\n",
"basebissect = baseangle/2\n",
"angles = [basebissect] # build a list of central direction for each sector\n",
"for i in range(1,sectors):\n",
" angles.append(angles[-1]+baseangle)\n",
"\n",
"windsbysector = [0 for i in range(sectors)] # prepare a holder for values for each sector\n",
"for hour in epw_data.wind_direction:\n",
" sector = min(angles, key=lambda x:abs(x-hour)) # find the closest sector angle\n",
" sectorindex = angles.index(sector)\n",
" windsbysector[sectorindex] = windsbysector[sectorindex] + 1\n",
"maxwind = max(windsbysector)\n",
"windsbysector = [wind/maxwind for wind in windsbysector] # normalize\n",
"\n",
"vectors = [] # create 3D vectors\n",
"for i in range(sectors):\n",
" angle = math.radians(90 + angles[i])\n",
" x = math.cos(angle) * windsbysector[i]\n",
" y = math.sin(angle) * windsbysector[i]\n",
" vectors.append((x,y,0))\n",
"print(vectors)\n",
" \n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Honeybee\n",
"\n",
"This is the honeybee module. Install with:\n",
"```\n",
"pip3 install lbt-honeybee\n",
"pip3 install honeybee-radiance\n",
"pip3 install honeybee-energy\n",
"```\n",
"The honeybee module basically allows you to build energy analysis (gbxml) models and run through radiance or energyplus\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '_base', '_basewithshade', '_lockable', 'altnumber', 'aperture', 'boundarycondition', 'config', 'door', 'extensions', 'extensionutil', 'face', 'facetype', 'finder', 'get_logger', 'importlib', 'ispkg', 'logger', 'logutil', 'name', 'orientation', 'pkgutil', 'properties', 'room', 'shade', 'typing', 'writer']\n"
]
}
],
"source": [
"import honeybee\n",
"print(dir(honeybee))"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"ename": "ModuleNotFoundError",
"evalue": "No module named 'honeybee.radiance'",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-19-db076f6eb03a>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mhoneybee\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mroom\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mRoom\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mhoneybee\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mradiance\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmaterial\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mglass\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mGlass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mhoneybee\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mradiance\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msky\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcertainIlluminance\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mCertainIlluminanceLevel\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mhoneybee\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mradiance\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrecipe\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpointintime\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgridbased\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mGridBased\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'honeybee.radiance'"
]
}
],
"source": [
"# script below from https://github.com/ladybug-tools/honeybee\n",
"\n",
"from honeybee.room import Room\n",
"from honeybee.radiance.material.glass import Glass\n",
"from honeybee.radiance.sky.certainIlluminance import CertainIlluminanceLevel\n",
"from honeybee.radiance.recipe.pointintime.gridbased import GridBased\n",
"\n",
"# create a test room\n",
"\n",
"room = Room(origin=(0, 0, 3.2), width=4.2, depth=6, height=3.2, rotation_angle=45)\n",
"print(room)\n",
"\n",
"# add fenestration\n",
"\n",
"# # add a window to the back wall\n",
"\n",
"room.add_fenestration_surface(wall_name=\"back\", width=2, height=2, sill_height=0.7)\n",
"\n",
"# add another window with custom material. This time to the right wall\n",
"\n",
"glass_60 = Glass.by_single_trans_value(\"tvis_0.6\", 0.6)\n",
"room.add_fenestration_surface(\"right\", 4, 1.5, 1.2, radiance_material=glass_60)\n",
"\n",
"# run a grid-based analysis for this room\n",
"\n",
"# generate the sky\n",
"\n",
"sky = CertainIlluminanceLevel(illuminance_value=2000)\n",
"\n",
"# generate grid of test points\n",
"\n",
"analysis_grid = room.generate_test_points(grid_size=0.5, height=0.75)\n",
"\n",
"# put the recipe together\n",
"\n",
"rp = GridBased(sky=sky, analysis_grids=(analysis_grid,), simulation_type=0,\n",
"hb_objects=(room,))\n",
"\n",
"# write and run the analysis\n",
"\n",
"batch_file = rp.write(target_folder=\"/home/yorik/ladybug\", project_name=\"room\")\n",
"rp.run(batch_file, debug=False)\n",
"\n",
"# results - in this case it will be an analysis grid\n",
"\n",
"result = rp.results()[0]\n",
"\n",
"# print the values for each point\n",
"\n",
"for value in result.combined_value_by_id():\n",
" print(\"illuminance value: %d lux\" % value[0])"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '_extend_honeybee', 'config', 'dynamic', 'geometry', 'get_logger', 'honeybee_radiance', 'lib', 'lightpath', 'logger', 'modifier', 'modifierset', 'mutil', 'primitive', 'properties', 'reader', 'sensor', 'sensorgrid', 'writer']\n"
]
}
],
"source": [
"import honeybee_radiance\n",
"print(dir(honeybee_radiance))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The above contents doesn't correspond to the examples nor what's on github... I suspect there is some big version difference between the version on pip and the one being developed. Not sure which one to take from now...\n",
"\n",
"Also, it seems honeybee.room can only create cubic rooms."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment