def cartesianCurl(f, F): """ The curl operator corresponding to ``cartesianGrad(f,F)``. For ``f`` of rank 1, it returns a vector in 3D or a scalar in 2D. For ``f`` scalar in 2D, it returns a vector. """ n = rank(f) gradf = cartesianGrad(f, F) if (n == 1): m = shape(f)[0] eps = PermutationSymbol(m) if (m == 3): (i, j, k) = indices(3) return as_tensor(eps[i, j, k] * gradf[k, j], (i, )) elif (m == 2): (j, k) = indices(2) return eps[j, k] * gradf[k, j] else: print("ERROR: Unsupported dimension of argument to curl.") exit() elif (n == 0): return as_vector((-gradf[1], gradf[0])) else: print("ERROR: Unsupported rank of argument to curl.") exit()
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 c(u): Fu = F(u) A, B, C, D = ufl.indices(4) i, j, k, l = ufl.indices(4) return (1.0 / J(u)) * as_tensor( Ctens[A, B, C, D] * Fu[i, A] * Fu[j, B] * Fu[k, C] * Fu[l, D], (i, j, k, l))
def __init__(self, membrane, kwargs): mem = membrane # Define material - incompressible Neo-Hookean self.nu = 0.5 # Poisson ratio, incompressible E = kwargs.get("E", None) if E is not None: self.E = Constant(E, name="Elastic modulus") mu = kwargs.get("mu", None) if mu is not None: self.mu = Constant(mu, name="Shear modulus") else: self.mu = Constant(self.E/(2*(1 + self.nu)), name="Shear modulus") if kwargs['cylindrical']: mem.I_C = tr(mem.C) + 1/det(mem.C) - 1 else: C_n = mem.C_n #mem.F_n.T*mem.F_n C_0 = mem.C_0 #mem.F_0.T*mem.F_0 C_0_sup = mem.C_0_sup i,j = ufl.indices(2) I1 = C_0_sup[i,j]*C_n[i,j] mem.I_C = I1 + 1/det(mem.C) self.psi = 0.5*self.mu*(mem.I_C - Constant(mem.nsd))
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 LHS(q_b, C, ur, ui, vr, vi, DX): # define the weak form with norm u v* i, j = indices(2) # define to avoid name collisions TT = C[i,j]*eps_T_Vgt(ur)[j]*eps_T_Vgt(vr)[i]*DX + \ C[i,j]*eps_T_Vgt(ui)[j]*eps_T_Vgt(vi)[i]*DX + \ C[i,j]*eps_T_Vgt(ui)[j]*eps_T_Vgt(vr)[i]*DX - \ C[i,j]*eps_T_Vgt(ur)[j]*eps_T_Vgt(vi)[i]*DX TZ = q_b*(C[i,j]*eps_T_Vgt(ur)[j]*eps_Z_Vgt(vr)[i]*DX + \ C[i,j]*eps_T_Vgt(ui)[j]*eps_Z_Vgt(vi)[i]*DX - \ C[i,j]*eps_T_Vgt(ui)[j]*eps_Z_Vgt(vr)[i]*DX + \ C[i,j]*eps_T_Vgt(ur)[j]*eps_Z_Vgt(vi)[i]*DX) ZT = q_b*(C[i,j]*eps_Z_Vgt(ur)[j]*eps_T_Vgt(vr)[i]*DX + \ C[i,j]*eps_Z_Vgt(ui)[j]*eps_T_Vgt(vi)[i]*DX - \ C[i,j]*eps_Z_Vgt(ui)[j]*eps_T_Vgt(vr)[i]*DX + \ C[i,j]*eps_Z_Vgt(ur)[j]*eps_T_Vgt(vi)[i]*DX) ZZ = q_b*q_b*(C[i,j]*eps_Z_Vgt(ur)[j]*eps_Z_Vgt(vr)[i]*DX + \ C[i,j]*eps_Z_Vgt(ui)[j]*eps_Z_Vgt(vi)[i]*DX + \ C[i,j]*eps_Z_Vgt(ui)[j]*eps_Z_Vgt(vr)[i]*DX - \ C[i,j]*eps_Z_Vgt(ur)[j]*eps_Z_Vgt(vi)[i]*DX) a = -TT + TZ + ZT - ZZ return a
def test_dolfin_expression_compilation_of_dot_with_index_notation(dolfin): # Define some PyDOLFIN coefficients mesh = dolfin.UnitSquareMesh(3, 3) # Using quadratic element deliberately for accuracy V = dolfin.VectorFunctionSpace(mesh, "CG", 2) u = dolfin.Function(V) u.interpolate(dolfin.Expression(("x[0]", "x[1]"))) # Define ufl expression with the simplest possible index notation uexpr = ufl.dot(u, u) # Needs expand_compounds, not testing here uexpr = u[0]*u[0] + u[1]*u[1] # Works i, = ufl.indices(1) uexpr = u[i]*u[i] # Works # Define expected output from compilation # Oneliner version (no reuse in this expression): xc, yc = ('v_w0[0]', 'v_w0[1]') expected_lines = ['double s[1];', 'Array<double> v_w0(2);', 'w0->eval(v_w0, x);', 's[0] = pow(%(x)s, 2) + pow(%(y)s, 2);' % {'x':xc, 'y':yc}, 'values[0] = s[0];'] # Define expected evaluation values: [(x,value), (x,value), ...] x, y = (0.6, 0.7) expected = (x*x+y*y) expected_values = [((0.0, 0.0), (0.0,)), ((x, y), (expected,)), ] # Execute all tests check_dolfin_expression_compilation(uexpr, expected_lines, expected_values, members={'w0':u})
def grad(self, o, s): # The representation # o = Grad(s) # is equivalent to # o = as_tensor(s[ii].dx(i), ii+(i,)) # with ii a tuple of free indices and i a single free index. # Make some unique utility indices from ufl import indices on = o.rank() ind = list(indices(on)) # Get underlying expression to take gradient of f, = o.ufl_operands fn = f.rank() ffc_assert(on == fn + 1, "Assuming grad always adds one axis.") s = MonomialSum(s) s.apply_indices(list(ind[:-1])) s = MonomialSum(s) s.apply_derivative([ind[-1]]) s = MonomialSum(s) s.apply_tensor(ind) return s
def dtheta_dC(self, u_, p_, ivar, theta_old_, thres, dt): theta_ = ivar["theta"] dFg_dtheta = self.F_g(theta_,tang=True) ktheta = self.res_dtheta_growth(u_, p_, ivar, theta_old_, thres, dt, 'ktheta') K_growth = self.res_dtheta_growth(u_, p_, ivar, theta_old_, thres, dt, 'tang') i, j, k, l = indices(4) if self.growth_trig == 'volstress': Cmat = self.S(u_,p_,ivar,tang=True) # TeX: \frac{\partial \vartheta}{\partial \boldsymbol{C}} = \frac{k(\vartheta) \Delta t}{\frac{\partial r}{\partial \vartheta}}\left(\boldsymbol{S} + \boldsymbol{C} : \frac{1}{2} \check{\mathbb{C}}\right) tangdC = (ktheta*dt/K_growth) * (self.S(u_,p_,ivar=ivar) + 0.5*as_tensor(self.kin.C(u_)[i,j]*Cmat[i,j,k,l], (k,l))) elif self.growth_trig == 'fibstretch': # TeX: \frac{\partial \vartheta}{\partial \boldsymbol{C}} = \frac{k(\vartheta) \Delta t}{\frac{\partial r}{\partial \vartheta}} \frac{1}{2\lambda_{f}} \boldsymbol{f}_{0}\otimes \boldsymbol{f}_{0} tangdC = (ktheta*dt/K_growth) * outer(self.kin.fib_funcs[0],self.kin.fib_funcs[0])/(2.*self.kin.fibstretch(u_,self.kin.fib_funcs[0])) else: raise NameError("Unkown growth_trig!") return tangdC
def grad(self, o, s): # The representation # o = Grad(s) # is equivalent to # o = as_tensor(s[ii].dx(i), ii+(i,)) # with ii a tuple of free indices and i a single free index. # Make some unique utility indices from ufl import indices on = len(o.ufl_shape) ind = list(indices(on)) # Get underlying expression to take gradient of f, = o.ufl_operands fn = len(f.ufl_shape) ffc_assert(on == fn + 1, "Assuming grad always adds one axis.") s = MonomialSum(s) s.apply_indices(list(ind[:-1])) s = MonomialSum(s) s.apply_derivative([ind[-1]]) s = MonomialSum(s) s.apply_tensor(ind) return s
def covariantDerivative(T): """ Returns a ``CurvilinearTensor`` that is the covariant derivative of the ``CurvilinearTensor`` argument ``T``. """ n = rank(T.T) ii = indices(n + 2) g = T.g gamma = getChristoffel(g) retval = grad(T.T) for i in range(0, n): # use ii[n] as the new index of the covariant deriv # use ii[n+1] as dummy index if (T.lowered[i]): retval -= as_tensor(T.T[ii[0:i]+(ii[n+1],)+ii[i+1:n]]\ *gamma[(ii[n+1],ii[i],ii[n])],\ ii[0:n+1]) else: retval += as_tensor(T.T[ii[0:i]+(ii[n+1],)+ii[i+1:n]]\ *gamma[(ii[i],ii[n+1],ii[n])],\ ii[0:n+1]) newLowered = T.lowered + [ True, ] return CurvilinearTensor(retval, g, newLowered)
def cartesianDiv(f, F): """ The divergence operator corresponding to ``cartesianGrad(f,F)`` that sums on the last two indices. """ n = rank(f) ii = indices(n) return as_tensor(cartesianGrad(f, F)[ii + (ii[n - 1], )], ii[0:n - 1])
def cartesianCurl(f, F): """ The curl operator corresponding to ``cartesianGrad(f,F)``. Only valid for ``f`` of rank 1, with dimension 3. """ eps = PermutationSymbol(3) gradf = cartesianGrad(f, F) (i, j, k) = indices(3) return as_tensor(eps[i, j, k] * gradf[k, j], (i, ))
def stf3d3(rank3_3d): r""" Return the symmetric and trace-free part of a 3D 3-tensor. Return the symmetric and trace-free part of a 3D 3-tensor. (dev(sym(.))) Returns the STF part :math:`A_{\langle i j k\rangle}` of a rank-3 tensor :math:`A \in \mathbb{R}^{3 \times 3 \times 3}` [TOR2018]_. .. math:: A_{\langle i j k\rangle}=A_{(i j k)}-\frac{1}{5}\left[A_{(i l l)} \delta_{j k}+A_{(l j l)} \delta_{i k}+A_{(l l k)} \delta_{i j}\right] A gradient :math:`\frac{\partial S_{\langle i j}}{\partial x_{k \rangle}}` of a symmetric 2-tensor :math:`S \in \mathbb{R}^{3 \times 3}` has the deviator [STR2005]_: .. math:: \frac{\partial S_{\langle i j}}{\partial x_{k \rangle}} =& \frac{1}{3}\left( \frac{\partial S_{i j}}{\partial x_{k}} +\frac{\partial S_{i k}}{\partial x_{j}} +\frac{\partial S_{j k}}{\partial x_{i}} \right) -\frac{1}{15} \biggl[ \left( \frac{\partial S_{i r}}{\partial x_{r}} +\frac{\partial S_{i r}}{\partial x_{r}} +\frac{\partial S_{r r}}{\partial x_{i}} \right) \delta_{j k} \\ &+ \left( \frac{\partial S_{j r}}{\partial x_{r}} +\frac{\partial S_{j r}}{\partial x_{r}} +\frac{\partial S_{r r}}{\partial x_{j}} \right) \delta_{i k} +\left( \frac{\partial S_{k r}}{\partial x_{r}} +\frac{\partial S_{k r}}{\partial x_{r}} +\frac{\partial S_{r r}}{\partial x_{k}} \right) \delta_{i j} \biggr] """ i, j, k, L = ufl.indices(4) delta = df.Identity(3) sym_ijk = sym3d3(rank3_3d)[i, j, k] traces_ijk = 1 / 5 * (+sym3d3(rank3_3d)[i, L, L] * delta[j, k] + sym3d3(rank3_3d)[L, j, L] * delta[i, k] + sym3d3(rank3_3d)[L, L, k] * delta[i, j]) tracefree_ijk = sym_ijk - traces_ijk return ufl.as_tensor(tracefree_ijk, (i, j, k))
def curvilinearInner(T, S): """ Returns the inner product of ``CurvilinearTensor`` objects ``T`` and ``S``, inserting factors of the metric and inverse metric as needed, depending on the co/contra-variant status of corresponding indices. """ Tsharp = T.sharp() Sflat = S.flat() ii = indices(rank(T.T)) return as_tensor(Tsharp.T[ii] * Sflat.T[ii], ())
def getChristoffel(g): """ Returns Christoffel symbols associated with a metric tensor ``g``. Indices are ordered based on the assumption that the first index is the raised one. """ a, b, c, d = indices(4) return as_tensor\ (0.5*inv(g)[a,b]\ *(grad(g)[c,b,d]\ + grad(g)[d,b,c]\ - grad(g)[d,c,b]), (a,d,c))
def Cremod(self, u_, p_, ivar, theta_old_, thres, dt): theta_ = ivar["theta"] i, j, k, l = indices(4) dphi_dtheta_ = self.phi_remod(theta_,tang=True) dtheta_dC_ = self.dtheta_dC(u_, p_, ivar, theta_old_, thres, dt) Cremod = 2.*dphi_dtheta_ * as_tensor(dtheta_dC_[i,j]*(self.stress_remod - self.stress_base)[k,l], (i,j,k,l)) return Cremod
def strongResidual(u, p, mu, rho, u_t=None, f=None): """ The momenum and continuity residuals, as a tuple, of the strong PDE, system, in terms of velocity ``u``, pressure ``p``, dynamic viscosity ``mu``, mass density ``rho``, and, optionally, the partial time derivative of velocity, ``u_t``, and a body force per unit mass, ``f``. """ DuDt = materialTimeDerivative(u, u_t, f) i, j = ufl.indices(2) r_M = rho * DuDt - as_tensor(grad(sigma(u, p, mu))[i, j, j], (i, )) r_C = rho * div(u) return r_M, r_C
def shear_stress(self, velocity): if self.geometric_dimension == 1: return dolf.Constant(4.0 / 3.0) * velocity.dx(0) / self.dolf_constants.Re i, j = ufl.indices(2) shear_stress = ( velocity[i].dx(j) + dolf.Constant(1.0 / 3.0) * self.identity_tensor[i, j] * dolf.div(velocity) ) / self.dolf_constants.Re # shear_stress = ( # velocity[i].dx(j) # + velocity[j].dx(i) # - dolf.Constant(2.0 / 3.0) * self.identity_tensor[i, j] * dolf.div(velocity) # ) / self.dolf_constants.Re return dolf.as_tensor(shear_stress, (i, j))
def __init__(self, angular_quad, L): from transport_data import angular_tensors_ext_module i,j,k1,k2,p,q = ufl.indices(6) tensors = angular_tensors_ext_module.AngularTensors(angular_quad, L) self.Y = ufl.as_tensor( numpy.reshape(tensors.Y(), tensors.shape_Y()) ) self.Q = ufl.as_tensor( numpy.reshape(tensors.Q(), tensors.shape_Q()) ) self.QT = ufl.transpose(self.Q) self.Qt = ufl.as_tensor( numpy.reshape(tensors.Qt(), tensors.shape_Qt()) ) self.QtT = ufl.as_tensor( self.Qt[k1,p,i], (p,i,k1) ) self.G = ufl.as_tensor( numpy.reshape(tensors.G(), tensors.shape_G()) ) self.T = ufl.as_tensor( numpy.reshape(tensors.T(), tensors.shape_T()) ) self.Wp = ufl.as_vector( angular_quad.get_pw() ) self.W = ufl.diag(self.Wp)
def curvilinearGrad(T): """ Returns the gradient of ``CurvilinearTensor`` argument ``T``, i.e., the covariant derivative with the last index raised. """ n = rank(T.T) ii = indices(n + 2) g = T.g deriv = covariantDerivative(T) invg = inv(g) # raise last index retval = as_tensor(deriv.T[ii[0:n+1]]*invg[ii[n:n+2]],\ ii[0:n]+(ii[n+1],)) return CurvilinearTensor(retval, g, T.lowered + [ False, ])
def solve(self, it=0): if self.group_GS: self.solve_group_GS(it) else: super(EllipticSNFluxModule, self).solve(it) self.slns_mg = split(self.sln) i,p,q,k1,k2 = ufl.indices(5) sol_timer = Timer("-- Complete solution") aux_timer = Timer("---- SOL: Computing angular flux + adjoint") # TODO: Move to Discretization V11 = FunctionSpace(self.DD.mesh, "CG", self.DD.parameters["p"]) for gto in range(self.DD.G): self.PD.get_xs('D', self.D, gto) form = self.D * ufl.diag_vector(ufl.as_matrix(self.DD.ordinates_matrix[i,p]*self.slns_mg[gto][q].dx(i), (p,q))) for gfrom in range(self.DD.G): pres_Ss = False # TODO: Enlarge self.S and self.C to (L+1)^2 (or 1./2.*(L+1)*(L+2) in 2D) to accomodate for anisotropic # scattering (lines below using CC, SS are correct only for L = 0, when the following inner loop runs only # once. for l in range(self.L+1): for m in range(-l, l+1): if self.DD.angular_quad.get_D() == 2 and divmod(l+m, 2)[1] == 0: continue pres_Ss |= self.PD.get_xs('Ss', self.S[l], gto, gfrom, l) self.PD.get_xs('C', self.C[l], gto, gfrom, l) if pres_Ss: Cd = ufl.diag(self.C) CC = self.tensors.Y[p,k1] * Cd[k1,k2] * self.tensors.Qt[k2,q,i] form += ufl.as_vector(CC[p,q,i] * self.slns_mg[gfrom][q].dx(i), p) # project(form, self.DD.Vpsi1, function=self.aux_slng, preconditioner_type="petsc_amg") # FASTER, but requires form compilation for each dir.: for pp in range(self.DD.M): assign(self.aux_slng.sub(pp), project(form[pp], V11, preconditioner_type="petsc_amg")) self.psi_mg[gto].assign(self.slns_mg[gto] + self.aux_slng) self.adj_psi_mg[gto].assign(self.slns_mg[gto] - self.aux_slng)
def Cgrowth_p(self, u_, p_, ivar, theta_old_, thres, dt): theta_ = ivar["theta"] dFg_dtheta = self.F_g(theta_,tang=True) i, j, k, l = indices(4) dtheta_dp_ = self.dtheta_dp(u_, p_, ivar, theta_old_, thres, dt) dS_dFg_ = self.dS_dFg(u_, p_, ivar, theta_old_, dt) dS_dFg_times_dFg_dtheta = as_tensor(dS_dFg_[i,j,k,l]*dFg_dtheta[k,l], (i,j)) Cgrowth_p = dS_dFg_times_dFg_dtheta * dtheta_dp_ return Cgrowth_p
def raiseLowerIndex(self, i): """ Flips the raised/lowered status of the ``i``-th index. """ n = rank(self.T) ii = indices(n + 1) mat = self.g if (self.lowered[i]): mat = inv(self.g) else: mat = self.g retval = as_tensor(self.T[ii[0:i]+(ii[i],)+ii[i+1:n]]\ *mat[ii[i],ii[n]],\ ii[0:i]+(ii[n],)+ii[i+1:n]) return CurvilinearTensor(retval,self.g,\ self.lowered[0:i]\ +[not self.lowered[i],]+self.lowered[i+1:])
def sym3d3(rank3_3d): r""" Return the symmetric part of a 3D 3-tensor. Returns the symmetric part :math:`A_{(i j k)}` of a 3D rank-3 tensor :math:`A \in \mathbb{R}^{3 \times 3 \times 3}` [STR2005]_ [TOR2018]_. .. math:: A_{(i j k)}=\frac{1}{6}\left( A_{i j k}+A_{i k j}+A_{j i k}+A_{j k i}+A_{k i j}+A_{k j i} \right) """ i, j, k = ufl.indices(3) symm_ijk = 1 / 6 * ( # All permutations +rank3_3d[i, j, k] + rank3_3d[i, k, j] + rank3_3d[j, i, k] + rank3_3d[j, k, i] + rank3_3d[k, i, j] + rank3_3d[k, j, i]) return ufl.as_tensor(symm_ijk, (i, j, k))
def div3d3(rank3_3d): r""" Return the 3D divergence of a 3-tensor. Return the 3D divergence of a 3-tensor as .. math:: {(\mathrm{div}(m))}_{ij} = \frac{\partial m_{ijk}}{\partial x_k} Compare with [SCH2009]_ (p. 92). .. [SCH2009] Heinz Schade, Klaus Neemann (2009). “Tensoranalysis”. 2. überarbeitete Auflage. """ i, j, k = ufl.indices(3) div_ij = rank3_3d[i, j, 0].dx(0) + rank3_3d[i, j, 1].dx(1) return ufl.as_tensor(div_ij, (i, j))
def _spatial_component(self, trial, test): pressure, velocity, temperature = trial test_pressure, test_velocity, test_temperature = test i, j = ufl.indices(2) if self.geometric_dimension == 1: continuity_component = test_pressure * velocity.dx(0) momentum_component = test_velocity.dx(0) * self.stress(pressure, velocity) else: continuity_component = test_pressure * dolf.div(velocity) momentum_component = dolf.inner( dolf.as_tensor(test_velocity[i].dx(j), (i, j)), self.stress(pressure, velocity), ) energy_component = dolf.inner( dolf.grad(test_temperature), self.heat_flux(temperature) ) return continuity_component + momentum_component + energy_component
def dS_dFg(self, u_, p_, ivar, theta_old_, dt): theta_ = ivar["theta"] Cmat = self.S(u_, p_, ivar, tang=True) i, j, k, l, m, n = indices(6) # elastic material tangent (living in intermediate growth configuration) Cmat_e = dot( self.F_g(theta_), dot(self.F_g(theta_), dot(Cmat, dot(self.F_g(theta_).T, self.F_g(theta_).T)))) Fginv_outertop_S = as_tensor( inv(self.F_g(theta_))[i, k] * self.S(u_, p_, ivar=ivar)[j, l], (i, j, k, l)) S_outerbot_Fginv = as_tensor( self.S(u_, p_, ivar=ivar)[i, l] * inv(self.F_g(theta_))[j, k], (i, j, k, l)) Fginv_outertop_Fginv = as_tensor( inv(self.F_g(theta_))[i, k] * inv(self.F_g(theta_))[j, l], (i, j, k, l)) FginvT_outertop_Ce = as_tensor( inv(self.F_g(theta_)).T[i, k] * self.C_e(self.kin.C(u_), theta_)[j, l], (i, j, k, l)) Ce_outerbot_FginvT = as_tensor( self.C_e(self.kin.C(u_), theta_)[i, l] * inv(self.F_g(theta_)).T[j, k], (i, j, k, l)) Cmat_e_with_C_e = 0.5 * as_tensor( Cmat_e[i, j, m, n] * (FginvT_outertop_Ce[m, n, k, l] + Ce_outerbot_FginvT[m, n, k, l]), (i, j, k, l)) Fginv_outertop_Fginv_with_Cmat_e_with_C_e = as_tensor( Fginv_outertop_Fginv[i, j, m, n] * Cmat_e_with_C_e[m, n, k, l], (i, j, k, l)) return -(Fginv_outertop_S + S_outerbot_Fginv) - Fginv_outertop_Fginv_with_Cmat_e_with_C_e
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 test_dolfin_expression_compilation_of_index_notation(dolfin): # Define some PyDOLFIN coefficients mesh = dolfin.UnitSquareMesh(3, 3) # Using quadratic element deliberately for accuracy V = dolfin.VectorFunctionSpace(mesh, "CG", 2) u = dolfin.Function(V) u.interpolate(dolfin.Expression(("x[0]", "x[1]"))) # Define ufl expression with index notation i, j = ufl.indices(2) uexpr = ((u[0]*u + u[1]*u)[i]*u[j])*(u[i]*u[j]) # Define expected output from compilation # Split version (reusing all product terms correctly!): xc, yc = ('v_w0[0]', 'v_w0[1]') svars = {'a':'s[2]', 'b':'s[4]', 'xx':'s[0]', 'yy':'s[3]', 'xy':'s[1]', 'x':xc, 'y':yc} expected_lines = ['double s[6];', 'Array<double> v_w0(2);', 'w0->eval(v_w0, x);', 's[0] = pow(%(x)s, 2);' % svars, 's[1] = %(x)s * %(y)s;' % svars, 's[2] = %(xx)s + %(xy)s;' % svars, 's[3] = pow(%(y)s, 2);' % svars, 's[4] = %(yy)s + %(xy)s;' % svars, 's[5] = (%(xx)s * (%(x)s * %(a)s) + %(xy)s * (%(x)s * %(b)s)) + (%(yy)s * (%(y)s * %(b)s) + %(xy)s * (%(y)s * %(a)s));' % svars, 'values[0] = s[5];'] # Making the above line correct was a nightmare because of operator sorting in ufl... Hoping it stays stable! # Define expected evaluation values: [(x,value), (x,value), ...] x, y = (0.6, 0.7) a = x*x+y*x b = x*y+y*y expected = (a*x)*(x*x) + (a*y)*(x*y) + (b*x)*(y*x) + (b*y)*(y*y) expected_values = [((0.0, 0.0), (0.0,)), ((x, y), (expected,)), ] # Execute all tests check_dolfin_expression_compilation(uexpr, expected_lines, expected_values, members={'w0':u})
def curvilinearDiv(T): """ Returns the divergence of the ``CurvilinearTensor`` argument ``T``, i.e., the covariant derivative, but contracting over the new index and the last raised index. NOTE: This operation is invalid for tensors that do not contain at least one raised index. """ n = rank(T.T) ii = indices(n) g = T.g j = -1 # last raised index for i in range(0, n): if (not T.lowered[i]): j = i if (j == -1): print("ERROR: Divergence operator requires at least one raised index.") exit() deriv = covariantDerivative(T) retval = as_tensor(deriv.T[ii[0:n] + (ii[j], )], ii[0:j] + ii[j + 1:n]) return CurvilinearTensor(retval, g, T.lowered[0:j] + T.lowered[j + 1:n])
def assemble_algebraic_system(self): if self.verb > 1: print0(self.print_prefix + "Assembling algebraic system.") mat_timer = Timer("---- MTX: Complete construction") ass_timer = Timer("---- MTX: Assembling") add_values_A = False add_values_B = False add_values_Q = False for gto in range(self.DD.G): #=============================== LOOP OVER GROUPS AND ASSEMBLE ================================ spc = self.print_prefix + " " if self.verb > 3 and self.DD.G > 1: print spc + 'GROUP [', gto, ',', gto, '] :' pres_fiss = self.PD.get_xs('chi', self.chi, gto) self.PD.get_xs('D', self.D, gto) self.PD.get_xs('St', self.R, gto) i,j,p,q,k1,k2 = ufl.indices(6) form = ( self.D*self.tensors.T[p,q,i,j]*self.u[gto][q].dx(j)*self.v[gto][p].dx(i) + self.R*self.tensors.W[p,q]*self.u[gto][q]*self.v[gto][p] ) * dx + self.bnd_matrix_form[gto] ass_timer.start() assemble(form, tensor=self.A, finalize_tensor=False, add_values=add_values_A) add_values_A = True ass_timer.stop() if self.fixed_source_problem: if self.PD.isotropic_source_everywhere: self.PD.get_Q(self.fixed_source, 0, gto) # FIXME: This assumes that adjoint source == forward source form = self.fixed_source * self.tensors.Wp[p] * self.v[gto][p] * dx + self.bnd_vector_form[gto] else: form = ufl.zero() for n in range(self.DD.M): self.PD.get_Q(self.fixed_source, n, gto) # FIXME: This assumes that adjoint source == forward source form += self.fixed_source[n,gto] * self.tensors.Wp[n] * self.v[gto][n] * dx + self.bnd_vector_form[gto] ass_timer.start() assemble(form, tensor=self.Q, finalize_tensor=False, add_values=add_values_Q) add_values_Q = True ass_timer.stop() for gfrom in range(self.DD.G): if self.verb > 3 and self.DD.G > 1: print spc + 'GROUP [', gto, ',', gfrom, '] :' pres_Ss = False # TODO: Enlarge self.S and self.C to (L+1)^2 (or 1./2.*(L+1)*(L+2) in 2D) to accomodate for anisotropic # scattering (lines below using CC, SS are correct only for L = 0, when the following inner loop runs only # once. for l in range(self.L+1): for m in range(-l, l+1): if self.DD.angular_quad.get_D() == 2 and divmod(l+m,2)[1] == 0: continue pres_Ss |= self.PD.get_xs('Ss', self.S[l], gto, gfrom, l) self.PD.get_xs('C', self.C[l], gto, gfrom, l) if pres_Ss: Sd = ufl.diag(self.S) SS = self.tensors.QT[p,k1]*Sd[k1,k2]*self.tensors.Q[k2,q] Cd = ufl.diag(self.C) CC = self.tensors.QtT[p,i,k1]*Cd[k1,k2]*self.tensors.Qt[k2,q,j] ass_timer.start() form = ( CC[p,i,q,j]*self.u[gfrom][q].dx(j)*self.v[gto][p].dx(i) - SS[q,p]*self.u[gfrom][q]*self.v[gto][p] ) * dx assemble(form, tensor=self.A, finalize_tensor=False, add_values=add_values_A) ass_timer.stop() if pres_fiss: pres_nSf = self.PD.get_xs('nSf', self.R, gfrom) if pres_nSf: ass_timer.start() if self.fixed_source_problem: form = -self.chi*self.R/(4*numpy.pi)*\ self.tensors.QT[p,0]*self.tensors.Q[0,q]*self.u[gfrom][q]*self.v[gto][p]*dx assemble(form, tensor=self.A, finalize_tensor=False, add_values=add_values_A) else: form = self.chi*self.R/(4*numpy.pi)*\ self.tensors.QT[p,0]*self.tensors.Q[0,q]*self.u[gfrom][q]*self.v[gto][p]*dx assemble(form, tensor=self.B, finalize_tensor=False, add_values=add_values_B) add_values_B = True ass_timer.stop() #============================= END LOOP OVER GROUPS AND ASSEMBLE =============================== self.A.apply("add") if self.fixed_source_problem: self.Q.apply("add") elif self.eigenproblem: self.B.apply("add")
def solve_group_GS(self, it=0, init_slns_ary=None): if self.verb > 1: print0(self.print_prefix + "Solving..." ) if self.eigenproblem: coupled_solver_error(__file__, "solve using group GS", "Group Gauss-Seidel for eigenproblem not yet supported") sol_timer = Timer("-- Complete solution") mat_timer = Timer("---- MTX: Complete construction") ass_timer = Timer("---- MTX: Assembling") sln_timer = Timer("---- SOL: Solving") # To simplify the weak forms u = self.u[0] v = self.v[0] if init_slns_ary is None: init_slns_ary = numpy.zeros((self.DD.G, self.local_sln_size)) for g in range(self.DD.G): self.slns_mg[g].vector()[:] = init_slns_ary[g] err = 0. for gsi in range(self.max_group_GS_it): #========================================== GAUSS-SEIDEL LOOP ============================================ if self.verb > 2: print self.print_prefix + 'Gauss-Seidel iteration {}'.format(gsi) for gto in range(self.DD.G): #========================================= LOOP OVER GROUPS =============================================== self.sln_vec = self.slns_mg[gto].vector() prev_slng_vec = self.aux_slng.vector() prev_slng_vec.zero() prev_slng_vec.axpy(1.0, self.sln_vec) prev_slng_vec.apply("insert") spc = self.print_prefix + " " if self.verb > 3 and self.DD.G > 1: print spc + 'GROUP [', gto, ',', gto, '] :' #==================================== ASSEMBLE WITHIN-GROUP PROBLEM ======================================== mat_timer.start() pres_fiss = self.PD.get_xs('chi', self.chi, gto) self.PD.get_xs('D', self.D, gto) self.PD.get_xs('St', self.R, gto) i,j,p,q,k1,k2 = ufl.indices(6) form = ( self.D*self.tensors.T[p,q,i,j]*u[q].dx(j)*v[p].dx(i) + self.R*self.tensors.W[p,q]*u[q]*v[p] ) * dx + self.bnd_matrix_form[gto] ass_timer.start() add_values_A = False add_values_Q = False assemble(form, tensor=self.A, finalize_tensor=False, add_values=add_values_A) add_values_A = True ass_timer.stop() if self.fixed_source_problem: if self.PD.isotropic_source_everywhere: self.PD.get_Q(self.fixed_source, 0, gto) # FIXME: This assumes that adjoint source == forward source form = self.fixed_source*self.tensors.Wp[p]*v[p]*dx + self.bnd_vector_form[gto] else: form = ufl.zero() for n in range(self.DD.M): self.PD.get_Q(self.fixed_source, n, gto) # FIXME: This assumes that adjoint source == forward source form += self.fixed_source[n,gto] * self.tensors.Wp[n] * v[n] * dx + self.bnd_vector_form[gto] ass_timer.start() assemble(form, tensor=self.Q, finalize_tensor=False, add_values=add_values_Q) add_values_Q = True ass_timer.stop() for gfrom in range(self.DD.G): if self.verb > 3 and self.DD.G > 1: print spc + 'GROUP [', gto, ',', gfrom, '] :' pres_Ss = False # TODO: Enlarge self.S and self.C to (L+1)^2 (or 1./2.*(L+1)*(L+2) in 2D) to accomodate for anisotropic # scattering (lines below using CC, SS are correct only for L = 0, when the following inner loop runs only # once. for l in range(self.L+1): for m in range(-l, l+1): if self.DD.angular_quad.get_D() == 2 and divmod(l+m,2)[1] == 0: continue pres_Ss |= self.PD.get_xs('Ss', self.S[l], gto, gfrom, l) self.PD.get_xs('C', self.C[l], gto, gfrom, l) if pres_Ss: Sd = ufl.diag(self.S) SS = self.tensors.QT[p,k1]*Sd[k1,k2]*self.tensors.Q[k2,q] Cd = ufl.diag(self.C) CC = self.tensors.QtT[p,i,k1]*Cd[k1,k2]*self.tensors.Qt[k2,q,j] ass_timer.start() if gfrom != gto: form = ( SS[p,q]*self.slns_mg[gfrom][q]*v[p] - CC[p,i,q,j]*self.slns_mg[gfrom][q].dx(j)*v[p].dx(i) ) * dx assemble(form, tensor=self.Q, finalize_tensor=False, add_values=add_values_Q) else: form = ( CC[p,i,q,j]*u[q].dx(j)*v[p].dx(i) - SS[q,p]*u[q]*v[p] ) * dx assemble(form, tensor=self.A, finalize_tensor=False, add_values=add_values_A) ass_timer.stop() if pres_fiss: pres_nSf = self.PD.get_xs('nSf', self.R, gfrom) if pres_nSf: ass_timer.start() if gfrom != gto: form = self.chi*self.R/(4*numpy.pi)*\ self.tensors.QT[p,0]*self.tensors.Q[0,q]*self.slns_mg[gfrom][q]*v[p]*dx assemble(form, tensor=self.Q, finalize_tensor=False, add_values=add_values_Q) else: # NOTE: Fixed-source case (eigenproblems can currently be solved only by the coupled-group scheme) if self.fixed_source_problem: form = -self.chi*self.R/(4*numpy.pi)*self.tensors.QT[p,0]*self.tensors.Q[0,q]*u[q]*v[p]*dx assemble(form, tensor=self.A, finalize_tensor=False, add_values=add_values_A) ass_timer.stop() #================================== END ASSEMBLE WITHIN-GROUP PROBLEM ======================================= self.A.apply("add") self.Q.apply("add") mat_timer.stop() self.save_algebraic_system({'A':'A_{}'.format(gto), 'Q':'Q_{}'.format(gto)}, it) #==================================== SOLVE WITHIN-GROUP PROBLEM ========================================== sln_timer.start() dolfin_solve(self.A, self.sln_vec, self.Q, "cg", "petsc_amg") sln_timer.stop() self.up_to_date["flux"] = False err = max(err, delta(self.sln_vec.array(), prev_slng_vec.array())) #==================================== END LOOP OVER GROUPS ==================================== if err < self.parameters["group_GS"]["tol"]: break
def __define_boundary_terms(self): if self.verb > 2: print0("Defining boundary terms") self.bnd_vector_form = numpy.empty(self.DD.G, dtype=object) self.bnd_matrix_form = numpy.empty(self.DD.G, dtype=object) n = FacetNormal(self.DD.mesh) i,p,q = ufl.indices(3) natural_boundaries = self.BC.vacuum_boundaries.union(self.BC.incoming_fluxes.keys()) nonzero = lambda x: numpy.all(x > 0) nonzero_inc_flux = any(map(nonzero, self.BC.incoming_fluxes.values())) if nonzero_inc_flux and not self.fixed_source_problem: coupled_solver_error(__file__, "define boundary terms", "Incoming flux specified for an eigenvalue problem"+\ "(Q must be given whenever phi_inc is; it may possibly be zero everywhere)") for g in range(self.DD.G): self.bnd_vector_form[g] = ufl.zero() self.bnd_matrix_form[g] = ufl.zero() if natural_boundaries: try: ds = Measure("ds")[self.DD.boundaries] except TypeError: coupled_solver_error(__file__, "define boundary terms", "File assigning boundary indices to facets required if vacuum or incoming flux boundaries " "are specified.") for bnd_idx in natural_boundaries: # NOTE: The following doesn't work because ufl.abs requires a function # #for g in range(self.DD.G): # self.bnd_matrix_form[g] += \ # abs(self.tensors.G[p,q,i]*n[i])*self.u[g][q]*self.v[g][p]*ds(bnd_idx) # # NOTE: Instead, the following explicit loop has to be used; this makes tensors.G unneccessary # for pp in range(self.DD.M): omega_p_dot_n = self.DD.ordinates_matrix[i,pp]*n[i] for g in range(self.DD.G): self.bnd_matrix_form[g] += \ abs(omega_p_dot_n)*self.tensors.Wp[pp]*self.u[g][pp]*self.v[g][pp]*ds(bnd_idx) if nonzero_inc_flux: for pp in range(self.DD.M): omega_p_dot_n = self.DD.ordinates_matrix[i,pp]*n[i] for bnd_idx, psi_inc in self.BC.incoming_fluxes.iteritems(): if psi_inc.shape != (self.DD.M, self.DD.G): coupled_solver_error(__file__, "define boundary terms", "Incoming flux with incorrect number of groups and directions specified: "+ "{}, expected ({}, {})".format(psi_inc.shape, self.DD.M, self.DD.G)) for g in range(self.DD.G): self.bnd_vector_form[g] += \ ufl.conditional(omega_p_dot_n < 0, omega_p_dot_n*self.tensors.Wp[pp]*psi_inc[pp,g]*self.v[g][pp]*ds(bnd_idx), ufl.zero()) # FIXME: This assumes zero adjoint outgoing flux else: # Apply vacuum b.c. everywhere ds = Measure("ds") for pp in range(self.DD.M): omega_p_dot_n = self.DD.ordinates_matrix[i,pp]*n[i] for g in range(self.DD.G): self.bnd_matrix_form[g] += abs(omega_p_dot_n)*self.tensors.Wp[pp]*self.u[g][pp]*self.v[g][pp]*ds()
t = variable(t_const) # Choose to have zero acceleration at $t=0$, for convenient # imposition of initial conditions. u_exact = Constant(1e-1) * (t**3) * as_vector( (sin(pi * x[0] / Constant(Lx)) * sin(pi * x[1] / Constant(Ly)), sin(pi * x[0] / Constant(Lx)) * sin(pi * x[1] / Constant(Ly)))) # Constitutive parameters for a St. Venant--Kirchhoff model: rho = Constant(1.0) mu = Constant(1.0) K = Constant(1.0) lam = K - 2.0 * mu / 3.0 I = Identity(d) # Re-usable definitions for nonlinear elasticity: i, j, k, l = ufl.indices(4) Ctens = as_tensor( lam * I[i, j] * I[k, l] + mu * (I[i, k] * I[j, l] + I[i, l] * I[j, k]), (i, j, k, l)) def F(u): return spline.grad(u) + I def S(u): E = 0.5 * (F(u).T * F(u)) i, j, k, l = ufl.indices(4) return as_tensor(Ctens[i, j, k, l] * E[k, l], (i, j))
def S(u): E = 0.5 * (F(u).T * F(u)) i, j, k, l = ufl.indices(4) return as_tensor(Ctens[i, j, k, l] * E[k, l], (i, j))
import ufl import numpy _i, _j = ufl.indices(2) # Heat params # # Volumetric expansion beta_C = 36.0E-6 # K^{-1} # Specific heat capacity C_pc = 0.9E+3 # J kg^{-1} K^{-1} # Heat conduction lambda_c = 1.13 # W m^{-1} K^{-1} room_temp = 293 def inv(A): """Matrix invariants""" return ufl.tr(A), 1. / 2 * A[_i, _j] * A[_i, _j], ufl.det(A) def eig(A): """Eigenvalues of 3x3 tensor""" eps = 1.0e-12 q = ufl.tr(A) / 3.0 p1 = 0.5 * (A[0, 1]**2 + A[1, 0]**2 + A[0, 2]**2 + A[2, 0]**2 + A[1, 2]**2 + A[2, 1]**2) p2 = (A[0, 0] - q)**2 + (A[1, 1] - q)**2 + (A[2, 2] - q)**2 + 2 * p1 p = ufl.sqrt(p2 / 6)
def H(u, udot): i, j, k, l = ufl.indices(4) return as_tensor(c(u)[i, j, k, l] * sym(gradx(u, udot))[k, l], (i, j))
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))