{"id":49117116,"url":"https://github.com/bjmorgan/mchammer-pt","last_synced_at":"2026-05-25T07:01:23.561Z","repository":{"id":352798364,"uuid":"1216715754","full_name":"bjmorgan/mchammer-pt","owner":"bjmorgan","description":"Parallel tempering / replica exchange orchestrator for mchammer Monte Carlo simulations with icet cluster expansions","archived":false,"fork":false,"pushed_at":"2026-05-24T17:29:41.000Z","size":461,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-24T19:23:54.234Z","etag":null,"topics":["icet","monte-carlo-simulation","parallel-tempering"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bjmorgan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-21T07:01:11.000Z","updated_at":"2026-05-22T10:19:16.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/bjmorgan/mchammer-pt","commit_stats":null,"previous_names":["bjmorgan/mchammer-pt"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/bjmorgan/mchammer-pt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bjmorgan%2Fmchammer-pt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bjmorgan%2Fmchammer-pt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bjmorgan%2Fmchammer-pt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bjmorgan%2Fmchammer-pt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bjmorgan","download_url":"https://codeload.github.com/bjmorgan/mchammer-pt/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bjmorgan%2Fmchammer-pt/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33464012,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-25T06:32:55.349Z","status":"ssl_error","status_checked_at":"2026-05-25T06:32:35.322Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["icet","monte-carlo-simulation","parallel-tempering"],"created_at":"2026-04-21T08:06:35.928Z","updated_at":"2026-05-25T07:01:23.555Z","avatar_url":"https://github.com/bjmorgan.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mchammer-pt\n\nReplica-exchange orchestrators for [`mchammer`](https://icet.materialsmodeling.org/)\nMonte Carlo with `icet` cluster expansions: canonical-ensemble\nparallel tempering across a temperature ladder, and replica-exchange\nWang-Landau (REWL) across an energy-window ladder, with optional\nmultiple walkers per window.\n\nFor an architecture overview, see [docs/architecture.md](docs/architecture.md).\n\n## Why\n\n`mchammer`'s canonical ensemble samples at a single temperature. Large\nsupercells with competing ordered basins can trap the chain in local\nminima; a single-temperature chain may never visit the other basin.\nParallel tempering runs `N` replicas at different temperatures and\nperiodically proposes configuration swaps between adjacent replicas,\nso a high-temperature chain can cross barriers and deliver escape\npaths to the colder chains.\n\n`mchammer`'s single-walker Wang-Landau samples a fixed energy window,\nbut on rugged density-of-states landscapes the walker spends long\nstretches near the window edges and the fill-factor schedule stalls\nbefore the histogram is flat. REWL splits the target energy range\ninto overlapping windows and proposes configuration swaps between\nadjacent windows weighted by their within-window density-of-states\nratio, so each walker mixes faster inside its own window and the\ncombined run converges in wall-clock time that scales with window\nwidth rather than total energy range. Each window optionally runs\nmultiple walkers in lockstep that share the flatness gate and merge\ntheir entropy estimates, further reducing the random-walk variance\nthat drives Wang-Landau's per-window convergence cost.\n\n## Features\n\n- `CanonicalParallelTempering` — canonical-ensemble PT with an\n  arbitrary temperature ladder.\n- `WangLandauParallelTempering` — replica-exchange Wang-Landau\n  (REWL) on top of icet's `WangLandauEnsemble`. Each window owns a\n  fixed energy range; adjacent windows attempt configuration swaps\n  with a within-window density-of-states ratio for acceptance.\n  `n_walkers_per_window` (scalar or per-window sequence) runs\n  multiple WL walkers inside the same window, sharing the flatness\n  gate and merging entropies across the group — straightforward to\n  configure here, not exposed by raw icet. To use the\n  Belardinelli-Pereyra 1/t schedule, pass\n  `ensemble_kwargs={'schedule': '1_over_t'}`; the default\n  `schedule='halving'` gives the standard WL fill-factor scheme.\n  Serial and process-parallel backends as for the canonical\n  orchestrator; checkpoint/resume into either pool kind.\n- `mchammer_pt.analysis.dos.stitch_entropy` and\n  `reweight_canonical_from_dos` post-process REWL output: stitch the\n  per-window ln g(E) curves into a single density of states (working\n  in log space, with bin-index matching that survives ULP-level\n  energy drift between windows), then evaluate canonical\n  thermodynamic observables on a user-supplied temperature grid.\n- `mchammer-pt-stitch` and `mchammer-pt-reweight` console scripts\n  expose the same pipeline from the command line, reading either an\n  mchammer-pt checkpoint HDF5 or `WangLandauDataContainer` files\n  directly.\n- Serial and multiprocessing backends, swappable via a single\n  constructor argument.\n- Custom Monte Carlo moves: pass any `mchammer.CanonicalEnsemble`\n  subclass via `ensemble_cls=`, with extra constructor arguments\n  forwarded via `ensemble_kwargs=`. Custom `_do_trial_step` overrides\n  ride the PT machinery without subclassing `Replica`.\n- Per-replica `mchammer.BaseObserver` attachment on both serial and\n  process-parallel pools, with each replica receiving its own\n  observer copy. Three attach paths cover the spectrum: pass an\n  observer instance for the common case (`attach_observer`), a class\n  plus constructor arguments when picklable (`attach_observer_class`),\n  or a top-level factory that constructs the observer inside each\n  worker — required for observers like `ClusterCountObserver` whose\n  constructors take icet `ClusterSpace` objects that do not pickle\n  (`attach_observer_factory`). The factory reloads the\n  `ClusterExpansion` from disk via\n  `ClusterExpansion.read(replica.cluster_expansion_path)`;\n  `ProcessPool` auto-populates the path on every worker.\n- HDF5 output bundling one `mchammer.BaseDataContainer` per replica plus\n  a compact `ExchangeHistory` of per-pair swap statistics and\n  replica-label trajectories.\n- Round-trip count and integrated-autocorrelation-time diagnostics\n  as pure functions over the run output.\n- `ExchangeCallback` protocol for PT-level events (with `ExchangePrinter`\n  and `SwapRateTracker` built-ins).\n- `CycleCallback` protocol for per-cycle hooks, with `ProgressPrinter`\n  built-in for periodic stderr progress lines on long runs (cycle,\n  percent, elapsed, ETA, swap-acceptance rates).\n- `CheckpointWriter` cycle callback and\n  `CanonicalParallelTempering.resume(...)` for crash-safe long runs\n  and bit-identical continuation across `pt.run()` calls (after\n  `ExchangeHistory.concatenate`). Same payload also written by\n  `pt.save_checkpoint(path)` and via the existing\n  `data_container_file=` constructor kwarg.\n- `mchammer_pt.testing.assert_boltzmann_sampling` — public utility for\n  pinning the empirical stationary distribution of a custom\n  `CanonicalEnsemble` subclass against an analytic Boltzmann fixture.\n  Downstream packages providing custom moves can use this to pin\n  stationarity correctness against the same anchor as mchammer-pt's\n  own test suite.\n\n## Install\n\n    pip install -e .\n\nRequires Python 3.11+. `pyproject.toml` pins `icet` to a patched\nfork (`git+https://gitlab.com/bjmorgan/icet.git@master`) that adds\nthe WL `schedule` parameter and the `_phase` /\n`_window_entry_step` attributes the REWL coordinator reads. Plain\nPyPI `icet` will break at the first MC step — let the install pull\nthe fork automatically rather than installing `icet` separately.\nThe pin will be relaxed once the upstream MR lands.\n\nOptional dev tooling: `pip install -e '.[dev]'` adds `pytest`,\n`mypy`, `ruff`.\n\n## Quickstart\n\n```python\nfrom ase.build import bulk\nfrom icet import ClusterExpansion\nfrom mchammer_pt import CanonicalParallelTempering\n\nce = ClusterExpansion.read(\"my_ce.ce\")\natoms = bulk(\"Cu\", \"fcc\", a=4.0, cubic=True).repeat((4, 4, 4))\n# ... decorate atoms with the correct composition ...\n\npt = CanonicalParallelTempering(\n    cluster_expansion=ce,\n    atoms=atoms,\n    temperatures=[100, 200, 350, 550, 800, 1200, 1800, 2700],\n    block_size=1000,\n    random_seed=0,\n    data_container_file=\"pt.h5\",\n)\n\n# Optional: live progress on stderr for long runs.\nfrom mchammer_pt import ProgressPrinter\npt.attach_cycle_callback(ProgressPrinter(interval=100))\n\npt.run(n_cycles=200)\n\n# Diagnostics.\nfrom mchammer_pt import (\n    round_trip_counts,\n    swap_acceptance_rates,\n    energy_autocorrelation_time,\n)\nprint(\"acceptance:\", swap_acceptance_rates(pt.history))\nprint(\"round-trips:\", round_trip_counts(pt.history.replica_labels_per_cycle))\nfor r in range(len(pt.pool)):\n    tau = energy_autocorrelation_time(pt.history.energies_per_cycle[:, r])\n    print(f\"replica {r}: tau = {tau:.1f} cycles\")\n```\n\nFor multiprocess parallelism, use the `process_pool` classmethod:\n\n```python\nwith CanonicalParallelTempering.process_pool(\n    cluster_expansion=ce,\n    atoms=atoms,\n    temperatures=[200, 400, 800, 1600],\n    block_size=1000,\n    random_seed=0,\n) as pt:\n    pt.run(n_cycles=200)\n```\n\nThe factory handles seed spawning, writing the CE to a managed temp\ndirectory, and constructing a `ProcessPool` at the same ladder as\nthe orchestrator. See `examples/03_parallel_workers.py`.\n\nObserver attachment is supported on both `SerialPool` and `ProcessPool`.\nSee the Features list above for the three attach paths and when to use each.\n\nFor custom Monte Carlo moves, subclass `mchammer.CanonicalEnsemble`\nand pass via `ensemble_cls=`:\n\n```python\nfrom mchammer.ensembles import CanonicalEnsemble\n\nclass MyMove(CanonicalEnsemble):\n    def _do_trial_step(self) -\u003e int:\n        # ... your custom move ...\n        return super()._do_trial_step()\n\nwith CanonicalParallelTempering.process_pool(\n    cluster_expansion=ce,\n    atoms=atoms,\n    temperatures=[200, 400, 800, 1600],\n    block_size=1000,\n    random_seed=0,\n    ensemble_cls=MyMove,\n) as pt:\n    pt.run(n_cycles=200)\n```\n\nSpawn workers re-import the class by fully qualified name, so define\nthe subclass in a `.py` module file rather than a Jupyter cell. See\n`examples/05_custom_ensemble.py` for a complete worked example.\n\n### Wang-Landau parallel tempering\n\nFor Wang-Landau parallel tempering, build per-window starting\nconfigurations whose energies lie inside their assigned windows,\nthen drive `WangLandauParallelTempering.from_bin_count` (or pass\nexplicit `windows=` for non-uniform splits):\n\n```python\nfrom mchammer_pt import WangLandauParallelTempering\n\n# `per_window_atoms` is a list[Atoms], one per window, with each\n# entry's energy in the corresponding window. Generating these\n# is the user's responsibility — typically a short pilot MC run.\npt = WangLandauParallelTempering.from_bin_count(\n    cluster_expansion=ce,\n    atoms=per_window_atoms,\n    n_bins=4,\n    energy_spacing=1.0,\n    minimum_energy=-32.0,\n    maximum_energy=32.0,\n    overlap=4,\n    block_size=len(per_window_atoms[0]) * 1000,\n    random_seed=0,\n)\npt.run(n_cycles=500)\n```\n\n`pt.run(...)` exits early once every replica reports `converged`.\n`WangLandauParallelTempering.process_pool(...)` spawns one OS\nprocess per replica. `save_checkpoint(path)` / `resume(path, ...)`\n/ `resume_process_pool(path, ...)` mirror the canonical surface.\nObservers attach the same way as on the canonical pool (via\n`pt.attach_observer(...)` or, for the class and factory paths,\ndirectly on `pt.pool`); each replica's recorded observable\ntrajectory ends up in its `WangLandauDataContainer`, ready for\nicet's `get_average_observables_wl` against the stitched ln g(E).\n\nStitch the per-window ln g(E) curves into a single density of\nstates, then reweight onto a canonical temperature grid:\n\n```python\nfrom mchammer_pt.analysis.dos import (\n    reweight_canonical_from_dos,\n    stitch_entropy,\n)\n\nper_window = [r.get_entropy() for r in pt.results()]\nstitched, errors = stitch_entropy(per_window, energy_spacing=1.0)\ncanonical = reweight_canonical_from_dos(\n    stitched, temperatures=[100, 200, 400, 800, 1600],\n)\n```\n\nThe same pipeline is available from the command line via the\n`mchammer-pt-stitch` and `mchammer-pt-reweight` console scripts, which\nread either an mchammer-pt checkpoint HDF5 or\n`WangLandauDataContainer` files directly. For production runs on a\nnew system, plan to validate the recovered DOS against ground truth\n(e.g. by brute-force enumeration on a small case, or against an\nanalytic result) before trusting downstream thermodynamic averages.\n\n## Examples\n\n- `examples/01_basic_canonical.py` — self-contained run on a toy Cu/Au CE.\n- `examples/02_custom_callback.py` — writing your own `ExchangeCallback`.\n- `examples/03_parallel_workers.py` — PT with the `ProcessPool`.\n- `examples/04_equilibrium_sampling.py` – discarding the initial burn-in period for equilibrium sampling.\n- `examples/05_custom_ensemble.py` — PT with a custom\n  `CanonicalEnsemble` subclass.\n- `examples/06_progress_monitoring.py` — live progress on stderr for\n  long runs via `ProgressPrinter`.\n- `examples/07_resume.py` — checkpoint and resume a PT run, with\n  bit-identical continuation.\n- `examples/08_rewl.py` — replica-exchange Wang-Landau on a 4x4\n  2D Ising model, with per-window seeding and DOS stitching.\n- `examples/09_dos_postprocessing.py` — stitching REWL output into\n  a single ln g(E) and reweighting onto a canonical temperature\n  grid via `mchammer_pt.analysis.dos`.\n\n## License\n\nMIT.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbjmorgan%2Fmchammer-pt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbjmorgan%2Fmchammer-pt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbjmorgan%2Fmchammer-pt/lists"}