PythTB 2.0.0 Release Notes#
PythTB 2.0.0 is a major redesign of the package, focused on long-term maintainability, performance, and a modern API. It introduces:
A modular package architecture
API cleanup and unification
Performance improvements via vectorization with significant speedups for large calculations
New
Lattice,Mesh, andWannierclassesExpanded functionality: maximally localized and disentangled Wannier functions, Quantum geometric tensor, quantum metric, local Chern marker, axion angle, and more
Wannier90 + Quantum ESPRESSO I/O utilities
Type hints and improved error handling
Improved testing and documentation
New readthedocs-based documentation site
This document summarizes the design philosophy, API changes, and migration guidance.
Why PythTB 2.0.0?#
The original package had a single-file (pythtb.py) design that served early use cases well. However, as the codebase expanded, this structure became unwieldy. Version 2.0.0 reorganizes the project into a modular structure with a more intuitive API and consistent naming, following PEP-8 and scientific Python community best practices.
The goals:
Improve readability and maintainability by breaking the code into smaller, focused modules.
Enhance performance through vectorized operations, enabling efficient computations on larger systems.
Provide a cleaner, more consistent API that is easier to learn and use.
Enable community contributions by establishing clear project structure and guidelines.
Establish a stable surface for future development
Major Changes#
Version Support#
What Changed:
Dropped support for Python <3.12 and NumPy <2.0
Rationale:
Allows use of latest syntax and features (match-case, improved typing, etc.).
Many scientific libraries have dropped support for older versions.
Follows SPEC-0: Scientific Python minimum standards.
Modular Package Structure#
Before: everything in a single pythtb.py file.
Now: split into multiple modules under the pythtb/ package:
Module |
Purpose |
|---|---|
|
Tight-binding model class - |
|
Wavefunction array - |
|
k-point and adiabatic parameter mesh construction and related routines |
|
|
|
Lattice geometry and related routines |
|
Hopping terms storage and manipulation |
|
General-purpose helpers |
|
Plotting utilities, not part of public API |
|
Canonical tight-binding model library (graphene, Haldane, SSH, etc.) |
|
Wannier90 file readers |
|
Quantum ESPRESSO file readers |
Rationale:
Separation of concerns -> easier maintenance, testing, and extension
Aligns with Python Packaging Authority best practices.
See also: Real Python: Python Application Layouts
New Core Classes#
Lattice#
Encapsulates lattice geometry, orbital positions, and nearest-neighbour shells. Every model and wavefunction array references the same lattice instance to ensure consistent coordinates. A Lattice can be shared across multiple models and wavefunction arrays. The Lattice class also provides methods for visualizing the lattice in real and reciprocal space.
To create a Lattice:
from pythtb import Lattice
a1 = [1.0, 0.0]
a2 = [0.0, 1.0]
tau_1 = [0.0, 0.0]
tau_2 = [0.5, 0.5]
lat = Lattice([a1, a2], [tau_1, tau_2], periodic_dirs=...) # '...' sets all directions as periodic
lat = Lattice([a1, a2], [tau_1, tau_2]) # not specifying periodic_dirs defaults to a finite system
lat = Lattice([a1, a2], [tau_1, tau_2], periodic_dirs=[0]) # We may set a subset of directions as periodic (as before)
Mesh#
Describes k-space grids, paths, and parameter sweeps. Encodes topology (loops, endpoints, adiabatic cycles) so WFArray can apply appropriate gauge conditions downstream.
To create a Mesh:
from pythtb import Mesh
mesh = Mesh(dim_k=2, axis_types=['k', 'k'])
mesh.build_grid([10, 10], gamma_centered=True) # builds a 10x10 k-point grid centered at Gamma
mesh = Mesh(dim_k=1, axis_types=['k'])
mesh.build_path([[0, 0], [0.5, 0], [0.5, 0.5]], [50, 50]) # builds a k-point path
mesh = Mesh(dim_k=1, dim_param=1, axis_types=['k', 'param']) # k-point + parameter sweep
mesh.build_grid([100, 20]) # builds a grid with 100 k-points and 20 parameter values
# makes the parameter axis a loop (adiabatic cycle) looping the second (index 1)
# component of the mesh vector back to itself along the second axis (index 1)
mesh.loop(1, 1)
# Manual point specification
mesh = Mesh(dim_k=1, axis_types=['k'])
k_pts = np.linspace(0, 1, 100, endpoint=True) # endpoint=True includes the last point
mesh.build_custom(k_pts)
# winds kx around the BZ and makes the path closed
mesh.loop(0, 0, winds_bz=True, closed=True)
Wannier#
Build Wannier gauges directly inside PythTB from a WFArray, perform projections, disentanglement and maximal localization, and analyze spreads and centers.
from pythtb import Wannier
wannier = Wannier(wf_array)
# Define trial functions as list of (orbital index, weight) tuples
tf_list = [[(orb, weight)] for orb, weight in zip(orbital_indices, weights)]
# project onto occupied bands
wannier.project(tf_list, band_idxs=list(range(n_occ)))
# disentanglement procedure
frozen_window = [E_min, E_max] # define frozen window energies
outer_window = [E_min_outer, E_max_outer] # define outer window energies
wannier.disentangle(
n_wfs=2,
frozen_window=frozen_window,
outer_window=outer_window,
max_iter=1000,
tol=1e-6
)
wannier.project(use_tilde=True) # re-project using disentangled states
# maximal localization
wannier.maxloc(
alpha=1/2,
max_iter=1000,
tol=1e-10,
grad_min=1e-10,
verbose=True)
API Cleanup & Naming Consistency#
Renaming Classes#
Renamed classes using PascalCase per PEP 8 conventions
< v2.0 |
v2.0 |
|---|---|
|
|
|
|
|
|
Dropped redundant object base class (i.e., class TBModel(object): is now class TBModel:). All classes are new-style in Python 3.
Rationale:
Follows community conventions.
Improves distinguishability of classes and functions.
Initialization Patterns#
The Lattice and Mesh classes were introduced to encapsulate lattice geometry and k-point/parameter mesh construction, respectively. This separates concerns and improves code organization. These classes can also be used as standalone utilities and shared across multiple models.
< v2.0 |
2.0 |
Purpose |
|---|---|---|
|
|
Lattice geometry and orbital positions are encapsulated in a |
|
|
Decouples wavefunction storage from model definition. |
Rationale:
Separation of concerns -> easier maintenance and testing.
Reusable components for advanced workflows.
Aligns with object-oriented design principles.
Method Renaming & Unification#
Many methods were renamed for clarity and consistency. Some methods were merged to reduce redundancy.
Examples:
< v2.0 |
v2.0 |
Notes |
|---|---|---|
|
|
Unified method for solving Hamiltonian at single/multiple k-points |
|
Renamed to |
Prevents syntax issues and improves clarity |
|
Renamed to |
Brevity and clarity |
Rationale:
Less functions to learn and remember.
Predictable, explicit naming make the library easier to use and less error-prone (Zen of Python, PEP 20).
Vectorization and Performance#
What Changed:
Performance bottlenecks were addressed via NumPy vectorization.
For example, the
tb_model._gen_hamandtb_model._sol_hamfunctions for generating and diagonalizing the Hamiltonian were a major bottleneck. It has been restructured to utilize NumPy vectorization, eliminating explicitforloops over the k-points.
TensorFlow optional acceleration added for large-scale problems. TensorFlow accelerates linear algebra operations on GPUs. This is an optional dependency that can be enabled for users with compatible hardware.
Rationale:
Loops in Python are slow; NumPy operations are orders of magnitude faster (Why NumPy is Fast, NumPy Best Practices, SciPy Lecture Notes: Performance).
Enables calculations over large k-point grids and many bands.
Output shapes now follow the most common convention (k-points as the leading axis).
Type Hints and Error Handling#
What Changed:
Added type annotations throughout codebase (PEP 484 – Type Hints
Replaced generic
raise Errorwith specific exceptions, e.g.,TypeErrororValueError
Rationale:
Type hints enable better linting and IDE support. (Type Checking – Python Docs)
Clear errors help users debug issues without digging into internals (Effective Error Messages
Logging vs. Print Statements#
What Changed:
Replaced print-based messaging with Python’s
loggingmodule.
Rationale:
Log messages can be filtered by severity (DEBUG, INFO, WARNING, ERROR).
Users can control where log messages go (stdout, file, etc.).
Packaging and Distribution#
What Changed:
Transitioned from setup.py to pyproject.toml, added standard files (LICENSE, CHANGELOG, CONTRIBUTING).
LICENSE: GPL-3 license (copied over from the .txt file).CHANGELOG: This file for tracking changes between versions.pyproject.toml: Now the recommended way to package Python projects.CONTRIBUTING: Outlines expectations and guidelines for contributors.
Rationale:
pyproject.tomlis the new standard for Python packaging.Tools like Poetry, Flit, and pip rely on standardized metadata.
CONTRIBUTINGandLICENSEare now expected in open source.
Testing and Continuous Integration#
What Changed:
Added a
tests/test_examplesdirectory with pytest-based regression tests for all examplesEach test runs the example scripts and compares outputs to reference data from v1.8.
Added a
tests/test_examples/make_test_example.pyto automatically generate a skeleton for new tests to be made in the future.Added a
tests/report_test_status.pyto print a summary of which tests are passing and the date/time of last pass/fail.See
tests/test_examples/README.mdfor more information.
Rationale:
Ensures backward compatibility and prevents regressions during refactors or feature additions
Encourages contributors to include tests alongside new code
If you have questions or want to propose a change, please open an issue or start a discussion.