Source code for simudo.fem.assign


from functools import reduce

import dolfin
import ufl.algebra
import ufl.constantvalue

from .function_subspace_registry import AssignmentError

__all__ = ['opportunistic_assign']

[docs]def opportunistic_assign(source, target, function_subspace_registry): ''' Assign the value of source `source` to target `target`, where `target` may be a function or subfunction. Use the fast :py:meth:`~.function_subspace_registry.FunctionSubspaceRegistry.assign` method if possible, otherwise py:func:`dolfin.project`. ''' if target is source: # wew lad return src = source.m_as(target.units) tgt = target.magnitude src_offset, src_scale, src = interpret_as_linear_combination(src) tgt_offset, tgt_scale, tgt = interpret_as_linear_combination(tgt) # tgt * tgt_scale + tgt_offset = src * src_scale + src_offset # tgt = src * (src_scale/tgt_scale) + (src_offset - tgt_offset) # tgt = src * scale + offset scale = src_scale/tgt_scale offset = src_offset - tgt_offset fsr = function_subspace_registry try: fsr.assign(target=tgt, source=src, coefficient=scale, offset=offset) except AssignmentError: pass else: return fsr.assign(target=tgt, source=dolfin.project( (src * scale + offset if offset else src * scale), fsr.get_function_space(tgt, collapsed=True))) return
class NotLinearCombination(Exception): pass # TODO: desperately needs unit test class InterpretAsLinearCombination(object): def interpret_constant(self, e): if isinstance(e, ufl.constantvalue.ConstantValue): return e.evaluate(None, {}, (), ()) else: return None def interpret_as_linear_combination(self, expr): ''' Return :code:`(offset, scale, terminal)` such that :code:`expr=terminal*scale+offset`. ''' try: return self._interpret_as_linear_combination(expr) except NotLinearCombination: return (0.0, 1.0, expr) def _interpret_as_linear_combination(self, expr): if isinstance(expr, ufl.algebra.Sum): terminal = None scale = 0.0 offset = 0.0 for x in expr.ufl_operands: c = self.interpret_constant(x) if c is not None: offset += c else: # not a constant offset_, scale_, terminal_ = ( self._interpret_as_linear_combination(x)) if terminal is not None and terminal != terminal_: raise NotLinearCombination() # fail: multiple terminals terminal = terminal_ offset += offset_ scale += scale_ if terminal is None: raise NotLinearCombination() return (offset, scale, terminal) elif isinstance(expr, ufl.algebra.Product): terminal = None offset = 0.0 scale = 1.0 offset_scale = 1.0 for x in expr.ufl_operands: c = self.interpret_constant(x) if c is not None: offset_scale *= c scale *= c else: offset_, scale_, terminal_ = ( self._interpret_as_linear_combination(x)) if terminal is not None and terminal_ is not None: raise NotLinearCombination() # fail: nonlinear terminal = terminal_ offset = offset_ scale *= scale_ offset *= offset_scale if terminal is None: raise NotLinearCombination() return (offset, scale, terminal) else: # TODO: extended check return (0.0, 1.0, expr) interpret_as_linear_combination = ( InterpretAsLinearCombination().interpret_as_linear_combination)