molforge 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {molforge-0.3.0 → molforge-0.4.0}/CHANGELOG.md +365 -0
- {molforge-0.3.0 → molforge-0.4.0}/PKG-INFO +9 -2
- {molforge-0.3.0 → molforge-0.4.0}/README.md +4 -1
- {molforge-0.3.0 → molforge-0.4.0}/pyproject.toml +6 -1
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/__init__.py +1 -1
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/core/__init__.py +4 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/core/metadata_keys.py +27 -0
- molforge-0.4.0/src/molforge/core/provenance.py +350 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/io/__init__.py +19 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/io/mmcif.py +103 -15
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/io/pdb_alphafold.py +10 -0
- molforge-0.4.0/src/molforge/io/trajectory.py +399 -0
- molforge-0.4.0/src/molforge/prep/__init__.py +62 -0
- molforge-0.4.0/src/molforge/prep/_deps.py +58 -0
- molforge-0.4.0/src/molforge/prep/_provenance.py +65 -0
- molforge-0.4.0/src/molforge/prep/clean.py +157 -0
- molforge-0.4.0/src/molforge/prep/fix.py +256 -0
- molforge-0.4.0/src/molforge/prep/pipeline.py +120 -0
- molforge-0.4.0/src/molforge/prep/protonate.py +123 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/docking/diffdock.py +39 -4
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/docking/vina.py +49 -1
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/folding/alphafold.py +18 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/folding/boltz.py +13 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/folding/esmfold.py +18 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/folding/rosettafold.py +12 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/generative/proteinmpnn.py +40 -6
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/generative/rfdiffusion.py +24 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/md/gromacs.py +48 -3
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/md/openmm.py +53 -3
- molforge-0.4.0/tests/unit/core/test_provenance.py +339 -0
- molforge-0.4.0/tests/unit/io/test_mmcif.py +767 -0
- molforge-0.4.0/tests/unit/io/test_trajectory.py +251 -0
- molforge-0.4.0/tests/unit/prep/test_clean.py +124 -0
- molforge-0.4.0/tests/unit/prep/test_fix.py +161 -0
- molforge-0.4.0/tests/unit/prep/test_pipeline.py +123 -0
- molforge-0.4.0/tests/unit/prep/test_protonate.py +121 -0
- molforge-0.4.0/tests/unit/wrappers/__init__.py +0 -0
- molforge-0.4.0/tests/unit/wrappers/test_provenance_adoption.py +692 -0
- molforge-0.3.0/tests/unit/io/test_mmcif.py +0 -171
- {molforge-0.3.0 → molforge-0.4.0}/.gitignore +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/LICENSE +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/data/README.md +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/notebooks/README.md +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/plugins/README.md +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/plugins/example_plugin/README.md +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/plugins/example_plugin/pyproject.toml +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/requirements/README.md +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/scripts/README.md +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/core/atom.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/core/atom_array.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/core/chain.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/core/constants.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/core/protein.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/core/residue.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/docking/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/ensembles/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/ensembles/clustering.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/ensembles/consensus.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/ensembles/density.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/ensembles/geometry.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/ensembles/weighting.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/generative.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/io/dispatch.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/io/fasta.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/io/mol2.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/io/pdb.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/io/pdbqt.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/io/pqr.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/io/sdf.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/md/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/metrics/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/metrics/dockq.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/metrics/gdt.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/metrics/lddt.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/metrics/tm.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/ml/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/ml/embeddings.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/ml/graph.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/ml/sequence_features.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/ml/structure_features.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/plugins/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/plugins/registry.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/py.typed +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/sequence/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/sequence/alignment.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/sequence/composition.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/sequence/matrices.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/sequence/mutations.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/structure/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/structure/contacts.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/structure/dihedrals.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/structure/dssp.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/structure/geometry.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/structure/rmsd.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/structure/sasa.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/structure/superposition.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/validation/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/validation/criteria.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/validation/orchestration.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/validation/verdict.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/docking/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/docking/_base.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/docking/prep.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/folding/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/folding/_base.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/generative/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/md/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/src/molforge/wrappers/md/_base.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/benchmarks/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/benchmarks/conftest.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/benchmarks/test_perf.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/conftest.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/cif/dipeptide.cif +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/fasta/.gitkeep +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/fasta/multiline_with_digits.fasta +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/fasta/simple.fasta +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/.gitkeep +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/ala_tripeptide_heavy.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/alphafold_mock.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/dipeptide.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/helix.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/mini_beta_sheet.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/mini_complex_bad.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/mini_complex_good.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/mini_complex_native.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/mini_ensemble.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/mini_mixed.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/mini_with_ligand.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/multi_model.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/real_small_protein.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/real_with_altloc_sidechains.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/real_with_ligand_realistic.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/tripeptide.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/with_altloc.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/fixtures/pdb/with_insertion_code.pdb +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/integration/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/integration/test_fixtures.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/integration/test_real_fixtures.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/integration/test_smoke.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/core/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/core/test_atom_array.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/core/test_constants.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/core/test_core_smoke.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/core/test_hierarchy.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/core/test_metadata_keys.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/docking/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/docking/test_docking_smoke.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/ensembles/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/ensembles/conftest.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/ensembles/test_clustering.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/ensembles/test_consensus.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/ensembles/test_density.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/ensembles/test_geometry.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/ensembles/test_smoke.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/ensembles/test_weighting.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/io/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/io/test_alphafold.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/io/test_dispatch.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/io/test_fasta.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/io/test_io_smoke.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/io/test_mol2.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/io/test_pdb.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/io/test_pdbqt.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/io/test_pqr.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/io/test_sdf.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/md/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/metrics/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/metrics/test_dockq.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/metrics/test_gdt.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/metrics/test_lddt.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/metrics/test_tm.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/ml/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/ml/test_embeddings.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/ml/test_graph.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/ml/test_sequence_features.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/ml/test_structure_features.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/plugins/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/plugins/test_plugins_smoke.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/plugins/test_registry.py +0 -0
- {molforge-0.3.0/tests/unit/sequence → molforge-0.4.0/tests/unit/prep}/__init__.py +0 -0
- {molforge-0.3.0/tests/unit/structure → molforge-0.4.0/tests/unit/sequence}/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/sequence/test_alignment.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/sequence/test_composition.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/sequence/test_matrices.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/sequence/test_mutations.py +0 -0
- {molforge-0.3.0/tests/unit/wrappers → molforge-0.4.0/tests/unit/structure}/__init__.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/structure/test_contacts.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/structure/test_dihedrals.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/structure/test_dssp.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/structure/test_geometry.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/structure/test_rmsd.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/structure/test_sasa.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/structure/test_structure_smoke.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/structure/test_superposition.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/test_typing.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/validation/test_criteria.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/validation/test_orchestration.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/validation/test_verdict.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_alphafold.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_boltz.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_diffdock.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_docking_base.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_esmfold.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_folding_base.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_gromacs.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_md_base.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_openmm.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_prep.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_proteinmpnn.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_rfdiffusion.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_rosettafold.py +0 -0
- {molforge-0.3.0 → molforge-0.4.0}/tests/unit/wrappers/test_vina.py +0 -0
|
@@ -7,6 +7,371 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.0] 2026-06-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Provenance adoption pass 2 — MD wrappers and prep functions.**
|
|
15
|
+
Completes the wrapper-adoption work. Where
|
|
16
|
+
pass 1 demonstrated cross-wrapper chaining (ESMFold → Vina),
|
|
17
|
+
pass 2 exercises chaining **within** a single wrapper's
|
|
18
|
+
multi-step pipeline and across the composable prep functions —
|
|
19
|
+
the harder case and the one that proves the design composes.
|
|
20
|
+
|
|
21
|
+
Adopted (2 wrappers + 5 functions):
|
|
22
|
+
|
|
23
|
+
- **`molforge.wrappers.md.OpenMM`** — `prepare` / `minimize` /
|
|
24
|
+
`run` each attach a `Provenance` to the output's metadata. Each
|
|
25
|
+
step's parent is the previous step's `Provenance`, so a full
|
|
26
|
+
pipeline leaves the final `Trajectory` with a 3-deep chain
|
|
27
|
+
that reads as `["OpenMM.prepare", "OpenMM.minimize", "OpenMM.run"]`
|
|
28
|
+
oldest-first. When the input `Protein` has its own `Provenance`
|
|
29
|
+
(e.g. it came from ESMFold), the chain extends back through it
|
|
30
|
+
— a sequence-to-trajectory workflow ends with a 4-deep chain
|
|
31
|
+
that traces all the way to the sequence.
|
|
32
|
+
|
|
33
|
+
- **`molforge.wrappers.md.GROMACS`** — same three-step pattern
|
|
34
|
+
as OpenMM, with the engine strings `"GROMACS.prepare"`,
|
|
35
|
+
`"GROMACS.minimize"`, `"GROMACS.run"`. The minimize step
|
|
36
|
+
appends to `simulation.metadata` (since minimize returns the
|
|
37
|
+
same Simulation in-place, not a new one), preserving the
|
|
38
|
+
chain across the mutation.
|
|
39
|
+
|
|
40
|
+
- **`molforge.prep.{remove_heterogens, fix_missing_atoms,
|
|
41
|
+
add_caps, add_hydrogens, prepare_for_md}`** — each prep
|
|
42
|
+
function chains a `Provenance` step onto the output's
|
|
43
|
+
metadata. `prepare_for_md` (which composes the other four in
|
|
44
|
+
sequence) leaves the result with a 4-deep chain naturally:
|
|
45
|
+
`["molforge.prep.remove_heterogens",
|
|
46
|
+
"molforge.prep.fix_missing_atoms",
|
|
47
|
+
"molforge.prep.add_caps",
|
|
48
|
+
"molforge.prep.add_hydrogens"]`. No special handling needed
|
|
49
|
+
in the composite — the inner functions chain themselves.
|
|
50
|
+
|
|
51
|
+
Two helpers, both private:
|
|
52
|
+
|
|
53
|
+
- **`molforge.wrappers.md.{openmm,gromacs}._parent_provenance(meta)`**
|
|
54
|
+
— extracts a `Provenance | None` from a free-form metadata
|
|
55
|
+
dict, narrowing the type. Per-wrapper rather than shared so
|
|
56
|
+
each MD wrapper stays self-contained.
|
|
57
|
+
|
|
58
|
+
- **`molforge.prep._provenance.chain_prep_provenance(output, *,
|
|
59
|
+
engine, parameters, input_protein)`** — the DRY-up for the
|
|
60
|
+
five prep functions. One place to evolve the prep-side
|
|
61
|
+
provenance shape later (e.g. when we want to also stamp the
|
|
62
|
+
PDBFixer / OpenMM versions used).
|
|
63
|
+
|
|
64
|
+
Backwards compatibility: every existing ad-hoc metadata key
|
|
65
|
+
(`metadata["engine"]`, `metadata["run_dir"]`, `metadata["emtol"]`,
|
|
66
|
+
etc.) is preserved unchanged. The new `metadata[PROVENANCE]` is
|
|
67
|
+
additive.
|
|
68
|
+
|
|
69
|
+
Tests (`tests/unit/wrappers/test_provenance_adoption.py`, 8 new
|
|
70
|
+
in addition to the 11 from pass 1):
|
|
71
|
+
|
|
72
|
+
- `TestOpenMMProvenanceChain` (2 tests) drives a full prepare →
|
|
73
|
+
minimize → run pipeline against a real OpenMM install
|
|
74
|
+
(skipped if openmm missing). The headline scenario
|
|
75
|
+
`["ESMFold", "OpenMM.prepare", "OpenMM.run"]` is exercised
|
|
76
|
+
end-to-end.
|
|
77
|
+
|
|
78
|
+
- `TestGROMACSProvenanceWiring` (2 tests) — GROMACS needs the
|
|
79
|
+
`gmx` binary which CI usually lacks. The tests inspect the
|
|
80
|
+
module source to assert the three step engine strings appear
|
|
81
|
+
and that `_parent_provenance(...)` is used — a regression net
|
|
82
|
+
that catches future code that bypasses the helper without
|
|
83
|
+
needing the real engine to run.
|
|
84
|
+
|
|
85
|
+
- `TestPrepProvenanceChain` (4 tests) — exercises each prep
|
|
86
|
+
function individually, then a two-function chain, then the
|
|
87
|
+
full `prepare_for_md` 4-step chain, then the "ESMFold + 4
|
|
88
|
+
prep steps" 5-deep chain (the most realistic scenario).
|
|
89
|
+
Skipped if openmm + pdbfixer aren't both installed.
|
|
90
|
+
|
|
91
|
+
With pass 2 complete, the headline scenario from the roadmap
|
|
92
|
+
("20 designs from ProteinMPNN, docked with Vina, refined with
|
|
93
|
+
OpenMM") is now fully traceable: every output object's
|
|
94
|
+
`metadata[PROVENANCE].chain()` reads as the producer pipeline,
|
|
95
|
+
with sufficient detail in each step's `parameters` to
|
|
96
|
+
reconstruct the exact call. The remaining provenance work is
|
|
97
|
+
optional polish: persistence to a sidecar format, hash-keyed
|
|
98
|
+
caching (the next roadmap item), and richer engine-version
|
|
99
|
+
introspection.
|
|
100
|
+
|
|
101
|
+
- **Provenance adoption across folding, docking, and generative
|
|
102
|
+
wrappers (pass 1).** Builds on the `Provenance` surface added in
|
|
103
|
+
`9fafbba`. Every wrapper in scope now attaches a `Provenance` to
|
|
104
|
+
the output's `metadata[PROVENANCE]` alongside its existing ad-hoc
|
|
105
|
+
keys, so the "20 designs from ProteinMPNN, docked with Vina"
|
|
106
|
+
scenario is now traceable end-to-end.
|
|
107
|
+
|
|
108
|
+
Wrappers adopted:
|
|
109
|
+
- **Folding**: ESMFold, AlphaFold, Boltz, RoseTTAFold. Each
|
|
110
|
+
records its `__init__` config (model name, device, msa mode,
|
|
111
|
+
recycles, etc.) in `parameters` and the input sequence in
|
|
112
|
+
`inputs`. Folding has no upstream wrapper, so `parent` is
|
|
113
|
+
`None`.
|
|
114
|
+
- **Docking**: Vina, DiffDock. Each `DockingResult.metadata`
|
|
115
|
+
gets a `Provenance` covering the run (box, exhaustiveness,
|
|
116
|
+
seed, etc. in `parameters`; receptor + ligand refs in
|
|
117
|
+
`inputs`); when the receptor was a `Protein` with its own
|
|
118
|
+
`Provenance`, that becomes the `parent` so a Vina pose
|
|
119
|
+
docked against an ESMFold prediction chains back to the
|
|
120
|
+
sequence. Per-pose `Pose.metadata` keeps existing per-pose
|
|
121
|
+
keys (`confidence`, `source_file`) — poses aren't
|
|
122
|
+
independently produced, so they share the result-level
|
|
123
|
+
provenance rather than each carrying their own.
|
|
124
|
+
- **Generative**: RFdiffusion, ProteinMPNN. Each returned
|
|
125
|
+
design (a `Protein` for RFdiffusion, a `DesignedSequence`
|
|
126
|
+
for ProteinMPNN) gets its own `Provenance` — all designs
|
|
127
|
+
from one call share the same Provenance object (frozen +
|
|
128
|
+
immutable, so by-reference sharing is safe). `design_index`
|
|
129
|
+
stays as a separate metadata key, not part of `parameters`,
|
|
130
|
+
since it identifies *which* design rather than the engine
|
|
131
|
+
config.
|
|
132
|
+
- **`molforge.io.load_alphafold`**: the loader helper. The
|
|
133
|
+
engine name reflects that this is the loader, not the
|
|
134
|
+
AlphaFold run itself (`engine="load_alphafold"`); the file
|
|
135
|
+
path goes in `inputs["path"]`.
|
|
136
|
+
|
|
137
|
+
Existing ad-hoc metadata keys are preserved across every change
|
|
138
|
+
— `metadata["engine"]`, `metadata["source_sequence"]`,
|
|
139
|
+
`metadata["source_args"]`, etc. continue to work for 1.x
|
|
140
|
+
backwards compatibility. The new `metadata[PROVENANCE]` is
|
|
141
|
+
*additive*, not a replacement, until 2.x.
|
|
142
|
+
|
|
143
|
+
Two wrapper-side surface changes worth noting:
|
|
144
|
+
- `Vina._parse_poses_pdbqt` and `DiffDock._parse_outputs`
|
|
145
|
+
gained optional `provenance_parameters` /
|
|
146
|
+
`provenance_inputs` / `provenance_parent` kwargs (and
|
|
147
|
+
DiffDock additionally `receptor_ref` / `ligand_ref`). The
|
|
148
|
+
kwargs are optional so legacy tests calling the parsers
|
|
149
|
+
directly without those refs still work — they just don't
|
|
150
|
+
get a Provenance attached. The main `dock()` entry points
|
|
151
|
+
always pass them.
|
|
152
|
+
- The `Vina` module gained a `_provenance_ref` helper for
|
|
153
|
+
converting a `Protein | str | PathLike` to a JSON-native
|
|
154
|
+
string identifier.
|
|
155
|
+
|
|
156
|
+
Tests (`tests/unit/wrappers/test_provenance_adoption.py`, 11
|
|
157
|
+
new): one assertion per adopted wrapper plus a parent-chaining
|
|
158
|
+
integration test that exercises the headline scenario
|
|
159
|
+
(`ESMFold -> Vina` chain). Each adoption test holds the wrapper
|
|
160
|
+
to a uniform contract: `engine` matches the documented name,
|
|
161
|
+
`parameters` contains every promised key, `inputs` contains the
|
|
162
|
+
expected input identifier(s). The chaining test confirms
|
|
163
|
+
`result.metadata[PROVENANCE].chain()` reads as
|
|
164
|
+
`["ESMFold", "Vina"]` oldest-first.
|
|
165
|
+
|
|
166
|
+
- **First-class provenance tracking: `molforge.core.Provenance`.** A
|
|
167
|
+
raw PDB and a folded AlphaFold prediction look identical at the
|
|
168
|
+
AtomArray level; the only difference is *how the structure was
|
|
169
|
+
produced*. Pre-existing engine wrappers already wrote some of this
|
|
170
|
+
information into `metadata` ad-hoc — ESMFold sets
|
|
171
|
+
`metadata["engine"] = "ESMFold"`, RFdiffusion sets
|
|
172
|
+
`metadata["source_args"]` — but the keys are scattered, the shapes
|
|
173
|
+
disagree across engines, and there's no concept of a *parent*
|
|
174
|
+
output, so a chain of operations (fold -> dock -> MD) is not
|
|
175
|
+
traceable. This commit canonicalises the shape.
|
|
176
|
+
|
|
177
|
+
The new `Provenance` dataclass (frozen, JSON-round-trippable) has:
|
|
178
|
+
- **`engine`** — producer name; an engine ("ESMFold", "Vina")
|
|
179
|
+
or a molforge function path ("molforge.prep.prepare_for_md").
|
|
180
|
+
- **`engine_version`** — engine's own version string.
|
|
181
|
+
- **`molforge_version`** — auto-filled from `molforge.__version__`.
|
|
182
|
+
- **`timestamp`** — ISO-8601 UTC, auto-filled.
|
|
183
|
+
- **`parameters`** — engine-specific arguments (must be
|
|
184
|
+
JSON-native; validated eagerly at construction so a wrapper
|
|
185
|
+
can't smuggle in a `Path` or NumPy array that crashes much
|
|
186
|
+
later at serialisation).
|
|
187
|
+
- **`inputs`** — identifiers of the input data (e.g.
|
|
188
|
+
`{"sequence": "MKTVRQ..."}`, `{"receptor": "/path/to.pdb"}`).
|
|
189
|
+
- **`parent`** — the provenance of the input this step
|
|
190
|
+
*consumed*. Recursively a `Provenance` or `None`. This is what
|
|
191
|
+
makes the system compositional: walking the parent chain
|
|
192
|
+
reconstructs the whole history.
|
|
193
|
+
|
|
194
|
+
Construction goes through `Provenance.from_engine(engine=...,
|
|
195
|
+
parameters=..., inputs=..., parent=...)` which auto-fills the two
|
|
196
|
+
version fields and the timestamp so wrappers don't have to think
|
|
197
|
+
about them. The dataclass is frozen — mutating an attached
|
|
198
|
+
provenance would corrupt the audit trail; use `.replace(**changes)`
|
|
199
|
+
to derive an amended copy.
|
|
200
|
+
|
|
201
|
+
Traversal helpers: `walk()` yields self then ancestors newest-first;
|
|
202
|
+
`chain()` returns the same list oldest-first (suitable for printing
|
|
203
|
+
as a left-to-right pipeline); `depth` is the step count.
|
|
204
|
+
|
|
205
|
+
Serialisation: `to_dict()` / `from_dict()` give a stable plain-dict
|
|
206
|
+
shape with parents nested recursively; `to_json()` / `from_json()`
|
|
207
|
+
are JSON convenience wrappers. The on-disk shape is part of the
|
|
208
|
+
stability commitment.
|
|
209
|
+
|
|
210
|
+
The new `metadata_keys.PROVENANCE = "provenance"` constant is the
|
|
211
|
+
documented key; `ProteinMetadata` TypedDict declares it. The
|
|
212
|
+
intended use is `protein.metadata[mk.PROVENANCE] = prov`.
|
|
213
|
+
**Wrappers are NOT updated in this commit** — that's deliberately
|
|
214
|
+
separate work. The existing ad-hoc `metadata["engine"]` keys
|
|
215
|
+
continue to work for the 1.x series; engines opt into the
|
|
216
|
+
`Provenance` system gradually.
|
|
217
|
+
|
|
218
|
+
*NOT persisted through PDB / mmCIF writers* — those preserve only
|
|
219
|
+
the six documented IO header keys (per the mmCIF audit in
|
|
220
|
+
`c3a012e`). Provenance is an in-memory concept; users wanting
|
|
221
|
+
persistence serialise via `to_json` to a sidecar file. This is a
|
|
222
|
+
documented limitation, not a bug. A future "molforge bundle"
|
|
223
|
+
format could carry provenance alongside structure data; out of
|
|
224
|
+
scope here.
|
|
225
|
+
|
|
226
|
+
33 new tests in `tests/unit/core/test_provenance.py` covering
|
|
227
|
+
construction (minimal, autofill, defensive copies, parent),
|
|
228
|
+
strict JSON-input validation, immutability (FrozenInstanceError +
|
|
229
|
+
`.replace`), traversal (walk / chain / depth), serialisation
|
|
230
|
+
(dict shape, JSON round-trip, missing-engine error, forward-
|
|
231
|
+
compatible deserialisation of older shapes), equality, and the
|
|
232
|
+
metadata-key integration. Module coverage 96.8%; the residual
|
|
233
|
+
two lines are a defensive `_molforge_version` exception fallback.
|
|
234
|
+
|
|
235
|
+
- **mmCIF writer round-trip audit and fidelity fixes.** A systematic
|
|
236
|
+
audit of `write_cif_string` against every PDB fixture in the repo
|
|
237
|
+
surfaced five concrete fidelity bugs in the pre-audit writer; all
|
|
238
|
+
five are now fixed.
|
|
239
|
+
1. **`model_id == 0` was clobbered to 1.** The old writer used
|
|
240
|
+
`int(model_id) or 1`, which turned every single-model PDB's
|
|
241
|
+
`model_id=0` (the `read_pdb` convention for files without
|
|
242
|
+
MODEL records) into 1 on write. The reader's matching
|
|
243
|
+
`or 1` default reinforced the change — every PDB → CIF
|
|
244
|
+
round-trip silently lost the convention. Writer now emits
|
|
245
|
+
the value verbatim; reader's default flipped from 1 to 0.
|
|
246
|
+
*Affected every PDB fixture (19/19) before the fix.*
|
|
247
|
+
2. **Partial / non-integer charges were truncated to int.** The
|
|
248
|
+
old writer emitted `f"{int(charge):d}"`, turning typical
|
|
249
|
+
PDBQT / PQR partial charges like `-0.297` into `0` and
|
|
250
|
+
`-1.5` into `-1`. Writer now emits `f"{charge:.4f}"` (4
|
|
251
|
+
decimal places preserves enough precision for typical
|
|
252
|
+
force-field partial charges); zero still emits the `?`
|
|
253
|
+
sentinel so "no charge information" round-trips cleanly.
|
|
254
|
+
3. **`metadata['classification']` and `metadata['deposition_date']`
|
|
255
|
+
not emitted at all.** Both PDB HEADER fields were captured
|
|
256
|
+
by `read_pdb` and dropped by `write_cif`. Writer now emits
|
|
257
|
+
`_struct_keywords.text` for classification and
|
|
258
|
+
`_pdbx_database_status.recvd_initial_deposition_date` for
|
|
259
|
+
the deposition date; reader picks them up symmetrically.
|
|
260
|
+
4. **`_entry.id` and `data_<id>` block name could disagree.**
|
|
261
|
+
The old writer used `protein.name` for the block name but
|
|
262
|
+
`metadata[pdb_id]` for `_entry.id`. When those differed, the
|
|
263
|
+
reader's `_entry.id` won and silently rewrote both fields on
|
|
264
|
+
round-trip (e.g. dipeptide.pdb's `name='dipeptide'` became
|
|
265
|
+
`'TEST'` because the HEADER's PDB id was `TEST`). The fix
|
|
266
|
+
uses one chosen identifier — preferring `metadata[pdb_id]`,
|
|
267
|
+
falling back to `protein.name`, then `"molforge"` — for both.
|
|
268
|
+
Identifiers with embedded whitespace (which `read_pdb`
|
|
269
|
+
tolerates from malformed HEADER lines) are now quoted in
|
|
270
|
+
`_entry.id` even though the block name has to substitute
|
|
271
|
+
underscores, so `pdb_id` whitespace survives round-trip.
|
|
272
|
+
When no `pdb_id` exists at all, the writer emits the
|
|
273
|
+
`_entry.id .` mmCIF sentinel and the reader knows to leave
|
|
274
|
+
`metadata[pdb_id]` absent rather than manufacturing one from
|
|
275
|
+
the block name.
|
|
276
|
+
5. **`serial == 0` was clobbered to `i+1`.** Latent twin of #1;
|
|
277
|
+
no fixture triggered it but the bug was there. Same fix
|
|
278
|
+
pattern: only synthesize a default when `serial <= 0`.
|
|
279
|
+
38 new tests in `tests/unit/io/test_mmcif.py` codify each fix as
|
|
280
|
+
a regression guard. The `TestFixtureSweep` parametrized test
|
|
281
|
+
iterates every PDB fixture in the repo and asserts that
|
|
282
|
+
coordinates, residue/chain/atom-name fields, residue_id, model_id,
|
|
283
|
+
serial, insertion_code, altloc, record_type, entity_type, and the
|
|
284
|
+
six tracked metadata keys all survive a PDB → CIF → in-memory
|
|
285
|
+
round-trip. Three classes — `TestEntryIdAndBlockNameConsistency`,
|
|
286
|
+
`TestAltlocRoundTrip`, and `TestFixtureSweep` — explicitly
|
|
287
|
+
document the two structural mmCIF limitations: (a) `Protein.name`
|
|
288
|
+
is recovered from `metadata[pdb_id]` on round-trip because mmCIF
|
|
289
|
+
carries only one identifier slot, and (b) altloc round-trip
|
|
290
|
+
requires the caller to pass `altloc="all"` (the default strategy
|
|
291
|
+
collapses to highest-occupancy and drops the label). Module
|
|
292
|
+
coverage at 82.9%; the residual misses are defensive error
|
|
293
|
+
branches in the tokenizer and reader.
|
|
294
|
+
- **Trajectory I/O: `read_trajectory`, `iter_trajectory`,
|
|
295
|
+
`write_trajectory`.** `Trajectory` was previously in-memory only —
|
|
296
|
+
any real MD trajectory bigger than RAM had no way into molforge.
|
|
297
|
+
This commit adds binary-trajectory I/O wrapping mdtraj, exposed
|
|
298
|
+
from `molforge.io`. Supported formats are everything mdtraj
|
|
299
|
+
handles: `.xtc` (GROMACS lossy, the common case), `.trr` (GROMACS
|
|
300
|
+
lossless), `.dcd` (CHARMM / NAMD / OpenMM), `.nc` / `.netcdf`
|
|
301
|
+
(AMBER), `.h5` / `.h5md` (HDF5-based), and multi-MODEL PDB. Three
|
|
302
|
+
functions: `read_trajectory(path, topology=..., stride=1,
|
|
303
|
+
atom_indices=None)` loads a whole file into a
|
|
304
|
+
`molforge.md.Trajectory` (use when it fits in memory);
|
|
305
|
+
`iter_trajectory(path, topology=..., chunk_size=100, stride=1,
|
|
306
|
+
atom_indices=None)` yields chunks of frames as `Trajectory` objects
|
|
307
|
+
with bounded memory; `write_trajectory(trajectory, path)` writes
|
|
308
|
+
out, format inferred from the extension. Coordinates are converted
|
|
309
|
+
nm ↔ Å automatically (molforge convention is Å throughout; mdtraj's
|
|
310
|
+
is nm), times in picoseconds either side. The `topology` argument
|
|
311
|
+
is required for binary formats that don't embed it (XTC, TRR, DCD,
|
|
312
|
+
NetCDF); it accepts either a `molforge.core.Protein` (reused
|
|
313
|
+
directly — no PDB round-trip when the caller passes a Protein in)
|
|
314
|
+
or a path to a PDB. PDB-format trajectories can pass
|
|
315
|
+
`topology=None`. The `atom_indices` parameter slices both the
|
|
316
|
+
coordinate array AND the returned topology, so callers analyzing
|
|
317
|
+
a subset (e.g. backbone atoms only) get a self-consistent
|
|
318
|
+
Trajectory rather than coords-and-topology mismatched. Lazy mdtraj
|
|
319
|
+
import: importing `molforge.io` does not import mdtraj; only the
|
|
320
|
+
trajectory functions do, and they raise
|
|
321
|
+
`MDEngineNotInstalledError` with install instructions when mdtraj
|
|
322
|
+
is absent. NOT wired into `load()` / `save()`: trajectories return
|
|
323
|
+
a different type than the dispatcher's `Protein` / `list[Protein]`
|
|
324
|
+
contract, and the topology argument is something the dispatcher
|
|
325
|
+
has no way to supply. Kept as dedicated entry points. 21 new tests
|
|
326
|
+
in `tests/unit/io/test_trajectory.py` covering reading from PDB,
|
|
327
|
+
the Å unit conversion, Protein-topology reuse identity, path-string
|
|
328
|
+
topology, no-topology PDB, atom-indices subset (including the
|
|
329
|
+
metadata-preserving Protein-slice path), stride, time-array
|
|
330
|
+
carry-through, the "source: mdtraj" metadata marker, the streaming
|
|
331
|
+
chunk count (3+3+3+1 from 10 frames), chunk validation, the
|
|
332
|
+
stride-plus-chunking compose case, the DCD / XTC / PDB write paths
|
|
333
|
+
(XTC round-trip with the documented 0.001-nm precision), and the
|
|
334
|
+
missing-mdtraj error path; mdtraj-using tests are
|
|
335
|
+
`pytest.importorskip` gated. The new module is at high coverage
|
|
336
|
+
(the few uncovered branches are unreachable error-handling
|
|
337
|
+
fallbacks). `pdbfixer.*` was added to mypy's missing-stubs override
|
|
338
|
+
list when `molforge.prep` shipped; no new mypy override is needed
|
|
339
|
+
here since mdtraj is already there.
|
|
340
|
+
- **New subpackage: `molforge.prep` for MD-system preparation.** A
|
|
341
|
+
raw PDB from AlphaFold, RoseTTAFold, the RCSB, or a docking engine
|
|
342
|
+
almost always needs the same clean-up before MD: drop
|
|
343
|
+
crystallographic clutter (waters, buffer salts, sometimes the
|
|
344
|
+
ligand), rebuild missing heavy atoms, cap free termini with ACE /
|
|
345
|
+
NME, and add explicit hydrogens at the right pH. The new
|
|
346
|
+
`molforge.prep` subpackage exposes one composable function per
|
|
347
|
+
step plus a convenience pipeline:
|
|
348
|
+
- `remove_heterogens(protein)` — pure-Python residue-name filter.
|
|
349
|
+
By default drops waters, ions, ligands, and everything outside
|
|
350
|
+
the 20 canonical amino acids + standard nucleotides. `keep_water`
|
|
351
|
+
/ `keep_ions` / `keep_ligands` toggles plus an explicit `keep`
|
|
352
|
+
allow-list for cofactors. Recognises multiple water aliases
|
|
353
|
+
(HOH, WAT, H2O, SOL, TIP*) and the common monatomic ions.
|
|
354
|
+
- `fix_missing_atoms(protein)` — wraps PDBFixer's rotamer-library
|
|
355
|
+
rebuild for incomplete side chains. `fix_missing_residues=False`
|
|
356
|
+
by default (de-novo loop modelling is risky);
|
|
357
|
+
`replace_nonstandard=True` by default (MSE → MET, etc.).
|
|
358
|
+
- `add_caps(protein)` — wraps PDBFixer to add ACE / NME caps at
|
|
359
|
+
free termini of every protein chain. Multi-chain aware;
|
|
360
|
+
non-protein chains (ligands, DNA) skipped. Either cap can be
|
|
361
|
+
disabled with an empty string.
|
|
362
|
+
- `add_hydrogens(protein, pH=7.4)` — wraps OpenMM
|
|
363
|
+
`Modeller.addHydrogens` for pH-aware protonation. Idempotent on
|
|
364
|
+
already-protonated input. The `force_field` kwarg accepts both
|
|
365
|
+
registered aliases (`"amber14"`, `"charmm36"`) and bare XML
|
|
366
|
+
filenames.
|
|
367
|
+
- `prepare_for_md(protein)` — convenience entry point that chains
|
|
368
|
+
the four steps with sensible defaults for an
|
|
369
|
+
AlphaFold-PDB-to-OpenMM workflow. Each step's options are
|
|
370
|
+
forwarded; individual steps can be turned off
|
|
371
|
+
(`add_caps_to_termini=False`, `add_explicit_hydrogens=False`).
|
|
372
|
+
|
|
373
|
+
## [v0.3.0] 2026-06-22
|
|
374
|
+
|
|
10
375
|
### Added
|
|
11
376
|
- **`molforge.io.read_pqr` / `write_pqr` are implemented.** PQR
|
|
12
377
|
(PDB2PQR / APBS) was a committed import path but a
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: molforge
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A unified Python library for structural bioinformatics, MD, protein engineering, and ML.
|
|
5
5
|
Project-URL: Homepage, https://github.com/DoctorDean/molforge
|
|
6
6
|
Project-URL: Documentation, https://doctordean.github.io/molforge/
|
|
@@ -50,6 +50,7 @@ Requires-Dist: biopython>=1.83; extra == 'all'
|
|
|
50
50
|
Requires-Dist: biotite>=0.40; extra == 'all'
|
|
51
51
|
Requires-Dist: mdtraj>=1.10; extra == 'all'
|
|
52
52
|
Requires-Dist: openmm>=8.1; (platform_system != 'Windows') and extra == 'all'
|
|
53
|
+
Requires-Dist: pdbfixer>=1.9; (platform_system != 'Windows') and extra == 'all'
|
|
53
54
|
Requires-Dist: rdkit>=2023.9; extra == 'all'
|
|
54
55
|
Requires-Dist: scipy>=1.11; extra == 'all'
|
|
55
56
|
Requires-Dist: torch>=2.1; extra == 'all'
|
|
@@ -83,6 +84,9 @@ Requires-Dist: openmm>=8.1; (platform_system != 'Windows') and extra == 'md'
|
|
|
83
84
|
Provides-Extra: ml
|
|
84
85
|
Requires-Dist: torch>=2.1; extra == 'ml'
|
|
85
86
|
Requires-Dist: transformers>=4.40; extra == 'ml'
|
|
87
|
+
Provides-Extra: prep
|
|
88
|
+
Requires-Dist: openmm>=8.1; (platform_system != 'Windows') and extra == 'prep'
|
|
89
|
+
Requires-Dist: pdbfixer>=1.9; (platform_system != 'Windows') and extra == 'prep'
|
|
86
90
|
Provides-Extra: sequence
|
|
87
91
|
Requires-Dist: biopython>=1.83; extra == 'sequence'
|
|
88
92
|
Provides-Extra: structure
|
|
@@ -146,6 +150,9 @@ pip install "molforge[ml]"
|
|
|
146
150
|
# with MD support (openmm, mdtraj)
|
|
147
151
|
pip install "molforge[md]"
|
|
148
152
|
|
|
153
|
+
# with structure prep for MD (pdbfixer + openmm)
|
|
154
|
+
pip install "molforge[prep]"
|
|
155
|
+
|
|
149
156
|
# with docking (rdkit for small molecules)
|
|
150
157
|
pip install "molforge[docking]"
|
|
151
158
|
|
|
@@ -221,7 +228,7 @@ molforge/
|
|
|
221
228
|
│ ├── plugins/ # Plugin registry and entry-point discovery
|
|
222
229
|
│ ├── metrics/ # TM-score, lDDT, GDT-TS, docking metrics
|
|
223
230
|
│ └── wrappers/ # Thin interfaces to external engines
|
|
224
|
-
│ ├── folding/ # AlphaFold, ESMFold, Boltz,
|
|
231
|
+
│ ├── folding/ # AlphaFold, ESMFold, Boltz, RoseTTAFold
|
|
225
232
|
│ ├── docking/ # AutoDock Vina, DiffDock
|
|
226
233
|
│ └── md/ # OpenMM, GROMACS
|
|
227
234
|
├── tests/ # pytest suite (909 passing + skips)
|
|
@@ -53,6 +53,9 @@ pip install "molforge[ml]"
|
|
|
53
53
|
# with MD support (openmm, mdtraj)
|
|
54
54
|
pip install "molforge[md]"
|
|
55
55
|
|
|
56
|
+
# with structure prep for MD (pdbfixer + openmm)
|
|
57
|
+
pip install "molforge[prep]"
|
|
58
|
+
|
|
56
59
|
# with docking (rdkit for small molecules)
|
|
57
60
|
pip install "molforge[docking]"
|
|
58
61
|
|
|
@@ -128,7 +131,7 @@ molforge/
|
|
|
128
131
|
│ ├── plugins/ # Plugin registry and entry-point discovery
|
|
129
132
|
│ ├── metrics/ # TM-score, lDDT, GDT-TS, docking metrics
|
|
130
133
|
│ └── wrappers/ # Thin interfaces to external engines
|
|
131
|
-
│ ├── folding/ # AlphaFold, ESMFold, Boltz,
|
|
134
|
+
│ ├── folding/ # AlphaFold, ESMFold, Boltz, RoseTTAFold
|
|
132
135
|
│ ├── docking/ # AutoDock Vina, DiffDock
|
|
133
136
|
│ └── md/ # OpenMM, GROMACS
|
|
134
137
|
├── tests/ # pytest suite (909 passing + skips)
|
|
@@ -58,6 +58,10 @@ md = [
|
|
|
58
58
|
"mdtraj>=1.10",
|
|
59
59
|
"openmm>=8.1; platform_system != 'Windows'",
|
|
60
60
|
]
|
|
61
|
+
prep = [
|
|
62
|
+
"openmm>=8.1; platform_system != 'Windows'",
|
|
63
|
+
"pdbfixer>=1.9; platform_system != 'Windows'",
|
|
64
|
+
]
|
|
61
65
|
docking = [
|
|
62
66
|
"rdkit>=2023.9",
|
|
63
67
|
]
|
|
@@ -70,7 +74,7 @@ io = [
|
|
|
70
74
|
"biotite>=0.40",
|
|
71
75
|
]
|
|
72
76
|
all = [
|
|
73
|
-
"molforge[structure,sequence,md,docking,ml,io]",
|
|
77
|
+
"molforge[structure,sequence,md,prep,docking,ml,io]",
|
|
74
78
|
]
|
|
75
79
|
dev = [
|
|
76
80
|
"pytest>=7.4",
|
|
@@ -229,6 +233,7 @@ module = [
|
|
|
229
233
|
"mdtraj.*",
|
|
230
234
|
"meeko.*",
|
|
231
235
|
"openmm.*",
|
|
236
|
+
"pdbfixer.*",
|
|
232
237
|
"rdkit.*",
|
|
233
238
|
"torch.*",
|
|
234
239
|
"transformers.*",
|
|
@@ -37,6 +37,7 @@ from molforge.core.constants import (
|
|
|
37
37
|
)
|
|
38
38
|
from molforge.core.metadata_keys import ProteinMetadata
|
|
39
39
|
from molforge.core.protein import Protein
|
|
40
|
+
from molforge.core.provenance import Provenance
|
|
40
41
|
from molforge.core.residue import Residue
|
|
41
42
|
|
|
42
43
|
__all__ = [ # noqa: RUF022 — grouped by concept, not alphabetical
|
|
@@ -56,6 +57,9 @@ __all__ = [ # noqa: RUF022 — grouped by concept, not alphabetical
|
|
|
56
57
|
# Metadata vocabulary (string constants live in
|
|
57
58
|
# molforge.core.metadata_keys; the TypedDict is re-exported here)
|
|
58
59
|
"ProteinMetadata",
|
|
60
|
+
# Provenance — first-class "what produced this output" record;
|
|
61
|
+
# stored under metadata[PROVENANCE] (see metadata_keys.PROVENANCE).
|
|
62
|
+
"Provenance",
|
|
59
63
|
# Constants & helpers
|
|
60
64
|
"THREE_TO_ONE",
|
|
61
65
|
"ONE_TO_THREE",
|
|
@@ -64,6 +64,23 @@ EXPERIMENTAL_METHOD = "experimental_method"
|
|
|
64
64
|
RESOLUTION = "resolution"
|
|
65
65
|
"""Resolution in Angstrom (float). Absent for non-diffraction structures."""
|
|
66
66
|
|
|
67
|
+
# ----------------------------------------------------------------------
|
|
68
|
+
# Provenance
|
|
69
|
+
# ----------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
PROVENANCE = "provenance"
|
|
72
|
+
"""First-class provenance record (:class:`molforge.core.Provenance`).
|
|
73
|
+
|
|
74
|
+
The canonical key for "what produced this output." Carries engine
|
|
75
|
+
name, version, parameters, inputs, and a recursive parent pointer to
|
|
76
|
+
the upstream step's provenance. See :mod:`molforge.core.provenance`
|
|
77
|
+
for construction helpers and the data shape.
|
|
78
|
+
|
|
79
|
+
This is the documented replacement for the older ad-hoc
|
|
80
|
+
``metadata["engine"]`` / ``metadata["source_args"]`` keys. Both
|
|
81
|
+
continue to work for backwards compatibility; new code should write a
|
|
82
|
+
:class:`Provenance` to this key instead."""
|
|
83
|
+
|
|
67
84
|
# ----------------------------------------------------------------------
|
|
68
85
|
# Uniform folding-engine keys (every folding wrapper sets these)
|
|
69
86
|
# ----------------------------------------------------------------------
|
|
@@ -166,6 +183,13 @@ class ProteinMetadata(TypedDict, total=False):
|
|
|
166
183
|
experimental_method: str
|
|
167
184
|
resolution: float
|
|
168
185
|
|
|
186
|
+
# Provenance — first-class record of what produced this output.
|
|
187
|
+
# The value type is molforge.core.Provenance; declared here as
|
|
188
|
+
# ``Any`` to avoid a runtime circular import (the dataclass lives
|
|
189
|
+
# in molforge.core.provenance which imports nothing from this
|
|
190
|
+
# module).
|
|
191
|
+
provenance: Any
|
|
192
|
+
|
|
169
193
|
# Uniform folding-engine keys.
|
|
170
194
|
engine: str
|
|
171
195
|
source_sequence: str
|
|
@@ -203,6 +227,7 @@ DOCUMENTED_KEYS: frozenset[str] = frozenset(
|
|
|
203
227
|
DEPOSITION_DATE,
|
|
204
228
|
EXPERIMENTAL_METHOD,
|
|
205
229
|
RESOLUTION,
|
|
230
|
+
PROVENANCE,
|
|
206
231
|
ENGINE,
|
|
207
232
|
SOURCE_SEQUENCE,
|
|
208
233
|
CONFIDENCE_PER_RESIDUE,
|
|
@@ -240,6 +265,8 @@ __all__ = [
|
|
|
240
265
|
"DEPOSITION_DATE",
|
|
241
266
|
"EXPERIMENTAL_METHOD",
|
|
242
267
|
"RESOLUTION",
|
|
268
|
+
# Provenance
|
|
269
|
+
"PROVENANCE",
|
|
243
270
|
# Uniform folding-engine keys
|
|
244
271
|
"ENGINE",
|
|
245
272
|
"SOURCE_SEQUENCE",
|