proteusPy.render_disulfide_schematic
Stand-alone program for creating 2D schematic diagrams of disulfide bonds.
This program provides a command-line interface to the disulfide_schematic module in the proteusPy package, allowing users to create publication-ready 2D diagrams of disulfide bonds with various customization options.
Author: Eric G. Suchanek, PhD
1#!/usr/bin/env python3 2""" 3Stand-alone program for creating 2D schematic diagrams of disulfide bonds. 4 5This program provides a command-line interface to the disulfide_schematic module 6in the proteusPy package, allowing users to create publication-ready 2D diagrams 7of disulfide bonds with various customization options. 8 9Author: Eric G. Suchanek, PhD 10""" 11 12import argparse 13import os 14import sys 15 16import matplotlib.pyplot as plt 17 18import proteusPy as pp 19from proteusPy.disulfide_schematic import ( 20 create_disulfide_schematic, 21 create_disulfide_schematic_from_model, 22) 23 24# Add the parent directory to the path so we can import proteusPy 25# sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) 26 27 28PDB_SS = None 29best_id = "2q7q_75D_140D" 30worst_id = "6vxk_801B_806B" 31 32 33def parse_arguments() -> argparse.Namespace: 34 """ 35 Parse command-line arguments for the disulfide schematic renderer. 36 37 :return: Parsed command-line arguments 38 :rtype: argparse.Namespace 39 """ 40 parser = argparse.ArgumentParser( 41 description="Create 2D schematic diagrams of disulfide bonds", 42 formatter_class=argparse.ArgumentDefaultsHelpFormatter, 43 ) 44 45 # Input source group (mutually exclusive) 46 input_group = parser.add_mutually_exclusive_group(required=True) 47 input_group.add_argument( 48 "--pdb_id", 49 type=str, 50 help="PDB ID to load disulfides from (will use the first disulfide found)", 51 ) 52 input_group.add_argument( 53 "--ss_id", 54 type=str, 55 default=best_id, 56 help="Specific disulfide ID to render (format: pdbid_resnum1chain_resnum2chain)", 57 ) 58 input_group.add_argument( 59 "--model", 60 action="store_true", 61 help="Create a model disulfide with specified dihedral angles", 62 ) 63 64 # Model disulfide parameters 65 model_group = parser.add_argument_group("Model disulfide parameters") 66 model_group.add_argument( 67 "--chi1", 68 type=float, 69 default=-60.0, 70 help="Chi1 dihedral angle for model disulfide", 71 ) 72 model_group.add_argument( 73 "--chi2", 74 type=float, 75 default=-60.0, 76 help="Chi2 dihedral angle for model disulfide", 77 ) 78 model_group.add_argument( 79 "--chi3", 80 type=float, 81 default=-90.0, 82 help="Chi3 dihedral angle for model disulfide", 83 ) 84 model_group.add_argument( 85 "--chi4", 86 type=float, 87 default=-60.0, 88 help="Chi4 dihedral angle for model disulfide", 89 ) 90 model_group.add_argument( 91 "--chi5", 92 type=float, 93 default=-60.0, 94 help="Chi5 dihedral angle for model disulfide", 95 ) 96 97 # Visualization options 98 viz_group = parser.add_argument_group("Visualization options") 99 viz_group.add_argument( 100 "--style", 101 type=str, 102 choices=["publication", "simple", "detailed"], 103 default="publication", 104 help="Visualization style", 105 ) 106 viz_group.add_argument( 107 "--show_angles", 108 action="store_true", 109 default=True, 110 help="Show dihedral angles in the schematic", 111 ) 112 viz_group.add_argument( 113 "--show_ca_ca_distance", 114 action="store_true", 115 default=True, 116 help="Show Cα-Cα distance in the schematic", 117 ) 118 viz_group.add_argument( 119 "--show_labels", 120 action="store_true", 121 default=True, 122 help="Show atom labels in the schematic", 123 ) 124 viz_group.add_argument( 125 "--hide_title", 126 action="store_true", 127 help="Hide the title in the schematic", 128 ) 129 viz_group.add_argument( 130 "--dpi", 131 type=int, 132 default=300, 133 help="Resolution for raster outputs (DPI)", 134 ) 135 viz_group.add_argument( 136 "--figsize", 137 type=float, 138 nargs=2, 139 default=(8, 6), 140 metavar=("WIDTH", "HEIGHT"), 141 help="Figure size in inches (width height)", 142 ) 143 144 # Output options 145 output_group = parser.add_argument_group("Output options") 146 output_group.add_argument( 147 "--output_file", 148 type=str, 149 help="Path to save the output file (supports .svg, .pdf, .png)", 150 ) 151 output_group.add_argument( 152 "--output_dir", 153 type=str, 154 default="schematic_outputs", 155 help="Directory to save the output file", 156 ) 157 output_group.add_argument( 158 "--display", 159 action="store_true", 160 help="Display the schematic instead of saving to a file", 161 ) 162 163 # Database options 164 db_group = parser.add_argument_group("Database options") 165 db_group.add_argument( 166 "--verbose", 167 action="store_true", 168 help="Show verbose output during database loading", 169 ) 170 171 return parser.parse_args() 172 173 174def load_disulfide( 175 args: argparse.Namespace, 176) -> pp.DisulfideBase.Disulfide | None: 177 """ 178 Load a disulfide based on the provided command-line arguments. 179 180 :param args: Parsed command-line arguments 181 :type args: argparse.Namespace 182 :return: Loaded disulfide object or None if not found 183 :rtype: Optional[pp.DisulfideBase.Disulfide] 184 """ 185 global PDB_SS 186 187 PDB_SS = pp.Load_PDB_SS(verbose=args.verbose, subset=False) 188 189 if args.model: 190 # Create a model disulfide with specified dihedral angles 191 model_ss = pp.DisulfideBase.Disulfide("model") 192 model_ss.build_model(args.chi1, args.chi2, args.chi3, args.chi4, args.chi5) 193 return model_ss 194 195 if args.pdb_id: 196 # Load disulfides from a specific PDB ID 197 pdb_ss = PDB_SS[args.pdb_id] 198 199 if len(pdb_ss) == 0: 200 print(f"No disulfides found for PDB ID: {args.pdb_id}") 201 return None 202 return pdb_ss[0] # Return the first disulfide 203 204 if args.ss_id: 205 return PDB_SS[args.ss_id] 206 207 return None 208 209 210def get_output_filename( 211 args: argparse.Namespace, disulfide: pp.DisulfideBase.Disulfide 212) -> str | None: 213 """ 214 Determine the output filename based on command-line arguments and disulfide properties. 215 216 :param args: Parsed command-line arguments 217 :type args: argparse.Namespace 218 :param disulfide: Disulfide object to render 219 :type disulfide: pp.DisulfideBase.Disulfide 220 :return: Output filename or None if display only 221 :rtype: Optional[str] 222 """ 223 if args.display: 224 return None 225 226 if args.output_file: 227 # Use the specified output file 228 if os.path.isabs(args.output_file): 229 return args.output_file 230 else: 231 return os.path.join(args.output_dir, args.output_file) 232 233 # Create a default filename based on the disulfide properties 234 if disulfide.name == "model": 235 base_name = f"model_chi1_{args.chi1}_chi2_{args.chi2}_chi3_{args.chi3}_chi4_{args.chi4}_chi5_{args.chi5}" 236 else: 237 base_name = f"{disulfide.pdb_id}_{disulfide.proximal}{disulfide.proximal_chain}_{disulfide.distal}{disulfide.distal_chain}" 238 239 filename = f"{base_name}_{args.style}.png" 240 return os.path.join(args.output_dir, filename) 241 242 243def render_disulfide_schematic( 244 disulfide: pp.DisulfideBase.Disulfide, args: argparse.Namespace 245) -> tuple[plt.Figure, plt.Axes]: 246 """ 247 Render a disulfide schematic based on the provided disulfide and arguments. 248 249 :param disulfide: Disulfide object to render 250 :type disulfide: pp.DisulfideBase.Disulfide 251 :param args: Parsed command-line arguments 252 :type args: argparse.Namespace 253 :return: Matplotlib figure and axes objects 254 :rtype: Tuple[plt.Figure, plt.Axes] 255 """ 256 output_file = get_output_filename(args, disulfide) 257 258 # Create output directory if it doesn't exist and we're saving to a file 259 if output_file: 260 os.makedirs(os.path.dirname(output_file), exist_ok=True) 261 262 # Render the schematic 263 if disulfide.name == "model" and args.model: 264 # For model disulfides, use the create_disulfide_schematic_from_model function 265 fig, ax = create_disulfide_schematic_from_model( 266 chi1=args.chi1, 267 chi2=args.chi2, 268 chi3=args.chi3, 269 chi4=args.chi4, 270 chi5=args.chi5, 271 output_file=output_file, 272 show_labels=args.show_labels, 273 show_angles=args.show_angles, 274 show_ca_ca_distance=args.show_ca_ca_distance, 275 style=args.style, 276 dpi=args.dpi, 277 figsize=args.figsize, 278 ) 279 else: 280 # For real disulfides, use the create_disulfide_schematic function 281 # Check if the hide_title argument was provided 282 show_title = not args.hide_title if hasattr(args, "hide_title") else True 283 284 fig, ax = create_disulfide_schematic( 285 disulfide=disulfide, 286 output_file=output_file, 287 show_labels=args.show_labels, 288 show_angles=args.show_angles, 289 show_title=show_title, 290 show_ca_ca_distance=args.show_ca_ca_distance, 291 style=args.style, 292 dpi=args.dpi, 293 figsize=args.figsize, 294 ) 295 296 return fig, ax 297 298 299def main(): 300 """ 301 Main function for the disulfide schematic renderer. 302 303 This function parses command-line arguments, loads the specified disulfide, 304 renders the schematic, and either saves it to a file or displays it. 305 """ 306 # Parse command-line arguments 307 args = parse_arguments() 308 309 # Load the disulfide 310 disulfide = load_disulfide(args) 311 if disulfide is None: 312 sys.exit(1) 313 314 # Print information about the disulfide 315 if disulfide.name == "model": 316 print( 317 f"Rendering model disulfide with angles: " 318 f"χ₁={args.chi1:.1f}°, χ₂={args.chi2:.1f}°, χ₃={args.chi3:.1f}°, " 319 f"χ₄={args.chi4:.1f}°, χ₅={args.chi5:.1f}°" 320 ) 321 else: 322 print( 323 f"Rendering disulfide: {disulfide.pdb_id} " 324 f"{disulfide.proximal}{disulfide.proximal_chain}-" 325 f"{disulfide.distal}{disulfide.distal_chain}" 326 ) 327 print( 328 f"Energy: {disulfide.energy:.2f} kcal/mol, " 329 f"Torsion Length: {disulfide.torsion_length:.2f}°, " 330 f"Cα Distance: {disulfide.ca_distance:.2f} Å" 331 ) 332 333 # Render the schematic 334 fig, ax = render_disulfide_schematic(disulfide, args) 335 336 # Display the schematic if requested 337 if args.display: 338 print("Displaying schematic...") 339 plt.show() 340 else: 341 output_file = get_output_filename(args, disulfide) 342 print(f"Saved schematic to: {output_file}") 343 344 345if __name__ == "__main__": 346 main()
34def parse_arguments() -> argparse.Namespace: 35 """ 36 Parse command-line arguments for the disulfide schematic renderer. 37 38 :return: Parsed command-line arguments 39 :rtype: argparse.Namespace 40 """ 41 parser = argparse.ArgumentParser( 42 description="Create 2D schematic diagrams of disulfide bonds", 43 formatter_class=argparse.ArgumentDefaultsHelpFormatter, 44 ) 45 46 # Input source group (mutually exclusive) 47 input_group = parser.add_mutually_exclusive_group(required=True) 48 input_group.add_argument( 49 "--pdb_id", 50 type=str, 51 help="PDB ID to load disulfides from (will use the first disulfide found)", 52 ) 53 input_group.add_argument( 54 "--ss_id", 55 type=str, 56 default=best_id, 57 help="Specific disulfide ID to render (format: pdbid_resnum1chain_resnum2chain)", 58 ) 59 input_group.add_argument( 60 "--model", 61 action="store_true", 62 help="Create a model disulfide with specified dihedral angles", 63 ) 64 65 # Model disulfide parameters 66 model_group = parser.add_argument_group("Model disulfide parameters") 67 model_group.add_argument( 68 "--chi1", 69 type=float, 70 default=-60.0, 71 help="Chi1 dihedral angle for model disulfide", 72 ) 73 model_group.add_argument( 74 "--chi2", 75 type=float, 76 default=-60.0, 77 help="Chi2 dihedral angle for model disulfide", 78 ) 79 model_group.add_argument( 80 "--chi3", 81 type=float, 82 default=-90.0, 83 help="Chi3 dihedral angle for model disulfide", 84 ) 85 model_group.add_argument( 86 "--chi4", 87 type=float, 88 default=-60.0, 89 help="Chi4 dihedral angle for model disulfide", 90 ) 91 model_group.add_argument( 92 "--chi5", 93 type=float, 94 default=-60.0, 95 help="Chi5 dihedral angle for model disulfide", 96 ) 97 98 # Visualization options 99 viz_group = parser.add_argument_group("Visualization options") 100 viz_group.add_argument( 101 "--style", 102 type=str, 103 choices=["publication", "simple", "detailed"], 104 default="publication", 105 help="Visualization style", 106 ) 107 viz_group.add_argument( 108 "--show_angles", 109 action="store_true", 110 default=True, 111 help="Show dihedral angles in the schematic", 112 ) 113 viz_group.add_argument( 114 "--show_ca_ca_distance", 115 action="store_true", 116 default=True, 117 help="Show Cα-Cα distance in the schematic", 118 ) 119 viz_group.add_argument( 120 "--show_labels", 121 action="store_true", 122 default=True, 123 help="Show atom labels in the schematic", 124 ) 125 viz_group.add_argument( 126 "--hide_title", 127 action="store_true", 128 help="Hide the title in the schematic", 129 ) 130 viz_group.add_argument( 131 "--dpi", 132 type=int, 133 default=300, 134 help="Resolution for raster outputs (DPI)", 135 ) 136 viz_group.add_argument( 137 "--figsize", 138 type=float, 139 nargs=2, 140 default=(8, 6), 141 metavar=("WIDTH", "HEIGHT"), 142 help="Figure size in inches (width height)", 143 ) 144 145 # Output options 146 output_group = parser.add_argument_group("Output options") 147 output_group.add_argument( 148 "--output_file", 149 type=str, 150 help="Path to save the output file (supports .svg, .pdf, .png)", 151 ) 152 output_group.add_argument( 153 "--output_dir", 154 type=str, 155 default="schematic_outputs", 156 help="Directory to save the output file", 157 ) 158 output_group.add_argument( 159 "--display", 160 action="store_true", 161 help="Display the schematic instead of saving to a file", 162 ) 163 164 # Database options 165 db_group = parser.add_argument_group("Database options") 166 db_group.add_argument( 167 "--verbose", 168 action="store_true", 169 help="Show verbose output during database loading", 170 ) 171 172 return parser.parse_args()
Parse command-line arguments for the disulfide schematic renderer.
Returns
Parsed command-line arguments
175def load_disulfide( 176 args: argparse.Namespace, 177) -> pp.DisulfideBase.Disulfide | None: 178 """ 179 Load a disulfide based on the provided command-line arguments. 180 181 :param args: Parsed command-line arguments 182 :type args: argparse.Namespace 183 :return: Loaded disulfide object or None if not found 184 :rtype: Optional[pp.DisulfideBase.Disulfide] 185 """ 186 global PDB_SS 187 188 PDB_SS = pp.Load_PDB_SS(verbose=args.verbose, subset=False) 189 190 if args.model: 191 # Create a model disulfide with specified dihedral angles 192 model_ss = pp.DisulfideBase.Disulfide("model") 193 model_ss.build_model(args.chi1, args.chi2, args.chi3, args.chi4, args.chi5) 194 return model_ss 195 196 if args.pdb_id: 197 # Load disulfides from a specific PDB ID 198 pdb_ss = PDB_SS[args.pdb_id] 199 200 if len(pdb_ss) == 0: 201 print(f"No disulfides found for PDB ID: {args.pdb_id}") 202 return None 203 return pdb_ss[0] # Return the first disulfide 204 205 if args.ss_id: 206 return PDB_SS[args.ss_id] 207 208 return None
Load a disulfide based on the provided command-line arguments.
Parameters
- args: Parsed command-line arguments
Returns
Loaded disulfide object or None if not found
211def get_output_filename( 212 args: argparse.Namespace, disulfide: pp.DisulfideBase.Disulfide 213) -> str | None: 214 """ 215 Determine the output filename based on command-line arguments and disulfide properties. 216 217 :param args: Parsed command-line arguments 218 :type args: argparse.Namespace 219 :param disulfide: Disulfide object to render 220 :type disulfide: pp.DisulfideBase.Disulfide 221 :return: Output filename or None if display only 222 :rtype: Optional[str] 223 """ 224 if args.display: 225 return None 226 227 if args.output_file: 228 # Use the specified output file 229 if os.path.isabs(args.output_file): 230 return args.output_file 231 else: 232 return os.path.join(args.output_dir, args.output_file) 233 234 # Create a default filename based on the disulfide properties 235 if disulfide.name == "model": 236 base_name = f"model_chi1_{args.chi1}_chi2_{args.chi2}_chi3_{args.chi3}_chi4_{args.chi4}_chi5_{args.chi5}" 237 else: 238 base_name = f"{disulfide.pdb_id}_{disulfide.proximal}{disulfide.proximal_chain}_{disulfide.distal}{disulfide.distal_chain}" 239 240 filename = f"{base_name}_{args.style}.png" 241 return os.path.join(args.output_dir, filename)
Determine the output filename based on command-line arguments and disulfide properties.
Parameters
- args: Parsed command-line arguments
- disulfide: Disulfide object to render
Returns
Output filename or None if display only
244def render_disulfide_schematic( 245 disulfide: pp.DisulfideBase.Disulfide, args: argparse.Namespace 246) -> tuple[plt.Figure, plt.Axes]: 247 """ 248 Render a disulfide schematic based on the provided disulfide and arguments. 249 250 :param disulfide: Disulfide object to render 251 :type disulfide: pp.DisulfideBase.Disulfide 252 :param args: Parsed command-line arguments 253 :type args: argparse.Namespace 254 :return: Matplotlib figure and axes objects 255 :rtype: Tuple[plt.Figure, plt.Axes] 256 """ 257 output_file = get_output_filename(args, disulfide) 258 259 # Create output directory if it doesn't exist and we're saving to a file 260 if output_file: 261 os.makedirs(os.path.dirname(output_file), exist_ok=True) 262 263 # Render the schematic 264 if disulfide.name == "model" and args.model: 265 # For model disulfides, use the create_disulfide_schematic_from_model function 266 fig, ax = create_disulfide_schematic_from_model( 267 chi1=args.chi1, 268 chi2=args.chi2, 269 chi3=args.chi3, 270 chi4=args.chi4, 271 chi5=args.chi5, 272 output_file=output_file, 273 show_labels=args.show_labels, 274 show_angles=args.show_angles, 275 show_ca_ca_distance=args.show_ca_ca_distance, 276 style=args.style, 277 dpi=args.dpi, 278 figsize=args.figsize, 279 ) 280 else: 281 # For real disulfides, use the create_disulfide_schematic function 282 # Check if the hide_title argument was provided 283 show_title = not args.hide_title if hasattr(args, "hide_title") else True 284 285 fig, ax = create_disulfide_schematic( 286 disulfide=disulfide, 287 output_file=output_file, 288 show_labels=args.show_labels, 289 show_angles=args.show_angles, 290 show_title=show_title, 291 show_ca_ca_distance=args.show_ca_ca_distance, 292 style=args.style, 293 dpi=args.dpi, 294 figsize=args.figsize, 295 ) 296 297 return fig, ax
Render a disulfide schematic based on the provided disulfide and arguments.
Parameters
- disulfide: Disulfide object to render
- args: Parsed command-line arguments
Returns
Matplotlib figure and axes objects
300def main(): 301 """ 302 Main function for the disulfide schematic renderer. 303 304 This function parses command-line arguments, loads the specified disulfide, 305 renders the schematic, and either saves it to a file or displays it. 306 """ 307 # Parse command-line arguments 308 args = parse_arguments() 309 310 # Load the disulfide 311 disulfide = load_disulfide(args) 312 if disulfide is None: 313 sys.exit(1) 314 315 # Print information about the disulfide 316 if disulfide.name == "model": 317 print( 318 f"Rendering model disulfide with angles: " 319 f"χ₁={args.chi1:.1f}°, χ₂={args.chi2:.1f}°, χ₃={args.chi3:.1f}°, " 320 f"χ₄={args.chi4:.1f}°, χ₅={args.chi5:.1f}°" 321 ) 322 else: 323 print( 324 f"Rendering disulfide: {disulfide.pdb_id} " 325 f"{disulfide.proximal}{disulfide.proximal_chain}-" 326 f"{disulfide.distal}{disulfide.distal_chain}" 327 ) 328 print( 329 f"Energy: {disulfide.energy:.2f} kcal/mol, " 330 f"Torsion Length: {disulfide.torsion_length:.2f}°, " 331 f"Cα Distance: {disulfide.ca_distance:.2f} Å" 332 ) 333 334 # Render the schematic 335 fig, ax = render_disulfide_schematic(disulfide, args) 336 337 # Display the schematic if requested 338 if args.display: 339 print("Displaying schematic...") 340 plt.show() 341 else: 342 output_file = get_output_filename(args, disulfide) 343 print(f"Saved schematic to: {output_file}")
Main function for the disulfide schematic renderer.
This function parses command-line arguments, loads the specified disulfide, renders the schematic, and either saves it to a file or displays it.