PetThermoTools 0.2.39__py3-none-any.whl → 0.2.40__py3-none-any.whl
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.
- PetThermoTools/Barom.py +690 -575
- PetThermoTools/GenFuncs.py +7 -2
- PetThermoTools/Liq.py +0 -8
- PetThermoTools/MELTS.py +85 -57
- PetThermoTools/Path.py +181 -121
- PetThermoTools/Path_wrappers.py +448 -259
- PetThermoTools/PhaseDiagrams.py +30 -98
- PetThermoTools/Saturation.py +353 -2
- PetThermoTools/_version.py +1 -1
- {PetThermoTools-0.2.39.dist-info → PetThermoTools-0.2.40.dist-info}/METADATA +1 -1
- PetThermoTools-0.2.40.dist-info/RECORD +20 -0
- PetThermoTools-0.2.39.dist-info/RECORD +0 -20
- {PetThermoTools-0.2.39.dist-info → PetThermoTools-0.2.40.dist-info}/LICENSE.txt +0 -0
- {PetThermoTools-0.2.39.dist-info → PetThermoTools-0.2.40.dist-info}/WHEEL +0 -0
- {PetThermoTools-0.2.39.dist-info → PetThermoTools-0.2.40.dist-info}/top_level.txt +0 -0
PetThermoTools/PhaseDiagrams.py
CHANGED
@@ -11,7 +11,12 @@ from tqdm.notebook import tqdm, trange
|
|
11
11
|
import random
|
12
12
|
import time
|
13
13
|
|
14
|
-
def phaseDiagram_calc(cores = None, Model = None, bulk = None, T_C = None, P_bar = None,
|
14
|
+
def phaseDiagram_calc(cores = None, Model = None, bulk = None, T_C = None, P_bar = None,
|
15
|
+
T_min_C = None, T_max_C = None, T_num = None,
|
16
|
+
P_min_bar = None, P_max_bar = None, P_num = None,
|
17
|
+
Fe3Fet_Liq = None, H2O_Liq = None, CO2_Liq = None,
|
18
|
+
Fe3Fet_init = None, H2O_init = None, CO2_init = None,
|
19
|
+
fO2_buffer = None, fO2_offset = None, i_max = 25, grid = True, refine = None):
|
15
20
|
"""
|
16
21
|
Calculate phase diagrams for igneous systems (rocks and magmas).
|
17
22
|
|
@@ -61,6 +66,20 @@ def phaseDiagram_calc(cores = None, Model = None, bulk = None, T_C = None, P_bar
|
|
61
66
|
pandas.DataFrame
|
62
67
|
A dataframe containing the phase diagram results.
|
63
68
|
"""
|
69
|
+
if H2O_Liq is not None:
|
70
|
+
print('Warning - the kwarg "H2O_Liq" will be removed from v1.0.0 onwards. Please use "H2O_init" instead.')
|
71
|
+
if H2O_init is None:
|
72
|
+
H2O_init = H2O_Liq
|
73
|
+
|
74
|
+
if CO2_Liq is not None:
|
75
|
+
print('Warning - the kwarg "CO2_Liq" will be removed from v1.0.0 onwards. Please use "CO2_init" instead.')
|
76
|
+
if CO2_init is None:
|
77
|
+
CO2_init = CO2_Liq
|
78
|
+
|
79
|
+
if Fe3Fet_Liq is not None:
|
80
|
+
print('Warning - the kwarg "Fe3Fet_Liq" will be removed from v1.0.0 onwards. Please use "Fe3Fet_init" instead.')
|
81
|
+
if Fe3Fet_init is None:
|
82
|
+
Fe3Fet_init = Fe3Fet_Liq
|
64
83
|
|
65
84
|
comp = bulk.copy()
|
66
85
|
|
@@ -80,7 +99,7 @@ def phaseDiagram_calc(cores = None, Model = None, bulk = None, T_C = None, P_bar
|
|
80
99
|
comp = comp.to_dict()
|
81
100
|
|
82
101
|
# ensure the bulk composition has the correct headers etc.
|
83
|
-
comp = comp_fix(Model = Model, comp = comp, Fe3Fet_Liq =
|
102
|
+
comp = comp_fix(Model = Model, comp = comp, Fe3Fet_Liq = Fe3Fet_init, H2O_Liq = H2O_init, CO2_Liq=CO2_init)
|
84
103
|
|
85
104
|
if type(comp) == dict:
|
86
105
|
if comp['Fe3Fet_Liq'] == 0.0 and "MELTS" in Model and fO2_buffer is None:
|
@@ -110,20 +129,6 @@ def phaseDiagram_calc(cores = None, Model = None, bulk = None, T_C = None, P_bar
|
|
110
129
|
c = 0
|
111
130
|
j = 0
|
112
131
|
|
113
|
-
|
114
|
-
# subarray_length = len(T_flat) // cores
|
115
|
-
|
116
|
-
# # Initialize an empty list to store subarrays
|
117
|
-
# subarrays_T = []
|
118
|
-
|
119
|
-
# Loop through the indices and create subarrays
|
120
|
-
# for i in range(subarray_length):
|
121
|
-
# subarray_T = T_flat[i::subarray_length]
|
122
|
-
# subarrays_T.append(subarray_T)
|
123
|
-
|
124
|
-
# subarray_P = T_flat[i::subarray_length]
|
125
|
-
# subarrays_P.append(subarray_P)
|
126
|
-
|
127
132
|
while len(T_flat)>1:
|
128
133
|
if j > i_max:
|
129
134
|
break
|
@@ -175,22 +180,6 @@ def phaseDiagram_calc(cores = None, Model = None, bulk = None, T_C = None, P_bar
|
|
175
180
|
T_path_C = np.flip(T_path_C)
|
176
181
|
P_path_bar = np.flip(P_path_bar)
|
177
182
|
|
178
|
-
# if "MELTS" not in Model:
|
179
|
-
# T_path_C = T_path_C.tolist()
|
180
|
-
# P_path_bar = P_path_bar.tolist()
|
181
|
-
|
182
|
-
# if j > 5:
|
183
|
-
# com = list(zip(T, P))
|
184
|
-
|
185
|
-
# # Step 2: Randomize the order of the combined list
|
186
|
-
# random.shuffle(com)
|
187
|
-
|
188
|
-
# # Step 3: Separate the pairs back into two arrays
|
189
|
-
# T_randomized, P_randomized = zip(*com)
|
190
|
-
|
191
|
-
# T_path_C = np.array(T_randomized)
|
192
|
-
# P_path_bar = np.array(P_randomized)
|
193
|
-
|
194
183
|
p = Process(target = path, args = (q,i), kwargs = {'Model': Model, 'comp': comp,
|
195
184
|
'T_path_C': T_path_C,
|
196
185
|
'P_path_bar': P_path_bar,
|
@@ -242,66 +231,6 @@ def phaseDiagram_calc(cores = None, Model = None, bulk = None, T_C = None, P_bar
|
|
242
231
|
p.join()
|
243
232
|
p.terminate()
|
244
233
|
|
245
|
-
# TIMEOUT = 150 #+ #0.5*len(T_flat)
|
246
|
-
# start = time.time()
|
247
|
-
# for p in ps:
|
248
|
-
# ret = []
|
249
|
-
# if time.time() - start <= TIMEOUT:
|
250
|
-
# while time.time() - start <= TIMEOUT:
|
251
|
-
# try:
|
252
|
-
# ret = q.get(timeout = 2)
|
253
|
-
# break
|
254
|
-
# except:
|
255
|
-
# continue
|
256
|
-
#
|
257
|
-
# # if p.is_alive():
|
258
|
-
# # time.sleep(.1)
|
259
|
-
# # else:
|
260
|
-
# # p.terminate()
|
261
|
-
# # p.join()
|
262
|
-
# # break
|
263
|
-
#
|
264
|
-
# # if p.is_alive():
|
265
|
-
# else:
|
266
|
-
# try:
|
267
|
-
# ret = q.get(timeout = 5)
|
268
|
-
# except:
|
269
|
-
# ret =[]
|
270
|
-
#
|
271
|
-
# p.terminate()
|
272
|
-
# p.join()
|
273
|
-
|
274
|
-
# else:
|
275
|
-
# p.terminate()
|
276
|
-
# p.join()
|
277
|
-
|
278
|
-
# try:
|
279
|
-
# ret = q.get(timeout = 5)
|
280
|
-
# except:
|
281
|
-
# ret = []
|
282
|
-
|
283
|
-
# qs.append(ret)
|
284
|
-
# if p.is_alive():
|
285
|
-
# while time.time() - start <= TIMEOUT:
|
286
|
-
# if not p.is_alive():
|
287
|
-
# p.terminate()
|
288
|
-
# p.join()
|
289
|
-
# break
|
290
|
-
# time.sleep(.1)
|
291
|
-
# else:
|
292
|
-
# p.terminate()
|
293
|
-
# p.join()
|
294
|
-
# else:
|
295
|
-
# p.terminate()
|
296
|
-
# p.join()
|
297
|
-
#
|
298
|
-
# try:
|
299
|
-
# ret = q.get(timeout = 2)
|
300
|
-
# except:
|
301
|
-
# ret = []
|
302
|
-
|
303
|
-
# qs.append(ret)
|
304
|
-
|
305
234
|
for p in ps:
|
306
235
|
p.kill()
|
307
236
|
|
@@ -347,9 +276,6 @@ def phaseDiagram_calc(cores = None, Model = None, bulk = None, T_C = None, P_bar
|
|
347
276
|
new_flat = flat[np.where((flat[:, None] == Res_flat).all(-1).any(-1) == False)]
|
348
277
|
|
349
278
|
if np.shape(new_flat)[0] > 0.0:
|
350
|
-
# df = pd.DataFrame(columns = ['T_C', 'P_bar'])
|
351
|
-
# for i in range(len(new_flat)):
|
352
|
-
# df.loc[i] = new_flat[i]
|
353
279
|
A = np.zeros((np.shape(new_flat)[0], np.shape(Combined.values)[1]))
|
354
280
|
A[:,:2] = new_flat
|
355
281
|
|
@@ -358,7 +284,6 @@ def phaseDiagram_calc(cores = None, Model = None, bulk = None, T_C = None, P_bar
|
|
358
284
|
C = np.concatenate((A, B))
|
359
285
|
|
360
286
|
Combined = pd.DataFrame(columns = list(Combined.keys()), data = C)
|
361
|
-
# Combined = pd.concat([Combined, df], axis = 0, ignore_index = True)
|
362
287
|
|
363
288
|
Combined['T_C'] = np.round(Combined['T_C'], 2)
|
364
289
|
Combined['P_bar'] = np.round(Combined['P_bar'], 2)
|
@@ -366,9 +291,15 @@ def phaseDiagram_calc(cores = None, Model = None, bulk = None, T_C = None, P_bar
|
|
366
291
|
Combined = Combined.reset_index(drop = True)
|
367
292
|
Combined = Combined.dropna(subset = ['T_C'])
|
368
293
|
|
294
|
+
if refine is not None:
|
295
|
+
for i in range(refine):
|
296
|
+
Combined = phaseDiagram_refine(Data = Combined, Model = Model, bulk = bulk, Fe3Fet_Liq=Fe3Fet_init,
|
297
|
+
H2O_Liq=H2O_init, CO2_Liq=CO2_init, fO2_buffer=fO2_buffer, fO2_offset=fO2_offset, i_max = i_max)
|
298
|
+
|
369
299
|
return Combined
|
370
300
|
|
371
|
-
def phaseDiagram_refine(Data = None, Model = None, bulk = None, Fe3Fet_Liq = None, H2O_Liq = None,
|
301
|
+
def phaseDiagram_refine(Data = None, Model = None, bulk = None, Fe3Fet_Liq = None, H2O_Liq = None, CO2_Liq = None,
|
302
|
+
fO2_buffer = None, fO2_offset = None, i_max = 25):
|
372
303
|
Combined = Data.copy()
|
373
304
|
# find existing T,P data
|
374
305
|
T_C = Combined['T_C'].unique()
|
@@ -508,7 +439,8 @@ def phaseDiagram_refine(Data = None, Model = None, bulk = None, Fe3Fet_Liq = Non
|
|
508
439
|
matching_df = matching_df.loc[np.where(matching_df['h'] != 0.0)[0],:]
|
509
440
|
matching_df = matching_df.reset_index(drop = True)
|
510
441
|
|
511
|
-
New = phaseDiagram_calc(cores =
|
442
|
+
New = phaseDiagram_calc(cores = multiprocessing.cpu_count(),
|
443
|
+
Model = Model, bulk = bulk, T_C = T_C, P_bar = P_bar, Fe3Fet_init = Fe3Fet_Liq, H2O_init = H2O_Liq, CO2_init = CO2_Liq, fO2_buffer = fO2_buffer, fO2_offset = fO2_offset, i_max = i_max, grid = False)
|
512
444
|
|
513
445
|
# A = New.values
|
514
446
|
# B = matching_df.values
|
PetThermoTools/Saturation.py
CHANGED
@@ -303,7 +303,357 @@ def findSatPressure(cores = None, Model = None, bulk = None, T_C_init = None, T_
|
|
303
303
|
return Res
|
304
304
|
|
305
305
|
|
306
|
-
def
|
306
|
+
def saturation_pressure(Model = "MELTSv1.2.0", cores = multiprocessing.cpu_count(),
|
307
|
+
bulk = None, T_C_init = None, T_fixed_C = None, P_bar_init = 5000,
|
308
|
+
Fe3Fet_Liq = None, Fe3Fet_init = None, H2O_Liq = None, H2O_init = None,
|
309
|
+
CO2_Liq = None, CO2_init = None, fO2_buffer = None, fO2_offset = None,
|
310
|
+
copy_columns = None, multi_processing = True, timeout = 180):
|
311
|
+
"""
|
312
|
+
Estimates the pressure at which volatile saturation occurs for one or more melt compositions
|
313
|
+
using MELTS thermodynamic models.
|
314
|
+
|
315
|
+
Supports both single and batched calculations, with optional multiprocessing to accelerate large-scale runs.
|
316
|
+
|
317
|
+
Parameters
|
318
|
+
----------
|
319
|
+
Model : str, default 'MELTSv1.2.0'
|
320
|
+
The thermodynamic model to use.
|
321
|
+
MELTS:
|
322
|
+
"MELTSv1.2.0"
|
323
|
+
"MELTSv1.1.0"
|
324
|
+
"MELTSv1.0.2" - only H2O
|
325
|
+
"pMELTS" - only H2O
|
326
|
+
cores : int, default multiprocessing.cpu_count()
|
327
|
+
Number of parallel processes to use.
|
328
|
+
bulk : dict, pandas.Series, or pandas.DataFrame
|
329
|
+
Bulk melt composition(s). If a Series or single dict is passed, a single calculation is run.
|
330
|
+
If a DataFrame is passed, a calculation is performed for each row.
|
331
|
+
T_C_init : float or np.ndarray, optional
|
332
|
+
Initial temperature(s) in degrees Celsius. System will co-solve the liquidus temperature and pressure of volatile saturation. Used if not specifying `T_fixed_C`.
|
333
|
+
T_fixed_C : float or np.ndarray, optional
|
334
|
+
Fixed temperature(s) for the saturation pressure calculation. Solid phases are disabled in all calculations.
|
335
|
+
P_bar_init : float or np.ndarray, default 5000
|
336
|
+
Initial pressure guess(es) in bars.
|
337
|
+
Fe3Fet_Liq : float or np.ndarray, optional [DEPRECATED]
|
338
|
+
Legacy alias for initial Fe³⁺/∑Fe ratio. Prefer `Fe3Fet_init`.
|
339
|
+
Fe3Fet_init : float or np.ndarray, optional
|
340
|
+
Initial Fe³⁺/∑Fe ratio(s) in the liquid.
|
341
|
+
H2O_Liq : float or np.ndarray, optional [DEPRECATED]
|
342
|
+
Legacy alias for initial H₂O content. Prefer `H2O_init`.
|
343
|
+
H2O_init : float or np.ndarray, optional
|
344
|
+
Initial H₂O content(s) in the liquid, in wt%.
|
345
|
+
CO2_Liq : float or np.ndarray, optional [DEPRECATED]
|
346
|
+
Legacy alias for initial CO₂ content. Prefer `CO2_init`.
|
347
|
+
CO2_init : float or np.ndarray, optional
|
348
|
+
Initial CO₂ content(s) in the liquid, in wt%.
|
349
|
+
fO2_buffer : str, optional
|
350
|
+
Oxygen fugacity buffer to use ('FMQ' or 'NNO').
|
351
|
+
fO2_offset : float or np.ndarray, optional
|
352
|
+
Offset from the fO₂ buffer in log units (e.g., +1.0 = 1 log unit above buffer).
|
353
|
+
copy_columns : str or list of str, optional
|
354
|
+
If bulk is a DataFrame, copies specified columns from input into output DataFrame for tracking.
|
355
|
+
multi_processing : bool, default True
|
356
|
+
Whether to parallelize the calculations across multiple cores.
|
357
|
+
timeout : int, default 180
|
358
|
+
Timeout for individual parallel processes, in seconds.
|
359
|
+
|
360
|
+
Returns
|
361
|
+
-------
|
362
|
+
pandas.DataFrame or dict
|
363
|
+
- If multiple calculations are performed, returns a DataFrame with saturation pressures and
|
364
|
+
other outputs, indexed by run number.
|
365
|
+
- If a single calculation is run (`multi_processing=False` or `bulk` is a single composition),
|
366
|
+
returns a dictionary of calculated results.
|
367
|
+
|
368
|
+
Raises
|
369
|
+
------
|
370
|
+
Warning
|
371
|
+
- If required MELTS packages are not found in the Python path.
|
372
|
+
- If unsupported fO₂ buffers are specified.
|
373
|
+
- If zero Fe³⁺/∑Fe or H₂O is specified in MELTS without buffer constraints.
|
374
|
+
|
375
|
+
Notes
|
376
|
+
-----
|
377
|
+
- Missing inputs for temperature, pressure, or fO₂ offset are auto-expanded if multiple runs are inferred.
|
378
|
+
- For MELTS-based models, small amounts of H₂O and ferric iron often improve model stability.
|
379
|
+
- The function handles multiprocessing with safe timeouts and partial result recovery.
|
380
|
+
"""
|
381
|
+
|
382
|
+
if "MELTS" in Model:
|
383
|
+
try:
|
384
|
+
from meltsdynamic import MELTSdynamic
|
385
|
+
except:
|
386
|
+
Warning('alphaMELTS for Python files are not on the python path. \n Please add these files to the path running \n import sys \n sys.path.append(r"insert_your_path_to_melts_here") \n You are looking for the location of the meltsdynamic.py file')
|
387
|
+
|
388
|
+
if bulk is not None:
|
389
|
+
comp = bulk.copy()
|
390
|
+
|
391
|
+
if H2O_Liq is not None:
|
392
|
+
print('Warning - the kwarg "H2O_Liq" will be removed from v1.0.0 onwards. Please use "H2O_init" instead.')
|
393
|
+
if H2O_init is None:
|
394
|
+
H2O_init = H2O_Liq
|
395
|
+
|
396
|
+
if CO2_Liq is not None:
|
397
|
+
print('Warning - the kwarg "CO2_Liq" will be removed from v1.0.0 onwards. Please use "CO2_init" instead.')
|
398
|
+
if CO2_init is None:
|
399
|
+
CO2_init = CO2_Liq
|
400
|
+
|
401
|
+
if Fe3Fet_Liq is not None:
|
402
|
+
print('Warning - the kwarg "Fe3Fet_Liq" will be removed from v1.0.0 onwards. Please use "Fe3Fet_init" instead.')
|
403
|
+
if Fe3Fet_init is None:
|
404
|
+
Fe3Fet_init = Fe3Fet_Liq
|
405
|
+
|
406
|
+
# if comp is entered as a pandas series, it must first be converted to a dict
|
407
|
+
if type(comp) == pd.core.series.Series:
|
408
|
+
comp = comp.to_dict()
|
409
|
+
|
410
|
+
if fO2_buffer is not None:
|
411
|
+
if fO2_buffer != "NNO":
|
412
|
+
if fO2_buffer != "FMQ":
|
413
|
+
raise Warning("fO2 buffer specified is not an allowed input. This argument can only be 'FMQ' or 'NNO' \n if you want to offset from these buffers use the 'fO2_offset' argument.")
|
414
|
+
|
415
|
+
# ensure the bulk composition has the correct headers etc.
|
416
|
+
comp = comp_fix(Model = Model, comp = comp, Fe3Fet_Liq = Fe3Fet_init, H2O_Liq = H2O_init, CO2_Liq = CO2_init)
|
417
|
+
|
418
|
+
if type(comp) == dict:
|
419
|
+
if comp['H2O_Liq'] == 0.0 and "MELTS" in Model:
|
420
|
+
raise Warning("Adding small amounts of H$_{2}$O may improve the ability of MELTS to accurately reproduce the saturation of oxide minerals. Additionally, sufficient H$_{2}$O is required in the model for MELTS to predict the crystallisation of apatite, rather than whitlockite.")
|
421
|
+
|
422
|
+
if comp['Fe3Fet_Liq'] == 0.0 and "MELTS" in Model and fO2_buffer is None:
|
423
|
+
raise Warning("MELTS often fails to produce any results when no ferric Fe is included in the starting composition and an fO2 buffer is not set.")
|
424
|
+
|
425
|
+
# specify the number of calculations to be performed in each sequence
|
426
|
+
One = 0
|
427
|
+
if type(comp) == pd.core.frame.DataFrame: # simplest scenario - one calculation per bulk composition imported
|
428
|
+
A = len(comp['SiO2_Liq'])//cores
|
429
|
+
B = len(comp['SiO2_Liq'])%cores
|
430
|
+
else:
|
431
|
+
if T_fixed_C is not None and type(T_fixed_C) == np.ndarray: # one calculation per T loaded.
|
432
|
+
A = len(T_fixed_C)//cores
|
433
|
+
B = len(T_fixed_C)%cores
|
434
|
+
elif fO2_offset is not None and type(fO2_offset) == np.ndarray: # one calculation per offset
|
435
|
+
A = len(fO2_offset)//cores
|
436
|
+
B = len(fO2_offset)%cores
|
437
|
+
else: # just one calculation
|
438
|
+
One = 1
|
439
|
+
A = 1
|
440
|
+
B = 0
|
441
|
+
|
442
|
+
Group = np.zeros(A) + cores
|
443
|
+
if B > 0:
|
444
|
+
Group = np.append(Group, B)
|
445
|
+
|
446
|
+
qs = []
|
447
|
+
q = Queue()
|
448
|
+
if One == 1:
|
449
|
+
if multi_processing:
|
450
|
+
p = Process(target = satP, args = (q,0),
|
451
|
+
kwargs = {'Model': Model, 'comp': comp, 'T_C_init': T_C_init,
|
452
|
+
'T_fixed_C': T_fixed_C, 'P_bar_init': P_bar_init,
|
453
|
+
'fO2_buffer': fO2_buffer, 'fO2_offset': fO2_offset})
|
454
|
+
p.start()
|
455
|
+
try:
|
456
|
+
ret = q.get(timeout = timeout)
|
457
|
+
except:
|
458
|
+
ret = []
|
459
|
+
|
460
|
+
TIMEOUT = 5
|
461
|
+
start = time.time()
|
462
|
+
if p.is_alive():
|
463
|
+
while time.time() - start <= TIMEOUT:
|
464
|
+
if not p.is_alive():
|
465
|
+
p.join()
|
466
|
+
p.terminate()
|
467
|
+
break
|
468
|
+
time.sleep(.1)
|
469
|
+
else:
|
470
|
+
p.terminate()
|
471
|
+
p.join(5)
|
472
|
+
else:
|
473
|
+
p.join()
|
474
|
+
p.terminate()
|
475
|
+
|
476
|
+
if len(ret) > 0:
|
477
|
+
Results, index = ret
|
478
|
+
else:
|
479
|
+
Results = {}
|
480
|
+
|
481
|
+
return Results
|
482
|
+
else:
|
483
|
+
Results = findSatPressure_MELTS(Model = Model, T_C_init=T_C_init, T_fixed_C=T_fixed_C,
|
484
|
+
P_bar_init=P_bar_init, comp = comp, fO2_buffer=fO2_buffer,
|
485
|
+
fO2_offset=fO2_offset)
|
486
|
+
return Results
|
487
|
+
else: # perform multiple crystallisation calculations
|
488
|
+
# first make sure everything is the right length
|
489
|
+
L = np.sum(Group)
|
490
|
+
if type(P_bar_init) == float or type(P_bar_init) == int:
|
491
|
+
P_bar_init = np.zeros(int(L)) + P_bar_init
|
492
|
+
if T_C_init is None:
|
493
|
+
T_C_init = [None] * int(L)
|
494
|
+
elif type(T_C_init) == float or type(T_C_init) == int:
|
495
|
+
T_C_init = np.zeros(int(L)) + T_C_init
|
496
|
+
if T_fixed_C is None:
|
497
|
+
T_fixed_C = [None] * int(L)
|
498
|
+
elif type(T_fixed_C) == float or type(T_fixed_C) == int:
|
499
|
+
T_fixed_C = np.zeros(int(L)) + T_fixed_C
|
500
|
+
if fO2_offset is None:
|
501
|
+
fO2_offset = [None] * int(L)
|
502
|
+
elif type(fO2_offset) == float or type(fO2_offset) == int:
|
503
|
+
fO2_offset = np.zeros(int(L)) + fO2_offset
|
504
|
+
|
505
|
+
index_in = np.arange(int(L))
|
506
|
+
combined_results = {}
|
507
|
+
index_out = np.array([], dtype=int)
|
508
|
+
timeout_main = timeout
|
509
|
+
|
510
|
+
while len(index_out)<len(index_in):
|
511
|
+
index = np.setdiff1d(index_in, index_out)
|
512
|
+
groups = np.array_split(index, cores)
|
513
|
+
non_empty_groups = [g for g in groups if g.size > 0]
|
514
|
+
groups = non_empty_groups
|
515
|
+
|
516
|
+
if len(groups[0])< 15:
|
517
|
+
timeout = len(groups[0])*timeout_main
|
518
|
+
else:
|
519
|
+
timeout = 15*timeout_main
|
520
|
+
|
521
|
+
processes = []
|
522
|
+
Start = time.time()
|
523
|
+
for i in range(len(groups)):
|
524
|
+
q = Queue()
|
525
|
+
p = Process(target = saturationP_multi, args = (q, groups[i]),
|
526
|
+
kwargs = {'Model': Model, 'comp': comp, 'T_C_init': T_C_init,
|
527
|
+
'T_fixed_C': T_fixed_C, 'P_bar_init': P_bar_init,
|
528
|
+
'fO2_buffer': fO2_buffer, 'fO2_offset': fO2_offset})
|
529
|
+
|
530
|
+
processes.append([p, q, groups[i]])
|
531
|
+
p.start()
|
532
|
+
|
533
|
+
for p, q, group in processes:
|
534
|
+
try:
|
535
|
+
if time.time() - Start < timeout + 5:
|
536
|
+
res = q.get(timeout = timeout)
|
537
|
+
else:
|
538
|
+
res = q.get(timeout=10)
|
539
|
+
except:
|
540
|
+
res = []
|
541
|
+
idx_chunks = np.array([group[0]], dtype = int)
|
542
|
+
|
543
|
+
p.join(timeout=2)
|
544
|
+
p.terminate()
|
545
|
+
|
546
|
+
if len(res) > 0.0:
|
547
|
+
idx_chunks, results = res
|
548
|
+
combined_results.update(results)
|
549
|
+
|
550
|
+
index_out = np.hstack((index_out, idx_chunks))
|
551
|
+
|
552
|
+
print(f"Completed {100*len(index_out)/len(index_in)} %")
|
553
|
+
|
554
|
+
results = combined_results
|
555
|
+
|
556
|
+
def results_to_dataframe(results, full_index):
|
557
|
+
# Convert 'Run X' → X and create DataFrame
|
558
|
+
df = pd.DataFrame.from_dict(results, orient='index')
|
559
|
+
df.index = df.index.str.extract(r'Run (\d+)')[0].values.astype(int)
|
560
|
+
|
561
|
+
# Reindex with full range, fill missing with 0.0
|
562
|
+
df = df.reindex(full_index, fill_value=0.0)
|
563
|
+
|
564
|
+
# Optional: sort index if needed
|
565
|
+
df = df.sort_index()
|
566
|
+
|
567
|
+
return df
|
568
|
+
Results = results_to_dataframe(results, index_in)
|
569
|
+
|
570
|
+
if copy_columns is not None:
|
571
|
+
if type(copy_columns) == str:
|
572
|
+
Results.insert(0, copy_columns, comp[copy_columns])
|
573
|
+
# Af_Combined.insert(0, copy_columns, comp[copy_columns])
|
574
|
+
elif type(copy_columns) == list:
|
575
|
+
j = 0
|
576
|
+
for i in copy_columns:
|
577
|
+
Results.insert(j, i, comp[i])
|
578
|
+
# Af_Combined.insert(j, i, comp[i])
|
579
|
+
j = j + 1
|
580
|
+
|
581
|
+
return Results
|
582
|
+
|
583
|
+
def saturationP_multi(q, index, *, Model = None, comp = None, T_C_init = None, T_fixed_C = None,
|
584
|
+
P_bar_init = None, fO2_buffer = None, fO2_offset = None):
|
585
|
+
results = {}
|
586
|
+
idx = []
|
587
|
+
if "MELTS" in Model:
|
588
|
+
from meltsdynamic import MELTSdynamic
|
589
|
+
|
590
|
+
if Model is None or Model == "MELTSv1.0.2":
|
591
|
+
melts = MELTSdynamic(1)
|
592
|
+
elif Model == "pMELTS":
|
593
|
+
melts = MELTSdynamic(2)
|
594
|
+
elif Model == "MELTSv1.1.0":
|
595
|
+
melts = MELTSdynamic(3)
|
596
|
+
elif Model == "MELTSv1.2.0":
|
597
|
+
melts = MELTSdynamic(4)
|
598
|
+
|
599
|
+
|
600
|
+
melts.engine.pressure = P_bar_init[0]
|
601
|
+
if T_fixed_C[0] is not None:
|
602
|
+
melts.engine.temperature = T_fixed_C[0] + 500
|
603
|
+
else:
|
604
|
+
melts.engine.temperature = T_C_init[0] + 500
|
605
|
+
|
606
|
+
if type(comp) == dict:
|
607
|
+
bulk = [comp['SiO2_Liq'], comp['TiO2_Liq'], comp['Al2O3_Liq'],
|
608
|
+
comp['Fe3Fet_Liq']*((159.59/2)/71.844)*comp['FeOt_Liq'],
|
609
|
+
comp['Cr2O3_Liq'], (1- comp['Fe3Fet_Liq'])*comp['FeOt_Liq'],
|
610
|
+
comp['MnO_Liq'], comp['MgO_Liq'], 0.0, 0.0, comp['CaO_Liq'],
|
611
|
+
comp['Na2O_Liq'], comp['K2O_Liq'], comp['P2O5_Liq'],
|
612
|
+
comp['H2O_Liq'], comp['CO2_Liq'], 0.0, 0.0, 0.0]
|
613
|
+
else:
|
614
|
+
bulk = [comp.loc[0,'SiO2_Liq'], comp.loc[0,'TiO2_Liq'], comp.loc[0,'Al2O3_Liq'],
|
615
|
+
comp.loc[0,'Fe3Fet_Liq']*((159.59/2)/71.844)*comp.loc[0,'FeOt_Liq'],
|
616
|
+
comp.loc[0,'Cr2O3_Liq'], (1- comp.loc[0,'Fe3Fet_Liq'])*comp.loc[0,'FeOt_Liq'],
|
617
|
+
comp.loc[0,'MnO_Liq'], comp.loc[0,'MgO_Liq'], 0.0, 0.0, comp.loc[0,'CaO_Liq'],
|
618
|
+
comp.loc[0,'Na2O_Liq'], comp.loc[0,'K2O_Liq'], comp.loc[0,'P2O5_Liq'],
|
619
|
+
comp.loc[0,'H2O_Liq'], comp.loc[0,'CO2_Liq'], 0.0, 0.0, 0.0]
|
620
|
+
|
621
|
+
melts.engine.setBulkComposition(bulk)
|
622
|
+
PL = melts.engine.calcSaturationState()
|
623
|
+
if T_fixed_C[0] is not None:
|
624
|
+
for p in PL:
|
625
|
+
if p != "fluid":
|
626
|
+
if p != "water":
|
627
|
+
melts.engine.setSystemProperties("Suppress", p)
|
628
|
+
else:
|
629
|
+
Suppress = ['rutile', 'tridymite']
|
630
|
+
for p in Suppress:
|
631
|
+
melts.engine.setSystemProperties("Suppress", p)
|
632
|
+
|
633
|
+
for i in index:
|
634
|
+
melts = melts.addNodeAfter()
|
635
|
+
try:
|
636
|
+
if type(comp) == dict:
|
637
|
+
Results, tr = findSatPressure_MELTS(Model = Model, T_C_init=T_C_init[i], T_fixed_C=T_fixed_C[i],
|
638
|
+
P_bar_init=P_bar_init[i], comp = comp, fO2_buffer=fO2_buffer,
|
639
|
+
fO2_offset=fO2_offset[i], trial = True, melts = melts, suppressed = True)
|
640
|
+
else:
|
641
|
+
Results, tr = findSatPressure_MELTS(Model = Model, T_C_init=T_C_init[i], T_fixed_C=T_fixed_C[i],
|
642
|
+
P_bar_init=P_bar_init[i], comp = comp.loc[i].to_dict(), fO2_buffer=fO2_buffer,
|
643
|
+
fO2_offset=fO2_offset[i], trial = True, melts = melts, suppressed = True)
|
644
|
+
|
645
|
+
results[f"Run {i}"] = Results
|
646
|
+
idx.append(i)
|
647
|
+
if tr is False:
|
648
|
+
break
|
649
|
+
except:
|
650
|
+
idx.append(i)
|
651
|
+
break
|
652
|
+
|
653
|
+
q.put([idx, results])
|
654
|
+
|
655
|
+
def satP(q, index, *, Model = None, comp = None, T_C_init = None, T_fixed_C = None,
|
656
|
+
P_bar_init = None, fO2_buffer = None, fO2_offset = None):
|
307
657
|
"""Find the saturation pressure for a given composition and temperature.
|
308
658
|
|
309
659
|
This function calculates the volatile saturation pressure for a given composition using the MELTS models. The results are returned in the form of a tuple and added to the specified queue.
|
@@ -329,7 +679,8 @@ def satP(q, index, *, Model = None, comp = None, T_C_init = None, T_fixed_C = No
|
|
329
679
|
|
330
680
|
def satP_multi(q, index, *, Model = None, comp = None, fO2_buffer = None):
|
331
681
|
if "MELTS" in Model:
|
332
|
-
Results = findSatPressure_MELTS_multi(Model = Model, comp = comp, fO2_buffer=fO2_buffer,
|
682
|
+
Results = findSatPressure_MELTS_multi(Model = Model, comp = comp, fO2_buffer=fO2_buffer,
|
683
|
+
fO2_offset=comp['fO2_offset'],
|
333
684
|
P_bar = comp['P_bar'], T_fixed_C=comp['T_fixed_C'])
|
334
685
|
q.put([Results, index])
|
335
686
|
return
|
PetThermoTools/_version.py
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
PetThermoTools/Barom.py,sha256=2NLhuidKKG8ljaXdWtQfGUE7QgIVMjHyJZq0GUtsayI,45818
|
2
|
+
PetThermoTools/Compositions.py,sha256=65NzfduzWdfHJ8VmHBN1Cv7fMz7kF3QbDVLei-e4v00,1483
|
3
|
+
PetThermoTools/GenFuncs.py,sha256=KS2zgccvUWDU_A4e6tK7NQrBrHYHTysHiFGC8WdilW8,17824
|
4
|
+
PetThermoTools/Holland.py,sha256=udBFeVUyTBpSfLIhx7Hy6o0I8ApNCDvwU_gZa0diY5w,7251
|
5
|
+
PetThermoTools/Installation.py,sha256=UfVOW1NZFdzMWPyID5u7t0KwvpJA0AqYohzidXIAwYs,6098
|
6
|
+
PetThermoTools/Liq.py,sha256=BxAiHTeVQSLgpJT1Ac71apOQYmY3LGTPrxXclwE4F6w,35145
|
7
|
+
PetThermoTools/MELTS.py,sha256=MIW1QUF-W76xjS4nD4tmFXKzYdh7ghY4W05kXU3hoBc,76412
|
8
|
+
PetThermoTools/Melting.py,sha256=6EXjDJi5ZEZkQdoZbClRvnm3la_9c1tqurwyagcp5ts,12808
|
9
|
+
PetThermoTools/Path.py,sha256=gF8C_bGV_BkVyCvBYEVWN2A6TcfHAppiV4jAOmJilA8,35207
|
10
|
+
PetThermoTools/Path_wrappers.py,sha256=gUxs_4Qbk4MLlLl4iySxfbfKU34588bIJAYyhHmhFdc,30177
|
11
|
+
PetThermoTools/PhaseDiagrams.py,sha256=7tagrboKpln3fTXOZVX1ouxhzhV2-cmWGo9Qtmtsfn4,29285
|
12
|
+
PetThermoTools/Plotting.py,sha256=uvL_j2emMveGumLQ-IeJqyMXGUQT_PyInOpGnsWziAI,28992
|
13
|
+
PetThermoTools/Saturation.py,sha256=m014Wtcd6pT8qmQhUoQQczZEB6dX3iIdxsCGtxmuOh0,29806
|
14
|
+
PetThermoTools/__init__.py,sha256=PbiwQj_mNNEwuIZOLETmtMMshiXa50wjCA6mfvpOpOs,2393
|
15
|
+
PetThermoTools/_version.py,sha256=_VYZ5bFE97hEGZ_S_lc6cSL5MwEE0cohtbh7q3t2VcM,296
|
16
|
+
PetThermoTools-0.2.40.dist-info/LICENSE.txt,sha256=-mkx4iEw8Pk1RZUvncBhGLW87Uur5JB7FBQtOmX-VP0,1752
|
17
|
+
PetThermoTools-0.2.40.dist-info/METADATA,sha256=LEddDdRVihjM4jtAPOkPKt6Qm5jYAWtjKOM8u0ZdFm4,796
|
18
|
+
PetThermoTools-0.2.40.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
19
|
+
PetThermoTools-0.2.40.dist-info/top_level.txt,sha256=IqK8iYBR3YJozzMOTRZ8x8mU2k6x8ycoMBxZTm-I06U,15
|
20
|
+
PetThermoTools-0.2.40.dist-info/RECORD,,
|
@@ -1,20 +0,0 @@
|
|
1
|
-
PetThermoTools/Barom.py,sha256=CXjwCb7KpQG8x7IhewCLTfqoJ02s4Ott3NwDcGQMWtQ,40221
|
2
|
-
PetThermoTools/Compositions.py,sha256=65NzfduzWdfHJ8VmHBN1Cv7fMz7kF3QbDVLei-e4v00,1483
|
3
|
-
PetThermoTools/GenFuncs.py,sha256=_hhy4Myen18rMa5AVagb0wvwdXVZ5BtF2bRW_vnM3tY,17681
|
4
|
-
PetThermoTools/Holland.py,sha256=udBFeVUyTBpSfLIhx7Hy6o0I8ApNCDvwU_gZa0diY5w,7251
|
5
|
-
PetThermoTools/Installation.py,sha256=UfVOW1NZFdzMWPyID5u7t0KwvpJA0AqYohzidXIAwYs,6098
|
6
|
-
PetThermoTools/Liq.py,sha256=2kdU8r00Y20khwvPvCvLr6P7y2HEOvCH2cwrF_qw5u8,35541
|
7
|
-
PetThermoTools/MELTS.py,sha256=DeTM8j2fJ5dAw_2DVCGfgh1R_lQB4Z5rLopKSJ1Q5xQ,73852
|
8
|
-
PetThermoTools/Melting.py,sha256=6EXjDJi5ZEZkQdoZbClRvnm3la_9c1tqurwyagcp5ts,12808
|
9
|
-
PetThermoTools/Path.py,sha256=ey1nKnmdtriQ_0W6nG5Hbqhhk4EYqY64y1bMwhoXTW0,33050
|
10
|
-
PetThermoTools/Path_wrappers.py,sha256=_0pBs_cK2hICxAHkYxKXICUnUEBSiUg07-qhgBeuTdc,26555
|
11
|
-
PetThermoTools/PhaseDiagrams.py,sha256=8S_BcqggBzfUbiCPcsJRWFBenGL4tcCevte3-ATQjQI,30884
|
12
|
-
PetThermoTools/Plotting.py,sha256=uvL_j2emMveGumLQ-IeJqyMXGUQT_PyInOpGnsWziAI,28992
|
13
|
-
PetThermoTools/Saturation.py,sha256=XXY6fKVouQM3RLgQgXur4xSq7_uGp7bCw_k7NNlWYi8,14095
|
14
|
-
PetThermoTools/__init__.py,sha256=PbiwQj_mNNEwuIZOLETmtMMshiXa50wjCA6mfvpOpOs,2393
|
15
|
-
PetThermoTools/_version.py,sha256=mHkADyHdAemaOsQ81BakYanLTpx-qI1XT0aama1lkCw,296
|
16
|
-
PetThermoTools-0.2.39.dist-info/LICENSE.txt,sha256=-mkx4iEw8Pk1RZUvncBhGLW87Uur5JB7FBQtOmX-VP0,1752
|
17
|
-
PetThermoTools-0.2.39.dist-info/METADATA,sha256=EaGuwfuigrt061ZQbwUN0JEYA0jg-D7YIQd-pXPwQe0,796
|
18
|
-
PetThermoTools-0.2.39.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
19
|
-
PetThermoTools-0.2.39.dist-info/top_level.txt,sha256=IqK8iYBR3YJozzMOTRZ8x8mU2k6x8ycoMBxZTm-I06U,15
|
20
|
-
PetThermoTools-0.2.39.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|