Skip to content

Commit 7d4ce76

Browse files
authored
Merge pull request #66 from debpal/main
Pull request summary
2 parents bd5b747 + 049dbf2 commit 7d4ce76

14 files changed

Lines changed: 2229 additions & 254 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
## ✨ Key Features
3737

38-
- Run `SWAT+` simulations by modifying model parameters through the `calibration.cal` file..
38+
- Run `SWAT+` simulations by modifying model parameters through the `calibration.cal` file.
3939
- Evaluate model performance against observed data using widely recognized statistical indicators.
4040
- Perform sensitivity analysis on model parameters using the [`SALib`](https://github.com/SALib/SALib) Python package.
4141
- Calibrate model parameters through multi-objective optimization and evolutionary algorithms using the [`pymoo`](https://github.com/anyoptimization/pymoo) Python package.

docs/changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
- Added the `pySWATPlus.SensitivityAnalyzer.simulation_and_indices` method to compute sensitivity indices directly against observed data without saving detailed simulation results.
88

9+
- Added the `pySWATPlus.PerformanceMetrics.indicator_from_file` method to compute performance metrics directly for simulated and observed files.
10+
911

1012
## Version 1.2.0 (October 13, 2025)
1113

docs/userguide/data_analysis.md

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
This section explains how to analysis data generated by different interfaces.
44

5-
## Time Series Data
5+
## Simulation Data
6+
7+
This section analyzes the simulation data generated using the
8+
[`run_swat`](https://swat-model.github.io/pySWATPlus/api/txtinout_reader/#pySWATPlus.TxtinoutReader.run_swat) method.
9+
10+
### Extract Time Series Data
611

712
A standard `SWAT+` simulation generates TXT files with time series columns: `day`, `mon`, and `yr` for day, month, and year, respectively.
813
The following method creates a time series `DataFrame` that includes a new `date` column with `datetime.date` objects and save the resulting DataFrame to a JSON file.
@@ -22,13 +27,33 @@ output = pySWATPlus.DataManager().simulated_timeseries_df(
2227
)
2328
```
2429

25-
## Statistics from Daily Time Series
30+
### Model Performance
31+
32+
Model performance can be evaluated by comparing simulated outputs with observed data, using selected indicators available in the property [`indicator_names`](https://swatmodel.github.io/pySWATPlus/api/performance_metrics/#pySWATPlus.PerformanceMetrics.indicator_names).
33+
34+
35+
```python
36+
output = pySWATPlus.PerformanceMetrics().indicator_from_file(
37+
sim_file=r"C:\Users\Username\custom_folder\channel_sd_day.txt",
38+
sim_col='flo_out',
39+
extract_sim={
40+
'has_units': True,
41+
'apply_filter': {'name': ['cha561']}
42+
},
43+
obs_file=r"C:\Users\Username\observed_folder\discharge_daily.csv",
44+
date_format='%Y-%m-%d',
45+
obs_col='discharge',
46+
indicators=['NSE', 'KGE', 'RMSE']
47+
)
48+
```
49+
50+
### Statistics from Daily Time Series
2651

2752
Monthly and yearly statistics such as maximum, minimum, mean, and standard deviation derived from daily time series data help summarize and interpret simulation variability over time.
2853
The following interface computes monthly and yearly statistical summaries for a Hydrological Response Unit (HRU) based on daily simulated flow discharge data. These metrics provide insight into seasonal patterns, flow extremes, and overall hydrological stability.
2954

3055
```python
31-
output = pySWATPlus.DataManager().simulated_timeseries_df(
56+
output = pySWATPlus.DataManager().hru_stats_from_daily_simulation(
3257
sim_file=r"C:\Users\Username\custom_folder\channel_sd_day.txt",
3358
has_units=True,
3459
gis_id=561,
@@ -37,7 +62,11 @@ output = pySWATPlus.DataManager().simulated_timeseries_df(
3762
)
3863
```
3964

40-
## Read Sensitivity Simulation Data
65+
## Sensitivity Simulation Scenarios
66+
67+
This section analyzes the time series scenarios generated from the sensitivity analysis based on sampled parameters.
68+
69+
### Read Time Series Scenarios
4170

4271
The sensitivity analysis performed using the
4372
[`simulation_by_sample_parameters`](https://swat-model.github.io/pySWATPlus/api/sensitivity_analyzer/#pySWATPlus.SensitivityAnalyzer.simulation_by_sample_parameters) method generates a file named `sensitivity_simulation.json`. This JSON file contains all the information required for sensitivity analysis, including:
@@ -46,7 +75,7 @@ The sensitivity analysis performed using the
4675
- `sample`: List of generated samples
4776
- `simulation`: Simulated `DataFrame` corresponding to each sample
4877

49-
To retrieve the selected `DataFrame` for all scenarios, use:
78+
To retrieve the selected time series `DataFrame` for all scenarios, use:
5079

5180
```python
5281
output = pySWATPlus.DataManager().read_sensitive_dfs(
@@ -57,31 +86,23 @@ output = pySWATPlus.DataManager().read_sensitive_dfs(
5786
)
5887
```
5988

60-
## Scenario Metrics
61-
62-
For a selected `DataFrame`, scenario metrics across all simulations can be computed by comparing model outputs with observed data.
89+
### Scenario Performance
6390

91+
For a selected `DataFrame`, the performance of all sensitivity scenarios can be assessed by comparing simulated outputs with observed data, using selected indicators available in the property [`indicator_names`](https://swatmodel.github.io/pySWATPlus/api/performance_metrics/#pySWATPlus.PerformanceMetrics.indicator_names).
6492

65-
- To get the mapping between available indicators and their abbreviations:
6693

67-
```python
68-
indicators = pySWATPlus.PerformanceMetrics().indicator_names
69-
```
70-
71-
- To compute metrics for all scenarios using the desired indicators:
72-
73-
```python
74-
output = pySWATPlus.SensitivityAnalyzer().scenario_indicators(
75-
sensim_file=r"C:\Users\Username\simulation_folder\sensitivity_simulation.json",
76-
df_name='channel_sd_mon_df',
77-
sim_col='flo_out',
78-
obs_file=r"C:\Users\Username\observed_folder\discharge_monthly.csv",
79-
date_format='%Y-%m-%d',
80-
obs_col='discharge',
81-
indicators=['NSE', 'MSE'],
82-
json_file=r"C:\Users\Username\data_analysis\performance_metrics.json"
83-
)
84-
```
94+
```python
95+
output = pySWATPlus.SensitivityAnalyzer().scenario_indicators(
96+
sensim_file=r"C:\Users\Username\simulation_folder\sensitivity_simulation.json",
97+
df_name='channel_sd_mon_df',
98+
sim_col='flo_out',
99+
obs_file=r"C:\Users\Username\observed_folder\discharge_monthly.csv",
100+
date_format='%Y-%m-%d',
101+
obs_col='discharge',
102+
indicators=['NSE', 'MSE'],
103+
json_file=r"C:\Users\Username\data_analysis\performance_metrics.json"
104+
)
105+
```
85106

86107

87108

docs/userguide/sensitivity_interface.md

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,14 @@ Currently, it supports [Sobol](https://doi.org/10.1016/S0378-4754(00)00270-6) sa
6161
- Structured export of results for downstream analysis
6262

6363

64+
!!! warning
65+
Before executing the following high-level interafce, comment out the code shown in
66+
[`Configuration Settings`](https://swat-model.github.io/pySWATPlus/userguide/sensitivity_interface/#configuration-settings)
67+
to avoid overwriting configuration files that may occur when rerunning the setup, which could affect the target results.
68+
69+
6470
```python
65-
# Sensitivity parameter space
71+
# Define parameter space
6672
parameters = [
6773
{
6874
'name': 'esco',
@@ -78,7 +84,7 @@ parameters = [
7884
}
7985
]
8086

81-
# Target data extraction from sensitivity simulation
87+
# Configuration of data extraction
8288
extract_data = {
8389
'channel_sdmorph_yr.txt': {
8490
'has_units': True,
@@ -108,12 +114,6 @@ if __name__ == '__main__':
108114
print(output)
109115
```
110116

111-
112-
!!! tip "Troubleshooting Parallel Processing Errors"
113-
If you encounter an error related to `concurrent.futures.ProcessPoolExecutor` and `multiprocessing` without a clear description,
114-
try closing the current command terminal and restarting it. This issue can occasionally occur due to lingering background processes
115-
or locked resources from previous runs.
116-
117117
## Sensitivity Indices
118118

119119
Sensitivity indices (first, second, and total orders) are computed using the indicators available in the `pySWATPlus.PerformanceMetrics` class, along with their confidence intervals.
@@ -135,7 +135,7 @@ output = pySWATPlus.SensitivityAnalyzer().parameter_sensitivity_indices(
135135

136136
## Integrated Simulation and Sensitivity
137137

138-
To compute sensitivity indices directly for multiple outputs against observed data and skip saving the detailed simulation results, use the following interface:
138+
To compute sensitivity indices for multiple outputs against observed data without saving detailed simulation time series for each parameter sample, use the following interface.
139139

140140

141141
```python
@@ -156,7 +156,7 @@ parameters = [
156156
]
157157

158158

159-
# Extract data configuration
159+
# Configuration of simulation data extraction
160160
extract_data = {
161161
'channel_sd_day.txt': {
162162
'has_units': True,
@@ -169,7 +169,7 @@ extract_data = {
169169
}
170170
}
171171

172-
# Observe data configuration
172+
# Configuration of observes data
173173
observe_data = {
174174
'channel_sd_day.txt': {
175175
'obs_file': r"C:\Users\Username\observed_folder\discharge_daily.csv",
@@ -181,7 +181,7 @@ observe_data = {
181181
}
182182
}
183183

184-
# Metric configuration
184+
# Configuration of performance metrics
185185
metric_config = {
186186
'channel_sd_day.txt': {
187187
'sim_col': 'flo_out',
@@ -200,11 +200,18 @@ if __name__ == '__main__':
200200
output = pySWATPlus.SensitivityAnalyzer().simulation_and_indices(
201201
parameters=parameters,
202202
sample_number=1,
203-
sensim_dir=r"C:\Users\dpal22\Desktop\swat_run\experiment_dominant_hru\empty_dir",
204-
txtinout_dir=r"C:\Users\dpal22\Desktop\swat_run\experiment_dominant_hru\txtinout_copy",
203+
sensim_dir=sim_dir,
204+
txtinout_dir=r"C:\Users\Username\custom_folder",
205205
extract_data=extract_data,
206206
observe_data=observe_data,
207207
metric_config=metric_config
208208
)
209209
print(output)
210-
```
210+
```
211+
212+
213+
214+
!!! tip "Troubleshooting Parallel Processing Errors"
215+
If you encounter an error related to `concurrent.futures.ProcessPoolExecutor` and `multiprocessing` without a clear description,
216+
try closing the current command terminal and restarting it. This issue can occasionally occur due to lingering background processes
217+
or locked resources from previous runs.

docs/userguide/swatplus_simulation.md

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
To run a `SWAT+` simulation, the `TxtInOut` folder must include all required input files, which are created during simulation setup in the [SWAT+ Editor](https://github.com/swat-model/swatplus-editor) via `QSWAT+`.
66

7-
Additionally, the `TxtInOut` folder must contain the `SWAT+` executable. If you need help locating it, open the **`Run SWAT+`** tab in the `SWAT+ Editor` to find its path. Once located, copy the executable file into the `TxtInOut` folder. If the executable is missing, the simulation will fail.
7+
Additionally, the `TxtInOut` folder must contain the `SWAT+` executable. The executable file compatible with your operating system and `SWAT+` version can be obtained from the [official GitHub releases](https://github.com/swat-model/swatplus/releases).
88

99
Once the `TxtInOut` folder is properly configured with the necessary input files and the `SWAT+` executable, you can initialize the `TxtinoutReader` class to interact with the `SWAT+` model:
1010

@@ -14,6 +14,7 @@ import pySWATPlus
1414
# Replace this with the path to your project's TxtInOut folder
1515
txtinout_dir = r"C:\Users\Username\project\Scenarios\Default\TxtInOut"
1616

17+
# Initialize TxtinoutReader class
1718
txtinout_reader = pySWATPlus.TxtinoutReader(
1819
tio_dir=txtinout_dir
1920
)
@@ -142,31 +143,30 @@ The following steps demonstrate how to configure parameters in a custom director
142143
Instead of performing each step separately as explained above, you can run a `SWAT+` simulation in a separate empty directory by configuring all options at once using a single function:
143144

144145
```python
145-
# Configure modified parameters
146-
parameters = [
147-
{
148-
'name': 'esco',
149-
'change_type': 'absval',
150-
'value': 0.3
151-
},
152-
{
153-
'name': 'perco',
154-
'change_type': 'absval',
155-
'value': 0.6
156-
}
157-
]
158-
159-
# Run SWAT+ simulation from the original `TxtInOut` folder
146+
# Run SWAT+ simulation to a custom directory
160147
txtinout_reader.run_swat(
161-
sim_dir=r"C:\Users\Username\custom_folder", # mandatory
162-
parameters=parameters, # optional
163-
begin_date='01-Jan-2012', # optional
164-
end_date= '31-Dec-2016', # optional
165-
simulation_timestep=0, # optional
166-
warmup=1, # optional
167-
print_prt_control={'channel_sd': {'daily': False}}, # optional
168-
print_begin_date='15-Jun-2012', # optional
169-
print_end_date='15-Jun-2016', # optional
170-
print_interval=1 # optional
148+
sim_dir=r"C:\Users\Username\empty_folder", # directory (optional)
149+
parameters=[
150+
{
151+
'name': 'esco',
152+
'change_type': 'absval',
153+
'value': 0.3
154+
},
155+
{
156+
'name': 'perco',
157+
'change_type': 'absval',
158+
'value': 0.6
159+
}
160+
], # modified parameter value (optional)
161+
begin_date='01-Jan-2012', # simulation begin date (optional)
162+
end_date= '31-Dec-2016', # simulation end date (optional)
163+
simulation_timestep=0, # simulation time step (optional)
164+
warmup=1, # warm-up years (optional)
165+
print_prt_control={
166+
'channel_sd': {'daily': False}
167+
}, # control print.prt file (optional)
168+
print_begin_date='15-Jun-2012', # begin date to print output (optional)
169+
print_end_date='15-Jun-2016', # end date to print output (optional)
170+
print_interval=1 # interval to print output (optional)
171171
)
172172
```

pySWATPlus/calibration.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@
1818

1919
class Calibration(pymoo.core.problem.Problem): # type: ignore[misc]
2020
'''
21-
Warning:
22-
This class is currently under development.
23-
2421
A `Problem` subclass from the [`pymoo`](https://github.com/anyoptimization/pymoo) Python package
2522
for performing model calibration against observed data using multi-objective optimization
2623
and evolutionary algorithms.
@@ -376,17 +373,14 @@ def _evaluate(
376373
df=merge_df[['sim', 'obs']],
377374
norm_col='obs'
378375
)
379-
# Indicator method from abbreviation
376+
# Indicator abbreviation
380377
obj_ind = self.objective_config[obj]['indicator']
381-
indicator_method = getattr(
382-
PerformanceMetrics(),
383-
f'compute_{obj_ind.lower()}'
384-
)
385-
# Indicator value computed from method
386-
ind_val = indicator_method(
378+
# Indicator value
379+
ind_val = PerformanceMetrics().compute_from_abbr(
387380
df=norm_df,
388381
sim_col='sim',
389-
obs_col='obs'
382+
obs_col='obs',
383+
indicator=obj_ind
390384
)
391385
# Objective value based on maximize or minimize direction
392386
obj_val = - ind_val if objs_dirs[obj_ind] == 'max' else ind_val

0 commit comments

Comments
 (0)