# Source code for imod.evaluate.streamfunction

```
import imod
def _streamfunction(sfrf, sfff, dx, dy):
# Scale total flux to length of cell traversed
# --> no, this gives really akward patterns
scale_factor = (
1.0 # (sfrf.dx ** 2 + sfff.dy ** 2) ** 0.5 / (dx ** 2 + dy ** 2) ** 0.5
)
sfrfi = sfrf * -scale_factor # FRF is negative eastward, so flip
sfffi = sfff * scale_factor # FFF is positive northward
# calculate projection of flux onto cross section line
# Fi_proj = (Fi dotproduct Si) / |Si|
# keep sign
Fi_dot_si = sfrfi * sfrf.dx + sfffi * sfff.dy
Fi_proj = Fi_dot_si / Fi_dot_si.ds
# calculate stream function by cumsumming from bottom upwards
# method taken from https://olsthoorn.readthedocs.io/en/latest/07_stream_lines.html
layindex = Fi_proj.layer.values
Fi_proj = (
Fi_proj.reindex(layer=layindex[::-1])
.cumsum(dim="layer")
.reindex(layer=layindex)
)
return Fi_proj
[docs]def streamfunction_line(frf, fff, start, end):
"""Obtain the streamfunction for a line cross section through
a three-dimensional flow field. The streamfunction is obtained
by first projecting the horizontal flow components onto the provided
cross-section. The streamfunction can be contoured to visualize stream lines.
Stream lines are an efficient way to visualize groundwater flow.
Note, however, that the streamfunction is only defined in 2D, non-diverging,
steady-state flow without sources and sinks. These assumption are violated even
in a 2D model, but even more so in the approach followed here. Flow perpendicular
to the cross-section will not be visualized. It is up to the user to choose
cross-sections as perpendicular to the main flow direction as possible.
The 2D streamfunction and stream line visualization is based on work of Em. Prof. Olsthoorn.
Parameters
----------
frf: `xarray.DataArray`
Three- (or higher) dimensional dataarray of flow component along the rows (FLOW RIGHT FACE).
fff: `xarray.DataArray`
Three- (or higher) dimensional dataarray of flow component along the columns (FLOW FRONT FACE).
start: (2, ) array_like
A latitude-longitude pair designating the start point of the cross
section.
end: (2, ) array_like
A latitude-longitude pair designating the end point of the cross
section.
Returns
-------
`xarray.DataArray`
The streamfunction projected on the cross-section between start and end coordinate,
with new dimension "s" along the cross-section. The cellsizes along "s" are given in
the "ds" coordinate.
"""
# interpolate frf and fff to cell center
frf = 0.5 * frf + 0.5 * frf.shift({"x": -1})
fff = 0.5 * fff + 0.5 * fff.shift({"y": -1})
# get cross section of frf and fff
sfrf = imod.select.cross_section_line(frf, start, end)
sfff = imod.select.cross_section_line(fff, start, end)
return _streamfunction(sfrf, sfff, frf.dx, fff.dy)
[docs]def streamfunction_linestring(frf, fff, linestring):
"""Obtain the streamfunction for a linestring cross section through
a three-dimensional flow field. The streamfunction is obtained
by first projecting the horizontal flow components onto the provided
cross-section. The streamfunction can be contoured to visualize stream lines.
Stream lines are an efficient way to visualize groundwater flow.
Note, however, that the streamfunction is only defined in 2D, non-diverging,
steady-state flow without sources and sinks. These assumption are violated even
in a 2D model, but even more so in the approach followed here. Flow perpendicular
to the cross-section will not be visualized. It is up to the user to choose
cross-sections as perpendicular to the main flow direction as possible.
The 2D streamfunction and stream line visualization is based on work of Em. Prof. Olsthoorn.
Parameters
----------
frf: `xarray.DataArray`
Three- (or higher) dimensional dataarray of flow component along the rows (FLOW RIGHT FACE).
fff: `xarray.DataArray`
Three- (or higher) dimensional dataarray of flow component along the columns (FLOW FRONT FACE).
linestring : shapely.geometry.LineString
Shapely geometry designating the linestring along which to sample the
cross section.
Returns
-------
`xarray.DataArray`
The streamfunction projected on the cross-section defined by provided linestring,
with new dimension "s" along the cross-section. The cellsizes along "s" are given in
the "ds" coordinate.
"""
# interpolate frf and fff to cell center
frf = 0.5 * frf + 0.5 * frf.shift({"x": -1})
fff = 0.5 * fff + 0.5 * fff.shift({"y": -1})
# get cross section of frf and fff
sfrf = imod.select.cross_section_linestring(frf, linestring)
sfff = imod.select.cross_section_linestring(fff, linestring)
return _streamfunction(sfrf, sfff, frf.dx, fff.dy)
def _quiver(sfrf, sfff, sflf): # , dx, dy, dz):
# Scale total flux to length of cell traversed
# --> no, this gives really akward patterns -> also for quivers? here it might seem like a good idea
scale_factor = (
1.0 # (sfrf.dx ** 2 + sfff.dy ** 2) ** 0.5 / (dx ** 2 + dy ** 2) ** 0.5
)
sfrfi = sfrf * -scale_factor # FRF is negative eastward, so flip
sfffi = sfff * scale_factor # FFF is positive northward
sflfi = sflf * scale_factor # FLF is positive upward
# calculate projection of flux onto cross section plane
# cross section plane is given by? u: <dx, dy, 0>, v: <0, 0, 1>
# Fi_proj_u = (Fi dotproduct Si) / |Si| * (Si / |Si|)
# unitvector not necessary: return u and v as scalars
# keep sign
Fi_dot_u = sfrfi * sfrf.dx + sfffi * sfff.dy # + sflfi * 0.
Fi_proj_u = Fi_dot_u / Fi_dot_u.ds
Fi_proj_v = sflfi # no projection necessary for v
return Fi_proj_u, Fi_proj_v
[docs]def quiver_line(frf, fff, flf, start, end):
"""Obtain the u and v components for quiver plots for a line cross section
through a three-dimensional flux field. The u and v components are obtained
by first projecting the threedimensional flux components onto the provided
cross-section.
Parameters
----------
frf: `xarray.DataArray`
Three- (or higher) dimensional dataarray of flow component along the rows (FLOW RIGHT FACE).
fff: `xarray.DataArray`
Three- (or higher) dimensional dataarray of flow component along the columns (FLOW FRONT FACE).
flf: `xarray.DataArray`
Three- (or higher) dimensional dataarray of flow component along the layers (FLOW LOWER FACE).
start: (2, ) array_like
A latitude-longitude pair designating the start point of the cross
section.
end: (2, ) array_like
A latitude-longitude pair designating the end point of the cross
section.
Returns
-------
u: `xarray.DataArray`
The u component (x-component) of the flow projection on the cross-section between start and end coordinate,
with new dimension "s" along the cross-section. The cellsizes along "s" are given in
the "ds" coordinate.
v: `xarray.DataArray`
The v component (y-component) of the flow projection on the cross-section between start and end coordinate,
with new dimension "s" along the cross-section. The cellsizes along "s" are given in
the "ds" coordinate.
Notes
-----
Use imod.evaluate.flow_velocity() first to obtain groundwater velocities
as input for this function. Velocity in x direction is, however, inverted and must
be re-inverted before using as input here.
"""
# interpolate frf and fff to cell center
frf = 0.5 * frf + 0.5 * frf.shift({"x": -1})
fff = 0.5 * fff + 0.5 * fff.shift({"y": -1})
# get cross section of frf, fff and flf
sfrf = imod.select.cross_section_line(frf, start, end)
sfff = imod.select.cross_section_line(fff, start, end)
sflf = imod.select.cross_section_line(flf, start, end)
return _quiver(sfrf, sfff, sflf)
[docs]def quiver_linestring(frf, fff, flf, linestring):
"""Obtain the u and v components for quiver plots for a linestring cross section
through a three-dimensional flow field. The u and v components are obtained
by first projecting the threedimensional flow components onto the provided
cross-section.
Parameters
----------
frf: `xarray.DataArray`
Three- (or higher) dimensional dataarray of flow component along the rows (FLOW RIGHT FACE).
fff: `xarray.DataArray`
Three- (or higher) dimensional dataarray of flow component along the columns (FLOW FRONT FACE).
flf: `xarray.DataArray`
Three- (or higher) dimensional dataarray of flow component along the layers (FLOW LOWER FACE).
linestring : shapely.geometry.LineString
Shapely geometry designating the linestring along which to sample the
cross section.
Returns
-------
u: `xarray.DataArray`
The u component (x-component) of the flow projection on the cross-section between start and end coordinate,
with new dimension "s" along the cross-section. The cellsizes along "s" are given in
the "ds" coordinate.
v: `xarray.DataArray`
The v component (y-component) of the flow projection on the cross-section between start and end coordinate,
with new dimension "s" along the cross-section. The cellsizes along "s" are given in
the "ds" coordinate.
Notes
-----
Use imod.evaluate.flow_velocity() first to obtain groundwater velocities
as input for this function. Velocity in x direction is, however, inverted and must
be re-inverted before using as input here.
"""
# interpolate frf and fff to cell center
frf = 0.5 * frf + 0.5 * frf.shift({"x": -1})
fff = 0.5 * fff + 0.5 * fff.shift({"y": -1})
# get cross section of frf, fff and flf
sfrf = imod.select.cross_section_linestring(frf, linestring)
sfff = imod.select.cross_section_linestring(fff, linestring)
sflf = imod.select.cross_section_linestring(flf, linestring)
return _quiver(sfrf, sfff, sflf)
```