molforge 0.2.0__tar.gz → 0.3.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.2.0 → molforge-0.3.0}/CHANGELOG.md +398 -159
- {molforge-0.2.0 → molforge-0.3.0}/PKG-INFO +4 -3
- {molforge-0.2.0 → molforge-0.3.0}/README.md +2 -1
- {molforge-0.2.0 → molforge-0.3.0}/pyproject.toml +1 -1
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/__init__.py +1 -1
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/io/__init__.py +16 -5
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/io/dispatch.py +16 -6
- molforge-0.3.0/src/molforge/io/mol2.py +361 -0
- molforge-0.3.0/src/molforge/io/pdbqt.py +265 -0
- molforge-0.3.0/src/molforge/io/pqr.py +232 -0
- molforge-0.3.0/src/molforge/io/sdf.py +309 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/docking/__init__.py +1 -1
- molforge-0.3.0/src/molforge/wrappers/docking/diffdock.py +372 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/docking/vina.py +11 -8
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/folding/__init__.py +0 -3
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/folding/_base.py +2 -2
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/md/__init__.py +3 -2
- molforge-0.3.0/src/molforge/wrappers/md/gromacs.py +671 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/md/openmm.py +52 -6
- molforge-0.3.0/tests/fixtures/pdb/ala_tripeptide_heavy.pdb +26 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/io/test_dispatch.py +30 -5
- molforge-0.3.0/tests/unit/io/test_mol2.py +353 -0
- molforge-0.3.0/tests/unit/io/test_pdbqt.py +244 -0
- molforge-0.3.0/tests/unit/io/test_pqr.py +205 -0
- molforge-0.3.0/tests/unit/io/test_sdf.py +256 -0
- molforge-0.3.0/tests/unit/wrappers/test_diffdock.py +308 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/wrappers/test_docking_base.py +0 -30
- molforge-0.3.0/tests/unit/wrappers/test_gromacs.py +588 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/wrappers/test_md_base.py +0 -45
- molforge-0.3.0/tests/unit/wrappers/test_openmm.py +235 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/wrappers/test_rfdiffusion.py +146 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/wrappers/test_rosettafold.py +0 -26
- molforge-0.2.0/src/molforge/io/mol2.py +0 -33
- molforge-0.2.0/src/molforge/io/pdbqt.py +0 -33
- molforge-0.2.0/src/molforge/io/pqr.py +0 -33
- molforge-0.2.0/src/molforge/io/sdf.py +0 -33
- molforge-0.2.0/src/molforge/wrappers/docking/diffdock.py +0 -54
- molforge-0.2.0/src/molforge/wrappers/folding/rosetta.py +0 -55
- molforge-0.2.0/src/molforge/wrappers/md/gromacs.py +0 -78
- molforge-0.2.0/tests/unit/wrappers/test_openmm.py +0 -157
- {molforge-0.2.0 → molforge-0.3.0}/.gitignore +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/LICENSE +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/data/README.md +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/notebooks/README.md +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/plugins/README.md +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/plugins/example_plugin/README.md +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/plugins/example_plugin/pyproject.toml +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/requirements/README.md +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/scripts/README.md +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/core/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/core/atom.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/core/atom_array.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/core/chain.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/core/constants.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/core/metadata_keys.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/core/protein.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/core/residue.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/docking/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/ensembles/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/ensembles/clustering.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/ensembles/consensus.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/ensembles/density.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/ensembles/geometry.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/ensembles/weighting.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/generative.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/io/fasta.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/io/mmcif.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/io/pdb.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/io/pdb_alphafold.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/md/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/metrics/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/metrics/dockq.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/metrics/gdt.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/metrics/lddt.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/metrics/tm.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/ml/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/ml/embeddings.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/ml/graph.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/ml/sequence_features.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/ml/structure_features.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/plugins/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/plugins/registry.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/py.typed +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/sequence/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/sequence/alignment.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/sequence/composition.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/sequence/matrices.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/sequence/mutations.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/structure/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/structure/contacts.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/structure/dihedrals.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/structure/dssp.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/structure/geometry.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/structure/rmsd.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/structure/sasa.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/structure/superposition.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/validation/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/validation/criteria.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/validation/orchestration.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/validation/verdict.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/docking/_base.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/docking/prep.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/folding/alphafold.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/folding/boltz.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/folding/esmfold.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/folding/rosettafold.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/generative/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/generative/proteinmpnn.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/generative/rfdiffusion.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/src/molforge/wrappers/md/_base.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/benchmarks/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/benchmarks/conftest.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/benchmarks/test_perf.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/conftest.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/cif/dipeptide.cif +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/fasta/.gitkeep +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/fasta/multiline_with_digits.fasta +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/fasta/simple.fasta +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/.gitkeep +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/alphafold_mock.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/dipeptide.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/helix.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/mini_beta_sheet.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/mini_complex_bad.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/mini_complex_good.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/mini_complex_native.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/mini_ensemble.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/mini_mixed.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/mini_with_ligand.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/multi_model.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/real_small_protein.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/real_with_altloc_sidechains.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/real_with_ligand_realistic.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/tripeptide.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/with_altloc.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/fixtures/pdb/with_insertion_code.pdb +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/integration/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/integration/test_fixtures.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/integration/test_real_fixtures.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/integration/test_smoke.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/core/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/core/test_atom_array.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/core/test_constants.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/core/test_core_smoke.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/core/test_hierarchy.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/core/test_metadata_keys.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/docking/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/docking/test_docking_smoke.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/ensembles/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/ensembles/conftest.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/ensembles/test_clustering.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/ensembles/test_consensus.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/ensembles/test_density.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/ensembles/test_geometry.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/ensembles/test_smoke.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/ensembles/test_weighting.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/io/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/io/test_alphafold.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/io/test_fasta.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/io/test_io_smoke.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/io/test_mmcif.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/io/test_pdb.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/md/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/metrics/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/metrics/test_dockq.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/metrics/test_gdt.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/metrics/test_lddt.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/metrics/test_tm.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/ml/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/ml/test_embeddings.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/ml/test_graph.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/ml/test_sequence_features.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/ml/test_structure_features.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/plugins/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/plugins/test_plugins_smoke.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/plugins/test_registry.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/sequence/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/sequence/test_alignment.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/sequence/test_composition.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/sequence/test_matrices.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/sequence/test_mutations.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/structure/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/structure/test_contacts.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/structure/test_dihedrals.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/structure/test_dssp.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/structure/test_geometry.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/structure/test_rmsd.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/structure/test_sasa.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/structure/test_structure_smoke.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/structure/test_superposition.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/test_typing.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/validation/test_criteria.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/validation/test_orchestration.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/validation/test_verdict.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/wrappers/__init__.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/wrappers/test_alphafold.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/wrappers/test_boltz.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/wrappers/test_esmfold.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/wrappers/test_folding_base.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/wrappers/test_prep.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/wrappers/test_proteinmpnn.py +0 -0
- {molforge-0.2.0 → molforge-0.3.0}/tests/unit/wrappers/test_vina.py +0 -0
|
@@ -7,6 +7,249 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
- **`molforge.io.read_pqr` / `write_pqr` are implemented.** PQR
|
|
12
|
+
(PDB2PQR / APBS) was a committed import path but a
|
|
13
|
+
`NotImplementedError` stub; it now parses and writes PQR files,
|
|
14
|
+
completing the format-I/O backlog (SDF, MOL2, PDBQT, PQR all real).
|
|
15
|
+
PQR is a PDB-like format that appends per-atom partial charge and
|
|
16
|
+
atomic radius as whitespace-separated trailing fields. Unlike PDB
|
|
17
|
+
or PDBQT, PQR is **not** strictly fixed-column past the
|
|
18
|
+
coordinates — different generators (PDB2PQR, AMBER, CHARMM, APBS)
|
|
19
|
+
emit different widths. The reader handles all of them by parsing
|
|
20
|
+
columns 1-54 as fixed (the atom record through coordinates,
|
|
21
|
+
PDB-compatible) and whitespace-splitting the remainder for charge
|
|
22
|
+
and radius. Charges land on `AtomArray.charge`; radii land on
|
|
23
|
+
`protein.metadata["radii"]` as a per-atom list (no native
|
|
24
|
+
`radius` field on `AtomArray` — electrostatics is a small enough
|
|
25
|
+
slice of the surface that this didn't warrant a core-type change).
|
|
26
|
+
The writer is the symmetric operation: it calls `write_pdb_string`,
|
|
27
|
+
truncates each atom line at column 54, then appends
|
|
28
|
+
`charge radius`. When no radii are recorded in metadata, a default
|
|
29
|
+
1.5 Å is used (a reasonable middle-of-the-road heavy-atom radius
|
|
30
|
+
that lets a charge-only Protein still be written as PQR). 19 new
|
|
31
|
+
tests in `tests/unit/io/test_pqr.py` covering the charge/radius
|
|
32
|
+
extractor (clean tokens, trailing garbage, defaults), reading from
|
|
33
|
+
string and from disk, the dispatcher routes, two variant-width
|
|
34
|
+
tails seen in real-world PDB2PQR / AMBER output, the full
|
|
35
|
+
round-trip (coordinates, charges, radii), and the default-radius
|
|
36
|
+
writer path; `pqr.py` is at 92.0% coverage.
|
|
37
|
+
`test_dispatch.py`: the two stub-format tests (load and save) now
|
|
38
|
+
monkeypatch a synthetic planned format rather than depending on
|
|
39
|
+
any real format being unimplemented — the dispatcher's
|
|
40
|
+
planned-readers fallback machinery still exists for future use, so
|
|
41
|
+
the tests are still meaningful. The `_PLANNED_READERS` dict in
|
|
42
|
+
`dispatch.py` is now empty.
|
|
43
|
+
- **`molforge.io.read_pdbqt` / `write_pdbqt` are implemented.** PDBQT
|
|
44
|
+
(AutoDock / Vina) was a committed import path but a
|
|
45
|
+
`NotImplementedError` stub; it now parses and writes PDBQT files.
|
|
46
|
+
PDBQT is a thin extension of PDB — columns 1-66 are PDB-compatible,
|
|
47
|
+
columns 71-76 hold the per-atom partial charge, and columns 78-79
|
|
48
|
+
hold the AutoDock atom type (`C`, `OA`, `HD`, `NA`, ...). The
|
|
49
|
+
reader reuses `molforge.io.read_pdb_string` for the heavy lifting
|
|
50
|
+
(atom-array construction, altloc handling, entity classification,
|
|
51
|
+
multi-MODEL parsing) and post-processes each atom line to pick up
|
|
52
|
+
the extra columns: charges are written to `AtomArray.charge`,
|
|
53
|
+
AutoDock types land on `protein.metadata["autodock_types"]` as a
|
|
54
|
+
per-atom list. `ROOT` / `BRANCH` / `TORSDOF` rotatable-bond markers
|
|
55
|
+
are read-tolerated (recognised and skipped — `AtomArray` doesn't
|
|
56
|
+
carry bond topology). The writer is the symmetric operation: it
|
|
57
|
+
calls `write_pdb_string`, then rewrites each `ATOM` / `HETATM` line
|
|
58
|
+
to append the charge and AutoDock-type columns; when no AutoDock
|
|
59
|
+
type is recorded in metadata, the element is used as a documented
|
|
60
|
+
best-effort fallback. Round-tripping preserves coordinates,
|
|
61
|
+
charges, and types. The Vina wrapper's pose parser is refactored to
|
|
62
|
+
go through `read_pdbqt_string` rather than its previous "truncate
|
|
63
|
+
every atom line to 66 columns and feed to the PDB reader" hack — so
|
|
64
|
+
per-atom charges now propagate to `Pose.ligand` instead of being
|
|
65
|
+
silently discarded. 23 new tests in `tests/unit/io/test_pdbqt.py`
|
|
66
|
+
covering the column extractors (charge and AutoDock type, including
|
|
67
|
+
the whitespace-split fallback), reading from string and from disk,
|
|
68
|
+
the dispatcher routes, the full round-trip (coordinates, charges,
|
|
69
|
+
types), the element-fallback writer path, multi-MODEL handling
|
|
70
|
+
(Vina pose output), and `ROOT` / `BRANCH` / `TORSDOF` tolerance;
|
|
71
|
+
`pdbqt.py` is at 93.8% coverage. The `test_dispatch.py` stub-format
|
|
72
|
+
tests are updated to use `.pqr` (the only remaining stub format).
|
|
73
|
+
- **`molforge.io.read_mol2` / `write_mol2` are implemented.** MOL2
|
|
74
|
+
(Tripos) was a committed import path but a `NotImplementedError`
|
|
75
|
+
stub; it now parses and writes Tripos MOL2 files. Like the SDF
|
|
76
|
+
reader, `read_mol2` is multi-molecule by default and returns
|
|
77
|
+
`list[Protein]` (the format supports multi-molecule files via
|
|
78
|
+
repeated `@<TRIPOS>MOLECULE` markers, common in docking output
|
|
79
|
+
libraries). The reader populates coordinates, elements (extracted
|
|
80
|
+
from the prefix of the Tripos atom type — `C.ar` → `C`, `N.am` →
|
|
81
|
+
`N`, two-letter `Cl`/`Br` preserved), atom names, per-atom partial
|
|
82
|
+
charges from the atom line's last column, and substructure info
|
|
83
|
+
(residue id / name from the MOL2 `subst_id` / `subst_name`
|
|
84
|
+
columns). Short atom lines (optional trailing columns omitted) and
|
|
85
|
+
non-conforming writers that emit `***` for the subst_id or a
|
|
86
|
+
non-numeric charge are tolerated with silent fallbacks rather
|
|
87
|
+
than crashing the whole molecule. Bond orders, ring information,
|
|
88
|
+
stereochemistry, and the `@<TRIPOS>SUBSTRUCTURE` /
|
|
89
|
+
`@<TRIPOS>CRYSIN` / `@<TRIPOS>UNITY` sections are intentionally
|
|
90
|
+
dropped — those need a chemistry toolkit. The writer emits a
|
|
91
|
+
minimal, spec-conformant MOL2 with an empty `@<TRIPOS>BOND` section
|
|
92
|
+
(some downstream tools error without the tag). The MOLECULE header
|
|
93
|
+
declares the atom count; a mismatch between that and the ATOM
|
|
94
|
+
section raises a clear error. `read_mol2` is wired into
|
|
95
|
+
`molforge.io.load`. 34 new tests in `tests/unit/io/test_mol2.py`
|
|
96
|
+
covering single/multi-molecule reading, Tripos atom-type element
|
|
97
|
+
extraction, two-letter elements, partial charges, optional-column
|
|
98
|
+
fallbacks, blank-line tolerance, every error path, dispatcher
|
|
99
|
+
integration, and the full round-trip; mol2.py is at 94.4% coverage
|
|
100
|
+
(the residual misses are unreachable defensive branches).
|
|
101
|
+
- **`molforge.io.read_sdf` / `write_sdf` are implemented.** SDF was a
|
|
102
|
+
committed import path but a `NotImplementedError` stub; it now
|
|
103
|
+
parses and writes V2000 SDF / MOL files. The reader is multi-
|
|
104
|
+
molecule by default, returning a `list[Protein]` (a single-molecule
|
|
105
|
+
`.mol` file still returns a one-element list, keeping the return
|
|
106
|
+
type uniform across callers). The atom block, title line, and the
|
|
107
|
+
``> <Name>`` / value property block all round-trip; properties land
|
|
108
|
+
on `Protein.metadata["properties"]`. The implementation uses no
|
|
109
|
+
chemistry toolkit — the V2000 atom block has a fixed positional
|
|
110
|
+
layout that's enough for everything molforge does downstream
|
|
111
|
+
(coordinates, pose ranking, distance calculations). Bond orders,
|
|
112
|
+
aromaticity, and stereochemistry are intentionally dropped; users
|
|
113
|
+
who need them should call RDKit directly. V3000 files are detected
|
|
114
|
+
and raise a clear error pointing at conversion paths. `read_sdf` is
|
|
115
|
+
wired into `molforge.io.load`, so `load("foo.sdf")` works. The
|
|
116
|
+
DiffDock wrapper, which previously parsed SDF inline, now goes
|
|
117
|
+
through `molforge.io.sdf.read_sdf_string` — the inline
|
|
118
|
+
`_ligand_from_sdf` helper is removed (-1 duplicated parser).
|
|
119
|
+
`api-stability.md` is updated; MOL2, PDBQT, and PQR remain
|
|
120
|
+
tentative. 26 new tests in `tests/unit/io/test_sdf.py` covering
|
|
121
|
+
single/multi-molecule reading, the title and property block,
|
|
122
|
+
round-trip writing, dispatcher integration, and every error path;
|
|
123
|
+
the eight DiffDock tests that exercised the inline parser are
|
|
124
|
+
removed (now subsumed by the SDF tests).
|
|
125
|
+
- **`GROMACS` is now a real MD engine.** `GROMACS`
|
|
126
|
+
(`molforge.wrappers.md`) was a coherent stub whose `prepare` /
|
|
127
|
+
`minimize` / `run` all raised `NotImplementedError`; it is now
|
|
128
|
+
fully implemented. [GROMACS](https://www.gromacs.org/) is a
|
|
129
|
+
command-line program (`gmx`), not a Python library, so the wrapper
|
|
130
|
+
drives it as a subprocess. One `prepare` / `minimize` / `run` cycle
|
|
131
|
+
maps onto the standard GROMACS workflow: `prepare` runs
|
|
132
|
+
`pdb2gmx` → `editconf` → (optionally) `solvate`; `minimize` writes
|
|
133
|
+
a steepest-descent `.mdp`, then `grompp` → `mdrun`; `run` writes a
|
|
134
|
+
production `.mdp`, runs `grompp` → `mdrun`, then reads the frames
|
|
135
|
+
back with `trjconv` and per-frame energies with `gmx energy`. All
|
|
136
|
+
state for a simulation lives in one run directory whose path is
|
|
137
|
+
carried on `Simulation.engine_handle` (and mirrored in
|
|
138
|
+
`metadata["run_dir"]`), so `minimize` and `run` continue from
|
|
139
|
+
whatever `prepare` produced. Trajectory frames are read back by
|
|
140
|
+
asking GROMACS itself (`gmx trjconv`) to convert its binary `.xtc`
|
|
141
|
+
to a multi-model PDB, which molforge's own PDB reader then parses —
|
|
142
|
+
the wrapper deliberately takes no dependency on a third-party
|
|
143
|
+
binary-trajectory library. Three small fixed-layout parsers
|
|
144
|
+
(`.gro` coordinates, multi-model PDB, `.xvg` columns) handle the
|
|
145
|
+
GROMACS outputs directly. `gmx` is resolved lazily via
|
|
146
|
+
`shutil.which`, so construction never touches the filesystem; a
|
|
147
|
+
clear `MDEngineNotInstalledError` (pointing at OpenMM) is raised
|
|
148
|
+
when it is absent. A constructor flag covers the water model, box
|
|
149
|
+
margin/type, and a `verbose` pass-through. 36 tests (a new
|
|
150
|
+
`test_gromacs.py`), covering construction and validation, `gmx`
|
|
151
|
+
resolution, the three parsers and their error paths, and the full
|
|
152
|
+
`prepare` / `minimize` / `run` pipeline driven by a mocked
|
|
153
|
+
`subprocess.run` that writes the files each `gmx` step would
|
|
154
|
+
produce; the wrapper module is at 94% coverage (the residual
|
|
155
|
+
misses are defensive `except` branches for corrupt output GROMACS
|
|
156
|
+
would never actually emit). The 6 obsolete `TestGROMACSStub` tests
|
|
157
|
+
are removed.
|
|
158
|
+
- **`DiffDock` is now a real docking engine.** `DiffDock`
|
|
159
|
+
(`molforge.wrappers.docking`) was a coherent stub whose `dock()`
|
|
160
|
+
raised `NotImplementedError`; it is now fully implemented.
|
|
161
|
+
[DiffDock](https://github.com/gcorso/DiffDock) is a
|
|
162
|
+
diffusion-generative model for *blind* protein-ligand docking — it
|
|
163
|
+
needs no search box, sampling poses over the whole receptor and
|
|
164
|
+
ranking them with a learned confidence model. Like the
|
|
165
|
+
`RoseTTAFold` wrapper, DiffDock ships as a research repository
|
|
166
|
+
rather than a pip package, so the wrapper drives it as a
|
|
167
|
+
subprocess: it locates the cloned repo (`$DIFFDOCK_HOME` or an
|
|
168
|
+
explicit `repo_dir`), materializes the receptor to PDB, accepts the
|
|
169
|
+
ligand as a SMILES string or a path to an SDF/MOL2 file, runs
|
|
170
|
+
`python -m inference`, and parses the ranked
|
|
171
|
+
`rank{N}_confidence{C}.sdf` output into a `DockingResult`. DiffDock
|
|
172
|
+
reports a *confidence* (higher = better), the opposite of Vina's
|
|
173
|
+
affinity convention; the wrapper stores the raw value in
|
|
174
|
+
`Pose.metadata["confidence"]` and sets `Pose.score` to its
|
|
175
|
+
negation, so `score` ascending is best-first for every engine. SDF
|
|
176
|
+
poses are parsed by reading the V2000 atom block directly (molforge's
|
|
177
|
+
RDKit-backed SDF reader is still a stub, and the atom block —
|
|
178
|
+
3D coordinates plus element symbols — needs no chemistry toolkit).
|
|
179
|
+
A constructor flag covers `samples_per_complex`, `inference_steps`,
|
|
180
|
+
and `batch_size`. 30 tests (a new `test_diffdock.py`), covering
|
|
181
|
+
construction and validation, install resolution, SDF and
|
|
182
|
+
confidence-from-filename parsing, and the `_run_cli` subprocess seam
|
|
183
|
+
via a mocked `subprocess.run`; the wrapper module is at 100%
|
|
184
|
+
coverage. The 4 obsolete `TestDiffDockStub` tests are removed.
|
|
185
|
+
- **OpenMM wrapper test coverage raised from 24% to 95%.** The
|
|
186
|
+
OpenMM tests previously gated every real path behind
|
|
187
|
+
`skipif(openmm installed)` — so `prepare` / `minimize` / `run`
|
|
188
|
+
were exercised by *nothing*: when openmm was absent they couldn't
|
|
189
|
+
run, and when present the negative-path tests skipped. The file is
|
|
190
|
+
restructured into a dependency-free half (construction, the
|
|
191
|
+
force-field registry, the missing-dependency errors) and a new
|
|
192
|
+
`TestRealOpenMM` class that runs `prepare` / `minimize` / `run`
|
|
193
|
+
end to end against a real OpenMM install — system building,
|
|
194
|
+
hydrogen addition, the minimizer, the integration loop, trajectory
|
|
195
|
+
assembly, and argument validation. A new chemically complete
|
|
196
|
+
heavy-atom fixture, `tests/fixtures/pdb/ala_tripeptide_heavy.pdb`
|
|
197
|
+
(ALA-ALA-ALA with all standard heavy atoms plus the C-terminal
|
|
198
|
+
OXT), gives the force field something it can template. The
|
|
199
|
+
real-engine tests are deliberately *not* marked `slow` — the
|
|
200
|
+
tripeptide is tiny and a 20-step run is sub-second — so they run
|
|
201
|
+
in the normal suite wherever openmm is installed, and skip cleanly
|
|
202
|
+
(9 skips) where it isn't. A new CI job, `md-openmm`, installs the
|
|
203
|
+
`[md]` extra and runs the MD wrapper tests on every push so
|
|
204
|
+
`TestRealOpenMM` is actually exercised.
|
|
205
|
+
- **RFdiffusion wrapper test coverage raised from 84% to 99%.** The
|
|
206
|
+
`_run_cli` subprocess seam of `wrappers.generative.rfdiffusion` —
|
|
207
|
+
previously untested — now has direct tests via a mocked
|
|
208
|
+
`subprocess.run` (no RFdiffusion or torch needed): command and
|
|
209
|
+
Hydra-arg assembly, the `design_*.pdb` output-parsing path, the
|
|
210
|
+
no-output `RuntimeError`, `CalledProcessError` → `RuntimeError`
|
|
211
|
+
translation, the public `generate()` entry point, and
|
|
212
|
+
`contigs` / `symmetry` pass-through. 5 new tests (16 → 21 in the
|
|
213
|
+
file). Mirrors the ProteinMPNN coverage work from the previous
|
|
214
|
+
cycle.
|
|
215
|
+
|
|
216
|
+
### Removed
|
|
217
|
+
- **BREAKING `molforge.wrappers.folding.Rosetta` removed.** The `Rosetta`
|
|
218
|
+
name was a placeholder from the `0.0.x` series whose meaning was
|
|
219
|
+
ambiguous — it could read as PyRosetta (the classical
|
|
220
|
+
sequence-design library) or RoseTTAFold (the deep-learning
|
|
221
|
+
model). The real wrapper now lives at `RoseTTAFold`. `Rosetta`
|
|
222
|
+
had been kept this cycle as a `DeprecationWarning`-emitting alias,
|
|
223
|
+
but since it never appeared in a tagged release, carrying it —
|
|
224
|
+
and the day-one deprecation it implies — into the 1.0 stable
|
|
225
|
+
surface added nothing. It is removed outright: import
|
|
226
|
+
`RoseTTAFold` instead. A PyRosetta wrapper, if ever added, would
|
|
227
|
+
be a separate class (`PyRosetta`) in its own module, since
|
|
228
|
+
PyRosetta's surface is far wider than the `FoldingEngine`
|
|
229
|
+
contract. The 3 alias tests are removed (folding-engine count
|
|
230
|
+
unchanged: ESMFold, AlphaFold, Boltz, RoseTTAFold).
|
|
231
|
+
|
|
232
|
+
### Fixed
|
|
233
|
+
+- **`OpenMM.prepare()` now adds missing hydrogens, so heavy-atom
|
|
234
|
+
structures are usable.** A force field needs explicit hydrogens,
|
|
235
|
+
but `prepare()` called `ForceField.createSystem()` directly on
|
|
236
|
+
whatever atoms the input had. Heavy-atom structures — the normal
|
|
237
|
+
output of every folding and docking engine, and what most PDB
|
|
238
|
+
files on disk contain — therefore failed with a cryptic
|
|
239
|
+
OpenMM "no template found for residue" error, making the wrapper
|
|
240
|
+
effectively unusable on exactly the structures molforge produces.
|
|
241
|
+
`prepare()` now runs `Modeller.addHydrogens()` before building the
|
|
242
|
+
system; the step is idempotent, so an already-protonated structure
|
|
243
|
+
is unaffected. Because adding hydrogens changes the atom count, the
|
|
244
|
+
molforge `Protein` attached to the returned `Simulation` is rebuilt
|
|
245
|
+
from the protonated structure, so its topology and the coordinate
|
|
246
|
+
array agree (previously a heavy-atom topology could be paired with
|
|
247
|
+
a protonated coordinate array). A new `add_hydrogens` constructor
|
|
248
|
+
flag (default `True`) lets callers who have pre-protonated their
|
|
249
|
+
structure opt out.
|
|
250
|
+
|
|
251
|
+
## [v0.2.0] 2026-05-26
|
|
252
|
+
|
|
10
253
|
### Added
|
|
11
254
|
- **ProteinMPNN wrapper test coverage raised from 69% to 96%.** The
|
|
12
255
|
two previously-untested seams of `wrappers.generative.proteinmpnn`
|
|
@@ -72,148 +315,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
72
315
|
still permitted but carry no cross-version stability guarantee. 15
|
|
73
316
|
new tests, including consistency checks that the parsers only emit
|
|
74
317
|
documented keys and that the TypedDict matches `DOCUMENTED_KEYS`.
|
|
75
|
-
|
|
76
|
-
### Fixed
|
|
77
|
-
- **`load_alphafold` now emits the uniform confidence metadata keys.**
|
|
78
|
-
`molforge.io.load_alphafold` previously wrote only AlphaFold-specific
|
|
79
|
-
keys (`plddt`, `plddt_per_residue`, `mean_plddt`, `source`), while
|
|
80
|
-
the AlphaFold *wrapper* wrote the cross-engine-uniform keys
|
|
81
|
-
(`confidence_per_atom`, `confidence_per_residue`, `mean_confidence`,
|
|
82
|
-
`engine`). Downstream code reading confidence uniformly across
|
|
83
|
-
engines silently missed AlphaFold structures loaded from disk.
|
|
84
|
-
`load_alphafold` now populates both sets (uniform keys preferred,
|
|
85
|
-
legacy keys retained for backward compatibility); the two carry
|
|
86
|
-
identical values. Surfaced by the API audit.
|
|
87
|
-
- **`GROMACS` and `DiffDock` are now coherent stubs.** Both are
|
|
88
|
-
exported (committed import paths) but unimplemented. Previously
|
|
89
|
-
they were *incoherent*: `GROMACS` didn't implement its `MDEngine`
|
|
90
|
-
abstract methods at all, so `GROMACS()` failed with a cryptic
|
|
91
|
-
"Can't instantiate abstract class" `TypeError` rather than a
|
|
92
|
-
meaningful message; both engines' methods raised a bare
|
|
93
|
-
`NotImplementedError` with no text. They are now coherent stubs —
|
|
94
|
-
instantiable, satisfying their respective engine ABCs
|
|
95
|
-
(`MDEngine` / `DockingEngine`), with every method raising
|
|
96
|
-
`NotImplementedError` carrying a clear message that points at the
|
|
97
|
-
working alternative (`OpenMM` / `Vina`) and the tracking issue.
|
|
98
|
-
10 new tests. Surfaced by the API audit.
|
|
99
|
-
- - **Lint drift from a Ruff version bump cleared; CI lint job green
|
|
100
|
-
again.** `.pre-commit-config.yaml` pinned `ruff-pre-commit` at
|
|
101
|
-
`v0.5.0`, but the `[dev]` extra installs `ruff>=0.5` unpinned, so
|
|
102
|
-
CI resolved a much newer Ruff (0.15.x) whose added rules flagged
|
|
103
|
-
33 pre-existing issues — meaning the CI `lint` job was effectively
|
|
104
|
-
red. All 33 are now resolved: a genuine dead variable in
|
|
105
|
-
`ensembles.clustering` removed, an unused `shutil` import dropped,
|
|
106
|
-
five `pytest.raises(match=...)` patterns with unescaped regex
|
|
107
|
-
metacharacters made explicit (raw strings / escaped dots), a
|
|
108
|
-
`zip()` given an explicit `strict=`, four nested `with` statements
|
|
109
|
-
collapsed, a `getattr()` call with a string literal in
|
|
110
|
-
`ensembles.weighting` replaced by a `cast`-backed direct attribute
|
|
111
|
-
access (dropping a now-misplaced `# noqa`), and a Ruff-version
|
|
112
|
-
formatting refresh applied across 24 files (cosmetic line-joining
|
|
113
|
-
only). Two intentional-notation cases
|
|
114
|
-
are configured rather than rewritten: `allowed-confusables`
|
|
115
|
-
permits `×`, `σ`, and `–` in docstrings (matrix dimensions, the
|
|
116
|
-
standard deviation, prose dashes), and `RUF022` is per-file-ignored
|
|
117
|
-
for the two modules whose `__all__` is deliberately grouped by
|
|
118
|
-
category with section comments. The `ruff` and `mypy` pre-commit
|
|
119
|
-
pins are bumped to the versions CI resolves, so the two stay in
|
|
120
|
-
lock-step and this drift cannot silently recur. No source-behaviour
|
|
121
|
-
or test-count change (918 pass + 11 skipped, unchanged).
|
|
122
|
-
|
|
123
|
-
### Changed
|
|
124
|
-
- **BREAKING: `cross_validate` now defaults to `on_error="raise"`.**
|
|
125
|
-
Previously `cross_validate` defaulted to `on_error="record"` —
|
|
126
|
-
exceptions raised by individual validators were silently caught,
|
|
127
|
-
recorded in verdict metadata, and the verdict marked
|
|
128
|
-
`passed=False`. The problem: a validator that throws on every
|
|
129
|
-
design (a misconfigured engine, a missing dependency, a bad
|
|
130
|
-
input) produced a full list of `passed=False` verdicts that
|
|
131
|
-
*looked* like a real result, hiding the bug. The new default
|
|
132
|
-
fails loud. Code that genuinely wants a batch to survive
|
|
133
|
-
individual validator failures must now pass `on_error="record"`
|
|
134
|
-
explicitly. Flagged and resolved by the API audit.
|
|
135
|
-
- **The entire `molforge` package is now `mypy --strict` clean.**
|
|
136
|
-
With `wrappers` and `plugins` brought up to strict, all 77 source
|
|
137
|
-
modules across every subpackage pass `mypy --strict` with zero
|
|
138
|
-
errors. The CI `typecheck` job is correspondingly simplified: the
|
|
139
|
-
previous two-step arrangement (a strict gate on the clean
|
|
140
|
-
subpackages plus a non-blocking informational full-tree run)
|
|
141
|
-
collapses to a single `mypy src` gate that fails the build on any
|
|
142
|
-
type error. The `tests/unit/test_typing.py` regression test is
|
|
143
|
-
likewise simplified to one whole-package check. 31 errors fixed in
|
|
144
|
-
this final tranche: 20 stale `# type: ignore` comments (made
|
|
145
|
-
redundant when the optional heavy dependencies were added to the
|
|
146
|
-
mypy `ignore_missing_imports` override), four deliberate engine-
|
|
147
|
-
method `# type: ignore[override]` annotations (the concrete
|
|
148
|
-
engine wrappers refine the permissive `**kwargs` signatures of
|
|
149
|
-
their `DockingEngine` / `MDEngine` / `GenerativeEngine` abstract
|
|
150
|
-
bases — an intentional, documented refinement that mypy's strict
|
|
151
|
-
Liskov check cannot model), `cast`s for the opaque
|
|
152
|
-
`Simulation.engine_handle` inside the OpenMM wrapper and for the
|
|
153
|
-
unstubbed-dependency return values, and `Vina.dock`'s receptor
|
|
154
|
-
narrowing switched from `hasattr` to `isinstance` (a more correct
|
|
155
|
-
check that mypy can also narrow on).
|
|
156
|
-
- **`molforge.ml` is now `mypy --strict` clean.** The ML subpackage
|
|
157
|
-
(sequence/structure featurization, protein-language-model
|
|
158
|
-
embeddings) joins the strict gate — eight strict-clean
|
|
159
|
-
subpackages in total, 51 source files. Six errors fixed: the four
|
|
160
|
-
numpy-widening `no-any-return`s in `embeddings.py` (resolved with
|
|
161
|
-
`cast`s), and two real type bugs in `structure_features.py` —
|
|
162
|
-
`pair_distances` and `pair_distance_features` declared
|
|
163
|
-
`atom_choice: str` but pass it to `distance_map`, which requires
|
|
164
|
-
the `Literal["ca","cb","heavy","all"]` the docstrings already
|
|
165
|
-
specify, and a coordinate feature array silently upcast to
|
|
166
|
-
float64 by a division. The `torch` and `transformers` (and
|
|
167
|
-
`colabfold`, `meeko`, `vina`) optional heavy dependencies, which
|
|
168
|
-
ship no type stubs, are added to the mypy `ignore_missing_imports`
|
|
169
|
-
override alongside the existing `Bio` / `biotite` / `mdtraj` /
|
|
170
|
-
`openmm` / `rdkit` entries. CI strict gate and the
|
|
171
|
-
`tests/unit/test_typing.py` regression test updated; only
|
|
172
|
-
`plugins` and `wrappers` remain outside the gate.
|
|
173
|
-
- **Six more subpackages are now `mypy --strict` clean.**
|
|
174
|
-
`molforge.io`, `molforge.sequence`, `molforge.structure`,
|
|
175
|
-
`molforge.metrics`, `molforge.ensembles`, and
|
|
176
|
-
`molforge.validation` now pass `mypy --strict` with zero errors,
|
|
177
|
-
joining `molforge.core` — seven strict-clean subpackages in total,
|
|
178
|
-
46 source files. The 12 errors fixed were mostly numpy operations
|
|
179
|
-
mypy widens to `Any` (resolved with explicit `cast`s that document
|
|
180
|
-
the known array dtype) and two stale `type: ignore` comments; two
|
|
181
|
-
were genuine annotation bugs — `_place_hydrogens` in `dssp.py` was
|
|
182
|
-
declared to return a single array but actually returns a
|
|
183
|
-
`(coords, mask)` tuple, and `_score` in `alignment.py` was
|
|
184
|
-
declared `NDArray[np.int_]` but builds an `int32` array (`np.int_`
|
|
185
|
-
is `int64` on 64-bit platforms). The CI strict gate now covers all
|
|
186
|
-
seven subpackages; the regression test
|
|
187
|
-
(`tests/unit/test_typing.py`, moved up from `tests/unit/core/` and
|
|
188
|
-
parametrized) checks each one in-suite. The remaining subpackages
|
|
189
|
-
(`ml`, `plugins`, `wrappers`) are still tracked by the
|
|
190
|
-
non-blocking informational `mypy src` CI step.
|
|
191
|
-
- **`molforge.core` is now `mypy --strict` clean, and CI enforces
|
|
192
|
-
it.** The `core` subpackage — the data model the rest of the
|
|
193
|
-
library is built on — now passes `mypy --strict` with zero
|
|
194
|
-
errors (fixed: two missing `NDArray` type arguments in
|
|
195
|
-
`AtomArray`, an `Any`-return in `Atom.coord`, and an untyped
|
|
196
|
-
`Chain.__iter__` that was suppressed with a `type: ignore`). The
|
|
197
|
-
CI `typecheck` job now runs `mypy --strict src/molforge/core/`
|
|
198
|
-
as a hard gate, with a separate non-blocking full-tree `mypy src`
|
|
199
|
-
step that keeps the remaining (out-of-`core`) type errors visible
|
|
200
|
-
while they're worked through. A new `slow`-marked regression test
|
|
201
|
-
(`tests/unit/core/test_typing.py`) runs the strict check in-suite
|
|
202
|
-
so a `core` type regression is caught locally too.
|
|
203
|
-
|
|
204
|
-
### Documented
|
|
205
|
-
- **`Simulation.engine_handle` contract clarified.** The attribute
|
|
206
|
-
type (`object | None`) is correct — it really is an opaque,
|
|
207
|
-
engine-specific handle — but the contract was under-specified.
|
|
208
|
-
The docstring now states explicitly that `engine_handle` is
|
|
209
|
-
engine-private (callers must not inspect it or set it), is **not
|
|
210
|
-
serialized** (it typically wraps unpicklable C-extension state;
|
|
211
|
-
persistence layers must drop it and let the engine wrapper
|
|
212
|
-
rebuild it on resume), and carries **no semver guarantee**. For
|
|
213
|
-
inspectable per-simulation data, `Simulation.metadata` is the
|
|
214
|
-
supported field. No code change. Flagged by the API audit.
|
|
215
|
-
|
|
216
|
-
### Added
|
|
217
318
|
- **RoseTTAFold All-Atom folding wrapper.** New file
|
|
218
319
|
`src/molforge/wrappers/folding/rosettafold.py` implements a real
|
|
219
320
|
wrapper around the Baker lab's RoseTTAFold-All-Atom (Krishna et
|
|
@@ -241,25 +342,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
241
342
|
prediction matching the rest of the folding wrappers; protein-
|
|
242
343
|
ligand and covalent-modification co-folding (RFAA's headline
|
|
243
344
|
capability) need a separate `predict_complex()` surface and
|
|
244
|
-
remain planned.
|
|
345
|
+
remain planned.
|
|
346
|
+
- 47 new tests (45 passing + 2 correctly skipped:
|
|
245
347
|
one for the torch tensor conversion when torch isn't installed,
|
|
246
348
|
one @slow end-to-end requiring `$RFAA_HOME`). Total test count:
|
|
247
349
|
830 → 875 passed + 11 skipped.
|
|
248
|
-
|
|
249
|
-
### Deprecated
|
|
250
|
-
- **`molforge.wrappers.folding.Rosetta` is now a deprecated alias
|
|
251
|
-
for `RoseTTAFold`.** The original `rosetta.py` placeholder was
|
|
252
|
-
ambiguous about whether it referred to PyRosetta (the Baker lab's
|
|
253
|
-
classical sequence-design library) or RoseTTAFold (the deep-
|
|
254
|
-
learning model). The new real wrapper lives at
|
|
255
|
-
`RoseTTAFold` for clarity. `Rosetta` is retained as a thin
|
|
256
|
-
subclass that emits `DeprecationWarning` on construction so
|
|
257
|
-
existing imports / isinstance checks keep working through the
|
|
258
|
-
next minor release. A PyRosetta wrapper, if added, would live in
|
|
259
|
-
a separate module (`pyrosetta.py`) since PyRosetta's surface is
|
|
260
|
-
much wider than the `FoldingEngine` contract.
|
|
261
|
-
|
|
262
|
-
### Added
|
|
263
350
|
- **Boltz / Boltz-2 folding wrapper.** Real implementation replacing
|
|
264
351
|
the `boltz.py` stub. Drives the `boltz predict` CLI via subprocess
|
|
265
352
|
against a temporary directory and parses the resulting mmCIF +
|
|
@@ -275,7 +362,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
275
362
|
metadata follows the uniform folding-engine convention
|
|
276
363
|
(`confidence_per_residue`, `confidence_per_atom`, `mean_confidence`)
|
|
277
364
|
and additionally surfaces Boltz-specific `ptm`, `iptm`, and
|
|
278
|
-
`confidence_score` from the JSON sidecar.
|
|
365
|
+
`confidence_score` from the JSON sidecar.
|
|
366
|
+
- 47 new tests (46 passing
|
|
279
367
|
+ 1 correctly skipped @slow end-to-end), structured as a series of
|
|
280
368
|
testable seams: construction, sequence validation, YAML input
|
|
281
369
|
construction, command-line assembly, environment setup, output
|
|
@@ -477,6 +565,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
477
565
|
the empty-entry-points fallthrough.
|
|
478
566
|
|
|
479
567
|
### Fixed
|
|
568
|
+
- **`load_alphafold` now emits the uniform confidence metadata keys.**
|
|
569
|
+
`molforge.io.load_alphafold` previously wrote only AlphaFold-specific
|
|
570
|
+
keys (`plddt`, `plddt_per_residue`, `mean_plddt`, `source`), while
|
|
571
|
+
the AlphaFold *wrapper* wrote the cross-engine-uniform keys
|
|
572
|
+
(`confidence_per_atom`, `confidence_per_residue`, `mean_confidence`,
|
|
573
|
+
`engine`). Downstream code reading confidence uniformly across
|
|
574
|
+
engines silently missed AlphaFold structures loaded from disk.
|
|
575
|
+
`load_alphafold` now populates both sets (uniform keys preferred,
|
|
576
|
+
legacy keys retained for backward compatibility); the two carry
|
|
577
|
+
identical values. Surfaced by the API audit.
|
|
578
|
+
- **`GROMACS` and `DiffDock` are now coherent stubs.** Both are
|
|
579
|
+
exported (committed import paths) but unimplemented. Previously
|
|
580
|
+
they were *incoherent*: `GROMACS` didn't implement its `MDEngine`
|
|
581
|
+
abstract methods at all, so `GROMACS()` failed with a cryptic
|
|
582
|
+
"Can't instantiate abstract class" `TypeError` rather than a
|
|
583
|
+
meaningful message; both engines' methods raised a bare
|
|
584
|
+
`NotImplementedError` with no text. They are now coherent stubs —
|
|
585
|
+
instantiable, satisfying their respective engine ABCs
|
|
586
|
+
(`MDEngine` / `DockingEngine`), with every method raising
|
|
587
|
+
`NotImplementedError` carrying a clear message that points at the
|
|
588
|
+
working alternative (`OpenMM` / `Vina`) and the tracking issue.
|
|
589
|
+
10 new tests. Surfaced by the API audit.
|
|
590
|
+
- - **Lint drift from a Ruff version bump cleared; CI lint job green
|
|
591
|
+
again.** `.pre-commit-config.yaml` pinned `ruff-pre-commit` at
|
|
592
|
+
`v0.5.0`, but the `[dev]` extra installs `ruff>=0.5` unpinned, so
|
|
593
|
+
CI resolved a much newer Ruff (0.15.x) whose added rules flagged
|
|
594
|
+
33 pre-existing issues — meaning the CI `lint` job was effectively
|
|
595
|
+
red. All 33 are now resolved: a genuine dead variable in
|
|
596
|
+
`ensembles.clustering` removed, an unused `shutil` import dropped,
|
|
597
|
+
five `pytest.raises(match=...)` patterns with unescaped regex
|
|
598
|
+
metacharacters made explicit (raw strings / escaped dots), a
|
|
599
|
+
`zip()` given an explicit `strict=`, four nested `with` statements
|
|
600
|
+
collapsed, a `getattr()` call with a string literal in
|
|
601
|
+
`ensembles.weighting` replaced by a `cast`-backed direct attribute
|
|
602
|
+
access (dropping a now-misplaced `# noqa`), and a Ruff-version
|
|
603
|
+
formatting refresh applied across 24 files (cosmetic line-joining
|
|
604
|
+
only). Two intentional-notation cases
|
|
605
|
+
are configured rather than rewritten: `allowed-confusables`
|
|
606
|
+
permits `×`, `σ`, and `–` in docstrings (matrix dimensions, the
|
|
607
|
+
standard deviation, prose dashes), and `RUF022` is per-file-ignored
|
|
608
|
+
for the two modules whose `__all__` is deliberately grouped by
|
|
609
|
+
category with section comments. The `ruff` and `mypy` pre-commit
|
|
610
|
+
pins are bumped to the versions CI resolves, so the two stay in
|
|
611
|
+
lock-step and this drift cannot silently recur. No source-behaviour
|
|
612
|
+
or test-count change (918 pass + 11 skipped, unchanged).
|
|
480
613
|
- **Docs notebooks no longer use symlinks.** The walkthrough and
|
|
481
614
|
example notebooks were previously symlinked from `docs/` into the
|
|
482
615
|
canonical `notebooks/` directory. Symlinks broke two things: (1)
|
|
@@ -499,6 +632,112 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
499
632
|
the actual public attributes. Discovered while writing ensemble
|
|
500
633
|
test fixtures.
|
|
501
634
|
|
|
635
|
+
### Changed
|
|
636
|
+
- **BREAKING: `cross_validate` now defaults to `on_error="raise"`.**
|
|
637
|
+
Previously `cross_validate` defaulted to `on_error="record"` —
|
|
638
|
+
exceptions raised by individual validators were silently caught,
|
|
639
|
+
recorded in verdict metadata, and the verdict marked
|
|
640
|
+
`passed=False`. The problem: a validator that throws on every
|
|
641
|
+
design (a misconfigured engine, a missing dependency, a bad
|
|
642
|
+
input) produced a full list of `passed=False` verdicts that
|
|
643
|
+
*looked* like a real result, hiding the bug. The new default
|
|
644
|
+
fails loud. Code that genuinely wants a batch to survive
|
|
645
|
+
individual validator failures must now pass `on_error="record"`
|
|
646
|
+
explicitly. Flagged and resolved by the API audit.
|
|
647
|
+
- **The entire `molforge` package is now `mypy --strict` clean.**
|
|
648
|
+
With `wrappers` and `plugins` brought up to strict, all 77 source
|
|
649
|
+
modules across every subpackage pass `mypy --strict` with zero
|
|
650
|
+
errors. The CI `typecheck` job is correspondingly simplified: the
|
|
651
|
+
previous two-step arrangement (a strict gate on the clean
|
|
652
|
+
subpackages plus a non-blocking informational full-tree run)
|
|
653
|
+
collapses to a single `mypy src` gate that fails the build on any
|
|
654
|
+
type error. The `tests/unit/test_typing.py` regression test is
|
|
655
|
+
likewise simplified to one whole-package check. 31 errors fixed in
|
|
656
|
+
this final tranche: 20 stale `# type: ignore` comments (made
|
|
657
|
+
redundant when the optional heavy dependencies were added to the
|
|
658
|
+
mypy `ignore_missing_imports` override), four deliberate engine-
|
|
659
|
+
method `# type: ignore[override]` annotations (the concrete
|
|
660
|
+
engine wrappers refine the permissive `**kwargs` signatures of
|
|
661
|
+
their `DockingEngine` / `MDEngine` / `GenerativeEngine` abstract
|
|
662
|
+
bases — an intentional, documented refinement that mypy's strict
|
|
663
|
+
Liskov check cannot model), `cast`s for the opaque
|
|
664
|
+
`Simulation.engine_handle` inside the OpenMM wrapper and for the
|
|
665
|
+
unstubbed-dependency return values, and `Vina.dock`'s receptor
|
|
666
|
+
narrowing switched from `hasattr` to `isinstance` (a more correct
|
|
667
|
+
check that mypy can also narrow on).
|
|
668
|
+
- **`molforge.ml` is now `mypy --strict` clean.** The ML subpackage
|
|
669
|
+
(sequence/structure featurization, protein-language-model
|
|
670
|
+
embeddings) joins the strict gate — eight strict-clean
|
|
671
|
+
subpackages in total, 51 source files. Six errors fixed: the four
|
|
672
|
+
numpy-widening `no-any-return`s in `embeddings.py` (resolved with
|
|
673
|
+
`cast`s), and two real type bugs in `structure_features.py` —
|
|
674
|
+
`pair_distances` and `pair_distance_features` declared
|
|
675
|
+
`atom_choice: str` but pass it to `distance_map`, which requires
|
|
676
|
+
the `Literal["ca","cb","heavy","all"]` the docstrings already
|
|
677
|
+
specify, and a coordinate feature array silently upcast to
|
|
678
|
+
float64 by a division. The `torch` and `transformers` (and
|
|
679
|
+
`colabfold`, `meeko`, `vina`) optional heavy dependencies, which
|
|
680
|
+
ship no type stubs, are added to the mypy `ignore_missing_imports`
|
|
681
|
+
override alongside the existing `Bio` / `biotite` / `mdtraj` /
|
|
682
|
+
`openmm` / `rdkit` entries. CI strict gate and the
|
|
683
|
+
`tests/unit/test_typing.py` regression test updated; only
|
|
684
|
+
`plugins` and `wrappers` remain outside the gate.
|
|
685
|
+
- **Six more subpackages are now `mypy --strict` clean.**
|
|
686
|
+
`molforge.io`, `molforge.sequence`, `molforge.structure`,
|
|
687
|
+
`molforge.metrics`, `molforge.ensembles`, and
|
|
688
|
+
`molforge.validation` now pass `mypy --strict` with zero errors,
|
|
689
|
+
joining `molforge.core` — seven strict-clean subpackages in total,
|
|
690
|
+
46 source files. The 12 errors fixed were mostly numpy operations
|
|
691
|
+
mypy widens to `Any` (resolved with explicit `cast`s that document
|
|
692
|
+
the known array dtype) and two stale `type: ignore` comments; two
|
|
693
|
+
were genuine annotation bugs — `_place_hydrogens` in `dssp.py` was
|
|
694
|
+
declared to return a single array but actually returns a
|
|
695
|
+
`(coords, mask)` tuple, and `_score` in `alignment.py` was
|
|
696
|
+
declared `NDArray[np.int_]` but builds an `int32` array (`np.int_`
|
|
697
|
+
is `int64` on 64-bit platforms). The CI strict gate now covers all
|
|
698
|
+
seven subpackages; the regression test
|
|
699
|
+
(`tests/unit/test_typing.py`, moved up from `tests/unit/core/` and
|
|
700
|
+
parametrized) checks each one in-suite. The remaining subpackages
|
|
701
|
+
(`ml`, `plugins`, `wrappers`) are still tracked by the
|
|
702
|
+
non-blocking informational `mypy src` CI step.
|
|
703
|
+
- **`molforge.core` is now `mypy --strict` clean, and CI enforces
|
|
704
|
+
it.** The `core` subpackage — the data model the rest of the
|
|
705
|
+
library is built on — now passes `mypy --strict` with zero
|
|
706
|
+
errors (fixed: two missing `NDArray` type arguments in
|
|
707
|
+
`AtomArray`, an `Any`-return in `Atom.coord`, and an untyped
|
|
708
|
+
`Chain.__iter__` that was suppressed with a `type: ignore`). The
|
|
709
|
+
CI `typecheck` job now runs `mypy --strict src/molforge/core/`
|
|
710
|
+
as a hard gate, with a separate non-blocking full-tree `mypy src`
|
|
711
|
+
step that keeps the remaining (out-of-`core`) type errors visible
|
|
712
|
+
while they're worked through. A new `slow`-marked regression test
|
|
713
|
+
(`tests/unit/core/test_typing.py`) runs the strict check in-suite
|
|
714
|
+
so a `core` type regression is caught locally too.
|
|
715
|
+
|
|
716
|
+
### Documented
|
|
717
|
+
- **`Simulation.engine_handle` contract clarified.** The attribute
|
|
718
|
+
type (`object | None`) is correct — it really is an opaque,
|
|
719
|
+
engine-specific handle — but the contract was under-specified.
|
|
720
|
+
The docstring now states explicitly that `engine_handle` is
|
|
721
|
+
engine-private (callers must not inspect it or set it), is **not
|
|
722
|
+
serialized** (it typically wraps unpicklable C-extension state;
|
|
723
|
+
persistence layers must drop it and let the engine wrapper
|
|
724
|
+
rebuild it on resume), and carries **no semver guarantee**. For
|
|
725
|
+
inspectable per-simulation data, `Simulation.metadata` is the
|
|
726
|
+
supported field. No code change. Flagged by the API audit.
|
|
727
|
+
|
|
728
|
+
### Deprecated
|
|
729
|
+
- **`molforge.wrappers.folding.Rosetta` is now a deprecated alias
|
|
730
|
+
for `RoseTTAFold`.** The original `rosetta.py` placeholder was
|
|
731
|
+
ambiguous about whether it referred to PyRosetta (the Baker lab's
|
|
732
|
+
classical sequence-design library) or RoseTTAFold (the deep-
|
|
733
|
+
learning model). The new real wrapper lives at
|
|
734
|
+
`RoseTTAFold` for clarity. `Rosetta` is retained as a thin
|
|
735
|
+
subclass that emits `DeprecationWarning` on construction so
|
|
736
|
+
existing imports / isinstance checks keep working through the
|
|
737
|
+
next minor release. A PyRosetta wrapper, if added, would live in
|
|
738
|
+
a separate module (`pyrosetta.py`) since PyRosetta's surface is
|
|
739
|
+
much wider than the `FoldingEngine` contract.
|
|
740
|
+
|
|
502
741
|
### Removed
|
|
503
742
|
- **`tests/unit/core/test_core_types.py`.** A pre-existing fossil
|
|
504
743
|
from before the view-based data-model refactor: it imported from
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: molforge
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.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/
|
|
@@ -43,7 +43,7 @@ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
|
43
43
|
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
44
44
|
Classifier: Typing :: Typed
|
|
45
45
|
Requires-Python: >=3.10
|
|
46
|
-
Requires-Dist: numpy>=
|
|
46
|
+
Requires-Dist: numpy>=2.2.6
|
|
47
47
|
Requires-Dist: typing-extensions>=4.7
|
|
48
48
|
Provides-Extra: all
|
|
49
49
|
Requires-Dist: biopython>=1.83; extra == 'all'
|
|
@@ -94,7 +94,8 @@ Description-Content-Type: text/markdown
|
|
|
94
94
|
# molforge
|
|
95
95
|
|
|
96
96
|
[](https://github.com/DoctorDean/molforge/actions/workflows/ci.yml)
|
|
97
|
-
[](https://pypi.org/project/molforge/)
|
|
97
|
+
[](https://pypi.org/project/molforge/)
|
|
98
|
+
[](https://pepy.tech/projects/molforge)
|
|
98
99
|
[](https://pypi.org/project/molforge/)
|
|
99
100
|
[](LICENSE)
|
|
100
101
|
[](https://github.com/astral-sh/ruff)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# molforge
|
|
2
2
|
|
|
3
3
|
[](https://github.com/DoctorDean/molforge/actions/workflows/ci.yml)
|
|
4
|
-
[](https://pypi.org/project/molforge/)
|
|
4
|
+
[](https://pypi.org/project/molforge/)
|
|
5
|
+
[](https://pepy.tech/projects/molforge)
|
|
5
6
|
[](https://pypi.org/project/molforge/)
|
|
6
7
|
[](LICENSE)
|
|
7
8
|
[](https://github.com/astral-sh/ruff)
|