diff --git a/biopandas/pdb/pandas_pdb.py b/biopandas/pdb/pandas_pdb.py index 6ed68cb..c4d2a00 100644 --- a/biopandas/pdb/pandas_pdb.py +++ b/biopandas/pdb/pandas_pdb.py @@ -24,7 +24,7 @@ from biopandas.constants import ATOMIC_MASSES -from .engines import amino3to1dict, pdb_df_columns, pdb_records +from .engines import amino3to1dict, pdb_records pd_version = LooseVersion(pd.__version__) @@ -823,9 +823,11 @@ def to_pdb(self, path, records=None, gz=False, append_newline=True): for idx in range(dfs[r][c].values.shape[0]): if len(dfs[r][c].values[idx]) > 8: dfs[r][c].values[idx] = str(dfs[r][c].values[idx]).strip() - if c in {"line_idx", "OUT"}: + if c in {"line_idx", "OUT", "model_id"}: pass - elif r in {"ATOM", "HETATM"} and c not in pdb_df_columns: + elif r in {"ATOM", "HETATM", "ANISOU"} and c not in { + col["id"] for col in pdb_records[r] + }: warn( "Column %s is not an expected column and" " will be skipped." % c diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6c0d62f..0e47d91 100755 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -11,6 +11,8 @@ The CHANGELOG for the current development version is available at - Fix: `PandasMol2.rmsd()` now normalizes by the number of atoms it actually compared. With the default `heavy_only=True`, the sum of squared deviations over the hydrogen-filtered atoms was divided by the total atom count including the discarded hydrogens, silently understating the result by a factor of `sqrt(n_total / n_heavy)`. Addresses issue [#163](https://github.com/BioPandas/biopandas/issues/163). +- Fix: `PandasPdb.to_pdb()` no longer fails with a `TypeError` on structures with `ANISOU` records after `label_models()`. The `model_id` column is now skipped silently, like in `to_pdb_stream()`, and other unexpected `ANISOU` columns are skipped with a warning, as for `ATOM` and `HETATM`. Addresses issue [#124](https://github.com/BioPandas/biopandas/issues/124). + ### 0.5.2 (24/08/2026) - Fix: improves robustness of `amino3to1` residue identification by handling `NaN` insertion codes (NumPy 2.4+ compatibility) and including chain IDs and separators to avoid collisions. PR [#157](https://github.com/BioPandas/biopandas/pull/157) diff --git a/tests/pdb/test_write_pdb.py b/tests/pdb/test_write_pdb.py index 0b183b7..5f4b48b 100644 --- a/tests/pdb/test_write_pdb.py +++ b/tests/pdb/test_write_pdb.py @@ -84,6 +84,35 @@ def test_anisou(): assert f1 == four_eiy +def test_anisou_with_model_id(): + """Test writing ANISOU entries after labelling models.""" + ppdb = PandasPdb() + ppdb.read_pdb(TESTDATA_FILENAME2) + ppdb.label_models() + assert "model_id" in ppdb.df["ANISOU"].columns + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + ppdb.to_pdb(path=OUTFILE, records=None) + assert not any("model_id" in str(x.message) for x in w) + with open(OUTFILE, "r") as f: + f1 = f.read() + os.remove(OUTFILE) + assert f1 == four_eiy + + +def test_anisou_nonexpected_column(): + """Test that unexpected ANISOU columns are skipped with a warning.""" + ppdb = PandasPdb() + ppdb.read_pdb(TESTDATA_FILENAME2) + ppdb.df["ANISOU"]["test"] = 1 + with pytest.warns(UserWarning, match="Column test is not an expected"): + ppdb.to_pdb(path=OUTFILE, records=None) + with open(OUTFILE, "r") as f: + f1 = f.read() + os.remove(OUTFILE) + assert f1 == four_eiy + + def test_add_remark(): """Test adding a REMARK entry.""" # Add remark