예제 #1
0
 def doInitialization(self):
     self.__pde = lpde.LinearPDE(self.domain)
     self.__pde.setSymmetryOn()
     self.__pde.setReducedOrderOn()
     self.__pde.getSolverOptions().setSolverMethod(
         lpde.SolverOptions.LUMPING)
     self.__pde.setValue(D=self.heat_capacity * self.density)
예제 #2
0
    def setUpPDE(self):
        """
        Creates and returns the underlying PDE.

        :rtype: `lpde.LinearPDE`
        """
        if self.__pde is None:
            if not HAVE_DIRECT:
                raise ValueError(
                    "Either this build of escript or the current MPI configuration does not support direct solvers."
                )
            pde = lpde.LinearPDE(self.__domain, numEquations=2)
            D = pde.createCoefficient('D')
            A = pde.createCoefficient('A')
            A[0, :, 0, :] = escript.kronecker(self.__domain.getDim())
            A[1, :, 1, :] = escript.kronecker(self.__domain.getDim())
            pde.setValue(A=A, D=D)
            if self.__fixAtBottom:
                DIM = self.__domain.getDim()
                z = self.__domain.getX()[DIM - 1]
                pde.setValue(q=whereZero(z - self.__BX[DIM - 1][0]) * [1, 1])

            pde.getSolverOptions().setSolverMethod(lpde.SolverOptions.DIRECT)
            pde.getSolverOptions().setTolerance(self.__tol)
            pde.setSymmetryOff()
        else:
            pde = self.__pde
            pde.resetRightHandSideCoefficients()
        return pde
    def setUpPDE(self):
        """
        Return the underlying PDE.

        :rtype: `LinearPDE`
        """
        if self.__pde is None:
            DIM = self.__domain.getDim()
            pde = lpde.LinearPDE(self.__domain, numEquations=2)
            if self._directSolver == True:
                pde.getSolverOptions().setSolverMethod(
                    lpde.SolverOptions.DIRECT)
            D = pde.createCoefficient('D')
            A = pde.createCoefficient('A')
            pde.setValue(A=A, D=D, q=self._q)
            pde.getSolverOptions().setTolerance(self.__tol)
            pde.setSymmetryOff()
        else:
            pde = self.__pde
            pde.resetRightHandSideCoefficients()
        pde.setValue(X=pde.createCoefficient('X'),
                     Y=pde.createCoefficient('Y'))
        return pde
예제 #4
0
    def __setSolver(self, mode, domain, sigma, boundary_mask, boundary_value,
                    f):
        """
    DESCRIPTION:
    -----------
    Setups the coupled PDE for real and complex part.

    ARGUMENTS:
    ----------
    mode           :: string with TE or TM mode
    domain         :: escript object with mesh domain
    sigma          :: escript object with conductivity model
    boundary_mask  :: escript object with boundary mask
    boundary_value :: dictionary with real/imag boundary values
    f              :: sounding frequency

    RETURNS:
    --------
    mt2d_fields    :: dictionary with solved PDE, magnetotelluric fields real/imag
    """

        # Constants:
        pi = cmath.pi  # Ratio circle circumference to diameter.
        mu0 = 4 * pi * 1e-7  # Free space permeability in V.s/(A.m).
        wm = 2 * pi * f * mu0  # Angular frequency times mu0.

        # ---
        # Setup the coupled PDE for real/imaginary parts:
        # ---

        # Initialise the PDE object for two coupled equations (real/imaginary).
        mtpde = pde.LinearPDE(domain, numEquations=2)

        # If set, solve the 2D case using the direct solver:
        if MT_2D._solver.upper() == "DIRECT":
            mtpde.getSolverOptions().setSolverMethod(
                pde.SolverOptions().DIRECT)
        else:
            mtpde.getSolverOptions().setSolverMethod(
                pde.SolverOptions().DEFAULT)

        # Now initialise the PDE coefficients 'A' and 'D',
        # as well as the Dirichlet variables 'q' and 'r':
        A = mtpde.createCoefficient("A")
        D = mtpde.createCoefficient("D")
        q = mtpde.createCoefficient("q")
        r = mtpde.createCoefficient("r")

        # Set the appropriate values for the coefficients depending on the mode:
        if mode.upper() == "TE":
            a_val = 1.0
            d_val = wm * sigma
        elif mode.upper() == "TM":
            a_val = 1.0 / sigma
            d_val = wm

        # ---
        # Define the PDE parameters, mind boundary conditions.
        # ---

        # Now define the rank-4 coefficient A:
        for i in range(domain.getDim()):
            A[0, i, 0, i] = a_val
            A[1, i, 1, i] = a_val

        # And define the elements of 'D' which are decomposed into real/imaginary values:
        D[0, 0] = 0
        D[1, 0] = d_val
        D[0, 1] = -d_val
        D[1, 1] = 0

        # Set Dirichlet boundaries and values:
        q[0] = boundary_mask
        r[0] = boundary_value['real']
        q[1] = boundary_mask
        r[1] = boundary_value['imag']

        # ---
        # Solve the PDE
        # ---

        mtpde.setValue(A=A, D=D, q=q, r=r)
        pde_solution = mtpde.getSolution()

        # And return the real and imaginary parts individually:
        mt2d_fields = {"real": pde_solution[0], "imag": pde_solution[1]}
        #<Note>: the electric field is returned for TE-mode.
        #        the magnetic field is returned for TM-mode.

        return mt2d_fields
예제 #5
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)