grdwindinversion 0.2.7__py3-none-any.whl → 0.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- grdwindinversion/config_prod.yaml +6 -0
- grdwindinversion/config_prod_recal.yaml +7 -3
- grdwindinversion/config_prod_recal_streaks_nrcsmod.yaml +48 -0
- grdwindinversion/config_prod_streaks.yaml +52 -0
- grdwindinversion/config_prod_streaks_nrcsmod.yaml +52 -0
- grdwindinversion/data_config.yaml +7 -4
- grdwindinversion/gradientFeatures.py +448 -0
- grdwindinversion/inversion.py +259 -75
- grdwindinversion/load_config.py +7 -4
- grdwindinversion/utils.py +40 -0
- grdwindinversion-0.3.1.dist-info/METADATA +67 -0
- grdwindinversion-0.3.1.dist-info/RECORD +22 -0
- grdwindinversion/streaks.py +0 -79
- grdwindinversion-0.2.7.dist-info/METADATA +0 -83
- grdwindinversion-0.2.7.dist-info/RECORD +0 -19
- {grdwindinversion-0.2.7.dist-info → grdwindinversion-0.3.1.dist-info}/AUTHORS.rst +0 -0
- {grdwindinversion-0.2.7.dist-info → grdwindinversion-0.3.1.dist-info}/LICENSE +0 -0
- {grdwindinversion-0.2.7.dist-info → grdwindinversion-0.3.1.dist-info}/WHEEL +0 -0
- {grdwindinversion-0.2.7.dist-info → grdwindinversion-0.3.1.dist-info}/entry_points.txt +0 -0
- {grdwindinversion-0.2.7.dist-info → grdwindinversion-0.3.1.dist-info}/top_level.txt +0 -0
grdwindinversion/inversion.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import tempfile
|
|
1
2
|
import traceback
|
|
2
3
|
|
|
3
4
|
import xsar
|
|
@@ -15,14 +16,12 @@ from scipy.ndimage import binary_dilation
|
|
|
15
16
|
import re
|
|
16
17
|
import string
|
|
17
18
|
import os
|
|
18
|
-
from grdwindinversion.
|
|
19
|
-
from grdwindinversion.utils import check_incidence_range, get_pol_ratio_name
|
|
19
|
+
from grdwindinversion.utils import check_incidence_range, get_pol_ratio_name, timing
|
|
20
20
|
from grdwindinversion.load_config import getConf
|
|
21
21
|
# optional debug messages
|
|
22
22
|
import logging
|
|
23
|
-
logging.
|
|
24
|
-
logging.
|
|
25
|
-
logging.INFO) # or .setLevel(logging.INFO)
|
|
23
|
+
logger = logging.getLogger('grdwindinversion.inversion')
|
|
24
|
+
logger.addHandler(logging.NullHandler())
|
|
26
25
|
|
|
27
26
|
|
|
28
27
|
def getSensorMetaDataset(filename):
|
|
@@ -45,10 +44,16 @@ def getSensorMetaDataset(filename):
|
|
|
45
44
|
return "S1B", "SENTINEL-1 B", xsar.Sentinel1Meta, xsar.Sentinel1Dataset
|
|
46
45
|
elif ("RS2" in filename):
|
|
47
46
|
return "RS2", "RADARSAT-2", xsar.RadarSat2Meta, xsar.RadarSat2Dataset
|
|
48
|
-
elif ("
|
|
49
|
-
return "RCM", "RADARSAT Constellation", xsar.RcmMeta, xsar.RcmDataset
|
|
47
|
+
elif ("RCM1" in filename):
|
|
48
|
+
return "RCM", "RADARSAT Constellation 1", xsar.RcmMeta, xsar.RcmDataset
|
|
49
|
+
elif ("RCM2" in filename):
|
|
50
|
+
return "RCM", "RADARSAT Constellation 2", xsar.RcmMeta, xsar.RcmDataset
|
|
51
|
+
elif ("RCM3" in filename):
|
|
52
|
+
return "RCM", "RADARSAT Constellation 3", xsar.RcmMeta, xsar.RcmDataset
|
|
53
|
+
|
|
50
54
|
else:
|
|
51
|
-
raise ValueError(
|
|
55
|
+
raise ValueError(
|
|
56
|
+
"must be S1A|S1B|RS2|RCM1|RCM2|RCM3, got filename %s" % filename)
|
|
52
57
|
|
|
53
58
|
|
|
54
59
|
def getOutputName2(input_file, outdir, sensor, meta, subdir=True):
|
|
@@ -96,12 +101,10 @@ def getOutputName2(input_file, outdir, sensor, meta, subdir=True):
|
|
|
96
101
|
new_format = f"{MISSIONID.lower()}--owi-xx-{meta_start_date.lower()}-{meta_stop_date.lower()}-_____-_____.nc"
|
|
97
102
|
elif sensor == 'RCM':
|
|
98
103
|
regex = re.compile(
|
|
99
|
-
"([
|
|
100
|
-
template = string.Template(
|
|
101
|
-
"${MISSIONID}_OK${DATA1}_PK${DATA2}_${DATA3}_${BEAM}_${DATE}_${TIME}_${POLARIZATION1}_${POLARIZATION2}_${PRODUCT}")
|
|
104
|
+
r"(RCM[0-9])_OK([0-9]+)_PK([0-9]+)_([0-9]+)_([A-Z]+)_(\d{8})_(\d{6})_([A-Z]{2}(?:_[A-Z]{2})?)_([A-Z]+)$")
|
|
102
105
|
match = regex.match(basename_match)
|
|
103
|
-
MISSIONID, DATA1, DATA2, DATA3,
|
|
104
|
-
new_format = f"{MISSIONID.lower()}-{
|
|
106
|
+
MISSIONID, DATA1, DATA2, DATA3, BEAM, DATE, TIME, POLARIZATION, PRODUCT = match.groups()
|
|
107
|
+
new_format = f"{MISSIONID.lower()}-{BEAM.lower()}-owi-xx-{meta_start_date.lower()}-{meta_stop_date.lower()}-_____-_____.nc"
|
|
105
108
|
else:
|
|
106
109
|
raise ValueError(
|
|
107
110
|
"sensor must be S1A|S1B|RS2|RCM, got sensor %s" % sensor)
|
|
@@ -209,6 +212,7 @@ def getAncillary(meta, ancillary_name='ecmwf'):
|
|
|
209
212
|
ancillary_name)
|
|
210
213
|
|
|
211
214
|
|
|
215
|
+
@timing(logger=logger.debug)
|
|
212
216
|
def inverse(dual_pol, inc, sigma0, sigma0_dual, ancillary_wind, dsig_cr, model_co, model_cross, **kwargs):
|
|
213
217
|
"""
|
|
214
218
|
Invert sigma0 to retrieve wind using model (lut or gmf).
|
|
@@ -282,7 +286,8 @@ def inverse(dual_pol, inc, sigma0, sigma0_dual, ancillary_wind, dsig_cr, model_c
|
|
|
282
286
|
return wind_co, None, None
|
|
283
287
|
|
|
284
288
|
|
|
285
|
-
|
|
289
|
+
@timing(logger=logger.debug)
|
|
290
|
+
def makeL2asOwi(xr_dataset, config):
|
|
286
291
|
"""
|
|
287
292
|
Rename xr_dataset variables and attributes to match naming convention.
|
|
288
293
|
|
|
@@ -290,12 +295,8 @@ def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks, apply_flatte
|
|
|
290
295
|
----------
|
|
291
296
|
xr_dataset: xarray.Dataset
|
|
292
297
|
dataset to rename
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
copol: str
|
|
296
|
-
copolarization name
|
|
297
|
-
crosspol: str
|
|
298
|
-
crosspolarization name
|
|
298
|
+
config: dict
|
|
299
|
+
configuration dict
|
|
299
300
|
|
|
300
301
|
Returns
|
|
301
302
|
-------
|
|
@@ -320,13 +321,27 @@ def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks, apply_flatte
|
|
|
320
321
|
'winddir_co': 'owiWindDirection_co',
|
|
321
322
|
'ancillary_wind_speed': 'owiAncillaryWindSpeed',
|
|
322
323
|
'ancillary_wind_direction': 'owiAncillaryWindDirection',
|
|
323
|
-
'sigma0_detrend': 'owiNrcs_detrend'
|
|
324
|
+
'sigma0_detrend': 'owiNrcs_detrend',
|
|
324
325
|
})
|
|
325
326
|
|
|
326
327
|
if "offboresight" in xr_dataset:
|
|
327
328
|
xr_dataset = xr_dataset.rename(
|
|
328
329
|
{"offboresight": "owiOffboresightAngle"})
|
|
329
330
|
|
|
331
|
+
if config["add_nrcs_model"]:
|
|
332
|
+
xr_dataset = xr_dataset.rename(
|
|
333
|
+
{"ancillary_nrcs": "owiAncillaryNrcs"})
|
|
334
|
+
xr_dataset.owiAncillaryNrcs.attrs["units"] = "m^2 / m^2"
|
|
335
|
+
xr_dataset.owiAncillaryNrcs.attrs[
|
|
336
|
+
"long_name"] = f"Ancillary Normalized Radar Cross Section - simulated from {config['l2_params']['copol_gmf']} & ancillary wind"
|
|
337
|
+
|
|
338
|
+
if config["l2_params"]["dual_pol"]:
|
|
339
|
+
xr_dataset = xr_dataset.rename(
|
|
340
|
+
{"ancillary_nrcs_cross": "owiAncillaryNrcs_cross"})
|
|
341
|
+
xr_dataset.owiAncillaryNrcs_cross.attrs["units"] = "m^2 / m^2"
|
|
342
|
+
xr_dataset.owiAncillaryNrcs_cross.attrs[
|
|
343
|
+
"long_name"] = f"Ancillary Normalized Radar Cross Section - simulated from {config['l2_params']['crosspol_gmf']} & ancillary wind"
|
|
344
|
+
|
|
330
345
|
xr_dataset.owiLon.attrs["units"] = "degrees_east"
|
|
331
346
|
xr_dataset.owiLon.attrs["long_name"] = "Longitude at wind cell center"
|
|
332
347
|
xr_dataset.owiLon.attrs["standard_name"] = "longitude"
|
|
@@ -343,23 +358,25 @@ def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks, apply_flatte
|
|
|
343
358
|
xr_dataset.owiElevationAngle.attrs["long_name"] = "Elevation angle at wind cell center"
|
|
344
359
|
xr_dataset.owiElevationAngle.attrs["standard_name"] = "elevation"
|
|
345
360
|
|
|
346
|
-
xr_dataset['owiNrcs'] = xr_dataset['sigma0_ocean'].sel(
|
|
361
|
+
xr_dataset['owiNrcs'] = xr_dataset['sigma0_ocean'].sel(
|
|
362
|
+
pol=config["l2_params"]["copol"])
|
|
347
363
|
xr_dataset.owiNrcs.attrs = xr_dataset.sigma0_ocean.attrs
|
|
348
364
|
xr_dataset.owiNrcs.attrs['units'] = 'm^2 / m^2'
|
|
349
365
|
xr_dataset.owiNrcs.attrs['long_name'] = 'Normalized Radar Cross Section'
|
|
350
366
|
xr_dataset.owiNrcs.attrs['definition'] = 'owiNrcs_no_noise_correction - owiNesz'
|
|
351
367
|
|
|
352
|
-
xr_dataset['owiMask_Nrcs'] = xr_dataset['sigma0_mask'].sel(
|
|
368
|
+
xr_dataset['owiMask_Nrcs'] = xr_dataset['sigma0_mask'].sel(
|
|
369
|
+
pol=config["l2_params"]["copol"])
|
|
353
370
|
xr_dataset.owiMask_Nrcs.attrs = xr_dataset.sigma0_mask.attrs
|
|
354
371
|
|
|
355
372
|
# NESZ & DSIG
|
|
356
373
|
xr_dataset = xr_dataset.assign(
|
|
357
|
-
owiNesz=(['line', 'sample'], xr_dataset.nesz.sel(pol=copol).values))
|
|
374
|
+
owiNesz=(['line', 'sample'], xr_dataset.nesz.sel(pol=config["l2_params"]["copol"]).values))
|
|
358
375
|
xr_dataset.owiNesz.attrs['units'] = 'm^2 / m^2'
|
|
359
376
|
xr_dataset.owiNesz.attrs['long_name'] = 'Noise Equivalent SigmaNaught'
|
|
360
377
|
|
|
361
378
|
xr_dataset['owiNrcs_no_noise_correction'] = xr_dataset['sigma0_ocean_raw'].sel(
|
|
362
|
-
pol=copol)
|
|
379
|
+
pol=config["l2_params"]["copol"])
|
|
363
380
|
xr_dataset.owiNrcs_no_noise_correction.attrs = xr_dataset.sigma0_ocean_raw.attrs
|
|
364
381
|
xr_dataset.owiNrcs_no_noise_correction.attrs['units'] = 'm^2 / m^2'
|
|
365
382
|
xr_dataset.owiNrcs_no_noise_correction.attrs[
|
|
@@ -378,7 +395,7 @@ def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks, apply_flatte
|
|
|
378
395
|
# sigma0_raw__corrected cross
|
|
379
396
|
if "sigma0_raw__corrected" in xr_dataset:
|
|
380
397
|
xr_dataset['owiNrcs_no_noise_correction_recalibrated'] = xr_dataset['sigma0_raw__corrected'].sel(
|
|
381
|
-
pol=copol)
|
|
398
|
+
pol=config["l2_params"]["copol"])
|
|
382
399
|
xr_dataset.owiNrcs_no_noise_correction_recalibrated.attrs = xr_dataset.sigma0_raw__corrected.attrs
|
|
383
400
|
xr_dataset.owiNrcs_no_noise_correction_recalibrated.attrs['units'] = 'm^2 / m^2'
|
|
384
401
|
xr_dataset.owiNrcs_no_noise_correction_recalibrated.attrs[
|
|
@@ -388,7 +405,7 @@ def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks, apply_flatte
|
|
|
388
405
|
|
|
389
406
|
xr_dataset.owiNrcs.attrs['definition'] = 'owiNrcs_no_noise_correction_recalibrated - owiNesz'
|
|
390
407
|
|
|
391
|
-
if dual_pol:
|
|
408
|
+
if config["l2_params"]["dual_pol"]:
|
|
392
409
|
|
|
393
410
|
xr_dataset = xr_dataset.rename({
|
|
394
411
|
'dsig_cross': 'owiDsig_cross',
|
|
@@ -399,31 +416,31 @@ def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks, apply_flatte
|
|
|
399
416
|
'sigma0_detrend_cross': 'owiNrcs_detrend_cross'
|
|
400
417
|
})
|
|
401
418
|
|
|
402
|
-
if apply_flattening:
|
|
419
|
+
if config["apply_flattening"]:
|
|
403
420
|
xr_dataset = xr_dataset.rename({
|
|
404
421
|
'nesz_cross_flattened': 'owiNesz_cross_flattened',
|
|
405
422
|
})
|
|
406
423
|
|
|
407
424
|
# nrcs cross
|
|
408
425
|
xr_dataset['owiNrcs_cross'] = xr_dataset['sigma0_ocean'].sel(
|
|
409
|
-
pol=crosspol)
|
|
426
|
+
pol=config["l2_params"]["crosspol"])
|
|
410
427
|
|
|
411
428
|
xr_dataset.owiNrcs_cross.attrs['units'] = 'm^2 / m^2'
|
|
412
429
|
xr_dataset.owiNrcs_cross.attrs['long_name'] = 'Normalized Radar Cross Section'
|
|
413
430
|
xr_dataset.owiNrcs_cross.attrs['definition'] = 'owiNrcs_cross_no_noise_correction - owiNesz_cross'
|
|
414
431
|
|
|
415
432
|
xr_dataset['owiMask_Nrcs_cross'] = xr_dataset['sigma0_mask'].sel(
|
|
416
|
-
pol=crosspol)
|
|
433
|
+
pol=config["l2_params"]["crosspol"])
|
|
417
434
|
xr_dataset.owiMask_Nrcs_cross.attrs = xr_dataset.sigma0_mask.attrs
|
|
418
435
|
|
|
419
436
|
# nesz cross
|
|
420
437
|
xr_dataset = xr_dataset.assign(owiNesz_cross=(
|
|
421
|
-
['line', 'sample'], xr_dataset.nesz.sel(pol=crosspol).values)) # no flattening
|
|
438
|
+
['line', 'sample'], xr_dataset.nesz.sel(pol=config["l2_params"]["crosspol"]).values)) # no flattening
|
|
422
439
|
xr_dataset.owiNesz_cross.attrs['units'] = 'm^2 / m^2'
|
|
423
440
|
xr_dataset.owiNesz_cross.attrs['long_name'] = 'Noise Equivalent SigmaNaught'
|
|
424
441
|
|
|
425
442
|
xr_dataset['owiNrcs_cross_no_noise_correction'] = xr_dataset['sigma0_ocean_raw'].sel(
|
|
426
|
-
pol=crosspol)
|
|
443
|
+
pol=config["l2_params"]["crosspol"])
|
|
427
444
|
|
|
428
445
|
xr_dataset.owiNrcs_cross_no_noise_correction.attrs['units'] = 'm^2 / m^2'
|
|
429
446
|
xr_dataset.owiNrcs_cross_no_noise_correction.attrs[
|
|
@@ -432,7 +449,7 @@ def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks, apply_flatte
|
|
|
432
449
|
# sigma0_raw__corrected cross
|
|
433
450
|
if "sigma0_raw__corrected" in xr_dataset:
|
|
434
451
|
xr_dataset['owiNrcs_cross_no_noise_correction_recalibrated'] = xr_dataset['sigma0_raw__corrected'].sel(
|
|
435
|
-
pol=crosspol)
|
|
452
|
+
pol=config["l2_params"]["crosspol"])
|
|
436
453
|
xr_dataset.owiNrcs_cross_no_noise_correction_recalibrated.attrs = xr_dataset.sigma0_raw__corrected.attrs
|
|
437
454
|
xr_dataset.owiNrcs_cross_no_noise_correction_recalibrated.attrs['units'] = 'm^2 / m^2'
|
|
438
455
|
xr_dataset.owiNrcs_cross_no_noise_correction_recalibrated.attrs[
|
|
@@ -442,10 +459,18 @@ def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks, apply_flatte
|
|
|
442
459
|
|
|
443
460
|
xr_dataset.owiNrcs_cross.attrs['definition'] = 'owiNrcs_cross_no_noise_correction_recalibrated - owiNesz_cross'
|
|
444
461
|
|
|
445
|
-
if
|
|
462
|
+
if config["add_gradientsfeatures"]:
|
|
446
463
|
xr_dataset = xr_dataset.rename({
|
|
447
|
-
'
|
|
464
|
+
'heterogeneity_mask': 'owiWindFilter'
|
|
448
465
|
})
|
|
466
|
+
else:
|
|
467
|
+
xr_dataset['owiWindFilter'] = xr.full_like(xr_dataset.owiNrcs, 0)
|
|
468
|
+
xr_dataset['owiWindFilter'].attrs['long_name'] = "Quality flag taking into account the local heterogeneity"
|
|
469
|
+
xr_dataset['owiWindFilter'].attrs['valid_range'] = np.array([0, 3])
|
|
470
|
+
xr_dataset['owiWindFilter'].attrs['flag_values'] = np.array([
|
|
471
|
+
0, 1, 2, 3])
|
|
472
|
+
xr_dataset['owiWindFilter'].attrs[
|
|
473
|
+
'flag_meanings'] = "homogeneous_NRCS, heterogeneous_from_co-polarization_NRCS, heterogeneous_from_cross-polarization_NRCS, heterogeneous_from_dual-polarization_NRCS"
|
|
449
474
|
|
|
450
475
|
# other variables
|
|
451
476
|
|
|
@@ -458,15 +483,6 @@ def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks, apply_flatte
|
|
|
458
483
|
xr_dataset['owiWindQuality'].attrs['flag_meanings'] = "good medium low poor"
|
|
459
484
|
xr_dataset['owiWindQuality'].attrs['comment'] = 'NOT COMPUTED YET'
|
|
460
485
|
|
|
461
|
-
xr_dataset['owiWindFilter'] = xr.full_like(xr_dataset.owiNrcs, 0)
|
|
462
|
-
xr_dataset['owiWindFilter'].attrs['long_name'] = "Quality flag taking into account the local heterogeneity"
|
|
463
|
-
xr_dataset['owiWindFilter'].attrs['valid_range'] = np.array([0, 3])
|
|
464
|
-
xr_dataset['owiWindFilter'].attrs['flag_values'] = np.array([
|
|
465
|
-
0, 1, 2, 3])
|
|
466
|
-
xr_dataset['owiWindFilter'].attrs[
|
|
467
|
-
'flag_meanings'] = "homogeneous_NRCS, heterogeneous_from_co-polarization_NRCS, heterogeneous_from_cross-polarization_NRCS, heterogeneous_from_dual-polarization_NRCS"
|
|
468
|
-
xr_dataset['owiWindFilter'].attrs['comment'] = 'NOT COMPUTED YET'
|
|
469
|
-
|
|
470
486
|
xr_dataset = xr_dataset.rename(
|
|
471
487
|
{"line": "owiAzSize", "sample": "owiRaSize"})
|
|
472
488
|
|
|
@@ -476,8 +492,6 @@ def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks, apply_flatte
|
|
|
476
492
|
xr_dataset = xr_dataset.drop_vars(["sigma0_raw__corrected"])
|
|
477
493
|
xr_dataset = xr_dataset.drop_dims(['pol'])
|
|
478
494
|
|
|
479
|
-
xr_dataset.compute()
|
|
480
|
-
|
|
481
495
|
table_fillValue = {
|
|
482
496
|
"owiWindQuality": -1,
|
|
483
497
|
"owiHeading": 9999.99,
|
|
@@ -505,7 +519,7 @@ def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks, apply_flatte
|
|
|
505
519
|
return xr_dataset, encoding
|
|
506
520
|
|
|
507
521
|
|
|
508
|
-
def preprocess(filename, outdir, config_path, overwrite=False,
|
|
522
|
+
def preprocess(filename, outdir, config_path, overwrite=False, add_gradientsfeatures=False, resolution='1000m'):
|
|
509
523
|
"""
|
|
510
524
|
Main function to generate L2 product.
|
|
511
525
|
|
|
@@ -549,6 +563,10 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
|
|
|
549
563
|
recalibration = config["recalibration"]
|
|
550
564
|
meta = fct_meta(filename)
|
|
551
565
|
|
|
566
|
+
# si une des deux n'est pas VV VH HH HV on ne fait rien
|
|
567
|
+
if not all([pol in ["VV", "VH", "HH", "HV"] for pol in meta.pols.split(' ')]):
|
|
568
|
+
raise ValueError(f"Polarisation non gérée : meta.pols = {meta.pols}")
|
|
569
|
+
|
|
552
570
|
no_subdir_cfg = config_base.get("no_subdir", False)
|
|
553
571
|
config["no_subdir"] = no_subdir_cfg
|
|
554
572
|
|
|
@@ -560,6 +578,26 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
|
|
|
560
578
|
f'Using meteorological convention because "winddir_convention" was not found in config.')
|
|
561
579
|
config["winddir_convention"] = winddir_convention
|
|
562
580
|
|
|
581
|
+
if "add_gradientsfeatures" in config_base:
|
|
582
|
+
add_gradientsfeatures = config_base["add_gradientsfeatures"]
|
|
583
|
+
else:
|
|
584
|
+
add_gradientsfeatures = False
|
|
585
|
+
logging.warning(
|
|
586
|
+
f'Not computing gradients by default')
|
|
587
|
+
config["add_gradientsfeatures"] = add_gradientsfeatures
|
|
588
|
+
|
|
589
|
+
if "add_nrcs_model" in config_base:
|
|
590
|
+
add_nrcs_model = config_base["add_nrcs_model"]
|
|
591
|
+
add_nrcs_model = False
|
|
592
|
+
logging.warning(
|
|
593
|
+
f'Force this variable to be false, before fixing the issue'
|
|
594
|
+
)
|
|
595
|
+
else:
|
|
596
|
+
add_nrcs_model = False
|
|
597
|
+
logging.warning(
|
|
598
|
+
f'Not computing nrcs from model by default')
|
|
599
|
+
config["add_nrcs_model"] = add_nrcs_model
|
|
600
|
+
|
|
563
601
|
# creating a dictionnary of parameters
|
|
564
602
|
config["l2_params"] = {}
|
|
565
603
|
|
|
@@ -607,6 +645,11 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
|
|
|
607
645
|
logging.error(e)
|
|
608
646
|
sys.exit(-1)
|
|
609
647
|
|
|
648
|
+
# add parameters in config
|
|
649
|
+
config["meta"] = meta
|
|
650
|
+
config["fct_dataset"] = fct_dataset
|
|
651
|
+
config["map_model"] = map_model
|
|
652
|
+
|
|
610
653
|
# load
|
|
611
654
|
xr_dataset = xr_dataset.load()
|
|
612
655
|
|
|
@@ -642,6 +685,7 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
|
|
|
642
685
|
config["l2_params"]["model_co"] = model_co
|
|
643
686
|
config["l2_params"]["model_cross"] = model_cross
|
|
644
687
|
config["sensor_longname"] = sensor_longname
|
|
688
|
+
config["sensor"] = sensor
|
|
645
689
|
|
|
646
690
|
# need to load LUTs before inversion
|
|
647
691
|
nc_luts = [x for x in [model_co, model_cross] if x.startswith("nc_lut")]
|
|
@@ -796,41 +840,155 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
|
|
|
796
840
|
xr_dataset.attrs["path_aux_cal_old"] = os.path.basename(os.path.dirname(
|
|
797
841
|
os.path.dirname(xsar_dataset.datatree['recalibration'].attrs['path_aux_cal_old'])))
|
|
798
842
|
|
|
799
|
-
if
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
# adding sigma0 detrend
|
|
806
|
-
xr_dataset_100['sigma0_detrend'] = xsarsea.sigma0_detrend(
|
|
807
|
-
xr_dataset_100.sigma0.sel(pol=copol), xr_dataset_100.incidence, model=model_co)
|
|
843
|
+
if add_nrcs_model:
|
|
844
|
+
# add timing
|
|
845
|
+
phi = np.abs(
|
|
846
|
+
np.rad2deg(xsarsea.dir_meteo_to_sample(
|
|
847
|
+
xr_dataset["ancillary_wind_direction"], xr_dataset["ground_heading"]))
|
|
848
|
+
)
|
|
808
849
|
|
|
850
|
+
varnames = ["ancillary_nrcs"]
|
|
851
|
+
gmf_names = [model_co]
|
|
809
852
|
if dual_pol:
|
|
810
|
-
|
|
811
|
-
|
|
853
|
+
varnames.append("ancillary_nrcs_cross")
|
|
854
|
+
gmf_names.append(model_cross)
|
|
812
855
|
|
|
813
|
-
|
|
814
|
-
[xr_dataset_100['sigma0_detrend'],
|
|
815
|
-
xr_dataset_100['sigma0_detrend_cross']],
|
|
816
|
-
dim='pol'
|
|
817
|
-
)
|
|
818
|
-
sigma0_detrend_combined['pol'] = [copol, crosspol]
|
|
819
|
-
|
|
820
|
-
xr_dataset_100['sigma0_detrend'] = sigma0_detrend_combined
|
|
856
|
+
for idx, gmf_name in enumerate(gmf_names):
|
|
821
857
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
858
|
+
@timing(logger=logger.info)
|
|
859
|
+
def apply_lut_to_dataset():
|
|
860
|
+
lut = xsarsea.windspeed.get_model(
|
|
861
|
+
gmf_name).to_lut(unit="linear")
|
|
826
862
|
|
|
827
|
-
|
|
828
|
-
|
|
863
|
+
def lut_selection(incidence, wspd, phi):
|
|
864
|
+
if "phi" in lut.coords:
|
|
865
|
+
return lut.sel(
|
|
866
|
+
incidence=incidence, wspd=wspd, phi=phi, method="nearest"
|
|
867
|
+
)
|
|
868
|
+
else:
|
|
869
|
+
return lut.sel(
|
|
870
|
+
incidence=incidence, wspd=wspd, method="nearest"
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
xr_dataset[varnames[idx]] = xr.apply_ufunc(
|
|
874
|
+
lut_selection,
|
|
875
|
+
xr_dataset.incidence,
|
|
876
|
+
xr_dataset.ancillary_wind_speed,
|
|
877
|
+
phi,
|
|
878
|
+
vectorize=True,
|
|
879
|
+
dask="parallelized",
|
|
880
|
+
output_dtypes=[float],
|
|
881
|
+
)
|
|
882
|
+
|
|
883
|
+
apply_lut_to_dataset()
|
|
829
884
|
|
|
830
885
|
return xr_dataset, out_file, config
|
|
831
886
|
|
|
832
887
|
|
|
833
|
-
def
|
|
888
|
+
def process_gradients(xr_dataset, config):
|
|
889
|
+
"""
|
|
890
|
+
Function to process gradients features.
|
|
891
|
+
|
|
892
|
+
Parameters
|
|
893
|
+
----------
|
|
894
|
+
xr_dataset : xarray.Dataset
|
|
895
|
+
Main dataset to process.
|
|
896
|
+
meta : object
|
|
897
|
+
Metadata from the original dataset.
|
|
898
|
+
fct_dataset : callable
|
|
899
|
+
Function to load the dataset.
|
|
900
|
+
map_model : dict
|
|
901
|
+
Mapping model for renaming variables.
|
|
902
|
+
config : dict
|
|
903
|
+
Configuration dictionary.
|
|
904
|
+
|
|
905
|
+
Returns
|
|
906
|
+
-------
|
|
907
|
+
tuple
|
|
908
|
+
Updated xr_dataset and xr_dataset_streaks dataset.
|
|
909
|
+
"""
|
|
910
|
+
from grdwindinversion.gradientFeatures import GradientFeatures
|
|
911
|
+
|
|
912
|
+
meta = config["meta"]
|
|
913
|
+
fct_dataset = config["fct_dataset"]
|
|
914
|
+
map_model = config["map_model"]
|
|
915
|
+
|
|
916
|
+
model_co = config["l2_params"]["model_co"]
|
|
917
|
+
model_cross = config["l2_params"]["model_cross"]
|
|
918
|
+
copol = config["l2_params"]["copol"]
|
|
919
|
+
crosspol = config["l2_params"]["crosspol"]
|
|
920
|
+
dual_pol = config["l2_params"]["dual_pol"]
|
|
921
|
+
|
|
922
|
+
# Load the 100m dataset
|
|
923
|
+
xsar_dataset_100 = fct_dataset(
|
|
924
|
+
meta, resolution='100m')
|
|
925
|
+
|
|
926
|
+
xr_dataset_100 = xsar_dataset_100.datatree['measurement'].to_dataset()
|
|
927
|
+
xr_dataset_100 = xr_dataset_100.rename(map_model)
|
|
928
|
+
# load dataset
|
|
929
|
+
xr_dataset_100 = xr_dataset_100.load()
|
|
930
|
+
|
|
931
|
+
# adding sigma0 detrend
|
|
932
|
+
xr_dataset_100['sigma0_detrend'] = xsarsea.sigma0_detrend(
|
|
933
|
+
xr_dataset_100.sigma0.sel(pol=copol), xr_dataset_100.incidence, model=model_co)
|
|
934
|
+
|
|
935
|
+
if dual_pol:
|
|
936
|
+
xr_dataset_100['sigma0_detrend_cross'] = xsarsea.sigma0_detrend(
|
|
937
|
+
xr_dataset_100.sigma0.sel(pol=crosspol), xr_dataset_100.incidence, model=model_cross)
|
|
938
|
+
|
|
939
|
+
sigma0_detrend_combined = xr.concat(
|
|
940
|
+
[xr_dataset_100['sigma0_detrend'],
|
|
941
|
+
xr_dataset_100['sigma0_detrend_cross']],
|
|
942
|
+
dim='pol'
|
|
943
|
+
)
|
|
944
|
+
sigma0_detrend_combined['pol'] = [copol, crosspol]
|
|
945
|
+
|
|
946
|
+
xr_dataset_100['sigma0_detrend'] = sigma0_detrend_combined
|
|
947
|
+
|
|
948
|
+
xr_dataset_100.land_mask.values = binary_dilation(xr_dataset_100['land_mask'].values.astype('uint8'),
|
|
949
|
+
structure=np.ones((3, 3), np.uint8), iterations=3)
|
|
950
|
+
xr_dataset_100['sigma0_detrend'] = xr.where(
|
|
951
|
+
xr_dataset_100['land_mask'], np.nan, xr_dataset_100['sigma0']).transpose(*xr_dataset_100['sigma0'].dims)
|
|
952
|
+
|
|
953
|
+
xr_dataset_100['ancillary_wind'] = (
|
|
954
|
+
xr_dataset_100.model_U10 + 1j * xr_dataset_100.model_V10) * np.exp(1j * np.deg2rad(xr_dataset_100.ground_heading))
|
|
955
|
+
|
|
956
|
+
downscales_factors = [1, 2, 4, 8]
|
|
957
|
+
# 4 and 8 must be in downscales_factors
|
|
958
|
+
assert all([x in downscales_factors for x in [4, 8]])
|
|
959
|
+
|
|
960
|
+
gradientFeatures = GradientFeatures(
|
|
961
|
+
xr_dataset=xr_dataset,
|
|
962
|
+
xr_dataset_100=xr_dataset_100,
|
|
963
|
+
windows_sizes=[1600, 3200],
|
|
964
|
+
downscales_factors=downscales_factors,
|
|
965
|
+
window_step=1
|
|
966
|
+
)
|
|
967
|
+
|
|
968
|
+
# Compute heterogeneity mask and variables
|
|
969
|
+
dataArraysHeterogeneity = gradientFeatures.get_heterogeneity_mask(config)
|
|
970
|
+
xr_dataset = xr_dataset.merge(dataArraysHeterogeneity)
|
|
971
|
+
|
|
972
|
+
# Add streaks dataset
|
|
973
|
+
streaks_indiv = gradientFeatures.streaks_individual()
|
|
974
|
+
if 'longitude' in streaks_indiv:
|
|
975
|
+
xr_dataset_streaks = xr.Dataset({
|
|
976
|
+
'longitude': streaks_indiv.longitude,
|
|
977
|
+
'latitude': streaks_indiv.latitude,
|
|
978
|
+
'dir_smooth': streaks_indiv.angle,
|
|
979
|
+
'dir_mean_smooth': gradientFeatures.streaks_mean_smooth().angle,
|
|
980
|
+
'dir_smooth_mean': gradientFeatures.streaks_smooth_mean().angle,
|
|
981
|
+
})
|
|
982
|
+
else:
|
|
983
|
+
logger.warn(
|
|
984
|
+
"'longitude' not found in streaks_indiv : there is probably an error")
|
|
985
|
+
xr_dataset_streaks = None
|
|
986
|
+
|
|
987
|
+
return xr_dataset, xr_dataset_streaks
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
@timing(logger=logger.info)
|
|
991
|
+
def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, resolution='1000m'):
|
|
834
992
|
"""
|
|
835
993
|
Main function to generate L2 product.
|
|
836
994
|
|
|
@@ -858,7 +1016,13 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
|
|
|
858
1016
|
"""
|
|
859
1017
|
|
|
860
1018
|
xr_dataset, out_file, config = preprocess(
|
|
861
|
-
filename, outdir, config_path, overwrite,
|
|
1019
|
+
filename, outdir, config_path, overwrite, resolution)
|
|
1020
|
+
|
|
1021
|
+
if config["add_gradientsfeatures"]:
|
|
1022
|
+
xr_dataset, xr_dataset_streaks = process_gradients(
|
|
1023
|
+
xr_dataset, config)
|
|
1024
|
+
else:
|
|
1025
|
+
xr_dataset_streaks = None
|
|
862
1026
|
|
|
863
1027
|
model_co = config["l2_params"]["model_co"]
|
|
864
1028
|
model_cross = config["l2_params"]["model_cross"]
|
|
@@ -951,8 +1115,9 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
|
|
|
951
1115
|
"long_name"] = f"{ancillary_name} wind direction in oceanographic convention (clockwise, to), ex: 0°=to north, 90°=to east"
|
|
952
1116
|
|
|
953
1117
|
xr_dataset, encoding = makeL2asOwi(
|
|
954
|
-
xr_dataset,
|
|
1118
|
+
xr_dataset, config)
|
|
955
1119
|
|
|
1120
|
+
xr_dataset = xr_dataset.compute()
|
|
956
1121
|
# add attributes
|
|
957
1122
|
firstMeasurementTime = None
|
|
958
1123
|
lastMeasurementTime = None
|
|
@@ -1035,7 +1200,26 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
|
|
|
1035
1200
|
|
|
1036
1201
|
os.makedirs(os.path.dirname(out_file), exist_ok=True)
|
|
1037
1202
|
|
|
1203
|
+
# Sauvegarde de xr_dataset dans le fichier de sortie final
|
|
1038
1204
|
xr_dataset.to_netcdf(out_file, mode="w", encoding=encoding)
|
|
1205
|
+
|
|
1206
|
+
# Vérifier si le dataset de streaks est présent
|
|
1207
|
+
if xr_dataset_streaks is not None:
|
|
1208
|
+
# Créer un fichier temporaire pour le dataset streaks
|
|
1209
|
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".nc") as tmp_file:
|
|
1210
|
+
temp_out_file = tmp_file.name
|
|
1211
|
+
|
|
1212
|
+
# Écrire xr_dataset_streaks dans le fichier temporaire
|
|
1213
|
+
xr_dataset_streaks.to_netcdf(
|
|
1214
|
+
temp_out_file, mode="w", group="owiWindStreaks")
|
|
1215
|
+
|
|
1216
|
+
# Charger le fichier temporaire et l'ajouter au fichier final en tant que groupe
|
|
1217
|
+
with xr.open_dataset(temp_out_file, group="owiWindStreaks") as ds_streaks:
|
|
1218
|
+
ds_streaks.to_netcdf(out_file, mode="a", group="owiWindStreaks")
|
|
1219
|
+
|
|
1220
|
+
# Supprimer le fichier temporaire après l'opération
|
|
1221
|
+
os.remove(temp_out_file)
|
|
1222
|
+
|
|
1039
1223
|
if generateCSV:
|
|
1040
1224
|
df = xr_dataset.to_dataframe()
|
|
1041
1225
|
df = df[df.owiMask == False]
|
grdwindinversion/load_config.py
CHANGED
|
@@ -3,11 +3,14 @@ import logging
|
|
|
3
3
|
import os
|
|
4
4
|
import grdwindinversion
|
|
5
5
|
from yaml import CLoader as Loader
|
|
6
|
-
|
|
6
|
+
local_config_potential_path1 = os.path.expanduser(
|
|
7
7
|
'~/.grdwindinversion/data_config.yaml')
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
local_config_potential_path2 = os.path.join(os.path.dirname(
|
|
9
|
+
grdwindinversion.__file__), 'local_data_config.yaml')
|
|
10
|
+
if os.path.exists(local_config_potential_path1):
|
|
11
|
+
config_path = local_config_potential_path1
|
|
12
|
+
elif os.path.exists(local_config_potential_path2):
|
|
13
|
+
config_path = local_config_potential_path2
|
|
11
14
|
else:
|
|
12
15
|
config_path = os.path.join(os.path.dirname(
|
|
13
16
|
grdwindinversion.__file__), 'data_config.yaml')
|
grdwindinversion/utils.py
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
1
3
|
import logging
|
|
2
4
|
import xsarsea
|
|
3
5
|
|
|
4
6
|
|
|
7
|
+
logging.basicConfig(level=logging.INFO,
|
|
8
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
9
|
+
logger = logging.getLogger('grdwindinversion')
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
mem_monitor = True
|
|
13
|
+
try:
|
|
14
|
+
from psutil import Process
|
|
15
|
+
except ImportError:
|
|
16
|
+
logger.warning("psutil module not found. Disabling memory monitor")
|
|
17
|
+
mem_monitor = False
|
|
18
|
+
|
|
19
|
+
|
|
5
20
|
def check_incidence_range(incidence, models, **kwargs):
|
|
6
21
|
"""
|
|
7
22
|
Check if the incidence range of the dataset is within the range of the LUT of the model.
|
|
@@ -81,3 +96,28 @@ def get_pol_ratio_name(model_co):
|
|
|
81
96
|
return "not_written_in_lut"
|
|
82
97
|
else:
|
|
83
98
|
return '/'
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def timing(logger=logger.debug):
|
|
102
|
+
"""provide a @timing decorator() for functions, that log time spent in it"""
|
|
103
|
+
|
|
104
|
+
def decorator(f):
|
|
105
|
+
# @wraps(f)
|
|
106
|
+
def wrapper(*args, **kwargs):
|
|
107
|
+
mem_str = ''
|
|
108
|
+
process = None
|
|
109
|
+
if mem_monitor:
|
|
110
|
+
process = Process(os.getpid())
|
|
111
|
+
startrss = process.memory_info().rss
|
|
112
|
+
starttime = time.time()
|
|
113
|
+
result = f(*args, **kwargs)
|
|
114
|
+
endtime = time.time()
|
|
115
|
+
if mem_monitor:
|
|
116
|
+
endrss = process.memory_info().rss
|
|
117
|
+
mem_str = 'mem: %+.1fMb' % ((endrss - startrss) / (1024 ** 2))
|
|
118
|
+
logger(
|
|
119
|
+
'timing %s : %.2fs. %s' % (f.__name__, endtime - starttime, mem_str))
|
|
120
|
+
return result
|
|
121
|
+
wrapper.__doc__ = f.__doc__
|
|
122
|
+
return wrapper
|
|
123
|
+
return decorator
|