%load_ext autoreload
The autoreload extension is already loaded. To reload it, use: %reload_ext autoreload
Basic examples¶
This notebook demonstrates how to use the pySWATPlus
library to interact with SWAT+ models, modify input files, and run simulations. We'll cover the following steps:
- Loading the necessary libraries.
- Initializing the
TxtinoutReader
to interact with the SWAT+ model. - Reading and modifying input files (e.g.,
plants.plt
). - Running the SWAT+ simulation and analyzing results.
- Running multiple simulations in parallel.
Let’s get started!
1. Importing Required Libraries¶
First, we import the pySWATPlus
module.
from pySWATPlus import TxtinoutReader
2. Setting Up the SWAT+ Model¶
To work with a SWAT+ model, we need to specify the path to the txtinout
folder. This folder contains all the input files required for the simulation, including the SWAT+ executable.
# Path to the SWAT+ model folder
txtinout_folder = '/mnt/c/Users/joans/OneDrive/Escriptori/icra/muga_windows'
filename = 'plants.plt'
3. Initializing the TxtinoutReader¶
The TxtinoutReader
class is used to interact with the SWAT+ model. It allows us to read, modify, and run simulations.
# Initialize the TxtinoutReader
# Note: The folder must contain a .exe file to run the simulation.
reader = TxtinoutReader(txtinout_folder)
4. Reading Input Files¶
We can read specific input files, such as plants.plt
, using the register_file
method. This method returns a FileReader
object that allows us to manipulate the file.
# Read the 'plants.plt' file
plants_reader = reader.register_file(
filename,
has_units=False, # Indicates if the file has units information
)
# Display the header of the file
plants_reader.header_file
'plants.plt: written by SWAT+ editor v2.2.0 on 2023-09-25 11:52 for SWAT+ rev.60.5.4\n'
5. Exploring the Input Data¶
The plants.plt
file contains information about different plant species used in the SWAT+ model. Let’s take a look at the data.
# Display the first few rows of the 'plants.plt' file
plants_reader.df.head()
name | plnt_typ | gro_trig | nfix_co | days_mat | bm_e | harv_idx | lai_pot | frac_hu1 | lai_max1 | ... | rt_st_end | plnt_pop1 | frac_lai1 | plnt_pop2 | frac_lai2 | frac_sw_gro | aeration | wnd_dead | wnd_flat | description | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | agrc | cold_annual | temp_gro | 0.0 | 110.0 | 30.0 | 0.40 | 4.0 | 0.05 | 0.05 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.0 | 0.0 | 0.0 | agricultural_land_close_grown |
1 | agrl | warm_annual | temp_gro | 0.0 | 110.0 | 100.0 | 0.45 | 3.0 | 0.15 | 0.05 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.0 | 0.0 | 0.0 | agricultural_land_generic |
2 | agrr | warm_annual | temp_gro | 0.0 | 110.0 | 39.0 | 0.50 | 3.0 | 0.15 | 0.05 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.0 | 0.0 | 0.0 | agricultural_land_row |
3 | alfa | perennial | temp_gro | 0.5 | 0.0 | 28.0 | 0.90 | 5.0 | 0.15 | 0.01 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.0 | 0.0 | 0.0 | alfalfa |
4 | almd | perennial | temp_gro | 0.0 | 0.0 | 110.0 | 0.05 | 1.2 | 0.05 | 0.05 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.0 | 0.0 | 0.0 | almond |
5 rows × 54 columns
6. Modifying Input Parameters¶
We can modify the input parameters directly in the DataFrame. For example, let’s change the bm_e
(biomass-energy ratio) for the agrc
plant.
# Modify the 'bm_e' value for the 'agrc' plant
plants_reader.df.loc['agrc', 'bm_e'] = 50
# Save the changes back to the file
plants_reader.overwrite_file()
#show the updated DataFrame
plants_reader.df.head()
name | plnt_typ | gro_trig | nfix_co | days_mat | bm_e | harv_idx | lai_pot | frac_hu1 | lai_max1 | ... | rt_st_end | plnt_pop1 | frac_lai1 | plnt_pop2 | frac_lai2 | frac_sw_gro | aeration | wnd_dead | wnd_flat | description | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | agrc | cold_annual | temp_gro | 0.0 | 110.0 | 30.0 | 0.40 | 4.0 | 0.05 | 0.05 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.0 | 0.0 | 0.0 | agricultural_land_close_grown |
1 | agrl | warm_annual | temp_gro | 0.0 | 110.0 | 100.0 | 0.45 | 3.0 | 0.15 | 0.05 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.0 | 0.0 | 0.0 | agricultural_land_generic |
2 | agrr | warm_annual | temp_gro | 0.0 | 110.0 | 39.0 | 0.50 | 3.0 | 0.15 | 0.05 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.0 | 0.0 | 0.0 | agricultural_land_row |
3 | alfa | perennial | temp_gro | 0.5 | 0.0 | 28.0 | 0.90 | 5.0 | 0.15 | 0.01 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.0 | 0.0 | 0.0 | alfalfa |
4 | almd | perennial | temp_gro | 0.0 | 0.0 | 110.0 | 0.05 | 1.2 | 0.05 | 0.05 | ... | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.5 | 0.0 | 0.0 | 0.0 | almond |
5 rows × 54 columns
7. Running the SWAT+ Simulation¶
Now that we’ve modified the input file, let’s run the SWAT+ simulation. We can also specify additional parameters, such as the simulation period and warmup period.
# Set the beginning and end years for the simulation
reader.set_beginning_and_end_year(2009, 2011)
# Set the warmup period (in years)
reader.set_warmup(1)
# Enable the 'channel_sd' object in the 'print.prt' file for daily output
reader.enable_object_in_print_prt(obj='channel_sd', daily=True, monthly=False, yearly=False, avann=False)
#run the simulation
reader.run_swat()
PosixPath('/mnt/c/Users/joans/OneDrive/Escriptori/icra/muga_windows')
8. Reading Simulation Results¶
After running the simulation, we can read the results from the output files. For example, let’s read the daily channel output (channel_sd_day.txt
).
# Read the daily channel output
channel_sd = reader.register_file('channel_sd_day.txt', has_units=True)
# Display the first few rows of the results
channel_sd.df.head()
jday | mon | day | yr | unit | gis_id name | area | precip | evap | seep | ... | cbod_out | dox_out | san_out | sil_out | cla_out | sag_out | lag_out | grv_out | null.2 | water_temp | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1.0 | 1.0 | 1.0 | 2010.0 | 1.0 | 1 cha01 | 0.2236E+02 | 0.1521E+04 | 0.1193E+03 | 0.2236E+03 | ... | 0.8638E+03 | 0.2762E+03 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 10.74 | 0.1074E+02 |
1 | 1.0 | 1.0 | 1.0 | 2010.0 | 2.0 | 2 cha02 | 0.2466E+02 | 0.6164E+03 | 0.1321E+03 | 0.2466E+03 | ... | 0.1511E+01 | 0.3294E+02 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 10.70 | 0.1074E+02 |
2 | 1.0 | 1.0 | 1.0 | 2010.0 | 3.0 | 3 cha03 | 0.2618E+01 | 0.1335E+03 | 0.1414E+02 | 0.2618E+02 | ... | 0.5492E+01 | 0.2366E+02 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 10.53 | 0.1074E+02 |
3 | 1.0 | 1.0 | 1.0 | 2010.0 | 4.0 | 4 cha04 | 0.2999E+01 | 0.7496E+02 | 0.1606E+02 | 0.2999E+02 | ... | 0.1831E+01 | 0.2307E+02 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 10.70 | 0.1074E+02 |
4 | 1.0 | 1.0 | 1.0 | 2010.0 | 5.0 | 5 cha05 | 0.8043E+01 | 0.1609E+03 | 0.4347E+02 | 0.8043E+02 | ... | 0.8453E+02 | 0.7442E+01 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 0.0000E+00 | 12.29 | 0.1074E+02 |
5 rows × 65 columns
9. Running SWAT+ in place with Specific Modifications¶
Instead of manually editing SWAT+ input files, you can specify parameter modifications using a structured params
dictionary when calling reader.run_swat()
. This is ideal for automating tests or running scenarios with slight input variations.
The params
dictionary follows this nested structure:
params = {
"<input_file>": {
"has_units": bool, # Optional. Whether the file has units information (default if False)
"<parameter_name>": [ # One or more changes to apply to the parameter
{
"value": float, # New value to assign
"change_type": str, # (Optional) One of: 'absval' (default), 'abschg', 'pctchg'
"filter_by": str # (Optional) pandas `.query()` filter string to select rows
},
# ... more changes for this parameter
]
},
# ... more input files
}
# Run the simulation with specific modifications
params = {
'plants.plt': {
'has_units': False,
'bm_e': [
{'value': 100, 'change_type': 'absval', 'filter_by': 'name == "agrl"'},
{'value': 110, 'change_type': 'absval', 'filter_by': 'name == "almd"'},
],
}
}
reader.run_swat(params)
PosixPath('/mnt/c/Users/joans/OneDrive/Escriptori/icra/muga_windows')
10. Running SWAT+ in a different folder¶
Sometimes, it's useful to run SWAT+ from a separate working directory—for example, when testing different parameter sets or isolating runs. You can do this by specifying a destination folder using run_swat_in_other_dir()
.
This method will:
- Copy the current SWAT+ project files into the target directory.
- Apply any parameter modifications (if provided).
- Run the simulation in that folder.
# Run the simulation with specific modifications
params = {
'plants.plt': {
'has_units': False,
'bm_e': [
{'value': 100, 'change_type': 'absval', 'filter_by': 'name == "agrl"'},
{'value': 110, 'change_type': 'absval', 'filter_by': 'name == "almd"'},
],
}
}
import tempfile
with tempfile.TemporaryDirectory() as tmp_dir:
simulation = reader.run_swat_in_other_dir(
target_dir=tmp_dir,
params=params
)
reader.run_swat(params)
11. Running Multiple Simulations in Parallel¶
If you need to run multiple simulations with different parameters, you can use the run_parallel_swat
method. This allows you to run simulations in parallel using multiple threads or processes.
import multiprocessing as mp
# Define multiple parameter sets
param_sets = [
{
'plants.plt': {
'has_units': False,
'bm_e': [
{'value': 100, 'change_type': 'absval', 'filter_by': 'name == "agrl"'},
{'value': 110, 'change_type': 'absval', 'filter_by': 'name == "almd"'},
],
}
},
{
'plants.plt': {
'has_units': False,
'bm_e': [
{'value': 120, 'change_type': 'absval', 'filter_by': 'name == "agrl"'},
{'value': 130, 'change_type': 'absval', 'filter_by': 'name == "almd"'},
],
}
},
]
def run_simulation(params):
with tempfile.TemporaryDirectory() as tmp_dir:
reader = TxtinoutReader(txtinout_folder)
return reader.run_swat_in_other_dir(
target_dir=tmp_dir,
params=params
)
# Run simulations in parallel
with mp.Pool(processes=2) as pool:
results = pool.map(run_simulation, param_sets)
# Print output directories
for path in results:
print("Simulation run in:", path)
Simulation run in: /tmp/tmpfaw1dgc6 Simulation run in: /tmp/tmp1r1951kq