-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RTX Code Release (PI-3): Enhancements to T-Route so it can be used for OWP's Replace and Route #837
base: master
Are you sure you want to change the base?
Changes from 12 commits
c22c82a
43d4489
c7cc896
a2c826f
4eba8a9
60f2c7c
7306c68
60986b8
510386e
3dfb149
0a6f481
627d57f
2cf4c3f
81fe066
d0f8575
35e4b6c
ffb9f96
b28544f
2ecd279
2deff3c
51b2253
b3960e0
5e1a004
884c101
ea96782
7e5dc45
4fc70fa
8ca681a
7c20da5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -79,3 +79,7 @@ bower_components/ | |
.grunt/ | ||
src/vendor/ | ||
dist/ | ||
data/ | ||
output | ||
test_compose.yaml | ||
src/app/.ruff_cache | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
FROM rockylinux:9.2 as rocky-base | ||
RUN yum install -y epel-release | ||
RUN yum install -y netcdf netcdf-fortran netcdf-fortran-devel netcdf-openmpi | ||
|
||
RUN yum install -y git cmake python python-devel pip | ||
|
||
WORKDIR "/t-route/" | ||
COPY . "/t-route/" | ||
|
||
RUN ln -s /usr/lib64/gfortran/modules/netcdf.mod /usr/include/openmpi-x86_64/netcdf.mod | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use the ENV var to point to the include dir There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just an FYI, this was taken from if I'm making a change to the troute_api container, then the regular container should reflect the same addition |
||
|
||
ENV PYTHONPATH=/t-route:$PYTHONPATH | ||
RUN pip install uv==0.2.5 | ||
RUN uv venv | ||
|
||
ENV VIRTUAL_ENV=/t-route/.venv | ||
ENV PATH="$VIRTUAL_ENV/bin:$PATH" | ||
|
||
RUN uv pip install --no-cache-dir -r requirements.txt | ||
|
||
ENV WITH_EDITABLE=true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think an editable build is really needed here |
||
RUN ./compiler.sh | ||
|
||
RUN uv pip install --no-cache-dir -r requirements-app.txt | ||
|
||
WORKDIR "/t-route/src/" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. posix newline |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#!/bin/bash | ||
|
||
if ! docker login --username ${GH_USERNAME} --password ${GH_TOKEN} ghcr.io; then | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs some additional context, probably in a readme, which describes what is accomplished with this script and what the ghcr.io resource is. Probably also good to describe how to build and use locally without using ghrc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This script is used to push a built T-Route container to their Github Container Registry. After more thought, I think it's best to delete this script and use CI/CD with github secrets set by maintainers to update docker containers rather than manual builds using ENV credentials. thoughts? |
||
echo "Error: Failed to login to ghcr.io. Please check your GH_USERNAME and GH_TOKEN env vars" | ||
exit 1 | ||
fi | ||
|
||
if ! docker build -t ghcr.io/NOAA-OWP/t-route/t-route-api:${TAG} -f Dockerfile.troute_api .; then | ||
echo "Error: Failed to build Docker image. Please check your Dockerfile and build context." | ||
exit 1 | ||
fi | ||
|
||
if ! docker push ghcr.io/NOAA-OWP/t-route/t-route-api:${TAG}; then | ||
echo "Error: Failed to push Docker image. Please check your TAG env var" | ||
exit 1 | ||
fi | ||
|
||
echo "Successfully built and pushed Docker image with tag: ${TAG}" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
services: | ||
troute: | ||
build: | ||
context: . | ||
dockerfile: Dockerfile.troute_api | ||
ports: | ||
- "8004:8000" | ||
volumes: | ||
- type: bind | ||
source: ./data/troute_output | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be worth moving these paths to ENV vars and using a .env file, with corresponding documentation/description of what is expected to be in these mounts when run, or what will be put in them when finsihed. |
||
target: /t-route/output | ||
- type: bind | ||
source: ./data | ||
target: /t-route/data | ||
- type: bind | ||
source: ./src/app/core | ||
target: /t-route/src/app/core | ||
command: sh -c ". /t-route/.venv/bin/activate && uvicorn app.main:app --host 0.0.0.0 --port 8000" | ||
healthcheck: | ||
test: curl --fail -I http://localhost:8000/health || exit 1 | ||
interval: 30s | ||
timeout: 5s | ||
retries: 3 | ||
start_period: 5s | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. newline will hereto be referred to as More cowbell! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a great addition |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the need to keep the empty folder in git? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was for having a place within the container for restart files to be saved. I'm going to look into generating this inside the container using the dockerfile |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
# T-Route FastAPI | ||
|
||
The following doc is meant to explain the T-Route FastAPI implementation using docker compose and shared volumes. | ||
|
||
## Why an API? | ||
|
||
T-Route is used in many contexts for hydrological river routing: | ||
- NGEN | ||
- Scientific Python | ||
- Replace and Route (RnR) | ||
|
||
In the latest PR for RnR, there is a requirement to run T-Route as a service. This service requires an easy way to dynamically create config files, restart flow from Initial Conditions, and run T-Route. To satisfy this requirement, a FastAPI endpoint was created in `/src/app` along with code to dynamically create t-route endpoints. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. links/references to RnR here would be useful context |
||
|
||
## Why use shared volumes? | ||
|
||
Since T-Route is running in a docker container, there has to be a connection between the directories on your machine and the directories within the container. We're sharing the following folders by default: | ||
- `data/rfc_channel_forcings` | ||
- For storing RnR RFC channel domain forcing files (T-Route inputs) | ||
- `data/rfc_geopackage_data` | ||
- For storing HYFeatures gpkg files | ||
- Indexed by the NHD COMID, also called hf_id. Ex: 2930769 is the hf_id for the CAGM7 RFC forecast point. | ||
- `data/troute_restart` | ||
- For storing TRoute Restart files | ||
- `data/troute_output` | ||
- For outputting results from the T-Route container | ||
|
||
## Quickstart | ||
1. From the Root directory, run: | ||
```shell | ||
docker compose up | ||
``` | ||
|
||
This will start the T-Route container and run the API on localhost:8004. To view the API spec and swagger docs, visit localhost:8004/docs | ||
|
||
2. Submit a request | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would be good to use an example with available data, I'm not sure that this example would run "out of the box" without staging the required inputs. If that is the case, steps to stage that data would be useful. |
||
```shell | ||
curl -X 'GET' \ | ||
'http://localhost:8004/api/v1/flow_routing/v4/?lid=CAGM7&feature_id=2930769&hy_id=1074884&initial_start=0&start_time=2024-08-24T00%3A00%3A00&num_forecast_days=5' \ | ||
-H 'accept: application/json' | ||
``` | ||
|
||
This curl command is pinging the flow_routing v4 endpoint `api/v1/flow_routing/v4/` with the following metadata: | ||
``` | ||
lid=CAGM7 | ||
feature_id=2930769 | ||
hy_id=1074884 | ||
initial_start=0 | ||
start_time=2024-08-24T00:00:00 | ||
num_forecast_days=5 | ||
``` | ||
|
||
which informs T-Route which location folder to look at, what feature ID to read a gpkg from, the HY feature_id where flow is starting, the initial start flow for flow restart, start-time of the routing run, and the number of days to forecast. | ||
|
||
You can also run the following args from the swagger endpoint: | ||
|
||
![alt text](swagger_endpoints.png) | ||
|
||
The result for a successful routing is a status 200: | ||
```json | ||
{ | ||
"message": "T-Route run successfully", | ||
"lid": "CAGM7", | ||
"feature_id": "2930769" | ||
} | ||
``` | ||
|
||
and an internal 500 error if there is something wrong. | ||
|
||
## Building and pushing to a container registry | ||
|
||
the `build.sh` script is created to simplify the process of pushing the T-route image to a docker container registry. Below is the URL for the pushed container: | ||
```shell | ||
ghcr.io/NOAA-OWP/t-route/t-route-api:${TAG} | ||
``` | ||
To run that script, the following ENV variables have to be set: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is part of what I was looking for when I seen |
||
- ${GH_USERNAME} | ||
- your github username | ||
- ${GH_TOKEN} | ||
- your github access token | ||
- ${TAG} | ||
- the version tag | ||
- ex: 0.0.2 | ||
|
||
If you want to build this off a forked version, change the container registry to your user accounts container registry. | ||
|
||
## Testing: | ||
|
||
To test this container, follow the steps within `test/api/README.md` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
fastapi==0.110.1 | ||
uvicorn==0.30.1 | ||
pydantic<=1.10 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
"""Author: Tadd Bindas""" | ||
|
||
from fastapi import APIRouter | ||
|
||
from app.api.routes import v4_routing | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. routes and routing -- the cognitive relationship here can be difficult. Two things would help
Both would probably be reasonable. |
||
|
||
api_router = APIRouter() | ||
api_router.include_router( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tag with |
||
v4_routing.router, prefix="/flow_routing/v4", tags=["v4 Flow Routing"] | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
"""Author: Tadd Bindas""" | ||
|
||
from typing import Annotated | ||
|
||
from fastapi import APIRouter, Depends | ||
from fastapi.responses import JSONResponse | ||
from nwm_routing import main_v04 as t_route | ||
|
||
from app.api.services.initialization import (create_initial_start_file, | ||
create_params, edit_yaml) | ||
from app.core import get_settings | ||
from app.core.settings import Settings | ||
from app.schemas import TRouteOuput | ||
|
||
router = APIRouter() | ||
|
||
|
||
@router.get("/", response_model=TRouteOuput) | ||
async def get_gauge_data( | ||
lid: str, | ||
feature_id: str, | ||
hy_id: str, | ||
initial_start: float, | ||
start_time: str, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use an actual datetime type |
||
num_forecast_days: int, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider using a conint (constrainted) here to forecast days run via API to something reasonable. Maybe a few years max to start with? |
||
settings: Annotated[Settings, Depends(get_settings)], | ||
) -> TRouteOuput: | ||
"""An API call for running T-Route | ||
|
||
Parameters: | ||
---------- | ||
lid: str | ||
The Location of the RFC Point | ||
feature_id: str | ||
The COMID associated with the LID | ||
hy_id: str | ||
The HY_ID associated with the LID | ||
initial_start: float | ||
The initial start for T-Route | ||
start_time: str | ||
The start time for the forecast | ||
num_forecast_days: int | ||
The number of days in the forecast | ||
|
||
Returns: | ||
------- | ||
TRouteOutput | ||
A successful T-Route run | ||
""" | ||
base_config = settings.base_config | ||
params = create_params( | ||
lid, feature_id, hy_id, initial_start, start_time, num_forecast_days, settings | ||
) | ||
restart_file = create_initial_start_file(params, settings) | ||
yaml_file_path = edit_yaml(base_config, params, restart_file) | ||
try: | ||
t_route(["-f", yaml_file_path.__str__()]) | ||
except Exception as e: | ||
JSONResponse( | ||
status_code=500, | ||
content={"message": e}, | ||
) | ||
|
||
yaml_file_path.unlink() | ||
|
||
return TRouteOuput( | ||
message="T-Route run successfully", | ||
lid=lid, | ||
feature_id=feature_id, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add new line for posix