Exemple #1
0
def update_solute_transport_pde(mesh,
                                solute_pde,
                                concentration_old,
                                v,
                                dt,
                                solute_source,
                                dispersion_tensor,
                                diffusivity,
                                l_disp,
                                t_disp,
                                rho_f,
                                verbose=False):
    """
    Solve the solute transport equation.
    """

    # calculate hydrodynamic dispersivity tensor from molecular diffusivity
    # and longitudinal and transverse dispersivity

    # calculate absolute velocity
    v_abs = (v[0]**2 + v[1]**2)**0.5

    # get rid of 0 values of velocity
    v_abs_1 = es.whereZero(v_abs) * 1e-20 + es.whereNonZero(v_abs) * v_abs

    Dxx = l_disp * (v[0]**2) / v_abs_1 + diffusivity
    Dyy = t_disp * (v[1]**2) / v_abs_1 + diffusivity

    # and set dispersion tensor to 0 where 0 absolute velocity
    Dxx = (Dxx * es.whereNonZero(v_abs) + es.whereZero(v_abs) * diffusivity)
    Dyy = (Dyy * es.whereNonZero(v_abs) + es.whereZero(v_abs) * diffusivity)

    # horizontal and vertical values of dispersion (Dxx and Dyy)
    dispersion_tensor[0, 0] = Dxx
    dispersion_tensor[1, 1] = Dyy

    # off-diagonal terms in tensor (Dxy and Dyx)
    # set to 0 for now, model becomes numerically unstable
    # testing with fixed values did not result in significant cahnges in solute conc...
    # todo, figure out why this is unstable or check the potential error
    dispersion_tensor[0, 1] = (l_disp - t_disp) * (v[0] * v[1]) / v_abs_1
    dispersion_tensor[1, 0] = (l_disp - t_disp) * (v[0] * v[1]) / v_abs_1

    u_old = concentration_old

    a_coeff = dt * dispersion_tensor * es.kronecker(mesh)
    c_coeff = dt * v
    d_coeff = 1
    y_coeff = u_old + solute_source * dt

    if verbose is True:
        print('solute transport coefficients')
        print('A: ', a_coeff.getShape(), a_coeff)
        print('C: ', c_coeff.getShape(), c_coeff)
        print('D: ', d_coeff)
        print('Y: ', y_coeff.getShape(), y_coeff)

    solute_pde.setValue(A=a_coeff, C=c_coeff, D=d_coeff, Y=y_coeff)

    return solute_pde
Exemple #2
0
    def __setBoundaryMask(self, X):
        """
    DESCRIPTION:
    -----------
    Define Dirichlet model boundaries conditions.

    ARGUMENTS:
    ----------
    X :: escript object with all coordinates

    RETURNS:
    --------
    boundary_mask :: escript object with mask values at boundaries

    """
        # Boundaries are defined as masks (1 or 0) for all mesh coordinates;
        # values at the boundary are '1', whereas all other values are '0'.
        mask_l = escript.whereZero(X[0] - escript.inf(X[0]))
        mask_r = escript.whereZero(X[0] - escript.sup(X[0]))
        mask_t = escript.whereZero(X[1] - escript.inf(X[1]))
        mask_b = escript.whereZero(X[1] - escript.sup(X[1]))

        # Combine the mask for all boundaries:
        boundary_mask = mask_t + mask_b + mask_l + mask_r

        return boundary_mask
 def test_yDirection(self):
     dim=self.domain.getDim()
     if dim==3:
         return
     u = Symbol('u',(2,), dim=dim)
     q = Symbol('q', (2,2))
     theta = Symbol('theta')
     theta=3.141/6
     q[0,0]=cos(theta)
     q[0,1]=-sin(theta)
     q[1,0]=sin(theta)
     q[1,1]=cos(theta)
     sigma = Symbol('sigma',(2,2))
     p = NonlinearPDE(self.domain, u, debug=0)
     epsilon = symmetric(grad(u))
  #   epsilon = matrixmult(matrixmult(q,epsilon0),q.transpose(1))
     c00=10;c01=8;c05=0
     c01=8;c11=10;c15=0
     c05=0;c15=0;c55=1
     sigma[0,0]=c00*epsilon[0,0]+c01*epsilon[1,1]+c05*2*epsilon[1,0]
     sigma[1,1]=c01*epsilon[0,0]+c11*epsilon[1,1]+c15*2*epsilon[1,0]
     sigma[0,1]=c05*epsilon[0,0]+c15*epsilon[1,1]+c55*2*epsilon[1,0]
     sigma[1,0]=sigma[0,1]
  #   sigma0=matrixmult(matrixmult(q.transpose(1),epsilon),q)
     x = self.domain.getX()
     gammaD=whereZero(x[1])*[1,1]#+whereZero(x[0])*[1,0]+whereZero(x[0]-1)*[1,0]  
     yconstraint = FunctionOnBoundary(self.domain).getX()[1]
     p.setValue(X=sigma,q=gammaD,y=[-50,0]*whereZero(yconstraint-1),r=[1,1])
     v = p.getSolution(u=[0,0])
     x=np.ndarray((2,))
     x[0]=0.5
     x[1]=0.5
     loc=Locator(v.getFunctionSpace(),x)
     valAtX=loc(v)
     self.assertTrue(valAtX[0]>10*valAtX[1])
Exemple #4
0
 def test_yDirection(self):
     dim=self.domain.getDim()
     if dim==3:
         return
     u = Symbol('u',(2,), dim=dim)
     q = Symbol('q', (2,2))
     theta = Symbol('theta')
     theta=3.141/6
     q[0,0]=cos(theta)
     q[0,1]=-sin(theta)
     q[1,0]=sin(theta)
     q[1,1]=cos(theta)
     sigma = Symbol('sigma',(2,2))
     p = NonlinearPDE(self.domain, u, debug=0)
     epsilon = symmetric(grad(u))
  #   epsilon = matrixmult(matrixmult(q,epsilon0),q.transpose(1))
     c00=10;c01=8;c05=0
     c01=8;c11=10;c15=0
     c05=0;c15=0;c55=1
     sigma[0,0]=c00*epsilon[0,0]+c01*epsilon[1,1]+c05*2*epsilon[1,0]
     sigma[1,1]=c01*epsilon[0,0]+c11*epsilon[1,1]+c15*2*epsilon[1,0]
     sigma[0,1]=c05*epsilon[0,0]+c15*epsilon[1,1]+c55*2*epsilon[1,0]
     sigma[1,0]=sigma[0,1]
  #   sigma0=matrixmult(matrixmult(q.transpose(1),epsilon),q)
     x = self.domain.getX()
     gammaD=whereZero(x[1])*[1,1]#+whereZero(x[0])*[1,0]+whereZero(x[0]-1)*[1,0]  
     yconstraint = FunctionOnBoundary(self.domain).getX()[1]
     p.setValue(X=sigma,q=gammaD,y=[-50,0]*whereZero(yconstraint-1),r=[1,1])
     v = p.getSolution(u=[0,0])
     x=np.ndarray((2,))
     x[0]=0.5
     x[1]=0.5
     loc=Locator(v.getFunctionSpace(),x)
     valAtX=loc(v)
     self.assertTrue(valAtX[0]>10*valAtX[1])
Exemple #5
0
 def getInverse(self, s):
     """
     returns the value of the inverse of the mapping for s
     """
     ms = whereZero(s)
     ms0 = whereZero(self.__sigma0)
     m = 1 / self.__a * log(((1 - ms) * s + ms * 1) / ((1 - ms0) * self.__sigma0 + ms0 * 1)) * (1 - ms) * (1 - ms0)
     return m
Exemple #6
0
 def test_run(self):
     #test just to confirm nlpde works   
     u=Symbol('u', dim=self.domain.getDim())
     nlpde = NonlinearPDE(self.domain, u)
     x=self.domain.getX()
     gammaD=whereZero(x[0])+whereZero(x[1])
     nlpde.setValue(X=grad(u), Y=5*u, q=gammaD, r=1)
     v=nlpde.getSolution(u=1)
 def test_run(self):
     #test just to confirm nlpde works   
     u=Symbol('u', dim=self.domain.getDim())
     nlpde = NonlinearPDE(self.domain, u)
     x=self.domain.getX()
     gammaD=whereZero(x[0])+whereZero(x[1])
     nlpde.setValue(X=grad(u), Y=5*u, q=gammaD, r=1)
     v=nlpde.getSolution(u=1)
    def getPotentialNumeric(self):
        """
        Returns 3 list each made up of a number of list containing primary, secondary and total
        potentials diferences. Each of the lists contain a list for each value of n.
        """
        primCon=self.primaryConductivity
        coords=self.domain.getX()
        tol=1e-8
        pde=LinearPDE(self.domain, numEquations=1)
        pde.getSolverOptions().setTolerance(tol)
        pde.setSymmetryOn()
        primaryPde=LinearPDE(self.domain, numEquations=1)
        primaryPde.getSolverOptions().setTolerance(tol)
        primaryPde.setSymmetryOn()
        DIM=self.domain.getDim()
        x=self.domain.getX()
        q=es.whereZero(x[DIM-1]-es.inf(x[DIM-1]))
        for i in xrange(DIM-1):
            xi=x[i]
            q+=es.whereZero(xi-es.inf(xi))+es.whereZero(xi-es.sup(xi))
        A = self.secondaryConductivity * es.kronecker(self.domain)
        APrimary = self.primaryConductivity * es.kronecker(self.domain)
        pde.setValue(A=A,q=q)
        primaryPde.setValue(A=APrimary,q=q)

        delPhiSecondaryList = []
        delPhiPrimaryList = []
        delPhiTotalList = []
        for i in range(1,self.n+1): # 1 to n
            maxR = self.numElectrodes - 1 - (2*i) #max amount of readings that will fit in the survey
            delPhiSecondary = []
            delPhiPrimary = []
            delPhiTotal = []
            for j in range(maxR):
                y_dirac=es.Scalar(0,es.DiracDeltaFunctions(self.domain))
                y_dirac.setTaggedValue(self.electrodeTags[j],self.current)
                y_dirac.setTaggedValue(self.electrodeTags[j + ((2*i) + 1)],-self.current)
                self.sources.append([self.electrodeTags[j], self.electrodeTags[j + ((2*i) + 1)]])
                primaryPde.setValue(y_dirac=y_dirac)
                numericPrimaryPot = primaryPde.getSolution()
                X=(primCon-self.secondaryConductivity) * es.grad(numericPrimaryPot)
                pde.setValue(X=X)
                u=pde.getSolution()
                loc=Locator(self.domain,[self.electrodes[j+i],self.electrodes[j+i+1]])
                self.samples.append([self.electrodeTags[j+i],self.electrodeTags[j+i+1]])
                valPrimary=loc.getValue(numericPrimaryPot)
                valSecondary=loc.getValue(u)
                delPhiPrimary.append(valPrimary[1]-valPrimary[0])
                delPhiSecondary.append(valSecondary[1]-valSecondary[0])
                delPhiTotal.append(delPhiPrimary[j]+delPhiSecondary[j])
            delPhiPrimaryList.append(delPhiPrimary)
            delPhiSecondaryList.append(delPhiSecondary)
            delPhiTotalList.append(delPhiTotal)

        self.delPhiPrimaryList=delPhiPrimaryList
        self.delPhiSecondaryList=delPhiSecondaryList
        self.delPhiTotalList = delPhiTotalList
        return [delPhiPrimaryList, delPhiSecondaryList, delPhiTotalList]
    def getPotentialNumeric(self):
        """
        Returns 3 list each made up of a number of list containing primary, secondary and total
        potentials diferences. Each of the lists contain a list for each value of n.
        """
        primCon=self.primaryConductivity
        coords=self.domain.getX()
        tol=1e-8
        pde=LinearPDE(self.domain, numEquations=1)
        pde.getSolverOptions().setTolerance(tol)
        pde.setSymmetryOn()
        primaryPde=LinearPDE(self.domain, numEquations=1)
        primaryPde.getSolverOptions().setTolerance(tol)
        primaryPde.setSymmetryOn()
        DIM=self.domain.getDim()
        x=self.domain.getX()
        q=whereZero(x[DIM-1]-inf(x[DIM-1]))
        for i in xrange(DIM-1):
            xi=x[i]
            q+=whereZero(xi-inf(xi))+whereZero(xi-sup(xi))
        A = self.secondaryConductivity * kronecker(self.domain)
        APrimary = self.primaryConductivity * kronecker(self.domain)
        pde.setValue(A=A,q=q)
        primaryPde.setValue(A=APrimary,q=q)

        delPhiSecondaryList = []
        delPhiPrimaryList = []
        delPhiTotalList = []
        for i in range(1,self.n+1): # 1 to n
            maxR = self.numElectrodes - 1 - (2*i) #max amount of readings that will fit in the survey
            delPhiSecondary = []
            delPhiPrimary = []
            delPhiTotal = []
            for j in range(maxR):
                y_dirac=Scalar(0,DiracDeltaFunctions(self.domain))
                y_dirac.setTaggedValue(self.electrodeTags[j],self.current)
                y_dirac.setTaggedValue(self.electrodeTags[j + ((2*i) + 1)],-self.current)
                self.sources.append([self.electrodeTags[j], self.electrodeTags[j + ((2*i) + 1)]])
                primaryPde.setValue(y_dirac=y_dirac)
                numericPrimaryPot = primaryPde.getSolution()
                X=(primCon-self.secondaryConductivity) * grad(numericPrimaryPot)
                pde.setValue(X=X)
                u=pde.getSolution()
                loc=Locator(self.domain,[self.electrodes[j+i],self.electrodes[j+i+1]])
                self.samples.append([self.electrodeTags[j+i],self.electrodeTags[j+i+1]])
                valPrimary=loc.getValue(numericPrimaryPot)
                valSecondary=loc.getValue(u)
                delPhiPrimary.append(valPrimary[1]-valPrimary[0])
                delPhiSecondary.append(valSecondary[1]-valSecondary[0])
                delPhiTotal.append(delPhiPrimary[j]+delPhiSecondary[j])
            delPhiPrimaryList.append(delPhiPrimary)
            delPhiSecondaryList.append(delPhiSecondary)
            delPhiTotalList.append(delPhiTotal)

        self.delPhiPrimaryList=delPhiPrimaryList
        self.delPhiSecondaryList=delPhiSecondaryList
        self.delPhiTotalList = delPhiTotalList
        return [delPhiPrimaryList, delPhiSecondaryList, delPhiTotalList]
    def getPotential(self):
        """
        returns a list containing 3 lists one for each the primary, secondary
        and total potential.
        """


        primCon=self.primaryConductivity
        coords=self.domain.getX()
        pde=LinearPDE(self.domain, numEquations=1)
        tol=1e-8
        pde.getSolverOptions().setTolerance(tol)
        pde.setSymmetryOn()

        DIM=self.domain.getDim()
        x=self.domain.getX()
        q=es.whereZero(x[DIM-1]-es.inf(x[DIM-1]))
        for i in xrange(DIM-1):
            xi=x[i]
            q+=es.whereZero(xi-es.inf(xi))+es.whereZero(xi-es.sup(xi))
        A = self.secondaryConductivity * es.kronecker(self.domain)
        pde.setValue(A=A,q=q)

        delPhiSecondary = []
        delPhiPrimary = []
        delPhiTotal = []
        if(len(self.electrodes[0])==3):

            for i in range(self.numElectrodes-1):
                analyticRs=es.Data(0,(3,),es.ContinuousFunction(self.domain))
                analyticRs[0]=(coords[0]-self.electrodes[i][0])
                analyticRs[1]=(coords[1]-self.electrodes[i][1])
                analyticRs[2]=(coords[2])
                rsMag=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**0.5
                analyticPrimaryPot=(self.current*(1./primCon))/(2*pi*(rsMag+(es.whereZero(rsMag)*0.0000001))) #the magic number 0.0000001 is to avoid devide by 0
                analyticRsPolePower=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**1.5
                analyticRsPolePower = analyticRsPolePower+(es.whereZero(analyticRsPolePower)*0.0000001)
                gradUPrimary = es.Data(0,(3,),es.ContinuousFunction(self.domain))
                gradUPrimary[0] =(self.current/(2*pi*primCon)) * (analyticRs[0]/analyticRsPolePower)
                gradUPrimary[1] =(self.current/(2*pi*primCon)) * (analyticRs[1]/analyticRsPolePower)
                gradUPrimary[2] =(self.current/(2*pi*primCon)) * (analyticRs[2]/analyticRsPolePower)
                gradUPrimary=-gradUPrimary
                X=(primCon-self.secondaryConductivity) * gradUPrimary
                pde.setValue(X=X)
                u=pde.getSolution()
                loc=Locator(self.domain,self.electrodes[i+1])
                delPhiSecondary.append(loc.getValue(u))
                delPhiPrimary.append(loc.getValue(analyticPrimaryPot))
        else:
            raise NotImplementedError("2d forward model is not yet implemented")

        self.delPhiSecondary = delPhiSecondary
        self.delPhiPrimary = delPhiPrimary
        for i in range(len(delPhiPrimary)):
            delPhiTotal.append(delPhiPrimary[i] + delPhiSecondary[i])
        self.delPhiTotal=delPhiTotal
        return [delPhiPrimary, delPhiSecondary, delPhiTotal]
    def getPotential(self):
        """
        returns a list containing 3 lists one for each the primary, secondary
        and total potential.
        """


        primCon=self.primaryConductivity
        coords=self.domain.getX()
        pde=LinearPDE(self.domain, numEquations=1)
        tol=1e-8
        pde.getSolverOptions().setTolerance(tol)
        pde.setSymmetryOn()

        DIM=self.domain.getDim()
        x=self.domain.getX()
        q=whereZero(x[DIM-1]-inf(x[DIM-1]))
        for i in xrange(DIM-1):
            xi=x[i]
            q+=whereZero(xi-inf(xi))+whereZero(xi-sup(xi))
        A = self.secondaryConductivity * kronecker(self.domain)
        pde.setValue(A=A,q=q)

        delPhiSecondary = []
        delPhiPrimary = []
        delPhiTotal = []
        if(len(self.electrodes[0])==3):

            for i in range(self.numElectrodes-1):
                analyticRs=Data(0,(3,),ContinuousFunction(self.domain))
                analyticRs[0]=(coords[0]-self.electrodes[i][0])
                analyticRs[1]=(coords[1]-self.electrodes[i][1])
                analyticRs[2]=(coords[2])
                rsMag=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**0.5
                analyticPrimaryPot=(self.current*(1./primCon))/(2*pi*(rsMag+(whereZero(rsMag)*0.0000001))) #the magic number 0.0000001 is to avoid devide by 0
                analyticRsPolePower=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**1.5
                analyticRsPolePower = analyticRsPolePower+(whereZero(analyticRsPolePower)*0.0000001)
                gradUPrimary = Data(0,(3,),ContinuousFunction(self.domain))
                gradUPrimary[0] =(self.current/(2*pi*primCon)) * (analyticRs[0]/analyticRsPolePower)
                gradUPrimary[1] =(self.current/(2*pi*primCon)) * (analyticRs[1]/analyticRsPolePower)
                gradUPrimary[2] =(self.current/(2*pi*primCon)) * (analyticRs[2]/analyticRsPolePower)
                gradUPrimary=-gradUPrimary
                X=(primCon-self.secondaryConductivity) * gradUPrimary
                pde.setValue(X=X)
                u=pde.getSolution()
                loc=Locator(self.domain,self.electrodes[i+1])
                delPhiSecondary.append(loc.getValue(u))
                delPhiPrimary.append(loc.getValue(analyticPrimaryPot))
        else:
            raise NotImplementedError("2d forward model is not yet implemented")

        self.delPhiSecondary = delPhiSecondary
        self.delPhiPrimary = delPhiPrimary
        for i in range(len(delPhiPrimary)):
            delPhiTotal.append(delPhiPrimary[i] + delPhiSecondary[i])
        self.delPhiTotal=delPhiTotal
        return [delPhiPrimary, delPhiSecondary, delPhiTotal]
Exemple #12
0
 def getInverse(self, s):
     """
     returns the value of the inverse of the mapping for s
     """
     ms = whereZero(s)
     ms0 = whereZero(self.__sigma0)
     m = 1 / self.__a * log(
         ((1 - ms) * s + ms * 1) /
         ((1 - ms0) * self.__sigma0 + ms0 * 1)) * (1 - ms) * (1 - ms0)
     return m
Exemple #13
0
 def __setOutput(self):
     if self.__location_of_constraint is None:
         x = self.domain.getX()
         val = self.value
         if isinstance(val, int) or isinstance(val, float):
             shape = ()
         elif isinstance(val, list) or isinstance(val, tuple):
             shape = (len(val), )
         elif isinstance(val, numpy.ndarray):
             shape = val.shape
         elif val is None:
             shape = ()
         else:
             shape = val.getShape()
         self.__location_of_constraint = Data(0, shape,
                                              x.getFunctionSpace())
         if self.domain.getDim() == 3:
             x0, x1, x2 = x[0], x[1], x[2]
             if self.left:
                 self.__location_of_constraint += es.whereZero(
                     x0 - es.inf(x0), self.tol)
             if self.right:
                 self.__location_of_constraint += es.whereZero(
                     x0 - es.sup(x0), self.tol)
             if self.front:
                 self.__location_of_constraint += es.whereZero(
                     x1 - es.inf(x1), self.tol)
             if self.back:
                 self.__location_of_constraint += es.whereZero(
                     x1 - es.sup(x1), self.tol)
             if self.bottom:
                 self.__location_of_constraint += es.whereZero(
                     x2 - es.inf(x2), self.tol)
             if self.top:
                 self.__location_of_constraint += es.whereZero(
                     x2 - es.sup(x2), self.tol)
         else:
             x0, x1 = x[0], x[1]
             if self.left:
                 self.__location_of_constraint += es.whereZero(
                     x0 - es.inf(x0), self.tol)
             if self.right:
                 self.__location_of_constraint += es.whereZero(
                     x0 - es.sup(x0), self.tol)
             if self.bottom:
                 self.__location_of_constraint += es.whereZero(
                     x1 - es.inf(x1), self.tol)
             if self.top:
                 self.__location_of_constraint += es.whereZero(
                     x1 - es.sup(x1), self.tol)
         if not self.value is None:
             self.__value_of_constraint = self.__location_of_constraint * self.value
    def setCoefficients(self, pde, system):
        """sets PDE coefficients"""        
        FAC_DIAG = self.FAC_DIAG
        FAC_OFFDIAG =self.FAC_OFFDIAG
        x = Solution(self.domain).getX()
        mask = whereZero(x[0])
        dim = self.domain.getDim()
        u_ex = self.getSolution(system)
        g_ex = self.getGrad(system)

        if system:
            A = Tensor4(0., Function(self.domain))
            for i in range(dim):
                A[i,:,i,:] = kronecker(dim)

            Y = Vector(0., Function(self.domain))
            if dim == 2:
                Y[0] = u_ex[0]*FAC_DIAG+u_ex[1]*FAC_OFFDIAG-20
                Y[1] = u_ex[1]*FAC_DIAG+u_ex[0]*FAC_OFFDIAG-10
            else:
                Y[0] = u_ex[0]*FAC_DIAG+u_ex[2]*FAC_OFFDIAG+u_ex[1]*FAC_OFFDIAG-60
                Y[1] = u_ex[1]*FAC_DIAG+u_ex[0]*FAC_OFFDIAG+u_ex[2]*FAC_OFFDIAG-20
                Y[2] = u_ex[2]*FAC_DIAG+u_ex[1]*FAC_OFFDIAG+u_ex[0]*FAC_OFFDIAG-22
            pde.setValue(r=u_ex, q=mask*numpy.ones(dim,),
                         A=A,
                         D=kronecker(dim)*(FAC_DIAG-FAC_OFFDIAG)+numpy.ones((dim,dim))*FAC_OFFDIAG,
                         Y=Y,
                         y=matrixmult(g_ex,self.domain.getNormal()))
        else:
            pde.setValue(r=u_ex, q=mask, A=kronecker(dim),
                         y=inner(g_ex, self.domain.getNormal()))
            if dim == 2:
                pde.setValue(Y=-20.)
            else:
                pde.setValue(Y=-60.)
    def _setCoefficients(self, pde, system):
        """sets PDE coefficients"""
        FAC_DIAG = 1.
        FAC_OFFDIAG = -0.4
        x = Solution(self.domain).getX()
        mask = whereZero(x[0])
        dim = self.domain.getDim()
        u_ex = self._getSolution(system)
        g_ex = self._getGrad(system)

        if system:
            A = Tensor4(0., Function(self.domain))
            for i in range(dim):
                A[i,:,i,:] = kronecker(dim)

            Y = Vector(0., Function(self.domain))
            if dim == 2:
                Y[0] = u_ex[0]*FAC_DIAG+u_ex[1]*FAC_OFFDIAG
                Y[1] = u_ex[1]*FAC_DIAG+u_ex[0]*FAC_OFFDIAG
            else:
                Y[0] = u_ex[0]*FAC_DIAG+u_ex[2]*FAC_OFFDIAG+u_ex[1]*FAC_OFFDIAG
                Y[1] = u_ex[1]*FAC_DIAG+u_ex[0]*FAC_OFFDIAG+u_ex[2]*FAC_OFFDIAG
                Y[2] = u_ex[2]*FAC_DIAG+u_ex[1]*FAC_OFFDIAG+u_ex[0]*FAC_OFFDIAG
            pde.setValue(r=u_ex, q=mask*numpy.ones(dim,),
                         A=A,
                         D=kronecker(dim)*(FAC_DIAG-FAC_OFFDIAG)+numpy.ones((dim,dim))*FAC_OFFDIAG,
                         Y=Y,
                         y=matrixmult(g_ex,self.domain.getNormal()))
        else:
            pde.setValue(r=u_ex, q=mask, A=kronecker(dim),
                         y=inner(g_ex, self.domain.getNormal()))
Exemple #16
0
 def __setOutput(self):
     x = self.domain.getX()
     self.__location_of_constraint = es.Scalar(0, x.getFunctionSpace())
     if self.domain.getDim() == 3:
         x0, x1, x2 = x[0], x[1], x[2]
         d = max(
             es.sup(x0) - es.inf(x0),
             sup(x1) - es.inf(x1),
             sup(x2) - es.inf(x2))
         if self.left:
             self.__location_of_constraint += es.whereZero(
                 x0 - es.inf(x0), self.tol * d)
         if self.right:
             self.__location_of_constraint += es.whereZero(
                 x0 - es.sup(x0), self.tol * d)
         if self.front:
             self.__location_of_constraint += es.whereZero(
                 x1 - es.inf(x1), self.tol * d)
         if self.back:
             self.__location_of_constraint += es.whereZero(
                 x1 - es.sup(x1), self.tol * d)
         if self.bottom:
             self.__location_of_constraint += es.whereZero(
                 x2 - es.inf(x2), self.tol * d)
         if self.top:
             self.__location_of_constraint += es.whereZero(
                 x2 - es.sup(x2), self.tol * d)
     else:
         x0, x1 = x[0], x[1]
         d = max(es.sup(x0) - es.inf(x0), es.sup(x1) - es.inf(x1))
         if self.left:
             self.__location_of_constraint += es.whereZero(
                 x0 - es.inf(x0), self.tol * d)
         if self.right:
             self.__location_of_constraint += es.whereZero(
                 x0 - es.sup(x0), self.tol * d)
         if self.bottom:
             self.__location_of_constraint += es.whereZero(
                 x1 - es.inf(x1), self.tol * d)
         if self.top:
             self.__location_of_constraint += es.whereZero(
                 x1 - es.sup(x1), self.tol * d)
     if not self.value is None:
         self.__value_of_constraint = self.__location_of_constraint * self.value
Exemple #17
0
 def __setOutput(self):
     x = self.domain.getX()
     self.__location_of_constraint = es.Scalar(0, x.getFunctionSpace())
     if self.domain.getDim() == 3:
         vertex = [es.inf(x[0]), es.inf(x[1]), es.inf(x[2])]
     else:
         vertex = [es.inf(x[0]), es.inf(x[1])]
     self.__location_of_constraint = es.whereZero(es.length(x - vertex),
                                                  self.tol)
     if not self.value is None:
         self.__value_of_constraint = self.__location_of_constraint * self.value
Exemple #18
0
 def test_setVals1eq(self):
     #test setting Coefficients with 1 equation
     dim=self.domain.getDim()
     u=Symbol('u', dim=dim)
     nlpde = NonlinearPDE(self.domain, u)
     x=self.domain.getX()
     gammaD=whereZero(x[0])+whereZero(x[1])
     nlpde.setValue(X=grad(u), Y=5*u, q=gammaD, r=1)
     A=nlpde.getCoefficient("A")
     B=nlpde.getCoefficient("B")
     C=nlpde.getCoefficient("C")
     D=nlpde.getCoefficient("D")
     if dim==2:
         ATest=numpy.empty((2,2), dtype=object)
         ATest[0]=1,0
         ATest[1]=0,1
         BTest=numpy.empty((2,), dtype=object)
         BTest[0]=0
         BTest[1]=0
         CTest=BTest
         self.assertTrue(A-ATest==Symbol(numpy.zeros((2,2))))
         self.assertTrue(B-BTest==Symbol(numpy.zeros((2,))))
         self.assertTrue(C-CTest==Symbol(numpy.zeros((2,))))
         temp=Symbol('temp') 
         self.assertTrue(D-temp.subs(temp,5)==temp.subs(temp,0))
     else:
         ATest=numpy.empty((3,3), dtype=object)
         ATest[0]=1,0,0
         ATest[1]=0,1,0
         ATest[2]=0,0,1
         BTest=numpy.empty((3,), dtype=object)
         BTest[0]=0
         BTest[1]=0
         BTest[2]=0
         CTest=BTest
         self.assertTrue(A-ATest==Symbol(numpy.zeros((3,3))))
         self.assertTrue(B-BTest==Symbol(numpy.zeros((3,))))
         self.assertTrue(C-CTest==Symbol(numpy.zeros((3,))))
         temp=Symbol('temp') 
         self.assertTrue(D-temp.subs(temp,5)==temp.subs(temp,0))
 def test_setVals1eq(self):
     #test setting Coefficients with 1 equation
     dim=self.domain.getDim()
     u=Symbol('u', dim=dim)
     nlpde = NonlinearPDE(self.domain, u)
     x=self.domain.getX()
     gammaD=whereZero(x[0])+whereZero(x[1])
     nlpde.setValue(X=grad(u), Y=5*u, q=gammaD, r=1)
     A=nlpde.getCoefficient("A")
     B=nlpde.getCoefficient("B")
     C=nlpde.getCoefficient("C")
     D=nlpde.getCoefficient("D")
     if dim==2:
         ATest=numpy.empty((2,2), dtype=object)
         ATest[0]=1,0
         ATest[1]=0,1
         BTest=numpy.empty((2,), dtype=object)
         BTest[0]=0
         BTest[1]=0
         CTest=BTest
         self.assertTrue(A-ATest==Symbol(numpy.zeros((2,2))))
         self.assertTrue(B-BTest==Symbol(numpy.zeros((2,))))
         self.assertTrue(C-CTest==Symbol(numpy.zeros((2,))))
         temp=Symbol('temp') 
         self.assertTrue(D-temp.subs(temp,5)==temp.subs(temp,0))
     else:
         ATest=numpy.empty((3,3), dtype=object)
         ATest[0]=1,0,0
         ATest[1]=0,1,0
         ATest[2]=0,0,1
         BTest=numpy.empty((3,), dtype=object)
         BTest[0]=0
         BTest[1]=0
         BTest[2]=0
         CTest=BTest
         self.assertTrue(A-ATest==Symbol(numpy.zeros((3,3))))
         self.assertTrue(B-BTest==Symbol(numpy.zeros((3,))))
         self.assertTrue(C-CTest==Symbol(numpy.zeros((3,))))
         temp=Symbol('temp') 
         self.assertTrue(D-temp.subs(temp,5)==temp.subs(temp,0))
Exemple #20
0
 def test_setVals2eq(self):
     #test setting Coefficients with 2 coeficients
     dim = self.domain.getDim()
     u = Symbol('u', (2, ), dim=dim)
     q = Symbol('q', (2, 2))
     nlpde = NonlinearPDE(self.domain, u, debug=0)
     x = self.domain.getX()
     gammaD = whereZero(
         x[1]) * [1, 1]  #+whereZero(x[0])*[1,0]+whereZero(x[0]-1)*[1,0]
     yconstraint = FunctionOnBoundary(self.domain).getX()[1]
     nlpde.setValue(X=grad(u), q=gammaD, Y=[-50, -50] * u)
     A = nlpde.getCoefficient("A")
     B = nlpde.getCoefficient("B")
     C = nlpde.getCoefficient("C")
     D = nlpde.getCoefficient("D")
     if dim == 2:
         ATest = numpy.empty((2, 2, 2, 2), dtype=object)
         ATest[0] = (((1, 0), (0, 0)), ((0, 1), (0, 0)))
         ATest[1] = (((0, 0), (1, 0)), ((0, 0), (0, 1)))
         BTest = numpy.empty((2, 2, 2), dtype=object)
         BTest[0] = 0
         BTest[1] = 0
         CTest = BTest
         DTest = numpy.empty((2, 2), dtype=object)
         DTest[0] = (-50, 0)
         DTest[1] = (0, -50)
         self.assertTrue(numpy.ndarray.__eq__(ATest, A).all())
         self.assertTrue(numpy.ndarray.__eq__(BTest, B).all())
         self.assertTrue(numpy.ndarray.__eq__(CTest, C).all())
         self.assertTrue(numpy.ndarray.__eq__(DTest, D).all())
     else:
         ATest = numpy.empty((2, 3, 2, 3), dtype=object)
         ATest[0] = (((1, 0, 0), (0, 0, 0)), ((0, 1, 0), (0, 0, 0)),
                     ((0, 0, 1), (0, 0, 0)))
         ATest[1] = (((0, 0, 0), (1, 0, 0)), ((0, 0, 0), (0, 1, 0)),
                     ((0, 0, 0), (0, 0, 1)))
         #ATest[1]=(((0,0,1),(1,0,0)),((0,0),(0,1)))
         BTest = numpy.empty((2, 3, 2), dtype=object)
         BTest[0] = 0
         BTest[1] = 0
         CTest = numpy.empty((2, 2, 3), dtype=object)
         CTest[0] = 0
         CTest[1] = 0
         DTest = numpy.empty((2, 2), dtype=object)
         DTest[0] = (-50, 0)
         DTest[1] = (0, -50)
         self.assertTrue(numpy.ndarray.__eq__(BTest, B).all())
         self.assertTrue(numpy.ndarray.__eq__(CTest, C).all())
         self.assertTrue(numpy.ndarray.__eq__(DTest, D).all())
 def test_setVals2eq(self):
     #test setting Coefficients with 2 coeficients
     dim=self.domain.getDim()
     u = Symbol('u',(2,), dim=dim)
     q = Symbol('q', (2,2))
     nlpde = NonlinearPDE(self.domain, u, debug=0)
     x = self.domain.getX()
     gammaD=whereZero(x[1])*[1,1]#+whereZero(x[0])*[1,0]+whereZero(x[0]-1)*[1,0]  
     yconstraint = FunctionOnBoundary(self.domain).getX()[1]
     nlpde.setValue(X=grad(u),q=gammaD,Y=[-50,-50]*u)
     A=nlpde.getCoefficient("A")
     B=nlpde.getCoefficient("B")
     C=nlpde.getCoefficient("C")
     D=nlpde.getCoefficient("D")
     if dim==2:
         ATest=numpy.empty((2,2,2,2),dtype=object)
         ATest[0]=(((1,0),(0,0)),((0,1),(0,0)))
         ATest[1]=(((0,0),(1,0)),((0,0),(0,1)))
         BTest=numpy.empty((2,2,2),dtype=object)
         BTest[0]=0
         BTest[1]=0
         CTest=BTest
         DTest=numpy.empty((2,2),dtype=object)
         DTest[0]=(-50,0)
         DTest[1]=(0,-50)
         self.assertTrue(numpy.ndarray.__eq__(ATest, A).all())
         self.assertTrue(numpy.ndarray.__eq__(BTest, B).all())
         self.assertTrue(numpy.ndarray.__eq__(CTest, C).all())
         self.assertTrue(numpy.ndarray.__eq__(DTest, D).all())
     else:
         ATest=numpy.empty((2,3,2,3),dtype=object)
         ATest[0]=(((1,0,0),(0,0,0)),((0,1,0),(0,0,0)),((0,0,1),(0,0,0)))
         ATest[1]=(((0,0,0),(1,0,0)),((0,0,0),(0,1,0)),((0,0,0),(0,0,1)))
         #ATest[1]=(((0,0,1),(1,0,0)),((0,0),(0,1)))
         BTest=numpy.empty((2,3,2),dtype=object)
         BTest[0]=0
         BTest[1]=0
         CTest=numpy.empty((2,2,3),dtype=object)
         CTest[0]=0
         CTest[1]=0
         DTest=numpy.empty((2,2),dtype=object)
         DTest[0]=(-50,0)
         DTest[1]=(0,-50)
         self.assertTrue(numpy.ndarray.__eq__(BTest, B).all())
         self.assertTrue(numpy.ndarray.__eq__(CTest, C).all())
         self.assertTrue(numpy.ndarray.__eq__(DTest, D).all())
Exemple #22
0
 def __setOutput(self):
     x = self.domain.getX()
     self.__location_of_constraint = es.Vector(0, x.getFunctionSpace())
     if self.domain.getDim() == 3:
         vertex = [es.inf(x[0]), es.inf(x[1]), es.inf(x[2])]
         msk = numpy.zeros((3, ))
         if self.comp_mask[0]: msk[0] = 1
         if self.comp_mask[1]: msk[1] = 1
         if self.comp_mask[2]: msk[2] = 1
     else:
         vertex = [es.inf(x[0]), es.inf(x[1])]
         msk = numpy.zeros((2, ))
         if self.comp_mask[0]: msk[0] = 1
         if self.comp_mask[1]: msk[1] = 1
     self.__location_of_constraint = es.whereZero(
         es.length(x - vertex), self.tol) * numpy.ones(shape)
     if not self.value is None:
         self.__value_of_constraint = self.__location_of_constraint * self.value
Exemple #23
0
def setup_rectangular_mesh(Parameters,
                           mesh_filename):
    """
    Create a rectangular mesh.
    """
    nx = int(math.ceil(Parameters.L / Parameters.cellsize_x))
    ny = int(math.ceil(Parameters.thickness / Parameters.cellsize_y))
    mesh = fl.Rectangle(l0=Parameters.L, l1=Parameters.thickness,
                        n0=nx, n1=ny)

    # calculate surface
    xy = mesh.getX()
    z_surface = (xy[0] - xy[0] + 1) * Parameters.thickness
    surface = es.whereZero(xy[1] - z_surface)
    sea_surface = None
    seawater = None

    return mesh, surface, sea_surface, seawater, z_surface
Exemple #24
0
 def __setOutput(self):
     if self.__location_of_constraint is None:
         x = self.domain.getX()
         val = self.value
         if isinstance(val, int) or isinstance(val, float):
             shape = ()
         elif isinstance(val, list) or isinstance(val, tuple):
             shape = (len(val), )
         elif isinstance(val, numpy.ndarray):
             shape = val.shape
         elif val is None:
             shape = ()
         else:
             shape = val.getShape()
         if self.domain.getDim() == 3:
             vertex = [es.inf(x[0]), es.inf(x[1]), es.inf(x[2])]
         else:
             vertex = [es.inf(x[0]), es.inf(x[1])]
         self.__location_of_constraint = es.whereZero(
             es.length(x - vertex), self.tol) * numpy.ones(shape)
         if not self.value is None:
             self.__value_of_constraint = self.__location_of_constraint * self.value
    def getPotential(self):
        """
        Returns 3 list each made up of a number of list containing primary, secondary and total
        potentials diferences. Each of the lists contain a list for each value of n.
        """

        primCon=self.primaryConductivity
        coords=self.domain.getX()
        pde=LinearPDE(self.domain, numEquations=1)
        tol=1e-8
        pde.getSolverOptions().setTolerance(tol)
        pde.setSymmetryOn()

        DIM=self.domain.getDim()
        x=self.domain.getX()
        q=whereZero(x[DIM-1]-inf(x[DIM-1]))
        for i in xrange(DIM-1):
            xi=x[i]
            q+=whereZero(xi-inf(xi))+whereZero(xi-sup(xi))
        A = self.secondaryConductivity * kronecker(self.domain)
        pde.setValue(A=A,q=q)

        delPhiSecondaryList = []
        delPhiPrimaryList = []
        delPhiTotalList = []
        for i in range(1,self.n+1): # 1 to n
            maxR = self.numElectrodes - 1 - i #max amount of readings that will fit in the survey
            delPhiSecondary = []
            delPhiPrimary = []
            delPhiTotal = []
            for j in range(maxR):
                analyticRs=Data(0,(3,),ContinuousFunction(self.domain))
                analyticRs[0]=(coords[0]-self.electrodes[j][0])
                analyticRs[1]=(coords[1]-self.electrodes[j][1])
                analyticRs[2]=(coords[2])
                rsMag=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**0.5
                analyticPrimaryPot=(self.current*(1./primCon))/(2*pi*(rsMag+(whereZero(rsMag)*0.0000001))) #the magic number 0.0000001 is to avoid devide by 0

                analyticRsPolePower=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**1.5
                analyticRsPolePower = analyticRsPolePower+(whereZero(analyticRsPolePower)*0.0000001)
                gradUPrimary = Data(0,(3,),ContinuousFunction(self.domain))
                gradUPrimary[0] =(self.current/(2*pi*primCon)) * (analyticRs[0]/analyticRsPolePower)
                gradUPrimary[1] =(self.current/(2*pi*primCon)) * (analyticRs[1]/analyticRsPolePower)
                gradUPrimary[2] =(self.current/(2*pi*primCon)) * (analyticRs[2]/analyticRsPolePower)
                gradUPrimary=-gradUPrimary
                X=(primCon-self.secondaryConductivity) * gradUPrimary
                pde.setValue(X=X)
                u=pde.getSolution()
                loc=Locator(self.domain,[self.electrodes[i+j],self.electrodes[i+j+1]])
                valPrimary=loc.getValue(analyticPrimaryPot)
                valSecondary=loc.getValue(u)
                delPhiPrimary.append(valPrimary[1]-valPrimary[0])
                delPhiSecondary.append(valSecondary[1]-valSecondary[0])
                delPhiTotal.append(delPhiPrimary[j]+delPhiSecondary[j])

            delPhiPrimaryList.append(delPhiPrimary)
            delPhiSecondaryList.append(delPhiSecondary)
            delPhiTotalList.append(delPhiTotal)



        self.delPhiPrimaryList=delPhiPrimaryList
        self.delPhiSecondaryList=delPhiSecondaryList
        self.delPhiTotalList = delPhiTotalList

        return [delPhiPrimaryList, delPhiSecondaryList, delPhiTotalList]
    def getPotential(self):
        """
        Returns 3 list each made up of a number of list containing primary, secondary and total
        potentials diferences. Each of the lists contain a list for each value of n.
        """
        coords=self.domain.getX()
        pde=LinearPDE(self.domain, numEquations=1)
        tol=1e-8
        pde.getSolverOptions().setTolerance(tol)
        pde.setSymmetryOn()
        primCon=self.primaryConductivity
        DIM=self.domain.getDim()
        x=self.domain.getX()
        q=whereZero(x[DIM-1]-inf(x[DIM-1]))
        for i in xrange(DIM-1):
            xi=x[i]
            q+=whereZero(xi-inf(xi))+whereZero(xi-sup(xi))
        A = self.secondaryConductivity * kronecker(self.domain)
        pde.setValue(A=A,q=q)


        delPhiSecondaryList = []
        delPhiPrimaryList = []
        delPhiTotalList = []
        for i in range(1,self.n+1): # 1 to n
            maxR = self.numElectrodes - 2 - (i) #max amount of readings that will fit in the survey
            delPhiSecondary = []
            delPhiPrimary = []
            delPhiTotal = []
            for j in range(maxR):
                analyticRsOne=Data(0,(3,),ContinuousFunction(self.domain))
                analyticRsOne[0]=(coords[0]-self.electrodes[j][0])
                analyticRsOne[1]=(coords[1]-self.electrodes[j][1])
                analyticRsOne[2]=(coords[2])
                rsMagOne=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**0.5
                analyticRsTwo=Data(0,(3,),ContinuousFunction(self.domain))
                analyticRsTwo[0]=(coords[0]-self.electrodes[j + 1][0])
                analyticRsTwo[1]=(coords[1]-self.electrodes[j + 1][1])
                analyticRsTwo[2]=(coords[2])
                rsMagTwo=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**0.5
                rsMagOne+=(whereZero(rsMagOne)*0.0000001)
                rsMagTwo+=(whereZero(rsMagTwo)*0.0000001)
                analyticPrimaryPot=(self.current/(2*pi*primCon*rsMagTwo))-(self.current/(2*pi*primCon*rsMagOne))

                analyticRsOnePower=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**1.5
                analyticRsOnePower = analyticRsOnePower+(whereZero(analyticRsOnePower)*0.0001)
                analyticRsTwoPower=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**1.5
                analyticRsTwoPower = analyticRsTwoPower+(whereZero(analyticRsTwoPower)*0.0001)

                gradAnalyticPrimaryPot = Data(0,(3,),ContinuousFunction(self.domain))
                gradAnalyticPrimaryPot[0] =(self.current/(2*pi*primCon)) * ((analyticRsOne[0]/analyticRsOnePower) - (analyticRsTwo[0]/analyticRsTwoPower))
                gradAnalyticPrimaryPot[1] =(self.current/(2*pi*primCon)) * ((analyticRsOne[1]/analyticRsOnePower) - (analyticRsTwo[1]/analyticRsTwoPower))
                gradAnalyticPrimaryPot[2] =(self.current/(2*pi*primCon)) * ((analyticRsOne[2]/analyticRsOnePower) - (analyticRsTwo[2]/analyticRsTwoPower))
                X=(primCon-self.secondaryConductivity) * (gradAnalyticPrimaryPot)
                pde.setValue(X=X)
                u=pde.getSolution()
                loc=Locator(self.domain,[self.electrodes[1+j+i],self.electrodes[j+i+2]])
                valPrimary=loc.getValue(analyticPrimaryPot)
                valSecondary=loc.getValue(u)
                delPhiPrimary.append(valPrimary[1]-valPrimary[0])
                delPhiSecondary.append(valSecondary[1]-valSecondary[0])
                delPhiTotal.append(delPhiPrimary[j]+delPhiSecondary[j])
            delPhiPrimaryList.append(delPhiPrimary)
            delPhiSecondaryList.append(delPhiSecondary)
            delPhiTotalList.append(delPhiTotal)

        self.delPhiPrimaryList=delPhiPrimaryList
        self.delPhiSecondaryList=delPhiSecondaryList
        self.delPhiTotalList = delPhiTotalList
        return [delPhiPrimaryList, delPhiSecondaryList, delPhiTotalList]
# 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)")

if SiloOutput:
    saveSilo(fn, PROPERTY=initial_model, mask=reg_mask)
    print('SILO file written with the following fields: Property (kg/m^3 or Pa), mask')


def ResidualCalculation(reg_mask):
Exemple #28
0
 def __setOutput(self):
     x = self.domain.getX()
     self.__location_of_constraint = es.Vector(0, x.getFunctionSpace())
     if self.domain.getDim() == 3:
         x0, x1, x2 = x[0], x[1], x[2]
         d = max(
             es.sup(x0) - es.inf(x0),
             es.sup(x1) - es.inf(x1),
             es.sup(x2) - es.inf(x2))
         left_mask = es.whereZero(x0 - es.inf(x0), self.tol * d)
         if self.left[0]:
             self.__location_of_constraint += left_mask * [1., 0., 0.]
         if self.left[1]:
             self.__location_of_constraint += left_mask * [0., 1., 0.]
         if self.left[2]:
             self.__location_of_constraint += left_mask * [0., 0., 1.]
         right_mask = es.whereZero(x0 - es.sup(x0), self.tol * d)
         if self.right[0]:
             self.__location_of_constraint += right_mask * [1., 0., 0.]
         if self.right[1]:
             self.__location_of_constraint += right_mask * [0., 1., 0.]
         if self.right[2]:
             self.__location_of_constraint += right_mask * [0., 0., 1.]
         front_mask = es.whereZero(x1 - es.inf(x1), self.tol * d)
         if self.front[0]:
             self.__location_of_constraint += front_mask * [1., 0., 0.]
         if self.front[1]:
             self.__location_of_constraint += front_mask * [0., 1., 0.]
         if self.front[2]:
             self.__location_of_constraint += front_mask * [0., 0., 1.]
         back_mask = es.whereZero(x1 - es.sup(x1), self.tol * d)
         if self.back[0]:
             self.__location_of_constraint += back_mask * [1., 0., 0.]
         if self.back[1]:
             self.__location_of_constraint += back_mask * [0., 1., 0.]
         if self.back[2]:
             self.__location_of_constraint += back_mask * [0., 0., 1.]
         bottom_mask = es.whereZero(x2 - es.inf(x2), self.tol * d)
         if self.bottom[0]:
             self.__location_of_constraint += bottom_mask * [1., 0., 0.]
         if self.bottom[1]:
             self.__location_of_constraint += bottom_mask * [0., 1., 0.]
         if self.bottom[2]:
             self.__location_of_constraint += bottom_mask * [0., 0., 1.]
         top_mask = es.whereZero(x2 - es.sup(x2), self.tol * d)
         if self.top[0]:
             self.__location_of_constraint += top_mask * [1., 0., 0.]
         if self.top[1]:
             self.__location_of_constraint += top_mask * [0., 1., 0.]
         if self.top[2]:
             self.__location_of_constraint += top_mask * [0., 0., 1.]
         if not self.value is None:
             self.__value_of_constraint = self.__location_of_constraint * self.value
     else:
         x0, x1 = x[0], x[1]
         d = max(es.sup(x0) - es.inf(x0), es.sup(x1) - es.inf(x1))
         left_mask = es.whereZero(x0 - es.inf(x0), self.tol * d)
         if self.left[0]:
             self.__location_of_constraint += left_mask * [1., 0.]
         if self.left[1]:
             self.__location_of_constraint += left_mask * [0., 1.]
         right_mask = es.whereZero(x0 - es.sup(x0), self.tol * d)
         if self.right[0]:
             self.__location_of_constraint += right_mask * [1., 0.]
         if self.right[1]:
             self.__location_of_constraint += right_mask * [0., 1.]
         bottom_mask = es.whereZero(x1 - es.inf(x1), self.tol * d)
         if self.bottom[0]:
             self.__location_of_constraint += bottom_mask * [1., 0.]
         if self.bottom[1]:
             self.__location_of_constraint += bottom_mask * [0., 1.]
         top_mask = es.whereZero(x1 - es.sup(x1), self.tol * d)
         if self.top[0]:
             self.__location_of_constraint += top_mask * [1., 0.]
         if self.top[1]:
             self.__location_of_constraint += top_mask * [0., 1.]
         if not self.value is None:
             self.__value_of_constraint = self.__location_of_constraint * self.value[:
                                                                                     2]
Exemple #29
0
def run_model_scenario_and_analyze_results(Parameters,
                                           ModelOptions,
                                           mesh_function,
                                           run,
                                           model_scenario_name,
                                           scenario_parameters,
                                           scenario_param_names,
                                           df,
                                           model_output_folder,
                                           scriptdir,
                                           scenario_name,
                                           nscenarios,
                                           dfo=None):

    year = 365.25 * 24 * 60 * 60

    model_file_adj = ''

    run_id = 'S%i' % run
    #scenario_name = run_id

    print '-' * 30
    print 'model scenario id %s, run %i of %i' % (run_id, run + 1, nscenarios)
    print '-' * 30

    model_file_adj += 'run%s' % run_id

    # update default parameters in Parameter class
    for scenario_param_name, scenario_parameter in \
            zip(scenario_param_names, scenario_parameters):

        if scenario_parameter is not None:
            # find model parameter name to adjust
            model_param_name = scenario_param_name[:-2]

            print 'updating parameter %s from %s to %s' \
                  % (model_param_name,
                     str(getattr(Parameters, model_param_name)),
                     str(scenario_parameter))

            # update model parameter
            setattr(Parameters, model_param_name, scenario_parameter)

            # and add model param name to filename
            model_file_adj += '_%s_%s' % (model_param_name,
                                          str(scenario_parameter))

    # set filename for mesh

    mesh_fn = os.path.join(
        scriptdir, 'model_output',
        '_%i_%s.msh' % (random.randint(0, 100), '%s.msh' % scenario_name))

    # get names and values of input parameters
    attributes = inspect.getmembers(
        Parameters, lambda attribute: not (inspect.isroutine(attribute)))
    attribute_dict = [
        attribute for attribute in attributes
        if not (attribute[0].startswith('__') and attribute[0].endswith('__'))
    ]

    if df is None:
        # get attributes
        attribute_names = [
            attribute[0] for attribute in attributes if not (
                attribute[0].startswith('__') and attribute[0].endswith('__'))
        ]

        # set up pandas dataframe to store model input params
        ind = [0]
        columns = attribute_names
        df = pd.DataFrame(index=ind, columns=columns)

    # store input parameters in dataframe
    for a in attribute_dict:
        if a[0] in df.columns:
            if type(a[1]) is list:
                df.ix[run, a[0]] = str(a[1])
            else:
                df.ix[run, a[0]] = a[1]

    #
    start_time = datetime.datetime.now()
    start_date_str = '%i-%i-%i' % (start_time.day, start_time.month,
                                   start_time.year)
    start_time_str = '%i:%i:%i' % (start_time.hour, start_time.minute,
                                   start_time.second)
    df.ix[run, 'start_date'] = start_date_str
    df.ix[run, 'start_time'] = start_time_str

    # run a single model scenario
    model_results = grompy_salt_lib.run_model_scenario(
        scenario_name, model_output_folder, model_file_adj, ModelOptions,
        Parameters, mesh_function, mesh_fn)

    end_time = datetime.datetime.now()
    runtime = end_time - start_time
    df.ix[run, 'computing_runtime_sec'] = runtime.total_seconds()

    print 'processing model results'
    # get model results
    (mesh, surface, sea_surface, k_vector, P, Conc, rho_f, viscosity, h, q,
     q_abs, nodal_flux, Pdiff, Cdiff, pressure_differences_max,
     concentration_differences_max, pressure_differences_mean,
     concentration_differences_mean, dts, runtimes, nsteps, output_step,
     boundary_conditions, boundary_fluxes, boundary_flux_stats,
     reached_steady_state) = model_results

    dt = dts[-1]
    runtime = runtimes[-1]

    flux = q

    [
        specified_pressure_bnd, specified_pressure,
        specified_concentration_bnd, active_concentration_bnd,
        specified_concentration, specified_concentration_rho_f, rch_bnd_loc,
        active_seepage_bnd
    ] = boundary_conditions

    [
        flux_surface_norm, land_flux_in, land_flux_out, submarine_flux,
        submarine_flux_in, submarine_flux_out
    ] = boundary_fluxes

    [
        total_flux_over_surface_norm, total_rch_flux, total_seepage_flux,
        total_land_flux_in, total_land_flux_out, total_submarine_flux,
        total_submarine_flux_in, total_submarine_flux_out, ext_rch,
        ext_seepage, ext_inflow, ext_outflow, ext_inflow_land,
        ext_outflow_land, ext_inflow_sea, ext_outflow_sea,
        ext_outflow_land_threshold, ext_outflow_sea_threshold, min_land_flux,
        max_land_flux, min_seepage_flux, max_seepage_flux, min_submarine_flux,
        max_submarine_flux
    ] = boundary_flux_stats

    newcols = [
        'model_scenario_id', 'P_min', 'P_max', 'C_min', 'C_max', 'h_min',
        'h_max', 'vx_min', 'vx_max', 'vy_min', 'vy_max', 'max_pressure_change',
        'max_concentration_change', 'runtime', 'nsteps', 'dt_final',
        'total_flux_over_surface', 'total_rch_flux', 'total_seepage_flux',
        'total_land_flux_in', 'total_land_flux_out', 'total_submarine_flux',
        'total_submarine_flux_in', 'total_submarine_flux_out',
        'ext_inflow_land', 'ext_outflow_land', 'ext_inflow_sea',
        'ext_outflow_sea', 'ext_outflow_land_exc_threshold',
        'ext_outflow_sea_exc_threshold', 'min_land_flux', 'max_land_flux',
        'min_seepage_flux', 'max_seepage_flux', 'min_submarine_flux',
        'max_submarine_flux'
    ]

    if run == 0:
        df = grompy_salt_lib.add_cols_to_df(df, newcols)
        df['model_scenario_id'] = ''

    # store model results in dataframe
    df.ix[run, 'model_scenario_id'] = run_id
    if model_scenario_name is not None:
        df.ix[run, 'model_scenario_name'] = model_scenario_name
    df.ix[run, 'P_min'] = es.inf(P)
    df.ix[run, 'P_max'] = es.sup(P)
    df.ix[run, 'C_min'] = es.inf(Conc)
    df.ix[run, 'C_max'] = es.sup(Conc)
    df.ix[run, 'h_min'] = es.inf(h)
    df.ix[run, 'h_max'] = es.sup(h)
    df.ix[run, 'vx_min'] = es.inf(flux[0])
    df.ix[run, 'vx_max'] = es.sup(flux[0])
    df.ix[run, 'vy_min'] = es.inf(flux[1])
    df.ix[run, 'vy_max'] = es.sup(flux[1])
    df.ix[run, 'max_pressure_change'] = es.Lsup(Pdiff)
    df.ix[run, 'max_concentration_change'] = es.Lsup(Cdiff)
    df.ix[run, 'runtime'] = runtime
    df.ix[run, 'nsteps'] = nsteps
    df.ix[run, 'dt_final'] = dt
    df.ix[run, 'total_flux_over_surface'] = total_flux_over_surface_norm[1]
    df.ix[run, 'total_rch_flux'] = total_rch_flux[1]
    df.ix[run, 'total_seepage_flux'] = total_seepage_flux[1]
    df.ix[run, 'total_land_flux_in'] = total_land_flux_in[1]
    df.ix[run, 'total_land_flux_out'] = total_land_flux_out[1]
    df.ix[run, 'total_submarine_flux'] = total_submarine_flux[1]
    df.ix[run, 'total_submarine_flux_in'] = total_submarine_flux_in[1]
    df.ix[run, 'total_submarine_flux_out'] = total_submarine_flux_out[1]
    df.ix[run, 'ext_inflow_land'] = ext_inflow_land[1]
    df.ix[run, 'ext_outflow_land'] = ext_outflow_land[1]
    df.ix[run, 'ext_inflow_sea'] = ext_inflow_sea[1]
    df.ix[run, 'ext_outflow_sea'] = ext_outflow_sea[1]
    df.ix[run,
          'ext_outflow_land_exc_threshold'] = ext_outflow_land_threshold[1]
    df.ix[run, 'ext_outflow_sea_exc_threshold'] = ext_outflow_sea_threshold[1]
    df.ix[run, 'min_land_flux'] = min_land_flux
    df.ix[run, 'max_land_flux'] = max_land_flux
    df.ix[run, 'min_seepage_flux'] = min_seepage_flux
    df.ix[run, 'max_seepage_flux'] = max_seepage_flux
    df.ix[run, 'min_submarine_flux'] = min_submarine_flux
    df.ix[run, 'max_submarine_flux'] = max_submarine_flux

    # check if model output folder exists and create if not
    if not os.path.exists(model_output_folder):
        os.makedirs(model_output_folder)

    # save model output to vtk file
    if ModelOptions.save_vtk_files is True:
        vtk_folder = os.path.join(model_output_folder, 'vtk_files')

        if not os.path.exists(vtk_folder):
            os.makedirs(vtk_folder)

        fn_VTK = os.path.join(vtk_folder,
                              '%s_final_output.vtu' % model_file_adj)
        print 'saving vtk file of model results: %s' % fn_VTK

        nodata = -99999
        flux_surface_plot = nodal_flux * surface + \
            nodata * es.whereZero(surface)
        if sea_surface is None:
            sea_surface_save = surface
        else:
            sea_surface_save = sea_surface

        esys.weipa.saveVTK(fn_VTK,
                           pressure=P,
                           concentration=Conc,
                           h=h,
                           flux=flux,
                           qx=flux[0],
                           qy=flux[1],
                           kx=k_vector[0],
                           ky=k_vector[1],
                           nodal_flux=nodal_flux,
                           surface=surface,
                           sea_surface=sea_surface_save,
                           nodal_flux_surface=flux_surface_plot,
                           specified_pressure_bnd=specified_pressure_bnd,
                           active_seepage_bnd=active_seepage_bnd,
                           recharge_bnd=rch_bnd_loc,
                           active_concentration_bnd=active_concentration_bnd,
                           flux_surface_norm=flux_surface_norm,
                           land_flux_in=land_flux_in,
                           land_flux_out=land_flux_out,
                           submarine_flux=submarine_flux,
                           submarine_flux_in=submarine_flux_in,
                           submarine_flux_out=submarine_flux_out)

        df.ix[run, 'vtk_filename'] = fn_VTK

    if ModelOptions.save_variables_to_csv is True:

        var_folder = os.path.join(model_output_folder, 'variables')

        if not os.path.exists(var_folder):
            os.makedirs(var_folder)

        q_abs = (flux[0]**2 + flux[1]**2)**0.5

        model_vars = [P, Conc, h, q_abs * year, flux[0] * year, flux[1] * year]
        varlabels = ['P', 'conc', 'h', 'v', 'vx', 'vy']

        for var, varlabel in zip(model_vars, varlabels):
            xya, va = grompy_salt_lib.convert_to_array(var)

            filename = os.path.join(
                var_folder, '%s_%s_final.csv' % (varlabel, model_file_adj))
            csv_str = 'x,y,%s\n' % varlabel
            for x, y, vai in zip(xya[:, 0], xya[:, 1], va):
                csv_str += '%0.3f,%0.3f,%0.3e\n' % (x, y, vai)

            print 'writing final variable %s to file %s' % (varlabel, filename)

            fout = open(filename, 'w')
            fout.write(csv_str)
            fout.close()

        # write node and element locations and connectivity to separate file
        mesh_fn = os.path.join(var_folder, 'mesh_%s.fly' % model_file_adj)
        mesh.write(mesh_fn)

    # write pressure and concentration differences
    diff_folder = os.path.join(model_output_folder, 'P_and_C_change')
    if not os.path.exists(diff_folder):
        os.makedirs(diff_folder)

    df_diff = pd.DataFrame(columns=[
        'timestep', 'dt', 'runtime', 'pressure_change_mean',
        'pressure_change_max', 'concentration_change_mean',
        'concentration_change_max'
    ],
                           index=np.arange(nsteps))

    #df_diff['timestep'] = np.arange(nsteps)
    df_diff['pressure_change_mean'] = pressure_differences_mean
    df_diff['pressure_change_max'] = pressure_differences_max
    df_diff['concentration_change_mean'] = concentration_differences_mean
    df_diff['concentration_change_max'] = concentration_differences_max
    df_diff['dt'] = dts
    df_diff['runtime'] = runtimes

    filename = os.path.join(diff_folder,
                            'P_and_C_changes_%s_final.csv' % model_file_adj)

    print 'saving P and C changes to %s' % filename
    df_diff.to_csv(filename, index_label='timestep')

    # merge new model results dataframe with existing model output, if any
    dfm = df
    if dfo is not None:
        dfm = pd.concat([dfo, df])

        # keep columnn order
        dfm = dfm[list(dfo.columns)]

    # save model runs input params and results to .csv file
    today = datetime.datetime.now()
    today_str = '%i-%i-%i' % (today.day, today.month, today.year)
    filename = os.path.join(
        model_output_folder, 'final_model_results_%s_%s_%i_runs.csv' %
        (scenario_name, today_str, nscenarios))

    # check if file exists already
    if os.path.exists(filename):
        backup_filename = filename + '_backup'
        os.rename(filename, backup_filename)
        print 'moved previous input & output data to %s' % backup_filename

    print 'saving model run input & output data to %s' % filename
    dfm.to_csv(filename, index_label='model_run')

    return df
Exemple #30
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)
Exemple #31
0
def setup_coastal_mesh(Parameters,
                       mesh_filename,
                       extend_domain=True,
                       max_length=1e5):
    """
    Create a mesh consisting of 3 blocks, with a smaller cell size in the middle block

    The middle block is centered around the fresh-salt water interface, which is calculated using the
    Ghyben-Herzberg equation.
    """

    #if Parameters.topo_gradient == 0:
    #    extent_salt_water = 0
    #else:
    #    extent_salt_water = (Parameters.thickness /
    #                         (41.0 * Parameters.topo_gradient))

    R = Parameters.recharge_flux
    B = Parameters.thickness
    K = Parameters.k * Parameters.rho_f_0 * 9.81 / Parameters.viscosity
    L = Parameters.L

    # calculate hydraulic head using analytical solution for confined aq
    # with uniform recharge + Dupuit assumptions
    xa = np.linspace(0, L, 101)
    h = R / (K * B) * (L * xa - 0.5 * xa**2)

    if h[-1] < (B / 40):
        print 'warning, calculated extent salt water toe exceeds model domain'
        print 'calculated h at model bnd = %0.2f m' % h[-1]
        print 'Ghyben-Herzberg depth of salt water interface = %0.2f m' % (h[-1] * 40)
        print 'thickness = %0.2f m' % Parameters.thickness

        if extend_domain is False:
            extent_salt_water = Parameters.L - Parameters.buffer_distance_land - 1.0
            #print 'choosing maximum possible extent of %0.2f m  for ' \
            #      'designing model grid' % extent_salt_water

            print 'entire top right triangle at landward side of model domain has fine discretization'
            fine_mesh = True
            adjust_length = False
        else:
            adjust_length = True

    else:

        fine_mesh = False

        # salt water toe touches bottom of model domain
        a = 0.5 * R / (K * B)
        b = -(R * L) / (K * B)
        c = B / 40.0

        D = np.sqrt(b**2 - 4 * a * c)
        extent_salt_water = (-b - D) / (2 * a)

        hs1 = R / (K * B) * (L * extent_salt_water -
                             0.5 * extent_salt_water**2)

        print 'calculated extent salt water toe = %0.2f m' % extent_salt_water

        try:
            assert np.abs(hs1 - B / 40.0) < 1e-3
        except AssertionError:
            msg = 'error, something wrong with calculated extent ' \
                  'salt water toe'
            raise ValueError(msg)

    if adjust_length is True:
        L_land  = extent_salt_water + Parameters.buffer_distance_land * 2
        if L_land > max_length:
            L_land = Parameters.L
            fine_mesh = True
        else:
            print 'extending model domain size to %0.3e' % L_land
    else:
        L_land = Parameters.L

    ###############################
    # use gmsh to construct domain
    ##############################
    xs = np.array([-Parameters.L_sea,
                   extent_salt_water - Parameters.buffer_distance_sea,
                   -Parameters.buffer_distance_sea,
                   -Parameters.L_sea,
                   extent_salt_water,
                   0,
                   extent_salt_water + Parameters.buffer_distance_land,
                   Parameters.buffer_distance_land,
                   L_land,
                   L_land,
                   -Parameters.L_sea])

    zs = xs * Parameters.topo_gradient

    zs[0:2] = zs[0:2] - Parameters.thickness
    zs[4] = zs[4] - Parameters.thickness
    zs[6] = zs[6] - Parameters.thickness
    zs[8] = zs[8] - Parameters.thickness
    zs[10] = 0.0

    #points = create_points(xs,zs)
    points = [pc.Point(x, z) for x, z in zip(xs, zs)]

    line1 = pc.Line(points[0], points[1])
    line2 = pc.Line(points[1], points[2])
    line3 = pc.Line(points[2], points[3])
    line4 = pc.Line(points[3], points[0])
    line5 = pc.Line(points[1], points[4])
    line6 = pc.Line(points[4], points[5])
    line7 = pc.Line(points[5], points[2])
    line8 = pc.Line(points[4], points[6])
    line9 = pc.Line(points[6], points[7])
    line10 = pc.Line(points[7], points[5])
    line11 = pc.Line(points[6], points[8])
    line12 = pc.Line(points[8], points[9])
    line13 = pc.Line(points[9], points[7])

    # new lines for sea surface
    # seabottom, x=-buffer to x=0
    # -line3 (pt 3 to 2)
    # - line7 (pt 2 to pt 5)
    # coastline to x=0, z=0
    #line14 = pc.Line(points[5], points[10])
    # x=0, z=0 to x=0, z=sea bottom
    #line15 = pc.Line(points[10], points[3])
    # coastline to x=0, sea bottom

    # finer grid cell size around fresh-salt water interface
    curve_a = pc.CurveLoop(line1, line2, line3, line4)
    curve_b = pc.CurveLoop(line5, line6, line7, -line2)
    curve_c = pc.CurveLoop(line8, line9, line10, -line6)
    curve_d = pc.CurveLoop(line11, line12, line13, -line9)

    #curve_seawater = pc.CurveLoop(-line3, -line7, line14, line15)

    surface_a = pc.PlaneSurface(curve_a)
    surface_b = pc.PlaneSurface(curve_b)
    surface_c = pc.PlaneSurface(curve_c)
    surface_d = pc.PlaneSurface(curve_d)

    #surface_seawater = pc.PlaneSurface(curve_seawater)

    surface_a.setLocalScale(factor=Parameters.grid_refinement_factor_sea)
    surface_b.setLocalScale(factor=Parameters.grid_refinement_factor)
    surface_c.setLocalScale(factor=Parameters.grid_refinement_factor)

    #surface_seawater.setLocalScale(factor=Parameters.grid_refinement_factor_seawater)

    if fine_mesh is True:
        print 'assigning refined grid from landward side of model domain'
        surface_d.setLocalScale(factor=Parameters.grid_refinement_factor)

    d = gmsh.Design(dim=2, element_size=Parameters.cellsize)

    ps1 = pc.PropertySet("sea_surface1", line3)
    ps2 = pc.PropertySet("sea_surface2", line7)
    ps3 = pc.PropertySet("land_surface1", line10)
    ps4 = pc.PropertySet("land_surface2", line13)
    #ps5 = pc.PropertySet("sea_surface", line14)

    #d.addItems(pc.PropertySet('sea', surface_a),
    #           pc.PropertySet('salt_wedge_sea_side', surface_b),
    #           pc.PropertySet('salt_wedge_land_side', surface_c),
    #           pc.PropertySet('land', surface_d),
    #           pc.PropertySet('seawater', surface_seawater),
    #           ps1, ps2, ps3, ps4, ps5)

    d.addItems(pc.PropertySet('sea', surface_a),
               pc.PropertySet('salt_wedge_sea_side', surface_b),
               pc.PropertySet('salt_wedge_land_side', surface_c),
               pc.PropertySet('land', surface_d),
               ps1, ps2, ps3, ps4)

    d.setMeshFileName(mesh_filename)

    mesh = fl.MakeDomain(d, optimizeLabeling=True)

    # calculate surface
    xy = mesh.getX()
    z_surface = xy[0] * Parameters.topo_gradient
    surface = es.whereZero(xy[1] - z_surface)

    # sea surface
    sea_surface = es.whereZero(xy[1]) * es.whereNegative(xy[0])

    seawater = es.whereNegative(xy[0]) * es.whereNegative(z_surface - xy[1])

    print bla

    return mesh, surface, sea_surface, seawater, z_surface
Exemple #32
0
def setup_coastal_mesh_glover1959(Parameters,
                                  mesh_filename):
    """
    Create a mesh consisting of 3 blocks, with a smaller cell size in the middle block

    The middle block is centered around the fresh-salt water interface, which is calculated using an anlytical
    solution by Glover (1959) Journal of Geophys. Res.
    """

    # if Parameters.topo_gradient == 0:
    #    extent_salt_water = 0
    # else:
    #    extent_salt_water = (Parameters.thickness /
    #                         (41.0 * Parameters.topo_gradient))

    if Parameters.ghyben_herzberg is True:

        R = Parameters.recharge_flux
        B = Parameters.thickness
        K = Parameters.k * Parameters.rho_f_0 * 9.81 / Parameters.viscosity
        L = Parameters.L

        # calculate hydraulic head using analytical solution for confined aq
        # with uniform recharge + Dupuit assumptions
        xa = np.linspace(0, L, 1001)
        h = R / (K * B) * (L * xa - 0.5 * xa ** 2)


        # calculate depth salt water interface
        from grompy_lib import depth_sw_interface_Glover1959

        rho_f = Parameters.rho_f_0 * Parameters.freshwater_concentration * Parameters.gamma + Parameters.rho_f_0
        rho_s = Parameters.rho_f_0 * Parameters.seawater_concentration * Parameters.gamma + Parameters.rho_f_0

        Qmax = Parameters.recharge_flux * L

        y_sw, int_sw_top, int_sw_bottom = depth_sw_interface_Glover1959(xa, Parameters.k, Parameters.viscosity,
                                                                        Parameters.topo_gradient, Parameters.thickness,
                                                                        rho_f, rho_s, Parameters.gamma,
                                                                        Qmax=Qmax)

        if Parameters.recharge_flux == 0.0:
            # assume with no rehcarge that the hydraulic head follows the land surface
            h = Parameters.topo_gradient * xa
            #y_sw, int_sw_top, int_sw_bottom

        if int_sw_bottom > L:
            print 'warning, calculated extent salt water toe exceeds model domain'
            print 'calculated toe of fresh_salt water bnd = %0.2f m' % int_sw_bottom
            extent_salt_water = Parameters.L - Parameters.buffer_distance_land - 1.0
            print 'choosing maximum possible extent of %0.2f m  for ' \
                  'designing model grid' % extent_salt_water

            fine_mesh = False

        else:

            fine_mesh = False
            extent_salt_water = int_sw_bottom
            print 'calculated extent salt water toe = %0.2f m' % int_sw_bottom

    else:
        print 'assuming a vertical fresh-salt water interface'
        extent_salt_water = 0.0
        fine_mesh = False

    L_land = Parameters.L

    ###############################
    # use gmsh to construct domain
    ##############################
    xs = np.array([-Parameters.L_sea,
                   extent_salt_water - Parameters.buffer_distance_sea,
                   -Parameters.buffer_distance_sea,
                   -Parameters.L_sea,
                   extent_salt_water,
                   0,
                   extent_salt_water + Parameters.buffer_distance_land,
                   Parameters.buffer_distance_land,
                   L_land,
                   L_land])

    zs = xs * Parameters.topo_gradient

    zs[0:2] = zs[0:2] - Parameters.thickness
    zs[4] = zs[4] - Parameters.thickness
    zs[6] = zs[6] - Parameters.thickness
    zs[8] = zs[8] - Parameters.thickness

    # points = create_points(xs,zs)
    points = [pc.Point(x, z) for x, z in zip(xs, zs)]

    line1 = pc.Line(points[0], points[1])
    line2 = pc.Line(points[1], points[2])
    line3 = pc.Line(points[2], points[3])
    line4 = pc.Line(points[3], points[0])
    line5 = pc.Line(points[1], points[4])
    line6 = pc.Line(points[4], points[5])
    line7 = pc.Line(points[5], points[2])
    line8 = pc.Line(points[4], points[6])
    line9 = pc.Line(points[6], points[7])
    line10 = pc.Line(points[7], points[5])
    line11 = pc.Line(points[6], points[8])
    line12 = pc.Line(points[8], points[9])
    line13 = pc.Line(points[9], points[7])

    # finer grid cell size around fresh-salt water interface
    curve_a = pc.CurveLoop(line1, line2, line3, line4)
    curve_b = pc.CurveLoop(line5, line6, line7, -line2)
    curve_c = pc.CurveLoop(line8, line9, line10, -line6)
    curve_d = pc.CurveLoop(line11, line12, line13, -line9)

    surface_a = pc.PlaneSurface(curve_a)
    surface_b = pc.PlaneSurface(curve_b)
    surface_c = pc.PlaneSurface(curve_c)
    surface_d = pc.PlaneSurface(curve_d)

    surface_a.setLocalScale(factor=Parameters.grid_refinement_factor_sea)
    surface_b.setLocalScale(factor=Parameters.grid_refinement_factor)
    surface_c.setLocalScale(factor=Parameters.grid_refinement_factor)

    if fine_mesh is True:
        print 'assigning refined grid to entire landward side of model domain'
        surface_d.setLocalScale(factor=Parameters.grid_refinement_factor)

    d = gmsh.Design(dim=2, element_size=Parameters.cellsize)

    ps1 = pc.PropertySet("sea_surface1", line3)
    ps2 = pc.PropertySet("sea_surface2", line7)
    ps3 = pc.PropertySet("land_surface1", line10)
    ps4 = pc.PropertySet("land_surface2", line13)

    d.addItems(pc.PropertySet('sea', surface_a),
               pc.PropertySet('salt_wedge_sea_side', surface_b),
               pc.PropertySet('salt_wedge_land_side', surface_c),
               pc.PropertySet('land', surface_d),
               ps1, ps2, ps3, ps4)

    d.setMeshFileName(mesh_filename)

    print '=' * 30

    mesh = fl.MakeDomain(d, optimizeLabeling=True)

    # calculate surface
    xy = mesh.getX()
    z_surface = xy[0] * Parameters.topo_gradient
    surface = es.whereZero(xy[1] - z_surface)

    # sea surface
    # sea_surface = surface * es.whereNegative(xy[0])
    sea_surface = None
    seawater = None

    return mesh, surface, sea_surface, seawater, z_surface
    def getPotential(self):
        """
        returns a list containing 3 lists one for each the primary, secondary
        and total potential.
        """

        primCon=self.primaryConductivity
        coords=self.domain.getX()
        pde=LinearPDE(self.domain, numEquations=1)
        tol=1e-8
        pde.getSolverOptions().setTolerance(tol)
        pde.setSymmetryOn()

        DIM=self.domain.getDim()
        x=self.domain.getX()
        q=whereZero(x[DIM-1]-inf(x[DIM-1]))
        for i in xrange(DIM-1):
            xi=x[i]
            q+=whereZero(xi-inf(xi))+whereZero(xi-sup(xi))
        A = self.secondaryConductivity * kronecker(self.domain)
        pde.setValue(A=A,q=q)

        delPhiSecondary = []
        delPhiPrimary = []
        delPhiTotal = []
        if(len(self.electrodes[0])==3):

            for i in range(self.numElectrodes-3):
                analyticRsOne=Data(0,(3,),ContinuousFunction(self.domain))
                analyticRsOne[0]=(coords[0]-self.electrodes[i][0])
                analyticRsOne[1]=(coords[1]-self.electrodes[i][1])
                analyticRsOne[2]=(coords[2])
                rsMagOne=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**0.5
                analyticRsTwo=Data(0,(3,),ContinuousFunction(self.domain))
                analyticRsTwo[0]=(coords[0]-self.electrodes[i+3][0])
                analyticRsTwo[1]=(coords[1]-self.electrodes[i+3][1])
                analyticRsTwo[2]=(coords[2])
                rsMagTwo=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**0.5
                rsMagOne+=(whereZero(rsMagOne)*0.0000001)
                rsMagTwo+=(whereZero(rsMagTwo)*0.0000001)
                analyticPrimaryPot=(self.current/(2*pi*primCon*rsMagOne))-(self.current/(2*pi*primCon*rsMagTwo))

                analyticRsOnePower=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**1.5
                analyticRsOnePower = analyticRsOnePower+(whereZero(analyticRsOnePower)*0.0001)
                analyticRsTwoPower=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**1.5
                analyticRsTwoPower = analyticRsTwoPower+(whereZero(analyticRsTwoPower)*0.0001)

                gradAnalyticPrimaryPot = Data(0,(3,),ContinuousFunction(self.domain))
                gradAnalyticPrimaryPot[0] =(self.current/(2*pi*primCon)) \
                        * ((-analyticRsOne[0]/analyticRsOnePower) \
                            + (analyticRsTwo[0]/analyticRsTwoPower))
                gradAnalyticPrimaryPot[1] =(self.current/(2*pi*primCon)) \
                        * ((-analyticRsOne[1]/analyticRsOnePower) \
                            + (analyticRsTwo[1]/analyticRsTwoPower))
                gradAnalyticPrimaryPot[2] =(self.current/(2*pi*primCon)) \
                        * ((-analyticRsOne[2]/analyticRsOnePower)
                            + (analyticRsTwo[2]/analyticRsTwoPower))
                X=(primCon-self.secondaryConductivity) * (gradAnalyticPrimaryPot)
                pde.setValue(X=X)
                u=pde.getSolution()
                loc=Locator(self.domain,[self.electrodes[i+1],self.electrodes[i+2]])
                valPrimary=loc.getValue(analyticPrimaryPot)
                valSecondary=loc.getValue(u)
                delPhiPrimary.append(valPrimary[1]-valPrimary[0])
                delPhiSecondary.append(valSecondary[1]-valSecondary[0])
                delPhiTotal.append(delPhiPrimary[i]+delPhiSecondary[i])
        else:
            raise NotImplementedError("2d forward model is not yet implemented")

        self.delPhiSecondary = delPhiSecondary
        self.delPhiPrimary = delPhiPrimary
        self.delPhiTotal=delPhiTotal
        return [delPhiPrimary, delPhiSecondary, delPhiTotal]
    def getPotential(self):
        """
        Returns 3 list each made up of a number of list containing primary, secondary and total
        potentials diferences. Each of the lists contain a list for each value of n.
        """

        primCon=self.primaryConductivity
        coords=self.domain.getX()
        pde=LinearPDE(self.domain, numEquations=1)
        tol=1e-8
        pde.getSolverOptions().setTolerance(tol)
        pde.setSymmetryOn()

        DIM=self.domain.getDim()
        x=self.domain.getX()
        q=es.whereZero(x[DIM-1]-es.inf(x[DIM-1]))
        for i in xrange(DIM-1):
            xi=x[i]
            q+=es.whereZero(xi-es.inf(xi))+es.whereZero(xi-es.sup(xi))
        A = self.secondaryConductivity * es.kronecker(self.domain)
        pde.setValue(A=A,q=q)

        delPhiSecondaryList = []
        delPhiPrimaryList = []
        delPhiTotalList = []
        for i in range(1,self.n+1): # 1 to n
            maxR = self.numElectrodes - 1 - i #max amount of readings that will fit in the survey
            delPhiSecondary = []
            delPhiPrimary = []
            delPhiTotal = []
            for j in range(maxR):
                analyticRs=es.Data(0,(3,),es.ContinuousFunction(self.domain))
                analyticRs[0]=(coords[0]-self.electrodes[j][0])
                analyticRs[1]=(coords[1]-self.electrodes[j][1])
                analyticRs[2]=(coords[2])
                rsMag=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**0.5
                analyticPrimaryPot=(self.current*(1./primCon))/(2*pi*(rsMag+(es.whereZero(rsMag)*0.0000001))) #the magic number 0.0000001 is to avoid devide by 0

                analyticRsPolePower=(analyticRs[0]**2+analyticRs[1]**2+analyticRs[2]**2)**1.5
                analyticRsPolePower = analyticRsPolePower+(es.whereZero(analyticRsPolePower)*0.0000001)
                gradUPrimary = es.Data(0,(3,),es.ContinuousFunction(self.domain))
                gradUPrimary[0] =(self.current/(2*pi*primCon)) * (analyticRs[0]/analyticRsPolePower)
                gradUPrimary[1] =(self.current/(2*pi*primCon)) * (analyticRs[1]/analyticRsPolePower)
                gradUPrimary[2] =(self.current/(2*pi*primCon)) * (analyticRs[2]/analyticRsPolePower)
                gradUPrimary=-gradUPrimary
                X=(primCon-self.secondaryConductivity) * gradUPrimary
                pde.setValue(X=X)
                u=pde.getSolution()
                loc=Locator(self.domain,[self.electrodes[i+j],self.electrodes[i+j+1]])
                valPrimary=loc.getValue(analyticPrimaryPot)
                valSecondary=loc.getValue(u)
                delPhiPrimary.append(valPrimary[1]-valPrimary[0])
                delPhiSecondary.append(valSecondary[1]-valSecondary[0])
                delPhiTotal.append(delPhiPrimary[j]+delPhiSecondary[j])

            delPhiPrimaryList.append(delPhiPrimary)
            delPhiSecondaryList.append(delPhiSecondary)
            delPhiTotalList.append(delPhiTotal)



        self.delPhiPrimaryList=delPhiPrimaryList
        self.delPhiSecondaryList=delPhiSecondaryList
        self.delPhiTotalList = delPhiTotalList

        return [delPhiPrimaryList, delPhiSecondaryList, delPhiTotalList]
    def getPotential(self):
        """
        returns a list containing 3 lists one for each the primary, secondary
        and total potential.
        """

        primCon=self.primaryConductivity
        coords=self.domain.getX()
        pde=LinearPDE(self.domain, numEquations=1)
        tol=1e-8
        pde.getSolverOptions().setTolerance(tol)
        pde.setSymmetryOn()

        DIM=self.domain.getDim()
        x=self.domain.getX()
        q=es.whereZero(x[DIM-1]-es.inf(x[DIM-1]))
        for i in xrange(DIM-1):
            xi=x[i]
            q+=es.whereZero(xi-es.inf(xi))+es.whereZero(xi-es.sup(xi))
        A = self.secondaryConductivity * es.kronecker(self.domain)
        pde.setValue(A=A,q=q)

        delPhiSecondary = []
        delPhiPrimary = []
        delPhiTotal = []
        if(len(self.electrodes[0])==3):

            for i in range(self.numElectrodes-3):
                analyticRsOne=es.Data(0,(3,),es.ContinuousFunction(self.domain))
                analyticRsOne[0]=(coords[0]-self.electrodes[i][0])
                analyticRsOne[1]=(coords[1]-self.electrodes[i][1])
                analyticRsOne[2]=(coords[2])
                rsMagOne=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**0.5
                analyticRsTwo=es.Data(0,(3,),es.ContinuousFunction(self.domain))
                analyticRsTwo[0]=(coords[0]-self.electrodes[i+3][0])
                analyticRsTwo[1]=(coords[1]-self.electrodes[i+3][1])
                analyticRsTwo[2]=(coords[2])
                rsMagTwo=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**0.5
                rsMagOne+=(es.whereZero(rsMagOne)*0.0000001)
                rsMagTwo+=(es.whereZero(rsMagTwo)*0.0000001)
                analyticPrimaryPot=(self.current/(2*pi*primCon*rsMagOne))-(self.current/(2*pi*primCon*rsMagTwo))

                analyticRsOnePower=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**1.5
                analyticRsOnePower = analyticRsOnePower+(es.whereZero(analyticRsOnePower)*0.0001)
                analyticRsTwoPower=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**1.5
                analyticRsTwoPower = analyticRsTwoPower+(es.whereZero(analyticRsTwoPower)*0.0001)

                gradAnalyticPrimaryPot = es.Data(0,(3,),es.ContinuousFunction(self.domain))
                gradAnalyticPrimaryPot[0] =(self.current/(2*pi*primCon)) \
                        * ((-analyticRsOne[0]/analyticRsOnePower) \
                            + (analyticRsTwo[0]/analyticRsTwoPower))
                gradAnalyticPrimaryPot[1] =(self.current/(2*pi*primCon)) \
                        * ((-analyticRsOne[1]/analyticRsOnePower) \
                            + (analyticRsTwo[1]/analyticRsTwoPower))
                gradAnalyticPrimaryPot[2] =(self.current/(2*pi*primCon)) \
                        * ((-analyticRsOne[2]/analyticRsOnePower)
                            + (analyticRsTwo[2]/analyticRsTwoPower))
                X=(primCon-self.secondaryConductivity) * (gradAnalyticPrimaryPot)
                pde.setValue(X=X)
                u=pde.getSolution()
                loc=Locator(self.domain,[self.electrodes[i+1],self.electrodes[i+2]])
                valPrimary=loc.getValue(analyticPrimaryPot)
                valSecondary=loc.getValue(u)
                delPhiPrimary.append(valPrimary[1]-valPrimary[0])
                delPhiSecondary.append(valSecondary[1]-valSecondary[0])
                delPhiTotal.append(delPhiPrimary[i]+delPhiSecondary[i])
        else:
            raise NotImplementedError("2d forward model is not yet implemented")

        self.delPhiSecondary = delPhiSecondary
        self.delPhiPrimary = delPhiPrimary
        self.delPhiTotal=delPhiTotal
        return [delPhiPrimary, delPhiSecondary, delPhiTotal]
    def getPotentialAnalytic(self):
        """
        Returns 3 list each made up of a number of list containing primary, secondary and total
        potentials diferences. Each of the lists contain a list for each value of n.
        """
        coords=self.domain.getX()
        pde=LinearPDE(self.domain, numEquations=1)
        tol=1e-8
        pde.getSolverOptions().setTolerance(tol)
        pde.setSymmetryOn()
        primCon=self.primaryConductivity
        DIM=self.domain.getDim()
        x=self.domain.getX()
        q=es.whereZero(x[DIM-1]-es.inf(x[DIM-1]))
        for i in xrange(DIM-1):
            xi=x[i]
            q+=es.whereZero(xi-es.inf(xi))+es.whereZero(xi-es.sup(xi))
        A = self.secondaryConductivity * es.kronecker(self.domain)
        pde.setValue(A=A,q=q)


        delPhiSecondaryList = []
        delPhiPrimaryList = []
        delPhiTotalList = []
        for i in range(1,self.n+1): # 1 to n
            maxR = self.numElectrodes - 1 - (2*i) #max amount of readings that will fit in the survey
            delPhiSecondary = []
            delPhiPrimary = []
            delPhiTotal = []
            for j in range(maxR):
                analyticRsOne=es.Data(0,(3,),es.ContinuousFunction(self.domain))
                analyticRsOne[0]=(coords[0]-self.electrodes[j][0])
                analyticRsOne[1]=(coords[1]-self.electrodes[j][1])
                analyticRsOne[2]=(coords[2])
                rsMagOne=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**0.5
                analyticRsTwo=es.Data(0,(3,),es.ContinuousFunction(self.domain))
                analyticRsTwo[0]=(coords[0]-self.electrodes[j + ((2*i) + 1)][0])
                analyticRsTwo[1]=(coords[1]-self.electrodes[j + ((2*i) + 1)][1])
                analyticRsTwo[2]=(coords[2])
                rsMagTwo=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**0.5
                self.sources.append([self.electrodeTags[j], self.electrodeTags[j + ((2*i) + 1)]])
                rsMagOne+=(es.whereZero(rsMagOne)*0.0000001)
                rsMagTwo+=(es.whereZero(rsMagTwo)*0.0000001)
                
                analyticPrimaryPot=(self.current/(2*pi*primCon*rsMagOne))-(self.current/(2*pi*primCon*rsMagTwo))
                analyticRsOnePower=(analyticRsOne[0]**2+analyticRsOne[1]**2+analyticRsOne[2]**2)**1.5
                analyticRsOnePower = analyticRsOnePower+(es.whereZero(analyticRsOnePower)*0.0001)
                analyticRsTwoPower=(analyticRsTwo[0]**2+analyticRsTwo[1]**2+analyticRsTwo[2]**2)**1.5
                analyticRsTwoPower = analyticRsTwoPower+(es.whereZero(analyticRsTwoPower)*0.0001)

                gradAnalyticPrimaryPot = es.Data(0,(3,),es.ContinuousFunction(self.domain))
                gradAnalyticPrimaryPot[0] =(self.current/(2*pi*primCon)) * ((-analyticRsOne[0]/analyticRsOnePower) + (analyticRsTwo[0]/analyticRsTwoPower))
                gradAnalyticPrimaryPot[1] =(self.current/(2*pi*primCon)) * ((-analyticRsOne[1]/analyticRsOnePower) + (analyticRsTwo[1]/analyticRsTwoPower))
                gradAnalyticPrimaryPot[2] =(self.current/(2*pi*primCon)) * ((-analyticRsOne[2]/analyticRsOnePower) + (analyticRsTwo[2]/analyticRsTwoPower))
                X=(primCon-self.secondaryConductivity) * (gradAnalyticPrimaryPot)
                pde.setValue(X=X)
                u=pde.getSolution()
                loc=Locator(self.domain,[self.electrodes[j+i],self.electrodes[j+i+1]])
                self.samples.append([self.electrodeTags[j+i],self.electrodeTags[j+i+1]])
                valPrimary=loc.getValue(analyticPrimaryPot)
                valSecondary=loc.getValue(u)
                delPhiPrimary.append(valPrimary[1]-valPrimary[0])
                delPhiSecondary.append(valSecondary[1]-valSecondary[0])
                delPhiTotal.append(delPhiPrimary[j]+delPhiSecondary[j])
            delPhiPrimaryList.append(delPhiPrimary)
            delPhiSecondaryList.append(delPhiSecondary)
            delPhiTotalList.append(delPhiTotal)

        self.delPhiPrimaryList=delPhiPrimaryList
        self.delPhiSecondaryList=delPhiSecondaryList
        self.delPhiTotalList = delPhiTotalList
        return [delPhiPrimaryList, delPhiSecondaryList, delPhiTotalList]
    def __init__(self,
                 domain,
                 omega,
                 x,
                 Z,
                 eta=None,
                 w0=1.,
                 mu=4 * math.pi * 1e-7,
                 sigma0=0.01,
                 airLayerLevel=None,
                 fixAirLayer=False,
                 coordinates=None,
                 tol=1e-8,
                 saveMemory=False,
                 directSolver=True):
        """
        initializes a new forward model.

        :param domain: domain of the model
        :type domain: `Domain`
        :param omega: frequency
        :type omega: positive ``float``
        :param x: coordinates of measurements
        :type x: ``list`` of ``tuple`` with ``float``
        :param Z: measured impedance (possibly scaled)
        :type Z: ``list`` of ``complex``
        :param eta: spatial confidence radius
        :type eta:  positive ``float`` or ``list`` of  positive ``float``
        :param w0: confidence factors for meassurements.
        :type w0: ``None`` or a list of positive ``float``
        :param mu: permeability
        :type mu: ``float``
        :param sigma0: background conductivity
        :type sigma0: ``float``
        :param airLayerLevel: position of the air layer from to bottom of the domain. If
                              not set the air layer starts at the top of the domain
        :type airLayerLevel: ``float`` or ``None``        
        :param fixAirLayer: fix air layer (TM mode)
        :type fixAirLayer: ``bool``
        :param coordinates: defines coordinate system to be used (not supported yet)
        :type coordinates: `ReferenceSystem` or `SpatialCoordinateTransformation`
        :param tol: tolerance of underlying PDE
        :type tol: positive ``float``
        :param saveMemory: if true stiffness matrix is deleted after solution
                           of the PDE to minimize memory use. This will
                           require more compute time as the matrix needs to be
                           reallocated at each iteration.
        :type saveMemory: ``bool``
        :param directSolver: if true a direct solver (rather than an iterative
                             solver) will be used to solve the PDE
        :type directSolver: ``bool``
        """
        super(MT2DBase, self).__init__()
        self.__trafo = coords.makeTransformation(domain, coordinates)
        if not self.getCoordinateTransformation().isCartesian():
            raise ValueError(
                "Non-Cartesian coordinates are not supported yet.")
        if len(x) != len(Z):
            raise ValueError(
                "Number of data points and number of impedance values don't match."
            )

        if eta is None:
            eta = escript.sup(domain.getSize()) * 0.45

        if isinstance(eta, float) or isinstance(eta, int):
            eta = [float(eta)] * len(Z)
        elif not len(eta) == len(Z):
            raise ValueError(
                "Number of confidence radii and number of impedance values don't match."
            )

        if isinstance(w0, float) or isinstance(w0, int):
            w0 = [float(w0)] * len(Z)
        elif not len(w0) == len(Z):
            raise ValueError(
                "Number of confidence factors and number of impedance values don't match."
            )

        self.__domain = domain
        self._omega_mu = omega * mu
        self._ks = escript.sqrt(self._omega_mu * sigma0 / 2.)

        xx = escript.Function(domain).getX()
        totalS = 0
        self._Z = [
            escript.Scalar(0., escript.Function(domain)),
            escript.Scalar(0., escript.Function(domain))
        ]
        self._weight = escript.Scalar(0., escript.Function(domain))

        for s in range(len(Z)):
            chi = self.getWeightingFactor(xx, 1., x[s], eta[s])
            f = escript.integrate(chi)
            if f < eta[s]**2 * 0.01:
                raise ValueError(
                    "Zero weight (almost) for data point %s. Change eta or refine mesh."
                    % (s, ))
            w02 = w0[s] / f
            totalS += w02
            self._Z[0] += chi * Z[s].real
            self._Z[1] += chi * Z[s].imag
            self._weight += chi * w02 / (abs(Z[s])**2)

        if not totalS > 0:
            raise ValueError(
                "Scaling of weight factors failed as sum is zero.")

        DIM = domain.getDim()
        z = domain.getX()[DIM - 1]
        self._ztop = escript.sup(z)
        self._zbottom = escript.inf(z)
        if airLayerLevel is None:
            airLayerLevel = self._ztop
        self._airLayerLevel = airLayerLevel

        # botton:
        mask0 = escript.whereZero(z - self._zbottom)
        r = mask0 * [
            escript.exp(self._ks * (self._zbottom - airLayerLevel)) *
            escript.cos(self._ks * (self._zbottom - airLayerLevel)),
            escript.exp(self._ks * (self._zbottom - airLayerLevel)) *
            escript.sin(self._ks * (self._zbottom - airLayerLevel))
        ]

        #top:
        if fixAirLayer:
            mask1 = escript.whereNonNegative(z - airLayerLevel)
            r += mask1 * [1, 0]
        else:
            mask1 = escript.whereZero(z - self._ztop)
            r += mask1 * [
                self._ks * (self._ztop - airLayerLevel) + 1, self._ks *
                (self._ztop - airLayerLevel)
            ]

        self._q = (mask0 + mask1) * [1, 1]
        self._r = r
        #====================================
        self.__tol = tol
        self._directSolver = directSolver
        self._saveMemory = saveMemory
        self.__pde = None
        if not saveMemory:
            self.__pde = self.setUpPDE()