feat: Add SOLUS soil dataset download and bedrock datasets - #51
Merged
Conversation
…nd incoporate new method for computing psib parameter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request adds the USDA SOLUS100 dataset as a third gridded soil data source and fixes the ROSETTA-based soil parameter workflow. The two changes are related: the parameter fixes apply to every source processed through ROSETTA, and SOLUS brings a depth-to-bedrock product that enables a new spatially variable initial groundwater tool for spin-up simulations.
The ROSETTA fixes address two stacked problems. First,
rosetta-soil0.3 (released 02/2026) changedrosetta()to returnalpha,n, andKsatin linear units instead of log10, so the back transform silently corrupted every derived grid in any environment installed after that release. The workflow now requests geometric-mean estimates (estimate_type="geo", matching the log10-mean convention of ROSETTA and ofrosetta-soil< 0.3) and the dependency is pinned torosetta-soil>=0.3. Second, the Brooks-Corey air-entry pressurepsibwas previously approximated as-1/alpha, which is not the ideal van Genuchten to Brooks-Corey conversion and overestimates the magnitude. It is now converted following Morel-Seytoux et al. (1996, WRR 32(5)), which preserves the effective capillary drive so that infiltration behavior is insensitive to the choice of retention model. As an independent check, the convertedpsibfor a loam agrees within ~6% of the Rawls & Brakensiek (1982) literature value for that texture class. Pixels are also now predicted in large batched calls rather than onerosetta()call per pixel, which substantially speeds up the soil workflow on large watersheds.Functions
The SOLUS tools live on the
Soilclass and mirror the structure of the existing ISRIC/POLARIS paths.run_soil_workflow(watershed, output_dir, source='SOLUS'): the existing workflow gains'SOLUS'as a source option. Downloads the property grids, fills nodata, estimates the tRIBS parameter grids with ROSETTA (model code 3: sand, silt, clay, bulk density), computes the conductivity decay parameterf, and writes the soil classification map, table, andscgrid.gdfexactly as for the other sources.get_solus_grids(bbox, depths, variables): downloads SOLUS100 (100 m) soil property rasters via Cloud Optimized GeoTIFF windowed reads, only the pixels covering the domain are fetched over HTTP, never the CONUS-wide file and reprojects them to the project CRS. Currently mapped for the top three depth layers (0-5, 5-15, 15-30 cm).get_solus_bedrock(bbox): downloads the SOLUS100 depth-to-bedrock raster the same way, converts cm to m, writes it as an ASCII grid, and sets theBEDROCKFILEoption.generate_initial_groundwater(bedrock_path, fraction, filename=None): builds a spatially variable initial water table by scaling a bedrock depth raster by a user-specified fraction (e.g. 0.7 places the initial water table 70% of the way from the surface to bedrock). Intended to simplify setting up the initial depth to bedrock raster, pairs naturally withget_solus_bedrock().process_solus_soil(grid_input, output=None, ks_only=False): converts downloaded SOLUS property grids into the tRIBS parameter grids (Ks, theta_r, theta_s, psib, m) through the same shared ROSETTA prediction and van Genuchten to Brooks-Corey conversion path used by the ISRIC workflow.Usage
Technical Changes
pytRIBS/soil/soil.py_rosetta_to_tribs_params(), the shared prediction core used by both the ISRIC and SOLUS paths so the ROSETTA handling and vG-to-BC conversions cannot drift apart. It batches pixels throughrosetta()in chunks (rosetta materializes a 1000-member bootstrap ensemble per call, so chunking bounds memory on large watersheds), requestsestimate_type="geo", convertspsibvia Morel-Seytoux et al. (1996) eq. 17.process_raw_soil()(ISRIC) onto the shared core, removing the per-pixel loop and the now incorrect10**back-transforms.get_solus_grids(),get_solus_bedrock(),process_solus_soil(), andgenerate_initial_groundwater(). SOLUS bulk density is delivered scaled by 100 and is converted to g/cm3 before prediction — without this, ROSETTA silently rejects the out-of-range values and falls back to its texture-only model._fill_solus_nonsoil(), which repairs SOLUS non-soil cells after the standard nodata fill and writes thenonsoil_mask.ascfootprint described above.create_soil_map()accepts the SOLUS texture grid types (sandtotal/claytotal, already in %, no unit conversion).compute_ks_decay()gained amin_ffloor (default 1e-4) applied after curve fitting, preventing near-zero decay values that tRIBS cannot handle numerically where Ks is nearly uniform with depth.run_soil_workflow()wires in the SOLUS branches (download, processing, soil map) and builds the decay-fit inputs with file-existence checks; parameter rasters are written with controlled decimal precision (grids at 2 decimals, groundwater at 0,fat 5), substantially reducing file sizes.pytRIBS/shared/inout.py:write_ascii()now detects ASCII grid header lines by content instead of assuming a fixed 6-line header, fixing a crash in the decimal-formatting path when GDAL writesdx/dyheaders in place ofcellsize.pyproject.toml:rosetta-soil>=0.1.2raised torosetta-soil>=0.3.