PyMVP 0.1.9__tar.gz → 0.2.1__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.
- {pymvp-0.1.9 → pymvp-0.2.1}/PKG-INFO +1 -1
- {pymvp-0.1.9 → pymvp-0.2.1}/PyMVP/main.py +145 -51
- {pymvp-0.1.9 → pymvp-0.2.1}/PyMVP/mvp_routines.py +84 -3
- {pymvp-0.1.9 → pymvp-0.2.1}/PyMVP.egg-info/PKG-INFO +1 -1
- {pymvp-0.1.9 → pymvp-0.2.1}/pyproject.toml +1 -1
- {pymvp-0.1.9 → pymvp-0.2.1}/PyMVP/__init__.py +0 -0
- {pymvp-0.1.9 → pymvp-0.2.1}/PyMVP.egg-info/SOURCES.txt +0 -0
- {pymvp-0.1.9 → pymvp-0.2.1}/PyMVP.egg-info/dependency_links.txt +0 -0
- {pymvp-0.1.9 → pymvp-0.2.1}/PyMVP.egg-info/requires.txt +0 -0
- {pymvp-0.1.9 → pymvp-0.2.1}/PyMVP.egg-info/top_level.txt +0 -0
- {pymvp-0.1.9 → pymvp-0.2.1}/setup.cfg +0 -0
|
@@ -36,7 +36,7 @@ from scipy.ndimage import median_filter
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
class Analyzer:
|
|
39
|
-
def __init__(self,
|
|
39
|
+
def __init__(self, Yorig=1950):
|
|
40
40
|
"""
|
|
41
41
|
Initialize the analyzer with the data path and reference year.
|
|
42
42
|
Args:
|
|
@@ -46,14 +46,13 @@ class Analyzer:
|
|
|
46
46
|
"""
|
|
47
47
|
self.Yorig = Yorig
|
|
48
48
|
self.date_ref = datetime(Yorig, 1, 1)
|
|
49
|
-
self.data_path = data_path
|
|
50
|
-
self.output_path = output_path if output_path is not None else data_path
|
|
51
|
-
self.subdirs = subdirs
|
|
52
49
|
self.mvp = False
|
|
53
50
|
self.ctd = False
|
|
51
|
+
self.speed = False
|
|
52
|
+
self.corrected = False
|
|
54
53
|
|
|
55
54
|
|
|
56
|
-
def load_mvp_data(self,delp=[],
|
|
55
|
+
def load_mvp_data(self,data_path, delp=[], subdirs=False,format='raw',only_new=False, output_path=None):
|
|
57
56
|
"""
|
|
58
57
|
Load MVP data from .raw and .log files in the data_path folder.
|
|
59
58
|
Fills the object attributes with data matrices and associated metadata.
|
|
@@ -61,8 +60,9 @@ class Analyzer:
|
|
|
61
60
|
delp (list): Indices of profiles to remove from the list (optional).
|
|
62
61
|
data_path (str): Path to the folder containing MVP files (optional).
|
|
63
62
|
"""
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
self.data_path = data_path
|
|
64
|
+
self.subdirs = subdirs
|
|
65
|
+
self.output_path = output_path
|
|
66
66
|
|
|
67
67
|
if format=='raw':
|
|
68
68
|
if self.subdirs:
|
|
@@ -300,7 +300,7 @@ class Analyzer:
|
|
|
300
300
|
|
|
301
301
|
|
|
302
302
|
|
|
303
|
-
def load_mvp_data_again(self,data_path
|
|
303
|
+
def load_mvp_data_again(self,data_path,format='raw',delp=[]):
|
|
304
304
|
"""
|
|
305
305
|
Load MVP data from .raw and .log files in the data_path folder.
|
|
306
306
|
Fills the object attributes with data matrices and associated metadata.
|
|
@@ -308,14 +308,13 @@ class Analyzer:
|
|
|
308
308
|
data_path (str): Path to the folder containing MVP files.
|
|
309
309
|
delp (list): Indices of profiles to remove from the list (optional).
|
|
310
310
|
"""
|
|
311
|
-
|
|
312
|
-
self.data_path = data_path
|
|
311
|
+
|
|
313
312
|
|
|
314
313
|
if format=='raw':
|
|
315
|
-
files = sorted(filter(os.path.isfile,glob.glob(
|
|
314
|
+
files = sorted(filter(os.path.isfile,glob.glob(data_path + '*.raw', recursive=True)))
|
|
316
315
|
elif format=='ncdf':
|
|
317
|
-
files = sorted(filter(os.path.isfile,glob.glob(
|
|
318
|
-
print('Found ' + str(len(files)) + ' MVP files in the directory: ' +
|
|
316
|
+
files = sorted(filter(os.path.isfile,glob.glob(data_path + '**/MVP*.nc', recursive=True)))
|
|
317
|
+
print('Found ' + str(len(files)) + ' MVP files in the directory: ' + data_path)
|
|
319
318
|
|
|
320
319
|
|
|
321
320
|
|
|
@@ -562,12 +561,9 @@ class Analyzer:
|
|
|
562
561
|
"""
|
|
563
562
|
|
|
564
563
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
elif format=='ncdf':
|
|
569
|
-
list_of_ctd_files = sorted(filter(os.path.isfile,\
|
|
570
|
-
glob.glob(data_path_ctd + 'CTD'+'*.nc')))
|
|
564
|
+
list_of_ctd_files = sorted(filter(os.path.isfile,\
|
|
565
|
+
glob.glob(data_path_ctd + 'CTD'+'*.nc')))
|
|
566
|
+
|
|
571
567
|
print('Found ' + str(len(list_of_ctd_files)) + ' CTD files in the directory: ' + data_path_ctd)
|
|
572
568
|
|
|
573
569
|
|
|
@@ -631,11 +627,6 @@ class Analyzer:
|
|
|
631
627
|
|
|
632
628
|
|
|
633
629
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
630
|
def compute_waterflow(self,horizontal_speed=2,corr=False):
|
|
640
631
|
"""
|
|
641
632
|
Compute the water flow speed (u,v) from the horizontal speed and the direction of the profiles.
|
|
@@ -655,6 +646,7 @@ class Analyzer:
|
|
|
655
646
|
|
|
656
647
|
self.SPEED_mvp = SPEED_MVP
|
|
657
648
|
print('Water flow speed computed successfully.')
|
|
649
|
+
self.speed = True
|
|
658
650
|
|
|
659
651
|
def print_profile_metadata(self):
|
|
660
652
|
"""
|
|
@@ -938,7 +930,7 @@ class Analyzer:
|
|
|
938
930
|
plt.xlabel('Fluorescence, ug/L')
|
|
939
931
|
plt.ylabel('Pressure, dbar')
|
|
940
932
|
|
|
941
|
-
def
|
|
933
|
+
def plot_diagramTS(self,id_mvp=None,id_ctd=None,correction=False):
|
|
942
934
|
"""
|
|
943
935
|
Plot the TS diagram (Salinity vs Temperature) for one or more profiles, with isopycnals.
|
|
944
936
|
Args:
|
|
@@ -1234,7 +1226,7 @@ class Analyzer:
|
|
|
1234
1226
|
print(f" MVP down: {rmse_cond_down:.4f} S/m (deep: {rmse_cond_down_deep:.4f} S/m)")
|
|
1235
1227
|
print(f" MVP up: {rmse_cond_up:.4f} S/m (deep: {rmse_cond_up_deep:.4f} S/m)")
|
|
1236
1228
|
|
|
1237
|
-
def correct_oxygen(self,id_mvp=None,id_ctd=None,num_sample=500,plotting=False,
|
|
1229
|
+
def correct_oxygen(self,id_mvp=None,id_ctd=None,num_sample=500,plotting=False,):
|
|
1238
1230
|
"""
|
|
1239
1231
|
Apply oxygen correction to MVP dissolved oxygen profiles thanks to CTD data.
|
|
1240
1232
|
Args:
|
|
@@ -1295,9 +1287,9 @@ class Analyzer:
|
|
|
1295
1287
|
rmse_after_full = np.mean(np.sqrt(np.nanmean((DO_mvp_corr_full_interp - DO_ctd_interp)**2,axis=1)))
|
|
1296
1288
|
print(f"RMSE after correction (full profile): {rmse_after_full:.4f}")
|
|
1297
1289
|
|
|
1290
|
+
self.DO_mvp_raw = self.DO_mvp.copy()
|
|
1291
|
+
self.DO_mvp = DO_mvp_corr_full
|
|
1298
1292
|
|
|
1299
|
-
if correction:
|
|
1300
|
-
self.DO_mvp = DO_mvp_corr_full
|
|
1301
1293
|
|
|
1302
1294
|
if plotting:
|
|
1303
1295
|
|
|
@@ -1314,7 +1306,6 @@ class Analyzer:
|
|
|
1314
1306
|
plt.show()
|
|
1315
1307
|
|
|
1316
1308
|
|
|
1317
|
-
|
|
1318
1309
|
def mvp_correction(self,high_cutoff=1,dp=0.1):
|
|
1319
1310
|
|
|
1320
1311
|
T_MVP_corr = []
|
|
@@ -1368,7 +1359,7 @@ class Analyzer:
|
|
|
1368
1359
|
self.COND_mvp_corr = {i: sublist for i, sublist in enumerate(C_MVP_corr)}
|
|
1369
1360
|
self.SALT_mvp_corr = {i: sublist for i, sublist in enumerate(S_MVP_corr)}
|
|
1370
1361
|
self.TIME_mvp_corr = {i: sublist for i, sublist in enumerate(Time_MVP_corr)}
|
|
1371
|
-
|
|
1362
|
+
self.corrected = True
|
|
1372
1363
|
|
|
1373
1364
|
print("MVP profiles corrected.")
|
|
1374
1365
|
|
|
@@ -1380,6 +1371,11 @@ class Analyzer:
|
|
|
1380
1371
|
"""
|
|
1381
1372
|
if not self.ctd:
|
|
1382
1373
|
raise ValueError("CTD data not loaded.")
|
|
1374
|
+
|
|
1375
|
+
if not self.corrected:
|
|
1376
|
+
raise ValueError("MVP data not corrected. Apply corrections first.")
|
|
1377
|
+
|
|
1378
|
+
|
|
1383
1379
|
|
|
1384
1380
|
if not hasattr(self, 'PRES_mvp_corr'):
|
|
1385
1381
|
raise ValueError("Corrected MVP data not available. Apply corrections first.")
|
|
@@ -1397,8 +1393,9 @@ class Analyzer:
|
|
|
1397
1393
|
max_lensalt = max([len(p) for p in self.SALT_mvp_corr.values()])
|
|
1398
1394
|
SALT_mvp_corr_mat = np.array([list(row) + [np.nan] * (max_lensalt - len(row)) for row in self.SALT_mvp_corr.values()])
|
|
1399
1395
|
|
|
1400
|
-
|
|
1401
|
-
|
|
1396
|
+
if self.speed:
|
|
1397
|
+
max_lenvspd = max([len(p) for p in self.SPEED_mvp_corr.values()])
|
|
1398
|
+
SPEED_mvp_corr_mat = np.array([list(row) + [np.nan] * (max_lenvspd - len(row)) for row in self.SPEED_mvp_corr.values()])
|
|
1402
1399
|
|
|
1403
1400
|
max_lentime = max([len(p) for p in self.TIME_mvp_corr.values()])
|
|
1404
1401
|
TIME_mvp_corr_mat = np.array([list(row) + [np.nan] * (max_lentime - len(row)) for row in self.TIME_mvp_corr.values()])
|
|
@@ -1406,22 +1403,123 @@ class Analyzer:
|
|
|
1406
1403
|
|
|
1407
1404
|
pressure_grid = np.linspace(np.nanmin(PRES_mvp_corr_mat), np.nanmax(PRES_mvp_corr_mat), length)
|
|
1408
1405
|
|
|
1409
|
-
self.
|
|
1410
|
-
self.
|
|
1411
|
-
self.
|
|
1412
|
-
self.
|
|
1413
|
-
self.
|
|
1406
|
+
self.TEMP_ctd_interp = mvp.vertical_interp(self.PRES_ctd, self.TEMP_ctd, pressure_grid)
|
|
1407
|
+
self.PRES_ctd_interp = mvp.vertical_interp(self.PRES_ctd, self.PRES_ctd, pressure_grid)
|
|
1408
|
+
self.COND_ctd_interp = mvp.vertical_interp(self.PRES_ctd, self.COND_ctd, pressure_grid)
|
|
1409
|
+
self.SALT_ctd_interp = mvp.vertical_interp(self.PRES_ctd, self.SALT_ctd, pressure_grid)
|
|
1410
|
+
self.DO_ctd_interp = mvp.vertical_interp(self.PRES_ctd, self.OXY_ctd, pressure_grid)
|
|
1411
|
+
self.FLUO_ctd_interp = mvp.vertical_interp(self.PRES_ctd, self.FLUO_ctd, pressure_grid)
|
|
1412
|
+
self.TURB_ctd_interp = mvp.vertical_interp(self.PRES_ctd, self.TURB_ctd, pressure_grid)
|
|
1414
1413
|
self.TEMP_mvp_corr_interp = mvp.vertical_interp(PRES_mvp_corr_mat, TEMP_mvp_corr_mat, pressure_grid)
|
|
1415
1414
|
self.PRES_mvp_corr_interp = mvp.vertical_interp(PRES_mvp_corr_mat, PRES_mvp_corr_mat, pressure_grid)
|
|
1416
1415
|
self.COND_mvp_corr_interp = mvp.vertical_interp(PRES_mvp_corr_mat, COND_mvp_corr_mat, pressure_grid)
|
|
1417
1416
|
self.SALT_mvp_corr_interp = mvp.vertical_interp(PRES_mvp_corr_mat, SALT_mvp_corr_mat, pressure_grid)
|
|
1418
|
-
self.
|
|
1417
|
+
self.DO_mvp_corr_interp = mvp.vertical_interp(self.PRES_mvp, self.DO_mvp, pressure_grid)
|
|
1418
|
+
self.FLUO_mvp_corr_interp = mvp.vertical_interp(self.PRES_mvp, self.FLUO_mvp, pressure_grid)
|
|
1419
|
+
self.TURB_mvp_corr_interp = mvp.vertical_interp(self.PRES_mvp, self.TURB_mvp, pressure_grid)
|
|
1420
|
+
self.PH_mvp_corr_interp = mvp.vertical_interp(self.PRES_mvp, self.PH_mvp, pressure_grid)
|
|
1421
|
+
self.SUNA_mvp_corr_interp = mvp.vertical_interp(self.PRES_mvp, self.SUNA_mvp, pressure_grid)
|
|
1422
|
+
|
|
1423
|
+
if self.speed:
|
|
1424
|
+
self.SPEED_mvp_corr_interp = mvp.vertical_interp(PRES_mvp_corr_mat, SPEED_mvp_corr_mat, pressure_grid)
|
|
1419
1425
|
self.TIME_mvp_corr_interp = mvp.vertical_interp(PRES_mvp_corr_mat, TIME_mvp_corr_mat, pressure_grid)
|
|
1420
1426
|
|
|
1421
1427
|
print('CTD data interpolated onto corrected MVP pressure levels.')
|
|
1422
1428
|
|
|
1423
1429
|
|
|
1424
|
-
def
|
|
1430
|
+
def corrige_MVP_offset_on_ctd_exact(self,id_mvp,id_ctd,min_depth=-1):
|
|
1431
|
+
"""
|
|
1432
|
+
This function corrects the offset between the MVP and CTD profiles by aligning the temperature, conductivity profiles. It calculates the mean difference in temperature between the two profiles and applies this correction to the CTD temperature data.
|
|
1433
|
+
id_mvp and id_ctd must be the same length as each MVP profile will be be corrected with the corresponding CTD profile. The function returns the corrected MVP temperature and conductivity profiles.
|
|
1434
|
+
This version of the correction suppose that CTD and MVP should be exactly the same profile (same location, same time). If it not the case, you shouldf use the other function _imple
|
|
1435
|
+
"""
|
|
1436
|
+
|
|
1437
|
+
mean_temp_diff = []
|
|
1438
|
+
mean_cond_diff = []
|
|
1439
|
+
|
|
1440
|
+
print("Calculating mean differences between MVP and CTD profiles before correction:")
|
|
1441
|
+
for i in range(len(id_mvp)):
|
|
1442
|
+
# Calculate the mean difference in temperature between the MVP and CTD profiles
|
|
1443
|
+
temp_diff = np.nanmean(self.TEMP_mvp_corr_interp[id_mvp[i]] - self.TEMP_ctd_interp[id_ctd[i]])
|
|
1444
|
+
mean_temp_diff.append(temp_diff)
|
|
1445
|
+
|
|
1446
|
+
cond_diff = np.nanmean(self.COND_mvp_corr_interp[id_mvp[i]] - self.COND_ctd_interp[id_ctd[i]])
|
|
1447
|
+
mean_cond_diff.append(cond_diff)
|
|
1448
|
+
print("Mean temperature difference between MVP and CTD profiles:", np.mean(mean_temp_diff))
|
|
1449
|
+
print("Mean conductivity difference between MVP and CTD profiles:", np.mean(mean_cond_diff))
|
|
1450
|
+
|
|
1451
|
+
for i in range(len(id_mvp)):
|
|
1452
|
+
self.TEMP_mvp_corr_interp[id_mvp[i]] = mvp.align_profiles(self.PRES_mvp_corr_interp[id_mvp[i]], self.TEMP_ctd_interp[id_ctd[i]], self.TEMP_mvp_corr_interp[id_mvp[i]],min_depth)[0]
|
|
1453
|
+
self.COND_mvp_corr_interp[id_mvp[i]] = mvp.align_profiles(self.PRES_mvp_corr_interp[id_mvp[i]], self.COND_ctd_interp[id_ctd[i]], self.COND_mvp_corr_interp[id_mvp[i]],min_depth)[0]
|
|
1454
|
+
|
|
1455
|
+
|
|
1456
|
+
mean_temp_diff = []
|
|
1457
|
+
mean_cond_diff = []
|
|
1458
|
+
print("After correction:")
|
|
1459
|
+
for i in range(len(id_mvp)):
|
|
1460
|
+
# Calculate the mean difference in temperature between the MVP and CTD profiles
|
|
1461
|
+
temp_diff = np.nanmean(self.TEMP_mvp_corr_interp[id_mvp[i]] - self.TEMP_ctd_interp[id_ctd[i]])
|
|
1462
|
+
mean_temp_diff.append(temp_diff)
|
|
1463
|
+
|
|
1464
|
+
cond_diff = np.nanmean(self.COND_mvp_corr_interp[id_mvp[i]] - self.COND_ctd_interp[id_ctd[i]])
|
|
1465
|
+
mean_cond_diff.append(cond_diff)
|
|
1466
|
+
print("Mean temperature difference between MVP and CTD profiles:", np.mean(mean_temp_diff))
|
|
1467
|
+
print("Mean conductivity difference between MVP and CTD profiles:", np.mean(mean_cond_diff))
|
|
1468
|
+
|
|
1469
|
+
|
|
1470
|
+
|
|
1471
|
+
def corrige_MVP_offset_on_ctd_simple(self,id_mvp,id_ctd,min_depth):
|
|
1472
|
+
"""
|
|
1473
|
+
This function corrects the offset between the MVP and CTD profiles by aligning the temperature, conductivity profiles. It calculates the mean difference in temperature between the two profiles and applies this correction to the CTD temperature data.
|
|
1474
|
+
id_mvp and id_ctd must be the same length as each MVP profile will be be corrected with the corresponding CTD profile. The function returns the corrected MVP temperature and conductivity profiles.
|
|
1475
|
+
This version of the correction is less restritive than the other one, does not need the CTD aand MVP profiles to be exactly similar
|
|
1476
|
+
We advice to choose a min_depth that avoid to take into acount the surface layer which can introduce errors.
|
|
1477
|
+
"""
|
|
1478
|
+
|
|
1479
|
+
mean_temp_diff = []
|
|
1480
|
+
mean_cond_diff = []
|
|
1481
|
+
print("Calculating mean differences between MVP and CTD profiles before correction:")
|
|
1482
|
+
for i in range(len(id_mvp)):
|
|
1483
|
+
id_valid = self.PRES_mvp_corr_interp[id_mvp[i]] >= min_depth
|
|
1484
|
+
# Calculate the mean difference in temperature between the MVP and CTD profiles
|
|
1485
|
+
temp_diff = np.nanmean(self.TEMP_mvp_corr_interp[id_mvp[i], id_valid] - self.TEMP_ctd_interp[id_ctd[i], id_valid])
|
|
1486
|
+
mean_temp_diff.append(temp_diff)
|
|
1487
|
+
|
|
1488
|
+
cond_diff = np.nanmean(self.COND_mvp_corr_interp[id_mvp[i], id_valid] - self.COND_ctd_interp[id_ctd[i], id_valid])
|
|
1489
|
+
mean_cond_diff.append(cond_diff)
|
|
1490
|
+
print("Mean temperature difference between MVP and CTD profiles:", np.mean(mean_temp_diff))
|
|
1491
|
+
print("Mean conductivity difference between MVP and CTD profiles:", np.mean(mean_cond_diff))
|
|
1492
|
+
|
|
1493
|
+
for i in range(len(id_mvp)):
|
|
1494
|
+
id_valid = self.PRES_mvp_corr_interp[id_mvp[i]] >= min_depth
|
|
1495
|
+
|
|
1496
|
+
# Calculate the mean difference in temperature between the MVP and CTD profiles
|
|
1497
|
+
temp_diff = np.nanmean(self.TEMP_mvp_corr_interp[id_mvp[i], id_valid] - self.TEMP_ctd_interp[id_ctd[i], id_valid])
|
|
1498
|
+
self.TEMP_mvp_corr_interp[id_mvp[i]] -= temp_diff
|
|
1499
|
+
|
|
1500
|
+
cond_diff = np.nanmean(self.COND_mvp_corr_interp[id_mvp[i], id_valid] - self.COND_ctd_interp[id_ctd[i], id_valid])
|
|
1501
|
+
self.COND_mvp_corr_interp[id_mvp[i]] -= cond_diff
|
|
1502
|
+
|
|
1503
|
+
|
|
1504
|
+
mean_temp_diff = []
|
|
1505
|
+
mean_cond_diff = []
|
|
1506
|
+
print("After correction:")
|
|
1507
|
+
for i in range(len(id_mvp)):
|
|
1508
|
+
id_valid = self.PRES_mvp_corr_interp[id_mvp[i]] >= min_depth
|
|
1509
|
+
|
|
1510
|
+
# Calculate the mean difference in temperature between the MVP and CTD profiles
|
|
1511
|
+
temp_diff = np.nanmean(self.TEMP_mvp_corr_interp[id_mvp[i], id_valid] - self.TEMP_ctd_interp[id_ctd[i], id_valid])
|
|
1512
|
+
mean_temp_diff.append(temp_diff)
|
|
1513
|
+
|
|
1514
|
+
cond_diff = np.nanmean(self.COND_mvp_corr_interp[id_mvp[i], id_valid] - self.COND_ctd_interp[id_ctd[i], id_valid])
|
|
1515
|
+
mean_cond_diff.append(cond_diff)
|
|
1516
|
+
print("Mean temperature difference between MVP and CTD profiles:", np.mean(mean_temp_diff))
|
|
1517
|
+
print("Mean conductivity difference between MVP and CTD profiles:", np.mean(mean_cond_diff))
|
|
1518
|
+
|
|
1519
|
+
|
|
1520
|
+
|
|
1521
|
+
|
|
1522
|
+
def to_netcdf(self, filepath, corrected=False, compression=True, engine=None, per_profile_files=False):
|
|
1425
1523
|
"""
|
|
1426
1524
|
Export MVP data to a NetCDF file using xarray.
|
|
1427
1525
|
|
|
@@ -1591,7 +1689,7 @@ class Analyzer:
|
|
|
1591
1689
|
'title': 'MVP profile data',
|
|
1592
1690
|
'Conventions': 'CF-1.8',
|
|
1593
1691
|
'institution': 'LMD/CNRS',
|
|
1594
|
-
'source': '
|
|
1692
|
+
'source': 'PyMVP',
|
|
1595
1693
|
'history': f"Created on {datetime.now().isoformat()}",
|
|
1596
1694
|
'mvp_Yorig': int(self.Yorig)
|
|
1597
1695
|
}
|
|
@@ -1606,17 +1704,13 @@ class Analyzer:
|
|
|
1606
1704
|
elif engine == 'h5netcdf':
|
|
1607
1705
|
encoding = {name: {'compression': 'gzip', 'compression_opts': 4} for name in data_vars.keys()}
|
|
1608
1706
|
|
|
1609
|
-
|
|
1610
|
-
if
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
ds.to_netcdf(out_path, encoding=encoding, engine=engine)
|
|
1617
|
-
print(f"NetCDF written: {out_path} using engine={engine}")
|
|
1618
|
-
return
|
|
1619
|
-
base_dir = filepath
|
|
1707
|
+
|
|
1708
|
+
if (not per_profile_files) and filepath.lower().endswith('.nc'):
|
|
1709
|
+
out_path = filepath
|
|
1710
|
+
ds.to_netcdf(out_path, encoding=encoding, engine=engine)
|
|
1711
|
+
print(f"NetCDF written: {out_path} using engine={engine}")
|
|
1712
|
+
return
|
|
1713
|
+
base_dir = filepath
|
|
1620
1714
|
|
|
1621
1715
|
if not base_dir.endswith(os.sep):
|
|
1622
1716
|
base_dir = base_dir + os.sep
|
|
@@ -36,12 +36,11 @@ import scipy.stats as st
|
|
|
36
36
|
from datetime import date
|
|
37
37
|
from datetime import datetime
|
|
38
38
|
from scipy import interpolate
|
|
39
|
-
from scipy.signal import butter, freqz
|
|
40
39
|
from scipy import signal
|
|
41
40
|
import gsw
|
|
42
|
-
from scipy.interpolate import
|
|
41
|
+
from scipy.interpolate import interp1d
|
|
43
42
|
from netCDF4 import Dataset
|
|
44
|
-
from scipy.signal import butter, filtfilt, correlate, correlation_lags
|
|
43
|
+
from scipy.signal import butter, filtfilt, correlate, correlation_lags,savgol_filter
|
|
45
44
|
|
|
46
45
|
#
|
|
47
46
|
################################################################################
|
|
@@ -993,3 +992,85 @@ def bin_average_v2(P,T,C,S,time,dp=0.05):
|
|
|
993
992
|
np.array(C_bin),
|
|
994
993
|
np.array(S_bin),
|
|
995
994
|
np.array(time_bin))
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
|
|
1002
|
+
def align_profiles(P, T_ref, T_to_align_raw, min_depth=0,max_shift=20):
|
|
1003
|
+
"""
|
|
1004
|
+
Pipeline complet :
|
|
1005
|
+
- estime ΔP
|
|
1006
|
+
- recale
|
|
1007
|
+
- estime ΔT
|
|
1008
|
+
- corrige
|
|
1009
|
+
"""
|
|
1010
|
+
|
|
1011
|
+
### 1. calcul delta de pression
|
|
1012
|
+
|
|
1013
|
+
# Masque pour exclure les valeurs non finies
|
|
1014
|
+
mask_nan = (
|
|
1015
|
+
np.isfinite(P) &
|
|
1016
|
+
np.isfinite(T_ref) &
|
|
1017
|
+
np.isfinite(T_to_align_raw)
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
P = P[mask_nan]
|
|
1021
|
+
T_ref = T_ref[mask_nan]
|
|
1022
|
+
T_to_align = T_to_align_raw[mask_nan]
|
|
1023
|
+
|
|
1024
|
+
# Masque pour exclure la surface
|
|
1025
|
+
mask = P >= min_depth
|
|
1026
|
+
|
|
1027
|
+
P = P[mask]
|
|
1028
|
+
T_ref = T_ref[mask]
|
|
1029
|
+
T_to_align = T_to_align[mask]
|
|
1030
|
+
|
|
1031
|
+
# Lissage léger
|
|
1032
|
+
T1s = savgol_filter(T_ref, 11, 2)
|
|
1033
|
+
T2s = savgol_filter(T_to_align, 11, 2)
|
|
1034
|
+
|
|
1035
|
+
# Gradients
|
|
1036
|
+
dT1 = np.gradient(T1s, P)
|
|
1037
|
+
dT2 = np.gradient(T2s, P)
|
|
1038
|
+
|
|
1039
|
+
# Normalisation (important pour corrélation)
|
|
1040
|
+
dT1 = (dT1 - np.mean(dT1)) / np.std(dT1)
|
|
1041
|
+
dT2 = (dT2 - np.mean(dT2)) / np.std(dT2)
|
|
1042
|
+
|
|
1043
|
+
# Corrélation
|
|
1044
|
+
corr = correlate(dT2, dT1, mode='full')
|
|
1045
|
+
lags = np.arange(-len(dT1)+1, len(dT1))
|
|
1046
|
+
|
|
1047
|
+
# Convertir en décalage en pression
|
|
1048
|
+
dP = np.mean(np.diff(P))
|
|
1049
|
+
shifts = lags * dP
|
|
1050
|
+
|
|
1051
|
+
# Limiter les shifts plausibles
|
|
1052
|
+
valid = np.abs(shifts) <= max_shift
|
|
1053
|
+
|
|
1054
|
+
deltaP = shifts[valid][np.argmax(corr[valid])]
|
|
1055
|
+
|
|
1056
|
+
### 2. recalage pression
|
|
1057
|
+
f = interp1d(P + deltaP, T_to_align, bounds_error=False, fill_value=np.nan)
|
|
1058
|
+
T_shifted = f(P)
|
|
1059
|
+
|
|
1060
|
+
### 3. calcul delta de température
|
|
1061
|
+
mask = (P >= min_depth) & np.isfinite(T_ref) & np.isfinite(T_shifted)
|
|
1062
|
+
deltaT = np.median(T_shifted[mask] - T_ref[mask])
|
|
1063
|
+
|
|
1064
|
+
### 4. recalage thermique
|
|
1065
|
+
T_corrected = T_shifted - deltaT
|
|
1066
|
+
|
|
1067
|
+
mask_corrected = np.isfinite(T_corrected)
|
|
1068
|
+
|
|
1069
|
+
# copie pour ne pas modifier l'original directement
|
|
1070
|
+
T_out = T_to_align_raw.copy()
|
|
1071
|
+
|
|
1072
|
+
# injection uniquement là où c’est valide
|
|
1073
|
+
T_out_indices = np.where(mask_nan)[0]
|
|
1074
|
+
T_out[T_out_indices[mask_corrected]] = T_corrected[mask_corrected]
|
|
1075
|
+
|
|
1076
|
+
return T_out, deltaP, deltaT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|