def run_one_step(self, dt, **kwds): """ Advance component by one time step of size dt. Parameters ---------- dt: float (time in seconds) The imposed timestep. """ # Calculate base gradient self._base_grad[ self._grid.active_links] = self._grid.calc_grad_at_link( self._base)[self._grid.active_links] # Calculate hydraulic gradient self._hydr_grad[ self._grid.active_links] = self._grid.calc_grad_at_link( self._thickness)[self._grid.active_links] # Calculate groundwater velocity self._vel[:] = -self._K * ( self._hydr_grad * np.cos(np.arctan(abs(self._base_grad))) + np.sin(np.arctan(self._base_grad))) self._vel[self._grid.status_at_link == INACTIVE_LINK] = 0.0 # Aquifer thickness at links (upwind) hlink = map_value_at_max_node_to_link(self._grid, "water_table__elevation", "aquifer__thickness") # Calculate specific discharge self._q[:] = hlink * self._vel # Groundwater flux divergence dqdx = self._grid.calc_flux_div_at_node(self._q) # Determine the relative aquifer thickness, 1 if permeable thickness is 0. soil_present = self._elev - self._base > 0.0 rel_thickness = np.ones_like(self._elev) rel_thickness[soil_present] = np.minimum( 1, self._thickness[soil_present] / (self._elev[soil_present] - self._base[soil_present]), ) # Calculate surface discharge at nodes self._qs[:] = _regularize_G( rel_thickness, self._r) * _regularize_R(self._recharge - dqdx) # Mass balance self._dhdt[:] = (1 / self._n) * (self._recharge - dqdx - self._qs) # Update self._thickness[self._cores] += self._dhdt[self._cores] * dt self._thickness[self._thickness < 0] = 0.0 # Recalculate water surface height self._wtable[self._cores] = (self._base[self._cores] + self._thickness[self._cores])
def run_one_step(self, dt): """Advance component by one time step of size dt. Parameters ---------- dt: float The imposed timestep. """ # check water table above surface if (self._wtable > self._elev).any(): warn("water table above elevation surface. " "Setting water table elevation here to " "elevation surface") self._wtable[self._wtable > self._elev] = self._elev[ self._wtable > self._elev] self._thickness[self._cores] = (self._wtable - self._base)[self._cores] # Calculate base gradient self._base_grad[ self._grid.active_links] = self._grid.calc_grad_at_link( self._base)[self._grid.active_links] cosa = np.cos(np.arctan(self._base_grad)) # Calculate hydraulic gradient self._hydr_grad[self._grid.active_links] = ( self._grid.calc_grad_at_link(self._wtable) * cosa)[self._grid.active_links] # Calculate groundwater velocity self._vel[:] = -self._K * self._hydr_grad # Aquifer thickness at links (upwind) hlink = (map_value_at_max_node_to_link( self._grid, "water_table__elevation", "aquifer__thickness") * cosa) # Calculate specific discharge self._q[:] = hlink * self._vel # Groundwater flux divergence dqdx = self._grid.calc_flux_div_at_node(self._q) # Regolith thickness reg_thickness = self._elev - self._base # update thickness from analytical self._thickness[self._cores] = _update_thickness( dt, self._thickness, reg_thickness, self._recharge, dqdx, self._n, self._r)[self._cores] self._thickness[self._thickness < 0] = 0.0 # Recalculate water surface height self._wtable[:] = self._base + self._thickness # Calculate surface discharge at nodes self._qs[:] = _regularize_G( self._thickness / reg_thickness, self._r) * _regularize_R(self._recharge - dqdx)
def run_with_adaptive_time_step_solver(self, dt): """ Advance component by one time step of size dt, subdividing the timestep into substeps as necessary to meet stability conditions. Note this method returns the fluxes at the last substep, but also returns a new field, average_surface_water__specific_discharge, that is averaged over all subtimesteps. To return state during substeps, provide a callback_fun. Parameters ---------- dt: float The imposed timestep. """ # check water table above surface if (self._wtable > self._elev).any(): warn("water table above elevation surface. " "Setting water table elevation here to " "elevation surface") self._wtable[self._wtable > self._elev] = self._elev[ self._wtable > self._elev] self._thickness[self._cores] = (self._wtable[self._cores] - self._base[self._cores]) # Calculate base gradient self._base_grad[ self._grid.active_links] = self._grid.calc_grad_at_link( self._base)[self._grid.active_links] cosa = np.cos(np.arctan(self._base_grad)) # Initialize reg_thickness, rel_thickness reg_thickness = self._elev - self._base soil_present = reg_thickness > 0.0 rel_thickness = np.ones_like(self._elev) # Initialize for average surface discharge qs_cumulative = np.zeros_like(self._elev) # Initialize variable timestep remaining_time = dt self._num_substeps = 0 while remaining_time > 0.0: # Calculate hydraulic gradient self._hydr_grad[self._grid.active_links] = ( self._grid.calc_grad_at_link( self._wtable)[self._grid.active_links] * cosa[self._grid.active_links]) # Calculate groundwater velocity self._vel[:] = -self._K * self._hydr_grad self._vel[self._grid.status_at_link == LinkStatus.INACTIVE] = 0.0 # Aquifer thickness at links (upwind) hlink = (map_value_at_max_node_to_link( self._grid, "water_table__elevation", "aquifer__thickness") * cosa) # Calculate specific discharge self._q[:] = hlink * self._vel # Groundwater flux divergence dqdx = self._grid.calc_flux_div_at_node(self._q) # calculate relative thickness rel_thickness[soil_present] = np.minimum( 1, self._thickness[soil_present] / (reg_thickness[soil_present])) # Calculate surface discharge at nodes self._qs[:] = _regularize_G( rel_thickness, self._r) * _regularize_R(self._recharge - dqdx) # Mass balance self._dhdt[:] = (1 / self._n) * (self._recharge - self._qs - dqdx) # calculate criteria for timestep self._dt_vn = self._vn_coefficient * min( self._n_link * self._grid.length_of_link**2 / (4 * self._K * hlink)) self._dt_courant = self._courant_coefficient * min( self._grid.length_of_link / abs(self._vel / self._n_link)) dt_stability = min(self._dt_courant, self._dt_vn) substep_dt = min([dt_stability, remaining_time]) # Update self._thickness[ self._cores] += self._dhdt[self._cores] * substep_dt self._thickness[self._thickness < 0] = 0.0 # Recalculate water surface height self._wtable[self._cores] = (self._base + self._thickness)[self._cores] # add cumulative sw discharge in substeps qs_cumulative += self._qs * substep_dt # calculate the time remaining and advance count of substeps remaining_time -= substep_dt self._num_substeps += 1 if self._old_style_callback: self._callback_fun(self._grid, substep_dt, **self._callback_kwds) else: self._callback_fun(self._grid, self.recharge, substep_dt, **self._callback_kwds) self._qsavg[:] = qs_cumulative / dt
def run_one_step(self, dt): """Advance component by one time step of size dt. Parameters ---------- dt: float The imposed timestep. """ # check water table above surface if (self._wtable > self._elev).any(): warn("water table above elevation surface. " "Setting water table elevation here to " "elevation surface") self._wtable[self._wtable > self._elev] = self._elev[ self._wtable > self._elev] self._thickness[self._cores] = (self._wtable[self._cores] - self._base[self._cores]) # Calculate base gradient self._base_grad[ self._grid.active_links] = self._grid.calc_grad_at_link( self._base)[self._grid.active_links] cosa = np.cos(np.arctan(self._base_grad)) # Calculate hydraulic gradient self._hydr_grad[self._grid.active_links] = ( self._grid.calc_grad_at_link(self._wtable)[self._grid.active_links] * cosa[self._grid.active_links]) # Calculate groundwater velocity self._vel[:] = -self._K * self._hydr_grad self._vel[self._grid.status_at_link == LinkStatus.INACTIVE] = 0.0 # Aquifer thickness at links (upwind) hlink = (map_value_at_max_node_to_link( self._grid, "water_table__elevation", "aquifer__thickness") * cosa) # Calculate specific discharge self._q[:] = hlink * self._vel # Groundwater flux divergence dqdx = self._grid.calc_flux_div_at_node(self._q) # Determine the relative aquifer thickness, 1 if permeable thickness is 0. soil_present = (self._elev - self._base) > 0.0 rel_thickness = np.ones_like(self._elev) rel_thickness[soil_present] = np.minimum( 1, self._thickness[soil_present] / (self._elev[soil_present] - self._base[soil_present]), ) # Calculate surface discharge at nodes self._qs[:] = _regularize_G( rel_thickness, self._r) * _regularize_R(self._recharge - dqdx) # Mass balance self._dhdt[:] = (1 / self._n) * (self._recharge - self._qs - dqdx) # Update self._thickness[self._cores] += self._dhdt[self._cores] * dt self._thickness[self._thickness < 0] = 0.0 # Recalculate water surface height self._wtable[self._cores] = (self._base + self._thickness)[self._cores]
def run_with_adaptive_time_step_solver(self, dt, courant_coefficient=0.01, **kwds): """ Advance component by one time step of size dt, subdividing the timestep into substeps as necessary to meet a Courant condition. Note this method only returns the fluxes at the last subtimestep. Parameters ---------- dt: float (time in seconds) The imposed timestep. courant_coefficient: float (-) The muliplying factor on the condition that the timestep is smaller than the minimum link length over groundwater flow velocity """ remaining_time = dt self._num_substeps = 0 while remaining_time > 0.0: # Calculate base gradient self._base_grad[ self._grid.active_links] = self._grid.calc_grad_at_link( self._base)[self._grid.active_links] # Calculate hydraulic gradient self._hydr_grad[ self._grid.active_links] = self._grid.calc_grad_at_link( self._thickness)[self._grid.active_links] # Calculate groundwater velocity self._vel[:] = -self._K * ( self._hydr_grad * np.cos(np.arctan(abs(self._base_grad))) + np.sin(np.arctan(self._base_grad))) self._vel[self._grid.status_at_link == INACTIVE_LINK] = 0.0 # Aquifer thickness at links (upwind) hlink = map_value_at_max_node_to_link(self._grid, "water_table__elevation", "aquifer__thickness") # Calculate specific discharge self._q[:] = hlink * self._vel # Groundwater flux divergence dqdx = self._grid.calc_flux_div_at_node(self._q) # Determine the relative aquifer thickness, 1 if permeable thickness is 0. soil_present = self._elev - self._base > 0.0 rel_thickness = np.ones_like(self._elev) rel_thickness[soil_present] = np.minimum( 1, self._thickness[soil_present] / (self._elev[soil_present] - self._base[soil_present]), ) # Calculate surface discharge at nodes self._qs[:] = _regularize_G( rel_thickness, self._r) * _regularize_R(self._recharge - dqdx) # Mass balance self._dhdt[:] = (1 / self._n) * (self._recharge - dqdx - self._qs) # calculate criteria for timestep max_vel = max(abs(self._vel / self._n_link)) grid_dist = min(self._grid.length_of_link) substep_dt = np.nanmin( [courant_coefficient * grid_dist / max_vel, remaining_time]) # Update self._thickness[ self._cores] += self._dhdt[self._cores] * substep_dt self._thickness[self._thickness < 0] = 0.0 # Recalculate water surface height self._wtable[self._cores] = (self._base[self._cores] + self._thickness[self._cores]) # calculate the time remaining and advance count of substeps remaining_time -= substep_dt self._num_substeps += 1
def run_with_adaptive_time_step_solver(self, dt): """ Advance component by one time step of size dt, subdividing the timestep into substeps as necessary to meet a Courant condition. Note this method only returns the fluxes at the last subtimestep. Parameters ---------- dt: float (time in seconds) The imposed timestep. """ # check water table above surface if (self._wtable > self._elev).any(): self._wtable[self._wtable > self._elev] = self._elev[ self._wtable > self._elev] self._thickness[self._cores] = (self._wtable[self._cores] - self._base[self._cores]) # Calculate base gradient self._base_grad[ self._grid.active_links] = self._grid.calc_grad_at_link( self._base)[self._grid.active_links] cosa = np.cos(np.arctan(self._base_grad)) # Initialize reg_thickness, rel_thickness reg_thickness = self._elev - self._base soil_present = reg_thickness > 0.0 rel_thickness = np.ones_like(self._elev) # Initialize for average surface discharge qs_cumulative = np.zeros_like(self._elev) # Initialize variable timestep remaining_time = dt self._num_substeps = 0 while remaining_time > 0.0: # Calculate hydraulic gradient self._hydr_grad[self._grid.active_links] = ( self._grid.calc_grad_at_link( self._wtable)[self._grid.active_links] * cosa[self._grid.active_links]) # Calculate groundwater velocity self._vel[:] = -self._K * self._hydr_grad self._vel[self._grid.status_at_link == LinkStatus.INACTIVE] = 0.0 # Aquifer thickness at links (upwind) hlink = (map_value_at_max_node_to_link( self._grid, "water_table__elevation", "aquifer__thickness") * cosa) # Calculate specific discharge self._q[:] = hlink * self._vel # Groundwater flux divergence dqdx = self._grid.calc_flux_div_at_node(self._q) # calculate relative thickness rel_thickness[soil_present] = np.minimum( 1, self._thickness[soil_present] / (reg_thickness[soil_present])) # Calculate surface discharge at nodes self._qs[:] = _regularize_G( rel_thickness, self._r) * _regularize_R(self._recharge - dqdx) # Mass balance self._dhdt[:] = (1 / self._n) * (self._recharge - self._qs - dqdx) # calculate criteria for timestep max_vel = max(abs(self._vel / self._n_link)) grid_dist = min(self._grid.length_of_link) substep_dt = np.nanmin([ self._courant_coefficient * grid_dist / max_vel, remaining_time ]) # Update self._thickness[ self._cores] += self._dhdt[self._cores] * substep_dt self._thickness[self._thickness < 0] = 0.0 # Recalculate water surface height self._wtable[self._cores] = (self._base + self._thickness)[self._cores] # add cumulative sw discharge in substeps qs_cumulative += self._qs * substep_dt # calculate the time remaining and advance count of substeps remaining_time -= substep_dt self._num_substeps += 1 self._qsavg[:] = qs_cumulative / dt
def run_with_adaptive_time_step_solver(self, dt): """ Advance component by one time step of size dt, subdividing the timestep into substeps as necessary to meet stability conditions. Note this method returns the fluxes at the last substep, but also returns a new field, average_surface_water__specific_discharge, that is averaged over all subtimesteps. To return state during substeps, provide a callback_fun. Parameters ---------- dt: float The imposed timestep. """ # check water table above surface if (self._wtable > self._elev).any(): warn( "water table above elevation surface. " "Setting water table elevation here to " "elevation surface" ) self._wtable[self._wtable > self._elev] = self._elev[ self._wtable > self._elev ] self._thickness[self._cores] = (self._wtable - self._base)[self._cores] # Calculate base gradient self._base_grad[self._grid.active_links] = self._grid.calc_grad_at_link( self._base )[self._grid.active_links] cosa = np.cos(np.arctan(self._base_grad)) # Initialize reg_thickness reg_thickness = self._elev - self._base # Initialize for average surface discharge qs_cumulative = np.zeros_like(self._elev) # Initialize variable timestep remaining_time = dt self._num_substeps = 0 while remaining_time > 0.0: # Calculate hydraulic gradient self._hydr_grad[self._grid.active_links] = ( self._grid.calc_grad_at_link(self._wtable) * cosa )[self._grid.active_links] # Calculate groundwater velocity self._vel[:] = -self._K * self._hydr_grad # Aquifer thickness at links (upwind) hlink = ( map_value_at_max_node_to_link( self._grid, "water_table__elevation", "aquifer__thickness" ) * cosa ) # Calculate specific discharge self._q[:] = hlink * self._vel # Groundwater flux divergence dqdx = self._grid.calc_flux_div_at_node(self._q) # calculate criteria for timestep dt_vn = self._vn_coefficient * np.min( np.divide( (self._n_link * self._grid.length_of_link ** 2), (4 * self._K * hlink), where=hlink > 0, out=np.ones_like(self._q) * 1e15, ) ) dt_courant = self._courant_coefficient * np.min( np.divide( self._grid.length_of_link, abs(self._vel / self._n_link), where=abs(self._vel) > 0, out=np.ones_like(self._q) * 1e15, ) ) substep_dt = min([dt_courant, dt_vn, remaining_time]) # print(np.argmin(np.array([self._dt_courant, self._dt_vn, remaining_time]))) # 0 = courant limited, 1 = vn limited, 2 = not limited # update thickness from analytical self._thickness[self._cores] = _update_thickness( substep_dt, self._thickness, reg_thickness, self._recharge, dqdx, self._n, self._r, )[self._cores] self._thickness[self._thickness < 0] = 0.0 # Recalculate water surface height self._wtable[:] = self._base + self._thickness # Calculate surface discharge at nodes self._qs[:] = _regularize_G( self._thickness / reg_thickness, self._r ) * _regularize_R(self._recharge - dqdx) # add cumulative sw discharge in substeps qs_cumulative += self._qs * substep_dt # calculate the time remaining and advance count of substeps remaining_time -= substep_dt self._num_substeps += 1 # run callback function if supplied self._callback_fun( self._grid, self._recharge, substep_dt, **self._callback_kwds ) self._qsavg[:] = qs_cumulative / dt
rmg.set_watershed_boundary_condition(z) # Set spatially variable diffusivity for soil creep df = pd.Series(data=np.array(rmg.at_node['terrace_wall__location']), name="walls") df1 = df.to_frame(name="walls") df1['Kd'] = '0' df1.loc[df1.walls == 1., 'Kd'] = 0. df1.loc[df1.walls == 0., 'Kd'] = 1.0 dfList = df1['Kd'].tolist() Kd = np.array(dfList) # Add Kd to rmg rmg.add_field('node', 'Kd', Kd) # Set links to kd value for uphill nodes maxField= map_value_at_max_node_to_link(rmg, 'topographic__elevation', 'Kd') rmg.add_field('link', 'Kd_maxField', maxField) # Set uplift rate uplift_rate = 0.0001 # [m/yr] # Initialize linear diffuser ld = LinearDiffuser(rmg, linear_diffusivity='Kd_maxField') # Set up LEM def run_LEM(years): for i in range(years): # Soil creep ld.run_one_step(1.) # Uplift