Coverage for biobb_mem/lipyphilic_biobb/lpp_assign_leaflets.py: 72%
69 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-08 09:07 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-08 09:07 +0000
1#!/usr/bin/env python3
3"""Module containing the Lipyphilic AssignLeaflets class and the command line interface."""
4from biobb_common.generic.biobb_object import BiobbObject
5from biobb_common.tools.file_utils import launchlogger
6import MDAnalysis as mda
7from biobb_mem.lipyphilic_biobb.common import ignore_no_box
8from lipyphilic.lib.assign_leaflets import AssignLeaflets
9import pandas as pd
10import numpy as np
13class LPPAssignLeaflets(BiobbObject):
14 """
15 | biobb_mem LPPAssignLeaflets
16 | Wrapper of the LiPyphilic AssignLeaflets module for assigning lipids to leaflets in a bilayer.
17 | LiPyphilic is a Python package for analyzing MD simulations of lipid bilayers. The parameter names and defaults are the same as the ones in the official `Lipyphilic documentation <https://lipyphilic.readthedocs.io/en/latest/reference/analysis/leaflets.html>`_.
19 Args:
20 input_top_path (str): Path to the input structure or topology file. File type: input. `Sample file <https://github.com/bioexcel/biobb_mem/raw/main/biobb_mem/test/data/A01JD/A01JD.pdb>`_. Accepted formats: crd (edam:3878), gro (edam:2033), mdcrd (edam:3878), mol2 (edam:3816), pdb (edam:1476), pdbqt (edam:1476), prmtop (edam:3881), psf (edam:3882), top (edam:3881), tpr (edam:2333), xml (edam:2332), xyz (edam:3887).
21 input_traj_path (str): Path to the input trajectory to be processed. File type: input. `Sample file <https://github.com/bioexcel/biobb_mem/raw/main/biobb_mem/test/data/A01JD/A01JD.xtc>`_. Accepted formats: arc (edam:2333), crd (edam:3878), dcd (edam:3878), ent (edam:1476), gro (edam:2033), inpcrd (edam:3878), mdcrd (edam:3878), mol2 (edam:3816), nc (edam:3650), pdb (edam:1476), pdbqt (edam:1476), restrt (edam:3886), tng (edam:3876), trr (edam:3910), xtc (edam:3875), xyz (edam:3887).
22 output_leaflets_path (str): Path to the output leaflet assignments. File type: output. `Sample file <https://github.com/bioexcel/biobb_mem/raw/main/biobb_mem/test/reference/lipyphilic_biobb/leaflets_data.csv>`_. Accepted formats: csv (edam:format_3752), npy (edam:format_4003).
23 properties (dic - Python dictionary object containing the tool parameters, not input/output files):
24 * **start** (*int*) - (None) Starting frame for slicing.
25 * **stop** (*int*) - (None) Ending frame for slicing.
26 * **steps** (*int*) - (None) Step for slicing.
27 * **lipid_sel** (*str*) - ("all") Selection string for the lipids in a membrane. The selection should cover **all** residues in the membrane, including cholesterol.
28 * **midplane_sel** (*str*) - (None) Selection string for residues that may be midplane. Any residues not in this selection will be assigned to a leaflet regardless of its proximity to the midplane. The default is `None`, in which case all lipids will be assigned to either the upper or lower leaflet.
29 * **midplane_cutoff** (*float*) - (0) Minimum distance in *z* an atom must be from the midplane to be assigned to a leaflet rather than the midplane. The default is `0`, in which case all lipids will be assigned to either the upper or lower leaflet. Must be non-negative.
30 * **n_bins** (*int*) - (1) Number of bins in *x* and *y* to use to create a grid of membrane patches. Local membrane midpoints are computed for each patch, and lipids assigned a leaflet based on the distance to their local membrane midpoint. The default is `1`, which is equivalent to computing a single global midpoint.
31 * **ignore_no_box** (*bool*) - (False) Ignore the absence of box information in the trajectory. If the trajectory does not contain box information, the box will be set to the minimum and maximum positions of the atoms in the trajectory.
32 * **remove_tmp** (*bool*) - (True) [WF property] Remove temporal files.
33 * **restart** (*bool*) - (False) [WF property] Do not execute if output files exist.
34 * **sandbox_path** (*str*) - ("./") [WF property] Parent path to the sandbox directory.
36 Examples:
37 This is a use example of how to use the building block from Python::
39 from biobb_mem.lipyphilic_biobb.lpp_assign_leaflets import lpp_assign_leaflets
40 prop = {
41 'lipid_sel': 'name GL1 GL2 ROH',
42 }
43 lpp_assign_leaflets(input_top_path='/path/to/myTopology.tpr',
44 input_traj_path='/path/to/myTrajectory.xtc',
45 output_leaflets_path='/path/to/leaflets.csv',
46 properties=prop)
48 Info:
49 * wrapped_software:
50 * name: LiPyphilic
51 * version: 0.10.0
52 * license: GPL-2.0
53 * ontology:
54 * name: EDAM
55 * schema: http://edamontology.org/EDAM.owl
57 """
59 def __init__(self,
60 input_top_path,
61 input_traj_path,
62 output_leaflets_path,
63 properties=None,
64 **kwargs) -> None:
65 properties = properties or {}
67 # Call parent class constructor
68 super().__init__(properties)
69 self.locals_var_dict = locals().copy()
71 # Input/Output files
72 self.io_dict = {
73 "in": {"input_top_path": input_top_path, "input_traj_path": input_traj_path},
74 "out": {"output_leaflets_path": output_leaflets_path}
75 }
76 self.start = properties.get('start', None)
77 self.stop = properties.get('stop', None)
78 self.steps = properties.get('steps', None)
79 self.lipid_sel = properties.get('lipid_sel', 'all')
80 self.midplane_sel = properties.get('midplane_sel', None)
81 self.midplane_cutoff = properties.get('midplane_cutoff', None)
82 self.n_bins = properties.get('n_bins', 1)
83 # Properties specific for BB
84 self.ignore_no_box = properties.get('ignore_no_box', True)
85 self.properties = properties
87 # Check the properties
88 self.check_properties(properties)
89 self.check_arguments()
91 @launchlogger
92 def launch(self) -> int:
93 """Execute the :class:`LPPAssignLeaflets <lipyphilic_biobb.lpp_assign_leaflets.LPPAssignLeaflets>` object."""
95 # Setup Biobb
96 if self.check_restart():
97 return 0
98 self.stage_files()
100 # Load the trajectory
101 u = mda.Universe(self.stage_io_dict["in"]["input_top_path"], self.stage_io_dict["in"]["input_traj_path"])
102 ignore_no_box(u, self.ignore_no_box, self.out_log, self.global_log)
103 # Create AssignLeaflets object
104 leaflets = AssignLeaflets(
105 universe=u,
106 lipid_sel=self.lipid_sel,
107 midplane_sel=self.midplane_sel,
108 midplane_cutoff=self.midplane_cutoff,
109 n_bins=self.n_bins
110 )
111 # Run the analysis
112 leaflets.run(start=self.start, stop=self.stop, step=self.steps)
114 out_format = self.stage_io_dict["out"]["output_leaflets_path"].split('.')[-1]
115 if out_format == 'csv':
116 # Save the results
117 frames = leaflets.leaflets.shape[1]
118 resnames = np.repeat(leaflets.membrane.resnames, frames)
119 resindices = np.tile(leaflets.membrane.resindices, frames)
120 frame_numbers = np.repeat(np.arange(frames), leaflets.membrane.n_residues)
122 df = pd.DataFrame({
123 'resname': resnames,
124 'resindex': resindices,
125 'frame': frame_numbers,
126 'leaflet_index': leaflets.leaflets.T.flatten()
127 })
129 # Save the DataFrame to a CSV file
130 df.to_csv(self.stage_io_dict["out"]["output_leaflets_path"], index=False)
131 elif out_format == 'npy':
132 np.save(self.stage_io_dict["out"]["output_leaflets_path"], leaflets.leaflets)
133 # Copy files to host
134 self.copy_to_host()
135 self.remove_tmp_files()
136 self.check_arguments(output_files_created=True, raise_exception=False)
138 return self.return_code
141def lpp_assign_leaflets(input_top_path: str,
142 input_traj_path: str,
143 output_leaflets_path: str = None,
144 properties: dict = None,
145 **kwargs) -> int:
146 """Execute the :class:`LPPAssignLeaflets <lipyphilic_biobb.lpp_assign_leaflets.LPPAssignLeaflets>` class and
147 execute the :meth:`launch() <lipyphilic_biobb.lpp_assign_leaflets.LPPAssignLeaflets.launch>` method."""
148 return LPPAssignLeaflets(**dict(locals())).launch()
151lpp_assign_leaflets.__doc__ = LPPAssignLeaflets.__doc__
152main = LPPAssignLeaflets.get_main(lpp_assign_leaflets, "Assign lipids to leaflets in a bilayer.")
155def display_nglview(input_top_path: str, output_leaflets_path: str, frame: int = 0):
156 """
157 Visualize the leaflets of a membrane using NGLView.
159 Args:
160 input_top_path (str): Path to the input topology file.
161 output_leaflets_path (str): Path to the CSV file containing leaflet assignments.
162 frame (int, optional): Frame number to visualize. Default is 0.
163 Returns:
164 nglview.NGLWidget: An NGLView widget displaying the membrane leaflets.
165 """
167 try:
168 import nglview as nv
169 except ImportError:
170 raise ImportError('Please install the nglview package to visualize the leaflets.')
171 # Read the leaflets DataFrame
172 df = pd.read_csv(output_leaflets_path)
173 top_idx = df[(df['frame'] == frame) & (df['leaflet_index'] == 1)]['resindex'].values
174 bot_idx = df[(df['frame'] == frame) & (df['leaflet_index'] == -1)]['resindex'].values
175 # Load the topology and convert the resindices to resnums (nglview uses resnums)
176 u = mda.Universe(input_top_path)
177 top_resnum = u.residues[top_idx].resnums
178 bot_resnum = u.residues[bot_idx].resnums
179 # Create the view
180 view = nv.show_file(input_top_path)
181 view.update_ball_and_stick(selection='all', opacity=0.0) # delete membrane
182 view.add_ball_and_stick(selection=", ".join(map(str, top_resnum)), color='blue')
183 view.add_ball_and_stick(selection=", ".join(map(str, bot_resnum)), color='yellow')
184 return view
187if __name__ == '__main__':
188 main()