def conditional(self, o, *operands): # Get condition and return values; and do safety check. cond, true, false = operands ffc_assert(len(cond) == 1 and firstkey(cond) == (), "Condtion should only be one function: " + repr(cond)) ffc_assert(len(true) == 1 and firstkey(true) == (), "True value of Condtional should only be one function: " + repr(true)) ffc_assert(len(false) == 1 and firstkey(false) == (), "False value of Condtional should only be one function: " + repr(false)) # Get values and test for None t_val = true[()] f_val = false[()] # Get the minimum type and number of operations # TODO: conditionals are currently always located inside the # ip loop, therefore the type has to be at least IP (fix bug # #1082048). This can be optimised. t = min([cond[()].t, t_val.t, f_val.t, IP]) ops = sum([cond[()].ops(), t_val.ops(), f_val.ops()]) # Create expression for conditional # TODO: Handle this differently to expose the variables which # are used to create the expressions. expr = create_symbol(format["evaluate conditional"](cond[()], t_val, f_val), t) num = len(self.conditionals) name = create_symbol(format["conditional"](num), t) if expr not in self.conditionals: self.conditionals[expr] = (t, ops, num) else: num = self.conditionals[expr][2] name = create_symbol(format["conditional"](num), t) return {(): name}
def conditional(self, o, *operands): # Get condition and return values; and do safety check. cond, true, false = operands ffc_assert(len(cond) == 1 and firstkey(cond) == (),\ "Condtion should only be one function: " + repr(cond)) ffc_assert(len(true) == 1 and firstkey(true) == (),\ "True value of Condtional should only be one function: " + repr(true)) ffc_assert(len(false) == 1 and firstkey(false) == (),\ "False value of Condtional should only be one function: " + repr(false)) # Get values and test for None t_val = true[()] f_val = false[()] # Get the minimum type and number of operations # TODO: conditionals are currently always located inside the ip loop, # therefore the type has to be at least IP (fix bug #1082048). This can # be optimised. t = min([cond[()].t, t_val.t, f_val.t, IP]) ops = sum([cond[()].ops(), t_val.ops(), f_val.ops()]) # Create expression for conditional # TODO: Handle this differently to expose the variables which are used # to create the expressions. expr = create_symbol( format["evaluate conditional"](cond[()], t_val, f_val), t) num = len(self.conditionals) name = create_symbol(format["conditional"](num), t) if not expr in self.conditionals: self.conditionals[expr] = (t, ops, num) else: num = self.conditionals[expr][2] name = create_symbol(format["conditional"](num), t) return {(): name}
def power(self, o): # Get base and exponent. base, expo = o.ufl_operands # Visit base to get base code. base_code = self.visit(base) # TODO: Are these safety checks needed? ffc_assert(() in base_code and len(base_code) == 1, "Only support function type base: " + repr(base_code)) # Get the base code and create power. val = base_code[()] # Handle different exponents if isinstance(expo, IntValue): return {(): create_product([val] * expo.value())} elif isinstance(expo, FloatValue): exp = format["floating point"](expo.value()) sym = create_symbol(format["std power"](str(val), exp), val.t, val, 1) return {(): sym} elif isinstance(expo, (Coefficient, Operator)): exp = self.visit(expo)[()] sym = create_symbol(format["std power"](str(val), exp), val.t, val, 1) return {(): sym} else: error("power does not support this exponent: " + repr(expo))
def _bessel_function(self, operands, format_function): # TODO: Are these safety checks needed? # TODO: work on reference instead of copies? (like # math_function) ffc_assert( len(operands) == 2, "BesselFunctions expect two operands of function type: " + repr(operands)) nu, x = operands ffc_assert( len(nu) == 1 and () in nu, "Expecting one operand of function type as first argument to BesselFunction : " + repr(nu)) ffc_assert( len(x) == 1 and () in x, "Expecting one operand of function type as second argument to BesselFunction : " + repr(x)) nu = nu[()] x = x[()] if nu is None: nu = format["floating point"](0.0) if x is None: x = format["floating point"](0.0) sym = create_symbol(format_function(x, nu), x.t, x, 1) return {(): sym}
def _create_entry_data(self, val, integral_type): # Multiply value by weight and determinant ACCESS = GEO weight = format["weight"](self.points) if self.points > 1: weight += format["component"]("", format["integration points"]) ACCESS = IP weight = self._create_symbol(weight, ACCESS)[()] # Create value. if integral_type in (point_integral_types + custom_integral_types): trans_set = set() value = create_product([val, weight]) else: f_scale_factor = format["scale factor"] trans_set = set([f_scale_factor]) value = create_product( [val, weight, create_symbol(f_scale_factor, GEO)]) # Update sets of used variables (if they will not be used # because of optimisations later, they will be reset). trans_set.update([str(x) for x in value.get_unique_vars(GEO)]) used_points = set([self.points]) ops = self._count_operations(value) used_psi_tables = set( [self.psi_tables_map[b] for b in value.get_unique_vars(BASIS)]) return (value, ops, [trans_set, used_points, used_psi_tables])
def _create_symbol(self, symbol, domain, _loop_index=[], _iden=None): return { (): create_symbol(symbol, domain, loop_index=_loop_index, iden=_iden) }
def _create_entry_data(self, val, integral_type): # Multiply value by weight and determinant ACCESS = GEO weight = format["weight"](self.points) if self.points > 1: weight += format["component"]("", format["integration points"]) ACCESS = IP weight = self._create_symbol(weight, ACCESS)[()] # Create value. if integral_type in (point_integral_types + custom_integral_types): trans_set = set() value = create_product([val, weight]) else: f_scale_factor = format["scale factor"] trans_set = set([f_scale_factor]) value = create_product([val, weight, create_symbol(f_scale_factor, GEO)]) # Update sets of used variables (if they will not be used # because of optimisations later, they will be reset). trans_set.update([str(x) for x in value.get_unique_vars(GEO)]) used_points = set([self.points]) ops = self._count_operations(value) used_psi_tables = set([self.psi_tables_map[b] for b in value.get_unique_vars(BASIS)]) return (value, ops, [trans_set, used_points, used_psi_tables])
def binary_condition(self, o, *operands): # Get LHS and RHS expressions and do safety checks. Might be # a bit too strict? lhs, rhs = operands ffc_assert( len(lhs) == 1 and firstkey(lhs) == (), "LHS of Condtion should only be one function: " + repr(lhs)) ffc_assert( len(rhs) == 1 and firstkey(rhs) == (), "RHS of Condtion should only be one function: " + repr(rhs)) # Map names from UFL to cpp.py. name_map = { "==": "is equal", "!=": "not equal", "<": "less than", ">": "greater than", "<=": "less equal", ">=": "greater equal", "&&": "and", "||": "or" } # Get the minimum type t = min(lhs[()].t, rhs[()].t) ops = lhs[()].ops() + rhs[()].ops() + 1 cond = str(lhs[()]) + format[name_map[o._name]] + str(rhs[()]) sym = create_symbol(format["grouping"](cond), t, base_op=ops) return {(): sym}
def max_value(self, o, *operands): # Take maximum value of operands. val0 = operands[0][()] val1 = operands[1][()] t = min(val0.t, val1.t) # FIXME: I don't know how to implement this the optimized way. Is this right? new_val = create_symbol(format["max value"](str(val0), str(val1)), t) return {(): new_val}
def _reduce_expression(expr, symbols, const_dict, f_name, use_expr_type=False): if use_expr_type: if expr not in const_dict: const_dict[expr] = len(const_dict) return create_symbol(f_name(const_dict[expr]), expr.t) # Only something to be done if we have more than one symbol. if len(symbols) > 1: sym_type = symbols[0].t # Create new symbol. if expr._prec == 2: new_sym = create_product(symbols) elif expr._prec == 3: new_sym = create_sum(symbols) if new_sym not in const_dict: const_dict[new_sym] = len(const_dict) s = create_symbol(f_name(const_dict[new_sym]), sym_type) return [s] return symbols
def max_value(self, o, *operands): # Take maximum value of operands. val0 = operands[0][()] val1 = operands[1][()] t = min(val0.t, val1.t) # FIXME: I don't know how to implement this the optimized # way. Is this right? new_val = create_symbol(format["max value"](str(val0), str(val1)), t) return {(): new_val}
def circumradius(self, o, *operands): # Safety check. ffc_assert(not operands, "Didn't expect any operands for Circumradius: " + repr(operands)) # FIXME: KBO: This has to change for higher order elements circumradius = format["circumradius"](self.restriction) self.trans_set.add(circumradius) return {():create_symbol(circumradius, GEO)}
def not_condition(self, o, *operands): # This is a Condition but not a BinaryCondition, and the operand will be another Condition # Get condition expression and do safety checks. # Might be a bit too strict? c, = operands ffc_assert(len(c) == 1 and c.keys()[0] == (),\ "Condition for NotCondition should only be one function: " + repr(c)) sym = create_symbol(format["not"](str(c[()])), c[()].t, base_op=c[()].ops()+1) return {(): sym}
def facet_area(self, o): # FIXME: KBO: This has to change for higher order elements # NOTE: Omitting restriction because the area of a facet is # the same on both sides. # FIXME: Since we use the scale factor, facet area has no # meaning for cell integrals. (Need check in FFC or UFL). area = format["facet area"] self.trans_set.add(area) return {(): create_symbol(area, GEO)}
def max_facet_edge_length(self, o): # FIXME: this has no meaning for cell integrals. (Need check in FFC or UFL). if self.tdim < 3: return self.facet_area(o) edgelen = format["max facet edge length"](self.restriction) self.trans_set.add(edgelen) return {():create_symbol(edgelen, GEO)}
def cell_volume(self, o): # FIXME: KBO: This has to change for higher order elements # detJ = format["det(J)"](self.restriction) # volume = format["absolute value"](detJ) # self.trans_set.add(detJ) volume = format["cell volume"](self.restriction) self.trans_set.add(volume) return {(): create_symbol(volume, GEO)}
def power(self, o): #print("\n\nVisiting Power: " + repr(o)) # Get base and exponent. base, expo = o.ufl_operands # Visit base to get base code. base_code = self.visit(base) # TODO: Are these safety checks needed? ffc_assert(() in base_code and len(base_code) == 1, "Only support function type base: " + repr(base_code)) # Get the base code and create power. val = base_code[()] # Handle different exponents if isinstance(expo, IntValue): return {(): create_product([val] * expo.value())} elif isinstance(expo, FloatValue): if self.parameters["format"] == "pyop2": exp = create_float(expo.value()) else: exp = format["floating point"](expo.value()) var = format["std power"][self.parameters["format"]](str(val), exp) if self.parameters["format"] == "pyop2": sym = create_funcall(var, [val, exp]) else: sym = create_symbol(var, val.t, val, 1, iden=var) return {(): sym} elif isinstance(expo, (Coefficient, Operator)): exp = self.visit(expo)[()] # print "pow exp: ", exp # print "pow val: ", val var = format["std power"][self.parameters["format"]](str(val), exp) if self.parameters["format"] == "pyop2": sym = create_funcall(var, [val, exp]) else: sym = create_symbol(var, val.t, val, 1, iden=var) return {(): sym} else: error("power does not support this exponent: " + repr(expo))
def abs(self, o, *operands): # TODO: Are these safety checks needed? ffc_assert(len(operands) == 1 and () in operands[0] and len(operands[0]) == 1, "Abs expects one operand of function type: " + repr(operands)) # Take absolute value of operand. val = operands[0][()] new_val = create_symbol(format["absolute value"](str(val)), val.t, val, 1) return {(): new_val}
def _math_function(self, operands, format_function): # TODO: Are these safety checks needed? ffc_assert(len(operands) == 1 and () in operands[0] and len(operands[0]) == 1, "MathFunctions expect one operand of function type: " + repr(operands)) # Use format function on value of operand. operand = operands[0] for key, val in list(operand.items()): new_val = create_symbol(format_function(str(val)), val.t, val, 1) operand[key] = new_val # raise Exception("pause") return operand
def abs(self, o, *operands): #print("\n\nVisiting Abs: " + repr(o) + "with operands: " + "\n".join(map(repr,operands))) # TODO: Are these safety checks needed? ffc_assert(len(operands) == 1 and () in operands[0] and len(operands[0]) == 1, \ "Abs expects one operand of function type: " + repr(operands)) # Take absolute value of operand. val = operands[0][()] new_val = create_symbol(format["absolute value"](str(val)), val.t, val, 1) return {():new_val}
def min_facet_edge_length(self, o): # FIXME: this has no meaning for cell integrals. (Need check in FFC or UFL). tdim = self.tdim # FIXME: o.domain().topological_dimension() ??? if tdim < 3: return self.facet_area(o) edgelen = format["min facet edge length"](self.restriction) self.trans_set.add(edgelen) return {(): create_symbol(edgelen, GEO)}
def __apply_transform(self, function, derivatives, multi, tdim, gdim): "Apply transformation (from derivatives) to basis or function." f_transform = format["transform"] # Add transformation if needed. transforms = [] for i, direction in enumerate(derivatives): ref = multi[i] t = f_transform("JINV", ref, direction, tdim, gdim, self.restriction) transforms.append(create_symbol(t, GEO)) transforms.append(function) return create_product(transforms)
def max_value(self, o, *operands): # Take maximum value of operands. val0 = operands[0][()] val1 = operands[1][()] t = min(val0.t, val1.t) # FIXME: I don't know how to implement this the optimized way. Is this right? if self.parameters["format"] == "pyop2": new_val = create_funcall("fmax", [val0, val1]) else: new_val = create_symbol(format["max value"](str(val0), str(val1)), t) return {(): new_val}
def _math_function(self, operands, format_function): #print("Calling _math_function() of optimisedquadraturetransformer.") # TODO: Are these safety checks needed? ffc_assert(len(operands) == 1 and () in operands[0] and len(operands[0]) == 1, \ "MathFunctions expect one operand of function type: " + repr(operands)) # Use format function on value of operand. operand = operands[0] for key, val in list(operand.items()): new_val = create_symbol(format_function(str(val)), val.t, val, 1) operand[key] = new_val #raise Exception("pause") return operand
def abs(self, o, *operands): # TODO: Are these safety checks needed? ffc_assert( len(operands) == 1 and () in operands[0] and len(operands[0]) == 1, "Abs expects one operand of function type: " + repr(operands)) # Take absolute value of operand. val = operands[0][()] new_val = create_symbol(format["absolute value"](str(val)), val.t, val, 1) return {(): new_val}
def not_condition(self, o, *operands): # This is a Condition but not a BinaryCondition, and the operand will be another Condition # Get condition expression and do safety checks. # Might be a bit too strict? c, = operands ffc_assert(len(c) == 1 and firstkey(c) == (),\ "Condition for NotCondition should only be one function: " + repr(c)) if self.parameters["format"] == "pyop2": return {(): create_expression(ast.Not, [c[()]], c[()].t)} var = format["not"](str(c[()])) sym = create_symbol(var, c[()].t, base_op=c[()].ops() + 1, iden=var) return {(): sym}
def cell_volume(self, o, *operands): # Safety check. ffc_assert(not operands, "Didn't expect any operands for CellVolume: " + repr(operands)) # FIXME: KBO: This has to change for higher order elements # detJ = format["det(J)"](self.restriction) # volume = format["absolute value"](detJ) # self.trans_set.add(detJ) volume = format["cell volume"](self.restriction) self.trans_set.add(volume) return {():create_symbol(volume, GEO)}
def facet_normal(self, o): components = self.component() # Safety check. ffc_assert(len(components) == 1, "FacetNormal expects 1 component index: " + repr(components)) # Handle 1D as a special case. # FIXME: KBO: This has to change for mD elements in R^n : m < # n if self.gdim == 1: # FIXME: MSA UFL uses shape (1,) now, can we remove the special case here then? normal_component = format["normal component"](self.restriction, "") else: normal_component = format["normal component"](self.restriction, components[0]) self.trans_set.add(normal_component) return {(): create_symbol(normal_component, GEO)}
def facet_normal(self, o): components = self.component() # Safety check. ffc_assert( len(components) == 1, "FacetNormal expects 1 component index: " + repr(components)) # Handle 1D as a special case. # FIXME: KBO: This has to change for mD elements in R^n : m < n if self.gdim == 1: # FIXME: MSA UFL uses shape (1,) now, can we remove the special case here then? normal_component = format["normal component"](self.restriction, "") else: normal_component = format["normal component"](self.restriction, components[0]) self.trans_set.add(normal_component) return {(): create_symbol(normal_component, GEO)}
def _bessel_function(self, operands, format_function): # TODO: Are these safety checks needed? # TODO: work on reference instead of copies? (like math_function) ffc_assert(len(operands) == 2,\ "BesselFunctions expect two operands of function type: " + repr(operands)) nu, x = operands ffc_assert(len(nu) == 1 and () in nu,\ "Expecting one operand of function type as first argument to BesselFunction : " + repr(nu)) ffc_assert(len(x) == 1 and () in x,\ "Expecting one operand of function type as second argument to BesselFunction : " + repr(x)) nu = nu[()] x = x[()] if nu is None: nu = format["floating point"](0.0) if x is None: x = format["floating point"](0.0) sym = create_symbol(format_function(x,nu), x.t, x, 1) return {():sym}
def binary_condition(self, o, *operands): # Get LHS and RHS expressions and do safety checks. Might be # a bit too strict? lhs, rhs = operands ffc_assert(len(lhs) == 1 and firstkey(lhs) == (), "LHS of Condtion should only be one function: " + repr(lhs)) ffc_assert(len(rhs) == 1 and firstkey(rhs) == (), "RHS of Condtion should only be one function: " + repr(rhs)) # Map names from UFL to cpp.py. name_map = {"==": "is equal", "!=": "not equal", "<": "less than", ">": "greater than", "<=": "less equal", ">=": "greater equal", "&&": "and", "||": "or"} # Get the minimum type t = min(lhs[()].t, rhs[()].t) ops = lhs[()].ops() + rhs[()].ops() + 1 cond = str(lhs[()]) + format[name_map[o._name]] + str(rhs[()]) sym = create_symbol(format["grouping"](cond), t, base_op=ops) return {(): sym}
def binary_condition(self, o, *operands): # Get LHS and RHS expressions and do safety checks. # Might be a bit too strict? lhs, rhs = operands ffc_assert(len(lhs) == 1 and firstkey(lhs) == (),\ "LHS of Condtion should only be one function: " + repr(lhs)) ffc_assert(len(rhs) == 1 and firstkey(rhs) == (),\ "RHS of Condtion should only be one function: " + repr(rhs)) # Map names from UFL to cpp.py. name_map = {"==":"is equal", "!=":"not equal",\ "<":"less than", ">":"greater than",\ "<=":"less equal", ">=":"greater equal",\ "&&": "and", "||": "or"} # Get the minimum type t = min(lhs[()].t, rhs[()].t) ops = lhs[()].ops() + rhs[()].ops() + 1 if self.parameters["format"] == "pyop2": name_map = { "==": ast.Eq, "!=": ast.NEq, "<": ast.Less, ">": ast.Greater, "<=": ast.LessEq, ">=": ast.GreaterEq, "&&": ast.And, "||": ast.Or } expr = create_expression(name_map[o._name], [lhs[()], rhs[()]], t) return {(): expr} cond = str(lhs[()]) + format[name_map[o._name]] + str(rhs[()]) var = format["grouping"](cond) sym = create_symbol(var, t, base_op=ops, iden=var) return {(): sym}
def create_function(self, ufl_function, derivatives, component, local_comp, local_offset, ffc_element, is_quad_element, transformation, multiindices, tdim, gdim, avg): "Create code for basis functions, and update relevant tables of used basis." ffc_assert(ufl_function in self._function_replace_values, "Expecting ufl_function to have been mapped prior to this call.") # Prefetch formats to speed up code generation. f_transform = format["transform"] f_detJ = format["det(J)"] # Reset code code = [] # Handle affine mappings. if transformation == "affine": # Loop derivatives and get multi indices. for multi in multiindices: deriv = [multi.count(i) for i in range(tdim)] if not any(deriv): deriv = [] # Create function name. function_name = self._create_function_name(component, deriv, avg, is_quad_element, ufl_function, ffc_element) if function_name: # Add transformation if needed. code.append(self.__apply_transform(function_name, derivatives, multi, tdim, gdim)) # Handle non-affine mappings. else: ffc_assert(avg is None, "Taking average is not supported for non-affine mappings.") # Loop derivatives and get multi indices. for multi in multiindices: deriv = [multi.count(i) for i in range(tdim)] if not any(deriv): deriv = [] if transformation in ["covariant piola", "contravariant piola"]: for c in range(tdim): function_name = self._create_function_name(c + local_offset, deriv, avg, is_quad_element, ufl_function, ffc_element) if function_name: # Multiply basis by appropriate transform. if transformation == "covariant piola": dxdX = create_symbol(f_transform("JINV", c, local_comp, tdim, gdim, self.restriction), GEO) function_name = create_product([dxdX, function_name]) elif transformation == "contravariant piola": detJ = create_fraction(create_float(1), create_symbol(f_detJ(self.restriction), GEO)) dXdx = create_symbol(f_transform("J", local_comp, c, gdim, tdim, self.restriction), GEO) function_name = create_product([detJ, dXdx, function_name]) # Add transformation if needed. code.append(self.__apply_transform(function_name, derivatives, multi, tdim, gdim)) elif transformation == "double covariant piola": # g_ij = (Jinv)_ki G_kl (Jinv)lj i = local_comp // tdim j = local_comp % tdim for k in range(tdim): for l in range(tdim): # Create mapping and basis name. function_name = self._create_function_name( k * tdim + l + local_offset, deriv, avg, is_quad_element, ufl_function, ffc_element) J1 = create_symbol( f_transform("JINV", k, i, tdim, gdim, self.restriction), GEO) J2 = create_symbol( f_transform("JINV", l, j, tdim, gdim, self.restriction), GEO) function_name = create_product([J1, function_name, J2]) # Add transformation if needed. code.append(self.__apply_transform( function_name, derivatives, multi, tdim, gdim)) elif transformation == "double contravariant piola": # g_ij = (detJ)^(-2) J_ik G_kl J_jl i = local_comp // tdim j = local_comp % tdim for k in range(tdim): for l in range(tdim): # Create mapping and basis name. function_name = self._create_function_name( k * tdim + l + local_offset, deriv, avg, is_quad_element, ufl_function, ffc_element) J1 = create_symbol( f_transform("J", i, k, tdim, gdim, self.restriction), GEO) J2 = create_symbol( f_transform("J", j, l, tdim, gdim, self.restriction), GEO) invdetJ = create_fraction( create_float(1), create_symbol(f_detJ(self.restriction), GEO)) function_name = create_product([invdetJ, invdetJ, J1, function_name, J2]) # Add transformation if needed. code.append(self.__apply_transform(function_name, derivatives, multi, tdim, gdim)) else: error("Transformation is not supported: ", repr(transformation)) if not code: return create_float(0.0) elif len(code) > 1: code = create_sum(code) else: code = code[0] return code
def create_argument(self, ufl_argument, derivatives, component, local_comp, local_offset, ffc_element, transformation, multiindices, tdim, gdim, avg): "Create code for basis functions, and update relevant tables of used basis." # Prefetch formats to speed up code generation. f_transform = format["transform"] f_detJ = format["det(J)"] # Reset code code = {} # Affine mapping if transformation == "affine": # Loop derivatives and get multi indices. for multi in multiindices: deriv = [multi.count(i) for i in range(tdim)] if not any(deriv): deriv = [] # Create mapping and basis name. mapping, basis = self._create_mapping_basis(component, deriv, avg, ufl_argument, ffc_element) if mapping not in code: code[mapping] = [] if basis is not None: # Add transformation if needed. code[mapping].append(self.__apply_transform(basis, derivatives, multi, tdim, gdim)) # Handle non-affine mappings. else: ffc_assert(avg is None, "Taking average is not supported for non-affine mappings.") # Loop derivatives and get multi indices. for multi in multiindices: deriv = [multi.count(i) for i in range(tdim)] if not any(deriv): deriv = [] if transformation in ["covariant piola", "contravariant piola"]: for c in range(tdim): # Create mapping and basis name. mapping, basis = self._create_mapping_basis(c + local_offset, deriv, avg, ufl_argument, ffc_element) if mapping not in code: code[mapping] = [] if basis is not None: # Multiply basis by appropriate transform. if transformation == "covariant piola": dxdX = create_symbol(f_transform("JINV", c, local_comp, tdim, gdim, self.restriction), GEO) basis = create_product([dxdX, basis]) elif transformation == "contravariant piola": detJ = create_fraction(create_float(1), create_symbol(f_detJ(self.restriction), GEO)) dXdx = create_symbol(f_transform("J", local_comp, c, gdim, tdim, self.restriction), GEO) basis = create_product([detJ, dXdx, basis]) # Add transformation if needed. code[mapping].append(self.__apply_transform(basis, derivatives, multi, tdim, gdim)) elif transformation == "double covariant piola": # g_ij = (Jinv)_ki G_kl (Jinv)lj i = local_comp // tdim j = local_comp % tdim for k in range(tdim): for l in range(tdim): # Create mapping and basis name. mapping, basis = self._create_mapping_basis( k * tdim + l + local_offset, deriv, avg, ufl_argument, ffc_element) if mapping not in code: code[mapping] = [] if basis is not None: J1 = create_symbol( f_transform("JINV", k, i, tdim, gdim, self.restriction), GEO) J2 = create_symbol( f_transform("JINV", l, j, tdim, gdim, self.restriction), GEO) basis = create_product([J1, basis, J2]) # Add transformation if needed. code[mapping].append( self.__apply_transform( basis, derivatives, multi, tdim, gdim)) elif transformation == "double contravariant piola": # g_ij = (detJ)^(-2) J_ik G_kl J_jl i = local_comp // tdim j = local_comp % tdim for k in range(tdim): for l in range(tdim): # Create mapping and basis name. mapping, basis = self._create_mapping_basis( k * tdim + l + local_offset, deriv, avg, ufl_argument, ffc_element) if mapping not in code: code[mapping] = [] if basis is not None: J1 = create_symbol( f_transform("J", i, k, gdim, tdim, self.restriction), GEO) J2 = create_symbol( f_transform("J", j, l, gdim, tdim, self.restriction), GEO) invdetJ = create_fraction( create_float(1), create_symbol(f_detJ(self.restriction), GEO)) basis = create_product([invdetJ, invdetJ, J1, basis, J2]) # Add transformation if needed. code[mapping].append( self.__apply_transform( basis, derivatives, multi, tdim, gdim)) else: error("Transformation is not supported: " + repr(transformation)) # Add sums and group if necessary. for key, val in list(code.items()): if len(val) > 1: code[key] = create_sum(val) else: code[key] = val[0] return code
def _create_symbol(self, symbol, domain): return {(): create_symbol(symbol, domain)}
def create_argument(self, ufl_argument, derivatives, component, local_comp, local_offset, ffc_element, transformation, multiindices, tdim, gdim, avg): "Create code for basis functions, and update relevant tables of used basis." ffc_assert(ufl_argument in self._function_replace_values, "Expecting ufl_argument to have been mapped prior to this call.") # Prefetch formats to speed up code generation. f_transform = format["transform"] f_detJ = format["det(J)"] # Reset code code = {} # Affine mapping if transformation == "affine": # Loop derivatives and get multi indices. for multi in multiindices: deriv = [multi.count(i) for i in range(self.tdim)] if not any(deriv): deriv = [] # Create mapping and basis name. mapping, basis = self._create_mapping_basis(component, deriv, avg, ufl_argument, ffc_element) if not mapping in code: code[mapping] = [] if basis is not None: # Add transformation if needed. code[mapping].append(self.__apply_transform(basis, derivatives, multi, tdim, gdim)) # Handle non-affine mappings. else: ffc_assert(avg is None, "Taking average is not supported for non-affine mappings.") # Loop derivatives and get multi indices. for multi in multiindices: deriv = [multi.count(i) for i in range(self.tdim)] if not any(deriv): deriv = [] for c in range(self.tdim): # Create mapping and basis name. mapping, basis = self._create_mapping_basis(c + local_offset, deriv, avg, ufl_argument, ffc_element) if not mapping in code: code[mapping] = [] if basis is not None: # Multiply basis by appropriate transform. if transformation == "covariant piola": dxdX = create_symbol(f_transform("JINV", c, local_comp, tdim, gdim, self.restriction), GEO) basis = create_product([dxdX, basis]) elif transformation == "contravariant piola": detJ = create_fraction(create_float(1), create_symbol(f_detJ(self.restriction), GEO)) dXdx = create_symbol(f_transform("J", local_comp, c, gdim, tdim, self.restriction), GEO) basis = create_product([detJ, dXdx, basis]) else: error("Transformation is not supported: " + repr(transformation)) # Add transformation if needed. code[mapping].append(self.__apply_transform(basis, derivatives, multi, tdim, gdim)) # Add sums and group if necessary. for key, val in code.items(): if len(val) > 1: code[key] = create_sum(val) else: code[key] = val[0] return code
def create_function(self, ufl_function, derivatives, component, local_comp, local_offset, ffc_element, is_quad_element, transformation, multiindices, tdim, gdim, avg): "Create code for basis functions, and update relevant tables of used basis." ffc_assert( ufl_function in self._function_replace_values, "Expecting ufl_function to have been mapped prior to this call.") # Prefetch formats to speed up code generation. f_transform = format["transform"] f_detJ = format["det(J)"] # Reset code code = [] # Handle affine mappings. if transformation == "affine": # Loop derivatives and get multi indices. for multi in multiindices: deriv = [multi.count(i) for i in range(tdim)] if not any(deriv): deriv = [] # Create function name. function_name = self._create_function_name( component, deriv, avg, is_quad_element, ufl_function, ffc_element) if function_name: # Add transformation if needed. code.append( self.__apply_transform(function_name, derivatives, multi, tdim, gdim)) # Handle non-affine mappings. else: ffc_assert( avg is None, "Taking average is not supported for non-affine mappings.") # Loop derivatives and get multi indices. for multi in multiindices: deriv = [multi.count(i) for i in range(tdim)] if not any(deriv): deriv = [] if transformation in [ "covariant piola", "contravariant piola" ]: for c in range(tdim): function_name = self._create_function_name( c + local_offset, deriv, avg, is_quad_element, ufl_function, ffc_element) if function_name: # Multiply basis by appropriate transform. if transformation == "covariant piola": dxdX = create_symbol( f_transform("JINV", c, local_comp, tdim, gdim, self.restriction), GEO) function_name = create_product( [dxdX, function_name]) elif transformation == "contravariant piola": detJ = create_fraction( create_float(1), create_symbol(f_detJ(self.restriction), GEO)) dXdx = create_symbol( f_transform("J", local_comp, c, gdim, tdim, self.restriction), GEO) function_name = create_product( [detJ, dXdx, function_name]) # Add transformation if needed. code.append( self.__apply_transform(function_name, derivatives, multi, tdim, gdim)) elif transformation == "double covariant piola": # g_ij = (Jinv)_ki G_kl (Jinv)lj i = local_comp // tdim j = local_comp % tdim for k in range(tdim): for l in range(tdim): # Create mapping and basis name. function_name = self._create_function_name( k * tdim + l + local_offset, deriv, avg, is_quad_element, ufl_function, ffc_element) J1 = create_symbol( f_transform("JINV", k, i, tdim, gdim, self.restriction), GEO) J2 = create_symbol( f_transform("JINV", l, j, tdim, gdim, self.restriction), GEO) function_name = create_product( [J1, function_name, J2]) # Add transformation if needed. code.append( self.__apply_transform(function_name, derivatives, multi, tdim, gdim)) elif transformation == "double contravariant piola": # g_ij = (detJ)^(-2) J_ik G_kl J_jl i = local_comp // tdim j = local_comp % tdim for k in range(tdim): for l in range(tdim): # Create mapping and basis name. function_name = self._create_function_name( k * tdim + l + local_offset, deriv, avg, is_quad_element, ufl_function, ffc_element) J1 = create_symbol( f_transform("J", i, k, tdim, gdim, self.restriction), GEO) J2 = create_symbol( f_transform("J", j, l, tdim, gdim, self.restriction), GEO) invdetJ = create_fraction( create_float(1), create_symbol(f_detJ(self.restriction), GEO)) function_name = create_product( [invdetJ, invdetJ, J1, function_name, J2]) # Add transformation if needed. code.append( self.__apply_transform(function_name, derivatives, multi, tdim, gdim)) else: error("Transformation is not supported: ", repr(transformation)) if not code: return create_float(0.0) elif len(code) > 1: code = create_sum(code) else: code = code[0] return code
def circumradius(self, o): # FIXME: KBO: This has to change for higher order elements circumradius = format["circumradius"](self.restriction) self.trans_set.add(circumradius) return {(): create_symbol(circumradius, GEO)}
def create_function(self, ufl_function, derivatives, component, local_comp, local_offset, ffc_element, is_quad_element, transformation, multiindices, tdim, gdim, avg): "Create code for basis functions, and update relevant tables of used basis." ffc_assert( ufl_function in self._function_replace_values, "Expecting ufl_function to have been mapped prior to this call.") # Prefetch formats to speed up code generation. f_transform = format["transform"] f_detJ = format["det(J)"] # Reset code code = [] # Handle affine mappings. if transformation == "affine": # Loop derivatives and get multi indices. for multi in multiindices: deriv = [multi.count(i) for i in range(tdim)] if not any(deriv): deriv = [] # Create function name. function_name = self._create_function_name( component, deriv, avg, is_quad_element, ufl_function, ffc_element) if function_name: # Add transformation if needed. code.append( self.__apply_transform(function_name, derivatives, multi, tdim, gdim)) # Handle non-affine mappings. else: ffc_assert( avg is None, "Taking average is not supported for non-affine mappings.") # Loop derivatives and get multi indices. for multi in multiindices: deriv = [multi.count(i) for i in range(tdim)] if not any(deriv): deriv = [] for c in range(tdim): function_name = self._create_function_name( c + local_offset, deriv, avg, is_quad_element, ufl_function, ffc_element) if function_name: # Multiply basis by appropriate transform. if transformation == "covariant piola": dxdX = create_symbol( f_transform("JINV", c, local_comp, tdim, gdim, self.restriction), GEO, loop_index=[c * gdim + local_comp], iden="K%s" % _choose_map(self.restriction)) function_name = create_product( [dxdX, function_name]) elif transformation == "contravariant piola": detJ = create_fraction( create_float(1), create_symbol(f_detJ(self.restriction), GEO, iden=f_detJ(self.restriction))) dXdx = create_symbol(f_transform("J", local_comp, c, gdim, tdim, self.restriction), GEO, \ loop_index=[local_comp*tdim + c], iden="J%s" % _choose_map(self.restriction)) function_name = create_product( [detJ, dXdx, function_name]) else: error("Transformation is not supported: ", repr(transformation)) # Add transformation if needed. code.append( self.__apply_transform(function_name, derivatives, multi, tdim, gdim)) if not code: return create_float(0.0) elif len(code) > 1: code = create_sum(code) else: code = code[0] return code
def create_argument(self, ufl_argument, derivatives, component, local_comp, local_offset, ffc_element, transformation, multiindices, tdim, gdim, avg): "Create code for basis functions, and update relevant tables of used basis." # Prefetch formats to speed up code generation. f_transform = format["transform"] f_detJ = format["det(J)"] # Reset code code = {} # Affine mapping if transformation == "affine": # Loop derivatives and get multi indices. for multi in multiindices: deriv = [multi.count(i) for i in range(tdim)] if not any(deriv): deriv = [] # Create mapping and basis name. mapping, basis = self._create_mapping_basis( component, deriv, avg, ufl_argument, ffc_element) if mapping not in code: code[mapping] = [] if basis is not None: # Add transformation if needed. code[mapping].append( self.__apply_transform(basis, derivatives, multi, tdim, gdim)) # Handle non-affine mappings. else: ffc_assert( avg is None, "Taking average is not supported for non-affine mappings.") # Loop derivatives and get multi indices. for multi in multiindices: deriv = [multi.count(i) for i in range(tdim)] if not any(deriv): deriv = [] if transformation in [ "covariant piola", "contravariant piola" ]: for c in range(tdim): # Create mapping and basis name. mapping, basis = self._create_mapping_basis( c + local_offset, deriv, avg, ufl_argument, ffc_element) if mapping not in code: code[mapping] = [] if basis is not None: # Multiply basis by appropriate transform. if transformation == "covariant piola": dxdX = create_symbol( f_transform("JINV", c, local_comp, tdim, gdim, self.restriction), GEO) basis = create_product([dxdX, basis]) elif transformation == "contravariant piola": detJ = create_fraction( create_float(1), create_symbol(f_detJ(self.restriction), GEO)) dXdx = create_symbol( f_transform("J", local_comp, c, gdim, tdim, self.restriction), GEO) basis = create_product([detJ, dXdx, basis]) # Add transformation if needed. code[mapping].append( self.__apply_transform(basis, derivatives, multi, tdim, gdim)) elif transformation == "double covariant piola": # g_ij = (Jinv)_ki G_kl (Jinv)lj i = local_comp // tdim j = local_comp % tdim for k in range(tdim): for l in range(tdim): # Create mapping and basis name. mapping, basis = self._create_mapping_basis( k * tdim + l + local_offset, deriv, avg, ufl_argument, ffc_element) if mapping not in code: code[mapping] = [] if basis is not None: J1 = create_symbol( f_transform("JINV", k, i, tdim, gdim, self.restriction), GEO) J2 = create_symbol( f_transform("JINV", l, j, tdim, gdim, self.restriction), GEO) basis = create_product([J1, basis, J2]) # Add transformation if needed. code[mapping].append( self.__apply_transform( basis, derivatives, multi, tdim, gdim)) elif transformation == "double contravariant piola": # g_ij = (detJ)^(-2) J_ik G_kl J_jl i = local_comp // tdim j = local_comp % tdim for k in range(tdim): for l in range(tdim): # Create mapping and basis name. mapping, basis = self._create_mapping_basis( k * tdim + l + local_offset, deriv, avg, ufl_argument, ffc_element) if mapping not in code: code[mapping] = [] if basis is not None: J1 = create_symbol( f_transform("J", i, k, gdim, tdim, self.restriction), GEO) J2 = create_symbol( f_transform("J", j, l, gdim, tdim, self.restriction), GEO) invdetJ = create_fraction( create_float(1), create_symbol(f_detJ(self.restriction), GEO)) basis = create_product( [invdetJ, invdetJ, J1, basis, J2]) # Add transformation if needed. code[mapping].append( self.__apply_transform( basis, derivatives, multi, tdim, gdim)) else: error("Transformation is not supported: " + repr(transformation)) # Add sums and group if necessary. for key, val in list(code.items()): if len(val) > 1: code[key] = create_sum(val) else: code[key] = val[0] return code
def _extract_variables(val, basis_consts, ip_consts, geo_consts, t_set, optimisation): f_G = format["geometry constant"] f_I = format["ip constant"] f_B = format["basis constant"] if val._prec == 0: return val elif val._prec == 1: if val.base_expr is None: return val new_base = _extract_variables(val.base_expr, basis_consts, ip_consts, geo_consts, t_set, optimisation) new_sym = create_symbol(val.v, val.t, new_base, val.base_op) if new_sym.t == BASIS: return _reduce_expression(new_sym, [], basis_consts, f_B, True) elif new_sym.t == IP: return _reduce_expression(new_sym, [], ip_consts, f_I, True) elif new_sym.t == GEO: return _reduce_expression(new_sym, [], geo_consts, f_G, True) # First handle child classes of product and sum. elif val._prec in (2, 3): new_vars = [] for v in val.vrs: new_vars.append( _extract_variables(v, basis_consts, ip_consts, geo_consts, t_set, optimisation)) if val._prec == 2: new_val = create_product(new_vars) if val._prec == 3: new_val = create_sum(new_vars) elif val._prec == 4: num = _extract_variables(val.num, basis_consts, ip_consts, geo_consts, t_set, optimisation) denom = _extract_variables(val.denom, basis_consts, ip_consts, geo_consts, t_set, optimisation) return create_fraction(num, denom) else: error("Unknown symbolic type: %s" % repr(val)) # Sort variables of product and sum. b_c, i_c, g_c = [], [], [] for v in new_val.vrs: if v.t == BASIS: if optimisation == "precompute_basis_const": b_c.append(v) elif v.t == IP: i_c.append(v) else: g_c.append(v) vrs = new_val.vrs[:] for v in g_c + i_c + b_c: vrs.remove(v) i_c.extend(_reduce_expression(new_val, g_c, geo_consts, f_G)) vrs.extend(_reduce_expression(new_val, i_c, ip_consts, f_I)) vrs.extend(_reduce_expression(new_val, b_c, basis_consts, f_B)) # print "b_c: " # for b in b_c: # print b # print "basis" # for k,v in basis_consts.items(): # print "k: ", k # print "v: ", v # print "geo" # for k,v in geo_consts.items(): # print "k: ", k # print "v: ", v # print "ret val: ", val if len(vrs) > 1: if new_val._prec == 2: new_object = create_product(vrs) elif new_val._prec == 3: new_object = create_sum(vrs) else: error("Must have product or sum here: %s" % repr(new_val)) if new_object.t == BASIS: if optimisation == "precompute_ip_const": return new_object elif optimisation == "precompute_basis_const": return _reduce_expression(new_object, [], basis_consts, f_B, True) elif new_object.t == IP: return _reduce_expression(new_object, [], ip_consts, f_I, True) elif new_object.t == GEO: return _reduce_expression(new_object, [], geo_consts, f_G, True) return vrs[0]
def _compute_basisvalues(data, dof_data): """From FIAT_NEW.expansions.""" UNROLL = True # Prefetch formats to speed up code generation. f_comment = format["comment"] f_add = format["add"] f_mul = format["mul"] f_imul = format["imul"] f_sub = format["sub"] f_group = format["grouping"] f_assign = format["assign"] f_sqrt = format["sqrt"]["ufc"] f_x = format["x coordinate"] f_y = format["y coordinate"] f_z = format["z coordinate"] f_double = format["float declaration"] f_basisvalue = format["basisvalues"] f_component = format["component"] f_float = format["floating point"] f_uint = format["uint declaration"] f_tensor = format["tabulate tensor"] f_loop = format["generate loop"] f_decl = format["declaration"] f_tmp = format["tmp value"] f_int = format["int"] f_r = format["free indices"][0] # Create temporary values. f1, f2, f3, f4, f5 = [create_symbol(f_tmp(i), CONST) for i in range(0,5)] # Get embedded degree. embedded_degree = dof_data["embedded_degree"] # Create helper symbols. symbol_p = create_symbol(f_r, CONST) symbol_x = create_symbol(f_x, CONST) symbol_y = create_symbol(f_y, CONST) symbol_z = create_symbol(f_z, CONST) int_n1 = f_int(embedded_degree + 1) float_1 = create_float(1) float_2 = create_float(2) float_0_5 = create_float(0.5) float_0_25 = create_float(0.25) # Initialise return code. code = [""] # Create zero array for basisvalues. # Get number of members of the expansion set. num_mem = dof_data["num_expansion_members"] code += [f_comment("Array of basisvalues")] code += [f_decl(f_double, f_component(f_basisvalue, num_mem), f_tensor([0.0]*num_mem))] # Declare helper variables, will be removed if not used. code += ["", f_comment("Declare helper variables")] # Keeping this here to avoid changing references # Get the element cell name element_cellname = data["cellname"] def _jrc(a, b, n): an = float( ( 2*n+1+a+b)*(2*n+2+a+b))/ float( 2*(n+1)*(n+1+a+b)) bn = float( (a*a-b*b) * (2*n+1+a+b))/ float( 2*(n+1)*(2*n+a+b)*(n+1+a+b) ) cn = float( (n+a)*(n+b)*(2*n+2+a+b))/ float( (n+1)*(n+1+a+b)*(2*n+a+b) ) return (an,bn,cn) # 1D if (element_cellname == "interval"): # FIAT_NEW.expansions.LineExpansionSet. # FIAT_NEW code # psitilde_as = jacobi.eval_jacobi_batch(0,0,n,ref_pts) # FIAT_NEW.jacobi.eval_jacobi_batch(a,b,n,xs) # The initial value basisvalue 0 is always 1.0 # FIAT_NEW code # for ii in range(result.shape[1]): # result[0,ii] = 1.0 + xs[ii,0] - xs[ii,0] code += ["", f_comment("Compute basisvalues")] code += [f_assign(f_component(f_basisvalue, 0), f_float(1.0))] # Only continue if the embedded degree is larger than zero. if embedded_degree > 0: # FIAT_NEW.jacobi.eval_jacobi_batch(a,b,n,xs). # result[1,:] = 0.5 * ( a - b + ( a + b + 2.0 ) * xsnew ) # The initial value basisvalue 1 is always x code += [f_assign(f_component(f_basisvalue, 1), f_x)] # Only active is embedded_degree > 1. if embedded_degree > 1: # FIAT_NEW.jacobi.eval_jacobi_batch(a,b,n,xs). # apb = a + b (equal to 0 because of function arguments) # for k in range(2,n+1): # a1 = 2.0 * k * ( k + apb ) * ( 2.0 * k + apb - 2.0 ) # a2 = ( 2.0 * k + apb - 1.0 ) * ( a * a - b * b ) # a3 = ( 2.0 * k + apb - 2.0 ) \ # * ( 2.0 * k + apb - 1.0 ) \ # * ( 2.0 * k + apb ) # a4 = 2.0 * ( k + a - 1.0 ) * ( k + b - 1.0 ) \ # * ( 2.0 * k + apb ) # a2 = a2 / a1 # a3 = a3 / a1 # a4 = a4 / a1 # result[k,:] = ( a2 + a3 * xsnew ) * result[k-1,:] \ # - a4 * result[k-2,:] # The below implements the above (with a = b = apb = 0) for r in range(2, embedded_degree+1): # Define helper variables a1 = 2.0*r*r*(2.0*r - 2.0) a3 = ((2.0*r - 2.0)*(2.0*r - 1.0 )*(2.0*r))/a1 a4 = (2.0*(r - 1.0)*(r - 1.0)*(2.0*r))/a1 assign_to = f_component(f_basisvalue, r) assign_from = f_sub([f_mul([f_x, f_component(f_basisvalue, r-1), f_float(a3)]), f_mul([f_component(f_basisvalue, r-2), f_float(a4)])]) code += [f_assign(assign_to, assign_from)] # Scale values. # FIAT_NEW.expansions.LineExpansionSet. # FIAT_NEW code # results = numpy.zeros( ( n+1 , len(pts) ) , type( pts[0][0] ) ) # for k in range( n + 1 ): # results[k,:] = psitilde_as[k,:] * math.sqrt( k + 0.5 ) lines = [] loop_vars = [(str(symbol_p), 0, int_n1)] # Create names. basis_k = create_symbol(f_component(f_basisvalue, str(symbol_p)), CONST) # Compute value. fac1 = create_symbol( f_sqrt(str(symbol_p + float_0_5)), CONST ) lines += [format["imul"](str(basis_k), str(fac1))] # Create loop (block of lines). code += f_loop(lines, loop_vars) # 2D elif (element_cellname == "triangle"): # FIAT_NEW.expansions.TriangleExpansionSet. # Compute helper factors # FIAT_NEW code # f1 = (1.0+2*x+y)/2.0 # f2 = (1.0 - y) / 2.0 # f3 = f2**2 fac1 = create_fraction(float_1 + float_2*symbol_x + symbol_y, float_2) fac2 = create_fraction(float_1 - symbol_y, float_2) code += [f_decl(f_double, str(f1), fac1)] code += [f_decl(f_double, str(f2), fac2)] code += [f_decl(f_double, str(f3), f2*f2)] code += ["", f_comment("Compute basisvalues")] # The initial value basisvalue 0 is always 1.0. # FIAT_NEW code # for ii in range( results.shape[1] ): # results[0,ii] = 1.0 + apts[ii,0]-apts[ii,0]+apts[ii,1]-apts[ii,1] code += [f_assign(f_component(f_basisvalue, 0), f_float(1.0))] def _idx2d(p, q): return (p + q)*(p + q + 1)//2 + q # Only continue if the embedded degree is larger than zero. if embedded_degree > 0: # The initial value of basisfunction 1 is equal to f1. # FIAT_NEW code # results[idx(1,0),:] = f1 code += [f_assign(f_component(f_basisvalue, 1), str(f1))] # NOTE: KBO: The order of the loops is VERY IMPORTANT!! # Only active is embedded_degree > 1. if embedded_degree > 1: # FIAT_NEW code (loop 1 in FIAT) # for p in range(1,n): # a = (2.0*p+1)/(1.0+p) # b = p / (p+1.0) # results[idx(p+1,0)] = a * f1 * results[idx(p,0),:] \ # - p/(1.0+p) * f3 *results[idx(p-1,0),:] # FIXME: KBO: Is there an error in FIAT? why is b not used? for r in range(1, embedded_degree): rr = _idx2d((r + 1), 0) assign_to = f_component(f_basisvalue, rr) ss = _idx2d(r, 0) tt = _idx2d((r - 1), 0) A = (2*r + 1.0)/(r + 1) B = r/(1.0 + r) v1 = f_mul([f_component(f_basisvalue, ss), f_float(A), str(f1)]) v2 = f_mul([f_component(f_basisvalue, tt), f_float(B), str(f3)]) assign_from = f_sub([v1, v2]) code += [f_assign(assign_to, assign_from)] # FIAT_NEW code (loop 2 in FIAT). # for p in range(n): # results[idx(p,1),:] = 0.5 * (1+2.0*p+(3.0+2.0*p)*y) \ # * results[idx(p,0)] for r in range(0, embedded_degree): # (p+q)*(p+q+1)//2 + q rr = _idx2d(r, 1) assign_to = f_component(f_basisvalue, rr) ss = _idx2d(r, 0) A = 0.5*(1 + 2*r) B = 0.5*(3 + 2*r) C = f_add([f_float(A), f_mul([f_float(B), str(symbol_y)])]) assign_from = f_mul([f_component(f_basisvalue, ss), f_group(C)]) code += [f_assign(assign_to, assign_from)] # Only active is embedded_degree > 1. if embedded_degree > 1: # FIAT_NEW code (loop 3 in FIAT). # for p in range(n-1): # for q in range(1,n-p): # (a1,a2,a3) = jrc(2*p+1,0,q) # results[idx(p,q+1),:] \ # = ( a1 * y + a2 ) * results[idx(p,q)] \ # - a3 * results[idx(p,q-1)] for r in range(0, embedded_degree - 1): for s in range(1, embedded_degree - r): rr = _idx2d(r, (s + 1)) ss = _idx2d(r, s) tt = _idx2d(r, s - 1) A, B, C = _jrc(2*r + 1, 0, s) assign_to = f_component(f_basisvalue, rr) assign_from = f_sub([f_mul([f_component(f_basisvalue, ss), f_group(f_add([f_float(B), f_mul([str(symbol_y), f_float(A)])]))]), f_mul([f_component(f_basisvalue, tt), f_float(C)])]) code += [f_assign(assign_to, assign_from)] # FIAT_NEW code (loop 4 in FIAT). # for p in range(n+1): # for q in range(n-p+1): # results[idx(p,q),:] *= math.sqrt((p+0.5)*(p+q+1.0)) n1 = embedded_degree + 1 for r in range(0, n1): for s in range(0, n1 - r): rr = _idx2d(r, s) A = (r + 0.5)*(r + s + 1) assign_to = f_component(f_basisvalue, rr) code += [f_imul(assign_to, f_sqrt(A))] # 3D elif (element_cellname == "tetrahedron"): # FIAT_NEW code (compute index function) TetrahedronExpansionSet. # def idx(p,q,r): # return (p+q+r)*(p+q+r+1)*(p+q+r+2)//6 + (q+r)*(q+r+1)//2 + r def _idx3d(p, q, r): return (p+q+r)*(p+q+r+1)*(p+q+r+2)//6 + (q+r)*(q+r+1)//2 + r # FIAT_NEW.expansions.TetrahedronExpansionSet. # Compute helper factors. # FIAT_NEW code # factor1 = 0.5 * ( 2.0 + 2.0*x + y + z ) # factor2 = (0.5*(y+z))**2 # factor3 = 0.5 * ( 1 + 2.0 * y + z ) # factor4 = 0.5 * ( 1 - z ) # factor5 = factor4 ** 2 fac1 = create_product([float_0_5, float_2 + float_2*symbol_x + symbol_y + symbol_z]) fac2 = create_product([float_0_25, symbol_y + symbol_z, symbol_y + symbol_z]) fac3 = create_product([float_0_5, float_1 + float_2*symbol_y + symbol_z]) fac4 = create_product([float_0_5, float_1 - symbol_z]) code += [f_decl(f_double, str(f1), fac1)] code += [f_decl(f_double, str(f2), fac2)] code += [f_decl(f_double, str(f3), fac3)] code += [f_decl(f_double, str(f4), fac4)] code += [f_decl(f_double, str(f5), f4*f4)] code += ["", f_comment("Compute basisvalues")] # The initial value basisvalue 0 is always 1.0. # FIAT_NEW code # for ii in range( results.shape[1] ): # results[0,ii] = 1.0 + apts[ii,0]-apts[ii,0]+apts[ii,1]-apts[ii,1] code += [f_assign(f_component(f_basisvalue, 0), f_float(1.0))] # Only continue if the embedded degree is larger than zero. if embedded_degree > 0: # The initial value of basisfunction 1 is equal to f1. # FIAT_NEW code # results[idx(1,0),:] = f1 code += [f_assign(f_component(f_basisvalue, 1), str(f1))] # NOTE: KBO: The order of the loops is VERY IMPORTANT!! # Only active is embedded_degree > 1 if embedded_degree > 1: # FIAT_NEW code (loop 1 in FIAT). # for p in range(1,n): # a1 = ( 2.0 * p + 1.0 ) / ( p + 1.0 ) # a2 = p / (p + 1.0) # results[idx(p+1,0,0)] = a1 * factor1 * results[idx(p,0,0)] \ # -a2 * factor2 * results[ idx(p-1,0,0) ] for r in range(1, embedded_degree): rr = _idx3d((r + 1), 0, 0) ss = _idx3d(r, 0, 0) tt = _idx3d((r - 1), 0, 0) A = (2*r + 1.0)/(r + 1) B = r/(r + 1.0) assign_to = f_component(f_basisvalue, rr) assign_from = f_sub([f_mul([f_float(A), str(f1), f_component(f_basisvalue, ss)]), f_mul([f_float(B), str(f2), f_component(f_basisvalue, tt)])]) code += [f_assign(assign_to, assign_from)] # FIAT_NEW code (loop 2 in FIAT). # q = 1 # for p in range(0,n): # results[idx(p,1,0)] = results[idx(p,0,0)] \ # * ( p * (1.0 + y) + ( 2.0 + 3.0 * y + z ) / 2 ) for r in range(0, embedded_degree): rr = _idx3d(r, 1, 0) ss = _idx3d(r, 0, 0) assign_to = f_component(f_basisvalue, rr) term0 = f_mul([f_float(0.5), f_group(f_add([f_float(2), f_mul([f_float(3), str(symbol_y)]), str(symbol_z)]))]) if r == 0: assign_from = f_mul([term0, f_component(f_basisvalue, ss)]) else: term1 = f_mul([f_float(r), f_group(f_add([f_float(1), str(symbol_y)]))]) assign_from = f_mul([f_group(f_add([term0, term1])), f_component(f_basisvalue, ss)]) code += [f_assign(assign_to, assign_from)] # Only active is embedded_degree > 1. if embedded_degree > 1: # FIAT_NEW code (loop 3 in FIAT). # for p in range(0,n-1): # for q in range(1,n-p): # (aq,bq,cq) = jrc(2*p+1,0,q) # qmcoeff = aq * factor3 + bq * factor4 # qm1coeff = cq * factor5 # results[idx(p,q+1,0)] = qmcoeff * results[idx(p,q,0)] \ # - qm1coeff * results[idx(p,q-1,0)] for r in range(0, embedded_degree - 1): for s in range(1, embedded_degree - r): rr = _idx3d(r, (s + 1), 0) ss = _idx3d(r, s, 0) tt = _idx3d(r, s - 1, 0) (A, B, C) = _jrc(2*r + 1, 0, s) assign_to = f_component(f_basisvalue, rr) term0 = f_mul([f_group(f_add([f_mul([f_float(A), str(f3)]), f_mul([f_float(B), str(f4)])])), f_component(f_basisvalue, ss)]) term1 = f_mul([f_float(C), str(f5), f_component(f_basisvalue, tt)]) assign_from = f_sub([term0, term1]) code += [f_assign(assign_to, assign_from)] # FIAT_NEW code (loop 4 in FIAT). # now handle r=1 # for p in range(n): # for q in range(n-p): # results[idx(p,q,1)] = results[idx(p,q,0)] \ # * ( 1.0 + p + q + ( 2.0 + q + p ) * z ) for r in range(0, embedded_degree): for s in range(0, embedded_degree - r): rr = _idx3d(r, s, 1) ss = _idx3d(r, s, 0) assign_to = f_component(f_basisvalue, rr) A = f_add([f_mul([f_float(2 + r + s), str(symbol_z)]), f_float(1 + r + s)]) assign_from = f_mul([f_group(A), f_component(f_basisvalue, ss)]) code += [f_assign(assign_to, assign_from)] # Only active is embedded_degree > 1. if embedded_degree > 1: # FIAT_NEW code (loop 5 in FIAT). # general r by recurrence # for p in range(n-1): # for q in range(0,n-p-1): # for r in range(1,n-p-q): # ar,br,cr = jrc(2*p+2*q+2,0,r) # results[idx(p,q,r+1)] = \ # (ar * z + br) * results[idx(p,q,r) ] \ # - cr * results[idx(p,q,r-1) ] for r in range(embedded_degree - 1): for s in range(0, embedded_degree - r - 1): for t in range(1, embedded_degree - r - s): rr = _idx3d(r, s, ( t + 1)) ss = _idx3d(r, s, t) tt = _idx3d(r, s, t - 1) (A, B, C) = _jrc(2*r + 2*s + 2, 0, t) assign_to = f_component(f_basisvalue, rr) az_b = f_group(f_add([f_float(B), f_mul([f_float(A), str(symbol_z)])])) assign_from = f_sub([f_mul([f_component(f_basisvalue, ss), az_b]), f_mul([f_float(C), f_component(f_basisvalue, tt)])]) code += [f_assign(assign_to, assign_from)] # FIAT_NEW code (loop 6 in FIAT). # for p in range(n+1): # for q in range(n-p+1): # for r in range(n-p-q+1): # results[idx(p,q,r)] *= math.sqrt((p+0.5)*(p+q+1.0)*(p+q+r+1.5)) for r in range(embedded_degree + 1): for s in range(embedded_degree - r + 1): for t in range(embedded_degree - r - s + 1): rr = _idx3d(r, s, t) A = (r + 0.5)*(r + s + 1)*(r + s + t + 1.5) assign_to = f_component(f_basisvalue, rr) multiply_by = f_sqrt(A) myline = f_imul(assign_to, multiply_by) code += [myline] else: error("Cannot compute basis values for shape: %d" % elemet_cell_domain) return code + [""]
def _extract_variables(val, basis_consts, ip_consts, geo_consts, t_set, optimisation): f_G = format["geometry constant"] f_I = format["ip constant"] f_B = format["basis constant"] if val._prec == 0: return val elif val._prec == 1: if val.base_expr is None: return val new_base = _extract_variables(val.base_expr, basis_consts, ip_consts, geo_consts, t_set, optimisation) new_sym = create_symbol(val.v, val.t, new_base, val.base_op) if new_sym.t == BASIS: return _reduce_expression(new_sym, [], basis_consts, f_B, True) elif new_sym.t == IP: return _reduce_expression(new_sym, [], ip_consts, f_I, True) elif new_sym.t == GEO: return _reduce_expression(new_sym, [], geo_consts, f_G, True) # First handle child classes of product and sum. elif val._prec in (2, 3): new_vars = [] for v in val.vrs: new_vars.append(_extract_variables(v, basis_consts, ip_consts, geo_consts, t_set, optimisation)) if val._prec == 2: new_val = create_product(new_vars) if val._prec == 3: new_val = create_sum(new_vars) elif val._prec == 4: num = _extract_variables(val.num, basis_consts, ip_consts, geo_consts, t_set, optimisation) denom = _extract_variables(val.denom, basis_consts, ip_consts, geo_consts, t_set, optimisation) return create_fraction(num, denom) else: error("Unknown symbolic type: %s" % repr(val)) # Sort variables of product and sum. b_c, i_c, g_c = [], [], [] for v in new_val.vrs: if v.t == BASIS: if optimisation == "precompute_basis_const": b_c.append(v) elif v.t == IP: i_c.append(v) else: g_c.append(v) vrs = new_val.vrs[:] for v in g_c + i_c + b_c: vrs.remove(v) i_c.extend(_reduce_expression(new_val, g_c, geo_consts, f_G)) vrs.extend(_reduce_expression(new_val, i_c, ip_consts, f_I)) vrs.extend(_reduce_expression(new_val, b_c, basis_consts, f_B)) # print "b_c: " # for b in b_c: # print b # print "basis" # for k,v in basis_consts.items(): # print "k: ", k # print "v: ", v # print "geo" # for k,v in geo_consts.items(): # print "k: ", k # print "v: ", v # print "ret val: ", val if len(vrs) > 1: if new_val._prec == 2: new_object = create_product(vrs) elif new_val._prec == 3: new_object = create_sum(vrs) else: error("Must have product or sum here: %s" % repr(new_val)) if new_object.t == BASIS: if optimisation == "precompute_ip_const": return new_object elif optimisation == "precompute_basis_const": return _reduce_expression(new_object, [], basis_consts, f_B, True) elif new_object.t == IP: return _reduce_expression(new_object, [], ip_consts, f_I, True) elif new_object.t == GEO: return _reduce_expression(new_object, [], geo_consts, f_G, True) return vrs[0]