PageRenderTime 61ms CodeModel.GetById 39ms RepoModel.GetById 0ms app.codeStats 0ms

/uflacs/backends/ffc/definitions.py

https://bitbucket.org/tferma/ffc
Python | 290 lines | 153 code | 47 blank | 90 comment | 19 complexity | 819af92181583418f4763d1bbadb068e MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. # Copyright (C) 2011-2015 Martin Sandve Alnæs
  3. #
  4. # This file is part of UFLACS.
  5. #
  6. # UFLACS is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU Lesser General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # UFLACS is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU Lesser General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU Lesser General Public License
  17. # along with UFLACS. If not, see <http://www.gnu.org/licenses/>
  18. """FFC specific definitions."""
  19. from six.moves import xrange as range
  20. from ufl.corealg.multifunction import MultiFunction
  21. from ufl.checks import is_cellwise_constant
  22. from ffc.log import error
  23. from uflacs.backends.ffc.common import FFCBackendSymbols
  24. # FIXME: Move these to FFCBackendSymbols
  25. #from uflacs.backends.ffc.common import format_entity_name
  26. from uflacs.backends.ffc.common import ufc_restriction_postfix
  27. from ufl.cell import num_cell_entities
  28. def num_coordinate_component_dofs(coordinate_element):
  29. """Get the number of dofs for a coordinate component for this degree.
  30. This is a local hack that works for Lagrange 1-3, better
  31. would be to get this passed by ffc from fiat through the ir.
  32. The table data is to messy to figure out a clean design for that quickly.
  33. """
  34. degree = coordinate_element.degree()
  35. cell = coordinate_element.cell()
  36. tdim = cell.topological_dimension()
  37. cellname = cell.cellname()
  38. d = 0
  39. for entity_dim in range(tdim+1):
  40. # n = dofs per cell entity
  41. if entity_dim == 0:
  42. n = 1
  43. elif entity_dim == 1:
  44. n = degree - 1
  45. elif entity_dim == 2:
  46. n = (degree - 2)*(degree - 1) // 2
  47. elif entity_dim == 3:
  48. n = (degree - 3)*(degree - 2)*(degree - 1) // 6
  49. else:
  50. error("Entity dimension out of range")
  51. # Accumulate
  52. num_entities = num_cell_entities[cellname][entity_dim]
  53. d += num_entities * n
  54. return d
  55. class FFCDefinitionsBackend(MultiFunction):
  56. """FFC specific code definitions."""
  57. def __init__(self, ir, language, parameters):
  58. MultiFunction.__init__(self)
  59. # Store ir and parameters
  60. self.ir = ir
  61. self.language = language
  62. self.parameters = parameters
  63. # FIXME: Make this configurable for easy experimentation with dolfin!
  64. # Coordinate dofs for each component are interleaved? Must match dolfin.
  65. self.interleaved_components = True # parameters["interleaved_coordinate_component_dofs"]
  66. # Configure definitions behaviour
  67. if self.ir["integral_type"] in ("custom", "vertex"):
  68. self.physical_coordinates_known = True
  69. else:
  70. self.physical_coordinates_known = False
  71. # Need this for custom integrals
  72. #classname = make_classname(prefix, "finite_element", ir["element_numbers"][ufl_element])
  73. coefficient_numbering = ir["uflacs"]["coefficient_numbering"]
  74. self.symbols = FFCBackendSymbols(self.language, coefficient_numbering)
  75. def get_includes(self):
  76. "Return include statements to insert at top of file."
  77. includes = []
  78. return includes
  79. def initial(self):
  80. "Return code inserted at beginning of kernel."
  81. return []
  82. def expr(self, t, mt, tabledata, access):
  83. error("Unhandled type {0}".format(type(t)))
  84. # === Generate code definitions ===
  85. def quadrature_weight(self, e, mt, tabledata, access):
  86. return []
  87. def constant_value(self, e, mt, tabledata, access):
  88. return []
  89. def argument(self, t, mt, tabledata, access):
  90. return []
  91. def coefficient(self, t, mt, tabledata, access):
  92. L = self.language
  93. # For a constant coefficient we reference the dofs directly, so no definition needed
  94. if is_cellwise_constant(mt.terminal):
  95. return []
  96. # No need to store basis function value in its own variable,
  97. # just get table value directly
  98. uname, begin, end = tabledata
  99. uname = L.Symbol(uname)
  100. # Empty loop needs to be skipped as zero tables may not be generated
  101. # FIXME: remove at earlier stage so dependent code can also be removed
  102. if begin >= end:
  103. code = [
  104. L.VariableDecl("double", access, 0.0),
  105. ]
  106. return code
  107. # Get various symbols
  108. entity = self.symbols.entity(self.ir["entitytype"], mt.restriction)
  109. iq = self.symbols.quadrature_loop_index()
  110. idof = self.symbols.coefficient_dof_sum_index()
  111. dof_access = self.symbols.coefficient_dof_access(mt.terminal, idof)
  112. table_access = uname[entity][iq][idof - begin]
  113. # Loop to accumulate linear combination of dofs and tables
  114. code = [
  115. L.VariableDecl("double", access, 0.0),
  116. L.ForRange(idof, begin, end,
  117. body=[L.AssignAdd(access, dof_access * table_access)])
  118. ]
  119. return code
  120. def _define_coordinate_dofs_lincomb(self, e, mt, tabledata, access):
  121. "Define something (x or J) linear combination of coordinate dofs with given table data."
  122. L = self.language
  123. # Get properties of domain
  124. domain = mt.terminal.ufl_domain()
  125. #tdim = domain.topological_dimension()
  126. gdim = domain.geometric_dimension()
  127. coordinate_element = domain.ufl_coordinate_element()
  128. #degree = coordinate_element.degree()
  129. num_scalar_dofs = num_coordinate_component_dofs(coordinate_element)
  130. # Reference coordinates are known, no coordinate field, so we compute
  131. # this component as linear combination of coordinate_dofs "dofs" and table
  132. uname, begin, end = tabledata
  133. uname = L.Symbol(uname)
  134. #if not ( end - begin <= num_scalar_dofs):
  135. # import IPython; IPython.embed()
  136. assert end - begin <= num_scalar_dofs
  137. entity = self.symbols.entity(self.ir["entitytype"], mt.restriction)
  138. if is_cellwise_constant(mt.expr):
  139. iq = 0
  140. else:
  141. iq = self.symbols.quadrature_loop_index()
  142. if 0: # FIXME: Make an option to test
  143. # Generated loop version:
  144. coefficient_dof = self.symbols.coefficient_dof_sum_index()
  145. dof_access = self.symbols.domain_dof_access(coefficient_dof, mt.flat_component,
  146. gdim, num_scalar_dofs,
  147. mt.restriction, self.interleaved_components)
  148. table_access = uname[entity][iq][coefficient_dof]
  149. # Loop to accumulate linear combination of dofs and tables
  150. code = [
  151. L.VariableDecl("double", access, 0.0),
  152. L.ForRange(coefficient_dof, begin, end,
  153. body=[L.AssignAdd(access, dof_access * table_access)])
  154. ]
  155. else:
  156. # Inlined version (we know this is bounded by a small number)
  157. dof_access = self.symbols.domain_dofs_access(gdim, num_scalar_dofs,
  158. mt.restriction,
  159. self.interleaved_components)
  160. value = L.Sum([dof_access[idof] * uname[entity][iq][idof - begin]
  161. for idof in range(begin, end)])
  162. # Inlined loop to accumulate linear combination of dofs and tables
  163. code = [
  164. L.VariableDecl("const double", access, value)
  165. ]
  166. return code
  167. def spatial_coordinate(self, e, mt, tabledata, access):
  168. """Return definition code for the physical spatial coordinates.
  169. If physical coordinates are given:
  170. No definition needed.
  171. If reference coordinates are given:
  172. x = sum_k xdof_k xphi_k(X)
  173. If reference facet coordinates are given:
  174. x = sum_k xdof_k xphi_k(Xf)
  175. """
  176. if self.physical_coordinates_known:
  177. return []
  178. else:
  179. return self._define_coordinate_dofs_lincomb(e, mt, tabledata, access)
  180. def cell_coordinate(self, e, mt, tabledata, access):
  181. """Return definition code for the reference spatial coordinates.
  182. If reference coordinates are given::
  183. No definition needed.
  184. If physical coordinates are given and domain is affine::
  185. X = K*(x-x0)
  186. This is inserted symbolically.
  187. If physical coordinates are given and domain is non- affine::
  188. Not currently supported.
  189. """
  190. return []
  191. def jacobian(self, e, mt, tabledata, access):
  192. """Return definition code for the Jacobian of x(X).
  193. J = sum_k xdof_k grad_X xphi_k(X)
  194. """
  195. if self.physical_coordinates_known:
  196. return []
  197. else:
  198. return self._define_coordinate_dofs_lincomb(e, mt, tabledata, access)
  199. def cell_orientation(self, e, mt, tabledata, access):
  200. # Would be nicer if cell_orientation was a double variable input,
  201. # but this is how dolfin/ufc/ffc currently passes this information.
  202. # 0 means up and gives +1.0, 1 means down and gives -1.0.
  203. L = self.language
  204. co = "cell_orientation" + ufc_restriction_postfix(mt.restriction)
  205. expr = L.VerbatimExpr("(" + co + " == 1) ? -1.0: +1.0;")
  206. code = [
  207. L.VariableDecl("const double", access, expr)
  208. ]
  209. return code
  210. def _expect_table(self, e, mt, tabledata, access):
  211. "These quantities refer to constant tables defined in ufc_geometry.h."
  212. # TODO: Inject const static table here instead?
  213. return []
  214. reference_cell_volume = _expect_table
  215. reference_facet_volume = _expect_table
  216. reference_normal = _expect_table
  217. cell_facet_jacobian = _expect_table
  218. cell_edge_vectors = _expect_table
  219. facet_edge_vectors = _expect_table
  220. facet_orientation = _expect_table
  221. def _expect_symbolic_lowering(self, e, mt, tabledata, access):
  222. "These quantities are expected to be replaced in symbolic preprocessing."
  223. error("Expecting {0} to be replaced in symbolic preprocessing.".format(type(e)))
  224. facet_normal = _expect_symbolic_lowering
  225. cell_normal = _expect_symbolic_lowering
  226. jacobian_inverse = _expect_symbolic_lowering
  227. jacobian_determinant = _expect_symbolic_lowering
  228. facet_jacobian = _expect_symbolic_lowering
  229. facet_jacobian_inverse = _expect_symbolic_lowering
  230. facet_jacobian_determinant = _expect_symbolic_lowering