wasp-ocean 0.1.0__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.
- wasp_ocean-0.1.0/LICENSE +21 -0
- wasp_ocean-0.1.0/PKG-INFO +153 -0
- wasp_ocean-0.1.0/README.md +121 -0
- wasp_ocean-0.1.0/pyproject.toml +51 -0
- wasp_ocean-0.1.0/setup.cfg +4 -0
- wasp_ocean-0.1.0/src/wasp/__init__.py +45 -0
- wasp_ocean-0.1.0/src/wasp/io_sar.py +203 -0
- wasp_ocean-0.1.0/src/wasp/io_ww3.py +80 -0
- wasp_ocean-0.1.0/src/wasp/partition.py +984 -0
- wasp_ocean-0.1.0/src/wasp/plotting.py +135 -0
- wasp_ocean-0.1.0/src/wasp/utils.py +354 -0
- wasp_ocean-0.1.0/src/wasp/wave_params.py +157 -0
- wasp_ocean-0.1.0/src/wasp_ocean.egg-info/PKG-INFO +153 -0
- wasp_ocean-0.1.0/src/wasp_ocean.egg-info/SOURCES.txt +15 -0
- wasp_ocean-0.1.0/src/wasp_ocean.egg-info/dependency_links.txt +1 -0
- wasp_ocean-0.1.0/src/wasp_ocean.egg-info/requires.txt +8 -0
- wasp_ocean-0.1.0/src/wasp_ocean.egg-info/top_level.txt +1 -0
wasp_ocean-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 WASP Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wasp-ocean
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: WAve Spectra Partitioning
|
|
5
|
+
Author-email: Your Name <your.email@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/jtcarvalho/wasp
|
|
8
|
+
Project-URL: Repository, https://github.com/jtcarvalho/wasp
|
|
9
|
+
Project-URL: Documentation, https://github.com/jtcarvalho/wasp/blob/main/README.md
|
|
10
|
+
Project-URL: Issues, https://github.com/jtcarvalho/wasp/issues
|
|
11
|
+
Keywords: waves,oceanography,SAR,WaveWatch3,spectral partitioning
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy<3.0.0,>=2.1.0
|
|
24
|
+
Requires-Dist: pandas<3.0.0,>=2.2.0
|
|
25
|
+
Requires-Dist: scipy<2.0.0,>=1.14.0
|
|
26
|
+
Requires-Dist: xarray>=2024.11.0
|
|
27
|
+
Requires-Dist: netCDF4<2.0.0,>=1.5.4
|
|
28
|
+
Requires-Dist: matplotlib<4.0.0,>=3.8.0
|
|
29
|
+
Requires-Dist: scikit-image<1.0.0,>=0.22.0
|
|
30
|
+
Requires-Dist: tqdm<5.0.0,>=4.65.0
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# **WASP** - **WA**ve **S**pectra **P**artitioning
|
|
34
|
+
|
|
35
|
+
Watershed Algorithm for partitioning the ocean wave spectra from WW3 and SAR (Sentinel
|
|
36
|
+
|
|
37
|
+
<!--
|
|
38
|
+
|
|
39
|
+
**🔗 Companion Repository:** For analysis and validation of partitioned spectra, see [**HIVE** (Hierarchical Integration of Verified wavE partitions)](https://github.com/jtcarvalho/hive)
|
|
40
|
+
|
|
41
|
+
-->
|
|
42
|
+
|
|
43
|
+
## 📋 What is WASP?
|
|
44
|
+
|
|
45
|
+
WASP focuses exclusively on **spectral partitioning** - the process of separating ocean wave spectra into individual wave systems (partitions). Each partition represents a distinct wave system characterized by significant wave height (Hs), peak period (Tp), and direction (Dp).
|
|
46
|
+
|
|
47
|
+
**WASP handles:**
|
|
48
|
+
|
|
49
|
+
- ✅ Spectral partitioning using watershed algorithm
|
|
50
|
+
- ✅ Processing SAR (Sentinel) and WW3 model spectra
|
|
51
|
+
- ✅ Extracting wave parameters (Hs, Tp, Dp) for each partition
|
|
52
|
+
|
|
53
|
+
👉 **For analysis and validation**, use the repository [**HIVE**](https://github.com/jtcarvalho/hive)
|
|
54
|
+
|
|
55
|
+
## 🚀 Installation
|
|
56
|
+
|
|
57
|
+
### Método 1: Instalação Local (Desenvolvimento)
|
|
58
|
+
|
|
59
|
+
Instale o pacote em modo editável para desenvolvimento ou uso local:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Clone o repositório
|
|
63
|
+
git clone https://github.com/jtcarvalho/wasp.git
|
|
64
|
+
cd wasp
|
|
65
|
+
|
|
66
|
+
# Instale em modo editável (recomendado)
|
|
67
|
+
pip install -e .
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Método 2: Ambiente Virtual Tradicional
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Clone o repositório
|
|
74
|
+
git clone https://github.com/jtcarvalho/wasp.git
|
|
75
|
+
cd wasp
|
|
76
|
+
|
|
77
|
+
# Crie ambiente virtual
|
|
78
|
+
python -m venv venv
|
|
79
|
+
|
|
80
|
+
# Ative o ambiente virtual
|
|
81
|
+
# No macOS/Linux:
|
|
82
|
+
source venv/bin/activate
|
|
83
|
+
# No Windows:
|
|
84
|
+
# venv\Scripts\activate
|
|
85
|
+
|
|
86
|
+
# Instale o pacote
|
|
87
|
+
pip install -e .
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Verificar Instalação
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Teste a importação
|
|
94
|
+
python -c "import wasp; print(f'WASP version: {wasp.__version__}')"
|
|
95
|
+
|
|
96
|
+
# Teste as funções principais
|
|
97
|
+
python -c "from wasp import partition_spectrum, calculate_wave_parameters; print('✓ Instalação bem-sucedida!')"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## 📦 Key Dependencies
|
|
101
|
+
|
|
102
|
+
- **NumPy >= 2.1.0** (required for `np.trapezoid`)
|
|
103
|
+
- pandas >= 2.2.0
|
|
104
|
+
- xarray >= 2024.11.0
|
|
105
|
+
- matplotlib >= 3.8.0
|
|
106
|
+
- scipy >= 1.14.0
|
|
107
|
+
- scikit-image >= 0.22.0
|
|
108
|
+
- netCDF4 >= 1.5.4
|
|
109
|
+
|
|
110
|
+
> ⚠️ **Importante:** NumPy < 2.1.0 causará erros pois `np.trapezoid` não está disponível.
|
|
111
|
+
|
|
112
|
+
## 💡 Uso Rápido
|
|
113
|
+
|
|
114
|
+
### Como Biblioteca Python
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
import numpy as np
|
|
118
|
+
from wasp import partition_spectrum, calculate_wave_parameters
|
|
119
|
+
|
|
120
|
+
# Seu espectro 2D (freq x dir)
|
|
121
|
+
E = np.array(...) # matriz de energia espectral [m²/Hz/rad]
|
|
122
|
+
freq = np.array(...) # frequências [Hz]
|
|
123
|
+
dirs = np.array(...) # direções [graus, convenção oceanográfica]
|
|
124
|
+
|
|
125
|
+
# Particionar o espectro
|
|
126
|
+
partitions = partition_spectrum(
|
|
127
|
+
E, freq, dirs,
|
|
128
|
+
energy_threshold=1e-6,
|
|
129
|
+
max_partitions=3
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Calcular parâmetros de cada partição
|
|
133
|
+
for i, partition in enumerate(partitions):
|
|
134
|
+
params = calculate_wave_parameters(partition, freq, dirs)
|
|
135
|
+
print(f"Partição {i+1}:")
|
|
136
|
+
print(f" Hs = {params['Hs']:.2f} m")
|
|
137
|
+
print(f" Tp = {params['Tp']:.1f} s")
|
|
138
|
+
print(f" Dp = {params['Dp']:.1f} deg")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Scripts de Exemplo
|
|
142
|
+
|
|
143
|
+
Veja a pasta [examples/](examples/) para scripts completos:
|
|
144
|
+
|
|
145
|
+
- **01_partition_sar.py**: Processar espectros SAR (Sentinel-1)
|
|
146
|
+
- **02_partition_ww3.py**: Processar espectros WaveWatch III
|
|
147
|
+
- **03_partition_ndbc.py**: Template para processar dados de bóia NDBC
|
|
148
|
+
- **04_validate.py**: Comparar e validar resultados SAR vs WW3
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
cd examples/
|
|
152
|
+
python 01_partition_sar.py
|
|
153
|
+
```
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# **WASP** - **WA**ve **S**pectra **P**artitioning
|
|
2
|
+
|
|
3
|
+
Watershed Algorithm for partitioning the ocean wave spectra from WW3 and SAR (Sentinel
|
|
4
|
+
|
|
5
|
+
<!--
|
|
6
|
+
|
|
7
|
+
**🔗 Companion Repository:** For analysis and validation of partitioned spectra, see [**HIVE** (Hierarchical Integration of Verified wavE partitions)](https://github.com/jtcarvalho/hive)
|
|
8
|
+
|
|
9
|
+
-->
|
|
10
|
+
|
|
11
|
+
## 📋 What is WASP?
|
|
12
|
+
|
|
13
|
+
WASP focuses exclusively on **spectral partitioning** - the process of separating ocean wave spectra into individual wave systems (partitions). Each partition represents a distinct wave system characterized by significant wave height (Hs), peak period (Tp), and direction (Dp).
|
|
14
|
+
|
|
15
|
+
**WASP handles:**
|
|
16
|
+
|
|
17
|
+
- ✅ Spectral partitioning using watershed algorithm
|
|
18
|
+
- ✅ Processing SAR (Sentinel) and WW3 model spectra
|
|
19
|
+
- ✅ Extracting wave parameters (Hs, Tp, Dp) for each partition
|
|
20
|
+
|
|
21
|
+
👉 **For analysis and validation**, use the repository [**HIVE**](https://github.com/jtcarvalho/hive)
|
|
22
|
+
|
|
23
|
+
## 🚀 Installation
|
|
24
|
+
|
|
25
|
+
### Método 1: Instalação Local (Desenvolvimento)
|
|
26
|
+
|
|
27
|
+
Instale o pacote em modo editável para desenvolvimento ou uso local:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Clone o repositório
|
|
31
|
+
git clone https://github.com/jtcarvalho/wasp.git
|
|
32
|
+
cd wasp
|
|
33
|
+
|
|
34
|
+
# Instale em modo editável (recomendado)
|
|
35
|
+
pip install -e .
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Método 2: Ambiente Virtual Tradicional
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Clone o repositório
|
|
42
|
+
git clone https://github.com/jtcarvalho/wasp.git
|
|
43
|
+
cd wasp
|
|
44
|
+
|
|
45
|
+
# Crie ambiente virtual
|
|
46
|
+
python -m venv venv
|
|
47
|
+
|
|
48
|
+
# Ative o ambiente virtual
|
|
49
|
+
# No macOS/Linux:
|
|
50
|
+
source venv/bin/activate
|
|
51
|
+
# No Windows:
|
|
52
|
+
# venv\Scripts\activate
|
|
53
|
+
|
|
54
|
+
# Instale o pacote
|
|
55
|
+
pip install -e .
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Verificar Instalação
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Teste a importação
|
|
62
|
+
python -c "import wasp; print(f'WASP version: {wasp.__version__}')"
|
|
63
|
+
|
|
64
|
+
# Teste as funções principais
|
|
65
|
+
python -c "from wasp import partition_spectrum, calculate_wave_parameters; print('✓ Instalação bem-sucedida!')"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 📦 Key Dependencies
|
|
69
|
+
|
|
70
|
+
- **NumPy >= 2.1.0** (required for `np.trapezoid`)
|
|
71
|
+
- pandas >= 2.2.0
|
|
72
|
+
- xarray >= 2024.11.0
|
|
73
|
+
- matplotlib >= 3.8.0
|
|
74
|
+
- scipy >= 1.14.0
|
|
75
|
+
- scikit-image >= 0.22.0
|
|
76
|
+
- netCDF4 >= 1.5.4
|
|
77
|
+
|
|
78
|
+
> ⚠️ **Importante:** NumPy < 2.1.0 causará erros pois `np.trapezoid` não está disponível.
|
|
79
|
+
|
|
80
|
+
## 💡 Uso Rápido
|
|
81
|
+
|
|
82
|
+
### Como Biblioteca Python
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
import numpy as np
|
|
86
|
+
from wasp import partition_spectrum, calculate_wave_parameters
|
|
87
|
+
|
|
88
|
+
# Seu espectro 2D (freq x dir)
|
|
89
|
+
E = np.array(...) # matriz de energia espectral [m²/Hz/rad]
|
|
90
|
+
freq = np.array(...) # frequências [Hz]
|
|
91
|
+
dirs = np.array(...) # direções [graus, convenção oceanográfica]
|
|
92
|
+
|
|
93
|
+
# Particionar o espectro
|
|
94
|
+
partitions = partition_spectrum(
|
|
95
|
+
E, freq, dirs,
|
|
96
|
+
energy_threshold=1e-6,
|
|
97
|
+
max_partitions=3
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Calcular parâmetros de cada partição
|
|
101
|
+
for i, partition in enumerate(partitions):
|
|
102
|
+
params = calculate_wave_parameters(partition, freq, dirs)
|
|
103
|
+
print(f"Partição {i+1}:")
|
|
104
|
+
print(f" Hs = {params['Hs']:.2f} m")
|
|
105
|
+
print(f" Tp = {params['Tp']:.1f} s")
|
|
106
|
+
print(f" Dp = {params['Dp']:.1f} deg")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Scripts de Exemplo
|
|
110
|
+
|
|
111
|
+
Veja a pasta [examples/](examples/) para scripts completos:
|
|
112
|
+
|
|
113
|
+
- **01_partition_sar.py**: Processar espectros SAR (Sentinel-1)
|
|
114
|
+
- **02_partition_ww3.py**: Processar espectros WaveWatch III
|
|
115
|
+
- **03_partition_ndbc.py**: Template para processar dados de bóia NDBC
|
|
116
|
+
- **04_validate.py**: Comparar e validar resultados SAR vs WW3
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
cd examples/
|
|
120
|
+
python 01_partition_sar.py
|
|
121
|
+
```
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "wasp-ocean"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "WAve Spectra Partitioning"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Your Name", email = "your.email@example.com"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["waves", "oceanography", "SAR", "WaveWatch3", "spectral partitioning"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"Topic :: Scientific/Engineering :: Atmospheric Science",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
dependencies = [
|
|
28
|
+
"numpy>=2.1.0,<3.0.0",
|
|
29
|
+
"pandas>=2.2.0,<3.0.0",
|
|
30
|
+
"scipy>=1.14.0,<2.0.0",
|
|
31
|
+
"xarray>=2024.11.0",
|
|
32
|
+
"netCDF4>=1.5.4,<2.0.0",
|
|
33
|
+
"matplotlib>=3.8.0,<4.0.0",
|
|
34
|
+
"scikit-image>=0.22.0,<1.0.0",
|
|
35
|
+
"tqdm>=4.65.0,<5.0.0",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/jtcarvalho/wasp"
|
|
40
|
+
Repository = "https://github.com/jtcarvalho/wasp"
|
|
41
|
+
Documentation = "https://github.com/jtcarvalho/wasp/blob/main/README.md"
|
|
42
|
+
Issues = "https://github.com/jtcarvalho/wasp/issues"
|
|
43
|
+
|
|
44
|
+
[tool.setuptools]
|
|
45
|
+
package-dir = {"" = "src"}
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.packages.find]
|
|
48
|
+
where = ["src"]
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.package-data]
|
|
51
|
+
wasp = ["*.py"]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WASP - Wave Analysis from Sentinel and WaveWatch III Partitioning
|
|
3
|
+
==================================================================
|
|
4
|
+
|
|
5
|
+
A Python package for spectral wave partitioning from SAR, WaveWatch III, and buoy data.
|
|
6
|
+
|
|
7
|
+
Core modules:
|
|
8
|
+
- partition: Spectral partitioning algorithms (Hanson & Phillips 2001)
|
|
9
|
+
- wave_params: Wave parameter calculations (Hs, Tp, Dir, etc.)
|
|
10
|
+
- io_sar: SAR data input/output
|
|
11
|
+
- io_ww3: WaveWatch III data input/output
|
|
12
|
+
- plotting: Visualization tools
|
|
13
|
+
- utils: Utility functions
|
|
14
|
+
|
|
15
|
+
Example usage:
|
|
16
|
+
-------------
|
|
17
|
+
from wasp.partition import partition_spectrum
|
|
18
|
+
from wasp.wave_params import calculate_wave_parameters
|
|
19
|
+
|
|
20
|
+
# Partition a 2D spectrum
|
|
21
|
+
partitions = partition_spectrum(
|
|
22
|
+
E, freq, dirs,
|
|
23
|
+
energy_threshold=1e-6,
|
|
24
|
+
max_partitions=3
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Calculate wave parameters for each partition
|
|
28
|
+
for i, partition in enumerate(partitions):
|
|
29
|
+
params = calculate_wave_parameters(partition, freq, dirs)
|
|
30
|
+
print(f"Partition {i+1}: Hs={params['Hs']:.2f}m, Tp={params['Tp']:.1f}s")
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
__version__ = "0.1.0"
|
|
34
|
+
__author__ = "Your Name"
|
|
35
|
+
|
|
36
|
+
# Import main functions for easy access
|
|
37
|
+
from .partition import partition_spectrum
|
|
38
|
+
from .wave_params import calculate_wave_parameters
|
|
39
|
+
from .utils import spectrum1d_from_2d
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
'partition_spectrum',
|
|
43
|
+
'calculate_wave_parameters',
|
|
44
|
+
'spectrum1d_from_2d',
|
|
45
|
+
]
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Funções para leitura e conversão de dados SAR (Sentinel-1)
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def convert_sar_energy_units(E_sar, k, phi):
|
|
10
|
+
"""
|
|
11
|
+
Converte espectro SAR de número de onda para frequência em m²·s·rad⁻¹ (padrão WW3).
|
|
12
|
+
|
|
13
|
+
Aplica conversão usando o jacobiano da relação de dispersão:
|
|
14
|
+
E(f,θ) [m²·s·rad⁻¹] = E(k,θ) [m⁴] × |dk/df| × (π/180)
|
|
15
|
+
|
|
16
|
+
Onde:
|
|
17
|
+
- |dk/df| = 8π²f/g (jacobiano da relação de dispersão ω² = gk)
|
|
18
|
+
- π/180 converte de θ[graus] para θ[radianos]
|
|
19
|
+
|
|
20
|
+
Parameters:
|
|
21
|
+
-----------
|
|
22
|
+
E_sar : ndarray
|
|
23
|
+
Espectro SAR em número de onda [m⁴]
|
|
24
|
+
k : ndarray
|
|
25
|
+
Números de onda [rad/m]
|
|
26
|
+
phi : ndarray
|
|
27
|
+
Direções [graus]
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
--------
|
|
31
|
+
E_m2_s_rad : ndarray (NF, ND)
|
|
32
|
+
Espectro convertido [m²·s·rad⁻¹]
|
|
33
|
+
freq : ndarray (NF,)
|
|
34
|
+
Frequências [Hz]
|
|
35
|
+
phi_oceanographic : ndarray (ND,)
|
|
36
|
+
Direções oceanográficas [graus]
|
|
37
|
+
dirs_rad : ndarray (ND,)
|
|
38
|
+
Direções em radianos
|
|
39
|
+
"""
|
|
40
|
+
g = 9.81
|
|
41
|
+
omega = np.sqrt(g * k)
|
|
42
|
+
freq = omega / (2 * np.pi)
|
|
43
|
+
|
|
44
|
+
# Jacobiano da transformação k -> f
|
|
45
|
+
# Da relação de dispersão: ω² = gk, onde ω = 2πf
|
|
46
|
+
# dk/df = 8π²f/g
|
|
47
|
+
dkdf = 8 * np.pi**2 * freq / g
|
|
48
|
+
dkdf_matrix = dkdf.reshape(-1, 1)
|
|
49
|
+
|
|
50
|
+
# Conversão de direção: graus -> radianos na densidade espectral
|
|
51
|
+
# E(θ_rad) = E(θ_deg) × (π/180)
|
|
52
|
+
deg_to_rad_factor = np.pi / 180.0
|
|
53
|
+
|
|
54
|
+
# E(k,θ) [m⁴] -> E(f,θ) [m²·s·rad⁻¹]
|
|
55
|
+
E_m2_s_rad = E_sar * dkdf_matrix * deg_to_rad_factor
|
|
56
|
+
|
|
57
|
+
# Ajuste shape para (NF, ND)
|
|
58
|
+
if E_m2_s_rad.shape[0] != len(freq):
|
|
59
|
+
E_m2_s_rad = E_m2_s_rad.T
|
|
60
|
+
|
|
61
|
+
# Dados SAR já estão em convenção oceanográfica (direção PARA onde vai)
|
|
62
|
+
phi_oceanographic = phi
|
|
63
|
+
dirs_rad = np.radians(phi_oceanographic)
|
|
64
|
+
|
|
65
|
+
# Diagnóstico: calcular m0 e Hs
|
|
66
|
+
ddir = 2 * np.pi / len(phi)
|
|
67
|
+
m0 = 0
|
|
68
|
+
for j in range(len(phi)):
|
|
69
|
+
E_clean = np.where(np.isfinite(E_m2_s_rad[:, j]) & (E_m2_s_rad[:, j] >= 0),
|
|
70
|
+
E_m2_s_rad[:, j], 0)
|
|
71
|
+
m0 += np.trapezoid(E_clean, freq) * ddir
|
|
72
|
+
hs = 4 * np.sqrt(m0)
|
|
73
|
+
|
|
74
|
+
print(f"╔══════════════════════════════════════════════════════════════╗")
|
|
75
|
+
print(f"║ CONVERSÃO SAR: m⁴ → m²·s·rad⁻¹ (WW3 units) ║")
|
|
76
|
+
print(f"╠══════════════════════════════════════════════════════════════╣")
|
|
77
|
+
print(f"║ Shape: {str(E_sar.shape):>52} ║")
|
|
78
|
+
print(f"║ Frequências: {len(freq):>2d} bins | Direções: {len(phi):>2d} bins ║")
|
|
79
|
+
print(f"║ Freq range: {freq[0]:.4f} - {freq[-1]:.4f} Hz ║")
|
|
80
|
+
print(f"║ Dir range: {phi[0]:.1f}° - {phi[-1]:.1f}° ║")
|
|
81
|
+
print(f"╟──────────────────────────────────────────────────────────────╢")
|
|
82
|
+
print(f"║ Jacobiano dk/df: {np.min(dkdf):.4f} - {np.max(dkdf):.4f} ║")
|
|
83
|
+
print(f"║ Fator angular (π/180): {deg_to_rad_factor:.6f} ║")
|
|
84
|
+
print(f"╟──────────────────────────────────────────────────────────────╢")
|
|
85
|
+
print(f"║ Parâmetros integrados: ║")
|
|
86
|
+
print(f"║ m0 = {m0:>10.6f} m² ║")
|
|
87
|
+
print(f"║ Hs = {hs:>10.6f} m ║")
|
|
88
|
+
print(f"╚══════════════════════════════════════════════════════════════╝")
|
|
89
|
+
|
|
90
|
+
return E_m2_s_rad, freq, phi_oceanographic, dirs_rad
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def load_sar_spectrum(ds, date_time=None, index=0):
|
|
94
|
+
"""
|
|
95
|
+
Carrega espectro SAR para data/hora específica.
|
|
96
|
+
|
|
97
|
+
Compatível com arquivos preprocessados Sentinel-1A/B (CMEMS).
|
|
98
|
+
Converte automaticamente de SAR (m⁴) para m²·s·rad⁻¹ (padrão WW3).
|
|
99
|
+
|
|
100
|
+
Parameters:
|
|
101
|
+
-----------
|
|
102
|
+
ds : xarray.Dataset
|
|
103
|
+
Dataset SAR aberto
|
|
104
|
+
date_time : str or pd.Timestamp, optional
|
|
105
|
+
Data/hora específica para buscar. Se None, usa index.
|
|
106
|
+
index : int
|
|
107
|
+
Índice da observação a carregar (usado se date_time=None)
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
--------
|
|
111
|
+
E2d : ndarray (NF, ND)
|
|
112
|
+
Espectro direcional 2D [m²·s·rad⁻¹]
|
|
113
|
+
freq : ndarray (NF,)
|
|
114
|
+
Frequências [Hz]
|
|
115
|
+
dirs : ndarray (ND,)
|
|
116
|
+
Direções [graus]
|
|
117
|
+
dirs_rad : ndarray (ND,)
|
|
118
|
+
Direções [radianos]
|
|
119
|
+
actual_time : pd.Timestamp
|
|
120
|
+
Timestamp da observação carregada
|
|
121
|
+
"""
|
|
122
|
+
print("Variáveis disponíveis no arquivo SAR:", list(ds.variables.keys()))
|
|
123
|
+
|
|
124
|
+
# Função auxiliar para buscar variável por múltiplos nomes possíveis
|
|
125
|
+
def get_var(ds, varnames):
|
|
126
|
+
for var in varnames:
|
|
127
|
+
if var in ds.variables:
|
|
128
|
+
return ds[var].values
|
|
129
|
+
raise ValueError(f"Nenhuma das variáveis {varnames} encontrada no arquivo SAR.")
|
|
130
|
+
|
|
131
|
+
# Nomes possíveis para cada variável
|
|
132
|
+
wave_spec_names = ['wave_spec', 'obs_params/wave_spec', 'wave_spectrum',
|
|
133
|
+
'obs_params/wave_spectrum']
|
|
134
|
+
k_names = ['wavenumber_spec', 'obs_params/wavenumber_spec']
|
|
135
|
+
phi_names = ['direction_spec', 'obs_params/direction_spec']
|
|
136
|
+
time_names = ['time', 'obs_params/time', 'TIME', 'obs_time',
|
|
137
|
+
'acquisition_time', 'time_center', 'valid_time', 't']
|
|
138
|
+
|
|
139
|
+
try:
|
|
140
|
+
# Tentar formato preprocessado CMEMS
|
|
141
|
+
E_sar = get_var(ds, wave_spec_names) # (NF, ND, Nobs)
|
|
142
|
+
k = get_var(ds, k_names) # (NF,)
|
|
143
|
+
phi = get_var(ds, phi_names) # (ND,)
|
|
144
|
+
|
|
145
|
+
# Buscar tempo
|
|
146
|
+
times = None
|
|
147
|
+
for tname in time_names:
|
|
148
|
+
if tname in ds.variables:
|
|
149
|
+
times = ds[tname].values
|
|
150
|
+
break
|
|
151
|
+
|
|
152
|
+
# Selecionar observação específica
|
|
153
|
+
nobs = E_sar.shape[2] if E_sar.ndim == 3 else 1
|
|
154
|
+
if nobs > 1:
|
|
155
|
+
if date_time is not None and times is not None:
|
|
156
|
+
if isinstance(date_time, str):
|
|
157
|
+
date_time = pd.to_datetime(date_time)
|
|
158
|
+
idx = abs(times - np.datetime64(date_time)).argmin()
|
|
159
|
+
actual_time = pd.to_datetime(times[idx])
|
|
160
|
+
else:
|
|
161
|
+
idx = index
|
|
162
|
+
actual_time = pd.to_datetime(times[idx]) if times is not None else None
|
|
163
|
+
E_sar = np.squeeze(E_sar[:, :, idx])
|
|
164
|
+
else:
|
|
165
|
+
E_sar = np.squeeze(E_sar)
|
|
166
|
+
actual_time = pd.to_datetime(times[0]) if times is not None else None
|
|
167
|
+
|
|
168
|
+
print(f"Usando arquivo preprocessado (CMEMS), shape E_sar: {E_sar.shape}")
|
|
169
|
+
|
|
170
|
+
except Exception as e:
|
|
171
|
+
# Tentar formato antigo (oswPolSpec)
|
|
172
|
+
if 'oswPolSpec' in ds.variables:
|
|
173
|
+
if 'time' in ds and len(ds.time) > 1:
|
|
174
|
+
if date_time is not None:
|
|
175
|
+
if isinstance(date_time, str):
|
|
176
|
+
date_time = pd.to_datetime(date_time)
|
|
177
|
+
idx = abs(ds.time.values - np.datetime64(date_time)).argmin()
|
|
178
|
+
actual_time = pd.to_datetime(ds.time.values[idx])
|
|
179
|
+
else:
|
|
180
|
+
idx = index
|
|
181
|
+
actual_time = pd.to_datetime(ds.time.values[idx])
|
|
182
|
+
E_sar = np.squeeze(ds.oswPolSpec.values[idx])
|
|
183
|
+
else:
|
|
184
|
+
E_sar = np.squeeze(ds.oswPolSpec.values)
|
|
185
|
+
if 'time' in ds:
|
|
186
|
+
timestamp = ds.time.values[0]
|
|
187
|
+
actual_time = pd.to_datetime(timestamp)
|
|
188
|
+
else:
|
|
189
|
+
actual_time = None
|
|
190
|
+
k = ds.oswK.values
|
|
191
|
+
phi = ds.oswPhi.values
|
|
192
|
+
print(f"Usando arquivo SAR antigo, shape E_sar: {E_sar.shape}")
|
|
193
|
+
else:
|
|
194
|
+
raise ValueError("Arquivo SAR não possui variáveis reconhecidas "
|
|
195
|
+
"(nem wave_spec nem oswPolSpec)")
|
|
196
|
+
|
|
197
|
+
print(f"Shape k: {k.shape}")
|
|
198
|
+
print(f"Shape phi: {phi.shape}")
|
|
199
|
+
|
|
200
|
+
# Converter SAR (m⁴) para m²·s·rad⁻¹ (padrão WW3)
|
|
201
|
+
E2d, freq, dirs, dirs_rad = convert_sar_energy_units(E_sar, k, phi)
|
|
202
|
+
|
|
203
|
+
return E2d, freq, dirs, dirs_rad, actual_time
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Funções para leitura e processamento de dados WW3
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import xarray as xr
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def find_closest_time(file_path, target_time_dt):
|
|
11
|
+
"""
|
|
12
|
+
Encontra o timestamp mais próximo no dataset WW3 ao tempo alvo.
|
|
13
|
+
|
|
14
|
+
Parameters:
|
|
15
|
+
-----------
|
|
16
|
+
file_path : str
|
|
17
|
+
Caminho do arquivo NetCDF do WW3
|
|
18
|
+
target_time_dt : pd.Timestamp
|
|
19
|
+
Tempo alvo para buscar
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
--------
|
|
23
|
+
itime : int
|
|
24
|
+
Índice do tempo mais próximo
|
|
25
|
+
closest_time : pd.Timestamp
|
|
26
|
+
Timestamp mais próximo encontrado
|
|
27
|
+
time_diff_hours : float
|
|
28
|
+
Diferença temporal em horas
|
|
29
|
+
"""
|
|
30
|
+
ds_temp = xr.open_dataset(file_path)
|
|
31
|
+
ww3_times = pd.to_datetime(ds_temp.time.values)
|
|
32
|
+
|
|
33
|
+
time_diffs = np.abs(ww3_times - target_time_dt)
|
|
34
|
+
itime = np.argmin(time_diffs)
|
|
35
|
+
closest_time = ww3_times[itime]
|
|
36
|
+
time_diff_hours = time_diffs[itime].total_seconds() / 3600
|
|
37
|
+
|
|
38
|
+
ds_temp.close()
|
|
39
|
+
|
|
40
|
+
return itime, closest_time, time_diff_hours
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def load_ww3_spectrum(file_path, time_index):
|
|
44
|
+
"""
|
|
45
|
+
Carrega espectro direcional 2D do WW3 e coordenadas.
|
|
46
|
+
|
|
47
|
+
Parameters:
|
|
48
|
+
-----------
|
|
49
|
+
file_path : str
|
|
50
|
+
Caminho do arquivo NetCDF do WW3
|
|
51
|
+
time_index : int
|
|
52
|
+
Índice temporal a carregar
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
--------
|
|
56
|
+
E2d : ndarray (NF, ND)
|
|
57
|
+
Espectro direcional 2D [m²·s·rad⁻¹]
|
|
58
|
+
freq : ndarray (NF,)
|
|
59
|
+
Frequências [Hz]
|
|
60
|
+
dirs : ndarray (ND,)
|
|
61
|
+
Direções [graus]
|
|
62
|
+
dirs_rad : ndarray (ND,)
|
|
63
|
+
Direções [radianos]
|
|
64
|
+
lon : float
|
|
65
|
+
Longitude do ponto
|
|
66
|
+
lat : float
|
|
67
|
+
Latitude do ponto
|
|
68
|
+
"""
|
|
69
|
+
ds = xr.open_dataset(file_path)
|
|
70
|
+
|
|
71
|
+
E2d = ds.efth[time_index, 0, :, :].values
|
|
72
|
+
freq = ds.frequency.values
|
|
73
|
+
dirs = ds.direction.values
|
|
74
|
+
dirs_rad = np.radians(dirs)
|
|
75
|
+
lon = ds.longitude.values[0]
|
|
76
|
+
lat = ds.latitude.values[0]
|
|
77
|
+
|
|
78
|
+
ds.close()
|
|
79
|
+
|
|
80
|
+
return E2d, freq, dirs, dirs_rad, lon, lat
|