Skip to content

[in2TopoHyd]: Module for computing hydraulic inital conditions - #90

Open
JuLa96 wants to merge 2 commits into
masterfrom
JL_AddTopoHydModule
Open

[in2TopoHyd]: Module for computing hydraulic inital conditions#90
JuLa96 wants to merge 2 commits into
masterfrom
JL_AddTopoHydModule

Conversation

@JuLa96

@JuLa96 JuLa96 commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

in2TopoHyd is an input module for deriving initial hydraulic conditions for the c1TIF computation module
for a debris-flow hydrograph at a prescribed release line. The module combines the topography of the release area with
a time-dependent discharge hydrograph (Topo-Hydrograph) to calculate the corresponding flow thickness and flow velocity at the release line.

The hydraulic conditions are derived from the terrain cross section along the release line.
A rating curve is calculated from the cross-sectional geometry, relating flow thickness to flow area.
For each discharge value of the input hydrograph, the module determines the corresponding mean flow velocity and flow thickness.
The flow thickness is then distributed over the wetted cells of the release cross section.

The resulting initial conditions contain the flow thickness as well as the velocity components in x, y and z direction for every wet cell and every hydrograph timestep.

closes #85

Compiled doc : https://docs.debrisframe.org/en/jl_addtopohydmodule/

initial conditions in time-dependent release mode

Add documentation

Add test case
@JuLa96 JuLa96 added the enhancement New feature or request label Aug 14, 2026
@JuLa96 JuLa96 self-assigned this Aug 14, 2026
@JuLa96
JuLa96 requested review from PaulaSp3 and fso42 August 14, 2026 08:46
@qltysh

qltysh Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

❌ 1 blocking issue (1 total)

Tool Category Rule Count
ruff Lint Local variable surfElevRC is assigned to but never used 1

Comment thread debrisframe/in2TopoHyd/in2TopoHyd.py
Comment thread debrisframe/in2TopoHyd/in2TopoHyd.py
Comment thread debrisframe/in2TopoHyd/in2TopoHyd.py
thicknessRC = ratingCurve["thickness"]
flowAreaRC = ratingCurve["flowArea"]
# fetch debris-flow surface elevation
surfElevRC = ratingCurve["surfElev"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local variable surfElevRC is assigned to but never used [ruff:F841]

Suggested change
surfElevRC = ratingCurve["surfElev"]
ratingCurve["surfElev"]

Comment thread debrisframe/in2TopoHyd/in2TopoHyd.py
@qltysh

qltysh Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Qlty


Coverage Impact

This PR will not change total coverage.

🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

Comment on lines +69 to +70
# inputDir = avaDir / "Outputs" / "in2TopoHyd" / "peakFiles"
# peakFilesDF = fU.makeSimDF(inputDir, avaDir=avaDir)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# inputDir = avaDir / "Outputs" / "in2TopoHyd" / "peakFiles"
# peakFilesDF = fU.makeSimDF(inputDir, avaDir=avaDir)


# Get csv-file with initial conditions
debrisDir = pathlib.Path(debrisDir)
inputDir = debrisDir / "Outputs" / "in2TopoHyd" / "peakFiles"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
inputDir = debrisDir / "Outputs" / "in2TopoHyd" / "peakFiles"
outputDir = debrisDir / "Outputs" / "in2TopoHyd" / "peakFiles"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the same DEM as in debrisParabolaChanneled? Then, I suggest adding POINTS and REL into this debris data folder.

Comment on lines +127 to +139

The :py:mod:`in2TopoHyd` module requires a

* **digital elevation model as raster file**,

* **a time-dependent discharge hydrograph**,

* **a release line**,

* **and levee points**.

:py:mod:`in2TopoHyd` calculations are performed within a process directory, organized with the
folder structure described in the `AvaFrame documentation <https://docs.avaframe.org/en/latest/moduleCom1DFA.html#input>`_.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest defining the directory structure of the inputs directly here instead of referencing to avaframe, similar to https://docs.avaframe.org/en/latest/moduleCom1DFA.html#input

^^^^^^^^^^^^

The release line is read from the ``Inputs/REL`` directory. It must contain **exactly two points**, representing the starting and ending point of the release line.
If a ``releaseScenario`` is specified in ``local_c1TIFCfg.ini``, this file is used (**with** extension .shp). Otherwise, the module searches the ``REL`` directory for a release file.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If a ``releaseScenario`` is specified in ``local_c1TIFCfg.ini``, this file is used (**with** extension .shp). Otherwise, the module searches the ``REL`` directory for a release file.
If a ``releaseScenario`` is specified in ``(local_)c1TIFCfg.ini``, this file is used (**with** extension .shp). Otherwise, the module searches the ``REL`` directory for a release file.

^^^^^^^^^^^^

The release line is read from the ``Inputs/REL`` directory. It must contain **exactly two points**, representing the starting and ending point of the release line.
If a ``releaseScenario`` is specified in ``local_c1TIFCfg.ini``, this file is used (**with** extension .shp). Otherwise, the module searches the ``REL`` directory for a release file.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

releaseScenario or timeDependentReleaseScenarios, I could not find releaseScenario in c1TIFCfg.ini


# get file name of release line
# first, check if name is provided in the c1TIF-config file
fname = debrisCfg["com1DFA_com1DFA_override"]["releaseScenario"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In debrisCfg["com1DFA_com1DFA_override"] there is no "releaseScenario".
python runIni.. raises an error!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file ends with "Levee", below in the documentation it says, the file needs to end with "levee" (lower case).
When running the example, I get this error:

File not found or there are more than one levee files in POINTS-folder!
Ensure that there is exactly one point-shapefile including the ending *levee.shp!

raise ValueError(message)

# get file name of levee points
fname = debrisDir + "/Inputs/POINTS/*levee.shp"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest providing the levee points in a folder "LEVEE", instead of considering the name within the file.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest moving the hydrograph csv file to a different folder (e.g., HYDR ??)
The csv file in the REL folder is the input file for c1TIF.

# if the tickness at the very left and the very right cell is still > 0,
# there are remaining subareas on both sides that have to be considered
left, right = idx[0], idx[-1]
diffElevLeft = elevation[left - 1] - elevation[left]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes that the channel is strictly interior, right? Is that validated before? Does that assumption always hold?
If the levee point is the first (or last) cell, eg.left -1 becomes -1 and then elevation would be wrapped...

raise ValueError(message)

# get file name of levee points
fname = debrisDir + "/Inputs/POINTS/*levee.shp"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If file is wanted, I suggest pathlib.Path(debrisDir, "Inputs", "POINTS").glob("*levee.shp") as this works both with pathlib path and str (as mentioned in the docstring)

@@ -0,0 +1,861 @@
"""
Get initial conditions for hydrograph

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this module should be tested by pytests. If it is too much for this PR, open an issue and adress it in a latter PR...


# calculate unit normal vectors on left- and right-handside
# of direction vector
n1 = np.array([-dy, dx]) / magnitude

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines 244 to 249 are duplicates of 468 to 473. I suggest extracting a shared helper

Returns
--------
thickness: 1D-array
flow thickness and velocity values as starting condition

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flowVel is missing from the returns

v = 2.1 * math.pow(q, 0.33) * math.pow(slope, 0.33)
flowVel.append(v)

#TODO: add addtional methods for calculating the flow velocity

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo additional

the elevation of the cross-section cells,
the path along (distance) along the cross-section cells
ratingCurve: dict
dictionary containing the miminimum elevation of cross section (channel)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo minimum

# assign levee points to neares cross-section coordinates
crossSection = assignCrossSectionCoords(leveePoints, crossSection)

# export cross section cell centers as points for plausability check

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo plausibility

# compute rating curve
ratingCurve = computeRatingCurve(crossSection, topoHydCfg)

# plot cross section and rating curve for plausability check

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo plausi...

crossSectionY + (csz - modulus))

# get indices of cross-section cells
col = np.int32(np.array((crossSectionX - xllcenter) / csz))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the same convention avaframe's own readLine uses (int(np.floor((x - xllcenter) / csz))) ?

  col = np.int32(np.round((crossSectionX - xllcenter) / csz))                                               
  row = np.int32(np.round((crossSectionY - yllcenter) / csz))                                               

As I think your combination shifts the cell if the center is not a (half-)multiple of cellsize.

assignRasterCoords snaps x to the nearest multiple of cellsize.
getCrossSectionCells converts to an index with np.int32(...), which truncates (chops off the decimal)
rather than rounds.

# assign raster cell coord to starting and ending point of release line
xcoord = []
ycoord = []
for i in range(len(releaseLine["x"])):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simpler would be:

Suggested change
for i in range(len(releaseLine["x"])):
for x, y in zip(releaseLine["x"], releaseLine["y"]):

below you can just use x, y instead of releaseLine["x"][i], releaseLine["y"][i]


return crossSection

def assignRasterCoords(cellSize, releaseLine):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't you use avaframe.in3Utils.geoTrans.getCellsAlongLine() instead?

crossSection: dict
dictionaray containing x,y-coordinates of cross-section cells,
the elevation of the cross-section cells,
the path along (distance) along the cross-section cells

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo (one along is enough)

# horizontal distance between neighbouring cells
distance = crossSection["s"]
# get elevation steps
dElev = float(topoHydCfg["GENERAL"]["dElev"])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
dElev = float(topoHydCfg["GENERAL"]["dElev"])
dElev = topoHydCfg["GENERAL"].getfloat("dElev")

left, right = idx[0], idx[-1]
diffElevLeft = elevation[left - 1] - elevation[left]
dsLeft = distance[left] - distance[left - 1]
slopeLeft = diffElevLeft / dsLeft

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it make sense to compute the slope as:
(elevation[left+1]) - elevation[left-1]) / (distance[left + 1] - distance[left-1])


return thickness, flowVel

def getCrossSectionCells(dem, releaseLine):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use avaframe.in3Utils.geoTrans.getCellsAlongLine() to simplify this function?


Returns
--------
flowDir: 1D-array

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
flowDir: 1D-array
flwDir: 1D-array

crossSection: dict
dictionary containing x,y-coordinates of cross-section cells,
the elevation of the cross-section cells,
the path along (distance) along the cross-section cells

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

twice "along"?

# combine to 3D-unit-normal vector
flwDir = np.append(flwDir, nz)
magnitude = np.sqrt(flwDir[0]**2 + flwDir[1]**2 + nz**2)
flwDir = flwDir / magnitude

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to normalize flwDir here?
Then also use DFAtls.normalize ?

Comment on lines +517 to +518
flwDir = np.append(flwDir, nz)
magnitude = np.sqrt(flwDir[0]**2 + flwDir[1]**2 + nz**2)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
flwDir = np.append(flwDir, nz)
magnitude = np.sqrt(flwDir[0]**2 + flwDir[1]**2 + nz**2)
flwDir = np.append(flwDir, nz)
magnitude = np.sqrt(flwDir[0]**2 + flwDir[1]**2 + flwDir[2]**2)

??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a inTopoHyd module

3 participants