Beispiel #1
0
def simulate_water_quality(tree, cell_res, fn,
                           parent_cell=None, current_cell=None):
    """
    Perform a water quality simulation by doing simulations on each
    type of cells (leaves), then adding them together going upward
    (summing the values of a node's subtrees and storing them at that
    node).

    `parent_cell` is the cell type (a string with a soil type and land
    use separated by a colon) of the parent of the present node in
    tree.

    `current_cell` is the cell type for the present node.

    `cell_res` is the size of each cell (used for turning inches of
    water into volumes of water).

    `tree` is the (sub)tree of cell distributions that is currently
    under consideration.

    `fn` is a function that takes a cell type and a number of cells
    and returns a dictionary containing runoff, et, and inf as
    volumes.  This typically just calls `simulate_cell_year`, but can
    be set to something else if e.g. a simulation over a different
    time-scale is desired.
    """
    # Internal node.
    if 'cell_count' in tree and 'distribution' in tree:
        # simulate subtrees
        tally = {}
        for cell, subtree in tree['distribution'].items():
            simulate_water_quality(subtree, cell_res, fn, current_cell, cell)
            subtree_ex_dist = subtree.copy()
            subtree_ex_dist.pop('distribution', None)
            tally = dict_plus(tally, subtree_ex_dist)

        # update this node
        tree.update(tally)

    # Leaf node.
    elif 'cell_count' in tree and 'distribution' not in tree:
        # runoff, et, inf
        n = tree['cell_count']
        result = fn(current_cell, n)
        tree.update(result)

        # water quality
        if n != 0:
            runoff = result['runoff-vol'] / n
            liters = get_volume_of_runoff(runoff, n, cell_res)
            land_use = current_cell.split(':')[1]
            if is_bmp(land_use) or land_use == 'no_till' or \
               land_use == 'cluster_housing':
                land_use = parent_cell.split(':')[1]
            for pol in get_pollutants():
                tree[pol] = get_pollutant_load(land_use, pol, liters)
Beispiel #2
0
def simulate_cell_day(precip, evaptrans, cell, cell_count):
    """
    Simulate a bunch of cells of the same type during a one-day event.

    `precip` is the amount of precipitation in inches.

    `evaptrans` is evapotranspiration.

    `cell` is a string which contains a soil type and land use
    separated by a colon.

    `cell_count` is the number of cells to simulate.

    The return value is a dictionary of runoff, evapotranspiration, and
    infiltration as volumes of water.
    """
    def clamp(runoff, et, inf, precip):
        """
        This function ensures that runoff + et + inf <= precip.

        NOTE: Infiltration is normally independent of the
        precipitation level, but this function introduces a slight
        dependency (that is, at very low levels of precipitation, this
        function can cause infiltration to be smaller than it
        ordinarily would be.
        """
        total = runoff + et + inf
        if (total > precip):
            scale = precip / total
            runoff *= scale
            et *= scale
            inf *= scale
        return (runoff, et, inf)

    precip = max(0.0, precip)
    soil_type, land_use, bmp = cell.lower().split(':')

    # If  there  is no  precipitation,  then  there  is no  runoff  or
    # infiltration;  however,  there  is evapotranspiration.   (It  is
    # understood that over a period of  time, this can lead to the sum
    # of the three values exceeding the total precipitation.)
    if precip == 0.0:
        return {
            'runoff-vol': 0.0,
            'et-vol': 0.0,
            'inf-vol': 0.0,
        }

    # If  the BMP  is cluster_housing  or  no_till, then  make it  the
    # land-use.  This is  done because those two types  of BMPs behave
    # more like land-uses than they do BMPs.
    if bmp and not is_bmp(bmp):
        land_use = bmp or land_use

    # When the land-use is a built-type and the level of precipitation
    # is two inches or less, use the Pitt Small Storm Hydrology Model.
    # When the land-use is a built-type but the level of precipitation
    # is higher,  the runoff is  the larger  of that predicted  by the
    # Pitt model and NRCS model.  Otherwise, return the NRCS amount.
    if is_built_type(land_use) and precip <= 2.0:
        runoff = runoff_pitt(precip, land_use)
    elif is_built_type(land_use):
        pitt_runoff = runoff_pitt(2.0, land_use)
        nrcs_runoff = runoff_nrcs(precip, evaptrans, soil_type, land_use)
        runoff = max(pitt_runoff, nrcs_runoff)
    else:
        runoff = runoff_nrcs(precip, evaptrans, soil_type, land_use)
    inf = max(0.0, precip - (evaptrans + runoff))

    (runoff, evaptrans, inf) = clamp(runoff, evaptrans, inf, precip)
    return {
        'runoff-vol': cell_count * runoff,
        'et-vol': cell_count * evaptrans,
        'inf-vol': cell_count * inf,
    }
Beispiel #3
0
def simulate_cell_day(precip, evaptrans, cell, cell_count):
    """
    Simulate a bunch of cells of the same type during a one-day event.

    `precip` is the amount of precipitation in inches.

    `evaptrans` is evapotranspiration.

    `cell` is a string which contains a soil type and land use
    separated by a colon.

    `cell_count` is the number of cells to simulate.

    The return value is a dictionary of runoff, evapotranspiration, and
    infiltration as volumes of water.
    """
    soil_type, land_use, bmp = cell.lower().split(':')

    # If there is no precipitation, then there is no runoff or
    # infiltration.  There is evapotranspiration, however (is
    # understood that over a period of time, this can lead to the sum
    # of the three values exceeding the total precipitation).
    if precip == 0.0:
        return {
            'runoff-vol': 0.0,
            'et-vol': cell_count * evaptrans,
            'inf-vol': 0.0
        }

    # Deal with the Best Management Practices (BMPs).  For most BMPs,
    # the infiltration is read from the table and the runoff is what
    # is left over after infiltration and evapotranspiration.  Rain
    # gardens are treated differently.
    if bmp and is_bmp(bmp) and bmp != 'rain_garden':
        inf = lookup_bmp_infiltration(soil_type, bmp)  # infiltration
        runoff = precip - (evaptrans + inf)  # runoff
        return {
            'runoff-vol': cell_count * runoff,
            'et-vol': cell_count * evaptrans,
            'inf-vol': cell_count * inf
        }
    elif bmp and bmp == 'rain_garden':
        # Here, return a mixture of 20% ideal rain garden and 80% high
        # intensity residential.
        inf = lookup_bmp_infiltration(soil_type, bmp)
        runoff = precip - (evaptrans + inf)
        hi_res_cell = soil_type + ':hi_residential:'
        hi_res = simulate_cell_day(precip, evaptrans, hi_res_cell, 1)
        hir_run = hi_res['runoff-vol']
        hir_et = hi_res['et-vol']
        hir_inf = hi_res['inf-vol']
        return {
            'runoff-vol': cell_count * (0.2 * runoff + 0.8 * hir_run),
            'et-vol': cell_count * (0.2 * evaptrans + 0.8 * hir_et),
            'inf-vol': cell_count * (0.2 * inf + 0.8 * hir_inf)
        }

    # At this point, if the `bmp` string has non-zero length, it is
    # equal to either 'no_till' or 'cluster_housing'.
    land_use = bmp or land_use

    # When the land use is a built-type and the level of precipitation
    # is two inches or less, use the Pitt Small Storm Hydrology Model.
    # When the land use is a built-type but the level of precipitation
    # is higher, the runoff is the larger of that predicted by the
    # Pitt model and NRCS model.  Otherwise, return the NRCS amount.
    if is_built_type(land_use) and precip <= 2.0:
        runoff = runoff_pitt(precip, land_use)
    elif is_built_type(land_use):
        pitt_runoff = runoff_pitt(2.0, land_use)
        nrcs_runoff = runoff_nrcs(precip, evaptrans, soil_type, land_use)
        runoff = max(pitt_runoff, nrcs_runoff)
    else:
        runoff = runoff_nrcs(precip, evaptrans, soil_type, land_use)
    inf = precip - (evaptrans + runoff)

    return {
        'runoff-vol': cell_count * runoff,
        'et-vol': cell_count * evaptrans,
        'inf-vol': cell_count * max(inf, 0.0),
    }