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