Skip to content

Commit

Permalink
Add make_normalized_origin_and_end_members; Update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
bpuchala committed Aug 18, 2024
1 parent be31144 commit 533d1ec
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 40 deletions.
1 change: 1 addition & 0 deletions python/libcasm/composition/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
FormationEnergyCalculator,
)
from ._methods import (
make_normalized_origin_and_end_members,
make_standard_axes,
print_axes_summary,
print_axes_table,
Expand Down
69 changes: 39 additions & 30 deletions python/libcasm/composition/_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,41 +13,47 @@
)


def make_normalized_axes(
def make_normalized_origin_and_end_members(
origin_and_end_members: np.ndarray,
tol: float = libcasm.casmglobal.TOL,
) -> np.ndarray:
"""Normalize composition axes so that one unit along a parametric composition
axis correponds to a change of one site per unit cell in the occupation.
R"""Normalize compositions so that one unit along a parametric composition
axis corresponds to a change of one site per unit cell in the occupation.
See :class:`CompositionConverter` for details on definitions.
Parameters
----------
origin_and_end_members: np.ndarray
A matrix with the origin and end member compositions as columns.
tol: float = libcasm.casmglobal.TOL
A matrix with the origin, :math:`\vec{n}_0`, as the first column and
end member compositions, :math:`\vec{n}_0 + \vec{q}_i` as the
remaining columns.
tol: float = :data:`~libcasm.casmglobal.TOL`
Tolerance for comparison. Used to find composition axes such that the
parametric composition parameters are non-negative.
Returns
-------
normalized_axes: np.ndarray
A modified `origin_and_end_members` matrix with the origin and end member
compositions as columns. The end member compositions are modified so that one
unit along the composition axis corresponds to a change of one site per unit
cell in the occupation.
normalized_origin_and_end_members: np.ndarray
A modified `origin_and_end_members` matrix. The origin (first column) is not
modified. The end member compositions (subsequent columns) are modified so
that a unit distance along each composition axis corresponds to a change of
one site per unit cell in the occupation.
"""
axes = origin_and_end_members
origin = axes[:, 0]
for i in range(1, axes.shape[1]):
if not np.isclose(np.sum(axes[:, i] - origin), 0.0, atol=tol):
M = origin_and_end_members
origin = M[:, 0]
for i in range(1, M.shape[1]):
if not np.isclose(np.sum(M[:, i] - origin), 0.0, atol=tol):
raise ValueError(
f"Invalid axes: column {i} sum does not match origin sum (column 0)."
"Invalid origin_and_end_members: "
f"sum of column {i} sum does not match sum of origin (column 0)."
)
if np.allclose(axes[:, i], axes[:, 0], atol=tol):
if np.allclose(M[:, i], M[:, 0], atol=tol):
raise ValueError(
f"Invalid axes: column {i} is the same as the origin (column 0)."
"Invalid origin_and_end_members: "
f"column {i} is the same as the origin (column 0)."
)
delta = axes[:, i] - origin
delta = M[:, i] - origin
inc_sum = 0.0
dec_sum = 0.0
for dx_i in delta:
Expand All @@ -56,8 +62,8 @@ def make_normalized_axes(
elif dx_i < -tol:
dec_sum += dx_i

axes[:, i] = origin + delta / inc_sum
return axes
M[:, i] = origin + delta / inc_sum
return M


def make_standard_axes(
Expand Down Expand Up @@ -88,17 +94,17 @@ def make_standard_axes(
If True, normalize the composition axes so that going one unit along the
composition axis corresponds to a change of one site per unit cell in the
occupation. If False, one unit along the composition axis corresponds to a
change from the origin to the end member composition.
tol: float = libcasm.casmglobal.TOL
change from the origin to an extreme end member composition.
tol: float = :data:`~libcasm.casmglobal.TOL`
Tolerance for comparison. Used to find composition axes such that the
parametric composition parameters are non-negative.
Returns
-------
calculator: CompositionCalculator
calculator: :class:`CompositionCalculator`
A composition calculator object
standard_axes: list[CompositionConverter]
List of standard composition axes
standard_axes: list[:class:`CompositionConverter`]
List of :class:`CompositionConverter` for the standard composition axes
"""

## Determine defaults
Expand Down Expand Up @@ -136,13 +142,16 @@ def make_standard_axes(
if vacancy_names is None:
vacancy_names = set(["va", "Va", "VA"])

_standard_axes = make_standard_origin_and_end_members(
_standard_origin_and_end_members = make_standard_origin_and_end_members(
components=components,
allowed_occs=allowed_occs,
tol=tol,
)
if normalize:
_standard_axes = [make_normalized_axes(axes) for axes in _standard_axes]
_standard_origin_and_end_members = [
make_normalized_origin_and_end_members(axes)
for axes in _standard_origin_and_end_members
]

calculator = CompositionCalculator(
components=components,
Expand All @@ -155,7 +164,7 @@ def make_standard_axes(
origin_and_end_members=axes,
vacancy_names=vacancy_names,
)
for axes in _standard_axes
for axes in _standard_origin_and_end_members
]
return (calculator, standard_axes)

Expand Down Expand Up @@ -211,7 +220,7 @@ def print_axes_table(
keys are the indices in the list. If a dict, the printed keys are the
dict keys.
out: Optional[TextIO] = None
Output stream. Defaults to `sys.stdout
Output stream. Defaults to `sys.stdout`
"""
if isinstance(possible_axes, list):
Expand Down Expand Up @@ -268,7 +277,7 @@ def print_axes_summary(
If True, include "chem_pot(Va)" in the summary; If False (default) assume
that the vacancy chemical potential is zero.
out: Optional[TextIO] = None
Output stream. Defaults to `sys.stdout
Output stream. Defaults to `sys.stdout`
"""

Expand Down
55 changes: 45 additions & 10 deletions python/src/composition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,9 @@ PYBIND11_MODULE(_composition, m) {
The method iterates over possible choices and checks:
1. Does the current choice of K end members span the full space?
2. Try each of the chosen K end members as the origin, and let remaining define composition axes. Does this result in only positive parametric composition parameters?
2. Try each of the chosen K end members as the origin, and let remaining
define composition axes. Does this result in only positive parametric
composition parameters?
If (1) and (2) are satisfied, that choice of origin and end members are
included in the results.
Expand All @@ -372,6 +374,9 @@ PYBIND11_MODULE(_composition, m) {
are the compositions of the end members. Rows are ordered according to
the order requested by ``components``.
These "end member compositions" are the extreme compositions allowed
by `allowed_occs`.
)pbdoc");

//
Expand Down Expand Up @@ -517,13 +522,41 @@ PYBIND11_MODULE(_composition, m) {
where:
- :math:`\vec{n}`: The mol composition as number of each component species per unit cell, a vector of length :math:`s`.
- :math:`\vec{x}`: The parametric composition, a vector of length :math:`k`, giving the composition along each composition axis when referenced to the origin composition.
- :math:`\vec{n}_0`: The origin in composition space, a vector of length :math:`s`, as the number of each component species per unit cell.
- :math:`\vec{n}`: The mol composition as number of each component species
per unit cell, a vector of length :math:`s`.
- :math:`\vec{x}`: The parametric composition, a vector of length :math:`k`,
giving the composition along each composition axis when referenced to the
origin composition.
- :math:`\vec{n}_0`: The origin in composition space, a vector of length
:math:`s`, as the number of each component species per unit cell.
- :math:`s`: The number of component species (``n_components``).
- :math:`k`: The number of independent composition axes (``k``).
- :math:`Q`: Matrix of shape=(:math:`s`, :math:`k`), with columns representing the end member compositions, as number of each component species per unit cell, for each independent composition axes.
- :math:`R`: Matrix of shape=(:math:`s`, :math:`k`), with columns forming the dual-spanning basis of Q, such that :math:`\mathbf{R}^{\mathsf{T}}\mathbf{Q} = \mathbf{Q}^{\mathsf{T}}\mathbf{R} = \mathbf{I}`.
- :math:`Q`: Matrix of shape=(:math:`s`, :math:`k`), with columns representing
the change in composition per unit cell along each independent composition axis.
- :math:`R`: Matrix of shape=(:math:`s`, :math:`k`), with columns forming the
dual-spanning basis of Q, such that
:math:`\mathbf{R}^{\mathsf{T}}\mathbf{Q} = \mathbf{Q}^{\mathsf{T}}\mathbf{R} = \mathbf{I}`.
The "parametric composition axes" are the columns of :math:`Q`,
:math:`\vec{q}_i`. Due to preservation of the number of sites per
unit cell, :math:`\sum_{i} Q_{ij} = 0` (vacancies are included as a
component). If :math:`\sum_{i} |Q_{ij}| = 2`, the parametric composition
axis can be said to be normalized in the sense that a unit distance along
that axis corresponds to a change in occupation of one site per unit cell.
Notes:
- The term "endmember" usually refers to the extreme compositions in a
solid solution. In the context of CompositionConverter, the term
"end member composition" is used to mean the composition one unit
distance along a parametric composition axis,
:math:`\vec{n}_0 + \vec{q}_i`.
- When printing formulas, the characters "a", "b", "c", etc. are used to
represent the parametric compositions, :math:`x_1`, :math:`x_2`,
:math:`x_3`, etc.
- When referring to parametric composition axes, the characters "a", "b",
"c", etc. are used to represent the parametric composition axes,
:math:`\vec{q}_1`, :math:`\vec{q}_2`, :math:`\vec{q}_3`, etc.
)pbdoc")
.def(py::init<std::vector<std::string> const &, Eigen::MatrixXd,
Expand All @@ -539,9 +572,11 @@ PYBIND11_MODULE(_composition, m) {
The requested component order in the composition vectors.
origin_and_end_members: numpy.ndarray[numpy.float64[n_components, n_composition_axes+1]]
A matrix representing the origin and end member compositions
for the choice of composition axes. The composition of the origin
is the first column of each matrix, and the subsequent columns
are the compositions of the end members. Rows are ordered according to
for the choice of composition axes. The composition of the origin,
:math:`\vec{n}_0`, is the first column of each matrix, and the
subsequent columns are the "end member compositions" one unit
distance along a parametric composition axis,
:math:`\vec{n}_0 + \vec{q}_i`. Rows are ordered according to
the order requested by ``components``.
vacancy_names : set[str]
Set of component names that should be recognized as vacancies.
Expand Down Expand Up @@ -655,7 +690,7 @@ PYBIND11_MODULE(_composition, m) {
.def("end_member_formula",
&composition::CompositionConverter::end_member_formula, py::arg("i"),
R"pbdoc(
Return formula for the i-th end member, :math:`q_i`.
Return formula for the i-th end member, :math:`\vec{n}_0 + \vec{q}_i`.
)pbdoc")
.def("param_component_formula",
&composition::CompositionConverter::comp_formula, py::arg("i"),
Expand Down

0 comments on commit 533d1ec

Please sign in to comment.