def _dx(self, *ii): "Return the partial derivative with respect to spatial variable number *ii*." d = self # Unwrap ii to allow .dx(i,j) and .dx((i,j)) if len(ii) == 1 and isinstance(ii[0], tuple): ii = ii[0] # Apply all derivatives for i in ii: d = Grad(d) # Take all components, applying repeated index sums in the [] operation return d.__getitem__((Ellipsis,) + ii)
def _dx(self, *ii): "Return the partial derivative with respect to spatial variable number *ii*." d = self # Unwrap ii to allow .dx(i,j) and .dx((i,j)) if len(ii) == 1 and isinstance(ii[0], tuple): ii = ii[0] # Apply all derivatives for i in ii: d = Grad(d) # Take all components, applying repeated index sums in the [] operation return d.__getitem__((Ellipsis, ) + ii)
def splitUFLForm(form): phi = form.arguments()[0] dphi = Grad(phi) source = ExprTensor(phi.ufl_shape) flux = ExprTensor(dphi.ufl_shape) boundarySource = ExprTensor(phi.ufl_shape) form = expand_indices(expand_derivatives(expand_compounds(form))) for integral in form.integrals(): if integral.integral_type() == 'cell': fluxExprs = splitMultiLinearExpr(integral.integrand(), [phi]) for op in fluxExprs: if op[0] == phi: source = source + fluxExprs[op] elif op[0] == dphi: flux = flux + fluxExprs[op] else: raise Exception('Invalid derivative encountered in bulk integral: ' + str(op[0])) elif integral.integral_type() == 'exterior_facet': fluxExprs = splitMultiLinearExpr(integral.integrand(), [phi]) for op in fluxExprs: if op[0] == phi: boundarySource = boundarySource + fluxExprs[op] else: raise Exception('Invalid derivative encountered in boundary integral: ' + str(op[0])) else: raise NotImplementedError('Integrals of type ' + integral.integral_type() + ' are not supported.') return source, flux, boundarySource
def predefineCoefficients(self, predefined, x): for coefficient, idx in self.coefficients.items(): for derivative in self.coefficient(idx, x): predefined[coefficient] = derivative coefficient = Grad(coefficient) predefined.update( {c: self.constant(i) for c, i in self.constants.items()})
def _dx(self, *ii): "Return the partial derivative with respect to spatial variable number i." d = self # Apply all derivatives for i in ii: d = Grad(d) # Take all components, applying repeated index sums in the [] operation return d[..., ii]
def _predefineCoefficients(self, predefined, x, side=None): for idx, coefficient in enumerate(self.coefficientList): for derivative in self.coefficient(idx, x, side=side): if side is None: predefined[coefficient] = derivative elif side == 'Side::in': predefined[coefficient('+')] = derivative elif side == 'Side::out': predefined[coefficient('-')] = derivative coefficient = Grad(coefficient)
def apply_grads(f): if not isinstance(f, FormArgument): print ',' * 60 print f print o print g print ',' * 60 error("What?") for i in range(ngrads): f = Grad(f) return f
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 grad(self, o): "Represent grad(grad(f)) as Grad(Grad(f))." # TODO: Not sure how to detect that gradient of f is cellwise constant. # Can we trust element degrees? #if o.is_cellwise_constant(): # return self.terminal(o) # TODO: Maybe we can ask "f.has_derivatives_of_order(n)" to check # if we should make a zero here? f, = o.operands() ufl_assert(isinstance(f, (Grad, Terminal)), "Expecting derivatives of child to be already expanded.") return (o, Grad(o))
def grad(f): """UFL operator: Take the gradient of f. This operator follows the grad convention where grad(s)[i] = s.dx(j) grad(v)[i,j] = v[i].dx(j) grad(T)[:,i] = T[:].dx(i) for scalar expressions s, vector expressions v, and arbitrary rank tensor expressions T. """ f = as_ufl(f) return Grad(f)
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 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
def form_argument(self, o): "Represent grad(f) as Grad(f)." # Collapse gradient of cellwise function to zero if o.is_cellwise_constant(): return self.terminal(o) return (o, Grad(o))