def create_modified_census(census): """ This creates a cell census, with modifications, that is suitable for use as input to `simulate_water_quality`. For every type of cell that undergoes modification, the modifications are indicated with a sub-distribution under that cell type. """ mod = copy.deepcopy(census) mod.pop('modifications', None) for (cell, subcensus) in mod['distribution'].items(): n = subcensus['cell_count'] changes = { 'distribution': { cell: { 'distribution': { cell: { 'cell_count': n } } } } } mod = dict_plus(mod, changes) for modification in (census.get('modifications') or []): for (orig_cell, subcensus) in modification['distribution'].items(): n = subcensus['cell_count'] soil1, land1 = orig_cell.split(':') soil2, land2, bmp = modification['change'].split(':') changed_cell = '%s:%s:%s' % (soil2 or soil1, land2 or land1, bmp) changes = { 'distribution': { orig_cell: { 'distribution': { orig_cell: { 'cell_count': -n }, changed_cell: { 'cell_count': n } } } } } mod = dict_plus(mod, changes) return mod
def create_modified_census(census): """ This creates a cell census, with modifications, that is suitable for use as input to `simulate_water_quality`. For every type of cell that undergoes modification, the modifications are indicated with a sub-distribution under that cell type. """ mod = copy.deepcopy(census) mod.pop('modifications', None) for (cell, subcensus) in mod['distribution'].items(): n = subcensus['cell_count'] changes = { 'distribution': { cell: { 'distribution': { cell: {'cell_count': n} } } } } mod = dict_plus(mod, changes) for modification in (census.get('modifications') or []): for (orig_cell, subcensus) in modification['distribution'].items(): n = subcensus['cell_count'] soil1, land1 = orig_cell.split(':') soil2, land2, bmp = modification['change'].split(':') changed_cell = '%s:%s:%s' % (soil2 or soil1, land2 or land1, bmp) changes = { 'distribution': { orig_cell: { 'distribution': { orig_cell: {'cell_count': -n}, changed_cell: {'cell_count': n} } } } } mod = dict_plus(mod, changes) return mod
def simulate_cell_year(cell, cell_count): """ Simulate a cell-type for an entire year using sample precipitation and evapotranspiration data. The `cell` parameter is a string with the soil type and land use separated by a colon. The `cell_count` parameter is the number of cells of this type. If the `precolumbian` parameter is true, then the cell is simulated under Pre-Columbian circumstances (anything other than water, woody wetland, and herbaceous wetland becomes mixed forest). """ split = cell.split(':') if (len(split) == 2): split.append('') land_use = split[1] bmp = split[2] retval = {} for day in range(365): (precip, evaptrans) = lookup_pet(day, bmp or land_use) result = simulate_cell_day(precip, evaptrans, cell, cell_count) retval = dict_plus(retval, result) return retval
def test_plus_3(self): """ Test dictionary arithmetic. """ a = {'x': {'y': {'z': {'a': 2, 'c': 13}, 'n': 144}}} b = {'x': {'y': None}} self.assertEqual(dict_plus(a, b), a)
def simulate_cell_year(cell, cell_count, precolumbian): """ Simulate a cell-type for an entire year using sample precipitation and evapotranspiration data. The `cell` parameter is a string with the soil type and land use separated by a colon. The `cell_count` parameter is the number of cells of this type. If the `precolumbian` parameter is true, then the cell is simulated under Pre-Columbian circumstances (anything other than water, woody wetland, and herbaceous wetland becomes mixed forest). """ (soil_type, land_use) = cell.split(':') if precolumbian: land_use = make_precolumbian(land_use) cell = soil_type + ':' + land_use retval = {} for day in range(365): pet = lookup_pet(day, land_use) retval = dict_plus(retval, simulate_cell_day(pet, cell, cell_count)) return retval
def test_plus_2(self): """ Test dictionary arithmetic. """ a = {'x': {'y': {'z': {'a': 2, 'c': 13}, 'n': 144}}} b = {'x': {'y': {'z': {'b': 8, 'c': 21}, 'm': 610}}} c = {'x': {'y': {'z': {'a': 2, 'b': 8, 'c': 34}, 'n': 144, 'm': 610}}} self.assertEqual(dict_plus(a, b), c)
def simulate_water_quality(tree, cell_res, fn, current_cell=None, precolumbian=False): """ Perform a water quality simulation by doing simulations on each of the cell types (leaves), then adding them together by summing the values of a node's subtrees and storing them at that node. `tree` is the (sub)tree of cell distributions that is currently under consideration. `cell_res` is the size of each cell (used for turning inches of water into volumes of water). `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. `current_cell` is the cell type for the present node. """ # Internal node. if 'cell_count' in tree and 'distribution' in tree: n = tree['cell_count'] # simulate subtrees if n != 0: tally = {} for cell, subtree in tree['distribution'].items(): simulate_water_quality(subtree, cell_res, fn, cell, precolumbian) subtree_ex_dist = subtree.copy() subtree_ex_dist.pop('distribution', None) tally = dict_plus(tally, subtree_ex_dist) tree.update(tally) # update this node # effectively a leaf elif n == 0: for pol in get_pollutants(): tree[pol] = 0.0 # Leaf node. elif 'cell_count' in tree and 'distribution' not in tree: n = tree['cell_count'] split = current_cell.split(':') if (len(split) == 2): split.append('') if precolumbian: split[1] = make_precolumbian(split[1]) result = fn('%s:%s:%s' % tuple(split), n) # runoff, et, inf tree.update(result) # water quality if n != 0: soil_type, land_use, bmp = split runoff = result['runoff-vol'] / n liters = get_volume_of_runoff(runoff, n, cell_res) for pol in get_pollutants(): tree[pol] = get_pollutant_load(land_use, pol, liters)
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)
def simulate_water_quality(tree, cell_res, fn, pct=1.0, current_cell=None, precolumbian=False): """ Perform a water quality simulation by doing simulations on each of the cell types (leaves), then adding them together by summing the values of a node's subtrees and storing them at that node. `tree` is the (sub)tree of cell distributions that is currently under consideration. `pct` is the percentage of calculated water volume to retain. `cell_res` is the size of each cell (used for turning inches of water into volumes of water). `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. `current_cell` is the cell type for the present node. """ # Internal node. if 'cell_count' in tree and 'distribution' in tree: n = tree['cell_count'] # simulate subtrees if n != 0: tally = {} for cell, subtree in tree['distribution'].items(): simulate_water_quality(subtree, cell_res, fn, pct, cell, precolumbian) subtree_ex_dist = subtree.copy() subtree_ex_dist.pop('distribution', None) tally = dict_plus(tally, subtree_ex_dist) tree.update(tally) # update this node # effectively a leaf elif n == 0: for pol in get_pollutants(): tree[pol] = 0.0 # Leaf node. elif 'cell_count' in tree and 'distribution' not in tree: # the number of cells covered by this leaf n = tree['cell_count'] # canonicalize the current_cell string split = current_cell.split(':') if (len(split) == 2): split.append('') if precolumbian: split[1] = make_precolumbian(split[1]) current_cell = '%s:%s:%s' % tuple(split) # run the runoff model on this leaf result = fn(current_cell, n) # runoff, et, inf runoff_adjustment = result['runoff-vol'] - (result['runoff-vol'] * pct) result['runoff-vol'] -= runoff_adjustment result['inf-vol'] += runoff_adjustment tree.update(result) # perform water quality calculation if n != 0: soil_type, land_use, bmp = split runoff_per_cell = result['runoff-vol'] / n liters = get_volume_of_runoff(runoff_per_cell, n, cell_res) for pol in get_pollutants(): tree[pol] = get_pollutant_load(land_use, pol, liters)