Skip to content

Commit

Permalink
feat(datatype): Add Degree-Days and Degree-Hours
Browse files Browse the repository at this point in the history
I also really started regretting that the data type for handling fractional values was written as 'percentage' instead of 'fraction.'  It seems that I was narrow-mindedly thinking of relative humidity in EPW files when I set up that type.  So I am fixing it here before we start having more dependencies on it.  Right now, it shouldn't really break anything outdside of the API.
  • Loading branch information
Chris Mackey committed Mar 7, 2019
1 parent a50f91e commit 8a5c5d9
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 59 deletions.
86 changes: 43 additions & 43 deletions ladybug/datatype/percentage.py → ladybug/datatype/fraction.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,44 @@
# coding=utf-8
"""Percentage data type."""
"""Fraction data type."""
from __future__ import division

from .base import DataTypeBase


class Percentage(DataTypeBase):
"""Percentage"""
_units = ('%', 'fraction', 'tenths', 'thousandths', 'okta')
_si_units = ('%', 'fraction', 'tenths', 'thousandths', 'okta')
_ip_units = ('%', 'fraction', 'tenths', 'thousandths', 'okta')
class Fraction(DataTypeBase):
"""Fraction"""
_units = ('fraction', '%', 'tenths', 'thousandths', 'okta')
_si_units = ('fraction', '%', 'tenths', 'thousandths', 'okta')
_ip_units = ('fraction', '%', 'tenths', 'thousandths', 'okta')
_abbreviation = 'Pct'

def _pct_to_fraction(self, value):
return value / 100.

def _pct_to_tenths(self, value):
return value / 10.
def _fraction_to_pct(self, value):
return value * 100.

def _pct_to_thousandths(self, value):
def _fraction_to_tenths(self, value):
return value * 10.

def _pct_to_okta(self, value):
return value / 12.5
def _fraction_to_thousandths(self, value):
return value * 1000.

def _fraction_to_pct(self, value):
return value * 100.
def _fraction_to_okta(self, value):
return value * 12.5

def _tenths_to_pct(self, value):
return value * 10.
def _pct_to_fraction(self, value):
return value / 100.

def _thousandths_to_pct(self, value):
def _tenths_to_fraction(self, value):
return value / 10.

def _okta_to_pct(self, value):
return value * 12.5
def _thousandths_to_fraction(self, value):
return value / 1000.

def _okta_to_fraction(self, value):
return value / 12.5

def to_unit(self, values, unit, from_unit):
"""Return values converted to the unit given the input from_unit."""
return self._to_unit_base('%', values, unit, from_unit)
return self._to_unit_base('fraction', values, unit, from_unit)

def to_ip(self, values, from_unit):
"""Return values in IP and the units to which the values have been converted."""
Expand All @@ -49,72 +49,72 @@ def to_si(self, values, from_unit):
return values, from_unit

@property
def isPercentage(self):
def isFraction(self):
"""Return True."""
return True


class PercentagePeopleDissatisfied(Percentage):
class PercentagePeopleDissatisfied(Fraction):
_min = 0
_max = 100
_max = 1
_abbreviation = 'PPD'


class RelativeHumidity(Percentage):
class RelativeHumidity(Fraction):
_min = 0
_abbreviation = 'RH'
_min_epw = 0
_max_epw = 110
_max_epw = 1.1
_missing_epw = 999


class HumidityRatio(Percentage):
class HumidityRatio(Fraction):
_min = 0
_max = 100
_max = 1
_abbreviation = 'HR'


class TotalSkyCover(Percentage):
class TotalSkyCover(Fraction):
# (used if Horizontal IR Intensity missing)
_min = 0
_max = 100
_max = 1
_abbreviation = 'CC'
_min_epw = 0
_max_epw = 100
_max_epw = 1
_missing_epw = 99


class OpaqueSkyCover(Percentage):
class OpaqueSkyCover(Fraction):
# (used if Horizontal IR Intensity missing)
_min = 0
_max = 100
_max = 1
_abbreviation = 'OSC'
_min_epw = 0
_max_epw = 100
_max_epw = 1
_missing_epw = 99


class AerosolOpticalDepth(Percentage):
class AerosolOpticalDepth(Fraction):
_min = 0
_max = 100
_max = 1
_abbreviation = 'AOD'
_min_epw = 0
_max_epw = 100
_max_epw = 1
_missing_epw = 0.999


class Albedo(Percentage):
class Albedo(Fraction):
_min = 0
_max = 100
_max = 1
_abbreviation = 'a'
_min_epw = 0
_max_epw = 100
_max_epw = 1
_missing_epw = 0.999


class LiquidPrecipitationQuantity(Percentage):
class LiquidPrecipitationQuantity(Fraction):
_min = 0
_abbreviation = 'LPQ'
_min_epw = 0
_max_epw = 100
_max_epw = 1
_missing_epw = 99
66 changes: 66 additions & 0 deletions ladybug/datatype/temperaturetime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# coding=utf-8
"""Temperature-Time data type."""
from __future__ import division

from .base import DataTypeBase


class TemperatureTime(DataTypeBase):
"""Temperature-Time"""
_units = ('degC-days', 'degF-days', 'degC-hours', 'degF-hours')
_si_units = ('degC-days', 'degC-hours')
_ip_units = ('degF-days', 'degF-hours')
_abbreviation = 'degTime'

def _degCdays_to_degFdays(self, value):
return value * 9. / 5.

def _degCdays_to_degChours(self, value):
return value * 24.

def _degCdays_to_degFhours(self, value):
return value * 24. * 9. / 5.

def _degFdays_to_degCdays(self, value):
return value * 5. / 9.

def _degChours_to_degCdays(self, value):
return value / 24.

def _degFhours_to_degCdays(self, value):
return (value / 24.) * 5. / 9.

def to_unit(self, values, unit, from_unit):
"""Return values converted to the unit given the input from_unit."""
return self._to_unit_base('degC-days', values, unit, from_unit)

def to_ip(self, values, from_unit):
"""Return values in IP and the units to which the values have been converted."""
if from_unit in self._ip_units:
return values, from_unit
elif from_unit == 'degC-hours':
return self.to_unit(values, 'degF-hours', from_unit), 'degF-hours'
else:
return self.to_unit(values, 'degF-days', from_unit), 'degF-days'

def to_si(self, values, from_unit):
"""Return values in SI and the units to which the values have been converted."""
if from_unit in self._si_units:
return values, from_unit
elif from_unit == 'degF-hours':
return self.to_unit(values, 'degC-hours', from_unit), 'degC-hours'
else:
return self.to_unit(values, 'degC-days', from_unit), 'degC-days'

@property
def isTemperatureTime(self):
"""Return True."""
return True


class CoolingDegreeTime(TemperatureTime):
_abbreviation = 'coolTime'


class HeatingDegreeTime(TemperatureTime):
_abbreviation = 'heatTime'
6 changes: 3 additions & 3 deletions ladybug/designday.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from .sunpath import Sunpath

from .datatype import angle, energyflux, energyintensity, \
percentage, pressure, speed, temperature
fraction, pressure, speed, temperature

from .skymodel import ashrae_revised_clear_sky, \
ashrae_clear_sky, calc_horizontal_infrared
Expand Down Expand Up @@ -663,7 +663,7 @@ def hourly_relative_humidity(self):
rh_data = [rel_humid_from_db_dpt(x, y) for x, y in zip(
self._dry_bulb_condition.hourly_values, dpt_data)]
return self._get_daily_data_collections(
percentage.RelativeHumidity(), '%', rh_data)
fraction.RelativeHumidity(), '%', rh_data)

@property
def hourly_barometric_pressure(self):
Expand Down Expand Up @@ -705,7 +705,7 @@ def hourly_solar_radiation(self):
def hourly_sky_cover(self):
"""A data collection containing hourly sky cover values in tenths."""
return self._get_daily_data_collections(
percentage.TotalSkyCover(), 'tenths', self._sky_condition.hourly_sky_cover)
fraction.TotalSkyCover(), 'tenths', self._sky_condition.hourly_sky_cover)

@property
def hourly_horizontal_infrared(self):
Expand Down
14 changes: 7 additions & 7 deletions ladybug/epw.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .header import Header
from .analysisperiod import AnalysisPeriod
from .datatype import angle, distance, energyflux, energyintensity, generic, \
illuminance, luminance, percentage, pressure, speed, temperature
illuminance, luminance, fraction, pressure, speed, temperature
from .skymodel import calc_sky_temperature
from .futil import write_to_file

Expand Down Expand Up @@ -1433,7 +1433,7 @@ class EPWFields(object):
'unit': 'C'
},

8: {'name': percentage.RelativeHumidity(),
8: {'name': fraction.RelativeHumidity(),
'type': int,
'unit': '%'
},
Expand Down Expand Up @@ -1503,12 +1503,12 @@ class EPWFields(object):
'unit': 'm/s'
},

22: {'name': percentage.TotalSkyCover(), # used if Horizontal IR is missing
22: {'name': fraction.TotalSkyCover(), # used if Horizontal IR is missing
'type': int,
'unit': 'tenths'
},

23: {'name': percentage.OpaqueSkyCover(), # used if Horizontal IR is missing
23: {'name': fraction.OpaqueSkyCover(), # used if Horizontal IR is missing
'type': int,
'unit': 'tenths'
},
Expand Down Expand Up @@ -1540,7 +1540,7 @@ class EPWFields(object):
'unit': 'mm'
},

29: {'name': percentage.AerosolOpticalDepth(),
29: {'name': fraction.AerosolOpticalDepth(),
'type': float,
'unit': 'fraction'
},
Expand All @@ -1556,7 +1556,7 @@ class EPWFields(object):
'unit': 'day'
},

32: {'name': percentage.Albedo(),
32: {'name': fraction.Albedo(),
'type': float,
'unit': 'fraction'
},
Expand All @@ -1566,7 +1566,7 @@ class EPWFields(object):
'unit': 'mm'
},

34: {'name': percentage.LiquidPrecipitationQuantity(),
34: {'name': fraction.LiquidPrecipitationQuantity(),
'type': float,
'unit': 'fraction'
}
Expand Down
2 changes: 1 addition & 1 deletion tests/datacollection_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from ladybug.dt import DateTime
from ladybug.datatype.generic import GenericType
from ladybug.datatype.temperature import Temperature
from ladybug.datatype.percentage import RelativeHumidity, HumidityRatio
from ladybug.datatype.fraction import RelativeHumidity, HumidityRatio

from ladybug.epw import EPW
from ladybug.psychrometrics import humid_ratio_from_db_rh
Expand Down
28 changes: 23 additions & 5 deletions tests/datatype_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from ladybug import datatype
from ladybug.datatype import base
from ladybug.datatype import angle, area, distance, energy, energyflux, \
energyintensity, generic, illuminance, luminance, mass, massflowrate, \
percentage, power, pressure, rvalue, speed, temperature, temperaturedelta, \
energyintensity, fraction, generic, illuminance, luminance, mass, massflowrate, \
power, pressure, rvalue, speed, temperature, temperaturedelta, temperaturetime, \
thermalcondition, specificenergy, uvalue, volume, volumeflowrate

import unittest
Expand Down Expand Up @@ -94,9 +94,27 @@ def test_temperaturedelta(self):
assert temp_type.to_unit([1], 'C', 'F')[0] == pytest.approx(0.5555, rel=1e-1)
assert temp_type.to_unit([1], 'C', 'K')[0] == pytest.approx(1, rel=1e-1)

def test_percentage(self):
"""Test Percentage type."""
pct_type = percentage.Percentage()
def test_temperaturetime(self):
"""Test TemperatureTime type."""
temp_type = temperaturetime.TemperatureTime()
for unit in temp_type.units:
assert temp_type.to_unit([1], unit, unit)[0] == pytest.approx(1, rel=1e-5)
ip_vals, ip_u = temp_type.to_ip([1], unit)
assert len(ip_vals) == 1
si_vals, si_u = temp_type.to_si([1], unit)
assert len(si_vals) == 1
for other_unit in temp_type.units:
assert len(temp_type.to_unit([1], other_unit, unit)) == 1
assert temp_type.to_unit([1], 'degF-days', 'degC-days')[0] == pytest.approx(1.8, rel=1e-1)
assert temp_type.to_unit([1], 'degC-hours', 'degC-days')[0] == pytest.approx(24, rel=1e-3)
assert temp_type.to_unit([1], 'degF-hours', 'degC-days')[0] == pytest.approx(43.2, rel=1e-1)
assert temp_type.to_unit([1], 'degC-days', 'degF-days')[0] == pytest.approx(0.5555, rel=1e-1)
assert temp_type.to_unit([1], 'degC-days', 'degC-hours')[0] == pytest.approx(1 / 24, rel=1e-1)
assert temp_type.to_unit([1], 'degC-days', 'degF-hours')[0] == pytest.approx(0.023148, rel=1e-1)

def test_fraction(self):
"""Test Fraction type."""
pct_type = fraction.Fraction()
for unit in pct_type.units:
assert pct_type.to_unit([1], unit, unit)[0] == pytest.approx(1, rel=1e-5)
ip_vals, ip_u = pct_type.to_ip([1], unit)
Expand Down

0 comments on commit 8a5c5d9

Please sign in to comment.