Source code for dragonfly.cli.translate

"""dragonfly translation commands."""
import click
import sys
import os
import logging
import json

from ladybug.futil import preparedir
from ladybug.commandutil import process_content_to_output
from honeybee.units import parse_distance_string
from honeybee.config import folders as hb_folders
from honeybee.model import Model as HBModel

from dragonfly.model import Model

_logger = logging.getLogger(__name__)


@click.group(help='Commands for translating Dragonfly JSON files to honeybee.')
def translate():
    pass


@translate.command('model-to-honeybee')
@click.argument('model-file', type=click.Path(
    exists=True, file_okay=True, dir_okay=False, resolve_path=True))
@click.option('--obj-per-model', '-o', help='Text to describe how the input Model '
              'should be divided across the output Models. Choose from: District, '
              'Building, Story.', type=str, default="Building", show_default=True)
@click.option('--multiplier/--full-geometry', ' /-fg', help='Flag to note if the '
              'multipliers on each Building story should be passed along to the '
              'generated Honeybee Room objects or if full geometry objects should be '
              'written for each story in the building.', default=True, show_default=True)
@click.option('--no-plenum/--plenum', ' /-p', help='Flag to indicate whether '
              'ceiling/floor plenums should be auto-generated for the Rooms.',
              default=True, show_default=True)
@click.option('--no-cap/--cap', ' /-c', help='Flag to indicate whether context shade '
              'buildings should be capped with a top face.',
              default=True, show_default=True)
@click.option('--no-ceil-adjacency/--ceil-adjacency', ' /-a', help='Flag to indicate '
              'whether adjacencies should be solved between interior stories when '
              'Room2D floor and ceiling geometries are coplanar. This ensures '
              'that Surface boundary conditions are used instead of Adiabatic ones. '
              'Note that this input has no effect when the object-per-model is Story.',
              default=True, show_default=True)
@click.option('--shade-dist', '-sd', help='An optional number to note the distance '
              'beyond which other buildings shade should not be exported into a Model. '
              'This can include the units of the distance (eg. 100ft) or, if no units '
              'are  provided, the value will be interpreted in the dragonfly model '
              'units. If None, all other buildings will be included as context shade in '
              'each and every Model. Set to 0 to exclude all neighboring buildings '
              'from the resulting models.', type=str, default=None, show_default=True)
@click.option('--enforce-adj-check/--bypass-adj-check', ' /-bc', help='Flag to note '
              'whether an exception should be raised if an adjacency between two '
              'Room2Ds is invalid or if the check should be bypassed and the invalid '
              'Surface boundary condition should be replaced with an Outdoor boundary '
              'condition. If bypass is selected, any Walls containing WindowParameters '
              'and an illegal boundary condition will also be replaced with an '
              'Outdoor boundary condition.', default=True, show_default=True)
@click.option('--enforce-solid/--permit-non-solid', ' /-pns', help='Flag to note '
              'whether rooms should be translated as solid extrusions whenever '
              'translating them with custom roof geometry produces a non-solid '
              'result or the non-solid room geometry should be allowed to remain '
              'in the result. The latter is useful for understanding why a '
              'particular roof geometry has produced a non-solid result.',
              default=True, show_default=True)
@click.option('--folder', '-f', help='Folder on this computer, into which the HBJSON '
              'files will be written. By default, the files will be output '
              'to the honeybee default simulation folder and placed in a project '
              'folder with the same name as the model json.',
              default=None, show_default=True,
              type=click.Path(file_okay=False, dir_okay=True, resolve_path=True))
@click.option('--log-file', '-log', help='Optional log file to output a JSON array of '
              'dictionaries with information about each of the generated HBJSONs, '
              'including their file paths. By default the list will be printed out to '
              'stdout', type=click.File('w'), default='-', show_default=True)
def model_to_honeybee_cli(
        model_file, obj_per_model, multiplier, no_plenum, no_cap,
        no_ceil_adjacency, shade_dist, enforce_adj_check, enforce_solid,
        folder, log_file):
    """Translate a Dragonfly Model file into one or more Honeybee Models.

    \b
    Args:
        model_file: Full path to a Dragonfly Model JSON or Pkl file.
    """
    try:
        full_geometry = not multiplier
        plenum = not no_plenum
        cap = not no_cap
        ceil_adjacency = not no_ceil_adjacency
        bypass_adj_check = not enforce_adj_check
        permit_non_solid = not enforce_solid
        model_to_honeybee(
            model_file, obj_per_model, full_geometry, plenum, cap, ceil_adjacency,
            shade_dist, bypass_adj_check, permit_non_solid, folder, log_file)
    except Exception as e:
        _logger.exception('Model translation failed.\n{}'.format(e))
        sys.exit(1)
    else:
        sys.exit(0)


[docs] def model_to_honeybee( model_file, obj_per_model='Building', full_geometry=False, plenum=False, cap=False, ceil_adjacency=False, shade_dist=None, bypass_adj_check=False, permit_non_solid=False, folder=None, log_file=None, multiplier=True, no_plenum=True, no_cap=True, no_ceil_adjacency=True, enforce_adj_check=True, enforce_solid=True): """Translate a Dragonfly Model file into one or more Honeybee Models. Args: model_file: Full path to a Dragonfly Model JSON or Pkl file. full_geometry: Boolean to note if the multipliers on each Story should be passed along to the generated Honeybee Room objects or if full geometry objects should be written for each story in the building. (Default: False). plenum: Boolean to indicate whether ceiling/floor plenums should be auto-generated for the Rooms. (Default: False). cap: Boolean to indicate whether context shade buildings should be capped with a top face. (Default: False). ceil_adjacency: Boolean to indicate whether adjacencies should be solved between interior stories when Room2D floor and ceiling geometries are coplanar. This ensures that Surface boundary conditions are used instead of Adiabatic ones. (Default: False). shade_dist: An optional number to note the distance beyond which other buildings shade should not be exported into a Model. This can include the units of the distance (eg. 100ft) or, if no units are provided, the value will be interpreted in the dragonfly model units. If None, all other buildings will be included as context shade in each and every Model. Set to 0 to exclude all neighboring buildings from the resulting models. (Default: None). bypass_adj_check: Boolean to note whether an exception should be raised if an adjacency between two Room2Ds is invalid or if the check should be bypassed and the invalid Surface boundary condition should be replaced with an Outdoor boundary condition. If bypass is selected, any Walls containing WindowParameters and an illegal boundary condition will also be replaced with an Outdoor boundary condition. (Default: False). permit_non_solid: Boolean to note whether rooms should be translated as solid extrusions whenever translating them with custom roof geometry produces a non-solid result or the non-solid room geometry should be allowed to remain in the result. The latter is useful for understanding why a particular roof geometry has produced a non-solid result. (Default: False). folder: Folder on this computer, into which the HBJSON files will be written. If None, the files will be output to the honeybee default simulation folder and placed in a project folder with the same name as the model json. (Default: None). log_file: Optional log file to output a JSON array of dictionaries with information about each of the generated HBJSONs, including their file paths. If None, the string will be returned from this method. """ # set the default folder to the default if it's not specified if folder is None: proj_name = \ os.path.basename(model_file).replace('.json', '').replace('.dfjson', '') folder = os.path.join( hb_folders.default_simulation_folder, proj_name, 'honeybee') preparedir(folder, remove_content=False) # re-serialize the Dragonfly Model and convert Dragonfly Model to Honeybee model = Model.from_file(model_file) if shade_dist is not None: shade_dist = parse_distance_string(shade_dist, model.units) multiplier = not full_geometry enforce_adj_check = not bypass_adj_check enforce_solid = not permit_non_solid hb_models = model.to_honeybee( obj_per_model, shade_dist, multiplier, plenum, cap, ceil_adjacency, enforce_adj=enforce_adj_check, enforce_solid=enforce_solid) # write out the honeybee JSONs and collect the info about them hb_jsons = [] for hb_model in hb_models: model_dict = hb_model.to_dict(triangulate_sub_faces=True) file_name = '{}.hbjson'.format(hb_model.identifier) file_path = os.path.join(folder, file_name) with open(file_path, 'w') as fp: json.dump(model_dict, fp) hb_info = { 'id': hb_model.identifier, 'path': file_name, 'full_path': os.path.abspath(file_path) } hb_jsons.append(hb_info) if log_file is None: return json.dumps(hb_jsons, indent=4) log_file.write(json.dumps(hb_jsons, indent=4))
@translate.command('model-to-honeybee-file') @click.argument('model-file', type=click.Path( exists=True, file_okay=True, dir_okay=False, resolve_path=True)) @click.option( '--multiplier/--full-geometry', ' /-fg', help='Flag to note if the ' 'multipliers on each Building story should be passed along to the ' 'generated Honeybee Room objects or if full geometry objects should be ' 'written for each story in the building.', default=True, show_default=True) @click.option( '--no-plenum/--plenum', ' /-p', help='Flag to indicate whether ' 'ceiling/floor plenums should be auto-generated for the Rooms.', default=True, show_default=True) @click.option( '--no-ceil-adjacency/--ceil-adjacency', ' /-a', help='Flag to indicate ' 'whether adjacencies should be solved between interior stories when ' 'Room2D floor and ceiling geometries are coplanar. This ensures ' 'that Surface boundary conditions are used instead of Adiabatic ones.', default=True, show_default=True) @click.option( '--enforce-adj-check/--bypass-adj-check', ' /-bc', help='Flag to note ' 'whether an exception should be raised if an adjacency between two ' 'Room2Ds is invalid or if the check should be bypassed and the invalid ' 'Surface boundary condition should be replaced with an Outdoor boundary ' 'condition. If bypass is selected, any Walls containing WindowParameters ' 'and an illegal boundary condition will also be replaced with an ' 'Outdoor boundary condition.', default=True, show_default=True) @click.option( '--enforce-solid/--permit-non-solid', ' /-pns', help='Flag to note ' 'whether rooms should be translated as solid extrusions whenever ' 'translating them with custom roof geometry produces a non-solid ' 'result or the non-solid room geometry should be allowed to remain ' 'in the result. The latter is useful for understanding why a ' 'particular roof geometry has produced a non-solid result.', default=True, show_default=True) @click.option( '--output-file', '-f', help='Optional file to output the Honeybee Model JSON string' ' with solved adjacency. By default it will be printed out to stdout', type=click.File('w'), default='-') def model_to_honeybee_file_cli( model_file, multiplier, no_plenum, no_ceil_adjacency, enforce_adj_check, enforce_solid, output_file): """Translate a Dragonfly Model into a single Honeybee Model. \b Args: model_file: Full path to a Dragonfly Model JSON or Pkl file. """ try: full_geometry = not multiplier plenum = not no_plenum ceil_adjacency = not no_ceil_adjacency bypass_adj_check = not enforce_adj_check permit_non_solid = not enforce_solid model_to_honeybee_file( model_file, full_geometry, plenum, ceil_adjacency, bypass_adj_check, permit_non_solid, output_file) except Exception as e: _logger.exception('Model translation failed.\n{}'.format(e)) sys.exit(1) else: sys.exit(0)
[docs] def model_to_honeybee_file( model_file, full_geometry=False, plenum=False, ceil_adjacency=False, bypass_adj_check=False, permit_non_solid=False, output_file=None, multiplier=True, no_plenum=True, no_ceil_adjacency=True, enforce_adj_check=True, enforce_solid=True): """Translate a Dragonfly Model into a single Honeybee Model. Args: model_file: Full path to a Dragonfly Model JSON or Pkl file. full_geometry: Boolean to note if the multipliers on each Story should be passed along to the generated Honeybee Room objects or if full geometry objects should be written for each story in the building. (Default: False). plenum: Boolean to indicate whether ceiling/floor plenums should be auto-generated for the Rooms. (Default: False). ceil_adjacency: Boolean to indicate whether adjacencies should be solved between interior stories when Room2D floor and ceiling geometries are coplanar. This ensures that Surface boundary conditions are used instead of Adiabatic ones. (Default: False). bypass_adj_check: Boolean to note whether an exception should be raised if an adjacency between two Room2Ds is invalid or if the check should be bypassed and the invalid Surface boundary condition should be replaced with an Outdoor boundary condition. If bypass is selected, any Walls containing WindowParameters and an illegal boundary condition will also be replaced with an Outdoor boundary condition. (Default: False). permit_non_solid: Boolean to note whether rooms should be translated as solid extrusions whenever translating them with custom roof geometry produces a non-solid result or the non-solid room geometry should be allowed to remain in the result. The latter is useful for understanding why a particular roof geometry has produced a non-solid result. (Default: False). output_file: Optional file to output the string of the HBJSON. If None, the string will simply be returned from this method. """ # serialize the Model parsed_model = Model.from_file(model_file) # convert the dragonfly Model to Honeybee multiplier = not full_geometry enforce_adj_check = not bypass_adj_check enforce_solid = not permit_non_solid hb_model = parsed_model.to_honeybee( object_per_model='District', use_multiplier=multiplier, add_plenum=plenum, solve_ceiling_adjacencies=ceil_adjacency, enforce_adj=enforce_adj_check, enforce_solid=enforce_solid)[0] # write the new model out to the file or stdout model_str = json.dumps(hb_model.to_dict()) return process_content_to_output(model_str, output_file)
@translate.command('merge-models-to-honeybee') @click.argument('base-model', type=click.Path( exists=True, file_okay=True, dir_okay=False, resolve_path=True)) @click.option( '--dragonfly-model', '-d', help='Other Dragonfly Model to be merged into ' 'the base model.', type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True), multiple=True) @click.option( '--honeybee-model', '-h', help='Other Honeybee Model to be merged into ' 'the base model.', type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True), multiple=True) @click.option( '--multiplier/--full-geometry', ' /-fg', help='Flag to note if the ' 'multipliers on each Building story should be passed along to the ' 'generated Honeybee Room objects or if full geometry objects should be ' 'written for each story in the building.', default=True, show_default=True) @click.option( '--no-plenum/--plenum', ' /-p', help='Flag to indicate whether ' 'ceiling/floor plenums should be auto-generated for the Rooms.', default=True, show_default=True) @click.option( '--default-adjacency/--solve-adjacency', ' /-sa', help='Flag to indicate ' 'whether all boundary conditions of the original models should be left as they ' 'are or whether adjacencies should be solved across the final model when ' 'everything is merged together. In this case, solving adjacencies will involve ' 'merging all coplanar faces across the Dragonfly/Honeybee Models, intersecting ' 'coplanar Faces to get matching areas, and setting Surface boundary conditions ' 'for all matching coplanar faces.', default=True, show_default=True) @click.option( '--enforce-adj-check/--bypass-adj-check', ' /-bc', help='Flag to note ' 'whether an exception should be raised if an adjacency between two ' 'Room2Ds is invalid or if the check should be bypassed and the invalid ' 'Surface boundary condition should be replaced with an Outdoor boundary ' 'condition. If bypass is selected, any Walls containing WindowParameters ' 'and an illegal boundary condition will also be replaced with an ' 'Outdoor boundary condition.', default=True, show_default=True) @click.option( '--enforce-solid/--permit-non-solid', ' /-pns', help='Flag to note ' 'whether rooms should be translated as solid extrusions whenever ' 'translating them with custom roof geometry produces a non-solid ' 'result or the non-solid room geometry should be allowed to remain ' 'in the result. The latter is useful for understanding why a ' 'particular roof geometry has produced a non-solid result.', default=True, show_default=True) @click.option( '--output-file', '-f', help='Optional file to output the Honeybee Model JSON string' ' with solved adjacency. By default it will be printed out to stdout', type=click.File('w'), default='-') def merge_models_to_honeybee_cli( base_model, dragonfly_model, honeybee_model, multiplier, no_plenum, default_adjacency, enforce_adj_check, enforce_solid, output_file): """Merge multiple Dragonfly and/or Honeybee Models into a single Honeybee Model. \b Args: base_model: Full path to a Dragonfly Model JSON or Pkl file that serves as the base into which the other model(s) will be merged. """ try: full_geometry = not multiplier plenum = not no_plenum solve_adjacency = not default_adjacency bypass_adj_check = not enforce_adj_check permit_non_solid = not enforce_solid merge_models_to_honeybee( base_model, dragonfly_model, honeybee_model, full_geometry, plenum, solve_adjacency, bypass_adj_check, permit_non_solid, output_file) except Exception as e: _logger.exception('Model merging failed.\n{}'.format(e)) sys.exit(1) else: sys.exit(0)
[docs] def merge_models_to_honeybee( base_model, dragonfly_model=(), honeybee_model=(), full_geometry=False, plenum=False, solve_adjacency=False, bypass_adj_check=False, permit_non_solid=False, output_file=None, multiplier=True, no_plenum=True, default_adjacency=True, enforce_adj_check=True, enforce_solid=True): """Merge multiple Dragonfly and/or Honeybee Models into a single Honeybee Model. Args: base_model: Full path to a Dragonfly Model JSON or Pkl file that serves as the base into which the other model(s) will be merged. dragonfly_model: List of other Dragonfly Models to be merged into the base model. honeybee_model: List of other Honeybee Models to be merged into the base model. full_geometry: Boolean to note if the multipliers on each Story should be passed along to the generated Honeybee Room objects or if full geometry objects should be written for each story in the building. (Default: False). plenum: Boolean to indicate whether ceiling/floor plenums should be auto-generated for the Rooms. (Default: False). solve_adjacency: Boolean to indicate whether all boundary conditions of the original models should be left as they are or whether adjacencies should be solved across the final model when everything is merged together. In this case, solving adjacencies will involve merging all coplanar faces across the Dragonfly/Honeybee Models, intersecting coplanar Faces to get matching areas, and setting Surface boundary conditions for all matching coplanar faces. (Default: False). bypass_adj_check: Boolean to note whether an exception should be raised if an adjacency between two Room2Ds is invalid or if the check should be bypassed and the invalid Surface boundary condition should be replaced with an Outdoor boundary condition. If bypass is selected, any Walls containing WindowParameters and an illegal boundary condition will also be replaced with an Outdoor boundary condition. (Default: False). permit_non_solid: Boolean to note whether rooms should be translated as solid extrusions whenever translating them with custom roof geometry produces a non-solid result or the non-solid room geometry should be allowed to remain in the result. The latter is useful for understanding why a particular roof geometry has produced a non-solid result. (Default: False). output_file: Optional file to output the string of the HBJSON. If None, the string will simply be returned from this method. """ # serialize the Model and convert the units parsed_model = Model.from_file(base_model) other_df_models = [Model.from_file(m) for m in dragonfly_model] for o_model in other_df_models: parsed_model.add_model(o_model) tol = parsed_model.tolerance multiplier = not full_geometry enforce_adj_check = not bypass_adj_check enforce_solid = not permit_non_solid # if solve dragonfly wall adjacencies if requested if solve_adjacency: for bldg in parsed_model.buildings: for story in bldg.unique_stories: story.remove_room_2d_colinear_vertices(tol) story.intersect_room_2d_adjacency(tol) for bldg in parsed_model.buildings: for story in bldg.unique_stories: story.solve_room_2d_adjacency(tol, resolve_window_conflicts=True) # convert the dragonfly Model to Honeybee hb_model = parsed_model.to_honeybee( object_per_model='District', use_multiplier=multiplier, add_plenum=plenum, solve_ceiling_adjacencies=solve_adjacency, enforce_adj=enforce_adj_check, enforce_solid=enforce_solid)[0] # merge the honeybee models other_hb_models = [HBModel.from_file(m) for m in honeybee_model] for o_model in other_hb_models: if solve_adjacency: for room in o_model.rooms: room.merge_coplanar_faces(o_model.tolerance, o_model.angle_tolerance) hb_model.add_model(o_model) # perform a final solve adjacency if requested if solve_adjacency and len(other_hb_models) != 0: hb_model.solve_adjacency(intersect=True) # write the new model out to the file or stdout model_str = json.dumps(hb_model.to_dict()) return process_content_to_output(model_str, output_file)