|
542 | 542 | "metadata": {}, |
543 | 543 | "outputs": [], |
544 | 544 | "source": [ |
| 545 | + "import math as _math\n", |
| 546 | + "\n", |
545 | 547 | "from matplotlib.patches import Circle, Ellipse\n", |
546 | 548 | "\n", |
547 | | - "# Elongated ligand: long axis along x.\n", |
548 | | - "R_long = 6.0 # tip-of-blob distance from COM (R_max)\n", |
549 | | - "R_short = 2.0 # short-axis blob distance from COM (R_min)\n", |
550 | | - "buffer = 1.0 # smaller than real 5 Å for visual clarity\n", |
551 | | - "reach = 2.0\n", |
| 549 | + "# Elongated ligand. Half-axes large enough that R_min is a meaningful\n", |
| 550 | + "# fraction of R_max (~ a 3:1 cigar) — typical of an elongated protein.\n", |
| 551 | + "R_long = 15.0 # x half-axis = R_max (long-axis surface distance)\n", |
| 552 | + "R_short = 5.0 # y half-axis = R_min (short-axis surface distance)\n", |
| 553 | + "buffer = 4.0 # close to the real 5 Å SHELL_INNER_BUFFER\n", |
| 554 | + "reach = 4.0 # smaller than real 10 Å for a tight figure\n", |
552 | 555 | "\n", |
553 | | - "inner = max(0.0, R_short - buffer)\n", |
554 | | - "outer = R_long + reach\n", |
| 556 | + "inner = max(0.0, R_short - buffer) # = 1.0\n", |
| 557 | + "outer = R_long + reach # = 19.0\n", |
555 | 558 | "\n", |
556 | | - "fig, ax = plt.subplots(figsize=(10, 6))\n", |
| 559 | + "fig, ax = plt.subplots(figsize=(12, 7))\n", |
557 | 560 | "\n", |
558 | | - "# Outside (not sampled) — light blue background\n", |
559 | | - "ax.add_patch(Circle((0, 0), 12, facecolor=\"#e8f4ff\", edgecolor=\"none\", zorder=0))\n", |
560 | | - "# Shell band (sampled, SAS decides) — light green\n", |
561 | | - "ax.add_patch(\n", |
562 | | - " Circle((0, 0), outer, facecolor=\"#d8f5d8\", edgecolor=\"green\", lw=2, zorder=1)\n", |
563 | | - ")\n", |
564 | | - "# Inner zone (guaranteed bulk, not sampled) — light gray\n", |
565 | | - "ax.add_patch(\n", |
566 | | - " Circle(\n", |
567 | | - " (0, 0), inner, facecolor=\"#cccccc\", edgecolor=\"orange\", lw=2, ls=\"--\", zorder=2\n", |
568 | | - " )\n", |
569 | | - ")\n", |
570 | | - "# Ligand blob: elongated ellipse along x\n", |
| 561 | + "# Background tiers (drawn back-to-front).\n", |
| 562 | + "# Outside the envelope — solvent we don't sample.\n", |
| 563 | + "ax.add_patch(Circle((0, 0), 24, facecolor=\"#e8f4ff\", edgecolor=\"none\"))\n", |
| 564 | + "# Shell band [inner, outer] — sampling region.\n", |
| 565 | + "ax.add_patch(Circle((0, 0), outer, facecolor=\"#d8f5d8\", edgecolor=\"green\", lw=2.5))\n", |
| 566 | + "# Ligand bulk (will be SAS-rejected within the shell band).\n", |
571 | 567 | "ax.add_patch(\n", |
572 | 568 | " Ellipse(\n", |
573 | 569 | " (0, 0),\n", |
574 | 570 | " 2 * R_long,\n", |
575 | 571 | " 2 * R_short,\n", |
576 | | - " facecolor=\"dimgray\",\n", |
577 | | - " edgecolor=\"black\",\n", |
| 572 | + " facecolor=\"#c98a8a\",\n", |
| 573 | + " edgecolor=\"darkred\",\n", |
578 | 574 | " lw=1.5,\n", |
579 | | - " alpha=0.85,\n", |
580 | | - " zorder=3,\n", |
| 575 | + " alpha=0.9,\n", |
581 | 576 | " )\n", |
582 | 577 | ")\n", |
583 | | - "# COM\n", |
584 | | - "ax.plot(0, 0, marker=\"*\", color=\"magenta\", markersize=18, zorder=4)\n", |
585 | | - "\n", |
586 | | - "# Direction arrows showing where the surface is\n", |
587 | | - "# Short-axis surface point (top)\n", |
588 | | - "ax.plot(0, R_short, marker=\"o\", color=\"red\", markersize=8, zorder=5)\n", |
589 | | - "ax.annotate(\n", |
590 | | - " \"surface point on short axis\\n(distance = R_min)\",\n", |
591 | | - " xy=(0, R_short),\n", |
592 | | - " xytext=(3, 4.5),\n", |
593 | | - " arrowprops={\"arrowstyle\": \"->\", \"color\": \"red\", \"lw\": 1},\n", |
594 | | - " fontsize=9,\n", |
595 | | - " color=\"red\",\n", |
596 | | - ")\n", |
597 | | - "# Long-axis surface point (right)\n", |
598 | | - "ax.plot(R_long, 0, marker=\"o\", color=\"red\", markersize=8, zorder=5)\n", |
599 | | - "ax.annotate(\n", |
600 | | - " \"surface point on long axis\\n(distance = R_max)\",\n", |
601 | | - " xy=(R_long, 0),\n", |
602 | | - " xytext=(R_long + 1.5, -3),\n", |
603 | | - " arrowprops={\"arrowstyle\": \"->\", \"color\": \"red\", \"lw\": 1},\n", |
604 | | - " fontsize=9,\n", |
605 | | - " color=\"red\",\n", |
| 578 | + "# Inner zone — preemptively skipped by the shell envelope.\n", |
| 579 | + "ax.add_patch(\n", |
| 580 | + " Circle((0, 0), inner, facecolor=\"#5a5a5a\", edgecolor=\"orange\", lw=2, ls=\"--\")\n", |
606 | 581 | ")\n", |
| 582 | + "# COM marker.\n", |
| 583 | + "ax.plot(0, 0, marker=\"*\", color=\"magenta\", markersize=18, zorder=10)\n", |
| 584 | + "\n", |
| 585 | + "\n", |
| 586 | + "# --- Surface points at different directions on the ligand boundary ---\n", |
| 587 | + "# Top: short axis. Right tip: long axis. Diagonal: in between.\n", |
| 588 | + "def ellipse_point(theta):\n", |
| 589 | + " return (R_long * _math.cos(theta), R_short * _math.sin(theta))\n", |
607 | 590 | "\n", |
608 | | - "# Annotations for the three regions\n", |
| 591 | + "\n", |
| 592 | + "surface_pts = [\n", |
| 593 | + " (ellipse_point(_math.pi / 2), \"short-axis surface\\n|r| = R_min\", (5, 9)),\n", |
| 594 | + " (ellipse_point(0), \"long-axis surface\\n|r| = R_max\", (R_long + 2, 4.5)),\n", |
| 595 | + " (ellipse_point(_math.pi / 4), \"diagonal surface\\n|r| ≈ 9.5\", (R_long + 2, -3)),\n", |
| 596 | + "]\n", |
| 597 | + "for (px, py), label, (lx, ly) in surface_pts:\n", |
| 598 | + " ax.plot(\n", |
| 599 | + " px,\n", |
| 600 | + " py,\n", |
| 601 | + " marker=\"o\",\n", |
| 602 | + " color=\"darkgreen\",\n", |
| 603 | + " markersize=10,\n", |
| 604 | + " zorder=11,\n", |
| 605 | + " markeredgecolor=\"white\",\n", |
| 606 | + " markeredgewidth=1.5,\n", |
| 607 | + " )\n", |
| 608 | + " ax.annotate(\n", |
| 609 | + " label,\n", |
| 610 | + " xy=(px, py),\n", |
| 611 | + " xytext=(lx, ly),\n", |
| 612 | + " fontsize=10,\n", |
| 613 | + " color=\"darkgreen\",\n", |
| 614 | + " arrowprops={\"arrowstyle\": \"->\", \"color\": \"darkgreen\", \"lw\": 1.2},\n", |
| 615 | + " )\n", |
| 616 | + "\n", |
| 617 | + "# --- Region legend boxes ---\n", |
609 | 618 | "ax.text(\n", |
610 | 619 | " 0,\n", |
611 | | - " -1.2,\n", |
612 | | - " \"guaranteed bulk\\n(< R_min − buffer)\\n← skipped →\",\n", |
| 620 | + " -10,\n", |
| 621 | + " \"shell band — sampled\\n(SAS rejects ligand bulk inside,\\n accepts surface layer)\",\n", |
613 | 622 | " ha=\"center\",\n", |
614 | | - " va=\"top\",\n", |
615 | | - " fontsize=9,\n", |
616 | | - " color=\"black\",\n", |
617 | | - " bbox={\"facecolor\": \"white\", \"edgecolor\": \"orange\", \"boxstyle\": \"round\"},\n", |
| 623 | + " fontsize=10,\n", |
| 624 | + " color=\"darkgreen\",\n", |
| 625 | + " bbox={\"facecolor\": \"white\", \"edgecolor\": \"darkgreen\", \"boxstyle\": \"round,pad=0.4\"},\n", |
618 | 626 | ")\n", |
619 | 627 | "ax.text(\n", |
620 | 628 | " 0,\n", |
621 | | - " outer + 0.3,\n", |
622 | | - " \"shell band [inner, outer] — sampled; SAS rejects bulk-side, accepts surface-side\",\n", |
| 629 | + " 22,\n", |
| 630 | + " \"outside envelope (|r| > R_max + reach) — not sampled\",\n", |
| 631 | + " ha=\"center\",\n", |
| 632 | + " fontsize=10,\n", |
| 633 | + " color=\"steelblue\",\n", |
| 634 | + " bbox={\"facecolor\": \"white\", \"edgecolor\": \"steelblue\", \"boxstyle\": \"round,pad=0.4\"},\n", |
| 635 | + ")\n", |
| 636 | + "ax.text(\n", |
| 637 | + " -12,\n", |
| 638 | + " 13,\n", |
| 639 | + " \"ligand bulk\\n(SAS-rejected\\nwithin shell band)\",\n", |
623 | 640 | " ha=\"center\",\n", |
624 | 641 | " fontsize=9,\n", |
625 | | - " color=\"green\",\n", |
626 | | - " bbox={\"facecolor\": \"white\", \"edgecolor\": \"green\", \"boxstyle\": \"round\"},\n", |
| 642 | + " color=\"darkred\",\n", |
| 643 | + " bbox={\"facecolor\": \"white\", \"edgecolor\": \"darkred\", \"boxstyle\": \"round,pad=0.3\"},\n", |
| 644 | + ")\n", |
| 645 | + "ax.annotate(\n", |
| 646 | + " \"\",\n", |
| 647 | + " xy=(-6, 2.5),\n", |
| 648 | + " xytext=(-11, 11),\n", |
| 649 | + " arrowprops={\"arrowstyle\": \"->\", \"color\": \"darkred\", \"lw\": 1.2},\n", |
627 | 650 | ")\n", |
628 | 651 | "ax.text(\n", |
629 | | - " 0,\n", |
630 | | - " 11.2,\n", |
631 | | - " \"outside the envelope (> R_max + reach) — not sampled\",\n", |
| 652 | + " 8,\n", |
| 653 | + " 13,\n", |
| 654 | + " \"shell skips this tiny\\ndeep-interior zone\\n(|r| < R_min − buffer)\",\n", |
632 | 655 | " ha=\"center\",\n", |
633 | 656 | " fontsize=9,\n", |
634 | | - " color=\"steelblue\",\n", |
635 | | - " bbox={\"facecolor\": \"white\", \"edgecolor\": \"steelblue\", \"boxstyle\": \"round\"},\n", |
| 657 | + " color=\"darkorange\",\n", |
| 658 | + " bbox={\"facecolor\": \"white\", \"edgecolor\": \"darkorange\", \"boxstyle\": \"round,pad=0.3\"},\n", |
| 659 | + ")\n", |
| 660 | + "ax.annotate(\n", |
| 661 | + " \"\",\n", |
| 662 | + " xy=(0.5, 0.5),\n", |
| 663 | + " xytext=(7, 11),\n", |
| 664 | + " arrowprops={\"arrowstyle\": \"->\", \"color\": \"darkorange\", \"lw\": 1.2},\n", |
636 | 665 | ")\n", |
637 | 666 | "\n", |
638 | | - "ax.set_xlim(-12, 12)\n", |
639 | | - "ax.set_ylim(-8, 12)\n", |
| 667 | + "ax.set_xlim(-24, 24)\n", |
| 668 | + "ax.set_ylim(-13, 24)\n", |
640 | 669 | "ax.set_aspect(\"equal\")\n", |
641 | 670 | "ax.set_title(\n", |
642 | | - " \"Why Shell with `inner = R_min − buffer` doesn't miss surface points\", fontsize=11\n", |
| 671 | + " \"Shell-band coverage on an elongated ligand\\n\"\n", |
| 672 | + " \"(green dots = surface points; all sit inside the shell band)\",\n", |
| 673 | + " fontsize=12,\n", |
643 | 674 | ")\n", |
644 | 675 | "ax.grid(True, alpha=0.3)\n", |
645 | 676 | "ax.axhline(0, color=\"lightgray\", lw=0.5)\n", |
|
0 commit comments