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.
- grdwindinversion-0.2.5/.github/workflows/ci.yml +97 -0
- {grdwindinversion-0.2.3.post15/grdwindinversion.egg-info → grdwindinversion-0.2.5}/PKG-INFO +1 -1
- grdwindinversion-0.2.5/grdwindinversion/config_prod.yaml +46 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/inversion.py +193 -100
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/main.py +1 -1
- grdwindinversion-0.2.5/grdwindinversion/utils.py +83 -0
- grdwindinversion-0.2.3.post15/grdwindinversion/utils.py → grdwindinversion-0.2.5/grdwindinversion/utils_memory.py +8 -4
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5/grdwindinversion.egg-info}/PKG-INFO +1 -1
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/SOURCES.txt +4 -2
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/recipe/meta.yaml +3 -0
- grdwindinversion-0.2.3.post15/grdwindinversion/config_prod.yaml → grdwindinversion-0.2.5/tests/config_test.yaml +1 -0
- grdwindinversion-0.2.5/tests/test_grdwindinversion_ci.py +55 -0
- grdwindinversion-0.2.3.post15/grdwindinversion/.travis.yml +0 -28
- grdwindinversion-0.2.3.post15/tests/test_grdwindinversion.py +0 -20
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/.editorconfig +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/.github/dependabot.yml +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/.github/workflows/publish.yml +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/.gitignore +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/.pre-commit-config.yaml +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/AUTHORS.rst +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/CONTRIBUTING.rst +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/HISTORY.rst +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/LICENSE +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/MANIFEST.in +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/Makefile +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/README.md +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/ci/requirements/docs.yaml +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/ci/requirements/environment.yaml +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/Makefile +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/_static/css/grdwindinversion.css +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/algorithm.rst +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/authors.rst +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/conf.py +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/contributing.rst +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/examples/wind-inversion-from-grd.ipynb +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/history.rst +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/index.rst +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/installation.rst +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/make.bat +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/modules.rst +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/readme.rst +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/usage.rst +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/.github/ISSUE_TEMPLATE.md +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/.gitignore +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/__init__.py +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/config_prod_recal.yaml +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/data_config.yaml +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/load_config.py +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/streaks.py +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/dependency_links.txt +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/entry_points.txt +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/requires.txt +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/top_level.txt +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/pyproject.toml +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/requirements_dev.txt +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/requirements_doc.txt +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/setup.cfg +0 -0
- {grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/tests/__init__.py +0 -0
- {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/
|
|
@@ -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}_${
|
|
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,
|
|
103
|
-
new_format = f"{MISSIONID.lower()}
|
|
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,
|
|
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 **
|
|
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
|
-
|
|
233
|
+
model_co=: str
|
|
233
234
|
model to use for VV or HH polarization.
|
|
234
|
-
|
|
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 **
|
|
241
|
+
inverted wind in ** antenna convention** .
|
|
241
242
|
|
|
242
243
|
See Also
|
|
243
244
|
--------
|
|
244
245
|
xsarsea documentation
|
|
245
|
-
https://
|
|
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
|
|
252
|
+
if model_co not in list_mods:
|
|
252
253
|
raise ValueError(
|
|
253
|
-
f"
|
|
254
|
-
if
|
|
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"
|
|
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=(
|
|
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=
|
|
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
|
-
|
|
611
|
-
|
|
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
|
-
|
|
620
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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.
|
|
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=
|
|
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=
|
|
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(
|
|
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['
|
|
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
|
-
|
|
752
|
-
|
|
753
|
-
|
|
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 =
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
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=
|
|
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
|
-
|
|
791
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
860
|
-
|
|
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
|
-
|
|
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
|
-
|
|
875
|
-
xr_dataset[
|
|
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
|
-
|
|
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
|
-
|
|
893
|
-
xr_dataset["winddir_dual"].attrs["
|
|
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
|
-
|
|
937
|
+
model_cross, crosspol)
|
|
904
938
|
xr_dataset["windspeed_cross"].attrs["standart_name"] = "wind_speed"
|
|
905
|
-
xr_dataset["windspeed_cross"].attrs["model"] = "%s" % (
|
|
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[
|
|
909
|
-
xr_dataset["winddir_cross"].attrs["
|
|
910
|
-
|
|
911
|
-
|
|
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
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
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 +
|
|
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
|
-
|
|
980
|
-
|
|
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
|
|
@@ -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(
|
|
23
|
-
|
|
24
|
-
mems['
|
|
25
|
-
|
|
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]
|
{grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/SOURCES.txt
RENAMED
|
@@ -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/
|
|
54
|
+
tests/config_test.yaml
|
|
55
|
+
tests/test_grdwindinversion_ci.py
|
|
@@ -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."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/_static/css/grdwindinversion.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/docs/examples/wind-inversion-from-grd.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/.github/ISSUE_TEMPLATE.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion/config_prod_recal.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/requires.txt
RENAMED
|
File without changes
|
{grdwindinversion-0.2.3.post15 → grdwindinversion-0.2.5}/grdwindinversion.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|