This file is indexed.

/usr/lib/python2.7/dist-packages/ufl/algorithms/change_to_reference.py is in python-ufl 1.4.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
"""Algorithm for replacing gradients in an expression with reference gradients and coordinate mappings."""

# Copyright (C) 2008-2014 Martin Sandve Alnes
#
# This file is part of UFL.
#
# UFL is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# UFL is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with UFL. If not, see <http://www.gnu.org/licenses/>.

from ufl.log import error, warning
from ufl.assertions import ufl_assert
from ufl.classes import (Terminal, ReferenceGrad, Grad,
                         Jacobian, JacobianInverse, JacobianDeterminant,
                         FacetJacobian, FacetJacobianInverse, FacetJacobianDeterminant,
                         CellFacetJacobian, QuadratureWeight)
from ufl.constantvalue import as_ufl
from ufl.algorithms.transformer import ReuseTransformer, apply_transformer
from ufl.algorithms.analysis import extract_type
from ufl.indexing import Index, indices
from ufl.tensors import as_tensor, as_vector
from ufl.compound_expressions import determinant_expr, cross_expr, inverse_expr
from ufl.operators import sqrt

class ChangeToReferenceValue(ReuseTransformer):
    def __init__(self):
        ReuseTransformer.__init__(self)

    def form_argument(self, o):
        # Represent 0-derivatives of form arguments on reference element

        element = o.element()

        local_value = PullbackOf(o) # FIXME implement PullbackOf type

        if isinstance(element, FiniteElement):
            S = element.sobolev_space()
            if S == HDiv:
                # Handle HDiv elements with contravariant piola mapping
                # contravariant_hdiv_mapping = (1/det J) * J * PullbackOf(o)
                J = FIXME
                detJ = FIXME
                mapping = (1/detJ) * J
                i,j = indices(2)
                global_value = as_vector(mapping[i,j] * local_value[j], i)
            elif S == HCurl:
                # Handle HCurl elements with covariant piola mapping
                # covariant_hcurl_mapping = JinvT * PullbackOf(o)
                JinvT = FIXME
                mapping = JinvT
                i,j = indices(2)
                global_value = as_vector(mapping[i,j] * local_value[j], i)
            else:
                # Handle the rest with no mapping.
                global_value = local_value
        else:
            error("FIXME: handle mixed element, components need different mappings")

        return global_value

class ChangeToReferenceGrad(ReuseTransformer):
    def __init__(self):
        ReuseTransformer.__init__(self)

    def grad(self, o):

        # FIXME: Handle HDiv elements with contravariant piola mapping specially?
        # FIXME: Handle HCurl elements with covariant piola mapping specially?

        # Peel off the Grads and count them
        ngrads = 0
        while isinstance(o, Grad):
            o, = o.operands()
            ngrads += 1
        f = o

        # Get domain and create Jacobian inverse object
        domain = f.domain()
        Jinv = JacobianInverse(domain)

        # This is an assumption in the below code TODO: Handle grad(grad(.)) for non-affine domains.
        ufl_assert(ngrads == 1 or Jinv.is_cellwise_constant(),
                   "Multiple grads for non-affine domains not currently supported in this algorithm.")

        # Create some new indices
        ii = indices(f.rank()) # Indices to get to the scalar component of f
        jj = indices(ngrads)   # Indices to sum over the local gradient axes with the inverse Jacobian
        kk = indices(ngrads)   # Indices for the leftover inverse Jacobian axes

        # Apply the same number of ReferenceGrad without mappings
        lgrad = f
        for i in xrange(ngrads):
            lgrad = ReferenceGrad(lgrad)

        # Apply mappings with scalar indexing operations (assumes ReferenceGrad(Jinv) is zero)
        jinv_lgrad_f = lgrad[ii+jj]
        for j,k in zip(jj,kk):
            jinv_lgrad_f = Jinv[j,k]*jinv_lgrad_f

        # Wrap back in tensor shape, derivative axes at the end
        jinv_lgrad_f = as_tensor(jinv_lgrad_f, ii+kk)

        return jinv_lgrad_f

    def reference_grad(self, o):
        error("Not expecting reference grad while applying change to reference grad.")

    def coefficient_derivative(self, o):
        error("Coefficient derivatives should be expanded before applying change to reference grad.")

class ChangeToReferenceGeometry(ReuseTransformer):
    def __init__(self, physical_coordinates_known):
        ReuseTransformer.__init__(self)
        self.physical_coordinates_known = physical_coordinates_known
        self._rcache = {}

    def jacobian(self, o):
        r = self._rcache.get(o)
        if r is None:
            domain = o.domain()
            x = domain.coordinates()
            if x is None:
                r = o
            else:
                r = ReferenceGrad(x)
            self._rcache[o] = r
        return r

    def jacobian_inverse(self, o):
        r = self._rcache.get(o)
        if r is None:
            domain = o.domain()
            J = self.jacobian(Jacobian(domain))
            r = inverse_expr(J)
            self._rcache[o] = r
        return r

    def jacobian_determinant(self, o):
        r = self._rcache.get(o)
        if r is None:
            domain = o.domain()
            J = self.jacobian(Jacobian(domain))
            r = determinant_expr(J)
            self._rcache[o] = r
        return r

    def facet_jacobian(self, o):
        r = self._rcache.get(o)
        if r is None:
            domain = o.domain()
            J = self.jacobian(Jacobian(domain))
            RFJ = CellFacetJacobian(domain)
            i,j,k = indices(3)
            r = as_tensor(J[i,k]*RFJ[k,j], (i,j))
            self._rcache[o] = r
        return r

    def facet_jacobian_inverse(self, o):
        r = self._rcache.get(o)
        if r is None:
            domain = o.domain()
            FJ = self.facet_jacobian(FacetJacobian(domain))
            r = inverse_expr(FJ)
            self._rcache[o] = r
        return r

    def facet_jacobian_determinant(self, o):
        r = self._rcache.get(o)
        if r is None:
            domain = o.domain()
            FJ = self.facet_jacobian(FacetJacobian(domain))
            r = determinant_expr(FJ)
            self._rcache[o] = r
        return r

    def spatial_coordinate(self, o):
        "Fall through to coordinate field of domain if it exists."
        if self.physical_coordinates_known:
            return o
        else:
            domain = o.domain()
            x = domain.coordinates()
            if x is None:
                return o
            else:
                return x

    def cell_coordinate(self, o):
        "Compute from physical coordinates if they are known, using the appropriate mappings."
        if self.physical_coordinates_known:
            r = self._rcache.get(o)
            if r is None:
                K = self.jacobian_inverse(JacobianInverse(domain))
                x = self.spatial_coordinate(SpatialCoordinate(domain))
                x0 = CellOrigin(domain)
                i,j = indices(2)
                X = as_tensor(K[i,j] * (x[j] - x0[j]), (i,))
                r = X
            return r
        else:
            return o

    def facet_cell_coordinate(self, o):
        if self.physical_coordinates_known:
            error("Missing computation of facet reference coordinates from physical coordinates via mappings.")
        else:
            return o

    def cell_normal(self, o):
        warning("Untested complicated code for cell normal. Please report if this works correctly or not.")

        r = self._rcache.get(o)
        if r is None:
            domain = o.domain()
            gdim = domain.geometric_dimension()
            tdim = domain.topological_dimension()

            if tdim == gdim - 1:

                if tdim == 2: # Surface in 3D
                    J = self.jacobian(Jacobian(domain))
                    cell_normal = cross_expr(J[:,0], J[:,1])

                elif tdim == 1: # Line in 2D
                    # TODO: Sign here is ambiguous, which normal?
                    cell_normal = as_vector((J[1,0], -J[0,0]))

                i = Index()
                cell_normal = cell_normal / sqrt(cell_normal[i]*cell_normal[i])

            elif tdim == gdim:
                cell_normal = as_vector((0.0,)*tdim + (1.0,))

            else:
                error("What do you mean by cell normal in gdim={0}, tdim={1}?".format(gdim, tdim))

            r = cell_normal
        return r

    def facet_normal(self, o):
        warning("Untested complicated code for facet normal. Please report if this works correctly or not.")

        r = self._rcache.get(o)
        if r is None:
            domain = o.domain()
            gdim = domain.geometric_dimension()
            tdim = domain.topological_dimension()

            FJ = self.facet_jacobian(FacetJacobian(domain))

            if tdim == 3:
                ufl_assert(gdim == 3, "Inconsistent dimensions.")
                ufl_assert(FJ.shape() == (3,2), "Inconsistent dimensions.")

                # Compute signed scaling factor
                scale = self.jacobian_determinant(JacobianDeterminant(domain))

                # Compute facet normal direction of 3D cell, product of two tangent vectors
                ndir = scale * cross_expr(FJ[:,0], FJ[:,1])

                # Normalise normal vector
                i = Index()
                n = ndir / sqrt(ndir[i]*ndir[i])
                r = n

            elif tdim == 2:
                if gdim == 2:
                    ufl_assert(FJ.shape() == (2,1), "Inconsistent dimensions.")

                    # Compute facet tangent
                    tangent = as_vector((FJ[0,0], FJ[1,0], 0))

                    # Compute cell normal
                    cell_normal = as_vector((0, 0, 1))

                    # Compute signed scaling factor
                    scale = self.jacobian_determinant(JacobianDeterminant(domain))
                else:
                    ufl_assert(FJ.shape() == (gdim,1), "Inconsistent dimensions.")

                    # Compute facet tangent
                    tangent = FJ[:,0]

                    # Compute cell normal
                    cell_normal = self.cell_normal(CellNormal(domain))

                    # Compute signed scaling factor (input in the manifold case)
                    scale = CellOrientation(domain)

                ufl_assert(len(tangent) == 3, "Inconsistent dimensions.")
                ufl_assert(len(cell_normal) == 3, "Inconsistent dimensions.")

                # Compute normal direction
                ndir = scale * cross_expr(tangent, cell_normal)

                # Normalise normal vector
                i = Index()
                n = ndir / sqrt(ndir[i]*ndir[i])
                r = n

            self._rcache[o] = r
        return r


def change_to_reference_grad(e):
    """Change Grad objects in expression to products of JacobianInverse and ReferenceGrad.

    Assumes the expression is preprocessed or at least that derivatives have been expanded.

    @param e:
        An Expr or Form.
    """
    return apply_transformer(e, ChangeToReferenceGrad())


def change_to_reference_geometry(e, physical_coordinates_known):
    """Change GeometricQuantity objects in expression to the lowest level GeometricQuantity objects.

    Assumes the expression is preprocessed or at least that derivatives have been expanded.

    @param e:
        An Expr or Form.
    """
    return apply_transformer(e, ChangeToReferenceGeometry(physical_coordinates_known))


def compute_integrand_scaling_factor(domain, integral_type):
    """Change integrand geometry to the right representations."""

    weight = QuadratureWeight(domain)

    if integral_type == "cell":
        scale = abs(JacobianDeterminant(domain)) * weight
    elif integral_type in ["exterior_facet", "exterior_facet_bottom", "exterior_facet_top", "exterior_facet_vert"]:
        scale = FacetJacobianDeterminant(domain) * weight
    elif integral_type in ["interior_facet", "interior_facet_horiz", "interior_facet_vert"]:
        scale = FacetJacobianDeterminant(domain)('-') * weight # TODO: Arbitrary restriction to '-', is that ok?
    elif integral_type == "quadrature":
        scale = weight
    elif integral_type == "point":
        scale = 1

    return scale


def change_integrand_geometry_representation(integrand, scale, integral_type):
    """Change integrand geometry to the right representations."""

    integrand = change_to_reference_grad(integrand)

    integrand = integrand * scale

    if integral_type == "quadrature":
        physical_coordinates_known = True
    else:
        physical_coordinates_known = False
    integrand = change_to_reference_geometry(integrand, physical_coordinates_known)

    return integrand