Example #1
0
    def power(self, o, fp, gp):
        f, g = o.ufl_operands

        if not is_true_ufl_scalar(f):
            error("Expecting scalar expression f in f**g.")
        if not is_true_ufl_scalar(g):
            error("Expecting scalar expression g in f**g.")

        # Derivation of the general case: o = f(x)**g(x)
        # do/df  = g * f**(g-1) = g / f * o
        # do/dg  = ln(f) * f**g = ln(f) * o
        # do/df * df + do/dg * dg = o * (g / f * df + ln(f) * dg)

        if isinstance(gp, Zero):
            # This probably produces better results for the common
            # case of f**constant
            op = fp * g * f**(g-1)
        else:
            # Note: This produces expressions like (1/w)*w**5 instead of w**4
            # op = o * (fp * g / f + gp * ln(f)) # This reuses o
            op = f**(g-1) * (g*fp + f*ln(f)*gp)  # This gives better accuracy in dolfin integration test

        # Example: d/dx[x**(x**3)]:
        # f = x
        # g = x**3
        # df = 1
        # dg = 3*x**2
        # op1 = o * (fp * g / f + gp * ln(f))
        #     = x**(x**3)   * (x**3/x + 3*x**2*ln(x))
        # op2 = f**(g-1) * (g*fp + f*ln(f)*gp)
        #     = x**(x**3-1) * (x**3 + x*3*x**2*ln(x))

        return op
Example #2
0
    def power(self, o, fp, gp):
        f, g = o.ufl_operands

        if not is_true_ufl_scalar(f):
            error("Expecting scalar expression f in f**g.")
        if not is_true_ufl_scalar(g):
            error("Expecting scalar expression g in f**g.")

        # Derivation of the general case: o = f(x)**g(x)
        # do/df  = g * f**(g-1) = g / f * o
        # do/dg  = ln(f) * f**g = ln(f) * o
        # do/df * df + do/dg * dg = o * (g / f * df + ln(f) * dg)

        if isinstance(gp, Zero):
            # This probably produces better results for the common
            # case of f**constant
            op = fp * g * f**(g - 1)
        else:
            # Note: This produces expressions like (1/w)*w**5 instead of w**4
            # op = o * (fp * g / f + gp * ln(f)) # This reuses o
            op = f**(g - 1) * (
                g * fp + f * ln(f) * gp
            )  # This gives better accuracy in dolfin integration test

        # Example: d/dx[x**(x**3)]:
        # f = x
        # g = x**3
        # df = 1
        # dg = 3*x**2
        # op1 = o * (fp * g / f + gp * ln(f))
        #     = x**(x**3)   * (x**3/x + 3*x**2*ln(x))
        # op2 = f**(g-1) * (g*fp + f*ln(f)*gp)
        #     = x**(x**3-1) * (x**3 + x*3*x**2*ln(x))

        return op
Example #3
0
 def __init__(self, arg1, arg2):
     Operator.__init__(self)
     ufl_assert(is_true_ufl_scalar(arg1), "Expecting scalar argument 1.")
     ufl_assert(is_true_ufl_scalar(arg2), "Expecting scalar argument 2.")
     self._name     = "atan_2"
     self._arg1 = arg1
     self._arg2 = arg2
Example #4
0
 def __init__(self, arg1, arg2):
     Operator.__init__(self, (arg1, arg2))
     if isinstance(arg1, (ComplexValue, complex)) or isinstance(arg2, (ComplexValue, complex)):
         raise TypeError("Atan2 does not support complex numbers.")
     if not is_true_ufl_scalar(arg1):
         error("Expecting scalar argument 1.")
     if not is_true_ufl_scalar(arg2):
         error("Expecting scalar argument 2.")
Example #5
0
 def __init__(self, arg1, arg2):
     Operator.__init__(self, (arg1, arg2))
     if isinstance(arg1, (ComplexValue, complex)) or isinstance(
             arg2, (ComplexValue, complex)):
         raise TypeError("Atan2 does not support complex numbers.")
     if not is_true_ufl_scalar(arg1):
         error("Expecting scalar argument 1.")
     if not is_true_ufl_scalar(arg2):
         error("Expecting scalar argument 2.")
Example #6
0
 def __init__(self, name, classname, nu, argument):
     Operator.__init__(self)
     ufl_assert(is_true_ufl_scalar(nu), "Expecting scalar nu.")
     ufl_assert(is_true_ufl_scalar(argument), "Expecting scalar argument.")
     fnu = float(nu)
     inu = int(nu)
     if fnu == inu:
         nu = as_ufl(inu)
     else:
         nu = as_ufl(fnu)
     self._classname = classname
     self._name     = name
     self._nu       = nu
     self._argument = argument
Example #7
0
    def __new__(cls, a, b):
        a = as_ufl(a)
        b = as_ufl(b)

        # Assertions
        # TODO: Enabled workaround for nonscalar division in __div__,
        # so maybe we can keep this assertion. Some algorithms may need updating.
        if not is_ufl_scalar(a):
            error("Expecting scalar nominator in Division.")
        if not is_true_ufl_scalar(b):
            error("Division by non-scalar is undefined.")
        if isinstance(b, Zero):
            error("Division by zero!")

        # Simplification a/b -> a
        if isinstance(a, Zero) or b == 1:
            return a
        # Simplification "literal a / literal b" -> "literal value of a/b"
        # Avoiding integer division by casting to float
        if isinstance(a, ScalarValue) and isinstance(b, ScalarValue):
            return as_ufl(float(a._value) / float(b._value))
        # Simplification "a / a" -> "1"
        if not a.free_indices() and not a.shape() and a == b:
            return as_ufl(1)

        # construct and initialize a new Division object
        self = AlgebraOperator.__new__(cls)
        self._init(a, b)
        return self
Example #8
0
    def __init__(self, name, classname, nu, argument):
        if not is_true_ufl_scalar(nu):
            error("Expecting scalar nu.")
        if not is_true_ufl_scalar(argument):
            error("Expecting scalar argument.")

        # Use integer representation if suitable
        fnu = float(nu)
        inu = int(nu)
        if fnu == inu:
            nu = as_ufl(inu)
        else:
            nu = as_ufl(fnu)

        Operator.__init__(self, (nu, argument))

        self._classname = classname
        self._name = name
Example #9
0
    def __init__(self, name, classname, nu, argument):
        if not is_true_ufl_scalar(nu):
            error("Expecting scalar nu.")
        if not is_true_ufl_scalar(argument):
            error("Expecting scalar argument.")

        # Use integer representation if suitable
        fnu = float(nu)
        inu = int(nu)
        if fnu == inu:
            nu = as_ufl(inu)
        else:
            nu = as_ufl(fnu)

        Operator.__init__(self, (nu, argument))

        self._classname = classname
        self._name = name
def sensitivity_rhs(a, u, L, v):
    """UFL form operator:
    Compute the right hand side for a sensitivity calculation system.

    The derivation behind this computation is as follows.
    Assume a, L to be bilinear and linear forms
    corresponding to the assembled linear system

        Ax = b.

    Where x is the vector of the discrete function corresponding to u.
    Let v be some scalar variable this equation depends on.
    Then we can write

        0 = d/dv[Ax-b] = dA/dv x + A dx/dv - db/dv,
        A dx/dv = db/dv - dA/dv x,

    and solve this system for dx/dv, using the same bilinear form a
    and matrix A from the original system.
    Assume the forms are written

        v = variable(v_expression)
        L = IL(v)*dx
        a = Ia(v)*dx

    where IL and Ia are integrand expressions.
    Define a Coefficient u representing the solution
    to the equations. Then we can compute db/dv
    and dA/dv from the forms

        da = diff(a, v)
        dL = diff(L, v)

    and the action of da on u by

        dau = action(da, u)

    In total, we can build the right hand side of the system
    to compute du/dv with the single line

        dL = diff(L, v) - action(diff(a, v), u)

    or, using this function

        dL = sensitivity_rhs(a, u, L, v)
    """
    msg = "Expecting (a, u, L, v), (bilinear form, function, linear form and scalar variable)."
    ufl_assert(isinstance(a, Form), msg)
    ufl_assert(isinstance(u, Coefficient), msg)
    ufl_assert(isinstance(L, Form), msg)
    ufl_assert(isinstance(v, Variable), msg)
    ufl_assert(is_true_ufl_scalar(v), "Expecting scalar variable.")
    from ufl.operators import diff
    return diff(L, v) - action(diff(a, v), u)
    def power(self, o, a, b):
        f, fp = a
        g, gp = b

        # Debugging prints, should never happen:
        if not is_true_ufl_scalar(f):
            print ":" * 80
            print "f =", str(f)
            print "g =", str(g)
            print ":" * 80
        ufl_assert(is_true_ufl_scalar(f),
                   "Expecting scalar expression f in f**g.")
        ufl_assert(is_true_ufl_scalar(g),
                   "Expecting scalar expression g in f**g.")

        # Derivation of the general case: o = f(x)**g(x)
        #
        #do_df = g * f**(g-1)
        #do_dg = ln(f) * f**g
        #op = do_df*fp + do_dg*gp
        #
        #do_df = o * g / f # f**g * g / f
        #do_dg = ln(f) * o
        #op = do_df*fp + do_dg*gp

        # Got two possible alternatives here:
        if True:  # This version looks better.
            # Rewriting o as f*f**(g-1) we can do:
            f_g_m1 = f**(g - 1)
            op = f_g_m1 * (fp * g + f * ln(f) * gp)
            # In this case we can rewrite o using new subexpression
            o = f * f_g_m1
        else:
            # Pulling o out gives:
            op = o * (fp * g / f + ln(f) * gp)
            # This produces expressions like (1/w)*w**5 instead of w**4
            # If we do this, we reuse o
            o = self.reuse_if_possible(o, f, g)

        return (o, op)
    def power(self, o, a, b):
        f, fp = a
        g, gp = b

        # Debugging prints, should never happen:
        if not is_true_ufl_scalar(f):
            print ":"*80
            print "f =", str(f)
            print "g =", str(g)
            print ":"*80
        ufl_assert(is_true_ufl_scalar(f), "Expecting scalar expression f in f**g.")
        ufl_assert(is_true_ufl_scalar(g), "Expecting scalar expression g in f**g.")

        # Derivation of the general case: o = f(x)**g(x)
        #
        #do_df = g * f**(g-1)
        #do_dg = ln(f) * f**g
        #op = do_df*fp + do_dg*gp
        #
        #do_df = o * g / f # f**g * g / f
        #do_dg = ln(f) * o
        #op = do_df*fp + do_dg*gp

        # Got two possible alternatives here:
        if True: # This version looks better.
            # Rewriting o as f*f**(g-1) we can do:
            f_g_m1 = f**(g-1)
            op = f_g_m1*(fp*g + f*ln(f)*gp)
            # In this case we can rewrite o using new subexpression
            o = f*f_g_m1
        else:
            # Pulling o out gives:
            op = o*(fp*g/f + ln(f)*gp)
            # This produces expressions like (1/w)*w**5 instead of w**4
            # If we do this, we reuse o
            o = self.reuse_if_possible(o, f, g)

        return (o, op)
Example #13
0
    def __new__(cls, a, b):
        a = as_ufl(a)
        b = as_ufl(b)
        if not is_true_ufl_scalar(a): error("Cannot take the power of a non-scalar expression.")
        if not is_true_ufl_scalar(b): error("Cannot raise an expression to a non-scalar power.")

        if isinstance(a, ScalarValue) and isinstance(b, ScalarValue):
            return as_ufl(a._value ** b._value)
        if a == 0 and isinstance(b, ScalarValue):
            bf = float(b)
            if bf < 0:
                error("Division by zero, annot raise 0 to a negative power.")
            else:
                return zero()
        if b == 1:
            return a
        if b == 0:
            return IntValue(1)

        # construct and initialize a new Power object
        self = AlgebraOperator.__new__(cls)
        self._init(a, b)
        return self
    def division(self, o, a, b):
        f, fp = a
        g, gp = b
        o = self.reuse_if_possible(o, f, g)

        ufl_assert(is_ufl_scalar(f), "Not expecting nonscalar nominator")
        ufl_assert(is_true_ufl_scalar(g), "Not expecting nonscalar denominator")

        #do_df = 1/g
        #do_dg = -h/g
        #op = do_df*fp + do_df*gp
        #op = (fp - o*gp) / g

        # Get o and gp as scalars, multiply, then wrap as a tensor again
        so, oi = as_scalar(o)
        sgp, gi = as_scalar(gp)
        o_gp = so*sgp
        if oi or gi:
            o_gp = as_tensor(o_gp, oi + gi)
        op = (fp - o_gp) / g

        return (o, op)
Example #15
0
    def division(self, o, fp, gp):
        f, g = o.ufl_operands

        if not is_ufl_scalar(f):
            error("Not expecting nonscalar nominator")
        if not is_true_ufl_scalar(g):
            error("Not expecting nonscalar denominator")

        # do_df = 1/g
        # do_dg = -h/g
        # op = do_df*fp + do_df*gp
        # op = (fp - o*gp) / g

        # Get o and gp as scalars, multiply, then wrap as a tensor
        # again
        so, oi = as_scalar(o)
        sgp, gi = as_scalar(gp)
        o_gp = so * sgp
        if oi or gi:
            o_gp = as_tensor(o_gp, oi + gi)
        op = (fp - o_gp) / g

        return op
Example #16
0
    def division(self, o, fp, gp):
        f, g = o.ufl_operands

        if not is_ufl_scalar(f):
            error("Not expecting nonscalar nominator")
        if not is_true_ufl_scalar(g):
            error("Not expecting nonscalar denominator")

        # do_df = 1/g
        # do_dg = -h/g
        # op = do_df*fp + do_df*gp
        # op = (fp - o*gp) / g

        # Get o and gp as scalars, multiply, then wrap as a tensor
        # again
        so, oi = as_scalar(o)
        sgp, gi = as_scalar(gp)
        o_gp = so * sgp
        if oi or gi:
            o_gp = as_tensor(o_gp, oi + gi)
        op = (fp - o_gp) / g

        return op
    def division(self, o, a, b):
        f, fp = a
        g, gp = b
        o = self.reuse_if_possible(o, f, g)

        ufl_assert(is_ufl_scalar(f), "Not expecting nonscalar nominator")
        ufl_assert(is_true_ufl_scalar(g),
                   "Not expecting nonscalar denominator")

        #do_df = 1/g
        #do_dg = -h/g
        #op = do_df*fp + do_df*gp
        #op = (fp - o*gp) / g

        # Get o and gp as scalars, multiply, then wrap as a tensor again
        so, oi = as_scalar(o)
        sgp, gi = as_scalar(gp)
        o_gp = so * sgp
        if oi or gi:
            o_gp = as_tensor(o_gp, oi + gi)
        op = (fp - o_gp) / g

        return (o, op)
Example #18
0
File: checks.py Project: FEniCS/ufl
def validate_form(form):  # TODO: Can we make this return a list of errors instead of raising exception?
    """Performs all implemented validations on a form. Raises exception if something fails."""
    errors = []

    if not isinstance(form, Form):
        msg = "Validation failed, not a Form:\n%s" % ufl_err_str(form)
        error(msg)
        # errors.append(msg)
        # return errors

    # FIXME: There's a bunch of other checks we should do here.

    # FIXME: Add back check for multilinearity
    # Check that form is multilinear
    # if not is_multilinear(form):
    #     errors.append("Form is not multilinear in arguments.")

    # FIXME DOMAIN: Add check for consistency between domains somehow
    domains = set(t.ufl_domain()
                  for e in iter_expressions(form)
                  for t in traverse_unique_terminals(e)) - {None}
    if not domains:
        errors.append("Missing domain definition in form.")

    # Check that cell is the same everywhere
    cells = set(dom.ufl_cell() for dom in domains) - {None}
    if not cells:
        errors.append("Missing cell definition in form.")
    elif len(cells) > 1:
        errors.append("Multiple cell definitions in form: %s" % str(cells))

    # Check that no Coefficient or Argument instance have the same
    # count unless they are the same
    coefficients = {}
    arguments = {}
    for e in iter_expressions(form):
        for f in traverse_unique_terminals(e):
            if isinstance(f, Coefficient):
                c = f.count()
                if c in coefficients:
                    g = coefficients[c]
                    if f is not g:
                        errors.append("Found different Coefficients with " +
                                      "same count: %s and %s." % (repr(f),
                                                                  repr(g)))
                else:
                    coefficients[c] = f

            elif isinstance(f, Argument):
                n = f.number()
                p = f.part()
                if (n, p) in arguments:
                    g = arguments[(n, p)]
                    if f is not g:
                        if n == 0:
                            msg = "TestFunctions"
                        elif n == 1:
                            msg = "TrialFunctions"
                        else:
                            msg = "Arguments with same number and part"
                        msg = "Found different %s: %s and %s." % (msg, repr(f), repr(g))
                        errors.append(msg)
                else:
                    arguments[(n, p)] = f

    # Check that all integrands are scalar
    for expression in iter_expressions(form):
        if not is_true_ufl_scalar(expression):
            errors.append("Found non-scalar integrand expression: %s\n" %
                          ufl_err_str(expression))

    # Check that restrictions are permissible
    for integral in form.integrals():
        # Only allow restrictions on interior facet integrals and
        # surface measures
        if integral.integral_type().startswith("interior_facet"):
            check_restrictions(integral.integrand(), True)
        else:
            check_restrictions(integral.integrand(), False)

    # Raise exception with all error messages
    # TODO: Return errors list instead, need to collect messages from
    # all validations above first.
    if errors:
        final_msg = 'Found errors in validation of form:\n%s' % '\n\n'.join(errors)
        error(final_msg)
Example #19
0
 def __init__(self, name, argument):
     Operator.__init__(self, (argument,))
     if not is_true_ufl_scalar(argument):
         error("Expecting scalar argument.")
     self._name = name
Example #20
0
 def __init__(self, name, argument):
     Operator.__init__(self, (argument, ))
     if not is_true_ufl_scalar(argument):
         error("Expecting scalar argument.")
     self._name = name
Example #21
0
    def __rmul__(self, integrand):
        # Let other types implement multiplication with Measure
        # if they want to (to support the dolfin-adjoint TimeMeasure)
        if not isinstance(integrand, Expr):
            return NotImplemented

        # Allow only scalar integrands
        if not is_true_ufl_scalar(integrand):
            error("Trying to integrate expression of rank %d with free indices %r." \
                  % (integrand.rank(), integrand.free_indices()))

        # Is the measure in a state where multiplication is not allowed?
        if self._domain_description == Measure.DOMAIN_ID_UNDEFINED:
            error("Missing domain id. You need to select a subdomain, " +\
                  "e.g. M = f*dx(0) for subdomain 0.")

        #else: # TODO: Do it this way instead, and move all logic below into preprocess:
        #    # Return a one-integral form:
        #    from ufl.form import Form
        #    return Form( [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata(), self.domain_data())] )
        #    # or if we move domain data into Form instead:
        #    integrals = [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata())]
        #    domain_data = { self.domain_type(): self.domain_data() }
        #    return Form(integrals, domain_data)

        # TODO: How to represent all kinds of domain descriptions is still a bit unclear
        # Create form if we have a sufficient domain description
        elif (# We have a complete measure with domain description
            isinstance(self._domain_description, DomainDescription)
            # Is the measure in a basic state 'foo*dx'?
            or self._domain_description == Measure.DOMAIN_ID_UNIQUE
            # Is the measure over everywhere?
            or self._domain_description == Measure.DOMAIN_ID_EVERYWHERE
            # Is the measure in a state not allowed prior to preprocessing?
            or self._domain_description == Measure.DOMAIN_ID_OTHERWISE
            ):
            # Create and return a one-integral form
            from ufl.form import Form
            return Form( [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata(), self.domain_data())] )

        # Did we get several ids?
        elif isinstance(self._domain_description, tuple):
            # FIXME: Leave this analysis to preprocessing
            return sum(integrand*self.reconstruct(domain_id=d) for d in self._domain_description)

        # Did we get a name?
        elif isinstance(self._domain_description, str):
            # FIXME: Leave this analysis to preprocessing

            # Get all domains and regions from integrand to analyse
            domains = extract_domains(integrand)

            # Get domain or region with this name from integrand, error if multiple found
            name = self._domain_description
            candidates = set()
            for TD in domains:
                if TD.name() == name:
                    candidates.add(TD)
            ufl_assert(len(candidates) > 0,
                       "Found no domain with name '%s' in integrand." % name)
            ufl_assert(len(candidates) == 1,
                       "Multiple distinct domains with same name encountered in integrand.")
            D, = candidates

            # Reconstruct measure with the found named domain or region
            measure = self.reconstruct(domain_id=D)
            return integrand*measure

        # Did we get a number?
        elif isinstance(self._domain_description, int):
            # FIXME: Leave this analysis to preprocessing

            # Get all top level domains from integrand to analyse
            domains = extract_top_domains(integrand)

            # Get domain from integrand, error if multiple found
            if len(domains) == 0:
                # This is the partially defined integral from dolfin expression mess
                cell = integrand.cell()
                D = None if cell is None else as_domain(cell)
            elif len(domains) == 1:
                D, = domains
            else:
                error("Ambiguous reference to integer subdomain with multiple top domains in integrand.")

            if D is None:
                # We have a number but not a domain? Leave it to preprocess...
                # This is the case with badly formed forms which can occur from dolfin
                # Create and return a one-integral form
                from ufl.form import Form
                return Form( [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata(), self.domain_data())] )
            else:
                # Reconstruct measure with the found numbered subdomain
                measure = self.reconstruct(domain_id=D[self._domain_description])
                return integrand*measure

        # Provide error to user
        else:
            error("Invalid domain id %s." % str(self._domain_description))
Example #22
0
 def __init__(self, arg1, arg2):
     Operator.__init__(self, (arg1, arg2))
     if not is_true_ufl_scalar(arg1):
         error("Expecting scalar argument 1.")
     if not is_true_ufl_scalar(arg2):
         error("Expecting scalar argument 2.")
Example #23
0
    def __rmul__(self, integrand):
        # Let other types implement multiplication with Measure
        # if they want to (to support the dolfin-adjoint TimeMeasure)
        if not isinstance(integrand, Expr):
            return NotImplemented

        # Allow only scalar integrands
        if not is_true_ufl_scalar(integrand):
            error("Trying to integrate expression of rank %d with free indices %r." \
                  % (integrand.rank(), integrand.free_indices()))

        # Is the measure in a state where multiplication is not allowed?
        if self._domain_description == Measure.DOMAIN_ID_UNDEFINED:
            error("Missing domain id. You need to select a subdomain, " +\
                  "e.g. M = f*dx(0) for subdomain 0.")

        #else: # TODO: Do it this way instead, and move all logic below into preprocess:
        #    # Return a one-integral form:
        #    from ufl.form import Form
        #    return Form( [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata(), self.domain_data())] )
        #    # or if we move domain data into Form instead:
        #    integrals = [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata())]
        #    domain_data = { self.domain_type(): self.domain_data() }
        #    return Form(integrals, domain_data)

        # TODO: How to represent all kinds of domain descriptions is still a bit unclear
        # Create form if we have a sufficient domain description
        elif (  # We have a complete measure with domain description
                isinstance(self._domain_description, DomainDescription)
                # Is the measure in a basic state 'foo*dx'?
                or self._domain_description == Measure.DOMAIN_ID_UNIQUE
                # Is the measure over everywhere?
                or self._domain_description == Measure.DOMAIN_ID_EVERYWHERE
                # Is the measure in a state not allowed prior to preprocessing?
                or self._domain_description == Measure.DOMAIN_ID_OTHERWISE):
            # Create and return a one-integral form
            from ufl.form import Form
            return Form([
                Integral(integrand, self.domain_type(), self.domain_id(),
                         self.metadata(), self.domain_data())
            ])

        # Did we get several ids?
        elif isinstance(self._domain_description, tuple):
            # FIXME: Leave this analysis to preprocessing
            return sum(integrand * self.reconstruct(domain_id=d)
                       for d in self._domain_description)

        # Did we get a name?
        elif isinstance(self._domain_description, str):
            # FIXME: Leave this analysis to preprocessing

            # Get all domains and regions from integrand to analyse
            domains = extract_domains(integrand)

            # Get domain or region with this name from integrand, error if multiple found
            name = self._domain_description
            candidates = set()
            for TD in domains:
                if TD.name() == name:
                    candidates.add(TD)
            ufl_assert(
                len(candidates) > 0,
                "Found no domain with name '%s' in integrand." % name)
            ufl_assert(
                len(candidates) == 1,
                "Multiple distinct domains with same name encountered in integrand."
            )
            D, = candidates

            # Reconstruct measure with the found named domain or region
            measure = self.reconstruct(domain_id=D)
            return integrand * measure

        # Did we get a number?
        elif isinstance(self._domain_description, int):
            # FIXME: Leave this analysis to preprocessing

            # Get all top level domains from integrand to analyse
            domains = extract_top_domains(integrand)

            # Get domain from integrand, error if multiple found
            if len(domains) == 0:
                # This is the partially defined integral from dolfin expression mess
                cell = integrand.cell()
                D = None if cell is None else as_domain(cell)
            elif len(domains) == 1:
                D, = domains
            else:
                error(
                    "Ambiguous reference to integer subdomain with multiple top domains in integrand."
                )

            if D is None:
                # We have a number but not a domain? Leave it to preprocess...
                # This is the case with badly formed forms which can occur from dolfin
                # Create and return a one-integral form
                from ufl.form import Form
                return Form([
                    Integral(integrand, self.domain_type(), self.domain_id(),
                             self.metadata(), self.domain_data())
                ])
            else:
                # Reconstruct measure with the found numbered subdomain
                measure = self.reconstruct(
                    domain_id=D[self._domain_description])
                return integrand * measure

        # Provide error to user
        else:
            error("Invalid domain id %s." % str(self._domain_description))
Example #24
0
def validate_form(
    form
):  # TODO: Can we make this return a list of errors instead of raising exception?
    """Performs all implemented validations on a form. Raises exception if something fails."""
    errors = []

    if not isinstance(form, Form):
        msg = "Validation failed, not a Form:\n%s" % ufl_err_str(form)
        error(msg)
        # errors.append(msg)
        # return errors

    # FIXME: There's a bunch of other checks we should do here.

    # FIXME: Add back check for multilinearity
    # Check that form is multilinear
    # if not is_multilinear(form):
    #     errors.append("Form is not multilinear in arguments.")

    # FIXME DOMAIN: Add check for consistency between domains somehow
    domains = set(t.ufl_domain() for e in iter_expressions(form)
                  for t in traverse_unique_terminals(e)) - {None}
    if not domains:
        errors.append("Missing domain definition in form.")

    # Check that cell is the same everywhere
    cells = set(dom.ufl_cell() for dom in domains) - {None}
    if not cells:
        errors.append("Missing cell definition in form.")
    elif len(cells) > 1:
        errors.append("Multiple cell definitions in form: %s" % str(cells))

    # Check that no Coefficient or Argument instance have the same
    # count unless they are the same
    coefficients = {}
    arguments = {}
    for e in iter_expressions(form):
        for f in traverse_unique_terminals(e):
            if isinstance(f, Coefficient):
                c = f.count()
                if c in coefficients:
                    g = coefficients[c]
                    if f is not g:
                        errors.append("Found different Coefficients with " +
                                      "same count: %s and %s." %
                                      (repr(f), repr(g)))
                else:
                    coefficients[c] = f

            elif isinstance(f, Argument):
                n = f.number()
                p = f.part()
                if (n, p) in arguments:
                    g = arguments[(n, p)]
                    if f is not g:
                        if n == 0:
                            msg = "TestFunctions"
                        elif n == 1:
                            msg = "TrialFunctions"
                        else:
                            msg = "Arguments with same number and part"
                        msg = "Found different %s: %s and %s." % (msg, repr(f),
                                                                  repr(g))
                        errors.append(msg)
                else:
                    arguments[(n, p)] = f

    # Check that all integrands are scalar
    for expression in iter_expressions(form):
        if not is_true_ufl_scalar(expression):
            errors.append("Found non-scalar integrand expression: %s\n" %
                          ufl_err_str(expression))

    # Check that restrictions are permissible
    for integral in form.integrals():
        # Only allow restrictions on interior facet integrals and
        # surface measures
        if integral.integral_type().startswith("interior_facet"):
            check_restrictions(integral.integrand(), True)
        else:
            check_restrictions(integral.integrand(), False)

    # Raise exception with all error messages
    # TODO: Return errors list instead, need to collect messages from
    # all validations above first.
    if errors:
        final_msg = 'Found errors in validation of form:\n%s' % '\n\n'.join(
            errors)
        error(final_msg)
Example #25
0
 def __init__(self, name, argument):
     Operator.__init__(self)
     ufl_assert(is_true_ufl_scalar(argument), "Expecting scalar argument.")
     self._name     = name
     self._argument = argument
Example #26
0
def sensitivity_rhs(a, u, L, v):
    """UFL form operator:
    Compute the right hand side for a sensitivity calculation system.

    The derivation behind this computation is as follows.
    Assume *a*, *L* to be bilinear and linear forms
    corresponding to the assembled linear system

    .. math::

        Ax = b.

    Where *x* is the vector of the discrete function corresponding to *u*.
    Let *v* be some scalar variable this equation depends on.
    Then we can write

    .. math::
        0 = \\frac{d}{dv}(Ax-b) = \\frac{dA}{dv} x + A \\frac{dx}{dv} -
        \\frac{db}{dv},

        A \\frac{dx}{dv} = \\frac{db}{dv} - \\frac{dA}{dv} x,

    and solve this system for :math:`\\frac{dx}{dv}`, using the same bilinear
    form *a* and matrix *A* from the original system.
    Assume the forms are written
    ::

        v = variable(v_expression)
        L = IL(v)*dx
        a = Ia(v)*dx

    where ``IL`` and ``Ia`` are integrand expressions.
    Define a ``Coefficient u`` representing the solution
    to the equations. Then we can compute :math:`\\frac{db}{dv}`
    and :math:`\\frac{dA}{dv}` from the forms
    ::

        da = diff(a, v)
        dL = diff(L, v)

    and the action of ``da`` on ``u`` by
    ::

        dau = action(da, u)

    In total, we can build the right hand side of the system
    to compute :math:`\\frac{du}{dv}` with the single line
    ::

        dL = diff(L, v) - action(diff(a, v), u)

    or, using this function,
    ::

        dL = sensitivity_rhs(a, u, L, v)
    """
    if not (isinstance(a, Form) and
            isinstance(u, Coefficient) and
            isinstance(L, Form) and
            isinstance(v, Variable)):
        error("Expecting (a, u, L, v), (bilinear form, function, linear form and scalar variable).")
    if not is_true_ufl_scalar(v):
        error("Expecting scalar variable.")
    from ufl.operators import diff
    return diff(L, v) - action(diff(a, v), u)
Example #27
0
def validate_form(form): # TODO: Can we make this return a list of errors instead of raising exception?
    """Performs all implemented validations on a form. Raises exception if something fails."""
    errors = []
    warnings = []

    if not isinstance(form, Form):
        msg = "Validation failed, not a Form:\n%s" % repr(form)
        error(msg)
        #errors.append(msg)
        #return errors

    # FIXME: Add back check for multilinearity
    # Check that form is multilinear
    #if not is_multilinear(form):
    #    errors.append("Form is not multilinear in arguments.")

    # FIXME DOMAIN: Add check for consistency between domains somehow
    domains = set(t.domain()
                  for e in iter_expressions(form)
                  for t in traverse_terminals(e)) - set((None,))
    if not domains:
        errors.append("Missing domain definition in form.")

    top_domains = set(dom.top_domain() for dom in domains if dom is not None)
    if not top_domains:
        errors.append("Missing domain definition in form.")
    elif len(top_domains) > 1:
        warnings.append("Multiple top domain definitions in form: %s" % str(top_domains))

    # Check that cell is the same everywhere
    cells = set(dom.cell() for dom in top_domains) - set((None,))
    if not cells:
        errors.append("Missing cell definition in form.")
    elif len(cells) > 1:
        errors.append("Multiple cell definitions in form: %s" % str(cells))

    # Check that no Coefficient or Argument instance
    # have the same count unless they are the same
    coefficients = {}
    arguments = {}
    for e in iter_expressions(form):
        for f in traverse_terminals(e):
            if isinstance(f, Coefficient):
                c = f.count()
                if c in coefficients:
                    g = coefficients[c]
                    if not f is g:
                        errors.append("Found different Coefficients with " + \
                                   "same count: %s and %s." % (repr(f), repr(g)))
                else:
                    coefficients[c] = f

            elif isinstance(f, Argument):
                c = f.count()
                if c in arguments:
                    g = arguments[c]
                    if not f is g:
                        if c == -2: msg = "TestFunctions"
                        elif c == -1: msg = "TrialFunctions"
                        else: msg = "Arguments with same count"
                        msg = "Found different %s: %s and %s." % (msg, repr(f), repr(g))
                        errors.append(msg)
                else:
                    arguments[c] = f

    # Check that all integrands are scalar
    for expression in iter_expressions(form):
        if not is_true_ufl_scalar(expression):
            errors.append("Found non-scalar integrand expression:\n%s\n%s" % \
                              (str(expression), repr(expression)))

    # Check that restrictions are permissible
    for integral in form.integrals():
        # Only allow restricitions on interior facet integrals and surface measures
        if integral.measure().domain_type() in (Measure.INTERIOR_FACET, Measure.SURFACE):
            check_restrictions(integral.integrand(), True)
        else:
            check_restrictions(integral.integrand(), False)

    # Raise exception with all error messages
    # TODO: Return errors list instead, need to collect messages from all validations above first.
    if errors:
        final_msg = 'Found errors in validation of form:\n%s' % '\n\n'.join(errors)
        error(final_msg)
Example #28
0
def sensitivity_rhs(a, u, L, v):
    """UFL form operator:
    Compute the right hand side for a sensitivity calculation system.

    The derivation behind this computation is as follows.
    Assume *a*, *L* to be bilinear and linear forms
    corresponding to the assembled linear system

    .. math::

        Ax = b.

    Where *x* is the vector of the discrete function corresponding to *u*.
    Let *v* be some scalar variable this equation depends on.
    Then we can write

    .. math::
        0 = \\frac{d}{dv}(Ax-b) = \\frac{dA}{dv} x + A \\frac{dx}{dv} -
        \\frac{db}{dv},

        A \\frac{dx}{dv} = \\frac{db}{dv} - \\frac{dA}{dv} x,

    and solve this system for :math:`\\frac{dx}{dv}`, using the same bilinear
    form *a* and matrix *A* from the original system.
    Assume the forms are written
    ::

        v = variable(v_expression)
        L = IL(v)*dx
        a = Ia(v)*dx

    where ``IL`` and ``Ia`` are integrand expressions.
    Define a ``Coefficient u`` representing the solution
    to the equations. Then we can compute :math:`\\frac{db}{dv}`
    and :math:`\\frac{dA}{dv}` from the forms
    ::

        da = diff(a, v)
        dL = diff(L, v)

    and the action of ``da`` on ``u`` by
    ::

        dau = action(da, u)

    In total, we can build the right hand side of the system
    to compute :math:`\\frac{du}{dv}` with the single line
    ::

        dL = diff(L, v) - action(diff(a, v), u)

    or, using this function,
    ::

        dL = sensitivity_rhs(a, u, L, v)
    """
    if not (isinstance(a, Form) and
            isinstance(u, Coefficient) and
            isinstance(L, Form) and
            isinstance(v, Variable)):
        error("Expecting (a, u, L, v), (bilinear form, function, linear form and scalar variable).")
    if not is_true_ufl_scalar(v):
        error("Expecting scalar variable.")
    from ufl.operators import diff
    return diff(L, v) - action(diff(a, v), u)