Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
966ef73
Create pyproject.toml
ZhouXY-PKU Apr 4, 2026
fe8f93f
Add ABACUS Wannier90 interface module
ZhouXY-PKU Apr 4, 2026
c02f916
Create runner.py
ZhouXY-PKU Apr 4, 2026
b43bd78
Create io_utils.py
ZhouXY-PKU Apr 4, 2026
cfc2e55
Add ABACUSWannier90 interface class
ZhouXY-PKU Apr 4, 2026
e80d40b
Create README.md
ZhouXY-PKU Apr 4, 2026
81a7b6e
Create example_basic.py
ZhouXY-PKU Apr 5, 2026
ef9c9ef
Create example_advanced.py
ZhouXY-PKU Apr 5, 2026
f6db979
Add README for ABACUS Wannier90 Interface
ZhouXY-PKU Apr 5, 2026
e5684db
Update example_basic.py
ZhouXY-PKU Apr 6, 2026
0e340b8
Revise example script documentation and parameters
ZhouXY-PKU Apr 6, 2026
2f5b08f
Create example_pw.py
ZhouXY-PKU Apr 6, 2026
261689d
Add example script for LCAO Gamma-only in ABACUS
ZhouXY-PKU Apr 6, 2026
79144d7
Merge branch 'develop' into ZhouXY-PKU-Wannier90
ZhouXY-PKU Apr 17, 2026
81a1955
Update io_utils.py
ZhouXY-PKU Apr 17, 2026
b529bbe
Update pyproject.toml
ZhouXY-PKU Apr 21, 2026
8693080
Update pyproject.toml
ZhouXY-PKU Apr 21, 2026
ddf25d4
Update example_advanced.py
ZhouXY-PKU Apr 21, 2026
24c2712
Update example_basic.py
ZhouXY-PKU Apr 21, 2026
7955048
Update example_pw.py
ZhouXY-PKU Apr 21, 2026
ad59f5e
Update and rename example_lcao_gamma.py to unsupported_lcao_gamma
ZhouXY-PKU Apr 21, 2026
165f11a
Update README.md
ZhouXY-PKU Apr 21, 2026
e528014
Update README.md
ZhouXY-PKU Apr 21, 2026
12679d9
Refactor comments and formatting in interface.py
ZhouXY-PKU Apr 21, 2026
e5cc9ce
Update io_utils.py
ZhouXY-PKU Apr 21, 2026
82bbb31
Update runner.py
ZhouXY-PKU Apr 21, 2026
0731077
Add files via upload
ZhouXY-PKU Apr 21, 2026
3d0c404
Delete interfaces/Wannier90_interface/examples/unsupported_lcao_gamma
ZhouXY-PKU Apr 21, 2026
3f1c387
Rename Bi.orb to Bi.orb
ZhouXY-PKU Apr 22, 2026
bcfffca
Rename Bi.upf to Bi.upf
ZhouXY-PKU Apr 22, 2026
ccad698
Add Se.orb example for Python interface
ZhouXY-PKU Apr 22, 2026
bc47e70
refactor: move dirs.
ZhouXY-PKU Apr 22, 2026
c23c9d0
refactor: move dirs.
ZhouXY-PKU Apr 23, 2026
03212d0
Modify path and add a CI/CD workflow.
ZhouXY-PKU Apr 24, 2026
c0e6acd
Update interface.yml
ZhouXY-PKU Apr 24, 2026
f973178
refactor: move files.
ZhouXY-PKU Apr 24, 2026
61eb1d8
Merge branch 'ZhouXY-PKU-Wannier90' of https://github.com/deepmodelin…
ZhouXY-PKU Apr 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions interfaces/Wannier90_interface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# ABACUS Wannier90 Interface
This package provides a user-friendly Python interface to bridge **ABACUS** (Atomic-scale Simulation Package) with **Wannier90**. It automates the workflow of generating Maximally Localized Wannier Functions (MLWFs) and tight-binding models from ABACUS calculations.
## Features
- **Automated Workflow**: Handles the core coupling pipeline (Steps 3-5 of the standard tutorial workflow).
- **Input Generation**: Automatically generates `wannier90.win`, `INPUT`, `KPT`, and `STRU` files.
- **Method Support**: Supports the recommended `wannier_method = 2` for efficient overlap matrix calculation.
- **Spin-Orbit Coupling**: Full support for SOC calculations (`nspin=4`, `lspinorb=1`).
## Installation
```bash
pip install .
# Or for development
pip install -e .
```
## Workflow Scope
This interface automates the technical coupling steps between ABACUS and Wannier90. In the context of the standard tutorial workflow, it covers the following stages:
| Step | Description | Responsibility |
| :--- | :--- | :--- |
| **Prerequisite** | **Step 1**: ABACUS SCF Calculation | User provides `scf_dir` |
| **Prerequisite** | **Step 2**: Determine Energy Windows | User provides `dis_win` parameters |
| **Automated** | **Step 3**: Generate `wannier90.win` & Run `-pp` | **Interface Step 1** |
| **Automated** | **Step 4**: ABACUS NSCF (Interface Mode) | **Interface Step 2 & 3** |
| **Automated** | **Step 5**: Wannier90 Minimization | **Interface Step 4** |
| **Post-process** | **Step 6**: WannierTools Analysis | User (Downstream tool) |
## Quick Start
Here is an example of generating Wannier functions for Bi2Se3:
```python
from abacusw90 import ABACUSWannier90
# 1. Initialize
# Assumes 'scf_dir' contains results from Step 1 (CHG, HR files)
job = ABACUSWannier90(work_dir="./Bi2Se3_wannier", scf_dir="./Bi2Se3_scf")
# 2. Define Structure
lattice = [[-2.069, -3.583614, 0.0], [2.069, -3.583614, 0.0], [0.0, 2.389075, 9.546667]]
atoms = [
{"name": "Bi", "pos": [0.399, 0.399, 0.697]},
{"name": "Bi", "pos": [0.601, 0.601, 0.303]},
# ... (other atoms)
]
job.set_structure(lattice, atoms)
# 3. Configure Wannier90
# Parameters usually determined in Step 2 (Band structure analysis)
job.set_wannier_parameters(
num_wann=30,
num_bands=100,
projections=["Bi : pz; px; py", "Se : pz; px; py"],
dis_win_min=3.0,
dis_win_max=18.0,
dis_froz_min=3.0,
dis_froz_max=14.8,
mp_grid=[4, 4, 4],
kpath=[
{"start_label": "G", "start_pos": [0,0,0], "end_label": "Z", "end_pos": [0,0,0.5]}
]
)
# 4. Configure ABACUS
job.set_abacus_parameters(ecutwfc=100, nbands=100, lspinorb=1)
# 5. Run Automation (Covers Tutorial Steps 3, 4, 5)
job.run()
```
## Detailed Workflow Steps
The `run()` method executes the following automated sequence:
1. **Generate Inputs & Preprocess**: Write `wannier90.win` and execute `wannier90 -pp` to generate `.nnkp`.
2. **Prepare ABACUS**: Parse `.nnkp` to generate ABACUS `KPT`, `INPUT`, and `STRU` files. Copy SCF charge densities.
3. **Run ABACUS Interface**: Execute ABACUS in NSCF mode with `towannier90=1`. This generates `mmn`, `amn`, `eig` files.
4. **Run Wannier90**: Execute `wannier90.x` to compute MLWFs and output `wannier90_hr.dat`.
## Requirements
- **ABACUS**: v3.0 or higher (with Wannier90 interface support).
- **Wannier90**: v3.0 or higher.
- **Python**: 3.8+
```

71 changes: 71 additions & 0 deletions interfaces/Wannier90_interface/abacusw90/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# ABACUS Wannier90 Interface
This package provides a user-friendly Python interface to bridge **ABACUS** (Atomic-scale Simulation Package) with **Wannier90**. It automates the workflow of generating Maximally Localized Wannier Functions (MLWFs) and tight-binding models from ABACUS calculations.
## Features
- **Automated Workflow**: Handles the core coupling pipeline (Steps 3-5 of the standard tutorial workflow).
- **Input Generation**: Automatically generates `wannier90.win`, `INPUT`, `KPT`, and `STRU` files.
- **Method Support**: Supports the recommended `wannier_method = 2` for efficient overlap matrix calculation.
- **Spin-Orbit Coupling**: Full support for SOC calculations (`nspin=4`, `lspinorb=1`).
## Installation
```bash
pip install .
# Or for development
pip install -e .
```
## Workflow Scope
This interface automates the technical coupling steps between ABACUS and Wannier90. In the context of the standard tutorial workflow, it covers the following stages:
| Step | Description | Responsibility |
| :--- | :--- | :--- |
| **Prerequisite** | **Step 1**: ABACUS SCF Calculation | User provides `scf_dir` |
| **Prerequisite** | **Step 2**: Determine Energy Windows | User provides `dis_win` parameters |
| **Automated** | **Step 3**: Generate `wannier90.win` & Run `-pp` | **Interface Step 1** |
| **Automated** | **Step 4**: ABACUS NSCF (Interface Mode) | **Interface Step 2 & 3** |
| **Automated** | **Step 5**: Wannier90 Minimization | **Interface Step 4** |
| **Post-process** | **Step 6**: WannierTools Analysis | User (Downstream tool) |
## Quick Start
Here is an example of generating Wannier functions for Bi2Se3:
```python
from abacusw90 import ABACUSWannier90
# 1. Initialize
# Assumes 'scf_dir' contains results from Step 1 (CHG, HR files)
job = ABACUSWannier90(work_dir="./Bi2Se3_wannier", scf_dir="./Bi2Se3_scf")
# 2. Define Structure
lattice = [[-2.069, -3.583614, 0.0], [2.069, -3.583614, 0.0], [0.0, 2.389075, 9.546667]]
atoms = [
{"name": "Bi", "pos": [0.399, 0.399, 0.697]},
{"name": "Bi", "pos": [0.601, 0.601, 0.303]},
# ... (other atoms)
]
job.set_structure(lattice, atoms)
# 3. Configure Wannier90
# Parameters usually determined in Step 2 (Band structure analysis)
job.set_wannier_parameters(
num_wann=30,
num_bands=100,
projections=["Bi : pz; px; py", "Se : pz; px; py"],
dis_win_min=3.0,
dis_win_max=18.0,
dis_froz_min=3.0,
dis_froz_max=14.8,
mp_grid=[4, 4, 4],
kpath=[
{"start_label": "G", "start_pos": [0,0,0], "end_label": "Z", "end_pos": [0,0,0.5]}
]
)
# 4. Configure ABACUS
job.set_abacus_parameters(ecutwfc=100, nbands=100, lspinorb=1)
# 5. Run Automation (Covers Tutorial Steps 3, 4, 5)
job.run()
```
## Detailed Workflow Steps
The `run()` method executes the following automated sequence:
1. **Generate Inputs & Preprocess**: Write `wannier90.win` and execute `wannier90 -pp` to generate `.nnkp`.
2. **Prepare ABACUS**: Parse `.nnkp` to generate ABACUS `KPT`, `INPUT`, and `STRU` files. Copy SCF charge densities.
3. **Run ABACUS Interface**: Execute ABACUS in NSCF mode with `towannier90=1`. This generates `mmn`, `amn`, `eig` files.
4. **Run Wannier90**: Execute `wannier90.x` to compute MLWFs and output `wannier90_hr.dat`.
## Requirements
- **ABACUS**: v3.0 or higher (with Wannier90 interface support).
- **Wannier90**: v3.0 or higher.
- **Python**: 3.8+



8 changes: 8 additions & 0 deletions interfaces/Wannier90_interface/abacusw90/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""
ABACUS Wannier90 Interface
"""

from .interface import ABACUSWannier90

__all__ = ["ABACUSWannier90"]
__version__ = "0.1.0"
247 changes: 247 additions & 0 deletions interfaces/Wannier90_interface/abacusw90/interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
"""
Core Interface Class for ABACUS Wannier90 workflow.
"""

import os
import shutil
from pathlib import Path
from typing import List, Dict, Optional
from dataclasses import dataclass, field

from . import io_utils
from . import runner

@dataclass
class ABACUSWannier90:
"""
Main interface class to handle ABACUS-Wannier90 workflow.
"""
# Directory settings
work_dir: str = "./wannier_work"
scf_dir: str = "./scf_out" # Directory containing ABACUS SCF results

# Executables
abacus_exe: str = "abacus"
wannier90_exe: str = "wannier90.x"

# Structure and files
structure: Dict = field(default_factory=dict)
pp_orbitals: Dict = field(default_factory=dict) # {'Bi': 'Bi.upf', ...}
orbital_files: List[str] = field(default_factory=list) # ['Bi.orb', ...]

# Internal objects
_wannier_input: Optional[io_utils.Wannier90Input] = field(default=None, init=False)
_abacus_input: Optional[io_utils.AbacusInput] = field(default=None, init=False)

def __post_init__(self):
self.work_dir = Path(self.work_dir)
self.scf_dir = Path(self.scf_dir)
self.work_dir.mkdir(parents=True, exist_ok=True)

def set_structure(self, lattice: List[List[float]], atoms: List[Dict]):
"""
Set the crystal structure.
atoms format: [{'name': 'Bi', 'pos': [0.0, 0.0, 0.0]}, ...]
"""
self.structure = {
'lattice': lattice,
'atoms': atoms
}

def set_wannier_parameters(
self,
num_wann: int,
num_bands: int,
projections: List[str],
dis_win_min: float,
dis_win_max: float,
dis_froz_min: float,
dis_froz_max: float,
mp_grid: List[int],
kpath: Optional[List[Dict]] = None,
spinors: bool = True,
write_hr: bool = True,
**kwargs
):
"""
Set parameters for Wannier90 calculation.
"""
self._wannier_input = io_utils.Wannier90Input(
num_wann=num_wann,
num_bands=num_bands,
projections=projections,
dis_win_min=dis_win_min,
dis_win_max=dis_win_max,
dis_froz_min=dis_froz_min,
dis_froz_max=dis_froz_max,
mp_grid=mp_grid,
spinors=spinors,
write_hr=write_hr,
kpath=kpath,
**kwargs
)

def set_abacus_parameters(
self,
ecutwfc: float,
nbands: int,
nspin: int = 4,
lspinorb: int = 1,
noncolin: int = 0,
scf_thr: float = 1e-8,
scf_nmax: int = 200,
**kwargs
):
"""
Set ABACUS input parameters for NSCF run.
"""
# Sync nbands with Wannier90 input
if self._wannier_input and nbands != self._wannier_input.params['num_bands']:
print(f"Warning: ABACUS nbands ({nbands}) overriden by Wannier90 num_bands ({self._wannier_input.params['num_bands']})")
nbands = self._wannier_input.params['num_bands']

# Default interface parameters
defaults = {
"calculation": "nscf",
"towannier90": 1,
"wannier_method": 2,
"nnkpfile": "wannier90.nnkp",
"symmetry": -1,
"init_chg": "file",
"scf_nmax": scf_nmax,
"scf_thr": scf_thr,
# User parameters
"ecutwfc": ecutwfc,
"nbands": nbands,
"nspin": nspin,
"lspinorb": lspinorb,
"noncolin": noncolin
}

# Update with user kwargs (allows override of defaults)
defaults.update(kwargs)

self._abacus_input = io_utils.AbacusInput(**defaults)

def _prepare_scf_files(self):
"""Prepare necessary files from SCF calculation."""
# Copy charge density files (SPIN1~4_CHG.cube)
for spin in range(4):
src = self.scf_dir / f"SPIN{spin+1}_CHG.cube"
if src.exists():
shutil.copy(src, self.work_dir)

def step1_generate_wannier_win(self):
"""Generate wannier90.win and run -pp to generate nnkp."""
print(">>> Step 1: Generating wannier90.win and preprocessing...")

# Write .win file
win_file = self.work_dir / "wannier90.win"
self._wannier_input.write(win_file, self.structure)

# Run wannier90 -pp
cmd = f"{self.wannier90_exe} -pp wannier90"
runner.run_command(cmd, cwd=self.work_dir)

nnkp_file = self.work_dir / "wannier90.nnkp"
runner.check_file_exists(nnkp_file)

print(">>> wannier90.nnkp generated successfully.")
return nnkp_file

def step2_prepare_abacus_input(self):
"""Prepare ABACUS input files for the interface run."""
print(">>> Step 2: Preparing ABACUS NSCF input for Wannier90...")

# Copy SCF results
self._prepare_scf_files()

# Parse nnkp to get KPOINTS for ABACUS
nnkp_path = self.work_dir / "wannier90.nnkp"
kpoints = io_utils.parse_nnkp(nnkp_path)

# Write KPT file
kpt_file = self.work_dir / "KPT"
with open(kpt_file, 'w') as f:
f.write("K_POINTS\n")
f.write(f"{len(kpoints)}\n")
f.write("Direct\n")
for k in kpoints:
f.write(f"{k[0]:.8f} {k[1]:.8f} {k[2]:.8f} 1.0\n")

# Write INPUT file
input_file = self.work_dir / "INPUT"
self._abacus_input.write(input_file)

# Write STRU file
stru_file = self.work_dir / "STRU"
self._write_stru(stru_file)

# Link/Copy Orbitals and PP
for orb in self.orbital_files:
if os.path.exists(orb):
shutil.copy(orb, self.work_dir)
for pp_name, pp_file in self.pp_orbitals.items():
if os.path.exists(pp_file):
shutil.copy(pp_file, self.work_dir)

def step3_run_abacus(self):
"""Run ABACUS to generate mmn, amn, eig."""
print(">>> Step 3: Running ABACUS to generate overlap matrices...")

cmd = self.abacus_exe
log_file = self.work_dir / "abacus_nscf.log"
runner.run_command(cmd, cwd=self.work_dir, log_file=log_file)

# Check output files
required_files = ["wannier90.mmn", "wannier90.amn", "wannier90.eig"]
for f in required_files:
runner.check_file_exists(self.work_dir / f)

print(">>> ABACUS calculation finished. Matrix elements generated.")

def step4_run_wannier90(self):
"""Run Wannier90 minimization."""
print(">>> Step 4: Running Wannier90 minimization...")

cmd = f"{self.wannier90_exe} wannier90"
log_file = self.work_dir / "wannier90.log"
runner.run_command(cmd, cwd=self.work_dir, log_file=log_file)

print(">>> Wannier90 finished. Check wannier90.wout and wannier90_hr.dat")

def run(self):
"""Execute the full workflow."""
self.step1_generate_wannier_win()
self.step2_prepare_abacus_input()
self.step3_run_abacus()
self.step4_run_wannier90()
print("All steps completed successfully.")

def _write_stru(self, filename):
"""Write ABACUS STRU file."""
with open(filename, 'w') as f:
f.write("ATOMIC_SPECIES\n")
for atom_name, pp_file in self.pp_orbitals.items():
f.write(f"{atom_name} 1.0 {os.path.basename(pp_file)}\n")

f.write("\nLATTICE_VECTORS\n")
for vec in self.structure['lattice']:
f.write(f"{vec[0]:.10f} {vec[1]:.10f} {vec[2]:.10f}\n")

f.write("\nATOMIC_POSITIONS\n")
f.write("Direct\n")

# Group atoms by type
atom_types = {}
for atom in self.structure['atoms']:
name = atom['name']
if name not in atom_types:
atom_types[name] = []
atom_types[name].append(atom['pos'])

for name, positions in atom_types.items():
f.write(f"{name}\n")
f.write("0.0\n") # Magnetic moment dummy
for pos in positions:
f.write(f"{pos[0]:.10f} {pos[1]:.10f} {pos[2]:.10f} 1 1 1\n")
Loading
Loading