"""UmiSchedules module."""
import calendar
import collections
import hashlib
from datetime import datetime
import numpy as np
import pandas as pd
from validator_collection import validators
from archetypal.schedule import Schedule, _ScheduleParser, get_year_for_first_weekday
from archetypal.template.umi_base import UmiBase
from archetypal.utils import log
[docs]class UmiSchedule(Schedule, UmiBase):
"""Class that handles Schedules."""
__slots__ = ("_quantity",)
def __init__(self, Name, quantity=None, **kwargs):
"""Initialize object with parameters.
Args:
Name:
quantity:
**kwargs:
"""
super(UmiSchedule, self).__init__(Name, **kwargs)
self.quantity = quantity
@property
def quantity(self):
"""Get or set the schedule quantity."""
return self._quantity
@quantity.setter
def quantity(self, value):
self._quantity = value
[docs] @classmethod
def constant_schedule(cls, value=1, Name="AlwaysOn", Type="Fraction", **kwargs):
"""Create an UmiSchedule with a constant value at each timestep.
Args:
Type:
value (float):
Name:
idf:
**kwargs:
"""
value = validators.float(value)
return super(UmiSchedule, cls).constant_schedule(
value=value, Name=Name, Type=Type, **kwargs
)
[docs] @classmethod
def random(cls, Name="AlwaysOn", Type="Fraction", **kwargs):
"""Create an UmiSchedule with a randomized value (0-1) at each timestep.
Args:
Name (str): The name of the Schedule.
Type (str or ScheduleTypeLimits):
**kwargs: keywords passed to the constructor.
"""
values = np.random.rand(
8760,
)
return cls(Values=values.tolist(), Name=Name, Type=Type, **kwargs)
[docs] @classmethod
def from_values(cls, Name, Values, Type="Fraction", **kwargs):
"""Create an UmiSchedule from a list of values.
Args:
Name (str): The name of the Schedule.
Values (list):
Type:
**kwargs:
"""
return super(UmiSchedule, cls).from_values(
Name=Name, Values=Values, Type=Type, **kwargs
)
[docs] def combine(self, other, weights=None, quantity=None):
"""Combine two UmiSchedule objects together.
Args:
other (UmiSchedule): The other Schedule object to combine with.
weights (list, dict or string): Attribute of self and other containing the
weight factor. If a list is passed, it must have len = 2; the first
element is applied to self and the second element is applied to other.
If a dict is passed, the self.Name and other.Name are the keys. If a
str is passed, the
quantity (list or dict or bool): Scalar value that will be multiplied by
self before the averaging occurs. This ensures that the resulting
schedule returns the correct integrated value. If a dict is passed,
keys are schedules Names and values are quantities.
Returns:
(UmiSchedule): the combined UmiSchedule object.
Raises:
TypeError: if Quantity is not of type list, tuple, dict or a callable.
"""
# Check if other is None. Simply return self
if not other:
return self
if not self:
return other
if not isinstance(other, UmiSchedule):
msg = "Cannot combine %s with %s" % (
self.__class__.__name__,
other.__class__.__name__,
)
raise NotImplementedError(msg)
# check if the schedule is the same
if self == other:
if self.quantity and other.quantity:
self.quantity += other.quantity
return self
# check if self is only zeros. Should not affect other.
if not np.any(self.all_values):
return other
# check if other is only zeros. Should not affect self.
if not np.any(other.all_values):
return self
if not weights:
log(
'using 1 as weighting factor in "{}" '
"combine.".format(self.__class__.__name__)
)
weights = [1, 1]
elif isinstance(weights, str):
# get the attribute from self and other
weights = [getattr(self, weights), getattr(other, weights)]
elif isinstance(weights, (list, tuple)):
# check if length is 2.
length = len(weights)
if length != 2:
raise ValueError(
"USing a list or tuple, the weights attribute must "
"have a length of 2. A length of {}".format(length)
)
elif isinstance(weights, dict):
weights = [weights[self.Name], weights[other.Name]]
if quantity is None:
new_values = np.average(
[self.all_values, other.all_values], axis=0, weights=weights
)
elif isinstance(quantity, dict):
# Multiplying the schedule values by the quantity for both self and other
# and then using a weighted average. Finally, new values are normalized.
new_values = np.average(
[
self.all_values * quantity[self.Name],
other.all_values * quantity[other.Name],
],
axis=0,
weights=weights,
)
new_values /= quantity[self.Name] + quantity[other.Name]
elif callable(quantity):
new_values = np.average(
np.stack((self.all_values, other.all_values), axis=1),
axis=1,
weights=[
quantity(self.predecessors.data),
quantity(other.predecessors.data),
],
)
elif isinstance(quantity, (list, tuple)):
# Multiplying the schedule values by the quantity for both self and other
# and then using a weighted average. Finally, new values are normalized.
self_quantity, other_quantity = quantity
new_values = (
self.all_values * self_quantity + other.all_values * other_quantity
) / sum(quantity)
elif isinstance(quantity, bool):
new_values = np.average(
[self.all_values, other.all_values],
axis=0,
weights=[self.quantity * weights[0], other.quantity * weights[1]],
)
else:
raise TypeError("Quantity is not of type list, tuple, dict or a callable")
# the new object's name
meta = self._get_predecessors_meta(other)
# Overriding meta Name
hasher = hashlib.md5()
hasher.update(new_values)
meta["Name"] = f"Combined_UmiSchedule_{hasher.hexdigest()}"
quantity = np.nansum(
[self.quantity or float("nan"), other.quantity or float("nan")]
)
new_obj = UmiSchedule.from_values(
Values=new_values, Type="Fraction", quantity=quantity, **meta
)
new_obj.predecessors.update(self.predecessors + other.predecessors)
new_obj.weights = sum(weights)
return new_obj
[docs] def develop(self):
"""Develop the UmiSchedule into a Year-Week-Day schedule structure."""
year, weeks, days = self.to_year_week_day()
lines = ["- {}".format(obj) for obj in self.predecessors]
_from = "\n".join(lines)
year.Comments = (
f"Year Week Day schedules created from: \n{_from}" + str(id(self)),
)
return year
[docs] def get_unique(self):
"""Return the first of all the created objects that is equivalent to self."""
return super(UmiSchedule, self.develop()).get_unique()
[docs] def to_dict(self):
"""Return UmiSchedule dictionary representation.
Hint:
UmiSchedule does not implement the to_dict method because it is not used
when generating the json file. Only Year-Week- and DaySchedule classes
are used.
"""
return self.to_ref()
[docs] def to_ref(self):
"""Return a ref pointer to self."""
return {"$ref": str(self.id)}
[docs] def validate(self):
"""Validate object and fill in missing values."""
return self
[docs] def mapping(self, validate=True):
"""Get a dict based on the object properties, useful for dict repr.
Args:
validate (bool): If True, try to validate object before returning the
mapping.
"""
if validate:
self.validate()
return dict(
Category=self.Category,
Type=self.Type,
Comments=self.Comments,
DataSource=self.DataSource,
Name=self.Name,
)
[docs] def get_ref(self, ref):
"""Get item matching reference id.
Args:
ref:
"""
return next(
iter(
[
value
for value in UmiSchedule.CREATED_OBJECTS
if value.id == ref["$ref"]
]
),
None,
)
[docs] def duplicate(self):
"""Get copy of self."""
return self.__copy__()
def __add__(self, other):
"""Return new object that is the combination of self and other."""
return UmiSchedule.combine(self, other)
def __repr__(self):
"""Return a representation of self."""
name = self.Name
resample = self.series.resample("D")
min = resample.min().mean()
mean = resample.mean().mean()
max = resample.max().mean()
return (
name
+ ": "
+ "mean daily min:{:.2f} mean:{:.2f} max:{:.2f} ".format(min, mean, max)
+ (f"quantity {self.quantity}" if self.quantity is not None else "")
)
def __str__(self):
"""Return the string representation of self."""
return repr(self)
def __hash__(self):
"""Return the hash value of self."""
return hash((self.__class__.__name__, getattr(self, "Name", None)))
def __eq__(self, other):
"""Assert self is equivalent to other."""
if not isinstance(other, UmiSchedule):
return NotImplemented
if self.all_values.size != other.all_values.size:
return NotImplemented
else:
return all(
[
self.strict == other.strict,
self.Type == other.Type,
self.quantity == other.quantity,
np.allclose(self.all_values, other.all_values, rtol=1e-02),
]
)
def __copy__(self):
"""Create a copy of self."""
return self.__class__(
Name=self.Name,
quantity=self.quantity,
Values=self.all_values.tolist(),
strict=self.strict,
Type=self.Type,
)
[docs]class YearSchedulePart:
"""Helper Class for YearSchedules defined with FromDay FromMonth ToDay ToMonth."""
__slots__ = ("_from_day", "_from_month", "_to_day", "_to_month", "_schedule")
def __init__(
self,
FromDay=None,
FromMonth=None,
ToDay=None,
ToMonth=None,
Schedule=None,
**kwargs,
):
"""Initialize YearSchedulePart.
Args:
FromDay (int): This numeric field is the starting day for the
schedule time period.
FromMonth (int): This numeric field is the starting month for the
schedule time period.
ToDay (int): This numeric field is the ending day for the schedule
time period.
ToMonth (int): This numeric field is the ending month for the
schedule time period.
Schedule (UmiSchedule): The associated UmiSchedule related to this
object.
kwargs (dict): Other Keyword arguments.
"""
self.FromDay = FromDay
self.FromMonth = FromMonth
self.ToDay = ToDay
self.ToMonth = ToMonth
self.Schedule = Schedule
@property
def FromDay(self):
"""Get or set the start day-of-month number [int]."""
return self._from_day
@FromDay.setter
def FromDay(self, value):
self._from_day = validators.integer(value, minimum=1, maximum=31)
@property
def FromMonth(self):
"""Get or set the start month-number [int]."""
return self._from_month
@FromMonth.setter
def FromMonth(self, value):
self._from_month = validators.integer(value, minimum=1, maximum=12)
@property
def ToDay(self):
"""Get or set the end day-of-month number [int]."""
return self._to_day
@ToDay.setter
def ToDay(self, value):
self._to_day = validators.integer(value, minimum=1, maximum=31)
@property
def ToMonth(self):
"""Get or set the end month-number [int]."""
return self._to_month
@ToMonth.setter
def ToMonth(self, value):
self._to_month = validators.integer(value, minimum=1, maximum=12)
@property
def Schedule(self):
"""Get or set the WeekSchedule object."""
return self._schedule
@Schedule.setter
def Schedule(self, value):
assert isinstance(value, WeekSchedule), "schedule must be of type WeekSchedule"
self._schedule = value
[docs] @classmethod
def from_dict(cls, data, schedules, **kwargs):
"""Create a YearSchedulePart object from a dictionary.
Args:
data (dict): The python dictionary.
schedules (dict): A dictionary of WeekSchedules with their id as keys.
**kwargs: keywords passed to parent constructor.
.. code-block:: python
data = {
'FromDay': 1,
'FromMonth': 1,
'ToDay': 31,
'ToMonth': 12,
'Schedule': {'$ref': '140622440042800'}
}
"""
schedule = schedules[data.pop("Schedule")["$ref"]]
ysp = cls(Schedule=schedule, **data, **kwargs)
return ysp
[docs] def to_dict(self):
"""Return YearSchedulePart dictionary representation."""
return collections.OrderedDict(
FromDay=self.FromDay,
FromMonth=self.FromMonth,
ToDay=self.ToDay,
ToMonth=self.ToMonth,
Schedule={"$ref": str(self.Schedule.id)},
)
def __str__(self):
"""Return string representation of self."""
return str(self.to_dict())
[docs] def mapping(self):
"""Get a dict based on the object properties, useful for dict repr."""
return dict(
FromDay=self.FromDay,
FromMonth=self.FromMonth,
ToDay=self.ToDay,
ToMonth=self.ToMonth,
Schedule=self.Schedule,
)
[docs] def get_unique(self):
"""Return the first of all the created objects that is equivalent to self."""
return self
def __eq__(self, other):
"""Assert self is equivalent to other."""
if not isinstance(other, YearSchedulePart):
return NotImplemented
else:
return all(
[
self.FromDay == other.FromDay,
self.FromMonth == other.FromMonth,
self.ToDay == other.ToDay,
self.ToMonth == other.ToMonth,
self.Schedule == other.Schedule,
]
)
def __iter__(self):
"""Iterate over attributes. Yields tuple of (keys, value)."""
for k, v in self.mapping().items():
yield k, v
def __hash__(self):
"""Return the hash value of self."""
return id(self)
[docs]class DaySchedule(UmiSchedule):
"""Superclass of UmiSchedule that handles daily schedules."""
__slots__ = ("_values",)
def __init__(self, Name, Values, Category="Day", **kwargs):
"""Initialize a DaySchedule object with parameters.
Args:
Values (list): List of 24 values.
Name (str): Name of the schedule.
Category (str): category identification (default: "Day").
**kwargs: Keywords passed to the :class:`UmiSchedule` constructor.
"""
super(DaySchedule, self).__init__(
Category=Category, Name=Name, Values=Values, **kwargs
)
@property
def all_values(self) -> np.ndarray:
"""Return numpy array of schedule Values."""
return np.array(self._values)
@all_values.setter
def all_values(self, value):
self._values = validators.iterable(value, maximum_length=24)
[docs] @classmethod
def from_epbunch(cls, epbunch, strict=False, **kwargs):
"""Create a DaySchedule from an EpBunch.
This method accepts "Schedule:Day:Hourly", "Schedule:Day:List" and
"Schedule:Day:Interval".
Args:
epbunch (EpBunch): The EpBunch object to construct a DaySchedule
from.
**kwargs: Keywords passed to the :class:`UmiSchedule` constructor.
See :class:`UmiSchedule` for more details.
"""
assert epbunch.key.lower() in (
"schedule:day:hourly",
"schedule:day:list",
"schedule:day:interval",
), (
f"Input error for '{epbunch.key}'. Expected on of "
f"('Schedule:Day:Hourly', 'Schedule:Day:List' and "
f"'Schedule:Day:Interval')"
)
start_day_of_the_week = epbunch.theidf.day_of_week_for_start_day
start_date = datetime(get_year_for_first_weekday(start_day_of_the_week), 1, 1)
sched = cls(
Name=epbunch.Name,
epbunch=epbunch,
schType=epbunch.key,
Type=cls.get_schedule_type_limits_name(epbunch),
Values=_ScheduleParser.get_schedule_values(
epbunch, start_date, strict=strict
),
**kwargs,
)
return sched
[docs] @classmethod
def from_values(cls, Name, Values, Type="Fraction", **kwargs):
"""Create a DaySchedule from an array of size (24,).
Args:
Name:
Values (array-like): A list of values of length 24.
Type (str): Schedule Type Limit name.
**kwargs: Keywords passed to the :class:`UmiSchedule` constructor.
See :class:`UmiSchedule` for more details.
"""
return cls(Name=Name, Values=Values, Type=Type, **kwargs)
[docs] @classmethod
def from_dict(cls, data, **kwargs):
"""Create a DaySchedule from a dictionary.
Args:
data (dict): A python dictionary with the structure shown bellow.
**kwargs: keywords passed to parents constructors.
.. code-block:: python
{
"$id": "67",
"Category": "Day",
"Type": "Fraction",
"Values": [...], # 24 hourly values
"Comments": "default",
"DataSource": "default",
"Name": "B_Res_D_Occ_WD"
},
"""
_id = data.pop("$id")
sched = cls.from_values(id=_id, **data, **kwargs)
return sched
[docs] def get_unique(self):
"""Return the first of all the created objects that is equivalent to self."""
return UmiBase.get_unique(self)
[docs] def to_dict(self):
"""Return DaySchedule dictionary representation."""
data_dict = collections.OrderedDict()
data_dict["$id"] = str(self.id)
data_dict["Category"] = self.Category
data_dict["Type"] = "Fraction" if self.Type is None else self.Type.Name
data_dict["Values"] = np.round(self.all_values, 3).tolist()
data_dict["Comments"] = validators.string(self.Comments, allow_empty=True)
data_dict["DataSource"] = self.DataSource
data_dict["Name"] = self.Name
return data_dict
[docs] def mapping(self, validate=True):
"""Get a dict based on the object properties, useful for dict repr.
Args:
validate (bool): If True, try to validate object before returning the
mapping.
"""
if validate:
self.validate()
return dict(
Category=self.Category,
Type=self.Type,
Values=self.all_values.round(3).tolist(),
Comments=self.Comments,
DataSource=self.DataSource,
Name=self.Name,
)
[docs] def to_ref(self):
"""Return a ref pointer to self."""
return {"$ref": str(self.id)}
[docs] def duplicate(self):
"""Get copy of self."""
return self.__copy__()
def __eq__(self, other):
"""Assert self is equivalent to other."""
if not isinstance(other, DaySchedule):
return NotImplemented
else:
return all(
[
self.Type == other.Type,
np.allclose(self.all_values, other.all_values, rtol=1e-02),
]
)
def __hash__(self):
"""Return the hash value of self."""
return super(DaySchedule, self).__hash__()
def __copy__(self):
"""Create a copy of self."""
return self.__class__(self.Name, Values=self.all_values.tolist())
[docs] def to_epbunch(self, idf):
"""Convert self to an epbunch given an idf model.
Args:
idf (IDF): An IDF model.
.. code-block:: python
SCHEDULE:DAY:HOURLY,
, !- Name
, !- Schedule Type Limits Name
0, !- Hour 1
0, !- Hour 2
0, !- Hour 3
0, !- Hour 4
0, !- Hour 5
0, !- Hour 6
0, !- Hour 7
0, !- Hour 8
0, !- Hour 9
0, !- Hour 10
0, !- Hour 11
0, !- Hour 12
0, !- Hour 13
0, !- Hour 14
0, !- Hour 15
0, !- Hour 16
0, !- Hour 17
0, !- Hour 18
0, !- Hour 19
0, !- Hour 20
0, !- Hour 21
0, !- Hour 22
0, !- Hour 23
0; !- Hour 24
Returns:
EpBunch: The EpBunch object added to the idf model.
"""
return idf.newidfobject(
key="Schedule:Day:Hourly".upper(),
**dict(
Name=self.Name,
Schedule_Type_Limits_Name=self.Type.to_epbunch(idf).Name,
**{"Hour_{}".format(i + 1): self.all_values[i] for i in range(24)},
),
)
[docs]class WeekSchedule(UmiSchedule):
"""Superclass of UmiSchedule that handles weekly schedules."""
__slots__ = ("_days", "_values")
def __init__(self, Name, Days=None, Category="Week", **kwargs):
"""Initialize a WeekSchedule object with parameters.
Args:
Days (list of DaySchedule): list of :class:`DaySchedule`.
**kwargs:
"""
super(WeekSchedule, self).__init__(Name, Category=Category, **kwargs)
self.Days = Days
@property
def Days(self):
"""Get or set the list of DaySchedule objects."""
return self._days
@Days.setter
def Days(self, value):
if value is not None:
assert all(
isinstance(x, DaySchedule) for x in value
), f"Input value error '{value}'. Expected list of DaySchedule."
self._days = value
[docs] @classmethod
def from_epbunch(cls, epbunch, **kwargs):
"""Create a WeekSchedule from a Schedule:Week:Daily object.
Args:
epbunch (EpBunch): The Schedule:Week:Daily object.
**kwargs: keywords passed to the constructor.
"""
assert (
epbunch.key.lower() == "schedule:week:daily"
), f"Expected a 'schedule:week:daily' not a '{epbunch.key.lower()}'"
Days = WeekSchedule.get_days(epbunch, **kwargs)
sched = cls(
Name=epbunch.Name,
schType=epbunch.key,
Days=Days,
**kwargs,
)
return sched
[docs] @classmethod
def from_dict(cls, data, day_schedules, **kwargs):
"""Create a WeekSchedule from a dictionary.
Args:
data (dict): The python dictionary.
day_schedules (dict): A dictionary of python DaySchedules with their id as
keys.
**kwargs: keywords passed to the constructor.
"""
refs = data.pop("Days")
_id = data.pop("$id")
Days = [day_schedules[ref["$ref"]] for ref in refs]
wc = cls(Days=Days, id=_id, **data, **kwargs)
return wc
[docs] def get_unique(self):
"""Return the first of all the created objects that is equivalent to self."""
return UmiBase.get_unique(self)
[docs] def to_dict(self):
"""Return WeekSchedule dictionary representation."""
data_dict = collections.OrderedDict()
data_dict["$id"] = str(self.id)
data_dict["Category"] = self.Category
data_dict["Days"] = [day.to_ref() for day in self.Days]
data_dict["Type"] = "Fraction" if self.Type is None else self.Type.Name
data_dict["Comments"] = validators.string(self.Comments, allow_empty=True)
data_dict["DataSource"] = self.DataSource
data_dict["Name"] = self.Name
return data_dict
[docs] def mapping(self, validate=True):
"""Get a dict based on the object properties, useful for dict repr.
Args:
validate (bool): If True, try to validate object before returning the
mapping.
"""
if validate:
self.validate()
return dict(
Category=self.Category,
Days=self.Days,
Type=self.Type,
Comments=self.Comments,
DataSource=self.DataSource,
Name=self.Name,
)
[docs] @classmethod
def get_days(cls, epbunch, **kwargs):
"""Get the DaySchedules referenced in the Week:Schedule:Days object.
Args:
list of DaySchedule: The list of DaySchedules referenced by the epbunch.
"""
assert (
epbunch.key.lower() == "schedule:week:daily"
), f"Expected a 'schedule:week:daily' not a '{epbunch.key.lower()}'"
Days = []
dayname = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
]
for day in dayname:
day_ep = epbunch.get_referenced_object("{}_ScheduleDay_Name".format(day))
Days.append(DaySchedule.from_epbunch(day_ep, **kwargs))
return Days
@property
def all_values(self) -> np.ndarray:
"""Return numpy array of schedule Values."""
if self._values is None:
self._values = np.concatenate([day.all_values for day in self.Days])
return self._values
[docs] def to_ref(self):
"""Return a ref pointer to self."""
return {"$ref": str(self.id)}
[docs] def duplicate(self):
"""Get copy of self."""
return self.__copy__()
def __eq__(self, other):
"""Assert self is equivalent to other."""
if not isinstance(other, WeekSchedule):
return NotImplemented
else:
return all(
[
self.Type == other.Type,
self.Days == other.Days,
]
)
def __hash__(self):
"""Return the hash value of self."""
return super(WeekSchedule, self).__hash__()
def __copy__(self):
"""Create a copy of self."""
return self.__class__(Name=self.Name, Days=self.Days)
[docs] def to_epbunch(self, idf):
"""Convert self to an epbunch given an idf model.
Args:
idf (IDF): An IDF model.
Returns:
EpBunch: The EpBunch object added to the idf model.
"""
return idf.newidfobject(
key="Schedule:Week:Daily".upper(),
**dict(
Name=self.Name,
**{
f"{calendar.day_name[i]}_ScheduleDay_Name": day.to_epbunch(idf).Name
for i, day in enumerate(self.Days)
},
Holiday_ScheduleDay_Name=self.Days[6].Name,
SummerDesignDay_ScheduleDay_Name=self.Days[0].Name,
WinterDesignDay_ScheduleDay_Name=self.Days[0].Name,
CustomDay1_ScheduleDay_Name=self.Days[1].Name,
CustomDay2_ScheduleDay_Name=self.Days[6].Name,
),
)
[docs]class YearSchedule(UmiSchedule):
"""Superclass of UmiSchedule that handles yearly schedules."""
def __init__(self, Name, Type="Fraction", Parts=None, Category="Year", **kwargs):
"""Initialize a YearSchedule object with parameters.
Args:
Category:
Name:
Type:
Parts (list of YearSchedulePart): The YearScheduleParts.
**kwargs:
"""
self.epbunch = kwargs.get("epbunch", None)
if Parts is None:
self.Parts = self._get_parts(self.epbunch)
else:
self.Parts = Parts
super(YearSchedule, self).__init__(
Name=Name, Type=Type, schType="Schedule:Year", Category=Category, **kwargs
)
def __eq__(self, other):
"""Assert self is equivalent to other."""
if not isinstance(other, YearSchedule):
return NotImplemented
else:
return all([self.Type == other.Type, self.Parts == other.Parts])
def __hash__(self):
"""Return the hash value of self."""
return super(YearSchedule, self).__hash__()
@property
def all_values(self) -> np.ndarray:
"""Return numpy array of schedule Values."""
if self._values is None:
index = pd.date_range(start=self.startDate, freq="1H", periods=8760)
series = pd.Series(index=index)
for part in self.Parts:
start = "{}-{}-{}".format(self.year, part.FromMonth, part.FromDay)
end = "{}-{}-{}".format(self.year, part.ToMonth, part.ToDay)
# Get week values from all_values of Days
one_week = np.array(
[
item
for sublist in part.Schedule.Days
for item in sublist.all_values
]
)
all_weeks = np.resize(one_week, len(series.loc[start:end]))
series.loc[start:end] = all_weeks
self._values = series.values
return self._values
[docs] @classmethod
def from_dict(cls, data, week_schedules, **kwargs):
"""Create a YearSchedule from a dictionary.
Args:
data (dict): The python dictionary.
week_schedule (dict): A dictionary of python WeekSchedules with their id as
keys.
**kwargs: keywords passed to the constructor.
"""
Parts = [
YearSchedulePart.from_dict(data, week_schedules)
for data in data.pop("Parts", None)
]
_id = data.pop("$id")
ys = cls(Parts=Parts, id=_id, **data, **kwargs)
return ys
[docs] def get_unique(self):
"""Return the first of all the created objects that is equivalent to self."""
return UmiBase.get_unique(self)
[docs] def to_dict(self):
"""Return YearSchedule dictionary representation."""
data_dict = collections.OrderedDict()
data_dict["$id"] = str(self.id)
data_dict["Category"] = self.Category
data_dict["Parts"] = [part.to_dict() for part in self.Parts]
data_dict["Type"] = "Fraction" if self.Type is None else self.Type.Name
data_dict["Comments"] = validators.string(self.Comments, allow_empty=True)
data_dict["DataSource"] = self.DataSource
data_dict["Name"] = self.Name
return data_dict
[docs] def to_epbunch(self, idf):
"""Convert self to an epbunch given an idf model.
Notes:
The object is added to the idf model.
Args:
idf (IDF): An IDF model.
Returns:
EpBunch: The EpBunch object added to the idf model.
"""
new_dict = dict(
Name=self.Name, Schedule_Type_Limits_Name=self.Type.to_epbunch(idf).Name
)
for i, part in enumerate(self.Parts):
new_dict.update(
{
"ScheduleWeek_Name_{}".format(i + 1): part.Schedule.to_epbunch(
idf
).Name,
"Start_Month_{}".format(i + 1): part.FromMonth,
"Start_Day_{}".format(i + 1): part.FromDay,
"End_Month_{}".format(i + 1): part.ToMonth,
"End_Day_{}".format(i + 1): part.ToDay,
}
)
return idf.newidfobject(key="Schedule:Year".upper(), **new_dict)
[docs] def mapping(self, validate=True):
"""Get a dict based on the object properties, useful for dict repr.
Args:
validate (bool): If True, try to validate object before returning the
mapping.
"""
if validate:
self.validate()
return dict(
Category=self.Category,
Parts=self.Parts,
Type=self.Type,
Comments=self.Comments,
DataSource=self.DataSource,
Name=self.Name,
)
def _get_parts(self, epbunch):
parts = []
for i in range(int(len(epbunch.fieldvalues[3:]) / 5)):
week_day_schedule_name = epbunch["ScheduleWeek_Name_{}".format(i + 1)]
FromMonth = epbunch["Start_Month_{}".format(i + 1)]
ToMonth = epbunch["End_Month_{}".format(i + 1)]
FromDay = epbunch["Start_Day_{}".format(i + 1)]
ToDay = epbunch["End_Day_{}".format(i + 1)]
parts.append(
YearSchedulePart(
FromDay,
FromMonth,
ToDay,
ToMonth,
next(
(
x
for x in self.CREATED_OBJECTS
if x.Name == week_day_schedule_name
and type(x).__name__ == "WeekSchedule"
)
),
)
)
return parts
[docs] def to_ref(self):
"""Return a ref pointer to self."""
return {"$ref": str(self.id)}