def _updateRHS(self, expressions, subs, **constants):
     """
     """
     ev = symb.Evaluator()
     names = []
     for name in sorted(expressions):
         if name in self.__COEFFICIENTS:
             ev.addExpression(expressions[name])
             names.append(name)
     if len(names) == 0:
         return
     self.trace3("Starting expression evaluation.")
     T0 = time()
     ev.subs(**subs)
     res = ev.evaluate()
     if len(names) == 1: res = [res]
     self.trace3("RHS expressions evaluated in %f seconds." % (time() - T0))
     if self._debug > self.DEBUG2:
         for i in range(len(names)):
             self.trace3("util.Lsup(%s)=%s" % (names[i], util.Lsup(res[i])))
     args = dict(zip(names, res))
     # reset coefficients may be set at previous calls:
     for n in self.__COEFFICIENTS:
         if not n in args and not n in constants: args[n] = Data()
     args = dict(list(args.items()) + list(constants.items()))
     if not 'r' in args: args['r'] = Data()
     self._lpde.setValue(**args)
 def _updateMatrix(self, expressions, subs):
     """
     """
     ev = symb.Evaluator()
     names = []
     for name in sorted(expressions):
         if not name in self.__COEFFICIENTS:
             ev.addExpression(expressions[name])
             names.append(name)
     if len(names) == 0:
         return
     self.trace3("Starting expression evaluation.")
     T0 = time()
     ev.subs(**subs)
     res = ev.evaluate()
     if len(names) == 1: res = [res]
     self.trace3("Matrix expressions evaluated in %f seconds." %
                 (time() - T0))
     if self._debug > self.DEBUG2:
         for i in range(len(names)):
             self.trace3("util.Lsup(%s)=%s" % (names[i], util.Lsup(res[i])))
     self._lpde.setValue(**dict(zip(names, res)))
    def getSensitivity(self, f, g=None, **subs):
        """
        Calculates the sensitivity of the solution of an input factor ``f``
        in direction ``g``.

        :param f: the input factor to be investigated. ``f`` may be of rank 0
                  or 1.
        :type f: `Symbol`
        :param g: the direction(s) of change.
                  If not present, it is *g=eye(n)* where ``n`` is the number of
                  components of ``f``.
        :type g: ``list`` or single of ``float``, ``numpy.array`` or `Data`.
        :param subs: Substitutions for all symbols used in the coefficients
                     including unknown *u* and the input factor ``f`` to be
                     investigated
        :return: the sensitivity
        :rtype: `Data` with shape  *u.getShape()+(len(g),)* if *len(g)>1* or
                *u.getShape()* if *len(g)==1*
        """
        s_f = f.getShape()
        if len(s_f) == 0:
            len_f = 1
        elif len(s_f) == 1:
            len_f = s_f[0]
        else:
            raise ValueError("rank of input factor must be zero or one.")

        if not g is None:
            if len(s_f) == 0:
                if not isinstance(g, list): g = [g]
            else:
                if isinstance(g, list):
                    if len(g) == 0:
                        raise ValueError("no direction given.")
                    if len(getShape(g[0])) == 0:
                        g = [
                            g
                        ]  # if g[0] is a scalar we assume that the list g is to be interprested a data object
                else:
                    g = [g]
            # at this point g is a list of directions:
            len_g = len(g)
            for g_i in g:
                if not getShape(g_i) == s_f:
                    raise ValueError(
                        "shape of direction (=%s) must match rank of input factor (=%s)"
                        % (getShape(g_i), s_f))
        else:
            len_g = len_f

        #*** at this point g is a list of direction or None and len_g is the
        #    number of directions to be investigated.

        # now we make sure that the operator in the lpde is set (it is the same
        # as for the Newton-Raphson scheme)
        # if the solution etc are cached this could be omitted:
        constants = {}
        expressions = {}
        for n, e in sorted(self._set_coeffs.items(), key=lambda x: x[0]):
            if n not in self.__COEFFICIENTS:
                if symb.isSymbol(e):
                    expressions[n] = e
                else:
                    constants[n] = e
        self._lpde.setValue(**constants)
        self._updateMatrix(self, expressions, subs)
        #=====================================================================
        self._lpde.getSolverOptions().setAbsoluteTolerance(0.)
        self._lpde.getSolverOptions().setTolerance(self._rtol)
        self._lpde.getSolverOptions().setVerbosity(self._debug > self.DEBUG1)
        #=====================================================================
        #
        #   evaluate the derivatives of X, etc with respect to f:
        #
        ev = symb.Evaluator()
        names = []
        if hasattr(self, "_r"):
            if symb.isSymbol(self._r):
                names.append('r')
                ev.addExpression(self._r.diff(f))
        for n in sorted(self._set_coeffs.keys()):
            if n in self.__COEFFICIENTS and symb.isSymbol(self._set_coeffs[n]):
                if n == "X" or n == "X_reduced":
                    T0 = time()
                    B, A = symb.getTotalDifferential(self._set_coeffs[n], f, 1)
                    if n == 'X_reduced':
                        self.trace3(
                            "Computing A_reduced, B_reduced took %f seconds." %
                            (time() - T0))
                        names.append('A_reduced')
                        ev.addExpression(A)
                        names.append('B_reduced')
                        ev.addExpression(B)
                    else:
                        self.trace3("Computing A, B took %f seconds." %
                                    (time() - T0))
                        names.append('A')
                        ev.addExpression(A)
                        names.append('B')
                        ev.addExpression(B)
                elif n == "Y" or n == "Y_reduced":
                    T0 = time()
                    D, C = symb.getTotalDifferential(self._set_coeffs[n], f, 1)
                    if n == 'Y_reduced':
                        self.trace3(
                            "Computing C_reduced, D_reduced took %f seconds." %
                            (time() - T0))
                        names.append('C_reduced')
                        ev.addExpression(C)
                        names.append('D_reduced')
                        ev.addExpression(D)
                    else:
                        self.trace3("Computing C, D took %f seconds." %
                                    (time() - T0))
                        names.append('C')
                        ev.addExpression(C)
                        names.append('D')
                        ev.addExpression(D)
                elif n in ("y", "y_reduced", "y_contact", "y_contact_reduced",
                           "y_dirac"):
                    names.append('d' + name[1:])
                    ev.addExpression(self._set_coeffs[name].diff(f))
                    relevant_symbols['d' +
                                     name[1:]] = self._set_coeffs[name].diff(f)
        res = ev.evaluate()
        if len(names) == 1: res = [res]
        self.trace3("RHS expressions evaluated in %f seconds." % (time() - T0))
        if self._debug > self.DEBUG2:
            for i in range(len(names)):
                self.trace3("util.Lsup(%s)=%s" % (names[i], util.Lsup(res[i])))
        coeffs_f = dict(zip(names, res))
        #

        # now we are ready to calculate the right hand side coefficients into
        # args by multiplication with g and grad(g).
        if len_g > 1:
            if self.getNumSolutions() == 1:
                u_g = Data(0., (len_g, ),
                           self._lpde.getFunctionSpaceForSolution())
            else:
                u_g = Data(0., (self.getNumSolutions(), len_g),
                           self._lpde.getFunctionSpaceForSolution())

        for i in range(len_g):
            # reset coefficients may be set at previous calls:
            args = {}
            for n in self.__COEFFICIENTS:
                args[n] = Data()
            args['r'] = Data()
            if g is None:  # g_l=delta_{il} and len_f=len_g
                for n, v in coeffs_f:
                    name = None
                    if len_f > 1:
                        val = v[:, i]
                    else:
                        val = v
                    if n.startswith("d"):
                        name = 'y' + n[1:]
                    elif n.startswith("D"):
                        name = 'Y' + n[1:]
                    elif n.startswith("r"):
                        name = 'r' + n[1:]
                    if name: args[name] = val
            else:
                g_i = g[i]
                for n, v in coeffs_f:
                    name = None
                    if n.startswith("d"):
                        name = 'y' + n[1:]
                        val = self.__mm(v, g_i)
                    elif n.startswith("r"):
                        name = 'r'
                        val = self.__mm(v, g_i)
                    elif n.startswith("D"):
                        name = 'Y' + n[1:]
                        val = self.__mm(v, g_i)
                    elif n.startswith("B") and isinstance(g_i, Data):
                        name = 'Y' + n[1:]
                        val = self.__mm(v, grad(g_i))
                    elif n.startswith("C"):
                        name = 'X' + n[1:]
                        val = matrix_multiply(v, g_i)
                    elif n.startswith("A") and isinstance(g_i, Data):
                        name = 'X' + n[1:]
                        val = self.__mm(v, grad(g_i))
                    if name:
                        if name in args:
                            args[name] += val
                        else:
                            args[name] = val
            self._lpde.setValue(**args)
            u_g_i = self._lpde.getSolution()

            if len_g > 1:
                if self.getNumSolutions() == 1:
                    u_g[i] = -u_g_i
                else:
                    u_g[:, i] = -u_g_i
            else:
                u_g = -u_g_i

        return u_g
    def getSolution(self, **subs):
        """
        Returns the solution of the PDE.

        :param subs: Substitutions for all symbols used in the coefficients
                     including the initial value for the unknown *u*.
        :return: the solution
        :rtype: `Data`
        """
        # get the initial value for the iteration process

        # collect components of unknown in u_syms
        u_syms = []
        simple_u = False
        for i in numpy.ndindex(self._unknown.getShape()):
            u_syms.append(
                symb.Symbol(self._unknown[i]).atoms(sympy.Symbol).pop().name)
        if len(set(u_syms)) == 1: simple_u = True
        e = symb.Evaluator(self._unknown)
        for sym in u_syms:
            if not sym in subs:
                raise KeyError("Initial value for '%s' missing." % sym)
            if not isinstance(subs[sym], Data):
                subs[sym] = Data(subs[sym],
                                 self._lpde.getFunctionSpaceForSolution())
            e.subs(**{sym: subs[sym]})
        ui = e.evaluate()

        # modify ui so it meets the constraints:
        q = self._lpde.getCoefficient("q")
        if not q.isEmpty():
            if hasattr(self, "_r"):
                r = self._r
                if symb.isSymbol(r):
                    r = symb.Evaluator(r).evaluate(**subs)
                elif not isinstance(r, Data):
                    r = Data(r, self._lpde.getFunctionSpaceForSolution())
                elif r.isEmpty():
                    r = 0
            else:
                r = 0
            ui = q * r + (1 - q) * ui

        # separate symbolic expressions from other coefficients
        constants = {}
        expressions = {}
        for n, e in sorted(self._set_coeffs.items(), key=lambda x: x[0]):
            if symb.isSymbol(e):
                expressions[n] = e
            else:
                constants[n] = e
        # set constant PDE values now
        self._lpde.setValue(**constants)
        self._lpde.getSolverOptions().setAbsoluteTolerance(0.)
        self._lpde.getSolverOptions().setVerbosity(self._debug > self.DEBUG1)
        #=====================================================================
        # perform Newton iterations until error is small enough or
        # maximum number of iterations reached
        n = 0
        omega = 1.  # relaxation factor
        use_simplified_Newton = False
        defect_norm = None
        delta_norm = None
        converged = False

        #subs[u_sym]=ui
        if simple_u:
            subs[u_syms[0]] = ui
        else:
            for i in range(len(u_syms)):
                subs[u_syms[i]] = ui[i]

        while not converged:
            if n > self._iteration_steps_max:
                raise iteration_steps_maxReached(
                    "maximum number of iteration steps reached, giving up.")
            self.trace1(5 * "=" + " iteration step %d " % n + 5 * "=")
            # calculate the correction delta_u
            if n == 0:
                self._updateLinearPDE(expressions, subs, **constants)
                defect_norm = self._getDefectNorm(
                    self._lpde.getRightHandSide())
                LINTOL = 0.1
            else:
                if not use_simplified_Newton:
                    self._updateMatrix(expressions, subs)
                if q_u is None:
                    LINTOL = 0.1 * min(qtol / defect_norm)
                else:
                    LINTOL = 0.1 * max(q_u**2, min(qtol / defect_norm))
                LINTOL = max(1e-4, min(LINTOL, 0.1))
                #LINTOL=1.e-5
            self._lpde.getSolverOptions().setTolerance(LINTOL)
            self.trace1("PDE is solved with rel. tolerance = %e" % LINTOL)
            delta_u = self._lpde.getSolution()

            #check for reduced defect:
            omega = min(2 * omega, 1.)  # raise omega
            defect_reduced = False
            ui_old = ui
            while not defect_reduced:
                ui = ui_old - delta_u * omega
                if simple_u:
                    subs[u_syms[0]] = ui
                else:
                    for i in range(len(u_syms)):
                        subs[u_syms[i]] = ui[i]
                self._updateRHS(expressions, subs, **constants)
                new_defect_norm = self._getDefectNorm(
                    self._lpde.getRightHandSide())
                defect_reduced = False
                for i in range(len(new_defect_norm)):
                    if new_defect_norm[i] < defect_norm[i]:
                        defect_reduced = True

                #print new_defect_norm
                #q_defect=max(self._getSafeRatio(new_defect_norm, defect_norm))
                # if defect_norm==0 and new_defect_norm!=0
                # this will be util.DBLE_MAX
                #self.trace1("Defect reduction = %e with relaxation factor %e."%(q_defect, omega))
                if not defect_reduced:
                    omega *= 0.5
                    if omega < self._omega_min:
                        raise DivergenceDetected(
                            "Underrelaxtion failed to reduce defect, giving up."
                        )
            self.trace1("Defect reduction with relaxation factor %e." %
                        (omega, ))
            delta_norm, delta_norm_old = self._getSolutionNorm(
                delta_u) * omega, delta_norm
            defect_norm, defect_norm_old = new_defect_norm, defect_norm
            u_norm = self._getSolutionNorm(ui, atol=self._atol)
            # set the tolerance on equation level:
            qtol = self._getSafeRatio(defect_norm_old * u_norm * self._rtol,
                                      delta_norm)
            # if defect_norm_old==0 and defect_norm_old!=0 this will be util.DBLE_MAX
            #    -> the ordering of the equations is not appropriate.
            # if defect_norm_old==0 and defect_norm_old==0  this is zero so
            # convergence can happen for defect_norm==0 only.
            if not max(qtol) < util.DBLE_MAX:
                raise InadmissiblePDEOrdering(
                    "Review ordering of PDE equations.")
            # check stopping criteria
            if not delta_norm_old is None:
                q_u = max(self._getSafeRatio(delta_norm, delta_norm_old))
                # if delta_norm_old==0 and delta_norm!=0
                # this will be util.DBLE_MAX
                if q_u <= self._quadratic_convergence_limit and not omega < 1.:
                    quadratic_convergence = True
                    self.trace1(
                        "Quadratic convergence detected (rate %e <= %e)" %
                        (q_u, self._quadratic_convergence_limit))

                    converged = all(
                        [defect_norm[i] <= qtol[i] for i in range(len(qtol))])

                else:
                    self.trace1(
                        "No quadratic convergence detected (rate %e > %e, omega=%e)"
                        % (q_u, self._quadratic_convergence_limit, omega))
                    quadratic_convergence = False
                    converged = False
            else:
                q_u = None
                converged = False
                quadratic_convergence = False
            if self._debug > self.DEBUG0:
                for i in range(len(u_norm)):
                    self.trace1(
                        "Component %s: u: %e, du: %e, defect: %e, qtol: %e" %
                        (i, u_norm[i], delta_norm[i], defect_norm[i], qtol[i]))
                if converged: self.trace1("Iteration has converged.")
            # Can we switch to simplified Newton?
            if quadratic_convergence:
                q_defect = max(self._getSafeRatio(defect_norm,
                                                  defect_norm_old))
                if q_defect < self._simplified_newton_limit:
                    use_simplified_Newton = True
                    self.trace1(
                        "Simplified Newton-Raphson is applied (rate %e < %e)."
                        % (q_defect, self._simplified_newton_limit))
            n += 1
        self.trace1(5 * "=" +
                    " Newton-Raphson iteration completed after %d steps " % n +
                    5 * "=")
        return ui