%load_ext autoreload
SWAT+ Model Simulation with pySWATPlus
¶
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
index='name' # Use the 'name' column as the index
)
# Display the header of the file
plants_reader.header_file
'plants.plt: written by SWAT+ editor v2.2.0 on 2023-09-25 11:54 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 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
agrc | agrc | cold_annual | temp_gro | 0.0 | 110.0 | 40.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 |
agrl | agrl | warm_annual | temp_gro | 0.0 | 110.0 | 50.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 |
agrr | agrr | warm_annual | temp_gro | 0.0 | 110.0 | 50.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 |
alfa | alfa | perennial | temp_gro | 0.5 | 0.0 | 50.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 |
almd | almd | perennial | temp_gro | 0.0 | 0.0 | 50.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'] = 40
# Save the changes back to the file
plants_reader.overwrite_file()
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)
Handling Simulation Output¶
To avoid cluttering the notebook with long simulation output:
- Set
show_output=False
to suppress all output. - Use
%%capture
to store the output and print only a portion (e.g., the first 20 lines).
%%capture captured_output
simulation_path = reader.run_swat(show_output=True)
# Print the first 20 lines of the output
print("Simulation Output (First 20 Lines):")
print("\n".join(captured_output.stdout.splitlines()[:20]))
Simulation Output (First 20 Lines): SWAT+ Revision 60.5.7 Soil & Water Assessment Tool PC Version Program reading . . . executing Date of Sim 3/23/2025 Time 19:59: 1 reading from precipitation file Time 19:59: 1 reading from temperature file Time 19:59: 1 reading from solar radiation file Time 19:59: 2 reading from relative humidity file Time 19:59: 2 reading from wind file Time 19:59: 2 reading from wgn file Time 19:59: 3 reading from wx station file Time 19:59: 3 Original Simulation 1 1 2009 Yr 1 of 3 Time 19:59: 4 Original Simulation 1 2 2009 Yr 1 of 3 Time 19:59: 4 Original Simulation 1 3 2009 Yr 1 of 3 Time 19:59: 4 Original Simulation 1 4 2009 Yr 1 of 3 Time 19:59: 5 Original Simulation 1 5 2009 Yr 1 of 3 Time 19:59: 5 Original Simulation 1 6 2009 Yr 1 of 3 Time 19:59: 5 Original Simulation 1 7 2009 Yr 1 of 3 Time 19:59: 5
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 | ... | 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.0 | cha01 | 22.360 | 1521.00 | 119.30 | ... | 869.100 | 278.100 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 10.74 | 10.74 |
1 | 1.0 | 1.0 | 1.0 | 2010.0 | 2.0 | 2.0 | cha02 | 24.660 | 616.40 | 132.10 | ... | 1.564 | 33.480 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 10.70 | 10.74 |
2 | 1.0 | 1.0 | 1.0 | 2010.0 | 3.0 | 3.0 | cha03 | 2.618 | 133.50 | 14.14 | ... | 5.037 | 23.630 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 10.53 | 10.74 |
3 | 1.0 | 1.0 | 1.0 | 2010.0 | 4.0 | 4.0 | cha04 | 2.999 | 74.96 | 16.06 | ... | 1.831 | 23.070 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 10.70 | 10.74 |
4 | 1.0 | 1.0 | 1.0 | 2010.0 | 5.0 | 5.0 | cha05 | 8.043 | 160.90 | 43.47 | ... | 84.530 | 7.442 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 12.29 | 10.74 |
5 rows × 66 columns
9. Running SWAT+ with Specific Modifications¶
Instead of modifying input files manually, you can directly specify the modifications when running the simulation. This is useful for quickly testing different scenarios.
Option 1: Modify Specific Values¶
You can specify the file, column, and value to modify. For example, let’s change the bm_e
value for the agrc
plant.
# Run the simulation with specific modifications
txtinout_path = reader.run_swat(
params={'plants.plt': ('name', [('agrc', 'bm_e', 40)])},
show_output=False
)
txtinout_path
PosixPath('/mnt/c/Users/joans/OneDrive/Escriptori/icra/muga_windows')
Option 2: Use Regular Expressions¶
You can also use regular expressions to select rows for modification. For example, let’s modify the bm_e
value for all plants whose names start with the letter "A".
# Run the simulation using a regular expression to select rows
txtinout_path = reader.run_swat(
params={'plants.plt': ('name', [(r'^A.*', 'bm_e', 40)])},
show_output=False
)
txtinout_path
PosixPath('/mnt/c/Users/joans/OneDrive/Escriptori/icra/muga_windows')
10. 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.
#You can also run multiple simulations in parallel
txtinout_paths = reader.run_parallel_swat(params = [{'plants.plt': ('name', [('bana', 'bm_e', 45)])}, {'plants.plt': ('name', [('bana', 'bm_e', 40)])}], n_workers = 2, parallelization='threads')
txtinout_paths
[PosixPath('/tmp/tmpa7a397py'), PosixPath('/tmp/tmpa227i7m4')]