diff --git a/README.md b/README.md index 8ed2f44..e60f92d 100644 --- a/README.md +++ b/README.md @@ -130,17 +130,16 @@ cd python && maturin develop --release && pytest # Python wheel ## Documentation -- **Guide** — the Markdown chapters under [`docs/`](./docs/): install, - getting started, concepts, examples, Packmol parity, architecture, and - extending. +- **Guide** — [`docs/`](./docs/), built with `zensical build`. Chapters: + install, getting started, concepts, examples, Packmol parity, architecture, + and extending. - **Rust API** — `cargo doc --open`, or [docs.rs](https://docs.rs/molcrafts-molpack). The four long-form chapters (getting started, concepts, architecture, extending) are also embedded in the rustdoc as `molpack::getting_started`, `molpack::concepts`, `molpack::architecture`, and `molpack::extending`. -- **Python** — the binding docs under [`docs/python/`](./docs/python/) - (installation, guide, examples, and the Python API reference), published as - the **Python** section of the same Zensical site. +- **Python** — binding docs live under `docs/python/`, published as the + **Python** section of the site. ## Contributing diff --git a/docs/assets/stylesheets/extra.css b/docs/assets/stylesheets/extra.css index 351b40f..c6501f8 100644 --- a/docs/assets/stylesheets/extra.css +++ b/docs/assets/stylesheets/extra.css @@ -1,535 +1,17 @@ -:root { - --molpack-accent: #c2410c; - --molpack-accent-strong: #9a3412; - --molpack-accent-soft: rgba(194, 65, 12, 0.14); - --molpack-secondary: #d97706; - --molpack-secondary-soft: rgba(217, 119, 6, 0.12); - --molpack-warm: #ea580c; - --molpack-panel-bg: color-mix(in srgb, var(--md-default-bg-color) 88%, white); - --molpack-panel-border: color-mix(in srgb, var(--molpack-accent) 24%, transparent); - --molpack-panel-shadow: 0 0.65rem 1.7rem rgba(92, 43, 17, 0.1); -} - -[data-md-color-scheme="molcrafts"], -[data-md-color-scheme="molcrafts-slate"] { - --md-typeset-color: var(--md-default-fg-color); - color: var(--md-typeset-color); -} - -[data-md-color-scheme="molcrafts-slate"] { - --molpack-accent: #fb923c; - --molpack-accent-strong: #fdba74; - --molpack-accent-soft: rgba(251, 146, 60, 0.16); - --molpack-secondary: #f97316; - --molpack-secondary-soft: rgba(249, 115, 22, 0.13); - --molpack-warm: #fdba74; - --molpack-panel-bg: color-mix(in srgb, var(--md-default-bg-color) 88%, #23170f); - --molpack-panel-border: color-mix(in srgb, var(--molpack-accent) 26%, transparent); - --molpack-panel-shadow: 0 0.65rem 1.7rem rgba(0, 0, 0, 0.28); -} - -[data-md-color-scheme="molcrafts-slate"] .md-typeset, -[data-md-color-scheme="molcrafts-slate"] .md-content, -[data-md-color-scheme="molcrafts-slate"] .molcrafts-home-hero, -[data-md-color-scheme="molcrafts-slate"] .molcrafts-home-hero__button { - color: var(--md-default-fg-color); -} - -[data-md-color-scheme="molcrafts-slate"] .md-typeset p, -[data-md-color-scheme="molcrafts-slate"] .md-typeset dd, -[data-md-color-scheme="molcrafts-slate"] .molcrafts-home-hero__description { - color: var(--md-default-fg-color--light); -} - -html[data-molcrafts-product="molpack"] .molcrafts-home-hero__aside::before { - display: none; - content: none; -} - -html[data-molcrafts-product="molpack"] .molcrafts-home-hero__aside { - width: min(100%, 23rem); -} - -html[data-molcrafts-product="molpack"] .molcrafts-home-hero__install { - width: 100%; - min-width: 0; - box-sizing: border-box; -} - -html[data-molcrafts-product="molpack"] .molcrafts-home-hero__install-tabs { - width: 100%; - display: flex; - min-width: 0; -} - -html[data-molcrafts-product="molpack"] .molcrafts-home-hero__install-row { - min-width: 0; -} - -html[data-molcrafts-product="molpack"] .molcrafts-home-hero__install-row code { - min-width: 0; -} - -html { - scroll-behavior: smooth; -} - -html[data-molcrafts-product="molpack"] .molcrafts-home-hero { - min-height: 27rem; - background: - linear-gradient(112deg, rgba(255, 247, 237, 0.98) 0%, rgba(255, 247, 237, 0.9) 44%, rgba(194, 65, 12, 0.13) 100%), - linear-gradient(90deg, rgba(194, 65, 12, 0.12) 0 1px, transparent 1px 100%), - linear-gradient(0deg, rgba(217, 119, 6, 0.09) 0 1px, transparent 1px 100%), - linear-gradient(135deg, var(--molpack-secondary-soft), transparent 46%), - var(--md-default-bg-color); - background-size: auto, 4.2rem 4.2rem, 4.2rem 4.2rem, auto, auto; -} - -html[data-molcrafts-product="molpack"] [data-md-color-scheme="molcrafts-slate"] .molcrafts-home-hero, -html[data-molcrafts-product="molpack"][data-md-color-scheme="molcrafts-slate"] .molcrafts-home-hero { - background: - linear-gradient(112deg, rgba(24, 18, 14, 0.98) 0%, rgba(24, 18, 14, 0.9) 44%, rgba(251, 146, 60, 0.16) 100%), - linear-gradient(90deg, rgba(251, 146, 60, 0.11) 0 1px, transparent 1px 100%), - linear-gradient(0deg, rgba(255, 247, 237, 0.07) 0 1px, transparent 1px 100%), - linear-gradient(135deg, var(--molpack-secondary-soft), transparent 46%), - var(--md-default-bg-color); - background-size: auto, 4.2rem 4.2rem, 4.2rem 4.2rem, auto, auto; -} - -html[data-molcrafts-product="molpack"] .molcrafts-home-hero::before { - background: - linear-gradient(112deg, transparent 0 48%, rgba(194, 65, 12, 0.12) 48% 48.3%, transparent 48.3% 100%), - linear-gradient(112deg, transparent 0 64%, rgba(217, 119, 6, 0.1) 64% 64.25%, transparent 64.25% 100%), - repeating-linear-gradient(112deg, transparent 0 1.4rem, rgba(92, 43, 17, 0.04) 1.4rem 1.45rem, transparent 1.45rem 3.7rem); -} - -html[data-molcrafts-product="molpack"] .molcrafts-home-hero__kicker, -html[data-molcrafts-product="molpack"] .molcrafts-manual-eyebrow { - color: var(--molpack-accent); -} - -html[data-molcrafts-product="molpack"] .molcrafts-home-hero h1 { - max-width: 17rem; - background: linear-gradient(96deg, var(--molpack-accent) 0%, var(--molpack-secondary) 58%, var(--molpack-warm) 100%); - -webkit-background-clip: text; - background-clip: text; - color: transparent; -} - -html[data-molcrafts-product="molpack"] .molcrafts-home-hero__description { - max-width: 39rem; -} - -.md-typeset .molcrafts-manual-home.molpack-home { - max-width: 1160px; - padding-top: 1.45rem; -} - -.md-typeset .molpack-system-section { - grid-template-columns: minmax(0, 1fr); - gap: 1rem; -} - -.md-typeset .molpack-system-section .molcrafts-manual-section__header { - position: static; - max-width: 43rem; -} - -.md-typeset .molpack-system-panel { - overflow: hidden; - margin: 0; - border: 1px solid var(--molpack-panel-border); - border-radius: var(--molcrafts-radius); - background: - linear-gradient(135deg, var(--molpack-panel-bg), color-mix(in srgb, var(--molpack-accent-soft) 56%, var(--md-default-bg-color))), - var(--md-default-bg-color); - box-shadow: var(--molpack-panel-shadow); -} - -.md-typeset .molpack-system-panel__header { - display: grid; - gap: 0.25rem; - padding: 0.85rem 1rem; - border-bottom: 1px solid color-mix(in srgb, var(--md-primary-fg-color) 12%, transparent); -} - -.md-typeset .molpack-system-panel__header span { - color: var(--md-default-fg-color--lighter); - font-size: 0.62rem; - font-weight: 850; - line-height: 1.35; - letter-spacing: 0; - text-transform: uppercase; -} - -.md-typeset .molpack-system-panel__header strong { - color: var(--md-default-fg-color); - font-size: 0.92rem; - line-height: 1.25; -} - -.md-typeset .molpack-system-flow { - position: relative; - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: 0.65rem; - padding: 1.35rem 1rem 1.05rem; -} - -.md-typeset .molpack-system-flow::before { - content: ""; - position: absolute; - top: 2.15rem; - right: 1.25rem; - left: 1.25rem; - height: 0.12rem; - background: linear-gradient(90deg, var(--molpack-accent), var(--molpack-secondary), var(--molpack-warm)); - opacity: 0.62; -} - -.md-typeset .molpack-system-flow > div { - position: relative; - min-width: 0; - min-height: 7.1rem; - padding: 1.25rem 0.75rem 0.75rem; - border: 1px solid color-mix(in srgb, var(--md-primary-fg-color) 13%, transparent); - border-radius: var(--molcrafts-radius); - background: color-mix(in srgb, var(--md-default-bg-color) 84%, transparent); - transition: border-color 180ms ease, box-shadow 180ms ease, transform 180ms ease, background-color 180ms ease; -} - -.md-typeset .molpack-system-flow > div:is(:focus-within, :hover) { - border-color: color-mix(in srgb, var(--molpack-accent) 46%, transparent); - box-shadow: 0 0.5rem 1.4rem color-mix(in srgb, var(--molpack-accent) 22%, transparent); - background: color-mix(in srgb, var(--md-default-bg-color) 94%, var(--molpack-accent-soft)); - transform: translateY(-2px); -} - -.md-typeset .molpack-system-flow > div::before { - content: ""; - position: absolute; - top: -0.32rem; - left: 0.72rem; - width: 0.62rem; - height: 0.62rem; - border: 0.14rem solid var(--molpack-accent); - border-radius: 999px; - background: var(--md-default-bg-color); -} - -.md-typeset .molpack-system-flow > div:nth-child(3)::before { - border-color: var(--molpack-secondary); -} - -.md-typeset .molpack-system-flow span, -.md-typeset .molcrafts-workflow-list.molpack-workflow-list .molcrafts-workflow-list__meta { - font-family: var(--md-code-font); - letter-spacing: 0; -} - -.md-typeset .molpack-system-flow span { - display: block; - color: var(--md-default-fg-color--lighter); - font-size: 0.58rem; - font-weight: 850; - line-height: 1.35; - text-transform: uppercase; -} - -.md-typeset .molpack-system-flow strong { - display: block; - margin-top: 0.38rem; - color: var(--md-default-fg-color); - font-size: 0.72rem; - line-height: 1.35; - overflow-wrap: anywhere; -} - -.md-typeset .molpack-system-flow a { - display: block; - color: inherit; - text-decoration: none; -} - -.md-typeset .molpack-results-section { - grid-template-columns: minmax(0, 1fr); - gap: 1rem; -} - -.md-typeset .molpack-results-section .molcrafts-manual-section__header { - position: static; - max-width: 50rem; -} - -.md-typeset .molpack-result-gallery { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 0.85rem; -} - -.md-typeset .molpack-result-figure { - min-width: 0; - margin: 0; - padding: 0.65rem; - border: 1px solid color-mix(in srgb, var(--md-primary-fg-color) 12%, transparent); - border-radius: var(--molcrafts-radius); - background: color-mix(in srgb, var(--md-default-bg-color) 92%, white); -} - -[data-md-color-scheme="molcrafts-slate"] .md-typeset .molpack-result-figure { - background: color-mix(in srgb, var(--md-default-bg-color) 92%, #23170f); -} - -.md-typeset .molpack-result-figure--wide { - grid-column: 1 / -1; -} - -.md-typeset .molpack-result-figure img { - display: block; - width: 100%; - max-height: 24rem; - object-fit: contain; - border-radius: calc(var(--molcrafts-radius) - 2px); - background: #fff; -} - -.md-typeset .molpack-result-figure--wide img { - max-height: 34rem; -} - -.md-typeset .molpack-result-figure figcaption { - margin: 0.55rem 0 0; - color: var(--md-default-fg-color--light); - font-size: 0.66rem; - line-height: 1.45; -} - +/* + * Product-level docs tweaks only. + * + * Visual system (hero, manual-home, workflow-list, doc-map, feature-matrix, + * grids, light/dark palettes) is owned by molcrafts-zensical-theme. + * Product accent is set in zensical.toml under [project.extra.molcrafts]. + * + * Do not re-skin manual components here — that fights the theme. + */ + +/* Keep figure embeds readable when parity / extending pages show charts. */ .md-typeset img.molpack-figure-small { display: block; width: min(100%, 34rem); margin-right: auto; margin-left: auto; } - -.md-typeset .molcrafts-workflow-list.molpack-workflow-list > article { - grid-template-columns: 7.5rem minmax(0, 1fr); - padding: 1.05rem 0.9rem; - border-radius: var(--molcrafts-radius); -} - -.md-typeset .molcrafts-workflow-list.molpack-workflow-list h3 { - font-size: 0.9rem; -} - -.md-typeset .molcrafts-workflow-list.molpack-workflow-list h3 a { - color: inherit; -} - -.md-typeset .molcrafts-workflow-list.molpack-workflow-list p { - max-width: 44rem; - font-size: 0.7rem; -} - -.md-typeset .molcrafts-workflow-list.molpack-workflow-list pre > code { - border-left-color: var(--molpack-accent); - font-size: 0.61rem; -} - -.md-typeset .molpack-doc-map h3 a { - color: inherit; - text-decoration: none; -} - -.md-typeset .molpack-doc-map { - grid-template-columns: repeat(5, minmax(0, 1fr)); - gap: 0.6rem; -} - -.md-typeset .molpack-doc-map section { - position: relative; - padding: 0.85rem 0.9rem; - border-bottom: 0; - border-radius: var(--molcrafts-radius); - background: color-mix(in srgb, var(--md-default-fg-color) 4%, transparent); - transition: background-color 160ms ease, transform 160ms ease; -} - -.md-typeset .molpack-doc-map section:is(:focus-within, :hover) { - background: color-mix(in srgb, var(--molpack-accent-soft) 58%, transparent); - transform: translateY(-1px); -} - -.md-typeset .molpack-system-flow a::after, -.md-typeset .molpack-doc-map h3 a::after { - content: ""; - position: absolute; - inset: 0; - z-index: 1; -} - -@media screen and (max-width: 76.2344em) { - .md-typeset .molpack-system-flow { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - - .md-typeset .molpack-system-flow::before { - display: none; - } - - .md-typeset .molpack-doc-map { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .md-typeset .molpack-result-gallery { - grid-template-columns: minmax(0, 1fr); - } -} - -@media screen and (max-width: 60em) { - html[data-molcrafts-product="molpack"] .molcrafts-home-hero { - min-height: 24rem; - } - - .md-typeset .molcrafts-manual-home.molpack-home { - padding-top: 1.2rem; - } - - .md-typeset .molpack-system-flow { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .md-typeset .molcrafts-workflow-list.molpack-workflow-list > article { - grid-template-columns: minmax(0, 1fr); - } -} - -@media screen and (max-width: 36em) { - html[data-molcrafts-product="molpack"] .molcrafts-home-hero h1 { - max-width: none; - } - - .md-typeset .molpack-system-panel__header, - .md-typeset .molpack-system-flow { - padding-right: 0.75rem; - padding-left: 0.75rem; - } - - .md-typeset .molpack-system-flow, - .md-typeset .molpack-doc-map, - .md-typeset .molpack-result-gallery { - grid-template-columns: minmax(0, 1fr); - } - - .md-typeset .molpack-system-flow > div { - min-height: 5.8rem; - } - - .md-typeset .molpack-result-figure { - padding: 0.45rem; - } - - .md-typeset .molpack-result-figure img, - .md-typeset .molpack-result-figure--wide img { - max-height: none; - } -} - -/* ── content pages (install, quickstart, guides) ─────────────────────── */ - -.md-typeset .molpack-next-cards { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); - gap: 0.65rem; - margin: 1rem 0 0.25rem; -} - -.md-typeset .molpack-next-cards > ul { - display: contents; - list-style: none; - margin: 0; - padding: 0; -} - -.md-typeset .molpack-next-cards li { - margin: 0; - padding: 0.85rem 0.95rem; - border: 1px solid color-mix(in srgb, var(--molpack-accent) 18%, transparent); - border-radius: var(--molcrafts-radius, 0.45rem); - background: - linear-gradient(145deg, color-mix(in srgb, var(--molpack-accent-soft) 40%, transparent), transparent 70%), - color-mix(in srgb, var(--md-default-bg-color) 94%, var(--molpack-accent-soft)); - box-shadow: 0 0.35rem 1rem color-mix(in srgb, var(--molpack-accent) 8%, transparent); - transition: border-color 160ms ease, transform 160ms ease, box-shadow 160ms ease; -} - -.md-typeset .molpack-next-cards li:is(:hover, :focus-within) { - border-color: color-mix(in srgb, var(--molpack-accent) 48%, transparent); - box-shadow: 0 0.5rem 1.35rem color-mix(in srgb, var(--molpack-accent) 16%, transparent); - transform: translateY(-1px); -} - -.md-typeset .molpack-next-cards li::marker { - content: ""; -} - -.md-typeset .molpack-next-cards a { - color: var(--md-default-fg-color); - font-weight: 650; - text-decoration: none; -} - -.md-typeset .molpack-next-cards strong a, -.md-typeset .molpack-next-cards a strong { - color: var(--molpack-accent-strong, var(--molpack-accent)); -} - -.md-typeset table:not([class]) { - border-radius: var(--molcrafts-radius, 0.45rem); - overflow: hidden; - box-shadow: 0 0 0 1px color-mix(in srgb, var(--molpack-accent) 12%, transparent); -} - -.md-typeset table:not([class]) th { - background: color-mix(in srgb, var(--molpack-accent-soft) 55%, var(--md-default-bg-color)); - color: var(--md-default-fg-color); - font-weight: 700; -} - -.md-typeset .admonition, -.md-typeset details { - border-radius: var(--molcrafts-radius, 0.45rem); - border-color: color-mix(in srgb, var(--molpack-accent) 28%, transparent); -} - -.md-typeset .admonition.tip, -.md-typeset details.tip { - border-color: color-mix(in srgb, var(--molpack-secondary) 40%, transparent); -} - -.md-typeset .tabbed-set { - border-radius: var(--molcrafts-radius, 0.45rem); - overflow: hidden; -} - -/* Inline figures in tutorial / parity pages */ -.md-typeset figure.molpack-inline-figure { - margin: 1.1rem 0; - padding: 0.75rem; - border: 1px solid color-mix(in srgb, var(--md-primary-fg-color) 12%, transparent); - border-radius: var(--molcrafts-radius, 0.45rem); - background: color-mix(in srgb, var(--md-default-bg-color) 92%, white); -} - -.md-typeset figure.molpack-inline-figure img { - display: block; - width: 100%; - border-radius: calc(var(--molcrafts-radius, 0.45rem) - 2px); -} - -.md-typeset figure.molpack-inline-figure figcaption { - margin-top: 0.5rem; - color: var(--md-default-fg-color--light); - font-size: 0.68rem; - line-height: 1.45; - text-align: center; -} diff --git a/docs/getting_started.md b/docs/getting_started.md index c22a0e3..0f06c91 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -96,12 +96,25 @@ molrs.write_pdb(packed, "water_box.pdb") ## Where next -
- -- **[Concepts](concepts/)** — targets, restraints, phases of the packer -- **[CLI](cli/)** — same job as a Packmol-style `.inp` -- **[Python guide](python/)** — fixed solutes, PBC, collective restraints -- **[Rust API](rust/)** — embed the engine in a native crate -- **[Packmol parity](packmol_parity/)** — what matches Packmol, and what does not - +
+ + Concepts + Targets, restraints, and packer phases. + + + CLI + Same job as a Packmol-style `.inp`. + + + Python + Fixed solutes, PBC, collective restraints. + + + Rust + Embed the engine in a native crate. + + + Packmol parity + What matches Packmol, and what does not. +
diff --git a/docs/index.md b/docs/index.md index 6415309..0b1ac62 100644 --- a/docs/index.md +++ b/docs/index.md @@ -44,109 +44,79 @@ hero:

molpack

-
+
-
+
-Core model +Start here -## Templates, counts, restraints — then one packing run +## Find the right page -molpack is organized around a fixed packing job at every entry point. Provide -molecule templates, copy counts, geometric restraints, and optional fixed -placements or periodic boundaries. The engine returns one packed configuration -with the requested distances and regions satisfied. +This page is an index into the manual. Every surface lowers to the same model: +molecule templates, copy counts, geometric restraints, then one packing run.
-
-
-One engine · four surfaces -Scripts for reproducible jobs · APIs for pipelines · handlers for observation -
- -
- -The [Quickstart](getting_started/) packs a first water box. Use -[Packmol scripts](cli/) for checked-in input files, [Python](python/) for -notebooks and pipelines, and [Rust](rust/) when packing is part of a native -application. +
-
+
-Results - -## What the engine produces +Workflows -Canonical Packmol-style workloads — confined solutes, interface distributions, -and multi-target boxes — pack under the same restraint model through every -surface. - -
- - - -
- -
- -
- -In practice - -## The same packing model through four entry points +## Same packing model, four entry points All entry points lower to the same target / count / restraint model. Pick the surface that matches how the rest of your workflow is written.
-
+
-
01 · Packmol script
+
Path 01 · Script
-### [Run a `.inp` job](cli/) +### Run a `.inp` job Use the CLI for reproducible packing jobs that already live as Packmol-style -input files. +input files. See the [CLI guide](cli/). ```text structure water.pdb @@ -159,12 +129,12 @@ end structure
-
02 · Python API
+
Path 02 · Python
-### [Pack from a notebook or pipeline](python/) +### Pack from a notebook or pipeline Load or build a frame, create immutable targets, and pass the packed frame to -the writer or analysis code you already use. +the writer or analysis code you already use. See the [Python API](python/). ```python water = Target(frame, 100).with_restraint( @@ -177,12 +147,12 @@ packed = Molpack().with_seed(42).pack([water])
-
03 · Rust API
+
Path 03 · Rust
-### [Embed the engine in a crate](rust/) +### Embed the engine in a crate -Use the native builder API for Rust applications, services, tests, and new -engine features. +Use the native builder API for applications, services, and tests. See the +[Rust API](rust/). ```rust let frame = Molpack::new() @@ -194,12 +164,12 @@ let frame = Molpack::new()
-
04 · Custom handler
+
Path 04 · Handler
-### [Observe or stop a run](rust/handlers-relaxers/) +### Observe or stop a run -Handlers receive structured events from the packing loop, so you can log -diagnostics, dump frames, or request early stop without changing the packer. +Handlers receive structured events from the packing loop. See +[Handlers and relaxers](rust/handlers-relaxers/). ```rust impl Handler for WatchFdist { @@ -215,41 +185,107 @@ impl Handler for WatchFdist {
-
+
-Find your page +Features -## The manual +## Core capabilities -Organized by entry point. **Tutorial** teaches the common packing model. -**Packmol script**, **Python**, and **Rust** cover the public surfaces. -**Handlers** cover observation and early-stop hooks. +Dense summary of what the engine and each surface cover.
-
-
-

Install

-

CLI binary, crates.io crate, and PyPI wheel — pick a surface and verify it loads.

-
-
-

Quickstart

-

Pack 100 waters in a 40 Å cube end-to-end, then read convergence diagnostics.

-
-
-

Packmol Script

-

`.inp` compatibility, formats, path resolution, and CLI examples.

-
-
-

Python API

-

Targets, restraints, packer options, PBC, examples, and API reference.

+
+
+
Targets
+
Molecule templates, copy counts, fixed placements, centering, and rotation bounds.
+
+
+
Restraints
+
Geometric boxes and spheres, planes, and collective distribution-matching terms.
+
+
+
Packer
+
GENCAN outer loop, pair-distance objective, optional rayon evaluation, seed control.
+
+
+
Scripts
+
Packmol-compatible `.inp` keywords, path resolution, and extra filetypes (SDF, LAMMPS).
+
+
+
Python
+
Frame-in / frame-out packing for notebooks and pipelines via molrs.
+
+
+
Rust
+
Native builders, handlers, relaxers, and feature-gated IO / FF / rayon.
+
+
+
-
-

Rust API

-

Builders, restraint scopes, periodic boxes, handlers, relaxers, examples.

+ +
+ +
+ +Map + +## Documentation map + +A compact mirror of the navigation tree for returning users. + +
+ +
+
+

Tutorial

+

Install, quickstart, concepts, examples, and Packmol parity notes.

+
+
+

Packmol Script

+

`.inp` overview, script notes, formats, and CLI examples.

+
+
+

Python

+

Installation, targets, restraints, packer options, PBC, and API reference.

+
+
+

Rust

+

Builders, restraints, PBC, handlers, relaxers, and example programs.

+
+
+

Development

+

Contributing, architecture, and extending the engine.

+
+
+
+ +
+ +
+ +Surfaces + +## Jump to a surface + +
+ +
diff --git a/docs/python/index.md b/docs/python/index.md index f910899..8c671ce 100644 --- a/docs/python/index.md +++ b/docs/python/index.md @@ -28,17 +28,39 @@ print(f"packed {frame['atoms'].nrows} atoms") ## Next steps -
- -- **[Installation](installation.md)** — pip install and verification -- **[Quickstart](getting-started.md)** — first pack end-to-end -- **[Targets](guide/targets.md)** — templates, counts, fixed placement -- **[Restraints](guide/restraints.md)** — geometric and collective restraints -- **[Packer](guide/packer.md)** — builder options, diagnostics, handlers -- **[Periodic boundaries](guide/periodic-boundaries.md)** — PBC rules -- **[Examples](examples.md)** — Packmol-equivalent workloads -- **[API reference](api-reference.md)** — class-by-class summary - + ## See also diff --git a/docs/zh/index.md b/docs/zh/index.md deleted file mode 100644 index 300c0bc..0000000 --- a/docs/zh/index.md +++ /dev/null @@ -1,249 +0,0 @@ ---- -title: molpack(中文) -description: 纯 Rust 实现的 Packmol 级分子装填,提供命令行、Python API 和 Rust API。 -hide: - - navigation - - toc -hero: - kicker: molpack 手册 - title: molpack - description: 纯 Rust 实现的 Packmol 兼容分子装填引擎 — `.inp` 脚本、Python 包与原生 Rust crate 共用同一套装填模型。 - install: - label: 安装 - methods: - - label: CLI - command: cargo install molcrafts-molpack --features cli - - label: Rust 库 - command: cargo add molcrafts-molpack - - label: Python - command: pip install molcrafts-molpack - badges: - - img: https://img.shields.io/crates/v/molcrafts-molpack?color=c2410c&label=crates.io - href: https://crates.io/crates/molcrafts-molpack - alt: crates.io 版本 - - img: https://img.shields.io/pypi/v/molcrafts-molpack?color=d97706&label=PyPI - href: https://pypi.org/project/molcrafts-molpack/ - alt: PyPI 版本 - - img: https://github.com/MolCrafts/molpack/actions/workflows/ci.yml/badge.svg - href: https://github.com/MolCrafts/molpack/actions/workflows/ci.yml - alt: CI 状态 - - img: https://img.shields.io/badge/license-BSD--3--Clause-blue.svg - href: https://github.com/MolCrafts/molpack/blob/master/LICENSE - alt: BSD-3-Clause 许可证 - actions: - - label: 快速开始 - href: ../getting_started/ - style: primary - - label: Packmol 脚本 - href: ../cli/ - - label: Python API - href: ../python/ - - label: Rust API - href: ../rust/ ---- - -

molpack

- -
- -
- -
- -核心模型 - -## 分子模板、数量、约束 — 然后运行一次装填 - -molpack 在每个入口下都围绕同一个固定装填任务:给出分子模板、副本数量、几何约束, -以及可选的固定放置或周期边界;引擎返回一个满足距离和区域要求的装填构型。 - -
- -
-
-一个引擎 · 四个入口 -脚本用于可复现任务 · API 用于流水线 · handler 用于观测 -
- -
- -[快速开始](../getting_started/) 会装填第一个水盒子。有 `.inp` 时用 -[Packmol 脚本](../cli/),在 notebook 与流水线里用 [Python](../python/),嵌入 -原生应用时用 [Rust](../rust/)。 - -
- -
- -
- -结果示意 - -## 引擎能装出什么 - -受限溶质、界面分布、多组分盒子 — 同一套约束模型贯穿所有入口。 - -
- - - -
- -
- -
- -实际使用 - -## 同一个装填模型,四个入口 - -所有入口都会落到同一个 target / count / restraint 模型。按工作流语言选择入口。 - -
- -
- -
- -
01 · Packmol 脚本
- -### [运行 `.inp` 任务](../cli/) - -CLI 适合已经写成 Packmol 风格输入、需要纳入版本控制的可复现任务。 - -```text -structure water.pdb - number 1000 - inside box 0. 0. 0. 40. 40. 40. -end structure -``` - -
- -
- -
02 · Python API
- -### [在 notebook 或流水线中装填](../python/) - -加载或构造 frame,创建不可变 target,再把结果交给已有的写出或分析代码。 - -```python -water = Target(frame, 100).with_restraint( - InsideBoxRestraint([0, 0, 0], [40, 40, 40]) -) -packed = Molpack().with_seed(42).pack([water]) -``` - -
- -
- -
03 · Rust API
- -### [把引擎嵌入 crate](../rust/) - -适合原生应用、服务、测试,以及引擎本身的新功能。 - -```rust -let frame = Molpack::new() - .with_seed(42) - .pack(&[water], 200)?; -``` - -
- -
- -
04 · 自定义 handler
- -### [观测或停止一次运行](../rust/handlers-relaxers/) - -Handler 接收装填循环中的结构化事件,便于记录诊断、导出中间 frame 或请求提前停止。 - -```rust -impl Handler for WatchFdist { - fn on_step(&mut self, info: &StepInfo, _sys: &PackContext) { - eprintln!("fdist={}", info.fdist); - } -} -``` - -
- -
- -
- -
- -
- -页面索引 - -## 手册 - -**Tutorial** 讲通用装填模型;**Packmol 脚本**、**Python**、**Rust** 覆盖公开界面; -**Handler** 讲观测与提前停止。 - -!!! note "中文内容" - 中文首页为导航入口;详细章节目前以英文正文为主。语言切换在页眉下拉菜单。 - -
- -
-
-

安装

-

CLI、crates.io 与 PyPI — 选一个入口并验证可导入 / 可运行。

-
-
-

快速开始

-

在 40 Å 立方体中装填 100 个水分子,并读取收敛诊断。

-
-
-

Packmol 脚本

-

兼容 `.inp`、文件格式、路径解析与 CLI 示例。

-
-
-

Python API

-

Targets、restraints、packer、PBC、示例与 API 参考。

-
-
-

Rust API

-

Builder、约束作用域、周期边界、handlers、relaxers。

-
-
- -
- -
diff --git a/python/pyproject.toml b/python/pyproject.toml index 1ab70f2..25964d9 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -58,11 +58,8 @@ dev = [ # `uv run --group dev tox -e py` from python/. "tox>=4.23", ] -# Documentation build. Zensical is the static-site generator; it reads the -# single site config at ../zensical.toml (the Python binding docs live under -# ../docs/python/ as the "Python" nav section). That config sets -# `theme.name = "molcrafts"` — the shared MolCrafts brand extension, now on -# PyPI, so the docs build is reproducible from this group alone. +# Documentation build: `zensical build` reads zensical.toml (docs → site). +# Theme: `theme.name = "molcrafts"` (molcrafts-zensical-theme on PyPI). doc = [ "zensical>=0.0.51", # The molcrafts docs theme (zensical.toml sets `theme.name = "molcrafts"`). diff --git a/python/tests/test_examples_smoke.py b/python/tests/test_examples_smoke.py deleted file mode 100644 index 55c9a0a..0000000 --- a/python/tests/test_examples_smoke.py +++ /dev/null @@ -1,84 +0,0 @@ -"""Smoke tests: every example script compiles and its PDB assets parse. - -Full workloads (the actual pack runs) are too large for the unit-test -suite — run ``python examples/pack_.py`` to exercise them. -""" - -from __future__ import annotations - -import ast -import sys -from pathlib import Path - -import pytest - -HERE = Path(__file__).resolve().parent -EXAMPLES = HERE.parent / "examples" -DATA_ROOT = HERE.parent.parent / "examples" - -sys.path.insert(0, str(EXAMPLES)) -from _common import read_pdb_as_arrays # noqa: E402 - -EXAMPLE_SCRIPTS = [ - "pack_water_cube.py", - "pack_mixture.py", - "pack_bilayer.py", - "pack_interface.py", - "pack_spherical.py", - "pack_solvprotein.py", -] - -# Maps every example script to the PDB fixtures it depends on. -EXAMPLE_FIXTURES = { - "pack_mixture.py": ["pack_mixture/water.pdb", "pack_mixture/urea.pdb"], - "pack_bilayer.py": ["pack_bilayer/water.pdb", "pack_bilayer/palmitoil.pdb"], - "pack_interface.py": [ - "pack_interface/water.pdb", - "pack_interface/chloroform.pdb", - "pack_interface/t3.pdb", - ], - "pack_spherical.py": ["pack_spherical/water.pdb", "pack_spherical/palmitoil.pdb"], - "pack_solvprotein.py": [ - "pack_solvprotein/protein.pdb", - "pack_solvprotein/water.pdb", - "pack_solvprotein/sodium.pdb", - "pack_solvprotein/chloride.pdb", - ], -} - - -@pytest.mark.parametrize("script", EXAMPLE_SCRIPTS) -def test_example_script_parses(script): - source = (EXAMPLES / script).read_text(encoding="utf-8") - ast.parse(source) - - -@pytest.mark.parametrize("script", EXAMPLE_SCRIPTS) -def test_example_script_has_main(script): - source = (EXAMPLES / script).read_text(encoding="utf-8") - tree = ast.parse(source) - fns = {n.name for n in ast.walk(tree) if isinstance(n, ast.FunctionDef)} - assert "main" in fns - - -@pytest.mark.parametrize( - "script,fixtures", - sorted(EXAMPLE_FIXTURES.items()), -) -def test_example_fixtures_exist(script, fixtures): - for rel in fixtures: - p = DATA_ROOT / rel - assert p.exists(), f"{script}: missing fixture {p}" - - -@pytest.mark.parametrize( - "script,fixtures", - sorted(EXAMPLE_FIXTURES.items()), -) -def test_example_fixtures_parse(script, fixtures): - for rel in fixtures: - pos, rad, els = read_pdb_as_arrays(DATA_ROOT / rel) - assert pos.shape[0] > 0 - assert pos.shape[1] == 3 - assert rad.shape[0] == pos.shape[0] - assert len(els) == pos.shape[0] diff --git a/tests/cli.rs b/tests/cli.rs index 52bae9d..6e91b50 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -118,102 +118,6 @@ end structure ); } -// ── smoke tests (run actual packing on canonical examples) ────────────────── - -/// Pack the mixture example and verify the output file is created. -#[test] -fn smoke_pack_mixture() { - let dir = example_dir("pack_mixture"); - let out_path = dir.join("_ci_mixture.pdb"); - let _ = std::fs::remove_file(&out_path); - - // Write a temp .inp that redirects output so we don't touch the committed file. - let inp = format!( - "tolerance 2.0\nseed 1234567\nfiletype pdb\noutput {}\n\n\ - structure water.pdb\n number 50\n inside box 0. 0. 0. 20. 20. 20.\nend structure\n", - out_path.display() - ); - - let out = Command::new(bin_path()) - .current_dir(&dir) - .stdin(std::process::Stdio::piped()) - .stdout(std::process::Stdio::null()) - .stderr(std::process::Stdio::null()) - .spawn() - .and_then(|mut child| { - use std::io::Write; - child.stdin.as_mut().unwrap().write_all(inp.as_bytes())?; - child.wait_with_output() - }) - .expect("run molpack"); - - assert!( - out.status.success(), - "molpack exited {:?}\nstdout: {}\nstderr: {}", - out.status, - String::from_utf8_lossy(&out.stdout), - String::from_utf8_lossy(&out.stderr) - ); - assert!( - out_path.exists(), - "output file not created: {}", - out_path.display() - ); - - // Clean up. - let _ = std::fs::remove_file(&out_path); -} - -/// Regression: a script with only a `pbc` directive and no `inside` -/// restraint must pack in O(seconds). Before the parser fix, `pbc` -/// was silently dropped and the packer fell back to a 2000 Å cell -/// grid (~10⁸ cells, 42 GB). With `pbc` wired through, the cell grid -/// is sized from the PBC box and the job finishes immediately. -#[test] -fn smoke_pack_pbc_only_finishes_quickly() { - let dir = example_dir("pack_mixture"); - let out_path = dir.join("_ci_pbc_only.pdb"); - let _ = std::fs::remove_file(&out_path); - - let inp = format!( - "tolerance 2.0\nseed 1234567\nfiletype pdb\noutput {}\n\ - pbc 30.0 30.0 30.0\n\n\ - structure water.pdb\n number 10\nend structure\n", - out_path.display() - ); - - let start = std::time::Instant::now(); - let out = Command::new(bin_path()) - .current_dir(&dir) - .stdin(std::process::Stdio::piped()) - .stdout(std::process::Stdio::null()) - .stderr(std::process::Stdio::piped()) - .spawn() - .and_then(|mut child| { - use std::io::Write; - child.stdin.as_mut().unwrap().write_all(inp.as_bytes())?; - child.wait_with_output() - }) - .expect("run molpack"); - let elapsed = start.elapsed(); - - let _ = std::fs::remove_file(&out_path); - - assert!( - out.status.success(), - "molpack exited {:?}\nstderr: {}", - out.status, - String::from_utf8_lossy(&out.stderr) - ); - // Previously this configuration hung indefinitely allocating cells. - // Giving ourselves a generous 30 s ceiling still catches a regression - // without being flaky on a loaded CI runner. - assert!( - elapsed.as_secs() < 30, - "pbc-only pack should finish fast; took {elapsed:?}" - ); -} - /// Verify file-argument mode resolves paths relative to the .inp directory. #[test] fn file_arg_resolves_paths_from_inp_dir() { diff --git a/zensical.toml b/zensical.toml index ec031b4..c7d6afe 100644 --- a/zensical.toml +++ b/zensical.toml @@ -1,21 +1,17 @@ +# molpack docs — single English site served at the root. +# +# zensical build # docs → site + [project] site_name = "molpack" site_description = "Packmol-grade molecular packing in pure Rust, with Python bindings." -# Site is built by Cloudflare Pages (pip install "molcrafts-molpack[doc]" + zensical). -# Keep site_url in sync with the Cloudflare custom domain when one is set. -site_url = "https://molcrafts.github.io/molpack/" +site_url = "https://molpack.molcrafts.org/" repo_url = "https://github.com/MolCrafts/molpack" repo_name = "MolCrafts/molpack" copyright = "Copyright © 2026 MolCrafts" docs_dir = "docs" site_dir = "site" -# Markdown extensions are intentionally not listed: Zensical's built-in default -# set (admonition, attr_list, def_list, footnotes, md_in_html, toc permalink, -# pymdownx.{arithmatex,details,emoji,highlight,inlinehilite,superfences,tabbed, -# tasklist}, and more) already covers everything these docs use — the CLI/Rust/ -# Python tabbed blocks, admonitions, and code annotations. - nav = [ { "Home" = "index.md" }, { "Tutorial" = [ @@ -58,32 +54,15 @@ nav = [ extra_css = ["assets/stylesheets/extra.css"] -# Shared MolCrafts theme (github.com/MolCrafts/molcrafts-zensical-theme). Provides -# the brand palette, light/dark schemes, navigation features, and the home-page -# hero/manual-home component system used by docs/index.md. The package must be -# installed to build (see the `doc` dependency group in python/pyproject.toml). +# https://zensical.org/docs/setup/language/#site-language [project.theme] name = "molcrafts" language = "en" -# Language selector. English is the default and lives at the site root; a -# Chinese (zh) tree lives under docs/zh/. Zensical renders the header dropdown -# from `extra.alternate` (theme partial partials/alternate.html). The `url` -# filter passes absolute paths through unchanged, so each `link` carries the -# site's base path (`/molpack/`, matching site_url) to stay correct from every -# page and depth. The zh pages are placeholders for now — no docs are -# translated yet. -[project.extra] -alternate = [ - { name = "English", link = "/molpack/", lang = "en" }, - { name = "简体中文", link = "/molpack/zh/", lang = "zh" }, -] - -# Selects the molpack accent (rust orange) for links, hovers, and hero eyebrows. [project.extra.molcrafts] product = "molpack" -accent = "#c2410c" # primary (主) — molpack signature colour -accent_soft = "rgba(194, 65, 12, 0.14)" # secondary (副) — soft fill behind it +accent = "#c2410c" +accent_soft = "rgba(194, 65, 12, 0.14)" [[project.extra.social]] icon = "fontawesome/brands/github"