def createAbsorptionLayerFunction(x, absorption_zone=300 * U.m, absorption_cut=1.e-2, top_absorption=False): """ Creates a distribution which is one in the interior of the domain of `x` and is falling down to the value 'absorption_cut' over a margin of thickness 'absorption_zone' toward each boundary except the top of the domain. :param x: location of points in the domain :type x: `escript.Data` :param absorption_zone: thickness of the absorption zone :param absorption_cut: value of decay function on domain boundary :return: function on 'x' which is one in the iterior and decays to almost zero over a margin toward the boundary. """ if absorption_zone is None or absorption_zone == 0: return 1 dom = x.getDomain() bb = escript.boundingBox(dom) DIM = dom.getDim() decay = -escript.log(absorption_cut) / absorption_zone**2 f = 1 for i in range(DIM): x_i = x[i] x_l = x_i - (bb[i][0] + absorption_zone) m_l = escript.whereNegative(x_l) f = f * ((escript.exp(-decay * (x_l * m_l)**2) - 1) * m_l + 1) if top_absorption or not DIM - 1 == i: x_r = (bb[i][1] - absorption_zone) - x_i m_r = escript.whereNegative(x_r) f = f * ((escript.exp(-decay * (x_r * m_r)**2) - 1) * m_r + 1) return f
def getWeightingFactor(self, x, wx0, x0, eta): """ returns the weighting factor """ try: origin, spacing, NE = x.getDomain().getGridParameters() cell = [int((x0[i] - origin[i]) / spacing[i]) for i in range(2)] midpoint = [ origin[i] + cell[i] * spacing[i] + spacing[i] / 2. for i in range(2) ] return wx0 * escript.whereNegative( escript.length(x - midpoint) - eta) except: return wx0 * escript.whereNegative(escript.length(x - x0) - eta)
def out(self): """ Generate the Gaussian profile Link against this method to get the output of this model. """ x = self.domain.getX() dim = self.domain.getDim() l = length(x - self.x_c[:dim]) m = whereNegative(l - self.r) return (m + (1. - m) * exp(-log(2.) * (l / self.width)**2)) * self.A
def out(self): """ Generate the Gaussian profile Link against this method to get the output of this model. """ x = self.domain.getX() dim = self.domain.getDim() l = length(x-self.x_c[:dim]) m = whereNegative(l-self.r) return (m+(1.-m)*exp(-log(2.)*(l/self.width)**2))*self.A
def iterate_coupled_flow_eqs(mesh, topo_gradient, pressure_pde, solute_pde, pressure_convergence_criterion, concentration_convergence_criterion, min_iterations, max_iterations, dt, g_vector, pressure, concentration, rho_f, phi, diffusivity, l_disp, t_disp, solute_source, specific_storage, k_tensor, k_vector, dispersion_tensor, viscosity, gamma, alpha, fluid_source, rho_f_0, specified_pressure_bnd, specified_pressure, specified_concentration_bnd, specified_concentration, specified_concentration_rho_f, rch_bnd_loc, recharge_mass_flux, coupled_iterations=True, solute_transport=True, heat_transport=False, steady_state=False, proj=None, drain_loc=None, seepage_bnd=False, recalculate_seepage_bnd=True, active_seepage_bnd=None, concentration_bnd_inflow_only=False, concentration_bnd_inflow_direction='up', max_allowed_CFL_number=None, force_CFL_timestep=False, dt_max=None, calculate_viscosity=False, verbose=False, iterate_seepage_in_one_timestep=False, max_seepage_iterations=50, ignore_convergence_failure=False): """ Iterative solve groundwater flow, solute transport and heat flow equations. solves either steady state or 1 timestep in implicit or explicit mode iterative coupling scheme of solute transport, pressure & flow eqs. and eqs of state follows Ackerer (2004), Geophysical Research Letters 31(12) Parameters --------- mesh : escript mesh object pressure_pde : groundwater flow PDE solute_pde solute transport PDE pressure_convergence_criterion : float convergence criterion groundwater flow eq. (Pa) concentration_convergence_criterion : float convergence criterion solute transport eq. (kg/kg) max_iterations : int max number of iterations dt : int timestep g_vector : gravity vector (0,g) pressure : pressure (Pa) concentration : solute concentration (kg/kg) rho_f : fluid density (kg / m3) phi : porosity D : solute diffusivity (...) l_disp : longitudinal dispersivity (...) t_disp : transverse dispersivity (...) solute_source : solute source (units...) specific_storage : specific storativity (...) k : permeability (m2) anisotropy : permeability anisotropy = horizontal/vertical permeability (dimensionless) viscosity : viscosity (...) gamma : ? alpha : ? fluid_source : fluid source term (...) rho_f_0 fluid density at solute concentration C=0 (kg/m3) specified_pressure_bnd location of specified pressure boundary specified_pressure specified pressure (Pa) specified_concentration_bnd location of specified concentration boundary specified_concentration specified concentration (kg/kg) rch_bnd_loc : recharge_mass_flux : float coupled_iterations : bool, optional couple groundwater and solute transport equations iteratively by adjusting density term solute_transport : bool, optional if True, simulate solute transport heat_transport : bool, optional if True, simulate heat transport steady_state : bool, optional True for steady state groundwater flow, False for transient verbose : bool, optional verbose text output drain_loc : location of drain boundary nodes debug : bool, optional debugging dt_max : float? =None ... proj : escript PDE for projecting element data to nodes seepage_optimization_automated : boolean Returns ------- pressure_t2_i2 : pressure at next timestep (t2) and last iteration (i2) concentration_t2_i2 : solute concentration (kg/kg) rho_f_t2_i2 : fluid density iteration : int number of iterations dt_max : max timestep size """ # calculate transverse dispersivity #t_disp = l_disp * disp_ratio year = 365.25 * 24 * 60 * 60. if verbose is True: print('running iterative solver for pressure and concentration PDEs') if coupled_iterations is False: print('pressure and concentration are not coupled') #pressure_new = pressure pressure_old_ts = pressure concentration_old_ts = concentration fluid_density_new = fluid_density_old = rho_f #pressure_t1 = pressure.copy() #concentration_t1 = concentration.copy() # added 22 jun 2016, not sure if this is ok: active_rch_bnd = rch_bnd_loc if coupled_iterations is True and calculate_viscosity is True: viscosity_new = calculate_viscosity_simple(concentration) else: viscosity_new = viscosity active_specified_concentration_bnd = specified_concentration_bnd iteration = 0 converged = False non_convergence = False ele_size = None q = None v = None while converged is False and non_convergence is False: if verbose is True: print('iteration ', iteration) if iteration > 0: print('pressure convergence ', es.Lsup(pressure_conv)) if solute_transport is True: # get flux q = calculate_q(k_vector, viscosity_new, pressure_old_ts, fluid_density_new, g_vector) v = q / phi # calculate new solute concentration concentration_old_iteration = concentration # finite element solute transport if concentration_bnd_inflow_only is True and iteration == 0: # only apply concentration bnd for inflow into model domain # assumes a horizontal model bnd # TODO: calculate flux normal to model boundary to account # for non-horizontal upper boundaries proj.setValue(D=es.kronecker(mesh), Y=q) try: nodal_q = proj.getSolution() except RuntimeError(msg): print('error, non-convergence') print(msg) non_convergence = True nodal_q_norm = rotate_vector_escript(nodal_q, topo_gradient) nodal_v = nodal_q / phi if concentration_bnd_inflow_direction == 'up': inflow_bnd = (es.whereNegative(nodal_q_norm[1]) * specified_concentration_bnd) elif concentration_bnd_inflow_direction == 'down': inflow_bnd = (es.wherePositive(nodal_q_norm[1]) * specified_concentration_bnd) elif concentration_bnd_inflow_direction == 'left': inflow_bnd = (es.wherePositive(nodal_q[0]) * specified_concentration_bnd) elif concentration_bnd_inflow_direction == 'right': inflow_bnd = (es.whereNegative(nodal_q[0]) * specified_concentration_bnd) if es.sup(inflow_bnd) > 0: active_specified_concentration_bnd = inflow_bnd else: min_x = es.inf( specified_concentration_bnd * specified_concentration_bnd.getDomain().getX()[0]) active_specified_concentration_bnd = \ (specified_concentration_bnd * es.whereZero( specified_concentration_bnd.getDomain().getX()[0] - min_x)) if verbose is True: print('warning, outflow for all specified ' \ 'concentration boundary nodes') #print 'using entire bnd instead' #active_specified_concentration_bnd = \ # specified_concentration_bnd print('using first node as fixed conc bnd instead') print('number of active conc bnd nodes:') print( np.sum( np.array(active_specified_concentration_bnd. toListOfTuples()))) if verbose is True: import grompy_lib xyi, ia = grompy_lib.convert_to_array( active_specified_concentration_bnd) xyc, ca = grompy_lib.convert_to_array( specified_concentration_bnd) print('inflow conc bnd nodes = %0.0f / %0.0f' \ % (ia.sum(), ca.sum())) print('x = %0.3f - %0.3f' % (xyi[ia == 1, 0].min(), xyi[ia == 1, 0].max())) print('qv conc bnd: ', (nodal_q[1] * specified_concentration_bnd)) #solute_pde.setValue(D=1, # r=specified_concentration_rho_f, # q=active_specified_concentration_bnd) solute_pde.setValue(D=1, r=specified_concentration, q=active_specified_concentration_bnd) solute_pde = update_solute_transport_pde(mesh, solute_pde, concentration_old_ts, v, dt, solute_source, dispersion_tensor, diffusivity, l_disp, t_disp, fluid_density_old) try: #solute_mass = solute_pde.getSolution() concentration = solute_pde.getSolution() except RuntimeError(error_msg): print('!! runtime error ', error_msg) print('solver options: ') print(solute_pde.getSolverOptions().getSummary()) non_convergence = True #raise RuntimeError(error_msg) # calculate concentration, using new solute mass and eq of state #concentration_new = calculate_concentration( # solute_mass, rho_f_0, gamma) #concentration_new = solve_solute_transport_v2( # solute_pde, mesh, # steady_state, # concentration_t1, v, dt, solute_source, # diffusivity, l_disp, t_disp, fluid_density_old, # rho_f_0, gamma) concentration_change_rate = \ (concentration - concentration_old_ts) / dt else: # no solute transport: concentration_change_rate = 0 if heat_transport is True: # no temperature in models yet: temperature_change_rate = 0 else: # no heat transport: temperature_change_rate = 0 if coupled_iterations is True: if verbose is True: print('recalculating fluid density and viscosity') # recalculate fluid density fluid_density_old = fluid_density_new fluid_density_new = \ calculate_fluid_density(concentration, gamma, rho_f_0) if calculate_viscosity is True: viscosity_new = \ calculate_viscosity_simple(concentration) else: # leave fluid density unchanged concentration_change_rate = 0 temperature_change_rate = 0 # store old pressure pressure_old_iteration = pressure if drain_loc is None or es.sup(drain_loc) == 0: # calculate pressure, no drain or seepage bnd pressure_pde = \ update_pressure_pde(pressure_pde, pressure_old_ts, phi, specific_storage, k_tensor, k_vector, fluid_density_new, viscosity_new, dt, rch_bnd_loc, recharge_mass_flux, fluid_source, g_vector, gamma, concentration_change_rate, alpha, temperature_change_rate) try: pressure = pressure_pde.getSolution() except RuntimeError(msg): print('error, non-convergence') print(msg) non_convergence = True #print 'no seepage bnd' else: # implement drain or seepage boundary if seepage_bnd is True: ## use seepage boundary: if active_seepage_bnd is None: # calculate pressure without any drain boundary pressure_pde.setValue(r=specified_pressure, q=specified_pressure_bnd) active_rch_bnd = rch_bnd_loc else: # incorporate active drain bnd of previous timestep specified_pressure_bnd_mod = \ es.wherePositive( specified_pressure_bnd + active_seepage_bnd) pressure_pde.setValue(r=specified_pressure, q=specified_pressure_bnd_mod) # do not change active rch bnd active_rch_bnd = rch_bnd_loc #active_rch_bnd = rch_bnd_loc * \ # es.whereZero(specified_pressure_bnd) #specified_flux = rch_bnd_loc * dt * recharge_mass_flux # calculate pressure with existing seepage bnd pressure_pde = \ update_pressure_pde(pressure_pde, pressure_old_ts, phi, specific_storage, k_tensor, k_vector, fluid_density_new, viscosity_new, dt, active_rch_bnd, recharge_mass_flux, fluid_source, g_vector, gamma, concentration_change_rate, alpha, temperature_change_rate) try: pressure = pressure_pde.getSolution() except RuntimeError: print("error, pressure PDE solver failed") converged = True non_convergence = True #if pressure_new not in locals(): # pressure_new = pressure_t1 # assign drain bnd nodes if active_seepage_bnd is None: active_seepage_bnd = \ es.wherePositive(drain_loc * pressure) if iteration < max_seepage_iterations and recalculate_seepage_bnd is True: # adjust seepage boundary, but only for first x iterations # to avoid instability if verbose is True: seepage_xy = active_seepage_bnd.getDomain().getX() seepage_nodes_xy = \ np.array(seepage_xy.toListOfTuples()) seepage_array = np.array( active_seepage_bnd.toListOfTuples()) ind = seepage_array > 0 print('\tbefore adjustment:') print('\tactive seepage bnd from x=%0.0f to %0.0f m' \ % (seepage_nodes_xy[ind, 0].min(), seepage_nodes_xy[ind, 0].max())) # remove seepage nodes that have become source of water q = calculate_q(k_vector, viscosity_new, pressure, fluid_density_new, g_vector) proj.setValue(D=es.kronecker(mesh), Y=q) try: nodal_q = proj.getSolution() except RuntimeError(msg): print('error, non-convergence') print(msg) non_convergence = True # calculate max vertical flux into the model domain at # drain bnd nodes # -> not possible, cannot mix face elements and normal elements # later on to adjust seepage... #nodal_q_norm = nodal_q * nodal_q.getDomain().getNormal() # nodal_q_norm = rotate_vector_escript( nodal_q, topo_gradient) #flux_seepage_bnd = active_seepage_bnd * nodal_q[1] flux_seepage_bnd = active_seepage_bnd * nodal_q_norm[1] #flux_seepage_bnd_corr = flux_seepage_bnd + seepage_change_buffer = 1e-3 / year seepage_inflow_nodes = \ es.whereNegative(flux_seepage_bnd + recharge_mass_flux / fluid_density_new) if verbose is True: print('\tflux seepage bnd (m/yr): ', flux_seepage_bnd * year) print('recharge ') print('\tseepage inflow nodes: ', seepage_inflow_nodes) #seepage_inflow_nodes = \ # es.whereNegative(flux_seepage_bnd) removed_seepage_inflow_nodes = seepage_inflow_nodes # add boundary nodes with P>0 to seepage bnd new_seepage_nodes = \ es.wherePositive(drain_loc * (1 - active_seepage_bnd) * pressure) # update the seepage bnd active_seepage_bnd = (active_seepage_bnd + new_seepage_nodes - removed_seepage_inflow_nodes) if verbose is True: seepage_xy = active_seepage_bnd.getDomain().getX() seepage_nodes_xy = \ np.array(seepage_xy.toListOfTuples()) seepage_array = np.array( active_seepage_bnd.toListOfTuples()) ind = np.array(seepage_array > 0) print('\tafter adjustment:') print('\tN=%i active seepage bnd x=%0.0f to %0.0f m' \ % (np.sum(ind.astype(int)), seepage_nodes_xy[ind, 0].min(), seepage_nodes_xy[ind, 0].max())) if iterate_seepage_in_one_timestep is True: # update the specified pressure boundary to include # new seepage nodes specified_pressure_bnd_mod = \ es.wherePositive( specified_pressure_bnd + active_seepage_bnd) #active_rch_bnd = rch_bnd_loc * es.whereZero(specified_pressure_bnd_mod) # changed to have steady recharge bnd regardless of seepage bnd, # 11 apr 2016, Elco active_rch_bnd = rch_bnd_loc # experiment, adjust recharge to have 0 rehcarge at seepage nodes # not sure if this makes sense... #specified_flux_adj = active_rch_bnd * dt * recharge_mass_flux # #pressure_pde.setValue(r=specified_pressure, # q=specified_pressure_bnd_mod, # y=specified_flux_adj) pressure_pde.setValue(r=specified_pressure, q=specified_pressure_bnd_mod) # recalculate pressure #pressure_pde = \ # update_pressure_pde(pressure_pde, # pressure_t1, # phi, specific_storage, # k_tensor, k_vector, # fluid_density_new, # viscosity_new, dt, # rch_bnd_loc, recharge_mass_flux, # fluid_source, g_vector, # gamma, concentration_change_rate, # alpha, temperature_change_rate) # recalculate pressure try: pressure = pressure_pde.getSolution() except RuntimeError(msg): print('error, non-convergence') print(msg) non_convergence = True # remove inflow nodes again #q = (k_vector / viscosity_new * # -(es.grad(pressure_new) # - fluid_density_new * g_vector) # / phi) q = calculate_q(k_vector, viscosity_new, pressure, fluid_density_new, g_vector) proj.setValue(D=es.kronecker(mesh), Y=q) nodal_q = proj.getSolution() # calculate max vertical flux into the model domain at # drain bnd nodes #nodal_q_norm = nodal_q * nodal_q.getDomain().getNormal() nodal_q_norm = rotate_vector_escript( nodal_q, topo_gradient) flux_seepage_bnd = active_seepage_bnd * nodal_q_norm[1] #removed_seepage_inflow_nodes = \ # es.whereNegative(flux_seepage_bnd - # seepage_inflow_threshold) #removed_seepage_inflow_nodes = \ # es.whereNegative(flux_seepage_bnd # + recharge_mass_flux # / fluid_density_new) removed_seepage_inflow_nodes = \ es.whereNegative(flux_seepage_bnd) active_seepage_bnd = (active_seepage_bnd - removed_seepage_inflow_nodes) if verbose is True: seepage_xy = active_seepage_bnd.getDomain().getX() seepage_nodes_xy = \ np.array(seepage_xy.toListOfTuples()) seepage_array = np.array( active_seepage_bnd.toListOfTuples()) ind = seepage_array > 0 print( '\tafter 2nd adjustment (removing inflow nodes):' ) print('\tN=%i active seepage bnd from ' \ 'x=%0.0f to %0.0f m' \ % (np.sum(ind.astype(int)), seepage_nodes_xy[ind, 0].min(), seepage_nodes_xy[ind, 0].max())) if iterate_seepage_in_one_timestep is True: # assign updated seepage nodes: specified_pressure_bnd_mod = \ es.wherePositive( specified_pressure_bnd + active_seepage_bnd) #active_rch_bnd = rch_bnd_loc * es.whereZero(specified_pressure_bnd_mod) active_rch_bnd = rch_bnd_loc # experiment, adjust recharge to have 0 rehcarge at seepage nodes # not sure if this makes sense... # ! probably the source of long timescale instability of seepage/rch bnd #specified_flux_adj = active_rch_bnd * dt * recharge_mass_flux #pressure_pde.setValue(r=specified_pressure, # q=specified_pressure_bnd_mod, # y=specified_flux_adj) pressure_pde.setValue(r=specified_pressure, q=specified_pressure_bnd_mod) # recalculate final pressure #pressure_pde = \ # update_pressure_pde(pressure_pde, # pressure_t1, # phi, specific_storage, # k_tensor, k_vector, # fluid_density_new, # viscosity_new, dt, # rch_bnd_loc, recharge_mass_flux, # fluid_source, g_vector, # gamma, concentration_change_rate, # alpha, temperature_change_rate) try: pressure = pressure_pde.getSolution() except RuntimeError(msg): print('error, non-convergence') print(msg) non_convergence = True # calculate convergence criteria pressure_conv = pressure - pressure_old_iteration if solute_transport is True: conc_conv = concentration - concentration_old_iteration else: conc_conv = 0.0 # check whether iterations have converged or not: if (es.Lsup(pressure_conv) < pressure_convergence_criterion) and \ (es.Lsup(conc_conv) < concentration_convergence_criterion)\ and iteration + 1 >= min_iterations: if iteration > 0 and verbose is True: print('iterations converged after %i iterations' % iteration) converged = True else: if verbose is True: print('iteration %i, max. pressure change %0.3e ' \ % (iteration, es.Lsup(pressure_conv))) print(' max. C change %0.3e ' \ % (es.Lsup(conc_conv))) if iteration + 1 >= max_iterations: print('warning, reached maximum number of %i iterations' \ % (iteration + 1)) print('iteration %i, max. pressure change %0.3e Pa, ' \ 'convergence at %0.2e Pa' \ % (iteration, es.Lsup(pressure_conv), pressure_convergence_criterion)) print(' max. C change %0.3e kg/kg, ' \ 'convergence at %0.2e kg/kg' \ % (es.Lsup(conc_conv), concentration_convergence_criterion)) converged = True non_convergence = True # check CFL number #max_CFL_number = calculate_CFL_number(q, dt) if ele_size is None: ele_size = q.getDomain().getSize() #print ele_size - q.getDomain().getSize() CFL_number = q * dt / ele_size max_CFL_number = es.Lsup(CFL_number) if max_CFL_number > 0.5 and verbose is True: print('warning, max CFL number = %0.2f, exceeds 0.5' \ % max_CFL_number) # recaclulcate timestep if max timestep exceeds CFL number if (max_allowed_CFL_number is not None and max_CFL_number > max_allowed_CFL_number and iteration == 0) \ or (force_CFL_timestep is True and iteration <= 1): # make sure iteration is repeated converged = False #CFL_number / flux * flux.getDomain().getSize() = dtc / dtc = max_allowed_CFL_number / q * ele_size new_timestep = es.inf((dtc**2)**0.5) if dt_max is not None and new_timestep > dt_max: new_timestep = dt_max dt = new_timestep if verbose is True: print('max CFL number = ', max_CFL_number) print('changing timestep from %0.2e sec to %0.2e sec' \ % (dt, new_timestep)) if coupled_iterations is False: converged = True iteration += 1 return (pressure, concentration, fluid_density_new, viscosity_new, q, v, active_specified_concentration_bnd, active_seepage_bnd, active_rch_bnd, iteration, es.Lsup(pressure_conv), es.Lsup(conc_conv), max_CFL_number, non_convergence, dt)
def solve_steady_state_pressure_eq_new(mesh, topo_gradient, pressure_pde, rho_f, k_tensor, k_vector, viscosity, g_vector, fluid_source, rch_bnd_loc, recharge_mass_flux, specified_pressure_bnd, specified_pressure, drain_bnd_loc, fluid_density, proj, debug=True): """ Solve the steady-state fluid flow equation for fluid pressure using escript with optional seepage bnd condition. """ year = 365.25 * 24 * 60 * 60.0 # calculate boundary flux specified_flux = rch_bnd_loc * recharge_mass_flux a = rho_f * k_tensor / viscosity * es.kronecker(mesh) d = 0.0 x = rho_f**2 * k_vector / viscosity * g_vector y = fluid_source # pressure_pde.setValue(A=a, D=d, X=x, Y=y, y=specified_flux, q=specified_pressure_bnd, r=specified_pressure) # calculate pressure, without seepage bnd pressure = pressure_pde.getSolution() debug = True if debug is True: print('initial calculated steady-state pressure: ', pressure) if es.sup(drain_bnd_loc) == 0: print('no drain or seepage bnd') active_seepage_bnd = es.wherePositive(drain_bnd_loc) return pressure, active_seepage_bnd # check if h > surface elevation #pressure_threshold = 0.01 * 9.81 * 1000.0 pressure_threshold = 0.0 active_seepage_bnd = es.wherePositive(drain_bnd_loc * pressure - pressure_threshold) if debug is True: print('active seepage nodes: ', np.sum(np.array(active_seepage_bnd.toListOfTuples()))) print('potential seepage nodes: ', np.sum(np.array(drain_bnd_loc.toListOfTuples()))) n_seepage_change = 9999 n_seepage_nodes = 99999 n_iter = 0 max_seepage_iter = 2000 while n_seepage_change > 0 and n_iter < max_seepage_iter: # add seepage to specified pressure bnd and update bnd conditions specified_pressure_bnd_mod = \ es.wherePositive( specified_pressure_bnd + active_seepage_bnd) #active_rch_bnd = rch_bnd_loc * es.whereZero(specified_pressure_bnd) active_rch_bnd = rch_bnd_loc specified_flux = active_rch_bnd * recharge_mass_flux pressure_pde.setValue(r=specified_pressure, q=specified_pressure_bnd_mod, y=specified_flux) # recalculate pressure pressure = pressure_pde.getSolution() if debug is True: print('new pressure: ', pressure) # calculate flux q = calculate_q(k_vector, viscosity, pressure, fluid_density, g_vector) proj.setValue(D=es.kronecker(mesh), Y=q) try: nodal_q = proj.getSolution() except RuntimeError(msg): print('error, non-convergence') print(msg) non_convergence = True # calculate max vertical flux into the model domain at # drain bnd nodes # using outer norm to calculate correct bnd flux does not work because # flux cannot be interpolated to same functionspace as pressure variable #nodal_q_norm_bnd = nodal_q * nodal_q.getDomain().getNormal() #nodal_q_norm = es.interpolate(nodal_q_norm_bnd, active_seepage_bnd.getFunctionSpace()) nodal_q_norm = rotate_vector_escript(nodal_q, topo_gradient) flux_seepage_bnd = active_seepage_bnd * nodal_q_norm[1] # remove inlfow nodes that are <= recharge flux_seepage_bnd_corr = flux_seepage_bnd + recharge_mass_flux / fluid_density # changed back to old method to speed up seepage bnd calc #flux_seepage_bnd_corr = flux_seepage_bnd # and remove only the worst x % of seepage nodes to avoid oscillations: seepage_threshold = es.inf(flux_seepage_bnd_corr) * 0.5 #seepage_threshold = 0.0 seepage_inflow_nodes = \ es.whereNegative(flux_seepage_bnd_corr - seepage_threshold) removed_seepage_inflow_nodes = seepage_inflow_nodes if debug is True: print('number of seepage inflow nodes: ', \ np.sum(np.array(seepage_inflow_nodes.toListOfTuples()))) xmin_seep = es.inf(seepage_inflow_nodes * seepage_inflow_nodes.getDomain().getX()[0] + (1 - seepage_inflow_nodes) * 999999) xmax_seep = es.sup(seepage_inflow_nodes * seepage_inflow_nodes.getDomain().getX()[0]) print('from x= %0.2f m to x= %0.2f m' % (xmin_seep, xmax_seep)) # add boundary nodes with P>0 to seepage bnd new_seepage_nodes = \ es.wherePositive(drain_bnd_loc * (1 - active_seepage_bnd) * pressure) if debug is True: print('number of new seepage nodes: ', \ np.sum(np.array(new_seepage_nodes.toListOfTuples()))) # update the seepage bnd active_seepage_bnd = (active_seepage_bnd + new_seepage_nodes - removed_seepage_inflow_nodes) n_seepage_nodes_old = n_seepage_nodes n_seepage_nodes = np.sum(np.array(active_seepage_bnd.toListOfTuples())) n_seepage_change = np.abs(n_seepage_nodes_old - n_seepage_nodes) if debug is True: print('final active seepage nodes: ', np.sum(np.array(active_seepage_bnd.toListOfTuples()))) print('potential seepage nodes: ', np.sum(np.array(drain_bnd_loc.toListOfTuples()))) if n_seepage_nodes_old < n_seepage_nodes: print( 'lowest number of seepage nodes reached, stopping iterations') n_seepage_change = 0 print('seepage iteration %i' % n_iter) print('seepage threshold ', seepage_threshold * year) print('change in seepage nodes from %0.0f to %0.0f' % (n_seepage_nodes_old, n_seepage_nodes)) n_iter += 1 # update specified pressure bnd condition specified_pressure_bnd_mod = \ es.wherePositive( specified_pressure_bnd + active_seepage_bnd) #active_rch_bnd = rch_bnd_loc * es.whereZero(specified_pressure_bnd) active_rch_bnd = rch_bnd_loc specified_flux = active_rch_bnd * recharge_mass_flux pressure_pde.setValue(r=specified_pressure, q=specified_pressure_bnd_mod, y=specified_flux) # recalculate pressure pressure = pressure_pde.getSolution() if debug is True: print('final pressure: ', pressure) return pressure, active_seepage_bnd
# create the output directory if not existing already try: os.mkdir(OUTPUTDIR) except: pass # read in the FEILDS Voxet initial_model = readVoxet(dom, MODEL_DATASET, MODEL_PROPERTY, MODEL_ORIGIN, 0., COORDINATES) # set the extents of the desired "region" for clipping and computation, # mask is = 1 OUTSIDE area of interest mask_air = whereNonNegative(dom.getX()[2]+spacing[2]/2) #reg_mask for air layer mask_LONG = wherePositive(Longitude_W-datacoords[0]) + whereNegative(Longitude_E-datacoords[0]) #reg_mask for longitude mask_LAT = whereNegative(Latitude_N-datacoords[1]) + wherePositive(Latitude_S-datacoords[1]) #reg_mask for latitude mask_h = wherePositive(datacoords[2]+(h_top/100)) + whereNegative(datacoords[2]+(h_base/100)) #reg_mask for depth if ReverseSelection: reg_mask = whereNonZero(mask_LONG+mask_LAT+mask_h) else: reg_mask = whereZero(mask_LONG+mask_LAT+mask_h) # prior to any computation, write out the selected region model as CSV # and Silo if requested fn = os.path.join(OUTPUTDIR, "region_%s")%(MODEL_PROPERTY) saveDataCSV(fn+".csv", Long=datacoords[0], Lat=datacoords[1], h=datacoords[2], PROPERTY=initial_model, mask=reg_mask) print("CSV file written with the following fields: Longitude (degrees)" +" Latitude (degrees), h (100km), Property (kg/m^3 or Pa)")
def setup_coastal_mesh(Parameters, mesh_filename, extend_domain=True, max_length=1e5): """ Create a mesh consisting of 3 blocks, with a smaller cell size in the middle block The middle block is centered around the fresh-salt water interface, which is calculated using the Ghyben-Herzberg equation. """ #if Parameters.topo_gradient == 0: # extent_salt_water = 0 #else: # extent_salt_water = (Parameters.thickness / # (41.0 * Parameters.topo_gradient)) R = Parameters.recharge_flux B = Parameters.thickness K = Parameters.k * Parameters.rho_f_0 * 9.81 / Parameters.viscosity L = Parameters.L # calculate hydraulic head using analytical solution for confined aq # with uniform recharge + Dupuit assumptions xa = np.linspace(0, L, 101) h = R / (K * B) * (L * xa - 0.5 * xa**2) if h[-1] < (B / 40): print 'warning, calculated extent salt water toe exceeds model domain' print 'calculated h at model bnd = %0.2f m' % h[-1] print 'Ghyben-Herzberg depth of salt water interface = %0.2f m' % (h[-1] * 40) print 'thickness = %0.2f m' % Parameters.thickness if extend_domain is False: extent_salt_water = Parameters.L - Parameters.buffer_distance_land - 1.0 #print 'choosing maximum possible extent of %0.2f m for ' \ # 'designing model grid' % extent_salt_water print 'entire top right triangle at landward side of model domain has fine discretization' fine_mesh = True adjust_length = False else: adjust_length = True else: fine_mesh = False # salt water toe touches bottom of model domain a = 0.5 * R / (K * B) b = -(R * L) / (K * B) c = B / 40.0 D = np.sqrt(b**2 - 4 * a * c) extent_salt_water = (-b - D) / (2 * a) hs1 = R / (K * B) * (L * extent_salt_water - 0.5 * extent_salt_water**2) print 'calculated extent salt water toe = %0.2f m' % extent_salt_water try: assert np.abs(hs1 - B / 40.0) < 1e-3 except AssertionError: msg = 'error, something wrong with calculated extent ' \ 'salt water toe' raise ValueError(msg) if adjust_length is True: L_land = extent_salt_water + Parameters.buffer_distance_land * 2 if L_land > max_length: L_land = Parameters.L fine_mesh = True else: print 'extending model domain size to %0.3e' % L_land else: L_land = Parameters.L ############################### # use gmsh to construct domain ############################## xs = np.array([-Parameters.L_sea, extent_salt_water - Parameters.buffer_distance_sea, -Parameters.buffer_distance_sea, -Parameters.L_sea, extent_salt_water, 0, extent_salt_water + Parameters.buffer_distance_land, Parameters.buffer_distance_land, L_land, L_land, -Parameters.L_sea]) zs = xs * Parameters.topo_gradient zs[0:2] = zs[0:2] - Parameters.thickness zs[4] = zs[4] - Parameters.thickness zs[6] = zs[6] - Parameters.thickness zs[8] = zs[8] - Parameters.thickness zs[10] = 0.0 #points = create_points(xs,zs) points = [pc.Point(x, z) for x, z in zip(xs, zs)] line1 = pc.Line(points[0], points[1]) line2 = pc.Line(points[1], points[2]) line3 = pc.Line(points[2], points[3]) line4 = pc.Line(points[3], points[0]) line5 = pc.Line(points[1], points[4]) line6 = pc.Line(points[4], points[5]) line7 = pc.Line(points[5], points[2]) line8 = pc.Line(points[4], points[6]) line9 = pc.Line(points[6], points[7]) line10 = pc.Line(points[7], points[5]) line11 = pc.Line(points[6], points[8]) line12 = pc.Line(points[8], points[9]) line13 = pc.Line(points[9], points[7]) # new lines for sea surface # seabottom, x=-buffer to x=0 # -line3 (pt 3 to 2) # - line7 (pt 2 to pt 5) # coastline to x=0, z=0 #line14 = pc.Line(points[5], points[10]) # x=0, z=0 to x=0, z=sea bottom #line15 = pc.Line(points[10], points[3]) # coastline to x=0, sea bottom # finer grid cell size around fresh-salt water interface curve_a = pc.CurveLoop(line1, line2, line3, line4) curve_b = pc.CurveLoop(line5, line6, line7, -line2) curve_c = pc.CurveLoop(line8, line9, line10, -line6) curve_d = pc.CurveLoop(line11, line12, line13, -line9) #curve_seawater = pc.CurveLoop(-line3, -line7, line14, line15) surface_a = pc.PlaneSurface(curve_a) surface_b = pc.PlaneSurface(curve_b) surface_c = pc.PlaneSurface(curve_c) surface_d = pc.PlaneSurface(curve_d) #surface_seawater = pc.PlaneSurface(curve_seawater) surface_a.setLocalScale(factor=Parameters.grid_refinement_factor_sea) surface_b.setLocalScale(factor=Parameters.grid_refinement_factor) surface_c.setLocalScale(factor=Parameters.grid_refinement_factor) #surface_seawater.setLocalScale(factor=Parameters.grid_refinement_factor_seawater) if fine_mesh is True: print 'assigning refined grid from landward side of model domain' surface_d.setLocalScale(factor=Parameters.grid_refinement_factor) d = gmsh.Design(dim=2, element_size=Parameters.cellsize) ps1 = pc.PropertySet("sea_surface1", line3) ps2 = pc.PropertySet("sea_surface2", line7) ps3 = pc.PropertySet("land_surface1", line10) ps4 = pc.PropertySet("land_surface2", line13) #ps5 = pc.PropertySet("sea_surface", line14) #d.addItems(pc.PropertySet('sea', surface_a), # pc.PropertySet('salt_wedge_sea_side', surface_b), # pc.PropertySet('salt_wedge_land_side', surface_c), # pc.PropertySet('land', surface_d), # pc.PropertySet('seawater', surface_seawater), # ps1, ps2, ps3, ps4, ps5) d.addItems(pc.PropertySet('sea', surface_a), pc.PropertySet('salt_wedge_sea_side', surface_b), pc.PropertySet('salt_wedge_land_side', surface_c), pc.PropertySet('land', surface_d), ps1, ps2, ps3, ps4) d.setMeshFileName(mesh_filename) mesh = fl.MakeDomain(d, optimizeLabeling=True) # calculate surface xy = mesh.getX() z_surface = xy[0] * Parameters.topo_gradient surface = es.whereZero(xy[1] - z_surface) # sea surface sea_surface = es.whereZero(xy[1]) * es.whereNegative(xy[0]) seawater = es.whereNegative(xy[0]) * es.whereNegative(z_surface - xy[1]) print bla return mesh, surface, sea_surface, seawater, z_surface