grdwindinversion 0.2.3.post15__py3-none-any.whl → 0.2.4__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_MODE}_${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_MODE.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,20 +225,20 @@ 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
  --------
@@ -248,12 +249,12 @@ def inverse(dual_pol, inc, sigma0, sigma0_dual, ancillary_wind, dsig_cr, model_v
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
@@ -532,6 +533,7 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
532
533
  Loader=yaml.FullLoader
533
534
  )
534
535
  try:
536
+ # check if sensor is in the config
535
537
  config = config_base[sensor]
536
538
  except Exception:
537
539
  raise KeyError("sensor %s not in this config" % sensor)
@@ -543,6 +545,19 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
543
545
  meta = fct_meta(filename)
544
546
 
545
547
  no_subdir_cfg = config_base.get("no_subdir", False)
548
+ config["no_subdir"] = no_subdir_cfg
549
+
550
+ if "winddir_convention" in config_base:
551
+ winddir_convention = config_base["winddir_convention"]
552
+ else:
553
+ winddir_convention = "meteorological"
554
+ logging.warning(
555
+ f'Using meteorological convention because "winddir_convention" was not found in config.')
556
+ config["winddir_convention"] = winddir_convention
557
+
558
+ # creating a dictionnary of parameters
559
+ config["l2_params"] = {}
560
+
546
561
  out_file = getOutputName2(filename, outdir, sensor,
547
562
  meta, subdir=not no_subdir_cfg)
548
563
 
@@ -607,24 +622,34 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
607
622
  copol_gmf = 'HH'
608
623
  crosspol_gmf = 'VH'
609
624
 
610
- model_vv = config["GMF_"+copol_gmf+"_NAME"]
611
- model_vh = config["GMF_"+crosspol_gmf+"_NAME"]
625
+ model_co = config["GMF_"+copol_gmf+"_NAME"]
626
+ model_cross = config["GMF_"+crosspol_gmf+"_NAME"]
627
+
628
+ # register paramaters in config
629
+ config["l2_params"]["dual_pol"] = dual_pol
630
+ config["l2_params"]["copol"] = copol
631
+ config["l2_params"]["crosspol"] = crosspol
632
+ config["l2_params"]["copol_gmf"] = copol_gmf
633
+ config["l2_params"]["crosspol_gmf"] = crosspol_gmf
634
+ config["l2_params"]["model_co"] = model_co
635
+ config["l2_params"]["model_cross"] = model_cross
636
+ config["sensor_longname"] = sensor_longname
612
637
 
613
638
  # need to load gmfs before inversion
614
- gmfs_impl = [x for x in [model_vv, model_vh] if "gmf_" in x]
639
+ gmfs_impl = [x for x in [model_co, model_cross] if "gmf_" in x]
615
640
  windspeed.gmfs.GmfModel.activate_gmfs_impl(gmfs_impl)
616
- sarwings_luts = [x for x in [model_vv, model_vh]
641
+ sarwings_luts = [x for x in [model_co, model_cross]
617
642
  if x.startswith("sarwing_lut_")]
618
643
 
619
644
  if len(sarwings_luts) > 0:
620
645
  windspeed.register_sarwing_luts(getConf()["sarwing_luts_path"])
621
646
 
622
- nc_luts = [x for x in [model_vv, model_vh] if x.startswith("nc_lut")]
647
+ nc_luts = [x for x in [model_co, model_cross] if x.startswith("nc_lut")]
623
648
 
624
649
  if len(nc_luts) > 0:
625
650
  windspeed.register_nc_luts(getConf()["nc_luts_path"])
626
651
 
627
- if (model_vv == "gmf_cmod7"):
652
+ if (model_co == "gmf_cmod7"):
628
653
  windspeed.register_cmod7(getConf()["lut_cmod7_path"])
629
654
  #  Step 2 - clean and prepare dataset
630
655
 
@@ -690,7 +715,7 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
690
715
  xr_dataset['ancillary_wind_direction'].attrs = {}
691
716
  xr_dataset['ancillary_wind_direction'].attrs['units'] = 'degrees_north'
692
717
  xr_dataset['ancillary_wind_direction'].attrs[
693
- 'long_name'] = f'{ancillary_name} wind direction (meteorological convention)'
718
+ 'long_name'] = f'{ancillary_name} wind direction in meteorological convention (clockwise, from), ex: 0°=from north, 90°=from east'
694
719
  xr_dataset['ancillary_wind_direction'].attrs['standart_name'] = 'wind_direction'
695
720
 
696
721
  xr_dataset['ancillary_wind_speed'] = np.sqrt(
@@ -705,7 +730,7 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
705
730
  xr_dataset['ancillary_wind_speed'].attrs['standart_name'] = 'wind_speed'
706
731
 
707
732
  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(
733
+ (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
734
  *xr_dataset['ancillary_wind_speed'].dims)
710
735
 
711
736
  xr_dataset.attrs['ancillary_source'] = xr_dataset['model_U10'].attrs['history'].split('decoded: ')[
@@ -734,13 +759,13 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
734
759
  xr_dataset['sigma0_ocean_raw'].attrs = xr_dataset['sigma0_raw'].attrs
735
760
 
736
761
  xr_dataset['sigma0_detrend'] = xsarsea.sigma0_detrend(
737
- xr_dataset.sigma0.sel(pol=copol), xr_dataset.incidence, model=model_vv)
762
+ xr_dataset.sigma0.sel(pol=copol), xr_dataset.incidence, model=model_co)
738
763
 
739
764
  # processing
740
765
  if dual_pol:
741
766
 
742
767
  xr_dataset['sigma0_detrend_cross'] = xsarsea.sigma0_detrend(
743
- xr_dataset.sigma0.sel(pol=crosspol), xr_dataset.incidence, model=model_vh)
768
+ xr_dataset.sigma0.sel(pol=crosspol), xr_dataset.incidence, model=model_cross)
744
769
  if config["apply_flattening"]:
745
770
  xr_dataset = xr_dataset.assign(nesz_cross_final=(
746
771
  ['line', 'sample'], windspeed.nesz_flattening(xr_dataset.nesz.sel(pol=crosspol), xr_dataset.incidence)))
@@ -756,15 +781,14 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
756
781
  xr_dataset.nesz_cross_final.attrs['long_name'] = 'Noise Equivalent SigmaNaught'
757
782
 
758
783
  # dsig
759
- sigma0_ocean_cross = xr_dataset['sigma0_ocean'].sel(pol=crosspol)
760
784
  xr_dataset["dsig_cross"] = windspeed.get_dsig(config["dsig_"+crosspol_gmf+"_NAME"], xr_dataset.incidence,
761
- sigma0_ocean_cross, xr_dataset.nesz_cross_final)
785
+ xr_dataset['sigma0_ocean'].sel(pol=crosspol), xr_dataset.nesz_cross_final)
762
786
 
763
787
  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
788
+ xr_dataset.dsig_cross.attrs['formula_used'] = config["dsig_" +
789
+ crosspol_gmf+"_NAME"]
790
+ xr_dataset.dsig_cross.attrs['apply_flattening'] = str(
791
+ config["apply_flattening"])
768
792
 
769
793
  if ((recalibration) & ("SENTINEL" in sensor_longname)):
770
794
  xr_dataset.attrs["path_aux_pp1_new"] = os.path.basename(os.path.dirname(
@@ -785,19 +809,21 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
785
809
 
786
810
  # adding sigma0 detrend
787
811
  xr_dataset_100['sigma0_detrend'] = xsarsea.sigma0_detrend(
788
- xr_dataset_100.sigma0.sel(pol=copol), xr_dataset_100.incidence, model=model_vv)
812
+ xr_dataset_100.sigma0.sel(pol=copol), xr_dataset_100.incidence, model=model_co)
789
813
 
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)
814
+ if dual_pol:
815
+ xr_dataset_100['sigma0_detrend_cross'] = xsarsea.sigma0_detrend(
816
+ xr_dataset_100.sigma0.sel(pol=crosspol), xr_dataset_100.incidence, model=model_cross)
792
817
 
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]
818
+ sigma0_detrend_combined = xr.concat(
819
+ [xr_dataset_100['sigma0_detrend'],
820
+ xr_dataset_100['sigma0_detrend_cross']],
821
+ dim='pol'
822
+ )
823
+ sigma0_detrend_combined['pol'] = [copol, crosspol]
824
+
825
+ xr_dataset_100['sigma0_detrend'] = sigma0_detrend_combined
799
826
 
800
- xr_dataset_100['sigma0_detrend'] = sigma0_detrend_combined
801
827
  xr_dataset_100.land_mask.values = binary_dilation(xr_dataset_100['land_mask'].values.astype('uint8'),
802
828
  structure=np.ones((3, 3), np.uint8), iterations=3)
803
829
  xr_dataset_100['sigma0_detrend'] = xr.where(
@@ -806,7 +832,7 @@ def preprocess(filename, outdir, config_path, overwrite=False, add_streaks=False
806
832
  xr_dataset['streaks_direction'] = get_streaks(
807
833
  xr_dataset, xr_dataset_100)
808
834
 
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
835
+ return xr_dataset, out_file, config
810
836
 
811
837
 
812
838
  def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add_streaks=False, resolution='1000m'):
@@ -836,9 +862,26 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
836
862
  final dataset
837
863
  """
838
864
 
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(
865
+ xr_dataset, out_file, config = preprocess(
840
866
  filename, outdir, config_path, overwrite, add_streaks, resolution)
841
867
 
868
+ model_co = config["l2_params"]["model_co"]
869
+ model_cross = config["l2_params"]["model_cross"]
870
+ copol = config["l2_params"]["copol"]
871
+ crosspol = config["l2_params"]["crosspol"]
872
+ copol_gmf = config["l2_params"]["copol_gmf"]
873
+ crosspol_gmf = config["l2_params"]["crosspol_gmf"]
874
+ dual_pol = config["l2_params"]["dual_pol"]
875
+ ancillary_name = config["ancillary"]
876
+ sensor_longname = config["sensor_longname"]
877
+
878
+ if dual_pol:
879
+ sigma0_ocean_cross = xr_dataset['sigma0_ocean'].sel(pol=crosspol)
880
+ dsig_cross = xr_dataset['dsig_cross']
881
+ else:
882
+ sigma0_ocean_cross = None
883
+ dsig_cross = 0.1 # default value set in xsarsea
884
+
842
885
  kwargs = {
843
886
  "inc_step_lr": config.pop("inc_step_lr", None),
844
887
  "wpsd_step_lr": config.pop("wspd_step_lr", None),
@@ -849,6 +892,11 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
849
892
  "resolution": config.pop("resolution", None),
850
893
  }
851
894
 
895
+ logging.info("Checking incidence range within LUTS incidence range")
896
+ #  warning if incidence is out of lut incidence range
897
+ inc_check_co, inc_check_cross = check_incidence_range(
898
+ xr_dataset['incidence'], [model_co, model_cross], **kwargs)
899
+
852
900
  wind_co, wind_dual, windspeed_cr = inverse(dual_pol,
853
901
  inc=xr_dataset['incidence'],
854
902
  sigma0=xr_dataset['sigma0_ocean'].sel(
@@ -856,60 +904,61 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
856
904
  sigma0_dual=sigma0_ocean_cross,
857
905
  ancillary_wind=xr_dataset['ancillary_wind'],
858
906
  dsig_cr=dsig_cross,
859
- model_vv=model_vv,
860
- model_vh=model_vh,
907
+ model_co=model_co,
908
+ model_cross=model_cross,
861
909
  ** kwargs)
910
+ wind_co.compute()
862
911
 
863
912
  # windspeed_co
864
913
  xr_dataset['windspeed_co'] = np.abs(wind_co)
865
914
  xr_dataset["windspeed_co"].attrs["units"] = "m.s⁻1"
866
915
  xr_dataset["windspeed_co"].attrs["long_name"] = "Wind speed inverted from model %s (%s)" % (
867
- model_vv, copol)
916
+ model_co, copol)
868
917
  xr_dataset["windspeed_co"].attrs["standart_name"] = "wind_speed"
869
918
  xr_dataset["windspeed_co"].attrs["model"] = wind_co.attrs["model"]
870
919
  del xr_dataset["windspeed_co"].attrs['comment']
871
920
 
872
921
  # 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"]
922
+ xr_dataset['winddir_co'] = transform_winddir(
923
+ wind_co, xr_dataset.ground_heading, winddir_convention=config["winddir_convention"])
924
+ xr_dataset['winddir_co'].attrs["model"] = "%s (%s)" % (model_co, copol)
880
925
 
881
926
  # windspeed_dual / windspeed_cr / /winddir_dual / winddir_cr
882
- if dual_pol:
927
+ if dual_pol and wind_dual is not None:
928
+ wind_dual.compute()
883
929
  xr_dataset['windspeed_dual'] = np.abs(wind_dual)
884
930
  xr_dataset["windspeed_dual"].attrs["units"] = "m.s⁻1"
885
931
  xr_dataset["windspeed_dual"].attrs["long_name"] = "Wind speed inverted from model %s (%s) & %s (%s)" % (
886
- model_vv, copol, model_vh, crosspol)
932
+ model_co, copol, model_cross, crosspol)
887
933
  xr_dataset["windspeed_dual"].attrs["standart_name"] = "wind_speed"
888
934
  xr_dataset["windspeed_dual"].attrs["model"] = wind_dual.attrs["model"]
889
935
  del xr_dataset["windspeed_dual"].attrs['comment']
890
936
 
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"]
937
+ xr_dataset['winddir_dual'] = transform_winddir(
938
+ wind_dual, xr_dataset.ground_heading, winddir_convention=config["winddir_convention"])
939
+ xr_dataset['winddir_dual'].attrs["model"] = "%s (%s) & %s (%s)" % (
940
+ model_co, copol, model_cross, crosspol)
898
941
 
899
942
  xr_dataset = xr_dataset.assign(
900
943
  windspeed_cross=(['line', 'sample'], windspeed_cr))
901
944
  xr_dataset["windspeed_cross"].attrs["units"] = "m.s⁻1"
902
945
  xr_dataset["windspeed_cross"].attrs["long_name"] = "Wind Speed inverted from model %s (%s)" % (
903
- model_vh, crosspol)
946
+ model_cross, crosspol)
904
947
  xr_dataset["windspeed_cross"].attrs["standart_name"] = "wind_speed"
905
- xr_dataset["windspeed_cross"].attrs["model"] = "%s" % (model_vh)
948
+ xr_dataset["windspeed_cross"].attrs["model"] = "%s" % (model_cross)
906
949
 
907
950
  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"
951
+ xr_dataset['winddir_cross'].attrs = xr_dataset['winddir_dual'].attrs
911
952
  xr_dataset["winddir_cross"].attrs["model"] = "No model used ; content is a copy of dualpol wind direction"
912
953
 
954
+ if config["winddir_convention"] == "oceanographic":
955
+ attrs = xr_dataset['ancillary_wind_direction'].attrs
956
+ xr_dataset['ancillary_wind_direction'] = xsarsea.dir_meteo_to_oceano(
957
+ xr_dataset['ancillary_wind_direction'])
958
+ xr_dataset['ancillary_wind_direction'].attrs = attrs
959
+ xr_dataset['ancillary_wind_direction'].attrs[
960
+ "long_name"] = f"{ancillary_name} wind direction in oceanographic convention (clockwise, to), ex: 0°=to north, 90°=to east"
961
+
913
962
  xr_dataset, encoding = makeL2asOwi(
914
963
  xr_dataset, dual_pol, copol, crosspol, add_streaks=add_streaks)
915
964
 
@@ -943,7 +992,7 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
943
992
  "xsar_version": xsar.__version__,
944
993
  "xsarsea_version": xsarsea.__version__,
945
994
  "pythonVersion": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
946
- "polarisationRatio": "/",
995
+ "polarisationRatio": get_pol_ratio_name(model_co),
947
996
  "l2ProcessingUtcTime": datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ"),
948
997
  "processingCenter": "IFREMER",
949
998
  "firstMeasurementTime": firstMeasurementTime,
@@ -960,25 +1009,32 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
960
1009
  "wnf_3km_average": "False",
961
1010
  "owiWindSpeedSrc": "owiWindSpeed",
962
1011
  "owiWindDirectionSrc": "/",
963
- "ancillary_source": xr_dataset.attrs['ancillary_source']
1012
+ "ancillary_source": xr_dataset.attrs['ancillary_source'],
1013
+ "winddir_convention": config["winddir_convention"],
1014
+ "incidence_within_lut_copol_incidence_range": str(inc_check_co),
1015
+ "incidence_within_lut_crosspol_incidence_range": str(inc_check_cross),
1016
+ "swath": xr_dataset.attrs['swath'],
1017
+ "footprint": xr_dataset.attrs['footprint'],
1018
+ "coverage": xr_dataset.attrs['coverage'],
1019
+
964
1020
  }
965
1021
 
966
1022
  for recalib_attrs in ["path_aux_pp1_new", 'path_aux_pp1_old', "path_aux_cal_new", "path_aux_cal_old"]:
967
1023
  if recalib_attrs in xr_dataset.attrs:
968
1024
  attrs[recalib_attrs] = xr_dataset.attrs[recalib_attrs]
969
1025
 
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",]
1026
+ for arg in ["passDirection", "orbit_pass"]:
1027
+ if arg in xr_dataset.attrs:
1028
+ attrs["passDirection"] = xr_dataset.attrs[arg]
1029
+
1030
+ _S1_added_attrs = ["ipf", "platform_heading"]
1031
+ _RCM_added_attrs = ["productId"]
975
1032
 
976
- for sup_attr in _S1_added_attrs + _RS2_added_attrs + _RCM_added_attrs:
1033
+ for sup_attr in _S1_added_attrs + _RCM_added_attrs:
977
1034
  if sup_attr in xr_dataset.attrs:
978
1035
  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])
1036
+
1037
+ attrs['footprint'] = str(attrs['footprint'])
982
1038
 
983
1039
  # add in kwargs in attrs
984
1040
  for key in kwargs:
@@ -1000,3 +1056,49 @@ def makeL2(filename, outdir, config_path, overwrite=False, generateCSV=True, add
1000
1056
  logging.info("OK for %s ", os.path.basename(filename))
1001
1057
 
1002
1058
  return out_file, xr_dataset
1059
+
1060
+
1061
+ def transform_winddir(wind_cpx, ground_heading, winddir_convention='meteorological'):
1062
+ """
1063
+
1064
+ Parameters
1065
+ ----------
1066
+ wind_cpx : xr.DataArray | np.complex64
1067
+ complex wind, relative to antenna, anticlockwise
1068
+
1069
+ ground_heading : xr.DataArray
1070
+ heading angle in degrees
1071
+
1072
+ winddir_convention : str
1073
+ wind direction convention to use, either 'meteorological' or 'oceanographic'
1074
+
1075
+ Returns
1076
+ -------
1077
+ xr.DataArray
1078
+ wind direction in degrees in the selected convention with appropriate long_name attribute
1079
+ """
1080
+ # to meteo winddir_convention
1081
+ dataArray = xsarsea.dir_sample_to_meteo(
1082
+ np.angle(wind_cpx, deg=True), ground_heading)
1083
+ long_name = "Wind direction in meteorological convention (clockwise, from), ex: 0°=from north, 90°=from east"
1084
+
1085
+ if winddir_convention == "meteorological":
1086
+ # do nothing
1087
+ pass
1088
+ elif winddir_convention == "oceanographic":
1089
+ # to oceano winddir_convention
1090
+ dataArray = xsarsea.dir_meteo_to_oceano(dataArray)
1091
+ long_name = "Wind direction in oceanographic convention (clockwise, to), ex: 0°=to north, 90°=to east"
1092
+ else:
1093
+ #  warning
1094
+ logging.warning(
1095
+ f"wind direction convention {winddir_convention} is not supported, using meteorological",)
1096
+ long_name = "Wind direction in meteorological convention (clockwise, from), ex: 0°=from north, 90°=from east"
1097
+
1098
+ dataArray = xsarsea.dir_to_360(dataArray)
1099
+ dataArray.attrs = {}
1100
+ dataArray.attrs["units"] = "degrees_north"
1101
+ dataArray.attrs["long_name"] = long_name
1102
+ dataArray.attrs["standart_name"] = "wind_direction"
1103
+
1104
+ 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.4
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,20 @@
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=sYF1TAYM5ifFqlGdVwz8ygydo3_6KnEqYb7eBWYeDiw,1047
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=NRHZdbkozNSF-JRnnr5o3JYJtKZLM4EVgOJ0oJONqM0,47639
8
+ grdwindinversion/load_config.py,sha256=bt7EXTmXfbuK0oNR-qwtijv_iZBQzi5BtY92RGKEo4Y,651
9
+ grdwindinversion/main.py,sha256=0DLDW0i14CWhDKAGegzOKlWnCkag_cGn3vzMIAKcv4g,2658
10
+ grdwindinversion/streaks.py,sha256=SucALdajwAr-tcqwJs6KOj8CQ7qQPp3JUKSh2dHAJTY,2950
11
+ grdwindinversion/utils.py,sha256=WmHL-NFv3B0PDbCauhBMeqW2ue_340iALmPH1kSd_Pc,2593
12
+ grdwindinversion/utils_memory.py,sha256=1N3Kh4qVZPELPU6I4onbmkur1CZd7EHZqfbSAa6eaVc,1480
13
+ grdwindinversion/.github/ISSUE_TEMPLATE.md,sha256=qiM_a7CCUz3fSrz3Q20Se1nwPNFS8QCc8tkwK_0DSCo,327
14
+ grdwindinversion-0.2.4.dist-info/AUTHORS.rst,sha256=KmhW_5LBKGTIGwWEVkoTm1qx_bvdDR3yYL-1cwbDOFQ,218
15
+ grdwindinversion-0.2.4.dist-info/LICENSE,sha256=-B8mBiTeY3J7OLuayiV1myqmc7yeijBc7s34kc8RTmg,1075
16
+ grdwindinversion-0.2.4.dist-info/METADATA,sha256=NDJbyIEKTwJi5Uz9KPJj6o1zqvitYAd2A_5YBGrdAHQ,2392
17
+ grdwindinversion-0.2.4.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
18
+ grdwindinversion-0.2.4.dist-info/entry_points.txt,sha256=2rjvlVCy0iasRXjOz3kOIGuy2OCGQ-VTNuwuViQ6cMM,95
19
+ grdwindinversion-0.2.4.dist-info/top_level.txt,sha256=z6lPix3QPEYOo37qq8plA2hY7S3C8MQZY81agRlksMI,17
20
+ grdwindinversion-0.2.4.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,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,,