Coverage for C:\src\imod-python\imod\mf6\adv.py: 91%

1"""

2 When simulating transport, MODFLOW6 needs to compute the concentration at a

3 cellface between 2 adjacent cells. It supports 3 ways of doing this. Each of

4 those has its own wrapper class. These numerical schemes differ in how much

5 numerical dispersion they cause, how much oscillations, and how timestep and

6 grid size affect stability. Central-in-space weighting is not often used

7 because it can result in spurious oscillations in the simulated concentrations.

8 Upstream weighting is a fast alternative, and TVD is a more expensive and more

9 robust alternative.

10"""

11

12 from copy import deepcopy

13 from typing import Optional, Tuple

14

15 from imod.mf6.interfaces.iregridpackage import IRegridPackage

16 from imod.mf6.package import Package

17 from imod.mf6.utilities.regrid import RegridderType

18

19

20 class Advection(Package, IRegridPackage):

21 _pkg_id = "adv"

22 _template = Package._initialize_template(_pkg_id)

23 _regrid_method: dict[str, tuple[RegridderType, str]] = {}

24

25 def __init__(self, scheme: str):

26 dict_dataset = {"scheme": scheme}

27 super().__init__(dict_dataset)

28

29 def render(self, directory, pkgname, globaltimes, binary):

30 scheme = self.dataset["scheme"].item()

31 return self._template.render({"scheme": scheme})

32

33 def mask(self, _) -> Package:

34 """

35 The mask method is irrelevant for this package , instead this method

36 retuns a copy of itself.

37 """

38 return deepcopy(self)

39

40 def get_regrid_methods(self) -> Optional[dict[str, Tuple[RegridderType, str]]]:

41 return self._regrid_method

42

43

44 class AdvectionUpstream(Advection):

45 """

46 The upstream weighting (first order upwind) scheme sets the concentration

47 at the cellface between two adjacent cells equal to the concentration in

48 the cell where the flow comes from. It surpresses oscillations.

49 Note: all constructor arguments will be ignored

50 """

51

52 def __init__(self, scheme: str = "upstream"):

53 if not scheme == "upstream":

54 raise ValueError(

55 "error in scheme parameter. Should be 'upstream' if present."

56 )

57 super().__init__(scheme="upstream")

58

59

60 class AdvectionCentral(Advection):

61 """

62 The central-in-space weighting scheme is based on a simple

63 distance-weighted linear interpolation between the center of cell n and the

64 center of cell m to calculate solute concentration at the shared face

65 between cell n and cell m. Although central-in-space is a misnomer for

66 grids without equal spacing between connected cells, it is retained here

67 for consistency with nomenclature used by other MODFLOW-based transport

68 programs, such as MT3D.

69 Note: all constructor arguments will be ignored

70 """

71

72 def __init__(self, scheme: str = "central"):

73 if not scheme == "central":

74 raise ValueError(

75 "error in scheme parameter. Should be 'central' if present."

76 )

77 super().__init__(scheme="central")

78

79

80 class AdvectionTVD(Advection):

81 """

82 An implicit second order TVD scheme. More expensive than upstream

83 weighting but more robust.

84 Note: all constructor arguments will be ignored

85 """

86

87 def __init__(self, scheme: str = "TVD"):

88 if not scheme == "TVD":

89 raise ValueError("error in scheme parameter. Should be 'TVD' if present.")

90 super().__init__(scheme="TVD")