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

75 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-05-28 14:30 +0000

1#!/usr/bin/env python3 

2 

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

4from pathlib import Path, PurePath 

5import sys 

6from biobb_common.generic.biobb_object import BiobbObject 

7from biobb_common.tools.file_utils import launchlogger 

8from biobb_mem.fatslim.common import ignore_no_box, move_output_file 

9import MDAnalysis as mda 

10 

11 

12class FatslimAPL(BiobbObject): 

13 """ 

14 | biobb_mem FatslimAPL 

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

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

17 

18 Args: 

19 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). 

20 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). 

21 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). 

22 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.csv>`_. Accepted formats: csv (edam:format_3752). 

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

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

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

26 * **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 

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

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

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

30 * **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. 

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

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

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

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

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

36 * **container_path** (*str*) - (None) Container path definition. 

37 * **container_image** (*str*) - ('afandiadib/ambertools:serial') Container image definition. 

38 * **container_volume_path** (*str*) - ('/tmp') Container volume path definition. 

39 * **container_working_dir** (*str*) - (None) Container working directory definition. 

40 * **container_user_id** (*str*) - (None) Container user_id definition. 

41 * **container_shell_path** (*str*) - ('/bin/bash') Path to default shell inside the container. 

42 

43 Examples: 

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

45 

46 from biobb_mem.fatslim.fatslim_apl import fatslim_apl 

47 prop = { 

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

49 'cutoff': 3 

50 } 

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

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

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

54 properties=prop) 

55 

56 Info: 

57 * wrapped_software: 

58 * name: FATSLiM 

59 * version: 0.2.2 

60 * license: GNU 

61 * ontology: 

62 * name: EDAM 

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

64 

65 """ 

66 

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

68 properties = properties or {} 

69 

70 # Call parent class constructor 

71 super().__init__(properties) 

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

73 

74 # Input/Output files 

75 self.io_dict = { 

76 "in": {"input_top_path": input_top_path, 

77 "input_traj_path": input_traj_path, 

78 "input_ndx_path": input_ndx_path}, 

79 "out": {"output_csv_path": output_csv_path} 

80 } 

81 

82 # Properties specific for BB 

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

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

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

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

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

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

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

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

91 self.properties = properties 

92 

93 # Check the properties 

94 self.check_properties(properties) 

95 self.check_arguments() 

96 

97 @launchlogger 

98 def launch(self) -> int: 

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

100 

101 # Setup Biobb 

102 if self.check_restart(): 

103 return 0 

104 self.stage_files() 

105 

106 unique_dir = self.stage_io_dict.get("unique_dir", "") 

107 if self.container_path: 

108 working_dir = self.container_volume_path if self.container_volume_path else "/data" 

109 else: 

110 working_dir = unique_dir 

111 

112 host_top_path = str(Path(unique_dir).joinpath(PurePath(self.io_dict["in"]["input_top_path"]).name)) 

113 host_traj_path = None 

114 if self.io_dict["in"].get("input_traj_path"): 

115 host_traj_path = str(Path(unique_dir).joinpath(PurePath(self.io_dict["in"]["input_traj_path"]).name)) 

116 

117 # Create index file using MDAnalysis 

118 u = mda.Universe(topology=host_top_path, coordinates=host_traj_path) 

119 ignore_no_box(u, self.ignore_no_box, self.out_log, self.global_log) 

120 

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

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

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

124 else: 

125 tmp_ndx = str(Path(unique_dir).joinpath('_apl_inp.ndx')) 

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

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

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

129 

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

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

132 

133 else: 

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

135 cfg = str(Path(unique_dir).joinpath('_output.gro')) 

136 # Save as GRO file with box information 

137 u.atoms.write(cfg) 

138 

139 tmp_csv = str(Path(unique_dir).joinpath('_out.csv')) 

140 

141 cmd_prefix = ["cd", working_dir, ";"] 

142 if not self.container_path and PurePath(self.binary_path).name == "fatslim": 

143 # FATSLiM 0.2.2 uses deprecated NumPy aliases removed in modern NumPy versions. 

144 fatslim_compat = Path(unique_dir).joinpath('fatslim_numpy_compat.py') 

145 with open(fatslim_compat, 'w') as compat_script: 

146 compat_script.write('import sys\n') 

147 compat_script.write('import numpy as np\n') 

148 compat_script.write('if not hasattr(np, "int"):\n') 

149 compat_script.write(' np.int = int\n') 

150 compat_script.write('from fatslimlib import main\n') 

151 compat_script.write('sys.exit(main(sys.argv[1:]))\n') 

152 binary_cmd = [sys.executable, PurePath(fatslim_compat).name] 

153 else: 

154 binary_cmd = [self.binary_path] 

155 

156 # Build command 

157 self.cmd = cmd_prefix + binary_cmd + [ 

158 "apl", 

159 "-n", PurePath(tmp_ndx).name, 

160 "-c", PurePath(cfg).name, 

161 "--export-apl-raw", PurePath(tmp_csv).name, 

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

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

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

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

166 ] 

167 

168 # Run Biobb block 

169 self.run_biobb() 

170 staged_output_csv = str(Path(unique_dir).joinpath(PurePath(self.stage_io_dict["out"]["output_csv_path"]).name)) 

171 move_output_file(tmp_csv, staged_output_csv, 

172 self.out_log, self.global_log) 

173 # Copy files to host 

174 self.copy_to_host() 

175 # Remove temporary files 

176 self.remove_tmp_files() 

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

178 

179 return self.return_code 

180 

181 

182def fatslim_apl(input_top_path: str, 

183 output_csv_path: str, 

184 input_traj_path: str = None, 

185 input_ndx_path: str = None, 

186 properties: dict = None, 

187 **kwargs) -> int: 

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

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

190 return FatslimAPL(**dict(locals())).launch() 

191 

192 

193fatslim_apl.__doc__ = FatslimAPL.__doc__ 

194main = FatslimAPL.get_main(fatslim_apl, 'Calculate the area per lipid.') 

195 

196if __name__ == '__main__': 

197 main()