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
|
#!/usr/bin/env python3
# encoding: utf-8
"""
_Operator.py
This contains all the pure-python code for Operator.tmpl
Created by Graham Dennis on 2007-10-18.
Copyright (c) 2007-2012, Graham Dennis
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
from xpdeint.ScriptElement import ScriptElement
from xpdeint.ParserException import ParserException
from xpdeint.Function import Function
from xpdeint.Utilities import lazy_property
from xpdeint.CallOnceGuards import callOncePerInstanceGuard
class _Operator (ScriptElement):
# This is an ordered list of (type, argName) pairs
evaluateOperatorFunctionArguments = []
# Operator kinds
IPOperatorKind = 1
OtherOperatorKind = 2
DeltaAOperatorKind = 3
operatorKind = OtherOperatorKind
vectorsMustBeInSubsetsOfIntegrationField = True
def __init__(self, *args, **KWs):
localKWs = self.extractLocalKWs(['name'], KWs)
ScriptElement.__init__(self, *args, **KWs)
# Set default variables
self.dependenciesEntity = None
self.operatorComponents = {}
self.operatorVector = None
self.resultVector = None
self.operatorNumber = -1
# the name is only used for filter operators, which default to a normal segment object if not named in the XML
self._name = None
if localKWs:
self._name = localKWs['name']
parent = self.parent
parent.addOperator(self)
if self._name:
evaluateOperatorFunctionName = self._name
else:
evaluateOperatorFunctionName = ''.join(['_', parent.id, '_evaluate_', self.name])
evaluateOperatorFunction = Function(name = evaluateOperatorFunctionName,
args = self.evaluateOperatorFunctionArguments,
implementation = self.evaluateOperatorFunctionContents,
description = self.description())
self.functions['evaluate'] = evaluateOperatorFunction
if hasattr(self, 'calculateOperatorFieldFunctionArguments'):
calculateOperatorFieldFunctionName = ''.join(['_', parent.id, '_calculate_', self.name, '_field'])
calculateOperatorFieldFunction = Function(name = calculateOperatorFieldFunctionName,
args = self.calculateOperatorFieldFunctionArguments,
implementation = self.calculateOperatorFieldFunctionContents,
description = self.description())
self.functions['calculateOperatorField'] = calculateOperatorFieldFunction
# The children
@property
def children(self):
# Return either or both of operatorVector and resultVector
# depending on whether or not they are 'None'
children = super(_Operator, self).children
children.extend([x for x in [self.operatorVector, self.resultVector] if x])
return children
@lazy_property
def name(self):
return 'operator' + str(self.operatorNumber)
@property
def targetVectors(self):
targetVectors = set()
# Loop over the vectors that the operators are going to operate on
for targetVectorDict in self.operatorComponents.values():
targetVectors.update(set(targetVectorDict.keys()))
return targetVectors
@lazy_property
def calculateOperatorFieldFunctionArgumentString(self):
return ', '.join([pair[0] + ' ' + pair[1] for pair in self.calculateOperatorFieldFunctionArguments])
@lazy_property
def evaluateOperatorFunctionArgumentString(self):
return ', '.join([pair[0] + ' ' + pair[1] for pair in self.evaluateOperatorFunctionArguments])
@lazy_property
def dynamicVectorsNeedingPrecalculation(self):
vectorSet = self.dependencies.copy()
vectorSet.update(self.targetVectors)
return [x for x in vectorSet if x.isComputed or x.isNoise]
@lazy_property
def primaryCodeBlock(self):
return self.codeBlocks['operatorDefinition']
@property
def dependencies(self):
return self.primaryCodeBlock.dependencies
@property
def operatorBasis(self):
return self.primaryCodeBlock.basis
@lazy_property
def field(self):
return self.parent.field
def preflight(self):
super(_Operator, self).preflight()
if self.primaryCodeBlock.dependenciesEntity:
# If this operator has dependencies, then the integrator cannot be initialised early.
# This only affects the multi-path drivers, which try to initialise integrators once
# per node, rather than once per path. The following code disables this optimisation
# in this situation
if self.operatorVector:
self.parent.parent.canBeInitialisedEarly = False
for dependency in self.primaryCodeBlock.dependencies:
if self.vectorsMustBeInSubsetsOfIntegrationField and not dependency.field.isSubsetOfField(self.field):
raise ParserException(self.primaryCodeBlock.dependenciesEntity.xmlElement,
"Can't depend on a vector that is in a field that has dimensions that "
"aren't in this field (%s).\n"
"The vector causing this problem is '%s'."
% (self.field.name, dependency.name))
@callOncePerInstanceGuard
def allocate(self): return super(_Operator, self).allocate()
@callOncePerInstanceGuard
def free(self): return super(_Operator, self).free()
@callOncePerInstanceGuard
def initialise(self): return super(_Operator, self).initialise()
@callOncePerInstanceGuard
def finalise(self): return super(_Operator, self).finalise()
|