Source code for ladybug.ddy

# coding=utf-8
from __future__ import division

from .location import Location
from .designday import DesignDay
from .futil import write_to_file

import os
import re
import platform
import codecs


[docs]class DDY(object): """A DDY object containing all of the data of a .ddy file. Args: location: A Ladybug location object design_days: A list of the design days in the ddy file. Properties: * file_path * location * design_days """ __slots__ = ('_location', '_design_days', '_file_path') def __init__(self, location, design_days): assert isinstance(location, Location), 'Expected' \ ' Location type. Got {}'.format(type(location)) self._location = location self.design_days = design_days self._file_path = None
[docs] @classmethod def from_dict(cls, data): """Create a DDY from a dictionary. Args: data: A python dictionary in the following format .. code-block:: python { "type": "DDY", "location": {}, # ladybug Location schema "design_days": [] # list of ladybug DesignDay schemas } """ required_keys = ('location', 'design_days') for key in required_keys: assert key in data, 'Required key "{}" is missing!'.format(key) return cls(Location.from_dict(data['location']), [DesignDay.from_dict(des_day) for des_day in data['design_days']])
[docs] @classmethod def from_ddy_file(cls, file_path): """Initialize from a ddy file object from an existing ddy file. Args: file_path: A string representing a complete path to the .ddy file. """ # check that the file is there if not os.path.isfile(file_path): raise ValueError( 'Cannot find a .ddy file at {}'.format(file_path)) if not file_path.lower().endswith('.ddy'): raise ValueError( 'DDY file does not have a .ddy extension.') # check the python version and open the file if platform.python_implementation() == 'IronPython': ddywin = codecs.open(file_path, 'r') else: ddywin = codecs.open(file_path, 'r', encoding='utf-8', errors='ignore') # extract all location and design day definitions from the file loc_p = re.compile(r"(Site:Location,(.|\n)*?((;\s*!)|(;\s*\n)|(;\n)))") dday_p = re.compile(r"(SizingPeriod:DesignDay,(.|\n)*?((;\s*!)|(;\s*\n)|(;\n)))") try: ddytxt = ddywin.read() loc_matches = loc_p.findall(ddytxt) dday_matches = dday_p.findall(ddytxt) except Exception as e: # the file likely doesn't exist import traceback raise Exception('{}\n{}'.format(e, traceback.format_exc())) else: # check to be sure a location and a design day was found assert len(loc_matches) > 0, 'No location objects found in .ddy file.' assert len(dday_matches) > 0, 'No design day objects found in .ddy file.' # build design day and location objects location = Location.from_idf(loc_matches[0][0]) ddays = [DesignDay.from_idf(match[0], location) for match in dday_matches] finally: ddywin.close() cls_ = cls(location, ddays) cls_._file_path = os.path.normpath(file_path) return cls_
[docs] @classmethod def from_design_day(cls, design_day): """Initialize from a ddy file object from a ladybug design day object. Args: design_day: A Ladybug DesignDay object. """ return cls(design_day.location, [design_day])
[docs] def filter_by_keyword(self, keyword): """Return a list of ddys that have a certain keyword in their name. This is useful for selecting out design days from a ddy file that are for a specific type of condition (for example, .4% cooling design days) """ filtered_days = [] for des_day in self._design_days: if keyword in des_day.name: filtered_days.append(des_day) return filtered_days
@property def file_path(self): """Get the original .ddy file path. Will be None if the DDY did not originate from a file. """ return self._file_path @property def location(self): """Get or set the location.""" return self._location @location.setter def location(self, data): assert isinstance(data, Location), 'Expected' \ ' Ladybug Location. Got {}'.format(type(data)) self._location = data for dd in self._design_days: if dd.location != self._location: dd.location = self._location print('Updating location of {} to {}.'.format(dd, self._location)) @property def design_days(self): """Get or set the design_days.""" return tuple(self._design_days) @design_days.setter def design_days(self, data): try: if not isinstance(data, list): data = list(data) except TypeError: raise TypeError('Expected list or tuple for DDY design_days. ' 'Got {}'.format(type(data))) for item in data: assert isinstance(item, DesignDay), 'Expected' \ ' DesignDay type. Got {}'.format(type(item)) self._design_days = data for dd in self._design_days: if dd.location != self._location: dd.location = self._location print('Updating location of {} to {}.'.format(dd, self._location))
[docs] def to_dict(self): """Convert the Design Day to a dictionary.""" return { 'type': 'DDY', 'location': self.location.to_dict(), 'design_days': [des_d.to_dict() for des_d in self.design_days] }
[docs] def to_file_string(self): """Get a text string for the entirety of the DDY file contents.""" data = self.location.to_idf() + '\n\n' for d_day in self.design_days: data = data + d_day.to_idf() + '\n\n' return data
[docs] def write(self, file_path): """Write DDY object as a .ddy file and return the file path. Args: file_path: Text for the full path to where the .ddy file will be written. """ if not file_path.lower().endswith('.ddy'): file_path += '.ddy' file_data = self.to_file_string() write_to_file(file_path, file_data, True) return file_data
[docs] def save(self, file_path): """Write DDY object as a file. Args: file_path: Text for the full path to where the file will be written. """ file_data = self.to_file_string() write_to_file(file_path, file_data, True)
[docs] def duplicate(self): """Get a copy of this object.""" return self.__copy__()
[docs] def ToString(self): """Overwrite .NET ToString.""" return self.__repr__()
def __copy__(self): new_ddy = DDY(self._location, [dday.duplicate() for dday in self._design_days]) new_ddy._file_path = self._file_path return new_ddy def __key(self): """A tuple based on the object properties, useful for hashing.""" return (hash(self._location),) + tuple(hash(dday) for dday in self._design_days) def __hash__(self): return hash(self.__key()) def __eq__(self, other): return isinstance(other, DDY) and self.__key() == other.__key() def __ne__(self, other): return not self.__eq__(other) def __len__(self): return len(self._design_days) def __getitem__(self, key): return self._design_days[key] def __setitem__(self, key, value): assert isinstance(value, DesignDay), 'Expected' \ ' DesignDay type. Got {}'.format(type(value)) self._design_days[key] = value def __iter__(self): return iter(self._design_days) def __contains__(self, item): return item in self._design_days def __repr__(self): """DDY object representation.""" return "DDY File - {} [# days: {}]".format( self.location.city, str(len(self._design_days)))