sisl 0.20.0 Release Notes
Released on 2026-10-18.
Highlights
Adaptive Brillouin zone integration with
GaussKronrodGrid, which puts k-points where the integrand changes fast (e.g. Green functions with a small broadening)
(PR891)
New features
saturateadds atoms to missing bondssaturate(geometry, [(selector, Saturate("H", bond=1.09)), ...])adds an atom for each missing bond of the selected atoms. The selectors are atom categories, so different environments get different atoms, e.g. hydrogen on carbon edges and fluorine on boron edges. The first rule that selects an atom is used.Saturateplaces the atoms in linear, trigonal planar or tetrahedral directions.(PR202)
Lenient construction of atom categories
AtomZaccepts symbols andAtomobjects, e.g.AtomZ("C").AtomCategoryaccepts the aliasesspecies=/element=(forZ=) andneighbours=, andTrueas a value (AtomCategory(odd=True)). An unknown keyword now lists close matches and all valid keywords. Sub-classes declare their keywords with class arguments, e.g.class AtomCoordination(AtomCategory, key="coordination", aliases=("cn",)).(PR687)
maskmethod for categories, and much faster atom categoriescategory.mask(geometry)returns a boolean array with one value per atom. The atom categories are now vectorized, and the parts of a composite category share one neighbor search. Selecting atoms withAtomNeighborsis more than 100 times faster for a thousand atoms.(PR687)
Added
unpack_trsto expand TRS reduced data to the full Brillouin zonemp_full, data_full = mp.unpack_trs(data, axis=0)returns the grid without time-reversal symmetry and a copy ofdatathat matches its k-points. Useweighted=Truefor data that is multiplied by the k-point weights, andconjugate=Truefor data that is complex conjugated at \(-\mathbf k\) (e.g. eigenstates).(PR886)
Adaptive Brillouin zone integration with
GaussKronrodGridAdaptiveBrillouinZoneis the base class of Brillouin zones that select their k-points from the integrand. Integrate withbz.integrate(func), wherefunc(k)returns a scalar, an array, or a sequence of these (integrated element by element, returned asoplist).bz.apply.averageintegrates parent methods (withwrap), so the self-energiesRealSpaceSEandRealSpaceSIaccept an adaptivebz; the otherapplymodes need fixed k-points and raise.bz.last_bz()returns aBrillouinZonewith the k-points and weights of the last integration.GaussKronrodGridrefines boxes of a Gauss-Kronrod rule where the integrand changes fast, e.g. Green functions with a small broadening.gaussselects the rule: 3, 7 (default) or 10 Gauss nodes, with 7, 15 or 21 k-points along each direction of a box.nkptis the initial number of k-points along each direction (0 for a direction that is not integrated), as forMonkhorstPack. A scalarnkptonly integrates the periodic directions of the parent lattice, and the default is one box along each periodic direction.max_klimits the total number of k-points, or (as a sequence) the k-point density along each direction. Time-reversal symmetry (trs) halves the integrated domain, as forMonkhorstPack.(PR891)
SparseCSRsupports bothint32andint64indicesThe index arrays (
ptr,ncolandcol) always share the same integer data-type. It is chosen automatically from the matrix dimensions and number of elements, so small matrices useint32, while matrices too large forint32useint64. The index arrays are promoted toint64when the sparse pattern grows beyond theint32range. A specific data-type can be requested withSparseCSR(..., idtype=np.int64).Fixes GH902.
(PR957)
Added
block_getto read key-value lines in blocksThe values are converted in the same way as
get. fdf lists, e.g.Label [1 -- 4, 6], are now returned as integer arrays.(PR1024)
Added
indicestoBlochto only calculate a sub-matrix of the unfolded matrixindicesis either an array (M[np.ix_(indices, indices)]) or a tuple(rows, cols)(M[np.ix_(rows, cols)]) of indices in the unfolded matrix. Only the requested elements are calculated and stored, e.g. the edges of a large Bloch expansion.self_energyandself_energywithcoupling=Truenow only calculate the required Green function elements, which greatly reduces the memory requirements for large Bloch expansions (e.g. graphene expanded 100x100 peaks at 0.4 GB instead of more than 25.6 GB). Thegreenmethods gained theindicesargument.(PR1027)
Reworked the Green function calculation
A complete re-write makes it more durable, it was a bit fragile in the way the details were layed out.
Changes and improvements
Category construction errors are no longer hidden
Calling a category with wrong arguments raises the real TypeError. Before,
AtomZ()returnedNone, and a TypeError inside a category’s__init__was reported as “found no matches”.AtomCategory()without keywords raises a TypeError. A string passed toAtomZis now read as a symbol, and an unknown symbol raises a ValueError.(PR687)
Faster element assignment and retrieval in sparse matrices
Setting elements one at a time, e.g.
H[i, j] = value, is roughly 2.5–3 times faster, and retrieving single elements is also faster.(PR957)
Changed internal
scipy.sparse.*_matrixto*_arrayThis is necessary to further the usage of scipy which now encourages the use of
*_array.There are some missing features using the array-like interface, but they should be easy to circumvent until full compatibility is shipped in scipy.
(PR957)
Distributions are now agnostic of the object they distribute
The distribution classes moved to
sisl.mpi, and were renamed:Partitionis nowsisl.mpi.Distribution,BlockCyclicPartitionis nowsisl.mpi.BlockCyclicDistribution. A distribution holds only its communicator and the indices it distributes, so the same distribution can be re-used on any object of the same length. Calling it,dist(obj, ...), forwards toobj.__sisl_distribute__(dist, ...), which lets the object decide what distributing it means. The communicator is a keyword-only argument defaulting tosisl.mpi.get_comm, soBlockCyclicDistribution(n)is enough for basic distributions.
Bugfixes
Category selection on a subset of atoms returned wrong indices
Selecting a category within a subset of atoms (e.g.
geometry._sanitize_atoms(AtomZ(7), [2, 3])) returned the positions in the subset, and not the atom indices.(PR687)
AtomNeighborswith a callableRthat returns a single radiusThe neighbor count failed with a TypeError. It now counts the atoms within the radius, including the atom itself, as documented.
(PR687)
NeighborFinder no longer misses neighbors for large cutoff radii
When the cutoff radius required bins bigger than the unit cell, the binning was done on an auxiliary tiled geometry that was constructed without a lattice.
Geometrythen invented one from the bounding box of the coordinates, so the bins were assigned in a different fractional frame than the one used when querying, and neighbors were silently dropped (or the setup crashed with anIndexError). The supercell index of the tiled lattice was also discarded when mapping the pairs back to the original geometry.Additionally, the minimum bin size for skewed cells is now derived from the distance between the lattice planes instead of the pairwise angles between lattice vectors, which was too small for genuinely triclinic cells, and NeighborFinder.find_close now handles coordinates outside the unit cell.
(PR1016)
Orthogonal overlap matrices no longer emit a
SparseEfficiencyWarningSkof orthogonal matrices (e.g.Sk) created the identity matrix by changing the sparsity structure of an empty matrix.(PR1024)
SelfEnergynow raises an AttributeError for missing attributesPreviously any missing attribute silently returned
None.(PR1024)
Fixed Grid.append, AndersonMixer, iter_shape, size_to_num and Orbital.equal
appendnow copies the data of both grids; before it returned zeros.AndersonMixernow keeps one history step and does Anderson mixing; before it silently did linear mixing. Its results therefore change.iter_shapenow yields all indices as tuples, also for 1D shapes.size_to_numnow converts numbers (in MB) to the requested unit. Orbital.equal and AtomicOrbital.equal now compare a string against the orbital name. tbtavncSileTBtrans.write_tbtav now k-averages variables wherenkptis not the first dimension; before it raised a NameError.Fixed a user pool in BrillouinZone.apply
bz.apply.renew(pool=pool)with a pool object raised a NameError. It now uses the number of CPUs of the pool, and warns only when it cannot find that number.Fixed explicit BandStructure divisions over a discontinuity
A
BandStructurewith a list ofdivisionsand a discontinuity (Noneinpoints) created k-points between the two points of the discontinuity. The division of that segment is now set to 1, as for an integerdivisions.Fixed the circle plane of BrillouinZone.param_circle
param_circlenow creates the circle perpendicular tonormal. Before, the circle was in a wrong plane for normals not along the third Cartesian direction.Fixed the number of points of BrillouinZone.param_circle for a discretization length
param_circlewith a floatN_or_dknow uses the circumference to get the number of points. Before, it used the area of the circle, which gave too few points for radii below \(2\) 1/Ang.Fixed the weights of a BrillouinZone with a single k-point as a flat list
BrillouinZone(parent, [kx, ky, kz])now has one weight. Before, it had 3 weights for the single k-point.Fixed time-reversal symmetry of a displaced MonkhorstPack
A
MonkhorstPackwithtrs=Trueand a displacement different from 0 and 1/2 gave wrong Brillouin zone averages. It now uses time-reversal symmetry only when the k-points are symmetric for \(\mathbf k\to-\mathbf k\).BrillouinZone.apply.array accepts scalar values
bz.apply.arraynow accepts methods (orwrapfunctions) that return a Python scalar. Before, it raised an AttributeError.GaussKronrodGrid.integrate warns for an integrand that is not finite
integratenow warns and stops when the integrand returns NaN or inf. Before, it returned the NaN as a converged value.MonkhorstPack raises a ValueError for directions without k-points
MonkhorstPack(parent, [10, 0, 1])now raises a ValueError. Before, it returned a grid without k-points (and a division by zero warning), unless all directions were 0.
Documentation
Tutorial and reference for atom categories
A new tutorial, “Selecting atoms with categories”, introduces the atom categories step by step, from a single category to geometry changes and saturation. The reference page Indexing atoms lists all categories, their keywords and operators, and shows how to write a new category.
(PR687)
Documented adaptive Brillouin zone integration
The Brillouin zone reference has a new section, “Adaptive integration”. It explains when an adaptive grid is useful, why
len(bz),bz.kand thebz.applymodes other thanaverageare not available, and shows how to average a Green function withGaussKronrodGrid.(PR891)
Corrected doc-strings of orbitals, shapes, mixers, NeighborFinder and range parsing
The orbital classes now show the real spherical harmonics that the code uses. hexagonal, goldene, AtomEven and AndersonMixer now report their public module, and hcp is annotated to return a
Geometry.Merged the basic tutorials into notebooks
The old text tutorials on geometries, supercells and Hamiltonians are replaced. A new notebook, “From a geometry to a Hamiltonian”, explains supercells, matrix elements,
find_nscandconstruct. The geometry quickstart now shows how to create a geometry from coordinates. The notebook tutorials have corrected text, and the anomalous Hall conductivity tutorial no longer needs files that another tutorial creates.
Toolboxes
New features
Added
sisl_toolbox.btd.permutefor bandwidth reducing permutation tablesThe Cython routines implement (reverse) Cuthill-McKee, Gibbs-Poole-Stockmeyer, generalized Gibbs-Poole-Stockmeyer and electrode rooted orderings on symmetric graphs, as well as a local refinement that applies to all of them.
reduce_bandwidthtakes aSparseCSRor a scipy sparse matrix/array, folds the supercell columns into the unit cell, symmetrizes the pattern, and returns the permutation tableperm[new] = oldfor the requested method. All routines work withint32andint64indices, and return arrays with the index data-type of the input.sisl_toolbox.btd.btd_partitionpartitions the permuted matrix into the block-tridiagonal matrix of minimal cost (speed,memoryorspeed-diagonalmodels), and returns the block sizes together with the permutation table (sorted within each block).(PR1024)
Changes and improvements
Refactored the electrode self-energies and Green function of
sisl_toolbox.btdPivotSelfEnergyandDownfoldSelfEnergyare replaced byElectrode(abstract base class),BlochElectrodeandDownfoldElectrode.DownfoldElectrodetakes any electrode (e.g. another downfolded electrode), the Hamiltonian and the downfolding orbitals/blocks explicitly, and has no knowledge of the device region.RealSpaceElectrodehandles electrodes with multiple semi-infinite directions, from aRealSpaceSE/RealSpaceSIor a GF file.DeviceGreentakes the electrodes as(electrode, orbitals)pairs and handles all permutations into the device region. Itspermuteargument is aPermuteBTD, a permutation table, or abtd_partitionmethod; a TBtrans output file is no longer accepted.DeviceGreen.from_tbtcreates the Green function from the regions and permutation tables of a TBtrans output file and the electrode Hamiltonians.DeviceGreen.from_fdfnow only reads the fdf file (and the Hamiltonian files it refers to, relative to the fdf file), and calculates the downfolding regions and permutation tables. It no longer acceptsuse_tbt_se. Electrodes can be read from fdf files withElectrode.from_fdf, which returns the electrode class matching the electrode block.TBT.Atoms.Devicedefaults to all atoms not in the electrodes nor in the buffer, and may contain electrode atoms: electrodes fully in the device region are not downfolded, electrodes partially in the device region are partially downfolded (only their atoms outside the device region).DownfoldElectrodeanddownfold_partitionaccept electrodes partially in the last block (device region), the electrode self-energy on those orbitals is added to the downfolded self-energy.DeviceGreenelectrodes may be given as(electrode, orbitals, bulk), wherebulkuses the electrode Hamiltonian in the device region.btd_partitionacceptsdenseblocks (e.g. electrode orbitals), which are guaranteed to span at most 2 neighboring blocks.DeviceGreen.transmissionandDeviceGreen.scattering_matrixaccept**kwargs(e.g.spin).Electrode.from_fdfandDeviceGreen.from_fdfacceptuse_gf(default false) to read the self-energies from theGFfiles of the electrode blocks, which is required for real-space electrodes (e.g.semi-inf-direction ab). Without chemical potentials and bias, TBtrans electrodes are all at 0 (as TBtrans).Electrode.from_fdfreturns aDownfoldElectrodefor electrodes not in the device region (downfold=Falsereturns the electrode itself).downfold_partitionfinds the downfolding regions (and their BTD blocks) of electrodes.btd_partitionanddownfold_partitionare agnostic to atoms and orbitals, they partition the rows of the passed matrix (H.toSparseAtom()for atoms).DeviceGreenelectrode indices andpermute(arrays orPermuteBTD) are atoms, or{"atoms": idx}/{"orbitals": idx}; atomic partitions are expanded to orbitals.DownfoldElectrode(electrode, H, elec, down)uses the same index convention,downis aPermuteBTD(explicit blocks) or the indices of the downfolding region (order and blocks found automatically). Method strings accept the TBtransatom+/orb+prefixes and default to atoms, as doTBT.BTD.Pivot.DeviceandTBT.BTD.Pivot.Elec[s]infrom_fdf.Electrode.orbsholds the device orbitals of an electrode’s self-energy, andDeviceGreenaccepts such electrodes without explicit indices.Fixed spin (non-colinear) expansion of downfolded self-energies, caching of downfolded self-energies and
DeviceGreenquantities with differentspin/dtypearguments, and the spin expansion ofDeviceGreenwhen the Hamiltonian is larger than the device region. Eigenchannels now use thespinof the scattering states. Thedelta-EfandV-fractionelectrode block options were never read from the fdf file, and abulk falseelectrode block option was read as true. Self-energies from GF files were returned as the bulk self-energy (\(E\mathbf S - \mathbf H - \boldsymbol\Sigma\)) instead of \(\boldsymbol\Sigma\). The electrodenameis an optional keyword-only argument, e.g.BlochElectrode(se, bloch, name="Left"). Unnamed electrodes are referenced inDeviceGreenby their index (or the electrode object), names are matched case-insensitively and must be unique.DeviceGreen(..., permute="electrode=<name|index>")starts the BTD partitioning from that electrode (defaults to the first).(PR1024)
Bugfixes
Easier and safer use of
sisl_toolbox.btddirectly from PythonDeviceGreen(..., eta=None)crashed on the first calculation,etanow defaults to the smallest electrodeetadivided by 10, as documented.DeviceGreenandDownfoldElectroderemove the couplings ofHalong the semi-infinite directions of the electrodes (on a copy), previously such couplings silently gave wrong results.DeviceGreenandDownfoldElectrodewarn when an electrode does not seem to be placed correctly: a wrong semi-infinite direction, an electrode not at the edge of the region, or orbitals ordered differently than the electrode.DownfoldElectrodeno longer emitsSparseEfficiencyWarning, and exposes theetaof its electrode.(PR1024)
Documentation
Added a pure-Python quick start for
sisl_toolbox.btdCovers electrodes, semi-infinite directions, electrode placement, Bloch expansions, k-point integration and downfolding, without TBtrans.
(PR1024)