Coverage for wrainfo/process_chains.py: 69%

109 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-10-18 12:40 +0000

1"""Process chains module.""" 

2 

3# WRaINfo, Is a software to process FURUNO weather radar data. 

4# 

5# Copyright (c) 2022, FernLab (GFZ Potsdam, fernlab@gfz-potsdam.de) 

6# 

7# This software was developed within the context of the RaINfo ("Potential use of 

8# high resolution weather data in agriculture") project of FernLab funded by 

9# the Impulse and Networking Fund of the Helmholtz Association. 

10# 

11# Licensed under the Apache License, Version 2.0 (the "License"); 

12# you may not use this file except in compliance with the License. 

13# 

14# You may obtain a copy of the License at 

15# http://www.apache.org/licenses/LICENSE-2.0 

16# 

17# Unless required by applicable law or agreed to in writing, software 

18# distributed under the License is distributed on an "AS IS" BASIS, 

19# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

20# See the License for the specific language governing permissions and 

21# limitations under the License. 

22 

23import logging 

24import os 

25import glob 

26import xarray as xr 

27import pathlib 

28import datetime as dt 

29import numpy as np 

30import wrainfo 

31 

32 

33# clutter map will be calculated monthly every first day of the month 

34# ------------------------------------------------------------------- 

35 

36def clutter_chain(start_time, 

37 path, 

38 elevation_angle="dataset1", 

39 days=90, 

40 threshold=0, 

41 status=True, 

42 pattern="_000.scnx.gz", 

43 data_type="cmap", 

44 logger=logging.getLogger()): 

45 """Create a ground clutter map and save NetCDF-file. 

46 

47 Parameters 

48 ---------- 

49 start_time : datetime.datetime 

50 start time which files will be read 

51 path : str 

52 path to configuration file 

53 elevation_angle : str 

54 choose dataset of one elevation angle 

55 (for example: dataset 1 is elevation angle 0.5°) 

56 days : integer 

57 Number of days for which a cmap is created 

58 threshold : float 

59 this is a threshold for the DBZH it should not be negative 

60 status : bool 

61 percentage of the finished clutter map 

62 pattern : str 

63 extension of the scnx files 

64 (scnx file: elevation angle 0.5° = "_000.scnx") 

65 data_type : str 

66 the clutter map (cmap) were created 

67 logger : file 

68 writing status messages to a file 

69 

70 Returns 

71 ------- 

72 : files 

73 NetCDF - files in output directory. 

74 """ 

75 # read output directory for clutter map from configuration file 

76 clutter_directory = wrainfo.reader.read_config_file(path=path, 

77 selection="monthly_clutter_directory") 

78 radar_location_identifier = wrainfo.reader.read_config_file(path=path, 

79 selection="radar_location_identifier") 

80 

81 # create the stop time 

82 # ----------------------- 

83 

84 stop_time = start_time + dt.timedelta(days=days) 

85 

86 # create a list of all input files for the defined time period 

87 # --------------------------------------------------------------- 

88 

89 flist = wrainfo.reader.create_filelist(path=path, 

90 starttime=start_time, 

91 endtime=stop_time, 

92 pattern=pattern) 

93 

94 # create clutter map 

95 # --------------------- 

96 logger.info("Creating cluttermap.") 

97 cmap = wrainfo.clutter.create_clutter_map_sequential(files=flist, 

98 threshold=threshold, 

99 grp=elevation_angle, 

100 status=status, 

101 logger=logger.getChild("wr_furuno.clutter" 

102 ".create_clutter_map_sequential")) 

103 

104 # elevation angle 

105 # ------------------ 

106 el = cmap.elevation.values[0] 

107 

108 # create outfilename 

109 # --------------------- 

110 outfilename = f"{radar_location_identifier}_{start_time:%Y%m%d}_{stop_time:%Y%m%d}_elev_{el:0.1f}_{data_type}.nc" 

111 year = str(stop_time.year) 

112 path1 = clutter_directory + "/" + year 

113 

114 if not os.path.exists(path1): 

115 os.makedirs(path1, exist_ok=True) 

116 # overwrite/remove if exist 

117 f = pathlib.Path(outfilename) 

118 f.unlink(missing_ok=True) 

119 

120 outfilename = os.path.join(path1, outfilename) 

121 

122 # output file as netcdf 

123 # ------------------------ 

124 logger.info("Creating cluttermap netcdf output.") 

125 print(f"-- output to {outfilename}") 

126 cmap.to_netcdf(outfilename) 

127 logger.info("Finished cluttermap chain.") 

128 

129 return True 

130 

131 

132# static clutter map will be processed each month from 3 last cluttermaps 

133# ----------------------------------------------------------------------- 

134 

135def static_cmap(path, 

136 pattern="_elev_0.5_cmap.nc"): 

137 """Create a static ground clutter map and save NetCDF-file. 

138 

139 Parameters 

140 ---------- 

141 path : str 

142 path to configuration file 

143 pattern : str 

144 extension of the netcdf file 

145 (cmaps for different elevation angles) 

146 

147 Returns 

148 ------- 

149 : files 

150 NetCDF - files in output directory. 

151 """ 

152 # read path of clutter directory from configuration file 

153 path_cmap = wrainfo.reader.read_config_file(path=path, 

154 selection="monthly_clutter_directory") 

155 path_cmap = path_cmap + "*" 

156 

157 # read radar location identifier from configuration file 

158 radar_location_identifier = wrainfo.reader.read_config_file(path=path, 

159 selection="radar_location_identifier") 

160 

161 # read output directory from configuration file 

162 outdir = wrainfo.reader.read_config_file(path=path, 

163 selection="static_clutter_directory") 

164 

165 # load the last 3 cmaps 

166 cmaps = sorted(glob.glob(os.path.join(path_cmap, "*"))) 

167 

168 # filter for elevation angle 

169 cmaps_filter = [] 

170 

171 for cmap in cmaps: 

172 if cmap.endswith(pattern): 

173 cmaps_filter.append(cmap) 

174 cmaps_filter = cmaps_filter[-3:] 

175 

176 # read the last 3 cmaps 

177 cmap1 = xr.open_dataset(cmaps_filter[0]) 

178 cmap2 = xr.open_dataset(cmaps_filter[1]) 

179 cmap3 = xr.open_dataset(cmaps_filter[2]) 

180 

181 # add cmaps 

182 cmap_final = cmap1 + cmap2 + cmap3 

183 

184 # outfilename 

185 base_name = os.path.basename(cmaps_filter[1]) 

186 el = base_name[28:36] 

187 

188 outfilename = f"{radar_location_identifier}_static_cluttermap_{el}.nc" 

189 

190 # output directory 

191 if not os.path.exists(outdir): 

192 os.makedirs(outdir, exist_ok=True) 

193 # overwrite/remove if exist 

194 f = pathlib.Path(outfilename) 

195 f.unlink(missing_ok=True) 

196 

197 outfilename = os.path.join(outdir, outfilename) 

198 

199 print(f"-- output to {outfilename}") 

200 cmap_final.to_netcdf(outfilename) 

201 

202 return True 

203 

204 

205# weather radar routine 

206# --------------------- 

207 

208def wr_routine_furuno(starttime, 

209 endtime, 

210 delta, 

211 path, 

212 data_type="Level2a", 

213 elevation_angle="dataset1", 

214 pattern="_000.scnx.gz", 

215 logger=logging.getLogger()): 

216 """Process of Furuno raw data. 

217 

218 Parameters 

219 ---------- 

220 starttime : datetime.datetime 

221 endtime : datetime.datetime 

222 delta : datetime.delta 

223 path : str 

224 path to configuration file 

225 data_type : str 

226 Level of processing 

227 pattern : extension of the scnx file 

228 (scnx file: elevation angle 0.5° = "_000.scnx") 

229 elevation_angle : str 

230 group of hdf5 dataset 

231 logger : file 

232 writing status messages to a file 

233 

234 Returns 

235 ------- 

236 : files 

237 NetCDF - files in output directory 

238 """ 

239 date = starttime 

240 stop_time = endtime 

241 

242 # read settings from configuration file 

243 re_idx = wrainfo.reader.read_config_file(path=path, 

244 selection="re_index_parameters") 

245 

246 re_index_parameters = np.arange(re_idx[0], re_idx[1], re_idx[2]) 

247 

248 dims = wrainfo.reader.read_config_file(path=path, 

249 selection="dimensions") 

250 

251 while date < stop_time: 

252 

253 # read static cmap 

254 # ---------------- 

255 

256 logger.info("Read static clutter map") 

257 clutter_dircetory = wrainfo.reader.read_config_file(path=path, 

258 selection="static_clutter_directory") 

259 cmap = xr.open_dataset(clutter_dircetory) 

260 cmap = cmap.DBZH_sum_bool 

261 

262 # load files 

263 # ---------- 

264 logger.info("Create list of files to be processed") 

265 flist_raw = wrainfo.reader.create_filelist(path=path, 

266 starttime=date, 

267 endtime=date + delta, 

268 pattern=pattern) 

269 

270 # start processing 

271 # ---------------- 

272 for file in flist_raw: 

273 

274 # extension of the file 

275 # --------------------- 

276 extension = os.path.splitext(file)[1] 

277 

278 if extension == ".gz": 

279 

280 # read Furuno scnx data 

281 # --------------------- 

282 logger.info("Read Furuno scnx file") 

283 try: 

284 ds = xr.open_dataset(file, 

285 engine="furuno", 

286 group=1, 

287 backend_kwargs=dict(reindex_angle=1.0)) 

288 

289 except Exception: 

290 # except errors if a file could not read 

291 logger.warning("Could not load scnx file:' " + file + "'.") 

292 continue 

293 

294 # rename VRADH to VRAD 

295 ds['VRAD'] = ds['VRADH'] 

296 ds = ds.drop(['VRADH']) 

297 

298 if extension == ".h5": 

299 

300 # read Furuno hdf5 data 

301 # --------------------- 

302 logger.info("Read Furuno hdf5 file") 

303 try: 

304 ds = xr.open_dataset(file, 

305 engine="odim", 

306 group=elevation_angle, 

307 backend_kwargs=dict(reindex_angle=1.0)) 

308 

309 except Exception: 

310 # except errors if a file could not read 

311 logger.warning("Could not load hdf5 file:' " + file + "'.") 

312 continue 

313 

314 # reindexing azimuth resolution 

315 # ----------------------------- 

316 ds = ds.reindex(azimuth=re_index_parameters, 

317 method="nearest") 

318 

319 # fix errors by elevation angle 

320 # ----------------------------- 

321 elevation_angle_ds = ds.elevation.median(dim="azimuth") 

322 ds["elevation"][:] = elevation_angle_ds 

323 

324 # SNR filter by RHOHV 

325 # -------------------- 

326 

327 ds = ds.where(ds.RHOHV > 0.9) 

328 

329 # remove static clutter with cmap 

330 # -------------------------------- 

331 

332 dbzh = ds.DBZH.where((cmap == False)) # noqa E712 

333 

334 dbzh_attrs = ds.DBZH.attrs 

335 ds["DBZH"] = xr.DataArray(dbzh, 

336 dims=dims, 

337 attrs=dbzh_attrs) 

338 

339 # clutter detection 

340 # -------------------- 

341 logger.info("Clutter classification.") 

342 ds = wrainfo.clutter.fuzzy_echo_classification(ds, 

343 cmap=cmap, 

344 dims=dims) 

345 

346 # set clutter pixel to NA 

347 ds = wrainfo.clutter.dbzh_no_clutter(ds) 

348 

349 # phase processing & attenuation correction (ZPHI method) 

350 # ------------------------------------------------------- 

351 logger.info("Phase processing and attenuation correction.") 

352 ds = wrainfo.attenuation_corr.attenuation_correction(ds, 

353 moment="DBZH_no_clutter", 

354 dims=dims) 

355 

356 # rain rate retreival 

357 # -------------------- 

358 logger.info("Create precipitation products.") 

359 

360 # QPE Z(R) 

361 logger.info("Create precipitation product with Z(R) relationship.") 

362 ds = wrainfo.precipitation.qpe_zr(ds, 

363 "DBZH_CORR", 

364 path=path, 

365 a=200, 

366 b=1.6) 

367 

368 # smoothing 

369 ds = ds.pad(azimuth=(3, 3), 

370 mode="wrap").rolling(azimuth=7, 

371 min_periods=3, 

372 center=True).mean().isel(azimuth=slice(3, -3)) 

373 

374 # georeferencing and gridding 

375 # --------------------------- 

376 logger.info("Georeferencing and gridding") 

377 

378 swp_georef = wrainfo.geometry.furuno_georeferencing(ds, 

379 path=path) 

380 

381 wrainfo.geometry.furuno_sweep_to_netcdf(swp_georef, 

382 path=path, 

383 data_type=data_type) 

384 del ds 

385 del swp_georef 

386 

387 date += delta 

388 

389 return True