Skip to content

Commit 36739ed

Browse files
committed
added optimal PT calculations
1 parent 4ac4257 commit 36739ed

File tree

7 files changed

+517
-2
lines changed

7 files changed

+517
-2
lines changed

burnman/classes/composite.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,40 @@ def endmember_partial_gibbs(self):
473473
j += n_endmembers
474474
return partial_gibbs
475475

476+
@material_property
477+
def endmember_partial_entropies(self):
478+
"""
479+
Returns the partial entropies for all
480+
the endmember minerals in the Composite
481+
"""
482+
partial_entropies = np.empty(self.n_endmembers)
483+
j = 0
484+
for i, n_endmembers in enumerate(self.endmembers_per_phase):
485+
if n_endmembers == 1:
486+
partial_entropies[j] = self.phases[i].molar_entropy
487+
else:
488+
partial_entropies[j : j + n_endmembers] = self.phases[
489+
i
490+
].partial_entropies
491+
j += n_endmembers
492+
return partial_entropies
493+
494+
@material_property
495+
def endmember_partial_volumes(self):
496+
"""
497+
Returns the partial volumes for all
498+
the endmember minerals in the Composite
499+
"""
500+
partial_volumes = np.empty(self.n_endmembers)
501+
j = 0
502+
for i, n_endmembers in enumerate(self.endmembers_per_phase):
503+
if n_endmembers == 1:
504+
partial_volumes[j] = self.phases[i].molar_volume
505+
else:
506+
partial_volumes[j : j + n_endmembers] = self.phases[i].partial_volumes
507+
j += n_endmembers
508+
return partial_volumes
509+
476510
@material_property
477511
def reaction_affinities(self):
478512
"""

burnman/optimize/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This file is part of BurnMan - a thermoelastic and thermodynamic toolkit for
22
# the Earth and Planetary Sciences
3-
# Copyright (C) 2012 - 2021 by the BurnMan team, released under the GNU
3+
# Copyright (C) 2012 - 2025 by the BurnMan team, released under the GNU
44
# GPL v2 or later.
55

66
"""

burnman/tools/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This file is part of BurnMan - a thermoelastic and thermodynamic toolkit for
22
# the Earth and Planetary Sciences
3-
# Copyright (C) 2012 - 2021 by the BurnMan team, released under the GNU
3+
# Copyright (C) 2012 - 2025 by the BurnMan team, released under the GNU
44
# GPL v2 or later.
55

66

@@ -23,4 +23,5 @@
2323
from . import plot
2424
from . import polytope
2525
from . import solution
26+
from . import thermobarometry
2627
from . import unitcell

burnman/tools/polytope.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,75 @@ def simplify_composite_with_composition(composite, composition):
312312
return Composite(new_phases)
313313
else:
314314
return composite
315+
316+
317+
def greedy_independent_endmember_selection(
318+
endmember_site_occupancies, site_occupancies, small_fraction_tol=0.0, norm_tol=1e-12
319+
):
320+
"""
321+
Greedy algorithm to select independent endmembers from a solution to approximate given
322+
site occupancies through a non-negative linear combination of endmember site occupancies.
323+
324+
This function starts with the full site occupancies and then iteratively selects endmembers
325+
to approximate those site occupancies. It loops through all possible endmembers in a number of steps.
326+
At each step the algorithm selects the endmember that can be subtracted in the largest amount from the
327+
current residual site occupancies without making any site occupancy negative.
328+
The process continues until either no endmember can be subtracted in an amount greater than fraction_tol,
329+
or the norm of the residual site occupancies is less than tol.
330+
331+
:param endmember_site_occupancies: A 2D array of shape (m, n), where m is the number of endmembers
332+
and n is the number of sites. Each row corresponds to the site occupancies of an endmember.
333+
:type endmember_site_occupancies: np.ndarray
334+
335+
:param site_occupancies: A 1D array of length n, representing the target site occupancies to approximate.
336+
:type site_occupancies: np.ndarray
337+
338+
:param small_fraction_tol: Algorithm stops if no endmember can be added with a molar fraction larger than this value.
339+
:type small_fraction_tol: float, optional, default 0.0
340+
341+
:param norm_tol: Algorithm stops if the norm of the residual site occupancies is less than this value.
342+
:type norm_tol: float, optional, default 1e-12
343+
344+
:return: indices of selected endmembers, their fractions, and the final residual site occupancies.
345+
:rtype: tuple(list[int], list[float], np.ndarray)
346+
"""
347+
348+
site_occupancy_residuals = site_occupancies.copy()
349+
indices = []
350+
fractions = []
351+
352+
for _ in range(endmember_site_occupancies.shape[0]):
353+
# compute fraction for each candidate endmember
354+
# fraction_i = min_j r_j / s_ij over s_ij > 0; if no s_ij>0 -> fraction_i = 0
355+
with np.errstate(divide="ignore", invalid="ignore"):
356+
ratios = np.where(
357+
endmember_site_occupancies > 0,
358+
site_occupancy_residuals[np.newaxis, :] / endmember_site_occupancies,
359+
np.inf,
360+
) # shape (m,n)
361+
362+
# For each row, take minimum ratio over columns where S>0; if all entries inf -> set 0
363+
fractions_all = np.min(ratios, axis=1)
364+
fractions_all[np.isinf(fractions_all)] = 0.0
365+
366+
# pick largest alpha
367+
i_max = int(np.argmax(fractions_all))
368+
fraction_max = float(fractions_all[i_max])
369+
if fraction_max <= small_fraction_tol:
370+
break # no further progress possible or desirable
371+
372+
# update residual
373+
site_occupancy_residuals = (
374+
site_occupancy_residuals - fraction_max * endmember_site_occupancies[i_max]
375+
)
376+
377+
# ensure non-negativity in elements of the residual
378+
site_occupancy_residuals[site_occupancy_residuals < 0] = 0.0
379+
indices.append(i_max)
380+
fractions.append(fraction_max)
381+
382+
# check if done
383+
if np.linalg.norm(site_occupancy_residuals) <= norm_tol:
384+
break
385+
386+
return indices, fractions, site_occupancy_residuals

0 commit comments

Comments
 (0)