9.5. Tables for Subsurface Parameters¶
9.5.1. Introduction¶
ParFlow domains with complex geology often involve many lines in the input script, which lengthens the script and makes it more cumbersome to navigate. Python PFTools makes it easy to do the following:
Load in a table of your subsurface properties
Export a table of the subsurface properties
Load a database of common soil and geologic properties to set up your domain
9.5.2. Usage of SubsurfacePropertiesBuilder
¶
First, we’ll show some usage examples of loading tables of parameters within a ParFlow Python script:
from parflow import Run
from parflow.tools.builders import SubsurfacePropertiesBuilder
table_test = Run("table_test", __file__)
table_test.GeomInput.Names = 'box_input indi_input'
table_test.GeomInput.indi_input.InputType = 'IndicatorField'
table_test.GeomInput.indi_input.GeomNames = 's1 s2 s3 s4 g1 g2 g3 g4'
# First example: table as in-line text
soil_properties = '''
# ----------------------------------------------------------------
# Sample header
# ----------------------------------------------------------------
key Perm Porosity RelPermAlpha RelPermN SatAlpha SatN SatSRes SatSSat
s1 0.26 0.375 3.548 4.162 3.548 4.162 0.000001 1
s2 0.04 - 3.467 2.738 - 2.738 0.000001 1
s3 0.01 0.387 2.692 2.445 2.692 2.445 0.000001 1
s4 0.01 0.439 - 2.659 0.501 2.659 0.000001 1
'''
# Creating object and assigning the subsurface properties
SubsurfacePropertiesBuilder(table_test) \
.load_txt_content(soil_properties) \ # loading the in-line text
.load_csv_file('geologic_properties_123.csv') \ # loading external csv file
.assign('g3', 'g4') \ # assigns properties of unit 'g3' to unit 'g4'
.apply() \ # setting keys based on loaded properties
.print_as_table() # printing table of loaded properties
At the top of the script, we import the SubsurfacePropertiesBuilder
class from parflow.tools.builders
. Then,
after specifying the GeomInput.indi_input.GeomNames
, we include a table for the soil properties as an in-line text variable.
Note that the GeomInput.indi_input.GeomNames
are in the first column (key
), and the parameter names are across the top.
These parameter names don’t have to match the key names exactly, but they have to be similar. We will explain this later.
We load the soil_properties
text by calling the load_txt_content
method on the SubsurfacePropertiesBuilder
object.
To load the geologic properties for the geometric units g1, g2, and g3, we call load_csv_file
to load an external csv file.
That now leaves one unit, g4, that needs properties. We use the assign
method to assign properties to unit g4 from the
properties of unit g3. Now that all the geometric units have properties, we call apply
to set the appropriate keys.
The print_as_table
method prints out the subsurface properties for each unit. Executing this example will result in a table that looks something like this:
key Perm Porosity RelPermAlpha RelPermN SatAlpha SatN SRes SSat
s1 0.26 0.375 3.548 4.162 3.548 4.162 1e-06 1.0
s2 0.04 - 3.467 2.738 - 2.738 1e-06 1.0
s3 0.01 0.387 2.692 2.445 2.692 2.445 1e-06 1.0
s4 0.01 0.439 - 2.659 0.501 2.659 1e-06 1.0
g1 0.26 0.375 3.548 4.162 3.548 4.162 1e-06 1.0
g2 0.04 - 3.467 2.738 - 2.738 1e-06 1.0
g3 0.01 0.387 2.692 2.445 2.692 2.445 1e-06 1.0
g4 0.01 0.387 2.692 2.445 2.692 2.445 1e-06 1.0
9.5.3. Table formatting for importing¶
Let’s have another look at the in-line table from the usage example above:
soil_properties = '''
# ----------------------------------------------------------------
# Sample header
# ----------------------------------------------------------------
key Perm Porosity RelPermAlpha RelPermN SatAlpha SatN SatSRes SatSSat
s1 0.26 0.375 3.548 4.162 3.548 4.162 0.000001 1
s2 0.04 - 3.467 2.738 - 2.738 0.000001 1
s3 0.01 0.387 2.692 2.445 2.692 2.445 0.000001 1
s4 0.01 0.439 - 2.659 0.501 2.659 0.000001 1
'''
These tables can be formatted in a number of different ways. Here are several considerations:
Any blank rows or rows beginning with
#
are ignored in processing.Delimiters can be either commas or spaces.
Table orientation does not matter (i.e., whether the field names are across the first row or down the first column). The only requirement is for that the top-left entry be
key
or one of its aliases.The table does not have to be completely filled. As shown here, blank property values must be designated by a hyphen.
To properly process the table and map to the correct keys, the field names (including
key
) must be one of several possible aliases. The aliases are listed in this yaml file that is included in the Python PFTools. These aliases include the exact end of the key name (e.g.,Perm.Value
as opposed to the aliasPerm
), so when in doubt, you can use the exact name.
9.5.4. Default database loading¶
We have added several databases of commonly used parameters for different soil and geologic units to provide some helpful guidance. To load these database, you can simply call the load_default_properties
method on the SubsurfacePropertiesBuilder
object.
The available databases in the Python PFTools package can be found in the “subsurface_*.txt” files here.
You can load any of the databases into your SubsurfacePropertiesBuilder
object by passing in the database
argument, which is the latter part of the database file name (e.g. “subsurface_conus_1.txt” can be loaded by calling load_default_properties('conus_1')
).
The default database is from Maxwell and Condon (2016). Note that the parameters in the databases are all in the default ParFlow units of meters and hours.
Below is an example of how to use the default database importer on the Run
object db_test
:
# setting GeomNames
db_test.GeomInput.Names = 'box_input indi_input'
db_test.GeomInput.box_input.InputType = 'Box'
db_test.GeomInput.box_input.GeomName = 'domain'
db_test.GeomInput.indi_input.InputType = 'IndicatorField'
db_test.GeomInput.indi_input.GeomNames = 's1 s2 g2'
# setting dictionary for mapping from database properties (i.e. the keys of map_dict)
# to the different subsurface units (i.e. the values of map_dict)
map_dict = {
'bedrock_1': ['domain', 'g2'],
'sand': 's1',
'loamy_sand': 's2'
}
SubsurfacePropertiesBuilder(db_test)\
.load_default_properties() \
.assign(mapping=map_dict) \
.apply() \
.print_as_table()
The dictionary map_dict
maps the database properties to the subsurface units in your Run
object.
Note that the properties from the database unit bedrock_1
are applied to both domain
and g2
. If you are assigning a database unit to multiple GeomNames
, these must be input as a list, as shown.
This will print the following:
key Perm Porosity SRes RelPermAlpha RelPermN
domain 0.005 0.33 0.001 1.0 3.0
g2 0.005 0.33 0.001 1.0 3.0
s1 0.269 0.38 0.14 3.55 4.16
s2 0.0436 0.39 1.26 3.47 2.74
9.5.5. Full API for SubsurfacePropertiesBuilder
¶
SubsurfacePropertiesBuilder(run=None)
: Instantiates aSubsurfacePropertiesBuilder
object. If the optionalRun
objectrun
is given, it will use the subsurface units inrun
for later application.run
must be provided as an argument either here or when calling theapply()
method (see below).load_csv_file(tableFile, encoding='utf-8-sig')
: Loads a comma-separated (csv) file to yourSubsurfacePropertiesBuilder
object. The default text encoding format isutf-8-sig
, which should translate files generated from Microsoft Excel.load_txt_file(tableFile, encoding='utf-8-sig')
: Loads a text file to yourSubsurfacePropertiesBuilder
object. The default text encoding format isutf-8-sig
.load_txt_content(txt_content)
: Loads in-line text to yourSubsurfacePropertiesBuilder
object.load_default_properties(database='conus_1')
: Loads one of several databases of subsurface properties. The default,conus_1
, is from Maxwell and Condon (2016).assign(old=None, new=None, mapping=None)
: Assigns properties to thenew
subsurface unit using the properties from theold
subsurface unit. Alternatively, a dictionary (mapping
) can be passed in as an argument, which should have the keys as theold
units, and the values as thenew
units. If anold
unit will apply to multiplenew
units, thenew
units need to be passed in as a list.apply(run=None, name_registration=True)
: Applies the loaded subsurface properties to the subsurface units in theRun
objectrun
. Ifrun
is not provided here, the user must provide therun
argument when instantiating theSubsurfacePropertiesBuilder``object. If ``name_registration
is set toTrue
, it will add the subsurface unit names (e.g., s1, s2 from the example above) to the list of unit names for each property (e.g., settingGeom.Perm.Names = 's1 s2 s3 s4'
), and set theaddon
keys not associated with a specific unit (e.g.,Phase.RelPerm.Type
).print()
: Prints out the subsurface parameters for all subsurface units in a hierarchical format.print_as_table(props_in_header=True, column_separator=' ')
: Prints out the subsurface parameters for all subsurface units in a table format.props_in_header
will print the table with the property names as column headings if set toTrue
, or as row headings if set toFalse
.
9.5.6. Exporting subsurface properties¶
It is often useful to have a table of the subsurface properties assigned to various subsurface units during a run. As mentioned in the run script API,
you can write out a table of the subsurface properties by calling the write_subsurface_table
method on your Run
object.
For example, try adding the following line just above the run()
method call in the default_richards.py
Python test, with the name of the output file passed in as the file_name
argument:
drich.write_subsurface_table(file_name='def_richards_subsurf.txt')
If you do not provide file_name
, the default file will be a .csv file with the name of your run and subsurface. In this case, the default file would be default_richards_subsurface.csv.
Execute the Python script, and you should see the output file def_richards_subsurf.txt containing the following:
key Perm Porosity SpecStorage RelPermAlpha RelPermN SatAlpha SatN SRes SSat
domain - - 0.0001 0.005 2.0 0.005 2.0 0.2 0.99
background 4.0 1.0 - - - - - - -
See that it only prints out the properties that are explicitly assigned to each of the subsurface units domain
and background
.
9.5.7. Examples¶
Full examples of the SubsurfacePropertiesBuilder
can be found in the new_features subdirectory of the ParFlow Python tests.
default_db: Loading the default database and mapping the database units to subsurface units in the current run.
tables_LW: Showing multiple ways to load tables to replace the subsurface property definition keys in the Little Washita test script.