def nabla_grad(self, o, a): sh = a.ufl_shape if sh == (): return Grad(a) else: j = Index() ii = tuple(indices(len(sh))) return as_tensor(a[ii].dx(j), (j, ) + ii)
def grad(self, o): "Represent grad(grad(f)) as Grad(Grad(f))." # Check that o is a "differential terminal" if not isinstance(o.ufl_operands[0], (Grad, Terminal)): error("Expecting only grads applied to a terminal.") return Grad(o)
def argument(self, o): # TODO: Enable this after fixing issue#13, unless we move # simplificat ion to a separate stage? # if is_cellwise_constant(o): # # Collapse gradient of cellwise constant function to zero # # TODO: Missing this type # return AnnotatedZero(o.ufl_shape + self._var_shape, arguments=(o,)) return Grad(o)
def geometric_quantity(self, o): """Default for geometric quantities is dg/dx = 0 if piecewise constant, otherwise keep Grad(g). Override for specific types if other behaviour is needed.""" if is_cellwise_constant(o): return self.independent_terminal(o) else: # TODO: Which types does this involve? I don't think the # form compilers will handle this. return Grad(o)
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 apply_grads(f): for i in range(ngrads): f = Grad(f) return f
def coefficient(self, o): if is_cellwise_constant(o): return self.independent_terminal(o) return Grad(o)
def generate_ref_tensor(element: FiniteElement = None): def monkey_patch_ufl(): from ufl.referencevalue import ReferenceValue oldinit = ReferenceValue.__init__ def newinit(self, f): if isinstance(f, ReferenceValue): f = f.ufl_operands[0] oldinit(self, f) ReferenceValue.__init__ = newinit monkey_patch_ufl() def generate_reference_tetrahedron_mesh(): vertices = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]], dtype=np.float64) cells = np.array([[0, 1, 2, 3]], dtype=np.int32) mesh = Mesh(MPI.comm_world, CellType.Type.tetrahedron, vertices, cells, [], cpp.mesh.GhostMode.none) return mesh mesh = generate_reference_tetrahedron_mesh() if element is None: element = FiniteElement("P", tetrahedron, 1) V = FunctionSpace(mesh, element) dofmap = V.dofmap().cell_dofs(0) dofmap_inverse = np.argsort(dofmap) u, v = TrialFunction(V), TestFunction(V) detJ = JacobianDeterminant(SpatialCoordinate(mesh)) A0 = np.zeros( (dofmap.size, dofmap.size, mesh.topology.dim, mesh.topology.dim), dtype=np.double) for i in range(mesh.topology.dim): for j in range(mesh.topology.dim): jit_result = jit.jit.ffc_jit( outer(Grad(Val(u)), Grad(Val(v)))[i, j] / detJ * dx) ufc_form = cpp.fem.make_ufc_form(jit_result[0]) a = cpp.fem.Form(ufc_form, [V._cpp_object, V._cpp_object]) assembler = cpp.fem.Assembler([[a]], [], []) A_scp = PETScMatrix() assembler.assemble(A_scp, cpp.fem.Assembler.BlockType.monolithic) A = utils.scipy2numpy(A_scp) A0[:, :, i, j] = A[dofmap_inverse].transpose() #print(79 * '=') #print("dphi_i/dX({})*dphi_j/dX({})".format(i, j)) #print(A[dofmap_inverse]) A0 = A0[dofmap_inverse, :, :, :] return A0
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