"""
.. _delta_values_example:

=============================
EOS: Calculating Delta-values
=============================
"""

# %%
# In this tutorial we compare the equation-of-state (EOS) calculated for 7 FCC
# metals using values from :class:`~ase.calculators.emt.EMT`, WIEN2k and
# experiment. Each EOS is described by three parameters:
#
# * volume per atom
# * bulk modulus
# * pressure derivative of the bulk modulus
#
# Differences between two EOS's can be measured by a single :math:`\Delta` value
# defined as:
#
# .. math::
#
#     \Delta = \sqrt{\frac{\int_{V_a}^{V_b}
#       \left( E_1(V) - E_2(V) \right)^2 \, dV}{V_b - V_a}}
#
# where :math:`E_n(V)` is the energy per atom as a function of volume.
# The :math:`\Delta` value can be calculated using the
# :func:`ase.utils.deltacodesdft.delta` function:
#
#
# .. autofunction:: ase.utils.deltacodesdft.delta
#    :noindex:
#
# .. seealso::
#
#    * Collection of ground-state elemental crystals: :ref:`dcdft`
#    * Equation-of-state module: :mod:`ase.eos`
#
# We get the WIEN2k and experimental numbers from
# the :ref:`dcdft` ASE-collection
# and we calculate the EMT EOS using this script:

from pathlib import Path

import numpy as np

from ase.calculators.emt import EMT
from ase.collections import dcdft
from ase.io import Trajectory

trajfiles = []

for symbol in ['Al', 'Ni', 'Cu', 'Pd', 'Ag', 'Pt', 'Au']:
    trajfile = Path(f'{symbol}.traj')
    with Trajectory(trajfile, 'w') as traj:
        for s in range(94, 108, 2):
            atoms = dcdft[symbol]
            atoms.set_cell(atoms.cell * (s / 100) ** (1 / 3), scale_atoms=True)
            atoms.calc = EMT()
            atoms.get_potential_energy()
            traj.write(atoms)
    trajfiles.append(trajfile)

# %%
# And fit to a Birch-Murnaghan EOS:

import json

from ase.eos import EquationOfState as EOS
from ase.io import read


def fit(symbol: str) -> tuple[float, float, float, float]:
    V = []
    E = []
    for atoms in read(f'{symbol}.traj@:'):
        V.append(atoms.get_volume() / len(atoms))
        E.append(atoms.get_potential_energy() / len(atoms))

    eos = EOS(V, E, 'birchmurnaghan')
    eos.fit(warn=False)
    e0, B, Bp, v0 = eos.eos_parameters
    return e0, v0, B, Bp


data = {}  # Dict[str, Dict[str, float]]
for path in trajfiles:
    symbol = path.stem
    e0, v0, B, Bp = fit(symbol)
    data[symbol] = {
        'emt_energy': e0,
        'emt_volume': v0,
        'emt_B': B,
        'emt_Bp': Bp,
    }

Path('fit.json').write_text(json.dumps(data))

# %%
# Result for Pt using EMT:

import matplotlib.pyplot as plt

V, E = [], []
for atoms in read('Pt.traj@:'):
    V.append(atoms.get_volume() / len(atoms))
    E.append(atoms.get_potential_energy() / len(atoms))

eos = EOS(V, E, 'birchmurnaghan')
eos.fit(warn=False)

fig = plt.figure()
ax = fig.gca()
eos.plot(ax=ax)  # draw onto the current axes
ax.set_xlim(14.0, 16.0)
ax.set_xlabel('volume [Å^3/atom]')
ax.set_ylabel('energy [eV/atom]')
plt.tight_layout()

# %%
# Result for Pt using EMT compared to experiment and WIEN2k
# Equilibrium volumes (Å^3/atom):
#
# .. csv-table::
#    :header: symbol, emt, exp, wien2k
#
#    Pt, 15.08, 15.02, 15.64
#    Al, 15.93, 16.27, 16.48
#    Ni, 10.60, 10.81, 10.89
#    Au, 16.68, 16.82, 17.97
#    Pd, 14.59, 14.56, 15.31
#    Ag, 16.77, 16.85, 17.85
#    Cu, 11.57, 11.65, 11.95


# %%
# Bulk moduli in GPa:
#
# .. csv-table::
#    :header: symbol, emt, exp, wien2k
#
#   Pt, 278.67, 285.51, 248.71
#   Al, 39.70, 77.14, 78.08
#   Ni, 176.23, 192.46, 200.37
#   Au, 174.12, 182.01, 139.11
#   Pd, 180.43, 187.19, 168.63
#   Ag, 100.06, 105.71, 90.15
#   Cu, 134.41, 144.28, 141.33

# %%
# Pressure derivative of bulk-moduli:
#
# .. csv-table::
#    :header: symbol, emt, exp, wien2k
#
#   Pt, 5.31, 5.18, 5.46
#   Al, 2.72, 4.45, 4.57
#   Ni, 3.76, 4.00, 5.00
#   Au, 5.46, 6.40, 5.76
#   Pd, 5.17, 5.00, 5.56
#   Ag, 4.75, 4.72, 5.42
#   Cu, 4.21, 4.88, 4.86

# %%
# Now, we can calculate :math:`\Delta` between EMT and WIEN2k for Pt:
from ase.units import kJ
from ase.utils.deltacodesdft import delta

delta(15.08, 278.67 * 1e-24 * kJ, 5.31, 15.64, 248.71 * 1e-24 * kJ, 5.46)

# %%
# Here are all the `\Delta` values (in meV/atom) calculated
# with the script below:
#
# .. csv-table::
#    :header: symbol, emt-exp, emt-wien2k, exp-wien2k
#
#   Pt, 3.5, 32.2, 35.9
#   Al, 5.9, 8.6, 3.6
#   Ni, 8.6, 12.5, 3.7
#   Au, 5.9, 43.7, 39.4
#   Pd, 1.0, 27.6, 29.0
#   Ag, 1.9, 22.4, 21.3
#   Cu, 2.7, 11.9, 9.5

from ase.eos import birchmurnaghan

# Read EMT data:
data = json.loads(Path('fit.json').read_text())
# Insert values from experiment and WIEN2k:
for symbol in data:
    dcdft_dct = dcdft.data[symbol]
    dcdft_dct['exp_B'] *= 1e-24 * kJ
    dcdft_dct['wien2k_B'] *= 1e-24 * kJ
    data[symbol].update(dcdft_dct)

for name in ['volume', 'B', 'Bp']:
    with open(name + '.csv', 'w') as fd:
        print('# symbol, emt, exp, wien2k', file=fd)
        for symbol, dct in data.items():
            values = [
                dct[code + '_' + name] for code in ['emt', 'exp', 'wien2k']
            ]
            if name == 'B':
                values = [val * 1e24 / kJ for val in values]
            print(
                f'{symbol},',
                ', '.join(f'{value:.2f}' for value in values),
                file=fd,
            )

with open('delta.csv', 'w') as fd:
    print('# symbol, emt-exp, emt-wien2k, exp-wien2k', file=fd)
    for symbol, dct in data.items():
        # Get v0, B, Bp:
        emt, exp, wien2k = (
            (dct[code + '_volume'], dct[code + '_B'], dct[code + '_Bp'])
            for code in ['emt', 'exp', 'wien2k']
        )
        print(
            f'{symbol}, {delta(*emt, *exp) * 1000:.1f}, '
            f'{delta(*emt, *wien2k) * 1000:.1f}, '
            f'{delta(*exp, *wien2k) * 1000:.1f}',
            file=fd,
        )

        if symbol == 'Pt':
            va = min(emt[0], exp[0], wien2k[0])
            vb = max(emt[0], exp[0], wien2k[0])
            v = np.linspace(0.94 * va, 1.06 * vb)
            for (v0, B, Bp), code in [
                (emt, 'EMT'),
                (exp, 'experiment'),
                (wien2k, 'WIEN2k'),
            ]:
                plt.plot(v, birchmurnaghan(v, 0.0, B, Bp, v0), label=code)
            e0 = dct['emt_energy']
            V = []
            E = []
            for atoms in read('Pt.traj@:'):
                V.append(atoms.get_volume() / len(atoms))
                E.append(atoms.get_potential_energy() / len(atoms) - e0)
            plt.plot(V, E, 'o')
            plt.legend()
            plt.xlabel('volume [Ang^3]')
            plt.ylabel('energy [eV/atom]')
            plt.savefig('Pt.png')
