Add file.cio writer and processx-based SWAT+ model runner#1
Add file.cio writer and processx-based SWAT+ model runner#1
file.cio writer and processx-based SWAT+ model runner#1Conversation
… read support Co-authored-by: tadhg-moore <23136262+tadhg-moore@users.noreply.github.com>
|
@copilot I have updated the package name. Can you provide a a how-to example vignette to show how to use the package to set up a SWAT+ simulation? |
…audem/TauDEM Co-authored-by: tadhg-moore <23136262+tadhg-moore@users.noreply.github.com>
…ia traudem Co-authored-by: tadhg-moore <23136262+tadhg-moore@users.noreply.github.com>
Added the vignette in commit The vignette ( Option A – Delineate watershed ( Option B – Use existing watershed ( Both paths end up with the same Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
I have added example files which can be used, DEM file, channels, watershed and subbasins. @copilot can you update the tests using these examples. |
… in delineate.R Co-authored-by: tadhg-moore <23136262+tadhg-moore@users.noreply.github.com>
Done in commit Tier 1 (8 tests, always run) — synthetic mock data, no file I/O required. Tier 2 (6 new tests, require
Tier 3 (3 tests, skipped unless TauDEM is installed) — end-to-end Also fixed two field-name lookup gaps in |
|
@copilot how does the python api use the TauDEM? Can you show me where in the api code. |
The Python API does not use TauDEM. Watershed delineation is handled entirely outside the editor — by QSWAT+ (which internally calls TauDEM) or ArcSWAT+ — and the editor only imports the results. The relevant parts of the Python API are:
So the R package's |
Co-authored-by: tadhg-moore <23136262+tadhg-moore@users.noreply.github.com>
Fixed in commit
Tests: 4 unit tests for R is not available in this sandbox environment so I wasn't able to run the tests directly here — but the fixes cover all the error paths. If you're still seeing failures after pulling, please share the error output and I'll address them. |
… duplicate sourceid LSU and CH both have ID sequences starting at 1, so setting sourceid = source_object.id produced duplicate sourceid values violating the INTEGER PRIMARY KEY constraint. Changed the schema to use a composite PRIMARY KEY (sourceid, sourcecat) which allows the same sourceid in different categories while preserving the uniqueness of each routing row. All gis_import.R lookups already filter by sourcecat before using sourceid as a lookup key, so no query changes are needed. Tests added: - (sourceid, sourcecat) pairs are globally unique in .build_gis_routing() - sourceid alone is NOT unique (LSU + CH overlap) — regression guard - DB insert test validates rows can be inserted without constraint error Co-authored-by: tadhg-moore <23136262+tadhg-moore@users.noreply.github.com>
delineate_watershed()gis_routing PRIMARY KEY violation and emit_progress single-arg crash
|
Currently the use_existing_watershed() and delineate_watershed() return data frames of the tables in the database. Can they be returned as sf objects to allow for inspection by the user? @copilot |
…weather file headers - delineate_watershed() and use_existing_watershed() now accept return_sf = TRUE (default) and return sf objects (WGS84) for subbasins, channels, lsus, hrus, points — enabling direct use with sf and ggplot2 for inspection - Added .drop_geom() helper to strip sf geometry before DB insert so write_gis_to_db() still receives plain data.frames - Added keep_geometry = FALSE parameter to all read_*_shp() functions and read_swatplus_gis() for direct shapefile reading with geometry - import_weather() now parses SWAT+ station file headers (line 3: nbyr tstep lat lon elev) to populate weather_file with correct lat/lon coordinates (was always 0.0) - Creates weather_sta_cli entries from unique (lat,lon) combinations using weather_sta_name(), matching Python WeatherImport.create_weather_stations() - Links each station to its closest file per weather type, matching Python WeatherImport.match_files_to_stations() - Updated test-import_weather.R: fixed pre-existing broken test (missing create_project_db call), added assertions for lat/lon parsing and station creation - Updated test-delineate.R: replaced incorrect use_existing_watershed test (wrong expected names) with two new tests verifying sf return (default) and data.frame return (return_sf = FALSE) Co-authored-by: tadhg-moore <23136262+tadhg-moore@users.noreply.github.com>
Done in commit sf objects from Both functions now accept Under the hood a Weather station import from file headers
The bundled |
gis_routing PRIMARY KEY violation and emit_progress single-arg crashgis_routing PRIMARY KEY violation, emit_progress crash, sf spatial returns, and weather station import
…n gap
- Fix `.vectorise_watershed_raster()`: fill isolated NA cells in watershed
raster with terra::focal(modal) before polygonizing, removing thin strip
gaps that appeared along channel centrelines in the delineated subbasins
- Add R/hru_overlay.R with delineate_hrus():
* Overlays land-use and soil rasters on subbasin polygons to create real HRUs
(one per unique landuse × soil × slope combination per subbasin)
* Mirrors QSWAT+ HRU delineation: computes arsub/arlsu/arland/arso/arslp
area-fraction columns correctly
* hru_threshold removes tiny HRU combinations and renormalises fractions
* Optional landuse_lookup/soil_lookup map integer raster values to SWAT+ codes
* Falls back to single default HRU per subbasin when no raster data found
* Internal helpers: .make_slope_labels, .classify_slope_values,
.extract_hru_fractions, .apply_hru_threshold, .compute_hru_fracs,
.project_raster_if_needed, .lookup_code
- Wire delineate_hrus() into delineate_watershed() via optional landuse/soil
params: when both are supplied the HRU overlay runs automatically after the
TauDEM pipeline, replacing placeholder HRUs with real soil×landuse×slope HRUs
- Add 7 unit tests in test-delineate.R covering slope label generation,
slope classification, threshold filtering, area-fraction arithmetic,
CRS validation, and end-to-end HRU delineation with synthetic rasters
Co-authored-by: tadhg-moore <23136262+tadhg-moore@users.noreply.github.com>
Agent-Logs-Url: https://github.com/limnotrack/swatplus-editor/sessions/0b260750-4c94-4433-a283-7ab41c4a4eec
|
I have added a Windows executable to run SWAT+ in inst/extbin. Can you write a R wrapper for this function using the processx R package. Then can you add a min reprex in a test to see if I can build and run a SWAT simulation using this package @copilot |
- Add R/run_swatplus.R:
* swatplus_exe(): resolves path to bundled Windows SWAT+ binary (inst/extbin/)
* run_swatplus(): processx-based wrapper launching SWAT+ in a working_dir,
streams stdout/stderr, checks exit code, returns named list with
status/stdout/stderr/elapsed/success
- Update run_all.R: replace system2() with run_swatplus(); check ret$success
before updating the DB timestamp
- Add processx (>= 3.5.0) to Suggests in DESCRIPTION
- Add tests/testthat/test-run_swatplus.R with:
* swatplus_exe() path resolution tests (always run)
* run_swatplus() validation tests: NULL exe, missing path, bad working_dir,
return-value shape on non-zero exit
* Minimal reprex test (Windows-only, requires processx + bundled exe):
create_project_db() → write_swatplus_files() → run_swatplus()
Co-authored-by: tadhg-moore <23136262+tadhg-moore@users.noreply.github.com>
Agent-Logs-Url: https://github.com/limnotrack/swatplus-editor/sessions/20fb0b1a-0084-45b7-8c8e-679a4df631b1
Done in commit
Minimal reprex test in # Always-run API tests
swatplus_exe() # NULL or valid .exe path
run_swatplus(NULL, ...) # errors cleanly
# Windows reprex (skipped on Linux/Mac)
db <- create_project_db(...)
write_swatplus_files(db, txt_dir) # produces file.cio etc.
result <- run_swatplus(swatplus_exe(), txt_dir)
# → list(status, stdout, stderr, elapsed, success)The reprex skips automatically on non-Windows or when |
gis_routing PRIMARY KEY violation, emit_progress crash, sf spatial returns, and weather station importgis_routing PRIMARY KEY violation, emit_progress crash, sf spatial returns, weather station import, HRU overlay, and SWAT+ model execution
- Add .write_file_cio(con, dir, version) to write_files.R:
* Mirrors Python fileio/config.py File_cio.write()
* Queries each relevant table to decide filename vs "null"
* Covers all 31 classifications (simulation, basin, climate, connect,
channel, reservoir, routing_unit, hru, exco, recall, dr, aquifer,
herd, water_rights, link, hydrology, structural, hru_parm_db, ops,
lum, chg, init, soils, decision_table, regions, pcp/tmp/slr/hmd/wnd/out paths)
* Header reads editor_version from project_config (falls back to "2.3.0")
* Format: section name + filenames left-justified in 16-char fields,
separated by 2 spaces, one line per section — matches Python output
- Call .write_file_cio() at end of write_swatplus_files()
- Update test-write_files.R:
* Add file.cio to existing "creates expected output files" assertions
* Add new test verifying file.cio sections and populated-table filenames
Fixes: test-run_swatplus.R:144 "file.exists(file.cio) == FALSE"
Co-authored-by: tadhg-moore <23136262+tadhg-moore@users.noreply.github.com>
Agent-Logs-Url: https://github.com/limnotrack/swatplus-editor/sessions/5a482e42-7a58-4442-b142-30f4e45dcfdc
gis_routing PRIMARY KEY violation, emit_progress crash, sf spatial returns, weather station import, HRU overlay, and SWAT+ model executionfile.cio writer and processx-based SWAT+ model runner
write_swatplus_files()never producedfile.cio— the master control file SWAT+ reads on startup to locate all other inputs — so the model could not run. Additionally, the model was launched via baresystem2()with no exit-code checking or output streaming.file.ciowriter (R/write_files.R)Added
.write_file_cio(con, dir, version), mirroringfileio/config.py File_cio.write():simulation,basin,climate,connect, …,regions, path entries)"null"otherwiseversionis read fromproject_config.editor_version(fallback"2.3.0")write_swatplus_files()after all other files are writtenprocessxmodel runner (R/run_swatplus.R)swatplus_exe()— resolves the bundled Windows binary ininst/extbin/run_swatplus(swat_exe, working_dir, args, timeout, echo, verbose)— replacessystem2()inrun_all(); streams stdout/stderr line-by-line, checks exit code, returnslist(status, stdout, stderr, elapsed, success)processx (>= 3.5.0)added toSuggestsTests
test-write_files.R: addedfile.cioexistence assertion to existing round-trip test; new test validates section names and that populated tables yield real filenamestest-run_swatplus.R: API/validation tests (always run) + Windows-only reprex (create_project_db() → write_swatplus_files() → run_swatplus())✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.