From dc43e8efe87f863e54a2b8e1a34df8298a26ec6f Mon Sep 17 00:00:00 2001 From: VGPReys Date: Wed, 14 Aug 2024 12:13:38 +0200 Subject: [PATCH 01/62] on the fly computation of nfle --- src/haddock/gear/expandable_parameters.py | 29 ++++++++---- src/haddock/gear/prepare_run.py | 47 +++++++++++++------ .../modules/refinement/emref/defaults.yaml | 2 +- .../modules/refinement/flexref/defaults.yaml | 2 +- .../modules/refinement/mdref/defaults.yaml | 2 +- 5 files changed, 55 insertions(+), 27 deletions(-) diff --git a/src/haddock/gear/expandable_parameters.py b/src/haddock/gear/expandable_parameters.py index 71d6e9765..0fddcab5c 100644 --- a/src/haddock/gear/expandable_parameters.py +++ b/src/haddock/gear/expandable_parameters.py @@ -109,8 +109,9 @@ # common error messages _emsg_num_differ = ( "The parameter block {!r} expects " - "{} parameters, but {} are present " - "in the user configuration file: {}." + "{} parameters ({}), but {} are present " + "in the user configuration file: {}. " + "Parameter(s) {} are missing." ) @@ -249,7 +250,7 @@ def _read_groups_in_user_config( extract_params: Callable[..., set[str]], _emsg_no_group: str = "Parameter block is not a valid expandable group", _emsg_unexpected_params: str = "Unexpected params for group", -) -> set[str]: +) -> tuple[set[str], dict[str, int]]: """ Read groups in user config. @@ -279,15 +280,18 @@ def _read_groups_in_user_config( Returns ------- - set + new : set[str] A set with the new parameters in the user configuration file that are acceptable according to the expandable rules. + param_name_counts : dict[str, int] + Count of expendable parameter parameter name """ # minimum=1 is used to capture groups with missing parameters user_groups = get_user_groups(user_config, minimum=1, reference=False) default_group_names = set(group[0] for group in default_groups) new: set[str] = set() + param_name_counts: dict[str, int] = {} for (param_name, group_idx), params in user_groups.items(): if param_name not in default_group_names: emsg = _emsg_no_group.format(param_name, group_idx) @@ -311,35 +315,40 @@ def _read_groups_in_user_config( # when num_found > num_expected the error about in `diff` is # triggered instead if num_found != num_expected: + expected_missings = set(expected_params).difference(params) emsg = _emsg_num_differ.format( param_name, num_expected, + ", ".join(expected_params), num_found, ", ".join(params), + ", ".join([f"`{p}`" for p in expected_missings]), ) raise ConfigurationError(emsg) related_params = extract_params(user_config, param_name, group_idx) + # Increment counts for this parameter + if related_params: + param_name_counts.setdefault(param_name, 0) + param_name_counts[param_name] = int(group_idx) new.update(related_params) - - return new + return new, param_name_counts def read_simplest_expandable( - expparams: Iterable[str], config: Iterable[str] + config: Iterable[str], expparams: Iterable[str], ) -> set[str]: """ Read expandable parameters from config file of the type `param_1`. Parameters ---------- + config : dict, dict.keys, set, or alike + The user configuration file. expparams : dict, dict.keys, set, or alike The parameter names that should be considered as expandable. Usually, this is a module subdictionary of `type_simplest_ep`. - config : dict, dict.keys, set, or alike - The user configuration file. - Returns ------- set of str diff --git a/src/haddock/gear/prepare_run.py b/src/haddock/gear/prepare_run.py index 075d2e548..9fcbdc119 100644 --- a/src/haddock/gear/prepare_run.py +++ b/src/haddock/gear/prepare_run.py @@ -1017,25 +1017,44 @@ def get_expandable_parameters( def _get_expandable( user_config: ParamMap, defaults: ParamMap, module_name: str, max_mols: int ) -> set[str]: - type_1 = get_single_index_groups(defaults) - type_2 = get_multiple_index_groups(defaults) - type_4 = get_mol_parameters(defaults) - + # Set parsing vars allowed_params: set[str] = set() - allowed_params.update( - read_single_idx_groups_user_config(user_config, type_1) - ) # noqa: E501 - allowed_params.update( - read_multiple_idx_groups_user_config(user_config, type_2) - ) # noqa: E501 + all_counts: dict[str, int] = {} + # Read single indexed groups (terminating by `_X`) + news_t1, counts_t1 = read_single_idx_groups_user_config( + user_config, + get_single_index_groups(defaults), + ) + allowed_params.update(news_t1) + all_counts.update(counts_t1) + # Read multiple indexed groups (terminating by `_X_Y`) + news_t2, counts_t2 = read_multiple_idx_groups_user_config( + user_config, + get_multiple_index_groups(defaults), + ) + allowed_params.update(news_t2) + all_counts.update(counts_t2) with suppress(KeyError): - type_3 = type_simplest_ep[get_module_name(module_name)] - allowed_params.update(read_simplest_expandable(type_3, user_config)) - - _ = read_mol_parameters(user_config, type_4, max_mols=max_mols) + news_t3 = read_simplest_expandable( + user_config, + type_simplest_ep[get_module_name(module_name)], + ) + allowed_params.update(news_t3) + # Read molecule paramters (starting by `mol_`) + _ = read_mol_parameters( + user_config, + get_mol_parameters(defaults), + max_mols=max_mols, + ) allowed_params.update(_) + # Add counted parameters to hidden user parameters + for param, count in all_counts.items(): + count_param_name = f"n{param}" + if count_param_name in defaults.keys(): + user_config[count_param_name] = count + # Return new set of allowed parameters return allowed_params diff --git a/src/haddock/modules/refinement/emref/defaults.yaml b/src/haddock/modules/refinement/emref/defaults.yaml index db7c386c8..f22d107c0 100644 --- a/src/haddock/modules/refinement/emref/defaults.yaml +++ b/src/haddock/modules/refinement/emref/defaults.yaml @@ -2096,7 +2096,7 @@ nfle: If >=1 then those must be defined manually with starting and end residue numbers in the fle_sta_* and fle_end_* variables and segment ID (segid) in the fle_seg_* variable. group: 'flexibility' - explevel: expert + explevel: hidden fle_sta_1: default: .nan type: integer diff --git a/src/haddock/modules/refinement/flexref/defaults.yaml b/src/haddock/modules/refinement/flexref/defaults.yaml index 7989eaa44..f0e1d54fd 100644 --- a/src/haddock/modules/refinement/flexref/defaults.yaml +++ b/src/haddock/modules/refinement/flexref/defaults.yaml @@ -2639,7 +2639,7 @@ nfle: If >=1 then those must be defined manually with starting and end residue numbers in the fle_sta_* and fle_end_* variables and segment ID (segid) in the fle_seg_* variable. group: 'flexibility' - explevel: expert + explevel: hidden fle_sta_1: default: .nan type: integer diff --git a/src/haddock/modules/refinement/mdref/defaults.yaml b/src/haddock/modules/refinement/mdref/defaults.yaml index 8714a22b5..cd2d255d6 100644 --- a/src/haddock/modules/refinement/mdref/defaults.yaml +++ b/src/haddock/modules/refinement/mdref/defaults.yaml @@ -2195,7 +2195,7 @@ nfle: If >=1 then those must be defined manually with starting and end residue numbers in the fle_sta_* and fle_end_* variables and segment ID (segid) in the fle_seg_* variable. group: 'flexibility' - explevel: expert + explevel: hidden fle_sta_1: default: .nan type: integer From 2a4100db9085a31c2f51e1784996a695ffc2e68e Mon Sep 17 00:00:00 2001 From: VGPReys Date: Wed, 14 Aug 2024 12:16:39 +0200 Subject: [PATCH 02/62] removing nfle parameter from examples --- .../docking-flexref-protein-glycan-full.cfg | 1 - .../docking-flexref-protein-glycan-test.cfg | 1 - .../docking-protein-peptide-cltsel-full.cfg | 2 -- .../docking-protein-peptide/docking-protein-peptide-full.cfg | 2 -- .../docking-protein-peptide-mdref-full.cfg | 2 -- .../docking-protein-peptide-mdref-test.cfg | 2 -- .../docking-protein-peptide/docking-protein-peptide-test.cfg | 5 +++-- 7 files changed, 3 insertions(+), 12 deletions(-) diff --git a/examples/docking-protein-glycan/docking-flexref-protein-glycan-full.cfg b/examples/docking-protein-glycan/docking-flexref-protein-glycan-full.cfg index 446b65d7e..e2db10af4 100644 --- a/examples/docking-protein-glycan/docking-flexref-protein-glycan-full.cfg +++ b/examples/docking-protein-glycan/docking-flexref-protein-glycan-full.cfg @@ -43,7 +43,6 @@ mdsteps_cool2 = 10000 mdsteps_cool3 = 10000 ambig_fname = "data/ambig.tbl" # give full flexibilit to the glycan -nfle = 1 fle_sta_1 = 1 fle_end_1 = 4 fle_seg_1 = "B" diff --git a/examples/docking-protein-glycan/docking-flexref-protein-glycan-test.cfg b/examples/docking-protein-glycan/docking-flexref-protein-glycan-test.cfg index 7fc02c3ff..9900453b2 100644 --- a/examples/docking-protein-glycan/docking-flexref-protein-glycan-test.cfg +++ b/examples/docking-protein-glycan/docking-flexref-protein-glycan-test.cfg @@ -39,7 +39,6 @@ mdsteps_cool2 = 10000 mdsteps_cool3 = 10000 ambig_fname = "data/ambig.tbl" # give full flexibilit to the glycan -nfle = 1 fle_sta_1 = 1 fle_end_1 = 4 fle_seg_1 = "B" diff --git a/examples/docking-protein-peptide/docking-protein-peptide-cltsel-full.cfg b/examples/docking-protein-peptide/docking-protein-peptide-cltsel-full.cfg index 3e6f2e3f6..0fe0e8f6d 100644 --- a/examples/docking-protein-peptide/docking-protein-peptide-cltsel-full.cfg +++ b/examples/docking-protein-peptide/docking-protein-peptide-cltsel-full.cfg @@ -51,7 +51,6 @@ reference_fname = "data/1nx1_refe.pdb" tolerance = 5 ambig_fname = "data/ambig.tbl" # Define peptide as fully flexible -nfle = 1 fle_sta_1 = 1 fle_end_1 = 11 fle_seg_1 = "B" @@ -72,7 +71,6 @@ reference_fname = "data/1nx1_refe.pdb" tolerance = 5 ambig_fname = "data/ambig.tbl" # Define peptide as fully flexible -nfle = 1 fle_sta_1 = 1 fle_end_1 = 11 fle_seg_1 = "B" diff --git a/examples/docking-protein-peptide/docking-protein-peptide-full.cfg b/examples/docking-protein-peptide/docking-protein-peptide-full.cfg index 08e0c49de..45b23459a 100644 --- a/examples/docking-protein-peptide/docking-protein-peptide-full.cfg +++ b/examples/docking-protein-peptide/docking-protein-peptide-full.cfg @@ -45,7 +45,6 @@ select = 400 tolerance = 5 ambig_fname = "data/ambig.tbl" # Define peptide as fully flexible -nfle = 1 fle_sta_1 = 1 fle_end_1 = 11 fle_seg_1 = "B" @@ -65,7 +64,6 @@ reference_fname = "data/1nx1_refe.pdb" tolerance = 5 ambig_fname = "data/ambig.tbl" # Define peptide as fully flexible -nfle = 1 fle_sta_1 = 1 fle_end_1 = 11 fle_seg_1 = "B" diff --git a/examples/docking-protein-peptide/docking-protein-peptide-mdref-full.cfg b/examples/docking-protein-peptide/docking-protein-peptide-mdref-full.cfg index ce095acbe..064d8363f 100644 --- a/examples/docking-protein-peptide/docking-protein-peptide-mdref-full.cfg +++ b/examples/docking-protein-peptide/docking-protein-peptide-mdref-full.cfg @@ -45,7 +45,6 @@ select = 400 tolerance = 5 ambig_fname = "data/ambig.tbl" # Define peptide as fully flexible -nfle = 1 fle_sta_1 = 1 fle_end_1 = 11 fle_seg_1 = "B" @@ -65,7 +64,6 @@ reference_fname = "data/1nx1_refe.pdb" tolerance = 5 ambig_fname = "data/ambig.tbl" # Define peptide as fully flexible -nfle = 1 fle_sta_1 = 1 fle_end_1 = 11 fle_seg_1 = "B" diff --git a/examples/docking-protein-peptide/docking-protein-peptide-mdref-test.cfg b/examples/docking-protein-peptide/docking-protein-peptide-mdref-test.cfg index 4da16060b..7571f8fb5 100644 --- a/examples/docking-protein-peptide/docking-protein-peptide-mdref-test.cfg +++ b/examples/docking-protein-peptide/docking-protein-peptide-mdref-test.cfg @@ -39,7 +39,6 @@ select = 5 tolerance = 20 ambig_fname = "data/ambig.tbl" # Define peptide as fully flexible -nfle = 1 fle_sta_1 = 1 fle_end_1 = 11 fle_seg_1 = "B" @@ -60,7 +59,6 @@ reference_fname = "data/1nx1_refe.pdb" tolerance = 20 ambig_fname = "data/ambig.tbl" # Define peptide as fully flexible -nfle = 1 fle_sta_1 = 1 fle_end_1 = 11 fle_seg_1 = "B" diff --git a/examples/docking-protein-peptide/docking-protein-peptide-test.cfg b/examples/docking-protein-peptide/docking-protein-peptide-test.cfg index 6c819ee2e..9b1b8a80b 100644 --- a/examples/docking-protein-peptide/docking-protein-peptide-test.cfg +++ b/examples/docking-protein-peptide/docking-protein-peptide-test.cfg @@ -39,13 +39,15 @@ select = 5 tolerance = 20 ambig_fname = "data/ambig.tbl" # Define peptide as fully flexible -nfle = 2 fle_sta_1 = 1 fle_end_1 = 5 fle_seg_1 = "B" fle_sta_2 = 6 fle_end_2 = 11 fle_seg_2 = "B" +#nseg1 = 2 +#seg_sta_1_1 = 1 +#seg_end_1_1 = 5 # Define automatically dihedral restraints # for alpha and beta secondary structure elements ssdihed = "alphabeta" @@ -63,7 +65,6 @@ reference_fname = "data/1nx1_refe.pdb" tolerance = 20 ambig_fname = "data/ambig.tbl" # Define peptide as fully flexible -nfle = 1 fle_sta_1 = 1 fle_end_1 = 11 fle_seg_1 = "B" From f95161b4675ef34c43c71a0602018c262e642dc7 Mon Sep 17 00:00:00 2001 From: VGPReys Date: Wed, 14 Aug 2024 12:19:58 +0200 Subject: [PATCH 03/62] removing commented lines --- .../docking-protein-peptide/docking-protein-peptide-test.cfg | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/docking-protein-peptide/docking-protein-peptide-test.cfg b/examples/docking-protein-peptide/docking-protein-peptide-test.cfg index 9b1b8a80b..672ae43ba 100644 --- a/examples/docking-protein-peptide/docking-protein-peptide-test.cfg +++ b/examples/docking-protein-peptide/docking-protein-peptide-test.cfg @@ -45,9 +45,6 @@ fle_seg_1 = "B" fle_sta_2 = 6 fle_end_2 = 11 fle_seg_2 = "B" -#nseg1 = 2 -#seg_sta_1_1 = 1 -#seg_end_1_1 = 5 # Define automatically dihedral restraints # for alpha and beta secondary structure elements ssdihed = "alphabeta" From 20c1fada941b9ad1e9bc6bc8375bbf7cd78866ba Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 10 Sep 2024 17:02:57 +0200 Subject: [PATCH 04/62] remove fcc as submodule --- .gitmodules | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index 7b813623c..6af268cad 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,3 @@ -[submodule "src/fcc"] - path = src/fcc - url = https://github.com/haddocking/fcc.git - [submodule "src/fast-rmsdmatrix"] path = src/fast-rmsdmatrix url = https://github.com/mgiulini/fast-rmsdmatrix.git From 6dc8a51791af6f992065d0d712734a5124eafbf7 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 10 Sep 2024 17:03:40 +0200 Subject: [PATCH 05/62] delete fcc submodule --- src/fcc | 1 - 1 file changed, 1 deletion(-) delete mode 160000 src/fcc diff --git a/src/fcc b/src/fcc deleted file mode 160000 index 3a1626de3..000000000 --- a/src/fcc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3a1626de3366f6db8b45caed3bd9fbc6b2881286 From 41d0e109bacb0119b2e4300c257800877fdf3535 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 10 Sep 2024 17:40:54 +0200 Subject: [PATCH 06/62] add `fcc` code here --- src/haddock/clis/re/clustfcc.py | 98 ++++--- src/haddock/fcc/__init__.py | 0 src/haddock/fcc/calc_fcc_matrix.py | 186 +++++++++++++ src/haddock/fcc/cluster_fcc.py | 253 ++++++++++++++++++ src/haddock/fcc/make_contacts.py | 122 +++++++++ src/haddock/fcc/pdb_chainxseg.py | 78 ++++++ src/haddock/fcc/ppretty_clusters.py | 84 ++++++ .../modules/analysis/clustfcc/__init__.py | 81 +++--- .../modules/analysis/clustfcc/clustfcc.py | 96 +++---- 9 files changed, 859 insertions(+), 139 deletions(-) create mode 100644 src/haddock/fcc/__init__.py create mode 100755 src/haddock/fcc/calc_fcc_matrix.py create mode 100755 src/haddock/fcc/cluster_fcc.py create mode 100755 src/haddock/fcc/make_contacts.py create mode 100755 src/haddock/fcc/pdb_chainxseg.py create mode 100755 src/haddock/fcc/ppretty_clusters.py diff --git a/src/haddock/clis/re/clustfcc.py b/src/haddock/clis/re/clustfcc.py index 993479f73..c40d5cc8d 100644 --- a/src/haddock/clis/re/clustfcc.py +++ b/src/haddock/clis/re/clustfcc.py @@ -1,13 +1,12 @@ """haddock3-re clustfcc subcommand.""" -from pathlib import Path import shutil -from fcc.scripts import cluster_fcc - +from pathlib import Path from haddock import log from haddock.core.defaults import INTERACTIVE_RE_SUFFIX from haddock.core.typing import Union +from haddock.fcc import cluster_fcc from haddock.gear.config import load as read_config from haddock.gear.config import save as save_config from haddock.libs.libclust import ( @@ -17,8 +16,8 @@ rank_clusters, write_structure_list, ) -from haddock.libs.libontology import ModuleIO from haddock.libs.libinteractive import look_for_capri, rewrite_capri_tables +from haddock.libs.libontology import ModuleIO from haddock.modules.analysis.clustfcc.clustfcc import ( get_cluster_centers, iterate_clustering, @@ -32,7 +31,7 @@ def add_clustfcc_arguments(clustfcc_subcommand): clustfcc_subcommand.add_argument( "clustfcc_dir", help="The clustfcc directory to recluster.", - ) + ) clustfcc_subcommand.add_argument( "-f", @@ -40,7 +39,7 @@ def add_clustfcc_arguments(clustfcc_subcommand): help="Minimum fraction of common contacts to be considered in a cluster.", # noqa: E501 required=False, type=float, - ) + ) clustfcc_subcommand.add_argument( "-s", @@ -48,56 +47,56 @@ def add_clustfcc_arguments(clustfcc_subcommand): help="Strictness factor.", required=False, type=float, - ) - + ) + clustfcc_subcommand.add_argument( "-t", "--min_population", help="Clustering population threshold.", required=False, type=int, - ) - + ) + clustfcc_subcommand.add_argument( "-p", "--plot_matrix", help="Generate the matrix plot with the clusters.", required=False, default=False, - action='store_true', - ) - + action="store_true", + ) + return clustfcc_subcommand def reclustfcc( - clustfcc_dir: str, - clust_cutoff: Union[bool, float] = None, - strictness: Union[bool, float] = None, - min_population: Union[bool, int] = None, - plot_matrix : bool = True, - ) -> Path: + clustfcc_dir: str, + clust_cutoff: Union[bool, float] = None, + strictness: Union[bool, float] = None, + min_population: Union[bool, int] = None, + plot_matrix: bool = True, +) -> Path: """ Recluster the models in the clustfcc directory. - + Parameters ---------- clustfcc_dir : str Path to the clustfcc directory. - + clust_cutoff : Union[bool, float] Fraction of common contacts to not be considered a singleton model. - + strictness : Union[bool, float] Fraction of common contacts to be considered to be part of the same cluster. - + min_population : Union[bool, int] Minimum cluster population. - + plot_matrix : bool Should the corresponding matrix plot be generated. - + Returns ------- outdir : Path @@ -121,8 +120,8 @@ def reclustfcc( # load the original clustering parameters via json clustfcc_params = read_config(Path(clustfcc_dir, "params.cfg")) - key = list(clustfcc_params['final_cfg'].keys())[0] - clustfcc_params = clustfcc_params['final_cfg'][key] + key = list(clustfcc_params["final_cfg"].keys())[0] + clustfcc_params = clustfcc_params["final_cfg"][key] log.info(f"Previous clustering parameters: {clustfcc_params}") # adjust the parameters @@ -137,16 +136,15 @@ def reclustfcc( # load the fcc matrix pool = cluster_fcc.read_matrix( Path(clustfcc_dir, "fcc.matrix"), - clustfcc_params['clust_cutoff'], - clustfcc_params['strictness'], - ) - + clustfcc_params["clust_cutoff"], + clustfcc_params["strictness"], + ) + # iterate clustering until at least one cluster is found clusters, min_population = iterate_clustering( - pool, - clustfcc_params['min_population'] - ) - clustfcc_params['min_population'] = min_population + pool, clustfcc_params["min_population"] + ) + clustfcc_params["min_population"] = min_population log.info(f"Updated clustering parameters: {clustfcc_params}") # Prepare output and read the elements @@ -160,20 +158,20 @@ def reclustfcc( _score_dic, sorted_score_dic = rank_clusters(clt_dic, min_population) output_models = add_cluster_info(sorted_score_dic, clt_dic) - + # Write unclustered structures - write_structure_list(models, - output_models, - out_fname=Path(outdir, "clustfcc.tsv")) - + write_structure_list( + models, output_models, out_fname=Path(outdir, "clustfcc.tsv") + ) + write_clustfcc_file( clusters, clt_centers, clt_dic, clustfcc_params, sorted_score_dic, - output_fname=Path(outdir, 'clustfcc.txt') - ) + output_fname=Path(outdir, "clustfcc.txt"), + ) save_config(clustfcc_params, Path(outdir, "params.cfg")) @@ -183,7 +181,7 @@ def reclustfcc( if caprieval_folder: log.info("Rewriting capri tables") rewrite_capri_tables(caprieval_folder, clt_dic, outdir) - + else: output_models = models @@ -194,25 +192,23 @@ def reclustfcc( final_order_idx, labels, cluster_ids = [], [], [] for pdb in output_models: final_order_idx.append(models.index(pdb)) - labels.append(pdb.file_name.replace('.pdb', '')) + labels.append(pdb.file_name.replace(".pdb", "")) cluster_ids.append(pdb.clt_id) # Get custom cluster data - matrix_cluster_dt, cluster_limits = get_cluster_matrix_plot_clt_dt( - cluster_ids - ) + matrix_cluster_dt, cluster_limits = get_cluster_matrix_plot_clt_dt(cluster_ids) # Define output filename - html_matrix_basepath = Path(outdir, 'fcc_matrix') + html_matrix_basepath = Path(outdir, "fcc_matrix") # Plot matrix html_matrixpath = plot_cluster_matrix( Path(clustfcc_dir, "fcc.matrix"), final_order_idx, labels, - dttype='FCC', + dttype="FCC", diag_fill=1, output_fname=html_matrix_basepath, matrix_cluster_dt=matrix_cluster_dt, cluster_limits=cluster_limits, - ) + ) log.info(f"Plotting matrix in {html_matrixpath}") - + return outdir diff --git a/src/haddock/fcc/__init__.py b/src/haddock/fcc/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/haddock/fcc/calc_fcc_matrix.py b/src/haddock/fcc/calc_fcc_matrix.py new file mode 100755 index 000000000..330db24b2 --- /dev/null +++ b/src/haddock/fcc/calc_fcc_matrix.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- + +""" +Calculates a matrix of fraction of common contacts between two or more structures. + +Authors: + RODRIGUES Joao + TRELLET Mikael + MELQUIOND Adrien +""" + + +# Contact Parsing routines +def parse_contact_file(f_list, ignore_chain): + """Parses a list of contact files.""" + + if ignore_chain: + contacts = [ + [int(l[0:5] + l[6:-1]) for l in open(f)] for f in f_list if f.strip() + ] + else: + contacts = [set([int(l) for l in open(f)]) for f in f_list if f.strip()] + + return contacts + + +# FCC Calculation Routine +def calculate_fcc(listA, listB): + """ + Calculates the fraction of common elements between two lists + taking into account chain IDs + """ + + cc = len(listA.intersection(listB)) + cc_v = len(listB.intersection(listA)) + + return (cc, cc_v) + + +def calculate_fcc_nc(listA, listB): + """ + Calculates the fraction of common elements between two lists + not taking into account chain IDs. Much Slower. + """ + + largest, smallest = sorted([listA, listB], key=len) + ncommon = len([ele for ele in largest if ele in smallest]) + return (ncommon, ncommon) + + +# Matrix Calculation + + +def calculate_pairwise_matrix(contacts, ignore_chain): + """Calculates a matrix of pairwise fraction of common contacts (FCC). + Outputs numeric indexes. + + contacts: list_of_unique_pairs_of_residues [set/list] + + Returns pairwise matrix as an iterator, each entry in the form: + FCC(cplx_1/cplx_2) FCC(cplx_2/cplx_1) + """ + + contact_lengths = [] + for c in contacts: + try: + ic = 1.0 / len(c) + except ZeroDivisionError: + ic = 0 + contact_lengths.append(ic) + + if ignore_chain: + calc_fcc = calculate_fcc_nc + else: + calc_fcc = calculate_fcc + + for i in range(len(contacts)): + + for k in range(i + 1, len(contacts)): + cc, cc_v = calc_fcc(contacts[i], contacts[k]) + fcc, fcc_v = cc * contact_lengths[i], cc * contact_lengths[k] + yield (i + 1, k + 1, fcc, fcc_v) + + +def _output_fcc(output, values, f_buffer): + + buf = [] + for i in values: + buf.append(i) + if len(buf) == f_buffer: + output( + "".join(["%s %s %1.3f %1.3f\n" % (i[0], i[1], i[2], i[3]) for i in buf]) + ) + buf = [] + output("".join(["%s %s %1.3f %1.3f\n" % (i[0], i[1], i[2], i[3]) for i in buf])) + + +if __name__ == "__main__": + + import optparse + import os + import sys + from time import ctime, time + + USAGE = "%s ... [options]\n" % os.path.basename( + sys.argv[0] + ) + + parser = optparse.OptionParser(usage=USAGE) + parser.add_option( + "-o", + "--output", + dest="output_file", + action="store", + type="string", + default=sys.stdout, + help="Output File [default: STDOUT]", + ) + parser.add_option( + "-f", + "--file", + dest="input_file", + action="store", + type="string", + help="Input file (one contact file name per line)", + ) + parser.add_option( + "-b", + "--buffer_size", + dest="buffer_size", + action="store", + type="string", + default=50000, + help="Buffer size for writing output. Number of lines to cache before writing to file [default: 50000]", + ) + parser.add_option( + "-i", + "--ignore_chain", + dest="ignore_chain_char", + action="store_true", + help="Ignore chain character in residue code. Use for homomeric complexes.", + ) + + (options, args) = parser.parse_args() + + if options.input_file: + args = [name.strip() for name in open(options.input_file)] + + if len(args) < 2: + sys.stderr.write( + "- Provide (at least) two structures to calculate a matrix. You provided %s.\n" + % len(args) + ) + sys.stderr.write(USAGE) + sys.exit(1) + + sys.stderr.write("+ BEGIN: %s\n" % ctime()) + if options.ignore_chain_char: + sys.stderr.write("+ Ignoring chains. Expect a considerable slowdown!!\n") + exclude_chains = True + else: + exclude_chains = False + + t_init = time() + sys.stderr.write("+ Parsing %i contact files\n" % len(args)) + + c = parse_contact_file(args, exclude_chains) + + m = calculate_pairwise_matrix(c, exclude_chains) + + if isinstance(options.output_file, str): + f = open(options.output_file, "w") + else: + f = options.output_file + + sys.stderr.write( + "+ Calculating Matrix\n" + ) # Matrix is calculated when writing. Generator property. + sys.stderr.write("+ Writing matrix to %s\n" % f.name) + _output_fcc(f.write, m, options.buffer_size) + + if isinstance(options.output_file, str): + f.close() + t_elapsed = time() - t_init + sys.stderr.write("+ END: %s [%6.2f seconds elapsed]\n" % (ctime(), t_elapsed)) diff --git a/src/haddock/fcc/cluster_fcc.py b/src/haddock/fcc/cluster_fcc.py new file mode 100755 index 000000000..08ceb64fe --- /dev/null +++ b/src/haddock/fcc/cluster_fcc.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- + +""" +Asymmetric Taylor-Butina Disjoint Clustering Algorithm. + +Authors: + RODRIGUES Joao + TRELLET Mikael + MELQUIOND Adrien +""" + +class Element(object): + """Defines a 'clusterable' Element""" + + __slots__ = ['name', 'cluster', 'neighbors'] + + def __init__(self, name): + self.name = name + self.cluster = 0 + self.neighbors = set() + + + def add_neighbor(self, neighbor): + """Adds another element to the neighbor list""" + self.neighbors.add(neighbor) + + def assign_cluster(self, clust_id): + """Assigns the Element to Cluster. 0 if unclustered""" + self.cluster = clust_id + +class Cluster(object): + """Defines a Cluster. A Cluster is created with a name and a center (Element class)""" + + __slots__ = ['name', 'center', 'members'] + + def __init__(self, name, center): + + self.name = name + self.center = center + + self.members = [] + + self.populate() + + def __len__(self): + return len(self.members)+1 # +1 Center + + def populate(self): + """ + Populates the Cluster member list through the + neighbor list of its center. + """ + + name = self.name + # Assign center + ctr = self.center + ctr.assign_cluster(name) + + mlist = self.members + # Assign members + ctr_nlist = (n for n in ctr.neighbors if not n.cluster) + for e in ctr_nlist: + mlist.append(e) + e.assign_cluster(name) + + def add_member(self, element): + """ + Adds one single element to the cluster. + """ + l = self.members + l.append(element) + element.assign_cluster(self.name) + +def read_matrix(path, cutoff, strictness): + """ + Reads in a four column matrix (1 2 0.123 0.456\n) + and creates an dictionary of Elements. + + The strictness factor is a that multiplies by the cutoff + to produce a new cutoff for the second half of the matrix. Used to + allow some variability while keeping very small interfaces from clustering + with anything remotely similar. + """ + + cutoff = float(cutoff) + partner_cutoff = float(cutoff) * float(strictness) + + elements = {} + + f = open(path, 'r') + for line in f: + ref, mobi, dRM, dMR = line.split() + ref = int(ref) + mobi = int(mobi) + dRM = float(dRM) + dMR = float(dMR) + + # Create or Retrieve Elements + if ref not in elements: + r = Element(ref) + elements[ref] = r + else: + r = elements[ref] + + if mobi not in elements: + m = Element(mobi) + elements[mobi] = m + else: + m = elements[mobi] + + # Assign neighbors + if dRM >= cutoff and dMR >= partner_cutoff: + r.add_neighbor(m) + if dMR >= cutoff and dRM >= partner_cutoff: + m.add_neighbor(r) + + f.close() + + return elements + +def remove_true_singletons(element_pool): + """ Removes from the pool elements without any neighbor """ + + ep = element_pool + + ts = set([e for e in ep if not ep[e].neighbors]) + + # Remove ts from everybody's neighbor list + ts_e = set(ep[e] for e in ts) + for e in element_pool: + ep[e].neighbors = ep[e].neighbors.difference(ts_e) + + # Remove ts from pool + for e in ts: + del ep[e] + + return (ts, ep) + +def cluster_elements(element_pool, threshold): + """ + Groups Elements within a given threshold + together in the same cluster. + """ + + clusters = [] + threshold -= 1 # Account for center + ep = element_pool + cn = 1 # Cluster Number + while 1: + # Clusterable elements + ce = [e for e in ep if not ep[e].cluster] + if not ce: # No more elements to cluster + break + + # Select Cluster Center + # Element with largest neighbor list + ctr_nlist, ctr = sorted([(len([se for se in ep[e].neighbors if not se.cluster]), e) for e in ce])[-1] + + # Cluster until length of remaining elements lists are above threshold + if ctr_nlist < threshold: + break + + # Create Cluster + c = Cluster(cn, ep[ctr]) + cn += 1 + clusters.append(c) + + return (ep, clusters) + +def output_clusters(handle, clusters): + """Outputs the cluster name, center, and members.""" + + write = handle.write + + for c in clusters: + write( "Cluster %s -> %s " %(c.name, c.center.name) ) + for m in sorted(c.members, key=lambda k: k.name): + write( "%s " %m.name ) + write("\n") + +if __name__ == "__main__": + + import optparse + import sys + from time import time, ctime + import os + + USAGE="%s [options]" %os.path.basename(sys.argv[0]) + + parser = optparse.OptionParser(usage=USAGE) + parser.add_option('-o', '--output', dest="output_handle", action='store', type='str', + default=sys.stdout, + help='Output File [STDOUT]') + parser.add_option('-c', '--cluster-size', dest="clus_size", action="store", type="int", + default=4, + help="Minimum number of elements in a cluster [4]") + parser.add_option('-s', '--strictness', dest="strictness", action="store", type='float', + default=0.75, + help="Multiplier for cutoff for M->R inclusion threshold. [0.75 or effective cutoff of 0.5625]") + + + (options, args) = parser.parse_args() + + if sys.version_info[0:2] < (2,6): + cur_version = "%s.%s" %sys.version_info[0:2] + sys.stderr.write("- Python version not supported (%s). Please use 2.5 or newer.\n" %cur_version ) + sys.exit(1) + if len(args) != 2: + sys.stderr.write("- Invalid number of arguments: %i\n" %len(args)) + sys.stderr.write("USAGE: %s\n" %USAGE) + sys.exit(1) + + fmatrix, cutoff = args + cutoff = float(cutoff) + + # Read Matrix + sys.stderr.write("+ BEGIN: %s\n" %ctime()) + t_init = time() + + try: + pool = read_matrix(fmatrix, cutoff, options.strictness) + except IOError: + sys.stderr.write("File not found: %s\n" %fmatrix) + sys.exit(1) + + sys.stderr.write("+ Read %ix%i distance matrix in %i seconds\n" %(len(pool), len(pool), int(time()-t_init))) + + # ts, pool = remove_true_singletons(pool) + # sys.stderr.write("+ Detected %i True Singletons\n" %len(ts)) + + # Cluster + element_pool, clusters = cluster_elements(pool, options.clus_size) + + # Output Clusters + o = options.output_handle + if isinstance(o, str): + o_handle = open(o, 'w') + else: + o_handle = o + + sys.stderr.write("+ Writing %i Clusters\n" %len(clusters)) + output_clusters(o_handle, clusters) + if isinstance(o, str): + o_handle.close() + + total_elements = len(element_pool) + clustered = sum([len(c) for c in clusters]) + # Calculate coverage + clust_coverage = clustered*100/float(total_elements) + sys.stderr.write("+ Coverage %3.2f%% (%i/%i)\n" %(clust_coverage, clustered, total_elements)) + t_elapsed = time()-t_init + sys.stderr.write( "+ END: %s [%3.2f seconds]\n" %(ctime(), t_elapsed)) diff --git a/src/haddock/fcc/make_contacts.py b/src/haddock/fcc/make_contacts.py new file mode 100755 index 000000000..60b190f08 --- /dev/null +++ b/src/haddock/fcc/make_contacts.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- + +""" +Script to calculate contact lists on PDB files. +Requires external executable to calculate the contacts! + +Authors: + RODRIGUES Joao + TRELLET Mikael + MELQUIOND Adrien +""" + +from multiprocessing import Process +from subprocess import Popen, PIPE + +def _calculate_contacts(executable, pdbfile, d_cutoff, filter_selection=None, extension='.contacts'): + """ + Outputs a list of contacts based on vector analysis + of the PDB file. + + Arguments: + executable - path to contact calculation program + pdbfile - path to PDB-formatted file (.pdb extension) + d_cutoff - minimal distance in A to consider a contact (float) + filter_selection - list of identifiers to filter contacts (list of strings) + """ + + pdbname = os.path.basename(pdbfile)[:-4] + + p = Popen([executable, pdbfile, d_cutoff], stdout=PIPE) + p_output = p.communicate()[0] + contacts = sorted(list(set([l for l in p_output.split('\n')][:-1]))) + + # Filter contacts + if filter_selection: + contacts = filter(lambda x: x[5] in filter_selection and x[-1] in filter_selection, contacts) + # + + outfile = os.path.join(os.path.dirname(pdbfile), "%s%s" %(pdbname, extension)) + with open(outfile, 'w') as o: + o.write('\n'.join(contacts)) + + return 0 + +if __name__ == '__main__': + + import optparse + import os, sys + + USAGE = "%s [-f structures.txt] [-n 4] [-c 5.0] file1.pdb file2.pdb" + + parser = optparse.OptionParser(usage=USAGE) + parser.add_option('-c', '--cutoff', dest="d_cutoff", action='store', type='string', + default="5.0", + help='Distance cutoff to evaluate contacts. [default: 5.0A]') + parser.add_option('-f', '--file', dest="input_file", action='store', type='string', + help='Input file (one file path per line)') + parser.add_option('-n', '--nproc', dest="nproc", action='store', type='string', + default=1, + help='Number of simultaneous processes to launch in each round. [default: 1]') + parser.add_option('-e', '--exec', dest="executable", action='store', type='string', + default='%s/../src/contact_fcc' %os.path.dirname(sys.argv[0]), + help='Path to the executable C++ program to calculate contacts [default: ../fcc/src/contact_fcc]') + parser.add_option('-s', '--selection', dest="selection", action='store', type='string', + default=None, + help='Filter contacts based on their segids. [Default: No filtering. All chains] [Example: A,C]') + + (options, args) = parser.parse_args() + + if options.input_file: + args = [name.strip() for name in open(options.input_file) if name.strip()] + + if not args: + print "No files provided. Exiting" + print USAGE + sys.exit(1) + + # Convert to full paths + args = map(os.path.abspath, args) + + nproc = int(options.nproc) + cutoff = options.d_cutoff + + executable = options.executable + if not os.path.exists(executable): + print "Path not found: %s" %os.path.abspath(executable) + sys.exit(1) + executable = os.path.abspath(executable) + + if options.selection: + filter_selection = set(options.selection.split(',')) + representative = open(args[0]) + repr_chains = dict([(j,str(i)) for i,j in enumerate(sorted(set([l[72] for l in representative if l.startswith('ATOM')])), start=1)]) + filter_selection = map(repr_chains.get, filter_selection) + representative.close() + oextension = '.contacts-'+''.join(options.selection.split(',')) + else: + filter_selection = None + oextension = '.contacts' + + queue = [] + + while 1: + + arg = args.pop() + # Create Process for arg + p = Process(target=_calculate_contacts, args=(executable, arg, cutoff, filter_selection, oextension)) + queue.append(p) + + if (len(queue) == nproc) or (not args and len(queue)): + + for job in queue: + job.start() + for job in queue: # Waiting for job to finish + job.join() + queue = [] + + if not args and not queue: + break + + print "Finished" diff --git a/src/haddock/fcc/pdb_chainxseg.py b/src/haddock/fcc/pdb_chainxseg.py new file mode 100755 index 000000000..d9204e7c0 --- /dev/null +++ b/src/haddock/fcc/pdb_chainxseg.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python + +""" +Exchanges segment ID for chain ID in a PDB file. + +usage: python pdb_chainxseg.py +example: python pdb_chainxseg.py 1CTF.pdb + +This program is part of the PDB tools distributed with HADDOCK +or with the HADDOCK tutorial. The utilities in this package +can be used to quickly manipulate PDB files, with the benefit +of 'piping' several different commands. This is a rewrite of old +FORTRAN77 code that was taking too much effort to compile. RIP. +""" + +import os +import re +import sys + +__author__ = "Joao Rodrigues" + +USAGE = "usage: " + sys.argv[0] + " \n" + +def check_input(args): + """Checks whether to read from stdin/file and validates user input/options.""" + + if not len(args): + # Read from pipe + if not sys.stdin.isatty(): + pdbfh = sys.stdin + else: + sys.stderr.write(USAGE) + sys.exit(1) + elif len(args) == 1: + # Read from file + if not os.path.exists(args[0]): + sys.stderr.write('File not found: ' + args[0] + '\n') + sys.stderr.write(USAGE) + sys.exit(1) + pdbfh = open(args[0], 'r') + else: + sys.stderr.write(USAGE) + sys.exit(1) + + return pdbfh + +def _swap_chainxseg(fhandle): + """Enclosing logic in a function to speed up a bit""" + + coord_re = re.compile('^(ATOM|HETATM)') + fhandle = fhandle + + for line in fhandle: + if coord_re.match(line): + yield line[:72] + line[21].ljust(4) + line[76:] + else: + yield line + +if __name__ == '__main__': + # Check Input + pdbfh = check_input(sys.argv[1:]) + + # Do the job + new_pdb = _swap_chainxseg(pdbfh) + + try: + sys.stdout.write(''.join(new_pdb)) + sys.stdout.flush() + except IOError: + # This is here to catch Broken Pipes + # for example to use 'head' or 'tail' without + # the error message showing up + pass + + # last line of the script + # We can close it even if it is sys.stdin + pdbfh.close() + sys.exit(0) \ No newline at end of file diff --git a/src/haddock/fcc/ppretty_clusters.py b/src/haddock/fcc/ppretty_clusters.py new file mode 100755 index 000000000..bf42582c3 --- /dev/null +++ b/src/haddock/fcc/ppretty_clusters.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +""" +Outputs the names of the cluster members based on a clustering output file +and the original file listing the PDB files + +Authors: + RODRIGUES Joao +""" + +import os +import sys + +USAGE = "python %s " %os.path.basename(sys.argv[0]) + +def read_clusters(path): + """ + Reads clusters from a FCC output file. + """ + + clusters = [] + cl_file = open(path, 'r') + for line in cl_file: + # Cluster 8 -> 193 141 142 144 151 168 171 172 178 + models = map(int, line.split()[3:]) + clusters.append(models) + + return clusters + +def read_list(path): + """ + Reads a list containing one file per line. + Returns an index of line number - line content + """ + + with open(path, 'r') as fhandle: + fdata = {} + for nline, line in enumerate(fhandle): + if not line.strip(): + continue + # Remove extension + fdata[nline+1] = '.'.join(line.strip().split('.')[:-1]) + + return fdata + +def cross_data(clusters, flist): + """ + Matches names in flist to the numbers in clusters. + """ + + named_clusters = [] + for cl in clusters: + ncl = [flist[s] for s in cl] + named_clusters.append(ncl) + + return named_clusters + +if __name__ == '__main__': + + if len(sys.argv[1:]) != 2: + print USAGE + sys.exit(1) + + + cluster_file = os.path.abspath(sys.argv[1]) + pdblist_file = os.path.abspath(sys.argv[2]) + + try: + cl_list = read_clusters(cluster_file) + except IOError: + sys.stderr.write('Error: file not found (%s)\nAborting..\n' %cluster_file) + sys.exit(1) + + try: + pdb_list = read_list(pdblist_file) + except IOError: + sys.stderr.write('Error: file not found (%s)\nAborting..\n' %pdblist_file) + sys.exit(1) + + named_clusters = cross_data(cl_list, pdb_list) + + # Output + for i, nc in enumerate(named_clusters): + print "Cluster %i -> %s" %(i+1, ' '.join(nc)) diff --git a/src/haddock/modules/analysis/clustfcc/__init__.py b/src/haddock/modules/analysis/clustfcc/__init__.py index 69d52b19c..639c9b561 100644 --- a/src/haddock/modules/analysis/clustfcc/__init__.py +++ b/src/haddock/modules/analysis/clustfcc/__init__.py @@ -7,13 +7,15 @@ For more details please check *Rodrigues, J. P. et al. Proteins: Struct. Funct. Bioinform. 80, 1810–1817 (2012)* """ # noqa: E501 +import importlib.resources import os from pathlib import Path -from fcc.scripts import calc_fcc_matrix, cluster_fcc +import haddock from haddock import FCC_path, log from haddock.core.defaults import MODULE_DEFAULT_YAML from haddock.core.typing import Union +from haddock.fcc import calc_fcc_matrix, cluster_fcc from haddock.libs.libclust import ( add_cluster_info, get_cluster_matrix_plot_clt_dt, @@ -22,15 +24,15 @@ write_structure_list, ) from haddock.libs.libsubprocess import JobInputFirst -from haddock.modules import get_engine -from haddock.modules import BaseHaddockModule, read_from_yaml_config +from haddock.modules import BaseHaddockModule, get_engine, read_from_yaml_config +from haddock.modules.analysis import get_analysis_exec_mode from haddock.modules.analysis.clustfcc.clustfcc import ( get_cluster_centers, iterate_clustering, write_clusters, write_clustfcc_file, ) -from haddock.modules.analysis import get_analysis_exec_mode + RECIPE_PATH = Path(__file__).resolve().parent DEFAULT_CONFIG = Path(RECIPE_PATH, MODULE_DEFAULT_YAML) @@ -42,11 +44,11 @@ class HaddockModule(BaseHaddockModule): name = RECIPE_PATH.name def __init__( - self, - order: int, - path: Path, - initial_params: Union[Path, str] = DEFAULT_CONFIG, - ) -> None: + self, + order: int, + path: Path, + initial_params: Union[Path, str] = DEFAULT_CONFIG, + ) -> None: super().__init__(order, path, initial_params) @classmethod @@ -55,11 +57,15 @@ def confirm_installation(cls) -> None: dcfg = read_from_yaml_config(DEFAULT_CONFIG) exec_path = Path(FCC_path, dcfg["executable"]) - if not os.access(exec_path, mode=os.F_OK): - raise Exception(f"Required {str(exec_path)} file does not exist.") + dcfg["executable"] = Path( + importlib.resources.files(haddock), "bin", "contact_fcc" + ) - if not os.access(exec_path, mode=os.X_OK): - raise Exception(f"Required {str(exec_path)} file is not executable") + # if not os.access(exec_path, mode=os.F_OK): + # raise Exception(f"Required {str(exec_path)} file does not exist.") + # + # if not os.access(exec_path, mode=os.X_OK): + # raise Exception(f"Required {str(exec_path)} file is not executable") return @@ -81,9 +87,9 @@ def _run(self) -> None: contact_f, contact_executable, self.params["contact_distance_cutoff"], - ) + ) contact_jobs.append(job) - + exec_mode = get_analysis_exec_mode(self.params["mode"]) Engine = get_engine(exec_mode, self.params) @@ -104,22 +110,19 @@ def _run(self) -> None: if not_found: # No contacts were calculated, we cannot cluster - self.finish_with_error( - "Several files were not generated:" - f" {not_found}" - ) + self.finish_with_error("Several files were not generated:" f" {not_found}") log.info("Calculating the FCC matrix") parsed_contacts = calc_fcc_matrix.parse_contact_file( contact_file_l, False, - ) + ) # Imporant: matrix is a generator object, be careful with it matrix = calc_fcc_matrix.calculate_pairwise_matrix( parsed_contacts, False, - ) + ) # write the matrix to a file, so we can read it afterwards and don't # need to reinvent the wheel handling this @@ -136,26 +139,26 @@ def _run(self) -> None: fcc_matrix_f, self.params["clust_cutoff"], self.params["strictness"], - ) + ) # iterate clustering until at least one cluster is found clusters, min_population = iterate_clustering( pool, - self.params['min_population'], - ) - self.params['min_population'] = min_population + self.params["min_population"], + ) + self.params["min_population"] = min_population # Prepare output and read the elements if clusters: # Write the clusters write_clusters(clusters) - + # Get the cluster centers clt_dic, clt_centers = get_cluster_centers( clusters, models_to_clust, - ) - + ) + # ranking clusters _scores, sorted_score_dic = rank_clusters(clt_dic, min_population) @@ -167,45 +170,41 @@ def _run(self) -> None: models_to_clust, self.output_models, out_fname="clustfcc.tsv", - ) + ) write_clustfcc_file( - clusters, - clt_centers, - clt_dic, - self.params, - sorted_score_dic - ) + clusters, clt_centers, clt_dic, self.params, sorted_score_dic + ) else: log.warning("No clusters were found") self.output_models = models_to_clust # type: ignore # Draw the matrix - if self.params['plot_matrix']: + if self.params["plot_matrix"]: # Obtain final models indices final_order_idx, labels, cluster_ids = [], [], [] for pdb in self.output_models: final_order_idx.append(models_to_clust.index(pdb)) - labels.append(pdb.file_name.replace('.pdb', '')) + labels.append(pdb.file_name.replace(".pdb", "")) cluster_ids.append(pdb.clt_id) # Get custom cluster data matrix_cluster_dt, cluster_limits = get_cluster_matrix_plot_clt_dt( cluster_ids - ) + ) # Define output filename - html_matrix_basepath = 'fcc_matrix' + html_matrix_basepath = "fcc_matrix" # Plot matrix html_matrixpath = plot_cluster_matrix( fcc_matrix_f, final_order_idx, labels, - dttype='FCC', + dttype="FCC", diag_fill=1, output_fname=html_matrix_basepath, matrix_cluster_dt=matrix_cluster_dt, cluster_limits=cluster_limits, - ) + ) log.info(f"Plotting matrix in {html_matrixpath}") # Export models for next module diff --git a/src/haddock/modules/analysis/clustfcc/clustfcc.py b/src/haddock/modules/analysis/clustfcc/clustfcc.py index b03aa3a1e..8f1abcdcf 100644 --- a/src/haddock/modules/analysis/clustfcc/clustfcc.py +++ b/src/haddock/modules/analysis/clustfcc/clustfcc.py @@ -1,11 +1,12 @@ """FCC clustering.""" + import os from pathlib import Path import numpy as np -from fcc.scripts import cluster_fcc from haddock import log +from haddock.fcc.scripts import cluster_fcc def iterate_clustering(pool, min_population_param): @@ -16,30 +17,28 @@ def iterate_clustering(pool, min_population_param): ---------- pool : fcc.Pool The pool object containing the fcc matrix. - + min_population_param : int The min_population parameter to start the clustering process. - + Returns ------- clusters : list A list of clusters. - + min_population : int The min_population used to obtain the clusters. """ cluster_check = False while not cluster_check: for min_population in range(min_population_param, 0, -1): - log.info(f'Clustering with min_population={min_population}') + log.info(f"Clustering with min_population={min_population}") _, clusters = cluster_fcc.cluster_elements( pool, threshold=min_population, - ) + ) if not clusters: - log.info( - "[WARNING] No cluster was found, decreasing min_population!" - ) + log.info("[WARNING] No cluster was found, decreasing min_population!") else: cluster_check = True # pass the actual min_population back to the param dict @@ -59,18 +58,18 @@ def write_clusters(clusters, out_filename="cluster.out"): ---------- clusters : list A list of clusters. - + out_filename : str, optional The name of the output file. The default is "cluster.out". - + Returns ------- None """ # write the classic output file for compatibility reasons - log.info(f'Saving output to {out_filename}') + log.info(f"Saving output to {out_filename}") cluster_out = Path(out_filename) - with open(cluster_out, 'w') as fh: + with open(cluster_out, "w") as fh: cluster_fcc.output_clusters(fh, clusters) fh.close() @@ -83,15 +82,15 @@ def get_cluster_centers(clusters, models_to_cluster): ---------- clusters : list A list of clusters. - + models_to_cluster : list A list of models to cluster. - + Returns ------- clt_dic : dict A dictionary containing the clusters. - + clt_centers : dict A dictionary containing the cluster centers. """ @@ -114,7 +113,14 @@ def get_cluster_centers(clusters, models_to_cluster): return clt_dic, clt_centers -def write_clustfcc_file(clusters, clt_centers, clt_dic, params, sorted_score_dic, output_fname='clustfcc.txt'): # noqa: E501 +def write_clustfcc_file( + clusters, + clt_centers, + clt_dic, + params, + sorted_score_dic, + output_fname="clustfcc.txt", +): # noqa: E501 """ Write the clustfcc.txt file. @@ -122,45 +128,42 @@ def write_clustfcc_file(clusters, clt_centers, clt_dic, params, sorted_score_dic ---------- clusters : list A list of clusters. - + clt_centers : dict A dictionary containing the cluster centers. - + clt_dic : dict A dictionary containing the clusters. - + params : dict A dictionary containing the clustering parameters. - + sorted_score_dic : list A list of sorted scores. - + Returns ------- None """ # Prepare clustfcc.txt - output_str = f'### clustfcc output ###{os.linesep}' + output_str = f"### clustfcc output ###{os.linesep}" output_str += os.linesep - output_str += f'Clustering parameters {os.linesep}' + output_str += f"Clustering parameters {os.linesep}" output_str += ( "> contact_distance_cutoff=" f"{params['contact_distance_cutoff']}A" - f"{os.linesep}") - output_str += ( - f"> clust_cutoff={params['clust_cutoff']}" - f"{os.linesep}") + f"{os.linesep}" + ) + output_str += f"> clust_cutoff={params['clust_cutoff']}" f"{os.linesep}" output_str += f"> min_population={params['min_population']}{os.linesep}" - output_str += ( - f"> strictness={params['strictness']}{os.linesep}") + output_str += f"> strictness={params['strictness']}{os.linesep}" output_str += os.linesep output_str += ( - "Note: Models marked with * represent the center of the cluster" - f"{os.linesep}") - output_str += ( - f"-----------------------------------------------{os.linesep}") + "Note: Models marked with * represent the center of the cluster" f"{os.linesep}" + ) + output_str += f"-----------------------------------------------{os.linesep}" output_str += os.linesep - output_str += f'Total # of clusters: {len(clusters)}{os.linesep}' + output_str += f"Total # of clusters: {len(clusters)}{os.linesep}" for cluster_rank, _e in enumerate(sorted_score_dic, start=1): cluster_id, _ = _e @@ -169,7 +172,7 @@ def write_clustfcc_file(clusters, clt_centers, clt_dic, params, sorted_score_dic model_score_l.sort() # subset_score_l = [e[0] for e in model_score_l][:min_population] subset_score_l = [e[0] for e in model_score_l] - subset_score_l = subset_score_l[:params['min_population']] + subset_score_l = subset_score_l[: params["min_population"]] top_mean_score = np.mean(subset_score_l) top_std = np.std(subset_score_l) output_str += ( @@ -180,25 +183,24 @@ def write_clustfcc_file(clusters, clt_centers, clt_dic, params, sorted_score_dic f"n={len(model_score_l)}, " f"top{params['min_population']}_avg_score = {top_mean_score:.2f} " f"+-{top_std:.2f})" - f"{os.linesep}") + f"{os.linesep}" + ) output_str += os.linesep - output_str += f'clt_rank\tmodel_name\tscore{os.linesep}' + output_str += f"clt_rank\tmodel_name\tscore{os.linesep}" for model_ranking, element in enumerate(model_score_l, start=1): score, pdb = element if pdb.file_name == center_pdb.file_name: output_str += ( - f"{model_ranking}\t{pdb.file_name}\t{score:.2f}\t*" - f"{os.linesep}") + f"{model_ranking}\t{pdb.file_name}\t{score:.2f}\t*" f"{os.linesep}" + ) else: output_str += ( - f"{model_ranking}\t{pdb.file_name}\t{score:.2f}" - f"{os.linesep}") - output_str += ( - "-----------------------------------------------" - f"{os.linesep}") + f"{model_ranking}\t{pdb.file_name}\t{score:.2f}" f"{os.linesep}" + ) + output_str += "-----------------------------------------------" f"{os.linesep}" - log.info('Saving detailed output to clustfcc.txt') - with open(output_fname, 'w') as out_fh: + log.info("Saving detailed output to clustfcc.txt") + with open(output_fname, "w") as out_fh: out_fh.write(output_str) return From e733592449fdb3f248ff8a8ca8541aa2f31a8c1b Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 10 Sep 2024 17:42:52 +0200 Subject: [PATCH 07/62] update `setup.py` --- setup.py | 229 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 168 insertions(+), 61 deletions(-) diff --git a/setup.py b/setup.py index 08ce8a5c2..a40b41f96 100644 --- a/setup.py +++ b/setup.py @@ -1,99 +1,206 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- """Setup dot py.""" +import os +import shutil +import subprocess +import sys +import tempfile +import urllib.request import warnings from os.path import dirname, join +from pathlib import Path from setuptools import SetuptoolsDeprecationWarning, find_packages, setup -# Import warnings object for later filtering out +from setuptools.command.build_ext import build_ext from setuptools.command.easy_install import EasyInstallDeprecationWarning +from setuptools.command.install import install # Add warnings filtering to the Setup Deprecation Warnings warnings.filterwarnings("ignore", category=SetuptoolsDeprecationWarning) warnings.filterwarnings("ignore", category=EasyInstallDeprecationWarning) +with open("requirements.txt") as f: + requirements = f.read().splitlines() + def read(*names, **kwargs) -> str: """Read description files.""" path = join(dirname(__file__), *names) - with open(path, encoding=kwargs.get('encoding', 'utf8')) as fh: + with open(path, encoding=kwargs.get("encoding", "utf8")) as fh: return fh.read() # activate once added, do not remove -long_description = '{}\n{}'.format( - read('README.md'), - read('CHANGELOG.md'), - ) +long_description = "{}\n{}".format( + read("README.md"), + read("CHANGELOG.md"), +) + + +CNS_BINARY_URL = "" + + +class CustomBuildInstall(install): + def run(self): + self.run_command("build_ext") + install.run(self) + + +class CustomBuild(build_ext): + def run(self): + # Ensure bin directory exists + self.bin_dir = os.path.join(self.get_install_dir(), "haddock", "bin") + os.makedirs(self.bin_dir, exist_ok=True) + + # Build FCC + self.build_submodule( + name="FCC", + repo_url="https://github.com/haddocking/fcc.git", + build_cmd="make", + src_dir="src", + binary_name="contact_fcc", + ) + + # Build fast-rmsdmatrix + self.build_submodule( + name="fast-rmsdmatrix", + repo_url="https://github.com/mgiulini/fast-rmsdmatrix.git", + build_cmd="make fast-rmsdmatrix", + src_dir="src", + binary_name="fast-rmsdmatrix", + ) + + # Run original build_ext + build_ext.run(self) + + def get_install_dir(self): + install_cmd = self.get_finalized_command("install") + return install_cmd.install_lib + + def build_submodule(self, name, repo_url, build_cmd, src_dir, binary_name): + print(f"Building {name}...") + with tempfile.TemporaryDirectory() as temp_dir: + + # clone the repository + subprocess.run(["git", "clone", repo_url, temp_dir], check=True) + + # Build + build_dir = Path(temp_dir, src_dir) + subprocess.run( + build_cmd, + shell=True, + cwd=build_dir, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + print("pass!") + + # Move the binary + src_bin = Path(build_dir, binary_name) + dst_bin = Path(self.bin_dir, binary_name) + + shutil.copy2(src_bin, dst_bin) + + print(dst_bin) + + +class CNSInstall(CustomBuildInstall): + """Custom class to handle the download of the CNS binary""" + + def run(self): + + CustomBuildInstall.run(self) + + # Run the standard installation + # install.run(self) + + # Get the installation directory + if self.install_lib is None: + # Something went wrong with the installation + sys.exit(1) + + bin_dir = Path(self.install_lib, "haddock", "bin") + cns_exec = Path(bin_dir, "cns") + + # Create the `bin/` directory + bin_dir.mkdir(exist_ok=True) + + # Download the binary + if cns_exec.exists(): + cns_exec.unlink() + + urllib.request.urlretrieve(CNS_BINARY_URL, cns_exec) + + os.chmod(cns_exec, 0o755) setup( - name='haddock3', - version='3.0.0', - description='Haddock 3.', + name="haddock3", + version="3.0.0", + description="Haddock 3.", long_description=long_description, - long_description_content_type='text/markdown', - license='Apache License 2.0', - author='HADDOCK', - author_email='A.M.J.J.Bonvin@uu.nl', - url='https://github.com/haddocking/haddock3', - packages=find_packages('src'), - package_dir={'': 'src'}, + long_description_content_type="text/markdown", + license="Apache License 2.0", + author="HADDOCK", + author_email="A.M.J.J.Bonvin@uu.nl", + url="https://github.com/haddocking/haddock3", + packages=find_packages("src"), + package_dir={"": "src"}, # py_modules=[splitext(basename(i))[0] for i in glob("src/*.py")], include_package_data=True, + # package_data={"haddock3": ["fcc/src/*", "fast-rmsdmatrix/src/*"]}, zip_safe=False, classifiers=[ # complete classifier list: # http://pypi.python.org/pypi?%3Aaction=list_classifiers - 'Development Status :: 4 - Beta', - 'License :: OSI Approved :: Apache Software License', - 'Natural Language :: English', - 'Operating System :: POSIX', - 'Operating System :: POSIX :: Linux', - 'Operating System :: MacOS', - 'Programming Language :: Python :: 3.9', - ], + "Development Status :: 4 - Beta", + "License :: OSI Approved :: Apache Software License", + "Natural Language :: English", + "Operating System :: POSIX", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS", + "Programming Language :: Python :: 3.9", + ], project_urls={ - 'webpage': 'https://github.com/haddocking/haddock3', - 'Documentation': 'https://github.com/haddocking/haddock3#readme', - 'Changelog': '', - 'Issue Tracker': 'https://github.com/haddocking/haddock3/issues', - 'Discussion Forum': 'https://github.com/haddocking/haddock3/issues', - }, + "webpage": "https://github.com/haddocking/haddock3", + "Documentation": "https://github.com/haddocking/haddock3#readme", + "Changelog": "", + "Issue Tracker": "https://github.com/haddocking/haddock3/issues", + "Discussion Forum": "https://github.com/haddocking/haddock3/issues", + }, keywords=[ - 'Structural Biology', - 'Biochemistry', - 'Docking', - 'Protein docking', - 'Proteins', - ], - python_requires='>=3.9, <3.10', - install_requires=[ - # not added on purpose - ], - extras_require={ - }, - setup_requires=[ - ], + "Structural Biology", + "Biochemistry", + "Docking", + "Protein docking", + "Proteins", + ], + python_requires=">=3.9, <3.10", + install_requires=[requirements], + extras_require={}, + setup_requires=[], entry_points={ - 'console_scripts': [ - 'haddock3 = haddock.clis.cli:maincli', + "console_scripts": [ + "haddock3 = haddock.clis.cli:maincli", "haddock3-mpitask = haddock.clis.cli_mpi:maincli", - 'haddock3-bm = haddock.clis.cli_bm:maincli', - 'haddock3-cfg = haddock.clis.cli_cfg:maincli', - 'haddock3-clean = haddock.clis.cli_clean:maincli', - 'haddock3-copy = haddock.clis.cli_cp:maincli', - 'haddock3-dmn = haddock.clis.cli_dmn:maincli', - 'haddock3-pp = haddock.clis.cli_pp:maincli', - 'haddock3-score = haddock.clis.cli_score:maincli', - 'haddock3-unpack = haddock.clis.cli_unpack:maincli', - 'haddock3-analyse = haddock.clis.cli_analyse:maincli', - 'haddock3-traceback = haddock.clis.cli_traceback:maincli', - 'haddock3-re = haddock.clis.cli_re:maincli', - 'haddock3-restraints = haddock.clis.cli_restraints:maincli' - ] - }, + "haddock3-bm = haddock.clis.cli_bm:maincli", + "haddock3-cfg = haddock.clis.cli_cfg:maincli", + "haddock3-clean = haddock.clis.cli_clean:maincli", + "haddock3-copy = haddock.clis.cli_cp:maincli", + "haddock3-dmn = haddock.clis.cli_dmn:maincli", + "haddock3-pp = haddock.clis.cli_pp:maincli", + "haddock3-score = haddock.clis.cli_score:maincli", + "haddock3-unpack = haddock.clis.cli_unpack:maincli", + "haddock3-analyse = haddock.clis.cli_analyse:maincli", + "haddock3-traceback = haddock.clis.cli_traceback:maincli", + "haddock3-re = haddock.clis.cli_re:maincli", + "haddock3-restraints = haddock.clis.cli_restraints:maincli", + ] + }, + cmdclass={"build_ext": CustomBuild, "install": CNSInstall}, # cmdclass={'build_ext': optional_build_ext}, # ext_modules=[ # Extension( @@ -104,4 +211,4 @@ def read(*names, **kwargs) -> str: # for root, _, _ in os.walk('src') # for path in glob(join(root, '*.c')) # ], - ) +) From 7a6054e88ed9487361aa8f3b4d99c0b27dc9bf87 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 10 Sep 2024 17:44:22 +0200 Subject: [PATCH 08/62] refactor `defaults.py` to find new cns binary location --- src/haddock/core/defaults.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/haddock/core/defaults.py b/src/haddock/core/defaults.py index 003889a54..77e73089e 100644 --- a/src/haddock/core/defaults.py +++ b/src/haddock/core/defaults.py @@ -1,21 +1,21 @@ """All default parameters used by the framework.""" + +import importlib.resources import string import sys from pathlib import Path import yaml -from haddock import core_path, haddock3_repository_path, log +import haddock +from haddock import core_path, log # Locate the CNS binary -cns_exec = Path(haddock3_repository_path, "bin", "cns") +cns_exec = Path(importlib.resources.files(haddock) / "bin" / "cns") # type: ignore if not cns_exec.exists(): - log.error( - 'CNS executable `bin/cns` not found. ' - 'Please check the install instructions.' - ) - sys.exit() + log.error(f"CNS binary not found at {cns_exec}") + sys.exit(1) MODULE_PATH_NAME = "step_" @@ -40,16 +40,11 @@ MODULE_DEFAULT_YAML = "defaults.yaml" """Default name of the yaml default parameters file.""" -CNS_MODULES = ["rigidbody", - "flexref", - "emscoring", - "mdscoring", - "mdref", - "emref"] +CNS_MODULES = ["rigidbody", "flexref", "emscoring", "mdscoring", "mdref", "emref"] """List of CNS modules available in HADDOCK3.""" -with open(Path(core_path, "mandatory.yaml"), 'r') as fin: +with open(Path(core_path, "mandatory.yaml"), "r") as fin: _ycfg = yaml.safe_load(fin) max_molecules_allowed = _ycfg["molecules"]["maxitems"] del _ycfg From 1297e8847ec119a7841d992984358cbfacc6fd47 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 10 Sep 2024 18:21:00 +0200 Subject: [PATCH 09/62] tweak `setup.py` --- setup.py | 104 ++++++++++++++++++++++++------------------------------- 1 file changed, 46 insertions(+), 58 deletions(-) diff --git a/setup.py b/setup.py index a40b41f96..4114391cf 100644 --- a/setup.py +++ b/setup.py @@ -7,55 +7,35 @@ import sys import tempfile import urllib.request -import warnings from os.path import dirname, join from pathlib import Path -from setuptools import SetuptoolsDeprecationWarning, find_packages, setup +from setuptools import find_packages, setup from setuptools.command.build_ext import build_ext -from setuptools.command.easy_install import EasyInstallDeprecationWarning from setuptools.command.install import install -# Add warnings filtering to the Setup Deprecation Warnings -warnings.filterwarnings("ignore", category=SetuptoolsDeprecationWarning) -warnings.filterwarnings("ignore", category=EasyInstallDeprecationWarning) - -with open("requirements.txt") as f: - requirements = f.read().splitlines() - - -def read(*names, **kwargs) -> str: - """Read description files.""" - path = join(dirname(__file__), *names) - with open(path, encoding=kwargs.get("encoding", "utf8")) as fh: - return fh.read() - - -# activate once added, do not remove -long_description = "{}\n{}".format( - read("README.md"), - read("CHANGELOG.md"), -) - - CNS_BINARY_URL = "" class CustomBuildInstall(install): + """Custom Build and Install class""" + def run(self): self.run_command("build_ext") install.run(self) class CustomBuild(build_ext): + """CustomBuild handles the build of the C++ dependencies""" + def run(self): - # Ensure bin directory exists - self.bin_dir = os.path.join(self.get_install_dir(), "haddock", "bin") - os.makedirs(self.bin_dir, exist_ok=True) + # TODO: Find a smarter way of passing this `bin_dir` without defining it outside `__init__` + self.bin_dir = Path(self.get_install_dir(), "haddock", "bin") + self.bin_dir.mkdir(exist_ok=True, parents=True) # Build FCC - self.build_submodule( + self.clone_and_build_submodule( name="FCC", repo_url="https://github.com/haddocking/fcc.git", build_cmd="make", @@ -64,7 +44,7 @@ def run(self): ) # Build fast-rmsdmatrix - self.build_submodule( + self.clone_and_build_submodule( name="fast-rmsdmatrix", repo_url="https://github.com/mgiulini/fast-rmsdmatrix.git", build_cmd="make fast-rmsdmatrix", @@ -76,10 +56,14 @@ def run(self): build_ext.run(self) def get_install_dir(self): + """Get the directory in which HADDOCK was installed""" install_cmd = self.get_finalized_command("install") - return install_cmd.install_lib + return install_cmd.install_lib # type: ignore - def build_submodule(self, name, repo_url, build_cmd, src_dir, binary_name): + def clone_and_build_submodule( + self, name, repo_url, build_cmd, src_dir, binary_name + ): + """Clone a repository and build.""" print(f"Building {name}...") with tempfile.TemporaryDirectory() as temp_dir: @@ -96,7 +80,8 @@ def build_submodule(self, name, repo_url, build_cmd, src_dir, binary_name): stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) - print("pass!") + + # TODO: Add error handling # Move the binary src_bin = Path(build_dir, binary_name) @@ -104,58 +89,71 @@ def build_submodule(self, name, repo_url, build_cmd, src_dir, binary_name): shutil.copy2(src_bin, dst_bin) - print(dst_bin) - class CNSInstall(CustomBuildInstall): """Custom class to handle the download of the CNS binary""" def run(self): - - CustomBuildInstall.run(self) + """Run the installation""" # Run the standard installation - # install.run(self) + CustomBuildInstall.run(self) # Get the installation directory if self.install_lib is None: - # Something went wrong with the installation + print("Something went wrong during installation.") sys.exit(1) + # Set where the cns binary needs to be bin_dir = Path(self.install_lib, "haddock", "bin") - cns_exec = Path(bin_dir, "cns") # Create the `bin/` directory bin_dir.mkdir(exist_ok=True) # Download the binary + cns_exec = Path(bin_dir, "cns") if cns_exec.exists(): cns_exec.unlink() urllib.request.urlretrieve(CNS_BINARY_URL, cns_exec) + # TODO: Handle failed download + os.chmod(cns_exec, 0o755) +with open("requirements.txt", "r", encoding="utf-8") as f: + requirements = f.read().splitlines() + + +def read_description(*names, **kwargs) -> str: + """Read description files.""" + path = join(dirname(__file__), *names) + with open(path, encoding=kwargs.get("encoding", "utf8")) as fh: + return fh.read() + + +readme = read_description("README.md") +changelog = read_description("CHANGELOG.md") +long_description = f"{readme}{os.linesep}{changelog}" + + setup( name="haddock3", version="3.0.0", - description="Haddock 3.", + description="HADDOCK3", long_description=long_description, long_description_content_type="text/markdown", license="Apache License 2.0", - author="HADDOCK", - author_email="A.M.J.J.Bonvin@uu.nl", + author="BonvinLab", + author_email="bonvinlab.support@uu.nl", url="https://github.com/haddocking/haddock3", packages=find_packages("src"), package_dir={"": "src"}, - # py_modules=[splitext(basename(i))[0] for i in glob("src/*.py")], include_package_data=True, - # package_data={"haddock3": ["fcc/src/*", "fast-rmsdmatrix/src/*"]}, zip_safe=False, classifiers=[ - # complete classifier list: - # http://pypi.python.org/pypi?%3Aaction=list_classifiers + # TODO: Update the classifiers - http://pypi.python.org/pypi?%3Aaction=list_classifiers "Development Status :: 4 - Beta", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", @@ -165,7 +163,7 @@ def run(self): "Programming Language :: Python :: 3.9", ], project_urls={ - "webpage": "https://github.com/haddocking/haddock3", + "webpage": "https://bonvinlab.org/haddock3", "Documentation": "https://github.com/haddocking/haddock3#readme", "Changelog": "", "Issue Tracker": "https://github.com/haddocking/haddock3/issues", @@ -201,14 +199,4 @@ def run(self): ] }, cmdclass={"build_ext": CustomBuild, "install": CNSInstall}, - # cmdclass={'build_ext': optional_build_ext}, - # ext_modules=[ - # Extension( - # splitext(relpath(path, 'src').replace(os.sep, '.'))[0], - # sources=[path], - # include_dirs=[dirname(path)] - # ) - # for root, _, _ in os.walk('src') - # for path in glob(join(root, '*.c')) - # ], ) From e404507d1894824b6e5dbdeb2949990c0f12f653 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 10 Sep 2024 18:21:11 +0200 Subject: [PATCH 10/62] typo --- src/haddock/modules/analysis/clustfcc/clustfcc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/haddock/modules/analysis/clustfcc/clustfcc.py b/src/haddock/modules/analysis/clustfcc/clustfcc.py index 8f1abcdcf..5933d3084 100644 --- a/src/haddock/modules/analysis/clustfcc/clustfcc.py +++ b/src/haddock/modules/analysis/clustfcc/clustfcc.py @@ -6,7 +6,7 @@ import numpy as np from haddock import log -from haddock.fcc.scripts import cluster_fcc +from haddock.fcc import cluster_fcc def iterate_clustering(pool, min_population_param): From 120e8e3db8c084a811413adde778df81dc306f71 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 10 Sep 2024 18:26:40 +0200 Subject: [PATCH 11/62] remove unused fcc scripts --- src/haddock/fcc/make_contacts.py | 122 ---------------------------- src/haddock/fcc/pdb_chainxseg.py | 78 ------------------ src/haddock/fcc/ppretty_clusters.py | 84 ------------------- 3 files changed, 284 deletions(-) delete mode 100755 src/haddock/fcc/make_contacts.py delete mode 100755 src/haddock/fcc/pdb_chainxseg.py delete mode 100755 src/haddock/fcc/ppretty_clusters.py diff --git a/src/haddock/fcc/make_contacts.py b/src/haddock/fcc/make_contacts.py deleted file mode 100755 index 60b190f08..000000000 --- a/src/haddock/fcc/make_contacts.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env python -# -*- coding: UTF-8 -*- - -""" -Script to calculate contact lists on PDB files. -Requires external executable to calculate the contacts! - -Authors: - RODRIGUES Joao - TRELLET Mikael - MELQUIOND Adrien -""" - -from multiprocessing import Process -from subprocess import Popen, PIPE - -def _calculate_contacts(executable, pdbfile, d_cutoff, filter_selection=None, extension='.contacts'): - """ - Outputs a list of contacts based on vector analysis - of the PDB file. - - Arguments: - executable - path to contact calculation program - pdbfile - path to PDB-formatted file (.pdb extension) - d_cutoff - minimal distance in A to consider a contact (float) - filter_selection - list of identifiers to filter contacts (list of strings) - """ - - pdbname = os.path.basename(pdbfile)[:-4] - - p = Popen([executable, pdbfile, d_cutoff], stdout=PIPE) - p_output = p.communicate()[0] - contacts = sorted(list(set([l for l in p_output.split('\n')][:-1]))) - - # Filter contacts - if filter_selection: - contacts = filter(lambda x: x[5] in filter_selection and x[-1] in filter_selection, contacts) - # - - outfile = os.path.join(os.path.dirname(pdbfile), "%s%s" %(pdbname, extension)) - with open(outfile, 'w') as o: - o.write('\n'.join(contacts)) - - return 0 - -if __name__ == '__main__': - - import optparse - import os, sys - - USAGE = "%s [-f structures.txt] [-n 4] [-c 5.0] file1.pdb file2.pdb" - - parser = optparse.OptionParser(usage=USAGE) - parser.add_option('-c', '--cutoff', dest="d_cutoff", action='store', type='string', - default="5.0", - help='Distance cutoff to evaluate contacts. [default: 5.0A]') - parser.add_option('-f', '--file', dest="input_file", action='store', type='string', - help='Input file (one file path per line)') - parser.add_option('-n', '--nproc', dest="nproc", action='store', type='string', - default=1, - help='Number of simultaneous processes to launch in each round. [default: 1]') - parser.add_option('-e', '--exec', dest="executable", action='store', type='string', - default='%s/../src/contact_fcc' %os.path.dirname(sys.argv[0]), - help='Path to the executable C++ program to calculate contacts [default: ../fcc/src/contact_fcc]') - parser.add_option('-s', '--selection', dest="selection", action='store', type='string', - default=None, - help='Filter contacts based on their segids. [Default: No filtering. All chains] [Example: A,C]') - - (options, args) = parser.parse_args() - - if options.input_file: - args = [name.strip() for name in open(options.input_file) if name.strip()] - - if not args: - print "No files provided. Exiting" - print USAGE - sys.exit(1) - - # Convert to full paths - args = map(os.path.abspath, args) - - nproc = int(options.nproc) - cutoff = options.d_cutoff - - executable = options.executable - if not os.path.exists(executable): - print "Path not found: %s" %os.path.abspath(executable) - sys.exit(1) - executable = os.path.abspath(executable) - - if options.selection: - filter_selection = set(options.selection.split(',')) - representative = open(args[0]) - repr_chains = dict([(j,str(i)) for i,j in enumerate(sorted(set([l[72] for l in representative if l.startswith('ATOM')])), start=1)]) - filter_selection = map(repr_chains.get, filter_selection) - representative.close() - oextension = '.contacts-'+''.join(options.selection.split(',')) - else: - filter_selection = None - oextension = '.contacts' - - queue = [] - - while 1: - - arg = args.pop() - # Create Process for arg - p = Process(target=_calculate_contacts, args=(executable, arg, cutoff, filter_selection, oextension)) - queue.append(p) - - if (len(queue) == nproc) or (not args and len(queue)): - - for job in queue: - job.start() - for job in queue: # Waiting for job to finish - job.join() - queue = [] - - if not args and not queue: - break - - print "Finished" diff --git a/src/haddock/fcc/pdb_chainxseg.py b/src/haddock/fcc/pdb_chainxseg.py deleted file mode 100755 index d9204e7c0..000000000 --- a/src/haddock/fcc/pdb_chainxseg.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python - -""" -Exchanges segment ID for chain ID in a PDB file. - -usage: python pdb_chainxseg.py -example: python pdb_chainxseg.py 1CTF.pdb - -This program is part of the PDB tools distributed with HADDOCK -or with the HADDOCK tutorial. The utilities in this package -can be used to quickly manipulate PDB files, with the benefit -of 'piping' several different commands. This is a rewrite of old -FORTRAN77 code that was taking too much effort to compile. RIP. -""" - -import os -import re -import sys - -__author__ = "Joao Rodrigues" - -USAGE = "usage: " + sys.argv[0] + " \n" - -def check_input(args): - """Checks whether to read from stdin/file and validates user input/options.""" - - if not len(args): - # Read from pipe - if not sys.stdin.isatty(): - pdbfh = sys.stdin - else: - sys.stderr.write(USAGE) - sys.exit(1) - elif len(args) == 1: - # Read from file - if not os.path.exists(args[0]): - sys.stderr.write('File not found: ' + args[0] + '\n') - sys.stderr.write(USAGE) - sys.exit(1) - pdbfh = open(args[0], 'r') - else: - sys.stderr.write(USAGE) - sys.exit(1) - - return pdbfh - -def _swap_chainxseg(fhandle): - """Enclosing logic in a function to speed up a bit""" - - coord_re = re.compile('^(ATOM|HETATM)') - fhandle = fhandle - - for line in fhandle: - if coord_re.match(line): - yield line[:72] + line[21].ljust(4) + line[76:] - else: - yield line - -if __name__ == '__main__': - # Check Input - pdbfh = check_input(sys.argv[1:]) - - # Do the job - new_pdb = _swap_chainxseg(pdbfh) - - try: - sys.stdout.write(''.join(new_pdb)) - sys.stdout.flush() - except IOError: - # This is here to catch Broken Pipes - # for example to use 'head' or 'tail' without - # the error message showing up - pass - - # last line of the script - # We can close it even if it is sys.stdin - pdbfh.close() - sys.exit(0) \ No newline at end of file diff --git a/src/haddock/fcc/ppretty_clusters.py b/src/haddock/fcc/ppretty_clusters.py deleted file mode 100755 index bf42582c3..000000000 --- a/src/haddock/fcc/ppretty_clusters.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env python - -""" -Outputs the names of the cluster members based on a clustering output file -and the original file listing the PDB files - -Authors: - RODRIGUES Joao -""" - -import os -import sys - -USAGE = "python %s " %os.path.basename(sys.argv[0]) - -def read_clusters(path): - """ - Reads clusters from a FCC output file. - """ - - clusters = [] - cl_file = open(path, 'r') - for line in cl_file: - # Cluster 8 -> 193 141 142 144 151 168 171 172 178 - models = map(int, line.split()[3:]) - clusters.append(models) - - return clusters - -def read_list(path): - """ - Reads a list containing one file per line. - Returns an index of line number - line content - """ - - with open(path, 'r') as fhandle: - fdata = {} - for nline, line in enumerate(fhandle): - if not line.strip(): - continue - # Remove extension - fdata[nline+1] = '.'.join(line.strip().split('.')[:-1]) - - return fdata - -def cross_data(clusters, flist): - """ - Matches names in flist to the numbers in clusters. - """ - - named_clusters = [] - for cl in clusters: - ncl = [flist[s] for s in cl] - named_clusters.append(ncl) - - return named_clusters - -if __name__ == '__main__': - - if len(sys.argv[1:]) != 2: - print USAGE - sys.exit(1) - - - cluster_file = os.path.abspath(sys.argv[1]) - pdblist_file = os.path.abspath(sys.argv[2]) - - try: - cl_list = read_clusters(cluster_file) - except IOError: - sys.stderr.write('Error: file not found (%s)\nAborting..\n' %cluster_file) - sys.exit(1) - - try: - pdb_list = read_list(pdblist_file) - except IOError: - sys.stderr.write('Error: file not found (%s)\nAborting..\n' %pdblist_file) - sys.exit(1) - - named_clusters = cross_data(cl_list, pdb_list) - - # Output - for i, nc in enumerate(named_clusters): - print "Cluster %i -> %s" %(i+1, ' '.join(nc)) From 41d6ff0c04a086bb00c32a21ff5354f9b0484fc0 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 10 Sep 2024 18:28:24 +0200 Subject: [PATCH 12/62] remove `main` from fcc scripts --- src/haddock/fcc/calc_fcc_matrix.py | 90 ------------------ src/haddock/fcc/cluster_fcc.py | 144 ++++++++--------------------- 2 files changed, 39 insertions(+), 195 deletions(-) diff --git a/src/haddock/fcc/calc_fcc_matrix.py b/src/haddock/fcc/calc_fcc_matrix.py index 330db24b2..c1e7e2749 100755 --- a/src/haddock/fcc/calc_fcc_matrix.py +++ b/src/haddock/fcc/calc_fcc_matrix.py @@ -94,93 +94,3 @@ def _output_fcc(output, values, f_buffer): ) buf = [] output("".join(["%s %s %1.3f %1.3f\n" % (i[0], i[1], i[2], i[3]) for i in buf])) - - -if __name__ == "__main__": - - import optparse - import os - import sys - from time import ctime, time - - USAGE = "%s ... [options]\n" % os.path.basename( - sys.argv[0] - ) - - parser = optparse.OptionParser(usage=USAGE) - parser.add_option( - "-o", - "--output", - dest="output_file", - action="store", - type="string", - default=sys.stdout, - help="Output File [default: STDOUT]", - ) - parser.add_option( - "-f", - "--file", - dest="input_file", - action="store", - type="string", - help="Input file (one contact file name per line)", - ) - parser.add_option( - "-b", - "--buffer_size", - dest="buffer_size", - action="store", - type="string", - default=50000, - help="Buffer size for writing output. Number of lines to cache before writing to file [default: 50000]", - ) - parser.add_option( - "-i", - "--ignore_chain", - dest="ignore_chain_char", - action="store_true", - help="Ignore chain character in residue code. Use for homomeric complexes.", - ) - - (options, args) = parser.parse_args() - - if options.input_file: - args = [name.strip() for name in open(options.input_file)] - - if len(args) < 2: - sys.stderr.write( - "- Provide (at least) two structures to calculate a matrix. You provided %s.\n" - % len(args) - ) - sys.stderr.write(USAGE) - sys.exit(1) - - sys.stderr.write("+ BEGIN: %s\n" % ctime()) - if options.ignore_chain_char: - sys.stderr.write("+ Ignoring chains. Expect a considerable slowdown!!\n") - exclude_chains = True - else: - exclude_chains = False - - t_init = time() - sys.stderr.write("+ Parsing %i contact files\n" % len(args)) - - c = parse_contact_file(args, exclude_chains) - - m = calculate_pairwise_matrix(c, exclude_chains) - - if isinstance(options.output_file, str): - f = open(options.output_file, "w") - else: - f = options.output_file - - sys.stderr.write( - "+ Calculating Matrix\n" - ) # Matrix is calculated when writing. Generator property. - sys.stderr.write("+ Writing matrix to %s\n" % f.name) - _output_fcc(f.write, m, options.buffer_size) - - if isinstance(options.output_file, str): - f.close() - t_elapsed = time() - t_init - sys.stderr.write("+ END: %s [%6.2f seconds elapsed]\n" % (ctime(), t_elapsed)) diff --git a/src/haddock/fcc/cluster_fcc.py b/src/haddock/fcc/cluster_fcc.py index 08ceb64fe..a5fc5e365 100755 --- a/src/haddock/fcc/cluster_fcc.py +++ b/src/haddock/fcc/cluster_fcc.py @@ -10,17 +10,17 @@ MELQUIOND Adrien """ + class Element(object): """Defines a 'clusterable' Element""" - __slots__ = ['name', 'cluster', 'neighbors'] + __slots__ = ["name", "cluster", "neighbors"] def __init__(self, name): self.name = name self.cluster = 0 self.neighbors = set() - def add_neighbor(self, neighbor): """Adds another element to the neighbor list""" self.neighbors.add(neighbor) @@ -29,26 +29,27 @@ def assign_cluster(self, clust_id): """Assigns the Element to Cluster. 0 if unclustered""" self.cluster = clust_id + class Cluster(object): """Defines a Cluster. A Cluster is created with a name and a center (Element class)""" - __slots__ = ['name', 'center', 'members'] + __slots__ = ["name", "center", "members"] def __init__(self, name, center): - + self.name = name self.center = center self.members = [] - + self.populate() - + def __len__(self): - return len(self.members)+1 # +1 Center - + return len(self.members) + 1 # +1 Center + def populate(self): """ - Populates the Cluster member list through the + Populates the Cluster member list through the neighbor list of its center. """ @@ -56,14 +57,14 @@ def populate(self): # Assign center ctr = self.center ctr.assign_cluster(name) - + mlist = self.members # Assign members ctr_nlist = (n for n in ctr.neighbors if not n.cluster) for e in ctr_nlist: mlist.append(e) e.assign_cluster(name) - + def add_member(self, element): """ Adds one single element to the cluster. @@ -72,12 +73,13 @@ def add_member(self, element): l.append(element) element.assign_cluster(self.name) + def read_matrix(path, cutoff, strictness): - """ - Reads in a four column matrix (1 2 0.123 0.456\n) + """ + Reads in a four column matrix (1 2 0.123 0.456\n) and creates an dictionary of Elements. - - The strictness factor is a that multiplies by the cutoff + + The strictness factor is a that multiplies by the cutoff to produce a new cutoff for the second half of the matrix. Used to allow some variability while keeping very small interfaces from clustering with anything remotely similar. @@ -85,10 +87,10 @@ def read_matrix(path, cutoff, strictness): cutoff = float(cutoff) partner_cutoff = float(cutoff) * float(strictness) - + elements = {} - f = open(path, 'r') + f = open(path, "r") for line in f: ref, mobi, dRM, dMR = line.split() ref = int(ref) @@ -102,12 +104,12 @@ def read_matrix(path, cutoff, strictness): elements[ref] = r else: r = elements[ref] - + if mobi not in elements: m = Element(mobi) elements[mobi] = m else: - m = elements[mobi] + m = elements[mobi] # Assign neighbors if dRM >= cutoff and dMR >= partner_cutoff: @@ -119,9 +121,10 @@ def read_matrix(path, cutoff, strictness): return elements + def remove_true_singletons(element_pool): - """ Removes from the pool elements without any neighbor """ - + """Removes from the pool elements without any neighbor""" + ep = element_pool ts = set([e for e in ep if not ep[e].neighbors]) @@ -137,30 +140,33 @@ def remove_true_singletons(element_pool): return (ts, ep) + def cluster_elements(element_pool, threshold): - """ - Groups Elements within a given threshold + """ + Groups Elements within a given threshold together in the same cluster. """ - + clusters = [] - threshold -= 1 # Account for center + threshold -= 1 # Account for center ep = element_pool - cn = 1 # Cluster Number + cn = 1 # Cluster Number while 1: # Clusterable elements ce = [e for e in ep if not ep[e].cluster] - if not ce: # No more elements to cluster + if not ce: # No more elements to cluster break - + # Select Cluster Center # Element with largest neighbor list - ctr_nlist, ctr = sorted([(len([se for se in ep[e].neighbors if not se.cluster]), e) for e in ce])[-1] + ctr_nlist, ctr = sorted( + [(len([se for se in ep[e].neighbors if not se.cluster]), e) for e in ce] + )[-1] # Cluster until length of remaining elements lists are above threshold if ctr_nlist < threshold: break - + # Create Cluster c = Cluster(cn, ep[ctr]) cn += 1 @@ -168,86 +174,14 @@ def cluster_elements(element_pool, threshold): return (ep, clusters) + def output_clusters(handle, clusters): """Outputs the cluster name, center, and members.""" write = handle.write for c in clusters: - write( "Cluster %s -> %s " %(c.name, c.center.name) ) + write("Cluster %s -> %s " % (c.name, c.center.name)) for m in sorted(c.members, key=lambda k: k.name): - write( "%s " %m.name ) + write("%s " % m.name) write("\n") - -if __name__ == "__main__": - - import optparse - import sys - from time import time, ctime - import os - - USAGE="%s [options]" %os.path.basename(sys.argv[0]) - - parser = optparse.OptionParser(usage=USAGE) - parser.add_option('-o', '--output', dest="output_handle", action='store', type='str', - default=sys.stdout, - help='Output File [STDOUT]') - parser.add_option('-c', '--cluster-size', dest="clus_size", action="store", type="int", - default=4, - help="Minimum number of elements in a cluster [4]") - parser.add_option('-s', '--strictness', dest="strictness", action="store", type='float', - default=0.75, - help="Multiplier for cutoff for M->R inclusion threshold. [0.75 or effective cutoff of 0.5625]") - - - (options, args) = parser.parse_args() - - if sys.version_info[0:2] < (2,6): - cur_version = "%s.%s" %sys.version_info[0:2] - sys.stderr.write("- Python version not supported (%s). Please use 2.5 or newer.\n" %cur_version ) - sys.exit(1) - if len(args) != 2: - sys.stderr.write("- Invalid number of arguments: %i\n" %len(args)) - sys.stderr.write("USAGE: %s\n" %USAGE) - sys.exit(1) - - fmatrix, cutoff = args - cutoff = float(cutoff) - - # Read Matrix - sys.stderr.write("+ BEGIN: %s\n" %ctime()) - t_init = time() - - try: - pool = read_matrix(fmatrix, cutoff, options.strictness) - except IOError: - sys.stderr.write("File not found: %s\n" %fmatrix) - sys.exit(1) - - sys.stderr.write("+ Read %ix%i distance matrix in %i seconds\n" %(len(pool), len(pool), int(time()-t_init))) - - # ts, pool = remove_true_singletons(pool) - # sys.stderr.write("+ Detected %i True Singletons\n" %len(ts)) - - # Cluster - element_pool, clusters = cluster_elements(pool, options.clus_size) - - # Output Clusters - o = options.output_handle - if isinstance(o, str): - o_handle = open(o, 'w') - else: - o_handle = o - - sys.stderr.write("+ Writing %i Clusters\n" %len(clusters)) - output_clusters(o_handle, clusters) - if isinstance(o, str): - o_handle.close() - - total_elements = len(element_pool) - clustered = sum([len(c) for c in clusters]) - # Calculate coverage - clust_coverage = clustered*100/float(total_elements) - sys.stderr.write("+ Coverage %3.2f%% (%i/%i)\n" %(clust_coverage, clustered, total_elements)) - t_elapsed = time()-t_init - sys.stderr.write( "+ END: %s [%3.2f seconds]\n" %(ctime(), t_elapsed)) From 7ebba33e9f0e73b8c5a94656035be2e0e54db359 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Wed, 11 Sep 2024 13:24:04 +0200 Subject: [PATCH 13/62] handle errors in custom install class --- setup.py | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 4114391cf..ad0658159 100644 --- a/setup.py +++ b/setup.py @@ -2,6 +2,7 @@ # -*- encoding: utf-8 -*- """Setup dot py.""" import os +import platform import shutil import subprocess import sys @@ -15,21 +16,43 @@ from setuptools.command.install import install -CNS_BINARY_URL = "" - - class CustomBuildInstall(install): """Custom Build and Install class""" def run(self): + + issues = self.flycheck() + if issues: + print("Issues found, installation cannot continue:") + for issue in issues: + print(f" - {issue}") + sys.exit(1) self.run_command("build_ext") install.run(self) + @staticmethod + def flycheck() -> list[str]: + """Helper method to make sure all system dependencies are installed""" + issues = [] + + if shutil.which("git") is None: + issues.append("git not found in $PATH") + + if shutil.which("make") is None: + issues.append("make not found in $PATH") + + python_version = sys.version_info + if python_version.major != 3 or python_version.minor < 9: + issues.append("Python 3.9+ is required, found: {sys.version}") + + return issues + class CustomBuild(build_ext): """CustomBuild handles the build of the C++ dependencies""" def run(self): + print("Building HADDOCK3 C++ dependencies...") # TODO: Find a smarter way of passing this `bin_dir` without defining it outside `__init__` self.bin_dir = Path(self.get_install_dir(), "haddock", "bin") self.bin_dir.mkdir(exist_ok=True, parents=True) @@ -60,7 +83,7 @@ def get_install_dir(self): install_cmd = self.get_finalized_command("install") return install_cmd.install_lib # type: ignore - def clone_and_build_submodule( + def clone_and_build_submodule( # pylint: disable=too-many-arguments self, name, repo_url, build_cmd, src_dir, binary_name ): """Clone a repository and build.""" @@ -90,7 +113,16 @@ def clone_and_build_submodule( shutil.copy2(src_bin, dst_bin) -class CNSInstall(CustomBuildInstall): +CNS_BINARIES = { + "x86_64-linux": "https://surfdrive.surf.nl/files/index.php/s/o8X7zZezIM3P0cE", # linux + "x86_64-darwin": "", # macOs + "arm64-darwin": "", # Mac M1/M2 + "aarch64-linux": "", # linux ARM + "armv7l-linux": "", # 32-bit ARM linux, like raspberryPi +} + + +class CustomInstall(CustomBuildInstall): """Custom class to handle the download of the CNS binary""" def run(self): @@ -115,12 +147,44 @@ def run(self): if cns_exec.exists(): cns_exec.unlink() - urllib.request.urlretrieve(CNS_BINARY_URL, cns_exec) + arch = self.get_arch() - # TODO: Handle failed download + if arch not in CNS_BINARIES: + print(f"Unknown architecture: {arch}") + print( + "Please set the CNS binary manually inside your configuration file as `cns_exec`", + ) + return + + cns_binary_url = CNS_BINARIES[arch] + + status, msg = self.download_file(cns_binary_url, cns_exec) + if not status: + print(msg) + print( + "Please set the CNS binary manually inside your configuration file as `cns_exec`" + ) + return os.chmod(cns_exec, 0o755) + @staticmethod + def download_file(url, dest) -> tuple[bool, str]: + """Download a file from a URL""" + try: + urllib.request.urlretrieve(url, dest) + return True, "Download successful" + except Exception as e: # pylint: disable=broad-except + return False, f"Download failed: {e}" + + @staticmethod + def get_arch(): + """Helper function to figure out the architetchure""" + system = platform.system().lower() + machine = platform.machine().lower() + + return f"{machine}-{system}" + with open("requirements.txt", "r", encoding="utf-8") as f: requirements = f.read().splitlines() @@ -196,7 +260,8 @@ def read_description(*names, **kwargs) -> str: "haddock3-traceback = haddock.clis.cli_traceback:maincli", "haddock3-re = haddock.clis.cli_re:maincli", "haddock3-restraints = haddock.clis.cli_restraints:maincli", + "haddock3-check = haddock.clis.cli_check_install:main", ] }, - cmdclass={"build_ext": CustomBuild, "install": CNSInstall}, + cmdclass={"build_ext": CustomBuild, "install": CustomInstall}, ) From b3c8f5f935127c50bcd373d0e999545794db18cb Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Wed, 11 Sep 2024 13:24:32 +0200 Subject: [PATCH 14/62] add cli_check_install this CLI checks if the binaries are in the proper locations --- src/haddock/clis/cli_check_install.py | 53 +++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/haddock/clis/cli_check_install.py diff --git a/src/haddock/clis/cli_check_install.py b/src/haddock/clis/cli_check_install.py new file mode 100644 index 000000000..7e5cc14a7 --- /dev/null +++ b/src/haddock/clis/cli_check_install.py @@ -0,0 +1,53 @@ +"""Check if HADDOCK3 was installed correctly.""" + +import importlib.resources +import logging +import platform +import sys +from pathlib import Path + +import haddock + + +logging.basicConfig( + level=logging.INFO, + format="[%(asctime)s - %(levelname)s] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", +) + + +BINARY_DIR = Path(importlib.resources.files(haddock) / "bin") # type: ignore + +BINARIES = { + "CNS": "cns", + "FCC": "contact_fcc", + "Fast-RMSDMatrix": "fast-rmsdmatrix", +} + + +def check_binary(software: str, binary_name: str) -> None: + """Check if the binary is installed.""" + expected_binary_location = Path(BINARY_DIR, binary_name) + if expected_binary_location.exists(): + logging.info(f"✅ {software} binary `{binary_name}` OK") + else: + logging.info( + f"⚠️ {software} binary `{binary_name}` not properly installed " + "- define it manually in the configuration file" + ) + + +def main(): + """Check if the installation was successful.""" + system = platform.system().lower() + machine = platform.machine().lower() + python_version = f"{sys.version_info.major}.{sys.version_info.minor}" + + logging.info("Checking HADDOCK3 installation...") + logging.info(f"ℹ️ Python{python_version} running on {machine}-{system}") + for software, binary_name in BINARIES.items(): + check_binary(software, binary_name) + + +if __name__ == "__main__": + main() From e0efeb59a8c547911d296fceda9c966c6158b240 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Wed, 11 Sep 2024 13:39:58 +0200 Subject: [PATCH 15/62] refactor binary paths [wip] --- src/haddock/core/defaults.py | 13 ++++--- .../modules/analysis/clustfcc/__init__.py | 38 ++++++++++++------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/haddock/core/defaults.py b/src/haddock/core/defaults.py index 77e73089e..4a9e7ddf0 100644 --- a/src/haddock/core/defaults.py +++ b/src/haddock/core/defaults.py @@ -2,20 +2,21 @@ import importlib.resources import string -import sys from pathlib import Path import yaml import haddock -from haddock import core_path, log +from haddock import core_path -# Locate the CNS binary -cns_exec = Path(importlib.resources.files(haddock) / "bin" / "cns") # type: ignore +BINARY_DIR = Path(importlib.resources.files(haddock) / "bin") # type: ignore + +cns_exec = Path(BINARY_DIR, "cns") if not cns_exec.exists(): - log.error(f"CNS binary not found at {cns_exec}") - sys.exit(1) + # This means the CNS binary is not in the default location + # and should be set manually in the configuration file + cns_exec = None MODULE_PATH_NAME = "step_" diff --git a/src/haddock/modules/analysis/clustfcc/__init__.py b/src/haddock/modules/analysis/clustfcc/__init__.py index 639c9b561..2143e0f88 100644 --- a/src/haddock/modules/analysis/clustfcc/__init__.py +++ b/src/haddock/modules/analysis/clustfcc/__init__.py @@ -13,7 +13,7 @@ import haddock from haddock import FCC_path, log -from haddock.core.defaults import MODULE_DEFAULT_YAML +from haddock.core.defaults import BINARY_DIR, MODULE_DEFAULT_YAML from haddock.core.typing import Union from haddock.fcc import calc_fcc_matrix, cluster_fcc from haddock.libs.libclust import ( @@ -54,20 +54,32 @@ def __init__( @classmethod def confirm_installation(cls) -> None: """Confirm if FCC is installed and available.""" - dcfg = read_from_yaml_config(DEFAULT_CONFIG) - exec_path = Path(FCC_path, dcfg["executable"]) - - dcfg["executable"] = Path( - importlib.resources.files(haddock), "bin", "contact_fcc" - ) + # The FCC binary can be either in the default binary path or in the - # if not os.access(exec_path, mode=os.F_OK): - # raise Exception(f"Required {str(exec_path)} file does not exist.") - # - # if not os.access(exec_path, mode=os.X_OK): - # raise Exception(f"Required {str(exec_path)} file is not executable") + dcfg = read_from_yaml_config(DEFAULT_CONFIG) + default_binary = Path(BINARY_DIR, "contact_fcc") + user_supplied_binary = Path(dcfg["executable"]) - return + if user_supplied_binary.exists(): + log.info("Using user supplied FCC binary: %s", user_supplied_binary) + else: + log.warning("User supplied FCC binary not found: %s", user_supplied_binary) + if default_binary.exists(): + log.info("Using default FCC binary: %s", default_binary) + dcfg["executable"] = default_binary + else: + log.error("Default FCC binary not found: %s", default_binary) + raise FileNotFoundError( + f"Default FCC binary not found: {default_binary}" + ) + + # Check if the binary is executable + exec_path = Path(dcfg["executable"]) + if not os.access(exec_path, mode=os.F_OK): + raise Exception(f"Required {str(exec_path)} file does not exist.") + + if not os.access(exec_path, mode=os.X_OK): + raise Exception(f"Required {str(exec_path)} file is not executable") def _run(self) -> None: """Execute module.""" From ff21e0bbf1ea18642ebedde9793b584d45303d8b Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Mon, 16 Sep 2024 16:31:27 +0200 Subject: [PATCH 16/62] add c++/c code --- src/haddock/deps/contact_fcc.cpp | 124 ++++ src/haddock/deps/fast-rmsdmatrix.c | 883 +++++++++++++++++++++++++++++ 2 files changed, 1007 insertions(+) create mode 100755 src/haddock/deps/contact_fcc.cpp create mode 100644 src/haddock/deps/fast-rmsdmatrix.c diff --git a/src/haddock/deps/contact_fcc.cpp b/src/haddock/deps/contact_fcc.cpp new file mode 100755 index 000000000..50382d213 --- /dev/null +++ b/src/haddock/deps/contact_fcc.cpp @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +struct Coor3f { + float x; + float y; + float z; +}; + +struct Residue { + int nr; + vector coor; + vector atom; + int seg; +}; + +vector res; + +bool seg_sorter (Residue res_a, Residue res_b) { + int segA = res_a.seg; + int segB = res_b.seg; + return (segA < segB); +}; + +int main(int argc, char *argv[]) { + char buf[2000]; + + if (argc < 3) { + fprintf(stderr,"ERROR: Too few arguments\n"); + fprintf(stderr, "Usage: contact \n"); + return 1; + } + + char *filename = argv[1]; + float cutoff = atof(argv[2]); + + if (cutoff < 0 || cutoff > 100) { + fprintf(stderr,"ERROR: Cutoff out of range\n"); + fprintf(stderr, "Usage: contact \n"); + return 1; + } + + FILE *fil = fopen(filename, "r"); + if (fil == NULL) { + fprintf(stderr, "ERROR: PDB file %s does not exist\n", filename); + return 1; + } + + int currnr = -99999; + char currseg; + int segid = 0; + + set nonconv; + while (!feof(fil)) { + char code[10]; + char atom[5]; + if (!fgets(buf, 2000, fil)) break; + sscanf(buf, "%s %*d %s", code, atom); + + // Ignore HETATM and hydrogens + if (!strncmp(code,"ATOM", 4) && ( atom[0] != 'H' && !( isdigit(atom[0]) && atom[1] == 'H' ) ) ) { + int nr = atoi(buf + 22); + char seg = buf[72]; + if (seg != currseg) { + currseg = seg; + segid++; + } + if (nr != currnr) { + Residue r; + r.nr = nr+10000; + r.seg = segid; + res.push_back(r); + currnr = r.nr; + } + Residue &rcurr = res[res.size() -1]; + Coor3f ccoor; + ccoor.x = atof(buf+27); + ccoor.y = atof(buf+38); + ccoor.z = atof(buf+46); + rcurr.coor.push_back(ccoor); + string atom2(atom); + rcurr.atom.push_back(atom2); + } + } + + if (!res.size()) {fprintf(stderr, "ERROR: PDB file %s contains no residues\n", filename); return 1;} + + // Sort the residues by segment to avoid random chain ordering problems + sort (res.begin(), res.end(), seg_sorter); + + double cutoffsq = cutoff * cutoff; + + for (int n = 0; n < res.size(); n++) { + vector &c1 = res[n].coor; + int seg1 = res[n].seg; + for (int nn = n + 1; nn < res.size(); nn++) { + int seg2 = res[nn].seg; + if (seg1 == seg2) continue; + vector &c2 = res[nn].coor; + for (int i = 0; i < res[n].coor.size(); i++) { + for (int ii = 0; ii < res[nn].coor.size(); ii++) { + double currdissq = + (c1[i].x - c2[ii].x) * (c1[i].x - c2[ii].x) + + (c1[i].y - c2[ii].y) * (c1[i].y - c2[ii].y) + + (c1[i].z - c2[ii].z) * (c1[i].z - c2[ii].z); + if (currdissq < cutoffsq) { + printf ("%d%d%d%d\n", res[n].nr, res[n].seg, res[nn].nr, res[nn].seg); + } + } + } + } + } + fclose(fil); +} diff --git a/src/haddock/deps/fast-rmsdmatrix.c b/src/haddock/deps/fast-rmsdmatrix.c new file mode 100644 index 000000000..d352bbabc --- /dev/null +++ b/src/haddock/deps/fast-rmsdmatrix.c @@ -0,0 +1,883 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct traj{ + int frames; /*!< number of frames in the trajectory */ + double **traj_coords; /*!< 2D array of xyz coordinates */ + int n_at; /*!< number of atoms in the atomistic structure */ + int pairs; /*!< number of possible pairs of structures */ +}traj; + +typedef struct alignments{ + double *rmsd_mat; /*!< condensed pairwise RMSD matrix */ + double **coms; /*!< array of centers of mass */ + int *ref_structs; /*!< array of reference structures */ + int *mod_structs; /*!< array of model structures */ +}alignments; + +void failed(char message[]) { + printf("\n FAILED *** %s ***\n \n", message); + exit(1); +} + +struct arguments; + +void zero_vec_d(double *a, int dim) { + + int i; + for (i = 0; i < dim; i++) { + a[i] = 0; + } +} + +int *i1t(int n1) { + int *p, *a; + int i; + if ((p = (int *) malloc((size_t) n1 * sizeof(int))) == NULL) + failed("i1t: failed"); + for (i = 0, a = p; i < n1; i++) + *a++ = 0; + return p; +} + +double *d1t(int n1) { + double *p, *a; + int i; + if ((p = (double *) malloc((size_t) n1 * sizeof(double))) == NULL) + failed("d1t: failed n1 "); + for (i = 0, a = p; i < n1; i++) + *a++ = 0; + return p; +} + +double **d2t(int n1, int n2) { + double **p, *a; + int i; + if ((p = (double **) malloc((size_t) n1 * sizeof(double *))) == NULL) + failed("d2t: failed n1"); + if ((p[0] = (double *) malloc((size_t) n1 * n2 * sizeof(double))) == NULL) + failed("d2t: failed n2"); + for (i = 0; i < n1 - 1; i++) + p[i + 1] = p[i] + n2; + for (i = 0, a = p[0]; i < n1 * n2; i++) + *a++ = 0; + return p; +} + +void free_d2t(double **p) { + + free(p[0]); + free(p); +} + +void free_d1t(double *p) { + + free(p); +} + +double scal_d(double *a, double *b, int dim) { + + int i; + double temp; + + temp = 0.0; + for (i = 0; i < dim; i++) { + temp += a[i] * b[i]; + } + return (temp); +} + +double norm_d(double *a, int dim) { + + return (sqrt(scal_d(a, a, dim))); +} + +void normalize_d(double *a, int dim) { + int i; + double temp; + + temp = norm_d(a, dim); + for (i = 0; i < dim; i++) { + a[i] = a[i] / temp; + } +} + +void vecprod_d(double *a, double *b, double *c) { + + c[0] = a[1] * b[2] - a[2] * b[1]; + c[1] = a[2] * b[0] - a[0] * b[2]; + c[2] = a[0] * b[1] - a[1] * b[0]; +} + +void myjacobi(double a[][3], int n, double *d, double v[][3], int *nrot) { + + int j, iq, ip, i; + double tresh, theta, tau, t, sm, s, h, g, c; + double b[3], z[3]; + +#define ROTATE(a, i, j, k, l) g=a[i][j];h=a[k][l];a[i][j]=g-s*(h+g*tau);a[k][l]=h+s*(g-h*tau); + + for (ip = 0; ip <= (n - 1); ip++) { + for (iq = 0; iq <= (n - 1); iq++) + v[ip][iq] = 0.0; + v[ip][ip] = 1.0; + } + for (ip = 0; ip <= (n - 1); ip++) { + b[ip] = d[ip] = a[ip][ip]; + z[ip] = 0.0; + } + *nrot = 0; + for (i = 1; i <= 500; i++) { + sm = 0.0; + for (ip = 0; ip <= n - 2; ip++) { + for (iq = ip + 1; iq <= (n - 1); iq++) + sm += fabs(a[ip][iq]); + } + if (sm == 0.0) { + return; + } + if (i < 4) + tresh = 0.2 * sm / (n * n); + else + tresh = 0.0; + for (ip = 0; ip <= n - 2; ip++) { + for (iq = ip + 1; iq <= (n - 1); iq++) { + g = 100.0 * fabs(a[ip][iq]); + if (i > 4 && (fabs((fabs(d[ip]) + g) - fabs(d[ip])) < 1.0e-6) + && (fabs((fabs(d[iq]) + g) - fabs(d[iq])) < 1.0e-6)) + a[ip][iq] = 0.0; + else if (fabs(a[ip][iq]) > tresh) { + h = d[iq] - d[ip]; + if (fabs((fabs(h) + g) - fabs(h)) < 1.0e-6) + t = (a[ip][iq]) / h; + else { + theta = 0.5 * h / (a[ip][iq]); + t = 1.0 / (fabs(theta) + sqrt(1.0 + theta * theta)); + if (theta < 0.0) + t = -t; + } + c = 1.0 / sqrt(1 + t * t); + s = t * c; + tau = s / (1.0 + c); + h = t * a[ip][iq]; + z[ip] -= h; + z[iq] += h; + d[ip] -= h; + d[iq] += h; + a[ip][iq] = 0.0; + for (j = 0; j <= ip - 1; j++) { + ROTATE (a, j, ip, j, iq) + } + for (j = ip + 1; j <= iq - 1; j++) { + ROTATE (a, ip, j, j, iq) + } + for (j = iq + 1; j <= (n - 1); j++) { + ROTATE (a, ip, j, iq, j) + } + for (j = 0; j <= (n - 1); j++) { + ROTATE (v, j, ip, j, iq) + } + ++(*nrot); + } + } + } + for (ip = 0; ip <= (n - 1); ip++) { + b[ip] += z[ip]; + d[ip] = b[ip]; + z[ip] = 0.0; + } + } + printf("Too many iterations in routine JACOBI %lf\n", sm); + /* exit (1); */ +#undef ROTATE +} + +int columns(char* string){ + /** + * routine that returns the number of columns for each row inside the file chosen. + * + * Parameter + * ---------- + * + * `string` : string token in account + */ + + int counter = 0; + + while(string){ + counter++; + string = strtok(NULL, " \t\v\r\n"); + } + return counter; +} + +int n_rows (FILE *f){ + /** + * routine that returns the number of rows in a file. It counts correctly this number even if the last row does not present \n + * + * Parameter + * ---------- + * + * `f` : FILE structure that represents the file opened. + */ + + size_t rows_i = 0, n = 0; + char *buf = NULL; + + fseek(f, 0, SEEK_SET); + + while(getline(&buf, &n, f) != -1) rows_i++; + + free(buf); + + return rows_i; +} + +int get_pair(int nmodels, int idx, int ij_arr[2]){ + if (nmodels < 0 || idx < 0){ + printf("get_pair cannot accept negative numbers"); + printf("Input is %d , %d", nmodels, idx); + exit(EXIT_FAILURE); + } + // solve the second degree equation + int b = 1 - (2 * nmodels); + int i = (-b - sqrt(b * b - 8 * idx)) / 2; + int j = idx + i * (b + i + 2) / 2 + 1; + ij_arr[0] = i; + ij_arr[1] = j; + return 0; +} + +double optimal_alignment(double **x, double **y, int cgnum, double u[][3]) { + /** + * routine that computes the Kabsch alignment and the rmsd between two configurations + * + * + * Parameters + * ---------- + * + * `x`, `y` : CG structures + * + * `cgnum` : length of CG mapping + * + * `u` : rotation matrix + */ + void myjacobi(double a[][3], int n, double *d, double v[][3], int *nrot); + int i, j, k, sign[3], order[3], nrot; + double e, e0; + double r[3][3], rt[3][3], temp; //, **x, **y; + double a[3][3], eval[3], evec[3][3]; + double eigenvalues[3], eigenvectors[3][3], b[3][3]; + int speak = 1; + + e0 = 0.0; + for (i = 0; i < cgnum; i++) { + e0 += 0.5 * norm_d(x[i], 3) * norm_d(x[i], 3); + e0 += 0.5 * norm_d(y[i], 3) * norm_d(y[i], 3); + } + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + r[i][j] = 0.0; + for (k = 0; k < cgnum; k++) { + r[i][j] += y[k][i] * x[k][j]; + } + rt[j][i] = r[i][j]; + } + } + + if (isnan(e0) == 1) printf("Found a NaN in Kabsch alignment at checkpoint 1\n"); + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + a[i][j] = 0; + for (k = 0; k < 3; k++) { + a[i][j] += rt[i][k] * r[k][j]; + } + } + } + + myjacobi(a, 3, eval, evec, &nrot); + /* we add small quantities in order to remove potentially dangerous degeneracies */ + if (isnan(eval[0]) == 1) printf("Found a NaN in Kabsch alignment at checkpoint 2\n"); + if (isnan(eval[1]) == 1) printf("Found a NaN in Kabsch alignment at checkpoint 3\n"); + if (isnan(eval[2]) == 1) printf("Found a NaN in Kabsch alignment at checkpoint 4\n"); + + eval[0] += 0.0000000000000001; + eval[1] += 0.00000000000000001; + eval[2] += 0.00000000000000002; + + if ((eval[0] < eval[1]) && (eval[0] < eval[2])) { + order[0] = 1; + order[1] = 2; + order[2] = 0; + } + + if ((eval[1] < eval[0]) && (eval[1] < eval[2])) { + order[0] = 0; + order[1] = 2; + order[2] = 1; + } + + if ((eval[2] < eval[0]) && (eval[2] < eval[1])) { + order[0] = 0; + order[1] = 1; + order[2] = 2; + } + + for (i = 0; i < 3; i++) { + eigenvalues[i] = eval[order[i]]; + for (j = 0; j < 3; j++) { + eigenvectors[i][j] = evec[j][order[i]]; + } + } + + normalize_d(eigenvectors[0], 3); + normalize_d(eigenvectors[1], 3); + vecprod_d(eigenvectors[0], eigenvectors[1], eigenvectors[2]); + normalize_d(eigenvectors[2], 3); + + for (i = 0; i < 2; i++) { + for (j = 0; j < 3; j++) { + b[i][j] = 0; + for (k = 0; k < 3; k++) { + b[i][j] += r[j][k] * eigenvectors[i][k]; + } + } + normalize_d(b[i], 3); + } + + vecprod_d(b[0], b[1], b[2]); + normalize_d(b[2], 3); + + temp = 0.0; + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + temp += b[2][i] * r[i][j] * eigenvectors[2][j]; + } + } + sign[2] = +1; + if (temp < 0) + sign[2] = -1; + sign[0] = sign[1] = 1; + + if (fabs(eigenvalues[2]) < 0.0000001) { + e = e0 - sqrt(eigenvalues[0]) - sqrt(eigenvalues[1]); + } else { + e = e0 - sqrt(eigenvalues[0]) - sqrt(eigenvalues[1]) - sign[2] * sqrt(eigenvalues[2]); + } + + if (isnan(e) == 1) { + printf("Found a NaN in Kabsch alignment at checkpoint 5 | \n"); + printf("e %lf e0 %lf e1 %lf e2 %lf e3 %lf\n", e, e0, eigenvalues[0], eigenvalues[1], eigenvalues[2]); + } +/********************/ + e = 2.0 * e / cgnum; + if (e < 0.0) { + if (fabs(e) < 1.0e-3) { + if (speak == 1) { + //printf("Warning. In Kabsch alignment found slightly negative value of e (%e). Identical structures? Setting it to 0.000001.\n", + // e); + e = 0.000001; + } + } + /* occasionally, when dealing with two practically identical configurations + the value of e may be slightly negative due to the small offsets and roundoff errors. + In this case we set it equal to zero. */ + else { + FILE *fe; + fe = fopen("error.dat", "w"); + fprintf(fe, "Error. In Kabsch alignment found negative value of e: (%lf)\n", e); + printf("Error. In Kabsch alignment found negative value of e: (%lf)\n", e); + fclose(fe); + exit(EXIT_FAILURE); + } + } + e = sqrt(e); + if (isnan(e) == 1) printf("Found a NaN in Kabsch alignment at checkpoint 6\n"); +/********************/ + // filling rotation_matrix + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + u[i][j] = 0.0; + for (k = 0; k < 3; k++) { + u[i][j] += b[k][i] * eigenvectors[k][j]; + } + } + } + return (e); +} + +void cycle_ilrmsd(traj *Trajectory, traj *Ligand_Trajectory, alignments *align, int start_pair[2]) { + /** + * routine that cycles over pairs of frames in a trajectory, aligning on the receptor + * and computing the interface ligand rmsd + * + * Parameters + * ---------- + * + * `Trajectory` : traj object + * + * `align` : alignments object + * + * `start_pair` : pair of structures to begin with + */ + int n; + int i, j; + double **x, **y, **xlig, **ylig, **ylig_rot; + double u[3][3], cm1[3], cm2[3]; + int rot_idx; + // allocating vectors + x = d2t(Trajectory->n_at, 3); + xlig = d2t(Ligand_Trajectory->n_at, 3); + y = d2t(Trajectory->n_at, 3); + ylig = d2t(Ligand_Trajectory->n_at, 3); + ylig_rot = d2t(Ligand_Trajectory->n_at, 3); + int ref = start_pair[0]; + int mod = start_pair[1]; + int old_ref = ref - 1; + + for (n = 0; n < Trajectory->pairs; n++) { + if (ref != old_ref) { + // recalculate com and translate structure + zero_vec_d(cm1, 3); + for (i = 0; i < Trajectory->n_at; i++) { + for (j = 0; j < 3; j++) { + cm1[j] += Trajectory->traj_coords[ref][i * 3 + j] / Trajectory->n_at; + } + } + // filling structure + for (i = 0; i < Trajectory->n_at; i++) { + for (j = 0; j < 3; j++) { + x[i][j] = Trajectory->traj_coords[ref][i * 3 + j] - cm1[j]; + } + } + // now the ligand + for (i = 0; i < Ligand_Trajectory->n_at; i++) { + for (j = 0; j < 3; j++) { + xlig[i][j] = Ligand_Trajectory->traj_coords[ref][i * 3 + j] - cm1[j]; + } + } + old_ref += 1; + } + + // second structure + zero_vec_d(cm2, 3); + for (i = 0; i < Trajectory->n_at; i++) { + for (j = 0; j < 3; j++) { + cm2[j] += Trajectory->traj_coords[mod][i * 3 + j] / Trajectory->n_at; + } + } + for (i = 0; i < Trajectory->n_at; i++) { + for (j = 0; j < 3; j++) { + y[i][j] = Trajectory->traj_coords[mod][i * 3 + j] - cm2[j]; + } + } + // now ylig + for (i = 0; i < Ligand_Trajectory->n_at; i++) { + for (j = 0; j < 3; j++) { + ylig[i][j] = Ligand_Trajectory->traj_coords[mod][i * 3 + j] - cm2[j]; + } + } + + // Alignment AND RMSD computation + align->rmsd_mat[n] = optimal_alignment(x, y, Trajectory->n_at, u); + align->ref_structs[n] = ref + 1; + align->mod_structs[n] = mod + 1; + + // now correcting the coordinates of the ligand + for (i = 0; i < Ligand_Trajectory->n_at; i++) { + for (j = 0; j < 3; j++) { + ylig_rot[i][j] = 0.0; + for (rot_idx = 0; rot_idx < 3; rot_idx++) { + ylig_rot[i][j] += u[rot_idx][j] * ylig[i][rot_idx]; + } + } + } + // calculate rmsd between ligands + double ilrmsd = 0.0; + double delta; + for (i = 0; i < Ligand_Trajectory->n_at; i++) { + for (j = 0; j < 3; j++) { + delta = ylig_rot[i][j] - xlig[i][j]; + ilrmsd += delta * delta; + } + } + + ilrmsd = sqrt(ilrmsd / Ligand_Trajectory->n_at); + // the value before was the receptor interface rmsd, now let's change it to the ligand interface rmsd + align->rmsd_mat[n] = ilrmsd; + + // update idx + if (mod == Trajectory->frames - 1) { + ref += 1; + mod = ref + 1; + } + else { + mod += 1; + } + + } + free_d2t(x); + free_d2t(y); + free_d2t(xlig); + free_d2t(ylig); + free_d2t(ylig_rot); +} + +void cycle_rmsd(traj *Trajectory, alignments *align, int start_pair[2]) { + /** + * routine that cycles over all pairs of frames in a trajectory, calling `optimal_alignment` + * + * Parameters + * ---------- + * + * `Trajectory` : traj object + * + * `align` : alignments object + * + * `start_pair` : pair of structures to begin with + */ + int n; + int i, j; + double **x, **y; + double u[3][3], cm1[3], cm2[3]; + printf("calculating rotation matrices..\n"); + // allocating vectors + x = d2t(Trajectory->n_at, 3); + y = d2t(Trajectory->n_at, 3); + int ref = start_pair[0]; + int mod = start_pair[1]; + int old_ref = ref - 1; + //for (n = 0; n < Trajectory->frames; n++) { + for (n = 0; n < Trajectory->pairs; n++) { + if (ref != old_ref) { + // recalculate com and translate structure + + zero_vec_d(cm1, 3); + for (i = 0; i < Trajectory->n_at; i++) { + for (j = 0; j < 3; j++) { + cm1[j] += Trajectory->traj_coords[ref][i * 3 + j] / Trajectory->n_at; + } + } + // filling structure + for (i = 0; i < Trajectory->n_at; i++) { + for (j = 0; j < 3; j++) { + x[i][j] = Trajectory->traj_coords[ref][i * 3 + j] - cm1[j]; + } + } + old_ref += 1; + } + // second structure + zero_vec_d(cm2, 3); + for (i = 0; i < Trajectory->n_at; i++) { + for (j = 0; j < 3; j++) { + cm2[j] += Trajectory->traj_coords[mod][i * 3 + j] / Trajectory->n_at; + } + } + for (i = 0; i < Trajectory->n_at; i++) { + for (j = 0; j < 3; j++) { + y[i][j] = Trajectory->traj_coords[mod][i * 3 + j] - cm2[j]; + } + } + // Alignment AND RMSD computation + align->rmsd_mat[n] = optimal_alignment(x, y, Trajectory->n_at, u); + align->ref_structs[n] = ref + 1; + align->mod_structs[n] = mod + 1; + + // update idx + if (mod == Trajectory->frames - 1) { + ref += 1; + mod = ref + 1; + } + else { + mod += 1; + } + + } + free_d2t(x); + free_d2t(y); +} + +void read_TrajectoryFile(char *TrajFileName, traj *Trajectory){ + /** + * routine that reads the input xyz coordinate file + * + * Parameters + * ---------- + * + * `TrajFileName` : trajectory filename + * + * `Trajectory` : traj object + */ + + FILE *ft; // trajectory file; + FILE *fe; // declaring error file; + + int rows_i, cols_i, i, p, frame_idx, j, count; + size_t line_buf_size = 0; + + char *token; + char *token_arr[100]; //*string, *line; + char *string = NULL; + char *line; + + /* Opening and check if it exist and if it is empty */ + ft = fopen(TrajFileName, "r"); + printf("Reading Trajectory FILE %s\n", TrajFileName); + + //check_empty_file(ft, TrajFileName); + + rows_i = n_rows(ft); // Number of rows in trajectory file "ft". + fseek(ft, 0, SEEK_SET); + + line = (char *) malloc (200); // Allocate char line with size (lenght) = 200; + // We are sure that the lenght of each line is less than 200 + + /* Initialize the 2D-array Trajectory->traj_coords[][] */ + for(i = 0; i < Trajectory->frames; i++){ + for(j = 0; j < Trajectory->n_at; j++){ + Trajectory->traj_coords[i][3 * j + 0] = 0.0; + Trajectory->traj_coords[i][3 * j + 1] = 0.0; + Trajectory->traj_coords[i][3 * j + 2] = 0.0; + } + } + + /* Checking for empty rows; + * Checking that there are ONLY rows with one column and three columns + * Checking that the rows with one column correspond to an integer number i.e. the number of atoms; + * Checking that the rows with three columns are float corresponding to x,y,z trajectory coordinate. */ + + frame_idx = 0; // Initialize frame index to 0 + p = 0; // Initialize counter "p" to 0 + + for(i=0; i=3*Trajectory->n_at) // p increases from 0 to 3*atomnum i.e. p=[0;3*230) i.e. p = [0;689] + p = 0; + + getline(&string, &line_buf_size, ft); // Reading entire line; + + strcpy(line, string); // Copying "string" in "line"; we need it in case of three columns. + + //if( i != (Trajectory->n_at + 2)*(frame_idx-1) + 1) // Checking for empty rows except for the 2nd row of each frame representing the title + // check_empty_rows(string); // that could also be an empty string + + token = strtok(string, " \t\v\r\n"); // Splitting the string-line in columns separated by spaces, tabs, or \n or \v or \r + + cols_i = columns(token); // Counting the number of columns in each row of file. + + + if(i!=(Trajectory->n_at + 2)*(frame_idx-1) + 1){ // exclude the 2nd row of each frame + + if(cols_i == 1){ + + if(i != (Trajectory->n_at + 2)*frame_idx) { + printf("Error. The %dth frame contains a different number of rows. Each frame must have %d rows\n", frame_idx+1, Trajectory->n_at + 2); + exit(EXIT_FAILURE); + } + else{ + frame_idx++; + if(frame_idx>Trajectory->frames){ + fe = fopen("error.dat", "w"); + fprintf(fe, "Error. The number of trajectory frames is higher than the declared one in parameter file (%d).\nAborting\n", Trajectory->frames); + fclose(fe); + printf("Error. The number of trajectory frames is higher than the declared one in parameter file (%d).\nAborting\n", Trajectory->frames); + exit(EXIT_FAILURE); + } + //check_int_string(string, i, TrajFileName); // Checking that each row is an INTEGER number. + if(atoi(string) != Trajectory->n_at){ + fe = fopen("error.dat","w"); + fprintf(fe,"Error. The number of atoms at %dth row has length (%d) different from atomnum(%d). Aborting\n",\ + i+1,atoi(string),Trajectory->n_at); + printf("Error. The number of atoms at %dth row has length (%d) different from atomnum(%d). Aborting\n",\ + i+1,atoi(string),Trajectory->n_at); + fclose(fe); + exit(EXIT_FAILURE); + } + } + } + + if(cols_i == 2){ + + if(i != (Trajectory->n_at + 2)*frame_idx + 1){ + fe = fopen("error.dat", "w"); + fprintf(fe,"Error. Each row must not contain 2 columns (except the title in the 2nd row of each frame that can contain N columns).\n\ + ONLY 1 or 4 columns are allowed in Trajectory (%dth row has 2 columns)\n", i+1); + + printf("Error. Each row must not contain 2 columns (except the title in the 2nd row of each frame that can contain N columns)\n\ + ONLY 1 or 4 columns are allowed in Trajectory (%dth row has 2 columns.\n", i+1); + fclose(fe); + exit(EXIT_FAILURE); + } + } + + if(cols_i == 3){ + + if(i != (Trajectory->n_at + 2)*frame_idx + 1){ + fe = fopen("error.dat", "w"); + fprintf(fe,"Error. Each row must not contain 3 columns (except the title in the 2nd row of each frame that can contain N columns). \ + ONLY 1 or 4 columns are allowed in Trajectory (%dth row has 3 columns)\n", i+1); + + printf("Error. Each row must not contain 3 columns (except the title in the 2nd row of each frame that can contain N columns) \ + ONLY 1 or 4 columns are allowed in Trajectory (%dth row has 3 columns.\n", i+1); + fclose(fe); + exit(EXIT_FAILURE); + } + } + + if(cols_i == 4){ + if(i != (Trajectory->n_at + 2)*frame_idx){ + token_arr[0] = strtok(line, " \t\v\r\n"); + + count = 0; + while(token_arr[count]){ + count++; + token_arr[count] = strtok(NULL, " \t\v\r\n"); + } + + for (j=1; j <= count-1 ; j++){ + //check_float_string(token_arr[j], i, TrajFileName); // Checking that each row is an FLOAT number. + Trajectory->traj_coords[frame_idx-1][p] = atof(token_arr[j]); // Assigning each float-string to traj_coords[i][j] 2D-array + p++; + } + + } + + else{ + printf("Error. Each row of the frame (except the 1st row containing the number of atoms and the 2nd row containing the title),\n\ + must contain 4 columns corresponding to at_name, x, y, and z coodinates. Check also if there is one extra row in %dth frame\n", + frame_idx); + exit(EXIT_FAILURE); + } + } + + if(cols_i > 4 ){ + if(i != (Trajectory->n_at + 2)*frame_idx){ + fe = fopen("error.dat","w"); + fprintf(fe, "Error. The maximum number of columns allowed is 4. The %dth row of your file contains %d columns\n", i+1, cols_i); + printf("Error. The maximum number of columns allowed is 4. The %dth row of your file contains %d columns\n", i+1, cols_i); + fclose(fe); + exit(EXIT_FAILURE); + } + } + } + + } // END FOR LOOP + + free(string); + //free(token); + free(line); + fclose(ft);// Close trajectory file. + // 1st check: frame_idx must be = frames + if (frame_idx != Trajectory->frames) { + fe = fopen("error.dat", "w"); + fprintf(fe, "Error. Frames completed: %d, while declared frames in parameter file are %d. Trajectory incomplete.\nAborting\n",frame_idx,\ + Trajectory->frames); + fclose(fe); + printf("Error. Frames completed: %d, while declared frames in parameter file are %d. Trajectory incomplete.\nAborting\n",frame_idx,\ + Trajectory->frames); + exit(EXIT_FAILURE); + } + + // final check: frame_idx corresponds with what we expect but the number of row is not the same => It means that + // the frame completed are frame_idx - 1 + else{ + if(rows_i != (Trajectory->n_at + 2)*Trajectory->frames){ + fe = fopen("error.dat", "w"); + fprintf(fe,"Error. Total number of rows: %d, while expected number of rows are %d. Trajectory incomplete.\nAborting\n",rows_i,\ + (Trajectory->n_at + 2)*Trajectory->frames); + fclose(fe); + printf("Error. Total number of rows: %d, while expected number of rows are %d. Trajectory incomplete.\nAborting\n", rows_i,\ + (Trajectory->n_at + 2)*Trajectory->frames); + exit(EXIT_FAILURE); + } + } +} + + +int main(int argc, char *argv[]) { + /** + * main file of the program + */ + time_t seconds; + time_t seconds_ref = time(NULL); + printf("Starting program at %s\n", ctime(&seconds_ref)); + + printf("input trajectory %s\n", argv[1]); + printf("core index is %s\n", argv[2]); + printf("number of pairs is %s\n", argv[3]); + printf("starting pair is %s-%s\n", argv[4], argv[5]); + traj *Trajectory = malloc (sizeof(traj)); + + int npairs = atoi(argv[3]); + int start_pair[2]; + start_pair[0] = atoi(argv[4]); + start_pair[1] = atoi(argv[5]); + + int rec_frames = atoi(argv[6]); + int rec_atomnum = atoi(argv[7]); + + Trajectory->frames = rec_frames; + Trajectory->n_at = rec_atomnum; + Trajectory->traj_coords = d2t(rec_frames, 3 * rec_atomnum); + Trajectory->pairs = npairs; + + printf("frames = %d\n", Trajectory->frames); + printf("overall pairs = %d\n", Trajectory->pairs); + + // read trajectory + read_TrajectoryFile(argv[1], Trajectory); + alignments *align = malloc (sizeof(alignments)); + align->rmsd_mat = d1t(Trajectory->pairs); + align->ref_structs = i1t(Trajectory->pairs); + align->mod_structs = i1t(Trajectory->pairs); + align->coms = d2t(rec_frames, 3); + + char out_filename[100]; + if (argc == 10){ + // if there are arguments 8 to 10, it is the ligand trajectory, frames and atomnum + traj *Ligand_Trajectory = malloc (sizeof(traj)); + printf("loading ligand data\n"); + + int ligand_atomnum = atoi(argv[9]); + Ligand_Trajectory->frames = rec_frames; + Ligand_Trajectory->n_at = ligand_atomnum; + Ligand_Trajectory->traj_coords = d2t(rec_frames, 3 * ligand_atomnum); + read_TrajectoryFile(argv[8], Ligand_Trajectory); + cycle_ilrmsd(Trajectory, Ligand_Trajectory, align, start_pair); + sprintf(out_filename, "ilrmsd_%s.matrix", argv[2]); + } + else{ + cycle_rmsd(Trajectory, align, start_pair); + sprintf(out_filename, "rmsd_%s.matrix", argv[2]); + } + // write the rmsd matrix + int i; + // check if file exists + if (access(out_filename, F_OK) != -1){ + printf("Warning: file %s already exists.\n", out_filename); + } + // write to file + FILE *frmsd; + frmsd = fopen(out_filename, "w"); + for (i = 0; i < Trajectory->pairs; i++){ + fprintf(frmsd, "%d %d %.3lf\n",align->ref_structs[i],align->mod_structs[i],align->rmsd_mat[i]); + } + + seconds = time(NULL); + printf("\nOverall execution time: %ld seconds\n", seconds-seconds_ref); + free_d2t(align->coms); + free_d1t(align->rmsd_mat); + free(align); + free_d2t(Trajectory->traj_coords); + free(Trajectory); + return 0; +} From 95593649374fcbefc75839fd1a97438e76736bb4 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Mon, 16 Sep 2024 16:31:39 +0200 Subject: [PATCH 17/62] remove submodules --- .gitmodules | 3 --- src/fast-rmsdmatrix | 1 - 2 files changed, 4 deletions(-) delete mode 100644 .gitmodules delete mode 160000 src/fast-rmsdmatrix diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 6af268cad..000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "src/fast-rmsdmatrix"] - path = src/fast-rmsdmatrix - url = https://github.com/mgiulini/fast-rmsdmatrix.git diff --git a/src/fast-rmsdmatrix b/src/fast-rmsdmatrix deleted file mode 160000 index 4008a980b..000000000 --- a/src/fast-rmsdmatrix +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4008a980bba142b331b01ab0c13a50f156e5879f From c28310573ee03f9e7fd387a190092a107f5ca349 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Mon, 16 Sep 2024 16:31:59 +0200 Subject: [PATCH 18/62] update install script --- setup.py | 105 ++++++++++++++++++------------------------------------- 1 file changed, 34 insertions(+), 71 deletions(-) diff --git a/setup.py b/setup.py index ad0658159..14d850509 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,6 @@ import shutil import subprocess import sys -import tempfile import urllib.request from os.path import dirname, join from pathlib import Path @@ -21,60 +20,53 @@ class CustomBuildInstall(install): def run(self): - issues = self.flycheck() - if issues: - print("Issues found, installation cannot continue:") - for issue in issues: - print(f" - {issue}") - sys.exit(1) - self.run_command("build_ext") install.run(self) - @staticmethod - def flycheck() -> list[str]: - """Helper method to make sure all system dependencies are installed""" - issues = [] - - if shutil.which("git") is None: - issues.append("git not found in $PATH") - - if shutil.which("make") is None: - issues.append("make not found in $PATH") - - python_version = sys.version_info - if python_version.major != 3 or python_version.minor < 9: - issues.append("Python 3.9+ is required, found: {sys.version}") - - return issues - class CustomBuild(build_ext): - """CustomBuild handles the build of the C++ dependencies""" + """CustomBuild handles the build of the C/C++ dependencies""" def run(self): - print("Building HADDOCK3 C++ dependencies...") - # TODO: Find a smarter way of passing this `bin_dir` without defining it outside `__init__` - self.bin_dir = Path(self.get_install_dir(), "haddock", "bin") - self.bin_dir.mkdir(exist_ok=True, parents=True) + print("Building HADDOCK3 C/C++ binary dependencies...") + bin_dir = Path(self.get_install_dir(), "haddock", "bin") + bin_dir.mkdir(exist_ok=True, parents=True) + + dep_dir = Path("src", "haddock", "deps") + assert dep_dir.exists() # Build FCC - self.clone_and_build_submodule( - name="FCC", - repo_url="https://github.com/haddocking/fcc.git", - build_cmd="make", - src_dir="src", - binary_name="contact_fcc", + cmd = "g++ -O2 -o contact_fcc contact_fcc.cpp" + + _ = subprocess.run( + cmd, + shell=True, + cwd=dep_dir, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=True, ) + src = Path(dep_dir, "contact_fcc") + dst = Path(bin_dir, "contact_fcc") + + shutil.move(src, dst) + # Build fast-rmsdmatrix - self.clone_and_build_submodule( - name="fast-rmsdmatrix", - repo_url="https://github.com/mgiulini/fast-rmsdmatrix.git", - build_cmd="make fast-rmsdmatrix", - src_dir="src", - binary_name="fast-rmsdmatrix", + cmd = "gcc -Wall -O3 -march=native -std=c99 -o fast-rmsdmatrix fast-rmsdmatrix.c -lm" + _ = subprocess.run( + cmd, + shell=True, + cwd=dep_dir, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=True, ) + src = Path(dep_dir, "fast-rmsdmatrix") + dst = Path(bin_dir, "fast-rmsdmatrix") + + shutil.move(src, dst) + # Run original build_ext build_ext.run(self) @@ -83,35 +75,6 @@ def get_install_dir(self): install_cmd = self.get_finalized_command("install") return install_cmd.install_lib # type: ignore - def clone_and_build_submodule( # pylint: disable=too-many-arguments - self, name, repo_url, build_cmd, src_dir, binary_name - ): - """Clone a repository and build.""" - print(f"Building {name}...") - with tempfile.TemporaryDirectory() as temp_dir: - - # clone the repository - subprocess.run(["git", "clone", repo_url, temp_dir], check=True) - - # Build - build_dir = Path(temp_dir, src_dir) - subprocess.run( - build_cmd, - shell=True, - cwd=build_dir, - check=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - - # TODO: Add error handling - - # Move the binary - src_bin = Path(build_dir, binary_name) - dst_bin = Path(self.bin_dir, binary_name) - - shutil.copy2(src_bin, dst_bin) - CNS_BINARIES = { "x86_64-linux": "https://surfdrive.surf.nl/files/index.php/s/o8X7zZezIM3P0cE", # linux From 949d7c5c247c19d3440ccefce6da8c89cef57a95 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Mon, 16 Sep 2024 17:43:11 +0200 Subject: [PATCH 19/62] remove unused script --- src/haddock/clis/cli_check_install.py | 53 --------------------------- 1 file changed, 53 deletions(-) delete mode 100644 src/haddock/clis/cli_check_install.py diff --git a/src/haddock/clis/cli_check_install.py b/src/haddock/clis/cli_check_install.py deleted file mode 100644 index 7e5cc14a7..000000000 --- a/src/haddock/clis/cli_check_install.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Check if HADDOCK3 was installed correctly.""" - -import importlib.resources -import logging -import platform -import sys -from pathlib import Path - -import haddock - - -logging.basicConfig( - level=logging.INFO, - format="[%(asctime)s - %(levelname)s] %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", -) - - -BINARY_DIR = Path(importlib.resources.files(haddock) / "bin") # type: ignore - -BINARIES = { - "CNS": "cns", - "FCC": "contact_fcc", - "Fast-RMSDMatrix": "fast-rmsdmatrix", -} - - -def check_binary(software: str, binary_name: str) -> None: - """Check if the binary is installed.""" - expected_binary_location = Path(BINARY_DIR, binary_name) - if expected_binary_location.exists(): - logging.info(f"✅ {software} binary `{binary_name}` OK") - else: - logging.info( - f"⚠️ {software} binary `{binary_name}` not properly installed " - "- define it manually in the configuration file" - ) - - -def main(): - """Check if the installation was successful.""" - system = platform.system().lower() - machine = platform.machine().lower() - python_version = f"{sys.version_info.major}.{sys.version_info.minor}" - - logging.info("Checking HADDOCK3 installation...") - logging.info(f"ℹ️ Python{python_version} running on {machine}-{system}") - for software, binary_name in BINARIES.items(): - check_binary(software, binary_name) - - -if __name__ == "__main__": - main() From 67fb9b029f1545bff0f3c30fd32ddcec6b477f3f Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Mon, 16 Sep 2024 17:47:33 +0200 Subject: [PATCH 20/62] use `Extensions` --- setup.py | 101 ++++++++++++++++++++++++++----------------------------- 1 file changed, 47 insertions(+), 54 deletions(-) diff --git a/setup.py b/setup.py index 14d850509..3c5d2f297 100644 --- a/setup.py +++ b/setup.py @@ -10,74 +10,65 @@ from os.path import dirname, join from pathlib import Path -from setuptools import find_packages, setup +from setuptools import Extension, find_packages, setup from setuptools.command.build_ext import build_ext from setuptools.command.install import install -class CustomBuildInstall(install): - """Custom Build and Install class""" - - def run(self): - - install.run(self) +cpp_extensions = [ + Extension( + "haddock.bin.contact_fcc", + sources=["src/haddock/deps/contact_fcc.cpp"], + extra_compile_args=["-O2"], + ), + Extension( + "haddock.bin.fast_rmsdmatrix", + sources=["src/haddock/deps/fast-rmsdmatrix.c"], + extra_compile_args=["-Wall", "-O3", "-march=native", "-std=c99"], + extra_link_args=["-lm"], + ), +] class CustomBuild(build_ext): - """CustomBuild handles the build of the C/C++ dependencies""" - def run(self): print("Building HADDOCK3 C/C++ binary dependencies...") - bin_dir = Path(self.get_install_dir(), "haddock", "bin") - bin_dir.mkdir(exist_ok=True, parents=True) - - dep_dir = Path("src", "haddock", "deps") - assert dep_dir.exists() - - # Build FCC - cmd = "g++ -O2 -o contact_fcc contact_fcc.cpp" - - _ = subprocess.run( - cmd, - shell=True, - cwd=dep_dir, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - check=True, + self.build_cpp_executable( + "contact_fcc", + ["g++", "-O2", "-o", "contact_fcc", "src/haddock/deps/contact_fcc.cpp"], ) - - src = Path(dep_dir, "contact_fcc") - dst = Path(bin_dir, "contact_fcc") - - shutil.move(src, dst) - - # Build fast-rmsdmatrix - cmd = "gcc -Wall -O3 -march=native -std=c99 -o fast-rmsdmatrix fast-rmsdmatrix.c -lm" - _ = subprocess.run( - cmd, - shell=True, - cwd=dep_dir, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - check=True, + self.build_cpp_executable( + "fast-rmsdmatrix", + [ + "gcc", + "-Wall", + "-O3", + "-march=native", + "-std=c99", + "-o", + "fast-rmsdmatrix", + "src/haddock/deps/fast-rmsdmatrix.c", + "-lm", + ], ) - src = Path(dep_dir, "fast-rmsdmatrix") - dst = Path(bin_dir, "fast-rmsdmatrix") - - shutil.move(src, dst) - - # Run original build_ext + # Run the standard build_ext build_ext.run(self) - def get_install_dir(self): - """Get the directory in which HADDOCK was installed""" - install_cmd = self.get_finalized_command("install") - return install_cmd.install_lib # type: ignore + def build_cpp_executable(self, name, cmd): + try: + subprocess.check_call(cmd) + bin_dir = os.path.join("src", "haddock", "bin") + os.makedirs(bin_dir, exist_ok=True) + shutil.move(name, os.path.join(bin_dir, name)) + print(f"Successfully built and moved {name}") + except subprocess.CalledProcessError as e: + print(f"Error building {name}: {e}") + raise CNS_BINARIES = { - "x86_64-linux": "https://surfdrive.surf.nl/files/index.php/s/o8X7zZezIM3P0cE", # linux + "x86_64-linux": "https://surfdrive.surf.nl/files/index.php/s/o8X7zZezIM3P0cE/download", # linux "x86_64-darwin": "", # macOs "arm64-darwin": "", # Mac M1/M2 "aarch64-linux": "", # linux ARM @@ -85,14 +76,14 @@ def get_install_dir(self): } -class CustomInstall(CustomBuildInstall): +class CustomInstall(install): """Custom class to handle the download of the CNS binary""" def run(self): """Run the installation""" # Run the standard installation - CustomBuildInstall.run(self) + install.run(self) # Get the installation directory if self.install_lib is None: @@ -101,6 +92,7 @@ def run(self): # Set where the cns binary needs to be bin_dir = Path(self.install_lib, "haddock", "bin") + bin_dir = Path("src", "haddock", "bin") # Create the `bin/` directory bin_dir.mkdir(exist_ok=True) @@ -177,6 +169,7 @@ def read_description(*names, **kwargs) -> str: url="https://github.com/haddocking/haddock3", packages=find_packages("src"), package_dir={"": "src"}, + package_data={"haddock": ["bin/*"]}, include_package_data=True, zip_safe=False, classifiers=[ @@ -223,8 +216,8 @@ def read_description(*names, **kwargs) -> str: "haddock3-traceback = haddock.clis.cli_traceback:maincli", "haddock3-re = haddock.clis.cli_re:maincli", "haddock3-restraints = haddock.clis.cli_restraints:maincli", - "haddock3-check = haddock.clis.cli_check_install:main", ] }, cmdclass={"build_ext": CustomBuild, "install": CustomInstall}, + ext_modules=cpp_extensions, ) From d1175249ca81dd2689918fa934c7209f8d186d41 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Mon, 16 Sep 2024 17:47:43 +0200 Subject: [PATCH 21/62] Update repository things --- .gitignore | 3 +-- MANIFEST.in | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 7c34e505b..fb841385f 100644 --- a/.gitignore +++ b/.gitignore @@ -154,6 +154,5 @@ docs/haddock.modules.rst docs/haddock.modules.base_cns_module.rst docs/setup.rst docs/clients/*rst -src/fast-rmsdmatrix -src/fcc +src/haddock/bin/ log diff --git a/MANIFEST.in b/MANIFEST.in index 370d15e4a..0dbcc6531 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -34,3 +34,5 @@ recursive-include varia *.f recursive-include varia *.inc recursive-include varia *.lua recursive-include varia *.md + +include src/haddock/bin/* From 95d6ed6152f7cff0fcf357cbd109431075a97b5e Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Mon, 16 Sep 2024 17:47:54 +0200 Subject: [PATCH 22/62] Add new paths to the binaries --- src/haddock/core/defaults.py | 5 +- .../modules/analysis/clustfcc/__init__.py | 30 +-- .../modules/analysis/ilrmsdmatrix/__init__.py | 174 +++++++----------- .../modules/analysis/rmsdmatrix/__init__.py | 113 +++++------- 4 files changed, 129 insertions(+), 193 deletions(-) diff --git a/src/haddock/core/defaults.py b/src/haddock/core/defaults.py index 4a9e7ddf0..2428ec99d 100644 --- a/src/haddock/core/defaults.py +++ b/src/haddock/core/defaults.py @@ -5,6 +5,7 @@ from pathlib import Path import yaml +from pkg_resources import resource_filename import haddock from haddock import core_path @@ -12,12 +13,14 @@ BINARY_DIR = Path(importlib.resources.files(haddock) / "bin") # type: ignore -cns_exec = Path(BINARY_DIR, "cns") +cns_exec = Path(resource_filename("haddock", "bin/cns")) if not cns_exec.exists(): # This means the CNS binary is not in the default location # and should be set manually in the configuration file cns_exec = None +CONTACT_FCC_EXEC = Path(resource_filename("haddock", "bin/contact_fcc")) +FAST_RMSDMATRIX_EXEC = Path(resource_filename("haddock", "bin/fast-rmsdmatrix")) MODULE_PATH_NAME = "step_" """ diff --git a/src/haddock/modules/analysis/clustfcc/__init__.py b/src/haddock/modules/analysis/clustfcc/__init__.py index 2143e0f88..1ac3a01b0 100644 --- a/src/haddock/modules/analysis/clustfcc/__init__.py +++ b/src/haddock/modules/analysis/clustfcc/__init__.py @@ -13,7 +13,11 @@ import haddock from haddock import FCC_path, log -from haddock.core.defaults import BINARY_DIR, MODULE_DEFAULT_YAML +from haddock.core.defaults import ( + BINARY_DIR, + CONTACT_FCC_EXEC, + MODULE_DEFAULT_YAML, + ) from haddock.core.typing import Union from haddock.fcc import calc_fcc_matrix, cluster_fcc from haddock.libs.libclust import ( @@ -57,29 +61,7 @@ def confirm_installation(cls) -> None: # The FCC binary can be either in the default binary path or in the dcfg = read_from_yaml_config(DEFAULT_CONFIG) - default_binary = Path(BINARY_DIR, "contact_fcc") - user_supplied_binary = Path(dcfg["executable"]) - - if user_supplied_binary.exists(): - log.info("Using user supplied FCC binary: %s", user_supplied_binary) - else: - log.warning("User supplied FCC binary not found: %s", user_supplied_binary) - if default_binary.exists(): - log.info("Using default FCC binary: %s", default_binary) - dcfg["executable"] = default_binary - else: - log.error("Default FCC binary not found: %s", default_binary) - raise FileNotFoundError( - f"Default FCC binary not found: {default_binary}" - ) - - # Check if the binary is executable - exec_path = Path(dcfg["executable"]) - if not os.access(exec_path, mode=os.F_OK): - raise Exception(f"Required {str(exec_path)} file does not exist.") - - if not os.access(exec_path, mode=os.X_OK): - raise Exception(f"Required {str(exec_path)} file is not executable") + dcfg["executable"] = CONTACT_FCC_EXEC def _run(self) -> None: """Execute module.""" diff --git a/src/haddock/modules/analysis/ilrmsdmatrix/__init__.py b/src/haddock/modules/analysis/ilrmsdmatrix/__init__.py index e6595869c..ce1e64595 100644 --- a/src/haddock/modules/analysis/ilrmsdmatrix/__init__.py +++ b/src/haddock/modules/analysis/ilrmsdmatrix/__init__.py @@ -15,13 +15,14 @@ chains, as no alignment is performed. The user must ensure that the numbering is coherent. """ + import os from pathlib import Path import numpy as np -from haddock import log, RMSD_path -from haddock.core.defaults import MODULE_DEFAULT_YAML +from haddock import RMSD_path, log +from haddock.core.defaults import FAST_RMSDMATRIX_EXEC, MODULE_DEFAULT_YAML from haddock.libs.libalign import ( check_chains, check_common_atoms, @@ -32,32 +33,24 @@ from haddock.libs.libontology import ModuleIO, RMSDFile from haddock.libs.libparallel import get_index_list from haddock.libs.libutil import parse_ncores -from haddock.modules import BaseHaddockModule -from haddock.modules import get_engine +from haddock.modules import BaseHaddockModule, get_engine from haddock.modules.analysis import get_analysis_exec_mode -from haddock.modules.analysis.ilrmsdmatrix.ilrmsd import ( - Contact, - ContactJob, - ) -from haddock.modules.analysis.rmsdmatrix import rmsd_dispatcher, RMSDJob -from haddock.modules.analysis.rmsdmatrix.rmsd import ( - XYZWriter, - XYZWriterJob, - ) +from haddock.modules.analysis.ilrmsdmatrix.ilrmsd import Contact, ContactJob +from haddock.modules.analysis.rmsdmatrix import RMSDJob, rmsd_dispatcher +from haddock.modules.analysis.rmsdmatrix.rmsd import XYZWriter, XYZWriterJob RECIPE_PATH = Path(__file__).resolve().parent DEFAULT_CONFIG = Path(RECIPE_PATH, MODULE_DEFAULT_YAML) -EXEC_PATH = Path(RMSD_path, "src/fast-rmsdmatrix") +EXEC_PATH = FAST_RMSDMATRIX_EXEC class HaddockModule(BaseHaddockModule): """HADDOCK3 module for clustering with RMSD.""" name = RECIPE_PATH.name - - def __init__(self, order, path, *ignore, init_params=DEFAULT_CONFIG, - **everything): + + def __init__(self, order, path, *ignore, init_params=DEFAULT_CONFIG, **everything): super().__init__(order, path, init_params) @classmethod @@ -67,12 +60,10 @@ def confirm_installation(cls) -> None: raise Exception( f"Required {str(EXEC_PATH)} file does not exist.{os.linesep}" "Old HADDOCK3 installation? Please follow the new installation instructions at https://github.com/haddocking/haddock3/blob/main/docs/INSTALL.md" # noqa : E501 - ) + ) if not os.access(EXEC_PATH, mode=os.X_OK): - raise Exception( - f"Required {str(EXEC_PATH)} file is not executable" - ) + raise Exception(f"Required {str(EXEC_PATH)} file is not executable") return @@ -82,7 +73,7 @@ def _rearrange_output(output_name, path, ncores): output_fname = Path(path, output_name) log.info(f"rearranging output files into {output_fname}") # Combine files - with open(output_fname, 'w') as out_file: + with open(output_fname, "w") as out_file: for core in range(ncores): tmp_file = Path(path, "ilrmsd_" + str(core) + ".matrix") with open(tmp_file) as infile: @@ -95,7 +86,7 @@ def _rearrange_output(output_name, path, ncores): @staticmethod def _rearrange_contact_output(output_name, path, ncores): """Combine different contact outputs in a single file. - + Parameters ---------- output_name : str @@ -104,7 +95,7 @@ def _rearrange_contact_output(output_name, path, ncores): Path to the output file. ncores : int Number of cores used in the calculation. - + Returns ------- res_resdic_npu : dict @@ -124,7 +115,7 @@ def _rearrange_contact_output(output_name, path, ncores): else: res_resdic[ch].append(resids) tmp_file.unlink() - + # Unique residues npu_resdic = {} for ch in res_resdic.keys(): @@ -134,7 +125,7 @@ def _rearrange_contact_output(output_name, path, ncores): log.info(f"Overall interface residues: {npu_resdic}") # Write to file - with open(output_fname, 'w') as out_file: + with open(output_fname, "w") as out_file: for chain in npu_resdic.keys(): out_file.write(f"{chain} ") out_file.write(" ".join([str(el) for el in npu_resdic[chain]])) @@ -152,36 +143,32 @@ def _run(self) -> None: self.finish_with_error(_e) # Get the models generated in previous step - models = self.previous_io.retrieve_models( - individualize=True - ) + models = self.previous_io.retrieve_models(individualize=True) # Parallelisation : optimal dispatching of models nmodels = len(models) - ncores = parse_ncores(n=self.params['ncores'], njobs=nmodels) - - if nmodels > self.params['max_models']: + ncores = parse_ncores(n=self.params["ncores"], njobs=nmodels) + + if nmodels > self.params["max_models"]: # too many input models : ilRMSD matrix would be too big => Abort! raise Exception( f"Too many models ({nmodels} > {self.params['max_models']}) " "for ilRMSD matrix calculation" - ) + ) # find the existing chains model_atoms = get_atoms(models[0]) mod_coord_dic, _ = load_coords( - models[0], - model_atoms, + models[0], + model_atoms, ) obs_chains = np.unique([el[0] for el in mod_coord_dic.keys()]) obs_chains = list(obs_chains) log.info(f"Observed chains: {obs_chains}") # assigning the chains to the receptor and ligand r_chain, l_chains = check_chains( - obs_chains, - self.params["receptor_chain"], - self.params["ligand_chains"] - ) + obs_chains, self.params["receptor_chain"], self.params["ligand_chains"] + ) log.info(f"Receptor chain: {r_chain}") log.info(f"Ligand chains: {l_chains}") self.params["receptor_chain"] = r_chain @@ -194,13 +181,13 @@ def _run(self) -> None: for core in range(ncores): output_name = "interface_contacts_" + str(core) + ".con" contact_obj = Contact( - model_list=models[index_list[core]:index_list[core + 1]], + model_list=models[index_list[core] : index_list[core + 1]], output_name=output_name, core=core, path=Path("."), contact_distance_cutoff=self.params["contact_distance_cutoff"], params=self.params, - ) + ) # running now the ContactJob job_f = Path(output_name) # init RMSDJob @@ -208,7 +195,7 @@ def _run(self) -> None: job_f, self.params, contact_obj, - ) + ) contact_jobs.append(job) exec_mode = get_analysis_exec_mode(self.params["mode"]) @@ -222,23 +209,22 @@ def _run(self) -> None: for job in contact_jobs: if not job.output.exists(): not_found.append(job.output.name) - wrn = f'Contacts were not calculated for {job.output.name}' + wrn = f"Contacts were not calculated for {job.output.name}" log.warning(wrn) else: contact_file_l.append(str(job.output)) - + if not_found: # Not all contacts were calculated, cannot proceed - self.finish_with_error("Several contact files were not generated:" - f" {not_found}") - + self.finish_with_error( + "Several contact files were not generated:" f" {not_found}" + ) + # Post-processing : single file output_name = "receptor_contacts.con" res_resdic = self._rearrange_contact_output( - output_name, - path=contact_obj.path, - ncores=ncores - ) + output_name, path=contact_obj.path, ncores=ncores + ) # if the receptor chain in res_resdic is empty, then the receptor has made no contacts and # the ilrmsd matrix cannot be calculated. This probably means that single chains structures @@ -247,97 +233,83 @@ def _run(self) -> None: _msg = f"No contacts found for receptor chain {r_chain}. Impossible to calculate ilRMSD matrix." _msg += " Please check your input and make sure that there are at least two chains in contact." self.finish_with_error(_msg) - + rec_traj_filename = Path("traj_rec.xyz") lig_traj_filename = Path("traj_lig.xyz") - res_resdic_rec = { - k: res_resdic[k] - for k in res_resdic - if k[0] == r_chain - } + res_resdic_rec = {k: res_resdic[k] for k in res_resdic if k[0] == r_chain} # ligand_chains is a list of chains - res_resdic_lig = { - k: res_resdic[k] - for k in l_chains - } + res_resdic_lig = {k: res_resdic[k] for k in l_chains} - log.info(f"Check common atoms for receptor (chain {list(res_resdic_rec.keys())})") + log.info( + f"Check common atoms for receptor (chain {list(res_resdic_rec.keys())})" + ) n_atoms_rec, common_keys_rec = check_common_atoms( models, res_resdic_rec, self.params["allatoms"], - self.params["atom_similarity"] - ) - - log.info(f"Check common atoms for ligand (chains {list(res_resdic_lig.keys())})") + self.params["atom_similarity"], + ) + + log.info( + f"Check common atoms for ligand (chains {list(res_resdic_lig.keys())})" + ) n_atoms_lig, common_keys_lig = check_common_atoms( models, res_resdic_lig, self.params["allatoms"], - self.params["atom_similarity"] - ) - + self.params["atom_similarity"], + ) + xyzwriter_jobs: list[XYZWriterJob] = [] for core in range(ncores): output_name_rec = Path("traj_rec_" + str(core) + ".xyz") # init XYZWriter xyzwriter_obj_rec = XYZWriter( - model_list=models[index_list[core]:index_list[core + 1]], + model_list=models[index_list[core] : index_list[core + 1]], output_name=output_name_rec, core=core, n_atoms=n_atoms_rec, common_keys=common_keys_rec, filter_resdic=res_resdic_rec, allatoms=self.params["allatoms"], - ) + ) # job_rec job_rec = XYZWriterJob( xyzwriter_obj_rec, - ) - + ) + xyzwriter_jobs.append(job_rec) output_name_lig = Path("traj_lig_" + str(core) + ".xyz") # init XYZWriter xyzwriter_obj_lig = XYZWriter( - model_list=models[index_list[core]:index_list[core + 1]], + model_list=models[index_list[core] : index_list[core + 1]], output_name=output_name_lig, core=core, n_atoms=n_atoms_lig, common_keys=common_keys_lig, filter_resdic=res_resdic_lig, allatoms=self.params["allatoms"], - ) + ) # job_lig job_lig = XYZWriterJob( xyzwriter_obj_lig, - ) + ) xyzwriter_jobs.append(job_lig) - + # run jobs engine = Engine(xyzwriter_jobs) engine.run() - rearrange_xyz_files( - rec_traj_filename, - path=Path("."), - ncores=ncores - ) - rearrange_xyz_files( - lig_traj_filename, - path=Path("."), - ncores=ncores - ) + rearrange_xyz_files(rec_traj_filename, path=Path("."), ncores=ncores) + rearrange_xyz_files(lig_traj_filename, path=Path("."), ncores=ncores) # Parallelisation : optimal dispatching of models tot_npairs = nmodels * (nmodels - 1) // 2 - ncores = parse_ncores(n=self.params['ncores'], njobs=tot_npairs) + ncores = parse_ncores(n=self.params["ncores"], njobs=tot_npairs) log.info(f"total number of pairs {tot_npairs}") - npairs, ref_structs, mod_structs = rmsd_dispatcher( - nmodels, - tot_npairs, - ncores) + npairs, ref_structs, mod_structs = rmsd_dispatcher(nmodels, tot_npairs, ncores) # Calculate the rmsd for each set of models ilrmsd_jobs: list[RMSDJob] = [] @@ -358,7 +330,7 @@ def _run(self) -> None: n_atoms_rec, lig_traj_filename, n_atoms_lig, - ) + ) ilrmsd_jobs.append(job) ilrmsd_engine = Engine(ilrmsd_jobs) @@ -371,23 +343,18 @@ def _run(self) -> None: # NOTE: If there is no output, most likely the RMSD calculation # timed out not_found.append(j.output.name) - wrn = f'ilRMSD results were not calculated for {j.output.name}' + wrn = f"ilRMSD results were not calculated for {j.output.name}" log.warning(wrn) else: ilrmsd_file_l.append(str(j.output)) if not_found: # Not all distances were calculated, cannot create the full matrix - self.finish_with_error("Several files were not generated:" - f" {not_found}") + self.finish_with_error("Several files were not generated:" f" {not_found}") # Post-processing : single file output_name = "ilrmsd.matrix" - self._rearrange_output( - output_name, - path=Path("."), - ncores=ncores - ) + self._rearrange_output(output_name, path=Path("."), ncores=ncores) # Delete the trajectory files if rec_traj_filename.exists(): os.unlink(rec_traj_filename) @@ -399,9 +366,6 @@ def _run(self) -> None: self.export_io_models() # Sending matrix path to the next step of the workflow matrix_io = ModuleIO() - ilrmsd_matrix_file = RMSDFile( - output_name, - npairs=tot_npairs - ) + ilrmsd_matrix_file = RMSDFile(output_name, npairs=tot_npairs) matrix_io.add(ilrmsd_matrix_file) matrix_io.save(filename="rmsd_matrix.json") diff --git a/src/haddock/modules/analysis/rmsdmatrix/__init__.py b/src/haddock/modules/analysis/rmsdmatrix/__init__.py index 4d8cc5fa5..07e4047ac 100644 --- a/src/haddock/modules/analysis/rmsdmatrix/__init__.py +++ b/src/haddock/modules/analysis/rmsdmatrix/__init__.py @@ -25,34 +25,34 @@ thus telling the module to consider residues from 1 to 4 of chain A and from 2 to 4 of chain B for the alignment and RMSD calculation. """ + import contextlib -from pathlib import Path import os +from pathlib import Path -from haddock import log, RMSD_path -from haddock.core.defaults import MODULE_DEFAULT_YAML +from haddock import RMSD_path, log +from haddock.core.defaults import FAST_RMSDMATRIX_EXEC, MODULE_DEFAULT_YAML from haddock.core.typing import Any, AtomsDict, FilePath -from haddock.libs.libalign import rearrange_xyz_files, check_common_atoms +from haddock.libs.libalign import check_common_atoms, rearrange_xyz_files from haddock.libs.libontology import ModuleIO, RMSDFile from haddock.libs.libparallel import get_index_list from haddock.libs.libutil import parse_ncores -from haddock.modules import BaseHaddockModule -from haddock.modules import get_engine +from haddock.modules import BaseHaddockModule, get_engine from haddock.modules.analysis import ( confirm_resdic_chainid_length, get_analysis_exec_mode, ) from haddock.modules.analysis.rmsdmatrix.rmsd import ( RMSDJob, - rmsd_dispatcher, XYZWriter, - XYZWriterJob + XYZWriterJob, + rmsd_dispatcher, ) RECIPE_PATH = Path(__file__).resolve().parent DEFAULT_CONFIG = Path(RECIPE_PATH, MODULE_DEFAULT_YAML) -EXEC_PATH = Path(RMSD_path, "src/fast-rmsdmatrix") +EXEC_PATH = FAST_RMSDMATRIX_EXEC class HaddockModule(BaseHaddockModule): @@ -60,10 +60,9 @@ class HaddockModule(BaseHaddockModule): name = RECIPE_PATH.name - def __init__(self, - order: int, - path: Path, - initial_params: FilePath = DEFAULT_CONFIG) -> None: + def __init__( + self, order: int, path: Path, initial_params: FilePath = DEFAULT_CONFIG + ) -> None: super().__init__(order, path, initial_params) @classmethod @@ -71,21 +70,24 @@ def confirm_installation(cls) -> None: """Confirm if fast-rmsdmatrix is installed and available.""" if not os.access(EXEC_PATH, mode=os.F_OK): - raise Exception(f"Required {str(EXEC_PATH)} file does not exist.{os.linesep}" - "Old HADDOCK3 installation? Please follow the new installation instructions at https://github.com/haddocking/haddock3/blob/main/docs/INSTALL.md") + raise Exception( + f"Required {str(EXEC_PATH)} file does not exist.{os.linesep}" + "Old HADDOCK3 installation? Please follow the new installation instructions at https://github.com/haddocking/haddock3/blob/main/docs/INSTALL.md" + ) if not os.access(EXEC_PATH, mode=os.X_OK): raise Exception(f"Required {str(EXEC_PATH)} file is not executable") return - def _rearrange_output(self, output_name: FilePath, path: FilePath, - ncores: int) -> None: + def _rearrange_output( + self, output_name: FilePath, path: FilePath, ncores: int + ) -> None: """Combine different rmsd outputs in a single file.""" output_fname = Path(path, output_name) self.log(f"rearranging output files into {output_fname}") # Combine files - with open(output_fname, 'w') as out_file: + with open(output_fname, "w") as out_file: for core in range(ncores): tmp_file = Path(path, "rmsd_" + str(core) + ".matrix") with open(tmp_file) as infile: @@ -111,72 +113,65 @@ def _run(self) -> None: self.finish_with_error(_e) # Get the models generated in previous step - models = self.previous_io.retrieve_models( - individualize=True - ) + models = self.previous_io.retrieve_models(individualize=True) nmodels = len(models) if nmodels > self.params["max_models"]: # too many input models : RMSD matrix would be too big => Abort! raise Exception("Too many models for RMSD matrix calculation") - + # index_list for the jobs with linear scaling - ncores = parse_ncores(n=self.params['ncores'], njobs=len(models)) + ncores = parse_ncores(n=self.params["ncores"], njobs=len(models)) index_list = get_index_list(nmodels, ncores) - + traj_filename = Path("traj.xyz") filter_resdic = { - key[-1]: value for key, value - in self.params.items() + key[-1]: value + for key, value in self.params.items() if key.startswith("resdic") - } - + } + # check common atoms - n_atoms, common_keys = check_common_atoms(models, - filter_resdic, - self.params["allatoms"], - self.params["atom_similarity"]) - + n_atoms, common_keys = check_common_atoms( + models, + filter_resdic, + self.params["allatoms"], + self.params["atom_similarity"], + ) + xyzwriter_jobs: list[XYZWriterJob] = [] for core in range(ncores): output_name = Path("traj_" + str(core) + ".xyz") # init RMSDJobFast xyzwriter_obj = XYZWriter( - model_list=models[index_list[core]:index_list[core + 1]], + model_list=models[index_list[core] : index_list[core + 1]], output_name=output_name, core=core, n_atoms=n_atoms, common_keys=common_keys, filter_resdic=filter_resdic, allatoms=self.params["allatoms"], - ) - #job_f = output_name + ) + # job_f = output_name job = XYZWriterJob( xyzwriter_obj, - ) + ) xyzwriter_jobs.append(job) - + # run jobs exec_mode = get_analysis_exec_mode(self.params["mode"]) Engine = get_engine(exec_mode, self.params) engine = Engine(xyzwriter_jobs) engine.run() - rearrange_xyz_files( - traj_filename, - path=Path("."), - ncores=ncores - ) - + rearrange_xyz_files(traj_filename, path=Path("."), ncores=ncores) + # Parallelisation : optimal dispatching of models tot_npairs = nmodels * (nmodels - 1) // 2 log.info(f"total number of pairs {tot_npairs}") - ncores = parse_ncores(n=self.params['ncores'], njobs=tot_npairs) - npairs, ref_structs, mod_structs = rmsd_dispatcher( - nmodels, - tot_npairs, - ncores) + ncores = parse_ncores(n=self.params["ncores"], njobs=tot_npairs) + npairs, ref_structs, mod_structs = rmsd_dispatcher(nmodels, tot_npairs, ncores) # Calculate the rmsd for each set of models rmsd_jobs: list[RMSDJob] = [] @@ -194,9 +189,9 @@ def _run(self) -> None: mod_structs[core], len(models), n_atoms, - ) + ) rmsd_jobs.append(job) - + engine = Engine(rmsd_jobs) engine.run() @@ -207,23 +202,18 @@ def _run(self) -> None: # NOTE: If there is no output, most likely the RMSD calculation # timed out not_found.append(job.output.name) - wrn = f'Rmsd results were not calculated for {job.output.name}' + wrn = f"Rmsd results were not calculated for {job.output.name}" log.warning(wrn) else: rmsd_file_l.append(str(job.output)) if not_found: # Not all distances were calculated, cannot create the full matrix - self.finish_with_error("Several files were not generated:" - f" {not_found}") + self.finish_with_error("Several files were not generated:" f" {not_found}") # Post-processing : single file final_output_name = "rmsd.matrix" - self._rearrange_output( - final_output_name, - path=Path("."), - ncores=ncores - ) + self._rearrange_output(final_output_name, path=Path("."), ncores=ncores) # Delete the trajectory file if traj_filename.exists(): os.unlink(traj_filename) @@ -233,9 +223,6 @@ def _run(self) -> None: self.export_io_models() # Sending matrix path to the next step of the workflow matrix_io = ModuleIO() - rmsd_matrix_file = RMSDFile( - final_output_name, - npairs=tot_npairs - ) + rmsd_matrix_file = RMSDFile(final_output_name, npairs=tot_npairs) matrix_io.add(rmsd_matrix_file) matrix_io.save(filename="rmsd_matrix.json") From 79b4ab0892be3bbcfeb5d3638d2e9a80f11cf0e1 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Mon, 16 Sep 2024 17:53:33 +0200 Subject: [PATCH 23/62] update `tests.yml` --- .github/workflows/tests.yml | 75 +++---------------------------------- 1 file changed, 6 insertions(+), 69 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 37d74bb82..7b7e624fc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,10 +6,6 @@ on: pull_request: branches: [main] -env: - CNS_EXEC: ${{ github.workspace }}/bin/cns - PYTHONPATH: ${{ github.workspace }}/src - jobs: unit: runs-on: ubuntu-latest @@ -38,80 +34,21 @@ jobs: pip install virtualenv==20.23.0 tox==4.5.1.1 - name: install HADDOCK - run: | - pwd - ls -lsa - mkdir bin - touch bin/cns - cd src/fcc/src - chmod u+x Makefile - ./Makefile 2>%1 >/dev/null || true - cd - - cd src/fast-rmsdmatrix/src - chmod u+x Makefile - make fast-rmsdmatrix - cd - - + run: python setup.py develop - name: types run: tox -e types - name: unit tests + id: unit run: tox -e test + - name: integration + if: steps.unit.outcome == 'success' + run: tox -e integration + - uses: codacy/codacy-coverage-reporter-action@v1 if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} with: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} coverage-reports: ./coverage.xml - - integration: - needs: unit - runs-on: ubuntu-latest - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} - strategy: - matrix: - python-version: [3.9] - - steps: - - uses: actions/checkout@v2 - with: - submodules: recursive - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install openmpi-bin libopenmpi3 libopenmpi-dev - - - name: install dependencies - run: | - python -m pip install pip==23.1.2 setuptools==67.7.2 wheel==0.40.0 - pip install virtualenv==20.23.0 tox==4.5.1.1 - - - name: install HADDOCK - run: | - pwd - ls -lsa - mkdir bin - touch bin/cns - cd src/fcc/src - chmod u+x Makefile - ./Makefile 2>%1 >/dev/null || true - cd - - cd src/fast-rmsdmatrix/src - chmod u+x Makefile - make fast-rmsdmatrix - cd - - - - name: install integration dependencies - run: | - curl ${{ secrets.CNS_LINK }} -o $CNS_EXEC -s - chmod +x $CNS_EXEC - - - name: run integration tests - run: tox -e integration From bde7fb49b427db170d5911a614b3b0b569f813f7 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Mon, 16 Sep 2024 18:02:15 +0200 Subject: [PATCH 24/62] change `cns_exec` [wip] --- src/haddock/core/defaults.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/haddock/core/defaults.py b/src/haddock/core/defaults.py index 2428ec99d..fa8a8805f 100644 --- a/src/haddock/core/defaults.py +++ b/src/haddock/core/defaults.py @@ -14,10 +14,6 @@ BINARY_DIR = Path(importlib.resources.files(haddock) / "bin") # type: ignore cns_exec = Path(resource_filename("haddock", "bin/cns")) -if not cns_exec.exists(): - # This means the CNS binary is not in the default location - # and should be set manually in the configuration file - cns_exec = None CONTACT_FCC_EXEC = Path(resource_filename("haddock", "bin/contact_fcc")) FAST_RMSDMATRIX_EXEC = Path(resource_filename("haddock", "bin/fast-rmsdmatrix")) From c2039780e75564501fb375d1431157be8cd15d8b Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Mon, 16 Sep 2024 18:09:22 +0200 Subject: [PATCH 25/62] disable type test [temp] --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7b7e624fc..456c1c160 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -36,8 +36,8 @@ jobs: - name: install HADDOCK run: python setup.py develop - - name: types - run: tox -e types + # - name: types + # run: tox -e types - name: unit tests id: unit From bd38a49ea8d6c4d877d329c4d028b97df1c12fd4 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Mon, 16 Sep 2024 18:23:14 +0200 Subject: [PATCH 26/62] update `CONTACT_FCC_EXEC` path --- src/haddock/modules/analysis/clustfcc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/haddock/modules/analysis/clustfcc/__init__.py b/src/haddock/modules/analysis/clustfcc/__init__.py index 484cf0df2..952892866 100644 --- a/src/haddock/modules/analysis/clustfcc/__init__.py +++ b/src/haddock/modules/analysis/clustfcc/__init__.py @@ -83,7 +83,7 @@ def _run(self) -> None: job = JobInputFirst( pdb_f, contact_f, - contact_executable, + CONTACT_FCC_EXEC, self.params["contact_distance_cutoff"], ) contact_jobs.append(job) From 48582b0734f0141b3201e48e96d9203871b25182 Mon Sep 17 00:00:00 2001 From: VGPReys Date: Tue, 17 Sep 2024 09:53:21 +0200 Subject: [PATCH 27/62] add new tests for counts checks --- src/haddock/gear/expandable_parameters.py | 2 +- tests/test_gear_expandable_parameters.py | 117 ++++++++++++++++++---- 2 files changed, 101 insertions(+), 18 deletions(-) diff --git a/src/haddock/gear/expandable_parameters.py b/src/haddock/gear/expandable_parameters.py index 0fddcab5c..db41380ad 100644 --- a/src/haddock/gear/expandable_parameters.py +++ b/src/haddock/gear/expandable_parameters.py @@ -330,7 +330,7 @@ def _read_groups_in_user_config( # Increment counts for this parameter if related_params: param_name_counts.setdefault(param_name, 0) - param_name_counts[param_name] = int(group_idx) + param_name_counts[param_name] += 1 new.update(related_params) return new, param_name_counts diff --git a/tests/test_gear_expandable_parameters.py b/tests/test_gear_expandable_parameters.py index 559f83c77..be3173ca9 100644 --- a/tests/test_gear_expandable_parameters.py +++ b/tests/test_gear_expandable_parameters.py @@ -1,6 +1,6 @@ """Test expandable parameter modules.""" -import importlib +import importlib import pytest from haddock.core.exceptions import ConfigurationError @@ -423,7 +423,7 @@ def test_read_single_idx_groups_user_config( expected, ): """Test read single index groups in user config.""" - result = read_single_idx_groups_user_config(user_config, default_groups) + result, _ = read_single_idx_groups_user_config(user_config, default_groups) assert result == expected @@ -431,32 +431,115 @@ def test_read_single_idx_groups_user_config( "user_config,default_groups,expected", [ ( - {"param_other_1", "param_else_1", - "param_other_2", "param_else_2", - "param_1", - "param_sam_1_1", "param_frodo_1_1", - "param_sam_1_2", "param_frodo_1_2", - "param_sam_2_1", "param_frodo_2_1", - }, - {("param1", "1"): {"sam", "frodo"}, - ("param2", "1"): {"sam", "frodo"}}, - {"param_frodo_1_1", "param_sam_1_1", - "param_frodo_1_2", "param_sam_1_2", - "param_sam_2_1", "param_frodo_2_1", - }, + # user_config + { + "param_other_1", "param_else_1", # param 1 + "param_other_2", "param_else_2", # param 2 + "param_1", # not a single index group + "param_other_1_1", # multiple index group + }, + # default_groups + { + ("param", "1"): {"other", "else"}, + }, + # expected + { + "param": 2, + }, ), ] ) -def test_read_multiple_idx_groups_user_config( +def test_read_single_idx_groups_user_config_count( user_config, default_groups, expected, ): """Test read single index groups in user config.""" - result = read_multiple_idx_groups_user_config(user_config, default_groups) + _, counts = read_single_idx_groups_user_config(user_config, default_groups) + assert counts == expected + + +@pytest.mark.parametrize( + "user_config,default_groups,expected", + [ + ( + # user_config + { + # single index ones should NOT be considered + "param_other_1", "param_else_1", + "param_other_2", "param_else_2", + "param_1", + # multiple index ones should be considered + "param_sam_1_1", "param_frodo_1_1", + "param_sam_1_2", "param_frodo_1_2", + "param_sam_2_1", "param_frodo_2_1", + }, + # default_groups + { + ("param1", "1"): {"sam", "frodo"}, + ("param2", "1"): {"sam", "frodo"}, + }, + # expected + { + "param_frodo_1_1", "param_sam_1_1", + "param_frodo_1_2", "param_sam_1_2", + "param_sam_2_1", "param_frodo_2_1", + }, + ), + ] + ) +def test_read_multiple_idx_groups_user_config( + user_config, + default_groups, + expected, + ): + """Test read multiple index groups in user config.""" + result, _ = read_multiple_idx_groups_user_config( + user_config, default_groups, + ) assert result == expected +@pytest.mark.parametrize( + "user_config,default_groups,expected", + [ + ( + # user_config + { + # single index ones should NOT be considered + "param_other_1", "param_else_1", + "param_other_2", "param_else_2", + "param_1", + # multiple index ones should be considered + "param_sam_1_1", "param_frodo_1_1", # param1 1 + "param_sam_1_2", "param_frodo_1_2", # param1 2 + "param_sam_2_1", "param_frodo_2_1", # param2 1 + }, + # default_groups + { + ("param1", "1"): {"sam", "frodo"}, + ("param2", "1"): {"sam", "frodo"}, + }, + # expected counts + { + "param1": 2, + "param2": 1, + }, + ), + ] + ) +def test_read_multiple_idx_groups_user_config_counts( + user_config, + default_groups, + expected, + ): + """Test counts multiple index groups in user config.""" + _, counts = read_multiple_idx_groups_user_config( + user_config, default_groups, + ) + assert counts == expected + + @pytest.mark.parametrize( "user_config,default_groups", [ From 0a75976cab204f8967937e183c2a5cc534506941 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 17 Sep 2024 10:16:49 +0200 Subject: [PATCH 28/62] add links --- setup.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 3c5d2f297..305ea57fd 100644 --- a/setup.py +++ b/setup.py @@ -33,11 +33,11 @@ class CustomBuild(build_ext): def run(self): print("Building HADDOCK3 C/C++ binary dependencies...") - self.build_cpp_executable( + self.build_executable( "contact_fcc", ["g++", "-O2", "-o", "contact_fcc", "src/haddock/deps/contact_fcc.cpp"], ) - self.build_cpp_executable( + self.build_executable( "fast-rmsdmatrix", [ "gcc", @@ -55,7 +55,7 @@ def run(self): # Run the standard build_ext build_ext.run(self) - def build_cpp_executable(self, name, cmd): + def build_executable(self, name, cmd): try: subprocess.check_call(cmd) bin_dir = os.path.join("src", "haddock", "bin") @@ -68,11 +68,10 @@ def build_cpp_executable(self, name, cmd): CNS_BINARIES = { - "x86_64-linux": "https://surfdrive.surf.nl/files/index.php/s/o8X7zZezIM3P0cE/download", # linux - "x86_64-darwin": "", # macOs - "arm64-darwin": "", # Mac M1/M2 - "aarch64-linux": "", # linux ARM - "armv7l-linux": "", # 32-bit ARM linux, like raspberryPi + "x86_64-linux": "https://surfdrive.surf.nl/files/index.php/s/BWa5OimzbNliTi6/download", + "x86_64-darwin": "https://surfdrive.surf.nl/files/index.php/s/3Fzzte0Zx0L8GTY/download", + "arm64-darwin": "https://surfdrive.surf.nl/files/index.php/s/bYB3xPWf7iwo07X/download", + "aarch64-linux": "https://surfdrive.surf.nl/files/index.php/s/3rHpxcufHGrntHn/download", } From 6e7e60ebf89a1287df77d1afea024e33fa0da708 Mon Sep 17 00:00:00 2001 From: VGPReys Date: Tue, 17 Sep 2024 11:10:27 +0200 Subject: [PATCH 29/62] also update symmetry parameters / cns code --- .../docking-protein-homotrimer-full.cfg | 6 ---- .../docking-protein-homotrimer-test.cfg | 8 +----- .../modules/refinement/emref/cns/emref.cns | 12 ++++---- .../refinement/emref/cns/symmultimer.cns | 12 ++++---- .../modules/refinement/emref/defaults.yaml | 28 +++++++++---------- .../refinement/flexref/cns/check-homomers.cns | 12 ++++---- .../refinement/flexref/cns/flexref.cns | 12 ++++---- .../refinement/flexref/cns/symmultimer.cns | 12 ++++---- .../modules/refinement/flexref/defaults.yaml | 28 +++++++++---------- .../modules/refinement/mdref/cns/mdref.cns | 12 ++++---- .../refinement/mdref/cns/symmultimer.cns | 12 ++++---- .../modules/refinement/mdref/defaults.yaml | 28 +++++++++---------- .../sampling/rigidbody/cns/check-homomers.cns | 12 ++++---- .../sampling/rigidbody/cns/randomairs.cns | 4 +-- .../sampling/rigidbody/cns/rigidbody.cns | 24 ++++++++-------- .../sampling/rigidbody/cns/symmultimer.cns | 12 ++++---- .../modules/sampling/rigidbody/defaults.yaml | 16 +++++------ 17 files changed, 119 insertions(+), 131 deletions(-) diff --git a/examples/docking-protein-homotrimer/docking-protein-homotrimer-full.cfg b/examples/docking-protein-homotrimer/docking-protein-homotrimer-full.cfg index cb519bef4..511f316cb 100644 --- a/examples/docking-protein-homotrimer/docking-protein-homotrimer-full.cfg +++ b/examples/docking-protein-homotrimer/docking-protein-homotrimer-full.cfg @@ -41,7 +41,6 @@ ambig_fname = "data/1qu9_whiscy_air.tbl" sampling = 2000 # Define CNS restraints between molecules ncs_on = true -numncs = 2 ncs_sta1_1=2 ncs_end1_1=128 ncs_seg1_1="A" @@ -56,7 +55,6 @@ ncs_end2_2=128 ncs_seg2_2="C" # Define C3 symmetry restraints sym_on = true -numc3sym = 1 c3sym_sta1_1=2 c3sym_end1_1=128 c3sym_seg1_1="A" @@ -79,7 +77,6 @@ tolerance = 5 ambig_fname = "data/1qu9_whiscy_air.tbl" # Define NCS restraints between molecules ncs_on = true -numncs = 2 ncs_sta1_1=2 ncs_end1_1=128 ncs_seg1_1="A" @@ -94,7 +91,6 @@ ncs_end2_2=128 ncs_seg2_2="C" # Define C3 symmetry restraints sym_on = true -numc3sym = 1 c3sym_sta1_1=2 c3sym_end1_1=128 c3sym_seg1_1="A" @@ -113,7 +109,6 @@ tolerance = 5 ambig_fname = "data/1qu9_whiscy_air.tbl" # Define NCS restraints between molecules ncs_on = true -numncs = 2 ncs_sta1_1=2 ncs_end1_1=128 ncs_seg1_1="A" @@ -128,7 +123,6 @@ ncs_end2_2=128 ncs_seg2_2="C" # Define C3 symmetry restraints sym_on = true -numc3sym = 1 c3sym_sta1_1=2 c3sym_end1_1=128 c3sym_seg1_1="A" diff --git a/examples/docking-protein-homotrimer/docking-protein-homotrimer-test.cfg b/examples/docking-protein-homotrimer/docking-protein-homotrimer-test.cfg index 2be4416c8..33980d886 100644 --- a/examples/docking-protein-homotrimer/docking-protein-homotrimer-test.cfg +++ b/examples/docking-protein-homotrimer/docking-protein-homotrimer-test.cfg @@ -36,8 +36,7 @@ ambig_fname = "data/1qu9_whiscy_air.tbl" sampling = 20 # Define NCS restraints between molecules ncs_on = true -numncs = 2 -ncs_sta1_1=2 +ncs_sta1_1=1 ncs_end1_1=128 ncs_seg1_1="A" ncs_sta2_1=2 @@ -51,7 +50,6 @@ ncs_end2_2=128 ncs_seg2_2="C" # Define C3 symmetry restraints sym_on = true -numc3sym = 1 c3sym_sta1_1=2 c3sym_end1_1=128 c3sym_seg1_1="A" @@ -74,7 +72,6 @@ tolerance = 20 ambig_fname = "data/1qu9_whiscy_air.tbl" # Define NCS restraints between molecules ncs_on = true -numncs = 2 ncs_sta1_1=2 ncs_end1_1=128 ncs_seg1_1="A" @@ -89,7 +86,6 @@ ncs_end2_2=128 ncs_seg2_2="C" # Define C3 symmetry restraints sym_on = true -numc3sym = 1 c3sym_sta1_1=2 c3sym_end1_1=128 c3sym_seg1_1="A" @@ -108,7 +104,6 @@ tolerance = 20 ambig_fname = "data/1qu9_whiscy_air.tbl" # Define NCS restraints between molecules ncs_on = true -numncs = 2 ncs_sta1_1=2 ncs_end1_1=128 ncs_seg1_1="A" @@ -123,7 +118,6 @@ ncs_end2_2=128 ncs_seg2_2="C" # Define C3 symmetry restraints sym_on = true -numc3sym = 1 c3sym_sta1_1=2 c3sym_end1_1=128 c3sym_seg1_1="A" diff --git a/src/haddock/modules/refinement/emref/cns/emref.cns b/src/haddock/modules/refinement/emref/cns/emref.cns index 60f72c296..76006e293 100644 --- a/src/haddock/modules/refinement/emref/cns/emref.cns +++ b/src/haddock/modules/refinement/emref/cns/emref.cns @@ -64,12 +64,12 @@ evaluate ($data.numncs = $numncs) ! Symmetry restraints evaluate ($data.ksym = $ksym) evaluate ($Data.flags.sym = $sym_on) -evaluate ($data.numc2sym = $numc2sym) -evaluate ($data.numc3sym = $numc3sym) -evaluate ($data.nums3sym = $nums3sym) -evaluate ($data.numc4sym = $numc4sym) -evaluate ($data.numc5sym = $numc5sym) -evaluate ($data.numc6sym = $numc6sym) +evaluate ($data.nc2sym = $nc2sym) +evaluate ($data.nc3sym = $nc3sym) +evaluate ($data.ns3sym = $ns3sym) +evaluate ($data.nc4sym = $nc4sym) +evaluate ($data.nc5sym = $nc5sym) +evaluate ($data.nc6sym = $nc6sym) !Dihedral angle energy term: evaluate ($data.flags.dihed = true) diff --git a/src/haddock/modules/refinement/emref/cns/symmultimer.cns b/src/haddock/modules/refinement/emref/cns/symmultimer.cns index 662bfa630..4bc582995 100644 --- a/src/haddock/modules/refinement/emref/cns/symmultimer.cns +++ b/src/haddock/modules/refinement/emref/cns/symmultimer.cns @@ -18,7 +18,7 @@ if ($Data.flags.sym eq true) then ! Define C2 symmetry restraints for symmetrical multimers ! eval ($ncount = 0) - while ($ncount < $data.numc2sym) loop c2symloop + while ($ncount < $data.nc2sym) loop c2symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c2sym_sta1_$ncount) @@ -65,7 +65,7 @@ if ($Data.flags.sym eq true) then ! Define C3 symmetry restraints for symmetrical multimers ! eval ($ncount = 0) - while ($ncount < $data.numc3sym) loop c3symloop + while ($ncount < $data.nc3sym) loop c3symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c3sym_sta1_$ncount) @@ -142,7 +142,7 @@ if ($Data.flags.sym eq true) then ! Define S3 symmetry restraints for symmetrical multimers ! eval ($ncount = 0) - while ($ncount < $data.nums3sym) loop s3symloop + while ($ncount < $data.ns3sym) loop s3symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $s3sym_sta1_$ncount) @@ -218,7 +218,7 @@ if ($Data.flags.sym eq true) then ! eval ($istep = 10) eval ($ncount = 0) - while ($ncount < $data.numc4sym) loop c4symloop + while ($ncount < $data.nc4sym) loop c4symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c4sym_sta1_$ncount) @@ -369,7 +369,7 @@ if ($Data.flags.sym eq true) then ! eval ($istep = 4) eval ($ncount = 0) - while ($ncount < $data.numc5sym) loop c5symloop + while ($ncount < $data.nc5sym) loop c5symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c5sym_sta1_$ncount) @@ -482,7 +482,7 @@ if ($Data.flags.sym eq true) then ! eval ($istep = 10) eval ($ncount = 0) - while ($ncount < $data.numc6sym) loop c6symloop + while ($ncount < $data.nc6sym) loop c6symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c6sym_sta1_$ncount) diff --git a/src/haddock/modules/refinement/emref/defaults.yaml b/src/haddock/modules/refinement/emref/defaults.yaml index fdf506336..2b191a935 100644 --- a/src/haddock/modules/refinement/emref/defaults.yaml +++ b/src/haddock/modules/refinement/emref/defaults.yaml @@ -237,7 +237,7 @@ symtbl_fname: long: Filename of the custom symmetry restraints file. group: 'symmetry' explevel: expert -numc2sym: +nc2sym: default: 0 type: integer min: 0 @@ -248,7 +248,7 @@ numc2sym: For each, a pair of segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c2sym_sta1_1: default: .nan type: integer @@ -309,7 +309,7 @@ c2sym_seg2_1: long: Segment ID of second C2 segment group: 'symmetry' explevel: expert -numc3sym: +nc3sym: default: 0 type: integer min: 0 @@ -320,7 +320,7 @@ numc3sym: For each, three segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c3sym_sta1_1: default: .nan type: integer @@ -411,7 +411,7 @@ c3sym_seg3_1: long: Segment ID of third C3 segment group: 'symmetry' explevel: expert -numc4sym: +nc4sym: default: 0 type: integer min: 0 @@ -422,7 +422,7 @@ numc4sym: For each, four segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c4sym_sta1_1: default: .nan type: integer @@ -543,7 +543,7 @@ c4sym_seg4_1: long: Segment ID of fourth C4 segment group: 'symmetry' explevel: expert -numc5sym: +nc5sym: default: 0 type: integer min: 0 @@ -554,7 +554,7 @@ numc5sym: For each, five segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c5sym_sta1_1: default: .nan type: integer @@ -705,7 +705,7 @@ c5sym_seg5_1: long: Segment ID of fifth C5 segment group: 'symmetry' explevel: expert -numc6sym: +nc6sym: default: 0 type: integer min: 0 @@ -716,7 +716,7 @@ numc6sym: For each, six segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c6sym_sta1_1: default: .nan type: integer @@ -897,7 +897,7 @@ c6sym_seg6_1: long: Segment ID of sixth C6 segment group: 'symmetry' explevel: expert -nums3sym: +ns3sym: default: 0 type: integer min: 0 @@ -908,7 +908,7 @@ nums3sym: For each, three segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden s3sym_sta1_1: default: .nan type: integer @@ -1019,7 +1019,7 @@ kncs: long: Force constant for non-crystallographic restraints group: 'symmetry' explevel: expert -numncs: +nncs: default: 0 type: integer min: 0 @@ -1029,7 +1029,7 @@ numncs: long: Number of non-crystallographic symmetry restraints. For each pairs of segments will have to be defined. Note that those must contain exactly the same residues/atom combinations. group: 'symmetry' - explevel: expert + explevel: hidden ncs_sta1_1: default: .nan type: integer diff --git a/src/haddock/modules/refinement/flexref/cns/check-homomers.cns b/src/haddock/modules/refinement/flexref/cns/check-homomers.cns index 00a359955..7faf7a4c9 100644 --- a/src/haddock/modules/refinement/flexref/cns/check-homomers.cns +++ b/src/haddock/modules/refinement/flexref/cns/check-homomers.cns @@ -22,32 +22,32 @@ while ($ncount < $data.ncomponents) loop checkmol end loop checkmol if ($data.flags.sym eq true) then - if ($data.numc2sym > 0) then + if ($data.nc2sym > 0) then if ($n_moving_mol = 2) then evaluate ($homosymmetry = true) end if end if - if ($data.numc2sym = 6) then + if ($data.nc2sym = 6) then if ($n_moving_mol = 4) then evaluate ($homosymmetry = true) end if end if - if ($data.numc3sym > 0) then + if ($data.nc3sym > 0) then if ($n_moving_mol = 3) then evaluate ($homosymmetry = true) end if end if - if ($data.numc4sym > 0) then + if ($data.nc4sym > 0) then if ($n_moving_mol = 4) then evaluate ($homosymmetry = true) end if end if - if ($data.numc5sym > 0) then + if ($data.nc5sym > 0) then if ($n_moving_mol = 5) then evaluate ($homosymmetry = true) end if end if - if ($data.numc6sym > 0) then + if ($data.nc6sym > 0) then if ($n_moving_mol = 6) then evaluate ($homosymmetry = true) end if diff --git a/src/haddock/modules/refinement/flexref/cns/flexref.cns b/src/haddock/modules/refinement/flexref/cns/flexref.cns index f5959d7c9..58680ecb6 100644 --- a/src/haddock/modules/refinement/flexref/cns/flexref.cns +++ b/src/haddock/modules/refinement/flexref/cns/flexref.cns @@ -123,12 +123,12 @@ evaluate ($data.numncs = $numncs) ! Symmetry restraints evaluate ($data.ksym = $ksym) evaluate ($Data.flags.sym = $sym_on) -evaluate ($data.numc2sym = $numc2sym) -evaluate ($data.numc3sym = $numc3sym) -evaluate ($data.nums3sym = $nums3sym) -evaluate ($data.numc4sym = $numc4sym) -evaluate ($data.numc5sym = $numc5sym) -evaluate ($data.numc6sym = $numc6sym) +evaluate ($data.nc2sym = $nc2sym) +evaluate ($data.nc3sym = $nc3sym) +evaluate ($data.ns3sym = $ns3sym) +evaluate ($data.nc4sym = $nc4sym) +evaluate ($data.nc5sym = $nc5sym) +evaluate ($data.nc6sym = $nc6sym) !Electrostatics: evaluate ($Data.flags.dihed =$dihedflag) diff --git a/src/haddock/modules/refinement/flexref/cns/symmultimer.cns b/src/haddock/modules/refinement/flexref/cns/symmultimer.cns index 662bfa630..4bc582995 100644 --- a/src/haddock/modules/refinement/flexref/cns/symmultimer.cns +++ b/src/haddock/modules/refinement/flexref/cns/symmultimer.cns @@ -18,7 +18,7 @@ if ($Data.flags.sym eq true) then ! Define C2 symmetry restraints for symmetrical multimers ! eval ($ncount = 0) - while ($ncount < $data.numc2sym) loop c2symloop + while ($ncount < $data.nc2sym) loop c2symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c2sym_sta1_$ncount) @@ -65,7 +65,7 @@ if ($Data.flags.sym eq true) then ! Define C3 symmetry restraints for symmetrical multimers ! eval ($ncount = 0) - while ($ncount < $data.numc3sym) loop c3symloop + while ($ncount < $data.nc3sym) loop c3symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c3sym_sta1_$ncount) @@ -142,7 +142,7 @@ if ($Data.flags.sym eq true) then ! Define S3 symmetry restraints for symmetrical multimers ! eval ($ncount = 0) - while ($ncount < $data.nums3sym) loop s3symloop + while ($ncount < $data.ns3sym) loop s3symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $s3sym_sta1_$ncount) @@ -218,7 +218,7 @@ if ($Data.flags.sym eq true) then ! eval ($istep = 10) eval ($ncount = 0) - while ($ncount < $data.numc4sym) loop c4symloop + while ($ncount < $data.nc4sym) loop c4symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c4sym_sta1_$ncount) @@ -369,7 +369,7 @@ if ($Data.flags.sym eq true) then ! eval ($istep = 4) eval ($ncount = 0) - while ($ncount < $data.numc5sym) loop c5symloop + while ($ncount < $data.nc5sym) loop c5symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c5sym_sta1_$ncount) @@ -482,7 +482,7 @@ if ($Data.flags.sym eq true) then ! eval ($istep = 10) eval ($ncount = 0) - while ($ncount < $data.numc6sym) loop c6symloop + while ($ncount < $data.nc6sym) loop c6symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c6sym_sta1_$ncount) diff --git a/src/haddock/modules/refinement/flexref/defaults.yaml b/src/haddock/modules/refinement/flexref/defaults.yaml index 592412d5d..a4a2fbbf5 100644 --- a/src/haddock/modules/refinement/flexref/defaults.yaml +++ b/src/haddock/modules/refinement/flexref/defaults.yaml @@ -574,7 +574,7 @@ symtbl_fname: long: Filename of the custom symmetry restraints file. group: 'symmetry' explevel: expert -numc2sym: +nc2sym: default: 0 type: integer min: 0 @@ -585,7 +585,7 @@ numc2sym: For each, a pair of segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c2sym_sta1_1: default: .nan type: integer @@ -646,7 +646,7 @@ c2sym_seg2_1: long: Segment ID of second C2 segment group: 'symmetry' explevel: expert -numc3sym: +nc3sym: default: 0 type: integer min: 0 @@ -657,7 +657,7 @@ numc3sym: For each, three segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c3sym_sta1_1: default: .nan type: integer @@ -748,7 +748,7 @@ c3sym_seg3_1: long: Segment ID of third C3 segment group: 'symmetry' explevel: expert -numc4sym: +nc4sym: default: 0 type: integer min: 0 @@ -759,7 +759,7 @@ numc4sym: For each, four segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c4sym_sta1_1: default: .nan type: integer @@ -880,7 +880,7 @@ c4sym_seg4_1: long: Segment ID of fourth C4 segment group: 'symmetry' explevel: expert -numc5sym: +nc5sym: default: 0 type: integer min: 0 @@ -891,7 +891,7 @@ numc5sym: For each, five segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c5sym_sta1_1: default: .nan type: integer @@ -1042,7 +1042,7 @@ c5sym_seg5_1: long: Segment ID of fifth C5 segment group: 'symmetry' explevel: expert -numc6sym: +nc6sym: default: 0 type: integer min: 0 @@ -1053,7 +1053,7 @@ numc6sym: For each, six segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c6sym_sta1_1: default: .nan type: integer @@ -1234,7 +1234,7 @@ c6sym_seg6_1: long: Segment ID of sixth C6 segment group: 'symmetry' explevel: expert -nums3sym: +ns3sym: default: 0 type: integer min: 0 @@ -1245,7 +1245,7 @@ nums3sym: For each, three segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden s3sym_sta1_1: default: .nan type: integer @@ -1356,7 +1356,7 @@ kncs: long: Force constant for non-crystallographic restraints group: 'symmetry' explevel: expert -numncs: +nncs: default: 0 type: integer min: 0 @@ -1366,7 +1366,7 @@ numncs: long: Number of non-crystallographic symmetry restraints. For each pairs of segments will have to be defined. Note that those must contain exactly the same residues/atom combinations. group: 'symmetry' - explevel: expert + explevel: hidden ncs_sta1_1: default: .nan type: integer diff --git a/src/haddock/modules/refinement/mdref/cns/mdref.cns b/src/haddock/modules/refinement/mdref/cns/mdref.cns index 0419ac3cf..f6e023011 100644 --- a/src/haddock/modules/refinement/mdref/cns/mdref.cns +++ b/src/haddock/modules/refinement/mdref/cns/mdref.cns @@ -72,12 +72,12 @@ evaluate ($data.numncs = $numncs) ! Symmetry restraints evaluate ($data.ksym = $ksym) evaluate ($Data.flags.sym = $sym_on) -evaluate ($data.numc2sym = $numc2sym) -evaluate ($data.numc3sym = $numc3sym) -evaluate ($data.nums3sym = $nums3sym) -evaluate ($data.numc4sym = $numc4sym) -evaluate ($data.numc5sym = $numc5sym) -evaluate ($data.numc6sym = $numc6sym) +evaluate ($data.nc2sym = $nc2sym) +evaluate ($data.nc3sym = $nc3sym) +evaluate ($data.ns3sym = $ns3sym) +evaluate ($data.nc4sym = $nc4sym) +evaluate ($data.nc5sym = $nc5sym) +evaluate ($data.nc6sym = $nc6sym) !Electrostatics: evaluate ($Data.flags.dihed =$dihedflag) diff --git a/src/haddock/modules/refinement/mdref/cns/symmultimer.cns b/src/haddock/modules/refinement/mdref/cns/symmultimer.cns index 662bfa630..4bc582995 100644 --- a/src/haddock/modules/refinement/mdref/cns/symmultimer.cns +++ b/src/haddock/modules/refinement/mdref/cns/symmultimer.cns @@ -18,7 +18,7 @@ if ($Data.flags.sym eq true) then ! Define C2 symmetry restraints for symmetrical multimers ! eval ($ncount = 0) - while ($ncount < $data.numc2sym) loop c2symloop + while ($ncount < $data.nc2sym) loop c2symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c2sym_sta1_$ncount) @@ -65,7 +65,7 @@ if ($Data.flags.sym eq true) then ! Define C3 symmetry restraints for symmetrical multimers ! eval ($ncount = 0) - while ($ncount < $data.numc3sym) loop c3symloop + while ($ncount < $data.nc3sym) loop c3symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c3sym_sta1_$ncount) @@ -142,7 +142,7 @@ if ($Data.flags.sym eq true) then ! Define S3 symmetry restraints for symmetrical multimers ! eval ($ncount = 0) - while ($ncount < $data.nums3sym) loop s3symloop + while ($ncount < $data.ns3sym) loop s3symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $s3sym_sta1_$ncount) @@ -218,7 +218,7 @@ if ($Data.flags.sym eq true) then ! eval ($istep = 10) eval ($ncount = 0) - while ($ncount < $data.numc4sym) loop c4symloop + while ($ncount < $data.nc4sym) loop c4symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c4sym_sta1_$ncount) @@ -369,7 +369,7 @@ if ($Data.flags.sym eq true) then ! eval ($istep = 4) eval ($ncount = 0) - while ($ncount < $data.numc5sym) loop c5symloop + while ($ncount < $data.nc5sym) loop c5symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c5sym_sta1_$ncount) @@ -482,7 +482,7 @@ if ($Data.flags.sym eq true) then ! eval ($istep = 10) eval ($ncount = 0) - while ($ncount < $data.numc6sym) loop c6symloop + while ($ncount < $data.nc6sym) loop c6symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c6sym_sta1_$ncount) diff --git a/src/haddock/modules/refinement/mdref/defaults.yaml b/src/haddock/modules/refinement/mdref/defaults.yaml index 18f27a8fb..2cb795de7 100644 --- a/src/haddock/modules/refinement/mdref/defaults.yaml +++ b/src/haddock/modules/refinement/mdref/defaults.yaml @@ -276,7 +276,7 @@ symtbl_fname: long: Filename of the custom symmetry restraints file. group: 'symmetry' explevel: expert -numc2sym: +nc2sym: default: 0 type: integer min: 0 @@ -287,7 +287,7 @@ numc2sym: For each, a pair of segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c2sym_sta1_1: default: .nan type: integer @@ -348,7 +348,7 @@ c2sym_seg2_1: long: Segment ID of second C2 segment group: 'symmetry' explevel: expert -numc3sym: +nc3sym: default: 0 type: integer min: 0 @@ -359,7 +359,7 @@ numc3sym: For each, three segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c3sym_sta1_1: default: .nan type: integer @@ -450,7 +450,7 @@ c3sym_seg3_1: long: Segment ID of third C3 segment group: 'symmetry' explevel: expert -numc4sym: +nc4sym: default: 0 type: integer min: 0 @@ -461,7 +461,7 @@ numc4sym: For each, four segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c4sym_sta1_1: default: .nan type: integer @@ -582,7 +582,7 @@ c4sym_seg4_1: long: Segment ID of fourth C4 segment group: 'symmetry' explevel: expert -numc5sym: +nc5sym: default: 0 type: integer min: 0 @@ -593,7 +593,7 @@ numc5sym: For each, five segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c5sym_sta1_1: default: .nan type: integer @@ -744,7 +744,7 @@ c5sym_seg5_1: long: Segment ID of fifth C5 segment group: 'symmetry' explevel: expert -numc6sym: +nc6sym: default: 0 type: integer min: 0 @@ -755,7 +755,7 @@ numc6sym: For each, six segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden c6sym_sta1_1: default: .nan type: integer @@ -936,7 +936,7 @@ c6sym_seg6_1: long: Segment ID of sixth C6 segment group: 'symmetry' explevel: expert -nums3sym: +ns3sym: default: 0 type: integer min: 0 @@ -947,7 +947,7 @@ nums3sym: For each, three segments on which the symmetry restraint is applied will have to be defined. Those must all have the same length. group: 'symmetry' - explevel: expert + explevel: hidden s3sym_sta1_1: default: .nan type: integer @@ -1058,7 +1058,7 @@ kncs: long: Force constant for non-crystallographic restraints group: 'symmetry' explevel: expert -numncs: +nncs: default: 0 type: integer min: 0 @@ -1068,7 +1068,7 @@ numncs: long: Number of non-crystallographic symmetry restraints. For each pairs of segments will have to be defined. Note that those must contain exactly the same residues/atom combinations. group: 'symmetry' - explevel: expert + explevel: hidden ncs_sta1_1: default: .nan type: integer diff --git a/src/haddock/modules/sampling/rigidbody/cns/check-homomers.cns b/src/haddock/modules/sampling/rigidbody/cns/check-homomers.cns index 00a359955..7faf7a4c9 100644 --- a/src/haddock/modules/sampling/rigidbody/cns/check-homomers.cns +++ b/src/haddock/modules/sampling/rigidbody/cns/check-homomers.cns @@ -22,32 +22,32 @@ while ($ncount < $data.ncomponents) loop checkmol end loop checkmol if ($data.flags.sym eq true) then - if ($data.numc2sym > 0) then + if ($data.nc2sym > 0) then if ($n_moving_mol = 2) then evaluate ($homosymmetry = true) end if end if - if ($data.numc2sym = 6) then + if ($data.nc2sym = 6) then if ($n_moving_mol = 4) then evaluate ($homosymmetry = true) end if end if - if ($data.numc3sym > 0) then + if ($data.nc3sym > 0) then if ($n_moving_mol = 3) then evaluate ($homosymmetry = true) end if end if - if ($data.numc4sym > 0) then + if ($data.nc4sym > 0) then if ($n_moving_mol = 4) then evaluate ($homosymmetry = true) end if end if - if ($data.numc5sym > 0) then + if ($data.nc5sym > 0) then if ($n_moving_mol = 5) then evaluate ($homosymmetry = true) end if end if - if ($data.numc6sym > 0) then + if ($data.nc6sym > 0) then if ($n_moving_mol = 6) then evaluate ($homosymmetry = true) end if diff --git a/src/haddock/modules/sampling/rigidbody/cns/randomairs.cns b/src/haddock/modules/sampling/rigidbody/cns/randomairs.cns index db61f2f26..ce39de650 100644 --- a/src/haddock/modules/sampling/rigidbody/cns/randomairs.cns +++ b/src/haddock/modules/sampling/rigidbody/cns/randomairs.cns @@ -102,9 +102,9 @@ while ($nchain1 < $data.ncomponents) loop nloop1 do (store5 = $nchain1) (segid $prot_segid_$nchain1) display RANDOM AIRS SAMPLED FROM ENTIRE SURFACE FOR MOLECULE $nchain1 else - evaluate ($numseg = abs($nrair_$nchain1)) + evaluate ($nseg = abs($nrair_$nchain1)) display RANDOM AIRS SAMPLED FROM SEGMENTS FOR MOLECULE $nchain1 - while ($fcounter < $numseg) loop Xflex + while ($fcounter < $nseg) loop Xflex evaluate($fcounter=$fcounter + 1) do (store5 = $nchain1) ( resid $rair_sta_$nchain1_$fcounter : $rair_end_$nchain1_$fcounter and segid $prot_segid_$nchain1) diff --git a/src/haddock/modules/sampling/rigidbody/cns/rigidbody.cns b/src/haddock/modules/sampling/rigidbody/cns/rigidbody.cns index 1da4ab5e9..a828552e2 100644 --- a/src/haddock/modules/sampling/rigidbody/cns/rigidbody.cns +++ b/src/haddock/modules/sampling/rigidbody/cns/rigidbody.cns @@ -58,26 +58,26 @@ evaluate ($toppar.par_nonbonded = "OPLSX") ! Symmetry restraints evaluate ($data.ksym = $ksym) evaluate ($Data.flags.sym = $sym_on) -evaluate ($data.numc2sym = $numc2sym) -evaluate ($data.numc3sym = $numc3sym) -evaluate ($data.nums3sym= $nums3sym) -evaluate ($data.numc4sym = $numc4sym) -evaluate ($data.numc5sym = $numc5sym) -evaluate ($data.numc6sym = $numc6sym) - -if ( $data.numc2sym eq 6) then +evaluate ($data.nc2sym = $nc2sym) +evaluate ($data.nc3sym = $nc3sym) +evaluate ($data.ns3sym= $ns3sym) +evaluate ($data.nc4sym = $nc4sym) +evaluate ($data.nc5sym = $nc5sym) +evaluate ($data.nc6sym = $nc6sym) + +if ( $data.nc2sym eq 6) then evaluate ($saprotocol.rotate180 = false) end if -if ( $data.numc3sym ne 0) then +if ( $data.nc3sym ne 0) then evaluate ($saprotocol.rotate180 = false) end if -if ( $data.numc4sym ne 0) then +if ( $data.nc4sym ne 0) then evaluate ($saprotocol.rotate180 = false) end if -if ( $data.numc5sym ne 0) then +if ( $data.nc5sym ne 0) then evaluate ($saprotocol.rotate180 = false) end if -if ( $data.numc6sym ne 0) then +if ( $data.nc6sym ne 0) then evaluate ($saprotocol.rotate180 = false) end if diff --git a/src/haddock/modules/sampling/rigidbody/cns/symmultimer.cns b/src/haddock/modules/sampling/rigidbody/cns/symmultimer.cns index 662bfa630..4bc582995 100644 --- a/src/haddock/modules/sampling/rigidbody/cns/symmultimer.cns +++ b/src/haddock/modules/sampling/rigidbody/cns/symmultimer.cns @@ -18,7 +18,7 @@ if ($Data.flags.sym eq true) then ! Define C2 symmetry restraints for symmetrical multimers ! eval ($ncount = 0) - while ($ncount < $data.numc2sym) loop c2symloop + while ($ncount < $data.nc2sym) loop c2symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c2sym_sta1_$ncount) @@ -65,7 +65,7 @@ if ($Data.flags.sym eq true) then ! Define C3 symmetry restraints for symmetrical multimers ! eval ($ncount = 0) - while ($ncount < $data.numc3sym) loop c3symloop + while ($ncount < $data.nc3sym) loop c3symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c3sym_sta1_$ncount) @@ -142,7 +142,7 @@ if ($Data.flags.sym eq true) then ! Define S3 symmetry restraints for symmetrical multimers ! eval ($ncount = 0) - while ($ncount < $data.nums3sym) loop s3symloop + while ($ncount < $data.ns3sym) loop s3symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $s3sym_sta1_$ncount) @@ -218,7 +218,7 @@ if ($Data.flags.sym eq true) then ! eval ($istep = 10) eval ($ncount = 0) - while ($ncount < $data.numc4sym) loop c4symloop + while ($ncount < $data.nc4sym) loop c4symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c4sym_sta1_$ncount) @@ -369,7 +369,7 @@ if ($Data.flags.sym eq true) then ! eval ($istep = 4) eval ($ncount = 0) - while ($ncount < $data.numc5sym) loop c5symloop + while ($ncount < $data.nc5sym) loop c5symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c5sym_sta1_$ncount) @@ -482,7 +482,7 @@ if ($Data.flags.sym eq true) then ! eval ($istep = 10) eval ($ncount = 0) - while ($ncount < $data.numc6sym) loop c6symloop + while ($ncount < $data.nc6sym) loop c6symloop eval ($ncount = $ncount + 1) evaluate ($i1start = $c6sym_sta1_$ncount) diff --git a/src/haddock/modules/sampling/rigidbody/defaults.yaml b/src/haddock/modules/sampling/rigidbody/defaults.yaml index ff3a35dd9..c909b6b41 100644 --- a/src/haddock/modules/sampling/rigidbody/defaults.yaml +++ b/src/haddock/modules/sampling/rigidbody/defaults.yaml @@ -956,7 +956,7 @@ symtbl_fname: long: Filename of the custom symmetry restraints file. group: 'symmetry' explevel: expert -numc2sym: +nc2sym: default: 0 type: integer min: 0 @@ -1028,7 +1028,7 @@ c2sym_seg2_1: long: Segment ID of second C2 segment group: 'symmetry' explevel: expert -numc3sym: +nc3sym: default: 0 type: integer min: 0 @@ -1130,7 +1130,7 @@ c3sym_seg3_1: long: Segment ID of third C3 segment group: 'symmetry' explevel: expert -numc4sym: +nc4sym: default: 0 type: integer min: 0 @@ -1262,7 +1262,7 @@ c4sym_seg4_1: long: Segment ID of fourth C4 segment group: 'symmetry' explevel: expert -numc5sym: +nc5sym: default: 0 type: integer min: 0 @@ -1424,7 +1424,7 @@ c5sym_seg5_1: long: Segment ID of fifth C5 segment group: 'symmetry' explevel: expert -numc6sym: +nc6sym: default: 0 type: integer min: 0 @@ -1616,7 +1616,7 @@ c6sym_seg6_1: long: Segment ID of sixth C6 segment group: 'symmetry' explevel: expert -nums3sym: +ns3sym: default: 0 type: integer min: 0 @@ -1738,7 +1738,7 @@ kncs: long: Force constant for non-crystallographic restraints group: 'symmetry' explevel: expert -numncs: +nncs: default: 0 type: integer min: 0 @@ -1748,7 +1748,7 @@ numncs: long: Number of non-crystallographic symmetry restraints. For each pairs of segments will have to be defined. Note that those must contain exactly the same residues/atom combinations. group: 'symmetry' - explevel: expert + explevel: hidden ncs_sta1_1: default: .nan type: integer From a0d7403184fb317c362bff5ced9750a03fb5892e Mon Sep 17 00:00:00 2001 From: VGPReys Date: Tue, 17 Sep 2024 11:14:33 +0200 Subject: [PATCH 30/62] update numncs in cns files --- src/haddock/modules/refinement/emref/cns/emref.cns | 2 +- src/haddock/modules/refinement/flexref/cns/flexref.cns | 2 +- src/haddock/modules/refinement/mdref/cns/mdref.cns | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/haddock/modules/refinement/emref/cns/emref.cns b/src/haddock/modules/refinement/emref/cns/emref.cns index 76006e293..a048bc1d1 100644 --- a/src/haddock/modules/refinement/emref/cns/emref.cns +++ b/src/haddock/modules/refinement/emref/cns/emref.cns @@ -59,7 +59,7 @@ evaluate ($data.kcont=$kcont) ! NCS restraints evaluate ($data.kncs = $kncs) evaluate ($Data.flags.ncs = $ncs_on) -evaluate ($data.numncs = $numncs) +evaluate ($data.nncs = $nncs) ! Symmetry restraints evaluate ($data.ksym = $ksym) diff --git a/src/haddock/modules/refinement/flexref/cns/flexref.cns b/src/haddock/modules/refinement/flexref/cns/flexref.cns index 58680ecb6..4ca0f0c3a 100644 --- a/src/haddock/modules/refinement/flexref/cns/flexref.cns +++ b/src/haddock/modules/refinement/flexref/cns/flexref.cns @@ -118,7 +118,7 @@ end if ! NCS restraints evaluate ($data.kncs = $kncs) evaluate ($Data.flags.ncs = $ncs_on) -evaluate ($data.numncs = $numncs) +evaluate ($data.nncs = $nncs) ! Symmetry restraints evaluate ($data.ksym = $ksym) diff --git a/src/haddock/modules/refinement/mdref/cns/mdref.cns b/src/haddock/modules/refinement/mdref/cns/mdref.cns index f6e023011..4c206f484 100644 --- a/src/haddock/modules/refinement/mdref/cns/mdref.cns +++ b/src/haddock/modules/refinement/mdref/cns/mdref.cns @@ -67,7 +67,7 @@ end if ! NCS restraints evaluate ($data.kncs = $kncs) evaluate ($Data.flags.ncs = $ncs_on) -evaluate ($data.numncs = $numncs) +evaluate ($data.nncs = $nncs) ! Symmetry restraints evaluate ($data.ksym = $ksym) From 06a0801adc62988a08c07e80e5d776ce4c4ce8d8 Mon Sep 17 00:00:00 2001 From: VGPReys Date: Tue, 17 Sep 2024 11:30:26 +0200 Subject: [PATCH 31/62] modify parameter name in ncs checks --- src/haddock/gear/prepare_run.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/haddock/gear/prepare_run.py b/src/haddock/gear/prepare_run.py index 1df4e65b2..7c4e6a745 100644 --- a/src/haddock/gear/prepare_run.py +++ b/src/haddock/gear/prepare_run.py @@ -799,18 +799,18 @@ def validate_ncs_params(params: dict) -> None: # Validate value of `numncs` ncs_suffixes = list(groupped_ncs.keys()) # Case when number of definition do not match - if params["numncs"] != len(ncs_suffixes): + if params["nncs"] != len(ncs_suffixes): msg = ( - f'Number of NCS restraints (`numncs = {params["numncs"]}`) ' + f'Number of NCS restraints (`nncs = {params["nncs"]}`) ' " do not match with the number of defined NCS restraints " f"({len(ncs_suffixes)})" ) error_list.append(msg) else: # Case when numbers do not match - if max(ncs_suffixes) != params["numncs"]: + if max(ncs_suffixes) != params["nncs"]: msg = ( - f'Number of NCS restraints (`numncs = {params["numncs"]}`) ' + f'Number of NCS restraints (`nncs = {params["nncs"]}`) ' " do not match with the number of defined NCS restraints " f"({', '.join([str(s) for s in ncs_suffixes])})" ) From 4258b99a0a458402b216880b559c12dc7713205f Mon Sep 17 00:00:00 2001 From: VGPReys Date: Tue, 17 Sep 2024 11:33:31 +0200 Subject: [PATCH 32/62] modify numncs parameter in tests --- tests/test_gear_prepare_run.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_gear_prepare_run.py b/tests/test_gear_prepare_run.py index 3e0a74b6f..92bf59f57 100644 --- a/tests/test_gear_prepare_run.py +++ b/tests/test_gear_prepare_run.py @@ -38,7 +38,7 @@ def fixture_proper_ncs_params() -> dict[str, Union[str, int]]: return { "ncs_on": True, - "numncs": 2, + "nncs": 2, "ncs_sta1_1": 1, "ncs_sta2_1": 1, "ncs_end1_1": 10, @@ -469,7 +469,7 @@ def test_validate_ncs_params_different_end(proper_ncs_params): def test_validate_ncs_params_wrong_count(proper_ncs_params): """Test NCS param error when missmatch in number of ncs defined.""" # Set wrong number of ncs definition - proper_ncs_params["numncs"] = 1 + proper_ncs_params["nncs"] = 1 with pytest.raises(ConfigurationError): assert validate_ncs_params(proper_ncs_params) is None @@ -486,6 +486,6 @@ def test_validate_ncs_params_wrong_suffix_value(proper_ncs_params): proper_ncs_params[k3] = proper_ncs_params[k2] # Delete key2 del proper_ncs_params[k2] - # Except error as _3 != (numncs = 2) + # Except error as _3 != (nncs = 2) with pytest.raises(ConfigurationError): assert validate_ncs_params(proper_ncs_params) is None From 9e1d9e367c3ef17efe2e1899a03a2b24d9809b11 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 17 Sep 2024 11:56:25 +0200 Subject: [PATCH 33/62] check arch --- .github/workflows/tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 456c1c160..2f3711e30 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,6 +33,9 @@ jobs: python -m pip install pip==23.1.2 setuptools==67.7.2 wheel==0.40.0 pip install virtualenv==20.23.0 tox==4.5.1.1 + - name: Print architecture + run: echo "Current architecture is ${{ runner.arch }}" + - name: install HADDOCK run: python setup.py develop From 3c1cacdc1112ef2cc77bd1bd784a6c4cf217afb5 Mon Sep 17 00:00:00 2001 From: VGPReys Date: Tue, 17 Sep 2024 12:09:49 +0200 Subject: [PATCH 34/62] fix testing changes... --- .../docking-protein-homotrimer-test.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/docking-protein-homotrimer/docking-protein-homotrimer-test.cfg b/examples/docking-protein-homotrimer/docking-protein-homotrimer-test.cfg index 33980d886..c8dca6e3a 100644 --- a/examples/docking-protein-homotrimer/docking-protein-homotrimer-test.cfg +++ b/examples/docking-protein-homotrimer/docking-protein-homotrimer-test.cfg @@ -36,7 +36,7 @@ ambig_fname = "data/1qu9_whiscy_air.tbl" sampling = 20 # Define NCS restraints between molecules ncs_on = true -ncs_sta1_1=1 +ncs_sta1_1=2 ncs_end1_1=128 ncs_seg1_1="A" ncs_sta2_1=2 From 7f98b9d9d352e710d5fd7f40305221d8351a23d9 Mon Sep 17 00:00:00 2001 From: VGPReys Date: Tue, 17 Sep 2024 12:10:47 +0200 Subject: [PATCH 35/62] upgrade test_examples_general.py with handling functions --- tests/test_examples_general.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/test_examples_general.py b/tests/test_examples_general.py index 1a4fd8fdc..cf15af7fe 100644 --- a/tests/test_examples_general.py +++ b/tests/test_examples_general.py @@ -73,9 +73,26 @@ def test_integration_examples(): def test_validate_cfg_files(): """Test all the examples configuration files are valid.""" for cfg_file in examples_cfg_files: + assert validate_cfg_file(cfg_file), f"Error detected in {cfg_file}!" + + +def validate_cfg_file(cfg_file: Path) -> bool: + """Handeler to validate a configuration file. + + Parameters + ---------- + cfg_file : Path + Path to the configuration file to check. + + Returns + ------- + bool + True if config file is OK else False + """ + try: if cfg_file.name == "params.cfg": # skip the params.cfg files as they might be part of old runs - continue + return True config_files = read_config(cfg_file) # update default non-mandatory parameters with user params params = recursive_dict_update( @@ -99,4 +116,9 @@ def test_validate_cfg_files(): reference_parameters=ALL_POSSIBLE_GENERAL_PARAMETERS, ) - validate_modules_params(modules_params, 20) \ No newline at end of file + validate_modules_params(modules_params, 20) + except Exception as e: + print(e) + return False + else: + return True From fa0cf07bd9827298110394ffa199259f9bb9bfc1 Mon Sep 17 00:00:00 2001 From: VGPReys Date: Tue, 17 Sep 2024 12:39:21 +0200 Subject: [PATCH 36/62] reverse changes on randomairs.cns --- src/haddock/modules/sampling/rigidbody/cns/randomairs.cns | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/haddock/modules/sampling/rigidbody/cns/randomairs.cns b/src/haddock/modules/sampling/rigidbody/cns/randomairs.cns index ce39de650..db61f2f26 100644 --- a/src/haddock/modules/sampling/rigidbody/cns/randomairs.cns +++ b/src/haddock/modules/sampling/rigidbody/cns/randomairs.cns @@ -102,9 +102,9 @@ while ($nchain1 < $data.ncomponents) loop nloop1 do (store5 = $nchain1) (segid $prot_segid_$nchain1) display RANDOM AIRS SAMPLED FROM ENTIRE SURFACE FOR MOLECULE $nchain1 else - evaluate ($nseg = abs($nrair_$nchain1)) + evaluate ($numseg = abs($nrair_$nchain1)) display RANDOM AIRS SAMPLED FROM SEGMENTS FOR MOLECULE $nchain1 - while ($fcounter < $nseg) loop Xflex + while ($fcounter < $numseg) loop Xflex evaluate($fcounter=$fcounter + 1) do (store5 = $nchain1) ( resid $rair_sta_$nchain1_$fcounter : $rair_end_$nchain1_$fcounter and segid $prot_segid_$nchain1) From 128d68d91ca79bb9e9effa12c42aacfab071c6e3 Mon Sep 17 00:00:00 2001 From: Victor Reys <132575181+VGPReys@users.noreply.github.com> Date: Tue, 17 Sep 2024 13:55:02 +0200 Subject: [PATCH 37/62] Apply typo suggestions from code review Co-authored-by: Anna Engel <113177776+AljaLEngel@users.noreply.github.com> --- .../docking-flexref-protein-glycan-full.cfg | 2 +- .../docking-flexref-protein-glycan-test.cfg | 2 +- src/haddock/gear/expandable_parameters.py | 2 +- tests/test_examples_general.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/docking-protein-glycan/docking-flexref-protein-glycan-full.cfg b/examples/docking-protein-glycan/docking-flexref-protein-glycan-full.cfg index e2db10af4..5787521ad 100644 --- a/examples/docking-protein-glycan/docking-flexref-protein-glycan-full.cfg +++ b/examples/docking-protein-glycan/docking-flexref-protein-glycan-full.cfg @@ -42,7 +42,7 @@ mdsteps_cool1 = 5000 mdsteps_cool2 = 10000 mdsteps_cool3 = 10000 ambig_fname = "data/ambig.tbl" -# give full flexibilit to the glycan +# give full flexibility to the glycan fle_sta_1 = 1 fle_end_1 = 4 fle_seg_1 = "B" diff --git a/examples/docking-protein-glycan/docking-flexref-protein-glycan-test.cfg b/examples/docking-protein-glycan/docking-flexref-protein-glycan-test.cfg index 9900453b2..86d062148 100644 --- a/examples/docking-protein-glycan/docking-flexref-protein-glycan-test.cfg +++ b/examples/docking-protein-glycan/docking-flexref-protein-glycan-test.cfg @@ -38,7 +38,7 @@ mdsteps_cool1 = 5000 mdsteps_cool2 = 10000 mdsteps_cool3 = 10000 ambig_fname = "data/ambig.tbl" -# give full flexibilit to the glycan +# give full flexibility to the glycan fle_sta_1 = 1 fle_end_1 = 4 fle_seg_1 = "B" diff --git a/src/haddock/gear/expandable_parameters.py b/src/haddock/gear/expandable_parameters.py index db41380ad..1676843b1 100644 --- a/src/haddock/gear/expandable_parameters.py +++ b/src/haddock/gear/expandable_parameters.py @@ -284,7 +284,7 @@ def _read_groups_in_user_config( A set with the new parameters in the user configuration file that are acceptable according to the expandable rules. param_name_counts : dict[str, int] - Count of expendable parameter parameter name + Count of expendable parameter parameter names """ # minimum=1 is used to capture groups with missing parameters user_groups = get_user_groups(user_config, minimum=1, reference=False) diff --git a/tests/test_examples_general.py b/tests/test_examples_general.py index cf15af7fe..975fc0cb3 100644 --- a/tests/test_examples_general.py +++ b/tests/test_examples_general.py @@ -73,7 +73,7 @@ def test_integration_examples(): def test_validate_cfg_files(): """Test all the examples configuration files are valid.""" for cfg_file in examples_cfg_files: - assert validate_cfg_file(cfg_file), f"Error detected in {cfg_file}!" + assert validate_cfg_file(cfg_file), f"Error detected in {cfg_file}!" def validate_cfg_file(cfg_file: Path) -> bool: From 4d971fd32582fbe3382087263b2998d04dd343b4 Mon Sep 17 00:00:00 2001 From: Alexandre Bonvin Date: Tue, 17 Sep 2024 14:09:24 +0200 Subject: [PATCH 38/62] Update src/haddock/modules/sampling/rigidbody/cns/rigidbody.cns Co-authored-by: Anna Engel <113177776+AljaLEngel@users.noreply.github.com> --- src/haddock/modules/sampling/rigidbody/cns/rigidbody.cns | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/haddock/modules/sampling/rigidbody/cns/rigidbody.cns b/src/haddock/modules/sampling/rigidbody/cns/rigidbody.cns index a828552e2..c3ffc1ed3 100644 --- a/src/haddock/modules/sampling/rigidbody/cns/rigidbody.cns +++ b/src/haddock/modules/sampling/rigidbody/cns/rigidbody.cns @@ -60,7 +60,7 @@ evaluate ($data.ksym = $ksym) evaluate ($Data.flags.sym = $sym_on) evaluate ($data.nc2sym = $nc2sym) evaluate ($data.nc3sym = $nc3sym) -evaluate ($data.ns3sym= $ns3sym) +evaluate ($data.ns3sym = $ns3sym) evaluate ($data.nc4sym = $nc4sym) evaluate ($data.nc5sym = $nc5sym) evaluate ($data.nc6sym = $nc6sym) From 362464f01cbe11894467161d822dca62774d748e Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 17 Sep 2024 15:12:35 +0200 Subject: [PATCH 39/62] refactor setup.py --- setup.py | 84 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/setup.py b/setup.py index 305ea57fd..d28103dfa 100644 --- a/setup.py +++ b/setup.py @@ -3,9 +3,9 @@ """Setup dot py.""" import os import platform -import shutil import subprocess import sys +import tempfile import urllib.request from os.path import dirname, join from pathlib import Path @@ -15,6 +15,13 @@ from setuptools.command.install import install +CNS_BINARIES = { + "x86_64-linux": "https://surfdrive.surf.nl/files/index.php/s/BWa5OimzbNliTi6/download", + "x86_64-darwin": "https://surfdrive.surf.nl/files/index.php/s/3Fzzte0Zx0L8GTY/download", + "arm64-darwin": "https://surfdrive.surf.nl/files/index.php/s/bYB3xPWf7iwo07X/download", + "aarch64-linux": "https://surfdrive.surf.nl/files/index.php/s/3rHpxcufHGrntHn/download", +} + cpp_extensions = [ Extension( "haddock.bin.contact_fcc", @@ -34,12 +41,12 @@ class CustomBuild(build_ext): def run(self): print("Building HADDOCK3 C/C++ binary dependencies...") self.build_executable( - "contact_fcc", - ["g++", "-O2", "-o", "contact_fcc", "src/haddock/deps/contact_fcc.cpp"], + name="contact_fcc", + cmd=["g++", "-O2", "-o", "contact_fcc", "src/haddock/deps/contact_fcc.cpp"], ) self.build_executable( - "fast-rmsdmatrix", - [ + name="fast-rmsdmatrix", + cmd=[ "gcc", "-Wall", "-O3", @@ -58,70 +65,65 @@ def run(self): def build_executable(self, name, cmd): try: subprocess.check_call(cmd) - bin_dir = os.path.join("src", "haddock", "bin") - os.makedirs(bin_dir, exist_ok=True) - shutil.move(name, os.path.join(bin_dir, name)) + # Ensure the source bin directory exists + src_bin_dir = Path("src", "haddock", "bin") + src_bin_dir.mkdir(exist_ok=True, parents=True) + + # Move the built executable to the source bin directory + src_bin_exec = Path(src_bin_dir, name) + if src_bin_exec.exists(): + src_bin_exec.unlink() + + self.move_file(name, src_bin_exec) + + # If build_lib exists, also copy to there + if hasattr(self, "build_lib"): + build_bin_dir = Path(self.build_lib, "haddock", "bin") + build_bin_dir.mkdir(exist_ok=True, parents=True) + self.copy_file(Path(src_bin_dir, name), Path(build_bin_dir, name)) + print(f"Successfully built and moved {name}") except subprocess.CalledProcessError as e: print(f"Error building {name}: {e}") raise -CNS_BINARIES = { - "x86_64-linux": "https://surfdrive.surf.nl/files/index.php/s/BWa5OimzbNliTi6/download", - "x86_64-darwin": "https://surfdrive.surf.nl/files/index.php/s/3Fzzte0Zx0L8GTY/download", - "arm64-darwin": "https://surfdrive.surf.nl/files/index.php/s/bYB3xPWf7iwo07X/download", - "aarch64-linux": "https://surfdrive.surf.nl/files/index.php/s/3rHpxcufHGrntHn/download", -} - - class CustomInstall(install): """Custom class to handle the download of the CNS binary""" def run(self): - """Run the installation""" - - # Run the standard installation install.run(self) - # Get the installation directory if self.install_lib is None: - print("Something went wrong during installation.") + print("Something went wrong during installation") sys.exit(1) - # Set where the cns binary needs to be - bin_dir = Path(self.install_lib, "haddock", "bin") - bin_dir = Path("src", "haddock", "bin") - - # Create the `bin/` directory - bin_dir.mkdir(exist_ok=True) - - # Download the binary - cns_exec = Path(bin_dir, "cns") - if cns_exec.exists(): - cns_exec.unlink() - arch = self.get_arch() if arch not in CNS_BINARIES: print(f"Unknown architecture: {arch}") - print( - "Please set the CNS binary manually inside your configuration file as `cns_exec`", - ) - return + sys.exit(1) cns_binary_url = CNS_BINARIES[arch] + src_bin_dir = Path("src", "haddock", "bin") + src_bin_dir.mkdir(exist_ok=True, parents=True) + + cns_exec = Path(src_bin_dir, "cns") status, msg = self.download_file(cns_binary_url, cns_exec) if not status: print(msg) - print( - "Please set the CNS binary manually inside your configuration file as `cns_exec`" - ) - return + sys.exit(1) + # Make it executable os.chmod(cns_exec, 0o755) + # check if this is being done via `pip install .` + if hasattr(self, "install_lib"): + install_bin_dir = Path(self.install_lib, "haddock", "bin") + install_bin_dir.mkdir(exist_ok=True, parents=True) + self.copy_file(cns_exec, Path(install_bin_dir, "cns")) + @staticmethod def download_file(url, dest) -> tuple[bool, str]: """Download a file from a URL""" From 0cde66ead79948bf95582b6aafa92fce3dc5ceb0 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 17 Sep 2024 15:12:45 +0200 Subject: [PATCH 40/62] update `tests.yml` --- .github/workflows/tests.yml | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2f3711e30..960a02282 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,31 +4,30 @@ on: push: branches: [main] pull_request: - branches: [main] jobs: unit: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.9] + # python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - name: Install system dependencies + - name: install system dependencies run: | sudo apt-get update - sudo apt-get install openmpi-bin libopenmpi3 libopenmpi-dev + sudo apt-get install -y openmpi-bin libopenmpi3 libopenmpi-dev - - name: install dependencies + - name: install test dependencies run: | python -m pip install pip==23.1.2 setuptools==67.7.2 wheel==0.40.0 pip install virtualenv==20.23.0 tox==4.5.1.1 @@ -36,19 +35,15 @@ jobs: - name: Print architecture run: echo "Current architecture is ${{ runner.arch }}" - - name: install HADDOCK - run: python setup.py develop - - # - name: types - # run: tox -e types + - name: install haddock3 (development mode) + run: | + python setup.py develop - - name: unit tests - id: unit - run: tox -e test + # - name: check types + # run: mypy src/ - - name: integration - if: steps.unit.outcome == 'success' - run: tox -e integration + - name: run tests + run: pytest . --cov --cov-report=term-missing --cov-append --cov-config=.coveragerc --hypothesis-show-statistics - uses: codacy/codacy-coverage-reporter-action@v1 if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} From 0392d334ce8fd8133bf1fcd082a6721e6dd891e6 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 17 Sep 2024 15:23:08 +0200 Subject: [PATCH 41/62] update tests.yml --- .github/workflows/tests.yml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 960a02282..2e916246d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,3 +1,4 @@ +--- name: tests on: @@ -28,25 +29,26 @@ jobs: sudo apt-get install -y openmpi-bin libopenmpi3 libopenmpi-dev - name: install test dependencies - run: | - python -m pip install pip==23.1.2 setuptools==67.7.2 wheel==0.40.0 - pip install virtualenv==20.23.0 tox==4.5.1.1 - - - name: Print architecture - run: echo "Current architecture is ${{ runner.arch }}" + run: >- + pip install coverage pytest pytest-cov hypothesis pytest-mock + fastapi httpx mpi4py - name: install haddock3 (development mode) - run: | - python setup.py develop + run: python setup.py develop # - name: check types # run: mypy src/ - name: run tests - run: pytest . --cov --cov-report=term-missing --cov-append --cov-config=.coveragerc --hypothesis-show-statistics + run: >- + pytest . + --cov --cov-report=term-missing --cov-append + --cov-config=.coveragerc --hypothesis-show-statistics - uses: codacy/codacy-coverage-reporter-action@v1 - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + if: >- + ${{ github.event_name != 'pull_request' || + github.event.pull_request.head.repo.full_name == github.repository }} with: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} coverage-reports: ./coverage.xml From 40d15aad3f195e15e17a66d5853309114946a8c2 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 17 Sep 2024 15:27:24 +0200 Subject: [PATCH 42/62] tweak tests.yml --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2e916246d..7ceb0adec 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,15 +33,15 @@ jobs: pip install coverage pytest pytest-cov hypothesis pytest-mock fastapi httpx mpi4py - - name: install haddock3 (development mode) - run: python setup.py develop + - name: install haddock3 + run: pip install -v . # - name: check types # run: mypy src/ - name: run tests run: >- - pytest . + pytest . -x --cov --cov-report=term-missing --cov-append --cov-config=.coveragerc --hypothesis-show-statistics From a7e4d87b96d5742269d794d128d054a672293ee5 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 17 Sep 2024 15:46:10 +0200 Subject: [PATCH 43/62] update ci --- .github/workflows/{tests.yml => ci.yml} | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) rename .github/workflows/{tests.yml => ci.yml} (76%) diff --git a/.github/workflows/tests.yml b/.github/workflows/ci.yml similarity index 76% rename from .github/workflows/tests.yml rename to .github/workflows/ci.yml index 7ceb0adec..da7c14060 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,5 @@ --- -name: tests +name: ci on: push: @@ -7,7 +7,7 @@ on: pull_request: jobs: - unit: + ci: runs-on: ubuntu-latest strategy: matrix: @@ -16,8 +16,6 @@ jobs: steps: - uses: actions/checkout@v4 - with: - submodules: recursive - uses: actions/setup-python@v5 with: @@ -30,20 +28,27 @@ jobs: - name: install test dependencies run: >- - pip install coverage pytest pytest-cov hypothesis pytest-mock - fastapi httpx mpi4py + pip install coverage pytest pytest-cov hypothesis pytest-mock mypy + fastapi==0.110.1 httpx==0.27.0 mpi4py==3.1.6 - name: install haddock3 run: pip install -v . + ## Disabled for now until we figure out a good configuration ## # - name: check types # run: mypy src/ + ############################################################### - name: run tests run: >- pytest . -x --cov --cov-report=term-missing --cov-append - --cov-config=.coveragerc --hypothesis-show-statistics + --hypothesis-show-statistics + + - name: generate coverage report + run: | + coverage report + coverage xml - uses: codacy/codacy-coverage-reporter-action@v1 if: >- From 13e3bab0bb1eecc1bc14ee23f389530dacb9e56c Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Tue, 17 Sep 2024 16:03:14 +0200 Subject: [PATCH 44/62] update docs.yml --- .github/workflows/docs.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e77c25e1e..e752afc06 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,23 +4,17 @@ on: push: branches: [main] pull_request: - branches: [main] jobs: build: - runs-on: ${{ matrix.platform }} - strategy: - matrix: - platform: [ubuntu-latest, macos-latest] - python-version: [3.9] + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + - uses: actions/setup-python@v2 with: - python-version: ${{ matrix.python-version }} + python-version: 3.9 - name: Install dependencies run: | From 257c8138464b00bdc3c7983f678caf7252f077a1 Mon Sep 17 00:00:00 2001 From: VGPReys Date: Thu, 19 Sep 2024 09:47:45 +0200 Subject: [PATCH 45/62] less_io to debug --- .../peptide-cyclisation/cyclise-peptide-full.cfg | 2 +- .../peptide-cyclisation/cyclise-peptide-test.cfg | 2 +- integration_tests/test_caprieval.py | 4 ++-- integration_tests/test_rigidbody.py | 4 ++-- src/haddock/libs/libcns.py | 4 ++-- .../modules/analysis/caprieval/__init__.py | 9 ++++++--- src/haddock/modules/analysis/caprieval/capri.py | 6 +++--- src/haddock/modules/defaults.yaml | 15 ++++++++------- src/haddock/modules/refinement/emref/__init__.py | 2 +- .../modules/refinement/flexref/__init__.py | 2 +- src/haddock/modules/refinement/mdref/__init__.py | 2 +- .../modules/sampling/rigidbody/__init__.py | 4 ++-- src/haddock/modules/scoring/emscoring/__init__.py | 2 +- src/haddock/modules/scoring/mdscoring/__init__.py | 2 +- src/haddock/modules/topology/topoaa/__init__.py | 2 +- tests/test_libcns.py | 2 +- 16 files changed, 34 insertions(+), 30 deletions(-) diff --git a/examples/peptide-cyclisation/cyclise-peptide-full.cfg b/examples/peptide-cyclisation/cyclise-peptide-full.cfg index 197443eda..13d4bd240 100644 --- a/examples/peptide-cyclisation/cyclise-peptide-full.cfg +++ b/examples/peptide-cyclisation/cyclise-peptide-full.cfg @@ -19,7 +19,7 @@ run_dir = "run1-cyclise-full" # and comment the lines under the HPC execution mode = "local" ncores = 10 -less_io = true +debug = false concat = 1 # molecules to be docked diff --git a/examples/peptide-cyclisation/cyclise-peptide-test.cfg b/examples/peptide-cyclisation/cyclise-peptide-test.cfg index ff659b78d..b03976762 100644 --- a/examples/peptide-cyclisation/cyclise-peptide-test.cfg +++ b/examples/peptide-cyclisation/cyclise-peptide-test.cfg @@ -19,7 +19,7 @@ run_dir = "run1-cyclise-test" # and comment the lines under the HPC execution mode = "local" ncores = 10 -less_io = true +debug = false concat = 1 # molecules to be docked diff --git a/integration_tests/test_caprieval.py b/integration_tests/test_caprieval.py index ab57cec60..48f87c56d 100644 --- a/integration_tests/test_caprieval.py +++ b/integration_tests/test_caprieval.py @@ -330,11 +330,11 @@ def test_caprieval_default( ) -def test_caprieval_less_io( +def test_caprieval_debug( caprieval_module, model_list, expected_ss_data, expected_clt_data ): caprieval_module.previous_io = MockPreviousIO(path=caprieval_module.path) - caprieval_module.params["less_io"] = True + caprieval_module.params["debug"] = False caprieval_module.run() diff --git a/integration_tests/test_rigidbody.py b/integration_tests/test_rigidbody.py index aef37fe10..34f9da2a1 100644 --- a/integration_tests/test_rigidbody.py +++ b/integration_tests/test_rigidbody.py @@ -122,7 +122,7 @@ def test_rigidbody_mpi(rigidbody_module): assert Path(rigidbody_module.path, f"rigidbody_{i}.inp").stat().st_size > 0 -def test_rigidbody_less_io(rigidbody_module): +def test_rigidbody_debug(rigidbody_module): sampling = 2 rigidbody_module.previous_io = MockPreviousIO(path=rigidbody_module.path) @@ -130,7 +130,7 @@ def test_rigidbody_less_io(rigidbody_module): rigidbody_module.params["cmrest"] = True rigidbody_module.params["mol_fix_origin_1"] = True rigidbody_module.params["mol_fix_origin_2"] = False - rigidbody_module.params["less_io"] = True + rigidbody_module.params["debug"] = False rigidbody_module.run() diff --git a/src/haddock/libs/libcns.py b/src/haddock/libs/libcns.py index fae999f10..24d4b13f7 100644 --- a/src/haddock/libs/libcns.py +++ b/src/haddock/libs/libcns.py @@ -273,7 +273,7 @@ def prepare_cns_input( ambig_fname: FilePath = "", native_segid: bool = False, default_params_path: Optional[Path] = None, - less_io: Optional[bool] = True, + debug: Optional[bool] = False, seed: Optional[int] = None, ) -> Union[Path, str]: """ @@ -372,7 +372,7 @@ def prepare_cns_input( inp = default_params + input_str + seed_str + output + segid_str + recipe_str - if less_io: + if not debug: return inp else: inp_file = Path(f"{identifier}_{model_number}.inp") diff --git a/src/haddock/modules/analysis/caprieval/__init__.py b/src/haddock/modules/analysis/caprieval/__init__.py index 4c38e5aea..4873a86af 100644 --- a/src/haddock/modules/analysis/caprieval/__init__.py +++ b/src/haddock/modules/analysis/caprieval/__init__.py @@ -112,7 +112,7 @@ def _run(self) -> None: exec_mode = get_analysis_exec_mode(self.params["mode"]) Engine = get_engine(exec_mode, self.params) - less_io = self.params["less_io"] and self.params["mode"] == "local" + less_io = not self.params["debug"] and self.params["mode"] == "local" # Each model is a job; this is not the most efficient way # but by assigning each model to an individual job @@ -133,7 +133,7 @@ def _run(self) -> None: path=Path("."), reference=reference, params=self.params, - less_io=less_io, + debug=less_io, ) ) @@ -151,7 +151,10 @@ def _run(self) -> None: else: self.log( - msg="DEPRECATION NOTICE: This execution mode (less_io=False) will no longer be supported in the next version.", + msg=( + "DEPRECATION NOTICE: This execution mode (debug=True) " + "will no longer be supported in the next version." + ), level="warning", ) jobs = merge_data(jobs) diff --git a/src/haddock/modules/analysis/caprieval/capri.py b/src/haddock/modules/analysis/caprieval/capri.py index 90cc4c558..a7eab5fe5 100644 --- a/src/haddock/modules/analysis/caprieval/capri.py +++ b/src/haddock/modules/analysis/caprieval/capri.py @@ -179,7 +179,7 @@ def __init__( path: Path, reference: PDBPath, params: ParamMap, - less_io: Optional[bool] = False, + debug: Optional[bool] = True, ) -> None: """ Initialize the class. @@ -225,7 +225,7 @@ def __init__( self.output = self.output_ss_fname self.identificator = identificator self.core_model_idx = identificator - self.less_io = less_io + self.debug = debug def calc_irmsd(self, cutoff: float = 5.0) -> None: """Calculate the I-RMSD. @@ -654,7 +654,7 @@ def run(self) -> Union[None, "CAPRI"]: log.debug(f"id {self.identificator}, calculating global RMSD") self.calc_global_rmsd() - if not self.less_io: + if self.debug: self.make_output() else: # The scheduler will use the return of the `run` method as the output of the tasks diff --git a/src/haddock/modules/defaults.yaml b/src/haddock/modules/defaults.yaml index 38f0ba99e..972a19b66 100644 --- a/src/haddock/modules/defaults.yaml +++ b/src/haddock/modules/defaults.yaml @@ -138,14 +138,15 @@ offline: generated files, therefore completely isolating haddock3 from any web call. group: "execution" explevel: easy -less_io: - default: false +debug: + default: true type: boolean - title: Reduce the amount of I/O operations. - short: Reduce the amount of I/O operations. - long: This option will reduce the amount of I/O operations by writing - less files to disk. This can be useful for example when running on - a network file system where I/O operations are slow. + title: Write intermediate and stdout files. + short: Write intermediate and stdout files. + long: When set to true, this option will increase the amount of I/O + operations by writing intermediate input, output and error files on disk. + Turning it to false can be useful for example when running on a network + file system where I/O operations are slow. group: "execution" explevel: easy incompatible: diff --git a/src/haddock/modules/refinement/emref/__init__.py b/src/haddock/modules/refinement/emref/__init__.py index 7c62266d3..acdefc589 100644 --- a/src/haddock/modules/refinement/emref/__init__.py +++ b/src/haddock/modules/refinement/emref/__init__.py @@ -99,7 +99,7 @@ def _run(self) -> None: "emref", ambig_fname=ambig_fname, native_segid=True, - less_io=self.params["less_io"], + debug=self.params["debug"], seed=model.seed if isinstance(model, PDBFile) else None, ) out_file = f"emref_{idx}.out" diff --git a/src/haddock/modules/refinement/flexref/__init__.py b/src/haddock/modules/refinement/flexref/__init__.py index 63ffa8d8b..fca1801a6 100644 --- a/src/haddock/modules/refinement/flexref/__init__.py +++ b/src/haddock/modules/refinement/flexref/__init__.py @@ -113,7 +113,7 @@ def _run(self) -> None: "flexref", ambig_fname=ambig_fname, native_segid=True, - less_io=self.params["less_io"], + debug=self.params["debug"], seed=model.seed if isinstance(model, PDBFile) else None, ) diff --git a/src/haddock/modules/refinement/mdref/__init__.py b/src/haddock/modules/refinement/mdref/__init__.py index c74653c21..3d651f585 100644 --- a/src/haddock/modules/refinement/mdref/__init__.py +++ b/src/haddock/modules/refinement/mdref/__init__.py @@ -112,7 +112,7 @@ def _run(self) -> None: "mdref", ambig_fname=ambig_fname, native_segid=True, - less_io=self.params["less_io"], + debug=self.params["debug"], seed=model.seed if isinstance(model, PDBFile) else None, ) out_file = f"mdref_{idx}.out" diff --git a/src/haddock/modules/sampling/rigidbody/__init__.py b/src/haddock/modules/sampling/rigidbody/__init__.py index 3eda396cc..e4a3f9fac 100644 --- a/src/haddock/modules/sampling/rigidbody/__init__.py +++ b/src/haddock/modules/sampling/rigidbody/__init__.py @@ -114,7 +114,7 @@ def prepare_cns_input_sequential( ambig_fname=ambig_fname, default_params_path=self.toppar_path, native_segid=True, - less_io=self.params["less_io"], + debug=self.params["debug"], seed=seed, ) _l.append((combination, rigidbody_input, ambig_fname, seed)) @@ -150,7 +150,7 @@ def prepare_cns_input_parallel( ambig_fname=ambig_fname, native_segid=True, default_params_path=self.toppar_path, - less_io=self.params["less_io"], + debug=self.params["debug"], seed=seed, ) diff --git a/src/haddock/modules/scoring/emscoring/__init__.py b/src/haddock/modules/scoring/emscoring/__init__.py index 5a8ba788a..b23d7d4e5 100644 --- a/src/haddock/modules/scoring/emscoring/__init__.py +++ b/src/haddock/modules/scoring/emscoring/__init__.py @@ -56,7 +56,7 @@ def _run(self) -> None: self.params, "emscoring", native_segid=True, - less_io=self.params["less_io"], + debug=self.params["debug"], seed=model.seed if isinstance(model, PDBFile) else None, ) diff --git a/src/haddock/modules/scoring/mdscoring/__init__.py b/src/haddock/modules/scoring/mdscoring/__init__.py index 1920ba22b..88cdc9bfa 100644 --- a/src/haddock/modules/scoring/mdscoring/__init__.py +++ b/src/haddock/modules/scoring/mdscoring/__init__.py @@ -56,7 +56,7 @@ def _run(self) -> None: self.params, "mdscoring", native_segid=True, - less_io=self.params["less_io"], + debug=self.params["debug"], seed=model.seed if isinstance(model, PDBFile) else None, ) diff --git a/src/haddock/modules/topology/topoaa/__init__.py b/src/haddock/modules/topology/topoaa/__init__.py index b8b5ed111..a9cbc7a1c 100644 --- a/src/haddock/modules/topology/topoaa/__init__.py +++ b/src/haddock/modules/topology/topoaa/__init__.py @@ -254,7 +254,7 @@ def _run(self) -> None: self.params, parameters_for_this_molecule, default_params_path=self.toppar_path, - write_to_disk=not self.params["less_io"], + write_to_disk=self.params["debug"], ) self.log("Topology CNS input created") diff --git a/tests/test_libcns.py b/tests/test_libcns.py index 8e9cd3316..c1978dc3c 100644 --- a/tests/test_libcns.py +++ b/tests/test_libcns.py @@ -127,7 +127,7 @@ def test_prepare_cns_input(pdbfile): ambig_fname="", native_segid=False, default_params_path=None, - less_io=True, + debug=False, seed=pdbfile.seed, ) From b0219906a534c3eb8e25085bde3d3a16163c9446 Mon Sep 17 00:00:00 2001 From: VGPReys Date: Thu, 19 Sep 2024 10:18:47 +0200 Subject: [PATCH 46/62] fix capri logics with debug --- integration_tests/test_caprieval.py | 2 +- src/haddock/modules/analysis/caprieval/__init__.py | 6 +++--- src/haddock/modules/analysis/caprieval/capri.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/integration_tests/test_caprieval.py b/integration_tests/test_caprieval.py index 48f87c56d..6764f5faf 100644 --- a/integration_tests/test_caprieval.py +++ b/integration_tests/test_caprieval.py @@ -330,7 +330,7 @@ def test_caprieval_default( ) -def test_caprieval_debug( +def test_caprieval_nodebug( caprieval_module, model_list, expected_ss_data, expected_clt_data ): caprieval_module.previous_io = MockPreviousIO(path=caprieval_module.path) diff --git a/src/haddock/modules/analysis/caprieval/__init__.py b/src/haddock/modules/analysis/caprieval/__init__.py index 4873a86af..92f49e435 100644 --- a/src/haddock/modules/analysis/caprieval/__init__.py +++ b/src/haddock/modules/analysis/caprieval/__init__.py @@ -112,7 +112,7 @@ def _run(self) -> None: exec_mode = get_analysis_exec_mode(self.params["mode"]) Engine = get_engine(exec_mode, self.params) - less_io = not self.params["debug"] and self.params["mode"] == "local" + _less_io = self.params["mode"] == "local" and not self.params["debug"] # Each model is a job; this is not the most efficient way # but by assigning each model to an individual job @@ -133,14 +133,14 @@ def _run(self) -> None: path=Path("."), reference=reference, params=self.params, - debug=less_io, + less_io=_less_io, ) ) engine = Engine(jobs) engine.run() - if less_io and isinstance(engine, Scheduler): + if _less_io and isinstance(engine, Scheduler): jobs = engine.results extract_data_from_capri_class( capri_objects=jobs, diff --git a/src/haddock/modules/analysis/caprieval/capri.py b/src/haddock/modules/analysis/caprieval/capri.py index a7eab5fe5..98efd7e62 100644 --- a/src/haddock/modules/analysis/caprieval/capri.py +++ b/src/haddock/modules/analysis/caprieval/capri.py @@ -179,7 +179,7 @@ def __init__( path: Path, reference: PDBPath, params: ParamMap, - debug: Optional[bool] = True, + less_io: Optional[bool] = True, ) -> None: """ Initialize the class. @@ -225,7 +225,7 @@ def __init__( self.output = self.output_ss_fname self.identificator = identificator self.core_model_idx = identificator - self.debug = debug + self.less_io = less_io def calc_irmsd(self, cutoff: float = 5.0) -> None: """Calculate the I-RMSD. @@ -654,7 +654,7 @@ def run(self) -> Union[None, "CAPRI"]: log.debug(f"id {self.identificator}, calculating global RMSD") self.calc_global_rmsd() - if self.debug: + if not self.less_io: self.make_output() else: # The scheduler will use the return of the `run` method as the output of the tasks From 0faedbaad404abcfa22d1df6294e936632a00e32 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Thu, 19 Sep 2024 13:42:02 +0200 Subject: [PATCH 47/62] change python version --- .github/workflows/ci.yml | 3 +-- requirements.txt | 24 ++++++++++++------------ setup.py | 9 +++++++-- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da7c14060..09557964d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,8 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - # python-version: ["3.9", "3.10", "3.11", "3.12"] - python-version: ["3.9"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 diff --git a/requirements.txt b/requirements.txt index 2d38b246a..6c4eeb34a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,12 @@ -pdb-tools==2.5.0 -biopython==1.79 -jsonpickle==2.1.0 -numpy==1.22.2 -pyyaml==6.0 -scipy==1.10.0 -toml==0.10.2 -tox==3.24.5 -pandas==1.5.1 -plotly==5.11.0 -kaleido==0.2.1 -freesasa==2.2.1 +pdb-tools>=2.5.0 +biopython>=1.79 +jsonpickle>=2.1.0 +numpy>=1.22.2 +pyyaml>=6.0 +scipy>=1.10.0 +toml>=0.10.2 +tox>=3.24.5 +pandas>=1.5.1 +plotly>=5.11.0 +kaleido>=0.2.1 +freesasa>=2.2.1 diff --git a/setup.py b/setup.py index d28103dfa..2a32e21da 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,6 @@ import platform import subprocess import sys -import tempfile import urllib.request from os.path import dirname, join from pathlib import Path @@ -38,6 +37,8 @@ class CustomBuild(build_ext): + """Custom build handles the C/C++ dependencies""" + def run(self): print("Building HADDOCK3 C/C++ binary dependencies...") self.build_executable( @@ -63,6 +64,7 @@ def run(self): build_ext.run(self) def build_executable(self, name, cmd): + """Helper function to execute the build command""" try: subprocess.check_call(cmd) # Ensure the source bin directory exists @@ -182,6 +184,9 @@ def read_description(*names, **kwargs) -> str: "Operating System :: POSIX :: Linux", "Operating System :: MacOS", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ], project_urls={ "webpage": "https://bonvinlab.org/haddock3", @@ -197,7 +202,7 @@ def read_description(*names, **kwargs) -> str: "Protein docking", "Proteins", ], - python_requires=">=3.9, <3.10", + python_requires=">=3.9, <3.13", install_requires=[requirements], extras_require={}, setup_requires=[], From d7350b4166cebdfb740e0ea2b997b150bbadb90e Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Thu, 19 Sep 2024 13:50:22 +0200 Subject: [PATCH 48/62] add setuptools as dependency --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 6c4eeb34a..68f773b85 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,4 @@ pandas>=1.5.1 plotly>=5.11.0 kaleido>=0.2.1 freesasa>=2.2.1 +setuptools From 840b3cb905bf494817efb160e54b9912121baa44 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Thu, 19 Sep 2024 13:51:54 +0200 Subject: [PATCH 49/62] update ci.yml --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09557964d..98acb4216 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,7 @@ jobs: strategy: matrix: python-version: ["3.9", "3.10", "3.11", "3.12"] + fail-fast: false steps: - uses: actions/checkout@v4 From 80ef6b24094153998149cb95aa90b0146ba70d9a Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Thu, 19 Sep 2024 13:55:43 +0200 Subject: [PATCH 50/62] fix numpy version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 68f773b85..2c7f05688 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ pdb-tools>=2.5.0 biopython>=1.79 jsonpickle>=2.1.0 -numpy>=1.22.2 +numpy==1.22.2 pyyaml>=6.0 scipy>=1.10.0 toml>=0.10.2 From 9fe01e1596ce6489251ebe0d06104601b9e40741 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Thu, 19 Sep 2024 13:59:23 +0200 Subject: [PATCH 51/62] revert versions --- requirements.txt | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/requirements.txt b/requirements.txt index 2c7f05688..2d38b246a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,12 @@ -pdb-tools>=2.5.0 -biopython>=1.79 -jsonpickle>=2.1.0 +pdb-tools==2.5.0 +biopython==1.79 +jsonpickle==2.1.0 numpy==1.22.2 -pyyaml>=6.0 -scipy>=1.10.0 -toml>=0.10.2 -tox>=3.24.5 -pandas>=1.5.1 -plotly>=5.11.0 -kaleido>=0.2.1 -freesasa>=2.2.1 -setuptools +pyyaml==6.0 +scipy==1.10.0 +toml==0.10.2 +tox==3.24.5 +pandas==1.5.1 +plotly==5.11.0 +kaleido==0.2.1 +freesasa==2.2.1 From 97628ac5dc8a71f83a2f2b32443c065f4e8f8d83 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Thu, 19 Sep 2024 14:06:17 +0200 Subject: [PATCH 52/62] update minimal python version --- .github/workflows/ci.yml | 4 ++-- setup.py | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98acb4216..f2a8aaa43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10"] fail-fast: false steps: @@ -41,7 +41,7 @@ jobs: - name: run tests run: >- - pytest . -x + pytest . --cov --cov-report=term-missing --cov-append --hypothesis-show-statistics diff --git a/setup.py b/setup.py index 2a32e21da..c1bbe77d5 100644 --- a/setup.py +++ b/setup.py @@ -183,10 +183,12 @@ def read_description(*names, **kwargs) -> str: "Operating System :: POSIX", "Operating System :: POSIX :: Linux", "Operating System :: MacOS", + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering :: Bio-Informatics", + "Topic :: Scientific/Engineering :: Chemistry", + "Topic :: Scientific/Engineering :: Physics", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", ], project_urls={ "webpage": "https://bonvinlab.org/haddock3", @@ -202,7 +204,7 @@ def read_description(*names, **kwargs) -> str: "Protein docking", "Proteins", ], - python_requires=">=3.9, <3.13", + python_requires=">=3.9, <3.11", install_requires=[requirements], extras_require={}, setup_requires=[], From b69868447168f527d2b4e320434291b754131829 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Thu, 19 Sep 2024 14:21:08 +0200 Subject: [PATCH 53/62] add disclaimer --- src/haddock/gear/greetings.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/haddock/gear/greetings.py b/src/haddock/gear/greetings.py index 10db12a46..3d45013a2 100644 --- a/src/haddock/gear/greetings.py +++ b/src/haddock/gear/greetings.py @@ -5,7 +5,7 @@ import sys from datetime import datetime from functools import partial -from typing import Sequence, Callable +from typing import Callable, Sequence from haddock import contact_us, version @@ -32,7 +32,15 @@ "GitHub issues": "https://github.com/haddocking/haddock3/issues", "BioExcel feedback": "https://www.bonvinlab.org/feedback", "BioExcel survey": "https://bioexcel.eu/bioexcel-survey-2024/", - } +} + + +DISCLAIMER = ( + "Some of the HADDOCK3 components use CNS (Crystallographic and NMR System) which is" + "free of use for non-profit applications. For commercial use it is your own responsibility" + "to have a proper license. For details refer to the " + " DISCLAIMER file in the HADDOCK3 repository." +) def get_initial_greeting() -> str: @@ -47,6 +55,8 @@ def get_initial_greeting() -> str: f"""# #{os.linesep}""" f"""##############################################{os.linesep}""" f"""{os.linesep}""" + f"""{DISCLAIMER}{os.linesep}""" + f"""{os.linesep}""" f"""Starting HADDOCK {version} on {now}{os.linesep}""" f"""{os.linesep}""" f"""Python {python_version}{os.linesep}""" @@ -81,7 +91,7 @@ def get_goodbye_help() -> str: def gen_feedback_messages(print_function: Callable) -> None: """Print list of feedbacks urls. - + Parameters ---------- print_function : Callable @@ -92,8 +102,8 @@ def gen_feedback_messages(print_function: Callable) -> None: ( "Your feedback matters in Haddock3!" " Share your experience and help us grow:" - ) ) + ) for name, url in feedback_urls.items(): print_function(f"{name}: {url}") From 0243172553926bcd1aa4b46f2d41bb1f8fb9d6af Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Thu, 19 Sep 2024 14:40:28 +0200 Subject: [PATCH 54/62] update ci.yml --- .github/workflows/ci.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2a8aaa43..b21e9bf7b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,9 +39,9 @@ jobs: # run: mypy src/ ############################################################### - - name: run tests + - name: run unit tests run: >- - pytest . + pytest tests/ --cov --cov-report=term-missing --cov-append --hypothesis-show-statistics @@ -57,3 +57,9 @@ jobs: with: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} coverage-reports: ./coverage.xml + + - name: run integration tests + run: >- + pytest integration_tests/ + --cov --cov-report=term-missing --cov-append + --hypothesis-show-statistics From 7bd41b6b1fe9ac705faf5ec30b6f3fe75dd25b0d Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Thu, 19 Sep 2024 14:48:32 +0200 Subject: [PATCH 55/62] update ci.yml --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b21e9bf7b..c92e672f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,6 +45,12 @@ jobs: --cov --cov-report=term-missing --cov-append --hypothesis-show-statistics + - name: run integration tests + run: >- + pytest integration_tests/ + --cov --cov-report=term-missing --cov-append + --hypothesis-show-statistics + - name: generate coverage report run: | coverage report @@ -57,9 +63,3 @@ jobs: with: project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} coverage-reports: ./coverage.xml - - - name: run integration tests - run: >- - pytest integration_tests/ - --cov --cov-report=term-missing --cov-append - --hypothesis-show-statistics From 0f9c4b94e227417f97eb149c9c4fb78cb6bf38d1 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Thu, 19 Sep 2024 14:56:05 +0200 Subject: [PATCH 56/62] update disclaimer format --- src/haddock/gear/greetings.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/haddock/gear/greetings.py b/src/haddock/gear/greetings.py index 3d45013a2..df3f2b2d3 100644 --- a/src/haddock/gear/greetings.py +++ b/src/haddock/gear/greetings.py @@ -36,10 +36,11 @@ DISCLAIMER = ( - "Some of the HADDOCK3 components use CNS (Crystallographic and NMR System) which is" - "free of use for non-profit applications. For commercial use it is your own responsibility" - "to have a proper license. For details refer to the " - " DISCLAIMER file in the HADDOCK3 repository." + "!! Some of the HADDOCK3 components use CNS (Crystallographic and NMR System)" + f" which is free of use for non-profit applications. !!{os.linesep}" + "!! For commercial use it is your own responsibility" + f" to have a proper license. !!{os.linesep}" + "!! For details refer to the DISCLAIMER file in the HADDOCK3 repository. !!" ) From 3f66ce6ec0e44ab8f422edb27c37540ae33db010 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Thu, 19 Sep 2024 15:10:47 +0200 Subject: [PATCH 57/62] add fallback logic to define `cns_exec` --- src/haddock/core/defaults.py | 16 ++++++++++++++-- src/haddock/modules/topology/topoaa/__init__.py | 7 +++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/haddock/core/defaults.py b/src/haddock/core/defaults.py index fa8a8805f..d957e6676 100644 --- a/src/haddock/core/defaults.py +++ b/src/haddock/core/defaults.py @@ -1,19 +1,31 @@ """All default parameters used by the framework.""" import importlib.resources +import os import string +import sys from pathlib import Path import yaml from pkg_resources import resource_filename import haddock -from haddock import core_path +from haddock import core_path, log BINARY_DIR = Path(importlib.resources.files(haddock) / "bin") # type: ignore -cns_exec = Path(resource_filename("haddock", "bin/cns")) +cns_exec = Path(resource_filename("haddock", "bin/cns_")) +if not cns_exec.exists(): + log.warning("CNS executable not found at %s", cns_exec) + _cns_exec = os.environ.get("CNS_EXEC") + if _cns_exec is None: + log.error( + "Please define the location the CNS binary by setting a CNS_EXEC system variable" + ) + sys.exit(1) + else: + cns_exec = Path(_cns_exec) CONTACT_FCC_EXEC = Path(resource_filename("haddock", "bin/contact_fcc")) FAST_RMSDMATRIX_EXEC = Path(resource_filename("haddock", "bin/fast-rmsdmatrix")) diff --git a/src/haddock/modules/topology/topoaa/__init__.py b/src/haddock/modules/topology/topoaa/__init__.py index b8b5ed111..29f556b3d 100644 --- a/src/haddock/modules/topology/topoaa/__init__.py +++ b/src/haddock/modules/topology/topoaa/__init__.py @@ -29,7 +29,7 @@ from functools import partial from pathlib import Path -from haddock.core.defaults import MODULE_DEFAULT_YAML +from haddock.core.defaults import MODULE_DEFAULT_YAML, cns_exec from haddock.core.typing import FilePath, Optional, ParamDict, ParamMap, Union from haddock.libs import libpdb from haddock.libs.libcns import ( @@ -37,7 +37,7 @@ load_workflow_params, prepare_output, prepare_single_input, -) + ) from haddock.libs.libontology import Format, PDBFile, TopologyFile from haddock.libs.libstructure import make_molecules from haddock.libs.libsubprocess import CNSJob @@ -262,13 +262,12 @@ def _run(self) -> None: # Add new job to the pool output_filename = Path(f"{model.stem}.{Format.CNS_OUTPUT}") err_fname = f"{model.stem}.cnserr" - job = CNSJob( topoaa_input, output_filename, err_fname, envvars=self.envvars, - cns_exec=self.params["cns_exec"], + cns_exec=cns_exec, ) jobs.append(job) From c6e8132ebd9fdf953682b9290865ae663378d25f Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Thu, 19 Sep 2024 15:12:45 +0200 Subject: [PATCH 58/62] typo --- src/haddock/core/defaults.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/haddock/core/defaults.py b/src/haddock/core/defaults.py index d957e6676..a0e965f08 100644 --- a/src/haddock/core/defaults.py +++ b/src/haddock/core/defaults.py @@ -15,7 +15,7 @@ BINARY_DIR = Path(importlib.resources.files(haddock) / "bin") # type: ignore -cns_exec = Path(resource_filename("haddock", "bin/cns_")) +cns_exec = Path(resource_filename("haddock", "bin/cns")) if not cns_exec.exists(): log.warning("CNS executable not found at %s", cns_exec) _cns_exec = os.environ.get("CNS_EXEC") From 69ce3dc216823c1d45ec1cf22ace731135904178 Mon Sep 17 00:00:00 2001 From: Rodrigo V Honorato Date: Thu, 19 Sep 2024 15:18:05 +0200 Subject: [PATCH 59/62] update `docs.yml` --- .github/workflows/docs.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e752afc06..e6fc6d62d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -21,5 +21,8 @@ jobs: python -m pip install pip==23.1.2 setuptools==67.7.2 wheel==0.40.0 pip install virtualenv==20.23.0 tox==4.5.1.1 + - name: install haddock3 + run: pip install -v . + - name: docs run: tox -e docs From 5357e081abb8a168e9beaf8750e4f80c25a810fe Mon Sep 17 00:00:00 2001 From: VGPReys Date: Fri, 20 Sep 2024 11:37:50 +0200 Subject: [PATCH 60/62] apply suggestions and set default value of debug to false --- src/haddock/modules/analysis/caprieval/__init__.py | 2 +- src/haddock/modules/analysis/caprieval/capri.py | 6 +++--- src/haddock/modules/defaults.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/haddock/modules/analysis/caprieval/__init__.py b/src/haddock/modules/analysis/caprieval/__init__.py index 92f49e435..894b8a906 100644 --- a/src/haddock/modules/analysis/caprieval/__init__.py +++ b/src/haddock/modules/analysis/caprieval/__init__.py @@ -133,7 +133,7 @@ def _run(self) -> None: path=Path("."), reference=reference, params=self.params, - less_io=_less_io, + debug=not _less_io, ) ) diff --git a/src/haddock/modules/analysis/caprieval/capri.py b/src/haddock/modules/analysis/caprieval/capri.py index 98efd7e62..47b8f85b0 100644 --- a/src/haddock/modules/analysis/caprieval/capri.py +++ b/src/haddock/modules/analysis/caprieval/capri.py @@ -179,7 +179,7 @@ def __init__( path: Path, reference: PDBPath, params: ParamMap, - less_io: Optional[bool] = True, + debug: Optional[bool] = False, ) -> None: """ Initialize the class. @@ -225,7 +225,7 @@ def __init__( self.output = self.output_ss_fname self.identificator = identificator self.core_model_idx = identificator - self.less_io = less_io + self.debug = debug def calc_irmsd(self, cutoff: float = 5.0) -> None: """Calculate the I-RMSD. @@ -654,7 +654,7 @@ def run(self) -> Union[None, "CAPRI"]: log.debug(f"id {self.identificator}, calculating global RMSD") self.calc_global_rmsd() - if not self.less_io: + if self.debug: self.make_output() else: # The scheduler will use the return of the `run` method as the output of the tasks diff --git a/src/haddock/modules/defaults.yaml b/src/haddock/modules/defaults.yaml index 972a19b66..5e4210915 100644 --- a/src/haddock/modules/defaults.yaml +++ b/src/haddock/modules/defaults.yaml @@ -139,7 +139,7 @@ offline: group: "execution" explevel: easy debug: - default: true + default: false type: boolean title: Write intermediate and stdout files. short: Write intermediate and stdout files. From a3d62784e74e9d809eee9fff5e256f21f255215c Mon Sep 17 00:00:00 2001 From: VGPReys Date: Fri, 20 Sep 2024 12:33:30 +0200 Subject: [PATCH 61/62] add debug = True in integration tests --- integration_tests/test_emref.py | 5 +++++ integration_tests/test_flexref.py | 4 +++- integration_tests/test_mdref.py | 6 +++++- integration_tests/test_restraints.py | 1 + integration_tests/test_rigidbody.py | 3 ++- integration_tests/test_topoaa.py | 3 +++ 6 files changed, 19 insertions(+), 3 deletions(-) diff --git a/integration_tests/test_emref.py b/integration_tests/test_emref.py index ab7ac0db2..be740fef8 100644 --- a/integration_tests/test_emref.py +++ b/integration_tests/test_emref.py @@ -67,6 +67,7 @@ def output(self): def test_emref_defaults(emref_module, calc_fnat): emref_module.previous_io = MockPreviousIO(path=emref_module.path) + emref_module.params["debug"] = True emref_module.run() @@ -91,6 +92,8 @@ def test_emref_fle(emref_module, calc_fnat): emref_module.params["fle_end_1 "] = 77 emref_module.params["fle_seg_1 "] = "B" + emref_module.params["debug"] = True + emref_module.run() assert Path(emref_module.path, "emref_1.pdb").exists() @@ -118,6 +121,8 @@ def test_emref_mutliple_fle(emref_module, calc_fnat): emref_module.params["fle_end_2 "] = 47 emref_module.params["fle_seg_2 "] = "B" + emref_module.params["debug"] = True + emref_module.run() assert Path(emref_module.path, "emref_1.pdb").exists() diff --git a/integration_tests/test_flexref.py b/integration_tests/test_flexref.py index 9b60d989b..86ef51f64 100644 --- a/integration_tests/test_flexref.py +++ b/integration_tests/test_flexref.py @@ -74,7 +74,6 @@ def test_flexref_defaults(flexref_module, calc_fnat): flexref_module.run() assert Path(flexref_module.path, "flexref_1.pdb").exists() - assert Path(flexref_module.path, "flexref_1.out.gz").exists() fnat = calc_fnat( model=Path(flexref_module.path, "flexref_1.pdb"), @@ -94,6 +93,7 @@ def test_flexref_fle(flexref_module, calc_fnat): flexref_module.params["fle_end_1 "] = 77 flexref_module.params["fle_seg_1 "] = "B" flexref_module.params["log_level"] = "verbose" + flexref_module.params["debug"] = True flexref_module.run() @@ -124,6 +124,8 @@ def test_flexref_mutliple_fle(flexref_module, calc_fnat): flexref_module.params["fle_end_2 "] = 47 flexref_module.params["fle_seg_2 "] = "B" + flexref_module.params["debug"] = True + flexref_module.run() assert Path(flexref_module.path, "flexref_1.pdb").exists() diff --git a/integration_tests/test_mdref.py b/integration_tests/test_mdref.py index 0102713b3..b6e20913f 100644 --- a/integration_tests/test_mdref.py +++ b/integration_tests/test_mdref.py @@ -67,7 +67,7 @@ def output(self): def test_mdref_defaults(mdref_module, calc_fnat): mdref_module.previous_io = MockPreviousIO(path=mdref_module.path) - + mdref_module.params["debug"] = True mdref_module.run() assert Path(mdref_module.path, "mdref_1.pdb").exists() @@ -91,6 +91,8 @@ def test_mdref_fle(mdref_module, calc_fnat): mdref_module.params["fle_end_1 "] = 77 mdref_module.params["fle_seg_1 "] = "B" + mdref_module.params["debug"] = True + mdref_module.run() assert Path(mdref_module.path, "mdref_1.pdb").exists() @@ -118,6 +120,8 @@ def test_mdref_mutliple_fle(mdref_module, calc_fnat): mdref_module.params["fle_end_2 "] = 47 mdref_module.params["fle_seg_2 "] = "B" + mdref_module.params["debug"] = True + mdref_module.run() assert Path(mdref_module.path, "mdref_1.pdb").exists() diff --git a/integration_tests/test_restraints.py b/integration_tests/test_restraints.py index 7aed72c54..bf2c6c719 100644 --- a/integration_tests/test_restraints.py +++ b/integration_tests/test_restraints.py @@ -88,6 +88,7 @@ def test_restraints_rigidbody(rigidbody_module): rigidbody_module.params["unambig_fname"] = Path(GOLDEN_DATA, "unambig.tbl") rigidbody_module.params["hbond_fname"] = Path(GOLDEN_DATA, "hbond.tbl") rigidbody_module.params["mode"] = "local" + rigidbody_module.params["debug"] = True rigidbody_module.run() diff --git a/integration_tests/test_rigidbody.py b/integration_tests/test_rigidbody.py index 34f9da2a1..25dd0ba67 100644 --- a/integration_tests/test_rigidbody.py +++ b/integration_tests/test_rigidbody.py @@ -83,6 +83,7 @@ def test_rigidbody_local(rigidbody_module): rigidbody_module.params["mol_fix_origin_1"] = True rigidbody_module.params["mol_fix_origin_2"] = False rigidbody_module.params["mode"] = "local" + rigidbody_module.params["debug"] = True rigidbody_module.run() @@ -108,6 +109,7 @@ def test_rigidbody_mpi(rigidbody_module): rigidbody_module.params["mol_fix_origin_2"] = False rigidbody_module.params["mode"] = "mpi" rigidbody_module.params["ncores"] = 1 + rigidbody_module.params["debug"] = True rigidbody_module.run() @@ -130,7 +132,6 @@ def test_rigidbody_debug(rigidbody_module): rigidbody_module.params["cmrest"] = True rigidbody_module.params["mol_fix_origin_1"] = True rigidbody_module.params["mol_fix_origin_2"] = False - rigidbody_module.params["debug"] = False rigidbody_module.run() diff --git a/integration_tests/test_topoaa.py b/integration_tests/test_topoaa.py index 4eef9b39a..d516df4fe 100644 --- a/integration_tests/test_topoaa.py +++ b/integration_tests/test_topoaa.py @@ -29,6 +29,7 @@ def test_topoaa_module_protein(topoaa_module): topoaa_module.params["mol1"] = {"prot_segid": "A"} topoaa_module.params["mol2"] = {"prot_segid": "B"} topoaa_module.params["cns_exec"] = CNS_EXEC + topoaa_module.params["debug"] = True topoaa_module.run() @@ -71,6 +72,7 @@ def test_topoaa_module_ligand(topoaa_module): topoaa_module.params["delenph"] = False topoaa_module.params["preprocess"] = False topoaa_module.params["cns_exec"] = CNS_EXEC + topoaa_module.params["debug"] = True topoaa_module.run() @@ -94,6 +96,7 @@ def test_topoaa_cyclic(topoaa_module): topoaa_module.params["disulphide_dist"] = 4.0 topoaa_module.params["mol1"] = {"cyclicpept": True} topoaa_module.params["cns_exec"] = CNS_EXEC + topoaa_module.params["debug"] = True topoaa_module.run() From 1910851dba0db1fdb30ec73d682988cea97d61e1 Mon Sep 17 00:00:00 2001 From: Rodrigo Vargas Honorato Date: Fri, 20 Sep 2024 13:46:12 +0200 Subject: [PATCH 62/62] Update pages.yml --- .github/workflows/pages.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 561f88626..4fdbc5523 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -26,15 +26,7 @@ jobs: # needs to install hd3 so the command-line pages are # rendered properly - name: Install HADDOCK3 - run: | - pwd - ls -lsa - mkdir bin - touch bin/cns - cd src/fcc/src - chmod u+x Makefile - ./Makefile 2>%1 >/dev/null || true - cd - + run: pip install -v . - name: Generate docs run: |