Class: AddElectricVehicleChargingLoad
- Inherits:
-
OpenStudio::Measure::ModelMeasure
- Object
- OpenStudio::Measure::ModelMeasure
- AddElectricVehicleChargingLoad
- Defined in:
- lib/measures/AddElectricVehicleChargingLoad/measure.rb
Overview
******************************************************************************* OpenStudio®, Copyright © Alliance for Sustainable Energy, LLC. See also openstudio.net/license *******************************************************************************
Defined Under Namespace
Classes: EVcharger, ElectricVehicle
Instance Method Summary collapse
-
#arguments(model) ⇒ Object
define the arguments that the user will input.
- #create_ev_sch(model, ev_chargers, max_charging_power, charge_on_sat, charge_on_sun) ⇒ Object
- #create_ev_sch_for_commercial_charge_station(model, ev_chargers, max_charging_power, num_evs, avg_arrival_time, arrival_time_variation_in_mins, avg_charge_hours, charge_time_variation_in_mins, charge_on_sat, charge_on_sun) ⇒ Object
- #create_ev_sch_for_home(model, ev_chargers, max_charging_power, num_evs, start_charge_time, avg_charge_hours, charge_on_sat, charge_on_sun) ⇒ Object
-
#create_ev_sch_for_workplace(model, ev_chargers, max_charging_power, num_evs, avg_arrival_time, arrival_time_variation_in_mins, avg_leave_time, avg_charge_hours, charge_time_variation_in_mins, charge_on_sat, charge_on_sun) ⇒ Object
********************************************* for workplace waitlist is only applicable to workplace.
- #create_ev_sch_single(model, ev_charger, charge_on_sat, charge_on_sun) ⇒ Object
-
#description ⇒ Object
human readable description.
-
#modeler_description ⇒ Object
human readable description of modeling approach.
-
#name ⇒ Object
human readable name.
-
#run(model, runner, user_arguments) ⇒ Object
define what happens when the measure is run.
Instance Method Details
#arguments(model) ⇒ Object
define the arguments that the user will input
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/measures/AddElectricVehicleChargingLoad/measure.rb', line 26 def arguments(model) args = OpenStudio::Measure::OSArgumentVector.new # building use type, choice argument, 'home' or 'workplace' bldg_use_type_chs = OpenStudio::StringVector.new bldg_use_type_chs << 'home' bldg_use_type_chs << 'workplace' bldg_use_type_chs << 'commercial station' bldg_use_type = OpenStudio::Measure::OSArgument.makeChoiceArgument('bldg_use_type', bldg_use_type_chs, true) bldg_use_type.setDisplayName('Building Use Type') bldg_use_type.setDefaultValue('home') args << bldg_use_type # Number of EV chargers num_ev_chargers = OpenStudio::Measure::OSArgument.makeIntegerArgument('num_ev_chargers', true) num_ev_chargers.setDisplayName('Number of EV Chargers') num_ev_chargers.setDefaultValue(1) args << num_ev_chargers # Number of Electric Vehicles num_evs = OpenStudio::Measure::OSArgument.makeIntegerArgument('num_evs', true) num_evs.setDisplayName('Number of Electric Vehicles') num_evs.setDefaultValue(1) args << num_evs # EV charger level, choice argument charger_level_chs = OpenStudio::StringVector.new charger_level_chs << 'Level 1' charger_level_chs << 'Level 2' charger_level_chs << 'DC charger' charger_level_chs << 'Supercharger' charger_level = OpenStudio::Measure::OSArgument.makeChoiceArgument('charger_level', charger_level_chs, true) charger_level.setDisplayName('EV Charger Level') charger_level.setDefaultValue('Level 2') args << charger_level # average arrival time, applicable for workplace only avg_arrival_time = OpenStudio::Measure::OSArgument.makeStringArgument('avg_arrival_time', false) avg_arrival_time.setDisplayName('Average arrival time, applicable for workplace only') avg_arrival_time.setDefaultValue('08:30') args << avg_arrival_time # average arrival time, applicable for workplace only avg_leave_time = OpenStudio::Measure::OSArgument.makeStringArgument('avg_leave_time', false) avg_leave_time.setDisplayName('Average leave time, applicable for workplace only') avg_leave_time.setDefaultValue('17:30') args << avg_leave_time # start charging time, required for home start_charge_time = OpenStudio::Measure::OSArgument.makeStringArgument('start_charge_time', false) start_charge_time.setDisplayName('Start charging time, required for home only') start_charge_time.setDefaultValue('21:00') args << start_charge_time # average needed hours to charge to full. This should vary with the charger level. avg_charge_hours = OpenStudio::Measure::OSArgument.makeDoubleArgument('avg_charge_hours', true) avg_charge_hours.setDisplayName('Average needed hours to charge to full (should vary with charger level)') avg_charge_hours.setDefaultValue(4) args << avg_charge_hours # variation of arrival time in minutes arrival_time_variation_in_mins = OpenStudio::Measure::OSArgument.makeDoubleArgument('arrival_time_variation_in_mins', false) arrival_time_variation_in_mins.setDescription('Actual arrival time can vary a certain period before and after the average arrival time. '\ 'This parameter describes this absolute time delta. '\ 'In other words, average arrival time plus/minus this parameter constitutes the arrival time range. ') arrival_time_variation_in_mins.setDisplayName('Variation of arrival time in minutes') arrival_time_variation_in_mins.setDefaultValue(30) args << arrival_time_variation_in_mins # variation of charge time in minutes charge_time_variation_in_mins = OpenStudio::Measure::OSArgument.makeDoubleArgument('charge_time_variation_in_mins', false) charge_time_variation_in_mins.setDescription('Actual charge time can vary a certain period around the average charge hours. '\ 'This parameter describes this absolute time delta. '\ 'In other words, average charge hours plus/minus this parameter constitutes the charge time range. ') charge_time_variation_in_mins.setDisplayName('Variation of charge time in minutes') charge_time_variation_in_mins.setDefaultValue(60) args << charge_time_variation_in_mins # if EVs are charged on Saturday charge_on_sat = OpenStudio::Measure::OSArgument.makeBoolArgument('charge_on_sat', false) charge_on_sat.setDisplayName('EVs are charged on Saturday') charge_on_sat.setDefaultValue(true) args << charge_on_sat # if EVs are charged on Sunday charge_on_sun = OpenStudio::Measure::OSArgument.makeBoolArgument('charge_on_sun', false) charge_on_sun.setDisplayName('EVs are charged on Sunday') charge_on_sun.setDefaultValue(true) args << charge_on_sun return args end |
#create_ev_sch(model, ev_chargers, max_charging_power, charge_on_sat, charge_on_sun) ⇒ Object
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/measures/AddElectricVehicleChargingLoad/measure.rb', line 267 def create_ev_sch(model, ev_chargers, max_charging_power, charge_on_sat, charge_on_sun) # create the schedule # Creating a schedule:ruleset ev_sch = OpenStudio::Model::ScheduleRuleset.new(model) ev_sch.setName('EV Charging Power Draw') ev_sch.defaultDaySchedule.setName('EV Charging Default') day_start_time = Time.strptime("00:00", '%H:%M') # initial EV load depends on if each charger charges overnight ev_load = 0 # kW ev_chargers.each do |ev_charger| puts "#{ev_charger.name}" puts "ev_charger.occupied_until_time - day_start_time - 24*60*60)/60: #{(ev_charger.occupied_until_time - day_start_time - 24*60*60)/60}" puts "ev_charger.occupied_start_time - day_start_time)/60: #{(ev_charger.occupied_start_time - day_start_time)/60}" puts "ev_charger.occupied_start_time: #{ev_charger.occupied_start_time}" puts "ev_charger.occupied_until_time: #{ev_charger.occupied_until_time}" puts "ev_charger.occupied_start_time.day: #{ev_charger.occupied_start_time.day}" puts "ev_charger.occupied_until_time.day: #{ev_charger.occupied_until_time.day}" ev_load += ev_charger.charging_power if ev_charger.occupied_start_time.day != ev_charger.occupied_until_time.day end ev_load_new = ev_load # kW puts "******Initial******" puts "ev_load: #{ev_load}" puts "ev_load_new: #{ev_load_new}" # iterate through 1440 minutes in one day for min in 1..24*60 ev_chargers.each do |ev_charger| # charging on the same day if ev_charger.occupied_start_time.day == ev_charger.occupied_until_time.day if ((ev_charger.occupied_start_time - day_start_time)/60).to_i == min if ev_load_new == ev_load ev_load_new = ev_load + ev_charger.charging_power else # if more than one chargers change status at this time point ev_load_new += ev_charger.charging_power end elsif ((ev_charger.occupied_until_time - day_start_time)/60).to_i == min if ev_load_new == ev_load ev_load_new = ev_load - ev_charger.charging_power else # if more than one chargers change status at this time point ev_load_new -= ev_charger.charging_power end end else # charging overnight if ((ev_charger.occupied_until_time - day_start_time - 24*60*60)/60).to_i == min if ev_load_new == ev_load ev_load_new = ev_load - ev_charger.charging_power else # if more than one chargers change status at this time point ev_load_new -= ev_charger.charging_power end elsif ((ev_charger.occupied_start_time - day_start_time)/60).to_i == min if ev_load_new == ev_load ev_load_new = ev_load + ev_charger.charging_power else # if more than one chargers change status at this time point ev_load_new += ev_charger.charging_power end end end end # if any change, add to schedule if ev_load_new != ev_load || min == 24*60 puts "****after****" puts "ev_load_new: #{ev_load_new}" puts "ev_load: #{ev_load}" time = OpenStudio::Time.new(0, 0, min) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(time, (ev_load/max_charging_power).round(2)) ev_load = ev_load_new end end if charge_on_sat ev_sch_sat = OpenStudio::Model::ScheduleRule.new(ev_sch, ev_sch.defaultDaySchedule) ev_sch_sat.setName('EV Charging Power Saturday') ev_sch_sat.setApplySaturday(true) else ev_sch_sat_rule = OpenStudio::Model::ScheduleRule.new(ev_sch) ev_sch_sat_rule.setName('EV Charging Power Saturday') ev_sch_sat_rule.setApplySaturday(true) ev_sch_sat = ev_sch_sat_rule.daySchedule ev_sch_sat.setName('EV Charging Saturday') ev_sch_sat.addValue(OpenStudio::Time.new(0,24,0), 0) end if charge_on_sun ev_sch_sun = OpenStudio::Model::ScheduleRule.new(ev_sch, ev_sch.defaultDaySchedule) ev_sch_sun.setName('EV Charging Power Sunday') ev_sch_sun.setApplySunday(true) else ev_sch_sun_rule = OpenStudio::Model::ScheduleRule.new(ev_sch) ev_sch_sun_rule.setName('EV Charging Power Sunday') ev_sch_sun_rule.setApplySunday(true) ev_sch_sun = ev_sch_sun_rule.daySchedule ev_sch_sun.setName('EV Charging Sunday') ev_sch_sun.addValue(OpenStudio::Time.new(0,24,0), 0) end return ev_sch end |
#create_ev_sch_for_commercial_charge_station(model, ev_chargers, max_charging_power, num_evs, avg_arrival_time, arrival_time_variation_in_mins, avg_charge_hours, charge_time_variation_in_mins, charge_on_sat, charge_on_sun) ⇒ Object
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 |
# File 'lib/measures/AddElectricVehicleChargingLoad/measure.rb', line 528 def create_ev_sch_for_commercial_charge_station(model, ev_chargers, max_charging_power, num_evs, avg_arrival_time, arrival_time_variation_in_mins, avg_charge_hours, charge_time_variation_in_mins, charge_on_sat, charge_on_sun) ev_list = [] for j in 1..num_evs ev = ElectricVehicle.new("ev_#{j.to_s}") ev.arrival_time = avg_arrival_time + rand(-arrival_time_variation_in_mins...arrival_time_variation_in_mins) * 60 # TODO make sure time format is working correctly, Ruby Times "+" adopts seconds ev.needed_charge_hours = avg_charge_hours + rand(-charge_time_variation_in_mins...charge_time_variation_in_mins) / 60.0 # +- variation minutes ev_list << ev end # find the earliest arrival time arrival_time_earliest = Time.strptime("23:00", '%H:%M') + 3600 # initial: 24:00 ev_list.each do |this_ev| if this_ev.arrival_time < arrival_time_earliest arrival_time_earliest = this_ev.arrival_time end end # For workplace: iterate through time, check status of each charger, if vacant, find the EV that has the earliest arrival time within uncharged EVs. # if this EV's leaving time is later than the current time, start charging until fully charged or leaving time, whichever comes first # when no EV is found any more, charging on this day ends, conclude the charging profile # 23 represent 23:00-24:00, corresponding to E+ schedule Until: 24:00 ev_sch_list = [] for hour in 0..23 current_time = Time.strptime("#{hour}:00", '%H:%M') + 3600 # %H: 00..23, 23 should represent the period 23:00-24:00, so add 1 hour to be the check point next if arrival_time_earliest > current_time ev_chargers.each do |ev_charger| if ev_charger.occupied if ev_charger.connected_ev.class.to_s != 'AddElectricVehicleChargingLoad::ElectricVehicle' runner.registerError("EV charger #{ev_charger.name.to_s} shows occupied, but no EV is connected.") return false end # disconnect EV if charged to full. Only check if expected end time is earlier than current time, otherwise check in next iteration. # Time addition uses seconds, so needs to multiple 3600 if ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 <= current_time ev_charger.occupied_until_time_list << ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 ev_charger.connected_ev.end_charge_time = ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 ev_charger.occupied = false ev_charger.connected_ev.has_been_charged = true ev_charger.connected_ev.connected_to_charger = false ev_charger.connected_ev = nil end end # continue to check if charger not occupied, then connect to an EV unless ev_charger.occupied next_ev_to_charge = nil wait_list_time_earliest = Time.strptime("23:00", '%H:%M') + 3600 # initial: 24:00 ev_list.each do |this_ev| # skip this EV if it is being charged or is being charged or already left next if this_ev.has_been_charged next if this_ev.connected_to_charger # get the uncharged, earliest arrival EV (so front in wait list) if this_ev.arrival_time < wait_list_time_earliest wait_list_time_earliest = this_ev.arrival_time next_ev_to_charge = this_ev end end # skip if no EV is on the wait list next if next_ev_to_charge.nil? if ev_charger.charged_ev_list.empty? ev_charger.occupied_start_time_list << wait_list_time_earliest next_ev_to_charge.start_charge_time = wait_list_time_earliest else if next_ev_to_charge.arrival_time < ev_charger.occupied_until_time_list[-1] next_ev_to_charge.start_charge_time = ev_charger.occupied_until_time_list[-1] ev_charger.occupied_start_time_list << ev_charger.occupied_until_time_list[-1] else next_ev_to_charge.start_charge_time = next_ev_to_charge.arrival_time ev_charger.occupied_start_time_list << next_ev_to_charge.arrival_time end end ev_charger.occupied = true next_ev_to_charge.connected_to_charger = true ev_charger.connected_ev = next_ev_to_charge ev_charger.charged_ev_list << next_ev_to_charge end end end ev_chargers.each do |ev_charger| # create schedule for each ev_charger # charger.charging_power ev_sch = create_ev_sch_single(model, ev_charger, charge_on_sat, charge_on_sun) ev_sch_list << ev_sch end # ev_sch = create_ev_sch(model, ev_chargers, max_charging_power, charge_on_sat, charge_on_sun) return ev_sch_list end |
#create_ev_sch_for_home(model, ev_chargers, max_charging_power, num_evs, start_charge_time, avg_charge_hours, charge_on_sat, charge_on_sun) ⇒ Object
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 |
# File 'lib/measures/AddElectricVehicleChargingLoad/measure.rb', line 618 def create_ev_sch_for_home(model, ev_chargers, max_charging_power, num_evs, start_charge_time, avg_charge_hours, charge_on_sat, charge_on_sun) ev_list = [] for j in 1..num_evs ev = ElectricVehicle.new("ev_#{j.to_s}") ev.needed_charge_hours = avg_charge_hours + rand(-60...60) / 60.0 # +- 1 hour ev_list << ev end # for homes, EV charging could go overnight, so iterate 48 hours for hour in 0..47 if hour <= 23 current_time = Time.strptime("#{hour}:00", '%H:%M') + 3600 # %H: 00..23, 23 should represent the period 23:00-24:00, so add 1 hour to be the check point else current_time = Time.strptime("#{hour-24}:00", '%H:%M') + 24*60*60 + 3600 # %H: the second day (for overnight). still 00..23, 23 should represent the period 23:00-24:00, so add 1 hour to be the check point end next if start_charge_time > current_time ev_chargers.each do |ev_charger| if ev_charger.occupied if ev_charger.connected_ev.class.to_s != 'AddElectricVehicleChargingLoad::ElectricVehicle' runner.registerError("EV charger #{ev_charger.name.to_s} shows occupied, but no EV is connected.") return false end # Time addition uses seconds, so needs to multiple 3600 if ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 <= current_time ev_charger.connected_ev.end_charge_time = ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 ev_charger.occupied_until_time = ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 ev_charger.occupied = false ev_charger.connected_ev.has_been_charged = true ev_charger.connected_ev.connected_to_charger = false ev_charger.connected_ev = nil end end # continue to check if charger not occupied, then connect to an EV unless ev_charger.occupied # no need of waitlist, just connect to whichever EV that hasn't been charged next_ev_to_charge = nil ev_list.each do |this_ev| # skip this EV if it is being charged or is being charged next if this_ev.has_been_charged next if this_ev.connected_to_charger next_ev_to_charge = this_ev break end # skip if no EV is on the wait list next if next_ev_to_charge.nil? if ev_charger.charged_ev_list.empty? ev_charger.occupied_start_time = start_charge_time next_ev_to_charge.start_charge_time = start_charge_time else next_ev_to_charge.start_charge_time = ev_charger.occupied_until_time end ev_charger.occupied = true next_ev_to_charge.connected_to_charger = true ev_charger.connected_ev = next_ev_to_charge ev_charger.charged_ev_list << next_ev_to_charge end end end ev_sch = create_ev_sch(model, ev_chargers, max_charging_power, charge_on_sat, charge_on_sun) return ev_sch end |
#create_ev_sch_for_workplace(model, ev_chargers, max_charging_power, num_evs, avg_arrival_time, arrival_time_variation_in_mins, avg_leave_time, avg_charge_hours, charge_time_variation_in_mins, charge_on_sat, charge_on_sun) ⇒ Object
********************************************* for workplace waitlist is only applicable to workplace. For homes, charging is scheduled with start_charge_time create all EV chargers
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 |
# File 'lib/measures/AddElectricVehicleChargingLoad/measure.rb', line 445 def create_ev_sch_for_workplace(model, ev_chargers, max_charging_power, num_evs, avg_arrival_time, arrival_time_variation_in_mins, avg_leave_time, avg_charge_hours, charge_time_variation_in_mins, charge_on_sat, charge_on_sun) ev_list = [] for j in 1..num_evs ev = ElectricVehicle.new("ev_#{j.to_s}") ev.arrival_time = avg_arrival_time + rand(-arrival_time_variation_in_mins...arrival_time_variation_in_mins) * 60 # TODO make sure time format is working correctly, Ruby Times "+" adopts seconds ev.leave_time = avg_leave_time + rand(-30...30) * 60 # TODO make sure time format is working correctly, Ruby Times "+" adopts seconds ev.leave_time = Time.strptime("23:00", '%H:%M') + 3600 if ev.leave_time > Time.strptime("23:00", '%H:%M') + 3600 # fix leave time at 24:00 if later than 24:00 ev.needed_charge_hours = avg_charge_hours + rand(-charge_time_variation_in_mins...charge_time_variation_in_mins) / 60.0 # +- variation charge time ev_list << ev end # find the earliest arrival time arrival_time_earliest = Time.strptime("23:00", '%H:%M') + 3600 # initial: 24:00 ev_list.each do |this_ev| if this_ev.arrival_time < arrival_time_earliest arrival_time_earliest = this_ev.arrival_time end end # For workplace: iterate through time, check status of each charger, if vacant, find the EV that has the earliest arrival time within uncharged EVs. # if this EV's leaving time is later than the current time, start charging until fully charged or leaving time, whichever comes first # when no EV is found any more, charging on this day ends, conclude the charging profile # 23 represent 23:00-24:00, corresponding to E+ schedule Until: 24:00 for hour in 0..23 current_time = Time.strptime("#{hour}:00", '%H:%M') + 3600 # %H: 00..23, 23 should represent the period 23:00-24:00, so add 1 hour to be the check point next if arrival_time_earliest > current_time ev_chargers.each do |ev_charger| if ev_charger.occupied if ev_charger.connected_ev.class.to_s != 'AddElectricVehicleChargingLoad::ElectricVehicle' runner.registerError("EV charger #{ev_charger.name.to_s} shows occupied, but no EV is connected.") return false end # disconnect EV if charged to full or till leave time. Only check if expected end time or leave is earlier than current time, otherwise check in next iteration. # Time addition uses seconds, so needs to multiple 3600 if ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 <= current_time || ev_charger.connected_ev.leave_time <= current_time if ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 > ev_charger.connected_ev.leave_time ev_charger.occupied_until_time = ev_charger.connected_ev.leave_time ev_charger.connected_ev.end_charge_time = ev_charger.connected_ev.leave_time else ev_charger.occupied_until_time = ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 ev_charger.connected_ev.end_charge_time = ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 end ev_charger.occupied = false ev_charger.connected_ev.has_been_charged = true ev_charger.connected_ev.connected_to_charger = false ev_charger.connected_ev = nil end end # continue to check if charger not occupied, then connect to an EV unless ev_charger.occupied next_ev_to_charge = nil wait_list_time_earliest = Time.strptime("23:00", '%H:%M') + 3600 # initial: 24:00 ev_list.each do |this_ev| # skip this EV if it is being charged or is being charged or already left next if this_ev.has_been_charged next if this_ev.connected_to_charger next if this_ev.leave_time <= current_time # get the uncharged, earliest arrival EV (so front in wait list) if this_ev.arrival_time < wait_list_time_earliest wait_list_time_earliest = this_ev.arrival_time next_ev_to_charge = this_ev end end # skip if no EV is on the wait list next if next_ev_to_charge.nil? if ev_charger.charged_ev_list.empty? ev_charger.occupied_start_time = wait_list_time_earliest next_ev_to_charge.start_charge_time = wait_list_time_earliest else next_ev_to_charge.start_charge_time = ev_charger.occupied_until_time end ev_charger.occupied = true next_ev_to_charge.connected_to_charger = true ev_charger.connected_ev = next_ev_to_charge ev_charger.charged_ev_list << next_ev_to_charge end end end ev_sch = create_ev_sch(model, ev_chargers, max_charging_power, charge_on_sat, charge_on_sun) return ev_sch end |
#create_ev_sch_single(model, ev_charger, charge_on_sat, charge_on_sun) ⇒ Object
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 |
# File 'lib/measures/AddElectricVehicleChargingLoad/measure.rb', line 365 def create_ev_sch_single(model, ev_charger, charge_on_sat, charge_on_sun) # create the schedule # Creating a schedule:ruleset ev_sch = OpenStudio::Model::ScheduleRuleset.new(model) ev_sch.setName("EV Charging Power Draw for charger #{ev_charger.name.to_s}") ev_sch.defaultDaySchedule.setName("EV Charging Default for charger #{ev_charger.name.to_s}") day_start_time = Time.strptime("00:00", '%H:%M') puts "ev_charger.occupied_start_time_list: #{ev_charger.occupied_start_time_list}" puts "ev_charger.occupied_until_time_list: #{ev_charger.occupied_until_time_list}" occupied_start_time_list = ev_charger.occupied_start_time_list occupied_until_time_list = ev_charger.occupied_until_time_list occupied_start_time_list.each_with_index do |occupied_start_time, idx| occupied_until_time = occupied_until_time_list[idx] if occupied_start_time.day == occupied_until_time.day # charging on the same day if idx > 0 && occupied_start_time == occupied_until_time_list[idx-1] # car charging are continuous without vacancy period end_time = OpenStudio::Time.new(0, 0, ((occupied_until_time - day_start_time)/60).to_i) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(end_time, 1) else # there are vacancy period between cars start_time = OpenStudio::Time.new(0, 0, ((occupied_start_time - day_start_time)/60).to_i) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(start_time, 0) end_time = OpenStudio::Time.new(0, 0, ((occupied_until_time - day_start_time)/60).to_i) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(end_time, 1) end else # charging overnight if idx > 0 && occupied_start_time == occupied_until_time_list[idx-1] # car charging are continuous without vacancy period end_time_1 = OpenStudio::Time.new(0, 24, 0, 0) # first till the end of the day end_time_2 = OpenStudio::Time.new(0, 0, ((occupied_until_time - day_start_time)/60).to_i) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(end_time_1, 1) ev_sch.defaultDaySchedule.addValue(end_time_2, 1) else # there are vacancy period between cars start_time = OpenStudio::Time.new(0, 0, ((occupied_start_time - day_start_time)/60).to_i) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(start_time, 0) end_time_1 = OpenStudio::Time.new(0, 24, 0, 0) # first till the end of the day end_time_2 = OpenStudio::Time.new(0, 0, ((occupied_until_time - day_start_time)/60).to_i) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(end_time_1, 1) ev_sch.defaultDaySchedule.addValue(end_time_2, 1) end end end if charge_on_sat ev_sch_sat = OpenStudio::Model::ScheduleRule.new(ev_sch, ev_sch.defaultDaySchedule) ev_sch_sat.setName('EV Charging Power Saturday') ev_sch_sat.setApplySaturday(true) else ev_sch_sat_rule = OpenStudio::Model::ScheduleRule.new(ev_sch) ev_sch_sat_rule.setName('EV Charging Power Saturday') ev_sch_sat_rule.setApplySaturday(true) ev_sch_sat = ev_sch_sat_rule.daySchedule ev_sch_sat.setName('EV Charging Saturday') ev_sch_sat.addValue(OpenStudio::Time.new(0,24,0), 0) end if charge_on_sun ev_sch_sun = OpenStudio::Model::ScheduleRule.new(ev_sch, ev_sch.defaultDaySchedule) ev_sch_sun.setName('EV Charging Power Sunday') ev_sch_sun.setApplySunday(true) else ev_sch_sun_rule = OpenStudio::Model::ScheduleRule.new(ev_sch) ev_sch_sun_rule.setName('EV Charging Power Sunday') ev_sch_sun_rule.setApplySunday(true) ev_sch_sun = ev_sch_sun_rule.daySchedule ev_sch_sun.setName('EV Charging Sunday') ev_sch_sun.addValue(OpenStudio::Time.new(0,24,0), 0) end return ev_sch end |
#description ⇒ Object
human readable description
16 17 18 |
# File 'lib/measures/AddElectricVehicleChargingLoad/measure.rb', line 16 def description return 'This measure adds electric vehicle charging load to the building. The user can specify the level of charger, number of chargers, number of EVs charging daily, start time, average number of hours to fully charge. ' end |
#modeler_description ⇒ Object
human readable description of modeling approach
21 22 23 |
# File 'lib/measures/AddElectricVehicleChargingLoad/measure.rb', line 21 def modeler_description return 'This measure will add electric vehicle charging load as exterior electric equipment. The user inputs of level of chargers, number of chargers, and number of EVs charging daily will be used to determine the load level, and the inputs of start time and average number of hours to fully charge will be used to determine load schedule.' end |
#name ⇒ Object
human readable name
10 11 12 13 |
# File 'lib/measures/AddElectricVehicleChargingLoad/measure.rb', line 10 def name # Measure name should be the title case of the class name. return 'AddElectricVehicleChargingLoad' end |
#run(model, runner, user_arguments) ⇒ Object
define what happens when the measure is run
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 |
# File 'lib/measures/AddElectricVehicleChargingLoad/measure.rb', line 172 def run(model, runner, user_arguments) super(model, runner, user_arguments) # use the built-in error checking if !runner.validateUserArguments(arguments(model), user_arguments) return false end # assign the user inputs to variables bldg_use_type = runner.getStringArgumentValue('bldg_use_type', user_arguments) num_ev_chargers = runner.getIntegerArgumentValue('num_ev_chargers', user_arguments) num_evs = runner.getIntegerArgumentValue('num_evs', user_arguments) charger_level = runner.getStringArgumentValue('charger_level', user_arguments) avg_arrival_time = runner.getStringArgumentValue('avg_arrival_time', user_arguments) avg_leave_time = runner.getStringArgumentValue('avg_leave_time', user_arguments) start_charge_time = runner.getStringArgumentValue('start_charge_time', user_arguments) avg_charge_hours = runner.getDoubleArgumentValue('avg_charge_hours', user_arguments) arrival_time_variation_in_mins = runner.getDoubleArgumentValue('arrival_time_variation_in_mins', user_arguments) charge_time_variation_in_mins = runner.getDoubleArgumentValue('charge_time_variation_in_mins', user_arguments) charge_on_sat = runner.getBoolArgumentValue('charge_on_sat', user_arguments) charge_on_sun = runner.getBoolArgumentValue('charge_on_sun', user_arguments) puts "num_ev_chargers: #{num_ev_chargers.inspect}" puts "avg_arrival_time: #{avg_arrival_time.inspect}" puts "avg_charge_hours: #{avg_charge_hours.inspect}" puts "charge_on_sat: #{charge_on_sat.inspect}" puts "charge_on_sun: #{charge_on_sun.inspect}" if bldg_use_type == 'workplace' # check avg_arrival_time and avg_leave_time should be in correct Time format begin avg_arrival_time = Time.strptime(avg_arrival_time, '%H:%M') avg_leave_time = Time.strptime(avg_leave_time, '%H:%M') rescue ArgumentError runner.registerError('For workplaces, average arrival and leave time are required, and should be in format of %H:%M, e.g., 16:00.') return false end # check avg_leave_time should be later than avg_arrival_time if avg_leave_time <= avg_arrival_time runner.registerError('For workplaces, average arrival time should be earlier than average leave time.') return false end elsif bldg_use_type == 'home' # check start_charge_time should be in correct Time format begin start_charge_time = Time.strptime(start_charge_time, '%H:%M') rescue ArgumentError runner.registerError('For homes, start charging time is required, and should be in format of %H:%M, e.g., 16:00.') return false end elsif bldg_use_type == 'commercial station' # check avg_arrival_time should be in correct Time format begin avg_arrival_time = Time.strptime(avg_arrival_time, '%H:%M') rescue ArgumentError runner.registerError('For commercial station, average arrival time is required, and should be in format of %H:%M, e.g., 10:00.') return false end else runner.registerError("Wrong building use type, available options: 'workplace' and 'home'.") return false end # report initial condition of model runner.registerInitialCondition("Starting to add electric vehicle to the building.") # Initialize the EV chargers ev_chargers = [] max_charging_power = 0 # initial for i in 1..num_ev_chargers charger = EVcharger.new("EVcharger_#{i.to_s}") charger.level = charger_level # charging power references: # https://calevip.org/electric-vehicle-charging-101 # https://rmi.org/electric-vehicle-charging-for-dummies/ # https://freewiretech.com/difference-between-ev-charging-levels/#:~:text=Level%201%20Charging&text=L1%20chargers%20plug%20directly%20into,is%20sufficient%20for%20many%20commuters. case charger_level when 'Level 1' charger.charging_power = 1.5 when 'Level 2' # charger.charging_power = 7.0 charger.charging_power = 9.6 # C2C expert match input when 'DC charger' # charger.charging_power = 50.0 charger.charging_power = 54.0 # C2C expert match input when 'Supercharger' charger.charging_power = 185 else runner.registerError("Wrong EV charging level, available options: 'Level 1', 'Level 2', 'DC charger', 'Supercharger'.") return false end max_charging_power += charger.charging_power ev_chargers << charger end def create_ev_sch(model, ev_chargers, max_charging_power, charge_on_sat, charge_on_sun) # create the schedule # Creating a schedule:ruleset ev_sch = OpenStudio::Model::ScheduleRuleset.new(model) ev_sch.setName('EV Charging Power Draw') ev_sch.defaultDaySchedule.setName('EV Charging Default') day_start_time = Time.strptime("00:00", '%H:%M') # initial EV load depends on if each charger charges overnight ev_load = 0 # kW ev_chargers.each do |ev_charger| puts "#{ev_charger.name}" puts "ev_charger.occupied_until_time - day_start_time - 24*60*60)/60: #{(ev_charger.occupied_until_time - day_start_time - 24*60*60)/60}" puts "ev_charger.occupied_start_time - day_start_time)/60: #{(ev_charger.occupied_start_time - day_start_time)/60}" puts "ev_charger.occupied_start_time: #{ev_charger.occupied_start_time}" puts "ev_charger.occupied_until_time: #{ev_charger.occupied_until_time}" puts "ev_charger.occupied_start_time.day: #{ev_charger.occupied_start_time.day}" puts "ev_charger.occupied_until_time.day: #{ev_charger.occupied_until_time.day}" ev_load += ev_charger.charging_power if ev_charger.occupied_start_time.day != ev_charger.occupied_until_time.day end ev_load_new = ev_load # kW puts "******Initial******" puts "ev_load: #{ev_load}" puts "ev_load_new: #{ev_load_new}" # iterate through 1440 minutes in one day for min in 1..24*60 ev_chargers.each do |ev_charger| # charging on the same day if ev_charger.occupied_start_time.day == ev_charger.occupied_until_time.day if ((ev_charger.occupied_start_time - day_start_time)/60).to_i == min if ev_load_new == ev_load ev_load_new = ev_load + ev_charger.charging_power else # if more than one chargers change status at this time point ev_load_new += ev_charger.charging_power end elsif ((ev_charger.occupied_until_time - day_start_time)/60).to_i == min if ev_load_new == ev_load ev_load_new = ev_load - ev_charger.charging_power else # if more than one chargers change status at this time point ev_load_new -= ev_charger.charging_power end end else # charging overnight if ((ev_charger.occupied_until_time - day_start_time - 24*60*60)/60).to_i == min if ev_load_new == ev_load ev_load_new = ev_load - ev_charger.charging_power else # if more than one chargers change status at this time point ev_load_new -= ev_charger.charging_power end elsif ((ev_charger.occupied_start_time - day_start_time)/60).to_i == min if ev_load_new == ev_load ev_load_new = ev_load + ev_charger.charging_power else # if more than one chargers change status at this time point ev_load_new += ev_charger.charging_power end end end end # if any change, add to schedule if ev_load_new != ev_load || min == 24*60 puts "****after****" puts "ev_load_new: #{ev_load_new}" puts "ev_load: #{ev_load}" time = OpenStudio::Time.new(0, 0, min) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(time, (ev_load/max_charging_power).round(2)) ev_load = ev_load_new end end if charge_on_sat ev_sch_sat = OpenStudio::Model::ScheduleRule.new(ev_sch, ev_sch.defaultDaySchedule) ev_sch_sat.setName('EV Charging Power Saturday') ev_sch_sat.setApplySaturday(true) else ev_sch_sat_rule = OpenStudio::Model::ScheduleRule.new(ev_sch) ev_sch_sat_rule.setName('EV Charging Power Saturday') ev_sch_sat_rule.setApplySaturday(true) ev_sch_sat = ev_sch_sat_rule.daySchedule ev_sch_sat.setName('EV Charging Saturday') ev_sch_sat.addValue(OpenStudio::Time.new(0,24,0), 0) end if charge_on_sun ev_sch_sun = OpenStudio::Model::ScheduleRule.new(ev_sch, ev_sch.defaultDaySchedule) ev_sch_sun.setName('EV Charging Power Sunday') ev_sch_sun.setApplySunday(true) else ev_sch_sun_rule = OpenStudio::Model::ScheduleRule.new(ev_sch) ev_sch_sun_rule.setName('EV Charging Power Sunday') ev_sch_sun_rule.setApplySunday(true) ev_sch_sun = ev_sch_sun_rule.daySchedule ev_sch_sun.setName('EV Charging Sunday') ev_sch_sun.addValue(OpenStudio::Time.new(0,24,0), 0) end return ev_sch end def create_ev_sch_single(model, ev_charger, charge_on_sat, charge_on_sun) # create the schedule # Creating a schedule:ruleset ev_sch = OpenStudio::Model::ScheduleRuleset.new(model) ev_sch.setName("EV Charging Power Draw for charger #{ev_charger.name.to_s}") ev_sch.defaultDaySchedule.setName("EV Charging Default for charger #{ev_charger.name.to_s}") day_start_time = Time.strptime("00:00", '%H:%M') puts "ev_charger.occupied_start_time_list: #{ev_charger.occupied_start_time_list}" puts "ev_charger.occupied_until_time_list: #{ev_charger.occupied_until_time_list}" occupied_start_time_list = ev_charger.occupied_start_time_list occupied_until_time_list = ev_charger.occupied_until_time_list occupied_start_time_list.each_with_index do |occupied_start_time, idx| occupied_until_time = occupied_until_time_list[idx] if occupied_start_time.day == occupied_until_time.day # charging on the same day if idx > 0 && occupied_start_time == occupied_until_time_list[idx-1] # car charging are continuous without vacancy period end_time = OpenStudio::Time.new(0, 0, ((occupied_until_time - day_start_time)/60).to_i) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(end_time, 1) else # there are vacancy period between cars start_time = OpenStudio::Time.new(0, 0, ((occupied_start_time - day_start_time)/60).to_i) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(start_time, 0) end_time = OpenStudio::Time.new(0, 0, ((occupied_until_time - day_start_time)/60).to_i) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(end_time, 1) end else # charging overnight if idx > 0 && occupied_start_time == occupied_until_time_list[idx-1] # car charging are continuous without vacancy period end_time_1 = OpenStudio::Time.new(0, 24, 0, 0) # first till the end of the day end_time_2 = OpenStudio::Time.new(0, 0, ((occupied_until_time - day_start_time)/60).to_i) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(end_time_1, 1) ev_sch.defaultDaySchedule.addValue(end_time_2, 1) else # there are vacancy period between cars start_time = OpenStudio::Time.new(0, 0, ((occupied_start_time - day_start_time)/60).to_i) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(start_time, 0) end_time_1 = OpenStudio::Time.new(0, 24, 0, 0) # first till the end of the day end_time_2 = OpenStudio::Time.new(0, 0, ((occupied_until_time - day_start_time)/60).to_i) # OpenStudio::Time.new(day,hr of day, minute of hr, seconds of hr?) ev_sch.defaultDaySchedule.addValue(end_time_1, 1) ev_sch.defaultDaySchedule.addValue(end_time_2, 1) end end end if charge_on_sat ev_sch_sat = OpenStudio::Model::ScheduleRule.new(ev_sch, ev_sch.defaultDaySchedule) ev_sch_sat.setName('EV Charging Power Saturday') ev_sch_sat.setApplySaturday(true) else ev_sch_sat_rule = OpenStudio::Model::ScheduleRule.new(ev_sch) ev_sch_sat_rule.setName('EV Charging Power Saturday') ev_sch_sat_rule.setApplySaturday(true) ev_sch_sat = ev_sch_sat_rule.daySchedule ev_sch_sat.setName('EV Charging Saturday') ev_sch_sat.addValue(OpenStudio::Time.new(0,24,0), 0) end if charge_on_sun ev_sch_sun = OpenStudio::Model::ScheduleRule.new(ev_sch, ev_sch.defaultDaySchedule) ev_sch_sun.setName('EV Charging Power Sunday') ev_sch_sun.setApplySunday(true) else ev_sch_sun_rule = OpenStudio::Model::ScheduleRule.new(ev_sch) ev_sch_sun_rule.setName('EV Charging Power Sunday') ev_sch_sun_rule.setApplySunday(true) ev_sch_sun = ev_sch_sun_rule.daySchedule ev_sch_sun.setName('EV Charging Sunday') ev_sch_sun.addValue(OpenStudio::Time.new(0,24,0), 0) end return ev_sch end # ********************************************* # for workplace # waitlist is only applicable to workplace. For homes, charging is scheduled with start_charge_time # create all EV chargers def create_ev_sch_for_workplace(model, ev_chargers, max_charging_power, num_evs, avg_arrival_time, arrival_time_variation_in_mins, avg_leave_time, avg_charge_hours, charge_time_variation_in_mins, charge_on_sat, charge_on_sun) ev_list = [] for j in 1..num_evs ev = ElectricVehicle.new("ev_#{j.to_s}") ev.arrival_time = avg_arrival_time + rand(-arrival_time_variation_in_mins...arrival_time_variation_in_mins) * 60 # TODO make sure time format is working correctly, Ruby Times "+" adopts seconds ev.leave_time = avg_leave_time + rand(-30...30) * 60 # TODO make sure time format is working correctly, Ruby Times "+" adopts seconds ev.leave_time = Time.strptime("23:00", '%H:%M') + 3600 if ev.leave_time > Time.strptime("23:00", '%H:%M') + 3600 # fix leave time at 24:00 if later than 24:00 ev.needed_charge_hours = avg_charge_hours + rand(-charge_time_variation_in_mins...charge_time_variation_in_mins) / 60.0 # +- variation charge time ev_list << ev end # find the earliest arrival time arrival_time_earliest = Time.strptime("23:00", '%H:%M') + 3600 # initial: 24:00 ev_list.each do |this_ev| if this_ev.arrival_time < arrival_time_earliest arrival_time_earliest = this_ev.arrival_time end end # For workplace: iterate through time, check status of each charger, if vacant, find the EV that has the earliest arrival time within uncharged EVs. # if this EV's leaving time is later than the current time, start charging until fully charged or leaving time, whichever comes first # when no EV is found any more, charging on this day ends, conclude the charging profile # 23 represent 23:00-24:00, corresponding to E+ schedule Until: 24:00 for hour in 0..23 current_time = Time.strptime("#{hour}:00", '%H:%M') + 3600 # %H: 00..23, 23 should represent the period 23:00-24:00, so add 1 hour to be the check point next if arrival_time_earliest > current_time ev_chargers.each do |ev_charger| if ev_charger.occupied if ev_charger.connected_ev.class.to_s != 'AddElectricVehicleChargingLoad::ElectricVehicle' runner.registerError("EV charger #{ev_charger.name.to_s} shows occupied, but no EV is connected.") return false end # disconnect EV if charged to full or till leave time. Only check if expected end time or leave is earlier than current time, otherwise check in next iteration. # Time addition uses seconds, so needs to multiple 3600 if ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 <= current_time || ev_charger.connected_ev.leave_time <= current_time if ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 > ev_charger.connected_ev.leave_time ev_charger.occupied_until_time = ev_charger.connected_ev.leave_time ev_charger.connected_ev.end_charge_time = ev_charger.connected_ev.leave_time else ev_charger.occupied_until_time = ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 ev_charger.connected_ev.end_charge_time = ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 end ev_charger.occupied = false ev_charger.connected_ev.has_been_charged = true ev_charger.connected_ev.connected_to_charger = false ev_charger.connected_ev = nil end end # continue to check if charger not occupied, then connect to an EV unless ev_charger.occupied next_ev_to_charge = nil wait_list_time_earliest = Time.strptime("23:00", '%H:%M') + 3600 # initial: 24:00 ev_list.each do |this_ev| # skip this EV if it is being charged or is being charged or already left next if this_ev.has_been_charged next if this_ev.connected_to_charger next if this_ev.leave_time <= current_time # get the uncharged, earliest arrival EV (so front in wait list) if this_ev.arrival_time < wait_list_time_earliest wait_list_time_earliest = this_ev.arrival_time next_ev_to_charge = this_ev end end # skip if no EV is on the wait list next if next_ev_to_charge.nil? if ev_charger.charged_ev_list.empty? ev_charger.occupied_start_time = wait_list_time_earliest next_ev_to_charge.start_charge_time = wait_list_time_earliest else next_ev_to_charge.start_charge_time = ev_charger.occupied_until_time end ev_charger.occupied = true next_ev_to_charge.connected_to_charger = true ev_charger.connected_ev = next_ev_to_charge ev_charger.charged_ev_list << next_ev_to_charge end end end ev_sch = create_ev_sch(model, ev_chargers, max_charging_power, charge_on_sat, charge_on_sun) return ev_sch end def create_ev_sch_for_commercial_charge_station(model, ev_chargers, max_charging_power, num_evs, avg_arrival_time, arrival_time_variation_in_mins, avg_charge_hours, charge_time_variation_in_mins, charge_on_sat, charge_on_sun) ev_list = [] for j in 1..num_evs ev = ElectricVehicle.new("ev_#{j.to_s}") ev.arrival_time = avg_arrival_time + rand(-arrival_time_variation_in_mins...arrival_time_variation_in_mins) * 60 # TODO make sure time format is working correctly, Ruby Times "+" adopts seconds ev.needed_charge_hours = avg_charge_hours + rand(-charge_time_variation_in_mins...charge_time_variation_in_mins) / 60.0 # +- variation minutes ev_list << ev end # find the earliest arrival time arrival_time_earliest = Time.strptime("23:00", '%H:%M') + 3600 # initial: 24:00 ev_list.each do |this_ev| if this_ev.arrival_time < arrival_time_earliest arrival_time_earliest = this_ev.arrival_time end end # For workplace: iterate through time, check status of each charger, if vacant, find the EV that has the earliest arrival time within uncharged EVs. # if this EV's leaving time is later than the current time, start charging until fully charged or leaving time, whichever comes first # when no EV is found any more, charging on this day ends, conclude the charging profile # 23 represent 23:00-24:00, corresponding to E+ schedule Until: 24:00 ev_sch_list = [] for hour in 0..23 current_time = Time.strptime("#{hour}:00", '%H:%M') + 3600 # %H: 00..23, 23 should represent the period 23:00-24:00, so add 1 hour to be the check point next if arrival_time_earliest > current_time ev_chargers.each do |ev_charger| if ev_charger.occupied if ev_charger.connected_ev.class.to_s != 'AddElectricVehicleChargingLoad::ElectricVehicle' runner.registerError("EV charger #{ev_charger.name.to_s} shows occupied, but no EV is connected.") return false end # disconnect EV if charged to full. Only check if expected end time is earlier than current time, otherwise check in next iteration. # Time addition uses seconds, so needs to multiple 3600 if ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 <= current_time ev_charger.occupied_until_time_list << ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 ev_charger.connected_ev.end_charge_time = ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 ev_charger.occupied = false ev_charger.connected_ev.has_been_charged = true ev_charger.connected_ev.connected_to_charger = false ev_charger.connected_ev = nil end end # continue to check if charger not occupied, then connect to an EV unless ev_charger.occupied next_ev_to_charge = nil wait_list_time_earliest = Time.strptime("23:00", '%H:%M') + 3600 # initial: 24:00 ev_list.each do |this_ev| # skip this EV if it is being charged or is being charged or already left next if this_ev.has_been_charged next if this_ev.connected_to_charger # get the uncharged, earliest arrival EV (so front in wait list) if this_ev.arrival_time < wait_list_time_earliest wait_list_time_earliest = this_ev.arrival_time next_ev_to_charge = this_ev end end # skip if no EV is on the wait list next if next_ev_to_charge.nil? if ev_charger.charged_ev_list.empty? ev_charger.occupied_start_time_list << wait_list_time_earliest next_ev_to_charge.start_charge_time = wait_list_time_earliest else if next_ev_to_charge.arrival_time < ev_charger.occupied_until_time_list[-1] next_ev_to_charge.start_charge_time = ev_charger.occupied_until_time_list[-1] ev_charger.occupied_start_time_list << ev_charger.occupied_until_time_list[-1] else next_ev_to_charge.start_charge_time = next_ev_to_charge.arrival_time ev_charger.occupied_start_time_list << next_ev_to_charge.arrival_time end end ev_charger.occupied = true next_ev_to_charge.connected_to_charger = true ev_charger.connected_ev = next_ev_to_charge ev_charger.charged_ev_list << next_ev_to_charge end end end ev_chargers.each do |ev_charger| # create schedule for each ev_charger # charger.charging_power ev_sch = create_ev_sch_single(model, ev_charger, charge_on_sat, charge_on_sun) ev_sch_list << ev_sch end # ev_sch = create_ev_sch(model, ev_chargers, max_charging_power, charge_on_sat, charge_on_sun) return ev_sch_list end def create_ev_sch_for_home(model, ev_chargers, max_charging_power, num_evs, start_charge_time, avg_charge_hours, charge_on_sat, charge_on_sun) ev_list = [] for j in 1..num_evs ev = ElectricVehicle.new("ev_#{j.to_s}") ev.needed_charge_hours = avg_charge_hours + rand(-60...60) / 60.0 # +- 1 hour ev_list << ev end # for homes, EV charging could go overnight, so iterate 48 hours for hour in 0..47 if hour <= 23 current_time = Time.strptime("#{hour}:00", '%H:%M') + 3600 # %H: 00..23, 23 should represent the period 23:00-24:00, so add 1 hour to be the check point else current_time = Time.strptime("#{hour-24}:00", '%H:%M') + 24*60*60 + 3600 # %H: the second day (for overnight). still 00..23, 23 should represent the period 23:00-24:00, so add 1 hour to be the check point end next if start_charge_time > current_time ev_chargers.each do |ev_charger| if ev_charger.occupied if ev_charger.connected_ev.class.to_s != 'AddElectricVehicleChargingLoad::ElectricVehicle' runner.registerError("EV charger #{ev_charger.name.to_s} shows occupied, but no EV is connected.") return false end # Time addition uses seconds, so needs to multiple 3600 if ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 <= current_time ev_charger.connected_ev.end_charge_time = ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 ev_charger.occupied_until_time = ev_charger.connected_ev.start_charge_time + ev_charger.connected_ev.needed_charge_hours * 3600 ev_charger.occupied = false ev_charger.connected_ev.has_been_charged = true ev_charger.connected_ev.connected_to_charger = false ev_charger.connected_ev = nil end end # continue to check if charger not occupied, then connect to an EV unless ev_charger.occupied # no need of waitlist, just connect to whichever EV that hasn't been charged next_ev_to_charge = nil ev_list.each do |this_ev| # skip this EV if it is being charged or is being charged next if this_ev.has_been_charged next if this_ev.connected_to_charger next_ev_to_charge = this_ev break end # skip if no EV is on the wait list next if next_ev_to_charge.nil? if ev_charger.charged_ev_list.empty? ev_charger.occupied_start_time = start_charge_time next_ev_to_charge.start_charge_time = start_charge_time else next_ev_to_charge.start_charge_time = ev_charger.occupied_until_time end ev_charger.occupied = true next_ev_to_charge.connected_to_charger = true ev_charger.connected_ev = next_ev_to_charge ev_charger.charged_ev_list << next_ev_to_charge end end end ev_sch = create_ev_sch(model, ev_chargers, max_charging_power, charge_on_sat, charge_on_sun) return ev_sch end # create EV load schedule (normalized) case bldg_use_type when 'workplace' ev_sch = create_ev_sch_for_workplace(model, ev_chargers, max_charging_power, num_evs, avg_arrival_time, arrival_time_variation_in_mins, avg_leave_time, avg_charge_hours, charge_time_variation_in_mins, charge_on_sat, charge_on_sun) when 'home' ev_sch = create_ev_sch_for_home(model, ev_chargers, max_charging_power, num_evs, start_charge_time, avg_charge_hours, charge_on_sat, charge_on_sun) when 'commercial station' ev_sch_list = create_ev_sch_for_commercial_charge_station(model, ev_chargers, max_charging_power, num_evs, avg_arrival_time, arrival_time_variation_in_mins, avg_charge_hours, charge_time_variation_in_mins, charge_on_sat, charge_on_sun) end case bldg_use_type when 'workplace', 'home' # Adding an EV charger definition and instance for the regular EV charging. ev_charger_def = OpenStudio::Model::ExteriorFuelEquipmentDefinition.new(model) ev_charger_level = (max_charging_power * 1000).round(0) # Converting from kW to watts ev_charger_def.setName("#{ev_charger_level}w EV Charging Definition") ev_charger_def.setDesignLevel(ev_charger_level) # creating EV charger object for the regular EV charging. ev_charger = OpenStudio::Model::ExteriorFuelEquipment.new(ev_charger_def, ev_sch) ev_charger.setName("#{ev_charger_level}w EV Charger") ev_charger.setFuelType('Electricity') ev_charger.setEndUseSubcategory('Electric Vehicles') when 'commercial station' ev_chargers.each_with_index do |ev_charger, idx| # Adding an EV charger definition and instance for the regular EV charging. ev_charger_def = OpenStudio::Model::ExteriorFuelEquipmentDefinition.new(model) ev_charger_level = (ev_charger.charging_power * 1000).round(0) # Converting from kW to watts ev_charger_def.setName("#{ev_charger_level}w EV Charging Definition") ev_charger_def.setDesignLevel(ev_charger_level) # creating EV charger object for the regular EV charging. ev_charger = OpenStudio::Model::ExteriorFuelEquipment.new(ev_charger_def, ev_sch_list[idx]) ev_charger.setName("#{ev_charger_level}w EV Charger") ev_charger.setFuelType('Electricity') ev_charger.setEndUseSubcategory('Electric Vehicles') end end runner.registerInfo("multiplier (kW) = #{max_charging_power}}") # echo the new space's name back to the user runner.registerInfo("EV load with #{num_ev_chargers} EV chargers and #{num_evs} EVs was added.") # report final condition of model runner.registerFinalCondition("The building completed adding EV load.") return true end |