rmsd.bend source
rmsd.bend on the hub · documented module
import Baseimport ./geom.bend as Gimport ./protein.bend as Pimport ./topology.bend as T# --- RMSD v0: centroid-free aligned RMSD (no rotation) + ensemble matrix.# Lockstep pairs zip: a longer list's extra elements are ignored, so# `rmsd` of unequal lists silently answers 0. Use `rmsd_checked` when the# mismatch must be visible instead.def pair_head_go(+p1: G.Vec3, y: P.Atom, rest: F32 & Nat) -> F32 & Nat: match y: case P.Atom{s2, e2, +p2}: T.sumd2_extend(G.Vec3.dist2(p1, p2), rest)def pair_head(x: P.Atom, y: P.Atom, rest: F32 & Nat) -> F32 & Nat: match x: case P.Atom{s1, e1, +p1}: pair_head_go(p1, y, rest)def pair_sumd2(xs: List<&2, P.Atom>, ys: List<&2, P.Atom>) -> F32 & Nat: match xs ys: case Nil{} Nil{}: (0.0, 0n) case Nil{} qh <> qt: (0.0, 0n) case h <> t Nil{}: (0.0, 0n) case x <> xt y <> yt: pair_head(x, y, pair_sumd2(xt, yt))def rmsd(xs: List<&2, P.Atom>, ys: List<&2, P.Atom>) -> F32: T.rg_of(pair_sumd2(xs, ys))# Length-checked RMSD: None{} on unequal lengths, Some{...} otherwise.def rmsd_checked_go( ok: Bool, +xs: List<&2, P.Atom>, +ys: List<&2, P.Atom>) -> Maybe<&2, F32>: match ok: case True{}: Some{rmsd(xs, ys)} case False{}: None{}def rmsd_checked( +xs: List<&2, P.Atom>, +ys: List<&2, P.Atom>) -> Maybe<&2, F32>: rmsd_checked_go(Nat.is_eq(List.length(&2, P.Atom, xs), List.length(&2, P.Atom, ys)), xs, ys)def rmsd_against(+q: List<&2, P.Atom>, h: List<&2, P.Atom>) -> F32: T.rg_of(pair_sumd2(q, h))def rmsd_to_all( +q: List<&2, P.Atom>, ys: List<&2, List<&2, P.Atom>>) -> List<&2, F32>: match ys: case Nil{}: Nil{} case h <> t: rmsd_against(q, h) <> rmsd_to_all(q, t)def rmsd_matrix( +xs: List<&2, List<&2, P.Atom>>, +all: List<&2, List<&2, P.Atom>>) -> List<&2, List<&2, F32>>: match xs: case Nil{}: Nil{} case h <> t: rmsd_to_all(h, all) <> rmsd_matrix(t, all)def rmsd_matrix_of(+ens: List<&2, List<&2, P.Atom>>) -> List<&2, List<&2, F32>>: rmsd_matrix(ens, ens)