def getNCSIZE(cas,dico,frgb): # ~~ check keyword ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ value,defaut = getKeyWord('PROCESSEURS PARALLELES',cas,dico,frgb) ncsize = -1 if value != []: ncsize = int(value[0]) elif defaut != []: ncsize = int(defaut[0]) return ncsize
def compilePRINCI(self,cfg,rebuild): if not "compile" in self.available.split(';'): return xref = self.active["xref"]; cfgname = self.active['cfg'] active = self.dids[xref][cfgname] confirmed = False # ~~> principal PRINCI file value,default = getKeyWord('FICHIER FORTRAN',active['cas'],DICOS[active['dico']]['dico'],DICOS[active['dico']]['frgb']) princiFile = ''; princiSafe = '' if value != []: # you do not need to compile the default executable princiFile = path.join(active['path'],eval(value[0])) if path.exists(princiFile): exeFile = path.join(active['safe'],path.splitext(eval(value[0]))[0] + cfg['SYSTEM']['sfx_exe']) if not path.exists(exeFile) or cfg['REBUILD'] == 0: print ' +> compiling princi file: ' + path.basename(princiFile) copyFile(princiFile,active['safe']) princiSafe = path.join(active['safe'],path.basename(princiFile)) confirmed = True else: raise Exception([{'name':'ACTION::compilePRINCI','msg':'I could not find your PRINCI file: '+princiFile}]) # ~~> associated PRINCI file for mod in active["links"]: link = active["links"][mod] value,default = getKeyWord('FICHIER FORTRAN',link['cas'],DICOS[link['dico']]['dico'],DICOS[link['dico']]['frgb']) princiFilePlage = '' if value != []: # you do not need to compile the default executable princiFilePlage = path.join(active['path'],eval(value[0])) if path.exists(princiFilePlage): if princiSafe != '': putFileContent(princiSafe,getFileContent(princiSafe)+['']+getFileContent(princiFilePlage)) else: print ' +> compiling princi file: ' + path.basename(princiFilePlage) exeFile = path.join(active['safe'],path.splitext(eval(value[0]))[0] + cfg['SYSTEM']['sfx_exe']) princiSafe = path.join(active['safe'],path.basename(princiFilePlage)) copyFile(princiFilePlage,active['safe']) confirmed = True else: raise Exception([{'name':'ACTION::compilePRINCI','msg':'I could not find your PRINCI file: '+princiFilePlage}]) if confirmed: try: compilePRINCI(princiSafe,active["code"],self.active['cfg'],cfg,self.bypass) except Exception as e: raise Exception([filterMessage({'name':'ACTION::compilePRINCI'},e,self.bypass)]) # only one item here #moveFile(exeFile,active['safe']) print ' ~> compilation successful ! created: ' + path.basename(exeFile)
def checkConsistency(cas,dico,frgb,cfg): # ~~ check for parallel consistency ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ value,defaut = getKeyWord('PROCESSEURS PARALLELES',cas,dico,frgb) ncsize = 0 if value != []: ncsize = int(value[0]) elif defaut != []: ncsize = int(defaut[0]) if ncsize > 1 and 'parallel' not in cfg['MODULES'].keys(): return False if ncsize < 2 and 'paravoid' not in cfg['MODULES'].keys(): return False # /!\ you might want to be more relaxed about this # ~~ check for openmi consistency ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ return True
def runXML(xmlFile,xmlConfig,bypass): xcpt = [] # try all keys for full report # ~~ Parse xmlFile ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ import xml.etree.ElementTree as XML print '... reading XML test specification file: ' + path.basename(xmlFile) f = open(xmlFile,'r') xmlTree = XML.parse(f) # may need to try first and report error xmlRoot = xmlTree.getroot() f.close() # ~~ Meta data process ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ title = "" #meta = META(title) #print '\n... acquiring meta data' display = False # ~~ Action analysis ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ do = ACTION(xmlFile,title,bypass) first = True for action in xmlRoot.findall("action"): if first: print '\n... looping through the todo list' first = False # ~~ Step 1. Common check for keys and CAS file ~~~~~~~~~~~~~~ try: doadd = do.addAction(action) except Exception as e: xcpt.append(filterMessage({'name':'runXML','msg':'add todo to the list'},e,bypass)) continue # bypass rest of the loop else: casFile = path.join(do.active['path'],doadd) if not path.isfile(casFile): xcpt.append({'name':'runXML','msg':'could not find your CAS file'+path.basename(casFile)}) continue # bypass rest of the loop # ~~ Step 2. Loop over configurations ~~~~~~~~~~~~~~~~~~~~~~~~ for cfgname in xmlConfig.keys(): cfg = xmlConfig[cfgname]['cfg'] if not do.addCFG(cfgname,cfg): continue # ~~> Parse DICO File and its IO Files default (only once) dicoFile = getDICO(cfg,do.active["code"]) do.updateCFG({'dico':dicoFile}) dico = DICOS[dicoFile]['dico'] frgb = DICOS[dicoFile]['frgb'] cas = readCAS(scanCAS(casFile),dico,frgb) if do.active["ncsize"] != '': setKeyValue('PROCESSEURS PARALLELES',cas,frgb,int(do.active["ncsize"])) do.updateCFG({'cas':cas}) if not checkConsistency(cas,dico,frgb,cfg): continue # ~~> Create the safe createDirectories(do.active['safe']) idico = DICOS[dicoFile]['input'] odico = DICOS[dicoFile]['output'] # ~~> Define config-split storage sortieFiles,iFS,oFS = setSafe(casFile,cas,idico,odico,do.active['safe']) # TODO: look at relative paths if sortieFiles != []: do.updateCFG({ 'sortie': sortieFiles }) do.updateCFG({ 'input':iFS }) do.updateCFG({ 'output':oFS }) # ~~> Case of coupling cplages,defaut = getKeyWord('COUPLING WITH',cas,dico,frgb) links = {} for cplage in cplages: for mod in cfg['MODULES'].keys(): if mod in cplage.lower(): # ~~> Extract the CAS File name casFilePlage,defaut = getKeyWord(mod.upper()+' STEERING FILE',cas,dico,frgb) if casFilePlage == []: casFilePlage = defaut[0] else: casFilePlage = eval(casFilePlage[0]) casFilePlage = path.join(path.dirname(casFile),casFilePlage) if not path.isfile(casFilePlage): raise Exception([{'name':'runCAS','msg':'missing coupling CAS file for '+mod+': '+casFilePlage}]) # ~~> Read the DICO File dicoFilePlage = getDICO(cfg,mod) dicoPlage = DICOS[dicoFilePlage]['dico'] frgbPlage = DICOS[dicoFilePlage]['frgb'] # ~~> Read the coupled CAS File casPlage = readCAS(scanCAS(casFilePlage),dicoPlage,frgbPlage) # ~~> Fill-in the safe idicoPlage = DICOS[dicoFilePlage]['input'] odicoPlage = DICOS[dicoFilePlage]['output'] sortiePlage,iFSPlage,oFSPlage = setSafe(casFilePlage,casPlage,idicoPlage,odicoPlage,do.active['safe']) # TODO: look at relative paths links.update({mod:{}}) links[mod].update({ 'code':mod, 'target':path.basename(casFilePlage), 'cas':casPlage, 'frgb':frgbPlage, 'dico':dicoFilePlage, 'iFS':iFSPlage, 'oFS':oFSPlage, 'sortie':sortiePlage }) if sortiePlage != []: links[mod].update({ 'sortie':sortiePlage }) if links != {}: do.updateCFG({ "links":links }) # ~~ Step 3. Complete all actions ~~~~~~~~~~~~~~~~~~~~~~~~~ # options.do takes: translate;run;compile and none doable = xmlConfig[cfgname]['options'].do if doable == '': doable = do.active["do"] if doable == '': doable = do.available display = display or xmlConfig[cfgname]['options'].display # ~~> Action type A. Translate the CAS file if "translate" in doable.split(';'): try: do.translateCAS(cfg['REBUILD']) except Exception as e: xcpt.append(filterMessage({'name':'runXML','msg':' +> translate'},e,bypass)) # ~~> Action type B. Analysis of the CAS file # TODO: # - comparison with DEFAULT values of the DICTIONARY #if "cas" in doable.split(';'): # - comparison of dictionnaries betwen configurations #if "dico" in doable.split(';'): # ~~> Action type C. Analysis of the PRINCI file # TODO: # - comparison with standard source files #if "princi" in doable.split(';'): # out = diffTextFiles( f,t ) # - comparison of called subroutines between configurations # ~~> Action type D. Compilation of PRINCI file # Contrary to the other step, Step 8 is completed where the original CAS file is # (for no particularly good reason) if "compile" in doable.split(';'): try: do.compilePRINCI(cfg,cfg['REBUILD']) except Exception as e: xcpt.append(filterMessage({'name':'runXML','msg':' +> compile'},e,bypass)) # ~~> Action type E. Running CAS files if "run" in doable.split(';'): try: do.runCAS(xmlConfig[cfgname]['options'],cfg,cfg['REBUILD']) except Exception as e: xcpt.append(filterMessage({'name':'runXML','msg':' +> run'},e,bypass)) # ~~ Extraction ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # did has all the IO references and the latest sortie files # ~~ Gathering targets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ plot = PLOT(title,bypass) first = True for typePlot in ["plot1d","plot2d","plot3d","plotpv"]: plot.addPlotType(typePlot) for ploting in xmlRoot.findall(typePlot): if first: print '\n... gathering targets through the plot list' first = False # ~~ Step 1. Common check for keys ~~~~~~~~~~~~~~~~~~~~~~~~ try: plot.addDraw(ploting) except Exception as e: xcpt.append(filterMessage({'name':'runXML','msg':'add plot to the list'},e,bypass)) continue # bypass the rest of the for loop # ~~ Step 2. Cumul layers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ for layer in ploting.findall("layer"): try: target = plot.addLayer(layer) except Exception as e: xcpt.append(filterMessage({'name':'runXML','msg':'add layer to the list'},e,bypass)) continue # bypass the rest of the for loop # ~~> round up targets and their configurations xref,src = target.split(':') if not do.dids.has_key(xref): xcpt.append({'name':'runXML','msg':'could not find reference to draw the action: '+xref}) # ~~> store layer and its configuration(s) try: findlayer = plot.findLayerConfig(do.dids[xref],src) except Exception as e: xcpt.append(filterMessage({'name':'runXML'},e)) continue # bypass the rest of the for loop else: plot.targetLayer(findlayer) plot.update(plot.drawing) # ~~ Matrix distribution by plot types ~~~~~~~~~~~~~~~~~~~~~~~~~~ # /!\ configurations cannot be called "together" or "distinct" or "oneofall" first = True for typePlot in plot.dids.keys(): if first: print '\n... plotting figures' first = False for xref in plot.dids[typePlot]: print ' +> reference: ',xref,' of type ',typePlot draw = plot.dids[typePlot][xref] xlayers = '' # now done with strings as arrays proved to be too challenging for layer in draw["layers"]: if layer['config'] == 'together': xys = [] for x in xlayers.split('|'): xys.append( (x+';'+':'.join( layer['fileName'].keys() )).strip(';') ) xlayers = '|'.join(xys) elif layer['config'] == 'distinct': ylayers = layer['fileName'].keys() xys = [] for i in range(len(ylayers)): for x in xlayers.split('|'): xys.append( (x+';'+ylayers[i]).strip(';') ) xlayers = '|'.join(xys) elif layer['config'] == 'oneofall': xys = []; cfg = layer['fileName'].keys()[0] #/!\ you are sure to have at least one (?) for x in xlayers.split('|'): xys.append( (x+';'+cfg).strip(';') ) xlayers = '|'.join(xys) else: if layer['config'] in layer['fileName'].keys(): xys = [] for x in xlayers.split('|'): xys.append( (x+';'+layer['config']).strip(';') ) xlayers = '|'.join(xys) nbFig = 0; alayers = xlayers.split('|') for cfglist in alayers: # ~~> Figure name if len(alayers) == 1: figureName = '.'.join([xref,draw['outFormat']]) else: nbFig += 1 figureName = '.'.join([xref,str(nbFig),draw['outFormat']]) print ' ~> saved as: ',figureName figureName = path.join(path.dirname(xmlFile),figureName) # ~~> Figure size draw["size"] = parseArrayPaires(draw["size"]) # ~~> Create Figure figure = Figure(typePlot,draw,display,figureName) for layer,cfgs in zip(draw["layers"],cfglist.split(';')): for cfg in cfgs.split(':'): for file in layer['fileName'][cfg][0]: # ~~ 1d plots ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if typePlot == "plot1d": #print typePlot,' ... drawing' figure.draw( layer['fileName'][cfg][2], { 'file': file, 'vars': layer["vars"], 'extract':parseArrayPaires(layer["extract"]), 'type': draw['type'], 'time':parseArrayPaires(layer["time"])[0] } ) # ~~ 2d plots ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if typePlot == "plot2d": # so far no plot type, but this may change #print typePlot,' ... drawing' figure.draw( layer['fileName'][cfg][2], { 'file': file, 'roi': parseArrayPaires(draw['roi']), 'vars': layer["vars"], 'extract':parseArrayPaires(layer["extract"]), 'type': draw['type'], 'time':parseArrayPaires(layer["time"])[0] } ) figure.show() """ # ~~> plot3d if plot["extract"] == '': plot["extract"] = "minmax" """ # ~~ Error management ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if xcpt != []: # raise full report raise Exception({'name':'runXML','msg':'in xmlFile: '+xmlFile,'tree':xcpt}) return
def runCAS(cfgName,cfg,codeName,casFile,options): # ~~~~ Read the DICO File ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ dicoFile = path.join(path.join(cfg['MODULES'][codeName]['path'],'lib'),codeName+cfg['TELVER']+'.dico') frgb,dico = scanDICO(dicoFile) iFS,oFS = getIOFilesSubmit(frgb,dico) # ~~ Read the principal CAS File ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if not path.exists(casFile): print '... inexistent CAS file: ',casFile return None # /!\ should you stop or carry on ? cas,lang = processCAS(casFile,frgb) if not checkConsistency(cas,dico,frgb,cfg): print '... inconsistent CAS file: ',casFile print ' +> you may be using an inappropriate configuration:',cfgName print ' +> or may be wishing for scalar mode while using parallel' return None # /!\ should you stop or carry on ? # ~~ Handling Directories ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CASDir = path.dirname(casFile) TMPDir = processTMP(casFile) # ~~ Read the included CAS File ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ cplages,defaut = getKeyWord('COUPLING WITH',cas,dico,frgb) #/!\ having done the loop this way it will not check for DELWAQ COUPLAGE = {} for cplage in cplages: for mod in cfg['MODULES'].keys(): if mod in cplage.lower(): # ~~~~ Extract the CAS File name ~~~~~~~~~~~~~~~~~~~~~~~ casFilePlage,defaut = getKeyWord(mod.upper()+' STEERING FILE',cas,dico,frgb) if casFilePlage == []: casFilePlage = defaut casFilePlage = path.join(CASDir,casFilePlage[0]) if not path.isfile(casFilePlage): print '... missing coupling CAS file for',mod,': ',casFilePlage return None # /!\ should you stop or carry on ? # ~~~~ Read the DICO File ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ dicoFilePlage = path.join(path.join(cfg['MODULES'][mod]['path'],'lib'),mod+cfg['TELVER']+'.dico') frgbPlage,dicoPlage = scanDICO(dicoFilePlage) iFSPlage,oFSPlage = getIOFilesSubmit(frgbPlage,dicoPlage) # ~~ Read the coupled CAS File ~~~~~~~~~~~~~~~~~~~~~~~~~ casPlage,lang = processCAS(casFilePlage,frgbPlage) if not checkConsistency(casPlage,dicoPlage,frgbPlage,cfg): print '... inconsistent CAS file: ',casFilePlage return None # /!\ should you stop or carry on ? COUPLAGE.update({mod:{}}) COUPLAGE[mod].update({'cas':casPlage,'frgb':frgbPlage,'iFS':iFSPlage,'oFS':oFSPlage,'dico':dicoPlage}) # ~~ Handling sortie file ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sortiefile = None if options.sortieFile: # define the filename (basename) of the sortie file sortiefile = path.basename(TMPDir)+'.sortie' # ~~ Handling all input files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # >>> Placing yourself where the CAS File is chdir(CASDir) # >>> Copy INPUT files into TMPDir if not processLIT(cas,iFS,TMPDir): sys.exit() for mod in COUPLAGE.keys(): if not processLIT(COUPLAGE[mod]['cas'],COUPLAGE[mod]['iFS'],TMPDir): sys.exit() # >>> Placing yourself into the TMPDir chdir(TMPDir) # >>> Creating LNG file processCONFIG(lang) # ~~ Handling Executable ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # >>> Names for the executable set #> names within TMPDir f90File = iFS['FICHIER FORTRAN'].split(';')[1] #> aggregation of PRINCI files for mod in COUPLAGE.keys(): f90FilePlage = COUPLAGE[mod]['iFS']['FICHIER FORTRAN'].split(';')[1] if path.isfile(f90FilePlage): putFileContent(f90File,getFileContent(f90File)+['']+getFileContent(f90FilePlage)) remove(f90FilePlage) objFile = path.splitext(f90File)[0] + cfg['SYSTEM']['SFX_OBJ'] #> default executable name exeFile = path.join(path.join(cfg['MODULES'][codeName]['path'],cfgName),codeName+cfg['TELVER']+cfg['SYSTEM']['SFX_EXE']) #> user defined executable name useFile = exeFile value,defaut = getKeyWord('FICHIER FORTRAN',cas,dico,frgb) if value != []: useFile = path.join(CASDir,path.splitext(value[0])[0]+cfg['SYSTEM']['SFX_EXE']) if path.exists(useFile) and cfg['REBUILD'] > 0: remove(useFile) #> default command line compilation and linkage if not path.exists(path.join(path.join(cfg['MODULES'][codeName]['path'],cfgName),codeName+cfg['TELVER']+'.cmdo')): print '\nNot able to find your OBJECT command line: ' + path.join(cfgName,codeName+cfg['TELVER']+'.cmdo') + '\n' print ' ... you have to compile this module at least: ' print ' +> ',codeName sys.exit() objCmd = getFileContent(path.join(path.join(cfg['MODULES'][codeName]['path'],cfgName),codeName+cfg['TELVER']+'.cmdo'))[0] if not path.exists(path.join(path.join(cfg['MODULES'][codeName]['path'],cfgName),codeName+cfg['TELVER']+'.cmdx')): print '\nNot able to find your OBJECT command line: ' + path.join(cfgName,codeName+cfg['TELVER']+'.cmdx') + '\n' print ' ... you have to compile this module at least: ' print ' +> ',codeName sys.exit() exeCmd = getFileContent(path.join(path.join(cfg['MODULES'][codeName]['path'],cfgName),codeName+cfg['TELVER']+'.cmdx'))[0] # >>> Compiling the executable if required if not processExecutable(useFile,objFile,f90File,objCmd,exeCmd,CASDir): sys.exit() # >>> Rename executable because of firewall issues ~~~~~~~~~~~~~~ runCmd = path.join(TMPDir,'out_'+path.basename(useFile)) shutil.move(path.basename(useFile),runCmd) if not options.compileonly: # ~~ Handling the parallelisation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ncsize = getNCSIZE(cas,dico,frgb) if ncsize > 1: # >>> MPI configuration # ~~> Executable /!\ you need one if ncsize > 1 mpiCmd = '' if cfg.has_key('MPI'): if cfg['MPI'].has_key('EXEC'): mpiCmd = cfg['MPI']['EXEC'] if mpiCmd == '': print '... I do not know how to run MPI, can you help ?' return None # /!\ should you stop or carry on ? # ~~> Assign the mpi_telemac.conf hosts = '' if cfg.has_key('MPI'): if cfg['MPI'].has_key('HOSTS'): hosts = cfg['MPI']['HOSTS'] # ~~> MPI Command line mpiCmd = mpiCmd.replace('<wdir>','-wdir '+TMPDir) # /!\ Make sure TMPDir works in UNC convention mpiCmd = mpiCmd.replace('<ncsize>','-n '+str(ncsize)) mpiCmd = mpiCmd.replace('<hosts>',hosts) mpiCmd = mpiCmd.replace('<exename>',runCmd) runCmd = mpiCmd # >>> Parallel tools # ~~> Default path PARDir = path.join(cfg['MODULES']['parallel']['path'],cfgName) # ~~> User path if cfg.has_key('PARALLEL'): if cfg['PARALLEL'].has_key('PATH'): PARDir = cfg['PARALLEL']['PATH'].replace('<root>',cfg['TELDIR']).replace('<config>',path.join(cfg['MODULES']['parallel']['path'],cfgName)) # ~~> Creating PARA file and the mpi_telemac.conf processPARALLEL(ncsize,TMPDir+sep) # /!\ Make sure TMPDir works in UNC convention # >>> Running the partionning if ncsize > 1: # ~~> PARTEL Executable exeCmd = path.join(PARDir,'partel'+cfg['SYSTEM']['SFX_EXE']) # ~~> Run PARTEL CONLIM = getCONLIM(cas,iFS) # no check on existence runPartition(exeCmd,cas,CONLIM,iFS,ncsize) for mod in COUPLAGE.keys(): CONLIM = getCONLIM(COUPLAGE[mod]['cas'],COUPLAGE[mod]['iFS']) runPartition(exeCmd,COUPLAGE[mod]['cas'],CONLIM,COUPLAGE[mod]['iFS'],ncsize) # >>> Running the Executable ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ print runCmd if not runCode(runCmd,sortiefile): sys.exit() # >>> Handling the recollection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if ncsize > 1: # ~~> GRETEL Executable exeCmd = path.join(PARDir,'gretel_autop'+cfg['SYSTEM']['SFX_EXE']) # ~~> Run GRETEL GLOGEO = getGLOGEO(cas,iFS) # no check on existence runRecollection(exeCmd,cas,GLOGEO,oFS,ncsize) for mod in COUPLAGE.keys(): GLOGEO = getGLOGEO(COUPLAGE[mod]['cas'],COUPLAGE[mod]['iFS']) runRecollection(exeCmd,COUPLAGE[mod]['cas'],GLOGEO,COUPLAGE[mod]['oFS'],ncsize) # ~~ Handling all output files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if not processECR(cas,oFS,CASDir,TMPDir,sortiefile,ncsize): sys.exit() for mod in COUPLAGE.keys(): if not processECR(COUPLAGE[mod]['cas'],COUPLAGE[mod]['oFS'],CASDir,TMPDir,'',ncsize): sys.exit() # ~~ Handling Directories ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ chdir(CASDir) if options.tmpdirectory or options.compileonly: removeDirectories(TMPDir) return sortiefile