-
Notifications
You must be signed in to change notification settings - Fork 17
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
Generate EV schedules #1757
base: ev_batteries
Are you sure you want to change the base?
Generate EV schedules #1757
Conversation
… into ev_schedules # Conflicts: # BuildResidentialHPXML/measure.xml # BuildResidentialScheduleFile/measure.xml # HPXMLtoOpenStudio/measure.xml
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.
Some initial comments/questions. I didn't scrutinize the actual methodology/engineering behind how the EV schedule is created.
EVOccupant: Column.new('ev_occupant', true, true, :int), | ||
PresentOccupants: Column.new('present_occupants', true, true, :int), |
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.
Why do we need these two new columns? As far as I can tell they aren't used, and they seem confusing.
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.
EVOccupant is the occupancy schedule of the occupant that is driving the EV.
PresentOccupants is the occupancy of all occupants.
EVOccupant column can be used, for example, to simulate demand response for EV where EV charging is delayed. Without knowing the EV occupant schedule it won't be possible to align charging with the time the occupant is home.
PresentOccupants is mostly for debugging/postprocessing purpose. I have used this to generate graphics like this below where I am looking at the away hours of EV occupants compared to away hours of all occupants.
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. Thanks for the description. Would it cause any issues for your use case if we only exported these columns when the debug
flag is used?
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.
Yeah, I think we can switch to using debug flag for outputting these. Or, can this be controlled through output variables?
if col2path[col_name] == schedules_path | ||
fail "Schedule column name '#{col_name}' is duplicated. [context: #{schedules_path}]" | ||
else | ||
@runner.registerWarning("Schedule column name '#{col_name}' already exist in #{col2path[col_name]}. Overwriting with #{schedules_path}.") | ||
end |
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.
If a column name shows up twice in the same file, it's an error, but if it shows up in different files, the last file is used? Do I have that right? I don't understand why we would want to treat the two situations differently.
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.
Yeah, you got that right. But I am unsure myself why I needed to add this. I will come back to this!
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 can't recall why I had to add this, and this change is also failing unit test. So, I am going to revert it for now.
if @hpxml_bldg.vehicles.to_a.empty? | ||
return | ||
end |
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'm a bit confused. I see that no ev_battery_charging
and ev_battery_charging
columns are created when there's no EV. This is different than other things (like clothes washers), where we create schedules even when the appliance is not present. Why?
It's particularly confusing that when you have no EV, you still get an ev_occupant
(and present_occupants
) columns. Why?
Finally, I ran this measure on the base-battery-ev.xml
file and it's showing 7 occupants?! The HPXML file has 3 bedrooms and doesn't have the number of occupants specified. Where did 7 come from?
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.
Yeah, this is the part we need to decide one way or the other. For other appliance, we can just generate the schedule based on the markov chain. For EV it's not so straight forward since it needs annual driving hours as an input to generate the schedule but that's not available if there is no EV in the HPXML. So, currently, I am not generating the EV schedule. But, yes, this is inconsistent with what we do with other appliances. We do have that input from ResStock though (even if there is no EV) - it's just not available in the HPXML file. We need to find a solution here.
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.
Regarding the 7 in "present_occupants", this is a bit tricky!
present_occupants column stores the occupancy information for each occupant independently in binary form. So, bit0 is for first occupant, bit1 is for second occupant, and bit3 for third occupant. When all three occupants are present the number is 0b00000111, which is 7. This is a solution I used instead of generating variable number of columns (occupant_1, occupant_2, ...) to store occupancy of each occupant. Yes, I realize this is a bit complicated and different than other columns so open to suggestion (including the suggestion to remove this column). I personally do like it though since it allows full visibility into occupancy of all occupants.
And extracting individual occupancy from this is simply:
for i in range(int(num_occupants)):
df[f'occupant_{i+1}_present'] = df['present_occupants'].map(lambda x: int(x) & (1 << i))
…o ev_schedules
… into ev_schedules # Conflicts: # HPXMLtoOpenStudio/measure.xml
Pull Request Description
We leverage the existing American Time Use Survey (ATUS) guided stochastic occupancy generator to generate the EV battery charging and discharging schedule. The schedule generator takes total hours driven per week as an input to the EV schedule generator and produces the charging and discharging schedule. The battery model takes care of the actual power draw during the charging and discharging period. An example of what these schedules look like is presented in Figure 1. This schedule shows that during an “away period” for the occupant, the vehicle is discharged after the occupant leaves the home. Then later in the day the vehicle is discharged again while the occupant travels home.
Figure 1 Illustration of EV battery discharge schedule generation.
Discharging Schedule Generation
The guiding theme for the discharge schedule is the assumption that the EV is owned/associated with just one of occupant in the house. We look at the away duration for each occupant and eliminate any occupant whose away hours would not be sufficient to fit the given number of driving hours per year. Then we randomly pick one of the occupant out of the eligible occupant. If no occupant happens to have sufficient away hours then we pick the occupant with the highest away hours and truncate the driving hours to whatever hours the occupant away schedule supports.
Once the occupant is chosen, we assume that the EV battery discharge will start immediately after they leave the home, and immediately before they arrive – simulating the scenario that they left the home driving the EV, stayed somewhere for a while and then returned home. Following that assumption, we sum the total away duration for the occupant, and proportionally assign the driving hours to each away period. The driving hours assigned to each away period is distributed symmetrically at the start and end – always making sure to leave the center 20% as idle to allow for some idle duration at the destination away from home. This algorithm is illustrated in the Figure 1.
Charging Schedule Generation
Currently, we assume that the occupant plugs in their vehicle as soon as they are home and keeps it plugged in until they leave. So, the charging schedule is identical to the occupancy schedule of the EV owning occupant. This makes the battery eligible to be charged anytime the EV is at home if the state of charge is below 100%. Whether or not the charging happens and at what power level is managed by the battery model. The TEMPO model makes similar assumptions in its “immediate” or “ASAP” charging strategy, as described in Yip et al. (2023).
Handling of fractional charging at home
Not all EV owners charge exclusively at home. RECS 2020 survey has a question about what fraction of their charging is done at home with options being 100%, 80 to 99%, 60 to 79% etc. This diversity of charging behavior is handled by proportionally reducing the driving hours and calculating effective driving hours. The underlying assumption in using this data is that respondents answered the fraction of their charging in terms of energy, as opposed to time. The "hours driven per week" fed into the schedule generator is assumed to have already applied this adjustment.
Checklist
Not all may apply:
EPvalidator.xml
) has been updatedopenstudio tasks.rb update_hpxmls
)HPXMLtoOpenStudio/tests/test*.rb
and/orworkflow/tests/test*.rb
)openstudio tasks.rb update_measures
has been run