Source code for honeybee.cli.validate

"""honeybee validation commands."""
import sys
import logging
import json as py_json
import click

from honeybee.model import Model
from honeybee.config import folders

_logger = logging.getLogger(__name__)


@click.group(help='Commands for validating Honeybee objects.')
def validate():
    pass


@validate.command('model')
@click.argument('model-file', type=click.Path(
    exists=True, file_okay=True, dir_okay=False, resolve_path=True))
@click.option(
    '--check-all/--room-overlaps', ' /-ro', help='Flag to note whether the output '
    'validation report should validate all possible issues with the model or only '
    'the Room collisions should be checked.', default=True, show_default=True)
@click.option(
    '--plain-text/--json', ' /-j', help='Flag to note whether the output validation '
    'report should be formatted as a JSON object instead of plain text. If set to JSON, '
    'the output object will contain several attributes. The "honeybee_core" and '
    '"honeybee_schema" attributes will note the versions of these libraries used in '
    'the validation process. An attribute called "fatal_error" is a text string '
    'containing an exception if the Model failed to serialize and will be an empty '
    'string if serialization was successful. An attribute called "errors" will '
    'contain a list of JSON objects for each invalid issue found in the model. A '
    'boolean attribute called "valid" will note whether the Model is valid or not.',
    default=True, show_default=True)
@click.option(
    '--output-file', '-f', help='Optional file to output the full report '
    'of the validation. By default it will be printed out to stdout',
    type=click.File('w'), default='-')
def validate_model_cli(model_file, check_all, plain_text, output_file):
    """Validate all properties of a Model file against Honeybee schema.

    This includes checking basic compliance with the 5 rules of honeybee geometry
    as well as checks for all extension attributes. The 5 rules of honeybee geometry
    are as follows.

    1. All Face3Ds must be planar to within the model tolerance.

    2. All Face3Ds must NOT be self-intersecting (like a bowtie shape)

    3. All children sub-faces (Apertures and Doors) must be co-planar with
        their parent Face and lie completely within its boundary.

    4. All adjacent object pairs (faces and sub-faces with a Surface boundary
        condition) must have matching areas.

    5. All Room volumes must be closed solids.

    \b
    Args:
        model_file: Full path to a Model JSON file.
    """
    try:
        json = not plain_text
        room_overlaps = not check_all
        validate_model(model_file, room_overlaps, json, output_file)
    except Exception as e:
        _logger.exception('Model validation failed.\n{}'.format(e))
        sys.exit(1)
    else:
        sys.exit(0)


[docs] def validate_model(model_file, room_overlaps=False, json=False, output_file=None, check_all=True, plain_text=True): """Validate all properties of a Model file against the Honeybee schema. This includes checking basic compliance with the 5 rules of honeybee geometry as well as checks for all extension attributes. Args: model_file: Full path to a Honeybee Model file. room_overlaps: Boolean to note whether the output validation report should only validate the Room collisions (True) or all possible issues with the model should be checked (False). (Default: False). json: Boolean to note whether the output validation report should be formatted as a JSON object instead of plain text. output_file: Optional file to output the string of the visualization file contents. If None, the string will simply be returned from this method. """ if not json: # re-serialize the Model to make sure no errors are found c_ver = folders.honeybee_core_version_str s_ver = folders.honeybee_schema_version_str ver_msg = 'Validating Model using honeybee-core=={} and ' \ 'honeybee-schema=={}'.format(c_ver, s_ver) print(ver_msg) parsed_model = Model.from_file(model_file) print('Re-serialization passed.') # perform several other checks for geometry rules and others if not room_overlaps: report = parsed_model.check_all(raise_exception=False, detailed=False) else: report = parsed_model.check_room_volume_collisions(raise_exception=False) print('Model checks completed.') # check the report and write the summary of errors if report == '': full_msg = ver_msg + '\nCongratulations! Your Model is valid!' else: full_msg = ver_msg + \ '\nYour Model is invalid for the following reasons:\n' + report if output_file is None: return full_msg else: output_file.write(full_msg) else: out_dict = { 'type': 'ValidationReport', 'app_name': 'Honeybee', 'app_version': folders.honeybee_core_version_str, 'schema_version': folders.honeybee_schema_version_str } try: parsed_model = Model.from_file(model_file) out_dict['fatal_error'] = '' if not room_overlaps: errors = parsed_model.check_all(raise_exception=False, detailed=True) else: errors = parsed_model.check_room_volume_collisions( raise_exception=False, detailed=True) out_dict['errors'] = errors out_dict['valid'] = True if len(out_dict['errors']) == 0 else False except Exception as e: out_dict['fatal_error'] = str(e) out_dict['errors'] = [] out_dict['valid'] = False if output_file is None: return py_json.dumps(out_dict, indent=4) else: output_file.write(py_json.dumps(out_dict, indent=4))
@validate.command('room-volumes') @click.argument('model-file', type=click.Path( exists=True, file_okay=True, dir_okay=False, resolve_path=True)) @click.option( '--output-file', '-f', help='Optional file to output the JSON strings of ' 'ladybug_geometry LineSegment3Ds that represent naked and non-manifold edges. ' 'By default it will be printed out to stdout', type=click.File('w'), default='-') def validate_room_volumes_cli(model_file, output_file): """Get a list of all naked and non-manifold edges preventing closed room volumes. This is helpful for visually identifying issues in geometry that are preventing the room volume from reading as closed. \b Args: model_file: Full path to a Honeybee Model file. """ try: validate_room_volumes(model_file, output_file) except Exception as e: _logger.exception('Model room volume validation failed.\n{}'.format(e)) sys.exit(1) else: sys.exit(0)
[docs] def validate_room_volumes(model_file, output_file=None): """Get a list of all naked and non-manifold edges preventing closed room volumes. This is helpful for visually identifying issues in geometry that are preventing the room volume from reading as closed. Args: model_file: Full path to a Honeybee Model file. output_file: Optional file to output the string of the visualization file contents. If None, the string will simply be returned from this method. """ # re-serialize the Model and collect all naked and non-manifold edges parsed_model = Model.from_file(model_file) problem_edges = [] for room in parsed_model.rooms: if not room.geometry.is_solid: problem_edges.extend(room.geometry.naked_edges) problem_edges.extend(room.geometry.non_manifold_edges) # write the new model out to the file or stdout prob_array = [lin.to_dict() for lin in problem_edges] if output_file is None: return py_json.dumps(prob_array) else: output_file.write(py_json.dumps(prob_array))