Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let simulate_day do Pre-Columbian calculation. #37

Merged
merged 2 commits into from
Aug 10, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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