Skip to content

Commit 16f5ab9

Browse files
committed
added MACAW EoS
1 parent 415ab09 commit 16f5ab9

File tree

6 files changed

+244
-0
lines changed

6 files changed

+244
-0
lines changed

burnman/eos/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from .vinet import Vinet
2727
from .morse_potential import Morse
2828
from .reciprocal_kprime import RKprime
29+
from .macaw import MACAW
2930
from .dks_liquid import DKS_L
3031
from .dks_solid import DKS_S
3132
from .aa import AA

burnman/eos/helper.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from . import birch_murnaghan as bm
1212
from . import birch_murnaghan_4th as bm4
1313
from . import modified_tait as mt
14+
from . import macaw
1415
from . import dks_liquid
1516
from . import dks_solid
1617
from . import hp
@@ -70,6 +71,8 @@ def create(method):
7071
return bm4.BM4()
7172
elif method == "mt":
7273
return mt.MT()
74+
elif method == "macaw":
75+
return macaw.MACAW()
7376
elif method == "hp98":
7477
return hp.HP98()
7578
elif method == "hp_tmt":

burnman/eos/macaw.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
from __future__ import absolute_import
2+
3+
# This file is part of BurnMan - a thermoelastic and thermodynamic toolkit
4+
# for the Earth and Planetary Sciences.
5+
# Copyright (C) 2012 - 2024 by the BurnMan team, released under the GNU
6+
# GPL v2 or later.
7+
8+
9+
import scipy.optimize as opt
10+
from . import equation_of_state as eos
11+
import warnings
12+
import numpy as np
13+
14+
15+
# Try to import the jit from numba. If it is
16+
# not available, just go with the standard
17+
# python interpreter
18+
try:
19+
from numba import jit
20+
except ImportError:
21+
22+
def jit(fn):
23+
return fn
24+
25+
26+
@jit(nopython=True)
27+
def make_params(K0, K0_prime, K_infinity_prime):
28+
a = (
29+
16.0 * np.power(K0_prime, 3.0)
30+
+ 84.0 * np.power(K0_prime, 2.0)
31+
+ 192.0 * K0_prime
32+
- 972.0 * K_infinity_prime
33+
+ 1177.0
34+
)
35+
b = 2.0 * np.power(K0_prime, 2.0) + 7.0 * K0_prime - 27.0 * K_infinity_prime + 38.0
36+
omega = np.power((a + np.sqrt(a * a - 32.0 * b * b * b)), 1.0 / 3.0)
37+
C = (
38+
(11.0 / 6.0)
39+
+ (1.0 / 3.0) * K0_prime
40+
- K_infinity_prime
41+
+ (np.power(2, -1.0 / 3.0) / 6) * omega
42+
+ (np.power(2, 1.0 / 3.0) / 3) * (b / omega)
43+
)
44+
B = K_infinity_prime - 1.0
45+
A = K0 / (B - 0.5 * C + np.power(B + C, 2.0))
46+
return A, B, C
47+
48+
49+
class MACAW(eos.EquationOfState):
50+
"""
51+
Class for the MACAW equation of state
52+
detailed in Lozano and Aslam (2022; https://doi.org/10.1063/5.0076897).
53+
54+
This equation of state has no temperature dependence.
55+
"""
56+
57+
def isothermal_bulk_modulus_reuss(self, pressure, temperature, volume, params):
58+
"""
59+
Returns isothermal bulk modulus :math:`K_T` :math:`[Pa]` as a function of pressure :math:`[Pa]`,
60+
temperature :math:`[K]` and volume :math:`[m^3]`.
61+
"""
62+
A, B, C = make_params(params["K_0"], params["Kprime_0"], params["Kprime_inf"])
63+
Vrel = volume / params["V_0"]
64+
term1 = A * np.power(Vrel, -(B + 1))
65+
term2 = np.exp((2.0 / 3.0) * C * (1 - np.power(Vrel, 1.5)))
66+
term3 = np.power(C * np.power(Vrel, 1.5) + B, 2.0) - (
67+
0.5 * C * np.power(Vrel, 1.5) - B
68+
)
69+
return term1 * term2 * term3
70+
71+
def volume(self, pressure, temperature, params):
72+
"""
73+
Get the Vinet volume at a reference temperature for a given
74+
pressure :math:`[Pa]`. Returns molar volume in :math:`[m^3]`
75+
"""
76+
77+
def delta_pressure(x):
78+
return self.pressure(0.0, x, params) - pressure
79+
80+
V = opt.brentq(delta_pressure, 0.1 * params["V_0"], 1.5 * params["V_0"])
81+
return V
82+
83+
def pressure(self, temperature, volume, params):
84+
"""
85+
Returns pressure :math:`[Pa]` as a function of volume :math:`[m^3]`.
86+
"""
87+
A, B, C = make_params(params["K_0"], params["Kprime_0"], params["Kprime_inf"])
88+
Vrel = volume / params["V_0"]
89+
term1 = A * np.power(Vrel, -(B + 1.0))
90+
term2 = np.exp((2.0 / 3.0) * C * (1.0 - np.power(Vrel, 1.5)))
91+
term3 = C * np.power(Vrel, 1.5) + B
92+
return term1 * term2 * term3 - A * (B + C) + params["P_0"]
93+
94+
def molar_internal_energy(self, pressure, temperature, volume, params):
95+
"""
96+
Returns the internal energy :math:`\\mathcal{E}` of the mineral. :math:`[J/mol]`
97+
"""
98+
A, B, C = make_params(params["K_0"], params["Kprime_0"], params["Kprime_inf"])
99+
Vrel = volume / params["V_0"]
100+
I1 = -params["V_0"] * (
101+
np.power(Vrel, -B) * np.exp((2.0 / 3.0) * C * (1.0 - np.power(Vrel, 1.5)))
102+
- 1.0
103+
)
104+
I0 = (-A * (B + C) + params["P_0"]) * params["V_0"] * (Vrel - 1.0)
105+
return -A * I1 - I0
106+
107+
def gibbs_free_energy(self, pressure, temperature, volume, params):
108+
"""
109+
Returns the Gibbs free energy :math:`\\mathcal{G}` of the mineral. :math:`[J/mol]`
110+
"""
111+
return (
112+
self.molar_internal_energy(pressure, temperature, volume, params)
113+
+ pressure * volume
114+
)
115+
116+
def isentropic_bulk_modulus_reuss(self, pressure, temperature, volume, params):
117+
"""
118+
Returns adiabatic bulk modulus :math:`K_s` of the mineral. :math:`[Pa]`.
119+
"""
120+
return self.isothermal_bulk_modulus_reuss(pressure, temperature, volume, params)
121+
122+
def shear_modulus(self, pressure, temperature, volume, params):
123+
"""
124+
Returns shear modulus :math:`G` of the mineral. :math:`[Pa]`
125+
"""
126+
return 1.0e99
127+
128+
def entropy(self, pressure, temperature, volume, params):
129+
"""
130+
Returns the molar entropy :math:`\\mathcal{S}` of the mineral. :math:`[J/K/mol]`
131+
"""
132+
return 0.0
133+
134+
def molar_heat_capacity_v(self, pressure, temperature, volume, params):
135+
"""
136+
Since this equation of state does not contain temperature effects, return a very small number. :math:`[J/K/mol]`
137+
"""
138+
return 1.0e-99
139+
140+
def molar_heat_capacity_p(self, pressure, temperature, volume, params):
141+
"""
142+
Since this equation of state does not contain temperature effects, return a very small number. :math:`[J/K/mol]`
143+
"""
144+
return 1.0e-99
145+
146+
def thermal_expansivity(self, pressure, temperature, volume, params):
147+
"""
148+
Since this equation of state does not contain temperature effects, return zero. :math:`[1/K]`
149+
"""
150+
return 0.0
151+
152+
def grueneisen_parameter(self, pressure, temperature, volume, params):
153+
"""
154+
Since this equation of state does not contain temperature effects, return zero. :math:`[unitless]`
155+
"""
156+
return 0.0
157+
158+
def validate_parameters(self, params):
159+
"""
160+
Check for existence and validity of the parameters.
161+
The value for :math:`K'_{\\infty}` is thermodynamically bounded
162+
between 5/3 and :math:`K'_0` :cite:`StaceyDavis2004`.
163+
"""
164+
165+
if "E_0" not in params:
166+
params["E_0"] = 0.0
167+
if "P_0" not in params:
168+
params["P_0"] = 1.0e5
169+
170+
# Check that all the required keys are in the dictionary
171+
expected_keys = ["V_0", "K_0", "Kprime_0", "Kprime_inf"]
172+
for k in expected_keys:
173+
if k not in params:
174+
raise KeyError("params object missing parameter : " + k)
175+
176+
# Finally, check that the values are reasonable.
177+
if params["P_0"] < 0.0:
178+
warnings.warn("Unusual value for P_0", stacklevel=2)
179+
if params["V_0"] < 1.0e-7 or params["V_0"] > 1.0e-3:
180+
warnings.warn("Unusual value for V_0", stacklevel=2)
181+
if params["K_0"] < 1.0e9 or params["K_0"] > 1.0e13:
182+
warnings.warn("Unusual value for K_0", stacklevel=2)
183+
if params["Kprime_0"] < 0.0 or params["Kprime_0"] > 10.0:
184+
warnings.warn("Unusual value for Kprime_0", stacklevel=2)
185+
if params["Kprime_inf"] < 1 + 45.0 / 29.0:
186+
warnings.warn("Value for Kprime_inf below recommended value", stacklevel=2)
187+
if params["Kprime_inf"] > params["Kprime_0"]:
188+
warnings.warn("Kprime_inf should be less than Kprime_0", stacklevel=2)
156 KB
Loading
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from __future__ import absolute_import
2+
3+
from burnman.tools.eos import check_eos_consistency
4+
from burnman import Mineral
5+
import numpy as np
6+
import matplotlib.pyplot as plt
7+
import matplotlib.image as mpimg
8+
9+
HMX_params = {
10+
"P_0": 1.0e5,
11+
"V_0": 1.0e-6, # arbitrary value
12+
"K_0": 15.22e9,
13+
"Kprime_0": 7.54,
14+
"Kprime_inf": 2.63,
15+
"molar_mass": 0.296155,
16+
"equation_of_state": "macaw",
17+
}
18+
19+
HMX = Mineral(HMX_params)
20+
21+
22+
if check_eos_consistency(HMX, tol=1.0e-5, including_shear_properties=False):
23+
print("The MACAW EoS is internally consistent.\n")
24+
25+
pressures = np.linspace(0.0, 100.0e9, 6)
26+
temperatures = 0.0 + 0.0 * pressures
27+
V, K_T = HMX.evaluate(["V", "K_T"], pressures, temperatures)
28+
29+
for i in range(6):
30+
print(
31+
f"{pressures[i]/1.e9:3.0f} GPa: "
32+
f"V/V_0 = {V[i]/HMX_params['V_0']:.3f}, "
33+
"K_T = {K_T[i]/1.e9:6.2f} GPa"
34+
)
35+
36+
pressures = np.linspace(0.0, 100.0e9, 101)
37+
temperatures = 0.0 + 0.0 * pressures
38+
K_T = HMX.evaluate(["K_T"], pressures, temperatures)[0]
39+
40+
fig1 = mpimg.imread("figures/Lozano_Aslam_2022_Fig6c_HMX.png")
41+
plt.imshow(fig1, extent=[0.0, 100.0, 0, 500.0], aspect="auto")
42+
43+
plt.plot(pressures / 1.0e9, K_T / 1.0e9, linestyle=":", c="red")
44+
plt.show()

misc/ref/macaw_benchmarks.py.out

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
The MACAW EoS is internally consistent.
2+
3+
0 GPa: V/V_0 = 1.000, K_T = 15.22 GPa
4+
20 GPa: V/V_0 = 0.704, K_T = 127.87 GPa
5+
40 GPa: V/V_0 = 0.626, K_T = 218.49 GPa
6+
60 GPa: V/V_0 = 0.579, K_T = 300.87 GPa
7+
80 GPa: V/V_0 = 0.546, K_T = 378.32 GPa
8+
100 GPa: V/V_0 = 0.520, K_T = 452.36 GPa

0 commit comments

Comments
 (0)