/usr/lib/python2.7/dist-packages/FIAT/trace.py is in python-fiat 1.6.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 | # Copyright (C) 2012-2015 Marie E. Rognes and David A. Ham
#
# This file is part of FIAT.
#
# FIAT 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.
#
# FIAT 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 FIAT. If not, see <http://www.gnu.org/licenses/>.
from __future__ import print_function
import numpy
from FIAT.discontinuous_lagrange import DiscontinuousLagrange
from FIAT.reference_element import ufc_simplex
from FIAT.functional import PointEvaluation
from FIAT.polynomial_set import mis
# Tolerance for geometry identifications
epsilon = 1.e-8
def extract_unique_facet(coordinates, tolerance=epsilon):
"""Determine whether a set of points, each point described by its
barycentric coordinates ('coordinates'), are all on one of the
facets and return this facet and whether search has been
successful.
"""
facets = []
for c in coordinates:
on_facet = set([i for (i, l) in enumerate(c) if abs(l) < tolerance])
facets += [on_facet]
unique_facet = facets[0]
for e in facets:
unique_facet = unique_facet & e
# Handle coordinates not on facets somewhat gracefully
if (len(unique_facet) != 1):
return (None, False)
# If we have a unique facet, return it and success
return (unique_facet.pop(), True)
def barycentric_coordinates(points, vertices):
"""Compute barycentric coordinates for a set of points ('points'),
relative to a simplex defined by a set of vertices ('vertices').
"""
# Form map matrix
last = numpy.asarray(vertices[-1])
T = numpy.matrix([numpy.array(v) - last for v in vertices[:-1]]).T
detT = numpy.linalg.det(T)
invT = numpy.linalg.inv(T)
# Compute barycentric coordinates for all points
coords = []
for p in points:
y = numpy.asarray(p) - last
lam = invT.dot(y.T)
lam = [lam[(0, i)] for i in range(len(y))]
lam += [1.0 - sum(lam)]
coords.append(lam)
return coords
def map_from_reference_facet(point, vertices):
"""
Input:
vertices: the vertices defining the physical facet
point: the reference point to be mapped to the facet
"""
# Compute barycentric coordinates of point relative to reference facet:
reference_simplex = ufc_simplex(len(vertices)-1)
reference_vertices = reference_simplex.get_vertices()
coords = barycentric_coordinates([point,], reference_vertices)[0]
# Evaluate physical coordinate of point using barycentric coordinates
point = sum(vertices[j]*coords[j] for j in range(len(coords)))
return tuple(point)
def map_to_reference_facet(points, vertices, facet):
"""Given a set of points in n D and a set of vertices describing a
facet of a simplex in n D (where the given points lie on this
facet) map the points to the reference simplex of dimension (n-1).
"""
# Compute barycentric coordinates of points with respect to
# the full physical simplex
all_coords = barycentric_coordinates(points, vertices)
# Extract vertices of reference facet simplex
reference_facet_simplex = ufc_simplex(len(vertices)-2)
ref_vertices = reference_facet_simplex.get_vertices()
reference_points = []
for (i, coords) in enumerate(all_coords):
# Extract correct subset of barycentric coordinates since we
# know which facet we are on
new_coords = [coords[j] for j in range(len(coords)) if (j != facet)]
# Evaluate reference coordinate of point using revised
# barycentric coordinates
reference_pt = sum(numpy.asarray(ref_vertices[j])*new_coords[j]
for j in range(len(new_coords)))
reference_points += [reference_pt]
return reference_points
class DiscontinuousLagrangeTrace(object):
""
def __init__(self, cell, k):
tdim = cell.get_spatial_dimension()
assert (tdim == 2 or tdim == 3)
# Store input cell and polynomial degree (k)
self.cell = cell
self.k = k
# Create DG_k space on the facet(s) of the cell
self.facet = ufc_simplex(tdim - 1)
self.DG = DiscontinuousLagrange(self.facet, k)
# Count number of facets for given cell. Assumption: we are on
# simplices
self.num_facets = tdim + 1
# Construct entity ids. Initialize all to empty, will fill
# later.
self.entity_ids = {}
topology = cell.get_topology()
for dim, entities in topology.items():
self.entity_ids[dim] = {}
for entity in entities:
self.entity_ids[dim][entity] = {}
# For each facet, we have dim(DG_k on that facet) number of dofs
n = self.DG.space_dimension()
for i in range(self.num_facets):
self.entity_ids[tdim-1][i] = range(i*n, (i+1)*n)
def degree(self):
return self.k
def value_shape(self):
return ()
def space_dimension(self):
"""The space dimension of the trace space corresponds to the
DG space dimesion on each facet times the number of facets."""
return self.DG.space_dimension()*self.num_facets
def entity_dofs(self):
return self.entity_ids
def mapping(self):
return ["affine" for i in range(self.space_dimension())]
def dual_basis(self):
# First create the points
points = []
# For each facet, map the subcomplex DG_k dofs from the lower
# dimensional reference element onto the facet and add to list
# of points
DG_k_dual_basis = self.DG.dual_basis()
t_dim = self.cell.get_spatial_dimension()
facets2indices = self.cell.get_topology()[t_dim - 1]
# Iterate over the facets and add points on each facet
for (facet, indices) in facets2indices.items():
vertices = self.cell.get_vertices_of_subcomplex(indices)
vertices = numpy.array(vertices)
for dof in DG_k_dual_basis:
# PointEvaluation only carries one point
point = list(dof.get_point_dict().keys())[0]
pt = map_from_reference_facet([point,], vertices)
points.append(pt)
# One degree of freedom per point:
nodes = [PointEvaluation(self.cell, x) for x in points]
return nodes
def tabulate(self, order, points):
"""Return tabulated values of derivatives up to given order of
basis functions at given points."""
# Standard derivatives don't make sense, but return zero
# because mixed elements compute all derivatives at once
if (order > 0):
values = {}
sdim = self.space_dimension()
alphas = mis(self.cell.get_spatial_dimension(), order)
for alpha in alphas:
values[alpha] = numpy.zeros(shape=(sdim, len(points)))
return values
# Identify which facet (if any) these points are on:
vertices = self.cell.vertices
coordinates = barycentric_coordinates(points, vertices)
(unique_facet, success) = extract_unique_facet(coordinates)
# All other basis functions evaluate to zero, so create an
# array of the right size
sdim = self.space_dimension()
values = numpy.zeros(shape=(sdim, len(points)))
# ... and plug in the non-zero values in just the right place
# if we found a unique facet
if success:
# Map point to "reference facet" (facet -> interval etc)
new_points = map_to_reference_facet(points, vertices, unique_facet)
# Call self.DG.tabulate(order, new_points) to compute the
# values of the points for the degrees of freedom on this facet
non_zeros = list(self.DG.tabulate(order, new_points).values())[0]
m = non_zeros.shape[0]
dg_dim = self.DG.space_dimension()
values[dg_dim*unique_facet:dg_dim*unique_facet+m, :] = non_zeros
# Return expected dictionary
tdim = self.cell.get_spatial_dimension()
key = tuple(0 for i in range(tdim))
return {key: values}
# These functions are only needed for evaluatebasis and
# evaluatebasisderivatives, disable those, and we should be in
# business.
def get_coeffs(self):
"""Return the expansion coefficients for the basis of the
finite element."""
msg = "Not implemented: shouldn't be implemented."
raise Exception(msg)
def get_num_members(self, arg):
msg = "Not implemented: shouldn't be implemented."
raise Exception(msg)
def dmats(self):
msg = "Not implemented."
raise Exception(msg)
def __str__(self):
return "DiscontinuousLagrangeTrace(%s, %s)" % (self.cell, self.k)
if __name__ == "__main__":
print("\n2D ----------------")
T = ufc_simplex(2)
element = DiscontinuousLagrangeTrace(T, 1)
pts = [(0.0, 1.0), (1.0, 0.0)]
print("values = ", element.tabulate(0, pts))
print("\n3D ----------------")
T = ufc_simplex(3)
element = DiscontinuousLagrangeTrace(T, 1)
pts = [(0.1, 0.0, 0.0), (0.0, 1.0, 0.0)]
print("values = ", element.tabulate(0, pts))
|