From 49d63f1b9a5721ff91b4ce8c6b8628c8ba4c222b Mon Sep 17 00:00:00 2001 From: David-Araripe Date: Thu, 11 Dec 2025 14:27:21 +0100 Subject: [PATCH 1/5] apply shake to the hydrogens in the beginning to allow Q to solve suboptimal bond geometries from auto-added hydrogens --- src/QligFEP/INPUTS/eq1.inp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QligFEP/INPUTS/eq1.inp b/src/QligFEP/INPUTS/eq1.inp index e07f0b4f..6c74aeda 100644 --- a/src/QligFEP/INPUTS/eq1.inp +++ b/src/QligFEP/INPUTS/eq1.inp @@ -6,7 +6,7 @@ bath_coupling 0.2 random_seed SEED_VAR initial_temperature 1 shake_solvent on -shake_hydrogens off +shake_hydrogens on shake_solute off lrf on separate_scaling on From f33ec7fac966283b5b6559a19e102f432d39c099 Mon Sep 17 00:00:00 2001 From: David-Araripe Date: Thu, 11 Dec 2025 14:28:39 +0100 Subject: [PATCH 2/5] do not apply shake to the solvents on equilibration steps 2 and 3 --- src/QligFEP/INPUTS/eq2.inp | 2 +- src/QligFEP/INPUTS/eq3.inp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QligFEP/INPUTS/eq2.inp b/src/QligFEP/INPUTS/eq2.inp index c131bb22..93e9cb6f 100644 --- a/src/QligFEP/INPUTS/eq2.inp +++ b/src/QligFEP/INPUTS/eq2.inp @@ -5,7 +5,7 @@ temperature 50 bath_coupling STEPSIZE shake_hydrogens STEPTOGGLE shake_solute off -shake_solvent on +shake_solvent off lrf on separate_scaling on diff --git a/src/QligFEP/INPUTS/eq3.inp b/src/QligFEP/INPUTS/eq3.inp index 754baac0..5ec68863 100644 --- a/src/QligFEP/INPUTS/eq3.inp +++ b/src/QligFEP/INPUTS/eq3.inp @@ -5,7 +5,7 @@ temperature 150 bath_coupling STEPSIZE shake_hydrogens STEPTOGGLE shake_solute off -shake_solvent on +shake_solvent off lrf on separate_scaling on From fe6a150e8548922e93c5cd8f5d86dd114c12cae6 Mon Sep 17 00:00:00 2001 From: David-Araripe Date: Thu, 11 Dec 2025 16:41:40 +0100 Subject: [PATCH 3/5] set shake_solute to be always off --- src/QligFEP/INPUTS/eq5.inp | 2 +- src/QligFEP/INPUTS/md_0000_1000.inp | 2 +- src/QligFEP/INPUTS/md_0500_0500.inp | 2 +- src/QligFEP/INPUTS/md_1000_0000.inp | 2 +- src/QligFEP/INPUTS/md_XXXX_XXXX.inp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/QligFEP/INPUTS/eq5.inp b/src/QligFEP/INPUTS/eq5.inp index d3159fa5..8ef383c5 100644 --- a/src/QligFEP/INPUTS/eq5.inp +++ b/src/QligFEP/INPUTS/eq5.inp @@ -4,7 +4,7 @@ stepsize STEPSIZE temperature T_VAR bath_coupling 10.0 shake_hydrogens STEPTOGGLE -shake_solute STEPTOGGLE +shake_solute off shake_solvent on lrf on separate_scaling on diff --git a/src/QligFEP/INPUTS/md_0000_1000.inp b/src/QligFEP/INPUTS/md_0000_1000.inp index 2b27fa11..16f51874 100644 --- a/src/QligFEP/INPUTS/md_0000_1000.inp +++ b/src/QligFEP/INPUTS/md_0000_1000.inp @@ -4,7 +4,7 @@ stepsize STEPSIZE temperature T_VAR bath_coupling 10 shake_hydrogens STEPTOGGLE -shake_solute STEPTOGGLE +shake_solute off shake_solvent on lrf on separate_scaling on diff --git a/src/QligFEP/INPUTS/md_0500_0500.inp b/src/QligFEP/INPUTS/md_0500_0500.inp index dd4bd8f0..ac7d8a51 100644 --- a/src/QligFEP/INPUTS/md_0500_0500.inp +++ b/src/QligFEP/INPUTS/md_0500_0500.inp @@ -4,7 +4,7 @@ stepsize STEPSIZE temperature T_VAR bath_coupling 10 shake_hydrogens STEPTOGGLE -shake_solute STEPTOGGLE +shake_solute off shake_solvent on lrf on separate_scaling on diff --git a/src/QligFEP/INPUTS/md_1000_0000.inp b/src/QligFEP/INPUTS/md_1000_0000.inp index b6d0c94e..ccdee6c9 100644 --- a/src/QligFEP/INPUTS/md_1000_0000.inp +++ b/src/QligFEP/INPUTS/md_1000_0000.inp @@ -4,7 +4,7 @@ stepsize STEPSIZE temperature T_VAR bath_coupling 10 shake_hydrogens STEPTOGGLE -shake_solute STEPTOGGLE +shake_solute off shake_solvent on lrf on separate_scaling on diff --git a/src/QligFEP/INPUTS/md_XXXX_XXXX.inp b/src/QligFEP/INPUTS/md_XXXX_XXXX.inp index 344a1cda..9dc0317a 100644 --- a/src/QligFEP/INPUTS/md_XXXX_XXXX.inp +++ b/src/QligFEP/INPUTS/md_XXXX_XXXX.inp @@ -4,7 +4,7 @@ stepsize STEPSIZE temperature T_VAR bath_coupling 10 shake_hydrogens STEPTOGGLE -shake_solute STEPTOGGLE +shake_solute off shake_solvent on lrf on separate_scaling on From 2b003672578c4e24dcde3fd4c68c3a257e1ab0a9 Mon Sep 17 00:00:00 2001 From: David-Araripe Date: Thu, 11 Dec 2025 21:41:52 +0100 Subject: [PATCH 4/5] fix bug when passing a custom experimental key --- src/QligFEP/analyze_FEP.py | 49 ++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/src/QligFEP/analyze_FEP.py b/src/QligFEP/analyze_FEP.py index b9c945ad..f7aee372 100644 --- a/src/QligFEP/analyze_FEP.py +++ b/src/QligFEP/analyze_FEP.py @@ -442,8 +442,18 @@ def load_experimental_data(self, exp_key: str): _to = self.data[self.system][fep]["to"] # search ligands in the edges + ddG = None for edge in self.mapping_json["edges"]: if edge["from"] == _from and edge["to"] == _to: + if exp_key not in edge: + logger.error( + f"Experimental key '{exp_key}' not found in edge {_from} -> {_to}. " + f"Available keys in this edge: {', '.join(edge.keys())}. " + f"Please check your mapping JSON file and ensure the experimental key is correct." + ) + raise KeyError( + f"Key '{exp_key}' not found in edge data. Check your mapping JSON file." + ) ddG = edge[exp_key] break @@ -489,15 +499,40 @@ def populate_mapping_dictionary(self, method, output_file: str | Path | None = N with output_file.open("w") as f: json.dump(self.mapping_json, f, indent=4) - @staticmethod - def prepare_df(json_dict, experimental_data: bool = True): - pref = "dg" if "dg_error" in json_dict["edges"][0] else "ddg" + def prepare_df(self, json_dict, experimental_data: bool = True): df = pd.DataFrame(json_dict["edges"]) if experimental_data: + if self.exp_key is None: + logger.error( + "No experimental key has been set. Call load_experimental_data() first " + "or set experimental_data=False." + ) + raise ValueError("exp_key is None - cannot prepare dataframe with experimental data") + + # For custom keys, edges have "{key}", for our suite they have "ddg_value" or "dg_value" + # First try the key directly for custom keys + expected_col = f"{self.exp_key}" + if expected_col not in df.columns: + # Fall back to checking for standard naming (dg_value/ddg_value) + if "dg_value" in df.columns: + expected_col = "dg_value" + elif "ddg_value" in df.columns: + expected_col = "ddg_value" + else: + available_cols = [col for col in df.columns if "delta_" in col or "_value" in col or "dg" in col.lower()] + logger.error( + f"Expected experimental data column 'delta_{self.exp_key}' not found in edges data. " + f"Available columns that might contain experimental data: {', '.join(available_cols) if available_cols else 'none'}. " + f"Please check that your experimental key '{self.exp_key}' matches the data in your mapping JSON file." + ) + raise KeyError( + f"Column 'delta_{self.exp_key}' not found. Check your mapping JSON file has the correct experimental data." + ) + df = ( df.assign( - ddg_value=lambda x: x[pref + "_value"], - residual=lambda x: x[pref + "_value"] - x["Q_ddG_avg"], + ddg_value=lambda x: x[expected_col], + residual=lambda x: x[expected_col] - x["Q_ddG_avg"], residual_abs=lambda x: x["residual"].abs(), ) .sort_values("residual_abs", ascending=False) @@ -646,7 +681,7 @@ def result_to_latex(res, latexify_each=False): # TODO: move this out of this me # set labels, make it square and add legend plt.title( f"{(target_name + ' ' if target_name is not None else '')}" - r"$\Delta\Delta \text{G}_{\text{BAR}}$ ($\mathrm{N}=" + r"$\Delta\Delta \mathrm{G}_{\mathrm{BAR}}$ ($\mathrm{N}=" f"{len(exp_values)}$)" ) plt.xlabel("$\Delta\Delta G_{exp} (kcal/mol)$") # noqa: W605 @@ -887,7 +922,7 @@ def main(args: argparse.Namespace): results_df = fep_reader.prepare_df(results_json) if fep_reader.ignored_edges: results_df = results_df.query("~fep_name.isin(@fep_reader.ignored_edges)").reset_index(drop=True) - fig, ax = fep_reader.create_ddG_plot(results_df=results_df) + fig, _ = fep_reader.create_ddG_plot(results_df=results_df) fig.savefig(f"{args.target}_ddG_plot.png", dpi=300, bbox_inches="tight") else: results_json = json.loads((Path.cwd() / results_file).read_text()) From 1fae2c07a9f306d6a598f77741eb0ffce0ec35c6 Mon Sep 17 00:00:00 2001 From: David-Araripe Date: Thu, 11 Dec 2025 21:48:07 +0100 Subject: [PATCH 5/5] improve comments and error messages --- src/QligFEP/analyze_FEP.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/QligFEP/analyze_FEP.py b/src/QligFEP/analyze_FEP.py index f7aee372..33139ed8 100644 --- a/src/QligFEP/analyze_FEP.py +++ b/src/QligFEP/analyze_FEP.py @@ -509,11 +509,10 @@ def prepare_df(self, json_dict, experimental_data: bool = True): ) raise ValueError("exp_key is None - cannot prepare dataframe with experimental data") - # For custom keys, edges have "{key}", for our suite they have "ddg_value" or "dg_value" - # First try the key directly for custom keys + # For custom keys, edges have "delta_{node key}", which needs to be passed by the user + # The keys we use for experimental values are "ddg_value" (edge) or "dg_value" (node) expected_col = f"{self.exp_key}" if expected_col not in df.columns: - # Fall back to checking for standard naming (dg_value/ddg_value) if "dg_value" in df.columns: expected_col = "dg_value" elif "ddg_value" in df.columns: @@ -521,12 +520,12 @@ def prepare_df(self, json_dict, experimental_data: bool = True): else: available_cols = [col for col in df.columns if "delta_" in col or "_value" in col or "dg" in col.lower()] logger.error( - f"Expected experimental data column 'delta_{self.exp_key}' not found in edges data. " + f"Expected experimental data column '{self.exp_key}' not found in edges data. " f"Available columns that might contain experimental data: {', '.join(available_cols) if available_cols else 'none'}. " - f"Please check that your experimental key '{self.exp_key}' matches the data in your mapping JSON file." + f"Please check for the correct key with experimental value on your mapping JSON file." ) raise KeyError( - f"Column 'delta_{self.exp_key}' not found. Check your mapping JSON file has the correct experimental data." + f"Column '{self.exp_key}' not found. Check your mapping JSON file has the correct experimental data." ) df = (