Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
315 changes: 29 additions & 286 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,299 +1,42 @@
.. image:: https://github.com/FABLE-3DXRD/xrd_simulator/blob/main/docs/source/images/logo.png?raw=true

.. image:: https://img.shields.io/pypi/pyversions/xrd-simulator.svg?
:target: https://pypi.org/project/xrd-simulator/

.. image:: https://github.com/FABLE-3DXRD/xrd_simulator/actions/workflows/python-package-run-tests-linux-py313.yml/badge.svg?
:target: https://github.com/FABLE-3DXRD/xrd_simulator/actions/workflows/python-package-run-tests-linux-py313.yml/badge.svg

.. image:: https://github.com/FABLE-3DXRD/xrd_simulator/actions/workflows/pages/pages-build-deployment/badge.svg?
:target: https://github.com/FABLE-3DXRD/xrd_simulator/actions/workflows/pages/pages-build-deployment/

.. image:: https://badge.fury.io/py/xrd-simulator.svg?
:target: https://pypi.org/project/xrd-simulator/

.. image:: https://anaconda.org/conda-forge/vsc-install/badges/platforms.svg?
:target: https://anaconda.org/conda-forge/xrd_simulator/

.. image:: https://anaconda.org/conda-forge/xrd_simulator/badges/latest_release_relative_date.svg?
:target: https://anaconda.org/conda-forge/xrd_simulator/

===================================================================================================
Simulate X-ray Diffraction from Polycrystals in 3D.
===================================================================================================
.. image:: https://img.shields.io/badge/stability-alpha-f4d03f.svg?
:target: https://github.com/FABLE-3DXRD/xrd_simulator/


The **X**-**R** ay **D** iffraction **SIMULATOR** package defines polycrystals as a mesh of tetrahedral single crystals
and simulates diffraction as collected by a 2D discretized detector array while the sample is rocked
around an arbitrary rotation axis. The full journal paper associated to the release of this code can be found here:

*xrd_simulator: 3D X-ray diffraction simulation software supporting 3D polycrystalline microstructure morphology descriptions
Henningsson, A. & Hall, S. A. (2023). J. Appl. Cryst. 56, 282-292.*
`https://doi.org/10.1107/S1600576722011001`_

``xrd_simulator`` was originally developed with the hope to answer questions about measurement optimization in
scanning x-ray diffraction experiments. However, ``xrd_simulator`` can simulate a wide range of experimental
diffraction setups. The essential idea is that the sample and beam topology can be arbitrarily specified,
and their interaction simulated as the sample is rocked. This means that standard "non-powder" experiments
such as `scanning-3dxrd`_ and full-field `3dxrd`_ (or HEDM if you like) can be simulated as well as more advanced
measurement sequences such as helical scans for instance. It is also possible to simulate `powder like`_
scenarios using orientation density functions as input.

===================================================================================================
Introduction
===================================================================================================
Before reading all the boring documentation (`which is hosted here`_) let's dive into some end to end
examples to get us started on a good flavour.

The ``xrd_simulator`` is built around four python objects which reflect a diffraction experiment:

* A **beam** of x-rays (using the ``xrd_simulator.beam`` module)
* A 2D area **detector** (using the ``xrd_simulator.detector`` module)
* A 3D **polycrystal** sample (using the ``xrd_simulator.polycrystal`` module)
* A rigid body sample **motion** (using the ``xrd_simulator.motion`` module)

Once these objects are defined it is possible to let the **detector** collect scattering of the **polycrystal**
as the sample undergoes the prescribed rigid body **motion** while being illuminated by the xray **beam**.

Let's go ahead and build ourselves some x-rays:

.. code:: python

import numpy as np
from xrd_simulator.beam import Beam

# The beam of xrays is represented as a convex polyhedron
# We specify the vertices in a numpy array.
beam_vertices = np.array(
[
[-1e6, -500.0, -500.0],
[-1e6, 500.0, -500.0],
[-1e6, 500.0, 500.0],
[-1e6, -500.0, 500.0],
[1e6, -500.0, -500.0],
[1e6, 500.0, -500.0],
[1e6, 500.0, 500.0],
[1e6, -500.0, 500.0],
]
)

beam = Beam(
beam_vertices,
xray_propagation_direction=np.array([1.0, 0.0, 0.0]),
wavelength=0.28523,
polarization_vector=np.array([0.0, 1.0, 0.0]),
)

We will also need to define a detector:

.. code:: python

from xrd_simulator.detector import Detector

# The detector plane is defined by it's corner coordinates det_corner_0,det_corner_1,det_corner_2
detector = Detector(
det_corner_0=np.array([142938.3, -38400.0, -38400.0]),
det_corner_1=np.array([142938.3, 38400.0, -38400.0]),
det_corner_2=np.array([142938.3, -38400.0, 38400.0]),
pixel_size=(55.0, 55.0),
gaussian_sigma=1.0,
max_gaussian_kernel_radius=5,
)

Next we go ahead and produce a sample, to do this we need to first define a mesh that
describes the topology of the sample, in this example we make the sample shaped as a ball:

.. code:: python

from xrd_simulator.mesh import TetraMesh

# xrd_simulator supports several ways to generate a mesh, here we
# generate meshed solid sphere using a level set.
mesh = TetraMesh.generate_mesh_from_levelset(
level_set=lambda x: np.linalg.norm(x) - 768.0,
bounding_radius=769.0,
max_cell_circumradius=550.0,
)

Every element in the sample is composed of some material, or "phase", we define the present phases
in a list of ``xrd_simulator.phase.Phase`` objects, in this example only a single phase is present:

.. code:: python

from xrd_simulator.phase import Phase

quartz = Phase(
unit_cell=[4.926, 4.926, 5.4189, 90.0, 90.0, 120.0],
sgname="P3221", # (Quartz)
path_to_cif_file=None, # phases can be defined from crystalographic information files
)

The polycrystal sample can now be created. In this example the crystal elements have random orientations
and the strain is uniformly zero in the sample:

.. code:: python

from scipy.spatial.transform import Rotation as R
from xrd_simulator.polycrystal import Polycrystal

orientation = R.random(mesh.number_of_elements).as_matrix()
# element_phase_map assigns each mesh element to a phase (0 = first phase)
element_phase_map = np.zeros(mesh.number_of_elements, dtype=int)
polycrystal = Polycrystal(
mesh,
orientation,
strain=np.zeros((3, 3)),
phases=quartz,
element_phase_map=element_phase_map,
)

We may save the polycrystal to disc by using the builtin ``save()`` command as

.. code:: python

polycrystal.save('my_polycrystal', save_mesh_as_xdmf=True)

We can visualize the sample by loading the .xdmf file into your favorite 3D rendering program.
In `paraview`_ the sampled colored by one of its Euler angles looks like this:

.. image:: https://github.com/FABLE-3DXRD/xrd_simulator/blob/main/docs/source/images/example_polycrystal_readme.png?raw=true
:align: center

We can now define some motion of the sample over which to integrate the diffraction signal:

.. code:: python

from xrd_simulator.motion import RigidBodyMotion

motion = RigidBodyMotion(
rotation_axis=np.array([0, 1 / np.sqrt(2), -1 / np.sqrt(2)]),
rotation_angle=np.radians(0.1),
translation=np.array([123, -153.3, 3.42]),
)

Now that we have an experimental setup we may collect diffraction by letting the beam and detector
interact with the sample:

.. code:: python

peaks_dict = polycrystal.diffract(beam, motion, detector=detector)
diffraction_pattern, peaks_dict = detector.render(
peaks_dict, frames_to_render=0, method="micro"
)

The resulting rendered detector frame will look something like the below. Note that the positions of the diffraction spots may vary as the crystal orientations were randomly generated!:

.. code:: python

import matplotlib.pyplot as plt

fig, ax = plt.subplots(1, 1, figsize=(12, 12))
# render returns (frames, height, width), take first frame
pattern = (
diffraction_pattern[0].cpu().numpy()
if hasattr(diffraction_pattern, "cpu")
else diffraction_pattern[0]
)
ax.imshow(pattern, cmap="gray", vmax=5000)
plt.show()

.. image:: https://github.com/FABLE-3DXRD/xrd_simulator/blob/main/docs/source/images/diffraction_pattern.png?raw=true
:align: center
:width: 95%

To compute several frames simply change the motion and collect the diffraction again. The sample may be moved before
each computation using the same or another motion.

.. code:: python

polycrystal.transform(motion, time=1.0)
peaks_dict = polycrystal.diffract(beam, motion, detector=detector)

Many more options for experimental setups and intensity rendering exist, have fun experimenting!
The above example code can be found as a `single .py file here.`_

======================================
Installation
Gaussian models
======================================

Anaconda installation (Linux and Macos)
=============================================
``xrd_simulator`` is distributed on the `conda-forge channel`_ and the preferred way to install
the xrd_simulator package is via `Anaconda`_::

conda install -c conda-forge xrd_simulator
conda create -n xrd_simulator
conda activate xrd_simulator

This is meant to work across OS-systems and requires an `Anaconda`_ installation.

(The conda-forge feedstock of ``xrd_simulator`` `can be found here.`_)

Anaconda installation (Windows)
======================================
``xrd_simulator`` can be installed on Windows via Anaconda. The package now uses `meshpy`_ which provides
better cross-platform support than the previous pygalmesh dependency.

Pip Installation
======================================
Pip installation is possible. The package uses `meshpy`_ which generally installs cleanly via pip::

pip install xrd-simulator

Source installation
===============================
Naturally one may also install from the sources::

git clone https://github.com/FABLE-3DXRD/xrd_simulator.git
cd xrd_simulator
python setup.py install

This will use `meshpy`_ which generally has better cross-platform support than pygalmesh.

Credits
===============================
``xrd_simulator`` makes good use of xfab and meshpy for tetrahedral mesh generation.
The source code of related repos can be found here:

* `https://github.com/FABLE-3DXRD/xfab`_
* `meshpy`_

Citation
===============================
If you feel that ``xrd_simulator`` was helpful in your research we would love for you to cite us.

*xrd_simulator: 3D X-ray diffraction simulation software supporting 3D polycrystalline microstructure morphology descriptions
Henningsson, A. & Hall, S. A. (2023). J. Appl. Cryst. 56, 282-292.*
`https://doi.org/10.1107/S1600576722011001`_

.. _https://doi.org/10.1107/S1600576722011001: https://doi.org/10.1107/S1600576722011001

.. _https://github.com/FABLE-3DXRD/xfab: https://github.com/FABLE-3DXRD/xfab

.. _https://github.com/marmakoide/miniball: https://github.com/marmakoide/miniball

.. _Anaconda: https://www.anaconda.com/products/individual

.. _meshpy: https://github.com/inducer/meshpy
Gaussian splatting [Kerbl2023] is a quite trendy set of algorithms for modeling and rendering 3D scenes from a series of images with varying viewpoint.
Par of the appeal of these new methods is that the reconstructed scenes can be rendered very efficiently with a rasterization-approach.

.. _https://github.com/inducer/meshpy: https://github.com/inducer/meshpy
We can model a polycrystal as a set of Gaussian subgrains, and under a set approximations, each such subgrain will cause a 2D-Gaussian gaussian-shaped
diffraction peak on the detector. The approach has been demonstrated and tested already in a serial-crystallography setting [Brehm2023].

.. _scanning-3dxrd: https://doi.org/10.1107/S1600576720001016
There are a number of peak-broadedning effects that we could consider to include, and as long as we assume that all of these have a Gaussian profile
and are small enough that non-linear terms can be discarded, they will continue to produce a Gaussian spot on the detector.

.. _3dxrd: https://en.wikipedia.org/wiki/3DXRD
* Grain size
* Detector point-spread
* Incidident beam bandwidth
* Incident beam angular divergence
* lattice misorientation spread (aka. mosaicity)
* Strain-broadening (probably modeled by dislocation-concentrations and contrast factors)

.. _powder like: https://en.wikipedia.org/wiki/Powder_diffraction
To limit the scope here, I implement a model with Grain-size and mosaicity only. But I will implement anisotropic misorientation densities.
The approach to derive and implement the model with more effect is essentially the same, but if you have several very-small terms, you can get into numerical
stability problems.

.. _which is hosted here: https://FABLE-3DXRD.github.io/xrd_simulator/
The geometric model of XRD
--------------------------

.. _which is hosted here: https://FABLE-3DXRD.github.io/xrd_simulator/
Given an incident beam descibed by some phase-space density, :math:`p(\mathbf{k})`, centered on a point
:math:`\mathbf{k}_0` with length :math:`2\pi/\lambda`. And given a crystallite with a "reciprocal space map"
:math:`f(\mathbf{q})` around a specific reciprocal lattice vector :math:`G_0`. We want to compute
the phase-space distribution of the scattered beam which is given by an integral:

.. _single .py file here.: https://github.com/FABLE-3DXRD/xrd_simulator/blob/main/docs/source/examples/readme_tutorial.py
..math::
I(\mathbf{p}) = \int \mathrm{d}\mathbf{k}\int \mathrm{d}\mathbf{q}
p(\mathbf{k})f(\mathbf{q})\delta(|\mathbf{k}|-|\mathbf{p}|)\delta{\mathbf{k}+\mathbf{q}-\mathbf{p}}


.. _paraview: https://www.paraview.org/

.. _can be found here.: https://github.com/conda-forge/xrd_simulator-feedstock
.. rubric:: References

.. _conda-forge channel: https://anaconda.org/conda-forge/xrd_simulator
.. [Kerbl2023] Bernhard Kerbl, Georgios Kopanas, Thomas Leimkuehler, and George Drettakis. 3d gaussian splatting for real-time radiance field rendering, 2023.
.. [Brehm2023] Brehm, W., White, T. & Chapman, H. N. (2023). Crystal diffraction prediction and partiality estimation using Gaussian basis functions. Acta Cryst. A79
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions notebooks/CsCl.cif
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# generated using pymatgen
data_CsCl
_symmetry_space_group_name_H-M 'P 1'
_cell_length_a 7.06201400
_cell_length_b 7.06201400
_cell_length_c 7.06201400
_cell_angle_alpha 90.00000000
_cell_angle_beta 90.00000000
_cell_angle_gamma 90.00000000
_symmetry_Int_Tables_number 1
_chemical_formula_structural CsCl
_chemical_formula_sum 'Cs4 Cl4'
_cell_volume 352.19705695
_cell_formula_units_Z 4
loop_
_symmetry_equiv_pos_site_id
_symmetry_equiv_pos_as_xyz
1 'x, y, z'
loop_
_atom_site_type_symbol
_atom_site_label
_atom_site_symmetry_multiplicity
_atom_site_fract_x
_atom_site_fract_y
_atom_site_fract_z
_atom_site_occupancy
Cs Cs0 1 0.00000000 0.00000000 0.00000000 1.0
Cs Cs1 1 0.00000000 0.50000000 0.50000000 1.0
Cs Cs2 1 0.50000000 0.00000000 0.50000000 1.0
Cs Cs3 1 0.50000000 0.50000000 0.00000000 1.0
Cl Cl4 1 0.50000000 0.00000000 0.00000000 1.0
Cl Cl5 1 0.50000000 0.50000000 0.50000000 1.0
Cl Cl6 1 0.00000000 0.00000000 0.50000000 1.0
Cl Cl7 1 0.00000000 0.50000000 0.00000000 1.0
Loading
Loading