Skip to content

Commit 302c8e9

Browse files
Expand lattice strain example to include fitting a profile #85
1 parent dfd4ced commit 302c8e9

File tree

2 files changed

+98
-1
lines changed

2 files changed

+98
-1
lines changed

docs/source/gallery/examples/geochem/mineral_lattice.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,45 @@
134134
ax.legend(bbox_to_anchor=(1.05, 1))
135135
fig
136136
########################################################################################
137+
# Given the lattice strain model and a partitioning profile (for e.g. REE data),
138+
# we can also fit a model to a given curve; here we fit to our REE data above,
139+
# for which we have some known parameters to compare to:
140+
#
141+
from pyrolite.mineral.lattice import fit_lattice_strain, _lattice_opt_function
142+
143+
_ri, _tk, _D = fit_lattice_strain(np.array(site3radii), site3Ds, z=3)
144+
########################################################################################
145+
# We can compare the results of this fit to our source parameters - the ionic radius of
146+
# La, 900°C and estimated :math:`D_{La}`:
147+
#
148+
import pandas as pd
149+
150+
pd.DataFrame(
151+
[(_ri, rLa), (Tk, _tk), (D_La, _D)],
152+
index=["radii", "T", "D"],
153+
columns=["Source Parameters", "Fit Parameters"],
154+
).T
155+
156+
########################################################################################
157+
# We can also compare the curves visually:
158+
#
159+
from pyrolite.plot.spider import REE_v_radii
160+
161+
ax = REE_v_radii()
162+
163+
ax.plot(site3radii, site3Ds, label="True", color="k")
164+
ax.plot(
165+
site3radii,
166+
_lattice_opt_function(site3radii, site3radii.mean(), 298 + 1000, 1, z=3),
167+
label="Starting Guess",
168+
ls="--",
169+
color="0.5",
170+
)
171+
172+
ax.plot(site3radii, fit_lattice_strain._opt(site3radii, _ri, _tk, _D, z=3), label="Fit")
173+
174+
ax.figure
175+
########################################################################################
137176
# .. [#ref_1] Blundy, J., Wood, B., 1994. Prediction of crystal–melt partition coefficients
138177
# from elastic moduli. Nature 372, 452.
139178
# doi: `10.1038/372452A0 <https://doi.org/10.1038/372452A0>`__

pyrolite/mineral/lattice.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@
2828
2929
"""
3030

31+
from functools import partial
32+
3133
import numpy as np
34+
import pandas as pd
35+
from scipy.optimize import curve_fit
3236

3337
from ..util.log import Handle
3438
from ..util.meta import sphinx_doi_link, update_docstring_references
@@ -104,7 +108,7 @@ def strain_coefficient(ri, rx, r0=None, E=None, T=298.15, z=None, **kwargs):
104108
"""
105109
n = 6.023 * 10**23
106110
if r0 is None:
107-
logger.warn("Use fictive ideal cation radii where possible.")
111+
logger.warning("Use fictive ideal cation radii where possible.")
108112
r0 = ri
109113
ri, rx, r0 = ri / 10**10, rx / 10**10, r0 / 10**10 # convert to meters
110114
E = E or youngs_modulus_approximation(z, r0) # use E if defined, else try to calc
@@ -182,6 +186,60 @@ def youngs_modulus_approximation(z, r):
182186
return E
183187

184188

189+
def _lattice_opt_function(xs, ri, Tk, D, z=3, E=None):
190+
return D * strain_coefficient(
191+
ri, xs, r0=ri, E=E or youngs_modulus_approximation(z, ri), T=Tk
192+
)
193+
194+
195+
def fit_lattice_strain(
196+
radii,
197+
ys,
198+
E=None,
199+
z=3,
200+
bounds=[(0.1, 2.2), (273.15, 273.15 + 2700), (0, np.inf)],
201+
r0=None,
202+
t0=273.15 + 500,
203+
d0=1.0,
204+
**kwargs,
205+
):
206+
"""
207+
Fit a lattice strain model to a given set of abundances.
208+
209+
Parameters
210+
----------
211+
radii : :class:`numpy.ndarray`
212+
Radii to fit against.
213+
ys : :class:`numpy.ndarray`
214+
Partition coefficients for given elemental data.
215+
E : :class:`float`, :code:`None`
216+
Young's modulus (stiffness) for the site, in pascals (Pa). Will be estimated using
217+
:func:`youngs_modulus_approximation` if none is given.
218+
z : :class:`int`
219+
Optional specification of cationic valence, for calcuation of approximate
220+
Young's modulus using :func:`youngs_modulus_approximation`,
221+
where the modulus is not specified.
222+
bounds : :class:`list`
223+
List of tuples specifying bounds on parameters `ri`, `T` and `D`.
224+
225+
Returns
226+
-------
227+
ri, tk, D : :class:`float`
228+
Radius, temperature and partition coefficeint describing the
229+
lattice strain fit.
230+
"""
231+
232+
popt, pcov = curve_fit(
233+
partial(_lattice_opt_function, z=z, E=E),
234+
radii,
235+
ys,
236+
p0=[r0 if r0 is not None else radii.mean(), t0, d0],
237+
bounds=pd.DataFrame(bounds).T.values,
238+
**kwargs,
239+
)
240+
return popt
241+
242+
185243
__doc__ = __doc__.format(
186244
brice1975=sphinx_doi_link("10.1016/0022-0248(75)90241-9"),
187245
blundy1994=sphinx_doi_link("10.1038/372452a0"),

0 commit comments

Comments
 (0)