oafuncs 0.0.98.51__tar.gz → 0.0.98.52__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. {oafuncs-0.0.98.51/oafuncs.egg-info → oafuncs-0.0.98.52}/PKG-INFO +1 -1
  2. oafuncs-0.0.98.52/oafuncs/_script/process_roms.py +620 -0
  3. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_linux.py +53 -4
  4. oafuncs-0.0.98.52/oafuncs/oa_model/roms.py +42 -0
  5. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52/oafuncs.egg-info}/PKG-INFO +1 -1
  6. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs.egg-info/SOURCES.txt +2 -2
  7. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/setup.py +1 -1
  8. oafuncs-0.0.98.51/oafuncs/oa_model/roms/__init__.py +0 -20
  9. oafuncs-0.0.98.51/oafuncs/oa_model/roms/test.py +0 -19
  10. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/LICENSE.txt +0 -0
  11. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/MANIFEST.in +0 -0
  12. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/README.md +0 -0
  13. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/__init__.py +0 -0
  14. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/_data/hycom.png +0 -0
  15. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/_data/oafuncs.png +0 -0
  16. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/_script/cprogressbar.py +0 -0
  17. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/_script/data_interp.py +0 -0
  18. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/_script/email.py +0 -0
  19. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/_script/netcdf_merge.py +0 -0
  20. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/_script/netcdf_modify.py +0 -0
  21. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/_script/netcdf_write.py +0 -0
  22. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/_script/parallel.py +0 -0
  23. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/_script/parallel_bak.py +0 -0
  24. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/_script/plot_dataset.py +0 -0
  25. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/_script/replace_file_content.py +0 -0
  26. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_cmap.py +0 -0
  27. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_data.py +0 -0
  28. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_date.py +0 -0
  29. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_down/User_Agent-list.txt +0 -0
  30. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_down/__init__.py +0 -0
  31. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_down/hycom_3hourly.py +0 -0
  32. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_down/idm.py +0 -0
  33. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_down/literature.py +0 -0
  34. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_down/read_proxy.py +0 -0
  35. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_down/test_ua.py +0 -0
  36. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_down/user_agent.py +0 -0
  37. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_draw.py +0 -0
  38. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_file.py +0 -0
  39. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_geo.py +0 -0
  40. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_help.py +0 -0
  41. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_model/__init__.py +0 -0
  42. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_model/wrf/__init__.py +0 -0
  43. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_model/wrf/little_r.py +0 -0
  44. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_nc.py +0 -0
  45. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_python.py +0 -0
  46. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_sign/__init__.py +0 -0
  47. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_sign/meteorological.py +0 -0
  48. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_sign/ocean.py +0 -0
  49. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_sign/scientific.py +0 -0
  50. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs/oa_tool.py +0 -0
  51. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs.egg-info/dependency_links.txt +0 -0
  52. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs.egg-info/requires.txt +0 -0
  53. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/oafuncs.egg-info/top_level.txt +0 -0
  54. {oafuncs-0.0.98.51 → oafuncs-0.0.98.52}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oafuncs
3
- Version: 0.0.98.51
3
+ Version: 0.0.98.52
4
4
  Summary: Oceanic and Atmospheric Functions
5
5
  Home-page: https://github.com/Industry-Pays/OAFuncs
6
6
  Author: Kun Liu
@@ -0,0 +1,620 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ process_roms_uv_fixed.py
4
+
5
+ ROMS -> lon/lat remapping with correct u/v handling following Fortran workflow:
6
+ - Average u/v to rho points (pad both ends then average)
7
+ - Vertical interpolate on SOURCE rho depths to standard target depths (spline fallback linear)
8
+ - Horizontal remap each target level to output lon/lat (xESMF)
9
+ Robust Cs handling, no silent NaN propagation, endpoint handling like Fortran splint.
10
+
11
+ Usage: edit __main__ input_nc/output_nc/varnames/target_lon/target_lat/target_depth.
12
+ Dependencies: numpy, xarray, scipy, xesmf
13
+ """
14
+
15
+ from typing import Optional, Sequence, Dict, Any, Tuple
16
+ import numpy as np
17
+ import xarray as xr
18
+ from scipy.interpolate import interp1d, CubicSpline
19
+ from rich import print
20
+
21
+ # Verbose toggles
22
+ VERBOSE = True
23
+ VERBOSE_DEBUG = False
24
+
25
+ # -------------------------
26
+ # Utilities
27
+ # -------------------------
28
+ def safe_nanmin(a):
29
+ a = np.asarray(a)
30
+ if np.isfinite(a).any():
31
+ return np.nanmin(a)
32
+ return np.nan
33
+
34
+
35
+ def safe_nanmax(a):
36
+ a = np.asarray(a)
37
+ if np.isfinite(a).any():
38
+ return np.nanmax(a)
39
+ return np.nan
40
+
41
+
42
+ def avg_to_rho_axis_padboth(arr: np.ndarray, axis: int) -> np.ndarray:
43
+ """
44
+ Pad both ends with edge values and average adjacent pairs along axis.
45
+ Input length n -> output length n+1. Works for N-D arrays.
46
+ """
47
+ arr = np.asarray(arr)
48
+ if arr.size == 0:
49
+ return arr
50
+ arr = arr.astype(np.float64, copy=False)
51
+ axis = axis if axis >= 0 else arr.ndim + axis
52
+ pad = [(0, 0)] * arr.ndim
53
+ pad[axis] = (1, 1)
54
+ arr_pad = np.pad(arr, pad, mode='edge')
55
+ # average adjacent pairs
56
+ slic_l = [slice(None)] * arr.ndim
57
+ slic_r = [slice(None)] * arr.ndim
58
+ slic_l[axis] = slice(0, arr_pad.shape[axis] - 1)
59
+ slic_r[axis] = slice(1, arr_pad.shape[axis])
60
+ left = arr_pad[tuple(slic_l)]
61
+ right = arr_pad[tuple(slic_r)]
62
+ out = 0.5 * (left + right)
63
+ return np.ascontiguousarray(out)
64
+
65
+
66
+ def _need_periodic(lon1d: np.ndarray, tol: float = 1.0) -> bool:
67
+ lon1d = np.asarray(lon1d, dtype=np.float64)
68
+ if lon1d.size == 0:
69
+ return False
70
+ span = np.nanmax(lon1d) - np.nanmin(lon1d)
71
+ return span > (360 - tol)
72
+
73
+
74
+ # -------------------------
75
+ # Vertical stretching helpers (Cs)
76
+ # -------------------------
77
+ def _compute_C_from_vstretching(s: np.ndarray, theta_s: float, theta_b: float, vstretching: int) -> np.ndarray:
78
+ s = np.asarray(s, dtype=np.float64)
79
+ vstretching = int(vstretching)
80
+ theta_s = float(theta_s)
81
+ theta_b = float(theta_b)
82
+ if vstretching == 1:
83
+ return (1 - theta_b) * (np.sinh(theta_s * s) / np.sinh(theta_s)) + \
84
+ theta_b * (-0.5 + 0.5 * np.tanh(theta_s * (s + 0.5)) / np.tanh(0.5 * theta_s))
85
+ elif vstretching == 2:
86
+ Csur = (1 - np.cosh(theta_s * s)) / (np.cosh(theta_s) - 1)
87
+ Cbot = -1 + (1 - np.sinh(theta_b * (s + 1))) / np.sinh(theta_b)
88
+ alpha, beta = 3.0, 3.0
89
+ Cweight = (s + 1) ** alpha * (1 + (alpha / beta) * (1 - (s + 1) ** beta))
90
+ return Cweight * Csur + (1 - Cweight) * Cbot
91
+ else:
92
+ # 4
93
+ Ctemp = (1 - np.cosh(theta_s * s)) / (np.cosh(theta_s) - 1)
94
+ denom = (1 - np.exp(-theta_b))
95
+ denom = denom if denom != 0 else 1e-12
96
+ return (np.exp(theta_b * Ctemp) - 1) / denom
97
+
98
+
99
+ # -------------------------
100
+ # Compute source z (vectorized), validate Cs
101
+ # -------------------------
102
+ def get_roms_depths(ds: xr.Dataset, is_w: bool = False, time_index: Optional[int] = None,
103
+ eps: float = 1e-8) -> xr.DataArray:
104
+ vtransform = int(getattr(ds, 'Vtransform', ds.attrs.get('Vtransform', 2)))
105
+ vstretching = int(getattr(ds, 'Vstretching', ds.attrs.get('Vstretching', 4)))
106
+ theta_s = float(ds.attrs.get('theta_s', getattr(ds, 'theta_s', 5.0)))
107
+ theta_b = float(ds.attrs.get('theta_b', getattr(ds, 'theta_b', 0.4)))
108
+
109
+ h = ds['h'].astype('f8').values
110
+ if 'mask_rho' in ds.variables:
111
+ mask_rho = ds['mask_rho'].astype('f8').values
112
+ h = h.copy()
113
+ h[mask_rho == 0] = np.nan
114
+
115
+ hc = None
116
+ if 'hc' in ds.variables:
117
+ try:
118
+ hc = float(np.array(ds['hc'].values))
119
+ except Exception:
120
+ hc = None
121
+ if hc is None:
122
+ hc = float(ds.attrs.get('hc', 1.0))
123
+ hc = float(hc)
124
+
125
+ # zeta
126
+ if 'zeta' in ds.variables:
127
+ zeta_da = ds['zeta']
128
+ if 'ocean_time' in zeta_da.dims:
129
+ if time_index is None:
130
+ zeta = zeta_da.astype('f8').values
131
+ else:
132
+ zeta = zeta_da.isel(ocean_time=int(time_index)).astype('f8').values
133
+ else:
134
+ zeta = zeta_da.astype('f8').values
135
+ else:
136
+ if time_index is None and 'ocean_time' in ds.dims:
137
+ ntime = int(ds.sizes.get('ocean_time', 1))
138
+ zeta = np.zeros((ntime,) + h.shape, dtype=np.float64)
139
+ else:
140
+ zeta = np.zeros_like(h, dtype=np.float64)
141
+
142
+ if is_w:
143
+ Ns = int(ds.sizes.get('s_w', 0))
144
+ sc_name, Cs_name = 'sc_w', 'Cs_w'
145
+ s_dim = 's_w'
146
+ else:
147
+ Ns = int(ds.sizes.get('s_rho', 0))
148
+ sc_name, Cs_name = 'sc_r', 'Cs_r'
149
+ s_dim = 's_rho'
150
+
151
+ if sc_name in ds.variables:
152
+ s = np.asarray(ds[sc_name].values, dtype=np.float64).ravel()
153
+ else:
154
+ if is_w:
155
+ s = (np.arange(0, Ns) - Ns) / float(Ns)
156
+ else:
157
+ s = (np.arange(1, Ns + 1) - Ns - 0.5) / float(Ns)
158
+
159
+ # Cs read + validate
160
+ if Cs_name in ds.variables:
161
+ try:
162
+ C_read = np.asarray(ds[Cs_name].values, dtype=np.float64).ravel()
163
+ except Exception:
164
+ C_read = None
165
+ else:
166
+ C_read = None
167
+
168
+ if C_read is None:
169
+ C = _compute_C_from_vstretching(s, theta_s, theta_b, vstretching)
170
+ else:
171
+ cmin = safe_nanmin(C_read)
172
+ cmax = safe_nanmax(C_read)
173
+ if (not np.isfinite(cmin)) or (not np.isfinite(cmax)) or (cmax > 5.0) or (cmin < -50.0):
174
+ if VERBOSE:
175
+ print(f"[get_roms_depths] Cs suspicious (min={cmin}, max={cmax}), recomputing")
176
+ C = _compute_C_from_vstretching(s, theta_s, theta_b, vstretching)
177
+ else:
178
+ C = C_read
179
+
180
+ s = np.asarray(s, dtype=np.float64).ravel()
181
+ C = np.asarray(C, dtype=np.float64).ravel()
182
+
183
+ # prepare h arr
184
+ h_arr = np.asarray(h, dtype=np.float64).copy()
185
+ h_arr[~np.isfinite(h_arr)] = np.nan
186
+ h_arr[h_arr <= 0] = np.nan
187
+
188
+ s3 = s[:, None, None]
189
+ C3 = C[:, None, None]
190
+ h3 = h_arr[None, :, :]
191
+
192
+ if zeta.ndim == 3:
193
+ zeta3 = zeta[:, None, :, :]
194
+ if vtransform == 1:
195
+ Zo = hc * s3 + (h3 - hc) * C3
196
+ denom_safe = np.where(np.abs(h3) > eps, h3, np.nan)
197
+ z = Zo[None, ...] + zeta3 * (1.0 + Zo[None, ...] / denom_safe[None, ...])
198
+ else:
199
+ denom_safe = np.where(np.abs(hc + h3) > eps, (hc + h3), np.nan)
200
+ Zo = (hc * s3 + h3 * C3) / denom_safe
201
+ z = zeta3 + (zeta3 + h3[None, ...]) * Zo[None, ...]
202
+ time_dim = 'ocean_time'
203
+ else:
204
+ zeta3 = zeta[None, :, :]
205
+ if vtransform == 1:
206
+ Zo = hc * s3 + (h3 - hc) * C3
207
+ denom_safe = np.where(np.abs(h3) > eps, h3, np.nan)
208
+ z = Zo + zeta3 * (1.0 + Zo / denom_safe)
209
+ else:
210
+ denom_safe = np.where(np.abs(hc + h3) > eps, (hc + h3), np.nan)
211
+ Zo = (hc * s3 + h3 * C3) / denom_safe
212
+ z = zeta3 + (zeta3 + h3) * Zo
213
+ time_dim = None
214
+
215
+ # bounds
216
+ if z.ndim == 4:
217
+ zeta_b = zeta3; h_b = h3[None, ...]
218
+ else:
219
+ zeta_b = zeta3; h_b = h3
220
+ z_max_allowed = zeta_b
221
+ z_min_allowed = zeta_b - h_b
222
+ z = np.where((z <= (z_max_allowed + 1e-6)) & (z >= (z_min_allowed - 1e-6)), z, np.nan)
223
+ z = np.where(np.abs(z) >= 2e4, np.nan, z)
224
+
225
+ # build DataArray
226
+ if time_dim is not None:
227
+ dims = (time_dim, s_dim, 'eta_rho', 'xi_rho')
228
+ coords = {time_dim: ds['ocean_time'] if 'ocean_time' in ds.coords else np.arange(z.shape[0])}
229
+ else:
230
+ dims = (s_dim, 'eta_rho', 'xi_rho')
231
+ coords = {}
232
+ coords[s_dim] = s
233
+ coords['eta_rho'] = ds['eta_rho'] if 'eta_rho' in ds.coords else np.arange(h_arr.shape[0])
234
+ coords['xi_rho'] = ds['xi_rho'] if 'xi_rho' in ds.coords else np.arange(h_arr.shape[1])
235
+
236
+ z_da = xr.DataArray(data=z, dims=dims, coords=coords)
237
+ if VERBOSE:
238
+ try:
239
+ if time_dim is not None:
240
+ zk = z_da.isel({time_dim: 0})
241
+ print(f"[get_roms_depths] sample z min/max (time0): {safe_nanmin(zk.values):.3f}/{safe_nanmax(zk.values):.3f}")
242
+ else:
243
+ print(f"[get_roms_depths] sample z min/max: {safe_nanmin(z):.3f}/{safe_nanmax(z):.3f}")
244
+ except Exception:
245
+ pass
246
+ return z_da
247
+
248
+
249
+ # -------------------------
250
+ # vertical interpolation per-column with Fortran endpoint policy
251
+ # -------------------------
252
+ def vertical_interp_with_endpoints(depths: np.ndarray, data: np.ndarray, target_depth: Sequence[float],
253
+ allow_extrapolation: bool = False, cubic_min_points: int = 4) -> np.ndarray:
254
+ """
255
+ Column-wise interpolation similar to Fortran spline+splint behavior:
256
+ - if n_valid >= cubic_min_points: cubic spline; for target outside source range assign endpoint value
257
+ - elif n_valid >=2: linear interpolation; for outside assign endpoint value
258
+ - else: NaN
259
+ depths: (Ns, eta, xi)
260
+ data: (Ns, eta, xi)
261
+ target_depth: 1D (negative depths)
262
+ """
263
+ depths = np.asarray(depths, dtype=np.float64)
264
+ data = np.asarray(data, dtype=np.float64)
265
+ target_depth = np.asarray(target_depth, dtype=np.float64)
266
+ nt = len(target_depth)
267
+ eta = data.shape[1]
268
+ xi = data.shape[2]
269
+ out = np.full((nt, eta, xi), np.nan, dtype=np.float64)
270
+
271
+ # compute h_actual to mask deeper than local depth
272
+ if np.isfinite(depths).any():
273
+ h_actual = np.abs(np.nanmin(depths, axis=0))
274
+ else:
275
+ h_actual = np.zeros((eta, xi), dtype=np.float64)
276
+ h_actual = np.where(np.isfinite(h_actual), h_actual, 0.0)
277
+
278
+ for i in range(eta):
279
+ for j in range(xi):
280
+ dcol = depths[:, i, j]
281
+ vcol = data[:, i, j]
282
+ ok = np.isfinite(dcol) & np.isfinite(vcol)
283
+ n_ok = int(ok.sum())
284
+ if n_ok < 2:
285
+ continue
286
+ d_valid = dcol[ok]
287
+ v_valid = vcol[ok]
288
+ # sort ascending depth for interp
289
+ order = np.argsort(d_valid)
290
+ d_valid = d_valid[order]
291
+ v_valid = v_valid[order]
292
+ # unique depths
293
+ d_u, idx = np.unique(d_valid, return_index=True)
294
+ v_u = v_valid[idx]
295
+ if d_u.size < 2:
296
+ continue
297
+ try:
298
+ if d_u.size >= cubic_min_points:
299
+ cs = CubicSpline(d_u, v_u, extrapolate=False)
300
+ y = cs(target_depth)
301
+ # handle endpoints: where target < d_u[0] -> assign v_u[0]; where > d_u[-1] -> v_u[-1]
302
+ left_mask = target_depth < d_u[0]
303
+ right_mask = target_depth > d_u[-1]
304
+ if left_mask.any():
305
+ y[left_mask] = v_u[0]
306
+ if right_mask.any():
307
+ y[right_mask] = v_u[-1]
308
+ out[:, i, j] = y
309
+ else:
310
+ f = interp1d(d_u, v_u, bounds_error=False, fill_value=np.nan, assume_sorted=True)
311
+ y = f(target_depth)
312
+ # fill outside with endpoints
313
+ left_mask = target_depth < d_u[0]
314
+ right_mask = target_depth > d_u[-1]
315
+ if left_mask.any():
316
+ y[left_mask] = v_u[0]
317
+ if right_mask.any():
318
+ y[right_mask] = v_u[-1]
319
+ out[:, i, j] = y
320
+ except Exception:
321
+ # fallback linear
322
+ try:
323
+ f = interp1d(d_u, v_u, bounds_error=False, fill_value=np.nan, assume_sorted=True)
324
+ y = f(target_depth)
325
+ left_mask = target_depth < d_u[0]
326
+ right_mask = target_depth > d_u[-1]
327
+ if left_mask.any():
328
+ y[left_mask] = v_u[0]
329
+ if right_mask.any():
330
+ y[right_mask] = v_u[-1]
331
+ out[:, i, j] = y
332
+ except Exception:
333
+ if VERBOSE_DEBUG:
334
+ print(f"[vertical_interp] interpolation failed at col {i},{j}")
335
+ continue
336
+ # ensure target deeper than local bathymetry get NaN
337
+ deeper = np.abs(target_depth) > h_actual[i, j]
338
+ if np.isfinite(h_actual[i, j]) and deeper.any():
339
+ out[deeper, i, j] = np.nan
340
+
341
+ if VERBOSE_DEBUG:
342
+ print("[vertical_interp] done; out nan_frac=", np.isnan(out).mean())
343
+ return out
344
+
345
+
346
+ # -------------------------
347
+ # xESMF regridder helpers
348
+ # -------------------------
349
+ def _make_src_dataset_for_grid(ds: xr.Dataset, grid: str) -> xr.Dataset:
350
+ # grid: 'rho','u','v'
351
+ if grid == 'rho':
352
+ lon2, lat2 = ds['lon_rho'].values, ds['lat_rho'].values
353
+ dims = ('eta_rho', 'xi_rho')
354
+ elif grid == 'u':
355
+ lon2, lat2 = ds['lon_u'].values, ds['lat_u'].values
356
+ dims = ('eta_u', 'xi_u')
357
+ elif grid == 'v':
358
+ lon2, lat2 = ds['lon_v'].values, ds['lat_v'].values
359
+ dims = ('eta_v', 'xi_v')
360
+ else:
361
+ raise ValueError("Unknown grid")
362
+ return xr.Dataset({'lon': (dims, lon2), 'lat': (dims, lat2)})
363
+
364
+
365
+ def _make_dst_dataset(target_lon: Sequence[float], target_lat: Sequence[float]) -> xr.Dataset:
366
+ return xr.Dataset({'lon': (('lon',), np.asarray(target_lon, dtype=np.float64)),
367
+ 'lat': (('lat',), np.asarray(target_lat, dtype=np.float64))})
368
+
369
+
370
+ def _make_regridder_safe(src_ds: xr.Dataset, dst_ds: xr.Dataset, method: str, fname: str, periodic: bool, reuse: bool):
371
+ try:
372
+ import xesmf as xe
373
+ except Exception as e:
374
+ raise ImportError("xesmf required. Install: conda install --solver=classic -c conda-forge xesmf esmpy. Error: " + str(e))
375
+ try:
376
+ reb = xe.Regridder(src_ds, dst_ds, method=method, periodic=periodic, filename=fname, reuse_weights=reuse)
377
+ except OSError:
378
+ reb = xe.Regridder(src_ds, dst_ds, method=method, periodic=periodic, reuse_weights=False)
379
+ try:
380
+ reb.to_netcdf(fname)
381
+ except Exception:
382
+ pass
383
+ return reb
384
+
385
+
386
+ # -------------------------
387
+ # Main processing function (implements Fortran logic for u/v)
388
+ # -------------------------
389
+ def process_roms_file(input_nc: str, output_nc: str, varnames: Sequence[str],
390
+ target_lon: Sequence[float], target_lat: Sequence[float], target_depth: Sequence[float],
391
+ overwrite_weights: bool = False):
392
+ ds = xr.open_dataset(input_nc, decode_cf=True, mask_and_scale=True)
393
+ times = np.asarray(ds['ocean_time'].values) if 'ocean_time' in ds.dims else np.array([0])
394
+
395
+ # dst ds for xESMF
396
+ dst_ds = _make_dst_dataset(target_lon, target_lat)
397
+ periodic = _need_periodic(np.asarray(target_lon))
398
+
399
+ # create rho regridder (final remap uses rho grid)
400
+ src_rho = _make_src_dataset_for_grid(ds, 'rho')
401
+ fname_rho = f"weights_bilin_rho_{src_rho['lon'].shape[0]}x{src_rho['lon'].shape[1]}_dst{len(target_lat)}x{len(target_lon)}.nc"
402
+ reb_rho = _make_regridder_safe(src_rho, dst_ds, method='bilinear', fname=fname_rho, periodic=periodic, reuse=not overwrite_weights)
403
+ reb_mask_rho = _make_regridder_safe(src_rho, dst_ds, method='nearest_s2d', fname=fname_rho.replace('.nc', '_mask.nc'), periodic=periodic, reuse=not overwrite_weights)
404
+
405
+ interp_results = {}
406
+
407
+ for var in varnames:
408
+ if VERBOSE:
409
+ print(f"**[main]** processing {var}")
410
+
411
+ if var not in ds.variables:
412
+ raise RuntimeError(f"{var} not found in dataset")
413
+
414
+ is_w_kind = True if 's_w' in ds[var].dims else False
415
+ is_rho_kind = True if 's_rho' in ds[var].dims or 'eta_rho' in ds[var].dims or 'xi_rho' in ds[var].dims else False
416
+ is_u_kind = True if 'eta_u' in ds[var].dims or 'xi_u' in ds[var].dims else False
417
+ is_v_kind = True if 'eta_v' in ds[var].dims or 'xi_v' in ds[var].dims else False
418
+
419
+ if is_u_kind:
420
+ u_da = ds[var].astype('f8')
421
+ u_vals = np.where(np.isfinite(u_da.values), u_da.values, np.nan) # (time, s_rho, eta_u, xi_u)
422
+ # average to rho grid:
423
+ u_rho = avg_to_rho_axis_padboth(u_vals, axis=-1) # -> shape (time, s, eta_u, xi_u+1) ; eta_u == eta_rho
424
+ eta_rho_len = ds.sizes['eta_rho']; xi_rho_len = ds.sizes['xi_rho']
425
+ if u_rho.shape[-2] != eta_rho_len or u_rho.shape[-1] != xi_rho_len:
426
+ raise RuntimeError(f"u_rho shape mismatch {u_rho.shape[-2:]} vs rho ({eta_rho_len},{xi_rho_len})")
427
+ # 旋转到东向分量(如果有angle)
428
+ angle = ds['angle'].values if 'angle' in ds.variables else np.zeros((eta_rho_len, xi_rho_len))
429
+ nt = u_rho.shape[0]; ns = u_rho.shape[1]
430
+ u_east = np.full_like(u_rho, np.nan)
431
+ for t in range(nt):
432
+ print(f'Processing time step {t+1}/{nt} for variable {var} (u-component)')
433
+ for k in range(ns):
434
+ print(f' Processing vertical level {k+1}/{ns} for variable {var} (u-component)')
435
+ ur = u_rho[t, k, :, :]
436
+ ang = angle
437
+ if ang.shape != ur.shape:
438
+ ang = np.broadcast_to(angle, ur.shape)
439
+ cosA = np.cos(ang)
440
+ sinA = np.sin(ang)
441
+ # 只处理u分量(东向)
442
+ u_east[t, k, :, :] = ur * cosA
443
+ # vertical interpolation ON SOURCE (rho) grid using z_rho
444
+ if 'z_rho' in ds.variables:
445
+ z_rho_da = ds['z_rho'].astype('f8')
446
+ z_rho = np.where(np.isfinite(z_rho_da.values), z_rho_da.values, np.nan)
447
+ else:
448
+ z_rho = get_roms_depths(ds, is_w=is_w_kind).values
449
+ res_u_time = []
450
+ for t in range(nt):
451
+ print(f'Processing time step {t+1}/{nt} for variable {var} (vertical interpolation)')
452
+ zcol = z_rho[t] if z_rho.ndim == 4 else z_rho
453
+ ue_src = u_east[t]
454
+ ue_vert = vertical_interp_with_endpoints(zcol, ue_src, target_depth)
455
+ levels_u = []
456
+ for k in range(ue_vert.shape[0]):
457
+ da_u = xr.DataArray(ue_vert[k], dims=('eta_rho', 'xi_rho'),
458
+ coords={'eta_rho': ds['eta_rho'], 'xi_rho': ds['xi_rho'],
459
+ 'lon': (('eta_rho', 'xi_rho'), ds['lon_rho'].values),
460
+ 'lat': (('eta_rho', 'xi_rho'), ds['lat_rho'].values)})
461
+ ru = reb_rho(da_u)
462
+ if 'mask_rho' in ds.variables:
463
+ da_mask_src = xr.DataArray(ds['mask_rho'].astype(np.float64).values, dims=('eta_rho', 'xi_rho'),
464
+ coords={'eta_rho': ds['eta_rho'], 'xi_rho': ds['xi_rho'],
465
+ 'lon': (('eta_rho', 'xi_rho'), ds['lon_rho'].values),
466
+ 'lat': (('eta_rho', 'xi_rho'), ds['lat_rho'].values)})
467
+ mask_dst = reb_mask_rho(da_mask_src)
468
+ ru = ru.where(mask_dst >= 0.5, np.nan)
469
+ levels_u.append(ru.values)
470
+ res_u_time.append(np.array(levels_u))
471
+ interp_results[var] = np.array(res_u_time)
472
+
473
+ elif is_v_kind:
474
+ v_da = ds[var].astype('f8')
475
+ v_vals = np.where(np.isfinite(v_da.values), v_da.values, np.nan) # (time, s_rho, eta_v, xi_v)
476
+ v_rho = avg_to_rho_axis_padboth(v_vals, axis=-2) # -> shape (time, s, eta_v+1, xi_v) ; xi_v == xi_rho
477
+ eta_rho_len = ds.sizes['eta_rho']; xi_rho_len = ds.sizes['xi_rho']
478
+ if v_rho.shape[-2] != eta_rho_len or v_rho.shape[-1] != xi_rho_len:
479
+ raise RuntimeError(f"v_rho shape mismatch {v_rho.shape[-2:]} vs rho ({eta_rho_len},{xi_rho_len})")
480
+ # 旋转到北向分量(如果有angle)
481
+ angle = ds['angle'].values if 'angle' in ds.variables else np.zeros((eta_rho_len, xi_rho_len))
482
+ nt = v_rho.shape[0]; ns = v_rho.shape[1]
483
+ v_north = np.full_like(v_rho, np.nan)
484
+ for t in range(nt):
485
+ print(f'Processing time step {t+1}/{nt} for variable {var} (v-component)')
486
+ for k in range(ns):
487
+ print(f' Processing vertical level {k+1}/{ns} for variable {var} (v-component)')
488
+ vr = v_rho[t, k, :, :]
489
+ ang = angle
490
+ if ang.shape != vr.shape:
491
+ ang = np.broadcast_to(angle, vr.shape)
492
+ cosA = np.cos(ang)
493
+ sinA = np.sin(ang)
494
+ # 只处理v分量(北向)
495
+ v_north[t, k, :, :] = vr * cosA
496
+ if 'z_rho' in ds.variables:
497
+ z_rho_da = ds['z_rho'].astype('f8')
498
+ z_rho = np.where(np.isfinite(z_rho_da.values), z_rho_da.values, np.nan)
499
+ else:
500
+ z_rho = get_roms_depths(ds, is_w=is_w_kind).values
501
+ res_v_time = []
502
+ for t in range(nt):
503
+ print(f'Processing time step {t+1}/{nt} for variable {var} (vertical interpolation)')
504
+ zcol = z_rho[t] if z_rho.ndim == 4 else z_rho
505
+ vn_src = v_north[t]
506
+ vn_vert = vertical_interp_with_endpoints(zcol, vn_src, target_depth)
507
+ levels_v = []
508
+ for k in range(vn_vert.shape[0]):
509
+ da_v = xr.DataArray(vn_vert[k], dims=('eta_rho', 'xi_rho'),
510
+ coords={'eta_rho': ds['eta_rho'], 'xi_rho': ds['xi_rho'],
511
+ 'lon': (('eta_rho', 'xi_rho'), ds['lon_rho'].values),
512
+ 'lat': (('eta_rho', 'xi_rho'), ds['lat_rho'].values)})
513
+ rv = reb_rho(da_v)
514
+ if 'mask_rho' in ds.variables:
515
+ da_mask_src = xr.DataArray(ds['mask_rho'].astype(np.float64).values, dims=('eta_rho', 'xi_rho'),
516
+ coords={'eta_rho': ds['eta_rho'], 'xi_rho': ds['xi_rho'],
517
+ 'lon': (('eta_rho', 'xi_rho'), ds['lon_rho'].values),
518
+ 'lat': (('eta_rho', 'xi_rho'), ds['lat_rho'].values)})
519
+ mask_dst = reb_mask_rho(da_mask_src)
520
+ rv = rv.where(mask_dst >= 0.5, np.nan)
521
+ levels_v.append(rv.values)
522
+ res_v_time.append(np.array(levels_v))
523
+ interp_results[var] = np.array(res_v_time)
524
+
525
+ elif is_rho_kind:
526
+ # scalar processing: temp/salt/zeta (vertical-first -> horizontal remap)
527
+ da = ds[var].astype('f8')
528
+ vals = np.where(np.isfinite(da.values), da.values, np.nan)
529
+ if da.ndim == 4:
530
+ nt = vals.shape[0]
531
+ out_time = []
532
+ for t in range(nt):
533
+ print(f'Processing time step {t+1}/{nt} for variable {var} (vertical interpolation)')
534
+ src = vals[t]
535
+ if is_w_kind and 'z_w' in ds.variables:
536
+ zsrc = np.where(np.isfinite(ds['z_w'].values), ds['z_w'].values, np.nan)
537
+ zcol = zsrc[t] if zsrc.ndim == 4 else zsrc
538
+ elif not is_w_kind and 'z_rho' in ds.variables:
539
+ zsrc = np.where(np.isfinite(ds['z_rho'].values), ds['z_rho'].values, np.nan)
540
+ zcol = zsrc[t] if zsrc.ndim == 4 else zsrc
541
+ else:
542
+ zcol = get_roms_depths(ds, is_w=is_w_kind, time_index=t).values
543
+ vert = vertical_interp_with_endpoints(zcol, src, target_depth)
544
+ levels = []
545
+ for k in range(vert.shape[0]):
546
+ print(f' Processing vertical level {k+1}/{vert.shape[0]} for variable {var} (vertical interpolation)')
547
+ da_l = xr.DataArray(vert[k], dims=('eta_rho', 'xi_rho'),
548
+ coords={'eta_rho': ds['eta_rho'], 'xi_rho': ds['xi_rho'],
549
+ 'lon': (('eta_rho', 'xi_rho'), ds['lon_rho'].values),
550
+ 'lat': (('eta_rho', 'xi_rho'), ds['lat_rho'].values)})
551
+ rt = reb_rho(da_l)
552
+ if 'mask_rho' in ds.variables:
553
+ da_mask_src = xr.DataArray(ds['mask_rho'].astype(np.float64).values, dims=('eta_rho','xi_rho'),
554
+ coords={'eta_rho': ds['eta_rho'], 'xi_rho': ds['xi_rho'],
555
+ 'lon': (('eta_rho','xi_rho'), ds['lon_rho'].values),
556
+ 'lat': (('eta_rho','xi_rho'), ds['lat_rho'].values)})
557
+ mask_dst = reb_mask_rho(da_mask_src)
558
+ rt = rt.where(mask_dst >= 0.5, np.nan)
559
+ levels.append(rt.values)
560
+ out_time.append(np.array(levels))
561
+ interp_results[var] = np.array(out_time)
562
+ elif da.ndim == 3:
563
+ nt = vals.shape[0]
564
+ out = []
565
+ for t in range(nt):
566
+ print(f'Processing time step {t+1}/{nt} for variable {var} (horizontal remap)')
567
+ da_l = xr.DataArray(vals[t], dims=('eta_rho', 'xi_rho'),
568
+ coords={'eta_rho': ds['eta_rho'], 'xi_rho': ds['xi_rho'],
569
+ 'lon': (('eta_rho','xi_rho'), ds['lon_rho'].values),
570
+ 'lat': (('eta_rho','xi_rho'), ds['lat_rho'].values)})
571
+ rt = reb_rho(da_l)
572
+ if 'mask_rho' in ds.variables:
573
+ da_mask_src = xr.DataArray(ds['mask_rho'].astype(np.float64).values, dims=('eta_rho','xi_rho'),
574
+ coords={'eta_rho': ds['eta_rho'], 'xi_rho': ds['xi_rho'],
575
+ 'lon': (('eta_rho','xi_rho'), ds['lon_rho'].values),
576
+ 'lat': (('eta_rho','xi_rho'), ds['lat_rho'].values)})
577
+ mask_dst = reb_mask_rho(da_mask_src)
578
+ rt = rt.where(mask_dst >= 0.5, np.nan)
579
+ out.append(rt.values)
580
+ interp_results[var] = np.array(out)
581
+ else:
582
+ raise ValueError(f"Unsupported variable dims for {var}")
583
+ else:
584
+ raise ValueError(f"Unknown grid type for variable {var}")
585
+
586
+ # write netcdf output - simple writer for (time,depth,lat,lon) or (time,lat,lon)
587
+ times_out = times
588
+ data_vars = {}
589
+ coords = {'time': ('time', times_out), 'lat': ('lat', target_lat), 'lon': ('lon', target_lon)}
590
+ if target_depth is not None:
591
+ coords['depth'] = ('depth', target_depth)
592
+
593
+ for vname, arr in interp_results.items():
594
+ a = np.asarray(arr)
595
+ if a.ndim == 4:
596
+ data_vars[vname] = (('time', 'depth', 'lat', 'lon'), a)
597
+ elif a.ndim == 3:
598
+ data_vars[vname] = (('time', 'lat', 'lon'), a)
599
+ else:
600
+ raise ValueError(f"Unexpected ndim for output {vname}: {a.ndim}")
601
+
602
+ ds_out = xr.Dataset(data_vars=data_vars, coords=coords)
603
+ encoding = {name: {'zlib': True, 'complevel': 4} for name in data_vars.keys()}
604
+ ds_out.to_netcdf(output_nc, encoding=encoding)
605
+ if VERBOSE:
606
+ print(f"[main] wrote {output_nc} variables: {list(data_vars.keys())}")
607
+
608
+
609
+ # -------------------------
610
+ # __main__ example
611
+ # -------------------------
612
+ if __name__ == "__main__":
613
+ input_nc = "./2024090100/nwa_his_0001.nc"
614
+ output_nc = "roms_interp.nc"
615
+ varnames = ['temp', 'zeta', 'w', 'u', 'v', 'salt']
616
+ target_lon = np.linspace(108, 140, 641)
617
+ target_lat = np.linspace(15, 40, 501)
618
+ target_depth = [-5, -10, -20, -30, -50, -75, -100, -125, -150, -200,
619
+ -250, -300, -400, -500, -600, -700, -800, -900, -1000]
620
+ process_roms_file(input_nc, output_nc, varnames, target_lon, target_lat, target_depth, overwrite_weights=False)
@@ -2,7 +2,7 @@ from rich import print
2
2
  import time
3
3
  import os
4
4
 
5
- __all__ = ["os_command", "get_queue_node", "query_queue", "running_jobs", "submit_job"]
5
+ __all__ = ["os_command", "get_queue_node", "query_queue", "running_jobs", "submit_job", "get_job_status"]
6
6
 
7
7
 
8
8
  # 负责执行命令并返回输出
@@ -21,6 +21,7 @@ def os_command(cmd):
21
21
  return None
22
22
  return result.stdout
23
23
 
24
+
24
25
  # 返回“队列名:节点数”的字典
25
26
  def get_queue_node():
26
27
  import re
@@ -50,6 +51,7 @@ def get_queue_node():
50
51
 
51
52
  return queue_node_dict
52
53
 
54
+
53
55
  def query_queue(need_node=1, queue_list =['dcu','bigmem','cpu_parallel','cpu_single']):
54
56
  queue_dict = get_queue_node()
55
57
  hs = None
@@ -65,6 +67,7 @@ def query_queue(need_node=1, queue_list =['dcu','bigmem','cpu_parallel','cpu_sin
65
67
  break
66
68
  return hs
67
69
 
70
+
68
71
  def running_jobs():
69
72
  # 通过qstat判断任务状态,是否还在进行中
70
73
  # status = os.popen('qstat').read()
@@ -73,6 +76,41 @@ def running_jobs():
73
76
  ids = [job.split()[0] for job in Jobs if job != '']
74
77
  return ids
75
78
 
79
+
80
+ def get_job_status(jobid):
81
+ """
82
+ 获取指定任务ID的ST状态(如R/PD/S等)
83
+ :param jobid: 任务ID(整数或字符串格式均可)
84
+ :return: 任务的ST状态字符串,未找到任务返回None
85
+ """
86
+ import re # 复用正则模块,内部导入避免依赖冲突
87
+ jobid_str = str(jobid).strip()
88
+ if not jobid_str.isdigit():
89
+ print(f'⚠️ 输入的任务ID {jobid} 格式无效,需为纯数字')
90
+ return None
91
+
92
+ # 执行squeue命令精准匹配目标任务(避免多ID混淆)
93
+ cmd = f'squeue | grep -w {jobid_str}'
94
+ output = os_command(cmd)
95
+
96
+ if not output:
97
+ print(f'❌ 未找到任务ID {jobid_str} 的相关信息(可能已完成或输入错误)')
98
+ return None
99
+
100
+ # 解析输出中的ST状态(匹配JOBID后的第五个字段,处理多空格分隔)
101
+ # 正则匹配逻辑:忽略前置空格 → 匹配JOBID → 匹配后续任意字符 → 捕获ST状态(单个大写字母/字母组合)
102
+ pattern = r'\s*\d+\s+\S+\s+\S+\s+\S+\s+(\S+)'
103
+ match = re.search(pattern, output)
104
+ if match:
105
+ st_status = match.group(1)
106
+ print(f'✅ 任务ID {jobid_str} 的ST状态:{st_status}')
107
+ return st_status
108
+ else:
109
+ print(f'❌ 无法解析任务ID {jobid_str} 的ST状态,命令输出:{output.strip()}')
110
+ return None
111
+
112
+
113
+
76
114
  def submit_job(working_dir=None, script_tmp='run.slurm', script_run='run.slurm', need_node=1, queue_tmp='<queue_name>', queue_list=['dcu', 'bigmem', 'cpu_parallel', 'cpu_single'], max_job=38, wait=False):
77
115
  '''提交任务到集群,并返回任务ID'''
78
116
  from .oa_file import replace_content
@@ -106,22 +144,33 @@ def submit_job(working_dir=None, script_tmp='run.slurm', script_run='run.slurm',
106
144
  time.sleep(30)
107
145
  else:
108
146
  print(f'提交任务成功,{content_sub.strip()}')
147
+ # time_s = time.time()
109
148
  job_id = content_sub.strip().split()[-1]
110
- break
149
+ print(f'等待60秒后,继续检查任务状态!')
150
+ time.sleep(60)
151
+ job_st = get_job_status(job_id)
152
+ # if job_st == 'PD' and (time.time()-time_s) > 60:
153
+ if job_st == 'PD':
154
+ os_command(f'scancel {job_id}')
155
+ print(f'因作业{job_id}处于PD状态,取消!')
156
+ else:
157
+ break
111
158
  else:
112
159
  print('没有足够的计算资源,等待30秒后重试!')
113
160
  time.sleep(30)
114
161
  else:
115
162
  print(f'当前系统任务数:{len(running_job)},等待60秒后重试!')
116
163
  time.sleep(60)
117
- print(f'等待10秒后,继续检查任务状态!')
164
+
165
+ print(f'等待10秒后,继续查询任务或进行下一个操作!')
118
166
  time.sleep(10)
119
167
 
120
168
  if wait:
121
169
  while True:
122
170
  if job_id in running_jobs():
123
171
  print(f'Time: {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
124
- print(f'任务{job_id}正在队列中...')
172
+ # print(f'任务{job_id}正在队列中...')
173
+ get_job_status(job_id)
125
174
  time.sleep(60)
126
175
  else:
127
176
  print(f'任务{job_id}已完成!')
@@ -0,0 +1,42 @@
1
+ from typing import Sequence
2
+
3
+ __all__ = ['interp']
4
+
5
+
6
+ def interp(input_nc: str, output_nc: str, varnames: Sequence[str],
7
+ target_lon: Sequence[float], target_lat: Sequence[float], target_depth: Sequence[float],
8
+ overwrite_weights: bool = False):
9
+ """
10
+ Perform vertical interpolation and horizontal remapping of ROMS model data.
11
+
12
+ Parameters
13
+ ----------
14
+ input_nc : str
15
+ Path to the input ROMS NetCDF file.
16
+ output_nc : str
17
+ Path to the output NetCDF file.
18
+ varnames : Sequence[str]
19
+ List of variable names to process.
20
+ target_lon : Sequence[float]
21
+ Target longitudes for horizontal remapping.
22
+ target_lat : Sequence[float]
23
+ Target latitudes for horizontal remapping.
24
+ target_depth : Sequence[float]
25
+ Target depths for vertical interpolation. Should be negative values (e.g., -5 for 5m depth).
26
+ overwrite_weights : bool, optional
27
+ Whether to overwrite existing regridding weights files. Default is False.
28
+
29
+ Examples
30
+ --------
31
+ input_nc = "./2024090100/nwa_his_0001.nc"
32
+ output_nc = "roms_interp.nc"
33
+ varnames = ['temp', 'zeta', 'w', 'u', 'v', 'salt']
34
+ target_lon = np.linspace(108, 140, 641)
35
+ target_lat = np.linspace(15, 40, 501)
36
+ target_depth = [-5, -10, -20, -30, -50, -75, -100, -125, -150, -200,
37
+ -250, -300, -400, -500, -600, -700, -800, -900, -1000]
38
+ """
39
+
40
+ from oafuncs._script.process_roms import process_roms_file
41
+ process_roms_file(input_nc, output_nc, varnames, target_lon, target_lat, target_depth,
42
+ overwrite_weights=overwrite_weights)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oafuncs
3
- Version: 0.0.98.51
3
+ Version: 0.0.98.52
4
4
  Summary: Oceanic and Atmospheric Functions
5
5
  Home-page: https://github.com/Industry-Pays/OAFuncs
6
6
  Author: Kun Liu
@@ -30,6 +30,7 @@ oafuncs/_script/netcdf_write.py
30
30
  oafuncs/_script/parallel.py
31
31
  oafuncs/_script/parallel_bak.py
32
32
  oafuncs/_script/plot_dataset.py
33
+ oafuncs/_script/process_roms.py
33
34
  oafuncs/_script/replace_file_content.py
34
35
  oafuncs/oa_down/User_Agent-list.txt
35
36
  oafuncs/oa_down/__init__.py
@@ -40,8 +41,7 @@ oafuncs/oa_down/read_proxy.py
40
41
  oafuncs/oa_down/test_ua.py
41
42
  oafuncs/oa_down/user_agent.py
42
43
  oafuncs/oa_model/__init__.py
43
- oafuncs/oa_model/roms/__init__.py
44
- oafuncs/oa_model/roms/test.py
44
+ oafuncs/oa_model/roms.py
45
45
  oafuncs/oa_model/wrf/__init__.py
46
46
  oafuncs/oa_model/wrf/little_r.py
47
47
  oafuncs/oa_sign/__init__.py
@@ -18,7 +18,7 @@ URL = "https://github.com/Industry-Pays/OAFuncs"
18
18
  EMAIL = "liukun0312@stu.ouc.edu.cn"
19
19
  AUTHOR = "Kun Liu"
20
20
  REQUIRES_PYTHON = ">=3.10.0" # 2025/03/13
21
- VERSION = "0.0.98.51"
21
+ VERSION = "0.0.98.52"
22
22
 
23
23
  # What packages are required for this module to be executed?
24
24
  REQUIRED = [
@@ -1,20 +0,0 @@
1
- #!/usr/bin/env python
2
- # coding=utf-8
3
- """
4
- Author: Liu Kun && 16031215@qq.com
5
- Date: 2025-03-09 16:30:02
6
- LastEditors: Liu Kun && 16031215@qq.com
7
- LastEditTime: 2025-03-09 18:23:30
8
- FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_model\\roms\\__init__.py
9
- Description:
10
- EditPlatform: vscode
11
- ComputerInfo: XPS 15 9510
12
- SystemInfo: Windows 11
13
- Python Version: 3.12
14
- """
15
-
16
-
17
-
18
-
19
- # 会导致OAFuncs直接导入所有函数,不符合模块化设计
20
- from .test import *
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env python
2
- # coding=utf-8
3
- """
4
- Author: Liu Kun && 16031215@qq.com
5
- Date: 2025-03-09 16:30:54
6
- LastEditors: Liu Kun && 16031215@qq.com
7
- LastEditTime: 2025-03-09 16:30:54
8
- FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_model\\roms\\depth.py
9
- Description:
10
- EditPlatform: vscode
11
- ComputerInfo: XPS 15 9510
12
- SystemInfo: Windows 11
13
- Python Version: 3.12
14
- """
15
-
16
- __all__ = ['test']
17
-
18
- def test():
19
- print('test')
File without changes
File without changes
File without changes
File without changes