Beispiel #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)
 def preflight(self):
   super(_DeltaAOperator, self).preflight()
   
   # Construct the operator components dictionary
   for integrationVector in self.integrationVectors:
     for componentName in integrationVector.components:
       derivativeString = "d%s_d%s" % (componentName, self.propagationDimension)
       
       # Map of operator names to vector -> component list dictionary
       self.operatorComponents[derivativeString] = {integrationVector: [componentName]}
       
       # Check that the user code block contains derivatives for every vector.
       # If not, throw an exception.
       
       if not derivativeString in self.primaryCodeBlock.codeString:
         raise ParserException(
           self.primaryCodeBlock.xmlElement,
           "Missing derivative for integration variable '%s' in vector '%s'." % (componentName, integrationVector.name)
         )
   
   
   
   # Our job here is to consider the case where the user's integration code
   # depends on a component of an integration vector which might get overwritten
   # in the process of looping over the integration code. For example, if the
   # user has code like:
   # dx_dt[j] = x[j-1]
   # then on the previous loop, x[j-1] will have been overwritten with
   # dx_dt[j-1]*_step (due to the way the delta a operator works). Consequently,
   # x[j-1] won't mean what the user think it means. This would be OK if the code
   # was
   # dx_dt[j] = x[j + 1]
   # however we cannot safely know in all cases if j + 1 is greater than j or not.
   #
   # The solution will be to create an array to save all of the results for dx_dt
   # and then copy the results back in to the x array.
   #
   # As an optimisation, we don't want to do this if all of the accesses for an
   # integer valued dimension is with just the value of the dimension index.
   # 
   # Additionally, if we have an integer-valued dimension at the start that we
   # need to fix this problem for, the array would need to be large enough to
   # hold all of the dimensions after that dimension as well. To reduce the
   # memory requirement for this, we will re-order the looping of the dimensions
   # to put any integer-valued dimensions that need this special treatment as the
   # innermost loops.
   
   dimRepNamesNeedingReordering = set()
   
   # Not all integration vectors may be forcing this reordering. For any that aren't,
   # we can just do the normal behaviour. This saves memory.
   self.vectorsForcingReordering = set()
   
   components = set()
   derivativeMap = {}
   propagationDimension = self.propagationDimension
   basis = self.primaryCodeBlock.basis
   dimRepNameMap = dict([(dimRep.name, dimRep) for dimRep in self.field.inBasis(basis)])
   
   for vector in self.integrationVectors:
     components.update(vector.components)
     for componentName in vector.components:
       derivativeString = ''.join(['d', componentName, '_d', propagationDimension])
       components.add(derivativeString)
       derivativeMap[derivativeString] = vector
   
   indexAccessedVariables = CodeParser.nonlocalDimensionAccessForComponents(components, self.primaryCodeBlock)
   
   simulationDriver = self.getVar('features')['Driver']
   
   for componentName, resultDict, codeSlice in indexAccessedVariables:
     
     vectors = [v for v in self.integrationVectors if componentName in v.components]
     
     if len(vectors) == 1:
       # Either our component belongs to one of the integration vectors
       vector = vectors[0]
     else:
       # Or it is a derivative, and so the vector we should use is the one for the original component
       vector = derivativeMap[componentName]
     
     # Add the dimension names that aren't being accessed with the dimension variable
     # to the set of dimensions needing reordering.
     dimRepNamesForThisVectorNeedingReordering = [dimRepName for dimRepName, (indexString, accessLoc) in resultDict.iteritems() if indexString != dimRepName]
     
     if vector.field.isDistributed:
       distributedDimRepsNeedingReordering = set(
         [dimRep.name for dimRep in self.field.inBasis(basis)
                 if dimRep.hasLocalOffset]
       ).intersection(dimRepNamesForThisVectorNeedingReordering)
       if distributedDimRepsNeedingReordering:
         # This vector is being accessed nonlocally on a dimension that is distributed. This isn't legal.
         dimRepName = list(distributedDimRepsNeedingReordering)[0]
         raise ParserException(self.xmlElement, 
                                 "The dimension '%(dimRepName)s' cannot be accessed nonlocally because it is being distributed with MPI. "
                                 "Try turning off MPI or re-ordering the dimensions in the <geometry> element." % locals())
     
     if dimRepNamesForThisVectorNeedingReordering:
       # If we have any dimensions that need reordering for this vector, add them to the complete set
       dimRepNamesNeedingReordering.update(dimRepNamesForThisVectorNeedingReordering)
       # ... and add the vector itself to the set of vectors forcing this reordering.
       self.vectorsForcingReordering.add(vector)
       
   
   
   # We now have all of the dimension names that need re-ordering to the end of the array.
   # We only need to do our magic if this set is non-empty
   if dimRepNamesNeedingReordering:
     
     # Now we need to construct a new field which has the same dimensions as self.field,
     # but has the dimensions that need reordering at the end.
     newFieldDimensions = self.field.dimensions[:]
     
     dimensionsNeedingReordering = []
     
     # Remove the dimensions needing reordering and replace them at the end
     for dim in newFieldDimensions[:]:
       if dim.inBasis(basis).name in dimRepNamesNeedingReordering:
         newFieldDimensions.remove(dim)
         newFieldDimensions.append(dim)
         dimensionsNeedingReordering.append(dim)
     
     loopingFieldName = ''.join([self.integrator.name, '_', self.name, '_looping_field'])
     
     loopingField = FieldElement(name = loopingFieldName,
                                 **self.argumentsToTemplateConstructors)
     
     loopingField.dimensions = [dim.copy(parent=loopingField) for dim in newFieldDimensions]
     self.primaryCodeBlock.field = loopingField
     
     # Now construct a second field for the vector which will hold our delta a operators
     deltaAFieldName = ''.join([self.integrator.name, '_', self.name, '_delta_a_field'])
     
     self.deltaAField = FieldElement(name = deltaAFieldName,
                                     **self.argumentsToTemplateConstructors)
     
     self.deltaAField.dimensions = [dim.copy(parent = self.deltaAField) for dim in dimensionsNeedingReordering]
     
     propagationDimension = self.propagationDimension
     
     # For each integration vector forcing the reordering, we need to construct
     # a corresponding vector in the new field.
     for integrationVector in self.vectorsForcingReordering:
       deltaAVector = VectorElement(
         name = integrationVector.name, field = self.deltaAField,
         parent = self, initialBasis = self.operatorBasis,
         type = integrationVector.type,
         **self.argumentsToTemplateConstructors
       )
       
       # The vector will only need initialisation if the derivatives are accessed out
       # of order, i.e. dphi_dt[j+1] for example. We can detect this later and change this
       # if that is the case.
       deltaAVector.needsInitialisation = False
       # Construct dx_dt variables for the delta a vector.
       deltaAVector.components = [''.join(['d', componentName, '_d', propagationDimension]) for componentName in integrationVector.components]
       
       # Make sure the vector gets allocated etc.
       self._children.append(deltaAVector)
       
       # Make the vector available when looping
       self.primaryCodeBlock.dependencies.add(deltaAVector)
       
       # Remove the components of the vector from our operatorComponents so that we won't get doubly-defined variables
       for componentName in deltaAVector.components:
         del self.operatorComponents[componentName]
       
       # Add the new delta a vector to the integration vector --> delta a vector map
       self.deltaAVectorMap[integrationVector] = deltaAVector
     
   
   # We need to rewrite all the derivatives to only use dimensions in the delta a field (if we have one)
   # This needs to be done even if we don't have a delta-a field as otherwise writing dx_dt(j: j) wouldn't
   # get transformed as dx_dt won't be vector.
   indexAccessedDerivatives = CodeParser.nonlocalDimensionAccessForComponents(derivativeMap.keys(), self.primaryCodeBlock)
   
   for componentName, resultDict, codeSlice in reversed(indexAccessedDerivatives):
     componentAccessString = componentName
     componentAccesses = []
     for dimRepName, (accessString, accessCodeLoc) in resultDict.iteritems():
       if not dimRepName in dimRepNamesNeedingReordering:
         continue
       componentAccesses.append('%(dimRepName)s => %(accessString)s' % locals())
     if componentAccesses:
       componentAccessString += '(' + ','.join(componentAccesses) + ')'
     
     # If we have at least one dimension that is not being accessed with the correct index,
     # we must initialise the delta a vector just in case. (See gravity.xmds for an example)
     if any([resultDict[dimRepName][0] != dimRepName for dimRepName in resultDict if dimRepName in dimRepNamesNeedingReordering]):
       # The integrationVector must be in the deltaAVectorMap because we would have had to allocate
       # a delta-a vector for this integration vector.
       deltaAVector = self.deltaAVectorMap[integrationVector]
       if not deltaAVector.needsInitialisation:
         deltaAVector.needsInitialisation = True
         deltaAVector.initialiser = VectorInitialisation(parent = deltaAVector, **self.argumentsToTemplateConstructors)
         deltaAVector.initialiser.vector = deltaAVector
     
     self.primaryCodeBlock.codeString = self.primaryCodeBlock.codeString[:codeSlice.start] + componentAccessString \
                                       + self.primaryCodeBlock.codeString[codeSlice.stop:]
     
   if self.deltaAField:
     copyDeltaAFunctionName = ''.join(['_', self.id, '_copy_delta_a'])
     loopingField = self.primaryCodeBlock.field
     arguments = [('real', '_step')]
     deltaAFieldReps = self.deltaAField.inBasis(self.operatorBasis)
     arguments.extend([('long', '_' + dimRep.name + '_index') \
                           for dimRep in loopingField.inBasis(self.operatorBasis) if not dimRep in deltaAFieldReps])
     copyDeltaAFunction = Function(name = copyDeltaAFunctionName,
                                   args = arguments,
                                   implementation = self.copyDeltaAFunctionContents,
                                   returnType = 'inline void')
     self.functions['copyDeltaA'] = copyDeltaAFunction
     
     # Create arguments dictionary for a call to the copyDeltaA function
     arguments = dict([('_' + dimRep.name + '_index', dimRep.loopIndex) \
                         for dimRep in loopingField.inBasis(self.operatorBasis) if not dimRep in deltaAFieldReps])
     functionCall = self.functions['copyDeltaA'].call(parentFunction = self.functions['evaluate'], arguments = arguments) + '\n'
     self.primaryCodeBlock.loopArguments['postDimensionLoopClosingCode'] = {
       self.deltaAField.dimensions[0].inBasis(self.operatorBasis).name: functionCall
     }
Beispiel #3
0
 def transformCodeString(self):
   CodeParser.checkForIntegerDivision(self)
   
   if self.codeString.count('\n'):
     # Deindent code and add '#line' compiler directives
     self.addCompilerLineDirectives()
Beispiel #4
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)
Beispiel #5
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:]