PyForTool
Python-fortran-tool
Loading...
Searching...
No Matches
applications.py
1"""
2This module implements the Applications class containing methods
3for high-to-moderate level transformation
4"""
5
6import copy
7import os
8
9from pyfortool.util import debugDecor, alltext, n2name, isStmt, PYFTError, tag, noParallel
10from pyfortool.expressions import createExpr, createExprPart, createElem, simplifyExpr
11from pyfortool.tree import updateTree
12from pyfortool.variables import updateVarList
13from pyfortool import NAMESPACE
15
16
17# pylint: disable-next=unused-argument
18def _loopVarPHYEX(lowerDecl, upperDecl, lowerUsed, upperUsed, name, index):
19 """
20 Try to guess the name of the variable to use for looping on indexes
21 :param lowerDecl, upperDecl: lower and upper bounds as defined in the declaration statement
22 :param lowerUsed, upperUsed: lower and upper bounds as given in the statement
23 :param name: name of the array
24 :param index: index of the rank
25 :return: the variable name of False to discard this statement
26 """
27 if lowerUsed is not None and lowerUsed.upper() == 'IIJB' and \
28 upperUsed is not None and upperUsed.upper() == 'IIJE':
29 varName = 'JIJ'
30 elif upperDecl is None or lowerDecl is None:
31 varName = False
32 elif upperDecl.upper() in ('KSIZE', 'KPROMA', 'KMICRO',
33 'IGRIM', 'IGACC', 'IGDRY', 'IGWET'):
34 varName = 'JL'
35 elif upperDecl.upper() in ('D%NIJT', 'IIJE') or lowerDecl.upper() in ('D%NIJT', 'IIJB') or \
36 'D%NIJT' in upperDecl.upper() + lowerDecl.upper():
37 # REAL, DIMENSION(MERGE(D%NIJT, 0, PARAMI%LDEPOSC)), INTENT(OUT) :: PINDEP
38 varName = 'JIJ'
39 elif upperDecl.upper() in ('IKB', 'IKE', 'IKT', 'D%NKT', 'KT') or \
40 'D%NKT' in upperDecl.upper():
41 # REAL, DIMENSION(MERGE(D%NIJT, 0, OCOMPUTE_SRC),
42 # MERGE(D%NKT, 0, OCOMPUTE_SRC)), INTENT(OUT) :: PSIGS
43 varName = 'JK'
44 elif upperDecl.upper() == 'KSV' or lowerDecl.upper() == 'KSV':
45 varName = 'JSV'
46 elif upperDecl.upper() == 'KRR':
47 varName = 'JRR'
48 elif upperDecl.upper() in ('D%NIT', 'IIE', 'IIU') or lowerDecl.upper() == 'IIB' or \
49 'D%NIT' in upperDecl.upper():
50 varName = 'JI'
51 elif upperDecl.upper() in ('D%NJT', 'IJE', 'IJU') or lowerDecl.upper() == 'IJB' or \
52 'D%NJT' in upperDecl.upper():
53 varName = 'JJ'
54 else:
55 varName = False
56 return varName
57
58
60 """
61 Methods for high-to-moderate level transformation
62 """
63
64 @debugDecor
66 """
67 Split all module and subroutine contain in a fortran file
68 Return a fortran file for each module and subroutine
69 """
70 for scope in self.getScopes(level=1, excludeContains=False, includeItself=True):
72 scope.path.split(":")[1].lower() + ".F90") as file:
73 file.extend(scope.findall('./{*}*'))
74 if file[-1].tail is None:
75 file[-1].tail = '\n'
76 file.write()
77
78 @debugDecor
79 def buildModi(self):
80 """
81 Build the modi_ file corresponding to the given scope
82 """
83 filename = self.getFileName()
84 fortran = 'MODULE MODI_' + os.path.splitext(os.path.basename(filename))[0].upper() + \
85 '\nEND MODULE'
87 os.path.join(os.path.dirname(filename), 'modi_' + os.path.basename(filename)), fortran)
88 module = modi.find('.//{*}program-unit')
89 module.insert(1, createElem('C', text='!Automatically generated by PyForTool', tail='\n'))
90 module.insert(2, createElem('implicit-none-stmt', text='IMPLICIT NONE', tail='\n'))
91 interface = createElem('interface-construct')
92 interface.append(createElem('interface-stmt', text='INTERFACE', tail='\n'))
93 for scope in self.getScopes(level=1):
94 prog = createElem('program-unit')
95 prog.append(copy.deepcopy(scope[0]))
96 for use in scope.findall('./use-stmt'):
97 prog.append(copy.deepcopy(use))
98 prog.append(createElem('implicit-none-stmt', text='IMPLICIT NONE', tail='\n'))
99 for var in [var for var in scope.varList if var['arg'] or var['result']]:
100 prog.append(createExpr(self.varSpec2stmt(var))[0])
101 for external in scope.findall('./{*}external-stmt'):
102 prog.append(copy.deepcopy(external))
103 end = copy.deepcopy(scope[-1])
104 end.tail = '\n'
105 prog.append(end)
106 interface.append(prog)
107 interface.append(createElem('end-interface-stmt', text='END INTERFACE', tail='\n'))
108 module.insert(3, interface)
109 modi.write()
110 modi.close()
111
112 @debugDecor
113 def deleteNonColumnCallsPHYEX(self, simplify=False):
114 """
115 Remove PHYEX routines that compute with different vertical columns not needed for AROME
116 MODE_ROTATE_WIND, UPDATE_ROTATE_WIND
117 If Simplify is True, also remove all variables only needed for these calls
118 :param simplify : if True, remove variables that are now unused
119 """
120 for subroutine in ('ROTATE_WIND', 'UPDATE_ROTATE_WIND', 'BL_DEPTH_DIAG_3D',
121 'TM06_H', 'TURB_HOR_SPLT'):
122 # Remove call statements
123 nb = self.removeCall(subroutine, simplify=simplify)
124 # Remove use statement
125 if nb > 0:
126 self.removeVar([(v['scopePath'], v['n']) for v in self.varList
127 if v['n'] == subroutine], simplify=simplify)
128
129 @debugDecor
131 """
132 Convert STR%VAR into single local variable contained in compute (a-stmt)
133 and in if-then-stmt, else-if-stmt, where-stmt
134 e.g.
135 ZA = 1 + CST%XG ==> ZA = 1 + XCST_G
136 ZA = 1 + PARAM_ICE%XRTMIN(3) ==> ZA = 1 + XPARAM_ICE_XRTMIN3
137 ZRSMIN(1:KRR) = ICED%XRTMIN(1:KRR) => ZRSMIN(1:KRR) = ICEDXRTMIN1KRR(1:KRR)
138 IF(TURBN%CSUBG_MF_PDF=='NONE')THEN => IF(CTURBNSUBG_MF_PDF=='NONE')THEN
139
140 RESTRICTION : works only if the r-component variable is contained in 1 parent structure.
141 Allowed for conversion : CST%XG
142 Not converted : TOTO%CST%XG (for now, recursion must be coded)
143 Not converted : TOTO%ARRAY(:) (shape of the array must be determined from E1)
144 """
145 def convertOneType(component, newVarList, scope):
146 # 1) Build the name of the new variable
147 objType = scope.getParent(component, 2) # The object STR%VAR
148 objTypeStr = alltext(objType).upper()
149 namedENn = objType.find('.//{*}N/{*}n')
150 structure = namedENn.text
151 variable = component.find('.//{*}ct').text.upper()
152 # If the variable is an array with index selection
153 # such as ICED%XRTMIN(1:KRR)
154 arrayIndices = ''
155 arrayRall = objType.findall('.//{*}array-R')
156 if len(arrayRall) > 0:
157 arrayR = copy.deepcopy(arrayRall[0]) # Save for the declaration
158 txt = alltext(arrayR).replace(',', '')
159 txt = txt.replace(':', '')
160 txt = txt.replace('(', '')
161 txt = txt.replace(')', '')
162 arrayIndices = arrayIndices + txt
163 elif len(objType.findall('.//{*}element-LT')) > 0:
164 # Case with single element such as ICED%XRTMIN(1)
165 for elem in objType.findall('.//{*}element'):
166 arrayIndices = arrayIndices + alltext(elem)
167 newName = variable[0] + structure + variable[1:] + arrayIndices
168 newName = newName.upper()
169
170 # 2) Replace the namedE>N>n by the newName and delete R-LT
171 # except for array with index selection (R-LT is moved)
172 namedENn.text = newName
173 objType.remove(objType.find('.//{*}R-LT'))
174 if len(arrayRall) > 0:
175 objType.insert(1, arrayR)
176
177 # 3) Add to the list of not already present for declaration
178 if newName not in newVarList:
179 if len(arrayRall) == 0:
180 newVarList[newName] = (None, objTypeStr)
181 else:
182 newVarList[newName] = (arrayR, objTypeStr)
183
184 scopes = self.getScopes()
185 if scopes[0].path.split('/')[-1].split(':')[1][:4] == 'MODD':
186 return
187 for scope in [scope for scope in scopes
188 if 'sub:' in scope.path and 'interface' not in scope.path]:
189 newVarList = {}
190 for ifStmt in (scope.findall('.//{*}if-then-stmt') +
191 scope.findall('.//{*}else-if-stmt') +
192 scope.findall('.//{*}where-stmt')):
193 compo = ifStmt.findall('.//{*}component-R')
194 if len(compo) > 0:
195 for elcompo in compo:
196 convertOneType(elcompo, newVarList, scope)
197
198 for aStmt in scope.findall('.//{*}a-stmt'):
199 # Exclude statements in which the component-R is in E1
200 # (e.g. PARAMI%XRTMIN(4) = 2)
201 if len(aStmt[0].findall('.//{*}component-R')) == 0: # E1 is the first son of aStmt
202 compoE2 = aStmt.findall('.//{*}component-R')
203 if len(compoE2) > 0:
204 # Exclude stmt from which E2 has only 1 named-E/{*}N/{*}n e.g. IKB = D%NKB
205 # warning, it does not handle yet op in simple statement
206 # such as ZEXPL = 1.- TURBN%XIMPL
207 # Include stmt from which E2 has 1 named-E/{*}N/{*}n AND E1 is an array;
208 # e.g. ZDELTVPT(JIJ,JK)=CSTURB%XLINF
209 nbNamedEinE2 = len(aStmt.findall('.//{*}E-2')[0].findall('.//{*}named-E/' +
210 '{*}N/{*}n'))
211 if nbNamedEinE2 > 1 or nbNamedEinE2 == 1 and \
212 len(aStmt[0].findall('.//{*}R-LT')) == 1:
213 for elcompoE2 in compoE2:
214 convertOneType(elcompoE2, newVarList, scope)
215
216 # Add the declaration of the new variables and their affectation
217 for el, var in newVarList.items():
218 if el[0].upper() == 'X' or el[0].upper() == 'P' or el[0].upper() == 'Z':
219 varType = 'REAL'
220 elif el[0].upper() == 'L' or el[0].upper() == 'O':
221 varType = 'LOGICAL'
222 elif el[0].upper() == 'N' or el[0].upper() == 'I' or el[0].upper() == 'K':
223 varType = 'INTEGER'
224 elif el[0].upper() == 'C':
225 varType = 'CHARACTER(LEN=LEN(' + var[1] + '))'
226 else:
227 raise PYFTError('Case not implemented for the first letter of the newVarName' +
228 el + ' in convertTypesInCompute')
229 varArray = ''
230 # Handle the case the variable is an array
231 if var[0]:
232 varArray = ', DIMENSION('
233 for i, sub in enumerate(var[0].findall('.//{*}section-subscript')):
234 if len(sub.findall('.//{*}upper-bound')) > 0:
235 dimSize = simplifyExpr(
236 alltext(sub.findall('.//{*}upper-bound')[0]) +
237 '-' + alltext(sub.findall('.//{*}lower-bound')[0]) +
238 ' + 1')
239 elif len(sub.findall('.//{*}lover-bound')) > 0:
240 dimSize = simplifyExpr(alltext(sub.findall('.//{*}lower-bound')[0]))
241 else: # Case XRTMIN(:)
242 dimSize = 'SIZE(' + var[1] + ',' + str(i+1) + ')'
243 varArray = ', DIMENSION(' + dimSize + ','
244 varArray = varArray[:-1] + ')'
245 scope.addVar([[scope.path, el, varType + varArray + ' :: ' + el, None]])
246
247 # Affectation
248 stmtAffect = createExpr(el + "=" + var[1])[0]
249 scope.insertStatement(scope.indent(stmtAffect), first=True)
250
251 @debugDecor
252 def deleteDrHook(self, simplify=False):
253 """
254 Remove DR_HOOK calls.
255 If Simplify is True, also remove all variables only needed for these calls (ZHOOK_HANDLE,
256 DR_HOOK, LHOOK, YOMHOOK, JPRB, PARKIND1)
257 :param simplify : if True, remove variables that are now unused
258 """
259 self.removeCall('DR_HOOK', simplify=simplify)
260
261 @debugDecor
262 def addDrHook(self):
263 """
264 Add DR_HOOK calls.
265 """
266 for scope in [scope for scope in self.getScopes()
267 if scope.path.split('/')[-1].split(':')[0] in ('func', 'sub') and
268 (len(scope.path.split('/')) == 1 or
269 scope.path.split('/')[-2].split(':')[0] != 'interface')]:
270 name = scope.path.split(':')[-1].upper()
271 # Add USE YOMHOOK, ONLY: LHOOK, DR_HOOK, JPHOOK
272 scope.addModuleVar([[scope.path, 'YOMHOOK', ['LHOOK', 'DR_HOOK', 'JPHOOK']]])
273 # REAL(KIND=JPHOOK) :: ZHOOK_HANDLE
274 scope.addVar([[scope.path, 'ZHOOK_HANDLE', 'REAL(KIND=JPHOOK) :: ZHOOK_HANDLE',
275 None]])
276 # Insert IF (LHOOK) CALL DR_HOOK('XXnameXX', 0, ZHOOK_HANDLE)
277 scope.insertStatement(createExpr(f"IF (LHOOK) CALL DR_HOOK('{name}', " +
278 "0, ZHOOK_HANDLE)")[0], True)
279 # Insert IF (LHOOK) CALL DR_HOOK('XXnameXX', 1, ZHOOK_HANDLE)
280 endStr = f"IF (LHOOK) CALL DR_HOOK('{name}', 1, ZHOOK_HANDLE)"
281 scope.insertStatement(createExpr(endStr)[0], False)
282 for ret in scope.findall('.//{*}return-stmt'):
283 par = scope.getParent(ret)
284 par.insert(list(par).index(ret), createExpr(endStr)[0])
285
286 @debugDecor
287 def deleteBudgetDDH(self, simplify=False):
288 """
289 Remove Budget calls.
290 If Simplify is True, also remove all variables only needed for these calls
291 :param simplify : if True, remove variables that are now unused
292 """
293 self.removeCall('BUDGET_STORE_INIT_PHY', simplify=simplify)
294 self.removeCall('BUDGET_STORE_END_PHY', simplify=simplify)
295 self.removeCall('BUDGET_STORE_ADD_PHY', simplify=simplify)
296 flagTorm = ['BUCONF%LBUDGET_SV', 'BUCONF%LBUDGET_TKE', 'BUCONF%LBUDGET_TH',
297 'BUCONF%LBUDGET_RI', 'BUCONF%LBUDGET_RV', 'BUCONF%LBUDGET_RG',
298 'BUCONF%LBUDGET_RS', 'BUCONF%LBUDGET_RH', 'BUCONF%LBUDGET_RR',
299 'BUCONF%LBUDGET_RC', 'BUCONF%LBUDGET_U', 'BUCONF%LBUDGET_V',
300 'BUCONF%LBUDGET_W']
301 self.setFalseIfStmt(flagTorm, simplify=simplify)
302
303 @debugDecor
304 def deleteRoutineCallsMesoNHGPU(self, simplify=True):
305 """
306 Remove Calls to routines not compatible with Méso-NH on GPU
307 e.g. CALL within a DO loop
308 e.g. OCND2 in condensation uses a CALL ICECLOUD and fonctions within computations
309 If Simplify is True, also remove all variables only needed for these calls
310 :param simplify : if True, remove variables that are now unused
311 """
312 self.setFalseIfStmt('OCND2', simplify=simplify)
313
314 @debugDecor
315 def addMPPDB_CHECKS(self, printsMode=False):
316
317 """
318 Add MPPDB_CHEKS on all intent REAL arrays on subroutines.
319 ****** Not applied on modd_ routines. ********
320 Handle optional arguments.
321 Example, for a BL89 routine with 4 arguments, 1 INTENT(IN),
322 2 INTENT(INOUT), 1 INTENT(OUT), it produces :
323 IF (MPPDB_INITIALIZED) THEN
324 !Check all IN arrays
325 CALL MPPDB_CHECK(PZZ, "BL89 beg:PZZ")
326 !Check all INOUT arrays
327 CALL MPPDB_CHECK(PDZZ, "BL89 beg:PDZZ")
328 CALL MPPDB_CHECK(PTHVREF, "BL89 beg:PTHVREF")
329 END IF
330 ...
331 IF (MPPDB_INITIALIZED) THEN
332 !Check all INOUT arrays
333 CALL MPPDB_CHECK(PDZZ, "BL89 end:PDZZ")
334 CALL MPPDB_CHECK(PTHVREF, "BL89 end:PTHVREF")
335 !Check all OUT arrays
336 CALL MPPDB_CHECK(PLM, "BL89 end:PLM")
337 END IF
338 param printsMode: if True, instead of CALL MPPDB_CHECK, add fortran prints for debugging
339 """
340 def addPrints_statement(var, typeofPrints='minmax'):
341 ifBeg, ifEnd = '', ''
342 if var['as']: # If array
343 varName = var['n']
344 if typeofPrints == 'minmax':
345 strMSG = f'MINMAX {varName} = \",MINVAL({varName}), MAXVAL({varName})'
346 elif typeofPrints == 'shape':
347 strMSG = f'SHAPE {varName} = \",SHAPE({varName})'
348 else:
349 raise PYFTError('typeofPrints is either minmax or shape in addPrints_statement')
350 else:
351 strMSG = var['n'] + ' = \",' + var['n']
352 if var['opt']:
353 ifBeg = ifBeg + 'IF (PRESENT(' + var['n'] + ')) THEN\n '
354 ifEnd = ifEnd + '\nEND IF'
355 return createExpr(ifBeg + "print*,\"" + strMSG + ifEnd)[0]
356
357 def addMPPDB_CHECK_statement(var, subRoutineName, strMSG='beg:'):
358 ifBeg, ifEnd, addD, addLastDim, addSecondDimType = '', '', '', '', ''
359 # Test if the variable is declared with the PHYEX D% structure,
360 # in that case, use the PHYEX MPPDB_CHECK interface
361 if var['as'][0][1]: # If not NoneType
362 if 'D%NIJT' in var['as'][0][1]:
363 addD = 'D,'
364 if len(var['as']) == 2:
365 # This handle 2D arrays with the last dim either D%NKT or anything else.
366 addLastDim = ', ' + var['as'][1][1]
367 if len(var['as']) >= 2:
368 # This adds information on the type of the second dimension :
369 # is it the vertical one or not, to remove extra points
370 if 'D%NK' in var['as'][1][1]:
371 addSecondDimType = ',' + '''"VERTICAL"'''
372 else:
373 addSecondDimType = ',' + '''"OTHER"'''
374 if 'MERGE' in var['as'][-1][1]: # e.g. MERGE(D%NKT,0,OCLOUDMODIFLM)
375 keyDimMerge = var['as'][-1][1].split(',')[2][:-1] # e.g. OCLOUDMODIFLM
376 ifBeg = 'IF (' + keyDimMerge + ') THEN\n'
377 ifEnd = '\nEND IF\n'
378 if var['opt']:
379 ifBeg = ifBeg + 'IF (PRESENT(' + var['n'] + ')) THEN\n IF (SIZE(' + \
380 var['n'] + ',1) > 0) THEN\n'
381 ifEnd = ifEnd + '\nEND IF\nEND IF'
382 argsMPPDB = var['n'] + ", " + "\"" + subRoutineName + " " + strMSG+var['n'] + "\""
383 return createExpr(ifBeg + "CALL MPPDB_CHECK(" + addD + argsMPPDB +
384 addLastDim + addSecondDimType + ")" + ifEnd)[0]
385 scopes = self.getScopes()
386 if scopes[0].path.split('/')[-1].split(':')[1][:4] == 'MODD':
387 return
388 for scope in scopes:
389 # Do not add MPPDB_CHEKS to :
390 # - MODULE or FUNCTION object,
391 # - interface subroutine from a MODI
392 # but only to SUBROUTINES
393 if 'sub:' in scope.path and 'func' not in scope.path and 'interface' not in scope.path:
394 subRoutineName = scope.path.split('/')[-1].split(':')[1]
395
396 # Look for all intent arrays only
397 arraysIn, arraysInOut, arraysOut = [], [], []
398 if not printsMode:
399 for var in scope.varList:
400 if var['arg'] and var['as'] and 'TYPE' not in var['t'] and \
401 'REAL' in var['t'] and var['scopePath'] == scope.path:
402 if var['i'] == 'IN':
403 arraysIn.append(var)
404 if var['i'] == 'INOUT':
405 arraysInOut.append(var)
406 if var['i'] == 'OUT':
407 arraysOut.append(var)
408 else:
409 for var in scope.varList:
410 if not var['t'] or var['t'] and 'TYPE' not in var['t']:
411 if var['i'] == 'IN':
412 arraysIn.append(var)
413 if var['i'] == 'INOUT':
414 arraysInOut.append(var)
415 if var['i'] == 'OUT':
416 arraysOut.append(var)
417 # Check if there is any intent variables
418 if len(arraysIn) + len(arraysInOut) + len(arraysOut) == 0:
419 break
420
421 # Add necessary module
422 if not printsMode:
423 scope.addModuleVar([(scope.path, 'MODE_MPPDB', None)])
424 else:
425 scope.addModuleVar([(scope.path, 'MODD_BLANK_n', ['LDUMMY1'])])
426
427 # Prepare some FORTRAN comments
428 commentIN = createElem('C', text='!Check all IN arrays', tail='\n')
429 commentINOUT = createElem('C', text='!Check all INOUT arrays', tail='\n')
430 commentOUT = createElem('C', text='!Check all OUT arrays', tail='\n')
431
432 # 1) variables IN and INOUT block (beggining of the routine)
433 if len(arraysIn) + len(arraysInOut) > 0:
434 if not printsMode:
435 ifMPPDBinit = createExpr("IF (MPPDB_INITIALIZED) THEN\n END IF")[0]
436 else:
437 ifMPPDBinit = createExpr("IF (LDUMMY1) THEN\n END IF")[0]
438 ifMPPDB = ifMPPDBinit.find('.//{*}if-block')
439
440 # Variables IN
441 if len(arraysIn) > 0:
442 ifMPPDB.insert(1, commentIN)
443 for i, var in enumerate(arraysIn):
444 if not printsMode:
445 ifMPPDB.insert(2 + i, addMPPDB_CHECK_statement(var, subRoutineName,
446 strMSG='beg:'))
447 else:
448 ifMPPDB.insert(2 + i, addPrints_statement(var,
449 typeofPrints='minmax'))
450 ifMPPDB.insert(3 + i, addPrints_statement(var,
451 typeofPrints='shape'))
452
453 # Variables INOUT
454 if len(arraysInOut) > 0:
455 shiftLineNumber = 2 if len(arraysIn) > 0 else 1
456 if not printsMode:
457 ifMPPDB.insert(len(arraysIn) + shiftLineNumber, commentINOUT)
458 else:
459 ifMPPDB.insert(len(arraysIn)*2 + shiftLineNumber-1, commentINOUT)
460
461 for i, var in enumerate(arraysInOut):
462 if not printsMode:
463 ifMPPDB.insert(len(arraysIn) + shiftLineNumber + 1 + i,
464 addMPPDB_CHECK_statement(var, subRoutineName,
465 strMSG='beg:'))
466 else:
467 ifMPPDB.insert(len(arraysIn) + shiftLineNumber + 1 + i,
468 addPrints_statement(var, typeofPrints='minmax'))
469
470 # Add the new IN and INOUT block
471 scope.insertStatement(scope.indent(ifMPPDBinit), first=True)
472
473 # 2) variables INOUT and OUT block (end of the routine)
474 if len(arraysInOut) + len(arraysOut) > 0:
475 if not printsMode:
476 ifMPPDBend = createExpr("IF (MPPDB_INITIALIZED) THEN\n END IF")[0]
477 else:
478 ifMPPDBend = createExpr("IF (LDUMMY1) THEN\n END IF")[0]
479 ifMPPDB = ifMPPDBend.find('.//{*}if-block')
480
481 # Variables INOUT
482 if len(arraysInOut) > 0:
483 ifMPPDB.insert(1, commentINOUT)
484 for i, var in enumerate(arraysInOut):
485 if not printsMode:
486 ifMPPDB.insert(2 + i, addMPPDB_CHECK_statement(var, subRoutineName,
487 strMSG='end:'))
488 else:
489 ifMPPDB.insert(2 + i, addPrints_statement(var,
490 typeofPrints='minmax'))
491
492 # Variables OUT
493 if len(arraysOut) > 0:
494 shiftLineNumber = 2 if len(arraysInOut) > 0 else 1
495 if not printsMode:
496 ifMPPDB.insert(len(arraysInOut) + shiftLineNumber, commentOUT)
497 else:
498 ifMPPDB.insert(len(arraysInOut)*2 + shiftLineNumber-1, commentOUT)
499 for i, var in enumerate(arraysOut):
500 if not printsMode:
501 ifMPPDB.insert(len(arraysInOut) + shiftLineNumber + 1 + i,
502 addMPPDB_CHECK_statement(var, subRoutineName,
503 strMSG='end:'))
504 else:
505 ifMPPDB.insert(len(arraysInOut) + shiftLineNumber + 1 + i,
506 addPrints_statement(var, typeofPrints='minmax'))
507
508 # Add the new INOUT and OUT block
509 scope.insertStatement(scope.indent(ifMPPDBend), first=False)
510
511 @debugDecor
512 def addStack(self, model, stopScopes, parser=None, parserOptions=None, wrapH=False):
513 """
514 Add specific allocations of local arrays on the fly for GPU
515 :param model : 'MESONH' or 'AROME' for specific objects related to the allocator or stack
516 :param stopScopes: scope paths where we stop to add stack
517 :param parser, parserOptions, wrapH: see the PYFT class
518
519 Stacks are added to all routines called by the scopes listed in stopScopes
520 """
521 if model == 'AROME':
522 # The AROME transformation needs an additional parameter
523 # We apply the transformation only if the routine is called
524 # from a scope within stopScopes
525 for scope in [scope for scope in self.getScopes()
526 if scope.path in stopScopes or
527 self.tree.isUnderStopScopes(scope.path, stopScopes)]:
528 # Intermediate transformation, needs cpp to be completed
529 # This version would be OK if we didn't need to read again the files with fxtran
530 # after transformation
531 # nb = scope.modifyAutomaticArrays(
532 # declTemplate="temp({type}, {name}, ({shape}))",
533 # startTemplate="alloc({name})")
534
535 # Full transformation, using CRAY pointers
536 # In comparison with the original transformation of Philippe,
537 # we do not call SOF with __FILE__ and __LINE__ because it breaks
538 # future reading with fxtran
539 nb = scope.modifyAutomaticArrays(
540 declTemplate="{type}, DIMENSION({shape}) :: {name}; " +
541 "POINTER(IP_{name}_, {name})",
542 startTemplate="IP_{name}_=YLSTACK%L(KIND({name})/4);" +
543 "YLSTACK%L(KIND({name})/4)=" +
544 "YLSTACK%L(KIND({name})/4)+" +
545 "KIND({name})*SIZE({name});" +
546 "IF(YLSTACK%L(KIND({name})/4)>" +
547 "YLSTACK%U(KIND({name})/4))" +
548 "CALL SOF('" + scope.getFileName() + ":{name}', " +
549 "KIND({name}))")
550
551 if nb > 0:
552 # Some automatic arrays have been modified,
553 # we need to add an argument to the routine
554 scope.addArgInTree('YDSTACK', 'TYPE (STACK) :: YDSTACK', -1,
555 stopScopes, moduleVarList=[('STACK_MOD', ['STACK', 'SOF'])],
556 otherNames=['YLSTACK'],
557 parser=parser, parserOptions=parserOptions, wrapH=wrapH)
558
559 # Copy the stack to a local variable and use it for call statements
560 # this operation must be done after the call to addArgInTree
561 scope.addVar([[scope.path, 'YLSTACK', 'TYPE (STACK) :: YLSTACK', None]])
562 scope.insertStatement(createExpr('YLSTACK=YDSTACK')[0], True)
563 for argN in scope.findall('.//{*}call-stmt/{*}arg-spec/' +
564 '{*}arg/{*}arg-N/../{*}named-E/{*}N'):
565 if n2name(argN) == 'YDSTACK':
566 argN[0].text = 'YLSTACK'
567
568 elif model == 'MESONH':
569 for scope in self.getScopes():
570 # We apply the transformation only if the routine is called
571 # from a scope within stopScopes
572 if (not self.tree.isValid) or stopScopes is None or scope.path in stopScopes or \
573 self.tree.isUnderStopScopes(scope.path, stopScopes):
574 nb = scope.modifyAutomaticArrays(
575 declTemplate="{type}, DIMENSION({doubledotshape}), " +
576 "POINTER, CONTIGUOUS :: {name}",
577 startTemplate="CALL MNH_MEM_GET({name}, {lowUpList})")
578 if nb > 0:
579 # Some automatic arrays have been modified
580 # we need to add the stack module,
581 scope.addModuleVar([(scope.path, 'MODE_MNH_ZWORK',
582 ['MNH_MEM_GET', 'MNH_MEM_POSITION_PIN',
583 'MNH_MEM_RELEASE'])])
584 # to pin the memory position,
585 scope.insertStatement(
586 createExpr(f"CALL MNH_MEM_POSITION_PIN('{scope.path}')")[0], True)
587 # and to realease the memory
588 scope.insertStatement(
589 createExpr(f"CALL MNH_MEM_RELEASE('{scope.path}')")[0], False)
590 else:
591 raise PYFTError('Stack is implemented only for AROME and MESONH models')
592
593 @debugDecor
594 def inlineContainedSubroutinesPHYEX(self, simplify=False):
595 """
596 Inline all contained subroutines in the main subroutine
597 Steps :
598 - Identify contained subroutines
599 - Look for all CALL statements, check if it is a containted routines; if yes, inline
600 - Delete the containted routines
601 :param simplify: try to simplify code (construct or variables becoming useless)
602 :param loopVar: None to create new variable for each added DO loop
603 (around ELEMENTAL subroutine calls)
604 or a function that return the name of the variable to use for
605 the loop control. This function returns a string (name of the variable),
606 or True to create a new variable, or False to not transform this statement
607 The functions takes as arguments:
608 - lower and upper bounds as defined in the declaration statement
609 - lower and upper bounds as given in the statement
610 - name of the array
611 - index of the rank
612 """
613 return self.inlineContainedSubroutines(simplify=simplify, loopVar=_loopVarPHYEX)
614
615 @debugDecor
616 @updateVarList
617 def removeIJDim(self, stopScopes, parser=None, parserOptions=None, wrapH=False, simplify=False):
618 """
619 Transform routines to be called in a loop on columns
620 :param stopScopes: scope paths where we stop to add the D argument (if needed)
621 :param parser, parserOptions, wrapH: see the PYFT class
622 :param simplify: try to simplify code (remove useless dimensions in call)
623
624 ComputeInSingleColumn :
625 - Remove all Do loops on JI and JJ
626 - Initialize former indexes JI, JJ, JIJ to first array element:
627 JI=D%NIB, JJ=D%NJB, JIJ=D%NIJB
628 - If simplify is True, replace (:,*) on I/J/IJ dimension on argument
629 with explicit (:,*) on CALL statements:
630 e.g. CALL FOO(D, A(:,JK,1), B(:,:))
631 ==> CALL FOO(D, A(JIJ,JK,1), B(:,:)) only if the target argument is not an array
632 """
633
634 indexToCheck = {'JI': ('D%NIB', 'D%NIT'),
635 'JJ': ('D%NJB', 'D%NJT'),
636 'JIJ': ('D%NIJB', 'D%NIJT')}
637
638 def slice2index(namedE, scope):
639 """
640 Transform a slice on the horizontal dimension into an index
641 Eg.: X(1:D%NIJT, 1:D%NKT) => X(JIJ, 1:D%NKT) Be careful, this array is not contiguous.
642 X(1:D%NIJT, JK) => X(JIJ, JK)
643 :param namedE: array to transform
644 :param scope: scope where the array is
645 """
646 # Loop on all array dimensions
647 for isub, sub in enumerate(namedE.findall('./{*}R-LT/{*}array-R/' +
648 '{*}section-subscript-LT/' +
649 '{*}section-subscript')):
650 if ':' in alltext(sub):
651 loopIndex, _, _ = scope.findIndexArrayBounds(namedE, isub, _loopVarPHYEX)
652 if loopIndex in indexToCheck: # To be transformed
653 if sub.text == ':':
654 sub.text = None
655 lowerBound = createElem('lower-bound')
656 sub.insert(0, lowerBound)
657 else:
658 lowerBound = sub.find('./{*}lower-bound')
659 lowerBound.tail = ''
660 for item in lowerBound:
661 lowerBound.remove(item)
662 upperBound = sub.find('./{*}upper-bound')
663 if upperBound is not None:
664 sub.remove(upperBound)
665 lowerBound.append(createExprPart(loopIndex))
666 if loopIndex not in indexRemoved:
667 indexRemoved.append(loopIndex)
668 # Transform array-R/section-subscript-LT/section-subscript
669 # into parens-R>/element-LT/element if needed
670 if ':' not in alltext(namedE.find('./{*}R-LT/{*}array-R/{*}section-subscript-LT')):
671 namedE.find('./{*}R-LT/{*}array-R').tag = f'{{{NAMESPACE}}}parens-R'
672 namedE.find('./{*}R-LT/{*}parens-R/' +
673 '{*}section-subscript-LT').tag = f'{{{NAMESPACE}}}element-LT'
674 for ss in namedE.findall('./{*}R-LT/{*}parens-R/' +
675 '{*}element-LT/{*}section-subscript'):
676 ss.tag = f'{{{NAMESPACE}}}element'
677 lowerBound = ss.find('./{*}lower-bound')
678 for item in lowerBound:
679 ss.append(item)
680 ss.remove(lowerBound)
681
682 # 0 - Preparation
683 self.addArrayParentheses()
685 if simplify:
686 self.attachArraySpecToEntity()
687 hUupperBounds = [v[1] for v in indexToCheck.values()] # Upper bounds for horizontal dim
688
689 # Loop on all scopes (reversed order); except functions (in particular
690 # FWSED from ice4_sedimentation_stat)
691 for scope in [scope for scope in self.getScopes()[::-1]
692 if 'func:' not in scope.path and
693 (scope.path in stopScopes or
694 self.tree.isUnderStopScopes(scope.path, stopScopes,
695 includeInterfaces=True))]:
696 indexRemoved = []
697
698 # 1 - Remove all DO loops on JI and JJ for preparation to compute on KLEV only
699 # Look for all do-nodes, check if the loop-index is one of the authorized
700 # list (indexToCheck), if found, removes it
701 for doNode in scope.findall('.//{*}do-construct')[::-1]:
702 for loopI in doNode.findall('./{*}do-stmt/{*}do-V/{*}named-E/{*}N'):
703 loopIname = n2name(loopI).upper()
704 if loopIname in indexToCheck:
705 # Move the content of the doNode (except do-stmt and end_do_stmt)
706 # in parent node
707 par = scope.getParent(doNode)
708 index = list(par).index(doNode)
709 for item in doNode[1:-1][::-1]:
710 par.insert(index, item)
711 par.remove(doNode) # remove the do_construct
712 if loopIname not in indexRemoved:
713 indexRemoved.append(loopIname)
714
715 # 2 - Reduce horizontal dimensions for intrinsic array functions
716 # SUM(X(:,:)) => SUM(X(JI, X))
717 # In the simplify==True case, SUM(X(:,:)) becomes SUM(X(:)) by removing first dimension
718 for intr in scope.findall('.//{*}R-LT/{*}parens-R/../..'):
719 intrName = n2name(intr.find('./{*}N')).upper()
720 if intrName in ('PACK', 'UNPACK', 'COUNT', 'MAXVAL', 'MINVAL', 'ALL', 'ANY', 'SUM'):
721 # Is it part of an expression or of an affectation statement?
722 # eg: CALL(UNPACK(Y(:), MASK=G(:,:)) * Z(:))
723 # or X(:,:) = UNPACK(Y(:), MASK=G(:,:)) * Z(:)
724 # If yes, we also need to transform X and Z
725 # if not, only arrays inside the function are transformed
726 parToUse = intr
727 par = intr
728 while par is not None and not isStmt(par):
729 par = scope.getParent(par)
730 if tag(par) in ('a-stmt', 'op-E'):
731 parToUse = par
732
733 # 2.1 Loop on all arrays in the expression using this intrinsic function
734 # to replace horizontal dimensions by indexes
735 for namedE in parToUse.findall('.//{*}R-LT/{*}array-R/../..'):
736 slice2index(namedE, scope)
737
738 # 2.2 Replace intrinsic function when argument becomes a scalar
739 if intr.find('.//{*}R-LT/{*}array-R') is None:
740 if intrName in ('MAXVAL', 'MINVAL', 'SUM', 'ALL', 'ANY'):
741 # eg: MAXVAL(X(:)) => MAXVAL(X(JI)) => X(JI)
742 parens = intr.find('./{*}R-LT/{*}parens-R')
743 parens.tag = f'{{{NAMESPACE}}}parens-E'
744 intrPar = scope.getParent(intr)
745 intrPar.insert(list(intrPar).index(intr), parens)
746 intrPar.remove(intr)
747 elif intrName == 'COUNT':
748 # eg: COUNT(X(:)) => COUNT(X(JI)) => MERGE(1, 0., X(JI))
749 nodeN = intr.find('./{*}N')
750 for item in nodeN[1:]:
751 nodeN.remove(item)
752 nodeN.find('./{*}n').text = 'MERGE'
753 elementLT = intr.find('./{*}R-LT/{*}parens-R/{*}element-LT')
754 for val in (1, 0):
755 element = createElem('element', tail=', ')
756 element.append(createExprPart(val))
757 elementLT.insert(0, element)
758
759 if simplify:
760 # 3 - Remove useless dimensions
761 # Arrays only on horizontal dimensions are transformed into scalars
762 # - at declaration "REAL :: P(D%NIT)" => "REAL :: P"
763 # - during call "CALL FOO(P(:)" => "CALL FOO(P)"
764 # "CALL FOO(Z(:,IK)" => "CALL FOO(Z(JIJ,IK)"
765 # - but "CALL FOO(Z(:,:)" is kept untouched
766 # All arrays are transformed except IN/OUT arrays of the top subroutine (stopScopes)
767 # that cannot be transformed into scalar
768
769 # At least for rain_ice.F90, inlining must be performed before executing this code
770 assert scope.find('.//{*}include') is None and \
771 scope.find('.//{*}include-stmt') is None, \
772 "inlining must be performed before removing horizontal dimensions"
773
774 if scope.path in stopScopes:
775 # List of dummy arguments whose shape cannot be modified
776 preserveShape = [v['n'] for v in scope.varList if v['arg']]
777 else:
778 preserveShape = []
779
780 # 4 - For all subroutines or modi_ interface
781 if 'sub:' in scope.path:
782 # Remove suppressed dimensions "Z(JIJI)" => "Z"
783 # We cannot do this based upon declaration transformation because an array can
784 # be declared in one scope and used in another sub-scope
785 for namedE in scope.findall('.//{*}named-E/{*}R-LT/{*}parens-R/../..'):
786 if n2name(namedE.find('./{*}N')).upper() not in preserveShape:
787 var = scope.varList.findVar(n2name(namedE.find('./{*}N')).upper())
788 if var is not None and var['as'] is not None and len(var['as']) > 0:
789 subs = namedE.findall('./{*}R-LT/{*}parens-R/' +
790 '{*}element-LT/{*}element')
791 if (len(subs) == 1 and var['as'][0][1] in hUupperBounds) or \
792 (len(subs) == 2 and (var['as'][0][1] in hUupperBounds and
793 var['as'][1][1] in hUupperBounds)):
794 namedE.remove(namedE.find('./{*}R-LT'))
795
796 # Remove (:) or (:,:) for horizontal array in call-statement
797 # or replace ':' by index
798 for call in scope.findall('.//{*}call-stmt'):
799 for namedE in call.findall('./{*}arg-spec//{*}named-E'):
800 subs = namedE.findall('.//{*}section-subscript')
801 var = scope.varList.findVar(n2name(namedE.find('./{*}N')).upper())
802 if len(subs) > 0 and (var is None or var['as'] is None or
803 len(var['as']) < len(subs)):
804 # Before adding a warning, functions (especially unpack) must
805 # be recognised
806 # logging.warning(("Don't know if first dimension of {name} must " +
807 # "be modified or not -> kept untouched"
808 # ).format(name=alltext(namedE)))
809 remove = False # to remove completly the parentheses
810 index = False # to transform ':' into index
811 elif (len(subs) >= 2 and
812 ':' in alltext(subs[0]) and var['as'][0][1] in hUupperBounds and
813 ':' in alltext(subs[1]) and var['as'][1][1] in hUupperBounds):
814 # eg: CALL(P(:, :)) with SIZE(P, 1) == D%NIT and SIZE(P, 2) == D%NJT
815 remove = len(subs) == 2
816 index = (len(subs) > 2 and
817 len([sub for sub in subs if ':' in alltext(sub)]) == 2)
818 elif (len(subs) >= 1 and
819 ':' in alltext(subs[0]) and var['as'][0][1] in hUupperBounds):
820 # eg: CALL(P(:)) with SIZE(P, 1) == D%NJT
821 remove = len(subs) == 1
822 index = (len(subs) > 1 and
823 len([sub for sub in subs if ':' in alltext(sub)]) == 1)
824 else:
825 remove = False
826 index = False
827 if remove:
828 if n2name(namedE.find('./{*}N')).upper() in preserveShape:
829 slice2index(namedE, scope)
830 else:
831 nodeRLT = namedE.find('.//{*}R-LT')
832 scope.getParent(nodeRLT).remove(nodeRLT)
833 if index:
834 slice2index(namedE, scope)
835
836 # Remove dimensions in variable declaration statements
837 # This modification must be done after other modifications so that
838 # the findVar method still return an array
839 for decl in scope.findall('.//{*}T-decl-stmt/{*}EN-decl-LT/{*}EN-decl'):
840 name = n2name(decl.find('./{*}EN-N/{*}N')).upper()
841 if name not in preserveShape:
842 varsShape = decl.findall('.//{*}shape-spec-LT')
843 for varShape in varsShape:
844 subs = varShape.findall('.//{*}shape-spec')
845 if (len(subs) == 1 and alltext(subs[0]) in hUupperBounds) or \
846 (len(subs) == 2 and (alltext(subs[0]) in hUupperBounds and
847 alltext(subs[1]) in hUupperBounds)):
848 # Transform array declaration into scalar declaration
849 itemToRemove = scope.getParent(varShape)
850 scope.getParent(itemToRemove).remove(itemToRemove)
851 # We should set scope.varList to None here to clear the cache
852 # but we don't to save some computational time
853
854 # 4 - Values for removed indexes
855 for loopIndex in indexRemoved:
856 # Initialize former indexes JI,JJ,JIJ to first array element:
857 # JI=D%NIB, JJ=D%NJB, JIJ=D%NIJB
858 scope.insertStatement(
859 createExpr(loopIndex + " = " + indexToCheck[loopIndex][0])[0], True)
860 if len(indexRemoved) > 0:
861 scope.addArgInTree('D', 'TYPE(DIMPHYEX_t) :: D',
862 0, stopScopes, moduleVarList=[('MODD_DIMPHYEX', ['DIMPHYEX_t'])],
863 parser=parser, parserOptions=parserOptions, wrapH=wrapH)
864 # Check loop index presence at declaration of the scope
865 scope.addVar([[scope.path, loopIndex, 'INTEGER :: ' + loopIndex, None]
866 for loopIndex in indexRemoved
867 if scope.varList.findVar(loopIndex, exactScope=True) is None])
868
869 @debugDecor
870 def removePHYEXUnusedLocalVar(self, excludeList=None, simplify=False):
871 """
872 Remove unused local variables (dummy and module variables are not suppressed)
873 This function is identical to variables.removeUnusedLocalVar except that this one
874 is specific to the PHYEX code and take into account the mnh_expand directives.
875 :param excludeList: list of variable names to exclude from removal (even if unused)
876 :param simplify: try to simplify code (if we delete a declaration statement that used a
877 variable as kind selector, and if this variable is not used else where,
878 we also delete it)
879 """
880
881 # Look for variables needed for the mnh_expand directives
882 for node in self.findall('.//{*}C'):
883 if node.text.startswith('!$mnh_expand_array(') or \
884 node.text.startswith('!$mnh_expand_where('):
885 if excludeList is None:
886 excludeList = []
887 elems = node.text.split('(')[1].split(')')[0].split(',')
888 excludeList.extend([v.strip().upper() for v in [e.split('=')[0] for e in elems]])
889 return self.removeUnusedLocalVar(excludeList=excludeList, simplify=simplify)
890
891 @debugDecor
892 def expandAllArraysPHYEX(self, concurrent=False):
893 """
894 Transform array syntax into DO loops
895 :param concurrent: use 'DO CONCURRENT' instead of simple 'DO' loops
896 """
897
898 # For simplicity, all functions (not only array functions) have been searched
899 # in the PHYEX source code
900 funcList = ['AA2', 'AA2W', 'AF3', 'AM3', 'ARTH', 'BB3', 'BB3W', 'COEFJ', 'COLL_EFFI',
901 'DELTA', 'DELTA_VEC', 'DESDTI', 'DESDTW', 'DQSATI_O_DT_1D',
902 'DQSATI_O_DT_2D_MASK', 'DQSATI_O_DT_3D', 'DQSATW_O_DT_1D',
903 'DQSATW_O_DT_2D_MASK', 'DQSATW_O_DT_3D', 'DSDD', 'DXF', 'DXM', 'DYF',
904 'DYM', 'DZF', 'DZM', 'ESATI', 'ESATW', 'FUNCSMAX', 'GAMMA_INC', 'GAMMA_X0D',
905 'GAMMA_X1D', 'GENERAL_GAMMA', 'GET_XKER_GWETH', 'GET_XKER_N_GWETH',
906 'GET_XKER_N_RACCS', 'GET_XKER_N_RACCSS', 'GET_XKER_N_RDRYG',
907 'GET_XKER_N_SACCRG', 'GET_XKER_N_SDRYG', 'GET_XKER_N_SWETH', 'GET_XKER_RACCS',
908 'GET_XKER_RACCSS', 'GET_XKER_RDRYG', 'GET_XKER_SACCRG', 'GET_XKER_SDRYG',
909 'GET_XKER_SWETH', 'GX_M_M', 'GX_M_U', 'GX_U_M', 'GX_V_UV', 'GX_W_UW', 'GY_M_M',
910 'GY_M_V', 'GY_U_UV', 'GY_V_M', 'GY_W_VW', 'GZ_M_M', 'GZ_M_W', 'GZ_U_UW',
911 'GZ_V_VW', 'GZ_W_M', 'HYPGEO', 'ICENUMBER2', 'LEAST_LL', 'LNORTH_LL',
912 'LSOUTH_LL', 'LWEST_LL', 'MOMG', 'MXF', 'MXM', 'MYF', 'MYM', 'MZF', 'MZM',
913 'QSATI_0D', 'QSATI_1D', 'QSATI_2D', 'QSATI_2D_MASK', 'QSATI_3D',
914 'QSATMX_TAB', 'QSATW_0D', 'QSATW_1D', 'QSATW_2D', 'QSATW_2D_MASK',
915 'QSATW_3D', 'RECT', 'REDIN', 'SINGL_FUNCSMAX', 'SM_FOES_0D', 'SM_FOES_1D',
916 'SM_FOES_2D', 'SM_FOES_2D_MASK', 'SM_FOES_3D', 'SM_PMR_HU_1D', 'SM_PMR_HU_3D',
917 'TIWMX_TAB', 'TO_UPPER', 'ZRIDDR', 'GAMMLN', 'COUNTJV2D', 'COUNTJV3D', 'UPCASE']
918
919 return self.removeArraySyntax(concurrent=concurrent, useMnhExpand=False,
920 loopVar=_loopVarPHYEX, reuseLoop=False,
921 funcList=funcList, updateMemSet=True, updateCopy=True)
922
923 @debugDecor
925 """
926 Convert intrinsic math functions **, LOG, ATAN, **2, **3, **4, EXP, COS, SIN, ATAN2
927 into a self defined function BR_ for MesoNH CPU/GPU bit-reproductibility
928 """
929 # Power integer allowed for BR_Pn and functions converted (from modi_bitrep.f90)
930 powerBRList = [2, 3, 4]
931 mathBRList = ['ALOG', 'LOG', 'EXP', 'COS', 'SIN', 'ASIN', 'ATAN', 'ATAN2']
932
933 for scope in self.getScopes():
934 # 1/2 Look for all operations and seek for power **
935 # <f:op-E>
936 # ... ==> leftOfPow
937 # <f:op>
938 # <f:o>**</f:o>
939 # </f:op>
940 # ... ==> rightOfPow
941 # </f:op-E>
942 for opo in scope.findall('.//{*}o'):
943 if alltext(opo) == '**':
944 op = scope.getParent(opo)
945 opE = scope.getParent(opo, level=2)
946 parOfopE = scope.getParent(opo, level=3)
947 # Save the position of the opE that will be converted
948 index = list(parOfopE).index(opE)
949
950 # Get the next/previous object after/before the ** operator which are
951 # the siblings of the parent of <f:o>*</f:o>
952 rightOfPow = scope.getSiblings(op, after=True, before=False)[0]
953 leftOfPow = scope.getSiblings(op, after=False, before=True)[0]
954
955 # Prepare the object that will contain the left and right of Pow
956 nodeRLT = createElem('R-LT')
957 parensR = createElem('parens-R', text='(', tail=')')
958 elementLT = createElem('element-LT')
959
960 # Regarding the right part of pow(), build a new node expression :
961 # If it is a number and check only for 2, 3 and 4 (e.g. A**2, B**3, D**4 etc)
962 if tag(rightOfPow) == 'literal-E':
963 # Handle '2.' and '2.0'
964 powerNumber = int(alltext(rightOfPow).replace('.', ''))
965 if powerNumber in powerBRList:
966 # <f:named-E>
967 # <f:N>
968 # <f:n>BR_Pn</f:n>
969 # </f:N>
970 # <f:R-LT>
971 # <f:parens-R>(
972 # <f:element-LT>
973 # <f:element>
974 # ... ==> leftOfPow
975 # </f:element>,
976 # </f:element-LT>
977 # </f:parens-R>)
978 # </f:R-LT>
979 # </f:named-E>
980 nodeBRP = createExprPart('BR_P' + str(powerNumber))
981 element = createElem('element')
982 element.append(leftOfPow)
983 elementLT.append(element)
984 # If the right part of pow() is not a number OR it is a number
985 # except 2, 3 or 4 (powerBRList)
986 if tag(rightOfPow) != 'literal-E' or \
987 (tag(rightOfPow) == 'literal-E' and
988 int(alltext(rightOfPow).replace('.', '')) not in powerBRList):
989 # <f:named-E>
990 # <f:N>
991 # <f:n>BR_POW</f:n> or <f:n>BR_Pn</f:n>
992 # </f:N>
993 # <f:R-LT>
994 # <f:parens-R>(
995 # <f:element-LT>
996 # <f:element>
997 # ... ==> leftOfPow
998 # </f:element>,
999 # <f:element>
1000 # ... ==> rightOfPow
1001 # </f:element>
1002 # </f:element-LT>
1003 # </f:parens-R>)
1004 # </f:R-LT>
1005 # </f:named-E>
1006 nodeBRP = createExprPart('BR_POW')
1007 leftElement = createElem('element', tail=',')
1008 leftElement.append(leftOfPow)
1009 rightElement = createElem('element')
1010 rightElement.append(rightOfPow)
1011 elementLT.append(leftElement)
1012 elementLT.append(rightElement)
1013
1014 # Insert the RLT object as a sibling of the BR_ object,
1015 # e.g. instead of the old object
1016 parensR.append(elementLT)
1017 nodeRLT.append(parensR)
1018 nodeBRP.insert(1, nodeRLT)
1019 nodeBRP.tail = opE.tail
1020 parOfopE.remove(opE)
1021 parOfopE.insert(index, nodeBRP)
1022
1023 # Add necessary module in the current scope
1024 scope.addModuleVar([(scope.path, 'MODI_BITREP', None)])
1025
1026 # 2/2 Look for all specific functions LOG, ATAN, EXP,etc
1027 for nnn in scope.findall('.//{*}named-E/{*}N/{*}n'):
1028 if alltext(nnn).upper() in mathBRList:
1029 if alltext(nnn).upper() == 'ALOG':
1030 nnn.text = 'BR_LOG'
1031 else:
1032 nnn.text = 'BR_' + nnn.text
1033 # Add necessary module in the current scope
1034 scope.addModuleVar([(scope.path, 'MODI_BITREP', None)])
1035
1036 @debugDecor
1038 """
1039 Convert all calling of functions and gradient present in shumansGradients
1040 table into the use of subroutines
1041 and use mnh_expand_directives to handle intermediate computations
1042 """
1043 def getDimsAndMNHExpandIndexes(zshugradwkDim, dimWorkingVar=''):
1044 dimSuffRoutine = ''
1045 if zshugradwkDim == 1:
1046 dimSuffRoutine = '2D' # e.g. in turb_ver_dyn_flux : MZM(ZCOEFS(:,IKB))
1047 dimSuffVar = '1D'
1048 mnhExpandArrayIndexes = 'JIJ=IIJB:IIJE'
1049 elif zshugradwkDim == 2:
1050 dimSuffVar = '2D'
1051 if 'D%NKT' in dimWorkingVar:
1052 mnhExpandArrayIndexes = 'JIJ=IIJB:IIJE,JK=1:IKT'
1053 elif 'D%NIT' in dimWorkingVar and 'D%NJT' in dimWorkingVar:
1054 # only found in turb_hor*
1055 mnhExpandArrayIndexes = 'JI=1:IIT,JJ=1:IJT'
1056 dimSuffRoutine = '2D' # e.g. in turb_hor : MZM(PRHODJ(:,:,IKB))
1057 else:
1058 # raise PYFTError('mnhExpandArrayIndexes construction case ' +
1059 # 'is not handled, case for zshugradwkDim == 2, ' +
1060 # "dimWorkingVar = ' + dimWorkingVar)
1061 dimSuffRoutine = ''
1062 mnhExpandArrayIndexes = 'JIJ=IIJB:IIJE,JK=1:IKT'
1063 elif zshugradwkDim == 3: # case for turb_hor 3D variables
1064 dimSuffVar = '3D'
1065 mnhExpandArrayIndexes = 'JI=1:IIT,JJ=1:IJT,JK=1:IKT'
1066 else:
1067 raise PYFTError('Shuman func to routine conversion not implemented ' +
1068 'for 4D+ dimensions variables')
1069 return dimSuffRoutine, dimSuffVar, mnhExpandArrayIndexes
1070
1071 def FUNCtoROUTINE(scope, stmt, itemFuncN, localShumansCount, inComputeStmt,
1072 nbzshugradwk, zshugradwkDim, dimWorkingVar):
1073 """
1074 :param scope: node on which the calling function is present before transformation
1075 :param stmt: statement node (a-stmt or call-stmt) that contains the function(s) to be
1076 transformed
1077 :param itemFuncN: <n>FUNCTIONNAME</n> node
1078 :param localShumansCount: instance of the shumansGradients dictionnary
1079 for the given scope (which contains the number of times a
1080 function has been called within a transformation)
1081 :param dimWorkingVar: string of the declaration of a potential working variable
1082 depending on the array on wich the shuman is applied
1083 (e.g. MZM(PRHODJ(:,IKB));
1084 dimWorkingVar = 'REAL, DIMENSION(D%NIJT) :: ' )
1085 :return zshugradwk
1086 :return callStmt: the new CALL to the routines statement
1087 :return computeStmt: the a-stmt computation statement if there was an operation
1088 in the calling function in stmt
1089 """
1090 # Function name, parent and grandParent
1091 parStmt = scope.getParent(stmt)
1092 parItemFuncN = scope.getParent(itemFuncN) # <N><n>MZM</N></n>
1093 # <named-E><N><n>MZM</N></n> <R-LT><f:parens-R>(<f:element-LT><f:element>....
1094 grandparItemFuncN = scope.getParent(itemFuncN, level=2)
1095 funcName = alltext(itemFuncN)
1096
1097 # workingItem = Content of the function
1098 indexForCall = list(parStmt).index(stmt)
1099 if inComputeStmt:
1100 # one for !$mnh_expand, one for !$acc kernels added at the previous
1101 # call to FUNCtoROUTINE
1102 indexForCall -= 2
1103 siblsItemFuncN = scope.getSiblings(parItemFuncN, after=True, before=False)
1104 workingItem = siblsItemFuncN[0][0][0]
1105 # Case where & is present in the working item.
1106 # We must look for all contents until the last ')'
1107 if len(siblsItemFuncN[0][0]) > 1:
1108 # last [0] is to avoid getting the '( )' from the function
1109 workingItem = scope.updateContinuation(siblsItemFuncN[0][0], removeALL=True,
1110 align=False, addBegin=False)[0]
1111
1112 # Detect if the workingItem contains expressions, if so:
1113 # create a compute statement embedded by mnh_expand directives
1114 opE = workingItem.findall('.//{*}op-E')
1115 scope.addArrayParenthesesInNode(workingItem)
1116 computeStmt = []
1117 dimSuffVar = str(zshugradwkDim) + 'D'
1118 dimSuffRoutine, dimSuffVar, mnhExpandArrayIndexes = \
1119 getDimsAndMNHExpandIndexes(zshugradwkDim, dimWorkingVar)
1120 if len(opE) > 0:
1121 nbzshugradwk += 1
1122 computingVarName = 'ZSHUGRADWK'+str(nbzshugradwk)+'_'+str(zshugradwkDim)+'D'
1123 # Add the declaration of the new computing var and workingVar if not already present
1124 if not scope.varList.findVar(computingVarName):
1125 scope.addVar([[scope.path, computingVarName,
1126 dimWorkingVar + computingVarName, None]])
1127 else:
1128 # Case of nested shuman/gradients with a working variable already declared.
1129 # dimWorkingVar is only set again for mnhExpandArrayIndexes
1130 computeVar = scope.varList.findVar(computingVarName)
1131 dimWorkingVar = 'REAL, DIMENSION('
1132 for dims in computeVar['as'][:arrayDim]:
1133 dimWorkingVar += dims[1] + ','
1134 dimWorkingVar = dimWorkingVar[:-1] + ') ::'
1135
1136 dimSuffRoutine, dimSuffVar, mnhExpandArrayIndexes = \
1137 getDimsAndMNHExpandIndexes(zshugradwkDim, dimWorkingVar)
1138
1139 # Insert the directives and the compute statement
1140 mnhOpenDir = "!$mnh_expand_array(" + mnhExpandArrayIndexes + ")"
1141 mnhCloseDir = "!$mnh_end_expand_array(" + mnhExpandArrayIndexes + ")"
1142 computeStmt = createExpr(computingVarName + " = " + alltext(workingItem))[0]
1143 workingItem = computeStmt.find('.//{*}E-1')
1144 parStmt.insert(indexForCall, createElem('C', text='!$acc kernels', tail='\n'))
1145 parStmt.insert(indexForCall + 1, createElem('C', text=mnhOpenDir, tail='\n'))
1146 parStmt.insert(indexForCall + 2, computeStmt)
1147 parStmt.insert(indexForCall + 3, createElem('C', text=mnhCloseDir, tail='\n'))
1148 parStmt.insert(indexForCall + 4, createElem('C',
1149 text='!$acc end kernels', tail='\n'))
1150 parStmt.insert(indexForCall + 5, createElem('C',
1151 text='!', tail='\n')) # To increase readibility
1152 indexForCall += 6
1153
1154 # Add the new CALL statement
1155 if zshugradwkDim == 1:
1156 dimSuffRoutine = '2D'
1157 workingVar = 'Z' + funcName + dimSuffVar + '_WORK' + str(localShumansCount[funcName])
1158 gpuGradientImplementation = '_PHY(D, '
1159 if funcName in ('GY_U_UV', 'GX_V_UV'):
1160 gpuGradientImplementation = '_DEVICE('
1161 callStmt = createExpr("CALL " + funcName + dimSuffRoutine +
1162 gpuGradientImplementation + alltext(workingItem) +
1163 ", " + workingVar + ")")[0]
1164 parStmt.insert(indexForCall, callStmt)
1165
1166 # Remove the function/gradient from the original statement
1167 parOfgrandparItemFuncN = scope.getParent(grandparItemFuncN)
1168 indexWorkingVar = list(parOfgrandparItemFuncN).index(grandparItemFuncN)
1169 savedTail = grandparItemFuncN.tail
1170 parOfgrandparItemFuncN.remove(grandparItemFuncN)
1171
1172 # Add the working variable within the original statement
1173 xmlWorkingvar = createExprPart(workingVar)
1174 xmlWorkingvar.tail = savedTail
1175 parOfgrandparItemFuncN.insert(indexWorkingVar, xmlWorkingvar)
1176
1177 # Add the declaration of the shuman-gradient workingVar if not already present
1178 if not scope.varList.findVar(workingVar):
1179 scope.addVar([[scope.path, workingVar, dimWorkingVar + workingVar, None]])
1180
1181 return callStmt, computeStmt, nbzshugradwk
1182
1183 shumansGradients = {'MZM': 0, 'MXM': 0, 'MYM': 0, 'MZF': 0, 'MXF': 0, 'MYF': 0,
1184 'DZM': 0, 'DXM': 0, 'DYM': 0, 'DZF': 0, 'DXF': 0, 'DYF': 0,
1185 'GZ_M_W': 0, 'GZ_W_M': 0, 'GZ_U_UW': 0, 'GZ_V_VW': 0,
1186 'GX_M_U': 0, 'GX_U_M': 0, 'GX_W_UW': 0, 'GX_M_M': 0,
1187 'GY_V_M': 0, 'GY_M_V': 0, 'GY_W_VW': 0, 'GY_M_M': 0,
1188 'GX_V_UV': 0, 'GY_U_UV': 0}
1189 scopes = self.getScopes()
1190 if scopes[0].path.split('/')[-1].split(':')[1][:4] == 'MODD':
1191 return
1192 for scope in scopes:
1193 if 'sub:' in scope.path and 'func' not in scope.path \
1194 and 'interface' not in scope.path:
1195 # Init : look for all a-stmt and call-stmt which contains a shuman or
1196 # gradients function, and save it into a list foundStmtandCalls
1197 foundStmtandCalls, computeStmtforParenthesis = {}, []
1198 aStmt = scope.findall('.//{*}a-stmt')
1199 callStmts = scope.findall('.//{*}call-stmt')
1200 aStmtandCallStmts = aStmt + callStmts
1201 for stmt in aStmtandCallStmts:
1202 elemN = stmt.findall('.//{*}n')
1203 for el in elemN:
1204 if alltext(el) in list(shumansGradients):
1205 # Expand the single-line if-stmt necessary
1206 # to add all the new lines further.
1207 parStmt = scope.getParent(stmt)
1208 if tag(parStmt) == 'action-stmt':
1209 scope.changeIfStatementsInIfConstructs(
1210 singleItem=scope.getParent(parStmt))
1211
1212 if str(stmt) in foundStmtandCalls:
1213 foundStmtandCalls[str(stmt)][1] += 1
1214 else:
1215 foundStmtandCalls[str(stmt)] = [stmt, 1]
1216
1217 # For each a-stmt and call-stmt containing at least 1 shuman/gradient function
1218 for stmt in foundStmtandCalls:
1219 localShumansGradients = copy.deepcopy(shumansGradients)
1220 elemToLookFor = [foundStmtandCalls[stmt][0]]
1221 previousComputeStmt = []
1222 maxnbZshugradwk = 0
1223
1224 while len(elemToLookFor) > 0:
1225 nbzshugradwk = 0
1226 for elem in elemToLookFor:
1227 elemN = elem.findall('.//{*}n')
1228 for el in elemN:
1229 if alltext(el) in list(localShumansGradients.keys()):
1230 # Check the dimensions of the stmt objects in which the
1231 # function exist for handling selecting-index
1232 # shuman-function use
1233 # 1) if the stmt is from an a-astmt, check E1
1234 nodeE1var = foundStmtandCalls[stmt][0].findall(
1235 './/{*}E-1/{*}named-E/{*}N')
1236 if len(nodeE1var) > 0:
1237 var = scope.varList.findVar(alltext(nodeE1var[0]))
1238 allSubscripts = foundStmtandCalls[stmt][0].findall(
1239 './/{*}E-1//{*}named-E/{*}R-LT/' +
1240 '{*}array-R/{*}section-subscript-LT')
1241 # 2) if the stmt is from a call-stmt,
1242 # check the first <named-E><N> in the function
1243 else:
1244 elPar = scope.getParent(el, level=2) # MXM(...)
1245 callVar = elPar.findall('.//{*}named-E/{*}N')
1246 if alltext(el)[0] == 'G':
1247 # If it is a gradient, the array on which the gradient
1248 # is applied is the last argument
1249
1250 # callVar[-1] is array on which the gradient is applied
1251 var = scope.varList.findVar(alltext(callVar[-1]))
1252 shumanIsCalledOn = scope.getParent(callVar[-1])
1253 else:
1254 # Shumans
1255 var, inested = None, 0
1256 # pylint: disable-next=unsubscriptable-object
1257 while not var or len(var['as']) == 0:
1258 # While the var is not an array already declared
1259 # callVar[0] is the first array on which the
1260 # function is applied
1261 var = scope.varList.findVar(
1262 alltext(callVar[inested]))
1263 inested += 1
1264 shumanIsCalledOn = scope.getParent(callVar[inested-1])
1265 allSubscripts = shumanIsCalledOn.findall(
1266 './/{*}R-LT/{*}array-R/' +
1267 '{*}section-subscript-LT')
1268
1269 # if var: # Protection in case of nested functions,
1270 # var is not an array but None
1271 arrayDim = len(var['as'])
1272
1273 # Look for subscripts in case of array sub-selection
1274 # (such as 1 or IKB)
1275 if len(allSubscripts) > 0:
1276 for subLT in allSubscripts:
1277 for sub in subLT:
1278 lowerBound = sub.findall('.//{*}lower-bound')
1279 if len(lowerBound) > 0:
1280 if len(sub.findall('.//{*}upper-bound')) > 0:
1281 # For protection: not handled with
1282 # lower:upper bounds
1283 raise PYFTError('ShumanFUNCtoCALL does ' +
1284 'not handle conversion ' +
1285 'to routine of array ' +
1286 'subselection lower:upper' +
1287 ': how to set up the ' +
1288 'shape of intermediate ' +
1289 'arrays ?')
1290 # Handle change of dimensions for
1291 # selecting index for the working arrays
1292 arrayDim -= 1
1293
1294 # Build the dimensions declaration in case of
1295 # working/intermediate variable needed
1296 dimWorkingVar = ''
1297 if var:
1298 dimWorkingVar = 'REAL, DIMENSION('
1299 for dims in var['as'][:arrayDim]:
1300 dimWorkingVar += dims[1] + ','
1301 dimWorkingVar = dimWorkingVar[:-1] + ') ::'
1302
1303 # Add existing working variable with the name of the function
1304 localShumansGradients[alltext(el)] += 1
1305
1306 # To be sure that ending !comments after the statement is
1307 # not impacting the placement of the last !mnh_expand_array
1308 if foundStmtandCalls[stmt][0].tail:
1309 foundStmtandCalls[stmt][0].tail = \
1310 foundStmtandCalls[stmt][0].tail.replace('\n', '') + '\n'
1311 else:
1312 foundStmtandCalls[stmt][0].tail = '\n'
1313
1314 # Transform the function into a call statement
1315 (newCallStmt, newComputeStmt,
1316 nbzshugradwk) = FUNCtoROUTINE(scope, elem, el,
1317 localShumansGradients,
1318 elem in previousComputeStmt,
1319 nbzshugradwk, arrayDim,
1320 dimWorkingVar)
1321 # Update the list of elements to check if there are still
1322 # remaining function to convert within the new call-stmt
1323 elemToLookFor.append(newCallStmt)
1324
1325 # If a new intermediate compute statement was created, it needs
1326 # to be checked and add Parenthesis to arrays for mnh_expand
1327 if len(newComputeStmt) > 0:
1328 elemToLookFor.append(newComputeStmt)
1329 computeStmtforParenthesis.append(newComputeStmt)
1330 # Allow to save that this newComputeStmt comes with 2
1331 # extra lines before and after
1332 # (mnh_expand and acc directives)
1333 previousComputeStmt.append(newComputeStmt)
1334 break
1335 # Check in old and new objects if there are still
1336 # remaining shuman/gradients functions
1337 elemToLookForNew = []
1338 for i in elemToLookFor:
1339 nodeNs = i.findall('.//{*}n')
1340 if len(nodeNs) > 0:
1341 for nnn in nodeNs:
1342 if alltext(nnn) in list(localShumansGradients):
1343 elemToLookForNew.append(i)
1344 break
1345 elemToLookFor = elemToLookForNew
1346 # Save the maximum number of necessary intermediate
1347 # computing variables ZSHUGRADWK
1348 if nbzshugradwk > maxnbZshugradwk:
1349 maxnbZshugradwk = nbzshugradwk
1350
1351 # Add parenthesis around all variables
1352 scope.addArrayParenthesesInNode(foundStmtandCalls[stmt][0])
1353
1354 # For the last compute statement, add mnh_expand and acc
1355 # kernels if not call statement
1356 if tag(foundStmtandCalls[stmt][0]) != 'call-stmt':
1357 # get mnhExpandArrayIndexes
1358 # Here dimSuffRoutine, dimSuffVar are not used
1359 dimSuffRoutine, dimSuffVar, mnhExpandArrayIndexes = \
1360 getDimsAndMNHExpandIndexes(arrayDim, dimWorkingVar)
1361
1362 parStmt = scope.getParent(foundStmtandCalls[stmt][0])
1363 indexForCall = list(parStmt).index(foundStmtandCalls[stmt][0])
1364 mnhOpenDir = "!$mnh_expand_array(" + mnhExpandArrayIndexes + ")"
1365 mnhCloseDir = "!$mnh_end_expand_array(" + mnhExpandArrayIndexes + ")"
1366 parStmt.insert(indexForCall,
1367 createElem('C', text="!$acc kernels", tail='\n'))
1368 parStmt.insert(indexForCall + 1,
1369 createElem('C', text=mnhOpenDir, tail='\n'))
1370 parStmt.insert(indexForCall + 3,
1371 createElem('C', text=mnhCloseDir, tail='\n'))
1372 parStmt.insert(indexForCall + 4,
1373 createElem('C', text="!$acc end kernels", tail='\n'))
1374 parStmt.insert(indexForCall + 5,
1375 createElem('C', text="!", tail='\n')) # For readibility
1376
1377 # For all saved intermediate newComputeStmt, add parenthesis around all variables
1378 for stmt in computeStmtforParenthesis:
1379 scope.addArrayParenthesesInNode(stmt)
1380
1381 @debugDecor
1382 @noParallel
1383 @updateTree('signal')
1385 """
1386 build module files containing helpers to copy user type structures
1387 """
1388 for scope in self.getScopes():
1389 if scope.path.split('/')[-1].split(':')[0] == 'type':
1390 typeName = scope.path.split('/')[-1].split(':')[1]
1391 filename = os.path.join(os.path.dirname(scope.getFileName()),
1392 "modd_util_{t}.F90".format(t=typeName.lower()))
1393 scope.tree.signal(filename)
1394 with open(filename, 'w', encoding="utf-8") as file:
1395 file.write("""
1396MODULE MODD_UTIL_{t}
1397USE {m}, ONLY: {t}
1398CONTAINS
1399SUBROUTINE COPY_{t} (YD, LDCREATED)""".format(t=typeName,
1400 m=scope.path.split('/')[-2].split(':')[1]))
1401
1402 for var in scope.varList:
1403 if 'TYPE(' in var['t'].replace(' ', '').upper():
1404 file.write("""
1405USE MODD_UTIL_{t}""".format(t=var['t'].replace(' ', '')[5:-1]))
1406
1407 file.write("""
1408IMPLICIT NONE
1409TYPE ({t}), INTENT(IN), TARGET :: YD
1410LOGICAL, OPTIONAL, INTENT(IN) :: LDCREATED
1411INTEGER :: I
1412LOGICAL :: LLCREATED
1413LLCREATED = .FALSE.
1414IF (PRESENT (LDCREATED)) THEN
1415 LLCREATED = LDCREATED
1416ENDIF
1417IF (.NOT. LLCREATED) THEN
1418 !$acc enter data create (YD)
1419 !$acc update device (YD)
1420ENDIF""".format(t=typeName))
1421
1422 for var in scope.varList:
1423 if var['allocatable']:
1424 file.write("""
1425IF (ALLOCATED (YD%{v})) THEN
1426 !$acc enter data create (YD%{v})
1427 !$acc update device (YD%{v})
1428 !$acc enter data attach (YD%{v})
1429ENDIF""".format(v=var['n']))
1430 if 'TYPE(' in var['t'].replace(' ', '').upper():
1431 if var['as'] is not None and len(var['as']) != 0:
1432 indexes = ['LBOUND(YD%{v}, 1) + I - 1'.format(v=var['n'])]
1433 for i in range(len(var['as']) - 1):
1434 indexes.append('LBOUND(YD%{v}, {i})'.format(v=var['n'],
1435 i=str(i + 2)))
1436 file.write("""
1437DO I=1, SIZE(YD%{v})
1438 CALL COPY_{t}(YD%{v}({i}), LDCREATED=.TRUE.)
1439ENDDO""".format(v=var['n'], t=var['t'].replace(' ', '')[5:-1], i=', '.join(indexes)))
1440 else:
1441 file.write("""
1442CALL COPY_{t}(YD%{v}, LDCREATED=.TRUE.)""".format(v=var['n'], t=var['t'].replace(' ', '')[5:-1]))
1443
1444 file.write("""
1445END SUBROUTINE COPY_{t}
1446
1447SUBROUTINE WIPE_{t} (YD, LDDELETED)""".format(t=typeName))
1448
1449 for var in scope.varList:
1450 if 'TYPE(' in var['t'].replace(' ', '').upper():
1451 file.write("""
1452USE MODD_UTIL_{t}""".format(t=var['t'].replace(' ', '')[5:-1]))
1453
1454 file.write("""
1455IMPLICIT NONE
1456TYPE ({t}), INTENT(IN), TARGET :: YD
1457LOGICAL, OPTIONAL, INTENT(IN) :: LDDELETED
1458INTEGER :: I
1459LOGICAL :: LLDELETED
1460LLDELETED = .FALSE.
1461IF (PRESENT (LDDELETED)) THEN
1462 LLDELETED = LDDELETED
1463ENDIF""".format(t=typeName))
1464
1465 for var in scope.varList:
1466 if 'TYPE(' in var['t'].replace(' ', '').upper():
1467 if var['as'] is not None and len(var['as']) != 0:
1468 indexes = ['LBOUND(YD%{v}, 1) + I - 1'.format(v=var['n'])]
1469 for i in range(len(var['as']) - 1):
1470 indexes.append('LBOUND(YD%{v}, {i})'.format(v=var['n'],
1471 i=str(i + 2)))
1472 file.write("""
1473DO I=1, SIZE(YD%{v})
1474 CALL WIPE_{t}(YD%{v}({i}), LDDELETED=.TRUE.)
1475ENDDO""".format(v=var['n'], t=var['t'].replace(' ', '')[5:-1], i=', '.join(indexes)))
1476 else:
1477 file.write("""
1478CALL WIPE_{t}(YD%{v}, LDDELETED=.TRUE.)""".format(v=var['n'], t=var['t'].replace(' ', '')[5:-1]))
1479 if var['allocatable']:
1480 file.write("""
1481IF (ALLOCATED (YD%{v})) THEN
1482 !$acc exit data detach (YD%{v})
1483 !$acc exit data delete (YD%{v})
1484ENDIF""".format(v=var['n']))
1485
1486 file.write("""
1487IF (.NOT. LLDELETED) THEN
1488 !$acc exit data delete (YD)
1489ENDIF
1490END SUBROUTINE WIPE_{t}
1491
1492END MODULE MODD_UTIL_{t}\n""".format(t=typeName))
addStack(self, model, stopScopes, parser=None, parserOptions=None, wrapH=False)
deleteRoutineCallsMesoNHGPU(self, simplify=True)
removeIJDim(self, stopScopes, parser=None, parserOptions=None, wrapH=False, simplify=False)
removePHYEXUnusedLocalVar(self, excludeList=None, simplify=False)
deleteBudgetDDH(self, simplify=False)
deleteNonColumnCallsPHYEX(self, simplify=False)
expandAllArraysPHYEX(self, concurrent=False)
inlineContainedSubroutinesPHYEX(self, simplify=False)
deleteDrHook(self, simplify=False)
addMPPDB_CHECKS(self, printsMode=False)
_loopVarPHYEX(lowerDecl, upperDecl, lowerUsed, upperUsed, name, index)
generateEmptyPYFT(filename, fortran=None, **kwargs)
Definition pyfortool.py:42