From c527f73753190eb75efc561dfc8bafdd4427f5bc Mon Sep 17 00:00:00 2001 From: Vitalii Koshura Date: Thu, 16 Jun 2022 01:45:57 +0200 Subject: [PATCH] [boinc-autodock-vina] Simplify json schema. Signed-off-by: Vitalii Koshura --- boinc-autodock-vina/README.md | 217 ++++----------- .../samples/basic_docking/1iep_vina.json | 30 +-- .../samples/basic_docking_full/1iep_vina.json | 32 +-- .../1iep_vina.json | 36 +-- .../1iep_vina_copy.json | 36 +-- .../boinc-autodock-vina.cpp | 2 +- .../src/boinc-autodock-vina/calculate.cpp | 6 +- boinc-autodock-vina/src/common/config.cpp | 166 +++++++----- boinc-autodock-vina/src/common/config.h | 4 +- .../src/unit-tests/config-tests.cpp | 107 +------- .../src/unit-tests/input-config-tests.cpp | 123 +++------ .../src/work-generator/input-config.cpp | 255 +++++++++++++----- .../src/work-generator/input-config.h | 7 +- 13 files changed, 438 insertions(+), 583 deletions(-) diff --git a/boinc-autodock-vina/README.md b/boinc-autodock-vina/README.md index d9e3dba..851a9d5 100644 --- a/boinc-autodock-vina/README.md +++ b/boinc-autodock-vina/README.md @@ -8,21 +8,9 @@ If `prepare_receptors` section contains multiple receptors, `work unit generator ## JSON schema -This file supports 7 groups of parameters: +This file supports next parameters: -- `prepare_receptors`: parameters for preparing receptors. This is an **optional** group. -- `prepare_ligands`: parameters for preparing ligands. This is an **optional** group. -- `input` - input PDBQT data. This is an **optional** group. -- `search_area` - search area. This is an **optional** group. -- `output` - output data. This is a **required** group. -- `advanced` - advanced parameters. This is an **optional** group. -- `misc` - miscellaneous parameters. This is an **optional** group. - -### **prepare_receptors** parameters group - -This group supports next parameters: - -- `receptors` - list of paths to receptors to be processed. This is a **required** `list of strings` parameter. Supported file types include PDB, MOL2, PDBQ, PDBQS, PDBQT, possibly PQR, CIF. +- `receptor` - list of paths to receptors to be processed. This is a **optional** `list of strings` parameter. Supported file types include PDB, MOL2, PDBQ, PDBQS, PDBQT, possibly PQR, CIF. This file should not have absolute path. Can't be used together with `maps` parameter. Either `receptor` or `maps` **should be specified** for `vina` and `vinardo` scoring function. This parameter is **not allowed** for `ad4` scoring function. - `repair` - type of repairs to make, this is an **optional** `string` parameter: - `bonds_hydrogens`: build bonds and add hydrogens - `bonds`: build a single bond from each atom with no bonds to its closest neighbor @@ -37,12 +25,7 @@ This group supports next parameters: - `nonstdres`: remove chains composed entirely of residues of types other than the standard 20 amino acids - `deleteAltB`: remove XX@B atoms and rename XX@A atoms->XX - `delete_nonstd_residue` - delete every nonstd residue from any chain. This is an optional **boolean** parameter. Default is **false**. - -### **prepare_ligands** parameters group - -This group supports next parameters: - -- `ligand` - path to ligand to be processed. This is a **required** `string` parameter. +- `ligand` - list of paths to ligands to be processed. This is an **optional** `list of strings` parameter. Either `ligands` or `batch` parameter should be specified. - `selected_ligands` - if `ligand` parameter refers to the file that contains more than 1 ligand, this parameter should include list of ligands that should be processed and used to generate work units. This is an **optional** `list of strings` parameter. - `multimol` - if `ligand` parameter refers to the file that contains more than 1 ligand, for correct processing this parameter should be set to `true`. This is an optional **bool** parameter with default value set to **false**. - `multimol_prefix` - if `ligand` parameter refers to the file that contains more than 1 ligand, replace internal molecule name in multi-molecule input by specified prefix. This is an **optional** `string` parameter. @@ -56,48 +39,34 @@ This group supports next parameters: - `double_bond_penalty` - penalty > 100 prevents breaking double bonds. This is an **optional** `double` parameter. - `remove_index_map` - do not write map of atom indices from input to PDBQT. This is an **optional** `boolean` parameter. Default is **false**. - `remove_smiles` - do not write smiles as remark to PDBQT. This is an **optional** `boolean` parameter. Default is **false**. - -### **input** parameters group - -This group supports next parameters: - -- `receptor` - path to PDBQT file with rigid part of the receptor. This file should not have absolute path. This is an **optional** `string` parameter. Can't be used together with `search_area.maps` parameter. Either `input.receptor` or `search_area.maps` **should be specified** for `vina` and `vinardo` scoring function. This parameter is **not allowed** for `ad4` scoring function. If `prepare_receptors` section is used, this parameter will be automatically filled from it. - `flex` - path to PDBQT file with flexible side chains, if any. This file should not have absolute path. This is an **optional** `string` parameter for `vina` and `vinardo` scoring functions. This parameter is **required** for `ad4` scoring function. -- `ligands` - paths to PDBQT files with ligands. This file should not have absolute path. This is an **optional** `list of string` parameters. Either `input.ligands` or `input.batch` parameter should be specified. If `prepare_ligands` section is used, this parameter will be automatically filled from it. +- `ligands` - paths to PDBQT files with ligands. This file should not have absolute path. This is an **optional** `list of string` parameters. Either `ligands` or `batch` parameter should be specified. - `batch` - paths to PDBQT files with batch ligands. This file should not have absolute path. This is an **optional** `list of string` parameters. Either `input.ligands` or `input.batch` parameter should be specified. - `scoring` - scoring function (`ad4`, `vina` or `vinardo`). This is an **optional** `string` parameter. Default function is `vina`. - -### **search_area** parameters group - -This group supports next parameters: - -- `maps` - path to the folder with affinity maps **including prefix**. This is an **optional** `string` parameter. E.g. for the folder with maps `.\maps\1iep_receptor.A.map` and `.\maps\1iep_receptor.C.map` should be provided as `maps\1iep_receptor`. If this parameter is not specified, then `search_area.center_x`, `search_area.center_y`, `search_area.center_z` and `search_area.size_x`, `search_area.size_y`, `search_area.size_x` parameters should be specified to generate affinity maps. Can't be used together with `input.receptor` parameter. Either `input.receptor` or `search_area.maps` **should be specified** for `vina` and `vinardo` scoring function. This parameter is **required** for `ad4` scoring function. -- `center_x` - X coordinate of the center (Angstrom). This `double` parameter is ignored when `search_area.maps` parameter is specified. -- `center_y` - Y coordinate of the center (Angstrom). This `double` parameter is ignored when `search_area.maps` parameter is specified. -- `center_z` - Z coordinate of the center (Angstrom). This `double` parameter is ignored when `search_area.maps` parameter is specified. -- `size_x` - size in the X dimension (Angstrom). This `double` parameter is ignored when `search_area.maps` parameter is specified. -- `size_y` - size in the Y dimension (Angstrom). This `double` parameter is ignored when `search_area.maps` parameter is specified. -- `size_z` - size in the Z dimension (Angstrom). This `double` parameter is ignored when `search_area.maps` parameter is specified. -- `autobox` - sets maps dimensions based on input ligand(s). This parameter is used when `advanced.score_only` or `advanced.local_only` parameter is specified. This is an **optional** `boolean` parameter. Default value is `false`. - -### **output** parameters group - -This group supports next parameters: - +- `maps` - path to the folder with affinity maps **including prefix**. This is an **optional** `string` parameter. E.g. for the folder with maps `.\maps\1iep_receptor.A.map` and `.\maps\1iep_receptor.C.map` should be provided as `maps\1iep_receptor`. If this parameter is not specified, then `center_x`, `center_y`, `center_z` and `size_x`, `size_y`, `size_x` parameters should be specified to generate affinity maps. Can't be used together with `receptor` parameter. Either `receptor` or `maps` **should be specified** for `vina` and `vinardo` scoring function. This parameter is **required** for `ad4` scoring function. +- `center_x` - X coordinate of the center (Angstrom). This `double` parameter is ignored when `maps` parameter is specified. +- `center_y` - Y coordinate of the center (Angstrom). This `double` parameter is ignored when `maps` parameter is specified. +- `center_z` - Z coordinate of the center (Angstrom). This `double` parameter is ignored when `maps` parameter is specified. +- `size_x` - size in the X dimension (Angstrom). This `double` parameter is ignored when `maps` parameter is specified. +- `size_y` - size in the Y dimension (Angstrom). This `double` parameter is ignored when `maps` parameter is specified. +- `size_z` - size in the Z dimension (Angstrom). This `double` parameter is ignored when `maps` parameter is specified. +- `autobox` - sets maps dimensions based on input ligand(s). This parameter is used when `score_only` or `local_only` parameter is specified. This is an **optional** `boolean` parameter. Default value is `false`. - `out` - path to output model file (PDBQT). This file should not have absolute path. This is an **optional** parameter. -- `dir` - path to output directory when: (1) in batch mode, (2) `input.ligands` parameter is specified and contains more than 1 file and `advanced.score_only` is not specified. This directory should not have absolute path. This is an **optional** `string` parameter. -- `write_maps` - output filename (directory + prefix name) for maps. Parameter `advanced.force_even_voxels` may be needed to comply with map format. This is an **optional** `string` parameter. E.g. for the folder with maps `.\maps\1iep_receptor.A.map` and `.\maps\1iep_receptor.C.map` should be provided as `maps\1iep_receptor`. - -### **advanced** parameters group - -This group supports next parameters: - +- `dir` - path to output directory when: (1) in batch mode, (2) `ligands` parameter is specified and contains more than 1 file and `score_only` is not specified. This directory should not have absolute path. This is an **optional** `string` parameter. +- `write_maps` - output filename (directory + prefix name) for maps. Parameter `force_even_voxels` may be needed to comply with map format. This is an **optional** `string` parameter. E.g. for the folder with maps `.\maps\1iep_receptor.A.map` and `.\maps\1iep_receptor.C.map` should be provided as `maps\1iep_receptor`. - `score_only` - search space can be omitted. This is an **optional** `boolean` parameter. Default value is `false`. - `local_only` - do local search only. This is an **optional** `boolean` parameter. Default value is `false`. -- `no_refine` - when `input.receptor` is provided, do not use explicit receptor atoms (instead of precalculated grids) for: (1) local optimization and scoring after docking, (2) `advanced.local_only` jobs, and (3) `advanced.score_only` jobs. This is an **optional** `boolean` parameter. Default value is `false`. +- `no_refine` - when `receptor` is provided, do not use explicit receptor atoms (instead of precalculated grids) for: (1) local optimization and scoring after docking, (2) `local_only` jobs, and (3) `score_only` jobs. This is an **optional** `boolean` parameter. Default value is `false`. - `force_even_voxels` - calculated grid maps will have an even number of voxels (intervals) in each dimension (odd number of grid points). This is an **optional** `boolean` parameter. Default value is `false`. - `randomize_only` - randomize input, attempting to avoid clashes. This is an **optional** `boolean` parameter. Default value is `false`. - `weight_glue` - macrocycle glue weight. This is an optional `double` parameter. Default value is `50.000000`. +- `seed` - explicit random seed. This is an **optional** `integer` parameter. Default value is `0`. +- `exhaustiveness` - exhaustiveness of the global search (roughly proportional to time): 1+. This is an **optional** `integer` parameter. Default value is `8`. +- `max_evals` - number of evaluations in each MC run (if zero the number of MC steps is based on heuristics). This is an **optional** `integer` parameter. Default value is `0`. +- `num_modes` - maximum number of binding modes to generate. This is an **optional** `integer` parameter. Default value is `9`. +- `min_rmsd` - minimum RMSD between output poses. This is an **optional** `double` parameter. Default value is `1.0`. +- `energy_range` - maximum energy difference between the best binding mode and the worst one displayed (kcal/mol). This is an **optional** `double` parameter. Default value is `3.0`. +- `spacing` - grid spacing (Angstrom). This is an **optional** `double` parameter. Default value is `0.375`. `vina` scoring function specific parameters. @@ -124,18 +93,6 @@ This group supports next parameters: - `weight_ad4_dsolv` - ad4_dsolv weight. This is an **optional** `double` parameter. Default value is `0.1322`. - `weight_ad4_rot` - ad4_rot weight. This is an **optional** `double` parameter. Default value is `0.2983`. -### **misc** parameters group - -This group supports next parameters: - -- `seed` - explicit random seed. This is an **optional** `integer` parameter. Default value is `0`. -- `exhaustiveness` - exhaustiveness of the global search (roughly proportional to time): 1+. This is an **optional** `integer` parameter. Default value is `8`. -- `max_evals` - number of evaluations in each MC run (if zero the number of MC steps is based on heuristics). This is an **optional** `integer` parameter. Default value is `0`. -- `num_modes` - maximum number of binding modes to generate. This is an **optional** `integer` parameter. Default value is `9`. -- `min_rmsd` - minimum RMSD between output poses. This is an **optional** `double` parameter. Default value is `1.0`. -- `energy_range` - maximum energy difference between the best binding mode and the worst one displayed (kcal/mol). This is an **optional** `double` parameter. Default value is `3.0`. -- `spacing` - grid spacing (Angstrom). This is an **optional** `double` parameter. Default value is `0.375`. - More information about specified parameters could be found on the official documentation webpage of Autodock-Vina: https://autodock-vina.readthedocs.io/en/latest/index.html ## Examples of JSON files with already prepared receptors and ligands @@ -144,12 +101,8 @@ More information about specified parameters could be found on the official docum ```json { - "input": { - "receptor": "1iep_receptor.pdbqt", - "ligands": [ - "1iep_ligand.pdbqt" - ] - } + "receptors": "1iep_receptor.pdbqt", + "ligands": "1iep_ligand.pdbqt" } ``` @@ -157,104 +110,42 @@ More information about specified parameters could be found on the official docum ```json { - "input": { - "receptor": "1iep_receptor.pdbqt", - "ligands": [ - "1iep_ligand.pdbqt" - ] - }, - "search_area": { - "center_x": 15.190, - "center_y": 53.903, - "center_z": 16.917, - "size_x": 20.0, - "size_y": 20.0, - "size_z": 20.0 - }, - "misc": { - "exhaustiveness": 4 - }, - "output": { - "out": "1iep_ligand_vina_out.pdbqt" - } + "receptors": "1iep_receptor.pdbqt", + "ligands": "1iep_ligand.pdbqt", + "center_x": 15.190, + "center_y": 53.903, + "center_z": 16.917, + "size_x": 20.0, + "size_y": 20.0, + "size_z": 20.0, + "exhaustiveness": 4, + "out": "1iep_ligand_vina_out.pdbqt" } ``` - -## Example of JSON files that contains information to prepare receptor and ligands - -### Minimal JSON file with one receptor and one ligand - -```json -{ - "prepare_receptors": { - "receptors": [ - "1iep_receptor.pdb" - ] - }, - "prepare_ligands": { - "ligand": "1iep_ligand.sdf" - } -} -``` - -### JSON file with one receptor and one ligand with with search area, exhaustiveness and output specified - -```json -{ - "prepare_receptors": { - "receptors": [ - "1iep_receptor.pdb" - ] - }, - "prepare_ligands": { - "ligand": "1iep_ligand.sdf" - }, - "search_area": { - "center_x": 15.190, - "center_y": 53.903, - "center_z": 16.917, - "size_x": 20.0, - "size_y": 20.0, - "size_z": 20.0 - }, - "misc": { - "exhaustiveness": 4 - }, - "output": { - "out": "1iep_ligand_vina_out.pdbqt" - } -} -``` - -From this file `work unit generator` will generate **one** work unit. - ### JSON file with two receptors and one ligand with with search area and exhaustiveness specified ```json { - "prepare_receptors": { - "receptors": [ - "1iep_receptor.pdb", - "1iep_receptor_copy.pdb" - ] - }, - "prepare_ligands": { - "ligand": "1iep_ligand.sdf" - }, - "search_area": { - "center_x": 15.190, - "center_y": 53.903, - "center_z": 16.917, - "size_x": 20.0, - "size_y": 20.0, - "size_z": 20.0 - }, - "misc": { - "exhaustiveness": 4 - } + "receptors": [ + "1iep_receptor.pdb", + "1iep_receptor_copy.pdb" + ], + "ligands": [ + "1iep_ligand.sdf", + "1iep_ligand_copy.sdf" + ], + "center_x": 15.190, + "center_y": 53.903, + "center_z": 16.917, + "size_x": 20.0, + "size_y": 20.0, + "size_z": 20.0, + "exhaustiveness": 4 } ``` -From this file `work unit generator` will generate **two** work units: -- first work unit with `1iep_receptor.pdb` -- second work unit with `1iep_receptor_copy.pdb` +From this file `work unit generator` will generate **four** work units: +- first work unit with `1iep_receptor.pdb` and `1iep_ligand.sdf` +- second work unit with `1iep_receptor.pdb` and `1iep_ligand_copy.sdf` +- third work unit with `1iep_receptor_copy.pdb` and `1iep_ligand.sdf` +- fourth work unit with `1iep_receptor_copy.pdb` and `1iep_ligand_copy.sdf` diff --git a/boinc-autodock-vina/samples/basic_docking/1iep_vina.json b/boinc-autodock-vina/samples/basic_docking/1iep_vina.json index c982ed5..fa27cb8 100644 --- a/boinc-autodock-vina/samples/basic_docking/1iep_vina.json +++ b/boinc-autodock-vina/samples/basic_docking/1iep_vina.json @@ -1,22 +1,12 @@ { - "input": { - "receptor": "1iep_receptor.pdbqt", - "ligands": [ - "1iep_ligand.pdbqt" - ] - }, - "search_area": { - "center_x": 15.190, - "center_y": 53.903, - "center_z": 16.917, - "size_x": 20.0, - "size_y": 20.0, - "size_z": 20.0 - }, - "misc": { - "exhaustiveness": 4 - }, - "output": { - "out": "1iep_ligand_vina_out.pdbqt" - } + "receptor": "1iep_receptor.pdbqt", + "ligand": "1iep_ligand.pdbqt", + "center_x": 15.190, + "center_y": 53.903, + "center_z": 16.917, + "size_x": 20.0, + "size_y": 20.0, + "size_z": 20.0, + "exhaustiveness": 4, + "out": "1iep_ligand_vina_out.pdbqt" } diff --git a/boinc-autodock-vina/samples/basic_docking_full/1iep_vina.json b/boinc-autodock-vina/samples/basic_docking_full/1iep_vina.json index f96890f..69f489f 100644 --- a/boinc-autodock-vina/samples/basic_docking_full/1iep_vina.json +++ b/boinc-autodock-vina/samples/basic_docking_full/1iep_vina.json @@ -1,24 +1,12 @@ { - "prepare_receptors": { - "receptors": [ - "1iep_receptor.pdb" - ] - }, - "prepare_ligands": { - "ligand": "1iep_ligand.sdf" - }, - "search_area": { - "center_x": 15.190, - "center_y": 53.903, - "center_z": 16.917, - "size_x": 20.0, - "size_y": 20.0, - "size_z": 20.0 - }, - "misc": { - "exhaustiveness": 4 - }, - "output": { - "out": "1iep_ligand_vina_out.pdbqt" - } + "receptor": "1iep_receptor.pdb", + "ligand": "1iep_ligand.sdf", + "center_x": 15.190, + "center_y": 53.903, + "center_z": 16.917, + "size_x": 20.0, + "size_y": 20.0, + "size_z": 20.0, + "exhaustiveness": 4, + "out": "1iep_ligand_vina_out.pdbqt" } diff --git a/boinc-autodock-vina/samples/basic_docking_full_generator_test/1iep_vina.json b/boinc-autodock-vina/samples/basic_docking_full_generator_test/1iep_vina.json index 63055eb..bec9ee1 100644 --- a/boinc-autodock-vina/samples/basic_docking_full_generator_test/1iep_vina.json +++ b/boinc-autodock-vina/samples/basic_docking_full_generator_test/1iep_vina.json @@ -1,25 +1,15 @@ { - "prepare_receptors": { - "receptors": [ - "1iep_receptor.pdb", - "1iep_receptor_copy.pdb" - ] - }, - "prepare_ligands": { - "ligand": "1iep_ligand.sdf" - }, - "search_area": { - "center_x": 15.190, - "center_y": 53.903, - "center_z": 16.917, - "size_x": 20.0, - "size_y": 20.0, - "size_z": 20.0 - }, - "misc": { - "exhaustiveness": 4 - }, - "output": { - "out": "1iep_ligand_vina_out.pdbqt" - } + "receptors": [ + "1iep_receptor.pdb", + "1iep_receptor_copy.pdb" + ], + "ligand": "1iep_ligand.sdf", + "center_x": 15.190, + "center_y": 53.903, + "center_z": 16.917, + "size_x": 20.0, + "size_y": 20.0, + "size_z": 20.0, + "exhaustiveness": 4, + "out": "1iep_ligand_vina_out.pdbqt" } diff --git a/boinc-autodock-vina/samples/basic_docking_full_generator_test/1iep_vina_copy.json b/boinc-autodock-vina/samples/basic_docking_full_generator_test/1iep_vina_copy.json index 63055eb..bec9ee1 100644 --- a/boinc-autodock-vina/samples/basic_docking_full_generator_test/1iep_vina_copy.json +++ b/boinc-autodock-vina/samples/basic_docking_full_generator_test/1iep_vina_copy.json @@ -1,25 +1,15 @@ { - "prepare_receptors": { - "receptors": [ - "1iep_receptor.pdb", - "1iep_receptor_copy.pdb" - ] - }, - "prepare_ligands": { - "ligand": "1iep_ligand.sdf" - }, - "search_area": { - "center_x": 15.190, - "center_y": 53.903, - "center_z": 16.917, - "size_x": 20.0, - "size_y": 20.0, - "size_z": 20.0 - }, - "misc": { - "exhaustiveness": 4 - }, - "output": { - "out": "1iep_ligand_vina_out.pdbqt" - } + "receptors": [ + "1iep_receptor.pdb", + "1iep_receptor_copy.pdb" + ], + "ligand": "1iep_ligand.sdf", + "center_x": 15.190, + "center_y": 53.903, + "center_z": 16.917, + "size_x": 20.0, + "size_y": 20.0, + "size_z": 20.0, + "exhaustiveness": 4, + "out": "1iep_ligand_vina_out.pdbqt" } diff --git a/boinc-autodock-vina/src/boinc-autodock-vina/boinc-autodock-vina.cpp b/boinc-autodock-vina/src/boinc-autodock-vina/boinc-autodock-vina.cpp index 423bec1..051cc11 100644 --- a/boinc-autodock-vina/src/boinc-autodock-vina/boinc-autodock-vina.cpp +++ b/boinc-autodock-vina/src/boinc-autodock-vina/boinc-autodock-vina.cpp @@ -132,7 +132,7 @@ int perform_docking(const std::string& in_zip, const std::string& out_zip) noexc return 1; } - if (!conf.validate()) { + if (!conf.validate(true)) { std::cerr << boinc_msg_prefix(buf, sizeof(buf)) << "Config validation failed, cannot proceed further" << std::endl; boinc_finish(1); return 1; diff --git a/boinc-autodock-vina/src/boinc-autodock-vina/calculate.cpp b/boinc-autodock-vina/src/boinc-autodock-vina/calculate.cpp index 49a6476..994cc60 100644 --- a/boinc-autodock-vina/src/boinc-autodock-vina/calculate.cpp +++ b/boinc-autodock-vina/src/boinc-autodock-vina/calculate.cpp @@ -27,8 +27,8 @@ bool calculator::calculate(const config& config, const int& ncpus, const std::fu config.misc.seed, vina_verbosity, config.advanced.no_refine, const_cast*>(&progress_callback)); - if (!config.input.receptor.empty() || !config.input.flex.empty()) { - vina.set_receptor(config.input.receptor, config.input.flex); + if (!config.input.receptors.empty() || !config.input.flex.empty()) { + vina.set_receptor(config.input.receptors.front(), config.input.flex); } if (config.input.scoring == scoring::vina) { @@ -53,7 +53,7 @@ bool calculator::calculate(const config& config, const int& ncpus, const std::fu } if (!config.input.ligands.empty()) { - vina.set_ligand_from_file(config.input.ligands); + vina.set_ligand_from_file(config.input.ligands.front()); if (config.input.scoring == scoring::vina || config.input.scoring == scoring::vinardo) { if (!config.search_area.maps.empty()) { diff --git a/boinc-autodock-vina/src/common/config.cpp b/boinc-autodock-vina/src/common/config.cpp index ef28a57..14c805f 100644 --- a/boinc-autodock-vina/src/common/config.cpp +++ b/boinc-autodock-vina/src/common/config.cpp @@ -24,13 +24,26 @@ #include "config.h" bool input::load(const jsoncons::basic_json& json, const std::filesystem::path& working_directory) { - if (json.contains("receptor")) { - const auto& value = std::filesystem::path(json["receptor"].as()); - if (value.is_absolute()) { - std::cerr << "Config should not contain absolute paths" << std::endl; - return false; + const std::string receptor_field_name = json.contains("receptors") ? "receptors" : json.contains("receptor") ? "receptor" : ""; + if (!receptor_field_name.empty()) { + if (json[receptor_field_name].is_array()) { + for (const auto& r : json[receptor_field_name].array_range()) { + const auto& value = std::filesystem::path(r.as()); + if (value.is_absolute()) { + std::cerr << "Config should not contain absolute paths" << std::endl; + return false; + } + receptors.emplace_back(std::filesystem::path(working_directory / value).string()); + } + } + else { + const auto& value = std::filesystem::path(json[receptor_field_name].as()); + if (value.is_absolute()) { + std::cerr << "Config should not contain absolute paths" << std::endl; + return false; + } + receptors.emplace_back(std::filesystem::path(working_directory / value).string()); } - receptor = std::filesystem::path(working_directory / value).string(); } if (json.contains("flex")) { const auto& value = std::filesystem::path(json["flex"].as()); @@ -40,9 +53,20 @@ bool input::load(const jsoncons::basic_json& json, const std::filesystem:: } flex = std::filesystem::path(working_directory / value).string(); } - if (json.contains("ligands")) { - for (const auto& l : json["ligands"].array_range()) { - const auto& value = std::filesystem::path(l.as()); + const std::string ligand_field_name = json.contains("ligands") ? "ligands" : json.contains("ligand") ? "ligand" : ""; + if (!ligand_field_name.empty()) { + if (json[ligand_field_name].is_array()) { + for (const auto& r : json[ligand_field_name].array_range()) { + const auto& value = std::filesystem::path(r.as()); + if (value.is_absolute()) { + std::cerr << "Config should not contain absolute paths" << std::endl; + return false; + } + ligands.emplace_back(std::filesystem::path(working_directory / value).string()); + } + } + else { + const auto& value = std::filesystem::path(json[ligand_field_name].as()); if (value.is_absolute()) { std::cerr << "Config should not contain absolute paths" << std::endl; return false; @@ -90,10 +114,28 @@ bool input::save(const json_encoder_helper& json, const std::filesystem::path& w std::cerr << std::endl; }; - if (!receptor.empty()) { - if (!json.value("receptor", filename_from_file(receptor))) { - error_message("receptor"); - return false; + if (!receptors.empty()) { + if (receptors.size() > 1) { + if (!json.begin_array("receptors")) { + error_message("receptors"); + return false; + } + for (const auto& receptor : receptors) { + if (!json.value(filename_from_file(receptor))) { + error_message("receptor"); + return false; + } + } + if (!json.end_array()) { + error_message("receptors"); + return false; + } + } + else { + if (!json.value("receptor", filename_from_file(receptors.front()))) { + error_message("receptor"); + return false; + } } } @@ -105,20 +147,28 @@ bool input::save(const json_encoder_helper& json, const std::filesystem::path& w } if (!ligands.empty()) { - if (!json.begin_array("ligands")) { - error_message("ligands"); - return false; + if (ligands.size() > 1) { + if (!json.begin_array("ligands")) { + error_message("ligands"); + return false; + } + for (const auto& ligand : ligands) { + if (!json.value(filename_from_file(ligand))) { + error_message("ligand"); + return false; + } + } + if (!json.end_array()) { + error_message("ligands"); + return false; + } } - for (const auto& ligand : ligands) { - if (!json.value(filename_from_file(ligand))) { + else { + if (!json.value("ligand", filename_from_file(ligands.front()))) { error_message("ligand"); return false; } } - if (!json.end_array()) { - error_message("ligands"); - return false; - } } if (!batch.empty()) { @@ -558,8 +608,8 @@ bool misc::save(const json_encoder_helper& json, const std::filesystem::path& wo return true; } -bool config::validate() const { - if (!input.receptor.empty() && !search_area.maps.empty()) { +bool config::validate(bool single_pair_allowed) const { + if (!input.receptors.empty() && !search_area.maps.empty()) { std::cerr << "Cannot specify both Input.receptor and SearchArea.maps at the same time,"; std::cerr << "Input.flex parameter is allowed with Input.receptor or SearchArea.maps"; std::cerr << std::endl; @@ -567,14 +617,14 @@ bool config::validate() const { } if (input.scoring == scoring::vina || input.scoring == scoring::vinardo) { - if (input.receptor.empty() && search_area.maps.empty()) { + if (input.receptors.empty() && search_area.maps.empty()) { std::cerr << "The Input.receptor or SearchArea.maps must be specified."; std::cerr << std::endl; return false; } } else if (input.scoring == scoring::ad4) { - if (!input.receptor.empty()) { + if (!input.receptors.empty()) { std::cerr << "No Input.receptor allowed, only Input.flex parameter with the AD4 scoring function."; std::cerr << std::endl; return false; @@ -617,6 +667,13 @@ bool config::validate() const { } } + if (single_pair_allowed && (input.receptors.size() > 1 || input.ligands.size() > 1)) + { + std::cerr << "Only single pair of receptor-ligand is allowed."; + std::cerr << std::endl; + return false; + } + return check_files_exist(); } @@ -663,26 +720,26 @@ bool config::load(const std::istream& config_stream, const std::filesystem::path } bool config::load(const jsoncons::basic_json& json, const std::filesystem::path& working_directory) { - if (json.contains("input") && !input.load(json["input"], working_directory)) { + if (!input.load(json, working_directory)) { return false; } - if (json.contains("search_area") && !search_area.load(json["search_area"], working_directory)) { + if (!search_area.load(json, working_directory)) { return false; } - if (json.contains("output") && !output.load(json["output"], working_directory)) { + if (!output.load(json, working_directory)) { return false; } - if (json.contains("advanced") && !advanced.load(json["advanced"], working_directory)) { + if (!advanced.load(json, working_directory)) { return false; } - if (json.contains("misc") && !misc.load(json["misc"], working_directory)) { + if (!misc.load(json, working_directory)) { return false; } - + if (output.out.empty()) { output.out = std::filesystem::path(working_directory / "result.pdbqt").string(); } @@ -707,70 +764,30 @@ bool config::save(const std::filesystem::path& config_file_path) const { return false; } - if (!json.begin_object("input")) { - error_message("input"); - return false; - } if (!input.save(json, config_file_path.parent_path())) { error_message("input"); return false; } - if (!json.end_object()) { - error_message("input"); - return false; - } - if (!json.begin_object("search_area")) { - error_message("search_area"); - return false; - } if (!search_area.save(json, config_file_path.parent_path())) { error_message("search_area"); return false; } - if (!json.end_object()) { - error_message("search_area"); - return false; - } - if (!json.begin_object("output")) { - error_message("output"); - return false; - } if (!output.save(json, config_file_path.parent_path())) { error_message("output"); return false; } - if (!json.end_object()) { - error_message("output"); - return false; - } - if (!json.begin_object("advanced")) { - error_message("advanced"); - return false; - } if (!advanced.save(json, config_file_path.parent_path())) { error_message("advanced"); return false; } - if (!json.end_object()) { - error_message("advanced"); - return false; - } - if (!json.begin_object("misc")) { - error_message("misc"); - return false; - } if (!misc.save(json, config_file_path.parent_path())) { error_message("misc"); return false; } - if (!json.end_object()) { - error_message("misc"); - return false; - } if (!json.end_object()) { std::cerr << "Failed to write [" << config_file_path.filename().string() << "] file"; @@ -784,8 +801,9 @@ bool config::save(const std::filesystem::path& config_file_path) const { std::vector config::get_files() const { std::vector files; - if (!input.receptor.empty()) { - files.push_back(input.receptor); + if (!input.receptors.empty()) { + for (const auto& receptor : input.receptors) + files.push_back(receptor); } if (!input.ligands.empty()) { diff --git a/boinc-autodock-vina/src/common/config.h b/boinc-autodock-vina/src/common/config.h index 5f0e3a4..2a7c4d4 100644 --- a/boinc-autodock-vina/src/common/config.h +++ b/boinc-autodock-vina/src/common/config.h @@ -46,7 +46,7 @@ class json_save { class input final : public json_load, public json_save { public: - std::string receptor; + std::vector receptors; std::string flex; std::vector ligands; std::vector batch; @@ -132,7 +132,7 @@ class config { advanced advanced; misc misc; - [[nodiscard]] bool validate() const; + [[nodiscard]] bool validate(bool single_pair_allowed = false) const; [[nodiscard]] bool check_files_exist() const; [[nodiscard]] bool load(const std::filesystem::path& config_file_path); [[nodiscard]] bool load(const std::istream& config_stream, const std::filesystem::path& working_directory); diff --git a/boinc-autodock-vina/src/unit-tests/config-tests.cpp b/boinc-autodock-vina/src/unit-tests/config-tests.cpp index 44d6e74..be9462f 100644 --- a/boinc-autodock-vina/src/unit-tests/config-tests.cpp +++ b/boinc-autodock-vina/src/unit-tests/config-tests.cpp @@ -46,7 +46,6 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Receptor) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); #ifdef WIN32 json_encoder.value("receptor", "C:\\test\\receptor_sample"); #else @@ -59,16 +58,11 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Receptor) { json_encoder.begin_array("batch"); json_encoder.value("batch_sample2"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("maps", "maps_sample"); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.value("dir", "dir_sample"); json_encoder.value("write_maps", "write_maps_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -86,7 +80,6 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Flex) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); #ifdef WIN32 json_encoder.value("flex", "C:\\test\\flex_sample"); @@ -99,16 +92,11 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Flex) { json_encoder.begin_array("batch"); json_encoder.value("batch_sample2"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("maps", "maps_sample"); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.value("dir", "dir_sample"); json_encoder.value("write_maps", "write_maps_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -126,7 +114,6 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Ligand) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); json_encoder.value("flex", "flex_sample"); json_encoder.begin_array("ligands"); @@ -139,16 +126,11 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Ligand) { json_encoder.begin_array("batch"); json_encoder.value("batch_sample2"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("maps", "maps_sample"); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.value("dir", "dir_sample"); json_encoder.value("write_maps", "write_maps_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -167,7 +149,6 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Batch) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); json_encoder.value("flex", "flex_sample"); json_encoder.begin_array("ligands"); @@ -180,16 +161,11 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Batch) { json_encoder.value("/home/test/batch_sample"); #endif json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("maps", "maps_sample"); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.value("dir", "dir_sample"); json_encoder.value("write_maps", "write_maps_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -207,7 +183,6 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Maps) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); json_encoder.value("flex", "flex_sample"); json_encoder.begin_array("ligands"); @@ -216,20 +191,15 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Maps) { json_encoder.begin_array("batch"); json_encoder.value("batch_sample2"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); #ifdef WIN32 json_encoder.value("maps", "C:\\test\\maps_sample"); #else json_encoder.value("maps", "/home/test/maps_sample"); #endif - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.value("dir", "dir_sample"); json_encoder.value("write_maps", "write_maps_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -247,7 +217,6 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Out) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); json_encoder.value("flex", "flex_sample"); json_encoder.begin_array("ligands"); @@ -256,11 +225,7 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Out) { json_encoder.begin_array("batch"); json_encoder.value("batch_sample2"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("maps", "maps_sample"); - json_encoder.end_object(); - json_encoder.begin_object("output"); #ifdef WIN32 json_encoder.value("out", "C:\\test\\out_sample"); #else @@ -269,7 +234,6 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Out) { json_encoder.value("dir", "dir_sample"); json_encoder.value("write_maps", "write_maps_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -287,7 +251,6 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Dir) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); json_encoder.value("flex", "flex_sample"); json_encoder.begin_array("ligands"); @@ -296,11 +259,7 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Dir) { json_encoder.begin_array("batch"); json_encoder.value("batch_sample2"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("maps", "maps_sample"); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); #ifdef WIN32 json_encoder.value("dir", "C:\\test\\dir_sample"); @@ -309,7 +268,6 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_Dir) { #endif json_encoder.value("write_maps", "write_maps_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -327,7 +285,6 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_WriteMaps) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); json_encoder.value("flex", "flex_sample"); json_encoder.begin_array("ligands"); @@ -336,11 +293,7 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_WriteMaps) { json_encoder.begin_array("batch"); json_encoder.value("batch_sample2"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("maps", "maps_sample"); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.value("dir", "dir_sample"); #ifdef WIN32 @@ -349,7 +302,6 @@ TEST_F(Config_UnitTests, FailOnAbsolutePathInConfig_WriteMaps) { json_encoder.value("write_maps", "/home/test/write_maps_sample"); #endif json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -367,13 +319,11 @@ TEST_F(Config_UnitTests, CheckForDeaultValueInConfig_Out) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); json_encoder.begin_array("ligands"); json_encoder.value("ligand_sample2"); json_encoder.end_array(); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -393,7 +343,6 @@ TEST_F(Config_UnitTests, LoadValidator) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); json_encoder.value("flex", "flex_sample"); json_encoder.begin_array("ligands"); @@ -405,8 +354,6 @@ TEST_F(Config_UnitTests, LoadValidator) { json_encoder.value("batch_sample2"); json_encoder.end_array(); json_encoder.value("scoring", std::string(magic_enum::enum_name(scoring::vinardo))); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("maps", "maps_sample"); json_encoder.value("center_x", 0.123456); json_encoder.value("center_y", 0.654321); @@ -415,13 +362,9 @@ TEST_F(Config_UnitTests, LoadValidator) { json_encoder.value("size_y", 0.0); json_encoder.value("size_z", -0.000135); json_encoder.value("autobox", true); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.value("dir", "dir_sample"); json_encoder.value("write_maps", "write_maps_sample"); - json_encoder.end_object(); - json_encoder.begin_object("advanced"); json_encoder.value("score_only", true); json_encoder.value("local_only", true); json_encoder.value("no_refine", true); @@ -444,8 +387,6 @@ TEST_F(Config_UnitTests, LoadValidator) { json_encoder.value("weight_ad4_dsolv", -1.064235); json_encoder.value("weight_ad4_rot", 1.064235); json_encoder.value("weight_glue", 1.024653); - json_encoder.end_object(); - json_encoder.begin_object("misc"); json_encoder.value("seed", static_cast(2ull)); json_encoder.value("exhaustiveness", static_cast(3ull)); json_encoder.value("max_evals", static_cast(4ull)); @@ -454,7 +395,6 @@ TEST_F(Config_UnitTests, LoadValidator) { json_encoder.value("energy_range", -2.0); json_encoder.value("spacing", -0.123); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -465,7 +405,8 @@ TEST_F(Config_UnitTests, LoadValidator) { ASSERT_TRUE(res); const auto receptor_sample = std::filesystem::current_path() /= "receptor_sample"; - EXPECT_STREQ(receptor_sample.string().c_str(), config.input.receptor.c_str()); + ASSERT_EQ(1, config.input.receptors.size()); + EXPECT_STREQ(receptor_sample.string().c_str(), config.input.receptors.front().c_str()); const auto flex_sample = std::filesystem::current_path() /= "flex_sample"; EXPECT_STREQ(flex_sample.string().c_str(), config.input.flex.c_str()); ASSERT_EQ(2, config.input.ligands.size()); @@ -535,13 +476,10 @@ TEST_F(Config_UnitTests, FailOn_output_out_NotSpecified) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); json_encoder.begin_array("ligands"); json_encoder.value("ligand_sample1"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("center_x", 0.123456); json_encoder.value("center_y", 0.654321); json_encoder.value("center_z", -0.123456); @@ -549,7 +487,6 @@ TEST_F(Config_UnitTests, FailOn_output_out_NotSpecified) { json_encoder.value("size_y", 0.0); json_encoder.value("size_z", -0.000135); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -572,25 +509,19 @@ TEST_F(Config_UnitTests, CheckThatReceptorAndLigandFilesArePresent) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); json_encoder.begin_array("ligands"); json_encoder.value("ligand_sample1"); json_encoder.value("ligand_sample2"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("center_x", 0.123456); json_encoder.value("center_y", 0.654321); json_encoder.value("center_z", -0.123456); json_encoder.value("size_x", -0.654321); json_encoder.value("size_y", 0.0); json_encoder.value("size_z", -0.000135); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -631,25 +562,19 @@ TEST_F(Config_UnitTests, CheckThatFlexFileIsPresent) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); json_encoder.value("flex", "flex_sample"); json_encoder.begin_array("ligands"); json_encoder.value("ligand_sample1"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("center_x", 0.123456); json_encoder.value("center_y", 0.654321); json_encoder.value("center_z", -0.123456); json_encoder.value("size_x", -0.654321); json_encoder.value("size_y", 0.0); json_encoder.value("size_z", -0.000135); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -680,26 +605,20 @@ TEST_F(Config_UnitTests, CheckThatReceptorAndBatchFilesArePresent) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); json_encoder.begin_array("batch"); json_encoder.value("batch_sample1"); json_encoder.value("batch_sample2"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("center_x", 0.123456); json_encoder.value("center_y", 0.654321); json_encoder.value("center_z", -0.123456); json_encoder.value("size_x", -0.654321); json_encoder.value("size_y", 0.0); json_encoder.value("size_z", -0.000135); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.value("dir", "dir_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -740,12 +659,9 @@ TEST_F(Config_UnitTests, CheckThatMapsFilesArePresent) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.begin_array("ligands"); json_encoder.value("ligand_sample1"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("maps", "receptor_sample"); json_encoder.value("center_x", 0.123456); json_encoder.value("center_y", 0.654321); @@ -753,11 +669,8 @@ TEST_F(Config_UnitTests, CheckThatMapsFilesArePresent) { json_encoder.value("size_x", -0.654321); json_encoder.value("size_y", 0.0); json_encoder.value("size_z", -0.000135); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -820,7 +733,6 @@ TEST_F(Config_UnitTests, TestConfigsEqualAfterReadWrite) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); json_encoder.value("receptor", "receptor_sample"); json_encoder.value("flex", "flex_sample"); json_encoder.begin_array("ligands"); @@ -832,8 +744,6 @@ TEST_F(Config_UnitTests, TestConfigsEqualAfterReadWrite) { json_encoder.value("batch_sample2"); json_encoder.end_array(); json_encoder.value("scoring", std::string(magic_enum::enum_name(scoring::vinardo))); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("maps", "maps_sample"); json_encoder.value("center_x", 0.123456); json_encoder.value("center_y", 0.654321); @@ -842,13 +752,9 @@ TEST_F(Config_UnitTests, TestConfigsEqualAfterReadWrite) { json_encoder.value("size_y", 0.0); json_encoder.value("size_z", -0.000135); json_encoder.value("autobox", true); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.value("dir", "dir_sample"); json_encoder.value("write_maps", "write_maps_sample"); - json_encoder.end_object(); - json_encoder.begin_object("advanced"); json_encoder.value("score_only", true); json_encoder.value("local_only", true); json_encoder.value("no_refine", true); @@ -871,8 +777,6 @@ TEST_F(Config_UnitTests, TestConfigsEqualAfterReadWrite) { json_encoder.value("weight_ad4_dsolv", -1.064235); json_encoder.value("weight_ad4_rot", 1.064235); json_encoder.value("weight_glue", 1.024653); - json_encoder.end_object(); - json_encoder.begin_object("misc"); json_encoder.value("seed", static_cast(2ull)); json_encoder.value("exhaustiveness", static_cast(3ull)); json_encoder.value("max_evals", static_cast(4ull)); @@ -881,7 +785,6 @@ TEST_F(Config_UnitTests, TestConfigsEqualAfterReadWrite) { json_encoder.value("energy_range", -2.0); json_encoder.value("spacing", -0.123); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -896,7 +799,9 @@ TEST_F(Config_UnitTests, TestConfigsEqualAfterReadWrite) { ASSERT_TRUE(config_copy.load(dummy_copy_json_file_path)); - EXPECT_STREQ(config.input.receptor.c_str(), config_copy.input.receptor.c_str()); + ASSERT_EQ(1, config.input.receptors.size()); + ASSERT_EQ(config.input.receptors.size(), config_copy.input.receptors.size()); + EXPECT_STREQ(config.input.receptors.front().c_str(), config_copy.input.receptors.front().c_str()); EXPECT_STREQ(config.input.flex.c_str(), config_copy.input.flex.c_str()); ASSERT_EQ(config.input.ligands.size(), config_copy.input.ligands.size()); EXPECT_STREQ(config.input.ligands[0].c_str(), config_copy.input.ligands[0].c_str()); @@ -1014,12 +919,10 @@ TEST_F(Config_UnitTests, TestGetOutFiles) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.value("dir", "dir_sample"); json_encoder.value("write_maps", "write_maps/sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); diff --git a/boinc-autodock-vina/src/unit-tests/input-config-tests.cpp b/boinc-autodock-vina/src/unit-tests/input-config-tests.cpp index 9a1520b..c090fbf 100644 --- a/boinc-autodock-vina/src/unit-tests/input-config-tests.cpp +++ b/boinc-autodock-vina/src/unit-tests/input-config-tests.cpp @@ -37,31 +37,25 @@ TEST_F(InputConfig_UnitTests, TestThatWorkGeneratorIsAbleToProcessAlreadyPrepare const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); - json_encoder.value("receptor", "receptor_sample"); + json_encoder.value("receptor", "receptor_sample.pdbqt"); json_encoder.begin_array("ligands"); - json_encoder.value("ligand_sample1"); + json_encoder.value("ligand_sample1.pdbqt"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("center_x", 0.123456); json_encoder.value("center_y", 0.654321); json_encoder.value("center_z", -0.123456); json_encoder.value("size_x", -0.654321); json_encoder.value("size_y", 0.0); json_encoder.value("size_z", -0.000135); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); dummy_ofstream dummy; - create_dummy_file(dummy, "receptor_sample"); - create_dummy_file(dummy, "ligand_sample1"); + create_dummy_file(dummy, "receptor_sample.pdbqt"); + create_dummy_file(dummy, "ligand_sample1.pdbqt"); generator generator; @@ -82,9 +76,10 @@ TEST_F(InputConfig_UnitTests, TestThatWorkGeneratorIsAbleToProcessAlreadyPrepare ASSERT_TRUE(conf.load(config_path)); ASSERT_TRUE(conf.validate()); - EXPECT_STREQ((zip_extract_path / "receptor_sample").string().data(), conf.input.receptor.data()); + ASSERT_EQ(1, conf.input.receptors.size()); + EXPECT_STREQ((zip_extract_path / "receptor_sample.pdbqt").string().data(), conf.input.receptors.front().data()); ASSERT_EQ(1, conf.input.ligands.size()); - EXPECT_STREQ((zip_extract_path / "ligand_sample1").string().data(), conf.input.ligands[0].data()); + EXPECT_STREQ((zip_extract_path / "ligand_sample1.pdbqt").string().data(), conf.input.ligands.front().data()); EXPECT_DOUBLE_EQ(0.123456, conf.search_area.center_x); EXPECT_DOUBLE_EQ(0.654321, conf.search_area.center_y); EXPECT_DOUBLE_EQ(-0.123456, conf.search_area.center_z); @@ -107,31 +102,25 @@ TEST_F(InputConfig_UnitTests, TestThatGeneratorAddsRandomSeedIfNotSet) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); - json_encoder.value("receptor", "receptor_sample"); + json_encoder.value("receptors", "receptor_sample.pdbqt"); json_encoder.begin_array("ligands"); - json_encoder.value("ligand_sample1"); + json_encoder.value("ligand_sample1.pdbqt"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("center_x", 0.123456); json_encoder.value("center_y", 0.654321); json_encoder.value("center_z", -0.123456); json_encoder.value("size_x", -0.654321); json_encoder.value("size_y", 0.0); json_encoder.value("size_z", -0.000135); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); dummy_ofstream dummy; - create_dummy_file(dummy, "receptor_sample"); - create_dummy_file(dummy, "ligand_sample1"); + create_dummy_file(dummy, "receptor_sample.pdbqt"); + create_dummy_file(dummy, "ligand_sample1.pdbqt"); generator generator; @@ -152,9 +141,10 @@ TEST_F(InputConfig_UnitTests, TestThatGeneratorAddsRandomSeedIfNotSet) { ASSERT_TRUE(conf.load(config_path)); ASSERT_TRUE(conf.validate()); - EXPECT_STREQ((zip_extract_path / "receptor_sample").string().data(), conf.input.receptor.data()); + ASSERT_EQ(1, conf.input.receptors.size()); + EXPECT_STREQ((zip_extract_path / "receptor_sample.pdbqt").string().data(), conf.input.receptors.front().data()); ASSERT_EQ(1, conf.input.ligands.size()); - EXPECT_STREQ((zip_extract_path / "ligand_sample1").string().data(), conf.input.ligands[0].data()); + EXPECT_STREQ((zip_extract_path / "ligand_sample1.pdbqt").string().data(), conf.input.ligands[0].data()); EXPECT_DOUBLE_EQ(0.123456, conf.search_area.center_x); EXPECT_DOUBLE_EQ(0.654321, conf.search_area.center_y); EXPECT_DOUBLE_EQ(-0.123456, conf.search_area.center_z); @@ -178,34 +168,28 @@ TEST_F(InputConfig_UnitTests, TestThatGeneratorPreservesRandomSeedIfSet) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); - json_encoder.value("receptor", "receptor_sample"); - json_encoder.begin_array("ligands"); - json_encoder.value("ligand_sample1"); + json_encoder.begin_array("receptor"); + json_encoder.value("receptor_sample.pdbqt"); + json_encoder.end_array(); + json_encoder.begin_array("ligand"); + json_encoder.value("ligand_sample1.pdbqt"); json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); json_encoder.value("center_x", 0.123456); json_encoder.value("center_y", 0.654321); json_encoder.value("center_z", -0.123456); json_encoder.value("size_x", -0.654321); json_encoder.value("size_y", 0.0); json_encoder.value("size_z", -0.000135); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); - json_encoder.end_object(); - json_encoder.begin_object("misc"); json_encoder.value("seed", static_cast(1234567890ull)); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); dummy_ofstream dummy; - create_dummy_file(dummy, "receptor_sample"); - create_dummy_file(dummy, "ligand_sample1"); + create_dummy_file(dummy, "receptor_sample.pdbqt"); + create_dummy_file(dummy, "ligand_sample1.pdbqt"); generator generator; @@ -226,9 +210,10 @@ TEST_F(InputConfig_UnitTests, TestThatGeneratorPreservesRandomSeedIfSet) { ASSERT_TRUE(conf.load(config_path)); ASSERT_TRUE(conf.validate()); - EXPECT_STREQ((zip_extract_path / "receptor_sample").string().data(), conf.input.receptor.data()); + ASSERT_EQ(1, conf.input.receptors.size()); + EXPECT_STREQ((zip_extract_path / "receptor_sample.pdbqt").string().data(), conf.input.receptors.front().data()); ASSERT_EQ(1, conf.input.ligands.size()); - EXPECT_STREQ((zip_extract_path / "ligand_sample1").string().data(), conf.input.ligands[0].data()); + EXPECT_STREQ((zip_extract_path / "ligand_sample1.pdbqt").string().data(), conf.input.ligands[0].data()); EXPECT_DOUBLE_EQ(0.123456, conf.search_area.center_x); EXPECT_DOUBLE_EQ(0.654321, conf.search_area.center_y); EXPECT_DOUBLE_EQ(-0.123456, conf.search_area.center_z); @@ -252,31 +237,23 @@ TEST_F(InputConfig_UnitTests, ValidateGetFilesProcessedFunction) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("input"); - json_encoder.value("receptor", "receptor_sample"); - json_encoder.begin_array("ligands"); - json_encoder.value("ligand_sample1"); - json_encoder.end_array(); - json_encoder.end_object(); - json_encoder.begin_object("search_area"); + json_encoder.value("receptors", "receptor_sample.pdbqt"); + json_encoder.value("ligand", "ligand_sample1.pdbqt"); json_encoder.value("center_x", 0.123456); json_encoder.value("center_y", 0.654321); json_encoder.value("center_z", -0.123456); json_encoder.value("size_x", -0.654321); json_encoder.value("size_y", 0.0); json_encoder.value("size_z", -0.000135); - json_encoder.end_object(); - json_encoder.begin_object("output"); json_encoder.value("out", "out_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); dummy_ofstream dummy; - create_dummy_file(dummy, "receptor_sample"); - create_dummy_file(dummy, "ligand_sample1"); + create_dummy_file(dummy, "receptor_sample.pdbqt"); + create_dummy_file(dummy, "ligand_sample1.pdbqt"); generator generator; EXPECT_EQ(0, generator.get_files_processed()); @@ -299,9 +276,10 @@ TEST_F(InputConfig_UnitTests, ValidateGetFilesProcessedFunction) { ASSERT_TRUE(conf.load(config_path)); ASSERT_TRUE(conf.validate()); - EXPECT_STREQ((zip_extract_path / "receptor_sample").string().data(), conf.input.receptor.data()); + ASSERT_EQ(1, conf.input.receptors.size()); + EXPECT_STREQ((zip_extract_path / "receptor_sample.pdbqt").string().data(), conf.input.receptors.front().data()); ASSERT_EQ(1, conf.input.ligands.size()); - EXPECT_STREQ((zip_extract_path / "ligand_sample1").string().data(), conf.input.ligands[0].data()); + EXPECT_STREQ((zip_extract_path / "ligand_sample1.pdbqt").string().data(), conf.input.ligands[0].data()); EXPECT_DOUBLE_EQ(0.123456, conf.search_area.center_x); EXPECT_DOUBLE_EQ(0.654321, conf.search_area.center_y); EXPECT_DOUBLE_EQ(-0.123456, conf.search_area.center_z); @@ -344,7 +322,6 @@ TEST_F(InputConfig_UnitTests, FailOnAbsolutePathInReceptors) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_receptors"); json_encoder.begin_array("receptors"); #ifdef WIN32 json_encoder.value("C:\\test\\receptor_sample"); @@ -353,7 +330,6 @@ TEST_F(InputConfig_UnitTests, FailOnAbsolutePathInReceptors) { #endif json_encoder.end_array(); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -374,7 +350,6 @@ TEST_F(InputConfig_UnitTests, CheckThatReceptorFileIsPresent) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_receptors"); json_encoder.begin_array("receptors"); #ifdef WIN32 json_encoder.value("receptor_sample"); @@ -383,7 +358,6 @@ TEST_F(InputConfig_UnitTests, CheckThatReceptorFileIsPresent) { #endif json_encoder.end_array(); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -403,7 +377,6 @@ TEST_F(InputConfig_UnitTests, ValidatePrepareReceptorsValues) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_receptors"); json_encoder.begin_array("receptors"); json_encoder.value("receptor_sample"); json_encoder.end_array(); @@ -414,7 +387,6 @@ TEST_F(InputConfig_UnitTests, ValidatePrepareReceptorsValues) { json_encoder.value("cleanup", std::string(magic_enum::enum_name(cleanup::nonstdres))); json_encoder.value("delete_nonstd_residue", true); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -432,8 +404,7 @@ TEST_F(InputConfig_UnitTests, ValidatePrepareReceptorsValues) { const auto& working_directory = std::filesystem::current_path(); prepare_receptors prepare_receptors; - ASSERT_TRUE(input_json.contains("prepare_receptors")); - ASSERT_TRUE(prepare_receptors.load(input_json["prepare_receptors"], working_directory)); + ASSERT_TRUE(prepare_receptors.load(input_json, working_directory)); ASSERT_TRUE(prepare_receptors.validate()); ASSERT_EQ(1, prepare_receptors.receptors.size()); @@ -455,10 +426,8 @@ TEST_F(InputConfig_UnitTests, FailOnNoReceptorSpecidiedWhenStructureIsNotEmpty) const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_receptors"); json_encoder.value("repair", std::string(magic_enum::enum_name(repair::bonds_hydrogens))); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -479,14 +448,12 @@ TEST_F(InputConfig_UnitTests, FailOnAbsolutePathInLigand) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_ligands"); #ifdef WIN32 json_encoder.value("ligand", "C:\\test\\ligand_sample"); #else json_encoder.value("ligand", "/home/test/ligand_sample"); #endif json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -507,7 +474,6 @@ TEST_F(InputConfig_UnitTests, FailOnAbsolutePathInSelectedLigands) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_ligands"); json_encoder.value("ligand", "ligand_sample"); json_encoder.begin_array("selected_ligands"); #ifdef WIN32 @@ -517,7 +483,6 @@ TEST_F(InputConfig_UnitTests, FailOnAbsolutePathInSelectedLigands) { #endif json_encoder.end_array(); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -541,7 +506,6 @@ TEST_F(InputConfig_UnitTests, FailOnNoLigandSpecifiedWhenStructureIsNotEmpty) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_ligands"); json_encoder.begin_array("selected_ligands"); #ifdef WIN32 json_encoder.value("ligand_1"); @@ -550,7 +514,6 @@ TEST_F(InputConfig_UnitTests, FailOnNoLigandSpecifiedWhenStructureIsNotEmpty) { #endif json_encoder.end_array(); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -571,10 +534,8 @@ TEST_F(InputConfig_UnitTests, CheckThatLigandFileIsPresent) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_ligands"); json_encoder.value("ligand", "ligand_sample"); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -595,13 +556,11 @@ TEST_F(InputConfig_UnitTests, FailWhenRigidityBondsSmartsPresentedWithoutRigidit const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_ligands"); json_encoder.value("ligand", "ligand_sample"); json_encoder.begin_array("rigidity_bonds_smarts"); json_encoder.value("rbs_sample"); json_encoder.end_array(); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -622,7 +581,6 @@ TEST_F(InputConfig_UnitTests, FailWhenRigidityBondsIndicesPresentedWithoutRigidi const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_ligands"); json_encoder.value("ligand", "ligand_sample"); json_encoder.begin_array("rigidity_bonds_indices"); json_encoder.begin_array(); @@ -631,7 +589,6 @@ TEST_F(InputConfig_UnitTests, FailWhenRigidityBondsIndicesPresentedWithoutRigidi json_encoder.end_array(); json_encoder.end_array(); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -652,7 +609,6 @@ TEST_F(InputConfig_UnitTests, FailWhenRigidityBondsIndicesContainsOnlyOneIndex) const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_ligands"); json_encoder.value("ligand", "ligand_sample"); json_encoder.begin_array("rigidity_bonds_smarts"); json_encoder.value("rbs_sample"); @@ -663,7 +619,6 @@ TEST_F(InputConfig_UnitTests, FailWhenRigidityBondsIndicesContainsOnlyOneIndex) json_encoder.end_array(); json_encoder.end_array(); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -684,7 +639,6 @@ TEST_F(InputConfig_UnitTests, TestThatRigidityBondsSmartsAndRigidityBondsIndices const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_ligands"); json_encoder.value("ligand", "ligand_sample"); json_encoder.begin_array("rigidity_bonds_smarts"); json_encoder.value("rbs_sample"); @@ -700,7 +654,6 @@ TEST_F(InputConfig_UnitTests, TestThatRigidityBondsSmartsAndRigidityBondsIndices json_encoder.end_array(); json_encoder.end_array(); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -721,7 +674,6 @@ TEST_F(InputConfig_UnitTests, FailOnAbsolutePathInMultimolPrefix) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_ligands"); json_encoder.value("ligand", "ligand_sample"); #ifdef WIN32 @@ -730,7 +682,6 @@ TEST_F(InputConfig_UnitTests, FailOnAbsolutePathInMultimolPrefix) { json_encoder.value("multimol_prefix", "/home/test/prefix_sample"); #endif json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -751,7 +702,6 @@ TEST_F(InputConfig_UnitTests, FailOnIllegalSymbolInMultimolPrefix) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_ligands"); json_encoder.value("ligand", "ligand_sample"); #ifdef WIN32 @@ -760,7 +710,6 @@ TEST_F(InputConfig_UnitTests, FailOnIllegalSymbolInMultimolPrefix) { json_encoder.value("multimol_prefix", "prefix/sample"); #endif json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -781,7 +730,6 @@ TEST_F(InputConfig_UnitTests, ValidatePrepareLigandsValues) { const json_encoder_helper json_encoder(jsoncons_encoder); json_encoder.begin_object(); - json_encoder.begin_object("prepare_ligands"); json_encoder.value("ligand", "ligand_sample"); json_encoder.begin_array("selected_ligands"); json_encoder.value("ligand_1"); @@ -807,7 +755,6 @@ TEST_F(InputConfig_UnitTests, ValidatePrepareLigandsValues) { json_encoder.value("remove_index_map", true); json_encoder.value("remove_smiles", true); json_encoder.end_object(); - json_encoder.end_object(); jsoncons_encoder.flush(); json.close(); @@ -826,11 +773,11 @@ TEST_F(InputConfig_UnitTests, ValidatePrepareLigandsValues) { const auto& working_directory = std::filesystem::current_path(); prepare_ligands prepare_ligands; - ASSERT_TRUE(input_json.contains("prepare_ligands")); - ASSERT_TRUE(prepare_ligands.load(input_json["prepare_ligands"], working_directory)); + ASSERT_TRUE(prepare_ligands.load(input_json, working_directory)); ASSERT_TRUE(prepare_ligands.validate()); - EXPECT_STREQ(ligand_sample.string().c_str(), prepare_ligands.ligand.c_str()); + ASSERT_EQ(1, prepare_ligands.ligands.size()); + EXPECT_STREQ(ligand_sample.string().c_str(), prepare_ligands.ligands.front().c_str()); ASSERT_EQ(2, prepare_ligands.selected_ligands.size()); const auto ligand_1 = (std::filesystem::current_path() / "ligand_1").string(); const auto ligand_2 = (std::filesystem::current_path() / "ligand_2").string(); diff --git a/boinc-autodock-vina/src/work-generator/input-config.cpp b/boinc-autodock-vina/src/work-generator/input-config.cpp index d8f2175..fe7cd5a 100644 --- a/boinc-autodock-vina/src/work-generator/input-config.cpp +++ b/boinc-autodock-vina/src/work-generator/input-config.cpp @@ -28,10 +28,30 @@ #include "temp-folder.h" +bool data_validate::compare_extension(const std::filesystem::path& file, const std::string& extension) { + auto file_extension = file.extension().string(); + std::transform(file_extension.cbegin(), file_extension.cend(), file_extension.begin(), [](const auto& c) {return std::tolower(c); }); + std::string lower_extension = extension; + std::transform(extension.cbegin(), extension.cend(), lower_extension.begin(), [](const auto& c) {return std::tolower(c); }); + return file_extension == lower_extension; +} + + bool prepare_receptors::load(const jsoncons::basic_json& json, [[maybe_unused]] const std::filesystem::path& working_directory) { - if (json.contains("receptors")) { - for (const auto& r : json["receptors"].array_range()) { - const auto& value = std::filesystem::path(r.as()); + const std::string receptor_field_name = json.contains("receptors") ? "receptors" : json.contains("receptor") ? "receptor" : ""; + if (!receptor_field_name.empty()) { + if (json[receptor_field_name].is_array()) { + for (const auto& r : json[receptor_field_name].array_range()) { + const auto& value = std::filesystem::path(r.as()); + if (value.is_absolute()) { + std::cerr << "Config should not contain absolute paths" << std::endl; + return false; + } + receptors.emplace_back(std::filesystem::path(working_directory / value).string()); + } + } + else { + const auto& value = std::filesystem::path(json[receptor_field_name].as()); if (value.is_absolute()) { std::cerr << "Config should not contain absolute paths" << std::endl; return false; @@ -115,14 +135,43 @@ bool prepare_receptors::validate() const { return true; } +bool prepare_receptors::is_prepare_needed() const { + for (const auto& r : receptors) { + if (!compare_extension(r, ".pdbqt")) { + return true; + } + } + + return ( + repair != repair::None || + !preserves.empty() || + cleanup != cleanup::none || + delete_nonstd_residue != false + ); +} + + bool prepare_ligands::load(const jsoncons::basic_json& json, const std::filesystem::path& working_directory) { - if (json.contains("ligand")) { - const auto& value = std::filesystem::path(json["ligand"].as()); - if (value.is_absolute()) { - std::cerr << "Config should not contain absolute paths" << std::endl; - return false; + const std::string ligand_field_name = json.contains("ligands") ? "ligands" : json.contains("ligand") ? "ligand" : ""; + if (!ligand_field_name.empty()) { + if (json[ligand_field_name].is_array()) { + for (const auto& r : json[ligand_field_name].array_range()) { + const auto& value = std::filesystem::path(r.as()); + if (value.is_absolute()) { + std::cerr << "Config should not contain absolute paths" << std::endl; + return false; + } + ligands.emplace_back(std::filesystem::path(working_directory / value).string()); + } + } + else { + const auto& value = std::filesystem::path(json[ligand_field_name].as()); + if (value.is_absolute()) { + std::cerr << "Config should not contain absolute paths" << std::endl; + return false; + } + ligands.emplace_back(std::filesystem::path(working_directory / value).string()); } - ligand = std::filesystem::path(working_directory / value).string(); } if (json.contains("selected_ligands")) { for (const auto& l : json["selected_ligands"].array_range()) { @@ -210,22 +259,51 @@ bool prepare_ligands::load(const jsoncons::basic_json& json, const std::fi } bool prepare_ligands::validate() const { - if (ligand.empty()) { + if (ligands.empty()) { std::cerr << "No ligand file specified." << std::endl; return false; } - if (!std::filesystem::exists(ligand) || !std::filesystem::is_regular_file(ligand)) { - std::cerr << "Ligand file <" << std::filesystem::path(ligand).filename().string() << "> is not found." << std::endl; - return false; + for (const auto& l : ligands) { + if (!std::filesystem::exists(l) || !std::filesystem::is_regular_file(l)) { + std::cerr << "Ligand file <" << std::filesystem::path(l).filename().string() << "> is not found." << std::endl; + return false; + } } + if (rigidity_bonds_smarts.size() != rigidity_bonds_indices.size()) { std::cerr << "Count of Rigidity Bonds indices pairs should be equal to Rigidity Bonds SMARTS count." << std::endl; return false; } + return true; } +bool prepare_ligands::is_prepare_needed() const { + for (const auto& l : ligands) { + if (!compare_extension(l, ".pdbqt")) { + return true; + } + } + + return ( + multimol != false || + !multimol_prefix.empty() || + break_macrocycle != false || + hydrate != false || + keep_nonpolar_hydrogens != false || + flex != false || + !rigidity_bonds_smarts.empty() || + !rigidity_bonds_indices.empty() || + flexible_amides != false || + apply_double_bond_penalty != false || + double_bond_penalty != .0 || + remove_index_map != false || + remove_smiles != false + ); +} + + bool generator::save_config(const config& config, const std::filesystem::path& working_directory, const std::filesystem::path& out_path, const std::string& prefix) { const temp_folder temp_path(working_directory); const auto& config_path = temp_path() / "config.json"; @@ -290,12 +368,12 @@ bool generator::process(const std::istream& config_stream, const std::filesystem const auto& json = jsoncons::json::parse(buffer); prepare_ligands prepare_ligands; - if (json.contains("prepare_ligands") && (!prepare_ligands.load(json["prepare_ligands"], working_directory) || !prepare_ligands.validate())) { + if (!prepare_ligands.load(json, working_directory) || !prepare_ligands.validate()) { return false; } prepare_receptors prepare_receptors; - if (json.contains("prepare_receptors") && (!prepare_receptors.load(json["prepare_receptors"], working_directory) || !prepare_receptors.validate())) { + if (!prepare_receptors.load(json, working_directory) || !prepare_receptors.validate()) { return false; } @@ -311,31 +389,41 @@ bool generator::process(const std::istream& config_stream, const std::filesystem config.misc.seed = dist(mt); } - const auto need_prepare_receptors_step = !prepare_receptors.receptors.empty(); - const auto need_prepare_ligand_step = !prepare_ligands.ligand.empty(); - - if (!need_prepare_receptors_step && !need_prepare_ligand_step) { + if (!prepare_receptors.is_prepare_needed() && !prepare_ligands.is_prepare_needed()) { if (config.validate()) { return save_config(config, working_directory, out_path, prefix); } } - if (need_prepare_ligand_step) { + std::vector prepared_receptors; + std::vector prepared_ligands; + + for (const auto& l : prepare_ligands.ligands) { std::stringstream cmd; #ifdef WIN32 + if (!std::filesystem::path(l).has_extension() || std::filesystem::path(l).extension().string() == ".pdbqt") { + continue; + } const auto source_file = "boinc-autodock-vina\\samples\\basic_docking_full\\1iep_ligand.pdbqt.tmp"; const auto target_file = "boinc-autodock-vina\\samples\\basic_docking_full\\1iep_ligand.pdbqt"; cmd << "cmd /c copy " << source_file << " " << target_file; - config.input.ligands.emplace_back((std::filesystem::current_path() / target_file).string()); + prepared_ligands.emplace_back((std::filesystem::current_path() / target_file).string()); #else cmd << "mk_prepare_ligand.py "; - cmd << "-i " << prepare_ligands.ligand << " "; + cmd << "-i " << l << " "; if (!prepare_ligands.multimol || prepare_ligands.multimol_prefix.empty() || prepare_ligands.selected_ligands.empty()) { - std::filesystem::path output(prepare_ligands.ligand); + std::filesystem::path output(l); + + if (data_validate::compare_extension(output, ".pdbqt")) { + prepared_ligands.emplace_back(output.string()); + continue; + } + output.replace_extension("pdbqt"); cmd << "-o " << output.string() << " "; - config.input.ligands.emplace_back(output.string()); + + prepared_ligands.emplace_back(output.string()); } if (prepare_ligands.break_macrocycle) { cmd << "-m "; @@ -383,54 +471,74 @@ bool generator::process(const std::istream& config_stream, const std::filesystem std::cerr << "Failed to prepare ligand(s): <" << std::filesystem::path(cmd.str()).filename().string() << ">" << std::endl; return false; } + } - for (const auto& l : prepare_ligands.selected_ligands) { - config.input.ligands.emplace_back(l); - } + for (const auto& l : prepare_ligands.selected_ligands) { + prepared_ligands.emplace_back(l); } - if (need_prepare_receptors_step) { - //TODO: Could run in parallel - for ([[maybe_unused]] const auto& r : prepare_receptors.receptors) { - std::stringstream cmd; + //TODO: Could run in parallel + for ([[maybe_unused]] const auto& r : prepare_receptors.receptors) { + std::stringstream cmd; #ifdef WIN32 - const auto source_file = "boinc-autodock-vina\\samples\\basic_docking_full\\1iep_receptor.pdbqt.tmp"; - const auto target_file = "boinc-autodock-vina\\samples\\basic_docking_full\\1iep_receptor.pdbqt"; - cmd << "cmd /c copy " << source_file << " " << target_file; - config.input.receptor = (std::filesystem::current_path() / target_file).string(); + if (!std::filesystem::path(r).has_extension() || std::filesystem::path(r).extension().string() == ".pdbqt") { + continue; + } + const auto source_file = "boinc-autodock-vina\\samples\\basic_docking_full\\1iep_receptor.pdbqt.tmp"; + const auto target_file = "boinc-autodock-vina\\samples\\basic_docking_full\\1iep_receptor.pdbqt"; + cmd << "cmd /c copy " << source_file << " " << target_file; + prepared_receptors.emplace_back((std::filesystem::current_path() / target_file).string()); + #else - cmd << "prepare_receptor "; - cmd << "-r " << r << " "; + std::filesystem::path output(r); - std::filesystem::path output(r); - output.replace_extension("pdbqt"); - config.input.receptor = output.string(); - cmd << "-o " << config.input.receptor << " "; + if (data_validate::compare_extension(output, ".pdbqt")) { + prepared_ligands.emplace_back(output.string()); + continue; + } - if (prepare_receptors.repair != repair::None) { - cmd << "- A " << magic_enum::enum_name(prepare_receptors.repair) << " "; - } - for (const auto& p : prepare_receptors.preserves) { - if (p == "all") { - cmd << "-C "; - } - else { - cmd << "-p " << p << " "; - } - } - if (prepare_receptors.cleanup != cleanup::none) { - cmd << "-U " << magic_enum::enum_name(prepare_receptors.cleanup) << " "; + output.replace_extension("pdbqt"); + prepared_receptors.emplace_back(output.string()); + + cmd << "prepare_receptor "; + cmd << "-r " << r << " "; + cmd << "-o " << output.string() << " "; + + if (prepare_receptors.repair != repair::None) { + cmd << "- A " << magic_enum::enum_name(prepare_receptors.repair) << " "; + } + for (const auto& p : prepare_receptors.preserves) { + if (p == "all") { + cmd << "-C "; } - if (prepare_receptors.delete_nonstd_residue) { - cmd << "-e "; + else { + cmd << "-p " << p << " "; } + } + if (prepare_receptors.cleanup != cleanup::none) { + cmd << "-U " << magic_enum::enum_name(prepare_receptors.cleanup) << " "; + } + if (prepare_receptors.delete_nonstd_residue) { + cmd << "-e "; + } #endif - boost::process::child prepare_receptor(cmd.str()); - prepare_receptor.wait(); - if (prepare_receptor.exit_code() != 0) { - std::cerr << "Failed to prepare receptors: <" << std::filesystem::path(cmd.str()).filename().string() << ">" << std::endl; - return false; + boost::process::child prepare_receptor(cmd.str()); + prepare_receptor.wait(); + if (prepare_receptor.exit_code() != 0) { + std::cerr << "Failed to prepare receptors: <" << std::filesystem::path(cmd.str()).filename().string() << ">" << std::endl; + return false; + } + } + + if (!prepared_receptors.empty()) { + for (const auto& receptor : prepared_receptors) { + config.input.receptors.clear(); + config.input.receptors.emplace_back(receptor); + + for (const auto& ligand : prepared_ligands) { + config.input.ligands.clear(); + config.input.ligands.emplace_back(ligand); } if (!config.validate()) { @@ -443,6 +551,31 @@ bool generator::process(const std::istream& config_stream, const std::filesystem } } } + else if (!prepared_ligands.empty()) { + for (const auto& ligand : prepared_ligands) { + config.input.ligands.clear(); + config.input.ligands.emplace_back(ligand); + } + + if (!config.validate()) { + std::cerr << "Failed to validate generated config." << std::endl; + return false; + } + if (!save_config(config, working_directory, out_path, prefix)) { + std::cerr << "Failed to save generated config." << std::endl; + return false; + } + } + else { + if (!config.validate()) { + std::cerr << "Failed to validate generated config." << std::endl; + return false; + } + if (!save_config(config, working_directory, out_path, prefix)) { + std::cerr << "Failed to save generated config." << std::endl; + return false; + } + } return true; } diff --git a/boinc-autodock-vina/src/work-generator/input-config.h b/boinc-autodock-vina/src/work-generator/input-config.h index 5c18210..3bba5b4 100644 --- a/boinc-autodock-vina/src/work-generator/input-config.h +++ b/boinc-autodock-vina/src/work-generator/input-config.h @@ -41,6 +41,8 @@ class data_validate { virtual ~data_validate() = default; [[nodiscard]] virtual bool validate() const = 0; + [[nodiscard]] virtual bool is_prepare_needed() const = 0; + [[nodiscard]] static bool compare_extension(const std::filesystem::path& file, const std::string& extension); }; class prepare_receptors final : public json_load, public data_validate { @@ -53,11 +55,12 @@ class prepare_receptors final : public json_load, public data_validate { [[nodiscard]] bool load(const jsoncons::basic_json& json, const std::filesystem::path& working_directory) override; [[nodiscard]] bool validate() const override; + [[nodiscard]] bool is_prepare_needed() const override; }; class prepare_ligands final : public json_load, public data_validate { public: - std::string ligand; + std::vector ligands; std::vector selected_ligands; bool multimol = false; std::string multimol_prefix; @@ -75,6 +78,7 @@ class prepare_ligands final : public json_load, public data_validate { [[nodiscard]] bool load(const jsoncons::basic_json& json, const std::filesystem::path& working_directory) override; [[nodiscard]] bool validate() const override; + [[nodiscard]] bool is_prepare_needed() const override; }; class generator { @@ -87,4 +91,5 @@ class generator { private: uint64_t current_wu_number = 0; + };