PyMVP 0.1.8__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.8 → pymvp-0.2.1}/PKG-INFO +1 -1
- {pymvp-0.1.8 → pymvp-0.2.1}/PyMVP/main.py +145 -52
- {pymvp-0.1.8 → pymvp-0.2.1}/PyMVP/mvp_routines.py +84 -3
- {pymvp-0.1.8 → pymvp-0.2.1}/PyMVP.egg-info/PKG-INFO +1 -1
- {pymvp-0.1.8 → pymvp-0.2.1}/pyproject.toml +1 -1
- {pymvp-0.1.8 → pymvp-0.2.1}/PyMVP/__init__.py +0 -0
- {pymvp-0.1.8 → pymvp-0.2.1}/PyMVP.egg-info/SOURCES.txt +0 -0
- {pymvp-0.1.8 → pymvp-0.2.1}/PyMVP.egg-info/dependency_links.txt +0 -0
- {pymvp-0.1.8 → pymvp-0.2.1}/PyMVP.egg-info/requires.txt +0 -0
- {pymvp-0.1.8 → pymvp-0.2.1}/PyMVP.egg-info/top_level.txt +0 -0
- {pymvp-0.1.8 → 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
|
|
|
@@ -629,14 +625,8 @@ class Analyzer:
|
|
|
629
625
|
print('CTD data loaded successfully.')
|
|
630
626
|
self.ctd = True
|
|
631
627
|
|
|
632
|
-
return
|
|
633
628
|
|
|
634
629
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
630
|
def compute_waterflow(self,horizontal_speed=2,corr=False):
|
|
641
631
|
"""
|
|
642
632
|
Compute the water flow speed (u,v) from the horizontal speed and the direction of the profiles.
|
|
@@ -656,6 +646,7 @@ class Analyzer:
|
|
|
656
646
|
|
|
657
647
|
self.SPEED_mvp = SPEED_MVP
|
|
658
648
|
print('Water flow speed computed successfully.')
|
|
649
|
+
self.speed = True
|
|
659
650
|
|
|
660
651
|
def print_profile_metadata(self):
|
|
661
652
|
"""
|
|
@@ -939,7 +930,7 @@ class Analyzer:
|
|
|
939
930
|
plt.xlabel('Fluorescence, ug/L')
|
|
940
931
|
plt.ylabel('Pressure, dbar')
|
|
941
932
|
|
|
942
|
-
def
|
|
933
|
+
def plot_diagramTS(self,id_mvp=None,id_ctd=None,correction=False):
|
|
943
934
|
"""
|
|
944
935
|
Plot the TS diagram (Salinity vs Temperature) for one or more profiles, with isopycnals.
|
|
945
936
|
Args:
|
|
@@ -1235,7 +1226,7 @@ class Analyzer:
|
|
|
1235
1226
|
print(f" MVP down: {rmse_cond_down:.4f} S/m (deep: {rmse_cond_down_deep:.4f} S/m)")
|
|
1236
1227
|
print(f" MVP up: {rmse_cond_up:.4f} S/m (deep: {rmse_cond_up_deep:.4f} S/m)")
|
|
1237
1228
|
|
|
1238
|
-
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,):
|
|
1239
1230
|
"""
|
|
1240
1231
|
Apply oxygen correction to MVP dissolved oxygen profiles thanks to CTD data.
|
|
1241
1232
|
Args:
|
|
@@ -1296,9 +1287,9 @@ class Analyzer:
|
|
|
1296
1287
|
rmse_after_full = np.mean(np.sqrt(np.nanmean((DO_mvp_corr_full_interp - DO_ctd_interp)**2,axis=1)))
|
|
1297
1288
|
print(f"RMSE after correction (full profile): {rmse_after_full:.4f}")
|
|
1298
1289
|
|
|
1290
|
+
self.DO_mvp_raw = self.DO_mvp.copy()
|
|
1291
|
+
self.DO_mvp = DO_mvp_corr_full
|
|
1299
1292
|
|
|
1300
|
-
if correction:
|
|
1301
|
-
self.DO_mvp = DO_mvp_corr_full
|
|
1302
1293
|
|
|
1303
1294
|
if plotting:
|
|
1304
1295
|
|
|
@@ -1315,7 +1306,6 @@ class Analyzer:
|
|
|
1315
1306
|
plt.show()
|
|
1316
1307
|
|
|
1317
1308
|
|
|
1318
|
-
|
|
1319
1309
|
def mvp_correction(self,high_cutoff=1,dp=0.1):
|
|
1320
1310
|
|
|
1321
1311
|
T_MVP_corr = []
|
|
@@ -1369,7 +1359,7 @@ class Analyzer:
|
|
|
1369
1359
|
self.COND_mvp_corr = {i: sublist for i, sublist in enumerate(C_MVP_corr)}
|
|
1370
1360
|
self.SALT_mvp_corr = {i: sublist for i, sublist in enumerate(S_MVP_corr)}
|
|
1371
1361
|
self.TIME_mvp_corr = {i: sublist for i, sublist in enumerate(Time_MVP_corr)}
|
|
1372
|
-
|
|
1362
|
+
self.corrected = True
|
|
1373
1363
|
|
|
1374
1364
|
print("MVP profiles corrected.")
|
|
1375
1365
|
|
|
@@ -1381,6 +1371,11 @@ class Analyzer:
|
|
|
1381
1371
|
"""
|
|
1382
1372
|
if not self.ctd:
|
|
1383
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
|
+
|
|
1384
1379
|
|
|
1385
1380
|
if not hasattr(self, 'PRES_mvp_corr'):
|
|
1386
1381
|
raise ValueError("Corrected MVP data not available. Apply corrections first.")
|
|
@@ -1398,8 +1393,9 @@ class Analyzer:
|
|
|
1398
1393
|
max_lensalt = max([len(p) for p in self.SALT_mvp_corr.values()])
|
|
1399
1394
|
SALT_mvp_corr_mat = np.array([list(row) + [np.nan] * (max_lensalt - len(row)) for row in self.SALT_mvp_corr.values()])
|
|
1400
1395
|
|
|
1401
|
-
|
|
1402
|
-
|
|
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()])
|
|
1403
1399
|
|
|
1404
1400
|
max_lentime = max([len(p) for p in self.TIME_mvp_corr.values()])
|
|
1405
1401
|
TIME_mvp_corr_mat = np.array([list(row) + [np.nan] * (max_lentime - len(row)) for row in self.TIME_mvp_corr.values()])
|
|
@@ -1407,22 +1403,123 @@ class Analyzer:
|
|
|
1407
1403
|
|
|
1408
1404
|
pressure_grid = np.linspace(np.nanmin(PRES_mvp_corr_mat), np.nanmax(PRES_mvp_corr_mat), length)
|
|
1409
1405
|
|
|
1410
|
-
self.
|
|
1411
|
-
self.
|
|
1412
|
-
self.
|
|
1413
|
-
self.
|
|
1414
|
-
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)
|
|
1415
1413
|
self.TEMP_mvp_corr_interp = mvp.vertical_interp(PRES_mvp_corr_mat, TEMP_mvp_corr_mat, pressure_grid)
|
|
1416
1414
|
self.PRES_mvp_corr_interp = mvp.vertical_interp(PRES_mvp_corr_mat, PRES_mvp_corr_mat, pressure_grid)
|
|
1417
1415
|
self.COND_mvp_corr_interp = mvp.vertical_interp(PRES_mvp_corr_mat, COND_mvp_corr_mat, pressure_grid)
|
|
1418
1416
|
self.SALT_mvp_corr_interp = mvp.vertical_interp(PRES_mvp_corr_mat, SALT_mvp_corr_mat, pressure_grid)
|
|
1419
|
-
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)
|
|
1420
1425
|
self.TIME_mvp_corr_interp = mvp.vertical_interp(PRES_mvp_corr_mat, TIME_mvp_corr_mat, pressure_grid)
|
|
1421
1426
|
|
|
1422
1427
|
print('CTD data interpolated onto corrected MVP pressure levels.')
|
|
1423
1428
|
|
|
1424
1429
|
|
|
1425
|
-
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):
|
|
1426
1523
|
"""
|
|
1427
1524
|
Export MVP data to a NetCDF file using xarray.
|
|
1428
1525
|
|
|
@@ -1592,7 +1689,7 @@ class Analyzer:
|
|
|
1592
1689
|
'title': 'MVP profile data',
|
|
1593
1690
|
'Conventions': 'CF-1.8',
|
|
1594
1691
|
'institution': 'LMD/CNRS',
|
|
1595
|
-
'source': '
|
|
1692
|
+
'source': 'PyMVP',
|
|
1596
1693
|
'history': f"Created on {datetime.now().isoformat()}",
|
|
1597
1694
|
'mvp_Yorig': int(self.Yorig)
|
|
1598
1695
|
}
|
|
@@ -1607,17 +1704,13 @@ class Analyzer:
|
|
|
1607
1704
|
elif engine == 'h5netcdf':
|
|
1608
1705
|
encoding = {name: {'compression': 'gzip', 'compression_opts': 4} for name in data_vars.keys()}
|
|
1609
1706
|
|
|
1610
|
-
|
|
1611
|
-
if
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
ds.to_netcdf(out_path, encoding=encoding, engine=engine)
|
|
1618
|
-
print(f"NetCDF written: {out_path} using engine={engine}")
|
|
1619
|
-
return
|
|
1620
|
-
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
|
|
1621
1714
|
|
|
1622
1715
|
if not base_dir.endswith(os.sep):
|
|
1623
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
|