.. : PyBEST: Pythonic Black-box Electronic Structure Tool : Copyright (C) 2016-- The PyBEST Development Team : : This file is part of PyBEST. : : PyBEST is free software; you can redistribute it and/or : modify it under the terms of the GNU General Public License : as published by the Free Software Foundation; either version 3 : of the License, or (at your option) any later version. : : PyBEST is distributed in the hope that it will be useful, : but WITHOUT ANY WARRANTY; without even the implied warranty of : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the : GNU General Public License for more details. : : You should have received a copy of the GNU General Public License : along with this program; if not, see : -- .. _dev_geh_long: Adding a New Hamiltonian Block ============================== Before starting an implementation, verify whether the block is already supported by looking at the ``pybest/featuredlists.py`` file. Check if your block string is registered under any of the following execution categories: * ``GeneralEffHamBlocks`` * ``GeneralEffHamCholeskyBlocks`` * ``GeneralEffHamSpecialBlocks`` If your block is not listed in any of these featured lists, it means the block is currently not implemented, and you must follow these strict steps to integrate it into the dynamic loader. .. _directory_routing_rules: Determine Directory and Create File ----------------------------------- The directory structure relies on translating the block's indices into an occupied/virtual string pattern. **Index Translation Mapping:** * **Occupied (o):** ``i, j, k, l, m, n, o`` * **Virtual (v):** ``a, b, c, d, e, f, g, h`` **Steps:** 1. Calculate the occupied/virtual string based on the name of the new block (e.g., ``X_iajb`` translates to ``ovov``). 2. Find the corresponding directory under ``eff_ham/blocks/`` (e.g., ``four_index/ovov/``). 3. Create a new Python file named exactly as your block but **strictly in lowercase** (e.g., ``X_iajb`` translates to ``x_iajb.py``). .. note:: **Case Sensitivity & Spin:** While the *file name* must be completely lowercase, the *method names* inside the file **must retain their exact original casing**. In PyBEST, uppercase and lowercase letters denote alpha and beta spins. Blocks like ``X_iajb`` and ``X_iAJB`` are completely different mathematical entities. Both of their getter methods would live inside the same lowercase ``x_iajb.py`` file. Implement with the Correct Getter Protocol --------------------------------------------- The dispatcher enforces distinct calling conventions based on memory management constraints. **You must adhere strictly to the naming conventions.** Helper Functions ^^^^^^^^^^^^^^^^ You can always add helper functions (e.g., for calculating separate T1/T2 contributions) in the same file to keep your code clean. The dynamic loader will correctly bind them to the ``GeneralEffHam`` instance **as long as the helper method's name contains the exact block name**. For example, for the ``X_ajbc`` block, a helper named ``get_t1_t2_X_ajbc`` is perfectly valid and will be loaded automatically. Protocol A: Dense Blocks (GeneralEffHamBlocks) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ These are standard, cacheable blocks. * **Naming:** The method **must** be named ``get_{block_name}_block`` (retaining exact case for the block name). * **Arguments:** ``self``, ``force_update`` (boolean). * **Behavior:** Compute and cache. The ``contract()`` runner handles the actual tensor math. .. code-block:: python # File: blocks/two_index/vv/x_ad.py def get_X_ad_block(self, force_update: bool = False): """Computes X_ad and caches it.""" if "X_ad" in self._cache and not force_update: return self._cache.load("X_ad") # ... compute X_ad ... return X_ad Protocol B: On-the-Fly Blocks (Cholesky & Special) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ These blocks are too large to cache and compute directly into an output tensor. .. note:: If you are implementing a Cholesky block (adding it to ``GeneralEffHamCholeskyBlocks``), you **always** have to make two implementations: 1. The Dense version following Protocol A (``get_{block_name}_block``). 2. The Cholesky version following Protocol B below. **For Special Blocks** (``GeneralEffHamSpecialBlocks``): These are inherently too large or complex to cache and bypass it entirely regardless of the active linear algebra factory. You **only** need to implement the Protocol B version below. * **Naming:** The method **must** be named ``get_{block_name}_block_cholesky`` (retaining exact case for the block name). * **Arguments:** ``self``, ``operand2`` (NIndex, usually b_vector), ``output`` (NIndex). *Note: Special unary blocks may only accept self and output.* * **Behavior:** Compute directly into the output vector using ``.contract(..., out=output)``. The ``output`` tensor is provided *by the developer* calling the API. The getter method does not allocate it; it must simply apply the correct contractions to populate or update the provided ``output`` object. .. code-block:: python # File: blocks/four_index/ovov/x_iajb.py def get_X_iAjb_sigma_block_cholesky(self, sigma): """Computes X_iAjb and applies it directly to the output 'sigma'.""" e_oovv = self._ham.get("eri_oovv") t_2 = self.rcc_iodata.t_2 # Compute directly into output e_oovv.contract("abcd,bdac->dac", t_2, factor=-1.0, out=sigma) Register in Featured Lists ----------------------------- Open ``pybest/featuredlists.py`` and add your block's exact string label (retaining upper/lower cases) to the appropriate list: .. code-block:: python # Example: Adding a new Dense block "X_new" GeneralEffHamBlocks = [ "X_im", "X_ad", # ... existing blocks ... "X_new", # Added here ] # Example: Adding a new Cholesky block "X_huge" GeneralEffHamCholeskyBlocks = [ "X_abcd", "X_AbCd", # ... existing blocks ... "X_huge", # Added here ] # Example: Adding a new Special block "X_special" GeneralEffHamSpecialBlocks = [ "X_akdc", "X_aKdC", # ... existing blocks ... "X_special", # Added here ] Route in BLOCK_LIBRARY ------------------------- Open ``pybest/eff_ham/GeneralEffHam_library.py`` and add the block label to the dictionary under the specific reference method, flavor, and parameter state it belongs to. .. code-block:: python BLOCK_LIBRARY = { "RCCSD": { "default": [], "flavor": { "RSF-CCSD": { "alpha": { 2: [ "X_im", "X_ad", # ... "X_new", "X_huge", "X_special", # <--- Register your new blocks here for alpha=2 # ... ], }, }, }, }, }