Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
86 changes: 13 additions & 73 deletions tight-binding-easy/run_simulation_spectrum.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,9 @@
import json
import yaml
import sys
from pathlib import Path
import pandas as pd
from typing import Final, Any
import numpy as np
import argparse
from datetime import datetime, timezone
from time import perf_counter

from src.utils import load_raw, should_skip_existing
from src.models import PhysicsConfig, TightBindingSystem
from src.git_utils import repo_state
from src.metadata import collect_metadata, save_metadata, load_metadata

PROJECT_ROOT: Final[str] = Path(__file__).resolve().parent
CONFIGS_DIR: Final[str] = PROJECT_ROOT / "configs"
RESULTS_DIR: Final[str] = PROJECT_ROOT / "results"
from src.git_utils import ensure_git_clean
from src.simulation import Simulation

def parse_simulation_args():
p = argparse.ArgumentParser()
Expand All @@ -32,71 +19,24 @@ def parse_simulation_args():

def main() -> None:

started_at = datetime.now(timezone.utc)
t0 = perf_counter()

args = parse_simulation_args()
ensure_git_clean(args.allow_dirty)

# Let the simulation run when either git is clean or --allow_dirty == True
dirty, reason = repo_state()
if dirty is None and not args.allow_dirty:
raise SystemExit(f"Cannot determine git status ({reason}). Use --allow-dirty to proceed.")
if dirty and not args.allow_dirty:
raise SystemExit("Repo has uncommitted changes. Commit/stash or rerun with --allow-dirty.")

RESULTS_DIR.mkdir(parents=True, exist_ok=True)

# Load physical system's config from yaml
raw = load_raw(CONFIGS_DIR / args.config_file)
# Parse yaml data and then create the run directory for that base config
base_config = PhysicsConfig(**raw)
base_id, base_dir = base_config.prepare_base_dir(base=RESULTS_DIR)

tb_system = TightBindingSystem(base_config)
# Modify the geometry:
# ... (ToDo)
geo_id, geo_dir = tb_system.prepare_geometry_dir(base=base_dir)

# Guard on the stored data
meta_path = geo_dir / "metadata.yaml"
existing_meta = load_metadata(meta_path)
stored_commit = existing_meta.get("git_commit")
current_meta = collect_metadata() # includes git_commit/git_describe/git_dirty
current_commit = current_meta.get("git_commit")
run = Simulation(args)
run.guard_commits()

if stored_commit and current_commit and stored_commit != current_commit and not args.force_rerun:
print(f"Existing results are from {stored_commit}; you’re on {current_commit}. "
"Use --force-rerun to overwrite.")
if run.should_skip("spectrum"):
print("Spectrum exits. Skipping.")
return

# Simulations (Closed system)
hamiltonian = tb_system.build_hamiltonian_dense()

# Spectrum
spectrum_path = geo_dir / "spectrum.csv"
if should_skip_existing(spectrum_path, "spectrum") and not args.force_rerun:
return
hamiltonian = run.tb_system.build_hamiltonian_dense()
spectrum, _ = np.linalg.eigh(hamiltonian)
pd.Series(spectrum).to_csv(spectrum_path, index=False)
print(f"Saved: {base_id}/{geo_id}")

# Save exact physical/geometric configs and metadata used
current_meta["run"] = {
"started_at": started_at.isoformat(),
"duration_sec": perf_counter() - t0,
"args": vars(args),
"base_id": base_id,
"geo_id": geo_id,
}
save_metadata(meta_path, current_meta)
(geo_dir / "geometry_config.yaml").write_text(
yaml.safe_dump(tb_system.geometry_state),
encoding="utf-8",
)
(geo_dir / "base_config.yaml").write_text(
yaml.safe_dump(raw),
encoding="utf-8"
)
run.save_series("spectrum", spectrum)
run.save_metadata()
run.save_configs()

pd.Series(spectrum).to_csv(run.artifact_csv_path("spectrum"), index=False)

if __name__ == "__main__":
main()
10 changes: 8 additions & 2 deletions tight-binding-easy/src/git_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,11 @@ def git_describe() -> str | None:
).strip()
except Exception:
return None



def ensure_git_clean(allow_dirty: bool) -> None:
# Let the simulation run when either git is clean or --allow_dirty == True
dirty, reason = repo_state()
if dirty is None and not allow_dirty:
raise SystemExit(f"Cannot determine git status ({reason}). Use --allow-dirty to proceed.")
if dirty and not allow_dirty:
raise SystemExit("Repo has uncommitted changes. Commit/stash or rerun with --allow-dirty.")
6 changes: 6 additions & 0 deletions tight-binding-easy/src/path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from typing import Final
from pathlib import Path

PROJECT_ROOT: Final[str] = Path(__file__).resolve().parent.parent
CONFIGS_DIR: Final[str] = PROJECT_ROOT / "configs"
RESULTS_DIR: Final[str] = PROJECT_ROOT / "results"
83 changes: 83 additions & 0 deletions tight-binding-easy/src/simulation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import argparse
import yaml
import pandas as pd
from pydantic import Field
from pathlib import Path
from dataclasses import dataclass
from datetime import datetime, timezone
from time import perf_counter
from .models import PhysicsConfig, TightBindingSystem
from .utils import load_raw
from .metadata import collect_metadata, save_metadata, load_metadata
from .path import CONFIGS_DIR, RESULTS_DIR

@dataclass
class Simulation:
# Required
args: argparse.Namespace
# Optional
started_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
t0: float = Field(default_factory=perf_counter)
raw: dict = Field(init=False)
base_config: PhysicsConfig = Field(init=False)
base_id: str = Field(init=False)
base_dir: Path = Field(init=False)
tb_system: TightBindingSystem = Field(init=False)
geo_id: str = Field(init=False)
geo_dir: Path = Field(init=False)
meta_path: Path = Field(init=False)
meta: dict = Field(init=False)
existing_meta: dict = Field(init=False)

def __post_init__(self):
RESULTS_DIR.mkdir(parents=True, exist_ok=True)
self.raw = load_raw(CONFIGS_DIR / self.args.config_file)
self.base_config = PhysicsConfig(**self.raw)
self.base_id, self.base_dir = self.base_config.prepare_base_dir(RESULTS_DIR)
self.tb_system = TightBindingSystem(self.base_config)
self.geo_id, self.geo_dir = self.tb_system.prepare_geometry_dir(base=self.base_dir)
self.meta_path = self.geo_dir / "metadata.yaml"
self.meta = collect_metadata()
self.existing_meta = load_metadata(self.meta_path)
self.base_path = self.geo_dir / "base_config.yaml"
self.geo_path = self.geo_dir / "geometry_config.yaml"

def artifact_csv_path(self, name: str) -> Path:
return self.geo_dir / f"{name}.csv"

def save_series(self, name: str, values) -> None:
path = self.artifact_csv_path(name)
pd.Series(values).to_csv(path, index=False)

def should_skip(self, name: str, suffix: str = ".csv") -> tuple[Path, bool]:
path = self.artifact_path(name, suffix)
skip = path.exists() and not self.args.force_rerun
if skip:
print(f"Skipping: {name} already exists at {path}")
return path, skip

# To prevent blindly overruning the preexisting results
def guard_commits(self):
stored = self.existing_meta.get("git_commit")
curr = self.meta.get("git_commit")
if stored and curr and stored != curr and not self.args.force_rerun:
raise SystemExit(
f"Existing results are from {stored}; you’re on {curr}. "
"Use --force-rerun to overwrite."
)

def save_metadata(self):
self.meta["run"] = {
"started_at": self.started_at.isoformat(),
"duration_sec": perf_counter() - self.t0,
"args": vars(self.args),
"allow_dirty": self.args.allow_dirty,
"force_rerun": self.args.force_rerun,
"base_id": self.base_id,
"geo_id": self.geo_id,
}
save_metadata(self.meta_path, self.meta)

def save_configs(self):
(self.base_path).write_text(yaml.safe_dump(self.raw), encoding="utf-8")
(self.geo_path).write_text(yaml.safe_dump(self.tb_system.geometry_state), encoding="utf-8")