hexrd.phase_transition.texture package

Submodules

Module contents

class hexrd.phase_transition.texture.DeLaValleePoussinKernel(halfwidth: float, crystal_symmetry: str | ndarray | None = None, sample_symmetry: str | ndarray | None = None)[source]

Bases: SO3Kernel

De la Vallée Poussin kernel on SO(3).

A radially symmetric kernel on SO(3) defined by:

K(ω) = C · cos(ω/2)^(2κ)

where ω is the misorientation angle, κ is the shape parameter, and C = B(3/2, 1/2) / B(3/2, κ + 1/2) is the normalization constant (B denotes the Beta function).

The halfwidth h is the angle at which the kernel drops to half its peak value. It is related to κ analytically by:

κ = ln(0.5) / (2 · ln(cos(h/2)))

Parameters

halfwidthfloat

Half-width parameter in radians — the angle at which K drops to half its maximum. Must be > 0, typically in [π/180, π/2].

crystal_symmetrystr or numpy.ndarray, optional

Crystal symmetry used to compute symmetry-reduced misorientation angles. Strings are interpreted as Laue group labels.

sample_symmetrystr or numpy.ndarray, optional

Sample symmetry used to compute symmetry-reduced misorientation angles. Strings are interpreted as Laue group labels, with triclinic, monoclinic, and orthorhombic sample labels mapped to their corresponding finite Laue groups.

Attributes

halfwidthfloat

Half-width parameter in radians

kappafloat

Shape parameter κ derived from half-width

norm_constantfloat

Normalization constant from the Beta function

Notes

Symmetry is opt-in and must be supplied explicitly. By default the kernel applies NO crystal or sample symmetry: symmetry-equivalent orientations are treated as distinct and the misorientation angle is the raw geometric one. To have the kernel respect symmetry - i.e. treat orientations related by a symmetry operation as identical and use the smallest equivalent misorientation - you must pass crystal_symmetry (and/or sample_symmetry). For example, a cubic material must be built with crystal_symmetry='oh'; omitting it silently ignores cubic symmetry and will overestimate misorientation angles.

Examples

>>> kernel = DeLaValleePoussinKernel(halfwidth=np.radians(15))
>>> R1 = np.eye(3)
>>> R2 = np.eye(3)  # Same orientation
>>> value = kernel.eval(R1, R2)  # Maximum value = C
>>>
>>> # Respect cubic crystal symmetry (otherwise it is ignored):
>>> kernel = DeLaValleePoussinKernel(
...     halfwidth=np.radians(15), crystal_symmetry='oh'
... )
property crystal_symmetry: str | None

str or None: Crystal symmetry label, if built from a label.

Returns None when the symmetry was supplied directly as a quaternion array, even if symmetry reduction is active. Use has_symmetry to test whether reduction is enabled.

eval(R1: ndarray, R2: ndarray) float | ndarray[source]

Evaluate de la Vallée Poussin kernel between rotations.

K(ω) = C · cos(ω/2)^(2κ)

Parameters

R1, R2array_like

Rotation matrices of shape (…, 3, 3)

Returns

float or numpy.ndarray

Kernel values, shape matches broadcasting of R1 and R2

Examples

>>> kernel = DeLaValleePoussinKernel(
...     halfwidth=np.radians(10)
... )
>>> value = kernel.eval(np.eye(3), np.eye(3))
property halfwidth: float

float: Half-width in radians (angle where K = K_max / 2).

property has_symmetry: bool

bool: Whether non-trivial symmetry reduction is enabled.

property kappa: float

float: Shape parameter κ.

misorientation_angle(R1: ndarray, R2: ndarray) float | ndarray[source]

Calculate misorientation angle between rotation matrices.

Without symmetry, uses the formula: cos(ω) = (trace(R1^T @ R2) - 1) / 2, and supports arbitrary broadcasting of R1 and R2. With crystal or sample symmetry, delegates to hexrd.core.rotations.misorientation for symmetry-reduced angles; in that case one of R1/R2 must be a single orientation (shape (3, 3)), which is reduced against the other as a batch.

Parameters

R1, R2array_like

Rotation matrices of shape (…, 3, 3)

Returns

float or numpy.ndarray

Misorientation angles in radians, shape matches input broadcasting

property norm_constant: float

float: Normalization constant from Beta function.

property sample_symmetry: str | None

str or None: Sample symmetry label, if built from a label.

Returns None when the symmetry was supplied directly as a quaternion array, even if symmetry reduction is active. Use has_symmetry to test whether reduction is enabled.

class hexrd.phase_transition.texture.SO3Kernel[source]

Bases: ABC

Abstract base class for kernels on the SO(3) rotation group.

All SO(3) kernels should inherit from this class and implement the eval() method for kernel evaluation.

abstract eval(R1: ndarray, R2: ndarray) float | ndarray[source]

Evaluate kernel between two rotations.

Parameters

R1, R2array_like

Rotation matrices of shape (…, 3, 3)

Returns

float or numpy.ndarray

Kernel values

class hexrd.phase_transition.texture.UniformODF(crystal_symmetry: str | ndarray | None = None, sample_symmetry: str | ndarray | None = None)[source]

Bases: object

Uniform (random) orientation distribution function.

Represents a completely isotropic texture where all crystal orientations are equally likely. The value is constant at 1 MRD (multiples of a random distribution), the standard normalization where the uniform distribution serves as the reference density.

Parameters

crystal_symmetrystr or numpy.ndarray, optional

Crystal symmetry as a Laue group label (‘ci’, ‘c2h’, ‘d2h’, ‘c4h’, ‘d4h’, ‘s6’, ‘d3d’, ‘c6h’, ‘d6h’, ‘th’, ‘oh’) or a quaternion symmetry array. Validated but inert (the value is 1 MRD regardless); default None.

sample_symmetrystr or numpy.ndarray, optional

Sample symmetry as a label (‘triclinic’, ‘monoclinic’, ‘orthorhombic’) or a quaternion array. Validated but inert; default None.

Attributes

valuefloat

Constant ODF value = 1.0 (MRD)

crystal_symmetrystr or None

Crystal symmetry label, or None if unset or given as an array

sample_symmetrystr or None

Sample symmetry label, or None if unset or given as an array

Examples

>>> odf = UniformODF('d6h', 'triclinic')  # hexagonal crystal
>>> orientations = np.eye(3).reshape(1, 3, 3)
>>> values = odf.eval(orientations)
>>> print(values[0])  # 1.0
analytic_texture_index() float[source]

Exact texture index J = <f^2> of the uniform ODF.

The uniform ODF is constant at 1 MRD, so J = 1 exactly.

Returns

float

Texture index, always 1.0.

property crystal_symmetry: str | None

Crystal symmetry label, or None if unset or given as an array.

eval(orientations: ndarray) float | ndarray[source]

Evaluate uniform ODF at given orientations.

For a uniform ODF, all orientations return 1.0 MRD (multiples of a random distribution).

Parameters

orientationsarray_like

Orientation matrices. Can be: - Single 3x3 rotation matrix - Array of shape (N, 3, 3) for N orientations - Any shape ending in (3, 3) for rotation matrices

Returns

float or numpy.ndarray

ODF values, all equal to 1.0 (MRD). A scalar float for a single (3, 3) orientation; otherwise an array whose shape matches the leading dimensions of the input.

Examples

>>> odf = UniformODF('oh', 'triclinic')
>>>
>>> # Single orientation
>>> R = np.eye(3)
>>> value = odf.eval(R)  # scalar
>>>
>>> # Multiple orientations
>>> Rs = np.array([np.eye(3), np.eye(3)])  # shape (2, 3, 3)
>>> values = odf.eval(Rs)  # shape (2,)
norm(n_orientations: int = 100000, seed=None) float[source]

L2 norm ||f|| = sqrt(J) (always 1.0 for a uniform ODF).

Parameters

n_orientationsint, optional

Unused; accepted for API parity with sampled ODFs.

seedint, optional

Unused; accepted for API parity with sampled ODFs.

Returns

float

L2 norm, always 1.0.

property sample_symmetry: str | None

Sample symmetry label, or None if unset or given as an array.

texture_index(n_orientations: int = 100000, seed=None) float[source]

Texture index J = <f^2> (always 1.0 for a uniform ODF).

Parameters

n_orientationsint, optional

Unused; accepted for API parity with sampled ODFs.

seedint, optional

Unused; accepted for API parity with sampled ODFs.

Returns

float

Texture index, always 1.0.

property value: float

Constant ODF value in MRD.

class hexrd.phase_transition.texture.UnimodalODF(modal_orientations, kernel, weights=None)[source]

Bases: object

Unimodal orientation distribution function.

Represents a texture with one or more preferred orientations around which crystal orientations are concentrated. Uses kernel functions to create smooth distributions around modal orientations.

Mathematical form: f(g) = Σᵢ wᵢ K(g, g₀ᵢ), Σᵢ wᵢ = 1 Because the de la Vallée Poussin kernel is normalized so its mean over SO(3) is 1 (MRD), this weighted sum is itself a valid ODF in MRD units (uniform texture = 1).

Parameters

modal_orientationsarray_like

Modal (preferred) orientation(s). Can be: - Single 3x3 rotation matrix - Array of shape (N, 3, 3) for N modal orientations

kernelDeLaValleePoussinKernel

Kernel function defining the shape of the distribution. The kernel is the single source of truth for symmetry: any crystal/sample symmetry must be set on the kernel, and the ODF exposes it via the crystal_symmetry/sample_symmetry properties.

weightsarray_like, optional

Weights for multiple modal orientations. Must sum to 1. If None, equal weights are used for multiple orientations.

Attributes

modal_orientationsnumpy.ndarray

Array of modal orientations, shape (N, 3, 3)

kernelDeLaValleePoussinKernel

Kernel function used for the distribution

weightsnumpy.ndarray

Component weights, shape (N,)

crystal_symmetrystr or None

Crystal symmetry label, delegated from the kernel

sample_symmetrystr or None

Sample symmetry label, delegated from the kernel

n_componentsint

Number of modal orientations

Examples

>>> from hexrd.phase_transition.texture import UnimodalODF
>>> from hexrd.phase_transition.texture import DeLaValleePoussinKernel
>>>
>>> # Single modal orientation (cubic crystal symmetry on the kernel)
>>> kernel = DeLaValleePoussinKernel(
...     halfwidth=np.radians(15), crystal_symmetry='oh'
... )
>>> modal = np.eye(3)  # Identity orientation
>>> odf = UnimodalODF(modal, kernel)
>>>
>>> # Evaluate at modal orientation (should give maximum value)
>>> value = odf.eval(modal)
analytic_texture_index() float | None[source]

Exact texture index J = <f^2> when a closed form is available.

For a single mode with no kernel symmetry, the de la Vallee Poussin ODF depends only on the misorientation angle from the mode, whose Haar density on SO(3) is p(omega) = (1 - cos omega) / pi. Integrating f^2 against it gives the closed form

J = (2 * C^2 / pi) * B(2*kappa + 1/2, 3/2),

where C and kappa are the kernel normalization constant and shape parameter. Multi-modal ODFs (cross terms between modes) and symmetry-reduced kernels have no simple closed form here, so this returns None and callers fall back to Monte Carlo estimation.

Returns

float or None

Exact texture index, or None if no closed form applies.

property crystal_symmetry

str or None: Crystal symmetry label, delegated from the kernel.

estimated_max_value()[source]

Estimate the maximum ODF value, in MRD.

The ODF maxima occur at (or very near) the modal orientations, so this evaluates the full ODF at each mode and returns the largest value.

Returns

float

Maximum ODF value in MRD (multiples of a random distribution)

eval(orientations)[source]

Evaluate unimodal ODF at given orientations.

Computes f(g) = Σᵢ wᵢ K(g, g₀ᵢ) for all input orientations, in MRD.

Parameters

orientationsarray_like

Orientation matrices of shape (…, 3, 3)

Returns

numpy.ndarray

ODF values with shape matching leading dimensions of input

Examples

>>> modal = np.eye(3)
>>> kernel = DeLaValleePoussinKernel(halfwidth=np.radians(10))
>>> odf = UnimodalODF(modal, kernel)
>>>
>>> # Single evaluation
>>> value = odf.eval(modal)  # Should give maximum
>>>
>>> # Batch evaluation
>>> Rs = np.array([np.eye(3), rotation_matrix_z(np.pi/4)])
>>> values = odf.eval(Rs)  # shape (2,)
property kernel

DeLaValleePoussinKernel: Kernel function.

property modal_orientations

numpy.ndarray: Modal orientations, shape (N, 3, 3).

property n_components

int: Number of modal orientations.

norm(n_orientations=100000, seed=None)[source]

L2 norm ||f|| = sqrt(J) of the ODF (MTEX norm), in MRD.

Uses the exact closed form when available; otherwise estimates the norm by Monte Carlo over Haar-uniform orientations.

Parameters

n_orientationsint, optional

Number of Haar-uniform samples for the Monte Carlo fallback, default 100000. Ignored when the closed form applies.

seedint, optional

Random seed for the Monte Carlo fallback.

Returns

float

L2 norm ||f|| = sqrt(J) (>= 1 in MRD units).

property sample_symmetry

str or None: Sample symmetry label, delegated from the kernel.

texture_index(n_orientations=100000, seed=None)[source]

Texture index J = <f^2> of the ODF, in MRD^2.

Uses the exact closed form when available (see analytic_texture_index); otherwise estimates J by Monte Carlo over Haar-uniform orientations.

Parameters

n_orientationsint, optional

Number of Haar-uniform samples for the Monte Carlo fallback, default 100000. Ignored when the closed form applies.

seedint, optional

Random seed for the Monte Carlo fallback.

Returns

float

Texture index J = <f^2> (>= 1 in MRD units).

property weights

numpy.ndarray: Component weights, shape (N,).

hexrd.phase_transition.texture.eval_odf_batch(odf, orientations, chunk_size=10000)[source]

Evaluate ODF on large batches of orientations with memory management.

For very large orientation datasets, this function processes orientations in chunks to avoid memory issues while maintaining vectorization benefits.

Parameters

odfODF object

ODF object with eval() method

orientationsarray_like

Large array of orientation matrices, shape (N, 3, 3). A single (3, 3) matrix is also accepted.

chunk_sizeint, optional

Number of orientations to process per chunk, default 10000

Returns

numpy.ndarray or float

ODF values, shape (N,) for an (N, 3, 3) input, or a scalar float for a single (3, 3) orientation.

Examples

>>> odf = UniformODF('oh', 'triclinic')
>>> # Large batch of orientations
>>> Rs = np.random.normal(size=(50000, 3, 3))  # Not actual rotations!
>>> # In practice, use proper rotation matrices
>>> values = eval_odf_batch(odf, Rs, chunk_size=5000)
hexrd.phase_transition.texture.eval_random_orientations(odf, n_orientations=1000, seed=None)[source]

Evaluate ODF at random orientations for statistical analysis.

Generates random rotation matrices and evaluates the ODF at these orientations. Useful for normalization checks and Monte Carlo integration.

Parameters

odfODF object

ODF to evaluate

n_orientationsint, optional

Number of random orientations to generate, default 1000

seedint, optional

Random seed for reproducibility

Returns

tuple of (numpy.ndarray, numpy.ndarray)

orientations : Random rotation matrices, shape (n_orientations, 3, 3) values : ODF values at these orientations, shape (n_orientations,)

Examples

>>> odf = UniformODF('oh', 'triclinic')
>>> orientations, values = eval_random_orientations(odf, n_orientations=100)
>>> print(f"Mean ODF value: {np.mean(values)}")  # Should be ~1.0 (MRD)
hexrd.phase_transition.texture.texture_index(odf, n_orientations=100000, seed=None)[source]

Monte Carlo estimate of the texture index J of an ODF.

The texture index is the mean of the squared ODF over SO(3) with the normalized Haar measure (so vol(SO(3)) = 1):

J = <f^2> = (1 / 8pi^2) * integral_{SO(3)} f(R)^2 dR

It is estimated here as <f^2> / <f>^2 over Haar-uniform random orientations. The <f>^2 denominator makes the estimate invariant to the absolute scale of the ODF, so J = 1 for the uniform (random) texture and J >= 1 for any texture, even when the ODF is not pre-normalized to mean 1 MRD (e.g. symmetry-reduced kernels, whose mean over SO(3) is the symmetry-group order rather than 1). For a properly mean-1 ODF this reduces to the usual J = <f^2>.

Parameters

odfODF object

ODF object with an eval() method (e.g., UniformODF, UnimodalODF)

n_orientationsint, optional

Number of Haar-uniform samples used for the estimate, default 100000. Sharp textures need more samples for a given accuracy.

seedint, optional

Random seed for reproducibility

Returns

float

Estimated texture index J = <f^2> (>= 1 in MRD units)

Notes

This is a stochastic estimate; its standard error decreases like 1/sqrt(n_orientations). For a single-mode de la Vallee Poussin ODF without symmetry it converges to the analytic value J = (2 C^2 / pi) * B(2*kappa + 1/2, 3/2), where C and kappa are the kernel normalization constant and shape parameter.

If the ODF exposes an analytic_texture_index() method that returns a value (not None), that exact result is used instead of sampling.

hexrd.phase_transition.texture.texture_norm(odf, n_orientations=100000, seed=None)[source]

Monte Carlo estimate of the L2 norm of an ODF (MTEX norm).

The L2 norm is the square root of the texture index:

||f|| = sqrt(<f^2>) = sqrt( (1 / 8pi^2) * integral f(R)^2 dR )

In MRD units the uniform (random) texture has norm 1; sharper textures have larger norms.

Parameters

odfODF object

ODF object with an eval() method

n_orientationsint, optional

Number of Haar-uniform samples used for the estimate, default 100000

seedint, optional

Random seed for reproducibility

Returns

float

Estimated L2 norm ||f|| = sqrt(J) (>= 1 in MRD units)

See Also

texture_index : the squared L2 norm, J = <f^2>