openstudio-standards 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.md +33 -0
- data/data/inventory/thermal_bridging.csv +90 -0
- data/data/standards/OpenStudio_Standards-deer-comstock.xlsx +0 -0
- data/data/standards/manage_OpenStudio_Standards.rb +1 -1
- data/data/standards/test_performance_expected_dd_results.csv +2014 -1891
- data/lib/openstudio-standards/btap/analysis.rb +8 -8
- data/lib/openstudio-standards/btap/bridging.rb +664 -645
- data/lib/openstudio-standards/btap/btap.model.rb +14 -14
- data/lib/openstudio-standards/btap/btap.rb +7 -7
- data/lib/openstudio-standards/btap/btap_result.rb +1 -1
- data/lib/openstudio-standards/btap/economics.rb +23 -23
- data/lib/openstudio-standards/btap/envelope.rb +8 -8
- data/lib/openstudio-standards/btap/equest.rb +1 -1
- data/lib/openstudio-standards/btap/geometry.rb +2 -2
- data/lib/openstudio-standards/btap/mpc.rb +7 -7
- data/lib/openstudio-standards/btap/schedules.rb +1 -1
- data/lib/openstudio-standards/btap/simmanager.rb +4 -4
- data/lib/openstudio-standards/btap/spaceloads.rb +26 -26
- data/lib/openstudio-standards/btap/utilities.rb +6 -6
- data/lib/openstudio-standards/btap/vintagizer.rb +1 -1
- data/lib/openstudio-standards/constructions/information.rb +83 -0
- data/lib/openstudio-standards/constructions/materials/modify.rb +72 -0
- data/lib/openstudio-standards/constructions/modify.rb +80 -0
- data/lib/openstudio-standards/create_typical/create_typical.rb +983 -0
- data/lib/openstudio-standards/create_typical/enumerations.rb +484 -0
- data/lib/openstudio-standards/create_typical/space_type_blend.rb +791 -0
- data/lib/openstudio-standards/create_typical/space_type_ratios.rb +494 -0
- data/lib/openstudio-standards/daylighting/space.rb +47 -0
- data/lib/openstudio-standards/geometry/create.rb +801 -0
- data/lib/openstudio-standards/geometry/create_bar.rb +2170 -0
- data/lib/openstudio-standards/geometry/information.rb +462 -0
- data/lib/openstudio-standards/geometry/modify.rb +48 -0
- data/lib/openstudio-standards/hvac/air_loop/information.rb +79 -0
- data/lib/openstudio-standards/hvac/cbecs_hvac.rb +616 -0
- data/lib/openstudio-standards/hvac/setpoint_managers/information.rb +91 -0
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2007/ashrae_90_1_2007.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2010/ashrae_90_1_2010.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2010/ashrae_90_1_2010.Model.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.Model.rb +2 -2
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.hvac_systems.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Model.rb +4 -36
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.hvac_systems.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb +4 -36
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb +3 -3
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.hvac_systems.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.Model.elevators.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.CoilHeatingGas.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.Model.elevators.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/nrel_nze_ready_2017/nrel_zne_ready_2017.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ze_aedg_multifamily/ze_aedg_multifamily.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.College.rb +7 -7
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.Courthouse.rb +8 -8
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.FullServiceRestaurant.rb +14 -14
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.HighRiseApartment.rb +9 -9
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.Hospital.rb +16 -16
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.Laboratory.rb +7 -7
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.LargeDataCenterHighITE.rb +8 -8
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.LargeDataCenterLowITE.rb +8 -8
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.LargeHotel.rb +11 -11
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.LargeOffice.rb +7 -7
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.LargeOfficeDetailed.rb +9 -9
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.MediumOffice.rb +8 -8
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.MediumOfficeDetailed.rb +11 -11
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.MidriseApartment.rb +9 -9
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.Outpatient.rb +19 -19
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.PrimarySchool.rb +10 -10
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.QuickServiceRestaurant.rb +13 -13
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.RetailStandalone.rb +6 -6
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.RetailStripmall.rb +6 -6
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SecondarySchool.rb +9 -9
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SmallDataCenterHighITE.rb +8 -8
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SmallDataCenterLowITE.rb +8 -8
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SmallHotel.rb +8 -8
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SmallOffice.rb +8 -8
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SmallOfficeDetailed.rb +11 -11
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SuperMarket.rb +10 -10
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SuperTallBuilding.rb +19 -19
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.TallBuilding.rb +18 -18
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.Warehouse.rb +6 -6
- data/lib/openstudio-standards/prototypes/common/do_not_edit_metaclasses.rb +957 -957
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.AirConditionerVariableRefrigerantFlow.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.CoilCoolingWaterToAirHeatPumpEquationFit.rb +84 -16
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.CoilHeatingDXSingleSpeed.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.CoilHeatingGas.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.CoilHeatingWaterToAirHeatPumpEquationFit.rb +61 -10
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.ControllerWaterCoil.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.CoolingTower.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.Fan.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.FanConstantVolume.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.FanOnOff.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.FanZoneExhaust.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.HeatExchangerAirToAirSensibleAndLatent.rb +2 -2
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.exterior_lights.rb +4 -4
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.hvac.rb +4 -4
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.rb +43 -30
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.swh.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.ServiceWaterHeating.rb +18 -11
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.SizingSystem.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +774 -117
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.radiant_system_controls.rb +340 -481
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.refrigeration.rb +3 -3
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.utilities.rb +3 -3
- data/lib/openstudio-standards/prototypes/common/prototype_metaprogramming.rb +22 -22
- data/lib/openstudio-standards/prototypes/deer/deer.Model.rb +1 -1
- data/lib/openstudio-standards/qaqc/calibration.rb +131 -0
- data/lib/openstudio-standards/qaqc/create_results.rb +983 -0
- data/lib/openstudio-standards/qaqc/envelope.rb +399 -0
- data/lib/openstudio-standards/qaqc/eui.rb +213 -0
- data/lib/openstudio-standards/qaqc/hvac.rb +1943 -0
- data/lib/openstudio-standards/qaqc/internal_loads.rb +568 -0
- data/lib/openstudio-standards/qaqc/reporting.rb +141 -0
- data/lib/openstudio-standards/qaqc/schedules.rb +129 -0
- data/lib/openstudio-standards/qaqc/service_water_heating.rb +273 -0
- data/lib/openstudio-standards/qaqc/weather_files.rb +497 -0
- data/lib/openstudio-standards/qaqc/zone_conditions.rb +278 -0
- data/lib/openstudio-standards/schedules/create.rb +364 -0
- data/lib/openstudio-standards/schedules/information.rb +169 -0
- data/lib/openstudio-standards/schedules/modify.rb +445 -0
- data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +110 -71
- data/lib/openstudio-standards/standards/Standards.AirTerminalSingleDuctParallelPIUReheat.rb +3 -3
- data/lib/openstudio-standards/standards/Standards.AirTerminalSingleDuctVAVReheat.rb +4 -4
- data/lib/openstudio-standards/standards/Standards.BoilerHotWater.rb +2 -1
- data/lib/openstudio-standards/standards/Standards.ChillerElectricEIR.rb +16 -10
- data/lib/openstudio-standards/standards/Standards.CoilCoolingDXSingleSpeed.rb +4 -4
- data/lib/openstudio-standards/standards/Standards.CoilCoolingDXTwoSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.CoilCoolingWaterToAirHeatPumpEquationFit.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.CoilDX.rb +4 -4
- data/lib/openstudio-standards/standards/Standards.CoilHeatingDXMultiSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +5 -5
- data/lib/openstudio-standards/standards/Standards.CoilHeatingGas.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.CoilHeatingGasMultiStage.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.CoilHeatingWaterToAirHeatPumpEquationFit.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.Construction.rb +17 -18
- data/lib/openstudio-standards/standards/Standards.CoolingTower.rb +6 -6
- data/lib/openstudio-standards/standards/Standards.CoolingTowerSingleSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.CoolingTowerTwoSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.CoolingTowerVariableSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.Fan.rb +6 -12
- data/lib/openstudio-standards/standards/Standards.FanVariableVolume.rb +2 -2
- data/lib/openstudio-standards/standards/Standards.FluidCooler.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.HeaderedPumpsVariableSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.HeatExchangerSensLat.rb +3 -3
- data/lib/openstudio-standards/standards/Standards.Model.rb +411 -261
- data/lib/openstudio-standards/standards/Standards.PlanarSurface.rb +2 -2
- data/lib/openstudio-standards/standards/Standards.PlantLoop.rb +94 -29
- data/lib/openstudio-standards/standards/Standards.Pump.rb +2 -2
- data/lib/openstudio-standards/standards/Standards.ScheduleConstant.rb +2 -2
- data/lib/openstudio-standards/standards/Standards.ScheduleRuleset.rb +14 -14
- data/lib/openstudio-standards/standards/Standards.Space.rb +37 -30
- data/lib/openstudio-standards/standards/Standards.SpaceType.rb +38 -29
- data/lib/openstudio-standards/standards/Standards.SubSurface.rb +7 -7
- data/lib/openstudio-standards/standards/Standards.Surface.rb +13 -13
- data/lib/openstudio-standards/standards/Standards.ThermalZone.rb +109 -66
- data/lib/openstudio-standards/standards/Standards.WaterHeaterMixed.rb +11 -4
- data/lib/openstudio-standards/standards/Standards.ZoneHVACComponent.rb +6 -6
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.AirLoopHVAC.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.PlantLoop.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.Space.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/ashrae_90_1_2007.Space.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/ashrae_90_1_2010.AirLoopHVAC.rb +4 -4
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/ashrae_90_1_2010.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/ashrae_90_1_2010.Space.rb +4 -4
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.AirLoopHVAC.rb +4 -4
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.CoolingTowerVariableSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.Model.rb +5 -21
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.Space.rb +4 -4
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.WaterHeaterMixed.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/ashrae_90_1_2013.ZoneHVACComponent.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.AirLoopHVAC.rb +36 -4
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.CoolingTowerVariableSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Space.rb +4 -4
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.ZoneHVACComponent.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/comstock_ashrae_90_1_2016/comstock_ashrae_90_1_2016.AirLoopHVAC.rb +26 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +53 -10
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.CoolingTowerVariableSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Space.rb +6 -6
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.ZoneHVACComponent.rb +2 -2
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/comstock_ashrae_90_1_2019/comstock_ashrae_90_1_2019.AirLoopHVAC.rb +26 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.curves.json +211 -211
- data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/data/doe_ref_1980_2004.economizers.json +14 -14
- data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.AirLoopHVAC.rb +4 -4
- data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.Model.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.PlantLoop.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/data/doe_ref_pre_1980.economizers.json +14 -14
- data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.AirLoopHVAC.rb +4 -4
- data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.Model.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.PlantLoop.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/nrel_zne_ready_2017.AirLoopHVAC.rb +6 -6
- data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/nrel_zne_ready_2017.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/nrel_zne_ready_2017.CoolingTowerVariableSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/nrel_zne_ready_2017.PlantLoop.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/nrel_zne_ready_2017.Space.rb +4 -4
- data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/ze_aedg_multifamily.AirLoopHVAC.rb +6 -6
- data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/ze_aedg_multifamily.AirTerminalSingleDuctVAVReheat.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/ze_aedg_multifamily.CoolingTowerVariableSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/ze_aedg_multifamily.PlantLoop.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/ze_aedg_multifamily.Space.rb +4 -4
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.AirLoopHVAC.rb +22 -28
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.AirTerminalSingleDuctParallelPIUReheat.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.AirTerminalSingleDuctVAVReheat.rb +2 -2
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.BoilerHotWater.rb +1 -74
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.ChillerElectricEIR.rb +7 -59
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilCoolingDXSingleSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilCoolingDXTwoSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilDX.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilHeatingDXSingleSpeed.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoilHeatingGas.rb +1 -21
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.DesignSpecificationOutdoorAir.rb +101 -0
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.FanConstantVolume.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.FanOnOff.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.HeatExchangerSensLat.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.Model.rb +643 -526
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.PlanarSurface.rb +8 -2
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.PlantLoop.rb +17 -77
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.Space.rb +74 -16
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.SpaceType.rb +96 -44
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.Surface.rb +6 -6
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.ThermalZone.rb +18 -6
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.ZoneHVACComponent.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.rb +328 -74
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/ashrae_90_1_prm_2019.Model.rb +0 -118
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/ashrae_90_1_prm_2019.rb +2 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.heat_rejection.json +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/baseline_outdoor_air.md +35 -0
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/docs/set_plug_load_measures.md +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/ashrae_90_1_prm.UserData.rb +228 -0
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_enums.rb +131 -0
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/userdata_csv/userdata_space.csv +1 -1
- data/lib/openstudio-standards/standards/cbes/cbes.AirLoopHVAC.rb +5 -5
- data/lib/openstudio-standards/standards/cbes/cbes.Model.rb +1 -1
- data/lib/openstudio-standards/standards/cbes/cbes.PlantLoop.rb +1 -1
- data/lib/openstudio-standards/standards/cbes/cbes.Space.rb +1 -1
- data/lib/openstudio-standards/standards/cbes/cbes_t24_2005/cbes_t24_2005.Space.rb +1 -1
- data/lib/openstudio-standards/standards/cbes/cbes_t24_2008/cbes_t24_2008.Space.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer.AirLoopHVAC.rb +109 -27
- data/lib/openstudio-standards/standards/deer/deer.Space.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer_1985/data/deer_1985.economizers.json +246 -4
- data/lib/openstudio-standards/standards/deer/deer_1996/data/deer_1996.economizers.json +246 -4
- data/lib/openstudio-standards/standards/deer/deer_2003/data/deer_2003.economizers.json +246 -4
- data/lib/openstudio-standards/standards/deer/deer_2003/deer_2003.ThermalZone.rb +18 -18
- data/lib/openstudio-standards/standards/deer/deer_2007/data/deer_2007.economizers.json +246 -4
- data/lib/openstudio-standards/standards/deer/deer_2007/deer_2007.ThermalZone.rb +18 -18
- data/lib/openstudio-standards/standards/deer/deer_2011/data/deer_2011.economizers.json +246 -4
- data/lib/openstudio-standards/standards/deer/deer_2011/deer_2011.ThermalZone.rb +18 -18
- data/lib/openstudio-standards/standards/deer/deer_2014/data/deer_2014.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2014/deer_2014.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2014/deer_2014.ThermalZone.rb +18 -18
- data/lib/openstudio-standards/standards/deer/deer_2015/data/deer_2015.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2015/deer_2015.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2015/deer_2015.ThermalZone.rb +18 -18
- data/lib/openstudio-standards/standards/deer/deer_2017/data/deer_2017.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2017/deer_2017.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2017/deer_2017.ThermalZone.rb +18 -18
- data/lib/openstudio-standards/standards/deer/deer_2020/data/deer_2020.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2020/deer_2020.AirLoopHVAC.rb +18 -5
- data/lib/openstudio-standards/standards/deer/deer_2020/deer_2020.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer_2020/deer_2020.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2020/deer_2020.ThermalZone.rb +18 -18
- data/lib/openstudio-standards/standards/deer/deer_2025/data/deer_2025.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2025/deer_2025.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2025/deer_2025.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer_2025/deer_2025.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2030/data/deer_2030.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2030/data/deer_2030.heat_pumps.json +2 -2
- data/lib/openstudio-standards/standards/deer/deer_2030/deer_2030.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2030/deer_2030.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer_2030/deer_2030.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2035/data/deer_2035.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2035/deer_2035.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2035/deer_2035.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer_2035/deer_2035.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2040/data/deer_2040.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2040/deer_2040.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2040/deer_2040.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer_2040/deer_2040.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2045/data/deer_2045.economizers.json +260 -0
- data/lib/openstudio-standards/standards/deer/deer_2045/deer_2045.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2045/deer_2045.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer_2045/deer_2045.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2050/data/deer_2050.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2050/deer_2050.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2050/deer_2050.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer_2050/deer_2050.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2055/data/deer_2055.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2055/deer_2055.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2055/deer_2055.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer_2055/deer_2055.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2060/data/deer_2060.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2060/deer_2060.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2060/deer_2060.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer_2060/deer_2060.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2065/data/deer_2065.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2065/deer_2065.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2065/deer_2065.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer_2065/deer_2065.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2070/data/deer_2070.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2070/deer_2070.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2070/deer_2070.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer_2070/deer_2070.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2075/data/deer_2075.economizers.json +248 -6
- data/lib/openstudio-standards/standards/deer/deer_2075/deer_2075.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_2075/deer_2075.FanVariableVolume.rb +1 -1
- data/lib/openstudio-standards/standards/deer/deer_2075/deer_2075.Space.rb +3 -3
- data/lib/openstudio-standards/standards/deer/deer_pre_1975/data/deer_pre_1975.economizers.json +246 -4
- data/lib/openstudio-standards/standards/necb/BTAP1980TO2010/data/space_types.json +447 -223
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/building_envelope.rb +1 -1
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/space_types.json +447 -223
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_systems.rb +5 -2
- data/lib/openstudio-standards/standards/necb/ECMS/data/chiller_types.json +25 -0
- data/lib/openstudio-standards/standards/necb/ECMS/data/chillers.json +44 -0
- data/lib/openstudio-standards/standards/necb/ECMS/data/curves.json +225 -0
- data/lib/openstudio-standards/standards/necb/ECMS/ecms.rb +2 -2
- data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +193 -73
- data/lib/openstudio-standards/standards/necb/ECMS/pv_ground.rb +1 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +10 -4
- data/lib/openstudio-standards/standards/necb/NECB2011/beps_compliance_path.rb +7 -7
- data/lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb +4 -5
- data/lib/openstudio-standards/standards/necb/NECB2011/data/chiller_types.json +32 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/data/chillers.json +1 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/constants.json +36 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/data/fuel_type_sets.json +7 -7
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +47587 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +49764 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Warehouse.osm +283 -297
- data/lib/openstudio-standards/standards/necb/NECB2011/data/space_type_unit_definitions.txt +2 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/space_types.json +447 -223
- data/lib/openstudio-standards/standards/necb/NECB2011/data/standards_data.rb +3 -3
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_system_1_multi_speed.rb +1 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +49 -27
- data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +400 -202
- data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +4 -4
- data/lib/openstudio-standards/standards/necb/NECB2015/data/space_types.json +637 -318
- data/lib/openstudio-standards/standards/necb/NECB2015/hvac_systems.rb +18 -1
- data/lib/openstudio-standards/standards/necb/NECB2015/necb_2015.rb +3 -3
- data/lib/openstudio-standards/standards/necb/NECB2017/data/space_types.json +637 -318
- data/lib/openstudio-standards/standards/necb/NECB2017/hvac_systems.rb +1 -1
- data/lib/openstudio-standards/standards/necb/NECB2017/necb_2017.rb +3 -3
- data/lib/openstudio-standards/standards/necb/NECB2020/building_envelope.rb +1 -1
- data/lib/openstudio-standards/standards/necb/NECB2020/data/space_types.json +615 -307
- data/lib/openstudio-standards/standards/necb/NECB2020/service_water_heating.rb +4 -4
- data/lib/openstudio-standards/standards/necb/common/btap_data.rb +10 -5
- data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +1 -1
- data/lib/openstudio-standards/utilities/assertion.rb +128 -0
- data/lib/openstudio-standards/utilities/logging.rb +2 -3
- data/lib/openstudio-standards/utilities/object_info.rb +39 -18
- data/lib/openstudio-standards/utilities/schedule_translator.rb +8 -6
- data/lib/openstudio-standards/utilities/simulation.rb +24 -11
- data/lib/openstudio-standards/utilities/sqlfile.rb +10 -5
- data/lib/openstudio-standards/version.rb +1 -1
- data/lib/openstudio-standards/weather/Weather.Model.rb +8 -9
- data/lib/openstudio-standards/weather/Weather.stat_file.rb +3 -3
- data/lib/openstudio-standards/weather/information.rb +35 -0
- data/lib/openstudio-standards.rb +69 -5
- metadata +54 -18
- data/License.txt +0 -65
- data/data/standards/OpenStudio_Standards-deer-ALL-comstock(space_types).xlsx +0 -0
- data/lib/openstudio-standards/hvac_sizing/Siz.AirLoopHVAC.rb +0 -59
- data/lib/openstudio-standards/hvac_sizing/Siz.CoilCoolingWater.rb +0 -13
- data/lib/openstudio-standards/hvac_sizing/Siz.HVACComponent.rb +0 -36
- data/lib/openstudio-standards/hvac_sizing/Siz.HeatingCoolingFuels.rb +0 -898
- data/lib/openstudio-standards/hvac_sizing/Siz.Model.rb +0 -126
- data/lib/openstudio-standards/hvac_sizing/Siz.ThermalZone.rb +0 -356
- data/lib/openstudio-standards/prototypes/ashrae_90_1/nrel_nze_ready_2017/nrel_zne_ready_2017.Model.rb +0 -35
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoolingTower.rb +0 -110
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.CoolingTowerVariableSpeed.rb +0 -5
@@ -0,0 +1,2170 @@
|
|
1
|
+
# Methods to create geometry
|
2
|
+
module OpenstudioStandards
|
3
|
+
module Geometry
|
4
|
+
# @!group CreateBar
|
5
|
+
|
6
|
+
# Building Form Defaults from Table 4.2 in Achieving the 30% Goal: Energy and Cost Savings Analysis of ASHRAE Standard 90.1-2010
|
7
|
+
# aspect ratio for NA replaced with floor area to perimeter ratio from prototype model
|
8
|
+
# currently no reason to split apart doe and deer inputs here
|
9
|
+
#
|
10
|
+
# @param building_type [String] standard building type
|
11
|
+
# @return [Hash] Hash of aspect_ratio, wwr, typical_story, and perim_mult
|
12
|
+
def self.building_form_defaults(building_type)
|
13
|
+
hash = {}
|
14
|
+
|
15
|
+
# DOE Prototypes
|
16
|
+
|
17
|
+
# calculate aspect ratios not represented on Table 4.2
|
18
|
+
primary_footprint = 73958.0
|
19
|
+
primary_p = 619.0 # wrote measure using calculate_perimeter method in os_lib_geometry
|
20
|
+
primary_ns_ew_ratio = 2.829268293 # estimated from ratio of ns/ew total wall area
|
21
|
+
primary_width = Math.sqrt(primary_footprint / primary_ns_ew_ratio)
|
22
|
+
primary_p_min = 2 * (primary_width + primary_width / primary_footprint)
|
23
|
+
primary_p_mult = primary_p / primary_p_min
|
24
|
+
|
25
|
+
secondary_footprint = 210887.0 / 2.0 # floor area divided by area instead of true footprint 128112.0)
|
26
|
+
secondary_p = 708.0 # wrote measure using calculate_perimeter method in os_lib_geometry
|
27
|
+
secondary_ns_ew_ratio = 2.069230769 # estimated from ratio of ns/ew total wall area
|
28
|
+
secondary_width = Math.sqrt(secondary_footprint / secondary_ns_ew_ratio)
|
29
|
+
secondary_p_min = 2 * (secondary_width + secondary_width / secondary_footprint)
|
30
|
+
secondary_p_mult = secondary_p / secondary_p_min
|
31
|
+
|
32
|
+
outpatient_footprint = 40946.0 / 3.0 # floor area divided by area instead of true footprint 17872.0)
|
33
|
+
outpatient_p = 537.0 # wrote measure using calculate_perimeter method in os_lib_geometry
|
34
|
+
outpatient_ns_ew_ratio = 1.56448737 # estimated from ratio of ns/ew total wall area
|
35
|
+
outpatient_width = Math.sqrt(outpatient_footprint / outpatient_ns_ew_ratio)
|
36
|
+
outpatient_p_min = 2 * (outpatient_width + outpatient_footprint / outpatient_width)
|
37
|
+
outpatient_p_mult = outpatient_p / outpatient_p_min
|
38
|
+
|
39
|
+
# primary_aspet_ratio = OpenstudioStandards::Geometry.aspect_ratio(73958.0, 2060.0)
|
40
|
+
# secondary_aspet_ratio = OpenstudioStandards::Geometry.aspect_ratio(128112.0, 2447.0)
|
41
|
+
# outpatient_aspet_ratio = OpenstudioStandards::Geometry.aspect_ratio(14782.0, 588.0)
|
42
|
+
supermarket_a = 45001.0
|
43
|
+
supermarket_p = 866.0
|
44
|
+
supermarket_wwr = 1880.0 / (supermarket_p * 20.0)
|
45
|
+
supermarket_aspect_ratio = OpenstudioStandards::Geometry.aspect_ratio(supermarket_a, supermarket_p)
|
46
|
+
|
47
|
+
hash['SmallOffice'] = { aspect_ratio: 1.5, wwr: 0.15, typical_story: 10.0, perim_mult: 1.0 }
|
48
|
+
hash['MediumOffice'] = { aspect_ratio: 1.5, wwr: 0.33, typical_story: 13.0, perim_mult: 1.0 }
|
49
|
+
hash['LargeOffice'] = { aspect_ratio: 1.5, wwr: 0.15, typical_story: 13.0, perim_mult: 1.0 }
|
50
|
+
hash['RetailStandalone'] = { aspect_ratio: 1.28, wwr: 0.07, typical_story: 20.0, perim_mult: 1.0 }
|
51
|
+
hash['RetailStripmall'] = { aspect_ratio: 4.0, wwr: 0.11, typical_story: 17.0, perim_mult: 1.0 }
|
52
|
+
hash['PrimarySchool'] = { aspect_ratio: primary_ns_ew_ratio.round(1), wwr: 0.35, typical_story: 13.0, perim_mult: primary_p_mult.round(3) }
|
53
|
+
hash['SecondarySchool'] = { aspect_ratio: secondary_ns_ew_ratio.round(1), wwr: 0.33, typical_story: 13.0, perim_mult: secondary_p_mult.round(3) }
|
54
|
+
hash['Outpatient'] = { aspect_ratio: outpatient_ns_ew_ratio.round(1), wwr: 0.20, typical_story: 10.0, perim_mult: outpatient_p_mult.round(3) }
|
55
|
+
hash['Hospital'] = { aspect_ratio: 1.33, wwr: 0.16, typical_story: 14.0, perim_mult: 1.0 }
|
56
|
+
hash['SmallHotel'] = { aspect_ratio: 3.0, wwr: 0.11, typical_story: 9.0, first_story: 11.0, perim_mult: 1.0 }
|
57
|
+
hash['LargeHotel'] = { aspect_ratio: 5.1, wwr: 0.27, typical_story: 10.0, first_story: 13.0, perim_mult: 1.0 }
|
58
|
+
|
59
|
+
# code in get_space_types_from_building_type is used to override building wwr with space type specific wwr
|
60
|
+
hash['Warehouse'] = { aspect_ratio: 2.2, wwr: 0.0, typical_story: 28.0, perim_mult: 1.0 }
|
61
|
+
|
62
|
+
hash['FullServiceRestaurant'] = { aspect_ratio: 1.0, wwr: 0.18, typical_story: 10.0, perim_mult: 1.0 }
|
63
|
+
hash['QuickServiceRestaurant'] = { aspect_ratio: 1.0, wwr: 0.18, typical_story: 10.0, perim_mult: 1.0 }
|
64
|
+
hash['MidriseApartment'] = { aspect_ratio: 2.75, wwr: 0.15, typical_story: 10.0, perim_mult: 1.0 }
|
65
|
+
hash['HighriseApartment'] = { aspect_ratio: 2.75, wwr: 0.15, typical_story: 10.0, perim_mult: 1.0 }
|
66
|
+
# SuperMarket inputs come from prototype model
|
67
|
+
hash['SuperMarket'] = { aspect_ratio: supermarket_aspect_ratio.round(1), wwr: supermarket_wwr.round(2), typical_story: 20.0, perim_mult: 1.0 }
|
68
|
+
|
69
|
+
# Add Laboratory and Data Centers
|
70
|
+
hash['Laboratory'] = { aspect_ratio: 1.33, wwr: 0.12, typical_story: 10.0, perim_mult: 1.0 }
|
71
|
+
hash['LargeDataCenterLowITE'] = { aspect_ratio: 1.67, wwr: 0.0, typical_story: 14.0, perim_mult: 1.0 }
|
72
|
+
hash['LargeDataCenterHighITE'] = { aspect_ratio: 1.67, wwr: 0.0, typical_story: 14.0, perim_mult: 1.0 }
|
73
|
+
hash['SmallDataCenterLowITE'] = { aspect_ratio: 1.5, wwr: 0.0, typical_story: 14.0, perim_mult: 1.0 }
|
74
|
+
hash['SmallDataCenterHighITE'] = { aspect_ratio: 1.5, wwr: 0.0, typical_story: 14.0, perim_mult: 1.0 }
|
75
|
+
|
76
|
+
# Add Courthouse and Education
|
77
|
+
hash['Courthouse'] = { aspect_ratio: 2.06, wwr: 0.18, typical_story: 16.0, perim_mult: 1.0 }
|
78
|
+
hash['College'] = { aspect_ratio: 2.5, wwr: 0.037, typical_story: 13.0, perim_mult: 1.0 }
|
79
|
+
|
80
|
+
# DEER Prototypes
|
81
|
+
hash['Asm'] = { aspect_ratio: 1.0, wwr: 0.19, typical_story: 15.0 }
|
82
|
+
hash['ECC'] = { aspect_ratio: 4.0, wwr: 0.25, typical_story: 13.0 }
|
83
|
+
hash['EPr'] = { aspect_ratio: 2.0, wwr: 0.16, typical_story: 12.0 }
|
84
|
+
hash['ERC'] = { aspect_ratio: 1.7, wwr: 0.03, typical_story: 12.0 }
|
85
|
+
hash['ESe'] = { aspect_ratio: 1.0, wwr: 0.15, typical_story: 13.0 }
|
86
|
+
hash['EUn'] = { aspect_ratio: 2.5, wwr: 0.3, typical_story: 14.0 }
|
87
|
+
hash['Gro'] = { aspect_ratio: 1.0, wwr: 0.07, typical_story: 25.0 }
|
88
|
+
hash['Hsp'] = { aspect_ratio: 1.5, wwr: 0.11, typical_story: 13.0 }
|
89
|
+
hash['Htl'] = { aspect_ratio: 3.0, wwr: 0.23, typical_story: 9.5, first_story: 12.0 }
|
90
|
+
hash['MBT'] = { aspect_ratio: 10.7, wwr: 0.12, typical_story: 15.0 }
|
91
|
+
hash['MFm'] = { aspect_ratio: 1.4, wwr: 0.24, typical_story: 9.5 }
|
92
|
+
hash['MLI'] = { aspect_ratio: 1.0, wwr: 0.01, typical_story: 35.0 }
|
93
|
+
hash['Mtl'] = { aspect_ratio: 5.1, wwr: 0.41, typical_story: 9.0 }
|
94
|
+
hash['Nrs'] = { aspect_ratio: 10.3, wwr: 0.2, typical_story: 13.0 }
|
95
|
+
hash['OfL'] = { aspect_ratio: 1.5, wwr: 0.33, typical_story: 12.0 }
|
96
|
+
hash['OfS'] = { aspect_ratio: 1.5, wwr: 0.33, typical_story: 12.0 }
|
97
|
+
hash['RFF'] = { aspect_ratio: 1.0, wwr: 0.25, typical_story: 13.0 }
|
98
|
+
hash['RSD'] = { aspect_ratio: 1.0, wwr: 0.13, typical_story: 13.0 }
|
99
|
+
hash['Rt3'] = { aspect_ratio: 1.0, wwr: 0.02, typical_story: 20.8 }
|
100
|
+
hash['RtL'] = { aspect_ratio: 1.0, wwr: 0.03, typical_story: 20.5 }
|
101
|
+
hash['RtS'] = { aspect_ratio: 1.0, wwr: 0.13, typical_story: 12.0 }
|
102
|
+
hash['SCn'] = { aspect_ratio: 1.0, wwr: 0.01, typical_story: 48.0 }
|
103
|
+
hash['SUn'] = { aspect_ratio: 1.0, wwr: 0.01, typical_story: 48.0 }
|
104
|
+
hash['WRf'] = { aspect_ratio: 1.6, wwr: 0.0, typical_story: 32.0 }
|
105
|
+
|
106
|
+
return hash[building_type]
|
107
|
+
end
|
108
|
+
|
109
|
+
# sort building stories
|
110
|
+
#
|
111
|
+
# @param model [OpenStudio::Model::Model] OpenStudio model object
|
112
|
+
# @return [Hash] An Hash with key OpenStudio BuildingStory objects and their minimum z value
|
113
|
+
def self.model_sort_building_stories_and_get_min_multiplier(model)
|
114
|
+
sorted_building_stories = {}
|
115
|
+
# loop through stories
|
116
|
+
model.getBuildingStorys.sort.each do |story|
|
117
|
+
story_min_z = nil
|
118
|
+
# loop through spaces in story.
|
119
|
+
story.spaces.sort.each do |space|
|
120
|
+
space_z_min = OpenstudioStandards::Geometry.surfaces_get_z_values(space.surfaces.to_a).min + space.zOrigin
|
121
|
+
if story_min_z.nil? || (story_min_z > space_z_min)
|
122
|
+
story_min_z = space_z_min
|
123
|
+
end
|
124
|
+
end
|
125
|
+
sorted_building_stories[story] = story_min_z
|
126
|
+
end
|
127
|
+
|
128
|
+
return sorted_building_stories
|
129
|
+
end
|
130
|
+
|
131
|
+
# gather envelope data for envelope simplification
|
132
|
+
#
|
133
|
+
# @param model [OpenStudio::Model::Model] OpenStudio model object
|
134
|
+
# @return [Hash] A hash of envelope data used by other methods
|
135
|
+
# @todo full list of hash returns aren't documented yet
|
136
|
+
def self.model_envelope_data(model)
|
137
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Gathering envelope data.')
|
138
|
+
|
139
|
+
# hash to contain envelope data
|
140
|
+
envelope_data_hash = {}
|
141
|
+
|
142
|
+
# used for overhang and party wall orientation catigorization
|
143
|
+
facade_options = {
|
144
|
+
'north_east' => 45.0,
|
145
|
+
'south_east' => 125.0,
|
146
|
+
'south_west' => 225.0,
|
147
|
+
'north_west' => 315.0
|
148
|
+
}
|
149
|
+
|
150
|
+
# get building level inputs
|
151
|
+
envelope_data_hash[:north_axis] = model.getBuilding.northAxis
|
152
|
+
envelope_data_hash[:building_floor_area] = model.getBuilding.floorArea
|
153
|
+
envelope_data_hash[:building_exterior_surface_area] = model.getBuilding.exteriorSurfaceArea
|
154
|
+
envelope_data_hash[:building_exterior_wall_area] = model.getBuilding.exteriorWallArea
|
155
|
+
envelope_data_hash[:building_exterior_roof_area] = envelope_data_hash[:building_exterior_surface_area] - envelope_data_hash[:building_exterior_wall_area]
|
156
|
+
envelope_data_hash[:building_air_volume] = model.getBuilding.airVolume
|
157
|
+
envelope_data_hash[:building_perimeter] = nil # will be applied for first story without ground walls
|
158
|
+
|
159
|
+
# get bounding_box
|
160
|
+
bounding_box = OpenStudio::BoundingBox.new
|
161
|
+
model.getSpaces.sort.each do |space|
|
162
|
+
space.surfaces.sort.each do |space_surface|
|
163
|
+
bounding_box.addPoints(space.transformation * space_surface.vertices)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
min_x = bounding_box.minX.get
|
167
|
+
min_y = bounding_box.minY.get
|
168
|
+
min_z = bounding_box.minZ.get
|
169
|
+
max_x = bounding_box.maxX.get
|
170
|
+
max_y = bounding_box.maxY.get
|
171
|
+
max_z = bounding_box.maxZ.get
|
172
|
+
envelope_data_hash[:building_min_xyz] = [min_x, min_y, min_z]
|
173
|
+
envelope_data_hash[:building_max_xyz] = [max_x, max_y, max_z]
|
174
|
+
|
175
|
+
# add orientation specific wwr
|
176
|
+
ext_surfaces_hash = OpenstudioStandards::Geometry.model_get_exterior_window_and_wall_area_by_orientation(model)
|
177
|
+
envelope_data_hash[:building_wwr_n] = ext_surfaces_hash['north_window'] / ext_surfaces_hash['north_wall']
|
178
|
+
envelope_data_hash[:building_wwr_s] = ext_surfaces_hash['south_window'] / ext_surfaces_hash['south_wall']
|
179
|
+
envelope_data_hash[:building_wwr_e] = ext_surfaces_hash['east_window'] / ext_surfaces_hash['east_wall']
|
180
|
+
envelope_data_hash[:building_wwr_w] = ext_surfaces_hash['west_window'] / ext_surfaces_hash['west_wall']
|
181
|
+
envelope_data_hash[:stories] = {} # each entry will be hash with buildingStory as key and attributes has values
|
182
|
+
envelope_data_hash[:space_types] = {} # each entry will be hash with spaceType as key and attributes has values
|
183
|
+
|
184
|
+
# as rough estimate overhang area / glazing area should be close to projection factor assuming overhang is same width as windows
|
185
|
+
# will only add building shading surfaces assoicated with a sub-surface.
|
186
|
+
building_overhang_area_n = 0.0
|
187
|
+
building_overhang_area_s = 0.0
|
188
|
+
building_overhang_area_e = 0.0
|
189
|
+
building_overhang_area_w = 0.0
|
190
|
+
|
191
|
+
# loop through stories based on mine z height of surfaces.
|
192
|
+
sorted_stories = sort_building_stories_and_get_min_multiplier(model).sort_by { |k, v| v }
|
193
|
+
sorted_stories.each do |story, story_min_z|
|
194
|
+
story_min_multiplier = nil
|
195
|
+
story_footprint = nil
|
196
|
+
story_multiplied_floor_area = OpenstudioStandards::Geometry.spaces_get_floor_area(story.spaces)
|
197
|
+
# goal of footprint calc is to count multiplier for hotel room on facade,but not to count what is intended as a story multiplier
|
198
|
+
story_multiplied_exterior_surface_area = OpenstudioStandards::Geometry.spaces_get_exterior_area(story.spaces)
|
199
|
+
story_multiplied_exterior_wall_area = OpenstudioStandards::Geometry.spaces_get_exterior_wall_area(story.spaces)
|
200
|
+
story_multiplied_exterior_roof_area = story_multiplied_exterior_surface_area - story_multiplied_exterior_wall_area
|
201
|
+
story_has_ground_walls = []
|
202
|
+
story_has_adiabatic_walls = []
|
203
|
+
story_included_in_building_area = false # will be true if any spaces on story are inclued in building area
|
204
|
+
story_max_z = nil
|
205
|
+
|
206
|
+
# loop through spaces for story gathering information
|
207
|
+
story.spaces.each do |space|
|
208
|
+
# get min multiplier value
|
209
|
+
multiplier = space.multiplier
|
210
|
+
if story_min_multiplier.nil? || (story_min_multiplier > multiplier)
|
211
|
+
story_min_multiplier = multiplier
|
212
|
+
end
|
213
|
+
|
214
|
+
# calculate footprint
|
215
|
+
story_footprint = story_multiplied_floor_area / story_min_multiplier
|
216
|
+
|
217
|
+
# see if part of floor area
|
218
|
+
if space.partofTotalFloorArea
|
219
|
+
story_included_in_building_area = true
|
220
|
+
|
221
|
+
# add to space type ratio hash when space is included in building floor area
|
222
|
+
if space.spaceType.is_initialized
|
223
|
+
space_type = space.spaceType.get
|
224
|
+
space_floor_area = space.floorArea * space.multiplier
|
225
|
+
if envelope_data_hash[:space_types].key?(space_type)
|
226
|
+
envelope_data_hash[:space_types][space_type][:floor_area] += space_floor_area
|
227
|
+
else
|
228
|
+
envelope_data_hash[:space_types][space_type] = {}
|
229
|
+
envelope_data_hash[:space_types][space_type][:floor_area] = space_floor_area
|
230
|
+
|
231
|
+
# make hash for heating and cooling setpoints
|
232
|
+
envelope_data_hash[:space_types][space_type][:htg_setpoint] = {}
|
233
|
+
envelope_data_hash[:space_types][space_type][:clg_setpoint] = {}
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
# add heating and cooling setpoints
|
238
|
+
if space.thermalZone.is_initialized && space.thermalZone.get.thermostatSetpointDualSetpoint.is_initialized
|
239
|
+
thermostat = space.thermalZone.get.thermostatSetpointDualSetpoint.get
|
240
|
+
|
241
|
+
# log heating schedule
|
242
|
+
if thermostat.heatingSetpointTemperatureSchedule.is_initialized
|
243
|
+
htg_sch = thermostat.heatingSetpointTemperatureSchedule.get
|
244
|
+
if envelope_data_hash[:space_types][space_type][:htg_setpoint].key?(htg_sch)
|
245
|
+
envelope_data_hash[:space_types][space_type][:htg_setpoint][htg_sch] += space_floor_area
|
246
|
+
else
|
247
|
+
envelope_data_hash[:space_types][space_type][:htg_setpoint][htg_sch] = space_floor_area
|
248
|
+
end
|
249
|
+
else
|
250
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space.thermalZone.get.name} containing #{space.name} doesn't have a heating setpoint schedule.")
|
251
|
+
end
|
252
|
+
|
253
|
+
# log cooling schedule
|
254
|
+
if thermostat.coolingSetpointTemperatureSchedule.is_initialized
|
255
|
+
clg_sch = thermostat.coolingSetpointTemperatureSchedule.get
|
256
|
+
if envelope_data_hash[:space_types][space_type][:clg_setpoint].key?(clg_sch)
|
257
|
+
envelope_data_hash[:space_types][space_type][:clg_setpoint][clg_sch] += space_floor_area
|
258
|
+
else
|
259
|
+
envelope_data_hash[:space_types][space_type][:clg_setpoint][clg_sch] = space_floor_area
|
260
|
+
end
|
261
|
+
else
|
262
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space.thermalZone.get.name} containing #{space.name} doesn't have a heating setpoint schedule.")
|
263
|
+
end
|
264
|
+
|
265
|
+
else
|
266
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space.name} either isn't in a thermal zone or doesn't have a thermostat assigned")
|
267
|
+
end
|
268
|
+
|
269
|
+
else
|
270
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space.name} is included in the building floor area but isn't assigned a space type.")
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|
274
|
+
|
275
|
+
# check for walls with adiabatic and ground boundary condition
|
276
|
+
space.surfaces.each do |surface|
|
277
|
+
next if surface.surfaceType != 'Wall'
|
278
|
+
|
279
|
+
if surface.outsideBoundaryCondition == 'Ground'
|
280
|
+
story_has_ground_walls << surface
|
281
|
+
elsif surface.outsideBoundaryCondition == 'Adiabatic'
|
282
|
+
story_has_adiabatic_walls << surface
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# populate overhang values
|
287
|
+
space.surfaces.each do |surface|
|
288
|
+
surface.subSurfaces.each do |sub_surface|
|
289
|
+
sub_surface.shadingSurfaceGroups.each do |shading_surface_group|
|
290
|
+
shading_surface_group.shadingSurfaces.each do |shading_surface|
|
291
|
+
absolute_azimuth = OpenStudio.convert(sub_surface.azimuth, 'rad', 'deg').get + sub_surface.space.get.directionofRelativeNorth + model.getBuilding.northAxis
|
292
|
+
absolute_azimuth -= 360.0 until absolute_azimuth < 360.0
|
293
|
+
# add to hash based on orientation
|
294
|
+
if (facade_options['north_east'] <= absolute_azimuth) && (absolute_azimuth < facade_options['south_east']) # East overhang
|
295
|
+
building_overhang_area_e += shading_surface.grossArea * space.multiplier
|
296
|
+
elsif (facade_options['south_east'] <= absolute_azimuth) && (absolute_azimuth < facade_options['south_west']) # South overhang
|
297
|
+
building_overhang_area_s += shading_surface.grossArea * space.multiplier
|
298
|
+
elsif (facade_options['south_west'] <= absolute_azimuth) && (absolute_azimuth < facade_options['north_west']) # West overhang
|
299
|
+
building_overhang_area_w += shading_surface.grossArea * space.multiplier
|
300
|
+
else # North overhang
|
301
|
+
building_overhang_area_n += shading_surface.grossArea * space.multiplier
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
# get max z
|
309
|
+
space_z_max = OpenstudioStandards::Geometry.surfaces_get_z_values(space.surfaces.to_a).max + space.zOrigin
|
310
|
+
if story_max_z.nil? || (story_max_z > space_z_max)
|
311
|
+
story_max_z = space_z_max
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
# populate hash for story data
|
316
|
+
envelope_data_hash[:stories][story] = {}
|
317
|
+
envelope_data_hash[:stories][story][:story_min_height] = story_min_z
|
318
|
+
envelope_data_hash[:stories][story][:story_max_height] = story_max_z
|
319
|
+
envelope_data_hash[:stories][story][:story_min_multiplier] = story_min_multiplier
|
320
|
+
envelope_data_hash[:stories][story][:story_has_ground_walls] = story_has_ground_walls
|
321
|
+
envelope_data_hash[:stories][story][:story_has_adiabatic_walls] = story_has_adiabatic_walls
|
322
|
+
envelope_data_hash[:stories][story][:story_included_in_building_area] = story_included_in_building_area
|
323
|
+
envelope_data_hash[:stories][story][:story_footprint] = story_footprint
|
324
|
+
envelope_data_hash[:stories][story][:story_multiplied_floor_area] = story_multiplied_floor_area
|
325
|
+
envelope_data_hash[:stories][story][:story_exterior_surface_area] = story_multiplied_exterior_surface_area
|
326
|
+
envelope_data_hash[:stories][story][:story_multiplied_exterior_wall_area] = story_multiplied_exterior_wall_area
|
327
|
+
envelope_data_hash[:stories][story][:story_multiplied_exterior_roof_area] = story_multiplied_exterior_roof_area
|
328
|
+
|
329
|
+
# get perimeter and adiabatic walls that appear to be party walls
|
330
|
+
perimeter_and_party_walls = OpenstudioStandards::Geometry.story_get_exterior_wall_perimeter(story,
|
331
|
+
multiplier_adjustment: story_min_multiplier,
|
332
|
+
bounding_box: bounding_box)
|
333
|
+
envelope_data_hash[:stories][story][:story_perimeter] = perimeter_and_party_walls[:perimeter]
|
334
|
+
envelope_data_hash[:stories][story][:story_party_walls] = []
|
335
|
+
east = false
|
336
|
+
south = false
|
337
|
+
west = false
|
338
|
+
north = false
|
339
|
+
perimeter_and_party_walls[:party_walls].each do |surface|
|
340
|
+
absolute_azimuth = OpenStudio.convert(surface.azimuth, 'rad', 'deg').get + surface.space.get.directionofRelativeNorth + model.getBuilding.northAxis
|
341
|
+
absolute_azimuth -= 360.0 until absolute_azimuth < 360.0
|
342
|
+
|
343
|
+
# add to hash based on orientation (initially added array of sourfaces, but swtiched to just true/false flag)
|
344
|
+
if (facade_options['north_east'] <= absolute_azimuth) && (absolute_azimuth < facade_options['south_east']) # East party walls
|
345
|
+
east = true
|
346
|
+
elsif (facade_options['south_east'] <= absolute_azimuth) && (absolute_azimuth < facade_options['south_west']) # South party walls
|
347
|
+
south = true
|
348
|
+
elsif (facade_options['south_west'] <= absolute_azimuth) && (absolute_azimuth < facade_options['north_west']) # West party walls
|
349
|
+
west = true
|
350
|
+
else # North party walls
|
351
|
+
north = true
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
if east then envelope_data_hash[:stories][story][:story_party_walls] << 'east' end
|
356
|
+
if south then envelope_data_hash[:stories][story][:story_party_walls] << 'south' end
|
357
|
+
if west then envelope_data_hash[:stories][story][:story_party_walls] << 'west' end
|
358
|
+
if north then envelope_data_hash[:stories][story][:story_party_walls] << 'north' end
|
359
|
+
|
360
|
+
# store perimeter from first story that doesn't have ground walls
|
361
|
+
if story_has_ground_walls.empty? && envelope_data_hash[:building_perimeter].nil?
|
362
|
+
envelope_data_hash[:building_perimeter] = envelope_data_hash[:stories][story][:story_perimeter]
|
363
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', " * #{story.name} is the first above grade story and will be used for the building perimeter.")
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
envelope_data_hash[:building_overhang_proj_factor_n] = building_overhang_area_n / ext_surfaces_hash['northWindow']
|
368
|
+
envelope_data_hash[:building_overhang_proj_factor_s] = building_overhang_area_s / ext_surfaces_hash['southWindow']
|
369
|
+
envelope_data_hash[:building_overhang_proj_factor_e] = building_overhang_area_e / ext_surfaces_hash['eastWindow']
|
370
|
+
envelope_data_hash[:building_overhang_proj_factor_w] = building_overhang_area_w / ext_surfaces_hash['westWindow']
|
371
|
+
|
372
|
+
# warn for spaces that are not on a story (in future could infer stories for these)
|
373
|
+
model.getSpaces.sort.each do |space|
|
374
|
+
if !space.buildingStory.is_initialized
|
375
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space.name} is not on a building story, may have unexpected results.")
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
return envelope_data_hash
|
380
|
+
end
|
381
|
+
|
382
|
+
# get length and width of rectangle matching bounding box aspect ratio will maintaining proper floor area
|
383
|
+
#
|
384
|
+
# @param envelope_data_hash [Hash] Hash of envelope data
|
385
|
+
# @return [Hash] hash of bar length and width
|
386
|
+
def self.bar_reduced_bounding_box(envelope_data_hash)
|
387
|
+
bar = {}
|
388
|
+
|
389
|
+
bounding_length = envelope_data_hash[:building_max_xyz][0] - envelope_data_hash[:building_min_xyz][0]
|
390
|
+
bounding_width = envelope_data_hash[:building_max_xyz][1] - envelope_data_hash[:building_min_xyz][1]
|
391
|
+
bounding_area = bounding_length * bounding_width
|
392
|
+
footprint_area = envelope_data_hash[:building_floor_area] / envelope_data_hash[:effective_num_stories].to_f
|
393
|
+
area_multiplier = footprint_area / bounding_area
|
394
|
+
edge_multiplier = Math.sqrt(area_multiplier)
|
395
|
+
bar[:length] = bounding_length * edge_multiplier
|
396
|
+
bar[:width] = bounding_width * edge_multiplier
|
397
|
+
|
398
|
+
return bar
|
399
|
+
end
|
400
|
+
|
401
|
+
# get length and width of rectangle matching longer of two edges, and reducing the other way until floor area matches
|
402
|
+
#
|
403
|
+
# @param envelope_data_hash [Hash] Hash of envelope data
|
404
|
+
# @return [Hash] hash of bar length and width
|
405
|
+
def self.bar_reduced_width(envelope_data_hash)
|
406
|
+
bar = {}
|
407
|
+
|
408
|
+
bounding_length = envelope_data_hash[:building_max_xyz][0] - envelope_data_hash[:building_min_xyz][0]
|
409
|
+
bounding_width = envelope_data_hash[:building_max_xyz][1] - envelope_data_hash[:building_min_xyz][1]
|
410
|
+
footprint_area = envelope_data_hash[:building_floor_area] / envelope_data_hash[:effective_num_stories].to_f
|
411
|
+
|
412
|
+
if bounding_length >= bounding_width
|
413
|
+
bar[:length] = bounding_length
|
414
|
+
bar[:width] = footprint_area / bounding_length
|
415
|
+
else
|
416
|
+
bar[:width] = bounding_width
|
417
|
+
bar[:length] = footprint_area / bounding_width
|
418
|
+
end
|
419
|
+
|
420
|
+
return bar
|
421
|
+
end
|
422
|
+
|
423
|
+
# get length and width of rectangle by stretching it until both floor area and exterior wall area or perimeter match
|
424
|
+
#
|
425
|
+
# @param envelope_data_hash [Hash] Hash of envelope data including
|
426
|
+
# @return [Hash] hash of bar length and width
|
427
|
+
def self.bar_stretched(envelope_data_hash)
|
428
|
+
bar = {}
|
429
|
+
|
430
|
+
bounding_length = envelope_data_hash[:building_max_xyz][0] - envelope_data_hash[:building_min_xyz][0]
|
431
|
+
bounding_width = envelope_data_hash[:building_max_xyz][1] - envelope_data_hash[:building_min_xyz][1]
|
432
|
+
a = envelope_data_hash[:building_floor_area] / envelope_data_hash[:effective_num_stories].to_f
|
433
|
+
p = envelope_data_hash[:building_perimeter]
|
434
|
+
|
435
|
+
if bounding_length >= bounding_width
|
436
|
+
bar[:length] = 0.25 * (p + Math.sqrt(p**2 - 16 * a))
|
437
|
+
bar[:width] = 0.25 * (p - Math.sqrt(p**2 - 16 * a))
|
438
|
+
else
|
439
|
+
bar[:length] = 0.25 * (p - Math.sqrt(p**2 - 16 * a))
|
440
|
+
bar[:width] = 0.25 * (p + Math.sqrt(p**2 - 16 * a))
|
441
|
+
end
|
442
|
+
|
443
|
+
return bar
|
444
|
+
end
|
445
|
+
|
446
|
+
# create_bar creates spaces based on a set of geometric characteristics
|
447
|
+
#
|
448
|
+
# @param model [OpenStudio::Model::Model] OpenStudio model object
|
449
|
+
# @param bar_hash [Hash] A hash object of bar characteristics
|
450
|
+
# @return [Array<OpenStudio::Model::Space>] An array of OpenStudio Space objects
|
451
|
+
def self.create_bar(model, bar_hash)
|
452
|
+
# make custom story hash when number of stories below grade > 0
|
453
|
+
# @todo update this so have option basements are not below 0? (useful for simplifying existing model and maintaining z position relative to site shading)
|
454
|
+
story_hash = {}
|
455
|
+
eff_below = bar_hash[:num_stories_below_grade]
|
456
|
+
eff_above = bar_hash[:num_stories_above_grade]
|
457
|
+
footprint_origin_point = bar_hash[:center_of_footprint]
|
458
|
+
typical_story_height = bar_hash[:floor_height]
|
459
|
+
|
460
|
+
# warn about site shading
|
461
|
+
if !model.getSite.shadingSurfaceGroups.empty?
|
462
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'The model has one or more site shading surfaces. New geometry may not be positioned where expected, it will be centered over the center of the original geometry.')
|
463
|
+
end
|
464
|
+
|
465
|
+
# flatten story_hash out to individual stories included in building area
|
466
|
+
stories_flat = []
|
467
|
+
stories_flat_counter = 0
|
468
|
+
bar_hash[:stories].each_with_index do |(k, v), i|
|
469
|
+
# k is invalid in some cases, old story object that has been removed, should be from low to high including basement
|
470
|
+
# skip if source story insn't included in building area
|
471
|
+
if v[:story_included_in_building_area].nil? || (v[:story_included_in_building_area] == true)
|
472
|
+
|
473
|
+
# add to counter
|
474
|
+
stories_flat_counter += v[:story_min_multiplier]
|
475
|
+
|
476
|
+
flat_hash = {}
|
477
|
+
flat_hash[:story_party_walls] = v[:story_party_walls]
|
478
|
+
flat_hash[:below_partial_story] = v[:below_partial_story]
|
479
|
+
flat_hash[:bottom_story_ground_exposed_floor] = v[:bottom_story_ground_exposed_floor]
|
480
|
+
flat_hash[:top_story_exterior_exposed_roof] = v[:top_story_exterior_exposed_roof]
|
481
|
+
if i < eff_below
|
482
|
+
flat_hash[:story_type] = 'b'
|
483
|
+
flat_hash[:multiplier] = 1
|
484
|
+
elsif i == eff_below
|
485
|
+
flat_hash[:story_type] = 'ground'
|
486
|
+
flat_hash[:multiplier] = 1
|
487
|
+
elsif stories_flat_counter == eff_below + eff_above.ceil
|
488
|
+
flat_hash[:story_type] = 'top'
|
489
|
+
flat_hash[:multiplier] = 1
|
490
|
+
else
|
491
|
+
flat_hash[:story_type] = 'mid'
|
492
|
+
flat_hash[:multiplier] = v[:story_min_multiplier]
|
493
|
+
end
|
494
|
+
|
495
|
+
compare_hash = {}
|
496
|
+
if !stories_flat.empty?
|
497
|
+
stories_flat.last.each { |k, v| compare_hash[k] = flat_hash[k] if flat_hash[k] != v }
|
498
|
+
end
|
499
|
+
if (bar_hash[:story_multiplier_method] != 'None' && stories_flat.last == flat_hash) || (bar_hash[:story_multiplier_method] != 'None' && compare_hash.size == 1 && compare_hash.include?(:multiplier))
|
500
|
+
stories_flat.last[:multiplier] += v[:story_min_multiplier]
|
501
|
+
else
|
502
|
+
stories_flat << flat_hash
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
if bar_hash[:num_stories_below_grade] > 0
|
508
|
+
|
509
|
+
# add in below grade levels (may want to add below grade multipliers at some point if we start running deep basements)
|
510
|
+
eff_below.times do |i|
|
511
|
+
story_hash["B#{i + 1}"] = { space_origin_z: footprint_origin_point.z - typical_story_height * (i + 1), space_height: typical_story_height, multiplier: 1 }
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
# add in above grade levels
|
516
|
+
if eff_above > 2
|
517
|
+
story_hash['ground'] = { space_origin_z: footprint_origin_point.z, space_height: typical_story_height, multiplier: 1 }
|
518
|
+
|
519
|
+
footprint_counter = 0
|
520
|
+
effective_stories_counter = 1
|
521
|
+
stories_flat.each do |hash|
|
522
|
+
next if hash[:story_type] != 'mid'
|
523
|
+
|
524
|
+
if footprint_counter == 0
|
525
|
+
string = 'mid'
|
526
|
+
else
|
527
|
+
string = "mid#{footprint_counter + 1}"
|
528
|
+
end
|
529
|
+
story_hash[string] = { space_origin_z: footprint_origin_point.z + typical_story_height * effective_stories_counter + typical_story_height * (hash[:multiplier] - 1) / 2.0, space_height: typical_story_height, multiplier: hash[:multiplier] }
|
530
|
+
footprint_counter += 1
|
531
|
+
effective_stories_counter += hash[:multiplier]
|
532
|
+
end
|
533
|
+
|
534
|
+
story_hash['top'] = { space_origin_z: footprint_origin_point.z + typical_story_height * (eff_above.ceil - 1), space_height: typical_story_height, multiplier: 1 }
|
535
|
+
elsif eff_above > 1
|
536
|
+
story_hash['ground'] = { space_origin_z: footprint_origin_point.z, space_height: typical_story_height, multiplier: 1 }
|
537
|
+
story_hash['top'] = { space_origin_z: footprint_origin_point.z + typical_story_height * (eff_above.ceil - 1), space_height: typical_story_height, multiplier: 1 }
|
538
|
+
else # one story only
|
539
|
+
story_hash['ground'] = { space_origin_z: footprint_origin_point.z, space_height: typical_story_height, multiplier: 1 }
|
540
|
+
end
|
541
|
+
|
542
|
+
# create footprints
|
543
|
+
if bar_hash[:bar_division_method] == 'Multiple Space Types - Simple Sliced'
|
544
|
+
footprints = []
|
545
|
+
story_hash.size.times do |i|
|
546
|
+
# adjust size of bar of top story is not a full story
|
547
|
+
if i + 1 == story_hash.size
|
548
|
+
area_multiplier = (1.0 - bar_hash[:num_stories_above_grade].ceil + bar_hash[:num_stories_above_grade])
|
549
|
+
edge_multiplier = Math.sqrt(area_multiplier)
|
550
|
+
length = bar_hash[:length] * edge_multiplier
|
551
|
+
width = bar_hash[:width] * edge_multiplier
|
552
|
+
else
|
553
|
+
length = bar_hash[:length]
|
554
|
+
width = bar_hash[:width]
|
555
|
+
end
|
556
|
+
footprints << OpenstudioStandards::Geometry.create_sliced_bar_simple_polygons(bar_hash[:space_types], length, width, bar_hash[:center_of_footprint])
|
557
|
+
end
|
558
|
+
|
559
|
+
elsif bar_hash[:bar_division_method] == 'Multiple Space Types - Individual Stories Sliced'
|
560
|
+
|
561
|
+
# update story_hash for partial_story_above
|
562
|
+
story_hash.each_with_index do |(k, v), i|
|
563
|
+
# adjust size of bar of top story is not a full story
|
564
|
+
if i + 1 == story_hash.size
|
565
|
+
story_hash[k][:partial_story_multiplier] = (1.0 - bar_hash[:num_stories_above_grade].ceil + bar_hash[:num_stories_above_grade])
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
footprints = OpenstudioStandards::Geometry.create_sliced_bar_multi_polygons(bar_hash[:space_types], bar_hash[:length], bar_hash[:width], bar_hash[:center_of_footprint], story_hash)
|
570
|
+
|
571
|
+
else
|
572
|
+
footprints = []
|
573
|
+
story_hash.size.times do |i|
|
574
|
+
# adjust size of bar of top story is not a full story
|
575
|
+
if i + 1 == story_hash.size
|
576
|
+
area_multiplier = (1.0 - bar_hash[:num_stories_above_grade].ceil + bar_hash[:num_stories_above_grade])
|
577
|
+
edge_multiplier = Math.sqrt(area_multiplier)
|
578
|
+
length = bar_hash[:length] * edge_multiplier
|
579
|
+
width = bar_hash[:width] * edge_multiplier
|
580
|
+
else
|
581
|
+
length = bar_hash[:length]
|
582
|
+
width = bar_hash[:width]
|
583
|
+
end
|
584
|
+
# perimeter defaults to 15 ft
|
585
|
+
footprints << OpenstudioStandards::Geometry.create_core_and_perimeter_polygons(length, width, bar_hash[:center_of_footprint])
|
586
|
+
end
|
587
|
+
|
588
|
+
# set primary space type to building default space type
|
589
|
+
space_types = bar_hash[:space_types].sort_by { |k, v| v[:floor_area] }
|
590
|
+
if space_types.last.first.class.to_s == 'OpenStudio::Model::SpaceType'
|
591
|
+
model.getBuilding.setSpaceType(space_types.last.first)
|
592
|
+
end
|
593
|
+
|
594
|
+
end
|
595
|
+
|
596
|
+
# make spaces from polygons
|
597
|
+
new_spaces = OpenstudioStandards::Geometry.create_spaces_from_polygons(model, footprints, bar_hash[:floor_height], bar_hash[:num_stories], bar_hash[:center_of_footprint], story_hash)
|
598
|
+
|
599
|
+
# put all of the spaces in the model into a vector for intersection and surface matching
|
600
|
+
spaces = OpenStudio::Model::SpaceVector.new
|
601
|
+
model.getSpaces.sort.each do |space|
|
602
|
+
spaces << space
|
603
|
+
end
|
604
|
+
|
605
|
+
# flag for intersection and matching type
|
606
|
+
diagnostic_intersect = true
|
607
|
+
|
608
|
+
# only intersect if make_mid_story_surfaces_adiabatic false
|
609
|
+
if diagnostic_intersect
|
610
|
+
|
611
|
+
model.getPlanarSurfaces.sort.each do |surface|
|
612
|
+
array = []
|
613
|
+
vertices = surface.vertices
|
614
|
+
fixed = false
|
615
|
+
vertices.each do |vertex|
|
616
|
+
next if fixed
|
617
|
+
|
618
|
+
if array.include?(vertex)
|
619
|
+
# create a new set of vertices
|
620
|
+
new_vertices = OpenStudio::Point3dVector.new
|
621
|
+
array_b = []
|
622
|
+
surface.vertices.each do |vertex_b|
|
623
|
+
next if array_b.include?(vertex_b)
|
624
|
+
|
625
|
+
new_vertices << vertex_b
|
626
|
+
array_b << vertex_b
|
627
|
+
end
|
628
|
+
surface.setVertices(new_vertices)
|
629
|
+
num_removed = vertices.size - surface.vertices.size
|
630
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{surface.name} has duplicate vertices. Started with #{vertices.size} vertices, removed #{num_removed}.")
|
631
|
+
fixed = true
|
632
|
+
else
|
633
|
+
array << vertex
|
634
|
+
end
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
# remove collinear points in a surface
|
639
|
+
model.getPlanarSurfaces.sort.each do |surface|
|
640
|
+
new_vertices = OpenStudio.removeCollinear(surface.vertices)
|
641
|
+
starting_count = surface.vertices.size
|
642
|
+
final_count = new_vertices.size
|
643
|
+
if final_count < starting_count
|
644
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "Removing #{starting_count - final_count} collinear vertices from #{surface.name}.")
|
645
|
+
surface.setVertices(new_vertices)
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
649
|
+
# remove duplicate surfaces in a space (should be done after remove duplicate and collinear points)
|
650
|
+
model.getSpaces.sort.each do |space|
|
651
|
+
# secondary array to compare against
|
652
|
+
surfaces_b = space.surfaces.sort
|
653
|
+
|
654
|
+
space.surfaces.sort.each do |surface_a|
|
655
|
+
# delete from secondary array
|
656
|
+
surfaces_b.delete(surface_a)
|
657
|
+
|
658
|
+
surfaces_b.each do |surface_b|
|
659
|
+
next if surface_a == surface_b # dont' test against same surface
|
660
|
+
|
661
|
+
if surface_a.equalVertices(surface_b)
|
662
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{surface_a.name} and #{surface_b.name} in #{space.name} have duplicate geometry, removing #{surface_b.name}.")
|
663
|
+
surface_b.remove
|
664
|
+
elsif surface_a.reverseEqualVertices(surface_b)
|
665
|
+
# @todo add logic to determine which face naormal is reversed and which is correct
|
666
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{surface_a.name} and #{surface_b.name} in #{space.name} have reversed geometry, removing #{surface_b.name}.")
|
667
|
+
surface_b.remove
|
668
|
+
end
|
669
|
+
end
|
670
|
+
end
|
671
|
+
end
|
672
|
+
|
673
|
+
if !(bar_hash[:make_mid_story_surfaces_adiabatic])
|
674
|
+
# intersect and surface match two pair by pair
|
675
|
+
spaces_b = model.getSpaces.sort
|
676
|
+
# looping through vector of each space
|
677
|
+
model.getSpaces.sort.each do |space_a|
|
678
|
+
spaces_b.delete(space_a)
|
679
|
+
spaces_b.each do |space_b|
|
680
|
+
# OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Intersecting and matching surfaces between #{space_a.name} and #{space.name}")
|
681
|
+
spaces_temp = OpenStudio::Model::SpaceVector.new
|
682
|
+
spaces_temp << space_a
|
683
|
+
spaces_temp << space_b
|
684
|
+
# intersect and sort
|
685
|
+
OpenStudio::Model.intersectSurfaces(spaces_temp)
|
686
|
+
OpenStudio::Model.matchSurfaces(spaces_temp)
|
687
|
+
end
|
688
|
+
end
|
689
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Intersecting and matching surfaces in model, this will create additional geometry.')
|
690
|
+
else
|
691
|
+
# elsif bar_hash[:double_loaded_corridor] # only intersect spaces in each story, not between wtory
|
692
|
+
model.getBuilding.buildingStories.sort.each do |story|
|
693
|
+
# intersect and surface match two pair by pair
|
694
|
+
spaces_b = story.spaces.sort
|
695
|
+
# looping through vector of each space
|
696
|
+
story.spaces.sort.each do |space_a|
|
697
|
+
spaces_b.delete(space_a)
|
698
|
+
spaces_b.each do |space_b|
|
699
|
+
spaces_temp = OpenStudio::Model::SpaceVector.new
|
700
|
+
spaces_temp << space_a
|
701
|
+
spaces_temp << space_b
|
702
|
+
|
703
|
+
# intersect and sort
|
704
|
+
OpenStudio::Model.intersectSurfaces(spaces_temp)
|
705
|
+
OpenStudio::Model.matchSurfaces(spaces_temp)
|
706
|
+
end
|
707
|
+
end
|
708
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Intersecting and matching surfaces in story #{story.name}, this will create additional geometry.")
|
709
|
+
end
|
710
|
+
end
|
711
|
+
else
|
712
|
+
if !(bar_hash[:make_mid_story_surfaces_adiabatic])
|
713
|
+
# intersect surfaces
|
714
|
+
# (when bottom floor has many space types and one above doesn't will end up with heavily subdivided floor. Maybe use adiabatic and don't intersect floor/ceilings)
|
715
|
+
intersect_surfaces = true
|
716
|
+
if intersect_surfaces
|
717
|
+
OpenStudio::Model.intersectSurfaces(spaces)
|
718
|
+
OpenStudio::Model.matchSurfaces(spaces)
|
719
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Intersecting and matching surfaces in model, this will create additional geometry.')
|
720
|
+
end
|
721
|
+
else
|
722
|
+
# elsif bar_hash[:double_loaded_corridor] # only intersect spaces in each story, not between wtory
|
723
|
+
model.getBuilding.buildingStories.sort.each do |story|
|
724
|
+
story_spaces = OpenStudio::Model::SpaceVector.new
|
725
|
+
story.spaces.sort.each do |space|
|
726
|
+
story_spaces << space
|
727
|
+
end
|
728
|
+
|
729
|
+
# intersect and sort
|
730
|
+
OpenStudio::Model.intersectSurfaces(story_spaces)
|
731
|
+
OpenStudio::Model.matchSurfaces(story_spaces)
|
732
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Intersecting and matching surfaces in story #{story.name}, this will create additional geometry.")
|
733
|
+
end
|
734
|
+
end
|
735
|
+
|
736
|
+
end
|
737
|
+
|
738
|
+
# set boundary conditions if not already set when geometry was created
|
739
|
+
# @todo update this to use space original z value vs. story name
|
740
|
+
if bar_hash[:num_stories_below_grade] > 0
|
741
|
+
model.getBuildingStorys.sort.each do |story|
|
742
|
+
next if !story.name.to_s.include?('Story B')
|
743
|
+
|
744
|
+
story.spaces.sort.each do |space|
|
745
|
+
next if !new_spaces.include?(space)
|
746
|
+
|
747
|
+
space.surfaces.sort.each do |surface|
|
748
|
+
next if surface.surfaceType != 'Wall'
|
749
|
+
next if surface.outsideBoundaryCondition != 'Outdoors'
|
750
|
+
|
751
|
+
surface.setOutsideBoundaryCondition('Ground')
|
752
|
+
end
|
753
|
+
end
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
757
|
+
# set wall boundary condtions to adiabatic if using make_mid_story_surfaces_adiabatic prior to windows being made
|
758
|
+
if bar_hash[:make_mid_story_surfaces_adiabatic]
|
759
|
+
|
760
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Finding non-exterior walls and setting boundary condition to adiabatic')
|
761
|
+
|
762
|
+
# need to organize by story incase top story is partial story
|
763
|
+
# should also be only for a single bar
|
764
|
+
story_bounding = {}
|
765
|
+
missed_match_count = 0
|
766
|
+
|
767
|
+
# gather new spaces by story
|
768
|
+
new_spaces.each do |space|
|
769
|
+
story = space.buildingStory.get
|
770
|
+
if story_bounding.key?(story)
|
771
|
+
story_bounding[story][:spaces] << space
|
772
|
+
else
|
773
|
+
story_bounding[story] = { spaces: [space] }
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
777
|
+
# get bounding box for each story
|
778
|
+
story_bounding.each do |story, v|
|
779
|
+
# get bounding_box
|
780
|
+
bounding_box = OpenStudio::BoundingBox.new
|
781
|
+
v[:spaces].each do |space|
|
782
|
+
space.surfaces.each do |space_surface|
|
783
|
+
bounding_box.addPoints(space.transformation * space_surface.vertices)
|
784
|
+
end
|
785
|
+
end
|
786
|
+
min_x = bounding_box.minX.get
|
787
|
+
min_y = bounding_box.minY.get
|
788
|
+
max_x = bounding_box.maxX.get
|
789
|
+
max_y = bounding_box.maxY.get
|
790
|
+
ext_wall_toll = 0.01
|
791
|
+
|
792
|
+
# check surfaces again against min/max and change to adiabatic if not fully on one min or max x or y
|
793
|
+
# todo - may need to look at aidiabiatc constructions in downstream measure. Some may be exterior party wall others may be interior walls
|
794
|
+
v[:spaces].each do |space|
|
795
|
+
space.surfaces.each do |space_surface|
|
796
|
+
next if space_surface.surfaceType != 'Wall'
|
797
|
+
next if space_surface.outsideBoundaryCondition == 'Surface' # if if found a match leave it alone, don't change to adiabiatc
|
798
|
+
|
799
|
+
surface_bounding_box = OpenStudio::BoundingBox.new
|
800
|
+
surface_bounding_box.addPoints(space.transformation * space_surface.vertices)
|
801
|
+
surface_on_outside = false
|
802
|
+
# check xmin
|
803
|
+
if (surface_bounding_box.minX.get - min_x).abs < ext_wall_toll && (surface_bounding_box.maxX.get - min_x).abs < ext_wall_toll then surface_on_outside = true end
|
804
|
+
# check xmax
|
805
|
+
if (surface_bounding_box.minX.get - max_x).abs < ext_wall_toll && (surface_bounding_box.maxX.get - max_x).abs < ext_wall_toll then surface_on_outside = true end
|
806
|
+
# check ymin
|
807
|
+
if (surface_bounding_box.minY.get - min_y).abs < ext_wall_toll && (surface_bounding_box.maxY.get - min_y).abs < ext_wall_toll then surface_on_outside = true end
|
808
|
+
# check ymax
|
809
|
+
if (surface_bounding_box.minY.get - max_y).abs < ext_wall_toll && (surface_bounding_box.maxY.get - max_y).abs < ext_wall_toll then surface_on_outside = true end
|
810
|
+
|
811
|
+
# change if not exterior
|
812
|
+
if !surface_on_outside
|
813
|
+
space_surface.setOutsideBoundaryCondition('Adiabatic')
|
814
|
+
missed_match_count += 1
|
815
|
+
end
|
816
|
+
end
|
817
|
+
end
|
818
|
+
end
|
819
|
+
|
820
|
+
if missed_match_count > 0
|
821
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "#{missed_match_count} surfaces that were exterior appear to be interior walls and had boundary condition chagned to adiabiatic.")
|
822
|
+
end
|
823
|
+
end
|
824
|
+
|
825
|
+
# sort stories (by name for now but need better way)
|
826
|
+
sorted_stories = {}
|
827
|
+
new_spaces.each do |space|
|
828
|
+
next if !space.buildingStory.is_initialized
|
829
|
+
|
830
|
+
story = space.buildingStory.get
|
831
|
+
if !sorted_stories.key?(name.to_s)
|
832
|
+
sorted_stories[story.name.to_s] = story
|
833
|
+
end
|
834
|
+
end
|
835
|
+
|
836
|
+
# flag space types that have wwr overrides
|
837
|
+
space_type_wwr_overrides = {}
|
838
|
+
|
839
|
+
# loop through building stories, spaces, and surfaces
|
840
|
+
sorted_stories.sort.each_with_index do |(key, story), i|
|
841
|
+
# flag for adiabatic floor if building doesn't have ground exposed floor
|
842
|
+
if stories_flat[i][:bottom_story_ground_exposed_floor] == false
|
843
|
+
adiabatic_floor = true
|
844
|
+
end
|
845
|
+
# flag for adiabatic roof if building doesn't have exterior exposed roof
|
846
|
+
if stories_flat[i][:top_story_exterior_exposed_roof] == false
|
847
|
+
adiabatic_ceiling = true
|
848
|
+
end
|
849
|
+
|
850
|
+
# make all mid story floor and ceilings adiabatic if requested
|
851
|
+
if bar_hash[:make_mid_story_surfaces_adiabatic]
|
852
|
+
if i > 0
|
853
|
+
adiabatic_floor = true
|
854
|
+
end
|
855
|
+
if i < sorted_stories.size - 1
|
856
|
+
adiabatic_ceiling = true
|
857
|
+
end
|
858
|
+
end
|
859
|
+
|
860
|
+
# flag orientations for this story to recieve party walls
|
861
|
+
party_wall_facades = stories_flat[i][:story_party_walls]
|
862
|
+
|
863
|
+
story.spaces.each do |space|
|
864
|
+
next if !new_spaces.include?(space)
|
865
|
+
|
866
|
+
space.surfaces. each do |surface|
|
867
|
+
# set floor to adiabatic if requited
|
868
|
+
if adiabatic_floor && surface.surfaceType == 'Floor'
|
869
|
+
surface.setOutsideBoundaryCondition('Adiabatic')
|
870
|
+
elsif adiabatic_ceiling && surface.surfaceType == 'RoofCeiling'
|
871
|
+
surface.setOutsideBoundaryCondition('Adiabatic')
|
872
|
+
end
|
873
|
+
|
874
|
+
# skip of not exterior wall
|
875
|
+
next if surface.surfaceType != 'Wall'
|
876
|
+
next if surface.outsideBoundaryCondition != 'Outdoors'
|
877
|
+
|
878
|
+
# get the absolute azimuth for the surface so we can categorize it
|
879
|
+
absolute_azimuth = OpenStudio.convert(surface.azimuth, 'rad', 'deg').get + surface.space.get.directionofRelativeNorth + model.getBuilding.northAxis
|
880
|
+
absolute_azimuth = absolute_azimuth % 360.0 # should result in value between 0 and 360
|
881
|
+
absolute_azimuth = absolute_azimuth.round(5) # this was creating issues at 45 deg angles with opposing facades
|
882
|
+
|
883
|
+
# target wwr values that may be changed for specific space types
|
884
|
+
wwr_n = bar_hash[:building_wwr_n]
|
885
|
+
wwr_e = bar_hash[:building_wwr_e]
|
886
|
+
wwr_s = bar_hash[:building_wwr_s]
|
887
|
+
wwr_w = bar_hash[:building_wwr_w]
|
888
|
+
|
889
|
+
# look for space type specific wwr values
|
890
|
+
if surface.space.is_initialized && surface.space.get.spaceType.is_initialized
|
891
|
+
space_type = surface.space.get.spaceType.get
|
892
|
+
|
893
|
+
# see if space type has wwr value
|
894
|
+
bar_hash[:space_types].each do |k, v|
|
895
|
+
if v.key?(:space_type) && space_type == v[:space_type]
|
896
|
+
|
897
|
+
# if matching space type specifies a wwr then override the orientation specific recommendations for this surface.
|
898
|
+
if v.key?(:wwr)
|
899
|
+
wwr_n = v[:wwr]
|
900
|
+
wwr_e = v[:wwr]
|
901
|
+
wwr_s = v[:wwr]
|
902
|
+
wwr_w = v[:wwr]
|
903
|
+
space_type_wwr_overrides[space_type] = v[:wwr]
|
904
|
+
end
|
905
|
+
end
|
906
|
+
end
|
907
|
+
end
|
908
|
+
|
909
|
+
# add fenestration (wwr for now, maybe overhang and overhead doors later)
|
910
|
+
if (absolute_azimuth >= 315.0) || (absolute_azimuth < 45.0)
|
911
|
+
if party_wall_facades.include?('north')
|
912
|
+
surface.setOutsideBoundaryCondition('Adiabatic')
|
913
|
+
else
|
914
|
+
surface.setWindowToWallRatio(wwr_n)
|
915
|
+
end
|
916
|
+
elsif (absolute_azimuth >= 45.0) && (absolute_azimuth < 135.0)
|
917
|
+
if party_wall_facades.include?('east')
|
918
|
+
surface.setOutsideBoundaryCondition('Adiabatic')
|
919
|
+
else
|
920
|
+
surface.setWindowToWallRatio(wwr_e)
|
921
|
+
end
|
922
|
+
elsif (absolute_azimuth >= 135.0) && (absolute_azimuth < 225.0)
|
923
|
+
if party_wall_facades.include?('south')
|
924
|
+
surface.setOutsideBoundaryCondition('Adiabatic')
|
925
|
+
else
|
926
|
+
surface.setWindowToWallRatio(wwr_s)
|
927
|
+
end
|
928
|
+
elsif (absolute_azimuth >= 225.0) && (absolute_azimuth < 315.0)
|
929
|
+
if party_wall_facades.include?('west')
|
930
|
+
surface.setOutsideBoundaryCondition('Adiabatic')
|
931
|
+
else
|
932
|
+
surface.setWindowToWallRatio(wwr_w)
|
933
|
+
end
|
934
|
+
else
|
935
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', 'Unexpected value of facade: ' + absolute_azimuth + '.')
|
936
|
+
return false
|
937
|
+
end
|
938
|
+
end
|
939
|
+
end
|
940
|
+
end
|
941
|
+
|
942
|
+
# report space types with custom wwr values
|
943
|
+
space_type_wwr_overrides.each do |space_type, wwr|
|
944
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "For #{space_type.name} the default building wwr was replaced with a space type specfic value of #{wwr}")
|
945
|
+
end
|
946
|
+
|
947
|
+
new_floor_area_si = 0.0
|
948
|
+
new_spaces.each do |space|
|
949
|
+
new_floor_area_si += space.floorArea * space.multiplier
|
950
|
+
end
|
951
|
+
new_floor_area_ip = OpenStudio.convert(new_floor_area_si, 'm^2', 'ft^2').get
|
952
|
+
|
953
|
+
final_floor_area_ip = OpenStudio.convert(model.getBuilding.floorArea, 'm^2', 'ft^2').get
|
954
|
+
if new_floor_area_ip == final_floor_area_ip
|
955
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Created bar envelope with floor area of #{OpenStudio.toNeatString(new_floor_area_ip, 0, true)} ft^2.")
|
956
|
+
else
|
957
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Created bar envelope with floor area of #{OpenStudio.toNeatString(new_floor_area_ip, 0, true)} ft^2. Total building area is #{OpenStudio.toNeatString(final_floor_area_ip, 0, true)} ft^2.")
|
958
|
+
end
|
959
|
+
|
960
|
+
return new_spaces
|
961
|
+
end
|
962
|
+
|
963
|
+
# give info messages bar hash for create_bar method
|
964
|
+
#
|
965
|
+
# @param model [OpenStudio::Model::Model] OpenStudio model object
|
966
|
+
# @param args [Hash] user arguments
|
967
|
+
# @param length [Double] length of building in meters
|
968
|
+
# @param width [Double] width of building in meters
|
969
|
+
# @param floor_height [Double] floor height in meters
|
970
|
+
# @param center_of_footprint [OpenStudio::Point3d] center of footprint
|
971
|
+
# @param space_types_hash [Hash] space type hash
|
972
|
+
# @param num_stories [Double] number of stories
|
973
|
+
# @return [Boolean] returns true if successful, false if not
|
974
|
+
def self.bar_hash_setup_run(model, args, length, width, floor_height, center_of_footprint, space_types_hash, num_stories)
|
975
|
+
# create envelope
|
976
|
+
# populate bar_hash and create envelope with data from envelope_data_hash and user arguments
|
977
|
+
bar_hash = {}
|
978
|
+
bar_hash[:length] = length
|
979
|
+
bar_hash[:width] = width
|
980
|
+
bar_hash[:num_stories_below_grade] = args[:num_stories_below_grade]
|
981
|
+
bar_hash[:num_stories_above_grade] = args[:num_stories_above_grade]
|
982
|
+
bar_hash[:floor_height] = floor_height
|
983
|
+
bar_hash[:center_of_footprint] = center_of_footprint
|
984
|
+
bar_hash[:bar_division_method] = args[:bar_division_method]
|
985
|
+
bar_hash[:story_multiplier_method] = args[:story_multiplier_method]
|
986
|
+
bar_hash[:make_mid_story_surfaces_adiabatic] = args[:make_mid_story_surfaces_adiabatic]
|
987
|
+
bar_hash[:space_types] = space_types_hash
|
988
|
+
bar_hash[:building_wwr_n] = args[:wwr]
|
989
|
+
bar_hash[:building_wwr_s] = args[:wwr]
|
990
|
+
bar_hash[:building_wwr_e] = args[:wwr]
|
991
|
+
bar_hash[:building_wwr_w] = args[:wwr]
|
992
|
+
|
993
|
+
# round up non integer stoires to next integer
|
994
|
+
num_stories_round_up = num_stories.ceil
|
995
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Making bar with length of #{OpenStudio.toNeatString(OpenStudio.convert(length, 'm', 'ft').get, 0, true)} ft and width of #{OpenStudio.toNeatString(OpenStudio.convert(width, 'm', 'ft').get, 0, true)} ft")
|
996
|
+
|
997
|
+
# party_walls_array to be used by orientation specific or fractional party wall values
|
998
|
+
party_walls_array = [] # this is an array of arrays, where each entry is effective building story with array of directions
|
999
|
+
|
1000
|
+
if args[:party_wall_stories_north] + args[:party_wall_stories_south] + args[:party_wall_stories_east] + args[:party_wall_stories_west] > 0
|
1001
|
+
|
1002
|
+
# loop through effective number of stories add orientation specific party walls per user arguments
|
1003
|
+
num_stories_round_up.times do |i|
|
1004
|
+
test_value = i + 1 - bar_hash[:num_stories_below_grade]
|
1005
|
+
|
1006
|
+
array = []
|
1007
|
+
if args[:party_wall_stories_north] >= test_value
|
1008
|
+
array << 'north'
|
1009
|
+
end
|
1010
|
+
if args[:party_wall_stories_south] >= test_value
|
1011
|
+
array << 'south'
|
1012
|
+
end
|
1013
|
+
if args[:party_wall_stories_east] >= test_value
|
1014
|
+
array << 'east'
|
1015
|
+
end
|
1016
|
+
if args[:party_wall_stories_west] >= test_value
|
1017
|
+
array << 'west'
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
# populate party_wall_array for this story
|
1021
|
+
party_walls_array << array
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
# calculate party walls if using party_wall_fraction method
|
1026
|
+
if args[:party_wall_fraction] > 0 && !party_walls_array.empty?
|
1027
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Both orientation and fractional party wall values arguments were populated, will ignore fractional party wall input')
|
1028
|
+
elsif args[:party_wall_fraction] > 0
|
1029
|
+
# orientation of long and short side of building will vary based on building rotation
|
1030
|
+
|
1031
|
+
# full story ext wall area
|
1032
|
+
typical_length_facade_area = length * floor_height
|
1033
|
+
typical_width_facade_area = width * floor_height
|
1034
|
+
|
1035
|
+
# top story ext wall area, may be partial story
|
1036
|
+
partial_story_multiplier = (1.0 - args[:num_stories_above_grade].ceil + args[:num_stories_above_grade])
|
1037
|
+
area_multiplier = partial_story_multiplier
|
1038
|
+
edge_multiplier = Math.sqrt(area_multiplier)
|
1039
|
+
top_story_length = length * edge_multiplier
|
1040
|
+
top_story_width = width * edge_multiplier
|
1041
|
+
top_story_length_facade_area = top_story_length * floor_height
|
1042
|
+
top_story_width_facade_area = top_story_width * floor_height
|
1043
|
+
|
1044
|
+
total_exterior_wall_area = 2 * (length + width) * (args[:num_stories_above_grade].ceil - 1.0) * floor_height + 2 * (top_story_length + top_story_width) * floor_height
|
1045
|
+
target_party_wall_area = total_exterior_wall_area * args[:party_wall_fraction]
|
1046
|
+
|
1047
|
+
width_counter = 0
|
1048
|
+
width_area = 0.0
|
1049
|
+
facade_area = typical_width_facade_area
|
1050
|
+
until (width_area + facade_area >= target_party_wall_area) || (width_counter == args[:num_stories_above_grade].ceil * 2)
|
1051
|
+
# update facade area for top story
|
1052
|
+
if width_counter == args[:num_stories_above_grade].ceil - 1 || width_counter == args[:num_stories_above_grade].ceil * 2 - 1
|
1053
|
+
facade_area = top_story_width_facade_area
|
1054
|
+
else
|
1055
|
+
facade_area = typical_width_facade_area
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
width_counter += 1
|
1059
|
+
width_area += facade_area
|
1060
|
+
|
1061
|
+
end
|
1062
|
+
width_area_remainder = target_party_wall_area - width_area
|
1063
|
+
|
1064
|
+
length_counter = 0
|
1065
|
+
length_area = 0.0
|
1066
|
+
facade_area = typical_length_facade_area
|
1067
|
+
until (length_area + facade_area >= target_party_wall_area) || (length_counter == args[:num_stories_above_grade].ceil * 2)
|
1068
|
+
# update facade area for top story
|
1069
|
+
if length_counter == args[:num_stories_above_grade].ceil - 1 || length_counter == args[:num_stories_above_grade].ceil * 2 - 1
|
1070
|
+
facade_area = top_story_length_facade_area
|
1071
|
+
else
|
1072
|
+
facade_area = typical_length_facade_area
|
1073
|
+
end
|
1074
|
+
|
1075
|
+
length_counter += 1
|
1076
|
+
length_area += facade_area
|
1077
|
+
end
|
1078
|
+
length_area_remainder = target_party_wall_area - length_area
|
1079
|
+
|
1080
|
+
# get rotation and best fit to adjust orientation for fraction party wall
|
1081
|
+
rotation = args[:building_rotation] % 360.0 # should result in value between 0 and 360
|
1082
|
+
card_dir_array = [0.0, 90.0, 180.0, 270.0, 360.0]
|
1083
|
+
# reverse array to properly handle 45, 135, 225, and 315
|
1084
|
+
best_fit = card_dir_array.reverse.min_by { |x| (x.to_f - rotation).abs }
|
1085
|
+
|
1086
|
+
if ![90.0, 270.0].include? best_fit
|
1087
|
+
width_card_dir = ['east', 'west']
|
1088
|
+
length_card_dir = ['north', 'south']
|
1089
|
+
else # if rotation is closest to 90 or 270 then reverse which orientation is used for length and width
|
1090
|
+
width_card_dir = ['north', 'south']
|
1091
|
+
length_card_dir = ['east', 'west']
|
1092
|
+
end
|
1093
|
+
|
1094
|
+
# if dont' find enough on short sides
|
1095
|
+
if width_area_remainder <= typical_length_facade_area
|
1096
|
+
|
1097
|
+
num_stories_round_up.times do |i|
|
1098
|
+
if i + 1 <= args[:num_stories_below_grade]
|
1099
|
+
party_walls_array << []
|
1100
|
+
next
|
1101
|
+
end
|
1102
|
+
if i + 1 - args[:num_stories_below_grade] <= width_counter
|
1103
|
+
if i + 1 - args[:num_stories_below_grade] <= width_counter - args[:num_stories_above_grade]
|
1104
|
+
party_walls_array << width_card_dir
|
1105
|
+
else
|
1106
|
+
party_walls_array << [width_card_dir.first]
|
1107
|
+
end
|
1108
|
+
else
|
1109
|
+
party_walls_array << []
|
1110
|
+
end
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
else
|
1114
|
+
# use long sides instead
|
1115
|
+
num_stories_round_up.times do |i|
|
1116
|
+
if i + 1 <= args[:num_stories_below_grade]
|
1117
|
+
party_walls_array << []
|
1118
|
+
next
|
1119
|
+
end
|
1120
|
+
if i + 1 - args[:num_stories_below_grade] <= length_counter
|
1121
|
+
if i + 1 - args[:num_stories_below_grade] <= length_counter - args[:num_stories_above_grade]
|
1122
|
+
party_walls_array << length_card_dir
|
1123
|
+
else
|
1124
|
+
party_walls_array << [length_card_dir.first]
|
1125
|
+
end
|
1126
|
+
else
|
1127
|
+
party_walls_array << []
|
1128
|
+
end
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
end
|
1132
|
+
# @todo currently won't go past making two opposing sets of walls party walls. Info and registerValue are after create_bar in measure.rb
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
# populate bar hash with story information
|
1136
|
+
bar_hash[:stories] = {}
|
1137
|
+
num_stories_round_up.times do |i|
|
1138
|
+
if party_walls_array.empty?
|
1139
|
+
party_walls = []
|
1140
|
+
else
|
1141
|
+
party_walls = party_walls_array[i]
|
1142
|
+
end
|
1143
|
+
|
1144
|
+
# add below_partial_story
|
1145
|
+
if num_stories.ceil > num_stories && i == num_stories_round_up - 2
|
1146
|
+
below_partial_story = true
|
1147
|
+
else
|
1148
|
+
below_partial_story = false
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
# bottom_story_ground_exposed_floor and top_story_exterior_exposed_roof already setup as bool
|
1152
|
+
bar_hash[:stories]["key #{i}"] = { story_party_walls: party_walls, story_min_multiplier: 1, story_included_in_building_area: true, below_partial_story: below_partial_story, bottom_story_ground_exposed_floor: args[:bottom_story_ground_exposed_floor], top_story_exterior_exposed_roof: args[:top_story_exterior_exposed_roof] }
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
# create bar
|
1156
|
+
new_spaces = create_bar(model, bar_hash)
|
1157
|
+
|
1158
|
+
# check expect roof and wall area
|
1159
|
+
target_footprint = bar_hash[:length] * bar_hash[:width]
|
1160
|
+
ground_floor_area = 0.0
|
1161
|
+
roof_area = 0.0
|
1162
|
+
new_spaces.each do |space|
|
1163
|
+
space.surfaces.each do |surface|
|
1164
|
+
if surface.surfaceType == 'Floor' && surface.outsideBoundaryCondition == 'Ground'
|
1165
|
+
ground_floor_area += surface.netArea
|
1166
|
+
elsif surface.surfaceType == 'RoofCeiling' && surface.outsideBoundaryCondition == 'Outdoors'
|
1167
|
+
roof_area += surface.netArea
|
1168
|
+
end
|
1169
|
+
end
|
1170
|
+
end
|
1171
|
+
# @todo extend to address when top and or bottom story are not exposed via argument
|
1172
|
+
if ground_floor_area > target_footprint + 0.001 || roof_area > target_footprint + 0.001
|
1173
|
+
# OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "Ground exposed floor or Roof area is larger than footprint, likely inter-floor surface matching and intersection error.")
|
1174
|
+
# return false
|
1175
|
+
|
1176
|
+
# not providing adiabatic work around when top story is partial story.
|
1177
|
+
if args[:num_stories_above_grade].to_f != args[:num_stories_above_grade].ceil
|
1178
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', 'Ground exposed floor or Roof area is larger than footprint, likely inter-floor surface matching and intersection error.')
|
1179
|
+
return false
|
1180
|
+
else
|
1181
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Ground exposed floor or Roof area is larger than footprint, likely inter-floor surface matching and intersection error, altering impacted surfaces boundary condition to be adiabatic.')
|
1182
|
+
match_error = true
|
1183
|
+
end
|
1184
|
+
else
|
1185
|
+
match_error = false
|
1186
|
+
end
|
1187
|
+
|
1188
|
+
# @todo should be able to remove this fix after OpenStudio intersection issue is fixed. At that time turn the above message into an error with return false after it
|
1189
|
+
if match_error
|
1190
|
+
|
1191
|
+
# identify z value of top and bottom story
|
1192
|
+
bottom_story = nil
|
1193
|
+
top_story = nil
|
1194
|
+
new_spaces.each do |space|
|
1195
|
+
story = space.buildingStory.get
|
1196
|
+
nom_z = story.nominalZCoordinate.get
|
1197
|
+
if bottom_story.nil?
|
1198
|
+
bottom_story = nom_z
|
1199
|
+
elsif bottom_story > nom_z
|
1200
|
+
bottom_story = nom_z
|
1201
|
+
end
|
1202
|
+
if top_story.nil?
|
1203
|
+
top_story = nom_z
|
1204
|
+
elsif top_story < nom_z
|
1205
|
+
top_story = nom_z
|
1206
|
+
end
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
# change boundary condition and intersection as needed.
|
1210
|
+
new_spaces.each do |space|
|
1211
|
+
if space.buildingStory.get.nominalZCoordinate.get > bottom_story
|
1212
|
+
# change floors
|
1213
|
+
space.surfaces.each do |surface|
|
1214
|
+
next if !(surface.surfaceType == 'Floor' && surface.outsideBoundaryCondition == 'Ground')
|
1215
|
+
|
1216
|
+
surface.setOutsideBoundaryCondition('Adiabatic')
|
1217
|
+
end
|
1218
|
+
end
|
1219
|
+
if space.buildingStory.get.nominalZCoordinate.get < top_story
|
1220
|
+
# change ceilings
|
1221
|
+
space.surfaces.each do |surface|
|
1222
|
+
next if !(surface.surfaceType == 'RoofCeiling' && surface.outsideBoundaryCondition == 'Outdoors')
|
1223
|
+
|
1224
|
+
surface.setOutsideBoundaryCondition('Adiabatic')
|
1225
|
+
end
|
1226
|
+
end
|
1227
|
+
end
|
1228
|
+
end
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
# create bar from arguments and building type hash
|
1232
|
+
#
|
1233
|
+
# @param args [Hash] user arguments
|
1234
|
+
# @option args [Double] :single_floor_area (0.0) Single floor area in ft^2. Non-zero value will fix the single floor area, overriding a user entry for total_bldg_floor_area
|
1235
|
+
# @option args [Double] :total_bldg_floor_area (10000.0) Total building floor area in ft^2
|
1236
|
+
# @option args [Double] :floor_height (0.0) Typical floor to floor height. Selecting a typical floor height of 0 will trigger a smart building type default.
|
1237
|
+
# @option args [Boolean] :custom_height_bar (true) This is argument value is only relevant when smart default floor to floor height is used for a building type that has spaces with custom heights.
|
1238
|
+
# @option args [Integer] :num_stories_above_grade (1) Number of stories above grade
|
1239
|
+
# @option args [Integer] :num_stories_below_grade (0) Number of stories below grade
|
1240
|
+
# @option args [Double] :building_rotation (0.0) Building rotation. Set Building Rotation off of North (positive value is clockwise). Rotation applied after geometry generation. Values greater than +/- 45 will result in aspect ratio and party wall orientations that do not match cardinal directions of the inputs.
|
1241
|
+
# @option args [Double] :ns_to_ew_ratio (0.0) Ratio of North/South facade length relative to east/west facade length. Selecting an aspect ratio of 0 will trigger a smart building type default. Aspect ratios less than one are not recommended for sliced bar geometry, instead rotate building and use a greater than 1 aspect ratio.
|
1242
|
+
# @option args [Double] :perim_mult (0.0) Perimeter multiplier. Selecting a value of 0 will trigger a smart building type default. This represents a multiplier for the building perimeter relative to the perimeter of a rectangular building that meets the area and aspect ratio inputs. Other than the smart default of 0.0 this argument should have a value of 1.0 or higher and is only applicable Multiple Space Types - Individual Stories Sliced division method.
|
1243
|
+
# @option args [Double] :bar_width (0.0) Bar Width. Non-zero value will fix the building width, overriding user entry for Perimeter Multiplier. NS/EW Aspect Ratio may be limited based on target width.
|
1244
|
+
# @option args [Double] :bar_sep_dist_mult (10.0) Bar separation distance multiplier. Multiplier of separation between bar elements relative to building height.
|
1245
|
+
# @option args [Double] :wwr (0.0) Window to wall ratio. Selecting a window to wall ratio of 0 will trigger a smart building type default.
|
1246
|
+
# @option args [Double] :party_wall_fraction (0.0) fraction of exterior wall area with an adjacent structure
|
1247
|
+
# @option args [Integer] :party_wall_stories_north (0) Number of North facing stories with party wall
|
1248
|
+
# @option args [Integer] :party_wall_stories_south (0) Number of South facing stories with party wall
|
1249
|
+
# @option args [Integer] :party_wall_stories_east (0) Number of East facing stories with party wall
|
1250
|
+
# @option args [Integer] :party_wall_stories_west (0) Number of West facing stories with party wall
|
1251
|
+
# @option args [Boolean] :bottom_story_ground_exposed_floor (true) Is the bottom story exposed to the ground
|
1252
|
+
# @option args [Boolean] :top_story_exterior_exposed_roof (true) Is the top story an exterior roof
|
1253
|
+
# @option args [String] :story_multiplier_method ('Basements Ground Mid Top') Calculation method for story multiplier. Options are 'None' and 'Basements Ground Mid Top'
|
1254
|
+
# @option args [Boolean] :make_mid_story_surfaces_adiabatic (true) Make mid story floor surfaces adiabatic. If set to true, this will skip surface intersection and make mid story floors and celings adiabatic, not just at multiplied gaps.
|
1255
|
+
# @option args [String] :bar_division_method ('Multiple Space Types - Individual Stories Sliced') Division method for bar space types. Options are 'Multiple Space Types - Simple Sliced', 'Multiple Space Types - Individual Stories Sliced', 'Single Space Type - Core and Perimeter'
|
1256
|
+
# @option args [String] :double_loaded_corridor ('Primary Space Type') Method for double loaded corridor. Add double loaded corridor for building types that have a defined circulation space type, to the selected space types. Options are 'None' and 'Primary Space Type'
|
1257
|
+
# @option args [String] :space_type_sort_logic ('Building Type > Size') Space type sorting method. Options are 'Size' and 'Building Type > Size'
|
1258
|
+
# @option args [String] :template ('90.1-2013') target standard
|
1259
|
+
# @param building_type_hash [Array<Hash>] array of building type hashes
|
1260
|
+
# @option building_type_hash [Double] :frac_bldg_area fraction of building area
|
1261
|
+
# @option building_type_hash [Hash] :space_types hash of space types data
|
1262
|
+
# @return [Boolean] returns true if successful, false if not
|
1263
|
+
def self.create_bar_from_args_and_building_type_hash(model, args, building_type_hash)
|
1264
|
+
# set argument defaults if not present
|
1265
|
+
args[:single_floor_area] = args.fetch(:single_floor_area, 0.0)
|
1266
|
+
args[:total_bldg_floor_area] = args.fetch(:total_bldg_floor_area, 10000.0)
|
1267
|
+
args[:floor_height] = args.fetch(:floor_height, 0.0)
|
1268
|
+
args[:custom_height_bar] = args.fetch(:custom_height_bar, true)
|
1269
|
+
args[:num_stories_above_grade] = args.fetch(:num_stories_above_grade, 1)
|
1270
|
+
args[:num_stories_below_grade] = args.fetch(:num_stories_below_grade, 0)
|
1271
|
+
args[:building_rotation] = args.fetch(:building_rotation, 0.0)
|
1272
|
+
args[:ns_to_ew_ratio] = args.fetch(:ns_to_ew_ratio, 0.0)
|
1273
|
+
args[:perim_mult] = args.fetch(:perim_mult, 0.0)
|
1274
|
+
args[:bar_width] = args.fetch(:bar_width, 0.0)
|
1275
|
+
args[:bar_sep_dist_mult] = args.fetch(:bar_sep_dist_mult, 10.0)
|
1276
|
+
args[:wwr] = args.fetch(:wwr, 0.0)
|
1277
|
+
args[:party_wall_fraction] = args.fetch(:party_wall_fraction, 0.0)
|
1278
|
+
args[:party_wall_stories_north] = args.fetch(:party_wall_stories_north, 0)
|
1279
|
+
args[:party_wall_stories_south] = args.fetch(:party_wall_stories_south, 0)
|
1280
|
+
args[:party_wall_stories_east] = args.fetch(:party_wall_stories_east, 0)
|
1281
|
+
args[:party_wall_stories_west] = args.fetch(:party_wall_stories_west, 0)
|
1282
|
+
args[:bottom_story_ground_exposed_floor] = args.fetch(:bottom_story_ground_exposed_floor, true)
|
1283
|
+
args[:top_story_exterior_exposed_roof] = args.fetch(:top_story_exterior_exposed_roof, true)
|
1284
|
+
args[:story_multiplier_method] = args.fetch(:story_multiplier_method, 'Basements Ground Mid Top')
|
1285
|
+
args[:make_mid_story_surfaces_adiabatic] = args.fetch(:make_mid_story_surfaces_adiabatic, true)
|
1286
|
+
args[:bar_division_method] = args.fetch(:bar_division_method, 'Multiple Space Types - Individual Stories Sliced')
|
1287
|
+
args[:double_loaded_corridor] = args.fetch(:double_loaded_corridor, 'Primary Space Type')
|
1288
|
+
args[:space_type_sort_logic] = args.fetch(:space_type_sort_logic, 'Building Type > Size')
|
1289
|
+
args[:template] = args.fetch(:template, '90.1-2013')
|
1290
|
+
|
1291
|
+
# get defaults for the primary building type
|
1292
|
+
primary_building_type = args[:primary_building_type]
|
1293
|
+
building_form_defaults = OpenstudioStandards::Geometry.building_form_defaults(primary_building_type)
|
1294
|
+
|
1295
|
+
# if aspect ratio, story height or wwr have argument value of 0 then use smart building type defaults
|
1296
|
+
# store list of defaulted items
|
1297
|
+
defaulted_args = []
|
1298
|
+
|
1299
|
+
if args[:ns_to_ew_ratio] == 0.0
|
1300
|
+
args[:ns_to_ew_ratio] = building_form_defaults[:aspect_ratio]
|
1301
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "0.0 value for aspect ratio will be replaced with smart default for #{primary_building_type} of #{building_form_defaults[:aspect_ratio]}.")
|
1302
|
+
end
|
1303
|
+
|
1304
|
+
if args[:perim_mult] == 0.0
|
1305
|
+
# if this is not defined then use default of 1.0
|
1306
|
+
if !building_form_defaults.key?(:perim_mult)
|
1307
|
+
args[:perim_mult] = 1.0
|
1308
|
+
else
|
1309
|
+
args[:perim_mult] = building_form_defaults[:perim_mult]
|
1310
|
+
end
|
1311
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "0.0 value for minimum perimeter multiplier will be replaced with smart default for #{primary_building_type} of #{building_form_defaults[:perim_mult]}.")
|
1312
|
+
elsif args[:perim_mult] < 1.0
|
1313
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', 'Other than the smart default value of 0, the minimum perimeter multiplier should be equal to 1.0 or greater.')
|
1314
|
+
return false
|
1315
|
+
end
|
1316
|
+
|
1317
|
+
if args[:floor_height] == 0.0
|
1318
|
+
args[:floor_height] = building_form_defaults[:typical_story]
|
1319
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "0.0 value for floor height will be replaced with smart default for #{primary_building_type} of #{building_form_defaults[:typical_story]}.")
|
1320
|
+
defaulted_args << 'floor_height'
|
1321
|
+
end
|
1322
|
+
# because of this can't set wwr to 0.0. If that is desired then we can change this to check for 1.0 instead of 0.0
|
1323
|
+
if args[:wwr] == 0.0
|
1324
|
+
args[:wwr] = building_form_defaults[:wwr]
|
1325
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "0.0 value for window to wall ratio will be replaced with smart default for #{primary_building_type} of #{building_form_defaults[:wwr]}.")
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
# report initial condition of model
|
1329
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "The building started with #{model.getSpaces.size} spaces.")
|
1330
|
+
|
1331
|
+
# determine of ns_ew needs to be mirrored
|
1332
|
+
mirror_ns_ew = false
|
1333
|
+
rotation = model.getBuilding.northAxis
|
1334
|
+
if rotation > 45.0 && rotation < 135.0
|
1335
|
+
mirror_ns_ew = true
|
1336
|
+
elsif rotation > 45.0 && rotation < 135.0
|
1337
|
+
mirror_ns_ew = true
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
# remove non-resource objects not removed by removing the building
|
1341
|
+
# remove_non_resource_objects(model)
|
1342
|
+
|
1343
|
+
# creating space types for requested building types
|
1344
|
+
building_type_hash.each do |building_type, building_type_hash|
|
1345
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Creating Space Types for #{building_type}.")
|
1346
|
+
|
1347
|
+
# mapping building_type name is needed for a few methods
|
1348
|
+
temp_standard = Standard.build('90.1-2013')
|
1349
|
+
building_type = temp_standard.model_get_lookup_name(building_type)
|
1350
|
+
|
1351
|
+
# create space_type_map from array
|
1352
|
+
sum_of_ratios = 0.0
|
1353
|
+
building_type_hash[:space_types] = building_type_hash[:space_types].sort_by { |k, v| v[:ratio] }.to_h
|
1354
|
+
building_type_hash[:space_types].each do |space_type_name, hash|
|
1355
|
+
next if hash[:space_type_gen] == false # space types like undeveloped and basement are skipped.
|
1356
|
+
|
1357
|
+
# create space type
|
1358
|
+
space_type = OpenStudio::Model::SpaceType.new(model)
|
1359
|
+
space_type.setStandardsBuildingType(building_type)
|
1360
|
+
space_type.setStandardsSpaceType(space_type_name)
|
1361
|
+
space_type.setName("#{building_type} #{space_type_name}")
|
1362
|
+
|
1363
|
+
# set color
|
1364
|
+
test = temp_standard.space_type_apply_rendering_color(space_type)
|
1365
|
+
if !test
|
1366
|
+
# @todo once fixed in standards un-comment this
|
1367
|
+
# OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "Could not find color for #{space_type.name}")
|
1368
|
+
end
|
1369
|
+
|
1370
|
+
# extend hash to hold new space type object
|
1371
|
+
hash[:space_type] = space_type
|
1372
|
+
|
1373
|
+
# add to sum_of_ratios counter for adjustment multiplier
|
1374
|
+
sum_of_ratios += hash[:ratio]
|
1375
|
+
end
|
1376
|
+
|
1377
|
+
# store multiplier needed to adjust sum of ratios to equal 1.0
|
1378
|
+
building_type_hash[:ratio_adjustment_multiplier] = 1.0 / sum_of_ratios
|
1379
|
+
end
|
1380
|
+
|
1381
|
+
# calculate length and with of bar
|
1382
|
+
total_bldg_floor_area_si = OpenStudio.convert(args[:total_bldg_floor_area], 'ft^2', 'm^2').get
|
1383
|
+
single_floor_area_si = OpenStudio.convert(args[:single_floor_area], 'ft^2', 'm^2').get
|
1384
|
+
|
1385
|
+
# store number of stories
|
1386
|
+
num_stories = args[:num_stories_below_grade] + args[:num_stories_above_grade]
|
1387
|
+
|
1388
|
+
# handle user-assigned single floor plate size condition
|
1389
|
+
if args[:single_floor_area] > 0.0
|
1390
|
+
footprint_si = single_floor_area_si
|
1391
|
+
total_bldg_floor_area_si = footprint_si * num_stories.to_f
|
1392
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'User-defined single floor area was used for calculation of total building floor area')
|
1393
|
+
# add warning if custom_height_bar is true and applicable building type is selected
|
1394
|
+
if args[:custom_height_bar]
|
1395
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Cannot use custom height bar with single floor area method, will not create custom height bar.')
|
1396
|
+
args[:custom_height_bar] = false
|
1397
|
+
end
|
1398
|
+
else
|
1399
|
+
footprint_si = nil
|
1400
|
+
end
|
1401
|
+
|
1402
|
+
# populate space_types_hash
|
1403
|
+
space_types_hash = {}
|
1404
|
+
multi_height_space_types_hash = {}
|
1405
|
+
custom_story_heights = []
|
1406
|
+
if args[:space_type_sort_logic] == 'Building Type > Size'
|
1407
|
+
building_type_hash = building_type_hash.sort_by { |k, v| v[:frac_bldg_area] }
|
1408
|
+
end
|
1409
|
+
building_type_hash.each do |building_type, building_type_hash|
|
1410
|
+
if args[:double_loaded_corridor] == 'Primary Space Type'
|
1411
|
+
|
1412
|
+
# see if building type has circulation space type, if so then merge that along with default space type into hash key in place of space type
|
1413
|
+
default_st = nil
|
1414
|
+
circ_st = nil
|
1415
|
+
building_type_hash[:space_types].each do |space_type_name, hash|
|
1416
|
+
if hash[:default] then default_st = space_type_name end
|
1417
|
+
if hash[:circ] then circ_st = space_type_name end
|
1418
|
+
end
|
1419
|
+
|
1420
|
+
# update building hash
|
1421
|
+
if !default_st.nil? && !circ_st.nil?
|
1422
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Combining #{default_st} and #{circ_st} into a group representing a double loaded corridor")
|
1423
|
+
|
1424
|
+
# add new item
|
1425
|
+
building_type_hash[:space_types]['Double Loaded Corridor'] = {}
|
1426
|
+
double_loaded_st = building_type_hash[:space_types]['Double Loaded Corridor']
|
1427
|
+
double_loaded_st[:ratio] = building_type_hash[:space_types][default_st][:ratio] + building_type_hash[:space_types][circ_st][:ratio]
|
1428
|
+
double_loaded_st[:double_loaded_corridor] = true
|
1429
|
+
double_loaded_st[:space_type] = model.getBuilding
|
1430
|
+
double_loaded_st[:children] = {}
|
1431
|
+
building_type_hash[:space_types][default_st][:orig_ratio] = building_type_hash[:ratio_adjustment_multiplier] * building_type_hash[:frac_bldg_area] * building_type_hash[:space_types][default_st][:ratio]
|
1432
|
+
building_type_hash[:space_types][circ_st][:orig_ratio] = building_type_hash[:ratio_adjustment_multiplier] * building_type_hash[:frac_bldg_area] * building_type_hash[:space_types][circ_st][:ratio]
|
1433
|
+
building_type_hash[:space_types][default_st][:name] = default_st
|
1434
|
+
building_type_hash[:space_types][circ_st][:name] = circ_st
|
1435
|
+
double_loaded_st[:children][:default] = building_type_hash[:space_types][default_st]
|
1436
|
+
double_loaded_st[:children][:circ] = building_type_hash[:space_types][circ_st]
|
1437
|
+
double_loaded_st[:orig_ratio] = 0.0
|
1438
|
+
|
1439
|
+
# zero out ratios from old item (don't delete because I still want the space types made)
|
1440
|
+
building_type_hash[:space_types][default_st][:ratio] = 0.0
|
1441
|
+
building_type_hash[:space_types][circ_st][:ratio] = 0.0
|
1442
|
+
end
|
1443
|
+
end
|
1444
|
+
|
1445
|
+
building_type_hash[:space_types].each do |space_type_name, hash|
|
1446
|
+
next if hash[:space_type_gen] == false
|
1447
|
+
|
1448
|
+
space_type = hash[:space_type]
|
1449
|
+
ratio_of_bldg_total = hash[:ratio] * building_type_hash[:ratio_adjustment_multiplier] * building_type_hash[:frac_bldg_area]
|
1450
|
+
final_floor_area = ratio_of_bldg_total * total_bldg_floor_area_si # I think I can just pass ratio but passing in area is cleaner
|
1451
|
+
|
1452
|
+
# only add custom height space if 0 is used for floor_height
|
1453
|
+
if defaulted_args.include?(:floor_height) && hash.key?(:story_height) && args[:custom_height_bar]
|
1454
|
+
multi_height_space_types_hash[space_type] = { floor_area: final_floor_area, space_type: space_type, story_height: hash[:story_height] }
|
1455
|
+
if hash.key?(:orig_ratio) then multi_height_space_types_hash[space_type][:orig_ratio] = hash[:orig_ratio] end
|
1456
|
+
custom_story_heights << hash[:story_height]
|
1457
|
+
if args[:wwr] == 0 && hash.key?(:wwr)
|
1458
|
+
multi_height_space_types_hash[space_type][:wwr] = hash[:wwr]
|
1459
|
+
end
|
1460
|
+
else
|
1461
|
+
# only add wwr if 0 used for wwr arg and if space type has wwr as key
|
1462
|
+
space_types_hash[space_type] = { floor_area: final_floor_area, space_type: space_type }
|
1463
|
+
if hash.key?(:orig_ratio) then space_types_hash[space_type][:orig_ratio] = hash[:orig_ratio] end
|
1464
|
+
if args[:wwr] == 0 && hash.key?(:wwr)
|
1465
|
+
space_types_hash[space_type][:wwr] = hash[:wwr]
|
1466
|
+
end
|
1467
|
+
if hash[:double_loaded_corridor]
|
1468
|
+
space_types_hash[space_type][:children] = hash[:children]
|
1469
|
+
end
|
1470
|
+
end
|
1471
|
+
end
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
# resort if not sorted by building type
|
1475
|
+
if args[:space_type_sort_logic] == 'Size'
|
1476
|
+
# added code to convert to hash. I use sort_by 3 other times, but those seem to be working fine as is now.
|
1477
|
+
space_types_hash = Hash[space_types_hash.sort_by { |k, v| v[:floor_area] }]
|
1478
|
+
end
|
1479
|
+
|
1480
|
+
# calculate targets for testing
|
1481
|
+
target_areas = {} # used for checks
|
1482
|
+
target_areas_cust_height = 0.0
|
1483
|
+
space_types_hash.each do |k, v|
|
1484
|
+
if v.key?(:orig_ratio)
|
1485
|
+
target_areas[k] = v[:orig_ratio] * total_bldg_floor_area_si
|
1486
|
+
else
|
1487
|
+
target_areas[k] = v[:floor_area]
|
1488
|
+
end
|
1489
|
+
end
|
1490
|
+
multi_height_space_types_hash.each do |k, v|
|
1491
|
+
if v.key?(:orig_ratio)
|
1492
|
+
target_areas[k] = v[:orig_ratio] * total_bldg_floor_area_si
|
1493
|
+
target_areas_cust_height += v[:orig_ratio] * total_bldg_floor_area_si
|
1494
|
+
else
|
1495
|
+
target_areas[k] = v[:floor_area]
|
1496
|
+
target_areas_cust_height += v[:floor_area]
|
1497
|
+
end
|
1498
|
+
end
|
1499
|
+
|
1500
|
+
# gather inputs
|
1501
|
+
if footprint_si.nil?
|
1502
|
+
footprint_si = (total_bldg_floor_area_si - target_areas_cust_height) / num_stories.to_f
|
1503
|
+
end
|
1504
|
+
floor_height = OpenStudio.convert(args[:floor_height], 'ft', 'm').get
|
1505
|
+
min_allow_size = OpenStudio.convert(15.0, 'ft', 'm').get
|
1506
|
+
specified_bar_width_si = OpenStudio.convert(args[:bar_width], 'ft', 'm').get
|
1507
|
+
|
1508
|
+
# set custom width
|
1509
|
+
if specified_bar_width_si > 0
|
1510
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Ignoring perimeter multiplier argument when non zero width argument is used')
|
1511
|
+
if footprint_si / specified_bar_width_si >= min_allow_size
|
1512
|
+
width = specified_bar_width_si
|
1513
|
+
length = footprint_si / width
|
1514
|
+
else
|
1515
|
+
length = min_allow_size
|
1516
|
+
width = footprint_si / length
|
1517
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'User specified width results in a length that is too short, adjusting width to be narrower than specified.')
|
1518
|
+
end
|
1519
|
+
width_cust_height = specified_bar_width_si
|
1520
|
+
else
|
1521
|
+
width = Math.sqrt(footprint_si / args[:ns_to_ew_ratio])
|
1522
|
+
length = footprint_si / width
|
1523
|
+
width_cust_height = Math.sqrt(target_areas_cust_height / args[:ns_to_ew_ratio])
|
1524
|
+
end
|
1525
|
+
length_cust_height = target_areas_cust_height / width_cust_height
|
1526
|
+
if args[:perim_mult] > 1.0 && target_areas_cust_height > 0.0
|
1527
|
+
# @todo update tests that hit this warning
|
1528
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Ignoring perimeter multiplier for bar that represents custom height spaces.')
|
1529
|
+
end
|
1530
|
+
|
1531
|
+
# check if dual bar is needed
|
1532
|
+
dual_bar = false
|
1533
|
+
if specified_bar_width_si > 0.0 && args[:bar_division_method] == 'Multiple Space Types - Individual Stories Sliced'
|
1534
|
+
if length / width != args[:ns_to_ew_ratio]
|
1535
|
+
|
1536
|
+
if args[:ns_to_ew_ratio] >= 1.0 && args[:ns_to_ew_ratio] > length / width
|
1537
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "Can't meet target aspect ratio of #{args[:ns_to_ew_ratio]}, Lowering it to #{length / width} ")
|
1538
|
+
args[:ns_to_ew_ratio] = length / width
|
1539
|
+
elsif args[:ns_to_ew_ratio] < 1.0 && args[:ns_to_ew_ratio] > length / width
|
1540
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "Can't meet target aspect ratio of #{args[:ns_to_ew_ratio]}, Increasing it to #{length / width} ")
|
1541
|
+
args[:ns_to_ew_ratio] = length / width
|
1542
|
+
else
|
1543
|
+
# check if each bar would be longer then 15 feet, then set as dual bar and override perimeter multiplier
|
1544
|
+
length_alt1 = ((args[:ns_to_ew_ratio] * footprint_si) / width + 2 * args[:ns_to_ew_ratio] * width - 2 * width) / (1 + args[:ns_to_ew_ratio])
|
1545
|
+
length_alt2 = length - length_alt1
|
1546
|
+
if [length_alt1, length_alt2].min >= min_allow_size
|
1547
|
+
dual_bar = true
|
1548
|
+
else
|
1549
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Second bar would be below minimum length, will model as single bar')
|
1550
|
+
# swap length and width if single bar and aspect ratio less than 1
|
1551
|
+
if args[:ns_to_ew_ratio] < 1.0
|
1552
|
+
width = length
|
1553
|
+
length = specified_bar_width_si
|
1554
|
+
end
|
1555
|
+
end
|
1556
|
+
end
|
1557
|
+
end
|
1558
|
+
elsif args[:perim_mult] > 1.0 && args[:bar_division_method] == 'Multiple Space Types - Individual Stories Sliced'
|
1559
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'You selected a perimeter multiplier greater than 1.0 for a supported bar division method. This will result in two detached rectangular buildings if secondary bar meets minimum size requirements.')
|
1560
|
+
dual_bar = true
|
1561
|
+
elsif args[:perim_mult] > 1.0
|
1562
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "You selected a perimeter multiplier greater than 1.0 but didn't select a bar division method that supports this. The value for this argument will be ignored by the measure")
|
1563
|
+
end
|
1564
|
+
|
1565
|
+
# calculations for dual bar, which later will be setup to run create_bar twice
|
1566
|
+
if dual_bar
|
1567
|
+
min_perim = 2 * width + 2 * length
|
1568
|
+
target_area = footprint_si
|
1569
|
+
target_perim = min_perim * args[:perim_mult]
|
1570
|
+
tol_testing = 0.00001
|
1571
|
+
dual_bar_calc_approach = nil # stretched, adiabatic_ends_bar_b, dual_bar
|
1572
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Minimum rectangle is #{OpenStudio.toNeatString(OpenStudio.convert(length, 'm', 'ft').get, 0, true)} ft x #{OpenStudio.toNeatString(OpenStudio.convert(width, 'm', 'ft').get, 0, true)} ft with an area of #{OpenStudio.toNeatString(OpenStudio.convert(length * width, 'm^2', 'ft^2').get, 0, true)} ft^2. Perimeter is #{OpenStudio.toNeatString(OpenStudio.convert(min_perim, 'm', 'ft').get, 0, true)} ft.")
|
1573
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Target dual bar perimeter is #{OpenStudio.toNeatString(OpenStudio.convert(target_perim, 'm', 'ft').get, 0, true)} ft.")
|
1574
|
+
|
1575
|
+
# determine which of the three paths to hit target perimeter multiplier are possible
|
1576
|
+
# A use dual bar non adiabatic
|
1577
|
+
# B use dual bar adiabatic
|
1578
|
+
# C use stretched bar (requires model to miss ns/ew ratio)
|
1579
|
+
|
1580
|
+
# custom quadratic equation to solve two bars with common width 2l^2 - p*l + 4a = 0
|
1581
|
+
if target_perim**2 - 32 * footprint_si > 0
|
1582
|
+
if specified_bar_width_si > 0
|
1583
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Ignoring perimeter multiplier argument and using use specified bar width.')
|
1584
|
+
dual_double_end_width = specified_bar_width_si
|
1585
|
+
dual_double_end_length = footprint_si / dual_double_end_width
|
1586
|
+
else
|
1587
|
+
dual_double_end_length = 0.25 * (target_perim + Math.sqrt(target_perim**2 - 32 * footprint_si))
|
1588
|
+
dual_double_end_width = footprint_si / dual_double_end_length
|
1589
|
+
end
|
1590
|
+
|
1591
|
+
# now that stretched bar is made, determine where to split it and rotate
|
1592
|
+
bar_a_length = (args[:ns_to_ew_ratio] * (dual_double_end_length + dual_double_end_width) - dual_double_end_width) / (1 + args[:ns_to_ew_ratio])
|
1593
|
+
bar_b_length = dual_double_end_length - bar_a_length
|
1594
|
+
area_a = bar_a_length * dual_double_end_width
|
1595
|
+
area_b = bar_b_length * dual_double_end_width
|
1596
|
+
else
|
1597
|
+
# this will throw it to adiabatic ends test
|
1598
|
+
bar_a_length = 0
|
1599
|
+
bar_b_length = 0
|
1600
|
+
end
|
1601
|
+
|
1602
|
+
if bar_a_length >= min_allow_size && bar_b_length >= min_allow_size
|
1603
|
+
dual_bar_calc_approach = 'dual_bar'
|
1604
|
+
else
|
1605
|
+
# adiabatic bar input calcs
|
1606
|
+
if target_perim**2 - 16 * footprint_si > 0
|
1607
|
+
adiabatic_dual_double_end_length = 0.25 * (target_perim + Math.sqrt(target_perim**2 - 16 * footprint_si))
|
1608
|
+
adiabatic_dual_double_end_width = footprint_si / adiabatic_dual_double_end_length
|
1609
|
+
# test for unexpected
|
1610
|
+
unexpected = false
|
1611
|
+
if (target_area - adiabatic_dual_double_end_length * adiabatic_dual_double_end_width).abs > tol_testing then unexpected = true end
|
1612
|
+
if specified_bar_width_si == 0
|
1613
|
+
if (target_perim - (adiabatic_dual_double_end_length * 2 + adiabatic_dual_double_end_width * 2)).abs > tol_testing then unexpected = true end
|
1614
|
+
end
|
1615
|
+
if unexpected
|
1616
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Unexpected values for dual rectangle adiabatic ends bar b.')
|
1617
|
+
end
|
1618
|
+
# now that stretched bar is made, determine where to split it and rotate
|
1619
|
+
adiabatic_bar_a_length = (args[:ns_to_ew_ratio] * (adiabatic_dual_double_end_length + adiabatic_dual_double_end_width)) / (1 + args[:ns_to_ew_ratio])
|
1620
|
+
adiabatic_bar_b_length = adiabatic_dual_double_end_length - adiabatic_bar_a_length
|
1621
|
+
adiabatic_area_a = adiabatic_bar_a_length * adiabatic_dual_double_end_width
|
1622
|
+
adiabatic_area_b = adiabatic_bar_b_length * adiabatic_dual_double_end_width
|
1623
|
+
else
|
1624
|
+
# this will throw it stretched single bar
|
1625
|
+
adiabatic_bar_a_length = 0
|
1626
|
+
adiabatic_bar_b_length = 0
|
1627
|
+
end
|
1628
|
+
if adiabatic_bar_a_length >= min_allow_size && adiabatic_bar_b_length >= min_allow_size
|
1629
|
+
dual_bar_calc_approach = 'adiabatic_ends_bar_b'
|
1630
|
+
else
|
1631
|
+
dual_bar_calc_approach = 'stretched'
|
1632
|
+
end
|
1633
|
+
end
|
1634
|
+
|
1635
|
+
# apply prescribed approach for stretched or dual bar
|
1636
|
+
if dual_bar_calc_approach == 'dual_bar'
|
1637
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Stretched #{OpenStudio.toNeatString(OpenStudio.convert(dual_double_end_length, 'm', 'ft').get, 0, true)} ft x #{OpenStudio.toNeatString(OpenStudio.convert(dual_double_end_width, 'm', 'ft').get, 0, true)} ft rectangle has an area of #{OpenStudio.toNeatString(OpenStudio.convert(dual_double_end_length * dual_double_end_width, 'm^2', 'ft^2').get, 0, true)} ft^2. When split in two the perimeter will be #{OpenStudio.toNeatString(OpenStudio.convert(dual_double_end_length * 2 + dual_double_end_width * 4, 'm', 'ft').get, 0, true)} ft")
|
1638
|
+
if (target_area - dual_double_end_length * dual_double_end_width).abs > tol_testing || (target_perim - (dual_double_end_length * 2 + dual_double_end_width * 4)).abs > tol_testing
|
1639
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Unexpected values for dual rectangle.')
|
1640
|
+
end
|
1641
|
+
|
1642
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "For stretched split bar, to match target ns/ew aspect ratio #{OpenStudio.toNeatString(OpenStudio.convert(bar_a_length, 'm', 'ft').get, 0, true)} ft of bar should be horizontal, with #{OpenStudio.toNeatString(OpenStudio.convert(bar_b_length, 'm', 'ft').get, 0, true)} ft turned 90 degrees. Combined area is #{OpenStudio.toNeatString(OpenStudio.convert(area_a + area_b, 'm^2', 'ft^2').get, 0, true)} ft^2. Combined perimeter is #{OpenStudio.toNeatString(OpenStudio.convert(bar_a_length * 2 + bar_b_length * 2 + dual_double_end_width * 4, 'm', 'ft').get, 0, true)} ft")
|
1643
|
+
if (target_area - (area_a + area_b)).abs > tol_testing || (target_perim - (bar_a_length * 2 + bar_b_length * 2 + dual_double_end_width * 4)).abs > tol_testing
|
1644
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Unexpected values for rotated dual rectangle')
|
1645
|
+
end
|
1646
|
+
elsif dual_bar_calc_approach == 'adiabatic_ends_bar_b'
|
1647
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Can't hit target perimeter with two rectangles, need to make two ends adiabatic")
|
1648
|
+
|
1649
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "For dual bar with adiabatic ends on bar b, to reach target aspect ratio #{OpenStudio.toNeatString(OpenStudio.convert(adiabatic_bar_a_length, 'm', 'ft').get, 0, true)} ft of bar should be north/south, with #{OpenStudio.toNeatString(OpenStudio.convert(adiabatic_bar_b_length, 'm', 'ft').get, 0, true)} ft turned 90 degrees. Combined area is #{OpenStudio.toNeatString(OpenStudio.convert(adiabatic_area_a + adiabatic_area_b, 'm^2', 'ft^2').get, 0, true)} ft^2}. Combined perimeter is #{OpenStudio.toNeatString(OpenStudio.convert(adiabatic_bar_a_length * 2 + adiabatic_bar_b_length * 2 + adiabatic_dual_double_end_width * 2, 'm', 'ft').get, 0, true)} ft")
|
1650
|
+
if (target_area - (adiabatic_area_a + adiabatic_area_b)).abs > tol_testing || (target_perim - (adiabatic_bar_a_length * 2 + adiabatic_bar_b_length * 2 + adiabatic_dual_double_end_width * 2)).abs > tol_testing
|
1651
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Unexpected values for rotated dual rectangle adiabatic ends bar b')
|
1652
|
+
end
|
1653
|
+
else
|
1654
|
+
# stretched bar
|
1655
|
+
dual_bar = false
|
1656
|
+
|
1657
|
+
stretched_length = 0.25 * (target_perim + Math.sqrt(target_perim**2 - 16 * footprint_si))
|
1658
|
+
stretched_width = footprint_si / stretched_length
|
1659
|
+
if (target_area - stretched_length * stretched_width).abs > tol_testing || (target_perim - (stretched_length + stretched_width) * 2) > tol_testing
|
1660
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Unexpected values for single stretched')
|
1661
|
+
end
|
1662
|
+
|
1663
|
+
width = stretched_width
|
1664
|
+
length = stretched_length
|
1665
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Creating a dual bar to match the target minimum perimeter multiplier at the given aspect ratio would result in a bar with edge shorter than #{OpenStudio.toNeatString(OpenStudio.convert(min_allow_size, 'm', 'ft').get, 0, true)} ft. Will create a single stretched bar instead that hits the target perimeter with a slightly different ns/ew aspect ratio.")
|
1666
|
+
end
|
1667
|
+
end
|
1668
|
+
|
1669
|
+
bars = {}
|
1670
|
+
bars['primary'] = {}
|
1671
|
+
if dual_bar
|
1672
|
+
if mirror_ns_ew && dual_bar_calc_approach == 'dual_bar'
|
1673
|
+
bars['primary'][:length] = dual_double_end_width
|
1674
|
+
bars['primary'][:width] = bar_a_length
|
1675
|
+
elsif dual_bar_calc_approach == 'dual_bar'
|
1676
|
+
bars['primary'][:length] = bar_a_length
|
1677
|
+
bars['primary'][:width] = dual_double_end_width
|
1678
|
+
elsif mirror_ns_ew
|
1679
|
+
bars['primary'][:length] = adiabatic_dual_double_end_width
|
1680
|
+
bars['primary'][:width] = adiabatic_bar_a_length
|
1681
|
+
else
|
1682
|
+
bars['primary'][:length] = adiabatic_bar_a_length
|
1683
|
+
bars['primary'][:width] = adiabatic_dual_double_end_width
|
1684
|
+
end
|
1685
|
+
else
|
1686
|
+
if mirror_ns_ew
|
1687
|
+
bars['primary'][:length] = width
|
1688
|
+
bars['primary'][:width] = length
|
1689
|
+
else
|
1690
|
+
bars['primary'][:length] = length
|
1691
|
+
bars['primary'][:width] = width
|
1692
|
+
end
|
1693
|
+
end
|
1694
|
+
bars['primary'][:floor_height] = floor_height # can make use of this when breaking out multi-height spaces
|
1695
|
+
bars['primary'][:num_stories] = num_stories
|
1696
|
+
bars['primary'][:center_of_footprint] = OpenStudio::Point3d.new(0.0, 0.0, 0.0)
|
1697
|
+
space_types_hash_secondary = {}
|
1698
|
+
if dual_bar
|
1699
|
+
# loop through each story and move portion for other bar to its own hash
|
1700
|
+
primary_footprint = bars['primary'][:length] * bars['primary'][:width]
|
1701
|
+
secondary_footprint = target_area - primary_footprint
|
1702
|
+
footprint_counter = primary_footprint
|
1703
|
+
secondary_footprint_counter = secondary_footprint
|
1704
|
+
story_counter = 0
|
1705
|
+
pri_sec_tol = 0.0001 # m^2
|
1706
|
+
pri_sec_min_area = 0.0001 # m^2
|
1707
|
+
space_types_hash.each do |k, v|
|
1708
|
+
space_type_left = v[:floor_area]
|
1709
|
+
|
1710
|
+
# do not go to next space type until this one is evaulate, which may span stories
|
1711
|
+
until space_type_left == 0.0 || story_counter >= num_stories
|
1712
|
+
|
1713
|
+
# use secondary footprint if any left
|
1714
|
+
if secondary_footprint_counter > 0.0
|
1715
|
+
hash_area = [space_type_left, secondary_footprint_counter].min
|
1716
|
+
|
1717
|
+
# confirm that the part of space type use or what is left is greater than min allowed value
|
1718
|
+
projected_space_type_left = space_type_left - hash_area
|
1719
|
+
test_a = hash_area >= pri_sec_min_area
|
1720
|
+
test_b = projected_space_type_left >= pri_sec_min_area || projected_space_type_left == 0.0 ? true : false
|
1721
|
+
test_c = k == space_types_hash.keys.last # if last space type accept sliver, no other space to infil
|
1722
|
+
if (test_a && test_b) || test_c
|
1723
|
+
if space_types_hash_secondary.key?(k)
|
1724
|
+
# add to what was added for previous story
|
1725
|
+
space_types_hash_secondary[k][:floor_area] += hash_area
|
1726
|
+
else
|
1727
|
+
# add new space type to hash
|
1728
|
+
if v.key?(:children)
|
1729
|
+
space_types_hash_secondary[k] = { floor_area: hash_area, space_type: v[:space_type], children: v[:children] }
|
1730
|
+
else
|
1731
|
+
space_types_hash_secondary[k] = { floor_area: hash_area, space_type: v[:space_type] }
|
1732
|
+
end
|
1733
|
+
end
|
1734
|
+
space_types_hash[k][:floor_area] -= hash_area
|
1735
|
+
secondary_footprint_counter -= hash_area
|
1736
|
+
space_type_left -= hash_area
|
1737
|
+
else
|
1738
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Shifting space types between bars to avoid sliver of #{k.name}.")
|
1739
|
+
end
|
1740
|
+
end
|
1741
|
+
|
1742
|
+
# remove space if entirely used up by secondary bar
|
1743
|
+
if space_types_hash[k][:floor_area] <= pri_sec_tol
|
1744
|
+
space_types_hash.delete(k)
|
1745
|
+
space_type_left = 0.0
|
1746
|
+
else
|
1747
|
+
# then look at primary bar
|
1748
|
+
hash_area_pri = [space_type_left, footprint_counter].min
|
1749
|
+
footprint_counter -= hash_area_pri
|
1750
|
+
space_type_left -= hash_area_pri
|
1751
|
+
end
|
1752
|
+
|
1753
|
+
# reset counter when full
|
1754
|
+
if footprint_counter <= pri_sec_tol && secondary_footprint_counter <= pri_sec_tol
|
1755
|
+
# check if this is partial top floor
|
1756
|
+
story_counter += 1
|
1757
|
+
if num_stories < story_counter + 1
|
1758
|
+
footprint_counter = primary_footprint * (num_stories - story_counter)
|
1759
|
+
secondary_footprint_counter = secondary_footprint * (num_stories - story_counter)
|
1760
|
+
else
|
1761
|
+
footprint_counter = primary_footprint
|
1762
|
+
secondary_footprint_counter = secondary_footprint
|
1763
|
+
end
|
1764
|
+
end
|
1765
|
+
end
|
1766
|
+
end
|
1767
|
+
end
|
1768
|
+
|
1769
|
+
# setup bar_hash and run create_bar
|
1770
|
+
bars['primary'][:space_types_hash] = space_types_hash
|
1771
|
+
bars['primary'][:args] = args
|
1772
|
+
v = bars['primary']
|
1773
|
+
OpenstudioStandards::Geometry.bar_hash_setup_run(model, v[:args], v[:length], v[:width], v[:floor_height], v[:center_of_footprint], v[:space_types_hash], v[:num_stories])
|
1774
|
+
|
1775
|
+
# store offset value for multiple bars
|
1776
|
+
if args.key?(:bar_sep_dist_mult) && args[:bar_sep_dist_mult] > 0.0
|
1777
|
+
offset_val = num_stories.ceil * floor_height * args[:bar_sep_dist_mult]
|
1778
|
+
elsif args.key?(:bar_sep_dist_mult)
|
1779
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Positive value is required for bar_sep_dist_mult, ignoring input and using value of 0.1')
|
1780
|
+
offset_val = num_stories.ceil * floor_height * 0.1
|
1781
|
+
else
|
1782
|
+
offset_val = num_stories.ceil * floor_height * 10.0
|
1783
|
+
end
|
1784
|
+
|
1785
|
+
if dual_bar
|
1786
|
+
args2 = args.clone
|
1787
|
+
bars['secondary'] = {}
|
1788
|
+
if mirror_ns_ew && dual_bar_calc_approach == 'dual_bar'
|
1789
|
+
bars['secondary'][:length] = bar_b_length
|
1790
|
+
bars['secondary'][:width] = dual_double_end_width
|
1791
|
+
elsif dual_bar_calc_approach == 'dual_bar'
|
1792
|
+
bars['secondary'][:length] = dual_double_end_width
|
1793
|
+
bars['secondary'][:width] = bar_b_length
|
1794
|
+
elsif mirror_ns_ew
|
1795
|
+
bars['secondary'][:length] = adiabatic_bar_b_length
|
1796
|
+
bars['secondary'][:width] = adiabatic_dual_double_end_width
|
1797
|
+
args2[:party_wall_stories_east] = num_stories.ceil
|
1798
|
+
args2[:party_wall_stories_west] = num_stories.ceil
|
1799
|
+
else
|
1800
|
+
bars['secondary'][:length] = adiabatic_dual_double_end_width
|
1801
|
+
bars['secondary'][:width] = adiabatic_bar_b_length
|
1802
|
+
args2[:party_wall_stories_south] = num_stories.ceil
|
1803
|
+
args2[:party_wall_stories_north] = num_stories.ceil
|
1804
|
+
end
|
1805
|
+
bars['secondary'][:floor_height] = floor_height # can make use of this when breaking out multi-height spaces
|
1806
|
+
bars['secondary'][:num_stories] = num_stories
|
1807
|
+
bars['secondary'][:space_types_hash] = space_types_hash_secondary
|
1808
|
+
if dual_bar_calc_approach == 'adiabatic_ends_bar_b'
|
1809
|
+
# warn that combination of dual bar with low perimeter multiplier and use of party wall may result in discrepency between target and actual adiabatic walls
|
1810
|
+
if args[:party_wall_fraction] > 0 || args[:party_wall_stories_north] > 0 || args[:party_wall_stories_south] > 0 || args[:party_wall_stories_east] > 0 || args[:party_wall_stories_west] > 0
|
1811
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'The combination of low perimeter multiplier and use of non zero party wall inputs may result in discrepency between target and actual adiabatic walls. This is due to the need to create adiabatic walls on secondary bar to maintian target building perimeter.')
|
1812
|
+
else
|
1813
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Adiabatic ends added to secondary bar because target perimeter multiplier could not be met with two full rectangular footprints.')
|
1814
|
+
end
|
1815
|
+
bars['secondary'][:center_of_footprint] = OpenStudio::Point3d.new(adiabatic_bar_a_length * 0.5 + adiabatic_dual_double_end_width * 0.5 + offset_val, adiabatic_bar_b_length * 0.5 + adiabatic_dual_double_end_width * 0.5 + offset_val, 0.0)
|
1816
|
+
else
|
1817
|
+
bars['secondary'][:center_of_footprint] = OpenStudio::Point3d.new(bar_a_length * 0.5 + dual_double_end_width * 0.5 + offset_val, bar_b_length * 0.5 + dual_double_end_width * 0.5 + offset_val, 0.0)
|
1818
|
+
end
|
1819
|
+
bars['secondary'][:args] = args2
|
1820
|
+
|
1821
|
+
# setup bar_hash and run create_bar
|
1822
|
+
v = bars['secondary']
|
1823
|
+
OpenstudioStandards::Geometry.bar_hash_setup_run(model, v[:args], v[:length], v[:width], v[:floor_height], v[:center_of_footprint], v[:space_types_hash], v[:num_stories])
|
1824
|
+
end
|
1825
|
+
|
1826
|
+
# future development (up against primary bar run intersection and surface matching after add all bars, avoid interior windows)
|
1827
|
+
# I could loop through each space type and give them unique height but for now will just take largest height and make bar of that height, which is fine for prototypes
|
1828
|
+
if !multi_height_space_types_hash.empty?
|
1829
|
+
args3 = args.clone
|
1830
|
+
bars['custom_height'] = {}
|
1831
|
+
if mirror_ns_ew
|
1832
|
+
bars['custom_height'][:length] = width_cust_height
|
1833
|
+
bars['custom_height'][:width] = length_cust_height
|
1834
|
+
else
|
1835
|
+
bars['custom_height'][:length] = length_cust_height
|
1836
|
+
bars['custom_height'][:width] = width_cust_height
|
1837
|
+
end
|
1838
|
+
if args[:party_wall_stories_east] + args[:party_wall_stories_west] + args[:party_wall_stories_south] + args[:party_wall_stories_north] > 0.0
|
1839
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'Ignorning party wall inputs for custom height bar')
|
1840
|
+
end
|
1841
|
+
|
1842
|
+
# disable party walls
|
1843
|
+
args3['party_wall_stories_east'] = 0
|
1844
|
+
args3['party_wall_stories_west'] = 0
|
1845
|
+
args3['party_wall_stories_south'] = 0
|
1846
|
+
args3['party_wall_stories_north'] = 0
|
1847
|
+
|
1848
|
+
# setup stories
|
1849
|
+
args3['num_stories_below_grade'] = 0
|
1850
|
+
args3['num_stories_above_grade'] = 1
|
1851
|
+
|
1852
|
+
# can make use of this when breaking out multi-height spaces
|
1853
|
+
bars['custom_height'][:floor_height] = floor_height
|
1854
|
+
bars['custom_height'][:num_stories] = num_stories
|
1855
|
+
bars['custom_height'][:center_of_footprint] = OpenStudio::Point3d.new(bars['primary'][:length] * -0.5 - length_cust_height * 0.5 - offset_val, 0.0, 0.0)
|
1856
|
+
bars['custom_height'][:floor_height] = OpenStudio.convert(custom_story_heights.max, 'ft', 'm').get
|
1857
|
+
bars['custom_height'][:num_stories] = 1
|
1858
|
+
bars['custom_height'][:space_types_hash] = multi_height_space_types_hash
|
1859
|
+
bars['custom_height'][:args] = args3
|
1860
|
+
|
1861
|
+
v = bars['custom_height']
|
1862
|
+
OpenstudioStandards::Geometry.bar_hash_setup_run(model, v[:args], v[:length], v[:width], v[:floor_height], v[:center_of_footprint], v[:space_types_hash], v[:num_stories])
|
1863
|
+
end
|
1864
|
+
|
1865
|
+
# diagnostic log
|
1866
|
+
sum_actual = 0.0
|
1867
|
+
sum_target = 0.0
|
1868
|
+
throw_error = false
|
1869
|
+
|
1870
|
+
# check expected floor areas against actual
|
1871
|
+
model.getSpaceTypes.sort.each do |space_type|
|
1872
|
+
next if !target_areas.key? space_type # space type in model not part of building type(s), maybe issue warning
|
1873
|
+
|
1874
|
+
# convert to IP
|
1875
|
+
actual_ip = OpenStudio.convert(space_type.floorArea, 'm^2', 'ft^2').get
|
1876
|
+
target_ip = OpenStudio.convert(target_areas[space_type], 'm^2', 'ft^2').get
|
1877
|
+
sum_actual += actual_ip
|
1878
|
+
sum_target += target_ip
|
1879
|
+
|
1880
|
+
if (space_type.floorArea - target_areas[space_type]).abs >= 1.0
|
1881
|
+
|
1882
|
+
if !args[:bar_division_method].include? 'Single Space Type'
|
1883
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "#{space_type.name} doesn't have the expected floor area (actual #{OpenStudio.toNeatString(actual_ip, 0, true)} ft^2, target #{OpenStudio.toNeatString(target_ip, 0, true)} ft^2)")
|
1884
|
+
throw_error = true
|
1885
|
+
else
|
1886
|
+
# will see this if use Single Space type division method on multi-use building or single building type without whole building space type
|
1887
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space_type.name} doesn't have the expected floor area (actual #{OpenStudio.toNeatString(actual_ip, 0, true)} ft^2, target #{OpenStudio.toNeatString(target_ip, 0, true)} ft^2)")
|
1888
|
+
end
|
1889
|
+
|
1890
|
+
end
|
1891
|
+
end
|
1892
|
+
|
1893
|
+
# report summary then throw error
|
1894
|
+
if throw_error
|
1895
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "Sum of actual floor area is #{sum_actual} ft^2, sum of target floor area is #{sum_target}.")
|
1896
|
+
return false
|
1897
|
+
end
|
1898
|
+
|
1899
|
+
# check party wall fraction by looping through surfaces
|
1900
|
+
if args[:party_wall_fraction] > 0
|
1901
|
+
actual_ext_wall_area = model.getBuilding.exteriorWallArea
|
1902
|
+
actual_party_wall_area = 0.0
|
1903
|
+
model.getSurfaces.sort.each do |surface|
|
1904
|
+
next if surface.outsideBoundaryCondition != 'Adiabatic'
|
1905
|
+
next if surface.surfaceType != 'Wall'
|
1906
|
+
|
1907
|
+
actual_party_wall_area += surface.grossArea * surface.space.get.multiplier
|
1908
|
+
end
|
1909
|
+
actual_party_wall_fraction = actual_party_wall_area / (actual_party_wall_area + actual_ext_wall_area)
|
1910
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Target party wall fraction is #{args[:party_wall_fraction]}. Realized fraction is #{actual_party_wall_fraction.round(2)}")
|
1911
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "party_wall_fraction_actual: #{actual_party_wall_fraction}")
|
1912
|
+
end
|
1913
|
+
|
1914
|
+
# check ns/ew aspect ratio (harder to check when party walls are added)
|
1915
|
+
wall_and_window_by_orientation = OpenstudioStandards::Geometry.model_get_exterior_window_and_wall_area_by_orientation(model)
|
1916
|
+
wall_ns = (wall_and_window_by_orientation['north_wall'] + wall_and_window_by_orientation['south_wall'])
|
1917
|
+
wall_ew = wall_and_window_by_orientation['east_wall'] + wall_and_window_by_orientation['west_wall']
|
1918
|
+
wall_ns_ip = OpenStudio.convert(wall_ns, 'm^2', 'ft^2').get
|
1919
|
+
wall_ew_ip = OpenStudio.convert(wall_ew, 'm^2', 'ft^2').get
|
1920
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "wall_area_ip: #{wall_ns_ip + wall_ew_ip} ft^2")
|
1921
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "ns_wall_area_ip: #{wall_ns_ip} ft^2")
|
1922
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "ew_wall_area_ip: #{wall_ew_ip} ft^2")
|
1923
|
+
# for now using perimeter of ground floor and average story area (building area / num_stories)
|
1924
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "floor_area_to_perim_ratio: #{model.getBuilding.floorArea / (OpenstudioStandards::Geometry.model_get_perimeter_length(model) * num_stories)}")
|
1925
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "bar_width: #{OpenStudio.convert(bars['primary'][:width], 'm', 'ft').get} ft")
|
1926
|
+
|
1927
|
+
if args[:party_wall_fraction] > 0 || args[:party_wall_stories_north] > 0 || args[:party_wall_stories_south] > 0 || args[:party_wall_stories_east] > 0 || args[:party_wall_stories_west] > 0
|
1928
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Target facade area by orientation not validated when party walls are applied')
|
1929
|
+
elsif args[:num_stories_above_grade] != args[:num_stories_above_grade].ceil
|
1930
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Target facade area by orientation not validated when partial top story is used')
|
1931
|
+
elsif dual_bar_calc_approach == 'stretched'
|
1932
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Target facade area by orientation not validated when single stretched bar has to be used to meet target minimum perimeter multiplier')
|
1933
|
+
elsif defaulted_args.include?(:floor_height) && args[:custom_height_bar] && !multi_height_space_types_hash.empty?
|
1934
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Target facade area by orientation not validated when a dedicated bar is added for space types with custom heights')
|
1935
|
+
elsif args[:bar_width] > 0
|
1936
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', 'Target facade area by orientation not validated when a dedicated custom bar width is defined')
|
1937
|
+
else
|
1938
|
+
|
1939
|
+
# adjust length versus width based on building rotation
|
1940
|
+
if mirror_ns_ew
|
1941
|
+
wall_target_ns_ip = 2 * OpenStudio.convert(width, 'm', 'ft').get * args[:perim_mult] * args[:num_stories_above_grade] * args[:floor_height]
|
1942
|
+
wall_target_ew_ip = 2 * OpenStudio.convert(length, 'm', 'ft').get * args[:perim_mult] * args[:num_stories_above_grade] * args[:floor_height]
|
1943
|
+
else
|
1944
|
+
wall_target_ns_ip = 2 * OpenStudio.convert(length, 'm', 'ft').get * args[:perim_mult] * args[:num_stories_above_grade] * args[:floor_height]
|
1945
|
+
wall_target_ew_ip = 2 * OpenStudio.convert(width, 'm', 'ft').get * args[:perim_mult] * args[:num_stories_above_grade] * args[:floor_height]
|
1946
|
+
end
|
1947
|
+
flag_error = false
|
1948
|
+
if (wall_target_ns_ip - wall_ns_ip).abs > 0.1
|
1949
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "North/South walls don't have the expected area (actual #{OpenStudio.toNeatString(wall_ns_ip, 4, true)} ft^2, target #{OpenStudio.toNeatString(wall_target_ns_ip, 4, true)} ft^2)")
|
1950
|
+
flag_error = true
|
1951
|
+
end
|
1952
|
+
if (wall_target_ew_ip - wall_ew_ip).abs > 0.1
|
1953
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', "East/West walls don't have the expected area (actual #{OpenStudio.toNeatString(wall_ew_ip, 4, true)} ft^2, target #{OpenStudio.toNeatString(wall_target_ew_ip, 4, true)} ft^2)")
|
1954
|
+
flag_error = true
|
1955
|
+
end
|
1956
|
+
if flag_error
|
1957
|
+
return false
|
1958
|
+
end
|
1959
|
+
end
|
1960
|
+
|
1961
|
+
# test for excessive exterior roof area (indication of problem with intersection and or surface matching)
|
1962
|
+
ext_roof_area = model.getBuilding.exteriorSurfaceArea - model.getBuilding.exteriorWallArea
|
1963
|
+
expected_roof_area = args[:total_bldg_floor_area] / (args[:num_stories_above_grade] + args[:num_stories_below_grade]).to_f
|
1964
|
+
if ext_roof_area > expected_roof_area && single_floor_area_si == 0.0 # only test if using whole-building area input
|
1965
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', 'Roof area larger than expected, may indicate problem with inter-floor surface intersection or matching.')
|
1966
|
+
return false
|
1967
|
+
end
|
1968
|
+
|
1969
|
+
# set building rotation
|
1970
|
+
initial_rotation = model.getBuilding.northAxis
|
1971
|
+
if args[:building_rotation] != initial_rotation
|
1972
|
+
model.getBuilding.setNorthAxis(args[:building_rotation])
|
1973
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Set Building Rotation to #{model.getBuilding.northAxis}. Rotation altered after geometry generation is completed, as a result party wall orientation and aspect ratio may not reflect input values.")
|
1974
|
+
end
|
1975
|
+
|
1976
|
+
# report final condition of model
|
1977
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "The building finished with #{model.getSpaces.size} spaces.")
|
1978
|
+
|
1979
|
+
return true
|
1980
|
+
end
|
1981
|
+
|
1982
|
+
# create bar from building type ratios
|
1983
|
+
# arguments are passed through to lower level methods.
|
1984
|
+
# See create_bar_from_args_and_building_type_hash for additional argument options.
|
1985
|
+
#
|
1986
|
+
# @param model [OpenStudio::Model::Model] OpenStudio model object
|
1987
|
+
# @param args [Hash] user arguments
|
1988
|
+
# @option args [String] :bldg_type_a ('SmallOffice') primary building type
|
1989
|
+
# @option args [String] :bldg_type_b (nil) building type b
|
1990
|
+
# @option args [String] :bldg_type_c (nil) building type c
|
1991
|
+
# @option args [String] :bldg_type_d (nil) building type d
|
1992
|
+
# @option args [String] :bldg_subtype_a ('NA') primary building subtype
|
1993
|
+
# @option args [String] :bldg_subtype_b ('NA') building type b subtype
|
1994
|
+
# @option args [String] :bldg_subtype_c ('NA') building type c subtype
|
1995
|
+
# @option args [String] :bldg_subtype_d ('NA') building type d subtype
|
1996
|
+
# @option args [String] :bldg_type_a_fract_bldg_area (1.0) building type a area fraction of total floor area
|
1997
|
+
# @option args [String] :bldg_type_b_fract_bldg_area (0.0) building type b area fraction of total floor area
|
1998
|
+
# @option args [String] :bldg_type_c_fract_bldg_area (0.0) building type c area fraction of total floor area
|
1999
|
+
# @option args [String] :bldg_type_d_fract_bldg_area (0.0) building type d area fraction of total floor area
|
2000
|
+
# @option args [String] :template ('90.1-2013') target standard
|
2001
|
+
# @return [Boolean] returns true if successful, false if not
|
2002
|
+
def self.create_bar_from_building_type_ratios(model, args)
|
2003
|
+
bldg_type_a = args.fetch(:bldg_type_a, 'SmallOffice')
|
2004
|
+
bldg_type_b = args.fetch(:bldg_type_b, nil)
|
2005
|
+
bldg_type_c = args.fetch(:bldg_type_c, nil)
|
2006
|
+
bldg_type_d = args.fetch(:bldg_type_d, nil)
|
2007
|
+
bldg_subtype_a = args.fetch(:bldg_subtype_a, 'NA')
|
2008
|
+
bldg_subtype_b = args.fetch(:bldg_subtype_b, 'NA')
|
2009
|
+
bldg_subtype_c = args.fetch(:bldg_subtype_c, 'NA')
|
2010
|
+
bldg_subtype_d = args.fetch(:bldg_subtype_d, 'NA')
|
2011
|
+
bldg_type_a_fract_bldg_area = args.fetch(:bldg_type_a_fract_bldg_area, 1.0)
|
2012
|
+
bldg_type_b_fract_bldg_area = args.fetch(:bldg_type_b_fract_bldg_area, 0.0)
|
2013
|
+
bldg_type_c_fract_bldg_area = args.fetch(:bldg_type_c_fract_bldg_area, 0.0)
|
2014
|
+
bldg_type_d_fract_bldg_area = args.fetch(:bldg_type_d_fract_bldg_area, 0.0)
|
2015
|
+
template = args.fetch(:template, '90.1-2013')
|
2016
|
+
|
2017
|
+
# check that sum of fractions for b,c, and d is less than 1.0 (so something is left for primary building type)
|
2018
|
+
bldg_type_a_fract_bldg_area = 1.0 - bldg_type_b_fract_bldg_area - bldg_type_c_fract_bldg_area - bldg_type_d_fract_bldg_area
|
2019
|
+
if bldg_type_a_fract_bldg_area <= 0.0
|
2020
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.Geometry.Create', 'Primary Building Type fraction of floor area must be greater than 0. Please lower one or more of the fractions for Building Type B-D.')
|
2021
|
+
return false
|
2022
|
+
end
|
2023
|
+
|
2024
|
+
# report initial condition of model
|
2025
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "The building started with #{model.getSpaces.size} spaces.")
|
2026
|
+
|
2027
|
+
# determine of ns_ew needs to be mirrored
|
2028
|
+
mirror_ns_ew = false
|
2029
|
+
rotation = model.getBuilding.northAxis
|
2030
|
+
if rotation > 45.0 && rotation < 135.0
|
2031
|
+
mirror_ns_ew = true
|
2032
|
+
elsif rotation > 45.0 && rotation < 135.0
|
2033
|
+
mirror_ns_ew = true
|
2034
|
+
end
|
2035
|
+
|
2036
|
+
# remove non-resource objects not removed by removing the building
|
2037
|
+
# remove_non_resource_objects(model)
|
2038
|
+
|
2039
|
+
# rename building to infer template in downstream measure
|
2040
|
+
name_array = [template, bldg_type_a]
|
2041
|
+
if bldg_type_b_fract_bldg_area > 0 then name_array << bldg_type_b end
|
2042
|
+
if bldg_type_c_fract_bldg_area > 0 then name_array << bldg_type_c end
|
2043
|
+
if bldg_type_d_fract_bldg_area > 0 then name_array << bldg_type_d end
|
2044
|
+
model.getBuilding.setName(name_array.join('|').to_s)
|
2045
|
+
|
2046
|
+
# hash to whole building type data
|
2047
|
+
building_type_hash = {}
|
2048
|
+
|
2049
|
+
# gather data for bldg_type_a
|
2050
|
+
building_type_hash[bldg_type_a] = {}
|
2051
|
+
building_type_hash[bldg_type_a][:frac_bldg_area] = bldg_type_a_fract_bldg_area
|
2052
|
+
building_type_hash[bldg_type_a][:space_types] = OpenstudioStandards::CreateTypical.get_space_types_from_building_type(bldg_type_a, building_subtype: bldg_subtype_a, template: template, whole_building: true)
|
2053
|
+
|
2054
|
+
# gather data for bldg_type_b
|
2055
|
+
if bldg_type_b_fract_bldg_area > 0
|
2056
|
+
building_type_hash[bldg_type_b] = {}
|
2057
|
+
building_type_hash[bldg_type_b][:frac_bldg_area] = bldg_type_b_fract_bldg_area
|
2058
|
+
building_type_hash[bldg_type_b][:space_types] = OpenstudioStandards::CreateTypical.get_space_types_from_building_type(bldg_type_b, building_subtype: bldg_subtype_b, template: template, whole_building: true)
|
2059
|
+
end
|
2060
|
+
|
2061
|
+
# gather data for bldg_type_c
|
2062
|
+
if bldg_type_c_fract_bldg_area > 0
|
2063
|
+
building_type_hash[bldg_type_c] = {}
|
2064
|
+
building_type_hash[bldg_type_c][:frac_bldg_area] = bldg_type_c_fract_bldg_area
|
2065
|
+
building_type_hash[bldg_type_c][:space_types] = OpenstudioStandards::CreateTypical.get_space_types_from_building_type(bldg_type_c, building_subtype: bldg_subtype_c, template: template, whole_building: true)
|
2066
|
+
end
|
2067
|
+
|
2068
|
+
# gather data for bldg_type_d
|
2069
|
+
if bldg_type_d_fract_bldg_area > 0
|
2070
|
+
building_type_hash[bldg_type_d] = {}
|
2071
|
+
building_type_hash[bldg_type_d][:frac_bldg_area] = bldg_type_d_fract_bldg_area
|
2072
|
+
building_type_hash[bldg_type_d][:space_types] = OpenstudioStandards::CreateTypical.get_space_types_from_building_type(bldg_type_d, building_subtype: bldg_subtype_d, template: template, whole_building: true)
|
2073
|
+
end
|
2074
|
+
|
2075
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Creating bar based on space ratios from #{bldg_type_a} for building form defaults.")
|
2076
|
+
|
2077
|
+
# call create_bar_from_args_and_building_type_hash to generate bar
|
2078
|
+
args[:primary_building_type] = bldg_type_a
|
2079
|
+
OpenstudioStandards::Geometry.create_bar_from_args_and_building_type_hash(model, args, building_type_hash)
|
2080
|
+
|
2081
|
+
return true
|
2082
|
+
end
|
2083
|
+
|
2084
|
+
# create bar from space type ratios
|
2085
|
+
# arguments are passed through to lower level methods.
|
2086
|
+
# See create_bar_from_args_and_building_type_hash for additional argument options.
|
2087
|
+
#
|
2088
|
+
# @param args [Hash] user arguments
|
2089
|
+
# @option args [String] :space_type_hash_string Space types ratio string in the form 'BuildingType | SpaceType => 0.75, BuildingType | SpaceType => 0.25'. Fractions should add up to 1. All space types should come from the selected OpenStudio Standards template.
|
2090
|
+
# @option args [String] :template ('90.1-2013') target standard
|
2091
|
+
# @return [Boolean] returns true if successful, false if not
|
2092
|
+
def self.create_bar_from_space_type_ratios(model, args)
|
2093
|
+
if args[:space_type_hash_string].empty?
|
2094
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', 'args hash passed to create_bar_from_space_type_ratios must include a non-empty :space_type_hash_string')
|
2095
|
+
return false
|
2096
|
+
end
|
2097
|
+
template = args.fetch(:template, '90.1-2013')
|
2098
|
+
|
2099
|
+
# process arg into hash
|
2100
|
+
space_type_hash_name = {}
|
2101
|
+
args[:space_type_hash_string][0..-1].split(/, /) do |entry|
|
2102
|
+
entry_map = entry.split(/=>/)
|
2103
|
+
value_str = entry_map[1]
|
2104
|
+
space_type_hash_name[entry_map[0].strip[0..-1].to_s] = value_str.nil? ? '' : value_str.strip[0..-1].to_f
|
2105
|
+
end
|
2106
|
+
|
2107
|
+
# create building type hash from space type ratios
|
2108
|
+
building_type_hash = {}
|
2109
|
+
building_type_fraction_of_building = 0.0
|
2110
|
+
space_type_hash_name.each do |building_space_type, ratio|
|
2111
|
+
building_type = building_space_type.split('|')[0].strip
|
2112
|
+
space_type = building_space_type.split('|')[1].strip
|
2113
|
+
|
2114
|
+
# harvest height and circ info from get_space_types_from_building_type(building_type, template, whole_building = true)
|
2115
|
+
building_type_lookup_info = OpenstudioStandards::CreateTypical.get_space_types_from_building_type(building_type, template: template)
|
2116
|
+
if building_type_lookup_info.empty?
|
2117
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{building_type} looks like an invalid building type for #{template}")
|
2118
|
+
end
|
2119
|
+
space_type_info_hash = {}
|
2120
|
+
if building_type_lookup_info.key?(space_type)
|
2121
|
+
if building_type_lookup_info[space_type].key?(:story_height)
|
2122
|
+
space_type_info_hash[:story_height] = building_type_lookup_info[space_type][:story_height]
|
2123
|
+
end
|
2124
|
+
if building_type_lookup_info[space_type].key?(:default)
|
2125
|
+
space_type_info_hash[:default] = building_type_lookup_info[space_type][:default]
|
2126
|
+
end
|
2127
|
+
if building_type_lookup_info[space_type].key?(:circ)
|
2128
|
+
space_type_info_hash[:circ] = building_type_lookup_info[space_type][:circ]
|
2129
|
+
end
|
2130
|
+
else
|
2131
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "#{space_type} looks like an invalid space type for #{building_type}")
|
2132
|
+
end
|
2133
|
+
|
2134
|
+
# extend harvested data with custom ratios from space type ratio string argument.
|
2135
|
+
if building_type_hash.key?(building_type)
|
2136
|
+
building_type_hash[building_type][:frac_bldg_area] += ratio
|
2137
|
+
space_type_info_hash[:ratio] = ratio
|
2138
|
+
building_type_hash[building_type][:space_types][space_type] = space_type_info_hash
|
2139
|
+
else
|
2140
|
+
building_type_hash[building_type] = {}
|
2141
|
+
building_type_hash[building_type][:frac_bldg_area] = ratio
|
2142
|
+
space_type_info_hash[:ratio] = ratio
|
2143
|
+
space_types = {}
|
2144
|
+
space_types[space_type] = space_type_info_hash
|
2145
|
+
building_type_hash[building_type][:space_types] = space_types
|
2146
|
+
end
|
2147
|
+
building_type_fraction_of_building += ratio
|
2148
|
+
end
|
2149
|
+
|
2150
|
+
# @todo confirm if this will get normalized up/down later of if I should fix or stop here instead of just a warning
|
2151
|
+
if building_type_fraction_of_building > 1.0
|
2152
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "Sum of Space Type Ratio of #{building_type_fraction_of_building} is greater than the expected value of 1.0")
|
2153
|
+
elsif building_type_fraction_of_building < 1.0
|
2154
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.Geometry.Create', "Sum of Space Type Ratio of #{building_type_fraction_of_building} is less than the expected value of 1.0")
|
2155
|
+
end
|
2156
|
+
|
2157
|
+
# identify primary building type for building form defaults
|
2158
|
+
# update to choose building with highest ratio
|
2159
|
+
primary_building_type = building_type_hash.keys.first
|
2160
|
+
|
2161
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.Geometry.Create', "Creating bar based space type ratios provided. Using building type #{primary_building_type} from the first ratio as the primary building type. This determines the building form defaults.")
|
2162
|
+
|
2163
|
+
# call create_bar_from_args_and_building_type_hash to generate bar
|
2164
|
+
args[:primary_building_type] = primary_building_type
|
2165
|
+
OpenstudioStandards::Geometry.create_bar_from_args_and_building_type_hash(model, args, building_type_hash)
|
2166
|
+
|
2167
|
+
return true
|
2168
|
+
end
|
2169
|
+
end
|
2170
|
+
end
|