Example #1
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)
Example #2
0
 def fixupNonlocallyAccessedComponents(self):
   """
   In user code, the user may refer to parts of a vector nonlocally in integer-valued dimensions.
   This code translates variables accessed with the ``phi(j: j-3, k:k+5, l:l/2, p:p*p, q:q, r:r)`` notation to a form
   that can be used in the C++ source file. The form currently used is ``_phi_jklpqr(j-3, k+5, l/2, p*p, q, r)``.
   
   This function makes an optimisation where if all dimensions are accessed locally,
   the ``phi(j: j, k:k, l:l, p:p, q: q, r: r)`` notation is replaced with the string ``phi`` which is a faster
   way of accessing the local value than through using the ``_phi_jklpqr(...)`` macro.
   """
   vectorsToFix = self.dependencies.copy()
   if self.targetVector: vectorsToFix.add(self.targetVector)
   
   nonlocalVariablesCreated = set()
   
   vectorOverrides = self.loopArguments.get('vectorOverrides', [])
   
   simulationDriver = self.getVar('features')['Driver']
   for componentName, vector, nonlocalAccessDict, codeSlice in reversed(CodeParser.nonlocalDimensionAccessForVectors(vectorsToFix, self)):
     availableDimReps = vector.field.inBasis(self.basis)
     validDimensionNames = [dimRep.name for dimRep in availableDimReps]
     validDimensionNames.extend([dimRep.name + "_index" for dimRep in availableDimReps])
     
     # If the dict is empty, then it probably means something else
     if not nonlocalAccessDict:
       continue
     
     if vector in vectorOverrides:
       vectorID = vector.id
       raise CodeParserException(self, codeSlice.start, "Cannot access vector '%(vectorID)s' non-locally." % locals())
     
     # Check that there are no dimensions listed in the nonlocalAccessDict that don't refer to valid
     # dimensions for this vector
     
     for dimName in nonlocalAccessDict.iterkeys():
       if not dimName in validDimensionNames:
         raise CodeParserException(self, nonlocalAccessDict[dimName][1], "Component '%s' doesn't have dimension '%s'." % (componentName, dimName))
     
     dimRepsNeeded = [dimRep for dimRep in availableDimReps if dimRep.name in nonlocalAccessDict and nonlocalAccessDict[dimRep.name][0] != dimRep.name]
     dimRepsNeeded.extend([dimRep for dimRep in availableDimReps if dimRep.name + "_index" in nonlocalAccessDict])
     
     if not dimRepsNeeded:
       replacementString = componentName
     else:
       # Check that the mpi distributed dimension isn't being accessed nonlocally.
       if vector.field.isDistributed:
         for dimRep in dimRepsNeeded:
           if dimRep.hasLocalOffset:
             dimRepName = dimRep.name
             raise CodeParserException(self, nonlocalAccessDict[dimRepName][1],
                                  "It is illegal to access the dimension '%(dimRepName)s' nonlocally because it is being distributed with MPI.\n"
                                  "Try not using MPI or changing the order of your dimensions." % locals())
       
       nonlocalAccessVariableName = '_%s_' % componentName
       nonlocalAccessVariableName += ''.join([dimRep.name for dimRep in dimRepsNeeded])
       
       if not nonlocalAccessVariableName in nonlocalVariablesCreated:
         # Populate with whatever we have set for us if it's there.
         indexOverrides = self.loopArguments.get('indexOverrides', {}).copy()
         for dimRep in dimRepsNeeded:
           indexOverrides[dimRep.name] = {vector.field: dimRep.loopIndex}
         
         argumentsString = ', '.join([dimRep.loopIndex for dimRep in dimRepsNeeded])
         vectorID = vector.id
         componentNumber = vector.components.index(componentName)
         defineString = "#define %(nonlocalAccessVariableName)s(%(argumentsString)s) " % locals()
         
         nonlocalAccessString = "_active_%(vectorID)s[%(componentNumber)s + (0" % locals()
         
         for dimRep in vector.field.inBasis(self.basis):
           termString = self.explicitIndexPointerTermForVectorAndDimRepWithFieldAndBasis(vector, dimRep, self.field, self.basis, indexOverrides)
           nonlocalAccessString += termString.replace('\n', ' \\\n')
         nonlocalAccessString += ') * _%(vectorID)s_ncomponents]' % locals()
         
         defineString += nonlocalAccessString + '\n'
         undefineString = "#undef %(nonlocalAccessVariableName)s\n" % locals()
         
         featureDict = {
           'vector': vector,
           'componentName': componentName,
           'availableDimReps': availableDimReps,
           'dimRepsNeeded': dimRepsNeeded,
           'nonlocalAccessVariableName': nonlocalAccessVariableName,
           'nonlocalAccessString': nonlocalAccessString,
           'defineString': defineString,
           'undefineString': undefineString
         }
         
         featureOrdering = ['Diagnostics']
         
         self.insertCodeForFeatures('nonlocalAccess', featureOrdering, featureDict)
         
         self.prefixCodeString += featureDict['defineString']
         self.postfixCodeString += featureDict['undefineString']
         nonlocalVariablesCreated.add(nonlocalAccessVariableName)
       
       arguments = []
       for dimRep in dimRepsNeeded:
         accessViaIndex = not dimRep.name in nonlocalAccessDict
         dimRepVariableName = dimRep.name if not accessViaIndex else dimRep.name + "_index"
         accessString = nonlocalAccessDict[dimRepVariableName][0]
         dimRepName = dimRep.name
         
         if not accessViaIndex:
           argumentValue = dimRep.nonlocalAccessIndexFromStringForFieldInBasis(accessString, self.field, self.basis)
           if not argumentValue:
             raise CodeParserException(self, nonlocalAccessDict[dimRep.name][1],
                                  "Cannot access the '%(dimRepName)s' dimension nonlocally with the string '%(accessString)s'. Check the documentation." % locals())
         else:
           argumentValue = accessString
         
         arguments.append('/* %(dimRepVariableName)s => %(accessString)s */ (%(argumentValue)s)' % locals())
       argumentsString = ', '.join(arguments)
       replacementString = '%(nonlocalAccessVariableName)s(%(argumentsString)s)' % locals()
     
     # Replace the phi(j => j + 7) string with the appropriate string
     # i.e. _phi_j(j + 7)
     self.codeString = self.codeString[:codeSlice.start] + replacementString + self.codeString[codeSlice.stop:]