diff --git a/HPXMLtoOpenStudio/measure.xml b/HPXMLtoOpenStudio/measure.xml index 365d339980..023d3777e5 100644 --- a/HPXMLtoOpenStudio/measure.xml +++ b/HPXMLtoOpenStudio/measure.xml @@ -3,8 +3,8 @@ 3.1 hpxm_lto_openstudio b1543b30-9465-45ff-ba04-1d1f85e763bc - c80e402f-4c82-4eae-9336-698bde0e09c3 - 2024-10-11T18:59:49Z + 72c688f5-e64c-48f3-b1e4-062d4e53586c + 2024-10-11T22:49:23Z D8922A73 HPXMLtoOpenStudio HPXML to OpenStudio Translator @@ -357,7 +357,7 @@ hpxml.rb rb resource - AF9C179B + 10F59ACC hpxml_schema/HPXML.xsd @@ -387,7 +387,7 @@ hvac.rb rb resource - CEDAF4B3 + 96EEE054 hvac_sizing.rb @@ -405,7 +405,7 @@ lighting.rb rb resource - E8D2508C + 9B17C563 location.rb @@ -441,7 +441,7 @@ misc_loads.rb rb resource - ABD35A7F + 2DCA8614 model.rb @@ -639,7 +639,7 @@ xmlhelper.rb rb resource - D1BB113E + DA4456A1 xmlvalidator.rb @@ -647,12 +647,6 @@ resource 93120E27 - - in.schedules.csv - csv - test - C8EBFF38 - test_airflow.rb rb diff --git a/HPXMLtoOpenStudio/resources/hpxml.rb b/HPXMLtoOpenStudio/resources/hpxml.rb index 2bfe50af41..cb6eb32629 100644 --- a/HPXMLtoOpenStudio/resources/hpxml.rb +++ b/HPXMLtoOpenStudio/resources/hpxml.rb @@ -11184,7 +11184,7 @@ def from_doc(performance_data_point) # # @return [Oga::XML::Document] The HPXML document def _create_hpxml_document - doc = XMLHelper.create_doc('1.0', 'UTF-8') + doc = XMLHelper.create_doc() hpxml = XMLHelper.add_element(doc, 'HPXML') XMLHelper.add_attribute(hpxml, 'xmlns', NameSpace) XMLHelper.add_attribute(hpxml, 'schemaVersion', Version::HPXML_Version) diff --git a/HPXMLtoOpenStudio/resources/hvac.rb b/HPXMLtoOpenStudio/resources/hvac.rb index a690a86e4c..846da1e163 100644 --- a/HPXMLtoOpenStudio/resources/hvac.rb +++ b/HPXMLtoOpenStudio/resources/hvac.rb @@ -266,7 +266,7 @@ def self.apply_heat_pump(runner, model, weather, spaces, hpxml_bldg, hpxml_heade # @param schedules_file [SchedulesFile] SchedulesFile wrapper class instance of detailed schedule files # @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit # @param hpxml_header [HPXML::Header] HPXML Header object (one per HPXML file) - # @return [TODO] TODO + # @return [OpenStudio::Model::AirLoopHVAC] The newly created air loop hvac object def self.apply_air_source_hvac_systems(model, runner, weather, cooling_system, heating_system, hvac_sequential_load_fracs, control_zone, hvac_unavailable_periods, schedules_file, hpxml_bldg, hpxml_header) is_heatpump = false @@ -433,23 +433,23 @@ def self.apply_air_source_hvac_systems(model, runner, weather, cooling_system, h fan = create_supply_fan(model, obj_name, fan_watts_per_cfm, fan_cfms) if heating_system.is_a?(HPXML::HeatPump) && (not heating_system.backup_system.nil?) && (not htg_ap.hp_min_temp.nil?) # Disable blower fan power below compressor lockout temperature if separate backup heating system - set_fan_power_ems_program(model, fan, htg_ap.hp_min_temp) + add_fan_power_ems_program(model, fan, htg_ap.hp_min_temp) end if (not cooling_system.nil?) && (not heating_system.nil?) && (cooling_system == heating_system) - disaggregate_fan_or_pump(model, fan, htg_coil, clg_coil, htg_supp_coil, cooling_system) + add_fan_pump_disaggregation_ems_program(model, fan, htg_coil, clg_coil, htg_supp_coil, cooling_system) else if not cooling_system.nil? if cooling_system.has_integrated_heating - disaggregate_fan_or_pump(model, fan, htg_coil, clg_coil, nil, cooling_system) + add_fan_pump_disaggregation_ems_program(model, fan, htg_coil, clg_coil, nil, cooling_system) else - disaggregate_fan_or_pump(model, fan, nil, clg_coil, nil, cooling_system) + add_fan_pump_disaggregation_ems_program(model, fan, nil, clg_coil, nil, cooling_system) end end if not heating_system.nil? if heating_system.is_heat_pump_backup_system - disaggregate_fan_or_pump(model, fan, nil, nil, htg_coil, heating_system) + add_fan_pump_disaggregation_ems_program(model, fan, nil, nil, htg_coil, heating_system) else - disaggregate_fan_or_pump(model, fan, htg_coil, nil, htg_supp_coil, heating_system) + add_fan_pump_disaggregation_ems_program(model, fan, htg_coil, nil, htg_supp_coil, heating_system) end end end @@ -475,15 +475,15 @@ def self.apply_air_source_hvac_systems(model, runner, weather, cooling_system, h # Air Loop air_loop = create_air_loop(model, obj_name, air_loop_unitary, control_zone, hvac_sequential_load_fracs, [htg_cfm.to_f, clg_cfm.to_f].max, heating_system, hvac_unavailable_periods) - add_backup_staging_EMS(model, air_loop_unitary, htg_supp_coil, control_zone, htg_coil) + add_backup_staging_ems_program(model, air_loop_unitary, htg_supp_coil, control_zone, htg_coil) apply_installation_quality(model, heating_system, cooling_system, air_loop_unitary, htg_coil, clg_coil, control_zone) # supp coil control in staging EMS - apply_two_speed_realistic_staging_EMS(model, air_loop_unitary, htg_supp_coil, control_zone, is_onoff_thermostat_ddb, cooling_system) + add_two_speed_staging_ems_program(model, air_loop_unitary, htg_supp_coil, control_zone, is_onoff_thermostat_ddb, cooling_system) - apply_supp_coil_EMS_for_ddb_thermostat(model, htg_supp_coil, control_zone, htg_coil, is_onoff_thermostat_ddb, cooling_system) + add_supplemental_coil_ems_program(model, htg_supp_coil, control_zone, htg_coil, is_onoff_thermostat_ddb, cooling_system) - apply_max_power_EMS(model, runner, air_loop_unitary, control_zone, heating_system, cooling_system, htg_supp_coil, clg_coil, htg_coil, schedules_file) + add_variable_speed_power_ems_program(model, runner, air_loop_unitary, control_zone, heating_system, cooling_system, htg_supp_coil, clg_coil, htg_coil, schedules_file) if is_heatpump && hpxml_header.defrost_model_type == HPXML::AdvancedResearchDefrostModelTypeAdvanced apply_advanced_defrost(model, htg_coil, air_loop_unitary, control_zone.spaces[0], htg_supp_coil, cooling_system, q_dot_defrost) @@ -500,7 +500,7 @@ def self.apply_air_source_hvac_systems(model, runner, weather, cooling_system, h # @param control_zone [OpenStudio::Model::ThermalZone] Conditioned space thermal zone # @param hvac_unavailable_periods [Hash] Map of htg/clg => HPXML::UnavailablePeriods for heating/cooling # @param unit_multiplier [Integer] Number of similar dwelling units - # @return [TODO] TODO + # @return [OpenStudio::Model::AirLoopHVAC] The newly created air loop hvac object def self.apply_evaporative_cooler(model, cooling_system, hvac_sequential_load_fracs, control_zone, hvac_unavailable_periods, unit_multiplier) @@ -526,7 +526,7 @@ def self.apply_evaporative_cooler(model, cooling_system, hvac_sequential_load_fr fan_watts_per_cfm = [2.79 * (clg_cfm / unit_multiplier)**-0.29, 0.6].min # W/cfm; fit of efficacy to air flow from the CEC listed equipment fan = create_supply_fan(model, obj_name, fan_watts_per_cfm, [clg_cfm]) fan.addToNode(air_loop.supplyInletNode) - disaggregate_fan_or_pump(model, fan, nil, evap_cooler, nil, cooling_system) + add_fan_pump_disaggregation_ems_program(model, fan, nil, evap_cooler, nil, cooling_system) # Outdoor air intake system oa_intake_controller = OpenStudio::Model::ControllerOutdoorAir.new(model) @@ -563,7 +563,7 @@ def self.apply_evaporative_cooler(model, cooling_system, hvac_sequential_load_fr # @param ground_diffusivity [TODO] TODO # @param hvac_unavailable_periods [Hash] Map of htg/clg => HPXML::UnavailablePeriods for heating/cooling # @param unit_multiplier [Integer] Number of similar dwelling units - # @return [TODO] TODO + # @return [OpenStudio::Model::AirLoopHVAC] The newly created air loop hvac object def self.apply_ground_to_air_heat_pump(model, runner, weather, heat_pump, hvac_sequential_load_fracs, control_zone, ground_conductivity, ground_diffusivity, hvac_unavailable_periods, unit_multiplier) @@ -722,7 +722,7 @@ def self.apply_ground_to_air_heat_pump(model, runner, weather, heat_pump, hvac_s rated_power: pump_w ) pump.addToNode(plant_loop.supplyInletNode) - disaggregate_fan_or_pump(model, pump, htg_coil, clg_coil, htg_supp_coil, heat_pump) + add_fan_pump_disaggregation_ems_program(model, pump, htg_coil, clg_coil, htg_supp_coil, heat_pump) # Pipes chiller_bypass_pipe = Model.add_pipe_adiabatic(model) @@ -738,11 +738,11 @@ def self.apply_ground_to_air_heat_pump(model, runner, weather, heat_pump, hvac_s # Fan fan = create_supply_fan(model, obj_name, heat_pump.fan_watts_per_cfm, [htg_cfm, clg_cfm]) - disaggregate_fan_or_pump(model, fan, htg_coil, clg_coil, htg_supp_coil, heat_pump) + add_fan_pump_disaggregation_ems_program(model, fan, htg_coil, clg_coil, htg_supp_coil, heat_pump) # Unitary System air_loop_unitary = create_air_loop_unitary_system(model, obj_name, fan, htg_coil, clg_coil, htg_supp_coil, htg_cfm, clg_cfm, 40.0) - set_pump_power_ems_program(model, pump_w, pump, air_loop_unitary) + add_pump_power_ems_program(model, pump_w, pump, air_loop_unitary) if heat_pump.is_shared_system # Shared pump power per ANSI/RESNET/ICC 301-2019 Section 4.4.5.1 (pump runs 8760) @@ -778,7 +778,7 @@ def self.apply_ground_to_air_heat_pump(model, runner, weather, heat_pump, hvac_s # @param hvac_sequential_load_fracs [Array] Array of daily fractions of remaining heating/cooling load to bet met by the HVAC system # @param control_zone [OpenStudio::Model::ThermalZone] Conditioned space thermal zone # @param hvac_unavailable_periods [Hash] Map of htg/clg => HPXML::UnavailablePeriods for heating/cooling - # @return [TODO] TODO + # @return [OpenStudio::Model::AirLoopHVAC] The newly created air loop hvac object def self.apply_water_loop_to_air_heat_pump(model, heat_pump, hvac_sequential_load_fracs, control_zone, hvac_unavailable_periods) if heat_pump.fraction_cool_load_served > 0 # WLHPs connected to chillers or cooling towers should have already been converted to @@ -819,7 +819,7 @@ def self.apply_water_loop_to_air_heat_pump(model, heat_pump, hvac_sequential_loa # Fan fan_power_installed = 0.0 # Use provided net COP fan = create_supply_fan(model, obj_name, fan_power_installed, [htg_cfm]) - disaggregate_fan_or_pump(model, fan, htg_coil, clg_coil, htg_supp_coil, heat_pump) + add_fan_pump_disaggregation_ems_program(model, fan, htg_coil, clg_coil, htg_supp_coil, heat_pump) # Unitary System air_loop_unitary = create_air_loop_unitary_system(model, obj_name, fan, htg_coil, clg_coil, htg_supp_coil, htg_cfm, nil) @@ -838,7 +838,7 @@ def self.apply_water_loop_to_air_heat_pump(model, heat_pump, hvac_sequential_loa # @param hvac_sequential_load_fracs [Array] Array of daily fractions of remaining heating/cooling load to bet met by the HVAC system # @param control_zone [OpenStudio::Model::ThermalZone] Conditioned space thermal zone # @param hvac_unavailable_periods [Hash] Map of htg/clg => HPXML::UnavailablePeriods for heating/cooling - # @return [TODO] TODO + # @return [OpenStudio::Model::ZoneHVACFourPipeFanCoil or OpenStudio::Model::ZoneHVACBaseboardConvectiveWater] The newly created zone hvac object def self.apply_boiler(model, runner, heating_system, hvac_sequential_load_fracs, control_zone, hvac_unavailable_periods) obj_name = Constants::ObjectTypeBoiler is_condensing = false # FUTURE: Expose as input; default based on AFUE @@ -924,7 +924,7 @@ def self.apply_boiler(model, runner, heating_system, hvac_sequential_load_fracs, plant_loop.addSupplyBranchForComponent(boiler) boiler.additionalProperties.setFeature('HPXML_ID', heating_system.id) # Used by reporting measure boiler.additionalProperties.setFeature('IsHeatPumpBackup', heating_system.is_heat_pump_backup_system) # Used by reporting measure - set_pump_power_ems_program(model, pump_w, pump, boiler) + add_pump_power_ems_program(model, pump_w, pump, boiler) if is_condensing && oat_reset_enabled setpoint_manager_oar = OpenStudio::Model::SetpointManagerOutdoorAirReset.new(model) @@ -1002,7 +1002,7 @@ def self.apply_boiler(model, runner, heating_system, hvac_sequential_load_fracs, zone_hvac.setMaximumSupplyAirFlowRate(UnitConversions.convert(fan_cfm, 'cfm', 'm^3/s')) zone_hvac.setMaximumHotWaterFlowRate(max_water_flow) zone_hvac.addToThermalZone(control_zone) - disaggregate_fan_or_pump(model, pump, zone_hvac, nil, nil, heating_system) + add_fan_pump_disaggregation_ems_program(model, pump, zone_hvac, nil, nil, heating_system) else # Heating Coil htg_coil = OpenStudio::Model::CoilHeatingWaterBaseboard.new(model) @@ -1020,9 +1020,9 @@ def self.apply_boiler(model, runner, heating_system, hvac_sequential_load_fracs, zone_hvac.addToThermalZone(control_zone) zone_hvac.additionalProperties.setFeature('IsHeatPumpBackup', heating_system.is_heat_pump_backup_system) # Used by reporting measure if heating_system.is_heat_pump_backup_system - disaggregate_fan_or_pump(model, pump, nil, nil, zone_hvac, heating_system) + add_fan_pump_disaggregation_ems_program(model, pump, nil, nil, zone_hvac, heating_system) else - disaggregate_fan_or_pump(model, pump, zone_hvac, nil, nil, heating_system) + add_fan_pump_disaggregation_ems_program(model, pump, zone_hvac, nil, nil, heating_system) end end @@ -1061,7 +1061,7 @@ def self.apply_electric_baseboard(model, heating_system, hvac_sequential_load_fr # @param hvac_sequential_load_fracs [Array] Array of daily fractions of remaining heating/cooling load to bet met by the HVAC system # @param control_zone [OpenStudio::Model::ThermalZone] Conditioned space thermal zone # @param hvac_unavailable_periods [Hash] Map of htg/clg => HPXML::UnavailablePeriods for heating/cooling - # @return [TODO] TODO + # @return [nil] def self.apply_unit_heater(model, heating_system, hvac_sequential_load_fracs, control_zone, hvac_unavailable_periods) obj_name = Constants::ObjectTypeUnitHeater @@ -1083,7 +1083,7 @@ def self.apply_unit_heater(model, heating_system, hvac_sequential_load_fracs, co htg_cfm = heating_system.heating_airflow_cfm fan_watts_per_cfm = heating_system.fan_watts / htg_cfm fan = create_supply_fan(model, obj_name, fan_watts_per_cfm, [htg_cfm]) - disaggregate_fan_or_pump(model, fan, htg_coil, nil, nil, heating_system) + add_fan_pump_disaggregation_ems_program(model, fan, htg_coil, nil, nil, heating_system) # Unitary System unitary_system = create_air_loop_unitary_system(model, obj_name, fan, htg_coil, nil, nil, htg_cfm, nil) @@ -1291,7 +1291,7 @@ def self.apply_dehumidifiers(runner, model, spaces, hpxml_bldg, hpxml_header) zone_hvac.additionalProperties.setFeature('HPXML_ID', dehumidifier_id) # Used by reporting measure if total_fraction_served < 1.0 - adjust_dehumidifier_load_EMS(total_fraction_served, zone_hvac, model, conditioned_space) + add_dehumidifier_load_adjustment_ems_program(total_fraction_served, zone_hvac, model, conditioned_space) end end @@ -1678,7 +1678,7 @@ def self.get_heat_cap_eir_fflow_spec(compressor_type) # # @param cooling_system [TODO] TODO # @param use_eer [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.set_cool_curves_central_air_source(cooling_system, use_eer = false) clg_ap = cooling_system.additional_properties clg_ap.cool_rated_cfm_per_ton = get_cool_cfm_per_ton(cooling_system.compressor_type, use_eer) @@ -1744,7 +1744,7 @@ def self.get_cool_capacity_ratios(hvac_system) # # @param heating_system [TODO] TODO # @param use_cop [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.set_heat_curves_central_air_source(heating_system, use_cop = false) htg_ap = heating_system.additional_properties htg_ap.heat_rated_cfm_per_ton = get_heat_cfm_per_ton(heating_system.compressor_type, use_cop) @@ -1782,7 +1782,7 @@ def self.set_heat_curves_central_air_source(heating_system, use_cop = false) # TODO # # @param heat_pump [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.set_heat_detailed_performance_data(heat_pump) hp_ap = heat_pump.additional_properties is_ducted = !heat_pump.distribution_system_idref.nil? @@ -1837,7 +1837,7 @@ def self.set_heat_detailed_performance_data(heat_pump) # TODO # # @param heat_pump [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.set_cool_detailed_performance_data(heat_pump) hp_ap = heat_pump.additional_properties is_ducted = !heat_pump.distribution_system_idref.nil? @@ -1918,7 +1918,7 @@ def self.get_heat_capacity_ratios(heat_pump) # TODO # # @param hvac_system [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.drop_intermediate_speeds(hvac_system) # For variable-speed systems, we only want to model min/max speeds in E+. # Here we drop any intermediate speeds that we may have added for other purposes (e.g. hvac sizing). @@ -1993,7 +1993,7 @@ def self.get_heat_cfm_per_ton(compressor_type, use_cop_or_htg_sys = false) # TODO # # @param heat_pump [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.set_curves_gshp(heat_pump) hp_ap = heat_pump.additional_properties @@ -2105,8 +2105,8 @@ def self.get_building_america_hvac_seasons(weather, latitude) # @param model [OpenStudio::Model::Model] OpenStudio Model object # @param fan [TODO] TODO # @param hp_min_temp [TODO] TODO - # @return [TODO] TODO - def self.set_fan_power_ems_program(model, fan, hp_min_temp) + # @return [nil] + def self.add_fan_power_ems_program(model, fan, hp_min_temp) # EMS is used to disable the fan power below the hp_min_temp; the backup heating # system will be operating instead. @@ -2159,8 +2159,8 @@ def self.set_fan_power_ems_program(model, fan, hp_min_temp) # @param pump_w [TODO] TODO # @param pump [TODO] TODO # @param heating_object [TODO] TODO - # @return [TODO] TODO - def self.set_pump_power_ems_program(model, pump_w, pump, heating_object) + # @return [nil] + def self.add_pump_power_ems_program(model, pump_w, pump, heating_object) # EMS is used to set the pump power. # Without EMS, the pump power will vary according to the plant loop part load ratio # (based on flow rate) rather than the boiler part load ratio (based on load). @@ -2236,8 +2236,8 @@ def self.set_pump_power_ems_program(model, pump_w, pump, heating_object) # @param clg_object [TODO] TODO # @param backup_htg_object [TODO] TODO # @param hpxml_object [TODO] TODO - # @return [TODO] TODO - def self.disaggregate_fan_or_pump(model, fan_or_pump, htg_object, clg_object, backup_htg_object, hpxml_object) + # @return [nil] + def self.add_fan_pump_disaggregation_ems_program(model, fan_or_pump, htg_object, clg_object, backup_htg_object, hpxml_object) # Disaggregate into heating/cooling output energy use. sys_id = hpxml_object.id @@ -2385,16 +2385,15 @@ def self.disaggregate_fan_or_pump(model, fan_or_pump, htg_object, clg_object, ba end end - # TODO + # Adjusts the HVAC load to the space when a dehumidifier serves less than 100% dehumidification load, since + # the EnergyPlus dehumidifier object can only model 100% dehumidification. # # @param fraction_served [TODO] TODO # @param zone_hvac [TODO] TODO # @param model [OpenStudio::Model::Model] OpenStudio Model object # @param conditioned_space [TODO] TODO - # @return [TODO] TODO - def self.adjust_dehumidifier_load_EMS(fraction_served, zone_hvac, model, conditioned_space) - # adjust hvac load to space when dehumidifier serves less than 100% dehumidification load. (With E+ dehumidifier object, it can only model 100%) - + # @return [nil] + def self.add_dehumidifier_load_adjustment_ems_program(fraction_served, zone_hvac, model, conditioned_space) # sensor dehumidifier_sens_htg = Model.add_ems_sensor( model, @@ -2819,7 +2818,7 @@ def self.convert_net_to_gross_capacity_cop(net_cap, fan_power, mode, net_cop = n # @param max_rated_fan_cfm [TODO] TODO # @param weather_temp [TODO] TODO # @param compressor_lockout_temp [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.process_neep_detailed_performance(detailed_performance_data, hvac_ap, mode, max_rated_fan_cfm, weather_temp, compressor_lockout_temp = nil) data_array = Array.new(2) { Array.new } detailed_performance_data.sort_by { |dp| dp.outdoor_temperature }.each do |data_point| @@ -2868,7 +2867,7 @@ def self.process_neep_detailed_performance(detailed_performance_data, hvac_ap, m # @param mode [TODO] TODO # @param compressor_lockout_temp [TODO] TODO # @param weather_temp [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.interpolate_to_odb_table_points(data_array, mode, compressor_lockout_temp, weather_temp) # Set of data used for table lookup data_array.each do |data| @@ -2990,7 +2989,7 @@ def self.interpolate_to_odb_table_point(detailed_performance_data, capacity_desc # @param data_array [TODO] TODO # @param mode [TODO] TODO # @param tol [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.add_data_point_adaptive_step_size(data_array, mode, tol = 0.1) data_array.each do |data| data_sorted = data.sort_by { |dp| dp.outdoor_temperature } @@ -3030,7 +3029,7 @@ def self.add_data_point_adaptive_step_size(data_array, mode, tol = 0.1) # # @param data_array [TODO] TODO # @param mode [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.correct_ft_cap_eir(data_array, mode) # Add sensitivity to indoor conditions # single speed cutler curve coefficients @@ -3247,7 +3246,7 @@ def self.create_dx_cooling_coil(model, obj_name, cooling_system, max_rated_fan_c clg_coil.additionalProperties.setFeature('HPXML_ID', cooling_system.id) # Used by reporting measure if has_deadband_control # Apply startup capacity degradation - apply_capacity_degradation_EMS(model, clg_ap, clg_coil.name.get, true, cap_fff_curve_0, eir_fff_curve_0) + add_capacity_degradation_ems_proram(model, clg_ap, clg_coil.name.get, true, cap_fff_curve_0, eir_fff_curve_0) end return clg_coil @@ -3413,7 +3412,7 @@ def self.create_dx_heating_coil(model, obj_name, heating_system, max_rated_fan_c htg_coil.additionalProperties.setFeature('HPXML_ID', heating_system.id) # Used by reporting measure if has_deadband_control # Apply startup capacity degradation - apply_capacity_degradation_EMS(model, htg_ap, htg_coil.name.get, false, cap_fff_curve_0, eir_fff_curve_0) + add_capacity_degradation_ems_proram(model, htg_ap, htg_coil.name.get, false, cap_fff_curve_0, eir_fff_curve_0) end return htg_coil @@ -3422,7 +3421,7 @@ def self.create_dx_heating_coil(model, obj_name, heating_system, max_rated_fan_c # TODO # # @param cooling_system [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.set_cool_rated_shrs_gross(cooling_system) clg_ap = cooling_system.additional_properties @@ -3525,9 +3524,9 @@ def self.get_supp_coil_avail_sch_actuator(model, htg_supp_coil) # @param control_zone [OpenStudio::Model::ThermalZone] Conditioned space thermal zone # @param htg_coil [OpenStudio::Model::CoilHeatingDXSingleSpeed or OpenStudio::Model::CoilHeatingDXMultiSpeed] OpenStudio Heating Coil object # @param is_onoff_thermostat_ddb [Boolean] Whether to apply on off thermostat deadband - # @param cooling_system [HPXML::CoolingSystem or HPXML::HeatPump] HPXML Cooling System or HPXML Heat Pump object + # @param cooling_system [HPXML::CoolingSystem or HPXML::HeatPump] The HPXML cooling system or heat pump of interest # @return [nil] - def self.apply_supp_coil_EMS_for_ddb_thermostat(model, htg_supp_coil, control_zone, htg_coil, is_onoff_thermostat_ddb, cooling_system) + def self.add_supplemental_coil_ems_program(model, htg_supp_coil, control_zone, htg_coil, is_onoff_thermostat_ddb, cooling_system) return if htg_supp_coil.nil? return unless cooling_system.compressor_type == HPXML::HVACCompressorTypeSingleStage return unless is_onoff_thermostat_ddb @@ -3639,7 +3638,7 @@ def self.apply_supp_coil_EMS_for_ddb_thermostat(model, htg_supp_coil, control_zo # @param cap_fff_curve [OpenStudio::Model::CurveQuadratic] OpenStudio CurveQuadratic object for heat pump capacity function of air flow rates # @param eir_fff_curve [OpenStudio::Model::CurveQuadratic] OpenStudio CurveQuadratic object for heat pump eir function of air flow rates # @return [nil] - def self.apply_capacity_degradation_EMS(model, system_ap, coil_name, is_cooling, cap_fff_curve, eir_fff_curve) + def self.add_capacity_degradation_ems_proram(model, system_ap, coil_name, is_cooling, cap_fff_curve, eir_fff_curve) # Note: Currently only available in 1 min time step if is_cooling c_d = system_ap.cool_c_d @@ -3776,9 +3775,9 @@ def self.apply_capacity_degradation_EMS(model, system_ap, coil_name, is_cooling, # @param htg_supp_coil [OpenStudio::Model::CoilHeatingElectric or OpenStudio::Model::CoilHeatingElectricMultiStage] OpenStudio Supplemental Heating Coil object # @param control_zone [OpenStudio::Model::ThermalZone] Conditioned space thermal zone # @param is_onoff_thermostat_ddb [Boolean] Whether to apply on off thermostat deadband - # @param cooling_system [HPXML::CoolingSystem or HPXML::HeatPump] HPXML Cooling System or HPXML Heat Pump object + # @param cooling_system [HPXML::CoolingSystem or HPXML::HeatPump] The HPXML cooling system or heat pump of interest # @return [nil] - def self.apply_two_speed_realistic_staging_EMS(model, unitary_system, htg_supp_coil, control_zone, is_onoff_thermostat_ddb, cooling_system) + def self.add_two_speed_staging_ems_program(model, unitary_system, htg_supp_coil, control_zone, is_onoff_thermostat_ddb, cooling_system) # Note: Currently only available in 1 min time step return unless is_onoff_thermostat_ddb return unless cooling_system.compressor_type == HPXML::HVACCompressorTypeTwoStage @@ -3930,14 +3929,14 @@ def self.apply_two_speed_realistic_staging_EMS(model, unitary_system, htg_supp_c # @param runner [OpenStudio::Measure::OSRunner] Object typically used to display warnings # @param air_loop_unitary [OpenStudio::Model::AirLoopHVACUnitarySystem] Air loop for the HVAC system # @param control_zone [OpenStudio::Model::ThermalZone] Conditioned space thermal zone - # @param heating_system [HPXML::HeatingSystem or HPXML::HeatPump] HPXML Heating System or HPXML Heat Pump object - # @param cooling_system [HPXML::CoolingSystem or HPXML::HeatPump] HPXML Cooling System or HPXML Heat Pump object + # @param heating_system [HPXML::HeatingSystem or HPXML::HeatPump] The HPXML heating system or heat pump of interest + # @param cooling_system [HPXML::CoolingSystem or HPXML::HeatPump] The HPXML cooling system or heat pump of interest # @param htg_supp_coil [OpenStudio::Model::CoilHeatingElectric or CoilHeatingElectricMultiStage] OpenStudio Supplemental Heating Coil object # @param clg_coil [OpenStudio::Model::CoilCoolingDXMultiSpeed] OpenStudio MultiStage Cooling Coil object # @param htg_coil [OpenStudio::Model::CoilHeatingDXMultiSpeed] OpenStudio MultiStage Heating Coil object # @param schedules_file [SchedulesFile] SchedulesFile wrapper class instance of detailed schedule files # @return [nil] - def self.apply_max_power_EMS(model, runner, air_loop_unitary, control_zone, heating_system, cooling_system, htg_supp_coil, clg_coil, htg_coil, schedules_file) + def self.add_variable_speed_power_ems_program(model, runner, air_loop_unitary, control_zone, heating_system, cooling_system, htg_supp_coil, clg_coil, htg_coil, schedules_file) return if schedules_file.nil? return if clg_coil.nil? && htg_coil.nil? @@ -4264,7 +4263,7 @@ def self.apply_max_power_EMS(model, runner, air_loop_unitary, control_zone, heat # @param control_zone [OpenStudio::Model::ThermalZone] Conditioned space thermal zone # @param htg_coil [OpenStudio::Model::CoilHeatingDXSingleSpeed or OpenStudio::Model::CoilHeatingDXMultiSpeed] OpenStudio Heating Coil object # @return [nil] - def self.add_backup_staging_EMS(model, unitary_system, htg_supp_coil, control_zone, htg_coil) + def self.add_backup_staging_ems_program(model, unitary_system, htg_supp_coil, control_zone, htg_coil) return unless htg_supp_coil.is_a? OpenStudio::Model::CoilHeatingElectricMultiStage # Note: Currently only available in 1 min time step @@ -4420,7 +4419,7 @@ def self.calc_plr_coefficients(c_d) # TODO # # @param cooling_system [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.set_cool_c_d(cooling_system) clg_ap = cooling_system.additional_properties @@ -4448,7 +4447,7 @@ def self.set_cool_c_d(cooling_system) # TODO # # @param heating_system [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.set_heat_c_d(heating_system) htg_ap = heating_system.additional_properties @@ -4472,12 +4471,13 @@ def self.set_heat_c_d(heating_system) htg_ap.heat_plf_fplr_spec = [calc_plr_coefficients(htg_ap.heat_c_d)] * num_speeds end - # TODO + # Calculates rated CEER (newer metric) from rated EER (older metric). # - # @param cooling_system [TODO] TODO - # @return [TODO] TODO + # Source: http://documents.dps.ny.gov/public/Common/ViewDoc.aspx?DocRefId=%7BB6A57FC0-6376-4401-92BD-D66EC1930DCF%7D + # + # @param cooling_system [HPXML::CoolingSystem or HPXML::HeatPump] The HPXML cooling system or heat pump of interest + # @return [Double] The CEER value (Btu/Wh) def self.calc_ceer_from_eer(cooling_system) - # Reference: http://documents.dps.ny.gov/public/Common/ViewDoc.aspx?DocRefId=%7BB6A57FC0-6376-4401-92BD-D66EC1930DCF%7D return cooling_system.cooling_efficiency_eer / 1.01 end @@ -4485,7 +4485,7 @@ def self.calc_ceer_from_eer(cooling_system) # # @param hvac_system [TODO] TODO # @param use_eer_cop [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.set_fan_power_rated(hvac_system, use_eer_cop) hvac_ap = hvac_system.additional_properties @@ -4525,7 +4525,7 @@ def self.get_unitary_system_from_air_loop_hvac(air_loop) # # @param heat_pump [TODO] TODO # @param weather [WeatherFile] Weather object containing EPW information - # @return [TODO] TODO + # @return [nil] def self.set_gshp_assumptions(heat_pump, weather) hp_ap = heat_pump.additional_properties geothermal_loop = heat_pump.geothermal_loop @@ -4619,7 +4619,7 @@ def self.get_sequential_load_schedule(model, fractions, unavailable_periods) # @param hvac_sequential_load_fracs [Array] Array of daily fractions of remaining heating/cooling load to bet met by the HVAC system # @param hvac_unavailable_periods [Hash] Map of htg/clg => HPXML::UnavailablePeriods for heating/cooling # @param heating_system [TODO] TODO - # @return [TODO] TODO + # @return [nil] def self.set_sequential_load_fractions(model, control_zone, hvac_object, hvac_sequential_load_fracs, hvac_unavailable_periods, heating_system = nil) heating_sch = get_sequential_load_schedule(model, hvac_sequential_load_fracs[:htg], hvac_unavailable_periods[:htg]) cooling_sch = get_sequential_load_schedule(model, hvac_sequential_load_fracs[:clg], hvac_unavailable_periods[:clg]) @@ -4679,7 +4679,7 @@ def self.set_sequential_load_fractions(model, control_zone, hvac_object, hvac_se # # @param heat_pump [TODO] TODO # @param runner [OpenStudio::Measure::OSRunner] Object typically used to display warnings - # @return [TODO] TODO + # @return [nil] def self.set_heat_pump_temperatures(heat_pump, runner = nil) hp_ap = heat_pump.additional_properties @@ -4757,8 +4757,8 @@ def self.get_charge_fault_heating_coeff(f_chg) # @param mode [TODO] TODO # @param defect_ratio [TODO] TODO # @param hvac_ap [TODO] TODO - # @return [TODO] TODO - def self.add_install_quality_calculations(fault_program, tin_sensor, tout_sensor, airflow_rated_defect_ratio, clg_or_htg_coil, model, f_chg, obj_name, mode, defect_ratio, hvac_ap) + # @return [nil] + def self.add_installation_quality_program(fault_program, tin_sensor, tout_sensor, airflow_rated_defect_ratio, clg_or_htg_coil, model, f_chg, obj_name, mode, defect_ratio, hvac_ap) if mode == :clg if clg_or_htg_coil.is_a? OpenStudio::Model::CoilCoolingDXSingleSpeed num_speeds = 1 @@ -4969,7 +4969,7 @@ def self.add_install_quality_calculations(fault_program, tin_sensor, tout_sensor # @param htg_coil [TODO] TODO # @param clg_coil [TODO] TODO # @param control_zone [OpenStudio::Model::ThermalZone] Conditioned space thermal zone - # @return [TODO] TODO + # @return [nil] def self.apply_installation_quality(model, heating_system, cooling_system, unitary_system, htg_coil, clg_coil, control_zone) if not cooling_system.nil? charge_defect_ratio = cooling_system.charge_defect_ratio @@ -5031,11 +5031,11 @@ def self.apply_installation_quality(model, heating_system, cooling_system, unita fault_program.addLine("Set F_CH = #{f_chg.round(3)}") if not cool_airflow_rated_defect_ratio.empty? - add_install_quality_calculations(fault_program, tin_sensor, tout_sensor, cool_airflow_rated_defect_ratio, clg_coil, model, f_chg, obj_name, :clg, cool_airflow_defect_ratio, clg_ap) + add_installation_quality_program(fault_program, tin_sensor, tout_sensor, cool_airflow_rated_defect_ratio, clg_coil, model, f_chg, obj_name, :clg, cool_airflow_defect_ratio, clg_ap) end if not heat_airflow_rated_defect_ratio.empty? - add_install_quality_calculations(fault_program, tin_sensor, tout_sensor, heat_airflow_rated_defect_ratio, htg_coil, model, f_chg, obj_name, :htg, heat_airflow_defect_ratio, htg_ap) + add_installation_quality_program(fault_program, tin_sensor, tout_sensor, heat_airflow_rated_defect_ratio, htg_coil, model, f_chg, obj_name, :htg, heat_airflow_defect_ratio, htg_ap) end Model.add_ems_program_calling_manager( @@ -5217,7 +5217,7 @@ def self.apply_advanced_defrost(model, htg_coil, air_loop_unitary, conditioned_s # TODO # # @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit - # @return [TODO] TODO + # @return [nil] def self.apply_shared_systems(hpxml_bldg) applied_clg = apply_shared_cooling_systems(hpxml_bldg) applied_htg = apply_shared_heating_systems(hpxml_bldg) @@ -5484,7 +5484,7 @@ def self.get_hpxml_hvac_systems(hpxml_bldg) # Ensure that no capacities/airflows are zero in order to prevent potential E+ errors. # # @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit - # @return [TODO] TODO + # @return [nil] def self.ensure_nonzero_sizing_values(hpxml_bldg) min_capacity = 1.0 # Btuh min_airflow = 3.0 # cfm; E+ min airflow is 0.001 m3/s @@ -5535,7 +5535,7 @@ def self.ensure_nonzero_sizing_values(hpxml_bldg) # # @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit # @param hpxml_header [HPXML::Header] HPXML Header object (one per HPXML file) - # @return [TODO] TODO + # @return [nil] def self.apply_unit_multiplier(hpxml_bldg, hpxml_header) unit_multiplier = hpxml_bldg.building_construction.number_of_units hpxml_bldg.heating_systems.each do |htg_sys| @@ -5577,13 +5577,15 @@ def self.apply_unit_multiplier(hpxml_bldg, hpxml_header) end end - # TODO + # Calculates rated SEER (older metric) from rated SEER2 (newer metric). # - # @param seer2 [TODO] TODO - # @param is_ducted [TODO] TODO - # @return [TODO] TODO + # Source: ANSI/RESNET/ICC 301 Table 4.4.4.1(1) SEER2/HSPF2 Conversion Factors + # This is based on a regression of products, not a translation. + # + # @param seer2 [Double] Cooling efficiency (Btu/Wh) + # @param is_ducted [Boolean] True if a ducted HVAC system + # @return [Double] SEER value (Btu/Wh) def self.calc_seer_from_seer2(seer2, is_ducted) - # ANSI/RESNET/ICC 301 Table 4.4.4.1(1) SEER2/HSPF2 Conversion Factors # Note: There are less common system types (packaged, small duct high velocity, # and space-constrained) that we don't handle here. if is_ducted # Ducted split system @@ -5593,13 +5595,15 @@ def self.calc_seer_from_seer2(seer2, is_ducted) end end - # TODO + # Calculates rated HSPF (older metric) from rated HSPF2 (newer metric). # - # @param hspf2 [TODO] TODO - # @param is_ducted [TODO] TODO - # @return [TODO] TODO + # Source: ANSI/RESNET/ICC 301 Table 4.4.4.1(1) SEER2/HSPF2 Conversion Factors + # This is based on a regression of products, not a translation. + # + # @param hspf2 [Double] Heating efficiency (Btu/Wh) + # @param is_ducted [Boolean] True if a ducted HVAC system + # @return [Double] HSPF value (Btu/Wh) def self.calc_hspf_from_hspf2(hspf2, is_ducted) - # ANSI/RESNET/ICC 301 Table 4.4.4.1(1) SEER2/HSPF2 Conversion Factors # Note: There are less common system types (packaged, small duct high velocity, # and space-constrained) that we don't handle here. if is_ducted # Ducted split system diff --git a/HPXMLtoOpenStudio/resources/lighting.rb b/HPXMLtoOpenStudio/resources/lighting.rb index 585b239246..53a0dfec1f 100644 --- a/HPXMLtoOpenStudio/resources/lighting.rb +++ b/HPXMLtoOpenStudio/resources/lighting.rb @@ -205,14 +205,14 @@ def self.apply(runner, model, spaces, hpxml_bldg, hpxml_header, schedules_file) end end - # TODO + # Calculates the annual interior lighting energy use based on the conditioned floor area and types of lamps. # # @param eri_version [String] Version of the ANSI/RESNET/ICC 301 Standard to use for equations/assumptions # @param cfa [Double] Conditioned floor area in the dwelling unit (ft2) - # @param f_int_cfl [TODO] TODO - # @param f_int_lfl [TODO] TODO - # @param f_int_led [TODO] TODO - # @return [TODO] TODO + # @param f_int_cfl [Double] Fraction of interior lighting that is compact fluorescent (CFL) + # @param f_int_lfl [Double] Fraction of interior lighting that is linear fluorescent (LFL) + # @param f_int_led [Double] Fraction of interior lighting that is light-emitting diode (LED) + # @return [Double or nil] Annual interior lighting energy use (kWh/yr) def self.calc_interior_energy(eri_version, cfa, f_int_cfl, f_int_lfl, f_int_led) return if f_int_cfl.nil? || f_int_lfl.nil? || f_int_led.nil? @@ -249,14 +249,14 @@ def self.calc_interior_energy(eri_version, cfa, f_int_cfl, f_int_lfl, f_int_led) return int_kwh end - # TODO + # Calculates the annual exterior lighting energy use based on the conditioned floor area and types of lamps. # # @param eri_version [String] Version of the ANSI/RESNET/ICC 301 Standard to use for equations/assumptions # @param cfa [Double] Conditioned floor area in the dwelling unit (ft2) - # @param f_ext_cfl [TODO] TODO - # @param f_ext_lfl [TODO] TODO - # @param f_ext_led [TODO] TODO - # @return [TODO] TODO + # @param f_ext_cfl [Double] Fraction of exterior lighting that is compact fluorescent (CFL) + # @param f_ext_lfl [Double] Fraction of exterior lighting that is linear fluorescent (LFL) + # @param f_ext_led [Double] Fraction of exterior lighting that is light-emitting diode (LED) + # @return [Double or nil] Annual exterior lighting energy use (kWh/yr) def self.calc_exterior_energy(eri_version, cfa, f_ext_cfl, f_ext_lfl, f_ext_led) return if f_ext_cfl.nil? || f_ext_lfl.nil? || f_ext_led.nil? @@ -293,14 +293,14 @@ def self.calc_exterior_energy(eri_version, cfa, f_ext_cfl, f_ext_lfl, f_ext_led) return ext_kwh end - # TODO + # Calculates the annual garage lighting energy use based on the garage area and types of lamps. # # @param eri_version [String] Version of the ANSI/RESNET/ICC 301 Standard to use for equations/assumptions - # @param gfa [TODO] TODO - # @param f_grg_cfl [TODO] TODO - # @param f_grg_lfl [TODO] TODO - # @param f_grg_led [TODO] TODO - # @return [TODO] TODO + # @param gfa [Double] Garage floor area (ft2) + # @param f_grg_cfl [Double] Fraction of garage lighting that is compact fluorescent (CFL) + # @param f_grg_lfl [Double] Fraction of garage lighting that is linear fluorescent (LFL) + # @param f_grg_led [Double] Fraction of garage lighting that is light-emitting diode (LED) + # @return [Double or nil] Annual garage lighting energy use (kWh/yr) def self.calc_garage_energy(eri_version, gfa, f_grg_cfl, f_grg_lfl, f_grg_led) return if f_grg_cfl.nil? || f_grg_lfl.nil? || f_grg_led.nil? diff --git a/HPXMLtoOpenStudio/resources/misc_loads.rb b/HPXMLtoOpenStudio/resources/misc_loads.rb index 6c972f5a1b..2e31b90403 100644 --- a/HPXMLtoOpenStudio/resources/misc_loads.rb +++ b/HPXMLtoOpenStudio/resources/misc_loads.rb @@ -36,12 +36,12 @@ def self.apply_plug_loads(runner, model, spaces, hpxml_bldg, hpxml_header, sched # # @param runner [OpenStudio::Measure::OSRunner] Object typically used to display warnings # @param model [OpenStudio::Model::Model] OpenStudio Model object - # @param plug_load [TODO] TODO + # @param plug_load [HPXML::PlugLoad] The HPXML plug load of interest # @param obj_name [String] Name for the OpenStudio object # @param spaces [Hash] Map of HPXML locations => OpenStudio Space objects # @param schedules_file [SchedulesFile] SchedulesFile wrapper class instance of detailed schedule files # @param unavailable_periods [HPXML::UnavailablePeriods] Object that defines periods for, e.g., power outages or vacancies - # @param apply_ashrae140_assumptions [TODO] TODO + # @param apply_ashrae140_assumptions [Boolean] True if an ASHRAE 140 test case where we want to override our normal assumptions # @return [nil] def self.apply_plug_load(runner, model, plug_load, obj_name, spaces, schedules_file, unavailable_periods, apply_ashrae140_assumptions) kwh = 0 @@ -132,7 +132,7 @@ def self.apply_fuel_loads(runner, model, spaces, hpxml_bldg, hpxml_header, sched # # @param runner [OpenStudio::Measure::OSRunner] Object typically used to display warnings # @param model [OpenStudio::Model::Model] OpenStudio Model object - # @param fuel_load [TODO] TODO + # @param fuel_load [HPXML::FuelLoad] The HPXML fuel load of interest # @param obj_name [String] Name for the OpenStudio object # @param spaces [Hash] Map of HPXML locations => OpenStudio Space objects # @param schedules_file [SchedulesFile] SchedulesFile wrapper class instance of detailed schedule files @@ -213,7 +213,7 @@ def self.apply_pools_and_permanent_spas(runner, model, spaces, hpxml_bldg, hpxml # # @param runner [OpenStudio::Measure::OSRunner] Object typically used to display warnings # @param model [OpenStudio::Model::Model] OpenStudio Model object - # @param pool_or_spa [TODO] TODO + # @param pool_or_spa [HPXML::Pool or HPXML::PermanentSpa] The HPXML pool or permanent space of interest # @param spaces [Hash] Map of HPXML locations => OpenStudio Space objects # @param schedules_file [SchedulesFile] SchedulesFile wrapper class instance of detailed schedule files # @param unavailable_periods [HPXML::UnavailablePeriods] Object that defines periods for, e.g., power outages or vacancies @@ -303,7 +303,7 @@ def self.apply_pool_or_permanent_spa_heater(runner, model, pool_or_spa, spaces, # # @param runner [OpenStudio::Measure::OSRunner] Object typically used to display warnings # @param model [OpenStudio::Model::Model] OpenStudio Model object - # @param pool_or_spa [TODO] TODO + # @param pool_or_spa [HPXML::Pool or HPXML::PermanentSpa] The HPXML pool or permanent space of interest # @param spaces [Hash] Map of HPXML locations => OpenStudio Space objects # @param schedules_file [SchedulesFile] SchedulesFile wrapper class instance of detailed schedule files # @param unavailable_periods [HPXML::UnavailablePeriods] Object that defines periods for, e.g., power outages or vacancies diff --git a/HPXMLtoOpenStudio/resources/xmlhelper.rb b/HPXMLtoOpenStudio/resources/xmlhelper.rb index bcbf52284b..05b66f4b7a 100644 --- a/HPXMLtoOpenStudio/resources/xmlhelper.rb +++ b/HPXMLtoOpenStudio/resources/xmlhelper.rb @@ -2,30 +2,32 @@ # Collection of helper methods related to XML reading/writing. module XMLHelper - # Adds the child element with 'element_name' and sets its value. Returns the - # child element. + # Adds the child element with 'element_name' and sets its value. If the value + # has been defaulted, the dataSource='software' attribute will be assigned to + # the element. # - # @param parent [TODO] TODO - # @param element_name [TODO] TODO - # @param value [TODO] TODO - # @param datatype [TODO] TODO - # @param defaulted [TODO] TODO - # @return [TODO] TODO + # @param parent [Oga::XML::Element] Parent element for the addition + # @param element_name [String] Name of the element to add + # @param value [*] The value for the element (could be a string, float, boolean, etc.) + # @param datatype [Symbol] Datatype of the element (:integer, :float, :boolean, or :string) + # @param defaulted [Boolean] Whether the value has been defaulted by OS-HPXML + # @return [Oga::XML::Element] The added element def self.add_element(parent, element_name, value = nil, datatype = nil, defaulted = false) added = XMLHelper.insert_element(parent, element_name, -1, value, datatype, defaulted) return added end - # Inserts the child element with 'element_name' and sets its value. Returns the - # child element. + # Inserts the child element with 'element_name' and sets its value. If the value + # has been defaulted, the dataSource='software' attribute will be assigned to + # the element. # - # @param parent [TODO] TODO - # @param element_name [TODO] TODO - # @param index [TODO] TODO - # @param value [TODO] TODO - # @param datatype [TODO] TODO - # @param defaulted [TODO] TODO - # @return [TODO] TODO + # @param parent [Oga::XML::Element] Parent element for the insertion + # @param element_name [String] Name of the element to insert + # @param index [Integer] The position of the element to be inserted + # @param value [*] The value for the element (could be a string, float, boolean, etc.) + # @param datatype [Symbol] Datatype of the element (:integer, :float, :boolean, or :string) + # @param defaulted [Boolean] Whether the value has been defaulted by OS-HPXML + # @return [Oga::XML::Element] The inserted element def self.insert_element(parent, element_name, index = 0, value = nil, datatype = nil, defaulted = false) added = Oga::XML::Element.new(name: element_name) if index == -1 @@ -53,14 +55,14 @@ def self.insert_element(parent, element_name, index = 0, value = nil, datatype = end # Adds the child element with 'element_name' to a single extension element and - # sets its value. Returns the extension element. + # sets its value. If the extension element already exists, it will be reused. # - # @param parent [TODO] TODO - # @param element_name [TODO] TODO - # @param value [TODO] TODO - # @param datatype [TODO] TODO - # @param defaulted [TODO] TODO - # @return [TODO] TODO + # @param parent [Oga::XML::Element] Parent element for the insertion + # @param element_name [String] Name of the extension element to add + # @param value [*] The value for the element (could be a string, float, boolean, etc.) + # @param datatype [Symbol] Datatype of the element (:integer, :float, :boolean, or :string) + # @param defaulted [Boolean] Whether the value has been defaulted by OS-HPXML + # @return [Oga::XML::Element] The added extension element def self.add_extension(parent, element_name, value = nil, datatype = nil, defaulted = false) extension = XMLHelper.create_elements_as_needed(parent, ['extension']) return XMLHelper.add_element(extension, element_name, value, datatype, defaulted) @@ -68,11 +70,10 @@ def self.add_extension(parent, element_name, value = nil, datatype = nil, defaul # Creates a hierarchy of elements under the parent element based on the supplied # list of element names. If a given child element already exists, it is reused. - # Returns the final element. # - # @param parent [TODO] TODO - # @param element_names [TODO] TODO - # @return [TODO] TODO + # @param parent [Oga::XML::Element] Parent element for the insertion + # @param element_names element_name [String] Name of the element to add + # @return [Oga::XML::Element] The final created element def self.create_elements_as_needed(parent, element_names) this_parent = parent element_names.each do |element_name| @@ -84,11 +85,11 @@ def self.create_elements_as_needed(parent, element_names) return this_parent end - # Deletes the child element with element_name. Returns the deleted element. + # Deletes the child element with element_name. # - # @param parent [TODO] TODO - # @param element_name [TODO] TODO - # @return [TODO] TODO + # @param parent [Oga::XML::Element] Parent element for the insertion + # @param element_name [String] Name of the element to delete + # @return [Oga::XML::Element] The deleted element def self.delete_element(parent, element_name) element = nil while !parent.at_xpath(element_name).nil? @@ -98,12 +99,12 @@ def self.delete_element(parent, element_name) return last_element end - # Returns the value of 'element_name' in the parent element or nil. + # Gets the value of 'element_name' in the parent element or nil. # - # @param parent [TODO] TODO - # @param element_name [TODO] TODO - # @param datatype [TODO] TODO - # @return [TODO] TODO + # @param parent [Oga::XML::Element] Parent element for the insertion + # @param element_name [String] Name of the element to get the value of + # @param datatype [Symbol] Datatype of the element (:integer, :float, :boolean, or :string) + # @return [* or nil] The value of the element in the specified datatype def self.get_value(parent, element_name, datatype) element = parent.at_xpath(element_name) if element.nil? @@ -125,12 +126,13 @@ def self.get_value(parent, element_name, datatype) return value end - # Returns the value(s) of 'element_name' in the parent element or []. + # Gets the value(s) of 'element_name' in the parent element or []. + # Use for elements that can occur multiple times. # - # @param parent [TODO] TODO - # @param element_name [TODO] TODO - # @param datatype [TODO] TODO - # @return [TODO] TODO + # @param parent [Oga::XML::Element] Parent element for the insertion + # @param element_name [String] Name of the element to get the values of + # @param datatype [Symbol] Datatype of the element (:integer, :float, :boolean, or :string) + # @return [Array<*>] Array of values in the specified datatype def self.get_values(parent, element_name, datatype) values = [] parent.xpath(element_name).each do |value| @@ -152,30 +154,31 @@ def self.get_values(parent, element_name, datatype) return values end - # Returns the element in the parent element. + # Gets the element with 'element_name' in the parent element. # - # @param parent [TODO] TODO - # @param element_name [TODO] TODO - # @return [TODO] TODO + # @param parent [Oga::XML::Element] Parent element for the insertion + # @param element_name [String] Name of the element to get + # @return [Oga::XML::Element] The element of interest def self.get_element(parent, element_name) return parent.at_xpath(element_name) end - # Returns the element in the parent element. + # Gets the elements with 'element_name' in the parent element. + # Use for elements that can occur multiple times. # - # @param parent [TODO] TODO - # @param element_name [TODO] TODO - # @return [TODO] TODO + # @param parent [Oga::XML::Element] Parent element for the insertion + # @param element_name [String] Name of the elements to get + # @return [Array] The elements of interest def self.get_elements(parent, element_name) return parent.xpath(element_name) end - # Returns the name of the first child element of the 'element_name' + # Gets the name of the first child element of the 'element_name' # element on the parent element. # - # @param parent [TODO] TODO - # @param element_name [TODO] TODO - # @return [TODO] TODO + # @param parent [Oga::XML::Element] Parent element for the insertion + # @param element_name [String] Name of the element with the child element to get + # @return [String or nil] Name of the child element, or nil if no child element def self.get_child_name(parent, element_name) element = parent.at_xpath(element_name) return if element.nil? || element.children.nil? @@ -187,76 +190,73 @@ def self.get_child_name(parent, element_name) end end - # Returns true if the element exists. + # Checks whether the element has a child element with 'element_name'. # - # @param parent [TODO] TODO - # @param element_name [TODO] TODO - # @return [TODO] TODO + # @param parent [Oga::XML::Element] Parent element for the insertion + # @param element_name [String] Name of the element to check for the presence of + # @return [Boolean] True if the element exists def self.has_element(parent, element_name) element = parent.at_xpath(element_name) return !element.nil? end - # Returns the attribute added + # Adds an attribute to the specified element. # - # @param element [TODO] TODO - # @param attr_name [TODO] TODO - # @param attr_val [TODO] TODO - # @return [TODO] TODO + # @param element [Oga::XML::Element] Element to add the attribute to + # @param attr_name [String] Name of the attribute + # @param attr_val [*] Value for the attribute + # @return [nil] def self.add_attribute(element, attr_name, attr_val) - added = element.set(attr_name, attr_val) - return added + element.set(attr_name, attr_val) end - # Returns the value of the attribute + # Gets the value of the specified attribute for the given element. # - # @param element [TODO] TODO - # @param attr_name [TODO] TODO - # @return [TODO] TODO + # @param element [Oga::XML::Element] Element with the attribute whose value we want + # @param attr_name [String] Name of the attribute + # @return [String or nil] The value of the attribute, or nil if not found def self.get_attribute_value(element, attr_name) return if element.nil? return element.get(attr_name) end - # TODO + # Deletes the specified attribute for the given element. # - # @param element [TODO] TODO - # @param attr_name [TODO] TODO - # @return [TODO] TODO + # @param element [Oga::XML::Element] Element with the attribute we want to delete + # @param attr_name [String] Name of the attribute + # @return [nil] def self.delete_attribute(element, attr_name) return if element.nil? element.unset(attr_name) end - # TODO + # Creates an empty XML document. # - # @param version [TODO] TODO - # @param encoding [TODO] TODO - # @param standalone [TODO] TODO - # @return [TODO] TODO - def self.create_doc(version = nil, encoding = nil, standalone = nil) - doc = Oga::XML::Document.new(xml_declaration: Oga::XML::XmlDeclaration.new(version: version, encoding: encoding, standalone: standalone)) # Oga.parse_xml + # @return [Oga::XML::Document] The new XML document + def self.create_doc() + xml_declaration = Oga::XML::XmlDeclaration.new(version: '1.0', encoding: 'UTF-8') + doc = Oga::XML::Document.new(xml_declaration: xml_declaration) # Oga.parse_xml return doc end - # TODO + # Obtains the XML document for the XML file at the specified path. # # @param hpxml_path [String] Path to the HPXML file - # @return [TODO] TODO + # @return [Oga::XML::Document] The XML document def self.parse_file(hpxml_path) file_read = File.read(hpxml_path) hpxml_doc = Oga.parse_xml(file_read) return hpxml_doc end - # TODO + # Writes the XML file for the given XML document. # # @param doc [Oga::XML::Document] Oga XML Document object - # @param out_path [TODO] TODO - # @return [TODO] TODO - def self.write_file(doc, out_path) + # @param hpxml_path [String] Path to the HPXML file + # @return [String] The written XML file as a string + def self.write_file(doc, hpxml_path) doc_s = doc.to_xml.delete("\r") # Manually apply pretty-printing (indentation and newlines) @@ -298,10 +298,10 @@ def self.write_file(doc, out_path) doc_s.gsub!('"', '"') # Write XML file - if not Dir.exist? File.dirname(out_path) - FileUtils.mkdir_p(File.dirname(out_path)) + if not Dir.exist? File.dirname(hpxml_path) + FileUtils.mkdir_p(File.dirname(hpxml_path)) end - File.open(out_path, 'w', newline: :crlf) do |f| + File.open(hpxml_path, 'w', newline: :crlf) do |f| f << doc_s end @@ -309,12 +309,12 @@ def self.write_file(doc, out_path) end end -# TODO +# Converts a value to float; throws an error if it can't be converted. # -# @param value [TODO] TODO -# @param parent [TODO] TODO -# @param element_name [TODO] TODO -# @return [TODO] TODO +# @param value [*] The value in any datatype (float, integer, string) +# @param parent [Oga::XML::Element] Parent element for the error message +# @param element_name [String] Name of the element for the error message +# @return [Double] The value converted to double def to_float(value, parent, element_name) begin return Float(value) @@ -323,12 +323,12 @@ def to_float(value, parent, element_name) end end -# TODO +# Converts a value to integer; throws an error if it can't be converted. # -# @param value [TODO] TODO -# @param parent [TODO] TODO -# @param element_name [TODO] TODO -# @return [TODO] TODO +# @param value [*] The value in any datatype (float, integer, string) +# @param parent [Oga::XML::Element] Parent element for the error message +# @param element_name [String] Name of the element for the error message +# @return [Integer] The value converted to integer def to_integer(value, parent, element_name) begin value = Float(value) @@ -342,12 +342,12 @@ def to_integer(value, parent, element_name) end end -# TODO +# Converts a value to boolean; throws an error if it can't be converted. # -# @param value [TODO] TODO -# @param parent [TODO] TODO -# @param element_name [TODO] TODO -# @return [TODO] TODO +# @param value [*] The value in any datatype (float, integer, string) +# @param parent [Oga::XML::Element] Parent element for the error message +# @param element_name [String] Name of the element for the error message +# @return [Boolean] The value converted to boolean def to_boolean(value, parent, element_name) if value.is_a? TrueClass return true @@ -362,36 +362,36 @@ def to_boolean(value, parent, element_name) fail "Cannot convert '#{value}' to boolean for #{parent.name}/#{element_name}." end -# TODO +# Converts a value to float or returns nil if nil provided; throws an error if it can't be converted. # -# @param value [TODO] TODO -# @param parent [TODO] TODO -# @param element_name [TODO] TODO -# @return [TODO] TODO +# @param value [*] The value in any datatype (float, integer, string) +# @param parent [Oga::XML::Element] Parent element for the error message +# @param element_name [String] Name of the element for the error message +# @return [Double or nil] The value converted to double, or nil def to_float_or_nil(value, parent, element_name) return if value.nil? return to_float(value, parent, element_name) end -# TODO +# Converts a value to integer or returns nil if nil provided; throws an error if it can't be converted. # -# @param value [TODO] TODO -# @param parent [TODO] TODO -# @param element_name [TODO] TODO -# @return [TODO] TODO +# @param value [*] The value in any datatype (float, integer, string) +# @param parent [Oga::XML::Element] Parent element for the error message +# @param element_name [String] Name of the element for the error message +# @return [Integer] The value converted to integer, or nil def to_integer_or_nil(value, parent, element_name) return if value.nil? return to_integer(value, parent, element_name) end -# TODO +# Converts a value to boolean or returns nil if nil provided; throws an error if it can't be converted. # -# @param value [TODO] TODO -# @param parent [TODO] TODO -# @param element_name [TODO] TODO -# @return [TODO] TODO +# @param value [*] The value in any datatype (float, integer, string) +# @param parent [Oga::XML::Element] Parent element for the error message +# @param element_name [String] Name of the element for the error message +# @return [Boolean] The value converted to boolean def to_boolean_or_nil(value, parent, element_name) return if value.nil? diff --git a/docs/source/workflow_inputs.rst b/docs/source/workflow_inputs.rst index ed22ed8844..31cfa0fd0e 100644 --- a/docs/source/workflow_inputs.rst +++ b/docs/source/workflow_inputs.rst @@ -2442,7 +2442,7 @@ Each central air conditioner is entered as a ``/HPXML/Building/BuildingDetails/S ``CoolingCapacity`` double Btu/hr >= 0 No autosized [#]_ Cooling output capacity ``CompressorType`` string See [#]_ No See [#]_ Type of compressor ``FractionCoolLoadServed`` double frac >= 0, <= 1 [#]_ Yes Fraction of cooling load served - ``AnnualCoolingEfficiency[Units="SEER" or Units="SEER2"]/Value`` double Btu/Wh or # > 0 Yes Rated efficiency [#]_ + ``AnnualCoolingEfficiency[Units="SEER" or Units="SEER2"]/Value`` double Btu/Wh > 0 Yes Rated efficiency [#]_ ``SensibleHeatFraction`` double frac > 0.5, <= 1 No See [#]_ Sensible heat fraction ``CoolingDetailedPerformanceData`` element No Cooling detailed performance data [#]_ ``extension/FanPowerWattsPerCFM`` double W/cfm >= 0 No See [#]_ Blower fan efficiency at maximum fan speed [#]_