Example #1
0
def getR2(y, y_fitted, chi=None):
    """
        calculates the coefficient of determination R^2 for  `y_fitted` as prediction for `y` over a region marked by chi>0 defined by
        
        R^2=1 - S_res/S_tot
        
        with S_res=int(chi*(y-y_fitted*1)**2,  S_tot=int(chi*(y-m(y)*1)**2), m(y)=int(chi*y)/int(chi)
        
        If R^2=1 then `y_fitted` is predicts `y` exactly. If R^2 then `y_fitted` does not make a better prediction than the mean. 
        
        :param y: target distribution
        :type y: `esys.escript.Scalar`
        :param y_fitted: fitted distribution
        :type y_fitted: `esys.escript.Scalar`
        :param chi: marker/weighting for region of interest
        :type chi: `esys.escript.Scalar` or None
        :rtype: `float`
        """
    if chi is None:
        chi = Scalar(1., Function(y_fitted.getFunctionSpace().getDomain()))

    ybar = integrate(chi * y) / integrate(chi)
    S_res = integrate(chi * (y - y_fitted)**2)
    S_tot = integrate(chi * (y - ybar)**2)
    if S_tot > 0:
        R2 = 1 - S_res / S_tot
    else:
        if S_res > 0:
            R2 = 0.
        else:
            R2 = 1.

    return R2
    def getDualProduct(self, m, r):
        """
        returns the dual product of a gradient represented by X=r[1] and Y=r[0]
        with a level set function m:

             *Y_i*m_i + X_ij*m_{i,j}*

        :type m: `Data`
        :type r: `ArithmeticTuple`
        :rtype: ``float``
        """
        A = 0
        if not r[0].isEmpty(): A += integrate(inner(r[0], m))
        if not r[1].isEmpty(): A += integrate(inner(r[1], grad(m)))
        return A
Example #3
0
    def getSourceScaling(self, u):
        """
        returns the scaling factor s required to rescale source F to minimize defect ``|s * u- data|^2``

        :param u: value of pressure solution (real and imaginary part)
        :type u: ``escript.Data`` of shape (2,)
        :rtype: `complex`
        """
        uTu = escript.integrate(self.__weight * escript.length(u)**2)
        uTar = escript.integrate(self.__weight * ( u[0]*self.__data[0]+u[1]*self.__data[1]) )
        uTai = escript.integrate(self.__weight * ( u[0]*self.__data[1]-u[1]*self.__data[0]) )
        if uTu > 0:
            return complex(uTar/uTu, uTai/uTu)
        else:
            return complex(1.,0)
Example #4
0
    def getDualProduct(self, m, r):
        """
        returns the dual product of a gradient represented by X=r[1] and Y=r[0]
        with a level set function m:

             *Y_i*m_i + X_ij*m_{i,j}*

        :type m: `Data`
        :type r: `ArithmeticTuple`
        :rtype: ``float``
        """
        A=0
        if not r[0].isEmpty(): A+=integrate(inner(r[0], m))
        if not r[1].isEmpty(): A+=integrate(inner(r[1], grad(m)))
        return A
Example #5
0
    def getValue(self, m, grad_m):
        """
        returns the value of the cost function J with respect to m.
        This equation is specified in the inversion cookbook.

        :rtype: ``float``
        """

        if m != self.__pre_input:
            raise RuntimeError("Attempt to change point using getValue")
        # substituting cached values
        m = self.__pre_input
        grad_m = self.__pre_args

        mu = self.__mu
        mu_c = self.__mu_c
        DIM = self.getDomain().getDim()
        numLS = self.getNumLevelSets()

        A = 0
        if self.__w0 is not None:
            r = inner(escript.integrate(m**2 * self.__w0), mu)
            self.logger.debug("J_R[m^2] = %e" % r)
            A += r

        if self.__w1 is not None:
            if numLS == 1:
                r = escript.integrate(inner(grad_m**2, self.__w1)) * mu
                self.logger.debug("J_R[grad(m)] = %e" % r)
                A += r
            else:
                for k in range(numLS):
                    r = mu[k] * escript.integrate(
                        inner(grad_m[k, :]**2, self.__w1[k, :]))
                    self.logger.debug("J_R[grad(m)][%d] = %e" % (k, r))
                    A += r

        if numLS > 1:
            for k in range(numLS):
                gk = grad_m[k, :]
                len_gk = escript.length(gk)
                for l in range(k):
                    gl = grad_m[l, :]
                    r = mu_c[l, k] * escript.integrate(self.__wc[l, k] * (
                        (len_gk * escript.length(gl))**2 - inner(gk, gl)**2))
                    self.logger.debug("J_R[cross][%d,%d] = %e" % (l, k, r))
                    A += r
        return A / 2
    def getDefect(self, rho, Hx, g_Hx):
        """
        Returns the defect value.

        :param rho: a suggestion for resistivity
        :type rho: ``Data`` of shape ()
        :param Hx: magnetic field
        :type Hx: ``Data`` of shape (2,)
        :param g_Hx: gradient of magnetic field
        :type g_Hx: ``Data`` of shape (2,2)

        :rtype: ``float``
        """
        x = g_Hx.getFunctionSpace().getX()
        Hx = escript.interpolate(Hx, x.getFunctionSpace())
        u0 = Hx[0]
        u1 = Hx[1]
        u01 = g_Hx[0, 1]
        u11 = g_Hx[1, 1]
        scale = rho / (u0**2 + u1**2)

        Z = self._Z
        A = escript.integrate(
            self._weight * (Z[0]**2 + Z[1]**2 + scale *
                            (-2 * Z[0] * (u0 * u01 + u1 * u11) + 2 * Z[1] *
                             (u1 * u01 - u0 * u11) + rho * (u01**2 + u11**2))))
        return A / 2
    def getDefect(self, sigma, Ex, dExdz):
        """
        Returns the defect value.

        :param sigma: a suggestion for conductivity
        :type sigma: ``Data`` of shape ()
        :param Ex: electric field
        :type Ex: ``Data`` of shape (2,)
        :param dExdz: vertical derivative of electric field
        :type dExdz: ``Data`` of shape (2,)

        :rtype: ``float``
        """
        x = dExdz.getFunctionSpace().getX()
        Ex = escript.interpolate(Ex, x.getFunctionSpace())
        u0 = Ex[0]
        u1 = Ex[1]
        u01 = dExdz[0]
        u11 = dExdz[1]
        scale = self._weight / (u01**2 + u11**2)

        Z = self._Z
        A = escript.integrate(
            scale * ((Z[0]**2 + Z[1]**2) * (u01**2 + u11**2) + 2 * Z[1] *
                     (u0 * u11 - u01 * u1) - 2 * Z[0] *
                     (u0 * u01 + u11 * u1) + u0**2 + u1**2))

        return A / 2
Example #8
0
    def rescaleWeights(self, scale=1., sigma_scale=1.):
        """
        rescales the weights such that

        :math: integrate( ( w omega**2 * sigma_scale * data * ((1/L_j)**2)**-1) +1 )/(data*omega**2 * ((1/L_j)**2)**-1) * sigma_scale )=scale

        :param scale: scale of data weighting factors
        :type scale: positive ``float``
        :param sigma_scale: scale of 1/vp**2 velocity.
        :type sigma_scale: ``Scalar``
        """
        raise Warning("rescaleWeights is not tested yet.")
        if not scale > 0:
            raise ValueError("Value for scale must be positive.")
        if not sigma_scale * omega**2 * d > 0:
            raise ValueError(
                "Rescaling of weights failed due to zero denominator.")
        # copy back original weights before rescaling
        #self.__weight=[1.*ow for ow in self.__origweight]
        L2 = 1 / escript.length(1 / self.edge_length)**2
        d = Lsup(escript.length(data))
        A = escript.integrate(self.__weight *
                              (sigma_scale * omega**2 * d + 1) /
                              (sigma_scale * omega**2 * d))
        if A > 0:
            self.__weight *= 1. / A
            if self.scaleF:
                self.__data *= escript.sqrt(A)
        else:
            raise ValueError("Rescaling of weights failed.")
    def getValue(self, m, grad_m):
        """
        returns the value of the cost function J with respect to m.
        This equation is specified in the inversion cookbook.

        :rtype: ``float``
        """

        if m != self.__pre_input:
            raise RuntimeError("Attempt to change point using getValue")
        # substituting cached values
        m = self.__pre_input
        grad_m = self.__pre_args

        mu = self.__mu
        mu_c = self.__mu_c
        DIM = self.getDomain().getDim()
        numLS = self.getNumLevelSets()

        A = 0
        if self.__w0 is not None:
            r = inner(integrate(m ** 2 * self.__w0), mu)
            self.logger.debug("J_R[m^2] = %e" % r)
            A += r

        if self.__w1 is not None:
            if numLS == 1:
                r = integrate(inner(grad_m ** 2, self.__w1)) * mu
                self.logger.debug("J_R[grad(m)] = %e" % r)
                A += r
            else:
                for k in range(numLS):
                    r = mu[k] * integrate(inner(grad_m[k, :] ** 2, self.__w1[k, :]))
                    self.logger.debug("J_R[grad(m)][%d] = %e" % (k, r))
                    A += r

        if numLS > 1:
            for k in range(numLS):
                gk = grad_m[k, :]
                len_gk = length(gk)
                for l in range(k):
                    gl = grad_m[l, :]
                    r = mu_c[l, k] * integrate(self.__wc[l, k] * ((len_gk * length(gl)) ** 2 - inner(gk, gl) ** 2))
                    self.logger.debug("J_R[cross][%d,%d] = %e" % (l, k, r))
                    A += r
        return A / 2
    def getNorm(self, m):
        """
        returns the norm of ``m``.

        :param m: level set function
        :type m: `Data`
        :rtype: ``float``
        """
        return sqrt(integrate(length(m)**2) / self.__vol_d)
    def getNorm(self, m):
        """
        returns the norm of ``m``.

        :param m: level set function
        :type m: `Data`
        :rtype: ``float``
        """
        return sqrt(integrate(length(m) ** 2) / self.__vol_d)
    def getValue(self, m, grad_m):
        """
        returns the value of the cost function J with respect to m.
        This equation is specified in the inversion cookbook.

        :rtype: ``float``
        """
        mu = self.__mu
        mu_c = self.__mu_c
        DIM = self.getDomain().getDim()
        numLS = self.getNumLevelSets()

        A = 0
        if self.__w0 is not None:
            r = inner(integrate(m**2 * self.__w0), mu)
            self.logger.debug("J_R[m^2] = %e" % r)
            A += r

        if self.__w1 is not None:
            if numLS == 1:
                r = integrate(inner(grad_m**2, self.__w1)) * mu
                self.logger.debug("J_R[grad(m)] = %e" % r)
                A += r
            else:
                for k in range(numLS):
                    r = mu[k] * integrate(
                        inner(grad_m[k, :]**2, self.__w1[k, :]))
                    self.logger.debug("J_R[grad(m)][%d] = %e" % (k, r))
                    A += r

        if numLS > 1:
            for k in range(numLS):
                gk = grad_m[k, :]
                len_gk = length(gk)
                for l in range(k):
                    gl = grad_m[l, :]
                    r = mu_c[l, k] * integrate(self.__wc[l, k] * (
                        (len_gk * length(gl))**2 - inner(gk, gl)**2))
                    self.logger.debug("J_R[cross][%d,%d] = %e" % (l, k, r))
                    A += r
        return A / 2
Example #13
0
    def getArguments(self, sigma):
        """
        Returns precomputed values shared by `getDefect()` and `getGradient()`.

        :param sigma: a suggestion for complex 1/V**2
        :type sigma: ``escript.Data`` of shape (2,)
        :return: solution,  uTar, uTai, uTu
        :rtype: ``escript.Data`` of shape (2,), 3 x `float`
        """
        pde=self.setUpPDE()
        D=pde.getCoefficient('D')
        D[0,0]=-self.__omega**2 * sigma[0]
        D[0,1]= self.__omega**2 * sigma[1]
        D[1,0]=-self.__omega**2 * sigma[1]
        D[1,1]=-self.__omega**2 * sigma[0]
        pde.setValue(D=D, Y=self.__F, y=self.__f, y_dirac=self.__f_dirac)
        u=pde.getSolution()

        uTar=escript.integrate(self.__weight * ( u[0]*self.__data[0]+u[1]*self.__data[1]) )
        uTai=escript.integrate(self.__weight * ( u[0]*self.__data[1]-u[1]*self.__data[0]) )
        uTu = escript.integrate( self.__weight * escript.length(u)**2 )
        return u, uTar, uTai, uTu
Example #14
0
    def getDefect(self, sigma, u, uTar, uTai, uTu):
        """
        Returns the defect value.

        :param sigma: a suggestion for complex 1/V**2
        :type sigma: ``escript.Data`` of shape (2,)
        :param u: a u vector
        :type u: ``escript.Data`` of shape (2,)
        :param uTar: equals `integrate( w  * (data[0]*u[0]+data[1]*u[1]))`
        :type uTar: `float`
        :param uTai: equals `integrate( w  * (data[1]*u[0]-data[0]*u[1]))`
        :type uTa: `float`
        :param uTu: equals `integrate( w  * (u,u))`
        :type uTu: `float`

        :rtype: ``float``
        """
        # assuming integrate(w * length(data)**2) =1
        if self.scaleF and abs(uTu) >0:
           A = 1.-(uTar**2 + uTai**2)/uTu
        else:
           A = escript.integrate(self.__weight*escript.length(self.__data)**2)- 2 * uTar + uTu
        return  A/2
Example #15
0
def RegionalCalculation(reg_mask):
    """
    Calculates the "regional" from the entire FEILDS model excluding the
    selected region and outputs gravity at the specified altitude...
    see above for the "residual"
    """

    # read in a gravity data grid to define data computation space
    G_DATA = os.path.join(DATADIR,'Final_BouguerTC_UC15K_qrtdeg.nc')
    FS=ReducedFunction(dom)
    nValues=[NX, NY, 1]
    first = [0, 0, cell_at_altitude]
    multiplier = [1, 1, 1]
    reverse = [0, 0, 0]
    byteorder = BYTEORDER_NATIVE
    gdata = readBinaryGrid(G_DATA, FS, shape=(),
                fill=-999999, byteOrder=byteorder,
                dataType=DATATYPE_FLOAT32, first=first, numValues=nValues,
                multiplier=multiplier, reverse=reverse)
    print("Grid successfully read")

    # get the masking and units sorted out for the data-space
    g_mask = whereNonZero(gdata+999999)

    gdata=gdata*g_mask * GRAV_UNITS

    # if people choose to have air in their region we exclude it from the
    # specified gravity calculation region
    if h_top < 0.:
        reg_mask = reg_mask+mask_air

    live_model = initial_model* whereNonPositive(reg_mask)
    dead_model = initial_model* wherePositive(reg_mask)

    if UseMean is True:
        # calculate the mean density within the selected region
        BackgroundDensity = integrate(dead_model)/integrate(wherePositive(reg_mask))
        print("Density mean for selected region equals = %s"%BackgroundDensity)

        live_model = live_model + BackgroundDensity * wherePositive(reg_mask)

    # create mapping
    rho_mapping = DensityMapping(dom, rho0=live_model)

    # invert sign of gravity field to account for escript's coordinate system
    gdata = -GRAV_UNITS * gdata

    # turn the scalars into vectors (vertical direction)
    d=kronecker(DIM)[DIM-1]
    w=safeDiv(1., g_mask)
    gravity_model=GravityModel(dom, w*d, gdata*d, fixPotentialAtBottom=False, coordinates=COORDINATES)
    gravity_model.rescaleWeights(rho_scale=rho_mapping.getTypicalDerivative())
    phi,_ = gravity_model.getArguments(live_model)
    g_init = -gravity_model.getCoordinateTransformation().getGradient(phi)
    g_init = interpolate(g_init, gdata.getFunctionSpace())
    print("Computed gravity: %s"%(g_init[2]))

    fn=os.path.join(OUTPUTDIR,'regional-gravity')
    if SiloOutput is True:
        saveSilo(fn, density=live_model, gravity_init=g_init, g_initz=-g_init[2], gravitymask=g_mask, modelmask=reg_mask)
        print('SILO file written with the following fields: density (kg/m^3), gravity vector (m/s^2), gz (m/s^2), gravitymask, modelmask')

    # to compare calculated data against input dataset.
    # Not used by default but should work if the input dataset is correct
    #gslice = g_init[2]*wherePositive(g_mask)
    #g_dash = integrate(gslice)/integrate(wherePositive(g_mask))
    #gdataslice = gdata*wherePositive(g_mask)
    #gdata_dash = integrate(gdataslice)/integrate(wherePositive(g_mask))
    #misfit=(gdataslice-gdata_dash)-(gslice-g_dash)
    saveDataCSV(fn+".csv", mask=g_mask, gz=-g_init[2], Long=datacoords[0], Lat=datacoords[1], h=datacoords[2])
    print('CSV file written with the following fields: Longitude (degrees) Latitude (degrees), h (100km), gz (m/s^2)')
Example #16
0
    def __init__(self,
                 domain,
                 numLevelSets=1,
                 w0=None,
                 w1=None,
                 wc=None,
                 location_of_set_m=escript.Data(),
                 useDiagonalHessianApproximation=False,
                 tol=1e-8,
                 coordinates=None,
                 scale=None,
                 scale_c=None):
        """
        initialization.

        :param domain: domain
        :type domain: `Domain`
        :param numLevelSets: number of level sets
        :type numLevelSets: ``int``
        :param w0: weighting factor for the m**2 term. If not set zero is assumed.
        :type w0: ``Scalar`` if ``numLevelSets`` == 1 or `Data` object of shape
                  (``numLevelSets`` ,) if ``numLevelSets`` > 1
        :param w1: weighting factor for the grad(m_i) terms. If not set zero is assumed
        :type w1: ``Vector`` if ``numLevelSets`` == 1 or `Data` object of shape
                  (``numLevelSets`` , DIM) if ``numLevelSets`` > 1
        :param wc: weighting factor for the cross gradient terms. If not set
                   zero is assumed. Used for the case if ``numLevelSets`` > 1
                   only. Only values ``wc[l,k]`` in the lower triangle (l<k)
                   are used.
        :type wc: `Data` object of shape (``numLevelSets`` , ``numLevelSets``)
        :param location_of_set_m: marks location of zero values of the level
                                  set function ``m`` by a positive entry.
        :type location_of_set_m: ``Scalar`` if ``numLevelSets`` == 1 or `Data`
                object of shape (``numLevelSets`` ,) if ``numLevelSets`` > 1
        :param useDiagonalHessianApproximation: if True cross gradient terms
                    between level set components are ignored when calculating
                    approximations of the inverse of the Hessian Operator.
                    This can speed-up the calculation of the inverse but may
                    lead to an increase of the number of iteration steps in the
                    inversion.
        :type useDiagonalHessianApproximation: ``bool``
        :param tol: tolerance when solving the PDE for the inverse of the
                    Hessian Operator
        :type tol: positive ``float``

        :param coordinates: defines coordinate system to be used
        :type coordinates: ReferenceSystem` or `SpatialCoordinateTransformation`
        :param scale: weighting factor for level set function variation terms.
                      If not set one is used.
        :type scale: ``Scalar`` if ``numLevelSets`` == 1 or `Data` object of
                     shape (``numLevelSets`` ,) if ``numLevelSets`` > 1
        :param scale_c: scale for the cross gradient terms. If not set
                   one is assumed. Used for the case if ``numLevelSets`` > 1
                   only. Only values ``scale_c[l,k]`` in the lower triangle
                   (l<k) are used.
        :type scale_c: `Data` object of shape (``numLevelSets``,``numLevelSets``)

        """
        if w0 is None and w1 is None:
            raise ValueError("Values for w0 or for w1 must be given.")
        if wc is None and numLevelSets > 1:
            raise ValueError("Values for wc must be given.")

        self.__pre_input = None
        self.__pre_args = None
        self.logger = logging.getLogger('inv.%s' % self.__class__.__name__)
        self.__domain = domain
        DIM = self.__domain.getDim()
        self.__numLevelSets = numLevelSets
        self.__trafo = makeTransformation(domain, coordinates)
        self.__pde = linearPDEs.LinearPDE(self.__domain,
                                          numEquations=self.__numLevelSets,
                                          numSolutions=self.__numLevelSets)
        self.__pde.getSolverOptions().setTolerance(tol)
        self.__pde.setSymmetryOn()
        self.__pde.setValue(
            A=self.__pde.createCoefficient('A'),
            D=self.__pde.createCoefficient('D'),
        )
        try:
            self.__pde.setValue(q=location_of_set_m)
        except linearPDEs.IllegalCoefficientValue:
            raise ValueError(
                "Unable to set location of fixed level set function.")

        # =========== check the shape of the scales: ========================
        if scale is None:
            if numLevelSets == 1:
                scale = 1.
            else:
                scale = np.ones((numLevelSets, ))
        else:
            scale = np.asarray(scale)
            if numLevelSets == 1:
                if scale.shape == ():
                    if not scale > 0:
                        raise ValueError("Value for scale must be positive.")
                else:
                    raise ValueError("Unexpected shape %s for scale." %
                                     scale.shape)
            else:
                if scale.shape is (numLevelSets, ):
                    if not min(scale) > 0:
                        raise ValueError(
                            "All values for scale must be positive.")
                else:
                    raise ValueError("Unexpected shape %s for scale." %
                                     scale.shape)

        if scale_c is None or numLevelSets < 2:
            scale_c = np.ones((numLevelSets, numLevelSets))
        else:
            scale_c = np.asarray(scale_c)
            if scale_c.shape == (numLevelSets, numLevelSets):
                if not all([[scale_c[l, k] > 0. for l in range(k)]
                            for k in range(1, numLevelSets)]):
                    raise ValueError(
                        "All values in the lower triangle of scale_c must be positive."
                    )
            else:
                raise ValueError("Unexpected shape %s for scale." %
                                 scale_c.shape)
        # ===== check the shape of the weights: =============================
        if w0 is not None:
            w0 = escript.interpolate(
                w0, self.__pde.getFunctionSpaceForCoefficient('D'))
            s0 = w0.getShape()
            if numLevelSets == 1:
                if not s0 == ():
                    raise ValueError("Unexpected shape %s for weight w0." %
                                     (s0, ))
            else:
                if not s0 == (numLevelSets, ):
                    raise ValueError("Unexpected shape %s for weight w0." %
                                     (s0, ))
            if not self.__trafo.isCartesian():
                w0 *= self.__trafo.getVolumeFactor()
        if not w1 is None:
            w1 = escript.interpolate(
                w1, self.__pde.getFunctionSpaceForCoefficient('A'))
            s1 = w1.getShape()
            if numLevelSets == 1:
                if not s1 == (DIM, ):
                    raise ValueError("Unexpected shape %s for weight w1." %
                                     (s1, ))
            else:
                if not s1 == (numLevelSets, DIM):
                    raise ValueError("Unexpected shape %s for weight w1." %
                                     (s1, ))
            if not self.__trafo.isCartesian():
                f = self.__trafo.getScalingFactors(
                )**2 * self.__trafo.getVolumeFactor()
                if numLevelSets == 1:
                    w1 *= f
                else:
                    for i in range(numLevelSets):
                        w1[i, :] *= f

        if numLevelSets == 1:
            wc = None
        else:
            wc = escript.interpolate(
                wc, self.__pde.getFunctionSpaceForCoefficient('A'))
            sc = wc.getShape()
            if not sc == (numLevelSets, numLevelSets):
                raise ValueError("Unexpected shape %s for weight wc." % (sc, ))
            if not self.__trafo.isCartesian():
                raise ValueError(
                    "Non-cartesian coordinates for cross-gradient term is not supported yet."
                )
        # ============= now we rescale weights: =============================
        L2s = np.asarray(escript.boundingBoxEdgeLengths(domain))**2
        L4 = 1 / np.sum(1 / L2s)**2
        if numLevelSets == 1:
            A = 0
            if w0 is not None:
                A = escript.integrate(w0)
            if w1 is not None:
                A += escript.integrate(inner(w1, 1 / L2s))
            if A > 0:
                f = scale / A
                if w0 is not None:
                    w0 *= f
                if w1 is not None:
                    w1 *= f
            else:
                raise ValueError("Non-positive weighting factor detected.")
        else:  # numLevelSets > 1
            for k in range(numLevelSets):
                A = 0
                if w0 is not None:
                    A = escript.integrate(w0[k])
                if w1 is not None:
                    A += escript.integrate(inner(w1[k, :], 1 / L2s))
                if A > 0:
                    f = scale[k] / A
                    if w0 is not None:
                        w0[k] *= f
                    if w1 is not None:
                        w1[k, :] *= f
                else:
                    raise ValueError(
                        "Non-positive weighting factor for level set component %d detected."
                        % k)

                # and now the cross-gradient:
                if wc is not None:
                    for l in range(k):
                        A = escript.integrate(wc[l, k]) / L4
                        if A > 0:
                            f = scale_c[l, k] / A
                            wc[l, k] *= f
#                       else:
#                           raise ValueError("Non-positive weighting factor for cross-gradient level set components %d and %d detected."%(l,k))

        self.__w0 = w0
        self.__w1 = w1
        self.__wc = wc

        self.__pde_is_set = False
        if self.__numLevelSets > 1:
            self.__useDiagonalHessianApproximation = useDiagonalHessianApproximation
        else:
            self.__useDiagonalHessianApproximation = True
        self._update_Hessian = True

        self.__num_tradeoff_factors = numLevelSets + (
            (numLevelSets - 1) * numLevelSets) // 2
        self.setTradeOffFactors()
        self.__vol_d = escript.vol(self.__domain)
    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()
    def __init__(
        self,
        domain,
        numLevelSets=1,
        w0=None,
        w1=None,
        wc=None,
        location_of_set_m=Data(),
        useDiagonalHessianApproximation=False,
        tol=1e-8,
        coordinates=None,
        scale=None,
        scale_c=None,
    ):
        """
        initialization.

        :param domain: domain
        :type domain: `Domain`
        :param numLevelSets: number of level sets
        :type numLevelSets: ``int``
        :param w0: weighting factor for the m**2 term. If not set zero is assumed.
        :type w0: ``Scalar`` if ``numLevelSets`` == 1 or `Data` object of shape
                  (``numLevelSets`` ,) if ``numLevelSets`` > 1
        :param w1: weighting factor for the grad(m_i) terms. If not set zero is assumed
        :type w1: ``Vector`` if ``numLevelSets`` == 1 or `Data` object of shape
                  (``numLevelSets`` , DIM) if ``numLevelSets`` > 1
        :param wc: weighting factor for the cross gradient terms. If not set
                   zero is assumed. Used for the case if ``numLevelSets`` > 1
                   only. Only values ``wc[l,k]`` in the lower triangle (l<k)
                   are used.
        :type wc: `Data` object of shape (``numLevelSets`` , ``numLevelSets``)
        :param location_of_set_m: marks location of zero values of the level
                                  set function ``m`` by a positive entry.
        :type location_of_set_m: ``Scalar`` if ``numLevelSets`` == 1 or `Data`
                object of shape (``numLevelSets`` ,) if ``numLevelSets`` > 1
        :param useDiagonalHessianApproximation: if True cross gradient terms
                    between level set components are ignored when calculating
                    approximations of the inverse of the Hessian Operator.
                    This can speed-up the calculation of the inverse but may
                    lead to an increase of the number of iteration steps in the
                    inversion.
        :type useDiagonalHessianApproximation: ``bool``
        :param tol: tolerance when solving the PDE for the inverse of the
                    Hessian Operator
        :type tol: positive ``float``

        :param coordinates: defines coordinate system to be used
        :type coordinates: ReferenceSystem` or `SpatialCoordinateTransformation`
        :param scale: weighting factor for level set function variation terms.
                      If not set one is used.
        :type scale: ``Scalar`` if ``numLevelSets`` == 1 or `Data` object of
                     shape (``numLevelSets`` ,) if ``numLevelSets`` > 1
        :param scale_c: scale for the cross gradient terms. If not set
                   one is assumed. Used for the case if ``numLevelSets`` > 1
                   only. Only values ``scale_c[l,k]`` in the lower triangle
                   (l<k) are used.
        :type scale_c: `Data` object of shape (``numLevelSets``,``numLevelSets``)

        """
        if w0 is None and w1 is None:
            raise ValueError("Values for w0 or for w1 must be given.")
        if wc is None and numLevelSets > 1:
            raise ValueError("Values for wc must be given.")

        self.__pre_input = None
        self.__pre_args = None
        self.logger = logging.getLogger("inv.%s" % self.__class__.__name__)
        self.__domain = domain
        DIM = self.__domain.getDim()
        self.__numLevelSets = numLevelSets
        self.__trafo = makeTransformation(domain, coordinates)
        self.__pde = LinearPDE(self.__domain, numEquations=self.__numLevelSets, numSolutions=self.__numLevelSets)
        self.__pde.getSolverOptions().setTolerance(tol)
        self.__pde.setSymmetryOn()
        self.__pde.setValue(A=self.__pde.createCoefficient("A"), D=self.__pde.createCoefficient("D"))
        try:
            self.__pde.setValue(q=location_of_set_m)
        except IllegalCoefficientValue:
            raise ValueError("Unable to set location of fixed level set function.")

        # =========== check the shape of the scales: ========================
        if scale is None:
            if numLevelSets == 1:
                scale = 1.0
            else:
                scale = np.ones((numLevelSets,))
        else:
            scale = np.asarray(scale)
            if numLevelSets == 1:
                if scale.shape == ():
                    if not scale > 0:
                        raise ValueError("Value for scale must be positive.")
                else:
                    raise ValueError("Unexpected shape %s for scale." % scale.shape)
            else:
                if scale.shape is (numLevelSets,):
                    if not min(scale) > 0:
                        raise ValueError("All values for scale must be positive.")
                else:
                    raise ValueError("Unexpected shape %s for scale." % scale.shape)

        if scale_c is None or numLevelSets < 2:
            scale_c = np.ones((numLevelSets, numLevelSets))
        else:
            scale_c = np.asarray(scale_c)
            if scale_c.shape == (numLevelSets, numLevelSets):
                if not all([[scale_c[l, k] > 0.0 for l in range(k)] for k in range(1, numLevelSets)]):
                    raise ValueError("All values in the lower triangle of scale_c must be positive.")
            else:
                raise ValueError("Unexpected shape %s for scale." % scale_c.shape)
        # ===== check the shape of the weights: =============================
        if w0 is not None:
            w0 = interpolate(w0, self.__pde.getFunctionSpaceForCoefficient("D"))
            s0 = w0.getShape()
            if numLevelSets == 1:
                if not s0 == ():
                    raise ValueError("Unexpected shape %s for weight w0." % (s0,))
            else:
                if not s0 == (numLevelSets,):
                    raise ValueError("Unexpected shape %s for weight w0." % (s0,))
            if not self.__trafo.isCartesian():
                w0 *= self.__trafo.getVolumeFactor()
        if not w1 is None:
            w1 = interpolate(w1, self.__pde.getFunctionSpaceForCoefficient("A"))
            s1 = w1.getShape()
            if numLevelSets == 1:
                if not s1 == (DIM,):
                    raise ValueError("Unexpected shape %s for weight w1." % (s1,))
            else:
                if not s1 == (numLevelSets, DIM):
                    raise ValueError("Unexpected shape %s for weight w1." % (s1,))
            if not self.__trafo.isCartesian():
                f = self.__trafo.getScalingFactors() ** 2 * self.__trafo.getVolumeFactor()
                if numLevelSets == 1:
                    w1 *= f
                else:
                    for i in range(numLevelSets):
                        w1[i, :] *= f

        if numLevelSets == 1:
            wc = None
        else:
            wc = interpolate(wc, self.__pde.getFunctionSpaceForCoefficient("A"))
            sc = wc.getShape()
            if not sc == (numLevelSets, numLevelSets):
                raise ValueError("Unexpected shape %s for weight wc." % (sc,))
            if not self.__trafo.isCartesian():
                raise ValueError("Non-cartesian coordinates for cross-gradient term is not supported yet.")
        # ============= now we rescale weights: =============================
        L2s = np.asarray(boundingBoxEdgeLengths(domain)) ** 2
        L4 = 1 / np.sum(1 / L2s) ** 2
        if numLevelSets == 1:
            A = 0
            if w0 is not None:
                A = integrate(w0)
            if w1 is not None:
                A += integrate(inner(w1, 1 / L2s))
            if A > 0:
                f = scale / A
                if w0 is not None:
                    w0 *= f
                if w1 is not None:
                    w1 *= f
            else:
                raise ValueError("Non-positive weighting factor detected.")
        else:  # numLevelSets > 1
            for k in range(numLevelSets):
                A = 0
                if w0 is not None:
                    A = integrate(w0[k])
                if w1 is not None:
                    A += integrate(inner(w1[k, :], 1 / L2s))
                if A > 0:
                    f = scale[k] / A
                    if w0 is not None:
                        w0[k] *= f
                    if w1 is not None:
                        w1[k, :] *= f
                else:
                    raise ValueError("Non-positive weighting factor for level set component %d detected." % k)

                # and now the cross-gradient:
                if wc is not None:
                    for l in range(k):
                        A = integrate(wc[l, k]) / L4
                        if A > 0:
                            f = scale_c[l, k] / A
                            wc[l, k] *= f
        #                       else:
        #                           raise ValueError("Non-positive weighting factor for cross-gradient level set components %d and %d detected."%(l,k))

        self.__w0 = w0
        self.__w1 = w1
        self.__wc = wc

        self.__pde_is_set = False
        if self.__numLevelSets > 1:
            self.__useDiagonalHessianApproximation = useDiagonalHessianApproximation
        else:
            self.__useDiagonalHessianApproximation = True
        self._update_Hessian = True

        self.__num_tradeoff_factors = numLevelSets + ((numLevelSets - 1) * numLevelSets) // 2
        self.setTradeOffFactors()
        self.__vol_d = vol(self.__domain)
Example #19
0
    def __init__(self,
                 domain,
                 omega,
                 w,
                 data,
                 F,
                 coordinates=None,
                 fixAtBottom=False,
                 tol=1e-10,
                 saveMemory=True,
                 scaleF=True):
        """
        initializes a new forward model with acoustic wave form inversion.

        :param domain: domain of the model
        :type domain: `Domain`
        :param w: weighting factors
        :type w: ``Scalar``
        :param data: real and imaginary part of data
        :type data: ``escript.Data`` of shape (2,)
        :param F: real and imaginary part of source given at Dirac points,
                  on surface or at volume.
        :type F: ``escript.Data`` of shape (2,)
        :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 PDE to minimize memory requests. This will
                           require more compute time as the matrix needs to be
                           reallocated.
        :type saveMemory: ``bool``
        :param scaleF: if true source F is scaled to minimize defect.
        :type scaleF: ``bool``
        :param fixAtBottom: if true pressure is fixed to zero at the bottom of
                            the domain
        :type fixAtBottom: ``bool``
        """
        super(AcousticWaveForm, self).__init__()
        self.__trafo = edc.makeTransformation(domain, coordinates)
        if not self.getCoordinateTransformation().isCartesian():
            raise ValueError(
                "Non-Cartesian Coordinates are not supported yet.")
        if not isinstance(data, escript.Data):
            raise ValueError("data must be an escript.Data object.")
        if not data.getFunctionSpace() == escript.FunctionOnBoundary(domain):
            raise ValueError("data must be defined on boundary")
        if not data.getShape() == (2, ):
            raise ValueError(
                "data must have shape (2,) (real and imaginary part).")
        if w is None:
            w = 1.
        if not isinstance(w, escript.Data):
            w = escript.Data(w, escript.FunctionOnBoundary(domain))
        else:
            if not w.getFunctionSpace() == escript.FunctionOnBoundary(domain):
                raise ValueError("Weights must be defined on boundary.")
            if not w.getShape() == ():
                raise ValueError("Weights must be scalar.")

        self.__domain = domain
        self.__omega = omega
        self.__weight = w
        self.__data = data
        self.scaleF = scaleF
        if scaleF:
            A = escript.integrate(self.__weight *
                                  escript.length(self.__data)**2)
            if A > 0:
                self.__data *= 1. / escript.sqrt(A)

        self.__BX = escript.boundingBox(domain)
        self.edge_lengths = np.asarray(escript.boundingBoxEdgeLengths(domain))

        if not isinstance(F, escript.Data):
            F = escript.interpolate(F, escript.DiracDeltaFunctions(domain))
        if not F.getShape() == (2, ):
            raise ValueError(
                "Source must have shape (2,) (real and imaginary part).")

        self.__F = escript.Data()
        self.__f = escript.Data()
        self.__f_dirac = escript.Data()

        if F.getFunctionSpace() == escript.DiracDeltaFunctions(domain):
            self.__f_dirac = F
        elif F.getFunctionSpace() == escript.FunctionOnBoundary(domain):
            self.__f = F
        else:
            self.__F = F
        self.__tol = tol
        self.__fixAtBottom = fixAtBottom
        self.__pde = None
        if not saveMemory:
            self.__pde = self.setUpPDE()