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