Source code for ufl.algorithms.formfiles

# -*- coding: utf-8 -*-
"""A collection of utility algorithms for handling UFL files."""

# Copyright (C) 2008-2016 Martin Sandve Alnæs
#
# This file is part of UFL (https://www.fenicsproject.org)
#
# SPDX-License-Identifier:    LGPL-3.0-or-later
#
# Modified by Anders Logg, 2008-2009.
# Modified by Marie E. Rognes, 2011.

import io
import os
import re
from ufl.log import error, warning
from ufl.utils.sorting import sorted_by_key
from ufl.form import Form
from ufl.finiteelement import FiniteElementBase
from ufl.core.expr import Expr
from ufl.constant import Constant
from ufl.argument import Argument
from ufl.coefficient import Coefficient


[docs]class FileData(object): def __init__(self): self.elements = [] self.coefficients = [] self.expressions = [] self.forms = [] self.object_names = {} self.object_by_name = {} self.reserved_objects = {} def __bool__(self): return bool(self.elements or self.coefficients or self.forms or self.expressions or self.object_names or self.object_by_name or self.reserved_objects) __nonzero__ = __bool__
[docs]def read_lines_decoded(fn): r = re.compile(b".*coding: *([^ ]+)") def match(line): return r.match(line, re.ASCII) # First read lines as bytes with io.open(fn, "rb") as f: lines = f.readlines() # Check for coding: in the first two lines for i in range(min(2, len(lines))): m = match(lines[i]) if m: encoding, = m.groups() # Drop encoding line lines = lines[:i] + lines[i + 1:] break else: # Default to utf-8 (works for ascii files # as well, default for python files in py3) encoding = "utf-8" # Decode all lines lines = [line.decode(encoding=encoding) for line in lines] return lines
[docs]def read_ufl_file(filename): "Read a UFL file." if not os.path.exists(filename): error("File '%s' doesn't exist." % filename) lines = read_lines_decoded(filename) code = "".join(lines) return code
[docs]def execute_ufl_code(uflcode): # Execute code namespace = {} exec(uflcode, namespace) return namespace
[docs]def interpret_ufl_namespace(namespace): "Takes a namespace dict from an executed ufl file and converts it to a FileData object." # Object to hold all returned data ufd = FileData() # Extract object names for Form, Coefficient and FiniteElementBase objects # The use of id(obj) as key in object_names is necessary # because we need to distinguish between instances, # and not just between objects with different values. for name, value in sorted_by_key(namespace): # Store objects by reserved name OR instance id reserved_names = ("unknown",) # Currently only one reserved name if name in reserved_names: # Store objects with reserved names ufd.reserved_objects[name] = value # FIXME: Remove after FFC is updated to use reserved_objects: ufd.object_names[name] = value ufd.object_by_name[name] = value elif isinstance(value, (FiniteElementBase, Coefficient, Constant, Argument, Form, Expr)): # Store instance <-> name mappings for important objects # without a reserved name ufd.object_names[id(value)] = name ufd.object_by_name[name] = value # Get list of exported forms forms = namespace.get("forms") if forms is None: # Get forms from object_by_name, which has already mapped # tuple->Form where needed def get_form(name): form = ufd.object_by_name.get(name) return form if isinstance(form, Form) else None a_form = get_form("a") L_form = get_form("L") M_form = get_form("M") forms = [a_form, L_form, M_form] # Add forms F and J if not "a" and "L" are used if a_form is None or L_form is None: F_form = get_form("F") J_form = get_form("J") forms += [F_form, J_form] # Remove Nones forms = [f for f in forms if isinstance(f, Form)] ufd.forms = forms # Validate types if not isinstance(ufd.forms, (list, tuple)): error("Expecting 'forms' to be a list or tuple, not '%s'." % type(ufd.forms)) if not all(isinstance(a, Form) for a in ufd.forms): error("Expecting 'forms' to be a list of Form instances.") # Get list of exported elements elements = namespace.get("elements") if elements is None: elements = [ufd.object_by_name.get(name) for name in ("element",)] elements = [e for e in elements if e is not None] ufd.elements = elements # Validate types if not isinstance(ufd.elements, (list, tuple)): error("Expecting 'elements' to be a list or tuple, not '%s'." % type(ufd.elements)) if not all(isinstance(e, FiniteElementBase) for e in ufd.elements): error("Expecting 'elements' to be a list of FiniteElementBase instances.") # Get list of exported coefficients # TODO: Temporarily letting 'coefficients' override 'functions', # but allow 'functions' for compatibility functions = namespace.get("functions", []) if functions: warning("Deprecation warning: Rename 'functions' to 'coefficients' to export coefficients.") ufd.coefficients = namespace.get("coefficients", functions) # Validate types if not isinstance(ufd.coefficients, (list, tuple)): error("Expecting 'coefficients' to be a list or tuple, not '%s'." % type(ufd.coefficients)) if not all(isinstance(e, Coefficient) for e in ufd.coefficients): error("Expecting 'coefficients' to be a list of Coefficient instances.") # Get list of exported expressions ufd.expressions = namespace.get("expressions", []) # Validate types if not isinstance(ufd.expressions, (list, tuple)): error("Expecting 'expressions' to be a list or tuple, not '%s'." % type(ufd.expressions)) if not all(isinstance(e[0], Expr) for e in ufd.expressions): error("Expecting 'expressions' to be a list of Expr instances.") # Return file data return ufd
[docs]def load_ufl_file(filename): "Load a UFL file with elements, coefficients, expressions and forms." # Read code from file and execute it uflcode = read_ufl_file(filename) namespace = execute_ufl_code(uflcode) return interpret_ufl_namespace(namespace)
[docs]def load_forms(filename): "Return a list of all forms in a file." ufd = load_ufl_file(filename) return ufd.forms