Source code for cea.demand.ventilation_air_flows_detailed

# -*- coding: utf-8 -*-
"""
Ventilation according to [DIN-16798-7]_ and [ISO-9972]_

.. [DIN-16798-7]  Energieeffizienz von Gebäuden - Teil 7: Modul M5-1, M 5-5, M 5-6, M 5-8 –
    Berechnungsmethoden zur Bestimmung der Luftvolumenströme in Gebäuden inklusive Infiltration;
    Deutsche Fassung prEN 16798-7:2014


.. [ISO-9972] Wärmetechnisches Verhalten von Gebäuden –
    Bestimmung der Luftdurchlässigkeit von Gebäuden –
    Differenzdruckverfahren (ISO 9972:2015);
    Deutsche Fassung EN ISO 9972:2015

Convention: all temperature inputs in (°C)
"""

from __future__ import division

import numpy as np
import pandas as pd
from scipy.optimize import minimize

from cea.geometry.geometry_reader import get_building_geometry_ventilation
from cea.utilities.physics import calc_rho_air

__author__ = "Gabriel Happle"
__copyright__ = "Copyright 2015, Architecture and Building Systems - ETH Zurich"
__credits__ = ["Gabriel Happle"]
__license__ = "MIT"
__version__ = "0.1"
__maintainer__ = "Daren Thomas"
__email__ = "cea@arch.ethz.ch"
__status__ = "Production"


# ventilation calculation


[docs]def calc_air_flows(temp_zone, u_wind, temp_ext, dict_props_nat_vent): """ Minimization of variable air flows as a function of zone gauge :param temp_zone: zone indoor air temperature (°C) :param u_wind: wind velocity (m/s) :param temp_ext: exterior air temperature (°C) :param dict_props_nat_vent: dictionary containing natural ventilation properties of zone qm_sum_in : total air mass flow rates into zone (kg/h) qm_sum_out : total air mass flow rates out of zone (kg/h) """ # Different solver options to try: # -------------------------------- # solver_options_nelder_mead ={'disp': False, 'maxiter': 100, 'maxfev': None, 'xtol': 0.1, 'ftol': 0.1} # solver_options_cg ={'disp': False, 'gtol': 1e-05, 'eps': 1.4901161193847656e-08, 'return_all': False, # 'maxiter': None, 'norm': -np.inf} # solver_options_powell = {'disp': True, 'maxiter': None, 'direc': None, 'maxfev': None, # 'xtol': 0.0001, 'ftol': 0.0001} # solver_options_tnc = {'disp': True, 'minfev': 0, 'scale': None, 'rescale': -1, 'offset': None, 'gtol': -1, # 'eps': 1e-08, 'eta': -1, 'maxiter': None, 'maxCGit': -1, 'mesg_num': None, # 'ftol': -1, 'xtol': -1, 'stepmx': 0, 'accuracy': 0} solver_options_cobyla = {'iprint': 1, 'disp': False, 'maxiter': 100, 'rhobeg': 1.0, 'tol': 0.001} # solve air flow mass balance via iteration p_zone_ref = 1 # (Pa) zone pressure, THE UNKNOWN VALUE res = minimize(calc_air_flow_mass_balance, p_zone_ref, args=(temp_zone, u_wind, temp_ext, dict_props_nat_vent, 'minimize',), method='COBYLA', options=solver_options_cobyla) # print(res) # get zone pressure of air flow mass balance p_zone = res.x # calculate air flows at zone pressure qm_sum_in, qm_sum_out = calc_air_flow_mass_balance(p_zone, temp_zone, u_wind, temp_ext, dict_props_nat_vent, 'calculate') return qm_sum_in, qm_sum_out
[docs]def get_properties_natural_ventilation(bpr, gv): """ gdf_geometry_building : GeoDataFrame containing geometry properties of single building gdf_architecture_building : GeoDataFrame containing architecture props of single building :param gv: globalvars :param bpr: building propert row :returns: dictionary containing natural ventilation properties of zone """ n50 = bpr.architecture.n50 vol_building = bpr.geometry['footprint'] * bpr.geometry['height_ag'] qv_delta_p_lea_ref_zone = calc_qv_delta_p_ref(n50, vol_building) area_facade_zone,\ area_roof_zone,\ height_zone,\ slope_roof = get_building_geometry_ventilation(bpr.geometry) class_shielding = gv.shielding_class # factor_cros = bpr.architecture.f_cros factor_cros = 0 # TODO write dict function to look up, ZERO is for office and industrial functions area_vent_zone = 0 # (cm2) area of ventilation openings # TODO: get from buildings properties # calculate properties that remain constant in the minimization # (a) LEAKAGES coeff_lea_path,\ height_lea_path,\ orientation_lea_path = allocate_default_leakage_paths(calc_coeff_lea_zone(qv_delta_p_lea_ref_zone), area_facade_zone, area_roof_zone, height_zone) coeff_wind_pressure_path_lea = lookup_coeff_wind_pressure(height_lea_path, class_shielding, orientation_lea_path, slope_roof, factor_cros) # (b) VENTILATION OPENINGS coeff_vent_path,\ height_vent_path,\ orientation_vent_path = allocate_default_ventilation_openings(calc_coeff_vent_zone(area_vent_zone), height_zone) coeff_wind_pressure_path_vent = lookup_coeff_wind_pressure(height_vent_path, class_shielding, orientation_vent_path, slope_roof, factor_cros) # make dict for output dict_props_nat_vent = {'coeff_lea_path': coeff_lea_path, 'height_lea_path': height_lea_path, 'coeff_wind_pressure_path_lea': coeff_wind_pressure_path_lea, 'coeff_vent_path': coeff_vent_path, 'height_vent_path': height_vent_path, 'coeff_wind_pressure_path_vent': coeff_wind_pressure_path_vent, 'factor_cros': factor_cros} return dict_props_nat_vent
# Wind pressure calculation
[docs]def calc_u_wind_site(u_wind_10): """ Adjusts meteorological wind velocity to site surroundings according to 6.4.2.2 in [1] :param u_wind_10: meteorological wind velocity (m/s) :returns: u_wind_site, site wind velocity (m/s) """ # terrain class of surroundings # 0 = open # 1 = rural # 2 = urban ter_class = 2 # TODO: move to globalvars # factors from Table B.11 in B.1.4.1 in [1] f_wnd = np.array([1.0, 0.9, 0.8]) # Equation (2) in [1] return f_wnd[ter_class] * u_wind_10
[docs]def lookup_coeff_wind_pressure(height_path, class_shielding, orientation_path, slope_roof, factor_cros): """ Lookup default wind pressure coefficients for air leakage paths according to B.1.3.3 in [1] :param height_path: :param class_shielding: :param orientation_path: :param slope_roof: :param factor_cros: :returns: wind pressure coefficients (-) Conventions: class_shielding = 0 : open terrain class_shielding = 1 : normal class_shielding = 2 : shielded orientation_path = 0 : facade facing wind 1 : facade not facing wind 2 : roof factor_cros = 0 : cross ventilation not possible = 1 : cross ventilation possible """ # Table B.5 in [1] table_coeff_wind_pressure_cross_ventilation = np.array([[0.5, -0.7, -0.7, -0.6, -0.2], [0.25, -0.5, -0.6, -0.5, -0.2], [0.05, -0.3, -0.5, -0.4, -0.2], [0.65, -0.7, -0.7, -0.6, -0.2], [0.45, -0.5, -0.6, -0.5, -0.2], [0.25, -0.3, -0.5, -0.4, -0.2], [0.8, -0.7, -0.7, -0.6, -0.2]]) # Table B.6 in [1] table_coeff_wind_pressure_non_cross_ventilation = np.array([0.05, -0.05, 0]) num_paths = height_path.shape[0] coeff_wind_pressure = np.zeros(num_paths) for i in range(0, num_paths): if factor_cros == 1: if 0 <= height_path[i] < 15: index_row = 0 elif 15 <= height_path[i] < 50: index_row = 3 elif height_path[i] >= 50: index_row = 6 else: # default index_row = 6 print('Warning: unsupported height') print(height_path[i]) index_row = min(index_row + class_shielding, 6) if orientation_path[i] == 2: if 0 <= slope_roof < 10: index_col = 0 elif 10 <= slope_roof <= 30: index_col = 1 elif slope_roof > 30: index_col = 2 else: # default index_col = 0 print('Warning: unsupported roof slope') print(slope_roof) index_col = index_col + orientation_path[i] elif orientation_path[i] == 0 or orientation_path[i] == 1: index_col = orientation_path[i] else: # default index_col = orientation_path[i] print('Warning: unsupported orientation') print(orientation_path[i]) coeff_wind_pressure[i] = table_coeff_wind_pressure_cross_ventilation[index_row, index_col] elif factor_cros == 0: index_col = orientation_path[i] coeff_wind_pressure[i] = table_coeff_wind_pressure_non_cross_ventilation[index_col] return coeff_wind_pressure
[docs]def calc_delta_p_path(p_zone_ref, height_path, temp_zone, coeff_wind_pressure_path, u_wind_site, temp_ext): """ Calculation of indoor-outdoor pressure difference at air path according to 6.4.2.4 in [1] :param p_zone_ref: zone reference pressure (Pa) :param height_path: height of ventilation path (m) :param temp_zone: air temperature of ventilation zone in (°C) :param coeff_wind_pressure_path: wind pressure coefficient of ventilation path (-) :param u_wind_site: wind velocity (m/s) :param temp_ext: external air temperature (°C) :returns: ``delta_p_path``, pressure difference across ventilation path (Pa) """ # constants from Table 12 in [1] # TODO import from global variables g = 9.81 # (m/s2) rho_air_ref = 1.23 # (kg/m3) temp_ext_ref = 283 # (K) temp_zone += 273 # conversion to (K) temp_ext += 273 # conversion to (K) # Equation (5) in [1] p_zone_path = p_zone_ref - rho_air_ref * height_path * g * temp_ext_ref / temp_zone # Equation (4) in [1] p_ext_path = rho_air_ref * ( 0.5 * coeff_wind_pressure_path * u_wind_site ** 2 - height_path * g * temp_ext_ref / temp_ext) # Equation (3) in [1] delta_p_path = p_ext_path - p_zone_path return delta_p_path
# leakages through the envelope
[docs]def calc_qv_delta_p_ref(n_delta_p_ref, vol_building): """ Calculate airflow at reference pressure according to 6.3.2 in [2] :param n_delta_p_ref: air changes at reference pressure [1/h] :param vol_building: building_volume [m3] :returns: qv_delta_p_ref : air volume flow rate at reference pressure (m3/h) """ # Eq. (9) in [2] return n_delta_p_ref * vol_building
[docs]def calc_qv_lea_path(coeff_lea_path, delta_p_lea_path): """ Calculate volume air flow of single leakage path according to 6.4.3.6.5 in [1] :param coeff_lea_path: coefficient of leakage path :param delta_p_lea_path: pressure difference across leakage path (Pa) :returns: qv_lea_path : volume flow rate across leakage path (m3/h) """ # default values in [1] # TODO reference global variables n_lea = 0.667 # (-), B.1.3.15 in [1] # Equation (64) in [1] qv_lea_path = coeff_lea_path * np.sign(delta_p_lea_path) * np.abs(delta_p_lea_path) ** n_lea return qv_lea_path
[docs]def calc_coeff_lea_zone(qv_delta_p_lea_ref): """ Calculate default leakage coefficient of zone according to B.1.3.16 in [1] :param qv_delta_p_lea_ref: air volume flow rate at reference pressure (m3/h) :returns: coeff_lea_zone : leakage coefficient of zone """ # default values in [1] # TODO reference global variables delta_p_lea_ref = 50 # (Pa), B.1.3.14 in [1] n_lea = 0.667 # (-), B.1.3.15 in [1] # Eq. (B.5) in [1] # TODO: Formula assumed to be wrong in [1], corrected to match Eq. (8) in [2] coeff_lea_zone = qv_delta_p_lea_ref / (delta_p_lea_ref ** n_lea) return coeff_lea_zone
[docs]def allocate_default_leakage_paths(coeff_lea_zone, area_facade_zone, area_roof_zone, height_zone): """ Allocate default leakage paths according to B.1.3.17 in [1] :param coeff_lea_zone: leakage coefficient of zone :param area_facade_zone: facade area of zone (m2) :param area_roof_zone: roof area of zone (m2) :param height_zone: height of zone (m) :returns: - coeff_lea_path : coefficients of default leakage paths - height_lea_path : heights of default leakage paths (m) - orientation_lea_path : orientation index of default leakage paths (-) """ # Equation (B.6) in [1] coeff_lea_facade = coeff_lea_zone * area_facade_zone / (area_facade_zone + area_roof_zone) # Equation (B.7) in [1] coeff_lea_roof = coeff_lea_zone * area_roof_zone / (area_facade_zone + area_roof_zone) coeff_lea_path = np.zeros(5) height_lea_path = np.zeros(5) orientation_lea_path = [0, 1, 0, 1, 2] # Table B.10 in [1] # default leakage path 1 coeff_lea_path[0] = 0.25 * coeff_lea_facade height_lea_path[0] = 0.25 * height_zone orientation_lea_path[0] = 0 # facade facing the wind # default leakage path 2 coeff_lea_path[1] = 0.25 * coeff_lea_facade height_lea_path[1] = 0.25 * height_zone orientation_lea_path[1] = 1 # facade not facing the wind # default leakage path 3 coeff_lea_path[2] = 0.25 * coeff_lea_facade height_lea_path[2] = 0.75 * height_zone orientation_lea_path[2] = 0 # facade facing the wind # default leakage path 4 coeff_lea_path[3] = 0.25 * coeff_lea_facade height_lea_path[3] = 0.75 * height_zone orientation_lea_path[3] = 1 # facade not facing the wind # default leakage path 5 coeff_lea_path[4] = coeff_lea_roof height_lea_path[4] = height_zone orientation_lea_path[4] = 2 # roof return coeff_lea_path, height_lea_path, orientation_lea_path
[docs]def calc_qm_lea(p_zone_ref, temp_zone, temp_ext, u_wind_site, dict_props_nat_vent): """ Calculation of leakage infiltration and exfiltration air mass flow as a function of zone indoor reference pressure :param p_zone_ref: zone reference pressure (Pa) :param temp_zone: air temperature in ventilation zone (°C) :param temp_ext: exterior air temperature (°C) :param u_wind_site: wind velocity (m/s) :param dict_props_nat_vent: dictionary containing natural ventilation properties of zone :returns: - qm_lea_in : air mass flow rate into zone through leakages (kg/h) - qm_lea_out : air mass flow rate out of zone through leakages (kg/h) """ # get default leakage paths from locals coeff_lea_path = dict_props_nat_vent['coeff_lea_path'] height_lea_path = dict_props_nat_vent['height_lea_path'] # lookup wind pressure coefficients for leakage paths from locals coeff_wind_pressure_path = dict_props_nat_vent['coeff_wind_pressure_path_lea'] # calculation of pressure difference at leakage path delta_p_path = calc_delta_p_path(p_zone_ref, height_lea_path, temp_zone, coeff_wind_pressure_path, u_wind_site, temp_ext) # calculation of leakage air volume flow at path qv_lea_path = calc_qv_lea_path(coeff_lea_path, delta_p_path) # Eq. (65) in [1], infiltration is sum of air flows greater zero qv_lea_in = qv_lea_path[np.where(qv_lea_path > 0)].sum() # Eq. (66) in [1], exfiltration is sum of air flows smaller zero qv_lea_out = qv_lea_path[np.where(qv_lea_path < 0)].sum() # conversion to air mass flows according to 6.4.3.8 in [1] # Eq. (67) in [1] qm_lea_in = qv_lea_in * calc_rho_air(temp_ext) # Eq. (68) in [1] qm_lea_out = qv_lea_out * calc_rho_air(temp_zone) # print (qm_lea_in, qm_lea_out) return qm_lea_in, qm_lea_out
# operation of window openings
[docs]def calc_qv_vent_path(coeff_vent_path, delta_p_vent_path): """ Calculate volume air flow of single ventilation opening path according to 6.4.3.6.4 in [1] :param coeff_vent_path : ventilation opening coefficient of air path :param delta_p_vent_path : pressure difference across air path (Pa) :returns: qv_vent_path : air volume flow rate across air path (m3/h) """ # default values in [1] # TODO reference global variables n_vent = 0.5 # (-), B.1.2.2 in [1] # Equation (60) in [1] qv_vent_path = coeff_vent_path * np.sign(delta_p_vent_path) * np.abs(delta_p_vent_path) ** n_vent return qv_vent_path
[docs]def calc_coeff_vent_zone(area_vent_zone): """ Calculate air volume flow coefficient of ventilation openings of zone according to 6.4.3.6.4 in [1] :param area_vent_zone : total area of ventilation openings of zone (cm2) :returns coeff_vent_zone : coefficient of ventilation openings of zone """ # default values in [1] # TODO reference global variables n_vent = 0.5 # (-), B.1.2.2 in [1] coeff_d_vent = 0.6 # (-), B.1.2.1 in [1] delta_p_vent_ref = 50 # (Pa) FIXME no default value specified in standard # constants from Table 12 in [1] rho_air_ref = 1.23 # (kg/m3) # Eq. (61) in [1] coeff_vent_zone = 3600 / 10000 * coeff_d_vent * area_vent_zone * (2 / rho_air_ref) ** 0.5 * \ (1 / delta_p_vent_ref) ** (n_vent - 0.5) return coeff_vent_zone
[docs]def allocate_default_ventilation_openings(coeff_vent_zone, height_zone): """ Allocate default ventilation openings according to B.1.3.13 in [1] :param coeff_vent_zone : coefficient of ventilation openings of zone :param height_zone : height of zone (m) :returns: - coeff_vent_path : coefficients of default ventilation opening paths - height_vent_path : heights of default ventilation opening paths (m) - orientation_vent_path : orientation index of default ventilation opening paths (-) """ # initialize coeff_vent_path = np.zeros(4) height_vent_path = np.zeros(4) orientation_vent_path = [0, 1, 0, 1] # Table B.9 in [1] # default leakage path 1 coeff_vent_path[0] = 0.25 * coeff_vent_zone height_vent_path[0] = 0.25 * height_zone orientation_vent_path[0] = 0 # facade facing the wind # default leakage path 2 coeff_vent_path[1] = 0.25 * coeff_vent_zone height_vent_path[1] = 0.25 * height_zone orientation_vent_path[1] = 1 # facade not facing the wind # default leakage path 3 coeff_vent_path[2] = 0.25 * coeff_vent_zone height_vent_path[2] = 0.75 * height_zone orientation_vent_path[2] = 0 # facade facing the wind # default leakage path 4 coeff_vent_path[3] = 0.25 * coeff_vent_zone height_vent_path[3] = 0.75 * height_zone orientation_vent_path[3] = 1 # facade not facing the wind return coeff_vent_path, height_vent_path, orientation_vent_path
[docs]def calc_qm_vent(p_zone_ref, temp_zone, temp_ext, u_wind_site, dict_props_nat_vent): """ Calculation of air flows through ventilation openings in the facade :param p_zone_ref : zone reference pressure (Pa) :param temp_zone : zone air temperature (°C) :param temp_ext : exterior air temperature (°C) :param u_wind_site : wind velocity (m/s) :param dict_props_nat_vent : dictionary containing natural ventilation properties of zone :returns: - qm_vent_in : air mass flow rate into zone through ventilation openings (kg/h) - qm_vent_out : air mass flow rate out of zone through ventilation openings (kg/h) """ # get properties from locals() coeff_vent_path = dict_props_nat_vent['coeff_vent_path'] height_vent_path = dict_props_nat_vent['height_vent_path'] coeff_wind_pressure_path = dict_props_nat_vent['coeff_wind_pressure_path_vent'] # calculation of pressure difference at leakage path delta_p_path = calc_delta_p_path(p_zone_ref, height_vent_path, temp_zone, coeff_wind_pressure_path, u_wind_site, temp_ext) # calculation of leakage air volume flow at path qv_vent_path = calc_qv_vent_path(coeff_vent_path, delta_p_path) # Eq. (62) in [1], air flow entering through ventilation openings is sum of air flows greater zero qv_vent_in = qv_vent_path[np.where(qv_vent_path > 0)].sum() # Eq. (63) in [1], air flow entering through ventilation openings is sum of air flows smaller zero qv_vent_out = qv_vent_path[np.where(qv_vent_path < 0)].sum() # conversion to air mass flows according to 6.4.3.8 in [1] # Eq. (67) in [1] qm_vent_in = qv_vent_in * calc_rho_air(temp_ext) # Eq. (68) in [1] qm_vent_out = qv_vent_out * calc_rho_air(temp_zone) # print (qm_lea_in, qm_lea_out) return qm_vent_in, qm_vent_out
# windows ventilation
[docs]def calc_area_window_free(area_window_max, r_window_arg): """ Calculate free window opening area according to 6.4.3.5.2 in [1] :param area_window_max : area of single operable window (m2) :param r_window_arg : fraction of window opening (-) :returns: area_window_free : open area of window (m2) """ # TODO: check final standard for area_window_max # default values # r_window_arg = 0.5 # (-), Tab 11 in [1] # TODO: this might be a dynamic parameter # Eq. (42) in [1] area_window_free = r_window_arg * area_window_max return area_window_free
[docs]def calc_area_window_tot(dict_windows_building, r_window_arg): """ Calculation of total open window area according to 6.4.3.5.2 in [1] :param dict_windows_building : dictionary containing information of all windows in building :param r_window_arg : fraction of window opening (-) :returns: area_window_tot = total open area of windows in building (m2) """ # Eq. (43) in [1] # TODO account only for operable windows and not total building window area area_window_tot = calc_area_window_free(sum(dict_windows_building['area_window']), r_window_arg) return area_window_tot
[docs]def calc_effective_stack_height(dict_windows_building): """ Calculation of effective stack height for window ventilation according to 6.4.3.4.1 in [1] :param dict_windows_building : dictionary containing information of all windows in building :returns: height_window_stack : effective stack height of windows of building (m) """ # TODO: maybe this formula is wrong --> check final edition of standard as soon as possible # first part of Eq. (46) in [1] height_window_stack_1 = dict_windows_building['height_window_in_zone'] + np.array(dict_windows_building[ 'height_window_above_ground']) / 2 # second part of Eq. (46) in [1] height_window_stack_2 = dict_windows_building['height_window_in_zone'] - np.array(dict_windows_building[ 'height_window_above_ground']) / 2 # Eq. (46) in [1] height_window_stack = max(height_window_stack_1) - min(height_window_stack_2) return height_window_stack
[docs]def calc_area_window_cros(dict_windows_building, r_window_arg): """ Calculate cross-ventilation window area according to the procedure in 6.4.3.5.4.3 in [1] :param dict_windows_building : dictionary containing information of all windows in building :param r_window_arg : fraction of window opening (-) :returns: area_window_cros : effective window area for cross ventilation (m2) """ # initialize results area_window_ori = np.zeros(4) area_window_cros = np.zeros(2) # area window tot area_window_tot = calc_area_window_tot(dict_windows_building, r_window_arg) for i in range(2): for j in range(4): # Eq. (51) in [1] alpha_ref = i * 45 + j * 90 alpha_max = alpha_ref + 45 alpha_min = alpha_ref - 45 for k in range(len(dict_windows_building['name_building'])): if alpha_min <= dict_windows_building['orientation_window'][k] <= \ alpha_max and dict_windows_building['angle_window'][k] >= 60: # Eq. (52) in [1] area_window_free = calc_area_window_free(dict_windows_building['area_window'][k], r_window_arg) area_window_ori[j] = area_window_ori[j] + area_window_free for j in range(4): if area_window_ori[j] > 0: # Eq. (53) in [1] area_window_cros[i] += 0.25 * 1 / (((1 / area_window_ori[j] ** 2) + (1 / (area_window_tot - area_window_ori[j]) ** 2)) ** 0.5) # Eq. (54) in [1] return min(area_window_cros)
[docs]def calc_qm_arg(factor_cros, temp_ext, dict_windows_building, u_wind_10, temp_zone, r_window_arg): """ Calculation of cross ventilated and non-cross ventilated window ventilation according to procedure in 6.4.3.5.4 in [1] :param factor_cros : cross ventilation factor [0,1] :param temp_ext : exterior temperature (°C) :param dict_windows_building : dictionary containing information of all windows in building :param u_wind_10 : wind velocity (m/s) :param temp_zone : zone temperature (°C) :param r_window_arg : fraction of window opening (-) :returns: window ventilation air mass flows in (kg/h) """ # initialize result q_v_arg_in = 0 q_v_arg_out = 0 qm_arg_in = qm_arg_out = 0 # this is the output for buildings without windows # if building has windows if dict_windows_building: # constants from Table 12 in [1] # TODO import from global variables rho_air_ref = 1.23 # (kg/m3) coeff_turb = 0.01 # (m/s) coeff_wind = 0.001 # (1/(m/s)) coeff_stack = 0.0035 # ((m/s)/(mK)) # default values from annex B in [1] coeff_d_window = 0.67 # (-), B.1.2.1 in [1] delta_c_p = 0.75 # (-), option 2 in B.1.3.4 in [1] # get necessary inputs rho_air_ext = calc_rho_air(temp_ext) rho_air_zone = calc_rho_air(temp_zone) area_window_tot = calc_area_window_tot(dict_windows_building, r_window_arg) h_window_stack = calc_effective_stack_height(dict_windows_building) # print(h_window_stack, area_window_tot) # volume flow rates of non-cross ventilated zone according to 6.4.3.5.4.2 in [1] if factor_cros == 0: # Eq. (47) in [1] # FIXME: this equation was modified from the version in the standard (possibly wrong in preliminary version) # TODO: check final edition of standard as soon as possible q_v_arg_in = 3600 * rho_air_ref / rho_air_ext * area_window_tot / 2 * ( coeff_turb + coeff_wind * u_wind_10 ** 2 + coeff_stack * h_window_stack * abs( temp_zone - temp_ext)) # ** 0.5 # Eq. (48) in [1] q_v_arg_out = -3600 * rho_air_ref / rho_air_zone * area_window_tot / 2 * ( coeff_turb + coeff_wind * u_wind_10 ** 2 + coeff_stack * h_window_stack * abs( temp_zone - temp_ext)) # ** 0.5 # print(coeff_turb + coeff_wind * u_wind_10 ** 2 + coeff_stack * h_window_stack * abs(temp_zone - temp_ext)) elif factor_cros == 1: # get window area of cross-ventilation area_window_cros = calc_area_window_cros(dict_windows_building, r_window_arg) # print(area_window_cros) # Eq. (49) in [1] q_v_arg_in = 3600 * rho_air_ref / rho_air_ext * (( coeff_d_window * area_window_cros * u_wind_10 * delta_c_p ** 0.5) ** 2 + ( area_window_tot / 2 * ( coeff_stack * h_window_stack * abs( temp_zone - temp_ext))) ** 2) ** 0.5 # Eq. (50) in [1] # TODO this formula was changed from the standard to use the air density in the zone # TODO adjusted from the standard to have consistent units # TODO check final edition of standard as soon as possible q_v_arg_out = -3600 * rho_air_ref / rho_air_zone * (( coeff_d_window * area_window_cros * u_wind_10 * delta_c_p ** 0.5) ** 2 + ( area_window_tot / 2 * ( coeff_stack * h_window_stack * abs( temp_zone - temp_ext))) ** 2) ** 0.5 # conversion to air mass flows according to 6.4.3.8 in [1] # Eq. (67) in [1] qm_arg_in = q_v_arg_in * calc_rho_air(temp_ext) # Eq. (68) in [1] qm_arg_out = q_v_arg_out * calc_rho_air(temp_zone) return qm_arg_in, qm_arg_out
# mass balance
[docs]def calc_air_flow_mass_balance(p_zone_ref, temp_zone, u_wind_10, temp_ext, dict_props_nat_vent, option): """ Air flow mass balance for iterative calculation according to 6.4.3.9 in [1] :param p_zone_ref : zone reference pressure (Pa) :param temp_zone : air temperature in ventilation zone (°C) :param u_wind_10 : meteorological wind velocity (m/s) :param temp_ext : exterior air temperature (°C) :param dict_props_nat_vent : dictionary containing natural ventilation properties of zone :param option : 'minimize' = returns sum of air mass flows, 'calculate' = returns air mass flows :returns: sum of air mass flows in and out of zone in (kg/h) """ # adjust wind velocity u_wind_site = calc_u_wind_site(u_wind_10) qm_sup_dis = 0 qm_eta_dis = 0 qm_lea_sup_dis = 0 qm_lea_eta_dis = 0 qm_comb_in = 0 qm_comb_out = 0 qm_pdu_in = 0 qm_pdu_out = 0 qm_arg_in = 0 # removed to speed up code, as window ventilation is always balanced # with the currently implemented method qm_arg_out = 0 # qm_vent_in, qm_vent_out = calc_qm_vent(p_zone_ref, temp_zone, temp_ext, u_wind_site, dict_props_nat_vent) qm_lea_in, qm_lea_out = calc_qm_lea(p_zone_ref, temp_zone, temp_ext, u_wind_site, dict_props_nat_vent) # print('iterate air flows') # print(qm_arg_in, qm_arg_out) # print(qm_vent_in, qm_vent_out) # print(qm_lea_in, qm_lea_out) # mass balance, Eq. (69) in [1] qm_balance = qm_sup_dis + qm_eta_dis + qm_lea_sup_dis + qm_lea_eta_dis + qm_comb_in + qm_comb_out + \ qm_pdu_in + qm_pdu_out + qm_arg_in + qm_arg_out + qm_vent_in + qm_vent_out + qm_lea_in + qm_lea_out qm_sum_in = qm_sup_dis + qm_lea_sup_dis + qm_comb_in + qm_pdu_in + qm_arg_in + qm_vent_in + qm_lea_in qm_sum_out = qm_eta_dis + qm_lea_eta_dis + qm_comb_out + qm_pdu_out + qm_arg_out + qm_vent_out + qm_lea_out # switch output according to selected option if option == 'minimize': return abs(qm_balance) # for minimization the mass balance is the output elif option == 'calculate': return qm_sum_in, qm_sum_out # for the calculation the total air mass flows are output
[docs]def testing(): import geopandas import cea.globalvar gv = cea.globalvar.GlobalVariables() from cea import inputlocator import time import cea.geometry.geometry_reader # generate windows based on geometry of vertical surfaces in radiation file locator = inputlocator.InputLocator(scenario_path=r'C:\reference-case\baseline') surface_properties = pd.read_csv(locator.get_surface_properties()) gdf_building_architecture = geopandas.GeoDataFrame.from_file( locator.get_building_architecture()).drop('geometry', axis=1).set_index('Name') prop_geometry = geopandas.GeoDataFrame.from_file(locator.get_building_geometry()) prop_geometry['footprint'] = prop_geometry.area prop_geometry['perimeter'] = prop_geometry.length prop_geometry = prop_geometry.drop('geometry', axis=1).set_index('Name') df_windows = cea.geometry.geometry_reader.simple_window_generator.create_windows(surface_properties, gdf_building_architecture) building_test = 'B153737' # 'B154767' this building doesn't have windows # get building windows df_windows_building_test = df_windows.loc[df_windows['name_building'] == building_test].to_dict('list') # get building geometry gdf_building_test = prop_geometry.ix[building_test] gdf_building_architecture = gdf_building_architecture.ix[building_test] r_window_arg = 0.1 temp_ext = 5 temp_zone = 22 u_wind = 0.5 u_wind_10 = u_wind factor_cros = 1 # 1 = cross ventilation possible # TODO: get from building properties dict_props_nat_vent = get_properties_natural_ventilation(gdf_building_test, gdf_building_architecture, gv) qm_arg_in, qm_arg_out \ = calc_qm_arg(factor_cros, temp_ext, df_windows_building_test, u_wind_10, temp_zone, r_window_arg) t0 = time.time() res = calc_air_flows(temp_zone, u_wind, temp_ext, dict_props_nat_vent) t1 = time.time() print(res) print(['time: ', t1 - t0])
if __name__ == '__main__': testing()