Sensitivity Analysis
Sensitivity analysis helps quantify how variation in input parameters affects model outputs. This tutorial demonstrates how to perform sensitivity analysis on SWAT+ model parameters. Two approaches are provided: a custom, user-defined workflow and a high-level automated interface, both using the SALib Python package based on Sobol sampling from a defined parameter space.
Custom Workflow
This approach allows users to define the sampling strategy, number of simulations, and custom performance metrics. It is ideal for those seeking fine control over the sensitivity analysis process, tailored to specific research or operational goals. In this example, we focus on two parameters, epco
and esco
, located in the hydrology.hyd
file.
Import the necessary packages and initialize the TxtinoutReader
class.
# Import packages
import pySWATPlus
import SALib.sample.sobol
import SALib.analyze.sobol
import concurrent.futures
import numpy
import random
import tempfile
# Initialize the TxtinoutReader class
txtinout_reader = pySWATPlus.TxtinoutReader(
path=r"C:\Users\Username\project\Scenarios\Default\TxtInOut"
)
Optionally specify the simulation time period, warm-up years, enable outputs via the print.prt
file, and fix any parameter values not involved in the sensitivity analysis.
# Set simulation timeline (optional)
txtinout_reader.set_begin_and_end_year(begin=2010, end=2016)
# Set warm-up year (optional)
txtinout_reader.set_warmup_year(warmup=1)
# Enable output for channel_sd_day.txt (optional)
txtinout_reader.enable_object_in_print_prt(
obj='channel_sd',
daily=True,
monthly=False,
yearly=False,
avann=False
)
# Fix non-sensitive parameters (optional)
hyd_register = simulation_reader.register_file(
filename='hydrology.hyd',
has_units=False
)
hyd_df = hyd_register.df
hyd_df['perco'] = 0.1
hyd_register.overwrite_file()
Define an evaluation function that runs the SWAT+ model with the specified parameter values and returns an evaluation metric. The current example returns a random value for demonstration purposes. Replace this with a proper objective function using simulated and observed data.
def run_swat_and_evaluate_metric(
epco: float,
esco: float
):
print(f'Running SWAT with epco = {epco} and esco = {esco}')
print('\n')
# Construct 'params' dictionary
params = {
'hydrology.hyd': {
'has_units': False,
'epco': [
{'value': epco, 'change_type': 'absval'},
],
'esco': [
{'value': esco, 'change_type': 'absval'},
],
}
}
# Temporary directory creation
with tempfile.TemporaryDirectory() as tmp_dir:
# Run simulation
simulation_reader = txtinout_reader.run_swat_in_other_dir(
target_dir=tmp_dir,
params=params
)
# Read results
file_register = simulation_reader.register_file(
filename='channel_sdmorph_day.txt',
has_units=True
)
# Get DataFrame
file_df = file_register.df
# Return a random value (replace with real evaluation logic)
return random.random()
# Wrapper function for parallel execution
def evaluate(params):
return run_swat_and_evaluate_metric(*params)
Define the sensitivity problem and generate samples using Sobol
sampling.
problem = {
'num_vars': 2,
'names': ['epco', 'esco'],
'bounds': [[0, 1]] * 2
}
# Generate Sobol samples
param_values = SALib.sample.sobol.sample(problem, 2)
Run simulations in parallel and compute Sobol indices to analyze parameter influence.
if __name__ == '__main__':
with concurrent.futures.ProcessPoolExecutor() as executor:
simulation_output = numpy.array(list(executor.map(evaluate, param_values)))
sobol_indices = SALib.analyze.sobol.analyze(problem, simulation_output)
print('First-order Sobol indices:', sobol_indices['S1'])
print('Total-order Sobol indices:', sobol_indices['ST'])
High-Level Sobol-Based Sensitivity Simulation Interface
This high-level interface automates the sensitivity simulation workflow. It includes:
- Automatic generation of Sobol samples for the defined parameter space
- Parallel computation to accelerate simulation runs
- Output extraction from target data files with filtering options (by date, column values, etc.)
- Structured export of results for downstream analysis
The output data can be used to compute performance metrics, compare with observed data, and estimate Sobol indices to quantify parameter influence.
This interface is ideal for users seeking a scalable, low-configuration solution.
import pySWATPlus
if __name__ == '__main__':
output = pySWATPlus.Scenario().simulation_by_sobol_sample(
var_names=[
'esco',
'|'.join(['bm_e', 'name == "agrl"'])
],
var_bounds=[
[0, 1],
[30, 40]
],
sample_number=1,
simulation_folder=r"C:\Users\Username\simulation_folder",
txtinout_folder=r"C:\Users\Username\project\Scenarios\Default\TxtInOut",
params={
'hydrology.hyd': {
'has_units': False,
'esco': {'value': 0}
},
'plants.plt': {
'has_units': False,
'bm_e': {'value': 0, 'filter_by': 'name == "agrl"'}
}
},
data_file='channel_sd_yr.txt',
unit_row=True,
start_date='2012-01-01',
end_date='2015-12-31',
filter_rows={'name': ['cha561']},
retain_cols=['name', 'flo_out'],
max_workers=4,
clean_setup=False
)
⚠️ Note: Before using this high-level interface, configure the
TxtInOut
folder with the simulation time period, warm-up years,print.prt
file settings, and any fixed parameter values not involved in sensitivity analysis.