Skip to content

Releases: cameronabrams/pestifer

Release v2.6.1

11 Jun 15:54

Choose a tag to compare

  • bugfix: large membrane-embedding builds aborted at the end of the make_membrane_system embed step with a VMD segmentation fault (signal 11), even though the embedded, solvated, and neutralized system had already been written correctly. The clash-removal steps in bilayer_embed.tcl built atomselect "index <list>" selections directly from the per-atom index lists returned by measure contacts; on a large solvated system those lists run to ~10^5 atoms, and VMD's atom-selection parse tree has one node per index whose destructor (atomparser_node::~atomparser_node) is recursive — so freeing such a selection recurses ~10^5 frames deep and overflows the stack during interpreter teardown at VMD exit (confirmed via core dump: VMDApp::~VMDAppDeleteInterpProcAtomSel::~AtomSelParseTree::~ParseTree → hundreds of thousands of nested ~atomparser_node frames). The crash was purely in at-exit teardown but returned signal 11 and aborted the whole build, and it reproduced on VMD 1.9.3, 2.0.0, and 2.0.1a1 (it is a long-standing VMD bug, not version-specific). The clashing atoms are now mapped to (segname, resid) by reading directly from the parent whole-molecule (all) selection's attribute lists, so no large index selection — and no large parse tree — is ever constructed
  • bugfix: the same clash-removal loops invoked delatom $seg $resid (which deletes an entire residue) once per contacting atom rather than once per residue, so a lipid or water with several atoms near the protein was deleted on the first call and then re-deleted on every subsequent atom — emitting tens of thousands of no residue ... of segment ... messages on a large embed. Each clashing residue is now deduplicated and deleted exactly once
  • enhancement: embed_protein no longer appends a spurious trailing regenerate angles dihedrals to the embed psfgen script. It called writescript(regenerate=True, writepsf=False, writepdb=False), which emitted a regenerate with no following writepsf/writepdb to consume it — pure wasted work regenerating angles and dihedrals for the ~2M-atom post-autoionize context. The bilayer_embed script already regenerates before each structure it writes, so embed_protein now uses regenerate=False (matching the sibling quilt call site)

Release v2.6.0

10 Jun 21:00

Choose a tag to compare

  • new feature: NAMD runs under SLURM can now span multiple nodes, controlled by a new namd.cpu-parallel-launcher option (auto | numactl | srun | mpirun | charmrun, default auto). Previously the SLURM CPU launch path always used numactl --interleave=all namd3 +p N, which runs a single node-local process and cannot use more than one node; on a multi-node allocation with an MPI NAMD build this failed immediately with the Charm++ "to use multiple processors ... charmrun +pN namd3 / build the mpi-linux-x86_64-smp version" error. auto keeps the single-node numactl launch for one-node allocations and switches to srun namd3 when SLURM_NNODES > 1; mpirun launches via the MPI runtime's own process manager (mpirun -np N namd3, e.g. Intel MPI Hydra); charmrun uses charmrun's ++mpiexec launcher for Charm++ net-layer builds. A companion namd.srun-mpi-type option emits srun --mpi=<plugin> (e.g. pmi2 / pmix) for MPI runtimes — notably Intel MPI — whose ranks would otherwise each initialize as an independent 1-PE job and collide on the output DCD with Couldn't open DCD file ...: File exists
  • bugfix: multi-node NAMD launches no longer stage parameter files to node-local scratch. _stage_params_to_local_scratch copied the parameter files to $TMPDIR and rewrote the NAMD config's parameters line to that absolute path, which is only valid when every PE runs on one node; under a multi-node srun/mpirun launch the files existed only on the launch node, so ranks on the other nodes died with UNABLE TO OPEN CHARMM PARAMETER FILE. Staging is now gated on a single-node launch, and multi-node runs read the parameter files from the shared filesystem (as they already do for structure/coordinates)
  • bugfix: the NAMD log parser no longer aborts the whole build on a single malformed line. Live NAMD stdout from a multi-rank run (e.g. srun/mpirun across nodes) can interleave output from different PEs, producing a garbled ENERGY: record with a token like 0LINE; process_energy_line then raised ValueError on float() and killed the run even though the simulation itself was fine. process_line now catches ValueError/IndexError/KeyError from a single line processor, logs it at debug level, and continues without appending a partial record
  • bugfix: subcontroller tasks now inherit the parent run's NAMD settings. Config.taskless_subconfig built the subcontroller configuration from schema defaults only, so MD runs launched inside make_membrane_system (which uses a subcontroller for the membrane-relaxation stages) ignored user overrides such as namd.cpu-parallel-launcher and reverted to the default — reintroducing the multi-node launch failure for the relaxation stages even when the top-level tasks were correctly configured. The subconfig now inherits the progenitor's user settings (NAMD launcher, paths, force field, psfgen options) and resets only the task list
  • bugfix: terminate/package builds that run no MD step (e.g. continuationpsfgenterminate) produced a consolidated minimal .prm with an empty NONBONDED section (nonbonded=0) and only partial bonded terms, so the packaged production run failed with DIDN'T FIND vdW PARAMETER FOR ATOM TYPE ... (the first such type encountered, e.g. NH3). The standard CHARMM .prm parameter files are staged by NAMD/MD tasks, so a build with no MD step had registered only topology .str stream files for consolidation; generate_minimal_params now always stages and includes the standard parameter set (via the NAMD scripter's fetch_standard_charmm_parameters) before consolidating, and warns loudly if a consolidated file still has no nonbonded parameters
  • bugfix: corrected a Tcl variable-dereference typo (set $box_min_z / set $box_max_z instead of set box_min_z / set box_max_z) in the bilayer-embed water-gap padding, which created a garbage-named variable and left the box bound unchanged when the water gap above or below the protein was under 3 Å
  • dependency: dropped the gputil runtime dependency (and the setuptools shim it required to provide distutils on Python 3.12+). Local NVIDIA GPU enumeration now queries nvidia-smi --query-gpu=index directly via a small helper, with the same behavior as before (enumerates physical GPUs, returns an empty list when nvidia-smi is absent or fails)

Release v2.5.2

28 May 21:17

Choose a tag to compare

  • bugfix: a plain pip install pestifer (without the optional ligand-paramgen extra) crashed on every CLI invocation with ModuleNotFoundError: No module named 'dimorphite_dl'; the optional rdkit and dimorphite_dl dependencies were imported at module load time in charmmff.ligand_paramgen.protonation and charmmff.ligand_paramgen.mol2_writer, and since subcommands/__init__.py eagerly imports the make-ligand-mol2 subcommand at startup, those imports ran for every command. The imports are now lazy (deferred into the functions that use them) and raise a clear pip install pestifer[ligand-paramgen] hint when actually needed
  • bugfix: the dependencies array in pyproject.toml was missing its closing ]

Release v2.5.1

28 May 19:06

Choose a tag to compare

  • bugfix: PSFContents.remove_ignored_residues crashed with TypeError: 'PSFAtom' object is not subscriptable because it wrongly indexed the result of BaseObjList.get() with [0]; get() returns the matched object directly when there is exactly one match (which is always the case for an atom-by-serial lookup), so the trailing [0] was indexing into the atom itself
  • bugfix: AsymmetricUnit extended ignored_residues twice with the same more_ignored_residues list (a copy-paste duplicate around the intervening grafts.assign_residues call), causing PSFContents.remove_ignored_residues to be called for the same residues twice and crash on the second pass when self.atoms.get() returned None (atoms already removed) and self.atoms.remove(None) invoked PSFAtom.__eq__ against None.serial
  • bugfix: continuation+psfgen builds that inserted protein residues at glycosylation sites (e.g. extending the HIV-1 gp41 ectodomain from MPER residue 685 through TM residue 710 on a pre-built glycoprotein) emitted glycan-link patches against the protein segment (patch 14ba A:1222 A:1223) instead of the glycan segment (patch 14ba AG01:1222 AG01:1223), causing psfgen to abort with no residue X of segment A / MOLECULE DESTROYED BY FATAL ERROR; two related issues were fixed: (a) Link._from_psflinkpatch populated chainID1/chainID2 from the psfgen segname but left segname1/segname2 unset, so when chainID1 was later remapped from the segname (AG01) to the biological chainID (A) so the lookup against PDB-derived residues could succeed, the writer at PsfgenScripter.write_link (which prefers L.segname1 over L.residue1.chainID) had nothing but the biological chainID to fall back on; (b) ResidueList.renumber, when renaming a glycan residue whose resid collided with a newly inserted protein residue (glycan A:686 → A:1324 after inserting protein A:686), only propagated the resid update to links keyed by biological chainID and skipped links from PSF REMARKS whose chainID1/chainID2 carried the psfgen segname, so those stale links became orphans and the orphan cleanup at LinkList.assign_residues removed the entire glycan tree from the residue list — leaving segment AG01 with only the renumbered root and stripping the patches' target residues; renumber now also keys its mapper_by_chain by segname when segname differs from chainID

Release v2.5.0

18 May 15:21

Choose a tag to compare

  • new feature: pestifer make-ligand-mol2 subcommand that, given a 4-letter RCSB PDB code or a path to a local .pdb file, generates a CGenFF-ready Tripos mol2 file for every HETATM ligand whose resname is not already defined in the loaded CHARMM topologies; for each unknown ligand it fetches the SMILES from the RCSB Chemical Component Dictionary (preferring the OpenEye canonical-stereo descriptor), builds an RDKit Mol whose heavy atoms are in PDB order and carry the original PDB atom names, protonates at the target pH (default 7.4) via Dimorphite-DL, places explicit Hs in 3D, and writes the mol2 via Open Babel with --title <RESNAME> so the CGenFF binary/web tool registers the resulting RESI under the correct residue name (rather than the leaked tempfile path); per-ligand failures are surfaced in a summary table without aborting the run, and --smiles RESNAME=... overrides the RCSB lookup when a custom tautomer or charge state is needed
  • new feature: new pestifer.charmmff.ligand_paramgen subpackage exposes the building blocks reused by make-ligand-mol2 as a library: fetch_ligand_smiles(comp_id) (RCSB lookup with LRU caching and HTTP-error → RCSBLookupError mapping), protonate_ligand(residue, smiles, ph) (RDKit + Dimorphite-DL pipeline that preserves PDB heavy-atom order and stamps each heavy atom with the original PDB name on the _pdb_name property), write_mol2(mol, resname, outpath) (PDB-block-via-obabel writer that propagates _pdb_name and assigns sequential H1, H2, ... names to hydrogens), and generate_ligand_mol2s(parsed, charmmff_content, outdir, ph, smiles_overrides) (the orchestrator the subcommand calls, which the build workflow will also call for unknown ligands)
  • new feature: well-known per-user toppar cache at ~/.pestifer/toppar/; when this directory exists, pestifer automatically prepends it to charmmff.user_custom.searchpath so .str files dropped there are picked up by every build without requiring a user_custom block in YAML; any project-local searchpath entry is loaded after the cache and overrides it (last-loaded wins) with a warning naming both files; designed as the default destination for .str files produced by running make-ligand-mol2 output through the CGenFF binary or web tool
  • enhancement: charmmff.user_custom.searchpath entries now expand ~/ (the user's home directory) and shell environment-variable references like $HOME/foo or ${SCRATCH}/charmm before the directory check; previously those were taken literally and silently failed
  • behavior change: CHARMMFFContent.add_custom_directory no longer asserts on duplicate-basename clashes between searchpath entries; instead, the later-loaded file wins and a warning is emitted naming both paths (the assertion previously crashed pestifer when, e.g., the user's project happened to contain a .str file with the same basename as one in the cache); same-resname clashes across differently-named files in the searchpath also emit a warning and follow last-loaded-wins semantics, so a project-local override of a cached parameterization is surfaced without silently merging
  • new optional dep: [project.optional-dependencies] table in pyproject.toml declares an ligand-paramgen extra (pip install 'pestifer[ligand-paramgen]') that pulls in rdkit and dimorphite_dl; the obabel binary remains a runtime-PATH dependency, documented alongside the new subcommand
  • test: tests/unit/test_charmmff/test_charmmffcontent.py::TestCharmmffContent.setUpClass was picking the CHARMMFF version directory by mtime rather than by parsed month-year (the production ResourceManager.charmmff_version_dirs() orders semantically); when the feb26 and jul24 directories were extracted out of release order, mtime selected jul24 and the test's hard-coded counts (calibrated against feb26) failed by ~1400 residues; setUpClass now delegates to ResourceManager.charmmff_version_dirs() so the test always exercises the same CHARMMFF release that production code uses
  • test: new tests/unit/test_charmmff/ligand_paramgen/ package covers the ligand-paramgen building blocks (29 tests): protonation invariants on five SMILES across a pH=7.4 charge range, RCSB descriptor-priority selection under unittest.mock of requests.get, mol2 title/atom-name round-trip via the real obabel binary, and orchestrator-level altloc dedup and known-resname filtering using real Hetatm objects; test_charmmffcontent.py gains three TestAddCustomDirectory cases (tilde expansion, duplicate-basename warning + last-wins, duplicate-RESI warning + last-wins) that build a stub CHARMMFFContent via __new__ to avoid paying the full tarball-load cost
  • doc: new docs/source/subs/make-ligand-mol2.rst covers the subcommand with examples (PDB code, local file, SMILES override, pH override) and outlines the CGenFF round-trip workflow (web tool → ~/.pestifer/toppar/); linked from the subcommand toctree in usage.rst; the charmmff.user_custom schema docstring gains "Per-user toppar cache" and tilde-expansion sections

Release v2.4.13

14 May 19:46

Choose a tag to compare

  • behavior change: make_membrane_system no longer silently turns on NAMD's on-the-fly pressureProfile calculation during NPT/NPAT bilayer-relaxation stages on CPU NAMD; pressure profile calculation is now opt-in via a new top-level make_membrane_system.compute_pressure_profile block (enabled: false default, plus slabs / freq knobs that map to pressureProfileSlabs / pressureProfileFreq); previously, any NPT/NPAT stage whose other_parameters did not explicitly mention pressureProfile had it auto-injected as on on CPU builds, surprising GPU users (the processor-type guard correctly skipped injection on GPU but the opt-out default was hostile and unintuitive); the mdplot task's profiles=['pressure'] list, which was unconditionally requested by equilibrate_bilayer, is now likewise gated on the new flag so no spurious pressure-profile plotting is attempted when the data is not produced
  • new feature: make_membrane_system now hard-errors with PestiferBuildError if compute_pressure_profile.enabled: true is set under GPU NAMD (processor-type: gpu), or if any explicit other_parameters.pressureProfile setting in a stage is truthy on GPU; CUDA-enabled NAMD does not support pressureProfile, and the previous silent-strip behavior masked user misconfiguration; the error message names the offending stage's ensemble and points at the two ways to fix the conflict (disable the flag, or use a CPU NAMD build)
  • doc: ex16 and ex17 example YAMLs (membrane-embedded HIV-1 gp41 MPER-TM trimer builds) now carry a header comment explaining that their post-equilibration NPAT stage and packaged production run use pressureProfile, so they must be run with a CPU NAMD build; the comment names the specific other_parameters blocks to strip if a user wants to run them on GPU

Release v2.4.12

13 May 21:50

Choose a tag to compare

  • bugfix: PackmolLogParser failed on logs produced by packmol 21.x because the new release reorganized its header in three breaking ways: the Periodic boundary condition activated: line became multi-line with indented Minimum coordinates: and Maximum coordinates: rows, the PBC Reference box: line was removed entirely (its information now carried by the min/max rows), and the per-type Maximum number of GENCAN loops for type: lines were dropped in favor of the aggregate for all molecule packing: value alone; the parser now auto-detects the PBC format by checking whether numeric tokens trail the activation line and reconstructs both pbc_boxsize (max-min) and pbc_reference_box (concatenated min+max) so downstream consumers see the same metadata schema; logs from packmol 20.15.1 continue to parse correctly via the legacy code path
  • bugfix: PackmolLogParser initialized each structure's current_gencan_loops counter only inside the per-type Maximum number of GENCAN loops for type: block, so a KeyError was raised on the first GENCAN report for any structure whose type lacked that header line (always the case in packmol 21.x); the counter is now initialized to 0 at structure-creation time
  • bugfix: PackmolLogParser.finalize() crashed with 'Axes' object is not subscriptable for single-structure packs because plt.subplots(1, 1) returns a bare Axes rather than a 1-element array; squeeze=False plus ax.flatten() makes the indexing uniform

Release v2.4.11

13 May 20:31

Choose a tag to compare

  • new feature: integrated the VMD ssrestraints plugin into the md task as a peer of constraints and colvar_specs; when a top-level ssrestraints sub-dict appears under an md task, pestifer invokes the plugin against the current state's PSF/PDB to emit a NAMD extraBonds file and wires extraBonds=on / extraBondsFile=<basename>.extrabonds into the generated NAMD config; the schema entry exposes all 13 plugin options (selection text, force constants for protein/NA, na mode, ideal, H-bond restraint sub-options) with defaults that mirror the plugin's own; the generated extraBonds file participates in the artifact pipeline via a new NAMDExtraBondsFileArtifact
  • enhancement: packmol is now version-validated at config-load time; pestifer probes the resolved binary by sending it an empty stdin and parses the version banner, refusing any version below 20.15.1 (the first release with the pbc keyword); when the resolved binary lives inside a conda environment, the error message hints at the AmberTools/packmol-memgen shadow problem and recommends installing a standalone packmol outside the conda env; paths.packmol may be set to an absolute path in user YAMLs to bypass PATH resolution entirely; ex16 and ex17 example YAMLs now demonstrate this pattern
  • enhancement: the PackmolScripter post-run log scan now flags lines containing ERROR, Could not open file, or Could not find and forces a non-zero return code so callers raise; older packmol could exit 0 while still failing internally, and this surfaces those failures
  • bugfix: [package vcompare [vmdinfo version] X.Y] returns unreliable results on VMD builds whose version banner has trailing tags or non-numeric components; replaced the comparison in both pestifer/resources/tcl/macros.tcl and the macro generator in pestifer/core/labels.py with [lindex [split [vmdinfo version] .] 0] < MAJOR, an integer-major-version gate that is robust across builds
  • doc: explain that the mdplot subcommand and task emit per-quantity CSV files alongside plots; explain that density is a pestifer-computed timeseries column derived from the cell volume, not a NAMD output; updated the mdplot 2.4.x enhancement docs (time-on-x-axis, automatic unit selection)
  • test: corrected _ARCHIVE_PREFIX in test_desolvate.py to match the schema default ('artifacts') so the test reflects what terminate actually produces by default; relaxed the per-binary path assertions in test_config_help_examples from strict equality to endswith() so legitimate per-example paths.packmol overrides (such as the absolute paths added to ex16/ex17) do not break the smoke test

Release v2.4.10

11 May 16:21

Choose a tag to compare

  • enhancement: GPU mode is now auto-detected by comparing paths.namd3 and paths.namd3gpu; if the two paths differ and namd3gpu is found in PATH, pestifer switches to GPU mode automatically — no namd.processor-type setting required; processor-type is retained in the schema for backward compatibility but is ignored; paths.namd3gpu defaults to namd3 (CPU mode when identical); workstation users with separate CPU and GPU-resident NAMD3 builds simply set paths.namd3gpu to the GPU binary name/path; the --gpu CLI flag now correctly propagates GPU mode to both the config and the live NAMD scripter
  • bugfix: GPU-resident NAMD3 config parameter renamed from GPUResident to CUDASOAintegrate to match current NAMD3 release notes; outputEnergies: 500 added to the gpu-resident schema section (NAMD recommends 500–1000 for GPU-resident mode); writescript now suppresses any param key that appears in gpu-resident (case-insensitive) so each parameter appears exactly once in the generated config file
  • enhancement: multi-GPU GPU-resident launches now include +pmepes K to assign fewer PEs to the PME device and reduce load imbalance; K is computed as total PEs minus 8×(N−1) for N GPUs, matching the NAMD3 recommended work distribution examples

Release v2.4.9

11 May 15:18

Choose a tag to compare

  • enhancement: mdplot timeseries plots now use simulation time on the bottom x-axis instead of raw timestep index; units are auto-selected by magnitude (ps when total duration < 1000 ps, ns otherwise); the integer timestep index is shown on the top spine via a secondary x-axis; NAMDLogParser.finalize() adds a dt_fs constant column to the energy dataframe so that chained runs with different timestep sizes are handled correctly — simulation time is computed in the plot task as cumsum(dt_fs × ΔTS / 1000) across the concatenated dataframe, which correctly accumulates time across run segments regardless of whether the timestep changes between segments
  • enhancement: mdplot timeseries y-axis unit labels now default to the correct physical units for known NAMD output columns (kcal/mol for energy terms, K for temperature, bar for pressure, ų for volume) rather than *; energy quantities whose maximum absolute value exceeds 1000 kcal/mol are automatically scaled by 1/1000 and labeled 1000 kcal/mol to avoid unwieldy tick magnitudes