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