Skip to content

Equilibration Module

Module for setting up NAMD equilibration protocols for membrane protein systems. Generates configuration files, restraint files, and run scripts for multi-stage equilibration simulations using AMBER force fields.

Import

from gatewizard.tools.equilibration import NAMDEquilibrationManager

Class: NAMDEquilibrationManager

Manager for NAMD equilibration simulations with support for multi-stage protocols, flexible restraints, and ensemble control.

Constructor

NAMDEquilibrationManager(working_dir: Path, namd_executable: str = "namd3")

Parameters:

Parameter Type Default Description
working_dir Path Required Working directory containing system files
namd_executable str "namd3" NAMD executable name or path

Returns: NAMDEquilibrationManager instance


Quick Start

Example 1: Automatic File Detection (Simplest)

from pathlib import Path
from gatewizard.tools.equilibration import NAMDEquilibrationManager

# Point to folder with system files
system_folder = Path("popc_membrane")

# Define equilibration stages
stages = [
    {
        'name': 'Equilibration 1',
        'time_ns': 0.125,
        'steps': 125000,
        'ensemble': 'NVT',
        'temperature': 310.15,
        'timestep': 1.0,
        'minimize_steps': 10000,
        'constraints': {
            'protein_backbone': 10.0,
            'protein_sidechain': 5.0,
            'lipid_head': 2.5,
            'lipid_tail': 2.5,
            'water': 0.0,
            'ions': 10.0,
            'other': 0.0
        }
    }
]

# Setup with automatic file detection (no system_files needed!)
# scheme_type is auto-detected from the 'ensemble' field in stages
manager = NAMDEquilibrationManager(system_folder)
result = manager.setup_namd_equilibration(
    stage_params_list=stages,
    output_name="equilibration_example_01"
)

# Note that output folder is created inside the system_folder

print(f"Setup complete: {result['namd_dir']}")
# Run with: cd {result['namd_dir']} && ./run_equilibration.sh

Example 2: Explicit File Paths (Alternative)

from pathlib import Path
from gatewizard.tools.equilibration import NAMDEquilibrationManager

# Point to folder with system files
system_folder = Path("popc_membrane")

# Explicitly define system files (if auto-detection doesn't work)
system_files = {
    'prmtop': str(system_folder / 'system.prmtop'),
    'inpcrd': str(system_folder / 'system.inpcrd'),
    'pdb': str(system_folder / 'system.pdb'),
    'bilayer_pdb': str(system_folder / 'bilayer_protein_protonated_prepared_lipid.pdb')
}

# Define equilibration stages
stages = [
    {
        'name': 'Equilibration 1',
        'time_ns': 0.125,
        'steps': 125000,
        'ensemble': 'NVT',
        'temperature': 310.15,
        'timestep': 1.0,
        'minimize_steps': 10000,
        'constraints': {
            'protein_backbone': 10.0,
            'protein_sidechain': 5.0,
            'lipid_head': 2.5,
            'lipid_tail': 2.5,
            'water': 0.0,
            'ions': 10.0,
            'other': 0.0
        }
    }
]

# Setup with explicit file paths
# scheme_type is auto-detected from the 'ensemble' field in stages
manager = NAMDEquilibrationManager(system_folder)
result = manager.setup_namd_equilibration(
    system_files=system_files,
    stage_params_list=stages,
    output_name="equilibration_example_02"
)

print(f"Setup complete: {result['namd_dir']}")
# Run with: cd {result['namd_dir']} && ./run_equilibration.sh

Helper Methods

Method: find_system_files()

Automatically detect system files in the working directory. Useful for verifying which files will be used before running setup.

find_system_files() -> Optional[Dict[str, str]]

Parameters: None

Returns: Optional[Dict[str, str]]

  • Dictionary with detected file paths, or None if required files not found
  • Keys: 'prmtop', 'inpcrd', 'pdb', 'bilayer_pdb'

Detection Priority:

  1. Topology: *.prmtop files
  2. Coordinates: *.inpcrd*.crd*.rst
  3. System PDB: system.pdb → any non-bilayer *.pdb
  4. Bilayer PDB: bilayer*_lipid.pdb (with CRYST1) → bilayer_*.pdb*_bilayer.pdb

Example 3:

from pathlib import Path
from gatewizard.tools.equilibration import NAMDEquilibrationManager

manager = NAMDEquilibrationManager(Path("popc_membrane"))

# Check which files will be used
system_files = manager.find_system_files()
if system_files:
    print("Detected files:")
    for key, path in system_files.items():
        print(f"  {key}: {Path(path).name}")

    # Now run setup with auto-detection
    #result = manager.setup_namd_equilibration(
    #    stage_params_list=stages
    #)
else:
    print("Required files not found - please check working directory")

# Output:
# Detected files:
#   prmtop: system.prmtop
#   inpcrd: system.inpcrd
#   pdb: system.pdb
#   bilayer_pdb: bilayer_protein_protonated_prepared_lipid.pdb

Core Method

Method: setup_namd_equilibration()

Complete equilibration setup with automatic file generation. This method:

  1. Auto-detects system files (if not provided)
  2. Creates output directory structure
  3. Copies system files (prmtop, inpcrd, pdb) to NAMD directory
  4. Generates NAMD configuration files for each stage
  5. Creates restraint files based on constraints
  6. Generates executable run script
  7. Creates protocol summary JSON
setup_namd_equilibration(
    system_files: Optional[Dict[str, str]] = None,
    stage_params_list: List[Dict[str, Any]] = None,
    output_name: str = "equilibration",
    scheme_type: str = "NPT",
    namd_executable: str = "namd3"
) -> Dict[str, Any]

Parameters:

Parameter Type Default Description
system_files Optional[Dict[str, str]] None System file paths (see below). If None, auto-detects files in working_dir
stage_params_list List[Dict[str, Any]] Required List of stage parameter dictionaries (see below). Each stage must include ensemble field
output_name str "equilibration" Base name for output folder
scheme_type Optional[str] None (Optional) Equilibration scheme (NVT, NPT, NPAT, NPgT). If None, auto-detected from first stage's ensemble field
namd_executable str "namd3" NAMD executable name/path

Returns: Dict[str, Any] with keys:

  • namd_dir (Path): NAMD output directory path
  • config_files (List[Path]): Generated .conf files
  • run_script (Path): Executable run script path
  • summary_file (Path): Protocol summary JSON path

System Files Dictionary

Auto-Detection (Recommended):

If system_files=None (default), the method will automatically search the working directory for:

  • *.prmtop - AMBER topology file
  • *.inpcrd, *.crd, or *.rst - AMBER coordinate file
  • system.pdb - System PDB file
  • bilayer*_lipid.pdb - Bilayer PDB with CRYST1 record (highest priority)

Manual Specification (Alternative):

If auto-detection fails or you need specific files, provide a dictionary with these keys:

Key Type Description
prmtop str Path to AMBER topology file (system.prmtop)
inpcrd str Path to AMBER coordinate file (system.inpcrd)
pdb str Path to system PDB file (system.pdb)
bilayer_pdb str REQUIRED Path to bilayer PDB with CRYST1 record

Important: CRYST1 Box Dimensions

The bilayer_pdb file must contain a CRYST1 record that defines the periodic box dimensions. This file is typically:

  • Named bilayer_*_lipid.pdb (from packmol-memgen --parametrize)
  • Generated during system preparation
  • Contains the header: CRYST1 70.335 70.833 85.067 90.00 90.00 90.00 P 1

Warning: Without proper CRYST1 information, NAMD will use incorrect box dimensions, leading to simulation failures.

Stage Parameters Dictionary

Each stage in stage_params_list must be a dictionary with the following structure:

Required Parameters:

Parameter Type Description
name str Stage name (e.g., "Equilibration 1"). Note: Names are optional and used primarily for logging and user feedback. The actual configuration file names are generated sequentially (step1, step2, etc.) regardless of the name you provide
time_ns float Simulation time in nanoseconds
steps int Number of MD steps
ensemble str Ensemble type (NVT, NPT, NPAT, NPgT)
temperature float Temperature in Kelvin
timestep float Timestep in femtoseconds (1.0 or 2.0)
constraints Dict[str, float] Restraint forces (see below)

Optional Parameters:

Parameter Type Default Description
minimize_steps int 0 Minimization steps before MD (first stage only)
pressure float 1.0 Pressure in bar (NPT, NPAT, NPgT only)
surface_tension float 0.0 Surface tension in dyn/cm (NPAT only)
dcd_freq int 5000 DCD output frequency (steps)
use_gpu bool True Enable GPU acceleration
cpu_cores int 1 Number of CPU cores
gpu_id int 0 GPU device ID
num_gpus int 1 Number of GPUs to use
custom_template str None Explicitly specify CHARMM-GUI template file (e.g., 'step6.3_equilibration.inp')

Constraints Dictionary:

Restraint forces in kcal/mol/Ų. Applied to atomic groups:

Key Description Typical Values
protein_backbone Protein backbone atoms (CA, C, N, O) 10.0 → 5.0 → 1.0 → 0.0
protein_sidechain Protein sidechain heavy atoms 5.0 → 2.5 → 0.5 → 0.0
lipid_head Lipid head group atoms (P, O) 5.0 → 2.5 → 1.0 → 0.0
lipid_tail Lipid tail carbon atoms 5.0 → 2.5 → 0.5 → 0.0
water Water molecules (usually unrestrained) 0.0
ions Ion atoms (K+, Cl-, etc.) 10.0 → 0.0
other Other molecules/ligands 0.0

Ensemble Types

Ensemble Full Name Control Use Case
NVT Canonical Temperature Initial heating, fixed box
NPT Isothermal-isobaric Temp + Pressure (isotropic) General equilibration
NPAT Constant surface tension Temp + Pressure (anisotropic) + Surface tension Membrane systems (recommended)
NPgT Constant surface tension Temp + Pressure + Surface tension Alternative membrane ensemble

Advanced Features

Per-Stage Ensemble Selection

Each stage can use a different ensemble by specifying the ensemble field. This allows flexible equilibration protocols:

stages = [
    {'name': 'Equilibration 1', 'ensemble': 'NVT', ...},   # Heat with NVT
    {'name': 'Equilibration 2', 'ensemble': 'NPT', ...},   # Equilibrate with NPT
    {'name': 'Equilibration 3', 'ensemble': 'NPAT', ...},  # Relax membrane with NPAT
]

How it works:

  • The scheme_type parameter is auto-detected from the first stage's ensemble field
  • Each stage uses its own ensemble to select the appropriate CHARMM-GUI template
  • When a stage's ensemble differs from the protocol default, a warning is logged
  • Templates are automatically loaded from the correct ensemble folder (01_NVT, 02_NPT, 03_NPAT, 04_NPgT)

Example: Mixed Ensemble Protocol

stages = [
    {
        'name': 'Initial Heating',
        'ensemble': 'NVT',      # Uses NVT template
        'time_ns': 0.25,
        'temperature': 310.15,
        'timestep': 1.0,
        'minimize_steps': 10000,
        'constraints': {'protein_backbone': 10.0, ...}
    },
    {
        'name': 'Pressure Equilibration',
        'ensemble': 'NPT',      # Uses NPT template (warning logged)
        'time_ns': 0.5,
        'temperature': 310.15,
        'pressure': 1.0,
        'timestep': 1.0,
        'constraints': {'protein_backbone': 5.0, ...}
    },
    {
        'name': 'Membrane Relaxation',
        'ensemble': 'NPAT',     # Uses NPAT template (warning logged)
        'time_ns': 1.0,
        'temperature': 310.15,
        'pressure': 1.0,
        'surface_tension': 0.0,
        'timestep': 2.0,
        'constraints': {'protein_backbone': 1.0, ...}
    }
]

# Setup automatically detects scheme_type='NVT' from first stage
# Stages 2-3 use different ensembles with appropriate templates
result = manager.setup_namd_equilibration(stage_params_list=stages)

Output:

INFO - Auto-detected scheme_type from stages: NVT
INFO - Generated: step1_equilibration.conf
WARNING - Stage 2 (Pressure Equilibration) uses ensemble 'NPT' but protocol default is 'NVT'. Using 'NPT' template.
INFO - Generated: step2_equilibration.conf
WARNING - Stage 3 (Membrane Relaxation) uses ensemble 'NPAT' but protocol default is 'NVT'. Using 'NPAT' template.
INFO - Generated: step3_equilibration.conf

Custom Template Selection

For advanced control, explicitly specify which CHARMM-GUI template to use with the custom_template parameter:

stages = [
    {
        'name': 'Strong Restraints',
        'ensemble': 'NPT',
        'custom_template': 'step6.1_equilibration.inp',  # Use template from stage 1
        'time_ns': 0.25,
        'constraints': {'protein_backbone': 10.0, ...}
    },
    {
        'name': 'Medium Restraints',
        'ensemble': 'NPT',
        'custom_template': 'step6.3_equilibration.inp',  # Use template from stage 3
        'time_ns': 0.5,
        'constraints': {'protein_backbone': 5.0, ...}
    },
    {
        'name': 'Light Restraints',
        'ensemble': 'NPAT',
        'custom_template': 'step6.5_equilibration.inp',  # Use template from stage 5
        'time_ns': 1.0,
        'constraints': {'protein_backbone': 1.0, ...}
    }
]

Use cases for custom templates:

  • Skip intermediate equilibration stages (use step6.5 directly)
  • Repeat a specific stage with different parameters
  • Mix templates from different equilibration phases
  • Test different template configurations

Available templates per ensemble:

  • step6.1_equilibration.inp through step6.6_equilibration.inp (6 equilibration stages)
  • step7_production.inp (production stage)

MDAnalysis Atom Counts and Selection Editing (GUI)

When using the Equilibration GUI frame, the constraint section for each stage now displays:

  • Atom count labels next to each constraint entry, showing how many atoms match the selection
  • Gear button (⚙) on each constraint row to edit the MDAnalysis selection string
  • Add Selection button (+) at the bottom of each stage to add custom named selections
  • Auto-detect ligands when an input folder is selected — non-standard residues are automatically added as ligand_<RESNAME> entries

Gear Button (Selection Editor):

Clicking the gear icon opens a modal dialog where you can:

  • View the current MDAnalysis selection string
  • Edit the selection expression
  • Click Test to count matching atoms in the loaded PDB
  • Click Apply to save the modified selection

Add Selection Button:

Clicking the + button opens a dialog to:

  • Enter a custom name (e.g., drug_A)
  • Provide an MDAnalysis selection string (e.g., resname LIG and around 5 protein)
  • Set the default restraint force (kcal/mol/Ų)
  • Test the selection before adding

New selections are added to all stages in the protocol.


Custom Stage Names

Stage names can be anything - they don't need to follow the "Equilibration N" convention:

stages = [
    {'name': 'Initial Heating', ...},           # Maps to step1
    {'name': 'Pressure Equilibration', ...},    # Maps to step2
    {'name': 'Membrane Relaxation', ...},       # Maps to step3
    {'name': 'Production Preparation', ...},    # Maps to step4
]

How it works:

  • Custom names are automatically mapped to sequential step numbers (step1, step2, step3, ...)
  • Config files use sequential naming: step1_equilibration.conf, step2_equilibration.conf, etc.
  • Restart files are properly chained: step2 loads from step1, step3 loads from step2, etc.
  • inputname variables are correctly set for all stages

Working Examples

Example 4: Three-Stage Protocol

from pathlib import Path
from gatewizard.tools.equilibration import NAMDEquilibrationManager

# Point to system folder
work_dir = Path(__file__).parent / "popc_membrane"

stages = [
    {
        'name': 'Equilibration 1',
        'time_ns': 0.125,
        'steps': 125000,
        'ensemble': 'NVT',
        'temperature': 303.15,
        'timestep': 1.0,
        'minimize_steps': 10000,
        'constraints': {
            'protein_backbone': 10.0,
            'protein_sidechain': 5.0,
            'lipid_head': 2.5,
            'lipid_tail': 2.5,
            'water': 0.0,
            'ions': 10.0,
            'other': 0.0
        }
    },
    {
        'name': 'Equilibration 2',
        'time_ns': 0.125,
        'steps': 125000,
        'ensemble': 'NVT',
        'temperature': 303.15,
        'timestep': 1.0,
        'constraints': {
            'protein_backbone': 5.0,
            'protein_sidechain': 2.5,
            'lipid_head': 2.5,
            'lipid_tail': 2.5,
            'water': 0.0,
            'ions': 0.0,
            'other': 0.0
        }
    },
    {
        'name': 'Equilibration 3',
        'time_ns': 0.125,
        'steps': 125000,
        'ensemble': 'NPT',
        'temperature': 303.15,
        'pressure': 1.0,
        'timestep': 1.0,
        'constraints': {
            'protein_backbone': 2.5,
            'protein_sidechain': 1.0,
            'lipid_head': 1.0,
            'lipid_tail': 1.0,
            'water': 0.0,
            'ions': 0.0,
            'other': 0.0
        }
    }
]

# Auto-detect files and setup
# scheme_type auto-detected from stages (NVT from first stage)
# Stage 3 uses NPT ensemble - warning will be logged
manager = NAMDEquilibrationManager(work_dir)
result = manager.setup_namd_equilibration(
    stage_params_list=stages,
    output_name="equilibration_example_04",
    namd_executable="namd3"
)

print(f"\n✓ Setup complete!")
print(f"  Config files: {len(result['config_files'])}")
print(f"  Run script: {result['run_script'].name}")

Example 5: Custom Four-Stage Protocol

from pathlib import Path
from gatewizard.tools.equilibration import NAMDEquilibrationManager

# Point to system folder
work_dir = Path(__file__).parent / "popc_membrane"
system_files = {
    'prmtop': str(work_dir / 'system.prmtop'),
    'inpcrd': str(work_dir / 'system.inpcrd'),
    'pdb': str(work_dir / 'system.pdb'),
    'bilayer_pdb': str(work_dir / 'bilayer_protein_protonated_prepared_lipid.pdb')

custom_protocol = [
    {
        'name': 'Initial Equilibration',
        'time_ns': 0.25,
        'steps': 250000,
        'ensemble': 'NVT',
        'temperature': 310.15,
        'timestep': 1.0,
        'minimize_steps': 10000,
        'constraints': {
            'protein_backbone': 10.0,
            'protein_sidechain': 5.0,
            'lipid_head': 5.0,
            'lipid_tail': 5.0,
            'water': 0.0,
            'ions': 10.0,
            'other': 0.0
        }
    },
    {
        'name': 'Pressure Equilibration',
        'time_ns': 0.5,
        'steps': 500000,
        'ensemble': 'NPT',
        'temperature': 310.15,
        'pressure': 1.0,
        'timestep': 1.0,
        'constraints': {
            'protein_backbone': 5.0,
            'protein_sidechain': 2.5,
            'lipid_head': 2.5,
            'lipid_tail': 2.5,
            'water': 0.0,
            'ions': 0.0,
            'other': 0.0
        }
    },
    {
        'name': 'Membrane Relaxation',
        'time_ns': 1.0,
        'steps': 500000,
        'ensemble': 'NPAT',
        'temperature': 310.15,
        'pressure': 1.0,
        'surface_tension': 0.0,
        'timestep': 2.0,
        'constraints': {
            'protein_backbone': 2.0,
            'protein_sidechain': 1.0,
            'lipid_head': 1.0,
            'lipid_tail': 0.5,
            'water': 0.0,
            'ions': 0.0,
            'other': 0.0
        }
    },
    {
        'name': 'Production Preparation',
        'time_ns': 2.0,
        'steps': 1000000,
        'ensemble': 'NPAT',
        'temperature': 310.15,
        'pressure': 1.0,
        'surface_tension': 0.0,
        'timestep': 2.0,
        'constraints': {
            'protein_backbone': 0.5,
            'protein_sidechain': 0.0,
            'lipid_head': 0.0,
            'lipid_tail': 0.0,
            'water': 0.0,
            'ions': 0.0,
            'other': 0.0
        }
    }
]

# Auto-detect and setup
# scheme_type auto-detected from stages (NVT from first stage)
# Stages 2-4 use different ensembles - warnings will be logged
manager = NAMDEquilibrationManager(work_dir)
result = manager.setup_namd_equilibration(
    stage_params_list=custom_protocol,
    output_name="equilibration_example_05",
    namd_executable="namd3"
)

print(f"\n✓ Setup complete!")
print(f"  Total stages: {len(custom_protocol)}")
print(f"  Total time: {sum(s['time_ns'] for s in custom_protocol):.1f} ns")
print(f"\nTo run:")
print(f"  cd {result['namd_dir']}")
print(f"  ./run_equilibration.sh")

Example 6: Complete CHARMM-GUI 7-Stage Protocol in NPT ensemble

from pathlib import Path
from gatewizard.tools.equilibration import NAMDEquilibrationManager

# System folder
work_dir = Path(__file__).parent / "popc_membrane"

stages = [
    {
        'name': 'Equilibration 1',
        'time_ns': 0.125,
        'steps': 125000,
        'ensemble': 'NPT',
        'temperature': 303.15,
        'minimize_steps': 10000,
        'timestep': 1.0,
        'constraints': {
            'protein_backbone': 10.0,
            'protein_sidechain': 5.0,
            'lipid_head': 2.5,
            'lipid_tail': 2.5,
            'water': 0.0,
            'ions': 10.0,
            'other': 0.0
        }
    },
    {
        'name': 'Equilibration 2',
        'time_ns': 0.125,
        'steps': 125000,
        'ensemble': 'NPT',
        'temperature': 303.15,
        'timestep': 1.0,
        'constraints': {
            'protein_backbone': 5.0,
            'protein_sidechain': 2.5,
            'lipid_head': 2.5,
            'lipid_tail': 2.5,
            'water': 0.0,
            'ions': 0.0,
            'other': 0.0
        }
    },
    {
        'name': 'Equilibration 3',
        'time_ns': 0.125,
        'steps': 125000,
        'ensemble': 'NPT',
        'temperature': 303.15,
        'pressure': 1.0,
        'surface_tension': 0.0,
        'timestep': 1.0,
        'constraints': {
            'protein_backbone': 2.5,
            'protein_sidechain': 1.0,
            'lipid_head': 1.0,
            'lipid_tail': 1.0,
            'water': 0.0,
            'ions': 0.0,
            'other': 0.0
        }
    },
    {
        'name': 'Equilibration 4',
        'time_ns': 0.5,
        'steps': 250000,
        'ensemble': 'NPT',
        'temperature': 303.15,
        'pressure': 1.0,
        'surface_tension': 0.0,
        'timestep': 2.0,
        'constraints': {
            'protein_backbone': 1.0,
            'protein_sidechain': 0.5,
            'lipid_head': 0.5,
            'lipid_tail': 0.5,
            'water': 0.0,
            'ions': 0.0,
            'other': 0.0
        }
    },
    {
        'name': 'Equilibration 5',
        'time_ns': 0.5,
        'steps': 250000,
        'ensemble': 'NPT',
        'temperature': 303.15,
        'pressure': 1.0,
        'surface_tension': 0.0,
        'timestep': 2.0,
        'constraints': {
            'protein_backbone': 0.5,
            'protein_sidechain': 0.1,
            'lipid_head': 0.1,
            'lipid_tail': 0.1,
            'water': 0.0,
            'ions': 0.0,
            'other': 0.0
        }
    },
    {
        'name': 'Equilibration 6',
        'time_ns': 0.5,
        'steps': 250000,
        'ensemble': 'NPT',
        'temperature': 303.15,
        'pressure': 1.0,
        'surface_tension': 0.0,
        'timestep': 2.0,
        'constraints': {
            'protein_backbone': 0.1,
            'protein_sidechain': 0.0,
            'lipid_head': 0.0,
            'lipid_tail': 0.0,
            'water': 0.0,
            'ions': 0.0,
            'other': 0.0
        }
    },
    {
        'name': 'Production',
        'time_ns': 10.0,
        'steps': 5000000,
        'ensemble': 'NPT',
        'temperature': 303.15,
        'pressure': 1.0,
        'surface_tension': 0.0,
        'timestep': 2.0,
        'constraints': {
            'protein_backbone': 0.0,
            'protein_sidechain': 0.0,
            'lipid_head': 0.0,
            'lipid_tail': 0.0,
            'water': 0.0,
            'ions': 0.0,
            'other': 0.0
        }
    }
]

# Auto-detect and setup
# scheme_type auto-detected from first stage's ensemble
manager = NAMDEquilibrationManager(work_dir)
result = manager.setup_namd_equilibration(
    stage_params_list=stages,
    output_name="equilibration_example_06",
    namd_executable="namd3"
)

print(f"\n✓ Complete! Generated {len(result['config_files'])} configuration files")
print(f"  Total equilibration: {sum(s['time_ns'] for s in stages[:-1]):.3f} ns")
print(f"  Production: {stages[-1]['time_ns']:.1f} ns")

Example 8: MDAnalysis Selections for Restraints

This example demonstrates the MDAnalysis-based selection system for precise atom counting and restraint generation, including auto-detection of non-standard residues (ligands, ions):

from pathlib import Path
from gatewizard.tools.equilibration import NAMDEquilibrationManager

# Point to the system folder
work_dir = Path("popc_membrane")
system_pdb = work_dir / "bilayer_protein_protonated_prepared_lipid.pdb"

manager = NAMDEquilibrationManager(work_dir)

# 1. Inspect default selections and atom counts
for name, sel in NAMDEquilibrationManager.DEFAULT_SELECTIONS.items():
    count = NAMDEquilibrationManager.count_selection_atoms(str(system_pdb), sel)
    print(f"  {name:25s}{count:>7d} atoms")

# 2. Auto-detect ligands / non-standard residues
all_sels = NAMDEquilibrationManager.get_default_selections(str(system_pdb))
for name, sel in all_sels.items():
    if name.startswith("ligand_"):
        count = NAMDEquilibrationManager.count_selection_atoms(str(system_pdb), sel)
        print(f"  {name:25s}{count:>7d} atoms  |  {sel}")

# 3. Count all selections at once
counts = NAMDEquilibrationManager.count_all_selections(str(system_pdb))

# 4. Generate restraints PDB via MDAnalysis selections
output_file = work_dir / "namd" / "restraints" / "step1_restraints.pdb"
selections_with_forces = {
    "protein_backbone":  ("protein and backbone", 10.0),
    "protein_sidechain": ("protein and not backbone", 5.0),
    "lipid_head":        (NAMDEquilibrationManager.DEFAULT_SELECTIONS["lipid_head"], 2.5),
    "lipid_tail":        (NAMDEquilibrationManager.DEFAULT_SELECTIONS["lipid_tail"], 2.5),
    "water":             (NAMDEquilibrationManager.DEFAULT_SELECTIONS["water"], 0.0),
    "ions":              (NAMDEquilibrationManager.DEFAULT_SELECTIONS["ions"], 10.0),
}
# Add any auto-detected ligand with force 1.0
for name, sel in all_sels.items():
    if name.startswith("ligand_"):
        selections_with_forces[name] = (sel, 1.0)

manager.generate_restraints_file_mda(
    system_pdb, selections_with_forces, output_file,
    stage_name="Equilibration 1",
)

# 5. Or use the high-level API with selections parameter
constraints = {"protein_backbone": 10.0, "protein_sidechain": 5.0, "lipid_head": 2.5,
               "lipid_tail": 2.5, "water": 0.0, "ions": 10.0}
selections = {name: sel for name, (sel, _) in selections_with_forces.items()}

manager.generate_restraints_file(
    system_pdb, constraints, output_file,
    stage_name="Eq1", selections=selections,
)

Output:

  protein_backbone            →      204 atoms
  protein_sidechain           →      488 atoms
  lipid_head                  →     2904 atoms
  lipid_tail                  →    13310 atoms
  water                       →    14685 atoms
  ions                        →        0 atoms
  other                       →       21 atoms
  ligand_Cl-                  →       10 atoms  |  resname Cl-
  ligand_K+                   →       11 atoms  |  resname K+


Example 7: Custom Template Selection

This example demonstrates explicit template control for advanced workflows:

from pathlib import Path
from gatewizard.tools.equilibration import NAMDEquilibrationManager

# System folder
work_dir = Path(__file__).parent / "popc_membrane"

system_files = {
    'prmtop': str(work_dir / 'system.prmtop'),
    'inpcrd': str(work_dir / 'system.inpcrd'),
    'pdb': str(work_dir / 'system.pdb'),
    'bilayer_pdb': str(work_dir / 'bilayer_protein_protonated_prepared_lipid.pdb')
}

# Use explicit templates to skip intermediate stages
stages = [
    {
        'name': 'Strong Restraints Phase',
        'ensemble': 'NPT',
        'custom_template': 'step6.1_equilibration.inp',  # Use stage 1 template
        'time_ns': 0.25,
        'timestep': 1.0,
        'temperature': 310.15,
        'pressure': 1.0,
        'minimize_steps': 10000,
        'constraints': {
            'protein_backbone': 10.0,
            'protein_sidechain': 5.0,
            'lipid_head': 5.0,
            'lipid_tail': 5.0,
            'water': 0.0,
            'ions': 10.0,
            'other': 0.0
        }
    },
    {
        'name': 'Medium Restraints Phase',
        'ensemble': 'NPT',
        'custom_template': 'step6.3_equilibration.inp',  # Skip to stage 3 template
        'time_ns': 0.5,
        'timestep': 1.0,
        'temperature': 310.15,
        'pressure': 1.0,
        'constraints': {
            'protein_backbone': 5.0,
            'protein_sidechain': 2.5,
            'lipid_head': 2.5,
            'lipid_tail': 2.5,
            'water': 0.0,
            'ions': 0.0,
            'other': 0.0
        }
    },
    {
        'name': 'Light Restraints Phase',
        'ensemble': 'NPAT',
        'custom_template': 'step6.5_equilibration.inp',  # Use stage 5 template
        'time_ns': 1.0,
        'timestep': 2.0,
        'temperature': 310.15,
        'pressure': 1.0,
        'surface_tension': 0.0,
        'constraints': {
            'protein_backbone': 1.0,
            'protein_sidechain': 0.5,
            'lipid_head': 0.5,
            'lipid_tail': 0.0,
            'water': 0.0,
            'ions': 0.0,
            'other': 0.0
        }
    }
]

# Setup with custom template selection
manager = NAMDEquilibrationManager(work_dir)
result = manager.setup_namd_equilibration(
    system_files=system_files,
    stage_params_list=stages,
    output_name="equilibration_example_07",
    namd_executable="namd3"
)

print(f"\n✓ Setup complete with custom templates!")
print(f"  Stage 1: Using template step6.1")
print(f"  Stage 2: Using template step6.3 (skipped step6.2)")
print(f"  Stage 3: Using template step6.5 (skipped step6.4)")

When to use custom templates:

  • Skip intermediate stages: Jump from step6.1 → step6.3 → step6.5
  • Repeat a stage: Use step6.2 multiple times with different parameters
  • Mix templates: Combine templates from different equilibration phases
  • Test protocols: Experiment with different template combinations

Best Practices

Restraint Progression

Recommended restraint schedule (kcal/mol/Ų):

Component Stage 1 Stage 2 Stage 3 Stage 4 Production
Protein backbone 10.0 5.0 2.5 1.0 0.0
Protein sidechain 5.0 2.5 1.0 0.5 0.0
Lipid heads 5.0 2.5 1.0 0.5 0.0
Lipid tails 5.0 2.5 1.0 0.0 0.0
Ions 10.0 0.0 0.0 0.0 0.0
Water 0.0 0.0 0.0 0.0 0.0

Guidelines:

  • Start with strong restraints (10.0) on protein backbone and ions
  • Gradually reduce restraints over 3-6 stages
  • Always keep water unrestrained (0.0)
  • Release ions early (after stage 1)
  • Release lipid tails before heads
  • Final stage should have minimal or zero restraints

Timestep Progression

Stage Timestep Restraints Notes
1-2 1.0 fs Strong (10.0-5.0) Initial equilibration, constrained
3-4 1.0-2.0 fs Medium (2.5-1.0) Transition to larger timestep
5+ 2.0 fs Light (< 1.0) Standard production timestep

Rules:

  • Use 1.0 fs with strong restraints (> 5.0 kcal/mol/Ų)
  • Switch to 2.0 fs when restraints < 2.5 kcal/mol/Ų
  • NAMD supports 2.0 fs with SHAKE/SETTLE for bonds to hydrogen
  • Never use > 2.0 fs for equilibration

Stage Lengths

Minimum recommended times:

Ensemble Stage Purpose Min Time Typical Time
NVT Initial heating 0.1 ns 0.125-0.25 ns
NPT Pressure equilibration 0.1 ns 0.125-0.5 ns
NPAT Membrane equilibration 0.5 ns 0.5-2.0 ns
Production Data collection 10 ns 50-500 ns

Total equilibration time:

  • Minimum: 1.0 ns (3-4 stages)
  • Standard: 2.0 ns (6 stages, CHARMM-GUI protocol)
  • Thorough: 3-5 ns (custom research protocols)

Ensemble Selection

For membrane protein systems:

Start with NVT (0.1-0.25 ns)

  • Heat system from 0 K to target temperature
  • Strong restraints on protein and ions
  • Fixed box dimensions

Switch to NPT (0.1-0.5 ns)

  • Equilibrate pressure (isotropic)
  • Medium restraints
  • Box can change size uniformly

Finish with NPAT (0.5-2.0 ns)

  • Equilibrate membrane (anisotropic pressure)
  • Light restraints
  • Lateral box dimensions independent from Z-axis
  • Recommended for membrane systems

Minimization

  • Use minimize_steps only in first stage
  • Typical value: 5000-10000 steps
  • Removes bad contacts/clashes
  • Always minimize before MD

GPU Acceleration

# Enable GPU acceleration (default)
stage = {
    'use_gpu': True,
    'gpu_id': 0,      # First GPU
    'num_gpus': 1,    # Single GPU
    'cpu_cores': 1    # Minimal CPU usage with GPU. Recommended > 4 depending on the system´s size.
}

Output Structure

{output_name}/
└── namd/
    ├── system.prmtop              # Copied AMBER topology
    ├── system.inpcrd              # Copied AMBER coordinates  
    ├── system.pdb                 # Copied system PDB (for structure/restraints)
    ├── bilayer_*_lipid.pdb        # Copied bilayer PDB (for CRYST1 box info only)
    ├── step1.conf                 # Stage 1 config
    ├── step2.conf                 # Stage 2 config
    ├── ...
    ├── run_equilibration.sh       # Executable run script
    ├── protocol_summary.json      # Protocol metadata
    ├── restraints/
        ├── step1_equilibration_restraints.pdb   # Stage 1 restraints
        ├── step2_equilibration_restraints.pdb   # Stage 2 restraints
    └── logs/
        ├── step1.log              # Stage 1 output
        ├── step2.log              # Stage 2 output
        └── ...

Generated Files:

File Purpose
system.pdb System structure for NAMD simulations and restraints
bilayer_*_lipid.pdb Original bilayer file (kept for reference, CRYST1 info read from this)
stepN.conf NAMD configuration for stage N
stepN_restraints.txt Harmonic restraints for stage N
run_equilibration.sh Bash script to run all stages sequentially
protocol_summary.json Protocol metadata (stages, parameters, files)
logs/stepN.log NAMD output log for stage N

Important Notes:

  • system.pdb is the actual system file used for simulations and restraint generation
  • bilayer_*_lipid.pdb is kept with its original name for reference and CRYST1 box dimensions
  • Both files are copied to the output directory but serve different purposes

Run Script Features:

  • Sequential execution of all stages
  • Automatic dependency management (restart files)
  • Progress tracking
  • Error handling
  • Timing information

Internal Methods (Advanced)

Class Attribute: DEFAULT_SELECTIONS

Default MDAnalysis selection strings for the seven standard restraint categories. These selections are used when MDAnalysis-based restraint generation is enabled.

NAMDEquilibrationManager.DEFAULT_SELECTIONS = {
    'protein_backbone':  'protein and backbone',
    'protein_sidechain': 'protein and not backbone',
    'lipid_head':        '(resname POPC POPE POPS DPPC ...) and (name P O11 O12 ...)',
    'lipid_tail':        '(resname POPC POPE POPS DPPC ...) and not (name P O11 ...)',
    'water':             'resname TIP3 HOH WAT SOL TIP4 SPC T3P T4P',
    'ions':              'resname NA CL K CA MG ZN FE CU SOD CLA POT CAL MAG ZIN IRN COP',
    'other':             'not (protein or lipids or water or ions)',
}

Each value is a valid MDAnalysis selection string. Users can override any selection through the GUI gear button or the API selections parameter.


Static Method: count_selection_atoms()

Count atoms matching an MDAnalysis selection expression.

NAMDEquilibrationManager.count_selection_atoms(pdb_path: str, selection: str) -> int

Parameters:

Parameter Type Description
pdb_path str Path to a PDB file
selection str MDAnalysis selection string

Returns: int — number of matching atoms (0 if selection is invalid or MDAnalysis unavailable)

count = NAMDEquilibrationManager.count_selection_atoms(
    "system.pdb", "protein and backbone"
)
print(f"Backbone atoms: {count}")

Static Method: get_default_selections()

Build the default selection dict, auto-detecting extra ligands / non-standard residues.

NAMDEquilibrationManager.get_default_selections(pdb_path: str) -> Dict[str, str]

Parameters:

Parameter Type Description
pdb_path str Path to a PDB file

Returns: Dict[str, str]{category_name: mda_selection_string, ...} including any auto-detected ligand_<RESNAME> entries.

The seven standard categories are always present. Any residue that falls into the other category is additionally split into individual ligand_<RESNAME> entries so users can assign per-ligand restraint forces.

sels = NAMDEquilibrationManager.get_default_selections("system.pdb")
for name, sel in sels.items():
    if name.startswith("ligand_"):
        print(f"  Detected: {name}{sel}")
# Output:
#   Detected: ligand_Cl- → resname Cl-
#   Detected: ligand_K+  → resname K+

Static Method: count_all_selections()

Count atoms for every selection in a dictionary in one call.

NAMDEquilibrationManager.count_all_selections(
    pdb_path: str,
    selections: Optional[Dict[str, str]] = None
) -> Dict[str, int]

Parameters:

Parameter Type Default Description
pdb_path str Required Path to a PDB file
selections Optional[Dict[str, str]] None {name: mda_selection_string}. If None, uses get_default_selections() (with auto-detected ligands)

Returns: Dict[str, int]{name: atom_count, ...}

counts = NAMDEquilibrationManager.count_all_selections("system.pdb")
for name, n in counts.items():
    print(f"  {name:25s}  {n:>7d} atoms")

Method: generate_restraints_file_mda()

Generate a restraints PDB using MDAnalysis selections instead of the built-in heuristic. Each entry maps a category name to a (mda_selection_string, force) tuple. For every ATOM/HETATM line the first matching selection determines the B-factor. Atoms matching no selection get B-factor 0.0.

generate_restraints_file_mda(
    system_pdb: Path,
    selections_with_forces: Dict[str, Tuple[str, float]],
    output_file: Path,
    stage_name: str = ""
) -> None

Parameters:

Parameter Type Description
system_pdb Path Path to the system PDB file
selections_with_forces Dict[str, Tuple[str, float]] {name: (selection_string, force), ...}
output_file Path Destination path for the restraints PDB
stage_name str Label for log messages (optional)
selections_with_forces = {
    "protein_backbone":  ("protein and backbone", 10.0),
    "protein_sidechain": ("protein and not backbone", 5.0),
    "lipid_head":        (DEFAULT_SELS["lipid_head"], 2.5),
    "lipid_tail":        (DEFAULT_SELS["lipid_tail"], 2.5),
    "water":             (DEFAULT_SELS["water"], 0.0),
    "ions":              (DEFAULT_SELS["ions"], 10.0),
    "ligand_Cl-":        ("resname Cl-", 1.0),
}

manager.generate_restraints_file_mda(
    system_pdb, selections_with_forces, output_file,
    stage_name="Equilibration 1",
)

Method: generate_restraints_file()

Generates restraint PDB files with B-factors encoding restraint forces for each atom type. Now supports optional MDAnalysis selections for precise atom classification.

generate_restraints_file(
    system_pdb: Path,
    constraints: Dict[str, float],
    output_file: Path,
    stage_name: str = "",
    selections: Optional[Dict[str, str]] = None
) -> None

Parameters:

Parameter Type Default Description
system_pdb Path Required Path to the system.pdb file (full system: protein + lipids + water + ions)
constraints Dict[str, float] Required Dictionary of restraint forces (kcal/mol/Ų) for each atom type
output_file Path Required Path for output restraints PDB file
stage_name str "" Stage name for logging (optional)
selections Optional[Dict[str, str]] None {constraint_name: mda_selection_string}. When provided, MDAnalysis is used instead of the built-in heuristic

Behavior:

When selections is None (legacy mode):

A. Classifies each atom by type using built-in residue/atom-name heuristics:

  • protein_backbone: CA, C, N, O atoms in protein residues
  • protein_sidechain: Heavy sidechain atoms in protein residues
  • lipid_head: P, O atoms in lipid head groups
  • lipid_tail: C atoms in lipid tails
  • water: H2O molecules (TIP3, HOH, WAT, SOL)
  • ions: Na+, Cl-, K+, Ca2+, Mg2+, etc.
  • other: Ligands and other molecules

B. Assigns B-factor values based on constraints dictionary

C. Writes restraints PDB file with modified B-factors

When selections is provided (MDAnalysis mode):

A. Pairs each constraint name with its MDAnalysis selection string

B. Delegates to generate_restraints_file_mda() for precise MDAnalysis-based classification

C. Useful for custom selections, auto-detected ligands, or non-standard residues

# Legacy mode (heuristic-based)
manager.generate_restraints_file(system_pdb, constraints, output_file)

# MDAnalysis mode (selection-based)
selections = {
    "protein_backbone": "protein and backbone",
    "protein_sidechain": "protein and not backbone",
    "ligand_Cl-": "resname Cl-",
}
manager.generate_restraints_file(
    system_pdb, constraints, output_file,
    selections=selections,
)

Example Output Log:

INFO - Generated restraints file: step1_equilibration_restraints.pdb
INFO - Source PDB: system.pdb
INFO - Stage: Equilibration 1
INFO - Total atoms processed: 45678
INFO -   protein_backbone: 1234 atoms, force = 10.0 kcal/mol/Ų
INFO -   protein_sidechain: 2345 atoms, force = 5.0 kcal/mol/Ų
INFO -   lipid_head: 512 atoms, force = 5.0 kcal/mol/Ų
INFO -   lipid_tail: 3456 atoms, force = 5.0 kcal/mol/Ų
INFO -   ions: 89 atoms, force = 10.0 kcal/mol/Ų


Method: generate_charmm_gui_config_file()

Generates NAMD configuration files using CHARMM-GUI templates with GateWizard customizations.

generate_charmm_gui_config_file(
    stage_name: str,
    stage_params: Dict[str, Any],
    stage_index: int,
    system_files: Dict[str, str],
    scheme_type: str,
    previous_stage_name: Optional[str] = None,
    all_stage_settings: Optional[Dict[str, Dict[str, Any]]] = None,
    force_scheme_type: bool = False
) -> str

Parameters:

Parameter Type Description
stage_name str Name of the equilibration stage
stage_params Dict[str, Any] Stage parameters dictionary
stage_index int Stage index (0-based)
system_files Dict[str, str] System file paths
scheme_type str CHARMM-GUI scheme type (NVT, NPT, NPAT, NPgT)
previous_stage_name Optional[str] Previous stage name for restart files
all_stage_settings Optional[Dict] All stages settings for context
force_scheme_type bool [NEW] If True, always use scheme_type for all stages (GUI mode)

Returns: NAMD configuration file content as string

Behavior:

API Mode (force_scheme_type=False, default):

  • Each stage can specify its own ensemble field
  • Template folder is selected based on the stage's ensemble value
  • Allows mixing NVT/NPT/NPAT templates across stages
  • Warning logged when stage ensemble differs from protocol scheme_type

GUI Mode (force_scheme_type=True):

  • ALL stages use the global scheme_type for template selection
  • Individual stage ensemble values are ignored for template selection
  • Ensures CHARMM-GUI protocol consistency when selected from GUI dropdown
  • Example: If "NPT" scheme selected, all stages use NPT templates (02_NPT folder)

Template Selection Logic:

  1. If custom_template specified → use that template file
  2. If force_scheme_type=True → use scheme_type for folder selection
  3. If force_scheme_type=False → use stage's ensemble for folder selection
  4. Templates loaded from: charmm_gui_templates/{scheme_folder}/{template_file}

Scheme to Folder Mapping:

  • NVT01_NVT/
  • NPT02_NPT/
  • NPAT03_NPAT/
  • NPgT04_NPgT/

Example (API Mode):

# Stage 1 uses NVT template, Stage 2 uses NPT template
config1 = manager.generate_charmm_gui_config_file(
    stage_name="Heating",
    stage_params={'ensemble': 'NVT', ...},
    stage_index=0,
    system_files=files,
    scheme_type='NVT',
    force_scheme_type=False  # API mode - use stage's ensemble
)

config2 = manager.generate_charmm_gui_config_file(
    stage_name="Pressure Eq",
    stage_params={'ensemble': 'NPT', ...},  # Different ensemble
    stage_index=1,
    system_files=files,
    scheme_type='NVT',  # Protocol default is NVT
    force_scheme_type=False  # Stage uses NPT template (warning logged)
)

Example (GUI Mode):

# User selected "NPT" scheme in GUI dropdown
# ALL stages use NPT templates regardless of individual ensemble values
config1 = manager.generate_charmm_gui_config_file(
    stage_name="Stage 1",
    stage_params={'ensemble': 'NVT', ...},  # Ignored for template selection
    stage_index=0,
    system_files=files,
    scheme_type='NPT',  # From GUI dropdown
    force_scheme_type=True  # GUI mode - enforce scheme_type
)
# Uses template from: 02_NPT/step6.1_equilibration.inp

config2 = manager.generate_charmm_gui_config_file(
    stage_name="Stage 2",
    stage_params={'ensemble': 'NVT', ...},  # Ignored for template selection
    stage_index=1,
    system_files=files,
    scheme_type='NPT',  # From GUI dropdown
    force_scheme_type=True  # GUI mode - enforce scheme_type
)
# Uses template from: 02_NPT/step6.2_equilibration.inp

Rationale for force_scheme_type:

The force_scheme_type parameter was added to support two different use cases:

  1. GUI Users: Select a CHARMM-GUI scheme (NPT, NPAT, etc.) and expect all stages to use templates from that scheme folder for consistency with CHARMM-GUI protocols.

  2. API Users: Have flexibility to mix different ensemble templates across stages for custom equilibration protocols (e.g., NVT → NPT → NPAT progression).


Common Issues

Missing CRYST1 Record

Error: Box dimensions not found in PDB file

Solution: Ensure bilayer_pdb contains CRYST1 record:

head bilayer_protein_protonated_prepared_lipid.pdb
# Should show: CRYST1   70.335   70.833   85.067  90.00  90.00  90.00 P 1

Correct file:

  • From packmol-memgen --parametrize step
  • Named bilayer_*_lipid.pdb
  • Contains proper PDB header with CRYST1
  • Not an intermediate Packmol file

Restraint File Errors

Error: Cannot generate restraint file OR restraints only applied to protein

Solution: Ensure system.pdb is used (not protein.pdb):

  • The restraints generation requires the full system.pdb file
  • This file must contain: protein + lipids + water + ions
  • Do NOT use protein.pdb (protein-only) for restraint generation
  • Check that system.pdb exists in the working directory or input folder
  • Verify atom names match AMBER topology
  • Verify segnames/chains are present (PROA, MEMB, SOLV)

Common mistake: Using protein.pdb instead of system.pdb results in:

  • No restraints applied to lipids (lipid_head, lipid_tail)
  • No restraints applied to ions
  • Membrane collapse during equilibration
  • System instability

Fix: The GUI now automatically uses system.pdb from the output directory where files are copied. If you see restraints being generated with protein.pdb, check:

  1. Is system.pdb present in the input folder?
  2. Is system.pdb copied to the output directory?
  3. Check the log for "Using PDB file for restraints generation: ..."

Timestep Too Large

Error: NAMD crashes with "Atoms moving too fast"

Solution: Reduce timestep or increase restraints:

  • Use 1.0 fs with restraints > 5.0 kcal/mol/Ų
  • Switch to 2.0 fs only when restraints < 2.5 kcal/mol/Ų
  • Add minimization step if starting from poor geometry

Ensemble Switching

Error: System unstable when switching NPT → NPAT

Solution: Ensure pressure is equilibrated first:

  • Run NPT for at least 0.25 ns before NPAT
  • Check box dimensions are stable in NPT
  • Use medium restraints during transition
  • Don't switch ensemble and reduce restraints simultaneously

See Also