2 #***********************************************************************
3 #* GNU Lesser General Public License
5 #* This file is part of the GFDL Flexible Modeling System (FMS).
7 #* FMS is free software: you can redistribute it and/or modify it under
8 #* the terms of the GNU Lesser General Public License as published by
9 #* the Free Software Foundation, either version 3 of the License, or (at
10 #* your option) any later version.
12 #* FMS is distributed in the hope that it will be useful, but WITHOUT
13 #* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 #* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 #* You should have received a copy of the GNU Lesser General Public
18 #* License along with the FV3 dynamical core.
19 #* If not, see <http://www.gnu.org/licenses/>.
20 #***********************************************************************
22 VERSION
= "_FILE_VERSION"
24 from Scientific
.IO
.NetCDF
import NetCDFFile
29 from optparse
import OptionParser
32 class drifters_combine
:
34 def __init__(self
, filenames
):
41 self
.position_atts
= {}
45 nc
= NetCDFFile(f
, 'r')
46 index_time
= nc
.variables
['index_time'][:]
47 time
= nc
.variables
['time'][:]
48 ids
= nc
.variables
['ids'][:]
50 positions
= nc
.variables
['positions'][:]
51 nd
= Num
.shape(positions
)[1]
53 # get global attributes
55 self
.global_atts
[a
] = nc
.__dict
__[a
]
58 for a
in nc
.variables
['positions'].__dict
__:
59 if not self
.position_atts
.has_key(a
):
60 self
.position_atts
[a
] = getattr(nc
.variables
['positions'], a
)
64 if 'fields' in nc
.variables
:
65 fields
= nc
.variables
['fields'][:]
66 nf
= Num
.shape(fields
)[1]
67 for a
in nc
.variables
['fields'].__dict
__:
68 if not self
.field_atts
.has_key(a
):
69 self
.field_atts
[a
] = getattr(nc
.variables
['fields'], a
)
71 if self
.nf
==None: self
.nf
= nf
72 if self
.nd
==None: self
.nd
= nd
74 if nf
!= self
.nf
or nd
!= self
.nd
:
75 raise 'Incompatible number of fields (nf) or space dimensions (nd) in file'% f
77 for i
in range(len(ids
)):
83 if nf
>0: fld
= fields
[i
,:]
85 if not self
.data
.has_key(it
):
88 if not self
.data
[it
].has_key(id):
89 self
.data
[it
][id] = (tim
,) + tuple(xyz
) + tuple(fld
)
91 def save(self
, outfile
):
93 nc
= NetCDFFile(outfile
,'w')
95 nc
.createDimension('it_id', None)
96 nc
.createDimension('nd', self
.nd
)
98 nc
.createDimension('nf', self
.nf
)
100 nc_index_time
= nc
.createVariable('index_time', Num
.Int
, ('it_id',))
101 nc_time
= nc
.createVariable('time', Num
.Float64
, ('it_id',))
102 nc_ids
= nc
.createVariable('ids', Num
.Int
, ('it_id',))
103 nc_positions
= nc
.createVariable('positions', Num
.Float64
, \
106 for a
in self
.global_atts
:
107 setattr(nc
, a
, self
.global_atts
[a
])
109 for a
in self
.position_atts
:
110 setattr(nc_positions
, a
, self
.position_atts
[a
])
113 nc_fields
= nc
.createVariable('fields', Num
.Float64
, \
115 for a
in self
.field_atts
:
116 setattr(nc_fields
, a
, self
.field_atts
[a
])
119 for it
in range(len(self
.data
)):
121 for id in self
.data
[it
]:
123 nc_index_time
[k
] = it
124 nc_time
[k
] = self
.data
[it
][id][0]
126 nc_positions
[k
:k
+1,:]= self
.data
[it
][id][1:1+self
.nd
]
128 nc_fields
[k
:k
+1,:]= self
.data
[it
][id][1+self
.nd
:1+self
.nd
+self
.nf
]
134 ###############################################################################
140 parser
= OptionParser(version
=VERSION
)
141 parser
.add_option('-p', '--pe_range', action
='store', type="string",
143 help='PE range (e.g. "10...20"). By default the output of all PEs will be combined.',
146 parser
.add_option('-f', '--file', action
='store', type="string",
148 help='Input file. Files FILE.XXXX to FILE.YYYY will be combined.',
149 default
='drifters_out.nc',
151 parser
.add_option('-o', '--output', action
='store', type="string",
153 help='The combined files will be saved as OUTPUT. By default OUTPUT=FILE.',
157 options
, args
= parser
.parse_args(sys
.argv
)
158 fname
= options
.filename
160 print 'ERROR: must supply a netcdf file name ([-f|--file] FILE).'
165 p1
= re
.search(r
'^\s*(\d+)\s*\.', options
.pe_range
)
166 p2
= re
.search(r
'\.\s*(\d+)\s*$', options
.pe_range
)
171 print 'ERROR: pe range should be in format [-p|--pe_range] "min_pe..max_pe".'
174 fnames
= [fname
+ '.' + str(pe
).zfill(4) for pe
in range(int(min_pe
), int(max_pe
)+1)]
177 fnames
= glob
.glob(fname
+ '.' + '????')
179 vfc
= drifters_combine(fnames
)
181 ofile
= options
.output
187 if __name__
=='__main__': main()