PetThermoTools 0.2.38__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/Melting.py +55 -18
- PetThermoTools/Path.py +181 -121
- PetThermoTools/Path_wrappers.py +448 -259
- PetThermoTools/PhaseDiagrams.py +30 -98
- PetThermoTools/Plotting.py +5 -4
- PetThermoTools/Saturation.py +353 -2
- PetThermoTools/_version.py +1 -1
- {PetThermoTools-0.2.38.dist-info → PetThermoTools-0.2.40.dist-info}/METADATA +1 -1
- PetThermoTools-0.2.40.dist-info/RECORD +20 -0
- PetThermoTools-0.2.38.dist-info/RECORD +0 -20
- {PetThermoTools-0.2.38.dist-info → PetThermoTools-0.2.40.dist-info}/LICENSE.txt +0 -0
- {PetThermoTools-0.2.38.dist-info → PetThermoTools-0.2.40.dist-info}/WHEEL +0 -0
- {PetThermoTools-0.2.38.dist-info → PetThermoTools-0.2.40.dist-info}/top_level.txt +0 -0
PetThermoTools/Barom.py
CHANGED
@@ -25,6 +25,42 @@ import time
|
|
25
25
|
|
26
26
|
def path_4_saturation_multi(q, index, *, Model = None, P_bar = None, comp = None, T_maxdrop_C = None, dt_C = None, T_initial_C = None, fO2_buffer = None,
|
27
27
|
fO2_offset = 0.0, H2O_Sat = None, phases = None):
|
28
|
+
"""
|
29
|
+
Run crystallization simulations at multiple pressures to determine pressure of mineral co-saturations using MELTS or MAGEMinCalc in parallel.
|
30
|
+
|
31
|
+
Parameters
|
32
|
+
----------
|
33
|
+
q : multiprocessing.Queue
|
34
|
+
Queue object for inter-process communication (used to return results).
|
35
|
+
index : array-like
|
36
|
+
Index values to iterate over P_bar.
|
37
|
+
Model : str
|
38
|
+
Thermodynamic model to use.
|
39
|
+
P_bar : array-like
|
40
|
+
Pressures in bar at which to simulate crystallization paths.
|
41
|
+
comp : dict
|
42
|
+
Bulk composition of the starting material.
|
43
|
+
T_maxdrop_C : float
|
44
|
+
Maximum temperature drop below the liquidus and/or starting temperature in Celsius.
|
45
|
+
dt_C : float
|
46
|
+
Step size for temperature change (in °C).
|
47
|
+
T_initial_C : float
|
48
|
+
Starting temperature (°C) for simulations.
|
49
|
+
fO2_buffer : str, optional
|
50
|
+
Oxygen fugacity buffer ("FMQ" or "NNO").
|
51
|
+
fO2_offset : float, default 0.0
|
52
|
+
Offset in log units from the specified fO2 buffer.
|
53
|
+
H2O_Sat : bool
|
54
|
+
Whether the system is fluid-saturated.
|
55
|
+
phases : list of str
|
56
|
+
List of phases to monitor for saturation.
|
57
|
+
|
58
|
+
Returns
|
59
|
+
-------
|
60
|
+
None
|
61
|
+
Results are put in the multiprocessing queue `q` as a list: [idx, results].
|
62
|
+
"""
|
63
|
+
|
28
64
|
results = {}
|
29
65
|
idx = []
|
30
66
|
|
@@ -83,7 +119,7 @@ def path_4_saturation_multi(q, index, *, Model = None, P_bar = None, comp = None
|
|
83
119
|
idx.append(i)
|
84
120
|
|
85
121
|
|
86
|
-
results[f"
|
122
|
+
results[f"Run {i}"] = Results
|
87
123
|
|
88
124
|
if tr is False:
|
89
125
|
break
|
@@ -98,8 +134,59 @@ def mineral_cosaturation(Model="MELTSv1.0.2", cores=int(np.floor(multiprocessing
|
|
98
134
|
P_bar=np.linspace(250, 5000, 32), Fe3Fet_init=None, H2O_init=None,
|
99
135
|
CO2_init=None, H2O_Sat=False, T_initial_C=None, dt_C=2,
|
100
136
|
T_maxdrop_C=50, T_cut_C=20, find_range=True,
|
101
|
-
find_min=True, fO2_buffer=None, fO2_offset=0.0, timeout = 90,
|
137
|
+
find_min=True, fO2_buffer=None, fO2_offset=0.0, timeout = 90, multi_processing = True):
|
138
|
+
"""
|
139
|
+
Determines the pressure at which two or more minerals co-saturate (following the method of Gualda et al. 2014).
|
102
140
|
|
141
|
+
Parameters
|
142
|
+
----------
|
143
|
+
Model : str
|
144
|
+
Thermodynamic model to use.
|
145
|
+
cores : int
|
146
|
+
Number of parallel processes to use.
|
147
|
+
bulk : dict
|
148
|
+
Bulk composition of the starting material.
|
149
|
+
phases : list of str
|
150
|
+
Mineral phases to track for saturation.
|
151
|
+
P_bar : array-like
|
152
|
+
Pressures to test in bars.
|
153
|
+
Fe3Fet_init : float, optional
|
154
|
+
Initial Fe³⁺/Fe_total ratio.
|
155
|
+
H2O_init : float, optional
|
156
|
+
Initial H₂O content in the system (wt%).
|
157
|
+
CO2_init : float, optional
|
158
|
+
Initial CO₂ content in the system (wt%).
|
159
|
+
H2O_Sat : bool
|
160
|
+
If True, run at H₂O saturation.
|
161
|
+
T_initial_C : float, optional
|
162
|
+
Starting temperature in °C.
|
163
|
+
dt_C : float
|
164
|
+
Temperature step size (°C).
|
165
|
+
T_maxdrop_C : float
|
166
|
+
Maximum temperature drop below the liquidus to search for satuartion (°C).
|
167
|
+
T_cut_C : float
|
168
|
+
Maximum acceptable difference in phase saturation temperatures for "co-saturation" (°C).
|
169
|
+
find_range : bool
|
170
|
+
If True, analyze phase co-saturation ranges.
|
171
|
+
find_min : bool
|
172
|
+
If True, determine pressure where phase saturation temperature difference is minimized.
|
173
|
+
fO2_buffer : str, optional
|
174
|
+
Oxygen fugacity buffer (e.g., "FMQ", "NNO").
|
175
|
+
fO2_offset : float, default 0.0
|
176
|
+
Offset from the fO2 buffer.
|
177
|
+
timeout : int
|
178
|
+
Timeout (in seconds) for each process.
|
179
|
+
multi_processing : bool
|
180
|
+
If True, run simulations in parallel using multiprocessing.
|
181
|
+
|
182
|
+
Returns
|
183
|
+
-------
|
184
|
+
out : dict
|
185
|
+
Contains either 'Output' (DataFrame of phase saturation conditions) or both 'Output' and 'CurveMin'.
|
186
|
+
Results : dict
|
187
|
+
Raw simulation outputs for each pressure step.
|
188
|
+
"""
|
189
|
+
|
103
190
|
comp = bulk.copy()
|
104
191
|
if H2O_Sat:
|
105
192
|
comp['H2O_Liq'] = 20
|
@@ -108,7 +195,7 @@ def mineral_cosaturation(Model="MELTSv1.0.2", cores=int(np.floor(multiprocessing
|
|
108
195
|
H2O_Liq=H2O_init, CO2_Liq=CO2_init)
|
109
196
|
|
110
197
|
combined_results = {}
|
111
|
-
if
|
198
|
+
if multi_processing:
|
112
199
|
index_in = np.arange(len(P_bar))
|
113
200
|
index_out = np.array([], dtype=int)
|
114
201
|
|
@@ -254,6 +341,28 @@ def mineral_cosaturation(Model="MELTSv1.0.2", cores=int(np.floor(multiprocessing
|
|
254
341
|
return out, Results
|
255
342
|
|
256
343
|
def findmin(out = None, P_bar = None, T_cut_C = None):
|
344
|
+
"""
|
345
|
+
Finds the minimum temperature offset between mineral saturation temperatures as a function of pressure.
|
346
|
+
|
347
|
+
Parameters
|
348
|
+
----------
|
349
|
+
out : pd.DataFrame
|
350
|
+
DataFrame containing phase saturation temperatures.
|
351
|
+
P_bar : array-like
|
352
|
+
Pressure values (bar) corresponding to the rows in `out`.
|
353
|
+
T_cut_C : float
|
354
|
+
Maximum acceptable temperature difference (°C) for co-saturation to be considered valid.
|
355
|
+
|
356
|
+
Returns
|
357
|
+
-------
|
358
|
+
CurveMin : dict
|
359
|
+
Dictionary containing:
|
360
|
+
- 'P_min': Pressure of minimum saturation temperature difference.
|
361
|
+
- 'Res_min': Minimum temperature difference.
|
362
|
+
- 'y_new': Interpolated curve of temperature differences.
|
363
|
+
- 'P_new': Pressure array for interpolated curve.
|
364
|
+
- 'test': 'Pass' or 'Fail' depending on whether the result meets `T_cut_C`.
|
365
|
+
"""
|
257
366
|
Res = out.copy()
|
258
367
|
|
259
368
|
if '3 Phase Saturation' in list(Res.keys()):
|
@@ -337,606 +446,612 @@ def findmin(out = None, P_bar = None, T_cut_C = None):
|
|
337
446
|
|
338
447
|
return CurveMin
|
339
448
|
|
340
|
-
|
341
449
|
def find_mineral_cosaturation(cores = None, Model = None, bulk = None, phases = None, P_bar = None, Fe3Fet_Liq = None,
|
342
450
|
H2O_Liq = None, H2O_Sat = False, T_initial_C = None, dt_C = None, T_maxdrop_C = None,
|
343
451
|
T_cut_C = None, find_range = None, find_min = None, fO2_buffer = None, fO2_offset = None):
|
344
|
-
|
345
|
-
Carry out multiple calculations in parallel. Allows isobaric, polybaric and isochoric crystallisation to be performed as well as isothermal, isenthalpic or isentropic decompression. All temperature inputs/outputs are reported in degrees celcius and pressure is reported in bars.
|
346
|
-
|
347
|
-
Parameters:
|
348
|
-
----------
|
349
|
-
cores: int
|
350
|
-
number of processes to run in parallel. Default will be determined using Multiprocessing.cpu_count().
|
351
|
-
|
352
|
-
Model: string
|
353
|
-
"MELTS" or "Holland". Dictates whether MELTS or MAGEMin calculations are performed. Default "MELTS".
|
354
|
-
Version of melts can be specified "MELTSv1.0.2", "MELTSv1.1.0", "MELTSv1.2.0", or "pMELTS". Default "v.1.0.2".
|
355
|
-
|
356
|
-
bulk: Dict or pd.DataFrame
|
357
|
-
Initial compositon for calculations. If type == Dict, the same initial composition will be used in all calculations.
|
358
|
-
|
359
|
-
phases: list
|
360
|
-
length 2 or 3, contains the phases of the co-saturated magma. Default = ['quartz1', 'plagioclase1', 'k-feldspar1'].
|
361
|
-
|
362
|
-
P_bar: np.ndarray
|
363
|
-
Calculation pressure. Length determines the number of calculations to be performed.
|
364
|
-
|
365
|
-
Fe3Fet_Liq: float or np.ndarray
|
366
|
-
Initial Fe 3+/total ratio.
|
367
|
-
|
368
|
-
H2O_Liq: float
|
369
|
-
H2O content of the initial melt phase.
|
370
|
-
|
371
|
-
T_initial_C: float
|
372
|
-
Starting temperature for the liquidus calculations. Default = 1200
|
373
|
-
|
374
|
-
T_maxdrop_C: float
|
375
|
-
Max temperature drop of the calculations. Default = 25
|
376
|
-
|
377
|
-
dt_C: float
|
378
|
-
Temperature change at each model step. Default = 1
|
379
|
-
|
380
|
-
T_cut_C: float
|
381
|
-
Temperature offset used to indicate whether the model has succeeded or failed in finding a match. Default = 10.
|
382
|
-
|
383
|
-
find_range: True/False
|
384
|
-
If True a new DataFrame will be included in Results, indicating all cases where the minimum offset is less than T_cut_C.
|
385
|
-
|
386
|
-
find_min: True/False
|
387
|
-
If True, a spline fit will be applied to the data to find the minimum point.
|
388
|
-
|
389
|
-
fO2_buffer: string
|
390
|
-
If the oxygen fugacity of the system is to be buffered during crystallisation/decompression, then an offset to a known buffer must be specified. Here the user can define the known buffer as either "FMQ" or "NNO".
|
391
|
-
|
392
|
-
fO2_offset: float
|
393
|
-
Offset from the buffer spcified in fO2_buffer (log units).
|
394
|
-
|
395
|
-
Returns:
|
396
|
-
----------
|
397
|
-
Results: Dict
|
398
|
-
Dictionary containing information regarding the saturation temperature of each phase and the residuals between the different phases
|
399
|
-
'''
|
400
|
-
print('This function will be removed following the update to v0.3.0. Please switch to using the mineral_cosaturation() function')
|
401
|
-
|
402
|
-
try:
|
403
|
-
from meltsdynamic import MELTSdynamic
|
404
|
-
except:
|
405
|
-
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')
|
406
|
-
|
407
|
-
T_step_C = dt_C
|
408
|
-
dt_C = T_maxdrop_C
|
409
|
-
|
410
|
-
comp = bulk.copy()
|
411
|
-
if H2O_Sat is True:
|
412
|
-
comp['H2O_Liq'] = 20
|
452
|
+
print("This function has been removed following update to v0.2.40. Please switch to using the mineral_cosaturation() function")
|
413
453
|
|
414
|
-
|
415
|
-
if Model is None:
|
416
|
-
Model == "MELTSv1.0.2"
|
454
|
+
return "This function has been removed following update to v0.2.40. Please switch to using the mineral_cosaturation() function"
|
417
455
|
|
418
|
-
|
419
|
-
|
456
|
+
# def find_mineral_cosaturation(cores = None, Model = None, bulk = None, phases = None, P_bar = None, Fe3Fet_Liq = None,
|
457
|
+
# H2O_Liq = None, H2O_Sat = False, T_initial_C = None, dt_C = None, T_maxdrop_C = None,
|
458
|
+
# T_cut_C = None, find_range = None, find_min = None, fO2_buffer = None, fO2_offset = None):
|
459
|
+
# '''
|
460
|
+
# Carry out multiple calculations in parallel. Allows isobaric, polybaric and isochoric crystallisation to be performed as well as isothermal, isenthalpic or isentropic decompression. All temperature inputs/outputs are reported in degrees celcius and pressure is reported in bars.
|
420
461
|
|
421
|
-
|
422
|
-
|
423
|
-
|
462
|
+
# Parameters:
|
463
|
+
# ----------
|
464
|
+
# cores: int
|
465
|
+
# number of processes to run in parallel. Default will be determined using Multiprocessing.cpu_count().
|
424
466
|
|
425
|
-
|
426
|
-
|
467
|
+
# Model: string
|
468
|
+
# "MELTS" or "Holland". Dictates whether MELTS or MAGEMin calculations are performed. Default "MELTS".
|
469
|
+
# Version of melts can be specified "MELTSv1.0.2", "MELTSv1.1.0", "MELTSv1.2.0", or "pMELTS". Default "v.1.0.2".
|
427
470
|
|
428
|
-
|
429
|
-
|
430
|
-
P_bar = np.array([-1])
|
431
|
-
elif type(P_bar) != np.ndarray:
|
432
|
-
P_bar = np.array([P_bar])
|
471
|
+
# bulk: Dict or pd.DataFrame
|
472
|
+
# Initial compositon for calculations. If type == Dict, the same initial composition will be used in all calculations.
|
433
473
|
|
434
|
-
|
435
|
-
|
436
|
-
elif type(Fe3Fet_Liq) != np.ndarray:
|
437
|
-
Fe3Fet_Liq = np.array([Fe3Fet_Liq])
|
474
|
+
# phases: list
|
475
|
+
# length 2 or 3, contains the phases of the co-saturated magma. Default = ['quartz1', 'plagioclase1', 'k-feldspar1'].
|
438
476
|
|
439
|
-
|
440
|
-
|
441
|
-
elif type(H2O_Liq) != np.ndarray:
|
442
|
-
H2O_Liq = np.array([H2O_Liq])
|
477
|
+
# P_bar: np.ndarray
|
478
|
+
# Calculation pressure. Length determines the number of calculations to be performed.
|
443
479
|
|
444
|
-
|
480
|
+
# Fe3Fet_Liq: float or np.ndarray
|
481
|
+
# Initial Fe 3+/total ratio.
|
445
482
|
|
446
|
-
|
447
|
-
|
448
|
-
if Fe3Fet_Liq[0] == -1:
|
449
|
-
Fe3Fet_Liq = None
|
450
|
-
if H2O_Liq[0] == -1:
|
451
|
-
H2O_Liq = None
|
483
|
+
# H2O_Liq: float
|
484
|
+
# H2O content of the initial melt phase.
|
452
485
|
|
453
|
-
|
454
|
-
|
455
|
-
T_initial_C = 1200
|
486
|
+
# T_initial_C: float
|
487
|
+
# Starting temperature for the liquidus calculations. Default = 1200
|
456
488
|
|
457
|
-
|
458
|
-
|
489
|
+
# T_maxdrop_C: float
|
490
|
+
# Max temperature drop of the calculations. Default = 25
|
459
491
|
|
460
|
-
|
461
|
-
|
492
|
+
# dt_C: float
|
493
|
+
# Temperature change at each model step. Default = 1
|
462
494
|
|
463
|
-
|
464
|
-
|
495
|
+
# T_cut_C: float
|
496
|
+
# Temperature offset used to indicate whether the model has succeeded or failed in finding a match. Default = 10.
|
465
497
|
|
466
|
-
|
467
|
-
|
498
|
+
# find_range: True/False
|
499
|
+
# If True a new DataFrame will be included in Results, indicating all cases where the minimum offset is less than T_cut_C.
|
468
500
|
|
469
|
-
|
470
|
-
|
471
|
-
if len(phases) == 3:
|
472
|
-
List = [phases[0], phases[1], phases[2], 'T_Liq', 'H2O_melt', '3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]]
|
473
|
-
for l in List:
|
474
|
-
Results[l] = base_array.copy()
|
475
|
-
else:
|
476
|
-
List = [phases[0], phases[1], 'T_Liq', 'H2O_melt', phases[0] + ' - ' + phases[1]]
|
477
|
-
for l in List:
|
478
|
-
Results[l] = base_array.copy()
|
479
|
-
|
480
|
-
# run calculations if only one initial composition provided
|
481
|
-
if type(comp) == dict:
|
482
|
-
for i in range(np.shape(base_array)[0]):
|
483
|
-
# if H2O specified set H2O on each iteration
|
484
|
-
if H2O_Liq is not None:
|
485
|
-
comp['H2O_Liq'] = H2O_Liq[i]
|
486
|
-
|
487
|
-
for j in range(np.shape(base_array)[1]):
|
488
|
-
#if Fe3Fet specified set Fe3Fet on each iteration
|
489
|
-
if Fe3Fet_Liq is not None:
|
490
|
-
comp['Fe3Fet_Liq'] = Fe3Fet_Liq[j]
|
491
|
-
|
492
|
-
# determine how many processes to run in parallel
|
493
|
-
if len(P_bar) > 1:
|
494
|
-
A = len(P_bar)//cores
|
495
|
-
B = len(P_bar) % cores
|
496
|
-
|
497
|
-
if A > 0:
|
498
|
-
Group = np.zeros(A) + cores
|
499
|
-
if B > 0:
|
500
|
-
Group = np.append(Group, B)
|
501
|
-
else:
|
502
|
-
Group = np.array([B])
|
501
|
+
# find_min: True/False
|
502
|
+
# If True, a spline fit will be applied to the data to find the minimum point.
|
503
503
|
|
504
|
-
|
505
|
-
|
506
|
-
q = Queue()
|
504
|
+
# fO2_buffer: string
|
505
|
+
# If the oxygen fugacity of the system is to be buffered during crystallisation/decompression, then an offset to a known buffer must be specified. Here the user can define the known buffer as either "FMQ" or "NNO".
|
507
506
|
|
507
|
+
# fO2_offset: float
|
508
|
+
# Offset from the buffer spcified in fO2_buffer (log units).
|
508
509
|
|
509
|
-
|
510
|
-
|
511
|
-
|
510
|
+
# Returns:
|
511
|
+
# ----------
|
512
|
+
# Results: Dict
|
513
|
+
# Dictionary containing information regarding the saturation temperature of each phase and the residuals between the different phases
|
514
|
+
# '''
|
515
|
+
# print('This function will be removed following the update to v0.3.0. Please switch to using the mineral_cosaturation() function')
|
516
|
+
|
517
|
+
# try:
|
518
|
+
# from meltsdynamic import MELTSdynamic
|
519
|
+
# except:
|
520
|
+
# 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')
|
521
|
+
|
522
|
+
# T_step_C = dt_C
|
523
|
+
# dt_C = T_maxdrop_C
|
512
524
|
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
'T_initial_C': T_initial_C, 'T_step_C': T_step_C,
|
517
|
-
'dt_C': dt_C, 'P_bar': P_bar[kk], 'phases': phases,
|
518
|
-
'H2O_Liq': H2O_Liq, 'fO2_buffer': fO2_buffer, 'fO2_offset': fO2_offset})
|
525
|
+
# comp = bulk.copy()
|
526
|
+
# if H2O_Sat is True:
|
527
|
+
# comp['H2O_Liq'] = 20
|
519
528
|
|
520
|
-
|
521
|
-
|
529
|
+
# # set default values if required
|
530
|
+
# if Model is None:
|
531
|
+
# Model == "MELTSv1.0.2"
|
532
|
+
|
533
|
+
# if cores is None:
|
534
|
+
# cores = multiprocessing.cpu_count()
|
535
|
+
|
536
|
+
# # if comp is entered as a pandas series, it must first be converted to a dict
|
537
|
+
# if type(comp) == pd.core.series.Series:
|
538
|
+
# comp = comp.to_dict()
|
522
539
|
|
523
|
-
|
540
|
+
# # ensure the bulk composition has the correct headers etc.
|
541
|
+
# comp = comp_fix(Model = Model, comp = comp)
|
524
542
|
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
543
|
+
# # create base array to be filled
|
544
|
+
# if P_bar is None:
|
545
|
+
# P_bar = np.array([-1])
|
546
|
+
# elif type(P_bar) != np.ndarray:
|
547
|
+
# P_bar = np.array([P_bar])
|
548
|
+
|
549
|
+
# if Fe3Fet_Liq is None:
|
550
|
+
# Fe3Fet_Liq = np.array([-1])
|
551
|
+
# elif type(Fe3Fet_Liq) != np.ndarray:
|
552
|
+
# Fe3Fet_Liq = np.array([Fe3Fet_Liq])
|
553
|
+
|
554
|
+
# if H2O_Liq is None:
|
555
|
+
# H2O_Liq = np.array([-1])
|
556
|
+
# elif type(H2O_Liq) != np.ndarray:
|
557
|
+
# H2O_Liq = np.array([H2O_Liq])
|
558
|
+
|
559
|
+
# base_array = np.zeros((len(H2O_Liq),len(Fe3Fet_Liq),len(P_bar)))
|
560
|
+
|
561
|
+
# if P_bar[0] == -1:
|
562
|
+
# P_bar = 1000
|
563
|
+
# if Fe3Fet_Liq[0] == -1:
|
564
|
+
# Fe3Fet_Liq = None
|
565
|
+
# if H2O_Liq[0] == -1:
|
566
|
+
# H2O_Liq = None
|
567
|
+
|
568
|
+
# # set default values for remaining parameters
|
569
|
+
# if T_initial_C is None:
|
570
|
+
# T_initial_C = 1200
|
571
|
+
|
572
|
+
# if dt_C is None:
|
573
|
+
# dt_C = 25
|
574
|
+
|
575
|
+
# if T_step_C is None:
|
576
|
+
# T_step_C = 1
|
577
|
+
|
578
|
+
# if T_cut_C is None:
|
579
|
+
# T_cut_C = 10
|
580
|
+
|
581
|
+
# if phases is None:
|
582
|
+
# phases = ['quartz1', 'alkali-feldspar1', 'plagioclase1']
|
583
|
+
|
584
|
+
# # create main output dictionary
|
585
|
+
# Results = {}
|
586
|
+
# if len(phases) == 3:
|
587
|
+
# List = [phases[0], phases[1], phases[2], 'T_Liq', 'H2O_melt', '3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]]
|
588
|
+
# for l in List:
|
589
|
+
# Results[l] = base_array.copy()
|
590
|
+
# else:
|
591
|
+
# List = [phases[0], phases[1], 'T_Liq', 'H2O_melt', phases[0] + ' - ' + phases[1]]
|
592
|
+
# for l in List:
|
593
|
+
# Results[l] = base_array.copy()
|
594
|
+
|
595
|
+
# # run calculations if only one initial composition provided
|
596
|
+
# if type(comp) == dict:
|
597
|
+
# for i in range(np.shape(base_array)[0]):
|
598
|
+
# # if H2O specified set H2O on each iteration
|
599
|
+
# if H2O_Liq is not None:
|
600
|
+
# comp['H2O_Liq'] = H2O_Liq[i]
|
601
|
+
|
602
|
+
# for j in range(np.shape(base_array)[1]):
|
603
|
+
# #if Fe3Fet specified set Fe3Fet on each iteration
|
604
|
+
# if Fe3Fet_Liq is not None:
|
605
|
+
# comp['Fe3Fet_Liq'] = Fe3Fet_Liq[j]
|
606
|
+
|
607
|
+
# # determine how many processes to run in parallel
|
608
|
+
# if len(P_bar) > 1:
|
609
|
+
# A = len(P_bar)//cores
|
610
|
+
# B = len(P_bar) % cores
|
611
|
+
|
612
|
+
# if A > 0:
|
613
|
+
# Group = np.zeros(A) + cores
|
614
|
+
# if B > 0:
|
615
|
+
# Group = np.append(Group, B)
|
616
|
+
# else:
|
617
|
+
# Group = np.array([B])
|
618
|
+
|
619
|
+
# # initialise queue
|
620
|
+
# qs = []
|
621
|
+
# q = Queue()
|
622
|
+
|
623
|
+
|
624
|
+
# # run calculations
|
625
|
+
# for k in range(len(Group)):
|
626
|
+
# ps = []
|
627
|
+
|
628
|
+
# for kk in range(int(cores*k), int(cores*k + Group[k])):
|
629
|
+
# p = Process(target = satTemperature, args = (q, kk),
|
630
|
+
# kwargs = {'Model': Model, 'comp': comp,
|
631
|
+
# 'T_initial_C': T_initial_C, 'T_step_C': T_step_C,
|
632
|
+
# 'dt_C': dt_C, 'P_bar': P_bar[kk], 'phases': phases,
|
633
|
+
# 'H2O_Liq': H2O_Liq, 'fO2_buffer': fO2_buffer, 'fO2_offset': fO2_offset})
|
634
|
+
|
635
|
+
# ps.append(p)
|
636
|
+
# p.start()
|
637
|
+
|
638
|
+
# TIMEOUT = 300
|
639
|
+
|
640
|
+
# start = time.time()
|
641
|
+
# for p in ps:
|
642
|
+
# if time.time() - start < TIMEOUT - 10:
|
643
|
+
# try:
|
644
|
+
# ret = q.get(timeout = TIMEOUT - (time.time()-start) + 10)
|
645
|
+
# except:
|
646
|
+
# ret = []
|
647
|
+
# else:
|
648
|
+
# try:
|
649
|
+
# ret = q.get(timeout = 10)
|
650
|
+
# except:
|
651
|
+
# ret = []
|
652
|
+
|
653
|
+
# qs.append(ret)
|
654
|
+
|
655
|
+
# TIMEOUT = 5
|
656
|
+
# start = time.time()
|
657
|
+
# for p in ps:
|
658
|
+
# if p.is_alive():
|
659
|
+
# while time.time() - start <= TIMEOUT:
|
660
|
+
# if not p.is_alive():
|
661
|
+
# p.join()
|
662
|
+
# p.terminate()
|
663
|
+
# break
|
664
|
+
# time.sleep(.1)
|
665
|
+
# else:
|
666
|
+
# p.terminate()
|
667
|
+
# p.join(5)
|
668
|
+
# else:
|
669
|
+
# p.join()
|
670
|
+
# p.terminate()
|
671
|
+
|
672
|
+
|
673
|
+
# # for p in ps:
|
674
|
+
# # try:
|
675
|
+
# # ret = q.get(timeout = 180)
|
676
|
+
# # except:
|
677
|
+
# # ret = []
|
678
|
+
# #
|
679
|
+
# # qs.append(ret)
|
680
|
+
# #
|
681
|
+
# # TIMEOUT = 20
|
682
|
+
# # start = time.time()
|
683
|
+
# # for p in ps:
|
684
|
+
# # if p.is_alive():
|
685
|
+
# # time.sleep(.1)
|
686
|
+
# # while time.time() - start <= TIMEOUT:
|
687
|
+
# # if not p.is_alive():
|
688
|
+
# # p.join()
|
689
|
+
# # p.terminate()
|
690
|
+
# # break
|
691
|
+
# # time.sleep(.1)
|
692
|
+
# # else:
|
693
|
+
# # p.terminate()
|
694
|
+
# # p.join(5)
|
695
|
+
# # else:
|
696
|
+
# # p.join()
|
697
|
+
# # p.terminate()
|
698
|
+
|
699
|
+
# # # extract results
|
700
|
+
# for kk in range(len(qs)):
|
701
|
+
# if len(qs[kk]) > 0:
|
702
|
+
# Res, index = qs[kk]
|
703
|
+
# for l in Results:
|
704
|
+
# if l != 'sat_surface':
|
705
|
+
# Results[l][i,j,index] = Res[l]
|
706
|
+
|
707
|
+
# # covert any empty values to nan
|
708
|
+
# for l in Results:
|
709
|
+
# if l != 'sat_surface':
|
710
|
+
# Results[l][np.where(Results[l] == 0.0)] = np.nan
|
711
|
+
|
712
|
+
# if find_min is not None:
|
713
|
+
# if H2O_Liq is not None:
|
714
|
+
# Results = findMinimum(Results = Results, P_bar = P_bar, T_cut_C = T_cut_C, H2O_Liq = H2O_Liq, Fe3Fet_Liq = Fe3Fet_Liq, phases = phases)
|
715
|
+
# else:
|
716
|
+
# Results = findMinimum(Results = Results, P_bar = P_bar, T_cut_C = T_cut_C, H2O_Liq = H2O_Liq, Fe3Fet_Liq = Fe3Fet_Liq, phases = phases)
|
717
|
+
|
718
|
+
# if find_range is not None:
|
719
|
+
# Results['range'] = np.zeros(np.shape(Results[l]))
|
720
|
+
# if len(phases) == 3:
|
721
|
+
# Results['range'][np.where(Results['3 Phase Saturation'] <= T_cut_C)] = True
|
722
|
+
# Results['range'][np.where(Results['3 Phase Saturation'] > T_cut_C)] = False
|
723
|
+
# else:
|
724
|
+
# Results['range'][np.where(Results[phases[0] + ' - ' + phases[1]] <= T_cut_C)] = True
|
725
|
+
# Results['range'][np.where(Results[phases[0] + ' - ' + phases[1]] > T_cut_C)] = False
|
611
726
|
|
612
727
|
|
613
728
|
|
614
|
-
|
615
|
-
|
616
|
-
def findMinimum(Results = None, P_bar = None, T_cut_C = None, H2O_Liq = None, Fe3Fet_Liq = None, phases = None):
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
def polymin(P_bar = None, Res = None):
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
def satTemperature(q, index, *, Model = None, comp = None, phases = None, T_initial_C = None, T_step_C = None, dt_C = None, P_bar = None, H2O_Liq = None, fO2_buffer = None, fO2_offset = None):
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
729
|
+
# return Results
|
730
|
+
|
731
|
+
# def findMinimum(Results = None, P_bar = None, T_cut_C = None, H2O_Liq = None, Fe3Fet_Liq = None, phases = None):
|
732
|
+
# '''
|
733
|
+
# Take the results of SatPress and search for the minimum point using a spline fit.
|
734
|
+
# '''
|
735
|
+
# if T_cut_C is None:
|
736
|
+
# T_cut_C = 10
|
737
|
+
|
738
|
+
# if H2O_Liq is None and Fe3Fet_Liq is None:
|
739
|
+
# if '3 Phase Saturation' in list(Results.keys()):
|
740
|
+
# Minimum = {'3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]}
|
741
|
+
# CurveMin = {}
|
742
|
+
# for m in Minimum:
|
743
|
+
# if len(Results[m][0,0,:][~np.isnan(Results[m][0,0,:])]) > 2:
|
744
|
+
# y = Results[m][0,0,:][(np.where(~np.isnan(Results[m][0,0,:]))) and (np.where(Results[m][0,0,:] < T_cut_C))]
|
745
|
+
# x = P_bar[(np.where(~np.isnan(Results[m][0,0,:]))) and (np.where(Results[m][0,0,:] < T_cut_C))]
|
746
|
+
|
747
|
+
# try:
|
748
|
+
# y_new = interpolate.UnivariateSpline(x, y, k = 5)
|
749
|
+
# except:
|
750
|
+
# y_new = interpolate.UnivariateSpline(x, y, k = 2)
|
751
|
+
|
752
|
+
# P_new = np.linspace(P_bar[np.where(P_bar == np.nanmin(P_bar[(np.where(~np.isnan(Results[m][0,0,:]))) and (np.where(Results[m][0,0,:] < T_cut_C))]))], P_bar[np.where(P_bar == np.nanmax(P_bar[(np.where(~np.isnan(Results[m][0,0,:]))) and (np.where(Results[m][0,0,:] < T_cut_C))]))], 200)
|
753
|
+
|
754
|
+
# NewMin = np.nanmin(y_new(P_new))
|
755
|
+
# P_min = P_new[np.where(y_new(P_new) == NewMin)][0]
|
756
|
+
# if NewMin < T_cut_C:
|
757
|
+
# Test = 'Pass'
|
758
|
+
# else:
|
759
|
+
# Test = 'Fail'
|
760
|
+
|
761
|
+
# CurveMin[m] = {'P_min': P_min, 'Res_min': NewMin, 'y_new': y_new(P_new), 'P_new': P_new, 'test': Test}
|
762
|
+
# else:
|
763
|
+
# y_new = np.nan
|
764
|
+
# P_new = np.nan
|
765
|
+
# NewMin = np.nan
|
766
|
+
# P_min = np.nan
|
767
|
+
# Test = 'Fail'
|
768
|
+
# CurveMin[m] = {'P_min': P_min, 'Res_min': NewMin, 'y_new': y_new, 'P_new': P_new, 'test': Test}
|
769
|
+
|
770
|
+
# Results['CurveMin'] = CurveMin
|
771
|
+
# else:
|
772
|
+
# m = phases[0] + ' - ' + phases[1]
|
773
|
+
# if len(Results[m][0,0,:][~np.isnan(Results[m][0,0,:])]) > 2:
|
774
|
+
# y = Results[m][0,0,:][np.where(~np.isnan(Results[m][0,0,:]))]
|
775
|
+
# x = P_bar[np.where(~np.isnan(Results[m][0,0,:]))]
|
776
|
+
|
777
|
+
# try:
|
778
|
+
# y_new = interpolate.UnivariateSpline(x, y, k=5)
|
779
|
+
# except:
|
780
|
+
# y_new = interpolate.UnivariateSpline(x, y, k = 2)
|
781
|
+
|
782
|
+
# P_new = np.linspace(P_bar[np.where(P_bar == np.nanmin(P_bar[np.where(~np.isnan(Results[m][0,0,:]))]))], P_bar[np.where(P_bar == np.nanmax(P_bar[np.where(~np.isnan(Results[m][0,0,:]))]))], 200)
|
783
|
+
|
784
|
+
# NewMin = np.nanmin(y_new(P_new))
|
785
|
+
# P_min = P_new[np.where(y_new(P_new) == NewMin)][0]
|
786
|
+
# if NewMin < T_cut_C:
|
787
|
+
# Test = 'Pass'
|
788
|
+
# else:
|
789
|
+
# Test = 'Fail'
|
790
|
+
# else:
|
791
|
+
# y_new = np.nan
|
792
|
+
# P_new = np.nan
|
793
|
+
# NewMin = np.nan
|
794
|
+
# P_min = np.nan
|
795
|
+
# Test = 'Fail'
|
796
|
+
|
797
|
+
# Results['CurveMin'] = {phases[0] + ' - ' + phases[1]: {'P_min': P_min, 'Res_min': NewMin, 'y_new': y_new, 'P_new': P_new, 'test': Test}}
|
798
|
+
|
799
|
+
# if H2O_Liq is not None and Fe3Fet_Liq is None:
|
800
|
+
# if '3 Phase Saturation' in list(Results.keys()):
|
801
|
+
# X, Y = np.meshgrid(P_bar, H2O_Liq)
|
802
|
+
# Y = Results['H2O_melt'][:,0,:].copy()
|
803
|
+
|
804
|
+
# Minimum = {'3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]}
|
805
|
+
# CurveMin = {}
|
806
|
+
# for m in Minimum:
|
807
|
+
# if len(Results[m][:,0,:][~np.isnan(Results[m][:,0,:])]) > 4:
|
808
|
+
# Res = Results[m][:,0,:].copy()
|
809
|
+
# Res[np.where(Res > T_cut_C*2)] = np.nan
|
810
|
+
# for i in range(len(H2O_Liq)):
|
811
|
+
# Res[i, :][np.where(Results['H2O_melt'][i,0,:] < 0.99*np.nanmax(Results['H2O_melt'][i,0,:]))] = np.nan
|
812
|
+
|
813
|
+
# A = Res.copy()
|
814
|
+
# Res[np.where(Res > T_cut_C)] = np.nan
|
815
|
+
# X, Y = np.meshgrid(P_bar, H2O_Liq)
|
816
|
+
# Y = Results['H2O_melt'][:,0,:]
|
817
|
+
|
818
|
+
# try:
|
819
|
+
# z_new = interpolate.SmoothBivariateSpline(X[np.where(~np.isnan(A) & ~np.isnan(Y))], Y[np.where(~np.isnan(A) & ~np.isnan(Y))], A[np.where(~np.isnan(A) & ~np.isnan(Y))], kx = 3, ky = 3)
|
820
|
+
# except:
|
821
|
+
# z_new = interpolate.SmoothBivariateSpline(X[np.where(~np.isnan(A) & ~np.isnan(Y))], Y[np.where(~np.isnan(A) & ~np.isnan(Y))], A[np.where(~np.isnan(A) & ~np.isnan(Y))], kx = 2, ky = 2)
|
822
|
+
|
823
|
+
# H2O_new = np.linspace(Y[np.where(Res == np.nanmin(Res))] - (H2O_Liq[1]-H2O_Liq[0]),
|
824
|
+
# Y[np.where(Res == np.nanmin(Res))] + (H2O_Liq[1]-H2O_Liq[0]), 20)
|
825
|
+
# P_new = np.linspace(X[np.where(Res == np.nanmin(Res))] - (P_bar[1]-P_bar[0]),
|
826
|
+
# X[np.where(Res == np.nanmin(Res))] + (P_bar[1]-P_bar[0]), 20)
|
827
|
+
|
828
|
+
# X_new, Y_new = np.meshgrid(P_new, H2O_new)
|
829
|
+
# x = X[~np.isnan(A)].flatten()
|
830
|
+
# y = Y[~np.isnan(A)].flatten()
|
831
|
+
|
832
|
+
# MyPoly = MultiPoint(list(zip(x, y))).convex_hull
|
833
|
+
|
834
|
+
# points = list(zip(X_new.flatten(), Y_new.flatten()))
|
835
|
+
# Include = np.zeros(len(X_new.flatten()))
|
836
|
+
# for i in range(len(points)):
|
837
|
+
# p = Point(points[i])
|
838
|
+
# Include[i] = p.within(MyPoly)
|
839
|
+
|
840
|
+
# YayNay = Include.reshape(X_new.shape)
|
841
|
+
# x_new = X_new[np.where(YayNay == True)].flatten()
|
842
|
+
# y_new = Y_new[np.where(YayNay == True)].flatten()
|
843
|
+
# Res_min = np.nanmin(z_new(x_new, y_new, grid = False))
|
844
|
+
# P_min = x_new[np.where(z_new(x_new, y_new, grid = False) == Res_min)]
|
845
|
+
# H2O_min = y_new[np.where(z_new(x_new, y_new, grid = False) == Res_min)]
|
846
|
+
# if Res_min < T_cut_C:
|
847
|
+
# Test = 'Pass'
|
848
|
+
# else:
|
849
|
+
# Test = 'Fail'
|
850
|
+
|
851
|
+
# CurveMin[m] = {'Res_min': Res_min, 'P_min': P_min[0], 'H2O_min': H2O_min[0], 'z_new': z_new, 'test': Test}
|
852
|
+
# else:
|
853
|
+
# CurveMin[m] = {'Res_min': np.nan, 'P_min': np.nan, 'H2O_min': np.nan, 'z_new': np.nan, 'test': 'Fail'}
|
854
|
+
|
855
|
+
# Results['CurveMin'] = CurveMin
|
856
|
+
|
857
|
+
# if H2O_Liq is not None and Fe3Fet_Liq is not None:
|
858
|
+
# if '3 Phase Saturation' in list(Results.keys()):
|
859
|
+
# X, Y = np.meshgrid(P_bar, H2O_Liq)
|
860
|
+
# Y = Results['H2O_melt'][:,0,:].copy()
|
861
|
+
|
862
|
+
# Minimum = {'3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]}
|
863
|
+
# CurveMin = {}
|
864
|
+
# for m in Minimum:
|
865
|
+
# Res_min_save = 50
|
866
|
+
# for w in range(len(Fe3Fet_Liq)):
|
867
|
+
# if len(Results[m][:,w,:][~np.isnan(Results[m][:,w,:])]) > 4:
|
868
|
+
# Res = Results[m][:,w,:].copy()
|
869
|
+
# Res[np.where(Res > T_cut_C*2)] = np.nan
|
870
|
+
# for i in range(len(H2O_Liq)):
|
871
|
+
# Res[i, :][np.where(Results['H2O_melt'][i,w,:] < 0.99*np.nanmax(Results['H2O_melt'][i,w,:]))] = np.nan
|
872
|
+
|
873
|
+
# A = Res.copy()
|
874
|
+
# X, Y = np.meshgrid(P_bar, H2O_Liq)
|
875
|
+
# Y = Results['H2O_melt'][:,w,:].copy()
|
876
|
+
|
877
|
+
# try:
|
878
|
+
# z_new = interpolate.SmoothBivariateSpline(X[np.where(~np.isnan(A) & ~np.isnan(Y))], Y[np.where(~np.isnan(A) & ~np.isnan(Y))], A[np.where(~np.isnan(A) & ~np.isnan(Y))], kx = 3, ky = 3)
|
879
|
+
# except:
|
880
|
+
# z_new = interpolate.SmoothBivariateSpline(X[np.where(~np.isnan(A) & ~np.isnan(Y))], Y[np.where(~np.isnan(A) & ~np.isnan(Y))], A[np.where(~np.isnan(A) & ~np.isnan(Y))], kx = 2, ky = 2)
|
881
|
+
|
882
|
+
# H2O_new = np.linspace(Y[np.where(Res == np.nanmin(Res))] - (H2O_Liq[1]-H2O_Liq[0]),
|
883
|
+
# Y[np.where(Res == np.nanmin(Res))] + (H2O_Liq[1]-H2O_Liq[0]), 20)
|
884
|
+
# P_new = np.linspace(X[np.where(Res == np.nanmin(Res))] - (P_bar[1]-P_bar[0]),
|
885
|
+
# X[np.where(Res == np.nanmin(Res))] + (P_bar[1]-P_bar[0]), 20)
|
886
|
+
|
887
|
+
# X_new, Y_new = np.meshgrid(P_new, H2O_new)
|
888
|
+
# x = X[~np.isnan(A)].flatten()
|
889
|
+
# y = Y[~np.isnan(A)].flatten()
|
890
|
+
|
891
|
+
# MyPoly = MultiPoint(list(zip(x, y))).convex_hull
|
892
|
+
|
893
|
+
# points = list(zip(X_new.flatten(), Y_new.flatten()))
|
894
|
+
# Include = np.zeros(len(X_new.flatten()))
|
895
|
+
# for i in range(len(points)):
|
896
|
+
# p = Point(points[i])
|
897
|
+
# Include[i] = p.within(MyPoly)
|
898
|
+
|
899
|
+
# YayNay = Include.reshape(X_new.shape)
|
900
|
+
# x_new = X_new[np.where(YayNay == True)].flatten()
|
901
|
+
# y_new = Y_new[np.where(YayNay == True)].flatten()
|
902
|
+
# Res_min = np.nanmin(z_new(x_new, y_new, grid = False))
|
903
|
+
# P_min = x_new[np.where(z_new(x_new, y_new, grid = False) == Res_min)]
|
904
|
+
# H2O_min = y_new[np.where(z_new(x_new, y_new, grid = False) == Res_min)]
|
905
|
+
# if Res_min < T_cut_C:
|
906
|
+
# Test = 'Pass'
|
907
|
+
# else:
|
908
|
+
# Test = 'Fail'
|
909
|
+
|
910
|
+
# if Res_min < Res_min_save:
|
911
|
+
# Res_min_save = Res_min
|
912
|
+
# CurveMin[m] = {'Res_min': Res_min, 'P_min': P_min[0], 'H2O_min': H2O_min[0], 'z_new': z_new, 'Fe3Fet_Liq': Fe3Fet_Liq[w], 'test': Test}
|
913
|
+
|
914
|
+
# Results['CurveMin'] = CurveMin
|
915
|
+
|
916
|
+
|
917
|
+
# return Results
|
918
|
+
|
919
|
+
# def polymin(P_bar = None, Res = None):
|
920
|
+
# '''
|
921
|
+
# Finds the minimum residual temperature using a 2nd degree polynomial.
|
922
|
+
# '''
|
923
|
+
# arr = np.sort(Res)
|
924
|
+
# Ind = np.where(Res == arr[0])[0][0]
|
925
|
+
|
926
|
+
# if P_bar[Ind] == np.nanmax(P_bar):
|
927
|
+
# p = np.array([0,1,0])
|
928
|
+
# p_min = np.array([P_bar[Ind]])
|
929
|
+
# elif P_bar[Ind] == np.nanmin(P_bar):
|
930
|
+
# p = np.array([0,1,0])
|
931
|
+
# p_min = np.array([P_bar[Ind]])
|
932
|
+
# else:
|
933
|
+
# p = np.polyfit(P_bar[np.array([Ind-1, Ind, Ind+1])],Res[np.array([Ind-1, Ind, Ind+1])],2)
|
934
|
+
|
935
|
+
# x = np.linspace(np.nanmin(P_bar),np.nanmax(P_bar),501)
|
936
|
+
# y = p[0]*x**2 + p[1]*x + p[2]
|
937
|
+
|
938
|
+
# p_min = x[np.where(y == np.nanmin(y))]
|
939
|
+
|
940
|
+
# return p, p_min
|
941
|
+
|
942
|
+
# def satTemperature(q, index, *, Model = None, comp = None, phases = None, T_initial_C = None, T_step_C = None, dt_C = None, P_bar = None, H2O_Liq = None, fO2_buffer = None, fO2_offset = None):
|
943
|
+
# '''
|
944
|
+
# Crystallisation calculations to be performed in parallel. Calculations may be either isobaric or isochoric.
|
945
|
+
|
946
|
+
# Parameters:
|
947
|
+
# ----------
|
948
|
+
# q: Multiprocessing Queue instance
|
949
|
+
# Queue instance to record the output variables
|
950
|
+
|
951
|
+
# index: int
|
952
|
+
# index of the calculation in the master code (e.g., position within a for loop) to aid indexing results after calculations are complete.
|
953
|
+
|
954
|
+
# Model: string
|
955
|
+
# "MELTS" or "Holland". Dictates whether MELTS or MAGEMin calculations are performed. Default "MELTS".
|
956
|
+
# Version of melts can be specified "MELTSv1.0.2", "MELTSv1.1.0", "MELTSv1.2.0", or "pMELTS". Default "v.1.0.2".
|
957
|
+
|
958
|
+
# comp: Dict
|
959
|
+
# Initial compositon for calculations.
|
960
|
+
|
961
|
+
# T_initial_C: float
|
962
|
+
# Initial guess for the liquidus temperature.
|
963
|
+
|
964
|
+
# T_step_C: float
|
965
|
+
# The temperature drop at each step of the calculation.
|
966
|
+
|
967
|
+
# dt_C: float
|
968
|
+
# Total temperature drop allowed duringmodel runs.
|
969
|
+
|
970
|
+
# P_bar: float
|
971
|
+
# Specifies the pressure of calculation (bar).
|
972
|
+
|
973
|
+
# Returns:
|
974
|
+
# ----------
|
975
|
+
# Results: Dict
|
976
|
+
# Dict containing a series of floats that represent the saturation temperature and residual temperature for each calculation.
|
977
|
+
|
978
|
+
# index: int
|
979
|
+
# index of the calculation
|
980
|
+
|
981
|
+
# '''
|
982
|
+
|
983
|
+
# Results = {}
|
984
|
+
# if "MELTS" in Model:
|
985
|
+
|
986
|
+
# try:
|
987
|
+
# Results = phaseSat_MELTS(Model = Model, comp = comp, phases = phases, T_initial_C = T_initial_C, T_step_C = T_step_C, dt_C = dt_C, P_bar = P_bar, H2O_Liq = H2O_Liq, fO2_buffer = fO2_buffer, fO2_offset = fO2_offset)
|
988
|
+
# except:
|
989
|
+
# Results = {phases[0]: np.nan, phases[1]: np.nan, phases[2]: np.nan, 'T_Liq': np.nan, 'H2O_melt': np.nan}
|
990
|
+
# if len(phases) == 2:
|
991
|
+
# del Results[phases[2]]
|
992
|
+
|
993
|
+
# if len(phases) == 3:
|
994
|
+
# Res = ['3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]]
|
995
|
+
# for R in Res:
|
996
|
+
# Results[R] = np.nan
|
997
|
+
|
998
|
+
# if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]) and ~np.isnan(Results[phases[2]]):
|
999
|
+
# Results['3 Phase Saturation'] = np.nanmax(np.array([abs(Results[phases[0]] - Results[phases[1]]), abs(Results[phases[0]] - Results[phases[2]]), abs(Results[phases[1]] - Results[phases[2]])]))
|
1000
|
+
# if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]):
|
1001
|
+
# Results[phases[0] + ' - ' + phases[1]] = abs(Results[phases[0]] - Results[phases[1]])
|
1002
|
+
# if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[2]]):
|
1003
|
+
# Results[phases[0] + ' - ' + phases[2]] = abs(Results[phases[0]] - Results[phases[2]])
|
1004
|
+
# if ~np.isnan(Results[phases[1]]) and ~np.isnan(Results[phases[2]]):
|
1005
|
+
# Results[phases[1] + ' - ' + phases[2]] = abs(Results[phases[1]] - Results[phases[2]])
|
1006
|
+
# else:
|
1007
|
+
# Results[phases[0] + ' - ' + phases[1]] = np.nan
|
1008
|
+
# if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]):
|
1009
|
+
# Results[phases[0] + ' - ' + phases[1]] = abs(Results[phases[0]] - Results[phases[1]])
|
1010
|
+
|
1011
|
+
# q.put([Results, index])
|
1012
|
+
# return
|
1013
|
+
|
1014
|
+
# if Model == "Holland":
|
1015
|
+
# import pyMAGEMINcalc as MM
|
1016
|
+
# Results = {phases[0]: np.nan, phases[1]: np.nan, phases[2]: np.nan, 'T_Liq': np.nan, 'H2O_melt': np.nan}
|
1017
|
+
# if len(phases) == 2:
|
1018
|
+
# del Results[phases[2]]
|
1019
|
+
|
1020
|
+
# #try:
|
1021
|
+
# Result = MM.path(comp = comp, phases = phases, T_min_C = dt_C, dt_C = T_step_C, P_bar = P_bar, find_liquidus = True)
|
1022
|
+
# #Result = stich(Result, Model = Model)
|
1023
|
+
|
1024
|
+
# for i in range(len(phases)):
|
1025
|
+
# try:
|
1026
|
+
# Results[phases[i]] = Result['Conditions']['T_C'][Result[phases[i]+'_prop']['mass'] > 0.0].values[0]
|
1027
|
+
# print(Results[phases[i]])
|
1028
|
+
# except:
|
1029
|
+
# Results[phases[i]] = np.nan
|
1030
|
+
|
1031
|
+
# Results['T_Liq'] = Result['Conditions']['T_C'].values[0]
|
1032
|
+
# Results['H2O_melt'] = Result['liq']['H2O'].values[0]
|
1033
|
+
|
1034
|
+
# if len(phases) == 3:
|
1035
|
+
# Res = ['3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]]
|
1036
|
+
# for R in Res:
|
1037
|
+
# Results[R] = np.nan
|
1038
|
+
|
1039
|
+
# if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]) and ~np.isnan(Results[phases[2]]):
|
1040
|
+
# Results['3 Phase Saturation'] = np.nanmax(np.array([abs(Results[phases[0]] - Results[phases[1]]), abs(Results[phases[0]] - Results[phases[2]]), abs(Results[phases[1]] - Results[phases[2]])]))
|
1041
|
+
# if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]):
|
1042
|
+
# Results[phases[0] + ' - ' + phases[1]] = abs(Results[phases[0]] - Results[phases[1]])
|
1043
|
+
# if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[2]]):
|
1044
|
+
# Results[phases[0] + ' - ' + phases[2]] = abs(Results[phases[0]] - Results[phases[2]])
|
1045
|
+
# if ~np.isnan(Results[phases[1]]) and ~np.isnan(Results[phases[2]]):
|
1046
|
+
# Results[phases[1] + ' - ' + phases[2]] = abs(Results[phases[1]] - Results[phases[2]])
|
1047
|
+
# else:
|
1048
|
+
# Results[phases[0] + ' - ' + phases[1]] = np.nan
|
1049
|
+
# if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]):
|
1050
|
+
# Results[phases[0] + ' - ' + phases[1]] = abs(Results[phases[0]] - Results[phases[1]])
|
936
1051
|
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
1052
|
+
# q.put([Results, index])
|
1053
|
+
# #except:
|
1054
|
+
# # q.put([Results, index])
|
1055
|
+
# return
|
941
1056
|
|
942
1057
|
|