|
| 1 | +# This file is part of BurnMan - a thermoelastic and thermodynamic toolkit for |
| 2 | +# the Earth and Planetary Sciences |
| 3 | +# Copyright (C) 2012 - 2025 by the BurnMan team, released under the GNU |
| 4 | +# GPL v2 or later. |
| 5 | + |
| 6 | + |
| 7 | +""" |
| 8 | +
|
| 9 | +example_modular_mgd_with_anharmonicity |
| 10 | +-------------------------------------- |
| 11 | +
|
| 12 | +This example shows how to create a mineral object using |
| 13 | +the Modular Mie-Grüneisen-Debye With Anharmonicity equation of state. |
| 14 | +
|
| 15 | +This equation of state is derived from the Modular Mie-Grüneisen-Debye equation of state, |
| 16 | +whose use was demonstrated in example_modular_mgd.py. Like that equation of state, |
| 17 | +the modified version demonstrated here allows users to pick and choose |
| 18 | +different options for two key aspects of the mineral's behavior: |
| 19 | +
|
| 20 | +1. The equation of state (EOS) used to describe the mineral's thermodynamic properties at the reference temperature. |
| 21 | +2. The model used to describe the mineral's Debye temperature as a function of volume. |
| 22 | +
|
| 23 | +In the Modular Mie-Grüneisen-Debye equation of state, anharmonicity is incorporated |
| 24 | +via extra terms in the Helmholtz energy expression; in other words, the model is |
| 25 | +somewhat removed from the underlying quasiharmonic form |
| 26 | +(although it shares the same volume dependence on the reference Debye temperature). |
| 27 | +In the modified equation of state described in this example, anharmonic effects are |
| 28 | +more tightly coupled to the basic quasiharmonic framework, and use the thermodynamic |
| 29 | +properties of that framework to describe the mineral's anharmonic behavior. The |
| 30 | +model used here is based on the empirical ansatz introduced by Wu and Wentzcovitch |
| 31 | +(2009; https://doi.org/10.1103/PhysRevB.79.104304), simplified somewhat to avoid |
| 32 | +in internal V(P(V, T), T=0) inversion to calculate the anharmonic contributions. |
| 33 | +This has the dual benefits of reducing computational complexity and improving |
| 34 | +the shape of the thermodynamic property functions as a function of temperature, |
| 35 | +especially at high volumes. The simplification still requires only one parameter, |
| 36 | +`c_anh`, which controls the strength of the anharmonic effects, but the value of that |
| 37 | +parameter may need to be adjusted relative to the model proposed by Wu and Wentzcovitch. |
| 38 | +
|
| 39 | +*Uses:* |
| 40 | +
|
| 41 | +* :class:`burnman.Mineral` |
| 42 | +
|
| 43 | +
|
| 44 | +*Demonstrates:* |
| 45 | +
|
| 46 | +* How to create a mineral object using the Modular Mie-Grüneisen-Debye equation of state. |
| 47 | +
|
| 48 | +""" |
| 49 | +# First, a couple of standard imports |
| 50 | +import numpy as np |
| 51 | +import matplotlib.pyplot as plt |
| 52 | + |
| 53 | +# Now some specific BurnMan imports |
| 54 | +# The Mineral class is the standard class for creating mineral objects |
| 55 | +from burnman import Mineral |
| 56 | + |
| 57 | +# For this example, we will use the SLB_2011 periclase mineral |
| 58 | +# as a starting point |
| 59 | +from burnman.minerals.SLB_2011 import periclase as mineral |
| 60 | + |
| 61 | +# This helper function creates an Equation of State object |
| 62 | +from burnman.eos.helper import create |
| 63 | + |
| 64 | +# These imports contain the different models available for the EOS |
| 65 | +from burnman.eos import debye_temperature_models |
| 66 | + |
| 67 | +# Create the SLB mineral object |
| 68 | +m_SLB = mineral() |
| 69 | + |
| 70 | +# Now create a new mineral object using the modular MGD with |
| 71 | +# anharmonicity equation of state. |
| 72 | +params = m_SLB.params.copy() |
| 73 | +params["equation_of_state"] = "modular_mgd_with_anharmonicity" |
| 74 | + |
| 75 | +# In these two lines, we set the reference EOS and Debye temperature model |
| 76 | +# to be an exact clone of the original SLB object |
| 77 | +params["reference_eos"] = create("bm3") |
| 78 | +params["debye_temperature_model"] = debye_temperature_models.SLB() |
| 79 | + |
| 80 | +# Now we just need to set the anharmonicity parameter |
| 81 | +# Positive values reduce the isochoric heat capacity, as expected from |
| 82 | +# classic anharmonicity theory. |
| 83 | +# Wu and Wentzcovitch (2009) proposed a value of c=0.1 for periclase, |
| 84 | +# but as mentioned above, the model implemented here is somewhat simplified |
| 85 | +# and c_anh needs to be adjusted downwards to avoid excessive depression of |
| 86 | +# the isochoric heat capacity, Grueneisen parameter, and bulk modulus. |
| 87 | +# Note also that the quasiharmonic reference state is somewhat different to |
| 88 | +# the one used by Wu and Wentzcovitch (2009); in particular, the thermal |
| 89 | +# expansion in the SLB model is significantly larger at high temperatures: |
| 90 | +# at 0 GPa and 3000 K alpha ~ 8e-5 / K in Wu and Wentzcovitch (2009), |
| 91 | +# but in the SLB model, alpha ~ 21.6e-5 / K at the same conditions. |
| 92 | +params["c_anh"] = 0.04 |
| 93 | + |
| 94 | +# Create the new mineral object with the property modifiers from the SLB model |
| 95 | +m = Mineral(params, property_modifiers=m_SLB.property_modifiers) |
| 96 | + |
| 97 | +# And we're done! Now we can find the properties of the new mineral object |
| 98 | +# using set_state or evaluate. |
| 99 | +m.set_state(5.0e9, 4000.0) |
| 100 | + |
| 101 | +# Print some properties to standard output |
| 102 | +print( |
| 103 | + f"Properties of the new {m.name} object at " |
| 104 | + f"{m.pressure/1.e9:.1f} GPa and {m.temperature:.1f} K:" |
| 105 | +) |
| 106 | +print(f"Volume: {m.V * 1.e6:.1f} cm^3/mol") |
| 107 | +print(f"Entropy: {m.S:.1f} J/K/mol") |
| 108 | +print(f"Isochoric heat capacity: {m.molar_heat_capacity_v:.1f} J/K/mol") |
| 109 | +print(f"Isobaric heat capacity: {m.molar_heat_capacity_p:.1f} J/K/mol") |
| 110 | +print(f"Thermal expansion coefficient: {m.alpha*1.e6:.1f} * 1/MK") |
| 111 | +print(f"Grueneisen parameter: {m.gr:.2f}") |
| 112 | +print(f"Isothermal bulk modulus: {m.K_T / 1.e9:.1f} GPa") |
| 113 | +print(f"Isentropic bulk modulus: {m.K_S / 1.e9:.1f} GPa") |
| 114 | + |
| 115 | +# Create the figure and axes for the plots |
| 116 | +fig = plt.figure(figsize=(12, 6)) |
| 117 | +ax = [fig.add_subplot(2, 4, i) for i in range(1, 8)] |
| 118 | + |
| 119 | +# Create plots for the mineral properties as a function of temperature |
| 120 | +# at different pressures. |
| 121 | +temperatures = np.linspace(10.0, 3000.0, 101) |
| 122 | +for P_GPa in [0, 10, 30, 60, 100]: |
| 123 | + P = P_GPa * 1.0e9 |
| 124 | + pressures = temperatures * 0.0 + P |
| 125 | + |
| 126 | + volumes_SLB, Cv_SLB, Cp_SLB, alpha_SLB, gr_SLB, K_T_SLB = m_SLB.evaluate( |
| 127 | + [ |
| 128 | + "V", |
| 129 | + "molar_heat_capacity_v", |
| 130 | + "molar_heat_capacity_p", |
| 131 | + "alpha", |
| 132 | + "gr", |
| 133 | + "K_T", |
| 134 | + ], |
| 135 | + pressures, |
| 136 | + temperatures, |
| 137 | + ) |
| 138 | + ln = ax[0].plot(temperatures, volumes_SLB / m_SLB.params["V_0"], linestyle=":") |
| 139 | + ax[1].plot(temperatures, Cv_SLB, linestyle=":", color=ln[0].get_color()) |
| 140 | + ax[2].plot(temperatures, Cp_SLB, linestyle=":", color=ln[0].get_color()) |
| 141 | + ax[3].plot(temperatures, alpha_SLB * 1.0e5, linestyle=":", color=ln[0].get_color()) |
| 142 | + ax[4].plot(temperatures, gr_SLB, linestyle=":", color=ln[0].get_color()) |
| 143 | + ax[5].plot(temperatures, K_T_SLB / 1.0e9, linestyle=":", color=ln[0].get_color()) |
| 144 | + ax[6].plot( |
| 145 | + temperatures, |
| 146 | + alpha_SLB * K_T_SLB / 1.0e6, |
| 147 | + linestyle=":", |
| 148 | + color=ln[0].get_color(), |
| 149 | + ) |
| 150 | + |
| 151 | + volumes, Cv, Cp, alpha, gr, K_T = m.evaluate( |
| 152 | + [ |
| 153 | + "V", |
| 154 | + "molar_heat_capacity_v", |
| 155 | + "molar_heat_capacity_p", |
| 156 | + "alpha", |
| 157 | + "gr", |
| 158 | + "K_T", |
| 159 | + ], |
| 160 | + pressures, |
| 161 | + temperatures, |
| 162 | + ) |
| 163 | + ax[0].plot(temperatures, volumes / m.params["V_0"], c=ln[0].get_color()) |
| 164 | + ax[1].plot(temperatures, Cv, c=ln[0].get_color(), label=f"{P/1.e9:.0f} GPa") |
| 165 | + ax[2].plot(temperatures, Cp, c=ln[0].get_color()) |
| 166 | + ax[3].plot(temperatures, alpha * 1.0e5, c=ln[0].get_color()) |
| 167 | + ax[4].plot(temperatures, gr, c=ln[0].get_color()) |
| 168 | + ax[5].plot(temperatures, K_T / 1.0e9, c=ln[0].get_color()) |
| 169 | + ax[6].plot(temperatures, alpha * K_T / 1.0e6, c=ln[0].get_color()) |
| 170 | + |
| 171 | +ax[1].legend() |
| 172 | + |
| 173 | +for i in range(6): |
| 174 | + ax[i].set_xlabel("Temperature (K)") |
| 175 | + ax[i].set_xlim(0.0, 3000.0) |
| 176 | +ax[1].set_ylim(0.0, 80.0) |
| 177 | +ax[2].set_ylim(0.0, 80.0) |
| 178 | +ax[3].set_ylim(0.0, 10.0) |
| 179 | +ax[4].set_ylim(1.0, 2.0) |
| 180 | +ax[5].set_ylim(100.0, 300.0) |
| 181 | + |
| 182 | +ax[0].set_ylabel("$V/V_0$ (cm³/mol)") |
| 183 | +ax[1].set_ylabel("$C_V$ (J/mol/K)") |
| 184 | +ax[2].set_ylabel("$C_P$ (J/mol/K)") |
| 185 | +ax[3].set_ylabel("$\\alpha$ (10$^{{-5}}$/K)") |
| 186 | +ax[4].set_ylabel("$\\gamma$") |
| 187 | +ax[5].set_ylabel("$K_T$ (GPa)") |
| 188 | +ax[6].set_ylabel("$\\alpha K_T$ (MPa/K)") |
| 189 | + |
| 190 | +fig.set_layout_engine("tight") |
| 191 | +plt.show() |
0 commit comments