Charge densities

The classes under the chden module handle the processing and plotting of charge densities. Note that there is no explicit dependence on any of the classes used in C2a, and therefore could be used standalone. However, it only supports orthorhombic cells.

class conquest2a.chden.chden(hkl: ndarray[tuple[Any, ...], dtype[int64]], offset: float, ch1: str, ch2: str | None = None, mode: Literal['sum', 'diff'] | None = None)

Process charge density data

CONQUEST supplies up to two chden .cube files:
  • chden_up/dn.cube for a spin polarised calculation

  • a single cube file, e.g. chden.cube for unpolarised calculations

This class uses ASE’s .cube file processor to get atoms and data. It combines chden up and dn if both arguments are supplied, to make a total charge density It also does up - dn to see the spin difference, if both arguments are supplied

Parameters:
  • hkl (INT ARRAY) – The \(hkl\) slice of the crystal to plot charge densities in.

  • offset (float) – The \(hkl\) direction defines a family of planes. Use offset to select which one (i.e. where in the unit cell).

  • ch1 (str) – Path to a charge density (.cube) file.

  • ch2 (str | None, optional) – Path to another charge density (.cube) file, defaults to None.

  • mode (Literal["sum", "diff"] | None, optional) – If two charge densities are supplied, whether to sum the data or find the difference, defaults to None

Raises:
  • ValueError – Cannot provide a mode if only one file is specified.

  • ValueError – If all Miller indices are 0: cannot slice through the origin

extract_slice(n_points: int = 1000, interp_order: int = 5) tuple[Any, ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[float64]]]

Sample the charge density on the \([hkl]\) plane at fractional offset.

For each point on a 2D \((t_1, t_2)\) grid centred on the plane origin:

  1. Compute its Cartesian position:

    \(p = \text{origin} + t_1 v_1 + t_2 v_2\)

  2. Convert to fractional coordinates: s = p @ inv(cell)

  3. Wrap into the unit cell to enforce periodic boundaries.

  4. Map fractional -> voxel index and interpolate via scipy.ndimage.map_coordinates.

Parameters:
  • n_points (int, optional) – Number of points to sample on the slice, defaults to 1000

  • interp_order (int, optional) – The polynomial degree for interpolation, defaults to 5

Returns:

A tuple containing:

  • density (REAL ARRAY) – Charge density in \(e/a_0^3\), shape (n_points, n_points)

  • v1 (REAL ARRAY) – First unit vector spanning the \([hkl]\) plane

  • v2 (REAL ARRAY) – Second unit vector spanning the \([hkl]\) plane

  • t1 (REAL ARRAY) – Scalar grid along \(v_1\)

  • t2 (REAL ARRAY) – Scalar grid along \(v_2\)

  • origin (float) – Cartesian slice origin in Bohr

inplane_basis() tuple[ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[float64]]]

Return two orthonormal Cartesian vectors \((v_1, v_2)\) spanning the \([hkl]\) plane and the unit plane-normal \(\hat{n}\).

1. The plane normal in Cartesian space is \(n = ha^* + kb^* + lc^*\) where \(a^*, b^*, c^*\) are the reciprocal lattice vectors (rows of inv(cell)^T). 2. Two fractional-space null vectors of \([hkl]\) are found via SVD. 3. Those are converted to Cartesian, then Gram-Schmidt orthonormalised so the axes are perpendicular in real-space.

plane_origin() Any

Return a Cartesian point lying on the plane \(ha + kb + lc =\) offset.

offset is a dimensionless fractional intercept (0-1 spans one interplanar period). Pick the simplest fractional coordinate satisfying the plane equation.

Returns:

Origin of the slice.

Return type:

REAL ARRAY

project_atoms(v1: ndarray[tuple[Any, ...], dtype[float64]], v2: ndarray[tuple[Any, ...], dtype[float64]], n_hat: ndarray[tuple[Any, ...], dtype[float64]], origin: ndarray[tuple[Any, ...], dtype[float64]], thickness: float = 0.5) tuple[ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[float64]], list[str]]

Project atoms within thickness of the slice plane onto \((v_1, v_2)\) axes.

Parameters:
  • v1 (REAL ARRAY) – First unit vector spanning the \([hkl]\) plane

  • v2 (REAL ARRAY) – Second unit vector spanning the \([hkl]\) plane

  • n_hat (REAL ARRAY) – Unit vector defining the slice

  • origin (REAL ARRAY) – The origin of the slice

  • thickness (float, optional) – The Cartesian distance perpendicular to the plane to consider atoms as lying on the slice, defaults to 0.5 Bohr.

Returns:

A tuple containing:

  • t1_proj (REAL ARRAY) – Positions of label along \(v_1\)

  • t2_proj (REAL ARRAY) – Positions of label along \(v_2\)

  • syms (list[str]) – List of atom labels

class conquest2a.chden.chden_plot(chden_instance: chden, show_atoms: bool = False, extension: str = 'png')

Helper class to analyse and plot charge densities.

Parameters:
  • chden_instance (chden) – chden instance to use

  • show_atoms (bool, optional) – Whether to show atom labels on the charge density plot, defaults to False

  • extension (str, optional) – File extension, defaults to “png”

plot_slice(density: ndarray[tuple[Any, ...], dtype[float64]], t1: ndarray[tuple[Any, ...], dtype[float64]], t2: ndarray[tuple[Any, ...], dtype[float64]], v1: ndarray[tuple[Any, ...], dtype[float64]], v2: ndarray[tuple[Any, ...], dtype[float64]], atom_data: tuple[ndarray[tuple[Any, ...], dtype[float64]], ndarray[tuple[Any, ...], dtype[float64]], list[str]] | None = None, cmap: str = 'viridis', log_scale: bool = False, vmin: float | None = 0.0, vmax: float | None = None, interpolation: str = 'lanczos', output: str | None = None) None

Function to create the actual plot and figure instance.

Parameters:
  • density (REAL ARRAY) – Charge density

  • t1 (REAL ARRAY) – Scalar grid along \(v_1\)

  • t2 (REAL ARRAY) – Scalar grid along \(v_2\)

  • v1 (REAL ARRAY) – First unit vector spanning the \([hkl]\) plane

  • v2 (REAL ARRAY) – Second unit vector spanning the \([hkl]\) plane

  • atom_data (tuple[REAL_ARRAY, REAL_ARRAY, list[str]] | None, optional) – Data for atom labels, see chden.project_atoms(), defaults to None

  • log_scale (bool, optional) – Whether to plot the charge density on a base-10 logarithmic scale - useful for revealing details without colour clipping, defaults to False

  • vmin (float | None, optional) – Minimum value to set the colour scale at, defaults to 0.0

  • vmax (float | None, optional) – Maximum value to set the colour scale at, defaults to None

  • interpolation (str, optional) – Interpolation type, see https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html, defaults to “lanczos”

  • output (str | None, optional) – Filename to save as. Will save with a useful name if not provided, defaults to None

run(filename: str | None, vmin: float | None = 0.0, vmax: float | None = None, thickness: float = 0.5, log_scale: bool = False, cmap: str = 'viridis', interpolation: str = 'lanczos') None

Runs the full sequence of steps:

  1. Fetches charge density files

  2. Extracts and analyses data

  3. Plots data and save

Parameters:
  • filename (str | None, optional :param vmin: Minimum value to set the colour scale at, defaults to 0.0) – Filename to save as. Will save with a useful name if not provided, defaults to None

  • vmax (float | None, optional) – Maximum value to set the colour scale at, defaults to None

  • interpolation (str, optional) – Interpolation type, see https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html, defaults to “lanczos”

  • thickness (float, optional) – The Cartesian distance perpendicular to the plane to consider atoms as lying on the slice, defaults to 0.5 Bohr.

  • log_scale (bool, optional) – Whether to plot the charge density on a base-10 logarithmic scale - useful for revealing details without colour clipping, defaults to False

  • cmap (str, optional) – Colour map to use, see https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.Colormap.html#matplotlib.colors.Colormap, defaults to “viridis”