grdwindinversion 0.2.3.post15__py3-none-any.whl → 0.2.5__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.
@@ -1,4 +1,5 @@
1
1
  no_subdir: True
2
+ winddir_convention: "meteorological"
2
3
  S1A:
3
4
  GMF_VV_NAME: "gmf_cmod5n"
4
5
  GMF_VH_NAME: "gmf_s1_v2"
@@ -16,6 +16,7 @@ import re
16
16
  import string
17
17
  import os
18
18
  from grdwindinversion.streaks import get_streaks
19
+ from grdwindinversion.utils import check_incidence_range, get_pol_ratio_name
19
20
  from grdwindinversion.load_config import getConf
20
21
  # optional debug messages
21
22
  import logging
@@ -97,10 +98,10 @@ def getOutputName2(input_file, outdir, sensor, meta, subdir=True):
97
98
  regex = re.compile(
98
99
  "([A-Z0-9]+)_OK([0-9]+)_PK([0-9]+)_(.*?)_(.*?)_(.*?)_(.*?)_(.*?)_(.*?)_(.*?)")
99
100
  template = string.Template(
100
- "${MISSIONID}_OK${DATA1}_PK${DATA2}_${DATA3}_${DATA4}_${DATE}_${TIME}_${POLARIZATION1}_${POLARIZATION2}_${PRODUCT}")
101
+ "${MISSIONID}_OK${DATA1}_PK${DATA2}_${DATA3}_${BEAM}_${DATE}_${TIME}_${POLARIZATION1}_${POLARIZATION2}_${PRODUCT}")
101
102
  match = regex.match(basename_match)
102
- MISSIONID, DATA1, DATA2, DATA3, DATA4, DATE, TIME, POLARIZATION1, POLARIZATION2, LAST = match.groups()
103
- new_format = f"{MISSIONID.lower()}--owi-xx-{meta_start_date.lower()}-{meta_stop_date.lower()}-_____-_____.nc"
103
+ MISSIONID, DATA1, DATA2, DATA3, BEAM_MODE, DATE, TIME, POLARIZATION1, POLARIZATION2, LAST = match.groups()
104
+ new_format = f"{MISSIONID.lower()}-{BEAM.lower()}-owi-xx-{meta_start_date.lower()}-{meta_stop_date.lower()}-_____-_____.nc"
104
105
  else:
105
106
  raise ValueError(
106
107
  "sensor must be S1A|S1B|RS2|RCM, got sensor %s" % sensor)
@@ -208,7 +209,7 @@ def getAncillary(meta, ancillary_name='ecmwf'):
208
209
  ancillary_name)
209
210
 
210
211
 
211
- def inverse(dual_pol, inc, sigma0, sigma0_dual, ancillary_wind, dsig_cr, model_vv, model_vh, **kwargs):
212
+ def inverse(dual_pol, inc, sigma0, sigma0_dual, ancillary_wind, dsig_cr, model_co, model_cross, **kwargs):
212
213
  """
213
214
  Invert sigma0 to retrieve wind using model (lut or gmf).
214
215
 
@@ -224,36 +225,36 @@ def inverse(dual_pol, inc, sigma0, sigma0_dual, ancillary_wind, dsig_cr, model_v
224
225
  sigma0 to be inverted for dualpol
225
226
  ancillary_wind=: xarray.DataArray (numpy.complex28)
226
227
  ancillary wind
227
- | (for example ecmwf winds), in **GMF convention** (-np.conj included),
228
+ | (for example ecmwf winds), in **ANTENNA convention**,
228
229
  dsig_cr=: float or xarray.DataArray
229
230
  parameters used for
230
231
 
231
232
  | `Jsig_cr=((sigma0_gmf - sigma0) / dsig_cr) ** 2`
232
- model_vv=: str
233
+ model_co=: str
233
234
  model to use for VV or HH polarization.
234
- model_vh=: str
235
+ model_cross=: str
235
236
  model to use for VH or HV polarization.
236
237
 
237
238
  Returns
238
239
  -------
239
240
  xarray.DataArray or tuple
240
- inverted wind in **gmf convention** .
241
+ inverted wind in ** antenna convention** .
241
242
 
242
243
  See Also
243
244
  --------
244
245
  xsarsea documentation
245
- https://cyclobs.ifremer.fr/static/sarwing_datarmor/xsarsea/examples/windspeed_inversion.html
246
+ https://cerweb.ifremer.fr/datarmor/doc_sphinx/xsarsea/
246
247
  """
247
248
  logging.debug("inversion")
248
249
 
249
250
  list_mods = windspeed.available_models().index.tolist(
250
251
  ) + windspeed.available_models().alias.tolist() + [None]
251
- if model_vv not in list_mods:
252
+ if model_co not in list_mods:
252
253
  raise ValueError(
253
- f"model_vv {model_vv} not in windspeed.available_models() : not going further")
254
- if model_vh not in list_mods:
254
+ f"model_co {model_co} not in windspeed.available_models() : not going further")
255
+ if model_cross not in list_mods:
255
256
  raise ValueError(
256
- f"model_vh {model_vh} not in windspeed.available_models() : not going further")
257
+ f"model_cross {model_cross} not in windspeed.available_models() : not going further")
257
258
 
258
259
  winds = windspeed.invert_from_model(
259
260
  inc,
@@ -261,7 +262,7 @@ def inverse(dual_pol, inc, sigma0, sigma0_dual, ancillary_wind, dsig_cr, model_v
261
262
  sigma0_dual,
262
263
  ancillary_wind=ancillary_wind,
263
264
  dsig_cr=dsig_cr,
264
- model=(model_vv, model_vh),
265
+ model=(model_co, model_cross),
265
266
  **kwargs)
266
267
 
267
268
  if dual_pol:
@@ -271,7 +272,7 @@ def inverse(dual_pol, inc, sigma0, sigma0_dual, ancillary_wind, dsig_cr, model_v
271
272
  inc.values,
272
273
  sigma0_dual.values,
273
274
  dsig_cr=dsig_cr.values,
274
- model=model_vh,
275
+ model=model_cross,
275
276
  **kwargs)
276
277
 
277
278
  return wind_co, wind_dual, wind_cross
@@ -281,7 +282,7 @@ def inverse(dual_pol, inc, sigma0, sigma0_dual, ancillary_wind, dsig_cr, model_v
281
282
  return wind_co, None, None
282
283
 
283
284
 
284
- def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks):
285
+ def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks, apply_flattening):
285
286
  """
286
287
  Rename xr_dataset variables and attributes to match naming convention.
287
288
 
@@ -391,13 +392,18 @@ def makeL2asOwi(xr_dataset, dual_pol, copol, crosspol, add_streaks):
391
392
 
392
393
  xr_dataset = xr_dataset.rename({
393
394
  'dsig_cross': 'owiDsig_cross',
394
- 'nesz_cross_final': 'owiNesz_cross_final',
395
395
  'winddir_cross': 'owiWindDirection_cross',
396
396
  'winddir_dual': 'owiWindDirection',
397
397
  'windspeed_cross': 'owiWindSpeed_cross',
398
398
  'windspeed_dual': 'owiWindSpeed',
399
399
  'sigma0_detrend_cross': 'owiNrcs_detrend_cross'
400
400
  })
401
+
402
+ if apply_flattening:
403
+ xr_dataset = xr_dataset.rename({
404
+ 'nesz_cross_flattened': 'owiNesz_cross_flattened',
405
+ })
406
+
401
407
  # nrcs cross
402
408
  xr_dataset['owiNrcs_cross'] = xr_dataset['sigma0_ocean'].sel(
403
409
  pol=crosspol)
@@ -532,6 +538,7 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
532
538
  Loader=yaml.FullLoader
533
539
  )
534
540
  try:
541
+ # check if sensor is in the config
535
542
  config = config_base[sensor]
536
543
  except Exception:
537
544
  raise KeyError("sensor %s not in this config" % sensor)
@@ -543,6 +550,19 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
543
550
  meta = fct_meta(filename)
544
551
 
545
552
  no_subdir_cfg = config_base.get("no_subdir", False)
553
+ config["no_subdir"] = no_subdir_cfg
554
+
555
+ if "winddir_convention" in config_base:
556
+ winddir_convention = config_base["winddir_convention"]
557
+ else:
558
+ winddir_convention = "meteorological"
559
+ logging.warning(
560
+ f'Using meteorological convention because "winddir_convention" was not found in config.')
561
+ config["winddir_convention"] = winddir_convention
562
+
563
+ # creating a dictionnary of parameters
564
+ config["l2_params"] = {}
565
+
546
566
  out_file = getOutputName2(filename, outdir, sensor,
547
567
  meta, subdir=not no_subdir_cfg)
548
568
 
@@ -607,24 +627,26 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
607
627
  copol_gmf = 'HH'
608
628
  crosspol_gmf = 'VH'
609
629
 
610
- model_vv = config["GMF_"+copol_gmf+"_NAME"]
611
- model_vh = config["GMF_"+crosspol_gmf+"_NAME"]
612
-
613
- # need to load gmfs before inversion
614
- gmfs_impl = [x for x in [model_vv, model_vh] if "gmf_" in x]
615
- windspeed.gmfs.GmfModel.activate_gmfs_impl(gmfs_impl)
616
- sarwings_luts = [x for x in [model_vv, model_vh]
617
- if x.startswith("sarwing_lut_")]
630
+ model_co = config["GMF_"+copol_gmf+"_NAME"]
631
+ model_cross = config["GMF_"+crosspol_gmf+"_NAME"]
618
632
 
619
- if len(sarwings_luts) > 0:
620
- windspeed.register_sarwing_luts(getConf()["sarwing_luts_path"])
633
+ # register paramaters in config
634
+ config["l2_params"]["dual_pol"] = dual_pol
635
+ config["l2_params"]["copol"] = copol
636
+ config["l2_params"]["crosspol"] = crosspol
637
+ config["l2_params"]["copol_gmf"] = copol_gmf
638
+ config["l2_params"]["crosspol_gmf"] = crosspol_gmf
639
+ config["l2_params"]["model_co"] = model_co
640
+ config["l2_params"]["model_cross"] = model_cross
641
+ config["sensor_longname"] = sensor_longname
621
642
 
622
- nc_luts = [x for x in [model_vv, model_vh] if x.startswith("nc_lut")]
643
+ # need to load LUTs before inversion
644
+ nc_luts = [x for x in [model_co, model_cross] if x.startswith("nc_lut")]
623
645
 
624
646
  if len(nc_luts) > 0:
625
647
  windspeed.register_nc_luts(getConf()["nc_luts_path"])
626
648
 
627
- if (model_vv == "gmf_cmod7"):
649
+ if (model_co == "gmf_cmod7"):
628
650
  windspeed.register_cmod7(getConf()["lut_cmod7_path"])
629
651
  #  Step 2 - clean and prepare dataset
630
652
 
@@ -690,7 +712,7 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
690
712
  xr_dataset['ancillary_wind_direction'].attrs = {}
691
713
  xr_dataset['ancillary_wind_direction'].attrs['units'] = 'degrees_north'
692
714
  xr_dataset['ancillary_wind_direction'].attrs[
693
- 'long_name'] = f'{ancillary_name} wind direction (meteorological convention)'
715
+ 'long_name'] = f'{ancillary_name} wind direction in meteorological convention (clockwise, from), ex: 0°=from north, 90°=from east'
694
716
  xr_dataset['ancillary_wind_direction'].attrs['standart_name'] = 'wind_direction'
695
717
 
696
718
  xr_dataset['ancillary_wind_speed'] = np.sqrt(
@@ -705,7 +727,7 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
705
727
  xr_dataset['ancillary_wind_speed'].attrs['standart_name'] = 'wind_speed'
706
728
 
707
729
  xr_dataset['ancillary_wind'] = xr.where(xr_dataset['mask'], np.nan,
708
- (xr_dataset.ancillary_wind_speed * np.exp(1j * xsarsea.dir_geo_to_sample(xr_dataset.ancillary_wind_direction, xr_dataset.ground_heading))).compute()).transpose(
730
+ (xr_dataset.ancillary_wind_speed * np.exp(1j * xsarsea.dir_meteo_to_sample(xr_dataset.ancillary_wind_direction, xr_dataset.ground_heading))).compute()).transpose(
709
731
  *xr_dataset['ancillary_wind_speed'].dims)
710
732
 
711
733
  xr_dataset.attrs['ancillary_source'] = xr_dataset['model_U10'].attrs['history'].split('decoded: ')[
@@ -734,37 +756,31 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
734
756
  xr_dataset['sigma0_ocean_raw'].attrs = xr_dataset['sigma0_raw'].attrs
735
757
 
736
758
  xr_dataset['sigma0_detrend'] = xsarsea.sigma0_detrend(
737
- xr_dataset.sigma0.sel(pol=copol), xr_dataset.incidence, model=model_vv)
759
+ xr_dataset.sigma0.sel(pol=copol), xr_dataset.incidence, model=model_co)
738
760
 
739
761
  # processing
740
762
  if dual_pol:
741
763
 
742
764
  xr_dataset['sigma0_detrend_cross'] = xsarsea.sigma0_detrend(
743
- xr_dataset.sigma0.sel(pol=crosspol), xr_dataset.incidence, model=model_vh)
765
+ xr_dataset.sigma0.sel(pol=crosspol), xr_dataset.incidence, model=model_cross)
744
766
  if config["apply_flattening"]:
745
- xr_dataset = xr_dataset.assign(nesz_cross_final=(
767
+ xr_dataset = xr_dataset.assign(nesz_cross_flattened=(
746
768
  ['line', 'sample'], windspeed.nesz_flattening(xr_dataset.nesz.sel(pol=crosspol), xr_dataset.incidence)))
747
- xr_dataset['nesz_cross_final'].attrs[
769
+ xr_dataset['nesz_cross_flattened'].attrs[
748
770
  "comment"] = 'nesz has been flattened using windspeed.nesz_flattening'
749
-
771
+ # dsig
772
+ xr_dataset["dsig_cross"] = windspeed.get_dsig(config["dsig_"+crosspol_gmf+"_NAME"], xr_dataset.incidence,
773
+ xr_dataset['sigma0_ocean'].sel(pol=crosspol), xr_dataset.nesz_cross_flattened)
750
774
  else:
751
- xr_dataset = xr_dataset.assign(
752
- nesz_cross_final=(['line', 'sample'], xr_dataset.nesz.sel(pol=crosspol).values))
753
- xr_dataset['nesz_cross_final'].attrs["comment"] = 'nesz has not been flattened'
754
-
755
- xr_dataset.nesz_cross_final.attrs['units'] = 'm^2 / m^2'
756
- xr_dataset.nesz_cross_final.attrs['long_name'] = 'Noise Equivalent SigmaNaught'
757
-
758
- # dsig
759
- sigma0_ocean_cross = xr_dataset['sigma0_ocean'].sel(pol=crosspol)
760
- xr_dataset["dsig_cross"] = windspeed.get_dsig(config["dsig_"+crosspol_gmf+"_NAME"], xr_dataset.incidence,
761
- sigma0_ocean_cross, xr_dataset.nesz_cross_final)
775
+ # dsig
776
+ xr_dataset["dsig_cross"] = windspeed.get_dsig(config["dsig_"+crosspol_gmf+"_NAME"], xr_dataset.incidence,
777
+ xr_dataset['sigma0_ocean'].sel(pol=crosspol), xr_dataset['sigma0_ocean'].sel(pol=crosspol))
762
778
 
763
779
  xr_dataset.dsig_cross.attrs['comment'] = 'variable used to ponderate copol and crosspol'
764
- dsig_cross = xr_dataset.dsig_cross
765
- else:
766
- sigma0_ocean_cross = None
767
- dsig_cross = 0.1 # default value set in xsarsea
780
+ xr_dataset.dsig_cross.attrs['formula_used'] = config["dsig_" +
781
+ crosspol_gmf+"_NAME"]
782
+ xr_dataset.dsig_cross.attrs['apply_flattening'] = str(
783
+ config["apply_flattening"])
768
784
 
769
785
  if ((recalibration) & ("SENTINEL" in sensor_longname)):
770
786
  xr_dataset.attrs["path_aux_pp1_new"] = os.path.basename(os.path.dirname(
@@ -785,19 +801,21 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
785
801
 
786
802
  # adding sigma0 detrend
787
803
  xr_dataset_100['sigma0_detrend'] = xsarsea.sigma0_detrend(
788
- xr_dataset_100.sigma0.sel(pol=copol), xr_dataset_100.incidence, model=model_vv)
804
+ xr_dataset_100.sigma0.sel(pol=copol), xr_dataset_100.incidence, model=model_co)
805
+
806
+ if dual_pol:
807
+ xr_dataset_100['sigma0_detrend_cross'] = xsarsea.sigma0_detrend(
808
+ xr_dataset_100.sigma0.sel(pol=crosspol), xr_dataset_100.incidence, model=model_cross)
789
809
 
790
- xr_dataset_100['sigma0_detrend_cross'] = xsarsea.sigma0_detrend(
791
- xr_dataset_100.sigma0.sel(pol=crosspol), xr_dataset_100.incidence, model=model_vh)
810
+ sigma0_detrend_combined = xr.concat(
811
+ [xr_dataset_100['sigma0_detrend'],
812
+ xr_dataset_100['sigma0_detrend_cross']],
813
+ dim='pol'
814
+ )
815
+ sigma0_detrend_combined['pol'] = [copol, crosspol]
792
816
 
793
- sigma0_detrend_combined = xr.concat(
794
- [xr_dataset_100['sigma0_detrend'],
795
- xr_dataset_100['sigma0_detrend_cross']],
796
- dim='pol'
797
- )
798
- sigma0_detrend_combined['pol'] = [copol, crosspol]
817
+ xr_dataset_100['sigma0_detrend'] = sigma0_detrend_combined
799
818
 
800
- xr_dataset_100['sigma0_detrend'] = sigma0_detrend_combined
801
819
  xr_dataset_100.land_mask.values = binary_dilation(xr_dataset_100['land_mask'].values.astype('uint8'),
802
820
  structure=np.ones((3, 3), np.uint8), iterations=3)
803
821
  xr_dataset_100['sigma0_detrend'] = xr.where(
@@ -806,7 +824,7 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
806
824
  xr_dataset['streaks_direction'] = get_streaks(
807
825
  xr_dataset, xr_dataset_100)
808
826
 
809
- return xr_dataset, dual_pol, copol, crosspol, copol_gmf, crosspol_gmf, model_vv, model_vh, sigma0_ocean_cross, dsig_cross, sensor_longname, out_file, config
827
+ return xr_dataset, out_file, config
810
828
 
811
829
 
812
830
  def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add_streaks=False, resolution='1000m'):
@@ -836,9 +854,26 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
836
854
  final dataset
837
855
  """
838
856
 
839
- xr_dataset, dual_pol, copol, crosspol, copol_gmf, crosspol_gmf, model_vv, model_vh, sigma0_ocean_cross, dsig_cross, sensor_longname, out_file, config = preprocess(
857
+ xr_dataset, out_file, config = preprocess(
840
858
  filename, outdir, config_path, overwrite, add_streaks, resolution)
841
859
 
860
+ model_co = config["l2_params"]["model_co"]
861
+ model_cross = config["l2_params"]["model_cross"]
862
+ copol = config["l2_params"]["copol"]
863
+ crosspol = config["l2_params"]["crosspol"]
864
+ copol_gmf = config["l2_params"]["copol_gmf"]
865
+ crosspol_gmf = config["l2_params"]["crosspol_gmf"]
866
+ dual_pol = config["l2_params"]["dual_pol"]
867
+ ancillary_name = config["ancillary"]
868
+ sensor_longname = config["sensor_longname"]
869
+
870
+ if dual_pol:
871
+ sigma0_ocean_cross = xr_dataset['sigma0_ocean'].sel(pol=crosspol)
872
+ dsig_cross = xr_dataset['dsig_cross']
873
+ else:
874
+ sigma0_ocean_cross = None
875
+ dsig_cross = 0.1 # default value set in xsarsea
876
+
842
877
  kwargs = {
843
878
  "inc_step_lr": config.pop("inc_step_lr", None),
844
879
  "wpsd_step_lr": config.pop("wspd_step_lr", None),
@@ -849,6 +884,11 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
849
884
  "resolution": config.pop("resolution", None),
850
885
  }
851
886
 
887
+ logging.info("Checking incidence range within LUTS incidence range")
888
+ #  warning if incidence is out of lut incidence range
889
+ inc_check_co, inc_check_cross = check_incidence_range(
890
+ xr_dataset['incidence'], [model_co, model_cross], **kwargs)
891
+
852
892
  wind_co, wind_dual, windspeed_cr = inverse(dual_pol,
853
893
  inc=xr_dataset['incidence'],
854
894
  sigma0=xr_dataset['sigma0_ocean'].sel(
@@ -856,62 +896,62 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
856
896
  sigma0_dual=sigma0_ocean_cross,
857
897
  ancillary_wind=xr_dataset['ancillary_wind'],
858
898
  dsig_cr=dsig_cross,
859
- model_vv=model_vv,
860
- model_vh=model_vh,
899
+ model_co=model_co,
900
+ model_cross=model_cross,
861
901
  ** kwargs)
902
+ wind_co.compute()
862
903
 
863
904
  # windspeed_co
864
905
  xr_dataset['windspeed_co'] = np.abs(wind_co)
865
906
  xr_dataset["windspeed_co"].attrs["units"] = "m.s⁻1"
866
907
  xr_dataset["windspeed_co"].attrs["long_name"] = "Wind speed inverted from model %s (%s)" % (
867
- model_vv, copol)
908
+ model_co, copol)
868
909
  xr_dataset["windspeed_co"].attrs["standart_name"] = "wind_speed"
869
910
  xr_dataset["windspeed_co"].attrs["model"] = wind_co.attrs["model"]
870
911
  del xr_dataset["windspeed_co"].attrs['comment']
871
912
 
872
913
  # winddir_co
873
- xr_dataset['winddir_co'] = (
874
- 90 - (np.angle(wind_co, deg=True)) + xr_dataset.ground_heading) % 360
875
- xr_dataset["winddir_co"].attrs["units"] = "degrees_north"
876
- xr_dataset["winddir_co"].attrs["long_name"] = "Wind direction in meteorological convention, 0=North, 90=East, inverted from model %s (%s)" % (
877
- model_vv, copol)
878
- xr_dataset["winddir_co"].attrs["standart_name"] = "wind_direction"
879
- xr_dataset["winddir_co"].attrs["model"] = wind_co.attrs["model"]
914
+ xr_dataset['winddir_co'] = transform_winddir(
915
+ wind_co, xr_dataset.ground_heading, winddir_convention=config["winddir_convention"])
916
+ xr_dataset['winddir_co'].attrs["model"] = "%s (%s)" % (model_co, copol)
880
917
 
881
918
  # windspeed_dual / windspeed_cr / /winddir_dual / winddir_cr
882
- if dual_pol:
919
+ if dual_pol and wind_dual is not None:
920
+ wind_dual.compute()
883
921
  xr_dataset['windspeed_dual'] = np.abs(wind_dual)
884
922
  xr_dataset["windspeed_dual"].attrs["units"] = "m.s⁻1"
885
923
  xr_dataset["windspeed_dual"].attrs["long_name"] = "Wind speed inverted from model %s (%s) & %s (%s)" % (
886
- model_vv, copol, model_vh, crosspol)
924
+ model_co, copol, model_cross, crosspol)
887
925
  xr_dataset["windspeed_dual"].attrs["standart_name"] = "wind_speed"
888
926
  xr_dataset["windspeed_dual"].attrs["model"] = wind_dual.attrs["model"]
889
927
  del xr_dataset["windspeed_dual"].attrs['comment']
890
928
 
891
- xr_dataset['winddir_dual'] = (
892
- 90 - (np.angle(wind_dual, deg=True)) + xr_dataset.ground_heading) % 360
893
- xr_dataset["winddir_dual"].attrs["units"] = "degrees_north"
894
- xr_dataset["winddir_dual"].attrs["long_name"] = "Wind direction in meteorological convention, 0=North, 90=East inverted from model %s (%s) & %s (%s)" % (
895
- model_vv, copol, model_vh, crosspol)
896
- xr_dataset["winddir_dual"].attrs["standart_name"] = "wind_direction"
897
- xr_dataset["winddir_dual"].attrs["model"] = wind_dual.attrs["model"]
929
+ xr_dataset['winddir_dual'] = transform_winddir(
930
+ wind_dual, xr_dataset.ground_heading, winddir_convention=config["winddir_convention"])
931
+ xr_dataset["winddir_dual"].attrs["model"] = "winddir_dual is a copy of copol wind direction"
898
932
 
899
933
  xr_dataset = xr_dataset.assign(
900
934
  windspeed_cross=(['line', 'sample'], windspeed_cr))
901
935
  xr_dataset["windspeed_cross"].attrs["units"] = "m.s⁻1"
902
936
  xr_dataset["windspeed_cross"].attrs["long_name"] = "Wind Speed inverted from model %s (%s)" % (
903
- model_vh, crosspol)
937
+ model_cross, crosspol)
904
938
  xr_dataset["windspeed_cross"].attrs["standart_name"] = "wind_speed"
905
- xr_dataset["windspeed_cross"].attrs["model"] = "%s" % (model_vh)
939
+ xr_dataset["windspeed_cross"].attrs["model"] = "%s" % (model_cross)
906
940
 
907
941
  xr_dataset['winddir_cross'] = xr_dataset['winddir_dual'].copy()
908
- xr_dataset["winddir_cross"].attrs["units"] = "degrees_north"
909
- xr_dataset["winddir_cross"].attrs["long_name"] = "Wind direction in meteorological convention, 0=North, 90=East, copied from dualpol"
910
- xr_dataset["winddir_cross"].attrs["standart_name"] = "wind_direction"
911
- xr_dataset["winddir_cross"].attrs["model"] = "No model used ; content is a copy of dualpol wind direction"
942
+ xr_dataset['winddir_cross'].attrs = xr_dataset['winddir_dual'].attrs
943
+ xr_dataset["winddir_cross"].attrs["model"] = "winddir_cross is a copy of copol wind direction"
944
+
945
+ if config["winddir_convention"] == "oceanographic":
946
+ attrs = xr_dataset['ancillary_wind_direction'].attrs
947
+ xr_dataset['ancillary_wind_direction'] = xsarsea.dir_meteo_to_oceano(
948
+ xr_dataset['ancillary_wind_direction'])
949
+ xr_dataset['ancillary_wind_direction'].attrs = attrs
950
+ xr_dataset['ancillary_wind_direction'].attrs[
951
+ "long_name"] = f"{ancillary_name} wind direction in oceanographic convention (clockwise, to), ex: 0°=to north, 90°=to east"
912
952
 
913
953
  xr_dataset, encoding = makeL2asOwi(
914
- xr_dataset, dual_pol, copol, crosspol, add_streaks=add_streaks)
954
+ xr_dataset, dual_pol, copol, crosspol, add_streaks=add_streaks, apply_flattening=config["apply_flattening"])
915
955
 
916
956
  #  add attributes
917
957
  firstMeasurementTime = None
@@ -943,7 +983,7 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
943
983
  "xsar_version": xsar.__version__,
944
984
  "xsarsea_version": xsarsea.__version__,
945
985
  "pythonVersion": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
946
- "polarisationRatio": "/",
986
+ "polarisationRatio": get_pol_ratio_name(model_co),
947
987
  "l2ProcessingUtcTime": datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ"),
948
988
  "processingCenter": "IFREMER",
949
989
  "firstMeasurementTime": firstMeasurementTime,
@@ -960,25 +1000,32 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
960
1000
  "wnf_3km_average": "False",
961
1001
  "owiWindSpeedSrc": "owiWindSpeed",
962
1002
  "owiWindDirectionSrc": "/",
963
- "ancillary_source": xr_dataset.attrs['ancillary_source']
1003
+ "ancillary_source": xr_dataset.attrs['ancillary_source'],
1004
+ "winddir_convention": config["winddir_convention"],
1005
+ "incidence_within_lut_copol_incidence_range": str(inc_check_co),
1006
+ "incidence_within_lut_crosspol_incidence_range": str(inc_check_cross),
1007
+ "swath": xr_dataset.attrs['swath'],
1008
+ "footprint": xr_dataset.attrs['footprint'],
1009
+ "coverage": xr_dataset.attrs['coverage'],
1010
+
964
1011
  }
965
1012
 
966
1013
  for recalib_attrs in ["path_aux_pp1_new", 'path_aux_pp1_old', "path_aux_cal_new", "path_aux_cal_old"]:
967
1014
  if recalib_attrs in xr_dataset.attrs:
968
1015
  attrs[recalib_attrs] = xr_dataset.attrs[recalib_attrs]
969
1016
 
970
- # new one to match convention
971
- _S1_added_attrs = ["product", "ipf", "multi_dataset", "footprint",
972
- "coverage", "orbit_pass", "platform_heading"]
973
- _RS2_added_attrs = ["passDirection", "swath", "footprint", "coverage"]
974
- _RCM_added_attrs = ["swath", "footprint", "coverage", "productId",]
1017
+ for arg in ["passDirection", "orbit_pass"]:
1018
+ if arg in xr_dataset.attrs:
1019
+ attrs["passDirection"] = xr_dataset.attrs[arg]
1020
+
1021
+ _S1_added_attrs = ["ipf", "platform_heading"]
1022
+ _RCM_added_attrs = ["productId"]
975
1023
 
976
- for sup_attr in _S1_added_attrs + _RS2_added_attrs + _RCM_added_attrs:
1024
+ for sup_attr in _S1_added_attrs + _RCM_added_attrs:
977
1025
  if sup_attr in xr_dataset.attrs:
978
1026
  attrs[sup_attr] = xr_dataset.attrs[sup_attr]
979
- for var in ['footprint', 'multidataset']:
980
- if var in attrs:
981
- attrs[var] = str(attrs[var])
1027
+
1028
+ attrs['footprint'] = str(attrs['footprint'])
982
1029
 
983
1030
  # add in kwargs in attrs
984
1031
  for key in kwargs:
@@ -1000,3 +1047,49 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
1000
1047
  logging.info("OK for %s ", os.path.basename(filename))
1001
1048
 
1002
1049
  return out_file, xr_dataset
1050
+
1051
+
1052
+ def transform_winddir(wind_cpx, ground_heading, winddir_convention='meteorological'):
1053
+ """
1054
+
1055
+ Parameters
1056
+ ----------
1057
+ wind_cpx : xr.DataArray | np.complex64
1058
+ complex wind, relative to antenna, anticlockwise
1059
+
1060
+ ground_heading : xr.DataArray
1061
+ heading angle in degrees
1062
+
1063
+ winddir_convention : str
1064
+ wind direction convention to use, either 'meteorological' or 'oceanographic'
1065
+
1066
+ Returns
1067
+ -------
1068
+ xr.DataArray
1069
+ wind direction in degrees in the selected convention with appropriate long_name attribute
1070
+ """
1071
+ # to meteo winddir_convention
1072
+ dataArray = xsarsea.dir_sample_to_meteo(
1073
+ np.angle(wind_cpx, deg=True), ground_heading)
1074
+ long_name = "Wind direction in meteorological convention (clockwise, from), ex: 0°=from north, 90°=from east"
1075
+
1076
+ if winddir_convention == "meteorological":
1077
+ # do nothing
1078
+ pass
1079
+ elif winddir_convention == "oceanographic":
1080
+ # to oceano winddir_convention
1081
+ dataArray = xsarsea.dir_meteo_to_oceano(dataArray)
1082
+ long_name = "Wind direction in oceanographic convention (clockwise, to), ex: 0°=to north, 90°=to east"
1083
+ else:
1084
+ #  warning
1085
+ logging.warning(
1086
+ f"wind direction convention {winddir_convention} is not supported, using meteorological",)
1087
+ long_name = "Wind direction in meteorological convention (clockwise, from), ex: 0°=from north, 90°=from east"
1088
+
1089
+ dataArray = xsarsea.dir_to_360(dataArray)
1090
+ dataArray.attrs = {}
1091
+ dataArray.attrs["units"] = "degrees_north"
1092
+ dataArray.attrs["long_name"] = long_name
1093
+ dataArray.attrs["standart_name"] = "wind_direction"
1094
+
1095
+ return dataArray
grdwindinversion/main.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from grdwindinversion.inversion import makeL2
2
- from grdwindinversion.utils import get_memory_usage
2
+ from grdwindinversion.utils_memory import get_memory_usage
3
3
  import grdwindinversion
4
4
  import time
5
5
  import logging
grdwindinversion/utils.py CHANGED
@@ -1,42 +1,83 @@
1
- def get_memory_usage(unit='Go', var='ru_maxrss', force_psutil=False):
1
+ import logging
2
+ import xsarsea
3
+
4
+
5
+ def check_incidence_range(incidence, models, **kwargs):
2
6
  """
3
- var str: ru_maxrss or ru_ixrss or ru_idrss or ru_isrss or current
7
+ Check if the incidence range of the dataset is within the range of the LUT of the model.
8
+ If not, warn the user : inversion will be approximate.
9
+
10
+ Parameters
11
+ ----------
12
+ incidence : xr.DataArray
13
+ incidence angle in degrees
14
+ models : list of str
15
+ list of model names
16
+
4
17
  Returns
5
18
  -------
19
+ list of bool
20
+ for each model,
21
+ True if the incidence range is within the range of the LUT of the model (correct)
22
+ False otherwise
23
+ """
24
+ if isinstance(models, str):
25
+ models = [models]
26
+ elif not isinstance(models, list):
27
+ raise TypeError("models should be a string or a list of strings")
28
+
29
+ rets = []
30
+ for model_name in models:
31
+ lut_range = xsarsea.windspeed.get_model(model_name).inc_range
32
+ if 'inc_range' in kwargs:
33
+ logging.debug(
34
+ f"GMF {model_name} inc_range will be changed by kwargs to {kwargs['inc_range']}")
35
+ lut_range = kwargs['inc_range']
36
+
37
+ inc_range = [incidence.values.min(), incidence.values.max()]
38
+ if (inc_range[0] >= lut_range[0] and inc_range[1] <= lut_range[1]):
39
+ rets.append(True)
40
+ else:
41
+ logging.warn(
42
+ f"incidence range {inc_range} is not within the range of the LUT of the model {model_name} {lut_range} : inversion will be approximate using LUT minmium|maximum incidences")
43
+ rets.append(False)
44
+
45
+ return rets
6
46
 
47
+
48
+ def get_pol_ratio_name(model_co):
49
+ """
50
+ Return polarization ration name of copol model
51
+
52
+ Parameters
53
+ ----------
54
+ model_co : str
55
+ copol model name
56
+
57
+ Returns
58
+ -------
59
+ str
60
+ if pol = 'HH', return polarization ratio name ; else return '/'
7
61
  """
8
- if unit == 'Go':
9
- factor = 1000000.
10
- elif unit == 'Mo':
11
- factor = 1000.
12
- elif unit == 'Ko':
13
- factor = 1.
62
+
63
+ model = xsarsea.windspeed.get_model(model_co)
64
+ if model.pol == 'HH':
65
+ try:
66
+ import re
67
+
68
+ def check_format(s):
69
+ pattern = r'^([a-zA-Z0-9]+)_R(high|low)_hh_([a-zA-Z0-9_]+)$'
70
+ match = re.match(pattern, s)
71
+ if match:
72
+ vvgmf, res, polrationame = match.groups()
73
+ return polrationame
74
+ else:
75
+ logging.warn(
76
+ f"String format is not correct for polarization ratio name = {s}\nReturning '/'")
77
+ return "/"
78
+ get_pol_ratio_name = check_format(model_co)
79
+ return get_pol_ratio_name
80
+ except AttributeError:
81
+ return "not_written_in_lut"
14
82
  else:
15
- raise Exception('not handle unit')
16
-
17
- try:
18
- if force_psutil:
19
- on_purpose_error
20
- import resource
21
- mems = {}
22
- mems['ru_maxrss'] = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / factor
23
- mems['ru_ixrss'] = resource.getrusage(resource.RUSAGE_SELF).ru_ixrss / factor
24
- mems['ru_idrss'] = resource.getrusage(resource.RUSAGE_SELF).ru_idrss / factor
25
- mems['ru_isrss'] = resource.getrusage(resource.RUSAGE_SELF).ru_isrss / factor
26
- mems['current'] = getCurrentMemoryUsage() / factor
27
- # memory_used_go = resource.getrusage(resource.RUSAGE_SELF).get(var) /factor
28
- memory_used_go = mems[var]
29
- except: # on windows resource is not usable
30
- import psutil
31
- memory_used_go = psutil.virtual_memory().used / factor / 1000.
32
- str_mem = 'RAM usage: %1.1f %s' % (memory_used_go, unit)
33
- return str_mem
34
-
35
-
36
- def getCurrentMemoryUsage():
37
- ''' Memory usage in kB '''
38
-
39
- with open('/proc/self/status') as f:
40
- memusage = f.read().split('VmRSS:')[1].split('\n')[0][:-3]
41
-
42
- return int(memusage.strip())
83
+ return '/'
@@ -0,0 +1,46 @@
1
+ def get_memory_usage(unit='Go', var='ru_maxrss', force_psutil=False):
2
+ """
3
+ var str: ru_maxrss or ru_ixrss or ru_idrss or ru_isrss or current
4
+ Returns
5
+ -------
6
+
7
+ """
8
+ if unit == 'Go':
9
+ factor = 1000000.
10
+ elif unit == 'Mo':
11
+ factor = 1000.
12
+ elif unit == 'Ko':
13
+ factor = 1.
14
+ else:
15
+ raise Exception('not handle unit')
16
+
17
+ try:
18
+ if force_psutil:
19
+ on_purpose_error
20
+ import resource
21
+ mems = {}
22
+ mems['ru_maxrss'] = resource.getrusage(
23
+ resource.RUSAGE_SELF).ru_maxrss / factor
24
+ mems['ru_ixrss'] = resource.getrusage(
25
+ resource.RUSAGE_SELF).ru_ixrss / factor
26
+ mems['ru_idrss'] = resource.getrusage(
27
+ resource.RUSAGE_SELF).ru_idrss / factor
28
+ mems['ru_isrss'] = resource.getrusage(
29
+ resource.RUSAGE_SELF).ru_isrss / factor
30
+ mems['current'] = getCurrentMemoryUsage() / factor
31
+ # memory_used_go = resource.getrusage(resource.RUSAGE_SELF).get(var) /factor
32
+ memory_used_go = mems[var]
33
+ except: # on windows resource is not usable
34
+ import psutil
35
+ memory_used_go = psutil.virtual_memory().used / factor / 1000.
36
+ str_mem = 'RAM usage: %1.1f %s' % (memory_used_go, unit)
37
+ return str_mem
38
+
39
+
40
+ def getCurrentMemoryUsage():
41
+ ''' Memory usage in kB '''
42
+
43
+ with open('/proc/self/status') as f:
44
+ memusage = f.read().split('VmRSS:')[1].split('\n')[0][:-3]
45
+
46
+ return int(memusage.strip())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: grdwindinversion
3
- Version: 0.2.3.post15
3
+ Version: 0.2.5
4
4
  Summary: Package to perform Wind inversion from GRD Level-1 SAR images
5
5
  License: MIT
6
6
  Classifier: Development Status :: 2 - Pre-Alpha
@@ -0,0 +1,19 @@
1
+ grdwindinversion/.gitignore,sha256=vmDRHGeESYckUdUztsPr7u6ZNfBYMCVR3GE3AJCBnsY,1204
2
+ grdwindinversion/__init__.py,sha256=VQc2V_j124NX2Gp06Go1oSZDSF4lBIVPYT-W9ZkNBXE,415
3
+ grdwindinversion/config_prod.yaml,sha256=sYF1TAYM5ifFqlGdVwz8ygydo3_6KnEqYb7eBWYeDiw,1047
4
+ grdwindinversion/config_prod_recal.yaml,sha256=WzucOsKVCK-lw5kRn-4llONwBxl8TogYrReeLycXC6k,1006
5
+ grdwindinversion/data_config.yaml,sha256=YbbgxqpgasR5RINO29Ep_1cELdZotoylLzn6Qh7f6LM,473
6
+ grdwindinversion/inversion.py,sha256=8--TyUzvsb3m9I2ajGelW_Fj2Us3kzKZrr9aysX4vxE,47299
7
+ grdwindinversion/load_config.py,sha256=bt7EXTmXfbuK0oNR-qwtijv_iZBQzi5BtY92RGKEo4Y,651
8
+ grdwindinversion/main.py,sha256=0DLDW0i14CWhDKAGegzOKlWnCkag_cGn3vzMIAKcv4g,2658
9
+ grdwindinversion/streaks.py,sha256=SucALdajwAr-tcqwJs6KOj8CQ7qQPp3JUKSh2dHAJTY,2950
10
+ grdwindinversion/utils.py,sha256=WmHL-NFv3B0PDbCauhBMeqW2ue_340iALmPH1kSd_Pc,2593
11
+ grdwindinversion/utils_memory.py,sha256=1N3Kh4qVZPELPU6I4onbmkur1CZd7EHZqfbSAa6eaVc,1480
12
+ grdwindinversion/.github/ISSUE_TEMPLATE.md,sha256=qiM_a7CCUz3fSrz3Q20Se1nwPNFS8QCc8tkwK_0DSCo,327
13
+ grdwindinversion-0.2.5.dist-info/AUTHORS.rst,sha256=KmhW_5LBKGTIGwWEVkoTm1qx_bvdDR3yYL-1cwbDOFQ,218
14
+ grdwindinversion-0.2.5.dist-info/LICENSE,sha256=-B8mBiTeY3J7OLuayiV1myqmc7yeijBc7s34kc8RTmg,1075
15
+ grdwindinversion-0.2.5.dist-info/METADATA,sha256=ei8sU-TLmzUASSVL80h4zQVwV83MHApbNidM9yriEJA,2392
16
+ grdwindinversion-0.2.5.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
17
+ grdwindinversion-0.2.5.dist-info/entry_points.txt,sha256=2rjvlVCy0iasRXjOz3kOIGuy2OCGQ-VTNuwuViQ6cMM,95
18
+ grdwindinversion-0.2.5.dist-info/top_level.txt,sha256=z6lPix3QPEYOo37qq8plA2hY7S3C8MQZY81agRlksMI,17
19
+ grdwindinversion-0.2.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (74.1.2)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,28 +0,0 @@
1
- # Config file for automatic testing at travis-ci.com
2
-
3
- language: python
4
- python:
5
- - 3.8
6
- - 3.7
7
- - 3.6
8
-
9
- # Command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors
10
- install: pip install -U tox-travis
11
-
12
- # Command to run tests, e.g. python setup.py test
13
- script: tox
14
-
15
- # Assuming you have installed the travis-ci CLI tool, after you
16
- # create the Github repo and add it to Travis, run the
17
- # following command to finish PyPI deployment setup:
18
- # $ travis encrypt --add deploy.password
19
- deploy:
20
- provider: pypi
21
- distributions: sdist bdist_wheel
22
- user: agrouaze
23
- password:
24
- secure: PLEASE_REPLACE_ME
25
- on:
26
- tags: true
27
- repo: agrouaze/grdwindinversion
28
- python: 3.8
@@ -1,19 +0,0 @@
1
- grdwindinversion/.gitignore,sha256=vmDRHGeESYckUdUztsPr7u6ZNfBYMCVR3GE3AJCBnsY,1204
2
- grdwindinversion/.travis.yml,sha256=q13Gro7mMK7xN5eDEhyMIgcXIYsgSQfAEzk4dv34ie0,694
3
- grdwindinversion/__init__.py,sha256=VQc2V_j124NX2Gp06Go1oSZDSF4lBIVPYT-W9ZkNBXE,415
4
- grdwindinversion/config_prod.yaml,sha256=tIWn83LXNIl9l6w2I4TU-ZIk35EZm44GeIXklgYqFw8,1010
5
- grdwindinversion/config_prod_recal.yaml,sha256=WzucOsKVCK-lw5kRn-4llONwBxl8TogYrReeLycXC6k,1006
6
- grdwindinversion/data_config.yaml,sha256=YbbgxqpgasR5RINO29Ep_1cELdZotoylLzn6Qh7f6LM,473
7
- grdwindinversion/inversion.py,sha256=WoMgi4gRjMU11FHhb0xlZS_Hl7KKY5W_gbvI86cGNh8,43984
8
- grdwindinversion/load_config.py,sha256=bt7EXTmXfbuK0oNR-qwtijv_iZBQzi5BtY92RGKEo4Y,651
9
- grdwindinversion/main.py,sha256=l5PEyIkqQ7Vjha0zs9FO3uqTpCVdNdoen5Zu8svJH8g,2651
10
- grdwindinversion/streaks.py,sha256=SucALdajwAr-tcqwJs6KOj8CQ7qQPp3JUKSh2dHAJTY,2950
11
- grdwindinversion/utils.py,sha256=nIao7g_lGKgzL5y_ipBsxLoQDetf0OLrPy8FEd4eyWU,1428
12
- grdwindinversion/.github/ISSUE_TEMPLATE.md,sha256=qiM_a7CCUz3fSrz3Q20Se1nwPNFS8QCc8tkwK_0DSCo,327
13
- grdwindinversion-0.2.3.post15.dist-info/AUTHORS.rst,sha256=KmhW_5LBKGTIGwWEVkoTm1qx_bvdDR3yYL-1cwbDOFQ,218
14
- grdwindinversion-0.2.3.post15.dist-info/LICENSE,sha256=-B8mBiTeY3J7OLuayiV1myqmc7yeijBc7s34kc8RTmg,1075
15
- grdwindinversion-0.2.3.post15.dist-info/METADATA,sha256=-EzqUV0fqvzAXgjgZYEKC5cloJCV0Dh9vahVZINXMbI,2399
16
- grdwindinversion-0.2.3.post15.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
17
- grdwindinversion-0.2.3.post15.dist-info/entry_points.txt,sha256=2rjvlVCy0iasRXjOz3kOIGuy2OCGQ-VTNuwuViQ6cMM,95
18
- grdwindinversion-0.2.3.post15.dist-info/top_level.txt,sha256=z6lPix3QPEYOo37qq8plA2hY7S3C8MQZY81agRlksMI,17
19
- grdwindinversion-0.2.3.post15.dist-info/RECORD,,