def mark(indicator, refineTolerance, coarsenTolerance=0, minLevel=0, maxLevel=None, gridView=None): if ufl and (isinstance(indicator, list) or isinstance(indicator, tuple)): indicator = ufl.as_vector(indicator) if ufl and isinstance(indicator, ufl.core.expr.Expr): # if gridView is None: _, coeff_ = extract_arguments_and_coefficients(indicator) gridView = [c.grid for c in coeff_ if hasattr(c, "grid")] gridView += [c.gridView for c in coeff_ if hasattr(c, "gridView")] if len(gridView) == 0: raise ValueError( "if a ufl expression is passed as indicator then the 'gridView' must also be provided" ) gridView = gridView[0] indicator = expression2GF(gridView, indicator, 0) if gridView is None: gridView = indicator.gridView try: if not gridView.canAdapt: raise AttributeError( "indicator function must be over grid view that supports adaptation" ) except AttributeError: raise AttributeError( "indicator function must be over grid view that supports adaptation" ) if maxLevel == None: maxLevel = -1 return gridView.mark(indicator, refineTolerance, coarsenTolerance, minLevel, maxLevel)
def _analyze_form_arguments(self): "Analyze which Argument and Coefficient objects can be found in the form." from ufl.algorithms.analysis import extract_arguments_and_coefficients arguments, coefficients = extract_arguments_and_coefficients(self) # Define canonical numbering of arguments and coefficients self._arguments = tuple( sorted(set(arguments), key=lambda x: x.number())) self._coefficients = tuple( sorted(set(coefficients), key=lambda x: x.count())) self._coefficient_numbering = dict( (c, i) for i, c in enumerate(self._coefficients))
def _analyze_form_arguments(self): "Analyze which Argument and Coefficient objects can be found in the form." from ufl.algorithms.analysis import extract_arguments_and_coefficients arguments, coefficients = extract_arguments_and_coefficients(self) # Define canonical numbering of arguments and coefficients self._arguments = tuple(sorted(set(arguments), key=lambda x: x.number())) self._coefficients = tuple(sorted(set(coefficients), key=lambda x: x.count())) self._coefficient_numbering = dict((c, i) for i, c in enumerate(self._coefficients))
def generateMethodBody(cppType, expr, returnResult, default, predefined): if expr is not None and not expr == [None]: try: dimR = expr.ufl_shape[0] except: if isinstance(expr,list) or isinstance(expr,tuple): expr = as_vector(expr) else: expr = as_vector([expr]) dimR = expr.ufl_shape[0] _, coeff = extract_arguments_and_coefficients(expr) coeff = {c : c.toVectorCoefficient()[0] for c in coeff if len(c.ufl_shape) == 0 and not c.is_cellwise_constant()} expr = replace(expr, coeff) t = ExprTensor(expr.ufl_shape) # , exprTensorApply(lambda u: u, expr.ufl_shape, expr)) expression = [expr[i] for i in t.keys()] u = extract_arguments_and_coefficients(expr)[0] if u != []: u = u[0] du = Grad(u) d2u = Grad(du) arg_u = Variable("const RangeType &", "u") arg_du = Variable("const JacobianRangeType &", "du") arg_d2u = Variable("const HessianRangeType &", "d2u") predefined.update( {u: arg_u, du: arg_du, d2u: arg_d2u} ) code, results = generateCode(predefined, expression, tempVars=False) result = Variable(cppType, 'result') if cppType == 'double': code = code + [assign(result, results[0])] else: code = code + [assign(result[i], r) for i, r in zip(t.keys(), results)] if returnResult: code = [Declaration(result)] + code + [return_(result)] else: result = Variable(cppType, 'result') code = [assign(result, construct(cppType,default) )] if returnResult: code = [Declaration(result)] + code + [return_(result)] return code
def __init__(self, interpolator, *functions, **kwargs): super().__init__() self.expr = interpolator.expr self.arguments, self.coefficients = extract_arguments_and_coefficients(self.expr) if isinstance(interpolator.V, firedrake.Function): self.V = interpolator.V.function_space() else: self.V = interpolator.V for coefficient in self.coefficients: self.add_dependency(coefficient, no_duplicates=True) for function in functions: self.add_dependency(function, no_duplicates=True)
def preprocess(form, object_names=None, common_cell=None, element_mapping=None): """ Preprocess raw input form to obtain form metadata, including a modified (preprocessed) form more easily manipulated by form compilers. The original form is left untouched. Currently, the following transformations are made to the preprocessed form: expand_compounds (side effect of calling expand_derivatives) expand_derivatives renumber arguments and coefficients and apply evt. element mapping """ tic = Timer('preprocess') # TODO: Reposition tic calls after refactoring. # Check that we get a form ufl_assert(isinstance(form, Form), "Expecting Form.") original_form = form # Object names is empty if not given object_names = object_names or {} # Element mapping is empty if not given element_mapping = element_mapping or {} # Create empty form data form_data = FormData() # Store copies of preprocess input data, for future validation if called again... form_data._input_object_names = dict(object_names) form_data._input_element_mapping = dict(element_mapping) #form_data._input_common_cell = no need to store this # Store name of form if given, otherwise empty string # such that automatic names can be assigned externally form_data.name = object_names.get(id(form), "") # Extract common cell common_cell = extract_common_cell(form, common_cell) # TODO: Split out expand_compounds from expand_derivatives # Expand derivatives tic('expand_derivatives') form = expand_derivatives(form, common_cell.geometric_dimension()) # EXPR # Propagate restrictions of interior facet integrals to the terminal nodes form = propagate_restrictions(form) # INTEGRAL, EXPR # --- BEGIN DOMAIN SPLITTING AND JOINING # Store domain metadata per domain type form_data.domain_data = extract_domain_data(form) # Split up integrals and group by domain type and domain id, # adding integrands with same domain and compiler data sub_integral_data = integral_dict_to_sub_integral_data( form.integral_groups()) # TODO: Replace integral_data with this through ufl and ffc? if 0: print_sub_integral_data(sub_integral_data) # Reconstruct form from these integrals in a more canonical representation form = reconstruct_form_from_sub_integral_data(sub_integral_data, form_data.domain_data) # Represent in the way ffc expects TODO: Change ffc? Fine for now. form_data.integral_data = convert_sub_integral_data_to_integral_data( sub_integral_data) # --- END DOMAIN SPLITTING AND JOINING # --- BEGIN FUNCTION ANALYSIS # --- BEGIN SPLIT EXPR JOIN # Replace arguments and coefficients with new renumbered objects tic('extract_arguments_and_coefficients') original_arguments, original_coefficients = \ extract_arguments_and_coefficients(form) tic('build_element_mapping') element_mapping = build_element_mapping(element_mapping, common_cell, original_arguments, original_coefficients) tic('build_argument_replace_map') # TODO: Remove renumbered ones? replace_map, renumbered_arguments, renumbered_coefficients = \ build_argument_replace_map(original_arguments, original_coefficients, element_mapping) # Note: This is the earliest point signature can be computed # Build mapping to original arguments and coefficients, which is # useful if the original arguments have data attached to them inv_replace_map = dict((w, v) for (v, w) in replace_map.iteritems()) original_arguments = [inv_replace_map[v] for v in renumbered_arguments] original_coefficients = [ inv_replace_map[w] for w in renumbered_coefficients ] # TODO: Build mapping from object to position instead? But we need mapped elements as well anyway. #argument_positions = { v: i } #coefficient_positions = { w: i } # Store data extracted by preprocessing form_data.original_arguments = original_arguments form_data.original_coefficients = original_coefficients # --- END SPLIT INTEGRAL JOIN # Mappings from elements and functions (coefficients and arguments) # that reside in form to objects with canonical numbering as well as # completed cells and elements # TODO: Create additional function mappings per integral, # to have different counts? Depends on future UFC design. form_data.element_replace_map = element_mapping form_data.function_replace_map = replace_map # Store some useful dimensions form_data.rank = len(form_data.original_arguments) form_data.num_coefficients = len(form_data.original_coefficients) # Store argument names form_data.argument_names = \ [object_names.get(id(form_data.original_arguments[i]), "v%d" % i) for i in range(form_data.rank)] # Store coefficient names form_data.coefficient_names = \ [object_names.get(id(form_data.original_coefficients[i]), "w%d" % i) for i in range(form_data.num_coefficients)] # --- END FUNCTION ANALYSIS # --- BEGIN SIGNATURE COMPUTATION # TODO: Compute signatures of each INTEGRAL and EXPR as well, perhaps compute it hierarchially from integral_data? # Store signature of form tic('signature') # TODO: Remove signature() from Form, not safe to cache with a replacement map #form_data.signature = form.signature(form_data.function_replace_map) form_data.signature = compute_form_signature( form, form_data.function_replace_map) # --- END SIGNATURE COMPUTATION # --- BEGIN CONSISTENCY CHECKS # Check that we don't have a mixed linear/bilinear form or anything like that ufl_assert( len(compute_form_arities(form)) == 1, "All terms in form must have same rank.") # --- END CONSISTENCY CHECKS # --- BEGIN ELEMENT DATA # Store elements, sub elements and element map tic('extract_elements') form_data.argument_elements = tuple(f.element() for f in renumbered_arguments) form_data.coefficient_elements = tuple(f.element() for f in renumbered_coefficients) form_data.elements = form_data.argument_elements + form_data.coefficient_elements form_data.unique_elements = unique_tuple(form_data.elements) form_data.sub_elements = extract_sub_elements(form_data.elements) form_data.unique_sub_elements = unique_tuple(form_data.sub_elements) # --- END ELEMENT DATA # --- BEGIN DOMAIN DATA # Store element domains (NB! This is likely to change!) # TODO: DOMAINS: What is a sensible way to store domains for a form? form_data.domains = tuple( sorted(set(element.domain() for element in form_data.unique_elements))) # Store toplevel domains (NB! This is likely to change!) # TODO: DOMAINS: What is a sensible way to store domains for a form? form_data.top_domains = tuple( sorted(set(domain.top_domain() for domain in form_data.domains))) # Store common cell form_data.cell = common_cell # Store data related to cell form_data.geometric_dimension = form_data.cell.geometric_dimension() form_data.topological_dimension = form_data.cell.topological_dimension() # Store number of domains for integral types form_data.num_sub_domains = extract_num_sub_domains(form) # --- END DOMAIN DATA # A coarse profiling implementation TODO: Add counting of nodes, Add memory usage tic.end() if preprocess.enable_profiling: print tic # Store preprocessed form and return form_data.preprocessed_form = form form_data.preprocessed_form._is_preprocessed = True # Attach signatures to original and preprocessed forms TODO: Avoid this? ufl_assert(form_data.preprocessed_form._signature is None, "") form_data.preprocessed_form._signature = form_data.signature ufl_assert(original_form._signature is None, "") original_form._signature = form_data.signature return form_data
def preprocess_expression(expr, object_names=None, common_cell=None, element_mapping=None): """ Preprocess raw input expression to obtain expression metadata, including a modified (preprocessed) expression more easily manipulated by expression compilers. The original expression is left untouched. Currently, the following transformations are made to the preprocessed form: expand_compounds (side effect of calling expand_derivatives) expand_derivatives renumber arguments and coefficients and apply evt. element mapping """ tic = Timer('preprocess_expression') # Check that we get an expression ufl_assert(isinstance(expr, Expr), "Expecting Expr.") # Object names is empty if not given object_names = object_names or {} # Element mapping is empty if not given element_mapping = element_mapping or {} # Create empty expression data expr_data = ExprData() # Store original expression expr_data.original_expr = expr # Store name of expr if given, otherwise empty string # such that automatic names can be assigned externally expr_data.name = object_names.get(id(expr), "") # TODO: Or default to 'expr'? # Extract common cell common_cell = extract_common_cell(expr, common_cell) # TODO: Split out expand_compounds from expand_derivatives # Expand derivatives tic('expand_derivatives') expr = expand_derivatives(expr, common_cell.geometric_dimension()) # Renumber indices #expr = renumber_indices(expr) # TODO: No longer needed? # Replace arguments and coefficients with new renumbered objects tic('extract_arguments_and_coefficients') original_arguments, original_coefficients = \ extract_arguments_and_coefficients(expr) tic('build_element_mapping') element_mapping = build_element_mapping(element_mapping, common_cell, original_arguments, original_coefficients) tic('build_argument_replace_map') replace_map, renumbered_arguments, renumbered_coefficients = \ build_argument_replace_map(original_arguments, original_coefficients, element_mapping) expr = replace(expr, replace_map) # Build mapping to original arguments and coefficients, which is # useful if the original arguments have data attached to them inv_replace_map = dict((w, v) for (v, w) in replace_map.iteritems()) original_arguments = [inv_replace_map[v] for v in renumbered_arguments] original_coefficients = [ inv_replace_map[w] for w in renumbered_coefficients ] # Store data extracted by preprocessing expr_data.arguments = renumbered_arguments # TODO: Needed? expr_data.coefficients = renumbered_coefficients # TODO: Needed? expr_data.original_arguments = original_arguments expr_data.original_coefficients = original_coefficients expr_data.renumbered_arguments = renumbered_arguments expr_data.renumbered_coefficients = renumbered_coefficients tic('replace') # Mappings from elements and functions (coefficients and arguments) # that reside in expr to objects with canonical numbering as well as # completed cells and elements expr_data.element_replace_map = element_mapping expr_data.function_replace_map = replace_map # Store signature of form tic('signature') expr_data.signature = compute_expression_signature( expr, expr_data.function_replace_map) # Store elements, sub elements and element map tic('extract_elements') expr_data.elements = tuple( f.element() for f in chain(renumbered_arguments, renumbered_coefficients)) expr_data.unique_elements = unique_tuple(expr_data.elements) expr_data.sub_elements = extract_sub_elements(expr_data.elements) expr_data.unique_sub_elements = unique_tuple(expr_data.sub_elements) # Store element domains (NB! This is likely to change!) # FIXME: DOMAINS: What is a sensible way to store domains for a expr? expr_data.domains = tuple( sorted(set(element.domain() for element in expr_data.unique_elements))) # Store toplevel domains (NB! This is likely to change!) # FIXME: DOMAINS: What is a sensible way to store domains for a expr? expr_data.top_domains = tuple( sorted(set(domain.top_domain() for domain in expr_data.domains))) # Store common cell expr_data.cell = common_cell # Store data related to cell expr_data.geometric_dimension = expr_data.cell.geometric_dimension() expr_data.topological_dimension = expr_data.cell.topological_dimension() # Store some useful dimensions expr_data.rank = len(expr_data.arguments) # TODO: Is this useful for expr? expr_data.num_coefficients = len(expr_data.coefficients) # Store argument names # TODO: Is this useful for expr? expr_data.argument_names = \ [object_names.get(id(expr_data.original_arguments[i]), "v%d" % i) for i in range(expr_data.rank)] # Store coefficient names expr_data.coefficient_names = \ [object_names.get(id(expr_data.original_coefficients[i]), "w%d" % i) for i in range(expr_data.num_coefficients)] # Store preprocessed expression expr_data.preprocessed_expr = expr tic.end() # A coarse profiling implementation # TODO: Add counting of nodes # TODO: Add memory usage if preprocess_expression.enable_profiling: print tic return expr_data
def preprocess(form, object_names=None, common_cell=None, element_mapping=None): """ Preprocess raw input form to obtain form metadata, including a modified (preprocessed) form more easily manipulated by form compilers. The original form is left untouched. Currently, the following transformations are made to the preprocessed form: expand_compounds (side effect of calling expand_derivatives) expand_derivatives renumber arguments and coefficients and apply evt. element mapping """ tic = Timer('preprocess') # TODO: Reposition tic calls after refactoring. # Check that we get a form ufl_assert(isinstance(form, Form), "Expecting Form.") original_form = form # Object names is empty if not given object_names = object_names or {} # Element mapping is empty if not given element_mapping = element_mapping or {} # Create empty form data form_data = FormData() # Store copies of preprocess input data, for future validation if called again... form_data._input_object_names = dict(object_names) form_data._input_element_mapping = dict(element_mapping) #form_data._input_common_cell = no need to store this # Store name of form if given, otherwise empty string # such that automatic names can be assigned externally form_data.name = object_names.get(id(form), "") # Extract common cell common_cell = extract_common_cell(form, common_cell) # TODO: Split out expand_compounds from expand_derivatives # Expand derivatives tic('expand_derivatives') form = expand_derivatives(form, common_cell.geometric_dimension()) # EXPR # Propagate restrictions of interior facet integrals to the terminal nodes form = propagate_restrictions(form) # INTEGRAL, EXPR # --- BEGIN DOMAIN SPLITTING AND JOINING # Store domain metadata per domain type form_data.domain_data = extract_domain_data(form) # Split up integrals and group by domain type and domain id, # adding integrands with same domain and compiler data sub_integral_data = integral_dict_to_sub_integral_data(form.integral_groups()) # TODO: Replace integral_data with this through ufl and ffc? if 0: print_sub_integral_data(sub_integral_data) # Reconstruct form from these integrals in a more canonical representation form = reconstruct_form_from_sub_integral_data(sub_integral_data, form_data.domain_data) # Represent in the way ffc expects TODO: Change ffc? Fine for now. form_data.integral_data = convert_sub_integral_data_to_integral_data(sub_integral_data) # --- END DOMAIN SPLITTING AND JOINING # --- BEGIN FUNCTION ANALYSIS # --- BEGIN SPLIT EXPR JOIN # Replace arguments and coefficients with new renumbered objects tic('extract_arguments_and_coefficients') original_arguments, original_coefficients = \ extract_arguments_and_coefficients(form) tic('build_element_mapping') element_mapping = build_element_mapping(element_mapping, common_cell, original_arguments, original_coefficients) tic('build_argument_replace_map') # TODO: Remove renumbered ones? replace_map, renumbered_arguments, renumbered_coefficients = \ build_argument_replace_map(original_arguments, original_coefficients, element_mapping) # Note: This is the earliest point signature can be computed # Build mapping to original arguments and coefficients, which is # useful if the original arguments have data attached to them inv_replace_map = dict((w,v) for (v,w) in replace_map.iteritems()) original_arguments = [inv_replace_map[v] for v in renumbered_arguments] original_coefficients = [inv_replace_map[w] for w in renumbered_coefficients] # TODO: Build mapping from object to position instead? But we need mapped elements as well anyway. #argument_positions = { v: i } #coefficient_positions = { w: i } # Store data extracted by preprocessing form_data.original_arguments = original_arguments form_data.original_coefficients = original_coefficients # --- END SPLIT INTEGRAL JOIN # Mappings from elements and functions (coefficients and arguments) # that reside in form to objects with canonical numbering as well as # completed cells and elements # TODO: Create additional function mappings per integral, # to have different counts? Depends on future UFC design. form_data.element_replace_map = element_mapping form_data.function_replace_map = replace_map # Store some useful dimensions form_data.rank = len(form_data.original_arguments) form_data.num_coefficients = len(form_data.original_coefficients) # Store argument names form_data.argument_names = \ [object_names.get(id(form_data.original_arguments[i]), "v%d" % i) for i in range(form_data.rank)] # Store coefficient names form_data.coefficient_names = \ [object_names.get(id(form_data.original_coefficients[i]), "w%d" % i) for i in range(form_data.num_coefficients)] # --- END FUNCTION ANALYSIS # --- BEGIN SIGNATURE COMPUTATION # TODO: Compute signatures of each INTEGRAL and EXPR as well, perhaps compute it hierarchially from integral_data? # Store signature of form tic('signature') # TODO: Remove signature() from Form, not safe to cache with a replacement map #form_data.signature = form.signature(form_data.function_replace_map) form_data.signature = compute_form_signature(form, form_data.function_replace_map) # --- END SIGNATURE COMPUTATION # --- BEGIN CONSISTENCY CHECKS # Check that we don't have a mixed linear/bilinear form or anything like that ufl_assert(len(compute_form_arities(form)) == 1, "All terms in form must have same rank.") # --- END CONSISTENCY CHECKS # --- BEGIN ELEMENT DATA # Store elements, sub elements and element map tic('extract_elements') form_data.argument_elements = tuple(f.element() for f in renumbered_arguments) form_data.coefficient_elements = tuple(f.element() for f in renumbered_coefficients) form_data.elements = form_data.argument_elements + form_data.coefficient_elements form_data.unique_elements = unique_tuple(form_data.elements) form_data.sub_elements = extract_sub_elements(form_data.elements) form_data.unique_sub_elements = unique_tuple(form_data.sub_elements) # --- END ELEMENT DATA # --- BEGIN DOMAIN DATA # Store element domains (NB! This is likely to change!) # TODO: DOMAINS: What is a sensible way to store domains for a form? form_data.domains = tuple(sorted(set(element.domain() for element in form_data.unique_elements))) # Store toplevel domains (NB! This is likely to change!) # TODO: DOMAINS: What is a sensible way to store domains for a form? form_data.top_domains = tuple(sorted(set(domain.top_domain() for domain in form_data.domains))) # Store common cell form_data.cell = common_cell # Store data related to cell form_data.geometric_dimension = form_data.cell.geometric_dimension() form_data.topological_dimension = form_data.cell.topological_dimension() # Store number of domains for integral types form_data.num_sub_domains = extract_num_sub_domains(form) # --- END DOMAIN DATA # A coarse profiling implementation TODO: Add counting of nodes, Add memory usage tic.end() if preprocess.enable_profiling: print tic # Store preprocessed form and return form_data.preprocessed_form = form form_data.preprocessed_form._is_preprocessed = True # Attach signatures to original and preprocessed forms TODO: Avoid this? ufl_assert(form_data.preprocessed_form._signature is None, "") form_data.preprocessed_form._signature = form_data.signature ufl_assert(original_form._signature is None, "") original_form._signature = form_data.signature return form_data
def preprocess_expression(expr, object_names=None, common_cell=None, element_mapping=None): """ Preprocess raw input expression to obtain expression metadata, including a modified (preprocessed) expression more easily manipulated by expression compilers. The original expression is left untouched. Currently, the following transformations are made to the preprocessed form: expand_compounds (side effect of calling expand_derivatives) expand_derivatives renumber arguments and coefficients and apply evt. element mapping """ tic = Timer('preprocess_expression') # Check that we get an expression ufl_assert(isinstance(expr, Expr), "Expecting Expr.") # Object names is empty if not given object_names = object_names or {} # Element mapping is empty if not given element_mapping = element_mapping or {} # Create empty expression data expr_data = ExprData() # Store original expression expr_data.original_expr = expr # Store name of expr if given, otherwise empty string # such that automatic names can be assigned externally expr_data.name = object_names.get(id(expr), "") # TODO: Or default to 'expr'? # Extract common cell common_cell = extract_common_cell(expr, common_cell) # TODO: Split out expand_compounds from expand_derivatives # Expand derivatives tic('expand_derivatives') expr = expand_derivatives(expr, common_cell.geometric_dimension()) # Renumber indices #expr = renumber_indices(expr) # TODO: No longer needed? # Replace arguments and coefficients with new renumbered objects tic('extract_arguments_and_coefficients') original_arguments, original_coefficients = \ extract_arguments_and_coefficients(expr) tic('build_element_mapping') element_mapping = build_element_mapping(element_mapping, common_cell, original_arguments, original_coefficients) tic('build_argument_replace_map') replace_map, renumbered_arguments, renumbered_coefficients = \ build_argument_replace_map(original_arguments, original_coefficients, element_mapping) expr = replace(expr, replace_map) # Build mapping to original arguments and coefficients, which is # useful if the original arguments have data attached to them inv_replace_map = dict((w,v) for (v,w) in replace_map.iteritems()) original_arguments = [inv_replace_map[v] for v in renumbered_arguments] original_coefficients = [inv_replace_map[w] for w in renumbered_coefficients] # Store data extracted by preprocessing expr_data.arguments = renumbered_arguments # TODO: Needed? expr_data.coefficients = renumbered_coefficients # TODO: Needed? expr_data.original_arguments = original_arguments expr_data.original_coefficients = original_coefficients expr_data.renumbered_arguments = renumbered_arguments expr_data.renumbered_coefficients = renumbered_coefficients tic('replace') # Mappings from elements and functions (coefficients and arguments) # that reside in expr to objects with canonical numbering as well as # completed cells and elements expr_data.element_replace_map = element_mapping expr_data.function_replace_map = replace_map # Store signature of form tic('signature') expr_data.signature = compute_expression_signature(expr, expr_data.function_replace_map) # Store elements, sub elements and element map tic('extract_elements') expr_data.elements = tuple(f.element() for f in chain(renumbered_arguments, renumbered_coefficients)) expr_data.unique_elements = unique_tuple(expr_data.elements) expr_data.sub_elements = extract_sub_elements(expr_data.elements) expr_data.unique_sub_elements = unique_tuple(expr_data.sub_elements) # Store element domains (NB! This is likely to change!) # FIXME: DOMAINS: What is a sensible way to store domains for a expr? expr_data.domains = tuple(sorted(set(element.domain() for element in expr_data.unique_elements))) # Store toplevel domains (NB! This is likely to change!) # FIXME: DOMAINS: What is a sensible way to store domains for a expr? expr_data.top_domains = tuple(sorted(set(domain.top_domain() for domain in expr_data.domains))) # Store common cell expr_data.cell = common_cell # Store data related to cell expr_data.geometric_dimension = expr_data.cell.geometric_dimension() expr_data.topological_dimension = expr_data.cell.topological_dimension() # Store some useful dimensions expr_data.rank = len(expr_data.arguments) # TODO: Is this useful for expr? expr_data.num_coefficients = len(expr_data.coefficients) # Store argument names # TODO: Is this useful for expr? expr_data.argument_names = \ [object_names.get(id(expr_data.original_arguments[i]), "v%d" % i) for i in range(expr_data.rank)] # Store coefficient names expr_data.coefficient_names = \ [object_names.get(id(expr_data.original_coefficients[i]), "w%d" % i) for i in range(expr_data.num_coefficients)] # Store preprocessed expression expr_data.preprocessed_expr = expr tic.end() # A coarse profiling implementation # TODO: Add counting of nodes # TODO: Add memory usage if preprocess_expression.enable_profiling: print tic return expr_data
def __init__(self, name, uflExpr, virtualize, dimRange=None, predefined=None): self.className = name self.targs = ['class GridPart'] if dimRange is not None: self.bindable = True self.dimRange = dimRange self.bindableBase = 'Dune::Fem::BindableGridFunctionWithSpace<GridPart,Dune::Dim<'+str(self.dimRange)+'>>' self.bases = ['public '+self.bindableBase] self.includeFiles = ['dune/fem/function/localfunction/bindable.hh'] else: self.bindable = False self.bases = [] self.includeFiles = [] self.includeFiles += ['dune/fem/common/intersectionside.hh'] self.gridPartType = TypeAlias("GridPartType", "GridPart") self.ctor_args = [] self.ctor_init = [] self.skeleton = None self.init = None self.vars = None uflExpr = [e for e in uflExpr if e is not None] coefficients = set() for expr in uflExpr: try: coefficients |= set(expr.coefficients()) except: _, cc = extract_arguments_and_coefficients(expr) coefficients |= set(cc) extracedAll = False while not extracedAll: extracedAll = True for c in coefficients: try: predef = c.predefined except AttributeError: continue for expr in predef.values(): _, cc = extract_arguments_and_coefficients(expr) cc = set(cc) if not cc.issubset(coefficients): coefficients |= cc extracedAll = False if not extracedAll: break self.constantList = sorted((c for c in coefficients if c.is_cellwise_constant()), key=lambda c: c.count()) self.coefficientList = sorted((c for c in coefficients if not c.is_cellwise_constant()), key=lambda c: c.count()) constants=[fieldVectorType(c,useScalar=True) for c in self.constantList] coefficients=(fieldVectorType(c) for c in self.coefficientList) constantNames=[getattr(c, 'name', None) for c in self.constantList] constantShapes=[getattr(c, 'ufl_shape', None) for c in self.constantList] coefficientNames=[getattr(c, 'name', None) for c in self.coefficientList] parameterNames=[getattr(c, 'parameter', None) for c in self.constantList] if not len(set(constantNames)) == len(constantNames): raise AttributeError("two constants have the same name which will lead to failure during code generation:"+','.join(c for c in constantNames)) # this part modifies duplicate names in coefficient - slow version # can be improved coefficientNames_ = coefficientNames coefficientNames = [] for c in coefficientNames_: if c is not None: while c in coefficientNames: c = c+"A" coefficientNames.append(c) self._constants = [] if constants is None else list(constants) self._coefficients = [] if coefficients is None else list(coefficients) self._constantShapes = [None,] * len(self._constants) if constantShapes is None else list(constantShapes) self._constantNames = [None,] * len(self._constants) if constantNames is None else list(constantNames) self._constantNames = ['constant' + str(i) if n is None else n for i, n in enumerate(self._constantNames)] if len(self._constantNames) != len(self._constants): raise ValueError("Length of constantNames must match length of constants") invalidConstants = [n for n in self._constantNames if n is not None and re.match('^[a-zA-Z_][a-zA-Z0-9_]*$', n) is None] if invalidConstants: raise ValueError('Constant names are not valid C++ identifiers:' + ', '.join(invalidConstants) + '.') self._constantValues = [ None if not hasattr(c,"value") else c.value for c in self.constantList ] self._coefficientNames = [None,] * len(self._coefficients) if coefficientNames is None else list(coefficientNames) self._coefficientNames = ['coefficient' + str(i) if n is None else n for i, n in enumerate(self._coefficientNames)] if len(self._coefficientNames) != len(self._coefficients): raise ValueError("Length of coefficientNames must match length of coefficients") invalidCoefficients = [n for n in self._coefficientNames if n is not None and re.match('^[a-zA-Z_][a-zA-Z0-9_]*$', n) is None] if invalidCoefficients: raise ValueError('Coefficient names are not valid C++ identifiers:' + ', '.join(invalidCoefficients) + '.') self._parameterNames = [None,] * len(self._constants) if parameterNames is None else list(parameterNames) if len(self._parameterNames) != len(self._constants): raise ValueError("Length of parameterNames must match length of constants") self._derivatives = [('RangeType', 'evaluate'), ('JacobianRangeType', 'jacobian'), ('HessianRangeType', 'hessian')] if self._coefficients: if virtualize: self.coefficientCppTypes = \ ['Dune::FemPy::VirtualizedGridFunction< ' +\ gridPartType(c) + ', ' + fieldVectorType(c) + ' >' \ if not c.cppTypeName.startswith("Dune::Python::SimpleGridFunction") \ else c.cppTypeName \ for c in self.coefficientList] # VirtualizedGF need GridParts but they have to be the same for all coefficients # Do we want this to work in some way? Then we can add # includes here - but '.bind(entity)' will fail. # for c in self.coefficientList: # self.includeFiles += c.grid.cppIncludes else: self.coefficientCppTypes = [c.cppTypeName for c in self.coefficientList] else: self.coefficientCppTypes = [] # need to replace possible grid functions in values of predefined # import pdb; pdb.set_trace() self.predefined = {} if predefined is None else predefined for idx, coefficient in enumerate(self.coefficientList): try: self.predefined.update( coefficient.predefined ) except AttributeError: pass for idx, coefficient in enumerate(self.coefficientList): for derivative in self.coefficient(idx, 'x', side=None): for k,v in self.predefined.items(): if coefficient == v: self.predefined[k] = derivative coefficient = Grad(coefficient)
def load(grid, form, *args, renumbering=None, tempVars=True, virtualize=True, modelPatch=[None,None], includes=None): if not isinstance(modelPatch,list) and not isinstance(modelPatch,tuple): modelPatch = [modelPatch,None] if isinstance(form, Equation): form = form.lhs - form.rhs if isinstance(form, Integrands): integrands = form else: if len(form.arguments()) < 2: raise ValueError("Integrands model requires form with at least two arguments.") phi_, u_ = form.arguments() if phi_.ufl_function_space().scalar: phi = TestFunction(phi_.ufl_function_space().toVectorSpace()) form = replace(form,{phi_:phi[0]}) else: phi = phi_ if u_.ufl_function_space().scalar: u = TrialFunction(u_.ufl_function_space().toVectorSpace()) form = replace(form,{u_:u[0]}) else: u = u_ if not isinstance(form, Form): raise ValueError("ufl.Form or ufl.Equation expected.") _, coeff_ = extract_arguments_and_coefficients(form) coeff_ = set(coeff_) # added for dirichlet treatment same as conservationlaw model dirichletBCs = [arg for arg in args if isinstance(arg, DirichletBC)] # remove the dirichletBCs arg = [arg for arg in args if not isinstance(arg, DirichletBC)] for dBC in dirichletBCs: _, coeff__ = extract_arguments_and_coefficients(dBC.ufl_value) coeff_ |= set(coeff__) coeff = {c : c.toVectorCoefficient()[0] for c in coeff_ if len(c.ufl_shape) == 0 and not c.is_cellwise_constant()} form = replace(form,coeff) uflExpr = [form] for dBC in dirichletBCs: arg.append(dBC.replace(coeff)) uflExpr += [dBC.ufl_value] # arg[-1].ufl_value] if modelPatch[1] is not None: uflExpr += modelPatch[1] derivatives = gatherDerivatives(form, [phi, u]) derivatives_phi = derivatives[0] derivatives_u = derivatives[1] integrands = Integrands(u, (d.ufl_shape for d in derivatives_u), (d.ufl_shape for d in derivatives_phi), uflExpr,virtualize) if modelPatch[0] is not None: modelPatch[0](integrands) # set up the source class source = Source(integrands, grid, includes, form, *args, tempVars=tempVars,virtualize=virtualize) # ufl coefficient and constants only have numbers which depend on the # order in whch they were generated - we need to keep track of how # these numbers are translated into the tuple numbering in the # generated C++ code if isinstance(form, Form): coefficients = set(integrands.coefficientList+integrands.constantList) numCoefficients = len(coefficients) if renumbering is None: renumbering = dict() renumbering.update((c, i) for i, c in enumerate(sorted((c for c in coefficients if not c.is_cellwise_constant()), key=lambda c: c.count()))) renumbering.update((c, i) for i, c in enumerate(c for c in coefficients if c.is_cellwise_constant())) coefficientNames = integrands._coefficientNames # ['coefficient' + str(i) if n is None else n for i, n in enumerate(getattr(c, 'name', None) for c in coefficients if not c.is_cellwise_constant())] else: coefficientNames = form.coefficientNames # call code generator from dune.generator import builder module = builder.load(source.name(), source, "Integrands") assert hasattr(module,"Integrands"),\ "GridViews of coefficients need to be compatible with the grid view of the ufl model" rangeValueTuple, domainValueTuple = source.valueTuples() setattr(module.Integrands, "_domainValueType", domainValueTuple) setattr(module.Integrands, "_rangeValueType", rangeValueTuple) # redirect the __init__ method to take care of setting coefficient and renumbering class Model(module.Integrands): def __init__(self, *args, **kwargs): self.base = module.Integrands init(self,integrands,*args,**kwargs) for c in integrands.constantList: c.registerModel(self) setattr(Model, '_coefficientNames', {n: i for i, n in enumerate(coefficientNames)}) if renumbering is not None: setattr(Model, '_renumbering', renumbering) Model._setConstant = module.Integrands.__dict__['setConstant'] setattr(Model, 'setConstant', setConstant) return Model
def compileUFL(form, patch, *args, **kwargs): if isinstance(form, Equation): form = form.lhs - form.rhs if not isinstance(form, Form): raise Exception("ufl.Form expected.") if len(form.arguments()) < 2: raise Exception("ConservationLaw model requires form with at least two arguments.") phi_, u_ = form.arguments() if phi_.ufl_function_space().scalar: phi = TestFunction(phi_.ufl_function_space().toVectorSpace()) form = replace(form,{phi_:phi[0]}) else: phi = phi_ if u_.ufl_function_space().scalar: u = TrialFunction(u_.ufl_function_space().toVectorSpace()) form = replace(form,{u_:u[0]}) else: u = u_ _, coeff_ = extract_arguments_and_coefficients(form) coeff_ = set(coeff_) # added for dirichlet treatment same as conservationlaw model dirichletBCs = [arg for arg in args if isinstance(arg, DirichletBC)] # remove the dirichletBCs arg = [arg for arg in args if not isinstance(arg, DirichletBC)] for dBC in dirichletBCs: _, coeff__ = extract_arguments_and_coefficients(dBC.ufl_value) coeff_ |= set(coeff__) if patch is not None: for a in patch: try: _, coeff__ = extract_arguments_and_coefficients(a) coeff_ |= set(coeff__) except: pass # a might be a float/int and not a ufl expression coeff = {c : c.toVectorCoefficient()[0] for c in coeff_ if len(c.ufl_shape) == 0 and not c.is_cellwise_constant()} form = replace(form,coeff) for bc in dirichletBCs: bc.ufl_value = replace(bc.ufl_value, coeff) if patch is not None: patch = [a if not isinstance(a, Expr) else replace(a,coeff) for a in patch] phi = form.arguments()[0] dimRange = phi.ufl_shape[0] u = form.arguments()[1] du = Grad(u) d2u = Grad(du) ubar = Coefficient(u.ufl_function_space()) dubar = Grad(ubar) d2ubar = Grad(dubar) dimDomain = u.ufl_shape[0] x = SpatialCoordinate(form.ufl_cell()) try: field = u.ufl_function_space().field except AttributeError: field = "double" # if exact solution is passed in subtract a(u,.) from the form if "exact" in kwargs: b = replace(form, {u: as_vector(kwargs["exact"])} ) form = form - b dform = apply_derivatives(derivative(action(form, ubar), ubar, u)) source, flux, boundarySource = splitUFLForm(form) linSource, linFlux, linBoundarySource = splitUFLForm(dform) fluxDivergence, _, _ = splitUFLForm(inner(source.as_ufl() - div(flux.as_ufl()), phi) * dx(0)) # split linNVSource off linSource # linSources = splitUFL2(u, du, d2u, linSource) # linNVSource = linSources[2] # linSource = linSources[0] + linSources[1] if patch is not None: model = ConservationLawModel(dimDomain, dimRange, u, modelSignature(form,*patch,*args)) else: model = ConservationLawModel(dimDomain, dimRange, u, modelSignature(form,None,*args)) model._replaceCoeff = coeff model.hasNeumanBoundary = not boundarySource.is_zero() #expandform = expand_indices(expand_derivatives(expand_compounds(equation.lhs))) #if expandform == adjoint(expandform): # model.symmetric = 'true' model.field = field dirichletBCs = [arg for arg in args if isinstance(arg, DirichletBC)] # deprecated # if "dirichlet" in kwargs: # dirichletBCs += [DirichletBC(u.ufl_function_space(), as_vector(value), bndId) for bndId, value in kwargs["dirichlet"].items()] uflCoefficients = set(form.coefficients()) for bc in dirichletBCs: _, c = extract_arguments_and_coefficients(bc.ufl_value) uflCoefficients |= set(c) if patch is not None: for a in patch: if isinstance(a, Expr): _, c = extract_arguments_and_coefficients(a) uflCoefficients |= set(c) constants = dict() coefficients = dict() for coefficient in uflCoefficients: try: name = getattr(coefficient, "name") except AttributeError: name = str(coefficient) if coefficient.is_cellwise_constant(): try: parameter = getattr(coefficient, "parameter") except AttributeError: parameter = None if len(coefficient.ufl_shape) == 0: constants[coefficient] = model.addConstant('double', name=name, parameter=parameter) elif len(coefficient.ufl_shape) == 1: constants[coefficient] = model.addConstant('Dune::FieldVector< double, ' + str(coefficient.ufl_shape[0]) + ' >', name=name, parameter=parameter) else: Exception('Currently, only scalars and vectors are supported as constants') else: shape = coefficient.ufl_shape[0] try: coefficients[coefficient] = model.addCoefficient( shape, coefficient.cppTypeName, name=name, field=coefficient.ufl_function_space().field) except AttributeError: coefficients[coefficient] = model.addCoefficient( shape, coefficient.cppTypeName, name=name) model.coefficients = coefficients model.constants = constants tempVars = kwargs.get("tempVars", True) predefined = {u: model.arg_u, du: model.arg_du, d2u: model.arg_d2u} predefined[x] = UnformattedExpression('auto', 'entity().geometry().global( Dune::Fem::coordinate( ' + model.arg_x.name + ' ) )') model.predefineCoefficients(predefined,'x') model.source = generateCode(predefined, source, tempVars=tempVars) model.flux = generateCode(predefined, flux, tempVars=tempVars) predefined.update({ubar: model.arg_ubar, dubar: model.arg_dubar, d2ubar: model.arg_d2ubar}) model.linSource = generateCode(predefined, linSource, tempVars=tempVars) model.linFlux = generateCode(predefined, linFlux, tempVars=tempVars) # model.linNVSource = generateCode({u: arg, du: darg, d2u: d2arg, ubar: argbar, dubar: dargbar, d2ubar: d2argbar}, linNVSource, model.coefficients, tempVars) predefined = {u: model.arg_u} predefined[x] = UnformattedExpression('auto', 'entity().geometry().global( Dune::Fem::coordinate( ' + model.arg_x.name + ' ) )') model.predefineCoefficients(predefined,'x') model.alpha = generateCode(predefined, boundarySource, tempVars=tempVars) predefined.update({ubar: model.arg_ubar}) model.linAlpha = generateCode(predefined, linBoundarySource, tempVars=tempVars) predefined = {u: model.arg_u, du: model.arg_du, d2u: model.arg_d2u} predefined[x] = UnformattedExpression('auto', 'entity().geometry().global( Dune::Fem::coordinate( ' + model.arg_x.name + ' ) )') model.predefineCoefficients(predefined,'x') model.fluxDivergence = generateCode(predefined, fluxDivergence, tempVars=tempVars) if dirichletBCs: model.hasDirichletBoundary = True predefined = {} predefined[x] = UnformattedExpression('auto', 'entity().geometry().global( Dune::Fem::coordinate( ' + model.arg_x.name + ' ) )') model.predefineCoefficients(predefined,'x') maxId = 0 codeDomains = [] bySubDomain = dict() neuman = [] for bc in dirichletBCs: if bc.subDomain in bySubDomain: raise Exception('Multiply defined Dirichlet boundary for subdomain ' + str(bc.subDomain)) if not isinstance(bc.functionSpace, (FunctionSpace, FiniteElementBase)): raise Exception('Function space must either be a ufl.FunctionSpace or a ufl.FiniteElement') if isinstance(bc.functionSpace, FunctionSpace) and (bc.functionSpace != u.ufl_function_space()): raise Exception('Space of trial function and dirichlet boundary function must be the same - note that boundary conditions on subspaces are not available, yet') if isinstance(bc.functionSpace, FiniteElementBase) and (bc.functionSpace != u.ufl_element()): raise Exception('Cannot handle boundary conditions on subspaces, yet') if isinstance(bc.value, list): neuman = [i for i, x in enumerate(bc.value) if x == None] else: neuman = [] value = ExprTensor(u.ufl_shape) for key in value.keys(): value[key] = Indexed(bc.ufl_value, MultiIndex(tuple(FixedIndex(k) for k in key))) if isinstance(bc.subDomain,int): bySubDomain[bc.subDomain] = value,neuman maxId = max(maxId, bc.subDomain) else: domain = ExprTensor(()) for key in domain.keys(): domain[key] = Indexed(bc.subDomain, MultiIndex(tuple(FixedIndex(k) for k in key))) codeDomains.append( (value,neuman,domain) ) defaultCode = [] defaultCode.append(Declaration(Variable('int', 'domainId'))) defaultCode.append(Declaration(Variable('auto', 'tmp0'), initializer=UnformattedExpression('auto','intersection.geometry().center()'))) for i,v in enumerate(codeDomains): block = Block() defaultCode.append( generateDirichletDomainCode(predefined, v[2], tempVars=tempVars)) defaultCode.append('if (domainId)') block = UnformattedBlock() block.append('std::fill( dirichletComponent.begin(), dirichletComponent.end(), ' + str(maxId+i+1) + ' );') if len(v[1])>0: [block.append('dirichletComponent[' + str(c) + '] = 0;') for c in v[1]] block.append('return true;') defaultCode.append(block) defaultCode.append(return_(False)) bndId = Variable('const int', 'bndId') getBndId = UnformattedExpression('int', 'BoundaryIdProviderType::boundaryId( ' + model.arg_i.name + ' )') switch = SwitchStatement(bndId, default=defaultCode) for i,v in bySubDomain.items(): code = [] if len(v[1])>0: [code.append('dirichletComponent[' + str(c) + '] = 0;') for c in v[1]] code.append(return_(True)) switch.append(i, code) model.isDirichletIntersection = [Declaration(bndId, initializer=getBndId), UnformattedBlock('std::fill( dirichletComponent.begin(), dirichletComponent.end(), ' + bndId.name + ' );'), switch ] switch = SwitchStatement(model.arg_bndId, default=assign(model.arg_r, construct("RRangeType", 0))) for i, v in bySubDomain.items(): switch.append(i, generateDirichletCode(predefined, v[0], tempVars=tempVars)) for i,v in enumerate(codeDomains): switch.append(i+maxId+1, generateDirichletCode(predefined, v[0], tempVars=tempVars)) model.dirichlet = [switch] return model