-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add VDI profiles based on lpagg #50
base: v0.2dev
Are you sure you want to change the base?
Conversation
@uvchik thanks very much, especially the speed looks promising! Not iterating through rows does indeed help a lot :-) I am not 100% sure yet, but I think it comes down to the following: At 15min frequency, Pandas df.resample("D").mean() will use the values between (including) Some more stuff that might need consideration:
There might be more stuff, but these are all minor issues I think. I need some time for more investigation, though, to see if the API fits all the needs that I can come up with. |
|
Here are the notes about what we discussed:
Some more notes that came up while replacing my own implementation in lpagg with yours:
load_curve_house.columns = pd.MultiIndex.from_product(
[
[house["house_type"]],
load_curve_house.columns,
],
names=['house_type', 'energy']
)
house_profiles[house["name"]] = load_curve_house
return pd.concat(
house_profiles.values(), keys=house_profiles.keys(), axis=1,
names=['name']
)
(Please tell me if I should do some of those implementations myself, or if you prefer to do it yourself.) Further discussion:
|
src/demandlib/vdi/regions.py
Outdated
load_curve_house.columns = pd.MultiIndex.from_product( | ||
[ | ||
[house["house_type"]], | ||
load_curve_house.columns, | ||
] | ||
) | ||
house_profiles[house["name"]] = load_curve_house | ||
return pd.concat( | ||
house_profiles.values(), keys=house_profiles.keys(), axis=1 | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My suggestion for named levels:
load_curve_house.columns = pd.MultiIndex.from_product(
[
[house["house_type"]],
load_curve_house.columns,
],
names=['house_type', 'energy']
)
house_profiles[house["name"]] = load_curve_house
return pd.concat(
house_profiles.values(), keys=house_profiles.keys(), axis=1,
names=['name']
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see copyright issues because of the implementation of the standard:
Copyright:
Verein Deutscher Ingenieure e.V.
VDI Standards Department
VDI-Platz 1, 40468 Duesseldorf, Germany
Reproduced with the permission of the Verein Deutscher Ingenieure e.V.,
for non-commercial use only.
@@ -27,7 +27,7 @@ | |||
import demandlib.bdew as bdew | |||
import demandlib.particular_profiles as profiles | |||
|
|||
# The following dictionary is create by "workalendar" | |||
# The following dictionary has been created by "workalendar" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# The following dictionary has been created by "workalendar" | |
# The following dictionary is created by "workalendar" |
I am a bit puzzled here. First, you write that the dictionary has been/ is created using workalendar
but later you define the holidays yourself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I am writing comments here... :-)
Since those lines are commented out, I think this is just to suggest how to use an alternative library, without creating a new dependency for the installation of demandlib. My only suggestion would be to also name https://pypi.org/project/holidays/ as another option. Personally, it took me much too long to stumble across it.
examples/vdi_profile_example.py
Outdated
"N_Pers": 3, | ||
"name": "EFH_{0}".format(n), | ||
"N_WE": 1, | ||
"Q_Heiz_a": 6000, | ||
"TRY": 4, | ||
"copies": 24, | ||
"house_type": "EFH", | ||
"sigma": 4, | ||
"Q_TWW_a": 1500, | ||
"W_a": 5250, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Language: "EFH", "WE", "Heiz" and "TWW" are German, all others should work in English. (First, I misinterpreted WE as weekend.)
Comprehensibility: What does W_a mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has bugged me, too. The reason is sticking to the notation in the norm itself. Even though there is an English version of VDI 4655, that one still uses the German notation. To name a few:
Q: Heat
W: Electrical work
TWW: Domestic hot water
TT: Typical day
a: Year
WE: Flats
Therefore W_a
is the electrical work of one year in kWh.
While this may not be ideal, I'd still argue that sticking to the source notation is very important, at least for the internal code. Making the public API English is something that might be worth considering, as a compromise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, it might be better to use English abbreviations in the code.
I took the liberty to make two changes based on my previous suggestions: Named column levels and the ability to set my own weather file path. These changes help me to use demandlib within lpagg. I think now the most important remaining issue is the ability to define the |
The temperature limit is connected to the efficiency status of the house.
85d4768
to
7cccf52
Compare
I have not tested it yet, but thanks for adding the temperature limits. Though, since you removed my solution to define a custom weather file, I need to ask: Was it rude (or just inconvenient) of me to push to this branch, or did you not agree with my approach? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the verbose inline documentation explaining what the code does. There is just one location where I found a missing piece of information.
More general, I am still not sure about the usage of German abbreviations. It might or might not be advised. (As the English version of the norm is using them, it can at leats be justified.)
src/demandlib/vdi/__init__.py
Outdated
from .dwd_try import find_try_region # noqa: F401 | ||
from .regions import Region # noqa: F401 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from .dwd_try import find_try_region # noqa: F401 | |
from .regions import Region # noqa: F401 | |
from .dwd_try import find_try_region | |
from .regions import Region | |
__all__ == [ | |
"find_try_region", | |
"Region", | |
] |
|
||
Returns | ||
------- | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing documentation of the return value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should use the default temperature limits if they are not defined by the user.
Also, I added my idea for changing the weather data as a suggestion.
summer=house["summer_temperature_limit"], | ||
winter=house["winter_temperature_limit"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
summer=house["summer_temperature_limit"], | |
winter=house["winter_temperature_limit"], | |
summer=house.get("summer_temperature_limit", 15), | |
winter=house.get("winter_temperature_limit", 5), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know how to best improve this, but it feels wrong to define the default values at two places in the code...
seasons=None, | ||
holidays=None, | ||
houses=None, | ||
resample_rule=None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am also adding the code snippets for changing the weather file.
resample_rule=None, | |
resample_rule=None, | |
file_weather=None, |
dictionary need to have the following keys. | ||
resample_rule : str | ||
Time interval to resample the profile e.g. 1H (1 hour) or 15min. | ||
The value will be passed to the pandas resample method. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The value will be passed to the pandas resample method. | |
The value will be passed to the pandas resample method. | |
file_weather : str (optional) | |
Path to a 'test reference year' (TRY) weather file by German DWD | |
(Deutscher Wetterdienst). If None, the file fitting the given | |
try_region will be loaded. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as you adhere to the TRY format, the actual file does not need to be provided by DWD.
self._try_region = try_region | ||
|
||
self._year = year | ||
self.weather = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.weather = None | |
self.weather = None | |
self.file_weather = file_weather |
fn_weather = os.path.join( | ||
os.path.dirname(__file__), | ||
"resources_weather", | ||
"TRY2010_{:02d}_Jahr.dat".format(self._try_region), | ||
) | ||
self.weather = dwd_try.read_dwd_weather_file(fn_weather) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn_weather = os.path.join( | |
os.path.dirname(__file__), | |
"resources_weather", | |
"TRY2010_{:02d}_Jahr.dat".format(self._try_region), | |
) | |
self.weather = dwd_try.read_dwd_weather_file(fn_weather) | |
if self.file_weather is None: | |
self.file_weather = os.path.join( | |
os.path.dirname(__file__), | |
"resources_weather", | |
"TRY2010_{:02d}_Jahr.dat".format(self._try_region), | |
) | |
self.weather = dwd_try.read_dwd_weather_file(self.file_weather) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There seems to be old code that has been commented out instead of being deleted.
dictionary need to have the following keys. | ||
resample_rule : str | ||
Time interval to resample the profile e.g. 1H (1 hour) or 15min. | ||
The value will be passed to the pandas resample method. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long as you adhere to the TRY format, the actual file does not need to be provided by DWD.
# typtage_combinations = settings["typtage_combinations"] | ||
# houses_list = settings["houses_list_VDI"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this old code that has been commented out instead of being deleted?
# if settings.get("zero_summer_heat_demand", None) is not None: | ||
# # Reduze the value of 'F_Heiz_TT' to zero. | ||
# # For modern houses, this eliminates the heat demand in summer | ||
# energy_factors_df.loc[ | ||
# (slice(None), slice(None), "F_Heiz_TT"), ("SWX", "SSX") | ||
# ] = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this old code that has been commented out instead of being deleted?
# df = pd.concat([df_typ, mytime], axis=1).ffill() | ||
# df.drop(df.head(1).index, inplace=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this old code that has been commented out instead of being deleted?
# The typical day calculation inherently does not add up to the | ||
# desired total energy demand of the full year. Here we fix that: | ||
# houses_dict = {h["name"]: h for h in self.houses} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this old code that has been commented out instead of being deleted?
As this is a draft, I marked it as such. Side note: I really like the feature. It's very handy to have generation of time series for SH, DHW, and electricity consumption at hand. |
Co-authored-by: Joris Zimmermann <[email protected]>
For me it is a bit rude to write your code into my approach without any comment. I already wrote improvements and got dozen of merge conflicts. If I know somebody else is working on the code I would push more often to avoid such problems. I do have a different approach for the weather file so I think the best way would be to finish this PR with cleaning the code and adding some tests and then start a new PR for the weather data. That makes it easier to track changes and to focus the discussion. One reason for that is that the original approach is connect to the TRY data. I am fine with adding your own weather data but it must be clear that you really should know what you are doing. |
Please accept my apologies for pushing into this branch, then. While I did leave a small comment about it here, I see how it creates unnecessary merge conflicts. Afterwards I learned how to use the "Suggested Change" feature and will stick to that in the future. |
def read_dwd_weather_file(weather_file_path=None, try_region=None): | ||
"""Read and interpolate 'DWD Testreferenzjahr' files.""" | ||
if weather_file_path is None: | ||
weather_file_path = os.path.join( | ||
os.path.dirname(__file__), | ||
"resources_weather", | ||
"TRY2010_{:02d}_Jahr.dat".format(try_region), | ||
) | ||
# The comments in DWD files before the header are not commented out. | ||
# Thus we have to search for the line with the header information: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Carefully reading the description, it is clear that we are talking about Testreferenzjahr files. However, there are a lot of types of DWD files (i.e. recordings from weather stations), maybe we could abbreviate it to TRY files like DWD does.
Hi there, it has been quite a while, but I am still depending on this implementation with its awesome speed. The changes I require to make it work for me personally are added in my fork and affect the ability to load other weather data. Just leaving the link here for reference: |
|
||
from demandlib import vdi | ||
|
||
# The following dictionary has been created by "workalendar" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# The following dictionary has been created by "workalendar" | |
# The following dictionary can also be created by "workalendar" |
Maybe this is more clear.
@jnettels: Honestly, I'm not really deep into demandlib. I'm willing to review and give feedback. If you have an improved version, you can file a PR against this branch. |
Thanks for your answer. My "improvements" concerning the weather data were already suggested, once with a rude (still sorry about that!) push to this PR, once with the "suggestion" feature. @uvchik already mentioned that he had a different approach for the weather data, so I do not think creating a new PR for that is helpful now. |
The basic code is taken from https://github.com/jnettels/lpagg but most of the code has been revised for modular use and to speed up the code. The lpagg package contains various functionality but only the VDI-profiles and reading the TRY data from DWD is used.
To test the new profiles use the
vdi_profile_example
in the examples section. The parameter of the houses are added within a loop. I used this to multiply the number of houses to check how long it would take to model e.g. 1000 houses. 1000 houses on an hourly base will take less than a minute, which shows that the draft is at least fast enough 🏎️Related to #36
Install this branch to test the code:
It is the first draft, I am open for concrete ideas.