Changeset 991df6a in flex_extract.git for python/ECFlexpart.py
- Timestamp:
- May 14, 2018, 10:11:29 PM (6 years ago)
- Branches:
- master, ctbto, dev
- Children:
- 812283d
- Parents:
- efdb01a
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
python/ECFlexpart.py
refdb01a r991df6a 3 3 #************************************************************************ 4 4 # TODO AP 5 #AP6 5 # - specifiy file header documentation 6 # - add class description in header information 7 7 # - apply classtests 8 8 # - add references to ECMWF specific software packages 9 # - add describtion of deacc_fluxes 10 # - change name of func deacc ( weil disagg ) 11 # - add desc of retrieve function 9 12 #************************************************************************ 10 """ 11 @Author: Anne Fouilloux (University of Oslo) 12 13 @Date: October 2014 14 15 @ChangeHistory: 16 November 2015 - Leopold Haimberger (University of Vienna): 17 - extended with Class Control 18 - removed functions mkdir_p, daterange, years_between, months_between 19 - added functions darain, dapoly, toparamId, init128, normalexit, 20 myerror, cleanup, install_args_and_control, 21 interpret_args_and_control, 22 - removed function __del__ in class EIFLexpart 23 - added the following functions in EIFlexpart: 24 - create_namelist 25 - process_output 26 - deacc_fluxes 27 - modified existing EIFlexpart - functions for the use in 28 flex_extract 29 - retrieve also longer term forecasts, not only analyses and 30 short term forecast data 31 - added conversion into GRIB2 32 - added conversion into .fp format for faster execution of FLEXPART 33 (see https://www.flexpart.eu/wiki/FpCtbtoWo4FpFormat) 34 35 February 2018 - Anne Philipp (University of Vienna): 36 - applied PEP8 style guide 37 - added documentation 38 - outsourced class Control 39 - outsourced class MarsRetrieval 40 - changed class name from EIFlexpart to ECFlexpart 41 - applied minor code changes (style) 42 43 @License: 44 (C) Copyright 2014-2018. 45 46 This software is licensed under the terms of the Apache Licence Version 2.0 47 which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 48 49 @Requirements: 50 - A standard python 2.6 or 2.7 installation 51 - dateutils 52 - matplotlib (optional, for debugging) 53 - ECMWF specific packages, all available from https://software.ecmwf.int/ 54 ECMWF WebMARS, gribAPI with python enabled, emoslib and 55 ecaccess web toolkit 56 57 @Description: 58 Further documentation may be obtained from www.flexpart.eu. 59 60 Functionality provided: 61 Prepare input 3D-wind fields in hybrid coordinates + 62 surface fields for FLEXPART runs 63 """ 13 #******************************************************************************* 14 # @Author: Anne Fouilloux (University of Oslo) 15 # 16 # @Date: October 2014 17 # 18 # @Change History: 19 # 20 # November 2015 - Leopold Haimberger (University of Vienna): 21 # - extended with class Control 22 # - removed functions mkdir_p, daterange, years_between, months_between 23 # - added functions darain, dapoly, toparamId, init128, normalexit, 24 # myerror, cleanup, install_args_and_control, 25 # interpret_args_and_control, 26 # - removed function __del__ in class EIFLexpart 27 # - added the following functions in EIFlexpart: 28 # - create_namelist 29 # - process_output 30 # - deacc_fluxes 31 # - modified existing EIFlexpart - functions for the use in 32 # flex_extract 33 # - retrieve also longer term forecasts, not only analyses and 34 # short term forecast data 35 # - added conversion into GRIB2 36 # - added conversion into .fp format for faster execution of FLEXPART 37 # (see https://www.flexpart.eu/wiki/FpCtbtoWo4FpFormat) 38 # 39 # February 2018 - Anne Philipp (University of Vienna): 40 # - applied PEP8 style guide 41 # - added documentation 42 # - removed function getFlexpartTime in class ECFlexpart 43 # - outsourced class ControlFile 44 # - outsourced class MarsRetrieval 45 # - changed class name from EIFlexpart to ECFlexpart 46 # - applied minor code changes (style) 47 # - removed "dead code" , e.g. retrieval of Q since it is not needed 48 # - removed "times" parameter from retrieve-method since it is not used 49 # 50 # @License: 51 # (C) Copyright 2014-2018. 52 # 53 # This software is licensed under the terms of the Apache Licence Version 2.0 54 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 55 # 56 # @Class Description: 57 # FLEXPART needs grib files in a specifc format. All necessary data fields 58 # for one time step are stored in a single file. The class represents an 59 # instance with all the parameter and settings necessary for retrieving 60 # MARS data and modifing them so they are fitting FLEXPART need. The class 61 # is able to disaggregate the fluxes and convert grid types to the one needed 62 # by FLEXPART, therefore using the FORTRAN program. 63 # 64 # @Class Content: 65 # - __init__ 66 # - write_namelist 67 # - retrieve 68 # - process_output 69 # - create 70 # - deacc_fluxes 71 # 72 #******************************************************************************* 73 64 74 # ------------------------------------------------------------------------------ 65 75 # MODULES 66 76 # ------------------------------------------------------------------------------ 67 77 import subprocess 68 from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter69 import traceback70 78 import shutil 71 79 import os 72 import errno73 80 import sys 74 81 import inspect 75 82 import glob 76 83 import datetime 77 from string import atoi78 84 from numpy import * 85 from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter 79 86 ecapi = True 80 87 try: … … 83 90 ecapi = False 84 91 from gribapi import * 92 93 # software specific classes and modules from flex_extract 85 94 from GribTools import GribTools 86 95 from Tools import init128, toparamId, silentremove, product 87 from Control import Control96 from ControlFile import ControlFile 88 97 from MARSretrieval import MARSretrieval 89 98 import Disagg 99 90 100 # ------------------------------------------------------------------------------ 91 101 # CLASS … … 93 103 class ECFlexpart: 94 104 ''' 95 Class to retrieve ECMWF data specific for running FLEXPART.105 Class to retrieve FLEXPART specific ECMWF data. 96 106 ''' 97 107 # -------------------------------------------------------------------------- 98 108 # CLASS FUNCTIONS 99 109 # -------------------------------------------------------------------------- 100 def __init__(self, c, fluxes=False): #done/ verstehen110 def __init__(self, c, fluxes=False): 101 111 ''' 102 112 @Description: … … 108 118 The current object of the class. 109 119 110 c: instance of class Control 111 Contains all the parameters of control files, which are e.g.:120 c: instance of class ControlFile 121 Contains all the parameters of CONTROL file, which are e.g.: 112 122 DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME, 113 123 STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT, … … 130 140 ''' 131 141 132 # different mars types for retrieving reanalysisdata for flexpart142 # different mars types for retrieving data for flexpart 133 143 self.types = dict() 134 144 try: … … 147 157 self.basetime = c.basetime 148 158 self.dtime = c.dtime 149 #self.mars = {}150 159 i = 0 151 #j = 0152 160 if fluxes is True and c.maxstep < 24: 153 161 # no forecast beyond one day is needed! 154 162 # Thus, prepare flux data manually as usual 155 # with only FCfields with start times at 00/12163 # with only forecast fields with start times at 00/12 156 164 # (but without 00/12 fields since these are 157 165 # the initialisation times of the flux fields 158 166 # and therefore are zero all the time) 159 self.types['FC'] = {'times': '00/12', 160 'steps': '{}/to/12/by/{}'.format(c.dtime, 161 c.dtime)} 162 #i = 1 163 #for k in [0, 12]: 164 # for j in range(int(c.dtime), 13, int(c.dtime)): 165 # self.mars['{:0>3}'.format(i * int(c.dtime))] = \ 166 # [c.type[1], '{:0>2}'.format(k), '{:0>3}'.format(j)] 167 # i += 1 167 self.types[c.type[1]] = {'times': '00/12', 'steps': 168 '{}/to/12/by/{}'.format(c.dtime, c.dtime)} 168 169 else: 169 170 for ty, st, ti in zip(c.type, c.step, c.time): … … 189 190 self.types[ty]['steps'] += '/' 190 191 self.types[ty]['steps'] += st 191 192 #self.mars['{:0>3}'.format(j)] = [ty,193 # '{:0>2}'.format(int(ti)),194 # '{:0>3}'.format(int(st))]195 #j += int(c.dtime)196 197 192 i += 1 198 print 'EC init: ', self.types #AP 193 199 194 # Different grids need different retrievals 200 195 # SH = Spherical Harmonics, GG = Gaussian Grid, … … 274 269 self.params['OG__ML'][0] += '/U/V/77' 275 270 elif c.gauss == '0' and c.eta == '0': 276 #AP then remove?!?!?!?# this is not recommended (inaccurate)271 # this is not recommended (inaccurate) 277 272 self.params['OG__ML'][0] += '/U/V' 278 273 elif c.gauss == '1' and c.eta == '0': … … 324 319 325 320 326 def write_namelist(self, c, filename): #done321 def write_namelist(self, c, filename): 327 322 ''' 328 323 @Description: … … 336 331 The current object of the class. 337 332 338 c: instance of class Control 339 Contains all the parameters of controlfiles, which are e.g.:333 c: instance of class ControlFile 334 Contains all the parameters of CONTROL files, which are e.g.: 340 335 DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME, 341 336 STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT, … … 383 378 return 384 379 385 def retrieve(self, server, dates, times, inputdir=''):380 def retrieve(self, server, dates, inputdir='.'): 386 381 ''' 387 382 @Description: 388 383 Finalizing the retrieval information by setting final details 384 depending on grid type. 385 Prepares MARS retrievals per grid type and submits them. 389 386 390 387 @Input: 391 388 self: instance of ECFlexpart 392 393 server: instance of ECMWFService 394 395 dates: 396 397 times: 389 The current object of the class. 390 391 server: instance of ECMWFService or ECMWFDataServer 392 The connection to the ECMWF server. This is different 393 for member state users which have full access and non 394 member state users which have only access to the public 395 data sets. The decision is made from command line argument 396 "public"; for public access its True (ECMWFDataServer) 397 for member state users its False (ECMWFService) 398 399 dates: string 400 Contains start and end date of the retrieval in the format 401 "YYYYMMDD/to/YYYYMMDD" 398 402 399 403 inputdir: string, optional 400 Default string is empty (''). 404 Path to the directory where the retrieved data is about 405 to be stored. The default is the current directory ('.'). 401 406 402 407 @Return: … … 405 410 self.dates = dates 406 411 self.server = server 407 408 if inputdir == "": 409 self.inputdir = '.' 410 else: 411 self.inputdir = inputdir 412 413 # Retrieve Q not for using Q but as a template for a reduced gaussian 414 # grid one date and time is enough 415 # Take analysis at 00 416 qdate = self.dates 417 idx = qdate.find("/") 418 if (idx > 0): 419 qdate = self.dates[:idx] 420 421 #QG = MARSretrieval(self.server, marsclass = self.marsclass, stream = self.stream, type = "an", levtype = "ML", levelist = "1", 422 #gaussian = "reduced",grid = '{}'.format((int(self.resol)+1)/2), resol = self.resol,accuracy = self.accuracy,target = self.inputdir+"/"+"QG.grb", 423 #date = qdate, time = "00",expver = self.expver, param = "133.128") 424 #QG.displayInfo() 425 #QG.dataRetrieve() 426 412 self.inputdir = inputdir 427 413 oro = False 414 428 415 for ftype in self.types: 429 416 for pk, pv in self.params.iteritems(): … … 453 440 else: 454 441 continue 455 456 442 if pk == 'GG__SL' and pv[0] == 'Q': 457 443 area = "" … … 461 447 gaussian = self.gaussian 462 448 449 # ------ on demand path -------------------------------------------------- 463 450 if self.basetime is None: 464 451 MR = MARSretrieval(self.server, … … 473 460 MR.displayInfo() 474 461 MR.dataRetrieve() 475 # The whole else section is only necessary for operational scripts. 476 # It could be removed 462 # ------ operational path ------------------------------------------------ 477 463 else: 478 464 # check if mars job requests fields beyond basetime. 479 465 # If yes eliminate those fields since they may not 480 466 # be accessible with user's credentials 481 sm1 = -1482 467 if 'by' in mfstep: 483 468 sm1 = 2 484 tm1 = -1 469 else: 470 sm1 = -1 471 485 472 if 'by' in mftime: 486 473 tm1 = 2 474 else: 475 tm1 = -1 476 487 477 maxtime = datetime.datetime.strptime( 488 478 mfdate.split('/')[-1] + mftime.split('/')[tm1], 489 479 '%Y%m%d%H') + datetime.timedelta( 490 480 hours=int(mfstep.split('/')[sm1])) 491 492 481 elimit = datetime.datetime.strptime( 493 482 mfdate.split('/')[-1] + … … 495 484 496 485 if self.basetime == '12': 486 # -------------- flux data ---------------------------- 497 487 if 'acc' in pk: 498 488 499 # Strategy: if maxtime-elimit> = 24h reduce date by 1, 500 # if 12h< = maxtime-elimit<12h reduce time for last date 501 # if maxtime-elimit<12h reduce step for last time 502 # A split of the MARS job into 2 is likely necessary. 503 maxtime = elimit-datetime.timedelta(hours=24) 489 # Strategy: 490 # if maxtime-elimit >= 24h reduce date by 1, 491 # if 12h <= maxtime-elimit<12h reduce time for last date 492 # if maxtime-elimit<12h reduce step for last time 493 # A split of the MARS job into 2 is likely necessary. 494 maxtime = elimit - datetime.timedelta(hours=24) 504 495 mfdate = '/'.join(('/'.join(mfdate.split('/')[:-1]), 505 496 datetime.datetime.strftime( … … 541 532 MR.displayInfo() 542 533 MR.dataRetrieve() 534 # -------------- non flux data ------------------------ 543 535 else: 544 536 MR = MARSretrieval(self.server, … … 555 547 MR.displayInfo() 556 548 MR.dataRetrieve() 557 else: 549 else: # basetime == 0 ??? #AP 550 558 551 maxtime = elimit - datetime.timedelta(hours=24) 559 552 mfdate = datetime.datetime.strftime(maxtime,'%Y%m%d') 560 561 553 mftimesave = ''.join(mftime) 562 554 … … 615 607 616 608 617 def process_output(self, c): #done609 def process_output(self, c): 618 610 ''' 619 611 @Description: 620 The grib files are postprocessed depending on selection in621 controlfile. The resulting files are moved to the output612 The grib files are postprocessed depending on the selection in 613 CONTROL file. The resulting files are moved to the output 622 614 directory if its not equla to the input directory. 623 615 The following modifications might be done if 624 properly switched in controlfile:616 properly switched in CONTROL file: 625 617 GRIB2 - Conversion to GRIB2 626 618 ECTRANS - Transfer of files to gateway server … … 632 624 The current object of the class. 633 625 634 c: instance of class Control 635 Contains all the parameters of control files, which are e.g.:626 c: instance of class ControlFile 627 Contains all the parameters of CONTROL file, which are e.g.: 636 628 DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME, 637 629 STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT, … … 650 642 ''' 651 643 652 print(' Postprocessing:\n Format: {}\n'.format(c.format))644 print('\n\nPostprocessing:\n Format: {}\n'.format(c.format)) 653 645 654 646 if c.ecapi is False: … … 675 667 for ofile in self.outputfilelist: 676 668 p = subprocess.check_call(['ectrans', '-overwrite', '-gateway', 677 c.gateway, '-remote', c.destination,678 '-source', ofile])669 c.gateway, '-remote', c.destination, 670 '-source', ofile]) 679 671 print('ectrans:', p) 680 672 … … 682 674 for ofile in self.outputfilelist: 683 675 p = subprocess.check_call(['ecp', '-o', ofile, 684 os.path.expandvars(c.ecfsdir)])676 os.path.expandvars(c.ecfsdir)]) 685 677 686 678 if c.outputdir != c.inputdir: … … 693 685 694 686 # generate AVAILABLE file 695 # Example of AVAILABLE file data 687 # Example of AVAILABLE file data: 696 688 # 20131107 000000 EN13110700 ON DISC 697 689 clist = [] … … 742 734 i += 1 743 735 744 # clist.sort() 745 # insert the date and time information of run star and end 736 # insert the date and time information of run start and end 746 737 # into the list of lines of COMMAND file 747 738 lflist = lflist[:i+1] + \ … … 753 744 g.write('\n'.join(lflist) + '\n') 754 745 755 # change to outputdir and start the 756 # grib2flexpart run 746 # change to outputdir and start the grib2flexpart run 757 747 # afterwards switch back to the working dir 758 748 os.chdir(c.outputdir) … … 765 755 return 766 756 767 def create(self, inputfiles, c): #done757 def create(self, inputfiles, c): 768 758 ''' 769 759 @Description: … … 773 763 An index file will be created which depends on the combination 774 764 of "date", "time" and "stepRange" values. This is used to iterate 775 over all messages in the grib files passed through the parameter776 "inputfiles" to seperate specific parameters into fort.* files.777 Afterwards the FORTRAN program Convert2 is called to convert765 over all messages in each grib file which were passed through the 766 parameter "inputfiles" to seperate specific parameters into fort.* 767 files. Afterwards the FORTRAN program Convert2 is called to convert 778 768 the data fields all to the same grid and put them in one file 779 per day. 769 per unique time step (combination of "date", "time" and 770 "stepRange"). 780 771 781 772 @Input: … … 786 777 Contains a list of files. 787 778 788 c: instance of class Control 789 Contains all the parameters of controlfiles, which are e.g.:779 c: instance of class ControlFile 780 Contains all the parameters of CONTROL files, which are e.g.: 790 781 DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME, 791 782 STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT, … … 826 817 # index_vals[2]: ('0', '12', '3', '6', '9') ; stepRange 827 818 828 # delete old fort.* files and open them newly829 819 fdict = {'10':None, '11':None, '12':None, '13':None, '16':None, 830 820 '17':None, '19':None, '21':None, '22':None, '20':None} 831 #for f in fdict.keys():832 # silentremove(c.inputdir + "/fort." + f)833 834 821 835 822 for prod in product(*index_vals): 823 # flag for Fortran program CONVERT2, initially False 824 convertFlag = False 836 825 print 'current prod: ', prod 837 826 # e.g. prod = ('20170505', '0', '12') … … 842 831 grib_index_select(iid, index_keys[i], prod[i]) 843 832 833 # get first id from current product 844 834 gid = grib_new_from_index(iid) 845 # do convert2 program if gid at this time is not None, 846 # therefore save in hid847 hid = gid835 836 # if there is data for this product combination 837 # prepare some date and time parameter before reading the data 848 838 if gid is not None: 839 # Fortran program CONVERT2 is only done if gid at this time is 840 # not None, therefore save information in convertFlag 841 convertFlag = True 842 # remove old fort.* files and open new ones 849 843 for k, f in fdict.iteritems(): 850 844 silentremove(c.inputdir + "/fort." + k) … … 900 894 pass 901 895 902 896 # helper variable to remember which fields are already used. 903 897 savedfields = [] 904 898 while 1: … … 924 918 elif paramId == 155 and gridtype == 'sh': 925 919 grib_write(gid, fdict['13']) 926 elif paramId in [129, 138, 155] and levtype == 'hybrid' \927 920 elif paramId in [129, 138, 155] and levtype == 'hybrid' \ 921 and c.wrf == '1': 928 922 pass 929 923 elif paramId == 246 or paramId == 247: … … 949 943 try: 950 944 if c.wrf == '1': 951 # die if abfrage scheint ueberfluessig da eh das gleihce ausgefuehrt wird 952 if levtype == 'hybrid': 945 if levtype == 'hybrid': # model layer 953 946 if paramId in [129, 130, 131, 132, 133, 138, 155]: 954 947 grib_write(gid, fwrf) 955 else: 948 else: # sfc layer 956 949 if paramId in wrfpars: 957 950 grib_write(gid, fwrf) … … 965 958 f.close() 966 959 967 # call for CONVERT2 968 # AUSLAGERN IN EIGENE FUNKTION 969 970 if hid is not None: 960 # call for CONVERT2 if flag is True 961 if convertFlag: 971 962 pwd = os.getcwd() 972 963 os.chdir(c.inputdir) … … 978 969 to 1 in CONTROL file') 979 970 971 # create the corresponding output file fort.15 972 # (generated by CONVERT2) + fort.16 (paramId 167 and 168) 980 973 p = subprocess.check_call([os.path.expandvars( 981 974 os.path.expanduser(c.exedir)) + '/CONVERT2'], shell=True) 982 975 os.chdir(pwd) 983 # create the corresponding output file fort.15 984 # (generated by CONVERT2) 985 # + fort.16 (paramId 167 and paramId 168) 976 977 # create final output filename, e.g. EN13040500 (ENYYMMDDHH) 986 978 fnout = c.inputdir + '/' + c.prefix 987 979 if c.maxstep > 12: … … 990 982 else: 991 983 suffix = cdateH[2:10] 992 993 984 fnout += suffix 994 985 print("outputfile = " + fnout) 995 986 self.outputfilelist.append(fnout) # needed for final processing 996 fout = open(fnout, 'wb') 997 shutil.copyfileobj(open(c.inputdir + '/fort.15', 'rb'), fout) 998 if c.cwc == '1': 999 shutil.copyfileobj(open(c.inputdir + '/fort.22', 'rb'), fout) 1000 shutil.copyfileobj(open(c.inputdir + '/flux' + cdate[0:2] + 1001 suffix, 'rb'), fout) 1002 shutil.copyfileobj(open(c.inputdir + '/fort.16', 'rb'), fout) 1003 orolsm = glob.glob(c.inputdir + 1004 '/OG_OROLSM__SL.*.' + c.ppid + '*')[0] 1005 shutil.copyfileobj(open(orolsm, 'rb'), fout) 1006 fout.close() 987 988 # create outputfile and copy all data from intermediate files 989 # to the outputfile (final GRIB files) 990 orolsm = os.path.basename(glob.glob( 991 c.inputdir + '/OG_OROLSM__SL.*.' + c.ppid + '*')[0]) 992 fluxfile = 'flux' + cdate[0:2] + suffix 993 if c.cwc != '1': 994 flist = ['fort.15', fluxfile, 'fort.16', orolsm] 995 else: 996 flist = ['fort.15', 'fort.22', fluxfile, 'fort.16', orolsm] 997 998 with open(fnout, 'wb') as fout: 999 for f in flist: 1000 shutil.copyfileobj(open(c.inputdir + '/' + f, 'rb'), fout) 1001 1007 1002 if c.omega == '1': 1008 fnout = c.outputdir + '/OMEGA'1009 fout = open(fnout, 'wb')1010 shutil.copyfileobj(open(c.inputdir + '/fort.25', 'rb'), fout)1003 with open(c.outputdir + '/OMEGA', 'wb') as fout: 1004 shutil.copyfileobj( 1005 open(c.inputdir + '/fort.25', 'rb'), fout) 1011 1006 1012 1007 try: … … 1020 1015 return 1021 1016 1022 1023 1017 def deacc_fluxes(self, inputfiles, c): 1024 1018 ''' 1025 1019 @Description: 1026 1020 Goes through all flux fields in ordered time and de-accumulate 1021 the fields. Afterwards the fields are disaggregated in time. 1022 Different versions of disaggregation is provided for rainfall 1023 data (darain, modified linear) and the surface fluxes and 1024 stress data (dapoly, cubic polynomial). 1027 1025 1028 1026 @Input: … … 1033 1031 Contains a list of files. 1034 1032 1035 c: instance of class Control 1036 Contains all the parameters of control files, which are e.g.:1033 c: instance of class ControlFile 1034 Contains all the parameters of CONTROL file, which are e.g.: 1037 1035 DAY1(start_date), DAY2(end_date), DTIME, MAXSTEP, TYPE, TIME, 1038 1036 STEP, CLASS(marsclass), STREAM, NUMBER, EXPVER, GRID, LEFT, … … 1133 1131 g = open(gnout, 'w') 1134 1132 h = open(hnout, 'w') 1133 1135 1134 print("outputfile = " + fnout) 1136 1135 f = open(fnout, 'w')
Note: See TracChangeset
for help on using the changeset viewer.