Skip to content
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

Draft
wants to merge 51 commits into
base: ev_batteries
Choose a base branch
from
Draft

Generate EV schedules #1757

wants to merge 51 commits into from

Conversation

rajeee
Copy link
Collaborator

@rajeee rajeee commented Jun 20, 2024

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.

image
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:

  • Schematron validator (EPvalidator.xml) has been updated
  • Sample files have been added/updated (openstudio tasks.rb update_hpxmls)
  • Tests have been added/updated (e.g., HPXMLtoOpenStudio/tests/test*.rb and/or workflow/tests/test*.rb)
  • Documentation has been updated
  • Changelog has been updated
  • openstudio tasks.rb update_measures has been run
  • No unexpected changes to simulation results of sample files

@shorowit shorowit added the enhancement New feature or request label Jul 11, 2024
@aspeake aspeake mentioned this pull request Sep 6, 2024
6 tasks
@rajeee rajeee requested a review from shorowit November 12, 2024 18:40
… into ev_schedules

# Conflicts:
#	BuildResidentialHPXML/measure.xml
#	BuildResidentialScheduleFile/measure.xml
#	HPXMLtoOpenStudio/measure.xml
Copy link
Contributor

@shorowit shorowit left a 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.

Comment on lines 1019 to 1020
EVOccupant: Column.new('ev_occupant', true, true, :int),
PresentOccupants: Column.new('present_occupants', true, true, :int),
Copy link
Contributor

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.

Copy link
Collaborator Author

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.
image

Copy link
Contributor

@shorowit shorowit Dec 11, 2024

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?

Copy link
Collaborator Author

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?

Comment on lines 1144 to 1148
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
Copy link
Contributor

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.

Copy link
Collaborator Author

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!

Copy link
Collaborator Author

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.

Comment on lines 1180 to 1182
if @hpxml_bldg.vehicles.to_a.empty?
return
end
Copy link
Contributor

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?

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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))

BuildResidentialScheduleFile/resources/schedules.rb Outdated Show resolved Hide resolved
@aspeake aspeake mentioned this pull request Jan 7, 2025
7 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Status: In progress
Development

Successfully merging this pull request may close these issues.

3 participants