Beispiel #1
0
def test_bisect():
    """Test the bisect rootfinding method."""
    def funct(x):
        return (x + 1) ** 2 - 1
    
    root_val = bisect(-5, 5, funct, 0.001, 0)
    assert root_val < 1e-3
Beispiel #2
0
def pmv_from_ppd(ppd, pmv_up_bound=3, ppd_tolerance=0.001):
    """Calculate the two possible Predicted Mean Vote (PMV) values for a PPD value.

    Args:
        ppd: The percentage of people dissatisfied (PPD) for which you want to know
            the possible PMV.
        pmv_up_bound: The upper limit of PMV expected for the input PPD.  Putting
            in a good estimate here will help the model converge on a
            solution faster. Defaut = 3
        ppd_tolerance: The acceptable error in meeting the target PPD.  Default = 0.001.

    Returns:
        A tuple with two elements

        -   pmv_lower: The lower (cold) PMV value that will produce the input ppd.
        -   pmv_upper: The upper (hot) PMV value that will produce the input ppd.
    """
    assert ppd > 5 and ppd < 100, \
        'PPD value {}% is outside acceptable limits of the PMV model.'.format(ppd)

    def fn(pmv):
        return (100.0 - 95.0 * math.exp(-0.03353 * pow(pmv, 4.) -
                                        0.2179 * pow(pmv, 2.0))) - ppd

    # Solve for the missing higher PMV value.
    pmv_upper = secant(0, pmv_up_bound, fn, ppd_tolerance)
    if pmv_upper is None:
        pmv_upper = bisect(0, pmv_up_bound, fn, ppd_tolerance, 0)
    pmv_lower = pmv_upper * -1

    return pmv_lower, pmv_upper
Beispiel #3
0
def calc_missing_utci_input(target_utci,
                            utci_inputs,
                            low_bound=0.,
                            up_bound=100.,
                            tolerance=0.001):
    """Return the value of a missing_utci_input given a target_utci and the 3 other inputs.

    This is particularly useful when trying to draw comfort polygons on charts
    using the UTCI model.

    Args:
        target_utci: The target UTCI temperature that you are trying to produce
            from the inputs to the UTCI model.
        utci_inputs: A dictionary of 4 UTCI inputs with the following keys:
            'ta', 'tr', 'vel', 'rh'.  Each key should correspond to a value
            that represents that UTCI input but one of these inputs should
            have a value of None.
            The input corresponding to None will be solved for by this function.
            One can also input None for both 'ta' and 'tr' to solve for the operative
            temperature that meets the target_utci. In this case, both 'ta' and 'tr'
            in the output dictionary will be the same.
            Example (solving for relative humidity):

         .. code-block:: python

            {'ta': 20, 'tr': 20, 'vel': 0.05, 'rh': None}

        low_bound: The lowest possible value of the missing input you are tying to
            find. Putting in a good value here will help the model converge to a
            solution faster.
        up_bound: The highest possible value of the missing input you are tying to
            find. Putting in a good value here will help the model converge to a
            solution faster.
        tolerance: The acceptable error in the target_utci. The default is set to 0.001

    Returns:
        complete_utci_inputs -- The utci_inputs dictionary but with values for
        all inputs. The missing input to the UTCI model will be filled by the value
        that returns the target_utci.
    """
    assert len(utci_inputs.keys()) == 4, \
        'utci_inputs must have 4 keys. Got {}.'.format(len(utci_inputs.keys()))

    # Determine the function that should be used given the missing input.
    if utci_inputs['ta'] is None and utci_inputs['tr'] is None:

        def fn(x):
            return universal_thermal_climate_index(
                x, x, utci_inputs['vel'], utci_inputs['rh']) - target_utci

        missing_key = ('ta', 'tr')
    elif utci_inputs['ta'] is None:

        def fn(x):
            return universal_thermal_climate_index(
                x, utci_inputs['tr'], utci_inputs['vel'],
                utci_inputs['rh']) - target_utci

        missing_key = 'ta'
    elif utci_inputs['tr'] is None:

        def fn(x):
            return universal_thermal_climate_index(
                utci_inputs['ta'], x, utci_inputs['vel'],
                utci_inputs['rh']) - target_utci

        missing_key = 'tr'
    elif utci_inputs['vel'] is None:

        def fn(x):
            return target_utci - universal_thermal_climate_index(
                utci_inputs['ta'], utci_inputs['tr'], x, utci_inputs['rh'])

        missing_key = 'vel'
    else:

        def fn(x):
            return universal_thermal_climate_index(
                utci_inputs['ta'], utci_inputs['tr'], utci_inputs['vel'],
                x) - target_utci

        missing_key = 'rh'

    # Solve for the missing input using the function.
    missing_val = secant(low_bound, up_bound, fn, tolerance)
    if missing_val is None:
        missing_val = bisect(low_bound, up_bound, fn, tolerance, 0)

    # complete the input dictionary
    if isinstance(missing_key, str):
        utci_inputs[missing_key] = missing_val
    else:
        for key in missing_key:
            utci_inputs[key] = missing_val
    return utci_inputs
Beispiel #4
0
def calc_missing_pmv_input(target_pmv,
                           pmv_inputs,
                           low_bound=0.,
                           up_bound=100.,
                           tolerance=0.001,
                           still_air_threshold=0.1):
    """Return the value of a missing_pmv_input given a target_pmv and the 6 other inputs.

    This is particularly useful when trying to draw comfort polygons on charts
    using the PMV model.

    Args:
        target_pmv: The target PMV that you are trying to produce from the inputs to
            the PMV model.
        pmv_inputs: A dictionary of 7 pmv inputs with the following keys:
            'ta', 'tr', 'vel', 'rh', 'met', 'clo', 'wme'.  Each key
            should correspond to a value that represents that pmv input
            but one of these inputs should have a value of None.
            The input corresponding to None will be solved for by this function.
            One can also input None for both 'ta' and 'tr' to solve for the operative
            temperature that meets the target_pmv. In this case, both 'ta' and 'tr'
            in the output dictionary will be the same.
            Example (solving for relative humidity):

            .. code-block:: python

                {'ta': 20, 'tr': 20, 'vel': 0.05, 'rh': None,'met': 1.2, 'clo': 0.75, 'wme': 0}

        low_bound: The lowest possible value of the missing input you are tying to
            find. Putting in a good value here will help the model converge to a
            solution faster.
        up_bound: The highest possible value of the missing input you are tying to
            find. Putting in a good value here will help the model converge to a
            solution faster.
        tolerance: The acceptable error in the target_pmv. The default is set to 0.001
        still_air_threshold: The air velocity in m/s at which the Pierce
            Standard Effective Temperature (SET) model will be used
            to correct values in the original Fanger PMV model.
            Default is 0.1 m/s per the 2015 release of ASHRAE Standard-55.

    Returns:
        complete_pmv_inputs -- The pmv_inputs dictionary but with values for all inputs.
        The missing input to the PMV model will be filled by the value
        that returns the target_pmv.
    """
    assert len(pmv_inputs.keys()) == 7, \
        'pmv_inputs must have 7 keys. Got {}.'.format(len(pmv_inputs.keys()))

    # Determine the function that should be used given the missing input.
    if pmv_inputs['ta'] is None and pmv_inputs['tr'] is None:

        def fn(x):
            return predicted_mean_vote(x, x, pmv_inputs['vel'],
                                       pmv_inputs['rh'], pmv_inputs['met'],
                                       pmv_inputs['clo'], pmv_inputs['wme'],
                                       still_air_threshold)['pmv'] - target_pmv

        missing_key = ('ta', 'tr')
    elif pmv_inputs['ta'] is None:

        def fn(x):
            return predicted_mean_vote(x, pmv_inputs['tr'], pmv_inputs['vel'],
                                       pmv_inputs['rh'], pmv_inputs['met'],
                                       pmv_inputs['clo'], pmv_inputs['wme'],
                                       still_air_threshold)['pmv'] - target_pmv

        missing_key = 'ta'
    elif pmv_inputs['tr'] is None:

        def fn(x):
            return predicted_mean_vote(pmv_inputs['ta'], x, pmv_inputs['vel'],
                                       pmv_inputs['rh'], pmv_inputs['met'],
                                       pmv_inputs['clo'], pmv_inputs['wme'],
                                       still_air_threshold)['pmv'] - target_pmv

        missing_key = 'tr'
    elif pmv_inputs['vel'] is None:

        def fn(x):
            return target_pmv - predicted_mean_vote(
                pmv_inputs['ta'], pmv_inputs['tr'], x, pmv_inputs['rh'],
                pmv_inputs['met'], pmv_inputs['clo'], pmv_inputs['wme'],
                still_air_threshold)['pmv']

        missing_key = 'vel'
    elif pmv_inputs['rh'] is None:

        def fn(x):
            return predicted_mean_vote(pmv_inputs['ta'], pmv_inputs['tr'],
                                       pmv_inputs['vel'], x, pmv_inputs['met'],
                                       pmv_inputs['clo'], pmv_inputs['wme'],
                                       still_air_threshold)['pmv'] - target_pmv

        missing_key = 'rh'
    elif pmv_inputs['met'] is None:

        def fn(x):
            return predicted_mean_vote(pmv_inputs['ta'], pmv_inputs['tr'],
                                       pmv_inputs['vel'], pmv_inputs['rh'], x,
                                       pmv_inputs['clo'], pmv_inputs['wme'],
                                       still_air_threshold)['pmv'] - target_pmv

        missing_key = 'met'
    elif pmv_inputs['clo'] is None:

        def fn(x):
            return predicted_mean_vote(pmv_inputs['ta'], pmv_inputs['tr'],
                                       pmv_inputs['vel'], pmv_inputs['rh'],
                                       pmv_inputs['met'], x, pmv_inputs['wme'],
                                       still_air_threshold)['pmv'] - target_pmv

        missing_key = 'clo'
    else:

        def fn(x):
            return predicted_mean_vote(pmv_inputs['ta'], pmv_inputs['tr'],
                                       pmv_inputs['vel'], pmv_inputs['rh'],
                                       pmv_inputs['met'], pmv_inputs['clo'], x,
                                       still_air_threshold)['pmv'] - target_pmv

        missing_key = 'wme'

    # Solve for the missing input using the function.
    missing_val = None
    if missing_key != 'clo':  # bisect is much better at finding reasonable clo values
        missing_val = secant(low_bound, up_bound, fn, tolerance)
    if missing_val is None:
        missing_val = bisect(low_bound, up_bound, fn, tolerance, 0)

    # complete the input dictionary
    if isinstance(missing_key, str):
        pmv_inputs[missing_key] = missing_val
    else:
        for key in missing_key:
            pmv_inputs[key] = missing_val
    return pmv_inputs
Beispiel #5
0
def predicted_mean_vote(ta,
                        tr,
                        vel,
                        rh,
                        met,
                        clo,
                        wme=0,
                        still_air_threshold=0.1):
    """Calculate PMV using Fanger's original equation and Pierce SET model when necessary.

    This method is the officially corrent way to calculate PMV comfort according to.
    the 2015 ASHRAE-55 Thermal Comfort Standard.  This function will return
    accurate values even if the air speed is above the sill air threshold of
    Fanger's original equation (> 0.1 m/s).

    Note:
        [1] ASHRAE Standard 55 (2017). "Thermal Environmental Conditions
        for Human Occupancy".

        [2] Hoyt Tyler, Schiavon Stefano, Piccioli Alberto, Cheung Toby, Moon Dustin,
        and Steinfeld Kyle, 2017, CBE Thermal Comfort Tool. Center for the Built
        Environment, University of California Berkeley,
        http://comfort.cbe.berkeley.edu/

        [3] Doherty, T.J., and E.A. Arens.  (1988).  Evaluation of the physiological
        bases of thermal comfort models. ASHRAE Transactions, Vol. 94, Part 1, 15 pp.
        https://escholarship.org/uc/item/6pq3r5pr

    Args:
        ta: Air temperature [C]
        tr: Mean radiant temperature [C]
        vel: Relative air velocity [m/s]
        rh: Relative humidity [%]
        met: Metabolic rate [met]
        clo: Clothing [clo]
        wme: External work [met], normally around 0 when seated
        still_air_threshold: The air velocity in m/s at which the Pierce
            Standard Effective Temperature (SET) model will be used
            to correct values in the original Fanger PMV model.
            Default is 0.1 m/s per the 2015 release of ASHRAE Standard-55.

    Returns:
        A dictionary containing results of the PMV model with the following keys

        -   pmv : Predicted mean vote (PMV)
        -   ppd : Percent predicted dissatisfied (PPD) [%]
        -   se_temp: Standard effective temperature (SET) [C]
        -   ta_adj: Air temperature adjusted for air speed [C]
        -   ce : Cooling effect. The difference between the air temperature
            and the adjusted air temperature [C]
        -   heat_loss: A dictionary with the 6 heat loss terms of the PMV model.
            The dictionary items are as follows:

            -   'cond': heat loss by conduction [W]
            -   'sweat': heat loss by sweating [W]
            -   'res_l': heat loss by latent respiration [W]
            -   'res_s' heat loss by dry respiration [W]
            -   'rad': heat loss by radiation [W]
            -   'conv' heat loss by convection [W]
    """
    se_temp = pierce_set(ta, tr, vel, rh, met, clo, wme)

    if vel <= still_air_threshold:
        pmv, ppd, heat_loss = fanger_pmv(ta, tr, vel, rh, met, clo, wme)
        ta_adj = ta
        ce = 0.
    else:
        ce_l = 0.
        ce_r = 40.
        eps = 0.001  # precision of ce

        def fn(ce):
            return se_temp - pierce_set(ta - ce, tr - ce, still_air_threshold,
                                        rh, met, clo, wme)

        try:
            ce = secant(ce_l, ce_r, fn, eps)
        except OverflowError:
            ce = None
        if ce is None:
            ce = bisect(ce_l, ce_r, fn, eps, 0)

        pmv, ppd, heat_loss = fanger_pmv(ta - ce, tr - ce, still_air_threshold,
                                         rh, met, clo, wme)
        ta_adj = ta - ce

    result = {}
    result['pmv'] = pmv
    result['ppd'] = ppd
    result['set'] = se_temp
    result['ta_adj'] = ta_adj
    result['ce'] = ce
    result['heat_loss'] = heat_loss

    return result
Beispiel #6
0
def predicted_mean_vote_no_set(ta,
                               tr,
                               vel,
                               rh,
                               met,
                               clo,
                               wme=0,
                               still_air_threshold=0.1):
    """Calculate PMV using Fanger's model and Pierce SET model ONLY WHEN NECESSARY.

    This method uses the officially correct way to calculate PMV comfort according to
    the 2015 ASHRAE-55 Thermal Comfort Standard. This function will return
    correct values even if the air speed is above the sill air threshold of
    Fanger's original equation (> 0.1 m/s).

    However, because this function does not return the Standard Effective Temperature
    (SET), it will run much faster for cases that are below the still air threshold
    (roughly 1/10th the time).

    Args:
        ta: Air temperature [C]
        tr: Mean radiant temperature [C]
        vel: Relative air velocity [m/s]
        rh: Relative humidity [%]
        met: Metabolic rate [met]
        clo: Clothing [clo]
        wme: External work [met], normally around 0 when seated
        still_air_threshold: The air velocity in m/s at which the Pierce
            Standard Effective Temperature (SET) model will be used
            to correct values in the original Fanger PMV model.
            Default is 0.1 m/s per the 2015 release of ASHRAE Standard-55.

    Returns:
        A dictionary containing results of the PMV model with the following keys

        -   pmv -- Predicted mean vote (PMV)
        -   ppd -- Percent predicted dissatisfied (PPD) [%]
        -   ta_adj -- Air temperature adjusted for air speed [C]
        -   ce -- Cooling effect. The difference between the air temperature
            and the adjusted air temperature [C]
        -   heat_loss -- A dictionary with the 6 heat loss terms of the PMV model.
            The dictionary keys are as follows:

            -   cond -- heat loss by conduction [W]
            -   sweat -- heat loss by sweating [W]
            -   res_l -- heat loss by latent respiration [W]
            -   res_s -- heat loss by dry respiration [W]
            -   rad -- heat loss by radiation [W]
            -   conv -- heat loss by convection [W]
    """
    if vel <= still_air_threshold:  # use the original Fanger model
        pmv, ppd, heat_loss = fanger_pmv(ta, tr, vel, rh, met, clo, wme)
        ta_adj, ce = ta, 0.
    else:  # use the SET model to correct the cooling effect in Fanger model
        ce_l = 0.
        ce_r = 40.
        eps = 0.001  # precision of ce
        se_temp = pierce_set(ta, tr, vel, rh, met, clo, wme)

        def fn(ce):
            return se_temp - pierce_set(ta - ce, tr - ce, still_air_threshold,
                                        rh, met, clo, wme)

        try:
            ce = secant(ce_l, ce_r, fn, eps)
        except OverflowError:
            ce = None
        if ce is None:  # ce can be None because OverflowError or max secant iterations
            ce = bisect(ce_l, ce_r, fn, eps, 0)

        pmv, ppd, heat_loss = fanger_pmv(ta - ce, tr - ce, still_air_threshold,
                                         rh, met, clo, wme)
        ta_adj = ta - ce

    result = {}
    result['pmv'] = pmv
    result['ppd'] = ppd
    result['ta_adj'] = ta_adj
    result['ce'] = ce
    result['heat_loss'] = heat_loss

    return result