Class: NECB2011
- Defined in:
- lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb,
lib/openstudio-standards/standards/necb/NECB2011/autozone.rb,
lib/openstudio-standards/standards/necb/NECB2011/lighting.rb,
lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb,
lib/openstudio-standards/standards/necb/NECB2011/hvac_system_4.rb,
lib/openstudio-standards/standards/necb/NECB2011/hvac_system_6.rb,
lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb,
lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb,
lib/openstudio-standards/standards/necb/NECB2011/hvac_system_2_and_5.rb,
lib/openstudio-standards/standards/necb/NECB2011/beps_compliance_path.rb,
lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb,
lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_multi_speed.rb,
lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_single_speed.rb,
lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_multi_speed.rb,
lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_single_speed.rb
Overview
This class holds methods that apply NECB2011 rules.
Direct Known Subclasses
BTAPPRE1980, ECMS, NECB2011College, NECB2011Courthouse, NECB2011FullServiceRestaurant, NECB2011HighriseApartment, NECB2011Hospital, NECB2011Laboratory, NECB2011LargeDataCenterHighITE, NECB2011LargeDataCenterLowITE, NECB2011LargeHotel, NECB2011LargeOffice, NECB2011LargeOfficeDetailed, NECB2011MediumOffice, NECB2011MediumOfficeDetailed, NECB2011MidriseApartment, NECB2011Outpatient, NECB2011PrimarySchool, NECB2011QuickServiceRestaurant, NECB2011RetailStandalone, NECB2011RetailStripmall, NECB2011SecondarySchool, NECB2011SmallDataCenterHighITE, NECB2011SmallDataCenterLowITE, NECB2011SmallHotel, NECB2011SmallOffice, NECB2011SmallOfficeDetailed, NECB2011SuperMarket, NECB2011SuperTallBuilding, NECB2011TallBuilding, NECB2011Warehouse, NECB2011_Prototype, NECB2015
Constant Summary
Constants inherited from Standard
Instance Attribute Summary collapse
-
#fuel_type_set ⇒ Object
Returns the value of attribute fuel_type_set.
-
#qaqc_data ⇒ Object
Returns the value of attribute qaqc_data.
-
#space_multiplier_map ⇒ Object
Returns the value of attribute space_multiplier_map.
-
#space_type_map ⇒ Object
Returns the value of attribute space_type_map.
-
#standards_data ⇒ Object
Returns the value of attribute standards_data.
-
#tbd ⇒ Object
readonly
Returns the value of attribute tbd.
-
#template ⇒ Object
readonly
Returns the value of attribute template.
Instance Method Summary collapse
- #add_all_spacetypes_to_model(model) ⇒ Object
-
#add_onespeed_DX_coil(model, always_on) ⇒ Object
Create a new DX cooling coil with NECB curve characteristics.
- #add_onespeed_htg_DX_coil(model, sch) ⇒ Object
- #add_ptac_dx_cooling(model, zone, zero_outdoor_air) ⇒ Object
- #add_sys1_unitary_ac_baseboard_heating(model:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', zones:, mau_type:, mau_heating_coil_type:, baseboard_type:, hw_loop:, multispeed: false) ⇒ Object
-
#add_sys1_unitary_ac_baseboard_heating_multi_speed(model:, zones:, mau_type:, mau_heating_coil_type:, baseboard_type:, hw_loop:) ⇒ Object
At this point the only way to implement multi-stage cooling and heating in OS is through the use of object “AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed”.
- #add_sys1_unitary_ac_baseboard_heating_single_speed(model:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', zones:, mau_type:, mau_heating_coil_type:, baseboard_type:, hw_loop:) ⇒ Object
- #add_sys2_FPFC_sys5_TPFC(model:, zones:, chiller_type:, fan_coil_type:, mau_cooling_type:, hw_loop:) ⇒ Object
- #add_sys3_and_8_zone_equip(air_loop, baseboard_type, hw_loop, model, zone) ⇒ Object
- #add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating(model:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', zones:, heating_coil_type:, baseboard_type:, hw_loop:, new_auto_zoner: true, multispeed: false) ⇒ Object
-
#add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating_multi_speed(model:, zones:, heating_coil_type:, baseboard_type:, hw_loop:, new_auto_zoner: true) ⇒ Object
Some tests still require a simple way to set up a system without sizing..
-
#add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating_single_speed(model:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', zones:, heating_coil_type:, baseboard_type:, hw_loop:, new_auto_zoner: true) ⇒ Object
Some tests still require a simple way to set up a system without sizing..
- #add_sys4_single_zone_make_up_air_unit_with_baseboard_heating(model:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', zones:, heating_coil_type:, baseboard_type:, hw_loop:) ⇒ Object
-
#add_sys6_multi_zone_built_up_system_with_baseboard_heating(model:, zones:, heating_coil_type:, baseboard_type:, chiller_type:, fan_type:, hw_loop:) ⇒ Object
end add_sys4_single_zone_make_up_air_unit_with_baseboard_heating.
- #add_sys6_multi_zone_reference_hp_with_baseboard_heating(model:, zones:, heating_coil_type:, baseboard_type:, hw_loop:, necb_reference_hp_supp_fuel: 'DefaultFuel') ⇒ Object
- #add_system_3_and_8_airloop(heating_coil_type, model, system_data, control_zone, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel') ⇒ Object
- #add_system_3_and_8_airloop_multi_speed(heating_coil_type, model, system_data, control_zone) ⇒ Object
-
#add_zone_baseboards(baseboard_type:, hw_loop:, model:, zone:) ⇒ Object
Zonal systems.
-
#adjust_wildcard_spacetype_schedule(space:, schedule:, lights_type: 'NECB_Default', lights_scale: 1.0) ⇒ Object
Set wildcard spactype schedule to NECB letter index.
-
#air_loop_hvac_apply_economizer_integration(air_loop_hvac, climate_zone) ⇒ Boolean
NECB always requires an integrated economizer (NoLockout); as per 5.2.2.8(3) this means that compressor allowed to turn on when economizer is open.
-
#air_loop_hvac_apply_energy_recovery_ventilator(air_loop_hvac, climate = nil) ⇒ Boolean
Add an ERV to this airloop.
-
#air_loop_hvac_apply_multizone_vav_outdoor_air_sizing(air_loop_hvac) ⇒ Object
NECB does not change damper positions.
-
#air_loop_hvac_apply_single_zone_controls(air_loop_hvac, climate_zone) ⇒ Boolean
NECB has no single zone air loop control requirements.
-
#air_loop_hvac_apply_vav_damper_action(air_loop_hvac) ⇒ Boolean
Set the VAV damper control to single maximum or dual maximum control depending on the standard.
-
#air_loop_hvac_demand_control_ventilation_required?(air_loop_hvac, climate_zone) ⇒ Boolean
Determine if demand control ventilation (DCV) is required for this air loop.
-
#air_loop_hvac_economizer_required?(air_loop_hvac) ⇒ Boolean
Determine whether or not this system is required to have an economizer.
-
#air_loop_hvac_enable_unoccupied_fan_shutoff(air_loop_hvac, min_occ_pct = 0.05) ⇒ Boolean
Shut off the system during unoccupied periods.
-
#air_loop_hvac_energy_recovery_ventilator_required?(air_loop_hvac, climate_zone) ⇒ Boolean
Check if ERV is required on this airloop.
-
#air_loop_hvac_motorized_oa_damper_limits(air_loop_hvac, climate_zone) ⇒ Array<Double>
Determine the air flow and number of story limits for whether motorized OA damper is required.
-
#air_loop_hvac_static_pressure_reset_required?(air_loop_hvac, has_ddc) ⇒ Boolean
NECB doesn’t require static pressure reset.
-
#air_terminal_single_duct_vav_reheat_set_heating_cap(air_terminal_single_duct_vav_reheat) ⇒ Boolean
Sets the capacity of the reheat coil based on the minimum flow fraction, and the maximum flow rate.
-
#apply_auto_zoning(model:, sizing_run_dir: Dir.pwd, lights_type: 'NECB_Default', lights_scale: 1.0) ⇒ Object
Top level method that merges spaces into zones where possible.
- #apply_building_default_constructionset(model) ⇒ Object
- #apply_default_constructionsets_to_spacetypes(climate_zone, model) ⇒ Object
- #apply_economizers(climate_zone, model) ⇒ Object
- #apply_envelope(model:, ext_wall_cond: nil, ext_floor_cond: nil, ext_roof_cond: nil, ground_wall_cond: nil, ground_floor_cond: nil, ground_roof_cond: nil, door_construction_cond: nil, fixed_window_cond: nil, glass_door_cond: nil, overhead_door_cond: nil, skylight_cond: nil, glass_door_solar_trans: nil, fixed_wind_solar_trans: nil, skylight_solar_trans: nil, infiltration_scale: nil, necb_hdd: true) ⇒ Object
-
#apply_fdwr_srr_daylighting(model:, fdwr_set: -1.0,, srr_set: -1.0,, necb_hdd: true) ⇒ Object
Thermal zones need to be set to determine conditioned spaces when applying fdwr and srr limits.
-
#apply_kiva_foundation(model) ⇒ Object
apply the Kiva foundation model to floors and walls with ground boundary condition created by: Kamel Haddad ([email protected]).
- #apply_limit_fdwr(model:, fdwr_lim:) ⇒ Object
- #apply_loads(model:, lights_type: 'NECB_Default', lights_scale: 1.0, validate: true, occupancy_loads_scale: nil, electrical_loads_scale: nil, oa_scale: nil) ⇒ Object
- #apply_loop_pump_power(model:, sizing_run_dir:) ⇒ Object
-
#apply_max_fdwr_nrcan(model:, fdwr_lim:) ⇒ Object
This method applies the maximum fenestration and door to wall ratio to a building as per NECB 2011 8.4.4.3 and 3.2.1.4 (or equivalent in other versions of the NECB).
-
#apply_max_srr_nrcan(model:, srr_lim:) ⇒ Object
This method is similar to the ‘apply_max_fdwr’ method above but applies the maximum skylight to roof area ratio to a building as per NECB 2011 8.4.4.3 and 3.2.1.4 (or equivalent in other versions of the NECB).
-
#apply_standard_construction_properties(model:, runner: nil, ext_wall_cond: nil, ext_floor_cond: nil, ext_roof_cond: nil, ground_wall_cond: nil, ground_floor_cond: nil, ground_roof_cond: nil, fixed_window_cond: nil, fixed_wind_solar_trans: nil, fixed_wind_vis_trans: nil, operable_wind_solar_trans: nil, operable_window_cond: nil, operable_wind_vis_trans: nil, glass_door_cond: nil, glass_door_solar_trans: nil, glass_door_vis_trans: nil, door_construction_cond: nil, overhead_door_cond: nil, skylight_cond: nil, skylight_solar_trans: nil, skylight_vis_trans: nil, tubular_daylight_dome_cond: nil, tubular_daylight_dome_solar_trans: nil, tubular_daylight_dome_vis_trans: nil, tubular_daylight_diffuser_cond: nil, tubular_daylight_diffuser_solar_trans: nil, tubular_daylight_diffuser_vis_trans: nil, necb_hdd: true) ⇒ Boolean
Go through the default construction sets and hard-assigned constructions.
- #apply_standard_efficiencies(model:, sizing_run_dir:, dcv_type: 'NECB_Default', necb_reference_hp: false) ⇒ Object
- #apply_standard_lights(set_lights: true, space_type:, space_type_properties:, lights_type:, lights_scale:) ⇒ Object
-
#apply_standard_skylight_to_roof_ratio(model:, srr_set: -1.0)) ⇒ Object
Reduces the SRR to the values specified by the PRM.
-
#apply_standard_window_to_wall_ratio(model:, fdwr_set: -1.0,, necb_hdd: true) ⇒ Object
Reduces the WWR to the values specified by the NECB NECB 3.2.1.4.
-
#apply_systems(model:, primary_heating_fuel:, swh_fuel:, sizing_run_dir:, shw_scale:, baseline_system_zones_map_option:) ⇒ Object
Organizes Zones and assigns them to appropriate systems according to NECB 2011-17 systems spacetype rules in Sec 8.
- #apply_systems_and_efficiencies(model:, sizing_run_dir:, primary_heating_fuel:, swh_fuel:, dcv_type: 'NECB_Default', ecm_system_name: 'NECB_Default', ecm_system_zones_map_option: 'NECB_Default', erv_package: 'NECB_Default', boiler_eff: nil, furnace_eff: nil, unitary_cop: nil, shw_eff: nil, daylighting_type: 'NECB_Default', nv_type: nil, nv_opening_fraction: nil, nv_temp_out_min: nil, nv_delta_temp_in_out: nil, pv_ground_type:, pv_ground_total_area_pv_panels_m2:, pv_ground_tilt_angle:, pv_ground_azimuth_angle:, pv_ground_module_description:, chiller_type: 'NECB_Default', shw_scale:, airloop_economizer_type: nil, baseline_system_zones_map_option:) ⇒ Object
-
#apply_thermal_bridging(model: nil, tbd_option: 'none', tbd_interpolate: false, wall_U: nil, floor_U: nil, roof_U: nil) ⇒ Boolean
(Optionally) uprates, then derates, envelope surface constructions due to MAJOR thermal bridges (e.g. roof parapets, corners, fenestration perimeters).
- #apply_weather_data(model:, epw_file:, custom_weather_folder: nil) ⇒ Object
-
#are_space_loads_similar?(space_1:, space_2:, surface_percent_difference_tolerance: 0.01, angular_percent_difference_tolerance: 0.001, heating_load_percent_difference_tolerance: 15.0) ⇒ Boolean
This method will determine if the loads on a space are similar.
-
#are_zone_loads_similar?(zone_1:, zone_2:) ⇒ Boolean
This method will determine if the loads on a zone are similar.
-
#assign_base_sys_name(airloop, sys_abbr:, sys_oa:, sys_name_pars:) ⇒ Object
Method to set the base system name based on the following syntax: |sys_abbr|sys_oa|shr>?|sc>?|sh>?|ssf>?|zh>?|zc>?|srf>?| “sys_abbr” designates the NECB system type (“sys_1, sys_2, … sys_6”) “sys_oa”: “mixed” or “doas” “sys_name_pars” is a hash for the remaining system name parts for heat recovery, heating, cooling, supply fan, zone heating, zone cooling, and return fan.
- #assign_contruction_to_adiabatic_surfaces(model) ⇒ Object
-
#auto_size_shw_capacity(model:, u: 0.45, height_to_radius: 2, shw_scale: 'NECB_Default') ⇒ Object
This calculates the volume and capacity of one mixed tank that is assumed to service all shw in the building u is the tank insulation in W/(m^2*K), height_to_radius is the ratio of tank radius to tank height and is dimensionless.
-
#auto_size_shw_pump_head(model, default: true, pipe_vel: 1.75, kin_visc_SI: 0.000004736, density_SI: 983, pipe_rough_m: 0.0000015) ⇒ Object
Autosize the pump head by calculating the piping longest piping length and deriving required head from that.
-
#auto_system_all_other_spaces(baseboard_type:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', boiler_fueltype:, chiller_type:, fan_type:, heating_coil_type_sys3:, heating_coil_type_sys4:, heating_coil_type_sys6:, hw_loop:, mau_cooling_type:, mau_heating_coil_type:, mau_type:, model:) ⇒ Object
This method will deal with all non wet, non-wild, and non-dwelling units thermal zones.
-
#auto_system_dwelling_units(baseboard_type:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', boiler_fueltype:, chiller_type:, fan_type:, heating_coil_type_sys3:, heating_coil_type_sys4:, heating_coil_type_sys6:, hw_loop:, mau_cooling_type:, mau_heating_coil_type:, mau_type:, model:, baseline_system_zones_map_option:) ⇒ Object
This method will ensure that all dwelling units are assigned to a system 1 or 3.
-
#auto_system_storage_spaces(baseboard_type:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', boiler_fueltype:, heating_coil_type_sys4:, model:) ⇒ Object
All wet spaces will be on their own system 4 AHU.
-
#auto_system_wet_spaces(baseboard_type:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', boiler_fueltype:, heating_coil_type_sys4:, model:) ⇒ Object
All wet spaces will be on their own system 4 AHU.
-
#auto_system_wild_spaces(baseboard_type:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'Defaultfuel', heating_coil_type_sys4:, model:) ⇒ Object
All wild spaces will be on a single system 4 ahu with the largests heating load zone being the control zone.
-
#auto_zone_all_other_spaces(model) ⇒ Object
This method will find all the spaces that are not wet, wild or dwelling units and zone them.
-
#auto_zone_dwelling_units(model) ⇒ Object
Dwelling unit spaces need to have their own HVAC system.
-
#auto_zone_wet_spaces(model:, lights_type: 'NECB_Default', lights_scale: 1.0) ⇒ Object
Something that the code is silent on are smelly humid areas that should not be on the same system as the rest of the building..
-
#auto_zone_wild_spaces(model:, lights_type: 'NECB_Default', lights_scale: 1.0) ⇒ Object
This will take all the wildcard spaces and merge them to be supported by a system 4.
-
#boiler_hot_water_apply_efficiency_and_curves(boiler_hot_water) ⇒ Boolean
Applies the standard efficiency ratings and typical performance curves to this object.
-
#boiler_hot_water_find_search_criteria(boiler_hot_water) ⇒ Hash
find search criteria.
- #check_boolean_value(value, varname) ⇒ Object
-
#check_datapoint_weather_folder(epw_file:, weather_folder:, custom_weather_folder: nil) ⇒ Object
This method checks if a zip file containing weather data is stored in an external directory.
-
#chiller_electric_eir_apply_efficiency_and_curves(chiller_electric_eir, clg_tower_objs) ⇒ Boolean
Applies the standard efficiency ratings and typical performance curves to this object.
-
#clean_and_scale_model(model:, rotation_degrees: nil, scale_x: nil, scale_y: nil, scale_z: nil) ⇒ Object
This method cleans the model of any existing HVAC systems and applies any desired ratation or scaling to the model.
-
#coil_cooling_dx_multi_speed_apply_efficiency_and_curves(coil_cooling_dx_multi_speed, sql_db_vars_map) ⇒ Boolean
Applies the standard efficiency ratings and typical performance curves to this object.
-
#coil_dx_heating_type(coil_dx, necb_reference_hp = false) ⇒ Object
NECB reference heat pump system heating type rules need to be flexible to account for 1.
- #coil_heating_dx_single_speed_find_capacity(coil_heating_dx_single_speed, necb_reference_hp = false) ⇒ Object
-
#coil_heating_gas_apply_efficiency_and_curves(coil_heating_gas) ⇒ Boolean
Applies the standard efficiency ratings and typical performance curves to this object.
-
#coil_heating_gas_find_capacity(coil_heating_gas) ⇒ Hash
find furnace capacity.
-
#coil_heating_gas_find_search_criteria ⇒ Hash
find search criteria.
-
#coil_heating_gas_multi_stage_apply_efficiency_and_curves(coil_heating_gas_multi_stage) ⇒ Boolean
Applies the standard efficiency ratings and typical performance curves to this object.
-
#coil_heating_gas_standard_minimum_thermal_efficiency(coil_heating_gas, rename = false) ⇒ Double
Finds lookup object in standards and return minimum thermal efficiency.
- #common_air_loop(model:, system_data:) ⇒ Object
-
#convert_arg_to_bool(variable:, default:) ⇒ Object
This method converts arguments to bool.
-
#convert_arg_to_f(variable:, default:) ⇒ Object
This is a helper method to convert arguments that may support ‘NECB_Default, and nils to convert to float’.
-
#convert_arg_to_string(variable:, default:) ⇒ Object
This method checks if a variable is a string.
-
#corrupt_standards_database ⇒ Object
2019-05-23 ckirney This is an ugly, disgusting, hack (hence the name) that I dreamed out so that we could quickly and easily finish the merge from the nrcan branch (using OS 2.6.0) to master (using OS 2.8.0).
-
#create_base_data(model) ⇒ Object
Generates the base data hash mainly used to perform qaqc.
-
#create_ems_to_turn_on_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed_for_night_cycle(multi_speed_heat_pump) ⇒ Object
Create EMS to turn on “AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed” in response to a call from the night cycle availability manager of the air loop.
- #create_heating_cooling_on_off_availability_schedule(model) ⇒ Object
-
#create_hw_loop_if_required(baseboard_type, boiler_fueltype, backup_boiler_fueltype, mau_heating_coil_type, model) ⇒ Object
Method will create a hot water loop if systems default fuel and medium sources require it.
-
#create_necb_system(baseboard_type:, boiler_fueltype:, chiller_type:, fan_type:, heating_coil_type_sys3:, heating_coil_type_sys4:, heating_coil_type_sys6:, hw_loop:, mau_cooling_type:, mau_heating_coil_type:, mau_type:, model:, zones:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel') ⇒ Object
Default method to create a necb system and assign array of zones to be supported by it.
-
#determine_control_zone(zones) ⇒ Object
This method will determine the control zone from the last sizing run space loads.
-
#determine_dominant_necb_schedule_type(model) ⇒ Object
This model determines the dominant NECB schedule type return s.each [String].
- #determine_dominant_schedule(spaces) ⇒ Object
-
#determine_necb_schedule_type(space) ⇒ String
This method determines the spacetype schedule type.
-
#determine_spacetype_vintage(model) ⇒ Object
this method will determine the vintage of NECB spacetypes the model contains.
-
#distance(loc1, loc2) ⇒ Object
Enter in [latitude, longitude] for each loc and this method will return the distance.
-
#download_and_save_file(weather_list_url:, weather_loc:, git_folder:) ⇒ Object
Arguments: weather_list_url (string): the web address of the json file containing the list of weather files on the repository weather_loc (string): the name of the epw file we are looking for without the .epw extension git_folder (string): the url of the folder containing the weather files.
-
#extract_weather_data(zipped_file:, weather_dir:) ⇒ Object
This method extracts data from a zip file.
-
#fan_baseline_impeller_efficiency(fan) ⇒ Double
Determines the baseline fan impeller efficiency based on the specified fan type.
- #fan_constant_volume_apply_prototype_fan_pressure_rise(fan_constant_volume) ⇒ Object
-
#fan_standard_minimum_motor_efficiency_and_size(fan, motor_bhp) ⇒ Array<Double>
Determines the minimum fan motor efficiency and nominal size for a given motor bhp.
-
#fan_variable_volume_apply_prototype_fan_pressure_rise(fan_variable_volume) ⇒ Object
Sets the fan pressure rise based on the Prototype buildings inputs which are governed by the flow rate coming through the fan and whether the fan lives inside a unit heater, PTAC, etc.
-
#fan_variable_volume_part_load_fan_power_limitation?(fan_variable_volume) ⇒ Boolean
Determines whether there is a requirement to have a VSD or some other method to reduce fan power at low part load ratios.
-
#find_mech_room(model) ⇒ Object
This method determines where the mechanical room is in a building.
- #friction_factor(re_pipe, relative_rough) ⇒ Object
- #get_all_spacetype_names ⇒ Object
-
#get_any_number_ppm(model) ⇒ Object
Define ScheduleTypeLimits for Any_Number_ppm.
-
#get_climate_zone_index(hdd) ⇒ Fixnum
This model gets the climate zone column index from tables 3.2.2.x.
-
#get_climate_zone_name(hdd) ⇒ Fixnum
This model gets the climate zone name and returns the climate zone string.
-
#get_max_space_height_for_space_type(space_type:) ⇒ Object
This method is needed to the space_type_apply_internal_loads space needed for the calculation of atriums’ LPD when LED lighting is used in atriums.
- #get_necb_hdd18(model:, necb_hdd: true) ⇒ Object
-
#get_necb_spacetype_system_selection(space) ⇒ Object
Determines what system index number is required for the space’s spacetype by NECB rules.
-
#get_necb_thermal_zone_system_selection(tz) ⇒ Object
Determines what system index number is required for the thermal zone based on the spacetypes it contains.
-
#get_parameters_sidelighting(daylight_space:, floor_surface:, floor_vertices:, floor_area:, primary_sidelighted_area:, area_weighted_vt_handle:, window_area_sum:) ⇒ Object
The below method is for the ‘model_add_daylighting_controls’ method.
-
#get_parameters_skylight(daylight_space:, skylight_area_weighted_vt_handle:, skylight_area_sum:, daylighted_under_skylight_area:) ⇒ Object
The below method is for the ‘model_add_daylighting_controls’ method.
- #get_qaqc_table(table_name:, search_criteria: nil) ⇒ Object
- #get_sql_table_to_json(model, report_name, report_for_string, table_name) ⇒ Object
- #get_sql_tables_to_json(model) ⇒ Object
- #get_standard_constant_value(constant_name:) ⇒ Object
- #get_standards_constant(name) ⇒ Object
- #get_standards_formula(name) ⇒ Object
- #get_standards_table(table_name:) ⇒ Object
-
#get_surface_exp_per(floor, walls) ⇒ Object
Find the exposed perimeter of a floor surface.
-
#get_weather_file_from_repo(epw_file:) ⇒ Object
This method handles looking for the epw_file in the github.com/canmet-energy/btap_weather repository.
-
#group_similar_zones_together(zones) ⇒ Object
This method is used to determine if there are single zones that can be grouped with zones of similar loads.
-
#heat_exchanger_air_to_air_sensible_and_latent_apply_effectiveness(heat_exchanger_air_to_air_sensible_and_latent, erv_name = nil) ⇒ Object
Sets the minimum effectiveness of the heat exchanger per the standard.
-
#init_qaqc(model) ⇒ Object
generates full qaqc.json.
-
#initialize ⇒ NECB2011
constructor
A new instance of NECB2011.
-
#is_a_necb_dwelling_unit?(space) ⇒ Boolean
Check if the space spactype is a dwelling unit as per NECB.
-
#is_an_necb_storage_space?(space) ⇒ Boolean
Check to see if this is a wet space that the NECB does not have a specified schedule or system for.
-
#is_an_necb_wet_space?(space) ⇒ Boolean
Check to see if this is a wet space that the NECB does not have a specified schedule or system for.
-
#is_an_necb_wildcard_space?(space) ⇒ Boolean
Check to see if this is a wildcard space that the NECB does not have a specified schedule or system for.
- #load_building_type_from_library(building_type:) ⇒ Object
- #load_qaqc_database_new ⇒ Object
-
#load_standards_database_new ⇒ Object
Combine the data from the JSON files into a single hash Load JSON files differently depending on whether loading from the OpenStudio CLI embedded filesystem or from typical gem installation.
- #look_up_csv_data(csv_fname, search_criteria) ⇒ Object
-
#max_fwdr(hdd) ⇒ Double
A constant float.
- #merge_recursively(a, b) ⇒ Object
- #model_add_construction_set_from_osm(model:, construction_set_name: 'BTAP-Mass', osm_path: File.absolute_path(File.join(__FILE__, '..', '..', 'common/construction_defaults.osm'))) ⇒ Object
-
#model_add_constructions(model) ⇒ Boolean
Adds code-minimum constructions based on the building type as defined in the OpenStudio_Standards_construction_sets.json file.
- #model_add_daylighting_controls(model:, daylighting_type:) ⇒ Object
- #model_add_hvac(model:) ⇒ Object
-
#model_add_loads(model, lights_type, lights_scale) ⇒ Boolean
Adds the loads and associated schedules for each space type as defined in the OpenStudio_Standards_space_types.json file.
-
#model_add_schedule(model, schedule_name) ⇒ ScheduleRuleset
Create a schedule from the openstudio standards dataset and add it to the model.
- #model_add_swh(model:, swh_fueltype: 'DefaultFuel', shw_scale:) ⇒ Object
- #model_apply_sizing_parameters(model) ⇒ Object
-
#model_apply_standard(model:, tbd_option: nil, tbd_interpolate: nil, epw_file:, custom_weather_folder: nil, sizing_run_dir: Dir.pwd, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', primary_heating_fuel: 'DefaultFuel', swh_fuel: 'DefaultFuel', dcv_type: 'NECB_Default', lights_type: 'NECB_Default', lights_scale: 'NECB_Default', daylighting_type: 'NECB_Default', ecm_system_name: 'NECB_Default', ecm_system_zones_map_option: 'NECB_Default', erv_package: 'NECB_Default', boiler_eff: nil, furnace_eff: nil, unitary_cop: nil, shw_eff: nil, ext_wall_cond: nil, ext_floor_cond: nil, ext_roof_cond: nil, ground_wall_cond: nil, ground_floor_cond: nil, ground_roof_cond: nil, door_construction_cond: nil, fixed_window_cond: nil, glass_door_cond: nil, overhead_door_cond: nil, skylight_cond: nil, glass_door_solar_trans: nil, fixed_wind_solar_trans: nil, skylight_solar_trans: nil, fdwr_set: nil, srr_set: nil, rotation_degrees: nil, scale_x: nil, scale_y: nil, scale_z: nil, nv_type: nil, nv_opening_fraction: nil, nv_temp_out_min: nil, nv_delta_temp_in_out: nil, pv_ground_type: nil, pv_ground_total_area_pv_panels_m2: nil, pv_ground_tilt_angle: nil, pv_ground_azimuth_angle: nil, pv_ground_module_description: nil, chiller_type: nil, occupancy_loads_scale: nil, electrical_loads_scale: nil, oa_scale: nil, infiltration_scale: nil, output_variables: nil, shw_scale: nil, output_meters: nil, airloop_economizer_type: nil, baseline_system_zones_map_option: nil, necb_hdd: true, boiler_fuel: nil, boiler_cap_ratio: nil) ⇒ Object
Created this method so that additional methods can be addded for bulding the prototype model in later code versions without modifying the build_protoype_model method or copying it wholesale for a few changes.
-
#model_create_prototype_model(template:, building_type:, epw_file:, custom_weather_folder: nil, debug: false, sizing_run_dir: Dir.pwd, primary_heating_fuel: 'Electricity', swh_fuel: 'NECB_Default', dcv_type: 'NECB_Default', lights_type: 'NECB_Default', lights_scale: 1.0, daylighting_type: 'NECB_Default', ecm_system_name: 'NECB_Default', ecm_system_zones_map_option: 'NECB_Default', erv_package: 'NECB_Default', boiler_eff: nil, unitary_cop: nil, furnace_eff: nil, shw_eff: nil, ext_wall_cond: nil, ext_floor_cond: nil, ext_roof_cond: nil, ground_wall_cond: nil, ground_floor_cond: nil, ground_roof_cond: nil, door_construction_cond: nil, fixed_window_cond: nil, glass_door_cond: nil, overhead_door_cond: nil, skylight_cond: nil, glass_door_solar_trans: nil, fixed_wind_solar_trans: nil, skylight_solar_trans: nil, rotation_degrees: nil, fdwr_set: -1.0,, srr_set: -1.0,, nv_type: nil, nv_opening_fraction: nil, nv_temp_out_min: nil, nv_delta_temp_in_out: nil, scale_x: nil, scale_y: nil, scale_z: nil, pv_ground_type: nil, pv_ground_total_area_pv_panels_m2: nil, pv_ground_tilt_angle: nil, pv_ground_azimuth_angle: nil, pv_ground_module_description: nil, chiller_type: nil, occupancy_loads_scale: nil, electrical_loads_scale: nil, oa_scale: nil, infiltration_scale: nil, output_variables: nil, shw_scale: nil, output_meters: nil, airloop_economizer_type: nil, baseline_system_zones_map_option: nil, tbd_option: nil, tbd_interpolate: false, necb_hdd: true, boiler_fuel: nil, boiler_cap_ratio: nil) ⇒ Object
This method is a wrapper to create the 16 archetypes easily.
-
#model_create_thermal_zones(model, space_multiplier_map = nil) ⇒ Boolean
Creates thermal zones to contain each space, as defined for each building in the system_to_space_map inside the Prototype.building_name e.g.
-
#model_enable_demand_controlled_ventilation(model, dcv_type = 'No_DCV') ⇒ Object
Note: Values for dcv_type are: ‘Occupancy_based_DCV’, ‘CO2_based_DCV’, ‘No_DCV’, ‘NECB_Default’.
-
#model_find_climate_zone_set(model, clim) ⇒ Object
Helper method to find out which climate zone set contains a specific climate zone.
- #necb_design_supply_temp_compliance(qaqc) ⇒ Object
- #necb_economizer_compliance(qaqc) ⇒ Object
-
#necb_envelope_compliance(qaqc) ⇒ Object
checks envelope compliance fenestration_to_door_and_window_percentage, skylight_to_roof_percentage.
- #necb_exterior_fenestration_compliance(qaqc) ⇒ Object
- #necb_exterior_ground_surfaces_compliance(qaqc) ⇒ Object
- #necb_exterior_opaque_compliance(qaqc) ⇒ Object
- #necb_hrv_compliance(qaqc, model) ⇒ Object
-
#necb_hrv_compliance_for_single_airloop(qaqc, model, air_loop_info) ⇒ Object
This methos is not used as part of the QAQC process, because support for MURBS has not been implemented for HRV in NECB 2011 and 2015.
-
#necb_hrv_compliance_inc_murb(qaqc, model) ⇒ Object
This methos is not used as part of the QAQC process, because support for MURBS has not been implemented for HRV in NECB 2011 and 2015.
- #necb_infiltration_compliance(qaqc, model) ⇒ Object
-
#necb_plantloop_sanity(qaqc) ⇒ Object
checks the pump power using pressure, and flowrate.
- #necb_qaqc(qaqc, model) ⇒ Object
- #necb_section_test(qaqc, result_value, bool_operator, expected_value, necb_section_name, test_text, tolerance = nil) ⇒ Object
-
#necb_space_compliance(qaqc) ⇒ Object
checks space compliance Re: lighting_per_area, occupancy_per_area, occupancy_schedule, electric_equipment_per_area.
- #necb_vav_fan_power_compliance(qaqc) ⇒ Object
- #necb_zone_sizing_compliance(qaqc) ⇒ Object
- #new_add_sys6_multi_zone_built_up_system_with_baseboard_heating(model:, zones:, heating_coil_type:, baseboard_type:, chiller_type:, fan_type:, hw_loop:) ⇒ Object
-
#percentage_difference(value_1, value_2) ⇒ Object
Math fundtion to determine percent difference.
-
#pump_standard_minimum_motor_efficiency_and_size(pump, motor_bhp) ⇒ Array<Double>
Determines the minimum pump motor efficiency and nominal size for a given motor bhp.
-
#pump_variable_speed_control_type(pump) ⇒ Boolean
Determine and set type of part load control type for heating and chilled water variable speed pumps.
-
#qaqc_only(model) ⇒ Object
generates only qaqc component.
-
#replace_massless_material_with_std_material(model, surf) ⇒ Object
Loop through the layers of the construction of the surface and replace any massless material with a standard one.
-
#sanity_check(qaqc) ⇒ Object
Checks if a space with a proper schedule is conditioned or not.
- #scale_model_geometry(model, x_scale, y_scale, z_scale) ⇒ Object
-
#set_boiler_cap_ratios(boiler_cap_ratio:, boiler_fuel:) ⇒ Object
This method expects a string with the following pattern: number-number The first number is the percent of the total capacity that the primary boiler’s capacity will be set to.
- #set_lighting_per_area(space_type:, definition:, lighting_per_area:, lights_scale:) ⇒ Object
- #set_lighting_per_area_led_lighting(space_type:, definition:, lighting_per_area_led_lighting:, lights_scale:) ⇒ Object
-
#set_necb_external_subsurface_conductance(subsurface, hdd) ⇒ Object
Set all external subsurfaces (doors, windows, skylights) to NECB values.
-
#set_necb_external_surface_conductance(surface, hdd, is_radiant = false, scaling_factor = 1.0) ⇒ String
Set all external surface conductances to NECB values.
-
#set_occ_sensor_spacetypes(model, space_type_map) ⇒ Boolean
Returns true if successful, false if not.
- #set_output_meters(model:, output_meters:) ⇒ Object
- #set_output_variables(model:, output_variables:) ⇒ Object
-
#set_random_rendering_color(object, random) ⇒ Object
This method will create a color object used in SU, 3D Viewer and Floorspace.js.
- #set_wildcard_schedules_to_dominant_building_schedule(model, runner = nil) ⇒ Object
- #set_zones_thermostat_schedule_based_on_space_type_schedules(model, runner = nil) ⇒ Object
-
#setup_chw_loop_with_components(model, chw_loop, chiller_type) ⇒ Object
of setup_hw_loop_with_components.
-
#setup_cw_loop_with_components(model, cw_loop, chiller1, chiller2) ⇒ Object
of setup_chw_loop_with_components.
- #setup_hw_loop_with_components(model, hw_loop, boiler_fueltype, backup_boiler_fueltype, pump_flow_sch) ⇒ Object
-
#space_apply_infiltration_rate(space) ⇒ Double
Set the infiltration rate for this space to include the impact of air leakage requirements in the standard.
-
#space_surface_report(space) ⇒ Object
This method gathers the surface information for the space to determine if spaces are the same.
-
#space_type_apply_internal_loads(space_type:, set_people: true, set_lights: true, set_electric_equipment: true, set_gas_equipment: true, set_ventilation: true, set_infiltration: true, lights_type: 'NECB_Default', lights_scale: 1.0) ⇒ Boolean
Sets the selected internal loads to standards-based or typical values.
-
#store_space_sizing_loads(model) ⇒ Object
Method to store space sizing loads.
-
#stored_space_cooling_load(space) ⇒ Object
Returns the cooling load per area for space after sizing runs has been done.
-
#stored_space_heating_load(space) ⇒ Object
Returns heating load per area for space after sizing run has been done.
-
#stored_zone_cooling_load(zone) ⇒ Object
Returns the cooling load per area for zone after sizing runs has been done.
-
#stored_zone_heating_load(zone) ⇒ Object
# Returns the heating load per area for zone after sizing runs has been done.
-
#surfaces_are_in_contact?(surf1, surf2) ⇒ Boolean
check if two surfaces are in contact.
-
#thermal_zone_demand_control_ventilation_required?(thermal_zone, climate_zone) ⇒ Boolean
Determine if demand control ventilation (DCV) is required for this zone based on area and occupant density.
-
#thermal_zone_get_centroid_per_floor(thermal_zone) ⇒ Object
This method cycles through the spaces in a thermal zone and then sorts them by story.
-
#three_vertices_same_line_and_dir?(vert1, vert2, vert3) ⇒ Boolean
check that three vertices are on the same line.
-
#update_sys_name(airloop, sys_abbr: nil, sys_oa: nil, sys_hr: nil, sys_htg: nil, sys_clg: nil, sys_sf: nil, zone_htg: nil, zone_clg: nil, sys_rf: nil) ⇒ Object
Method to update the base system name based on the inputs provided.
-
#validate_and_upate_space_types(model) ⇒ Object
This method will validate that the space types in the model are indeed the correct NECB spacetypes names.
-
#validate_primary_heating_fuel(primary_heating_fuel:) ⇒ Object
This method is defined and used by the vintage classes to address he issue with the heat pump fuel types.
-
#water_heater_mixed_apply_efficiency(water_heater_mixed) ⇒ Boolean
Applies the standard efficiency ratings and typical losses and paraisitic loads to this object.
-
#zone_hvac_component_occupancy_ventilation_control(zone_hvac_component) ⇒ Object
do not apply zone hvac ventilation control.
Methods inherited from Standard
#adjust_sizing_system, #afue_to_thermal_eff, #air_loop_hvac_add_motorized_oa_damper, #air_loop_hvac_adjust_minimum_vav_damper_positions, #air_loop_hvac_adjust_minimum_vav_damper_positions_outpatient, #air_loop_hvac_allowable_system_brake_horsepower, #air_loop_hvac_apply_baseline_fan_pressure_rise, #air_loop_hvac_apply_economizer_limits, #air_loop_hvac_apply_energy_recovery_ventilator_efficiency, #air_loop_hvac_apply_maximum_reheat_temperature, #air_loop_hvac_apply_minimum_vav_damper_positions, #air_loop_hvac_apply_prm_baseline_controls, #air_loop_hvac_apply_prm_baseline_economizer, #air_loop_hvac_apply_prm_baseline_fan_power, #air_loop_hvac_apply_prm_sizing_temperatures, #air_loop_hvac_apply_standard_controls, #air_loop_hvac_data_center_area_served, #air_loop_hvac_dcv_required_when_erv, #air_loop_hvac_demand_control_ventilation_limits, #air_loop_hvac_disable_multizone_vav_optimization, #air_loop_hvac_dx_cooling?, #air_loop_hvac_economizer?, #air_loop_hvac_economizer_limits, #air_loop_hvac_economizer_type_allowable?, #air_loop_hvac_enable_demand_control_ventilation, #air_loop_hvac_enable_multizone_vav_optimization, #air_loop_hvac_enable_optimum_start, #air_loop_hvac_enable_supply_air_temperature_reset_delta, #air_loop_hvac_enable_supply_air_temperature_reset_outdoor_temperature, #air_loop_hvac_enable_supply_air_temperature_reset_warmest_zone, #air_loop_hvac_energy_recovery?, #air_loop_hvac_energy_recovery_ventilator_flow_limit, #air_loop_hvac_energy_recovery_ventilator_heat_exchanger_type, #air_loop_hvac_energy_recovery_ventilator_type, #air_loop_hvac_fan_power_limitation_pressure_drop_adjustment_brake_horsepower, #air_loop_hvac_find_design_supply_air_flow_rate, #air_loop_hvac_floor_area_served, #air_loop_hvac_floor_area_served_exterior_zones, #air_loop_hvac_floor_area_served_interior_zones, #air_loop_hvac_get_occupancy_schedule, #air_loop_hvac_get_relief_fan_power, #air_loop_hvac_get_return_fan_power, #air_loop_hvac_get_supply_fan, #air_loop_hvac_get_supply_fan_power, #air_loop_hvac_has_parallel_piu_air_terminals?, #air_loop_hvac_has_simple_transfer_air?, #air_loop_hvac_humidifier_count, #air_loop_hvac_include_cooling_coil?, #air_loop_hvac_include_economizer?, #air_loop_hvac_include_evaporative_cooler?, #air_loop_hvac_include_hydronic_cooling_coil?, #air_loop_hvac_include_unitary_system?, #air_loop_hvac_include_wshp?, #air_loop_hvac_integrated_economizer_required?, #air_loop_hvac_minimum_zone_ventilation_efficiency, #air_loop_hvac_motorized_oa_damper_required?, #air_loop_hvac_multi_stage_dx_cooling?, #air_loop_hvac_multizone_vav_optimization_required?, #air_loop_hvac_multizone_vav_system?, #air_loop_hvac_optimum_start_required?, #air_loop_hvac_prm_baseline_economizer_required?, #air_loop_hvac_prm_economizer_type_and_limits, #air_loop_hvac_remove_erv, #air_loop_hvac_remove_motorized_oa_damper, #air_loop_hvac_residential_area_served, #air_loop_hvac_return_air_plenum, #air_loop_hvac_set_minimum_damper_position, #air_loop_hvac_set_vsd_curve_type, #air_loop_hvac_single_zone_controls_num_stages, #air_loop_hvac_standby_mode_occupancy_control, #air_loop_hvac_supply_air_temperature_reset_required?, #air_loop_hvac_supply_return_exhaust_relief_fans, #air_loop_hvac_system_fan_brake_horsepower, #air_loop_hvac_system_multiplier, #air_loop_hvac_terminal_reheat?, #air_loop_hvac_total_cooling_capacity, #air_loop_hvac_unitary_system?, #air_loop_hvac_unoccupied_fan_shutoff_required?, #air_loop_hvac_unoccupied_threshold, #air_loop_hvac_vav_damper_action, #air_loop_hvac_vav_system?, #air_terminal_single_duct_parallel_piu_reheat_apply_minimum_primary_airflow_fraction, #air_terminal_single_duct_parallel_piu_reheat_apply_prm_baseline_fan_power, #air_terminal_single_duct_parallel_piu_reheat_fan_on_flow_fraction, #air_terminal_single_duct_parallel_reheat_piu_minimum_primary_airflow_fraction, #air_terminal_single_duct_vav_reheat_apply_initial_prototype_damper_position, #air_terminal_single_duct_vav_reheat_apply_minimum_damper_position, #air_terminal_single_duct_vav_reheat_minimum_damper_position, #air_terminal_single_duct_vav_reheat_reheat_type, #apply_lighting_schedule, #apply_limit_to_subsurface_ratio, #boiler_get_eff_fplr, #boiler_hot_water_find_capacity, #boiler_hot_water_find_design_water_flow_rate, #boiler_hot_water_standard_minimum_thermal_efficiency, build, #chiller_electric_eir_find_capacity, #chiller_electric_eir_find_search_criteria, #chiller_electric_eir_get_cap_f_t_curve_name, #chiller_electric_eir_get_eir_f_plr_curve_name, #chiller_electric_eir_get_eir_f_t_curve_name, #chiller_electric_eir_standard_minimum_full_load_efficiency, #chw_sizing_control, #coil_cooling_dx_multi_speed_find_capacity, #coil_cooling_dx_multi_speed_standard_minimum_cop, #coil_cooling_dx_single_speed_apply_efficiency_and_curves, #coil_cooling_dx_single_speed_find_capacity, #coil_cooling_dx_single_speed_standard_minimum_cop, #coil_cooling_dx_two_speed_apply_efficiency_and_curves, #coil_cooling_dx_two_speed_find_capacity, #coil_cooling_dx_two_speed_standard_minimum_cop, #coil_cooling_water_to_air_heat_pump_apply_efficiency_and_curves, #coil_cooling_water_to_air_heat_pump_find_capacity, #coil_cooling_water_to_air_heat_pump_standard_minimum_cop, #coil_heating_dx_multi_speed_apply_efficiency_and_curves, #coil_heating_dx_single_speed_apply_defrost_eir_curve_limits, #coil_heating_dx_single_speed_apply_efficiency_and_curves, #coil_heating_dx_single_speed_standard_minimum_cop, #coil_heating_gas_additional_search_criteria, #coil_heating_gas_apply_prototype_efficiency, #coil_heating_gas_multi_stage_find_capacity, #coil_heating_gas_multi_stage_find_search_criteria, #coil_heating_water_to_air_heat_pump_apply_efficiency_and_curves, #coil_heating_water_to_air_heat_pump_find_capacity, #coil_heating_water_to_air_heat_pump_standard_minimum_cop, #combustion_eff_to_thermal_eff, #controller_water_coil_set_convergence_limits, #convert_curve_biquadratic, #cooling_tower_single_speed_apply_efficiency_and_curves, #cooling_tower_two_speed_apply_efficiency_and_curves, #cooling_tower_variable_speed_apply_efficiency_and_curves, #cop_heating_to_cop_heating_no_fan, #cop_no_fan_to_eer, #cop_no_fan_to_seer, #cop_to_eer, #cop_to_kw_per_ton, #cop_to_seer, #create_air_conditioner_variable_refrigerant_flow, #create_boiler_hot_water, #create_central_air_source_heat_pump, #create_coil_cooling_dx_single_speed, #create_coil_cooling_dx_two_speed, #create_coil_cooling_water, #create_coil_cooling_water_to_air_heat_pump_equation_fit, #create_coil_heating_dx_single_speed, #create_coil_heating_electric, #create_coil_heating_gas, #create_coil_heating_water, #create_coil_heating_water_to_air_heat_pump_equation_fit, #create_curve_bicubic, #create_curve_biquadratic, #create_curve_cubic, #create_curve_exponent, #create_curve_quadratic, #create_fan_constant_volume, #create_fan_constant_volume_from_json, #create_fan_on_off, #create_fan_on_off_from_json, #create_fan_variable_volume, #create_fan_variable_volume_from_json, #create_fan_zone_exhaust, #create_fan_zone_exhaust_from_json, #define_space_multiplier, #eer_to_cop, #eer_to_cop_no_fan, #ems_friendly_name, #enthalpy_recovery_ratio_design_to_typical_adjustment, #fan_constant_volume_airloop_fan_pressure_rise, #fan_on_off_airloop_or_unitary_fan_pressure_rise, #fan_on_off_apply_prototype_fan_pressure_rise, #fan_variable_volume_airloop_fan_pressure_rise, #fan_variable_volume_cooling_system_type, #fan_variable_volume_part_load_fan_power_limitation_capacity_limit, #fan_variable_volume_part_load_fan_power_limitation_hp_limit, #fan_variable_volume_set_control_type, #fan_zone_exhaust_apply_prototype_fan_pressure_rise, #find_exposed_conditioned_roof_surfaces, #find_exposed_conditioned_vertical_surfaces, #find_highest_roof_centre, #fluid_cooler_apply_minimum_power_per_flow, #get_avg_of_other_zones, #get_default_surface_cons_from_surface_type, #get_fan_object_for_airloop, #get_fan_schedule_for_each_zone, #get_group_heat_types, #get_outdoor_subsurface_ratio, #get_weekday_values_from_8760, #get_wtd_avg_of_other_zones, #headered_pumps_variable_speed_set_control_type, #heat_exchanger_air_to_air_sensible_and_latent_apply_prototype_efficiency, #heat_exchanger_air_to_air_sensible_and_latent_apply_prototype_efficiency_enthalpy_recovery_ratio, #heat_exchanger_air_to_air_sensible_and_latent_apply_prototype_nominal_electric_power, #heat_exchanger_air_to_air_sensible_and_latent_enthalpy_recovery_ratio_to_effectiveness, #heat_exchanger_air_to_air_sensible_and_latent_minimum_effectiveness, #heat_exchanger_air_to_air_sensible_and_latent_prototype_default_fan_efficiency, #hspf_to_cop, #hspf_to_cop_no_fan, #interior_lighting_get_prm_data, #kw_per_ton_to_cop, #load_hvac_map, #load_initial_osm, #load_standards_database, #make_ruleset_sched_from_8760, #make_week_ruleset_sched_from_168, #model_add_baseboard, #model_add_cav, #model_add_central_air_source_heat_pump, #model_add_chw_loop, #model_add_construction, #model_add_construction_set, #model_add_crac, #model_add_crah, #model_add_curve, #model_add_cw_loop, #model_add_data_center_hvac, #model_add_data_center_load, #model_add_district_ambient_loop, #model_add_doas, #model_add_doas_cold_supply, #model_add_elevator, #model_add_elevators, #model_add_evap_cooler, #model_add_exhaust_fan, #model_add_four_pipe_fan_coil, #model_add_furnace_central_ac, #model_add_ground_hx_loop, #model_add_high_temp_radiant, #model_add_hp_loop, #model_add_hvac_system, #model_add_hw_loop, #model_add_ideal_air_loads, #model_add_low_temp_radiant, #model_add_material, #model_add_minisplit_hp, #model_add_plant_supply_water_temperature_control, #model_add_prm_baseline_system, #model_add_prm_elevators, #model_add_psz_ac, #model_add_psz_vav, #model_add_ptac, #model_add_pthp, #model_add_pvav, #model_add_pvav_pfp_boxes, #model_add_radiant_basic_controls, #model_add_radiant_proportional_controls, #model_add_refrigeration_case, #model_add_refrigeration_compressor, #model_add_refrigeration_system, #model_add_refrigeration_walkin, #model_add_residential_erv, #model_add_residential_ventilator, #model_add_split_ac, #model_add_swh_end_uses_by_space, #model_add_transformer, #model_add_typical_exterior_lights, #model_add_typical_refrigeration, #model_add_typical_swh, #model_add_unitheater, #model_add_vav_pfp_boxes, #model_add_vav_reheat, #model_add_vrf, #model_add_water_source_hp, #model_add_waterside_economizer, #model_add_window_ac, #model_add_zone_erv, #model_add_zone_heat_cool_request_count_program, #model_add_zone_ventilation, #model_apply_baseline_exterior_lighting, #model_apply_hvac_efficiency_standard, #model_apply_infiltration_standard, #model_apply_multizone_vav_outdoor_air_sizing, #model_apply_prm_baseline_sizing_schedule, #model_apply_prm_baseline_skylight_to_roof_ratio, #model_apply_prm_baseline_window_to_wall_ratio, #model_apply_prm_construction_types, #model_apply_prm_sizing_parameters, #model_apply_standard_constructions, #model_apply_standard_infiltration, #model_baseline_system_vav_fan_type, #model_create_exterior_lighting_area_length_count_hash, #model_create_multizone_fan_schedule, #model_create_prm_any_baseline_building, #model_create_prm_baseline_building, #model_create_prm_baseline_building_requires_proposed_model_sizing_run, #model_create_prm_baseline_building_requires_vlt_sizing_run, #model_create_prm_proposed_building, #model_create_prm_stable_baseline_building, #model_create_space_type_hash, #model_create_story_hash, #model_cw_loop_cooling_tower_fan_type, #model_differentiate_primary_secondary_thermal_zones, #model_effective_num_stories, #model_elevator_fan_pwr, #model_elevator_lift_power, #model_elevator_lighting_pct_incandescent, #model_eliminate_outlier_zones, #model_find_and_add_construction, #model_find_ashrae_hot_water_demand, #model_find_icc_iecc_2015_hot_water_demand, #model_find_icc_iecc_2015_internal_loads, #model_find_object, #model_find_objects, #model_find_prototype_floor_area, #model_find_target_eui, #model_find_target_eui_by_end_use, #model_find_water_heater_capacity_volume_and_parasitic, #model_get_baseline_system_type_by_zone, #model_get_building_properties, #model_get_climate_zone_set_from_list, #model_get_construction_properties, #model_get_construction_set, #model_get_district_heating_zones, #model_get_lookup_name, #model_get_or_add_ambient_water_loop, #model_get_or_add_chilled_water_loop, #model_get_or_add_ground_hx_loop, #model_get_or_add_heat_pump_loop, #model_get_or_add_hot_water_loop, #model_is_hvac_autosized, #model_legacy_results_by_end_use_and_fuel_type, #model_make_name, #model_prm_baseline_system_change_fuel_type, #model_prm_baseline_system_groups, #model_prm_baseline_system_number, #model_prm_baseline_system_type, #model_prm_skylight_to_roof_ratio_limit, #model_process_results_for_datapoint, #model_remap_office, #model_remove_external_shading_devices, #model_remove_prm_ems_objects, #model_remove_prm_hvac, #model_remove_unused_resource_objects, #model_set_vav_terminals_to_control_for_outdoor_air, #model_system_outdoor_air_sizing_vrp_method, #model_two_pipe_loop, #model_typical_display_case_zone, #model_typical_hvac_system_type, #model_typical_walkin_zone, #model_validate_standards_spacetypes_in_model, #model_ventilation_method, #model_walkin_freezer_latent_case_credit_curve, #model_zones_with_occ_and_fuel_type, #planar_surface_apply_standard_construction, #plant_loop_adiabatic_pipes_only, #plant_loop_apply_prm_baseline_chilled_water_pumping_type, #plant_loop_apply_prm_baseline_chilled_water_temperatures, #plant_loop_apply_prm_baseline_condenser_water_pumping_type, #plant_loop_apply_prm_baseline_condenser_water_temperatures, #plant_loop_apply_prm_baseline_hot_water_pumping_type, #plant_loop_apply_prm_baseline_hot_water_temperatures, #plant_loop_apply_prm_baseline_pump_power, #plant_loop_apply_prm_baseline_pumping_type, #plant_loop_apply_prm_baseline_temperatures, #plant_loop_apply_prm_number_of_boilers, #plant_loop_apply_prm_number_of_chillers, #plant_loop_apply_prm_number_of_cooling_towers, #plant_loop_apply_standard_controls, #plant_loop_capacity_w_by_maxflow_and_delta_t_forwater, #plant_loop_enable_supply_water_temperature_reset, #plant_loop_find_maximum_loop_flow_rate, #plant_loop_prm_baseline_condenser_water_temperatures, #plant_loop_set_chw_pri_sec_configuration, #plant_loop_supply_water_temperature_reset_required?, #plant_loop_swh_loop?, #plant_loop_swh_system_type, #plant_loop_total_cooling_capacity, #plant_loop_total_floor_area_served, #plant_loop_total_heating_capacity, #plant_loop_total_rated_w_per_gpm, #plant_loop_variable_flow_system?, #prototype_apply_condenser_water_temperatures, #prototype_condenser_water_temperatures, #pump_variable_speed_get_control_type, #pump_variable_speed_set_control_type, register_standard, #remove_air_loops, #remove_all_hvac, #remove_all_plant_loops, #remove_all_zone_equipment, #remove_hvac, #remove_plant_loops, #remove_unused_curves, #remove_vrf, #remove_zone_equipment, #rename_air_loop_nodes, #rename_plant_loop_nodes, #safe_load_model, #seer_to_cop, #seer_to_cop_no_fan, #set_maximum_fraction_outdoor_air_schedule, #space_add_daylighting_controls, #space_conditioning_category, #space_daylighted_area_window_width, #space_daylighted_areas, #space_daylighting_control_required?, #space_daylighting_fractions_and_windows, #space_get_equip_annual_array, #space_get_loads_for_all_equips, #space_infiltration_rate_75_pa, #space_internal_load_annual_array, #space_occupancy_annual_array, #space_remove_daylighting_controls, #space_set_baseline_daylighting_controls, #space_sidelighting_effective_aperture, #space_skylight_effective_aperture, #space_type_apply_int_loads_prm, #space_type_apply_internal_load_schedules, #space_type_apply_rendering_color, #space_type_get_construction_properties, #space_type_get_standards_data, #space_type_light_sch_change, #standard_design_sizing_temperatures, #standards_lookup_table_first, #standards_lookup_table_many, #strip_model, #sub_surface_create_centered_subsurface_from_scaled_surface, #sub_surface_create_scaled_subsurfaces_from_surface, #surface_adjust_fenestration_in_a_surface, #surface_subsurface_ua, #thermal_eff_to_afue, #thermal_eff_to_comb_eff, #thermal_zone_add_exhaust, #thermal_zone_add_exhaust_fan_dcv, #thermal_zone_apply_prm_baseline_supply_temperatures, #thermal_zone_conditioning_category, #thermal_zone_demand_control_ventilation_limits, #thermal_zone_exhaust_fan_dcv_required?, #thermal_zone_fossil_or_electric_type, #thermal_zone_get_annual_operating_hours, #thermal_zone_get_zone_fuels_for_occ_and_fuel_type, #thermal_zone_infer_system_type, #thermal_zone_occupancy_eflh, #thermal_zone_occupancy_type, #thermal_zone_peak_internal_load, #thermal_zone_prm_baseline_cooling_design_supply_temperature, #thermal_zone_prm_baseline_heating_design_supply_temperature, #thermal_zone_prm_lab_delta_t, #thermal_zone_prm_unitheater_design_supply_temperature, #true?, #validate_initial_model, #water_heater_convert_energy_factor_to_thermal_efficiency_and_ua, #water_heater_convert_uniform_energy_factor_to_energy_factor, #water_heater_determine_sub_type, #water_heater_mixed_additional_search_criteria, #water_heater_mixed_apply_prm_baseline_fuel_type, #water_heater_mixed_find_capacity, #water_heater_mixed_get_efficiency_requirement, #zone_hvac_component_apply_prm_baseline_fan_power, #zone_hvac_component_apply_standard_controls, #zone_hvac_component_apply_vestibule_heating_control, #zone_hvac_component_prm_baseline_fan_efficacy, #zone_hvac_component_vestibule_heating_control_required?, #zone_hvac_get_fan_object, #zone_hvac_model_standby_mode_occupancy_control, #zone_hvac_unoccupied_threshold
Methods included from PrototypeFan
apply_base_fan_variables, #create_fan_by_name, #get_fan_from_standards, #lookup_fan_curve_coefficients_from_json, #prototype_fan_apply_prototype_fan_efficiency
Methods included from CoilDX
#coil_dx_find_search_criteria, #coil_dx_heat_pump?, #coil_dx_subcategory
Methods included from CoolingTower
#cooling_tower_apply_minimum_power_per_flow, #cooling_tower_apply_minimum_power_per_flow_gpm_limit
Methods included from Pump
#pump_apply_prm_pressure_rise_and_motor_efficiency, #pump_apply_standard_minimum_motor_efficiency, #pump_brake_horsepower, #pump_motor_horsepower, #pump_pumppower, #pump_rated_w_per_gpm
Methods included from Fan
#fan_adjust_pressure_rise_to_meet_fan_power, #fan_apply_standard_minimum_motor_efficiency, #fan_brake_horsepower, #fan_change_impeller_efficiency, #fan_change_motor_efficiency, #fan_design_air_flow, #fan_fanpower, #fan_motor_horsepower, #fan_rated_w_per_cfm, #fan_small_fan?
Constructor Details
#initialize ⇒ NECB2011
Returns a new instance of NECB2011.
146 147 148 149 150 151 152 153 154 155 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 146 def initialize super() @template = self.class.name @standards_data = load_standards_database_new corrupt_standards_database @tbd = nil # puts "loaded these tables..." # puts @standards_data.keys.size # raise("tables not all loaded in parent #{}") if @standards_data.keys.size < 24 end |
Instance Attribute Details
#fuel_type_set ⇒ Object
Returns the value of attribute fuel_type_set.
14 15 16 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 14 def fuel_type_set @fuel_type_set end |
#qaqc_data ⇒ Object
Returns the value of attribute qaqc_data.
2 3 4 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 2 def qaqc_data @qaqc_data end |
#space_multiplier_map ⇒ Object
Returns the value of attribute space_multiplier_map.
13 14 15 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 13 def space_multiplier_map @space_multiplier_map end |
#space_type_map ⇒ Object
Returns the value of attribute space_type_map.
12 13 14 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 12 def space_type_map @space_type_map end |
#standards_data ⇒ Object
Returns the value of attribute standards_data.
11 12 13 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 11 def standards_data @standards_data end |
#tbd ⇒ Object (readonly)
Returns the value of attribute tbd.
9 10 11 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 9 def tbd @tbd end |
#template ⇒ Object (readonly)
Returns the value of attribute template.
10 11 12 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 10 def template @template end |
Instance Method Details
#add_all_spacetypes_to_model(model) ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/beps_compliance_path.rb', line 2 def add_all_spacetypes_to_model(model) # Get the space Type data from @standards data spacetype_data = nil if @standards_data['space_types'].is_a?(Hash) == true spacetype_data = @standards_data['space_types']['table'] else spacetype_data = @standards_data['space_types'] end spacetype_data.each do |spacedata| space_type = OpenStudio::Model::SpaceType.new(model) space_type.setStandardsSpaceType(spacedata['space_type']) space_type.setStandardsBuildingType(spacedata['building_type']) space_type.setName("#{spacedata['building_type']} #{spacedata['space_type']}") # Loads space_type_apply_internal_loads(space_type: space_type) # Schedules space_type_apply_internal_load_schedules(space_type, true, true, true, true, true, true, true) end end |
#add_onespeed_DX_coil(model, always_on) ⇒ Object
Create a new DX cooling coil with NECB curve characteristics
1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1933 def add_onespeed_DX_coil(model, always_on) # clg_cap_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model) # clg_cap_f_of_temp = model_add_curve("DXCOOL-NECB2011-REF-CAPFT") clg_cap_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model) clg_cap_f_of_temp.setCoefficient1Constant(0.867905) clg_cap_f_of_temp.setCoefficient2x(0.0142459) clg_cap_f_of_temp.setCoefficient3xPOW2(0.000554364) clg_cap_f_of_temp.setCoefficient4y(-0.00755748) clg_cap_f_of_temp.setCoefficient5yPOW2(3.3048e-05) clg_cap_f_of_temp.setCoefficient6xTIMESY(-0.000191808) clg_cap_f_of_temp.setMinimumValueofx(13.0) clg_cap_f_of_temp.setMaximumValueofx(24.0) clg_cap_f_of_temp.setMinimumValueofy(24.0) clg_cap_f_of_temp.setMaximumValueofy(46.0) # clg_cap_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model) clg_cap_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model) clg_cap_f_of_flow.setCoefficient1Constant(1.0) clg_cap_f_of_flow.setCoefficient2x(0.0) clg_cap_f_of_flow.setCoefficient3xPOW2(0.0) clg_cap_f_of_flow.setMinimumValueofx(0.0) clg_cap_f_of_flow.setMaximumValueofx(1.0) # clg_energy_input_ratio_f_of_temp = = model_add_curve(""DXCOOL-NECB2011-REF-COOLEIRFT") # clg_energy_input_ratio_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model) clg_energy_input_ratio_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model) clg_energy_input_ratio_f_of_temp.setCoefficient1Constant(0.116936) clg_energy_input_ratio_f_of_temp.setCoefficient2x(0.0284933) clg_energy_input_ratio_f_of_temp.setCoefficient3xPOW2(-0.000411156) clg_energy_input_ratio_f_of_temp.setCoefficient4y(0.0214108) clg_energy_input_ratio_f_of_temp.setCoefficient5yPOW2(0.000161028) clg_energy_input_ratio_f_of_temp.setCoefficient6xTIMESY(-0.000679104) clg_energy_input_ratio_f_of_temp.setMinimumValueofx(13.0) clg_energy_input_ratio_f_of_temp.setMaximumValueofx(24.0) clg_energy_input_ratio_f_of_temp.setMinimumValueofy(24.0) clg_energy_input_ratio_f_of_temp.setMaximumValueofy(46.0) # clg_energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model) # clg_energy_input_ratio_f_of_flow = = model_add_curve("DXCOOL-NECB2011-REF-CAPFFLOW") clg_energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model) clg_energy_input_ratio_f_of_flow.setCoefficient1Constant(1.0) clg_energy_input_ratio_f_of_flow.setCoefficient2x(0.0) clg_energy_input_ratio_f_of_flow.setCoefficient3xPOW2(0.0) clg_energy_input_ratio_f_of_flow.setMinimumValueofx(0.0) clg_energy_input_ratio_f_of_flow.setMaximumValueofx(1.0) # NECB curve modified to take into account how PLF is used in E+, and PLF ranges (> 0.7) # clg_part_load_ratio = model_add_curve("DXCOOL-NECB2011-REF-COOLPLFFPLR") clg_part_load_ratio = OpenStudio::Model::CurveCubic.new(model) clg_part_load_ratio.setCoefficient1Constant(0.0277) clg_part_load_ratio.setCoefficient2x(4.9151) clg_part_load_ratio.setCoefficient3xPOW2(-8.184) clg_part_load_ratio.setCoefficient4xPOW3(4.2702) clg_part_load_ratio.setMinimumValueofx(0.7) clg_part_load_ratio.setMaximumValueofx(1.0) return OpenStudio::Model::CoilCoolingDXSingleSpeed.new(model, always_on, clg_cap_f_of_temp, clg_cap_f_of_flow, clg_energy_input_ratio_f_of_temp, clg_energy_input_ratio_f_of_flow, clg_part_load_ratio) end |
#add_onespeed_htg_DX_coil(model, sch) ⇒ Object
1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1998 def add_onespeed_htg_DX_coil(model, sch) htg_cap_f_of_temp = OpenStudio::Model::CurveCubic.new(model) htg_cap_f_of_temp.setCoefficient1Constant(0.729009) htg_cap_f_of_temp.setCoefficient2x(0.0319275) htg_cap_f_of_temp.setCoefficient3xPOW2(0.000136404) htg_cap_f_of_temp.setCoefficient4xPOW3(-8.748e-06) htg_cap_f_of_temp.setMinimumValueofx(-20.0) htg_cap_f_of_temp.setMaximumValueofx(20.0) htg_cap_f_of_flow = OpenStudio::Model::CurveCubic.new(model) htg_cap_f_of_flow.setCoefficient1Constant(0.84) htg_cap_f_of_flow.setCoefficient2x(0.16) htg_cap_f_of_flow.setCoefficient3xPOW2(0.0) htg_cap_f_of_flow.setCoefficient4xPOW3(0.0) htg_cap_f_of_flow.setMinimumValueofx(0.5) htg_cap_f_of_flow.setMaximumValueofx(1.5) htg_energy_input_ratio_f_of_temp = OpenStudio::Model::CurveCubic.new(model) htg_energy_input_ratio_f_of_temp.setCoefficient1Constant(1.2183) htg_energy_input_ratio_f_of_temp.setCoefficient2x(-0.03612) htg_energy_input_ratio_f_of_temp.setCoefficient3xPOW2(0.00142) htg_energy_input_ratio_f_of_temp.setCoefficient4xPOW3(-2.68e-05) htg_energy_input_ratio_f_of_temp.setMinimumValueofx(-20.0) htg_energy_input_ratio_f_of_temp.setMaximumValueofx(20.0) htg_energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model) htg_energy_input_ratio_f_of_flow.setCoefficient1Constant(1.3824) htg_energy_input_ratio_f_of_flow.setCoefficient2x(-0.4336) htg_energy_input_ratio_f_of_flow.setCoefficient3xPOW2(0.0512) htg_energy_input_ratio_f_of_flow.setMinimumValueofx(0.0) htg_energy_input_ratio_f_of_flow.setMaximumValueofx(1.0) htg_part_load_ratio = OpenStudio::Model::CurveCubic.new(model) htg_part_load_ratio.setCoefficient1Constant(0.3696) htg_part_load_ratio.setCoefficient2x(2.3362) htg_part_load_ratio.setCoefficient3xPOW2(-2.9577) htg_part_load_ratio.setCoefficient4xPOW3(1.2596) htg_part_load_ratio.setMinimumValueofx(0.7) htg_part_load_ratio.setMaximumValueofx(1.0) dx_htg_coil = OpenStudio::Model::CoilHeatingDXSingleSpeed.new(model, sch, htg_cap_f_of_temp, htg_cap_f_of_flow, htg_energy_input_ratio_f_of_temp, htg_energy_input_ratio_f_of_flow, htg_part_load_ratio) dx_htg_coil.setMinimumOutdoorDryBulbTemperatureforCompressorOperation(-10) return dx_htg_coil end |
#add_ptac_dx_cooling(model, zone, zero_outdoor_air) ⇒ Object
2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 2073 def add_ptac_dx_cooling(model, zone, zero_outdoor_air) # Create a PTAC for each zone: # PTAC DX Cooling with electric heating coil; electric heating coil is always off # TO DO: need to apply this system to space types: # (1) data processing area: control room, data centre # when cooling capacity <= 20kW and # (2) residential/accommodation: murb, hotel/motel guest room # when building/space heated only (this as per NECB; apply to # all for initial work? CAN-QUEST limitation) # TO DO: PTAC characteristics: sizing, fan schedules, temperature setpoints, interaction with MAU always_on = model.alwaysOnDiscreteSchedule always_off = BTAP::Resources::Schedules::StandardSchedules::ON_OFF.always_off(model) htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_off) # Set up PTAC DX coil with NECB performance curve characteristics; clg_coil = add_onespeed_DX_coil(model, always_on) # Set up PTAC constant volume supply fan fan = OpenStudio::Model::FanOnOff.new(model) fan.setPressureRise(640) # This method will seem like an error in number of args..but this is due to swig voodoo. ptac = OpenStudio::Model::ZoneHVACPackagedTerminalAirConditioner.new(model, always_on, fan, htg_coil, clg_coil) ptac.setName("#{zone.name} PTAC") ptac.(always_off) if zero_outdoor_air ptac.setOutdoorAirFlowRateWhenNoCoolingorHeatingisNeeded 1.0e-5 ptac.setOutdoorAirFlowRateDuringCoolingOperation(1.0e-5) ptac.setOutdoorAirFlowRateDuringHeatingOperation(1.0e-5) end ptac.addToThermalZone(zone) end |
#add_sys1_unitary_ac_baseboard_heating(model:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', zones:, mau_type:, mau_heating_coil_type:, baseboard_type:, hw_loop:, multispeed: false) ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_single_speed.rb', line 2 def add_sys1_unitary_ac_baseboard_heating(model:, necb_reference_hp:false, necb_reference_hp_supp_fuel:'DefaultFuel', zones:, mau_type:, mau_heating_coil_type:, baseboard_type:, hw_loop:, multispeed: false) if multispeed add_sys1_unitary_ac_baseboard_heating_multi_speed(model: model, zones: zones, mau_type: mau_type, mau_heating_coil_type: mau_heating_coil_type, baseboard_type: baseboard_type, hw_loop: hw_loop) else add_sys1_unitary_ac_baseboard_heating_single_speed(model: model, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel, zones: zones, mau_type: mau_type, mau_heating_coil_type: mau_heating_coil_type, baseboard_type: baseboard_type, hw_loop: hw_loop) end end |
#add_sys1_unitary_ac_baseboard_heating_multi_speed(model:, zones:, mau_type:, mau_heating_coil_type:, baseboard_type:, hw_loop:) ⇒ Object
At this point the only way to implement multi-stage cooling and heating in OS is through the use of object “AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed”. This component uses as an argument a control zone and then it responds to a call for heating or cooling for that control zone. This aspect of this component makes it incompatible with how a system_1 make up air unit works where a constant supply air temperature is delivered to the spaces. It is therefore not recommended to use this method and to use the single speed implementation of systems_1.
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_multi_speed.rb', line 8 def add_sys1_unitary_ac_baseboard_heating_multi_speed(model:, zones:, mau_type:, mau_heating_coil_type:, baseboard_type:, hw_loop:) # Keep all data and assumptions for both systems on the top here for easy reference. system_data = {} system_data[:name] = 'Sys_1_Make-up air unit' system_data[:PreheatDesignTemperature] = 7.0 system_data[:PreheatDesignHumidityRatio] = 0.008 system_data[:PrecoolDesignTemperature] = 13.0 system_data[:PrecoolDesignHumidityRatio] = 0.008 system_data[:SizingOption] = 'NonCoincident' system_data[:CoolingDesignAirFlowMethod] = 'DesignDay' system_data[:CoolingDesignAirFlowRate] = 0.0 system_data[:HeatingDesignAirFlowMethod] = 'DesignDay' system_data[:HeatingDesignAirFlowRate] = 0.0 system_data[:SystemOutdoorAirMethod] = 'ZoneSum' system_data[:CentralCoolingDesignSupplyAirHumidityRatio] = 0.0085 system_data[:CentralHeatingDesignSupplyAirHumidityRatio] = 0.0080 system_data[:CentralCoolingDesignSupplyAirTemperature] = 13.0 system_data[:CentralHeatingDesignSupplyAirTemperature] = 43.0 system_data[:AllOutdoorAirinCooling] = true system_data[:AllOutdoorAirinHeating] = true system_data[:TypeofLoadtoSizeOn] = 'VentilationRequirement' system_data[:MinimumSystemAirFlowRatio] = 1.0 system_data[:MinimumOutdoorDryBulbTemperatureforCompressorOperation] = -10.0 # Zone data system_data[:system_supply_air_temperature] = 20.0 system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference] = 11.0 system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference] = 21.0 system_data[:ZoneCoolingSizingFactor] = 1.1 system_data[:ZoneHeatingSizingFactor] = 1.3 # System Type 1: PTAC with no heating (unitary AC) # Zone baseboards, electric or hot water depending on argument baseboard_type # baseboard_type choices are "Hot Water" or "Electric" # PSZ to represent make-up air unit (if present) # This measure creates: # a PTAC unit for each zone in the building; DX cooling coil # and heating coil that is always off # Baseboards ("Hot Water or "Electric") in zones connected to hot water loop # MAU is present if argument mau == true, not present if argument mau == false # MAU is PSZ; DX cooling # MAU heating coil: hot water coil or electric, depending on argument mau_heating_coil_type # mau_heating_coil_type choices are "Hot Water", "Electric" # boiler_fueltype choices match OS choices for Boiler component fuel type, i.e. # "NaturalGas","Electricity","PropaneGas","FuelOil#1","FuelOil#2","Coal","Diesel","Gasoline","OtherFuel1" # Some system parameters are set after system is set up; by applying method 'apply_hvac_efficiency_standard' always_on = model.alwaysOnDiscreteSchedule # define always off schedule for ptac heating coil always_off = BTAP::Resources::Schedules::StandardSchedules::ON_OFF.always_off(model) # Create MAU # TO DO: MAU sizing, characteristics (fan operation schedules, temperature setpoints, outdoor air, etc) if mau_type == true mau_air_loop = common_air_loop(model: model, system_data: system_data) mau_fan = OpenStudio::Model::FanConstantVolume.new(model, always_on) # Setup heating and cooling coils mau_clg_coil = OpenStudio::Model::CoilCoolingDXMultiSpeed.new(model) mau_clg_coil.setFuelType('Electricity') mau_clg_stage_1 = OpenStudio::Model::CoilCoolingDXMultiSpeedStageData.new(model) mau_clg_stage_2 = OpenStudio::Model::CoilCoolingDXMultiSpeedStageData.new(model) mau_clg_coil.addStage(mau_clg_stage_1) mau_clg_coil.addStage(mau_clg_stage_2) mau_clg_coil.setApplyPartLoadFractiontoSpeedsGreaterthan1(false) mau_htg_coil = OpenStudio::Model::CoilHeatingGasMultiStage.new(model) mau_htg_stage_1 = OpenStudio::Model::CoilHeatingGasMultiStageStageData.new(model) mau_htg_coil.addStage(mau_htg_stage_1) mau_htg_stage_1.setNominalCapacity(0.001) if mau_heating_coil_type == 'Electric' mau_supplemental_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) elsif mau_heating_coil_type == 'Hot Water' mau_supplemental_htg_coil = OpenStudio::Model::CoilHeatingWater.new(model, always_on) hw_loop.addDemandBranchForComponent(mau_supplemental_htg_coil) else raise("#{mau_heating_coil_type} is not a valid heating coil type.)") end # @todo other fuel-fired heating coil types? (not available in OpenStudio/E+ - may need to play with efficiency to mimic other fuel types) # This method will seem like an error in number of args..but this is due to swig voodoo. air_to_air_heatpump = OpenStudio::Model::AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.new(model, mau_fan, mau_htg_coil, mau_clg_coil, mau_supplemental_htg_coil) air_to_air_heatpump.setName("#{zones[0].name} ASHP") air_to_air_heatpump.setMinimumOutdoorDryBulbTemperatureforCompressorOperation(system_data[:MinimumOutdoorDryBulbTemperatureforCompressorOperation]) air_to_air_heatpump.setControllingZoneorThermostatLocation(zones[0]) air_to_air_heatpump.(always_on) air_to_air_heatpump.setNumberofSpeedsforHeating(1) air_to_air_heatpump.setNumberofSpeedsforCooling(2) # oa_controller oa_controller = OpenStudio::Model::ControllerOutdoorAir.new(model) oa_controller.autosizeMinimumOutdoorAirFlowRate # Set mechanical ventilation controller outdoor air to ZoneSum (used to be defaulted to ZoneSum but now should be # set explicitly) oa_controller.controllerMechanicalVentilation.setSystemOutdoorAirMethod('ZoneSum') # oa_system oa_system = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, oa_controller) # Add the components to the air loop # in order from closest to zone to furthest from zone supply_inlet_node = mau_air_loop.supplyInletNode air_to_air_heatpump.addToNode(supply_inlet_node) oa_system.addToNode(supply_inlet_node) # Add a setpoint manager to control the supply air temperature sat_sch = OpenStudio::Model::ScheduleRuleset.new(model) sat_sch.setName('Makeup-Air Unit Supply Air Temp') sat_sch.defaultDaySchedule.setName('Makeup Air Unit Supply Air Temp Default') sat_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), system_data[:system_supply_air_temperature]) setpoint_mgr = OpenStudio::Model::SetpointManagerScheduled.new(model, sat_sch) setpoint_mgr.addToNode(mau_air_loop.supplyOutletNode) # Create MAU end zones.each do |zone| # Zone sizing temperature difference sizing_zone = zone.sizingZone sizing_zone.setZoneCoolingDesignSupplyAirTemperatureInputMethod('TemperatureDifference') sizing_zone.setZoneCoolingDesignSupplyAirTemperatureDifference(11.0) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureInputMethod('TemperatureDifference') sizing_zone.setZoneHeatingDesignSupplyAirTemperatureDifference(21.0) sizing_zone.setZoneCoolingSizingFactor(1.1) sizing_zone.setZoneHeatingSizingFactor(1.3) # Set up PTAC heating coil; apply always off schedule # htg_coil_elec = OpenStudio::Model::CoilHeatingElectric.new(model,always_on) zero_outdoor_air = true # flag to set outside air flow to zero add_ptac_dx_cooling(model, zone, zero_outdoor_air) # add zone baseboards add_zone_baseboards(baseboard_type: baseboard_type, hw_loop: hw_loop, model: model, zone: zone) # # Create a diffuser and attach the zone/diffuser pair to the MAU air loop, if applicable if mau_type == true diffuser = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(model, always_on) mau_air_loop.addBranchForZone(zone, diffuser.to_StraightComponent) # components for MAU end # of zone loop end return true end |
#add_sys1_unitary_ac_baseboard_heating_single_speed(model:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', zones:, mau_type:, mau_heating_coil_type:, baseboard_type:, hw_loop:) ⇒ Object
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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_single_speed.rb', line 30 def add_sys1_unitary_ac_baseboard_heating_single_speed(model:, necb_reference_hp:false, necb_reference_hp_supp_fuel:'DefaultFuel', zones:, mau_type:, mau_heating_coil_type:, baseboard_type:, hw_loop:) # Keep all data and assumptions for both systems on the top here for easy reference. system_data = {} system_data[:name] = 'Sys_1_Make-up air unit' system_data[:PreheatDesignTemperature] = 7.0 system_data[:PreheatDesignHumidityRatio] = 0.008 system_data[:PrecoolDesignTemperature] = 13.0 system_data[:PrecoolDesignHumidityRatio] = 0.008 system_data[:SizingOption] = 'NonCoincident' system_data[:CoolingDesignAirFlowMethod] = 'DesignDay' system_data[:CoolingDesignAirFlowRate] = 0.0 system_data[:HeatingDesignAirFlowMethod] = 'DesignDay' system_data[:HeatingDesignAirFlowRate] = 0.0 system_data[:SystemOutdoorAirMethod] = 'ZoneSum' system_data[:CentralCoolingDesignSupplyAirHumidityRatio] = 0.0085 system_data[:CentralHeatingDesignSupplyAirHumidityRatio] = 0.0080 system_data[:CentralCoolingDesignSupplyAirTemperature] = 13.0 system_data[:CentralHeatingDesignSupplyAirTemperature] = 43.0 system_data[:AllOutdoorAirinCooling] = true system_data[:AllOutdoorAirinHeating] = true if necb_reference_hp system_data[:TypeofLoadtoSizeOn] = 'Total' else system_data[:TypeofLoadtoSizeOn] = 'VentilationRequirement' end system_data[:MinimumSystemAirFlowRatio] = 1.0 # Zone Sizing data system_data[:system_supply_air_temperature] = 20.0 system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference] = 11.0 system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference] = 21.0 system_data[:ZoneDXCoolingSizingFactor] = 1.0 system_data[:ZoneDXHeatingSizingFactor] = 1.3 system_data[:ZoneCoolingSizingFactor] = 1.1 system_data[:ZoneHeatingSizingFactor] = 1.3 # System Type 1: PTAC with no heating (unitary AC) # Zone baseboards, electric or hot water depending on argument baseboard_type # baseboard_type choices are "Hot Water" or "Electric" # PSZ to represent make-up air unit (if present) # This measure creates: # a PTAC unit for each zone in the building; DX cooling coil # and heating coil that is always off # Baseboards ("Hot Water or "Electric") in zones connected to hot water loop # MAU is present if argument mau == true, not present if argument mau == false # MAU is PSZ; DX cooling # MAU heating coil: hot water coil or electric, depending on argument mau_heating_coil_type # mau_heating_coil_type choices are "Hot Water", "Electric" # boiler_fueltype choices match OS choices for Boiler component fuel type, i.e. # "NaturalGas","Electricity","PropaneGas","FuelOilNo1","FuelOilNo2","Coal","Diesel","Gasoline","OtherFuel1" # If reference_hp = true, NECB 8.4.4.13 Heat Pump System Type 1: CAV Packaged rooftop heat pump with # zone baseboard (electric or hot water depending on argument baseboard_type) # Some system parameters are set after system is set up; by applying method 'apply_hvac_efficiency_standard' always_on = model.alwaysOnDiscreteSchedule always_off = BTAP::Resources::Schedules::StandardSchedules::ON_OFF.always_off(model) # Create MAU # TO DO: MAU sizing, characteristics (fan operation schedules, temperature setpoints, outdoor air, etc) if mau_type == true mau_air_loop = common_air_loop(model: model, system_data: system_data) #if reference_hp # AirLoopHVACUnitaryHeatPumpAirToAir needs FanOnOff in order for the fan to turn off during off hours # mau_fan = OpenStudio::Model::FanOnOff.new(model, always_on) #else mau_fan = OpenStudio::Model::FanConstantVolume.new(model, always_on) #end # MAU Heating type selection. raise("Flag 'necb_reference_hp' is true while 'mau_heating_coil_type' is not set to type DX") if (necb_reference_hp && (mau_heating_coil_type != 'DX')) if mau_heating_coil_type == 'Electric' # electric coil mau_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) elsif mau_heating_coil_type == 'Hot Water' mau_htg_coil = OpenStudio::Model::CoilHeatingWater.new(model, always_on) hw_loop.addDemandBranchForComponent(mau_htg_coil) elsif mau_heating_coil_type == 'DX' mau_htg_coil = add_onespeed_htg_DX_coil(model, always_on) mau_htg_coil.setName('CoilHeatingDXSingleSpeed_ashp') end # Set up Single Speed DX coil with mau_clg_coil = add_onespeed_DX_coil(model, always_on) mau_clg_coil.setName('CoilCoolingDXSingleSpeed_dx') mau_clg_coil.setName('CoilCoolingDXSingleSpeed_ashp') if necb_reference_hp # Set up OA system oa_controller = OpenStudio::Model::ControllerOutdoorAir.new(model) oa_controller.autosizeMinimumOutdoorAirFlowRate # Set mechanical ventilation controller outdoor air to ZoneSum (used to be defaulted to ZoneSum but now should be # set explicitly) oa_controller.controllerMechanicalVentilation.setSystemOutdoorAirMethod('ZoneSum') oa_system = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, oa_controller) # Add the components to the air loop # in order from closest to zone to furthest from zone supply_inlet_node = mau_air_loop.supplyInletNode # Reference HP requires slight changes to default MAU heating #if reference_hp # Create supplemental heating coil based on default regional fuel type # epw = OpenStudio::EpwFile.new(model.weatherFile.get.path.get) #primary_heating_fuel = @standards_data['regional_fuel_use'].detect { |fuel_sources| fuel_sources['state_province_regions'].include?(epw.stateProvinceRegion) }['fueltype_set'] #if primary_heating_fuel == 'NaturalGas' # supplemental_htg_coil = OpenStudio::Model::CoilHeatingGas.new(model, always_on) #elsif primary_heating_fuel == 'Electricity' or primary_heating_fuel == 'FuelOilNo2' # supplemental_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) #else #hot water coils is an option in the future # raise('Invalid fuel type selected for heat pump supplemental coil') #end #air_to_air_heatpump = OpenStudio::Model::AirLoopHVACUnitaryHeatPumpAirToAir.new(model, always_on, mau_fan, mau_htg_coil, mau_clg_coil, supplemental_htg_coil) #air_to_air_heatpump.setName("#{control_zone.name} ASHP") #air_to_air_heatpump.setControllingZone(control_zone) #air_to_air_heatpump.setSupplyAirFanOperatingModeSchedule(always_on) #air_to_air_heatpump.addToNode(supply_inlet_node) #else mau_fan.addToNode(supply_inlet_node) mau_htg_coil.addToNode(supply_inlet_node) mau_clg_coil.addToNode(supply_inlet_node) #end oa_system.addToNode(supply_inlet_node) # Add a setpoint manager to control the supply air temperature if necb_reference_hp setpoint_mgr = OpenStudio::Model::SetpointManagerWarmest.new(model) setpoint_mgr.setMinimumSetpointTemperature(13) setpoint_mgr.setMaximumSetpointTemperature(20) setpoint_mgr.addToNode(mau_air_loop.supplyOutletNode) else sat_sch = OpenStudio::Model::ScheduleRuleset.new(model) sat_sch.setName('Makeup-Air Unit Supply Air Temp') sat_sch.defaultDaySchedule.setName('Makeup Air Unit Supply Air Temp Default') sat_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), system_data[:system_supply_air_temperature]) setpoint_mgr = OpenStudio::Model::SetpointManagerScheduled.new(model, sat_sch) setpoint_mgr.addToNode(mau_air_loop.supplyOutletNode) end end zones.each do |zone| # Zone sizing temperature difference sizing_zone = zone.sizingZone sizing_zone.setZoneCoolingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneCoolingDesignSupplyAirTemperatureDifference(system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureDifference(system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference]) # Different sizing factors for reference HP capacity if necb_reference_hp sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneDXCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneDXHeatingSizingFactor]) else sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneHeatingSizingFactor]) end # Create a PTAC for each zone: # PTAC DX Cooling with electric heating coil; electric heating coil is always off # TO DO: need to apply this system to space types: # (1) data processing area: control room, data centre # when cooling capacity <= 20kW and # (2) residential/accommodation: murb, hotel/motel guest room # when building/space heated only (this as per NECB; apply to # all for initial work? CAN-QUEST limitation) # TO DO: PTAC characteristics: sizing, fan schedules, temperature setpoints, interaction with MAU # htg_coil_elec = OpenStudio::Model::CoilHeatingElectric.new(model,always_on) zero_outdoor_air = true # flag to set outside air flow to 0.0 # Reference HP system does not use PTAC unless necb_reference_hp add_ptac_dx_cooling(model, zone, zero_outdoor_air) end # add zone baseboards add_zone_baseboards(baseboard_type: baseboard_type, hw_loop: hw_loop, model: model, zone: zone) # # Create a diffuser and attach the zone/diffuser pair to the MAU air loop, if applicable if necb_reference_hp # Create CAV RH (RH based on region's default fuel type or user input) if necb_reference_hp_supp_fuel == 'DefaultFuel' epw = OpenStudio::EpwFile.new(model.weatherFile.get.path.get) necb_reference_hp_supp_fuel = @standards_data['regional_fuel_use'].detect { |fuel_sources| fuel_sources['state_province_regions'].include?(epw.stateProvinceRegion) }['fueltype_set'] end if necb_reference_hp_supp_fuel == 'NaturalGas' rh_coil = OpenStudio::Model::CoilHeatingGas.new(model, always_on) elsif necb_reference_hp_supp_fuel == 'Electricity' or necb_reference_hp_supp_fuel == 'FuelOilNo2' rh_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) else #hot water coils is an option in the future raise('Invalid fuel type selected for heat pump supplemental coil') end cav_rh_terminal = OpenStudio::Model::AirTerminalSingleDuctConstantVolumeReheat.new(model, always_on, rh_coil) mau_air_loop.addBranchForZone(zone, cav_rh_terminal.to_StraightComponent) elsif mau_type == true diffuser = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(model, always_on) mau_air_loop.addBranchForZone(zone, diffuser.to_StraightComponent) # components for MAU end # of zone loop end if mau_type sys_name_pars = {} sys_name_pars['sys_hr'] = 'none' sys_name_pars['sys_clg'] = 'dx' sys_name_pars['sys_clg'] = 'ashp' if necb_reference_hp sys_name_pars['sys_htg'] = mau_heating_coil_type sys_name_pars['sys_htg'] = 'ashp' if necb_reference_hp sys_name_pars['sys_sf'] = 'cv' sys_name_pars['zone_htg'] = baseboard_type sys_oa = 'doas' if necb_reference_hp sys_name_pars['zone_clg'] = 'none' sys_oa = 'mixed' else sys_name_pars['zone_clg'] = 'ptac' sys_oa = 'doas' end sys_name_pars['sys_rf'] = 'none' assign_base_sys_name(mau_air_loop, sys_abbr: 'sys_1', sys_oa: sys_oa, sys_name_pars: sys_name_pars) end return true end |
#add_sys2_FPFC_sys5_TPFC(model:, zones:, chiller_type:, fan_coil_type:, mau_cooling_type:, hw_loop:) ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_2_and_5.rb', line 2 def add_sys2_FPFC_sys5_TPFC(model:, zones:, chiller_type:, fan_coil_type:, mau_cooling_type:, hw_loop:) # System 2 AHU data system_data = {} system_data[:name] = 'Sys_2_Make-up air unit' system_data[:PreheatDesignTemperature] = 7.0 system_data[:PreheatDesignHumidityRatio] = 0.008 system_data[:PrecoolDesignTemperature] = 13.0 system_data[:PrecoolDesignHumidityRatio] = 0.008 system_data[:SizingOption] = 'NonCoincident' system_data[:CoolingDesignAirFlowMethod] = 'DesignDay' system_data[:CoolingDesignAirFlowRate] = 0.0 system_data[:HeatingDesignAirFlowMethod] = 'DesignDay' system_data[:HeatingDesignAirFlowRate] = 0.0 system_data[:SystemOutdoorAirMethod] = 'ZoneSum' system_data[:CentralCoolingDesignSupplyAirHumidityRatio] = 0.0085 system_data[:CentralHeatingDesignSupplyAirHumidityRatio] = 0.0080 system_data[:CentralCoolingDesignSupplyAirTemperature] = 13.0 system_data[:CentralHeatingDesignSupplyAirTemperature] = 13.1 system_data[:AllOutdoorAirinCooling] = false system_data[:AllOutdoorAirinHeating] = false system_data[:TypeofLoadtoSizeOn] = 'Sensible' system_data[:SetpointManagerSingleZoneReheatSupplyTempMax] = 13.0 system_data[:SetpointManagerSingleZoneReheatSupplyTempMin] = 13.1 system_data[:MinimumSystemAirFlowRatio] = 1.0 # System 2 Zone data system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference] = 11.0 system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference] = 21.0 system_data[:ZoneCoolingSizingFactor] = 1.1 system_data[:ZoneHeatingSizingFactor] = 1.3 # System Type 2: FPFC or System 5: TPFC # This measure creates: # -a four pipe or a two pipe fan coil unit for each zone in the building; # -a make up air-unit to provide ventilation to each zone; # -a heating loop, cooling loop and condenser loop to serve four pipe fan coil units # Arguments: # boiler_fueltype: "NaturalGas","Electricity","PropaneGas","FuelOilNo1","FuelOilNO2","Coal","Diesel","Gasoline","OtherFuel1" # chiller_type: "Scroll";"Centrifugal";"Rotary Screw";"Reciprocating" # mua_cooling_type: make-up air unit cooling type "DX";"Hydronic" # fan_coil_type options are "TPFC" or "FPFC" # @todo Add arguments as needed when the sizing routine is finalized. For example we will need to know the # required size of the boilers to decide on how many units are needed based on NECB rules. always_on = model.alwaysOnDiscreteSchedule # schedule for two-pipe fan coil operation. 3 seasons for heating/cooling. tpfc_clg_availability_sch, tpfc_htg_availability_sch = create_heating_cooling_on_off_availability_schedule(model) # Create a chilled water loop chw_loop = OpenStudio::Model::PlantLoop.new(model) chiller1, chiller2 = setup_chw_loop_with_components(model, chw_loop, chiller_type) # Create a condenser Loop cw_loop = OpenStudio::Model::PlantLoop.new(model) ctower = setup_cw_loop_with_components(model, cw_loop, chiller1, chiller2) # Set up make-up air unit for ventilation # TO DO: Need to investigate characteristics of make-up air unit for NECB reference # and define them here air_loop = mau_air_loop = common_air_loop(model: model, system_data: system_data) air_loop.setName(system_data[:name]) fan = OpenStudio::Model::FanConstantVolume.new(model, always_on) # Assume direct-fired gas heating coil for now; need to add logic # to set up hydronic or electric coil depending on proposed? htg_coil = OpenStudio::Model::CoilHeatingGas.new(model, always_on) # Add DX or hydronic cooling coil if mau_cooling_type == 'DX' clg_coil = add_onespeed_DX_coil(model, tpfc_clg_availability_sch) clg_coil.setName('CoilCoolingDXSingleSpeed_dx') elsif mau_cooling_type == 'Hydronic' clg_coil = OpenStudio::Model::CoilCoolingWater.new(model, tpfc_clg_availability_sch) chw_loop.addDemandBranchForComponent(clg_coil) end # does MAU have an economizer? oa_controller = OpenStudio::Model::ControllerOutdoorAir.new(model) oa_controller.autosizeMinimumOutdoorAirFlowRate # Set mechanical ventilation controller outdoor air to ZoneSum (used to be defaulted to ZoneSum but now should be # set explicitly) oa_controller.controllerMechanicalVentilation.setSystemOutdoorAirMethod('ZoneSum') # oa_system = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model,oa_controller) oa_system = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, oa_controller) # Add the components to the air loop # in order from closest to zone to furthest from zone supply_inlet_node = air_loop.supplyInletNode fan.addToNode(supply_inlet_node) htg_coil.addToNode(supply_inlet_node) clg_coil.addToNode(supply_inlet_node) oa_system.addToNode(supply_inlet_node) # Add a setpoint manager single zone reheat to control the # supply air temperature based on the needs of default zone (OpenStudio picks one) # TO DO: need to have method to pick appropriate control zone? setpoint_mgr_single_zone_reheat = OpenStudio::Model::SetpointManagerSingleZoneReheat.new(model) setpoint_mgr_single_zone_reheat.setMinimumSupplyAirTemperature(system_data[:SetpointManagerSingleZoneReheatSupplyTempMin]) setpoint_mgr_single_zone_reheat.setMaximumSupplyAirTemperature(system_data[:SetpointManagerSingleZoneReheatSupplyTempMax]) setpoint_mgr_single_zone_reheat.addToNode(air_loop.supplyOutletNode) # Set up zonal FC (ZoneHVAC,cooling coil, heating coil, fan) in each zone zones.each do |zone| # Zone sizing temperature difference sizing_zone = zone.sizingZone sizing_zone.setZoneCoolingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneCoolingDesignSupplyAirTemperatureDifference(system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureDifference(system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneHeatingSizingFactor]) # fc supply fan fc_fan = OpenStudio::Model::FanConstantVolume.new(model, always_on) if fan_coil_type == 'FPFC' # heating coil fc_htg_coil = OpenStudio::Model::CoilHeatingWater.new(model, always_on) # cooling coil fc_clg_coil = OpenStudio::Model::CoilCoolingWater.new(model, always_on) elsif fan_coil_type == 'TPFC' # heating coil fc_htg_coil = OpenStudio::Model::CoilHeatingWater.new(model, tpfc_htg_availability_sch) # cooling coil fc_clg_coil = OpenStudio::Model::CoilCoolingWater.new(model, tpfc_clg_availability_sch) end # connect heating coil to hot water loop and cooling coil to chw loop. hw_loop.addDemandBranchForComponent(fc_htg_coil) chw_loop.addDemandBranchForComponent(fc_clg_coil) # add connections to FPFC. # This method will seem like an error in number of args..but this is due to swig voodoo. zone_fc = OpenStudio::Model::ZoneHVACFourPipeFanCoil.new(model, always_on, fc_fan, fc_clg_coil, fc_htg_coil) zone_fc.addToThermalZone(zone) # Create a diffuser and attach the zone/diffuser pair to the air loop (make-up air unit) diffuser = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(model, always_on) air_loop.addBranchForZone(zone, diffuser.to_StraightComponent) # zone loop end sys_abbr = 'sys_2' sys_abbr = 'sys_5' if fan_coil_type == 'TPFC' sys_name_pars = {} sys_name_pars['sys_hr'] = 'none' sys_name_pars['sys_clg'] = mau_cooling_type sys_name_pars['sys_htg'] = 'g' sys_name_pars['sys_sf'] = 'cv' sys_name_pars['zone_htg'] = fan_coil_type sys_name_pars['zone_clg'] = fan_coil_type sys_name_pars['sys_rf'] = 'none' assign_base_sys_name(mau_air_loop, sys_abbr: sys_abbr, sys_oa: 'doas', sys_name_pars: sys_name_pars) end |
#add_sys3_and_8_zone_equip(air_loop, baseboard_type, hw_loop, model, zone) ⇒ Object
250 251 252 253 254 255 256 257 258 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_single_speed.rb', line 250 def add_sys3_and_8_zone_equip(air_loop, baseboard_type, hw_loop, model, zone) always_on = model.alwaysOnDiscreteSchedule add_zone_baseboards(baseboard_type: baseboard_type, hw_loop: hw_loop, model: model, zone: zone) diffuser = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(model, always_on) air_loop.addBranchForZone(zone, diffuser.to_StraightComponent) end |
#add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating(model:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', zones:, heating_coil_type:, baseboard_type:, hw_loop:, new_auto_zoner: true, multispeed: false) ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_single_speed.rb', line 2 def add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating(model:, necb_reference_hp:false, necb_reference_hp_supp_fuel:'DefaultFuel', zones:, heating_coil_type:, baseboard_type:, hw_loop:, new_auto_zoner: true, multispeed: false) if multispeed add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating_multi_speed(model: model, zones: zones, heating_coil_type: heating_coil_type, baseboard_type: baseboard_type, hw_loop: hw_loop, new_auto_zoner: new_auto_zoner) else add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating_single_speed(model: model, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel, zones: zones, heating_coil_type: heating_coil_type, baseboard_type: baseboard_type, hw_loop: hw_loop, new_auto_zoner: new_auto_zoner) end end |
#add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating_multi_speed(model:, zones:, heating_coil_type:, baseboard_type:, hw_loop:, new_auto_zoner: true) ⇒ Object
Some tests still require a simple way to set up a system without sizing.. so we are keeping the auto_zoner flag for this method.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_multi_speed.rb', line 3 def add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating_multi_speed(model:, zones:, heating_coil_type:, baseboard_type:, hw_loop:, new_auto_zoner: true) system_data = {} system_data[:name] = 'Sys_3_PSZ' system_data[:CentralCoolingDesignSupplyAirTemperature] = 13.0 system_data[:CentralHeatingDesignSupplyAirTemperature] = 43.0 system_data[:AllOutdoorAirinCooling] = false system_data[:AllOutdoorAirinHeating] = false system_data[:TypeofLoadtoSizeOn] = 'Sensible' system_data[:MinimumSystemAirFlowRatio] = 1.0 system_data[:PreheatDesignTemperature] = 7.0 system_data[:PreheatDesignHumidityRatio] = 0.008 system_data[:PrecoolDesignTemperature] = 13.0 system_data[:PrecoolDesignHumidityRatio] = 0.008 system_data[:SizingOption] = 'NonCoincident' system_data[:CoolingDesignAirFlowMethod] = 'DesignDay' system_data[:CoolingDesignAirFlowRate] = 0.0 system_data[:HeatingDesignAirFlowMethod] = 'DesignDay' system_data[:HeatingDesignAirFlowRate] = 0.0 system_data[:SystemOutdoorAirMethod] = 'ZoneSum' system_data[:CentralCoolingDesignSupplyAirHumidityRatio] = 0.0085 system_data[:CentralHeatingDesignSupplyAirHumidityRatio] = 0.0080 # System 3 Zone data system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference] = 11.0 system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference] = 21.0 system_data[:SetpointManagerSingleZoneReheatSupplyTempMin] = 13.0 system_data[:SetpointManagerSingleZoneReheatSupplyTempMax] = 43.0 system_data[:ZoneDXCoolingSizingFactor] = 1.0 system_data[:ZoneDXHeatingSizingFactor] = 1.3 system_data[:ZoneCoolingSizingFactor] = 1.1 system_data[:ZoneHeatingSizingFactor] = 1.3 system_data[:MinimumOutdoorDryBulbTemperatureforCompressorOperation] = -10.0 if new_auto_zoner == true # Create system airloop # Add Air Loop air_loop = add_system_3_and_8_airloop_multi_speed(heating_coil_type, model, system_data, determine_control_zone(zones)) # Add Zone equipment zones.each do |zone| # Zone sizing temperature difference sizing_zone = zone.sizingZone sizing_zone.setZoneCoolingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneCoolingDesignSupplyAirTemperatureDifference(system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureDifference(system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneHeatingSizingFactor]) add_sys3_and_8_zone_equip(air_loop, baseboard_type, hw_loop, model, zone) end return true else zones.each do |zone| air_loop = add_system_3_and_8_airloop_multi_speed(heating_coil_type, model, system_data, zone) add_sys3_and_8_zone_equip(air_loop, baseboard_type, hw_loop, model, zone) end return true end end |
#add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating_single_speed(model:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', zones:, heating_coil_type:, baseboard_type:, hw_loop:, new_auto_zoner: true) ⇒ Object
Some tests still require a simple way to set up a system without sizing.. so we are keeping the auto_zoner flag for this method.
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 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_single_speed.rb', line 33 def add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating_single_speed(model:, necb_reference_hp:false, necb_reference_hp_supp_fuel:'DefaultFuel', zones:, heating_coil_type:, baseboard_type:, hw_loop:, new_auto_zoner: true) system_data = {} system_data[:name] = 'Sys_3_PSZ' system_data[:CentralCoolingDesignSupplyAirTemperature] = 13.0 system_data[:CentralHeatingDesignSupplyAirTemperature] = 43.0 system_data[:AllOutdoorAirinCooling] = false system_data[:AllOutdoorAirinHeating] = false system_data[:TypeofLoadtoSizeOn] = 'Sensible' system_data[:MinimumSystemAirFlowRatio] = 1.0 system_data[:PreheatDesignTemperature] = 7.0 system_data[:PreheatDesignHumidityRatio] = 0.008 system_data[:PrecoolDesignTemperature] = 13.0 system_data[:PrecoolDesignHumidityRatio] = 0.008 system_data[:SizingOption] = 'NonCoincident' system_data[:CoolingDesignAirFlowMethod] = 'DesignDay' system_data[:CoolingDesignAirFlowRate] = 0.0 system_data[:HeatingDesignAirFlowMethod] = 'DesignDay' system_data[:HeatingDesignAirFlowRate] = 0.0 system_data[:SystemOutdoorAirMethod] = 'ZoneSum' system_data[:CentralCoolingDesignSupplyAirHumidityRatio] = 0.0085 system_data[:CentralHeatingDesignSupplyAirHumidityRatio] = 0.0080 # System 3 Zone data system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference] = 11.0 system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference] = 21.0 system_data[:SetpointManagerSingleZoneReheatSupplyTempMin] = 13.0 system_data[:SetpointManagerSingleZoneReheatSupplyTempMax] = 43.0 system_data[:ZoneDXCoolingSizingFactor] = 1.0 system_data[:ZoneDXHeatingSizingFactor] = 1.3 system_data[:ZoneCoolingSizingFactor] = 1.1 system_data[:ZoneHeatingSizingFactor] = 1.3 system_data[:MinimumOutdoorDryBulbTemperatureforCompressorOperation] = -10.0 if new_auto_zoner == true # Create system airloop # Add Air Loop air_loop = add_system_3_and_8_airloop(heating_coil_type, model, system_data, determine_control_zone(zones), necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel) # Add Zone equipment zones.each do |zone| # Zone sizing temperature difference sizing_zone = zone.sizingZone sizing_zone.setZoneCoolingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneCoolingDesignSupplyAirTemperatureDifference(system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureDifference(system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference]) if necb_reference_hp sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneDXCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneDXHeatingSizingFactor]) else sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneHeatingSizingFactor]) end add_sys3_and_8_zone_equip(air_loop, baseboard_type, hw_loop, model, zone) end else zones.each do |zone| air_loop = add_system_3_and_8_airloop(heating_coil_type, model, system_data, zone, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel) add_sys3_and_8_zone_equip(air_loop, baseboard_type, hw_loop, model, zone) end end sys_name_pars = {} sys_name_pars['sys_hr'] = 'none' sys_name_pars['sys_clg'] = 'dx' sys_name_pars['sys_clg'] = 'ashp' if necb_reference_hp sys_name_pars['sys_htg'] = heating_coil_type sys_name_pars['sys_htg'] = 'ashp' if necb_reference_hp sys_name_pars['sys_sf'] = 'cv' sys_name_pars['zone_htg'] = baseboard_type sys_name_pars['zone_clg'] = 'none' sys_name_pars['sys_rf'] = 'none' assign_base_sys_name(air_loop, sys_abbr: 'sys_3', sys_oa: 'mixed', sys_name_pars: sys_name_pars) return true end |
#add_sys4_single_zone_make_up_air_unit_with_baseboard_heating(model:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', zones:, heating_coil_type:, baseboard_type:, hw_loop:) ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_4.rb', line 2 def add_sys4_single_zone_make_up_air_unit_with_baseboard_heating(model:, necb_reference_hp:false, necb_reference_hp_supp_fuel:'DefaultFuel', zones:, heating_coil_type:, baseboard_type:, hw_loop:) system_data = {} system_data[:name] = 'Sys_4_PSZ' system_data[:CentralCoolingDesignSupplyAirTemperature] = 13.0 system_data[:CentralHeatingDesignSupplyAirTemperature] = 43.0 system_data[:AllOutdoorAirinCooling] = false system_data[:AllOutdoorAirinHeating] = false system_data[:TypeofLoadtoSizeOn] = 'Sensible' system_data[:MinimumSystemAirFlowRatio] = 1.0 system_data[:PreheatDesignTemperature] = 7.0 system_data[:PreheatDesignHumidityRatio] = 0.008 system_data[:PrecoolDesignTemperature] = 13.0 system_data[:PrecoolDesignHumidityRatio] = 0.008 system_data[:SizingOption] = 'NonCoincident' system_data[:CoolingDesignAirFlowMethod] = 'DesignDay' system_data[:CoolingDesignAirFlowRate] = 0.0 system_data[:HeatingDesignAirFlowMethod] = 'DesignDay' system_data[:HeatingDesignAirFlowRate] = 0.0 system_data[:SystemOutdoorAirMethod] = 'ZoneSum' system_data[:CentralCoolingDesignSupplyAirHumidityRatio] = 0.0085 system_data[:CentralHeatingDesignSupplyAirHumidityRatio] = 0.0080 # zone system_data[:SetpointManagerSingleZoneReheatSupplyTempMax] = 43.0 system_data[:SetpointManagerSingleZoneReheatSupplyTempMin] = 13.0 system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference] = 11.0 system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference] = 21.0 system_data[:ZoneHeatingDesignSupplyAirTemperature] = 43.0 system_data[:ZoneDXCoolingSizingFactor] = 1.0 system_data[:ZoneDXHeatingSizingFactor] = 1.3 system_data[:ZoneCoolingSizingFactor] = 1.1 system_data[:ZoneHeatingSizingFactor] = 1.3 # System Type 4: PSZ-AC # This measure creates: # -a constant volume packaged single-zone A/C unit # for each zone in the building; DX cooling with # heating coil: fuel-fired or electric, depending on argument heating_coil_type # heating_coil_type choices are "Electric", "Gas" # zone baseboards: hot water or electric, depending on argument baseboard_type # baseboard_type choices are "Hot Water" or "Electric" # boiler_fueltype choices match OS choices for Boiler component fuel type, i.e. # "NaturalGas","Electricity","PropaneGas","FuelOilNo1","FuelOil#2","Coal","Diesel","Gasoline","OtherFuel1" # NOTE: This is the same as system type 3 (single zone make-up air unit and single zone rooftop unit are both PSZ systems) # SHOULD WE COMBINE sys3 and sys4 into one script? # # control_zone = determine_control_zone(zones) # Todo change this when control zone method is working. control_zone = zones.first always_on = model.alwaysOnDiscreteSchedule # Create a PSZ for each zone # TO DO: need to apply this system to space types: # (1) automotive area: repair/parking garage, fire engine room, indoor truck bay # (2) supermarket/food service: food preparation with kitchen hood/vented appliance # (3) warehouse area (non-refrigerated spaces) air_loop = common_air_loop(model: model, system_data: system_data) air_loop.setName("#{system_data[:name]}_#{control_zone.name}") # Zone sizing temperature difference sizing_zone = control_zone.sizingZone sizing_zone.setZoneCoolingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneCoolingDesignSupplyAirTemperatureDifference(system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneCoolingDesignSupplyAirTemperatureDifference(system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference]) if necb_reference_hp sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneDXCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneDXHeatingSizingFactor]) else sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneHeatingSizingFactor]) end if necb_reference_hp # AirLoopHVACUnitaryHeatPumpAirToAir needs FanOnOff in order for the fan to turn off during off hours fan = OpenStudio::Model::FanOnOff.new(model, always_on) else fan = OpenStudio::Model::FanConstantVolume.new(model, always_on) end # Set up DX coil with NECB performance curve characteristics; clg_coil = add_onespeed_DX_coil(model, always_on) clg_coil.setName('CoilCoolingDXSingleSpeed_dx') clg_coil.setName('CoilCoolingDXSingleSpeed_ashp') if necb_reference_hp raise("Flag 'necb_reference_hp' is set to true while parameter 'heating_coil_type' is not set to DX") if (necb_reference_hp && (heating_coil_type != 'DX')) if heating_coil_type == 'Electric' # electric coil htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) elsif heating_coil_type == 'Gas' htg_coil = OpenStudio::Model::CoilHeatingGas.new(model, always_on) elsif heating_coil_type == 'DX' htg_coil = add_onespeed_htg_DX_coil(model, always_on) htg_coil.setName('CoilHeatingDXSingleSpeed_ashp') end # TO DO: other fuel-fired heating coil types? (not available in OpenStudio/E+ - may need to play with efficiency to mimic other fuel types) # oa_controller oa_controller = OpenStudio::Model::ControllerOutdoorAir.new(model) oa_controller.autosizeMinimumOutdoorAirFlowRate # Set mechanical ventilation controller outdoor air to ZoneSum (used to be defaulted to ZoneSum but now should be # set explicitly) oa_controller.controllerMechanicalVentilation.setSystemOutdoorAirMethod('ZoneSum') # oa_system oa_system = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, oa_controller) # Add the components to the air loop # in order from closest to zone to furthest from zone supply_inlet_node = air_loop.supplyInletNode if necb_reference_hp #create supplemental heating coil based on default regional fuel type if necb_reference_hp_supp_fuel == 'DefaultFuel' epw = OpenStudio::EpwFile.new(model.weatherFile.get.path.get) necb_reference_hp_supp_fuel = @standards_data['regional_fuel_use'].detect { |fuel_sources| fuel_sources['state_province_regions'].include?(epw.stateProvinceRegion) }['fueltype_set'] end if necb_reference_hp_supp_fuel == 'NaturalGas' supplemental_htg_coil = OpenStudio::Model::CoilHeatingGas.new(model, always_on) elsif necb_reference_hp_supp_fuel == 'Electricity' or necb_reference_hp_supp_fuel == 'FuelOilNo2' supplemental_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) else #hot water coils is an option in the future raise('Invalid fuel type selected for heat pump supplemental coil') end # This method will seem like an error in number of args..but this is due to swig voodoo. air_to_air_heatpump = OpenStudio::Model::AirLoopHVACUnitaryHeatPumpAirToAir.new(model, always_on, fan, htg_coil, clg_coil, supplemental_htg_coil) air_to_air_heatpump.setName("#{control_zone.name} ASHP") air_to_air_heatpump.setControllingZone(control_zone) air_to_air_heatpump.(always_on) air_to_air_heatpump.addToNode(supply_inlet_node) else fan.addToNode(supply_inlet_node) htg_coil.addToNode(supply_inlet_node) clg_coil.addToNode(supply_inlet_node) end oa_system.addToNode(supply_inlet_node) # Add a setpoint manager single zone reheat to control the # supply air temperature based on the needs of this zone setpoint_mgr_single_zone_reheat = OpenStudio::Model::SetpointManagerSingleZoneReheat.new(model) setpoint_mgr_single_zone_reheat.setControlZone(control_zone) setpoint_mgr_single_zone_reheat.setMinimumSupplyAirTemperature(system_data[:SetpointManagerSingleZoneReheatSupplyTempMin]) setpoint_mgr_single_zone_reheat.setMaximumSupplyAirTemperature(system_data[:SetpointManagerSingleZoneReheatSupplyTempMax]) setpoint_mgr_single_zone_reheat.addToNode(air_loop.supplyOutletNode) # Create sensible heat exchanger # heat_exchanger = BTAP::Resources::HVAC::Plant::add_hrv(model) # heat_exchanger.setSensibleEffectivenessat100HeatingAirFlow(0.5) # heat_exchanger.setSensibleEffectivenessat75HeatingAirFlow(0.5) # heat_exchanger.setSensibleEffectivenessat100CoolingAirFlow(0.5) # heat_exchanger.setSensibleEffectivenessat75CoolingAirFlow(0.5) # heat_exchanger.setLatentEffectivenessat100HeatingAirFlow(0.0) # heat_exchanger.setLatentEffectivenessat75HeatingAirFlow(0.0) # heat_exchanger.setLatentEffectivenessat100CoolingAirFlow(0.0) # heat_exchanger.setLatentEffectivenessat75CoolingAirFlow(0.0) # heat_exchanger.setSupplyAirOutletTemperatureControl(false) # # Connect heat exchanger # oa_node = oa_system.outboardOANode # heat_exchanger.addToNode(oa_node.get) zones.each do |zone| sizing_zone = zone.sizingZone sizing_zone.setZoneCoolingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneCoolingDesignSupplyAirTemperatureDifference(system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureDifference(system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference]) if necb_reference_hp sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneDXCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneDXHeatingSizingFactor]) else sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneHeatingSizingFactor]) end # Create a diffuser and attach the zone/diffuser pair to the air loop # diffuser = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(model,always_on) add_zone_baseboards(baseboard_type: baseboard_type, hw_loop: hw_loop, model: model, zone: zone) diffuser = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(model, always_on) air_loop.addBranchForZone(zone, diffuser.to_StraightComponent) # zone loop end sys_name_pars = {} sys_name_pars['sys_hr'] = 'none' sys_name_pars['sys_clg'] = 'dx' sys_name_pars['sys_clg'] = 'ashp' if necb_reference_hp sys_name_pars['sys_htg'] = heating_coil_type sys_name_pars['sys_htg'] = 'ashp' if necb_reference_hp sys_name_pars['sys_sf'] = 'cv' sys_name_pars['zone_htg'] = baseboard_type sys_name_pars['zone_clg'] = 'none' sys_name_pars['sys_rf'] = 'none' assign_base_sys_name(air_loop, sys_abbr: 'sys_4', sys_oa: 'mixed', sys_name_pars: sys_name_pars) return true end |
#add_sys6_multi_zone_built_up_system_with_baseboard_heating(model:, zones:, heating_coil_type:, baseboard_type:, chiller_type:, fan_type:, hw_loop:) ⇒ Object
end add_sys4_single_zone_make_up_air_unit_with_baseboard_heating
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_6.rb', line 4 def add_sys6_multi_zone_built_up_system_with_baseboard_heating(model:, zones:, heating_coil_type:, baseboard_type:, chiller_type:, fan_type:, hw_loop:) # System Type 6: VAV w/ Reheat # This measure creates: # a single hot water loop with a natural gas or electric boiler or for the building # a single chilled water loop with water cooled chiller for the building # a single condenser water loop for heat rejection from the chiller # a VAV system w/ hot water or electric heating, chilled water cooling, and # hot water or electric reheat for each story of the building # Arguments: # "boiler_fueltype" choices match OS choices for boiler fuel type: # "NaturalGas","Electricity","PropaneGas","FuelOilNo1","FuelOil#2","Coal","Diesel","Gasoline","OtherFuel1" # "heating_coil_type": "Electric" or "Hot Water" # "baseboard_type": "Electric" and "Hot Water" # "chiller_type": "Scroll";"Centrifugal";""Screw";"Reciprocating" # "fan_type": "AF_or_BI_rdg_fancurve";"AF_or_BI_inletvanes";"fc_inletvanes";"var_speed_drive" # system_data = {} system_data[:name] = 'Sys_6_VAV with Reheat' system_data[:CentralCoolingDesignSupplyAirTemperature] = 13.0 system_data[:CentralHeatingDesignSupplyAirTemperature] = 13.1 system_data[:AllOutdoorAirinCooling] = false system_data[:AllOutdoorAirinHeating] = false system_data[:MinimumSystemAirFlowRatio] = 0.3 # zone data system_data[:system_supply_air_temperature] = 13.0 system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference] = 11.0 system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference] = 21.0 system_data[:ZoneCoolingSizingFactor] = 1.1 system_data[:ZoneHeatingSizingFactor] = 1.3 system_data[:ZoneVAVMinFlowFactorPerFloorArea] = 0.002 system_data[:ZoneVAVMaxReheatTemp] = 43.0 system_data[:ZoneVAVDamperAction] = 'Normal' always_on = model.alwaysOnDiscreteSchedule # Chilled Water Plant chw_loop = OpenStudio::Model::PlantLoop.new(model) chiller1, chiller2 = setup_chw_loop_with_components(model, chw_loop, chiller_type) # Condenser System cw_loop = OpenStudio::Model::PlantLoop.new(model) ctower = setup_cw_loop_with_components(model, cw_loop, chiller1, chiller2) # Make a Packaged VAV w/ PFP Boxes for each story of the building model.getBuildingStorys.sort.each do |story| unless (OpenstudioStandards::Geometry.building_story_get_thermal_zones(story) & zones).empty? air_loop = common_air_loop(model: model, system_data: system_data) air_loop.setName('Sys_6_VAV with Reheat') supply_fan = OpenStudio::Model::FanVariableVolume.new(model, always_on) supply_fan.setName('Sys6 Supply Fan') return_fan = OpenStudio::Model::FanVariableVolume.new(model, always_on) return_fan.setName('Sys6 Return Fan') if heating_coil_type == 'Hot Water' htg_coil = OpenStudio::Model::CoilHeatingWater.new(model, always_on) hw_loop.addDemandBranchForComponent(htg_coil) end if heating_coil_type == 'Electric' htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) end clg_coil = OpenStudio::Model::CoilCoolingWater.new(model, always_on) chw_loop.addDemandBranchForComponent(clg_coil) oa_controller = OpenStudio::Model::ControllerOutdoorAir.new(model) oa_controller.autosizeMinimumOutdoorAirFlowRate # Set mechanical ventilation controller outdoor air to ZoneSum (used to be defaulted to ZoneSum but now should be # set explicitly) oa_controller.controllerMechanicalVentilation.setSystemOutdoorAirMethod('ZoneSum') oa_system = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, oa_controller) # Add the components to the air loop # in order from closest to zone to furthest from zone supply_inlet_node = air_loop.supplyInletNode supply_outlet_node = air_loop.supplyOutletNode supply_fan.addToNode(supply_inlet_node) htg_coil.addToNode(supply_inlet_node) clg_coil.addToNode(supply_inlet_node) oa_system.addToNode(supply_inlet_node) returnAirNode = oa_system.returnAirModelObject.get.to_Node.get return_fan.addToNode(returnAirNode) # Add a setpoint manager to control the # supply air to a constant temperature sat_sch = OpenStudio::Model::ScheduleRuleset.new(model) sat_sch.setName('Supply Air Temp') sat_sch.defaultDaySchedule.setName('Supply Air Temp Default') sat_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), system_data[:system_supply_air_temperature]) sat_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, sat_sch) sat_stpt_manager.addToNode(supply_outlet_node) # Make a VAV terminal with HW reheat for each zone on this story that is in intersection with the zones array. # and hook the reheat coil to the HW loop (OpenstudioStandards::Geometry.building_story_get_thermal_zones(story) & zones).each do |zone| # Zone sizing parameters sizing_zone = zone.sizingZone sizing_zone.setZoneCoolingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneCoolingDesignSupplyAirTemperatureDifference(system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureDifference(system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneHeatingSizingFactor]) if heating_coil_type == 'Hot Water' reheat_coil = OpenStudio::Model::CoilHeatingWater.new(model, always_on) hw_loop.addDemandBranchForComponent(reheat_coil) elsif heating_coil_type == 'Electric' reheat_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) end # Set zone baseboards add_zone_baseboards(model: model, zone: zone, baseboard_type: baseboard_type, hw_loop: hw_loop) vav_terminal = OpenStudio::Model::AirTerminalSingleDuctVAVReheat.new(model, always_on, reheat_coil) air_loop.addBranchForZone(zone, vav_terminal.to_StraightComponent) # NECB2011 minimum zone airflow setting vav_terminal.setFixedMinimumAirFlowRate(system_data[:ZoneVAVMinFlowFactorPerFloorArea] * zone.floorArea) vav_terminal.setMaximumReheatAirTemperature(system_data[:ZoneVAVMaxReheatTemp]) vav_terminal.setDamperHeatingAction(system_data[:ZoneVAVDamperAction]) end sys_name_pars = {} sys_name_pars['sys_hr'] = 'none' sys_name_pars['sys_htg'] = heating_coil_type sys_name_pars['sys_clg'] = 'Chilled Water' sys_name_pars['sys_sf'] = 'vv' sys_name_pars['zone_htg'] = baseboard_type sys_name_pars['zone_clg'] = 'none' sys_name_pars['sys_rf'] = 'vv' assign_base_sys_name(air_loop, sys_abbr: 'sys_6', sys_oa: 'mixed', sys_name_pars: sys_name_pars) end # next story end # for debugging # puts "end add_sys6_multi_zone_built_up_with_baseboard_heating" return true end |
#add_sys6_multi_zone_reference_hp_with_baseboard_heating(model:, zones:, heating_coil_type:, baseboard_type:, hw_loop:, necb_reference_hp_supp_fuel: 'DefaultFuel') ⇒ Object
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_6.rb', line 316 def add_sys6_multi_zone_reference_hp_with_baseboard_heating(model:, zones:, heating_coil_type:, baseboard_type:, hw_loop:, necb_reference_hp_supp_fuel:'DefaultFuel') #system data system_data = {} system_data[:name] = 'Sys_6_VAV with Reheat' system_data[:CentralCoolingDesignSupplyAirTemperature] = 13.0 system_data[:CentralHeatingDesignSupplyAirTemperature] = 13.1 system_data[:AllOutdoorAirinCooling] = false system_data[:AllOutdoorAirinHeating] = false # zone data system_data[:system_supply_air_temperature] = 13.0 system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference] = 11.0 system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference] = 21.0 system_data[:ZoneDXCoolingSizingFactor] = 1.0 system_data[:ZoneDXHeatingSizingFactor] = 1.3 always_on = model.alwaysOnDiscreteSchedule model.getBuildingStorys.sort.each do |story| unless (OpenstudioStandards::Geometry.building_story_get_thermal_zones(story) & zones).empty? air_loop = common_air_loop(model: model, system_data: system_data) air_loop.setName('Sys_6_CAV') supply_fan = OpenStudio::Model::FanConstantVolume.new(model, always_on) supply_fan.setName('Sys6 Supply Fan') return_fan = OpenStudio::Model::FanConstantVolume.new(model, always_on) return_fan.setName('Sys6 Return Fan') htg_coil = add_onespeed_htg_DX_coil(model, always_on) htg_coil.setName('CoilHeatingDXSingleSpeed_ashp') clg_coil = add_onespeed_DX_coil(model, always_on) clg_coil.setName('CoilCoolingDXSingleSpeed_ashp') oa_controller = OpenStudio::Model::ControllerOutdoorAir.new(model) oa_controller.autosizeMinimumOutdoorAirFlowRate # Set mechanical ventilation controller outdoor air to ZoneSum (used to be defaulted to ZoneSum but now should be # set explicitly) oa_controller.controllerMechanicalVentilation.setSystemOutdoorAirMethod('ZoneSum') oa_system = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, oa_controller) # Add the components to the air loop # in order from closest to zone to furthest from zone supply_inlet_node = air_loop.supplyInletNode supply_outlet_node = air_loop.supplyOutletNode supply_fan.addToNode(supply_inlet_node) htg_coil.addToNode(supply_inlet_node) clg_coil.addToNode(supply_inlet_node) oa_system.addToNode(supply_inlet_node) returnAirNode = oa_system.returnAirModelObject.get.to_Node.get return_fan.addToNode(returnAirNode) # Add a setpoint manager to control the # supply air to a constant temperature sat_sch = OpenStudio::Model::ScheduleRuleset.new(model) sat_sch.setName('Supply Air Temp') #sat_sch.defaultDaySchedule.setName('Supply Air Temp Default') #sat_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), system_data[:system_supply_air_temperature]) #sat_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, sat_sch) sat_stpt_manager = OpenStudio::Model::SetpointManagerWarmest.new(model) sat_stpt_manager.setControlVariable("Temperature") sat_stpt_manager.setMinimumSetpointTemperature(13) sat_stpt_manager.setMaximumSetpointTemperature(24) sat_stpt_manager.addToNode(supply_outlet_node) # Make CAV terminals for each zone on this story that is in intersection with the zones array. (OpenstudioStandards::Geometry.building_story_get_thermal_zones(story) & zones).each do |zone| # Zone sizing parameters sizing_zone = zone.sizingZone sizing_zone.setZoneCoolingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneCoolingDesignSupplyAirTemperatureDifference(system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureDifference(system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneDXCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneDXHeatingSizingFactor]) # Set zone baseboards add_zone_baseboards(model: model, zone: zone, baseboard_type: baseboard_type, hw_loop: hw_loop) # Create CAV RH (RH based on region's default fuel type) if necb_reference_hp_supp_fuel == 'DefaultFuel' epw = OpenStudio::EpwFile.new(model.weatherFile.get.path.get) necb_reference_hp_supp_fuel = @standards_data['regional_fuel_use'].detect { |fuel_sources| fuel_sources['state_province_regions'].include?(epw.stateProvinceRegion) }['fueltype_set'] end if necb_reference_hp_supp_fuel == 'NaturalGas' rh_coil = OpenStudio::Model::CoilHeatingGas.new(model, always_on) elsif necb_reference_hp_supp_fuel == 'Electricity' or necb_reference_hp_supp_fuel == 'FuelOilNo2' rh_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) else #hot water coils is an option in the future raise('Invalid fuel type selected for heat pump supplemental coil') end cav_rh_terminal = OpenStudio::Model::AirTerminalSingleDuctConstantVolumeReheat.new(model, always_on, rh_coil) air_loop.addBranchForZone(zone, cav_rh_terminal.to_StraightComponent) end sys_name_pars = {} sys_name_pars['sys_hr'] = 'none' sys_name_pars['sys_htg'] = 'ashp' sys_name_pars['sys_clg'] = 'ashp' sys_name_pars['sys_sf'] = 'cv' sys_name_pars['zone_htg'] = baseboard_type sys_name_pars['zone_clg'] = 'none' sys_name_pars['sys_rf'] = 'cv' assign_base_sys_name(air_loop, sys_abbr: 'sys_6', sys_oa: 'mixed', sys_name_pars: sys_name_pars) end end end |
#add_system_3_and_8_airloop(heating_coil_type, model, system_data, control_zone, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel') ⇒ Object
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_single_speed.rb', line 133 def add_system_3_and_8_airloop(heating_coil_type, model, system_data, control_zone, necb_reference_hp:false, necb_reference_hp_supp_fuel:'DefaultFuel') # System Type 3: PSZ-AC # This measure creates: # -a constant volume packaged single-zone A/C unit # for each zone in the building; DX cooling with # heating coil: fuel-fired or electric, depending on argument heating_coil_type # heating_coil_type choices are "Electric", "Gas", "DX" # zone baseboards: hot water or electric, depending on argument baseboard_type # baseboard_type choices are "Hot Water" or "Electric" # boiler_fueltype choices match OS choices for Boiler component fuel type, i.e. # "NaturalGas","Electricity","PropaneGas","FuelOilNo1","FuelOilNo2","Coal","Diesel","Gasoline","OtherFuel1" always_on = model.alwaysOnDiscreteSchedule air_loop = common_air_loop(model: model, system_data: system_data) air_loop.setName("#{system_data[:name]} #{control_zone.name}") # Zone sizing temperature difference sizing_zone = control_zone.sizingZone sizing_zone.setZoneCoolingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneCoolingDesignSupplyAirTemperatureDifference(system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureDifference(system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference]) if necb_reference_hp sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneDXCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneDXHeatingSizingFactor]) else sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneHeatingSizingFactor]) end if necb_reference_hp #AirLoopHVACUnitaryHeatPumpAirToAir needs FanOnOff in order for the fan to turn off during off hours fan = OpenStudio::Model::FanOnOff.new(model, always_on) else fan = OpenStudio::Model::FanConstantVolume.new(model, always_on) end # Set up DX coil if necb_reference_hp #NECB curve characteristics clg_coil = add_onespeed_DX_coil(model, always_on) clg_coil.setName('CoilCoolingDXSingleSpeed_ashp') else clg_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(model) #sets default OS curve (but will be replaced with NECB curves later) clg_coil.setName('CoilCoolingDXSingleSpeed_dx') end raise("Flag 'necb_reference_hp' is set to true while parameter 'heating_coil_type' is not set to DX") if (necb_reference_hp && (heating_coil_type != 'DX')) case heating_coil_type when 'Electric' # electric coil htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) when 'Gas' htg_coil = OpenStudio::Model::CoilHeatingGas.new(model, always_on) when 'DX' #create main DX heating coil htg_coil = add_onespeed_htg_DX_coil(model, always_on) htg_coil.setName('CoilHeatingDXSingleSpeed_ashp') sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneDXHeatingSizingFactor]) sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneDXCoolingSizingFactor]) else raise("#{heating_coil_type} is not a valid heating coil type.)") end # TO DO: other fuel-fired heating coil types? (not available in OpenStudio/E+ - may need to play with efficiency to mimic other fuel types) # oa_controller oa_controller = OpenStudio::Model::ControllerOutdoorAir.new(model) oa_controller.autosizeMinimumOutdoorAirFlowRate # Set mechanical ventilation controller outdoor air to ZoneSum (used to be defaulted to ZoneSum but now should be # set explicitly) oa_controller.controllerMechanicalVentilation.setSystemOutdoorAirMethod('ZoneSum') # oa_system oa_system = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, oa_controller) # Add the components to the air loop # in order from closest to zone to furthest from zone supply_inlet_node = air_loop.supplyInletNode if necb_reference_hp #create supplemental heating coil based on default regional fuel type if necb_reference_hp_supp_fuel == 'DefaultFuel' epw = OpenStudio::EpwFile.new(model.weatherFile.get.path.get) necb_reference_hp_supp_fuel = @standards_data['regional_fuel_use'].detect { |fuel_sources| fuel_sources['state_province_regions'].include?(epw.stateProvinceRegion) }['fueltype_set'] end if necb_reference_hp_supp_fuel == 'NaturalGas' supplemental_htg_coil = OpenStudio::Model::CoilHeatingGas.new(model, always_on) elsif necb_reference_hp_supp_fuel == 'Electricity' or necb_reference_hp_supp_fuel == 'FuelOilNo2' supplemental_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) else #hot water coils is an option in the future raise('Invalid fuel type selected for heat pump supplemental coil') end # This method will seem like an error in number of args..but this is due to swig voodoo. air_to_air_heatpump = OpenStudio::Model::AirLoopHVACUnitaryHeatPumpAirToAir.new(model, always_on, fan, htg_coil, clg_coil, supplemental_htg_coil) air_to_air_heatpump.setName("#{control_zone.name} ASHP") air_to_air_heatpump.setControllingZone(control_zone) air_to_air_heatpump.(always_on) air_to_air_heatpump.addToNode(supply_inlet_node) else fan.addToNode(supply_inlet_node) htg_coil.addToNode(supply_inlet_node) clg_coil.addToNode(supply_inlet_node) end oa_system.addToNode(supply_inlet_node) # Add a setpoint manager single zone reheat to control the # supply air temperature based on the needs of this zone setpoint_mgr_single_zone_reheat = OpenStudio::Model::SetpointManagerSingleZoneReheat.new(model) setpoint_mgr_single_zone_reheat.setControlZone(control_zone) setpoint_mgr_single_zone_reheat.setMinimumSupplyAirTemperature(system_data[:SetpointManagerSingleZoneReheatSupplyTempMin]) setpoint_mgr_single_zone_reheat.setMaximumSupplyAirTemperature(system_data[:SetpointManagerSingleZoneReheatSupplyTempMax]) setpoint_mgr_single_zone_reheat.addToNode(air_loop.supplyOutletNode) return air_loop end |
#add_system_3_and_8_airloop_multi_speed(heating_coil_type, model, system_data, control_zone) ⇒ Object
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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_3_and_8_multi_speed.rb', line 81 def add_system_3_and_8_airloop_multi_speed(heating_coil_type, model, system_data, control_zone) # System Type 3: PSZ-AC # This measure creates: # -a constant volume packaged single-zone A/C unit # for each zone in the building; DX cooling with # heating coil: fuel-fired or electric, depending on argument heating_coil_type # heating_coil_type choices are "Electric", "Gas", "DX" # zone baseboards: hot water or electric, depending on argument baseboard_type # baseboard_type choices are "Hot Water" or "Electric" # boiler_fueltype choices match OS choices for Boiler component fuel type, i.e. # "NaturalGas","Electricity","PropaneGas","FuelOilNo1","FuelOilNo2","Coal","Diesel","Gasoline","OtherFuel1" always_on = model.alwaysOnDiscreteSchedule air_loop = common_air_loop(model: model, system_data: system_data) air_loop.setName("#{system_data[:name]} #{control_zone.name}") # Zone sizing temperature difference sizing_zone = control_zone.sizingZone sizing_zone.setZoneCoolingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneCoolingDesignSupplyAirTemperatureDifference(system_data[:ZoneCoolingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureInputMethod(system_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod]) sizing_zone.setZoneHeatingDesignSupplyAirTemperatureDifference(system_data[:ZoneHeatingDesignSupplyAirTemperatureDifference]) sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneHeatingSizingFactor]) fan = OpenStudio::Model::FanConstantVolume.new(model, always_on) # Setup heating and cooling coils if (heating_coil_type == 'Gas') || (heating_coil_type == 'Electric') clg_coil = OpenStudio::Model::CoilCoolingDXMultiSpeed.new(model) clg_coil.setFuelType('Electricity') clg_stage_1 = OpenStudio::Model::CoilCoolingDXMultiSpeedStageData.new(model) clg_stage_2 = OpenStudio::Model::CoilCoolingDXMultiSpeedStageData.new(model) clg_coil.addStage(clg_stage_1) clg_coil.addStage(clg_stage_2) clg_coil.setApplyPartLoadFractiontoSpeedsGreaterthan1(false) htg_coil = OpenStudio::Model::CoilHeatingGasMultiStage.new(model) htg_stage_1 = OpenStudio::Model::CoilHeatingGasMultiStageStageData.new(model) htg_coil.addStage(htg_stage_1) if heating_coil_type == 'Gas' supplemental_htg_coil = OpenStudio::Model::CoilHeatingGas.new(model, always_on) supplemental_htg_coil.setNominalCapacity(0.001) elsif heating_coil_type == 'Electric' supplemental_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) htg_stage_1.setNominalCapacity(0.001) end # Single stage DX and Electric heating elsif heating_coil_type == 'DX' clg_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(model) clg_coil.setMinimumOutdoorDryBulbTemperatureforCompressorOperation(system_data[:MinimumOutdoorDryBulbTemperatureforCompressorOperation]) htg_coil = OpenStudio::Model::CoilHeatingDXSingleSpeed.new(model) supplemental_htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) else raise("#{heating_coil_type} is not a valid heating coil type.)") end # oa_controller oa_controller = OpenStudio::Model::ControllerOutdoorAir.new(model) oa_controller.autosizeMinimumOutdoorAirFlowRate # Set mechanical ventilation controller outdoor air to ZoneSum (used to be defaulted to ZoneSum but now should be # set explicitly) oa_controller.controllerMechanicalVentilation.setSystemOutdoorAirMethod('ZoneSum') # oa_system oa_system = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, oa_controller) # Add the components to the air loop # in order from closest to zone to furthest from zone supply_inlet_node = air_loop.supplyInletNode if (heating_coil_type == 'Gas') || (heating_coil_type == 'Electric') # This method will seem like an error in number of args..but this is due to swig voodoo. air_to_air_heatpump = OpenStudio::Model::AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.new(model, fan, htg_coil, clg_coil, supplemental_htg_coil) air_to_air_heatpump.setControllingZoneorThermostatLocation(control_zone) air_to_air_heatpump.setNumberofSpeedsforHeating(1) air_to_air_heatpump.setNumberofSpeedsforCooling(2) air_to_air_heatpump.setMinimumOutdoorDryBulbTemperatureforCompressorOperation(system_data[:MinimumOutdoorDryBulbTemperatureforCompressorOperation]) elsif heating_coil_type == 'DX' # This method will seem like an error in number of args..but this is due to swig voodoo. air_to_air_heatpump = OpenStudio::Model::AirLoopHVACUnitaryHeatPumpAirToAir.new(model, always_on, fan, htg_coil, clg_coil, supplemental_htg_coil) air_to_air_heatpump.setControllingZone(zone) end air_to_air_heatpump.setName("#{control_zone.name} ASHP") air_to_air_heatpump.(always_on) air_to_air_heatpump.addToNode(supply_inlet_node) oa_system.addToNode(supply_inlet_node) # Add a setpoint manager single zone reheat to control the # supply air temperature based on the needs of this zone setpoint_mgr_single_zone_reheat = OpenStudio::Model::SetpointManagerSingleZoneReheat.new(model) setpoint_mgr_single_zone_reheat.setControlZone(control_zone) setpoint_mgr_single_zone_reheat.setMinimumSupplyAirTemperature(system_data[:SetpointManagerSingleZoneReheatSupplyTempMin]) setpoint_mgr_single_zone_reheat.setMaximumSupplyAirTemperature(system_data[:SetpointManagerSingleZoneReheatSupplyTempMax]) setpoint_mgr_single_zone_reheat.addToNode(air_loop.supplyOutletNode) return air_loop end |
#add_zone_baseboards(baseboard_type:, hw_loop:, model:, zone:) ⇒ Object
Zonal systems
2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 2053 def add_zone_baseboards(baseboard_type:, hw_loop:, model:, zone:) always_on = model.alwaysOnDiscreteSchedule if baseboard_type == 'Electric' zone_elec_baseboard = OpenStudio::Model::ZoneHVACBaseboardConvectiveElectric.new(model) zone_elec_baseboard.addToThermalZone(zone) end return unless baseboard_type == 'Hot Water' baseboard_coil = OpenStudio::Model::CoilHeatingWaterBaseboard.new(model) # Connect baseboard coil to hot water loop hw_loop.addDemandBranchForComponent(baseboard_coil) zone_baseboard = OpenStudio::Model::ZoneHVACBaseboardConvectiveWater.new(model, always_on, baseboard_coil) # add zone_baseboard to zone zone_baseboard.addToThermalZone(zone) end |
#adjust_wildcard_spacetype_schedule(space:, schedule:, lights_type: 'NECB_Default', lights_scale: 1.0) ⇒ Object
Set wildcard spactype schedule to NECB letter index.
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 728 def adjust_wildcard_spacetype_schedule(space:, schedule:, lights_type: 'NECB_Default', lights_scale: 1.0) if space.spaceType.empty? OpenStudio.logFree(OpenStudio::Error, "Error: No spacetype assigned for #{space.name.get}. This must be assigned. Aborting.") end # Get current spacetype name space_type_name = space.spaceType.get.standardsSpaceType.get.to_s # Determine new spacetype name. regex = /^(.*sch-)(\S)$/ new_spacetype_name = "#{space_type_name.match(regex).captures.first}#{schedule}" new_spacetype = nil # if the new spacetype does not match the old space type. we gotta update the space with the new spacetype. if space_type_name != new_spacetype_name new_spacetype = space.model.getSpaceTypes.detect do |spacetype| !spacetype.standardsBuildingType.empty? && # need to do this to prevent an exception. (spacetype.standardsBuildingType.get == space.spaceType.get.standardsBuildingType.get) && !spacetype.standardsSpaceType.empty? && # need to do this to prevent an exception. (spacetype.standardsSpaceType.get == new_spacetype_name) end if new_spacetype.nil? # Space type is not in model. need to create from scratch. new_spacetype = OpenStudio::Model::SpaceType.new(space.model) new_spacetype.setStandardsBuildingType(space.spaceType.get.standardsBuildingType.get) new_spacetype.setStandardsSpaceType(new_spacetype_name) new_spacetype.setName("#{space.spaceType.get.standardsBuildingType.get} #{new_spacetype_name}") space_type_apply_internal_loads(space_type: new_spacetype, lights_type: lights_type, lights_scale: lights_scale) space_type_apply_internal_load_schedules(new_spacetype, true, true, true, true, true, true, true) end space.setSpaceType(new_spacetype) # sanity check. raise 'could not reassign space type schedule.' if schedule != space.spaceType.get.name.get.match(regex)[2] end return space end |
#air_loop_hvac_apply_economizer_integration(air_loop_hvac, climate_zone) ⇒ Boolean
this method assumes you previously checked that an economizer is required at all via #economizer_required?
NECB always requires an integrated economizer (NoLockout); as per 5.2.2.8(3) this means that compressor allowed to turn on when economizer is open
90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 90 def air_loop_hvac_apply_economizer_integration(air_loop_hvac, climate_zone) # Get the OA system and OA controller oa_sys = air_loop_hvac.airLoopHVACOutdoorAirSystem # No OA system return false if !oa_sys.is_initialized oa_sys = oa_sys.get oa_control = oa_sys.getControllerOutdoorAir # Apply integrated economizer oa_control.setLockoutType('NoLockout') return true end |
#air_loop_hvac_apply_energy_recovery_ventilator(air_loop_hvac, climate = nil) ⇒ Boolean
Add exception logic for systems serving parking garage, warehouse, or multifamily
Add an ERV to this airloop. Will be a rotary-type HX
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 290 def air_loop_hvac_apply_energy_recovery_ventilator(air_loop_hvac, climate = nil) # Get the oa system oa_system = nil if air_loop_hvac.airLoopHVACOutdoorAirSystem.is_initialized oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem.get else OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}, ERV cannot be added because the system has no OA intake.") return false end # Create an ERV erv = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(air_loop_hvac.model) erv.setName("#{air_loop_hvac.name} ERV") erv.setSensibleEffectivenessat100HeatingAirFlow(0.5) erv.setLatentEffectivenessat100HeatingAirFlow(0.5) erv.setSensibleEffectivenessat75HeatingAirFlow(0.5) erv.setLatentEffectivenessat75HeatingAirFlow(0.5) erv.setSensibleEffectivenessat100CoolingAirFlow(0.5) erv.setLatentEffectivenessat100CoolingAirFlow(0.5) erv.setSensibleEffectivenessat75CoolingAirFlow(0.5) erv.setLatentEffectivenessat75CoolingAirFlow(0.5) erv.setSupplyAirOutletTemperatureControl(true) erv.setHeatExchangerType('Rotary') erv.setFrostControlType('ExhaustOnly') erv.setEconomizerLockout(true) erv.setThresholdTemperature(-23.3) # -10F erv.setInitialDefrostTimeFraction(0.167) erv.setRateofDefrostTimeFractionIncrease(1.44) # Add the ERV to the OA system erv.addToNode(oa_system.outboardOANode.get) # Add a setpoint manager OA pretreat # to control the ERV spm_oa_pretreat = OpenStudio::Model::SetpointManagerOutdoorAirPretreat.new(air_loop_hvac.model) spm_oa_pretreat.setMinimumSetpointTemperature(-99.0) spm_oa_pretreat.setMaximumSetpointTemperature(99.0) spm_oa_pretreat.setMinimumSetpointHumidityRatio(0.00001) spm_oa_pretreat.setMaximumSetpointHumidityRatio(1.0) # Reference setpoint node and # Mixed air stream node are outlet # node of the OA system mixed_air_node = oa_system.mixedAirModelObject.get.to_Node.get spm_oa_pretreat.setReferenceSetpointNode(mixed_air_node) spm_oa_pretreat.setMixedAirStreamNode(mixed_air_node) # Outdoor air node is # the outboard OA node of teh OA system spm_oa_pretreat.setOutdoorAirStreamNode(oa_system.outboardOANode.get) # Return air node is the inlet # node of the OA system return_air_node = oa_system.returnAirModelObject.get.to_Node.get spm_oa_pretreat.setReturnAirStreamNode(return_air_node) # Attach to the outlet of the ERV erv_outlet = erv.primaryAirOutletModelObject.get.to_Node.get spm_oa_pretreat.addToNode(erv_outlet) # Apply the prototype Heat Exchanger power assumptions. heat_exchanger_air_to_air_sensible_and_latent_apply_prototype_nominal_electric_power(erv) # Determine if the system is a DOAS based on # whether there is 100% OA in heating and cooling sizing. is_doas = false sizing_system = air_loop_hvac.sizingSystem if sizing_system.allOutdoorAirinCooling && sizing_system.allOutdoorAirinHeating is_doas = true end # Set the bypass control type # If DOAS system, BypassWhenWithinEconomizerLimits # to disable ERV during economizing. # Otherwise, BypassWhenOAFlowGreaterThanMinimum # to disable ERV during economizing and when OA # is also greater than minimum. bypass_ctrl_type = if is_doas 'BypassWhenWithinEconomizerLimits' else 'BypassWhenOAFlowGreaterThanMinimum' end oa_system.getControllerOutdoorAir.setHeatRecoveryBypassControlType(bypass_ctrl_type) return true end |
#air_loop_hvac_apply_multizone_vav_outdoor_air_sizing(air_loop_hvac) ⇒ Object
NECB does not change damper positions
return [Boolean] returns true if successful, false if not
13 14 15 16 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 13 def air_loop_hvac_apply_multizone_vav_outdoor_air_sizing(air_loop_hvac) # Do not change anything. return true end |
#air_loop_hvac_apply_single_zone_controls(air_loop_hvac, climate_zone) ⇒ Boolean
NECB has no single zone air loop control requirements
475 476 477 478 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 475 def air_loop_hvac_apply_single_zone_controls(air_loop_hvac, climate_zone) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: No special economizer controls were modeled.") return true end |
#air_loop_hvac_apply_vav_damper_action(air_loop_hvac) ⇒ Boolean
see if this impacts the sizing run.
Set the VAV damper control to single maximum or dual maximum control depending on the standard.
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 427 def air_loop_hvac_apply_vav_damper_action(air_loop_hvac) damper_action = 'Single Maximum' # Interpret this as an EnergyPlus input damper_action_eplus = nil if damper_action == 'Single Maximum' damper_action_eplus = 'Normal' elsif damper_action == 'Dual Maximum' # EnergyPlus 8.7 changed the meaning of 'Reverse'. # For versions of OpenStudio using E+ 8.6 or lower damper_action_eplus = if air_loop_hvac.model.version < OpenStudio::VersionString.new('2.0.5') 'Reverse' # For versions of OpenStudio using E+ 8.7 or higher else 'ReverseWithLimits' end end # Set the control for any VAV reheat terminals # on this airloop. control_type_set = false air_loop_hvac.demandComponents.each do |equip| if equip.to_AirTerminalSingleDuctVAVReheat.is_initialized term = equip.to_AirTerminalSingleDuctVAVReheat.get # Dual maximum only applies to terminals with HW reheat coils if damper_action == 'Dual Maximum' if term.reheatCoil.to_CoilHeatingWater.is_initialized term.setDamperHeatingAction(damper_action_eplus) control_type_set = true end else term.setDamperHeatingAction(damper_action_eplus) control_type_set = true term.setMaximumFlowFractionDuringReheat(0.5) end end end if control_type_set OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: VAV damper action was set to #{damper_action} control.") end return true end |
#air_loop_hvac_demand_control_ventilation_required?(air_loop_hvac, climate_zone) ⇒ Boolean
Add exception logic for systems that serve multifamily, parking garage, warehouse
Determine if demand control ventilation (DCV) is required for this air loop.
416 417 418 419 420 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 416 def air_loop_hvac_demand_control_ventilation_required?(air_loop_hvac, climate_zone) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{template} #{climate_zone}: #{air_loop_hvac.name}: DCV is not required for any system.") dcv_required = false return dcv_required end |
#air_loop_hvac_economizer_required?(air_loop_hvac) ⇒ Boolean
Determine whether or not this system is required to have an economizer.
22 23 24 25 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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 22 def air_loop_hvac_economizer_required?(air_loop_hvac) economizer_required = false # need a better way to determine if an economizer is needed. return economizer_required if ((air_loop_hvac.name.to_s.include? 'Outpatient F1' ) || (air_loop_hvac.sizingSystem.typeofLoadtoSizeOn.to_s == "VentilationRequirement")) # A big number of btu per hr as the minimum requirement infinity_btu_per_hr = 999_999_999_999 minimum_capacity_btu_per_hr = infinity_btu_per_hr # Determine if the airloop serves any computer rooms # / data centers, which changes the economizer. is_dc = false if air_loop_hvac_data_center_area_served(air_loop_hvac) > 0 is_dc = true end # Determine the minimum capacity that requires an economizer minimum_capacity_btu_per_hr = 68_243 # NECB requires economizer for cooling cap > 20 kW # puts air_loop_hvac.name.to_s # Design Supply Air Flow Rate: This method below reads the value from the sql file. dsafr_m3_per_s = air_loop_hvac.autosizedDesignSupplyAirFlowRate min_dsafr_l_per_s = 1500 unless dsafr_m3_per_s.empty? dsafr_l_per_s = dsafr_m3_per_s.get * 1000 if dsafr_l_per_s > min_dsafr_l_per_s economizer_required = true puts "economizer_required = true for #{air_loop_hvac.name} because dsafr_l_per_s(#{dsafr_l_per_s}) > 1500" if is_dc OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{air_loop_hvac.name} requires an economizer because the 'Design Supply Air Flow Rate' of #{dsafr_l_per_s} L/s exceeds the minimum air flow rate of #{min_dsafr_l_per_s} L/s for data centers.") else OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{air_loop_hvac.name} requires an economizer because the 'Design Supply Air Flow Rate' of #{dsafr_l_per_s} L/s exceeds the minimum air flow rate of #{min_dsafr_l_per_s} L/s.") end end end # Check whether the system requires an economizer by comparing # the system capacity to the minimum capacity. total_cooling_capacity_w = air_loop_hvac_total_cooling_capacity(air_loop_hvac) total_cooling_capacity_btu_per_hr = OpenStudio.convert(total_cooling_capacity_w, 'W', 'Btu/hr').get if total_cooling_capacity_btu_per_hr >= minimum_capacity_btu_per_hr if is_dc OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{air_loop_hvac.name} requires an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr exceeds the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr for data centers.") else OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{air_loop_hvac.name} requires an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr exceeds the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr.") end puts "economizer_required = true for #{air_loop_hvac.name} because total_cooling_capacity_btu_per_hr(#{total_cooling_capacity_btu_per_hr}) >= #{minimum_capacity_btu_per_hr}" economizer_required = true else if is_dc OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{air_loop_hvac.name} does not require an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr is less than the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr for data centers.") else OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{air_loop_hvac.name} does not require an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr is less than the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr.") end end return economizer_required end |
#air_loop_hvac_enable_unoccupied_fan_shutoff(air_loop_hvac, min_occ_pct = 0.05) ⇒ Boolean
Shut off the system during unoccupied periods. During these times, systems will cycle on briefly if temperature drifts below setpoint. For systems with fan-powered terminals, the whole system (not just the terminal fans) will cycle on. Terminal-only night cycling is not used because the terminals cannot provide cooling, so terminal-only night cycling leads to excessive unmet cooling hours during unoccupied periods. If the system already has a schedule other than Always-On, no change will be made. If the system has an Always-On schedule assigned, a new schedule will be created. In this case, occupied is defined as the total percent occupancy for the loop for all zones served.
the system will be considered unoccupied.
1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1095 def air_loop_hvac_enable_unoccupied_fan_shutoff(air_loop_hvac, min_occ_pct = 0.05) # Set the system to night cycle air_loop_hvac.setNightCycleControlType('CycleOnAny') # Check if already using a schedule other than always on avail_sch = air_loop_hvac.availabilitySchedule unless avail_sch == air_loop_hvac.model.alwaysOnDiscreteSchedule OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: Availability schedule is already set to #{avail_sch.name}. Will assume this includes unoccupied shut down; no changes will be made.") return true end # Get the airloop occupancy schedule loop_occ_sch = air_loop_hvac_get_occupancy_schedule(air_loop_hvac, occupied_percentage_threshold: min_occ_pct) flh = OpenstudioStandards::Schedules.schedule_get_equivalent_full_load_hours(loop_occ_sch) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}: Annual occupied hours = #{flh.round} hr/yr, assuming a #{min_occ_pct} occupancy threshold. This schedule will be used as the HVAC operation schedule.") # Set HVAC availability schedule to follow occupancy air_loop_hvac.setAvailabilitySchedule(loop_occ_sch) air_loop_hvac.supplyComponents.each do |comp| if comp.to_AirLoopHVACUnitaryHeatPumpAirToAir.is_initialized comp.to_AirLoopHVACUnitaryHeatPumpAirToAir.get.(loop_occ_sch) elsif comp.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.is_initialized comp.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.get.setAvailabilitySchedule(loop_occ_sch) end end return true end |
#air_loop_hvac_energy_recovery_ventilator_required?(air_loop_hvac, climate_zone) ⇒ Boolean
Add exception logic for systems serving parking garage, warehouse, or multifamily
Check if ERV is required on this airloop.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 110 def air_loop_hvac_energy_recovery_ventilator_required?(air_loop_hvac, climate_zone) # ERV Not Applicable for AHUs that serve # parking garage, warehouse, or multifamily # if space_types_served_names.include?('PNNL_Asset_Rating_Apartment_Space_Type') || # space_types_served_names.include?('PNNL_Asset_Rating_LowRiseApartment_Space_Type') || # space_types_served_names.include?('PNNL_Asset_Rating_ParkingGarage_Space_Type') || # space_types_served_names.include?('PNNL_Asset_Rating_Warehouse_Space_Type') # OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not applicable because it because it serves parking garage, warehouse, or multifamily.") # return false # end erv_required = nil # ERV not applicable for medical AHUs (AHU1 in Outpatient), per AIA 2001 - 7.31.D2. if air_loop_hvac.name.to_s.include? 'Outpatient F1' erv_required = false return erv_required end # ERV not applicable for medical AHUs, per AIA 2001 - 7.31.D2. if air_loop_hvac.name.to_s.include? 'VAV_ER' erv_required = false return erv_required elsif air_loop_hvac.name.to_s.include? 'VAV_OR' erv_required = false return erv_required end # ERV Not Applicable for AHUs that have DCV # or that have no OA intake. controller_oa = nil controller_mv = nil oa_system = nil if air_loop_hvac.airLoopHVACOutdoorAirSystem.is_initialized oa_system = air_loop_hvac.airLoopHVACOutdoorAirSystem.get controller_oa = oa_system.getControllerOutdoorAir controller_mv = controller_oa.controllerMechanicalVentilation if controller_mv.demandControlledVentilation == true OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}, ERV not applicable because DCV enabled.") return false end else OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}, ERV not applicable because it has no OA intake.") return false end # Get the AHU design supply air flow rate dsn_flow_m3_per_s = nil if air_loop_hvac.designSupplyAirFlowRate.is_initialized dsn_flow_m3_per_s = air_loop_hvac.designSupplyAirFlowRate.get elsif air_loop_hvac.autosizedDesignSupplyAirFlowRate.is_initialized dsn_flow_m3_per_s = air_loop_hvac.autosizedDesignSupplyAirFlowRate.get else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name} design supply air flow rate is not available, cannot apply efficiency standard.") return false end dsn_flow_cfm = OpenStudio.convert(dsn_flow_m3_per_s, 'm^3/s', 'cfm').get # Get the minimum OA flow rate min_oa_flow_m3_per_s = nil if controller_oa.minimumOutdoorAirFlowRate.is_initialized min_oa_flow_m3_per_s = controller_oa.minimumOutdoorAirFlowRate.get elsif controller_oa.autosizedMinimumOutdoorAirFlowRate.is_initialized min_oa_flow_m3_per_s = controller_oa.autosizedMinimumOutdoorAirFlowRate.get else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{controller_oa.name}: minimum OA flow rate is not available, cannot apply efficiency standard.") return false end min_oa_flow_cfm = OpenStudio.convert(min_oa_flow_m3_per_s, 'm^3/s', 'cfm').get # Calculate the percent OA at design airflow pct_oa = min_oa_flow_m3_per_s / dsn_flow_m3_per_s # The NECB2011 requirement is that systems with an exhaust heat content > 150 kW require an HRV # The calculation for this is done below, to modify erv_required # erv_cfm set to nil here as placeholder, will lead to erv_required = false erv_cfm = nil # Determine if an ERV is required # erv_required = nil if erv_cfm.nil? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}, ERV not required based on #{(pct_oa * 100).round}% OA flow, design supply air flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}.") erv_required = false elsif dsn_flow_cfm < erv_cfm OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}, ERV not required based on #{(pct_oa * 100).round}% OA flow, design supply air flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}. Does not exceed minimum flow requirement of #{erv_cfm}cfm.") erv_required = false else OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}, ERV required based on #{(pct_oa * 100).round}% OA flow, design supply air flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}. Exceeds minimum flow requirement of #{erv_cfm}cfm.") erv_required = true end # This code modifies erv_required for NECB2011 # Calculation of exhaust heat content and check whether it is > 150 kW # get all zones in the model zones = air_loop_hvac.thermalZones # initialize counters sum_zone_oa = 0.0 sum_zone_oa_times_heat_design_t = 0.0 # zone loop zones.each do |zone| # get design heat temperature for each zone; this is equivalent to design exhaust temperature heat_design_t = 21.0 zone_thermostat = zone.thermostat.get if zone_thermostat.to_ThermostatSetpointDualSetpoint.is_initialized dual_thermostat = zone_thermostat.to_ThermostatSetpointDualSetpoint.get if dual_thermostat.heatingSetpointTemperatureSchedule.is_initialized htg_temp_sch = dual_thermostat.heatingSetpointTemperatureSchedule.get htg_temp_sch_ruleset = htg_temp_sch.to_ScheduleRuleset.get winter_dd_sch = htg_temp_sch_ruleset.winterDesignDaySchedule heat_design_t = winter_dd_sch.values.max end end # initialize counter zone_oa = 0.0 # outdoor defined at space level; get OA flow for all spaces within zone spaces = zone.spaces # space loop spaces.each do |space| unless space.designSpecificationOutdoorAir.empty? # if empty, don't do anything outdoor_air = space.designSpecificationOutdoorAir.get # in bTAP, outdoor air specified as outdoor air per oa_flow_per_floor_area = outdoor_air.outdoorAirFlowperFloorArea oa_flow = oa_flow_per_floor_area * space.floorArea * zone.multiplier # oa flow for the space zone_oa += oa_flow # add up oa flow for all spaces to get zone air flow end # space loop end sum_zone_oa += zone_oa # sum of all zone oa flows to get system oa flow sum_zone_oa_times_heat_design_t += (zone_oa * heat_design_t) # calculated to get oa flow weighted average of design exhaust temperature # zone loop end # Calculate average exhaust temperature (oa flow weighted average) avg_exhaust_temp = sum_zone_oa_times_heat_design_t / sum_zone_oa # for debugging/testing # puts "average exhaust temp = #{avg_exhaust_temp}" # puts "sum_zone_oa = #{sum_zone_oa}" # Get January winter design temperature # get model weather file name weather_file_path = air_loop_hvac.model.weatherFile.get.path.get.to_s stat_file_path = weather_file_path.gsub('.epw', '.stat') stat_file = OpenstudioStandards::Weather::StatFile.new(stat_file_path) # get winter(heating) design temp stored in array # Note that the NECB2011 specifies using the 2.5% january design temperature # The outdoor temperature used here is the 0.4% heating design temperature of the coldest month, available in stat file outdoor_temp = stat_file.heating_design_info[1] # for debugging/testing # puts "outdoor design temp = #{outdoor_temp}" # Calculate exhaust heat content exhaust_heat_content = 0.00123 * sum_zone_oa * 1000.0 * (avg_exhaust_temp - outdoor_temp) # for debugging/testing # puts "exhaust heat content = #{exhaust_heat_content}" # Modify erv_required based on exhaust heat content if exhaust_heat_content > 150.0 erv_required = true OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}, ERV required based on exhaust heat content.") else erv_required = false OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{air_loop_hvac.name}, ERV not required based on exhaust heat content.") end return erv_required end |
#air_loop_hvac_motorized_oa_damper_limits(air_loop_hvac, climate_zone) ⇒ Array<Double>
Determine the air flow and number of story limits for whether motorized OA damper is required. If both nil, never required
493 494 495 496 497 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 493 def air_loop_hvac_motorized_oa_damper_limits(air_loop_hvac, climate_zone) minimum_oa_flow_cfm = 0 maximum_stories = 0 return [minimum_oa_flow_cfm, maximum_stories] end |
#air_loop_hvac_static_pressure_reset_required?(air_loop_hvac, has_ddc) ⇒ Boolean
NECB doesn’t require static pressure reset.
return [Boolean] returns true if static pressure reset is required, false if not
483 484 485 486 487 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 483 def air_loop_hvac_static_pressure_reset_required?(air_loop_hvac, has_ddc) # static pressure reset not required sp_reset_required = false return sp_reset_required end |
#air_terminal_single_duct_vav_reheat_set_heating_cap(air_terminal_single_duct_vav_reheat) ⇒ Boolean
Sets the capacity of the reheat coil based on the minimum flow fraction, and the maximum flow rate.
2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 2505 def air_terminal_single_duct_vav_reheat_set_heating_cap(air_terminal_single_duct_vav_reheat) flow_rate_fraction = 0.0 if air_terminal_single_duct_vav_reheat.constantMinimumAirFlowFraction.is_initialized flow_rate_fraction = air_terminal_single_duct_vav_reheat.constantMinimumAirFlowFraction.get else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.AirTerminalSingleDuctVAVReheat', \ "Minimum flow fraction is not defined for terminal device #{air_terminal_single_duct_vav_reheat.name}") return false end cap = 1.2 * 1000.0 * flow_rate_fraction * air_terminal_single_duct_vav_reheat.autosizedMaximumAirFlowRate.to_f * (43.0 - 13.0) if air_terminal_single_duct_vav_reheat.reheatCoil.to_CoilHeatingElectric.is_initialized reheat_coil = air_terminal_single_duct_vav_reheat.reheatCoil.to_CoilHeatingElectric.get reheat_coil.setNominalCapacity(cap) elsif air_terminal_single_duct_vav_reheat.reheatCoil.to_CoilHeatingWater.is_initialized reheat_coil = air_terminal_single_duct_vav_reheat.reheatCoil.to_CoilHeatingWater.get reheat_coil.setPerformanceInputMethod('NominalCapacity') reheat_coil.setRatedCapacity(cap) end air_terminal_single_duct_vav_reheat.setMaximumReheatAirTemperature(43.0) return true end |
#apply_auto_zoning(model:, sizing_run_dir: Dir.pwd, lights_type: 'NECB_Default', lights_scale: 1.0) ⇒ Object
Top level method that merges spaces into zones where possible. This requires a sizing run. This follows the spirit of the EE4 modelling manual found here www.nrcan.gc.ca/energy/software-tools/7457 where the A zone includes those areas in the building that meet three criteria:
-
Served by the same HVAC system
-
Similar operation and function
-
Similar heating/cooling loads
Some expections are dwelling units wet zone and wild zones. These spaces will have special considerations when autozoning a building.
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 54 def apply_auto_zoning(model:, sizing_run_dir: Dir.pwd, lights_type: 'NECB_Default', lights_scale: 1.0) raise('validation of model failed.') unless validate_initial_model(model) # Check to see if model is using another vintage of spacetypes. If so overwrite the @standards for the object with the # other spacetype data. This is required for correct system mapping. template = determine_spacetype_vintage(model) unless template == self.class.name # Frankenstein the standards data wrt spacetype data. @standards_data['space_types'] = Standard.build(template).standards_data['space_types'] end # The first thing we need to do is get a sizing run to determine the heating loads of all the spaces. The default # btap geometry has a one to one relationship of zones to spaces.. So we simply create the thermal zones for all the spaces. # to do this we need to create thermals zone for each space. # Remove any Thermal zones assigned before model.getThermalZones.each(&:remove) # create new thermal zones one to one with spaces. model_create_thermal_zones(model) # do a sizing run. if model_run_sizing_run(model, "#{sizing_run_dir}/autozone") == false raise('autorun sizing run failed!') end # collect sizing information on each space. store_space_sizing_loads(model) # Remove any Thermal zones assigned again to start fresh. model.getThermalZones.each(&:remove) auto_zone_dwelling_units(model) auto_zone_wet_spaces(model: model, lights_type: lights_type, lights_scale: lights_scale) auto_zone_all_other_spaces(model) auto_zone_wild_spaces(model: model, lights_type: lights_type, lights_scale: lights_scale) # This will color the spaces and zones. random = Random.new(1234) # Set ideal hvac in case we want to not implement the hvac yet and still run osm right after this function. # model.getThermalZones.each { |zone| zone.setUseIdealAirLoads(true) } model.getThermalZones.sort.each { |item| item.setRenderingColor(set_random_rendering_color(item, random)) } model.getSpaceTypes.sort.each { |item| item.setRenderingColor(set_random_rendering_color(item, random)) } end |
#apply_building_default_constructionset(model) ⇒ Object
486 487 488 489 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 486 def apply_building_default_constructionset(model) bldg_def_const_set = model_add_construction_set_from_osm(model: model) model.getBuilding.setDefaultConstructionSet(bldg_def_const_set) end |
#apply_default_constructionsets_to_spacetypes(climate_zone, model) ⇒ Object
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 491 def apply_default_constructionsets_to_spacetypes(climate_zone, model) model.getSpaceTypes.sort.each do |space_type| # Get the standards building type stds_building_type = nil if space_type.standardsBuildingType.is_initialized stds_building_type = space_type.standardsBuildingType.get else OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Space type called '#{space_type.name}' has no standards building type.") end # Get the standards space type stds_spc_type = nil if space_type.standardsSpaceType.is_initialized stds_spc_type = space_type.standardsSpaceType.get else OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Space type called '#{space_type.name}' has no standards space type.") end # If the standards space type is Attic, # the building type should be blank. if stds_spc_type == 'Attic' stds_building_type = '' end # Attempt to make a construction set for this space type # and assign it if it can be created. spc_type_const_set = model_add_construction_set_from_osm(model: model) if spc_type_const_set.is_initialized space_type.setDefaultConstructionSet(spc_type_const_set.get) end end end |
#apply_economizers(climate_zone, model) ⇒ Object
1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1558 def apply_economizers(climate_zone, model) # NECB2011 prescribes ability to provide 100% OA (5.2.2.7-5.2.2.9) econ_max_100_pct_oa_sch = OpenStudio::Model::ScheduleRuleset.new(model) econ_max_100_pct_oa_sch.setName('Economizer Max OA Fraction 100 pct') econ_max_100_pct_oa_sch.defaultDaySchedule.setName('Economizer Max OA Fraction 100 pct Default') econ_max_100_pct_oa_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1.0) # Check each airloop model.getAirLoopHVACs.sort.each do |air_loop| if air_loop_hvac_economizer_required?(air_loop) == true # If an economizer is required, determine the economizer type # in the prototype buildings, which depends on climate zone. economizer_type = nil # NECB 5.2.2.8 states that economizer can be controlled based on difference betweeen # return air temperature and outside air temperature OR return air enthalpy # and outside air enthalphy; latter chosen to be consistent with MNECB and CAN-QUEST implementation economizer_type = 'DifferentialEnthalpy' # Set the economizer type # Get the OA system and OA controller oa_sys = air_loop.airLoopHVACOutdoorAirSystem if oa_sys.is_initialized oa_sys = oa_sys.get else OpenStudio.logFree(OpenStudio::Error, 'openstudio.prototype.Model', "#{air_loop.name} is required to have an economizer, but it has no OA system.") next end oa_control = oa_sys.getControllerOutdoorAir oa_control.setEconomizerControlType(economizer_type) end end end |
#apply_envelope(model:, ext_wall_cond: nil, ext_floor_cond: nil, ext_roof_cond: nil, ground_wall_cond: nil, ground_floor_cond: nil, ground_roof_cond: nil, door_construction_cond: nil, fixed_window_cond: nil, glass_door_cond: nil, overhead_door_cond: nil, skylight_cond: nil, glass_door_solar_trans: nil, fixed_wind_solar_trans: nil, skylight_solar_trans: nil, infiltration_scale: nil, necb_hdd: true) ⇒ Object
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 712 def apply_envelope(model:, ext_wall_cond: nil, ext_floor_cond: nil, ext_roof_cond: nil, ground_wall_cond: nil, ground_floor_cond: nil, ground_roof_cond: nil, door_construction_cond: nil, fixed_window_cond: nil, glass_door_cond: nil, overhead_door_cond: nil, skylight_cond: nil, glass_door_solar_trans: nil, fixed_wind_solar_trans: nil, skylight_solar_trans: nil, infiltration_scale: nil, necb_hdd: true) raise('validation of model failed.') unless validate_initial_model(model) model_apply_infiltration_standard(model) ecm = ECMS.new ecm.scale_infiltration_loads(model: model, scale: infiltration_scale) model.getInsideSurfaceConvectionAlgorithm.setAlgorithm('TARP') model.getOutsideSurfaceConvectionAlgorithm.setAlgorithm('TARP') model_add_constructions(model) apply_standard_construction_properties(model: model, ext_wall_cond: ext_wall_cond, ext_floor_cond: ext_floor_cond, ext_roof_cond: ext_roof_cond, ground_wall_cond: ground_wall_cond, ground_floor_cond: ground_floor_cond, ground_roof_cond: ground_roof_cond, door_construction_cond: door_construction_cond, fixed_window_cond: fixed_window_cond, glass_door_cond: glass_door_cond, overhead_door_cond: overhead_door_cond, skylight_cond: skylight_cond, glass_door_solar_trans: glass_door_solar_trans, fixed_wind_solar_trans: fixed_wind_solar_trans, skylight_solar_trans: skylight_solar_trans, necb_hdd: necb_hdd) model_create_thermal_zones(model, @space_multiplier_map) end |
#apply_fdwr_srr_daylighting(model:, fdwr_set: -1.0,, srr_set: -1.0,, necb_hdd: true) ⇒ Object
Thermal zones need to be set to determine conditioned spaces when applying fdwr and srr limits.
# fdwr_set/srr_set settings:
# 0-1: Remove all windows/skylights and add windows/skylights to match this fdwr/srr
# -1: Remove all windows/skylights and add windows/skylights to match max fdwr/srr from NECB
# -2: Do not apply any fdwr/srr changes, leave windows/skylights alone (also works for fdwr/srr > 1)
# -3: Use old method which reduces existing window/skylight size (if necessary) to meet maximum NECB fdwr/srr
# limit
# <-3.1: Remove all the windows/skylights
# > 1: Do nothing
983 984 985 986 987 988 989 990 991 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 983 def apply_fdwr_srr_daylighting(model:, fdwr_set: -1.0, srr_set: -1.0, necb_hdd: true) fdwr_set = -1.0 if (fdwr_set == 'NECB_default') || fdwr_set.nil? srr_set = -1.0 if (srr_set == 'NECB_default') || srr_set.nil? fdwr_set = fdwr_set.to_f srr_set = srr_set.to_f apply_standard_window_to_wall_ratio(model: model, fdwr_set: fdwr_set, necb_hdd: necb_hdd) apply_standard_skylight_to_roof_ratio(model: model, srr_set: srr_set) # model_add_daylighting_controls(model) # to be removed after refactor. end |
#apply_kiva_foundation(model) ⇒ Object
apply the Kiva foundation model to floors and walls with ground boundary condition created by: Kamel Haddad ([email protected])
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 758 def apply_kiva_foundation(model) # define a Kiva model for the whole bldg that's used for the first floor in contact with ground in each zone bldg_kiva_model = OpenStudio::Model::FoundationKiva.new(model) bldg_kiva_model.setName("Bldg Kiva Foundation") bldg_kiva_model.setWallHeightAboveGrade(0.0) bldg_kiva_model.setWallDepthBelowSlab(0.0) model.getThermalZones.sort.each do |zone| zone_kiva_models = [bldg_kiva_model] zone_grd_flr_counter = 0 zone.spaces.sort.each do |space| # store space floors and walls in contact with ground and exterior walls space_ground_floors = [] space_ground_walls = [] space_ext_walls = [] space_ground_floors += space.surfaces.select {|surf| surf.surfaceType.downcase == 'floor' && surf.isGroundSurface } space_ground_walls += space.surfaces.select {|surf| surf.surfaceType.downcase == 'wall' && surf.isGroundSurface } space_ext_walls += space.surfaces.select {|surf| surf.surfaceType.downcase == 'wall' && surf.outsideBoundaryCondition.downcase == 'outdoors'} # loop through space floors in contact with ground and assing a Kiva model for each space_ground_floors.each do |gfloor| zone_grd_flr_counter += 1 if zone_grd_flr_counter > 1 # a new Kiva model is needed for each additional floor in contact with the ground in the zone kiva_model = OpenStudio::Model::FoundationKiva.new(model) kiva_model.setName("#{gfloor.name.to_s} Kiva Foundation") kiva_model.setWallHeightAboveGrade(0.0) kiva_model.setWallDepthBelowSlab(0.0) zone_kiva_models << kiva_model end # Kiva model only works with standard materials. Replace constructions massless materials with standard ones. replace_massless_material_with_std_material(model,gfloor) gfloor.setOutsideBoundaryCondition('Foundation') gfloor.setAdjacentFoundation(zone_kiva_models.last) # Set the exposed perimeter for space floors in contact with the ground. floor_exp_per = 0.0 if !space_ground_walls.empty? floor_exp_per += get_surface_exp_per(gfloor,space_ground_walls) elsif !space_ext_walls.empty? floor_exp_per += get_surface_exp_per(gfloor,space_ext_walls) end gfloor.createSurfacePropertyExposedFoundationPerimeter('TotalExposedPerimeter',floor_exp_per) # specify a foundation boundary condition for space walls in contact with the ground and in # contact with the space floor in contact with ground 'gfloor' space_ground_walls.each do |gwall| if surfaces_are_in_contact?(gfloor,gwall) replace_massless_material_with_std_material(model,gwall) gwall.setOutsideBoundaryCondition('Foundation') gwall.setAdjacentFoundation(zone_kiva_models.last) end end end end end kiva_settings = model.getFoundationKivaSettings if !model.getFoundationKivas.empty? end |
#apply_limit_fdwr(model:, fdwr_lim:) ⇒ Object
25 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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 25 def apply_limit_fdwr(model:, fdwr_lim:) # Loop through all spaces in the model, and # per the PNNL PRM Reference Manual, find the areas # of each space conditioning category (res, nonres, semi-heated) # separately. Include space multipliers. nr_wall_m2 = 0.001 # Avoids divide by zero errors later nr_wind_m2 = 0 res_wall_m2 = 0.001 res_wind_m2 = 0 sh_wall_m2 = 0.001 sh_wind_m2 = 0 total_wall_m2 = 0.001 total_subsurface_m2 = 0.0 # Store the space conditioning category for later use space_cats = {} model.getSpaces.sort.each do |space| # Loop through all surfaces in this space wall_area_m2 = 0 wind_area_m2 = 0 space.surfaces.sort.each do |surface| # Skip non-outdoor surfaces next unless surface.outsideBoundaryCondition == 'Outdoors' # Skip non-walls next unless surface.surfaceType.casecmp('wall').zero? # This wall's gross area (including window area) wall_area_m2 += surface.grossArea * space.multiplier # Subsurfaces in this surface surface.subSurfaces.sort.each do |ss| wind_area_m2 += ss.netArea * space.multiplier end end # Determine the space category # @todo This should really use the heating/cooling loads # from the proposed building. However, in an attempt # to avoid another sizing run just for this purpose, # conditioned status is based on heating/cooling # setpoints. If heated-only, will be assumed Semiheated. # The full-bore method is on the next line in case needed. # cat = thermal_zone_conditioning_category(space, template, climate_zone) cooled = OpenstudioStandards::Space.space_cooled?(space) heated = OpenstudioStandards::Space.space_heated?(space) cat = 'Unconditioned' # Unconditioned if !heated && !cooled cat = 'Unconditioned' # Heated-Only elsif heated && !cooled cat = 'Semiheated' # Heated and Cooled else res = OpenstudioStandards::ThermalZone.thermal_zone_residential?(space.thermalZone.get) cat = if res 'ResConditioned' else 'NonResConditioned' end end space_cats[space] = cat # NECB2011 keep track of totals for NECB regardless of conditioned or not. total_wall_m2 += wall_area_m2 total_subsurface_m2 += wind_area_m2 # this contains doors as well. # Add to the correct category case cat when 'Unconditioned' next # Skip unconditioned spaces when 'NonResConditioned' nr_wall_m2 += wall_area_m2 nr_wind_m2 += wind_area_m2 when 'ResConditioned' res_wall_m2 += wall_area_m2 res_wind_m2 += wind_area_m2 when 'Semiheated' sh_wall_m2 += wall_area_m2 sh_wind_m2 += wind_area_m2 end end # Calculate the WWR of each category wwr_nr = ((nr_wind_m2 / nr_wall_m2) * 100.0).round(1) wwr_res = ((res_wind_m2 / res_wall_m2) * 100).round(1) wwr_sh = ((sh_wind_m2 / sh_wall_m2) * 100).round(1) fdwr = ((total_subsurface_m2 / total_wall_m2) * 100).round(1) # used by NECB2011 # Convert to IP and report nr_wind_ft2 = OpenStudio.convert(nr_wind_m2, 'm^2', 'ft^2').get nr_wall_ft2 = OpenStudio.convert(nr_wall_m2, 'm^2', 'ft^2').get res_wind_ft2 = OpenStudio.convert(res_wind_m2, 'm^2', 'ft^2').get res_wall_ft2 = OpenStudio.convert(res_wall_m2, 'm^2', 'ft^2').get sh_wind_ft2 = OpenStudio.convert(sh_wind_m2, 'm^2', 'ft^2').get sh_wall_ft2 = OpenStudio.convert(sh_wall_m2, 'm^2', 'ft^2').get OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "WWR NonRes = #{wwr_nr.round}%; window = #{nr_wind_ft2.round} ft2, wall = #{nr_wall_ft2.round} ft2.") OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "WWR Res = #{wwr_res.round}%; window = #{res_wind_ft2.round} ft2, wall = #{res_wall_ft2.round} ft2.") OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "WWR Semiheated = #{wwr_sh.round}%; window = #{sh_wind_ft2.round} ft2, wall = #{sh_wall_ft2.round} ft2.") # WWR limit wwr_lim = 40.0 # Check against WWR limit red_nr = wwr_nr > wwr_lim red_res = wwr_res > wwr_lim red_sh = wwr_sh > wwr_lim # puts "Current FDWR is #{fdwr}, must be less than #{fdwr_lim}." # puts "Current subsurf area is #{total_subsurface_m2} and gross surface area is #{total_wall_m2}" # Stop here unless windows / doors need reducing return true unless fdwr > fdwr_lim OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Reducing the size of all windows (by raising sill height) to reduce window area down to the limit of #{wwr_lim.round}%.") # Determine the factors by which to reduce the window / door area mult = fdwr_lim / fdwr # Reduce the window area if any of the categories necessary model.getSpaces.sort.each do |space| # Loop through all surfaces in this space space.surfaces.sort.each do |surface| # Skip non-outdoor surfaces next unless surface.outsideBoundaryCondition == 'Outdoors' # Skip non-walls next unless surface.surfaceType == 'Wall' # Subsurfaces in this surface surface.subSurfaces.sort.each do |ss| # Reduce the size of the window red = 1.0 - mult OpenstudioStandards::Geometry.sub_surface_reduce_area_by_percent_by_raising_sill(ss, red) end end end return true end |
#apply_loads(model:, lights_type: 'NECB_Default', lights_scale: 1.0, validate: true, occupancy_loads_scale: nil, electrical_loads_scale: nil, oa_scale: nil) ⇒ Object
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 668 def apply_loads(model:, lights_type: 'NECB_Default', lights_scale: 1.0, validate: true, occupancy_loads_scale: nil, electrical_loads_scale: nil, oa_scale: nil) # Create ECM object. ecm = ECMS.new lights_scale = convert_arg_to_f(variable: lights_scale, default: 1.0) if validate raise('validation of model failed.') unless validate_initial_model(model) raise('validation of spacetypes failed.') unless validate_and_upate_space_types(model) end # this sets/stores the template version loads that the model uses. model.getBuilding.setStandardsTemplate(self.class.name) set_occ_sensor_spacetypes(model, @space_type_map) model_add_loads(model, lights_type, lights_scale) ecm.scale_occupancy_loads(model: model, scale: occupancy_loads_scale) ecm.scale_electrical_loads(model: model, scale: electrical_loads_scale) ecm.scale_oa_loads(model: model, scale: oa_scale) end |
#apply_loop_pump_power(model:, sizing_run_dir:) ⇒ Object
1129 1130 1131 1132 1133 1134 1135 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1129 def apply_loop_pump_power(model:, sizing_run_dir:) # Remove duplicate materials and constructions # Note For NECB2015 This is the 2nd time this method is bieng run. # First time it ran in the super() within model_apply_standard() method # model = return BTAP::FileIO::remove_duplicate_materials_and_constructions(model) return model end |
#apply_max_fdwr_nrcan(model:, fdwr_lim:) ⇒ Object
This method applies the maximum fenestration and door to wall ratio to a building as per NECB 2011 8.4.4.3 and 3.2.1.4 (or equivalent in other versions of the NECB). It first checks for al exterior walls adjacent to conditioned spaces. It distinguishes between plenums and other conditioned spaces. It uses both to calculate the maximum window area to be applied to the building but attempts to put these windows only on non-plenum conditioned spaces (if possible).
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 651 def apply_max_fdwr_nrcan(model:, fdwr_lim:) # First determine which vertical (between 89 and 91 degrees from horizontal) walls are adjacent to conditioned # spaces. exp_surf_info = find_exposed_conditioned_vertical_surfaces(model) # If there are none (or very few) then throw a warning. if exp_surf_info['total_exp_wall_area_m2'] < 0.1 OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', 'This building has no exposed walls adjacent to heated spaces.') return false end construct_set = model.getBuilding.defaultConstructionSet.get fixed_window_construct_set = construct_set.defaultExteriorSubSurfaceConstructions.get.fixedWindowConstruction.get # IF FDWR is greater than 1 then something is wrong raise an error. If it is less than 0.001 assume all the windows # should go. if fdwr_lim > 1 OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', 'This building requires a larger window area than there is wall area.') return false elsif fdwr_lim < 0.001 exp_surf_info['exp_nonplenum_walls'].sort.each do |exp_surf| exp_surf.subSurfaces.sort.each(&:remove) end return true end # Get the required window area. win_area = fdwr_lim * exp_surf_info['total_exp_wall_area_m2'] # Try to put the windows on non-plenum walls if possible. So determine if you can fit the required window area # on the non-plenum wall area. if win_area <= exp_surf_info['exp_nonplenum_wall_area_m2'] # If you can fit the windows on the non-plenum wall area then recalculate the window ratio so that is is only for # the non-plenum walls. nonplenum_fdwr = win_area / exp_surf_info['exp_nonplenum_wall_area_m2'] exp_surf_info['exp_nonplenum_walls'].sort.each do |exp_surf| # Remove any subsurfaces, add the window, set the name to be whatever the surface name is plus the subsurface # type (which will be 'fixedwindow') exp_surf.subSurfaces.sort.each(&:remove) exp_surf.setWindowToWallRatio(nonplenum_fdwr) exp_surf.subSurfaces.sort.each do |sub_surf| sub_surf.setSubSurfaceType('FixedWindow') sub_surf.setConstruction(fixed_window_construct_set) new_name = exp_surf.name.to_s + '_' + sub_surf.subSurfaceType.to_s sub_surf.setName(new_name) end end else # There was not enough non-plenum wall area so add the windows to both the plenum and non-plenum walls. This is # done separately because the 'find_exposed_conditioned_vertical_surfaces' method returns the plenum and # non-plenum walls separately. exp_surf_info['exp_nonplenum_walls'].sort.each do |exp_surf| # Remove any subsurfaces, add the window, set the name to be whatever the surface name is plus the subsurface # type (which will be 'fixedwindow') exp_surf.subSurfaces.sort.each(&:remove) exp_surf.setWindowToWallRatio(fdwr_lim) exp_surf.subSurfaces.sort.each do |sub_surf| sub_surf.setSubSurfaceType('FixedWindow') sub_surf.setConstruction(fixed_window_construct_set) new_name = exp_surf.name.to_s + '_' + sub_surf.subSurfaceType.to_s sub_surf.setName(new_name) end end exp_surf_info['exp_plenum_walls'].sort.each do |exp_surf| # Remove any subsurfaces, add the window, set the name to be whatever the surface name is plus the subsurface # type (which will be 'fixedwindow') exp_surf.subSurfaces.sort.each(&:remove) exp_surf.setWindowToWallRatio(fdwr_lim) exp_surf.subSurfaces.sort.each do |sub_surf| sub_surf.setSubSurfaceType('FixedWindow') sub_surf.setConstruction(fixed_window_construct_set) new_name = exp_surf.name.to_s + '_' + sub_surf.subSurfaceType.to_s sub_surf.setName(new_name) end end end return true end |
#apply_max_srr_nrcan(model:, srr_lim:) ⇒ Object
This method is similar to the ‘apply_max_fdwr’ method above but applies the maximum skylight to roof area ratio to a building as per NECB 2011 8.4.4.3 and 3.2.1.4 (or equivalent in other versions of the NECB). It first checks for all exterior roofs adjacent to conditioned spaces. It distinguishes between plenums and other conditioned spaces. It uses only the non-plenum roof area to calculate the maximum skylight area to be applied to the building.
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 731 def apply_max_srr_nrcan(model:, srr_lim:) # First determine which roof surfaces are adjacent to heated spaces (both plenum and non-plenum). exp_surf_info = find_exposed_conditioned_roof_surfaces(model) # If the non-plenum roof area is very small raise a warning. It may be perfectly fine but it is probably a good # idea to warn the user. if exp_surf_info['exp_nonplenum_roof_area_m2'] < 0.1 OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'This building has no exposed ceilings adjacent to spaces that are not attics or plenums. No skylights will be added.') return false end # If the SRR is greater than one something is seriously wrong so raise an error. If it is less than 0.001 assume # all the skylights should go. if srr_lim > 1 OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', 'This building requires a larger skylight area than there is roof area.') return false elsif srr_lim < 0.001 exp_surf_info['exp_nonplenum_roofs'].sort.each do |exp_surf| exp_surf.subSurfaces.sort.each(&:remove) end return true end construct_set = model.getBuilding.defaultConstructionSet.get skylight_construct_set = construct_set.defaultExteriorSubSurfaceConstructions.get.skylightConstruction.get # Go through all of exposed roofs adjacent to heated, non-plenum spaces, remove any existing subsurfaces, and add # a skylight in the centroid of the surface, with the same shape of the surface, only scaled to be the area # determined by the SRR. The name of the skylight will be the surface name with the subsurface type attached # ('skylight' in this case). Note that this method will only work if the surface does not fold into itself (like an # L or a V). exp_surf_info['exp_nonplenum_roofs'].sort.each do |roof| # sub_surface_create_centered_subsurface_from_scaled_surface(roof, srr_lim, model) sub_surface_create_scaled_subsurfaces_from_surface(surface: roof, area_fraction: srr_lim, construction: skylight_construct_set) end return true end |
#apply_standard_construction_properties(model:, runner: nil, ext_wall_cond: nil, ext_floor_cond: nil, ext_roof_cond: nil, ground_wall_cond: nil, ground_floor_cond: nil, ground_roof_cond: nil, fixed_window_cond: nil, fixed_wind_solar_trans: nil, fixed_wind_vis_trans: nil, operable_wind_solar_trans: nil, operable_window_cond: nil, operable_wind_vis_trans: nil, glass_door_cond: nil, glass_door_solar_trans: nil, glass_door_vis_trans: nil, door_construction_cond: nil, overhead_door_cond: nil, skylight_cond: nil, skylight_solar_trans: nil, skylight_vis_trans: nil, tubular_daylight_dome_cond: nil, tubular_daylight_dome_solar_trans: nil, tubular_daylight_dome_vis_trans: nil, tubular_daylight_diffuser_cond: nil, tubular_daylight_diffuser_solar_trans: nil, tubular_daylight_diffuser_vis_trans: nil, necb_hdd: true) ⇒ Boolean
Go through the default construction sets and hard-assigned constructions. Clone the existing constructions and set their intended surface type and standards construction type per the PRM. For some standards, this will involve making modifications. For others, it will not.
90.1-2007, 90.1-2010, 90.1-2013
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 300 def apply_standard_construction_properties(model:, runner: nil, # ext surfaces ext_wall_cond: nil, ext_floor_cond: nil, ext_roof_cond: nil, # ground surfaces ground_wall_cond: nil, ground_floor_cond: nil, ground_roof_cond: nil, # fixed Windows fixed_window_cond: nil, fixed_wind_solar_trans: nil, fixed_wind_vis_trans: nil, # operable windows operable_wind_solar_trans: nil, operable_window_cond: nil, operable_wind_vis_trans: nil, # glass doors glass_door_cond: nil, glass_door_solar_trans: nil, glass_door_vis_trans: nil, # opaque doors door_construction_cond: nil, overhead_door_cond: nil, # skylights skylight_cond: nil, skylight_solar_trans: nil, skylight_vis_trans: nil, # tubular daylight dome tubular_daylight_dome_cond: nil, tubular_daylight_dome_solar_trans: nil, tubular_daylight_dome_vis_trans: nil, # tubular daylight diffuser tubular_daylight_diffuser_cond: nil, tubular_daylight_diffuser_solar_trans: nil, tubular_daylight_diffuser_vis_trans: nil, necb_hdd: true) model.getDefaultConstructionSets.sort.each do |default_surface_construction_set| BTAP.runner_register('Info', 'apply_standard_construction_properties', runner) if model.weatherFile.empty? || model.weatherFile.get.path.empty? || !File.exist?(model.weatherFile.get.path.get.to_s) BTAP.runner_register('Error', 'Weather file is not defined. Please ensure the weather file is defined and exists.', runner) return false end # hdd required in scope for eval function. hdd = get_necb_hdd18(model: model, necb_hdd: necb_hdd) # Lambdas are preferred over methods in methods for small utility methods. correct_cond = lambda do |conductivity, surface_type| return conductivity.nil? || conductivity.to_f <= 0.0 || conductivity == 'NECB_Default' ? eval(model_find_objects(@standards_data['surface_thermal_transmittance'], surface_type)[0]['formula']) : conductivity.to_f end # Converts trans and vis to nil if requesting default.. or casts the string to a float. correct_vis_trans = lambda do |value| return value.nil? || value.to_f <= 0.0 || value == 'NECB_Default' ? nil : value.to_f end BTAP::Resources::Envelope::ConstructionSets.customize_default_surface_construction_set!(model: model, name: "#{default_surface_construction_set.name.get} at hdd = #{get_necb_hdd18(model: model, necb_hdd: necb_hdd)}", default_surface_construction_set: default_surface_construction_set, # ext surfaces ext_wall_cond: correct_cond.call(ext_wall_cond, 'boundary_condition' => 'Outdoors', 'surface' => 'Wall'), ext_floor_cond: correct_cond.call(ext_floor_cond, 'boundary_condition' => 'Outdoors', 'surface' => 'Floor'), ext_roof_cond: correct_cond.call(ext_roof_cond, 'boundary_condition' => 'Outdoors', 'surface' => 'RoofCeiling'), # ground surfaces ground_wall_cond: correct_cond.call(ground_wall_cond, 'boundary_condition' => 'Ground', 'surface' => 'Wall'), ground_floor_cond: correct_cond.call(ground_floor_cond, 'boundary_condition' => 'Ground', 'surface' => 'Floor'), ground_roof_cond: correct_cond.call(ground_roof_cond, 'boundary_condition' => 'Ground', 'surface' => 'RoofCeiling'), # fixed Windows fixed_window_cond: correct_cond.call(fixed_window_cond, 'boundary_condition' => 'Outdoors', 'surface' => 'Window'), fixed_wind_solar_trans: correct_vis_trans.call(fixed_wind_solar_trans), fixed_wind_vis_trans: correct_vis_trans.call(fixed_wind_vis_trans), # operable windows operable_wind_solar_trans: correct_vis_trans.call(operable_wind_solar_trans), operable_window_cond: correct_cond.call(fixed_window_cond, 'boundary_condition' => 'Outdoors', 'surface' => 'Window'), operable_wind_vis_trans: correct_vis_trans.call(operable_wind_vis_trans), # glass doors glass_door_cond: correct_cond.call(glass_door_cond, 'boundary_condition' => 'Outdoors', 'surface' => 'Window'), glass_door_solar_trans: correct_vis_trans.call(glass_door_solar_trans), glass_door_vis_trans: correct_vis_trans.call(glass_door_vis_trans), # opaque doors door_construction_cond: correct_cond.call(door_construction_cond, 'boundary_condition' => 'Outdoors', 'surface' => 'Door'), overhead_door_cond: correct_cond.call(overhead_door_cond, 'boundary_condition' => 'Outdoors', 'surface' => 'Door'), # skylights skylight_cond: correct_cond.call(skylight_cond, 'boundary_condition' => 'Outdoors', 'surface' => 'Window'), skylight_solar_trans: correct_vis_trans.call(skylight_solar_trans), skylight_vis_trans: correct_vis_trans.call(skylight_vis_trans), # tubular daylight dome tubular_daylight_dome_cond: correct_cond.call(skylight_cond, 'boundary_condition' => 'Outdoors', 'surface' => 'Window'), tubular_daylight_dome_solar_trans: correct_vis_trans.call(tubular_daylight_dome_solar_trans), tubular_daylight_dome_vis_trans: correct_vis_trans.call(tubular_daylight_dome_vis_trans), # tubular daylight diffuser tubular_daylight_diffuser_cond: correct_cond.call(skylight_cond, 'boundary_condition' => 'Outdoors', 'surface' => 'Window'), tubular_daylight_diffuser_solar_trans: correct_vis_trans.call(tubular_daylight_diffuser_solar_trans), tubular_daylight_diffuser_vis_trans: correct_vis_trans.call(tubular_daylight_diffuser_vis_trans)) end # sets all surfaces to use default constructions sets except adiabatic, where it does a hard assignment of the interior wall construction type. model.getPlanarSurfaces.sort.each(&:resetConstruction) # if the default construction set is defined..try to assign the interior wall to the adiabatic surfaces BTAP::Resources::Envelope.assign_interior_surface_construction_to_adiabatic_surfaces(model, nil) BTAP.runner_register('Info', ' apply_standard_construction_properties was sucessful.', runner) end |
#apply_standard_efficiencies(model:, sizing_run_dir:, dcv_type: 'NECB_Default', necb_reference_hp: false) ⇒ Object
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1060 def apply_standard_efficiencies(model:, sizing_run_dir:, dcv_type: 'NECB_Default', necb_reference_hp: false) raise('validation of model failed.') unless validate_initial_model(model) climate_zone = 'NECB HDD Method' raise("sizing run 1 failed! check #{sizing_run_dir}") if model_run_sizing_run(model, "#{sizing_run_dir}/plant_loops") == false # This is needed for NECB2011 as a workaround for sizing the reheat boxes. model.getAirTerminalSingleDuctVAVReheats.each { |iobj| air_terminal_single_duct_vav_reheat_set_heating_cap(iobj) } # Apply the prototype HVAC assumptions model_apply_prototype_hvac_assumptions(model, nil, climate_zone) # Apply the HVAC efficiency standard sql_db_vars_map = {} model_apply_hvac_efficiency_standard(model, climate_zone, sql_db_vars_map: sql_db_vars_map, necb_ref_hp: necb_reference_hp) model_enable_demand_controlled_ventilation(model, dcv_type) return sql_db_vars_map end |
#apply_standard_lights(set_lights: true, space_type:, space_type_properties:, lights_type:, lights_scale:) ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/lighting.rb', line 2 def apply_standard_lights(set_lights: true, space_type:, space_type_properties:, lights_type:, lights_scale:) ##### Remove leading or trailing whitespace in case users add them in inputs if lights_scale.instance_of?(String) lights_scale = lights_scale.strip end if lights_type.nil? || lights_type == 'none' lights_type = 'NECB_Default' end if lights_scale.nil? || lights_scale == 'none' || lights_scale == 'NECB_Default' lights_scale = 1.0 end ##### Convert a string to a float if lights_scale.instance_of?(String) lights_scale = lights_scale.to_f end lights_have_info = false lighting_per_area = space_type_properties['lighting_per_area'].to_f lighting_per_person = space_type_properties['lighting_per_person'].to_f lights_frac_to_return_air = space_type_properties['lighting_fraction_to_return_air'].to_f lights_frac_radiant = space_type_properties['lighting_fraction_radiant'].to_f lights_frac_visible = space_type_properties['lighting_fraction_visible'].to_f lights_frac_replaceable = space_type_properties['lighting_fraction_replaceable'].to_f lights_have_info = true if !lighting_per_area.zero? || !lighting_per_person.zero? ##### NOTE: Reference for LED lighting's return air, radiant, and visible fraction values is: page 142, NREL (2014), "Proven Energy-Saving Technologies for Commercial Properties", available at https://www.nrel.gov/docs/fy15osti/63807.pdf if lights_type == 'LED' led_lights_have_info = false led_spacetype_data = @standards_data['tables']['led_lighting_data']['table'] standards_building_type = space_type.standardsBuildingType.is_initialized ? space_type.standardsBuildingType.get : nil standards_space_type = space_type.standardsSpaceType.is_initialized ? space_type.standardsSpaceType.get : nil led_space_type_properties = led_spacetype_data.detect { |s| (s['building_type'] == standards_building_type) && (s['space_type'] == standards_space_type) } if led_space_type_properties.nil? raise("#{standards_building_type} for #{standards_space_type} was not found please verify the led lighting database names match the space type names.") end lighting_per_area_led_lighting = led_space_type_properties['lighting_per_area'].to_f lights_frac_to_return_air_led_lighting = led_space_type_properties['lighting_fraction_to_return_air'].to_f lights_frac_radiant_led_lighting = led_space_type_properties['lighting_fraction_radiant'].to_f lights_frac_visible_led_lighting = led_space_type_properties['lighting_fraction_visible'].to_f led_lights_have_info = true unless lighting_per_area_led_lighting.zero? end return unless set_lights && lights_have_info # Remove all but the first instance instances = space_type.lights.sort if instances.empty? definition = OpenStudio::Model::LightsDefinition.new(space_type.model) if lights_type == 'NECB_Default' definition.setName("#{space_type.name} Lights Definition") elsif lights_type == 'LED' definition.setName("#{space_type.name} Lights Definition - LED lighting") end # puts definition.name().to_s instance = OpenStudio::Model::Lights.new(definition) instance.setName("#{space_type.name} Lights") # puts instance.name.to_s instance.setSpaceType(space_type) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} had no lights, one has been created.") instances << instance elsif instances.size > 1 instances.each_with_index do |inst, i| next if i.zero? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "Removed #{inst.name} from #{space_type.name}.") inst.remove end end # Modify the definition of the instance space_type.lights.sort.each do |inst| definition = inst.lightsDefinition unless lighting_per_area.zero? if lights_type == 'NECB_Default' set_lighting_per_area(space_type: space_type, definition: definition, lighting_per_area: lighting_per_area, lights_scale: lights_scale) elsif lights_type == 'LED' set_lighting_per_area_led_lighting(space_type: space_type, definition: definition, lighting_per_area_led_lighting: lighting_per_area_led_lighting, lights_scale: lights_scale) end end unless lighting_per_person.zero? definition.setWattsperPerson(OpenStudio.convert(lighting_per_person.to_f, 'W/person', 'W/person').get) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} set lighting to #{lighting_per_person} W/person.") end unless lights_frac_to_return_air.zero? if lights_type == 'NECB_Default' definition.setReturnAirFraction(lights_frac_to_return_air) elsif lights_type == 'LED' definition.setReturnAirFraction(lights_frac_to_return_air_led_lighting) end end unless lights_frac_radiant.zero? if lights_type == 'NECB_Default' definition.setFractionRadiant(lights_frac_radiant) elsif lights_type == 'LED' definition.setFractionRadiant(lights_frac_radiant_led_lighting) end end unless lights_frac_visible.zero? if lights_type == 'NECB_Default' definition.setFractionVisible(lights_frac_visible) elsif lights_type == 'LED' definition.setFractionVisible(lights_frac_visible_led_lighting) end end # unless lights_frac_replaceable.zero? # definition.setFractionReplaceable(lights_frac_replaceable) # end end # If additional lights are specified, add those too additional_lighting_per_area = space_type_properties['additional_lighting_per_area'].to_f # If there are none then exit method return if additional_lighting_per_area.zero? # Create the lighting definition additional_lights_def = OpenStudio::Model::LightsDefinition.new(space_type.model) additional_lights_def.setName("#{space_type.name} Additional Lights Definition") additional_lights_def.setWattsperSpaceFloorArea(OpenStudio.convert(additional_lighting_per_area.to_f, 'W/ft^2', 'W/m^2').get) additional_lights_def.setReturnAirFraction(lights_frac_to_return_air) additional_lights_def.setFractionRadiant(lights_frac_radiant) additional_lights_def.setFractionVisible(lights_frac_visible) # Create the lighting instance and hook it up to the space type additional_lights = OpenStudio::Model::Lights.new(additional_lights_def) additional_lights.setName("#{space_type.name} Additional Lights") additional_lights.setSpaceType(space_type) end |
#apply_standard_skylight_to_roof_ratio(model:, srr_set: -1.0)) ⇒ Object
Reduces the SRR to the values specified by the PRM. SRR reduction will be done by shrinking vertices toward the centroid.
164 165 166 167 168 169 170 171 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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 164 def apply_standard_skylight_to_roof_ratio(model:, srr_set: -1.0) # If srr_set is between 1.0 and 1.2 set it to the maximum allowed by the NECB. If srr_set is between 0.0 and 1.0 # apply whatever was passed. If srr_set >= 1.2 then set the existing srr of the building to be the necb maximum # only if the the srr exceeds this maximum (otherwise leave it to be whatever was modeled). # srr_set settings: # 0-1: Remove all skylights and add skylights to match this srr # -1: Remove all skylights and add skylights to match max srr from NECB # -2: Do not apply any srr changes, leave skylights alone (also works for srr > 1) # -3: Use old method which reduces existing skylight size (if necessary) to meet maximum NECB skylight limit # <-3.1: Remove all skylights # > 1: Do nothing return if srr_set.to_f > 1.0 return apply_max_srr_nrcan(model: model, srr_lim: srr_set.to_f) if srr_set.to_f >= 0.0 && srr_set.to_f <= 1.0 # Get the maximum NECB srr return apply_max_srr_nrcan(model: model, srr_lim: get_standards_constant('skylight_to_roof_ratio_max_value').to_f) if srr_set.to_f >= -1.1 && srr_set.to_f <= -0.9 return if srr_set.to_f >= -2.1 && srr_set.to_f <= -1.9 return apply_max_srr_nrcan(model: model, srr_lim: srr_set.to_f) if srr_set.to_f < -3.1 return unless srr_set.to_f >= -3.1 && srr_set.to_f <= -2.9 # SRR limit srr_lim = get_standards_constant('skylight_to_roof_ratio_max_value') * 100.0 # Loop through all spaces in the model, and # per the PNNL PRM Reference Manual, find the areas # of each space conditioning category (res, nonres, semi-heated) # separately. Include space multipliers. nr_wall_m2 = 0.001 # Avoids divide by zero errors later nr_sky_m2 = 0 res_wall_m2 = 0.001 res_sky_m2 = 0 sh_wall_m2 = 0.001 sh_sky_m2 = 0 total_roof_m2 = 0.001 total_subsurface_m2 = 0 model.getSpaces.sort.each do |space| # Loop through all surfaces in this space wall_area_m2 = 0 sky_area_m2 = 0 space.surfaces.sort.each do |surface| # Skip non-outdoor surfaces next unless surface.outsideBoundaryCondition == 'Outdoors' # Skip non-walls next unless surface.surfaceType == 'RoofCeiling' # This wall's gross area (including skylight area) wall_area_m2 += surface.grossArea * space.multiplier # Subsurfaces in this surface surface.subSurfaces.sort.each do |ss| sky_area_m2 += ss.netArea * space.multiplier end end # Determine the space category cat = 'NonRes' if OpenstudioStandards::Space.space_residential?(space) cat = 'Res' end # if space.is_semiheated # cat = 'Semiheated' # end # Add to the correct category case cat when 'NonRes' nr_wall_m2 += wall_area_m2 nr_sky_m2 += sky_area_m2 when 'Res' res_wall_m2 += wall_area_m2 res_sky_m2 += sky_area_m2 when 'Semiheated' sh_wall_m2 += wall_area_m2 sh_sky_m2 += sky_area_m2 end total_roof_m2 += wall_area_m2 total_subsurface_m2 += sky_area_m2 end # Calculate the SRR of each category srr_nr = ((nr_sky_m2 / nr_wall_m2) * 100).round(1) srr_res = ((res_sky_m2 / res_wall_m2) * 100).round(1) srr_sh = ((sh_sky_m2 / sh_wall_m2) * 100).round(1) srr = ((total_subsurface_m2 / total_roof_m2) * 100.0).round(1) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "The skylight to roof ratios (SRRs) are: NonRes: #{srr_nr.round}%, Res: #{srr_res.round}%.") # Check against SRR limit red_nr = srr_nr > srr_lim red_res = srr_res > srr_lim red_sh = srr_sh > srr_lim # Stop here unless windows need reducing return true unless srr > srr_lim OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Model', "Reducing the size of all windows (by raising sill height) to reduce window area down to the limit of #{srr_lim.round}%.") # Determine the factors by which to reduce the window / door area mult = srr_lim / srr # Reduce the subsurface areas model.getSpaces.sort.each do |space| # Loop through all surfaces in this space space.surfaces.sort.each do |surface| # Skip non-outdoor surfaces next unless surface.outsideBoundaryCondition == 'Outdoors' # Skip non-walls next unless surface.surfaceType == 'RoofCeiling' # Subsurfaces in this surface surface.subSurfaces.sort.each do |ss| # Reduce the size of the subsurface red = 1.0 - mult OpenstudioStandards::Geometry.sub_surface_reduce_area_by_percent_by_shrinking_toward_centroid(ss, red) end end end return true end |
#apply_standard_window_to_wall_ratio(model:, fdwr_set: -1.0,, necb_hdd: true) ⇒ Object
Reduces the WWR to the values specified by the NECB NECB 3.2.1.4
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 4 def apply_standard_window_to_wall_ratio(model:, fdwr_set: -1.0, necb_hdd: true) # NECB FDWR limit hdd = get_necb_hdd18(model: model, necb_hdd: necb_hdd) # Get the maximum NECB fdwr # fdwr_set settings: # 0-1: Remove all windows and add windows to match this fdwr # -1: Remove all windows and add windows to match max fdwr from NECB # -2: Do not apply any fdwr changes, leave windows alone (also works for fdwr > 1) # -3: Use old method which reduces existing window size (if necessary) to meet maximum NECB fdwr limit # <-3.1: Remove all windows and doors # > 1: Do nothing return if fdwr_set.to_f > 1.0 return apply_max_fdwr_nrcan(model: model, fdwr_lim: fdwr_set.to_f) if fdwr_set.to_f >= 0.0 && fdwr_set.to_f <= 1.0 return apply_max_fdwr_nrcan(model: model, fdwr_lim: max_fwdr(hdd).round(3)) if fdwr_set.to_f >= -1.1 && fdwr_set.to_f <= -0.9 return if fdwr_set.to_f >= -2.1 && fdwr_set.to_f <= -1.9 return apply_limit_fdwr(model: model, fdwr_lim: (max_fwdr(hdd) * 100.0).round(1)) if fdwr_set.to_f >= -3.1 && fdwr_set.to_f <= -2.9 return apply_max_fdwr_nrcan(model: model, fdwr_lim: fdwr_set.to_f) if fdwr_set.to_f < -3.1 end |
#apply_systems(model:, primary_heating_fuel:, swh_fuel:, sizing_run_dir:, shw_scale:, baseline_system_zones_map_option:) ⇒ Object
Organizes Zones and assigns them to appropriate systems according to NECB 2011-17 systems spacetype rules in Sec 8. requires requires fuel type to be assigned for each system aspect. Defaults to gas hydronic.
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 96 def apply_systems(model:, primary_heating_fuel:, swh_fuel:, sizing_run_dir:, shw_scale:, baseline_system_zones_map_option:) raise('validation of model failed.') unless validate_initial_model(model) # Check to see if model is using another vintage of spacetypes. If so overwrite the @standards for the object with the # other spacetype data. This is required for correct system mapping. template = determine_spacetype_vintage(model) unless template == self.class.name # Frankenstein the standards data wrt spacetype data. @standards_data['space_types'] = Standard.build(template).standards_data['space_types'] end # do a sizing run. if model_run_sizing_run(model, "#{sizing_run_dir}/autozone_systems") == false raise('autorun sizing run failed!') end # collect sizing information on each space. store_space_sizing_loads(model) # remove idealair from zones if any. model.getZoneHVACIdealLoadsAirSystems.each(&:remove) @hw_loop = create_hw_loop_if_required(self.fuel_type_set.baseboard_type, self.fuel_type_set.boiler_fueltype, self.fuel_type_set.backup_boiler_fueltype, self.fuel_type_set.mau_heating_coil_type, model) # Rule that all dwelling units have their own zone and system. auto_system_dwelling_units(model: model, necb_reference_hp: self.fuel_type_set.necb_reference_hp, necb_reference_hp_supp_fuel: self.fuel_type_set.necb_reference_hp_supp_fuel, baseboard_type: self.fuel_type_set.baseboard_type, boiler_fueltype: self.fuel_type_set.boiler_fueltype, chiller_type: self.fuel_type_set.chiller_type, fan_type: self.fuel_type_set.fan_type, heating_coil_type_sys3: self.fuel_type_set.heating_coil_type_sys3, heating_coil_type_sys4: self.fuel_type_set.heating_coil_type_sys4, hw_loop: @hw_loop, heating_coil_type_sys6: self.fuel_type_set.heating_coil_type_sys6, mau_cooling_type: self.fuel_type_set.mau_cooling_type, mau_heating_coil_type: self.fuel_type_set.mau_heating_coil_type, mau_type: self.fuel_type_set.mau_type, baseline_system_zones_map_option: baseline_system_zones_map_option) # Assign a single system 4 for all wet spaces.. and assign the control zone to the one with the largest load. auto_system_wet_spaces(baseboard_type: self.fuel_type_set.baseboard_type, necb_reference_hp: self.fuel_type_set.necb_reference_hp, necb_reference_hp_supp_fuel: self.fuel_type_set.necb_reference_hp_supp_fuel, boiler_fueltype: self.fuel_type_set.boiler_fueltype, heating_coil_type_sys4: self.fuel_type_set.heating_coil_type_sys4, model: model) # Assign a single system 4 for all storage spaces.. and assign the control zone to the one with the largest load. auto_system_storage_spaces(baseboard_type: self.fuel_type_set.baseboard_type, necb_reference_hp: self.fuel_type_set.necb_reference_hp, necb_reference_hp_supp_fuel: self.fuel_type_set.necb_reference_hp_supp_fuel, boiler_fueltype: self.fuel_type_set.boiler_fueltype, heating_coil_type_sys4: self.fuel_type_set.heating_coil_type_sys4, model: model) # Assign the wild spaces to a single system 4 system with a control zone with the largest load. auto_system_wild_spaces(baseboard_type: self.fuel_type_set.baseboard_type, necb_reference_hp: self.fuel_type_set.necb_reference_hp, necb_reference_hp_supp_fuel: self.fuel_type_set.necb_reference_hp_supp_fuel, heating_coil_type_sys4: self.fuel_type_set.heating_coil_type_sys4, model: model) # do the regular assignment for the rest and group where possible. auto_system_all_other_spaces(model: model, necb_reference_hp: self.fuel_type_set.necb_reference_hp, necb_reference_hp_supp_fuel: self.fuel_type_set.necb_reference_hp_supp_fuel, baseboard_type: self.fuel_type_set.baseboard_type, boiler_fueltype: self.fuel_type_set.boiler_fueltype, chiller_type: self.fuel_type_set.chiller_type, fan_type: self.fuel_type_set.fan_type, heating_coil_type_sys3: self.fuel_type_set.heating_coil_type_sys3, heating_coil_type_sys4: self.fuel_type_set.heating_coil_type_sys4, hw_loop: @hw_loop, heating_coil_type_sys6: self.fuel_type_set.heating_coil_type_sys6, mau_cooling_type: self.fuel_type_set.mau_cooling_type, mau_heating_coil_type: self.fuel_type_set.mau_heating_coil_type, mau_type: self.fuel_type_set.mau_type ) model_add_swh(model: model, swh_fueltype: swh_fuel, shw_scale: shw_scale) model_apply_sizing_parameters(model) # set a larger tolerance for unmet hours from default 0.2 to 1.0C model.getOutputControlReportingTolerances.setToleranceforTimeHeatingSetpointNotMet(1.0) model.getOutputControlReportingTolerances.setToleranceforTimeCoolingSetpointNotMet(1.0) end |
#apply_systems_and_efficiencies(model:, sizing_run_dir:, primary_heating_fuel:, swh_fuel:, dcv_type: 'NECB_Default', ecm_system_name: 'NECB_Default', ecm_system_zones_map_option: 'NECB_Default', erv_package: 'NECB_Default', boiler_eff: nil, furnace_eff: nil, unitary_cop: nil, shw_eff: nil, daylighting_type: 'NECB_Default', nv_type: nil, nv_opening_fraction: nil, nv_temp_out_min: nil, nv_delta_temp_in_out: nil, pv_ground_type:, pv_ground_total_area_pv_panels_m2:, pv_ground_tilt_angle:, pv_ground_azimuth_angle:, pv_ground_module_description:, chiller_type: 'NECB_Default', shw_scale:, airloop_economizer_type: nil, baseline_system_zones_map_option:) ⇒ Object
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 548 def apply_systems_and_efficiencies(model:, sizing_run_dir:, primary_heating_fuel:, swh_fuel:, dcv_type: 'NECB_Default', ecm_system_name: 'NECB_Default', ecm_system_zones_map_option: 'NECB_Default', erv_package: 'NECB_Default', boiler_eff: nil, furnace_eff: nil, unitary_cop: nil, shw_eff: nil, daylighting_type: 'NECB_Default', nv_type: nil, nv_opening_fraction: nil, nv_temp_out_min: nil, nv_delta_temp_in_out: nil, pv_ground_type:, pv_ground_total_area_pv_panels_m2:, pv_ground_tilt_angle:, pv_ground_azimuth_angle:, pv_ground_module_description:, chiller_type: 'NECB_Default', shw_scale:, airloop_economizer_type: nil, baseline_system_zones_map_option:) # Create ECM object. ecm = ECMS.new # -------- Systems Layout----------- # Create Default Systems. apply_systems(model: model, primary_heating_fuel: primary_heating_fuel, swh_fuel: swh_fuel, sizing_run_dir: sizing_run_dir, shw_scale: shw_scale, baseline_system_zones_map_option: baseline_system_zones_map_option) # Apply new ECM system. Overwrite standard as required. ecm.apply_system_ecm(model: model, ecm_system_name: ecm_system_name, template_standard: self, ecm_system_zones_map_option: ecm_system_zones_map_option) # -------- Performace, Efficiencies, Controls and Sensors ------------ # # Set code standard equipment characteristics. sql_db_vars_map = apply_standard_efficiencies(model: model, sizing_run_dir: sizing_run_dir, necb_reference_hp: self.fuel_type_set.necb_reference_hp) # Apply System ecm.apply_system_efficiencies_ecm(model: model, ecm_system_name: ecm_system_name, template_standard: self) # Apply ECM ERV charecteristics as required. Part 2 of above ECM. ecm.apply_erv_ecm_efficiency(model: model, erv_package: erv_package) # Apply DCV as required model_enable_demand_controlled_ventilation(model, dcv_type) # Apply Boiler Efficiency ecm.modify_boiler_efficiency(model: model, boiler_eff: boiler_eff) # Apply Furnace Efficiency ecm.modify_furnace_efficiency(model: model, furnace_eff: furnace_eff) # Apply Unitary curves ecm.modify_unitary_cop(model: model, unitary_cop: unitary_cop, sizing_done: false, sql_db_vars_map: sql_db_vars_map) # Apply SHW Efficiency ecm.modify_shw_efficiency(model: model, shw_eff: shw_eff) # Apply daylight controls. model_add_daylighting_controls(model: model, daylighting_type: daylighting_type) # Apply Chiller efficiency ecm.modify_chiller_efficiency(model: model, chiller_type: chiller_type) # Apply airloop economizer ecm.add_airloop_economizer(model: model, airloop_economizer_type: airloop_economizer_type) # Perform a second sizing run if needed if (!unitary_cop.nil? && unitary_cop != 'NECB_Default') || !model.getPlantLoops.empty? if model_run_sizing_run(model, "#{sizing_run_dir}/SR2") == false raise('sizing run 2 failed!') end end # apply unitary cop ecm.modify_unitary_cop(model: model, unitary_cop: unitary_cop, sizing_done: true, sql_db_vars_map: sql_db_vars_map) # set capacities of district heating and cooling equipment for ground-source heat pump ecm district_heat = false if model.version < OpenStudio::VersionString.new('3.7.0') district_heat = !model.getDistrictHeatings.empty? else district_heat = !model.getDistrictHeatingWaters.empty? end ecm.set_ghx_loop_district_cap(model) if (district_heat && !model.getDistrictCoolings.empty?) # -------Pump sizing required by some vintages---------------- # Apply Pump power as required. apply_loop_pump_power(model: model, sizing_run_dir: sizing_run_dir) # -------Natural ventilation---------------- # Apply natural ventilation using simplified method. if nv_type == 'add_nv' ecm.apply_nv(model: model, nv_type: nv_type, nv_opening_fraction: nv_opening_fraction, nv_temp_out_min: nv_temp_out_min, nv_delta_temp_in_out: nv_delta_temp_in_out) end # -------Ground-mounted PV panels---------------- # Apply ground-mounted PV panels as required. if pv_ground_type == 'add_pv_ground' ecm.apply_pv_ground(model: model, pv_ground_type: pv_ground_type, pv_ground_total_area_pv_panels_m2: pv_ground_total_area_pv_panels_m2, pv_ground_tilt_angle: pv_ground_tilt_angle, pv_ground_azimuth_angle: pv_ground_azimuth_angle, pv_ground_module_description: pv_ground_module_description) end # Rename air loop and plant loop nodes to accommodate coming OpenStudio version rename_air_loop_nodes(model) rename_plant_loop_nodes(model) end |
#apply_thermal_bridging(model: nil, tbd_option: 'none', tbd_interpolate: false, wall_U: nil, floor_U: nil, roof_U: nil) ⇒ Boolean
(Optionally) uprates, then derates, envelope surface constructions due to MAJOR thermal bridges (e.g. roof parapets, corners, fenestration perimeters). See lib/openstudio-standards/btap/bridging.rb, which relies on the Thermal Bridging & Derating (TBD) gem.
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1018 def apply_thermal_bridging(model: nil, tbd_option: 'none', tbd_interpolate: false, wall_U: nil, floor_U: nil, roof_U: nil) return false unless model.is_a?(OpenStudio::Model::Model) return false unless tbd_option.respond_to?(:to_s) tbd_option = tbd_option.to_s # 4x options: # - 'none' (TBD is ignored) # - derate using 'bad' PSI factors (BTAP-costed) # - derate using 'good' PSI factors (BTAP-costed) # - 'uprate' (then derate), i.e. iterative process (BTAP-costed) ok = tbd_option == 'bad' || tbd_option == 'good' || tbd_option == 'uprate' return true if tbd_option == 'none' return false unless ok argh = {} # BTAP/TBD arguments ok = tbd_interpolate == true || tbd_interpolate == false argh[:interpolate] = tbd_interpolate if ok argh[:interpolate] = false unless ok argh[:walls ] = { uo: wall_U } argh[:floors] = { uo: floor_U } argh[:roofs ] = { uo: roof_U } if tbd_option == 'uprate' argh[:walls ][:ut] = wall_U argh[:floors ][:ut] = floor_U argh[:roofs ][:ut] = roof_U elsif tbd_option == 'good' argh[:quality] = :good end # default == :bad @tbd = BTAP::Bridging.new(model, argh) true end |
#apply_weather_data(model:, epw_file:, custom_weather_folder: nil) ⇒ Object
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 691 def apply_weather_data(model:, epw_file:, custom_weather_folder: nil) # Create full path to weather file weather_files = File.absolute_path(File.join(__FILE__, '..', '..', '..', '..', '..' , '..', "data/weather")) weather_file = File.join(weather_files, epw_file) # Check if the weather file exists. If it does continue as normal, otherwise try to dowload it from the # canmet-energy/btap_weather repository unless File.exist?(weather_file) # Check if btap_batch transferred the weather file weather_transfer = check_datapoint_weather_folder(epw_file: epw_file, weather_folder: weather_files, custom_weather_folder: custom_weather_folder) # If btap_batch didn't transfer the weather file, download it. get_weather_file_from_repo(epw_file: epw_file) unless weather_transfer end # Fix EMS references. Temporary workaround for OS issue #2598 model_temp_fix_ems_references(model) model.getThermostatSetpointDualSetpoints(&:remove) model.getYearDescription.setDayofWeekforStartDay('Sunday') weather_file_path = OpenstudioStandards::Weather.get_standards_weather_file_path(epw_file) OpenstudioStandards::Weather.model_set_building_location(model, weather_file_path: weather_file_path) end |
#are_space_loads_similar?(space_1:, space_2:, surface_percent_difference_tolerance: 0.01, angular_percent_difference_tolerance: 0.001, heating_load_percent_difference_tolerance: 15.0) ⇒ Boolean
This method will determine if the loads on a space are similar. (Exposure, space type, space loads, and schedules, etc)
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 543 def are_space_loads_similar?(space_1:, space_2:, surface_percent_difference_tolerance: 0.01, angular_percent_difference_tolerance: 0.001, heating_load_percent_difference_tolerance: 15.0) # Do they have the same space type? return false unless space_1.multiplier == space_2.multiplier # Ensure that they both have defined spacetypes return false if space_1.spaceType.empty? return false if space_2.spaceType.empty? # ensure that they have the same spacetype. return false unless space_1.spaceType.get == space_2.spaceType.get # Perform surface comparision. If ranges are within percent_difference_tolerance.. they can be considered the same. space_1_floor_area = space_1.floorArea space_2_floor_area = space_2.floorArea space_1_surface_report = space_surface_report(space_1) space_2_surface_report = space_surface_report(space_2) # Spaces should have the same number of surface orientations. return false unless space_1_surface_report.size == space_2_surface_report.size # spaces should have similar loads return false unless percentage_difference(stored_space_heating_load(space_1), stored_space_heating_load(space_2)) <= heating_load_percent_difference_tolerance # Each surface should match space_1_surface_report.each do |space_1_surface| surface_match = space_2_surface_report.detect do |space_2_surface| space_1_surface[:surface_type] == space_2_surface[:surface_type] && space_1_surface[:boundary_condition] == space_2_surface[:boundary_condition] && percentage_difference(space_1_surface[:tilt], space_2_surface[:tilt]) <= angular_percent_difference_tolerance && percentage_difference(space_1_surface[:azimuth], space_2_surface[:azimuth]) <= angular_percent_difference_tolerance && percentage_difference(space_1_surface[:surface_area_to_floor_ratio], space_2_surface[:surface_area_to_floor_ratio]) <= surface_percent_difference_tolerance && percentage_difference(space_1_surface[:glazed_subsurface_area_to_floor_ratio], space_2_surface[:glazed_subsurface_area_to_floor_ratio]) <= surface_percent_difference_tolerance && percentage_difference(space_1_surface[:opaque_subsurface_area_to_floor_ratio], space_2_surface[:opaque_subsurface_area_to_floor_ratio]) <= surface_percent_difference_tolerance end return false if surface_match.nil? end return true end |
#are_zone_loads_similar?(zone_1:, zone_2:) ⇒ Boolean
This method will determine if the loads on a zone are similar. (Exposure, space type, space loads, and schedules, etc)
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 526 def are_zone_loads_similar?(zone_1:, zone_2:) # make sure they have the same number of spaces. truthes = [] return false if zone_1.spaces.size != zone_2.spaces.size zone_1.spaces.each do |space_1| zone_2.spaces.each do |space_2| if are_space_loads_similar?(space_1: space_1, space_2: space_2) truthes << true end end end # truthes sizes should be the same as the # of spaces if all spaces are similar. return truthes.size == zone_1.spaces.size end |
#assign_base_sys_name(airloop, sys_abbr:, sys_oa:, sys_name_pars:) ⇒ Object
Method to set the base system name based on the following syntax: |sys_abbr|sys_oa|shr>?|sc>?|sh>?|ssf>?|zh>?|zc>?|srf>?| “sys_abbr” designates the NECB system type (“sys_1, sys_2, … sys_6”) “sys_oa”: “mixed” or “doas” “sys_name_pars” is a hash for the remaining system name parts for heat recovery, heating, cooling, supply fan, zone heating, zone cooling, and return fan
2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 2194 def assign_base_sys_name(airloop, sys_abbr:, sys_oa:, sys_name_pars:) sys_name = "#{sys_abbr}|#{sys_oa}|" sys_name_pars.each do |key, value| case key.downcase when 'sys_hr' case value.downcase when 'none' sys_name += 'shr>none' end when 'sys_htg' case value.downcase when 'none' sys_name += 'sh>none' when 'electric' sys_name += 'sh>c-e' when 'hot water' sys_name += 'sh>c-hw' when 'gas' sys_name += 'sh>c-g' when 'dx' sys_name += 'sh>ashp' when 'ccashp' sys_name += 'sh>ccashp' when 'ashp' sys_name += 'sh>ashp' end when 'sys_clg' case value.downcase when 'none' sys_name += 'sc>none' when 'chilled water' sys_name += 'sc>c-chw' when 'dx' if sys_name_pars['sys_htg'] == 'dx' sys_name += 'sc>ashp' else sys_name += 'sc>dx' end when 'ccashp' sys_name += 'sc>ccashp' when 'ashp' sys_name += 'sc>ashp' end when 'sys_sf' case value.downcase when 'none' sys_name += 'ssf>none' when 'cv' sys_name += 'ssf>cv' when 'vv' sys_name += 'ssf>vv' end when 'zone_htg' case value.downcase when 'none' sys_name += 'zh>none' when 'electric' sys_name += 'zh>b-e' when 'hot water' sys_name += 'zh>b-hw' when 'tpfc' sys_name += 'zh>fpfc' when 'fpfc' sys_name += 'zh>tpfc' when 'pthp' sys_name += 'zh>pthp' end when 'zone_clg' case value.downcase when 'none' sys_name += 'zc>none' when 'tpfc' sys_name += 'zc>tpfc' when 'fpfc' sys_name += 'zc>fpfc' when 'ptac' sys_name += 'zc>ptac' when 'pthp' sys_name += 'zc>pthp' end when 'sys_rf' case value.downcase when 'none' sys_name += 'srf>none' when 'cv' sys_name += 'srf>cv' when 'vv' sys_name += 'srf>vv' end end sys_name += '|' end airloop.setName(sys_name) end |
#assign_contruction_to_adiabatic_surfaces(model) ⇒ Object
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 539 def assign_contruction_to_adiabatic_surfaces(model) cp02_carpet_pad = OpenStudio::Model::MasslessOpaqueMaterial.new(model) cp02_carpet_pad.setName('CP02 CARPET PAD') cp02_carpet_pad.setRoughness('VeryRough') cp02_carpet_pad.setThermalResistance(0.21648) cp02_carpet_pad.setThermalAbsorptance(0.9) cp02_carpet_pad.setSolarAbsorptance(0.7) cp02_carpet_pad.setVisibleAbsorptance(0.8) normalweight_concrete_floor = OpenStudio::Model::StandardOpaqueMaterial.new(model) normalweight_concrete_floor.setName('100mm Normalweight concrete floor') normalweight_concrete_floor.setRoughness('MediumSmooth') normalweight_concrete_floor.setThickness(0.1016) normalweight_concrete_floor.setConductivity(2.31) normalweight_concrete_floor.setDensity(2322) normalweight_concrete_floor.setSpecificHeat(832) nonres_floor_insulation = OpenStudio::Model::MasslessOpaqueMaterial.new(model) nonres_floor_insulation.setName('Nonres_Floor_Insulation') nonres_floor_insulation.setRoughness('MediumSmooth') nonres_floor_insulation.setThermalResistance(2.88291975297193) nonres_floor_insulation.setThermalAbsorptance(0.9) nonres_floor_insulation.setSolarAbsorptance(0.7) nonres_floor_insulation.setVisibleAbsorptance(0.7) floor_adiabatic_construction = OpenStudio::Model::Construction.new(model) floor_adiabatic_construction.setName('Floor Adiabatic construction') floor_layers = OpenStudio::Model::MaterialVector.new floor_layers << cp02_carpet_pad floor_layers << normalweight_concrete_floor floor_layers << nonres_floor_insulation floor_adiabatic_construction.setLayers(floor_layers) g01_13mm_gypsum_board = OpenStudio::Model::StandardOpaqueMaterial.new(model) g01_13mm_gypsum_board.setName('G01 13mm gypsum board') g01_13mm_gypsum_board.setRoughness('Smooth') g01_13mm_gypsum_board.setThickness(0.0127) g01_13mm_gypsum_board.setConductivity(0.1600) g01_13mm_gypsum_board.setDensity(800) g01_13mm_gypsum_board.setSpecificHeat(1090) g01_13mm_gypsum_board.setThermalAbsorptance(0.9) g01_13mm_gypsum_board.setSolarAbsorptance(0.7) g01_13mm_gypsum_board.setVisibleAbsorptance(0.5) wall_adiabatic_construction = OpenStudio::Model::Construction.new(model) wall_adiabatic_construction.setName('Wall Adiabatic construction') wall_layers = OpenStudio::Model::MaterialVector.new wall_layers << g01_13mm_gypsum_board wall_layers << g01_13mm_gypsum_board wall_adiabatic_construction.setLayers(wall_layers) m10_200mm_concrete_block_basement_wall = OpenStudio::Model::StandardOpaqueMaterial.new(model) m10_200mm_concrete_block_basement_wall.setName('M10 200mm concrete block basement wall') m10_200mm_concrete_block_basement_wall.setRoughness('MediumRough') m10_200mm_concrete_block_basement_wall.setThickness(0.2032) m10_200mm_concrete_block_basement_wall.setConductivity(1.326) m10_200mm_concrete_block_basement_wall.setDensity(1842) m10_200mm_concrete_block_basement_wall.setSpecificHeat(912) basement_wall_construction = OpenStudio::Model::Construction.new(model) basement_wall_construction.setName('Basement Wall construction') basement_wall_layers = OpenStudio::Model::MaterialVector.new basement_wall_layers << m10_200mm_concrete_block_basement_wall basement_wall_construction.setLayers(basement_wall_layers) basement_floor_construction = OpenStudio::Model::Construction.new(model) basement_floor_construction.setName('Basement Floor construction') basement_floor_layers = OpenStudio::Model::MaterialVector.new basement_floor_layers << m10_200mm_concrete_block_basement_wall basement_floor_layers << cp02_carpet_pad basement_floor_construction.setLayers(basement_floor_layers) model.getSurfaces.sort.each do |surface| if surface.outsideBoundaryCondition.to_s == 'Adiabatic' if surface.surfaceType.to_s == 'Wall' surface.setConstruction(wall_adiabatic_construction) else surface.setConstruction(floor_adiabatic_construction) end elsif surface.outsideBoundaryCondition.to_s == 'OtherSideCoefficients' # Ground if surface.surfaceType.to_s == 'Wall' surface.setOutsideBoundaryCondition('Ground') surface.setConstruction(basement_wall_construction) else surface.setOutsideBoundaryCondition('Ground') surface.setConstruction(basement_floor_construction) end end end end |
#auto_size_shw_capacity(model:, u: 0.45, height_to_radius: 2, shw_scale: 'NECB_Default') ⇒ Object
This calculates the volume and capacity of one mixed tank that is assumed to service all shw in the building u is the tank insulation in W/(m^2*K), height_to_radius is the ratio of tank radius to tank height and is dimensionless
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb', line 218 def auto_size_shw_capacity(model:, u: 0.45, height_to_radius: 2, shw_scale: 'NECB_Default') peak_flow_rate = 0 shw_space_types = [] space_peak_flows = [] water_use = 0 weekly_peak_flow = { 'Default|Wkdy' => Array.new(24, 0), 'Sat' => Array.new(24, 0), 'Sun|Hol' => Array.new(24, 0) } peak_day_sched = nil peak_hour_sched = 0 peak_flow_sched = 0 next_hour_day = nil next_hour_hour = 0 next_hour_flow = 0 total_peak_flow_rate = 0 shw_spaces = [] shw_sched_names = [] ##### Modify shw_scale if required if shw_scale.instance_of?(String) shw_scale = shw_scale.strip # remove leading or trailing whitespace in case users add them in shw_scale end if shw_scale == 'NECB_Default' or shw_scale.nil? or shw_scale == 'none' or shw_scale == false shw_scale = 1.0 elsif shw_scale.instance_of?(String) # Convert a string to a float shw_scale = shw_scale.to_f end # First go through all the spaces in the building and determine and determine their shw requirements space_types_table = @standards_data['space_types'] model.getSpaces.sort.each do |space| space_peak_flow = 0 data = nil space_type_name = space.spaceType.get.standardsSpaceType.get.to_s tank_temperature = 60 # find the specific space_type properties from standard.json space_types_table.each do |space_type| if (space_type['building_type'] + ' ' + space_type_name) == (space_type['building_type'] + ' ' + space_type['space_type']) break if space_type['necb_hvac_system_selection_type'] == '- undefined -' # If there is no service hot water load.. Don't bother adding anything. break if (space_type['service_water_heating_peak_flow_per_area'].to_f == 0.0 && space_type['service_water_heating_peak_flow_rate'].to_f == 0.0) || space_type['service_water_heating_schedule'].nil? # If there is a service hot water load collect the space information data = space_type break end end # If there is no service hot water load.. Don't bother adding anything. # Skip space types with no data next if data.nil? space_area = OpenStudio.convert(space.floorArea, 'm^2', 'ft^2').get # ft2 # Calculate the peak shw flow rate for the space. Peak flow from JSON file is in US Gal/hr/ft^2 # space_peak_flow_ind is the peak flow rate for the space while space_peak_flow is the peak flow # rate for the space multiplied by the space (ultimately thermal zone) multiplier. space_peak_flow is used for # much of the rest as it reflects how much water is used. space_peak_flow_ind is recorded and used later on # when defining water use equipment. When when water use equipment is assigned to spaces then the water use # by the equipment is multiplied by the space multiplier. Note that there is a separate water use equipment # multiplier as well which is different than the space (ultimately thermal zone) multiplier. space_peak_flow_ind = data['service_water_heating_peak_flow_per_area'].to_f * space_area * shw_scale space_peak_flow = space_peak_flow_ind * space.multiplier # space_peak_flows << space_peak_flow # Add the peak shw flow rate for the space to the total for the entire building total_peak_flow_rate += space_peak_flow # Get the tank temperature for the space. This should always be 60 C but I added this part in case something changes in the future. if data['service_water_heating_target_temperature'].nil? || (data['service_water_heating_target_temperature'] <= 16) tank_temperature = 60 else tank_temperature = data['service_water_heating_target_temperature'] end # Get the shw schedule # shw_sched_names << data['service_water_heating_schedule'] # 'shw_peakflow_ind_SI' is the shw peak flow rate of the individual space (without the space multiplier) space_info = { 'shw_spaces' => space, 'shw_peakflow_SI' => OpenStudio.convert(space_peak_flow, 'gal/hr', 'm^3/s').get, 'shw_peakflow_ind_SI' => OpenStudio.convert(space_peak_flow_ind, 'gal/hr', 'm^3/s').get, 'shw_temp_SI' => tank_temperature, 'shw_sched' => data['service_water_heating_schedule'] } shw_spaces << space_info # The following gets the water use schedule for space and applies it to the peak flow rate for the space. This # creates an array containing the hourly shw consumption for the space for each day type (Weekday/default, Saturday, # Sunday/Holiday). The hourly shw consumption for each space is added to the array ultimately producing an array # containing the hourly shw demand for the entire building. This is used to determine the peak shw demand # hour and rate for the building. This is different than the overall peak shw demand for the building in that it # takes into account the shw schedule. The peak shw demand hour and rate should always be less than the overall peak # shw demand. # Cycle through the hash accumulating the shw rates for each day type. weekly_peak_flow.sort.each do |day_peak_sched| day_sched = [] # Create the search criteria and retrieve the schedule for the current space and current day type. search_criteria = { 'template' => template, 'name' => data['service_water_heating_schedule'], 'day_types' => day_peak_sched[0] } schedules_table = @standards_data['schedules'] day_sched = model_find_object(schedules_table, search_criteria) # Make sure the schedule is not empty and contains 24 hours. if day_sched.empty? || day_sched['values'].size != 24 OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.model_add_swh', "The water use schedule called #{data['service_water_heating_schedule']} for #{space_type_name} is corrupted or could not be found. Please check that the schedules.json file is available and that the schedule names are spelled correctly") return false end # For each hour of the current day type multiply the shw schedule fractional multiplier (representing the fraction of the total shw # rate used in that hour) times the overall peak shw rate for the current space. Add the resulting values to the # array tracking hourly shw demand for the building. Also, determine what the highest hourly demand is for the # building. day_peak_sched[1].sort.each_with_index do |hour_flow, hour_index| weekly_peak_flow[day_peak_sched[0]][hour_index] += day_sched['values'][hour_index] * space_peak_flow if weekly_peak_flow[day_peak_sched[0]][hour_index] > peak_flow_sched peak_flow_sched = weekly_peak_flow[day_peak_sched[0]][hour_index] end end end end if shw_spaces.empty? space_info = { 'shw_spaces' => nil, 'shw_peakflow_SI' => 0, 'shw_peakflow_ind_SI' => 0, 'shw_temp_SI' => 60, 'shw_sched' => [] } shw_spaces << space_info tank_param = { 'tank_volume_SI' => 0, 'tank_capacity_SI' => 0, 'max_temp_SI' => 60, 'loop_peak_flow_rate_SI' => 0, 'parasitic_loss' => 0, 'spaces_w_dhw' => shw_spaces } return tank_param end next_day_test = nil next_hour_test = 0 # The following loop goes through each hour in the array tracking hourly shw demand to find which hours contain the # peak hourly shw demand (this is in case the peak hourly shw demand occurs more than once). It then determines what # the hourly shw demand is for the following hour. It is meant to determine, of the peak hourly shw times, which has # the highest shw demand the following hour. This is used to determine shw capacity and volume. weekly_peak_flow.sort.each do |day_peak_sched| day_peak_sched[1].sort.each_with_index do |hour_flow, hour_index| if hour_flow == peak_flow_sched if hour_index == 23 next_hour_test = 0 case day_peak_sched[0] when 'Default|Wkdy' next_day_test = 'Sat' when 'Sat' next_day_test = 'Sun|Hol' when 'Sun|Hol' next_day_test = 'Default|Wkdy' end else next_hour_test = hour_index + 1 next_day_test = day_peak_sched[0] end if next_hour_flow < weekly_peak_flow[next_day_test][next_hour_test] next_hour_flow = weekly_peak_flow[next_day_test][next_hour_test] next_hour_hour = next_hour_test next_hour_day = next_day_test peak_day_sched = day_peak_sched[0] peak_hour_sched = hour_index end end end end # The shw tank is sized so that it can fulfill the hour with the highest shw needs. Since the flow is in US Gal/hr # No conversion is necessary. tank_volume = peak_flow_sched # Interperite the fractional shw schedules as being the fraction of the hour that the maximum shw rate is used and determine # what this fraction is for the entire building. peak_time_fraction = 1 - (peak_flow_sched / total_peak_flow_rate) # Assume the shw tank needs some minimum amount of time to recover (avoids requiring a ridiculously high capacity). # If the recovery time is to short then the tank needs to hold enough water to service the peak shw hour and the one # after. Then give the tank the entire hour to heat up again. Note again that since peak flows are per hour, and # we are only looking at an hour, no conversion is necessary. if peak_time_fraction <= 0.2 tank_volume += next_hour_flow peak_time_fraction = 1 end tank_volume_SI = OpenStudio.convert(tank_volume, 'gal', 'm^3').get # Determine the tank capacity as the heat output required to heat up the entire volume of the tank in time remaining # in the hour after the peak shw draw is stopped (assume water is provided to the building at 15C ). max_temp = -273 shw_spaces.each do |shw_space| if shw_space['shw_temp_SI'] > max_temp max_temp = shw_space['shw_temp_SI'] end end tank_capacity_SI = tank_volume_SI * 1000 * 4180 * (max_temp - 15) / (3600 * peak_time_fraction) tank_radius = (tank_volume_SI / (height_to_radius * Math::PI))**(1.0 / 3) tank_area = 2 * (1 + height_to_radius) * Math::PI * (tank_radius**2) room_temp = OpenStudio.convert(70, 'F', 'C').get parasitic_loss = u * tank_area * (max_temp - room_temp) tank_param = { 'tank_volume_SI' => tank_volume_SI, 'tank_capacity_SI' => tank_capacity_SI, 'max_temp_SI' => max_temp, 'loop_peak_flow_rate_SI' => OpenStudio.convert(total_peak_flow_rate, 'gal/hr', 'm^3/s').get, 'parasitic_loss' => parasitic_loss, 'spaces_w_dhw' => shw_spaces } return tank_param end |
#auto_size_shw_pump_head(model, default: true, pipe_vel: 1.75, kin_visc_SI: 0.000004736, density_SI: 983, pipe_rough_m: 0.0000015) ⇒ Object
Autosize the pump head by calculating the piping longest piping length and deriving required head from that. If default is set to true then it returns a default pump head of 179532 Pa which is based on the OpenStudio 2.4.1 defaults for a constant speed pump. The method first assumes that the tank and pump are located in the space closest to the center of the bottom of the building. It then assumes that water is delivered to the bottom center of every space that has a demand for shw. It calculates the x, y, and z components of the vector between the shw space and the spaces with demand for shw. The distance of the piping run is calculated by adding the x, y, and z components of the vector (rather than the magnitude of the vector). For the purposes of calculating pressure loss along the pipe bends, and other minor losses are accounted by doubling the calculated length of the pipe. The default kinematic viscosity of water is assumed to be that at 60 C (in m^2/s). The default density of water is assumed to be 983 kg/m^3 as per hypertextbook.com/facts/2007/AllenMa.shtml accessed 2018-07-27. The pipe is assumed to be made out of PVC and have a roughness height of 1.5*10^-6 m as per: www.pipeflow.com/pipe-pressure-drop-calculations/pipe-roughness accessed on 2018-07-25. The default maximum velocity is from the table from ‘The Engineering Toolbox’ link: www.engineeringtoolbox.com/flow-velocity-water-pipes-d_385.html Chris Kirney 2018-07-27.
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb', line 444 def auto_size_shw_pump_head(model, default: true, pipe_vel: 1.75, kin_visc_SI: 0.000004736, density_SI: 983, pipe_rough_m: 0.0000015) return 179532 if default mech_room, cond_spaces = find_mech_room(model) return 179532 if mech_room.nil? || cond_spaces.nil? space_coord_dists = [] total_peak_flow = 0 hl_Pas = [] # Now go through each space with a shw load and determine the x, y, and z components of a vector from the centroid # of the floor of the space containing the shw_tank and the centroid of the floor of the given space cond_spaces.each do |cond_space| # Find the specific space_type properties from standard.json spaceType_name = cond_space['space'].spaceType.get.nameString sp_type = spaceType_name[15..-1] # Including regular expressions in the following match for cases where extra characters, which do not belong, are # added to either the space type in the model or the space type reference file. sp_type_info = @standards_data['tables']['space_types']['table'].detect do |data| (Regexp.new(data['space_type'].to_s.upcase).match(sp_type.upcase) || Regexp.new(sp_type.upcase).match(data['space_type'].to_s.upcase) || (data['space_type'].to_s.upcase == sp_type.upcase)) && (data['building_type'].to_s == 'Space Function') end # If the space type could not be found let the use know and go on to the next space. if sp_type_info.nil? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.model_add_swh', "The space type called #{sp_type} could not be found. Please check that the schedules.json file is available and that the space types are spelled correctly") next end next if sp_type_info['service_water_heating_peak_flow_per_area'].to_f == 0.0 && sp_type_info['service_water_heating_peak_flow_rate'].to_f == 0.0 || sp_type_info['service_water_heating_schedule'].nil? space_area = OpenStudio.convert(cond_space['space'].floorArea, 'm^2', 'ft^2').get # ft2 # Calculate the peak shw flow rate for the space space_peak_flow = (sp_type_info['service_water_heating_peak_flow_per_area'].to_f * space_area) * cond_space['space'].multiplier space_peak_flow_SI = OpenStudio.convert(space_peak_flow, 'gal/hr', 'm^3/s').get # Determine the total shw peak flow for the building. total_peak_flow += space_peak_flow_SI # I use centroid for the floor as the location of the source or point of use for the shw system. if space_peak_flow_SI > 0 space_coord_dist = [] cond_space['space_centroid'].each_with_index do |dist, coord| space_coord_dist << (dist - mech_room['space_centroid'][coord]).abs end space_coord_dists << space_coord_dist end end # The piping run length from the shw tank to a given space is assumed to be the sum of the coordinates of the vector # described above. The longest piping run becomes the one used for sizing. Note that I double the length of this # piping run below when calculating head loss. space_coord_dists.sort.each do |space_coord_dist| sizing_pipe_length = space_coord_dist[0] + space_coord_dist[1] + space_coord_dist[2] # The shw pump is sized by assuming that the sum of the peak shw volume flow rates for each space has to be fed # through the longest piping run. So for the sizing calculations below, the flow rate is the sum of the peak volume # flow rates for the entire building. The length of the piping run is twice the calculated longest piping run # described above. # Step 1: Calculate the Reynold's number. Note kinematic viscosity is set for water at 60 C and pipe diameter is # set to 3/4". These can be changed by passing different values to the method. I got the kinematic # viscosity from www.engineeringtoolbox.com/water-dynamic-kinematic-viscosity-d_596.html accessed 2018-07-05. # I got the pipe roughness from www.pipeflow.com/pipe-pressure-drop-calculations/pipe-roughness accessed on # 2018-07-25. I assume 3/4" pipe because that is what Mike Lubun says is used in most cases (unless it # it is for process water but we assume that is not the case). # Determine the bulk velocity of the shw through the pipe. # find pipe diameter for the peak flow pipe_dia_m = (4.0 * total_peak_flow / (Math::PI * pipe_vel))**0.5 # Get the Reynolds number. re_pipe = (pipe_vel * pipe_dia_m) / kin_visc_SI # Step 2: Figure out what the Darcy-Weisbach friction factor is. relative_rough = pipe_rough_m / pipe_dia_m f = friction_factor(re_pipe, relative_rough) # Step 3: Calculate the major head loss # Note that you may be thinking that I forgot to divide the last term by 2 in the equation below. I didn't. # I multiplied the piping length by 2 because I did not take pipe bends etc. into account and I calculate the # maximum piping run in a really approximate way. Thus I multiply the piping run by 2. If you can think # of something better please replace what I have. # hl is taken from https://neutrium.net/fluid_flow/pressure-loss-in-pipe accessed 2018-07-26 (I added the height # component). Note that while I allow all of the other physical values to be set I assume that you are building on # earth hence g is hard coded to 9.81 m/s^2. hl_Pa = (f * (sizing_pipe_length / pipe_dia_m) * (pipe_vel**2) * density_SI) + density_SI * space_coord_dist[2] * 9.81 if hl_Pa < 1 hl_Pa = 1 end hl_Pas << hl_Pa end # If no spaces with shw were found return the default pump head. return 179532 if hl_Pas.empty? return hl_Pas.max_by { |hl| hl } end |
#auto_system_all_other_spaces(baseboard_type:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', boiler_fueltype:, chiller_type:, fan_type:, heating_coil_type_sys3:, heating_coil_type_sys4:, heating_coil_type_sys6:, hw_loop:, mau_cooling_type:, mau_heating_coil_type:, mau_type:, model:) ⇒ Object
This method will deal with all non wet, non-wild, and non-dwelling units thermal zones.
984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 984 def auto_system_all_other_spaces(baseboard_type:, necb_reference_hp:false, necb_reference_hp_supp_fuel:'DefaultFuel', boiler_fueltype:, chiller_type:, fan_type:, heating_coil_type_sys3:, heating_coil_type_sys4:, heating_coil_type_sys6:, hw_loop:, mau_cooling_type:, mau_heating_coil_type:, mau_type:, model:) zones = [] other_spaces = model.getSpaces.select do |space| !is_a_necb_dwelling_unit?(space) && !is_an_necb_wildcard_space?(space) && !is_an_necb_storage_space?(space) end other_spaces.each do |space| zones << space.thermalZone.get end zones.uniq! # since dwelling units are all zoned 1:1 to space:zone we simply add the zone to the appropriate btap system. create_necb_system(baseboard_type: baseboard_type, boiler_fueltype: boiler_fueltype, chiller_type: chiller_type, fan_type: fan_type, heating_coil_type_sys3: heating_coil_type_sys3, heating_coil_type_sys4: heating_coil_type_sys4, heating_coil_type_sys6: heating_coil_type_sys6, hw_loop: @hw_loop, mau_cooling_type: mau_cooling_type, mau_heating_coil_type: mau_heating_coil_type, mau_type: mau_type, model: model, zones: zones, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel) end |
#auto_system_dwelling_units(baseboard_type:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', boiler_fueltype:, chiller_type:, fan_type:, heating_coil_type_sys3:, heating_coil_type_sys4:, heating_coil_type_sys6:, hw_loop:, mau_cooling_type:, mau_heating_coil_type:, mau_type:, model:, baseline_system_zones_map_option:) ⇒ Object
This method will ensure that all dwelling units are assigned to a system 1 or 3. There is an option to have a shared AHU or not.
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 1031 def auto_system_dwelling_units(baseboard_type:, necb_reference_hp:false, necb_reference_hp_supp_fuel:'DefaultFuel', boiler_fueltype:, chiller_type:, fan_type:, heating_coil_type_sys3:, heating_coil_type_sys4:, heating_coil_type_sys6:, hw_loop:, mau_cooling_type:, mau_heating_coil_type:, mau_type:, model:, baseline_system_zones_map_option:) system_zones_hash = {} # Determine if dwelling units have a shared AHU. If user entered building stories > 4 then set to true. if baseline_system_zones_map_option == 'one_sys_per_dwelling_unit' dwelling_shared_ahu = false elsif baseline_system_zones_map_option == 'one_sys_per_bldg' || baseline_system_zones_map_option == 'NECB_Default' || baseline_system_zones_map_option == 'none' || baseline_system_zones_map_option == nil || necb_reference_hp dwelling_shared_ahu = true end # store dwelling zones into array zones = [] model.getSpaces.select { |space| is_a_necb_dwelling_unit?(space) }.each do |space| zones << space.thermalZone.get end zones.uniq! # sort system 1 or 3 used for each dwelling unit as per T8.4.4.8.A NECB 2011-17 zones.each do |zone| system_zones_hash[get_necb_thermal_zone_system_selection(zone)] = [] if system_zones_hash[get_necb_thermal_zone_system_selection(zone)].nil? system_zones_hash[get_necb_thermal_zone_system_selection(zone)] << zone end # go through each system and zones pairs to system_zones_hash.each_pair do |system, sys_zones| case system when 1 if dwelling_shared_ahu add_sys1_unitary_ac_baseboard_heating(model: model, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel, zones: sys_zones, mau_type: mau_type, mau_heating_coil_type: mau_heating_coil_type, baseboard_type: baseboard_type, hw_loop: @hw_loop, multispeed: false) else # Create a separate air loop for each unit. sys_zones.each do |zone| add_sys1_unitary_ac_baseboard_heating(model: model, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel, zones: [zone], mau_type: mau_type, mau_heating_coil_type: mau_heating_coil_type, baseboard_type: baseboard_type, hw_loop: @hw_loop, multispeed: false) end end when 3 if dwelling_shared_ahu add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating(model: model, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel, zones: sys_zones, heating_coil_type: heating_coil_type_sys3, baseboard_type: baseboard_type, hw_loop: @hw_loop, multispeed: false) else # Create a separate air loop for each unit. sys_zones.each do |zone| add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating(model: model, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel, zones: [zone], heating_coil_type: heating_coil_type_sys3, baseboard_type: baseboard_type, hw_loop: @hw_loop, multispeed: false) end end end end end |
#auto_system_storage_spaces(baseboard_type:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', boiler_fueltype:, heating_coil_type_sys4:, model:) ⇒ Object
All wet spaces will be on their own system 4 AHU.
1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 1153 def auto_system_storage_spaces(baseboard_type:, necb_reference_hp:false, necb_reference_hp_supp_fuel:'DefaultFuel', boiler_fueltype:, heating_coil_type_sys4:, model:) # Determine what zones are storage zones. tz = [] storage_spaces = model.getSpaces.select { |space| is_an_necb_storage_space?(space) } storage_spaces.each { |space| tz << space.thermalZone.get } tz.uniq! return if tz.empty? # create a system 4 for the zones. add_sys4_single_zone_make_up_air_unit_with_baseboard_heating(model: model, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel, zones: tz, heating_coil_type: heating_coil_type_sys4, baseboard_type: baseboard_type, hw_loop: @hw_loop) # add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating(model: model, # zones: tz, # heating_coil_type: heating_coil_type_sys4, # baseboard_type: baseboard_type, # hw_loop: @hw_loop, # multispeed: true) end |
#auto_system_wet_spaces(baseboard_type:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', boiler_fueltype:, heating_coil_type_sys4:, model:) ⇒ Object
All wet spaces will be on their own system 4 AHU.
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 1123 def auto_system_wet_spaces(baseboard_type:, necb_reference_hp:false, necb_reference_hp_supp_fuel:'DefaultFuel', boiler_fueltype:, heating_coil_type_sys4:, model:) # Determine what zones are wet zones. wet_tz = [] wet_spaces = model.getSpaces.select { |space| is_an_necb_wet_space?(space) } wet_spaces.each { |space| wet_tz << space.thermalZone.get } wet_tz.uniq! # create a system 4 for the wet zones. return if wet_tz.empty? add_sys4_single_zone_make_up_air_unit_with_baseboard_heating(model: model, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel, zones: wet_tz, heating_coil_type: heating_coil_type_sys4, baseboard_type: baseboard_type, hw_loop: @hw_loop) # add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating(model: model, # zones: wet_tz, # heating_coil_type: heating_coil_type_sys4, # baseboard_type: baseboard_type, # hw_loop: @hw_loop, # multispeed: false) end |
#auto_system_wild_spaces(baseboard_type:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'Defaultfuel', heating_coil_type_sys4:, model:) ⇒ Object
All wild spaces will be on a single system 4 ahu with the largests heating load zone being the control zone.
1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 1184 def auto_system_wild_spaces(baseboard_type:, necb_reference_hp:false, necb_reference_hp_supp_fuel:'Defaultfuel', heating_coil_type_sys4:, model:) zones = [] wild_spaces = model.getSpaces.select { |space| !is_an_necb_wet_space?(space) && is_an_necb_wildcard_space?(space) } wild_spaces.each { |space| zones << space.thermalZone.get } zones.uniq! return if zones.empty? # create a system 4 for the wild zones. add_sys4_single_zone_make_up_air_unit_with_baseboard_heating(model: model, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel, zones: zones, heating_coil_type: heating_coil_type_sys4, baseboard_type: baseboard_type, hw_loop: @hw_loop) # add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating(model: model, # zones: zones, # heating_coil_type: heating_coil_type_sys4, # baseboard_type: baseboard_type, # hw_loop: @hw_loop, # multispeed: true) end |
#auto_zone_all_other_spaces(model) ⇒ Object
This method will find all the spaces that are not wet, wild or dwelling units and zone them. It will try to determine if the spaces are similar based on exposure and load and blend those spaces into the same zone. It will not merge spaces from different floors, since this will impact Chris Kirneys costing algorithms.
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 340 def auto_zone_all_other_spaces(model) other_tz_array = [] # iterate through all non wildcard spaces. model.getSpaces.select { |space| !is_a_necb_dwelling_unit?(space) && !is_an_necb_wildcard_space?(space) }.each do |space| # skip if already assigned to a thermal zone. next unless space.thermalZone.empty? # create new zone for this space based on the space name. zone = OpenStudio::Model::ThermalZone.new(model) tz_name = "ALL_ST=#{space.spaceType.get.standardsSpaceType.get}_FL=#{space.buildingStory.get.name}_SCH=#{determine_dominant_schedule([space])}" zone.setName(tz_name) # sets space mulitplier unless it is nil or 1. unless space_multiplier_map[space.name.to_s].nil? || (space_multiplier_map[space.name.to_s] == 1) zone.setMultiplier(space_multiplier_map[space.name.to_s]) end # Assign space to the new zone. space.setThermalZone(zone) # Add a thermostat space_type_name = space.spaceType.get.name.get thermostat_name = space_type_name + ' Thermostat' thermostat = model.getThermostatSetpointDualSetpointByName(thermostat_name) if thermostat.empty? # The thermostat name for the spacetype should exist. OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "Thermostat #{thermostat_name} not found for space name: #{space.name}") else thermostat_clone = thermostat.get.clone(model).to_ThermostatSetpointDualSetpoint.get zone.setThermostatSetpointDualSetpoint(thermostat_clone) # Set Ideal loads to thermal zone for sizing for NECB needs. We need this for sizing. ideal_loads = OpenStudio::Model::ZoneHVACIdealLoadsAirSystem.new(model) ideal_loads.addToThermalZone(zone) end # Go through other spaces and if you find something with similar loads on the same floor, add it to the zone. model.getSpaces.select { |curr_space| !is_a_necb_dwelling_unit?(curr_space) && !is_an_necb_wildcard_space?(curr_space) }.each do |space_target| if space_target.thermalZone.empty? if are_space_loads_similar?(space_1: space, space_2: space_target) && space.buildingStory.get == space_target.buildingStory.get # added since chris needs zones to not span floors for costing. space_target.setThermalZone(zone) end end end other_tz_array << zone end return other_tz_array end |
#auto_zone_dwelling_units(model) ⇒ Object
Dwelling unit spaces need to have their own HVAC system. Thankfully NECB defines what spacetypes are considering dwelling units and have been defined as spaces that are openstudio-standards/standards/necb/NECB2011/data/necb_hvac_system_selection_type.json as spaces that are Residential/Accomodation and Sleeping area’ this is determine by the is_a_necb_dwelling_unit? method. The thermostat is set by the space-type schedule. This will return an array of TZ.
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 255 def auto_zone_dwelling_units(model) dwelling_tz_array = [] # ----Dwelling units----------- will always have their own system per unit, so they should have their own thermal zone. model.getSpaces.select { |space| is_a_necb_dwelling_unit?(space) }.each do |space| zone = OpenStudio::Model::ThermalZone.new(model) zone.setName("DU_BT=#{space.spaceType.get.standardsBuildingType.get}_ST=#{space.spaceType.get.standardsSpaceType.get}_FL=#{space.buildingStory.get.name}_SCH#{determine_dominant_schedule([space])}") unless space_multiplier_map[space.name.to_s].nil? || (space_multiplier_map[space.name.to_s] == 1) zone.setMultiplier(space_multiplier_map[space.name.to_s]) end space.setThermalZone(zone) # Add a thermostat based on the space type. space_type_name = space.spaceType.get.name.get thermostat_name = space_type_name + ' Thermostat' thermostat = model.getThermostatSetpointDualSetpointByName(thermostat_name) if thermostat.empty? # The thermostat name for the spacetype should exist. OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "Thermostat #{thermostat_name} not found for space name: #{space.name}") else thermostat_clone = thermostat.get.clone(model).to_ThermostatSetpointDualSetpoint.get zone.setThermostatSetpointDualSetpoint(thermostat_clone) # Set Ideal loads to thermal zone for sizing for NECB needs. We need this for sizing. OpenStudio::Model::ZoneHVACIdealLoadsAirSystem.new(model).addToThermalZone(zone) end dwelling_tz_array << zone end return dwelling_tz_array end |
#auto_zone_wet_spaces(model:, lights_type: 'NECB_Default', lights_scale: 1.0) ⇒ Object
Something that the code is silent on are smelly humid areas that should not be on the same system as the rest of the
building.. These are the 'wet' spaces and have been defined as locker and washroom areas.. These will be put under
their own single system 4 system. These will be set to the dominant floor schedule.
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 288 def auto_zone_wet_spaces(model:, lights_type: 'NECB_Default', lights_scale: 1.0) wet_zone_array = [] model.getSpaces.select { |space| is_an_necb_wet_space?(space) }.each do |space| # if this space was already assigned to something skip it. next unless space.thermalZone.empty? # get space to dominant schedule dominant_schedule = determine_dominant_schedule(space.model.getSpaces) # create new TZ and set space to the zone. zone = OpenStudio::Model::ThermalZone.new(model) space.setThermalZone(zone) tz_name = "WET_ST=#{space.spaceType.get.standardsSpaceType.get}_FL=#{space.buildingStory.get.name}_SCH#{dominant_schedule}" zone.setName(tz_name) # Set multiplier from the original tz multiplier. unless space_multiplier_map[space.name.to_s].nil? || (space_multiplier_map[space.name.to_s] == 1) zone.setMultiplier(space_multiplier_map[space.name.to_s]) end # this method will determine if the right schedule was used for this wet & wild space if not.. it will reset the space # to use the correct schedule version of the wet and wild space type. adjust_wildcard_spacetype_schedule(space: space, schedule: dominant_schedule, lights_type: lights_type, lights_scale: lights_scale) # Find spacetype thermostat and assign it to the zone. thermostat_name = space.spaceType.get.name.get + ' Thermostat' thermostat = model.getThermostatSetpointDualSetpointByName(thermostat_name) if thermostat.empty? # The thermostat name for the spacetype should exist. OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "Thermostat #{thermostat_name} not found for space name: #{space.name}-") else thermostat_clone = thermostat.get.clone(model).to_ThermostatSetpointDualSetpoint.get zone.setThermostatSetpointDualSetpoint(thermostat_clone) # Set Ideal loads to thermal zone for sizing for NECB needs. We need this for sizing. ideal_loads = OpenStudio::Model::ZoneHVACIdealLoadsAirSystem.new(model) ideal_loads.addToThermalZone(zone) end # Go through other spaces to see if there are similar spaces with similar loads on the same floor that can be grouped. model.getSpaces.select { |s| is_an_necb_wet_space?(s) }.each do |space_target| if space_target.thermalZone.empty? if are_space_loads_similar?(space_1: space, space_2: space_target) && space.buildingStory.get == space_target.buildingStory.get # added since chris needs zones to not span floors for costing. adjust_wildcard_spacetype_schedule(space: space_target, schedule: dominant_schedule, lights_type: lights_type, lights_scale: lights_scale) space_target.setThermalZone(zone) end end end wet_zone_array << zone end return wet_zone_array end |
#auto_zone_wild_spaces(model:, lights_type: 'NECB_Default', lights_scale: 1.0) ⇒ Object
This will take all the wildcard spaces and merge them to be supported by a system 4. The control zone will be the zone that has the largest heating load per area.
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 387 def auto_zone_wild_spaces(model:, lights_type: 'NECB_Default', lights_scale: 1.0) other_tz_array = [] # iterate through wildcard spaces. model.getSpaces.select { |space| is_an_necb_wildcard_space?(space) && !is_an_necb_wet_space?(space) }.each do |space| # skip if already assigned to a thermal zone. next unless space.thermalZone.empty? # create new zone for this space based on the space name. zone = OpenStudio::Model::ThermalZone.new(model) tz_name = "WILD_ST=#{space.spaceType.get.standardsSpaceType.get}_FL=#{space.buildingStory.get.name}_SCH=#{determine_dominant_schedule(space.model.getSpaces)}" zone.setName(tz_name) # sets space mulitplier unless it is nil or 1. unless space_multiplier_map[space.name.to_s].nil? || (space_multiplier_map[space.name.to_s] == 1) zone.setMultiplier(space_multiplier_map[space.name.to_s]) end # Assign space to the new zone. space.setThermalZone(zone) # lets keep the wild schedules to be the same as what dominate the floor. dominant_floor_schedule = determine_dominant_schedule(space.model.getSpaces) adjust_wildcard_spacetype_schedule(space: space, schedule: dominant_floor_schedule, lights_type: lights_type, lights_scale: lights_scale) # Add a thermostat space_type_name = space.spaceType.get.name.get thermostat_name = space_type_name + ' Thermostat' thermostat = model.getThermostatSetpointDualSetpointByName(thermostat_name) if thermostat.empty? # The thermostat name for the spacetype should exist. OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "Thermostat #{thermostat_name} not found for space name: #{space.name}") else thermostat_clone = thermostat.get.clone(model).to_ThermostatSetpointDualSetpoint.get zone.setThermostatSetpointDualSetpoint(thermostat_clone) # Set Ideal loads to thermal zone for sizing for NECB needs. We need this for sizing. ideal_loads = OpenStudio::Model::ZoneHVACIdealLoadsAirSystem.new(model) ideal_loads.addToThermalZone(zone) end # Go through other spaces and if you find something with similar loads on the same floor, add it to the zone. model.getSpaces.select { |curr_space| is_an_necb_wildcard_space?(curr_space) && !is_an_necb_wet_space?(curr_space) }.each do |space_target| if space_target.thermalZone.empty? if are_space_loads_similar?(space_1: space, space_2: space_target) && (space.buildingStory.get == space_target.buildingStory.get) # added since chris needs zones to not span floors for costing. space_target.setThermalZone(zone) end end end other_tz_array << zone end return other_tz_array wild_zone_array = [] # Get a list of all the wild spaces. model.getSpaces.select { |space| is_an_necb_wildcard_space?(space) && !is_an_necb_wet_space?(space) }.each do |space| # if this space was already assigned to something skip it. next unless space.thermalZone.empty? # find adjacent spaces to the current space. adj_spaces = OpenstudioStandards::Geometry.space_get_adjacent_spaces_with_shared_wall_areas(space, true) adj_spaces = adj_spaces.map { |key, value| key } # find unassigned adjacent wild spaces that have not been assigned that have the same multiplier these will be # lumped together in the same zone. wild_adjacent_spaces = adj_spaces.select do |adj_space| is_an_necb_wildcard_space?(adj_space) && !is_an_necb_wet_space?(adj_space) && adj_space.thermalZone.empty? && (space_multiplier_map[space.name.to_s] == space_multiplier_map[adj_space.name.to_s]) end # put them all together. wild_adjacent_spaces << space # Get adjacent candidate foster zones. Must not be a wildcard space and must not be linked to another space incase # it is part of a mirrored space. other_adjacent_spaces = adj_spaces.select do |adj_space| (is_an_necb_wildcard_space?(adj_space) == false) && (adj_space.thermalZone.get.spaces.size == 1) && (space_multiplier_map[space.name.to_s] == space_multiplier_map[adj_space.name.to_s]) end # If there are adjacent spaces that fit the above criteria. # We will need to set each space to the dominant floor schedule by setting the spaces spacetypes to that # schedule version and eventually set it to a system 4 unless other_adjacent_spaces.empty? # assign the space(s) to the adjacent thermal zone. schedule_type = determine_dominant_schedule(space.buildingStory.get.spaces) zone = other_adjacent_spaces.first.thermalZone.get wild_adjacent_spaces.each do |curr_space| adjust_wildcard_spacetype_schedule(curr_space, schedule_type, @lights_type, @lights_scale, @space_height) curr_space.setThermalZone(zone) end end # create new TZ and set space to the zone. zone = OpenStudio::Model::ThermalZone.new(model) space.setThermalZone(zone) zone.setName("Wild-ZN:BT=#{space.spaceType.get.standardsBuildingType.get}:ST=#{space.spaceType.get.standardsSpaceType.get}:FL=#{space.buildingStory.get.name}:") # Set multiplier from the original tz multiplier. unless space_multiplier_map[space.name.to_s].nil? || (space_multiplier_map[space.name.to_s] == 1) zone.setMultiplier(space_multiplier_map[space.name.to_s]) end # Set space to dominant dominant_floor_schedule = determine_dominant_schedule(space.buildingStory.get.spaces) # this method will determine if the right schedule was used for this wet & wild space if not.. it will reset the space # to use the correct schedule version of the wet and wild space type. adjust_wildcard_spacetype_schedule(space: space, schedule: dominant_floor_schedule, lights_type: @lights_type, lights_scale: @lights_scale) # Find spacetype thermostat and assign it to the zone. thermostat_name = space.spaceType.get.name.get + ' Thermostat' thermostat = model.getThermostatSetpointDualSetpointByName(thermostat_name) if thermostat.empty? # The thermostat name for the spacetype should exist. OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "Thermostat #{thermostat_name} not found for space name: #{space.name}") else thermostat_clone = thermostat.get.clone(model).to_ThermostatSetpointDualSetpoint.get zone.setThermostatSetpointDualSetpoint(thermostat_clone) # Set Ideal loads to thermal zone for sizing for NECB needs. We need this for sizing. ideal_loads = OpenStudio::Model::ZoneHVACIdealLoadsAirSystem.new(model) ideal_loads.addToThermalZone(zone) end # Go through other spaces to see if there are similar spaces with similar loads on the same floor that can be grouped. model.getSpaces.select { |s| is_an_necb_wildcard_space?(s) && !is_an_necb_wet_space?(s) }.each do |space_target| if space_target.thermalZone.empty? if are_space_loads_similar?(space_1: space, space_2: space_target) && space.buildingStory.get == space_target.buildingStory.get # added since chris needs zones to not span floors for costing. adjust_wildcard_spacetype_schedule(space: space_target, schedule: dominant_floor_schedule, lights_type: @lights_type, lights_scale: @lights_scale) space_target.setThermalZone(zone) end end end wild_zone_array << zone end return wild_zone_array end |
#boiler_hot_water_apply_efficiency_and_curves(boiler_hot_water) ⇒ Boolean
Applies the standard efficiency ratings and typical performance curves to this object.
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 533 def boiler_hot_water_apply_efficiency_and_curves(boiler_hot_water) successfully_set_all_properties = false # Define the criteria to find the boiler properties # in the hvac standards data set. search_criteria = boiler_hot_water_find_search_criteria(boiler_hot_water) fuel_type = search_criteria['fuel_type'] fluid_type = search_criteria['fluid_type'] # Get the capacity capacity_w = boiler_hot_water_find_capacity(boiler_hot_water) boiler_capacity = capacity_w # Use the NECB capacities if the SystemFuels class is not defined (i.e. this method was not called from something # that created a SystemFuels object) or if either primary or secondary boiler capacity fractions are not defined. if !self.fuel_type_set.is_a?(SystemFuels) || self.fuel_type_set.primary_boiler_cap_frac.nil? || self.fuel_type_set.secondary_boiler_cap_frac.nil? # Check if secondary and/or modulating boiler required # If boiler names include 'Primary Boiler' or 'Secondary Boiler' then NECB rules are applied if boiler_hot_water.name.to_s.include?('Primary Boiler') || boiler_hot_water.name.to_s.include?('Secondary Boiler') if capacity_w / 1000.0 >= 352.0 if boiler_hot_water.name.to_s.include?('Primary Boiler') boiler_capacity = capacity_w boiler_hot_water.setBoilerFlowMode('LeavingSetpointModulated') boiler_hot_water.setMinimumPartLoadRatio(0.25) elsif boiler_hot_water.name.to_s.include?('Secondary Boiler') boiler_capacity = 0.001 end elsif ((capacity_w / 1000.0) >= 176.0) && ((capacity_w / 1000.0) < 352.0) boiler_capacity = capacity_w / 2 elsif (capacity_w / 1000.0) <= 176.0 if boiler_hot_water.name.to_s.include?('Primary Boiler') if capacity_w <= 1.0 boiler_capacity = 1.0 else boiler_capacity = capacity_w end elsif boiler_hot_water.name.to_s.include?('Secondary Boiler') boiler_capacity = 0.001 end end end else boiler_capacity = capacity_w * self.fuel_type_set.primary_boiler_cap_frac if boiler_hot_water.name.to_s.include?('Primary Boiler') boiler_capacity = capacity_w * self.fuel_type_set.secondary_boiler_cap_frac if boiler_hot_water.name.to_s.include?('Secondary Boiler') end boiler_hot_water.setNominalCapacity(boiler_capacity) # Convert capacity to Btu/hr capacity_btu_per_hr = OpenStudio.convert(boiler_capacity, 'W', 'Btu/hr').get capacity_kbtu_per_hr = OpenStudio.convert(boiler_capacity, 'W', 'kBtu/hr').get # Get the boiler properties boiler_table = @standards_data['boilers'] blr_props = model_find_object(boiler_table, search_criteria, capacity_btu_per_hr) unless blr_props OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.BoilerHotWater', "For #{boiler_hot_water.name}, cannot find boiler properties, cannot apply efficiency standard.") successfully_set_all_properties = false return successfully_set_all_properties end # Make the EFFFPLR curve eff_fplr = model_add_curve(boiler_hot_water.model, blr_props['efffplr']) if eff_fplr boiler_hot_water.setNormalizedBoilerEfficiencyCurve(eff_fplr) else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.BoilerHotWater', "For #{boiler_hot_water.name}, cannot find eff_fplr curve, will not be set.") successfully_set_all_properties = false end # Get the minimum efficiency standards thermal_eff = nil # If specified as AFUE unless blr_props['minimum_annual_fuel_utilization_efficiency'].nil? min_afue = blr_props['minimum_annual_fuel_utilization_efficiency'] thermal_eff = afue_to_thermal_eff(min_afue) new_comp_name = "#{boiler_hot_water.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_afue} AFUE" OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.BoilerHotWater', "For #{template}: #{boiler_hot_water.name}: #{fuel_type} #{fluid_type} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; AFUE = #{min_afue}") end # If specified as thermal efficiency unless blr_props['minimum_thermal_efficiency'].nil? thermal_eff = blr_props['minimum_thermal_efficiency'] new_comp_name = "#{boiler_hot_water.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{thermal_eff} Thermal Eff" OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.BoilerHotWater', "For #{template}: #{boiler_hot_water.name}: #{fuel_type} #{fluid_type} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; Thermal Efficiency = #{thermal_eff}") end # If specified as combustion efficiency unless blr_props['minimum_combustion_efficiency'].nil? min_comb_eff = blr_props['minimum_combustion_efficiency'] thermal_eff = combustion_eff_to_thermal_eff(min_comb_eff) new_comp_name = "#{boiler_hot_water.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_comb_eff} Combustion Eff" OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.BoilerHotWater', "For #{template}: #{boiler_hot_water.name}: #{fuel_type} #{fluid_type} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; Combustion Efficiency = #{min_comb_eff}") end # Set the name boiler_hot_water.setName(new_comp_name) # Set the efficiency values unless thermal_eff.nil? boiler_hot_water.setNominalThermalEfficiency(thermal_eff) end return successfully_set_all_properties end |
#boiler_hot_water_find_search_criteria(boiler_hot_water) ⇒ Hash
find search criteria
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 503 def boiler_hot_water_find_search_criteria(boiler_hot_water) # Define the criteria to find the boiler properties # in the hvac standards data set. search_criteria = {} search_criteria['template'] = template # Get fuel type fuel_type = nil case boiler_hot_water.fuelType when 'NaturalGas' fuel_type = 'Gas' when 'Electricity' fuel_type = 'Electric' when 'FuelOilNo1', 'FuelOilNo2' fuel_type = 'Oil' else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.BoilerHotWater', "For #{boiler_hot_water.name}, a fuel type of #{fuel_type} is not yet supported. Assuming 'Gas.'") fuel_type = 'Gas' end search_criteria['fuel_type'] = fuel_type # Get the fluid type fluid_type = 'Hot Water' search_criteria['fluid_type'] = fluid_type return search_criteria end |
#check_boolean_value(value, varname) ⇒ Object
1908 1909 1910 1911 1912 1913 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1908 def check_boolean_value(value, varname) return true if value =~ /^(true|t|yes|y|1)$/i return false if value.empty? || value =~ /^(false|f|no|n|0)$/i raise ArgumentError, "invalid value for #{varname}: #{value}" end |
#check_datapoint_weather_folder(epw_file:, weather_folder:, custom_weather_folder: nil) ⇒ Object
This method checks if a zip file containing weather data is stored in an external directory. If it is, then it checks if the name of the zip file (without extension) matches the name of the desired epw file (without extension). If it does then it copies the zip file to the openstudio-standards weather directory and expands the file. Presumably the zip file contains the .ddy, .epw, and .stat files needed by the rest of BTAP. If no appropriate weather zip files are present in the external directory then the method returns false. Arguments: epw_file (string): The name of the .epw file that BTAP will use. weather_folder (string): The path to the openstudio-standards weather folder. custom_weather_folder (string): The path to the external folder presumably containing the weather data zip file.
2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 2368 def check_datapoint_weather_folder(epw_file:, weather_folder:, custom_weather_folder: nil) # Check if the external weather directory exists and return false if there isn't one return false if custom_weather_folder.nil? # Check if there are any zip files in the external weather directory and return false if there isn't any. zip_search_term = File.join(custom_weather_folder.to_s, '*.zip') zip_files = Dir[zip_search_term] return false if zip_files.empty? # Look for a zip file in the external directory named after the .epw file. If there isn't one return false. weather_loc = epw_file[0..-5] weather_zip = weather_loc + '.zip' zip_find = zip_files.select{ |check_file | File.basename(check_file).to_s.downcase == weather_zip.to_s.downcase } return false if zip_find.empty? # Copy the zip file we want from the external weather directory to the openstudio-standards weather directory and # extract the weather data in the file. puts "Copying: #{zip_find[0]} from the btap_cli weather folder to the openstudio-standards weather folder: #{weather_folder}" FileUtils.cp(zip_find[0], weather_folder) dest_zip = File.join(weather_folder, weather_zip) # Return true if everything goes well. return extract_weather_data(zipped_file: dest_zip, weather_dir: weather_folder) end |
#chiller_electric_eir_apply_efficiency_and_curves(chiller_electric_eir, clg_tower_objs) ⇒ Boolean
Applies the standard efficiency ratings and typical performance curves to this object.
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 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 642 def chiller_electric_eir_apply_efficiency_and_curves(chiller_electric_eir, clg_tower_objs) # Define the criteria to find the chiller properties # in the hvac standards data set. search_criteria = chiller_electric_eir_find_search_criteria(chiller_electric_eir) cooling_type = search_criteria['cooling_type'] condenser_type = search_criteria['condenser_type'] compressor_type = search_criteria['compressor_type'] # Get the chiller capacity capacity_w = chiller_electric_eir_find_capacity(chiller_electric_eir) # All chillers must be modulating down to 25% of their capacity chiller_electric_eir.setChillerFlowMode('LeavingSetpointModulated') chiller_electric_eir.setMinimumPartLoadRatio(0.25) chiller_electric_eir.setMinimumUnloadingRatio(0.25) chiller_capacity = capacity_w # If the chiller name includes 'Primary' or 'Secondary' then apply NECB rules if (chiller_electric_eir.name.to_s.include? 'Primary') || (chiller_electric_eir.name.to_s.include? 'Secondary') if (capacity_w / 1000.0) < 2100.0 if chiller_electric_eir.name.to_s.include? 'Primary Chiller' chiller_capacity = capacity_w elsif chiller_electric_eir.name.to_s.include? 'Secondary Chiller' chiller_capacity = 0.001 end else chiller_capacity = capacity_w / 2.0 end end chiller_electric_eir.setReferenceCapacity(chiller_capacity) # Convert capacity to tons capacity_tons = OpenStudio.convert(chiller_capacity, 'W', 'ton').get # Get chiller compressor type if needed chiller_types = ['reciprocating','scroll','rotary screw','centrifugal'] chiller_name_has_type = chiller_types.any? {|type| chiller_electric_eir.name.to_s.downcase.include? type} unless chiller_name_has_type chlr_type_search_criteria = {} chlr_type_search_criteria['cooling_type'] = cooling_type chlr_types_table = @standards_data['chiller_types'] chlr_type_props = model_find_object(chlr_types_table, chlr_type_search_criteria, capacity_tons) unless chlr_type_props OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.ChillerElectricEIR', "For #{chiller_electric_eir.name}, cannot find chiller type information") successfully_set_all_properties = false return successfully_set_all_properties end compressor_type = chlr_type_props['compressor_type'] chiller_electric_eir.setName(chiller_electric_eir.name.to_s + ' ' + compressor_type) end # Get the chiller properties search_criteria['compressor_type'] = compressor_type chlr_table = @standards_data['chillers'] chlr_props = model_find_object(chlr_table, search_criteria, capacity_tons, Date.today) unless chlr_props OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.ChillerElectricEIR', "For #{chiller_electric_eir.name}, cannot find chiller properties, cannot apply standard efficiencies or curves.") successfully_set_all_properties = false return successfully_set_all_properties end # Make the CAPFT curve cool_cap_ft = model_add_curve(chiller_electric_eir.model, chlr_props['capft']) if cool_cap_ft chiller_electric_eir.setCoolingCapacityFunctionOfTemperature(cool_cap_ft) else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.ChillerElectricEIR', "For #{chiller_electric_eir.name}, cannot find cool_cap_ft curve, will not be set.") successfully_set_all_properties = false end # Make the EIRFT curve cool_eir_ft = model_add_curve(chiller_electric_eir.model, chlr_props['eirft']) if cool_eir_ft chiller_electric_eir.setElectricInputToCoolingOutputRatioFunctionOfTemperature(cool_eir_ft) else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.ChillerElectricEIR', "For #{chiller_electric_eir.name}, cannot find cool_eir_ft curve, will not be set.") successfully_set_all_properties = false end # Make the EIRFPLR curve # which may be either a CurveBicubic or a CurveQuadratic based on chiller type cool_plf_fplr = model_add_curve(chiller_electric_eir.model, chlr_props['eirfplr']) if cool_plf_fplr chiller_electric_eir.setElectricInputToCoolingOutputRatioFunctionOfPLR(cool_plf_fplr) else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.ChillerElectricEIR', "For #{chiller_electric_eir.name}, cannot find cool_plf_fplr curve, will not be set.") successfully_set_all_properties = false end # Set the efficiency value kw_per_ton = nil cop = nil if chlr_props['minimum_full_load_efficiency'] kw_per_ton = chlr_props['minimum_full_load_efficiency'] cop = kw_per_ton_to_cop(kw_per_ton) chiller_electric_eir.setReferenceCOP(cop) else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.ChillerElectricEIR', "For #{chiller_electric_eir.name}, cannot find minimum full load efficiency, will not be set.") successfully_set_all_properties = false end # Set cooling tower properties now that the new COP of the chiller is set if chiller_electric_eir.name.to_s.include? 'Primary Chiller' # Single speed tower model assumes 25% extra for compressor power tower_cap = capacity_w * (1.0 + 1.0 / chiller_electric_eir.referenceCOP) if (tower_cap / 1000.0) < 1750 clg_tower_objs[0].setNumberofCells(1) else clg_tower_objs[0].setNumberofCells((tower_cap / (1000 * 1750) + 0.5).round) end clg_tower_objs[0].setFanPoweratDesignAirFlowRate(0.015 * tower_cap) end # Append the name with size and kw/ton chiller_electric_eir.setName("#{chiller_electric_eir.name} #{capacity_tons.round}tons #{kw_per_ton.round(1)}kW/ton") OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.ChillerElectricEIR', "For #{template}: #{chiller_electric_eir.name}: #{cooling_type} #{condenser_type} #{compressor_type} Capacity = #{capacity_tons.round}tons; COP = #{cop.round(1)} (#{kw_per_ton.round(1)}kW/ton)") return successfully_set_all_properties end |
#clean_and_scale_model(model:, rotation_degrees: nil, scale_x: nil, scale_y: nil, scale_z: nil) ⇒ Object
This method cleans the model of any existing HVAC systems and applies any desired ratation or scaling to the model.
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 508 def clean_and_scale_model(model:, rotation_degrees: nil, scale_x: nil, scale_y: nil, scale_z: nil) # clean model.. BTAP::Resources::Envelope::remove_all_envelope_information(model) model = remove_all_hvac(model) model.getThermalZones.sort.each { |zone| zone.setUseIdealAirLoads(true) } model.getZoneHVACPackagedTerminalAirConditioners.each(&:remove) model.getCoilCoolingDXSingleSpeeds.each(&:remove) model.getZoneHVACBaseboardConvectiveWaters.each(&:remove) model.getAirLoopHVACZoneMixers.each(&:remove) model.getAirLoopHVACZoneSplitters.each(&:remove) model.getAirTerminalSingleDuctConstantVolumeNoReheats.each(&:remove) model.getWaterUseEquipmentDefinitions.each(&:remove) model.getWaterUseEquipments.each(&:remove) model.getWaterUseConnectionss.each(&:remove) model.getPumpConstantSpeeds.each(&:remove) model.getPumpVariableSpeeds.each(&:remove) model.getBoilerHotWaters.each(&:remove) model.getBoilerSteams.each(&:remove) model.getPlantLoops.each(&:remove) model.getSchedules.each(&:remove) model.getThermalZones.sort.each { |zone| zone.thermostat(&:remove) } model.getSpaces.sort.each { |space| space.designSpecificationOutdoorAir(&:remove) } model.getThermostatSetpointDualSetpoints.each(&:remove) scale_x = 1.0 scale_y = 1.0 scale_z = 1.0 # Rotate to model if requested rotation_degrees = convert_arg_to_f(variable: rotation_degrees, default: 0.0) BTAP::Geometry.rotate_building(model: model, degrees: rotation_degrees) unless rotation_degrees == 0.0 # Scale model if requested scale_x = convert_arg_to_f(variable: scale_x, default: 1.0) scale_y = convert_arg_to_f(variable: scale_y, default: 1.0) scale_z = convert_arg_to_f(variable: scale_z, default: 1.0) return unless scale_x != 1.0 || scale_y != 1.0 || scale_z != 1.0 BTAP::Geometry.scale_model(model, scale_x, scale_y, scale_z) end |
#coil_cooling_dx_multi_speed_apply_efficiency_and_curves(coil_cooling_dx_multi_speed, sql_db_vars_map) ⇒ Boolean
Applies the standard efficiency ratings and typical performance curves to this object.
899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 899 def coil_cooling_dx_multi_speed_apply_efficiency_and_curves(coil_cooling_dx_multi_speed, sql_db_vars_map) successfully_set_all_properties = true model = coil_cooling_dx_multi_speed.model multi_speed_heat_pump = coil_cooling_dx_multi_speed.containingHVACComponent.get.to_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed.get airloop = multi_speed_heat_pump.airLoopHVAC.get # Define the criteria to find the properties in the hvac standards data set search_criteria = coil_dx_find_search_criteria(coil_cooling_dx_multi_speed) capacity_w = coil_cooling_dx_multi_speed_find_capacity(coil_cooling_dx_multi_speed) # Find design outside air flow rate and flow fraction controller_oa = nil if airloop.airLoopHVACOutdoorAirSystem.is_initialized oa_system = airloop.airLoopHVACOutdoorAirSystem.get controller_oa = oa_system.getControllerOutdoorAir end min_oa_flow_rate = 0.0 oaf = 0.0 if controller_oa min_oa_flow_rate = nil if controller_oa.minimumOutdoorAirFlowRate.is_initialized min_oa_flow_rate = controller_oa.minimumOutdoorAirFlowRate.get elsif controller_oa.autosizedMinimumOutdoorAirFlowRate.is_initialized min_oa_flow_rate = controller_oa.autosizedMinimumOutdoorAirFlowRate.get end if min_oa_flow_rate then oaf = min_oa_flow_rate.to_f / airloop.autosizedDesignSupplyAirFlowRate.to_f end end # Find required capacity of each stage and total number of stages based on NECB rules # This implementation is limited to 4 stages only. The capacity of stages 1-3 is set to # 66 kW as stipulated by NECB. The capacity of the 4th stage is then allowed to exceed 66 kW # up to the design capacity. stage_cap = [] num_stages = (capacity_w / (66.0 * 1000.0) + 0.5).round max_cap = 66.0 * 1000.0 * num_stages final_num_stages = num_stages case num_stages when 1 stage_cap[0] = capacity_w / 2.0 stage_cap[1] = 2.0 * stage_cap[0] final_num_stages = 2 else stage_cap[0] = 66.0 * 1000.0 stage_cap[1] = 2.0 * stage_cap[0] case num_stages when 2 when 3 stage_cap[2] = 3.0 * stage_cap[0] else final_num_stages = 4 stage_cap[2] = 3.0 * stage_cap[0] stage_cap[3] = max_cap end end # Set final number of cooling stages and create missing stages if needed for istage in 2..final_num_stages - 1 new_clg_stage = OpenStudio::Model::CoilCoolingDXMultiSpeedStageData.new(model) coil_cooling_dx_multi_speed.addStage(new_clg_stage) end multi_speed_heat_pump.setNumberofSpeedsforCooling(final_num_stages) # Set final capacities for each of the stages. The flow rate for each of the stages # is maintained above the outside air flow rate coil_cooling_dx_multi_speed.stages[0].setGrossRatedTotalCoolingCapacity(stage_cap[0]) coil_cooling_dx_multi_speed.stages[1].setGrossRatedTotalCoolingCapacity(stage_cap[1]) case coil_cooling_dx_multi_speed.stages.size when 2 if oaf > 0.5 then multi_speed_heat_pump.setSpeed1SupplyAirFlowRateDuringCoolingOperation(min_oa_flow_rate) end when 3 coil_cooling_dx_multi_speed.stages[2].setGrossRatedTotalCoolingCapacity(stage_cap[2]) if (oaf > 0.333) && (oaf <= 0.666) multi_speed_heat_pump.setSpeed1SupplyAirFlowRateDuringCoolingOperation(min_oa_flow_rate) elsif oaf > 0.666 multi_speed_heat_pump.setSpeed1SupplyAirFlowRateDuringCoolingOperation(min_oa_flow_rate) multi_speed_heat_pump.setSpeed2SupplyAirFlowRateDuringCoolingOperation(min_oa_flow_rate) end when 4 coil_cooling_dx_multi_speed.stages[2].setGrossRatedTotalCoolingCapacity(stage_cap[2]) coil_cooling_dx_multi_speed.stages[3].setGrossRatedTotalCoolingCapacity(stage_cap[3]) if (oaf > 0.25) && (oaf <= 0.5) multi_speed_heat_pump.setSpeed1SupplyAirFlowRateDuringCoolingOperation(min_oa_flow_rate) elsif (oaf > 0.5) && (oaf <= 0.75) multi_speed_heat_pump.setSpeed1SupplyAirFlowRateDuringCoolingOperation(min_oa_flow_rate) multi_speed_heat_pump.setSpeed2SupplyAirFlowRateDuringCoolingOperation(min_oa_flow_rate) elsif oaf > 0.75 multi_speed_heat_pump.setSpeed1SupplyAirFlowRateDuringCoolingOperation(min_oa_flow_rate) multi_speed_heat_pump.setSpeed2SupplyAirFlowRateDuringCoolingOperation(min_oa_flow_rate) multi_speed_heat_pump.setSpeed3SupplyAirFlowRateDuringCoolingOperation(min_oa_flow_rate) end end capacity_btu_per_hr = OpenStudio.convert(stage_cap.last, 'W', 'Btu/hr').get capacity_kbtu_per_hr = OpenStudio.convert(stage_cap.last, 'W', 'kBtu/hr').get # Lookup efficiencies depending on whether it is a unitary AC or a heat pump ac_props = nil ac_props = if coil_dx_heat_pump?(coil_cooling_dx_multi_speed) model_find_object(standards_data['heat_pumps'], search_criteria, capacity_btu_per_hr, Date.today) else model_find_object(standards_data['unitary_acs'], search_criteria, capacity_btu_per_hr, Date.today) end # Check to make sure properties were found if ac_props.nil? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilCoolingDXMultiSpeed', "For #{coil_cooling_dx_multi_speed.name}, cannot find efficiency info, cannot apply efficiency standard.") successfully_set_all_properties = false return sql_db_vars_map end # get clg stages clg_stages = coil_cooling_dx_multi_speed.stages # Make the COOL-CAP-FT curve cool_cap_ft = model_add_curve(model, ac_props['cool_cap_ft']) if cool_cap_ft clg_stages.sort.each do |stage| stage.setTotalCoolingCapacityFunctionofTemperatureCurve(cool_cap_ft) end else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilCoolingDXMultiSpeed', "For #{coil_cooling_dx_multi_speed.name}, cannot find cool_cap_ft curve, will not be set.") successfully_set_all_properties = false return sql_db_vars_map end # Make the COOL-CAP-FFLOW curve cool_cap_fflow = model_add_curve(model, ac_props['cool_cap_fflow']) if cool_cap_fflow clg_stages.sort.each do |stage| stage.setTotalCoolingCapacityFunctionofFlowFractionCurve(cool_cap_fflow) end else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilCoolingDXMultiSpeed', "For #{coil_cooling_dx_multi_speed.name}, cannot find cool_cap_fflow curve, will not be set.") successfully_set_all_properties = false return sql_db_vars_map end # Make the COOL-EIR-FT curve cool_eir_ft = model_add_curve(model, ac_props['cool_eir_ft']) if cool_eir_ft clg_stages.sort.each do |stage| stage.setEnergyInputRatioFunctionofTemperatureCurve(cool_eir_ft) end else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilCoolingDXMultiSpeed', "For #{coil_cooling_dx_multi_speed.name}, cannot find cool_eir_ft curve, will not be set.") successfully_set_all_properties = false return sql_db_vars_map end # Make the COOL-EIR-FFLOW curve cool_eir_fflow = model_add_curve(model, ac_props['cool_eir_fflow']) if cool_eir_fflow clg_stages.sort.each do |stage| stage.setEnergyInputRatioFunctionofFlowFractionCurve(cool_eir_fflow) end else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilCoolingDXMultiSpeed', "For #{coil_cooling_dx_multi_speed.name}, cannot find cool_eir_fflow curve, will not be set.") successfully_set_all_properties = false return sql_db_vars_map end # Make the COOL-PLF-FPLR curve cool_plf_fplr = model_add_curve(model, ac_props['cool_plf_fplr']) if cool_plf_fplr clg_stages.sort.each do |stage| stage.setPartLoadFractionCorrelationCurve(cool_plf_fplr) end else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilCoolingDXMultiSpeed', "For #{coil_cooling_dx_multi_speed.name}, cannot find cool_plf_fplr curve, will not be set.") successfully_set_all_properties = false return sql_db_vars_map end # Set the COP values cop, new_comp_name = coil_cooling_dx_multi_speed_standard_minimum_cop(coil_cooling_dx_multi_speed) unless cop.nil? clg_stages.sort.each do |curr_istage| curr_istage.setGrossRatedCoolingCOP(cop) end end sql_db_vars_map[new_comp_name] = coil_cooling_dx_multi_speed.name.to_s coil_cooling_dx_multi_speed.setName(new_comp_name) # It was found that the heat pump OS object doesn't respond to the call to turn on from the # system availability manager night cycle. This EMS script is then implemented to check the status # of the system availability manager night cycle and force the heat pump to turn on when needed. The # heat pump is still turned on when its availability schedule calls for it. create_ems_to_turn_on_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed_for_night_cycle(multi_speed_heat_pump) return sql_db_vars_map end |
#coil_dx_heating_type(coil_dx, necb_reference_hp = false) ⇒ Object
NECB reference heat pump system heating type rules need to be flexible to account for
-
DX htg/cooling + gas supplement htg
-
Potential lack of AirLoopHVACUnitaryHeatPumpAirToAir or AirLoopHVACUnitarySystem
2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 2435 def coil_dx_heating_type(coil_dx, necb_reference_hp = false) supp_htg_type = nil # If not heat pump reference case use the standard implementation. if !necb_reference_hp return super(coil_dx) else if coil_dx.airLoopHVAC.empty? if coil_dx.containingHVACComponent.is_initialized containing_comp = coil_dx.containingHVACComponent.get if containing_comp.to_AirLoopHVACUnitaryHeatPumpAirToAir.is_initialized supp_htg_coil = containing_comp.to_AirLoopHVACUnitaryHeatPumpAirToAir.get.supplementalHeatingCoil if supp_htg_coil.to_CoilHeatingElectric.is_initialized supp_htg_type = 'Electric Resistance or None' elsif supp_htg_coil.to_CoilHeatingGas.is_initialized or supp_htg_coil.to_CoilHeatingWater.is_initialized supp_htg_type = 'All Other' else # None supp_htg_type = 'Electric Resistance or None' end else # For other virtual wrapper, use method in Standard.DXCoil # Or add future wrappers here return super end end elsif coil_dx.airLoopHVAC.is_initialized # Heat pumps without a wrapper (lone DX coils in the air loop) airloop = coil_dx.airLoopHVAC.get num_of_DX_Coils = 0 num_of_supp_coils = 0 supp_htg_type = '' # Go through and determine number of each type of coils in air loop to determine supp_htg_type airloop.supplyComponents.each do |supply_component| if supply_component.to_CoilHeatingDXSingleSpeed.is_initialized or supply_component.to_CoilHeatingDXMultiSpeed.is_initialized supply_component.to_CoilHeatingDXVariableSpeed.is_initialized num_of_DX_Coils = num_of_DX_Coils + 1 elsif supply_component.to_CoilCoolingDXSingleSpeed.is_initialized or supply_component.to_CoilCoolingDXTwoSpeed.is_initialized or supply_component.to_CoilCoolingDXTwoSpeed.is_initialized or supply_component.to_CoilCoolingDXVariableSpeed.is_initialized or supply_component.to_CoilCoolingDXMultiSpeed.is_initialized or supply_component.to_CoilCoolingDXCurveFitPerformance.is_initialized or supply_component.to_CoilCoolingDXTwoStageWithHumidityControlMode.is_initialized num_of_DX_Coils = num_of_DX_Coils + 1 elsif supply_component.to_CoilHeatingGas.is_initialized or supply_component.to_CoilHeatingGasMultiStage.is_initialized or supply_component.to_CoilHeatingWater.is_initialized num_of_supp_coils = num_of_supp_coils + 1 supp_htg_type = 'All Other' elsif supply_component.to_CoilHeatingElectric.is_initialized num_of_supp_coils = num_of_supp_coils + 1 supp_htg_type = 'Electric Resistance or None' end end #Two possible heat pump configuration if num_of_DX_Coils == 2 && num_of_supp_coils == 1 #Scenario 1: 1 DX htg + 1 DX clg + 1 Non-DX htg coil puts "scenario 1 supp_htg_type #{supp_htg_type}" return supp_htg_type # return supplmental heating type else #Scenario 2: num_of_DX_Coils < 2 or num_of_supp_coils = 0; puts "scenario 2 supp_htg_type #{supp_htg_type}" puts "num_of_DX_Coils #{num_of_DX_Coils}" puts "num_of_supp_coils #{num_of_supp_coils}" return supp_htg_type = 'Electric Resistance or None' end end end end |
#coil_heating_dx_single_speed_find_capacity(coil_heating_dx_single_speed, necb_reference_hp = false) ⇒ Object
2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 2342 def coil_heating_dx_single_speed_find_capacity(coil_heating_dx_single_speed, necb_reference_hp = false) # Set Rated heating capacity = 50% cooling coil capacity at -8.3 C outdoor [8.4.4.13 (2)(c)] if necb_reference_hp #NECB reference heat pump rules apply # grab paired cooling coil if coil_heating_dx_single_speed.airLoopHVAC.empty? if coil_heating_dx_single_speed.containingHVACComponent.is_initialized containing_comp = coil_heating_dx_single_speed.containingHVACComponent.get if containing_comp.to_AirLoopHVACUnitaryHeatPumpAirToAir.is_initialized clg_coil = containing_comp.to_AirLoopHVACUnitaryHeatPumpAirToAir.get.coolingCoil elsif containing_comp.to_AirLoopHVACUnitarySystem.is_initialized unitary = containing_comp.to_AirLoopHVACUnitarySystem.get if unitary.coolingCoil.is_initialized clg_coil = unitary.coolingCoil.get end end # @todo Add other unitary systems elsif coil_heating_dx_single_speed.containingZoneHVACComponent.is_initialized containing_comp = coil_heating_dx_single_speed.containingZoneHVACComponent.get # PTHP if containing_comp.to_ZoneHVACPackagedTerminalHeatPump.is_initialized pthp = containing_comp.to_ZoneHVACPackagedTerminalHeatPump.get clg_coil = containing_comp.to_ZoneHVACPackagedTerminalHeatPump.get.coolingCoil end end elsif coil_heating_dx_single_speed.airLoopHVAC.is_initialized air_loop = coil_heating_dx_single_speed.airLoopHVAC.get # Check for the presence of any other type of cooling coil clg_types = ['OS:Coil:Cooling:DX:SingleSpeed', 'OS:Coil:Cooling:DX:TwoSpeed', 'OS:Coil:Cooling:DX:MultiSpeed'] clg_types.each do |ct| coils = air_loop.supplyComponents(ct.to_IddObjectType) next if coils.empty? clg_coil = coils[0] puts "coils = air_loop.supplyComponents(ct.to_IddObjectType) #{}" break # Stop on first DX cooling coil found end end # Paired cooling coil parameters clg_coil = clg_coil.to_CoilCoolingDXSingleSpeed.get capacity_w = coil_cooling_dx_single_speed_find_capacity(clg_coil) indoor_wb = 19.4 #rated indoor wb outdoor_db = -8.3 # outdoor db # heating capacity = capacity factor (function of temp) from biquadratic curve # with curve limits on minimum y/outdoor db (no extrapolation) cooling_cap_f_temp_curve = clg_coil.totalCoolingCapacityFunctionOfTemperatureCurve cooling_cap_f_temp_factor_min_y = cooling_cap_f_temp_curve.evaluate(indoor_wb,outdoor_db) htg_cap_w_min_y = capacity_w*0.5*cooling_cap_f_temp_factor_min_y # heating capacity = capacity factor (function of temp) from biquadratic curve # without curve limits on minimum y/outdoor db (extrapolate) cooling_cap_f_temp_const = 0.867905 cooling_cap_f_temp_x = 0.0142459 cooling_cap_f_temp_x2 = 0.00055436 cooling_cap_f_temp_y = -0.0075575 cooling_cap_f_temp_y2 = 3.3e-05 cooling_cap_f_temp_xy = -0.0001918 cooling_cap_f_temp_factor_no_min_y = cooling_cap_f_temp_const + cooling_cap_f_temp_x*indoor_wb + cooling_cap_f_temp_x2*indoor_wb**2 + cooling_cap_f_temp_y*outdoor_db + cooling_cap_f_temp_y2*outdoor_db**2 + cooling_cap_f_temp_xy*indoor_wb*outdoor_db htg_cap_w_no_min_y = capacity_w*0.5*cooling_cap_f_temp_factor_no_min_y puts "capacity_w #{capacity_w}" puts "cooling_cap_f_temp_factor_no_min_y #{cooling_cap_f_temp_factor_no_min_y}" puts "cooling_cap_f_temp_factor_min_y #{cooling_cap_f_temp_factor_min_y}" puts "htg_cap_w_no_min_y #{htg_cap_w_no_min_y}" puts "htg_cap_w_min_y #{htg_cap_w_min_y}" # use actual factor from -8.3 to compute rated heating capacity unless it's < 0 if cooling_cap_f_temp_factor_no_min_y>0 htg_cap_w = htg_cap_w_no_min_y else htg_cap_w = htg_cap_w_min_y end # Hardsize rated capacity of heating coil coil_heating_dx_single_speed.setRatedTotalHeatingCapacity(htg_cap_w) return htg_cap_w else # Do not follow NECB reference HP rule; proceed as usual return super(coil_heating_dx_single_speed) end end |
#coil_heating_gas_apply_efficiency_and_curves(coil_heating_gas) ⇒ Boolean
Applies the standard efficiency ratings and typical performance curves to this object.
858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 858 def coil_heating_gas_apply_efficiency_and_curves(coil_heating_gas) successfully_set_all_properties = true # Define the search criteria search_criteria = coil_heating_gas_find_search_criteria # Get the coil capacity capacity_w = coil_heating_gas_find_capacity(coil_heating_gas) capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get # lookup properties coil_table = @standards_data['furnaces'] coil_props = model_find_object(coil_table, search_criteria, [capacity_btu_per_hr, 0.001].max, Date.today) # Check to make sure properties were found if coil_props.nil? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingGas', "For #{coil_heating_gas.name}, cannot find efficiency info using #{search_criteria}, cannot apply efficiency standard.") successfully_set_all_properties = false end # Make the plf vs plr curve plffplr_curve = model_add_curve(coil_heating_gas.model, coil_props['efffplr']) if plffplr_curve coil_heating_gas.setPartLoadFractionCorrelationCurve(plffplr_curve) else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingGas', "For #{coil_heating_gas.name}, cannot find plffplr curve, will not be set.") successfully_set_all_properties = false end # Thermal efficiency thermal_eff = coil_heating_gas_standard_minimum_thermal_efficiency(coil_heating_gas) # Set the efficiency values coil_heating_gas.setGasBurnerEfficiency(thermal_eff.to_f) return successfully_set_all_properties end |
#coil_heating_gas_find_capacity(coil_heating_gas) ⇒ Hash
find furnace capacity
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 776 def coil_heating_gas_find_capacity(coil_heating_gas) # Get the coil capacity capacity_w = nil if coil_heating_gas.nominalCapacity.is_initialized capacity_w = coil_heating_gas.nominalCapacity.get elsif coil_heating_gas.autosizedNominalCapacity.is_initialized capacity_w = coil_heating_gas.autosizedNominalCapacity.get else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingGas', "For #{coil_heating_gas.name} capacity is not available, cannot apply efficiency standard.") successfully_set_all_properties = false return successfully_set_all_properties end return capacity_w end |
#coil_heating_gas_find_search_criteria ⇒ Hash
find search criteria
763 764 765 766 767 768 769 770 771 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 763 def coil_heating_gas_find_search_criteria # Define the criteria to find the furnace properties # in the hvac standards data set. search_criteria = {} search_criteria['fluid_type'] = 'Air' search_criteria['fuel_type'] = 'Gas' return search_criteria end |
#coil_heating_gas_multi_stage_apply_efficiency_and_curves(coil_heating_gas_multi_stage) ⇒ Boolean
Applies the standard efficiency ratings and typical performance curves to this object.
1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1147 def coil_heating_gas_multi_stage_apply_efficiency_and_curves(coil_heating_gas_multi_stage) successfully_set_all_properties = true model = coil_heating_gas_multi_stage.model # get multi speed heat pump and air loop multi_speed_heat_pump = nil multi_speed_heat_pumps = model.getAirLoopHVACUnitaryHeatPumpAirToAirMultiSpeeds multi_speed_heat_pumps.sort.each do |iheat_pump| htg_coil = iheat_pump.heatingCoil if htg_coil.name.to_s.strip == coil_heating_gas_multi_stage.name.to_s.strip multi_speed_heat_pump = iheat_pump break end end airloop = multi_speed_heat_pump.airLoopHVAC.get # Define the criteria to find the properties in the hvac standards data set. search_criteria = coil_heating_gas_multi_stage_find_search_criteria(coil_heating_gas_multi_stage) fuel_type = search_criteria['fuel_type'] capacity_w = coil_heating_gas_multi_stage_find_capacity(coil_heating_gas_multi_stage) # Find system design outside air flow rate and fraction controller_oa = nil if airloop.airLoopHVACOutdoorAirSystem.is_initialized oa_system = airloop.airLoopHVACOutdoorAirSystem.get controller_oa = oa_system.getControllerOutdoorAir end min_oa_flow_rate = 0.0 oaf = 0.0 if controller_oa min_oa_flow_rate = nil if controller_oa.minimumOutdoorAirFlowRate.is_initialized min_oa_flow_rate = controller_oa.minimumOutdoorAirFlowRate.get elsif controller_oa.autosizedMinimumOutdoorAirFlowRate.is_initialized min_oa_flow_rate = controller_oa.autosizedMinimumOutdoorAirFlowRate.get end if min_oa_flow_rate then oaf = min_oa_flow_rate.to_f / airloop.autosizedDesignSupplyAirFlowRate.to_f end end # Find capacities of each of the stages and the total number of stages required based on NECB rules. # This implementation is limited to 4 stages. The capacity of stages 1-3 is set to 66 kW as stipulated # by NECB. The capacity of the 4th stage can exceed 66 kW up to the design capacity. htg_stages = coil_heating_gas_multi_stage.stages num_stages = (capacity_w / (66.0 * 1000.0) + 0.5).round max_cap = 66.0 * 1000.0 * num_stages stage_cap = [] final_num_stages = num_stages if capacity_w == 0.001 final_num_stages = 1 stage_cap[0] = capacity_w else case num_stages when 1 stage_cap[0] = capacity_w / 2.0 stage_cap[1] = 2.0 * stage_cap[0] final_num_stages = 2 else stage_cap[0] = 66.0 * 1000.0 stage_cap[1] = 2.0 * stage_cap[0] case num_stages when 2 when 3 stage_cap[2] = 3.0 * stage_cap[0] else final_num_stages = 4 stage_cap[2] = 3.0 * stage_cap[0] stage_cap[3] = max_cap end end end # Set final number of stages and create missing stages if needed for istage in 1..final_num_stages - 1 new_htg_stage = OpenStudio::Model::CoilHeatingGasMultiStageStageData.new(model) coil_heating_gas_multi_stage.addStage(new_htg_stage) end multi_speed_heat_pump.setNumberofSpeedsforHeating(final_num_stages) # Set final capacities for each of the stages. The air flow rate for each of the stages # is maintained above the outside air flow rate coil_heating_gas_multi_stage.stages[0].setNominalCapacity(stage_cap[0]) case coil_heating_gas_multi_stage.stages.size when 2 coil_heating_gas_multi_stage.stages[1].setNominalCapacity(stage_cap[1]) if oaf > 0.5 then multi_speed_heat_pump.setSpeed1SupplyAirFlowRateDuringHeatingOperation(min_oa_flow_rate) end when 3 coil_heating_gas_multi_stage.stages[1].setNominalCapacity(stage_cap[1]) coil_heating_gas_multi_stage.stages[2].setNominalCapacity(stage_cap[2]) if (oaf > 0.333) && (oaf <= 0.666) multi_speed_heat_pump.setSpeed1SupplyAirFlowRateDuringHeatingOperation(min_oa_flow_rate) elsif oaf > 0.666 multi_speed_heat_pump.setSpeed1SupplyAirFlowRateDuringHeatingOperation(min_oa_flow_rate) multi_speed_heat_pump.setSpeed2SupplyAirFlowRateDuringHeatingOperation(min_oa_flow_rate) end when 4 coil_heating_gas_multi_stage.stages[1].setNominalCapacity(stage_cap[1]) coil_heating_gas_multi_stage.stages[2].setNominalCapacity(stage_cap[2]) coil_heating_gas_multi_stage.stages[3].setNominalCapacity(stage_cap[3]) if (oaf > 0.25) && (oaf <= 0.5) multi_speed_heat_pump.setSpeed1SupplyAirFlowRateDuringHeatingOperation(min_oa_flow_rate) elsif (oaf > 0.5) && (oaf <= 0.75) multi_speed_heat_pump.setSpeed1SupplyAirFlowRateDuringHeatingOperation(min_oa_flow_rate) multi_speed_heat_pump.setSpeed2SupplyAirFlowRateDuringHeatingOperation(min_oa_flow_rate) elsif oaf > 0.75 multi_speed_heat_pump.setSpeed1SupplyAirFlowRateDuringHeatingOperation(min_oa_flow_rate) multi_speed_heat_pump.setSpeed2SupplyAirFlowRateDuringHeatingOperation(min_oa_flow_rate) multi_speed_heat_pump.setSpeed3SupplyAirFlowRateDuringHeatingOperation(min_oa_flow_rate) end end # Convert capacity to Btu/hr capacity_btu_per_hr = OpenStudio.convert(stage_cap.last, 'W', 'Btu/hr').get capacity_kbtu_per_hr = OpenStudio.convert(stage_cap.last, 'W', 'kBtu/hr').get # Lookup efficiencies heater_props = nil heater_props = model_find_object(standards_data['furnaces'], search_criteria, capacity_btu_per_hr, Date.today) # Check to make sure properties were found if heater_props.nil? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingGasMultiSpeed', "For #{coil_heating_gas_multi_stage.name}, cannot find efficiency info, cannot apply efficiency standard.") successfully_set_all_properties = false return successfully_set_all_properties end # Make the EFFPLR curve efffplr = model_add_curve(coil_heating_gas_multi_stage.model, heater_props['efffplr']) if efffplr coil_heating_gas_multi_stage.setPartLoadFractionCorrelationCurve(efffplr) else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingGasMultiStage', "For #{coil_heating_gas_multi_stage.name}, cannot find efffplr curve, will not be set.") successfully_set_all_properties = false return successfully_set_all_properties end # Get the minimum efficiency standards thermal_eff = nil # If specified as AFUE unless heater_props['minimum_annual_fuel_utilization_efficiency'].nil? min_afue = heater_props['minimum_annual_fuel_utilization_efficiency'] thermal_eff = afue_to_thermal_eff(min_afue) new_comp_name = "#{coil_heating_gas_multi_stage.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_afue} AFUE" OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilHeatingGasMultiStage', "For #{template}: #{coil_heating_gas_multi_stage.name}: #{fuel_type} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; AFUE = #{min_afue}") end # If specified as thermal efficiency unless heater_props['minimum_thermal_efficiency'].nil? thermal_eff = heater_props['minimum_thermal_efficiency'] new_comp_name = "#{coil_heating_gas_multi_stage.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{thermal_eff} Thermal Eff" OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilHeatingGasMultiStage', "For #{template}: #{coil_heating_gas_multi_stage.name}: #{fuel_type} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; Thermal Efficiency = #{thermal_eff}") end # If specified as combustion efficiency unless heater_props['minimum_combustion_efficiency'].nil? min_comb_eff = heater_props['minimum_combustion_efficiency'] thermal_eff = combustion_eff_to_thermal_eff(min_comb_eff) new_comp_name = "#{coil_heating_gas_multi_stage.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_comb_eff} Combustion Eff" OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.BoilerHotWater', "For #{template}: #{coil_heating_gas_multi_stage.name}: #{fuel_type} Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; Combustion Efficiency = #{min_comb_eff}") end coil_heating_gas_multi_stage.setName(new_comp_name) # Set the name coil_heating_gas_multi_stage.setName(new_comp_name) # Get heating stages htg_stages = coil_heating_gas_multi_stage.stages # Set the efficiency values unless thermal_eff.nil? htg_stages.sort.each do |stage| stage.setGasBurnerEfficiency(thermal_eff) end end return successfully_set_all_properties end |
#coil_heating_gas_standard_minimum_thermal_efficiency(coil_heating_gas, rename = false) ⇒ Double
Finds lookup object in standards and return minimum thermal efficiency
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 795 def coil_heating_gas_standard_minimum_thermal_efficiency(coil_heating_gas, rename = false) # Get the coil properties search_criteria = coil_heating_gas_find_search_criteria capacity_w = coil_heating_gas_find_capacity(coil_heating_gas) capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get # Get the minimum efficiency standards thermal_eff = nil # Get the coil properties coil_table = @standards_data['furnaces'] coil_props = model_find_object(coil_table, search_criteria, [capacity_btu_per_hr, 0.001].max) unless coil_props OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingGas', "For #{coil_heating_gas.name}, cannot find coil props, cannot apply efficiency standard.") successfully_set_all_properties = false return successfully_set_all_properties end # New name initial value new_comp_name = coil_heating_gas.name # If specified as AFUE unless coil_props['minimum_annual_fuel_utilization_efficiency'].nil? min_afue = coil_props['minimum_annual_fuel_utilization_efficiency'] thermal_eff = afue_to_thermal_eff(min_afue) new_comp_name = "#{coil_heating_gas.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_afue} AFUE" OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilHeatingGas', "For #{template}: #{coil_heating_gas.name}: Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; AFUE = #{min_afue}") end # If specified as thermal efficiency unless coil_props['minimum_thermal_efficiency'].nil? thermal_eff = coil_props['minimum_thermal_efficiency'] new_comp_name = "#{coil_heating_gas.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{thermal_eff} Thermal Eff" OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilHeatingGas', "For #{template}: #{coil_heating_gas.name}: Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; Thermal Efficiency = #{thermal_eff}") end # If specified as combustion efficiency unless coil_props['minimum_combustion_efficiency'].nil? min_comb_eff = coil_props['minimum_combustion_efficiency'] thermal_eff = combustion_eff_to_thermal_eff(min_comb_eff) new_comp_name = "#{coil_heating_gas.name} #{capacity_kbtu_per_hr.round}kBtu/hr #{min_comb_eff} Combustion Eff" OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.CoilHeatingGas', "For #{template}: #{coil_heating_gas.name}: Capacity = #{capacity_kbtu_per_hr.round}kBtu/hr; Combustion Efficiency = #{min_comb_eff}") end unless thermal_eff OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.CoilHeatingGas', "For #{CoilHeatingGas.name}, cannot find coil efficiency, cannot apply efficiency standard.") successfully_set_all_properties = false return successfully_set_all_properties end # Rename if rename coil_heating_gas.setName(new_comp_name) end return thermal_eff end |
#common_air_loop(model:, system_data:) ⇒ Object
2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 2112 def common_air_loop(model:, system_data:) mau_air_loop = OpenStudio::Model::AirLoopHVAC.new(model) mau_air_loop.setName(system_data[:name]) air_loop_sizing = mau_air_loop.sizingSystem air_loop_sizing.autosizeDesignOutdoorAirFlowRate air_loop_sizing.setPreheatDesignTemperature(system_data[:PreheatDesignTemperature]) unless system_data[:PreheatDesignTemperature].nil? air_loop_sizing.setPreheatDesignHumidityRatio(system_data[:PreheatDesignHumidityRatio]) unless system_data[:PreheatDesignHumidityRatio].nil? air_loop_sizing.setPrecoolDesignTemperature(system_data[:PrecoolDesignTemperature]) unless system_data[:PrecoolDesignTemperature].nil? air_loop_sizing.setPrecoolDesignHumidityRatio(system_data[:PrecoolDesignHumidityRatio]) unless system_data[:PrecoolDesignHumidityRatio].nil? air_loop_sizing.setSizingOption(system_data[:SizingOption]) unless system_data[:SizingOption].nil? air_loop_sizing.setCoolingDesignAirFlowMethod(system_data[:CoolingDesignAirFlowMethod]) unless system_data[:CoolingDesignAirFlowMethod].nil? air_loop_sizing.setCoolingDesignAirFlowRate(system_data[:CoolingDesignAirFlowRate]) unless system_data[:CoolingDesignAirFlowRate].nil? air_loop_sizing.setHeatingDesignAirFlowMethod(system_data[:HeatingDesignAirFlowMethod]) unless system_data[:HeatingDesignAirFlowMethod].nil? air_loop_sizing.setHeatingDesignAirFlowRate(system_data[:HeatingDesignAirFlowRate]) unless system_data[:HeatingDesignAirFlowRate].nil? air_loop_sizing.setSystemOutdoorAirMethod(system_data[:SystemOutdoorAirMethod]) unless system_data[:SystemOutdoorAirMethod].nil? air_loop_sizing.setCentralCoolingDesignSupplyAirHumidityRatio(system_data[:CentralCoolingDesignSupplyAirHumidityRatio]) unless system_data[:CentralCoolingDesignSupplyAirHumidityRatio].nil? air_loop_sizing.setCentralHeatingDesignSupplyAirHumidityRatio(system_data[:CentralHeatingDesignSupplyAirHumidityRatio]) unless system_data[:CentralHeatingDesignSupplyAirHumidityRatio].nil? air_loop_sizing.setTypeofLoadtoSizeOn(system_data[:TypeofLoadtoSizeOn]) unless system_data[:TypeofLoadtoSizeOn].nil? air_loop_sizing.setCentralCoolingDesignSupplyAirTemperature(system_data[:CentralCoolingDesignSupplyAirTemperature]) unless system_data[:CentralCoolingDesignSupplyAirTemperature].nil? air_loop_sizing.setCentralHeatingDesignSupplyAirTemperature(system_data[:CentralHeatingDesignSupplyAirTemperature]) unless system_data[:CentralHeatingDesignSupplyAirTemperature].nil? air_loop_sizing.setAllOutdoorAirinCooling(system_data[:AllOutdoorAirinCooling]) unless system_data[:AllOutdoorAirinCooling].nil? air_loop_sizing.setAllOutdoorAirinHeating(system_data[:AllOutdoorAirinHeating]) unless system_data[:AllOutdoorAirinHeating].nil? if model.version < OpenStudio::VersionString.new('2.7.0') air_loop_sizing.setMinimumSystemAirFlowRatio(system_data[:MinimumSystemAirFlowRatio]) unless system_data[:MinimumSystemAirFlowRatio].nil? else air_loop_sizing.setCentralHeatingMaximumSystemAirFlowRatio(system_data[:MinimumSystemAirFlowRatio]) unless system_data[:MinimumSystemAirFlowRatio].nil? end return mau_air_loop end |
#convert_arg_to_bool(variable:, default:) ⇒ Object
This method converts arguments to bool. Anything other than a bool false or string ‘false’ is converted to a bool true. Bool false and case insesitive string false are turned into bool false.
28 29 30 31 32 33 34 35 36 37 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 28 def convert_arg_to_bool(variable:, default:) return default if variable.nil? if variable.is_a? String return default if variable.to_s.downcase == 'necb_default' return false if variable.to_s.downcase == 'false' return true if variable.to_s.downcase == 'true' end return false if variable == false return variable end |
#convert_arg_to_f(variable:, default:) ⇒ Object
This is a helper method to convert arguments that may support ‘NECB_Default, and nils to convert to float’
17 18 19 20 21 22 23 24 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 17 def convert_arg_to_f(variable:, default:) return variable if variable.kind_of?(Numeric) return default if variable.nil? || (variable.to_s == 'NECB_Default') return unless variable.kind_of?(String) variable = variable.strip return variable.to_f end |
#convert_arg_to_string(variable:, default:) ⇒ Object
This method checks if a variable is a string. If it is anything but a string it returns the default. If it is a string set to “NECB_Default” it return the default. Otherwise it returns the strirng set to it.
41 42 43 44 45 46 47 48 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 41 def convert_arg_to_string(variable:, default:) return default if variable.nil? if variable.is_a? String return default if variable.to_s.downcase == 'necb_default' return variable end return default end |
#corrupt_standards_database ⇒ Object
2019-05-23 ckirney This is an ugly, disgusting, hack (hence the name) that I dreamed out so that we could quickly and easily finish the merge from the nrcan branch (using OS 2.6.0) to master (using OS 2.8.0). This must be revised and a more elegant solution found.
This method takes everything in the @standards_data hash and adds it to the main @standards_data hash. This was done because other contributors insist on using the ‘model_find_object’ method which is passed a hash and some search criteria. The ‘model_find_objects’ then looks through the hash to information matching the search criteria. NECB standards assumes that the ‘standards_lookup_table_first’ method is used. This does basically the some thing as ‘model_find_objects’ only it assumes that you are looking in the standards hash and you tell it which table in the standards hash to look for.
1323 1324 1325 1326 1327 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1323 def corrupt_standards_database @standards_data['tables'].each do |table| @standards_data[table[0]] = table[1]['table'] end end |
#create_base_data(model) ⇒ Object
Generates the base data hash mainly used to perform qaqc.
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 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 183 def create_base_data(model) # construct command with local libs os_version = OpenStudio.openStudioLongVersion eplus_version = OpenStudio.energyPlusVersion puts "\n\n\nOS_version is [#{os_version.strip}]" puts "\n\n\nEP_version is [#{eplus_version.strip}]" # Ensure all surfaces are unique. surfaces = model.getSurfaces.sort # Sort surfaces by type interior_surfaces = BTAP::Geometry::Surfaces.filter_by_boundary_condition(surfaces, ['Surface', 'Adiabatic']) interior_floors = BTAP::Geometry::Surfaces.filter_by_surface_types(interior_surfaces, 'Floor') outdoor_surfaces = BTAP::Geometry::Surfaces.filter_by_boundary_condition(surfaces, 'Outdoors') outdoor_walls = BTAP::Geometry::Surfaces.filter_by_surface_types(outdoor_surfaces, 'Wall') outdoor_roofs = BTAP::Geometry::Surfaces.filter_by_surface_types(outdoor_surfaces, 'RoofCeiling') outdoor_floors = BTAP::Geometry::Surfaces.filter_by_surface_types(outdoor_surfaces, 'Floor') outdoor_subsurfaces = outdoor_surfaces.flat_map(&:subSurfaces) ground_surfaces = BTAP::Geometry::Surfaces.filter_by_boundary_condition(surfaces, ['Ground', 'Foundation']) ground_walls = BTAP::Geometry::Surfaces.filter_by_surface_types(ground_surfaces, 'Wall') ground_roofs = BTAP::Geometry::Surfaces.filter_by_surface_types(ground_surfaces, 'RoofCeiling') ground_floors = BTAP::Geometry::Surfaces.filter_by_surface_types(ground_surfaces, 'Floor') windows = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(outdoor_subsurfaces, ['FixedWindow', 'OperableWindow']) skylights = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(outdoor_subsurfaces, ['Skylight', 'TubularDaylightDiffuser', 'TubularDaylightDome']) doors = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(outdoor_subsurfaces, ['Door', 'GlassDoor']) overhead_doors = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(outdoor_subsurfaces, ['OverheadDoor']) # Peaks electric_peak = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters'" \ " AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - Electricity' AND RowName='Electricity:Facility'" \ " AND ColumnName='Electricity Maximum Value' AND Units='W'") natural_gas_peak = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters'" \ " AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - Natural Gas' AND RowName='NaturalGas:Facility'" \ " AND ColumnName='Natural Gas Maximum Value' AND Units='W'") get_sql_tables_to_json(model) # Create hash to store all the collected data. qaqc = {} qaqc[:sql_data] = get_sql_tables_to_json(model) error_warning = [] qaqc[:os_standards_revision] = OpenstudioStandards.git_revision qaqc[:os_standards_version] = OpenstudioStandards::VERSION qaqc[:openstudio_version] = os_version.strip qaqc[:energyplus_version] = eplus_version.strip # Store Building data. qaqc[:building] = {} qaqc[:building][:name] = model.building.get.name.get qaqc[:building][:conditioned_floor_area_m2] = nil if model.building.get.conditionedFloorArea.empty? error_warning << "model.building.get.conditionedFloorArea() is empty for #{model.building.get.name.get}" else qaqc[:building][:conditioned_floor_area_m2] = model.building.get.conditionedFloorArea.get end qaqc[:building][:exterior_area_m2] = model.building.get.exteriorSurfaceArea # m2 qaqc[:building][:volume] = model.building.get.airVolume # m3 qaqc[:building][:number_of_stories] = model.getBuildingStorys.size qaqc[:building][:standards_number_of_stories] = nil qaqc[:building][:standards_number_of_stories] = model.building.get.standardsNumberOfStories.get unless model.building.get.standardsNumberOfStories.empty? qaqc[:building][:standards_number_of_above_ground_stories] = nil qaqc[:building][:standards_number_of_above_ground_stories] = model.building.get.standardsNumberOfAboveGroundStories.get unless model.building.get.standardsNumberOfAboveGroundStories.empty? qaqc[:building][:standards_number_of_living_units] = nil qaqc[:building][:standards_number_of_living_units] = model.building.get.standardsNumberOfLivingUnits.get unless model.building.get.standardsNumberOfLivingUnits.empty? qaqc[:building][:nominal_floor_to_ceiling_height] = nil qaqc[:building][:nominal_floor_to_ceiling_height] = model.building.get.nominalFloortoCeilingHeight.get unless model.building.get.nominalFloortoCeilingHeight.empty? qaqc[:building][:nominal_floor_to_floor_height] = nil qaqc[:building][:nominal_floor_to_floor_height] = model.building.get.nominalFloortoFloorHeight.get unless model.building.get.nominalFloortoFloorHeight.empty? # Store Geography Data qaqc[:geography] = {} qaqc[:geography][:hdd_necb] = get_necb_hdd18(model: model, necb_hdd: true) qaqc[:geography][:hdd] = get_necb_hdd18(model: model, necb_hdd: false) weather_file_path = model.weatherFile.get.path.get.to_s stat_file_path = weather_file_path.gsub('.epw', '.stat') stat_file = OpenstudioStandards::Weather::StatFile.new(stat_file_path) qaqc[:geography][:cdd] = stat_file.cdd18 qaqc[:geography][:climate_zone] = NECB2011.new.get_climate_zone_name(qaqc[:geography][:hdd]) qaqc[:geography][:city] = model.getWeatherFile.city qaqc[:geography][:state_province_region] = model.getWeatherFile.stateProvinceRegion qaqc[:geography][:country] = model.getWeatherFile.country qaqc[:geography][:latitude] = model.getWeatherFile.latitude qaqc[:geography][:longitude] = model.getWeatherFile.longitude # Spacetype Breakdown qaqc[:spacetype_area_breakdown] = {} model.getSpaceTypes.sort.each do |spaceType| next if spaceType.floorArea == 0 # data for space type breakdown display = spaceType.name.get floor_area_si = 0 # loop through spaces so I can skip if not included in floor area spaceType.spaces.sort.each do |space| next if !space.partofTotalFloorArea floor_area_si += space.floorArea * space.multiplier end qaqc[:spacetype_area_breakdown][spaceType.name.get.gsub(/\s+/, '_').downcase.to_sym] = floor_area_si end # Economics Section qaqc[:economics] = {} provinces_names_map = { 'QC' => 'Quebec', 'NL' => 'Newfoundland and Labrador', 'NS' => 'Nova Scotia', 'PE' => 'Prince Edward Island', 'ON' => 'Ontario', 'MB' => 'Manitoba', 'SK' => 'Saskatchewan', 'AB' => 'Alberta', 'BC' => 'British Columbia', 'YT' => 'Yukon', 'NT' => 'Northwest Territories', 'NB' => 'New Brunswick', 'NU' => 'Nunavut' } neb_prices_csv_file_name = "#{File.dirname(__FILE__)}/qaqc_resources/neb_end_use_prices.csv" puts neb_prices_csv_file_name building_type = 'Commercial' province = provinces_names_map[qaqc[:geography][:state_province_region]] neb_fuel_list = ['Electricity', 'Natural Gas', 'Oil'] neb_eplus_fuel_map = { 'Electricity' => 'Electricity', 'Natural Gas' => 'Gas', 'Oil' => 'FuelOilNo2' } qaqc[:economics][:total_neb_cost] = 0.0 qaqc[:economics][:total_neb_cost_per_m2] = 0.0 neb_eplus_fuel_map.each do |neb_fuel, ep_fuel| search_info = { 0 => building_type, 1 => province, 2 => neb_fuel } row = look_up_csv_data(neb_prices_csv_file_name, search_info) neb_fuel_cost = row['2020'] fuel_consumption_gj = 0.0 if neb_fuel == 'Electricity' || neb_fuel == 'Natural Gas' if model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters' AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - #{ep_fuel}' AND RowName='#{ep_fuel}:Facility' AND ColumnName='#{ep_fuel} Annual Value' AND Units='GJ'").is_initialized fuel_consumption_gj = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters' AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - #{ep_fuel}' AND RowName='#{ep_fuel}:Facility' AND ColumnName='#{ep_fuel} Annual Value' AND Units='GJ'").get end else if model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters' AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - Other' AND RowName='#{ep_fuel}:Facility' AND ColumnName='Annual Value' AND Units='GJ'").is_initialized fuel_consumption_gj = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='EnergyMeters' AND ReportForString='Entire Facility' AND TableName='Annual and Peak Values - Other' AND RowName='#{ep_fuel}:Facility' AND ColumnName='Annual Value' AND Units='GJ'").get end end qaqc[:economics][:"#{neb_fuel}_neb_cost"] = fuel_consumption_gj * neb_fuel_cost.to_f qaqc[:economics][:"#{neb_fuel}_neb_cost_per_m2"] = qaqc[:economics][:"#{neb_fuel}_neb_cost"] / qaqc[:building][:conditioned_floor_area_m2] unless model.building.get.conditionedFloorArea.empty? qaqc[:economics][:total_neb_cost] += qaqc[:economics][:"#{neb_fuel}_neb_cost"] qaqc[:economics][:total_neb_cost_per_m2] += qaqc[:economics][:"#{neb_fuel}_neb_cost_per_m2"] || 0.0 end # Fuel cost based local utility rates costing_rownames = model.sqlFile.get.execAndReturnVectorOfString("SELECT RowName FROM TabularDataWithStrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-7. Energy Cost Summary' AND ColumnName='Total Energy Cost'") #==> ["Electricity", "Natural Gas", "Additional", "Total"] costing_rownames = validate_optional(costing_rownames, model, 'N/A') if costing_rownames != 'N/A' costing_rownames.each do |rowname| case rowname when 'Electricity' qaqc[:economics][:electricity_cost] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-7. Energy Cost Summary' AND ColumnName='Total Energy Cost' AND RowName='#{rowname}'").get qaqc[:economics][:electricity_cost_per_m2] = qaqc[:economics][:electricity_cost] / qaqc[:building][:conditioned_floor_area_m2] unless model.building.get.conditionedFloorArea.empty? when 'Natural Gas' qaqc[:economics][:natural_gas_cost] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-7. Energy Cost Summary' AND ColumnName='Total Energy Cost' AND RowName='#{rowname}'").get qaqc[:economics][:natural_gas_cost_per_m2] = qaqc[:economics][:natural_gas_cost] / qaqc[:building][:conditioned_floor_area_m2] unless model.building.get.conditionedFloorArea.empty? when 'Additional' qaqc[:economics][:additional_cost] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-7. Energy Cost Summary' AND ColumnName='Total Energy Cost' AND RowName='#{rowname}'").get qaqc[:economics][:additional_cost_per_m2] = qaqc[:economics][:additional_cost] / qaqc[:building][:conditioned_floor_area_m2] unless model.building.get.conditionedFloorArea.empty? when 'Total' qaqc[:economics][:total_cost] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-7. Energy Cost Summary' AND ColumnName='Total Energy Cost' AND RowName='#{rowname}'").get qaqc[:economics][:total_cost_per_m2] = qaqc[:economics][:total_cost] / qaqc[:building][:conditioned_floor_area_m2] unless model.building.get.conditionedFloorArea.empty? end end else error_warning << "costing is unavailable because the sql statement is nil RowName FROM TabularDataWithStrings WHERE ReportName='LEEDsummary' AND ReportForString='Entire Facility' AND TableName='EAp2-7. Energy Cost Summary' AND ColumnName='Total Energy Cost'" end # Store end_use data end_uses = [ 'Heating', 'Cooling', 'Interior Lighting', 'Exterior Lighting', 'Interior Equipment', 'Exterior Equipment', 'Fans', 'Pumps', 'Heat Rejection', 'Humidification', 'Heat Recovery', 'Water Systems', 'Refrigeration', 'Generators', 'Total End Uses' ] fuels = [ ['Electricity', 'GJ'], ['Natural Gas', 'GJ'], ['Additional Fuel', 'GJ'], ['District Cooling', 'GJ'], ['District Heating', 'GJ'] ] qaqc[:end_uses] = {} qaqc[:end_uses_eui] = {} end_uses.each do |use_type| qaqc[:end_uses]["#{use_type.gsub(/\s+/, '_').downcase.to_sym}_gj"] = 0 qaqc[:end_uses_eui]["#{use_type.gsub(/\s+/, '_').downcase.to_sym}_gj_per_m2"] = 0 fuels.each do |fuel_type| value = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND RowName='#{use_type}' AND ColumnName='#{fuel_type[0]}' AND Units='#{fuel_type[1]}'") if value.empty? || (value.get == 0) else qaqc[:end_uses]["#{use_type.gsub(/\s+/, '_').downcase.to_sym}_gj"] += value.get unless qaqc[:building][:conditioned_floor_area_m2].nil? qaqc[:end_uses_eui]["#{use_type.gsub(/\s+/, '_').downcase.to_sym}_gj_per_m2"] += value.get / qaqc[:building][:conditioned_floor_area_m2] end end end value = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM tabulardatawithstrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND RowName='#{use_type}' AND ColumnName='Water' AND Units='m3'") if value.empty? || (value.get == 0) else qaqc[:end_uses]["#{use_type.gsub(/\s+/, '_').downcase.to_sym}_water_m3"] = value.get unless qaqc[:building][:conditioned_floor_area_m2].nil? qaqc[:end_uses_eui]["#{use_type.gsub(/\s+/, '_').downcase.to_sym}_water_m3_per_m2"] = value.get / qaqc[:building][:conditioned_floor_area_m2] end end end # Store Peak Data qaqc[:meter_peaks] = {} qaqc[:meter_peaks][:electric_w] = electric_peak.empty? ? 'NA' : electric_peak.get qaqc[:meter_peaks][:natural_gas_w] = natural_gas_peak.empty? ? 'NA' : natural_gas_peak.get # Store unmet hour data qaqc[:unmet_hours] = {} qaqc[:unmet_hours][:cooling] = model.getFacility.hoursCoolingSetpointNotMet.get unless model.getFacility.hoursCoolingSetpointNotMet.empty? qaqc[:unmet_hours][:heating] = model.getFacility.hoursHeatingSetpointNotMet.get unless model.getFacility.hoursHeatingSetpointNotMet.empty? # puts "\n\n\n#{costing_rownames}\n\n\n" # Padmassun's Code -- Tarrif end # Padmassun's Code -- Service Hotwater Heating *start* qaqc[:service_water_heating] = {} qaqc[:service_water_heating][:total_nominal_occupancy] = -1 # qaqc[:service_water_heating][:total_nominal_occupancy]=model.sqlFile().get().execAndReturnVectorOfDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='OutdoorAirSummary' AND ReportForString='Entire Facility' AND TableName='Average Outdoor Air During Occupied Hours' AND ColumnName='Nominal Number of Occupants'").get.inject(0, :+) qaqc[:service_water_heating][:total_nominal_occupancy] = get_total_nominal_capacity(model) qaqc[:service_water_heating][:electricity_per_year] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND ColumnName='Electricity' AND RowName='Water Systems'") qaqc[:service_water_heating][:electricity_per_year] = validate_optional(qaqc[:service_water_heating][:electricity_per_year], model, -1) qaqc[:service_water_heating][:electricity_per_day] = qaqc[:service_water_heating][:electricity_per_year] / 365.5 qaqc[:service_water_heating][:electricity_per_day_per_occupant] = qaqc[:service_water_heating][:electricity_per_day] / qaqc[:service_water_heating][:total_nominal_occupancy] qaqc[:service_water_heating][:natural_gas_per_year] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND ColumnName='Natural Gas' AND RowName='Water Systems'") qaqc[:service_water_heating][:natural_gas_per_year] = validate_optional(qaqc[:service_water_heating][:natural_gas_per_year], model, -1) qaqc[:service_water_heating][:additional_fuel_per_year] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND ColumnName='Additional Fuel' AND RowName='Water Systems'") qaqc[:service_water_heating][:additional_fuel_per_year] = validate_optional(qaqc[:service_water_heating][:additional_fuel_per_year], model, -1) qaqc[:service_water_heating][:water_m3_per_year] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='AnnualBuildingUtilityPerformanceSummary' AND ReportForString='Entire Facility' AND TableName='End Uses' AND ColumnName='Water' AND RowName='Water Systems'") qaqc[:service_water_heating][:water_m3_per_year] = validate_optional(qaqc[:service_water_heating][:water_m3_per_year], model, -1) qaqc[:service_water_heating][:water_m3_per_day] = qaqc[:service_water_heating][:water_m3_per_year] / 365.5 qaqc[:service_water_heating][:water_m3_per_day_per_occupant] = qaqc[:service_water_heating][:water_m3_per_day] / qaqc[:service_water_heating][:total_nominal_occupancy] # puts qaqc[:service_water_heating][:total_nominal_occupancy] # Padmassun's Code -- Service Hotwater Heating *end* # Store Envelope data. qaqc[:envelope] = {} # Get Areas qaqc[:envelope][:outdoor_walls_area_m2] = outdoor_walls.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } qaqc[:envelope][:outdoor_roofs_area_m2] = outdoor_roofs.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } qaqc[:envelope][:outdoor_floors_area_m2] = outdoor_floors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } qaqc[:envelope][:ground_walls_area_m2] = ground_walls.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } qaqc[:envelope][:ground_roofs_area_m2] = ground_roofs.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } qaqc[:envelope][:ground_floors_area_m2] = ground_floors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } qaqc[:envelope][:interior_floors_area_m2] = interior_floors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier } # Subsurface areas qaqc[:envelope][:windows_area_m2] = windows.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier * e.multiplier } qaqc[:envelope][:skylights_area_m2] = skylights.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier * e.multiplier } qaqc[:envelope][:doors_area_m2] = doors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier * e.multiplier } qaqc[:envelope][:overhead_doors_area_m2] = overhead_doors.inject(0) { |sum, e| sum + e.netArea * e.space.get.multiplier * e.multiplier } # Total Building Surface Area. qaqc[:envelope][:total_exterior_area_m2] = qaqc[:envelope][:outdoor_walls_area_m2] + qaqc[:envelope][:outdoor_roofs_area_m2] + qaqc[:envelope][:outdoor_floors_area_m2] + qaqc[:envelope][:ground_walls_area_m2] + qaqc[:envelope][:ground_roofs_area_m2] + qaqc[:envelope][:ground_floors_area_m2] + qaqc[:envelope][:windows_area_m2] + qaqc[:envelope][:skylights_area_m2] + qaqc[:envelope][:doors_area_m2] + qaqc[:envelope][:overhead_doors_area_m2] # Total Building Ground Surface Area. qaqc[:envelope][:total_ground_area_m2] = qaqc[:envelope][:ground_walls_area_m2] + qaqc[:envelope][:ground_roofs_area_m2] + qaqc[:envelope][:ground_floors_area_m2] # Total Building Outdoor Surface Area. qaqc[:envelope][:total_outdoor_area_m2] = qaqc[:envelope][:outdoor_walls_area_m2] + qaqc[:envelope][:outdoor_roofs_area_m2] + qaqc[:envelope][:outdoor_floors_area_m2] + qaqc[:envelope][:windows_area_m2] + qaqc[:envelope][:skylights_area_m2] + qaqc[:envelope][:doors_area_m2] + qaqc[:envelope][:overhead_doors_area_m2] # Average Conductances by surface Type qaqc[:envelope][:outdoor_walls_average_conductance_w_per_m2_k] = OpenstudioStandards::Constructions.surfaces_get_conductance(outdoor_walls).round(4) if !outdoor_walls.empty? qaqc[:envelope][:outdoor_roofs_average_conductance_w_per_m2_k] = OpenstudioStandards::Constructions.surfaces_get_conductance(outdoor_roofs).round(4) if !outdoor_roofs.empty? qaqc[:envelope][:outdoor_floors_average_conductance_w_per_m2_k] = OpenstudioStandards::Constructions.surfaces_get_conductance(outdoor_floors).round(4) if !outdoor_floors.empty? qaqc[:envelope][:ground_walls_average_conductance_w_per_m2_k] = OpenstudioStandards::Constructions.surfaces_get_conductance(ground_walls).round(4) if !ground_walls.empty? qaqc[:envelope][:ground_roofs_average_conductance_w_per_m2_k] = OpenstudioStandards::Constructions.surfaces_get_conductance(ground_roofs).round(4) if !ground_roofs.empty? qaqc[:envelope][:ground_floors_average_conductance_w_per_m2_k] = OpenstudioStandards::Constructions.surfaces_get_conductance(ground_floors).round(4) if !ground_floors.empty? qaqc[:envelope][:windows_average_conductance_w_per_m2_k] = OpenstudioStandards::Constructions.surfaces_get_conductance(windows).round(4) if !windows.empty? qaqc[:envelope][:skylights_average_conductance_w_per_m2_k] = OpenstudioStandards::Constructions.surfaces_get_conductance(skylights).round(4) if !skylights.empty? qaqc[:envelope][:doors_average_conductance_w_per_m2_k] = OpenstudioStandards::Constructions.surfaces_get_conductance(doors).round(4) if !doors.empty? qaqc[:envelope][:overhead_doors_average_conductance_w_per_m2_k] = OpenstudioStandards::Constructions.surfaces_get_conductance(overhead_doors).round(4) if !overhead_doors.empty? # #Average Conductances for building whole weight factors !outdoor_walls.empty? ? o_wall_cond_weight = qaqc[:envelope][:outdoor_walls_average_conductance_w_per_m2_k] * qaqc[:envelope][:outdoor_walls_area_m2] : o_wall_cond_weight = 0 !outdoor_roofs.empty? ? o_roof_cond_weight = qaqc[:envelope][:outdoor_roofs_average_conductance_w_per_m2_k] * qaqc[:envelope][:outdoor_roofs_area_m2] : o_roof_cond_weight = 0 !outdoor_floors.empty? ? o_floor_cond_weight = qaqc[:envelope][:outdoor_floors_average_conductance_w_per_m2_k] * qaqc[:envelope][:outdoor_floors_area_m2] : o_floor_cond_weight = 0 !ground_walls.empty? ? g_wall_cond_weight = qaqc[:envelope][:ground_walls_average_conductance_w_per_m2_k] * qaqc[:envelope][:ground_walls_area_m2] : g_wall_cond_weight = 0 !ground_roofs.empty? ? g_roof_cond_weight = qaqc[:envelope][:ground_roofs_average_conductance_w_per_m2_k] * qaqc[:envelope][:ground_roofs_area_m2] : g_roof_cond_weight = 0 !ground_floors.empty? ? g_floor_cond_weight = qaqc[:envelope][:ground_floors_average_conductance_w_per_m2_k] * qaqc[:envelope][:ground_floors_area_m2] : g_floor_cond_weight = 0 !windows.empty? ? win_cond_weight = qaqc[:envelope][:windows_average_conductance_w_per_m2_k] * qaqc[:envelope][:windows_area_m2] : win_cond_weight = 0 # doors.size > 0 ? sky_cond_weight = qaqc[:envelope][:skylights_average_conductance_w_per_m2_k] * qaqc[:envelope][:skylights_area_m2] : sky_cond_weight = 0 if !doors.empty? && !qaqc[:envelope][:skylights_average_conductance_w_per_m2_k].nil? && !qaqc[:envelope][:skylights_area_m2].nil? sky_cond_weight = qaqc[:envelope][:skylights_average_conductance_w_per_m2_k] * qaqc[:envelope][:skylights_area_m2] else sky_cond_weight = 0 end !overhead_doors.empty? ? door_cond_weight = qaqc[:envelope][:doors_average_conductance_w_per_m2_k] * qaqc[:envelope][:doors_area_m2] : door_cond_weight = 0 !overhead_doors.empty? ? overhead_door_cond_weight = qaqc[:envelope][:overhead_doors_average_conductance_w_per_m2_k] * qaqc[:envelope][:overhead_doors_area_m2] : overhead_door_cond_weight = 0 # Building Average Conductance qaqc[:envelope][:building_outdoor_average_conductance_w_per_m2_k] = ( o_floor_cond_weight + o_roof_cond_weight + o_wall_cond_weight + win_cond_weight + sky_cond_weight + door_cond_weight + overhead_door_cond_weight) / qaqc[:envelope][:total_outdoor_area_m2] # Building Average Ground Conductance qaqc[:envelope][:building_ground_average_conductance_w_per_m2_k] = ( g_floor_cond_weight + g_roof_cond_weight + g_wall_cond_weight) / qaqc[:envelope][:total_ground_area_m2] # Building Average Conductance qaqc[:envelope][:building_average_conductance_w_per_m2_k] = ( (qaqc[:envelope][:building_ground_average_conductance_w_per_m2_k] * qaqc[:envelope][:total_ground_area_m2]) + (qaqc[:envelope][:building_outdoor_average_conductance_w_per_m2_k] * qaqc[:envelope][:total_outdoor_area_m2]) ) / (qaqc[:envelope][:total_ground_area_m2] + qaqc[:envelope][:total_outdoor_area_m2]) qaqc[:envelope][:fdwr] = (BTAP::Geometry.get_fwdr(model) * 100.0).round(1) qaqc[:envelope][:srr] = (BTAP::Geometry.get_srr(model) * 100.0).round(1) qaqc[:envelope][:constructions] = {} qaqc[:envelope][:constructions][:exterior_fenestration] = [] constructions = [] outdoor_subsurfaces.each { |surface| constructions << surface.construction.get } ext_const_base = Hash.new(0) constructions.each { |name| ext_const_base[name.to_Construction.get] += 1 } # iterate thought each construction and get store data ext_const_base.sort.each do |construction, count| construction_info = {} qaqc[:envelope][:constructions][:exterior_fenestration] << construction_info construction_info[:name] = construction.name.get construction_info[:net_area_m2] = construction.getNetArea.round(2) construction_info[:thermal_conductance_m2_w_per_k] = OpenstudioStandards::Constructions.construction_get_conductance(construction).round(3) construction_info[:solar_transmittance] = OpenstudioStandards::Constructions.construction_get_solar_transmittance(construction).round(3) construction_info[:visible_tranmittance] = OpenstudioStandards::Constructions.construction_get_visible_transmittance(construction).round(3) end # Exterior qaqc[:envelope][:constructions][:exterior_opaque] = [] constructions = [] outdoor_surfaces.each { |surface| constructions << surface.construction.get } ext_const_base = Hash.new(0) constructions.each { |name| ext_const_base[name.to_Construction.get] += 1 } # iterate thought each construction and get store data ext_const_base.sort.each do |construction, count| construction_info = {} qaqc[:envelope][:constructions][:exterior_opaque] << construction_info construction_info[:name] = construction.name.get construction_info[:net_area_m2] = construction.getNetArea.round(2) construction_info[:thermal_conductance_m2_w_per_k] = construction.thermalConductance.get.round(3) if construction.thermalConductance.is_initialized construction_info[:net_area_m2] = construction.to_Construction.get.getNetArea.round(2) construction_info[:solar_absorptance] = construction.to_Construction.get.layers[0].exteriorVisibleAbsorptance.get end # Ground qaqc[:envelope][:constructions][:ground] = [] constructions = [] ground_surfaces.each { |surface| constructions << surface.construction.get } ext_const_base = Hash.new(0) constructions.each { |name| ext_const_base[name.to_Construction.get] += 1 } # iterate thought each construction and get store data ext_const_base.sort.each do |construction, count| construction_info = {} qaqc[:envelope][:constructions][:ground] << construction_info construction_info[:name] = construction.name.get construction_info[:net_area_m2] = construction.getNetArea.round(2) construction_info[:thermal_conductance_m2_w_per_k] = construction.thermalConductance.get.round(3) if construction.thermalConductance.is_initialized construction_info[:net_area_m2] = construction.to_Construction.get.getNetArea.round(2) construction_info[:solar_absorptance] = construction.to_Construction.get.layers[0].exteriorVisibleAbsorptance.get end qaqc[:envelope][:average_thermal_conductance_m2_w_per_k] = # Store Space data. qaqc[:spaces] = [] model.getSpaces.sort.each do |space| spaceinfo = {} qaqc[:spaces] << spaceinfo spaceinfo[:name] = space.name.get # name should be defined test spaceinfo[:multiplier] = space.multiplier spaceinfo[:volume] = space.volume # should be greater than zero spaceinfo[:exterior_wall_area] = space.exteriorWallArea # just for information. spaceinfo[:space_type_name] = space.spaceType.get.name.get unless space.spaceType.empty? # should have a space types name defined. spaceinfo[:thermal_zone] = space.thermalZone.get.name.get unless space.thermalZone.empty? # should be assigned a thermalzone name. # puts space.name.get # puts space.thermalZone.empty? spaceinfo[:breathing_zone_outdoor_airflow_vbz] = -1 breathing_zone_outdoor_airflow_vbz = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='Standard62.1Summary' AND ReportForString='Entire Facility' AND TableName='Zone Ventilation Parameters' AND ColumnName='Breathing Zone Outdoor Airflow - Vbz' AND Units='m3/s' AND RowName='#{spaceinfo[:thermal_zone].to_s.upcase}' ") spaceinfo[:breathing_zone_outdoor_airflow_vbz] = breathing_zone_outdoor_airflow_vbz.get unless breathing_zone_outdoor_airflow_vbz.empty? spaceinfo[:infiltration_method] = 'N/A' spaceinfo[:infiltration_flow_per_m2] = -1.0 if space.spaceInfiltrationDesignFlowRates[0].nil? error_warning << "space.spaceInfiltrationDesignFlowRates[0] is empty for #{spaceinfo[:name]}" error_warning << "space.spaceInfiltrationDesignFlowRates[0].designFlowRateCalculationMethod is empty for #{spaceinfo[:name]}" error_warning << "space.spaceInfiltrationDesignFlowRates[0].flowperExteriorSurfaceArea is empty for #{spaceinfo[:name]}" else spaceinfo[:infiltration_method] = space.spaceInfiltrationDesignFlowRates[0].designFlowRateCalculationMethod spaceinfo[:infiltration_flow_per_m2] = 'N/A' spaceinfo[:infiltration_flow_per_m2] = space.spaceInfiltrationDesignFlowRates[0].flowperExteriorSurfaceArea.get.round(5) unless space.spaceInfiltrationDesignFlowRates[0].flowperExteriorSurfaceArea.empty? end # the following should have values unless the spacetype is "undefined" other they should be set to the correct NECB values. if space.spaceType.empty? error_warning << "space.spaceType is empty for #{space.name.get}" else spaceinfo[:occupancy_schedule] = nil if space.spaceType.get.defaultScheduleSet.empty? error_warning << "space.spaceType.get.defaultScheduleSet is empty for #{space.name.get}" else if space.spaceType.get.defaultScheduleSet.get.numberofPeopleSchedule.empty? error_warning << "space.spaceType.get.defaultScheduleSet.get.numberofPeopleSchedule is empty for #{space.name.get}" else spaceinfo[:occupancy_schedule] = space.spaceType.get.defaultScheduleSet.get.numberofPeopleSchedule.get.name.get # should not empty. end end spaceinfo[:occ_per_m2] = space.spaceType.get.people[0].peopleDefinition.peopleperSpaceFloorArea.get.round(3) unless space.spaceType.get.people[0].nil? || space.spaceType.get.people[0].peopleDefinition.peopleperSpaceFloorArea.empty? if space.spaceType.get.lights[0].nil? error_warning << "space.spaceType.get.lights[0] is nil for Space:[#{space.name.get}] Space Type:[#{spaceinfo[:space_type_name]}]" else spaceinfo[:lighting_w_per_m2] = space.spaceType.get.lights[0].lightsDefinition.wattsperSpaceFloorArea # .get.round(3) unless space.spaceType.get.lights[0].nil? spaceinfo[:lighting_w_per_m2] = validate_optional(spaceinfo[:lighting_w_per_m2], model, -1.0) unless spaceinfo[:lighting_w_per_m2].nil? spaceinfo[:lighting_w_per_m2] = spaceinfo[:lighting_w_per_m2].round(3) end end # spaceinfo[:electric_w_per_m2] = space.spaceType.get.electricEquipment[0].electricEquipmentDefinition.wattsperSpaceFloorArea.get.round(3) unless space.spaceType.get.electricEquipment[0].nil? unless space.spaceType.get.electricEquipment[0].nil? unless space.spaceType.get.electricEquipment[0].electricEquipmentDefinition.wattsperSpaceFloorArea.empty? spaceinfo[:electric_w_per_m2] = space.spaceType.get.electricEquipment[0].electricEquipmentDefinition.wattsperSpaceFloorArea.get.round(3) end end spaceinfo[:shw_m3_per_s] = space.waterUseEquipment[0].waterUseEquipmentDefinition.peakFlowRate.round(3) unless space.waterUseEquipment[0].nil? spaceinfo[:waterUseEquipment] = [] if !space.waterUseEquipment.empty? waterUseEquipment_info = {} spaceinfo[:waterUseEquipment] << waterUseEquipment_info waterUseEquipment_info[:peak_flow_rate] = space.waterUseEquipment[0].waterUseEquipmentDefinition.peakFlowRate waterUseEquipment_info[:peak_flow_rate_per_area] = waterUseEquipment_info[:peak_flow_rate] / space.floorArea area_per_occ = space.spaceType.get.people[0].nil? ? 0.0 : validate_optional(space.spaceType.get.people[0].spaceFloorAreaPerPerson, model, -1.0) # Watt per person = m3/s/m3 * 1000W/kW * (specific heat * dT) * m2/person waterUseEquipment_info[:shw_watts_per_person] = waterUseEquipment_info[:peak_flow_rate_per_area] * 1000 * (4.19 * 44.4) * 1000 * area_per_occ # puts waterUseEquipment_info[:shw_watts_per_ponce the erson] # puts "\n\n\n" end end end # Store Thermal zone data qaqc[:thermal_zones] = [] model.getThermalZones.sort.each do |zone| zoneinfo = {} qaqc[:thermal_zones] << zoneinfo zoneinfo[:name] = zone.name.get zoneinfo[:floor_area] = zone.floorArea zoneinfo[:multiplier] = zone.multiplier zoneinfo[:is_conditioned] = 'N/A' if zone.isConditioned.empty? error_warning << "zone.isConditioned is empty for #{zone.name.get}" else zoneinfo[:is_conditioned] = zone.isConditioned.get end zoneinfo[:is_ideal_air_loads] = zone.useIdealAirLoads zoneinfo[:heating_sizing_factor] = -1.0 if zone.sizingZone.zoneHeatingSizingFactor.empty? error_warning << "zone.sizingZone.zoneHeatingSizingFactor is empty for #{zone.name.get}" else zoneinfo[:heating_sizing_factor] = zone.sizingZone.zoneHeatingSizingFactor.get end zoneinfo[:cooling_sizing_factor] = -1.0 # zone.sizingZone.zoneCoolingSizingFactor.get if zone.sizingZone.zoneCoolingSizingFactor.empty? error_warning << "zone.sizingZone.zoneCoolingSizingFactor is empty for #{zone.name.get}" else zoneinfo[:cooling_sizing_factor] = zone.sizingZone.zoneCoolingSizingFactor.get end zoneinfo[:zone_heating_design_supply_air_temperature] = zone.sizingZone.zoneHeatingDesignSupplyAirTemperature zoneinfo[:zone_cooling_design_supply_air_temperature] = zone.sizingZone.zoneCoolingDesignSupplyAirTemperature zoneinfo[:spaces] = [] zone.spaces.sort.each do |space| spaceinfo = {} zoneinfo[:spaces] << spaceinfo spaceinfo[:name] = space.name.get spaceinfo[:type] = space.spaceType.get.name.get unless space.spaceType.empty? end zoneinfo[:equipment] = [] zone.equipmentInHeatingOrder.each do |equipment| item = {} zoneinfo[:equipment] << item item[:name] = equipment.name.get if equipment.to_ZoneHVACComponent.is_initialized item[:type] = 'ZoneHVACComponent' elsif equipment.to_StraightComponent.is_initialized item[:type] = 'StraightComponent' end end # zone end # Store Air Loop Information qaqc[:air_loops] = [] model.getAirLoopHVACs.sort.each do |air_loop| air_loop_info = {} air_loop_info[:name] = air_loop.name.get air_loop_info[:thermal_zones] = [] air_loop_info[:total_floor_area_served] = 0.0 air_loop_info[:total_breathing_zone_outdoor_airflow_vbz] = 0.0 air_loop.thermalZones.sort.each do |zone| air_loop_info[:thermal_zones] << zone.name.get vbz = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='Standard62.1Summary' AND ReportForString='Entire Facility' AND TableName='Zone Ventilation Parameters' AND ColumnName='Breathing Zone Outdoor Airflow - Vbz' AND Units='m3/s' AND RowName='#{zone.name.get.to_s.upcase}' ") vbz = validate_optional(vbz, model, 0) air_loop_info[:total_breathing_zone_outdoor_airflow_vbz] += vbz air_loop_info[:total_floor_area_served] += zone.floorArea * zone.multiplier.to_f end air_loop_info[:area_outdoor_air_rate_m3_per_s_m2] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='Standard62.1Summary' AND ReportForString='Entire Facility' AND TableName='System Ventilation Parameters' AND ColumnName='Area Outdoor Air Rate - Ra' AND Units='m3/s-m2' AND RowName='#{air_loop_info[:name].to_s.upcase}' ") air_loop_info[:area_outdoor_air_rate_m3_per_s_m2] = validate_optional(air_loop_info[:area_outdoor_air_rate_m3_per_s_m2], model, -1.0) air_loop_info[:outdoor_air_L_per_s] = -1.0 unless air_loop_info[:area_outdoor_air_rate_m3_per_s_m2] == -1.0 air_loop_info[:outdoor_air_L_per_s] = air_loop_info[:area_outdoor_air_rate_m3_per_s_m2] * air_loop_info[:total_floor_area_served] * 1000 end # Fan unless air_loop.supplyFan.empty? air_loop_info[:supply_fan] = {} if air_loop.supplyFan.get.to_FanConstantVolume.is_initialized air_loop_info[:supply_fan][:type] = 'CV' fan = air_loop.supplyFan.get.to_FanConstantVolume.get elsif air_loop.supplyFan.get.to_FanVariableVolume.is_initialized air_loop_info[:supply_fan][:type] = 'VV' fan = air_loop.supplyFan.get.to_FanVariableVolume.get end air_loop_info[:supply_fan][:name] = fan.name.get # puts "\n\n\n\n#{fan.name.get}\n\n\n\n" air_loop_info[:supply_fan][:fan_efficiency] = fan.fanEfficiency air_loop_info[:supply_fan][:motor_efficiency] = fan.motorEfficiency air_loop_info[:supply_fan][:pressure_rise] = fan.pressureRise air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] = -1.0 max_air_flow_info = model.sqlFile.get.execAndReturnVectorOfString("SELECT RowName FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Max Air Flow Rate' AND Units='m3/s' ") max_air_flow_info = validate_optional(max_air_flow_info, model, 'N/A') if max_air_flow_info == 'N/A' error_warning << "max_air_flow_info is nil because the following sql statement returned nil: RowName FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Max Air Flow Rate' AND Units='m3/s' " else if max_air_flow_info.include? air_loop_info[:supply_fan][:name].to_s.upcase.to_s air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Max Air Flow Rate' AND Units='m3/s' AND RowName='#{air_loop_info[:supply_fan][:name].upcase}' ").get air_loop_info[:supply_fan][:rated_electric_power_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Rated Electric Power' AND Units='W' AND RowName='#{air_loop_info[:supply_fan][:name].upcase}' ") # Version 3.2.0 has renamed rated electric_power to rated electricity rate if air_loop_info[:supply_fan][:rated_electric_power_w].empty? air_loop_info[:supply_fan][:rated_electric_power_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Rated Electricity Rate' AND Units='W' AND RowName='#{air_loop_info[:supply_fan][:name].upcase}' ") end air_loop_info[:supply_fan][:rated_electric_power_w] = air_loop_info[:supply_fan][:rated_electric_power_w].get else error_warning << "#{air_loop_info[:supply_fan][:name]} does not exist in sql file WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Fans' AND ColumnName='Max Air Flow Rate' AND Units='m3/s'" end end end # economizer air_loop_info[:economizer] = {} air_loop_info[:economizer][:name] = air_loop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.name.get unless air_loop.airLoopHVACOutdoorAirSystem.empty? or air_loop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.name.empty? air_loop_info[:economizer][:control_type] = air_loop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.getEconomizerControlType unless air_loop.airLoopHVACOutdoorAirSystem.empty? # DX cooling coils air_loop_info[:cooling_coils] = {} air_loop_info[:cooling_coils][:dx_single_speed] = [] air_loop_info[:cooling_coils][:dx_two_speed] = [] air_loop_info[:cooling_coils][:coil_cooling_water] = [] # Heating Coil air_loop_info[:heating_coils] = {} air_loop_info[:heating_coils][:coil_heating_gas] = [] air_loop_info[:heating_coils][:coil_heating_electric] = [] air_loop_info[:heating_coils][:coil_heating_water] = [] # Heat Excahnger air_loop_info[:heat_exchanger] = {} air_loop.supplyComponents.each do |supply_comp| if supply_comp.to_CoilHeatingGas.is_initialized coil = {} air_loop_info[:heating_coils][:coil_heating_gas] << coil gas = supply_comp.to_CoilHeatingGas.get coil[:name] = gas.name.get coil[:type] = 'Gas' coil[:efficency] = gas.gasBurnerEfficiency # coil[:nominal_capacity]= gas.nominalCapacity() coil[:nominal_capacity] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Heating Coils' AND ColumnName='Nominal Total Capacity' AND RowName='#{coil[:name].to_s.upcase}'") coil[:nominal_capacity] = validate_optional(coil[:nominal_capacity], model, -1.0) end if supply_comp.to_CoilHeatingElectric.is_initialized coil = {} air_loop_info[:heating_coils][:coil_heating_electric] << coil electric = supply_comp.to_CoilHeatingElectric.get coil[:name] = electric.name.get coil[:type] = 'Electric' coil[:nominal_capacity] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Heating Coils' AND ColumnName='Nominal Total Capacity' AND RowName='#{coil[:name].to_s.upcase}'") coil[:nominal_capacity] = validate_optional(coil[:nominal_capacity], model, -1.0) end if supply_comp.to_CoilHeatingWater.is_initialized coil = {} air_loop_info[:heating_coils][:coil_heating_water] << coil water = supply_comp.to_CoilHeatingWater.get coil[:name] = water.name.get coil[:type] = 'Water' coil[:nominal_capacity] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Heating Coils' AND ColumnName='Nominal Total Capacity' AND RowName='#{coil[:name].to_s.upcase}'") coil[:nominal_capacity] = validate_optional(coil[:nominal_capacity], model, -1.0) end if supply_comp.to_HeatExchangerAirToAirSensibleAndLatent.is_initialized heatExchanger = supply_comp.to_HeatExchangerAirToAirSensibleAndLatent.get air_loop_info[:heat_exchanger][:name] = heatExchanger.name.get end end # I dont think i need to get the type of heating coil from the sql file, because the coils are differentiated by class, and I have hard coded the information # model.sqlFile().get().execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName= 'Heating Coils' AND ColumnName='Type' ").get #padmussen to complete #AND RowName='#{air_loop_info[:heating_coils][:name].upcase}' # Collect all the fans into the the array. air_loop.supplyComponents.each do |supply_comp| if supply_comp.to_CoilCoolingDXSingleSpeed.is_initialized coil = {} air_loop_info[:cooling_coils][:dx_single_speed] << coil single_speed = supply_comp.to_CoilCoolingDXSingleSpeed.get coil[:name] = single_speed.name.get coil[:cop] = single_speed.ratedCOP.to_f coil[:nominal_total_capacity_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Cooling Coils' AND ColumnName='Nominal Total Capacity' AND RowName='#{coil[:name].upcase}' ") coil[:nominal_total_capacity_w] = validate_optional(coil[:nominal_total_capacity_w], model, -1.0) end if supply_comp.to_CoilCoolingDXTwoSpeed.is_initialized coil = {} air_loop_info[:cooling_coils][:dx_two_speed] << coil two_speed = supply_comp.to_CoilCoolingDXTwoSpeed.get coil[:name] = two_speed.name.get coil[:cop_low] = two_speed.ratedLowSpeedCOP.to_f coil[:cop_high] = two_speed.ratedHighSpeedCOP.to_f coil[:nominal_total_capacity_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Cooling Coils' AND ColumnName='Nominal Total Capacity' AND RowName='#{coil[:name].upcase}' ") coil[:nominal_total_capacity_w] = validate_optional(coil[:nominal_total_capacity_w], model, -1.0) end if supply_comp.to_CoilCoolingWater.is_initialized coil = {} air_loop_info[:cooling_coils][:coil_cooling_water] << coil coil_cooling_water = supply_comp.to_CoilCoolingWater.get coil[:name] = coil_cooling_water.name.get coil[:nominal_total_capacity_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Cooling Coils' AND ColumnName='Nominal Total Capacity' AND RowName='#{coil[:name].upcase}' ") coil[:nominal_total_capacity_w] = validate_optional(coil[:nominal_total_capacity_w], model, -1.0) coil[:nominal_sensible_heat_ratio] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Cooling Coils' AND ColumnName='Nominal Sensible Heat Ratio' AND RowName='#{coil[:name].upcase}' ") coil[:nominal_sensible_heat_ratio] = validate_optional(coil[:nominal_sensible_heat_ratio], model, -1.0) end end qaqc[:air_loops] << air_loop_info end qaqc[:plant_loops] = [] model.getPlantLoops.sort.each do |plant_loop| plant_loop_info = {} qaqc[:plant_loops] << plant_loop_info plant_loop_info[:name] = plant_loop.name.get sizing = plant_loop.sizingPlant plant_loop_info[:design_loop_exit_temperature] = sizing.designLoopExitTemperature plant_loop_info[:loop_design_temperature_difference] = sizing.loopDesignTemperatureDifference # Create Container for plant equipment arrays. plant_loop_info[:pumps] = [] plant_loop_info[:boilers] = [] plant_loop_info[:chiller_electric_eir] = [] plant_loop_info[:cooling_tower_single_speed] = [] plant_loop_info[:water_heater_mixed] = [] plant_loop.supplyComponents.each do |supply_comp| # Collect Constant Speed if supply_comp.to_PumpConstantSpeed.is_initialized pump = supply_comp.to_PumpConstantSpeed.get pump_info = {} plant_loop_info[:pumps] << pump_info pump_info[:name] = pump.name.get pump_info[:type] = 'Pump:ConstantSpeed' pump_info[:head_pa] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Pumps' AND ColumnName='Head' AND RowName='#{pump_info[:name].upcase}' ") pump_info[:head_pa] = validate_optional(pump_info[:head_pa], model, -1.0) pump_info[:water_flow_m3_per_s] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Pumps' AND ColumnName='Water Flow' AND RowName='#{pump_info[:name].upcase}' ") pump_info[:water_flow_m3_per_s] = validate_optional(pump_info[:water_flow_m3_per_s], model, -1.0) pump_info[:electric_power_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Pumps' AND ColumnName='Electric Power' AND RowName='#{pump_info[:name].upcase}' ") pump_info[:electric_power_w] = validate_optional(pump_info[:electric_power_w], model, -1.0) pump_info[:motor_efficency] = pump.motorEfficiency end # Collect Variable Speed if supply_comp.to_PumpVariableSpeed.is_initialized pump = supply_comp.to_PumpVariableSpeed.get pump_info = {} plant_loop_info[:pumps] << pump_info pump_info[:name] = pump.name.get pump_info[:type] = 'Pump:VariableSpeed' pump_info[:head_pa] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Pumps' AND ColumnName='Head' AND RowName='#{pump_info[:name].upcase}' ") pump_info[:head_pa] = validate_optional(pump_info[:head_pa], model, -1.0) pump_info[:water_flow_m3_per_s] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Pumps' AND ColumnName='Water Flow' AND RowName='#{pump_info[:name].upcase}' ") pump_info[:water_flow_m3_per_s] = validate_optional(pump_info[:water_flow_m3_per_s], model, -1.0) pump_info[:electric_power_w] = model.sqlFile.get.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='EquipmentSummary' AND ReportForString='Entire Facility' AND TableName='Pumps' AND ColumnName='Electric Power' AND RowName='#{pump_info[:name].upcase}' ") pump_info[:electric_power_w] = validate_optional(pump_info[:electric_power_w], model, -1.0) pump_info[:motor_efficency] = pump.motorEfficiency end # Collect HotWaterBoilers if supply_comp.to_BoilerHotWater.is_initialized boiler = supply_comp.to_BoilerHotWater.get boiler_info = {} plant_loop_info[:boilers] << boiler_info boiler_info[:name] = boiler.name.get boiler_info[:type] = 'Boiler:HotWater' boiler_info[:fueltype] = boiler.fuelType boiler_info[:nominal_capacity] = boiler.nominalCapacity boiler_info[:nominal_capacity] = validate_optional(boiler_info[:nominal_capacity], model, -1.0) end # Collect ChillerElectricEIR if supply_comp.to_ChillerElectricEIR.is_initialized chiller = supply_comp.to_ChillerElectricEIR.get chiller_info = {} plant_loop_info[:chiller_electric_eir] << chiller_info chiller_info[:name] = chiller.name.get chiller_info[:type] = 'Chiller:Electric:EIR' chiller_info[:reference_capacity] = validate_optional(chiller.referenceCapacity, model, -1.0) chiller_info[:reference_leaving_chilled_water_temperature] = chiller.referenceLeavingChilledWaterTemperature end # Collect CoolingTowerSingleSpeed if supply_comp.to_CoolingTowerSingleSpeed.is_initialized coolingTower = supply_comp.to_CoolingTowerSingleSpeed.get coolingTower_info = {} plant_loop_info[:cooling_tower_single_speed] << coolingTower_info coolingTower_info[:name] = coolingTower.name.get coolingTower_info[:type] = 'CoolingTower:SingleSpeed' coolingTower_info[:fan_power_at_design_air_flow_rate] = validate_optional(coolingTower.fanPoweratDesignAirFlowRate, model, -1.0) end # Collect WaterHeaterMixed if supply_comp.to_WaterHeaterMixed.is_initialized waterHeaterMixed = supply_comp.to_WaterHeaterMixed.get waterHeaterMixed_info = {} plant_loop_info[:water_heater_mixed] << waterHeaterMixed_info waterHeaterMixed_info[:name] = waterHeaterMixed.name.get waterHeaterMixed_info[:type] = 'WaterHeater:Mixed' waterHeaterMixed_info[:heater_thermal_efficiency] = waterHeaterMixed.heaterThermalEfficiency.get unless waterHeaterMixed.heaterThermalEfficiency.empty? waterHeaterMixed_info[:heater_fuel_type] = waterHeaterMixed.heaterFuelType end end qaqc[:eplusout_err] = {} warnings = model.sqlFile.get.execAndReturnVectorOfString("SELECT ErrorMessage FROM Errors WHERE ErrorType='0' ") warnings = validate_optional(warnings, model, 'N/A') unless warnings == 'N/A' qaqc[:eplusout_err][:warnings] = model.sqlFile.get.execAndReturnVectorOfString("SELECT ErrorMessage FROM Errors WHERE ErrorType='0' ").get qaqc[:eplusout_err][:fatal] = model.sqlFile.get.execAndReturnVectorOfString("SELECT ErrorMessage FROM Errors WHERE ErrorType='2' ").get qaqc[:eplusout_err][:severe] = model.sqlFile.get.execAndReturnVectorOfString("SELECT ErrorMessage FROM Errors WHERE ErrorType='1' ").get end qaqc[:ruby_warnings] = error_warning end qaqc[:code_metrics] = {} qaqc[:code_metrics]['heating_gj'] = qaqc[:end_uses]['heating_gj'] qaqc[:code_metrics]['cooling_gj'] = qaqc[:end_uses]['cooling_gj'] qaqc[:code_metrics][:ep_conditioned_floor_area_m2] = qaqc[:building][:conditioned_floor_area_m2] qaqc[:code_metrics][:os_conditioned_floor_area_m2] = qaqc[:envelope][:interior_floors_area_m2] + qaqc[:envelope][:outdoor_floors_area_m2] + qaqc[:envelope][:ground_floors_area_m2] # TEDI unless qaqc[:building][:conditioned_floor_area_m2].nil? qaqc[:code_metrics][:building_tedi_gj_per_m2] = (qaqc[:end_uses]['heating_gj'] + qaqc[:end_uses]['cooling_gj'] ) / qaqc[:building][:conditioned_floor_area_m2] # Mech TEDI? qaqc[:code_metrics][:building_medi_gj_per_m2] = (qaqc[:end_uses]['fans_gj'] + qaqc[:end_uses]['pumps_gj'] + qaqc[:end_uses]['heat_rejection_gj'] + qaqc[:end_uses]['humidification_gj'] + qaqc[:end_uses]['heat_recovery_gj'] ) / qaqc[:building][:conditioned_floor_area_m2] end return qaqc end |
#create_ems_to_turn_on_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed_for_night_cycle(multi_speed_heat_pump) ⇒ Object
Create EMS to turn on “AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed” in response to a call from the night cycle availability manager of the air loop. It was found that this object doesn’t respond properly to this call from the night cycle
1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1095 def create_ems_to_turn_on_AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed_for_night_cycle(multi_speed_heat_pump) model = multi_speed_heat_pump.model avail_manager_name = nil if multi_speed_heat_pump.airLoopHVAC.is_initialized if !multi_speed_heat_pump.airLoopHVAC.get.availabilityManagers.empty? avail_manager_name = multi_speed_heat_pump.airLoopHVAC.get.availabilityManagers[0].name.to_s end end return unless avail_manager_name avail_manager_out_var_name = 'Availability Manager Night Cycle Control Status' avail_manager_out_var = OpenStudio::Model::OutputVariable.new(avail_manager_out_var_name, model) avail_manager_out_var.setKeyValue(avail_manager_name) avail_manager_out_var.setReportingFrequency('Timestep') night_cycle_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, avail_manager_out_var) heat_pump_avail_sch = nil if multi_speed_heat_pump.availabilitySchedule.is_initialized heat_pump_avail_sch = multi_speed_heat_pump.availabilitySchedule.get elsif multi_speed_heat_pump.airLoopHVAC.get.availabilitySchedule.is_initialized heat_pump_avail_sch = multi_speed_heat_pump.airLoopHVAC.get.availabilitySchedule.get else heat_pump_avail_sch = OpenStudio::Model::ScheduleConstant.new(model) heat_pump_avail_sch.setValue(1.0) end heat_pump_avail_sch_var = OpenStudio::Model::OutputVariable.new('Schedule Value', model) heat_pump_avail_sch_var.setKeyValue(heat_pump_avail_sch.name.to_s) heat_pump_avail_sch_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, heat_pump_avail_sch_var) updated_heat_pump_avail_sch = OpenStudio::Model::ScheduleConstant.new(model) multi_speed_heat_pump.setAvailabilitySchedule(updated_heat_pump_avail_sch) # This method will seem like an error in number of args..but this is due to swig voodoo. heat_pump_avail_sch_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(updated_heat_pump_avail_sch, 'Schedule:Constant', 'Schedule Value') heat_pump_avail_sch_prog = OpenStudio::Model::EnergyManagementSystemProgram.new(model) heat_pump_avail_sch_prog.setName("#{ems_friendly_name(multi_speed_heat_pump.name)} Availability Schedule Program by Line") heat_pump_avail_sch_prog_body = <<-EMS IF #{heat_pump_avail_sch_sensor.handle} > 0.0 SET #{heat_pump_avail_sch_actuator.handle} = #{heat_pump_avail_sch_sensor.handle} ELSEIF #{night_cycle_sensor.handle} == 2.0 SET #{heat_pump_avail_sch_actuator.handle} = 1.0 ELSE SET #{heat_pump_avail_sch_actuator.handle} = 0.0 ENDIF EMS heat_pump_avail_sch_prog.setBody(heat_pump_avail_sch_prog_body) pcm = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model) pcm.setName("#{heat_pump_avail_sch_prog.name} Calling Manager") pcm.setCallingPoint('InsideHVACSystemIterationLoop') pcm.addProgram(heat_pump_avail_sch_prog) end |
#create_heating_cooling_on_off_availability_schedule(model) ⇒ Object
2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 2142 def create_heating_cooling_on_off_availability_schedule(model) # @todo Create a feature to derive start and end heating and cooling seasons from weather file. avail_data = [{ start_month: 1, start_day: 1, end_month: 6, end_day: 30, htg_value: 1, clg_value: 0 }, { start_month: 7, start_day: 1, end_month: 10, end_day: 31, htg_value: 0, clg_value: 1 }, { start_month: 11, start_day: 1, end_month: 12, end_day: 31, htg_value: 1, clg_value: 0 }] # Heating coil availability schedule for tpfc htg_availability_sch = OpenStudio::Model::ScheduleRuleset.new(model) htg_availability_sch.setName('tpfc_htg_availability') # Cooling coil availability schedule for tpfc clg_availability_sch = OpenStudio::Model::ScheduleRuleset.new(model) clg_availability_sch.setName('tpfc_clg_availability') avail_data.each do |data| htg_availability_sch_rule = OpenStudio::Model::ScheduleRule.new(htg_availability_sch) htg_availability_sch_rule.setName('tpfc_htg_availability_sch_rule') htg_availability_sch_rule.setStartDate(model.getYearDescription.makeDate(data[:start_month], data[:start_day])) htg_availability_sch_rule.setEndDate(model.getYearDescription.makeDate(data[:end_month], data[:end_day])) htg_availability_sch_rule.setApplySunday(true) htg_availability_sch_rule.setApplyMonday(true) htg_availability_sch_rule.setApplyTuesday(true) htg_availability_sch_rule.setApplyWednesday(true) htg_availability_sch_rule.setApplyThursday(true) htg_availability_sch_rule.setApplyFriday(true) htg_availability_sch_rule.setApplySaturday(true) day_schedule = htg_availability_sch_rule.daySchedule day_schedule.setName('tpfc_htg_availability_sch_rule_day') day_schedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), data[:htg_value]) clg_availability_sch_rule = OpenStudio::Model::ScheduleRule.new(clg_availability_sch) clg_availability_sch_rule.setName('tpfc_clg_availability_sch_rule') clg_availability_sch_rule.setStartDate(model.getYearDescription.makeDate(data[:start_month], data[:start_day])) clg_availability_sch_rule.setEndDate(model.getYearDescription.makeDate(data[:end_month], data[:end_day])) clg_availability_sch_rule.setApplySunday(true) clg_availability_sch_rule.setApplyMonday(true) clg_availability_sch_rule.setApplyTuesday(true) clg_availability_sch_rule.setApplyWednesday(true) clg_availability_sch_rule.setApplyThursday(true) clg_availability_sch_rule.setApplyFriday(true) clg_availability_sch_rule.setApplySaturday(true) day_schedule = clg_availability_sch_rule.daySchedule day_schedule.setName('tpfc_clg_availability_sch_rule_day') day_schedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), data[:clg_value]) end return clg_availability_sch, htg_availability_sch end |
#create_hw_loop_if_required(baseboard_type, boiler_fueltype, backup_boiler_fueltype, mau_heating_coil_type, model) ⇒ Object
Method will create a hot water loop if systems default fuel and medium sources require it.
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 828 def create_hw_loop_if_required(baseboard_type, boiler_fueltype, backup_boiler_fueltype, mau_heating_coil_type, model) # get systems that will be used in the model based on the space types to determine if a hw_loop is required. systems_used = [] model.getSpaces.sort.each do |space| systems_used << get_necb_spacetype_system_selection(space) systems_used.uniq! end # See if we need to create a hot water loop based on fueltype and systems used. hw_loop_needed = false systems_used.each do |system| case system.to_s when '2', '5', '7' hw_loop_needed = true when '1', '6' if (mau_heating_coil_type == 'Hot Water') || (baseboard_type == 'Hot Water') hw_loop_needed = true end when '3', '4' if (mau_heating_coil_type == 'Hot Water') || (baseboard_type == 'Hot Water') hw_loop_needed = true if baseboard_type == 'Hot Water' end end if hw_loop_needed # just need one true condition to need a boiler. break end # each end # create hw_loop as needed.. Assuming one loop per model. if hw_loop_needed @hw_loop = OpenStudio::Model::PlantLoop.new(model) always_on = model.alwaysOnDiscreteSchedule setup_hw_loop_with_components(model, @hw_loop, boiler_fueltype, backup_boiler_fueltype, always_on) end return @hw_loop end |
#create_necb_system(baseboard_type:, boiler_fueltype:, chiller_type:, fan_type:, heating_coil_type_sys3:, heating_coil_type_sys4:, heating_coil_type_sys6:, hw_loop:, mau_cooling_type:, mau_heating_coil_type:, mau_type:, model:, zones:, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel') ⇒ Object
Default method to create a necb system and assign array of zones to be supported by it. It will try to bring zones with similar loads on the same airloops and set control zones where possible for single zone systems and will create monolithic system 6 multizones where possible.
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 869 def create_necb_system(baseboard_type:, boiler_fueltype:, chiller_type:, fan_type:, heating_coil_type_sys3:, heating_coil_type_sys4:, heating_coil_type_sys6:, hw_loop:, mau_cooling_type:, mau_heating_coil_type:, mau_type:, model:, zones:, necb_reference_hp:false, necb_reference_hp_supp_fuel:'DefaultFuel') # The goal is to minimize the number of system when possible. system_zones_hash = {} zones.each do |zone| system_zones_hash[get_necb_thermal_zone_system_selection(zone)] = [] if system_zones_hash[get_necb_thermal_zone_system_selection(zone)].nil? system_zones_hash[get_necb_thermal_zone_system_selection(zone)] << zone end # puts JSON.pretty_generate(system_zones_hash) # go through each system and zones pairs to system_zones_hash.each_pair do |system, sys_zones| case system when 0, nil # Do nothing no system assigned to zone. Used for Unconditioned spaces when 1 group_similar_zones_together(sys_zones).each do |curr_zones| mau_air_loop = add_sys1_unitary_ac_baseboard_heating(model: model, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel, zones: curr_zones, mau_type: mau_type, mau_heating_coil_type: mau_heating_coil_type, baseboard_type: baseboard_type, hw_loop: @hw_loop, multispeed: false) end when 2 group_similar_zones_together(sys_zones).each do |curr_zones| add_sys2_FPFC_sys5_TPFC(model: model, zones: curr_zones, chiller_type: chiller_type, mau_cooling_type: mau_cooling_type, fan_coil_type: 'FPFC', hw_loop: @hw_loop) end when 3 group_similar_zones_together(sys_zones).each do |curr_zones| add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating(model: model, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel, zones: curr_zones, heating_coil_type: heating_coil_type_sys3, baseboard_type: baseboard_type, hw_loop: @hw_loop, multispeed: false) end when 4 group_similar_zones_together(sys_zones).each do |curr_zones| add_sys4_single_zone_make_up_air_unit_with_baseboard_heating(model: model, necb_reference_hp: necb_reference_hp, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel, zones: curr_zones, heating_coil_type: heating_coil_type_sys4, baseboard_type: baseboard_type, hw_loop: @hw_loop) # add_sys3and8_single_zone_packaged_rooftop_unit_with_baseboard_heating(model: model, # zones: zones, # heating_coil_type: heating_coil_type_sys4, # baseboard_type: baseboard_type, # hw_loop: @hw_loop, # multispeed: false) end when 5 group_similar_zones_together(sys_zones).each do |curr_zones| add_sys2_FPFC_sys5_TPFC(model: model, zones: curr_zones, chiller_type: chiller_type, mau_cooling_type: mau_cooling_type, fan_coil_type: 'TPFC', hw_loop: @hw_loop) end when 6 if necb_reference_hp add_sys6_multi_zone_reference_hp_with_baseboard_heating(model: model, zones: sys_zones, heating_coil_type: heating_coil_type_sys6, baseboard_type: baseboard_type, hw_loop:@hw_loop, necb_reference_hp_supp_fuel: necb_reference_hp_supp_fuel) else add_sys6_multi_zone_built_up_system_with_baseboard_heating(model: model, zones: sys_zones, heating_coil_type: heating_coil_type_sys6, baseboard_type: baseboard_type, chiller_type: chiller_type, fan_type: fan_type, hw_loop: @hw_loop) end when 7 group_similar_zones_together(sys_zones).each do |curr_zones| add_sys2_FPFC_sys5_TPFC(model: model, zones: curr_zones, chiller_type: chiller_type, fan_coil_type: 'FPFC', mau_cooling_type: mau_cooling_type, hw_loop: @hw_loop) end end end end |
#determine_control_zone(zones) ⇒ Object
This method will determine the control zone from the last sizing run space loads.
1214 1215 1216 1217 1218 1219 1220 1221 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 1214 def determine_control_zone(zones) # In this case the control zone is the load with the largest heating loads. This may cause overheating of some zones. # but this is preferred to unmet heating. # Iterate through zones. zone_heating_load_hash = {} zones.each { |zone| zone_heating_load_hash[zone] = stored_zone_heating_load(zone) } return zone_heating_load_hash.max_by(&:last).first end |
#determine_dominant_necb_schedule_type(model) ⇒ Object
This model determines the dominant NECB schedule type return s.each [String]
808 809 810 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 808 def determine_dominant_necb_schedule_type(model) return determine_dominant_schedule(model.getSpaces) end |
#determine_dominant_schedule(spaces) ⇒ Object
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 767 def determine_dominant_schedule(spaces) # lookup necb space type properties space_type_properties = @standards_data['space_types'] # Here is a hash to keep track of the m2 running total of spacetypes for each # sched type. # 2018-04-11: Not sure if this is still used but the list was expanded to incorporate additional existing or potential # future schedules. schedule_hash = Hash[ 'A', 0, 'B', 0, 'C', 0, 'D', 0, 'E', 0, 'F', 0, 'G', 0, 'H', 0, 'I', 0, 'J', 0, 'K', 0, 'L', 0, 'M', 0, 'N', 0, 'O', 0, 'P', 0, 'Q', 0 ] # iterate through spaces in building. spaces.select { |space| !is_an_necb_wildcard_space?(space) && (space.spaceType.get.standardsSpaceType.get != '- undefined -') }.each do |space| # Ensure space floors are multiplied. mult = @space_multiplier_map[space.name.to_s].nil? ? 1.0 : @space_multiplier_map[space.name.to_s] # puts "this #{determine_necb_schedule_type(space)}" schedule_hash[determine_necb_schedule_type(space)] += space.floorArea * mult end # finds max value and returns NECB schedule letter. # determine dominant letter schedule. return schedule_hash.max_by(&:last).first end |
#determine_necb_schedule_type(space) ⇒ String
This method determines the spacetype schedule type. This will re
816 817 818 819 820 821 822 823 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 816 def determine_necb_schedule_type(space) spacetype_data = @standards_data['space_types'] raise "Spacetype not defined for space #{space.get.name}) if space.spaceType.empty?" if space.spaceType.empty? raise "Undefined standardsSpaceType or StandardsBuildingType for space #{space.spaceType.get.name}) if space.spaceType.empty?" if space.spaceType.get.standardsSpaceType.empty? | space.spaceType.get.standardsBuildingType.empty? space_type_properties = spacetype_data.detect { |st| (st['space_type'] == space.spaceType.get.standardsSpaceType.get) && (st['building_type'] == space.spaceType.get.standardsBuildingType.get) } return space_type_properties['necb_schedule_type'].strip end |
#determine_spacetype_vintage(model) ⇒ Object
this method will determine the vintage of NECB spacetypes the model contains. It will return nil if it can’t determine it.
1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1139 def determine_spacetype_vintage(model) #this code is the list of available vintages space_type_vintage_list = ['NECB2011', 'NECB2015', 'NECB2017', 'NECB2020', 'BTAPPRE1980', 'BTAP1980TO2010'] #this reorders the list to do the current class first. space_type_vintage_list.insert(0, space_type_vintage_list.delete(self.class.name)) # Set the space_type space_type_vintage = nil # get list of space types used in model and a mapped string. model_space_type_names = model.getSpaceTypes.map do |spacetype| [spacetype.standardsBuildingType.get.to_s + '-' + spacetype.standardsSpaceType.get.to_s] end # Now iterate though each vintage space_type_vintage_list.each do |template| # Create the standard object and get a list of all the spacetypes available for that vintage. standard_space_type_list = Standard.build(template).get_all_spacetype_names.map { |spacetype| [spacetype[0].to_s + '-' + spacetype[1].to_s] } # set array to contain unknown spacetypes. unknown_spacetypes = [] # iterate though all space types that the model is using model_space_type_names.each do |space_type_name| # push unknown spacetypes into the array. unknown_spacetypes << space_type_name unless standard_space_type_list.include?(space_type_name) end if unknown_spacetypes.empty? # No unknowns, so return the template and don't bother looking for others. return template end end return space_type_vintage end |
#distance(loc1, loc2) ⇒ Object
Enter in [latitude, longitude] for each loc and this method will return the distance.
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 162 def distance(loc1, loc2) rad_per_deg = Math::PI / 180 # PI / 180 rkm = 6371 # Earth radius in kilometers rm = rkm * 1000 # Radius in meters dlat_rad = (loc2[0] - loc1[0]) * rad_per_deg # Delta, converted to rad dlon_rad = (loc2[1] - loc1[1]) * rad_per_deg lat1_rad, lon1_rad = loc1.map { |i| i * rad_per_deg } lat2_rad, lon2_rad = loc2.map { |i| i * rad_per_deg } a = Math.sin(dlat_rad / 2)**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * Math.sin(dlon_rad / 2)**2 c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) rm * c # Delta in meters end |
#download_and_save_file(weather_list_url:, weather_loc:, git_folder:) ⇒ Object
Arguments: weather_list_url (string): the web address of the json file containing the list of weather files on the repository weather_loc (string): the name of the epw file we are looking for without the .epw extension git_folder (string): the url of the folder containing the weather files. As of 2023-07-07 this this is either the
url of the historical weather data folder or the future weather data folder.
2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 2302 def download_and_save_file(weather_list_url:, weather_loc:, git_folder:) status = false attempt = 1 # Try to download the list of weather files 5 times, waiting 5 seconds between each attempt. until attempt > 5 begin puts "Beginning attempt #{attempt} to download #{weather_list_url}" # Download the list of weather files on the repository URI.open(weather_list_url) do |web_data| # Convert the weather file list to an array if web_data.size <= 100 raise("Could not read #{weather_list_url}!") end weather_files = (JSON.parse(web_data.read)).to_a # Check to see if the requested weather file is on the list zip_name = weather_files.find{ |weather_file| weather_file.match(weather_loc) } # If the weather file is on the list proceed, otherwise report that it could not be found unless zip_name.nil? # Found the weather file on the list # Define the full url of the weather zip file we want to download save_file_url = git_folder + zip_name # Define the local location of where the weather zip file will be saved weather_dir = File.absolute_path(File.join(__FILE__, '..', '..', '..', '..', '..' , '..', "data/weather")) save_file = File.join(weather_dir, zip_name) attemptb = 1 # Try to download the weather file up to 5 times, waiting 5 seconds between each attempt. until attemptb > 5 begin puts "Beginning attempt #{attemptb} to download #{save_file_url}" # Download the weather zip file from the repository URI.open(save_file_url) do |file_url| if file_url.size <= 100 raise("Could not read #{save_file_url}!") end # Save the zip file in the /data/weather folder File.open(save_file, 'wb') { |f| f.write(file_url.read) } puts "Downloaded #{save_file_url} to #{save_file}" # Extract the individual weother files from the zip file status = extract_weather_data(zipped_file: save_file, weather_dir: weather_dir) end attemptb = 10 rescue sleep(30) attemptb += 1 end end end end attempt = 10 rescue sleep(30) attempt += 1 end end return status end |
#extract_weather_data(zipped_file:, weather_dir:) ⇒ Object
This method extracts data from a zip file. The name implies it is to be used for zip files containing weather data but really it can be used to extract any zip file. Arguments: zipped_file (string): As the name implies, this is the name and path to the zip file we want to expand. weather_dir (string): This is the folder where the zipped files will be extracted to. Don’t let the name fool you.
it can be any folder not just a weather directory.
2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 2395 def extract_weather_data(zipped_file:, weather_dir:) # Set a flag to check if the weather data is for future weather. future_file = false # Start expanding the data. Zip::File.open(zipped_file) do |zip_file| puts "Expanding #{zipped_file}" # Cycle through each file in the zip file zip_file.each do |entry| # Define the location of where the file will be saved locally curr_save_file = File.join(weather_dir, entry.name.to_s) puts "Extracting #{entry.name} to #{curr_save_file}" # Check if the file includes a '_ASHRAE.ddy' term. If there is, later on we will rename the '_ASHRAE.ddy' file # to just '.ddy'. This is so the rest of BTAP uses the _ASHRAE.ddy file. entry_name = entry.name.to_s if entry_name.length > 11 future_file = true if entry_name[-11..-1] == '_ASHRAE.ddy' end # entry.extract # This was required before but now it isn't. I'm confused so am saving this comment to # remind me if there are problems later. # Read the data from the file content = entry.get_input_stream.read # Save the data locally File.open(curr_save_file, 'wb') { |save_f| save_f.write(content) } end end if future_file # Rename the non ASHRAE.ddy as '_non_ASHRAE.ddy' and save the '_ASHRAE.ddy' as the regular '.ddy' file. This is # because the ASHRAE .ddy file includes sizing information not included in the regular .ddy file for future # weather data files. Unfortunately, openstudio-standards just looks for the regular .ddy file for sizing # information which is why the switch is done. weather_loc = (File.basename(zipped_file).to_s)[0..-5] puts "Renaming #{weather_loc}.ddy as #{weather_loc}_orig.ddy and #{weather_loc}_ASHRAE.ddy as #{weather_loc}.ddy." puts "This is so that the design weather information in the #{weather_loc}_ASHRAE.ddy file is used." orig_ddy_name = File.join(weather_dir, (weather_loc + ".ddy")) ashrae_ddy_name = File.join(weather_dir, (weather_loc + "_ASHRAE.ddy")) rev_ddy_name = File.join(weather_dir, (weather_loc + "_orig.ddy")) FileUtils.cp(orig_ddy_name, rev_ddy_name) FileUtils.cp(ashrae_ddy_name, orig_ddy_name) end # Return true if everything worked out return true end |
#fan_baseline_impeller_efficiency(fan) ⇒ Double
Add fan type to data model and modify this method
Determines the baseline fan impeller efficiency based on the specified fan type.
1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1330 def fan_baseline_impeller_efficiency(fan) # Assume that the fan efficiency is 65% for normal fans # @todo add fan type to fan data model # and infer impeller efficiency from that? # or do we always assume a certain type of # fan impeller for the baseline system? # @todo check COMNET and T24 ACM and PNNL 90.1 doc fan_impeller_eff = 0.65 return fan_impeller_eff end |
#fan_constant_volume_apply_prototype_fan_pressure_rise(fan_constant_volume) ⇒ Object
1529 1530 1531 1532 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1529 def fan_constant_volume_apply_prototype_fan_pressure_rise(fan_constant_volume) fan_constant_volume.setPressureRise(get_standards_constant('fan_constant_volume_pressure_rise_value')) return true end |
#fan_standard_minimum_motor_efficiency_and_size(fan, motor_bhp) ⇒ Array<Double>
Determines the minimum fan motor efficiency and nominal size for a given motor bhp. This should be the total brake horsepower with any desired safety factor already included. This method picks the next nominal motor catgory larger than the required brake horsepower, and the efficiency is based on that size. For example, if the bhp = 6.3, the nominal size will be 7.5HP and the efficiency for 90.1-2010 will be 91.7% from Table 10.8B. This method assumes 4-pole, 1800rpm totally-enclosed fan-cooled motors.
1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1353 def fan_standard_minimum_motor_efficiency_and_size(fan, motor_bhp) fan_motor_eff = 0.85 nominal_hp = motor_bhp # Don't attempt to look up motor efficiency # for zero-hp fans, which may occur when there is no # airflow required for a particular system, typically # heated-only spaces with high internal gains # and no OA requirements such as elevator shafts. return [fan_motor_eff, 0] if motor_bhp == 0.0 # Lookup the minimum motor efficiency motors_table = @standards_data['motors'] # Assuming all fan motors are 4-pole ODP motor_use = 'FAN' motor_type = '' if (fan.class.name == 'OpenStudio::Model::FanConstantVolume') || (fan.class.name == 'OpenStudio::Model::FanOnOff') motor_type = 'CONSTANT' elsif fan.class.name == 'OpenStudio::Model::FanVariableVolume' # Is this a return or supply fan if fan.name.to_s.include?('Supply') motor_type += 'VARIABLE-SUPPLY' elsif fan.name.to_s.include?('Return') motor_type += 'VARIABLE-RETURN' end # 0.909 corrects for 10% over sizing implemented upstream # 0.7457 is to convert from bhp to kW fan_power_kw = 0.909 * 0.7457 * motor_bhp power_vs_flow_curve_name = if fan_power_kw >= 25.0 'VarVolFan-FCInletVanes-NECB2011-FPLR' elsif fan_power_kw >= 7.5 && fan_power_kw < 25 'VarVolFan-AFBIInletVanes-NECB2011-FPLR' else 'VarVolFan-AFBIFanCurve-NECB2011-FPLR' end power_vs_flow_curve = model_add_curve(fan.model, power_vs_flow_curve_name) fan.setFanPowerMinimumFlowRateInputMethod('Fraction') fan.setFanPowerCoefficient5(0.0) fan.setFanPowerMinimumFlowFraction(power_vs_flow_curve.minimumValueofx) fan.setFanPowerCoefficient1(power_vs_flow_curve.coefficient1Constant) fan.setFanPowerCoefficient2(power_vs_flow_curve.coefficient2x) fan.setFanPowerCoefficient3(power_vs_flow_curve.coefficient3xPOW2) fan.setFanPowerCoefficient4(power_vs_flow_curve.coefficient4xPOW3) elsif fan.class.name == 'OpenStudio::Model::FanZoneExhaust' motor_type = 'CONSTANT-RETURN' else raise('') end search_criteria = { 'motor_use' => motor_use, 'motor_type' => motor_type, 'number_of_poles' => 4.0, 'type' => 'Enclosed' } # Exception for small fans, including # zone exhaust, fan coil, and fan powered terminals. # In this case, use the 0.5 HP for the lookup. if fan_small_fan?(fan) nominal_hp = 0.5 else motor_properties = model_find_object(motors_table, search_criteria, motor_bhp) if motor_properties.nil? OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Fan', "For #{fan.name}, could not find motor properties using search criteria: #{search_criteria}, motor_bhp = #{motor_bhp} hp.") return [fan_motor_eff, nominal_hp] end nominal_hp = motor_properties['maximum_capacity'].to_f.round(1) # If the biggest fan motor size is hit, use the highest category efficiency if nominal_hp == 9999.0 OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Fan', "For #{fan.name}, there is no greater nominal HP. Use the efficiency of the largest motor category.") nominal_hp = motor_bhp end # Round to nearest whole HP for niceness if nominal_hp >= 2 nominal_hp = nominal_hp.round end end # Get the efficiency based on the nominal horsepower # Add 0.01 hp to avoid search errors. motor_properties = model_find_object(motors_table, search_criteria, nominal_hp + 0.01) if motor_properties.nil? OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Fan', "For #{fan.name}, could not find nominal motor properties using search criteria: #{search_criteria}, motor_hp = #{nominal_hp} hp.") return [fan_motor_eff, nominal_hp] end fan_motor_eff = motor_properties['nominal_full_load_efficiency'] return [fan_motor_eff, nominal_hp] end |
#fan_variable_volume_apply_prototype_fan_pressure_rise(fan_variable_volume) ⇒ Object
Sets the fan pressure rise based on the Prototype buildings inputs which are governed by the flow rate coming through the fan and whether the fan lives inside a unit heater, PTAC, etc.
1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1546 def fan_variable_volume_apply_prototype_fan_pressure_rise(fan_variable_volume) # 1000 Pa for supply fan and 458.33 Pa for return fan (accounts for efficiency differences between two fans) if fan_variable_volume.name.to_s.include?('Supply') sfan_deltaP = get_standards_constant('supply_fan_variable_volume_pressure_rise_value') fan_variable_volume.setPressureRise(sfan_deltaP) elsif fan_variable_volume.name.to_s.include?('Return') rfan_deltaP = get_standards_constant('return_fan_variable_volume_pressure_rise_value') fan_variable_volume.setPressureRise(rfan_deltaP) end return true end |
#fan_variable_volume_part_load_fan_power_limitation?(fan_variable_volume) ⇒ Boolean
Determines whether there is a requirement to have a VSD or some other method to reduce fan power at low part load ratios.
1506 1507 1508 1509 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1506 def fan_variable_volume_part_load_fan_power_limitation?(fan_variable_volume) part_load_control_required = false return part_load_control_required end |
#find_mech_room(model) ⇒ Object
This method determines where the mechanical room is in a building. Mechanical rooms are assumed to be conditioned spaces that are not plenums. It goes through all of the spaces in a model ignoring unconditioned spaces and plenums. It then determines the floor of the space, and the centroid of the floor. It accumulates space floor area*floor centroid information and total floor area. It uses this to determine the centroid of the building. It also notes if the space has a space type with ‘Electrical/Mechanical’ in it. If it does then it assumes that this is a mechanical room and tracks it separately. Once it has gone through all of the spaces it determines if any mechanical rooms were found. If some were found it finds the lowest one in the building (if more than one) and returns that along with all of the conditioned, non-plenum, spaces. If none were found then it searches for the lowest space in the building closest to the building centroid and returns that as the centroid (along with eth conditioned, non-plenum, spaces).
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb', line 570 def find_mech_room(model) cond_spaces = [] total_peak_flow = 0 mech_rooms = [] check_spaces = nil building_centre = Array.new(3, 0) total_peak_flow = 0 lowest_space = 1000000000000000 sp_func_regex = Regexp.new('Space Function') mech_regex = Regexp.new('Electrical/Mechanical') mech_flag = false index = 0 model.getSpaces.sort.each do |space| spaceType_name = space.spaceType.get.nameString sp_type = spaceType_name[15..-1] # Including regular expressions in the following match for cases where extra characters, which do not belong, are # added to either the space type in the model or the space type reference file. sp_type_info = @standards_data['tables']['space_types']['table'].detect do |data| (Regexp.new(data['space_type'].to_s.upcase).match(sp_type.upcase) || Regexp.new(sp_type.upcase).match(data['space_type'].to_s.upcase) || (data['space_type'].to_s.upcase == sp_type.upcase)) && (data['building_type'].to_s == 'Space Function') end if sp_type_info.nil? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.model_add_swh', "The space type called #{sp_type} could not be found. Please check that the schedules.json file is available and that the space types are spelled correctly") next end # Determine if space is heated or cooled via spacetype heating or cooling setpoints also checking if the space is # a plenum by checking if there is a hvac system associtated with it if sp_type_info['heating_setpoint_schedule'].nil? heated = false else heated = true end if sp_type_info['cooling_setpoint_schedule'].nil? cooled = false else cooled = true end if (sp_type_info['necb_hvac_system_selection_type'] == '- undefined -') || /undefined/.match(sp_type_info['necb_hvac_system_selection_type']) not_plenum = false else not_plenum = true end # Determine the bottom surface of the space and calculate it's centroid. Although the mech room is assumed to # be in a space that conditioned and is not a plenum (or attic space) all spaces in the building may not be. # Doing the following to determine the centroid for the entire building (including unconditioned or plenum # spaces). # Get the coordinates of the origin for the space (the coordinates of points in the space are relative to this). xOrigin = space.xOrigin yOrigin = space.yOrigin zOrigin = space.zOrigin # Get the surfaces for the space. space_surfaces = space.surfaces # Find the floor (aka the surface with the lowest centroid). min_surf = space_surfaces.min_by { |sp_surface| sp_surface.centroid.z.to_f } # The following is added to determine the overall floor centroid because some spaces have floors composed of more than one surface. floor_centroid = [0, 0, 0] space_surfaces.each do |sp_surface| if min_surf.centroid.z.to_f == sp_surface.centroid.z.to_f floor_centroid[0] = floor_centroid[0] + sp_surface.centroid.x.to_f * sp_surface.grossArea.to_f floor_centroid[1] = floor_centroid[1] + sp_surface.centroid.y.to_f * sp_surface.grossArea.to_f floor_centroid[2] = floor_centroid[2] + sp_surface.grossArea end end floor_centroid[0] = floor_centroid[0] / floor_centroid[2] floor_centroid[1] = floor_centroid[1] / floor_centroid[2] if lowest_space > (min_surf.centroid.z.to_f + zOrigin) lowest_space = min_surf.centroid.z.to_f + zOrigin end # This part is used to determine the overall x, y centre of the building. This is determined by summing the x # and y components times the floor area and diving by the total floor area. This is only for conditioned spaces. building_centre[0] += (floor_centroid[0] + xOrigin) * floor_centroid[2] building_centre[1] += (floor_centroid[1] + yOrigin) * floor_centroid[2] building_centre[2] += (floor_centroid[2]) # Check if the space is conditioned and not a plenum. If it is then add it to the list of conditioned, non-plenum, # spaces and check if it has a 'Mechanical/Electrical' space type. Note that the mech room is assumed to # be in a space that conditioned and is not a plenum (or attic space). if (heated == true || cooled == true) && (not_plenum == true) if mech_regex.match(spaceType_name) mech_rooms << index end cond_space = { 'space_name' => space.nameString, 'space' => space, 'space_centroid' => [floor_centroid[0] + xOrigin, floor_centroid[1] + yOrigin, min_surf.centroid.z.to_f + zOrigin], 'building_cent_dist' => 0 } cond_spaces << cond_space index += 1 end end return [cond_spaces[mech_rooms[0]], cond_spaces] if mech_rooms.size == 1 if mech_rooms.size > 1 check_spaces = [] lowest_space = 10000000000000 mech_rooms.each do |mech_room| check_spaces << cond_spaces[mech_room] if cond_spaces[mech_room]['space_centroid'][2].to_f < lowest_space lowest_space = cond_spaces[mech_room]['space_centroid'][2].to_f end end else check_spaces = cond_spaces end # If no heated or cooled spaces were found then return false if cond_spaces.empty? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.model_add_swh', 'No heated or cooled spaces types could be found which were not also a plenum.') return false end # This is where the average happens building_centre[0] /= building_centre[2] building_centre[1] /= building_centre[2] # Go through each space on the lowest floor of the building and determine the distance between the centroid of the # space's floors and the center of the building I calculated just above. centre_spaces = [] check_spaces.each do |check_space| if check_space['space_centroid'][2] == lowest_space check_space['building_cent_dist'] = Math.sqrt(((check_space['space_centroid'][0] - building_centre[0])**2) + ((check_space['space_centroid'][1] - building_centre[1])**2)) centre_spaces << check_space end end # Determine which of the floor spaces is closest to the centre of the building and that one becomes the location of # the mechanical room. centre_space = centre_spaces.min_by { |dist| dist['building_cent_dist'].round(1) } return [centre_space, cond_spaces] end |
#friction_factor(re_pipe, relative_rough) ⇒ Object
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb', line 531 def friction_factor(re_pipe, relative_rough) # This method determines the Darcy-Weisbach friction factor assuming the pipe is circular and filled. if re_pipe <= 2100 # Laminar flow use the Uagen-Poiseuille equation. https://neutrium.net/fluid_flow/pressure-loss-in-pipe # accessed 2018-07-25. f = 64.to_f / re_pipe.to_f elsif re_pipe > 2100 && re_pipe <= 4000 # In the transition flow region I interpolate by Reynolds number between laminar and turbulent regimes. Yeah, that's # crap but if you can come up with something better you are welcome to replace what I have below. flam = 64.to_f / 2100.to_f pipe_rough_fact = relative_rough / 3.7 factor_A = -2 * Math.log10(pipe_rough_fact + (12.to_f / 4000.to_f)) factor_B = -2 * Math.log10(pipe_rough_fact + ((2.51 * factor_A) / 4000)) factor_C = -2 * Math.log10(pipe_rough_fact + ((2.51 * factor_B) / 4000)) fturb = 1 / ((factor_A - (((factor_B - factor_A)**2) / (factor_C - 2 * factor_B + factor_A)))**2) re_int = (re_pipe - 2100.to_f) / 1900.to_f f = ((fturb - flam) * re_int) + flam elsif re_pipe > 4000 # Turbulent flow use Serghide's Equation which I got from https://neutrium.net/fluid_flow/pressure-loss-in-pipe # accessed 2018-07-25. Apparently it is good for 4000 < Re < 1x10^10 and relative roughness between 1x10-7 and 1. pipe_rough_fact = relative_rough / 3.7 factor_A = -2 * Math.log10(pipe_rough_fact + (12 / re_pipe)) factor_B = -2 * Math.log10(pipe_rough_fact + ((2.51 * factor_A) / re_pipe)) factor_C = -2 * Math.log10(pipe_rough_fact + ((2.51 * factor_B) / re_pipe)) f = 1 / ((factor_A - (((factor_B - factor_A)**2) / (factor_C - 2 * factor_B + factor_A)))**2) end return f end |
#get_all_spacetype_names ⇒ Object
157 158 159 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 157 def get_all_spacetype_names return @standards_data['space_types'].map { |space_types| [space_types['building_type'], space_types['space_type']] } end |
#get_any_number_ppm(model) ⇒ Object
(upon other BTAP tasks) This function can be added to btap/schedules.rb > module StandardScheduleTypeLimits
Define ScheduleTypeLimits for Any_Number_ppm
1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1741 def get_any_number_ppm(model) name = 'Any_Number_ppm' any_number_ppm_schedule_type_limits = model.getScheduleTypeLimitsByName(name) return any_number_ppm_schedule_type_limits.get unless any_number_ppm_schedule_type_limits.empty? any_number_ppm_schedule_type_limits = OpenStudio::Model::ScheduleTypeLimits.new(model) any_number_ppm_schedule_type_limits.setName(name) any_number_ppm_schedule_type_limits.setNumericType('CONTINUOUS') any_number_ppm_schedule_type_limits.setUnitType('Dimensionless') any_number_ppm_schedule_type_limits.setLowerLimitValue(400.0) any_number_ppm_schedule_type_limits.setUpperLimitValue(1000.0) return any_number_ppm_schedule_type_limits end |
#get_climate_zone_index(hdd) ⇒ Fixnum
This model gets the climate zone column index from tables 3.2.2.x
1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1333 def get_climate_zone_index(hdd) # check for climate zone index from NECB 3.2.2.X case hdd when 0..2999 then return 0 # climate zone 4 when 3000..3999 then return 1 # climate zone 5 when 4000..4999 then return 2 # climate zone 6 when 5000..5999 then return 3 # climate zone 7a when 6000..6999 then return 4 # climate zone 7b when 7000..1000000 then return 5 # climate zone 8 end end |
#get_climate_zone_name(hdd) ⇒ Fixnum
This model gets the climate zone name and returns the climate zone string.
1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1355 def get_climate_zone_name(hdd) case get_climate_zone_index(hdd) when 0 then return '4' when 1 then return '5' # climate zone 5 when 2 then return '6' # climate zone 6 when 3 then return '7a' # climate zone 7a when 4 then return '7b' # climate zone 7b when 5 then return '8' # climate zone 8 end end |
#get_max_space_height_for_space_type(space_type:) ⇒ Object
This method is needed to the space_type_apply_internal_loads space needed for the calculation of atriums’ LPD when LED lighting is used in atriums. ***END***
1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1952 def get_max_space_height_for_space_type(space_type:) # initialize return value to zero. max_space_height_for_space_type = 0.0 # Interate through all spaces in model.. not just ones that have space type defined.. Is this right sara? space_type.spaces.sort.each do |space| # Get only the wall type surfaces and iterate throught them. space.surfaces.sort.select(&:surfaceType == 'Wall').each do |wall_surface| # Find the vertex with the max z value. vertex_with_max_height = wall_surface.vertices.max_by(&:z) # replace max if this surface has something bigger. max_space_height_for_space_type = vertex_with_max_height.z if vertex_with_max_height.z > max_space_height_for_space_type end end return max_space_height_for_space_type end |
#get_necb_hdd18(model:, necb_hdd: true) ⇒ Object
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 178 def get_necb_hdd18(model:, necb_hdd: true) max_distance_tolerance = 500000 min_distance = 100000000000000.0 necb_closest = nil weather_file_path = model.weatherFile.get.path.get.to_s epw_file = model.weatherFile.get.file.get stat_file_path = weather_file_path.gsub('.epw', '.stat') stat_file = OpenstudioStandards::Weather::StatFile.new(stat_file_path) # If necb_hdd is false use the information in the .stat file associated with the.epw file. unless necb_hdd return stat_file.hdd18 end # this extracts the table from the json database. necb_2015_table_c1 = @standards_data['tables']['necb_2015_table_c1']['table'] necb_2015_table_c1.each do |necb| next if necb['lat_long'].nil? # Need this until Tyson cleans up table. dist = distance([epw_file.latitude, epw_file.longitude], necb['lat_long']) if min_distance > dist min_distance = dist necb_closest = necb end end if ((min_distance / 1000.0) > max_distance_tolerance) && !stat_file.hdd18.nil? puts "Could not find close NECB HDD from Table C1 < #{max_distance_tolerance}km. Closest city is #{min_distance / 1000.0}km away. Using epw hdd18 instead." return stat_file.hdd18 else dist_clause = "%.2f % #{(min_distance / 1000.0)}" puts "INFO:NECB HDD18 of #{necb_closest['degree_days_below_18_c'].to_f} at nearest city #{necb_closest['city']},#{necb_closest['province']}, at a distance of " + dist_clause + 'km from epw location. Ref: nbc_2015_table_c1' return necb_closest['degree_days_below_18_c'].to_f end end |
#get_necb_spacetype_system_selection(space) ⇒ Object
Determines what system index number is required for the space’s spacetype by NECB rules.
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 684 def get_necb_spacetype_system_selection(space) space_type_table = @standards_data['space_types'] space_type_data = model_find_object(space_type_table, 'space_type' => space.spaceType.get.standardsSpaceType.get, 'building_type' => space.spaceType.get.standardsBuildingType.get) if space_type_data.nil? raise("Could not find space_type_data for #{{ 'space_type' => space.spaceType.get.standardsSpaceType.get, 'building_type' => space.spaceType.get.standardsBuildingType.get }} ") end # identify space-system_index and assign the right NECB system type 1-7. necb_hvac_system_selection_table = @standards_data['necb_hvac_system_selection_type'] necb_hvac_system_select = necb_hvac_system_selection_table.detect do |curr_necb_hvac_system_select| curr_necb_hvac_system_select['necb_hvac_system_selection_type'] == space_type_data['necb_hvac_system_selection_type'] && curr_necb_hvac_system_select['min_stories'] <= space.model.getBuilding.standardsNumberOfAboveGroundStories.get && curr_necb_hvac_system_select['max_stories'] >= space.model.getBuilding.standardsNumberOfAboveGroundStories.get && curr_necb_hvac_system_select['min_cooling_capacity_kw'] <= stored_space_cooling_load(space) && curr_necb_hvac_system_select['max_cooling_capacity_kw'] >= stored_space_cooling_load(space) end raise('could not find system for given spacetype') if necb_hvac_system_select.nil? return necb_hvac_system_select['system_type'] end |
#get_necb_thermal_zone_system_selection(tz) ⇒ Object
Determines what system index number is required for the thermal zone based on the spacetypes it contains
708 709 710 711 712 713 714 715 716 717 718 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 708 def get_necb_thermal_zone_system_selection(tz) systems = [] tz.spaces.each do |space| systems << get_necb_spacetype_system_selection(space) end systems.uniq! systems.compact! raise('This thermal zone spaces require different systems.') if systems.size > 1 return systems.first end |
#get_parameters_sidelighting(daylight_space:, floor_surface:, floor_vertices:, floor_area:, primary_sidelighted_area:, area_weighted_vt_handle:, window_area_sum:) ⇒ Object
The below method is for the ‘model_add_daylighting_controls’ method
1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1969 def get_parameters_sidelighting(daylight_space:, floor_surface:, floor_vertices:, floor_area:, primary_sidelighted_area:, area_weighted_vt_handle:, window_area_sum:) daylight_space.surfaces.sort.each do |surface| ##### Get the vertices of each exterior wall of the daylight_space on the floor ##### (these vertices will be used to calculate daylight_space depth in relation to the exterior wall, and ##### the distance of the window to vertical walls on each side of the window) if surface.outsideBoundaryCondition == 'Outdoors' && surface.surfaceType == 'Wall' wall_vertices_x_on_floor = [] wall_vertices_y_on_floor = [] surface_z_min = [surface.vertices[0].z, surface.vertices[1].z, surface.vertices[2].z, surface.vertices[3].z].min surface.vertices.each do |vertex| # puts vertex.z if vertex.z == surface_z_min && surface_z_min == floor_vertices[0][0].z wall_vertices_x_on_floor << vertex.x wall_vertices_y_on_floor << vertex.y end end end if surface.outsideBoundaryCondition == 'Outdoors' && surface.surfaceType == 'Wall' && surface_z_min == floor_vertices[0][0].z ##### Calculate the daylight_space depth in relation to the considered exterior wall. ##### To calculate daylight_space depth, first get the floor vertices which are on the opposite side of the considered exterior wall. floor_vertices_x_wall_opposite = [] floor_vertices_y_wall_opposite = [] floor_vertices[0].each do |floor_vertex| if (floor_vertex.x != wall_vertices_x_on_floor[0] && floor_vertex.y != wall_vertices_y_on_floor[0]) || (floor_vertex.x != wall_vertices_x_on_floor[1] && floor_vertex.y != wall_vertices_y_on_floor[1]) floor_vertices_x_wall_opposite << floor_vertex.x floor_vertices_y_wall_opposite << floor_vertex.y end end ##### To calculate daylight_space depth, second calculate floor length on both sides: (1) exterior wall side, (2) on the opposite side of the exterior wall floor_width_wall_side = Math.sqrt((wall_vertices_x_on_floor[0] - wall_vertices_x_on_floor[1])**2 + (wall_vertices_y_on_floor[0] - wall_vertices_y_on_floor[1])**2) floor_width_wall_opposite = Math.sqrt((floor_vertices_x_wall_opposite[0] - floor_vertices_x_wall_opposite[1])**2 + (floor_vertices_y_wall_opposite[0] - floor_vertices_y_wall_opposite[1])**2) ##### Now, daylight_space depth can be calculated using the floor area and two lengths of the floor (note that these two lengths are in parallel to each other). daylight_space_depth = 2 * floor_area / (floor_width_wall_side + floor_width_wall_opposite) ##### Loop through the windows (including fixed and operable ones) to get window specification (width, height, area, visible transmittance (VT)), and area-weighted VT surface.subSurfaces.sort.each do |subsurface| # puts subsurface.name.to_s if subsurface.subSurfaceType == 'FixedWindow' || subsurface.subSurfaceType == 'OperableWindow' window_vt = subsurface.visibleTransmittance window_vt = window_vt.get window_area = subsurface.netArea window_area_sum += window_area area_weighted_vt_handle += window_area * window_vt window_vertices = subsurface.vertices if window_vertices[0].z.round(2) == window_vertices[1].z.round(2) window_width = Math.sqrt((window_vertices[0].x - window_vertices[1].x)**2.0 + (window_vertices[0].y - window_vertices[1].y)**2.0) else window_width = Math.sqrt((window_vertices[1].x - window_vertices[2].x)**2.0 + (window_vertices[1].y - window_vertices[2].y)**2.0) end window_head_height = [window_vertices[0].z, window_vertices[1].z, window_vertices[2].z, window_vertices[3].z].max.round(2) primary_sidelighted_area_depth = [window_head_height, daylight_space_depth].min # as per NECB2011: 4.2.2.9. ##### Calculate the distance of the window to vertical walls on each side of the window (this is used to determine the sidelighted area's width). window_vertices_on_floor = [] window_vertices.each do |vertex| window_vertices_on_floor << floor_surface.plane.project(vertex) end window_wall_distance_side1 = [Math.sqrt((wall_vertices_x_on_floor[0] - window_vertices_on_floor[0].x)**2.0 + (wall_vertices_y_on_floor[0] - window_vertices_on_floor[0].y)**2.0), Math.sqrt((wall_vertices_x_on_floor[0] - window_vertices_on_floor[2].x)**2.0 + (wall_vertices_y_on_floor[0] - window_vertices_on_floor[2].y)**2.0), 0.6].min # 0.6 m as per NECB2011: 4.2.2.9. window_wall_distance_side2 = [Math.sqrt((wall_vertices_x_on_floor[1] - window_vertices_on_floor[0].x)**2.0 + (wall_vertices_y_on_floor[1] - window_vertices_on_floor[0].y)**2.0), Math.sqrt((wall_vertices_x_on_floor[1] - window_vertices_on_floor[2].x)**2.0 + (wall_vertices_y_on_floor[1] - window_vertices_on_floor[2].y)**2.0), 0.6].min # 0.6 m as per NECB2011: 4.2.2.9. primary_sidelighted_area_width = window_wall_distance_side1 + window_width + window_wall_distance_side2 primary_sidelighted_area += primary_sidelighted_area_depth * primary_sidelighted_area_width # if subsurface.subSurfaceType == "FixedWindow" || subsurface.subSurfaceType == "OperableWindow" end # surface.subSurfaces.each do |subsurface| end # if surface.outsideBoundaryCondition == "Outdoors" && surface.surfaceType == "Wall" && surface_z_min == floor_vertices[0][0].z end # daylight_space.surfaces.each do |surface| end return primary_sidelighted_area, area_weighted_vt_handle, window_area_sum end |
#get_parameters_skylight(daylight_space:, skylight_area_weighted_vt_handle:, skylight_area_sum:, daylighted_under_skylight_area:) ⇒ Object
The below method is for the ‘model_add_daylighting_controls’ method
2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 2051 def get_parameters_skylight(daylight_space:, skylight_area_weighted_vt_handle:, skylight_area_sum:, daylighted_under_skylight_area:) daylight_space.surfaces.sort.each do |surface| ##### Get roof vertices if surface.outsideBoundaryCondition == 'Outdoors' && surface.surfaceType == 'RoofCeiling' roof_vertices = surface.vertices end ##### Loop through each subsurface to calculate daylighted_area_under_skylights and skylight_effective_aperture for each daylight_space surface.subSurfaces.sort.each do |subsurface| if subsurface.subSurfaceType == 'Skylight' skylight_vt = subsurface.visibleTransmittance skylight_vt = skylight_vt.get skylight_area = subsurface.netArea skylight_area_sum += skylight_area skylight_area_weighted_vt_handle += skylight_area * skylight_vt ##### Get skylight vertices skylight_vertices = subsurface.vertices ##### Calculate skylight width and height skylight_width = Math.sqrt((skylight_vertices[0].x - skylight_vertices[1].x)**2.0 + (skylight_vertices[0].y - skylight_vertices[1].y)**2.0) skylight_length = Math.sqrt((skylight_vertices[0].x - skylight_vertices[3].x)**2.0 + (skylight_vertices[0].y - skylight_vertices[3].y)**2.0) ##### Get ceiling height assuming the skylight is flush with the ceiling ceiling_height = skylight_vertices[0].z ##### Calculate roof lengths ##### (Note: used OpenStudio BCL measure called "assign_ashrae_9012010_daylighting_controls" with some changes/correcctions) roof_length_0 = Math.sqrt((roof_vertices[0].x - roof_vertices[1].x)**2.0 + (roof_vertices[0].y - roof_vertices[1].y)**2.0) roof_length_1 = Math.sqrt((roof_vertices[1].x - roof_vertices[2].x)**2.0 + (roof_vertices[1].y - roof_vertices[2].y)**2.0) roof_length_2 = Math.sqrt((roof_vertices[2].x - roof_vertices[3].x)**2.0 + (roof_vertices[2].y - roof_vertices[3].y)**2.0) roof_length_3 = Math.sqrt((roof_vertices[3].x - roof_vertices[0].x)**2.0 + (roof_vertices[3].y - roof_vertices[0].y)**2.0) ##### Find the skylight point that is the closest one to roof_vertex_0 ##### (Note: used OpenStudio BCL measure called "assign_ashrae_9012010_daylighting_controls" with some changes/correcctions) roof_vertex_0_skylight_vertex_0 = Math.sqrt((roof_vertices[0].x - skylight_vertices[0].x)**2.0 + (roof_vertices[0].y - skylight_vertices[0].y)**2.0) roof_vertex_0_skylight_vertex_1 = Math.sqrt((roof_vertices[0].x - skylight_vertices[1].x)**2.0 + (roof_vertices[0].y - skylight_vertices[1].y)**2.0) roof_vertex_0_skylight_vertex_2 = Math.sqrt((roof_vertices[0].x - skylight_vertices[2].x)**2.0 + (roof_vertices[0].y - skylight_vertices[2].y)**2.0) roof_vertex_0_skylight_vertex_3 = Math.sqrt((roof_vertices[0].x - skylight_vertices[3].x)**2.0 + (roof_vertices[0].y - skylight_vertices[3].y)**2.0) roof_vertex_0_closest_distance = [roof_vertex_0_skylight_vertex_0, roof_vertex_0_skylight_vertex_1, roof_vertex_0_skylight_vertex_2, roof_vertex_0_skylight_vertex_3].min if roof_vertex_0_closest_distance == roof_vertex_0_skylight_vertex_0 roof_vertex_0_closest_point = skylight_vertices[0] elsif roof_vertex_0_closest_distance == roof_vertex_0_skylight_vertex_1 roof_vertex_0_closest_point = skylight_vertices[1] elsif roof_vertex_0_closest_distance == roof_vertex_0_skylight_vertex_2 roof_vertex_0_closest_point = skylight_vertices[2] elsif roof_vertex_0_closest_distance == roof_vertex_0_skylight_vertex_3 roof_vertex_0_closest_point = skylight_vertices[3] end ##### Find the skylight point that is the closest one to roof_vertex_2 ##### (Note: used OpenStudio BCL measure called "assign_ashrae_9012010_daylighting_controls" with some changes/correcctions) roof_vertex_2_skylight_vertex_0 = Math.sqrt((roof_vertices[2].x - skylight_vertices[0].x)**2.0 + (roof_vertices[2].y - skylight_vertices[0].y)**2.0) roof_vertex_2_skylight_vertex_1 = Math.sqrt((roof_vertices[2].x - skylight_vertices[1].x)**2.0 + (roof_vertices[2].y - skylight_vertices[1].y)**2.0) roof_vertex_2_skylight_vertex_2 = Math.sqrt((roof_vertices[2].x - skylight_vertices[2].x)**2.0 + (roof_vertices[2].y - skylight_vertices[2].y)**2.0) roof_vertex_2_skylight_vertex_3 = Math.sqrt((roof_vertices[2].x - skylight_vertices[3].x)**2.0 + (roof_vertices[2].y - skylight_vertices[3].y)**2.0) roof_vertex_2_closest_distance = [roof_vertex_2_skylight_vertex_0, roof_vertex_2_skylight_vertex_1, roof_vertex_2_skylight_vertex_2, roof_vertex_2_skylight_vertex_3].min if roof_vertex_2_closest_distance == roof_vertex_2_skylight_vertex_0 roof_vertex_2_closest_point = skylight_vertices[0] elsif roof_vertex_2_closest_distance == roof_vertex_2_skylight_vertex_1 roof_vertex_2_closest_point = skylight_vertices[1] elsif roof_vertex_2_closest_distance == roof_vertex_2_skylight_vertex_2 roof_vertex_2_closest_point = skylight_vertices[2] elsif roof_vertex_2_closest_distance == roof_vertex_2_skylight_vertex_3 roof_vertex_2_closest_point = skylight_vertices[3] end ##### Calculate the vertical distance from the closest skylight points (projection onto the roof) to the wall (projection onto the roof) for roof_vertex_0 and roof_vertex_2 ##### (Note: used OpenStudio BCL measure called "assign_ashrae_9012010_daylighting_controls" with some changes/correcctions) ##### For the calculation of each vertical distance: (1) first the area of the triangle is calculated knowing the cooridantes of its three corners; ##### (2) the vertical distance (i.e. triangle height) is calculated knowing the triangle area and the associated roof length. rv_0_triangle_0_area = 0.5 * (((roof_vertex_0_closest_point.x - roof_vertices[1].x) * (roof_vertex_0_closest_point.y - roof_vertices[0].y)) - ((roof_vertex_0_closest_point.x - roof_vertices[0].x) * (roof_vertex_0_closest_point.y - roof_vertices[1].y))).abs rv_0_distance_0 = (2.0 * rv_0_triangle_0_area) / roof_length_0 rv_0_triangle_3_area = 0.5 * (((roof_vertex_0_closest_point.x - roof_vertices[3].x) * (roof_vertex_0_closest_point.y - roof_vertices[0].y)) - ((roof_vertex_0_closest_point.x - roof_vertices[0].x) * (roof_vertex_0_closest_point.y - roof_vertices[3].y))).abs rv_0_distance_3 = (2.0 * rv_0_triangle_3_area) / roof_length_3 rv_2_triangle_1_area = 0.5 * (((roof_vertex_2_closest_point.x - roof_vertices[1].x) * (roof_vertex_2_closest_point.y - roof_vertices[2].y)) - ((roof_vertex_2_closest_point.x - roof_vertices[2].x) * (roof_vertex_2_closest_point.y - roof_vertices[1].y))).abs rv_2_distance_1 = (2.0 * rv_2_triangle_1_area) / roof_length_1 rv_2_triangle_2_area = 0.5 * (((roof_vertex_2_closest_point.x - roof_vertices[3].x) * (roof_vertex_2_closest_point.y - roof_vertices[2].y)) - ((roof_vertex_2_closest_point.x - roof_vertices[2].x) * (roof_vertex_2_closest_point.y - roof_vertices[3].y))).abs rv_2_distance_2 = (2.0 * rv_2_triangle_2_area) / roof_length_2 ##### Set the vertical distances from the closest skylight points (projection onto the roof) to the wall (projection onto the roof) for roof_vertex_0 and roof_vertex_2 distance_1 = rv_0_distance_0 distance_2 = rv_0_distance_3 distance_3 = rv_2_distance_1 distance_4 = rv_2_distance_2 ##### Calculate the width and length of the daylighted area under the skylight as per NECB2011: 4.2.2.5. ##### Note: In the below loops, if any exterior walls has window(s), the width and length of the daylighted area under the skylight are re-calculated as per NECB2011: 4.2.2.5. daylighted_under_skylight_width = skylight_width + [0.7 * ceiling_height, distance_1].min + [0.7 * ceiling_height, distance_4].min daylighted_under_skylight_length = skylight_length + [0.7 * ceiling_height, distance_2].min + [0.7 * ceiling_height, distance_3].min ##### As noted above, the width and length of the daylighted area under the skylight are re-calculated (as per NECB2011: 4.2.2.5.), if any exterior walls has window(s). ##### To this end, the window_head_height should be calculated, as below: daylight_space.surfaces.sort.each do |curr_surface| if curr_surface.outsideBoundaryCondition == 'Outdoors' && curr_surface.surfaceType == 'Wall' wall_vertices_on_floor_x = [] wall_vertices_on_floor_y = [] wall_vertices = curr_surface.vertices if wall_vertices[0].z == wall_vertices[1].z wall_vertices_on_floor_x << wall_vertices[0].x wall_vertices_on_floor_x << wall_vertices[1].x wall_vertices_on_floor_y << wall_vertices[0].y wall_vertices_on_floor_y << wall_vertices[1].y elsif wall_vertices[0].z == wall_vertices[3].z wall_vertices_on_floor_x << wall_vertices[0].x wall_vertices_on_floor_x << wall_vertices[3].x wall_vertices_on_floor_y << wall_vertices[0].y wall_vertices_on_floor_y << wall_vertices[3].y end window_vertices = subsurface.vertices window_head_height = [window_vertices[0].z, window_vertices[1].z, window_vertices[2].z, window_vertices[3].z].max.round(2) ##### Calculate the exterior wall length (on the floor) exterior_wall_length = Math.sqrt((wall_vertices_on_floor_x[0] - wall_vertices_on_floor_x[1])**2.0 + (wall_vertices_on_floor_y[0] - wall_vertices_on_floor_y[1])**2.0) ##### Calculate the vertical distance of skylight_vertices[0] projection onto the roof/floor to the exterior wall skylight_vertex_0_triangle_area = 0.5 * (((wall_vertices_on_floor_x[0] - wall_vertices_on_floor_x[1]) * (wall_vertices_on_floor_y[0] - skylight_vertices[0].y)) - ((wall_vertices_on_floor_x[0] - skylight_vertices[0].x) * (wall_vertices_on_floor_y[0] - wall_vertices_on_floor_y[1]))).abs skylight_vertex_0_distance = (2.0 * skylight_vertex_0_triangle_area) / exterior_wall_length ##### Calculate the vertical distance of skylight_vertices[1] projection onto the roof/floor to the exterior wall skylight_vertex_1_triangle_area = 0.5 * (((wall_vertices_on_floor_x[0] - wall_vertices_on_floor_x[1]) * (wall_vertices_on_floor_y[0] - skylight_vertices[1].y)) - ((wall_vertices_on_floor_x[0] - skylight_vertices[1].x) * (wall_vertices_on_floor_y[0] - wall_vertices_on_floor_y[1]))).abs skylight_vertex_1_distance = (2.0 * skylight_vertex_1_triangle_area) / exterior_wall_length ##### Calculate the vertical distance of skylight_vertices[3] projection onto the roof/floor to the exterior wall skylight_vertex_3_triangle_area = 0.5 * (((wall_vertices_on_floor_x[0] - wall_vertices_on_floor_x[1]) * (wall_vertices_on_floor_y[0] - skylight_vertices[3].y)) - ((wall_vertices_on_floor_x[0] - skylight_vertices[3].x) * (wall_vertices_on_floor_y[0] - wall_vertices_on_floor_y[1]))).abs skylight_vertex_3_distance = (2.0 * skylight_vertex_3_triangle_area) / exterior_wall_length ##### Loop through the subsurfaces that has exterior windows to re-calculate the width and length of the daylighted area under the skylight curr_surface.subSurfaces.sort.each do |curr_subsurface| if curr_subsurface.subSurfaceType == 'FixedWindow' || curr_subsurface.subSurfaceType == 'OperableWindow' if skylight_vertex_0_distance == skylight_vertex_1_distance # skylight_01 is in parellel to the exterior wall if skylight_vertex_0_distance.round(2) == distance_2.round(2) daylighted_under_skylight_length = skylight_length + [0.7 * ceiling_height, distance_2, distance_2 - window_head_height].min + [0.7 * ceiling_height, distance_3].min elsif skylight_vertex_0_distance.round(2) == distance_3.round(2) daylighted_under_skylight_length = skylight_length + [0.7 * ceiling_height, distance_2].min + [0.7 * ceiling_height, distance_3, distance_3 - window_head_height].min end elsif skylight_vertex_0_distance == skylight_vertex_3_distance # skylight_03 is in parellel to the exterior wall if skylight_vertex_0_distance.round(2) == distance_1.round(2) daylighted_under_skylight_width = skylight_width + [0.7 * ceiling_height, distance_1, distance_1 - window_head_height].min + [0.7 * ceiling_height, distance_4].min elsif skylight_vertex_0_distance.round(2) == distance_4.round(2) daylighted_under_skylight_width = skylight_width + [0.7 * ceiling_height, distance_1].min + [0.7 * ceiling_height, distance_4, distance_4 - window_head_height].min end # if skylight_vertex_0_distance == skylight_vertex_1_distance end daylighted_under_skylight_area += daylighted_under_skylight_length * daylighted_under_skylight_width # if subsurface.subSurfaceType == "FixedWindow" || subsurface.subSurfaceType == "OperableWindow" end # surface.subSurfaces.each do |subsurface| end # if surface.outsideBoundaryCondition == "Outdoors" && surface.surfaceType == "Wall" end # daylight_space.surfaces.each do |surface| end # if subsurface.subSurfaceType == "Skylight" end # surface.subSurfaces.each do |subsurface| end # daylight_space.surfaces.each do |surface| end return daylighted_under_skylight_area, skylight_area_weighted_vt_handle, skylight_area_sum end |
#get_qaqc_table(table_name:, search_criteria: nil) ⇒ Object
140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 140 def get_qaqc_table(table_name:, search_criteria: nil) return_objects = nil table = @qaqc_data['tables'][table_name] raise("could not find #{table_name} in qaqc table database. ") if table.nil? return table if search_criteria.nil? # removed table beause need to use the object['refs'] rows = table['table'] search_criteria.each do |key, value| rows = rows.select { |row| row[key] == value } end return rows end |
#get_sql_table_to_json(model, report_name, report_for_string, table_name) ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 4 def get_sql_table_to_json(model, report_name, report_for_string, table_name) table = [] query_row_names = " SELECT DISTINCT RowName FROM tabulardatawithstrings WHERE ReportName='#{report_name}' AND ReportForString='#{report_for_string}' AND TableName='#{table_name}'" row_names = model.sqlFile.get.execAndReturnVectorOfString(query_row_names).get # get Columns query_col_names = " SELECT DISTINCT ColumnName FROM tabulardatawithstrings WHERE ReportName='#{report_name}' AND ReportForString='#{report_for_string}' AND TableName='#{table_name}'" col_names = model.sqlFile.get.execAndReturnVectorOfString(query_col_names).get # get units query_unit_names = " SELECT DISTINCT Units FROM tabulardatawithstrings WHERE ReportName='#{report_name}' AND ReportForString='#{report_for_string}' AND TableName='#{table_name}'" unit_names = model.sqlFile.get.execAndReturnVectorOfString(query_unit_names).get row_names.each do |row| next if row.nil? || row == '' row_hash = {} row_hash[:name] = row col_names.each do |col| unit_names.each do |unit| query = " SELECT Value FROM tabulardatawithstrings WHERE ReportName='#{report_name}' AND ReportForString='#{report_for_string}' AND TableName='#{table_name}' AND RowName='#{row}' AND ColumnName='#{col}' AND Units='#{unit}' " column_name = col.to_s.gsub(/\s+/, '_').downcase # If the column name is "additional_fuel" and the file contains a boiler with a FuelOilNo2 fuel type assume # the column name should be "fueloilno2". if column_name.include? 'additional_fuel' model.getPlantLoops.sort.each do |iplantloop| boilers = iplantloop.components.select { |icomponent| icomponent.to_BoilerHotWater.is_initialized } column_name = 'fueloilno2' unless boilers.select { |boiler| boiler.to_BoilerHotWater.get.fuelType.to_s == 'FuelOilNo2' }.empty? end end column_name += "_#{unit}" if unit != '' value = model.sqlFile.get.execAndReturnFirstString(query) next if value.empty? || value.get.nil? value = value.get.strip # check is value is a number if (begin Float(value) rescue StandardError false end) && value.to_f != 0 row_hash[column_name] = value.to_f # Check if value is a date elsif unit == '' && value =~ /\d\d-\D\D\D-\d\d:\d\d/ row_hash[column_name] = DateTime.parse(value) # skip if value in an empty string or a zero value elsif value != '' && value != '0.00' row_hash[column_name] = value end end end if row_hash.size > 1 table << row_hash end end result = { report_name: report_name, report_for_string: report_for_string, table_name: table_name, table: table } return result end |
#get_sql_tables_to_json(model) ⇒ Object
106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 106 def get_sql_tables_to_json(model) sql_data = [] sql_data << get_sql_table_to_json(model, 'AnnualBuildingUtilityPerformanceSummary', 'Entire Facility', 'End Uses') sql_data << get_sql_table_to_json(model, 'AnnualBuildingUtilityPerformanceSummary', 'Entire Facility', 'Site and Source Energy') # sql_data << get_sql_table_to_json(model, "AnnualBuildingUtilityPerformanceSummary", "Entire Facility", "On-Site Thermal Sources") # sql_data << get_sql_table_to_json(model, "AnnualBuildingUtilityPerformanceSummary", "Entire Facility", "Comfort and Setpoint Not Met Summary") # sql_data << get_sql_table_to_json(model, "InputVerificationandResultsSummary", "Entire Facility", "Window-Wall Ratio") # sql_data << get_sql_table_to_json(model, "InputVerificationandResultsSummary", "Entire Facility", "Conditioned Window-Wall Ratio") # sql_data << get_sql_table_to_json(model, "InputVerificationandResultsSummary", "Entire Facility", "Skylight-Roof Ratio") # sql_data << get_sql_table_to_json(model, "DemandEndUseComponentsSummary", "Entire Facility", "End Uses") # sql_data << get_sql_table_to_json(model, "ComponentSizingSummary", "Entire Facility", "AirLoopHVAC") return sql_data end |
#get_standard_constant_value(constant_name:) ⇒ Object
58 59 60 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 58 def get_standard_constant_value(constant_name:) puts 'do nothing' end |
#get_standards_constant(name) ⇒ Object
129 130 131 132 133 134 135 136 137 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 129 def get_standards_constant(name) object = @standards_data['constants'][name] if object.nil? || object['value'].nil? raise("could not find #{name} in standards constants database. ") end return object['value'] end |
#get_standards_formula(name) ⇒ Object
139 140 141 142 143 144 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 139 def get_standards_formula(name) object = @standards_data['formulas'][name] raise("could not find #{name} in standards formual database. ") if object.nil? || object['value'].nil? return object['value'] end |
#get_standards_table(table_name:) ⇒ Object
50 51 52 53 54 55 56 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 50 def get_standards_table(table_name:) if @standards_data['tables'][table_name].nil? = "Could not find table #{table_name} in database." OpenStudio.logFree(OpenStudio::Error, 'openstudio.Standards.NECB', ) end @standards_data['tables'][table_name] end |
#get_surface_exp_per(floor, walls) ⇒ Object
Find the exposed perimeter of a floor surface. For each side of the floor loop through the walls and find the walls that share sides with the floor. Then sum the lengths of the sides of the walls that come in contact with sides of the floor. created by: Kamel Haddad ([email protected])
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 901 def get_surface_exp_per(floor,walls) floor_exp_per = 0.0 vert1 = floor.vertices[0] # loop through the indices of the floor surface for index in 1..floor.vertices.size if index < floor.vertices.size vert2 = floor.vertices[index] else vert2 = floor.vertices[0] end side_length = ((vert2.x-vert1.x)**2+(vert2.y-vert1.y)**2+(vert2.z-vert1.z)**2)**0.5 walls_exp_per = 0.0 walls.each do |wall| vert3 = wall.vertices[0] # loop through the indices of the wall surface for index2 in 1..wall.vertices.size-1 if index2 < wall.vertices.size vert4 = wall.vertices[index2] else vert4 = wall.vertices[0] end vert1_2_3_on_same_line = three_vertices_same_line_and_dir?(vert1,vert2,vert3) if vert1_2_3_on_same_line vert1_2_4_on_same_line = three_vertices_same_line_and_dir?(vert1,vert2,vert4) if vert1_2_4_on_same_line wall_width = ((vert4.x-vert3.x)**2+(vert4.y-vert3.y)**2+(vert4.z-vert3.z)**2)**0.5 walls_exp_per += wall_width end end vert3 = vert4 end end # increment the exposed perimeter of the floor. Limit the length of the walls in contact with the # side of the floor to the length of the side of the floor. floor_exp_per += [walls_exp_per,side_length].min vert1 = vert2 end return floor_exp_per end |
#get_weather_file_from_repo(epw_file:) ⇒ Object
This method handles looking for the epw_file in the github.com/canmet-energy/btap_weather repository. It checks for the epw_file in the historical data first. If it is not there then it looks in the future weather data. If it is not there either, it throws an error. epw_file (String): The name of the epw file. The different weather files all share the same name as the epw file,
only the extension changes.
2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 2272 def get_weather_file_from_repo(epw_file:) # Get just the weather file name without the extension weather_loc = epw_file[0..-5] # Get the url of the file containing the historical weather data file names in the repository and the repository # folder containing the files historic_weather_files_loc = @standards_data['constants']['historic_weather_file_list']['value'].to_s historic_git_folder = @standards_data['constants']['historic_weather_folder_url']['value'].to_s # Get the files from the repository success_flag = download_and_save_file(weather_list_url: historic_weather_files_loc, weather_loc: weather_loc, git_folder: historic_git_folder) return if success_flag # If the file could not be found in the historical data look for it with the future weather data. puts "Could not find #{epw_file} in historical weather data files, looking in future weather data files." future_weather_files_loc = @standards_data['constants']['future_weather_file_list']['value'].to_s future_git_folder = @standards_data['constants']['future_weather_folder_url']['value'].to_s success_flag = download_and_save_file(weather_list_url: future_weather_files_loc, weather_loc: weather_loc, git_folder: future_git_folder) return if success_flag raise("Could not locate the following file in the canmet/btap_weather repository or could not extract the data: #{epw_file}. Please check the spelling of the file or visit https://github.com/canmet-energy/btap_weather to see if the file exists.") end |
#group_similar_zones_together(zones) ⇒ Object
This method is used to determine if there are single zones that can be grouped with zones of similar loads.
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 1224 def group_similar_zones_together(zones) total_zones_input = zones.size array_of_array_of_zones = [] accounted_for = [] # Go through other zones to see if there are similar zones with similar loads on the same floor that can be grouped. zones.each do |zone| similar_array_of_zones = [] next if accounted_for.include?(zone.name.to_s) similar_array_of_zones << zone accounted_for << zone.name.to_s zones.each do |zone_target| unless accounted_for.include?(zone_target.name.to_s) if are_zone_loads_similar?(zone_1: zone, zone_2: zone_target) similar_array_of_zones << zone_target accounted_for << zone_target.name.to_s end end end array_of_array_of_zones << similar_array_of_zones end total_zones_output = 0 array_of_array_of_zones.each do |curr_zones| total_zones_output += curr_zones.size end # puts total_zones_output # puts accounted_for.sort # sanity check. if total_zones_output != total_zones_input # puts JSON.pretty_generate(array_of_array_of_zones) # puts JSON.pretty_generate(accounted_for.sort) raise('') end return array_of_array_of_zones end |
#heat_exchanger_air_to_air_sensible_and_latent_apply_effectiveness(heat_exchanger_air_to_air_sensible_and_latent, erv_name = nil) ⇒ Object
Sets the minimum effectiveness of the heat exchanger per the standard.
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 376 def heat_exchanger_air_to_air_sensible_and_latent_apply_effectiveness(heat_exchanger_air_to_air_sensible_and_latent, erv_name = nil) # Assumed to be sensible and latent at all flow # This will now get data of the erv from the json file instead of hardcoding it. Defaults to NECB2011 erv we have been using. erv_name = 'NECB_Default' if erv_name.nil? erv_info = @standards_data['tables']['erv']['table'].detect { |item| item['erv_name'] == erv_name } raise("Could not find #{erv_name} in #{self.class.name} class' erv.json file or it's parents. The available ervs are #{@standards_data['tables']['erv']['table'].map { |item| item['erv_name'] }}") if erv_info.nil? heat_exchanger_air_to_air_sensible_and_latent.setHeatExchangerType(erv_info['HeatExchangerType']) heat_exchanger_air_to_air_sensible_and_latent.setSensibleEffectivenessat100HeatingAirFlow(erv_info['SensibleEffectivenessat100HeatingAirFlow']) heat_exchanger_air_to_air_sensible_and_latent.setLatentEffectivenessat100HeatingAirFlow(erv_info['LatentEffectivenessat100HeatingAirFlow']) heat_exchanger_air_to_air_sensible_and_latent.setSensibleEffectivenessat100CoolingAirFlow(erv_info['SensibleEffectivenessat100CoolingAirFlow']) heat_exchanger_air_to_air_sensible_and_latent.setLatentEffectivenessat100CoolingAirFlow(erv_info['LatentEffectivenessat100CoolingAirFlow']) if heat_exchanger_air_to_air_sensible_and_latent.model.version < OpenStudio::VersionString.new('3.8.0') heat_exchanger_air_to_air_sensible_and_latent.setSensibleEffectivenessat75HeatingAirFlow(erv_info['SensibleEffectivenessat75HeatingAirFlow']) heat_exchanger_air_to_air_sensible_and_latent.setLatentEffectivenessat75HeatingAirFlow(erv_info['LatentEffectivenessat75HeatingAirFlow']) heat_exchanger_air_to_air_sensible_and_latent.setSensibleEffectivenessat75CoolingAirFlow(erv_info['SensibleEffectivenessat75CoolingAirFlow']) heat_exchanger_air_to_air_sensible_and_latent.setLatentEffectivenessat75CoolingAirFlow(erv_info['LatentEffectivenessat75CoolingAirFlow']) else heat_exchanger_air_to_air_sensible_and_latent.setSensibleEffectivenessat75HeatingAirFlow(erv_info['SensibleEffectivenessat75HeatingAirFlow']) unless erv_info['SensibleEffectivenessat75HeatingAirFlow'].zero? heat_exchanger_air_to_air_sensible_and_latent.setLatentEffectivenessat75HeatingAirFlow(erv_info['LatentEffectivenessat75HeatingAirFlow']) unless erv_info['LatentEffectivenessat75HeatingAirFlow'].zero? heat_exchanger_air_to_air_sensible_and_latent.setSensibleEffectivenessat75CoolingAirFlow(erv_info['SensibleEffectivenessat75CoolingAirFlow']) unless erv_info['SensibleEffectivenessat75CoolingAirFlow'].zero? heat_exchanger_air_to_air_sensible_and_latent.setLatentEffectivenessat75CoolingAirFlow(erv_info['LatentEffectivenessat75CoolingAirFlow']) unless erv_info['LatentEffectivenessat75CoolingAirFlow'].zero? end heat_exchanger_air_to_air_sensible_and_latent.setSupplyAirOutletTemperatureControl(erv_info['SupplyAirOutletTemperatureControl']) heat_exchanger_air_to_air_sensible_and_latent.setFrostControlType(erv_info['FrostControlType']) heat_exchanger_air_to_air_sensible_and_latent.setEconomizerLockout(erv_info['EconomizerLockout']) heat_exchanger_air_to_air_sensible_and_latent.setThresholdTemperature(erv_info['ThresholdTemperature']) heat_exchanger_air_to_air_sensible_and_latent.setInitialDefrostTimeFraction(erv_info['InitialDefrostTimeFraction']) update_sys_name(heat_exchanger_air_to_air_sensible_and_latent.airLoopHVAC.get, sys_hr: 'erv') return true end |
#init_qaqc(model) ⇒ Object
generates full qaqc.json
155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 155 def init_qaqc(model) # load the qaqc.json files # This is currently disabled as most tests are now done using regression and unit tests.. but we may bring this back. # @qaqc_data = self.load_qaqc_database_new() # generate base qaqc hash qaqc = create_base_data(model) # performs the qaqc on the given base qaqc hash # necb_qaqc(qaqc, model) return qaqc end |
#is_a_necb_dwelling_unit?(space) ⇒ Boolean
Check if the space spactype is a dwelling unit as per NECB.
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 667 def is_a_necb_dwelling_unit?(space) space_type_table = @standards_data['space_types'] space_type_data = model_find_object(space_type_table, 'template' => self.class.name, 'space_type' => space.spaceType.get.standardsSpaceType.get, 'building_type' => space.spaceType.get.standardsBuildingType.get) necb_hvac_system_selection_table = @standards_data['necb_hvac_system_selection_type'] necb_hvac_system_select = necb_hvac_system_selection_table.detect do |curr_necb_hvac_system_select| curr_necb_hvac_system_select['necb_hvac_system_selection_type'] == space_type_data['necb_hvac_system_selection_type'] && curr_necb_hvac_system_select['min_stories'] <= space.model.getBuilding.standardsNumberOfAboveGroundStories.get && curr_necb_hvac_system_select['max_stories'] >= space.model.getBuilding.standardsNumberOfAboveGroundStories.get end return necb_hvac_system_select['dwelling'] == true end |
#is_an_necb_storage_space?(space) ⇒ Boolean
Check to see if this is a wet space that the NECB does not have a specified schedule or system for. Currently hardcoded to Locker room and washroom.
661 662 663 664 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 661 def is_an_necb_storage_space?(space) # Hack! Should replace this with a proper table lookup. return space.spaceType.get.standardsSpaceType.get.include?('Storage') end |
#is_an_necb_wet_space?(space) ⇒ Boolean
Check to see if this is a wet space that the NECB does not have a specified schedule or system for. Currently hardcoded to Locker room and washroom.
654 655 656 657 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 654 def is_an_necb_wet_space?(space) # Hack! Should replace this with a proper table lookup. return space.spaceType.get.standardsSpaceType.get.include?('Locker room') || space.spaceType.get.standardsSpaceType.get.include?('Washroom') end |
#is_an_necb_wildcard_space?(space) ⇒ Boolean
Check to see if this is a wildcard space that the NECB does not have a specified schedule or system for.
641 642 643 644 645 646 647 648 649 650 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 641 def is_an_necb_wildcard_space?(space) space_type_table = @standards_data['space_types'] space_type_data = model_find_object(space_type_table, 'template' => self.class.name, 'space_type' => space.spaceType.get.standardsSpaceType.get, 'building_type' => space.spaceType.get.standardsBuildingType.get) raise(space.to_s) if space_type_data.nil? return space_type_data['necb_hvac_system_selection_type'] == 'Wildcard' end |
#load_building_type_from_library(building_type:) ⇒ Object
340 341 342 343 344 345 346 347 348 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 340 def load_building_type_from_library(building_type:) osm_model_path = File.absolute_path(File.join(__FILE__, '..', '..', '..', "necb/NECB2011/data/geometry/#{building_type}.osm")) model = false if File.file?(osm_model_path) model = BTAP::FileIO.load_osm(osm_model_path) model.getBuilding.setName(building_type) end return model end |
#load_qaqc_database_new ⇒ Object
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 120 def load_qaqc_database_new # Combine the data from the JSON files into a single hash files = Dir.glob("#{File.dirname(__FILE__)}/qaqc_data/*.json").select { |e| File.file? e } @qaqc_data = {} @qaqc_data['tables'] = {} files.each do |file| # puts "loading qaqc data from #{file}" data = JSON.parse(File.read(file)) if !data['tables'].nil? @qaqc_data['tables'] = [*@qaqc_data['tables'], *data['tables']].to_h else @qaqc_data[data.keys.first] = data[data.keys.first] end end # Write test report file. test_result_file = File.join(File.dirname(__FILE__), '..', 'NECB2011_QAQC.json') File.open(test_result_file, 'w') { |f| f.write(JSON.pretty_generate(@qaqc_data)) } return @qaqc_data end |
#load_standards_database_new ⇒ Object
Combine the data from the JSON files into a single hash Load JSON files differently depending on whether loading from the OpenStudio CLI embedded filesystem or from typical gem installation
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 65 def load_standards_database_new @standards_data = {} @standards_data['tables'] = {} if __dir__[0] == ':' # Running from OpenStudio CLI ('../common', /.*\.json/).each do |file| data = JSON.parse(EmbeddedScripting.getFileAsString(file)) if !data['tables'].nil? @standards_data['tables'] = [*@standards_data['tables'], *data['tables']].to_h else @standards_data[data.keys.first] = data[data.keys.first] end end else path = "#{File.dirname(__FILE__)}/../common/" raise 'Could not find common folder' unless Dir.exist?(path) files = Dir.glob("#{path}/*.json").select { |e| File.file? e } files.each do |file| data = JSON.parse(File.read(file)) if !data['tables'].nil? @standards_data['tables'] = [*@standards_data['tables'], *data['tables']].to_h else @standards_data[data.keys.first] = data[data.keys.first] end end end if __dir__[0] == ':' # Running from OpenStudio CLI ('data/', /.*\.json/).each do |file| data = JSON.parse(EmbeddedScripting.getFileAsString(file)) if !data['tables'].nil? @standards_data['tables'] = [*@standards_data['tables'], *data['tables']].to_h else @standards_data[data.keys.first] = data[data.keys.first] end end else files = Dir.glob("#{File.dirname(__FILE__)}/data/*.json").select { |e| File.file? e } files.each do |file| data = JSON.parse(File.read(file)) if !data['tables'].nil? @standards_data['tables'] = [*@standards_data['tables'], *data['tables']].to_h else @standards_data[data.keys.first] = data[data.keys.first] end end end # Write database to file. # File.open(File.join(File.dirname(__FILE__), '..', 'NECB2011.json'), 'w') {|f| f.write(JSON.pretty_generate(@standards_data))} return @standards_data end |
#look_up_csv_data(csv_fname, search_criteria) ⇒ Object
1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1915 def look_up_csv_data(csv_fname, search_criteria) = { headers: :first_row, converters: [:numeric] } unless File.exist?(csv_fname) raise "File: [#{csv_fname}] Does not exist" end # we'll save the matches here matches = nil # save a copy of the headers headers = nil CSV.open(csv_fname, 'r', **) do |csv| # Since CSV includes Enumerable we can use 'find_all' # which will return all the elements of the Enumerble for # which the block returns true matches = csv.find_all do |row| match = true search_criteria.keys.each do |key| match &&= (row[key].strip == search_criteria[key].strip) end match end headers = csv.headers end # puts matches raise('More than one match') if matches.size > 1 puts "Zero matches found for [#{search_criteria}]" if matches.empty? # return matches[0] return matches[0] end |
#max_fwdr(hdd) ⇒ Double
Returns a constant float.
286 287 288 289 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 286 def max_fwdr(hdd) # get formula from json database. return eval(get_standards_formula('fdwr_formula')) end |
#merge_recursively(a, b) ⇒ Object
102 103 104 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 102 def merge_recursively(a, b) a.merge(b) { |key, a_item, b_item| merge_recursively(a_item, b_item) } end |
#model_add_construction_set_from_osm(model:, construction_set_name: 'BTAP-Mass', osm_path: File.absolute_path(File.join(__FILE__, '..', '..', 'common/construction_defaults.osm'))) ⇒ Object
524 525 526 527 528 529 530 531 532 533 534 535 536 537 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 524 def model_add_construction_set_from_osm(model:, construction_set_name: 'BTAP-Mass', osm_path: File.absolute_path(File.join(__FILE__, '..', '..', 'common/construction_defaults.osm'))) # load resources model construction_library = BTAP::FileIO.load_osm(osm_path) if !construction_library.getDefaultConstructionSetByName(construction_set_name.to_s).is_initialized runner.registerError('Did not find the expected construction in library.') return false end selected_construction_set = construction_library.getDefaultConstructionSetByName(construction_set_name.to_s).get new_construction_set = selected_construction_set.clone(model).to_DefaultConstructionSet.get return new_construction_set end |
#model_add_constructions(model) ⇒ Boolean
Adds code-minimum constructions based on the building type as defined in the OpenStudio_Standards_construction_sets.json file. Where there is a separate construction set specified for the individual space type, this construction set will be created and applied to this space type, overriding the whole-building construction set.
471 472 473 474 475 476 477 478 479 480 481 482 483 484 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 471 def model_add_constructions(model) OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Started applying constructions') # Assign construction to adiabatic construction # Assign a material to all internal mass objects assign_contruction_to_adiabatic_surfaces(model) # The constructions lookup table uses a slightly different list of # building types. apply_building_default_constructionset(model) # Make a construction set for each space type, if one is specified # apply_default_constructionsets_to_spacetypes(climate_zone, model) OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Finished applying constructions') return true end |
#model_add_daylighting_controls(model:, daylighting_type:) ⇒ Object
1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1372 def model_add_daylighting_controls(model:, daylighting_type:) return if daylighting_type == 'none' ##### Find spaces with exterior fenestration including fixed window, operable window, and skylight. daylight_spaces = [] daylight_spaces_target_illuminance_setpoint_hash = {} model.getSpaces.sort.each do |space| space.surfaces.sort.each do |surface| surface.subSurfaces.sort.each do |subsurface| if subsurface.outsideBoundaryCondition == 'Outdoors' && (subsurface.subSurfaceType == 'FixedWindow' || subsurface.subSurfaceType == 'OperableWindow' || subsurface.subSurfaceType == 'Skylight') daylight_spaces << space space_type = space.spaceType.get space_type_name = space.spaceType.get.name.to_s space_type_name = space_type_name.gsub('Space Function', '') # puts "space_type_name is #{space_type_name}" # Gather minimum illuminance level as per NECB lux_spacetype_data = @standards_data['tables']['space_types']['table'] standards_building_type = space_type.standardsBuildingType.is_initialized ? space_type.standardsBuildingType.get : nil standards_space_type = space_type.standardsSpaceType.is_initialized ? space_type.standardsSpaceType.get : nil lux_space_type_properties = lux_spacetype_data.detect { |s| (s['building_type'] == standards_building_type) && (s['space_type'] == standards_space_type) } if lux_space_type_properties.nil? raise("#{standards_building_type} for #{standards_space_type} was not found please verify the target_illuminance_setpoint database names match the space type names.") end target_illuminance_setpoint = lux_space_type_properties['target_illuminance_setpoint'].to_f daylight_spaces_target_illuminance_setpoint_hash[space.name.to_s] = target_illuminance_setpoint # subsurface.outsideBoundaryCondition == "Outdoors" && (subsurface.subSurfaceType == "FixedWindow" || "OperableWindow") end # surface.subSurfaces.each do |subsurface| end # space.surfaces.each do |surface| end # model.getSpaces.sort.each do |space| end ##### Remove duplicate spaces from the "daylight_spaces" array, as a daylighted space may have various fenestration types. daylight_spaces = daylight_spaces.uniq # puts "daylight_spaces are #{daylight_spaces}" if daylighting_type.nil? || daylighting_type == false || daylighting_type == 'none' || daylighting_type == 'NECB_Default' # puts daylighting sensors in the spaces as per NECB requirements; so some spaces may not have sensors ##### Create hashes for "Primary Sidelighted Areas", "Sidelighting Effective Aperture", "Daylighted Area Under Skylights", ##### and "Skylight Effective Aperture" for the whole model. ##### Each of these hashes will be used later in this function (i.e. model_add_daylighting_controls) ##### to provide a dictionary of daylighted space names and the associated value (i.e. daylighted area or effective aperture). primary_sidelighted_area_hash = {} sidelighting_effective_aperture_hash = {} daylighted_area_under_skylights_hash = {} skylight_effective_aperture_hash = {} ##### Calculate "Primary Sidelighted Areas" AND "Sidelighting Effective Aperture" as per NECB2011. # @todo consider removing overlapped sidelighted area daylight_spaces.sort.each do |daylight_space| primary_sidelighted_area = 0.0 area_weighted_vt_handle = 0.0 area_weighted_vt = 0.0 window_area_sum = 0.0 ##### Calculate floor area of the daylight_space and get floor vertices of the daylight_space (to be used for the calculation of daylight_space depth) floor_surface = nil floor_area = 0.0 floor_vertices = [] daylight_space.surfaces.sort.each do |surface| if surface.surfaceType == 'Floor' floor_surface = surface floor_area += surface.netArea floor_vertices << surface.vertices end end ##### Loop through the surfaces of each daylight_space to calculate primary_sidelighted_area and ##### area-weighted visible transmittance and window_area_sum which are used to calculate sidelighting_effective_aperture primary_sidelighted_area, area_weighted_vt_handle, window_area_sum = get_parameters_sidelighting(daylight_space: daylight_space, floor_surface: floor_surface, floor_vertices: floor_vertices, floor_area: floor_area, primary_sidelighted_area: primary_sidelighted_area, area_weighted_vt_handle: area_weighted_vt_handle, window_area_sum: window_area_sum) primary_sidelighted_area_hash[daylight_space.name.to_s] = primary_sidelighted_area ##### Calculate area-weighted VT of glazing (this is used to calculate sidelighting effective aperture; see NECB2011: 4.2.2.10.). area_weighted_vt = area_weighted_vt_handle / window_area_sum sidelighting_effective_aperture_hash[daylight_space.name.to_s] = window_area_sum * area_weighted_vt / primary_sidelighted_area # daylight_spaces.each do |daylight_space| end ##### Calculate "Daylighted Area Under Skylights" AND "Skylight Effective Aperture" daylight_spaces.sort.each do |daylight_space| # puts daylight_space.name.to_s skylight_area = 0.0 skylight_area_weighted_vt_handle = 0.0 skylight_area_weighted_vt = 0.0 skylight_area_sum = 0.0 daylighted_under_skylight_area = 0.0 ##### Loop through the surfaces of each daylight_space to calculate daylighted_area_under_skylights and skylight_effective_aperture for each daylight_space daylighted_under_skylight_area, skylight_area_weighted_vt_handle, skylight_area_sum = get_parameters_skylight(daylight_space: daylight_space, skylight_area_weighted_vt_handle: skylight_area_weighted_vt_handle, skylight_area_sum: skylight_area_sum, daylighted_under_skylight_area: daylighted_under_skylight_area) daylighted_area_under_skylights_hash[daylight_space.name.to_s] = daylighted_under_skylight_area ##### Calculate skylight_effective_aperture as per NECB2011: 4.2.2.7. ##### Note that it was assumed that the skylight is flush with the ceiling. Therefore, area-weighted average well factor (WF) was set to 0.9 in the below Equation. skylight_area_weighted_vt = skylight_area_weighted_vt_handle / skylight_area_sum skylight_effective_aperture_hash[daylight_space.name.to_s] = 0.85 * skylight_area_sum * skylight_area_weighted_vt * 0.9 / daylighted_under_skylight_area # daylight_spaces.each do |daylight_space| end # puts "primary_sidelighted_area_hash is #{primary_sidelighted_area_hash}" # puts sidelighting_effective_aperture_hash # puts daylighted_area_under_skylights_hash # puts skylight_effective_aperture_hash ##### Find office spaces >= 25m2 among daylight_spaces offices_larger_25m2 = [] daylight_spaces.sort.each do |daylight_space| ## The following steps are for in case an office has multiple floors at various heights ## 1. Calculate number of floors of each daylight_space ## 2. Find the lowest z among all floors of each daylight_space ## 3. Find lowest floors of each daylight_space (these floors are at the same level) ## 4. Calculate 'daylight_space_area' as sum of area of all the lowest floors of each daylight_space, and gather the vertices of all the lowest floors of each daylight_space ## 1. Calculate number of floors of daylight_space floor_vertices = [] number_floor = 0 daylight_space.surfaces.sort.each do |surface| if surface.surfaceType == 'Floor' floor_vertices << surface.vertices number_floor += 1 end end ## 2. Loop through all floors of daylight_space, and find the lowest z among all floors of daylight_space lowest_floor_z = [] highest_floor_z = [] for i in 0..number_floor - 1 if i == 0 lowest_floor_z = floor_vertices[i][0].z highest_floor_z = floor_vertices[i][0].z else if lowest_floor_z > floor_vertices[i][0].z lowest_floor_z = floor_vertices[i][0].z else lowest_floor_z = lowest_floor_z end if highest_floor_z < floor_vertices[i][0].z highest_floor_z = floor_vertices[i][0].z else highest_floor_z = highest_floor_z end end end ## 3 and 4. Loop through all floors of daylight_space, and calculate the sum of area of all the lowest floors of daylight_space, ## and gather the vertices of all the lowest floors of daylight_space daylight_space_area = 0 lowest_floors_vertices = [] floor_vertices = [] daylight_space.surfaces.sort.each do |surface| if surface.surfaceType == 'Floor' floor_vertices = surface.vertices if floor_vertices[0].z == lowest_floor_z lowest_floors_vertices << floor_vertices daylight_space_area += surface.netArea end end end if daylight_space.spaceType.get.standardsSpaceType.get.to_s == 'Office - enclosed' && daylight_space_area >= 25.0 offices_larger_25m2 << daylight_space.name.to_s end end ##### find daylight_spaces which do not need daylight sensor controls based on the primary_sidelighted_area as per NECB2011: 4.2.2.8. ##### Note: Office spaces >= 25m2 are excluded (i.e. they should have daylighting controls even if their primary_sidelighted_area <= 100m2), as per NECB2011: 4.2.2.2. daylight_spaces_exception = [] primary_sidelighted_area_hash.sort.each do |key_daylight_space_name, value_primary_sidelighted_area| if value_primary_sidelighted_area <= 100.0 && [key_daylight_space_name].any? { |word| offices_larger_25m2.include?(word) } == false daylight_spaces_exception << key_daylight_space_name end end ##### find daylight_spaces which do not need daylight sensor controls based on the sidelighting_effective_aperture as per NECB2011: 4.2.2.8. ##### Note: Office spaces >= 25m2 are excluded (i.e. they should have daylighting controls even if their sidelighting_effective_aperture <= 10%), as per NECB2011: 4.2.2.2. sidelighting_effective_aperture_hash.sort.each do |key_daylight_space_name, value_sidelighting_effective_aperture| if value_sidelighting_effective_aperture <= 0.1 && [key_daylight_space_name].any? { |word| offices_larger_25m2.include?(word) } == false daylight_spaces_exception << key_daylight_space_name end end ##### find daylight_spaces which do not need daylight sensor controls based on the daylighted_area_under_skylights as per NECB2011: 4.2.2.4. ##### Note: Office spaces >= 25m2 are excluded (i.e. they should have daylighting controls even if their daylighted_area_under_skylights <= 400m2), as per NECB2011: 4.2.2.2. daylighted_area_under_skylights_hash.sort.each do |key_daylight_space_name, value_daylighted_area_under_skylights| if value_daylighted_area_under_skylights <= 400.0 && [key_daylight_space_name].any? { |word| offices_larger_25m2.include?(word) } == false daylight_spaces_exception << key_daylight_space_name end end ##### find daylight_spaces which do not need daylight sensor controls based on the skylight_effective_aperture criterion as per NECB2011: 4.2.2.4. ##### Note: Office spaces >= 25m2 are excluded (i.e. they should have daylighting controls even if their skylight_effective_aperture <= 0.6%), as per NECB2011: 4.2.2.2. skylight_effective_aperture_hash.sort.each do |key_daylight_space_name, value_skylight_effective_aperture| if value_skylight_effective_aperture <= 0.006 && [key_daylight_space_name].any? { |word| offices_larger_25m2.include?(word) } == false daylight_spaces_exception << key_daylight_space_name end end # puts daylight_spaces_exception ##### Loop through the daylight_spaces and exclude the daylight_spaces that do not meet the criteria (see above) as per NECB2011: 4.2.2.4. and 4.2.2.8. daylight_spaces_exception.sort.each do |daylight_space_exception| daylight_spaces.sort.each do |daylight_space| if daylight_space.name.to_s == daylight_space_exception daylight_spaces.delete(daylight_space) end end end # puts daylight_spaces # elsif daylighting_type == 'add_daylighting_controls' # puts daylighting sensors in all spaces regardless of NECB requirements end #if daylighting_type.nil? || daylighting_type == false || daylighting_type == 'none' || daylighting_type == 'NECB_Default' ##### Create one daylighting sensor and put it at the center of each daylight_space if the space area < 250m2; ##### otherwise, create two daylight sensors, divide the space into two parts and put each of the daylight sensors at the center of each part of the space. daylight_spaces.sort.each do |daylight_space| # puts daylight_space.name.to_s ##### 1. Calculate number of floors of each daylight_space ##### 2. Find the lowest z among all floors of each daylight_space ##### 3. Find lowest floors of each daylight_space (these floors are at the same level) ##### 4. Calculate 'daylight_space_area' as sum of area of all the lowest floors of each daylight_space, and gather the vertices of all the lowest floors of each daylight_space ##### 5. Find min and max of x and y among vertices of all the lowest floors of each daylight_space ##### Calculate number of floors of daylight_space floor_vertices = [] number_floor = 0 daylight_space.surfaces.sort.each do |surface| if surface.surfaceType == 'Floor' floor_vertices << surface.vertices number_floor += 1 end end ##### Loop through all floors of daylight_space, and find the lowest z among all floors of daylight_space lowest_floor_z = [] highest_floor_z = [] for i in 0..number_floor - 1 if i == 0 lowest_floor_z = floor_vertices[i][0].z highest_floor_z = floor_vertices[i][0].z else if lowest_floor_z > floor_vertices[i][0].z lowest_floor_z = floor_vertices[i][0].z else lowest_floor_z = lowest_floor_z end if highest_floor_z < floor_vertices[i][0].z highest_floor_z = floor_vertices[i][0].z else highest_floor_z = highest_floor_z end end end # puts lowest_floor_z ##### Loop through all floors of daylight_space, and calculate the sum of area of all the lowest floors of daylight_space, ##### and gather the vertices of all the lowest floors of daylight_space daylight_space_area = 0 lowest_floors_vertices = [] floor_vertices = [] daylight_space.surfaces.sort.each do |surface| if surface.surfaceType == 'Floor' floor_vertices = surface.vertices if floor_vertices[0].z == lowest_floor_z lowest_floors_vertices << floor_vertices daylight_space_area += surface.netArea end end end # puts daylight_space.name.to_s # puts number_floor # puts lowest_floors_vertices # puts daylight_space_area ##### Loop through all lowest floors of daylight_space and find the min and max of x and y among their vertices xmin = lowest_floors_vertices[0][0].x ymin = lowest_floors_vertices[0][0].y xmax = lowest_floors_vertices[0][0].x ymax = lowest_floors_vertices[0][0].y zmin = lowest_floor_z for i in 0..lowest_floors_vertices.count - 1 # this loops through each of the lowers floors of daylight_space for j in 0..lowest_floors_vertices[i].count - 1 # this loops through each of vertices of each of the lowers floors of daylight_space if xmin > lowest_floors_vertices[i][j].x xmin = lowest_floors_vertices[i][j].x end if ymin > lowest_floors_vertices[i][j].y ymin = lowest_floors_vertices[i][j].y end if xmax < lowest_floors_vertices[i][j].x xmax = lowest_floors_vertices[i][j].x end if ymax < lowest_floors_vertices[i][j].y ymax = lowest_floors_vertices[i][j].y end end end # puts daylight_space.name.to_s # puts xmin # puts xmax # puts ymin # puts ymax ##### Get the thermal zone of daylight_space (this is used later to assign daylighting sensor) zone = daylight_space.thermalZone # puts "zone name is #{zone}" if !zone.empty? zone = daylight_space.thermalZone.get ##### Get the floor of the daylight_space floors = [] daylight_space.surfaces.sort.each do |surface| if surface.surfaceType == 'Floor' floors << surface end end ##### Set daylighting controls illuminance setpoint and number of stepped control steps number_of_stepped_control_steps = 2 ##### Note that the minimum number of stepped control steps is two steps as per NECB2011. illuminance_setpoint = daylight_spaces_target_illuminance_setpoint_hash.select {|key| key == daylight_space.name.to_s } illuminance_setpoint = illuminance_setpoint[daylight_space.name.to_s] ##### Create daylighting sensor control ##### NOTE: NECB2011 has some requirements on the number of sensors in spaces based on the area of the spaces. ##### However, EnergyPlus/OpenStudio allows to put maximum two built-in sensors in each thermal zone rather than in each space. ##### Since a thermal zone may include several spaces which are not next to each other on the same floor, or ##### a thermal zone may include spaces on different floors, a simplified method has been used to create a daylighting sensor. ##### So, in each thermal zone, only one daylighting sensor has been created even if the area of that thermal zone requires more than one daylighting sensor. ##### Also, it has been assumed that a thermal zone includes spaces which are next to each other and are on the same floor. ##### Furthermore, the one daylighting sensor in each thermal zone (where the thermal zone needs daylighting sensor), ##### the sensor has been put at the intersection of the minimum and maximum x and y of the lowest floor of that thermal zones. sensor = OpenStudio::Model::DaylightingControl.new(daylight_space.model) sensor.setName("#{daylight_space.name} daylighting control") sensor.setSpace(daylight_space) sensor.setIlluminanceSetpoint(illuminance_setpoint) sensor.setLightingControlType('Stepped') sensor.setNumberofSteppedControlSteps(number_of_stepped_control_steps) x_pos = (xmin + xmax) / 2.0 y_pos = (ymin + ymax) / 2.0 z_pos = zmin + 0.8 # put it 0.8 meter above the floor sensor_vertex = OpenStudio::Point3d.new(x_pos, y_pos, z_pos) sensor.setPosition(sensor_vertex) zone.setPrimaryDaylightingControl(sensor) zone.setFractionofZoneControlledbyPrimaryDaylightingControl(1.0) # if !zone.empty? end # daylight_spaces.each do |daylight_space| end # END if daylighting_controls_type.nil? || daylighting_controls_type == false || daylighting_controls_type == 'none' || daylighting_controls_type == 'NECB_Default' end |
#model_add_hvac(model:) ⇒ Object
2 3 4 5 6 7 8 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 2 def model_add_hvac(model:) OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Started Adding HVAC') system_fuel_defaults = get_canadian_system_defaults_by_weatherfile_name(model) necb_autozone_and_autosystem(model: model, runner: nil, use_ideal_air_loads: false, system_fuel_defaults: system_fuel_defaults) OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Finished adding HVAC') return true end |
#model_add_loads(model, lights_type, lights_scale) ⇒ Boolean
Adds the loads and associated schedules for each space type as defined in the OpenStudio_Standards_space_types.json file. This includes lights, plug loads, occupants, ventilation rate requirements, infiltration, gas equipment (for kitchens, etc.) and typical schedules for each. Some loads are governed by the standard, others are typical values pulled from sources such as the DOE Reference and DOE Prototype Buildings.
1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1930 def model_add_loads(model, lights_type, lights_scale) OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Started applying space types (loads)') # Loop through all the space types currently in the model, # which are placeholders, and give them appropriate loads and schedules model.getSpaceTypes.sort.each do |space_type| # Rendering color space_type_apply_rendering_color(space_type) # Loads space_type_apply_internal_loads(space_type: space_type, lights_type: lights_type, lights_scale: lights_scale) # Schedules space_type_apply_internal_load_schedules(space_type, true, true, true, true, true, true, true) end OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Finished applying space types (loads)') return true end |
#model_add_schedule(model, schedule_name) ⇒ ScheduleRuleset
make return an OptionalScheduleRuleset
Create a schedule from the openstudio standards dataset and add it to the model.
125 126 127 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 125 def model_add_schedule(model, schedule_name) super(model, schedule_name) end |
#model_add_swh(model:, swh_fueltype: 'DefaultFuel', shw_scale:) ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb', line 2 def model_add_swh(model:, swh_fueltype: 'DefaultFuel', shw_scale:) OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Started Adding Service Water Heating') # Get default fuel based on epw location province. if swh_fueltype == 'DefaultFuel' epw = OpenStudio::EpwFile.new(model.weatherFile.get.path.get) swh_fueltype = @standards_data['regional_fuel_use'].detect { |fuel_sources| fuel_sources['state_province_regions'].include?(epw.stateProvinceRegion) }['fueltype_set'] end # Calculate the tank size and service water pump information shw_sizing = auto_size_shw_capacity(model: model, shw_scale: shw_scale) if shw_sizing['loop_peak_flow_rate_SI'] == 0 # Only add a shw_loop if at least one space calls for shw. If no space calls for shw put out a warning but do not # add a shw loop. OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'No Service Water Heating Added') return true else shw_pump_head = auto_size_shw_pump_head(model, default: false) end # Add the main service water heating loop shw_pump_motor_eff = 0.9 main_swh_loop = OpenstudioStandards::ServiceWaterHeating.create_service_water_heating_loop(model, system_name: 'Main Service Water Loop', service_water_temperature: shw_sizing['max_temp_SI'], service_water_pump_head: shw_pump_head, service_water_pump_motor_efficiency: shw_pump_motor_eff, water_heater_capacity: shw_sizing['tank_capacity_SI'], water_heater_volume: shw_sizing['tank_volume_SI'], water_heater_fuel: swh_fueltype, on_cycle_parasitic_fuel_consumption_rate: shw_sizing['parasitic_loss'], off_cycle_parasitic_fuel_consumption_rate: shw_sizing['parasitic_loss']) # Note that when water use equipment is assigned to spaces then the water used by the equipment is multiplied by # the space (ultimately thermal zone) multiplier. Note that there is a separate water use equipment multiplier # as well which is different than the space (ultimately thermal zone) multiplier. shw_sizing['spaces_w_dhw'].each do |space| OpenstudioStandards::ServiceWaterHeating.create_water_use(model, name: "#{space['shw_spaces'].name.get.capitalize}", flow_rate: space['shw_peakflow_ind_SI'], flow_rate_fraction_schedule: model_add_schedule(model, space['shw_sched']), water_use_temperature: space['shw_temp_SI'], service_water_loop: main_swh_loop, space: space['shw_spaces']) end OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Finished adding Service Water Heating') return true end |
#model_apply_sizing_parameters(model) ⇒ Object
1523 1524 1525 1526 1527 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1523 def model_apply_sizing_parameters(model) model.getSizingParameters.setHeatingSizingFactor(get_standards_constant('sizing_factor_max_heating')) model.getSizingParameters.setCoolingSizingFactor(get_standards_constant('sizing_factor_max_cooling')) OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.Model', "Set sizing factors to #{get_standards_constant('sizing_factor_max_heating')} for heating and #{get_standards_constant('sizing_factor_max_heating')} for cooling.") end |
#model_apply_standard(model:, tbd_option: nil, tbd_interpolate: nil, epw_file:, custom_weather_folder: nil, sizing_run_dir: Dir.pwd, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', primary_heating_fuel: 'DefaultFuel', swh_fuel: 'DefaultFuel', dcv_type: 'NECB_Default', lights_type: 'NECB_Default', lights_scale: 'NECB_Default', daylighting_type: 'NECB_Default', ecm_system_name: 'NECB_Default', ecm_system_zones_map_option: 'NECB_Default', erv_package: 'NECB_Default', boiler_eff: nil, furnace_eff: nil, unitary_cop: nil, shw_eff: nil, ext_wall_cond: nil, ext_floor_cond: nil, ext_roof_cond: nil, ground_wall_cond: nil, ground_floor_cond: nil, ground_roof_cond: nil, door_construction_cond: nil, fixed_window_cond: nil, glass_door_cond: nil, overhead_door_cond: nil, skylight_cond: nil, glass_door_solar_trans: nil, fixed_wind_solar_trans: nil, skylight_solar_trans: nil, fdwr_set: nil, srr_set: nil, rotation_degrees: nil, scale_x: nil, scale_y: nil, scale_z: nil, nv_type: nil, nv_opening_fraction: nil, nv_temp_out_min: nil, nv_delta_temp_in_out: nil, pv_ground_type: nil, pv_ground_total_area_pv_panels_m2: nil, pv_ground_tilt_angle: nil, pv_ground_azimuth_angle: nil, pv_ground_module_description: nil, chiller_type: nil, occupancy_loads_scale: nil, electrical_loads_scale: nil, oa_scale: nil, infiltration_scale: nil, output_variables: nil, shw_scale: nil, output_meters: nil, airloop_economizer_type: nil, baseline_system_zones_map_option: nil, necb_hdd: true, boiler_fuel: nil, boiler_cap_ratio: nil) ⇒ Object
Created this method so that additional methods can be addded for bulding the prototype model in later code versions without modifying the build_protoype_model method or copying it wholesale for a few changes.
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 352 def model_apply_standard(model:, tbd_option: nil, tbd_interpolate: nil, epw_file:, custom_weather_folder: nil, sizing_run_dir: Dir.pwd, necb_reference_hp: false, necb_reference_hp_supp_fuel: 'DefaultFuel', primary_heating_fuel: 'DefaultFuel', swh_fuel: 'DefaultFuel', dcv_type: 'NECB_Default', lights_type: 'NECB_Default', lights_scale: 'NECB_Default', daylighting_type: 'NECB_Default', ecm_system_name: 'NECB_Default', ecm_system_zones_map_option: 'NECB_Default', erv_package: 'NECB_Default', boiler_eff: nil, furnace_eff: nil, unitary_cop: nil, shw_eff: nil, ext_wall_cond: nil, ext_floor_cond: nil, ext_roof_cond: nil, ground_wall_cond: nil, ground_floor_cond: nil, ground_roof_cond: nil, door_construction_cond: nil, fixed_window_cond: nil, glass_door_cond: nil, overhead_door_cond: nil, skylight_cond: nil, glass_door_solar_trans: nil, fixed_wind_solar_trans: nil, skylight_solar_trans: nil, fdwr_set: nil, srr_set: nil, rotation_degrees: nil, scale_x: nil, scale_y: nil, scale_z: nil, nv_type: nil, nv_opening_fraction: nil, nv_temp_out_min: nil, nv_delta_temp_in_out: nil, pv_ground_type: nil, pv_ground_total_area_pv_panels_m2: nil, pv_ground_tilt_angle: nil, pv_ground_azimuth_angle: nil, pv_ground_module_description: nil, chiller_type: nil, occupancy_loads_scale: nil, electrical_loads_scale: nil, oa_scale: nil, infiltration_scale: nil, output_variables: nil, shw_scale: nil, output_meters: nil, airloop_economizer_type: nil, baseline_system_zones_map_option: nil, necb_hdd: true, boiler_fuel: nil, boiler_cap_ratio: nil) primary_heating_fuel = validate_primary_heating_fuel(primary_heating_fuel: primary_heating_fuel) self.fuel_type_set = SystemFuels.new() swh_fuel = swh_fuel.nil? ? 'NECB_Default' : swh_fuel.to_s self.fuel_type_set.set_defaults(standards_data: @standards_data, primary_heating_fuel: primary_heating_fuel, swh_fuel: swh_fuel) swh_fuel = self.fuel_type_set.swh_fueltype if swh_fuel.to_s.downcase == 'necb_default' clean_and_scale_model(model: model, rotation_degrees: rotation_degrees, scale_x: scale_x, scale_y: scale_y, scale_z: scale_z) fdwr_set = convert_arg_to_f(variable: fdwr_set, default: -1) srr_set = convert_arg_to_f(variable: srr_set, default: -1) necb_hdd = convert_arg_to_bool(variable: necb_hdd, default: true) boiler_fuel = convert_arg_to_string(variable: boiler_fuel, default: nil) boiler_cap_ratio = convert_arg_to_string(variable: boiler_cap_ratio, default: nil) boiler_cap_ratios = set_boiler_cap_ratios(boiler_cap_ratio: boiler_cap_ratio, boiler_fuel: boiler_fuel) unless boiler_cap_ratio.nil? && boiler_fuel.nil? self.fuel_type_set.set_boiler_fuel(standards_data: @standards_data, boiler_fuel: boiler_fuel, boiler_cap_ratios: boiler_cap_ratios) unless boiler_fuel.nil? # Ensure the volume calculation in all spaces is done automatically model.getSpaces.sort.each do |space| space.autocalculateVolume end apply_weather_data(model: model, epw_file: epw_file, custom_weather_folder: custom_weather_folder) apply_loads(model: model, lights_type: lights_type, lights_scale: lights_scale, occupancy_loads_scale: occupancy_loads_scale, electrical_loads_scale: electrical_loads_scale, oa_scale: oa_scale) apply_envelope(model: model, ext_wall_cond: ext_wall_cond, ext_floor_cond: ext_floor_cond, ext_roof_cond: ext_roof_cond, ground_wall_cond: ground_wall_cond, ground_floor_cond: ground_floor_cond, ground_roof_cond: ground_roof_cond, door_construction_cond: door_construction_cond, fixed_window_cond: fixed_window_cond, glass_door_cond: glass_door_cond, overhead_door_cond: overhead_door_cond, skylight_cond: skylight_cond, glass_door_solar_trans: glass_door_solar_trans, fixed_wind_solar_trans: fixed_wind_solar_trans, skylight_solar_trans: skylight_solar_trans, infiltration_scale: infiltration_scale, necb_hdd: necb_hdd) apply_fdwr_srr_daylighting(model: model, fdwr_set: fdwr_set, srr_set: srr_set, necb_hdd: necb_hdd) apply_thermal_bridging(model: model, tbd_option: tbd_option, tbd_interpolate: tbd_interpolate, wall_U: ext_wall_cond, floor_U: ext_floor_cond, roof_U: ext_roof_cond) apply_auto_zoning(model: model, sizing_run_dir: sizing_run_dir, lights_type: lights_type, lights_scale: lights_scale) apply_kiva_foundation(model) apply_systems_and_efficiencies(model: model, sizing_run_dir: sizing_run_dir, primary_heating_fuel: primary_heating_fuel, swh_fuel: swh_fuel, dcv_type: dcv_type, ecm_system_name: ecm_system_name, ecm_system_zones_map_option: ecm_system_zones_map_option, erv_package: erv_package, boiler_eff: boiler_eff, unitary_cop: unitary_cop, furnace_eff: furnace_eff, shw_eff: shw_eff, daylighting_type: daylighting_type, nv_type: nv_type, nv_opening_fraction: nv_opening_fraction, nv_temp_out_min: nv_temp_out_min, nv_delta_temp_in_out: nv_delta_temp_in_out, pv_ground_type: pv_ground_type, pv_ground_total_area_pv_panels_m2: pv_ground_total_area_pv_panels_m2, pv_ground_tilt_angle: pv_ground_tilt_angle, pv_ground_azimuth_angle: pv_ground_azimuth_angle, pv_ground_module_description: pv_ground_module_description, chiller_type: chiller_type, shw_scale: shw_scale, airloop_economizer_type: airloop_economizer_type, baseline_system_zones_map_option: baseline_system_zones_map_option) self.set_output_variables(model: model, output_variables: output_variables) self.set_output_meters(model: model, output_meters: output_meters) return model end |
#model_create_prototype_model(template:, building_type:, epw_file:, custom_weather_folder: nil, debug: false, sizing_run_dir: Dir.pwd, primary_heating_fuel: 'Electricity', swh_fuel: 'NECB_Default', dcv_type: 'NECB_Default', lights_type: 'NECB_Default', lights_scale: 1.0, daylighting_type: 'NECB_Default', ecm_system_name: 'NECB_Default', ecm_system_zones_map_option: 'NECB_Default', erv_package: 'NECB_Default', boiler_eff: nil, unitary_cop: nil, furnace_eff: nil, shw_eff: nil, ext_wall_cond: nil, ext_floor_cond: nil, ext_roof_cond: nil, ground_wall_cond: nil, ground_floor_cond: nil, ground_roof_cond: nil, door_construction_cond: nil, fixed_window_cond: nil, glass_door_cond: nil, overhead_door_cond: nil, skylight_cond: nil, glass_door_solar_trans: nil, fixed_wind_solar_trans: nil, skylight_solar_trans: nil, rotation_degrees: nil, fdwr_set: -1.0,, srr_set: -1.0,, nv_type: nil, nv_opening_fraction: nil, nv_temp_out_min: nil, nv_delta_temp_in_out: nil, scale_x: nil, scale_y: nil, scale_z: nil, pv_ground_type: nil, pv_ground_total_area_pv_panels_m2: nil, pv_ground_tilt_angle: nil, pv_ground_azimuth_angle: nil, pv_ground_module_description: nil, chiller_type: nil, occupancy_loads_scale: nil, electrical_loads_scale: nil, oa_scale: nil, infiltration_scale: nil, output_variables: nil, shw_scale: nil, output_meters: nil, airloop_economizer_type: nil, baseline_system_zones_map_option: nil, tbd_option: nil, tbd_interpolate: false, necb_hdd: true, boiler_fuel: nil, boiler_cap_ratio: nil) ⇒ Object
This method is a wrapper to create the 16 archetypes easily. # 55 args
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 212 def model_create_prototype_model(template:, building_type:, epw_file:, custom_weather_folder: nil, debug: false, sizing_run_dir: Dir.pwd, primary_heating_fuel: 'Electricity', swh_fuel: 'NECB_Default', dcv_type: 'NECB_Default', lights_type: 'NECB_Default', lights_scale: 1.0, daylighting_type: 'NECB_Default', ecm_system_name: 'NECB_Default', ecm_system_zones_map_option: 'NECB_Default', erv_package: 'NECB_Default', boiler_eff: nil, unitary_cop: nil, furnace_eff: nil, shw_eff: nil, ext_wall_cond: nil, ext_floor_cond: nil, ext_roof_cond: nil, ground_wall_cond: nil, ground_floor_cond: nil, ground_roof_cond: nil, door_construction_cond: nil, fixed_window_cond: nil, glass_door_cond: nil, overhead_door_cond: nil, skylight_cond: nil, glass_door_solar_trans: nil, fixed_wind_solar_trans: nil, skylight_solar_trans: nil, rotation_degrees: nil, fdwr_set: -1.0, srr_set: -1.0, nv_type: nil, nv_opening_fraction: nil, nv_temp_out_min: nil, nv_delta_temp_in_out: nil, scale_x: nil, scale_y: nil, scale_z: nil, pv_ground_type: nil, pv_ground_total_area_pv_panels_m2: nil, pv_ground_tilt_angle: nil, pv_ground_azimuth_angle: nil, pv_ground_module_description: nil, chiller_type: nil, occupancy_loads_scale: nil, electrical_loads_scale: nil, oa_scale: nil, infiltration_scale: nil, output_variables: nil, shw_scale: nil, output_meters: nil, airloop_economizer_type: nil, baseline_system_zones_map_option: nil, tbd_option: nil, tbd_interpolate: false, necb_hdd: true, boiler_fuel: nil, boiler_cap_ratio: nil) model = load_building_type_from_library(building_type: building_type) return model_apply_standard(model: model, tbd_option: tbd_option, tbd_interpolate: tbd_interpolate, epw_file: epw_file, custom_weather_folder: custom_weather_folder, sizing_run_dir: sizing_run_dir, primary_heating_fuel: primary_heating_fuel, swh_fuel: swh_fuel, dcv_type: dcv_type, # Four options: (1) 'NECB_Default', (2) 'No_DCV', (3) 'Occupancy_based_DCV' , (4) 'CO2_based_DCV' lights_type: lights_type, # Two options: (1) 'NECB_Default', (2) 'LED' lights_scale: lights_scale, daylighting_type: daylighting_type, # Two options: (1) nil/none/false/'NECB_Default' (Option #1 puts daylighting sensors in the spaces as per NECB requirements; so some spaces may not have sensors), (2) 'add_daylighting_controls' (Option #2 puts daylighting sensors in all spaces regardless of NECB requirements) ecm_system_name: ecm_system_name, ecm_system_zones_map_option: ecm_system_zones_map_option, # (1) 'NECB_Default' (2) 'one_sys_per_floor' (3) 'one_sys_per_bldg' erv_package: erv_package, boiler_eff: boiler_eff, unitary_cop: unitary_cop, furnace_eff: furnace_eff, shw_eff: shw_eff, ext_wall_cond: ext_wall_cond, ext_floor_cond: ext_floor_cond, ext_roof_cond: ext_roof_cond, ground_wall_cond: ground_wall_cond, ground_floor_cond: ground_floor_cond, ground_roof_cond: ground_roof_cond, door_construction_cond: door_construction_cond, fixed_window_cond: fixed_window_cond, glass_door_cond: glass_door_cond, overhead_door_cond: overhead_door_cond, skylight_cond: skylight_cond, glass_door_solar_trans: glass_door_solar_trans, fixed_wind_solar_trans: fixed_wind_solar_trans, skylight_solar_trans: skylight_solar_trans, rotation_degrees: rotation_degrees, fdwr_set: fdwr_set, srr_set: srr_set, nv_type: nv_type, # Two options: (1) nil/none/false/'NECB_Default', (2) 'add_nv' nv_opening_fraction: nv_opening_fraction, # options: (1) nil/none/false (2) 'NECB_Default' (i.e. 0.1), (3) opening fraction of windows, which can be a float number between 0.0 and 1.0 nv_temp_out_min: nv_temp_out_min, # options: (1) nil/none/false(2) 'NECB_Default' (i.e. 13.0 based on inputs from Michel Tardif re a real school in QC), (3) minimum outdoor air temperature (in Celsius) below which natural ventilation is shut down nv_delta_temp_in_out: nv_delta_temp_in_out, # options: (1) nil/none/false (2) 'NECB_Default' (i.e. 1.0 based on inputs from Michel Tardif re a real school in QC), (3) temperature difference (in Celsius) between the indoor and outdoor air temperatures below which ventilation is shut down scale_x: scale_x, scale_y: scale_y, scale_z: scale_z, pv_ground_type: pv_ground_type, # Two options: (1) nil/none/false/'NECB_Default', (2) 'add_pv_ground' pv_ground_total_area_pv_panels_m2: pv_ground_total_area_pv_panels_m2, # Options: (1) nil/none/false, (2) 'NECB_Default' (i.e. building footprint), (3) area value (e.g. 50) pv_ground_tilt_angle: pv_ground_tilt_angle, # Options: (1) nil/none/false, (2) 'NECB_Default' (i.e. latitude), (3) tilt angle value (e.g. 20) pv_ground_azimuth_angle: pv_ground_azimuth_angle, # Options: (1) nil/none/false, (2) 'NECB_Default' (i.e. south), (3) azimuth angle value (e.g. 90) pv_ground_module_description: pv_ground_module_description, # Options: (1) nil/none/false, (2) 'NECB_Default' (i.e. Standard), (3) other options ('Standard', 'Premium', ThinFilm') occupancy_loads_scale: occupancy_loads_scale, electrical_loads_scale: electrical_loads_scale, oa_scale: oa_scale, infiltration_scale: infiltration_scale, chiller_type: chiller_type, # Options: (1) 'NECB_Default'/nil/'none'/false (i.e. do nothing), (2) e.g. 'VSD' output_variables: output_variables, shw_scale: shw_scale, # Options: (1) 'NECB_Default'/nil/'none'/false (i.e. do nothing), (2) a float number larger than 0.0 output_meters: output_meters, airloop_economizer_type: airloop_economizer_type, # (1) 'NECB_Default'/nil/' (2) 'DifferentialEnthalpy' (3) 'DifferentialTemperature' baseline_system_zones_map_option: baseline_system_zones_map_option, # Three options: (1) 'NECB_Default'/'none'/nil (i.e. 'one_sys_per_bldg'), (2) 'one_sys_per_dwelling_unit', (3) 'one_sys_per_bldg' necb_hdd: necb_hdd, boiler_fuel: boiler_fuel, boiler_cap_ratio: boiler_cap_ratio ) end |
#model_create_thermal_zones(model, space_multiplier_map = nil) ⇒ Boolean
Creates thermal zones to contain each space, as defined for each building in the system_to_space_map inside the Prototype.building_name e.g. (Prototype.secondary_school.rb) file.
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 8 def model_create_thermal_zones(model, space_multiplier_map = nil) OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Started creating thermal zones') space_multiplier_map = {} if space_multiplier_map.nil? # Remove any Thermal zones assigned model.getThermalZones.each(&:remove) # Create a thermal zone for each space in the self model.getSpaces.sort.each do |space| zone = OpenStudio::Model::ThermalZone.new(model) zone.setName("#{space.name} ZN") unless space_multiplier_map[space.name.to_s].nil? || (space_multiplier_map[space.name.to_s] == 1) zone.setMultiplier(space_multiplier_map[space.name.to_s]) end space.setThermalZone(zone) # Skip thermostat for spaces with no space type next if space.spaceType.empty? # Add a thermostat space_type_name = space.spaceType.get.name.get thermostat_name = space_type_name + ' Thermostat' thermostat = model.getThermostatSetpointDualSetpointByName(thermostat_name) if thermostat.empty? OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', "Thermostat #{thermostat_name} not found for space name: #{space.name}") else thermostat_clone = thermostat.get.clone(model).to_ThermostatSetpointDualSetpoint.get zone.setThermostatSetpointDualSetpoint(thermostat_clone) # Set Ideal loads to thermal zone for sizing for NECB needs. We need this for sizing. ideal_loads = OpenStudio::Model::ZoneHVACIdealLoadsAirSystem.new(model) ideal_loads.addToThermalZone(zone) end end OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', 'Finished creating thermal zones') end |
#model_enable_demand_controlled_ventilation(model, dcv_type = 'No_DCV') ⇒ Object
Note: Values for dcv_type are: ‘Occupancy_based_DCV’, ‘CO2_based_DCV’, ‘No_DCV’, ‘NECB_Default’
1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1756 def model_enable_demand_controlled_ventilation(model, dcv_type = 'No_DCV') return if dcv_type == 'NECB_Defualt' if dcv_type == 'Occupancy_based_DCV' || dcv_type == 'CO2_based_DCV' # @todo IMPORTANT: (upon other BTAP tasks) Set a value for the "Outdoor Air Flow per Person" field of the "OS:DesignSpecification:OutdoorAir" object # Note: The "Outdoor Air Flow per Person" field is required for occupancy-based DCV. # Note: The "Outdoor Air Flow per Person" values should be based on ASHRAE 62.1: Article 6.2.2.1. # Note: The "Outdoor Air Flow per Person" should be entered for "ventilation_per_person" in "lib/openstudio-standards/standards/necb/NECB2011/data/space_types.json" ##### Define indoor CO2 availability schedule (required for CO2-based DCV) ##### Reference: see page B.13 of PNNL (2017), "Impacts of Commercial Building Controls on Energy Savings and Peak Load Reduction", available a: https://www.energy.gov/eere/buildings/downloads/impacts-commercial-building-controls-energy-savings-and-peak-load-reduction ##### Note: the defined schedule here is redundant as the schedule says it is always on AND ##### the "ZoneControl:ContaminantController" object says that "If this field is left blank, the schedule has a value of 1 for all time periods". indoor_co2_availability_schedule = OpenStudio::Model::ScheduleCompact.new(model) indoor_co2_availability_schedule.setName('indoor_co2_availability_schedule') indoor_co2_availability_schedule.setScheduleTypeLimits(BTAP::Resources::Schedules::StandardScheduleTypeLimits.get_fraction(model)) indoor_co2_availability_schedule.to_ScheduleCompact.get # indoor_co2_availability_schedule.setString(1,"indoor_co2_availability_schedule") indoor_co2_availability_schedule.setString(3, 'Through: 12/31') indoor_co2_availability_schedule.setString(4, 'For: Weekdays SummerDesignDay') indoor_co2_availability_schedule.setString(5, 'Until: 07:00') indoor_co2_availability_schedule.setString(6, '0.0') indoor_co2_availability_schedule.setString(7, 'Until: 22:00') indoor_co2_availability_schedule.setString(8, '1.0') indoor_co2_availability_schedule.setString(9, 'Until: 24:00') indoor_co2_availability_schedule.setString(10, '0.0') indoor_co2_availability_schedule.setString(11, 'For: Saturday WinterDesignDay') indoor_co2_availability_schedule.setString(12, 'Until: 07:00') indoor_co2_availability_schedule.setString(13, '0.0') indoor_co2_availability_schedule.setString(14, 'Until: 18:00') indoor_co2_availability_schedule.setString(15, '1.0') indoor_co2_availability_schedule.setString(16, 'Until: 24:00') indoor_co2_availability_schedule.setString(17, '0.0') indoor_co2_availability_schedule.setString(18, 'For: AllOtherDays') indoor_co2_availability_schedule.setString(19, 'Until: 24:00') indoor_co2_availability_schedule.setString(20, '0.0') ##### Define indoor CO2 setpoint schedule (required for CO2-based DCV) ##### Reference: see page B.13 of PNNL (2017), "Impacts of Commercial Building Controls on Energy Savings and Peak Load Reduction", available a: https://www.energy.gov/eere/buildings/downloads/impacts-commercial-building-controls-energy-savings-and-peak-load-reduction indoor_co2_setpoint_schedule = OpenStudio::Model::ScheduleCompact.new(model) indoor_co2_setpoint_schedule.setName('indoor_co2_setpoint_schedule') indoor_co2_setpoint_schedule.setScheduleTypeLimits(get_any_number_ppm(model)) indoor_co2_setpoint_schedule.to_ScheduleCompact.get indoor_co2_setpoint_schedule.setString(3, 'Through: 12/31') indoor_co2_setpoint_schedule.setString(4, 'For: AllDays') indoor_co2_setpoint_schedule.setString(5, 'Until: 24:00') indoor_co2_setpoint_schedule.setString(6, '1000.0') # indoor_co2_setpoint_schedule.setToConstantValue(1000.0) #1000 ppm ##### Define outdoor CO2 schedule (required for CO2-based DCV ##### Reference: see page B.13 of PNNL (2017), "Impacts of Commercial Building Controls on Energy Savings and Peak Load Reduction", available a: https://www.energy.gov/eere/buildings/downloads/impacts-commercial-building-controls-energy-savings-and-peak-load-reduction outdoor_co2_schedule = OpenStudio::Model::ScheduleCompact.new(model) outdoor_co2_schedule.setName('outdoor_co2_schedule') outdoor_co2_schedule.setScheduleTypeLimits(get_any_number_ppm(model)) outdoor_co2_schedule.to_ScheduleCompact.get outdoor_co2_schedule.setString(3, 'Through: 12/31') outdoor_co2_schedule.setString(4, 'For: AllDays') outdoor_co2_schedule.setString(5, 'Until: 24:00') outdoor_co2_schedule.setString(6, '400.0') # outdoor_co2_schedule.setToConstantValue(400.0) #400 ppm ##### Define ZoneAirContaminantBalance (required for CO2-based DCV) zone_air_contaminant_balance = model.getZoneAirContaminantBalance zone_air_contaminant_balance.setCarbonDioxideConcentration(true) zone_air_contaminant_balance.setOutdoorCarbonDioxideSchedule(outdoor_co2_schedule) ##### Set CO2 controller in each space (required for CO2-based DCV) model.getSpaces.sort.each do |space| # puts space.name.to_s zone = space.thermalZone if !zone.empty? zone = space.thermalZone.get end zone_control_co2 = OpenStudio::Model::ZoneControlContaminantController.new(zone.model) zone_control_co2.setName("#{space.name} Zone Control Contaminant Controller") zone_control_co2.setCarbonDioxideControlAvailabilitySchedule(indoor_co2_availability_schedule) zone_control_co2.setCarbonDioxideSetpointSchedule(indoor_co2_setpoint_schedule) zone.setZoneControlContaminantController(zone_control_co2) end # if dcv_type == "Occupancy_based_DCV" || dcv_type == "CO2_based_DCV" end ##### Loop through AirLoopHVACs model.getAirLoopHVACs.sort.each do |air_loop| ##### Loop through AirLoopHVAC's supply nodes to: ##### (1) Find its AirLoopHVAC:OutdoorAirSystem using the supply node; ##### (2) Find Controller:OutdoorAir using AirLoopHVAC:OutdoorAirSystem; ##### (3) Get "Controller Mechanical Ventilation" from Controller:OutdoorAir. air_loop.supplyComponents.sort.each do |supply_component| ##### Find AirLoopHVAC:OutdoorAirSystem of AirLoopHVAC using the supply node. hvac_component = supply_component.to_AirLoopHVACOutdoorAirSystem if !hvac_component.empty? ##### Find Controller:OutdoorAir using AirLoopHVAC:OutdoorAirSystem. hvac_component = hvac_component.get controller_oa = hvac_component.getControllerOutdoorAir ##### Get "Controller Mechanical Ventilation" from Controller:OutdoorAir. controller_mv = controller_oa.controllerMechanicalVentilation ##### Set "Demand Controlled Ventilation" to "Yes" or "No" in Controller:MechanicalVentilation depending on dcv_type. if (dcv_type == 'CO2_based_DCV') || (dcv_type == 'Occupancy_based_DCV') # Occupancy controller_mv.setDemandControlledVentilation(true) ##### Set the "System Outdoor Air Method" field based on dcv_type in the Controller:MechanicalVentilation object if dcv_type == 'CO2_based_DCV' controller_mv.setSystemOutdoorAirMethod('IndoorAirQualityProcedure') else # dcv_type == 'Occupancy_based_DCV' controller_mv.setSystemOutdoorAirMethod('ZoneSum') end elsif dcv_type == 'No_DCV' controller_mv.setDemandControlledVentilation(false) end # puts controller_mv # if !hvac_component.empty? end # air_loop.supplyComponents.each do |supply_component| end # model.getAirLoopHVACs.each do |air_loop| end end |
#model_find_climate_zone_set(model, clim) ⇒ Object
Helper method to find out which climate zone set contains a specific climate zone. Returns climate zone set name as String if success, nil if not found.
1653 1654 1655 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1653 def model_find_climate_zone_set(model, clim) return 'NECB-CNEB ClimatZone 4-8' end |
#necb_design_supply_temp_compliance(qaqc) ⇒ Object
1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1470 def necb_design_supply_temp_compliance(qaqc) necb_section_name = get_qaqc_table(table_name: 'design_supply_temp_compliance')['refs'].join(',') qaqc_table = get_qaqc_table(table_name: 'design_supply_temp_compliance') tolerance = 3 qaqc[:thermal_zones].each do |zoneinfo| # skipping undefined schedules if (qaqc_table['exclude']['exclude_string'].any? { |ex_string| zoneinfo[:name].to_s.include? ex_string }) && !qaqc_table['exclude']['exclude_string'].empty? puts "#{zoneinfo[:name]} was skipped in necb_zone_sizing_compliance because it contains #{qaqc_table['exclude']['exclude_string'].join(',')}" next end design_supply_temp_compliance = qaqc_table['table'] design_supply_temp_compliance.each do |compliance| if compliance['var'] == 'heating_design_supply_air_temp' result_value = zoneinfo[:zone_heating_design_supply_air_temperature] elsif compliance['var'] == 'cooling_design_supply_temp' result_value = zoneinfo[:zone_cooling_design_supply_air_temperature] end next if result_value.nil? test_text = "[ZONE][#{zoneinfo[:name]}] #{compliance['var']}" # puts key necb_section_test( qaqc, result_value, compliance['bool_operator'], compliance['expected_value'], necb_section_name, test_text, tolerance ) end end # Design supply temp test # necb_section_name = "NECB2011-?" # round_precision = 3 # qaqc[:thermal_zones].each do |zoneinfo| # # skipping undefined schedules # if zoneinfo[:name].to_s.include?"- undefined -" # next # end # data = {} # #data[:heating_sizing_factor] = [1.3 , zoneinfo[:heating_sizing_factor]] # #data[:cooling_sizing_factor] = [1.1 ,zoneinfo[:cooling_sizing_factor]] # data[:heating_design_supply_air_temp] = [43.0, zoneinfo[:zone_heating_design_supply_air_temperature] ] #unless zoneinfo[:zone_heating_design_supply_air_temperature].nil? # data[:cooling_design_supply_temp] = [13.0, zoneinfo[:zone_cooling_design_supply_air_temperature] ] # data.each do |key,value| # #puts key # necb_section_test( # qaqc, # value[0], # '==', # value[1], # necb_section_name, # "[ZONE][#{zoneinfo[:name]}] #{key}", # round_precision # ) # end # end end |
#necb_economizer_compliance(qaqc) ⇒ Object
1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1532 def necb_economizer_compliance(qaqc) # determine correct economizer usage according to section 5.2.2.7 of NECB2011 necb_section_name = get_qaqc_table(table_name: 'economizer_compliance')['refs'].join(',') qaqc_table = get_qaqc_table(table_name: 'economizer_compliance') # stores the full hash of qaqc for economizer_compliance # necb_section_name = "NECB2011-5.2.2.7" qaqc[:air_loops].each do |air_loop_info| capacity = -1.0 if !air_loop_info[:cooling_coils][:dx_single_speed][0].nil? puts 'capacity = air_loop_info[:cooling_coils][:dx_single_speed][0][:nominal_total_capacity_w]' capacity = air_loop_info[:cooling_coils][:dx_single_speed][0][:nominal_total_capacity_w] elsif !air_loop_info[:cooling_coils][:dx_two_speed][0].nil? puts 'capacity = air_loop_info[:cooling_coils][:dx_two_speed][0][:cop_high]' capacity = air_loop_info[:cooling_coils][:dx_two_speed][0][:cop_high] elsif !air_loop_info[:cooling_coils][:coil_cooling_water][0].nil? puts 'capacity = air_loop_info[:cooling_coils][:coil_cooling_water][0][:nominal_total_capacity_w]' capacity = air_loop_info[:cooling_coils][:coil_cooling_water][0][:nominal_total_capacity_w] end puts capacity if capacity == -1.0 # This should not happen qaqc[:errors] << "[necb_economizer_compliance] air_loop_info[:cooling_coils] for #{air_loop_info[:name]} does not have a capacity " else # check for correct economizer usage # puts "air_loop_info[:supply_fan][:max_air_flow_rate]: #{air_loop_info[:supply_fan][:max_air_flow_rate]}" unless air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] == -1.0 # capacity should be in kW max_air_flow_rate_m3_per_s = air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] necb_section_test( qaqc, eval(qaqc_table['table'][0]['expected_value']), '==', air_loop_info[:economizer][:control_type], necb_section_name, "[AIR LOOP][#{air_loop_info[:name]}][:economizer][:control_type]" ) end end end end |
#necb_envelope_compliance(qaqc) ⇒ Object
checks envelope compliance fenestration_to_door_and_window_percentage, skylight_to_roof_percentage
1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1168 def necb_envelope_compliance(qaqc) # Envelope necb_section_name = 'NECB2011-Section 3.2.1.4' # store hdd in short form hdd = qaqc[:geography][:hdd] # calculate fdwr based on hdd. fdwr = 0 if hdd < 4000 fdwr = 0.40 elsif (hdd >= 4000) && (hdd <= 7000) fdwr = (2000 - 0.2 * hdd) / 3000 elsif hdd > 7000 fdwr = 0.20 end # hardset srr to 0.05 srr = 0.05 # create table of expected values and results. data = {} data[:fenestration_to_door_and_window_percentage] = [fdwr * 100, qaqc[:envelope][:fdwr].round(3)] data[:skylight_to_roof_percentage] = [srr * 100, qaqc[:envelope][:srr].round(3)] # perform test. result must be less than or equal to. data.each do |key, value| necb_section_test( qaqc, value[0], '>=', value[1], necb_section_name, "[ENVELOPE]#{key}", 1 # padmassun added tollerance ) end end |
#necb_exterior_fenestration_compliance(qaqc) ⇒ Object
1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1308 def necb_exterior_fenestration_compliance(qaqc) # Exterior Fenestration necb_section_name = get_qaqc_table(table_name: 'exterior_fenestration_compliance')['refs'].join(',') climate_index = NECB2011.new.get_climate_zone_index(qaqc[:geography][:hdd]) tolerance = 3 # puts "\n\n" # puts "climate_index: #{climate_index}" # puts get_qaqc_table("exterior_fenestration_compliance", {"var" => "ext_window_conductances", "climate_index" => 2}) ['ext_window_conductances', 'ext_door_conductances', 'ext_overhead_door_conductances', 'ext_skylight_conductances'].each do |compliance_var| qaqc_table = get_qaqc_table(table_name: 'exterior_fenestration_compliance', search_criteria: { 'var' => compliance_var, 'climate_index' => climate_index }).first # puts "\n#{qaqc_table}\n" if compliance_var == 'ext_window_conductances' result_value = qaqc[:envelope][:windows_average_conductance_w_per_m2_k] elsif compliance_var == 'ext_door_conductances' result_value = qaqc[:envelope][:doors_average_conductance_w_per_m2_k] elsif compliance_var == 'ext_overhead_door_conductances' result_value = qaqc[:envelope][:overhead_doors_average_conductance_w_per_m2_k] elsif compliance_var == 'ext_skylight_conductances' result_value = qaqc[:envelope][:skylights_average_conductance_w_per_m2_k] end test_text = "[ENVELOPE] #{compliance_var}" next if result_value.nil? necb_section_test( qaqc, result_value, qaqc_table['bool_operator'], qaqc_table['expected_value'], necb_section_name, test_text, tolerance ) end # necb_section_name = "NECB2011-Section 3.2.2.3" # climate_index = BTAP::Compliance::NECB2011::get_climate_zone_index(qaqc[:geography][:hdd]) # result_value_index = 6 # round_precision = 3 # data = {} # data[:ext_window_conductances] = [2.400,2.200,2.200,2.200,2.200,1.600,qaqc[:envelope][:windows_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:windows_average_conductance_w_per_m2_k].nil? # data[:ext_door_conductances] = [2.400,2.200,2.200,2.200,2.200,1.600,qaqc[:envelope][:doors_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:doors_average_conductance_w_per_m2_k].nil? # data[:ext_overhead_door_conductances] = [2.400,2.200,2.200,2.200,2.200,1.600,qaqc[:envelope][:overhead_doors_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:overhead_doors_average_conductance_w_per_m2_k].nil? # data[:ext_skylight_conductances] = [2.400,2.200,2.200,2.200,2.200,1.600,qaqc[:envelope][:skylights_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:skylights_average_conductance_w_per_m2_k].nil? # data.each do |key,value| # #puts key # necb_section_test( # qaqc, # value[result_value_index].round(round_precision), # '==', # value[climate_index].round(round_precision), # necb_section_name, # "[ENVELOPE]#{key}", # round_precision # ) # end end |
#necb_exterior_ground_surfaces_compliance(qaqc) ⇒ Object
1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1366 def necb_exterior_ground_surfaces_compliance(qaqc) # Exterior Ground surfaces necb_section_name = get_qaqc_table(table_name: 'exterior_ground_surfaces_compliance')['refs'].join(',') climate_index = NECB2011.new.get_climate_zone_index(qaqc[:geography][:hdd]) tolerance = 3 # puts "\n\n" # puts "climate_index: #{climate_index}" # puts get_qaqc_table("exterior_ground_surfaces_compliance", {"var" => "ground_wall_conductances", "climate_index" => 2}) ['ground_wall_conductances', 'ground_roof_conductances', 'ground_floor_conductances'].each do |compliance_var| qaqc_table = get_qaqc_table(table_name: 'exterior_ground_surfaces_compliance', search_criteria: { 'var' => compliance_var, 'climate_index' => climate_index }).first # puts "\n#{qaqc_table}\n" if compliance_var == 'ground_wall_conductances' result_value = qaqc[:envelope][:ground_walls_average_conductance_w_per_m2_k] elsif compliance_var == 'ground_roof_conductances' result_value = qaqc[:envelope][:ground_roofs_average_conductance_w_per_m2_k] elsif compliance_var == 'ground_floor_conductances' result_value = qaqc[:envelope][:ground_floors_average_conductance_w_per_m2_k] end test_text = "[ENVELOPE] #{compliance_var}" next if result_value.nil? necb_section_test( qaqc, result_value, qaqc_table['bool_operator'], qaqc_table['expected_value'], necb_section_name, test_text, tolerance ) end # necb_section_name = "NECB2011-Section 3.2.3.1" # climate_index = BTAP::Compliance::NECB2011::get_climate_zone_index(qaqc[:geography][:hdd]) # result_value_index = 6 # round_precision = 3 # data = {} # data[:ground_wall_conductances] = [ 0.568,0.379,0.284,0.284,0.284,0.210, qaqc[:envelope][:ground_walls_average_conductance_w_per_m2_k] ] unless qaqc[:envelope][:ground_walls_average_conductance_w_per_m2_k].nil? # data[:ground_roof_conductances] = [ 0.568,0.379,0.284,0.284,0.284,0.210, qaqc[:envelope][:ground_roofs_average_conductance_w_per_m2_k] ] unless qaqc[:envelope][:ground_roofs_average_conductance_w_per_m2_k].nil? # data[:ground_floor_conductances] = [ 0.757,0.757,0.757,0.757,0.757,0.379, qaqc[:envelope][:ground_floors_average_conductance_w_per_m2_k] ] unless qaqc[:envelope][:ground_floors_average_conductance_w_per_m2_k].nil? # data.each {|key,value| necb_section_test( # qaqc, # value[result_value_index], # '==', # value[climate_index], # necb_section_name, # "[ENVELOPE]#{key}", # round_precision # ) # } end |
#necb_exterior_opaque_compliance(qaqc) ⇒ Object
1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1255 def necb_exterior_opaque_compliance(qaqc) # puts JSON.pretty_generate @qaqc_data # Exterior Opaque necb_section_name = get_qaqc_table(table_name: 'exterior_opaque_compliance')['refs'].join(',') climate_index = NECB2011.new.get_climate_zone_index(qaqc[:geography][:hdd]) puts "HDD #{qaqc[:geography][:hdd]}" tolerance = 3 # puts "\n\n" # puts "climate_index: #{climate_index}" # puts get_qaqc_table("exterior_opaque_compliance", {"var" => "ext_wall_conductances", "climate_index" => 2}) ['ext_wall_conductances', 'ext_roof_conductances', 'ext_floor_conductances'].each do |compliance_var| qaqc_table = get_qaqc_table(table_name: 'exterior_opaque_compliance', search_criteria: { 'var' => compliance_var, 'climate_index' => climate_index }).first # puts "\n#{qaqc_table}\n" if compliance_var == 'ext_wall_conductances' result_value = qaqc[:envelope][:outdoor_walls_average_conductance_w_per_m2_k] elsif compliance_var == 'ext_roof_conductances' result_value = qaqc[:envelope][:outdoor_floors_average_conductance_w_per_m2_k] elsif compliance_var == 'ext_floor_conductances' result_value = qaqc[:envelope][:outdoor_roofs_average_conductance_w_per_m2_k] end test_text = "[ENVELOPE] #{compliance_var}" next if result_value.nil? necb_section_test( qaqc, result_value, qaqc_table['bool_operator'], qaqc_table['expected_value'], necb_section_name, test_text, tolerance ) end # result_value_index = 6 # round_precision = 3 # data = {} # data[:ext_wall_conductances] = [0.315,0.278,0.247,0.210,0.210,0.183,qaqc[:envelope][:outdoor_walls_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:outdoor_walls_average_conductance_w_per_m2_k].nil? # data[:ext_roof_conductances] = [0.227,0.183,0.183,0.162,0.162,0.142,qaqc[:envelope][:outdoor_roofs_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:outdoor_roofs_average_conductance_w_per_m2_k].nil? # data[:ext_floor_conductances] = [0.227,0.183,0.183,0.162,0.162,0.142,qaqc[:envelope][:outdoor_floors_average_conductance_w_per_m2_k]] unless qaqc[:envelope][:outdoor_floors_average_conductance_w_per_m2_k].nil? # data.each {|key,value| necb_section_test( # qaqc, # value[result_value_index], # '==', # value[climate_index], # necb_section_name, # "[ENVELOPE]#{key}", # round_precision # ) # } end |
#necb_hrv_compliance(qaqc, model) ⇒ Object
1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1573 def necb_hrv_compliance(qaqc, model) # HRV check hrv_compliance = get_qaqc_table(table_name: 'hrv_compliance')['table'] necb_section_name = get_qaqc_table(table_name: 'hrv_compliance')['refs'].join(',') qaqc[:air_loops].each do |air_loop_info| hrv_compliance.each do |compliance| data = {} # puts "\nspaceinfo[#{compliance['var']}]" result_value = !air_loop_info[:heat_exchanger].empty? # puts "#{compliance['test_text']}" test_text = "[AIR LOOP][:heat_exchanger] for [#{air_loop_info[:name]}] is present?" # puts "result_value: #{result_value}" # puts "test_text: #{test_text}\n" # data[:infiltration_method] = [ "Flow/ExteriorArea", spaceinfo[:infiltration_method] , nil ] # data[:infiltration_flow_per_m2] = [ 0.00025, spaceinfo[:infiltration_flow_per_m2], 5 ] # data.each do |key,value| # puts key outdoor_air_L_per_s = air_loop_info[:outdoor_air_L_per_s] weather_file_path = model.weatherFile.get.path.get.to_s stat_file_path = weather_file_path.gsub('.epw', '.stat') stat_file = OpenstudioStandards::Weather::StatFile.new(stat_file_path) db990 = stat_file.heating_design_info[2] necb_section_test( qaqc, result_value, '==', eval(compliance['expected_value']), necb_section_name, test_text, compliance['tolerance'] ) end end # necb_section_name = "NECB2011-5.2.10.1" # qaqc[:air_loops].each do |air_loop_info| # unless air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] == -1.0 # weather_file_path = model.weatherFile.get.path.get.to_s # stat_file_path = weather_file_path.gsub('.epw', '.stat') # stat_file = OpenstudioStandards::Weather::StatFile.new(stat_file_path) # db990 = stat_file.heating_design_info[2] # hrv_calc = 0.00123 * air_loop_info[:outdoor_air_L_per_s] * (21 - db990) #=AP46*(21-O$1) # hrv_reqd = hrv_calc > 150 ? true : false # #qaqc[:information] << "[Info][TEST-PASS][#{necb_section_name}]:#{test_text} result value:#{result_value} #{bool_operator} expected value:#{expected_value}" # hrv_present = false # unless air_loop_info[:heat_exchanger].empty? # hrv_present = true # end # necb_section_test( # qaqc, # hrv_reqd, # '==', # hrv_present, # necb_section_name, # "[AIR LOOP][:heat_exchanger] for [#{air_loop_info[:name]}] is present?" # ) # else # qaqc['warnings'] << "[hrv_compliance] air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] == -1.0 for [#{air_loop_info[:name]}]" # end # end end |
#necb_hrv_compliance_for_single_airloop(qaqc, model, air_loop_info) ⇒ Object
This methos is not used as part of the QAQC process, because support for MURBS has not been implemented for HRV in NECB 2011 and 2015
This method will run the HRV compliance for a single air loop
1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1728 def necb_hrv_compliance_for_single_airloop(qaqc, model, air_loop_info) # HRV check hrv_compliance = get_qaqc_table('hrv_compliance')['table'] necb_section_name = get_qaqc_table('hrv_compliance')['refs'].join(',') hrv_compliance.each do |compliance| data = {} # puts "\nspaceinfo[#{compliance['var']}]" result_value = !air_loop_info[:heat_exchanger].empty? # puts "#{compliance['test_text']}" test_text = "[AIR LOOP][:heat_exchanger] for [#{air_loop_info[:name]}] is present?" # puts "result_value: #{result_value}" # puts "test_text: #{test_text}\n" # data[:infiltration_method] = [ "Flow/ExteriorArea", spaceinfo[:infiltration_method] , nil ] # data[:infiltration_flow_per_m2] = [ 0.00025, spaceinfo[:infiltration_flow_per_m2], 5 ] # data.each do |key,value| # puts key outdoor_air_L_per_s = air_loop_info[:outdoor_air_L_per_s] weather_file_path = model.weatherFile.get.path.get.to_s stat_file_path = weather_file_path.gsub('.epw', '.stat') stat_file = OpenstudioStandards::Weather::StatFile.new(stat_file_path) db990 = stat_file.heating_design_info[2] necb_section_test( qaqc, result_value, '==', eval(compliance['expected_value']), necb_section_name, test_text, compliance['tolerance'] ) end # necb_section_name = "NECB2011-5.2.10.1" # qaqc[:air_loops].each do |air_loop_info| # unless air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] == -1.0 # weather_file_path = model.weatherFile.get.path.get.to_s # stat_file_path = weather_file_path.gsub('.epw', '.stat') # stat_file = OpenstudioStandards::Weather::StatFile.new(stat_file_path) # db990 = stat_file.heating_design_info[2] # hrv_calc = 0.00123 * air_loop_info[:outdoor_air_L_per_s] * (21 - db990) #=AP46*(21-O$1) # hrv_reqd = hrv_calc > 150 ? true : false # #qaqc[:information] << "[Info][TEST-PASS][#{necb_section_name}]:#{test_text} result value:#{result_value} #{bool_operator} expected value:#{expected_value}" # hrv_present = false # unless air_loop_info[:heat_exchanger].empty? # hrv_present = true # end # necb_section_test( # qaqc, # hrv_reqd, # '==', # hrv_present, # necb_section_name, # "[AIR LOOP][:heat_exchanger] for [#{air_loop_info[:name]}] is present?" # ) # else # qaqc['warnings'] << "[hrv_compliance] air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] == -1.0 for [#{air_loop_info[:name]}]" # end # end end |
#necb_hrv_compliance_inc_murb(qaqc, model) ⇒ Object
This methos is not used as part of the QAQC process, because support for MURBS has not been implemented for HRV in NECB 2011 and 2015
1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1638 def necb_hrv_compliance_inc_murb(qaqc, model) murb_hrv_compliance = get_qaqc_table('murb_hrv_compliance') hrv_spacetpye_ignore_regex = murb_hrv_compliance['ignored_spacetypes_regex'] hrv_dwelling_unit_spacetpye_regex = murb_hrv_compliance['dwelling_unit_spacetype_regex'] necb_section_name = murb_hrv_compliance['refs'].join(',') model.getAirLoopHVACs.sort.each do |air_loop| air_loop_info = {} qaqc[:air_loops].each do |air_loop_i| next unless air_loop_i[:name] == air_loop.name.get air_loop_info = air_loop_i end zones = air_loop.thermalZones if zones.length == 1 # here the Airloop is serving only one zone # So, next we need to determine if the zone has only # one dwelling unit and no other space types other than stairs, corridor, and lobby zone = zones.first # get the spaces and keep track of the number of spaces and dewlling units num_of_served_spaces = zone.spaces.length contains_dwelling_unit = false if num_of_served_spaces == 0 qaqc[:warnings] << "[necb_murb_hrv_compliance] Thermal Zone [#{zone.name}] does not serve any Spaces" else spaces = zone.spaces() spaces.each do |z_space| spacetype = z_space.spaceType spacetype = validate_optional(spacetype, model, nil) if spacetype.nil? qaqc[:warnings] << "[necb_murb_hrv_compliance] Space [#{z_space.name}] does not have a SpaceType" else # reduce the number of spaces if the space served by the thermal zone is a # stairwell/staircase/lobby/corridor spacetype_name = spacetype.name.to_s ignored_spacetypes_regex = Regexp.new(hrv_spacetpye_ignore_regex, Regexp::IGNORECASE) if ignored_spacetypes_regex =~ spacetype_name num_of_served_spaces -= 1 end # detect is the thermal zone serves a Dwelling Unit dwelling_unit_regex = Regexp.new(hrv_dwelling_unit_spacetpye_regex, Regexp::IGNORECASE) if dwelling_unit_regex =~ spacetype_name contains_dwelling_unit = true end end end if (num_of_served_spaces == 1) && contains_dwelling_unit # here the Thermal zone serves one space that is a dwelling unit # and other space types such as lobby, stairs, or corridors are ignored # So in this case, an HRV is required test_text = "[AIR LOOP][:heat_exchanger] (murb) for [#{air_loop_info[:name]}] is present?" result_value = murb_hrv_compliance['table']['expected_value'] necb_section_test( qaqc, result_value, '==', true, necb_section_name, test_text, nil ) else # Here either the number of served spaces exceed 1 or # does not contain a dwelling unit, So a regular HRV check has to be done for this air loop qaqc[:warnings] << "[necb_murb_hrv_compliance] Regular HRV compliance check for airloop: [#{air_loop.name}], because (it does not serve a single dwelling unit) OR (serves multiple spacetypes)" necb_hrv_compliance_for_single_airloop(qaqc, model, air_loop_info) end end else # here the Airloop does not serve any zones, or it serves more than one zone # So, a regular hrv compliance must be done for this air loop qaqc[:warnings] << "[necb_murb_hrv_compliance] Regular HRV compliance check for airloop: [#{air_loop.name}], because it serves multiple Thermal zones" necb_hrv_compliance_for_single_airloop(qaqc, model, air_loop_info) end end end |
#necb_infiltration_compliance(qaqc, model) ⇒ Object
1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1202 def necb_infiltration_compliance(qaqc, model) # Infiltration # puts "\n" # puts get_qaqc_table("infiltration_compliance") # puts "\n" # puts "\n" # puts get_qaqc_table("infiltration_compliance", {"var" => ":infiltration_method"} ) # puts "\n" # puts "\n" infiltration_compliance = get_qaqc_table(table_name: 'infiltration_compliance')['table'] necb_section_name = get_qaqc_table(table_name: 'infiltration_compliance')['refs'].join(',') qaqc[:spaces].each do |spaceinfo| model.getSpaces.sort.each do |space| next unless space.name.get == spaceinfo[:name] found = false space.surfaces.each do |surface| next unless surface.outsideBoundaryCondition == 'Outdoors' found = true # peform this infiltration qaqc if and only if the space's surface is in contact with outdoors infiltration_compliance.each do |compliance| # puts "\nspaceinfo[#{compliance['var']}]" eval_string = "spaceinfo[:#{compliance['var']}]" result_value = eval(eval_string) # puts "#{compliance['test_text']}" test_text = "[SPACE][#{spaceinfo[:name]}]-#{compliance['var']}" # puts "result_value: #{result_value}" # puts "test_text: #{test_text}\n" # data[:infiltration_method] = [ "Flow/ExteriorArea", spaceinfo[:infiltration_method] , nil ] # data[:infiltration_flow_per_m2] = [ 0.00025, spaceinfo[:infiltration_flow_per_m2], 5 ] # data.each do |key,value| # puts key necb_section_test( qaqc, result_value, compliance['bool_operator'], compliance['expected_value'], necb_section_name, test_text, compliance['tolerance'] ) end # peform qaqc only once per space break end if !found qaqc[:warnings] << "necb_infiltration_compliance for SPACE:[#{spaceinfo[:name]}] was skipped because it does not contain surfaces with 'Outside' boundary condition." end end end end |
#necb_plantloop_sanity(qaqc) ⇒ Object
checks the pump power using pressure, and flowrate
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1035 def necb_plantloop_sanity(qaqc) necb_section_name = 'SANITY-??' qaqc[:plant_loops].each do |plant_loop_info| pump_head = plant_loop_info[:pumps][0][:head_pa] flow_rate = plant_loop_info[:pumps][0][:water_flow_m3_per_s] * 1000 hp_check = ((flow_rate * 60 * 60) / 1000 * 1000 * 9.81 * pump_head * 0.000101997) / 3600000 puts "\npump_head #{pump_head}" puts "name: #{qaqc[:building][:name]}" puts "name: #{plant_loop_info[:name]}" puts "flow_rate #{flow_rate}" puts "hp_check #{hp_check}\n" pump_power_hp = plant_loop_info[:pumps][0][:electric_power_w] / 1000 * 0.746 percent_diff = (hp_check - pump_power_hp).to_f.abs / hp_check * 100 if percent_diff.nan? qaqc[:ruby_warnings] << "(hp_check - pump_power_hp).to_f.abs/hp_check * 100 for #{plant_loop_info[:name]} is NaN" next end if pump_power_hp < 1.0 qaqc[:warnings] << "necb_plantloop_sanity [SKIP] [PLANT LOOP][#{plant_loop_info[:name]}][:pumps][0][:electric_power_hp] because pump_power_hp: [#{pump_power_hp}] < 1 hp" next end necb_section_test( qaqc, percent_diff, '<=', 20, # diff of 20% necb_section_name, "[PLANT LOOP][#{plant_loop_info[:name]}][:pumps][0][:electric_power_hp] [#{pump_power_hp}]; NECB value [#{hp_check}]; Percent Diff" ) end end |
#necb_qaqc(qaqc, model) ⇒ Object
1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1841 def necb_qaqc(qaqc, model) puts "\n\nin necb_qaqc 2011 now\n\n" # Now perform basic QA/QC on items for NECB2011 qaqc[:information] = [] qaqc[:warnings] = [] qaqc[:errors] = [] qaqc[:unique_errors] = [] necb_space_compliance(qaqc) necb_envelope_compliance(qaqc) necb_infiltration_compliance(qaqc, model) necb_exterior_opaque_compliance(qaqc) necb_exterior_fenestration_compliance(qaqc) necb_exterior_ground_surfaces_compliance(qaqc) necb_zone_sizing_compliance(qaqc) necb_design_supply_temp_compliance(qaqc) necb_economizer_compliance(qaqc) necb_hrv_compliance(qaqc, model) necb_vav_fan_power_compliance(qaqc) sanity_check(qaqc) necb_plantloop_sanity(qaqc) qaqc[:information] = qaqc[:information].sort qaqc[:warnings] = qaqc[:warnings].sort qaqc[:errors] = qaqc[:errors].sort qaqc[:unique_errors] = qaqc[:unique_errors].sort return qaqc end |
#necb_section_test(qaqc, result_value, bool_operator, expected_value, necb_section_name, test_text, tolerance = nil) ⇒ Object
1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1882 def necb_section_test(qaqc, result_value, bool_operator, expected_value, necb_section_name, test_text, tolerance = nil) test = 'eval_failed' command = '' if tolerance.is_a?(Integer) command = "#{result_value}.round(#{tolerance}) #{bool_operator} #{expected_value}.round(#{tolerance})" elsif expected_value.is_a?(String) && result_value.is_a?(String) command = "'#{result_value}' #{bool_operator} '#{expected_value}'" else command = "#{result_value} #{bool_operator} #{expected_value}" end test = eval(command) test_res = nil test_res = true if test.to_s.downcase == 'true' test_res = false if test.to_s.downcase == 'false' raise "Eval command failed #{test}" if test_res.nil? if test_res qaqc[:information] << "[Info][TEST-PASS][#{necb_section_name}]:#{test_text} result value:#{result_value} #{bool_operator} expected value:#{expected_value}" else qaqc[:errors] << "[ERROR][TEST-FAIL][#{necb_section_name}]:#{test_text} expected value:#{expected_value} #{bool_operator} result value:#{result_value}" unless (expected_value == -1.0) || (expected_value == 'N/A') qaqc[:unique_errors] << "[ERROR][TEST-FAIL][#{necb_section_name}]:#{test_text} expected value:#{expected_value} #{bool_operator} result value:#{result_value}" end end end |
#necb_space_compliance(qaqc) ⇒ Object
checks space compliance Re: lighting_per_area, occupancy_per_area, occupancy_schedule, electric_equipment_per_area
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1073 def necb_space_compliance(qaqc) # #Padmassun's Code Start # csv_file_name ="#{File.dirname(__FILE__)}/necb_2011_spacetype_info.csv" qaqc[:spaces].each do |space| building_type = '' space_type = '' if space[:space_type_name].include? 'Space Function ' space_type = space[:space_type_name].to_s.rpartition('Space Function ')[2].strip building_type = 'Space Function' elsif space[:space_type_name].include? ' WholeBuilding' space_type = space[:space_type_name].to_s.rpartition(' WholeBuilding')[0].strip building_type = 'WholeBuilding' end ['lighting_per_area_w_per_m2', 'occupancy_per_area_people_per_m2', 'occupancy_schedule', 'electric_equipment_per_area_w_per_m2'].each do |compliance_var| qaqc_table = get_qaqc_table(table_name: 'space_compliance', search_criteria: { 'building_type' => building_type, 'space_type' => space_type }).first puts "\n#{qaqc_table}\n" necb_section_name = get_qaqc_table(table_name: 'space_compliance')['refs'][compliance_var] tolerance = get_qaqc_table(table_name: 'space_compliance')['tolerance'][compliance_var] # puts "\ncompliance_var:#{compliance_var}\n\tnecb_section_name:#{necb_section_name}\n\texp Value:#{qaqc_table[compliance_var]}\n" if compliance_var == 'lighting_per_area_w_per_m2' if space[:lighting_w_per_m2].nil? result_value = 0 else result_value = space[:lighting_w_per_m2] * qaqc_table['lpd_ratio'] end elsif compliance_var == 'occupancy_per_area_people_per_m2' result_value = space[:occ_per_m2] elsif compliance_var == 'occupancy_schedule' result_value = space[:occupancy_schedule] elsif compliance_var == 'electric_equipment_per_area_w_per_m2' result_value = space[:electric_w_per_m2] end test_text = "[SPACE][#{space[:name]}]-[TYPE:][#{space_type}]-#{compliance_var}" next if result_value.nil? necb_section_test( qaqc, result_value, '==', qaqc_table[compliance_var], necb_section_name, test_text, tolerance ) end # row = look_up_csv_data(csv_file_name,{2 => space_type, 1 => building_type}) # if row.nil? # #raise ("space type of [#{space_type}] and/or building type of [#{building_type}] was not found in the excel sheet for space: [#{space[:name]}]") # qaqc[:ruby_warnings] << "space type of [#{space_type}] and/or building type of [#{building_type}] was not found in the excel sheet for space: [#{space[:name]}]" # puts "space type of [#{space_type}] and/or building type of [#{building_type}] was not found in the excel sheet for space: [#{space[:name]}]" # else # #correct the data from the csv file to include a multiplier of 0.9 for specific space types. # reduceLPDSpaces = ["Classroom/lecture/training", "Conf./meet./multi-purpose", "Lounge/recreation", # "Washroom-sch-A", "Washroom-sch-B", "Washroom-sch-C", "Washroom-sch-D", "Washroom-sch-E", # "Washroom-sch-F", "Washroom-sch-G", "Washroom-sch-H", "Washroom-sch-I", "Dress./fitt. - performance arts", # "Locker room", "Retail - dressing/fitting","Locker room-sch-A","Locker room-sch-B","Locker room-sch-C", # "Locker room-sch-D","Locker room-sch-E","Locker room-sch-F","Locker room-sch-G","Locker room-sch-H", # "Locker room-sch-I", "Office - open plan - occsens", "Office - enclosed - occsens", "Storage area - occsens", # "Hospital - medical supply - occsens", "Storage area - refrigerated - occsens"] # if reduceLPDSpaces.include?(space_type) # row[3] = row[3]*0.9 # puts "\n============================\nspace_type: #{space_type}\n============================\n" # end # # Start of Space Compliance # necb_section_name = "NECB2011-Section 8.4.3.6" # data = {} # data[:lighting_per_area] = [ row[3],'==',space[:lighting_w_per_m2] , "Table 4.2.1.6" ,1 ] unless space[:lighting_w_per_m2].nil? # data[:occupancy_per_area] = [ row[4],'==',space[:occ_per_m2] , "Table A-8.4.3.3.1" ,3 ] unless space[:occ_per_m2].nil? # data[:occupancy_schedule] = [ row[5],'==',space[:occupancy_schedule], "Table A-8.4.3.3.1" ,nil ] unless space[:occupancy_schedule].nil? # data[:electric_equipment_per_area] = [ row[6],'==',space[:electric_w_per_m2] , "Table A-8.4.3.3.1" ,1 ] unless space[:electric_w_per_m2].nil? # data.each do |key,value| # #puts key # necb_section_test( # qaqc, # value[0], # value[1], # value[2], # value[3], # "[SPACE][#{space[:name]}]-[TYPE:][#{space_type}]#{key}", # value[4] # ) # end # end#space Compliance end # Padmassun's Code End end |
#necb_vav_fan_power_compliance(qaqc) ⇒ Object
1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1788 def necb_vav_fan_power_compliance(qaqc) necb_section_name = get_qaqc_table(table_name: 'vav_fan_power_compliance')['refs'].join(',') qaqc_table = get_qaqc_table(table_name: 'vav_fan_power_compliance') # necb_section_name = "NECB2011-5.2.3.3" qaqc[:air_loops].each do |air_loop_info| # necb_clg_cop = air_loop_info[:cooling_coils][:dx_single_speed][:cop] #*assuming that the cop is defined correctly* if air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s].nil? qaqc[:warnings] << '[vav_fan_power_compliance] air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] is nil' next end max_air_flow_rate_m3_per_s = air_loop_info[:supply_fan][:max_air_flow_rate_m3_per_s] necb_supply_fan_w = -1 if air_loop_info[:name].include? 'PSZ' necb_supply_fan_w = eval(qaqc_table['formulas']['NECB PSZ fan power (W)']).round(2) elsif air_loop_info[:name].include? 'VAV' necb_supply_fan_w = eval(qaqc_table['formulas']['NECB VAV fan power (W)']).round(2) end if air_loop_info[:supply_fan][:rated_electric_power_w].nil? qaqc[:warnings] << '[vav_fan_power_compliance] air_loop_info[:supply_fan][:rated_electric_power_w] is nil' next end supply_fan_w = (air_loop_info[:supply_fan][:rated_electric_power_w]).round(3) absolute_diff = (necb_supply_fan_w - supply_fan_w).to_f.abs if absolute_diff < 10 # This case should ALWAYS PASS necb_section_test( qaqc, 10, '>=', absolute_diff, necb_section_name, "[AIR LOOP][#{air_loop_info[:name]}][:supply_fan][:rated_electric_power_w] [#{supply_fan_w}] Absolute Difference from NECB value [#{necb_supply_fan_w}]" ) next else # The test should pass if and only if the percent difference is less than 10% percent_diff = ((necb_supply_fan_w - supply_fan_w).to_f.abs / necb_supply_fan_w * 100).round(3) necb_section_test( qaqc, 10, '>=', percent_diff, necb_section_name, "[AIR LOOP][#{air_loop_info[:name]}][:supply_fan][:rated_electric_power_w] [#{supply_fan_w}] Percent Diff from NECB value [#{necb_supply_fan_w}]" ) end end end |
#necb_zone_sizing_compliance(qaqc) ⇒ Object
1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1418 def necb_zone_sizing_compliance(qaqc) # Zone Sizing test necb_section_name = get_qaqc_table(table_name: 'zone_sizing_compliance')['refs'].join(',') qaqc_table = get_qaqc_table(table_name: 'zone_sizing_compliance') tolerance = 3 # necb_section_name = "NECB2011-?" # round_precision = 3 qaqc[:thermal_zones].each do |zoneinfo| # skipping undefined schedules if (qaqc_table['exclude']['exclude_string'].any? { |ex_string| zoneinfo[:name].to_s.include? ex_string }) && !qaqc_table['exclude']['exclude_string'].empty? # if zoneinfo[:name].to_s.include?"- undefined -" puts "#{zoneinfo[:name]} was skipped in necb_zone_sizing_compliance because it contains #{qaqc_table['exclude']['exclude_string'].join(',')}" next end zone_sizing_compliance = qaqc_table['table'] zone_sizing_compliance.each do |compliance| eval_string = "zoneinfo[:#{compliance['var']}]" result_value = eval(eval_string) next if result_value.nil? test_text = "[ZONE][#{zoneinfo[:name]}] #{compliance['var']}" # puts key necb_section_test( qaqc, result_value, compliance['bool_operator'], compliance['expected_value'], necb_section_name, test_text, tolerance ) end # data = {} # data[:heating_sizing_factor] = [1.3 , zoneinfo[:heating_sizing_factor]] # data[:cooling_sizing_factor] = [1.1 ,zoneinfo[:cooling_sizing_factor]] # #data[:heating_design_supply_air_temp] = [43.0, zoneinfo[:zone_heating_design_supply_air_temperature] ] #unless zoneinfo[:zone_heating_design_supply_air_temperature].nil? # #data[:cooling_design_supply_temp] = [13.0, zoneinfo[:zone_cooling_design_supply_air_temperature] ] # data.each do |key,value| # #puts key # necb_section_test( # qaqc, # value[0], # '==', # value[1], # necb_section_name, # "[ZONE][#{zoneinfo[:name]}] #{key}", # round_precision # ) # end end end |
#new_add_sys6_multi_zone_built_up_system_with_baseboard_heating(model:, zones:, heating_coil_type:, baseboard_type:, chiller_type:, fan_type:, hw_loop:) ⇒ Object
163 164 165 166 167 168 169 170 171 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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_system_6.rb', line 163 def new_add_sys6_multi_zone_built_up_system_with_baseboard_heating(model:, zones:, heating_coil_type:, baseboard_type:, chiller_type:, fan_type:, hw_loop:) # System Type 6: VAV w/ Reheat # This measure creates: # a single hot water loop with a natural gas or electric boiler or for the building # a single chilled water loop with water cooled chiller for the building # a single condenser water loop for heat rejection from the chiller # a VAV system w/ hot water or electric heating, chilled water cooling, and # hot water or electric reheat for each story of the building # Arguments: # "boiler_fueltype" choices match OS choices for boiler fuel type: # "NaturalGas","Electricity","PropaneGas","FuelOilNo1","FuelOilNo2","Coal","Diesel","Gasoline","OtherFuel1" # "heating_coil_type": "Electric" or "Hot Water" # "baseboard_type": "Electric" and "Hot Water" # "chiller_type": "Scroll";"Centrifugal";""Screw";"Reciprocating" # "fan_type": "AF_or_BI_rdg_fancurve";"AF_or_BI_inletvanes";"fc_inletvanes";"var_speed_drive" system_6_data = {} system_6_data[:name] = 'Sys_6_VAV with Reheat' system_6_data[:CentralCoolingDesignSupplyAirTemperature] = 13.0 system_6_data[:CentralHeatingDesignSupplyAirTemperature] = 43.0 system_6_data[:AllOutdoorAirinCooling] = false system_6_data[:AllOutdoorAirinHeating] = false system_6_data[:MinimumSystemAirFlowRatio] = 0.03 # zone data system_6_data[:system_supply_air_temperature] = 13.0 system_6_data[:ZoneCoolingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_6_data[:ZoneCoolingDesignSupplyAirTemperatureDifference] = 11.0 system_6_data[:ZoneHeatingDesignSupplyAirTemperatureInputMethod] = 'TemperatureDifference' system_6_data[:ZoneHeatingDesignSupplyAirTemperatureDifference] = 21.0 system_6_data[:ZoneCoolingSizingFactor] = 1.1 system_6_data[:ZoneHeatingSizingFactor] = 1.3 system_6_data[:ZoneVAVMinFlowFactorPerFloorArea] = 0.002 system_6_data[:ZoneVAVMaxReheatTemp] = 43.0 system_6_data[:ZoneVAVDamperAction] = 'Normal' system_data = system_6_data always_on = model.alwaysOnDiscreteSchedule # Chilled Water Plant chw_loop = OpenStudio::Model::PlantLoop.new(model) chiller1, chiller2 = setup_chw_loop_with_components(model, chw_loop, chiller_type) # Condenser System cw_loop = OpenStudio::Model::PlantLoop.new(model) ctower = setup_cw_loop_with_components(model, cw_loop, chiller1, chiller2) # Make a Packaged VAV w/ PFP Boxes for each story of the building model.getBuildingStorys.sort.each do |story| unless (OpenstudioStandards::Geometry.building_story_get_thermal_zones(story) & zones).empty? air_loop = common_air_loop(model: model, system_data: system_data) air_loop.setName(system_data[:name]) air_loop_sizing = air_loop.sizingSystem air_loop_sizing.setCentralCoolingDesignSupplyAirTemperature(system_data[:CentralCoolingDesignSupplyAirTemperature]) air_loop_sizing.setCentralHeatingDesignSupplyAirTemperature(system_data[:CentralHeatingDesignSupplyAirTemperature]) air_loop_sizing.setAllOutdoorAirinCooling(system_data[:AllOutdoorAirinCooling]) air_loop_sizing.setAllOutdoorAirinHeating(system_data[:AllOutdoorAirinHeating]) if model.version < OpenStudio::VersionString.new('2.7.0') air_loop_sizing.setMinimumSystemAirFlowRatio(system_data[:MinimumSystemAirFlowRatio]) else air_loop_sizing.setCentralHeatingMaximumSystemAirFlowRatio(system_data[:MinimumSystemAirFlowRatio]) end supply_fan = OpenStudio::Model::FanVariableVolume.new(model, always_on) supply_fan.setName('Sys6 Supply Fan') return_fan = OpenStudio::Model::FanVariableVolume.new(model, always_on) return_fan.setName('Sys6 Return Fan') if heating_coil_type == 'Hot Water' htg_coil = OpenStudio::Model::CoilHeatingWater.new(model, always_on) hw_loop.addDemandBranchForComponent(htg_coil) end if heating_coil_type == 'Electric' htg_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) end clg_coil = OpenStudio::Model::CoilCoolingWater.new(model, always_on) chw_loop.addDemandBranchForComponent(clg_coil) oa_controller = OpenStudio::Model::ControllerOutdoorAir.new(model) oa_controller.autosizeMinimumOutdoorAirFlowRate # Set mechanical ventilation controller outdoor air to ZoneSum (used to be defaulted to ZoneSum but now should be # set explicitly) oa_controller.controllerMechanicalVentilation.setSystemOutdoorAirMethod('ZoneSum') oa_system = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, oa_controller) # Add the components to the air loop # in order from closest to zone to furthest from zone supply_inlet_node = air_loop.supplyInletNode supply_outlet_node = air_loop.supplyOutletNode supply_fan.addToNode(supply_inlet_node) htg_coil.addToNode(supply_inlet_node) clg_coil.addToNode(supply_inlet_node) oa_system.addToNode(supply_inlet_node) returnAirNode = oa_system.returnAirModelObject.get.to_Node.get return_fan.addToNode(returnAirNode) # Add a setpoint manager to control the # supply air to a constant temperature sat_sch = OpenStudio::Model::ScheduleRuleset.new(model) sat_sch.setName('Supply Air Temp') sat_sch.defaultDaySchedule.setName('Supply Air Temp Default') sat_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), system_data[:system_supply_air_temperature]) sat_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, sat_sch) sat_stpt_manager.addToNode(supply_outlet_node) # Make a VAV terminal with HW reheat for each zone on this story that is in intersection with the zones array. # and hook the reheat coil to the HW loop (OpenstudioStandards::Geometry.building_story_get_thermal_zones(story) & zones).each do |zone| # Zone sizing parameters sizing_zone = zone.sizingZone sizing_zone.setZoneCoolingDesignSupplyAirTemperature(system_data[:ZoneCoolingDesignSupplyAirTemperature]) sizing_zone.setZoneHeatingDesignSupplyAirTemperature(system_data[:ZoneHeatingDesignSupplyAirTemperature]) sizing_zone.setZoneCoolingSizingFactor(system_data[:ZoneCoolingSizingFactor]) sizing_zone.setZoneHeatingSizingFactor(system_data[:ZoneHeatingSizingFactor]) if heating_coil_type == 'Hot Water' reheat_coil = OpenStudio::Model::CoilHeatingWater.new(model, always_on) hw_loop.addDemandBranchForComponent(reheat_coil) elsif heating_coil_type == 'Electric' reheat_coil = OpenStudio::Model::CoilHeatingElectric.new(model, always_on) end vav_terminal = OpenStudio::Model::AirTerminalSingleDuctVAVReheat.new(model, always_on, reheat_coil) air_loop.addBranchForZone(zone, vav_terminal.to_StraightComponent) # NECB2011 minimum zone airflow setting vav_terminal.setFixedMinimumAirFlowRate(system_data[:ZoneVAVMinFlowFactorPerFloorArea] * zone.floorArea) vav_terminal.setMaximumReheatAirTemperature(system_data[:ZoneVAVMaxReheatTemp]) vav_terminal.setDamperHeatingAction(system_data[:ZoneVAVDamperAction]) # Set zone baseboards add_zone_baseboards(model: model, zone: zone, baseboard_type: baseboard_type, hw_loop: hw_loop) end end # next story end # for debugging # puts "end add_sys6_multi_zone_built_up_with_baseboard_heating" return true end |
#percentage_difference(value_1, value_2) ⇒ Object
Math fundtion to determine percent difference.
721 722 723 724 725 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 721 def percentage_difference(value_1, value_2) return 0.0 if value_1 == value_2 return ((value_1 - value_2).abs / ((value_1 + value_2) / 2) * 100) end |
#pump_standard_minimum_motor_efficiency_and_size(pump, motor_bhp) ⇒ Array<Double>
Determines the minimum pump motor efficiency and nominal size for a given motor bhp. This should be the total brake horsepower with any desired safety factor already included. This method picks the next nominal motor catgory larger than the required brake horsepower, and the efficiency is based on that size. For example, if the bhp = 6.3, the nominal size will be 7.5HP and the efficiency for 90.1-2010 will be 91.7% from Table 10.8B. This method assumes 4-pole, 1800rpm totally-enclosed fan-cooled motors.
1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1459 def pump_standard_minimum_motor_efficiency_and_size(pump, motor_bhp) motor_eff = 0.85 nominal_hp = motor_bhp # Don't attempt to look up motor efficiency # for zero-hp pumps (required for circulation-pump-free # service water heating systems). return [1.0, 0] if motor_bhp == 0.0 # Lookup the minimum motor efficiency motors = @standards_data['motors'] # Assuming all pump motors are 4-pole ODP search_criteria = { 'motor_use' => 'PUMP', 'number_of_poles' => 4.0, 'type' => 'Enclosed' } motor_properties = model_find_object(motors, search_criteria, motor_bhp) if motor_properties.nil? OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Pump', "For #{pump.name}, could not find motor properties using search criteria: #{search_criteria}, motor_bhp = #{motor_bhp} hp.") return [motor_eff, nominal_hp] end motor_eff = motor_properties['nominal_full_load_efficiency'] nominal_hp = motor_properties['maximum_capacity'].to_f.round(1) # Round to nearest whole HP for niceness if nominal_hp >= 2 nominal_hp = nominal_hp.round end # Get the efficiency based on the nominal horsepower # Add 0.01 hp to avoid search errors. motor_properties = model_find_object(motors, search_criteria, nominal_hp + 0.01) if motor_properties.nil? OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Fan', "For #{pump.name}, could not find nominal motor properties using search criteria: #{search_criteria}, motor_hp = #{nominal_hp} hp.") return [motor_eff, nominal_hp] end motor_eff = motor_properties['nominal_full_load_efficiency'] return [motor_eff, nominal_hp] end |
#pump_variable_speed_control_type(pump) ⇒ Boolean
Determine and set type of part load control type for heating and chilled water variable speed pumps
1539 1540 1541 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1539 def pump_variable_speed_control_type(pump) return false end |
#qaqc_only(model) ⇒ Object
generates only qaqc component
168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 168 def qaqc_only(model) # load the qaqc.json files @qaqc_data = load_qaqc_database_new # generate base qaqc hash qaqc = create_base_data(model) # performs the qaqc on the given base qaqc hash. # using `qaqc.clone` as an argument to pass in a shallow copy, so that the argument passed can stay unmodified. necb_qaqc_with_base = necb_qaqc(qaqc.clone, model) # subract base data from qaqc return (necb_qaqc_with_base.to_a - qaqc.to_a).to_h end |
#replace_massless_material_with_std_material(model, surf) ⇒ Object
Loop through the layers of the construction of the surface and replace any massless material with a standard one. The material used instead is from the EnergyPlus dataset file ‘ASHRAE_2005_HOF_Materials.idf’ with the name: ‘Insulation: Expanded polystyrene - extruded (smooth skin surface) (HCFC-142b exp.)’. The thickness of the new material is based on the thermal resistance of the massless material it replaces. created by: Kamel Haddad ([email protected])
861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 861 def replace_massless_material_with_std_material(model,surf) std_const_name = "#{surf.construction.get.name.to_s}_std" std_const = model.getLayeredConstructions.select {|const| const.name.to_s == std_const_name} new_const = nil if !std_const.empty? new_const = std_const[0] else new_layers = {} has_massless_mat = false layer_index = 0 surf.construction.get.to_LayeredConstruction.get.layers.each do |layer| if layer.to_MasslessOpaqueMaterial.is_initialized then has_massless_mat = true new_mat = OpenStudio::Model::StandardOpaqueMaterial.new(model) new_mat.setName("Expanded Polystyrene") new_mat.setThermalConductivity(0.029) new_mat.setDensity(29.0) new_mat.setSpecificHeat(1210.0) new_mat.setRoughness('MediumSmooth') new_mat.setThickness(layer.to_MasslessOpaqueMaterial.get.thermalResistance.to_f * new_mat.thermalConductivity.to_f) else new_mat = layer end new_layers[layer_index] = new_mat layer_index += 1 end if has_massless_mat new_const = OpenStudio::Model::Construction.new(model) new_layers.keys.sort.each {|layer_index| new_const.to_LayeredConstruction.get.insertLayer(layer_index,new_layers[layer_index])} new_const.setName("#{surf.construction.get.name.to_s}_std") end end surf.setConstruction(new_const) if !new_const.nil? end |
#sanity_check(qaqc) ⇒ Object
Checks if a space with a proper schedule is conditioned or not
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb', line 1002 def sanity_check(qaqc) qaqc[:sanity_check] = {} qaqc[:sanity_check][:fail] = [] qaqc[:sanity_check][:pass] = [] # Padmassun's code for isConditioned start qaqc[:thermal_zones].each do |zoneinfo| zoneinfo[:spaces].each do |space| # skip plenums and undefined spaces/zones if zoneinfo[:name].to_s.include? '- undefined -' next end if zoneinfo[:space_type_name].to_s.include? 'Space Function - undefined -' if zoneinfo[:is_conditioned].to_s == 'No' qaqc[:sanity_check][:pass] << "[TEST-PASS][SANITY_CHECK-PASS] for [SPACE][#{space[:name]}] and [THERMAL ZONE] [#{zoneinfo[:name]}] where isConditioned is supposed to be [" 'No' "] and found as #{zoneinfo[:is_conditioned]}" else qaqc[:sanity_check][:fail] << "[ERROR][SANITY_CHECK-FAIL] for [SPACE][#{space[:name]}] and [THERMAL ZONE] [#{zoneinfo[:name]}] where isConditioned is supposed to be [" 'No' "] but found as #{zoneinfo[:is_conditioned]}" end else if zoneinfo[:is_conditioned].to_s == 'Yes' qaqc[:sanity_check][:pass] << "[TEST-PASS][SANITY_CHECK-PASS] for [SPACE][#{space[:name]}] and [THERMAL ZONE] [#{zoneinfo[:name]}] where isConditioned is supposed to be [" 'Yes' "] and found as #{zoneinfo[:is_conditioned]}" elsif zoneinfo[:name] qaqc[:sanity_check][:fail] << "[ERROR][SANITY_CHECK-FAIL] for [SPACE][#{space[:name]}] and [THERMAL ZONE] [#{zoneinfo[:name]}] where isConditioned is supposed to be [" 'Yes' "] but found as #{zoneinfo[:is_conditioned]}" end end end end qaqc[:sanity_check][:fail] = qaqc[:sanity_check][:fail].sort qaqc[:sanity_check][:pass] = qaqc[:sanity_check][:pass].sort # Padmassun's code for isConditioned end end |
#scale_model_geometry(model, x_scale, y_scale, z_scale) ⇒ Object
631 632 633 634 635 636 637 638 639 640 641 642 643 644 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 631 def scale_model_geometry(model, x_scale, y_scale, z_scale) # Identity matrix for setting space origins m = OpenStudio::Matrix.new(4, 4, 0) m[0, 0] = 1.0 / x_scale m[1, 1] = 1.0 / y_scale m[2, 2] = 1.0 / z_scale m[3, 3] = 1.0 t = OpenStudio::Transformation.new(m) model.getPlanarSurfaceGroups.each do |planar_surface| planar_surface.changeTransformation(t) end return model end |
#set_boiler_cap_ratios(boiler_cap_ratio:, boiler_fuel:) ⇒ Object
This method expects a string with the following pattern: number-number The first number is the percent of the total capacity that the primary boiler’s capacity will be set to. The second number is the percent of the total capacity that the secondary boiler’s capacity will be set to. If no boiler_cap_ratio is provided the the primary boiler will have its capacity set to 75% of the total and the secondary boiler will have its capacity set to 25% of the total. If a boiler_cap_ratio is set to ‘0_0’ then the NECB default capacities are assigned.
2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 2450 def set_boiler_cap_ratios(boiler_cap_ratio:, boiler_fuel:) # Rules if boiler_fuel is defined unless boiler_fuel.nil? # Set the NECB default boiler capacities if the boiler_cap_ratio is set to '0-0' if boiler_cap_ratio == '0-0' boiler_cap_ratios = { primary_ratio: nil, secondary_ratio: nil } return boiler_cap_ratios elsif !boiler_fuel.to_s.downcase.include?('backup') && boiler_cap_ratio.nil? # Set the NECB default boiler capacities if the boiler_cap_ratio in not defined and the boiler fuel type is set # and the primary and secondary fuel types are the same. boiler_cap_ratios = { primary_ratio: nil, secondary_ratio: nil } return boiler_cap_ratios end end # Assuming the above rules do not apply, set the default boiler capacity ratio to 75% for the primary boiler and 25% # for the secondary boiler if boiler_cap_ratio.nil? boiler_cap_ratios = { primary_ratio: 0.75, secondary_ratio: 0.25 } return boiler_cap_ratios end # If you defined the boiler capacity ratios set them accordingly. # Split the capacity ratio using the '-' symbol string_ratios = boiler_cap_ratio.to_s.split('-') # Turn the percentages into fractions primary_ratio = string_ratios[0].to_f/100.0 secondary_ratio = string_ratios[1].to_f/100.0 # Set the hash containg the ratios and return boiler_cap_ratios = { primary_ratio: primary_ratio, secondary_ratio: secondary_ratio } return boiler_cap_ratios end |
#set_lighting_per_area(space_type:, definition:, lighting_per_area:, lights_scale:) ⇒ Object
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/lighting.rb', line 146 def set_lighting_per_area(space_type:, definition:, lighting_per_area:, lights_scale:) occ_sens_lpd_frac = 1.0 # NECB2011 space types that require a reduction in the LPD to account for # the requirement of an occupancy sensor (8.4.4.6(3) and 4.2.2.2(2)) reduce_lpd_spaces = ['Classroom/lecture/training', 'Conf./meet./multi-purpose', 'Lounge/recreation', 'Conf./meet./multi-purpose', 'Washroom-sch-A', 'Washroom-sch-B', 'Washroom-sch-C', 'Washroom-sch-D', 'Washroom-sch-E', 'Washroom-sch-F', 'Washroom-sch-G', 'Washroom-sch-H', 'Washroom-sch-I', 'Dress./fitt. - performance arts', 'Locker room', 'Locker room-sch-A', 'Locker room-sch-B', 'Locker room-sch-C', 'Locker room-sch-D', 'Locker room-sch-E', 'Locker room-sch-F', 'Locker room-sch-G', 'Locker room-sch-H', 'Locker room-sch-I', 'Retail - dressing/fitting'] if reduce_lpd_spaces.include?(space_type.standardsSpaceType.get) # Note that "Storage area", "Storage area - refrigerated", "Hospital - medical supply" and "Office - enclosed" # LPD should only be reduced if their space areas are less than specific area values. # This is checked in a space loop after this function in the calling routine. occ_sens_lpd_frac = 0.9 end definition.setWattsperSpaceFloorArea(OpenStudio.convert(lighting_per_area.to_f * occ_sens_lpd_frac * lights_scale, 'W/ft^2', 'W/m^2').get) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} set LPD to #{lighting_per_area} W/ft^2.") end |
#set_lighting_per_area_led_lighting(space_type:, definition:, lighting_per_area_led_lighting:, lights_scale:) ⇒ Object
1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1877 def set_lighting_per_area_led_lighting(space_type:, definition:, lighting_per_area_led_lighting:, lights_scale:) # puts "#{space_type.name.to_s} - 'space_height' - #{space_height.to_s}" occ_sens_lpd_frac = 1.0 # NECB2011 space types that require a reduction in the LPD to account for # the requirement of an occupancy sensor (8.4.4.6(3) and 4.2.2.2(2)) reduce_lpd_spaces = ['Classroom/lecture/training', 'Conf./meet./multi-purpose', 'Lounge/recreation', 'Conf./meet./multi-purpose', 'Washroom-sch-A', 'Washroom-sch-B', 'Washroom-sch-C', 'Washroom-sch-D', 'Washroom-sch-E', 'Washroom-sch-F', 'Washroom-sch-G', 'Washroom-sch-H', 'Washroom-sch-I', 'Dress./fitt. - performance arts', 'Locker room', 'Locker room-sch-A', 'Locker room-sch-B', 'Locker room-sch-C', 'Locker room-sch-D', 'Locker room-sch-E', 'Locker room-sch-F', 'Locker room-sch-G', 'Locker room-sch-H', 'Locker room-sch-I', 'Retail - dressing/fitting'] if reduce_lpd_spaces.include?(space_type.standardsSpaceType.get) # Note that "Storage area", "Storage area - refrigerated", "Hospital - medical supply" and "Office - enclosed" # LPD should only be reduced if their space areas are less than specific area values. # This is checked in a space loop after this function in the calling routine. occ_sens_lpd_frac = 0.9 end # ##### Since Atrium's LPD for LED lighting depends on atrium's height, the height of the atrium (if applicable) should be found. standards_space_type = space_type.standardsSpaceType.is_initialized ? space_type.standardsSpaceType.get : nil if standards_space_type.include? 'Atrium' # @todo Note that since none of the archetypes has Atrium, this was tested for 'Dining'. #Atrium puts "#{standards_space_type} - has atrium" # space_type.name.to_s # Get the max height for the spacetype. max_space_height_for_spacetype = get_max_space_height_for_space_type(space_type: space_type) if max_space_height_for_spacetype < 12.0 # @todo Note that since none of the archetypes has Atrium, this was tested for 'Dining' with the threshold of 5.0 m for space_height. # @todo Regarding the below equations, identify which version of ASHRAE 90.1 was used in NECB2015. atrium_lpd_eq_smaller_12_intercept = 0 atrium_lpd_eq_smaller_12_slope = 1.06 atrium_lpd_eq_larger_12_intercept = 4.3 atrium_lpd_eq_larger_12_slope = 1.06 lighting_per_area_led_lighting_atrium = (atrium_lpd_eq_smaller_12_intercept + atrium_lpd_eq_smaller_12_slope * 12.0) * 0.092903 # W/ft2 @todo Note that for NECB2011, a constant LPD is used for atrium based on NECB2015's equations. NECB2011's threshold for height is 13.0 m. elsif max_space_height_for_spacetype >= 12.0 && max_space_height_for_spacetype < 13.0 lighting_per_area_led_lighting_atrium = (atrium_lpd_eq_larger_12_intercept + atrium_lpd_eq_larger_12_slope * 12.5) * 0.092903 # W/ft2 else # i.e. space_height >= 13.0 lighting_per_area_led_lighting_atrium = (atrium_lpd_eq_larger_12_intercept + atrium_lpd_eq_larger_12_slope * 13.0) * 0.092903 # W/ft2 end puts "#{standards_space_type} - has lighting_per_area_led_lighting_atrium - #{lighting_per_area_led_lighting_atrium}" lighting_per_area_led_lighting = lighting_per_area_led_lighting_atrium end lighting_per_area_led_lighting *= lights_scale definition.setWattsperSpaceFloorArea(OpenStudio.convert(lighting_per_area_led_lighting.to_f * occ_sens_lpd_frac, 'W/ft^2', 'W/m^2').get) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} set LPD to #{lighting_per_area_led_lighting} W/ft^2.") end |
#set_necb_external_subsurface_conductance(subsurface, hdd) ⇒ Object
Set all external subsurfaces (doors, windows, skylights) to NECB values.
450 451 452 453 454 455 456 457 458 459 460 461 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 450 def set_necb_external_subsurface_conductance(subsurface, hdd) conductance_value = 0 return unless subsurface.outsideBoundaryCondition.downcase.match('outdoors') case subsurface.subSurfaceType.downcase when /window/ conductance_value = @standards_data['conductances']['Window'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor when /door/ conductance_value = @standards_data['conductances']['Door'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor end subsurface.setRSI(1 / conductance_value) end |
#set_necb_external_surface_conductance(surface, hdd, is_radiant = false, scaling_factor = 1.0) ⇒ String
Set all external surface conductances to NECB values.
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb', line 412 def set_necb_external_surface_conductance(surface, hdd, is_radiant = false, scaling_factor = 1.0) conductance_value = 0 if surface.outsideBoundaryCondition.casecmp('outdoors').zero? case surface.surfaceType.downcase when 'wall' conductance_value = @standards_data['conductances']['Wall'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor when 'floor' conductance_value = @standards_data['conductances']['Floor'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor when 'roofceiling' conductance_value = @standards_data['conductances']['Roof'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor end if is_radiant conductance_value *= 0.80 end return BTAP::Geometry::Surfaces.set_surfaces_construction_conductance([surface], conductance_value) end return unless surface.outsideBoundaryCondition.downcase =~ /ground/ case surface.surfaceType.downcase when 'wall' conductance_value = @standards_data['conductances']['GroundWall'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor when 'floor' conductance_value = @standards_data['conductances']['GroundFloor'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor when 'roofceiling' conductance_value = @standards_data['conductances']['GroundRoof'].find { |i| i['hdd'] > hdd }['thermal_transmittance'] * scaling_factor end if is_radiant conductance_value *= 0.80 end return BTAP::Geometry::Surfaces.set_surfaces_construction_conductance([surface], conductance_value) end |
#set_occ_sensor_spacetypes(model, space_type_map) ⇒ Boolean
Returns true if successful, false if not
1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1272 def set_occ_sensor_spacetypes(model, space_type_map) building_type = 'Space Function' space_type_map.each do |space_type_name, space_names| space_names.sort.each do |space_name| space = model.getSpaceByName(space_name) next if space.empty? space = space.get # Check if space type for this space matches NECB2011 specific space type # for occupancy sensor that is area dependent. Note: space.floorArea in m2. # Evaluate the formula in the database. standard_space_type_name = space_type_name floor_area = space.floorArea if eval(@standards_data['formulas']['occupancy_sensors_space_types_formula']['value']) # If there is only one space assigned to this space type, then reassign this stub # to the @@template duplicate with appendage " - occsens", otherwise create a new stub # for this space. Required to use reduced LPD by NECB2011 0.9 factor. space_type_name_occsens = space_type_name + ' - occsens' stub_space_type_occsens = model.getSpaceTypeByName("#{building_type} #{space_type_name_occsens}") if stub_space_type_occsens.empty? # create a new space type just once for space_type_name appended with " - occsens" stub_space_type_occsens = OpenStudio::Model::SpaceType.new(model) stub_space_type_occsens.setStandardsBuildingType(building_type) stub_space_type_occsens.setStandardsSpaceType(space_type_name_occsens) stub_space_type_occsens.setName("#{building_type} #{space_type_name_occsens}") space_type_apply_rendering_color(stub_space_type_occsens) space.setSpaceType(stub_space_type_occsens) else # reassign occsens space type stub already created... stub_space_type_occsens = stub_space_type_occsens.get space.setSpaceType(stub_space_type_occsens) end end end end return true end |
#set_output_meters(model:, output_meters:) ⇒ Object
2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 2238 def set_output_meters(model:,output_meters:) unless output_meters.nil? or output_meters.empty? # remove existing output meters existing_meters = model.getOutputMeters # OpenStudio doesn't seemt to like two meters of the same name, even if they have different reporting frequencies. output_meters.each do |new_meter| #check if meter already exists result = existing_meters.select { |e_m| e_m.name == new_meter['name'] } puts("More and one output meter named #{new_meter['name']}") if result.size > 1 if result.size >= 1 existing_meter = result[0] puts("A meter named #{new_meter['name']} already exists. One will not be added to the model.") if existing_meter.reportingFrequency != new_meter['frequency'] existing_meter.setReportingFrequency(new_meter['frequency']) puts("Changing reporting frequency of existing meter to #{new_meter['frequency']}.") end end if result.size == 0 meter = OpenStudio::Model::OutputMeter.new(model) meter.setName(new_meter['name']) meter.setReportingFrequency(new_meter['frequency']) puts("Adding meter for #{meter.name} reporting #{new_meter['frequency']}") end end end return model end |
#set_output_variables(model:, output_variables:) ⇒ Object
2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 2224 def set_output_variables(model:,output_variables:) unless output_variables.nil? or output_variables.empty? output_variables.each do |output_variable| puts output_variable puts output_variable['frequency'] raise("Frequency is not valid. Must by \"hourly\" or \"timestep\" but got #{output_variable}.") unless ["timestep","hourly",'daily','monthly','annual'].include?(output_variable['frequency']) output = OpenStudio::Model::OutputVariable.new(output_variable['variable'],model) output.setKeyValue(output_variable['key']) output.setReportingFrequency(output_variable['frequency']) end end return model end |
#set_random_rendering_color(object, random) ⇒ Object
This method will create a color object used in SU, 3D Viewer and Floorspace.js
1263 1264 1265 1266 1267 1268 1269 1270 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 1263 def set_random_rendering_color(object, random) rendering_color = OpenStudio::Model::RenderingColor.new(object.model) rendering_color.setName(object.name.get) rendering_color.setRenderingRedValue(random.rand(255)) rendering_color.setRenderingGreenValue(random.rand(255)) rendering_color.setRenderingBlueValue(random.rand(255)) return rendering_color end |
#set_wildcard_schedules_to_dominant_building_schedule(model, runner = nil) ⇒ Object
763 764 765 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 763 def set_wildcard_schedules_to_dominant_building_schedule(model, runner = nil) # Get rid of. end |
#set_zones_thermostat_schedule_based_on_space_type_schedules(model, runner = nil) ⇒ Object
1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1591 def set_zones_thermostat_schedule_based_on_space_type_schedules(model, runner = nil) puts 'in set_zones_thermostat_schedule_based_on_space_type_schedules' BTAP.runner_register('DEBUG', 'Start-set_zones_thermostat_schedule_based_on_space_type_schedules', runner) model.getThermalZones.sort.each do |zone| BTAP.runner_register('DEBUG', "Zone = #{zone.name} Spaces =#{zone.spaces.size} ", runner) array = [] zone.spaces.sort.each do |space| schedule_type = determine_necb_schedule_type(space).to_s BTAP.runner_register('DEBUG', "space name/type:#{space.name}/#{schedule_type}", runner) # if wildcard space type, need to get dominant schedule type if '*'.to_s == schedule_type dominant_sched_type = determine_dominant_necb_schedule_type(model) schedule_type = dominant_sched_type end array << schedule_type end array.uniq! if array.size > 1 BTAP.runner_register('Error', "#{zone.name} has spaces with different schedule types. Please ensure that all the spaces are of the same schedule type A to I.", runner) return false end htg_search_string = "NECB-#{array[0]}-Thermostat Setpoint-Heating" clg_search_string = "NECB-#{array[0]}-Thermostat Setpoint-Cooling" if model.getScheduleRulesetByName(htg_search_string).empty? == false htg_sched = model.getScheduleRulesetByName(htg_search_string).get else BTAP.runner_register('ERROR', "heating_thermostat_setpoint_schedule NECB-#{array[0]} does not exist", runner) return false end if model.getScheduleRulesetByName(clg_search_string).empty? == false clg_sched = model.getScheduleRulesetByName(clg_search_string).get else BTAP.runner_register('ERROR', "cooling_thermostat_setpoint_schedule NECB-#{array[0]} does not exist", runner) return false end name = "NECB-#{array[0]}-Thermostat Dual Setpoint Schedule" # If dual setpoint already exists, use that one, else create one ds = if model.getThermostatSetpointDualSetpointByName(name).empty? == false model.getThermostatSetpointDualSetpointByName(name).get else BTAP::Resources::Schedules.create_annual_thermostat_setpoint_dual_setpoint(model, name, htg_sched, clg_sched) end thermostat_clone = ds.clone.to_ThermostatSetpointDualSetpoint.get zone.setThermostatSetpointDualSetpoint(thermostat_clone) BTAP.runner_register('Info', "ThermalZone #{zone.name} set to DualSetpoint Schedule NECB-#{array[0]}", runner) end BTAP.runner_register('DEBUG', 'END-set_zones_thermostat_schedule_based_on_space_type_schedules', runner) return true end |
#setup_chw_loop_with_components(model, chw_loop, chiller_type) ⇒ Object
of setup_hw_loop_with_components
1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1715 def setup_chw_loop_with_components(model, chw_loop, chiller_type) chw_loop.setName('Chilled Water Loop') sizing_plant = chw_loop.sizingPlant sizing_plant.setLoopType('Cooling') sizing_plant.setDesignLoopExitTemperature(7.0) sizing_plant.setLoopDesignTemperatureDifference(6.0) # Note: pump of 'chilled water loop' has been changed to the variable one as the constant one caused fatal errors for LargeOffice-Yellowknife-NaturalGas for some ECMs and inputs. # Fatal error was: 'CheckForRunawayPlantTemps: Simulation terminated because of run away plant temperatures, too cold' OR '..., too hot' for the PlantLoop of 'Chilled Water Loop'. # Note that the variable speed pump has been already used for 'Hot Water Loop'. chw_pump = OpenStudio::Model::PumpVariableSpeed.new(model) chiller1 = OpenStudio::Model::ChillerElectricEIR.new(model) chiller2 = OpenStudio::Model::ChillerElectricEIR.new(model) chiller1.setCondenserType('WaterCooled') chiller2.setCondenserType('WaterCooled') chiller1_name = "Primary Chiller WaterCooled #{chiller_type}".strip chiller1.setName(chiller1_name) chiller2_name = "Secondary Chiller WaterCooled #{chiller_type}".strip chiller2.setName(chiller2_name) chiller_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model) chw_supply_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model) # Add the components to the chilled water loop chw_supply_inlet_node = chw_loop.supplyInletNode chw_supply_outlet_node = chw_loop.supplyOutletNode chw_pump.addToNode(chw_supply_inlet_node) chw_loop.addSupplyBranchForComponent(chiller1) chw_loop.addSupplyBranchForComponent(chiller2) chw_loop.addSupplyBranchForComponent(chiller_bypass_pipe) chw_supply_outlet_pipe.addToNode(chw_supply_outlet_node) # Add a setpoint manager to control the # chilled water to a constant temperature chw_t_c = 7.0 chw_t_sch = BTAP::Resources::Schedules.create_annual_constant_ruleset_schedule(model, 'CHW Temp', 'Temperature', chw_t_c) chw_t_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, chw_t_sch) chw_t_stpt_manager.addToNode(chw_supply_outlet_node) return chiller1, chiller2 end |
#setup_cw_loop_with_components(model, cw_loop, chiller1, chiller2) ⇒ Object
of setup_chw_loop_with_components
1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1761 def setup_cw_loop_with_components(model, cw_loop, chiller1, chiller2) cw_loop.setName('Condenser Water Loop') cw_sizing_plant = cw_loop.sizingPlant cw_sizing_plant.setLoopType('Condenser') cw_sizing_plant.setDesignLoopExitTemperature(29.0) cw_sizing_plant.setLoopDesignTemperatureDifference(6.0) # Note: pump of 'Condenser water loop' has been changed to the variable one as the constant one caused fatal errors for LargeOffice-Montreal-NaturalGas for some ECMs and inputs. # Fatal error was: 'Plant temperatures are getting far too cold, check controls and relative loads and capacities'. # Note that the variable speed pump has been already used for 'Hot Water Loop' and 'Chilled Water Loop'. cw_pump = OpenStudio::Model::PumpVariableSpeed.new(model) clg_tower = OpenStudio::Model::CoolingTowerSingleSpeed.new(model) # TO DO: Need to define and set cooling tower curves clg_tower_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model) cw_supply_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model) # Add the components to the condenser water loop cw_supply_inlet_node = cw_loop.supplyInletNode cw_supply_outlet_node = cw_loop.supplyOutletNode cw_pump.addToNode(cw_supply_inlet_node) clg_tower.setDesignInletAirWetBulbTemperature(24.0) clg_tower.setDesignInletAirDryBulbTemperature(35.0) clg_tower.setDesignApproachTemperature(5.0) clg_tower.setDesignRangeTemperature(6.0) cw_loop.addSupplyBranchForComponent(clg_tower) cw_loop.addSupplyBranchForComponent(clg_tower_bypass_pipe) cw_supply_outlet_pipe.addToNode(cw_supply_outlet_node) cw_loop.addDemandBranchForComponent(chiller1) cw_loop.addDemandBranchForComponent(chiller2) # Add a setpoint manager to control the # condenser water to constant temperature cw_t_c = 29.0 cw_t_sch = BTAP::Resources::Schedules.create_annual_constant_ruleset_schedule(model, 'CW Temp', 'Temperature', cw_t_c) cw_t_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, cw_t_sch) cw_t_stpt_manager.addToNode(cw_supply_outlet_node) return clg_tower end |
#setup_hw_loop_with_components(model, hw_loop, boiler_fueltype, backup_boiler_fueltype, pump_flow_sch) ⇒ Object
1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1657 def setup_hw_loop_with_components(model, hw_loop, boiler_fueltype, backup_boiler_fueltype, pump_flow_sch) hw_loop.setName('Hot Water Loop') sizing_plant = hw_loop.sizingPlant sizing_plant.setLoopType('Heating') sizing_plant.setDesignLoopExitTemperature(82.0) #@todo units sizing_plant.setLoopDesignTemperatureDifference(16.0) # pump (set to variable speed for now till fix to run away plant temperature is found) # pump = OpenStudio::Model::PumpConstantSpeed.new(model) pump = OpenStudio::Model::PumpVariableSpeed.new(model) # @todo the keyword "setPumpFlowRateSchedule" does not seem to work. A message # was sent to NREL to let them know about this. Once there is a fix for this, # use the proper pump schedule depending on whether we have two-pipe or four-pipe # fan coils. # pump.resetPumpFlowRateSchedule() # pump.setPumpFlowRateSchedule(pump_flow_sch) # boiler boiler1 = OpenStudio::Model::BoilerHotWater.new(model) boiler2 = OpenStudio::Model::BoilerHotWater.new(model) boiler1.setFuelType(boiler_fueltype) boiler2.setFuelType(backup_boiler_fueltype) boiler1.setName('Primary Boiler') boiler2.setName('Secondary Boiler') # boiler_bypass_pipe boiler_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model) # supply_outlet_pipe supply_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model) # Add the components to the hot water loop hw_supply_inlet_node = hw_loop.supplyInletNode hw_supply_outlet_node = hw_loop.supplyOutletNode pump.addToNode(hw_supply_inlet_node) hw_loop.addSupplyBranchForComponent(boiler1) hw_loop.addSupplyBranchForComponent(boiler2) hw_loop.addSupplyBranchForComponent(boiler_bypass_pipe) supply_outlet_pipe.addToNode(hw_supply_outlet_node) # Add a setpoint manager to control the # hot water based on outdoor temperature hw_oareset_stpt_manager = OpenStudio::Model::SetpointManagerOutdoorAirReset.new(model) hw_oareset_stpt_manager.setControlVariable('Temperature') hw_oareset_stpt_manager.setSetpointatOutdoorLowTemperature(82.0) hw_oareset_stpt_manager.setOutdoorLowTemperature(-16.0) hw_oareset_stpt_manager.setSetpointatOutdoorHighTemperature(60.0) hw_oareset_stpt_manager.setOutdoorHighTemperature(0.0) hw_oareset_stpt_manager.addToNode(hw_supply_outlet_node) end |
#space_apply_infiltration_rate(space) ⇒ Double
handle doors and vestibules
Set the infiltration rate for this space to include the impact of air leakage requirements in the standard.
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1207 def space_apply_infiltration_rate(space) # Remove infiltration rates set at the space type. infiltration_data = @standards_data['infiltration'] unless space.spaceType.empty? space.spaceType.get.spaceInfiltrationDesignFlowRates.each(&:remove) end # Remove infiltration rates set at the space object. space.spaceInfiltrationDesignFlowRates.each(&:remove) exterior_wall_and_roof_and_subsurface_area = OpenstudioStandards::Geometry.space_get_exterior_wall_and_subsurface_and_roof_area(space) # To do # Don't create an object if there is no exterior wall area if exterior_wall_and_roof_and_subsurface_area <= 0.0 OpenStudio.logFree(OpenStudio::Info, 'openstudio.Standards.Model', "For #{template}, no exterior wall area was found, no infiltration will be added.") return true end # Calculate the total infiltration, assuming # that it only occurs through exterior walls and roofs (not floors as # explicit stated in the NECB2011 so overhang/cantilevered floors will # have no effective infiltration) tot_infil_m3_per_s = get_standards_constant('infiltration_rate_m3_per_s_per_m2') * exterior_wall_and_roof_and_subsurface_area # Now spread the total infiltration rate over all # exterior surface area (for the E+ input field) this will include the exterior floor if present. all_ext_infil_m3_per_s_per_m2 = tot_infil_m3_per_s / space.exteriorArea OpenStudio.logFree(OpenStudio::Debug, 'openstudio.Standards.Space', "For #{space.name}, adj infil = #{all_ext_infil_m3_per_s_per_m2.round(8)} m^3/s*m^2.") # Get any infiltration schedule already assigned to this space or its space type # If not, the always on schedule will be applied. infil_sch = nil unless space.spaceInfiltrationDesignFlowRates.empty? old_infil = space.spaceInfiltrationDesignFlowRates[0] if old_infil.schedule.is_initialized infil_sch = old_infil.schedule.get end end if infil_sch.nil? && space.spaceType.is_initialized space_type = space.spaceType.get unless space_type.spaceInfiltrationDesignFlowRates.empty? old_infil = space_type.spaceInfiltrationDesignFlowRates[0] if old_infil.schedule.is_initialized infil_sch = old_infil.schedule.get end end end if infil_sch.nil? infil_sch = space.model.alwaysOnDiscreteSchedule end # Create an infiltration rate object for this space infiltration = OpenStudio::Model::SpaceInfiltrationDesignFlowRate.new(space.model) infiltration.setName("#{space.name} Infiltration") infiltration.setFlowperExteriorSurfaceArea(all_ext_infil_m3_per_s_per_m2) infiltration.setSchedule(infil_sch) infiltration.setConstantTermCoefficient(get_standards_constant('infiltration_constant_term_coefficient')) infiltration.setTemperatureTermCoefficient(get_standards_constant('infiltration_constant_term_coefficient')) infiltration.setVelocityTermCoefficient(get_standards_constant('infiltration_velocity_term_coefficient')) infiltration.setVelocitySquaredTermCoefficient(get_standards_constant('infiltration_velocity_squared_term_coefficient')) infiltration.setSpace(space) return true end |
#space_surface_report(space) ⇒ Object
This method gathers the surface information for the space to determine if spaces are the same.
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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 586 def space_surface_report(space) surface_report = [] space_floor_area = space.floorArea ['Outdoors', 'Ground'].each do |bc| surfaces = BTAP::Geometry::Surfaces.filter_by_boundary_condition(space.surfaces, [bc]).each do |surface| # sum wall area and subsurface area by direction. This is the old way so excluding top and bottom surfaces. # new way glazings = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(surface.subSurfaces, ['FixedWindow', 'OperableWindow', 'GlassDoor', 'Skylight', 'TubularDaylightDiffuser', 'TubularDaylightDome']) doors = BTAP::Geometry::Surfaces.filter_subsurfaces_by_types(surface.subSurfaces, ['Door', 'OverheadDoor']) azimuth = (surface.azimuth() * 180.0 / Math::PI) tilt = (surface.tilt() * 180.0 / Math::PI) surface_data = surface_report.detect do |curr_surface_data| curr_surface_data[:surface_type] == surface.surfaceType && curr_surface_data[:azimuth] == azimuth && curr_surface_data[:tilt] == tilt && curr_surface_data[:boundary_condition] == bc end if surface_data.nil? surface_data = { surface_type: surface.surfaceType, azimuth: azimuth, tilt: tilt, boundary_condition: bc, surface_area: 0, surface_area_to_floor_ratio: 0, glazed_subsurface_area: 0, glazed_subsurface_area_to_floor_ratio: 0, opaque_subsurface_area: 0, opaque_subsurface_area_to_floor_ratio: 0 } surface_report << surface_data end surface_data[:surface_area] += surface.grossArea.to_i surface_data[:surface_area_to_floor_ratio] += surface.grossArea / space.floorArea surface_data[:glazed_subsurface_area] += glazings.map { |subsurface| subsurface.grossArea * subsurface.multiplier }.inject(0) { |sum, x| sum + x }.to_i surface_data[:glazed_subsurface_area_to_floor_ratio] += glazings.map { |subsurface| subsurface.grossArea * subsurface.multiplier }.inject(0) { |sum, x| sum + x } / space.floorArea surface_data[:surface_area] += doors.map { |subsurface| subsurface.grossArea * subsurface.multiplier }.inject(0) { |sum, x| sum + x }.to_i surface_data[:surface_area_to_floor_ratio] += doors.map { |subsurface| subsurface.grossArea * subsurface.multiplier }.inject(0) { |sum, x| sum + x } / space.floorArea end end surface_report.sort! { |a, b| [a[:surface_type], a[:azimuth], a[:tilt], a[:boundary_condition]] <=> [b[:surface_type], b[:azimuth], b[:tilt], b[:boundary_condition]] } return surface_report end |
#space_type_apply_internal_loads(space_type:, set_people: true, set_lights: true, set_electric_equipment: true, set_gas_equipment: true, set_ventilation: true, set_infiltration: true, lights_type: 'NECB_Default', lights_scale: 1.0) ⇒ Boolean
Sets the selected internal loads to standards-based or typical values. For each category that is selected get all load instances. Remove all but the first instance if multiple instances. Add a new instance/definition if no instance exists. Modify the definition for the remaining instance to have the specified values. This method does not alter any loads directly assigned to spaces. This method skips plenums.
Also, assign reasonable clothing, air velocity, and work efficiency inputs to allow reasonable thermal comfort metrics to be calculated. to return air, fraction radiant, and fraction visible.
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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/beps_compliance_path.rb', line 47 def space_type_apply_internal_loads(space_type:, set_people: true, set_lights: true, set_electric_equipment: true, set_gas_equipment: true, set_ventilation: true, set_infiltration: true, lights_type: 'NECB_Default', lights_scale: 1.0) # Skip plenums # Check if the space type name # contains the word plenum. if space_type.name.get.to_s.downcase.include?('plenum') return false end if space_type.standardsSpaceType.is_initialized if space_type.standardsSpaceType.get.downcase.include?('plenum') return false end end # Get the space Type data from @standards data spacetype_data = @standards_data['tables']['space_types']['table'] standards_building_type = space_type.standardsBuildingType.is_initialized ? space_type.standardsBuildingType.get : nil standards_space_type = space_type.standardsSpaceType.is_initialized ? space_type.standardsSpaceType.get : nil space_type_properties = spacetype_data.detect { |s| (s['building_type'] == standards_building_type) && (s['space_type'] == standards_space_type) } # Need to add a check, or it'll crash on space_type_properties['occupancy_per_area'].to_f below if space_type_properties.nil? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} was not found in the standards data.") return false end # People people_have_info = false occupancy_per_area = space_type_properties['occupancy_per_area'].to_f people_have_info = true unless occupancy_per_area.zero? if set_people && people_have_info # Remove all but the first instance instances = space_type.people.sort if instances.empty? # Create a new definition and instance definition = OpenStudio::Model::PeopleDefinition.new(space_type.model) definition.setName("#{space_type.name} People Definition") instance = OpenStudio::Model::People.new(definition) instance.setName("#{space_type.name} People") instance.setSpaceType(space_type) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} had no people, one has been created.") instances << instance elsif instances.size > 1 instances.each_with_index do |inst, i| next if i.zero? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "Removed #{inst.name} from #{space_type.name}.") inst.remove end end # Modify the definition of the instance space_type.people.sort.each do |inst| definition = inst.peopleDefinition unless occupancy_per_area.zero? definition.setPeopleperSpaceFloorArea(OpenStudio.convert(occupancy_per_area / 1000, 'people/ft^2', 'people/m^2').get) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} set occupancy to #{occupancy_per_area} people/1000 ft^2.") end # set fraction radiant ## definition.setFractionRadiant(0.3) # Clothing schedule for thermal comfort metrics clothing_sch = space_type.model.getScheduleRulesetByName('Clothing Schedule') if clothing_sch.is_initialized clothing_sch = clothing_sch.get else clothing_sch = OpenStudio::Model::ScheduleRuleset.new(space_type.model) clothing_sch.setName('Clothing Schedule') clothing_sch.defaultDaySchedule.setName('Clothing Schedule Default Winter Clothes') clothing_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1.0) sch_rule = OpenStudio::Model::ScheduleRule.new(clothing_sch) sch_rule.daySchedule.setName('Clothing Schedule Summer Clothes') sch_rule.daySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0.5) sch_rule.setStartDate(OpenStudio::Date.new(OpenStudio::MonthOfYear.new(5), 1)) sch_rule.setEndDate(OpenStudio::Date.new(OpenStudio::MonthOfYear.new(9), 30)) end inst.setClothingInsulationSchedule(clothing_sch) # Air velocity schedule for thermal comfort metrics air_velo_sch = space_type.model.getScheduleRulesetByName('Air Velocity Schedule') if air_velo_sch.is_initialized air_velo_sch = air_velo_sch.get else air_velo_sch = OpenStudio::Model::ScheduleRuleset.new(space_type.model) air_velo_sch.setName('Air Velocity Schedule') air_velo_sch.defaultDaySchedule.setName('Air Velocity Schedule Default') air_velo_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0.2) end inst.setAirVelocitySchedule(air_velo_sch) # Work efficiency schedule for thermal comfort metrics work_efficiency_sch = space_type.model.getScheduleRulesetByName('Work Efficiency Schedule') if work_efficiency_sch.is_initialized work_efficiency_sch = work_efficiency_sch.get else work_efficiency_sch = OpenStudio::Model::ScheduleRuleset.new(space_type.model) work_efficiency_sch.setName('Work Efficiency Schedule') work_efficiency_sch.defaultDaySchedule.setName('Work Efficiency Schedule Default') work_efficiency_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0) end inst.setWorkEfficiencySchedule(work_efficiency_sch) end end # Lights apply_standard_lights(set_lights: set_lights, space_type: space_type, space_type_properties: space_type_properties, lights_type: lights_type, lights_scale: lights_scale) # Electric Equipment elec_equip_have_info = false elec_equip_per_area = space_type_properties['electric_equipment_per_area'].to_f elec_equip_frac_latent = space_type_properties['electric_equipment_fraction_latent'].to_f elec_equip_frac_radiant = space_type_properties['electric_equipment_fraction_radiant'].to_f elec_equip_frac_lost = space_type_properties['electric_equipment_fraction_lost'].to_f elec_equip_have_info = true unless elec_equip_per_area.zero? if set_electric_equipment && elec_equip_have_info # Remove all but the first instance instances = space_type.electricEquipment.sort if instances.empty? definition = OpenStudio::Model::ElectricEquipmentDefinition.new(space_type.model) definition.setName("#{space_type.name} Elec Equip Definition") instance = OpenStudio::Model::ElectricEquipment.new(definition) instance.setName("#{space_type.name} Elec Equip") instance.setSpaceType(space_type) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} had no electric equipment, one has been created.") instances << instance elsif instances.size > 1 instances.each_with_index do |inst, i| next if i.zero? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "Removed #{inst.name} from #{space_type.name}.") inst.remove end end # Modify the definition of the instance space_type.electricEquipment.sort.each do |inst| definition = inst.electricEquipmentDefinition unless elec_equip_per_area.zero? definition.setWattsperSpaceFloorArea(OpenStudio.convert(elec_equip_per_area.to_f, 'W/ft^2', 'W/m^2').get) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} set electric EPD to #{elec_equip_per_area} W/ft^2.") end unless elec_equip_frac_latent.zero? definition.setFractionLatent(elec_equip_frac_latent) end unless elec_equip_frac_radiant.zero? definition.setFractionRadiant(elec_equip_frac_radiant) end unless elec_equip_frac_lost.zero? definition.setFractionLost(elec_equip_frac_lost) end end end # Gas Equipment gas_equip_have_info = false gas_equip_per_area = space_type_properties['gas_equipment_per_area'].to_f gas_equip_frac_latent = space_type_properties['gas_equipment_fraction_latent'].to_f gas_equip_frac_radiant = space_type_properties['gas_equipment_fraction_radiant'].to_f gas_equip_frac_lost = space_type_properties['gas_equipment_fraction_lost'].to_f gas_equip_have_info = true unless gas_equip_per_area.zero? if set_gas_equipment && gas_equip_have_info # Remove all but the first instance instances = space_type.gasEquipment.sort if instances.empty? definition = OpenStudio::Model::GasEquipmentDefinition.new(space_type.model) definition.setName("#{space_type.name} Gas Equip Definition") instance = OpenStudio::Model::GasEquipment.new(definition) instance.setName("#{space_type.name} Gas Equip") instance.setSpaceType(space_type) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} had no gas equipment, one has been created.") instances << instance elsif instances.size > 1 instances.each_with_index do |inst, i| next if i.zero? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "Removed #{inst.name} from #{space_type.name}.") inst.remove end end # Modify the definition of the instance space_type.gasEquipment.sort.each do |inst| definition = inst.gasEquipmentDefinition unless gas_equip_per_area.zero? definition.setWattsperSpaceFloorArea(OpenStudio.convert(gas_equip_per_area.to_f, 'Btu/hr*ft^2', 'W/m^2').get) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} set gas EPD to #{elec_equip_per_area} Btu/hr*ft^2.") end unless gas_equip_frac_latent.zero? definition.setFractionLatent(gas_equip_frac_latent) end unless gas_equip_frac_radiant.zero? definition.setFractionRadiant(gas_equip_frac_radiant) end unless gas_equip_frac_lost.zero? definition.setFractionLost(gas_equip_frac_lost) end end end # Ventilation ventilation_have_info = false ventilation_per_area = space_type_properties['ventilation_per_area'].to_f ventilation_per_person = space_type_properties['ventilation_per_person'].to_f ventilation_ach = space_type_properties['ventilation_air_changes'].to_f ventilation_occupancy_per_area = space_type_properties['ventilation_occupancy_rate_people_per_1000ft2'].to_f ventilation_have_info = true unless ventilation_per_area.zero? ventilation_have_info = true unless ventilation_per_person.zero? ventilation_have_info = true unless ventilation_ach.zero? # Get the design OA or create a new one if none exists ventilation = space_type.designSpecificationOutdoorAir if ventilation.is_initialized ventilation = ventilation.get else ventilation = OpenStudio::Model::DesignSpecificationOutdoorAir.new(space_type.model) ventilation.setName("#{space_type.name} Ventilation") space_type.setDesignSpecificationOutdoorAir(ventilation) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} had no ventilation specification, one has been created.") end if set_ventilation && ventilation_have_info # Modify the ventilation properties ventilation.setOutdoorAirMethod('Sum') unless ventilation_per_area.zero? ventilation.setOutdoorAirFlowperFloorArea(OpenStudio.convert(ventilation_per_area.to_f, 'ft^3/min*ft^2', 'm^3/s*m^2').get) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} set ventilation per area to #{ventilation_per_area} cfm/ft^2.") end unless ventilation_per_person.zero? # For BTAP we often use an occupancy per area rate for ventilation which is different from the one used for # everything else. The mod_ventilation_per_person rate adjusts the per person ventilation rate so that the # proper ventilation rate is calculated when using the general occupant per area rate. mod_ventilation_per_person = ventilation_per_person * ventilation_occupancy_per_area / occupancy_per_area ventilation.setOutdoorAirFlowperPerson(OpenStudio.convert(mod_ventilation_per_person.to_f, 'ft^3/min*person', 'm^3/s*person').get) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} set ventilation per person to #{mod_ventilation_per_person} cfm/person.") end unless ventilation_ach.zero? ventilation.setOutdoorAirFlowAirChangesperHour(ventilation_ach) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} set ventilation to #{ventilation_ach} ACH.") end elsif set_ventilation && !ventilation_have_info # All space types must have a design spec OA # object for ventilation controls to work correctly, # even if the values are all zero. ventilation.setOutdoorAirFlowperFloorArea(0) ventilation.setOutdoorAirFlowperPerson(0) ventilation.setOutdoorAirFlowAirChangesperHour(0) end # Infiltration infiltration_have_info = false infiltration_per_area_ext = space_type_properties['infiltration_per_exterior_area'].to_f infiltration_per_area_ext_wall = space_type_properties['infiltration_per_exterior_wall_area'].to_f infiltration_ach = space_type_properties['infiltration_air_changes'].to_f unless infiltration_per_area_ext.zero? && infiltration_per_area_ext_wall.zero? && infiltration_ach.zero? infiltration_have_info = true end return unless set_infiltration && infiltration_have_info # Remove all but the first instance instances = space_type.spaceInfiltrationDesignFlowRates.sort if instances.empty? instance = OpenStudio::Model::SpaceInfiltrationDesignFlowRate.new(space_type.model) instance.setName("#{space_type.name} Infiltration") instance.setSpaceType(space_type) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} had no infiltration objects, one has been created.") instances << instance elsif instances.size > 1 instances.each_with_index do |inst, i| next if i.zero? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "Removed #{inst.name} from #{space_type.name}.") inst.remove end end # Modify each instance space_type.spaceInfiltrationDesignFlowRates.sort.each do |inst| unless infiltration_per_area_ext.zero? inst.setFlowperExteriorSurfaceArea(OpenStudio.convert(infiltration_per_area_ext.to_f, 'ft^3/min*ft^2', 'm^3/s*m^2').get) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} set infiltration to #{ventilation_ach} per ft^2 exterior surface area.") end unless infiltration_per_area_ext_wall.zero? inst.setFlowperExteriorWallArea(OpenStudio.convert(infiltration_per_area_ext_wall.to_f, 'ft^3/min*ft^2', 'm^3/s*m^2').get) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} set infiltration to #{infiltration_per_area_ext_wall} per ft^2 exterior wall area.") end unless infiltration_ach.zero? inst.setAirChangesperHour(infiltration_ach) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.SpaceType', "#{space_type.name} set infiltration to #{ventilation_ach} ACH.") end end end |
#store_space_sizing_loads(model) ⇒ Object
Method to store space sizing loads. This is needed because later when the zones are destroyed this information will be lost.
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 193 def store_space_sizing_loads(model) @stored_space_heating_sizing_loads = {} @stored_space_cooling_sizing_loads = {} model.getSpaces.sort.each do |space| space_type = space.spaceType.get.standardsSpaceType.get # error if zone design load methods are not available if space.model.version < OpenStudio::VersionString.new('3.6.0') OpenStudio.logFree(OpenStudio::Error, 'openstudio.autozone', "Required ThermalZone methods .autosizedHeatingDesignLoad and .autosizedCoolingDesignLoad are not available in pre-OpenStudio 3.6.0 versions. Use a more recent version of OpenStudio.") end @stored_space_heating_sizing_loads[space] = space_type == '- undefined -' ? 0.0 : space.thermalZone.get.autosizedHeatingDesignLoad.get / space.floorArea @stored_space_cooling_sizing_loads[space] = space_type == '- undefined -' ? 0.0 : space.thermalZone.get.autosizedCoolingDesignLoad.get / space.floorArea end end |
#stored_space_cooling_load(space) ⇒ Object
Returns the cooling load per area for space after sizing runs has been done.
222 223 224 225 226 227 228 229 230 231 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 222 def stored_space_cooling_load(space) if @stored_space_cooling_sizing_loads.nil? # do a sizing run. raise('autorun sizing run failed!') if model_run_sizing_run(space.model, "#{Dir.pwd}/autozone") == false # collect sizing information on each space. store_space_sizing_loads(space.model) end @stored_space_cooling_sizing_loads[space] end |
#stored_space_heating_load(space) ⇒ Object
Returns heating load per area for space after sizing run has been done.
210 211 212 213 214 215 216 217 218 219 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 210 def stored_space_heating_load(space) if @stored_space_heating_sizing_loads.nil? # do a sizing run. raise('autorun sizing run failed!') if model_run_sizing_run(space.model, "#{Dir.pwd}/autozone") == false # collect sizing information on each space. store_space_sizing_loads(space.model) end @stored_space_heating_sizing_loads[space] end |
#stored_zone_cooling_load(zone) ⇒ Object
Returns the cooling load per area for zone after sizing runs has been done.
243 244 245 246 247 248 249 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 243 def stored_zone_cooling_load(zone) total = 0.0 zone.spaces.each do |space| total += stored_space_cooling_load(space) end return total end |
#stored_zone_heating_load(zone) ⇒ Object
# Returns the heating load per area for zone after sizing runs has been done.
234 235 236 237 238 239 240 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/autozone.rb', line 234 def stored_zone_heating_load(zone) total = 0.0 zone.spaces.each do |space| total += stored_space_heating_load(space) end return total end |
#surfaces_are_in_contact?(surf1, surf2) ⇒ Boolean
check if two surfaces are in contact. For every two consecutive vertices on surface 1, loop through two consecutive vertices of surface two. Then check whether the vertices of surfaces 2 are on the same line as the vertices from surface 1. If the two vectors defined by the two vertices on surface 1 and those on surface 2 overlap, then the two surfaces are in contact. If a side from surface 2 is in contact with a side from surface 1, the length of the side from surface 2 is limited to the length of the side from surface 1. created by: Kamel Haddad ([email protected])
820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 820 def surfaces_are_in_contact?(surf1,surf2) surfaces_in_contact = false vert1 = surf1.vertices[0] for index1 in 1..surf1.vertices.size if index1 < surf1.vertices.size vert2 = surf1.vertices[index1] else vert2 = surf1.vertices[0] end seg12_length = ((vert2.x-vert1.x)**2+(vert2.y-vert1.y)**2+(vert2.z-vert1.z)**2)**0.5 surf2_seg_length = 0.0 vert3 = surf2.vertices[0] for index2 in 1..surf2.vertices.size if index2 < surf2.vertices.size vert4 = surf2.vertices[index2] else vert4 = surf2.vertices[0] end vert1_2_3_same_line_and_dir = three_vertices_same_line_and_dir?(vert1,vert2,vert3) if vert1_2_3_same_line_and_dir vert1_2_4_same_line_and_dir = three_vertices_same_line_and_dir?(vert1,vert2,vert4) if vert1_2_4_same_line_and_dir surfaces_in_contact = true seg34_length = ((vert4.x-vert3.x)**2+(vert4.y-vert3.y)**2+(vert4.z-vert3.z)**2)**0.5 surf2_seg_length += seg34_length raise("Surface #{surf2.name.to_s} has sides in contact with surface #{surf1.name.to_s} but with a length greater than the max.") if surf2_seg_length > seg12_length end end vert3 = vert4 end vert1 = vert2 end return surfaces_in_contact end |
#thermal_zone_demand_control_ventilation_required?(thermal_zone, climate_zone) ⇒ Boolean
Add exception logic for 90.1-2013 for cells, sickrooms, labs, barbers, salons, and bowling alleys
Determine if demand control ventilation (DCV) is required for this zone based on area and occupant density. Does not account for System requirements like ERV, economizer, etc. Those are accounted for in the AirLoopHVAC method of the same name.
1519 1520 1521 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1519 def thermal_zone_demand_control_ventilation_required?(thermal_zone, climate_zone) return false end |
#thermal_zone_get_centroid_per_floor(thermal_zone) ⇒ Object
This method cycles through the spaces in a thermal zone and then sorts them by story. The method then cycles through the spaces on a story and then calculates the centroid of the spaces in the thermal zone on that floor. The method returns an array of hashes, one for each story. Each hash has the following structure:
{
story_name: Name of a given story.
spaces: Array containing all of the spaces in the thermal zone on the story in story_name.
centroid: Array containing the x, y, and z coordinates of the centroid of the ceilings of the spaces
listed in 'spaces:' above.
ceiling_area: Total area of the ceilings of the spaces in 'spaces:' above.
}
Only spaces which are conditioned (heated or cooled) and are not plenums are included.
1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 1816 def thermal_zone_get_centroid_per_floor(thermal_zone) stories = [] thermal_zone.spaces.sort.each do |space| spaceType_name = space.spaceType.get.nameString sp_type = spaceType_name[15..-1] # Including regular expressions in the following match for cases where extra characters, which do not belong, are # added to either the space type in the model or the space type reference file. sp_type_info = @standards_data['space_types'].detect do |data| (Regexp.new(data['space_type'].to_s.upcase).match(sp_type.upcase) || Regexp.new(sp_type.upcase).match(data['space_type'].to_s.upcase) || (data['space_type'].to_s.upcase == sp_type.upcase)) && (data['building_type'].to_s == 'Space Function') end if sp_type_info.nil? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.thermal_zone_get_centroid_per_floor', "The space type called #{sp_type} could not be found. Please check that the schedules.json file is available and that the space types are spelled correctly") next end # Determine if space is heated or cooled via spacetype heating or cooling setpoints also checking if the space is # a plenum by checking if there is a hvac system associtated with it if sp_type_info['heating_setpoint_schedule'].nil? heated = false else heated = true end if sp_type_info['cooling_setpoint_schedule'].nil? cooled = false else cooled = true end if (sp_type_info['necb_hvac_system_selection_type'] == '- undefined -') || /undefined/.match(sp_type_info['necb_hvac_system_selection_type']) not_plenum = false else not_plenum = true end # If the spaces are heated or cooled and are not a plenum then continue if (heated || cooled) && not_plenum # Get the story name and sit it to none if there is no story name story_name = space.buildingStory.get.nameString story_name = 'none' if story_name.nil? # If this is the first story in the arry then add a new one. if stories.empty? stories << { story_name: story_name, spaces: [space], centroid: [0, 0, 0], ceiling_area: 0 } next else # If this is not the first story in the array check if the story already is in the array. i = nil stories.each_with_index do |storycheck, index| if storycheck[:story_name] == story_name i = index end end # If the story is not in the array then add it. if i.nil? stories << { story_name: story_name, spaces: [space], centroid: [0, 0, 0], ceiling_area: 0 } else # If the story is already in the arry then add the space to the array of spaces for that story stories[i][:spaces] << space end end end end # Go through each story in the array above stories.each do |story| tz_centre = [0, 0, 0, 0] # Go through each space in a given story story[:spaces].each do |space| # Determine the top surface of the space and calculate it's centroid. # Get the coordinates of the origin for the space (the coordinates of points in the space are relative to this). xOrigin = space.xOrigin yOrigin = space.yOrigin zOrigin = space.zOrigin # Go through each surface in the space and find ceilings by determining which is called 'RoofCeiing'. Find the # overall centroid of all the ceilings in the spaces. Find centroid by multiplying the centroid of the surfaces # multiplied by the area of the surface and add them all up. Then divide this by the overall area. This is the # area weighted average of the centroid coordinates. ceiling_centroid = [0, 0, 0, 0] space.surfaces.each do |sp_surface| if sp_surface.surfaceType.to_s.upcase == 'ROOFCEILING' ceiling_centroid[0] = ceiling_centroid[0] + sp_surface.centroid.x.to_f * sp_surface.grossArea.to_f ceiling_centroid[1] = ceiling_centroid[1] + sp_surface.centroid.y.to_f * sp_surface.grossArea.to_f ceiling_centroid[2] = ceiling_centroid[2] + sp_surface.centroid.z.to_f * sp_surface.grossArea.to_f ceiling_centroid[3] = ceiling_centroid[3] + sp_surface.grossArea end end ceiling_centroid[0] = ceiling_centroid[0] / ceiling_centroid[3] ceiling_centroid[1] = ceiling_centroid[1] / ceiling_centroid[3] ceiling_centroid[2] = ceiling_centroid[2] / ceiling_centroid[3] # This part is used to determine the overall x, y centre of the thermal zone. This is determined by summing the # x and y components times the ceiling area and diving by the total ceiling area. I also added z since the # ceilings may not be all have the same height. tz_centre[0] += (ceiling_centroid[0] + xOrigin) * ceiling_centroid[3] tz_centre[1] += (ceiling_centroid[1] + yOrigin) * ceiling_centroid[3] tz_centre[2] += (ceiling_centroid[2] + zOrigin) * ceiling_centroid[3] tz_centre[3] += (ceiling_centroid[3]) end tz_centre[0] /= tz_centre[3] tz_centre[1] /= tz_centre[3] tz_centre[2] /= tz_centre[3] # Update the :centroid and :ceiling_area hashes for the story to reflect the x, y, and z coordinates of the # overall centroid of spaces on that floor. story[:centroid] = tz_centre[0..2] story[:ceiling_area] = tz_centre[3] end return stories end |
#three_vertices_same_line_and_dir?(vert1, vert2, vert3) ⇒ Boolean
check that three vertices are on the same line. Also check that the vectors from vert1 and vert2 and from vert1 and vert3 are in the same direction. created by: Kamel Haddad ([email protected])
945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 945 def three_vertices_same_line_and_dir?(vert1,vert2,vert3) tol = 1.0e-5 vec12x,vec12y,vec12z = -vert1.x+vert2.x,-vert1.y+vert2.y,-vert1.z+vert2.z # x,y,z of vector 12 vec12x = 0.0 if vec12x.abs < tol vec12y = 0.0 if vec12y.abs < tol vec12z = 0.0 if vec12z.abs < tol vec13x,vec13y,vec13z = -vert1.x+vert3.x,-vert1.y+vert3.y,-vert1.z+vert3.z # x,y,z of vector 13 vec13x = 0.0 if vec13x.abs < tol vec13y = 0.0 if vec13y.abs < tol vec13z = 0.0 if vec13z.abs < tol # x,y,z of the cross product of the vectors 12 and 13 cross_12_13_x = vec12y*vec13z-vec12z*vec13y cross_12_13_y = vec12z*vec13x-vec12x*vec13z cross_12_13_z = vec12x*vec13y-vec12y*vec13x # vectors are in parallel when x,y,z of cross product are 0.0 vertices_on_same_line = false vertices_on_same_line = true if (cross_12_13_x == 0.0) && (cross_12_13_y == 0.0) && (cross_12_13_z == 0.0) vectors_same_direction = false if vertices_on_same_line vec12_13_x_factor = vec13x*vec12x vec12_13_y_factor = vec13y*vec12y vec12_13_z_factor = vec13z*vec12z vectors_same_direction = true if (vec12_13_x_factor >= 0.0) && (vec12_13_y_factor >= 0.0) && (vec12_13_z_factor >= 0.0) end same_line_same_dir = vertices_on_same_line && vectors_same_direction return same_line_same_dir end |
#update_sys_name(airloop, sys_abbr: nil, sys_oa: nil, sys_hr: nil, sys_htg: nil, sys_clg: nil, sys_sf: nil, zone_htg: nil, zone_clg: nil, sys_rf: nil) ⇒ Object
Method to update the base system name based on the inputs provided. Only the parts of the name with string inputs are updated
2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb', line 2298 def update_sys_name(airloop, sys_abbr: nil, sys_oa: nil, sys_hr: nil, sys_htg: nil, sys_clg: nil, sys_sf: nil, zone_htg: nil, zone_clg: nil, sys_rf: nil) name_parts = airloop.name.to_s.split('|').reject(&:empty?) if sys_abbr.is_a? String then name_parts[0] = sys_abbr end if sys_oa.is_a? String then name_parts[1] = sys_oa end for i in 0..name_parts.size - 1 if (name_parts[i].include? 'shr>') && (sys_hr.is_a? String) name_parts[i] = "shr>#{sys_hr}" elsif (name_parts[i].include? 'sh>') && (sys_htg.is_a? String) name_parts[i] = "sh>#{sys_htg}" elsif (name_parts[i].include? 'sc>') && (sys_clg.is_a? String) name_parts[i] = "sc>#{sys_clg}" elsif (name_parts[i].include? 'ssf') && (sys_sf.is_a? String) name_parts[i] = "ssf>#{sys_sf}" elsif (name_parts[i].include? 'zh>') && (zone_htg.is_a? String) name_parts[i] = "zh>#{zone_htg}" elsif (name_parts[i].include? 'zc>') && (zone_clg.is_a? String) name_parts[i] = "zc>#{zone_clg}" elsif (name_parts[i].include? 'srf>') && (sys_rf.is_a? String) name_parts[i] = "srf>#{sys_rf}" end end sys_name = '' name_parts.each { |part| sys_name += "#{part}|" } # Check if the last part of the system name is an integer. If it is, then remove the last part from the system name. check_int = begin Integer(name_parts.last.strip) rescue StandardError nil end sys_name = sys_name.chop unless check_int.nil? airloop.setName(sys_name) end |
#validate_and_upate_space_types(model) ⇒ Object
This method will validate that the space types in the model are indeed the correct NECB spacetypes names.
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1170 def validate_and_upate_space_types(model) space_type_vintage = determine_spacetype_vintage(model) if space_type_vintage.nil? = "These some of the spacetypes in the model are not part of any necb standard.\n Please ensure all spacetype in model are correct." puts "Error: #{}" OpenStudio.logFree(OpenStudio::Error, 'openstudio.Standards.NECB', ) return false elsif space_type_vintage == self.class.name # the spacetype in the model match the version we are trying to create. # no translation neccesary. return true else # Need to translate to current vintage. no_errors = true st_model_vintage_string = "#{space_type_vintage}_space_type" bt_model_vintage_string = "#{space_type_vintage}_building_type" st_target_vintage_string = "#{self.class.name}_space_type" bt_target_vintage_string = "#{self.class.name}_building_type" space_type_upgrade_map = @standards_data['space_type_upgrade_map'] model.getSpaceTypes.sort.each do |st| space_type_map = space_type_upgrade_map.detect { |row| (row[st_model_vintage_string] == st.standardsSpaceType.get.to_s) && (row[bt_model_vintage_string] == st.standardsBuildingType.get.to_s) } st.setStandardsBuildingType(space_type_map[bt_target_vintage_string].to_s.strip) raise('could not set buildingtype') unless st.setStandardsBuildingType(space_type_map[bt_target_vintage_string].to_s.strip) raise('could not set this') unless st.setStandardsSpaceType(space_type_map[st_target_vintage_string].to_s.strip) # Set name of spacetype to new name. st.setName("#{st.standardsBuildingType.get} #{st.standardsSpaceType.get}") end return no_errors end end |
#validate_primary_heating_fuel(primary_heating_fuel:) ⇒ Object
This method is defined and used by the vintage classes to address he issue with the heat pump fuel types. This method does nothing when creating NECB reference buildings.
2440 2441 2442 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 2440 def validate_primary_heating_fuel(primary_heating_fuel:) return primary_heating_fuel end |
#water_heater_mixed_apply_efficiency(water_heater_mixed) ⇒ Boolean
Applies the standard efficiency ratings and typical losses and paraisitic loads to this object. Efficiency and skin loss coefficient (UA) Per PNNL www.energycodes.gov/sites/default/files/documents/PrototypeModelEnhancements_2014_0.pdf Appendix A: Service Water Heating
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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 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 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb', line 60 def water_heater_mixed_apply_efficiency(water_heater_mixed) # Get the capacity of the water heater # @todo add capability to pull autosized water heater capacity # if the Sizing:WaterHeater object is ever implemented in OpenStudio. capacity_w = water_heater_mixed.heaterMaximumCapacity if capacity_w.empty? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.WaterHeaterMixed', "For #{water_heater_mixed.name}, cannot find capacity, standard will not be applied.") return false else capacity_w = capacity_w.get end capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get capacity_kbtu_per_hr = OpenStudio.convert(capacity_w, 'W', 'kBtu/hr').get # Get the volume of the water heater # @todo add capability to pull autosized water heater volume # if the Sizing:WaterHeater object is ever implemented in OpenStudio. volume_m3 = water_heater_mixed.tankVolume if volume_m3.empty? OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.WaterHeaterMixed', "For #{water_heater_mixed.name}, cannot find volume, standard will not be applied.") return false else volume_m3 = volume_m3.get end volume_gal = OpenStudio.convert(volume_m3, 'm^3', 'gal').get # Get the heater fuel type fuel_type = water_heater_mixed.heaterFuelType unless fuel_type == 'NaturalGas' || fuel_type == 'Electricity' || fuel_type == 'FuelOilNo2' OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.WaterHeaterMixed', "For #{water_heater_mixed.name}, fuel type of #{fuel_type} is not yet supported, standard will not be applied.") end # Calculate the water heater efficiency and # skin loss coefficient (UA) # Calculate the energy factor (EF) # From PNNL http://www.energycodes.gov/sites/default/files/documents/PrototypeModelEnhancements_2014_0.pdf # Appendix A: Service Water Heating water_heater_eff = nil ua_btu_per_hr_per_f = nil sl_btu_per_hr = nil case fuel_type when 'Electricity' volume_l_per_s = volume_m3 * 1000 if capacity_btu_per_hr <= OpenStudio.convert(12, 'kW', 'Btu/hr').get # Fixed water heater efficiency per PNNL water_heater_eff = 1 # Calculate the max allowable standby loss (SL) sl_w = if volume_l_per_s < 270 40 + 0.2 * volume_l_per_s # assume bottom inlet else 0.472 * volume_l_per_s - 33.5 # assume bottom inlet end sl_btu_per_hr = OpenStudio.convert(sl_w, 'W', 'Btu/hr').get else # Fixed water heater efficiency per PNNL water_heater_eff = 1 # Calculate the max allowable standby loss (SL) # use this - NECB does not give SL calculation for cap > 12 kW sl_btu_per_hr = 20 + (35 * Math.sqrt(volume_gal)) end # Calculate the skin loss coefficient (UA) ua_btu_per_hr_per_f = sl_btu_per_hr / 70 when 'NaturalGas' if capacity_btu_per_hr <= 75_000 # Fixed water heater thermal efficiency per PNNL water_heater_eff = 0.82 # Calculate the minimum Energy Factor (EF) base_ef = 0.67 vol_drt = 0.0019 ef = base_ef - (vol_drt * volume_gal) # Calculate the Recovery Efficiency (RE) # based on a fixed capacity of 75,000 Btu/hr # and a fixed volume of 40 gallons by solving # this system of equations: # ua = (1/.95-1/re)/(67.5*(24/41094-1/(re*cap))) # 0.82 = (ua*67.5+cap*re)/cap cap = 75_000.0 re = (Math.sqrt(6724 * ef**2 * cap**2 + 40_409_100 * ef**2 * cap - 28_080_900 * ef * cap + 29_318_000_625 * ef**2 - 58_636_001_250 * ef + 29_318_000_625) + 82 * ef * cap + 171_225 * ef - 171_225) / (200 * ef * cap) # Calculate the skin loss coefficient (UA) # based on the actual capacity. ua_btu_per_hr_per_f = (water_heater_eff - re) * capacity_btu_per_hr / 67.5 else # Thermal efficiency requirement from 90.1 et = 0.8 # Calculate the max allowable standby loss (SL) cap_adj = 800 vol_drt = 110 sl_btu_per_hr = (capacity_btu_per_hr / cap_adj + vol_drt * Math.sqrt(volume_gal)) # Calculate the skin loss coefficient (UA) ua_btu_per_hr_per_f = (sl_btu_per_hr * et) / 70 # Calculate water heater efficiency water_heater_eff = (ua_btu_per_hr_per_f * 70 + capacity_btu_per_hr * et) / capacity_btu_per_hr end when 'FuelOilNo2' if capacity_btu_per_hr <= 75_000 # Fixed water heater thermal efficiency per PNNL water_heater_eff = 0.82 # Calculate the minimum Energy Factor (EF) base_ef = 0.67 vol_drt = 0.0019 ef = base_ef - (vol_drt * volume_gal) # Calculate the Recovery Efficiency (RE) # based on a fixed capacity of 75,000 Btu/hr # and a fixed volume of 40 gallons by solving # this system of equations: # ua = (1/.95-1/re)/(67.5*(24/41094-1/(re*cap))) # 0.82 = (ua*67.5+cap*re)/cap cap = 75_000.0 re = (Math.sqrt(6724 * ef**2 * cap**2 + 40_409_100 * ef**2 * cap - 28_080_900 * ef * cap + 29_318_000_625 * ef**2 - 58_636_001_250 * ef + 29_318_000_625) + 82 * ef * cap + 171_225 * ef - 171_225) / (200 * ef * cap) # Calculate the skin loss coefficient (UA) # based on the actual capacity. ua_btu_per_hr_per_f = (water_heater_eff - re) * capacity_btu_per_hr / 67.5 else # Thermal efficiency requirement from 90.1 et = 0.8 # Calculate the max allowable standby loss (SL) cap_adj = 800 vol_drt = 110 sl_btu_per_hr = (capacity_btu_per_hr / cap_adj + vol_drt * Math.sqrt(volume_gal)) # Calculate the skin loss coefficient (UA) ua_btu_per_hr_per_f = (sl_btu_per_hr * et) / 70 # Calculate water heater efficiency water_heater_eff = (ua_btu_per_hr_per_f * 70 + capacity_btu_per_hr * et) / capacity_btu_per_hr end end # Convert to SI ua_btu_per_hr_per_c = OpenStudio.convert(ua_btu_per_hr_per_f, 'Btu/hr*R', 'W/K').get # Set the water heater properties # Efficiency water_heater_mixed.setHeaterThermalEfficiency(water_heater_eff) # Skin loss water_heater_mixed.setOffCycleLossCoefficienttoAmbientTemperature(ua_btu_per_hr_per_c) water_heater_mixed.setOnCycleLossCoefficienttoAmbientTemperature(ua_btu_per_hr_per_c) # @todo Parasitic loss (pilot light) # PNNL document says pilot lights were removed, but IDFs # still have the on/off cycle parasitic fuel consumptions filled in water_heater_mixed.setOnCycleParasiticFuelType(fuel_type) # self.setOffCycleParasiticFuelConsumptionRate(??) water_heater_mixed.setOnCycleParasiticHeatFractiontoTank(0) water_heater_mixed.setOffCycleParasiticFuelType(fuel_type) # self.setOffCycleParasiticFuelConsumptionRate(??) water_heater_mixed.setOffCycleParasiticHeatFractiontoTank(0.8) # set part-load performance curve if (fuel_type == 'NaturalGas') || (fuel_type == 'FuelOilNo2') plf_vs_plr_curve = model_add_curve(water_heater_mixed.model, 'SWH-EFFFPLR-NECB2011') water_heater_mixed.setPartLoadFactorCurve(plf_vs_plr_curve) end # Append the name with standards information water_heater_mixed.setName("#{water_heater_mixed.name} #{water_heater_eff.round(3)} Therm Eff") OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.WaterHeaterMixed', "For #{template}: #{water_heater_mixed.name}; thermal efficiency = #{water_heater_eff.round(3)}, skin-loss UA = #{ua_btu_per_hr_per_f.round}Btu/hr-R") return true end |
#zone_hvac_component_occupancy_ventilation_control(zone_hvac_component) ⇒ Object
do not apply zone hvac ventilation control
1125 1126 1127 |
# File 'lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb', line 1125 def zone_hvac_component_occupancy_ventilation_control(zone_hvac_component) return false end |