grdwindinversion 0.2.3.post15__tar.gz → 0.2.4__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.4/.github/workflows/ci.yml +98 -0
  2. {grdwindinversion-0.2.3.post15/grdwindinversion.egg-info → grdwindinversion-0.2.4}/PKG-INFO +1 -1
  3. grdwindinversion-0.2.4/grdwindinversion/config_prod.yaml +46 -0
  4. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion/inversion.py +179 -77
  5. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion/main.py +1 -1
  6. grdwindinversion-0.2.4/grdwindinversion/utils.py +83 -0
  7. grdwindinversion-0.2.3.post15/grdwindinversion/utils.py → grdwindinversion-0.2.4/grdwindinversion/utils_memory.py +8 -4
  8. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4/grdwindinversion.egg-info}/PKG-INFO +1 -1
  9. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion.egg-info/SOURCES.txt +4 -1
  10. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/recipe/meta.yaml +3 -0
  11. grdwindinversion-0.2.3.post15/grdwindinversion/config_prod.yaml → grdwindinversion-0.2.4/tests/config_test.yaml +1 -0
  12. grdwindinversion-0.2.4/tests/test_grdwindinversion_ci.py +55 -0
  13. grdwindinversion-0.2.3.post15/tests/test_grdwindinversion.py +0 -20
  14. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/.editorconfig +0 -0
  15. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/.github/dependabot.yml +0 -0
  16. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/.github/workflows/publish.yml +0 -0
  17. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/.gitignore +0 -0
  18. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/.pre-commit-config.yaml +0 -0
  19. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/AUTHORS.rst +0 -0
  20. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/CONTRIBUTING.rst +0 -0
  21. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/HISTORY.rst +0 -0
  22. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/LICENSE +0 -0
  23. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/MANIFEST.in +0 -0
  24. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/Makefile +0 -0
  25. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/README.md +0 -0
  26. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/ci/requirements/docs.yaml +0 -0
  27. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/ci/requirements/environment.yaml +0 -0
  28. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/Makefile +0 -0
  29. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/_static/css/grdwindinversion.css +0 -0
  30. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/algorithm.rst +0 -0
  31. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/authors.rst +0 -0
  32. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/conf.py +0 -0
  33. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/contributing.rst +0 -0
  34. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/examples/wind-inversion-from-grd.ipynb +0 -0
  35. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/history.rst +0 -0
  36. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/index.rst +0 -0
  37. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/installation.rst +0 -0
  38. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/make.bat +0 -0
  39. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/modules.rst +0 -0
  40. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/readme.rst +0 -0
  41. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/docs/usage.rst +0 -0
  42. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion/.github/ISSUE_TEMPLATE.md +0 -0
  43. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion/.gitignore +0 -0
  44. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion/.travis.yml +0 -0
  45. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion/__init__.py +0 -0
  46. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion/config_prod_recal.yaml +0 -0
  47. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion/data_config.yaml +0 -0
  48. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion/load_config.py +0 -0
  49. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion/streaks.py +0 -0
  50. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion.egg-info/dependency_links.txt +0 -0
  51. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion.egg-info/entry_points.txt +0 -0
  52. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion.egg-info/requires.txt +0 -0
  53. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/grdwindinversion.egg-info/top_level.txt +0 -0
  54. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/pyproject.toml +0 -0
  55. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/requirements_dev.txt +0 -0
  56. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/requirements_doc.txt +0 -0
  57. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/setup.cfg +0 -0
  58. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/tests/__init__.py +0 -0
  59. {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.4}/tox.ini +0 -0
@@ -0,0 +1,98 @@
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 "'sarwing_luts_path': './test_data/GMFS/v1.6/'" >> ~/.grdwindinversion/data_config.yaml
81
+ #echo "'nc_luts_path': ./test_data/GMFS/nc_luts" >> ~/.grdwindinversion/data_config.yaml
82
+ #echo "'lut_cmod7_path': './test_data/GMFS/v1.6/GMF_cmod7_official/cmod7_and_python_script'" >> ~/.grdwindinversion/data_config.yaml
83
+ #echo "'lut_ms1ahw_path': './test_data/GMFS/v1.6/GMF_cmodms1ahw'" >> ~/.grdwindinversion/data_config.yaml
84
+
85
+ # Run the tests
86
+ - name: Run tests
87
+ run: |
88
+ eval "$(micromamba shell hook --shell bash)"
89
+ micromamba activate grdwind_env
90
+ pytest
91
+
92
+ # Optionally, upload test artifacts (NetCDF files or logs) if needed
93
+ #- name: Upload test artifacts
94
+ # if: failure() # Only upload on failure
95
+ # uses: actions/upload-artifact@v2
96
+ # with:
97
+ # name: test-output
98
+ # 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.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,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_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
@@ -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.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
@@ -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
@@ -41,6 +42,7 @@ grdwindinversion/load_config.py
41
42
  grdwindinversion/main.py
42
43
  grdwindinversion/streaks.py
43
44
  grdwindinversion/utils.py
45
+ grdwindinversion/utils_memory.py
44
46
  grdwindinversion.egg-info/PKG-INFO
45
47
  grdwindinversion.egg-info/SOURCES.txt
46
48
  grdwindinversion.egg-info/dependency_links.txt
@@ -50,4 +52,5 @@ grdwindinversion.egg-info/top_level.txt
50
52
  grdwindinversion/.github/ISSUE_TEMPLATE.md
51
53
  recipe/meta.yaml
52
54
  tests/__init__.py
53
- tests/test_grdwindinversion.py
55
+ tests/config_test.yaml
56
+ 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,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."""