def value_of_constraint(self): """ return the values used to constrain a solution :return: values to be used at the locations of the constraints. If ``value`` is not given ``None`` is rerturned. :rtype: `escript.Scalar` """ out_loc=0 out=0 if not self.location_of_constraint0 is None: tmp=wherePositive(self.location_of_constraint0) out=out*(1.-tmp)+self.value_of_constraint0*tmp out_loc=wherePositive(out_loc+tmp) if not self.location_of_constraint1 is None: tmp=wherePositive(self.location_of_constraint1) out=out*(1.-tmp)+self.value_of_constraint1*tmp out_loc=wherePositive(out_loc+tmp) if not self.location_of_constraint2 is None: tmp=wherePositive(self.location_of_constraint2) out=out*(1.-tmp)+self.value_of_constraint2*tmp out_loc=wherePositive(out_loc+tmp) if not self.location_of_constraint3 is None: tmp=wherePositive(self.location_of_constraint3) out=out*(1.-tmp)+self.value_of_constraint3*tmp out_loc=wherePositive(out_loc+tmp) return out
def value_of_constraint(self): """ return the values used to constrain a solution :return: values to be used at the locations of the constraints. If ``value`` is not given ``None`` is rerturned. :rtype: `escript.Scalar` """ out_loc = 0 out = 0 if not self.location_of_constraint0 is None: tmp = wherePositive(self.location_of_constraint0) out = out * (1. - tmp) + self.value_of_constraint0 * tmp out_loc = wherePositive(out_loc + tmp) if not self.location_of_constraint1 is None: tmp = wherePositive(self.location_of_constraint1) out = out * (1. - tmp) + self.value_of_constraint1 * tmp out_loc = wherePositive(out_loc + tmp) if not self.location_of_constraint2 is None: tmp = wherePositive(self.location_of_constraint2) out = out * (1. - tmp) + self.value_of_constraint2 * tmp out_loc = wherePositive(out_loc + tmp) if not self.location_of_constraint3 is None: tmp = wherePositive(self.location_of_constraint3) out = out * (1. - tmp) + self.value_of_constraint3 * tmp out_loc = wherePositive(out_loc + tmp) return out
def __update(self, tag, tag_value, value): if self.__pde == None: self.__pde = LinearPDE(self.domain, numSolutions=1) mask = Scalar(0., Function(self.domain)) mask.setTaggedValue(tag, 1.) self.__pde.setValue(Y=mask) mask = wherePositive(abs(self.__pde.getRightHandSide())) value *= (1. - mask) value += tag_value * mask return value
def __update(self,tag,tag_value,value): if self.__pde==None: self.__pde=LinearPDE(self.domain,numSolutions=1) mask=Scalar(0.,Function(self.domain)) mask.setTaggedValue(tag,1.) self.__pde.setValue(Y=mask) mask=wherePositive(abs(self.__pde.getRightHandSide())) value*=(1.-mask) value+=tag_value*mask return value
def location_of_constraint(self): """ return the values used to constrain a solution :return: the mask marking the locations of the constraints :rtype: `escript.Scalar` """ out_loc=0 if not self.location_of_constraint0 is None: out_loc=wherePositive(out_loc+wherePositive(self.location_of_constraint0)) if not self.location_of_constraint1 is None: out_loc=wherePositive(out_loc+wherePositive(self.location_of_constraint1)) if not self.location_of_constraint2 is None: out_loc=wherePositive(out_loc+wherePositive(self.location_of_constraint2)) if not self.location_of_constraint3 is None: out_loc=wherePositive(out_loc+wherePositive(self.location_of_constraint3)) return out_loc
def location_of_constraint(self): """ return the values used to constrain a solution :return: the mask marking the locations of the constraints :rtype: `escript.Scalar` """ out_loc = 0 if not self.location_of_constraint0 is None: out_loc = wherePositive( out_loc + wherePositive(self.location_of_constraint0)) if not self.location_of_constraint1 is None: out_loc = wherePositive( out_loc + wherePositive(self.location_of_constraint1)) if not self.location_of_constraint2 is None: out_loc = wherePositive( out_loc + wherePositive(self.location_of_constraint2)) if not self.location_of_constraint3 is None: out_loc = wherePositive( out_loc + wherePositive(self.location_of_constraint3)) return out_loc
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
def RegionalCalculation(reg_mask): """ Calculates the "regional" from the entire FEILDS model excluding the selected region and outputs gravity at the specified altitude... see above for the "residual" """ # read in a gravity data grid to define data computation space G_DATA = os.path.join(DATADIR,'Final_BouguerTC_UC15K_qrtdeg.nc') FS=ReducedFunction(dom) nValues=[NX, NY, 1] first = [0, 0, cell_at_altitude] multiplier = [1, 1, 1] reverse = [0, 0, 0] byteorder = BYTEORDER_NATIVE gdata = readBinaryGrid(G_DATA, FS, shape=(), fill=-999999, byteOrder=byteorder, dataType=DATATYPE_FLOAT32, first=first, numValues=nValues, multiplier=multiplier, reverse=reverse) print("Grid successfully read") # get the masking and units sorted out for the data-space g_mask = whereNonZero(gdata+999999) gdata=gdata*g_mask * GRAV_UNITS # if people choose to have air in their region we exclude it from the # specified gravity calculation region if h_top < 0.: reg_mask = reg_mask+mask_air live_model = initial_model* whereNonPositive(reg_mask) dead_model = initial_model* wherePositive(reg_mask) if UseMean is True: # calculate the mean density within the selected region BackgroundDensity = integrate(dead_model)/integrate(wherePositive(reg_mask)) print("Density mean for selected region equals = %s"%BackgroundDensity) live_model = live_model + BackgroundDensity * wherePositive(reg_mask) # create mapping rho_mapping = DensityMapping(dom, rho0=live_model) # invert sign of gravity field to account for escript's coordinate system gdata = -GRAV_UNITS * gdata # turn the scalars into vectors (vertical direction) d=kronecker(DIM)[DIM-1] w=safeDiv(1., g_mask) gravity_model=GravityModel(dom, w*d, gdata*d, fixPotentialAtBottom=False, coordinates=COORDINATES) gravity_model.rescaleWeights(rho_scale=rho_mapping.getTypicalDerivative()) phi,_ = gravity_model.getArguments(live_model) g_init = -gravity_model.getCoordinateTransformation().getGradient(phi) g_init = interpolate(g_init, gdata.getFunctionSpace()) print("Computed gravity: %s"%(g_init[2])) fn=os.path.join(OUTPUTDIR,'regional-gravity') if SiloOutput is True: saveSilo(fn, density=live_model, gravity_init=g_init, g_initz=-g_init[2], gravitymask=g_mask, modelmask=reg_mask) print('SILO file written with the following fields: density (kg/m^3), gravity vector (m/s^2), gz (m/s^2), gravitymask, modelmask') # to compare calculated data against input dataset. # Not used by default but should work if the input dataset is correct #gslice = g_init[2]*wherePositive(g_mask) #g_dash = integrate(gslice)/integrate(wherePositive(g_mask)) #gdataslice = gdata*wherePositive(g_mask) #gdata_dash = integrate(gdataslice)/integrate(wherePositive(g_mask)) #misfit=(gdataslice-gdata_dash)-(gslice-g_dash) saveDataCSV(fn+".csv", mask=g_mask, gz=-g_init[2], Long=datacoords[0], Lat=datacoords[1], h=datacoords[2]) print('CSV file written with the following fields: Longitude (degrees) Latitude (degrees), h (100km), gz (m/s^2)')
# 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(self, domainbuilder, rho0=None, drho=None, rho_z0=None, rho_beta=None, k0=None, dk=None, k_z0=None, k_beta=None, w0=None, w1=None, w_gc=None,rho_at_depth=None, k_at_depth=None): """ Sets up the inversion from an instance ``domainbuilder`` of a `DomainBuilder`. Gravity and magnetic data attached to the ``domainbuilder`` are considered in the inversion. If magnetic data are given as scalar it is assumed that values are collected in direction of the background magnetic field. :param domainbuilder: Domain builder object with gravity source(s) :type domainbuilder: `DomainBuilder` :param rho0: reference density, see `DensityMapping`. If not specified, zero is used. :type rho0: ``float`` or `Scalar` :param drho: density scale, see `DensityMapping`. If not specified, 2750 kg/m^3 is used. :type drho: ``float`` or `Scalar` :param rho_z0: reference depth for depth weighting for density, see `DensityMapping`. If not specified, zero is used. :type rho_z0: ``float`` or `Scalar` :param rho_beta: exponent for density depth weighting, see `DensityMapping`. If not specified, zero is used. :type rho_beta: ``float`` or `Scalar` :param k0: reference susceptibility, see `SusceptibilityMapping`. If not specified, zero is used. :type k0: ``float`` or `Scalar` :param dk: susceptibility scale, see `SusceptibilityMapping`. If not specified, 1. is used. :type dk: ``float`` or `Scalar` :param k_z0: reference depth for susceptibility depth weighting, see `SusceptibilityMapping`. If not specified, zero is used. :type k_z0: ``float`` or `Scalar` :param k_beta: exponent for susceptibility depth weighting, see `SusceptibilityMapping`. If not specified, zero is used. :type k_beta: ``float`` or `Scalar` :param w0: weighting factor for level set term in the regularization. If not set zero is assumed. :type w0: ``Scalar`` or ``float`` :param w1: weighting factor for the gradient term in the regularization see `Regularization`. If not set zero is assumed. :type w1: `es.Data` or ``ndarray`` of shape (DIM,) :param w_gc: weighting factor for the cross gradient term in the regularization, see `Regularization`. If not set one is assumed. :type w_gc: `Scalar` or `float` :param k_at_depth: value for susceptibility at depth, see `DomainBuilder`. :type k_at_depth: ``float`` or ``None`` :param rho_at_depth: value for density at depth, see `DomainBuilder`. :type rho_at_depth: ``float`` or ``None`` """ self.logger.info('Retrieving domain...') dom=domainbuilder.getDomain() DIM=dom.getDim() trafo=makeTransformation(dom, domainbuilder.getReferenceSystem()) rock_mask=es.wherePositive(domainbuilder.getSetDensityMask() + domainbuilder.getSetSusceptibilityMask()) #======================== self.logger.info('Creating mappings ...') if rho_at_depth: rho2= rock_mask * rho_at_depth + (1-rock_mask) * rho0 elif rho0: rho2= (1-rock_mask) * rho0 else: rho2=0 if k_at_depth: k2= rock_mask * k_at_depth + (1-rock_mask) * k0 elif k0: k2= (1-rock_mask) * k0 else: k2=0 rho_mapping=DensityMapping(dom, rho0=rho2, drho=drho, z0=rho_z0, beta=rho_beta) rho_scale_mapping=rho_mapping.getTypicalDerivative() self.logger.debug("rho_scale_mapping = %s"%rho_scale_mapping) k_mapping=SusceptibilityMapping(dom, k0=k2, dk=dk, z0=k_z0, beta=k_beta) k_scale_mapping=k_mapping.getTypicalDerivative() self.logger.debug("k_scale_mapping = %s"%k_scale_mapping) #======================== self.logger.info("Setting up regularization...") if w1 is None: w1=[1.]*DIM regularization=Regularization(dom, numLevelSets=1,w0=w0, w1=w1, location_of_set_m=rock_mask, coordinates=trafo) #==================================================================== self.logger.info("Retrieving gravity surveys...") surveys=domainbuilder.getGravitySurveys() g=[] w=[] for g_i,sigma_i in surveys: w_i=es.safeDiv(1., sigma_i) if g_i.getRank()==0: g_i=g_i*es.kronecker(DIM)[DIM-1] if w_i.getRank()==0: w_i=w_i*es.kronecker(DIM)[DIM-1] g.append(g_i) w.append(w_i) self.logger.debug("Added gravity survey:") self.logger.debug("g = %s"%g_i) self.logger.debug("sigma = %s"%sigma_i) self.logger.debug("w = %s"%w_i) self.logger.info("Setting up gravity model...") gravity_model=GravityModel(dom, w, g, fixPotentialAtBottom=self._fixGravityPotentialAtBottom, coordinates=trafo) gravity_model.rescaleWeights(rho_scale=rho_scale_mapping) #==================================================================== self.logger.info("Retrieving magnetic field surveys...") d_b=es.normalize(domainbuilder.getBackgroundMagneticFluxDensity()) surveys=domainbuilder.getMagneticSurveys() B=[] w=[] for B_i,sigma_i in surveys: w_i=es.safeDiv(1., sigma_i) if self.magnetic_intensity_data: if not B_i.getRank()==0: B_i=es.length(B_i) if not w_i.getRank()==0: w_i=length(w_i) else: if B_i.getRank()==0: B_i=B_i*d_b if w_i.getRank()==0: w_i=w_i*d_b B.append(B_i) w.append(w_i) self.logger.debug("Added magnetic survey:") self.logger.debug("B = %s"%B_i) self.logger.debug("sigma = %s"%sigma_i) self.logger.debug("w = %s"%w_i) self.logger.info("Setting up magnetic model...") if self.self_demagnetization: magnetic_model=SelfDemagnetizationModel(dom, w, B, domainbuilder.getBackgroundMagneticFluxDensity(), fixPotentialAtBottom=self._fixMagneticPotentialAtBottom, coordinates=trafo) else: if self.magnetic_intensity_data: magnetic_model=MagneticIntensityModel(dom, w, B, domainbuilder.getBackgroundMagneticFluxDensity(), fixPotentialAtBottom=self._fixMagneticPotentialAtBottom, coordinates=trafo) else: magnetic_model=MagneticModel(dom, w, B, domainbuilder.getBackgroundMagneticFluxDensity(), fixPotentialAtBottom=self._fixMagneticPotentialAtBottom, coordinates=trafo) magnetic_model.rescaleWeights(k_scale=k_scale_mapping) #==================================================================== self.logger.info("Setting cost function...") self.setCostFunction(InversionCostFunction(regularization, (rho_mapping, k_mapping), ((gravity_model,0), (magnetic_model,1)) ))