HsingMD / src /casmd /predict /prep.py
zzhaobz's picture
Deploy sanitized fictional Variant A release
4c02fbe
Raw History Blame Contribute Delete
3.91 kB
"""Format sequences for submission to the three online predictors.
This is the 'link out' half of the hybrid model: CasMD writes
submission-ready files and prints the server URLs; the user pastes
into the web UIs manually.
"""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
import json
SERVER_URLS = {
"boltz": "https://tamarind.bio/boltz-2",
"protenix": "https://www.bioos.com/protenix",
"af3": "https://alphafoldserver.com",
}
@dataclass(frozen=True)
class PrepResult:
boltz_input: Path
protenix_input: Path
af3_input: Path
def prep_inputs(
*,
protein_fasta: str,
rna_sequence: str | None,
dna_sequence: str | None,
target_complement: str | None,
output_dir: Path,
job_name: str,
) -> PrepResult:
"""Write three submission files (one per backend) ready to paste."""
output_dir = Path(output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
protein_seq = _fasta_to_seq(protein_fasta)
# ---- Boltz-2 input (YAML-like sequence block) ----
boltz_lines = ["sequences:"]
boltz_lines.append(" - protein:")
boltz_lines.append(" id: A")
boltz_lines.append(f" sequence: {protein_seq}")
chain_id = ord("B")
if rna_sequence:
boltz_lines.append(" - rna:")
boltz_lines.append(f" id: {chr(chain_id)}")
boltz_lines.append(f" sequence: {rna_sequence}")
chain_id += 1
if dna_sequence:
boltz_lines.append(" - dna:")
boltz_lines.append(f" id: {chr(chain_id)}")
boltz_lines.append(f" sequence: {dna_sequence}")
chain_id += 1
if target_complement:
boltz_lines.append(" - dna:")
boltz_lines.append(f" id: {chr(chain_id)}")
boltz_lines.append(f" sequence: {target_complement}")
boltz_path = output_dir / "boltz_input.yaml"
boltz_path.write_text("\n".join(boltz_lines) + "\n")
# ---- Protenix input (JSON) — uses proteinChain/rnaSequence/dnaSequence keys ----
protenix_seqs: list[dict] = [
{"proteinChain": {"sequence": protein_seq, "count": 1}}
]
if rna_sequence:
protenix_seqs.append({"rnaSequence": {"sequence": rna_sequence, "count": 1}})
if dna_sequence:
protenix_seqs.append({"dnaSequence": {"sequence": dna_sequence, "count": 1}})
if target_complement:
protenix_seqs.append({"dnaSequence": {"sequence": target_complement, "count": 1}})
protenix_input = [{"name": job_name, "covalent_bonds": [], "sequences": protenix_seqs}]
protenix_path = output_dir / "protenix_input.json"
protenix_path.write_text(json.dumps(protenix_input, indent=2))
# ---- AF3 input (AlphaFold Server JSON schema — list wrapper + dialect/version) ----
af3_seqs: list[dict] = [
{"proteinChain": {"sequence": protein_seq, "count": 1, "useStructureTemplate": False}}
]
if rna_sequence:
af3_seqs.append({"rnaSequence": {"sequence": rna_sequence, "count": 1}})
if dna_sequence:
af3_seqs.append({"dnaSequence": {"sequence": dna_sequence, "count": 1}})
if target_complement:
af3_seqs.append({"dnaSequence": {"sequence": target_complement, "count": 1}})
af3_input = [
{
"name": job_name,
"modelSeeds": [],
"sequences": af3_seqs,
"dialect": "alphafoldserver",
"version": 3,
}
]
af3_path = output_dir / "af3_input.json"
af3_path.write_text(json.dumps(af3_input, indent=2))
return PrepResult(
boltz_input=boltz_path,
protenix_input=protenix_path,
af3_input=af3_path,
)
def _fasta_to_seq(fasta: str) -> str:
"""Strip header lines from a FASTA, return concatenated sequence."""
lines = [ln.strip() for ln in fasta.strip().splitlines()]
return "".join(ln for ln in lines if not ln.startswith(">"))