modify-package

The subcommand is meant for development use only, and is only available when pestifer is installed as an editable source package. It allows modifications to your source package, including adding and deleting examples and updating atomselect macros.

This subcommand will only work on a full source repository, so if you want to use it, you will need to fork the repository cameronabrams/pestifer on GitHub and then clone your fork to your local machine. If you have not already done so, you can do this with the following commands:

git clone git@github.com:your-name/pestifer.git # assuming you have SSH access to your github account and you forked pestifer
cd pestifer
pip install -e . # so that you can tell `pestifer` to modify itself

The Git workflow is automatic

Because every modification is meant to become a pull request, modify-package folds the Git workflow into each command and runs it by default: it verifies your working tree is clean, makes the change on a fresh branch (auto-named modpkg/<category>-<verb>-<detail>), and commits exactly the files it touched — then prints the git push / gh pr create steps. You do not need to create a branch or commit by hand.

  • --branch NAME — name the branch yourself instead of using the auto-generated name.

  • --no-branch — skip branching and committing entirely; just apply the change to your working tree (useful when you are already on a feature branch, or want to stage the change yourself).

Both options are available on every example, pdb-repo, and charmmff verb. The default (auto-branch) requires a clean working tree, so the branch contains only your contribution.

Adding an Example

I developed most of the examples by iteration, so I would start with a simple YAML config file and then modify it to add new features or test new functionality. The simplest way to start with a new example is to use pestifer new-system. For example, consider the myoglobin example. I started with:

$ cd
$ mkdir ~/1mob_working_directory # create a working directory
$ cd ~/1mob_working_directory
$ pestifer new-system 1mob # this is the PDB ID for sperm whale myoglobin
$ pestifer build 1mob.yaml # this will run the psfgen task and generate a PSF file for the system

This run built successfully. So I rebuilt a full template config:

$ pestifer new-system 1mob --full

Then I edited 1mob.yaml to add a salt concentration specification to the solvate task. Then I ran it again:

$ pestifer build 1mob.yaml

That also built successfully, so I added a new example to the pestifer package (from a clean working tree, so the auto-created branch contains only the new example):

$ cd ~/1mob_working_directory  # where I have the 1mob.yaml file
$ pestifer modify-package example add 1mob.yaml  # add the example to the package

This copies 1mob.yaml to the appropriate location in the pestifer source package, updates the generated docs/source/examples/19/1mob.rst to link the new example, and — because the Git workflow is automatic (see above) — creates a branch modpkg/example-add-1mob-yaml and commits exactly those files. Then I edited docs/source/examples/19/1mob.rst to add a description of the example, and folded that edit into the same commit:

$ cd ~/Git/pestifer
$ git add docs/source/examples/19/1mob.rst
$ git commit --amend --no-edit

Then, I recompiled the documenation while my package was still on the modpkg/example-add-1mob-yaml branch:

$ cd ~/Git/pestifer/docs
$ make html

And I made sure the new example was there. It also appeared correctly as number 19 when displaying the examples:

$ pestifer show-resources examples

    Examples:

       ID      DBID  Name                            Title
        1      6pti  bpti1                           Bovine Pancreatic Trypsin Inhibitor (BPTI)
        2      6pti  bpti2                           BPTI with phosphate ion excluded in salty solution
        3      6pti  bpti3                           BPTI, no phosphate, some random mutations plus deletion of one disulfide
        4      6pti  bpti4                           BPTI, no phosphate, introducing a disulfide via mutations
        5      1f7a  hiv-protease                    HIV-1 protease dimer
        6      1fas  green-mamba-toxin               Fasciculin 1, an Anti-Acetylcholinesterase Toxin from Green Mamba Snake Venom
        7      4zmj  hiv-sosip-env-ectodomain1       Closed, Unliganded HIV-1 BG505 Env SOSIP-664 Trimer
        8      4tvp  hiv-sosip-env-ectodomain2       Closed, PGT122/35O22-Liganded HIV-1 BG505 Env SOSIP.664 Trimer (Fabs removed)
        9      8fad  hiv-ad8-env-ectodomain          HIV-1 Env Trimer 8fad
       10      8fae  hiv-ae2-env-ectodomain          HIV-1 Env Trimer 8fae
       11      7txd  hiv-sosip-env-ectodomain3       HIV-1 Env Trimer 7txd, substitutions, no ligands
       12      5vn3  hiv-sosip-env-ectodomain4       HIV-1 Env Trimer 5vn3, excluding sCD4 and Fab chains, with Gly3 stubs replacing missing v1/2
       13      2ins  insulin-hexamer                 hexameric insulin
       14      4zxb  insulin-receptor-ectodomain     insulin receptor ectodomain, Fabs removed
       15      7xix  sars-cov2-S-BA2                 BA.2 SARS-CoV-2 Spike 7xix, fully glycosylated using grafts, and cleaved
       16      6e8w  hiv-mpertm3-membrane1           HIV-1 gp41 MPER-TM trimer 6e8w embedded in DMPC/DHPC bilayer
       17      6e8w  hiv-mpertm3-membrane2           HIV-1 gp41 MPER-TM trimer 6e8w embedded in model viral bilayer
       18      5fkw  ecoli-polymerase                E. Coli replicative DNA polymerase complex bound to a primer-template DNA
       19      1mob  1mob                            Sperm whale myoglobin

I then renamed this example using the modify-package subcommand:

$ pestifer modify-package example rename 19 sperm-whale-myoglobin

Satisfied with the example, I merged the branch back into main:

$ git checkout main
$ git merge modpkg/example-add-1mob-yaml
$ git branch -d modpkg/example-add-1mob-yaml

If you want to add an example, you can do so in your own fork of the repository, and then submit a pull request to have it merged into the main repository.

Contributing a New Custom Residue

Pestifer ships a small set of built-in custom residue definitions (for example, the CGenFF ligands 83G and LF0) that live in pestifer’s shared charmmff/custom/ directory (release-independent, a sibling of the per-release version directories) and supplement the standard CHARMM release. You can contribute a new one from any file containing a CHARMM RESI block (a .str, .rtf, or .top file), and modify-package will:

  1. validate the file and extract the RESI names it defines,

  2. refuse names that already exist in the force field (unless you pass --force),

  3. copy the file into the shared charmmff/custom/ directory,

  4. register each RESI name under a segtype in pestifer.core.labels (default ligand; use --segtype to choose another), and

  5. clear the resource cache so the new residue is picked up on the next run.

The simplest invocation makes the change in your working tree:

$ pestifer modify-package charmmff add-residue mylig.str --segtype ligand

Folding in the git workflow

Because a contribution is meant to become a pull request, the branch-and-commit step runs automatically (see above): from a clean working tree it creates a fresh branch off HEAD, makes the change, and commits exactly the files it touched (the copied custom file and, if a segtype was registered, labels.py) — never anything else in your tree. Pass --branch NAME to name the branch, or --no-branch to apply the change without committing:

$ pestifer modify-package charmmff add-residue mylig.str --branch add-mylig-residue

Installed custom residue file: .../charmmff/custom/mylig.str
  RESI defined: MYLIG
  classified MYLIG as segtype 'ligand'
  resource cache cleared; it will rebuild on the next run.

Committed to new branch 'add-mylig-residue':
    pestifer/resources/charmmff/custom/mylig.str
    pestifer/core/labels.py

Review it with `git show`, then push and open a pull request:
    git push -u origin add-mylig-residue
    gh pr create      # or open the PR on GitHub

Pushing the branch and opening the pull request are left to you — inspect the commit with git show first, then push to your fork and open a PR against cameronabrams/pestifer for review.

Contributing PDB-repository coordinates for a residue

Adding a residue’s topology (above) lets pestifer build the residue, but make_membrane_system also needs sampled coordinates to place a lipid on the grid. Those live in pestifer’s built-in PDB repository: a set of collections, each a <stream>.tgz tarball whose single top-level directory is the stream name and which holds one <RESI>/ subdirectory per residue (its info.yaml plus a set of conformer PDBs).

First generate the coordinates with make-pdb-collection, which samples conformers of the residue and writes the entry directory:

$ pestifer make-pdb-collection --resname MYLIP --output-dir mycoords
# -> mycoords/MYLIP/{info.yaml, MYLIP-00.pdb, MYLIP-01.pdb, ...}

Then install that entry into the repository with pdb-repo add-entry, which validates it (info.yaml parses and every conformer PDB it names is present) and adds it under <collection>/MYLIP/ in the target collection tarball, creating the tarball if the collection does not exist yet:

$ pestifer modify-package pdb-repo add-entry mycoords/MYLIP --branch add-mylip-coords

By default the collection is the residue’s segtype (lipid -> the lipid collection; ion/water -> solvent); use --collection NAME to choose another, and --force to replace an entry already present for that resname. As with the other verbs, the change is committed on a fresh branch by default (--branch NAME to name it, --no-branch to skip) — from a clean working tree, containing exactly the changed collection tarball for you to push and open as a pull request.

Installing many entries at once

make-pdb-collection --streamID <stream> (or --substreamID) writes a whole directory of entries – one <RESI>/ subdirectory per residue in the stream – rather than a single entry. pdb-repo add-entry accepts that container directory directly and installs every entry it finds in one command, instead of you having to invoke it once per residue:

$ pestifer make-pdb-collection --streamID lipid --substreamID yeast --output-dir lipid-yeast
# -> lipid-yeast/{DYPC/, DYPE/, PYPE/, YOPA/, ...}
$ pestifer modify-package pdb-repo add-entry lipid-yeast --branch add-yeast-lipids

Each entry goes to its own default collection (its residue’s segtype), so a mixed directory can populate several collections at once; --collection NAME forces them all into one. Every affected tarball is validated up front and repacked once. The argument is the same either way – point add-entry at a single <RESI>/ directory for one entry, or at a directory of them for a batch.

Regenerating the derived segtype classification

Pestifer classifies most residues into a segtype (protein, lipid, glycan, nucleicacid, ligand, ion, water) by the CHARMM topology/stream file each residue is defined in, rather than from a hand-maintained list. This classification is stored in the generated resource pestifer/resources/labels/derived_segtypes.json and merged into Labels.segtype_of_resname at import. After updating the bundled CHARMM force field (or adding a residue whose classification should follow from its defining file), regenerate it with

$ pestifer modify-package charmmff regenerate-segtypes

This re-derives the classification from every installed CHARMM release, excluding the curated names in pestifer.core.labels (which always win), and rewrites the JSON. Like the other developer actions, it can be combined with --branch to make the change on a fresh branch and commit it for review.

The modification ledger

Every mutating modify-package command records what it did in an append-only ledger at pestifer/resources/modifications.jsonl (one JSON object per line). Each entry captures the category and verb, a one-line summary, the files touched, the committer, the branch, and a unique id. The ledger complements the resource provenance tags shown by show-resources — provenance says a file is custom; the ledger says who added it, when, via which operation. Because the entry is committed alongside the change, it travels in the contribution’s branch and PR.

List what has been recorded with ledger show:

$ pestifer modify-package ledger show            # all entries, oldest first
$ pestifer modify-package ledger show --limit 10 --category charmmff

#1  2026-07-08  charmmff/add-residue  add MYLIG to built-in custom [segtype: ligand]
#2  2026-07-08  pdb-repo/add-entry    add MYLIG coordinates to the lipid collection

Reverting a modification

ledger revert <id> reverses a recorded modification. It finds the commit that made the change, git-reverts it into the working tree, and then curates the ledger so the audit trail survives: the original entry is marked reverted and a new revert entry is appended. Like the other verbs it runs on a fresh branch by default (--branch NAME / --no-branch apply):

$ pestifer modify-package ledger revert 1

Reverted modification #1 (add MYLIG to built-in custom [segtype: ligand]); recorded as #3.

Committed the revert to new branch 'modpkg/revert-1':
...

Revert uses git under the hood, so it works uniformly for any verb and surfaces a conflict if a later change touched the same files. Only modifications that were committed (the default flow) can be reverted this way; a change made with --no-branch is left to you to undo by hand.