def preflight(self): super(_EXOperator, self).preflight() for operatorName in self.operatorNames: self.operatorComponents[operatorName] = {} operatorNamesUsed = set() operatorNames = set(self.operatorNames) sharedCodeBlock = self.parent.sharedCodeBlock operatorTargetPairs = CodeParser.targetComponentsForOperatorsInString(self.operatorNames, sharedCodeBlock) targetComponents = set() for vector in sharedCodeBlock.dependencies: targetComponents.update(vector.components) indexAccessedVariables = None # We loop over this in reverse order as we will be modifying the code string. So in order to not have to # re-run targetComponentsForOperatorsInString after each modification, we loop over the operatorTargetPairs in # reverse order so that slices (character index ranges) for earlier operator-target pairs don't change for operatorName, target, codeSlice in reversed(operatorTargetPairs): operatorNamesUsed.add(operatorName) # Target is what is inside the square brackets in the integration code block # As this is the EX operator, we have fewer constraints. # If the target matches something of the form 'phi' or 'phi[j+3, k*k][m % 3, n]' # Then we don't need to use our result vector to make 'phi' available to the operator # If it doesn't match, then we have to assume that the expression is well formed, and # copy it into the result vector then fourier transform it into the space of the operator targetVector = None replacementStringSuffix = '' if target in targetComponents: targetComponentName = target # We have direct access to the component, so just work out which vector it belongs to # Now we need to get the vector corresponding to componentName tempVectorList = [v for v in sharedCodeBlock.dependencies if targetComponentName in v.components] assert len(tempVectorList) == 1 targetVector = tempVectorList[0] else: # The target of our EX operator isn't a simple component. # In principle, this could be OK. We just need to construct the variable using the result vector. # If the user has made a mistake with their code, the compiler will barf, not xpdeint. This isn't ideal # but we can't understand an arbitrary string; that's what the compiler is for. targetComponentName = sharedCodeBlock.addCodeStringToSpecialTargetsVector(target, codeSlice) targetVector = sharedCodeBlock.specialTargetsVector # We have our match, now we need to create the operatorComponents dictionary if not operatorName in self.operatorComponents: self.operatorComponents[operatorName] = {} if not targetVector in self.operatorComponents[operatorName]: self.operatorComponents[operatorName][targetVector] = [targetComponentName] elif not targetComponentName in self.operatorComponents[operatorName][targetVector]: self.operatorComponents[operatorName][targetVector].append(targetComponentName) if targetVector.type == 'complex': for v in [self.operatorVector, self.resultVector]: if v: v.type = 'complex' # Set the replacement string for the L[x] operator replacementString = "_%(operatorName)s_%(targetComponentName)s" % locals() sharedCodeBlock.codeString = sharedCodeBlock.codeString[:codeSlice.start] + replacementString + sharedCodeBlock.codeString[codeSlice.stop:] # If any operator names weren't used in the code, issue a warning unusedOperatorNames = operatorNames.difference(operatorNamesUsed) if unusedOperatorNames: unusedOperatorNamesString = ', '.join(unusedOperatorNames) parserWarning(self.xmlElement, "The following EX operator names were declared but not used: %(unusedOperatorNamesString)s" % locals()) # Iterate over the operator components adding the appropriate bits to the resultVector, but do it in # the order of the components in the targetVectors to make it easier to optimise out an FFT. for operatorName, operatorDict in self.operatorComponents.iteritems(): for targetVector, targetVectorUsedComponents in operatorDict.iteritems(): for targetComponent in targetVector.components: if targetComponent in targetVectorUsedComponents: self.resultVector.components.append("_%(operatorName)s_%(targetComponent)s" % locals()) if self.resultVector.nComponents == 0: self.resultVector.remove() self.resultVector = None # Add the result vector to the shared dependencies for the operator container # These dependencies are just the delta a dependencies, so this is just adding # our result vector to the dependencies for the delta a operator if self.resultVector: sharedCodeBlock.dependencies.add(self.resultVector) # If we are nonconstant then we need to add the target vectors to the dependencies of our primary code block if not 'calculateOperatorField' in self.functions: self.primaryCodeBlock.dependencies.update(self.targetVectors) vectors = set(self.targetVectors) vectors.add(self.resultVector) self.registerVectorsRequiredInBasis(vectors, self.operatorBasis)
def preflight(self): super(_IPOperator, self).preflight() for operatorName in self.operatorNames: self.operatorComponents[operatorName] = {} sharedCodeBlock = self.parent.sharedCodeBlock operatorTargetPairs = CodeParser.targetComponentsForOperatorsInString(self.operatorNames, sharedCodeBlock) operatorNamesUsed = set() operatorNames = set(self.operatorNames) integrationVectors = self.parent.deltaAOperator.integrationVectors field = self.field legalTargetComponentNames = set() for v in integrationVectors: legalTargetComponentNames.update(v.components) targetComponentNamesUsed = set() indexAccessedVariables = None # We loop over this in reverse order as we will be modifying the code string. So in order to not have to # re-run targetComponentsForOperatorsInString after each modification, we loop over the operatorTargetPairs in # reverse order so that slices (character index ranges) for earlier operator-target pairs don't change for operatorName, target, codeSlice in reversed(operatorTargetPairs): operatorNamesUsed.add(operatorName) # Target is what is inside the square brackets in the integration code block # As this is the IP operator, we have a few additional constraints # Firstly, the targets must be of the form 'phi' or 'phi[j,k][m,n]' # where j, k, m, n are the names of the integer dimension if target in legalTargetComponentNames: # Everything is OK componentName = target else: if indexAccessedVariables == None: indexAccessedVariables = CodeParser.nonlocalDimensionAccessForVectors( integrationVectors, sharedCodeBlock ) try: # This will extract the componentName corresponding to the indexed variable in the target # or it will fail because it isn't of that form. componentName, resultDict = [ (l[0], l[2]) for l in indexAccessedVariables if sharedCodeBlock.codeString[l[3]] == target ][0] except IndexError: # Target didn't match something of the form 'phi[j, k][m+3,n-9]' raise ParserException( self.xmlElement, "IP operators can only act on components of integration vectors. " "The '%(operatorName)s' operator acting on '%(target)s' doesn't seem to be of the right form " "or '%(target)s' isn't in one of the integration vectors." % locals(), ) # Check that nonlocally-accessed dimensions are being accessed with the dimension names # i.e. of the form 'phi(j: j, k:k, m:m, n:n)' not 'phi(j: j-7, k: k*2, m: 3, n: n+1)' for dimName, (indexString, codeSlice) in resultDict.iteritems(): if not dimName == indexString: raise ParserException( self.xmlElement, "IP operators can only act on every value of a dimension. " "The problem was caused by the '%(operatorName)s' operator acting on '%(target)s'. " "EX operators do not have this restriction." % locals(), ) if componentName in targetComponentNamesUsed: raise ParserException( self.xmlElement, "Check the documentation, only one IP operator can act on a given component, " "and this operator can only appear once. " "The problem was with the '%(componentName)s' term appearing more than once in an IP operator. " "You may be able to accomplish what you are trying with an EX operator." % locals(), ) targetComponentNamesUsed.add(componentName) # Now we need to get the vector corresponding to componentName tempVectorList = [v for v in integrationVectors if componentName in v.components] assert len(tempVectorList) == 1 targetVector = tempVectorList[0] # We have our match, now we need to create the operatorComponents dictionary if not targetVector in self.operatorComponents[operatorName]: self.operatorComponents[operatorName][targetVector] = [componentName] else: self.operatorComponents[operatorName][targetVector].append(componentName) if targetVector.type == "real": self.operatorVector.type = "real" # Check the sanity of the integration code. # i.e. check that we don't have something of the form: # dy_dt = L[x]. # Obviously the user could hide this from us, but if we can check the most # common case that frequently goes wrong, then we should. CodeParser.performIPOperatorSanityCheck( componentName, self.propagationDimension, codeSlice, sharedCodeBlock ) # Replace the L[x] string with 0.0 sharedCodeBlock.codeString = ( sharedCodeBlock.codeString[: codeSlice.start] + "0.0" + sharedCodeBlock.codeString[codeSlice.stop :] ) # If any operator names weren't used in the code, issue a warning unusedOperatorNames = operatorNames.difference(operatorNamesUsed) if unusedOperatorNames: unusedOperatorNamesString = ", ".join(unusedOperatorNames) parserWarning( self.xmlElement, "The following operator names weren't used: %(unusedOperatorNamesString)s" % locals() ) del self.functions["evaluate"] vectors = set(self.targetVectors) self.registerVectorsRequiredInBasis(vectors, self.parent.ipOperatorBasis)