Source code for gromacs.gmx_check

#!/usr/bin/env python3

"""Module containing the GMX Check class and the command line interface."""

from typing import Optional

from biobb_common.generic.biobb_object import BiobbObject
from biobb_common.tools import file_utils as fu
from biobb_common.tools.file_utils import launchlogger

from biobb_analysis.gromacs.common import (
    check_energy_path,
    check_index_path,
    check_input_path,
    check_out_log_path,
    check_traj_path,
    get_binary_path,
    is_valid_boolean,
    is_valid_float,
)


[docs] class GMXCheck(BiobbObject): """ | biobb_analysis GMXCheck | Wrapper of the GROMACS check module for comparing and validating GROMACS files. | `GROMACS check <http://manual.gromacs.org/current/onlinehelp/gmx-check.html>`_ reads, analyzes and compares run input, trajectory and energy files reporting potential differences and inconsistencies. Args: input_structure_path (str) (Optional): Path to the first GROMACS run input file. File type: input. `Sample file <https://github.com/bioexcel/biobb_analysis/raw/master/biobb_analysis/test/data/gromacs/topology.tpr>`_. Accepted formats: tpr (edam:format_2333), gro (edam:format_2033), g96 (edam:format_2033), pdb (edam:format_1476), brk (edam:format_2033), ent (edam:format_1476). input_structure_2_path (str) (Optional): Path to the second GROMACS run input file. File type: input. `Sample file <https://github.com/bioexcel/biobb_analysis/raw/master/biobb_analysis/test/data/gromacs/topology.tpr>`_. Accepted formats: tpr (edam:format_2333), gro (edam:format_2033), g96 (edam:format_2033), pdb (edam:format_1476), brk (edam:format_2033), ent (edam:format_1476). input_traj_path (str) (Optional): Path to the first GROMACS trajectory file. File type: input. `Sample file <https://github.com/bioexcel/biobb_analysis/raw/master/biobb_analysis/test/data/gromacs/trajectory.trr>`_. Accepted formats: xtc (edam:format_3875), trr (edam:format_3910), cpt (edam:format_2333), gro (edam:format_2033), g96 (edam:format_2033), pdb (edam:format_1476), tng (edam:format_3876). input_traj_2_path (str) (Optional): Path to the second GROMACS trajectory file. File type: input. `Sample file <https://github.com/bioexcel/biobb_analysis/raw/master/biobb_analysis/test/data/gromacs/trajectory.trr>`_. Accepted formats: xtc (edam:format_3875), trr (edam:format_3910), cpt (edam:format_2333), gro (edam:format_2033), g96 (edam:format_2033), pdb (edam:format_1476), tng (edam:format_3876). input_energy_path (str) (Optional): Path to the first GROMACS energy file. File type: input. `Sample file <https://github.com/bioexcel/biobb_analysis/raw/master/biobb_analysis/test/data/gromacs/energy.edr>`_. Accepted formats: edr (edam:format_2330). input_energy_2_path (str) (Optional): Path to the second GROMACS energy file. File type: input. `Sample file <https://github.com/bioexcel/biobb_analysis/raw/master/biobb_analysis/test/data/gromacs/energy.edr>`_. Accepted formats: edr (edam:format_2330). structure_check_path (str) (Optional): Path to the structure file to analyze for internal consistency. File type: input. `Sample file <https://github.com/bioexcel/biobb_analysis/raw/master/biobb_analysis/test/data/gromacs/topology.tpr>`_. Accepted formats: tpr (edam:format_2333), gro (edam:format_2033), g96 (edam:format_2033), pdb (edam:format_1476), brk (edam:format_2033), ent (edam:format_1476). input_index_path (str) (Optional): Path to the GROMACS index file. File type: input. `Sample file <https://github.com/bioexcel/biobb_analysis/raw/master/biobb_analysis/test/data/gromacs/index.ndx>`_. Accepted formats: ndx (edam:format_2033). output_log_path (str): Path to the text file storing the gmx check console output. File type: output. `Sample file <https://github.com/bioexcel/biobb_analysis/raw/master/biobb_analysis/test/reference/gromacs/ref_check.log>`_. Accepted formats: txt (edam:format_2330), log (edam:format_2330), out (edam:format_2330). properties (dic - Python dictionary object containing the tool parameters, not input/output files): * **vdwfac** (*float*) - (0.8) Fraction of the sum of Van der Waals radii used as warning cutoff. * **bonlo** (*float*) - (0.4) Minimum fraction of the sum of Van der Waals radii for bonded atoms. * **bonhi** (*float*) - (0.7) Maximum fraction of the sum of Van der Waals radii for bonded atoms. * **relative_tolerance** (*float*) - (0.001) Relative tolerance for comparing real values. * **absolute_tolerance** (*float*) - (0.001) Absolute tolerance, useful when sums are close to zero. * **rmsd** (*bool*) - (False) Print RMSD for coordinates, velocities and forces. * **compare_ab** (*bool*) - (False) Compare the A and B topologies from a single input file. * **last_energy_term** (*str*) - (None) Last energy term to compare. * **binary_path** (*str*) - ("gmx") Path to the GROMACS executable binary. * **remove_tmp** (*bool*) - (True) [WF property] Remove temporal files. * **restart** (*bool*) - (False) [WF property] Do not execute if output files exist. * **sandbox_path** (*str*) - ("./") [WF property] Parent path to the sandbox directory. * **container_path** (*str*) - (None) Container path definition. * **container_image** (*str*) - ('gromacs/gromacs:2022.2') Container image definition. * **container_volume_path** (*str*) - ('/tmp') Container volume path definition. * **container_working_dir** (*str*) - (None) Container working directory definition. * **container_user_id** (*str*) - (None) Container user_id definition. * **container_shell_path** (*str*) - ('/bin/bash') Path to default shell inside the container. Examples: This is a use example of how to use the building block from Python:: from biobb_analysis.gromacs.gmx_check import gmx_check prop = {} gmx_check( input_structure_path='/path/to/myTopology.tpr', input_structure_2_path='/path/to/myTopologyCopy.tpr', output_log_path='/path/to/check.log', properties=prop ) Info: * wrapped_software: * name: GROMACS check * version: >=2024.5 * license: LGPL 2.1 * ontology: * name: EDAM * schema: http://edamontology.org/EDAM.owl """ def __init__( self, input_structure_path=None, input_structure_2_path=None, input_traj_path=None, input_traj_2_path=None, input_energy_path=None, input_energy_2_path=None, structure_check_path=None, input_index_path=None, output_log_path=None, properties=None, **kwargs, ) -> None: properties = properties or {} # Call parent class constructor super().__init__(properties) self.locals_var_dict = locals().copy() # Input/Output files self.io_dict = { "in": { "input_structure_path": input_structure_path, "input_structure_2_path": input_structure_2_path, "input_traj_path": input_traj_path, "input_traj_2_path": input_traj_2_path, "input_energy_path": input_energy_path, "input_energy_2_path": input_energy_2_path, "structure_check_path": structure_check_path, "input_index_path": input_index_path, }, "out": {"output_log_path": output_log_path}, } # Properties specific for BB self.vdwfac = properties.get("vdwfac") self.bonlo = properties.get("bonlo") self.bonhi = properties.get("bonhi") self.relative_tolerance = properties.get("relative_tolerance") self.absolute_tolerance = properties.get("absolute_tolerance") self.rmsd = properties.get("rmsd", False) self.compare_ab = properties.get("compare_ab", False) self.last_energy_term = properties.get("last_energy_term") self.properties = properties # Properties common in all GROMACS BB self.binary_path = get_binary_path(properties, "binary_path") # Check the properties self.check_init(properties) def _validate_float_property(self, prop_name, default_value=None): """Validates a float property and converts it to string.""" value = self.properties.get(prop_name, default_value) if value is None: return None if not is_valid_float(value): fu.log( f"{self.__class__.__name__}: Incorrect {prop_name} provided, exiting", self.out_log, ) raise SystemExit(f"{self.__class__.__name__}: Incorrect {prop_name} provided") return str(value) def _validate_boolean_property(self, prop_name, default_value=False): """Validates a boolean property.""" value = self.properties.get(prop_name, default_value) if not is_valid_boolean(value): fu.log( f"{self.__class__.__name__}: Incorrect {prop_name} provided, exiting", self.out_log, ) raise SystemExit(f"{self.__class__.__name__}: Incorrect {prop_name} provided") return value
[docs] def check_data_params(self, out_log, err_log): """Checks all the input/output paths and parameters""" input_values = [ self.io_dict["in"].get("input_structure_path"), self.io_dict["in"].get("input_structure_2_path"), self.io_dict["in"].get("input_traj_path"), self.io_dict["in"].get("input_traj_2_path"), self.io_dict["in"].get("input_energy_path"), self.io_dict["in"].get("input_energy_2_path"), self.io_dict["in"].get("structure_check_path"), self.io_dict["in"].get("input_index_path"), ] if not any(input_values): fu.log( f"{self.__class__.__name__}: No input files provided, exiting", out_log, ) raise SystemExit(f"{self.__class__.__name__}: No input files provided") if self.io_dict["in"].get("input_structure_path"): self.io_dict["in"]["input_structure_path"] = check_input_path( self.io_dict["in"]["input_structure_path"], out_log, self.__class__.__name__ ) if self.io_dict["in"].get("input_structure_2_path"): self.io_dict["in"]["input_structure_2_path"] = check_input_path( self.io_dict["in"]["input_structure_2_path"], out_log, self.__class__.__name__ ) if self.io_dict["in"].get("input_traj_path"): self.io_dict["in"]["input_traj_path"] = check_traj_path( self.io_dict["in"]["input_traj_path"], out_log, self.__class__.__name__ ) if self.io_dict["in"].get("input_traj_2_path"): self.io_dict["in"]["input_traj_2_path"] = check_traj_path( self.io_dict["in"]["input_traj_2_path"], out_log, self.__class__.__name__ ) if self.io_dict["in"].get("input_energy_path"): self.io_dict["in"]["input_energy_path"] = check_energy_path( self.io_dict["in"]["input_energy_path"], out_log, self.__class__.__name__ ) if self.io_dict["in"].get("input_energy_2_path"): self.io_dict["in"]["input_energy_2_path"] = check_energy_path( self.io_dict["in"]["input_energy_2_path"], out_log, self.__class__.__name__ ) if self.io_dict["in"].get("structure_check_path"): self.io_dict["in"]["structure_check_path"] = check_input_path( self.io_dict["in"]["structure_check_path"], out_log, self.__class__.__name__ ) if self.io_dict["in"].get("input_index_path"): self.io_dict["in"]["input_index_path"] = check_index_path( self.io_dict["in"]["input_index_path"], out_log, self.__class__.__name__ ) if not self.io_dict["out"].get("output_log_path"): fu.log( f"{self.__class__.__name__}: No output log path provided, exiting", out_log, ) raise SystemExit(f"{self.__class__.__name__}: No output log path provided") self.io_dict["out"]["output_log_path"] = check_out_log_path( self.io_dict["out"]["output_log_path"], out_log, self.__class__.__name__ ) self.vdwfac = self._validate_float_property("vdwfac", self.vdwfac) self.bonlo = self._validate_float_property("bonlo", self.bonlo) self.bonhi = self._validate_float_property("bonhi", self.bonhi) self.relative_tolerance = self._validate_float_property( "relative_tolerance", self.relative_tolerance ) self.absolute_tolerance = self._validate_float_property( "absolute_tolerance", self.absolute_tolerance ) self.rmsd = self._validate_boolean_property("rmsd", self.rmsd) self.compare_ab = self._validate_boolean_property("compare_ab", self.compare_ab)
[docs] @launchlogger def launch(self) -> int: """Execute the :class:`GMXCheck <gromacs.gmx_check.GMXCheck>` object.""" # check input/output paths and parameters self.check_data_params(self.out_log, self.err_log) # Setup Biobb if self.check_restart(): return 0 self.stage_files() self.cmd = [self.binary_path, "check"] if self.stage_io_dict["in"].get("input_traj_path"): self.cmd.extend(["-f", self.stage_io_dict["in"]["input_traj_path"]]) if self.stage_io_dict["in"].get("input_traj_2_path"): self.cmd.extend(["-f2", self.stage_io_dict["in"]["input_traj_2_path"]]) if self.stage_io_dict["in"].get("input_structure_path"): self.cmd.extend(["-s1", self.stage_io_dict["in"]["input_structure_path"]]) if self.stage_io_dict["in"].get("input_structure_2_path"): self.cmd.extend(["-s2", self.stage_io_dict["in"]["input_structure_2_path"]]) if self.stage_io_dict["in"].get("structure_check_path"): self.cmd.extend(["-c", self.stage_io_dict["in"]["structure_check_path"]]) if self.stage_io_dict["in"].get("input_energy_path"): self.cmd.extend(["-e", self.stage_io_dict["in"]["input_energy_path"]]) if self.stage_io_dict["in"].get("input_energy_2_path"): self.cmd.extend(["-e2", self.stage_io_dict["in"]["input_energy_2_path"]]) if self.stage_io_dict["in"].get("input_index_path"): self.cmd.extend(["-n", self.stage_io_dict["in"]["input_index_path"]]) if self.vdwfac is not None: self.cmd.extend(["-vdwfac", self.vdwfac]) if self.bonlo is not None: self.cmd.extend(["-bonlo", self.bonlo]) if self.bonhi is not None: self.cmd.extend(["-bonhi", self.bonhi]) if self.relative_tolerance is not None: self.cmd.extend(["-tol", self.relative_tolerance]) if self.absolute_tolerance is not None: self.cmd.extend(["-abstol", self.absolute_tolerance]) if self.rmsd: self.cmd.append("-rmsd") if self.compare_ab: self.cmd.append("-ab") if self.last_energy_term: self.cmd.extend(["-lastener", self.last_energy_term]) self.cmd.extend( [">", self.stage_io_dict["out"]["output_log_path"]], ) # Run Biobb block self.run_biobb() # Copy files to host self.copy_to_host() self.remove_tmp_files() self.check_arguments(output_files_created=True, raise_exception=False) return self.return_code
[docs] def gmx_check( input_structure_path: Optional[str] = None, input_structure_2_path: Optional[str] = None, input_traj_path: Optional[str] = None, input_traj_2_path: Optional[str] = None, input_energy_path: Optional[str] = None, input_energy_2_path: Optional[str] = None, structure_check_path: Optional[str] = None, input_index_path: Optional[str] = None, output_log_path: Optional[str] = None, properties: Optional[dict] = None, **kwargs, ) -> int: """Create the :class:`GMXCheck <gromacs.gmx_check.GMXCheck>` class and execute the :meth:`launch() <gromacs.gmx_check.GMXCheck.launch>` method.""" return GMXCheck(**dict(locals())).launch()
gmx_check.__doc__ = GMXCheck.__doc__ main = GMXCheck.get_main( gmx_check, "Checks and compares GROMACS topology, trajectory or energy files.", ) if __name__ == "__main__": main()