1 import Base 2 import ./geom.bend as G 3 import ./protein.bend as P 4 import ./pdb.bend as Pdb 5 import ./templates.bend as Templates 6 7 # --- Residue templates: CCD-derived bonds applied by name. --- 8 # TplAtom carries parse-time identity (chain/seq/residue/atom names) that 9 # P.Atom drops. A template bond fires for same-residue atom pairs whose 10 # names are linked in the residue's template; unknown residues yield no 11 # template bonds (inference + CONECT cover them). The union is used 12 # downstream; duplicates across sources are harmless for boolean 13 # exclusions and negligible in energies (documented). 14 15 type TplAtom is Data: 16 TA{serial: U32, chain: U32, seq: Nat, res: String, name: String} 17 18 def tpl_bond_match(+n1: String, +n2: String, +x: String, +y: String) -> Bool: 19 Bool.or(Bool.and(String.eq(n1, x), String.eq(n2, y)), Bool.and(String.eq(n1, y), String.eq(n2, x))) 20 21 def tpl_has_scan(bs: List<&2, String>, +n1: String, +n2: String) -> Bool: 22 match bs: 23 case Nil{}: 24 False{} 25 case x <> Nil{}: 26 False{} 27 case x <> y <> t: 28 Bool.or(tpl_bond_match(n1, n2, x, y), tpl_has_scan(t, n1, n2)) 29 30 def tpl_has_bond(+res: String, +n1: String, +n2: String) -> Bool: 31 tpl_has_scan(Templates.tpl_bonds_for(res), n1, n2) 32 33 def tpl_bonded(+ra: String, +na: String, +ca: U32, +qa: Nat, rb: String, nb: String, cb: U32, qb: Nat) -> Bool: 34 Bool.and(Bool.and(U32.is_eq(ca, cb), Nat.is_eq(qa, qb)), Bool.and(String.eq(ra, rb), tpl_has_bond(ra, na, nb))) 35 36 def tpl_one(a: TplAtom, h: TplAtom) -> List<&1, U32 & U32>: 37 match a h: 38 case TA{sa, ca, qa, ra, na} TA{sb, cb, qb, rb, nb}: 39 Bool.pick(List<&1, U32 & U32>, tpl_bonded(ra, na, ca, qa, rb, nb, cb, qb), [(sa, sb)], Nil{}) 40 41 def append_tpl(a: List<&1, U32 & U32>, b: List<&1, U32 & U32>) -> List<&1, U32 & U32>: 42 match a: 43 case Nil{}: 44 b 45 case h <> t: 46 h <> append_tpl(t, b) 47 48 def tpl_row(+a: TplAtom, ys: List<&2, TplAtom>) -> List<&1, U32 & U32>: 49 match ys: 50 case Nil{}: 51 Nil{} 52 case h <> t: 53 append_tpl(tpl_one(a, h), tpl_row(a, t)) 54 55 def tpl_total(+xs: List<&2, TplAtom>) -> List<&1, U32 & U32>: 56 match xs: 57 case Nil{}: 58 Nil{} 59 case h <> +t: 60 append_tpl(tpl_row(h, t), tpl_total(t)) 61 62 def parse_tpl_seq(serial: U32, ch: U32, ms: Maybe<&2, Nat>, res_s: String, name_s: String) -> Maybe<&2, TplAtom>: 63 match ms: 64 case None{}: 65 None{} 66 case Some{seq}: 67 Some{TA{serial, ch, seq, String.trim(res_s), String.trim(name_s)}} 68 69 def parse_tpl_chain(serial: U32, chain_s: String, seq_s: String, res_s: String, name_s: String) -> Maybe<&2, TplAtom>: 70 parse_tpl_seq(serial, Pdb.chain_u32(chain_s), Nat.read(String.trim(seq_s)), res_s, name_s) 71 72 def parse_tpl_serial(ms: Maybe<&2, U32>, chain_s: String, seq_s: String, res_s: String, name_s: String) -> Maybe<&2, TplAtom>: 73 match ms: 74 case None{}: 75 None{} 76 case Some{serial}: 77 parse_tpl_chain(serial, chain_s, seq_s, res_s, name_s) 78 79 def parse_tpl_fields(serial_s: String, chain_s: String, seq_s: String, res_s: String, name_s: String) -> Maybe<&2, TplAtom>: 80 parse_tpl_serial(U32.read(String.trim(serial_s)), chain_s, seq_s, res_s, name_s) 81 82 def parse_tpl_line(+line: String) -> Maybe<&2, TplAtom>: 83 parse_tpl_fields(Pdb.pdb_slice(line, 6n, 5n), Pdb.pdb_slice(line, 21n, 1n), Pdb.pdb_slice(line, 22n, 4n), Pdb.pdb_slice(line, 17n, 3n), Pdb.pdb_slice(line, 12n, 4n)) 84 85 def collect_tpl_head(m: Maybe<&2, TplAtom>, rest: List<&2, TplAtom> & Nat) -> List<&2, TplAtom> & Nat: 86 match m: 87 case None{}: 88 match rest: 89 case (as, n): 90 (as, 1n+n) 91 case Some{x}: 92 match rest: 93 case (as, n): 94 (x <> as, n) 95 96 def collect_tpl(lines: List<&2, String>) -> List<&2, TplAtom> & Nat: 97 match lines: 98 case Nil{}: 99 (Nil{}, 0n) 100 case h <> t: 101 collect_tpl_head(parse_tpl_line(h), collect_tpl(t)) 102 103 def tpl_atoms_only(p: List<&2, TplAtom> & Nat) -> List<&2, TplAtom>: 104 match p: 105 case (xs, n): 106 xs