grdwindinversion 0.2.3.post15__tar.gz → 0.2.5__tar.gz

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.
Files changed (59) hide show
  1. grdwindinversion-0.2.5/.github/workflows/ci.yml +97 -0
  2. {grdwindinversion-0.2.3.post15/grdwindinversion.egg-info → grdwindinversion-0.2.5}/PKG-INFO +1 -1
  3. grdwindinversion-0.2.5/grdwindinversion/config_prod.yaml +46 -0
  4. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/inversion.py +193 -100
  5. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/main.py +1 -1
  6. grdwindinversion-0.2.5/grdwindinversion/utils.py +83 -0
  7. grdwindinversion-0.2.3.post15/grdwindinversion/utils.py → grdwindinversion-0.2.5/grdwindinversion/utils_memory.py +8 -4
  8. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5/grdwindinversion.egg-info}/PKG-INFO +1 -1
  9. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/SOURCES.txt +4 -2
  10. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/recipe/meta.yaml +3 -0
  11. grdwindinversion-0.2.3.post15/grdwindinversion/config_prod.yaml → grdwindinversion-0.2.5/tests/config_test.yaml +1 -0
  12. grdwindinversion-0.2.5/tests/test_grdwindinversion_ci.py +55 -0
  13. grdwindinversion-0.2.3.post15/grdwindinversion/.travis.yml +0 -28
  14. grdwindinversion-0.2.3.post15/tests/test_grdwindinversion.py +0 -20
  15. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/.editorconfig +0 -0
  16. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/.github/dependabot.yml +0 -0
  17. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/.github/workflows/publish.yml +0 -0
  18. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/.gitignore +0 -0
  19. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/.pre-commit-config.yaml +0 -0
  20. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/AUTHORS.rst +0 -0
  21. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/CONTRIBUTING.rst +0 -0
  22. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/HISTORY.rst +0 -0
  23. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/LICENSE +0 -0
  24. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/MANIFEST.in +0 -0
  25. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/Makefile +0 -0
  26. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/README.md +0 -0
  27. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/ci/requirements/docs.yaml +0 -0
  28. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/ci/requirements/environment.yaml +0 -0
  29. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/Makefile +0 -0
  30. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/_static/css/grdwindinversion.css +0 -0
  31. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/algorithm.rst +0 -0
  32. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/authors.rst +0 -0
  33. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/conf.py +0 -0
  34. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/contributing.rst +0 -0
  35. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/examples/wind-inversion-from-grd.ipynb +0 -0
  36. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/history.rst +0 -0
  37. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/index.rst +0 -0
  38. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/installation.rst +0 -0
  39. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/make.bat +0 -0
  40. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/modules.rst +0 -0
  41. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/readme.rst +0 -0
  42. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/usage.rst +0 -0
  43. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/.github/ISSUE_TEMPLATE.md +0 -0
  44. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/.gitignore +0 -0
  45. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/__init__.py +0 -0
  46. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/config_prod_recal.yaml +0 -0
  47. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/data_config.yaml +0 -0
  48. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/load_config.py +0 -0
  49. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/streaks.py +0 -0
  50. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/dependency_links.txt +0 -0
  51. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/entry_points.txt +0 -0
  52. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/requires.txt +0 -0
  53. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/top_level.txt +0 -0
  54. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/pyproject.toml +0 -0
  55. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/requirements_dev.txt +0 -0
  56. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/requirements_doc.txt +0 -0
  57. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/setup.cfg +0 -0
  58. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/tests/__init__.py +0 -0
  59. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/tox.ini +0 -0
@@ -0,0 +1,97 @@
1
+ name: CI Workflow for grdwindinversion
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ branches:
9
+ - main
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ timeout-minutes: 360 # 6 hours limit for the job
15
+
16
+ steps:
17
+ # Checkout the code
18
+ - name: Checkout code
19
+ uses: actions/checkout@v4
20
+
21
+ - uses: mamba-org/setup-micromamba@v1
22
+ with:
23
+ micromamba-version: "1.5.9-1" # any version from https://github.com/mamba-org/micromamba-releases
24
+ channels: tcevaer, conda-forge, defaults
25
+ init-shell: bash
26
+ post-cleanup: "all"
27
+
28
+ - name: Create environment and install tools
29
+ run: micromamba create -n grdwind_env pytest conda-build boa python=3.10 -y -c tcevaer -c conda-forge
30
+
31
+ - name: Build package
32
+ run: |
33
+ cd recipe
34
+ eval "$(micromamba shell hook --shell bash)"
35
+ micromamba activate grdwind_env
36
+ conda mambabuild . -c tcevaer -c conda-forge
37
+
38
+ # Install the built package into the environment
39
+ - name: Install the built package
40
+ run: |
41
+ eval "$(micromamba shell hook --shell bash)"
42
+ micromamba activate grdwind_env
43
+ conda install --use-local grdwindinversion -y -c tcevaer -c conda-forge
44
+
45
+ # Cache the test data if previously downloaded (up to 10 GB limit for the cache)
46
+ # WARNING : modify the key if the data is modified !!
47
+ - name: Cache test data
48
+ uses: actions/cache@v4
49
+ id: cache
50
+ with:
51
+ path: ./test_data
52
+ key: test-data-v3
53
+ restore-keys: test-data-v3
54
+
55
+ # Download test data if not already cached
56
+ - name: Download test data
57
+ if: steps.cache.outputs.cache-hit != 'true' # Only download if cache miss
58
+ run: |
59
+ mkdir -p ./test_data/
60
+ wget https://cloud.ifremer.fr/index.php/s/ExLQ2TnYAqozPWE/download -O /tmp/ecmwf.zip
61
+ unzip /tmp/ecmwf.zip -d ./test_data/
62
+ wget https://cloud.ifremer.fr/index.php/s/kRgdOOPsjoZieZR/download -O /tmp/l1.zip
63
+ unzip /tmp/l1.zip -d ./test_data/
64
+ timeout-minutes: 200 # Adjust depending on the size of your data
65
+
66
+ # Set up xsar configuration
67
+ - name: Setup xsar configuration
68
+ run: |
69
+ mkdir -p ~/.xsar
70
+ echo "data_dir: /tmp" > ~/.xsar/config.yaml
71
+ echo "auxiliary_dir: ./test_data/auxiliary" >> ~/.xsar/config.yaml
72
+ echo "path_dataframe_aux: ./test_data/auxiliary/active_aux.csv" >> ~/.xsar/config.yaml
73
+
74
+ # Set up grdwindinversion configuration
75
+ - name: Setup grdwindinversion configuration
76
+ run: |
77
+ mkdir -p ~/.grdwindinversion
78
+ echo "'ecmwf_0100_1h': ./test_data/ECMWF/forecast/hourly/0100deg/netcdf_light/%Y/%j/ECMWF_FORECAST_0100_%Y%m%d%H%M_10U_10V.nc" > ~/.grdwindinversion/data_config.yaml
79
+ echo "'ecmwf_0125_1h': ./test_data/ECMWF/0.125deg/1h/forecasts/%Y/%j/ecmwf_%Y%m%d%H%M.nc" >> ~/.grdwindinversion/data_config.yaml
80
+ #echo "'nc_luts_path': ./test_data/GMFS/nc_luts" >> ~/.grdwindinversion/data_config.yaml
81
+ #echo "'lut_cmod7_path': './test_data/GMFS/v1.6/GMF_cmod7_official/cmod7_and_python_script'" >> ~/.grdwindinversion/data_config.yaml
82
+ #echo "'lut_ms1ahw_path': './test_data/GMFS/v1.6/GMF_cmodms1ahw'" >> ~/.grdwindinversion/data_config.yaml
83
+
84
+ # Run the tests
85
+ - name: Run tests
86
+ run: |
87
+ eval "$(micromamba shell hook --shell bash)"
88
+ micromamba activate grdwind_env
89
+ pytest
90
+
91
+ # Optionally, upload test artifacts (NetCDF files or logs) if needed
92
+ #- name: Upload test artifacts
93
+ # if: failure() # Only upload on failure
94
+ # uses: actions/upload-artifact@v2
95
+ # with:
96
+ # name: test-output
97
+ # path: ./test_output/
@@ -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,46 @@
1
+ no_subdir: True
2
+ winddir_convention: "meteorological"
3
+ S1A:
4
+ GMF_VV_NAME: "gmf_cmod5n"
5
+ GMF_VH_NAME: "gmf_s1_v2"
6
+ dsig_VH_NAME: "gmf_s1_v2"
7
+ apply_flattening: True
8
+ recalibration: False
9
+ ancillary: "ecmwf"
10
+ inc_step: 0.1
11
+ wspd_step: 0.1
12
+ phi_step: 1.0
13
+ resolution: "high"
14
+ S1B:
15
+ GMF_VV_NAME: "gmf_cmod5n"
16
+ GMF_VH_NAME: "gmf_s1_v2"
17
+ dsig_VH_NAME: "gmf_s1_v2"
18
+ apply_flattening: True
19
+ recalibration: False
20
+ ancillary: "ecmwf"
21
+ inc_step: 0.1
22
+ wspd_step: 0.1
23
+ phi_step: 1.0
24
+ resolution: "high"
25
+ RS2:
26
+ GMF_VV_NAME: "gmf_cmod5n"
27
+ GMF_VH_NAME: "gmf_rs2_v2"
28
+ dsig_VH_NAME: "gmf_rs2_v2"
29
+ apply_flattening: False
30
+ recalibration: False
31
+ ancillary: "ecmwf"
32
+ inc_step: 0.1
33
+ wspd_step: 0.1
34
+ phi_step: 1.0
35
+ resolution: "high"
36
+ RCM:
37
+ GMF_VV_NAME: "gmf_cmod5n"
38
+ GMF_VH_NAME: "gmf_rcm_noaa"
39
+ dsig_VH_NAME: "gmf_s1_v2"
40
+ apply_flattening: True
41
+ recalibration: False
42
+ ancillary: "ecmwf"
43
+ inc_step: 0.1
44
+ wspd_step: 0.1
45
+ phi_step: 1.0
46
+ resolution: "high"
@@ -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
@@ -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
@@ -0,0 +1,83 @@
1
+ import logging
2
+ import xsarsea
3
+
4
+
5
+ def check_incidence_range(incidence, models, **kwargs):
6
+ """
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
+
17
+ Returns
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
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 '/'
61
+ """
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"
82
+ else:
83
+ return '/'
@@ -19,10 +19,14 @@ def get_memory_usage(unit='Go', var='ru_maxrss', force_psutil=False):
19
19
  on_purpose_error
20
20
  import resource
21
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
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
26
30
  mems['current'] = getCurrentMemoryUsage() / factor
27
31
  # memory_used_go = resource.getrusage(resource.RUSAGE_SELF).get(var) /factor
28
32
  memory_used_go = mems[var]
@@ -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
@@ -13,6 +13,7 @@ requirements_dev.txt
13
13
  requirements_doc.txt
14
14
  tox.ini
15
15
  .github/dependabot.yml
16
+ .github/workflows/ci.yml
16
17
  .github/workflows/publish.yml
17
18
  ci/requirements/docs.yaml
18
19
  ci/requirements/environment.yaml
@@ -31,7 +32,6 @@ docs/usage.rst
31
32
  docs/_static/css/grdwindinversion.css
32
33
  docs/examples/wind-inversion-from-grd.ipynb
33
34
  grdwindinversion/.gitignore
34
- grdwindinversion/.travis.yml
35
35
  grdwindinversion/__init__.py
36
36
  grdwindinversion/config_prod.yaml
37
37
  grdwindinversion/config_prod_recal.yaml
@@ -41,6 +41,7 @@ grdwindinversion/load_config.py
41
41
  grdwindinversion/main.py
42
42
  grdwindinversion/streaks.py
43
43
  grdwindinversion/utils.py
44
+ grdwindinversion/utils_memory.py
44
45
  grdwindinversion.egg-info/PKG-INFO
45
46
  grdwindinversion.egg-info/SOURCES.txt
46
47
  grdwindinversion.egg-info/dependency_links.txt
@@ -50,4 +51,5 @@ grdwindinversion.egg-info/top_level.txt
50
51
  grdwindinversion/.github/ISSUE_TEMPLATE.md
51
52
  recipe/meta.yaml
52
53
  tests/__init__.py
53
- tests/test_grdwindinversion.py
54
+ tests/config_test.yaml
55
+ tests/test_grdwindinversion_ci.py
@@ -28,6 +28,9 @@ requirements:
28
28
  - scipy
29
29
  - fsspec
30
30
  - aiohttp
31
+ - xarray-safe-s1
32
+ - xarray-safe-rcm
33
+ - xradarsat2
31
34
 
32
35
  about:
33
36
  home: https://github.com/umr-lops/grdwindinversion
@@ -1,4 +1,5 @@
1
1
  no_subdir: True
2
+ convention: "meteorological"
2
3
  S1A:
3
4
  GMF_VV_NAME: "gmf_cmod5n"
4
5
  GMF_VH_NAME: "gmf_s1_v2"
@@ -0,0 +1,55 @@
1
+ import pytest
2
+ import os
3
+ import urllib.request
4
+ from grdwindinversion.inversion import makeL2
5
+
6
+ # What must be done by the tests:
7
+ # - Download L1 data
8
+ # - Download needed GMFs
9
+ # - Download ECMWF data
10
+ # -
11
+ # - Setup data-config pour xsar et grdwindinversion
12
+ #
13
+ # - For recal : download auxiliary files
14
+ #
15
+
16
+
17
+ def test_makeL2_generation():
18
+ l1_files = [
19
+ "./test_data/L1/S1A_IW_GRDH_1SDV_20210909T130650_20210909T130715_039605_04AE83_C34F.SAFE",
20
+ "./test_data/L1/RCM1_OK2767220_PK2769320_1_SCLND_20230930_214014_VV_VH_GRD",
21
+ "./test_data/L1/RS2_OK141302_PK1242223_DK1208537_SCWA_20220904_093402_VV_VH_SGF"
22
+ ]
23
+
24
+ # l1_files = [
25
+ # "/home/datawork-cersat-public/cache/project/mpc-sentinel1/data/esa/sentinel-1a/L1/IW/S1A_IW_GRDH_1S/2021/252/S1A_IW_GRDH_1SDV_20210909T130650_20210909T130715_039605_04AE83_C34F.SAFE"
26
+ # ]
27
+
28
+ outdir = "./out_test_data"
29
+ os.makedirs(outdir, exist_ok=True)
30
+
31
+ config_dir = os.path.dirname(__file__)
32
+ config_path = os.path.join(config_dir, "config_test.yaml")
33
+
34
+ for f in l1_files:
35
+ # Run the makeL2 function
36
+ print(f)
37
+ output_nc_file, dataset = makeL2(
38
+ filename=f,
39
+ outdir=outdir,
40
+ config_path=config_path,
41
+ overwrite=True, # Set to True to ensure a clean run
42
+ generateCSV=False, # Disable CSV generation for now
43
+ add_streaks=False,
44
+ resolution="1000m",
45
+ )
46
+
47
+ # Check if the output file (NetCDF) is generated
48
+ assert os.path.exists(
49
+ output_nc_file), f"NetCDF output file not created for {f}"
50
+
51
+ # Optionally, check the dataset has content
52
+ assert dataset is not None, f"No dataset generated for {f}"
53
+ assert (
54
+ "owiWindSpeed" in dataset.variables
55
+ ), "Expected variable 'owiWindSpeed' missing in the dataset"
@@ -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,20 +0,0 @@
1
- #!/usr/bin/env python
2
-
3
- """Tests for `grdwindinversion` package."""
4
-
5
-
6
- import unittest
7
- import grdwindinversion
8
-
9
-
10
- class TestGrdwindinversion(unittest.TestCase):
11
- """Tests for `grdwindinversion` package."""
12
-
13
- def setUp(self):
14
- """Set up test fixtures, if any."""
15
-
16
- def tearDown(self):
17
- """Tear down test fixtures, if any."""
18
-
19
- def test_000_something(self):
20
- """Test something."""