Changelog#
All notable changes to CrystOD, newest first.
v0.4.2#
New quantitative engine
crystod --diagram --vasp: crystal-orbital diagrams from finished VASP runs. The third engine next to extended Hückel and PySCF, and the first one that computes nothing itself:crystod --diagram -c POSCAR --co-left SrTi --co-right O3 --vasp [ROOT]reads the crystal run inROOT/BANDand the two sublattice runs inROOT/BAND_sublattice*(DIR/bandwhen it carries thePROCAR; gzipped files are accepted) and draws the same three columns from them. Which sublattice directory is the left and which the right column is decided from the real elements of its POSCAR, not from its name;--vasp-crystal/-left/-right DIRoverride the search. A sublattice run keeps one sublattice and replaces the other byVa<q><sign>point-charge species (Va2-,Va4+) of a modified VASP — a smeared Coulomb charge plus a short repulsive wall standing for the Pauli repulsion of the removed ion’s core; VASP is proprietary and is not distributed with CrystOD, the patch is available to VASP licensees on request, and--vaspitself needs only the output files. The companioncrystod --diagram ... --vasp-setup [ROOT]writes those runs’ inputs — POSCAR with the crystal’s cell and atom order and the removed atoms renamed from the resolved oxidation states, POTCAR split out of the crystal run’s (or taken from--potcar-dir/PMG_VASP_PSP_DIR, with--potcar-map EL=NAMEchoosing a variant), INCAR withLORBIT = 12and noNELECT, and one explicit KPOINTS holding the symmetry-reduced weighted mesh followed by the zero-weight special points, so one run per directory gives both the SCF and the special-point eigenvalues — and prints the three run commands without starting VASP. Levels, occupations and the complexLORBIT = 12projections come fromPROCAR; each band’s projection vector is treated as a vector in an AO-like (atom, l, m) basis and classified with the same representation matrices and character projectors CrystOD builds for a genuine AO basis, so every level carries a space-group irrep whose multiset reproduces the site-symmetry induced representation the report prints (32 of 34 manifolds of SrTiO3 exactly; a best irrep weight below 0.9 is warned about, and on SrTiO3 every level reaches 1.000). The three runs each pin their own G = 0 potential, so the crystal column is taken as the reference and every fragment level is raised onto it by a site-resolved shift: one constant per (column, element), fitted to the symmetry-forbidden probes (fragment levels whose irrep has no partner in the other sublattice at that k and which must therefore coincide with their crystal counterpart, weighted four times) and to the pure XPS-style counterparts. The shells of one atom move together, different sites do not: on SrTiO3 the Ti site, charged by covalent back-donation in the crystal, has all its levels raised 12.3 eV more than the ionic Sr site, which no single shift per column can describe. The energy zero of the page and the tables is--vasp-zero vbm|efermi|raw(defaultE − E_VBM);--vasp-align rigidfalls back to one shift per column (its numbers are printed as a diagnostic in either mode),--vasp-anchor EL nlpins a column on a chosen shell and--no-alignkeeps the raw scales. An occupied-band trace sum rule that uses no projection cross-checks the result, and the bonding/antibonding colouring — which a plane-wave calculation must read from the aligned energies because it has no overlap matrix — is active for every crystal level whose parents sit on sites whose own anchors agree within 1 eV, and suppressed (neutral grey stroke) elsewhere rather than guessed. Output:CrystOD_{cell}_vasp.htmlthrough the usual writer, aCrystOD_{cell}_vasp_levels.txtwith every number (per-level shift and the full anchor table included), a machine-readableCrystOD_{cell}_vasp_levels.jsonwith the same content, and the terminal report of the other engines. Non-spin-polarized primitive cells only, and every share is a PAW-sphere projection, blind to the radial channel. New modulescrystod/vasp_io.pyandcrystod/crystal_orbital_vasp.py; documentation: crystod. Tests in section 3, run offline againstexample/03_hybridization/vasp_SrTiO3andexample/03_hybridization/vasp_CaF2, trimmed copies of six real VASP runs restricted to the four special k points each, which reproduce every number of the untrimmed runs.The PROCAR projections are classified in the Bloch gauge CrystOD builds its permutation representation in, with no further conjugation. The engine first conjugated the representation with
Lambda_a = exp(2 pi i k . tau_a), by analogy with the PySCF engine, whose AO coefficients really are in the atomic gauge. That conjugation is an exact no-op for every site on half-integer coordinates, so neither SrTiO3 nor ScF3 could test it. Fluorite CaF2 can — its F sites sit on (1/4,1/4,1/4) and (3/4,3/4,3/4) of the primitive basis — and it shows the conjugation is wrong: with it 21 of the 85 labelled levels fall below irrep purity 0.90 (minimum 0.000 at W, 12 failures at L), the fragment manifolds stop reproducing the SALC decomposition, the Ca shell spread is 6.58 eV and the F spread 2.31 eV so both sites fail the scale test, an F 2s anchor is paired with a crystal level 13.6 eV away, and the page carries 0 bonding and 0 antibonding levels. Without it every labelled level of CaF2 has purity 1.000 at GM, L, X and W, the manifolds match the group-theory decomposition exactly, and the page comes out 30 nonbonding / 6 antibonding / 5 bonding. The three half-integer-site systems are numerically unchanged, as the no-op argument predicts. CaF2 is now a test fixture of section 3, and it is also the ionic control of the site-resolved alignment: Ca +4.54 and F +4.43 eV move together to 0.1 eV, against 12.3 eV between Ti and Sr in SrTiO3.The point-charge wall is calibrated, and it is the default.
--vasp-setupwritesVACSIGMA 0.5,VACRWALL 0.45andVACWALL = 2.5 q e^2 sqrt(2/pi)/sigma, one height perVaspecies so the charge scaling stays automatic; the new--vasp-sigmaand--vasp-wall-factorchange the first and the last, and--vasp-rwalldefaults to 0.45 instead of 0.5. The same values are the defaults of the modified VASP, so a run made by hand and a run set up by CrystOD agree. A 72-point scan over(sigma, VACRWALL, height)on SrTiO3 and ScF3 organizes the whole family by one coordinate, the radiusr10 = b sqrt(2 ln(A/10 eV))at which the wall still repels by 10 eV: below 0.7 A the anion sublattice collapses into the bare-q/rwell (occupied bands below the projection floor, negative gaps, metallic points), above 1.4 A the wall expels the anion valence shell (an O 2p manifold three times too wide, alignment shifts to +47 eV). The recommended setting sits atr10 = 0.99 Aforq = +2and 1.13 A forq = +4, the middle of the 0.85-1.25 A plateau, so ONE setting serves +2, +3 and +4; it is the joint minimum of the anchor-residual rms on both systems (SrTiO3 0.407 eV, ScF3 0.304 eV) and simultaneously the point where the anion p-manifold width matches the crystal best (-0.13 and +0.06 eV, against +0.49 and +0.40 eV before), and it transfers to CaF2 without retuning (rms 0.289 eV). What the wall chooses is the anion column’s position: across the scan the alignment shift ranges over 45 eV while the residual rms on the plateau moves by 0.1 eV. The shippedexample/SrTiO3_Pm-3mandexample/ScF3_Pm-3msublattice runs were recomputed at the new default (the SrTiO3 anion shift moves from +5.144 to +6.147 eV).Two defects the fluorite study exposed are fixed with it. The anchor pool is cut per element, with that element’s own provisional shift, instead of once per column with a rigid pre-shift: with two sites 9 eV apart that one pre-shift is wrong for one of them and silently drops a whole shell’s anchors, so a site could report
shell spread 0.20 eV -> shells agreewhen its own level table shows 1.56 eV. A shell that is drawn but contributed no anchor is now named in the report. And the parent energy a bond character is read against averages over all parents, with the deep semicore shells excluded (the criterion--valence-onlyuses for the other two engines) instead of everything below a 5 % connector weight: the old cut made both members of one three-level interaction antibonding with no bonding partner at all. The report also names, per k point, the fragment levels the drawn window cuts off the top — an empty cation d manifold can land a few hundredths of an eV above the defaultVBM + 10 eVand vanish at one k point out of four with no message.The forms of
--vasp, the optional-c, and the setting the labels belong to.--vaspnow takes no path (the current directory is the ROOT), one ROOT as before, or the three run directories in any order —--vasp ./BAND ./BAND_sublattice1 ./BAND_sublattice2, whatever they are named and wherever they live, the crystal run being the one whose POSCAR carries noVaspecies; two paths, or more than three, are refused in one line that shows the accepted forms (naming the three used to be an argparse “unrecognized arguments” error).-cis now optional with--vasp: without it the structure is the crystal run’s own POSCAR. With it, the analysis setting is the primitive cell of that file — so the irreps agree level for level with the extended-Hückel and PySCF diagrams and withcrystod -c FILE --element ... --orbital ...of the same file — and every run is mapped onto it: the same lattice (the Cartesian matrices within a relative 1e-3), one global origin shift per run, a permutation of the atoms and lattice-vector wraps, withVaatoms mapping onto the removed ones. A-cfile with Sr at the origin against runs written with Ti at the origin — the same crystal, the origin moved by (1/2,1/2,1/2) — used to stop withERROR: ./BAND/POSCAR has Ti where the primitive cell has Sr (atom 1); it is now the same analysis, and every number of it (raw and aligned energies, site shifts, projected weights, purities, shares, connector weights) is identical to the analysis in the runs’ own setting to the last printed digit. What does differ is the irrep labels, which depend on the choice of origin (SrTiO3 at R: 1+ ↔ 2-, 3+ ↔ 3-, 4+ ↔ 5-; at X: 1+ ↔ 3-, 2+ ↔ 4-, 2- ↔ 4+, 5+ ↔ 5-; at M: 1+ ↔ 4+, 2+ ↔ 3+, 2- ↔ 3-), so the mapping is stated in the terminal report, in the level table, in the JSON ("setting") and as a chip on the page whenever it is not the identity. The transport needs no per-atom Bloch phase: the PROCAR projection vector is in VASP’s periodic gauge (the gauge the permutation representation is built in, section 2 of the engine report), where a coefficient belongs to an atom and not to one image of it, so the wraps cancel against the lattice sum and the origin shift contributes the one factor exp(−2πi k·s) to every atom alike. Proved on fluorite, whose quarter-coordinate F sites are the only fixture that can see the difference: transported with the atomic-gauge wrap factor exp(−2πi k·n_a) instead, 6 of 85 labelled levels fall below irrep purity 0.90 (minimum 0.249, three levels of one L manifold split 0.249/0.251/0.469), while the bare reordering gives the reference run’s 80 levels with purity 1.000 and identical energies. Names: without-c, or with a-cfile that lives inside one of the run directories, the page is no longer named after the run directory (CrystOD_BAND_vasp.html) but after the compound and the space group —CrystOD_SrTiO3_Pm-3m_vasp.html, titledSrTiO3, Pm-3m; an ordinary-cfile keeps theCrystOD_{cell}_vasp.htmlconvention of the other engines. If the-cfile and the crystal run are the same crystal in genuinely different bases the analysis falls back to the crystal run’s own setting and says so; if they are not the same crystal, the run stops with one line naming what differs and suggesting-c BAND/POSCAR.--vasp-setupfollows the crystal run, not the-cfile:-cis optional there too (without it the structure isROOT/BAND/POSCAR), and the POSCARs it writes are nowROOT/BAND/POSCARwith one sublattice renamed toVa<q><sign>— its lattice, its origin and its ion order, line for line — with the-cfile only naming the fragments and the formal charges; it used to write them in the-csetting, so a shifted-csilently produced fragment runs on another origin and on a lattice constant 3.2e-5 Å away from the crystal run they are compared with, which the new mapping then quietly repaired. A run directory that already holds a finished calculation (OUTCAR,PROCAR,vasprun.xml) is no longer overwritten without--force: rewriting it replaced theINCARthat was actually run.example/03_hybridization/vasp_SrTiO3gained the Sr-originPOSCAR-finishof those runs and a README; tests in section 3.New command
crystod-xrd: powder X-ray diffraction patterns.crystod-xrd -c POSCARprints the Bragg peaks of the structure (h k l, multiplicity, d, 2θ, relative intensity), writes them as a comma-separated table and draws the broadened pattern as a PDF with tick marks at the peak positions. The intensities come from pymatgen’sXRDCalculator.--xraytypeselects the radiation: a Kα doublet (CuKa, the default, andMoKa,AgKa,CoKa,FeKa,CrKa) superposes the Kα1 and Kα2 patterns in the 2:1 ratio, a single line (CuKa1,CuKb,MoKa2, …) gives the monochromatic pattern, with the wavelengths of the RIETAN-FP manual.--peak-profiledraws Lorentzian (default) or Gaussian peaks, both normalized to unit area so the two figures share one intensity scale;--width,--two-thetaand--min-intensityadjust the figure and the window. Every reflection is kept by default, including the ones below pymatgen’s own 0.1 % cut-off. Families of planes that share a d spacing (cubic (3 0 0) and (2 2 1)) are listed together. This is the formerscript/xrd_pattern_poscar.py; on ScF3 the command reproduces all 36 peaks of that script to within 5×10⁻⁵ in 2θ and intensity. The Python form is the new API domaincrystod.xrd(load_structure,compute_xrd_pattern,smear_pattern,write_peak_table,plot_xrd_pattern), andcrystod-xrd --example ScF3/--example SrTiO3run on the bundled cells. Documentation: crystod-xrd. Tests in the new section 36 (21 checks, including Bragg’s law for every peak and the unit area of both profiles); worked example inexample/36_xrd_pattern.New command
crystod-search: search the Materials Project and download POSCAR files.crystod-search SrTiO3lists the matches the way the website’s table does (formula, space group, material ID, band gap, energy above hull, sites), sorted by the energy above the hull, with*after the ID of every experimentally observed material (mp-5229 *). The query is read like the website’s search box: a formula (SrTiO3,*TiO3), an anonymous formula (ABO3), a chemical system (Sr-Ti-Ofor the compounds of exactly these elements,Sr-*with a wildcard element), a list of elements (Sr,Ti,O, every material containing at least these) or material IDs (mp-5229,mp-5532). Server-side filters narrow the list:--experimental,--stable,--ehull,--band-gap,--sites,--spg,--excludeand--subsystems(a chemical system with all its subsystems), with--sortand--max(default 1000; a longer list, or--sort spg, which the server cannot sort on, is fetched in ID order and sorted locally, so no material is repeated or skipped at a page boundary).crystod-search --get mp-5532writesPOSCAR_Sr2TiO4_I4mmm_mp-5532into the current directory, and a bare--getafter a query downloads every listed material. The POSCAR holds the spglib-standardized conventional cell (14 atoms for Sr2TiO4), found at the tolerances the Materials Project assigns its space groups with (0.1 Å, 5°), so the file carries the listed space group exactly (the stored cell of Sr3Ti2O7, mp-3349, reads as C2/m at the default tolerance ofcrystod, the download as I4/mmm);--cell primitivewrites the standardized primitive cell asPPOSCAR_Sr2TiO4_I4mmm_mp-5532instead (7 atoms; the SALC analysis gives the same result for either file), so the two cells of one material never overwrite each other;--directorywrites{formula}_{space group}_{ID}/POSCAR(PPOSCAR),-onames a single file, an identical existing file is kept and a different one is replaced only with--force. The API returns material IDs in its 8-letter alphabetical form (mp-aaaaahtd); they are shown as the website shows them (mp-5229, numbers up tomp-3347529, letters above), and both spellings are accepted. The client talks to thematerials/summaryendpoint of the Materials Project REST API withrequests, now a dependency; the free API key is read fromMP_API_KEYor fromPMG_MAPI_KEYof the pymatgen settings file, and a missing, legacy or rejected key stops with a one-lineERROR:that says where to get one. The Python form is the new API domaincrystod.search(search_materials,fetch_materials,write_poscar,normalize_material_id, …), andcrystod-search --example SrTiO3/Sr-Ti-O/mp-5229run three ready-made searches. Documentation: crystod-search. Tests in the new section 37 (95 checks, run offline against a local stand-in for the API that serves data recorded from the Materials Project;CRYSTOD_LIVE_MP=1adds one live search); worked example inexample/37_mp_search.Fixed:
crystod-phonon --modulationcrashed at many q points and froze wrong structures at the others. Reported on a Cmcm structure (--qpoint 0.5 0.5 0 --mode 1 --amplitude 1.5, expected Pnma) and on I4/mmm La2-xSrxNiO4 (the two X arms--qpoint1 0 0 0.5 --mode1 1 --qpoint2 -0.5 0.5 0 --mode2 1, expected P4_2/mnm): both stopped with “RuntimeError: Coupled irrep spaces are not equivalent.” Three errors in the mode construction were behind it. spgrep’s irrep-projected rows were read as bras instead of kets; phonopy’s dynamical matrix, which already carries the phase convention the displacement representation acts on, was re-phased once more; and the symmetry analysis ran on an spglib re-standardized copy of the primitive cell (shifted by c/2 for the Cmcm structure, with three atoms wrapped by a lattice vector for the nickelate) while the dynamical matrix stayed on phonopy’s. Whether a q point crashed therefore depended on how it was written, not on which arm it was: on the shipped Sr3Ti2O7 example(0.5, 0.5, 0)ran and(-0.5, 0.5, 0), the same point, crashed; “Coupled irrep spaces with different dimensions” (ScF3 at(-0.5, 0.5, 0.5)) and “… is not scalar” were the other forms. Where it did not crash, the result was silently wrong for structures with atoms off the inversion centres: the frozen-in pattern was not the eigen-displacement of the chosen mode but a mixture of the levels of its irrep (overlap 0.31 with the correct pattern for Sr3Ti2O7 X3- mode 1), Sr3Ti2O7 X3- mode 1 at(0.5, 0.5, 0)printed Cmcm only because the space group is reported at 0.1 Å (Ama2 at 1e-5), and the two-arm direction X3-(a;a) came out Pnnm (Pnn2 at 1e-3) instead of P4_2/mnm. Structures with every atom at 0 or 1/2, such as the cubic perovskite examples, kept their space groups. One solver,crystod.symmetry_adapted_modes, now serves--modulationand--vector: it works on phonopy’s primitive cell as it is (nothing is re-standardized, so the output keeps the origin and orientation of the input) with the dynamical matrix exactly as phonopy returns it, reads the projected rows as kets, orthonormalizes repeated occurrences of one irrep (spgrep leaves them non-orthogonal; ScF3 in R32 and RuCl3 in P3_112 failed even at Γ), and checks every run against the phonopy spectrum, the eigenvalue equation of every vector and orthonormality. The two reported commands give Pnma and P4_2/mnm; every arm and everyq + Gspelling constructs, andq + Gfreezes the same structure asq.Fixed:
--modulationfroze the mass-weighted eigenvector instead of the displacement, with an arbitrary phase. The frozen-in field is now the eigen-displacemente_j exp(2 pi i q.x_j) / sqrt(m_j)— for a non-degenerate mode at a time-reversal-invariant q, the pattern phonopy’s ownMODULATIONwrites, up to its sign (the test suite compares the two); without the 1/sqrt(m), heavy atoms moved too much against light ones in every mode that moves several species (the Sc-F mode X3- 9 of ScF3: overlap 0.990 with the true pattern).--amplitudeis now the norm of the displacement of one primitive cell, exactly so at a time-reversal-invariant q (2q a reciprocal lattice vector), where every mode vector is now real; the global phase used to be whatever the eigensolver returned, so only part of the requested amplitude was frozen in (70 % for Sr3Ti2O7 X3- mode 1 at(0.5, 0.5, 0)). At those q the partners of a degenerate level are real, orthonormal and chosen, and numbered, by the isotropy subgroups they freeze into (the most symmetric first), a rule that does not depend on the origin, orientation or lattice basis of the cell, equivalent partners listed along the crystal axes where those tell them apart (--mode 1 2 3of the R4+ triplet of ScF3 rotate about a, b and c; the Si Γ triplets are x, y, z, and the Si--vectorexample files were regenerated) and by their displacement patterns where they do not (the two Pnma partners at X of Si, which used to swap with the origin of the input), and the partners of one level signed relative to each other, so single partners and equal-amplitude sums give order-parameter directions, the same domain for every origin (a complex irrep and its time-reversal partner form one real level, and the Degeneracy column shows twice the irrep dimension; its partners are fixed by the displacement patterns); at any other q the global phase is fixed by a convention only (independent of the orientation of the input) and is not physically controlled. The manual’s new “What is frozen in” section states the convention.Fixed with it:
--vector,--subgroup --modulateand the error paths.--vectorshares the solver and no longer falls back to plain phonopy eigenvectors (with a warning) where the construction failed, e.g. at the reported Cmcm q point and at the P point of I4/mmm Sr3Ti2O7.--subgroup --modulatewrites the X3-(a;a) P4_2/mnm and X3-(a;b) Pnnm structures of Sr3Ti2O7 that it reported as “no candidate reproduced”, and the Y2- (Pnma) and Y4- structures of the reported Cmcm case, which it skipped with “no structures generated”; it now also tries negative coefficients, after all positive ones, so directions such as N1+(a;-a;a;a) (I4_1/amd), (a;-a;b;b) (Imma) and (a;b;-a;b) (C2/c) of the La2-xSrxNiO4 case are generated too, and every printed reproduce command regenerates its file exactly (the amplitudes are generated with the ten significant digits the command shows; 0.3 x 0.5478 used to be printed as 0.16434 but frozen in as 0.16433999999999999).--subgroupno longer lists the acoustic modes at Γ (rigid translations) when--thresholdlies above zero; their directions could only end as “no candidate reproduced” notes. A q that no supercell of at most 12 cells per axis holds is refused before any mode is computed (0.15 used to be frozen into a 7-cell supercell on which the modulation is not periodic); a phonopy object whose primitive cell is not primitive is refused with a message pointing toprimitive_matrix="auto"; a construction that fails ends in oneERROR:line instead of a traceback. Frequencies use the phonopy object’s unit conversion factor instead of the VASP constant, and the “Inputed cell was converted into primitive cell” line is gone from--modulation. Python API:SymmetryAdaptedModulation.mode_vectorsnow holds the freezing vectors above (with 1/sqrt(m), normalized over the primitive cell), the new attributeeigenvectorsthe phonopy-convention eigenvectors;get_commensurate_supercell_sizesraisesValueErrorfor an incommensurate q;crystod.symmetry_adapted_modes.NonPrimitiveCellError(aValueError) is new.Structures written by
--modulation,--subgroup --modulateor--vectorof v0.4.1 and earlier should be regenerated — for structures with atoms off the inversion centres they are wrong whatever space group was printed, and elsewhere the domain may differ. The shipped Sr3Ti2O7 structures inexample/25_modulationwere regenerated (same space groups; the old X3- pattern overlapped the correct one by 0.31), and the directory gained the (a;a)/(a;b) structures of--subgroup --modulateand the two-arm P4_2/mnm example. The ScF3 structures there are kept as written: every command gives the same space group, and the README says which ones now come out as another domain. ItsIm-3command, whose third arm repeated the first (--qpoint3 0 0.5 0.5, which gives Immm), now reads--qpoint3 0.5 0.5 0. Tests in sections 24, 25 (including a comparison with phonopy’s own modulation) and 27.Fixed:
crystod-phonon --vibrationwrote structures of the wrong symmetry. The symmetry-only command froze the raw spgrep projected row asRe(row_j exp(2 pi i q.R)): the Bloch factorexp(2 pi i q.x_j)of each atom’s own position was missing, and the phase and, at a time-reversal-invariant q, the partner basis of the row were whatever the projection returned — the error fixed in--modulationabove. Where the missing factors happened to be equal on all atoms of the pattern (ScF3 at R written0.5 0.5 0.5) the structure came out right; elsewhere it could not be trusted. Sr3Ti2O7 X3- (--qpoint 0.5 0.5 0) froze into Ama2 in 4 of its 6 mode spaces, and at the same point written-0.5 0.5 0one space gave Cmce, the subgroup of another irrep (X3- is Cmcm,crystod-group --parent I4/mmm --irrep X3-); in all, 54 of the 144 components of the X irreps at four spellings of the two X arms were not an isotropy subgroup of their irrep, 162 of 252 for La2-xSrxNiO4 at X and N (most N components Cm or Cc instead of C2/m or C2/c), 156 of 216 for the Y irreps of the reported Cmcm SrLi2Nb2O7 (8 of the 10 Y2- spaces Pna2_1 instead of Pnma), and 9 of 108 for ScF3 at Γ, R, M and X (R1+ gave I4/mmm at-0.5 0.5 0.5). A written component is now a genuine partner of its mode space, built by the solver of--modulationwith no dynamical matrix (crystod.symmetry_adapted_modes.solve_symmetry_adapted_spaces; every mode space one level; the spaces of a repeated irrep an orthonormal basis of its patterns fixed by the structure — the atom orbit each lives on, then how the atoms move along the crystal axes, then the products of neighbouring displacements — instead of the copies spgrep’s projection returns, which depend on how q is written): at a time-reversal-invariant q real in the lattice gauge, along the directions, in the order and with the signs--modulationuses, with the Bloch factor taken from the cell the supercell is built on; at any other q a Bloch wave with the phase convention of--modulation. It is a unit-norm symmetry-adapted displacement pattern, not a normal mode (no masses, no force constants), and--amplitudeis now the displacement norm of one primitive cell at such a q. Every component of those four test sets (720 in all) now freezes into the single-arm isotropy subgroup of its irrep at a tolerance of 1e-5 (X3- Cmcm, X4- Cmme, X2+ and X3+ Cmce, X1- Ccce, X1+ Cmmm, X2- Cmcm, X4+ Cccm; Y2- and Y2+ Pnma, Y4- Pmmn, Y4+ Pbcn, Y1+ Pmma, Y1- Pnna, Y3+ Pnnm, Y3- Pbcm; ScF3 R4+ I4/mcm for each partner), and so do the 15645 labelled components at the time-reversal-invariant special points of 58 test structures in 54 space groups, apart from the rigid translations at Γ; at those pointsq,q + Gand-qwrite the same structure for the same irrep label, occurrence and component (the mode-space numbers follow the irrep order of the spelling used, as before; a complex irrep and its conjugate, which share one real space, hand out its partners in the order of their labels), an origin-shifted input gives the same space groups, and where an irrep occurs once the written pattern is the--modulationmode of that irrep (identical vectors on the same cell for ScF3 at Γ, R, M and X and for Sr3Ti2O7 and La2-xSrxNiO4 at Γ and M). The mode-space listing, labels and numbering are unchanged; the written structures, the--export-npzdisplacements and the printed displacement lines change. Python API: the newSymmetryOnlyVibrations.get_symmetry_adapted_spacesreturns the partners in the order ofdescribe_mode_spaces, andget_supercell_displacementsnow appliesexp(2 pi i q.(R + x_j)), so a projected row given to it gets its per-atom factor as well. The shippedexample/26_vibrationfiles were regenerated (the same I4/mcm; component 1 of R4+ is now the rotation about a, the structure--modulation --mode 1writes) and its README, which still showed the 0-based numbering, reproduced. Tests in section 26.Fixed with it: partners chosen by round-off where symmetry leaves the basis open. At a time-reversal-invariant q, the real space of a complex irrep and its conjugate, and a pseudo-real level, get their partners from quadratic forms of the displacements (
--modulation,--vectorand--vibrationalike). Where every atom of such a space moves on a circle, those forms were degenerate, and the eigensolver turned the basis by round-off, so the written pattern could change with how q was written (--vibrationon P-4 MnZnGa4Se8 at Γ, spelled0 -1 -1: a GM3 pattern rotated by 45°). The forms now include how each atom moves along the crystal axes, and a part that no form splits keeps its basis. The--modulation,--vectorand--subgroup --modulateruns of the reported Cmcm and La2-xSrxNiO4 cases, Sr3Ti2O7, ScF3 and Si write the same files as before.
v0.4.1#
crystod-group --parentis now the primary spelling of the isotropy-subgroup command;--supergroupstays as an alias.--supergroup SG --irrep IRsat next to--supergroup-cif FILE --subgroup-cif FILE(the symmetry-mode analysis of section 16), and the two were easy to confuse when coming back to the tool after a while: one lists the subgroups an irrep of the parent group SG can produce, the other decomposes a distorted structure. The command is thereforecrystod-group --parent Pm-3m --irrep R4+ [--order-parameter 0 0 a], named after the value it takes;--supergroup Pm-3mis accepted unchanged and gives byte-identical output, so existing scripts keep working. The help texts, error messages (--parent requires --irrep), the documentation (section 13, quick start, tutorials, MCP page), the example README and the test suite use the new spelling.crystod-group --supergroup-cif: the sublattice setting follows the orientation of the input files (reported on PbTiO3 Pm-3m → P4mm: the polarization along c of the P4mm CIF came out along −b of the cubic parent in the displacement table and in theGM4-VESTA file — crystallographically equivalent, but not the structure the user supplied). A symmetric parent leaves the sublattice basis degenerate: every basis S·W (W a point operation of the parent) pairs the atoms equally well and merely presents the same distortion in a rotated setting, 24 of them for a cubic parent, and the enumeration order used to pick one. Among the settings tied on the total distortion, the one whose child axes are rotated least against the parent axes is now chosen, both lattices taken as the input files orient them (recovered from spglib’s non-idealized standardization, which keeps the Cartesian frame of the file: a CIF sits with c along z and a in the xz plane, a POSCAR as written), so displacements and mode VESTA files follow the axes of the subgroup structure — a POSCAR whose polar axis lies along Cartesian y comes out along b. When no setting aligns the axes (standard settings that permute them, √2×√2 cells, a rhombohedral child in its hexagonal setting) the residual rotation is printed with the cell relation. Irreps, isotropy subgroups, dimensions and amplitudes of all 18 validated pairs are unchanged to the last digit; the direction labels of some pairs switch to a domain-equivalent form in the new setting (SrTiO3 R5-(a,0,0)instead of(0,0,a)), and the example VESTA files were regenerated. Tests in section 16 (PbTiO3 polarized along c and along Cartesian y).
v0.4.0#
PySCF is now an optional dependency (breaking change).
pip install CrystODno longer pulls in PySCF (about 500 MB with its dependencies); the quantitative engines —crystod --diagram/--band/--dos/--visualize --pyscfandcrystod-mol --diagram --pyscf— needpip install "CrystOD[quantum]"once. Everything else (SALC irreps, extended-Hückel crystal-orbital and MO diagrams, group theory, phonon irreps, modulations, spin bases, Brillouin zones, molecular SALCs) runs without it. A--pyscfrun in an environment without PySCF stops with one line that names the install command instead of a traceback, and the API classescrystod.salc.PySCFCrystalOrbitalDiagram/crystod.mol.PyscfDiagramraiseImportErrorwith the same hint (crystod/_optional.py). Thepyscf<2.14cap moves into the extra. Verified by installing the built wheel into a fresh environment without PySCF: every command imports, the bundled examples run, and both--pyscfpaths fail with the one-line message. New extras:quantum,doc,dev(= quantum + doc + ruff, nbconvert, ipykernel, build, twine) andall.--example NAME: the quick start runs right afterpip install.crystod,crystod-phonon,crystod-molandcrystod-bzgained--example. A few small inputs ship inside the package (crystod/examples/: the ScF3 and SrTiO3 cells, the 4×4×4FORCE_SETSof SrTiO3, CH4 and NH3 — 108 kB in total), and--example NAMEcopies the files of that example into the working directory, prints the ordinary command line it stands for (Running: crystod -c 221_PPOSCAR_ScF3 --element Sc --orbital d) and runs it, so the files are there to edit and re-run.--examplealone lists the names (ScF3_d,SrTiO3_d,ScF3_diagram;SrTiO3,SrTiO3_subgroup;CH4,NH3;ScF3). An existing identical file is left alone, a differing file of the same name is never overwritten, and further arguments pass through (crystod-phonon --example SrTiO3 --tolerance 1e-3). The registry (crystod.examples) is shared by the tutorials and the MCP server. Tests in sections 6, 20, 27 and 34.--kpointaccepts a high-symmetry label in every mode ofcrystod.crystod -c POSCAR --atomic-orbital Sc-d F-p --kpoint R— the form the documentation had shown all along — was rejected in the SALC and hybridization modes (”–kpoint requires three coordinates”): labels were resolved only by--star-of-k,--visualizeand--diagram. The label is now looked up in the ISO-IR special-point table the survey itself uses — the namescrystod-bz --show-kpointprints (GM,X,M,R;GM,Y,A,V,L,Mfor C2/m), resolved in the spglib primitive basis of the analysis — so--kpoint R,--kpoint Yand--kpoint GM(alsoGAMMA,GandΓ) work in every mode and print exactly what the coordinate form prints; an unknown label answers with the labels the space group has. Tests in section 6.Fixed: seekpath labels were resolved in the wrong basis on base-centred monoclinic cells.
crystod --star-of-k,crystod --visualizeand the--qpointlabels ofcrystod-phonon --vibration/--vector/--modulationtake their coordinates from seekpath, whose primitive cell differs from spglib’s by an integer change of basis for C-centred monoclinic lattices (VO2 C2/m: seekpath’sCwas analyzed at[0.355, 0.355, 0], a different point, after a warning that the “coordinates might need a basis transformation”). The coordinates are now transformed into the spglib basis whenever the two cells are related by such a change of basis (C→[-0.355, 0.355, 0], the ISO-IR line LD), and the warning remains only for the case that cannot be transformed. Also fixed: theGalias of Gamma hid the genuineGpoint of body-centred tetragonal cells (--qpoint Gon I4/mmm Sr3Ti2O7 now gives seekpath’sG = [1/2, 1/2, −0.018]). Tests in section 6.crystod-phononstops with one line whenFORCE_SETSis missing.--irreps,--fatband,--lt,--vectorand--subgroupreadFORCE_SETS(FORCE_CONSTANTSwith--readfc) from the working directory, as phonopy does; a run from another directory used to die in a phonopyFileNotFoundErrortraceback and now says where the file is expected (only--modulationalso looks next to the cell file).README and documentation for a first-time visitor. The README opens with the questions CrystOD answers, four output images (crystal-orbital diagram of ScF3, phonon dispersion of SrTiO3 with ISO-IR labels, 3D Brillouin zone, MO diagram of CH4), badges, the two-line installation and a quick start that needs no input file. The documentation site gained the same images, a “Try it” column in the question table, two Getting-Started pages (Your first analysis, CrystOD for phonopy users), a Tutorials page and a Contributing page.
API reference (Sphinx autodoc). Every symbol exported by
crystod.salc,crystod.group,crystod.phonon,crystod.bz,crystod.mag,crystod.mdandcrystod.molnow carries a Google-style docstring (arguments, returns, raised errors, a runnable example) and is rendered at https://mochizuki-tus.github.io/CrystOD/api/ (doc/api/); the lazy namespaces work with autodoc unchanged. The docs workflow installs the package for the build; the-Wbuild stays clean.Three Jupyter tutorials in
tutorials/— phonon irrep labeling for phonopy users, isotropy-subgroup search from imaginary modes, MO diagrams of CH4 and NH3 — executed on the bundled inputs with their outputs saved, the Python API and the command line side by side.crystod-mcp: a Model Context Protocol server. A separate package incrystod-mcp/(pip install ./crystod-mcp; MCP Python SDK 2.x) exposes ten CrystOD analyses as tools — isotropy subgroups, irrep products, reducible-representation decomposition, ligand-field splitting, character tables, phonon irreps, imaginary-mode subgroups, crystal-orbital irreps, special k points, molecular symmetry — to Claude Desktop, Claude Code and other MCP clients. Every result carries the ISO-IR and CrystOD citations, each tool runs under a timeout (60 s,CRYSTOD_MCP_TIMEOUT), and 22 tests cover the tools and the protocol round trip. Not on PyPI yet.Continuous integration. GitHub Actions now run the test suite on every push and pull request (Linux with Python 3.10/3.12/3.13, macOS with 3.12),
ruff(rule set inruff.toml), the docs build with warnings as errors (as before), and publish a tagged releasev*to PyPI through Trusted Publishing after a manual approval (publish.yml; the one-time PyPI setup is described in the file).CONTRIBUTING.mddescribes the development setup, the section-number convention (feature N ↔testsuite.py N↔example/N_*↔ documentation section N) and the release recipe.Housekeeping:
*.bakand.DS_Storeare ignored, the duplicated.gitignoreblock is gone,crystod --versionis checked against the package version in the test suite, and the PyPI description says what the package does.
v0.3.8#
Package metadata now describes what the package can actually do.
requires-pythonsaid>=3.9, which was untrue and already published: phonopy’s newest release carrying a CPython 3.9 wheel is 2.9.1 (2021), so pip back-solves to a phonopy sdist that fails to build, andpip install crystodcannot succeed on 3.9 at all — measured, with both pip and uv. The floor is now>=3.10, verified end to end by building throwaway environments on 3.10 through 3.14 and running the analyses in them (the source itself is 3.9-clean; the dependency stack is what sets the floor).Operating System :: OS Independentwas likewise false: pyscf has never published a Windows wheel for any release and its own documentation directs Windows users to the Windows Subsystem for Linux, so the classifier is replaced by Linux and macOS, joined by the per-minor Python classifiers. Every one of the twelve dependencies now carries a lower bound traced to the specific API that sets it —phonopy>=2.9forphonopy.phonon.character_table,sympy>=1.10forhermite_normal_form,spgrep>=0.3.1for the spinor irreps behind--spinor,ase>=3.21because 3.20 silently writes a VASP 4 POSCAR with no element line,pymatgen>=2025.1.9because older releases pull a monty whosezopensignature breaksStructure.from_file, and so on — andCITATION.cffgained thedate-released,repository-code,url,abstractandkeywordsfields it needed for a citation with a year in it (validated with cffconvert).pyscfis capped below 2.14.0, which cannot run a zero-electron cell. 2.14.0 rewrotepbc.scf.khf.get_occasif 0 < nocc < nmo / elif nocc == nmo / else: raise, and a fragment that a GTH pseudopotential fully ionizes — Al³⁺ of AlN keeps no valence electrons — hasnocc == 0, which matches neither branch and dies withFailed to assign mo_occ. Nocc (0) > Nmo (52), an inequality that is not even true. Isolated by crossing the two variables: the same structure passes on 2.13.1 under both Python 3.10 and 3.11 and fails on 2.14.0 under both, so it is the library version and not the interpreter. Only fragments that end up empty are affected — the crystal column of the same AlN run converges on 2.14.0. The cap is a stopgap; the durable fix is to skip the self-consistent cycle for a cell with no electrons.A purely ferroelastic pair no longer aborts the symmetry-mode analysis (found while reproducing 27 saved AMPLIMODES runs; La3Ni2O7 I4/mmm → Fmmm died with “cannot separate the surviving from the broken parent operations”). How many parent operations survive in the child was derived from the child’s own space group, but that count is only a lower bound: mode amplitudes are measured in the strain-free parent-derived reference lattice, so an operation that the child’s metric breaks with no atomic counterpart still leaves the displacement field invariant and belongs in the analysis group. In La3Ni2O7 all 16 parent operations hold exactly while spglib reads the child as Fmmm (8), and cutting the mismatch spectrum in the middle of 16 exact zeros was impossible — correctly refused, but the wrong question. Whenever at least that many operations sit at the numerical-noise level they are now all kept, and the report says so (“8 parent operations that the child’s own metric breaks leave its atomic pattern exactly invariant … a spontaneous strain”). Reproduces the AMPLIMODES reference exactly: a single GM1+ mode, isotropy subgroup I4/mmm itself, dimension 4, 0.4666 Å, maximum displacement 0.2669 Å.
--poscar2cifwarns when the CIF comes out more symmetric than the POSCAR. The CIF holds the spglib-standardized structure, so a displacement smaller than the tolerance is averaged away — silently turning a distorted structure into its own parent, after which a symmetry-mode analysis reports no distortion at all with nothing in the output to explain why. GaN F-43m → I-42d is the case that exposed it: its entire distortion is 0.0002 Å against the 0.01 Å default, and the child CIF came out byte-identical to the parent’s. The conversion now names both space groups and points at--tolerance; at--tolerance 0.0001the pair reproduces AMPLIMODES exactly (W1 at (1/2,1/4,3/4) → I-42d, 0.0004 Å). Tests in sections 15 and 16.crystod-group --supergroup-cifworks for rhombohedral parents (reported on Ag3SI R32 → R3, which died with “could not fit the ISO-IR origin shift”). The centring translations used to match the ISO-IR origin against spglib’s conventional operations were generated from the rows of the primitive matrix, whose mod-1 lattice happens to coincide with the columns’ for every centring type except R — rows give (1/3, 1/3, 1/3) where the obverse setting needs (2/3, 1/3, 1/3), so no origin could ever fit an R-centred parent. Validated against ISODISTORT for Ag3SI: GM1 0.0741 Å matches exactly, and the polar GM2 amplitude agrees to five decimals (Ag in-plane 0.52161, I 0.16902, S 0.44628 Å) once the origin conventions are aligned — ISODISTORT pins the first orbit’s polar displacement to zero, while CrystOD keeps the AMPLIMODES minimum-distortion origin (the acoustic translation is removed, so the reported GM2 is 0.6512 Å instead of ISODISTORT’s origin-dependent 0.7070 Å). Tests in section 16, including the measured origin-convention equivalence.--diagram --pyscfcaches its SCFs by default, asCHK_{formula}.chk. A three-SCF periodic run is the expensive part of the page, and until now it was thrown away unless the user had thought to pass--chkbeforehand. The densities are now saved under the compound’s reduced formula in conventional chemical order (rutile TiO2 →CHK_TiO2.chk, the same naming the symmetry-mode tables use), and the next run on that structure reads them and skips the SCFs (measured on TiO2 at Γ: 8 s instead of a full three-SCF run). The automatic file is a cache, so unlike an explicit--chkit never blocks a run and never costs one: options that do not match the stored ones (a different--xc,--kmesh, an--onsitefile where fragment columns are wanted, a pre-electron-count-fix checkpoint), a checkpoint left by a same-formula polymorph (rocksalt and wurtzite AlN are bothCHK_AlN.chk, with 2 and 4 atoms per cell — that comparison used to raise a raw numpy broadcast error), and a file that is not readable at all (an interrupted write, a foreign file that happens to match the name) each print one line and recompute. The checkpoint is written to a temporary file and renamed, so an interrupted write cannot leave a truncated one behind, and if it cannot be written at all (read-only or full working directory) the run says so and keeps going — losing a finished three-SCF calculation to a cache the user never asked for would be absurd.--no-chkturns the cache off entirely.--chk FILEkeeps the strict contract — that file is the user’s, so a mismatch still aborts with thecrystod --chk-infopointer instead of silently overwriting it.--electronsno longer invalidates a checkpoint (it fills the diagram’s arrows, it is not an SCF input), while the SCF’s ownconv_tol/max_cycle, which do shape the density, are now part of the comparison.--chk FILEkeeps the strict contract — that file is the user’s, so a mismatch still aborts with thecrystod --chk-infopointer instead of silently overwriting it. Tests in section 3.The extended-Hückel diagram now CAUTIONs that its level ORDER can be qualitatively wrong. Prompted by the rutile TiO2 case above: the model is non-self-consistent with fixed atomic parameters, so the symmetry labels, the state compositions and the bonding/antibonding pattern are reliable, but the energies carry no charge-transfer response and a whole band can land in the wrong place (TiO2’s artificial Ti 4p band inside the O 2p band, which costs the compound its insulating filling). The terminal report prints the caution once per run, the page footer carries it, and the
--visualizelevels viewer — which solves the identical Hamiltonian — prints it too. What the caution claims is deliberately narrow: the symmetry is rigorous (irrep labels, which states may mix, which stay nonbonding), the order can be wrong, and a wrong order takes the compositions with it (in the same TiO2 the bonding Γ₃⁺ comes out Ti-dominated and its antibonding partner O-dominated — the reverse of the heteropolar rule and of PySCF, verified against both engines). The prescribed--pyscfcross-check is qualified rather than absolute: it cannot run for the lanthanides, which no GTH basis covers, and there the caution says to read the diagram as a symmetry analysis instead. Test in section 3.Fixed: the
--diagramhover sketch could draw a 90%-s level as d lobes. Rutile TiO2’s Ti 4s Γ₁⁺ fragment level (Löwdin: Ti 4s 89.9%, Ti 3s 5.6%, Ti 3d 0.8%) rendered with drawn d:s amplitudes of 2.7:1 — the picture contradicted its own tooltip. The extended-Hückel sketch weighted raw eigenvector coefficients by the STO amplitude at a fixed 2-bohr radius, where the compact Ti 3d weighs ~4× the diffuse Ti 4s and the semicore 3s orthogonality tail cancels most of the s channel. The sketch now uses the recipe the PySCF sketches and the--visualizelevels viewer already use: lobe sizes calibrated per (atom, l) channel to the Löwdin populations, with the probe radius chosen per channel (1.5/2.0/2.5/3.0 bohr, the one with the largest accumulated amplitude — away from semicore nodes). The TiO2 4s sphere now dominates its own sketch, and the drawn d amplitude² matches its 0.8% population. Level energies and occupations are untouched (verified identical across the full TiO2 page, 373 levels); the filtered--atomic-orbitalsketch keeps the legacy fixed-radius amplitudes, since its channel populations include shells deliberately not drawn. Tests in section 3.The extended-Hückel diagram discloses its two artifact modes for diffuse cation shells instead of silently mis-filling. Diagnosed on rutile TiO2, where the Γ-point HOMO (Γ₅⁻) showed 2 of 4 electrons although TiO2 is a band insulator. The electron count is correct everywhere (76 = 2×22 + 4×8; every column sums exactly); what breaks is the level ordering, twice: (1) the ζ = 0.675 Ti 4p STO engulfs the whole O²⁻ coordination cage, so the point-charge penetration term — a small correction for compact shells — shifts its on-site energy by −45.9 eV (to −51.3 eV, below Ti 3p); (2) its Bloch sums are all near-dependent (shell-block overlap eigenvalues ≤ 0.055); the doublet that survives the linear-dependency cut comes back as a first-order
~estimate at ≈ h̄(1+(K−1)(ε−1)) ≈ −13 eV, right at the O 2p band top, and breaks the insulating filling — in the reporter’s phonopy-relaxed cell it lands inside the band, swallows 4 electrons and leaves the true O 2p top (Γ₅⁻) half-filled; in the slightly larger experimental cell shipped as the test structure it lands just above the band and is itself the half-filled HOMO, taking 2. (The deep Ti 4s Γ₁⁺ fragment level at −31.4 eV is the same physics from the other side: its in-phase Bloch sum has ε = 8.8, and the Wolfsberg–Helmholz energy h̄(K+(1−K)/ε) → K·h̄ overbinds large-overlap combinations — the classic EHT counterintuitive-overbinding artifact — to −36.7 eV pure; intra-sublattice mixing with the Ti 3s/3d Γ₁⁺ sums brings the fragment level back up to −31.4 eV. It miscounts nothing: in the crystal column the corresponding Ti 4s/O 2s bonding state at −36.6 eV simply occupies the O 2s Γ₁⁺ slot’s 2 electrons.) Now disclosed at every surface: the on-site report prints the decomposition (Ti 4p -51.32 [VSIP -5.44, field -45.88]) and WARNs when the point-charge shift exceeds 8 eV; the atomic-column tooltip carries the same warning; and an aufbau that runs out mid-level flags the partially filled level in the terminal and its tooltip (both engines — a genuinely metallic k point prints the same honest note). A column whose electron count is odd (a neutral open-shell sublattice: one level is necessarily half-filled at every k point) and a fractional--electronsrequest get their own matter-of-fact tooltip notes instead of the warning — parity and rounding are properties, not artifacts. Tests in section 3 (TiO2 at Γ).Fixed: five two-shell
--multipletconfigurations aborted before printing the ground state (260828 bug report).(t2g)^2(eg)^2,(t2g)^3(eg)^{1,2,3}and(t2g)^4(eg)^2of m-3m/d died withERROR: multiplet-energy coefficient failed to snap to an exact fraction (8.66...)— exit 1, noGround-state Term Symbol. The coupled-parent CI table recovered the off-diagonal coefficients asgram[pivot,q]/sqrt(gram[pivot,pivot]), and in these configurations they are genuinely irrational (2sqrt(2)B,5sqrt(3)B,4sqrt(3)B— the same square roots the printed Tanabe–Sugano matrices carry), so the rational-snap assumption could never hold. The Gram productsc_p c_qthemselves are basis-invariant and rational, so the off-diagonal is now snapped at the Gram level and factored exactly assqrt(radicand) * (rational linear form)with a squarefree radicand:<1|H|2> = +-(5sqrt(3)B)prints where the run used to die, purely rational forms print byte-identically to before (+-(10B)), and a float reconstruction cross-check guards the factorization. Should a future block still have no such closed form, its table row says so in one line instead of killing the run — the term energies and the ground state never depended on it (they use the trace identity) — and a rank-1 consistency guard (the Gram diagonal must reproduce asradicand * c_q^2) protects the printed closed forms against any silent violation of the single-intertwiner assumption. All five configurations now complete with the physically correct ground states ((t2g)^3(eg)^2→^6A1g, the d⁵ high-spin sextet). Tests in section 14., so the choice is the user’s rather than a guess.--basislists all 21 GTH basis sets PySCF ships grouped by element coverage — the decisive facts being that onlygth-szv-molopt-srandgth-dzvp-molopt-srreach past Ar (71 elements: H–Rn minus the lanthanides) and that no GTH basis PySCF ships covers La–Lu at all, so--pyscfcannot run on a rare-earth compound (the extended-Hückel engine, which has its own La/Ce/Sm/Gd/Yb/Lu parameters, still can), whilegth-tzvp/gth-qzv2p/gth-aug-*/gth-cc-*are main-group or light-element sets;--pseudolists all 11 GTH pseudopotentials the same way (gth-pbe,gth-pade,gth-lda,gth-hfrevspan H–Rn);--xclists the LDA/GGA/meta-GGA/hybrid functionals verified (by running each of them) in this code path (lda,svwn,pbe,pbesol,revpbe,blyp,bp86,pw91,b97-d,scan,r2scan,tpss,revtpss,b3lyp,b3lyp5,pbe0,hse06,m06,m06-2x,wb97x, andhf), noting that hybrids want--ke-cutoff 150or more;--orbitalnames the shells it accepts (s–i);--kpointpoints atcrystod-bz --show-kpointfor a space group’s labels. The lists are hard-coded so that--helpstays import-free (no PySCF import for a help screen), and a testsuite check compares them againstpyscf.pbc.gto.basis/pseudo.ALIASso they cannot drift. Also fixed in the same pass: a basis or pseudopotential with no entry for one of the elements now fails withERROR: the basis 'gth-qzv2p' has no entry for Sc.plus the list of sets that do cover the structure, instead of PySCF’sBasisNotFoundErrorfrom deep insidecell.build();--xcis validated the same way, so an unknown name (pz,vwn— common shorthands libxc does not have) and a VV10 functional (wb97m-v,b97m-v,wb97x-v, which PySCF’s periodic code cannot evaluate — it has no nonlocal-correlation path, and the old failure was anAttributeErrorinsideget_veffafter all three cells were built) each end with one sentence naming the remedy; and the empty-shell caveat added earlier in this release recommended--basis gth-qzv2punconditionally, which is impossible for anything past Ar — the recommendation now appears only when PySCF actually ships that set for every element, and says so plainly otherwise.Removed the stale “pre-v0.3.0 flat modes” notice from
crystod --help, the quickstart and the CLI docstrings: the flat spellings predate the first PyPI release (0.3.6), so no released version ever had them and the version numbers only dated the help text. A flag belonging to a sectioned command still gets the same helpful answer, now without the archaeology:ERROR: '--bz' is not a crystod option. The equivalent command is: crystod-bz.Fixed: charged fragment cells converged with the wrong electron count on any k-mesh larger than 1×1×1 — every fragment column of the default (formal-charge)
--diagram --pyscfmode was affected. PySCF subtractscell.chargeonce from the Born–von Kármán supercell total (tot_electrons(nkpts) = ΣZ_val·nkpts − charge), not once per cell, so a fragment with formal charge q kept q·(nkpts−1)/nkpts spurious electrons per cell. Measured on the shipped ScF3 checkpoint (2×2×2 mesh): the Sc⁺³ sublattice carried 10.5 electrons per cell instead of 8, the F₃⁻³ sublattice 21.5 instead of 24, while the crystal (charge 0) was exact at 32. The fragment cells now pin their per-cell count explicitly (cell.nelectron), which makestot_electrons(nkpts)exact on every mesh. Checkpoints written before this fix are refused when the bug could have bitten them — charged fragments on a k-mesh — with a message saying why; crystal-only (--onsite) and neutral-fragment checkpoints remain valid. Affected.chkfiles and the fragment columns of previously generated default-mode pages should be regenerated. Found by the adversarial review of the neutral-sublattice feature below, whose all-zero charges are immune (they were, until this fix, the only exactly counted fragment configuration).--diagram --pyscfaccepts neutral-atom sublattices (--oxidation Al=0 N=0) — the extended-Hückel engine’s convention, previously rejected with “the formal charges must make every fragment closed-shell”. Two things had to give: (1) a neutral sublattice usually has an odd electron count per cell (neutral Al: 3), which a spin-restricted driver with integer occupations cannot hold — pyscf’s aufbau would silently drop the unpaired electron (nocc = nelectron // 2). Such a cell is now solved with Fermi smearing automatically (fractional occupations; sigma 0.2 eV unless--sigmasays otherwise, announced in the run report; which columns were smeared is recorded in the checkpoint) — the same smearing the convergence ladder already used as a fallback, so nothing downstream is new. (2) The isolated-atom columns must hit the right ground spin state: the old parity fallback (spin 0, else spin 1) would have computed neutral N as an excited spin-1 doublet. The ion/atom solver now scans the parity-consistent spins (up to 8 unpaired — Gd/Cm reach 4f⁷5d¹/5f⁷6d¹) and keeps the lowest converged energy, so neutral N comes out as the Hund ⁴S spin-3 atom, neutral Al as the ²P spin-1 atom, and closed-shell ions still win at spin 0 (sub-second atomic calculations; this also upgrades genuinely open-shell ions such as high-spin d⁵, which the old path forced to spin 1). A trial whose DIIS stalls is retried with second-order SCF (plain DIIS fails on neutral Ni’s near-degenerate 3d⁹4s¹ ground state, which would otherwise silently lose to a converged excited quintet 4.8 eV up), a failed trial is skipped with a note instead of killing the run, and if an unconverged spin state still undercuts the chosen one, a warning says the shown column may be an excited spin state. With all oxidation states 0 the point-charge lattice vanishes, so the fragments are bare neutral sublattices + ghost basis, exactly the EHT model. Verified on rocksalt AlN at X: Al column 3 electrons, N column 5, both smeared,Al^+0 (UKS PBE, 3 electrons, spin 1 (alpha levels))/N^+0 (UKS PBE, 5 electrons, spin 3 (alpha levels)), and the Al 3p Bloch combination below Al 3s — the same intra-sublattice-overlap physics the EHT pages show. Test in section 3.Empty ion shells in the PySCF atomic columns are now flagged as what they are — finite-basis virtuals, not Rydberg levels. In
crystod --diagram --pyscf, the isolated-ion columns showed Al⁺³ 4p below 4s (and N⁻³ 3p below 3s), inverting the physical order. The cause is the defaultgth-dzvp-molopt-srbasis: optimized for condensed-phase valence states, it carries no diffuse functions, so any empty level beyond the lowest empty state of its l is merely the orthogonal remainder of the contraction — a compact, node-rich function pushed up by the kinetic energy of its enforced node (the repulsive GTH s-channel projector plausibly adds to this, though the experiment did not separate the two contributions). Verified against the NIST free-ion spectrum of Al⁺³ (Na-like Al III): with the production basis 4s is +7.1 eV off (4p only +1.5 eV, hence the inversion), while re-running the identical bare-ion problem ingth-qzv2plands 4s/4p within 0.1 eV of experiment — the pseudopotential is innocent. For anions the defect is deeper: one more electron on a free anion is unbound, so every empty anion level is a discretized continuum state with no physical counterpart at any basis size (in the crystal the Madelung potential provides the binding). Both cases are now disclosed everywhere the levels appear: the tooltip of each affected level, the page footer, and the terminal report — each including the concrete remedy--basis gth-qzv2pwhen the run uses a molopt-family basis (with two honest caveats: diffuse-richer bases make the periodic SCF heavier and possibly ill-conditioned, and the 0.1 eV figure is the bare-ion validation — Kohn–Sham virtuals of ions that keep electrons retain the functional’s own eV-scale attachment error, which no basis removes). The lowest empty state of each l of a cation is left unflagged — occupied semicore shells below it do not consume the slot, so Na⁺ 3s (the first empty s above the 2s²2p⁶ core, basis-converged to 0.2 eV) is trusted while Na⁺ 4s is flagged — because those are genuine electron-affinity levels (Al⁺³ 3s/3p/3d match NIST to 0.2–1.2 eV even in the production basis); a formally neutral atom, whose Coulomb tail binds no attachment states, has all its empty shells flagged. Test in section 3 (NaCl: Na⁺ virtual notes, Cl⁻ continuum notes, and the recommendation string in the page).The atomic-shell columns work in the PySCF engine too — as genuine isolated-ion calculations.
crystod --diagram --pyscfnow runs one additional PySCF calculation per element: the isolated ion at its formal charge (the same pymatgen-guessed/--oxidationcharges the fragment sublattices already use), with the same basis, pseudopotential and functional as the periodic calculations — so the page tells the user’s three-stage story explicitly: charged atom → charged sublattice (+ point-charge lattice) → crystal. Closed-shell ions (Na⁺, Cl⁻, Sc³⁺, F⁻, N³⁻ …) are solved with RKS; a cation whose formal charge empties the pseudo-valence space entirely (Al³⁺ with GTH-q3) gets the bare-ion one-electron spectrum instead (its 3s eigenvalue, −27.9 eV, reproduces Al’s third ionization potential of 28.4 eV); odd-electron ions fall back to UKS with a tooltip flag. Because the molecular (vacuum) and periodic (G = 0) energy references share no common zero — and vacuum anion levels are unbound (F⁻ 2p at +3.5 eV) — each element’s ion column is rigidly shifted so its deepest shell sits at the degeneracy-weighted, all-k mean of the fragment levels that shell dominates (the band’s center of gravity, which is the tight-binding on-site energy in an orthogonal basis); the anchor, the shift and the raw vacuum levels are all printed and repeated in the tooltips. Splitting connector lines reuse the levels’ displayed Löwdin/Mulliken shell populations.--onsitepages are unchanged (the single-Hamiltonian mode has no atomic stage). Tests in section 3 (default-mode NaCl at X: ion report, anchoring, and the Cl 3p fragment→ion link).crystod --diagramgained outermost atomic-shell columns — the crystal analogue of the MolODligand-aocolumn. With more than one atom of an element per primitive cell (wurtzite AlN: two Al), the fragment sublattice column alone can look puzzling — 26 electrons on the Al side, and Al-3p Bloch combinations below Al-3s at several k points, both genuine consequences of the intra-sublattice Al–Al overlap. The diagram now shows where that splitting comes from, exactly like the H₄ column ofcrystod-mol: the new outermost columns hold one level per (element, shell) at the k-independent on-site energy (VSIP + spherical part of the point-charge ligand field, e.g.2Al 3s,2Al 3p, no occupation arrows), and every sublattice level is connected to its parent shell(s) by correlation lines weighted with the same Löwdin shell populations as its tooltip — so “Al 3p GM4 at −16.35 eV sits below Al 3s GM4 at −6.50 eV” reads directly as the bonding/antibonding fan opened by the two equivalent atoms out of the atomic 3s (−16.80) and 3p (−11.00) levels. The terminal report prints the same on-site levels per fragment (“on-site atomic levels (VSIP + ligand field, eV): …”), and the page footer explains the columns. Layout grows from three to five columns (2Al AOs | Al | crystal orbitals | N | 2N AOs); engines that do not build the AO columns (--pyscf) keep the three-column page unchanged. Tests in section 3 (ScF3:Sc AOs/3F AOsheaders and3F 2plabel; rocksalt AlN at X: the Al 3s fragment level linked to its parentAl 3son-site level).Fixed:
crystod --diagramdeleted physical levels in dense sublattices — rocksalt AlN had no Al 3s at the X point. The extended-Hückel solver excludes Bloch combinations whose overlap eigenvalue falls below 0.2, because their variational energies diverge as (1−K)·h/ε (the overlap catastrophe of diffuse shells). In a dense sublattice, however, a single shell’s own Bloch sum can fall below that floor — the fcc Al sublattice of rocksalt AlN (twelve neighbours at 2.86 Å) puts the one Al 3s X1+ combination at eigenvalue 0.15 — and deleting it removed a whole physical level: the Al column at X had no 3s state, so the aufbau filling put three electrons into 3p. Such modes are now kept as separate levels whose energies are estimated at first order in the Löwdin orthogonalization (H̃ = H − (S−1)(h̄ᵢ+h̄ⱼ)/2, the same correction the coupling tables use; for a single Bloch sum this gives the bounded E = h(1+(K−1)σ) instead of the divergent h(K+(1−K)/ε)). Estimated energies are marked~in the terminal and explained in the level’s tooltip (and carry"est": 1in the page’s level JSON); only truly linearly dependent combinations (eigenvalue < 10⁻⁶, no physical content) are still removed, reported separately. The variational levels are bit-identical to before, and in the previously validated materials (ScF3, SrTiO3, BaTiO3, ZrO2) every restored level is empty, so no occupied manifold changed. AlN now shows Al 3s X1+ at X and Al 3p GM4- at Γ with the textbook 3s²3p¹ filling at Γ, and the crystal column gains the previously missing antibonding partners — the lowest empty state at X becomes X1+, matching the X-point conduction-band symmetry of rocksalt AlN in DFT. Regression tests in section 3 (the restored Sc 4s R1+ level of ScF3, and rocksalt AlN at X: the ~ estimate plus the 13-electron aufbau count of the Al column).
v0.3.7#
The SALC viewer shows the k point in both bases with
--conventional:--kpointis given in the primitive reciprocal basis throughout CrystOD, and the conventional-cell display now lists both —X [0.0, 0.0, 0.5] (primitive)andX [0.5, 0.5, 0.0] (conventional)for I4/mmm La3Ni2O7 — so the shape of the commensurate display supercell (2 × 2 × 1 conventional cells at X) is evident at a glance. The conversion uses the same primitive-to-conventional matrix as the displayed cell (fcc Si X: [0,1/2,1/2] → [1,0,0]); pages without--conventionalare unchanged. Tests in section 6 (the –visualize extras block).The symmetry-mode decomposition table is saved as
sym_mode_{formula}(e.g.sym_mode_K2SeO4) next to the VESTA files of every--supergroup-cifrun, named by the parent composition. The formula follows the conventional chemical order — cations before anions, the cation on the most special Wyckoff site (letter closest to a) first, then increasing valence (SrTiO3, KNbO3, PbZrO3; La3Ni2O7 keeps La first because the site rule beats Ni’s lower valence) — computed from the structure itself (oxidation-state guesses, electronegativity fallback), so POSCAR inputs work the same as CIFs. The same formula is written into every--poscar2cifoutput as the IUCr_chemical_formula_sumfield ("K2 Se O4"). Second non-special-k reference case added: the K2SeO4 lock-in ferroelectric Pnma → Pna2₁ (3× along a, SM = (1/3,0,0)) reproduces AMPLIMODES entry by entry — GM1+ 0.9467, polar GM4- 0.4230, SM2 1.2885, SM3 0.1727 Å, total 1.6629 Å. Tests in sections 15 and 16.Fixed: the one-sentence rejection of a wrong
--dimincrystod-phonon --modulationdepended on the phonopy version — older phonopy raisesValueErrorfor a supercell that does not match the force data, phonopy 4 anIndexErrorfrom the site-symmetry lookup, which escaped as a raw traceback. Both are caught now.crystod-group --supergroup-cifsupports order parameters at non-special k points (symmetry lines, planes and general points on the 1/24 grid). The reference case is the PbZrO3 antiferroelectric, Pm-3m → Pbam at 8× the cubic primitive cell, whose distortion condenses the Σ point (1/4,1/4,0) and the S point (1/4,1/2,1/4) on top of R/X/M modes — previously rejected with “some folding k-points are not tabulated special points”. Small irreps at such points are computed with spgrep at the exact k, named through the bundled ISO-IR tables (SM2, S4, …), and the full induced representations are built from the ISO-IR full-star matrices themselves (tabulated arm order, deterministic across spgrep versions; the spgrep-basis induction is the automatic fallback). Every AMPLIMODES reference number is reproduced — SM2 1.2871 Å → Pbam, R4+ 1.5232 Å → Imma, S4 0.4470 Å → Cmce, R5+ 0.0810 Å, X3- 0.0374 Å, M5- 0.0473 Å, total 2.0462 Å — and each mode’s isotropy subgroup is additionally verified by freezing that single mode’s displacement field and re-measuring its space group with spglib. Because the phase gauge that fixes the ISOTROPY web tables’ order-parameter axes is not part of the bundled data, the direction pattern of a line mode may differ from ISOSUBGROUP’s (a printed note says so); subgroup, dimension and amplitude are gauge-independent. Internals: the mode projectors are now assembled per k star in a Fourier-block basis (star arms × parent atoms) instead of dense displacement matrices on the invariant-core supercell — the old route would have needed ~22 GB at PbZrO3’s 4×4×4 invariant core (320 atoms), the new one runs the whole analysis in ~18 s; outputs of all previously validated cases (SrTiO3, ZrO2, CaTiO3, BaTiO3, AlF3, La3Ni2O7, SrLi2Nb2O7 ×2) are unchanged to the last printed digit. Tests in section 16, including the frozen-field re-measurement.
v0.3.6#
Python API: every analysis is now reachable as a Python function, grouped into one module per command —
crystod.salc,crystod.group,crystod.phonon,crystod.bz,crystod.mag,crystod.md,crystod.mol— so that a part of CrystOD can be used inside another program without going through the command line (full guide: the Python API page of the documentation). The domain modules are a curated view over the implementation modules, which keep their own names:from crystod.phonon_irreps import get_irrep_labels,from crystod.operations import wigner_D_realand every other pre-0.3.6 import path works unchanged, and the command line is untouched apart from the new--subgroupmode below. Attribute access is lazy (PEP 562), soimport crystodplus all seven domains costs ~0.09 s and pulls in nothing heavier than NumPy — phonopy, spgrep, spglib, PySCF and matplotlib load only when a function that needs them is actually called. New in the API layer:crystod.group.isotropy_subgroups(space_group, irrep[, order_parameter])returns thecrystod-group --supergrouptable asIsotropySubgrouprecords (irrep, direction, space-group number/symbol, cell size, index, free-parameter count, and the conventional basis/origin in the parent convention);crystod.phonon.label_phonon_modes(phonon, qpoint)labels the modes of a live phonopy object with ISO-IR irreps (star arms mapped onto the tabulated arm automatically), returningPhononModerecords with 1-based band indices;crystod.phonon.imaginary_mode_subgroups(phonon, qpoint)andscan_imaginary_modes(phonon)combine the two into the symmetry-lowering step of a structure search. Bad input raisesValueErrorrather than exiting: the implementation modules report errors the way a command line wants them (raise SystemExit), which would tear down a program that merely called the function — the API layer translates that at its boundary. This matters for the labels of symmetry lines and planes (DT5,LD3, …), which are legitimate mode labels with no isotropy subgroups in the tables;imaginary_mode_subgroups/scan_imaginary_modesrecord such a level inerrorswith an emptysubgroupsand carry on. Tested in the new testsuite section 35 (31 checks: import hygiene, backward compatibility, API-vs-CLI agreement on every order-parameter direction — including the arm grouping of multi-arm stars such as M3+(0;0;a) — the error contract, and the SrTiO3 R-point soft mode).Fixed: the documentation site always displayed version 0.3.3.
doc/conf.pyread the version throughimportlib.metadata("CrystOD")and fell back to a hard-coded string, but the docs workflow installs only Sphinx and never CrystOD itself, so every published build took the fallback. The version is now read straight out ofpyproject.tomlwhen the package is not installed.crystod-phonon --modulationruns from the files you already have. A unit cell plusFORCE_SETS(orFORCE_CONSTANTSwith--readfc) is enough —crystod-phonon --modulation -c 221_PPOSCAR_ScF3 --qpoint 0.5 0.5 0.5 --mode 1 2 3 --amplitude 0.3produces the byte-identical structure that the--yaml phonopy_params.yamlform does, so no phonopy yaml has to be generated first. The supercell of the force calculation is read fromphonopy_disp.yamlwhen present and otherwise inferred from the atom count of the force file (respecting the axis equivalence of the lattice: a cell with |a| = |b| never gets unequal multipliers); which route was taken is printed, and--dimoverrides it. A supercell that does not match the force data now aborts with one sentence instead of a phonopy traceback.--yamlstill works and still wins when both are given. Fixed in the same pass:--tolerance/--symprecreached the symmetry-adapted mode construction but never the space group reported for the generated structure, which was hard-coded at 0.1 Å — a tolerance comparable to the modulation amplitude itself, so the printed label depended on the absolute amplitude scale (--mode 1 2 3 --amplitude 0.3 0.15 0.075, the direction(a,b,c), printedC2/cinstead ofP-1). One--tolerancenow sets both.crystod-phonon --subgroup --modulate: the distorted structure of every enumerated order-parameter direction, generated in the same run — one POSCAR per direction plus the--modulationcommand that reproduces it, so the candidate structures of a structure search come straight out of the instability analysis (cubic SrTiO3 R5-: six structures, I4/mcm, R-3c, Imma, C2/m, C2/c, P-1). Which combination of the degenerate modes realizes which direction is not fixed by any convention CrystOD could assume, so it is measured rather than assumed: candidate combinations are generated, the space group, primitive-cell multiplication and index of each generated structure are determined with spglib, and the triple is matched against the enumerated table — a direction no candidate reproduces is reported as not generated instead of guessed at. When two directions of one irrep share that triple —R5+of Pm-3m puts both(0,a,b)and(a,a,b)at C2/m, size 2, index 24 — the conventional cell metric separates them, so a domain of one is never written under the other’s name, and the pattern of zero and equal components decides which label a structure takes; every enumerated row ends up either as a file or as an explicit note. Multi-arm stars go through the multi-q form of--modulationautomatically (M3+ of a perovskite: one arm for(0;0;a), two for(0;a;a), three for(a;a;a)).--amplitudescales the set (default 0.3 Å). Tests in section 27, including an independent re-measurement of every written structure and an execution of every printed reproduce command.crystod-group --parentis accepted as an alias of--supergroup: the flag names the parent group but returns its subgroups, which reads backwards, so the option is now also available under the name of the value it takes. Both spellings produce byte-identical output.crystod-phonon --subgroup: the command-line form of the same analysis — every imaginary phonon level is labeled with its irrep and each order-parameter direction resolved to the space group it condenses into (cubic SrTiO3, R5- triplet: I4/mcm, R-3c, Imma, C2/m, C2/c, P-1), scanning every q point commensurate with the supercell when no--qpointis given.--thresholdsets what counts as imaginary (default -0.1 THz);--yaml phonopy_params.yamlreplaces--dim/-c. Freezing in a single eigenvector explores only one direction of a degenerate level, so this is the enumeration a structure search needs before it can claim a ground state. Tests in section 27.
v0.3.5#
All irreducible-representation tables unified on the bundled ISO-IR data; the
irreptablesandirreppackage dependencies are removed. Every table lookup — crystal orbitals, hybridization, SALC/basis functions, phonon irreps/vibrations/modulations, spin bases, direct products, isotropy subgroups, little-group character tables, and special-k listings — now reads the ISO-IR dataset shipped in the package (crystod/CIR_data.txt.gz; ISOTROPY Software Suite: H. T. Stokes, B. J. Campbell and R. Cordes, Acta Cryst. A69, 388-395 (2013)). Labels follow the ISO-IR (ISOTROPY, Miller-Love) convention uniformly at special points and on symmetry lines/planes; at the maximal k points they coincide with the previously used Bilbao-convention labels for all 230 space groups (verified programmatically: operator sets, representative star arms, characters, and label strings all agree). Line terms formerly named from a DIRPRO-fitted map follow ISO-IR now (S of the P6₃/mmc family → Q, T → LD, V of I4/mmm → LD; the DT numbering of Fm-3m/Ia-3d is permuted). The non-self-conjugate P/N/W points of the centred nonsymmorphic groups (P of I-4̄2d/Ia-3̄d, N of I4₁32, W of Fd-3̄m, …) are resolved by one exact rule — complex conjugation combined with the wrapped-lattice translation phase — replacing the previous per-case gauge fixes and fitted-name substitutions (chirality-sensitive results such as I4₁32 N1 → P4₁22 are reproduced). The provenance notes (“absent from the … tables”, “[…; ISO-IR labels]”) are gone from the outputs since there is only one convention now. Verified by the full testsuite (639 checks) and by a feature-by-feature side-by-side comparison against the previous pipeline (61 command runs: no physical differences — decompositions, frequencies, subgroup tables, and exported files agree).License changed to MIT (possible now that the GPL table packages are no longer used).
Rare-earth support: archived neutral-atom reference levels (
reference/atomic_level_*) added for Y and La–Lu (def2-SVP for Y/La, Stuttgart RSC 1997 / ECP28MWB for Ce–Yb, CRENBL for Lu — the 4f shell stays in the valence space), and extended-Hückel parameters for La, Ce, Sm, Gd, Yb, Lu (YAeHMOP tables) together with new f-orbital STO overlaps (σ/π/δ/φ channels, validated against independent grid integration).crystod --diagramnow works for e.g. CeO₂, with the 4f crystal-field splitting labeled (GM4⁻/GM5⁻/GM2⁻ at Γ of Fm-3̄m).crystod --visualizewithout--kpointnow visualizes the SALCs at every special k point of the space group (one HTML per point,SALC_{POSCAR}_{element}_{orbital}_{kpoint}.html), mirroring the--diagrambehaviour.The citation notice is printed at the end of every subcommand’s
--help.Documentation front page gained an interactive “molecule → crystal” band-formation demo and embedded MO/crystal-orbital diagrams.
Cd parameterized for every extended-Hückel mode (
--diagram, the new levels viewer,crystod-mol): the YAeHMOP table entry (5s −11.80 eV / ζ 1.640, 5p −8.20 eV / ζ 1.600; the filled 4d¹⁰ stays inactive there, exactly as for Zn — it enters the full-electron basis as a semicore shell instead) plus the archived neutral-atom reference calculation (reference/atomic_level_Cd, RHF/def2-svp + def2 ECP-28 — def2-svp does cover Cd; the 4d sits at −19.6 eV). CdF₂ at Γ comes out textbook: the ligand-field-split Cd 4d¹⁰ semicore (6e + 4e), the F 2p valence band, and the Cd 5s conduction level as the LUMO.script/collect_atomic_levels.pynow takes element arguments (… Cd) to regenerate ONLY those entries — a full re-collection re-runs every archived atom and can trip the 1e-4 hartree cross-check on unrelated elements when the BLAS environment differs from the original archive’s.Extended-Hückel eigen-levels in the SALC viewer (bare
crystod --visualize -c POSCAR,crystod/visualize_eht.py): the extended-Hückel counterpart of--visualize --pyscf— without--element/--orbitalthe viewer now shows the eigen-levels of the--diagramengine instead of the SALC basis: one page per special k point, Mode | Irrep | Comp. | Energy (eV) rows for every level inside the energy window (HOMO−15 .. LUMO+10 eV by default;--windowopens the deep shells), the clicked row rendering the eigenvector’s wave function with lobe sizes calibrated to the Löwdin populations (per-channel best probe radius among 1.5/2.0/2.5/3.0 bohr, so semicore orthogonalization nodes don’t flip drawn signs — same recipe as the PySCF viewer). No SCF and no options required: STO basis, atomic levels and point charges are tabulated (ScF₃’s four k-point pages appear in seconds, levels identical to the--diagramcrystal column).--sublattice Scshows the pre-bonding sublattice block of the shared Hamiltonian instead of the crystal — all views on ONE energy reference by construction.--kpoint/--window/--diagonalize/--valence-only/--bond/--conventional/--electrons/--oxidationwork as in the PySCF version (--real-coefficientis accepted but inert on the level pages — the degenerate partners are always realified and the drawn field is Re[ψ], which the sidebar now states honestly on the PySCF pages too); PySCF-only flags (--chk,--basis,--kmesh, …) are rejected with a pointer to--visualize --pyscf, and--elementwithout--orbitalstill directs to the basis viewer. Tests in section 5.Fixed: a missing structure file crashed with an
AttributeErrortraceback ('NoneType' object has no attribute 'totuple') in most modes — phonopy’sread_crystal_structurereturnsNoneinstead of raising. Every POSCAR-reading entry point (--diagramboth engines,--dos,--band,--visualize --pyscf, the SALC analysis,--producthybridization,crystod-phonon --qpoint) now goes through the sharedread_poscar_or_exithelper (previously only the SALC viewer/BZ/star-of-k/mag paths used it) and printsERROR: No POSCAR named 225_PPOSCAR_ZrO2!— the unified message across all modes.--diagram --conventional: conventional-cell orbital sketches (both engines): the Level-details hover sketch draws the wave function on the primitive k-commensurate supercell by default — at Γ of an fcc crystal that is the 3-atom rhombohedral primitive cell, hard to relate to the textbook conventional picture.--conventional(same flag as the SALC viewer) wraps every atom into the conventional cell of the detected centring (times the diagonal multiples that keep the Bloch phase exactly commensurate: ZrO₂ Fm-3m draws the full 12-atom fluorite cube at Γ and X, 2×1×1 cubes at W, 2×2×2 at L), each atom carrying the exact phase of its own primitive-lattice translation — verified against exp(2πi k_conv·r) sign patterns at X for both engines. The sketch caption names the displayed cell (orbital sketch (conventional cell (F centring), 1 x 1 x 1)), the page footer says which cell the sketches use, and the flag is display-only: the diagram, the populations and any--chkare untouched (a checkpoint written without it is reused as-is). Default-mode pages are unchanged apart from the new caption (the rewritten supercell enumeration was verified bit-identical).--visualize --pyscfgained the same flag (it was silently dropped there before — the PySCF SALC-viewer pages now honor--conventionallike the classic viewer always did). Adversarial review fixes in passing: the primitive→conventional matrix was transposed for non-symmetric centrings — phonopy’sMacts on column-vector lattices, so the row matrix isinv(M)ᵀ, notinv(M); the two coincide for P/F/I (why every cubic example was right) but R-centred structures drew an oblique det-3 supercell instead of the hexagonal cell, and A/C got axis-swapped cells — a pre-existing bug shared by the SALC viewer’s--conventional,crystod-phonon --vector --conventionalandcrystod-mag’s display cell, all fixed at the source (GeTe R3m now frames the true 4.23/4.23/10.89 Å γ=120° hexagonal cell);--dosnow rejects being combined with--diagram/--visualize(it silently swallowed them and their flags); the--bond/--conventionalguards run before every mode branch (--star-of-k --conventionalused to be silently ignored). Regression tests: the conventional NaCl frame must be the cubic cell (orthogonal, equal edges) and the Na 2s Bloch signs at X must follow exp(2πi k_conv·r); the NH3 1e/2e sketch-phase check was rewritten to the gauge-invariant bond-directed product Σ_H s_H (p_N·r_NH) — the old max-|s| pick broke on the mirror-hydrogen tie whenever the degenerate-partner canonicalization landed on the other gauge (a BLAS-environment effect, the sketches themselves were correct).Added two-fragment extended-Hückel MO diagrams (
crystod-mol --diagram --xyz FILE.xyz --ao-left FORMULA --ao-right FORMULA, no--pyscf): the molecule is split into two arbitrary submolecules by chemical formula (benzeneH6/C6, CH3OHH4/CO, O2O/O) and drawn as the three-column diagram (left fragment MOs | molecule MOs | right fragment MOs) with the same symmetry + overlap machinery as the single-center mode — all three columns solved in the one molecular AO space (the fragment’s (H, S) sub-block is the isolated fragment in extended Hückel), molecular MOs projected onto the fragment MOs through the shared overlap matrix, COOP bonding colors, VESTA fragment-line colors, orbital sketches, and irrep labels assigned from the MO characters after solving with core shells counted in the numbering (benzene:(2a1g)^2 … (1e1g)^4with the Hückel π ladder 1a2u < 1e1g < 1e2u < 1b1g, label-compatible with--pyscf). A fragment keeping a higher symmetry than the molecule (the exactly degenerate CO π pair of CH3OH under Cs) is decomposed with the irrep projectors; non-invariant fragments and linear molecules fall back to unlabeled levels. Tests in section 33.Fixed (2026-08-01/02): the
--pyscfMolOD orbital sketches drew CORE levels from their valence orthogonalization tails — the r0 = 2 bohr radial weighting of the 2026-07-28 phase fix, taken alone, kills the contracted 1s functions (dead at r0), so e.g. the C 1s combinations of benzene’s C6 fragment rendered as 2p-like lobes. Each (atom, l) sketch channel is now rescaled to its Löwdin population (signs/orientation still from the r0 amplitudes; the same two-step recipe as the crystal engine’s sketches), the r0 amplitudes gained the missing per-primitivegto_normnormalization (verified to reproduceeval_gtoto 8 digits; without it the per-shell weights were off by 0.28–7.7× and could flip a channel’s relative sign), and non-degenerate levels get a deterministic dominant-lobe-positive phase. Core levels are drawn as the pure s spheres they are (Löwdin p admixture of the benzene C-1s a1g state: 0.22%, honestly drawn at √0.002 ≈ 4% of the s lobes); the NH3 1e/2e phase regression test still passes and the example diagrams were regenerated. — the--chkrestart files are binary (compressed npz, for size and load speed), so the calculation conditions they lock in were invisible until a parameter-mismatch error spelled one of them out.--chk-infoprints everything the file stores — structure, method (XC/basis/pseudo/ke_cutoff/k-mesh/max_l/smearing), electron count and oxidation states, fragment split, which densities it holds (full three-SCF run vs an--onsitecrystal-only checkpoint) on how many k points × AOs, the SCF energies — plus a ready-to-pastereuse with: --co-left … --kmesh … --chk FILEoption string that reproduces the stored conditions exactly (with--onsiteappended automatically for crystal-only files). The parameter-mismatch error now points to it.crystod --band --pyscf: electronic band structure and fatbands from the recorded density matrix (crystod/band_pyscf.py): the whole-band-structure companion to--dos— read the full dispersion first, then zoom into the special k points with the crystal-orbital diagram. VASP-style two-step: the SCF (or a--chkrestart, including the crystal-only checkpoints of--onsiteruns via--onsite) provides the density matrix,get_bandsdiagonalizes it non-self-consistently along the automatic seekpath high-symmetry path (--band-pointsper leg, default 41); E_F/VBM/CBM come from a zero-temperature filling of the uniform SCF mesh (metal-safe), plots are VBM-referenced (--align absoluteto disable) and the terminal names the band-edge k points (ScF3: VBM at R, CBM at Γ, gap 5.199 eV — matching the--dosmesh gap exactly).--fatbandadds the same per-AO projections the DOS and the diagram compositions use (--projectionLöwdin default): one overview page with every element in its VESTA color, plus one page per element with the s/p/d/f breakdown (total | s | p | d panels, weights as dot sizes). Outputs:BAND_<structure>.pdf(+_fatband.pdf,_fatband_<El>.pdf), a long-format CSV of every band energy (and every (element, l) weight), and a TXT summary. The chk parameter guard applies as usual — a--max-l 2checkpoint needs--max-l 2here too, and the mismatch message names the offending option.Fragment-column levels show their composition too (
--diagram, both engines): a repeated fragment label (F 2p GM4-#1/#2, or a fragment-SCF eigenstate whose label only names its dominant shell) used to be opaque — the Level-details bars and hover tooltip now show every fragment/onsite level’s per-shell AO populations in the same--projectionmeasure as the crystal levels, listing the level’s own sublattice shells only: a fragment level is a sublattice state, and per-shell entries of the other element read as contamination (user feedback on SrTiO3 — “O states inside a Ti-only level”). The weight that does sit beyond the own shells — the counterpoise ghost tail in the fragment-SCF mode, and in every mode the Löwdin attribution of the overlap density (the state’s coefficients stay on its own side) — is aggregated into one closing note, e.g.Loewdin: O 2p 78.1%, O 3p 16.7% (+5.2% on the removed sublattice's basis: ghost tail + Loewdin attribution). The full cross-side breakdown of an early draft of this display is what exposed the--onsiteblock pathology below (a “Ti 3d GM3+” whose bars read Ti 3d 39.5 / Ti 4d 27.6 / O 2s+3s 28%).--onsite: the single-Hamiltonian crystal-orbital diagram (--diagram --pyscf): the default mode draws each fragment column from its own SCF (formal-charge ions + point charges + ghosts), so a crystal level’s vertical offset against its parent mixes the bonding shift with a site-dependent environment error that no rigid alignment can remove — quantified on SrTiO3 at R with the on-site decomposition d = ⟨φ_frag|F_crystal|φ_frag⟩: the chemically inert Sr 4s column sat 1.59 eV too deep (pure reference error, zero bonding), making the weakly bonding Sr 4p crystal level (P=+0.031, true bonding shift −0.06 eV) appear risen by +1.5 eV — a bonding state that looked antibonding.--onsiteremoves the problem structurally: only the crystal SCF runs, and the fragment columns are the per-(element, shell) on-site multiplets of its converged Fock — F(k) diagonalized within each shell’s own symmetry-adapted Bloch orbitals (the tight-binding on-site energies ⟨φ|F|φ⟩; one level per induced irrep, matching the site-symmetry table exactly; no cross-shell mixing). Whole-sublattice blocks were tried and rejected on user scrutiny: with this diffuse basis the raw AO block lets cation functions fall variationally into the removed side’s potential wells (a “Ti 3d GM3+” at O-2p depth with 28% Löwdin weight on O — a disguised anion state, the ghost/BSSE pathology in new clothes), while Löwdin- or projection-orthogonalized blocks load strongly-overlapped shells with huge orthogonalization penalties (the O 2s on-site swung −6.9 → +0.5 → +12.6 eV across the three block conventions; compact shells agreed throughout) — the per-shell Rayleigh quotient has no variational freedom to abuse, and it is the d = ⟨φ|F_crystal|φ⟩ reference of the decomposition analysis above, so the parent→crystal offset is the orbital interaction by construction. (The extended-Hückel engine’s whole-side block stays safe because its minimal STO basis offers no diffuse escape routes.) One operator for every column means no fragment SCF, no point charges, no reference alignment: on SrTiO3 at R the Sr 4p parent (−5.44) now sits above its weakly bonding crystal level (−5.54), the inert Ti 3s column coincides with its crystal counterpart to 0.2 eV, the O 2s band’s genuine +0.43 eV closed-shell push-up by the Ti 3p semicore below it (upper member of a filled–filled pair; its COOP stays bonding-positive through the valence-channel donation into the empty Ti 4p — energy shift and overlap population are different observables) is displayed honestly instead of being masked by the alignment, and on ScF3 at R the diagram finally reads like the textbook: bonding R3+ below its F 2p parent, the antibonding t2g LUMO above its Sc 3d parent (t2g 3.82 / eg 4.01 bare splitting — the large crystal eg–t2g gap emerges from the hybridization, not the bare ligand field). A full-run--chkis reused (only the crystal density is read;--no-ghostdifferences are ignored there, being numerically inert); an--onsite-written chk holds only the crystal density and a full-mode run against it aborts with a clear message.crystod --dos --pyscfaccepts--onsitetoo — the DOS never needs the fragment densities, so it runs on a single SCF and on the crystal-only chk files. Column occupations remain the formal ionic counts (a display convention, stated in the page footer). Also fixed in passing: the PySCF diagram pages carried the extended-Hückel footer (“VSIP … Wolfsberg-Helmholz”) and a “(full-electron basis)” chip — each engine now writes its own footer/chips.One displayed composition per crystal level — the PySCF AO populations everywhere (
--diagram, extended-Hückel and--pyscf): the Level-details bars used to quote the Löwdin fragment-level projection (“Sc 3d R5+ — 69.7%”) while the hover tooltip quoted the per-(element, shell) AO populations (“Loewdin: Sc 3d 76.0%”) — two genuinely different measures of the same state shown side by side with no way to tell which to trust (the fragment eigenstate “Sc 3d R5+” itself mixes 3d and 4d AO character, so the numbers must disagree — Sc 4d read 19.3% vs 8.9% on the ScF3 t2g LUMO). The panel bars, the tooltip and the terminal now all quote one identical list: the per-(element, shell) AO populations of the PySCF eigenvector in the--projectionmeasure (Löwdin default — non-negative, summing to exactly 100%), i.e. the same partial-charge machinery ascrystod --dos --pyscf, with symmetry doing the orbital selection (a state of irrep Γ only picks up the Γ-adapted combination of each shell; forbidden shells project to ~0) — every entry is labeled with the crystal irrep (Sc 3d R5+ 76.0% F 2p R5+ 9.5% Sc 4d R5+ 8.9% F 3p R5+ 5.7%). The extended-Hückel--diagramgets the same treatment (Löwdin over the Bloch-STO overlaps), so both engines read identically. The fragment projection is demoted to an internal quantity — it still positions the correlation lines and feeds the deep-level alignment anchors (bit-identical before/after), but its weights are no longer displayed as percentages. Verified panel==tooltip==terminal over every crystal level of ScF3 (140 PySCF levels × Löwdin and Mulliken, 81 EHT levels) and SrTiO3 (87 EHT levels).Testsuite section 3 caught up with the whole
--diagramseries (an adversarial review found every regression): the two test commands dropped the removed--atomic-orbitalflag, the terminal regexes follow the#Nlabels and one-decimal percentages, the multi-shell sketch probe parses the currentVARIANTSJSON (its physics assertions — bonding Sc 3p lobe in phase with F 2s at X, antibonding opposite — pass unchanged), the “sketches not embedded” expectations flipped to always-embedded, and the--diagramrequires---co-left/--co-righterror message no longer advertises the removed--atomic-orbitaloption. 41/41 pass.crystod --dos --pyscf: DOS/PDOS and partial charges from the recorded density matrix (crystod/dos_pyscf.py): the--chkfile already stores the converged density matrices — everything the band step needs — socrystod --dos --pyscf -c POSCAR --chk FILE [--dos-kmesh 8 8 8]diagonalizes them non-self-consistently on a dense mesh (VASP-style two-step; ~40 s for ScF3 on 8×8×8 with zero new SCF) and writes, in thecalc_pyscf_dos.pystyle: a Gaussian-broadened total DOS + element×angular PDOS plot (VESTA colors, PDF), a CSV of every curve, and a text summary with the gap (VBM/CBM/E_F from zero-temperature filling of the whole mesh, metal-safe). Partial charges are reported in both conventions side by side — with the diffuse molopt basis they disagree spectacularly (ScF3: Löwdin Sc +0.004 because the diffuse Sc 5s/4d claim the density sitting on the F sites, vs Mulliken Sc +1.57/F −0.52 with the semicore 3s²3p⁶ intact and the covalent d occupation 1.06) — an atomic partition of a continuous density is a convention, and both sums reproduce the electron count to 32.0000 as a built-in sanity check. Unlike the diagram’s composition lists, the PDOS weights live directly in the AO basis (no fragment-state projection), so the fragment double-counting issue cannot occur;--projection lowdin|mullikenpicks the PDOS measure.Löwdin-orthogonalized composition lists (
--diagram, both engines): the composition panel used to show renormalized plain projections |⟨φ_fragment|S|ψ⟩|² — but the fragment eigenstates of the two columns are mutually non-orthogonal (⟨Sc 4p|S|F 2s⟩ = 0.69 in ScF3), so the projections double-count shared density: the occupied F 2s R4- band state summed to 144% before renormalization and displayed as “Sc 4p 34%”, diffuse virtuals (F 3s at +48 eV) appeared at 9% inside the semicore bands, and the t2g LUMO showed a spurious “F 3d 0.9%”. The compositions are now computed in the Löwdin-orthogonalized fragment-level basis (weights |G^{−1/2}Φ†Sψ|², G = Φ†SΦ — the orthonormal set closest to the original fragment states, so the labels keep their meaning; the weights sum to the span completeness ≤ 100%): the same states become F 2s 76.5 / Sc 4p 15.8 / Sc 3p 7.5, Sc 3p 87.6 with F 3s down to 3.4, and Sc 3d 99.8 with the F 3d admixture gone. The deep-level alignment anchors intentionally keep the raw projections (their purity criteria were validated on them; anchors and shifts are bit-identical before/after). Note the composition percentages are CrystOD’s decomposition of the PySCF eigenvectors, not a PySCF charge analysis — the per-(element, shell) Löwdin/Mulliken row in the tooltip is the population-style companion number.Resonance-based semicore rule in the bonding-character classifier (
--diagram, both engines): one filled semicore shell acts in two distinct ways, and the classifier now distinguishes them per level. Resonant filled–filled pairing (Sc 3p × F 2s of ScF3, 6 eV apart) is genuine He₂-like closed-shell physics — its occupied partners now show as the bonding/antibonding pair they are (R4-/X3- #1 blue with P ≈ +0.05, #2 red with P ≈ −0.03..−0.05). Far off-resonant orthogonality tails of the same shell (Sc 3p inside the F 2p band, 23 eV above) are excluded — previously they flipped the sign of donation-bonding states: the occupied M3- of ScF3 at −4.18 eV read “antibonding −0.045” although its Sc 4p × F 2p channel is bonding +0.058, because the 1.5% Sc 3p tail contributed −0.102 (and Sc 3p escaped the old per-fragment-HOMO semicore flag, being the HOMO of Sc³⁺ itself). Semicore shells are now flagged against the crystal VBM using reference-consistent Hamiltonian expectations ⟨φ|F|φ⟩, and excluded from a level’s P only when the level lies more than 10 eV from that shell’s band top. ScF3 outcome: M3- #2 → bonding +0.059, GM4- #2 sharpens from +0.013 to +0.111, R1+ #2 → +0.112, symmetry-nonbonding states stay exactly 0, and the R4-/X3- closed-shell pairs keep their textbook blue/red split. The molecular engines keep the legacy per-fragment-HOMO rule (adequate there; the pathology needs a highly charged cation whose fragment HOMO is the semicore).Löwdin-corrected coupling table (
--diagram --pyscf): the fragment levels of the two columns are not mutually orthogonal, so the bare matrix element ⟨φ_L|F(k)|φ_R⟩ carries an unphysical overlap-times-mean-energy part |S|·(e_L+e_R)/2 — a compact semicore level against a diffuse virtual showed |H| of 5–15 eV without any real resonance (Sc 3s × F 2p read 12.5 eV, Sc 4p × F 2s 14.7 eV). The reported strength is now the first-order Löwdin-orthogonalized coupling |H̃| = |⟨φ_L|F|φ_R⟩ − S·(e_L+e_R)/2| with e_X = ⟨φ_X|F|φ_X⟩ (invariant under the G=0 reference), and_coupling.txtgains |S| and |H|raw columns so the overlap contamination is visible (ScF3 R point: Sc 3p × F 2s 4.3 → 1.7 eV, Sc 3s × F 2p 12.5 → 5.7, Sc 4p × F 2s 14.7 → 7.1). Couplings against the very diffuse F 3s/3p/3d fragment virtuals legitimately stay at the eV scale — a state with ⟨r⟩ ≳ 1.1 Å (plus its counterpoise ghost tail) has large Fock elements with everything; the physically suppressing factor is ΔE, i.e. themixcolumn the table is sorted by. Reminder that ⟨i|H|j⟩ itself does not vanish for large ΔE — the mixing |H|/ΔE and the 2nd-order energy |H|²/ΔE do.Bonding-character colors in every diagram + VESTA element colors for the fragment columns: the COOP classification below now runs in all four diagram commands —
crystod --diagram(extended-Hückel and--pyscf) andcrystod-mol --diagram(both engines), through one shared classifier (crystal_orbital_diagram.assign_bond_characters); NH₃ verifies the molecular case (σ bonding blue, σ* red, the N 1s core black, valence MOs carrying the “(semicore N 1s excluded)” note). The fragment/sublattice columns are no longer occupied-blue/empty-gray: every fragment level is drawn in the VESTA color of its dominant element (Sc levels purple, F levels light blue-violet, Sr/Ti/O each their own), so a composite sublattice like SrTi reads at a glance; occupation stays visible through the electron arrows.Bonding-character colors in the crystal-orbital diagram (
--diagram --pyscf): crystal-orbital lines are now colored blue = bonding / black = nonbonding / red = antibonding, classified by the COOP-style left–right overlap population P = 2 Re[c_L† S_LR c_R] of each eigenstate (P > 0: charge accumulates between the sublattices; P < 0: internuclear node; exactly 0 for symmetry-nonbonding states such as the GM5- of ScF3 — the value is shown in every level’s tooltip, and the footer carries the legend). Two rejected alternatives, for the record: a plain energy comparison with the parent fragment levels fails because point-charge-vs-crystal environment shifts (~0.5 eV) drown the ionic splittings, and an (F − E·S) energy partition is unusable in this non-orthogonal basis (diffuse-shell Mulliken cross terms dominate — the ScF3 σ level came out “antibonding +2.45 eV”). Semicore orthogonality tails are excluded from P (shells whose own bands lie ≥ 10 eV below their fragment column’s HOMO, unless the level is that semicore band): with the Sc 3s tail included the bonding σ R1+ of ScF3 reads P = −0.015, without it P = +0.112 — the same semicore-node story as the sketches, now in the classification. Note the generic COOP asymmetry: |P| of antibonding partners is systematically larger than of their bonding counterparts (0.5–6 vs 0.01–0.2 here).PySCF eigen-levels in the SALC viewer (
crystod --visualize --pyscf): the SALC viewer shows the symmetry-adapted basis — the states before any Hamiltonian; it can now show the actual PySCF eigenstates in the same interactive page.crystod --visualize --pyscf -c POSCAR [--sublattice Sc|F3] --bond Sc F 3 --real-coefficient: without--sublatticethe full crystal’s levels (the states after bonding), with it one fragment sublattice’s pre-bonding levels (formal-charge ions + ghost basis + point-charge Madelung field — exactly the--diagram --pyscfcolumns). The SALC-basis table becomes Mode | Irrep | Comp. | Energy (eV) — one row per degenerate partner, labels in the diagram convention (GM4- #2,Sc 3d R3+), energies on the shared deep-level-aligned scale so the Sc page, the F3 page and the crystal page are directly comparable — and clicking a row renders the eigenvector’s wave function (every element, all shells s..f, population-calibrated lobe sizes with a per-level shared normalization) on the k-commensurate display cell with the usual bonds/polyhedra. The special k points are enumerated automatically from the space group (one page per k;--kpoint GMrestricts). All--diagram --pyscfoptions apply (--basis/--xc/--kmesh/--ke-cutoff/--max-l/--projection/--no-align/...), including--chk: the three ScF3 commands above share one SCF through one checkpoint file (~5 s per page set after the first run).--window LO HIoverrides the default energy window (HOMO−15 .. LUMO+10 eV of the displayed column), which keeps the page sizes manageable.--diagonalizecanonicalizes degenerate partners (RREF): the SCF’s arbitrary unitary mixture — which draws e.g. a tilted d_z² — becomes axis-aligned components (the R5+ t2g triplet of ScF3 turns into pure d_xy/d_yz/d_xz) with unchanged energies; each channel’s drawn sign is probed at the 1.5–3 bohr radius where its amplitude is largest, so a semicore orthogonalization node at one radius cannot flip it (note that a valence σ level can faithfully look “antibonding” near a semicore-carrying atom — the ScF3 R1+ case carries the real orthogonality node against the −50 eV Sc 3s band, verified against real-space pbc_eval_gto).--valence-onlydrops such semicore shells (⟨r⟩(Sc 3s) = 0.77 Å vs the 2.03 Å bond; auto-detected as occupied fragment bands > 12 eV below the crystal VBM and printed — Sc 3s/3p and F 2s here) from the drawn wave functions, so the σ level shows its bonding Sc 4s component; levels a semicore shell dominates (the semicore bands themselves) keep it.VESTA-style periodic boundary in the orbital sketches (
--diagram, extended-Hückel and--pyscf): the hover wave-function sketch used to draw only the bare atom list of the k-commensurate supercell; it now draws the crystal the way VESTA does — atoms on the supercell boundary appear at every translationally equivalent position (a corner atom at all eight corners, an edge atom on all parallel edges), each carrying the wave function, and the supercell outline is drawn as a dashed frame. The boundary images carry identical lobes by construction: within the k-commensurate supercell the Bloch function is exactly periodic (e^{ik·T_super} = 1), which is precisely why that supercell is the drawing unit — at R the frame is the 2×2×2 cell and the sign alternation between primitive cells stays visible inside it. Bonds across the boundary now appear too. Molecular (crystod-mol) sketches are unchanged.Extended-Hückel
--diagramfragment columns now number repeated (element, shell, irrep) labels — e.g. the two F 2p GM4- combinations of ScF3 at GM becomeF 2p GM4-#1/F 2p GM4-#2in the terminal, the HTML, and the crystal composition lists — the same convention--diagram --pyscfalready used.--chk(WAVECAR-style restart),--projection, saved coupling tables,#Ncrystal labels (--diagram --pyscf):--chk FILEwrites the three converged density matrices (all the band step needs) plus the defining parameters after the SCFs, and a rerun with the file present skips all three SCFs entirely — the parameters (basis, functional, k-mesh, cutoff, fragments, structure, …) are verified first and a mismatch aborts with the offending names.--projection lowdin|mullikenselects the population measure used for the sketch lobe sizes and the per-(element, shell) rows (default: Löwdin;mullikenrestores Mulliken gross populations — note the two disagree strongly on diffuse empty levels, where atomic attribution is inherently convention-dependent). The full same-irrep resonance-integral tables<φ_left|F(k)|φ_right>(all pairs with |H|, ΔE, and the two-level mixing fraction, per k point, on the aligned scale) are now written to<output-stem>_coupling.txtnext to the HTML — the terminal keeps showing the top 8. Crystal-column levels are labeledGM4- #2(second GM4- multiplet from the bottom, the same#Nconvention as the fragment columns) instead ofGM4-(2), which read like a degeneracy count; the extended-Hückel--diagramuses the same format now.Population-true sketch lobes and a Löwdin row in the Level details (
--diagram --pyscf): a crystal GM4- level of ScF3 (--max-l 2) drew a p lobe on the Sc site at 83% of the largest F lobe while its composition list read as pure F — misleading in opposite directions, because the state genuinely carries ~8% Sc p weight (mostly diffuse 4p + semicore 3p: the symmetry-allowed T1u ligand-to-metal donation; every basis function is atom-centred, so nothing sits in the interstitial voids). (i) The sketch used to scale lobes by the wave-function amplitude at r0 = 2 bohr, where the diffuse gth-dzvp Sc 4p is 5.4x an F 2p — a 2–3x exaggeration over any population measure; lobe sizes now follow the per-(atom, l) Löwdin population — |coefficient|² in the symmetrically orthogonalized basis, the orthonormal set closest to the atomic orbitals, i.e. the intuitive “squared LCAO weight” made rigorous for a non-orthogonal basis (non-negative and summing to 100%, where Mulliken’s overlap cross terms go negative or overshoot on diffuse empty levels) — with signs and orientation still taken from the r0 amplitudes so semicore orthogonalization tails keep their inverted phases. (ii) The composition list projects onto the surviving fragment levels only — under--max-l 2the empty Sc 4p GM4- fragment level is removed by the ghost filter, its 4.5% absolute projection silently vanished and the renormalization inflated the F entries to 92.3%; every crystal level’s detail tooltip and terminal row now add the full per-(element, shell) Löwdin populations and state the fragment-projection coverage whenever it falls below 98%.Exact degeneracies via Fock group-averaging;
--max-l(--diagram --pyscf): the PySCF SCF carries no point-group constraint, so the FFT grid breaks degeneracies numerically — negligibly for semilocal functionals (<1 meV for PBE/ScF3), but by 0.4–0.8 eV for grid-evaluated exact exchange (B3LYP at ke_cutoff 80), which tore a GM5- triplet into a doublet plus an unlabeled level. The displayed levels are now re-diagonalized from the group-averaged Fock F̄ = (1/|G|) Σ_g D(g)† F D(g) built with the verified AO representation, so every degeneracy is exact by construction; the raw symmetry-breaking magnitude is printed per k point (--no-symmetrizeshows the raw spectrum), and a NOTE recommends ke_cutoff ≥ 150 for hybrid energetics. The new--max-l Ldrops basis shells above l = L (e.g.--max-l 2removes the f polarization functions, whose tails otherwise show up as small Sc-4f/Ti-4f weights in the O-2p/F-2p valence compositions — a basis-decomposition effect absent from VASP’s atomic-sphere projections).Ghost-aware fragment sketches and
--no-ghost(--diagram --pyscf): a fragment level can legitimately carry weight on the ghost basis of the removed sublattice (symmetry-allowed variational tail toward the point charges, the counterpoise design), but the sketch drew that as an atom-centred orbital on the empty site — and the coefficient-based drawing exaggerated diffuse ghost functions far beyond their density share (an F 2p R1+ level with 10% Mulliken ghost weight drew its peak on the empty Sc site). Fragment sketches now draw only the fragment’s own sublattice components, the ghost weight of every level is printed in the terminal report and shown in the Level-details panel, and the new--no-ghostoption applies the hard constraint instead: the removed sublattice’s basis functions are excluded from the fragment variational space entirely (measured BSSE on ScF3: ~2 mHa per fragment), with the small-basis fragment states zero-padded back into the shared AO space for the projections.Frontier-centered default view, always-on wave-function sketches, faster
--pyscf(crystal-orbital diagrams): the interactive energy window now opens on ±8 eV around the HOMO/LUMO midpoint (the VBM/CBM region) instead of a fixed −20..10 eV;--diagramno longer takes--atomic-orbital— every level (extended-Hückel and--pyscf) embeds the hover wave-function sketch of all its atomic-orbital components (the old filter could render a level by its minority admixture, e.g. Sr 5s drawn as its Ti 4s tail; same-l shells accumulate with radial weights at r0 = 2 bohr, contracted-GTO weights in the PySCF case); the--pyscfSCF now solves only the irreducible wedge of the k-mesh (symmetry-reduced, expanded withto_khf, point-charge Hamiltonian re-attached) and starts each fragment from the crystal density restricted to its own sublattice AO block, which both speeds it up and fixes the SrTi(6+)/BaTi(6+) convergence failures; a non-converging SCF escalates through a Fermi-smearing / virtual-level-shift ladder automatically. Deep-level (XPS-style) column alignment added in the same series: each of the three calculations pins its own G = 0 average potential, so the raw columns are offset by one rigid k-independent constant each — the deepest chemically inert fragment level (absolute counterpart purity ≥ 80%, near-purest anchor pairs only) keeps its pre-bonding energy (--no-alignto disable).Added quantitative crystal-orbital diagrams via PySCF PBC (
crystod --diagram --pyscf -c POSCAR --co-left Sc --co-right F3 [--basis ...] [--pseudo ...] [--xc ...] [--kmesh N N N] [--ke-cutoff E] [--sigma S] [--oxidation El=Q ...] [--no-align]), the crystalline counterpart ofcrystod-mol --diagram --pyscf(crystod/crystal_orbital_pyscf.py): three periodic KRKS/KRHF calculations sharing one AO space — each fragment keeps its sublattice as real formal-charge ions plus the removed sublattice as ghost basis functions (counterpoise-consistent) and its formal-charge point lattice (jellium-referenced FFT potential, validated againstpoint_charge_field.ewald_site_potentialto 2e-6 eV for neutral arrays; every cell is neutral, electron counts even and additive). The diagram is built at the special k points only (SCF mesh defaults toround(8 Å/|a_i|)), every level is labeled with its little-group irrep by crystod’s own machinery evaluated directly in the PySCF AO basis — the periodic-vs-atomic Bloch-gauge conjugationΛ D Λ⁻¹(essential for non-half-integer sites, e.g. fluorite X/L/W) and the l=3 real-orbital reordering are handled, and the representation is verified against PySCF’s overlap (D†SD = S, residual printed) at every k point. Degenerate levels are clustered adaptively (groups merged until the irrep multiplicities are integral, since the FFT grid splits symmetry-degenerate levels by 1e-4..1e-1 eV depending on the system). The three columns are put on one energy scale by deep-level (XPS-style) alignment — each calculation pins its own G=0 average potential, so the raw columns carry one rigid k-independent offset each; the deepest chemically inert fragment level (counterpart purity ≥ 80%) keeps its pre-bonding energy, using only the near-purest anchor pairs (--no-alignto disable) — and the report adds the site-symmetry induced representation of every (element, shell) block plus the same-irrep coupling<φ_left|F(k)|φ_right>of the converged crystal Fock operator with the two-level mixing fraction (same irrep does not imply mixing). Non-converging SCFs escalate through a Fermi-smearing ladder automatically. Examples: ScF3, ZrO2 (fluorite), SrTiO3, BaTiO3 (polar P4mm) inexample/03_hybridization.Added two standalone PySCF PBC example scripts in
example/test_pyscf/:pyscf_band_structure.py(VASP-style two-step band structure — SCF on a regular mesh, then non-SCF diagonalization on the zero-weight k path; reads VASP KPOINTS band paths, overlays VASP EIGENVAL aligned at the VBM, element-projected Mulliken fatbands, DOS,--save-mocrystal-orbital export; reproduces the SrTiO3 VASP reference gap to 0.04 eV) andpyscf_band_localized.py(the same solid solved with molecular-chemistry machinery — Gaussian density fitting, Becke grids, all-electron bases — with a--benchmarkmode; measured conclusion: the localized-basis solid is cheapest with the uniform-grid FFT Coulomb, and the molecular GDF route is orders of magnitude slower for periodic systems).
v0.3.4#
ISO-IR fallback labeling: irreducible representations at NON-special k/q points (symmetry lines, planes, and general points) are now labeled everywhere.
Added
crystod/isoir.py, a parser and labeler for the ISO-IR dataset of the ISOTROPY Software Suite [H. T. Stokes, B. J. Campbell and R. Cordes, Acta Cryst. A 69, 388-395 (2013); iso.byu.edu/irtables.php]. The complex-irrep table (CIR_data.txt.gz, 1.1 MB gzip) ships inside the package, holds the full space-group representation matrices for every k-vector type (points, lines, planes, general point), and is evaluated at arbitrary k with free parameters. A k point absent from the irreptables (BCS) tables — where previous versions fell back to genericirrep_Nnames,custom, or-— now gets Miller-Love / ISOTROPY irrep labels (T1,DT5,LD3,GP1, …) and its k-vector-type letter as the k-point name, acrosscrystod(SALC, with a provenance note in the output),crystod --atomic-orbital,crystod-mag, andcrystod-phonon --vibration/--vector/--modulation(phonopy band sets are decomposed against the ISO-IR irreps, so accidental degeneracy is handled). Tabulated k points keep their Bilbao-convention labels unchanged;--spinoris unaffected (the ISO-IR dataset has no double-group irreps). Conventions handled explicitly: ISO-IR uses the exp(+2 pi i k.t) translation phase (spgrep/irreptables use exp(-2 pi i k.t)), so spgrep characters are matched against the complex conjugate of the ISO-IR characters — for complex-type irreps the two conventions genuinely swap labels (e.g. Pnma R point: Bilbao R1 = ISOTROPY R2) — and the structure is transformed into the ISOTROPY standard setting (origin choice 2 etc.) via the spglib Hall number, verified for two-origin groups such as Fd-3m. Validated against the ISOSUBGROUP-derived label crosswalk (SG 141/142 X, 230 N) and by gauge-free isotropy-subgroup construction (SG 68 T).crystod-phonon --vector/--modulation: output files of a non-special q are named with the ISO-IR k-vector-type letter (phonon_modes_MoS2_U.txt,POSCAR_MoS2_U_mode1_U2_conv.vesta,MPOSCAR_DT_mode1_DT3_Cmcm). Since every q on one symmetry line shares that letter,--keep-q-coordskeeps the coordinate-based q label (q_0.5_0_0.2) so line scans do not overwrite each other (the irrep tag stays).crystod-phonon --irreps --all-irreps: writesphonon_irreps_all.yaml, which additionally lists the phonon irreps at the midpoints of the seekpath k-path segments (the symmetry lines DT, Z, SM, LD, S, T, … connecting the special points), labeled via the ISO-IR tables, together with the seekpath path string (k_path:) and the midpoint list (path_midpoints:). The default run stays the fast special-points-only survey and keeps writingphonon_irreps.yaml, so the two files coexist; the output file name is now printed at the end of the run.crystod --atomic-orbital: the element/orbital tokens accept a hyphen as well as an underscore (Ti-d O-p==Ti_d O_p); a token without a separator is now rejected with an explicit message instead of a bareValueErrortraceback.crystod-group --supergroup-cif(symmetry-mode analysis): every run now also writes one VESTA file per activated irrep,{supergroup_file}_{irrep}.vesta, visualizing that irrep’s displacement pattern as arrows on the parent-derived reference structure (invariant-core cell, largest arrow scaled to 1.5 Å) — e.g. the five frozen modes of Pnma CaTiO3 (X5+, M2+, M3+, R4+, R5+) as separate files.--conventionalswitches the files to the parent conventional basis (_convsuffix).crystod-group --table --space-group SG --kpoint ...: displays the character table of the little group of k — the space-group analogue of the point-group--table— with irreptables (BCS) irrep labels at tabulated k points and ISO-IR (Miller-Love) labels on symmetry lines/planes, Seitz-symbol column headers, and the little-group symbol.crystod-group --basis/--generate-basiswith--space-group --kpoint: non-special k points are labeled via the ISO-IR tables as well (e.g.--basis x y z --sg Pm-3m --kpoint 0 0 0.1givesDT [0, 0, 0.1]withDT1(1) + DT5(2)instead of genericirrep_Nnames), including centred lattices (C/F/I/R). Fixed in the same change: the conventional-to-primitive translation conversion of the--basisspace-group path mixed row/column conventions, which silently broke the symmetry-operation group for the rhombohedral (R) centring — R-group--basisruns now label correctly at special points too. The misleading “No irreps … in irreptables!” warning is no longer printed when the ISO-IR fallback succeeds.crystod-group --product --sg: product terms landing on symmetry lines outside both the CDML tables and the DIRPRO-fitted name map — previously shown with positional names like(1/8,1/8,0)(2)— are now named from the ISO-IR tables and marked[non-tabulated; ISO-IR labels]with a citation note (e.g.N1 x P1 = DT1 + DT4 + DT2 + DT3in I4_132). The DIRPRO-fitted CDML names keep precedence, since the two conventions genuinely differ at some lines (CDML V of I4/mmm = ISOTROPY LD, T/S of the hexagonal groups = ISOTROPY LD/Q, and the DT numbering of Fm-3m/Ia-3d is permuted — recorded incrystod/dirpro_line_names.py). Distinct +k/-k line stars of acentric groups are disambiguated with the CDML “A” suffix (P1 x X1 = LD1 + LD2 + LDA1 + LDA2in I4), matching the PA-point convention; -k line stars themselves are labeled through the complex conjugates of the tabulated +k irreps. Sweep-validated over 43 centred/polar space groups (all pairwise k-star products): every line term is named, dimension bookkeeping closes throughout.Fixed
crystod-group --product --sg 122: every product involving a P or PA irrep of I-42d aborted with “the irreptables characters of P1 … do not correspond to any allowed small representation”. The shipped irreptables P entry of SG 122 is tabulated in the conjugate gauge (its characters are those of the -k partners, matching no allowed small irrep at P itself), so fitted CDML names for P/PA were added tocrystod/dirpro_line_names.pythrough the name-preserving conjugate correspondence — in agreement with the ISO-IR labels at this point and the ISOSUBGROUP P1PA1/P2PA2 conjugate pairing (P1 x PA1 = GM1 + GM4 + GM5,M1 x P1 = PA2,P1 x X1 = LD1 + LD2 + LD3 + LD4).
v0.3.3#
crystod-group grows into a full Bilbao-style representation-theory toolbox: space-group irrep direct products (DIRPRO), isotropy subgroups (ISOSUBGROUP), multi-electron term symbols with exact Racah multiplet energies, POSCAR <-> Bilbao-style CIF conversion, and AMPLIMODES-style symmetry-mode analysis - each validated against its Bilbao counterpart. crystod-mol gains molecular-orbital diagrams from symmetry + overlap alone. The shared section numbers are regrouped strictly by command.
Section renumbering: the shared section numbers of
testsuite.py, theexample/directories, and the documentation are regrouped strictly by command — 1 library core, 2-6crystod, 7-17crystod-group(the v0.3.3 additions now sit inside the group block: 13--supergroup, 14--multiplet, 15--poscar2cif/--cif2poscar, 16--supergroup-cif, 17 CLI regression), 18-20crystod-bz, 21-27crystod-phonon, 28-29crystod-mag, 30-31crystod-md, 32-34crystod-mol. The example directories were renamed accordingly (e.g.example/30_isotropy_subgroup->example/13_isotropy_subgroup,example/17_phonon_irrep->example/21_phonon_irrep).Added space-group irrep direct products:
crystod-group --product R4- R5+ --sg Pm-3mdecomposes the direct product of full space-group irreps (induced over the whole star of each k point) into full space-group irreps with CDML labels — the offline counterpart of the Bilbao Crystallographic Server DIRPRO [M. I. Aroyo et al., Acta Cryst. A62, 115-128 (2006)]. The wavevector selection rules over the star arms are applied exactly (translation sums carried out analytically over the finite factor group), the star-size x small-dimension bookkeeping always closes, and any number of factors is supported. Products landing on symmetry lines outside the tabulated special points (DT, SM, V, T, S, …) are decomposed with on-the-flyspgrepsmall irreps and named from a reference-fitted CDML table; missing -k stars of polar groups (CDML “A” points such as PA of I-43m) are synthesized as conjugate irreps; the shippedirreptablescharacters are refined against exactspgrepvalues (and the broken P-point entry of SG 230 is substituted). Cross-validated line by line against the 9007-product DIRPRO reference set inDIRPRO/(20 space groups, all Bravais classes): 96.8% exact; every remaining difference was traced — with dimension-count and label-bijection impossibility proofs — to inconsistencies in the reference notes themselves (seeexample/07_direct_product/README).Added isotropy subgroups (
crystod-group --supergroup Pm-3m --irrep GM4- [--order-parameter 0 0 a]): computes which space group survives when a distortion with the given irrep condenses along an order-parameter direction, or enumerates all direction types (strata) with their subgroups, cell sizes, indices, and (single-direction mode) the conventional basis/origin of the subgroup. Full-star treatment of zone-boundary irreps (cell enlargement detected from the order parameter’s translation stabilizer), real (physically) irreducible forms, arbitrary space groups. Several irreps at once (--irrep X3- X2+) enumerate the coupled-order-parameter isotropy subgroups on the direct sum of the irreps (single-irrep tables printed first, then the coupled directions with every irrep nonzero; every irrep keeps its own free-parameter letters) — I4/mmm X3- + X2+ gives the hybrid-improper-ferroelectricX3-(0;a) X2+(0;c)-> Cmc2_1 (= A2_1am, Ca3Ti2O7) for same-arm rotation + tilt, and Pm-3m R4+ + M3+ the Howard-Stokes mixed perovskite tilt systems (a-a-c+ -> Pnma). Complex- and pseudoreal-type irreps (Frobenius-Schur indicator) are analyzed in the physically irreducible doubled real form with ISOTROPY-style pair labels (Ia-3d P2 -> P1P2, +-k pairs I-42d P1 -> P1PA1), with automatic handling of conjugate-gauge and origin-choice tabulations and a fitted-name recovery for broken irreptables entries (N of I4_132); the subgroup types are identified through a generic-orbit structure standardized by spglib (robust for subgroup axes along sublattice-cell diagonals), and the real irrep basis is canonicalized so the direction labels stay clean signed patterns. Offline counterpart of ISOSUBGROUP (ISOTROPY Software Suite, https://iso.byu.edu); validated exhaustively against the 910 ISOSUBGROUP reference tables inSUBGROUP/(SG 16-230, all parameter-free k points, 3535 irreps,script/validate_isosubgroup.py): 3533/3535 exact in the full (subgroup, size, index) multiset (25 up to the enantiomorphic partner, 44 up to a verified label swap), the only exceptions being W of Ibca (a spgrep limitation) and the untabulated rhombohedral L star of R-3m.Added multi-electron term symbols (
crystod-group --multiplet T2g2 --pg m-3m [--orbital d]): the Pauli-allowed many-electron states of an electron configuration over point-group irrep shells, with spin multiplicities sorted Hund-first — (t2g)^2 = ^3T1g + ^1A1g + ^1Eg + ^1T2g. The antisymmetrization is exact (symmetrized powers / Schur functors via the Frobenius formula, Murnaghan-Nakayama symmetric-group characters, class power maps of the point group); several inequivalent shells couple by spatial direct products and spin angular-momentum addition (T2g2 Eg1); shell tokens accept bothT2g^2and the quoting-freeT2g2(an unquoted^is a glob character in zsh);--orbital dprints the ligand-field splitting of the parent orbital and validates the occupied shells against it; every result closes with a state-count check (product of C(2 dim, n)). Hole equivalence and closed shells come out automatically ((t2g)^4 = (t2g)^2 terms, (t2g)^6 = ^1A1g). Validated against the standard crystal-field term tables (t2g^n, eg^n, t2g^2 eg^1 in Oh; e^2 in Td/C3v). The ground-state term symbol is always printed (Hund’s rules); with--orbital, the exact Coulomb multiplet energies of every term are computed in Racah parameters (A, B, C for d shells; reduced Slater-Condon F_k otherwise) from exact Gaunt-coefficient two-electron integrals + Slater determinants + spin/point-group projectors on the determinant space — (t2g)^3 gives the Tanabe-Sugano table ^4A2g = 3A - 15B, ^2Eg = ^2T1g = 3A - 6B + 3C, ^2T2g = 3A + 5C — with closed-form configuration-interaction eigenvalues for doubly-occurring terms (e.g. 3A - 3B + 3C +- 3sqrt(2)B in (t2g)^2(eg)^1), a built-in trace-identity check, and the energy-determined ground state (“lowest for any B, C > 0” when provable). The free-ion limit is reproduced exactly ((T1u)^2 –orbital p: ^3P = F0 - 5F2, ^1D = F0 + F2, ^1S = F0 + 10F2).--visualizeadditionally writes the exact term eigenstates as an interactive HTML page: every term’s full Slater-determinant expansion drawn as orbital box diagrams (up/down arrows over the symmetry-identified real orbitals) with exact coefficients, degenerate partners and CI states switchable.Added POSCAR <-> Bilbao-style CIF (
crystod-group --poscar2cif -c POSCAR [--tolerance 0.01]and the inverse--cif2poscar -c FILE.cif [--conventional]):--poscar2cifwrites<POSCAR>.cifin the layout of the Bilbao Crystallographic Server files — spglib-standardized ITA conventional cell and origin, aligned CIF keys with the quoted Hermann-Mauguin symbol and 4-decimal cell parameters, the full conventional-cell operator list as compact unquotedx+1/2,-y,zstrings (proper block first, centring translations included), and one representative site per Wyckoff orbit with 5-decimal coordinates — unlike the pymatgen CifWriter layout (script/poscar2cif.pykeeps producing the pymatgen flavour as<POSCAR>_pmg.cif).--cif2poscarreads any CIF (Bilbao or pymatgen flavour), expands the symmetry operations, and writes the spglib-standardized primitive cell (or, with--conventional, the conventional cell) as a POSCAR in the crystod test-file style to the input path without.cif. Validated against a Bilbao reference CIF (identical operator set; also used directly as--cif2poscarinput), the ITA Pnma general positions, and structure-matching round trips (SrTiO3, ScF3, F-centred NaCl).Added symmetry-mode analysis (
crystod-group --supergroup-cif HIGH.cif --subgroup-cif LOW.cif): the displacive distortion between a high-symmetry and a low-symmetry structure (CIF or POSCAR) is decomposed into symmetry-adapted modes of the parent space group — for every parent irrep the k-vector, order-parameter direction, isotropy subgroup, number of independent modes, and mode amplitude in Angstrom (AMPLIMODES normalization: within the primitive cell of the distorted structure, strain-free parent-derived reference lattice), plus the automatically determined cell relation (strain-tolerant lattice matching + atom pairing), the displacement table, and normalized polarization vectors. The direction and isotropy-subgroup columns are computed with the full induced-irrep/isotropy machinery of section 13; non-invariant subgroup lattices are handled by enlarging to the largest parent-invariant sublattice (complete k stars) with exact amplitude rescaling; polar subgroups get the minimum-distortion origin (acoustic component removed); the lattice matching uses principal strains (20% tolerance, atom-count-based index), so strongly relaxed pairs work; and every run is closed by a projector-completeness check. Offline counterpart of AMPLIMODES [D. Orobengoa et al., J. Appl. Cryst. 42, 820-833 (2009)]; validated against its output for SrTiO3 Pm-3m -> I4/mcm (R5-, 0.3303 A, entry by entry) and the F-centred ZrO2 Fm-3m -> P4_2/nmc (X2-, 0.5773 A), plus ferroelectric BaTiO3 -> P4mm (GM4-) and large-tilt AlF3 -> R-3c (R4+, ~14% lattice strain), and cross-validated against the section-25 modulation structures (ScF3 R-3c/Im-3/P4mbm/Imma/I4mmm/Pbnm; frozen amplitudes recovered exactly, including the multi-irrep Pbnm case R4+ + M3+ with inactive secondaries X5+/M2+/R5+). f shells are fully supported (--orbital f, reduced F0/F2/F4/F6 output; hydrogenic 4f ratios F4/F2 = 0.138, F6/F2 = 0.0151 as the reference for CI blocks and the ground state) — e.g. (T1u)^3 = ^4A1u + ^2Eu + ^2T1u + ^2T2u with ^4A1u = 3F0 - (105/4)F2 - (189/2)F4 - (3705/4)F6.Added molecular-orbital diagrams (
crystod-mol --diagram --xyz FILE.xyz): the MO diagram of a single-center molecule (NH3, CH4, SF6, …) from symmetry and overlap alone — ligand SALCs + central-atom orbitals combined per irrep with the Wolfsberg-Helmholz approximation H_ij = K S_ij (H_ii + H_jj)/2 over exact single-zeta-STO overlap integrals (prolate-spheroidal quadrature; ligand-ligand overlap included), i.e. symmetry-adapted extended Hückel [M. Wolfsberg and L. Helmholz, J. Chem. Phys. 20, 837 (1952); R. Hoffmann, J. Chem. Phys. 39, 1397 (1963)]. Writes an interactive HTML/SVG diagram by default (ligand AOs | ligand SALCs | MOs | central AOs, correlation lines weighted by composition, electron arrows, HOMO/LUMO markers, per-level composition panel, adjustable energy window) and prints the SALCs, the inter-fragment overlap integrals, and the full MO table. The MO numbering counts core shells as in photoelectron spectroscopy: CH4 (2a1)^2 (1t2)^6, NH3 (2a1)^2 (1e)^4 (3a1)^2 (3a1 lone-pair HOMO), SF6 filled to the nonbonding F 2p block. Elements H-Cl. With--pyscfthe diagram becomes quantitative: three PySCF SCF calculations in one AO space (fragments with ghost basis on the removed atoms, counterpoise-consistent; spin/spherically averaged by fractional occupations) give the pre-bonding fragment columns and the exact projection of every molecular MO onto them, with crystod-convention irrep labels assigned from the MO characters on the PySCF AO basis (σ/π labels for linear molecules);--ao-left/--ao-rightselect any fragment partition by formula (O2O/O, CH3OHH4/CO, benzeneH6/C6), and--basis/--theory/--xc/--charge/--spinfollow script/calc_pyscf.py [Q. Sun et al., WIREs Comput. Mol. Sci. 8, e1340 (2018)]. Every level of both diagram flavours carries a drag-rotatable orbital sketch in the hover panel (molecule + VESTA-style +/− lobes from the actual eigenvector, degenerate partners switchable), and the energy window of the page is interactive (input boxes / Ctrl+scroll zoom / drag pan; with all-electron core levels below -40 eV the default window is clamped to [-40, 15] eV and a Show-all-energy-levels button restores the full range). Fixed (2026-07-28): the--pyscfsketches compressed each atom’s contracted s/p functions by a bare coefficient sum, which can invert the far-field lobe sign (tight and diffuse contracted functions enter one MO with opposite signs) — the bonding 1e of NH3 was drawn with an inverted N-2p lobe and looked identical to the antibonding 2e; every contracted function is now weighted by its radial amplitude at r0 = 2 bohr before summing, so the drawn phases are the real wave-function phases (verified against real-space psi evaluation; regression test in section 33), and the example + binary pyscf diagrams were regenerated.CLI polish:
--space-group/--point-groupgained the no-hyphen aliases--spacegroup/--pointgroupeverywhere (in addition to--sg/--pg),crystod-bz --show-kpointalso accepts--sg/--spacegroup, and space groups can be given by number (1-230) as well as by symbol in all--sgmodes (crystod-bz --show-kpoint --sg 221,crystod-group --basis x y z --spacegroup 221 ...).SALC viewer pages are now ~40-190x smaller (e.g. the R-point PySCF eigen-level page of ScF3: 19.5 MB -> 180 kB; SALC_F_p_R: 17 MB -> 116 kB): the orbital-lobe surface grids — previously ~97-99% of the HTML — are no longer embedded. Each lobe’s angular function Re[sum_m c_m X_lm] is a homogeneous degree-l polynomial on the unit sphere, so the page now ships only the exact polynomial coefficients per lobe (fit residual < 1e-6, asserted) plus one shared lobe-builder JavaScript that reconstructs the identical surface grids client-side at load. Appearance, mode switching, depth fade, opacity and compass behaviour are unchanged; the classic, molecular, and PySCF eigen-level viewers all share the writer and shrink alike.
Added crystal-orbital diagrams (
crystod --diagram -c POSCAR --co-left SrTi --co-right O3 [--atomic-orbital Ti-3d Ti-4s O-2p] [--oxidation Sr=+2 Ti=+4 O=-2]): the analysis CrystOD is named after — the crystalline analogue of thecrystod-mol --ao-left/--ao-rightMO diagram.--co-left/--co-rightsplit the crystal into two fragment sublattices by chemical formula, each treated with its full-electron basis (WIEN2k-style: every core and valence shell; valence with the extended-Hueckel parameters, cores with Slater-rule exponents and the archived neutral-atom PySCF Hartree-Fock/def2-svp levels — one calculation per element inreference/atomic_level_*, generated with the user-standardscript/calc_pyscf.pydriver viascript/generate_atomic_levels.py, collected intocrystod/atomic_levels.pywith angular-momentum shell assignment and an archive cross-check; beyond Kr the def2 ECP freezes the deep cores, which are omitted exactly as in pseudopotential DFT), and each fragment feels the removed sublattice as a point-charge lattice with the formal oxidation states (pymatgen-guessed or--oxidation) — the Madelung ligand field of the pre-bonding states, entering as exact same-site STO matrix elements (real-spherical-harmonics Laplace expansion in the wigner_D_real convention, closed-form radial integrals, Loewdin-consistent with the on-site metric, near shells explicit + Ewald long-range tail; validated against the NaCl Madelung constant and the exact 6Dq/-4Dq octahedral ratio), so the fragment d states carry the true electrostatic t2g/eg splittings; the background-dependent monopole of the charged sublattice array is omitted (it cancels against the intra-atomic charging energy absent from EHT VSIPs; the jellium-referenced values are printed). At every special k point the Bloch orbitals are symmetry-adapted per little-group irrep, all overlaps are evaluated exactly as Bloch lattice sums of STO integrals (double-zeta d shells, generic sigma/pi/delta Slater-Koster assembly via exact real Wigner-D rotations, validated to machine precision; per-shell-pair cutoffs probed from the actual STO tails — diffuse cation shells reach 30+ bohr), and the Wolfsberg-Helmholz generalized eigenproblem (diagonal carrying the same-orbital neighbour-cell Bloch sums; ligand-field blocks added identically to fragment and crystal Hamiltonians, symmetry invariance of S and H self-checked per k point) implements the COD mixing rule (same irrep -> bonding/antibonding, no partner -> rigorously nonbonding); near-linearly-dependent Bloch combinations of the diffuse cation shells (overlap eigenvalue < 0.2, the extended-Hueckel overlap catastrophe) are removed by canonical orthogonalization and counted in the report. Output: per-k terminal tables (fragment levels labeled by their dominant element+shell) and an interactive HTML with one diagram per k point (k-point buttons, composition-weighted correlation lines, electron arrows, HOMO/LUMO, full-electron filling with--electronsoverride, energy window opening on -20 .. 10 eV with a Show-all button for the core levels, and — with the optional--atomic-orbitalsketch filter — a hover wave-function sketch: Re[psi] of exactly the selected components on the k-commensurate supercell with s/p/d lobes, drag-rotatable with switchable degenerate partners). The extended-Hueckel parameter tables were extended to H-Bi (3d/4d metals with the standard double-zeta d contractions). ScF3 (Sc | F3, 48 electrons) and SrTiO3 (SrTi | O3, 56 electrons with the ECP-28-frozen Sr core) reproduce the symmetry-resolved perovskite bonding picture: flat core levels at the archived atomic energies, fragment t2g below eg in the octahedral anion field, F-2s/Sc-3d(eg) sigma bonds at GM with the t2g GM5+ rigorously nonbonding, and sigma/sigma* eg + pi/pi* t2g splittings with nonbonding R4+/R5- at R. Fixed (2026-07-28): the--atomic-orbitalsketches wrote same-l shells of one atom into the same s/p/d slots by plain assignment, so a multi-shell filter (e.g.Sc-p= 2p+3p+4p) drew only the last shell’s raw STO coefficients — a semicore level like Sc 3p came out as the tiny sign-inverted 4p orthogonalization tail, the crystal analogue of the contracted-GTO compression bug in thecrystod-mol --pyscfsketches; the shells now accumulate, each weighted by its STO radial amplitude at r0 = 2 bohr, so the drawn lobe phases are the real wave-function phases (regression test in section 3: the X-point Sc-3p/F-2s bonding/antibonding pair of ScF3), and the example diagrams were regenerated.
v0.3.2#
Added
crystod-mol, a new sectioned command for molecules (XYZ files):--symmetrydetects the molecular point group with pymatgen (Schoenflies + Hermann-Mauguin symbols and the symmetry operations by class — the molecular analogue ofphonopy --symmetry), and--element EL --orbital s|p|d|fbuilds molecular SALCs: the site-permutation characters are multiplied by the orbital characters, decomposed into point-group irreps (same character tables and labels ascrystod-group), and the explicit SALCs are projected out (via the permutation x real-orbital Wigner-D representation, so p/d/f orbital components mix correctly).--alignrotates the molecule into the standard point-group orientation for textbook axis conventions,--show-matrixprints the site-permutation matrices, and--tolerancecontrols the symmetry detection.--visualizewrites the SALCs as the same standalone interactive 3D HTML viewer ascrystod --visualize(molecule in a vacuum box at Gamma; no cell edges, x/y/z compass;--outputand--bond EL1 EL2 MAXsupported). The detected symmetry operations are snapped onto the exact character-table matrices, so the SALC coefficients are clean small integers whenever possible. Addspymatgenas a dependency.Added
crystod-bz --show-kpoint --space-group SG: prints the special (high-symmetry) k points of a space group with their CDML labels (fromirreptables, the same k-point definition as the SALC/irrep analyses) in the primitive reciprocal basis, and additionally in the conventional reciprocal basis for centred lattices (F, I, C, A, B, R) such as Fm-3m, where the two definitions differ.testsuite.pygained thecrystod-molsections (32 and 34 in the current numbering) with the molecule files inexample/test_XYZs(O2, H2O, NH3, CH4).Fixed: the SALC viewer (
crystod --visualizeandcrystod-mol --visualize) squeezed the structure into the left half of the viewport and opened too zoomed-in, clipping part of the structure. Cause: with the compass added as a second plotly scene, the main scene had no explicitdomainand was grid-split to half the width. The main scene now claims the full plot area and the initial camera starts further out, so the whole structure is visible on load (the storedexample/05_visualized_basispages were regenerated accordingly).
v0.3.1#
crystod-phonon --modulation workflow overhaul — corrected frequencies, CDML irrep labels, a mode-table preview, and informative default file names — plus star-arm-aware k-point/irrep labeling across all commands.
crystod-phonon --modulationcan now be run with--qpointonly (no--mode): it prints the mode table (mode number, frequency, irrep, degeneracy) and the star of q, then exits without generating a structure — useful for inspecting the modes at a q point before choosing which to apply.Fixed: the frequencies in the
--modulationmode table were wrong whenever an irrep occurs more than once at the q point (each irrep-projected block was diagonalized on its own, dropping the coupling between blocks carrying equivalent irreps — e.g. every X-point mode of Sr3Ti2O7 except the singly-occurring X1-/X2+ irreps). The dynamical matrix is now block-diagonalized with the same equivalent-irrep clustering ascrystod-phonon --vector, the mode vectors are true eigenvectors, and every run is verified internally against the plain phonopy spectrum. Mode numbers at affected q points differ from v0.3.0 accordingly; the frequencies now agree withcrystod-phonon --irreps(phonon_irreps.yaml).The
--modulationmode table now shows CDML irrep labels (X3-(1),GM5-(2), …) from irreptables — the same labeling ascrystod-phonon --vector/--irreps— instead of internal block indices (the column header changed fromIrrep BlocktoIrrep; labels fall back to-when irreptables labeling is unavailable, e.g. at non-special q points).Irrep and k-point labeling now works at EVERY arm of a special-point star, not only at the single arm tabulated in irreptables/seekpath — across ALL label lookups: the SALC analysis (
crystod -c ... --element/--orbital),--atomic-orbitalhybridization,--visualize,--star-of-k(the header now shows e.g.X [0.0, 0.5, 0.5]instead ofcustom),crystod-mag,crystod-phonon --vibration,--vector(q label in the report and VESTA file names), and--modulation. E.g. all three X arms (1/2,0,0)/(0,1/2,0)/(0,0,1/2) of Pm-3m, and both X arms (0,0,1/2)/(1/2,1/2,0) of the I4/mmm primitive cell, now get X labels; previously only the tabulated arm was labeled (the SALC decomposition fell back to genericirrep_Nnames). For the phonon commands the k point is mapped onto the tabulated arm (star-arm spectra are identical band by band); for the SALC/orbital analyses, which compare little-group characters operation by operation, the characters are transported by the conjugation isomorphismh -> g^-1 h gbetween the little groups, including the Bloch phase from lattice-translation offsets (verified at the X and W stars of nonsymmorphic Fd-3m Si).Fixed: special k points with 1/3-type coordinates (K/H of hexagonal groups, …) were rounded to 0.333333 in the table-derived k-point lists, which silently broke the little-group detection — e.g. the
crystod-magsurvey printed a wrong decomposition with genericirrep_Nlabels at K/H. Table-derived coordinates are now snapped to exact fractions, and k/q-point input across the commands accepts fractions (--qpoint 1/3 1/3 0) while near-fraction decimals are snapped (0.333333 -> 1/3).Fixed:
crystod-magexported the SAME spin structure twice (e.g...._GM5+_dipole_x_conv.vestaand an identical..._x_conv_2.vesta) when a 2-dim irrep space came out of the projection in the circular complex basis (x + iy, x - iy): taking the real part folded both partners onto the same vector. Such spaces are now recombined unitarily into a real orthonormal basis (possible whenever the space is closed under complex conjugation), so the two components export as orthogonal partners (..._dipole_xand..._dipole_y), matching the 2-dim order parameter (found for the in-plane Ni dipoles/quadrupoles of I4/mmm La3Ni2O7).The
--modulationdefault output name is nowMPOSCAR_{q}_{mode}_{irrep}_{subgroup}(e.g.MPOSCAR_R_mode1+2+3_R4+_R-3c; one{q}_{mode}_{irrep}group per term in multi-q runs) instead ofPOSCAR_modulated;--outputstill overrides it.testsuite.pysections are renumbered by command group (1 library core, 2-6crystod, 7-13crystod-group, 14-16crystod-bz, 17-23crystod-phonon, 24-25crystod-mag, 26-27crystod-md; each group closes with that command’s CLI regression section), and everyexample/directory is prefixed with its section number (example/24_phonon_vector<->python testsuite.py 24). New example directories with captured output were added for the previously undocumented features:01_wigner_d(library demo),04_star_of_k,07_direct_product,10_basis_function,11_generate_basis_function,12_show_coset, and19_bz_supercell. Seeexample/READMEfor the full map.
v0.3.0#
CrystOD has been restructured into sectioned commands (phonopy style: one main crystod command plus per-domain crystod-* commands), with phonopy-aligned option names (-c/--cell for the structure file, with --poscar kept as an alias). The old crystod --<mode> flat forms were removed; invoking one prints an error with the equivalent sectioned command.
Mode numbering is now 1-based across ALL commands and outputs:
crystod-phonon --vector --mode,--modulation --mode/--modeN,--vibration --mode-index/--component-index(the component default is now 1),crystod --visualize --mode-index, and every printed mode table / Mode Space / component listing. A0or out-of-range number is rejected with a clear 1-based message.crystod --visualizeHTML redesigned as an interactive viewer modeled after Henrique Miranda’s phonon website (BSD-3-Clause; design imitated, no code copied): sidebar with structure summary, irreducible decomposition, a clickable SALC-basis table replacing the dropdown, lobe-opacity / cell / atom display controls, and a credit link in the page. New--conventionaldisplays the SALC in the conventional cell (primitive-to-conventional matrix from the detected centring, as incrystod-phonon --vector). New--bond EL1 EL2 MAX(repeatable) draws bonds within the cutoff and VESTA-style translucent coordination polyhedra around the EL1 atoms, with boundary atoms completed so polyhedra at the cell edge are not cut off.crystod --visualizenow always writes the interactive 3D HTML, auto-namedSALC_{element}_{orbital}_{kpoint}.htmlwhen--outputis omitted (previously nothing was written without--output).crystod-phonon --vectorusability update: mode numbering is now 1-based, matching the printed table (--mode 0is rejected with a clear message); the q-point mode table (space group, q-points, frequencies, irreps) is automatically saved asphonon_modes_<formula>_<qlabel>.txt; and omitting--modenow exports ALL modes as individual VESTA files (--modestill selects specific modes, summed into one file). VESTA file names now carry the irrep label and zero-padded mode numbers (POSCAR_<formula>_<qlabel>_mode<NN>_<irrep>.vesta) so thatlslists them in order.Redesigned the main
crystodcommand: the flagship SALC analysis runs without a mode flag (crystod -c POSCAR --element Ti --orbital d; hybridization via--atomic-orbital),--visualizemerges the former--visualize-basis, and--star-of-kstays as the symmetry-information option.--kpointnow also accepts high-symmetry labels (GM/X/M/R) in--star-of-k/--visualizemode — the documented--kpoint Mform previously failed to parse. Added--version;crystod --helplists all sectioned commands.Added
crystod-bz(first sectioned command), merging--bzand--bz-supercell: with the default identity--trans-matit plots the unit-cell BZ with an automatic (seekpath) or manual (--band/--band-labels) k-path; with any non-identity--trans-matit plots the unit-cell BZ together with the folded BZ of the transformed (super)lattice.Added
crystod-md(MD section) with two modes:--adp(replacing--xdatcar2adp) writes atomic displacement parameters as a CIF;--summary(based onscript/md_summary.py) reports time-averaged lattice parameters and cell volume with standard deviations over a step range (--start-step/--end-step).--dimnow also accepts a nine-value diagonal matrix (quoted or unquoted);--formatselects the trajectory format (vasp; LAMMPS planned). A missing trajectory file is reported cleanly instead of a traceback.Added
crystod-phonon(phonon section), merging the six flat phonon modes as--irreps(old--phonon-irrep, renamed to match phonopy),--fatband,--lt,--vector,--modulation, and--vibration(force-data-free).--dimaccepts three values or a nine-value diagonal matrix, quoted or unquoted;--band-labelsis the primary spelling (--labelkept as alias); numbered--qpoint1/--mode1/...arguments still work in--modulation.Added
crystod-group(representation-theory section) for point AND space groups, merging six flat modes:--product(old--direct-product),--table(character-table display, formerly--direct-product --show-irrep-tablewithout irreps),--decompose(old--decompose-irrep),--ligand-field ORBITAL(old--ligand-field-split --orbital),--basis(old--basis-function),--generate-basis(old--generate-basis-function), and--coset(old--show-coset).--pg/--sgare provided as short aliases of--point-group/--space-group; labels starting with a minus sign (-43m,-1, …) are handled.crystod-mag --conventionalexports the symmetry-adapted spin structures in the conventional cell (primitive-to-conventional matrix from the detected centring;_conv-suffixed VESTA files).Added
crystod-mag(magnetism section), replacing--spin-basis: symmetry-adapted spin bases as the default action, with per-atom spin directions and the noncollinear magnetization input printed by default (no--show-spin-directionneeded). New--format vasp|qeselects the printed input: a VASPMAGMOMline, or the Quantum ESPRESSO&SYSTEMcounterpart (noncolin, per-typestarting_magnetization/angle1/angle2, with the magnetic element split into one type per spin direction).Removed the old flat forms of all sectioned modes —
--salc,--visualize-basis,--bz,--bz-supercell,--xdatcar2adp,--spin-basis,--phonon-irrep,--phonon-fatband,--phonon-lt,--phonon-vector,--modulation,--vibration,--direct-product,--decompose-irrep,--ligand-field-split,--basis-function,--generate-basis-function, and--show-coset. Invoking one prints an error naming the sectioned replacement.
v0.2.3#
Added
--phonon-vector: phonon-eigenvector visualization as VESTA files with displacement arrows, from the same POSCAR + FORCE_SETS/FORCE_CONSTANTS inputs as--phonon-irrep. Modes are listed with frequencies and irrep labels; the commensurate supercell and Bloch phase factors are applied automatically for fractional q-points. Degenerate modes are exported as symmetry-adapted eigenvectors (aligned along symmetry-dictated directions, as in--modulation) rather than arbitrary eigensolver combinations.--conventionalswitches the output from the primitive to the conventional cell.Added
--decompose-irrep: interactive decomposition of a reducible representation into point-group irreps from user-entered characters (based onscript/decomose_to_irreps.pyby Hiroki Koiso), with a non-interactive--charactersoption.Added
--ligand-field-split: ligand-field splitting of an atomic orbital (s-i) into point-group irreps (based onscript/ligand_field_spliting.pyby Hiroki Koiso).Added
--xdatcar2adp: time-averaged structure and symmetry-constrained ADPs (U_ij) from an MD XDATCAR trajectory as a CIF (based onscript/xdatcar_to_adp.pyby Ko Sato), with a fast built-in XDATCAR parser instead of pymatgen.Added
--phonon-fatband: element-projected phonon fatbands (PDF per element) along an automatic seekpath k-path, computed directly from POSCAR + FORCE_SETS/FORCE_CONSTANTS via the phonopy API. Colored with VESTA default element colors;--nacapplies the non-analytical term correction (LO/TO splitting) from aBORNfile.Added
--bz-supercell: interactive 3D plot of the unit-cell BZ together with the BZ of a transformed (super)lattice, tiled at the |det T| folded supercell reciprocal-lattice points (based onscript/supercell_BZ.pyby Hiroki Koiso).Added
--phonon-lt: phonon bands colored by longitudinal/transverse character with an optional--naccorrection (based onscript/LT_phonon_band.py, after Qijing Zheng), using the exact eigenvector projection onto the propagation direction so that diagonal path segments are also colored correctly.Added
--spin-basis: symmetry-adapted spin bases (cluster multipoles / SAMM, after M.-T. Suzuki et al.) with FM/AFM separation, multipole-rank naming, per-atom spin directions with a VASP MAGMOM line (--show-spin-direction), VESTA export, and automatic magnetic supercells for q != 0. Omitting--qpointlists the spin-multipole irreps at all special k points (as in thecrystodSALC survey).--basis-functionnow supports the axial-vector componentsRx,Ry,Rz(transforming with det(R) R, like magnetic moments), including mixed polar-axial products such as the toroidalx*Ry - y*Rx. In space-group mode, omitting--kpointnow analyzes all special k points automatically (as in thecrystodSALC survey).Fixed point-group labels starting with a minus sign (
-43m,-42m,-6m2,-1, …) being rejected by the argument parser in all--point-group/--subgroupmodes; both--point-group -43mand--point-group="-43m"now work.--dimnow also accepts unquoted values (--dim 4 4 4) in all modes.Fixed
--phonon-irrepwith phonopy >= 2.21, where the removedIrReps.frequenciesproperty caused anAttributeError.
v0.2.2#
Added
--bz: interactive 3D Brillouin-zone plot (HTML) with automatic space-group detection and a seekpath-generated high-symmetry k-path, or a manual path via--band/--label. Based onscript/brillouin_zone_plot.pyby Hiroki Koiso.Added
seekpathandscipyto the package dependencies.
v0.2.1#
Added
--visualize-basis: SALC coefficients and interactive 3D HTML visualization, with the--real-coefficientoption for real-type degenerate irreps.Added
--star-of-kas a standalone command, and automatic star-of-q display in--modulation.Added
--show-coset: left-coset decomposition G = sum g_i H (point groups) and right-coset decomposition G = sum G_k g_i (space groups, one coset per star arm).Added
--generate-basis-function: automatic 1st-3rd order polynomial basis functions classified by irrep.Rewrote the Wigner-D routines in
operations.pywith pure NumPy (no sympy) and fixed several bugs: the parity factor for improper operations (det vs det^l), the beta = pi Euler-angle branch, the p_y row sign in the complex-to-real transform, and a float-determinant perturbation that broke the Euler-angle extraction.Fixed numerical-noise blowup in space-group basis-function coefficients (
--basis-function/--generate-basis-function).Improved compatibility with recent phonopy
PhonopyAtoms(removed remaining ASE-style getter calls).