PetThermoTools 0.2.39__py3-none-any.whl → 0.2.41__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 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"index = {i}"] = Results
122
+ results[f"Run {i}"] = Results
87
123
 
88
124
  if tr is False:
89
125
  break
@@ -98,8 +134,70 @@ 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, multiprocesses = True):
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
+ ## make sure everything is a float
190
+ T_initial_C = to_float(T_initial_C)
191
+ T_maxdrop_C = to_float(T_maxdrop_C)
192
+ T_cut_C = to_float(T_cut_C)
193
+
194
+ P_bar = to_float(P_bar)
195
+
196
+ Fe3Fet_init= to_float(Fe3Fet_init)
197
+ H2O_init = to_float(H2O_init)
198
+ CO2_init = to_float(CO2_init)
199
+ fO2_offset = to_float(fO2_offset)
200
+
103
201
  comp = bulk.copy()
104
202
  if H2O_Sat:
105
203
  comp['H2O_Liq'] = 20
@@ -108,7 +206,7 @@ def mineral_cosaturation(Model="MELTSv1.0.2", cores=int(np.floor(multiprocessing
108
206
  H2O_Liq=H2O_init, CO2_Liq=CO2_init)
109
207
 
110
208
  combined_results = {}
111
- if multiprocesses:
209
+ if multi_processing:
112
210
  index_in = np.arange(len(P_bar))
113
211
  index_out = np.array([], dtype=int)
114
212
 
@@ -254,6 +352,28 @@ def mineral_cosaturation(Model="MELTSv1.0.2", cores=int(np.floor(multiprocessing
254
352
  return out, Results
255
353
 
256
354
  def findmin(out = None, P_bar = None, T_cut_C = None):
355
+ """
356
+ Finds the minimum temperature offset between mineral saturation temperatures as a function of pressure.
357
+
358
+ Parameters
359
+ ----------
360
+ out : pd.DataFrame
361
+ DataFrame containing phase saturation temperatures.
362
+ P_bar : array-like
363
+ Pressure values (bar) corresponding to the rows in `out`.
364
+ T_cut_C : float
365
+ Maximum acceptable temperature difference (°C) for co-saturation to be considered valid.
366
+
367
+ Returns
368
+ -------
369
+ CurveMin : dict
370
+ Dictionary containing:
371
+ - 'P_min': Pressure of minimum saturation temperature difference.
372
+ - 'Res_min': Minimum temperature difference.
373
+ - 'y_new': Interpolated curve of temperature differences.
374
+ - 'P_new': Pressure array for interpolated curve.
375
+ - 'test': 'Pass' or 'Fail' depending on whether the result meets `T_cut_C`.
376
+ """
257
377
  Res = out.copy()
258
378
 
259
379
  if '3 Phase Saturation' in list(Res.keys()):
@@ -337,606 +457,612 @@ def findmin(out = None, P_bar = None, T_cut_C = None):
337
457
 
338
458
  return CurveMin
339
459
 
340
-
341
460
  def find_mineral_cosaturation(cores = None, Model = None, bulk = None, phases = None, P_bar = None, Fe3Fet_Liq = None,
342
461
  H2O_Liq = None, H2O_Sat = False, T_initial_C = None, dt_C = None, T_maxdrop_C = None,
343
462
  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
463
+ print("This function has been removed following update to v0.2.40. Please switch to using the mineral_cosaturation() function")
413
464
 
414
- # set default values if required
415
- if Model is None:
416
- Model == "MELTSv1.0.2"
465
+ return "This function has been removed following update to v0.2.40. Please switch to using the mineral_cosaturation() function"
417
466
 
418
- if cores is None:
419
- cores = multiprocessing.cpu_count()
467
+ # def find_mineral_cosaturation(cores = None, Model = None, bulk = None, phases = None, P_bar = None, Fe3Fet_Liq = None,
468
+ # H2O_Liq = None, H2O_Sat = False, T_initial_C = None, dt_C = None, T_maxdrop_C = None,
469
+ # T_cut_C = None, find_range = None, find_min = None, fO2_buffer = None, fO2_offset = None):
470
+ # '''
471
+ # 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
472
 
421
- # if comp is entered as a pandas series, it must first be converted to a dict
422
- if type(comp) == pd.core.series.Series:
423
- comp = comp.to_dict()
473
+ # Parameters:
474
+ # ----------
475
+ # cores: int
476
+ # number of processes to run in parallel. Default will be determined using Multiprocessing.cpu_count().
424
477
 
425
- # ensure the bulk composition has the correct headers etc.
426
- comp = comp_fix(Model = Model, comp = comp)
478
+ # Model: string
479
+ # "MELTS" or "Holland". Dictates whether MELTS or MAGEMin calculations are performed. Default "MELTS".
480
+ # Version of melts can be specified "MELTSv1.0.2", "MELTSv1.1.0", "MELTSv1.2.0", or "pMELTS". Default "v.1.0.2".
427
481
 
428
- # create base array to be filled
429
- if P_bar is None:
430
- P_bar = np.array([-1])
431
- elif type(P_bar) != np.ndarray:
432
- P_bar = np.array([P_bar])
482
+ # bulk: Dict or pd.DataFrame
483
+ # Initial compositon for calculations. If type == Dict, the same initial composition will be used in all calculations.
433
484
 
434
- if Fe3Fet_Liq is None:
435
- Fe3Fet_Liq = np.array([-1])
436
- elif type(Fe3Fet_Liq) != np.ndarray:
437
- Fe3Fet_Liq = np.array([Fe3Fet_Liq])
485
+ # phases: list
486
+ # length 2 or 3, contains the phases of the co-saturated magma. Default = ['quartz1', 'plagioclase1', 'k-feldspar1'].
438
487
 
439
- if H2O_Liq is None:
440
- H2O_Liq = np.array([-1])
441
- elif type(H2O_Liq) != np.ndarray:
442
- H2O_Liq = np.array([H2O_Liq])
488
+ # P_bar: np.ndarray
489
+ # Calculation pressure. Length determines the number of calculations to be performed.
443
490
 
444
- base_array = np.zeros((len(H2O_Liq),len(Fe3Fet_Liq),len(P_bar)))
491
+ # Fe3Fet_Liq: float or np.ndarray
492
+ # Initial Fe 3+/total ratio.
445
493
 
446
- if P_bar[0] == -1:
447
- P_bar = 1000
448
- if Fe3Fet_Liq[0] == -1:
449
- Fe3Fet_Liq = None
450
- if H2O_Liq[0] == -1:
451
- H2O_Liq = None
494
+ # H2O_Liq: float
495
+ # H2O content of the initial melt phase.
452
496
 
453
- # set default values for remaining parameters
454
- if T_initial_C is None:
455
- T_initial_C = 1200
497
+ # T_initial_C: float
498
+ # Starting temperature for the liquidus calculations. Default = 1200
456
499
 
457
- if dt_C is None:
458
- dt_C = 25
500
+ # T_maxdrop_C: float
501
+ # Max temperature drop of the calculations. Default = 25
459
502
 
460
- if T_step_C is None:
461
- T_step_C = 1
503
+ # dt_C: float
504
+ # Temperature change at each model step. Default = 1
462
505
 
463
- if T_cut_C is None:
464
- T_cut_C = 10
506
+ # T_cut_C: float
507
+ # Temperature offset used to indicate whether the model has succeeded or failed in finding a match. Default = 10.
465
508
 
466
- if phases is None:
467
- phases = ['quartz1', 'alkali-feldspar1', 'plagioclase1']
509
+ # find_range: True/False
510
+ # If True a new DataFrame will be included in Results, indicating all cases where the minimum offset is less than T_cut_C.
468
511
 
469
- # create main output dictionary
470
- Results = {}
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])
512
+ # find_min: True/False
513
+ # If True, a spline fit will be applied to the data to find the minimum point.
503
514
 
504
- # initialise queue
505
- qs = []
506
- q = Queue()
515
+ # fO2_buffer: string
516
+ # 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
517
 
518
+ # fO2_offset: float
519
+ # Offset from the buffer spcified in fO2_buffer (log units).
508
520
 
509
- # run calculations
510
- for k in range(len(Group)):
511
- ps = []
521
+ # Returns:
522
+ # ----------
523
+ # Results: Dict
524
+ # Dictionary containing information regarding the saturation temperature of each phase and the residuals between the different phases
525
+ # '''
526
+ # print('This function will be removed following the update to v0.3.0. Please switch to using the mineral_cosaturation() function')
527
+
528
+ # try:
529
+ # from meltsdynamic import MELTSdynamic
530
+ # except:
531
+ # 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')
532
+
533
+ # T_step_C = dt_C
534
+ # dt_C = T_maxdrop_C
512
535
 
513
- for kk in range(int(cores*k), int(cores*k + Group[k])):
514
- p = Process(target = satTemperature, args = (q, kk),
515
- kwargs = {'Model': Model, 'comp': comp,
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})
536
+ # comp = bulk.copy()
537
+ # if H2O_Sat is True:
538
+ # comp['H2O_Liq'] = 20
519
539
 
520
- ps.append(p)
521
- p.start()
540
+ # # set default values if required
541
+ # if Model is None:
542
+ # Model == "MELTSv1.0.2"
543
+
544
+ # if cores is None:
545
+ # cores = multiprocessing.cpu_count()
546
+
547
+ # # if comp is entered as a pandas series, it must first be converted to a dict
548
+ # if type(comp) == pd.core.series.Series:
549
+ # comp = comp.to_dict()
522
550
 
523
- TIMEOUT = 300
551
+ # # ensure the bulk composition has the correct headers etc.
552
+ # comp = comp_fix(Model = Model, comp = comp)
524
553
 
525
- start = time.time()
526
- for p in ps:
527
- if time.time() - start < TIMEOUT - 10:
528
- try:
529
- ret = q.get(timeout = TIMEOUT - (time.time()-start) + 10)
530
- except:
531
- ret = []
532
- else:
533
- try:
534
- ret = q.get(timeout = 10)
535
- except:
536
- ret = []
537
-
538
- qs.append(ret)
539
-
540
- TIMEOUT = 5
541
- start = time.time()
542
- for p in ps:
543
- if p.is_alive():
544
- while time.time() - start <= TIMEOUT:
545
- if not p.is_alive():
546
- p.join()
547
- p.terminate()
548
- break
549
- time.sleep(.1)
550
- else:
551
- p.terminate()
552
- p.join(5)
553
- else:
554
- p.join()
555
- p.terminate()
556
-
557
-
558
- # for p in ps:
559
- # try:
560
- # ret = q.get(timeout = 180)
561
- # except:
562
- # ret = []
563
- #
564
- # qs.append(ret)
565
- #
566
- # TIMEOUT = 20
567
- # start = time.time()
568
- # for p in ps:
569
- # if p.is_alive():
570
- # time.sleep(.1)
571
- # while time.time() - start <= TIMEOUT:
572
- # if not p.is_alive():
573
- # p.join()
574
- # p.terminate()
575
- # break
576
- # time.sleep(.1)
577
- # else:
578
- # p.terminate()
579
- # p.join(5)
580
- # else:
581
- # p.join()
582
- # p.terminate()
583
-
584
- # # extract results
585
- for kk in range(len(qs)):
586
- if len(qs[kk]) > 0:
587
- Res, index = qs[kk]
588
- for l in Results:
589
- if l != 'sat_surface':
590
- Results[l][i,j,index] = Res[l]
591
-
592
- # covert any empty values to nan
593
- for l in Results:
594
- if l != 'sat_surface':
595
- Results[l][np.where(Results[l] == 0.0)] = np.nan
596
-
597
- if find_min is not None:
598
- if H2O_Liq is not None:
599
- Results = findMinimum(Results = Results, P_bar = P_bar, T_cut_C = T_cut_C, H2O_Liq = H2O_Liq, Fe3Fet_Liq = Fe3Fet_Liq, phases = phases)
600
- else:
601
- Results = findMinimum(Results = Results, P_bar = P_bar, T_cut_C = T_cut_C, H2O_Liq = H2O_Liq, Fe3Fet_Liq = Fe3Fet_Liq, phases = phases)
602
-
603
- if find_range is not None:
604
- Results['range'] = np.zeros(np.shape(Results[l]))
605
- if len(phases) == 3:
606
- Results['range'][np.where(Results['3 Phase Saturation'] <= T_cut_C)] = True
607
- Results['range'][np.where(Results['3 Phase Saturation'] > T_cut_C)] = False
608
- else:
609
- Results['range'][np.where(Results[phases[0] + ' - ' + phases[1]] <= T_cut_C)] = True
610
- Results['range'][np.where(Results[phases[0] + ' - ' + phases[1]] > T_cut_C)] = False
554
+ # # create base array to be filled
555
+ # if P_bar is None:
556
+ # P_bar = np.array([-1])
557
+ # elif type(P_bar) != np.ndarray:
558
+ # P_bar = np.array([P_bar])
559
+
560
+ # if Fe3Fet_Liq is None:
561
+ # Fe3Fet_Liq = np.array([-1])
562
+ # elif type(Fe3Fet_Liq) != np.ndarray:
563
+ # Fe3Fet_Liq = np.array([Fe3Fet_Liq])
564
+
565
+ # if H2O_Liq is None:
566
+ # H2O_Liq = np.array([-1])
567
+ # elif type(H2O_Liq) != np.ndarray:
568
+ # H2O_Liq = np.array([H2O_Liq])
569
+
570
+ # base_array = np.zeros((len(H2O_Liq),len(Fe3Fet_Liq),len(P_bar)))
571
+
572
+ # if P_bar[0] == -1:
573
+ # P_bar = 1000
574
+ # if Fe3Fet_Liq[0] == -1:
575
+ # Fe3Fet_Liq = None
576
+ # if H2O_Liq[0] == -1:
577
+ # H2O_Liq = None
578
+
579
+ # # set default values for remaining parameters
580
+ # if T_initial_C is None:
581
+ # T_initial_C = 1200
582
+
583
+ # if dt_C is None:
584
+ # dt_C = 25
585
+
586
+ # if T_step_C is None:
587
+ # T_step_C = 1
588
+
589
+ # if T_cut_C is None:
590
+ # T_cut_C = 10
591
+
592
+ # if phases is None:
593
+ # phases = ['quartz1', 'alkali-feldspar1', 'plagioclase1']
594
+
595
+ # # create main output dictionary
596
+ # Results = {}
597
+ # if len(phases) == 3:
598
+ # 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]]
599
+ # for l in List:
600
+ # Results[l] = base_array.copy()
601
+ # else:
602
+ # List = [phases[0], phases[1], 'T_Liq', 'H2O_melt', phases[0] + ' - ' + phases[1]]
603
+ # for l in List:
604
+ # Results[l] = base_array.copy()
605
+
606
+ # # run calculations if only one initial composition provided
607
+ # if type(comp) == dict:
608
+ # for i in range(np.shape(base_array)[0]):
609
+ # # if H2O specified set H2O on each iteration
610
+ # if H2O_Liq is not None:
611
+ # comp['H2O_Liq'] = H2O_Liq[i]
612
+
613
+ # for j in range(np.shape(base_array)[1]):
614
+ # #if Fe3Fet specified set Fe3Fet on each iteration
615
+ # if Fe3Fet_Liq is not None:
616
+ # comp['Fe3Fet_Liq'] = Fe3Fet_Liq[j]
617
+
618
+ # # determine how many processes to run in parallel
619
+ # if len(P_bar) > 1:
620
+ # A = len(P_bar)//cores
621
+ # B = len(P_bar) % cores
622
+
623
+ # if A > 0:
624
+ # Group = np.zeros(A) + cores
625
+ # if B > 0:
626
+ # Group = np.append(Group, B)
627
+ # else:
628
+ # Group = np.array([B])
629
+
630
+ # # initialise queue
631
+ # qs = []
632
+ # q = Queue()
633
+
634
+
635
+ # # run calculations
636
+ # for k in range(len(Group)):
637
+ # ps = []
638
+
639
+ # for kk in range(int(cores*k), int(cores*k + Group[k])):
640
+ # p = Process(target = satTemperature, args = (q, kk),
641
+ # kwargs = {'Model': Model, 'comp': comp,
642
+ # 'T_initial_C': T_initial_C, 'T_step_C': T_step_C,
643
+ # 'dt_C': dt_C, 'P_bar': P_bar[kk], 'phases': phases,
644
+ # 'H2O_Liq': H2O_Liq, 'fO2_buffer': fO2_buffer, 'fO2_offset': fO2_offset})
645
+
646
+ # ps.append(p)
647
+ # p.start()
648
+
649
+ # TIMEOUT = 300
650
+
651
+ # start = time.time()
652
+ # for p in ps:
653
+ # if time.time() - start < TIMEOUT - 10:
654
+ # try:
655
+ # ret = q.get(timeout = TIMEOUT - (time.time()-start) + 10)
656
+ # except:
657
+ # ret = []
658
+ # else:
659
+ # try:
660
+ # ret = q.get(timeout = 10)
661
+ # except:
662
+ # ret = []
663
+
664
+ # qs.append(ret)
665
+
666
+ # TIMEOUT = 5
667
+ # start = time.time()
668
+ # for p in ps:
669
+ # if p.is_alive():
670
+ # while time.time() - start <= TIMEOUT:
671
+ # if not p.is_alive():
672
+ # p.join()
673
+ # p.terminate()
674
+ # break
675
+ # time.sleep(.1)
676
+ # else:
677
+ # p.terminate()
678
+ # p.join(5)
679
+ # else:
680
+ # p.join()
681
+ # p.terminate()
682
+
683
+
684
+ # # for p in ps:
685
+ # # try:
686
+ # # ret = q.get(timeout = 180)
687
+ # # except:
688
+ # # ret = []
689
+ # #
690
+ # # qs.append(ret)
691
+ # #
692
+ # # TIMEOUT = 20
693
+ # # start = time.time()
694
+ # # for p in ps:
695
+ # # if p.is_alive():
696
+ # # time.sleep(.1)
697
+ # # while time.time() - start <= TIMEOUT:
698
+ # # if not p.is_alive():
699
+ # # p.join()
700
+ # # p.terminate()
701
+ # # break
702
+ # # time.sleep(.1)
703
+ # # else:
704
+ # # p.terminate()
705
+ # # p.join(5)
706
+ # # else:
707
+ # # p.join()
708
+ # # p.terminate()
709
+
710
+ # # # extract results
711
+ # for kk in range(len(qs)):
712
+ # if len(qs[kk]) > 0:
713
+ # Res, index = qs[kk]
714
+ # for l in Results:
715
+ # if l != 'sat_surface':
716
+ # Results[l][i,j,index] = Res[l]
717
+
718
+ # # covert any empty values to nan
719
+ # for l in Results:
720
+ # if l != 'sat_surface':
721
+ # Results[l][np.where(Results[l] == 0.0)] = np.nan
722
+
723
+ # if find_min is not None:
724
+ # if H2O_Liq is not None:
725
+ # Results = findMinimum(Results = Results, P_bar = P_bar, T_cut_C = T_cut_C, H2O_Liq = H2O_Liq, Fe3Fet_Liq = Fe3Fet_Liq, phases = phases)
726
+ # else:
727
+ # Results = findMinimum(Results = Results, P_bar = P_bar, T_cut_C = T_cut_C, H2O_Liq = H2O_Liq, Fe3Fet_Liq = Fe3Fet_Liq, phases = phases)
728
+
729
+ # if find_range is not None:
730
+ # Results['range'] = np.zeros(np.shape(Results[l]))
731
+ # if len(phases) == 3:
732
+ # Results['range'][np.where(Results['3 Phase Saturation'] <= T_cut_C)] = True
733
+ # Results['range'][np.where(Results['3 Phase Saturation'] > T_cut_C)] = False
734
+ # else:
735
+ # Results['range'][np.where(Results[phases[0] + ' - ' + phases[1]] <= T_cut_C)] = True
736
+ # Results['range'][np.where(Results[phases[0] + ' - ' + phases[1]] > T_cut_C)] = False
611
737
 
612
738
 
613
739
 
614
- return Results
615
-
616
- def findMinimum(Results = None, P_bar = None, T_cut_C = None, H2O_Liq = None, Fe3Fet_Liq = None, phases = None):
617
- '''
618
- Take the results of SatPress and search for the minimum point using a spline fit.
619
- '''
620
- if T_cut_C is None:
621
- T_cut_C = 10
622
-
623
- if H2O_Liq is None and Fe3Fet_Liq is None:
624
- if '3 Phase Saturation' in list(Results.keys()):
625
- Minimum = {'3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]}
626
- CurveMin = {}
627
- for m in Minimum:
628
- if len(Results[m][0,0,:][~np.isnan(Results[m][0,0,:])]) > 2:
629
- y = Results[m][0,0,:][(np.where(~np.isnan(Results[m][0,0,:]))) and (np.where(Results[m][0,0,:] < T_cut_C))]
630
- x = P_bar[(np.where(~np.isnan(Results[m][0,0,:]))) and (np.where(Results[m][0,0,:] < T_cut_C))]
631
-
632
- try:
633
- y_new = interpolate.UnivariateSpline(x, y, k = 5)
634
- except:
635
- y_new = interpolate.UnivariateSpline(x, y, k = 2)
636
-
637
- 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)
638
-
639
- NewMin = np.nanmin(y_new(P_new))
640
- P_min = P_new[np.where(y_new(P_new) == NewMin)][0]
641
- if NewMin < T_cut_C:
642
- Test = 'Pass'
643
- else:
644
- Test = 'Fail'
645
-
646
- CurveMin[m] = {'P_min': P_min, 'Res_min': NewMin, 'y_new': y_new(P_new), 'P_new': P_new, 'test': Test}
647
- else:
648
- y_new = np.nan
649
- P_new = np.nan
650
- NewMin = np.nan
651
- P_min = np.nan
652
- Test = 'Fail'
653
- CurveMin[m] = {'P_min': P_min, 'Res_min': NewMin, 'y_new': y_new, 'P_new': P_new, 'test': Test}
654
-
655
- Results['CurveMin'] = CurveMin
656
- else:
657
- m = phases[0] + ' - ' + phases[1]
658
- if len(Results[m][0,0,:][~np.isnan(Results[m][0,0,:])]) > 2:
659
- y = Results[m][0,0,:][np.where(~np.isnan(Results[m][0,0,:]))]
660
- x = P_bar[np.where(~np.isnan(Results[m][0,0,:]))]
661
-
662
- try:
663
- y_new = interpolate.UnivariateSpline(x, y, k=5)
664
- except:
665
- y_new = interpolate.UnivariateSpline(x, y, k = 2)
666
-
667
- 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)
668
-
669
- NewMin = np.nanmin(y_new(P_new))
670
- P_min = P_new[np.where(y_new(P_new) == NewMin)][0]
671
- if NewMin < T_cut_C:
672
- Test = 'Pass'
673
- else:
674
- Test = 'Fail'
675
- else:
676
- y_new = np.nan
677
- P_new = np.nan
678
- NewMin = np.nan
679
- P_min = np.nan
680
- Test = 'Fail'
681
-
682
- Results['CurveMin'] = {phases[0] + ' - ' + phases[1]: {'P_min': P_min, 'Res_min': NewMin, 'y_new': y_new, 'P_new': P_new, 'test': Test}}
683
-
684
- if H2O_Liq is not None and Fe3Fet_Liq is None:
685
- if '3 Phase Saturation' in list(Results.keys()):
686
- X, Y = np.meshgrid(P_bar, H2O_Liq)
687
- Y = Results['H2O_melt'][:,0,:].copy()
688
-
689
- Minimum = {'3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]}
690
- CurveMin = {}
691
- for m in Minimum:
692
- if len(Results[m][:,0,:][~np.isnan(Results[m][:,0,:])]) > 4:
693
- Res = Results[m][:,0,:].copy()
694
- Res[np.where(Res > T_cut_C*2)] = np.nan
695
- for i in range(len(H2O_Liq)):
696
- Res[i, :][np.where(Results['H2O_melt'][i,0,:] < 0.99*np.nanmax(Results['H2O_melt'][i,0,:]))] = np.nan
697
-
698
- A = Res.copy()
699
- Res[np.where(Res > T_cut_C)] = np.nan
700
- X, Y = np.meshgrid(P_bar, H2O_Liq)
701
- Y = Results['H2O_melt'][:,0,:]
702
-
703
- try:
704
- 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)
705
- except:
706
- 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)
707
-
708
- H2O_new = np.linspace(Y[np.where(Res == np.nanmin(Res))] - (H2O_Liq[1]-H2O_Liq[0]),
709
- Y[np.where(Res == np.nanmin(Res))] + (H2O_Liq[1]-H2O_Liq[0]), 20)
710
- P_new = np.linspace(X[np.where(Res == np.nanmin(Res))] - (P_bar[1]-P_bar[0]),
711
- X[np.where(Res == np.nanmin(Res))] + (P_bar[1]-P_bar[0]), 20)
712
-
713
- X_new, Y_new = np.meshgrid(P_new, H2O_new)
714
- x = X[~np.isnan(A)].flatten()
715
- y = Y[~np.isnan(A)].flatten()
716
-
717
- MyPoly = MultiPoint(list(zip(x, y))).convex_hull
718
-
719
- points = list(zip(X_new.flatten(), Y_new.flatten()))
720
- Include = np.zeros(len(X_new.flatten()))
721
- for i in range(len(points)):
722
- p = Point(points[i])
723
- Include[i] = p.within(MyPoly)
724
-
725
- YayNay = Include.reshape(X_new.shape)
726
- x_new = X_new[np.where(YayNay == True)].flatten()
727
- y_new = Y_new[np.where(YayNay == True)].flatten()
728
- Res_min = np.nanmin(z_new(x_new, y_new, grid = False))
729
- P_min = x_new[np.where(z_new(x_new, y_new, grid = False) == Res_min)]
730
- H2O_min = y_new[np.where(z_new(x_new, y_new, grid = False) == Res_min)]
731
- if Res_min < T_cut_C:
732
- Test = 'Pass'
733
- else:
734
- Test = 'Fail'
735
-
736
- CurveMin[m] = {'Res_min': Res_min, 'P_min': P_min[0], 'H2O_min': H2O_min[0], 'z_new': z_new, 'test': Test}
737
- else:
738
- CurveMin[m] = {'Res_min': np.nan, 'P_min': np.nan, 'H2O_min': np.nan, 'z_new': np.nan, 'test': 'Fail'}
739
-
740
- Results['CurveMin'] = CurveMin
741
-
742
- if H2O_Liq is not None and Fe3Fet_Liq is not None:
743
- if '3 Phase Saturation' in list(Results.keys()):
744
- X, Y = np.meshgrid(P_bar, H2O_Liq)
745
- Y = Results['H2O_melt'][:,0,:].copy()
746
-
747
- Minimum = {'3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]}
748
- CurveMin = {}
749
- for m in Minimum:
750
- Res_min_save = 50
751
- for w in range(len(Fe3Fet_Liq)):
752
- if len(Results[m][:,w,:][~np.isnan(Results[m][:,w,:])]) > 4:
753
- Res = Results[m][:,w,:].copy()
754
- Res[np.where(Res > T_cut_C*2)] = np.nan
755
- for i in range(len(H2O_Liq)):
756
- Res[i, :][np.where(Results['H2O_melt'][i,w,:] < 0.99*np.nanmax(Results['H2O_melt'][i,w,:]))] = np.nan
757
-
758
- A = Res.copy()
759
- X, Y = np.meshgrid(P_bar, H2O_Liq)
760
- Y = Results['H2O_melt'][:,w,:].copy()
761
-
762
- try:
763
- 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)
764
- except:
765
- 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)
766
-
767
- H2O_new = np.linspace(Y[np.where(Res == np.nanmin(Res))] - (H2O_Liq[1]-H2O_Liq[0]),
768
- Y[np.where(Res == np.nanmin(Res))] + (H2O_Liq[1]-H2O_Liq[0]), 20)
769
- P_new = np.linspace(X[np.where(Res == np.nanmin(Res))] - (P_bar[1]-P_bar[0]),
770
- X[np.where(Res == np.nanmin(Res))] + (P_bar[1]-P_bar[0]), 20)
771
-
772
- X_new, Y_new = np.meshgrid(P_new, H2O_new)
773
- x = X[~np.isnan(A)].flatten()
774
- y = Y[~np.isnan(A)].flatten()
775
-
776
- MyPoly = MultiPoint(list(zip(x, y))).convex_hull
777
-
778
- points = list(zip(X_new.flatten(), Y_new.flatten()))
779
- Include = np.zeros(len(X_new.flatten()))
780
- for i in range(len(points)):
781
- p = Point(points[i])
782
- Include[i] = p.within(MyPoly)
783
-
784
- YayNay = Include.reshape(X_new.shape)
785
- x_new = X_new[np.where(YayNay == True)].flatten()
786
- y_new = Y_new[np.where(YayNay == True)].flatten()
787
- Res_min = np.nanmin(z_new(x_new, y_new, grid = False))
788
- P_min = x_new[np.where(z_new(x_new, y_new, grid = False) == Res_min)]
789
- H2O_min = y_new[np.where(z_new(x_new, y_new, grid = False) == Res_min)]
790
- if Res_min < T_cut_C:
791
- Test = 'Pass'
792
- else:
793
- Test = 'Fail'
794
-
795
- if Res_min < Res_min_save:
796
- Res_min_save = Res_min
797
- 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}
798
-
799
- Results['CurveMin'] = CurveMin
800
-
801
-
802
- return Results
803
-
804
- def polymin(P_bar = None, Res = None):
805
- '''
806
- Finds the minimum residual temperature using a 2nd degree polynomial.
807
- '''
808
- arr = np.sort(Res)
809
- Ind = np.where(Res == arr[0])[0][0]
810
-
811
- if P_bar[Ind] == np.nanmax(P_bar):
812
- p = np.array([0,1,0])
813
- p_min = np.array([P_bar[Ind]])
814
- elif P_bar[Ind] == np.nanmin(P_bar):
815
- p = np.array([0,1,0])
816
- p_min = np.array([P_bar[Ind]])
817
- else:
818
- p = np.polyfit(P_bar[np.array([Ind-1, Ind, Ind+1])],Res[np.array([Ind-1, Ind, Ind+1])],2)
819
-
820
- x = np.linspace(np.nanmin(P_bar),np.nanmax(P_bar),501)
821
- y = p[0]*x**2 + p[1]*x + p[2]
822
-
823
- p_min = x[np.where(y == np.nanmin(y))]
824
-
825
- return p, p_min
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
- Crystallisation calculations to be performed in parallel. Calculations may be either isobaric or isochoric.
830
-
831
- Parameters:
832
- ----------
833
- q: Multiprocessing Queue instance
834
- Queue instance to record the output variables
835
-
836
- index: int
837
- index of the calculation in the master code (e.g., position within a for loop) to aid indexing results after calculations are complete.
838
-
839
- Model: string
840
- "MELTS" or "Holland". Dictates whether MELTS or MAGEMin calculations are performed. Default "MELTS".
841
- Version of melts can be specified "MELTSv1.0.2", "MELTSv1.1.0", "MELTSv1.2.0", or "pMELTS". Default "v.1.0.2".
842
-
843
- comp: Dict
844
- Initial compositon for calculations.
845
-
846
- T_initial_C: float
847
- Initial guess for the liquidus temperature.
848
-
849
- T_step_C: float
850
- The temperature drop at each step of the calculation.
851
-
852
- dt_C: float
853
- Total temperature drop allowed duringmodel runs.
854
-
855
- P_bar: float
856
- Specifies the pressure of calculation (bar).
857
-
858
- Returns:
859
- ----------
860
- Results: Dict
861
- Dict containing a series of floats that represent the saturation temperature and residual temperature for each calculation.
862
-
863
- index: int
864
- index of the calculation
865
-
866
- '''
867
-
868
- Results = {}
869
- if "MELTS" in Model:
870
-
871
- try:
872
- 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)
873
- except:
874
- Results = {phases[0]: np.nan, phases[1]: np.nan, phases[2]: np.nan, 'T_Liq': np.nan, 'H2O_melt': np.nan}
875
- if len(phases) == 2:
876
- del Results[phases[2]]
877
-
878
- if len(phases) == 3:
879
- Res = ['3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]]
880
- for R in Res:
881
- Results[R] = np.nan
882
-
883
- if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]) and ~np.isnan(Results[phases[2]]):
884
- 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]])]))
885
- if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]):
886
- Results[phases[0] + ' - ' + phases[1]] = abs(Results[phases[0]] - Results[phases[1]])
887
- if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[2]]):
888
- Results[phases[0] + ' - ' + phases[2]] = abs(Results[phases[0]] - Results[phases[2]])
889
- if ~np.isnan(Results[phases[1]]) and ~np.isnan(Results[phases[2]]):
890
- Results[phases[1] + ' - ' + phases[2]] = abs(Results[phases[1]] - Results[phases[2]])
891
- else:
892
- Results[phases[0] + ' - ' + phases[1]] = np.nan
893
- if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]):
894
- Results[phases[0] + ' - ' + phases[1]] = abs(Results[phases[0]] - Results[phases[1]])
895
-
896
- q.put([Results, index])
897
- return
898
-
899
- if Model == "Holland":
900
- import pyMAGEMINcalc as MM
901
- Results = {phases[0]: np.nan, phases[1]: np.nan, phases[2]: np.nan, 'T_Liq': np.nan, 'H2O_melt': np.nan}
902
- if len(phases) == 2:
903
- del Results[phases[2]]
904
-
905
- #try:
906
- Result = MM.path(comp = comp, phases = phases, T_min_C = dt_C, dt_C = T_step_C, P_bar = P_bar, find_liquidus = True)
907
- #Result = stich(Result, Model = Model)
908
-
909
- for i in range(len(phases)):
910
- try:
911
- Results[phases[i]] = Result['Conditions']['T_C'][Result[phases[i]+'_prop']['mass'] > 0.0].values[0]
912
- print(Results[phases[i]])
913
- except:
914
- Results[phases[i]] = np.nan
915
-
916
- Results['T_Liq'] = Result['Conditions']['T_C'].values[0]
917
- Results['H2O_melt'] = Result['liq']['H2O'].values[0]
918
-
919
- if len(phases) == 3:
920
- Res = ['3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]]
921
- for R in Res:
922
- Results[R] = np.nan
923
-
924
- if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]) and ~np.isnan(Results[phases[2]]):
925
- 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]])]))
926
- if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]):
927
- Results[phases[0] + ' - ' + phases[1]] = abs(Results[phases[0]] - Results[phases[1]])
928
- if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[2]]):
929
- Results[phases[0] + ' - ' + phases[2]] = abs(Results[phases[0]] - Results[phases[2]])
930
- if ~np.isnan(Results[phases[1]]) and ~np.isnan(Results[phases[2]]):
931
- Results[phases[1] + ' - ' + phases[2]] = abs(Results[phases[1]] - Results[phases[2]])
932
- else:
933
- Results[phases[0] + ' - ' + phases[1]] = np.nan
934
- if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]):
935
- Results[phases[0] + ' - ' + phases[1]] = abs(Results[phases[0]] - Results[phases[1]])
740
+ # return Results
741
+
742
+ # def findMinimum(Results = None, P_bar = None, T_cut_C = None, H2O_Liq = None, Fe3Fet_Liq = None, phases = None):
743
+ # '''
744
+ # Take the results of SatPress and search for the minimum point using a spline fit.
745
+ # '''
746
+ # if T_cut_C is None:
747
+ # T_cut_C = 10
748
+
749
+ # if H2O_Liq is None and Fe3Fet_Liq is None:
750
+ # if '3 Phase Saturation' in list(Results.keys()):
751
+ # Minimum = {'3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]}
752
+ # CurveMin = {}
753
+ # for m in Minimum:
754
+ # if len(Results[m][0,0,:][~np.isnan(Results[m][0,0,:])]) > 2:
755
+ # y = Results[m][0,0,:][(np.where(~np.isnan(Results[m][0,0,:]))) and (np.where(Results[m][0,0,:] < T_cut_C))]
756
+ # x = P_bar[(np.where(~np.isnan(Results[m][0,0,:]))) and (np.where(Results[m][0,0,:] < T_cut_C))]
757
+
758
+ # try:
759
+ # y_new = interpolate.UnivariateSpline(x, y, k = 5)
760
+ # except:
761
+ # y_new = interpolate.UnivariateSpline(x, y, k = 2)
762
+
763
+ # 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)
764
+
765
+ # NewMin = np.nanmin(y_new(P_new))
766
+ # P_min = P_new[np.where(y_new(P_new) == NewMin)][0]
767
+ # if NewMin < T_cut_C:
768
+ # Test = 'Pass'
769
+ # else:
770
+ # Test = 'Fail'
771
+
772
+ # CurveMin[m] = {'P_min': P_min, 'Res_min': NewMin, 'y_new': y_new(P_new), 'P_new': P_new, 'test': Test}
773
+ # else:
774
+ # y_new = np.nan
775
+ # P_new = np.nan
776
+ # NewMin = np.nan
777
+ # P_min = np.nan
778
+ # Test = 'Fail'
779
+ # CurveMin[m] = {'P_min': P_min, 'Res_min': NewMin, 'y_new': y_new, 'P_new': P_new, 'test': Test}
780
+
781
+ # Results['CurveMin'] = CurveMin
782
+ # else:
783
+ # m = phases[0] + ' - ' + phases[1]
784
+ # if len(Results[m][0,0,:][~np.isnan(Results[m][0,0,:])]) > 2:
785
+ # y = Results[m][0,0,:][np.where(~np.isnan(Results[m][0,0,:]))]
786
+ # x = P_bar[np.where(~np.isnan(Results[m][0,0,:]))]
787
+
788
+ # try:
789
+ # y_new = interpolate.UnivariateSpline(x, y, k=5)
790
+ # except:
791
+ # y_new = interpolate.UnivariateSpline(x, y, k = 2)
792
+
793
+ # 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)
794
+
795
+ # NewMin = np.nanmin(y_new(P_new))
796
+ # P_min = P_new[np.where(y_new(P_new) == NewMin)][0]
797
+ # if NewMin < T_cut_C:
798
+ # Test = 'Pass'
799
+ # else:
800
+ # Test = 'Fail'
801
+ # else:
802
+ # y_new = np.nan
803
+ # P_new = np.nan
804
+ # NewMin = np.nan
805
+ # P_min = np.nan
806
+ # Test = 'Fail'
807
+
808
+ # Results['CurveMin'] = {phases[0] + ' - ' + phases[1]: {'P_min': P_min, 'Res_min': NewMin, 'y_new': y_new, 'P_new': P_new, 'test': Test}}
809
+
810
+ # if H2O_Liq is not None and Fe3Fet_Liq is None:
811
+ # if '3 Phase Saturation' in list(Results.keys()):
812
+ # X, Y = np.meshgrid(P_bar, H2O_Liq)
813
+ # Y = Results['H2O_melt'][:,0,:].copy()
814
+
815
+ # Minimum = {'3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]}
816
+ # CurveMin = {}
817
+ # for m in Minimum:
818
+ # if len(Results[m][:,0,:][~np.isnan(Results[m][:,0,:])]) > 4:
819
+ # Res = Results[m][:,0,:].copy()
820
+ # Res[np.where(Res > T_cut_C*2)] = np.nan
821
+ # for i in range(len(H2O_Liq)):
822
+ # Res[i, :][np.where(Results['H2O_melt'][i,0,:] < 0.99*np.nanmax(Results['H2O_melt'][i,0,:]))] = np.nan
823
+
824
+ # A = Res.copy()
825
+ # Res[np.where(Res > T_cut_C)] = np.nan
826
+ # X, Y = np.meshgrid(P_bar, H2O_Liq)
827
+ # Y = Results['H2O_melt'][:,0,:]
828
+
829
+ # try:
830
+ # 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)
831
+ # except:
832
+ # 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)
833
+
834
+ # H2O_new = np.linspace(Y[np.where(Res == np.nanmin(Res))] - (H2O_Liq[1]-H2O_Liq[0]),
835
+ # Y[np.where(Res == np.nanmin(Res))] + (H2O_Liq[1]-H2O_Liq[0]), 20)
836
+ # P_new = np.linspace(X[np.where(Res == np.nanmin(Res))] - (P_bar[1]-P_bar[0]),
837
+ # X[np.where(Res == np.nanmin(Res))] + (P_bar[1]-P_bar[0]), 20)
838
+
839
+ # X_new, Y_new = np.meshgrid(P_new, H2O_new)
840
+ # x = X[~np.isnan(A)].flatten()
841
+ # y = Y[~np.isnan(A)].flatten()
842
+
843
+ # MyPoly = MultiPoint(list(zip(x, y))).convex_hull
844
+
845
+ # points = list(zip(X_new.flatten(), Y_new.flatten()))
846
+ # Include = np.zeros(len(X_new.flatten()))
847
+ # for i in range(len(points)):
848
+ # p = Point(points[i])
849
+ # Include[i] = p.within(MyPoly)
850
+
851
+ # YayNay = Include.reshape(X_new.shape)
852
+ # x_new = X_new[np.where(YayNay == True)].flatten()
853
+ # y_new = Y_new[np.where(YayNay == True)].flatten()
854
+ # Res_min = np.nanmin(z_new(x_new, y_new, grid = False))
855
+ # P_min = x_new[np.where(z_new(x_new, y_new, grid = False) == Res_min)]
856
+ # H2O_min = y_new[np.where(z_new(x_new, y_new, grid = False) == Res_min)]
857
+ # if Res_min < T_cut_C:
858
+ # Test = 'Pass'
859
+ # else:
860
+ # Test = 'Fail'
861
+
862
+ # CurveMin[m] = {'Res_min': Res_min, 'P_min': P_min[0], 'H2O_min': H2O_min[0], 'z_new': z_new, 'test': Test}
863
+ # else:
864
+ # CurveMin[m] = {'Res_min': np.nan, 'P_min': np.nan, 'H2O_min': np.nan, 'z_new': np.nan, 'test': 'Fail'}
865
+
866
+ # Results['CurveMin'] = CurveMin
867
+
868
+ # if H2O_Liq is not None and Fe3Fet_Liq is not None:
869
+ # if '3 Phase Saturation' in list(Results.keys()):
870
+ # X, Y = np.meshgrid(P_bar, H2O_Liq)
871
+ # Y = Results['H2O_melt'][:,0,:].copy()
872
+
873
+ # Minimum = {'3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]}
874
+ # CurveMin = {}
875
+ # for m in Minimum:
876
+ # Res_min_save = 50
877
+ # for w in range(len(Fe3Fet_Liq)):
878
+ # if len(Results[m][:,w,:][~np.isnan(Results[m][:,w,:])]) > 4:
879
+ # Res = Results[m][:,w,:].copy()
880
+ # Res[np.where(Res > T_cut_C*2)] = np.nan
881
+ # for i in range(len(H2O_Liq)):
882
+ # Res[i, :][np.where(Results['H2O_melt'][i,w,:] < 0.99*np.nanmax(Results['H2O_melt'][i,w,:]))] = np.nan
883
+
884
+ # A = Res.copy()
885
+ # X, Y = np.meshgrid(P_bar, H2O_Liq)
886
+ # Y = Results['H2O_melt'][:,w,:].copy()
887
+
888
+ # try:
889
+ # 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)
890
+ # except:
891
+ # 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)
892
+
893
+ # H2O_new = np.linspace(Y[np.where(Res == np.nanmin(Res))] - (H2O_Liq[1]-H2O_Liq[0]),
894
+ # Y[np.where(Res == np.nanmin(Res))] + (H2O_Liq[1]-H2O_Liq[0]), 20)
895
+ # P_new = np.linspace(X[np.where(Res == np.nanmin(Res))] - (P_bar[1]-P_bar[0]),
896
+ # X[np.where(Res == np.nanmin(Res))] + (P_bar[1]-P_bar[0]), 20)
897
+
898
+ # X_new, Y_new = np.meshgrid(P_new, H2O_new)
899
+ # x = X[~np.isnan(A)].flatten()
900
+ # y = Y[~np.isnan(A)].flatten()
901
+
902
+ # MyPoly = MultiPoint(list(zip(x, y))).convex_hull
903
+
904
+ # points = list(zip(X_new.flatten(), Y_new.flatten()))
905
+ # Include = np.zeros(len(X_new.flatten()))
906
+ # for i in range(len(points)):
907
+ # p = Point(points[i])
908
+ # Include[i] = p.within(MyPoly)
909
+
910
+ # YayNay = Include.reshape(X_new.shape)
911
+ # x_new = X_new[np.where(YayNay == True)].flatten()
912
+ # y_new = Y_new[np.where(YayNay == True)].flatten()
913
+ # Res_min = np.nanmin(z_new(x_new, y_new, grid = False))
914
+ # P_min = x_new[np.where(z_new(x_new, y_new, grid = False) == Res_min)]
915
+ # H2O_min = y_new[np.where(z_new(x_new, y_new, grid = False) == Res_min)]
916
+ # if Res_min < T_cut_C:
917
+ # Test = 'Pass'
918
+ # else:
919
+ # Test = 'Fail'
920
+
921
+ # if Res_min < Res_min_save:
922
+ # Res_min_save = Res_min
923
+ # 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}
924
+
925
+ # Results['CurveMin'] = CurveMin
926
+
927
+
928
+ # return Results
929
+
930
+ # def polymin(P_bar = None, Res = None):
931
+ # '''
932
+ # Finds the minimum residual temperature using a 2nd degree polynomial.
933
+ # '''
934
+ # arr = np.sort(Res)
935
+ # Ind = np.where(Res == arr[0])[0][0]
936
+
937
+ # if P_bar[Ind] == np.nanmax(P_bar):
938
+ # p = np.array([0,1,0])
939
+ # p_min = np.array([P_bar[Ind]])
940
+ # elif P_bar[Ind] == np.nanmin(P_bar):
941
+ # p = np.array([0,1,0])
942
+ # p_min = np.array([P_bar[Ind]])
943
+ # else:
944
+ # p = np.polyfit(P_bar[np.array([Ind-1, Ind, Ind+1])],Res[np.array([Ind-1, Ind, Ind+1])],2)
945
+
946
+ # x = np.linspace(np.nanmin(P_bar),np.nanmax(P_bar),501)
947
+ # y = p[0]*x**2 + p[1]*x + p[2]
948
+
949
+ # p_min = x[np.where(y == np.nanmin(y))]
950
+
951
+ # return p, p_min
952
+
953
+ # 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):
954
+ # '''
955
+ # Crystallisation calculations to be performed in parallel. Calculations may be either isobaric or isochoric.
956
+
957
+ # Parameters:
958
+ # ----------
959
+ # q: Multiprocessing Queue instance
960
+ # Queue instance to record the output variables
961
+
962
+ # index: int
963
+ # index of the calculation in the master code (e.g., position within a for loop) to aid indexing results after calculations are complete.
964
+
965
+ # Model: string
966
+ # "MELTS" or "Holland". Dictates whether MELTS or MAGEMin calculations are performed. Default "MELTS".
967
+ # Version of melts can be specified "MELTSv1.0.2", "MELTSv1.1.0", "MELTSv1.2.0", or "pMELTS". Default "v.1.0.2".
968
+
969
+ # comp: Dict
970
+ # Initial compositon for calculations.
971
+
972
+ # T_initial_C: float
973
+ # Initial guess for the liquidus temperature.
974
+
975
+ # T_step_C: float
976
+ # The temperature drop at each step of the calculation.
977
+
978
+ # dt_C: float
979
+ # Total temperature drop allowed duringmodel runs.
980
+
981
+ # P_bar: float
982
+ # Specifies the pressure of calculation (bar).
983
+
984
+ # Returns:
985
+ # ----------
986
+ # Results: Dict
987
+ # Dict containing a series of floats that represent the saturation temperature and residual temperature for each calculation.
988
+
989
+ # index: int
990
+ # index of the calculation
991
+
992
+ # '''
993
+
994
+ # Results = {}
995
+ # if "MELTS" in Model:
996
+
997
+ # try:
998
+ # 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)
999
+ # except:
1000
+ # Results = {phases[0]: np.nan, phases[1]: np.nan, phases[2]: np.nan, 'T_Liq': np.nan, 'H2O_melt': np.nan}
1001
+ # if len(phases) == 2:
1002
+ # del Results[phases[2]]
1003
+
1004
+ # if len(phases) == 3:
1005
+ # Res = ['3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]]
1006
+ # for R in Res:
1007
+ # Results[R] = np.nan
1008
+
1009
+ # if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]) and ~np.isnan(Results[phases[2]]):
1010
+ # 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]])]))
1011
+ # if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]):
1012
+ # Results[phases[0] + ' - ' + phases[1]] = abs(Results[phases[0]] - Results[phases[1]])
1013
+ # if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[2]]):
1014
+ # Results[phases[0] + ' - ' + phases[2]] = abs(Results[phases[0]] - Results[phases[2]])
1015
+ # if ~np.isnan(Results[phases[1]]) and ~np.isnan(Results[phases[2]]):
1016
+ # Results[phases[1] + ' - ' + phases[2]] = abs(Results[phases[1]] - Results[phases[2]])
1017
+ # else:
1018
+ # Results[phases[0] + ' - ' + phases[1]] = np.nan
1019
+ # if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]):
1020
+ # Results[phases[0] + ' - ' + phases[1]] = abs(Results[phases[0]] - Results[phases[1]])
1021
+
1022
+ # q.put([Results, index])
1023
+ # return
1024
+
1025
+ # if Model == "Holland":
1026
+ # import pyMAGEMINcalc as MM
1027
+ # Results = {phases[0]: np.nan, phases[1]: np.nan, phases[2]: np.nan, 'T_Liq': np.nan, 'H2O_melt': np.nan}
1028
+ # if len(phases) == 2:
1029
+ # del Results[phases[2]]
1030
+
1031
+ # #try:
1032
+ # Result = MM.path(comp = comp, phases = phases, T_min_C = dt_C, dt_C = T_step_C, P_bar = P_bar, find_liquidus = True)
1033
+ # #Result = stich(Result, Model = Model)
1034
+
1035
+ # for i in range(len(phases)):
1036
+ # try:
1037
+ # Results[phases[i]] = Result['Conditions']['T_C'][Result[phases[i]+'_prop']['mass'] > 0.0].values[0]
1038
+ # print(Results[phases[i]])
1039
+ # except:
1040
+ # Results[phases[i]] = np.nan
1041
+
1042
+ # Results['T_Liq'] = Result['Conditions']['T_C'].values[0]
1043
+ # Results['H2O_melt'] = Result['liq']['H2O'].values[0]
1044
+
1045
+ # if len(phases) == 3:
1046
+ # Res = ['3 Phase Saturation', phases[0] + ' - ' + phases[1], phases[0] + ' - ' + phases[2], phases[1] + ' - ' + phases[2]]
1047
+ # for R in Res:
1048
+ # Results[R] = np.nan
1049
+
1050
+ # if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]) and ~np.isnan(Results[phases[2]]):
1051
+ # 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]])]))
1052
+ # if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]):
1053
+ # Results[phases[0] + ' - ' + phases[1]] = abs(Results[phases[0]] - Results[phases[1]])
1054
+ # if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[2]]):
1055
+ # Results[phases[0] + ' - ' + phases[2]] = abs(Results[phases[0]] - Results[phases[2]])
1056
+ # if ~np.isnan(Results[phases[1]]) and ~np.isnan(Results[phases[2]]):
1057
+ # Results[phases[1] + ' - ' + phases[2]] = abs(Results[phases[1]] - Results[phases[2]])
1058
+ # else:
1059
+ # Results[phases[0] + ' - ' + phases[1]] = np.nan
1060
+ # if ~np.isnan(Results[phases[0]]) and ~np.isnan(Results[phases[1]]):
1061
+ # Results[phases[0] + ' - ' + phases[1]] = abs(Results[phases[0]] - Results[phases[1]])
936
1062
 
937
- q.put([Results, index])
938
- #except:
939
- # q.put([Results, index])
940
- return
1063
+ # q.put([Results, index])
1064
+ # #except:
1065
+ # # q.put([Results, index])
1066
+ # return
941
1067
 
942
1068