Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ API
:maxdepth: 1

ctr_structure_factors
wyckoff_fitting

GUI API
-------
Expand Down
272 changes: 272 additions & 0 deletions doc/source/wyckoff_fitting.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
Wyckoff Symmetry And Fitting
============================

The CTR symmetry helpers make Wyckoff information available as metadata on
ordinary ``UnitCell`` atoms. Structure-factor calculations still use normal
``UnitCell.basis`` coordinates; symmetry is used only for queries, file
persistence, and optional fit-parameter construction.

The symmetry construction path uses optional PyXtal support. Importing
``CTRcalc``, ``CTRuc``, or ``CTRsymmetry`` does not import PyXtal. PyXtal is
only required when assigning symmetry from a structure seed or querying
Wyckoff tables. Files that already contain saved symmetry metadata can be
loaded and fitted without PyXtal.

Building A Surface Symmetry Model
---------------------------------

Use ``CTRsymmetry.model_from_seed`` to assign parent-crystal Wyckoff sites and
then build a commensurate CTR surface unit cell:

.. code-block:: python

import numpy as np
from orgui.datautils.xrayutils import CTRsymmetry

surface_spec = CTRsymmetry.SurfaceCellSpec(
parent_a=(4.653255, 4.653255, 2.9692),
parent_alpha=(90.0, 90.0, 90.0),
transform=np.asarray(
[
[1.0, 0.0, 1.0],
[-1.0, 0.0, 1.0],
[0.0, 1.0, 0.0],
]
),
layer_origins=(0.0, 0.5),
translation_range=1,
)

model = CTRsymmetry.model_from_seed(parent_structure, surface_spec, tol=1e-3)
unitcell = model.build_unitcell("RuO2")

The ``transform`` columns are surface-cell vectors expressed in parent
conventional fractional coordinates. Parent coordinates are mapped to the
surface cell as:

.. math::

p_{\mathrm{surface}} = S^{-1}(p_{\mathrm{parent}} - p_{\mathrm{origin}}).

If PyXtal standardizes or reorients the input structure, the surface transform
must be expressed in that PyXtal parent basis.

Querying Wyckoff Metadata
-------------------------

``UnitCell`` exposes resolved metadata:

.. code-block:: python

unitcell.wyckoff_sites()
unitcell.wyckoff_couplings("O_4f")
unitcell.wyckoff_site_couplings("O_4f")
unitcell.atom_wyckoff_metadata(0)

``wyckoff_sites()`` returns site dictionaries with the site id, element,
Wyckoff label, free variables, generated atom indices, space group, and status.
The status is one of:

``metadata_only``
Symmetry metadata is present, but no Wyckoff-related fit parameter has been
added.

``symmetry_preserving``
A true Wyckoff variable fit was added with ``addWyckoffParameter``.

``site_displaced``
A representative-site shift was added with ``addWyckoffShift``. This moves
all atoms in the assigned site together, but may lower the crystallographic
site symmetry.

``partially_overridden``
A manual atom-coordinate parameter touches atoms in the site.

Two Fit Modes
-------------

``addWyckoffParameter`` fits a true Wyckoff variable such as ``u``, ``v``, or
``w``. It preserves the assigned Wyckoff-site symmetry because all equivalent
atom coordinates are updated through the stored affine Wyckoff couplings:

.. code-block:: python

unitcell.addWyckoffParameter(
"O_4f",
"u",
absolute_limits=(0.28, 0.34),
)

The fitted value is a delta from the stored variable value. For rutile oxygen,
coordinates such as ``u``, ``1-u``, ``0.5+u``, and ``0.5-u`` therefore move
with the correct signs.

``addWyckoffShift`` fits a parent conventional fractional displacement of the
representative site coordinate:

.. code-block:: python

unitcell.addWyckoffShift(
"O_4f",
"x",
limits=(-0.02, 0.02),
)

Here ``"x"``, ``"y"``, and ``"z"`` are parent conventional fractional axes,
not generated surface-cell axes. The shift is propagated through the stored
space-group operation for each generated atom and then through the surface-cell
transform. This intentionally keeps the original atom assignment and atom count
while allowing the site to move away from its exact Wyckoff constraint.

Plural helpers are available for fitting several variables or shifts:

.. code-block:: python

unitcell.addWyckoffParameters("Mg_8d") # all free variables
unitcell.addWyckoffShifts("Mg_8d", axes=("x", "z"))

Container Classes
-----------------

``Film`` and ``PoissonSurface`` forward Wyckoff helper calls to their single
template unit cell:

.. code-block:: python

film.addWyckoffParameter("O_4f", "u", limits=(-0.05, 0.05))
poisson.addWyckoffShift("O_4f", "x", limits=(-0.02, 0.02))

``EpitaxyInterface`` follows the same selector convention as its existing
``addFitParameter`` API. Select the internal unit cell with ``unitcell``:

.. code-block:: python

interface.addWyckoffParameter(
"O_4f",
"u",
limits=(-0.05, 0.05),
unitcell="top",
)

interface.addWyckoffShift(
"O_4f",
"x",
limits=(-0.02, 0.02),
unitcell=["top", "bottom"],
)

When a list is supplied, one parameter is added to each selected internal unit
cell. They remain separate parameters unless a higher-level ``SXRDCrystal``
coupled parameter links them.

Linking Across Unit Cells
-------------------------

``SXRDCrystal`` provides the same two Wyckoff helper modes at the crystal
level. These helpers create one coupled crystal parameter and ordinary
relative fit parameters inside each selected unit cell.

For a symmetry-preserving Wyckoff variable:

.. code-block:: python

crystal.addWyckoffParameter(
{
"RuO2": ("O_4f", "u"),
"TiO2": ("O_4f", "u"),
},
name="shared_rutile_oxygen_u",
limits=(-0.05, 0.05),
)

For a representative-site shift:

.. code-block:: python

crystal.addWyckoffShift(
{
"RuO2": ("O_4f", "x"),
"TiO2": ("O_4f", "x"),
},
name="shared_oxygen_x_shift",
limits=(-0.02, 0.02),
)

For ``EpitaxyInterface`` components, select the internal unit cell in the
value dictionary, or use a ``(component, unitcell)`` key:

.. code-block:: python

crystal.addWyckoffParameter(
{
"interface": {
"site_id": "O_4f",
"variable": "u",
"unitcell": ("top", "bottom"),
}
},
name="shared_interface_u",
limits=(-0.05, 0.05),
)

crystal.addWyckoffShift(
{("interface", "top"): ("O_4f", "x")},
name="top_interface_oxygen_shift",
limits=(-0.02, 0.02),
)

Persistence
-----------

Symmetry-bearing ``.xtal`` and ``.xpr`` files store resolved plain-text tables
after the layer metadata. The persisted information includes:

* space group number and symbol;
* surface transform and origin;
* Wyckoff site ids, labels, variables, and representative parent coordinates;
* generated atom metadata;
* Wyckoff variable couplings;
* representative-site shift couplings.

Reloaded files can be queried and fitted with ``addWyckoffParameter`` and
``addWyckoffShift`` without PyXtal because the resolved couplings are already
present in the file.

API Reference
-------------

.. automodule:: orgui.datautils.xrayutils.CTRsymmetry
:members: SurfaceCellSpec, SurfaceSymmetryModel, possible_wyckoff_positions,
sites_from_seed, model_from_seed, surface_unitcell_from_seed
:member-order: bysource
:no-index:

.. autoclass:: orgui.datautils.xrayutils.CTRuc.UnitCell
:members: wyckoff_sites, wyckoff_couplings, wyckoff_site_couplings,
atom_wyckoff_metadata, addWyckoffParameter, addWyckoffParameters,
addWyckoffShift, addWyckoffShifts
:member-order: bysource
:no-index:

.. autoclass:: orgui.datautils.xrayutils.CTRfilm.Film
:members: addWyckoffParameter, addWyckoffParameters, addWyckoffShift,
addWyckoffShifts
:member-order: bysource
:no-index:

.. autoclass:: orgui.datautils.xrayutils.CTRfilm.EpitaxyInterface
:members: addWyckoffParameter, addWyckoffParameters, addWyckoffShift,
addWyckoffShifts
:member-order: bysource
:no-index:

.. autoclass:: orgui.datautils.xrayutils.CTRfilm.PoissonSurface
:members: addWyckoffParameter, addWyckoffParameters, addWyckoffShift,
addWyckoffShifts
:member-order: bysource
:no-index:

.. autoclass:: orgui.datautils.xrayutils.CTRcalc.SXRDCrystal
:members: addWyckoffParameter, addWyckoffShift
:member-order: bysource
:no-index:
Loading