Exemplo n.º 1
0
 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)
Exemplo n.º 2
0
    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)