Example #1
0
    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
Example #2
0
    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
Example #3
0
 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
Example #4
0
 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
Example #5
0
    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
Example #6
0
    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
Example #7
0
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)
Example #8
0
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
Example #9
0
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)')
Example #10
0
# 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)) ))