/usr/lib/python2.7/dist-packages/ufl/integral.py is in python-ufl 1.3.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 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 | """The Integral class."""
# Copyright (C) 2008-2013 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/>.
#
# Modified by Anders Logg, 2008-2009
#
# First added: 2008-03-14
# Last changed: 2011-08-25
from ufl.log import error, warning
from ufl.assertions import ufl_assert
from ufl.constantvalue import is_true_ufl_scalar, is_python_scalar
from ufl.expr import Expr
from ufl.domains import DomainDescription, Domain, Region, extract_top_domains, extract_domains, as_domain
# TODO: Move these somewhere more suitable?
def is_globally_constant(expr):
"""Check if an expression is globally constant, which
includes spatially independent constant coefficients that
are not known before assembly time."""
from ufl.algorithms.traversal import traverse_terminals
from ufl.argument import Argument
from ufl.coefficient import Coefficient
for e in traverse_terminals(expr):
if isinstance(e, Argument):
return False
if isinstance(e, Coefficient) and e.element().family() != "Real":
return False
if not e.is_cellwise_constant():
return False
# All terminals passed constant check
return True
# TODO: Move these somewhere more suitable?
def is_scalar_constant_expression(expr):
"""Check if an expression is a globally constant scalar expression."""
if is_python_scalar(expr):
return True
if expr.shape() != ():
return False
return is_globally_constant(expr)
# TODO: Define some defaults as to how metadata should represent integration data here?
# quadrature_degree, quadrature_rule, ...
def register_domain_type(domain_type, measure_name):
domain_type = domain_type.replace(" ", "_")
if domain_type in Measure._domain_types:
ufl_assert(Measure._domain_types[domain_type] == measure_name,
"Domain type already added with different measure name!")
# Nothing to do
else:
ufl_assert(measure_name not in Measure._domain_types.values(),
"Measure name already used for another domain type!")
Measure._domain_types[domain_type] = measure_name
class MeasureSum(object):
"""Notational intermediate object to translate the notation
'f*(ds(1)+ds(3))'
into
'f*ds(1) + f*ds(3)'.
Note that MeasureSum objects will never actually be part of forms.
"""
__slots__ = ("_measures",)
def __init__(self, *measures):
self._measures = measures
def __rmul__(self, other):
integrals = [other*m for m in self._measures]
return sum(integrals)
def __add__(self, other):
if isinstance(other, Measure):
return MeasureSum(*(self._measures + (other,)))
elif isinstance(other, MeasureSum):
return MeasureSum(*(self._measures + other._measures))
return NotImplemented
def __str__(self):
return "{\n " + "\n + ".join(map(str,self._measures)) + "\n}"
def as_domain_type(domain_type):
_domain_type = domain_type.replace(" ", "_")
# Map short domain type name to long automatically
if not _domain_type in Measure._domain_types:
for k, v in Measure._domain_types.iteritems():
if v == domain_type:
_domain_type = k
break
# In the end, did we find a valid domain type?
if not _domain_type in Measure._domain_types:
error("Invalid domain type %s." % domain_type)
return _domain_type
class Measure(object):
"""A measure for integration."""
__slots__ = ("_domain_type",
"_domain_description",
"_metadata",
"_domain_data",
"_repr",)
# Enumeration of special domain ids
DOMAIN_ID_UNDEFINED = "undefined"
DOMAIN_ID_UNIQUE = "unique"
DOMAIN_ID_EVERYWHERE = "everywhere"
DOMAIN_ID_OTHERWISE = "otherwise"
DOMAIN_ID_DEFAULT = DOMAIN_ID_EVERYWHERE # The one used by dx,ds,dS,etc.
#DOMAIN_ID_DEFAULT = DOMAIN_ID_UNIQUE # The one used by dx,ds,dS,etc.
DOMAIN_ID_CONSTANTS = (DOMAIN_ID_UNDEFINED,
DOMAIN_ID_UNIQUE,
DOMAIN_ID_EVERYWHERE,
DOMAIN_ID_OTHERWISE,
)
# Enumeration of valid domain types
CELL = "cell"
EXTERIOR_FACET = "exterior_facet"
INTERIOR_FACET = "interior_facet"
POINT = "point"
MACRO_CELL = "macro_cell"
SURFACE = "surface"
_domain_types = { \
CELL: "dx",
EXTERIOR_FACET: "ds",
INTERIOR_FACET: "dS",
POINT: "dP",
MACRO_CELL: "dE",
SURFACE: "dc"
}
_domain_types_tuple = (CELL, EXTERIOR_FACET, INTERIOR_FACET,
POINT, MACRO_CELL, SURFACE)
def __init__(self, domain_type, domain_id=None, metadata=None, domain_data=None):
# Allow long domain type names with ' ' or '_'
self._domain_type = as_domain_type(domain_type)
# Can't use this constant as default value
if domain_id is None:
self._domain_description = Measure.DOMAIN_ID_DEFAULT
else:
self._domain_description = domain_id
# Data for form compiler
self._metadata = metadata
# Data for problem solving environment
self._domain_data = domain_data
# TODO: Is repr of measure used anywhere anymore? Maybe we can fix this now:
# NB! Deliberately ignoring domain data in repr, since it causes a bug
# in the cache mechanisms in PyDOLFIN. I don't believe this is the
# last word in this case, but it should fix all known problems for now.
self._repr = "Measure(%r, %r, %r)" % (self._domain_type, self._domain_description,
self._metadata)
#self._repr = "Measure(%r, %r, %r, %r)" % (self._domain_type, self._domain_description,
# self._metadata, self._domain_data)
def reconstruct(self, domain_id=None, metadata=None, domain_data=None):
"""Construct a new Measure object with some properties replaced with new values.
Example:
<dm = Measure instance>
b = dm.reconstruct(domain_id=2)
c = dm.reconstruct(metadata={ "quadrature_degree": 3 })
Used by the call operator, so this is equivalent:
b = dm(2)
c = dm(0, { "quadrature_degree": 3 })
"""
if domain_id is None:
domain_id = self._domain_description
if metadata is None:
metadata = self._metadata
if domain_data is None:
domain_data = self._domain_data
return Measure(self._domain_type, domain_id, metadata, domain_data)
def domain_type(self):
'Return the domain type, one of "cell", "exterior_facet", "interior_facet", etc.'
return self._domain_type
def domain_description(self):
"""Return the domain description of this measure.
NB! Can be one of many types, this is work in progress!"""
return self._domain_description
def domain_id(self):
"Return the domain id of this measure (integer)."
#ufl_assert(isinstance(self._domain_description, int), "Measure does not have an integer domain id.") # TODO: Enable this
return self._domain_description
def metadata(self):
"""Return the integral metadata. This data is not interpreted by UFL.
It is passed to the form compiler which can ignore it or use it to
compile each integral of a form in a different way."""
return self._metadata
def domain_data(self):
"""Return the integral domain_data. This data is not interpreted by UFL.
Its intension is to give a context in which the domain id is interpreted."""
return self._domain_data
# TODO: Figure out if we should keep this / how long to keep it / how to deprecate
def __getitem__(self, domain_data):
"""Return a new Measure for same integration type with an attached
context for interpreting domain ids. The default ID of this new Measure
is undefined, and thus it must be qualified with a domain id to use
in an integral. Example: dx = dx[boundaries]; L = f*v*dx(0) + g*v+dx(1)."""
return self.reconstruct(domain_id=Measure.DOMAIN_ID_UNDEFINED,
domain_data=domain_data)
def __call__(self, domain_id=None, metadata=None):
"""Return integral of same type on given sub domain,
optionally with some metadata attached."""
if domain_id is None and metadata is None:
# Let syntax dx() mean integral over everywhere
return self.reconstruct(domain_id=Measure.DOMAIN_ID_EVERYWHERE)
else:
# If we get keyword metadata, attatch it, if we get a domain id, use it
return self.reconstruct(domain_id=domain_id, metadata=metadata)
def __add__(self, other):
if isinstance(other, Measure):
# Let dx(1) + dx(2) equal dx((1,2))
return MeasureSum(self, other)
else:
# Can only add Measures
return NotImplemented
def __mul__(self, other):
if isinstance(other, Measure):
# Tensor product measure support
return ProductMeasure(self, other)
else:
# Can't multiply Measure from the right with non-Measure type
return NotImplemented
def __rmul__(self, integrand):
# Let other types implement multiplication with Measure
# if they want to (to support the dolfin-adjoint TimeMeasure)
if not isinstance(integrand, Expr):
return NotImplemented
# Allow only scalar integrands
if not is_true_ufl_scalar(integrand):
error("Trying to integrate expression of rank %d with free indices %r." \
% (integrand.rank(), integrand.free_indices()))
# Is the measure in a state where multiplication is not allowed?
if self._domain_description == Measure.DOMAIN_ID_UNDEFINED:
error("Missing domain id. You need to select a subdomain, " +\
"e.g. M = f*dx(0) for subdomain 0.")
#else: # TODO: Do it this way instead, and move all logic below into preprocess:
# # Return a one-integral form:
# from ufl.form import Form
# return Form( [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata(), self.domain_data())] )
# # or if we move domain data into Form instead:
# integrals = [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata())]
# domain_data = { self.domain_type(): self.domain_data() }
# return Form(integrals, domain_data)
# TODO: How to represent all kinds of domain descriptions is still a bit unclear
# Create form if we have a sufficient domain description
elif (# We have a complete measure with domain description
isinstance(self._domain_description, DomainDescription)
# Is the measure in a basic state 'foo*dx'?
or self._domain_description == Measure.DOMAIN_ID_UNIQUE
# Is the measure over everywhere?
or self._domain_description == Measure.DOMAIN_ID_EVERYWHERE
# Is the measure in a state not allowed prior to preprocessing?
or self._domain_description == Measure.DOMAIN_ID_OTHERWISE
):
# Create and return a one-integral form
from ufl.form import Form
return Form( [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata(), self.domain_data())] )
# Did we get several ids?
elif isinstance(self._domain_description, tuple):
# FIXME: Leave this analysis to preprocessing
return sum(integrand*self.reconstruct(domain_id=d) for d in self._domain_description)
# Did we get a name?
elif isinstance(self._domain_description, str):
# FIXME: Leave this analysis to preprocessing
# Get all domains and regions from integrand to analyse
domains = extract_domains(integrand)
# Get domain or region with this name from integrand, error if multiple found
name = self._domain_description
candidates = set()
for TD in domains:
if TD.name() == name:
candidates.add(TD)
ufl_assert(len(candidates) > 0,
"Found no domain with name '%s' in integrand." % name)
ufl_assert(len(candidates) == 1,
"Multiple distinct domains with same name encountered in integrand.")
D, = candidates
# Reconstruct measure with the found named domain or region
measure = self.reconstruct(domain_id=D)
return integrand*measure
# Did we get a number?
elif isinstance(self._domain_description, int):
# FIXME: Leave this analysis to preprocessing
# Get all top level domains from integrand to analyse
domains = extract_top_domains(integrand)
# Get domain from integrand, error if multiple found
if len(domains) == 0:
# This is the partially defined integral from dolfin expression mess
cell = integrand.cell()
D = None if cell is None else as_domain(cell)
elif len(domains) == 1:
D, = domains
else:
error("Ambiguous reference to integer subdomain with multiple top domains in integrand.")
if D is None:
# We have a number but not a domain? Leave it to preprocess...
# This is the case with badly formed forms which can occur from dolfin
# Create and return a one-integral form
from ufl.form import Form
return Form( [Integral(integrand, self.domain_type(), self.domain_id(), self.metadata(), self.domain_data())] )
else:
# Reconstruct measure with the found numbered subdomain
measure = self.reconstruct(domain_id=D[self._domain_description])
return integrand*measure
# Provide error to user
else:
error("Invalid domain id %s." % str(self._domain_description))
def __str__(self):
d = Measure._domain_types[self._domain_type]
metastring = "" if self._metadata is None else ("<%s>" % repr(self._metadata))
return "%s%s%s" % (d, self._domain_description, metastring)
def __repr__(self):
return self._repr
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
return repr(self) == repr(other)
class ProductMeasure(Measure):
"Representation of a product measure."
__slots__ = ("_measures",)
def __init__(self, *measures):
"Create ProductMeasure from given list of measures."
self._measures = list(measures)
ufl_assert(len(self._measures) > 0, "Expecting at least one measure")
# FIXME: MER: The below is clearly wrong, but preprocess pose some
# pretty hard restrictions. To be dealt with later.
# self._domain_type = tuple(m.domain_type() for m in self._measures)
# self._domain_description = tuple(m.domain_id() for m in self._measures)
self._domain_type = measures[0].domain_type()
self._domain_description = measures[0].domain_id()
self._metadata = None
self._domain_data = None
self._repr = "ProductMeasure(*%r)" % self._measures
def __mul__(self, other):
"Flatten multiplication of product measures."
if isinstance(other, Measure):
measures = self.sub_measures() + [other]
return ProductMeasure(*measures)
#error("Can't multiply ProductMeasure from the right (with %r)." % (other,))
return NotImplemented
def __rmul__(self, integrand):
error("TODO: Implement ProductMeasure.__rmul__ to construct integral and form somehow.")
def sub_measures(self):
"Return submeasures."
return self._measures
# Transitional helper until we decide to change Integral permanently
def Integral2(integrand, domain_type, domain_desc, compiler_data, domain_data):
return Integral(integrand, domain_type, domain_desc, compiler_data, domain_data)
class Integral(object):
"An integral over a single domain."
__slots__ = ("_integrand",
"_domain_type",
"_domain_description",
"_compiler_data",
"_domain_data", # TODO: Make this part of Form instead?
"_measure", # TODO: Remove when not used anywhere
)
def __init__(self, integrand, domain_type, domain_description, compiler_data, domain_data):
ufl_assert(isinstance(integrand, Expr),
"Expecting integrand to be an Expr instance.")
self._integrand = integrand
self._domain_type = domain_type
self._domain_description = domain_description
self._compiler_data = compiler_data
self._domain_data = domain_data
# TODO: Remove this, kept for a transitional period:
self._measure = Measure(domain_type, domain_description, compiler_data, domain_data)
def reconstruct(self, integrand=None, domain_type=None, domain_description=None, compiler_data=None, domain_data=None):
"""Construct a new Integral object with some properties replaced with new values.
Example:
<a = Integral instance>
b = a.reconstruct(expand_compounds(a.integrand()))
c = a.reconstruct(compiler_data={'quadrature_degree':2})
"""
if integrand is None:
integrand = self.integrand()
if domain_type is None:
domain_type = self.domain_type()
if domain_description is None:
domain_description = self.domain_description()
if compiler_data is None:
compiler_data = self.compiler_data()
if domain_data is None:
domain_data = self.domain_data()
return Integral(integrand, domain_type, domain_description, compiler_data, domain_data)
def integrand(self):
"Return the integrand expression, which is an Expr instance."
return self._integrand
def domain_type(self):
"Return the domain type of this integral."
return self._domain_type
def domain_description(self):
"""Return the domain description of this integral.
NB! Can be one of many types, this is work in progress!"""
return self._domain_description
def domain_id(self):
"Return the domain id of this integral."
#ufl_assert(isinstance(self._domain_description, int), "Integral does not have an integer domain id.") # TODO: Enable this
return self._domain_description
def compiler_data(self):
"Return the compiler metadata this integral has been annotated with."
return self._compiler_data
def domain_data(self): # TODO: Move to Form?
"Return the assembler metadata this integral has been annotated with."
return self._domain_data
def measure(self): # TODO: Remove this
"Return the measure associated with this integral."
return self._measure
def __neg__(self):
return self.reconstruct(-self._integrand)
def __mul__(self, scalar):
ufl_assert(is_python_scalar(scalar),
"Cannot multiply an integral with non-constant values.")
return self.reconstruct(scalar*self._integrand)
def __rmul__(self, scalar):
ufl_assert(is_scalar_constant_expression(scalar),
"An integral can only be multiplied by a "
"globally constant scalar expression.")
return self.reconstruct(scalar*self._integrand)
def __str__(self):
return "{ %s } * %s" % (self._integrand, self._measure) # FIXME
def __repr__(self):
return "Integral(%r, %r, %r, %r, %r)" % (self._integrand, self._domain_type, self._domain_description,
self._compiler_data, self._domain_data)
def __eq__(self, other):
return (self._domain_type == other._domain_type
and self._domain_description == other._domain_description
and self._integrand == other._integrand
and self._compiler_data == other._compiler_data
and self._domain_data == other._domain_data
)
def __hash__(self):
data = (hash(self._integrand), self._domain_type, self._domain_description)
# Assuming we can get away with few collisions by ignoring metadata:
# hash(self._compiler_data), hash(self._domain_data)
return hash(data)
|