gwsource.network#
Global node graph of a simulation: several GWF models, their exchanges, package nodes and MVR.
Nodes (gwsource.flowstep.NodeBlock, in this order): the cells of every GWF model (user node
numbering, model by model as listed in mfsim.nam or given), then one node per reach, lake, well
or UZF cell of every enabled advanced package (open_simulation(package_nodes=...)). A FlowStep
built here has blocks; everything downstream (assembly, solve, metrics, diagnostics, travel
time) works on it unchanged, because it is just a larger graph.
Edges (each water transfer counted once):
- intercell flows of each model (FLOW-JA-FACE, IMETH = 1);
- GWF-GWF exchanges between two models that are both read: from the exchange's FLOW-JA-FACE
record (IMETH = 6) of the first model that lists it; the second model's record must be its
exact negative (checked); both models' boundary terms are dropped. An exchange with a model
that is not read stays a boundary term ('GWF-GWF').
- cell <-> package node: the GWF budget's aquifer-side record of that package (SFR, LAK, MAW,
UZF-GWRCH), after checking, cell by cell, that the package budget's GWF record is its negative
to aquifer_rtol (default AQUIFER_RTOL) of the package's largest exchange; the record is then
an edge, no longer a term;
- package node <-> package node: the package budget's FLOW-JA-FACE (SFR reach to reach, LAK
outlet to lake, UZF cell to cell);
- MVR (see below).
Every other record is a source or sink term of its node (gwsource.packages lists them). Package
node terms are named '<package>:<record>' (parent = the package name, package_type = its type),
so select(package='MAW') selects all terms of the MAW wells, whatever the package is called, and
select(package='MAW', term='RATE') their pumping.
With several models, every term package name is prefixed '<model>/'; selectors also match the
name without the prefix, so select(package='WEL') selects the wells of every model.
Water mover. With an MVR budget file, each transfer of q from a provider (package, bound) to a
receiver package node becomes an edge, if the receiver package has nodes and the provider's
TO-MVR record(s) for that bound are in the graph: on a cell (DRN-TO-MVR, 'UZF-GWD TO-MVR', ...)
or on an enabled package node (TO-MVR, RATE-TO-MVR, REJ-INF-TO-MVR, ...). When one provider
bound has several such records (a UZF cell's REJ-INF-TO-MVR and its cell's GWD TO-MVR; a MAW
well's RATE-TO-MVR and FW-RATE-TO-MVR), the transfer is shared among them in proportion to their
rates. The routed rate is taken off the provider's TO-MVR sink and the receiver's FROM-MVR
source; a remainder within MOVER_SNAP_RTOL of the original record (MF6 computes the moved share
at the last outer iteration, so the records differ at solver tolerance) is set to 0 and shows up
in the node residual instead. Transfers that cannot be routed stay sink and source terms, as
without package nodes. Movers between models (the simulation-level MVE) are not read.
Residuals: the FLOW-JA-FACE diagonal of a cell keeps MF6's own residual (which includes its
exchange flows); the diagonal of a package node is the sum of all its records in the package
budget, MF6's balance of that node. diagnostics.cell_residual() recomputes both from the graph.
Water volume: package nodes take the VOLUME auxiliary of their STORAGE record (SFR reach, lake,
MAW well, UZF cell) as FlowStep.fixed_volume; a package without it gets zero-volume nodes
(listed in notes['zero_volume_packages']). Cells keep area x saturated thickness x porosity.
Per-model output: model_view() and result_view() cut a network step and a trace result down to
one model's cells (with that model's grid shape and files), so the single-model exports and
reshaping (gwsource.export) apply; package_rows() lists the package-node results as a table.
- class gwsource.network.TermTable(node, flow, package, bound, boundname, packages, keep)[source]#
Bases:
objectExternal term entries under assembly: global node, flow, package, bound, boundname.
- Parameters:
- class gwsource.network.Links(rows=<factory>, cols=<factory>, vals=<factory>)[source]#
Bases:
objectCSR triplets (row, column, FLOW-JA-FACE value) of the global graph under assembly.
- gwsource.network.package_blocks(pkg_steps, start)[source]#
Return one block per enabled package, after the model blocks.
- Parameters:
pkg_steps (list[PackageStep])
start (int)
- Return type:
- gwsource.network.model_terms(steps, blocks, multi)[source]#
Return the term table of every model's external terms, on global nodes.
- gwsource.network.package_terms(pkg_steps, blocks, multi, names_of)[source]#
Return the term table of every package node's source and sink records.
- gwsource.network.join_tables(first, second)[source]#
Return the two term tables concatenated (second's packages after first's).
- gwsource.network.model_links(links, steps, blocks)[source]#
Add every model's FLOW-JA-FACE (diagonal included) on global nodes.
- gwsource.network.exchange_links(links, table, blocks)[source]#
Turn GWF-GWF exchange terms between models that are both read into edges; return names.
- gwsource.network.aquifer_links(links, table, pkg_steps, blocks, model_offset, n_nodes, aquifer_rtol=0.0001)[source]#
Turn each package's cell exchange into edges and drop the aquifer-side GWF records. Returns {block label: (global package nodes, global cells)} of the aquifer connections. The edges take the GWF budget's values (so cell residuals stay MF6's); the package budget's GWF record must have the same connections with opposite flows to aquifer_rtol of the package's largest exchange (the two sides differ at solver tolerance: MF6 evaluates them with the head and the package stage of different iterates). A larger tolerance lets a loosely solved model through: the difference stays in the package node's residual (its balance in the package budget), which closure and duality include as the residual term.
- gwsource.network.internal_links(links, pkg_steps, blocks)[source]#
Add package-package edges (SFR reach to reach, LAK outlets, UZF cell to cell).
- Parameters:
links (Links)
pkg_steps (list[PackageStep])
- Return type:
None
- gwsource.network.mover_records(table)[source]#
Index the kept mover records once. Returns ({(model, provider package): {bound: TO-MVR entries}}, {(model, receiver package): {global node: FROM-MVR entries}}).
- gwsource.network.mover_links(links, table, transfers, blocks)[source]#
Route MVR transfers between nodes of the graph; return the total routed rate.
- gwsource.network.package_diagonals(links, pkg_steps, blocks)[source]#
Add each package node's own budget residual as its diagonal entry.
- Parameters:
links (Links)
pkg_steps (list[PackageStep])
- Return type:
None
- gwsource.network.build_csr(n, links)[source]#
Return (ia, ja, flowja) sorted by row, diagonal first, then by column.
- gwsource.network.compact_terms(table)[source]#
Return the kept entries; packages whose entries all became edges are removed. Packages that had no entries in the budget (an empty record) are kept, as in a cells-only step, so that selecting them still works.
- gwsource.network.build_network(steps, pkg_steps, transfers, names_of=None, aquifer_rtol=0.0001)[source]#
Return the global FlowStep of several model steps, package steps and MVR transfers. aquifer_rtol: tolerance of the GWF and package budgets' aquifer exchange (aquifer_links).
- Parameters:
pkg_steps (list[PackageStep])
transfers (dict[str, MoverTransfer])
aquifer_rtol (float)
- Return type:
- gwsource.network.simulation_models(ws)[source]#
Return the names of every GWF6 model in mfsim.nam, in order.
- class gwsource.network.NetworkReader(models, packages, movers, notes=<factory>, aquifer_rtol=0.0001)[source]#
Bases:
objectEvery model's grid and budget, and the package and MVR budgets, opened once. read() returns the global FlowStep of one time step; steps and step_length() follow the first model, so trace_transient() works on a NetworkReader as on a StepReader. aquifer_rtol is the tolerance of the GWF and package budgets' aquifer exchange (see aquifer_links). The budget files stay open until close() (or the end of a with block).
- Parameters:
models (dict[str, StepReader])
packages (list[tuple[PackageFiles, CellBudgetFile]])
notes (dict)
aquifer_rtol (float)
- property steps: list[tuple[int, int]]#
0-based (kstp, kper) of every saved time step (of the first model).
- gwsource.network.open_simulation(ws, models=None, package_nodes=(), boundnames=False, aquifer_rtol=0.0001)[source]#
Open the models of a simulation (all GWF models by default) and their package budgets. package_nodes: package types that get nodes, e.g. ("SFR", "MAW"); each needs BUDGET FILEOUT, and MVR (if present in a model with package nodes) needs BUDGET FILEOUT too. aquifer_rtol: largest allowed difference, relative to a package's largest aquifer exchange, between the GWF budget's record of the package and the package budget's GWF record. The two differ at solver tolerance; a loosely solved model may need more than the default, and the difference then shows up in the package nodes' residuals.
- gwsource.network.read_network(ws, kstpkper=(0, 0), models=None, package_nodes=(), boundnames=False, aquifer_rtol=0.0001)[source]#
Read one time step of a simulation's models (and package nodes) into one global FlowStep. aquifer_rtol: tolerance of the aquifer exchange check (see open_simulation).
- gwsource.network.global_values(step, per_model, fill=nan)[source]#
Return a per-node array from {model: per-cell array (any shape)}; fill elsewhere.
- gwsource.network.global_nodes(step, label, local)[source]#
Return the global nodes of 0-based local indices (cells or package nodes) of a block.
- gwsource.network.model_block(step, model=None)[source]#
Return a model's block; model may be omitted when the step has only one model.
- gwsource.network.model_view(step, model=None)[source]#
Return one model's cells as a single-model FlowStep (for export and reshaping only). Edges to other models and to package nodes are left out, so its budgets do not close; trace the network step and cut the result with result_view().
- gwsource.network.term_index(step, block)[source]#
Return the indices of the term entries located on a block's nodes.
- gwsource.network.result_view(result, step, model=None)[source]#
Return a trace result cut down to one model's cells (pairs with model_view()).
- Parameters:
result (BackwardResult | ForwardResult)
step (FlowStep)
model (str | None)
- gwsource.network.transient_view(result, model=None)[source]#
Return a TransientResult cut down to one model's cells (statistics and kept series).
- Parameters:
model (str | None)
- gwsource.network.package_rows(step, result)[source]#
Return one row per package node: block, id and every per-node output of every group.
- Parameters:
step (FlowStep)
result (BackwardResult | ForwardResult)
- Return type: