# coding=utf-8
"""Settings for the EnergyPlus Shadow Calculation."""
from __future__ import division
from ..reader import parse_idf_string
from ..writer import generate_idf_string
from honeybee.typing import valid_string, int_in_range
[docs]
class ShadowCalculation(object):
"""Settings for the EnergyPlus Shadow Calculation.
Args:
solar_distribution: Text desribing how EnergyPlus should treat beam solar
radiation and reflectances from surfaces that strike the building
surfaces. (Default: FullExteriorWithReflections). Choose from the following:
* MinimalShadowing
* FullExterior
* FullInteriorAndExterior
* FullExteriorWithReflections
* FullInteriorAndExteriorWithReflections
calculation_method: Text noting whether CPU-based polygon clipping method or
GPU-based pixel counting method should be used. For low numbers of shading
surfaces (less than ~200), PolygonClipping requires less runtime than
PixelCounting. However, PixelCounting runtime scales significantly
better at higher numbers of shading surfaces. PixelCounting also has
no limitations related to zone concavity when used with any
“FullInterior” solar distribution options. (Default: PolygonClipping).
Choose from the following:
* PolygonClipping
* PixelCounting
calculation_update_method: Text describing how often the solar and shading
calculations are updated with respect to the flow of time in the
simulation. (Default: Periodic). Choose from the following:
* Periodic
* Timestep
calculation_frequency: Integer for the number of days in each period for
which a unique shadow calculation will be performed. This field is only
used if the Periodic method is used in the previous field. (Default: 30).
maximum_figures: Integer for the number of figures used in shadow overlaps.
(Default: 15000).
Properties:
* solar_distribution
* calculation_method
* calculation_update_method
* calculation_frequency
* maximum_figures
"""
__slots__ = (
'_solar_distribution', '_calculation_method', '_calculation_update_method',
'_calculation_frequency', '_maximum_figures')
SOLAR_DISTRIBUTIONS = (
'MinimalShadowing', 'FullExterior', 'FullInteriorAndExterior',
'FullExteriorWithReflections', 'FullInteriorAndExteriorWithReflections')
CALCULATION_METHODS = ('PolygonClipping', 'PixelCounting')
CALCULATION_UPDATE_METHODS = ('Periodic', 'Timestep')
def __init__(self, solar_distribution='FullExteriorWithReflections',
calculation_method='PolygonClipping',
calculation_update_method='Periodic',
calculation_frequency=30, maximum_figures=15000):
"""Initialize ShadowCalculation."""
self.solar_distribution = solar_distribution
self.calculation_method = calculation_method
self.calculation_update_method = calculation_update_method
self.calculation_frequency = calculation_frequency
self.maximum_figures = maximum_figures
@property
def solar_distribution(self):
"""Get or set text for how solar reflectances from surfaces should be treated.
Choose from the options below:
* MinimalShadowing
* FullExterior
* FullInteriorAndExterior
* FullExteriorWithReflections
* FullInteriorAndExteriorWithReflections
"""
return self._solar_distribution
@solar_distribution.setter
def solar_distribution(self, value):
clean_input = valid_string(value).lower()
for key in self.SOLAR_DISTRIBUTIONS:
if key.lower() == clean_input:
value = key
break
else:
raise ValueError(
'solar_distribution {} is not recognized.\nChoose from the '
'following:\n{}'.format(value, self.SOLAR_DISTRIBUTIONS))
self._solar_distribution = value
@property
def calculation_method(self):
"""Get or set text for whether CPU-based or GPU-based methods should be used.
Choose from the options below:
* PolygonClipping
* PixelCounting
"""
return self._calculation_method
@calculation_method.setter
def calculation_method(self, value):
clean_input = valid_string(value).lower()
for key in self.CALCULATION_METHODS:
if key.lower() == clean_input:
value = key
break
else:
raise ValueError(
'calculation_method "{}" is not recognized.\nChoose from the '
'following:\n{}'.format(value, self.CALCULATION_METHODS))
self._calculation_method = value
@property
def calculation_update_method(self):
"""Get or set text for how often the solar and shading calculations are updated.
Choose from the options below:
* Periodic
* Timestep
"""
return self._calculation_update_method
@calculation_update_method.setter
def calculation_update_method(self, value):
clean_input = valid_string(value).lower()
for key in self.CALCULATION_UPDATE_METHODS:
if key.lower() == clean_input:
value = key
break
else:
raise ValueError(
'calculation_update_method "{}" is not recognized.\nChoose from the '
'following:\n{}'.format(value, self.CALCULATION_UPDATE_METHODS))
self._calculation_update_method = value
@property
def calculation_frequency(self):
"""Get or set a integer for the number of days with unique shadow calculations.
"""
return self._calculation_frequency
@calculation_frequency.setter
def calculation_frequency(self, value):
self._calculation_frequency = int_in_range(
value, 1, input_name='shadow calculation calculation frequency')
@property
def maximum_figures(self):
"""Get or set a integer for the number of figures used in shadow overlaps."""
return self._maximum_figures
@maximum_figures.setter
def maximum_figures(self, value):
self._maximum_figures = int_in_range(
value, 200, input_name='shadow calculation maximum figures')
[docs]
@classmethod
def from_idf(cls, idf_string, solar_distribution='FullExteriorWithReflections'):
"""Create a ShadowCalculation object from an EnergyPlus IDF text string.
Args:
idf_string: A text string fully describing an EnergyPlus
ShadowCalculation definition.
solar_distribution: Text desribing how EnergyPlus should treat beam
solar radiation and reflectances from surfaces that strike the
building surfaces.
"""
# check the inputs
ep_strs = parse_idf_string(idf_string, 'ShadowCalculation,')
# extract the properties from the string
calc_method = 'PolygonClipping'
update_method = 'Periodic'
calculation_frequency = 20
maximum_figures = 15000
try:
calc_method = ep_strs[0] if ep_strs[0] != '' else update_method
update_method = ep_strs[1] if ep_strs[1] != '' else update_method
calculation_frequency = ep_strs[2] if ep_strs[2] != '' else 20
maximum_figures = ep_strs[3] if ep_strs[3] != '' else 15000
except IndexError:
pass # shorter ShadowCalculation definition
return cls(solar_distribution, calc_method, update_method,
calculation_frequency, maximum_figures)
[docs]
@classmethod
def from_dict(cls, data):
"""Create a ShadowCalculation object from a dictionary.
Args:
data: A ShadowCalculation dictionary in following the format below.
.. code-block:: python
{
"type": "ShadowCalculation",
"solar_distribution": 'FullInteriorAndExteriorWithReflections',
"calculation_method": 'PolygonClipping',
"calculation_update_method": 'Periodic',
"calculation_frequency": 30,
"maximum_figures": 15000
}
"""
assert data['type'] == 'ShadowCalculation', \
'Expected ShadowCalculation dictionary. Got {}.'.format(data['type'])
solar_distribution = data['solar_distribution'] if \
'solar_distribution' in data else 'FullExteriorWithReflections'
calculation_method = data['calculation_method'] if \
'calculation_method' in data else 'PolygonClipping'
update_method = data['calculation_update_method'] if \
'calculation_update_method' in data else 'Periodic'
calculation_frequency = data['calculation_frequency'] if \
'calculation_frequency' in data else 30
maximum_figures = data['maximum_figures'] if \
'maximum_figures' in data else 15000
return cls(solar_distribution, calculation_method, update_method,
calculation_frequency, maximum_figures)
[docs]
def to_idf(self):
"""Get an EnergyPlus string representation of the ShadowCalculation.
.. code-block:: shell
ShadowCalculation,
PolygonClipping, !- calculation method
Periodic, !- calculation update method
30, !- calculation frequency
15000; !- maximum figures
"""
values = (self.calculation_method, self.calculation_update_method,
self.calculation_frequency, self.maximum_figures)
comments = ('calculation method', 'calculation update method',
'calculation frequency', 'maximum figures')
return generate_idf_string('ShadowCalculation', values, comments)
[docs]
def to_dict(self):
"""ShadowCalculation dictionary representation."""
return {
'type': 'ShadowCalculation',
'solar_distribution': self.solar_distribution,
'calculation_method': self.calculation_method,
'calculation_update_method': self.calculation_update_method,
'calculation_frequency': self.calculation_frequency,
'maximum_figures': self.maximum_figures
}
[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):
return ShadowCalculation(
self.solar_distribution, self.calculation_method,
self.calculation_update_method, self.calculation_frequency,
self.maximum_figures)
def __key(self):
"""A tuple based on the object properties, useful for hashing."""
return (self.solar_distribution, self.calculation_method,
self.calculation_update_method, self.calculation_frequency,
self.maximum_figures)
def __hash__(self):
return hash(self.__key())
def __eq__(self, other):
return isinstance(other, ShadowCalculation) and self.__key() == other.__key()
def __ne__(self, other):
return not self.__eq__(other)
def __repr__(self):
return self.to_idf()