def _encapsulate(prefix, object_names, analysis, parameters): # Extract data from analysis form_datas, elements, element_map, domains = analysis # FIXME: Encapsulate domains? num_form_datas = len(form_datas) common_space = False # Special case: single element if num_form_datas == 0: capsules = _encapsule_element(prefix, elements) # Special case: with error control elif parameters["error_control"] and num_form_datas == 11: capsules = [_encapsule_form(prefix, object_names, form_data, i, element_map) for (i, form_data) in enumerate(form_datas[:num_form_datas-1])] capsules += [_encapsule_form(prefix, object_names, form_datas[-1], num_form_datas-1, element_map, "GoalFunctional")] # Otherwise: generate standard capsules for each form else: capsules = [_encapsule_form(prefix, object_names, form_data, i, element_map) for (i, form_data) in enumerate(form_datas)] # Check if all argument elements are equal elements = [] for form_data in form_datas: elements += form_data.argument_elements common_space = all_equal(elements) return (capsules, common_space)
def _encapsulate(prefix, object_names, analysis, parameters): # Extract data from analysis form_datas, elements, element_map, domains = analysis # FIXME: Encapsulate domains? num_form_datas = len(form_datas) common_space = False # Special case: single element if num_form_datas == 0: capsules = _encapsule_element(prefix, elements) # Special case: with error control elif parameters["error_control"] and num_form_datas == 11: capsules = [_encapsule_form(prefix, object_names, form_data, i, element_map) for (i, form_data) in enumerate(form_datas[:num_form_datas - 1])] capsules += [_encapsule_form(prefix, object_names, form_datas[-1], num_form_datas - 1, element_map, "GoalFunctional")] # Otherwise: generate standard capsules for each form else: capsules = [_encapsule_form(prefix, object_names, form_data, i, element_map) for (i, form_data) in enumerate(form_datas)] # Check if all argument elements are equal elements = [] for form_data in form_datas: elements += form_data.argument_elements common_space = all_equal(elements) return (capsules, common_space)
def _validate_quadrature_schemes_of_elements(quad_schemes, elements): # Update scheme for QuadratureElements if quad_schemes and all_equal(quad_schemes): scheme = quad_schemes[0] else: scheme = "canonical" info("Quadrature rule must be equal within each sub domain, using %s rule." % scheme) for element in elements: if element.family() == "Quadrature": qs = element.quadrature_scheme() if qs != scheme: error("Quadrature element must have specified quadrature scheme (%s) equal to the integral (%s)." % (qs, scheme))
def _extract_common_quadrature_rule(integral_metadatas): # Check that quadrature rule is the same (To support mixed rules # would be some work since num_points is used to identify # quadrature rules in large parts of the pipeline) quadrature_rules = [md["quadrature_rule"] for md in integral_metadatas] if all_equal(quadrature_rules): qr = quadrature_rules[0] else: qr = "canonical" # FIXME: Shouldn't we raise here? info("Quadrature rule must be equal within each sub domain, using %s rule." % qr) return qr
def _extract_common_quadrature_rule(integral_metadatas): # Check that quadrature rule is the same (To support mixed rules # would be some work since num_points is used to identify # quadrature rules in large parts of the pipeline) quadrature_rules = [md["quadrature_rule"] for md in integral_metadatas] if all_equal(quadrature_rules): qr = quadrature_rules[0] else: qr = "canonical" # FIXME: Shouldn't we raise here? info( "Quadrature rule must be equal within each sub domain, using %s rule." % qr) return qr
def _extract_common_quadrature_degree(integral_metadatas): # Check that quadrature degree is the same quadrature_degrees = [md["quadrature_degree"] for md in integral_metadatas] for d in quadrature_degrees: if not isinstance(d, int): error("Invalid non-integer quadrature degree %s" % (str(d),)) qd = max(quadrature_degrees) if not all_equal(quadrature_degrees): # FIXME: Shouldn't we raise here? # TODO: This may be loosened up without too much effort, # if the form compiler handles mixed integration degree, # something that most of the pipeline seems to be ready for. info("Quadrature degree must be equal within each sub domain, using degree %d." % qd) return qd
def _validate_quadrature_schemes_of_elements(quad_schemes, elements): # Update scheme for QuadratureElements if quad_schemes and all_equal(quad_schemes): scheme = quad_schemes[0] else: scheme = "canonical" info( "Quadrature rule must be equal within each sub domain, using %s rule." % scheme) for element in elements: if element.family() == "Quadrature": qs = element.quadrature_scheme() if qs != scheme: error( "Quadrature element must have specified quadrature scheme (%s) equal to the integral (%s)." % (qs, scheme))
def _extract_common_quadrature_degree(integral_metadatas): # Check that quadrature degree is the same quadrature_degrees = [md["quadrature_degree"] for md in integral_metadatas] for d in quadrature_degrees: if not isinstance(d, int): error("Invalid non-integer quadrature degree %s" % (str(d), )) qd = max(quadrature_degrees) if not all_equal(quadrature_degrees): # FIXME: Shouldn't we raise here? # TODO: This may be loosened up without too much effort, # if the form compiler handles mixed integration degree, # something that most of the pipeline seems to be ready for. info( "Quadrature degree must be equal within each sub domain, using degree %d." % qd) return qd
def _attach_integral_metadata(form_data, parameters): "Attach integral metadata" # Recognized metadata keys metadata_keys = ("representation", "quadrature_degree", "quadrature_rule") # Iterate over integral collections quad_schemes = [] for ida in form_data.integral_data: # TODO: Is it possible to detach this from IntegralData? It's a bit strange from the ufl side. common_metadata = ida.metadata # Iterate over integrals integral_metadatas = [] for integral in ida.integrals: # Fill in integral metadata with default values # NB! This modifies the metadata of the input integral data! integral_metadata = integral.metadata() or {} for key in metadata_keys: if key not in integral_metadata: integral_metadata[key] = parameters[key] # Automatic selection of representation r = integral_metadata["representation"] # Hack to override representation with environment variable forced_r = os.environ.get("FFC_FORCE_REPRESENTATION") if forced_r: r = forced_r warning("representation: forced by $FFC_FORCE_REPRESENTATION to '%s'" % r) elif r == "auto": r = _auto_select_representation(integral, form_data.unique_sub_elements, form_data.function_replace_map) info("representation: auto --> %s" % r) elif r in ("quadrature", "tensor", "uflacs"): info("representation: %s" % r) else: info("Valid choices are 'tensor', 'quadrature', 'uflacs', or 'auto'.") error("Illegal choice of representation for integral: " + str(r)) integral_metadata["representation"] = r # Automatic selection of quadrature degree qd = integral_metadata["quadrature_degree"] # Special case: handling -1 as "auto" for quadrature_degree if qd in ("auto", -1): qd = _auto_select_quadrature_degree(integral.integrand(), r, form_data.unique_sub_elements, form_data.element_replace_map) info("quadrature_degree: auto --> " + str(qd)) else: if isinstance(qd, tuple): qd = tuple(int(q) for q in qd) else: qd = int(qd) info("quadrature_degree: " + str(qd)) # Validate degree if not qd >= 0: info("Valid choices are nonnegative integers or 'auto'.") error("Illegal quadrature degree for integral: " + str(qd)) tdim = integral.ufl_domain().topological_dimension() _check_quadrature_degree(qd, tdim) integral_metadata["quadrature_degree"] = qd assert isinstance(qd, (int, tuple)) # Automatic selection of quadrature rule qr = integral_metadata["quadrature_rule"] if qr == "auto": # Just use default for now. qr = "default" info("quadrature_rule: auto --> %s" % qr) elif qr in ("default", "canonical", "vertex"): info("quadrature_rule: %s" % qr) else: info("Valid choices are 'default', 'canonical', 'vertex', and 'auto'.") error("Illegal choice of quadrature rule for integral: " + str(qr)) integral_metadata["quadrature_rule"] = qr quad_schemes.append(qr) # Append to list of metadata integral_metadatas.append(integral_metadata) # Extract common metadata for integral collection if len(ida.integrals) == 1: common_metadata.update(integral_metadatas[0]) else: # Check that representation is the same # (Generating code with different representations within a # single tabulate_tensor is considered not worth the effort) representations = [md["representation"] for md in integral_metadatas] if all_equal(representations): r = representations[0] else: r = "quadrature" info("Integral representation must be equal within each sub domain, using %s representation." % r) # Check that quadrature degree is the same # FIXME: Why must the degree within a sub domain be the same? # This makes no sense considering that num_points is # used as a key all over in quadrature representation... quadrature_degrees = [md["quadrature_degree"] for md in integral_metadatas] if all_equal(quadrature_degrees): qd = quadrature_degrees[0] else: if isinstance(quadrature_degrees[0], tuple): qd = tuple(map(max, zip(*quadrature_degrees))) else: qd = max(quadrature_degrees) info("Quadrature degree must be equal within each sub domain, using degree " + str(qd)) assert isinstance(qd, (int, tuple)) # Check that quadrature rule is the same # FIXME: Why must the rule within a sub domain be the same? # To support this would be more work since num_points is used # to identify quadrature rules in the quadrature representation. quadrature_rules = [md["quadrature_rule"] for md in integral_metadatas] if all_equal(quadrature_rules): qr = quadrature_rules[0] else: qr = "canonical" info("Quadrature rule must be equal within each sub domain, using %s rule." % qr) # Update common metadata assert isinstance(qd, (int, tuple)) common_metadata["representation"] = r common_metadata["quadrature_degree"] = qd common_metadata["quadrature_rule"] = qr # Update scheme for QuadratureElements if quad_schemes and all_equal(quad_schemes): scheme = quad_schemes[0] else: scheme = "canonical" info("Quadrature rule must be equal within each sub domain, using %s rule." % scheme) # FIXME: This modifies the elements depending on the form compiler parameters, # this is a serious breach of the immutability of ufl objects, since the # element quad scheme is part of the signature and hash of the element... for element in form_data.unique_sub_elements: if element.family() == "Quadrature": element._quad_scheme = scheme
def __init__(self, monomial): "Create transformed monomial from given monomial." # Reset monomial data self.float_value = monomial.float_value self.determinants = [] self.coefficients = [] self.transforms = [] self.arguments = [] # Reset index counters _reset_indices() # Initialize index map index_map = {} # Iterate over factors for f in monomial.factors: # Create FIAT element ufl_element = f.element() fiat_element = create_element(ufl_element) # Note nifty aspect here: when gdim != tdim, it might be # (in particular, is for H(div)/H(curl), that the value # dimension is different for the physical and reference # elements. # Get number of components # FIXME: Can't handle tensor-valued elements: vdim = shape[0] shape = ufl_element.value_shape() assert(len(shape) <= 1), \ "MonomialTransformation does not handle tensor-valued elements" if len(shape) == 0: vdim = 1 else: vdim = shape[0] # Extract dimensions sdim = fiat_element.space_dimension() domain, = ufl_element.domains() # Assuming single domain gdim = domain.geometric_dimension() tdim = domain.topological_dimension() # Extract basis function index and coefficients if isinstance(f.function, Argument): vindex = MonomialIndex(index_type=MonomialIndex.PRIMARY, index_range=list(range(sdim)), index_id=f.index()) elif isinstance(f.function, Coefficient): vindex = MonomialIndex(index_range=list(range(sdim))) coefficient = MonomialCoefficient(vindex, f.index()) self.coefficients.append(coefficient) # Extract components components = self._extract_components(f, index_map, vdim) if len(components) > 1: raise MonomialException( "Can only handle rank 0 or rank 1 tensors.") # Handle non-affine mappings (Piola) if len(components) > 0: # We can only handle rank 1 elements for now component = components[0] # Get mapping (all need to be equal) mappings = [] for i in component.index_range: (offset, ufl_sub_element) = ufl_element.extract_component(i) fiat_sub_element = create_element(ufl_sub_element) mappings.extend(fiat_sub_element.mapping()) if not all_equal(mappings): raise MonomialException("Mappings differ: " + str(mappings)) mapping = mappings[0] # Get component index relative to its sub element and its sub element (component_index, sub_element) = ufl_element.extract_component( component.index_range[0]) # Get offset if len(component_index) == 0: offset = 0 else: offset = component.index_range[0] - component_index[0] # MER: Need to handle mappings in special ways if gdim # != tdim and some Piolas are present. This could # probably be merged with the offset code above, but I # was not able to wrap my head around the offsets # always referring to component.index_range[0]. if (gdim != tdim): assert len(component.index_range) == 1, \ "Component transform not implemented for this case. Please request this feature." component, offset = transform_component( component.index_range[0], offset, ufl_element) component = MonomialIndex(index_type=MonomialIndex.FIXED, index_range=[component], index_id=None) components = [ component, ] # Add transforms where appropriate if mapping == "contravariant piola": # phi(x) = (det J)^{-1} J Phi(X) index0 = component index1 = MonomialIndex( index_range=list(range(tdim))) + offset transform = MonomialTransform(index0, index1, MonomialTransform.J, f.restriction, offset) self.transforms.append(transform) determinant = MonomialDeterminant( power=-1, restriction=f.restriction) self.determinants.append(determinant) components[0] = index1 elif mapping == "covariant piola": # phi(x) = J^{-T} Phi(X) index0 = MonomialIndex( index_range=list(range(tdim))) + offset index1 = component transform = MonomialTransform(index0, index1, MonomialTransform.JINV, f.restriction, offset) self.transforms.append(transform) components[0] = index0 # Extract derivatives / transforms derivatives = [] for d in f.derivatives: index0 = MonomialIndex(index_range=list(range(tdim))) if d in index_map: index1 = index_map[d] elif isinstance(d, FixedIndex): index1 = MonomialIndex(index_type=MonomialIndex.FIXED, index_range=[int(d)], index_id=int(d)) else: index1 = MonomialIndex(index_range=list(range(gdim))) index_map[d] = index1 transform = MonomialTransform(index0, index1, MonomialTransform.JINV, f.restriction, 0) self.transforms.append(transform) derivatives.append(index0) # Extract restriction restriction = f.restriction # Create basis function v = MonomialArgument(ufl_element, vindex, components, derivatives, restriction) self.arguments.append(v) # Figure out secondary and auxiliary indices internal_indices = self._extract_internal_indices(None) external_indices = self._extract_external_indices(None) for i in internal_indices + external_indices: # Skip already visited indices if not i.index_type is None: continue # Set index type and id num_internal = len([j for j in internal_indices if j == i]) num_external = len([j for j in external_indices if j == i]) if num_internal == 1 and num_external == 1: i.index_type = MonomialIndex.SECONDARY i.index_id = _next_secondary_index() elif num_internal == 2 and num_external == 0: i.index_type = MonomialIndex.INTERNAL i.index_id = _next_internal_index() elif num_internal == 0 and num_external == 2: i.index_type = MonomialIndex.EXTERNAL i.index_id = _next_external_index() else: raise Exception( "Summation index does not appear exactly twice: %s" % str(i))
def __init__(self, monomial): "Create transformed monomial from given monomial." # Reset monomial data self.float_value = monomial.float_value self.determinants = [] self.coefficients = [] self.transforms = [] self.arguments = [] # Reset index counters _reset_indices() # Initialize index map index_map = {} # Iterate over factors for f in monomial.factors: # Create FIAT element ufl_element = f.element() fiat_element = create_element(ufl_element) # Note nifty aspect here: when gdim != tdim, it might be # (in particular, is for H(div)/H(curl), that the value # dimension is different for the physical and reference # elements. # Get number of components # FIXME: Can't handle tensor-valued elements: vdim = shape[0] shape = ufl_element.value_shape() assert(len(shape) <= 1), \ "MonomialTransformation does not handle tensor-valued elements" if len(shape) == 0: vdim = 1 else: vdim = shape[0] # Extract dimensions sdim = fiat_element.space_dimension() cell = ufl_element.cell() gdim = cell.geometric_dimension() tdim = cell.topological_dimension() # Extract basis function index and coefficients if isinstance(f.function, Argument): vindex = MonomialIndex(index_type=MonomialIndex.PRIMARY, index_range=list(range(sdim)), index_id=f.index()) elif isinstance(f.function, Coefficient): vindex = MonomialIndex(index_range=list(range(sdim))) coefficient = MonomialCoefficient(vindex, f.index()) self.coefficients.append(coefficient) # Extract components components = self._extract_components(f, index_map, vdim) if len(components) > 1: raise MonomialException("Can only handle rank 0 or rank 1 tensors.") # Handle non-affine mappings (Piola) if len(components) > 0: # We can only handle rank 1 elements for now component = components[0] # Get mapping (all need to be equal) mappings = [] for i in component.index_range: (offset, ufl_sub_element) = ufl_element.extract_component(i) fiat_sub_element = create_element(ufl_sub_element) mappings.extend(fiat_sub_element.mapping()) if not all_equal(mappings): raise MonomialException("Mappings differ: " + str(mappings)) mapping = mappings[0] # Get component index relative to its sub element and its sub element (component_index, sub_element) = ufl_element.extract_component(component.index_range[0]) # Get offset if len(component_index) == 0: offset = 0 else: offset = component.index_range[0] - component_index[0] # MER: Need to handle mappings in special ways if gdim # != tdim and some Piolas are present. This could # probably be merged with the offset code above, but I # was not able to wrap my head around the offsets # always referring to component.index_range[0]. if (gdim != tdim): assert len(component.index_range) == 1, \ "Component transform not implemented for this case. Please request this feature." component, offset = transform_component(component.index_range[0], offset, ufl_element) component = MonomialIndex(index_type=MonomialIndex.FIXED, index_range=[component], index_id=None) components = [component, ] # Add transforms where appropriate if mapping == "contravariant piola": # phi(x) = (det J)^{-1} J Phi(X) index0 = component index1 = MonomialIndex(index_range=list(range(tdim))) + offset transform = MonomialTransform(index0, index1, MonomialTransform.J, f.restriction, offset) self.transforms.append(transform) determinant = MonomialDeterminant(power=-1, restriction=f.restriction) self.determinants.append(determinant) components[0] = index1 elif mapping == "covariant piola": # phi(x) = J^{-T} Phi(X) index0 = MonomialIndex(index_range=list(range(tdim))) + offset index1 = component transform = MonomialTransform(index0, index1, MonomialTransform.JINV, f.restriction, offset) self.transforms.append(transform) components[0] = index0 # Extract derivatives / transforms derivatives = [] for d in f.derivatives: index0 = MonomialIndex(index_range=list(range(tdim))) if d in index_map: index1 = index_map[d] elif isinstance(d, FixedIndex): index1 = MonomialIndex(index_type=MonomialIndex.FIXED, index_range=[int(d)], index_id=int(d)) else: index1 = MonomialIndex(index_range=list(range(gdim))) index_map[d] = index1 transform = MonomialTransform(index0, index1, MonomialTransform.JINV, f.restriction, 0) self.transforms.append(transform) derivatives.append(index0) # Extract restriction restriction = f.restriction # Create basis function v = MonomialArgument(ufl_element, vindex, components, derivatives, restriction) self.arguments.append(v) # Figure out secondary and auxiliary indices internal_indices = self._extract_internal_indices(None) external_indices = self._extract_external_indices(None) for i in internal_indices + external_indices: # Skip already visited indices if not i.index_type is None: continue # Set index type and id num_internal = len([j for j in internal_indices if j == i]) num_external = len([j for j in external_indices if j == i]) if num_internal == 1 and num_external == 1: i.index_type = MonomialIndex.SECONDARY i.index_id = _next_secondary_index() elif num_internal == 2 and num_external == 0: i.index_type = MonomialIndex.INTERNAL i.index_id = _next_internal_index() elif num_internal == 0 and num_external == 2: i.index_type = MonomialIndex.EXTERNAL i.index_id = _next_external_index() else: raise Exception("Summation index does not appear exactly twice: %s" % str(i))
def _attach_integral_metadata(form_data, parameters): "Attach integral metadata" # Recognized metadata keys metadata_keys = ("representation", "quadrature_degree", "quadrature_rule") # Iterate over integral collections quad_schemes = [] for ida in form_data.integral_data: common_metadata = ida.metadata # TODO: Is it possible to detach this from IntegralData? It's a bit strange from the ufl side. # Iterate over integrals integral_metadatas = [] for integral in ida.integrals: # Get metadata for integral integral_metadata = integral.compiler_data() or {} for key in metadata_keys: if not key in integral_metadata: integral_metadata[key] = parameters[key] # Special case: handling -1 as "auto" for quadrature_degree if integral_metadata["quadrature_degree"] == -1: integral_metadata["quadrature_degree"] = "auto" # Check metadata r = integral_metadata["representation"] qd = integral_metadata["quadrature_degree"] qr = integral_metadata["quadrature_rule"] if not r in ("quadrature", "tensor", "uflacs", "auto"): info("Valid choices are 'tensor', 'quadrature', 'uflacs', or 'auto'.") error("Illegal choice of representation for integral: " + str(r)) if not qd == "auto": qd = int(qd) if not qd >= 0: info("Valid choices are nonnegative integers or 'auto'.") error("Illegal quadrature degree for integral: " + str(qd)) integral_metadata["quadrature_degree"] = qd if not qr in ("default", "canonical", "vertex", "auto"): info("Valid choices are 'default', 'canonical', 'vertex', and 'auto'.") error("Illegal choice of quadrature rule for integral: " + str(qr)) # Automatic selection of representation if r == "auto": # TODO: This doesn't really need the measure except for code redesign # reasons, pass integrand instead to reduce dependencies. # Not sure if function_replace_map is really needed either, # just passing it to be on the safe side. r = _auto_select_representation(integral, form_data.unique_sub_elements, form_data.function_replace_map) info("representation: auto --> %s" % r) integral_metadata["representation"] = r else: info("representation: %s" % r) # Automatic selection of quadrature degree if qd == "auto": qd = _auto_select_quadrature_degree(integral.integrand(), r, form_data.unique_sub_elements, form_data.element_replace_map) info("quadrature_degree: auto --> %d" % qd) integral_metadata["quadrature_degree"] = qd else: info("quadrature_degree: %d" % qd) _check_quadrature_degree(qd, form_data.topological_dimension) # Automatic selection of quadrature rule if qr == "auto": # Just use default for now. qr = "default" info("quadrature_rule: auto --> %s" % qr) integral_metadata["quadrature_rule"] = qr else: info("quadrature_rule: %s" % qr) quad_schemes.append(qr) # Append to list of metadata integral_metadatas.append(integral_metadata) # Extract common metadata for integral collection if len(ida.integrals) == 1: common_metadata.update(integral_metadatas[0]) else: # Check that representation is the same # FIXME: Why must the representation within a sub domain be the same? representations = [md["representation"] for md in integral_metadatas] if not all_equal(representations): r = "quadrature" info("Integral representation must be equal within each sub domain, using %s representation." % r) else: r = representations[0] # Check that quadrature degree is the same # FIXME: Why must the degree within a sub domain be the same? quadrature_degrees = [md["quadrature_degree"] for md in integral_metadatas] if not all_equal(quadrature_degrees): qd = max(quadrature_degrees) info("Quadrature degree must be equal within each sub domain, using degree %d." % qd) else: qd = quadrature_degrees[0] # Check that quadrature rule is the same # FIXME: Why must the rule within a sub domain be the same? quadrature_rules = [md["quadrature_rule"] for md in integral_metadatas] if not all_equal(quadrature_rules): qr = "canonical" info("Quadrature rule must be equal within each sub domain, using %s rule." % qr) else: qr = quadrature_rules[0] # Update common metadata common_metadata["representation"] = r common_metadata["quadrature_degree"] = qd common_metadata["quadrature_rule"] = qr # Update scheme for QuadratureElements if not all_equal(quad_schemes): scheme = "canonical" info("Quadrature rule must be equal within each sub domain, using %s rule." % qr) else: scheme = quad_schemes[0] for element in form_data.sub_elements: if element.family() == "Quadrature": element._quad_scheme = scheme
def _attach_integral_metadata(form_data, parameters): "Attach integral metadata" # Recognized metadata keys metadata_keys = ("representation", "quadrature_degree", "quadrature_rule") # Iterate over integral collections quad_schemes = [] for ida in form_data.integral_data: common_metadata = ida.metadata # TODO: Is it possible to detach this from IntegralData? It's a bit strange from the ufl side. # Iterate over integrals integral_metadatas = [] for integral in ida.integrals: # Get metadata for integral integral_metadata = integral.compiler_data() or {} for key in metadata_keys: if not key in integral_metadata: integral_metadata[key] = parameters[key] # Special case: handling -1 as "auto" for quadrature_degree if integral_metadata["quadrature_degree"] == -1: integral_metadata["quadrature_degree"] = "auto" # Check metadata r = integral_metadata["representation"] qd = integral_metadata["quadrature_degree"] qr = integral_metadata["quadrature_rule"] if not r in ("quadrature", "tensor", "uflacs", "auto"): info( "Valid choices are 'tensor', 'quadrature', 'uflacs', or 'auto'." ) error("Illegal choice of representation for integral: " + str(r)) if not qd == "auto": qd = int(qd) if not qd >= 0: info("Valid choices are nonnegative integers or 'auto'.") error("Illegal quadrature degree for integral: " + str(qd)) integral_metadata["quadrature_degree"] = qd if not qr in ("default", "canonical", "vertex", "auto"): info( "Valid choices are 'default', 'canonical', 'vertex', and 'auto'." ) error("Illegal choice of quadrature rule for integral: " + str(qr)) # Automatic selection of representation if r == "auto": # TODO: This doesn't really need the measure except for code redesign # reasons, pass integrand instead to reduce dependencies. # Not sure if function_replace_map is really needed either, # just passing it to be on the safe side. r = _auto_select_representation(integral, form_data.unique_sub_elements, form_data.function_replace_map) info("representation: auto --> %s" % r) integral_metadata["representation"] = r else: info("representation: %s" % r) # Automatic selection of quadrature degree if qd == "auto": qd = _auto_select_quadrature_degree( integral.integrand(), r, form_data.unique_sub_elements, form_data.element_replace_map) info("quadrature_degree: auto --> %d" % qd) integral_metadata["quadrature_degree"] = qd else: info("quadrature_degree: %d" % qd) _check_quadrature_degree(qd, form_data.topological_dimension) # Automatic selection of quadrature rule if qr == "auto": # Just use default for now. qr = "default" info("quadrature_rule: auto --> %s" % qr) integral_metadata["quadrature_rule"] = qr else: info("quadrature_rule: %s" % qr) quad_schemes.append(qr) # Append to list of metadata integral_metadatas.append(integral_metadata) # Extract common metadata for integral collection if len(ida.integrals) == 1: common_metadata.update(integral_metadatas[0]) else: # Check that representation is the same # FIXME: Why must the representation within a sub domain be the same? representations = [ md["representation"] for md in integral_metadatas ] if not all_equal(representations): r = "quadrature" info( "Integral representation must be equal within each sub domain, using %s representation." % r) else: r = representations[0] # Check that quadrature degree is the same # FIXME: Why must the degree within a sub domain be the same? quadrature_degrees = [ md["quadrature_degree"] for md in integral_metadatas ] if not all_equal(quadrature_degrees): qd = max(quadrature_degrees) info( "Quadrature degree must be equal within each sub domain, using degree %d." % qd) else: qd = quadrature_degrees[0] # Check that quadrature rule is the same # FIXME: Why must the rule within a sub domain be the same? quadrature_rules = [ md["quadrature_rule"] for md in integral_metadatas ] if not all_equal(quadrature_rules): qr = "canonical" info( "Quadrature rule must be equal within each sub domain, using %s rule." % qr) else: qr = quadrature_rules[0] # Update common metadata common_metadata["representation"] = r common_metadata["quadrature_degree"] = qd common_metadata["quadrature_rule"] = qr # Update scheme for QuadratureElements if not all_equal(quad_schemes): scheme = "canonical" info( "Quadrature rule must be equal within each sub domain, using %s rule." % qr) else: scheme = quad_schemes[0] for element in form_data.sub_elements: if element.family() == "Quadrature": element._quad_scheme = scheme