Module: OpenstudioStandards::ServiceWaterHeating
- Defined in:
- lib/openstudio-standards/service_water_heating/information.rb,
lib/openstudio-standards/service_water_heating/create_typical.rb,
lib/openstudio-standards/service_water_heating/create_water_use.rb,
lib/openstudio-standards/service_water_heating/create_water_heater.rb,
lib/openstudio-standards/service_water_heating/create_piping_losses.rb,
lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb
Overview
The ServiceWaterHeating module provides methods to create, modify, and get information about service water heating
Information collapse
-
.water_heater_sizing_from_water_use_equipment(water_use_equipment_array, capacity_to_volume_ratio: 1.0, water_heater_efficiency: 0.8, inlet_temperature: 40.0, supply_temperature: 140.0, peak_flow_fraction: 1.0, minimum_volume: 40.0) ⇒ Hash
Determines water heater capacity and volume from an array of water use equipment.
Create Typical collapse
-
.circulating_building_type?(standards_building_type) ⇒ Boolean
Check if the standards building type tends to have a circulating system by default.
-
.create_typical_service_water_heating(model, water_heating_fuel: nil, circulating: nil) ⇒ Array<OpenStudio::Model::PlantLoop>
add typical swh demand and supply to model.
Create Water Use collapse
-
.create_water_use(model, name: 'Main Water Use', flow_rate: 0.0, flow_rate_fraction_schedule: nil, water_use_temperature: 43.3, water_use_temperature_schedule: nil, sensible_fraction: 0.2, latent_fraction: 0.05, service_water_loop: nil, space: nil) ⇒ OpenStudio::Model::WaterUseEquipment
Creates a water use and attaches it to a service water loop and a space, if provided.
Create Water Heater collapse
-
.create_heatpump_water_heater(model, heat_pump_type: 'PumpedCondenser', water_heater_capacity: 500.0, water_heater_volume: OpenStudio.convert(80.0, 'gal', 'm^3').get, coefficient_of_performance: 2.8, electric_backup_capacity: 4500.0, on_cycle_parasitic_fuel_consumption_rate: 0.0, off_cycle_parasitic_fuel_consumption_rate: 0.0, service_water_temperature: OpenStudio.convert(125.0, 'F', 'C').get, service_water_temperature_schedule: nil, set_peak_use_flowrate: false, peak_flowrate: nil, flowrate_schedule: nil, water_heater_thermal_zone: nil, service_water_loop: nil, use_ems_control: false) ⇒ OpenStudio::Model::WaterHeaterMixed
Creates a heatpump water heater and attaches it to the supplied service water heating loop.
-
.create_water_heater(model, water_heater_capacity: nil, water_heater_volume: nil, water_heater_fuel: 'Electricity', on_cycle_parasitic_fuel_consumption_rate: 0.0, off_cycle_parasitic_fuel_consumption_rate: 0.0, service_water_temperature: 60.0, service_water_temperature_schedule: nil, set_peak_use_flowrate: false, peak_flowrate: nil, flowrate_schedule: nil, water_heater_thermal_zone: nil, number_of_water_heaters: 1, service_water_loop: nil) ⇒ OpenStudio::Model::WaterHeaterMixed
Creates a water heater and attaches it to the supplied service water heating loop.
Create Piping Losses collapse
-
.create_service_water_heating_piping_losses(model, service_water_loop, circulating: true, pipe_insulation_thickness: 0.0, floor_area: nil, number_of_stories: nil, pipe_length: 6.1, air_temperature: 21.1) ⇒ Boolean
Adds piping losses to a service water heating Loop.
Create Loop collapse
-
.create_booster_water_heating_loop(model, system_name: 'Booster Water Loop', water_heater_capacity: 8000.0, water_heater_volume: OpenStudio.convert(6.0, 'gal', 'm^3').get, water_heater_fuel: 'Electricity', on_cycle_parasitic_fuel_consumption_rate: 0.0, off_cycle_parasitic_fuel_consumption_rate: 0.0, service_water_temperature: 82.2, service_water_temperature_schedule: nil, water_heater_thermal_zone: nil, service_water_loop: nil) ⇒ OpenStudio::Model::PlantLoop
Creates a booster water heater on its own loop and attaches it to the main service water heating loop.
-
.create_service_water_heating_loop(model, system_name: 'Service Water Loop', service_water_temperature: 60.0, service_water_pump_head: 29861.0, service_water_pump_motor_efficiency: 0.3, water_heater_capacity: nil, water_heater_volume: nil, water_heater_fuel: 'Electricity', on_cycle_parasitic_fuel_consumption_rate: 0.0, off_cycle_parasitic_fuel_consumption_rate: 0.0, water_heater_thermal_zone: nil, number_of_water_heaters: 1, add_piping_losses: false, pipe_insulation_thickness: 0.0127, floor_area: nil, number_of_stories: nil) ⇒ OpenStudio::Model::PlantLoop
Creates a service water heating loop.
Class Method Details
.circulating_building_type?(standards_building_type) ⇒ Boolean
Check if the standards building type tends to have a circulating system by default
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 |
# File 'lib/openstudio-standards/service_water_heating/create_typical.rb', line 266 def self.circulating_building_type?(standards_building_type) circulating_bldg_types = [ # DOE building types 'Office', 'PrimarySchool', 'Outpatient', 'Hospital', 'SmallHotel', 'LargeHotel', 'FullServiceRestaurant', 'HighriseApartment', # DEER building types 'Asm', # 'Assembly' 'ECC', # 'Education - Community College' 'EPr', # 'Education - Primary School' 'ERC', # 'Education - Relocatable Classroom' 'ESe', # 'Education - Secondary School' 'EUn', # 'Education - University' 'Gro', # 'Grocery' 'Hsp', # 'Health/Medical - Hospital' 'Htl', # 'Lodging - Hotel' 'MBT', # 'Manufacturing Biotech' 'MFm', # 'Residential Multi-family' 'Mtl', # 'Lodging - Motel' 'Nrs', # 'Health/Medical - Nursing Home' 'OfL', # 'Office - Large' # 'RFF', # 'Restaurant - Fast-Food' 'RSD' # 'Restaurant - Sit-Down' ] return circulating_bldg_types.include?(standards_building_type) end |
.create_booster_water_heating_loop(model, system_name: 'Booster Water Loop', water_heater_capacity: 8000.0, water_heater_volume: OpenStudio.convert(6.0, 'gal', 'm^3').get, water_heater_fuel: 'Electricity', on_cycle_parasitic_fuel_consumption_rate: 0.0, off_cycle_parasitic_fuel_consumption_rate: 0.0, service_water_temperature: 82.2, service_water_temperature_schedule: nil, water_heater_thermal_zone: nil, service_water_loop: nil) ⇒ OpenStudio::Model::PlantLoop
Creates a booster water heater on its own loop and attaches it to the main service water heating loop.
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/service_water_heating/create_water_heating_loop.rb', line 185 def self.create_booster_water_heating_loop(model, system_name: 'Booster Water Loop', water_heater_capacity: 8000.0, water_heater_volume: OpenStudio.convert(6.0, 'gal', 'm^3').get, water_heater_fuel: 'Electricity', on_cycle_parasitic_fuel_consumption_rate: 0.0, off_cycle_parasitic_fuel_consumption_rate: 0.0, service_water_temperature: 82.2, service_water_temperature_schedule: nil, water_heater_thermal_zone: nil, service_water_loop: nil) if service_water_loop.nil? OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.ServiceWaterHeating', "#{__method__} requires the service_water_loop argument to couple the booster water heating loop with a heat exchanger.") return nil else OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Adding booster water heater to #{service_water_loop.name}") end water_heater_volume_gal = OpenStudio.convert(water_heater_volume, 'm^3', 'gal').get water_heater_capacity_kbtu_per_hr = OpenStudio.convert(water_heater_capacity, 'W', 'kBtu/hr').get # Booster water heating loop booster_service_water_loop = OpenStudio::Model::PlantLoop.new(model) booster_service_water_loop.setName('Booster Service Water Loop') if service_water_temperature > 82.2 service_water_loop.setMaximumLoopTemperature(service_water_temperature) else service_water_loop.setMaximumLoopTemperature(82.2) end # set minimum temperature of 125F for legionella growth risk swh_loop_min_c = OpenStudio.convert(125.0, 'F', 'C').get service_water_loop.setMinimumLoopTemperature(swh_loop_min_c) # create and add booster water heater to loop booster_water_heater = OpenstudioStandards::ServiceWaterHeating.create_water_heater(model, water_heater_capacity: water_heater_capacity, water_heater_volume: water_heater_volume, water_heater_fuel: water_heater_fuel, on_cycle_parasitic_fuel_consumption_rate: on_cycle_parasitic_fuel_consumption_rate, off_cycle_parasitic_fuel_consumption_rate: off_cycle_parasitic_fuel_consumption_rate, service_water_temperature: service_water_temperature, service_water_temperature_schedule: service_water_temperature_schedule, water_heater_thermal_zone: water_heater_thermal_zone, service_water_loop: booster_service_water_loop) booster_water_heater.setName("#{water_heater_volume_gal}gal #{water_heater_fuel} Booster Water Heater - #{water_heater_capacity_kbtu_per_hr.round}kBtu/hr") booster_water_heater.setEndUseSubcategory('Booster') # Service water heating loop controls swh_temp_f = OpenStudio.convert(service_water_temperature, 'C', 'F').get swh_delta_t_r = 9.0 # default to 9 R temperature difference swh_delta_t_k = OpenStudio.convert(swh_delta_t_r, 'R', 'K').get swh_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model, service_water_temperature, name: "Service Water Booster Temp - #{swh_temp_f.round}F", schedule_type_limit: 'Temperature') swh_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, swh_temp_sch) swh_stpt_manager.setName('Hot water booster setpoint manager') swh_stpt_manager.addToNode(booster_service_water_loop.supplyOutletNode) sizing_plant = booster_service_water_loop.sizingPlant sizing_plant.setLoopType('Heating') sizing_plant.setDesignLoopExitTemperature(service_water_temperature) sizing_plant.setLoopDesignTemperatureDifference(swh_delta_t_k) # Booster water heating pump swh_pump = OpenStudio::Model::PumpVariableSpeed.new(model) swh_pump.setName('Booster Water Loop Pump') swh_pump.setRatedPumpHead(0.0) # As if there is no circulation pump swh_pump.setRatedPowerConsumption(0.0) # As if there is no circulation pump swh_pump.setMotorEfficiency(1) swh_pump.setPumpControlType('Continuous') swh_pump.setMinimumFlowRate(0.0) swh_pump.addToNode(booster_service_water_loop.supplyInletNode) # Service water heating loop bypass pipes water_heater_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model) booster_service_water_loop.addSupplyBranchForComponent(water_heater_bypass_pipe) coil_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model) booster_service_water_loop.addDemandBranchForComponent(coil_bypass_pipe) supply_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model) supply_outlet_pipe.addToNode(booster_service_water_loop.supplyOutletNode) demand_inlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model) demand_inlet_pipe.addToNode(booster_service_water_loop.demandInletNode) demand_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model) demand_outlet_pipe.addToNode(booster_service_water_loop.demandOutletNode) # Heat exchanger to supply the booster water heater with normal hot water from the main service water loop hx = OpenStudio::Model::HeatExchangerFluidToFluid.new(model) hx.setName('Booster Water Heating Heat Exchanger') hx.setHeatExchangeModelType('Ideal') hx.setControlType('UncontrolledOn') hx.setHeatTransferMeteringEndUseType('LoopToLoop') # Add the HX to the supply side of the booster loop hx.addToNode(booster_service_water_loop.supplyInletNode) # Add the HX to the demand side of the main service water loop service_water_loop.addDemandBranchForComponent(hx) # Add a plant component temperature source to the demand outlet # of the HX to represent the fact that the water used by the booster # would in reality be at the mains temperature. mains_src = OpenStudio::Model::PlantComponentTemperatureSource.new(model) mains_src.setName('Mains Water Makeup for SWH Booster') mains_src.addToNode(hx.demandOutletModelObject.get.to_Node.get) # use the site water mains temperature schedule if available, # otherwise use the annual average outdoor air temperature site_water_mains = model.getSiteWaterMainsTemperature if site_water_mains.temperatureSchedule.is_initialized water_mains_temp_sch = site_water_mains.temperatureSchedule.get elsif site_water_mains.annualAverageOutdoorAirTemperature.is_initialized mains_src_temp_c = site_water_mains.annualAverageOutdoorAirTemperature.get mains_src.setSourceTemperature(mains_src_temp_c) water_mains_temp_sch = OpenStudio::Model::ScheduleConstant.new(model) water_mains_temp_sch.setName('Booster Water Makeup Temperature') water_mains_temp_sch.setValue(mains_src_temp_c) else # assume 50F mains_src_temp_c = OpenStudio.convert(50.0, 'F', 'C').get mains_src.setSourceTemperature(mains_src_temp_c) water_mains_temp_sch = OpenStudio::Model::ScheduleConstant.new(model) water_mains_temp_sch.setName('Booster Water Makeup Temperature') water_mains_temp_sch.setValue(mains_src_temp_c) end mains_src.setTemperatureSpecificationType('Scheduled') mains_src.setSourceTemperatureSchedule(water_mains_temp_sch) return booster_service_water_loop end |
.create_heatpump_water_heater(model, heat_pump_type: 'PumpedCondenser', water_heater_capacity: 500.0, water_heater_volume: OpenStudio.convert(80.0, 'gal', 'm^3').get, coefficient_of_performance: 2.8, electric_backup_capacity: 4500.0, on_cycle_parasitic_fuel_consumption_rate: 0.0, off_cycle_parasitic_fuel_consumption_rate: 0.0, service_water_temperature: OpenStudio.convert(125.0, 'F', 'C').get, service_water_temperature_schedule: nil, set_peak_use_flowrate: false, peak_flowrate: nil, flowrate_schedule: nil, water_heater_thermal_zone: nil, service_water_loop: nil, use_ems_control: false) ⇒ OpenStudio::Model::WaterHeaterMixed
Creates a heatpump water heater and attaches it to the supplied service water heating loop.
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 |
# File 'lib/openstudio-standards/service_water_heating/create_water_heater.rb', line 208 def self.create_heatpump_water_heater(model, heat_pump_type: 'PumpedCondenser', water_heater_capacity: 500.0, water_heater_volume: OpenStudio.convert(80.0, 'gal', 'm^3').get, coefficient_of_performance: 2.8, electric_backup_capacity: 4500.0, on_cycle_parasitic_fuel_consumption_rate: 0.0, off_cycle_parasitic_fuel_consumption_rate: 0.0, service_water_temperature: OpenStudio.convert(125.0, 'F', 'C').get, service_water_temperature_schedule: nil, set_peak_use_flowrate: false, peak_flowrate: nil, flowrate_schedule: nil, water_heater_thermal_zone: nil, service_water_loop: nil, use_ems_control: false) # create heat pump water heater if heat_pump_type == 'WrappedCondenser' hpwh = OpenStudio::Model::WaterHeaterHeatPumpWrappedCondenser.new(model) elsif heat_pump_type == 'PumpedCondenser' hpwh = OpenStudio::Model::WaterHeaterHeatPump.new(model) end # calculate tank height and radius water_heater_capacity_kbtu_per_hr = OpenStudio.convert(water_heater_capacity, 'W', 'kBtu/hr').get hpwh_vol_gal = OpenStudio.convert(water_heater_volume, 'm^3', 'gal').get tank_height = (0.0188 * hpwh_vol_gal) + 0.0935 # linear relationship that gets GE height at 50 gal and AO Smith height at 80 gal tank_radius = (0.9 * water_heater_volume / (Math::PI * tank_height))**0.5 tank_surface_area = 2.0 * Math::PI * tank_radius * (tank_radius + tank_height) tank_ua = 3.9 # default ua assumption u_tank = (5.678 * tank_ua) / OpenStudio.convert(tank_surface_area, 'm^2', 'ft^2').get hpwh.setName("#{hpwh_vol_gal.round}gal Heat Pump Water Heater - #{water_heater_capacity_kbtu_per_hr.round(0)}kBtu/hr") # set min/max HPWH operating temperature limit hpwh_op_min_temp_c = OpenStudio.convert(45.0, 'F', 'C').get hpwh_op_max_temp_c = OpenStudio.convert(120.0, 'F', 'C').get if heat_pump_type == 'WrappedCondenser' hpwh.setMinimumInletAirTemperatureforCompressorOperation(hpwh_op_min_temp_c) hpwh.setMaximumInletAirTemperatureforCompressorOperation(hpwh_op_max_temp_c) # set sensor heights if hpwh_vol_gal <= 50.0 hpwh.setDeadBandTemperatureDifference(0.5) h_ue = (1 - (3.5 / 12.0)) * tank_height # in the 4th node of the tank (counting from top) h_le = (1 - (10.5 / 12.0)) * tank_height # in the 11th node of the tank (counting from top) h_condtop = (1 - (5.5 / 12.0)) * tank_height # in the 6th node of the tank (counting from top) h_condbot = (1 - (10.99 / 12.0)) * tank_height # in the 11th node of the tank h_hpctrl = (1 - (2.5 / 12.0)) * tank_height # in the 3rd node of the tank hpwh.setControlSensor1HeightInStratifiedTank(h_hpctrl) hpwh.setControlSensor1Weight(1.0) hpwh.setControlSensor2HeightInStratifiedTank(h_hpctrl) else hpwh.setDeadBandTemperatureDifference(3.89) h_ue = (1 - (3.5 / 12.0)) * tank_height # in the 3rd node of the tank (counting from top) h_le = (1 - (9.5 / 12.0)) * tank_height # in the 10th node of the tank (counting from top) h_condtop = (1 - (5.5 / 12.0)) * tank_height # in the 6th node of the tank (counting from top) h_condbot = 0.01 # bottom node h_hpctrl_up = (1 - (2.5 / 12.0)) * tank_height # in the 3rd node of the tank h_hpctrl_low = (1 - (8.5 / 12.0)) * tank_height # in the 9th node of the tank hpwh.setControlSensor1HeightInStratifiedTank(h_hpctrl_up) hpwh.setControlSensor1Weight(0.75) hpwh.setControlSensor2HeightInStratifiedTank(h_hpctrl_low) end hpwh.setCondenserBottomLocation(h_condbot) hpwh.setCondenserTopLocation(h_condtop) hpwh.setTankElementControlLogic('MutuallyExclusive') hpwh.autocalculateEvaporatorAirFlowRate elsif heat_pump_type == 'PumpedCondenser' hpwh.setDeadBandTemperatureDifference(3.89) hpwh.autosizeEvaporatorAirFlowRate end # set heat pump water heater properties hpwh.setFanPlacement('DrawThrough') hpwh.setOnCycleParasiticElectricLoad(0.0) hpwh.setOffCycleParasiticElectricLoad(0.0) hpwh.setParasiticHeatRejectionLocation('Outdoors') # set temperature setpoint schedule if service_water_temperature_schedule.nil? # service water heating loop controls swh_temp_c = service_water_temperature swh_temp_f = OpenStudio.convert(swh_temp_c, 'C', 'F').get swh_delta_t_r = 9.0 # 9F delta-T swh_temp_c = OpenStudio.convert(swh_temp_f, 'F', 'C').get swh_delta_t_k = OpenStudio.convert(swh_delta_t_r, 'R', 'K').get service_water_temperature_schedule = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model, swh_temp_c, name: "Heat Pump Water Heater Temp - #{swh_temp_f.round}F", schedule_type_limit: 'Temperature') end hpwh.setCompressorSetpointTemperatureSchedule(service_water_temperature_schedule) # coil curves hpwh_cap = OpenStudio::Model::CurveBiquadratic.new(model) hpwh_cap.setName('HPWH-Cap-fT') hpwh_cap.setCoefficient1Constant(0.563) hpwh_cap.setCoefficient2x(0.0437) hpwh_cap.setCoefficient3xPOW2(0.000039) hpwh_cap.setCoefficient4y(0.0055) hpwh_cap.setCoefficient5yPOW2(-0.000148) hpwh_cap.setCoefficient6xTIMESY(-0.000145) hpwh_cap.setMinimumValueofx(0.0) hpwh_cap.setMaximumValueofx(100.0) hpwh_cap.setMinimumValueofy(0.0) hpwh_cap.setMaximumValueofy(100.0) hpwh_cop = OpenStudio::Model::CurveBiquadratic.new(model) hpwh_cop.setName('HPWH-COP-fT') hpwh_cop.setCoefficient1Constant(1.1332) hpwh_cop.setCoefficient2x(0.063) hpwh_cop.setCoefficient3xPOW2(-0.0000979) hpwh_cop.setCoefficient4y(-0.00972) hpwh_cop.setCoefficient5yPOW2(-0.0000214) hpwh_cop.setCoefficient6xTIMESY(-0.000686) hpwh_cop.setMinimumValueofx(0.0) hpwh_cop.setMaximumValueofx(100.0) hpwh_cop.setMinimumValueofy(0.0) hpwh_cop.setMaximumValueofy(100.0) # create DX coil object if heat_pump_type == 'WrappedCondenser' coil = hpwh.dXCoil.to_CoilWaterHeatingAirToWaterHeatPumpWrapped.get coil.setRatedCondenserWaterTemperature(48.89) coil.autocalculateRatedEvaporatorAirFlowRate elsif heat_pump_type == 'PumpedCondenser' coil = hpwh.dXCoil.to_CoilWaterHeatingAirToWaterHeatPump.get coil.autosizeRatedEvaporatorAirFlowRate end # set coil properties coil.setName("#{hpwh.name} Coil") coil.setRatedHeatingCapacity(water_heater_capacity) coil.setRatedCOP(coefficient_of_performance) coil.setRatedSensibleHeatRatio(0.88) # default sensible_heat_ratio assumption coil.setRatedEvaporatorInletAirDryBulbTemperature(OpenStudio.convert(67.5, 'F', 'C').get) coil.setRatedEvaporatorInletAirWetBulbTemperature(OpenStudio.convert(56.426, 'F', 'C').get) coil.setEvaporatorFanPowerIncludedinRatedCOP(true) coil.setEvaporatorAirTemperatureTypeforCurveObjects('WetBulbTemperature') coil.setHeatingCapacityFunctionofTemperatureCurve(hpwh_cap) coil.setHeatingCOPFunctionofTemperatureCurve(hpwh_cop) coil.setMaximumAmbientTemperatureforCrankcaseHeaterOperation(0.0) # set tank properties if heat_pump_type == 'WrappedCondenser' tank = hpwh.tank.to_WaterHeaterStratified.get tank.setTankHeight(tank_height) tank.setHeaterPriorityControl('MasterSlave') if hpwh_vol_gal <= 50.0 tank.setHeater1DeadbandTemperatureDifference(25.0) tank.setHeater2DeadbandTemperatureDifference(30.0) else tank.setHeater1DeadbandTemperatureDifference(18.5) tank.setHeater2DeadbandTemperatureDifference(3.89) end hpwh_bottom_element_sp = OpenStudio::Model::ScheduleConstant.new(model) hpwh_bottom_element_sp.setName("#{hpwh.name} BottomElementSetpoint") hpwh_top_element_sp = OpenStudio::Model::ScheduleConstant.new(model) hpwh_top_element_sp.setName("#{hpwh.name} TopElementSetpoint") tank.setHeater1Capacity(electric_backup_capacity) tank.setHeater1Height(h_ue) tank.setHeater1SetpointTemperatureSchedule(hpwh_top_element_sp) # Overwritten later by EMS tank.setHeater2Capacity(electric_backup_capacity) tank.setHeater2Height(h_le) tank.setHeater2SetpointTemperatureSchedule(hpwh_bottom_element_sp) tank.setUniformSkinLossCoefficientperUnitAreatoAmbientTemperature(u_tank) tank.setNumberofNodes(12) tank.setAdditionalDestratificationConductivity(0) tank.setNode1AdditionalLossCoefficient(0) tank.setNode2AdditionalLossCoefficient(0) tank.setNode3AdditionalLossCoefficient(0) tank.setNode4AdditionalLossCoefficient(0) tank.setNode5AdditionalLossCoefficient(0) tank.setNode6AdditionalLossCoefficient(0) tank.setNode7AdditionalLossCoefficient(0) tank.setNode8AdditionalLossCoefficient(0) tank.setNode9AdditionalLossCoefficient(0) tank.setNode10AdditionalLossCoefficient(0) tank.setNode11AdditionalLossCoefficient(0) tank.setNode12AdditionalLossCoefficient(0) tank.setUseSideDesignFlowRate(0.9 * water_heater_volume / 60.1) tank.setSourceSideDesignFlowRate(0) tank.setSourceSideFlowControlMode('') tank.setSourceSideInletHeight(0) tank.setSourceSideOutletHeight(0) elsif heat_pump_type == 'PumpedCondenser' tank = hpwh.tank.to_WaterHeaterMixed.get tank.setDeadbandTemperatureDifference(3.89) tank.setHeaterControlType('Cycle') tank.setHeaterMaximumCapacity(electric_backup_capacity) end tank.setName("#{hpwh.name} Tank") tank.setEndUseSubcategory('Service Hot Water') tank.setTankVolume(0.9 * water_heater_volume) tank.setMaximumTemperatureLimit(90.0) tank.setHeaterFuelType('Electricity') tank.setHeaterThermalEfficiency(1.0) tank.setOffCycleParasiticFuelConsumptionRate(off_cycle_parasitic_fuel_consumption_rate) tank.setOffCycleParasiticFuelType('Electricity') tank.setOnCycleParasiticFuelConsumptionRate(on_cycle_parasitic_fuel_consumption_rate) tank.setOnCycleParasiticFuelType('Electricity') # set fan properties fan = hpwh.fan.to_FanOnOff.get fan.setName("#{hpwh.name} Fan") fan_power = 0.0462 # watts per cfm if hpwh_vol_gal <= 50.0 fan.setFanEfficiency(23.0 / fan_power * OpenStudio.convert(1.0, 'ft^3/min', 'm^3/s').get) fan.setPressureRise(23.0) else fan.setFanEfficiency(65.0 / fan_power * OpenStudio.convert(1.0, 'ft^3/min', 'm^3/s').get) fan.setPressureRise(65.0) end # determine maximum flow rate from water heater capacity # use 5.035E-5 m^3/s/W from EnergyPlus used to autocalculate the evaporator air flow rate in WaterHeater:HeatPump:PumpedCondenser and Coil:WaterHeating:AirToWaterHeatPump:Pumped fan_flow_rate_m3_per_s = water_heater_capacity * 5.035e-5 fan.setMaximumFlowRate(fan_flow_rate_m3_per_s) fan.setMotorEfficiency(1.0) fan.setMotorInAirstreamFraction(1.0) fan.setEndUseSubcategory('Service Hot Water') if water_heater_thermal_zone.nil? # add in schedules for Tamb, RHamb, and the compressor # assume the water heater is indoors at 71.6F / 22C default_water_heater_ambient_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model, OpenStudio.convert(71.6, 'F', 'C').get, name: 'Water Heater Ambient Temp Schedule 70F', schedule_type_limit: 'Temperature') tank.setAmbientTemperatureIndicator('Schedule') tank.setAmbientTemperatureSchedule(default_water_heater_ambient_temp_sch) tank.resetAmbientTemperatureThermalZone hpwh_rhamb = OpenStudio::Model::ScheduleConstant.new(model) hpwh_rhamb.setName("#{hpwh.name} Ambient Humidity Schedule") hpwh_rhamb.setValue(0.5) hpwh.setInletAirConfiguration('Schedule') hpwh.setInletAirTemperatureSchedule(default_water_heater_ambient_temp_sch) hpwh.setInletAirHumiditySchedule(hpwh_rhamb) hpwh.setCompressorLocation('Schedule') hpwh.setCompressorAmbientTemperatureSchedule(default_water_heater_ambient_temp_sch) else hpwh.addToThermalZone(water_heater_thermal_zone) hpwh.setInletAirConfiguration('ZoneAirOnly') hpwh.setCompressorLocation('Zone') tank.setAmbientTemperatureIndicator('ThermalZone') tank.setAmbientTemperatureThermalZone(water_heater_thermal_zone) tank.resetAmbientTemperatureSchedule end if set_peak_use_flowrate rated_flow_rate_m3_per_s = peak_flowrate rated_flow_rate_gal_per_min = OpenStudio.convert(rated_flow_rate_m3_per_s, 'm^3/s', 'gal/min').get tank.setPeakUseFlowRate(rated_flow_rate_m3_per_s) tank.setUseFlowRateFractionSchedule(flowrate_schedule) unless flowrate_schedule.nil? end # add EMS for overriding HPWH setpoints schedules (for upper/lower heating element in water tank and compressor in heat pump) if heat_pump_type == 'WrappedCondenser' && use_ems_control std = Standard.build('90.1-2013') hpwh_name_ems_friendly = std.ems_friendly_name(hpwh.name) # create an ambient temperature sensor for the air that blows through the HPWH evaporator if water_heater_thermal_zone.nil? # assume the condenser is outside amb_temp_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Site Outdoor Air Drybulb Temperature') amb_temp_sensor.setName("#{hpwh_name_ems_friendly}_amb_temp") amb_temp_sensor.setKeyName('Environment') else amb_temp_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Mean Air Temperature') amb_temp_sensor.setName("#{hpwh_name_ems_friendly}_amb_temp") amb_temp_sensor.setKeyName(water_heater_thermal_zone.name.to_s) end # create actuator for heat pump compressor if service_water_temperature_schedule.to_ScheduleConstant.is_initialized service_water_temperature_schedule = service_water_temperature_schedule.to_ScheduleConstant.get schedule_type = 'Schedule:Constant' elsif service_water_temperature_schedule.to_ScheduleCompact.is_initialized service_water_temperature_schedule = service_water_temperature_schedule.to_ScheduleCompact.get schedule_type = 'Schedule:Compact' elsif service_water_temperature_schedule.to_ScheduleRuleset.is_initialized service_water_temperature_schedule = service_water_temperature_schedule.to_ScheduleRuleset.get schedule_type = 'Schedule:Year' else OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.ServiceWaterHeating', "Unsupported schedule type for HPWH setpoint schedule #{service_water_temperature_schedule.name}.") return false end hpwhschedoverride_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(service_water_temperature_schedule, schedule_type, 'Schedule Value') hpwhschedoverride_actuator.setName("#{hpwh_name_ems_friendly}_HPWHSchedOverride") # create actuator for lower heating element in water tank leschedoverride_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(hpwh_bottom_element_sp, 'Schedule:Constant', 'Schedule Value') leschedoverride_actuator.setName("#{hpwh_name_ems_friendly}_LESchedOverride") # create actuator for upper heating element in water tank ueschedoverride_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(hpwh_top_element_sp, 'Schedule:Constant', 'Schedule Value') ueschedoverride_actuator.setName("#{hpwh_name_ems_friendly}_UESchedOverride") # create sensor for heat pump compressor t_set_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value') t_set_sensor.setName("#{hpwh_name_ems_friendly}_T_set") t_set_sensor.setKeyName(service_water_temperature_schedule.name.to_s) # define control configuration t_offset = 9.0 # deg-C # get tank specifications upper_element_db = tank.heater1DeadbandTemperatureDifference # define control logic hpwh_ctrl_program = OpenStudio::Model::EnergyManagementSystemProgram.new(model) hpwh_ctrl_program.setName("#{hpwh_name_ems_friendly}_Control") hpwh_ctrl_program.addLine("SET #{hpwhschedoverride_actuator.name} = #{t_set_sensor.name}") # lockout hp when ambient temperature is either too high or too low hpwh_ctrl_program.addLine("IF (#{amb_temp_sensor.name}<#{hpwh_op_min_temp_c}) || (#{amb_temp_sensor.name}>#{hpwh_op_max_temp_c})") hpwh_ctrl_program.addLine("SET #{ueschedoverride_actuator.name} = #{t_set_sensor.name}") hpwh_ctrl_program.addLine("SET #{leschedoverride_actuator.name} = #{t_set_sensor.name}") hpwh_ctrl_program.addLine('ELSE') # upper element setpoint temperature hpwh_ctrl_program.addLine("SET #{ueschedoverride_actuator.name} = #{t_set_sensor.name} - #{t_offset}") # upper element cut-in temperature hpwh_ctrl_program.addLine("SET #{ueschedoverride_actuator.name}_cut_in = #{ueschedoverride_actuator.name} - #{upper_element_db}") # lower element disabled hpwh_ctrl_program.addLine("SET #{leschedoverride_actuator.name} = 0") # lower element disabled hpwh_ctrl_program.addLine("SET #{leschedoverride_actuator.name}_cut_in = 0") hpwh_ctrl_program.addLine('ENDIF') # create a program calling manager program_calling_manager = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model) program_calling_manager.setName("#{hpwh_name_ems_friendly}_ProgramManager") program_calling_manager.setCallingPoint('InsideHVACSystemIterationLoop') program_calling_manager.addProgram(hpwh_ctrl_program) end # add the water heater to the service water loop if provided unless service_water_loop.nil? service_water_loop.addSupplyBranchForComponent(tank) end OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Added heat pump water heater called #{tank.name}") return hpwh end |
.create_service_water_heating_loop(model, system_name: 'Service Water Loop', service_water_temperature: 60.0, service_water_pump_head: 29861.0, service_water_pump_motor_efficiency: 0.3, water_heater_capacity: nil, water_heater_volume: nil, water_heater_fuel: 'Electricity', on_cycle_parasitic_fuel_consumption_rate: 0.0, off_cycle_parasitic_fuel_consumption_rate: 0.0, water_heater_thermal_zone: nil, number_of_water_heaters: 1, add_piping_losses: false, pipe_insulation_thickness: 0.0127, floor_area: nil, number_of_stories: nil) ⇒ OpenStudio::Model::PlantLoop
Creates a service water heating loop.
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 |
# File 'lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb', line 31 def self.create_service_water_heating_loop(model, system_name: 'Service Water Loop', service_water_temperature: 60.0, service_water_pump_head: 29861.0, service_water_pump_motor_efficiency: 0.3, water_heater_capacity: nil, water_heater_volume: nil, water_heater_fuel: 'Electricity', on_cycle_parasitic_fuel_consumption_rate: 0.0, off_cycle_parasitic_fuel_consumption_rate: 0.0, water_heater_thermal_zone: nil, number_of_water_heaters: 1, add_piping_losses: false, pipe_insulation_thickness: 0.0127, floor_area: nil, number_of_stories: nil) # create service water heating loop service_water_loop = OpenStudio::Model::PlantLoop.new(model) if service_water_temperature > 60.0 service_water_loop.setMaximumLoopTemperature(service_water_temperature) else service_water_loop.setMaximumLoopTemperature(60.0) end # set minimum temperature of 125F for legionella growth risk swh_loop_min_c = OpenStudio.convert(125.0, 'F', 'C').get service_water_loop.setMinimumLoopTemperature(swh_loop_min_c) if system_name.nil? system_name = 'Service Water Loop' end service_water_loop.setName(system_name) # service water heating loop controls swh_temp_f = OpenStudio.convert(service_water_temperature, 'C', 'F').get swh_delta_t_r = 9.0 # default to 9 R temperature difference swh_delta_t_k = OpenStudio.convert(swh_delta_t_r, 'R', 'K').get swh_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model, service_water_temperature, name: "Service Water Loop Temp - #{swh_temp_f.round}F", schedule_type_limit: 'Temperature') swh_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, swh_temp_sch) swh_stpt_manager.setName('Service hot water setpoint manager') swh_stpt_manager.addToNode(service_water_loop.supplyOutletNode) sizing_plant = service_water_loop.sizingPlant sizing_plant.setLoopType('Heating') sizing_plant.setDesignLoopExitTemperature(service_water_temperature) sizing_plant.setLoopDesignTemperatureDifference(swh_delta_t_k) # determine if circulating or non-circulating based on supplied head pressure if service_water_pump_head.nil? || service_water_pump_head <= 1 # set pump head pressure to near zero if there is no circulation pump service_water_pump_head = 0.001 service_water_pump_motor_efficiency = 1 circulating = false else circulating = true end # add pump if circulating swh_pump = OpenStudio::Model::PumpConstantSpeed.new(model) swh_pump.setName("#{service_water_loop.name} Circulator Pump") swh_pump.setPumpControlType('Intermittent') else swh_pump = OpenStudio::Model::PumpVariableSpeed.new(model) swh_pump.setName("#{service_water_loop.name} Water Mains Pressure Driven") swh_pump.setPumpControlType('Continuous') end swh_pump.setRatedPumpHead(service_water_pump_head.to_f) swh_pump.setMotorEfficiency(service_water_pump_motor_efficiency) swh_pump.addToNode(service_water_loop.supplyInletNode) # add water heater case water_heater_fuel when 'None' # don't add a water heater when 'HeatPump' OpenstudioStandards::ServiceWaterHeating.create_heatpump_water_heater(model, water_heater_capacity: water_heater_capacity, water_heater_volume: water_heater_volume, on_cycle_parasitic_fuel_consumption_rate: on_cycle_parasitic_fuel_consumption_rate, off_cycle_parasitic_fuel_consumption_rate: off_cycle_parasitic_fuel_consumption_rate, service_water_temperature: service_water_temperature, service_water_temperature_schedule: swh_temp_sch, set_peak_use_flowrate: false, peak_flowrate: 0.0, flowrate_schedule: nil, water_heater_thermal_zone: water_heater_thermal_zone, service_water_loop: service_water_loop, use_ems_control: false) else OpenstudioStandards::ServiceWaterHeating.create_water_heater(model, water_heater_capacity: water_heater_capacity, water_heater_volume: water_heater_volume, water_heater_fuel: water_heater_fuel, on_cycle_parasitic_fuel_consumption_rate: on_cycle_parasitic_fuel_consumption_rate, off_cycle_parasitic_fuel_consumption_rate: off_cycle_parasitic_fuel_consumption_rate, service_water_temperature: service_water_temperature, service_water_temperature_schedule: swh_temp_sch, set_peak_use_flowrate: false, peak_flowrate: 0.0, flowrate_schedule: nil, water_heater_thermal_zone: water_heater_thermal_zone, number_of_water_heaters: number_of_water_heaters, service_water_loop: service_water_loop) end # add pipe losses if requested if add_piping_losses OpenstudioStandards::ServiceWaterHeating.create_service_water_heating_piping_losses(model, service_water_loop, circulating: circulating, pipe_insulation_thickness: pipe_insulation_thickness, floor_area: floor_area, number_of_stories: number_of_stories) end # service water heating loop bypass pipes water_heater_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model) service_water_loop.addSupplyBranchForComponent(water_heater_bypass_pipe) coil_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model) service_water_loop.addDemandBranchForComponent(coil_bypass_pipe) supply_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model) supply_outlet_pipe.addToNode(service_water_loop.supplyOutletNode) demand_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model) demand_outlet_pipe.addToNode(service_water_loop.demandOutletNode) if circulating OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Added circulating SWH loop called #{service_water_loop.name}") else OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Added non-circulating SWH loop called #{service_water_loop.name}") end return service_water_loop end |
.create_service_water_heating_piping_losses(model, service_water_loop, circulating: true, pipe_insulation_thickness: 0.0, floor_area: nil, number_of_stories: nil, pipe_length: 6.1, air_temperature: 21.1) ⇒ Boolean
Adds piping losses to a service water heating Loop. Assumes the piping system use insulated 0.75 inch copper piping. For circulating systems, assume length of piping is proportional to the building floor area and number of stories. For non-circulating systems, assume that the water heaters are close to the point of use.
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 |
# File 'lib/openstudio-standards/service_water_heating/create_piping_losses.rb', line 24 def self.create_service_water_heating_piping_losses(model, service_water_loop, circulating: true, pipe_insulation_thickness: 0.0, floor_area: nil, number_of_stories: nil, pipe_length: 6.1, air_temperature: 21.1) # Estimate pipe length if circulating # For circulating systems, get pipe length based on the size of the building. # Formula from A.3.1 PrototypeModelEnhancements_2014_0.pdf # get the floor area floor_area = model.getBuilding.floorArea if floor_area.nil? floor_area_ft2 = OpenStudio.convert(floor_area, 'm^2', 'ft^2').get # get the number of stories number_of_stories = model.getBuilding.buildingStories.size if number_of_stories.nil? # calculate the piping length pipe_length_ft = 2.0 * (Math.sqrt(floor_area_ft2 / number_of_stories) + (10.0 * (number_of_stories - 1.0))) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Pipe length #{pipe_length_ft.round}ft = 2.0 * ( (#{floor_area_ft2.round}ft2 / #{number_of_stories} stories)^0.5 + (10.0ft * (#{number_of_stories} stories - 1.0) ) )") else # For non-circulating systems, assume water heater is close to point of use # get pipe length pipe_length_m = pipe_length.nil? ? 6.1 : pipe_length pipe_length_ft = OpenStudio.convert(pipe_length_m, 'm', 'ft').get OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Pipe length #{pipe_length_ft.round}ft. For non-circulating systems, assume water heater is close to point of use.") end # For systems whose water heater object represents multiple pieces # of equipment, multiply the piping length by the number of pieces of equipment. service_water_loop.supplyComponents('OS_WaterHeater_Mixed'.to_IddObjectType).each do |sc| next unless sc.to_WaterHeaterMixed.is_initialized water_heater = sc.to_WaterHeaterMixed.get # get number of water heaters if water_heater.additionalProperties.getFeatureAsInteger('component_quantity').is_initialized comp_qty = water_heater.additionalProperties.getFeatureAsInteger('component_quantity').get else comp_qty = 1 end # if more than 1 water heater, multiply the pipe length by the number of water heaters, # unless the user has specified a pipe length if comp_qty > 1 && pipe_length.nil? OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Piping length has been multiplied by #{comp_qty}X because #{water_heater.name} represents #{comp_qty} pieces of equipment.") pipe_length_ft *= comp_qty break end end # Service water heating piping heat loss scheduled air temperature air_temperature_f = OpenStudio.convert(air_temperature, 'C', 'F').get swh_piping_air_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model, air_temperature, name: "#{service_water_loop.name} Piping Air Temp - #{air_temperature_f.round}F", schedule_type_limit: 'Temperature') # Service water heating piping heat loss scheduled air velocity swh_piping_air_velocity_m_per_s = 0.3 swh_piping_air_velocity_mph = OpenStudio.convert(swh_piping_air_velocity_m_per_s, 'm/s', 'mile/hr').get swh_piping_air_velocity_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model, swh_piping_air_velocity_m_per_s, name: "#{service_water_loop.name} Piping Air Velocity - #{swh_piping_air_velocity_mph.round(2)}mph") # Material for 3/4in type L (heavy duty) copper pipe copper_pipe = OpenStudio::Model::StandardOpaqueMaterial.new(model) copper_pipe.setName('Copper pipe 0.75in type L') copper_pipe.setRoughness('Smooth') copper_pipe.setThickness(OpenStudio.convert(0.045, 'in', 'm').get) copper_pipe.setThermalConductivity(386.0) copper_pipe.setDensity(OpenStudio.convert(556, 'lb/ft^3', 'kg/m^3').get) copper_pipe.setSpecificHeat(OpenStudio.convert(0.092, 'Btu/lb*R', 'J/kg*K').get) copper_pipe.setThermalAbsorptance(0.9) # @todo find reference for property copper_pipe.setSolarAbsorptance(0.7) # @todo find reference for property copper_pipe.setVisibleAbsorptance(0.7) # @todo find reference for property # Construction for pipe pipe_construction = OpenStudio::Model::Construction.new(model) # Add insulation material to insulated pipe if pipe_insulation_thickness > 0 # Material for fiberglass insulation # R-value from Owens-Corning 1/2in fiberglass pipe insulation # https://www.grainger.com/product/OWENS-CORNING-1-2-Thick-40PP22 # but modified until simulated heat loss = 17.7 Btu/hr/ft of pipe with 140F water and 70F air pipe_insulation_thickness_in = OpenStudio.convert(pipe_insulation_thickness, 'm', 'in').get insulation = OpenStudio::Model::StandardOpaqueMaterial.new(model) insulation.setName("Fiberglass batt #{pipe_insulation_thickness_in.round(2)}in") insulation.setRoughness('Smooth') insulation.setThickness(OpenStudio.convert(pipe_insulation_thickness_in, 'in', 'm').get) insulation.setThermalConductivity(OpenStudio.convert(0.46, 'Btu*in/hr*ft^2*R', 'W/m*K').get) insulation.setDensity(OpenStudio.convert(0.7, 'lb/ft^3', 'kg/m^3').get) insulation.setSpecificHeat(OpenStudio.convert(0.2, 'Btu/lb*R', 'J/kg*K').get) insulation.setThermalAbsorptance(0.9) # Irrelevant for Pipe:Indoor; no radiation model is used insulation.setSolarAbsorptance(0.7) # Irrelevant for Pipe:Indoor; no radiation model is used insulation.setVisibleAbsorptance(0.7) # Irrelevant for Pipe:Indoor; no radiation model is used pipe_construction.setName("Copper pipe 0.75in type L with #{pipe_insulation_thickness_in.round(2)}in fiberglass batt") pipe_construction.setLayers([insulation, copper_pipe]) else pipe_construction.setName('Uninsulated copper pipe 0.75in type L') pipe_construction.setLayers([copper_pipe]) end heat_loss_pipe = OpenStudio::Model::PipeIndoor.new(model) heat_loss_pipe.setName("#{service_water_loop.name} Pipe #{pipe_length_ft.round}ft") heat_loss_pipe.setEnvironmentType('Schedule') heat_loss_pipe.setAmbientTemperatureSchedule(swh_piping_air_temp_sch) heat_loss_pipe.setAmbientAirVelocitySchedule(swh_piping_air_velocity_sch) heat_loss_pipe.setConstruction(pipe_construction) heat_loss_pipe.setPipeInsideDiameter(OpenStudio.convert(0.785, 'in', 'm').get) heat_loss_pipe.setPipeLength(OpenStudio.convert(pipe_length_ft, 'ft', 'm').get) heat_loss_pipe.addToNode(service_water_loop.demandInletNode) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Added #{pipe_length_ft.round}ft of #{pipe_construction.name} losing heat to #{air_temperature_f.round}F air to #{service_water_loop.name}.") return true end |
.create_typical_service_water_heating(model, water_heating_fuel: nil, circulating: nil) ⇒ Array<OpenStudio::Model::PlantLoop>
add support for other loop configurations, such as by space type, space type adjacent, or building type
add typical swh demand and supply to model
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 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 |
# File 'lib/openstudio-standards/service_water_heating/create_typical.rb', line 16 def self.create_typical_service_water_heating(model, water_heating_fuel: nil, circulating: nil) # array of service hot water loops swh_systems = [] # water use equipment on the building loop shared_water_use_equipment = [] booster_water_use_equipment = [] # @todo remove once model_add_schedule is refactored std = Standard.build('90.1-2013') # loop through space types adding demand side of swh model.getSpaces.sort.each do |space| next unless space.spaceType.is_initialized total_space_floor_area_m2 = space.floorArea * space.multiplier total_space_floor_area_ft2 = OpenStudio.convert(total_space_floor_area_m2, 'm^2', 'ft^2').get space_type = space.spaceType.get next unless space_type.standardsSpaceType.is_initialized next unless space_type.standardsBuildingType.is_initialized standards_space_type = space_type.standardsSpaceType.get standards_building_type = space_type.standardsBuildingType.get # load typical water use equipment data data = JSON.parse(File.read("#{File.dirname(__FILE__)}/data/typical_water_use_equipment.json"), symbolize_names: true) space_type_properties = data[:space_types].select { |hash| (hash[:space_type] == standards_space_type) && (hash[:building_type] == standards_building_type) } # skip spaces with no equipment defined next if space_type_properties.empty? water_use_equipment = space_type_properties[0][:water_use_equipment] # store one per unit equipment space_water_use_equipment = [] if space.hasAdditionalProperties && space.additionalProperties.hasFeature('num_units') num_units = space.additionalProperties.getFeatureAsInteger('num_units').get else # assume 1 space is 1 unit num_units = space.multiplier end # loop through and add water use equipment to space water_use_equipment.each do |w| # get water use equipment properties water_use_name = w[:equipment_name] peak_flow_rate_gal_per_hr = w[:peak_flow_rate_gph].to_f peak_flow_rate_gal_per_hr_per_ft2 = w[:peak_flow_rate_gph_per_floor_area_ft2].to_f loop_type = w[:loop_type] temperature = w[:mixed_water_temperature_f] flow_rate_schedule = w[:flow_rate_schedule] sensible_fraction = w[:sensible_fraction] latent_fraction = w[:latent_fraction] # derived from equipment properties is_booster = water_use_name && water_use_name.downcase.include?('booster') water_use_name = water_use_name ? "#{space.name} #{water_use_name}" : "#{space.name} Water Use" mixed_water_temperature_c = OpenStudio.convert(temperature, 'F', 'C').get # @todo replace this line once model_add_schedule is refactored to not require a standard flow_rate_schedule = std.model_add_schedule(model, flow_rate_schedule) # skip undefined equipment next unless peak_flow_rate_gal_per_hr > 0.0 || peak_flow_rate_gal_per_hr_per_ft2 > 0.0 # If there is no SWH schedule, assume no SWH use for this space type. unless flow_rate_schedule OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.ServiceWaterHeating', "No service water heating schedule was specified for space type #{space_type.name} with standards space type #{standards_space_type}. Assuming an always off schedule.") flow_rate_schedule = model.alwaysOffDiscreteSchedule end # Determine the peak flow rate and default water heating fuel case loop_type when 'One Per Unit' # calculate peak flow rate if peak_flow_rate_gal_per_hr.zero? && peak_flow_rate_gal_per_hr_per_ft2 > 0.0 peak_flow_rate_gal_per_hr = num_units * peak_flow_rate_gal_per_hr_per_ft2 * total_space_floor_area_ft2 else peak_flow_rate_gal_per_hr *= num_units end # update water use name water_use_name = "#{water_use_name} #{num_units} unit(s)" else # calculate peak flow rate if peak_flow_rate_gal_per_hr.zero? && peak_flow_rate_gal_per_hr_per_ft2 > 0.0 peak_flow_rate_gal_per_hr = peak_flow_rate_gal_per_hr_per_ft2 * total_space_floor_area_ft2 end end # convert to SI peak_flow_rate_m3_per_s = OpenStudio.convert(peak_flow_rate_gal_per_hr, 'gal/hr', 'm^3/s').get # create water use equipment water_use_equip = OpenstudioStandards::ServiceWaterHeating.create_water_use(model, name: water_use_name, flow_rate: peak_flow_rate_m3_per_s, flow_rate_fraction_schedule: flow_rate_schedule, water_use_temperature: mixed_water_temperature_c, sensible_fraction: sensible_fraction, latent_fraction: latent_fraction, space: space) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Added water use equipment #{water_use_equip.name}") # create service hot water loop for 'One Per Space' and 'One Per Unit' dedicated equipment case loop_type when 'Shared' if is_booster booster_water_use_equipment << water_use_equip else shared_water_use_equipment << water_use_equip end when 'One Per Space', 'One Per Unit' space_water_use_equipment << water_use_equip else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.ServiceWaterHeating', "Water use equipment service loop type #{loop_type} not recognized. Cannot attach equipment to a loop.") end end # create water loop for 'One Per Space' and 'One Per Unit' equipment unless space_water_use_equipment.empty? water_heater_capacity_w = num_units * OpenStudio.convert(20.0, 'kBtu/hr', 'W').get water_heater_volume_m3 = num_units * OpenStudio.convert(50.0, 'gal', 'm^3').get num_water_heaters = num_units # default to electricity for single units dedicated_water_heating_fuel = water_heating_fuel || 'Electricity' # default to 140F service_water_loop_temperature_c = OpenStudio.convert(140.0, 'F', 'C').get # add service water loop with water heater swh_loop = OpenstudioStandards::ServiceWaterHeating.create_service_water_heating_loop(model, system_name: "#{space.name} Service Water Loop", service_water_temperature: service_water_loop_temperature_c, service_water_pump_head: 0.01, service_water_pump_motor_efficiency: 1.0, water_heater_capacity: water_heater_capacity_w, water_heater_volume: water_heater_volume_m3, water_heater_fuel: dedicated_water_heating_fuel, number_of_water_heaters: num_water_heaters, add_piping_losses: true, floor_area: total_space_floor_area_m2, number_of_stories: 1) # add loop to array swh_systems << swh_loop # Attach water use equipment to the loop space_water_use_equipment.each do |water_use_equip| swh_connection = water_use_equip.waterUseConnections swh_loop.addDemandBranchForComponent(swh_connection.get) if swh_connection.is_initialized end end end ############################################################################ # default to gas for shared system types and booster systems shared_water_heating_fuel = water_heating_fuel || 'NaturalGas' booster_water_heating_fuel = water_heating_fuel || 'Electricity' # @todo get maximum service water temperature from shared_water_use_equipment water_heater_temp_f = 140.0 water_heater_temp_c = OpenStudio.convert(water_heater_temp_f, 'F', 'C').get # defaults for circulating or noncirculating systems # @todo Remove hard-coded building-type-based lookups for circulating vs. non-circulating SWH systems if circulating.nil? if model.getBuilding.standardsBuildingType.is_initialized circulating = OpenstudioStandards::ServiceWaterHeating.circulating_building_type?(model.getBuilding.standardsBuildingType.get) else OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.ServiceWaterHeating', 'Unable to determine the standards building type. Assuming the building does not have a circulating service water heating loop.') circulating = false end end # create a shared water loop unless shared_water_use_equipment.empty? && booster_water_use_equipment.empty? if circulating # Table A.2 in PrototypeModelEnhancements_2014_0.pdf shows 10ft on everything except SecondarySchool which has 11.4ft service_water_pump_head_pa = OpenStudio.convert(10.0, 'ftH_{2}O', 'Pa').get service_water_pump_motor_efficiency = 0.3 else service_water_pump_head_pa = 0.01 service_water_pump_motor_efficiency = 1.0 end water_heater_sizing = OpenstudioStandards::ServiceWaterHeating.water_heater_sizing_from_water_use_equipment(shared_water_use_equipment) water_heater_capacity_w = water_heater_sizing[:water_heater_capacity] water_heater_volume_m3 = water_heater_sizing[:water_heater_volume] # Add a shared service water heating loop with water heater shared_swh_loop = OpenstudioStandards::ServiceWaterHeating.create_service_water_heating_loop(model, system_name: 'Shared Service Water Loop', service_water_temperature: water_heater_temp_c, service_water_pump_head: service_water_pump_head_pa, service_water_pump_motor_efficiency: service_water_pump_motor_efficiency, water_heater_capacity: water_heater_capacity_w, water_heater_volume: water_heater_volume_m3, water_heater_fuel: shared_water_heating_fuel, add_piping_losses: true) # Add loop to array swh_systems << shared_swh_loop # Attach all water use equipment to the shared loop shared_water_use_equipment.sort.each do |water_use_equip| swh_connection = water_use_equip.waterUseConnections shared_swh_loop.addDemandBranchForComponent(swh_connection.get) if swh_connection.is_initialized end # Attach booster water heater loop to shared loop unless booster_water_use_equipment.empty? # find_water_heater_capacity_volume_and_parasitic booster_water_heater_sizing = OpenstudioStandards::ServiceWaterHeating.water_heater_sizing_from_water_use_equipment(booster_water_use_equipment, water_heater_efficiency: 1.0, inlet_temperature: 140.0, supply_temperature: 180.0) # Note that booster water heaters are always assumed to be electric resistance booster_water_loop_temperature_c = OpenStudio.convert(180.0, 'F', 'C').get swh_booster_loop = OpenstudioStandards::ServiceWaterHeating.create_booster_water_heating_loop(model, system_name: 'Booster Water Loop', water_heater_capacity: booster_water_heater_sizing[:water_heater_capacity], service_water_temperature: booster_water_loop_temperature_c, service_water_loop: shared_swh_loop) # Add loop to array swh_systems << swh_booster_loop # Attach booster water use equipment to the booster loop booster_water_use_equipment.each do |booster_equip| booster_swh_connection = booster_equip.waterUseConnections swh_booster_loop.addDemandBranchForComponent(booster_swh_connection.get) if booster_swh_connection.is_initialized end end end return swh_systems end |
.create_water_heater(model, water_heater_capacity: nil, water_heater_volume: nil, water_heater_fuel: 'Electricity', on_cycle_parasitic_fuel_consumption_rate: 0.0, off_cycle_parasitic_fuel_consumption_rate: 0.0, service_water_temperature: 60.0, service_water_temperature_schedule: nil, set_peak_use_flowrate: false, peak_flowrate: nil, flowrate_schedule: nil, water_heater_thermal_zone: nil, number_of_water_heaters: 1, service_water_loop: nil) ⇒ OpenStudio::Model::WaterHeaterMixed
Creates a water heater and attaches it to the supplied service water heating loop.
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 |
# File 'lib/openstudio-standards/service_water_heating/create_water_heater.rb', line 28 def self.create_water_heater(model, water_heater_capacity: nil, water_heater_volume: nil, water_heater_fuel: 'Electricity', on_cycle_parasitic_fuel_consumption_rate: 0.0, off_cycle_parasitic_fuel_consumption_rate: 0.0, service_water_temperature: 60.0, service_water_temperature_schedule: nil, set_peak_use_flowrate: false, peak_flowrate: nil, flowrate_schedule: nil, water_heater_thermal_zone: nil, number_of_water_heaters: 1, service_water_loop: nil) # create water heater object # @todo Standards - Change water heater methodology to follow 'Model Enhancements Appendix A.' water_heater = OpenStudio::Model::WaterHeaterMixed.new(model) # default water heater capacity if nil if water_heater_capacity.nil? water_heater_capacity = OpenStudio.convert(200.0, 'kBtu/hr', 'W').get end water_heater_capacity_kbtu_per_hr = OpenStudio.convert(water_heater_capacity, 'W', 'kBtu/hr').get water_heater.setHeaterMaximumCapacity(water_heater_capacity) # default water heater volume if nil if water_heater_volume.nil? water_heater_volume = OpenStudio.convert(100.0, 'gal', 'm^3').get end water_heater_volume_gal = OpenStudio.convert(water_heater_volume, 'm^3', 'gal').get water_heater.setTankVolume(water_heater_volume) water_heating_sizing = water_heater.waterHeaterSizing # set the water heater fuel case water_heater_fuel when 'Natural Gas', 'NaturalGas', 'Gas' water_heater.setHeaterFuelType('Gas') water_heater.setHeaterThermalEfficiency(0.78) water_heater.setOnCycleParasiticFuelConsumptionRate(on_cycle_parasitic_fuel_consumption_rate) water_heater.setOffCycleParasiticFuelConsumptionRate(off_cycle_parasitic_fuel_consumption_rate) water_heater.setOnCycleParasiticFuelType('Gas') water_heater.setOffCycleParasiticFuelType('Gas') water_heater.setOffCycleLossCoefficienttoAmbientTemperature(6.0) water_heater.setOnCycleLossCoefficienttoAmbientTemperature(6.0) # assume 0.5 hour recovery temperature for natural gas water heaters water_heating_sizing.setDesignMode('PeakDraw') water_heating_sizing.setTimeforTankRecovery(0.5) when 'Electricity', 'Electric', 'Elec' water_heater.setHeaterFuelType('Electricity') water_heater.setHeaterThermalEfficiency(1.0) water_heater.setOnCycleParasiticFuelConsumptionRate(on_cycle_parasitic_fuel_consumption_rate) water_heater.setOffCycleParasiticFuelConsumptionRate(off_cycle_parasitic_fuel_consumption_rate) water_heater.setOnCycleParasiticFuelType('Electricity') water_heater.setOffCycleParasiticFuelType('Electricity') water_heater.setOffCycleLossCoefficienttoAmbientTemperature(1.053) water_heater.setOnCycleLossCoefficienttoAmbientTemperature(1.053) # assume 1.0 hour recovery temperature for electric water heaters water_heating_sizing.setDesignMode('PeakDraw') water_heating_sizing.setTimeforTankRecovery(1.0) when 'FuelOilNo2' water_heater.setHeaterFuelType('FuelOilNo2') water_heater.setHeaterThermalEfficiency(0.78) water_heater.setOnCycleParasiticFuelConsumptionRate(on_cycle_parasitic_fuel_consumption_rate) water_heater.setOffCycleParasiticFuelConsumptionRate(off_cycle_parasitic_fuel_consumption_rate) water_heater.setOnCycleParasiticFuelType('FuelOilNo2') water_heater.setOffCycleParasiticFuelType('FuelOilNo2') water_heater.setOffCycleLossCoefficienttoAmbientTemperature(6.0) water_heater.setOnCycleLossCoefficienttoAmbientTemperature(6.0) # assume 0.5 hour recovery temperature for fuel oil water heaters water_heating_sizing.setDesignMode('PeakDraw') water_heating_sizing.setTimeforTankRecovery(0.5) when 'HeatPump', 'SimpleHeatPump' OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.ServiceWaterHeating', 'Simple workaround to represent heat pump water heaters without incurring significant runtime penalty associated with using correct objects.') # Make a part-load efficiency modifier curve with a value above 1, which is multiplied by the nominal efficiency of 100% to represent the COP of a HPWH. # @todo could make this workaround better by using EMS to modify this curve output in realtime based on the OA temperature. hpwh_cop = 2.8 water_heater.setHeaterFuelType('Electricity') water_heater.setHeaterThermalEfficiency(1.0) eff_f_of_plr = OpenStudio::Model::CurveCubic.new(model) eff_f_of_plr.setName("HPWH_COP_#{hpwh_cop}") eff_f_of_plr.setCoefficient1Constant(hpwh_cop) eff_f_of_plr.setCoefficient2x(0.0) eff_f_of_plr.setCoefficient3xPOW2(0.0) eff_f_of_plr.setCoefficient4xPOW3(0.0) eff_f_of_plr.setMinimumValueofx(0.0) eff_f_of_plr.setMaximumValueofx(1.0) water_heater.setPartLoadFactorCurve(eff_f_of_plr) water_heater.setOnCycleParasiticFuelConsumptionRate(on_cycle_parasitic_fuel_consumption_rate) water_heater.setOffCycleParasiticFuelConsumptionRate(off_cycle_parasitic_fuel_consumption_rate) water_heater.setOnCycleParasiticFuelType('Electricity') water_heater.setOffCycleParasiticFuelType('Electricity') water_heater.setOffCycleLossCoefficienttoAmbientTemperature(1.053) water_heater.setOnCycleLossCoefficienttoAmbientTemperature(1.053) else OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.ServiceWaterHeating', "#{water_heater_fuel} is not a valid water heater fuel. Valid choices are NaturalGas, Electricity, and HeatPump.") end # set water temperature properties water_heater.setDeadbandTemperatureDifference(2.0) water_heater.setDeadbandTemperatureDifference(OpenStudio.convert(3.6, 'R', 'K').get) water_heater.setHeaterControlType('Cycle') water_heater.setOffCycleParasiticHeatFractiontoTank(0.8) water_heater.setIndirectWaterHeatingRecoveryTime(1.5) # 1.5hrs # create service water temperature schedule based on the service_water_temperature if none provided if service_water_temperature_schedule.nil? swh_temp_c = service_water_temperature swh_temp_f = OpenStudio.convert(swh_temp_c, 'C', 'F').get service_water_temperature_schedule = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model, swh_temp_c, name: "Service Water Loop Temp - #{swh_temp_f.round}F", schedule_type_limit: 'Temperature') end water_heater.setMaximumTemperatureLimit(service_water_temperature) water_heater.setSetpointTemperatureSchedule(service_water_temperature_schedule) # set peak flow rate characteristics if set_peak_use_flowrate water_heater.setPeakUseFlowRate(peak_flowrate) unless peak_flowrate.nil? water_heater.setUseFlowRateFractionSchedule(flowrate_schedule) unless flowrate_schedule.nil? end # set the water heater ambient conditions if water_heater_thermal_zone.nil? # assume the water heater is indoors at 71.6F / 22C indoor_temp_f = 71.6 indoor_temp_c = OpenStudio.convert(indoor_temp_f, 'F', 'C').get default_water_heater_ambient_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model, indoor_temp_c, name: "Water Heater Ambient Temp Schedule #{indoor_temp_f}F", schedule_type_limit: 'Temperature') water_heater.setAmbientTemperatureIndicator('Schedule') water_heater.setAmbientTemperatureSchedule(default_water_heater_ambient_temp_sch) water_heater.resetAmbientTemperatureThermalZone else water_heater.setAmbientTemperatureIndicator('ThermalZone') water_heater.setAmbientTemperatureThermalZone(water_heater_thermal_zone) water_heater.resetAmbientTemperatureSchedule end # assign a quantity to the water heater if it represents multiple water heaters if number_of_water_heaters > 1 water_heater.setName("#{number_of_water_heaters}X #{(water_heater_volume_gal / number_of_water_heaters).round}gal #{water_heater_fuel} Water Heater - #{(water_heater_capacity_kbtu_per_hr / number_of_water_heaters).round}kBtu/hr") water_heater.additionalProperties.setFeature('component_quantity', number_of_water_heaters) else water_heater.setName("#{water_heater_volume_gal.round}gal #{water_heater_fuel} Water Heater - #{water_heater_capacity_kbtu_per_hr.round}kBtu/hr") end # add the water heater to the service water loop if provided unless service_water_loop.nil? service_water_loop.addSupplyBranchForComponent(water_heater) end OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Added water heater called #{water_heater.name}") return water_heater end |
.create_water_use(model, name: 'Main Water Use', flow_rate: 0.0, flow_rate_fraction_schedule: nil, water_use_temperature: 43.3, water_use_temperature_schedule: nil, sensible_fraction: 0.2, latent_fraction: 0.05, service_water_loop: nil, space: nil) ⇒ OpenStudio::Model::WaterUseEquipment
Creates a water use and attaches it to a service water loop and a space, if provided
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 |
# File 'lib/openstudio-standards/service_water_heating/create_water_use.rb', line 21 def self.create_water_use(model, name: 'Main Water Use', flow_rate: 0.0, flow_rate_fraction_schedule: nil, water_use_temperature: 43.3, water_use_temperature_schedule: nil, sensible_fraction: 0.2, latent_fraction: 0.05, service_water_loop: nil, space: nil) # IP conversions for naming flow_rate_gpm = OpenStudio.convert(flow_rate, 'm^3/s', 'gal/min').get water_use_temperature_f = OpenStudio.convert(water_use_temperature, 'C', 'F').get # default name name = 'Main Water Use' if name.nil? # water use definition water_use_def = OpenStudio::Model::WaterUseEquipmentDefinition.new(model) # set sensible and latent fractions water_use_sensible_frac_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model, sensible_fraction, name: "Fraction Sensible - #{sensible_fraction}", schedule_type_limit: 'Fractional') water_use_latent_frac_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model, latent_fraction, name: "Fraction Latent - #{latent_fraction}", schedule_type_limit: 'Fractional') water_use_def.setSensibleFractionSchedule(water_use_sensible_frac_sch) water_use_def.setLatentFractionSchedule(water_use_latent_frac_sch) water_use_def.setPeakFlowRate(flow_rate) water_use_def.setName("#{name} #{flow_rate_gpm.round(2)}gpm #{water_use_temperature_f.round}F") # target mixed water temperature if water_use_temperature_schedule.nil? water_use_temperature_schedule = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model, water_use_temperature, name: "Mixed Water At Faucet Temp - #{water_use_temperature_f.round}F", schedule_type_limit: 'Temperature') end water_use_def.setTargetTemperatureSchedule(water_use_temperature_schedule) # create water use equipment water_fixture = OpenStudio::Model::WaterUseEquipment.new(water_use_def) water_fixture.setFlowRateFractionSchedule(flow_rate_fraction_schedule) # create water use connection swh_connection = OpenStudio::Model::WaterUseConnections.new(model) swh_connection.addWaterUseEquipment(water_fixture) # add to the space if provided if space.nil? water_fixture.setName("#{name} Service Water Use #{flow_rate_gpm.round(2)}gpm #{water_use_temperature_f.round}F") swh_connection.setName("#{name} WUC #{flow_rate_gpm.round(2)}gpm #{water_use_temperature_f.round}F") else water_fixture.setName("#{space.name} Service Water Use #{flow_rate_gpm.round(2)}gpm #{water_use_temperature_f.round}F") swh_connection.setName("#{space.name} WUC #{flow_rate_gpm.round(2)}gpm #{water_use_temperature_f.round}F") water_fixture.setSpace(space) end # add to the service water loop if provided unless service_water_loop.nil? service_water_loop.addDemandBranchForComponent(swh_connection) OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Adding water fixture to #{service_water_loop.name}.") end OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Added #{water_fixture.name}.") return water_fixture end |
.water_heater_sizing_from_water_use_equipment(water_use_equipment_array, capacity_to_volume_ratio: 1.0, water_heater_efficiency: 0.8, inlet_temperature: 40.0, supply_temperature: 140.0, peak_flow_fraction: 1.0, minimum_volume: 40.0) ⇒ Hash
Determines water heater capacity and volume from an array of water use equipment.
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 |
# File 'lib/openstudio-standards/service_water_heating/information.rb', line 17 def self.water_heater_sizing_from_water_use_equipment(water_use_equipment_array, capacity_to_volume_ratio: 1.0, water_heater_efficiency: 0.8, inlet_temperature: 40.0, supply_temperature: 140.0, peak_flow_fraction: 1.0, minimum_volume: 40.0) # Initialize hash water_heater_sizing = {} # Get the maximum flow rates for all pieces of water use equipment adjusted_max_flow_rates_gal_per_hr = [] water_use_equipment_array.sort.each do |water_use_equip| water_use_equip_sch = water_use_equip.flowRateFractionSchedule next if water_use_equip_sch.empty? water_use_equip_sch = water_use_equip_sch.get max_sch_value = OpenstudioStandards::Schedules.schedule_get_min_max(water_use_equip_sch)['max'] # Get peak flow rate from water use equipment definition peak_flow_rate_m3_per_s = water_use_equip.waterUseEquipmentDefinition.peakFlowRate # Calculate adjusted flow rate based on the peak fraction found in the flow rate fraction schedule adjusted_peak_flow_rate_m3_per_s = max_sch_value * peak_flow_rate_m3_per_s adjusted_max_flow_rates_gal_per_hr << OpenStudio.convert(adjusted_peak_flow_rate_m3_per_s, 'm^3/s', 'gal/hr').get end # Sum gph values from water use equipment to use in formula total_adjusted_flow_rate_gal_per_hr = adjusted_max_flow_rates_gal_per_hr.sum # Calculate capacity based on analysis of combined water use equipment maximum flow rates and schedules # Max gal/hr * 8.4 lb/gal * 1 Btu/lb F * (120F - 40F)/0.8 = Btu/hr water_heater_capacity_btu_per_hr = peak_flow_fraction * total_adjusted_flow_rate_gal_per_hr * 8.4 * 1.0 * (supply_temperature - inlet_temperature) / water_heater_efficiency OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Capacity of #{water_heater_capacity_btu_per_hr.round} Btu/hr = #{peak_flow_fraction} peak fraction * #{total_adjusted_flow_rate_gal_per_hr.round} gal/hr * 8.4 lb/gal * 1.0 Btu/lb F * (#{supply_temperature.round} - #{inlet_temperature.round} deltaF / #{water_heater_efficiency} htg eff).") water_heater_capacity_w = OpenStudio.convert(water_heater_capacity_btu_per_hr, 'Btu/hr', 'W').get # Calculate volume based on capacity # A.1.4 Total Storage Volume and Water Heater Capacity of PrototypeModelEnhancements_2014_0.pdf shows 1 gallon of storage to 1 kBtu/h of capacity water_heater_capacity_kbtu_per_hr = OpenStudio.convert(water_heater_capacity_btu_per_hr, 'Btu/hr', 'kBtu/hr').get water_heater_volume_gal = water_heater_capacity_kbtu_per_hr * capacity_to_volume_ratio # increase tank size to the minimum volume if calculated value is smaller water_heater_volume_gal = minimum_volume if water_heater_volume_gal < minimum_volume # gal water_heater_volume_m3 = OpenStudio.convert(water_heater_volume_gal, 'gal', 'm^3').get # Populate return hash water_heater_sizing[:water_heater_capacity] = water_heater_capacity_w water_heater_sizing[:water_heater_volume] = water_heater_volume_m3 return water_heater_sizing end |