Skip to content

Commit

Permalink
Merge pull request #37 from jamesmcclain/feature/precolumbian
Browse files Browse the repository at this point in the history
Let simulate_day do Pre-Columbian calculation.
  • Loading branch information
James McClain committed Aug 10, 2015
2 parents 1d2506d + 5c15db5 commit 2ac761e
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 59 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ The `simulate_cell_year` function simulates the events of an entire year for one

2. `cell_count` is the number of occurrences of that type of cell in the area of interest.

3. `precolumbian` is a boolean which determines whether to simulate the cell type as-shown or under Pre-Columbian circumstances. When a Pre-Columbian simulation is done, all land uses other than *water* and *wetland* are treated as *mixed forest*.

The output of this function is a dictionary with three keys: `runoff-vol`, `et-vol`, and `inf-vol`. These are the volumes of runoff, evapotranspiration, and infiltration, respectively, in units of inch-cells. The algorithm used to calculate these quantities is close to TR-55, the algorithm found in [the USDA's Technical Release 55, revised 1986](http://www.cpesc.org/reference/tr55.pdf), but with a few differences. The main difference is the use of *Pitt Small Storm Hydrology Model* for low levels of precipitation when the land use is a built-type.

### `simulate_water_quality`
Expand Down Expand Up @@ -62,9 +60,11 @@ The `simulate_water_quality` function does a water quality calculation over an e

The single cells of *deciduous forest*, *no-till*, and the *rock* are all underneath a node of three cells of type *deciduous forest*. That indicates a land use modification has taken place: in this case, two of three original cells of *deciduous forest* have undergone modifications.

2. The `cell_res` parameter gives the resolution (size) of each cell. It is used for converting runoff, evapotranspiration, and infiltration amounts from inches to volumes.
2. `fn` is the function that is used to perform the runoff, evapotranspiration, and infiltration calculation. It is similar to `simulate_cell_year`, except it only takes `cell` and `cell_count` arguments.

3. The `cell_res` parameter gives the resolution (size) of each cell. It is used for converting runoff, evapotranspiration, and infiltration amounts from inches to volumes.

3. `fn` is the function that is used to perform the runoff, evapotranspiration, and infiltration calculation. It is similar to `simulate_cell_year`, except it only takes `cell` and `cell_count` arguments.
4. `precolumbian` is a boolean which determines whether to simulate the cell type as-shown or under Pre-Columbian circumstances. When a Pre-Columbian simulation is done, all land uses other than *water* and *wetland* are treated as *mixed forest*.

### `simulate_modifications`

Expand Down Expand Up @@ -102,9 +102,11 @@ This function is used to simulate the effects of land use modifications. The ar

Modifications are given as an array of dictionaries. Each dictionary contains a `change` key whose value encodes the modification that has taken place. In the example above, `"::no_till"` indicates that the no-till farming BMP has been applied, while `"a:rock:"` means that that particular area has been reclassified as being mostl rocks sitting on top of A-type soil.

2. The `fn` argument is as described previously, it is responsible for performing the simulation at each cell.
2. The `cell_res` argument is as described previously.

3. The `fn` argument is as described previously, it is responsible for performing the simulation at each cell.

3. The `cell_res` argument is as described previously.
4. `precolumbian` is a boolean which determines whether to simulate the cell type as-shown or under Pre-Columbian circumstances. When a Pre-Columbian simulation is done, all land uses other than *water* and *wetland* are treated as *mixed forest*.

The output is dictionary with two keys, `modified` and `unmodified`. These respectively contain modified and unmodified trees (the trees are as described in the discussion of `simulate_water_quality`) with runoff, evapotranspiration, infiltration, and pollutant loads included.

Expand Down
57 changes: 33 additions & 24 deletions test/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1386,23 +1386,15 @@ def test_simulate_day_3(self):
result2 = simulate_cell_day(42, 93, 'a:rock:', 2)
self.assertEqual(result1['runoff-vol'] * 2, result2['runoff-vol'])

def test_simulate_year(self):
def test_simulate_cell_year(self):
"""
Yearly simulation.
"""
result1 = simulate_cell_year('a:hi_residential', 42, False)
result2 = simulate_cell_year('a:mixed_forest', 42, False)
result1 = simulate_cell_year('a:hi_residential:', 42)
result2 = simulate_cell_year('a:mixed_forest:', 42)
self.assertNotEqual(result1, result2)
self.assertGreater(result1['runoff-vol'], result2['runoff-vol'])

def test_simulate_year_precolumbian(self):
"""
Yearly simulation in Pre-Columbian times.
"""
result1 = simulate_cell_year('a:hi_residential', 42, True)
result2 = simulate_cell_year('a:mixed_forest', 42, True)
self.assertEqual(result1, result2)

def test_create_unmodified_census(self):
"""
Test create_unmodified_census.
Expand Down Expand Up @@ -1512,7 +1504,7 @@ def test_simulate_water_quality_1(self):
}

def fn(cell, cell_count):
return simulate_cell_year(cell, cell_count, False)
return simulate_cell_year(cell, cell_count)

simulate_water_quality(census, 93, fn)
left = census['distribution']['a:rock']
Expand Down Expand Up @@ -1551,7 +1543,7 @@ def test_simulate_water_quality_2(self):
}

def fn(cell, cell_count):
return simulate_cell_year(cell, cell_count, False)
return simulate_cell_year(cell, cell_count)

simulate_water_quality(census1, 93, fn)
simulate_water_quality(census2, 93, fn)
Expand All @@ -1563,30 +1555,47 @@ def test_simulate_water_quality_precolumbian(self):
Test the water quality simulation in Pre-Columbian times.
"""
census1 = {
"cell_count": 3,
"cell_count": 8,
"distribution": {
"a:rock": {"cell_count": 1},
"b:herbaceous_wetland": {"cell_count": 1},
"a:water": {"cell_count": 1}
"a:hi_residential": {"cell_count": 1},
"b:no_till": {"cell_count": 1},
"c:pasture": {"cell_count": 1},
"d:row_crop": {"cell_count": 1},
"a:water": {"cell_count": 1},
"b:chaparral": {"cell_count": 1},
"c:desert": {"cell_count": 1},
"d:tall_grass_prairie": {"cell_count": 1}
}
}

census2 = {
"cell_count": 3,
"cell_count": 8,
"distribution": {
"a:mixed_forest": {"cell_count": 1},
"b:herbaceous_wetland": {"cell_count": 1},
"a:water": {"cell_count": 1}
"b:mixed_forest": {"cell_count": 1},
"c:mixed_forest": {"cell_count": 1},
"d:mixed_forest": {"cell_count": 1},
"a:water": {"cell_count": 1},
"b:chaparral": {"cell_count": 1},
"c:desert": {"cell_count": 1},
"d:tall_grass_prairie": {"cell_count": 1}
}
}

census3 = census2.copy()

def fn(cell, cell_count):
return simulate_cell_year(cell, cell_count, True)
return simulate_cell_year(cell, cell_count)

simulate_water_quality(census1, 93, fn, precolumbian=True)
simulate_water_quality(census2, 93, fn, precolumbian=True)
simulate_water_quality(census3, 93, fn, precolumbian=False)

simulate_water_quality(census1, 93, fn)
simulate_water_quality(census2, 93, fn)
for key in set(census1.keys()) - set(['distribution']):
self.assertEqual(census1[key], census2[key])
self.assertAlmostEqual(census1[key], census2[key])

for key in set(census1.keys()) - set(['distribution']):
self.assertAlmostEqual(census2[key], census3[key])

def test_year_1(self):
"""
Expand Down
40 changes: 18 additions & 22 deletions tr55/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def simulate_cell_day(precip, evaptrans, cell, cell_count):
}


def simulate_cell_year(cell, cell_count, precolumbian):
def simulate_cell_year(cell, cell_count):
"""
Simulate a cell-type for an entire year using sample precipitation and
evapotranspiration data.
Expand All @@ -192,11 +192,6 @@ def simulate_cell_year(cell, cell_count, precolumbian):
split = cell.split(':')
if (len(split) == 2):
split.append('')

if precolumbian:
split[1] = make_precolumbian(split[1])

cell = '%s:%s:%s' % tuple(split[:3])
land_use = split[1]
bmp = split[2]

Expand Down Expand Up @@ -254,7 +249,7 @@ def create_modified_census(census):
return mod


def simulate_water_quality(tree, cell_res, fn, current_cell=None):
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
Expand All @@ -280,7 +275,7 @@ def simulate_water_quality(tree, cell_res, fn, current_cell=None):
if n != 0:
tally = {}
for cell, subtree in tree['distribution'].items():
simulate_water_quality(subtree, cell_res, fn, cell)
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)
Expand All @@ -294,14 +289,17 @@ def simulate_water_quality(tree, cell_res, fn, current_cell=None):
# Leaf node.
elif 'cell_count' in tree and 'distribution' not in tree:
n = tree['cell_count']
result = fn(current_cell, n) # runoff, et, inf
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:
split = current_cell.split(':')
if (len(split) == 2):
split.append('')
soil_type, land_use, bmp = split
runoff = result['runoff-vol'] / n
liters = get_volume_of_runoff(runoff, n, cell_res)
Expand Down Expand Up @@ -332,7 +330,7 @@ def postpass(tree):
postpass(subtree)


def simulate_modifications(census, fn, cell_res):
def simulate_modifications(census, fn, cell_res, precolumbian=False):
"""
Simulate an entire year, including effects of modifications.
Expand All @@ -343,11 +341,11 @@ def simulate_modifications(census, fn, cell_res):
`cell_res` is as described in `simulate_water_quality`.
"""
mod = create_modified_census(census)
simulate_water_quality(mod, cell_res, fn)
simulate_water_quality(mod, cell_res, fn, precolumbian=precolumbian)
postpass(mod)

unmod = create_unmodified_census(census)
simulate_water_quality(unmod, cell_res, fn)
simulate_water_quality(unmod, cell_res, fn, precolumbian=precolumbian)
postpass(unmod)

return {
Expand All @@ -367,12 +365,12 @@ def simulate_year(census, cell_res=10, precolumbian=False):
`precolumbian` is as described in `simulate_cell_year`.
"""
def fn(cell, cell_count):
return simulate_cell_year(cell, cell_count, precolumbian)
return simulate_cell_year(cell, cell_count)

return simulate_modifications(census, fn, cell_res)
return simulate_modifications(census, fn, cell_res, precolumbian)


def simulate_day(census, precip, cell_res=10):
def simulate_day(census, precip, cell_res=10, precolumbian=False):
"""
Simulate a day, including effects of modifications.
Expand All @@ -388,12 +386,10 @@ def fn(cell, cell_count):
split = cell.split(':')
if (len(split) == 2):
split.append('')
cell = '%s:%s:%s' % tuple(split[:3])

land_use = split[1]
bmp = split[2]
(_, land_use, bmp) = split
et = et_max * lookup_ki(bmp or land_use)

return simulate_cell_day(precip, et, cell, cell_count)

return simulate_modifications(census, fn, cell_res)
return simulate_modifications(census, fn, cell_res, precolumbian)
4 changes: 2 additions & 2 deletions tr55/tablelookup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"""

from tr55.tables import SAMPLE_YEAR, BMPS, BUILT_TYPES, LAND_USE_VALUES, \
PRE_COLUMBIAN_LAND_USES, POLLUTANTS, POLLUTION_LOADS
NON_NATURAL, POLLUTANTS, POLLUTION_LOADS


def lookup_ki(land_use):
Expand Down Expand Up @@ -75,7 +75,7 @@ def make_precolumbian(land_use):
"""
Project the given land use to a Pre-Columbian one.
"""
if land_use not in PRE_COLUMBIAN_LAND_USES:
if land_use in NON_NATURAL:
return 'mixed_forest'
else:
return land_use
Expand Down
7 changes: 2 additions & 5 deletions tr55/tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,11 +428,8 @@
'commercial', 'industrial', 'transportation',
'urban_grass'])

PRE_COLUMBIAN_LAND_USES = set([
'water',
'woody_wetland',
'herbaceous_wetland'
])
NON_NATURAL = set(['pasture', 'hay', 'row_crop', 'green_roof']) \
| set(['no_till']) | BMPS | BUILT_TYPES

# The set of pollutants that we are concerned with.
POLLUTANTS = set(['tn', 'tp', 'bod', 'tss'])
Expand Down

0 comments on commit 2ac761e

Please sign in to comment.