File: _IPOperator.py

package info (click to toggle)
xmds2 3.0.0%2Bdfsg-5
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 52,068 kB
  • sloc: python: 63,652; javascript: 9,230; cpp: 3,929; ansic: 1,463; makefile: 121; sh: 54
file content (156 lines) | stat: -rw-r--r-- 6,985 bytes parent folder | download | duplicates (3)
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
#!/usr/bin/env python3
# encoding: utf-8
"""
_IPOperator.py

Created by Graham Dennis on 2008-02-20.

Copyright (c) 2008-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.Operators.Operator import Operator
from xpdeint.ParserException import ParserException, parserWarning

from xpdeint import CodeParser
from xpdeint.Utilities import lazy_property

class _IPOperator(Operator):
  evaluateOperatorFunctionArguments = [('int', '_exponent')]
  operatorKind = Operator.IPOperatorKind
  expFunction = 'exp'
  valueSuffix = ''
  
  @lazy_property
  def integrator(self):
    # Our parent is an OperatorContainer, and its parent is the Integrator
    return self.parent.parent
  
  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.items():
          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)