# -*- coding: utf-8 -*-
# Copyright (C) 2009-2017 Anders Logg and Martin Sandve Alnæs
#
# This file is part of UFLACS.
#
# UFLACS 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.
#
# UFLACS 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 UFLACS. If not, see <http://www.gnu.org/licenses/>.
# Note: Most of the code in this file is a direct translation from the old implementation in FFC
from ffc.uflacs.backends.ufc.generator import ufc_generator
from ffc.uflacs.backends.ufc.utils import generate_return_new_switch, generate_return_sizet_switch, generate_return_bool_switch
[docs]class ufc_dofmap(ufc_generator):
"Each function maps to a keyword in the template. See documentation of ufc_generator."
def __init__(self):
ufc_generator.__init__(self, "dofmap")
[docs] def topological_dimension(self, L, topological_dimension):
return L.Return(topological_dimension)
[docs] def needs_mesh_entities(self, L, needs_mesh_entities):
"needs_mesh_entities is a list of num dofs per entity."
return generate_return_bool_switch(L, "d", needs_mesh_entities, False)
[docs] def global_dimension(self, L, ir):
# A list of num dofs per entity
num_entity_dofs, num_global_dofs = ir["global_dimension"]
# Input array
entities = L.Symbol("num_global_entities")
# Accumulate number of dofs per entity times given number of entities in mesh
entity_dofs = []
for dim, num in enumerate(num_entity_dofs):
if num == 1:
entity_dofs.append(entities[dim])
elif num > 1:
entity_dofs.append(num * entities[dim])
if entity_dofs:
dimension = L.Sum(entity_dofs)
else:
dimension = 0
# Add global dofs if any
if num_global_dofs:
dimension = dimension + num_global_dofs
return L.Return(dimension)
[docs] def num_global_support_dofs(self, L, num_global_support_dofs):
return L.Return(num_global_support_dofs)
[docs] def num_element_support_dofs(self, L, num_element_support_dofs):
return L.Return(num_element_support_dofs)
[docs] def num_element_dofs(self, L, num_element_dofs):
return L.Return(num_element_dofs)
[docs] def num_facet_dofs(self, L, num_facet_dofs):
return L.Return(num_facet_dofs)
[docs] def num_entity_dofs(self, L, num_entity_dofs):
return generate_return_sizet_switch(L, "d", num_entity_dofs, 0)
[docs] def num_entity_closure_dofs(self, L, num_entity_closure_dofs):
return generate_return_sizet_switch(L, "d", num_entity_closure_dofs, 0)
[docs] def tabulate_dofs(self, L, ir):
# Input arguments
entity_indices = L.Symbol("entity_indices")
num_mesh_entities = L.Symbol("num_global_entities")
# Output arguments
dofs_variable = L.Symbol("dofs")
ir = ir["tabulate_dofs"]
if ir is None:
# Special case for SpaceOfReals, ir returns None
# FIXME: This is how the old code did in this case,
# is it correct? Is there only 1 dof?
# I guess VectorElement(Real) handled elsewhere?
code = [L.Assign(dofs_variable[0], 0)]
return L.StatementList(code)
# Extract representation
#(dofs_per_element, num_dofs_per_element, need_offset, fakes) = ir # names from original ffc code
(subelement_dofs, num_dofs_per_subelement, need_offset, is_subelement_real) = ir
#len(is_subelement_real) == len(subelement_dofs) == len(num_dofs_per_subelement) == number of combined (flattened) subelements?
#is_subelement_real[subelement_number] is bool, is subelement Real?
#num_dofs_per_subelement[subelement_number] is int, number of dofs for each (flattened) subelement
#subelement_dofs[subelement_number] is list of entity_dofs for each (flattened) subelement
#len(entity_dofs) == tdim+1
#len(entity_dofs[cell_entity_dim]) == "num_cell_entities[dim]" (i.e. 3 vertices, 3 edges, 1 face for triangle)
#len(entity_dofs[cell_entity_dim][entity_index[dim][k]]) == number of dofs for this entity
#num = entity_dofs[dim]
#entity_dofs =? entity_dofs[d][i][:] # dofs on entity (d,i)
# Collect code pieces in list
code = []
# Declare offset if needed
if need_offset:
offset = L.Symbol("offset")
code.append(L.VariableDecl("std::size_t", offset, value=0))
else:
offset = 0
# Generate code for each element
subelement_offset = 0
for (subelement_index, entity_dofs) in enumerate(subelement_dofs):
# Handle is_subelement_real (Space of reals)
if is_subelement_real[subelement_index]:
assert num_dofs_per_subelement[subelement_index] == 1
code.append(L.Assign(dofs_variable[subelement_offset], offset))
if need_offset:
code.append(L.AssignAdd(offset, 1))
subelement_offset += 1
continue
# Generate code for each degree of freedom for each dimension
for (cell_entity_dim, dofs_on_cell_entity) in enumerate(entity_dofs):
num_dofs_per_mesh_entity = len(dofs_on_cell_entity[0])
assert all(num_dofs_per_mesh_entity == len(dofs)
for dofs in dofs_on_cell_entity)
# Ignore if no dofs for this dimension
if num_dofs_per_mesh_entity == 0:
continue
# For each cell entity of this dimension
for (cell_entity_index, dofs) in enumerate(dofs_on_cell_entity):
# dofs is a list of the local dofs that live on this cell entity
# find offset for this particular mesh entity
entity_offset = len(dofs) * entity_indices[cell_entity_dim, cell_entity_index]
for (j, dof) in enumerate(dofs):
# dof is the local dof index on the subelement
# j is the local index of dof among the dofs
# on this particular cell/mesh entity
local_dof_index = subelement_offset + dof
global_dof_index = offset + entity_offset + j
code.append(L.Assign(dofs_variable[local_dof_index], global_dof_index))
# Update offset corresponding to mesh entity:
if need_offset:
value = num_dofs_per_mesh_entity * num_mesh_entities[cell_entity_dim]
code.append(L.AssignAdd(offset, value))
subelement_offset += num_dofs_per_subelement[subelement_index]
return L.StatementList(code)
[docs] def tabulate_facet_dofs(self, L, ir):
all_facet_dofs = ir["tabulate_facet_dofs"]
# Input arguments
facet = L.Symbol("facet")
dofs = L.Symbol("dofs")
# For each facet, copy all_facet_dofs[facet][:] into output argument array dofs[:]
cases = []
for f, single_facet_dofs in enumerate(all_facet_dofs):
assignments = [L.Assign(dofs[i], dof)
for (i, dof) in enumerate(single_facet_dofs)]
if assignments:
cases.append((f, L.StatementList(assignments)))
if cases:
return L.Switch(facet, cases, autoscope=False)
else:
return L.NoOp()
[docs] def tabulate_entity_dofs(self, L, ir):
entity_dofs, num_dofs_per_entity = ir["tabulate_entity_dofs"]
# Output argument array
dofs = L.Symbol("dofs")
# Input arguments
d = L.Symbol("d")
i = L.Symbol("i")
# TODO: Removed check for (d <= tdim + 1)
tdim = len(num_dofs_per_entity) - 1
# Generate cases for each dimension:
all_cases = []
for dim in range(tdim + 1):
# Ignore if no entities for this dimension
if num_dofs_per_entity[dim] == 0:
continue
# Generate cases for each mesh entity
cases = []
for entity in range(len(entity_dofs[dim])):
casebody = []
for (j, dof) in enumerate(entity_dofs[dim][entity]):
casebody += [L.Assign(dofs[j], dof)]
cases.append((entity, L.StatementList(casebody)))
# Generate inner switch
# TODO: Removed check for (i <= num_entities-1)
inner_switch = L.Switch(i, cases, autoscope=False)
all_cases.append((dim, inner_switch))
if all_cases:
return L.Switch(d, all_cases, autoscope=False)
else:
return L.NoOp()
[docs] def tabulate_entity_closure_dofs(self, L, ir):
# Extract variables from ir
entity_closure_dofs, entity_dofs, num_dofs_per_entity = \
ir["tabulate_entity_closure_dofs"]
# Output argument array
dofs = L.Symbol("dofs")
# Input arguments
d = L.Symbol("d")
i = L.Symbol("i")
# TODO: Removed check for (d <= tdim + 1)
tdim = len(num_dofs_per_entity) - 1
# Generate cases for each dimension:
all_cases = []
for dim in range(tdim + 1):
num_entities = len(entity_dofs[dim])
# Generate cases for each mesh entity
cases = []
for entity in range(num_entities):
casebody = []
for (j, dof) in enumerate(entity_closure_dofs[(dim, entity)]):
casebody += [L.Assign(dofs[j], dof)]
cases.append((entity, L.StatementList(casebody)))
# Generate inner switch unless empty
if cases:
# TODO: Removed check for (i <= num_entities-1)
inner_switch = L.Switch(i, cases, autoscope=False)
all_cases.append((dim, inner_switch))
if all_cases:
return L.Switch(d, all_cases, autoscope=False)
else:
return L.NoOp()
[docs] def num_sub_dofmaps(self, L, num_sub_dofmaps):
return L.Return(num_sub_dofmaps)
[docs] def create_sub_dofmap(self, L, ir):
classnames = ir["create_sub_dofmap"]
return generate_return_new_switch(L, "i", classnames, factory=ir["jit"])