diff --git a/autotest/test_prt_budget.py b/autotest/test_prt_budget.py index acf9cdb5d96..273ab12049c 100644 --- a/autotest/test_prt_budget.py +++ b/autotest/test_prt_budget.py @@ -19,7 +19,6 @@ from flopy.utils.binaryfile import HeadFile from framework import TestFramework from prt_test_utils import ( - DEFAULT_EXIT_SOLVE_TOL, HorizontalCase, all_equal, check_budget_data, @@ -88,7 +87,6 @@ def build_prt_sim(name, gwf_ws, prt_ws, mf6): trackcsv_filerecord=[prp_track_csv_file], stop_at_weak_sink=False, boundnames=True, - exit_solve_tolerance=DEFAULT_EXIT_SOLVE_TOL, extend_tracking=True, ) diff --git a/autotest/test_prt_drape.py b/autotest/test_prt_drape.py index 703f4ef0d13..6c17d003996 100644 --- a/autotest/test_prt_drape.py +++ b/autotest/test_prt_drape.py @@ -20,7 +20,6 @@ from flopy.utils.binaryfile import HeadFile from framework import TestFramework from prt_test_utils import ( - DEFAULT_EXIT_SOLVE_TOL, all_equal, check_track_data, get_model_name, @@ -182,7 +181,6 @@ def build_prt_sim(name, gwf_ws, prt_ws, mf6): track_filerecord=[prp_track_file], trackcsv_filerecord=[prp_track_csv_file], drape="drp" in name, - exit_solve_tolerance=DEFAULT_EXIT_SOLVE_TOL, extend_tracking=True, ) diff --git a/autotest/test_prt_dry.py b/autotest/test_prt_dry.py index abc11fa7fe5..d68af937af1 100644 --- a/autotest/test_prt_dry.py +++ b/autotest/test_prt_dry.py @@ -301,7 +301,6 @@ def build_prt_sim(name, gwf, prt_ws, mf6, drape=False, dry_tracking_method=False packagedata=prp_data, nreleasetimes=1, releasetimes=[(0.0,)], - exit_solve_tolerance=1e-7, drape=drape, dry_tracking_method=dry_tracking_method, pname="prp", diff --git a/autotest/test_prt_exg.py b/autotest/test_prt_exg.py index 8c7176fb803..04caca2b396 100644 --- a/autotest/test_prt_exg.py +++ b/autotest/test_prt_exg.py @@ -24,7 +24,6 @@ from flopy.utils.binaryfile import HeadFile from framework import TestFramework from prt_test_utils import ( - DEFAULT_EXIT_SOLVE_TOL, FlopyReadmeCase, check_budget_data, check_track_data, @@ -73,7 +72,6 @@ def build_mf6_sim(idx, test): packagedata=rpts, perioddata={0: ["FIRST"]}, boundnames="bnms" in name, - exit_solve_tolerance=DEFAULT_EXIT_SOLVE_TOL, extend_tracking=True, ) diff --git a/autotest/test_prt_fmi.py b/autotest/test_prt_fmi.py index 8ebe9f2b80d..1664cf13bf4 100644 --- a/autotest/test_prt_fmi.py +++ b/autotest/test_prt_fmi.py @@ -38,7 +38,6 @@ from flopy.utils.binaryfile import HeadFile from framework import TestFramework from prt_test_utils import ( - DEFAULT_EXIT_SOLVE_TOL, FlopyReadmeCase, all_equal, check_budget_data, @@ -116,7 +115,6 @@ def build_prt_sim(name, gwf_ws, prt_ws, mf6): trackcsv_filerecord=[prp_track_csv_file], stop_at_weak_sink="saws" in prt_name, boundnames=True, - exit_solve_tolerance=DEFAULT_EXIT_SOLVE_TOL, extend_tracking="noext" not in prt_name, ) diff --git a/autotest/test_prt_quad_refinement.py b/autotest/test_prt_quad_refinement.py index 1e372ce4be4..93a3de8f85c 100644 --- a/autotest/test_prt_quad_refinement.py +++ b/autotest/test_prt_quad_refinement.py @@ -18,7 +18,6 @@ from flopy.utils.gridgen import Gridgen from framework import TestFramework from prt_test_utils import ( - DEFAULT_EXIT_SOLVE_TOL, FlopyReadmeCase, check_budget_data, check_track_data, @@ -144,7 +143,6 @@ def build_mf6_sim(idx, test, **kwargs): nreleasepts=len(rpts), packagedata=rpts, perioddata={0: ["FIRST"]}, - exit_solve_tolerance=DEFAULT_EXIT_SOLVE_TOL, dev_forceternary=tracking_method == "ternary", extend_tracking=True, ) diff --git a/autotest/test_prt_release_timing.py b/autotest/test_prt_release_timing.py index e343f0e1bfd..d9d60f8be1c 100644 --- a/autotest/test_prt_release_timing.py +++ b/autotest/test_prt_release_timing.py @@ -24,7 +24,6 @@ from framework import TestFramework from modflow_devtools.markers import requires_pkg from prt_test_utils import ( - DEFAULT_EXIT_SOLVE_TOL, FlopyReadmeCase, all_equal, check_budget_data, @@ -176,7 +175,6 @@ def build_prt_sim(name, gwf_ws, prt_ws, mf6): else None, release_time_frequency=0.2 if "freq" in name else None, print_input=True, - exit_solve_tolerance=DEFAULT_EXIT_SOLVE_TOL, extend_tracking=True, release_time_tolerance=0.2 if "tol" in name else None, ) diff --git a/autotest/test_prt_stop_zones.py b/autotest/test_prt_stop_zones.py index 0bc8d0d84e6..7de77878392 100644 --- a/autotest/test_prt_stop_zones.py +++ b/autotest/test_prt_stop_zones.py @@ -35,7 +35,6 @@ from framework import TestFramework from matplotlib.collections import LineCollection from prt_test_utils import ( - DEFAULT_EXIT_SOLVE_TOL, FlopyReadmeCase, check_budget_data, check_track_data, @@ -127,7 +126,6 @@ def build_prt_sim(name, gwf_ws, prt_ws, mf6): packagedata=FlopyReadmeCase.releasepts_prt, perioddata={0: ["FIRST"]}, istopzone=1, - exit_solve_tolerance=DEFAULT_EXIT_SOLVE_TOL, extend_tracking=True, ) diff --git a/autotest/test_prt_track_events.py b/autotest/test_prt_track_events.py index c83dc83b175..4ebfac77e5b 100644 --- a/autotest/test_prt_track_events.py +++ b/autotest/test_prt_track_events.py @@ -34,7 +34,6 @@ from flopy.utils.binaryfile import HeadFile from framework import TestFramework from prt_test_utils import ( - DEFAULT_EXIT_SOLVE_TOL, FlopyReadmeCase, check_budget_data, check_track_data, @@ -140,7 +139,6 @@ def build_prt_sim(name, gwf_ws, prt_ws, mf6): nreleasepts=len(releasepts_prt[grp]), packagedata=releasepts_prt[grp], perioddata={0: ["FIRST"]}, - exit_solve_tolerance=DEFAULT_EXIT_SOLVE_TOL, extend_tracking=True, ) for grp in ["a", "b"] diff --git a/autotest/test_prt_triangle.py b/autotest/test_prt_triangle.py index 2f7bdcc3e22..66b5c62989e 100644 --- a/autotest/test_prt_triangle.py +++ b/autotest/test_prt_triangle.py @@ -177,7 +177,6 @@ def build_prt_sim(idx, name, gwf_ws, prt_ws, targets): perioddata={0: ["FIRST"]}, boundnames=True, stop_at_weak_sink=True, # currently required for this problem - exit_solve_tolerance=1e-5, extend_tracking=True, ) prt_track_file = f"{prtname}.trk" diff --git a/autotest/test_prt_weak_sinks.py b/autotest/test_prt_weak_sinks.py index 78b08ac5223..cb822e1b3e1 100644 --- a/autotest/test_prt_weak_sinks.py +++ b/autotest/test_prt_weak_sinks.py @@ -34,7 +34,6 @@ from flopy.utils.binaryfile import HeadFile from framework import TestFramework from prt_test_utils import ( - DEFAULT_EXIT_SOLVE_TOL, FlopyReadmeCase, check_budget_data, check_track_data, @@ -97,7 +96,6 @@ def build_prt_sim(name, gwf_ws, prt_ws, mf6): packagedata=FlopyReadmeCase.releasepts_prt, perioddata={0: ["FIRST"]}, stop_at_weak_sink="saws" in name, - exit_solve_tolerance=DEFAULT_EXIT_SOLVE_TOL, extend_tracking=True, ) diff --git a/doc/ReleaseNotes/develop.tex b/doc/ReleaseNotes/develop.tex index 4c5e63e92cc..8c2cc6fb424 100644 --- a/doc/ReleaseNotes/develop.tex +++ b/doc/ReleaseNotes/develop.tex @@ -54,6 +54,7 @@ \item With a flow model using the Newton formulation, the PRT model could crash upon a particle's entry into a dry cell. This has been fixed. \item With a flow model using the Newton formulation, the PRT model could enter an endless loop upon a particle's entry to a dry cell if that cell contains a boundary package (e.g. a pumping well). Where the particle should be captured and terminate, it would instead be passed back and forth between the cell bottom and the top of the cell below. To avoid this, particles are forbidden from backtracking (reentering the previous cell) within the same time step. \item The PRT model now allows more control over vertical particle motion in dry conditions. In addition to the existing DRAPE option, which controls release-time behavior, the PRP package now provides a DRY\_TRACKING\_METHOD option which configures how dry particles (particles in dry cells, or above the water table in partially saturated cells) behave at tracking time. This option is relevant only when the Newton formulation is used, in which case dry cells remain active; otherwise, dry cells are inactive and particles will terminate. See the MF6IO document for a detailed explanation of DRY\_TRACKING\_METHOD. + \item The PRT model's Particle Release Point (PRP) package now provides an option EXIT\_SOLVE\_TOLERANCE which configures the tolerance to use when solving for a particle's exit location from an unstructured grid cell. This value is only used for the generalized (ternary) tracking method on vertex grids. A value of 0.00001 is set by default. This value works well for many problems, but the value that strikes the best balance between accuracy and runtime is problem-dependent. \end{itemize} %\underline{INTERNAL FLOW PACKAGES} diff --git a/doc/mf6io/mf6ivar/dfn/prt-prp.dfn b/doc/mf6io/mf6ivar/dfn/prt-prp.dfn index 5c12e990a4c..be094718487 100644 --- a/doc/mf6io/mf6ivar/dfn/prt-prp.dfn +++ b/doc/mf6io/mf6ivar/dfn/prt-prp.dfn @@ -30,9 +30,10 @@ block options name exit_solve_tolerance type double precision reader urword -optional false +optional true longname exit solve tolerance description the convergence tolerance for iterative solution of particle exit location and time in the generalized Pollock's method. A value of 0.00001 works well for many problems, but the value that strikes the best balance between accuracy and runtime is problem-dependent. +default_value 1e-5 block options name local_z diff --git a/src/Model/ParticleTracking/prt-prp.f90 b/src/Model/ParticleTracking/prt-prp.f90 index 96ebf145415..7c747915763 100644 --- a/src/Model/ParticleTracking/prt-prp.f90 +++ b/src/Model/ParticleTracking/prt-prp.f90 @@ -1,6 +1,6 @@ module PrtPrpModule use KindModule, only: DP, I4B, LGP - use ConstantsModule, only: DZERO, DEM1, DONE, LENFTYPE, LINELENGTH, & + use ConstantsModule, only: DZERO, DEM1, DEM5, DONE, LENFTYPE, LINELENGTH, & LENBOUNDNAME, LENPAKLOC, TABLEFT, TABCENTER, & MNORMAL, DSAME, DEP3, DEP9 use BndModule, only: BndType @@ -64,7 +64,6 @@ module PrtPrpModule real(DP), pointer :: offset => null() !< release time offset real(DP), pointer :: stoptime => null() !< stop time for all release points real(DP), pointer :: stoptraveltime => null() !< stop travel time for all points - logical(LGP), pointer :: foundtol => null() !< whether tolerance option was found integer(I4B), pointer, contiguous :: rptnode(:) => null() !< release point reduced nns integer(I4B), pointer, contiguous :: rptzone(:) => null() !< release point zone numbers real(DP), pointer, contiguous :: rptx(:) => null() !< release point x coordinates @@ -173,7 +172,6 @@ subroutine prp_da(this) call mem_deallocate(this%extol) call mem_deallocate(this%rttol) call mem_deallocate(this%rtfreq) - call mem_deallocate(this%foundtol) ! Deallocate arrays call mem_deallocate(this%rptx) @@ -263,7 +261,6 @@ subroutine prp_allocate_scalars(this) call mem_allocate(this%extol, 'EXTOL', this%memoryPath) call mem_allocate(this%rttol, 'RTTOL', this%memoryPath) call mem_allocate(this%rtfreq, 'RTFREQ', this%memoryPath) - call mem_allocate(this%foundtol, 'FOUNDTOL', this%memoryPath) ! Set values this%ilocalz = 0 @@ -284,10 +281,9 @@ subroutine prp_allocate_scalars(this) this%irlstls = 0 this%ifrctrn = 0 this%iexmeth = 0 - this%extol = DZERO + this%extol = DEM5 this%rttol = DSAME * DEP9 this%rtfreq = DZERO - this%foundtol = .false. end subroutine prp_allocate_scalars @@ -782,7 +778,6 @@ subroutine prp_options(this, option, found) if (this%extol <= DZERO) & call store_error('EXIT_SOLVE_TOLERANCE MUST BE POSITIVE') found = .true. - this%foundtol = .true. case ('RELEASE_TIME_TOLERANCE') this%rttol = this%parser%GetDouble() if (this%rttol <= DZERO) & @@ -832,9 +827,6 @@ subroutine prp_read_dimensions(this) integer(I4B) :: ierr logical :: isfound, endOfBlock - if (.not. this%foundtol) & - call store_error('EXIT_SOLVE_TOLERANCE MISSING, VALUE REQUIRED') - ! get dimension block call this%parser%GetBlock('DIMENSIONS', isfound, ierr, & supportOpenClose=.true.) diff --git a/src/Solution/ParticleTracker/mf6_6_0_prt_migration_guide.md b/src/Solution/ParticleTracker/mf6_6_0_prt_migration_guide.md new file mode 100644 index 00000000000..0e64dec5731 --- /dev/null +++ b/src/Solution/ParticleTracker/mf6_6_0_prt_migration_guide.md @@ -0,0 +1,211 @@ +# MODFLOW 6.6.0 Particle Tracking (PRT) Model Migration Guide + +This document describes how to migrate PRT models from MODFLOW 6.5.0 to 6.6.0. The particle tracker has evolved since the last release — while the basics remain the same, there are a few changes worthy of note and, in some cases, detailed explanation. + +Rather than describing bug fixes and new features separately, this document is structured by topic, and mentions relevant bug fixes within each topic section. See the release notes for a complete (though less detailed) list of fixes, some of which are unrelated to any topic described here. + + + + +- [Exit solve tolerance](#exit-solve-tolerance) +- [Release time selection](#release-time-selection) + - [Explicit selections](#explicit-selections) + - [Relative selections](#relative-selections) + - [Periodic releases](#periodic-releases) +- [Tracking time selection](#tracking-time-selection) +- [Extended tracking](#extended-tracking) +- [Vertical tracking](#vertical-tracking) + - [Release time](#release-time) + - [Tracking time](#tracking-time) + + + +## Exit solve tolerance + +The PRT model's Particle Release Point (PRP) package now provides an option `EXIT_SOLVE_TOLERANCE` which configures the tolerance to use when solving for a particle's exit location from an unstructured grid cell. This value is only used for the generalized (ternary) tracking method on vertex grids. + +A value of 0.00001 is set by default. This value works well for many problems, but the value that strikes the best balance between accuracy and runtime is problem-dependent. + +## Release time selection + +The PRT model's Particle Release Point (PRP) package is responsible for configuring when particles should be released. This can be done with explicitly provided release times, or with reference to stress periods and time steps. + +### Explicit selections + +Previously, explicit times were provided via a `RELEASE_TIMES` (or `RELEASE_TIMES_FILE`) variable. This pattern does not appear elsewhere in MODFLOW 6. To conform more closely to MODFLOW 6 input conventions, times must now be specified with list input, via a new `RELEASETIMES` block. The list has a single `time` column. A `DIMENSIONS` block containing an `NRELEASETIMES` variable must also be provided. + +For instance, a sample PRP file in the new format: + +``` +BEGIN options + PRINT_INPUT + EXIT_SOLVE_TOLERANCE 1.00000000E-05 + EXTEND_TRACKING +END options + +BEGIN dimensions + NRELEASEPTS 9 + NRELEASETIMES 2 +END dimensions + +BEGIN packagedata + 1 1 1 1 0.10000000 9.10000000 0.50000000 + 2 1 1 1 0.20000000 9.20000000 0.50000000 + 3 1 1 1 0.30000001 9.30000001 0.50000000 + 4 1 1 1 0.40000001 9.40000001 0.50000000 + 5 1 1 1 0.50000000 9.50000000 0.50000000 + 6 1 1 1 0.60000002 9.60000002 0.50000000 + 7 1 1 1 0.69999999 9.69999999 0.50000000 + 8 1 1 1 0.80000001 9.80000001 0.50000000 + 9 1 1 1 0.89999998 9.89999998 0.50000000 +END packagedata + +BEGIN releasetimes + 0.50000000 + 0.60000000 +END releasetimes +``` + +**Note**: List-based input leaves open the possibility to provide more information via the time selection mechanism in the future, by way of additional columns. + +### Relative selections + +MF6.5.0 provided a `FRACTION` option for period block release settings, which could be used to release particles at an offset from the beginning of any specified time steps. This option has been removed — particle release times configured with period block settings now occur at the beginning of the specified time steps, bringing the PRP package period block settings into conformity with the period block settings in the Output Control (OC) package. For fine-grained control over release times, specify them explicitly. + +### Periodic releases + +In MF6.6.0, the PRP package also provides a new option to configure release times at a regular time interval, `RELEASE_TIME_FREQUENCY` accepting a double precision value. + +## Tracking time selection + +Similarly, the PRT model's Output Control (OC) package allows selection of times for status reporting to occur. Previously, times were provided via a `TRACK_TIMES` (or `TRACK_TIMES_FILE`) variable. + +Like the PRP package, tracking times for the OC package must now be specified with list input, via a new `TRACKTIMES` block. The list has a single `time` column. A `DIMENSIONS` block containing an `NTRACKTIMES` variable must also be provided. + +A sample OC file: + +``` +BEGIN options + TRACK FILEOUT prt.trk + TRACKCSV FILEOUT prt.trk.csv + TRACK_USERTIME +END options + +BEGIN dimensions + NTRACKTIMES 1000 +END dimensions + +BEGIN tracktimes + 0.00000000 + 0.05000000 + 0.10000000 +... (truncated) +``` + +## Extended tracking + +To achieve the same behavior as with a MODFLOW 6.5.0 PRT model, the `EXTEND_TRACKING` keyword must be supplied to the PRP package. In MF6.5.0, PRT followed MODPATH 7 in "extending" particle tracking beyond the end of the simulation time, until all particles terminate, if the last stress period is steady state. While consistent with MP7, this means a particle tracking simulation may fail to halt if particles get caught in flow cycles. To guarantee termination under the default settings, MF6.6.0 introduces the `EXTEND_TRACKING` keyword to opt into tracking extension — otherwise particles will terminate at the simulation end time. + +## Vertical tracking + +This section describes the approach PRT takes to vertical particle motion. It is reproduced (sans diagrams) in the MF6IO guide's PRT chapter. + +The motion of a particle is determined by the groundwater velocity field in which the particle is immersed. In a fully saturated cell, or the saturated portion of a partially saturated cell, the velocity field is calculated from the flows entering and exiting the cell. In a completely dry cell, or the dry portion of a partially saturated cell, the fate of a particle depends on whether the cell is an active part of the flow simulation, whether the particle is in a dry or wet part of the cell, and user-selected options. + +A cell can be inactive either because it has been removed from the active simulation using the IDOMAIN array or because it is completely dry, i.e., the head in the cell is below the bottom elevation of the cell. Deactivation of completely dry cells is the default behavior in MODFLOW 6. However, when the Newton-Raphson formulation is used to solve for groundwater flow, completely dry cells remain active. + +Sometimes it is convenient to avoid "stranding" particles — rather than terminating dry particles, it is often convenient instead to move them down to the saturated zone and continue tracking. PRT allows particles (and indeed configures them by default) to move instantaneously down to the water table in dry conditions. This was the only supported behavior in MF6.5.0. With MF6.6.0, PRT continues to support this behavior by default, while providing a few alternatives. + +Release-time and tracking-time considerations are implemented (and thus described here) separately. + +**Legend** + +Diagrams use the following conventions. + +* Stadium-shaped boxes represent steps or processes. +* Square boxes represent outcomes. +* Diamond boxes represent conditions (i.e. runtime state). +* Round-corner boxes represent user options. +* Thin lines represent decisions made by the program on the basis of runtime state, e.g. particle, cell, flows. +* Thick lines represent decisions made by the user by way of options. +* Green outcome boxes indicate the particle remains active. +* Red outcome boxes indicate the particle terminates. + +```mermaid +flowchart LR + OPTION[Outcome] + OPTION(OPTION) ==> |Yes| STEP([Step]) + STEP --> ACTIVE + OPTION ==> |No| CONDITION{Condition} + CONDITION --> |Yes| TERMINATE + CONDITION --> |No| ACTIVE + ACTIVE[Active]:::active + TERMINATE[Termination]:::terminate + + classDef active stroke:#98fb98 + classDef terminate stroke:#f08080 +``` + +### Release time + +At release time, PRT decides whether to release each particle or terminate it unreleased. + +If the cell into which the particle is being released is inactive, behavior is determined by the `DRAPE` option. If the `DRAPE` option is enabled, the particle will be released from the top-most active cell beneath it, if any. If there is no active cell underneath the particle in any layer, or if `DRAPE` is not enabled, the particle will terminate unreleased (with status code 8). + +If the cell into which the particle is being released is active, the particle will be released at the user-specified location, even if that location is in the dry portion of the cell or the cell is dry-but-active. + +Note that for a dry-but-active cell the `DRAPE` option has no effect. In that case, the particle is released into the cell, and its subsequent behavior can be configured using the `DRY_TRACKING_METHOD` option discussed below. + +```mermaid +flowchart LR + ACTIVE --> |No| DRAPE(DRAPE) + ACTIVE{Cell active?} --> |Yes| RELEASE[Release in specified cell]:::release + DRAPE ==> |No| TERMINATE:::terminate + DRAPE ==> |Yes| ACTIVE_UNDER{Active under?} + ACTIVE_UNDER --> |Yes| RELEASE_AT_TABLE[Drape to active cell]:::release + ACTIVE_UNDER --> |No| TERMINATE[Terminate] + + classDef release stroke:#98fb98 + classDef terminate stroke:#f08080 +``` + +### Tracking time + +During tracking, the fate of a particle depends on the status of the cell that contains the particle, whether the particle is in a wet or dry part of the cell, and the `DRY_TRACKING_METHOD` option. + +A particle immersed in the groundwater flow field during a given time step can end up in an inactive cell, a dry-but-active cell, or the dry part of a partially saturated cell if the water table drops on the next time step. + +A particle that finds itself in an inactive cell will terminate with status code 7. This is consistent with the behavior of MODPATH 7. + +Dry-but-active cells can occur when the Newton-Raphson formulation is used to solve for groundwater flow. As discussed above, particles can be released into dry-but-active cells. + +A particle in a dry-but-active cell, or above the water table in a partially saturated cell, which we call a dry particle, need not terminate. The PRP package provides a `DRY_TRACKING_METHOD` option that determines how dry particles should behave. Supported values are `DROP` (the default), `STOP`, and `STAY`. + +If `DROP` is selected, or if a `DRY_TRACKING_METHOD` is unspecified, a dry particle is passed vertically and instantaneously to the water table (if the cell is partially saturated) or to the bottom of the cell (if the cell is dry). This repeats (i.e., the particle may drop through multiple cells) until it reaches the water table. Tracking then proceeds as usual. If the vertical column containing the particle is entirely dry, the particle will terminate upon reaching the bottom of the model grid. + +If `STOP` is selected, dry particles will be terminated. + +If `STAY` is selected, a dry particle will remain stationary until a) the water table rises and tracking can continue, b) the particle terminates due to reaching its `STOPTIME` or `STOPTRAVELTIME`, or c) the simulation ends. + +```mermaid +flowchart LR + ACTIVE{Cell active?} --> |No| TERMINATE{Terminate} + ACTIVE{Cell active?} --> |Yes| PARTICLE_DRY + PARTICLE_DRY{Particle dry?} --> |Yes| DRY_TRACKING_METHOD(DRY_TRACKING_METHOD) + DRY_TRACKING_METHOD ==> |STOP| TERMINATE[Terminate]:::terminate + DRY_TRACKING_METHOD ==> |DROP| CELL_DRY{Cell dry?} + CELL_DRY --> |Yes| DROP_BOTTOM[Pass to cell bottom]:::track + CELL_DRY --> |No| DROP_TABLE([Pass to water table]) + DRY_TRACKING_METHOD ==> |STAY| STAY[Stationary]:::track + DROP_TABLE --> TRACK:::track + PARTICLE_DRY --> |No| TRACK[Track normally] + + classDef track stroke:#98fb98 + classDef terminate stroke:#f08080 +``` + +**Note**: In MF6.5, behavior was as described by `DROP`, with one major exception: lack of an exit face (i.e. any face with outgoing flow) took precedence over cell saturation; a particle finding itself in a dry cell with no outgoing flow would previously terminate, where if `DROP` is selected (or a dry tracking method unspecified) the pass-to-bottom method will now be applied instead. + +With this change, it also becomes necessary to prohibit backtracking between vertically adjacent pairs of cells within the same time step, in order to avoid the possibility of infinite loops — a particle might otherwise be passed endlessly between e.g. the bottom face of a cell containing a pumping well and the top face of the cell below. Note that this limitation applies only to vertically adjacent cells, and only to the immediately previous cell — a particle may re-enter a cell after entering a third cell. + +A divide-by-zero crash has also been fixed for `gfortran`, which could occur upon a particle's entry into a dry cell in a structured grid. In MF6.5.0, this could prevent the particle from "dropping" properly. \ No newline at end of file diff --git a/src/Solution/ParticleTracker/vertical.md b/src/Solution/ParticleTracker/vertical.md deleted file mode 100644 index f0afbc109ba..00000000000 --- a/src/Solution/ParticleTracker/vertical.md +++ /dev/null @@ -1,133 +0,0 @@ -# Vertical tracking - -This document describes the approach PRT takes to vertical particle motion. - -## Legend - -Diagrams use the following conventions. - -* Stadium-shaped boxes represent steps or processes. -* Square boxes represent outcomes. -* Diamond boxes represent conditions (i.e. runtime state). -* Round-corner boxes represent user options. -* Thin lines represent decisions made by the program on the basis of runtime state, e.g. particle, cell, flows. -* Thick lines represent decisions made by the user by way of options. -* Green outcome boxes indicate the particle remains active. -* Red outcome boxes indicate the particle terminates. - -```mermaid -flowchart LR - OPTION[Outcome] - OPTION(OPTION) ==> |Yes| STEP([Step]) - STEP --> ACTIVE - OPTION ==> |No| CONDITION{Condition} - CONDITION --> |Yes| TERMINATE - CONDITION --> |No| ACTIVE - ACTIVE[Active]:::active - TERMINATE[Termination]:::terminate - - classDef active stroke:#98fb98 - classDef terminate stroke:#f08080 -``` - -## The problem - -When a particle is in the flow field, vertical motion can be solved in the same way as lateral motion. Special handling is necessary above the water table. - -A "dry" cell is either 1) an inactive cell or 2) an active-but-dry cell, as can occur with the Newton formulation. - -Normally, an inactive cell might be dry or explicitly disabled (idomain). With Newton, dry cells remain active. - -## The approach - -Release-time and tracking-time considerations are described (and implemented) separately. - -Each particle is either released into the simulation, or terminates unreleased. In the former case the particle's first record will be reason 0 (release), status 1 (active). In the latter reason 3 (termination), status 8 (permanently unreleased). - -At each time step, the PRT model applies the tracking method to each particle. The particle's trajectory is solved over the model grid until the end of the time step, or until the particle terminates (due e.g. to stop time or encountering a termination condition), whichever occurs first. - -Particles may traverse an arbitrary number of cells in a time step, in the lateral as well as vertical dimensions. - -Sometimes it is convenient to avoid "stranding" particles — rather than terminating dry particles, it is often convenient instead to move them down to the saturated zone and continue tracking. PRT allows particles (and indeed configures them by default) to move instantaneously down to the water table in dry conditions. - -### Release - -At release time, PRT decides whether to release each particle or to terminate it unreleased. - -If the release cell is active, the particle will be released at the specified coordinates. - -If the release cell is inactive, behavior is determined by the `DRAPE` option. If the `DRAPE` option is enabled, the particle will be "draped" down to and released from the top-most active cell beneath it, if any. If there is no active cell underneath the particle in any layer, or if `DRAPE` is not enabled, the particle will terminate unreleased (with status code 8). - -Since under the Newton formulation dry cells can remain active, the `DRAPE` option has no effect when Newton is on (assuming particles are not released into disabled grid regions). Vertical tracking behavior with Newton can be configured with tracking-time settings. - -```mermaid -flowchart LR - ACTIVE --> |No| DRAPE(DRAPE) - ACTIVE{Cell active?} --> |Yes| RELEASE[Release in specified cell]:::release - DRAPE ==> |No| TERMINATE:::terminate - DRAPE ==> |Yes| ACTIVE_UNDER{Active under?} - ACTIVE_UNDER --> |Yes| RELEASE_AT_TABLE[Drape to active cell]:::release - ACTIVE_UNDER --> |No| TERMINATE[Terminate] - - classDef release stroke:#98fb98 - classDef terminate stroke:#f08080 -``` - -### Tracking - -A particle might find itself above the water table for one of two reasons: - -1. It was released above the water table. - - With the Newton formulation, particles can be released into dry-but-active cells. - -2. The water table has receded. - - Particle trajectories are solved over the same time discretization used by the flow model. A particle may be immersed in the flow field in one time step, and find that the water table has dropped below it in the next. - -Tracking and termination decisions are made on the basis of information like - -1) a cell's active status -2) whether the cell is dry -3) whether the cell has outgoing flow across any face -4) whether the particle is dry (above the water table) -5) the particle's prior path - -A particle which finds itself in an inactive cell will terminate with status code 7. This is consistent with MODPATH 7's behavior. - -A particle in a dry-but-active cell, or above the water table in a partially saturated cell, need not terminate. We call such a particle dry. MODFLOW version 6.6.0 introduces a new option `DRY_TRACKING_METHOD` for the PRP package, determining how dry particles should behave. Supported values are: - -- `DROP` (default) -- `STOP` -- `STAY` - -If `DROP` is selected, or if a `DRY_TRACKING_METHOD` is unspecified, a particle in a dry position is passed vertically and instantaneously to the water table (if the cell is partially saturated) or to the bottom of the cell (if the cell is dry). This repeats (i.e. the particle may drop through multiple cells) until it reaches the water table. Tracking then proceeds as usual. - -**Note**: A divide-by-zero crash has been fixed for `gfortran`, which could occur upon a particle's entry into a dry cell in a structured grid. - -If `STOP` is selected, dry particles will be terminated. - -If `STAY` is selected, a dry particle will remain stationary until a) the water table rises and tracking can continue or b) the simulation ends. - -```mermaid -flowchart LR - ACTIVE{Cell active?} --> |No| TERMINATE{Terminate} - ACTIVE{Cell active?} --> |Yes| PARTICLE_DRY - PARTICLE_DRY{Particle dry?} --> |Yes| DRY_TRACKING_METHOD(DRY_TRACKING_METHOD) - DRY_TRACKING_METHOD ==> |STOP| TERMINATE[Terminate]:::terminate - DRY_TRACKING_METHOD ==> |DROP| CELL_DRY{Cell dry?} - CELL_DRY --> |Yes| DROP_BOTTOM[Pass to cell bottom]:::track - CELL_DRY --> |No| DROP_TABLE([Pass to water table]) - DRY_TRACKING_METHOD ==> |STAY| STAY[Stationary]:::track - DROP_TABLE --> TRACK:::track - PARTICLE_DRY --> |No| TRACK[Track normally] - - classDef track stroke:#98fb98 - classDef terminate stroke:#f08080 -``` - -#### Caveat - -In MF6.5, behavior was as described by `DROP`, with one major exception: lack of an exit face (i.e. any face with outgoing flow) took precedence over cell saturation; a particle finding itself in a dry cell with no outgoing flow would previously terminate, where if `DROP` is selected (or a dry tracking method unspecified) the pass-to-bottom method will now be applied instead. - -With this change, it also becomes necessary to prohibit backtracking between vertically adjacent pairs of cells within the same time step, in order to avoid the possibility of infinite loops — a particle might otherwise be passed endlessly between e.g. the bottom face of a cell containing a pumping well and the top face of the cell below. Note that this limitation applies only to vertically adjacent cells, and only to the immediately previous cell — a particle may re-enter a cell after entering a third cell. \ No newline at end of file