Se realizaron 6 simulaciones de dinámica molecular (10 ns c/u, GPU RTX 5080) de la proteína de fusión RSV-F (PDB: 5EA3) en GROMACS 2025.3:
Parámetros: caja cúbica con borde de 1.2 nm, TIP3P, PME, 310 K (v-rescale), dt = 2 fs, 10 ns producción.
SYS_DIR <- "/home/svirology/DINÁMICAS_MULTISITIO_FINALES/systems_final_simple"
systems <- tribble(
~Label, ~Site, ~Ligand, ~Dir,
"Site O — Rimegepant", "Site O", "Rimegepant", file.path(SYS_DIR, "00_site_O_RIMEGEPANT_SULFATE_complex"),
"Site II — Chlorhexidine","Site II", "Chlorhexidine", file.path(SYS_DIR, "02_site_II_CHLORHEXIDINE_complex"),
"Site III — Rimegepant", "Site III","Rimegepant", file.path(SYS_DIR, "03_site_III_RIMEGEPANT_SULFATE_complex"),
"Site IV — Atovaquone", "Site IV", "Atovaquone", file.path(SYS_DIR, "04_site_IV_ATOVAQUONE_complex"),
"Site V — Vibegron", "Site V", "Vibegron", file.path(SYS_DIR, "06_site_V_VIBEGRON_complex"),
"Apo (control)", "Apo", "—", file.path(SYS_DIR, "apo_protein")
)
kable(systems[, c("Label","Ligand","Dir")], col.names = c("Sistema","Ligando","Directorio")) %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"), full_width = FALSE)| Sistema | Ligando | Directorio |
|---|---|---|
| Site O — Rimegepant | Rimegepant | /home/svirology/DINÁMICAS_MULTISITIO_FINALES/systems_final_simple/00_site_O_RIMEGEPANT_SULFATE_complex |
| Site II — Chlorhexidine | Chlorhexidine | /home/svirology/DINÁMICAS_MULTISITIO_FINALES/systems_final_simple/02_site_II_CHLORHEXIDINE_complex |
| Site III — Rimegepant | Rimegepant | /home/svirology/DINÁMICAS_MULTISITIO_FINALES/systems_final_simple/03_site_III_RIMEGEPANT_SULFATE_complex |
| Site IV — Atovaquone | Atovaquone | /home/svirology/DINÁMICAS_MULTISITIO_FINALES/systems_final_simple/04_site_IV_ATOVAQUONE_complex |
| Site V — Vibegron | Vibegron | /home/svirology/DINÁMICAS_MULTISITIO_FINALES/systems_final_simple/06_site_V_VIBEGRON_complex |
| Apo (control) | — | /home/svirology/DINÁMICAS_MULTISITIO_FINALES/systems_final_simple/apo_protein |
# Lector XVG robusto (según skill md-analysis)
read_xvg <- function(filepath, col_names = c("X", "Y")) {
if (!file.exists(filepath)) return(NULL)
lines <- readLines(filepath, warn = FALSE)
data_ln <- lines[!grepl("^[@#]", lines) & nchar(trimws(lines)) > 0]
if (length(data_ln) == 0) return(NULL)
df <- tryCatch(
read.table(text = paste(data_ln, collapse = "\n"), fill = TRUE, comment.char = ""),
error = function(e) NULL
)
if (is.null(df) || ncol(df) < 2) return(NULL)
df <- df[, 1:min(length(col_names), ncol(df)), drop = FALSE]
colnames(df) <- col_names[1:ncol(df)]
df[is.finite(df[[1]]) & is.finite(df[[2]]), ]
}
# Adjunta metadatos y convierte ps→ns si max(X) > 500
attach_meta <- function(df, label, time_col = "X") {
if (is.null(df) || nrow(df) == 0) return(NULL)
x_vals <- df[[time_col]]
if (max(x_vals, na.rm = TRUE) > 500) x_vals <- x_vals / 1000
df$Time_ns <- x_vals
df$Label <- label
df
}
# Combina lista de data.frames ignorando NULLs
safe_bind <- function(lst) {
items <- Filter(Negate(is.null), lst)
if (length(items) == 0) return(data.frame())
do.call(rbind, items)
}all_data <- list()
for (i in seq_len(nrow(systems))) {
s <- systems[i, ]
d <- s$Dir
label <- s$Label
entry <- list(label = label, site = s$Site, ligand = s$Ligand)
# RMSD backbone
df <- read_xvg(file.path(d, "rmsd_backbone.xvg"))
if (!is.null(df)) {
df$Y <- df$Y * 10 # nm → Å
entry$rmsd <- attach_meta(df, label)
}
# RMSD ligando (solo complejos)
df <- read_xvg(file.path(d, "rmsd_ligand.xvg"))
if (!is.null(df)) {
df$Y <- df$Y * 10
entry$rmsd_lig <- attach_meta(df, label)
}
# RMSF
df <- read_xvg(file.path(d, "rmsf_residue.xvg"), c("Residue","RMSF"))
if (!is.null(df)) {
df$RMSF <- df$RMSF * 10 # nm → Å
df$Label <- label
entry$rmsf <- df
}
# Energía potencial (en ps, attach_meta convierte automáticamente)
df <- read_xvg(file.path(d, "energy.xvg"), c("X","Potential"))
if (!is.null(df)) {
entry$energy <- attach_meta(df, label)
}
# SASA
df <- read_xvg(file.path(d, "sasa.xvg"), c("X","SASA"))
if (!is.null(df)) {
entry$sasa <- attach_meta(df, label)
}
# Radio de giro
df <- read_xvg(file.path(d, "gyrate.xvg"), c("X","Rg"))
if (!is.null(df)) {
df$Rg <- df$Rg * 10 # nm → Å
entry$gyrate <- attach_meta(df, label)
}
# H-bonds desde caché numpy (via Python → CSV temporal)
hb_cache <- file.path(d, "hbonds_mda.npy")
if (file.exists(hb_cache) && s$Ligand != "—") {
csv_tmp <- file.path(d, "hbonds_mda.csv")
if (!file.exists(csv_tmp)) {
system(paste0(
"python3 -c \"import numpy as np; arr=np.load('", hb_cache, "'); ",
"import pandas as pd; ",
"pd.DataFrame({'frame':range(len(arr)),'hbonds':arr}).to_csv('", csv_tmp, "',index=False)\""
))
}
if (file.exists(csv_tmp)) {
hb <- read.csv(csv_tmp)
hb$Time_ns <- seq(0, 10, length.out = nrow(hb))
hb$Label <- label
entry$hbonds <- hb
}
}
all_data[[label]] <- entry
}
cat("Sistemas cargados:", length(all_data), "\n")## Sistemas cargados: 6
## Métricas disponibles por sistema:
for (nm in names(all_data)) {
metricas <- names(all_data[[nm]])[!names(all_data[[nm]]) %in% c("label","site","ligand")]
cat(sprintf(" %-30s %s\n", nm, paste(metricas, collapse=", ")))
}## Site O — Rimegepant rmsd, rmsd_lig, rmsf, energy, sasa, gyrate, hbonds
## Site II — Chlorhexidine rmsd, rmsd_lig, rmsf, energy, sasa, gyrate, hbonds
## Site III — Rimegepant rmsd, rmsd_lig, rmsf, energy, sasa, gyrate, hbonds
## Site IV — Atovaquone rmsd, rmsd_lig, rmsf, energy, sasa, gyrate, hbonds
## Site V — Vibegron rmsd, rmsd_lig, rmsf, energy, sasa, gyrate, hbonds
## Apo (control) rmsd, rmsf, energy, sasa, gyrate
rmsd_df <- safe_bind(lapply(all_data, function(x) x$rmsd))
p_bb <- ggplot(rmsd_df, aes(x = Time_ns, y = Y, color = Label, linetype = Label)) +
geom_line(alpha = 0.85, size = 0.7) +
scale_color_manual(values = site_colors) +
scale_linetype_manual(values = c(
"Site O — Rimegepant" = "solid",
"Site II — Chlorhexidine"= "solid",
"Site III — Rimegepant" = "solid",
"Site IV — Atovaquone" = "solid",
"Site V — Vibegron" = "solid",
"Apo (control)" = "dashed"
)) +
labs(title = "RMSD Backbone Cα — RSV-F (5EA3)",
subtitle = "Referencia: estructura inicial minimizada",
x = "Tiempo (ns)", y = "RMSD (Å)",
color = "Sistema", linetype = "Sistema") +
theme_bw(base_size = 12) +
theme(legend.position = "right") +
coord_cartesian(xlim = c(0, 10))
print(p_bb)rmsd_stats <- rmsd_df %>%
group_by(Label) %>%
summarise(
Media_Å = round(mean(Y, na.rm = TRUE), 2),
SD_Å = round(sd(Y, na.rm = TRUE), 2),
Min_Å = round(min(Y, na.rm = TRUE), 2),
Max_Å = round(max(Y, na.rm = TRUE), 2),
.groups = "drop"
)
rmsd_stats %>%
kable(caption = "Estadísticas RMSD Backbone (Å)") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"), full_width = FALSE) %>%
row_spec(which(rmsd_stats$Media_Å == min(rmsd_stats$Media_Å)), bold = TRUE, color = "white", background = "#2A9D8F")| Label | Media_Å | SD_Å | Min_Å | Max_Å |
|---|---|---|---|---|
| Apo (control) | 2.86 | 0.59 | 0.01 | 4.77 |
| Site II — Chlorhexidine | 3.60 | 0.58 | 0.01 | 4.74 |
| Site III — Rimegepant | 2.01 | 0.30 | 0.01 | 2.90 |
| Site IV — Atovaquone | 2.77 | 0.49 | 0.00 | 4.04 |
| Site O — Rimegepant | 2.46 | 0.43 | 0.01 | 3.97 |
| Site V — Vibegron | 2.50 | 0.66 | 0.01 | 4.26 |
rmsd_lig_df <- safe_bind(lapply(all_data, function(x) x$rmsd_lig))
# Panel A: todos los sitios
p_lig_all <- ggplot(rmsd_lig_df, aes(x = Time_ns, y = Y, color = Label)) +
geom_line(alpha = 0.85, size = 0.7) +
scale_color_manual(values = site_colors) +
labs(title = "A) RMSD Ligando — todos los sitios",
x = "Tiempo (ns)", y = "RMSD (Å)", color = "Sistema") +
theme_bw(base_size = 12) +
coord_cartesian(xlim = c(0, 10))
# Panel B: zoom sitios estables (sin Site O)
rmsd_lig_stable <- rmsd_lig_df %>%
filter(!grepl("Site O", Label))
p_lig_stable <- ggplot(rmsd_lig_stable, aes(x = Time_ns, y = Y, color = Label)) +
geom_line(alpha = 0.85, size = 0.7) +
scale_color_manual(values = site_colors) +
labs(title = "B) RMSD Ligando — Sitios II–V (zoom)",
x = "Tiempo (ns)", y = "RMSD (Å)", color = "Sistema") +
theme_bw(base_size = 12) +
coord_cartesian(xlim = c(0, 10))
p_lig_all / p_lig_stablelig_stats <- rmsd_lig_df %>%
group_by(Label) %>%
summarise(
Media_Å = round(mean(Y, na.rm = TRUE), 2),
SD_Å = round(sd(Y, na.rm = TRUE), 2),
Max_Å = round(max(Y, na.rm = TRUE), 2),
Estabilidad = case_when(
mean(Y, na.rm = TRUE) < 3 ~ "Muy estable ✅",
mean(Y, na.rm = TRUE) < 8 ~ "Estable ✅",
mean(Y, na.rm = TRUE) < 20 ~ "Moderado ⚠️",
TRUE ~ "Escapó ❌"
),
.groups = "drop"
)
lig_stats %>%
kable(caption = "Estadísticas RMSD Ligando (Å)") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"), full_width = FALSE) %>%
row_spec(which(lig_stats$Media_Å == min(lig_stats$Media_Å)), bold = TRUE,
color = "white", background = "#2A9D8F") %>%
row_spec(which(lig_stats$Media_Å == max(lig_stats$Media_Å)), bold = TRUE,
color = "white", background = "#E63946")| Label | Media_Å | SD_Å | Max_Å | Estabilidad |
|---|---|---|---|---|
| Site II — Chlorhexidine | 3.20 | 0.88 | 6.62 | Estable ✅ |
| Site III — Rimegepant | 1.75 | 0.75 | 7.78 | Muy estable ✅ |
| Site IV — Atovaquone | 7.26 | 4.32 | 16.82 | Estable ✅ |
| Site O — Rimegepant | 69.86 | 37.42 | 122.71 | Escapó ❌ |
| Site V — Vibegron | 6.89 | 2.46 | 12.48 | Estable ✅ |
rmsf_df <- safe_bind(lapply(all_data, function(x) x$rmsf))
# Panel por sistema
ggplot(rmsf_df, aes(x = Residue, y = RMSF, color = Label, fill = Label)) +
geom_line(size = 0.7, alpha = 0.9) +
geom_area(alpha = 0.12, position = "identity") +
scale_color_manual(values = site_colors) +
scale_fill_manual(values = site_colors) +
facet_wrap(~ Label, ncol = 2, scales = "free_y") +
labs(title = "RMSF por Residuo — Flexibilidad del Backbone",
subtitle = "Picos indican regiones de alta movilidad (loops, extremos)",
x = "Número de residuo", y = "RMSF (Å)") +
theme_bw(base_size = 11) +
theme(legend.position = "none",
strip.background = element_rect(fill = "#264653"),
strip.text = element_text(color = "white", face = "bold"))# Overlay comparativo
ggplot(rmsf_df, aes(x = Residue, y = RMSF, color = Label)) +
geom_line(size = 0.6, alpha = 0.8) +
scale_color_manual(values = site_colors) +
labs(title = "RMSF Overlay — comparación de todos los sistemas",
x = "Residuo", y = "RMSF (Å)", color = "Sistema") +
theme_bw(base_size = 12)gyrate_df <- safe_bind(lapply(all_data, function(x) x$gyrate))
ggplot(gyrate_df, aes(x = Time_ns, y = Rg, color = Label, linetype = Label)) +
geom_line(alpha = 0.85, size = 0.7) +
scale_color_manual(values = site_colors) +
scale_linetype_manual(values = c(
"Site O — Rimegepant" = "solid",
"Site II — Chlorhexidine"= "solid",
"Site III — Rimegepant" = "solid",
"Site IV — Atovaquone" = "solid",
"Site V — Vibegron" = "solid",
"Apo (control)" = "dashed"
)) +
labs(title = "Radio de Giro (Rg) — Compacidad estructural",
subtitle = "Rg estable indica que la proteína no se despliega",
x = "Tiempo (ns)", y = "Rg (Å)",
color = "Sistema", linetype = "Sistema") +
theme_bw(base_size = 12) +
coord_cartesian(xlim = c(0, 10))gyrate_df %>%
group_by(Label) %>%
summarise(
Rg_medio_Å = round(mean(Rg, na.rm = TRUE), 2),
Rg_SD_Å = round(sd(Rg, na.rm = TRUE), 2),
Rg_min_Å = round(min(Rg, na.rm = TRUE), 2),
Rg_max_Å = round(max(Rg, na.rm = TRUE), 2),
.groups = "drop"
) %>%
kable(caption = "Estadísticas Radio de Giro (Å)") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"), full_width = FALSE)| Label | Rg_medio_Å | Rg_SD_Å | Rg_min_Å | Rg_max_Å |
|---|---|---|---|---|
| Apo (control) | 29.05 | 0.39 | 27.96 | 30.10 |
| Site II — Chlorhexidine | 29.06 | 0.39 | 27.67 | 29.97 |
| Site III — Rimegepant | 28.86 | 0.22 | 28.17 | 29.52 |
| Site IV — Atovaquone | 28.14 | 0.44 | 27.13 | 29.52 |
| Site O — Rimegepant | 28.47 | 0.42 | 27.34 | 29.49 |
| Site V — Vibegron | 28.83 | 0.35 | 27.96 | 29.97 |
energy_df <- safe_bind(lapply(all_data, function(x) x$energy))
ggplot(energy_df, aes(x = Time_ns, y = Potential / 1000, color = Label, linetype = Label)) +
geom_line(alpha = 0.8, size = 0.6) +
scale_color_manual(values = site_colors) +
scale_linetype_manual(values = c(
"Site O — Rimegepant" = "solid",
"Site II — Chlorhexidine"= "solid",
"Site III — Rimegepant" = "solid",
"Site IV — Atovaquone" = "solid",
"Site V — Vibegron" = "solid",
"Apo (control)" = "dashed"
)) +
labs(title = "Energía Potencial del sistema",
subtitle = "Valores estables indican simulaciones bien equilibradas",
x = "Tiempo (ns)", y = "Energía potencial (kJ/mol × 10³)",
color = "Sistema", linetype = "Sistema") +
theme_bw(base_size = 12) +
coord_cartesian(xlim = c(0, 10))energy_df %>%
group_by(Label) %>%
summarise(
E_media = round(mean(Potential, na.rm = TRUE)),
E_SD = round(sd(Potential, na.rm = TRUE)),
E_drift = round(last(Potential) - first(Potential)),
.groups = "drop"
) %>%
kable(col.names = c("Sistema","E media (kJ/mol)","SD (kJ/mol)","Drift total (kJ/mol)"),
caption = "Energía Potencial — estadísticas de simulación") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"), full_width = FALSE)| Sistema | E media (kJ/mol) | SD (kJ/mol) | Drift total (kJ/mol) |
|---|---|---|---|
| Apo (control) | -2521840 | 1642 | 3482 |
| Site II — Chlorhexidine | -2522524 | 1634 | -580 |
| Site III — Rimegepant | -2521807 | 1658 | -357 |
| Site IV — Atovaquone | -2521712 | 1591 | 1117 |
| Site O — Rimegepant | -2590529 | 1623 | -1365 |
| Site V — Vibegron | -2522930 | 1673 | -3052 |
sasa_df <- safe_bind(lapply(all_data, function(x) x$sasa))
ggplot(sasa_df, aes(x = Time_ns, y = SASA, color = Label, linetype = Label)) +
geom_line(alpha = 0.85, size = 0.7) +
scale_color_manual(values = site_colors) +
scale_linetype_manual(values = c(
"Site O — Rimegepant" = "solid",
"Site II — Chlorhexidine"= "solid",
"Site III — Rimegepant" = "solid",
"Site IV — Atovaquone" = "solid",
"Site V — Vibegron" = "solid",
"Apo (control)" = "dashed"
)) +
labs(title = "SASA — Superficie Accesible al Solvente",
subtitle = "Cambios en SASA reflejan apertura/cierre de cavidades",
x = "Tiempo (ns)", y = "SASA (nm²)",
color = "Sistema", linetype = "Sistema") +
theme_bw(base_size = 12) +
coord_cartesian(xlim = c(0, 10))hbond_list <- lapply(all_data, function(x) x$hbonds)
hbond_df <- safe_bind(Filter(Negate(is.null), hbond_list))
if (nrow(hbond_df) > 0) {
# Suavizado: media móvil de 50 frames
smooth_hb <- function(x, w = 50) {
if (length(x) < w) return(x)
stats::filter(x, rep(1/w, w), sides = 2)
}
hbond_smooth <- hbond_df %>%
group_by(Label) %>%
mutate(hb_smooth = as.numeric(smooth_hb(hbonds, 50))) %>%
ungroup()
ggplot(hbond_smooth, aes(x = Time_ns, y = hb_smooth, color = Label)) +
geom_line(alpha = 0.9, size = 0.9, na.rm = TRUE) +
scale_color_manual(values = site_colors) +
labs(title = "Puentes de Hidrógeno Proteína–Ligando (MDAnalysis)",
subtitle = "Criterio geométrico: d(D-A) ≤ 3.5 Å, ángulo D-H-A ≥ 150°",
x = "Tiempo (ns)", y = "N° H-bonds (media móvil 50 frames)",
color = "Sistema") +
theme_bw(base_size = 12) +
coord_cartesian(xlim = c(0, 10), ylim = c(0, NA))
}if (nrow(hbond_df) > 0) {
hbond_df %>%
group_by(Label) %>%
summarise(
HBonds_medio = round(mean(hbonds, na.rm = TRUE), 3),
HBonds_max = max(hbonds, na.rm = TRUE),
Frames_con_HB = sum(hbonds > 0),
Pct_frames = round(100 * mean(hbonds > 0), 1),
.groups = "drop"
) %>%
arrange(desc(HBonds_medio)) %>%
kable(col.names = c("Sistema","H-bonds medio","H-bonds máx","Frames con HB","% frames"),
caption = "Estadísticas de Puentes de Hidrógeno") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"), full_width = FALSE) %>%
row_spec(1, bold = TRUE, color = "white", background = "#F4A261")
}| Sistema | H-bonds medio | H-bonds máx | Frames con HB | % frames |
|---|---|---|---|---|
| Site V — Vibegron | 1.357 | 3 | 947 | 94.6 |
| Site III — Rimegepant | 0.864 | 3 | 837 | 83.6 |
| Site IV — Atovaquone | 0.261 | 2 | 232 | 23.2 |
| Site O — Rimegepant | 0.018 | 1 | 18 | 1.8 |
| Site II — Chlorhexidine | 0.000 | 0 | 0 | 0.0 |
library(dplyr); library(tidyr); library(ggplot2); library(patchwork)
CONTACTS_DIR <- "/home/svirology/DINÁMICAS_MULTISITIO_FINALES/analisis_resultados"
contact_files <- list(
"Site O — Rimegepant" = "contacts_time_00_site_O_RIMEGEPANT_SULFATE_complex.csv",
"Site II — Chlorhexidine"= "contacts_time_02_site_II_CHLORHEXIDINE_complex.csv",
"Site III — Rimegepant" = "contacts_time_03_site_III_RIMEGEPANT_SULFATE_complex.csv",
"Site IV — Atovaquone" = "contacts_time_04_site_IV_ATOVAQUONE_complex.csv",
"Site V — Vibegron" = "contacts_time_06_site_V_VIBEGRON_complex.csv"
)
# Leer y combinar
contacts_all <- lapply(names(contact_files), function(sys_label) {
path <- file.path(CONTACTS_DIR, contact_files[[sys_label]])
if (!file.exists(path)) return(NULL)
df <- read.csv(path, stringsAsFactors = FALSE)
df$System <- sys_label
df
}) %>% Filter(Negate(is.null), .) %>% bind_rows()
# Etiqueta residuo limpia
contacts_all <- contacts_all %>%
mutate(res_label = paste0(resname, resid))
cat("Sistemas cargados:", n_distinct(contacts_all$System), "\n")## Sistemas cargados: 5
## Total registros: 2476
# Función: heatmap + curva de contactos totales para un sistema
plot_contact_heatmap <- function(sys_label, top_n = 20) {
df <- contacts_all %>% filter(System == sys_label)
if (nrow(df) == 0) return(NULL)
# Seleccionar los top_n residuos más contactados (total acumulado)
top_res <- df %>%
group_by(res_label) %>%
summarise(total = sum(n_frames_contact), .groups = "drop") %>%
slice_max(total, n = top_n) %>%
pull(res_label)
df_top <- df %>% filter(res_label %in% top_res)
# Ordenar residuos por posición en secuencia (resid numérico)
res_order <- df_top %>%
distinct(res_label, resid) %>%
arrange(resid) %>%
pull(res_label)
df_top <- df_top %>%
mutate(res_label = factor(res_label, levels = res_order))
# ── Heatmap ──────────────────────────────────────────────────────────────
p_heat <- ggplot(df_top,
aes(x = time_ns, y = res_label, fill = contact_fraction)) +
geom_tile(color = NA) +
scale_fill_gradientn(
colours = c("#f7fbff","#c6dbef","#6baed6","#2171b5","#08306b"),
limits = c(0, 1),
labels = scales::percent,
name = "Frames\ncon contacto"
) +
scale_x_continuous(breaks = seq(0, 10, 2), expand = c(0, 0)) +
labs(
title = sys_label,
subtitle = paste0("Top ", top_n,
" residuos · Corte 4.5 Å · Ventanas de 0.2 ns"),
x = "Tiempo (ns)",
y = NULL
) +
theme_bw(base_size = 11) +
theme(
axis.text.y = element_text(size = 7.5, family = "mono"),
legend.position = "right",
panel.grid = element_blank(),
plot.title = element_text(face = "bold", size = 12),
plot.subtitle = element_text(size = 9, color = "grey40")
)
# ── Curva de contactos totales por ventana ───────────────────────────────
curve_df <- df %>%
group_by(time_ns, bin) %>%
summarise(
n_res_contacted = n_distinct(res_label),
mean_frac_contact = mean(contact_fraction),
.groups = "drop"
)
p_curve <- ggplot(curve_df, aes(x = time_ns)) +
geom_ribbon(aes(ymin = 0, ymax = n_res_contacted),
fill = "#2171b5", alpha = 0.15) +
geom_line(aes(y = n_res_contacted, color = "Residuos en contacto"),
size = 0.9) +
geom_line(aes(y = mean_frac_contact * max(n_res_contacted),
color = "Fracción media contacto (escala)"),
size = 0.7, linetype = "dashed") +
scale_color_manual(
values = c("Residuos en contacto" = "#08306b",
"Fracción media contacto (escala)" = "#D62728"),
name = NULL
) +
scale_x_continuous(breaks = seq(0, 10, 2), expand = c(0, 0)) +
scale_y_continuous(expand = expansion(mult = c(0, 0.1))) +
labs(x = "Tiempo (ns)", y = "N° residuos") +
theme_bw(base_size = 10) +
theme(legend.position = "bottom",
legend.text = element_text(size = 8),
panel.grid.minor = element_blank())
p_heat / p_curve + plot_layout(heights = c(3, 1))
}# Tabla: residuos más contactados por sistema
contacts_top_table <- contacts_all %>%
group_by(System, res_label) %>%
summarise(
total_frames = sum(n_frames_contact),
mean_fraction = round(mean(contact_fraction), 3),
n_windows = n_distinct(bin),
windows_present = sum(contact_fraction > 0),
pct_time = round(100 * windows_present / n_windows, 1),
.groups = "drop"
) %>%
group_by(System) %>%
slice_max(mean_fraction, n = 5) %>%
arrange(System, desc(mean_fraction))
contacts_top_table %>%
select(System, res_label, mean_fraction, pct_time) %>%
rename(Sistema = System,
Residuo = res_label,
`Fracción media` = mean_fraction,
`% tiempo en contacto` = pct_time) %>%
kable(caption = "Top 5 residuos más contactados por sistema (4.5 Å, 10 ns)") %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"),
full_width = FALSE) %>%
collapse_rows(columns = 1, valign = "top")| Sistema | Residuo | Fracción media | % tiempo en contacto |
|---|---|---|---|
| Site II — Chlorhexidine | ASN181 | 1.000 | 100 |
| LYS184 | 1.000 | 100 | |
| LYS185 | 1.000 | 100 | |
| ASP182 | 0.998 | 100 | |
| SER188 | 0.997 | 100 | |
| Site III — Rimegepant | ALA268 | 1.000 | 100 |
| ARG252 | 0.999 | 100 | |
| GLY253 | 0.999 | 100 | |
| LEU229 | 0.997 | 100 | |
| HIS230 | 0.996 | 100 | |
| Site IV — Atovaquone | SER338 | 0.950 | 100 |
| LYS340 | 0.923 | 100 | |
| LEU369 | 0.909 | 100 | |
| ILE344 | 0.892 | 100 | |
| SER364 | 0.876 | 100 | |
| Site O — Rimegepant | LYS39 | 0.795 | 100 |
| ILE38 | 0.743 | 100 | |
| ASN37 | 0.583 | 100 | |
| LYS40 | 0.435 | 100 | |
| VAL41 | 0.431 | 100 | |
| Site V — Vibegron | ASN123 | 1.000 | 100 |
| TYR124 | 1.000 | 100 | |
| LEU98 | 0.994 | 100 | |
| LYS94 | 0.951 | 100 | |
| ASN91 | 0.938 | 100 |
Estructuras extraídas directamente de la trayectoria de MD. Azul/verde/púrpura: proteína (cartoon). Naranja brillante: residuos en contacto ≤ 4.5 Å. Color sólido: ligando (sticks + esferas). Sin solvente ni iones.
POSES_DIR <- "/home/svirology/DINÁMICAS_MULTISITIO_FINALES/analisis_resultados/poses_timepoints"
pose_systems <- list(
list(site="Site_O", ligand="Rimegepant", label="Site Ø — Rimegepant"),
list(site="Site_II", ligand="Chlorhexidine", label="Site II — Chlorhexidine"),
list(site="Site_III", ligand="Rimegepant", label="Site III — Rimegepant"),
list(site="Site_IV", ligand="Atovaquone", label="Site IV — Atovaquone"),
list(site="Site_V", ligand="Vibegron", label="Site V — Vibegron")
)library(knitr)
for (sys in pose_systems) {
imgs <- lapply(c(0, 5, 10), function(t) {
file.path(POSES_DIR,
paste0(sys$site, "_", sys$ligand, "_t", t, "ns.png"))
})
exist <- sapply(imgs, file.exists)
if (!all(exist)) {
cat(paste0("\n> ⚠ Imágenes no encontradas para ", sys$label, "\n\n"))
next
}
cat(paste0("\n### ", sys$label, "\n\n"))
# Componer panel 3 columnas con knitr::include_graphics
cat('<div style="display:flex; gap:6px; align-items:flex-start; margin-bottom:18px;">\n')
for (i in seq_along(imgs)) {
t_label <- c("t = 0 ns", "t = 5 ns", "t = 10 ns")[i]
cat(paste0(
'<div style="flex:1; text-align:center;">',
'<p style="font-size:12px; font-weight:bold; margin:0 0 4px 0; color:#444;">',
t_label, '</p>',
'<img src="', imgs[[i]], '" style="width:100%; border:1px solid #ddd;',
' border-radius:4px;" />',
'</div>\n'
))
}
cat('</div>\n\n')
}t = 0 ns
t = 5 ns
t = 10 ns
t = 0 ns
t = 5 ns
t = 10 ns
t = 0 ns
t = 5 ns
t = 10 ns
t = 0 ns
t = 5 ns
t = 10 ns
t = 0 ns
t = 5 ns
t = 10 ns
# Preparar subsets
rmsd_bb_5 <- rmsd_df %>% filter(Label != "Apo (control)")
rmsd_bb_apo <- rmsd_df %>% filter(Label == "Apo (control)")
p1 <- ggplot(rmsd_df, aes(x = Time_ns, y = Y, color = Label, linetype = Label)) +
geom_line(alpha = 0.85, size = 0.65) +
scale_color_manual(values = site_colors) +
scale_linetype_manual(values = setNames(
c("solid","solid","solid","solid","solid","dashed"), names(site_colors))) +
labs(title = "A) RMSD Backbone Cα", x = "Tiempo (ns)", y = "RMSD (Å)") +
theme_bw(base_size = 11) + coord_cartesian(xlim = c(0,10)) +
theme(legend.position = "none")
p2 <- ggplot(rmsd_lig_df %>% filter(!grepl("Site O", Label)),
aes(x = Time_ns, y = Y, color = Label)) +
geom_line(alpha = 0.85, size = 0.65) +
geom_line(data = rmsd_lig_df %>% filter(grepl("Site O", Label)),
aes(x = Time_ns, y = Y, color = Label), linetype = "dashed", size = 0.65) +
scale_color_manual(values = site_colors) +
labs(title = "B) RMSD Ligando", x = "Tiempo (ns)", y = "RMSD (Å)") +
theme_bw(base_size = 11) + coord_cartesian(xlim = c(0,10)) +
theme(legend.position = "none")
p3 <- ggplot(gyrate_df, aes(x = Time_ns, y = Rg, color = Label, linetype = Label)) +
geom_line(alpha = 0.85, size = 0.65) +
scale_color_manual(values = site_colors) +
scale_linetype_manual(values = setNames(
c("solid","solid","solid","solid","solid","dashed"), names(site_colors))) +
labs(title = "C) Radio de Giro", x = "Tiempo (ns)", y = "Rg (Å)") +
theme_bw(base_size = 11) + coord_cartesian(xlim = c(0,10)) +
theme(legend.position = "none")
p4 <- ggplot(sasa_df, aes(x = Time_ns, y = SASA, color = Label, linetype = Label)) +
geom_line(alpha = 0.85, size = 0.65) +
scale_color_manual(values = site_colors) +
scale_linetype_manual(values = setNames(
c("solid","solid","solid","solid","solid","dashed"), names(site_colors))) +
labs(title = "D) SASA", x = "Tiempo (ns)", y = "SASA (nm²)") +
theme_bw(base_size = 11) + coord_cartesian(xlim = c(0,10)) +
theme(legend.position = "bottom",
legend.text = element_text(size = 8))
(p1 | p2) / (p3 | p4) +
plot_annotation(
title = "Dinámicas Moleculares RSV-F (5EA3) — 5 sitios de unión + control apo (10 ns, GPU)",
subtitle = "AMBER99sb-ildn + GAFF2 | TIP3P | 310 K | dt=2 fs",
theme = theme(plot.title = element_text(size = 14, face = "bold"),
plot.subtitle = element_text(size = 10))
)# Construir tabla resumen completa
resumen <- systems %>%
rowwise() %>%
mutate(
RMSD_prot_A = {
d <- all_data[[Label]]
if (!is.null(d$rmsd)) round(mean(d$rmsd$Y, na.rm=TRUE), 2) else NA
},
RMSD_lig_A = {
d <- all_data[[Label]]
if (!is.null(d$rmsd_lig)) round(mean(d$rmsd_lig$Y, na.rm=TRUE), 2) else NA
},
Rg_A = {
d <- all_data[[Label]]
if (!is.null(d$gyrate)) round(mean(d$gyrate$Rg, na.rm=TRUE), 2) else NA
},
SASA_nm2 = {
d <- all_data[[Label]]
if (!is.null(d$sasa)) round(mean(d$sasa$SASA, na.rm=TRUE), 2) else NA
},
HBonds = {
d <- all_data[[Label]]
if (!is.null(d$hbonds)) round(mean(d$hbonds$hbonds, na.rm=TRUE), 3) else NA
},
Estabilidad = {
rl <- all_data[[Label]]$rmsd_lig
if (is.null(rl)) "—"
else {
m <- mean(rl$Y, na.rm=TRUE)
if (m < 3) "Muy estable ✅" else if (m < 8) "Estable ✅" else if (m < 20) "Moderado ⚠️" else "Escapó ❌"
}
}
) %>%
ungroup() %>%
select(Label, Ligand, RMSD_prot_A, RMSD_lig_A, Rg_A, SASA_nm2, HBonds, Estabilidad)
resumen %>%
kable(
col.names = c("Sistema","Ligando","RMSD prot (Å)","RMSD lig (Å)",
"Rg (Å)","SASA (nm²)","H-bonds","Estabilidad"),
caption = "Resumen comparativo — Dinámica Molecular RSV-F 10 ns"
) %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"), full_width = TRUE) %>%
row_spec(which(resumen$Label == "Site III — Rimegepant"), bold = TRUE,
background = "#fff3cd") %>%
row_spec(which(resumen$Label == "Apo (control)"), italic = TRUE,
background = "#f0f0f0")| Sistema | Ligando | RMSD prot (Å) | RMSD lig (Å) | Rg (Å) | SASA (nm²) | H-bonds | Estabilidad |
|---|---|---|---|---|---|---|---|
| Site O — Rimegepant | Rimegepant | 2.46 | 69.86 | 28.47 | 214.80 | 0.018 | Escapó ❌ |
| Site II — Chlorhexidine | Chlorhexidine | 3.60 | 3.20 | 29.06 | 219.05 | 0.000 | Estable ✅ |
| Site III — Rimegepant | Rimegepant | 2.01 | 1.75 | 28.86 | 214.20 | 0.864 | Muy estable ✅ |
| Site IV — Atovaquone | Atovaquone | 2.77 | 7.26 | 28.14 | 216.59 | 0.261 | Estable ✅ |
| Site V — Vibegron | Vibegron | 2.50 | 6.89 | 28.83 | 217.56 | 1.357 | Estable ✅ |
| Apo (control) | — | 2.86 | NA | 29.05 | 216.36 | NA | — |
# Score compuesto (mayor es mejor):
# RMSD ligando inverso + H-bonds
candidatos <- resumen %>%
filter(Ligand != "—", !is.na(RMSD_lig_A)) %>%
mutate(
Score_RMSD = 1 / (1 + RMSD_lig_A / 10), # 0-1, mayor RMSD = menor score
Score_HB = ifelse(is.na(HBonds), 0, HBonds / max(HBonds, na.rm=TRUE)),
Score_total = round((Score_RMSD * 0.6 + Score_HB * 0.4), 3)
) %>%
arrange(desc(Score_total))
candidatos %>%
select(Label, Ligand, RMSD_lig_A, HBonds, Score_RMSD, Score_HB, Score_total) %>%
kable(
col.names = c("Sistema","Ligando","RMSD lig (Å)","H-bonds","Score RMSD","Score HB","Score Total"),
caption = "Ranking de candidatos (Score = 0.6×RMSD_inv + 0.4×HBonds_norm)"
) %>%
kable_styling(bootstrap_options = c("striped","hover","condensed"), full_width = FALSE) %>%
row_spec(1, bold = TRUE, color = "white", background = "#2A9D8F")| Sistema | Ligando | RMSD lig (Å) | H-bonds | Score RMSD | Score HB | Score Total |
|---|---|---|---|---|---|---|
| Site III — Rimegepant | Rimegepant | 1.75 | 0.864 | 0.8510638 | 0.6366986 | 0.765 |
| Site V — Vibegron | Vibegron | 6.89 | 1.357 | 0.5920663 | 1.0000000 | 0.755 |
| Site II — Chlorhexidine | Chlorhexidine | 3.20 | 0.000 | 0.7575758 | 0.0000000 | 0.455 |
| Site IV — Atovaquone | Atovaquone | 7.26 | 0.261 | 0.5793743 | 0.1923360 | 0.425 |
| Site O — Rimegepant | Rimegepant | 69.86 | 0.018 | 0.1252191 | 0.0132646 | 0.080 |
# Gráfico de ranking
ggplot(candidatos, aes(x = reorder(Label, Score_total), y = Score_total, fill = Label)) +
geom_col(width = 0.6, show.legend = FALSE) +
geom_text(aes(label = Score_total), hjust = -0.2, size = 4) +
scale_fill_manual(values = site_colors) +
coord_flip(ylim = c(0, max(candidatos$Score_total) * 1.2)) +
labs(title = "Ranking de candidatos farmacológicos",
subtitle = "Score compuesto: 60% estabilidad (RMSD inverso) + 40% H-bonds",
x = NULL, y = "Score (0–1)") +
theme_bw(base_size = 12)Preparación de sistemas: Las estructuras de los complejos proteína–ligando se generaron mediante docking molecular con AutoDock Vina sobre la proteína de fusión RSV-F (PDB: 5EA3, residuos 1–513). Los 5 sitios de unión se seleccionaron de la biblioteca DrugCentral (2,331 → 1,020 compuestos FDA-aprobados tras filtrado). Las topologías de los ligandos se generaron con ACPYPE v2023.10.27 (campo de fuerza GAFF2, cargas AM1-BCC).
Dinámica molecular: Proteína parametrizada con
AMBER99sb-ildn; solvente TIP3P; caja cúbica con borde de 1.2 nm.
Minimización de energía (steepest descent, 50,000 pasos), equilibración
NVT (100 ps, 310 K, v-rescale), producción 10 ns (dt = 2 fs). Todas las
simulaciones en GROMACS 2025.3 con GPU NVIDIA RTX 5080
(-ntmpi 1 -ntomp 16 -nb gpu -pme gpu -bonded gpu -update gpu).
Análisis: RMSD/RMSF/Rg calculados con
gmx rms, gmx rmsf, gmx gyrate.
Energía potencial con gmx energy (índice 10). SASA con
gmx sasa. Puentes de hidrógeno proteína–ligando con
MDAnalysis v2.x (HydrogenBondAnalysis, d(D-A) ≤ 3.5 Å,
ángulo D-H-A ≥ 150°). Visualización en R 4.x con ggplot2 +
patchwork.
## R version 4.3.3 (2024-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 24.04.4 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.12.0
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0
##
## locale:
## [1] LC_CTYPE=C.UTF-8 LC_NUMERIC=C LC_TIME=C.UTF-8
## [4] LC_COLLATE=C.UTF-8 LC_MONETARY=C.UTF-8 LC_MESSAGES=C.UTF-8
## [7] LC_PAPER=C.UTF-8 LC_NAME=C LC_ADDRESS=C
## [10] LC_TELEPHONE=C LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C
##
## time zone: America/Mexico_City
## tzcode source: system (glibc)
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] knitr_1.52 rmarkdown_2.32 stringr_1.6.0 reshape2_1.4.5
## [5] tibble_3.3.1 kableExtra_1.4.1 patchwork_1.3.2 gridExtra_2.3.1
## [9] scales_1.4.0 readr_2.2.0 tidyr_1.3.2 dplyr_1.2.1
## [13] ggplot2_4.0.3
##
## loaded via a namespace (and not attached):
## [1] gtable_0.3.6 jsonlite_2.0.0 compiler_4.3.3 Rcpp_1.1.2
## [5] tidyselect_1.2.1 xml2_1.6.0 jquerylib_0.1.4 textshaping_1.0.5
## [9] systemfonts_1.3.2 yaml_2.3.12 fastmap_1.2.0 plyr_1.8.9
## [13] R6_2.6.1 labeling_0.4.3 generics_0.1.4 svglite_2.2.2
## [17] bslib_0.12.0 pillar_1.11.1 RColorBrewer_1.1-3 tzdb_0.5.0
## [21] rlang_1.3.0 stringi_1.8.9 cachem_1.1.0 xfun_0.62
## [25] sass_0.4.10 S7_0.2.2 otel_0.2.0 viridisLite_0.4.3
## [29] cli_3.6.6 withr_3.0.3 magrittr_2.0.5 digest_0.6.39
## [33] grid_4.3.3 rstudioapi_0.19.0 hms_1.1.4 lifecycle_1.0.5
## [37] vctrs_0.7.3 evaluate_1.0.5 glue_1.8.1 farver_2.1.2
## [41] purrr_1.2.2 tools_4.3.3 pkgconfig_2.0.3 htmltools_0.5.9