diff --git a/HPXMLtoOpenStudio/measure.xml b/HPXMLtoOpenStudio/measure.xml
index 9789069849..00973803a7 100644
--- a/HPXMLtoOpenStudio/measure.xml
+++ b/HPXMLtoOpenStudio/measure.xml
@@ -3,8 +3,8 @@
3.1
hpxm_lto_openstudio
b1543b30-9465-45ff-ba04-1d1f85e763bc
- b47e5bc1-d61f-47e6-a666-87ee96ba5a14
- 2025-01-14T17:40:54Z
+ b037a307-ab67-4971-9b3b-116d5b731eb6
+ 2025-01-14T20:21:44Z
D8922A73
HPXMLtoOpenStudio
HPXML to OpenStudio Translator
@@ -363,7 +363,7 @@
hpxml.rb
rb
resource
- E35D68B4
+ 9CB0E51C
hpxml_schema/HPXML.xsd
@@ -645,7 +645,7 @@
vehicle.rb
rb
resource
- A355B556
+ E49A8C65
version.rb
@@ -771,7 +771,7 @@
test_validation.rb
rb
test
- 0062A418
+ 5DB1F814
test_vehicle.rb
diff --git a/HPXMLtoOpenStudio/resources/hpxml.rb b/HPXMLtoOpenStudio/resources/hpxml.rb
index 3ffd081e49..8f62c40311 100644
--- a/HPXMLtoOpenStudio/resources/hpxml.rb
+++ b/HPXMLtoOpenStudio/resources/hpxml.rb
@@ -9411,13 +9411,12 @@ def to_doc(building)
sys_id = XMLHelper.add_element(vehicle, 'SystemIdentifier')
XMLHelper.add_attribute(sys_id, 'id', @id)
vehicle_type_element = XMLHelper.add_element(vehicle, 'VehicleType')
+ vehicle_type = XMLHelper.add_element(vehicle_type_element, @vehicle_type)
case @vehicle_type
when HPXML::VehicleTypeBEV
- electric_vehicle = XMLHelper.add_element(vehicle_type_element, @vehicle_type)
- battery = XMLHelper.add_element(electric_vehicle, 'Battery')
-
# Battery
+ battery = XMLHelper.add_element(vehicle_type, 'Battery')
XMLHelper.add_element(battery, 'BatteryType', @battery_type, :string, @battery_type_isdefaulted) unless @battery_type.nil?
if not @nominal_capacity_kwh.nil?
nominal_capacity = XMLHelper.add_element(battery, 'NominalCapacity')
@@ -9443,16 +9442,16 @@ def to_doc(building)
XMLHelper.add_extension(battery, 'LifetimeModel', @lifetime_model, :string, @lifetime_model_isdefaulted) unless @lifetime_model.nil?
# Battery-Electric Vehicle
- fraction_charged_location = XMLHelper.add_element(electric_vehicle, 'FractionChargedLocation') unless @fraction_charged_home.nil?
+ fraction_charged_location = XMLHelper.add_element(vehicle_type, 'FractionChargedLocation') unless @fraction_charged_home.nil?
XMLHelper.add_element(fraction_charged_location, 'Location', HPXML::ElectricVehicleChargingLocation, :string) unless @fraction_charged_home.nil?
XMLHelper.add_element(fraction_charged_location, 'Percentage', @fraction_charged_home, :float, @fraction_charged_home_isdefaulted) unless @fraction_charged_home.nil?
if not @ev_charger_idref.nil?
- charger = XMLHelper.add_element(electric_vehicle, 'ConnectedCharger')
+ charger = XMLHelper.add_element(vehicle_type, 'ConnectedCharger')
XMLHelper.add_attribute(charger, 'idref', @ev_charger_idref)
end
- XMLHelper.add_extension(electric_vehicle, 'WeekdayScheduleFractions', @ev_charging_weekday_fractions, :string, @ev_charging_weekday_fractions_isdefaulted) unless @ev_charging_weekday_fractions.nil?
- XMLHelper.add_extension(electric_vehicle, 'WeekendScheduleFractions', @ev_charging_weekend_fractions, :string, @ev_charging_weekend_fractions_isdefaulted) unless @ev_charging_weekend_fractions.nil?
- XMLHelper.add_extension(electric_vehicle, 'MonthlyScheduleMultipliers', @ev_charging_monthly_multipliers, :string, @ev_charging_monthly_multipliers_isdefaulted) unless @ev_charging_monthly_multipliers.nil?
+ XMLHelper.add_extension(vehicle_type, 'WeekdayScheduleFractions', @ev_charging_weekday_fractions, :string, @ev_charging_weekday_fractions_isdefaulted) unless @ev_charging_weekday_fractions.nil?
+ XMLHelper.add_extension(vehicle_type, 'WeekendScheduleFractions', @ev_charging_weekend_fractions, :string, @ev_charging_weekend_fractions_isdefaulted) unless @ev_charging_weekend_fractions.nil?
+ XMLHelper.add_extension(vehicle_type, 'MonthlyScheduleMultipliers', @ev_charging_monthly_multipliers, :string, @ev_charging_monthly_multipliers_isdefaulted) unless @ev_charging_monthly_multipliers.nil?
end
# Vehicle
@@ -9476,19 +9475,21 @@ def from_doc(vehicle)
@fuel_economy = XMLHelper.get_value(vehicle, 'FuelEconomyCombined/Value', :float)
@fuel_economy_units = XMLHelper.get_value(vehicle, 'FuelEconomyCombined/Units', :string)
@vehicle_type = XMLHelper.get_child_name(vehicle, 'VehicleType')
- battery_prefix = "VehicleType/#{@vehicle_type}/Battery"
- @battery_type = XMLHelper.get_value(vehicle, "#{battery_prefix}/BatteryType", :string)
- @nominal_capacity_kwh = XMLHelper.get_value(vehicle, "#{battery_prefix}/NominalCapacity[Units='#{UnitsKwh}']/Value", :float)
- @nominal_capacity_ah = XMLHelper.get_value(vehicle, "#{battery_prefix}/NominalCapacity[Units='#{UnitsAh}']/Value", :float)
- @usable_capacity_kwh = XMLHelper.get_value(vehicle, "#{battery_prefix}/UsableCapacity[Units='#{UnitsKwh}']/Value", :float)
- @usable_capacity_ah = XMLHelper.get_value(vehicle, "#{battery_prefix}/UsableCapacity[Units='#{UnitsAh}']/Value", :float)
- @nominal_voltage = XMLHelper.get_value(vehicle, "#{battery_prefix}/NominalVoltage", :float)
- @fraction_charged_home = XMLHelper.get_value(vehicle, "VehicleType/#{@vehicle_type}/FractionChargedLocation/Percentage", :float)
- @ev_charger_idref = HPXML::get_idref(XMLHelper.get_element(vehicle, "VehicleType/#{@vehicle_type}/ConnectedCharger"))
- @lifetime_model = XMLHelper.get_value(vehicle, "#{battery_prefix}/extension/LifetimeModel", :string)
- @ev_charging_weekday_fractions = XMLHelper.get_value(vehicle, "VehicleType/#{@vehicle_type}/extension/WeekdayScheduleFractions", :string)
- @ev_charging_weekend_fractions = XMLHelper.get_value(vehicle, "VehicleType/#{@vehicle_type}/extension/WeekendScheduleFractions", :string)
- @ev_charging_monthly_multipliers = XMLHelper.get_value(vehicle, "VehicleType/#{@vehicle_type}/extension/MonthlyScheduleMultipliers", :string)
+ if @vehicle_type == HPXML::VehicleTypeBEV
+ battery_prefix = "VehicleType/#{@vehicle_type}/Battery"
+ @battery_type = XMLHelper.get_value(vehicle, "#{battery_prefix}/BatteryType", :string)
+ @nominal_capacity_kwh = XMLHelper.get_value(vehicle, "#{battery_prefix}/NominalCapacity[Units='#{UnitsKwh}']/Value", :float)
+ @nominal_capacity_ah = XMLHelper.get_value(vehicle, "#{battery_prefix}/NominalCapacity[Units='#{UnitsAh}']/Value", :float)
+ @usable_capacity_kwh = XMLHelper.get_value(vehicle, "#{battery_prefix}/UsableCapacity[Units='#{UnitsKwh}']/Value", :float)
+ @usable_capacity_ah = XMLHelper.get_value(vehicle, "#{battery_prefix}/UsableCapacity[Units='#{UnitsAh}']/Value", :float)
+ @nominal_voltage = XMLHelper.get_value(vehicle, "#{battery_prefix}/NominalVoltage", :float)
+ @fraction_charged_home = XMLHelper.get_value(vehicle, "VehicleType/#{@vehicle_type}/FractionChargedLocation/Percentage", :float)
+ @ev_charger_idref = HPXML::get_idref(XMLHelper.get_element(vehicle, "VehicleType/#{@vehicle_type}/ConnectedCharger"))
+ @lifetime_model = XMLHelper.get_value(vehicle, "#{battery_prefix}/extension/LifetimeModel", :string)
+ @ev_charging_weekday_fractions = XMLHelper.get_value(vehicle, "VehicleType/#{@vehicle_type}/extension/WeekdayScheduleFractions", :string)
+ @ev_charging_weekend_fractions = XMLHelper.get_value(vehicle, "VehicleType/#{@vehicle_type}/extension/WeekendScheduleFractions", :string)
+ @ev_charging_monthly_multipliers = XMLHelper.get_value(vehicle, "VehicleType/#{@vehicle_type}/extension/MonthlyScheduleMultipliers", :string)
+ end
end
# Returns the EV charger for the vehicle.
diff --git a/HPXMLtoOpenStudio/resources/vehicle.rb b/HPXMLtoOpenStudio/resources/vehicle.rb
index 037b2762c0..37e05a9f1f 100644
--- a/HPXMLtoOpenStudio/resources/vehicle.rb
+++ b/HPXMLtoOpenStudio/resources/vehicle.rb
@@ -13,8 +13,10 @@ class Vehicle
# @return [nil]
def self.apply(runner, model, spaces, hpxml_bldg, schedules_file)
hpxml_bldg.vehicles.each do |vehicle|
- next unless vehicle.vehicle_type == HPXML::VehicleTypeBEV
-
+ if vehicle.vehicle_type != HPXML::VehicleTypeBEV
+ runner.registerWarning("Unexpected vehicle type '#{vehicle.vehicle_type}'. Detailed vehicle charging will not be modeled.")
+ next
+ end
apply_electric_vehicle(runner, model, spaces, hpxml_bldg, vehicle, schedules_file)
end
end
@@ -70,7 +72,7 @@ def self.get_ev_charging_schedules(runner, model, vehicle, schedules_file)
def self.apply_electric_vehicle(runner, model, spaces, hpxml_bldg, vehicle, schedules_file)
model.getElectricEquipments.sort.each do |ee|
if ee.endUseSubcategory.start_with? Constants::ObjectTypeMiscElectricVehicleCharging
- runner.registerWarning('Electric vehicle was specified as a plug load and as a battery, vehicle charging will be modeled as a plug load.')
+ runner.registerWarning('Electric vehicle charging was specified as both a PlugLoad and a Vehicle, the latter will be ignored.')
return
end
end
@@ -78,19 +80,13 @@ def self.apply_electric_vehicle(runner, model, spaces, hpxml_bldg, vehicle, sche
# Assign charging and vehicle space
ev_charger = vehicle.ev_charger
if ev_charger.nil?
- runner.registerWarning('Electric vehicle specified with no charger provided; battery will not be modeled.')
+ runner.registerWarning('Electric vehicle specified with no charger provided; detailed EV charging will not be modeled.')
return
end
vehicle.location = ev_charger.location
- # Get schedules to calculate effective discharge power
- charging_schedule, discharging_schedule = get_ev_charging_schedules(runner, model, vehicle, schedules_file)
- if charging_schedule.nil? && discharging_schedule.nil?
- runner.registerWarning('Electric vehicle battery specified with no charging/discharging schedule provided; battery will not be modeled.')
- return
- end
-
# Calculate annual driving hours
+ charging_schedule, discharging_schedule = get_ev_charging_schedules(runner, model, vehicle, schedules_file)
if discharging_schedule.to_ScheduleFile.is_initialized
dis_sch = discharging_schedule.to_ScheduleFile.get
col = dis_sch.columnNumber - 1
diff --git a/HPXMLtoOpenStudio/tests/test_validation.rb b/HPXMLtoOpenStudio/tests/test_validation.rb
index 30564087d1..9f4b11f1e9 100644
--- a/HPXMLtoOpenStudio/tests/test_validation.rb
+++ b/HPXMLtoOpenStudio/tests/test_validation.rb
@@ -1851,7 +1851,8 @@ def test_ruby_warning_messages
'schedule-file-max-power-ratio-with-single-speed-system' => ['Maximum power ratio schedule is only supported for variable speed systems.'],
'schedule-file-max-power-ratio-with-two-speed-system' => ['Maximum power ratio schedule is only supported for variable speed systems.'],
'schedule-file-max-power-ratio-with-separate-backup-system' => ['Maximum power ratio schedule is only supported for integrated backup system. Schedule is ignored for heating.'],
- 'ev-charging-methods' => ['Electric vehicle was specified as a plug load and as a battery, vehicle charging will be modeled as a plug load.'] }
+ 'ev-charging-methods' => ['Electric vehicle charging was specified as both a PlugLoad and a Vehicle, the latter will be ignored.'],
+ 'vehicle-phev' => ["Unexpected vehicle type 'PlugInHybridElectricVehicle'. Detailed vehicle charging will not be modeled."] }
all_expected_warnings.each_with_index do |(warning_case, expected_warnings), i|
puts "[#{i + 1}/#{all_expected_warnings.size}] Testing #{warning_case}..."
@@ -2005,6 +2006,9 @@ def test_ruby_warning_messages
hpxml_bldg.header.schedules_filepaths << File.join(File.dirname(__FILE__), '../resources/schedule_files/hvac-variable-system-maximum-power-ratios-varied.csv')
when 'ev-charging-methods'
hpxml, hpxml_bldg = _create_hpxml('base-battery-ev-plug-load-ev.xml')
+ when 'vehicle-phev'
+ hpxml, hpxml_bldg = _create_hpxml('base-battery-ev-scheduled.xml')
+ hpxml_bldg.vehicles[0].vehicle_type = 'PlugInHybridElectricVehicle'
else
fail "Unhandled case: #{warning_case}."
end
diff --git a/docs/source/workflow_inputs.rst b/docs/source/workflow_inputs.rst
index dbc8c7855b..26cc55689c 100644
--- a/docs/source/workflow_inputs.rst
+++ b/docs/source/workflow_inputs.rst
@@ -4640,7 +4640,7 @@ If not entered, the simulation will not include batteries.
HPXML Vehicles
**************
-A vehicle can can be entered in ``/HPXML/Building/BuildingDetails/Systems/Vehicles/Vehicle``. Currently only a battery electric vehicle can be modeled with ``/Vehicle/VehicleType/BatteryElectricVehicle``.
+A vehicle can be entered in ``/HPXML/Building/BuildingDetails/Systems/Vehicles/Vehicle``. Currently only a battery electric vehicle can be modeled with ``/Vehicle/VehicleType/BatteryElectricVehicle``.
This provides detailed modeling of electric vehicles (batteries and charging/discharging) as an alternative to the simple EV charging in :ref:`plug_loads`.
If not entered, the simulation will not include a detailed electric vehicle model.
diff --git a/workflow/tests/util.rb b/workflow/tests/util.rb
index b479ef5c01..3e69e4f237 100644
--- a/workflow/tests/util.rb
+++ b/workflow/tests/util.rb
@@ -240,11 +240,8 @@ def _verify_outputs(rundir, hpxml_path, results, hpxml, unit_multiplier)
if !hpxml_bldg.vehicles.empty? && !hpxml_bldg.vehicles[0].ev_charger_idref.nil? && !hpxml_bldg.vehicles[0].ev_charging_weekday_fractions.nil?
next if message.include? 'Electric vehicle hours per week inputted (14.0) do not match the hours per week calculated from the discharging schedule (8.9). The inputted hours per week value will be ignored.'
end
- if !hpxml_bldg.vehicles.empty? && hpxml_bldg.vehicles[0].ev_charger_idref.nil?
- next if message.include? 'Electric vehicle specified with no charger provided; battery will not be modeled.'
- end
if !hpxml_bldg.vehicles.empty? && !hpxml_bldg.plug_loads.select { |p| p.plug_load_type == HPXML::PlugLoadTypeElectricVehicleCharging }.empty?
- next if message.include? 'Electric vehicle was specified as a plug load and as a battery, vehicle charging will be modeled as a plug load.'
+ next if message.include? 'Electric vehicle charging was specified as both a PlugLoad and a Vehicle, the latter will be ignored.'
end
if hpxml_path.include? 'base-location-capetown-zaf.xml'
next if message.include? 'OS Message: Minutes field (60) on line 9 of EPW file'