Source code for aim2dat.strct.ext_analysis.warren_cowley_order_parameters
"""Functions to calculate the Warren-Cowley like order parameters."""
# Standard library imports
from statistics import mean
# Internal library imports
from aim2dat.strct.ext_analysis.decorator import external_analysis_method
from aim2dat.strct.strct import Structure
from aim2dat.utils.maths import calc_polygon_area
[docs]
@external_analysis_method
def calculate_warren_cowley_order_p(
structure: Structure, r_max: float = 20.0, max_shells: int = 3
):
"""
Calculate Warren-Cowley like order parameters as defined in :doi:`10.1103/PhysRevB.96.024104`.
Parameters
-----------
structure : aim2dat.strct.Structure
Structure object.
r_max : float (optional)
Cut-off value for the maximum distance between two atoms in angstrom.
max_shells : int
Number of neighbour shells that are evaluated.
Returns
--------
dict
Dictionary containing the order parameters.
"""
# TODO calculate in voronoi tessellation? and implement different weights
voronoi_list = structure.calculate_voronoi_tessellation(r_max)
for vlist_site in voronoi_list:
for vinfo in vlist_site:
vinfo["weight"] = calc_polygon_area(vinfo["vertices"])
kinds = structure["elements"]
kind_dict = structure._element_dict
at_fracts = {kind: len(val) / len(kinds) for kind, val in kind_dict.items()}
order_p_list = []
if len(kind_dict) == 1:
return None, {
"order_p": {kinds[0]: [0.0 for _ in range(max_shells)]},
"order_p_sites": [[0.0 for _ in range(max_shells)] for _ in kinds],
}
for site_idx, kind in enumerate(kinds):
total_weights = [0.0 for idx in range(max_shells)]
order_p = [0.0 for idx in range(max_shells)]
_calculate_order_p_recursive(
(site_idx, (0, 0, 0)),
0,
1.0,
[],
order_p,
total_weights,
kinds,
max_shells,
voronoi_list,
)
order_p_list.append(
[float(1.0 - val / at_fracts[kind] / t_w) for val, t_w in zip(order_p, total_weights)]
)
order_p_dict = {}
for kind, kind_sites in kind_dict.items():
order_p_dict[kind] = [
mean(order_p_list[idx][shell] for idx in kind_sites) for shell in range(max_shells)
]
return None, {"order_p": order_p_dict, "order_p_sites": order_p_list}
def _calculate_order_p_recursive(
current_rep, shell, cumul_w, rep_list, order_p, total_weights, kinds, max_shells, voronoi_list
):
if shell < max_shells:
orig_kind = kinds[current_rep[0]] if len(rep_list) == 0 else kinds[rep_list[0][0]]
neighbours = voronoi_list[current_rep[0]]
rep_list.append(current_rep)
for neigh_info in neighbours:
neigh_rep = (
neigh_info["index"],
tuple(val0 + val1 for val0, val1 in zip(current_rep[1], neigh_info["replica"])),
)
if any(neigh_rep == rep0 for rep0 in rep_list):
continue
weight = neigh_info["weight"] * cumul_w
total_weights[shell] += weight
if kinds[neigh_rep[0]] == orig_kind:
order_p[shell] += weight
_calculate_order_p_recursive(
neigh_rep,
shell + 1,
weight,
rep_list.copy(),
order_p,
total_weights,
kinds,
max_shells,
voronoi_list,
)