Coverage for C:\src\imod-python\imod\wq\dis.py: 17%

48 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-08 10:26 +0200

1import jinja2 

2 

3from imod.wq.pkgbase import Package 

4 

5 

6class TimeDiscretization(Package): 

7 """ 

8 Time discretisation package class. 

9 

10 Parameters 

11 ---------- 

12 timestep_duration: float 

13 is the length of the current stress period (PERLEN). If the flow 

14 solution is transient, timestep_duration specified here must be equal to 

15 that specified for the flow model. If the flow solution is steady-state, 

16 timestep_duration can be set to any desired length. 

17 n_timesteps: int, optional 

18 is the number of time steps for the transient flow solution in the 

19 current stress period (NSTP). If the flow solution is steady-state, 

20 n_timestep=1. Default value is 1. 

21 transient: bool, optional 

22 Flag indicating wether the flow simulation is transient (True) or False 

23 (Steady State). 

24 Default is True. 

25 timestep_multiplier: float, optional 

26 is the multiplier for the length of successive time steps used in the 

27 transient flow solution (TSMULT); it is used only if n_timesteps>1. 

28 timestep_multiplier>0, the length of each flow time step within the 

29 current stress period is calculated using the geometric progression as 

30 in MODFLOW. Note that both n_timesteps and timestep_multiplier specified 

31 here must be identical to those specified in the flow model if the flow 

32 model is transient. 

33 timestep_multiplier ≤ 0, the length of each flow time step within the 

34 current stress period is read from the record TSLNGH. This option is 

35 needed in case the length of time steps for the flow solution is not 

36 based on a geometric progression in a flow model, unlike MODFLOW. 

37 Default is 1.0. 

38 max_n_transport_timestep: int, optional 

39 is the maximum number of transport steps allowed within one time step of 

40 the flow solution (mxstrn). If the number of transport steps within a 

41 flow time step exceeds max_n_transport_timestep, the simulation is 

42 terminated. 

43 Default is 50_000. 

44 transport_timestep_multiplier: float or {"None"}, optional 

45 is the multiplier for successive transport steps within a flow time step 

46 (TTSMULT). 

47 If the Generalized Conjugate Gradient (GCG) solver is used and the 

48 solution option for the advection term is the standard finite difference 

49 method. A value between 1.0 and 2.0 is generally adequate. If the GCG 

50 package is not used, the transport solution is solved explicitly as in 

51 the original MT3D code, and transport_timestep_multiplier is always set 

52 to 1.0 regardless of the user-specified input. Note that for the 

53 particle tracking based solution options and the 3rd-order TVD scheme, 

54 transport_timestep_multiplier does not apply. 

55 Default is {"None"}. 

56 transport_initial_timestep: int, optional 

57 is the user-specified transport stepsize within each time step of the 

58 flow solution (DT0). 

59 transport_initial_timestep is interpreted differently depending on 

60 whether the solution option chosen is explicit or implicit: For explicit 

61 solutions (i.e., the GCG solver is not used), the program will always 

62 calculate a maximum transport stepsize which meets the various stability 

63 criteria. Setting transport_initial_timestep to zero causes the model 

64 calculated transport stepsize to be used in the simulation. However, the 

65 model-calculated transport_initial_timestep may not always be optimal. 

66 In this situation, transport_initial_timestep should be adjusted to find 

67 a value that leads to the best results. If transport_initial_timestep is 

68 given a value greater than the model-calculated stepsize, the 

69 model-calculated stepsize, instead of the user-specified value, will be 

70 used in the simulation. 

71 For implicit solutions (i.e., the GCG solver is used), 

72 transport_initial_timestep is the initial transport stepsize. If it is 

73 specified as zero, the model-calculated value of 

74 transport_initial_timestep, based on the user-specified Courant number 

75 in the Advection Package, will be used. The subsequent transport 

76 stepsize may increase or remain constant depending on the userspecified 

77 transport stepsize multiplier transport_timestep_multiplier and the 

78 solution scheme for the advection term. 

79 Default is 0. 

80 """ 

81 

82 _pkg_id = "dis" 

83 

84 def __init__( 

85 self, 

86 timestep_duration, 

87 n_timesteps=1, 

88 transient=True, 

89 timestep_multiplier=1.0, 

90 max_n_transport_timestep=50_000, 

91 transport_timestep_multiplier=None, 

92 transport_initial_timestep=0.0, 

93 ): 

94 super().__init__() 

95 self["timestep_duration"] = timestep_duration 

96 self["n_timesteps"] = n_timesteps 

97 self["transient"] = transient 

98 self["timestep_multiplier"] = timestep_multiplier 

99 self["max_n_transport_timestep"] = max_n_transport_timestep 

100 if transport_timestep_multiplier is not None: 

101 self["transport_timestep_multiplier"] = transport_timestep_multiplier 

102 self["transport_initial_timestep"] = transport_initial_timestep 

103 

104 def _render(self, globaltimes): 

105 d = {} 

106 dicts = {} 

107 _dis_mapping = ( 

108 ("perlen", "timestep_duration"), 

109 ("nstp", "n_timesteps"), 

110 ("sstr", "transient"), 

111 ("tsmult", "timestep_multiplier"), 

112 ) 

113 d["mapping"] = _dis_mapping 

114 datavars = [t[1] for t in _dis_mapping] 

115 for varname in datavars: 

116 dicts[varname] = self._compose_values_time(varname, globaltimes) 

117 if varname == "transient": 

118 for k, v in dicts[varname].items(): 

119 if v == 1: 

120 dicts[varname][k] = "tr" 

121 else: 

122 dicts[varname][k] = "ss" 

123 d["dicts"] = dicts 

124 d["n_periods"] = len(globaltimes) 

125 

126 _dis_template = jinja2.Template( 

127 "\n" 

128 " nper = {{n_periods}}\n" 

129 " {%- for name, dictname in mapping -%}" 

130 " {%- for time, value in dicts[dictname].items() %}\n" 

131 " {{name}}_p{{time}} = {{value}}" 

132 " {%- endfor -%}" 

133 " {%- endfor -%}" 

134 ) 

135 

136 return _dis_template.render(d) 

137 

138 def _render_btn(self, globaltimes): 

139 d = {} 

140 dicts = {} 

141 # TODO: check what's necessary 

142 _btn_mapping = ( 

143 # ("perlen", "duration"), # should not be necessary 

144 # ("nstp", "n_timesteps"), # should not be necessary 

145 ("tsmult", "timestep_multiplier"), 

146 # ("tslngh", "timestep_length"), 

147 ("dt0", "transport_initial_timestep"), 

148 ("ttsmult", "transport_timestep_multiplier"), 

149 ("mxstrn", "max_n_transport_timestep"), 

150 ) 

151 _btn_template = jinja2.Template( 

152 " {%- for name, dictname in mapping -%}" 

153 " {%- for time, value in dicts[dictname].items() %}\n" 

154 " {{name}}_p{{time}} = {{value}}" 

155 " {%- endfor -%}" 

156 " {%- endfor -%}" 

157 ) 

158 mapping = tuple( 

159 [(k, v) for k, v in _btn_mapping if v in self.dataset.data_vars] 

160 ) 

161 d["mapping"] = mapping 

162 datavars = [t[1] for t in mapping] 

163 for varname in datavars: 

164 dicts[varname] = self._compose_values_time(varname, globaltimes) 

165 d["dicts"] = dicts 

166 return _btn_template.render(d) 

167 

168 def _pkgcheck(self, ibound=None): 

169 to_check = [ 

170 "timestep_duration", 

171 "n_timesteps", 

172 "transient", 

173 "timestep_multiplier", 

174 "max_n_transport_timestep", 

175 "transport_initial_timestep", 

176 ] 

177 if "transport_timestep_multiplier" in self.dataset.data_vars: 

178 to_check.append("transport_timestep_multiplier") 

179 self._check_positive(to_check)