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()
PDB_SS = None
best_id = '2q7q_75D_140D'
worst_id = '6vxk_801B_806B'
def parse_arguments() -> argparse.Namespace:
 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

def load_disulfide(args: argparse.Namespace) -> proteusPy.DisulfideBase.Disulfide | None:
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

def get_output_filename( args: argparse.Namespace, disulfide: proteusPy.DisulfideBase.Disulfide) -> str | None:
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

def render_disulfide_schematic( disulfide: proteusPy.DisulfideBase.Disulfide, args: argparse.Namespace) -> tuple[matplotlib.figure.Figure, matplotlib.axes._axes.Axes]:
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

def main():
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.