Skip to content

Commit d56f9dd

Browse files
authored
Merge pull request #655 from bobmyhill/improve_solution_testing
Remove "free" from energy argument and method names, improve solution testing
2 parents 9e243f4 + 5134539 commit d56f9dd

36 files changed

+334
-161
lines changed

Readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.14238360.svg)](https://doi.org/10.5281/zenodo.14238360)
22
[![DOI](https://joss.theoj.org/papers/10.21105/joss.05389/status.svg)](https://doi.org/10.21105/joss.05389)
3-
[![coverage](https://img.shields.io/badge/coverage-%3E92%25-green")](https://github.com/geodynamics/burnman/actions/workflows/coverage.yml)
3+
[![coverage](https://img.shields.io/badge/coverage-%3E93%25-green")](https://github.com/geodynamics/burnman/actions/workflows/coverage.yml)
44

55
# BurnMan - a Python toolkit for planetary geophysics, geochemistry and thermodynamics
66

burnman/classes/combinedmineral.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def __init__(
3434
self,
3535
mineral_list,
3636
molar_amounts,
37-
free_energy_adjustment=[],
37+
energy_adjustment=[],
3838
name="User-created endmember",
3939
):
4040
model = MechanicalSolution(endmembers=[[m, ""] for m in mineral_list])
@@ -54,9 +54,9 @@ def __init__(
5454
"n": sum(self.mixture.formula.values()),
5555
}
5656

57-
if free_energy_adjustment != []:
58-
assert len(free_energy_adjustment) == 3
59-
dE, dS, dV = free_energy_adjustment
57+
if energy_adjustment != []:
58+
assert len(energy_adjustment) == 3
59+
dE, dS, dV = energy_adjustment
6060
self.property_modifiers = [
6161
["linear", {"delta_E": dE, "delta_S": dS, "delta_V": dV}]
6262
]

burnman/classes/elasticsolution.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def activities(self):
150150
]
151151

152152
gibbs_pure = [
153-
self.solution_model.endmembers[i][0].method.gibbs_free_energy(
153+
self.solution_model.endmembers[i][0].method.gibbs_energy(
154154
self.pressure,
155155
self.temperature,
156156
volumes[i],

burnman/classes/elasticsolutionmodel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ def entropy_hessian(self, volume, temperature, molar_fractions):
296296
def pressure_hessian(self, volume, temperature, molar_fractions):
297297
return np.zeros((len(molar_fractions), len(molar_fractions)))
298298

299-
def _configurational_entropy(self, molar_fractions):
299+
def configurational_entropy(self, molar_fractions):
300300
site_noccupancies = np.einsum(
301301
"i, ij", molar_fractions, self.endmember_noccupancies
302302
)

burnman/classes/mineral.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ def _delta_volume(pressure, volume, temperature):
224224
@copy_documentation(Material.molar_gibbs)
225225
def molar_gibbs(self):
226226
return (
227-
self.method.gibbs_free_energy(
227+
self.method.gibbs_energy(
228228
self.pressure,
229229
self.temperature,
230230
self._molar_volume_unmodified,

burnman/classes/solution.py

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def excess_partial_gibbs(self):
201201
Returns excess partial molar gibbs free energy [J/mol].
202202
Property specific to solutions.
203203
"""
204-
return self.solution_model.excess_partial_gibbs_free_energies(
204+
return self.solution_model.excess_partial_gibbs_energies(
205205
self.pressure, self.temperature, self.molar_fractions
206206
)
207207

@@ -264,7 +264,7 @@ def excess_gibbs(self):
264264
Returns molar excess gibbs free energy [J/mol].
265265
Property specific to solutions.
266266
"""
267-
return self.solution_model.excess_gibbs_free_energy(
267+
return self.solution_model.excess_gibbs_energy(
268268
self.pressure, self.temperature, self.molar_fractions
269269
)
270270

@@ -612,16 +612,25 @@ def f(i, j):
612612
@cached_property
613613
def stoichiometric_array(self):
614614
"""
615-
An array where each element arr[i,j] corresponds
615+
A numpy array where each element arr[i,j] corresponds
616616
to the number of atoms of element[j] in endmember[i].
617617
"""
618618
return np.array(self.stoichiometric_matrix)
619619

620620
@cached_property
621621
def reaction_basis(self):
622622
"""
623-
An array where each element arr[i,j] corresponds
624-
to the number of moles of endmember[j] involved in reaction[i].
623+
Returns a basis for the reaction space, represented as the left
624+
nullspace of the stoichiometric matrix.
625+
626+
Each row of the returned array corresponds to a linearly independent
627+
chemical reaction that conserves mass and charge.
628+
The value at arr[i, j] gives the number of moles of endmember[j]
629+
involved in reaction[i].
630+
631+
:returns: An array where each row defines a balanced chemical reaction
632+
in terms of molar amounts of endmembers.
633+
:rtype: numpy.ndarray
625634
"""
626635
reaction_basis = np.array(
627636
[v[:] for v in self.stoichiometric_matrix.T.nullspace()], dtype=float
@@ -641,11 +650,25 @@ def n_reactions(self):
641650

642651
@cached_property
643652
def compositional_basis(self):
644-
"""_summary_
653+
"""
654+
:returns: A basis for the compositional degrees of freedom,
655+
orthogonal to the reaction space.
645656
646-
:return: _description_
647-
:rtype: _type_
657+
Each row of the returned array defines an independent
658+
compositional constraint in terms of moles of endmembers.
659+
These constraints span the row space of the stoichiometric
660+
matrix and are complementary to the reaction basis
661+
(which spans the left null space).
662+
663+
For example, endmembers with site-species occupancies
664+
[Fe][Mg] and [Mg][Fe] have reaction basis vector [1, -1].
665+
A compositional basis vector orthogonal to this is: [1, 1],
666+
which satisfies the requirement that the Fe-Mg ratio of the
667+
system remains fixed and the total amount of the two sites
668+
remains equal.
669+
:rtype: numpy.ndarray
648670
"""
671+
649672
return complete_basis(self.reaction_basis)[self.n_reactions :]
650673

651674
@cached_property
@@ -672,8 +695,29 @@ def dependent_element_indices(self):
672695
@cached_property
673696
def compositional_null_basis(self):
674697
"""
675-
An array N such that N.b = 0 for all bulk compositions that can
676-
be produced with a linear sum of the endmembers in the solution.
698+
Returns a basis for the compositional nullspace (kernel)
699+
of the stoichiometric matrix.
700+
701+
This basis consists of vectors that represent linear constraints
702+
any valid bulk composition must satisfy to be formed from the
703+
endmembers. In other words, for any bulk composition vector `b`
704+
that can be expressed as a combination of the endmembers,
705+
multiplying it by these vectors results in zero (`N @ b = 0`).
706+
707+
These constraints typically reflect conserved properties
708+
such as elemental mass balance or charge neutrality.
709+
710+
Example: Suppose the stoichiometric matrix corresponds to
711+
elements C and O in species, and the nullspace basis vector
712+
is `[1, -2]`. Then for any bulk composition vector
713+
`b = [b_C, b_O]`, the constraint `1 * b_C - 2 * b_O = 0`
714+
must hold. This means the ratio of carbon to oxygen must
715+
satisfy that relationship to be achievable from the given
716+
endmembers.
717+
718+
:return: A 2D array where each row is a basis vector of the
719+
nullspace.
720+
:rtype: numpy.ndarray
677721
"""
678722
null = self.stoichiometric_matrix.nullspace()
679723
null_basis = np.array([v[:] for v in null])
@@ -687,15 +731,15 @@ def compositional_null_basis(self):
687731
@cached_property
688732
def endmember_formulae(self):
689733
"""
690-
A list of formulae for all the endmember in the solution.
734+
A list of formulae for all the endmembers in the solution.
691735
"""
692736
mbrs = self.solution_model.endmembers
693737
return [mbr[0].params["formula"] for mbr in mbrs]
694738

695739
@cached_property
696740
def endmember_names(self):
697741
"""
698-
A list of names for all the endmember in the solution.
742+
A list of names for all the endmembers in the solution.
699743
"""
700744
return [mbr[0].name for mbr in self.solution_model.endmembers]
701745

burnman/classes/solutionmodel.py

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def __init__(self):
185185
"""
186186
pass
187187

188-
def excess_gibbs_free_energy(self, pressure, temperature, molar_fractions):
188+
def excess_gibbs_energy(self, pressure, temperature, molar_fractions):
189189
"""
190190
Given a list of molar fractions of different phases,
191191
compute the excess Gibbs free energy of the solution.
@@ -205,9 +205,7 @@ def excess_gibbs_free_energy(self, pressure, temperature, molar_fractions):
205205
"""
206206
return np.dot(
207207
np.array(molar_fractions),
208-
self.excess_partial_gibbs_free_energies(
209-
pressure, temperature, molar_fractions
210-
),
208+
self.excess_partial_gibbs_energies(pressure, temperature, molar_fractions),
211209
)
212210

213211
def excess_volume(self, pressure, temperature, molar_fractions):
@@ -271,13 +269,11 @@ def excess_enthalpy(self, pressure, temperature, molar_fractions):
271269
:returns: The excess enthalpy of the solution.
272270
:rtype: float
273271
"""
274-
return self.excess_gibbs_free_energy(
272+
return self.excess_gibbs_energy(
275273
pressure, temperature, molar_fractions
276274
) + temperature * self.excess_entropy(pressure, temperature, molar_fractions)
277275

278-
def excess_partial_gibbs_free_energies(
279-
self, pressure, temperature, molar_fractions
280-
):
276+
def excess_partial_gibbs_energies(self, pressure, temperature, molar_fractions):
281277
"""
282278
Given a list of molar fractions of different phases,
283279
compute the excess Gibbs free energy for each endmember
@@ -375,7 +371,7 @@ def __init__(self, endmembers):
375371
self.n_endmembers = len(endmembers)
376372
self.site_formulae = [e[1] for e in endmembers]
377373

378-
def excess_gibbs_free_energy(self, pressure, temperature, molar_fractions):
374+
def excess_gibbs_energy(self, pressure, temperature, molar_fractions):
379375
return 0.0
380376

381377
def excess_volume(self, pressure, temperature, molar_fractions):
@@ -387,9 +383,7 @@ def excess_entropy(self, pressure, temperature, molar_fractions):
387383
def excess_enthalpy(self, pressure, temperature, molar_fractions):
388384
return 0.0
389385

390-
def excess_partial_gibbs_free_energies(
391-
self, pressure, temperature, molar_fractions
392-
):
386+
def excess_partial_gibbs_energies(self, pressure, temperature, molar_fractions):
393387
return np.zeros_like(molar_fractions)
394388

395389
def excess_partial_volumes(self, pressure, temperature, molar_fractions):
@@ -440,9 +434,7 @@ def _calculate_endmember_configurational_entropies(self):
440434
)
441435
self.endmember_configurational_entropies = S_conf
442436

443-
def excess_partial_gibbs_free_energies(
444-
self, pressure, temperature, molar_fractions
445-
):
437+
def excess_partial_gibbs_energies(self, pressure, temperature, molar_fractions):
446438
return self._ideal_excess_partial_gibbs(temperature, molar_fractions)
447439

448440
def excess_partial_entropies(self, pressure, temperature, molar_fractions):
@@ -462,7 +454,7 @@ def entropy_hessian(self, pressure, temperature, molar_fractions):
462454
def volume_hessian(self, pressure, temperature, molar_fractions):
463455
return np.zeros((len(molar_fractions), len(molar_fractions)))
464456

465-
def _configurational_entropy(self, molar_fractions):
457+
def configurational_entropy(self, molar_fractions):
466458
site_noccupancies = np.einsum(
467459
"i, ij", molar_fractions, self.endmember_noccupancies
468460
)
@@ -656,9 +648,7 @@ def _non_ideal_excess_partial_gibbs(self, pressure, temperature, molar_fractions
656648
Vint = self._non_ideal_interactions(self.Wv, molar_fractions)
657649
return Eint - temperature * Sint + pressure * Vint
658650

659-
def excess_partial_gibbs_free_energies(
660-
self, pressure, temperature, molar_fractions
661-
):
651+
def excess_partial_gibbs_energies(self, pressure, temperature, molar_fractions):
662652
ideal_gibbs = IdealSolution._ideal_excess_partial_gibbs(
663653
self, temperature, molar_fractions
664654
)
@@ -888,9 +878,7 @@ def _non_ideal_excess_partial_gibbs(self, pressure, temperature, molar_fractions
888878
Eint, Sint, Vint = self._non_ideal_interactions(molar_fractions)
889879
return Eint - temperature * Sint + pressure * Vint
890880

891-
def excess_partial_gibbs_free_energies(
892-
self, pressure, temperature, molar_fractions
893-
):
881+
def excess_partial_gibbs_energies(self, pressure, temperature, molar_fractions):
894882
ideal_gibbs = IdealSolution._ideal_excess_partial_gibbs(
895883
self, temperature, molar_fractions
896884
)
@@ -1031,9 +1019,7 @@ def volume_hess(pressure, temperature, molar_amounts):
10311019

10321020
self.volume_hessian = volume_hess
10331021

1034-
def excess_partial_gibbs_free_energies(
1035-
self, pressure, temperature, molar_fractions
1036-
):
1022+
def excess_partial_gibbs_energies(self, pressure, temperature, molar_fractions):
10371023
ideal_gibbs = IdealSolution._ideal_excess_partial_gibbs(
10381024
self, temperature, molar_fractions
10391025
)
@@ -1251,8 +1237,11 @@ def _make_prefactors_and_exponents(self, a):
12511237

12521238
def _make_interaction_arrays(self, Ws, n):
12531239
"""
1254-
A hidden convenience function that splits each
1255-
excess term into _summary_
1240+
A hidden convenience function that splits the user-defined
1241+
excess interaction terms into an array of interaction values
1242+
and a list of Interaction dataclass objects that facilitate
1243+
fast computation of first and second compositional derivatives
1244+
of thermodynamic properties.
12561245
12571246
:param Ws: List of interactions in form input by the user.
12581247
:type Ws: list of lists
@@ -1488,9 +1477,7 @@ def _non_ideal_excess_partial_gibbs(self, pressure, temperature, molar_fractions
14881477
gibbs += np.einsum("i, ij->j", mbr_gibbs, mbr_gradients)
14891478
return gibbs
14901479

1491-
def excess_partial_gibbs_free_energies(
1492-
self, pressure, temperature, molar_fractions
1493-
):
1480+
def excess_partial_gibbs_energies(self, pressure, temperature, molar_fractions):
14941481
ideal_gibbs = IdealSolution._ideal_excess_partial_gibbs(
14951482
self, temperature, molar_fractions
14961483
)

burnman/eos/aa.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ def molar_heat_capacity_p(self, pressure, temperature, volume, params):
344344

345345
return C_p
346346

347-
def gibbs_free_energy(self, pressure, temperature, volume, params):
347+
def gibbs_energy(self, pressure, temperature, volume, params):
348348
"""
349349
Returns the Gibbs free energy at the pressure and temperature of the mineral [J/mol]
350350
F + PV

burnman/eos/birch_murnaghan.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ def _molar_internal_energy(self, pressure, temperature, volume, params):
301301

302302
return -intPdV + params["E_0"]
303303

304-
def gibbs_free_energy(self, pressure, temperature, volume, params):
304+
def gibbs_energy(self, pressure, temperature, volume, params):
305305
"""
306306
Returns the Gibbs free energy :math:`\\mathcal{G}`
307307
of the mineral. :math:`[J/mol]`

burnman/eos/brosh_calphad.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ def _C_T(self, temperature, params):
397397

398398
return C_T
399399

400-
def gibbs_free_energy(self, pressure, temperature, volume, params):
400+
def gibbs_energy(self, pressure, temperature, volume, params):
401401
"""
402402
Returns the Gibbs free energy of the mineral. :math:`[J/mol]`
403403
"""

0 commit comments

Comments
 (0)