Coverage for biobb_model / model / fix_pdb.py: 88%

48 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-22 13:18 +0000

1#!/usr/bin/env python3 

2 

3"""Module containing the FixPdb class and the command line interface.""" 

4 

5from typing import Optional 

6 

7from biobb_common.generic.biobb_object import BiobbObject 

8from biobb_common.tools.file_utils import launchlogger 

9 

10from biobb_model.model.fix_pdb_utils import Structure, generate_map_online 

11 

12 

13class FixPdb(BiobbObject): 

14 """ 

15 | biobb_model FixPdb 

16 | Class to renumerate residues in a PDB structure according to a reference sequence from UniProt. 

17 | Fix the residue numbering in a PDB structure according to a reference sequence from UniProt. 

18 

19 Args: 

20 input_pdb_path (str): Input PDB file path. File type: input. `Sample file <https://github.com/bioexcel/biobb_model/raw/master/biobb_model/test/data/model/2ki5.pdb>`_. Accepted formats: pdb (edam:format_1476). 

21 output_pdb_path (str): Output PDB file path. File type: output. `Sample file <https://github.com/bioexcel/biobb_model/raw/master/biobb_model/test/reference/model/output_pdb_path.pdb>`_. Accepted formats: pdb (edam:format_1476). 

22 properties (dict - Python dictionary object containing the tool parameters, not input/output files): 

23 * **forced_uniprot_references** (*str*) - (None) Set the UniProt accessions for sequences to be used as reference. 

24 * **remove_tmp** (*bool*) - (True) [WF property] Remove temporal files. 

25 * **restart** (*bool*) - (False) [WF property] Do not execute if output files exist. 

26 * **sandbox_path** (*str*) - ("./") [WF property] Parent path to the sandbox directory. 

27 Examples: 

28 This is a use example of how to use the building block from Python:: 

29 from biobb_model.model.fix_pdb import fix_pdb 

30 prop = { 'forced_uniprot_references': ["P00533"] } 

31 fix_pdb(input_pdb_path='/path/to/myStructure.pdb', output_pdb_path='/path/to/newStructure.pdb', properties=prop) 

32 Info: 

33 * wrapped_software: 

34 * name: In house 

35 * license: Apache-2.0 

36 * ontology: 

37 * name: EDAM 

38 * schema: http://edamontology.org/EDAM.owl 

39 """ 

40 

41 def __init__( 

42 self, 

43 input_pdb_path: str, 

44 output_pdb_path: str, 

45 properties: Optional[dict] = None, 

46 **kwargs, 

47 ) -> None: 

48 properties = properties or {} 

49 

50 # Call parent class constructor 

51 super().__init__(properties) 

52 self.locals_var_dict = locals().copy() 

53 

54 # Input/Output files 

55 self.io_dict = { 

56 "in": {"input_pdb_path": input_pdb_path}, 

57 "out": {"output_pdb_path": output_pdb_path}, 

58 } 

59 

60 # Properties specific for BB 

61 self.forced_uniprot_references = properties.get("forced_uniprot_references") 

62 # If input forced uniprot references is a string and not a list then convert it 

63 if isinstance(self.forced_uniprot_references, str): 

64 self.forced_uniprot_references = self.forced_uniprot_references.split(" ") 

65 

66 # Check the properties 

67 self.check_properties(properties) 

68 self.check_arguments() 

69 

70 @launchlogger 

71 def launch(self) -> int: 

72 """Execute the :class:`FixPdb <model.fix_pdb.FixPdb>` object.""" 

73 

74 # Setup Biobb 

75 if self.check_restart(): 

76 return 0 

77 

78 # Run code 

79 self.return_code = 0 

80 

81 # Get the user arguments 

82 input_pdb_path = self.io_dict["in"]["input_pdb_path"] 

83 output_pdb_path = self.io_dict["out"]["output_pdb_path"] 

84 forced_uniprot_references = self.forced_uniprot_references 

85 

86 # Read and parse the input pdb file 

87 structure = Structure.from_pdb_file(input_pdb_path) 

88 

89 # Add protein chains in case they are missing 

90 chains = structure.chains 

91 if len(chains) == 0 or ( 

92 len(chains) == 1 and (chains[0].name == " " or chains[0].name == "X") 

93 ): 

94 structure.raw_protein_chainer() 

95 

96 # Run all the mapping function 

97 # mapping: Optional[dict[Any, Any]] = {} 

98 # if forced_uniprot_references: 

99 mapping = generate_map_online(structure, forced_uniprot_references) # type: ignore 

100 

101 # In case something went wrong with the mapping stop here 

102 if not mapping: 

103 self.return_code = -1 

104 return self.return_code 

105 

106 # Change residue numbers in the structure according to the mapping results 

107 mapped_residue_numbers = mapping["residue_reference_numbers"] 

108 for r, residue in enumerate(structure.residues): 

109 mapped_residue_number = mapped_residue_numbers[r] 

110 if mapped_residue_number is None: 

111 continue 

112 residue.number = mapped_residue_number 

113 

114 # Write the modified structure to a new pdb file 

115 structure.generate_pdb_file(output_pdb_path) 

116 

117 print("Fixed :)") 

118 

119 # Remove temporal files 

120 self.remove_tmp_files() 

121 

122 self.check_arguments(output_files_created=True, raise_exception=False) 

123 return self.return_code 

124 

125 

126def fix_pdb( 

127 input_pdb_path: str, 

128 output_pdb_path: str, 

129 properties: Optional[dict] = None, 

130 **kwargs, 

131) -> int: 

132 """Create :class:`FixPdb <model.fix_pdb.FixPdb>` class and 

133 execute the :meth:`launch() <model.fix_pdb.FixPdb.launch>` method.""" 

134 return FixPdb(**dict(locals())).launch() 

135 

136 

137fix_pdb.__doc__ = FixPdb.__doc__ 

138main = FixPdb.get_main(fix_pdb, "Model the missing atoms in the backbone of a PDB structure.") 

139 

140if __name__ == "__main__": 

141 main()