Пример #1
0
def create_element(ufl_element):

    # Create element signature for caching (just use UFL element)
    element_signature = ufl_element

    # Check cache
    element = _cache.get(element_signature, None)
    if element:
        debug("Reusing element from cache")
        return element

    if isinstance(ufl_element, ufl.MixedElement):
        # Create mixed element (implemented by FFC)
        elements = _extract_elements(ufl_element)
        element = MixedElement(elements)
    elif isinstance(ufl_element, ufl.RestrictedElement):
        # Create restricted element(implemented by FFC)
        element = _create_restricted_element(ufl_element)
    elif isinstance(ufl_element, (ufl.FiniteElement, ufl.OuterProductElement, ufl.EnrichedElement, ufl.BrokenElement, ufl.TraceElement, ufl.FacetElement, ufl.InteriorElement)):
        # Create regular FIAT finite element
        element = _create_fiat_element(ufl_element)
    else:
        error("Cannot handle this element type: %s" % str(ufl_element))

    # Store in cache
    _cache[element_signature] = element

    return element
Пример #2
0
def integrate(monomial, integral_type, facet0, facet1, quadrature_degree,
              quadrature_rule, cellname, facet_cellname):
    """Compute the reference tensor for a given monomial term of a
    multilinear form"""

    info("Precomputing integrals on reference element")

    # Start timing
    tic = time.time()

    # Initialize quadrature points and weights
    (points, weights) = _init_quadrature(monomial.arguments, integral_type,
                                         quadrature_degree, quadrature_rule,
                                         cellname, facet_cellname)

    # Initialize quadrature table for basis functions
    table = _init_table(monomial.arguments, integral_type, points, facet0,
                        facet1)

    # Compute table Psi for each factor
    psis = [_compute_psi(v, table, len(points), integral_type) \
                for v in monomial.arguments]

    # Compute product of all Psis
    A0 = _compute_product(psis, monomial.float_value * weights)

    # Report elapsed time and number of entries
    toc = time.time() - tic
    num_entries = numpy.prod(numpy.shape(A0))
    debug("%d entries computed in %.3g seconds" % (num_entries, toc))
    debug("Shape of reference tensor: " + str(numpy.shape(A0)))

    return A0
Пример #3
0
def create_element(ufl_element):

    # Create element signature for caching (just use UFL element)
    element_signature = ufl_element

    # Check cache
    element = _cache.get(element_signature, None)
    if element:
        debug("Reusing element from cache")
        return element

    if isinstance(ufl_element, ufl.MixedElement):
        # Create mixed element (implemented by FFC)
        elements = _extract_elements(ufl_element)
        element = MixedElement(elements)
    elif isinstance(ufl_element, ufl.RestrictedElement):
        # Create restricted element(implemented by FFC)
        element = _create_restricted_element(ufl_element)
    elif isinstance(ufl_element,
                    (ufl.FiniteElement, ufl.OuterProductElement,
                     ufl.EnrichedElement, ufl.BrokenElement, ufl.TraceElement,
                     ufl.FacetElement, ufl.InteriorElement)):
        # Create regular FIAT finite element
        element = _create_fiat_element(ufl_element)
    else:
        error("Cannot handle this element type: %s" % str(ufl_element))

    # Store in cache
    _cache[element_signature] = element

    return element
Пример #4
0
def _auto_select_representation(integral, elements, function_replace_map):
    """
    Automatically select a suitable representation for integral.
    Note that the selection is made for each integral, not for
    each term. This means that terms which are grouped by UFL
    into the same integral (if their measures are equal) will
    necessarily get the same representation.
    """

    # Skip unsupported integration domain types
    if integral.integral_type() == "vertex":
        return "quadrature"

    # Get ALL sub elements, needed to check for restrictions of EnrichedElements.
    sub_elements = []
    for e in elements:
        sub_elements += _get_sub_elements(e)

    # Use quadrature representation if we have a quadrature element
    if len([e for e in sub_elements if e.family() == "Quadrature"]):
        return "quadrature"

    # Estimate cost of tensor representation
    tensor_cost = estimate_cost(integral, function_replace_map)
    debug("Estimated cost of tensor representation: " + str(tensor_cost))

    # Use quadrature if tensor representation is not possible
    if tensor_cost == -1:
        return "quadrature"

    # Otherwise, select quadrature when cost is high
    if tensor_cost <= 3:
        return "tensor"
    else:
        return "quadrature"
Пример #5
0
def verify_element(num_elements, i, ufl_element):
    info("\nVerifying element %d of %d: %s" %
         (i, num_elements, str(ufl_element)))
    error = compile_element(ufl_element, ffc_fail, log_file)

    # Return if test failed
    if error:
        return 1

    # Get FIAT values that are formatted in the same way as the values from
    # evaluate_basis and evaluate_basis_derivatives.
    # t = time.time()
    fiat_values = get_fiat_values(ufl_element)
    # print "fiat_vals: ", time.time() - t

    # Get FFC values.
    t = time.time()
    ffc_values = get_ffc_values(ufl_element)
    if ffc_values is None:
        return 1
    debug("  time to compute FFC values: %f" % (time.time() - t))

    # Compare values and return number of tests.
    return verify_values(ufl_element, fiat_values, ffc_values, dif_cri,
                         dif_acc, correct, log_file)
Пример #6
0
def create_element(ufl_element):

    # Create element signature for caching (just use UFL element)
    element_signature = ufl_element

    # Check cache
    if element_signature in _cache:
        debug("Reusing element from cache")
        return _cache[element_signature]

    # Create regular FIAT finite element
    if isinstance(ufl_element, ufl.FiniteElement):
        element = _create_fiat_element(ufl_element)

    # Create mixed element (implemented by FFC)
    elif isinstance(ufl_element, ufl.MixedElement):
        elements = _extract_elements(ufl_element)
        element = MixedElement(elements)

    # Create element union (implemented by FFC)
    elif isinstance(ufl_element, ufl.EnrichedElement):
        elements = [create_element(e) for e in ufl_element._elements]
        element = EnrichedElement(elements)

    # Create restricted element(implemented by FFC)
    elif isinstance(ufl_element, ufl.RestrictedElement):
        element = _create_restricted_element(ufl_element)

    else:
        error("Cannot handle this element type: %s" % str(ufl_element))

    # Store in cache
    _cache[element_signature] = element

    return element
Пример #7
0
def estimate_cost(integral, function_replace_map):
    """
    Estimate cost of tensor representation for integral. The cost is
    computed as the sum of the number of coefficients and derivatives,
    if the integrand can be represented as a monomial, and -1 if not.
    """

    # Check that integral type is supported
    supported = ["cell", "exterior_facet", "interior_facet"]
    if not integral.integral_type() in supported:
        return -1

    # Extract monomial integrand
    integrand = integral.integrand()
    try:
        monomial_form = extract_monomial_form([integrand], function_replace_map)
        transform_monomial_form(monomial_form)
    except Exception as exception:
        debug("Monomial extraction failed: " + str(exception))
        return -1

    # Check that we get just one integrand
    if not len(monomial_form) == 1:
        error("Expecting just one integrand.")

    # Compute cost
    cost = 0
    for integrand in monomial_form:
        for monomial in integrand.monomials:
            cost = max(cost, len(monomial.coefficients) + len(monomial.transforms))
    return cost
Пример #8
0
def _analyze_form(form, parameters):
    "Analyze form, returning form data."

    # Check that form is not empty
    if form.empty():
        error("Form (%s) seems to be zero: cannot compile it." % str(form))

    # Hack to override representation with environment variable
    forced_r = os.environ.get("FFC_FORCE_REPRESENTATION")
    if forced_r:
        warning(
            "representation:    forced by $FFC_FORCE_REPRESENTATION to '%s'" %
            forced_r)
        r = forced_r
    else:
        # Check representation parameters to figure out how to
        # preprocess
        r = _extract_representation_family(form, parameters)
    debug("Preprocessing form using '%s' representation family." % r)

    # Compute form metadata
    if r == "uflacs":
        # Temporary workaround to let uflacs have a different
        # preprocessing pipeline than the legacy quadrature
        # representation. This approach imposes a limitation that,
        # e.g. uflacs and qudrature, representations cannot be mixed
        # in the same form.
        from ufl.classes import Jacobian
        form_data = compute_form_data(form,
                                      do_apply_function_pullbacks=True,
                                      do_apply_integral_scaling=True,
                                      do_apply_geometry_lowering=True,
                                      preserve_geometry_types=(Jacobian, ),
                                      do_apply_restrictions=True)
    elif r == "tsfc":
        try:
            # TSFC provides compute_form_data wrapper using correct
            # kwargs
            from tsfc.ufl_utils import compute_form_data as tsfc_compute_form_data
        except ImportError:
            error(
                "Failed to import tsfc.ufl_utils.compute_form_data when asked "
                "for tsfc representation.")
        form_data = tsfc_compute_form_data(form)
    elif r == "quadrature":
        # quadrature representation
        form_data = compute_form_data(form)
    else:
        error("Unexpected representation family '%s' for form preprocessing." %
              r)

    info("")
    info(str(form_data))

    # Attach integral meta data
    _attach_integral_metadata(form_data, r, parameters)
    _validate_representation_choice(form_data, r)

    return form_data
Пример #9
0
def _analyze_form(form, parameters):
    "Analyze form, returning form data."

    # Check that form is not empty
    if form.empty():
        error("Form (%s) seems to be zero: cannot compile it." % str(form))

    # Hack to override representation with environment variable
    forced_r = os.environ.get("FFC_FORCE_REPRESENTATION")
    if forced_r:
        warning("representation:    forced by $FFC_FORCE_REPRESENTATION to '%s'" % forced_r)
        r = forced_r
    else:
        # Check representation parameters to figure out how to
        # preprocess
        r = _extract_representation_family(form, parameters)
    debug("Preprocessing form using '%s' representation family." % r)

    # Compute form metadata
    if r == "uflacs":
        # Temporary workaround to let uflacs have a different
        # preprocessing pipeline than the legacy quadrature
        # representation. This approach imposes a limitation that,
        # e.g. uflacs and qudrature, representations cannot be mixed
        # in the same form.
        from ufl.classes import Jacobian
        form_data = compute_form_data(form,
                                      do_apply_function_pullbacks=True,
                                      do_apply_integral_scaling=True,
                                      do_apply_geometry_lowering=True,
                                      preserve_geometry_types=(Jacobian,),
                                      do_apply_restrictions=True)
    elif r == "tsfc":
        try:
            # TSFC provides compute_form_data wrapper using correct
            # kwargs
            from tsfc.ufl_utils import compute_form_data as tsfc_compute_form_data
        except ImportError:
            error("Failed to import tsfc.ufl_utils.compute_form_data when asked "
                  "for tsfc representation.")
        form_data = tsfc_compute_form_data(form)
    elif r == "quadrature":
        # quadrature representation
        form_data = compute_form_data(form)
    else:
        error("Unexpected representation family '%s' for form preprocessing." % r)

    info("")
    info(str(form_data))

    # Attach integral meta data
    _attach_integral_metadata(form_data, r, parameters)
    _validate_representation_choice(form_data, r)

    return form_data
def estimate_cost(integral, function_replace_map):
    """
    Estimate cost of tensor representation for integrand. The cost is
    computed as the sum of the number of coefficients and derivatives,
    if the integrand can be represented as a monomial, and -1 if not.
    """

    # TODO: Implement this to take integrand instead of integral? May allow simplification inn caller code.

    # Extract monomial integrand
    try:
        monomial_form = extract_monomial_form([integral], function_replace_map)
        transform_monomial_form(monomial_form)
    except Exception, exception:
        debug("Monomial extraction failed: " + str(exception))
        return -1
Пример #11
0
def estimate_cost(integral, function_replace_map):
    """
    Estimate cost of tensor representation for integrand. The cost is
    computed as the sum of the number of coefficients and derivatives,
    if the integrand can be represented as a monomial, and -1 if not.
    """

    # TODO: Implement this to take integrand instead of integral? May allow simplification inn caller code.

    # Extract monomial integrand
    try:
        monomial_form = extract_monomial_form([integral], function_replace_map)
        transform_monomial_form(monomial_form)
    except Exception, exception:
        debug("Monomial extraction failed: " + str(exception))
        return -1
    def __init__(self, monomial):
        "Create geometry tensor for given monomial."

        # Save monomial data
        self.determinant = monomial.determinant
        self.coefficients = monomial.coefficients
        self.transforms = monomial.transforms

        # Extract indices
        secondary_indices = monomial.extract_unique_indices(MonomialIndex.SECONDARY)
        external_indices  = monomial.extract_unique_indices(MonomialIndex.EXTERNAL)

        # Create multiindices
        self.secondary_multi_index = create_multiindex(secondary_indices)
        self.external_multi_index  = create_multiindex(external_indices)

        debug("Secondary multi index: " + str(self.secondary_multi_index))
        debug("External multi index:  " + str(self.external_multi_index))
def integrate(monomial,
              domain_type,
              facet0, facet1,
              quadrature_degree,
              quadrature_rule,
              cellname,
              facet_cellname):
    """Compute the reference tensor for a given monomial term of a
    multilinear form"""

    info("Precomputing integrals on reference element")

    # Start timing
    tic = time.time()

    # Initialize quadrature points and weights
    (points, weights) = _init_quadrature(monomial.arguments,
                                         domain_type,
                                         quadrature_degree,
                                         quadrature_rule,
                                         cellname,
                                         facet_cellname)

    # Initialize quadrature table for basis functions
    table = _init_table(monomial.arguments,
                        domain_type,
                        points,
                        facet0, facet1)

    # Compute table Psi for each factor
    psis = [_compute_psi(v, table, len(points), domain_type) \
                for v in monomial.arguments]

    # Compute product of all Psis
    A0 = _compute_product(psis, monomial.float_value * weights)

    # Report elapsed time and number of entries
    toc = time.time() - tic
    num_entries = numpy.prod(numpy.shape(A0))
    debug("%d entries computed in %.3g seconds" % (num_entries, toc))
    debug("Shape of reference tensor: " + str(numpy.shape(A0)))

    return A0
Пример #14
0
    def __init__(self, monomial):
        "Create geometry tensor for given monomial."

        # Save monomial data
        self.determinants = monomial.determinants
        self.coefficients = monomial.coefficients
        self.transforms = monomial.transforms

        # Extract indices
        secondary_indices = monomial.extract_unique_indices(
            MonomialIndex.SECONDARY)
        external_indices = monomial.extract_unique_indices(
            MonomialIndex.EXTERNAL)

        # Create multiindices
        self.secondary_multi_index = create_multiindex(secondary_indices)
        self.external_multi_index = create_multiindex(external_indices)

        debug("Secondary multi index: " + str(self.secondary_multi_index))
        debug("External multi index:  " + str(self.external_multi_index))
Пример #15
0
def _auto_select_quadrature_degree(integrand, representation, elements,
                                   element_replace_map):
    "Automatically select a suitable quadrature degree for integrand."
    # TODO: Move this to form preprocessing, as part of integral_data?

    # Use quadrature element degree if any is found
    quadrature_degrees = [
        e.degree() for e in elements if e.family() == "Quadrature"
    ]
    if quadrature_degrees:
        debug("Found quadrature element(s) with the following degree(s): " +
              str(quadrature_degrees))
        ffc_assert(min(quadrature_degrees) == max(quadrature_degrees), \
                   "All QuadratureElements in an integrand must have the same degree: %s" \
                   % str(quadrature_degrees))
        debug("Selecting quadrature degree based on quadrature element: " +
              str(quadrature_degrees[0]))
        ffc_assert(
            representation != "tensor",
            "Tensor representation does not support quadrature elements.")
        return quadrature_degrees[0]

    # Otherwise estimate total degree of integrand
    q = estimate_total_polynomial_degree(integrand, default_quadrature_degree,
                                         element_replace_map)
    debug(
        "Selecting quadrature degree based on total polynomial degree of integrand: "
        + str(q))

    return q
    def __init__(self,
                 monomial,
                 domain_type,
                 facet0, facet1,
                 quadrature_order,
                 quadrature_rule,
                 cellname,
                 facet_cellname):
        "Create reference tensor for given monomial."

        # Compute reference tensor
        self.A0 = integrate(monomial,
                            domain_type,
                            facet0, facet1,
                            quadrature_order,
                            quadrature_rule,
                            cellname,
                            facet_cellname)

        # Extract indices
        primary_indices   = monomial.extract_unique_indices(MonomialIndex.PRIMARY)
        secondary_indices = monomial.extract_unique_indices(MonomialIndex.SECONDARY)
        internal_indices  = monomial.extract_unique_indices(MonomialIndex.INTERNAL)

        # Create multiindices
        self.primary_multi_index   = create_multiindex(primary_indices)
        self.secondary_multi_index = create_multiindex(secondary_indices)
        self.internal_multi_index  = create_multiindex(internal_indices)

        # Store monomial
        self.monomial = monomial

        debug("Primary multi index:   " + str(self.primary_multi_index))
        debug("Secondary multi index: " + str(self.secondary_multi_index))
        debug("Internal multi index:  " + str(self.internal_multi_index))
Пример #17
0
    def __init__(self, monomial, domain_type, facet0, facet1, quadrature_order,
                 quadrature_rule, cellname, facet_cellname):
        "Create reference tensor for given monomial."

        # Compute reference tensor
        self.A0 = integrate(monomial, domain_type, facet0, facet1,
                            quadrature_order, quadrature_rule, cellname,
                            facet_cellname)

        # Extract indices
        primary_indices = monomial.extract_unique_indices(
            MonomialIndex.PRIMARY)
        secondary_indices = monomial.extract_unique_indices(
            MonomialIndex.SECONDARY)
        internal_indices = monomial.extract_unique_indices(
            MonomialIndex.INTERNAL)

        # Create multiindices
        self.primary_multi_index = create_multiindex(primary_indices)
        self.secondary_multi_index = create_multiindex(secondary_indices)
        self.internal_multi_index = create_multiindex(internal_indices)

        # Store monomial
        self.monomial = monomial

        debug("Primary multi index:   " + str(self.primary_multi_index))
        debug("Secondary multi index: " + str(self.secondary_multi_index))
        debug("Internal multi index:  " + str(self.internal_multi_index))
Пример #18
0
def verify_element(num_elements, i, ufl_element):
    info("\nVerifying element %d of %d: %s" % (i, num_elements, str(ufl_element)))
    error = compile_element(ufl_element, ffc_fail, log_file)

    # Return if test failed
    if error:
        return 1

    # Get FIAT values that are formatted in the same way as the values from
    # evaluate_basis and evaluate_basis_derivatives.
    # t = time.time()
    fiat_values = get_fiat_values(ufl_element)
    # print "fiat_vals: ", time.time() - t

    # Get FFC values.
    t = time.time()
    ffc_values = get_ffc_values(ufl_element)
    if ffc_values is None:
        return 1
    debug("  time to compute FFC values: %f" % (time.time() - t))

    # Compare values and return number of tests.
    return verify_values(ufl_element, fiat_values, ffc_values, dif_cri, dif_acc, correct, log_file)
Пример #19
0
def _auto_select_representation(integral, elements, function_replace_map):
    """
    Automatically select a suitable representation for integral.
    Note that the selection is made for each integral, not for
    each term. This means that terms which are grouped by UFL
    into the same integral (if their measures are equal) will
    necessarily get the same representation.
    """

    # Get ALL sub elements, needed to check for restrictions of EnrichedElements.
    sub_elements = []
    for e in elements:
        sub_elements += _get_sub_elements(e)

    # Use quadrature representation if we have a quadrature element
    if len([e for e in sub_elements if e.family() == "Quadrature"]):
        return "quadrature"

    # Use quadrature representation if any elements are restricted to
    # UFL.Measure. This is used when integrals are computed over discontinuities.
    #if len([e for e in sub_elements if isinstance(e.cell_restriction(), Measure)]):
    #    return "quadrature"

    # Estimate cost of tensor representation
    tensor_cost = estimate_cost(integral, function_replace_map)
    debug("Estimated cost of tensor representation: " + str(tensor_cost))

    # Use quadrature if tensor representation is not possible
    if tensor_cost == -1:
        return "quadrature"

    # Otherwise, select quadrature when cost is high
    if tensor_cost <= 3:
        return "tensor"
    else:
        return "quadrature"
Пример #20
0
def create_psi_tables(tables, eliminate_zeros, entity_type):
    "Create names and maps for tables and non-zero entries if appropriate."

    debug("\nQG-utils, psi_tables:\n" + str(tables))

    # Create element map {points:{element:number,},}
    # and a plain dictionary {name:values,}.
    element_map, flat_tables = flatten_psi_tables(tables, entity_type)
    debug("\nQG-utils, psi_tables, flat_tables:\n" + str(flat_tables))

    # Reduce tables such that we only have those tables left with unique values
    # Create a name map for those tables that are redundant.
    name_map, unique_tables = unique_psi_tables(flat_tables, eliminate_zeros)
    debug("\nQG-utils, psi_tables, unique_tables:\n" + str(unique_tables))
    debug("\nQG-utils, psi_tables, name_map:\n" + str(name_map))

    return (element_map, name_map, unique_tables)
Пример #21
0
def _auto_select_representation(integral, elements, function_replace_map):
    """
    Automatically select a suitable representation for integral.
    Note that the selection is made for each integral, not for
    each term. This means that terms which are grouped by UFL
    into the same integral (if their measures are equal) will
    necessarily get the same representation.
    """

    # Get ALL sub elements, needed to check for restrictions of EnrichedElements.
    sub_elements = []
    for e in elements:
        sub_elements += _get_sub_elements(e)

    # Use quadrature representation if we have a quadrature element
    if len([e for e in sub_elements if e.family() == "Quadrature"]):
        return "quadrature"

    # Use quadrature representation if any elements are restricted to
    # UFL.Measure. This is used when integrals are computed over discontinuities.
    #if len([e for e in sub_elements if isinstance(e.cell_restriction(), Measure)]):
    #    return "quadrature"

    # Estimate cost of tensor representation
    tensor_cost = estimate_cost(integral, function_replace_map)
    debug("Estimated cost of tensor representation: " + str(tensor_cost))

    # Use quadrature if tensor representation is not possible
    if tensor_cost == -1:
        return "quadrature"

    # Otherwise, select quadrature when cost is high
    if tensor_cost <= 3:
        return "tensor"
    else:
        return "quadrature"
Пример #22
0
def create_psi_tables(tables, eliminate_zeros, entity_type):
    "Create names and maps for tables and non-zero entries if appropriate."

    debug("\nQG-utils, psi_tables:\n" + str(tables))

    # Create element map {points:{element:number,},} and a plain
    # dictionary {name:values,}.
    element_map, flat_tables = flatten_psi_tables(tables, entity_type)
    debug("\nQG-utils, psi_tables, flat_tables:\n" + str(flat_tables))

    # Reduce tables such that we only have those tables left with
    # unique values. Create a name map for those tables that are
    # redundant.
    name_map, unique_tables = unique_psi_tables(flat_tables, eliminate_zeros)
    debug("\nQG-utils, psi_tables, unique_tables:\n" + str(unique_tables))
    debug("\nQG-utils, psi_tables, name_map:\n" + str(name_map))

    return (element_map, name_map, unique_tables)
Пример #23
0
def _auto_select_quadrature_degree(integrand, representation, elements, element_replace_map):
    "Automatically select a suitable quadrature degree for integrand."
    # TODO: Move this to form preprocessing, as part of integral_data?

    # Use quadrature element degree if any is found
    quadrature_degrees = [e.degree() for e in elements if e.family() == "Quadrature"]
    if quadrature_degrees:
        debug("Found quadrature element(s) with the following degree(s): " + str(quadrature_degrees))
        ffc_assert(min(quadrature_degrees) == max(quadrature_degrees), \
                   "All QuadratureElements in an integrand must have the same degree: %s" \
                   % str(quadrature_degrees))
        debug("Selecting quadrature degree based on quadrature element: " + str(quadrature_degrees[0]))
        ffc_assert(representation != "tensor", "Tensor representation does not support quadrature elements.")
        return quadrature_degrees[0]

    # Otherwise estimate total degree of integrand
    q = estimate_total_polynomial_degree(integrand, default_quadrature_degree, element_replace_map)
    debug("Selecting quadrature degree based on total polynomial degree of integrand: " + str(q))

    return q
Пример #24
0
def unique_psi_tables(tables, eliminate_zeros):
    """Returns a name map and a dictionary of unique tables. The function
    checks if values in the tables are equal, if this is the case it
    creates a name mapping. It also create additional information
    (depending on which parameters are set) such as if the table
    contains all ones, or only zeros, and a list on non-zero columns.
    unique_tables - {name:values,}.  name_map -
    {original_name:[new_name, non-zero-columns (list), is zero (bool),
    is ones (bool)],}.

    """

    # Get unique tables (from old table utility).
    name_map, inverse_name_map = unique_tables(tables)

    debug("\ntables: " + str(tables))
    debug("\nname_map: " + str(name_map))
    debug("\ninv_name_map: " + str(inverse_name_map))

    # Set values to zero if they are lower than threshold.
    format_epsilon = format["epsilon"]
    for name in tables:
        # Get values.
        vals = tables[name]
        for r in range(numpy.shape(vals)[0]):
            for c in range(numpy.shape(vals)[1]):
                if abs(vals[r][c]) < format_epsilon:
                    vals[r][c] = 0
        tables[name] = vals

    # Extract the column numbers that are non-zero.  If optimisation
    # option is set counter for non-zero column arrays.
    i = 0
    non_zero_columns = {}
    if eliminate_zeros:
        for name in sorted(tables.keys()):

            # Get values.
            vals = tables[name]

            # Skip if values are missing
            if len(vals) == 0:
                continue

            # Use the first row as reference.
            non_zeros = list(vals[0].nonzero()[0])

            # If all columns in the first row are non zero, there's no
            # point in continuing.
            if len(non_zeros) == numpy.shape(vals)[1]:
                continue

            # If we only have one row (IP) we just need the nonzero
            # columns.
            if numpy.shape(vals)[0] == 1:
                if list(non_zeros):
                    non_zeros.sort()
                    non_zero_columns[name] = (i, non_zeros)

                    # Compress values.
                    tables[name] = vals[:, non_zeros]
                    i += 1

            # Check if the remaining rows are nonzero in the same
            # positions, else expand.
            else:
                for j in range(1, numpy.shape(vals)[0]):
                    # All rows must have the same non-zero columns for
                    # the optimization to work (at this stage).
                    new_non_zeros = list(vals[j].nonzero()[0])
                    if non_zeros != new_non_zeros:
                        non_zeros = non_zeros + [c for c in new_non_zeros if c not in non_zeros]
                        # If this results in all columns being
                        # non-zero, continue.
                        if len(non_zeros) == numpy.shape(vals)[1]:
                            continue

                # Only add nonzeros if it results in a reduction of
                # columns.
                if len(non_zeros) != numpy.shape(vals)[1]:
                    if list(non_zeros):
                        non_zeros.sort()
                        non_zero_columns[name] = (i, non_zeros)

                        # Compress values.
                        tables[name] = vals[:, non_zeros]
                        i += 1

    # Check if we have some zeros in the tables.
    names_zeros = contains_zeros(tables)

    # Get names of tables with all ones.
    names_ones = get_ones(tables)

    # Add non-zero column, zero and ones info to inverse_name_map (so
    # we only need to pass around one name_map to code generating
    # functions).
    for name in inverse_name_map:
        if inverse_name_map[name] in non_zero_columns:
            nzc = non_zero_columns[inverse_name_map[name]]
            zero = inverse_name_map[name] in names_zeros
            ones = inverse_name_map[name] in names_ones
            inverse_name_map[name] = [inverse_name_map[name], nzc, zero, ones]
        else:
            zero = inverse_name_map[name] in names_zeros
            ones = inverse_name_map[name] in names_ones
            inverse_name_map[name] = [inverse_name_map[name], (), zero, ones]

    # If we found non zero columns we might be able to reduce number
    # of tables further.
    if non_zero_columns:
        # Try reducing the tables. This is possible if some tables
        # have become identical as a consequence of compressing the
        # tables.  This happens with e.g., gradients of linear basis
        # FE0 = {-1,0,1}, nzc0 = [0,2]
        # FE1 = {-1,1,0}, nzc1 = [0,1]  -> FE0 = {-1,1}, nzc0 = [0,2], nzc1 = [0,1].

        # Call old utility function again.
        nm, inv_nm = unique_tables(tables)

        # Update name maps.
        for name in inverse_name_map:
            if inverse_name_map[name][0] in inv_nm:
                inverse_name_map[name][0] = inv_nm[inverse_name_map[name][0]]
        for name in nm:
            maps = nm[name]
            for m in maps:
                if name not in name_map:
                    name_map[name] = []
                if m in name_map:
                    name_map[name] += name_map[m] + [m]
                    del name_map[m]
                else:
                    name_map[name].append(m)

        # Get new names of tables with all ones (for vector
        # constants).
        names = get_ones(tables)

        # Because these tables now contain ones as a consequence of
        # compression we still need to consider the non-zero columns
        # when looking up values in coefficient arrays. The psi
        # entries can however we neglected and we don't need to
        # tabulate the values (if option is set).
        for name in names:
            if name in name_map:
                maps = name_map[name]
                for m in maps:
                    inverse_name_map[m][3] = True
            if name in inverse_name_map:
                    inverse_name_map[name][3] = True

    # Write protect info and return values
    for name in inverse_name_map:
        inverse_name_map[name] = tuple(inverse_name_map[name])

    # Note: inverse_name_map here is called name_map in
    # create_psi_tables and the quadraturetransformerbase class
    return (inverse_name_map, tables)
Пример #25
0
def unique_psi_tables(tables, eliminate_zeros):
    """Returns a name map and a dictionary of unique tables. The function checks
    if values in the tables are equal, if this is the case it creates a name
    mapping. It also create additional information (depending on which parameters
    are set) such as if the table contains all ones, or only zeros, and a list
    on non-zero columns.
    unique_tables - {name:values,}.
    name_map      - {original_name:[new_name, non-zero-columns (list), is zero (bool), is ones (bool)],}."""

    # Get unique tables (from old table utility).
    name_map, inverse_name_map = unique_tables(tables)

    debug("\ntables: " + str(tables))
    debug("\nname_map: " + str(name_map))
    debug("\ninv_name_map: " + str(inverse_name_map))

    # Set values to zero if they are lower than threshold.
    format_epsilon = format["epsilon"]
    for name in tables:
        # Get values.
        vals = tables[name]
        for r in range(numpy.shape(vals)[0]):
            for c in range(numpy.shape(vals)[1]):
                if abs(vals[r][c]) < format_epsilon:
                    vals[r][c] = 0
        tables[name] = vals

    # Extract the column numbers that are non-zero.
    # If optimisation option is set
    # counter for non-zero column arrays.
    i = 0
    non_zero_columns = {}
    if eliminate_zeros:
        for name in sorted(tables.keys()):

            # Get values.
            vals = tables[name]

            # Skip if values are missing
            if len(vals) == 0:
                continue

            # Use the first row as reference.
            non_zeros = list(vals[0].nonzero()[0])

            # If all columns in the first row are non zero, there's no point
            # in continuing.
            if len(non_zeros) == numpy.shape(vals)[1]:
                continue

            # If we only have one row (IP) we just need the nonzero columns.
            if numpy.shape(vals)[0] == 1:
                if list(non_zeros):
                    non_zeros.sort()
                    non_zero_columns[name] = (i, non_zeros)

                    # Compress values.
                    tables[name] = vals[:, non_zeros]
                    i += 1

            # Check if the remaining rows are nonzero in the same positions, else expand.
            else:
                for j in range(1, numpy.shape(vals)[0]):
                    # All rows must have the same non-zero columns
                    # for the optimization to work (at this stage).
                    new_non_zeros = list(vals[j].nonzero()[0])
                    if non_zeros != new_non_zeros:
                        non_zeros = non_zeros + [
                            c for c in new_non_zeros if not c in non_zeros
                        ]
                        # If this results in all columns being non-zero, continue.
                        if len(non_zeros) == numpy.shape(vals)[1]:
                            continue

                # Only add nonzeros if it results in a reduction of columns.
                if len(non_zeros) != numpy.shape(vals)[1]:
                    if list(non_zeros):
                        non_zeros.sort()
                        non_zero_columns[name] = (i, non_zeros)

                        # Compress values.
                        tables[name] = vals[:, non_zeros]
                        i += 1

    # Check if we have some zeros in the tables.
    names_zeros = contains_zeros(tables)

    # Get names of tables with all ones.
    names_ones = get_ones(tables)

    # Add non-zero column, zero and ones info to inverse_name_map
    # (so we only need to pass around one name_map to code generating functions).
    for name in inverse_name_map:
        if inverse_name_map[name] in non_zero_columns:
            nzc = non_zero_columns[inverse_name_map[name]]
            zero = inverse_name_map[name] in names_zeros
            ones = inverse_name_map[name] in names_ones
            inverse_name_map[name] = [inverse_name_map[name], nzc, zero, ones]
        else:
            zero = inverse_name_map[name] in names_zeros
            ones = inverse_name_map[name] in names_ones
            inverse_name_map[name] = [inverse_name_map[name], (), zero, ones]

    # If we found non zero columns we might be able to reduce number of tables further.
    if non_zero_columns:
        # Try reducing the tables. This is possible if some tables have become
        # identical as a consequence of compressing the tables.
        # This happens with e.g., gradients of linear basis
        # FE0 = {-1,0,1}, nzc0 = [0,2]
        # FE1 = {-1,1,0}, nzc1 = [0,1]  -> FE0 = {-1,1}, nzc0 = [0,2], nzc1 = [0,1].

        # Call old utility function again.
        nm, inv_nm = unique_tables(tables)

        # Update name maps.
        for name in inverse_name_map:
            if inverse_name_map[name][0] in inv_nm:
                inverse_name_map[name][0] = inv_nm[inverse_name_map[name][0]]
        for name in nm:
            maps = nm[name]
            for m in maps:
                if not name in name_map:
                    name_map[name] = []
                if m in name_map:
                    name_map[name] += name_map[m] + [m]
                    del name_map[m]
                else:
                    name_map[name].append(m)

        # Get new names of tables with all ones (for vector constants).
        names = get_ones(tables)

        # Because these tables now contain ones as a consequence of compression
        # we still need to consider the non-zero columns when looking up values
        # in coefficient arrays. The psi entries can however we neglected and we
        # don't need to tabulate the values (if option is set).
        for name in names:
            if name in name_map:
                maps = name_map[name]
                for m in maps:
                    inverse_name_map[m][3] = True
            if name in inverse_name_map:
                inverse_name_map[name][3] = True

    # Write protect info and return values
    for name in inverse_name_map:
        inverse_name_map[name] = tuple(inverse_name_map[name])

    # Note: inverse_name_map here is called name_map in create_psi_tables and the quadraturetransformerbase class
    return (inverse_name_map, tables)
Пример #26
0
def jit_form(form, parameters=None):
    "Just-in-time compile the given form."
    from ffc.backends.ufc import build_ufc_module

    # Check that we get a Form
    if not isinstance(form, Form):
        error("Unable to convert object to a UFL form: %s" % repr(form))

    # Check parameters
    parameters = _check_parameters(form, parameters)

    # Set log level
    set_level(parameters["log_level"])
    set_prefix(parameters["log_prefix"])

    # Wrap input
    jit_object = JITObject(form, parameters)

    # Set prefix for generated code
    module_name = "ffc_form_" + jit_object.signature()

    # Use Instant cache if possible
    cache_dir = parameters["cache_dir"] or None
    module = instant.import_module(module_name, cache_dir=cache_dir)
    if module:
        debug("Reusing form from cache.")
    else:
        # Take lock to serialise file removal.
        # Need to add "_0" to lock as instant.import_module acquire
        # lock with name: module_name
        with instant.file_lock(instant.get_default_cache_dir(),
                               module_name + "_0") as lock:

            # Retry Instant cache. The module may have been created while we waited
            # for the lock, even if it didn't exist before.
            module = instant.import_module(module_name, cache_dir=cache_dir)
            if module:
                debug("Reusing form from cache.")
            else:
                # Write a message
                log(INFO + 5,
                    "Calling FFC just-in-time (JIT) compiler, this may take some time.")

                # Generate code
                compile_form(form,
                             prefix=module_name,
                             parameters=parameters)

                # Build module using Instant (through UFC)
                debug("Compiling and linking Python extension module, this may take some time.")
                hfile   = module_name + ".h"
                cppfile = module_name + ".cpp"

                if parameters["cpp_optimize"]:
                    cppargs = parameters["cpp_optimize_flags"].split()
                else:
                    cppargs = ["-O0"]

                module = build_ufc_module(
                    hfile,
                    source_directory = os.curdir,
                    signature = module_name,
                    sources = [cppfile] if parameters["split"] else [],
                    cppargs = cppargs,
                    cache_dir = cache_dir)

                # Remove code
                if os.path.isfile(hfile):
                    os.unlink(hfile)
                if parameters["split"] :
                    if os.path.isfile(cppfile):
                        os.unlink(cppfile)

    # Construct instance of compiled form
    check_swig_version(module)
    prefix = module_name
    compiled_form = _instantiate_form(module, prefix)
    return compiled_form, module, prefix
Пример #27
0
def remove_unused(code, used_set=set()):
    """
    Remove unused variables from a given C++ code. This is useful when
    generating code that will be compiled with gcc and parameters -Wall
    -Werror, in which case gcc returns an error when seeing a variable
    declaration for a variable that is never used.

    Optionally, a set may be specified to indicate a set of variables
    names that are known to be used a priori.
    """

    # Dictionary of (declaration_line, used_lines) for variables
    variables = {}

    # List of variable names (so we can search them in order)
    variable_names = []

    lines = code.split("\n")
    for (line_number, line) in enumerate(lines):
        # Exclude commented lines.
        if line[:2] == "//" or line[:3] == "///":
            continue

        # Split words
        words = [word for word in line.split(" ") if word != ""]

        # Remember line where variable is declared
        for type in [
                type for type in types if " ".join(type) in " ".join(words)
        ]:  # Fewer matches than line below.
            # for type in [type for type in types if len(words) > len(type)]:
            variable_type = words[0:len(type)]
            variable_name = words[len(type)]

            # Skip special characters
            if variable_name in special_characters:
                continue

            # Test if any of the special characters are present in the variable name
            # If this is the case, then remove these by assuming that the 'real' name
            # is the first entry in the return list. This is implemented to prevent
            # removal of e.g. 'double array[6]' if it is later used in a loop as 'array[i]'
            if variable_type == type:

                # Create correct variable name (e.g. y instead of
                # y[2]) for variables with separators
                seps_present = [
                    sep for sep in special_characters if sep in variable_name
                ]
                if seps_present:
                    variable_name = sorted(
                        [variable_name.split(sep)[0] for sep in seps_present])
                    variable_name = variable_name[0]

                variables[variable_name] = (line_number, [])
                if variable_name not in variable_names:
                    variable_names += [variable_name]

        # Mark line for used variables
        for variable_name in variables:
            (declaration_line, used_lines) = variables[variable_name]
            if _variable_in_line(variable_name,
                                 line) and line_number > declaration_line:
                variables[variable_name] = (declaration_line,
                                            used_lines + [line_number])

    # Reverse the order of the variable names to catch variables used
    # only by variables that are removed
    variable_names.reverse()

    # Remove declarations that are not used
    removed_lines = []
    for variable_name in variable_names:
        (declaration_line, used_lines) = variables[variable_name]
        for line in removed_lines:
            if line in used_lines:
                used_lines.remove(line)
        if not used_lines and variable_name not in used_set:
            debug("Removing unused variable: %s" % variable_name)
            lines[
                declaration_line] = None  # KBO: Need to completely remove line for evaluate_basis* to work
            # lines[declaration_line] = "// " + lines[declaration_line]
            removed_lines += [declaration_line]
    return "\n".join([line for line in lines if line is not None])
Пример #28
0
def remove_unused(code, used_set=set()):
    """
    Remove unused variables from a given C++ code. This is useful when
    generating code that will be compiled with gcc and parameters -Wall
    -Werror, in which case gcc returns an error when seeing a variable
    declaration for a variable that is never used.

    Optionally, a set may be specified to indicate a set of variables
    names that are known to be used a priori.
    """

    # Dictionary of (declaration_line, used_lines) for variables
    variables = {}

    # List of variable names (so we can search them in order)
    variable_names = []

    lines = code.split("\n")
    for (line_number, line) in enumerate(lines):
        # Exclude commented lines.
        if line[:2] == "//" or line[:3] == "///":
            continue

        # Split words
        words = [word for word in line.split(" ") if not word == ""]

        # Remember line where variable is declared
        for type in [type for type in types if " ".join(type) in " ".join(words)]: # Fewer matches than line below.
        # for type in [type for type in types if len(words) > len(type)]:
            variable_type = words[0:len(type)]
            variable_name = words[len(type)]

            # Skip special characters
            if variable_name in special_characters:
                continue

            # Test if any of the special characters are present in the variable name
            # If this is the case, then remove these by assuming that the 'real' name
            # is the first entry in the return list. This is implemented to prevent
            # removal of e.g. 'double array[6]' if it is later used in a loop as 'array[i]'
            if variable_type == type:

                # Create correct variable name (e.g. y instead of
                # y[2]) for variables with separators
                seps_present = [sep for sep in special_characters if sep in variable_name]
                if seps_present:
                    variable_name = [variable_name.split(sep)[0] for sep in seps_present]
                    variable_name.sort()
                    variable_name = variable_name[0]

                variables[variable_name] = (line_number, [])
                if not variable_name in variable_names:
                    variable_names += [variable_name]

        # Mark line for used variables
        for variable_name in variables:
            (declaration_line, used_lines) = variables[variable_name]
            if _variable_in_line(variable_name, line) and line_number > declaration_line:
                variables[variable_name] = (declaration_line, used_lines + [line_number])

    # Reverse the order of the variable names to catch variables used
    # only by variables that are removed
    variable_names.reverse()

    # Remove declarations that are not used
    removed_lines = []
    for variable_name in variable_names:
        (declaration_line, used_lines) = variables[variable_name]
        for line in removed_lines:
            if line in used_lines:
                used_lines.remove(line)
        if not used_lines and not variable_name in used_set:
            debug("Removing unused variable: %s" % variable_name)
            lines[declaration_line] = None # KBO: Need to completely remove line for evaluate_basis* to work
            # lines[declaration_line] = "// " + lines[declaration_line]
            removed_lines += [declaration_line]
    return "\n".join([line for line in lines if not line is None])