Skip to content

Commit

Permalink
feat(*): working version
Browse files Browse the repository at this point in the history
  • Loading branch information
devangcx committed Jul 30, 2022
1 parent 8117b72 commit 1c6dd4a
Show file tree
Hide file tree
Showing 13 changed files with 109 additions and 43 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ tox.ini
#generate html model
model.html

venv/*
venv/*
*.devcontainer
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
FROM python:3.7 as base

RUN apt-get update \
&& apt-get -y install ffmpeg libsm6 libxext6 xvfb --no-install-recommends \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Estidama-Daylight App

### An app to run compliance analysis for PBRS (Pearl Building Rating System) LBi-7 Daylight & Glare credit for Abu Dhabi
![App](/images/app.png)

## To run the app locally
Expand Down
9 changes: 3 additions & 6 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from pathlib import Path

from pollination_streamlit_io import get_host
from pollination_streamlit.api.client import ApiClient

from model import sensor_grids
from introduction import introduction
Expand Down Expand Up @@ -79,15 +78,13 @@ def main():
st.session_state.api_client = api_client

with tab3:
st.session_state.job_url = 'https://app.pollination.cloud/devang/projects/demo/studies/8123d1b6-71f5-414b-9601-269d5eda5c46'
st.session_state.api_client = ApiClient(
api_token='CC0E061B.0C4E4FA7BBA51BB7BEE453D3')

if 'job_url' not in st.session_state:
st.error('Go back to the Simulation tab and submit the job.')
return

sim_dict, res_dict = visualization(st.session_state.job_url,
sim_dict, res_dict = visualization(st.session_state.host,
st.session_state.hbjson_path,
st.session_state.job_url,
st.session_state.api_client,
st.session_state.temp_folder)
if sim_dict and res_dict:
Expand Down
7 changes: 5 additions & 2 deletions estidama.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,14 @@ def description(self) -> Union[None, str]:
if self._month == 9:
return 'Equinox'
elif self._month == 6:
return 'Solstice'
return 'Summer Solstice'

def as_string(self) -> str:
def __str__(self) -> str:
return f'{self._month}_{self._day}_{self._hour}'

def __repr__(self) -> str:
return f'{self.description()} @ {self._hour}:00'


SIM_TIMES = [PointInTime(9, 21, 10), PointInTime(9, 21, 12), PointInTime(9, 21, 14),
PointInTime(6, 21, 10), PointInTime(6, 21, 12), PointInTime(6, 21, 14)]
36 changes: 36 additions & 0 deletions helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import streamlit as st
from pathlib import Path
from typing import Dict
from honeybee.model import Model as HBModel
from honeybee.room import Room

Expand Down Expand Up @@ -48,3 +49,38 @@ def hash_model(hb_model: HBModel) -> dict:
def hash_room(room: Room) -> dict:
"""Help Streamlit hash a Honeybee room object."""
return {'name': room.identifier, 'volume': room.volume, 'faces': len(room.faces)}


def create_analytical_mesh(results_folder: Path, hb_model: HBModel) -> dict:
""" Generate analysis grid for sketchup and rhino
args:
results_folder: Path to the result folder with grids_info.json and .res files.
hb_model: A Honeybee model.
returns:
An analytical mesh object.
"""
hb_model = hb_model.to_dict()

info_file = results_folder.joinpath('grids_info.json')
info = json.loads(info_file.read_text())
grids = hb_model['properties']['radiance']['sensor_grids']

geometries = []
merged_values = []
for i, grid in enumerate(info):
result_file = Path(results_folder, f"{grid['full_id']}.res")
values = [float(v) for v in result_file.read_text().splitlines()]
# clean dict
mesh = json.dumps(grids[i]['mesh'])

merged_values += values
geometries.append(json.loads(mesh))

analytical_mesh = {
"type": "AnalyticalMesh",
"mesh": geometries,
"values": merged_values
}
return analytical_mesh
Binary file modified images/app.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions introduction.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ def introduction(host: str, target_folder: Path,

hbjson_path = write_hbjson(target_folder, hb_model)
show_model(hbjson_path, target_folder, key='model')
else:
st.success('Model linked. Move to the next tab to select occupied areas.')
else:
hb_model = None

Expand Down
5 changes: 4 additions & 1 deletion model.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from honeybee.facetype import Floor
from honeybee_vtk.vtkjs.schema import SensorGridOptions

from pollination_streamlit_io import send_hbjson
from pollination_streamlit_io import send_hbjson, send_geometry

from helper import write_hbjson, hash_model, hash_room
from web import show_model
Expand Down Expand Up @@ -64,6 +64,9 @@ def add_sensor_grids(hb_model: HBModel, rooms: List[Room],
"""
grids = [generate_room_grid(room, grid_size, tolerance) for room in rooms]

if hb_model.properties.radiance.sensor_grids:
hb_model.properties.radiance.sensor_grids = ()

model = hb_model.duplicate()
model.properties.radiance.add_sensor_grids(grids)

Expand Down
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

pollination-streamlit-viewer>=0.4.5
pollination-streamlit-io>=0.31.3
pollination-streamlit>=0.3.0
streamlit>=1.11.0
streamlit-folium>=0.6.14
honeybee-vtk >= 0.38.3
pandas==1.3.5
honeybee-vtk>=0.38.3
pandas>=1.3.5
plotly>=5.9.0
18 changes: 9 additions & 9 deletions result.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,11 @@ def prepare_results(program: Program, occupied_rooms: List[Room],
compliant_areas = []
file_name = f'grid_{room.display_name}.res'
for sim_time in SIM_TIMES:
sim_id = sim_dict[sim_time.as_string()]
sim_id = sim_dict[str(sim_time)]
res_file_path = res_file_dict[sim_id].joinpath(file_name)
compliant_area = room.floor_area*percentage_complied(
res_file_path, program.min_threshold)
data[sim_time.as_string()].append(compliant_area)
data[str(sim_time)].append(compliant_area)
compliant_areas.append(compliant_area)

average_compliant_area = mean(compliant_areas)
Expand All @@ -186,7 +186,7 @@ def prepare_results(program: Program, occupied_rooms: List[Room],
data['names'].append('<b>Total</b>')
data['areas'].append(f'<b>{total_area}</b>')
for sim_time in SIM_TIMES:
data[sim_time.as_string()].append('')
data[str(sim_time)].append('')
data['average'].append(f'<b>{total_average_area}</b>')

data_df = pd.DataFrame.from_dict(data)
Expand Down Expand Up @@ -230,17 +230,17 @@ def result(program: Program, occupied_rooms: List[Room],
st.plotly_chart(figure, use_container_width=True)

if compliant_area_percentage >= program.credit_2_threshold:
st.success(f'{compliant_area_percentage*100}% area complies with the'
' requirements. Therefore, 2 Credit points can be claimed.')
st.success(f'**{compliant_area_percentage*100}%** area complies with the'
' requirements. Therefore, **2 Credit points** can be claimed.')
additional_notes(program)
st.balloons()

elif compliant_area_percentage >= program.credit_1_threshold:
st.success(f'{compliant_area_percentage*100}% area complies with the'
' requirements. Therefore, 1 Credit point can be claimed.')
st.success(f'**{compliant_area_percentage*100}%** area complies with the'
' requirements. Therefore, **1 Credit point** can be claimed.')
additional_notes(program)
st.balloons()
else:
st.write(
f'Only {compliant_area_percentage*100}% area complies with the requirements.'
' Hence, no credit point can be claimed.')
f'Only **{compliant_area_percentage*100}%** area complies with the'
' requirements. Hence, no credit point can be claimed.')
2 changes: 1 addition & 1 deletion simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def create_job(hbjson_path: Path, api_client: ApiClient, owner: str, project: st
argument['sky'] = cie_sky(latitude, longitude, point.month,
point.day, point.hour, north_angle, ground_reflectance)
arguments.append(argument)
argument['month_day_hour'] = point.as_string()
argument['month_day_hour'] = str(point)

new_job.arguments = arguments

Expand Down
62 changes: 42 additions & 20 deletions visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
from typing import Dict, Tuple, Union
from pathlib import Path

from honeybee.model import Model as HBModel

from pollination_streamlit_viewer import viewer
from pollination_streamlit.api.client import ApiClient
from pollination_streamlit.interactors import Job
from pollination_streamlit_io import send_hbjson, send_geometry
from queenbee.job.job import JobStatusEnum

from estidama import SIM_TIMES
from helper import create_analytical_mesh


class SimStatus(Enum):
Expand Down Expand Up @@ -146,13 +150,15 @@ def generate_dicts(job: Job, target_folder: Path) -> Tuple[Dict[str, str],
return sim_dict, viz_dict, res_file_dict


def visualization(job_url: str,
def visualization(host: str, hbjson_path: Path, job_url: str,
api_client: ApiClient,
target_folder: Path) -> Tuple[Union[None, Dict[str, str]],
Union[None, Dict[str, Path]]]:
"""UI of visualization tab of the Estidama-daylight app.
args:
host: A string representing the environment the app is running inside.
hbjson_path: Path to the HBJSON file with grids.
job_url: Valid URL of a job on Pollination as a string.
api_client: ApiClient object containing Pollination credentials.
target_folder: Path to the target folder where outputs from the finished job
Expand Down Expand Up @@ -181,25 +187,41 @@ def visualization(job_url: str,
st.write('See how much daylight the occupied areas receive on selected points'
' in time during the year.')

col0, col1 = st.columns(2)

with col0:
for sim_time in SIM_TIMES[:3]:
id = sim_dict[sim_time.as_string()]
viz = viz_dict[id].joinpath('point_in_time.vtkjs')
st.write(
f'Daylight levels on {sim_time.description()} @ {sim_time.hour}:00')
viewer(key=f'{sim_time.as_string()}_viewer',
content=viz.read_bytes(), style={'height': '344px'})

with col1:
for sim_time in SIM_TIMES[3:]:
id = sim_dict[sim_time.as_string()]
viz = viz_dict[id].joinpath('point_in_time.vtkjs')
st.write(
f'Daylight levels on Summer {sim_time.description()} @ {sim_time.hour}:00')
viewer(key=f'{sim_time.as_string()}_viewer',
content=viz.read_bytes(), style={'height': '344px'})
if host.lower() == 'web':
col0, col1 = st.columns(2)

with col0:
for sim_time in SIM_TIMES[:3]:
id = sim_dict[str(sim_time)]
viz = viz_dict[id].joinpath('point_in_time.vtkjs')
st.write(f'{sim_time.description()} @ {sim_time.hour}:00')
viewer(key=f'{str(sim_time)}_viewer',
content=viz.read_bytes(), style={'height': '344px'})

with col1:
for sim_time in SIM_TIMES[3:]:
id = sim_dict[str(sim_time)]
viz = viz_dict[id].joinpath('point_in_time.vtkjs')
st.write(f'{sim_time.description()} @ {sim_time.hour}:00')
viewer(key=f'{str(sim_time)}_viewer',
content=viz.read_bytes(), style={'height': '344px'})

elif host.lower() == 'rhino' or host.lower() == 'sketchup':

options = {
f'{sim_time.description()} @ {sim_time.hour}:00': sim_time
for sim_time in SIM_TIMES}

option = st.radio(f'Select time', list(options.keys()))

sim_time = options[option]
id = sim_dict[str(sim_time)]
res_path = res_file_dict[id]

hb_model = HBModel.from_hbjson(hbjson_path)
send_hbjson(key='model-results', hbjson=hb_model.to_dict())
analytical_mesh = create_analytical_mesh(res_path, hb_model)
send_geometry(key=f'{str(sim_time)}_viz', geometry=analytical_mesh)

st.write('Go to the next tab to see the results.')

Expand Down

0 comments on commit 1c6dd4a

Please sign in to comment.