Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Changelog

- 2026-08-01: Fixed autotoppar/PRODRG topology generation when the unknown ligand is embedded in a larger system - Issue #1645
- 2026-07-31: Fixed D-amino acid detection - Issue #1636
- 2026-07-31: Fixed topocg issue removing ligands - Issue #1638
- 2026-07-27: Added `rnascan` module for mutagenesis scanning of RNA bases (mutating interface nucleotides to A, C, G, U) - Issue #1631
Expand Down
11 changes: 8 additions & 3 deletions integration_tests/test_alascan.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,12 @@ def test_alascan_with_ligand_topar(alascan_module_protlig, mocker):


def test_alascan_without_ligand_topar(alascan_module, mocker):
"""Test the use of alascan in presence of a ligand without topo/param."""
"""Test alascan with a ligand but no user-provided topo/param.

haddock3-score now runs topoaa with ``autotoppar=true``, so the ligand
topology/parameters are generated on-the-fly with PRODRG and the ligand is
retained during scoring, just like when they are provided explicitly.
"""
alascan_module.previous_io = MockPreviousIO_protlig(path=alascan_module.path)
alascan_module.run()

Expand All @@ -235,6 +240,6 @@ def test_alascan_without_ligand_topar(alascan_module, mocker):

# Loop over files
for mutated_fpath in mutated_filepaths:
# Make sure the ligand is not in it
# Ligand is retained thanks to autotoppar/PRODRG topology generation
file_content = mutated_fpath.read_text()
assert file_content.count("G39") == 0
assert file_content.count("G39") > 20
64 changes: 34 additions & 30 deletions src/haddock/clis/cli_score.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
haddock3-score complex.pdb -p nemsteps 50 w_air 1 electflag True

"""

import argparse
import sys
import tempfile
Expand All @@ -26,14 +27,14 @@
Callable,
FilePath,
Namespace,
)
)
from haddock.libs.libcli import _ParamsToDict


ap = argparse.ArgumentParser(
prog="haddock3-score",
description=__doc__,
)
)

ap.add_argument("pdb_file", help="Input PDB file")

Expand All @@ -43,32 +44,33 @@
type=str,
required=False,
help="Run directory name.",
)
)

ap.add_argument(
"--full",
action="store_true",
help="Print all energy components",
)
)

ap.add_argument(
"--outputpdb",
action="store_true",
help="Save the output PDB file (minimized structure)",
)
)

ap.add_argument(
"--outputpsf",
action="store_true",
help="Save the output PSF file (topology)",
)
)

ap.add_argument(
"-k" "--keep-all",
"-k",
"--keep-all",
dest="keep_all",
action="store_true",
help="Keep the whole run folder.",
)
)

ap.add_argument(
"-p",
Expand All @@ -82,7 +84,7 @@
action=_ParamsToDict,
default={},
nargs="*",
)
)


def _ap() -> ArgumentParser:
Expand All @@ -107,14 +109,14 @@ def maincli() -> None:


def main(
pdb_file: FilePath,
run_dir: FilePath,
full: bool = False,
outputpdb: bool = False,
outputpsf: bool = False,
keep_all: bool = False,
**kwargs: Any,
) -> None:
pdb_file: FilePath,
run_dir: FilePath,
full: bool = False,
outputpdb: bool = False,
outputpsf: bool = False,
keep_all: bool = False,
**kwargs: Any,
) -> None:
"""
Calculate the score of a complex using the ``emscoring`` module.

Expand Down Expand Up @@ -175,7 +177,7 @@ def main(
f"valid `emscoring` parameter.{os.linesep}"
"Valid emscoring parameters are: "
f"{', '.join(sorted(default_emscoring))}"
)
)
# Compare the user-given value to the default one
if value != default_emscoring[param]:
print(
Expand All @@ -185,9 +187,11 @@ def main(
# get the type of default value
default_type = type(default_emscoring[param])
# cast the value to the same type
if default_type == bool:
if default_type is bool:
if value.lower() not in ["true", "false"]:
sys.exit(f"* ERROR * Boolean parameter {param} should be True or False")
sys.exit(
f"* ERROR * Boolean parameter {param} should be True or False"
)
value = value.lower() == "true"
elif param.endswith("_fname"):
value = EmptyPath() if str(value) == "" else Path(value).resolve()
Expand All @@ -200,7 +204,7 @@ def main(
"* ATTENTION * Non-default parameter values were used. "
"They should be properly reported if the output "
"data are used for publication."
)
)
print(f"used emscoring parameters: {ems_dict}")

# create run directory
Expand All @@ -212,7 +216,6 @@ def main(

# create temporary file
with tempfile.NamedTemporaryFile(prefix=input_pdb.stem, suffix=".pdb") as tmp:

# create a copy of the input pdb
input_pdb_copy = Path(tmp.name)
shutil.copy(input_pdb, input_pdb_copy)
Expand All @@ -223,7 +226,8 @@ def main(
"molecules": [input_pdb_copy],
"ligand_param_fname": ems_dict["ligand_param_fname"],
"ligand_top_fname": ems_dict["ligand_top_fname"],
},
"autotoppar": True,
},
"emscoring": ems_dict,
}

Expand Down Expand Up @@ -256,7 +260,7 @@ def main(
+ ems_dict["w_desolv"] * desolv
+ ems_dict["w_air"] * air
+ ems_dict["w_bsa"] * bsa
)
)

print(
"> HADDOCK-score ="
Expand All @@ -265,7 +269,7 @@ def main(
f" + ({ems_dict['w_desolv']} * desolv)"
f" + ({ems_dict['w_air']} * air)"
f" + ({ems_dict['w_bsa']} * bsa)"
)
)
print(f"> HADDOCK-score (emscoring) = {haddock_score_itw:.4f}")

if full:
Expand All @@ -277,23 +281,23 @@ def main(
shutil.copy(
Path(run_dir, "1_emscoring", "emscoring_1.pdb"),
outputpdb_name,
)
)

if outputpsf:
outputpsf_name = Path(f"{input_pdb.stem}_hs.psf")
print(f"> writing {outputpsf_name}")
shutil.copy(
Path(run_dir, "0_topoaa", f"{input_pdb_copy.stem}_haddock.psf"),
outputpsf_name,
)
)

if not keep_all:
shutil.rmtree(run_dir)
else:
print(
'The folder where the calculations were performed was kept.'
f' See folder: {run_dir}'
)
"The folder where the calculations were performed was kept."
f" See folder: {run_dir}"
)


if __name__ == "__main__":
Expand Down
Loading
Loading