Coverage for biobb_mem/fatslim/fatslim_apl.py: 78%

73 statements  

« prev     ^ index     » next       coverage.py v7.10.1, created at 2025-08-01 02:58 +0000

1#!/usr/bin/env python3 

2 

3"""Module containing the FATSLiM Area per Lipid class and the command line interface.""" 

4import argparse 

5from pathlib import PurePath 

6from biobb_common.generic.biobb_object import BiobbObject 

7from biobb_common.configuration import settings 

8from biobb_common.tools.file_utils import launchlogger 

9from biobb_common.tools import file_utils as fu 

10import MDAnalysis as mda 

11from biobb_mem.fatslim.common import calculate_box 

12import shutil 

13 

14 

15class FatslimAPL(BiobbObject): 

16 """ 

17 | biobb_mem FatslimAPL 

18 | Wrapper of the `FATSLiM area per lipid <https://pythonhosted.org/fatslim/documentation/apl.html>`_ module for area per lipid calculation. 

19 | FATSLiM is designed to provide efficient and robust analysis of physical parameters from MD trajectories, with a focus on processing large trajectory files quickly. 

20 

21 Args: 

22 input_top_path (str): Path to the input topology file. File type: input. `Sample file <https://github.com/bioexcel/biobb_mem/raw/main/biobb_mem/test/data/A01JD/A01JD.pdb>`_. 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). 

23 input_traj_path (str) (Optional): Path to the GROMACS trajectory file. File type: input. `Sample file <https://github.com/bioexcel/biobb_mem/raw/main/biobb_mem/test/data/A01JD/A01JD.xtc>`_. 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). 

24 input_ndx_path (str) (Optional): Path to the input index NDX file for lipid headgroups and the interacting group. File type: input. `Sample file <https://github.com/bioexcel/biobb_mem/raw/main/biobb_mem/test/data/A01JD/headgroups.ndx>`_. Accepted formats: ndx (edam:format_2033). 

25 output_csv_path (str): Path to the output CSV file. File type: output. `Sample file <https://github.com/bioexcel/biobb_mem/raw/main/biobb_mem/test/reference/fatslim/apl.ndx>`_. Accepted formats: csv (edam:format_3752). 

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

27 * **lipid_selection** (*str*) - ("not protein and element P") Headgroups MDAnalysis `selection <https://docs.mdanalysis.org/stable/documentation_pages/selections.html>`_. 

28 * **protein_selection** (*str*) - ("protein and not element H") Protein selection interacting with the membrane. 

29 * **cutoff** (*float*) - (3) This option allows user to specify the cutoff distance (in nm) to be used when performing the neighbor search needed by the APL calculation algorithm 

30 * **limit** (*float*) - (10) This option allows user to specify the upper limit (in nm2) for a valid area per lipid value. 

31 * **begin_frame** (*int*) - (-1) First frame index to be used for analysis. 

32 * **end_frame** (*int*) - (-1) Last frame index to be used for analysis. 

33 * **ignore_no_box** (*bool*) - (False) Ignore the absence of box information in the topology. If the topology does not contain box information, the box will be set to the minimum and maximum positions of the atoms. 

34 * **return_hydrogen** (*bool*) - (False) Include hydrogen atoms in the output index file. 

35 * **binary_path** (*str*) - ("fatslim") Path to the fatslim executable binary. 

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

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

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

39 

40 Examples: 

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

42 

43 from biobb_mem.fatslim.fatslim_apl import fatslim_apl 

44 prop = { 

45 'lipid_selection': '(resname DPPC and name P8)', 

46 'cutoff': 3 

47 } 

48 fatslim_apl(input_top_path='/path/to/myTopology.tpr', 

49 input_traj_path='/path/to/myTrajectory.xtc', 

50 output_csv_path='/path/to/newIndex.ndx', 

51 properties=prop) 

52 

53 Info: 

54 * wrapped_software: 

55 * name: FATSLiM 

56 * version: 0.2.2 

57 * license: GNU 

58 * ontology: 

59 * name: EDAM 

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

61 

62 """ 

63 

64 def __init__(self, input_top_path, output_csv_path, input_traj_path=None, input_ndx_path=None, properties=None, **kwargs) -> None: 

65 properties = properties or {} 

66 

67 # Call parent class constructor 

68 super().__init__(properties) 

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

70 

71 # Input/Output files 

72 self.io_dict = { 

73 "in": {"input_top_path": input_top_path, 

74 "input_traj_path": input_traj_path, 

75 "input_ndx_path": input_ndx_path}, 

76 "out": {"output_csv_path": output_csv_path} 

77 } 

78 

79 # Properties specific for BB 

80 self.lipid_selection = properties.get('lipid_selection', "not protein and element P") 

81 self.protein_selection = properties.get('protein_selection', "protein and not element H") 

82 self.cutoff = properties.get('cutoff', 3) 

83 self.limit = properties.get('cutoff', 10) 

84 self.begin_frame = properties.get('begin_frame', -1) 

85 self.end_frame = properties.get('end_frame', -1) 

86 self.ignore_no_box = properties.get('ignore_no_box', False) 

87 self.binary_path = properties.get('binary_path', 'fatslim') 

88 self.properties = properties 

89 

90 # Check the properties 

91 self.check_properties(properties) 

92 self.check_arguments() 

93 

94 @launchlogger 

95 def launch(self) -> int: 

96 """Execute the :class:`FatslimAPL <fatslim.fatslim_apl.FatslimAPL>` fatslim.fatslim_apl.FatslimAPL object.""" 

97 

98 # Setup Biobb 

99 if self.check_restart(): 

100 return 0 

101 self.stage_files() 

102 

103 # Create index file using MDAnalysis 

104 u = mda.Universe(topology=self.stage_io_dict["in"]["input_top_path"], 

105 coordinates=self.stage_io_dict["in"].get("input_traj_path")) 

106 if u.dimensions is None: 

107 # FATSLiM ValueError: Box does not correspond to PBC=xyz 

108 if self.ignore_no_box: 

109 fu.log('Setting box dimensions using the minimum and maximum positions of the atoms.', 

110 self.out_log, self.global_log) 

111 calculate_box(u) 

112 else: 

113 fu.log('The trajectory does not contain box information. ' 

114 'Please set the ignore_no_box property to True to ignore this error.', 

115 self.out_log, self.global_log) 

116 

117 # Build the index to select the atoms from the membrane 

118 if self.stage_io_dict["in"].get('input_ndx_path', None): 

119 self.tmp_ndx = self.stage_io_dict["in"]["input_ndx_path"] 

120 else: 

121 self.tmp_ndx = str(PurePath(fu.create_unique_dir()).joinpath('apl_inp.ndx')) 

122 with mda.selections.gromacs.SelectionWriter(self.tmp_ndx, mode='w') as ndx: 

123 ndx.write(u.select_atoms(self.lipid_selection), name='headgroups') 

124 ndx.write(u.select_atoms(self.protein_selection), name='protein') 

125 

126 if self.stage_io_dict["in"]["input_top_path"].endswith('gro'): 

127 self.cfg = self.stage_io_dict["in"]["input_top_path"] 

128 self.cmd = [] 

129 else: 

130 # Convert topology .gro and add box dimensions if not available in the topology 

131 self.cfg = str(PurePath(fu.create_unique_dir()).joinpath('output.gro')) 

132 self.tmp_files.extend([PurePath(self.cfg).parent]) 

133 self.cmd = ['gmx', 'editconf', 

134 '-f', self.stage_io_dict["in"]["input_top_path"], 

135 '-o', self.cfg, 

136 '-box', ' '.join(map(str, u.dimensions[:3])), ';', 

137 ] 

138 self.tmp_csv = str(PurePath(self.stage_io_dict["unique_dir"]).joinpath('out.csv')) 

139 # Build command 

140 self.cmd.extend([ 

141 self.binary_path, "apl", 

142 "-n", self.tmp_ndx, 

143 "-c", self.cfg, 

144 "--export-apl-raw", self.tmp_csv, 

145 "--apl-cutoff", str(self.cutoff), 

146 "--apl-limit", str(self.limit), 

147 "--begin-frame", str(self.begin_frame), 

148 "--end-frame", str(self.end_frame) 

149 ]) 

150 

151 # Run Biobb block 

152 self.run_biobb() 

153 shutil.move(self.tmp_csv[:-4]+'_frame_00000.csv', self.stage_io_dict["out"]["output_csv_path"]) 

154 # Copy files to host 

155 self.copy_to_host() 

156 # Remove temporary files 

157 self.tmp_files.extend([ 

158 self.stage_io_dict.get("unique_dir"), 

159 PurePath(self.tmp_ndx).parent 

160 ]) 

161 self.remove_tmp_files() 

162 

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

164 

165 return self.return_code 

166 

167 

168def fatslim_apl(input_top_path: str, output_csv_path: str, input_traj_path: str = None, input_ndx_path: str = None, properties: dict = None, **kwargs) -> int: 

169 """Execute the :class:`FatslimAPL <fatslim.fatslim_apl.FatslimAPL>` class and 

170 execute the :meth:`launch() <fatslim.fatslim_apl.FatslimAPL.launch>` method.""" 

171 

172 return FatslimAPL(input_top_path=input_top_path, 

173 input_traj_path=input_traj_path, 

174 input_ndx_path=input_ndx_path, 

175 output_csv_path=output_csv_path, 

176 properties=properties, **kwargs).launch() 

177 

178 

179def main(): 

180 """Command line execution of this building block. Please check the command line documentation.""" 

181 parser = argparse.ArgumentParser(description="Calculate the area per lipid.", formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, width=99999)) 

182 parser.add_argument('--config', required=False, help='Configuration file') 

183 

184 # Specific args of each building block 

185 required_args = parser.add_argument_group('required arguments') 

186 required_args.add_argument('--input_top_path', required=True, help='Path to the input structure or topology file. Accepted formats: ent, gro, pdb, tpr.') 

187 required_args.add_argument('--output_csv_path', required=True, help='Path to the GROMACS index file. Accepted formats: ndx') 

188 parser.add_argument('--input_traj_path', required=False, help='Path to the input trajectory to be processed. Accepted formats: gro, pdb, tng, trr, xtc.') 

189 

190 args = parser.parse_args() 

191 args.config = args.config or "{}" 

192 properties = settings.ConfReader(config=args.config).get_prop_dic() 

193 

194 # Specific call of each building block 

195 fatslim_apl(input_top_path=args.input_top_path, 

196 output_csv_path=args.output_csv_path, 

197 properties=properties) 

198 

199 

200if __name__ == '__main__': 

201 main()