[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-2283":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":10,"rankLanguage":10,"license":22,"archived":23,"fork":23,"defaultBranch":24,"hasWiki":23,"hasPages":23,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":26,"readmeContent":27,"aiSummary":28,"trendingCount":16,"starSnapshotCount":16,"syncStatus":15,"lastSyncTime":29,"discoverSource":30},2283,"PyChem-Pro","vijaymasand\u002FPyChem-Pro","vijaymasand","PyChem-Pro: a molecular viewer, cheminformatics library, molecular descriptor calculator, molecular editor, chemical drawing editor, and many more...","",null,"Python",180,6,103,2,0,3,11,77,9,2.54,"Other",false,"main",[],"2026-06-12 02:00:39","\u003Ctable border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n  \u003Ctr>\n    \u003Ctd width=\"96\" valign=\"middle\">\u003Cimg src=\"assets\u002Ficon.svg\" alt=\"PyChem-Pro logo\" width=\"80\" height=\"80\">\u003C\u002Ftd>\n    \u003Ctd valign=\"middle\">\u003Ch1>PyChem-Pro\u003C\u002Fh1>\u003C\u002Ftd>\n  \u003C\u002Ftr>\n\u003C\u002Ftable>\n\n> A pure-Python desktop application and library for chemistry and cheminformatics — molecular visualization, SMILES parsing, PDB loading, MMFF94 geometry optimization, descriptors, and a plugin ecosystem. No RDKit. No OpenBabel. Everything implemented from scratch.\n\n**Repository:** https:\u002F\u002Fgithub.com\u002Fvijaymasand\u002FPyChem-Pro\n\n[![Buy Me A Coffee](https:\u002F\u002Fwww.buymeacoffee.com\u002Fassets\u002Fimg\u002Fcustom_images\u002Fyellow_img.png)](https:\u002F\u002Fbuymeacoffee.com\u002Fvijaymasand)\n\n---\n\n## Table of Contents\n\n- [About PyChem-Pro](#about-pychem-pro)\n- [Key Features](#key-features)\n- [Architecture Overview](#architecture-overview)\n- [Tech Stack](#tech-stack)\n- [System Requirements](#system-requirements)\n- [Installation](#installation)\n- [Running the Application](#running-the-application)\n  - [Recommended: One-Click Launchers](#recommended-one-click-launchers)\n  - [Manual Setup (all platforms)](#manual-setup-all-platforms)\n- [Quick Start — Python API](#quick-start--python-api)\n- [Project Structure](#project-structure)\n- [Services Layer (Public Protocols)](#services-layer-public-protocols)\n- [MMFF94 Force Field](#mmff94-force-field)\n- [Rendering Pipeline](#rendering-pipeline)\n- [Multiprocessing](#multiprocessing)\n- [Plugins](#plugins)\n- [Development Workflow](#development-workflow)\n- [Contributing](#contributing)\n- [Cross-Platform Notes](#cross-platform-notes)\n- [Performance Targets](#performance-targets)\n- [Roadmap](#roadmap)\n- [License](#license)\n- [Citation](#citation)\n- [Acknowledgments](#acknowledgments)\n\n---\n\n## About PyChem-Pro\n\nPyChem-Pro is a desktop chemistry application and Python library that combines molecular visualization (like PyMOL) with cheminformatics primitives (like RDKit). Unlike most tools in the space, PyChem-Pro is **pure Python with NumPy**. There is no C++ extension, no dependency on any cheminformatics library. Every feature — SMILES parsing, 3D coordinate generation, force field optimization, descriptor calculation, Shrake-Rupley SASA, ring perception, protein cartoon rendering — is implemented from scratch and readable end-to-end.\n\nThis makes PyChem-Pro:\n\n- **Educational.** Graduate students can open any file and see how a real MMFF94 optimizer or a Catmull-Rom ribbon renderer actually works.\n- **Portable.** No compilers. No system libraries beyond Python and Qt. Runs on Windows, macOS, and Linux identically.\n- **Extensible.** A service-oriented architecture with Protocol interfaces lets researchers swap the force field, the renderer, or the file loader without touching the rest of the codebase.\n- **Academic-friendly.** Intended for inclusion in university cheminformatics curricula and as an open-source reference implementation.\n\n## Key Features\n\n### Chemistry\n- SMILES parser with Huckel aromaticity perception, stereochemistry, bracketed atoms, radicals\n- PDB \u002F MOL \u002F MOL2 \u002F SDF file readers with automatic bond detection and CONECT parsing\n- 3D coordinate generation from molecular topology (BFS + force-field relaxation)\n- **MMFF94 force field** with hydrogen addition, bond stretching, angle bending, torsion, Van der Waals, and BCI partial charge assignment\n- L-BFGS and steepest descent optimizers\n- Gasteiger partial charges, AM1 and PM3 semi-empirical charge models\n- Molecular descriptor calculator (constitutional, topological, electronic, geometric, hybrid, quantum, fingerprints)\n- Morgan (ECFP) and topological fingerprints\n- Center of Mass and Shrake-Rupley SASA\n\n### Visualization\n- Hardware-aware software 3D rendering (QPainter with gradient sphere shading)\n- Ball-and-stick, space-fill, wireframe, cartoon, ribbon, and backbone modes\n- PyMOL-style protein cartoons with alpha-helix \u002F beta-sheet \u002F coil detection\n- Color-by secondary structure, rainbow, chain, B-factor\n- 2D chemical structure viewer with single\u002Fdouble\u002Ftriple\u002Faromatic bond rendering, wedge\u002Fhash stereo bonds, atom labels, formal charges\n- Natural-language atom selection (`sele('organic')`, `sele('within 5.0 COM')`, `sele('chain A and helix')`)\n- High-DPI image export (72 \u002F 150 \u002F 300 \u002F 600 DPI)\n- Offline ray-tracer for publication-quality images\n- Print (Ctrl+P) — prints 2D and 3D views on a single page\n\n### Architecture\n- Service-oriented with `typing.Protocol` interfaces\n- EventBus for decoupled pub\u002Fsub communication\n- ParallelExecutor at 50% CPU cores across file loading, force field, rendering, descriptors, ray-tracing, and conformer search\n- Public `pychem` Python package usable in Jupyter notebooks without PySide6\n\n### Plugin System\n- `BasePlugin` \u002F `PluginWidget` API for custom analysis, visualization, I\u002FO, and utility plugins\n- Built-in templates for analysis, visualization, and I\u002FO plugin types\n- QSAR modeling, Ramachandran, descriptor pruning, docking pose visualization, molecular weight calculator — all shipped as plugins\n\n---\n\n## Architecture Overview\n\n```\n┌─────────────────────────────────────────────────────────────────────┐\n│                     PUBLIC API LAYER (pychem\u002F)                       │\n│  No Qt dependency. Jupyter-friendly.                                 │\n│  import pychem; pychem.parse_smiles(\"CCO\"); pychem.optimize(mol)     │\n└──────────────────────────────┬──────────────────────────────────────┘\n                               │\n┌──────────────────────────────▼──────────────────────────────────────┐\n│                     SERVICE REGISTRY (src\u002Fcore\u002Fregistry.py)          │\n│  Plain Python factory, no DI framework.                              │\n│  registry.forcefield, registry.loader, registry.descriptors, ...     │\n└───┬──────────┬──────────┬──────────┬──────────┬──────────┬─────────┘\n    │          │          │          │          │          │\n┌───▼───┐ ┌───▼───┐ ┌───▼───┐ ┌───▼───┐ ┌───▼───┐ ┌───▼────┐\n│Force  │ │Render │ │Loader │ │Coord  │ │Descr  │ │Plugin  │\n│Field  │ │Service│ │Service│ │Gen    │ │Calc   │ │Manager │\n└───┬───┘ └───┬───┘ └───┬───┘ └───┬───┘ └───┬───┘ └───┬────┘\n    │         │         │         │         │         │\n┌───▼─────────▼─────────▼─────────▼─────────▼─────────▼──────────────┐\n│                     CORE DOMAIN (src\u002Fcore\u002Fdomain\u002F)                   │\n│  Molecule, Atom, Bond, Element — zero external dependencies          │\n└─────────────────────────────────────────────────────────────────────┘\n┌─────────────────────────────────────────────────────────────────────┐\n│                     INFRASTRUCTURE (src\u002Fcore\u002F)                       │\n│  EventBus, ParallelExecutor, Protocols, Performance Profiler         │\n└─────────────────────────────────────────────────────────────────────┘\n```\n\n**Dependency rules (enforced by convention):**\n\n1. Core domain imports nothing from services, features, app, or Qt.\n2. Infrastructure imports only from core domain.\n3. Services import from core domain + protocols. Never from other service implementations.\n4. Features import from services (via protocols) + core domain + Qt.\n5. App imports from features + services + core domain + Qt.\n6. Public API imports from services + core domain. Never from Qt.\n7. Plugins import from public API + Qt compat layer. Never from service internals.\n\nEvery subsystem implements a `typing.Protocol` interface (`IForceField`, `IRenderer`, `ILoader`, `ICoordinateGenerator`, `IDescriptorCalculator`) so the implementation can be swapped without touching consumers.\n\n---\n\n## Tech Stack\n\n| Layer | Technology |\n|-------|------------|\n| Language | Python 3.10+ (3.13\u002F3.14 supported) |\n| GUI Framework | PySide6 (Qt 6.5+) |\n| Numerical | NumPy 1.24+ |\n| Multiprocessing | Python `concurrent.futures.ProcessPoolExecutor` with forced `spawn` context for cross-platform safety |\n| Bundler (optional) | Nuitka 2.0+ for producing standalone binaries |\n| Plugin stack (optional) | pandas, scipy, scikit-learn, matplotlib, packaging — only needed if you run plugins that require them |\n\n**No chemistry dependencies.** PyChem-Pro does not use any cheminformatics or external chemistry library.\n\n### Vendored\n\n- **OASA** (Open Structure Access) — a subset is vendored under `src\u002Fvendors\u002Foasa\u002F` and used for SMILES\u002FInChI utilities and 2D coordinate generation fallbacks. It is treated as frozen third-party code and not modified.\n\n---\n\n## System Requirements\n\n- **Operating System:** Windows 10\u002F11, macOS 12+, or any Linux distribution with Qt 6 support (Ubuntu 22.04+, Fedora 37+, Arch, etc.)\n- **Python:** 3.10 or newer\n- **RAM:** 4 GB minimum, 8 GB recommended for large proteins (>5000 atoms)\n- **Display:** OpenGL 3.3+ optional (used by the future hardware-accelerated renderer path). The default QPainter renderer works on any display.\n- **CPU:** Multi-core recommended. The `ParallelExecutor` uses 50% of available cores automatically.\n\n### Cross-Platform Notes\n\nPyChem-Pro is designed to work identically on Windows, macOS, and Linux. Two design decisions enforce this:\n\n1. **Multiprocessing:** `ProcessPoolExecutor` is always created with the `spawn` start method (`multiprocessing.get_context('spawn')`). This avoids `fork()`-related crashes on macOS (Qt + CoreFoundation assertion) and matches the only option available on Windows.\n2. **File paths:** All path handling uses `os.path` or `pathlib` — no hard-coded separators.\n3. **Fonts:** The UI uses a best-available fallback sequence and will warn but not fail if a platform-specific font is missing.\n\n---\n\n## Installation\n\n### 1. Clone the repository\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fvijaymasand\u002FPyChem-Pro.git\ncd PyChem-Pro\n```\n\n### 2. Create a virtual environment\n\n```bash\n# macOS \u002F Linux\npython3 -m venv venv\nsource venv\u002Fbin\u002Factivate\n\n# Windows (cmd)\npython -m venv venv\nvenv\\Scripts\\activate.bat\n\n# Windows (PowerShell)\npython -m venv venv\nvenv\\Scripts\\Activate.ps1\n```\n\n### 3. Install Dependencies\n\n```bash\n# Upgrade pip \npip install --upgrade pip\n# (If this fails on Windows, use: python -m pip install --upgrade pip)\n\n# Install required packages\npip install -r requirements.txt\n```\n\nThis installs the runtime dependencies:\n- `PySide6 >= 6.5` (Qt GUI framework)\n- `numpy >= 1.24`, `matplotlib >= 3.7`, `pandas >= 2.0`, `scipy >= 1.10`\n- `scikit-learn >= 1.3`, `rdkit >= 2023.3` (used by QSAR and descriptor plugins)\n- `psutil`, `pillow`\n\nBuild tools (Nuitka, dmgbuild) are in `requirements-build.txt` and only needed if you want to compile a standalone binary — not required for running from source.\n\n### 4. Optional — install plugin dependencies\n\nThe built-in plugins in `plugins\u002F` have optional dependencies. Install them only if you plan to use those plugins:\n\n```bash\npip install packaging pandas scipy scikit-learn matplotlib rdkit\n```\n\nWithout these, the core application runs fine; the affected plugins are simply skipped at startup with a warning.\n\n---\n\n## Running the Application\n\n### Recommended: One-Click Launchers\n\nThe easiest way to run PyChem-Pro is to double-click the launcher for your platform. No terminal, no Python knowledge, no setup steps required.\n\n| Platform | File to double-click |\n|----------|----------------------|\n| macOS | `PyChem.command` (in Finder) |\n| Windows | `PyChem.bat`, then use the `PyChem - Launch.lnk` shortcut created on first run |\n\nBoth launchers handle everything automatically and follow the same steps:\n\n| Step | What happens |\n|------|-------------|\n| 1 | Checks whether Python 3.10+ is already installed |\n| 2 | **If Python is missing** — installs it automatically: macOS uses Homebrew (asks for your password once); Windows uses `winget` (built into Windows 10\u002F11), falling back to a silent download from python.org |\n| 3 | Creates a PyChem-Pro icon shortcut for easy access (first run only) |\n| 4 | Creates an isolated Python virtual environment inside the project folder (`venv\u002F`) so no system packages are touched (first run only) |\n| 5 | Installs all required packages from `requirements.txt` into that venv (first run only) |\n| 6 | Launches PyChem |\n\n> **First run** takes 2–5 minutes while packages download and install. Every run after that starts in seconds — the venv is reused as-is.\n\n> The launchers are provided for your convenience and are the recommended way to get started. If you prefer to manage your Python environment yourself, or if a launcher does not work on your machine, use the manual setup below — it is straightforward and gives you full control.\n\n---\n\n### Manual Setup (all platforms)\n\n#### 1. Install Python 3.10+\n\n- **macOS:** Download from [python.org\u002Fdownloads](https:\u002F\u002Fwww.python.org\u002Fdownloads\u002F) or run `brew install python@3.12`\n- **Linux:** `sudo apt install python3` (Ubuntu\u002FDebian) or `sudo dnf install python3` (Fedora)\n- **Windows:** Download from [python.org\u002Fdownloads](https:\u002F\u002Fwww.python.org\u002Fdownloads\u002F) — check \"Add Python to PATH\" during install\n\nVerify:\n```bash\npython3 --version   # should print 3.10 or newer\n```\n\n#### 2. Clone the repository\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fvijaymasand\u002FPyChem-Pro.git\ncd PyChem-Pro\n```\n\n#### 3. Create a virtual environment\n\n```bash\n# macOS \u002F Linux\npython3 -m venv venv\nsource venv\u002Fbin\u002Factivate\n\n# Windows (cmd)\npython -m venv venv\nvenv\\Scripts\\activate.bat\n```\n\n#### 4. Install dependencies\n\n```bash\npip install --upgrade pip\npip install -r requirements.txt\n```\n\n#### 5. Run PyChem-Pro\n\n```bash\npython main.py\n```\n\nThe first launch generates a 10-year development license automatically. No network access required.\n\n### Expected startup output\n\n```\nUsing PySide6 framework\nPlugin system initialized successfully\n```\n\n---\n\n## Quick Start — Python API\n\nPyChem-Pro provides a public package at the top level (`pychem\u002F`) that does not require PySide6 to be imported. You can use it in Jupyter notebooks or scripts.\n\n```python\nimport pychem\n\n# Parse SMILES\nmol = pychem.parse_smiles(\"CCO\")  # ethanol\nprint(mol.molecular_formula())    # C2H6O\n\n# Generate 3D coordinates\npychem.generate_3d(mol)\n\n# Full MMFF94 optimization (adds hydrogens, assigns BCI charges, minimizes)\nresult = pychem.optimize(mol, max_iters=500)\nprint(f\"Converged: {result.converged}, E = {result.final_energy:.2f} kcal\u002Fmol\")\nprint(f\"Steps: {result.num_steps}\")\n\n# Partial charges are now assigned\nfor atom in mol.atoms:\n    print(f\"  {atom.symbol}{atom.index}: q = {atom.partial_charge:+.4f}\")\n\n# Molecular descriptors\ndesc = pychem.descriptors(mol)\nprint(desc)\n# {'molecular_weight': 46.07, 'num_atoms': 9, 'num_bonds': 8,\n#  'num_heavy_atoms': 3, 'formula': 'C2H6O', 'num_rings': 0, 'total_charge': 0}\n\n# Batch descriptor calculation (uses ParallelExecutor)\nmols = [pychem.parse_smiles(s) for s in [\"CCO\", \"c1ccccc1\", \"CC(=O)O\", \"CCCCCC\"]]\nbatch = pychem.descriptors_batch(mols)\n\n# Load a protein from PDB\nprotein = pychem.load(\"1AKE.pdb\")\nprint(f\"{protein.num_atoms} atoms, {protein.num_bonds} bonds\")\n\n# Add explicit hydrogens only (no optimization)\nh_count = pychem.add_hydrogens(mol)\n\n# Compute MMFF94 BCI charges only\npychem.compute_charges(mol)\n```\n\n### Available API functions\n\n```python\npychem.parse_smiles(smiles: str) -> Molecule\npychem.load(path: str) -> Molecule\npychem.generate_3d(mol: Molecule, optimize: bool = True, max_steps: int = 200) -> None\npychem.optimize(mol: Molecule, max_iters: int = 500, method: str = 'lbfgs') -> OptimizationResult\npychem.add_hydrogens(mol: Molecule) -> int\npychem.compute_charges(mol: Molecule) -> None\npychem.descriptors(mol: Molecule, names: list[str] | None = None) -> dict\npychem.descriptors_batch(mols: list[Molecule], names: list[str] | None = None) -> list[dict]\n```\n\n---\n\n## Project Structure\n\n```\nPyChem\u002F\n├── pychem\u002F                        # Public API (no Qt dependency)\n│   ├── __init__.py                #   import pychem\n│   ├── api.py                     #   public facade functions\n│   └── _bridge.py                 #   ServiceRegistry singleton\n│\n├── src\u002F\n│   ├── core\u002F                      # Infrastructure and core domain\n│   │   ├── domain\u002Fmodels\u002F         # Molecule, Atom, Bond, Element\n│   │   ├── protocols\u002F             # IForceField, IRenderer, ILoader, ...\n│   │   ├── events.py              # EventBus + event dataclasses\n│   │   ├── parallel.py            # ParallelExecutor (50% CPU cores)\n│   │   ├── registry.py            # ServiceRegistry\n│   │   ├── performance\u002F           # Profiler, parallel loader\n│   │   └── security\u002F              # License manager\n│   │\n│   ├── services\u002F                  # Service implementations\n│   │   ├── forcefield\u002F            # MMFF94Service, HydrogenAdder,\n│   │   │                          # AngleBending, Torsion, parameters\n│   │   ├── rendering\u002F             # RendererFactory, parallel_projection\n│   │   ├── loading\u002F               # LoaderService\n│   │   ├── coordinates\u002F           # CoordinateGeneratorService\n│   │   └── descriptors\u002F           # DescriptorService\n│   │\n│   ├── app\u002F                       # GUI application (thin shell)\n│   │   ├── main_window.py         # QMainWindow wiring\n│   │   ├── menu_bar.py            # Menu construction\n│   │   ├── toolbar.py             # Toolbar construction\n│   │   ├── file_operations.py     # Open \u002F Save \u002F Export \u002F Print\n│   │   ├── chemistry_actions.py   # Optimize \u002F Charges \u002F Descriptors\n│   │   ├── viewer_coordinator.py  # View toggles, COM\u002Fcentroid spheres\n│   │   ├── molecule_controller.py # Signal wiring, undo\u002Fredo, selection\n│   │   ├── conversion_worker.py   # QThread SMILES->3D worker\n│   │   ├── plugin_interface.py    # Plugin browser UI\n│   │   ├── plugin_card.py\n│   │   └── plugin_installer.py\n│   │\n│   ├── features\u002F                  # Feature modules (split by domain)\n│   │   ├── visualization_3d\u002F      # MolViewer3D + painter_renderer +\n│   │   │                          # mouse_controller + protein_rendering\n│   │   ├── visualization_2d\u002F      # MolViewer2D + bond\u002Fatom renderers\n│   │   ├── cheminformatics\u002F       # AM1, PM3, Gasteiger, MMFF94 legacy\n│   │   ├── layout_3d\u002F             # 3D coordinate generator\n│   │   ├── layout_2d\u002F             # 2D layout generators\n│   │   ├── smiles_parser\u002F         # OpenSMILES parser\n│   │   ├── smiles_generator\u002F      # SMILES writer\n│   │   ├── io\u002F                    # File readers\u002Fwriters\n│   │   ├── descriptor_calculator\u002F # Descriptor GUI and engines\n│   │   ├── scripting_console\u002F     # Python REPL + atom selection\n│   │   ├── control_panel\u002F         # Input panel\n│   │   └── ui\u002F                    # Dialogs (colors, spheres, etc.)\n│   │\n│   ├── plugins\u002F                   # Plugin manager infrastructure\n│   ├── shared\u002F                    # Qt compat, theme\n│   └── vendors\u002Foasa\u002F              # Vendored OASA library (frozen)\n│\n├── plugins\u002F                       # Built-in and user plugins\n│   ├── docking_pose_visualizer.py\n│   ├── ramachandran_plugin.py\n│   ├── qsar_modeler_plugin.py\n│   ├── mol_weight_calculator.py\n│   └── ...\n│\n├── tests\u002F                         # Unit tests\n├── testing\u002F                       # Development \u002F debugging scripts\n├── docs\u002F                          # Architecture specs, user guides\n│   └── superpowers\u002F\n│       ├── specs\u002F                 # Design documents\n│       └── plans\u002F                 # Implementation plans\n├── main.py                        # Application entry point\n├── build.py                       # Nuitka bundler script\n├── requirements.txt\n└── README.md                      # You are here\n```\n\n---\n\n## Services Layer (Public Protocols)\n\nAll major subsystems are exposed through `typing.Protocol` interfaces so alternative implementations can be dropped in without touching consumers.\n\n```python\nfrom src.core.protocols import (\n    IForceField, IRenderer, ILoader,\n    ICoordinateGenerator, IDescriptorCalculator,\n    OptimizationResult, Camera, RenderMode,\n)\n```\n\n### Example: registering a custom force field\n\n```python\nfrom src.core.protocols.forcefield import IForceField, OptimizationResult\nfrom src.core.domain.models.molecule import Molecule\n\nclass MyForceField:\n    \"\"\"Custom force field implementing the IForceField protocol.\"\"\"\n\n    def add_hydrogens(self, mol: Molecule) -> int:\n        ...\n\n    def assign_atom_types(self, mol: Molecule) -> None:\n        ...\n\n    def assign_charges(self, mol: Molecule) -> None:\n        ...\n\n    def optimize_geometry(self, mol, max_iters=500, convergence=1e-4,\n                          method='lbfgs') -> OptimizationResult:\n        ...\n\n    def compute_energy(self, mol: Molecule) -> float:\n        ...\n\n# Install into the registry at startup\nfrom src.core.registry import ServiceRegistry\nregistry = ServiceRegistry()\nregistry.forcefield = MyForceField()\n```\n\nThe class does not need to inherit from anything — `typing.Protocol` uses structural subtyping.\n\n---\n\n## MMFF94 Force Field\n\nPyChem-Pro ships a pure-Python implementation of the Merck Molecular Force Field (MMFF94). At present, it is not perfect. It is simplified relative to the full 75K+ rule set but covers standard organic chemistry:\n\n| Term | Implementation |\n|------|----------------|\n| Bond stretching | Hookean with MMFF94 `r0` and `kb` parameters |\n| Angle bending | Cubic-corrected `0.021914 ka (theta - theta0)^2 [1 + cb (theta - theta0)]` with analytical gradient |\n| Torsion (dihedral) | Fourier `V1 cos(phi) + V2 cos(2 phi) + V3 cos(3 phi)` with analytical gradient |\n| Van der Waals | Lennard-Jones 12-6 with 1-2 and 1-3 exclusions, parallelized for >200 pairs |\n| Partial charges | Bond Charge Increment (BCI) method |\n| Hydrogen addition | Hybridization-aware 3D placement (tetrahedral \u002F trigonal \u002F linear) |\n\n### Optimization pipeline\n\n1. Assign hybridization (sp \u002F sp2 \u002F sp3)\n2. Add explicit hydrogens with ideal 3D positions\n3. Assign BCI partial charges\n4. Build interaction lists (bonds, angles, torsions, VdW pairs)\n5. Minimize energy via steepest descent or L-BFGS with Armijo line search\n\nVerified analytical gradients against numerical finite differences within `1e-4` for angles and `1e-3` for torsions.\n\n### Files\n\n```\nsrc\u002Fservices\u002Fforcefield\u002F\n├── mmff94_service.py       # Unified IForceField implementation\n├── hydrogen.py             # HydrogenAdder with hybridization-aware placement\n├── angle_bending.py        # AngleBendingCalculator\n├── torsion.py              # TorsionCalculator\n└── parameters.py           # Consolidated bond\u002Fangle\u002Ftorsion\u002FVdW\u002FBCI tables\n```\n\n---\n\n## Rendering Pipeline\n\nThe 3D viewer (`src\u002Ffeatures\u002Fvisualization_3d\u002Fui\u002Fmol_viewer_3d.py`) paints directly via QPainter. Rendering logic is extracted into `painter_renderer.py` (the engine) and `mouse_controller.py` (interaction).\n\n### Optimizations\n\n- **Gradient cache.** `QRadialGradient` color stops are cached by `(element, radius_bucket, is_hovered, use_ssao, depth_bucket)`. Rebuilding a positioned gradient from cached stops is free; the expensive color arithmetic runs once per unique atom type.\n- **Off-screen culling.** Atoms and bonds outside the viewport plus a 50-100 pixel margin are skipped before any draw call.\n- **LOD (Level of Detail).** Atoms projected to less than 2 px radius are drawn as plain filled circles instead of gradient spheres.\n- **Parallel pre-render.** For large molecules, projection, depth sort, and visibility culling can be split across `ParallelExecutor` workers (in `src\u002Fservices\u002Frendering\u002Fparallel_projection.py`). Draw calls themselves remain on the main thread per Qt's threading model.\n\n### Protein cartoon rendering\n\n`src\u002Ffeatures\u002Fvisualization_3d\u002Fservices\u002Fprotein_rendering.py` implements:\n\n- Simplified DSSP-style secondary structure detection (~85% accuracy on standard tests)\n- Catmull-Rom spline smoothing for ribbon paths\n- PyMOL-style cartoon tubes for helices, flat ribbons with arrow heads for sheets, thin coils for loops\n- Color schemes: secondary structure, rainbow, by chain, by B-factor\n\n---\n\n## Multiprocessing\n\nA single `ParallelExecutor` at `src\u002Fcore\u002Fparallel.py` wraps `concurrent.futures.ProcessPoolExecutor` and is shared across all services. It uses **50% of available CPU cores** by default.\n\n### Forced spawn start method\n\n```python\nimport multiprocessing as mp\n_mp_context = mp.get_context('spawn')\n```\n\nCross-platform rationale:\n- **macOS:** `fork()` + Qt causes CoreFoundation assertions. `spawn` avoids it.\n- **Windows:** `fork()` is not available. `spawn` is the only option.\n- **Linux:** `fork()` works, but `spawn` is safer with Qt and matches other platforms.\n\n### Where multiprocessing is used\n\n| Service | What is parallelized |\n|---------|----------------------|\n| File loading | PDB \u002F MOL2 atom record parsing split into N chunks |\n| MMFF94 force field | VdW pairwise computation split into N chunks (only when >200 pairs) |\n| Coordinate generation | N conformers with independent random seeds, return lowest energy |\n| Descriptor calculation | Batch molecules distributed across workers |\n| 3D rendering | Projection, depth sort, culling split across workers (large molecules only) |\n| Ray-tracing export | 64x64 pixel tiles processed in parallel |\n\nAll worker functions are **module-level** (required by the `spawn` start method for pickling).\n\n---\n\n## Plugins\n\nPyChem-Pro has a first-class plugin system. Plugins appear as dockable panels in the GUI and can subscribe to molecule-changed events.\n\n### Minimal plugin\n\n```python\nfrom src.plugins.base_plugin import BasePlugin, PluginWidget\nfrom src.plugins.plugin_types import PluginInfo, PluginType\nfrom src.shared.qt_compat import QVBoxLayout, QPushButton, QLabel\n\nclass HelloPluginWidget(PluginWidget):\n    def setup_ui(self):\n        layout = QVBoxLayout(self.widget)\n        self.label = QLabel(\"No molecule loaded\")\n        layout.addWidget(self.label)\n\n    def on_molecule_changed(self, molecule):\n        if molecule:\n            self.label.setText(f\"{molecule.num_atoms} atoms\")\n\nclass HelloPlugin(BasePlugin):\n    def get_info(self) -> PluginInfo:\n        return PluginInfo(\n            name=\"Hello Plugin\",\n            version=\"1.0.0\",\n            description=\"Shows atom count\",\n            author=\"Your Name\",\n            plugin_type=PluginType.ANALYSIS,\n            dependencies=[],\n        )\n\n    def create_widget(self):\n        return HelloPluginWidget(self)\n\n    def initialize(self):\n        return True\n\n    def cleanup(self):\n        pass\n\n    def on_molecule_changed(self, molecule):\n        if hasattr(self, 'widget') and self.widget:\n            self.widget.on_molecule_changed(molecule)\n```\n\nDrop the file into `plugins\u002F` and restart PyChem, or use **Plugins → Installed Plugins...** to load it manually.\n\n### Built-in plugins\n\n| Plugin | Purpose |\n|--------|---------|\n| `mol_weight_calculator.py` | Molecular weight and element composition |\n| `ramachandran_plugin.py` | Ramachandran plot for protein backbones |\n| `qsar_modeler_plugin.py` | QSAR model building and validation |\n| `qsar_rfa_mars_plugin.py` | Random Forest + MARS QSAR |\n| `docking_pose_visualizer.py` | Docking pose comparison |\n| `DescriptorPruningApp.py` | Descriptor correlation pruning |\n| `example_analysis_plugin.py` | Template for analysis plugins |\n| `example_visualization_plugin.py` | Template for visualization plugins |\n\nOptional plugins declare their external dependencies in `PluginInfo.dependencies`. If a required package is missing, the plugin is skipped with a warning at startup.\n\n---\n\n## Development Workflow\n\n### Coding standards\n\n- **Style.** Follow PEP 8, 4-space indentation, max 100 chars per line.\n- **Typing.** Use `typing.Protocol` for service interfaces, dataclasses for DTOs, and type hints everywhere reasonable.\n- **No chemistry dependencies.** NumPy is the only permitted numerical dependency.\n- **Module-level functions for multiprocessing.** Any function passed to `ParallelExecutor.map` must be defined at module scope (required by the `spawn` start method).\n- **Cross-platform paths.** Use `os.path` or `pathlib`. Never hard-code `\u002F` or `\\`.\n- **Large files.** Keep individual Python files under ~800 lines. If a file grows beyond that, split it into focused modules.\n\n### Commit conventions\n\n- Use imperative present-tense commit subjects: `feat: add X`, `fix: correct Y`, `refactor: split Z`, `perf: cache W`, `docs: explain V`.\n- First line 72 chars max. Body wrapped at 72 chars.\n- Do not add `Co-Authored-By: Claude` or any AI attribution.\n- Co-authorship for human collaborators is welcome via the standard `Co-Authored-By: Name \u003Cemail>` trailer.\n\n### Running the application during development\n\n```bash\nsource venv\u002Fbin\u002Factivate\npython main.py\n```\n\nStartup profiling is enabled by default and prints timing information for Qt imports, MainWindow creation, and total startup time.\n\n### Building a standalone binary (optional)\n\n```bash\npython build.py\n```\n\nThis uses Nuitka to produce a compiled executable in `build\u002F`. Windows and macOS builds are tested; Linux builds work but are not packaged for distribution.\n\n---\n\n## Contributing\n\nContributions are welcome — bug reports, feature requests, pull requests, new plugins, documentation improvements, and test coverage.\n\n### Before submitting a pull request\n\n1. Run the full unit test suite and make sure nothing regresses:\n   ```bash\n   for t in tests\u002Ftest_*.py; do python3 \"$t\"; done\n   ```\n2. Launch the GUI and verify the affected workflow manually.\n3. Update relevant documentation in `docs\u002F` if you changed an interface.\n4. Follow the commit conventions above.\n\n### Reporting bugs\n\nPlease include:\n- Operating system and version\n- Python version (`python --version`)\n- PySide6 version (`pip show PySide6`)\n- Full traceback if there is an error\n- Minimal reproducer (SMILES string, PDB id, or attached file)\n\n---\n\n## Performance Targets\n\nApproximate targets measured on a 4-core Intel machine with 8 GB RAM. Actual numbers depend on the molecule.\n\n| Metric | Target |\n|--------|--------|\n| MMFF94 ethanol optimization | ~0.3 s (full force field with H addition) |\n| MMFF94 insulin (51 residues) | \u003C30 s |\n| 3D render FPS (100 atoms) | ~60 FPS |\n| 3D render FPS (5000 atoms) | ~30 FPS |\n| 3D render FPS (10K atoms) | ~60 FPS on GL path (future) |\n| PDB load (10K atoms) | ~1 s (parallel) |\n| Descriptor calculation (100 mols) | ~50% faster than sequential via ParallelExecutor |\n| Startup time | ~1.5 s |\n\n---\n\n## Roadmap\n\nThe architecture spec at `docs\u002Fsuperpowers\u002Fspecs\u002F2026-04-11-architecture-redesign-design.md` captures the long-term plan. Short-term items:\n\n- Hardware-accelerated OpenGL renderer with protein cartoon support\n- Stereochemistry enforcement in 3D coordinate generation (R\u002FS, E\u002FZ)\n- Ring template library for faster and more accurate cyclic structure generation\n- Conformer ensemble analysis (RMSD clustering, Boltzmann-weighted properties)\n- In-app 2D chemical structure editor \u002F sketcher\n- Extended MMFF94 parameter tables (halogens, phosphorus, metals)\n- Docking interface (scoring function only, no compiled dependencies)\n\nSee `docs\u002F` for detailed specs and implementation plans.\n\n---\n\n## License\n\nPyChem-Pro is licensed under the **[Polyform Noncommercial License 1.0.0](LICENSE)**.\n\nCopyright (c) 2026 Vijay Masand and Gaurav Masand\n\n- **Personal and educational use** — free to use, modify, and distribute.\n- **Commercial use** — strictly prohibited. This includes using PyChem-Pro as part of a paid service, distributing it for a fee, or relying on it to generate revenue.\n\nSee the `LICENSE` file for the full legal text.\n\nVendored OASA code retains its original BSD-style license.\n\n---\n\n## Citation\n\nIf you use PyChem-Pro in academic work, please cite:\n\n```\n@software{pychem,\n  title   = {PyChem-Pro: A Pure-Python Cheminformatics and Molecular Visualization Toolkit},\n  author  = {Masand, Gaurav and Masand, Vijay},\n  year    = {2026},\n  url     = {https:\u002F\u002Fgithub.com\u002Fvijaymasand\u002FPyChem},\n  note    = {Accessed: YYYY-MM-DD}\n}\n```\n\nA proper DOI will be issued once the first tagged release is published.\n\n---\n\n## Acknowledgments\n\n- The **MMFF94 force field** is the work of Thomas A. Halgren (Merck, 1996). Our implementation is a simplified pure-Python version of the published specification.\n- The **OASA** library (Open Structure Access, Beda Kosata) is vendored under `src\u002Fvendors\u002Foasa\u002F` and provides SMILES\u002FInChI utilities and 2D layout primitives.\n- **PyMOL** and **Jmol** inspired the protein cartoon representations.\n- **PySide6 \u002F Qt** from the Qt Project provides the GUI framework.\n\n---\n\n## Contact\n\n- GitHub: https:\u002F\u002Fgithub.com\u002Fvijaymasand\u002FPyChem-Pro\n- Issues: https:\u002F\u002Fgithub.com\u002Fvijaymasand\u002FPyChem-Pro\u002Fissues\n\nQuestions, feedback, and pull requests are welcome.\nMany more functions coming in version 2.0.\n","PyChem-Pro 是一个用于化学和化学信息学的纯 Python 桌面应用程序和库，支持分子可视化、SMILES 解析、PDB 文件加载、MMFF94 几何优化、描述符计算等功能。项目完全从零开始实现，不依赖于任何外部化学信息学库如 RDKit 或 OpenBabel，而是基于 NumPy 构建。这使得 PyChem-Pro 不仅具有教育意义，适合研究生学习分子优化器等核心算法的工作原理，还具备跨平台特性，在 Windows、macOS 和 Linux 上均可一致运行。其服务导向架构允许研究人员轻松替换力场、渲染器或文件加载器，非常适合用作学术研究中的参考实现以及大学化学信息学课程的教学工具。","2026-06-11 02:49:14","CREATED_QUERY"]