pyextal.jaxMultipole.model ========================== .. py:module:: pyextal.jaxMultipole.model .. autoapi-nested-parse:: Hansen-Coppens multipole model of X-ray and electron structure factors. Atomic density, in the atom's local frame (Hansen & Coppens 1978): .. math:: \rho(\mathbf r) = P_c \rho_c(r) + \sum_{\rm shells} P_v \kappa^3 \rho_v(\kappa r) + \sum_{l=0}^{l_{max}} \kappa'^3 R_l(\kappa' r) \sum_m P_{lm} d_{lm}(\hat r), with ``R_l = zeta^(n_l+3)/(n_l+2)! r^n_l exp(-zeta r)``. Its form factor, with ``k = 4 pi s`` and the engine's ``exp(+2 pi i h.r)`` phase convention, .. math:: f(\mathbf h) = P_c f_c(k) + \sum P_v f_v(k/\kappa) + \sum_l 4\pi i^l \langle j_l \rangle(k; \kappa'\zeta_l, n_l) \sum_m P_{lm} d_{lm}(\hat{\mathbf k}_{\rm loc}). The ``4 pi i^l`` comes from the plane-wave expansion; it multiplies the *same* density-normalised ``d_lm`` because ``d_lm`` is a constant multiple of the wavefunction-normalised harmonic. Crystal sums, over the whole group modulo lattice translations (inversion and centring included) divided by the order of each site's stabiliser: .. math:: F_x(\mathbf h) &= \sum_j \frac{o_j}{|S_j|} \sum_{(W,w)} f_j(W^T\mathbf h)\, T_j(W^T\mathbf h)\, e^{2\pi i \mathbf h\cdot(W\mathbf x_j + \mathbf w)} \\ U_{\mathbf h} &= \frac{\gamma}{\pi\Omega} \sum_j \frac{o_j}{|S_j|} \sum_{(W,w)} \frac{M}{s^2}\,[Z_j - f_j(W^T\mathbf h)]\, T_j\, e^{\ldots} with ``T_j(h) = exp(-h^T beta_j h)``, ``M = 0.023933754`` (Mott-Bethe), ``gamma = 1 + 1.9569341e-3 kV``, ``Omega`` the cell volume. This is exactly the engine's ``sfsub.f90`` (which transforms the reflection, not the atom, so the asymmetric atom's own frame serves for all its images). The sum over the full group also projects any populations onto the site-allowed ones, so a model can never smuggle in a forbidden multipole -- which is also how it is tested. Modes ----- ``'hc'`` The multipole model above, radial functions from an XD bank. ``'cm'`` The engine's independent-atom model: Cromer-Mann ``f_x`` from ``sftabl.sct`` with the engine's ``Z_eff`` (``sum a + c`` for two-letter labels, the true ``Z`` for ion labels; ``xtal0.f90:405-416``). Only ADPs are meaningful. Reproducing ``defaultsf(3, g)`` in this mode validates every piece of bookkeeping -- orbits, phases, ADPs, gamma, Omega -- independently of the radial functions. :param The model takes one flat *physical* vector ``p`` laid out by `Layout`: :type The model takes one flat *physical* vector ``p`` laid out by `Layout`: per :param site ``Pv`` (per valence shell): :param ``kappa`` (per shell: :param or one shared): :param : :param ``kappa_p``: :param the ``P_lm`` up to the site's ``lmax`` and the six ``U_ij``: :param (crystallographic: :param Angstrom^2). Which of them are refined: :param tied or restrained: :param is `jaxMultipole.params`'s business; the model just evaluates.: Classes ------- .. autoapisummary:: pyextal.jaxMultipole.model.AtomSpec pyextal.jaxMultipole.model.Layout pyextal.jaxMultipole.model.MultipoleModel Functions --------- .. autoapisummary:: pyextal.jaxMultipole.model.deformation_orbital pyextal.jaxMultipole.model.default_n_l Module Contents --------------- .. py:function:: deformation_orbital(config) The valence orbital whose single-zeta exponent sets the deformation functions. The one with the highest ``l``, so 3d over 4s for Ti and 2p over 2s for O; ``None`` for an atom without valence orbitals. .. py:function:: default_n_l(orbital_name, lmax) Hansen-Coppens default powers: ``n_l = max(2(n-1), l)`` of the orbital ``n``. Gives (2, 2, 2, 3, 4) for a 2p atom (O), 4 throughout for 3d (Ti, Ni), 6 for 4d, 8 for 5d and for 5s (Sr). Pass explicit ``n_l`` to follow another convention (XD uses 4, 4, 6, 8 for second-row p atoms, for instance). .. py:class:: AtomSpec How one site is modelled. :ivar site: Site label in the `Crystal`. :vartype site: str :ivar mode: ``'hc'`` or ``'cm'``. :vartype mode: str :ivar bank: XD bank (``'RDF_SCM'``, ``'PBE-QZ4P-ZORA'`` or a path). :vartype bank: str :ivar key: Bank entry; the element by default. :vartype key: str, optional :ivar valence: Valence orbitals (bank default if None). :vartype valence: list[str], optional :ivar occupations: Orbital occupation overrides. :vartype occupations: dict, optional :ivar core_from: Bank entry supplying the core. :vartype core_from: str, optional :ivar shells: ``'joined'`` (one Pv for all valence orbitals) or ``'separate'``. :vartype shells: str :ivar kappa_shared: One kappa for all valence shells. :vartype kappa_shared: bool :ivar lmax: Highest deformation order, -1 for none. :vartype lmax: int :ivar n_l: Radial powers per l; `default_n_l` if None. :vartype n_l: tuple, optional :ivar zeta: Deformation exponent (bohr^-1) before kappa' -- one value for every l, or one per l (``lmax + 1`` values; e.g. VALRAY's separate alpha_l). Twice the single-zeta exponent of `deformation_orbital` if None. :vartype zeta: float | sequence, optional :ivar frame: ``(z, x)`` direct-lattice directions of the local axes; automatic if None (`jaxMultipole.symmetry.local_frame`). :vartype frame: tuple, optional .. py:attribute:: site :type: str .. py:attribute:: mode :type: str :value: 'hc' .. py:attribute:: bank :type: str :value: 'RDF_SCM' .. py:attribute:: key :type: str :value: None .. py:attribute:: valence :type: list :value: None .. py:attribute:: occupations :type: dict :value: None .. py:attribute:: core_from :type: str :value: None .. py:attribute:: shells :type: str :value: 'joined' .. py:attribute:: kappa_shared :type: bool :value: True .. py:attribute:: lmax :type: int :value: 4 .. py:attribute:: n_l :type: tuple :value: None .. py:attribute:: zeta :type: object :value: None .. py:attribute:: frame :type: tuple :value: None .. py:class:: Layout Names and positions of the physical parameters. :ivar names: ``'Ti1.Pv'``, ``'Ti1.kappa'``, ``'O1.P20'``, ``'O1.U11'``, ... :vartype names: list[str] :ivar p0: Default values (neutral valence populations, kappa = 1, P_lm = 0, U from the crystal). :vartype p0: np.ndarray .. py:attribute:: names :type: list .. py:attribute:: p0 :type: numpy.ndarray .. py:method:: __post_init__() .. py:method:: __len__() .. py:method:: __getitem__(name) .. py:method:: __contains__(name) .. py:method:: site_names(site) .. py:class:: MultipoleModel(crystal, specs=()) Structure factors of a crystal under a multipole model. :param crystal: The crystal. :type crystal: Crystal :param specs: One spec per site, or ``label -> spec`` keyword dict; sites without a spec get ``AtomSpec(site)``. :type specs: list[AtomSpec] | dict .. py:attribute:: crystal .. py:attribute:: sites :value: [] .. py:attribute:: layout .. py:method:: _resolve(i, site, spec) .. py:method:: _build_layout() .. py:method:: geometry(hkl) Everything about a set of reflections that does not depend on ``p``. :param hkl: ``(nh, 3)`` reflections; ``000`` is not allowed. :type hkl: array-like :returns: NumPy arrays: ``hkl``, ``s``, ``k`` (bohr^-1), ``Q`` (the six ADP quadratic forms per operation), and per site ``phase`` (complex, ``(nh, nops)``) and ``Y`` (harmonics of the local directions, ``(nh, nops, (lmax+1)^2)``). :rtype: dict .. py:method:: _beta_matrix() .. py:method:: _site_terms(p, geo, j, dtype) ``(f_sph (nh,), f_aniso (nh, nops) | None, T (nh, nops))`` of site j. .. py:method:: _crystal_sum(p, geo, electron, static) .. py:method:: ug(p, geo) Electron structure factors, Angstrom^-2 (complex). .. py:method:: fx(p, geo, static=False) X-ray structure factors, electrons (complex); ``static`` drops the ADPs. .. py:method:: electrons(p) Electrons in the cell: ``sum occ * mult * (Pc + sum Pv + P00)``. .. py:method:: nuclear_charge() .. py:method:: describe() One line per site: mode, partition, frame, allowed functions.