Passed
Pull Request — dev (#1183)
by Patrik
04:02
created

solph._helpers   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 144
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 14
eloc 53
dl 0
loc 144
rs 10
c 0
b 0
f 0

3 Functions

Rating   Name   Duplication   Size   Complexity  
A warn_if_missing_attribute() 0 10 1
A check_node_object_for_missing_attribute() 0 12 2
C create_time_index() 0 91 11
1
# -*- coding: utf-8 -*-
2
3
"""
4
Private helper functions.
5
6
SPDX-FileCopyrightText: Uwe Krien <[email protected]>
7
SPDX-FileCopyrightText: Simon Hilpert
8
SPDX-FileCopyrightText: Cord Kaldemeyer
9
SPDX-FileCopyrightText: Stephan Günther
10
SPDX-FileCopyrightText: Birgit Schachler
11
SPDX-FileCopyrightText: David Fuhrländer
12
SPDX-FileCopyrightText: Johannes Röder
13
14
SPDX-License-Identifier: MIT
15
16
"""
17
18
import calendar
19
import datetime
20
from warnings import warn
21
22
import pandas as pd
23
from oemof.tools import debugging
24
25
26
def check_node_object_for_missing_attribute(obj, attribute):
27
    """Raises a predefined warning if object does not have attribute.
28
29
    Arguments
30
    ---------
31
32
    obj : python object
33
    attribute : (string) name of the attribute to test for
34
35
    """
36
    if not getattr(obj, attribute):
37
        warn_if_missing_attribute(obj, attribute)
38
39
40
def warn_if_missing_attribute(obj, attribute):
41
    """Raises warning if attribute is missing for given object"""
42
    msg = (
43
        "Attribute <{0}> is missing in Node <{1}> of {2}.\n"
44
        "If this is intended and you know what you are doing you can"
45
        "disable the SuspiciousUsageWarning globally."
46
    )
47
    warn(
48
        msg.format(attribute, obj.label, type(obj)),
49
        debugging.SuspiciousUsageWarning,
50
    )
51
52
53
def create_time_index(
54
    year: int = None,
55
    interval_length: float = None,
56
    number_of_intervals: int = None,
57
    start: datetime.date = None,
58
    number: int = None,
59
    interval: float = None,
60
):
61
    """
62
    Create a datetime index for one year.
63
64
    Notes
65
    -----
66
    To create 8760 hourly intervals for a non leap year a datetime index with
67
    8761 time points need to be created. So the number of time steps is always
68
    the number of intervals plus one.
69
70
    Parameters
71
    ----------
72
    year : int, datetime
73
        The year of the index.
74
        Used to automatically set start and number for the specific year.
75
    interval_length : float
76
        The length time interval in hours e.g. 0.5 for 30min or 2 for a
77
        two hour interval (default: 1).
78
    number_of_intervals : int
79
        The number of time intervals. By default number is calculated to create
80
        an index of one year. For a shorter or longer period the number of
81
        intervals can be set by the user.
82
    start : datetime.datetime or datetime.date
83
        Optional start time. If start is not set, 00:00 of the first day of
84
        the given year is the start time.
85
86
    Examples
87
    --------
88
    >>> len(create_time_index(2014))
89
    8761
90
    >>> len(create_time_index(2012))  # leap year
91
    8785
92
    >>> len(create_time_index(2014, interval=0.5))
93
    17521
94
    >>> len(create_time_index(2014, interval=0.5, number=10))
95
    11
96
    >>> len(create_time_index(2014, number=10))
97
    11
98
    >>> str(create_time_index(2014, interval=0.5, number=10)[-1])
99
    '2014-01-01 05:00:00'
100
    >>> str(create_time_index(2014, interval=2, number=10)[-1])
101
    '2014-01-01 20:00:00'
102
    """
103
    if number is not None:
104
        warnig_text = "The argument 'number_of_intervals' repleaces 'number'."
105
        if number_of_intervals is None:
106
            warn(warnig_text)
107
            number_of_intervals = number
108
        else:
109
            raise AttributeError(warnig_text + " You cannot set both!")
110
111
    if interval is not None:
112
        warnig_text = "The argument 'interval_length' repleaces 'interval'."
113
        if interval_length is None:
114
            warn(warnig_text)
115
            interval_length = interval
116
        else:
117
            raise AttributeError(warnig_text + " You cannot set both!")
118
    if interval_length is None:
119
        interval_length = 1
120
121
    if number_of_intervals is None:
122
        if calendar.isleap(year):
123
            hours_in_year = 8784
124
        else:
125
            hours_in_year = 8760
126
        number_of_intervals = round(hours_in_year / interval_length)
127
    if start is not None:
128
        if year is not None:
129
            raise ValueError(
130
                "Arguments 'start' and 'year' are mutually exclusive."
131
            )
132
    else:
133
        start = f"1/1/{year}"
134
    try:
135
        time_index = pd.date_range(
136
            start, periods=number_of_intervals + 1, freq=f"{interval_length}h"
137
        )
138
    except ValueError:
139
        # Pandas <2.2 compatibility
140
        time_index = pd.date_range(
141
            start, periods=number_of_intervals + 1, freq=f"{interval_length}H"
142
        )
143
    return time_index
144