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.
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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