openenergyid 0.1.31__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. openenergyid/__init__.py +8 -0
  2. openenergyid/abstractsim/__init__.py +5 -0
  3. openenergyid/abstractsim/abstract.py +102 -0
  4. openenergyid/baseload/__init__.py +15 -0
  5. openenergyid/baseload/analysis.py +190 -0
  6. openenergyid/baseload/exceptions.py +9 -0
  7. openenergyid/baseload/models.py +32 -0
  8. openenergyid/capacity/__init__.py +6 -0
  9. openenergyid/capacity/main.py +103 -0
  10. openenergyid/capacity/models.py +32 -0
  11. openenergyid/const.py +29 -0
  12. openenergyid/dyntar/__init__.py +20 -0
  13. openenergyid/dyntar/const.py +31 -0
  14. openenergyid/dyntar/main.py +313 -0
  15. openenergyid/dyntar/models.py +101 -0
  16. openenergyid/elia/__init__.py +4 -0
  17. openenergyid/elia/api.py +91 -0
  18. openenergyid/elia/const.py +18 -0
  19. openenergyid/energysharing/__init__.py +12 -0
  20. openenergyid/energysharing/const.py +8 -0
  21. openenergyid/energysharing/data_formatting.py +77 -0
  22. openenergyid/energysharing/main.py +122 -0
  23. openenergyid/energysharing/models.py +80 -0
  24. openenergyid/enums.py +16 -0
  25. openenergyid/models.py +174 -0
  26. openenergyid/mvlr/__init__.py +19 -0
  27. openenergyid/mvlr/helpers.py +30 -0
  28. openenergyid/mvlr/main.py +34 -0
  29. openenergyid/mvlr/models.py +227 -0
  30. openenergyid/mvlr/mvlr.py +450 -0
  31. openenergyid/pvsim/__init__.py +8 -0
  32. openenergyid/pvsim/abstract.py +60 -0
  33. openenergyid/pvsim/elia/__init__.py +3 -0
  34. openenergyid/pvsim/elia/main.py +89 -0
  35. openenergyid/pvsim/main.py +49 -0
  36. openenergyid/pvsim/pvlib/__init__.py +11 -0
  37. openenergyid/pvsim/pvlib/main.py +115 -0
  38. openenergyid/pvsim/pvlib/models.py +235 -0
  39. openenergyid/pvsim/pvlib/quickscan.py +99 -0
  40. openenergyid/pvsim/pvlib/weather.py +91 -0
  41. openenergyid/sim/__init__.py +5 -0
  42. openenergyid/sim/main.py +67 -0
  43. openenergyid/simeval/__init__.py +6 -0
  44. openenergyid/simeval/main.py +148 -0
  45. openenergyid/simeval/models.py +162 -0
  46. openenergyid-0.1.31.dist-info/METADATA +32 -0
  47. openenergyid-0.1.31.dist-info/RECORD +50 -0
  48. openenergyid-0.1.31.dist-info/WHEEL +5 -0
  49. openenergyid-0.1.31.dist-info/licenses/LICENSE +21 -0
  50. openenergyid-0.1.31.dist-info/top_level.txt +1 -0
@@ -0,0 +1,91 @@
1
+ """
2
+ Weather data utilities for PV simulation.
3
+
4
+ Provides functions to retrieve and process weather data for PV simulations,
5
+ including timezone-aware handling and leap year adjustments.
6
+ """
7
+
8
+ import datetime as dt
9
+ import typing
10
+
11
+ import pandas as pd
12
+ import pvlib
13
+
14
+
15
+ def get_utc_offset_on_1_jan(timezone: str) -> int:
16
+ """
17
+ Get the UTC offset in hours for the given timezone on January 1st.
18
+
19
+ Args:
20
+ timezone: The timezone string (e.g., "Europe/Amsterdam").
21
+
22
+ Returns:
23
+ The UTC offset in hours as an integer.
24
+
25
+ Raises:
26
+ ValueError: If the UTC offset cannot be determined.
27
+ """
28
+ jan_first = pd.Timestamp("2020-01-01 00:00:00", tz=timezone)
29
+ utc_offset = jan_first.utcoffset()
30
+ if utc_offset is None:
31
+ raise ValueError(f"Could not determine UTC offset for timezone {timezone}")
32
+ return int(utc_offset.total_seconds() / 3600)
33
+
34
+
35
+ def get_weather(
36
+ latitude: float, longitude: float, start: dt.date, end: dt.date, tz: str
37
+ ) -> pd.DataFrame:
38
+ """
39
+ Retrieve and process weather data for the specified location and date range.
40
+
41
+ Downloads a "normal year" TMY dataset from PVGIS, aligns it to the requested
42
+ date range and timezone, and handles leap year adjustments.
43
+
44
+ Args:
45
+ latitude: Latitude of the location.
46
+ longitude: Longitude of the location.
47
+ start: Start date (inclusive).
48
+ end: End date (exclusive).
49
+ tz: Timezone string.
50
+
51
+ Returns:
52
+ A pandas DataFrame indexed by timestamp with weather data.
53
+ """
54
+ # Get a "normal year" from pvgis, it will be indexed in 1990
55
+ utc_offset = get_utc_offset_on_1_jan(tz)
56
+ weather = pvlib.iotools.get_pvgis_tmy(
57
+ latitude=latitude, longitude=longitude, roll_utc_offset=utc_offset
58
+ )[0]
59
+ weather = typing.cast(pd.DataFrame, weather)
60
+ weather = weather.tz_convert(tz)
61
+ weather.index.name = None
62
+
63
+ # Check if 29 februari is included in the weather data
64
+ weather_index = typing.cast(pd.DatetimeIndex, weather.index)
65
+ leap_included = "02-29" in weather_index.strftime("%m-%d").unique()
66
+
67
+ # Construct our desired index
68
+ new_index = pd.date_range(start, end, freq="15min", tz=tz)
69
+ temp_df = pd.DataFrame(index=new_index)
70
+ # Add a key that doesn't contain year
71
+ temp_df["tkey"] = new_index.tz_convert("UTC").strftime("%m-%dT%H:%M:%S%z")
72
+ temp_df["timestamp"] = new_index
73
+ if not leap_included:
74
+ # Replace all tkey's starting with "02-29" with "02-28"
75
+ temp_df.loc[temp_df.tkey.str.startswith("02-29"), "tkey"] = temp_df.loc[
76
+ temp_df.tkey.str.startswith("02-29"), "tkey"
77
+ ].str.replace("02-29", "02-28")
78
+
79
+ # Add the key to the weather frame and join
80
+ weather["tkey"] = weather_index.tz_convert("UTC").strftime("%m-%dT%H:%M:%S%z")
81
+ df = (
82
+ pd.merge(temp_df, weather, on="tkey", how="left")
83
+ .set_index("timestamp")
84
+ .drop(columns=["tkey"])
85
+ )
86
+
87
+ # Interpolate and drop last value
88
+ df = df.interpolate(method="time")
89
+ df = df.iloc[:-1]
90
+
91
+ return df
@@ -0,0 +1,5 @@
1
+ """Main Simulation Package that can handle every simulation."""
2
+
3
+ from .main import ExAnteData, FullSimulationInput, run_simulation
4
+
5
+ __all__ = ["FullSimulationInput", "run_simulation", "ExAnteData"]
@@ -0,0 +1,67 @@
1
+ """Generic Simulation Analysis Module."""
2
+
3
+ from typing import Annotated, Union
4
+
5
+ import aiohttp
6
+ from pydantic import BaseModel, Field
7
+
8
+ from ..abstractsim import SimulationSummary, Simulator
9
+ from ..pvsim import PVSimulationInput, apply_simulation
10
+ from ..pvsim import get_simulator as get_pv_simulator
11
+ from ..simeval import EvaluationInput, compare_results, evaluate
12
+ from ..simeval.models import Frequency
13
+
14
+ # Here we define all types of simulations
15
+ SimulationInput = Annotated[Union[PVSimulationInput], Field(discriminator="type")]
16
+
17
+
18
+ def get_simulator(input_: SimulationInput) -> Simulator:
19
+ """Get an instance of the simulator based on the input data."""
20
+ # Only PV simulators for now
21
+ return get_pv_simulator(input_)
22
+
23
+
24
+ class ExAnteData(EvaluationInput):
25
+ """Ex-ante data for simulation analysis."""
26
+
27
+
28
+ class FullSimulationInput(BaseModel):
29
+ """Full input for running a simulation analysis."""
30
+
31
+ ex_ante_data: ExAnteData
32
+ simulation_parameters: SimulationInput
33
+ timezone: str = "Europe/Brussels"
34
+ return_frequencies: list[Frequency] | None = Field(
35
+ default=None,
36
+ examples=["MS", "W-MON"],
37
+ description="Optional list of frequencies that should be included in the analysis. Be default, only `total` is included, but you can add more here. Uses the Pandas freqstr.",
38
+ )
39
+
40
+
41
+ async def run_simulation(
42
+ input_: FullSimulationInput, session: aiohttp.ClientSession
43
+ ) -> SimulationSummary:
44
+ """Run the full simulation analysis workflow."""
45
+ df = input_.ex_ante_data.to_pandas(timezone=input_.timezone)
46
+
47
+ ex_ante_eval = evaluate(df, return_frequencies=input_.return_frequencies)
48
+
49
+ simulator: Simulator = get_simulator(input_.simulation_parameters)
50
+ await simulator.load_resources(session=session)
51
+
52
+ sim_eval = evaluate(simulator.result_as_frame(), return_frequencies=input_.return_frequencies)
53
+
54
+ df_post = apply_simulation(df, simulator.simulation_results)
55
+
56
+ post_eval = evaluate(df_post, return_frequencies=input_.return_frequencies)
57
+
58
+ comparison = compare_results(ex_ante_eval, post_eval)
59
+
60
+ summary = SimulationSummary.from_simulation(
61
+ ex_ante=ex_ante_eval,
62
+ simulation_result=sim_eval,
63
+ ex_post=post_eval,
64
+ comparison=comparison,
65
+ )
66
+
67
+ return summary
@@ -0,0 +1,6 @@
1
+ """Module containing basic evaluation functions for energy systems."""
2
+
3
+ from .main import compare_results, evaluate
4
+ from .models import EvaluationInput, EvaluationOutput
5
+
6
+ __all__ = ["EvaluationInput", "EvaluationOutput", "evaluate", "compare_results"]
@@ -0,0 +1,148 @@
1
+ """Module for evaluating energy simulation data."""
2
+
3
+ import typing
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
+
8
+ from .. import const
9
+
10
+
11
+ def evaluate(
12
+ data: pd.DataFrame, return_frequencies: list[str] | None = None
13
+ ) -> dict[str, pd.DataFrame | pd.Series]:
14
+ """Evaluate the data and return resampled results.
15
+
16
+ Args:
17
+ data: A pandas DataFrame containing time series data with columns:
18
+ - electricity_delivered
19
+ - electricity_exported
20
+ - electricity_produced
21
+ return_frequencies: List of pandas offset aliases for resampling frequencies.
22
+ Defaults to ['MS'] (Month Start) if None.
23
+
24
+ Returns:
25
+ A dictionary with keys as frequencies and values as resampled DataFrames.
26
+ """
27
+ evaluator = Evaluator(data=data, return_frequencies=return_frequencies)
28
+ return evaluator.evaluate()
29
+
30
+
31
+ class Evaluator:
32
+ """Evaluator for basic energy system evaluation."""
33
+
34
+ def __init__(self, data: pd.DataFrame, return_frequencies: list[str] | None = None):
35
+ """Initialize the evaluator with data and return frequencies."""
36
+ self.data = data.copy()
37
+ if return_frequencies is None:
38
+ self.return_frequencies = []
39
+ else:
40
+ self.return_frequencies = return_frequencies
41
+
42
+ def evaluate(self) -> dict[str, pd.DataFrame | pd.Series]:
43
+ """Evaluate the data and return resampled results."""
44
+ if const.ELECTRICITY_DELIVERED not in self.data.columns:
45
+ self.data[const.ELECTRICITY_DELIVERED] = float("NaN")
46
+ if const.ELECTRICITY_EXPORTED not in self.data.columns:
47
+ self.data[const.ELECTRICITY_EXPORTED] = float("NaN")
48
+ if const.ELECTRICITY_PRODUCED not in self.data.columns:
49
+ self.data[const.ELECTRICITY_PRODUCED] = float("NaN")
50
+ if const.PRICE_ELECTRICITY_DELIVERED not in self.data.columns:
51
+ self.data[const.PRICE_ELECTRICITY_DELIVERED] = float("NaN")
52
+ if const.PRICE_ELECTRICITY_EXPORTED not in self.data.columns:
53
+ self.data[const.PRICE_ELECTRICITY_EXPORTED] = float("NaN")
54
+
55
+ # Add electricy_consumed
56
+ self.data[const.ELECTRICITY_CONSUMED] = (
57
+ self.data[const.ELECTRICITY_DELIVERED]
58
+ - self.data[const.ELECTRICITY_EXPORTED].fillna(0.0)
59
+ + self.data[const.ELECTRICITY_PRODUCED].fillna(0.0)
60
+ ).clip(lower=0)
61
+
62
+ # Add electricy_self_consumed
63
+ self.data[const.ELECTRICITY_SELF_CONSUMED] = (
64
+ self.data[const.ELECTRICITY_PRODUCED] - self.data[const.ELECTRICITY_EXPORTED]
65
+ ).clip(lower=0)
66
+
67
+ # Add costs
68
+ self.data[const.COST_ELECTRICITY_DELIVERED] = (
69
+ self.data[const.ELECTRICITY_DELIVERED] * self.data[const.PRICE_ELECTRICITY_DELIVERED]
70
+ )
71
+ self.data[const.EARNINGS_ELECTRICITY_EXPORTED] = (
72
+ self.data[const.ELECTRICITY_EXPORTED] * self.data[const.PRICE_ELECTRICITY_EXPORTED]
73
+ )
74
+ self.data[const.COST_ELECTRICITY_NET] = (
75
+ self.data[const.COST_ELECTRICITY_DELIVERED]
76
+ - self.data[const.EARNINGS_ELECTRICITY_EXPORTED]
77
+ )
78
+
79
+ # Calculate sums
80
+ results: dict[str, pd.DataFrame | pd.Series] = {}
81
+ results["total"] = (
82
+ self.data[
83
+ [
84
+ const.ELECTRICITY_DELIVERED,
85
+ const.ELECTRICITY_EXPORTED,
86
+ const.ELECTRICITY_PRODUCED,
87
+ const.ELECTRICITY_CONSUMED,
88
+ const.ELECTRICITY_SELF_CONSUMED,
89
+ const.COST_ELECTRICITY_DELIVERED,
90
+ const.EARNINGS_ELECTRICITY_EXPORTED,
91
+ const.COST_ELECTRICITY_NET,
92
+ ]
93
+ ]
94
+ .dropna(axis=1, how="all")
95
+ .sum()
96
+ )
97
+
98
+ for freq in self.return_frequencies:
99
+ resampled = (
100
+ self.data[
101
+ [
102
+ const.ELECTRICITY_DELIVERED,
103
+ const.ELECTRICITY_EXPORTED,
104
+ const.ELECTRICITY_PRODUCED,
105
+ const.ELECTRICITY_CONSUMED,
106
+ const.ELECTRICITY_SELF_CONSUMED,
107
+ const.COST_ELECTRICITY_DELIVERED,
108
+ const.EARNINGS_ELECTRICITY_EXPORTED,
109
+ const.COST_ELECTRICITY_NET,
110
+ ]
111
+ ]
112
+ .dropna(axis=1, how="all")
113
+ .resample(freq)
114
+ .sum()
115
+ )
116
+ results[freq] = resampled
117
+
118
+ # Add ratios
119
+ for _, frame in results.items():
120
+ if const.ELECTRICITY_SELF_CONSUMED in frame:
121
+ frame[const.RATIO_SELF_CONSUMPTION] = np.divide(
122
+ frame[const.ELECTRICITY_SELF_CONSUMED], frame[const.ELECTRICITY_PRODUCED]
123
+ )
124
+ frame[const.RATIO_SELF_SUFFICIENCY] = np.divide(
125
+ frame[const.ELECTRICITY_SELF_CONSUMED],
126
+ frame[const.ELECTRICITY_CONSUMED],
127
+ )
128
+ return results
129
+
130
+
131
+ def compare_results(
132
+ res_1: dict[str, pd.DataFrame | pd.Series], res_2: dict[str, pd.DataFrame | pd.Series]
133
+ ) -> dict[str, dict[str, pd.Series | pd.DataFrame]]:
134
+ """Compare two evaluation results and return the differences."""
135
+ results = {}
136
+ for key in res_1.keys():
137
+ if key in res_2:
138
+ df_1 = res_1[key]
139
+ df_2 = res_2[key]
140
+ if isinstance(df_1, pd.Series) and isinstance(df_2, pd.Series):
141
+ df_1 = df_1.to_frame().T
142
+ df_2 = df_2.to_frame().T
143
+ df_1, df_2 = typing.cast(pd.DataFrame, df_1), typing.cast(pd.DataFrame, df_2)
144
+ diff = df_2 - df_1
145
+ results[key] = {}
146
+ results[key]["diff"] = diff.dropna(how="all", axis=1).squeeze(axis=0)
147
+ results[key]["ratio_diff"] = (diff / df_1).dropna(how="all", axis=1).squeeze(axis=0)
148
+ return results
@@ -0,0 +1,162 @@
1
+ """Models for basic energy system evaluation."""
2
+
3
+ from typing import Annotated, Literal, Union
4
+
5
+ from pydantic import BaseModel, Field, RootModel, StringConstraints, confloat, conlist
6
+
7
+ from ..models import TimeDataFrame
8
+
9
+
10
+ class EvaluationInput(TimeDataFrame):
11
+ """Input frame for basic energy system evaluation."""
12
+
13
+ columns: list[
14
+ Literal[
15
+ "electricity_delivered",
16
+ "electricity_exported",
17
+ "electricity_produced",
18
+ "price_electricity_delivered",
19
+ "price_electricity_exported",
20
+ ]
21
+ ] = Field(min_length=1, max_length=5)
22
+ data: list[conlist(item_type=confloat(allow_inf_nan=True), min_length=1, max_length=5)] # type: ignore
23
+
24
+
25
+ class EvaluationOutput(TimeDataFrame):
26
+ """Output frame for basic energy system evaluation."""
27
+
28
+ columns: list[
29
+ Literal[
30
+ "electricity_delivered",
31
+ "electricity_exported",
32
+ "electricity_produced",
33
+ "electricity_consumed",
34
+ "electricity_self_consumed",
35
+ "cost_electricity_delivered",
36
+ "earnings_electricity_exported",
37
+ "cost_electricity_net",
38
+ "ratio_self_consumption",
39
+ "ratio_self_sufficiency",
40
+ ]
41
+ ] = Field(min_length=1, max_length=10)
42
+ data: list[conlist(item_type=confloat(allow_inf_nan=True), min_length=1, max_length=10)] # type: ignore
43
+
44
+
45
+ # ---------- Reusable bits ----------
46
+
47
+ Frequency = Annotated[
48
+ str,
49
+ StringConstraints(pattern=r"^(\d+min|H|D|W(?:-[A-Z]{3})?|MS|M|Q|QS|A|AS)$"),
50
+ Field(
51
+ title="Frequency key",
52
+ description=(
53
+ "Pandas-style frequency string (freqstr). "
54
+ "Typical examples: '15min', 'H', 'D', 'MS', 'W-MON'."
55
+ ),
56
+ examples=["15min", "H", "MS", "W-MON"],
57
+ ),
58
+ ]
59
+
60
+ Metric = Annotated[
61
+ str,
62
+ Field(
63
+ description="Metric identifier.",
64
+ examples=[
65
+ "electricity_delivered",
66
+ "electricity_exported",
67
+ "electricity_produced",
68
+ "electricity_consumed",
69
+ "electricity_self_consumed",
70
+ "cost_electricity_delivered",
71
+ "earnings_electricity_exported",
72
+ "cost_electricity_net",
73
+ "ratio_self_consumption",
74
+ "ratio_self_sufficiency",
75
+ ],
76
+ ),
77
+ ]
78
+
79
+
80
+ class MetricSummary(RootModel[dict[Metric, float]]):
81
+ """Total/aggregate values per metric (e.g. { 'electricity_delivered': 123.4 })."""
82
+
83
+ root: dict[Metric, float]
84
+
85
+
86
+ # ---------- Eval payloads ----------
87
+ # We model “either { 'total': MetricSummary } OR { '<freq>': EvaluationOutput, ... }”
88
+ # as two schemas and present them via oneOf.
89
+
90
+
91
+ class EvalTotals(BaseModel):
92
+ """Totals per metric (no time series)."""
93
+
94
+ total: MetricSummary = Field(
95
+ ..., description="Aggregate totals per metric over the full evaluation window."
96
+ )
97
+
98
+
99
+ class EvalByFrequency(RootModel[dict[Frequency, EvaluationOutput]]):
100
+ """Per-frequency time series results."""
101
+
102
+ root: dict[Frequency, EvaluationOutput]
103
+
104
+
105
+ EvalPayload = Annotated[
106
+ Union[EvalTotals, EvalByFrequency],
107
+ Field(description="Either totals-only, or per-frequency time series results."),
108
+ ]
109
+
110
+ # ---------- Comparison payloads ----------
111
+ # Shape:
112
+ # EITHER { "total": { "diff": MetricSummary } } or { "total": { "ratio_diff": MetricSummary } }
113
+ # OR { "<freq>": { "diff": EvaluationOutput } } or { "<freq>": { "ratio_diff": EvaluationOutput } }
114
+
115
+
116
+ class DiffTotal(BaseModel):
117
+ """Totals-level absolute differences."""
118
+
119
+ diff: MetricSummary = Field(..., description="Absolute differences of totals.")
120
+
121
+
122
+ class RatioDiffTotal(BaseModel):
123
+ """Totals-level relative (ratio) differences."""
124
+
125
+ ratio_diff: MetricSummary = Field(..., description="Relative (ratio) differences of totals.")
126
+
127
+
128
+ class DiffTS(BaseModel):
129
+ """Time series absolute differences at a given frequency."""
130
+
131
+ diff: EvaluationOutput = Field(
132
+ ..., description="Absolute differences as a time series at this frequency."
133
+ )
134
+
135
+
136
+ class RatioDiffTS(BaseModel):
137
+ """Time series relative (ratio) differences at a given frequency."""
138
+
139
+ ratio_diff: EvaluationOutput = Field(
140
+ ..., description="Relative (ratio) differences as a time series at this frequency."
141
+ )
142
+
143
+
144
+ # Enforce exclusivity if you decide to combine diff/ratio into one model; here we keep them separate for clean docs.
145
+
146
+
147
+ class ComparisonTotals(BaseModel):
148
+ """Totals comparison; pick exactly one of diff or ratio_diff by choosing the model."""
149
+
150
+ total: DiffTotal | RatioDiffTotal = Field(..., description="Totals-level comparison.")
151
+
152
+
153
+ class ComparisonByFrequency(RootModel[dict[Frequency, Union[DiffTS, RatioDiffTS]]]):
154
+ """Per-frequency comparison; for each freq, pick diff or ratio_diff."""
155
+
156
+ root: dict[Frequency, DiffTS | RatioDiffTS]
157
+
158
+
159
+ ComparisonPayload = Annotated[
160
+ Union[ComparisonTotals, ComparisonByFrequency],
161
+ Field(description="Comparison results at totals level or per-frequency."),
162
+ ]
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.4
2
+ Name: openenergyid
3
+ Version: 0.1.31
4
+ Summary: Open Source Python library for energy analytics and simulations
5
+ Author-email: Jan Pecinovsky <jan@energieid.be>, Max Helskens <max@energieid.be>, Oscar Swyns <oscar@energieid.be>
6
+ License-Expression: MIT
7
+ Keywords: energy,analytics,simulation
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Natural Language :: English
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Scientific/Engineering
15
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
16
+ Classifier: Topic :: Scientific/Engineering :: Physics
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: aiohttp>=3.12.15
22
+ Requires-Dist: pandera[polars]>=0.22.1
23
+ Requires-Dist: pvlib>=0.13.0
24
+ Requires-Dist: pydantic>=2.8.2
25
+ Requires-Dist: statsmodels>=0.14.2
26
+ Dynamic: license-file
27
+
28
+ # OpenEnergyID
29
+
30
+ Open Source Python library for energy data analytics and simulations
31
+
32
+ [*more info for developers*](DEVELOPERS.md)
@@ -0,0 +1,50 @@
1
+ openenergyid/__init__.py,sha256=W7FsfPYrI4DcjTcIucigfC7QR_cSuAcx2ocomFDAYzc,193
2
+ openenergyid/const.py,sha256=2cRYP2rF8YtW4vP1nnsyn-JBgmTEfVO1tS6tqJvC6_M,1458
3
+ openenergyid/enums.py,sha256=jdw4CB1gkisx0re_SesrTEyh_T-UxYp6uieE7iYlHdA,357
4
+ openenergyid/models.py,sha256=G3xXbdNewcXSBr-vyvg4pE6zdaI44OrJIWQxgKjwvkg,6313
5
+ openenergyid/abstractsim/__init__.py,sha256=cl-Eh5y_ZOmacZ0X2YW4TeooqR-oD68Fju3UsH9Yziw,228
6
+ openenergyid/abstractsim/abstract.py,sha256=dhUNoaHiKH-ug2mcHUxDINbKMKh7VWMiAnEhPAUAFiE,3252
7
+ openenergyid/baseload/__init__.py,sha256=nEN4IX5m1_X11paXt11G-G9_iJ-ypwccc9NyQhkwtAQ,437
8
+ openenergyid/baseload/analysis.py,sha256=B7hlTE0EVJeRXLYaBjLfqoV2yrBJ-3h7jdIu0JXLGQg,8410
9
+ openenergyid/baseload/exceptions.py,sha256=uPPQlFmOikp3wuwdVxj3Mx-45TzPkLF86rKMFjT5qB4,250
10
+ openenergyid/baseload/models.py,sha256=wnqZWIenguLWcI8-YKIDf6LuQhtSrNi1m0P7QeEKgMI,996
11
+ openenergyid/capacity/__init__.py,sha256=TVWtOBEA2h_Fy17TRYUBmxXUgd00-CJcbObjFkvRPwQ,221
12
+ openenergyid/capacity/main.py,sha256=vANuP4PVPWdfSnCZdEVvJD01RQHRzNr1sD1pkQeu29s,3675
13
+ openenergyid/capacity/models.py,sha256=aFarIPXzaaKqfEzYDVXG44uDaPTapqNsxKHFpysc9N8,834
14
+ openenergyid/dyntar/__init__.py,sha256=lUrk7ktS7yAqiafRHFoBE0RvFSI9mzDoO37diwLHuBg,495
15
+ openenergyid/dyntar/const.py,sha256=eJJV9VfpHlS9vWV47DWQkS3ICIXWhDmG4cU-ofbZJ3Q,1100
16
+ openenergyid/dyntar/main.py,sha256=6vdJ_jBdoDg5ec0vevMjdBS-p3DDCe3vlvie_Y4unIE,10639
17
+ openenergyid/dyntar/models.py,sha256=jqLY_sGG19eYQX2YG0btxdUNhMOYu6mkguBqDKxJCwI,3099
18
+ openenergyid/elia/__init__.py,sha256=shcLGBpzMm6r8teov7YwGD424f-um58jCiVM2yeHycQ,126
19
+ openenergyid/elia/api.py,sha256=Azs7D7Jr4zlHerD7Ueo4bs2P6ZwJ-0JfeNuvFEFfm3I,2772
20
+ openenergyid/elia/const.py,sha256=b0dQHVSHR4O9sztN7C07vijTGoAURERthP8pKn-k8gc,442
21
+ openenergyid/energysharing/__init__.py,sha256=A4JfrUYf-hBCzhUm0qL1GGlNMvpO8OwXJo80dJxFIvw,274
22
+ openenergyid/energysharing/const.py,sha256=X2zEPtTlsmZ66w6RmLS_h8NmdzObAEi5N6-0yrLN5V4,219
23
+ openenergyid/energysharing/data_formatting.py,sha256=47OAKDmsxgrChrU0jqCCMf1MTXK0qUU9HV2znevTg2o,2613
24
+ openenergyid/energysharing/main.py,sha256=nLXwhA6omGBIjNJzcOlqTmYWF3uuBqqsAO9dsUKf4xo,4492
25
+ openenergyid/energysharing/models.py,sha256=1GPigtXffO1EsZhFkHG0OYwGXK71I9xLC1fD5rHxKwQ,2527
26
+ openenergyid/mvlr/__init__.py,sha256=QNLdUqzE3lg4hkMc5Z47ft7DFydQnw-BF1LaLpLLcaY,471
27
+ openenergyid/mvlr/helpers.py,sha256=Uzbfrj3IpH26wA206KOl0hNucKE-n9guJNC_EROBVKA,983
28
+ openenergyid/mvlr/main.py,sha256=X7H1lzKx3JYOQkHR0R3HLqHvCGa8PuI2CafvYquhEZI,1491
29
+ openenergyid/mvlr/models.py,sha256=3Y_y3A5FmJxDEYT4XGpXiuUTMS8p0umPMeXNFRufGAY,8580
30
+ openenergyid/mvlr/mvlr.py,sha256=zcVWsKDfcS2jdfa-45QPu3YCnhL4p14FHuWpQLLwIL4,18626
31
+ openenergyid/pvsim/__init__.py,sha256=gVNEFsEGEMz9SqhrUCH91GWEgYGCWH6hwbztO6I-OfM,204
32
+ openenergyid/pvsim/abstract.py,sha256=IjYndlmvi9VTnUQ0vCDOLc5wB9d1jWnbq-e5zq0Br88,1688
33
+ openenergyid/pvsim/main.py,sha256=1Pcdkgg3NgSPtESqhqaJdlw8hoBTiwV8XjNtIzWq2go,1796
34
+ openenergyid/pvsim/elia/__init__.py,sha256=XVynbFXNWjDhNDuyJJoe6CYKq5dk46IW21sFqzfrPTE,113
35
+ openenergyid/pvsim/elia/main.py,sha256=hqA1Kbsk3yUgR_Oe_iGzsSnaqSvpjm971G6zljeteac,2900
36
+ openenergyid/pvsim/pvlib/__init__.py,sha256=07PCnTLDi9yPUCXiI2Fa25KXtw2KdS65ICkgZZnjzj4,244
37
+ openenergyid/pvsim/pvlib/main.py,sha256=ZpjYyK60vr45KWld5gZVIS3xi_9Ze_Ggka_2zzIhgAc,3145
38
+ openenergyid/pvsim/pvlib/models.py,sha256=a3xomqRu7WwKQLTTZ57EsCPGfHQsgFL-ZdwOSG_tjXE,7421
39
+ openenergyid/pvsim/pvlib/quickscan.py,sha256=7DoUiUzPd4vZKQxL_Uo6bCz_THJ-G1ul9Ni-eS5N6RM,3874
40
+ openenergyid/pvsim/pvlib/weather.py,sha256=D2xbkTYg3mtt7GDoz0uu_ML2U6ElsQHjbvWs28q-5tM,3022
41
+ openenergyid/sim/__init__.py,sha256=gKslrJwVB80Tm1s27OJyNKiouq0CcAt-vvPXhsu2sec,198
42
+ openenergyid/sim/main.py,sha256=93owbBfC2XXXnmN8Vq7EqcwV-5sI_oYgbonyHs9NPZE,2261
43
+ openenergyid/simeval/__init__.py,sha256=hk-q7enbBpxi3g2D9LMYmw7R1ikdte8mYIvPLVg0aBs,252
44
+ openenergyid/simeval/main.py,sha256=5pucS9mGKKeQmM5P-cyYdw4FQtyBXfil6MU4ppWTpGQ,5895
45
+ openenergyid/simeval/models.py,sha256=xUUr2NEXEUGzo6Jq2X-aDEWyVjINouPZhpHxxmYucNo,4964
46
+ openenergyid-0.1.31.dist-info/licenses/LICENSE,sha256=NgRdcNHwyXVCXZ8sJwoTp0DCowThJ9LWWl4xhbV1IUY,1074
47
+ openenergyid-0.1.31.dist-info/METADATA,sha256=F_nGe1ktTCiWcKb30IMeQxprRbD_X9j_Gz6NWMRPy4c,1216
48
+ openenergyid-0.1.31.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
49
+ openenergyid-0.1.31.dist-info/top_level.txt,sha256=vf3DmJCXgD_lJjSPFktonUJJT6vEg6KIPCKMPfCWCFI,13
50
+ openenergyid-0.1.31.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 EnergieID cvba-so
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ openenergyid