Source code for rgpycrumbs.plumed.direct_reconstruction

# /// script
# requires-python = "<3.10"
# dependencies = [
#  "pandas", "numpy", "matplotlib", "cmcrameri", "chemparseplot"
# ]
# ///

import sys

import numpy as np
from chemparseplot.parse.plumed import calculate_fes_from_hills, find_fes_minima

# --- Example Usage ---
if __name__ == "__main__":
[docs] HILLS_FILENAME = "HILLS"
# [CV1_is_periodic, CV2_is_periodic] # For a 1D FES, only the first value is used. IS_PERIODIC = [False, False] # If periodic, what are the boundaries? E.g., [-np.pi, np.pi] or [0, 360] # Set to None if not periodic. PERIODIC_BOUNDS_CV1 = None PERIODIC_BOUNDS_CV2 = None # How many points should the FES grid have in each dimension? N_POINTS_GRID = 128 # To reconstruct the FES using only the first N hills, set a number. # Set to None to use all hills in the file. MAX_HILLS_TO_USE = None # --- 2. LOAD DATA AND RUN CALCULATION --- try: print(f"Loading data from '{HILLS_FILENAME}'...") # Plumed HILLS files often start with comment lines like "#! FIELDS ..." # np.loadtxt handles these automatically. hills_data = np.loadtxt(HILLS_FILENAME) print(f"Successfully loaded {hills_data.shape[0]} hills.") except FileNotFoundError: print(f"\nError: The file '{HILLS_FILENAME}' was not found.") print("Please make sure the file is in the same directory as the script,") print("or change the HILLS_FILENAME variable.") sys.exit(1) # Exit the script except Exception as e: print(f"\nAn error occurred while loading the file: {e}") sys.exit(1) num_hills, num_cols = hills_data.shape # Automatically detect dimension and prepare the calculation if num_cols >= 7: # 2D Case print("Detected 2D data (>= 7 columns).") hills_dict = { "hillsfile": hills_data, "per": IS_PERIODIC, "pcv1": PERIODIC_BOUNDS_CV1, "pcv2": PERIODIC_BOUNDS_CV2, } elif num_cols >= 5: # 1D Case print("Detected 1D data (>= 5 columns).") hills_dict = { "hillsfile": hills_data, "per": [IS_PERIODIC[0]], # Use only the first periodicity flag "pcv1": PERIODIC_BOUNDS_CV1, "pcv2": None, # Not used for 1D } else: print(f"\nError: Unsupported number of columns ({num_cols}) in the HILLS file.") print("Expected >= 5 for 1D or >= 7 for 2D.") sys.exit(1) # Calculate the FES print("Calculating Free Energy Surface...") fes_result = calculate_fes_from_hills( hills_dict, imax=MAX_HILLS_TO_USE, npoints=N_POINTS_GRID ) print("Calculation complete.") # --- 3. FIND AND PRINT MINIMA --- print("\nSearching for FES minima...") try: # Shift the FES to have a minimum of 0 before finding minima fes_result["fes"] -= np.min(fes_result["fes"]) minima_result = find_fes_minima(fes_result, nbins=8) if minima_result: print("Found minima:") print(minima_result["minima"].to_string()) else: print("No local minima found with the current nbins setting.") except ValueError as e: print(f"Could not find minima: {e}") minima_result = None # --- 4. PLOT THE RESULTS --- try: from chemparseplot.plot.plumed import plot_fes_1d, plot_fes_2d print("\nPlotting results...") if fes_result["dimension"] == 2: fig = plot_fes_2d(fes_result, minima_result) elif fes_result["dimension"] == 1: fig = plot_fes_1d(fes_result, minima_result) import matplotlib.pyplot as plt plt.savefig("fes_plot_with_minima.png", dpi=300) print("Saved plot to fes_plot_with_minima.png") plt.show() except ImportError: print("\nRequired plotting dependencies not found. Skipping plots.") except Exception as e: print(f"\nAn error occurred during plotting: {e}")