def modified_terminal(self, o): mt = analyse_modified_terminal(o) terminal = mt.terminal if not isinstance(terminal, Coefficient): # Only split coefficients return o if type(terminal.ufl_element()) != MixedElement: # Only split mixed coefficients return o # Reference value expected assert mt.reference_value # Derivative indices beta = indices(mt.local_derivatives) components = [] for subcoeff in self._split[terminal]: # Apply terminal modifiers onto the subcoefficient component = construct_modified_terminal(mt, subcoeff) # Collect components of the subcoefficient for alpha in numpy.ndindex(subcoeff.ufl_element().reference_value_shape()): # New modified terminal: component[alpha + beta] components.append(component[alpha + beta]) # Repack derivative indices to shape c, = indices(1) return ComponentTensor(as_tensor(components)[c], MultiIndex((c,) + beta))
def hashdatas(): for i in range(3): for ii in ((i,), (i, 0), (1, i)): jj = tuple(FixedIndex(j) for j in ii) expr = MultiIndex(jj) reprs.add(repr(expr)) hashes.add(hash(expr)) yield compute_multiindex_hashdata(expr, {})
def test_index_simplification_reference_grad(self): mesh = Mesh(VectorElement("P", quadrilateral, 1)) i, = indices(1) A = as_tensor(Indexed(Jacobian(mesh), MultiIndex((i, i))), (i,)) expr = apply_derivatives(apply_geometry_lowering( apply_algebra_lowering(A[0]))) assert expr == ReferenceGrad(SpatialCoordinate(mesh))[0, 0] assert expr.ufl_free_indices == () assert expr.ufl_shape == ()
def indexed(self, o, expr, multiindex): indices = list(multiindex) while indices and isinstance(expr, ListTensor) and isinstance(indices[0], FixedIndex): index = indices.pop(0) expr = expr.ufl_operands[int(index)] if indices: return Indexed(expr, MultiIndex(tuple(indices))) else: return expr
def count_flops(n): mesh = Mesh(VectorElement('CG', interval, 1)) tfs = FunctionSpace(mesh, TensorElement('DG', interval, 1, shape=(n, n))) vfs = FunctionSpace(mesh, VectorElement('DG', interval, 1, dim=n)) ensemble_f = Coefficient(vfs) ensemble2_f = Coefficient(vfs) phi = TestFunction(tfs) i, j = indices(2) nc = 42 # magic number L = ((IndexSum( IndexSum( Product(nc * phi[i, j], Product(ensemble_f[i], ensemble_f[i])), MultiIndex((i, ))), MultiIndex((j, ))) * dx) + (IndexSum( IndexSum( Product(nc * phi[i, j], Product( ensemble2_f[j], ensemble2_f[j])), MultiIndex( (i, ))), MultiIndex((j, ))) * dx) - (IndexSum( IndexSum( 2 * nc * Product(phi[i, j], Product(ensemble_f[i], ensemble2_f[j])), MultiIndex((i, ))), MultiIndex((j, ))) * dx)) kernel, = compile_form(L, parameters=dict(mode='spectral')) return EstimateFlops().visit(kernel.ast)
def hashdatas(): ijs = [] iind = indices(3) jind = indices(3) for i in iind: ijs.append((i,)) for j in jind: ijs.append((i, j)) ijs.append((j, i)) for ij in ijs: expr = MultiIndex(ij) reprs.add(repr(expr)) hashes.add(hash(expr)) yield compute_multiindex_hashdata(expr, {})
def _split_indexed(o, self): aggregate, multiindex = o.ufl_operands indices = multiindex.indices() result = [] for agg in self(aggregate): ncmp = len(agg.ufl_shape) idx = indices[:ncmp] indices = indices[ncmp:] if ncmp == 0: result.append(agg) else: mi = multiindex if multiindex.indices() == idx else MultiIndex(idx) result.append(ufl_reuse_if_untouched(o, agg, mi)) return tuple(result)
def test_index_simplification_handles_repeated_indices(self): mesh = Mesh(VectorElement("P", quadrilateral, 1)) V = FunctionSpace(mesh, TensorElement("DQ", quadrilateral, 0)) K = JacobianInverse(mesh) G = outer(Identity(2), Identity(2)) i, j, k, l, m, n = indices(6) A = as_tensor(K[m, i] * K[n, j] * G[i, j, k, l], (m, n, k, l)) i, j = indices(2) # Can't use A[i, i, j, j] because UFL automagically index-sums # repeated indices in the __getitem__ call. Adiag = Indexed(A, MultiIndex((i, i, j, j))) A = as_tensor(Adiag, (i, j)) v = TestFunction(V) f = inner(A, v)*dx fd = compute_form_data(f, do_apply_geometry_lowering=True) integral, = fd.preprocessed_form.integrals() assert integral.integrand().ufl_free_indices == ()
def hashdatas(): for rep in range(nrep): # Resetting index_numbering for each repetition, # resulting in hashdata staying the same for # each repetition but repr and hashes changing # because new indices are created each repetition. index_numbering = {} i, j, k, l = indices(4) for expr in (MultiIndex((i,)), MultiIndex((i,)), # r MultiIndex((i, j)), MultiIndex((j, i)), MultiIndex((i, j)), # r MultiIndex((i, j, k)), MultiIndex((k, j, i)), MultiIndex((j, i))): # r reprs.add(repr(expr)) hashes.add(hash(expr)) yield compute_multiindex_hashdata(expr, index_numbering)
def _split_indexed(o, self, inct): aggregate, multiindex = o.ufl_operands indices = multiindex.indices() result = [] for agg in self(aggregate, False): ncmp = len(agg.ufl_shape) if ncmp == 0: result.append(agg) elif not inct: idx = indices[:ncmp] indices = indices[ncmp:] mi = multiindex if multiindex.indices() == idx else MultiIndex(idx) result.append(ufl_reuse_if_untouched(o, agg, mi)) else: # shape and inct aggshape = (flatten(agg.ufl_shape) + tuple(itertools.repeat(1, len(aggregate.ufl_shape) - 1))) agg = reshape(agg, aggshape) result.append(ufl_reuse_if_untouched(o, agg, multiindex)) return tuple(result)
def apply_mapping(expression, mapping, domain): """ This applies the appropriate transformation to the given expression for interpolation to a specific element, according to the manner in which it maps from the reference cell. The following is borrowed from the UFC documentation: Let g be a field defined on a physical domain T with physical coordinates x. Let T_0 be a reference domain with coordinates X. Assume that F: T_0 -> T such that x = F(X) Let J be the Jacobian of F, i.e J = dx/dX and let K denote the inverse of the Jacobian K = J^{-1}. Then we (currently) have the following four types of mappings: 'affine' mapping for g: G(X) = g(x) For vector fields g: 'contravariant piola' mapping for g: G(X) = det(J) K g(x) i.e G_i(X) = det(J) K_ij g_j(x) 'covariant piola' mapping for g: G(X) = J^T g(x) i.e G_i(X) = J^T_ij g(x) = J_ji g_j(x) 'double covariant piola' mapping for g: G(X) = J^T g(x) J i.e. G_il(X) = J_ji g_jk(x) J_kl 'double contravariant piola' mapping for g: G(X) = det(J)^2 K g(x) K^T i.e. G_il(X)=(detJ)^2 K_ij g_jk K_lk If 'contravariant piola' or 'covariant piola' are applied to a matrix-valued function, the appropriate mappings are applied row-by-row. :arg expression: UFL expression :arg mapping: a string indicating the mapping to apply """ mesh = expression.ufl_domain() if mesh is None: mesh = domain if domain is not None and mesh != domain: raise NotImplementedError("Multiple domains not supported") rank = len(expression.ufl_shape) if mapping == "affine": return expression elif mapping == "covariant piola": J = Jacobian(mesh) *i, j, k = indices(len(expression.ufl_shape) + 1) expression = Indexed(expression, MultiIndex((*i, k))) return as_tensor(J.T[j, k] * expression, (*i, j)) elif mapping == "contravariant piola": K = JacobianInverse(mesh) detJ = JacobianDeterminant(mesh) *i, j, k = indices(len(expression.ufl_shape) + 1) expression = Indexed(expression, MultiIndex((*i, k))) return as_tensor(detJ * K[j, k] * expression, (*i, j)) elif mapping == "double covariant piola" and rank == 2: J = Jacobian(mesh) return J.T * expression * J elif mapping == "double contravariant piola" and rank == 2: K = JacobianInverse(mesh) detJ = JacobianDeterminant(mesh) return (detJ)**2 * K * expression * K.T else: raise NotImplementedError("Don't know how to handle mapping type %s for expression of rank %d" % (mapping, rank))
def _mapped(self, t): # Check that we have a valid input object if not isinstance(t, Terminal): error("Expecting a Terminal.") # Get modifiers accumulated by previous handler calls ngrads = self._ngrads restricted = self._restricted avg = self._avg if avg != "": error("Averaging not implemented.") # FIXME # These are the global (g) and reference (r) values if isinstance(t, FormArgument): g = t r = ReferenceValue(g) elif isinstance(t, GeometricQuantity): g = t r = g else: error("Unexpected type {0}.".format(type(t).__name__)) # Some geometry mapping objects we may need multiple times below domain = t.ufl_domain() J = Jacobian(domain) detJ = JacobianDeterminant(domain) K = JacobianInverse(domain) # Restrict geometry objects if applicable if restricted: J = J(restricted) detJ = detJ(restricted) K = K(restricted) # Create Hdiv mapping from possibly restricted geometry objects Mdiv = (1.0 / detJ) * J # Get component indices of global and reference terminal objects gtsh = g.ufl_shape # rtsh = r.ufl_shape gtcomponents = compute_indices(gtsh) # rtcomponents = compute_indices(rtsh) # Create core modified terminal, with eventual # layers of grad applied directly to the terminal, # then eventual restriction applied last for i in range(ngrads): g = Grad(g) r = ReferenceGrad(r) if restricted: g = g(restricted) r = r(restricted) # Get component indices of global and reference objects with # grads applied gsh = g.ufl_shape # rsh = r.ufl_shape # gcomponents = compute_indices(gsh) # rcomponents = compute_indices(rsh) # Get derivative component indices dsh = gsh[len(gtsh):] dcomponents = compute_indices(dsh) # Create nested array to hold expressions for global # components mapped from reference values def ndarray(shape): if len(shape) == 0: return [None] elif len(shape) == 1: return [None] * shape[-1] else: return [ndarray(shape[1:]) for i in range(shape[0])] global_components = ndarray(gsh) # Compute mapping from reference values for each global component for gtc in gtcomponents: if isinstance(t, FormArgument): # Find basic subelement and element-local component # ec, element, eoffset = t.ufl_element().extract_component2(gtc) # FIXME: Translate this correctly eoffset = 0 ec, element = t.ufl_element().extract_reference_component(gtc) # Select mapping M from element, pick row emapping = # M[ec,:], or emapping = [] if no mapping if isinstance(element, MixedElement): error("Expecting a basic element here.") mapping = element.mapping() if mapping == "contravariant Piola": # S == HDiv: # Handle HDiv elements with contravariant piola # mapping contravariant_hdiv_mapping = (1/det J) * # J * PullbackOf(o) ec, = ec emapping = Mdiv[ec, :] elif mapping == "covariant Piola": # S == HCurl: # Handle HCurl elements with covariant piola mapping # covariant_hcurl_mapping = JinvT * PullbackOf(o) ec, = ec emapping = K[:, ec] # Column of K is row of K.T elif mapping == "identity": emapping = None else: error("Unknown mapping {0}".format(mapping)) elif isinstance(t, GeometricQuantity): eoffset = 0 emapping = None else: error("Unexpected type {0}.".format(type(t).__name__)) # Create indices # if rtsh: # i = Index() if len(dsh) != ngrads: error("Mismatch between derivative shape and ngrads.") if ngrads: ii = indices(ngrads) else: ii = () # Apply mapping row to reference object if emapping: # Mapped, always nonscalar terminal Not # using IndexSum for the mapping row dot product to # keep it simple, because we don't have a slice type emapped_ops = [emapping[s] * Indexed(r, MultiIndex((FixedIndex(eoffset + s),) + ii)) for s in range(len(emapping))] emapped = sum(emapped_ops[1:], emapped_ops[0]) elif gtc: # Nonscalar terminal, unmapped emapped = Indexed(r, MultiIndex((FixedIndex(eoffset),) + ii)) elif ngrads: # Scalar terminal, unmapped, with derivatives emapped = Indexed(r, MultiIndex(ii)) else: # Scalar terminal, unmapped, no derivatives emapped = r for di in dcomponents: # Multiply derivative mapping rows, parameterized by # free column indices dmapping = as_ufl(1) for j in range(ngrads): dmapping *= K[ii[j], di[j]] # Row of K is column of JinvT # Compute mapping from reference values for this # particular global component global_value = dmapping * emapped # Apply index sums # if rtsh: # global_value = IndexSum(global_value, MultiIndex((i,))) # for j in range(ngrads): # Applied implicitly in the dmapping * emapped above # global_value = IndexSum(global_value, MultiIndex((ii[j],))) # This is the component index into the full object # with grads applied gc = gtc + di # Insert in nested list comp = global_components for i in gc[:-1]: comp = comp[i] comp[0 if gc == () else gc[-1]] = global_value # Wrap nested list in as_tensor unless we have a scalar # expression if gsh: tensor = as_tensor(global_components) else: tensor, = global_components return tensor