Mercurial > repos > galaxy-australia > alphafold2
changeset 20:6ab1a261520a draft
planemo upload for repository https://github.com/usegalaxy-au/tools-au commit c3a90eb12ada44d477541baa4dd6182be29cd554-dirty
author | galaxy-australia |
---|---|
date | Sun, 28 Jul 2024 20:09:55 +0000 |
parents | 2f7702fd0a4c |
children | e7f1b552a695 |
files | alphafold.html alphafold.xml macro_output.xml macro_test_output.xml scripts/alphafold.html scripts/outputs.py |
diffstat | 6 files changed, 826 insertions(+), 702 deletions(-) [+] |
line wrap: on
line diff
--- a/alphafold.html Wed May 08 06:26:55 2024 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,656 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> - - <head> - <meta charset="utf-8"> - <meta http-equiv="X-UA-Compatible" content="IE=edge"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <title> Alphafold structure prediction </title> - - <link rel="preconnect" href="https://fonts.googleapis.com"> - <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> - <link href="https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap" rel="stylesheet"> - <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> - <script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.1.0/chroma.min.js" integrity="sha512-yocoLferfPbcwpCMr8v/B0AB4SWpJlouBwgE0D3ZHaiP1nuu5djZclFEIj9znuqghaZ3tdCMRrreLoM8km+jIQ==" crossorigin="anonymous"></script> - - <style type="text/css"> - * { - margin: 0; - padding: 0; - } - html, body { - width: 100%; - font-size: 1rem; - } - body { - font-family: 'Ubuntu', sans-serif; - } - canvas { - background-color: white; - } - h1, h2, h3, h4, h5, h6 { - color: dodgerblue; - text-align: center; - font-weight: lighter; - } - h1 { - margin: 2rem; - font-size: 3rem; - } - h2 { - font-size: 2rem; - margin-top: 1rem; - margin-bottom: .5rem; - } - button.btn { - color: #ccc; - margin: 1rem; - padding: .5rem; - font-size: 1rem; - min-width: 4rem; - border: none; - border-radius: .5rem; - background-color: grey; - transition-duration: 0.25s; - cursor: pointer; - } - button.btn.selected { - color: #eee; - background-color: dodgerblue; - } - button.btn.green { - color: #eee; - background-color: #10941f; - } - button.btn:focus { - outline: none; - color: inherit; - } - button.btn:hover { - color: white; - box-shadow: 0 0 10px dodgerblue; - } - button.btn.green:hover { - color: white; - box-shadow: 0 0 10px limegreen; - } - .main { - min-height: 90vh; - position: relative; - } - .flex { - display: flex; - justify-content: center; - align-items: center; - padding: 1rem; - } - .col { - flex-direction: column; - flex-grow: 0; - } - .controls { - padding-bottom: 10vh; - } - .box { - padding: .5rem 1rem; - margin: .5rem auto; - width: fit-content; - border-radius: 1rem; - } - .mono { - font-family: monospace; - color: #555; - background-color: #ddd; - padding: .25rem; - border-radius: .25rem; - } - .space-1 { - line-height: 1.2; - } - .space-2 { - line-height: 1.5; - } - .relative { - position: relative; - } - .legend { - max-width: 350px; - } - .legend .scale { - display: flex; - flex-direction: column; - align-items: center; - } - .legend .color { - width: 150px; - height: 30px; - justify-content: space-between; - background: linear-gradient( - 90deg, - rgba(255,55,0,1) 0%, - rgba(216,224,6,1) 33%, - rgba(34,213,238,1) 66%, - rgba(3,30,148,1) 100% - ); - } - .legend .ticks { - margin-top: -1rem; - width: 180px; - justify-content: space-between; - } - #ngl-root-parent { - width: 40vw; - height: 30vw; - margin: auto; - position: relative; - } - #ngl-root { - width: 40vw; - height: 30vw; - border-radius: 15px; - border: 1px solid grey; - } - #ngl-nothing { - position: absolute; - top: 0; - left: 0; - display: none; - text-align: center; - width: 40vw; - height: 30vw; - padding-top: 12vw; - } - #ngl-loading { - position: absolute; - top: 0; - left: 0; - display: flex; - justify-content: center; - align-items: center; - width: 800px; - height: 600px; - width: 40vw; - height: 30vw; - } - #ngl-loading svg { - width: 30%; - height: 30%; - width: 10vw; - height: 10vw; - } - - /* Responsive */ - @media (max-width: 1400px) { - :root { - font-size: 10pt; - } - button.btn { - margin: .5rem; - padding: .25rem; - } - .box { - padding: .5rem; - margin: .5rem auto; - } - .legend { - max-width: 200px; - } - .help-text { - font-size: 0.8rem; - } - .mono { - padding: .25rem .5rem; - } - } - @media (max-width: 1000px) { - :root { - font-size: 8pt; - } - } - @media (max-width: 800px) { - :root { - font-size: 6pt; - } - } - </style> - - <script src="https://cdn.rawgit.com/arose/ngl/v2.0.0-dev.37/dist/ngl.js"></script> - </head> - - - <body> - <h1> Alphafold structure prediction </h1> - - <div class="main flex"> - <div class="col relative"> - <div id="ngl-root-parent"> - - <div id="ngl-root"></div> - - <div id="ngl-nothing"> - Select a representation to display - </div> - - <div id="ngl-loading"> - <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"> - <g transform="rotate(0 50 50)"> - <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> - <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.9166666666666666s" repeatCount="indefinite"></animate> - </rect> - </g><g transform="rotate(30 50 50)"> - <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> - <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.8333333333333334s" repeatCount="indefinite"></animate> - </rect> - </g><g transform="rotate(60 50 50)"> - <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> - <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.75s" repeatCount="indefinite"></animate> - </rect> - </g><g transform="rotate(90 50 50)"> - <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> - <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.6666666666666666s" repeatCount="indefinite"></animate> - </rect> - </g><g transform="rotate(120 50 50)"> - <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> - <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5833333333333334s" repeatCount="indefinite"></animate> - </rect> - </g><g transform="rotate(150 50 50)"> - <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> - <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5s" repeatCount="indefinite"></animate> - </rect> - </g><g transform="rotate(180 50 50)"> - <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> - <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.4166666666666667s" repeatCount="indefinite"></animate> - </rect> - </g><g transform="rotate(210 50 50)"> - <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> - <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.3333333333333333s" repeatCount="indefinite"></animate> - </rect> - </g><g transform="rotate(240 50 50)"> - <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> - <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.25s" repeatCount="indefinite"></animate> - </rect> - </g><g transform="rotate(270 50 50)"> - <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> - <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.16666666666666666s" repeatCount="indefinite"></animate> - </rect> - </g><g transform="rotate(300 50 50)"> - <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> - <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.08333333333333333s" repeatCount="indefinite"></animate> - </rect> - </g><g transform="rotate(330 50 50)"> - <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> - <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animate> - </rect> - </g> - </svg> - </div> - </div> - - <div class="flex"> - <div class="box space-1"> - <p> - <span class="mono">Scroll up/down</span> - to zoom in and out - </p> - <p> - <span class="mono">Click + drag</span> - to rotate the structure - </p> - <p> - <span class="mono">CTRL + click + drag</span> - to move the structure - </p> - <p> - <span class="mono">Click</span> - an atom to bring it into focus - </p> - </div> - - <div class="box legend"> - <div class="scale"> - <div class="color"></div> - <div class="flex ticks"> - <div><50</div> - <div>70</div> - <div>90+</div> - </div> - </div> - - <div> - <p class="text-center"> - <small> - Alphafold produces a - <a href="https://alphafold.ebi.ac.uk/faq#faq-5" target="_blank"> - per-residue confidence score (pLDDT) - </a> - between 0 and 100. Some regions below 50 pLDDT may be - unstructured in isolation. - </small> - </p> - </div> - </div> - </div> - </div> - - <div class="flex col controls"> - <div class="box text-center"> - <h3> Select model </h3> - <p>The top-ranked structures predicted by Alphafold</p> - <div> - <button class="btn selected" id="btn-ranked_0" onclick="setModel(0);"> - Ranked 0 - </button> - - <button class="btn" id="btn-ranked_1" onclick="setModel(1);"> - Ranked 1 - </button> - - <button class="btn" id="btn-ranked_2" onclick="setModel(2);"> - Ranked 2 - </button> - - <button class="btn" id="btn-ranked_3" onclick="setModel(3);"> - Ranked 3 - </button> - - <button class="btn" id="btn-ranked_4" onclick="setModel(4);"> - Ranked 4 - </button> - </div> - </div> - - <div class="box text-center"> - <h3> Toggle representations </h3> - <div> - <button class="btn selected" id="btn-cartoon" onclick="toggleModelRepresentation('cartoon');"> - Cartoon - </button> - - <button class="btn" id="btn-ball-stick" onclick="toggleModelRepresentation('ball+stick');"> - Ball + stick - </button> - - <button class="btn" id="btn-surface" onclick="toggleModelRepresentation('surface');"> - Surface - </button> - - <button class="btn" id="btn-backbone" onclick="toggleModelRepresentation('backbone');"> - Backbone - </button> - </div> - </div> - - <div class="box text-center"> - <h3> Actions </h3> - <div> - <button class="btn selected" id="btn-toggle-spin" onclick="toggleSpin();"> - Toggle spin - </button> - - <button class="btn" id="btn-toggle-dark" onclick="toggleDark();"> - Dark mode - </button> - </div> - </div> - - <div class="box text-center"> - <h3> Download </h3> - <div> - <button class="btn green" onclick="downloadPng();"> - Snapshot - </button> - - <button class="btn green" onclick="downloadPdb();"> - PDB - </button> - </div> - </div> - </div> - </div> - </body> - - - <script type="text/javascript"> - - // Render NGLviewer for PDB files - - // State management has been implemented with vanilla Js but could have used - // Vue - it's a fairly simple use case so a global 'state' object works fine - // without complicating things too much. - - - // Define a custom color scheme to represent model confidence consistently - // across different representations - // ------------------------------------------------------------------------ - const colorScale = chroma.scale([ - 'red', 'yellow', 'green', 'cyan', 'blue' - ]).mode('lab').domain([0, 0.9]); - - const confidenceScheme = NGL.ColormakerRegistry.addScheme(function (params) { - this.atomColor = function (atom) { - // Actually model confidence (pLDDT) - const c = atom.bfactor; - const BREAK_RED = 40; // Below this is just plain red - let range, r, g, b; - - if (c < BREAK_RED) { - return 0xFF0000; - } - const p = (c - BREAK_RED) / (100 - BREAK_RED) - return eval(colorScale(p).hex().replace('#', '0x')); - }; - }); - - // NGL color schemes https://nglviewer.org/ngl/api/manual/usage/coloring.html - const COLORSCHEME = confidenceScheme; //'bfactor' - - const MODELS = [ - 'ranked_0.pdb', - 'ranked_1.pdb', - 'ranked_2.pdb', - 'ranked_3.pdb', - 'ranked_4.pdb', - ] - - const REPRESENTATIONS = [ - 'cartoon', - 'ball+stick', - 'surface', - 'backbone', - ] - - const DEFAULT_REPRESENTATION = REPRESENTATIONS[0]; - const MAX_CLICK_INTERVAL_MS = 500; // For debouncing model clicks - - let stage; - let nonceSetModel; - - let state = { - model: 0, - modelObject: null, - representations: {}, - colorScheme: 'bfactor', - darkMode: false, - loading: 1, - spin: true, - } - - const uri = (i) => MODELS[i]; - // Switch to this function to return sample model URI (local dev) - // const uri = (i) => `https://raw.githubusercontent.com/neoformit/alphafold-galaxy/main/data/${MODELS[i]}`; - - document.addEventListener("DOMContentLoaded", async function () { - // Can set debug for development if NGL is being... funny - // NGL.setDebug(true) - - // Create NGL Stage object - stage = new NGL.Stage("ngl-root", { backgroundColor: 'white' }); - - // Handle window resizing - window.addEventListener("resize", () => stage.handleResize()); - - loadModel(); - while (true) { - if (!state.loading) { - // Reload page if NGL failed to display. Weird occassional bug. - const canvas = document.querySelector('#ngl-root canvas'); - canvas.height < 50 && window.reload(); - break - } - await new Promise(resolve => setTimeout(resolve, 500)); - } - }); - - // Models ------------------------------------------------------------------ - - const setModel = (ix) => { - state.model = ix; - stage.removeComponent(state.modelObject); - setLoading(1); - - // Debounce rapid model clicking with a nonce - nonceSetModel = new Object(); - const localNonce = nonceSetModel; - setTimeout( () => { - if (localNonce === nonceSetModel) { - // The user has stopped clicking, hurray... - loadModel().then(updateButtons); - } - }, MAX_CLICK_INTERVAL_MS); - } - - const loadModel = () => { - reps = Object.keys(state.representations); - if (reps.length) { - state.representations = {}; - } else { - reps = [DEFAULT_REPRESENTATION]; - } - - // Load PDB entry - return stage.loadFile(uri(state.model)).then( (o) => { - state.modelObject = o; - reps.forEach( (r) => addModelRepresentation(r) ); - stage.setSpin(state.spin); - o.autoView(); - setLoading(0); - }) - } - - // Representations --------------------------------------------------------- - - const toggleModelRepresentation = (rep) => { - rep in state.representations ? - removeModelRepresentation(rep) - : addModelRepresentation(rep) - } - - const addModelRepresentation = (rep) => { - state.representations[rep] = - state.modelObject.addRepresentation(rep, {colorScheme: COLORSCHEME}); - updateButtons(); - } - - const removeModelRepresentation = (rep) => { - o = state.representations[rep]; - state.modelObject.removeRepresentation(o); - delete state.representations[rep]; - updateButtons(); - } - - const clearModelRepresentations = () => { - state.modelObject && state.modelObject.removeAllRepresentations(); - state.representations = {}; - } - - // Actions ----------------------------------------------------------------- - - const toggleDark = () => { - state.darkMode = !state.darkMode; - stage.setParameters({ - backgroundColor: state.darkMode ? 'black' : 'white', - }); - const btn = document.querySelector('#btn-toggle-dark'); - btn && btn.classList.toggle('selected'); - } - - const setLoading = (state) => { - document.getElementById('ngl-loading') - .style.display = state ? 'flex' : 'none'; - state.loading = state; - } - - const toggleSpin = () => { - stage.toggleSpin(); - const btn = document.querySelector('#btn-toggle-spin'); - btn && btn.classList.toggle('selected'); - state.spin = !state.spin; - } - - const downloadPng = () => { - const params = { - factor: 3, - antialias: true, - } - stage.makeImage(params).then( (blob) => { - const name = MODELS[state.model].replace('.pdb', '.png'); - const url = URL.createObjectURL(blob); - makeDownload(url, name); - }) - } - - const downloadPdb = () => { - const url = uri(state.model); - const name = `alphafold_${MODELS[state.model]}`; - makeDownload(url, name); - } - - const makeDownload = (url, name) => { - // Will not work with cross-origin urls (i.e. during development) - console.log(`Creating file download for ${name}, href ${url}`); - const saveLink = document.createElement('a'); - saveLink.href = url; - saveLink.download = name; - document.body.appendChild(saveLink); - saveLink.dispatchEvent( - new MouseEvent('click', { - bubbles: true, - cancelable: true, - view: window - }) - ); - document.body.removeChild(saveLink); - } - - const updateButtons = () => { - MODELS.forEach( (name, i) => { - const id = `#btn-${name.replace('.pdb', '')}`; - const btn = document.querySelector(id); - if (!btn) return - i == state.model ? - btn.classList.add('selected') - : btn.classList.remove('selected'); - }) - - REPRESENTATIONS.forEach( (name) => { - const id = `#btn-${name}`.replace('+', '-'); - const btn = document.querySelector(id); - if (!btn) return - if (name in state.representations) { - btn.classList.add('selected') - } else { - btn.classList.remove('selected'); - } - }); - - // Show "Nothing to display" if no representations are selected - document.querySelector('#ngl-nothing').style.display = - Object.keys(state.representations).length ? - 'none' - : 'block'; - } - - </script> - -</html>
--- a/alphafold.xml Wed May 08 06:26:55 2024 +0000 +++ b/alphafold.xml Sun Jul 28 20:09:55 2024 +0000 @@ -1,9 +1,9 @@ -<tool id="alphafold" name="Alphafold 2" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="20.01"> +<tool id="alphafold" name="Alphafold 2" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="22.05"> <description> - AI-guided 3D structural prediction of proteins</description> <macros> - <token name="@TOOL_VERSION@">2.3.1</token> + <token name="@TOOL_VERSION@">2.3.2</token> <token name="@TOOL_MINOR_VERSION@">2.3</token> - <token name="@VERSION_SUFFIX@">5</token> + <token name="@VERSION_SUFFIX@">0</token> <import>macro_output.xml</import> <import>macro_test_output.xml</import> </macros> @@ -17,12 +17,12 @@ <xref type="bio.tools">alphafold_2</xref> </xrefs> <requirements> - <container type="docker">neoformit/alphafold:v2.3.1_2</container> + <container type="docker">neoformit/alphafold:v2.3.2_0</container> </requirements> <required_files> <include path="scripts/outputs.py" /> <include path="scripts/validate_fasta.py" /> - <include path="alphafold.html" /> + <include path="scripts/alphafold.html" /> </required_files> <command detect_errors="exit_code"><![CDATA[ @@ -46,7 +46,7 @@ && python3 '$__tool_directory__/scripts/validate_fasta.py' input.fasta --min_length \${ALPHAFOLD_AA_LENGTH_MIN:-0} --max_length \${ALPHAFOLD_AA_LENGTH_MAX:-0} -#if $model_preset == 'multimer': +#if $model_preset.selection == 'multimer': --multimer --max-sequences \${ALPHAFOLD_MAX_SEQUENCES:-10} #end if @@ -60,7 +60,7 @@ ## Run AlphaFold ------------------------------------------------------------- #if os.environ.get('PLANEMO_TESTING'): ## Run in testing mode (mocks a successful AlphaFold run by copying outputs) - && echo "Creating dummy outputs for model_preset=$model_preset..." + && echo "Creating dummy outputs for model_preset=$model_preset.selection..." && bash '$__tool_directory__/scripts/mock_alphafold.sh' $model_preset #else: ## Run AlphaFold @@ -68,7 +68,7 @@ --fasta_paths alphafold.fasta --output_dir output --data_dir \${ALPHAFOLD_DB:-/data}/@TOOL_MINOR_VERSION@/ - --model_preset=$model_preset + --model_preset=$model_preset.selection ## Set reference database paths --uniref90_database_path \${ALPHAFOLD_DB:-/data}/@TOOL_MINOR_VERSION@/uniref90/uniref90.fasta @@ -83,21 +83,33 @@ --small_bfd_database_path \${ALPHAFOLD_DB:-/data}/@TOOL_MINOR_VERSION@/small_bfd/bfd-first_non_consensus_sequences.fasta #end if - #if $max_template_date: - --max_template_date=$max_template_date + #if $advanced.max_template_date: + --max_template_date=$advanced.max_template_date #else --max_template_date=\$TODAY #end if - --use_gpu_relax=\${ALPHAFOLD_USE_GPU:-True} ## introduced in v2.1.2 + --use_gpu_relax=\${ALPHAFOLD_USE_GPU:-True} - #if $model_preset == 'multimer': + #if $model_preset.selection == 'multimer': --pdb_seqres_database_path=\${ALPHAFOLD_DB:-/data}/@TOOL_MINOR_VERSION@/pdb_seqres/pdb_seqres.txt --uniprot_database_path=\${ALPHAFOLD_DB:-/data}/@TOOL_MINOR_VERSION@/uniprot/uniprot.fasta - --num_multimer_predictions_per_model=1 ## introduced in v2.2.0 + --num_multimer_predictions_per_model=$model_preset.num_multimer_predictions_per_model #else --pdb70_database_path \${ALPHAFOLD_DB:-/data}/@TOOL_MINOR_VERSION@/pdb70/pdb70 #end if + + ## Galaxy-specific options -------------------------------------------- + ## See https://github.com/neoformit/alphafold/tree/release_2.3.2_galaxy + #if $advanced.disable_amber_relax: + --disable_amber_relax + #end if + + #if $advanced.limit_model_outputs: + --output_models=$limit_model_outputs + #end if + ## End Galaxy-specific options ---------------------------------------- + #end if ## Generate additional outputs ------------------------------------------------ @@ -106,13 +118,13 @@ $outputs.model_pkls $outputs.pae_csv $outputs.plots -#if $model_preset == 'multimer': +#if $model_preset.selection == 'multimer': --multimer #end if ## HTML output && mkdir -p '${ html.files_path }' -&& cp '$__tool_directory__/alphafold.html' '${html}' +&& cp output/alphafold/extra/alphafold.html '${html}' && cp output/alphafold/ranked_*.pdb '${html.files_path}' ## This is a (hacky) fix for a bug that has appeared in multiple Pulsar servers. @@ -136,21 +148,6 @@ </conditional> <param - name="max_template_date" - type="text" - label="Max template date (yyyy-mm-dd) (optional)" - help="The model will reference PDB structures deposited before this date only. Defaults to today's date." - optional="true" - > - <sanitizer> - <valid initial="string.digits"> - <add value="-" /> - </valid> - </sanitizer> - <validator type="regex">[0-9]{4}-[0-9]{2}-[0-9]{2}</validator> - </param> - - <param name="dbs" type="select" display="radio" @@ -162,8 +159,9 @@ <option value="full">Full database</option> </param> + <conditional name="model_preset"> <param - name="model_preset" + name="selection" type="select" label="Model preset" help="Select which prediction model to run. The monomer model is the most accurate for single protein prediction. The multimer model allows prediction of protein complexes." @@ -176,6 +174,56 @@ multimer - model a protein complex (requires multi-sequence FASTA input) </option> </param> + <when value="monomer"></when> + <when value="monomer_ptm"></when> + <when value="multimer"> + <param + name="num_multimer_predictions_per_model" + type="integer" + value="5" + label="Multimer predictions per model" + help="How many predictions (each with a different random seed) will be generated per model. E.g. if this is 2 and there are 5 models then there will be 10 predictions per input. For a small drop in accuracy you may wish to run a single seed per model (default 5, max 10)." + min="1" + max="10" + /> + </when> + </conditional> + + <section name="advanced" title="Advanced options" expanded="false"> + <param + name="max_template_date" + type="text" + label="Max template date (yyyy-mm-dd) (optional)" + help="The model will reference PDB structures deposited before this date only. Defaults to today's date." + optional="true" + > + <sanitizer> + <valid initial="string.digits"> + <add value="-" /> + </valid> + </sanitizer> + <validator type="regex">[0-9]{4}-[0-9]{2}-[0-9]{2}</validator> + </param> + + <param + name="disable_amber_relax" + type="boolean" + label="Disable Amber relaxation" + value="false" + optional="true" + help="Amber relaxation can be disabled to speed up processing time. Amber relaxation is used to refine predicted structures by removing stereochemical violations, resulting in more accurate prediction of side-chain geometry. Disabling this option with large proteins may lead to artefacts in the predicted structure. Disabling amber relax will result in the unrelaxed models being collected as PDB outputs." + /> + + <param + name="limit_model_outputs" + type="integer" + label="Limit model outputs" + value="5" + help="Limit the number of models to output. The top N models will be output, where N is the value entered here (default 5). Please note that the top-ranking model is not always the correct one, and it is usually recommended to inspect multiple models. Reducing the number of models will result in a slight reduction in run time." + min="1" + max="5" + /> + </section> <section name="outputs" title="Optional outputs" expanded="false"> <param @@ -228,6 +276,13 @@ label="relax_metrics.json" help="A JSON-formatted text file containing relax metrics (mostly remaining violations)." /> + <param + name="timings_json" + type="boolean" + checked="false" + label="timings.json" + help="A JSON file with timings reported for each phase of the AlphaFold run." + /> </section> </inputs> @@ -241,6 +296,7 @@ <expand macro="output_pae_csv" /> <expand macro="output_plots" /> <expand macro="output_relax_json" /> + <expand macro="output_timings_json" /> </outputs> <tests> @@ -250,7 +306,7 @@ <param name="input_mode" value="history"/> <param name="fasta_file" value="test1.fasta"/> </conditional> - <param name="model_preset" value="monomer"/> + <param name="model_preset|selection" value="monomer"/> <expand macro="test_output_pdb_models" /> </test> @@ -260,7 +316,7 @@ <param name="input_mode" value="history"/> <param name="fasta_file" value="test1.fasta"/> </conditional> - <param name="model_preset" value="monomer"/> + <param name="model_preset|selection" value="monomer"/> <param name="outputs|plots" value="true"/> <param name="outputs|confidence_scores" value="true"/> <param name="outputs|plddts" value="true"/> @@ -281,7 +337,7 @@ <param name="input_mode" value="history"/> <param name="fasta_file" value="test1.fasta"/> </conditional> - <param name="model_preset" value="monomer_ptm"/> + <param name="model_preset|selection" value="monomer_ptm"/> <param name="outputs|plots" value="true"/> <param name="outputs|confidence_scores" value="true"/> <param name="outputs|plddts" value="true"/> @@ -303,19 +359,21 @@ <param name="input_mode" value="history"/> <param name="fasta_file" value="multimer.fasta"/> </conditional> - <param name="model_preset" value="multimer"/> + <param name="model_preset|selection" value="multimer"/> <param name="outputs|plots" value="true"/> <param name="outputs|confidence_scores" value="true"/> <param name="outputs|plddts" value="true"/> <param name="outputs|pae_csv" value="true"/> <param name="outputs|model_pkls" value="true"/> <param name="outputs|relax_json" value="true"/> + <param name="outputs|timings_json" value="true"/> <expand macro="test_output_plots_3" /> <expand macro="test_output_confidence_scores" /> <expand macro="test_output_plddts" /> <expand macro="test_output_pdb_models" /> <expand macro="test_output_pickles" /> <expand macro="test_output_relax_json" /> + <expand macro="test_output_timings_json" /> <expand macro="test_output_pae_csv" /> </test> </tests> @@ -325,7 +383,7 @@ | AlphaFold v2: AI-guided 3D structural prediction of proteins | - | **NOTE: this tool packages AlphaFold v2.3.1.** + | **NOTE: this tool packages** `a modified branch of AlphaFold v2.3.2. <https://github.com/neoformit/alphafold/tree/release_2.3.2_galaxy>`_ | | This means that the neural network has been trained on PDBs with a release | date before 2021-09-30 (the training cutoff was 2018-04-30 until ``v2.3.0``). @@ -333,12 +391,9 @@ | Find out more in the technical and release notes: | - - `Release notes for v2.3.1 <https://github.com/deepmind/alphafold/releases/tag/v2.3.1>`_ + - `Release notes for v2.3.2 <https://github.com/deepmind/alphafold/releases/tag/v2.3.2>`_ - `Technical notes for v2.3 <https://github.com/deepmind/alphafold/blob/main/docs/technical_note_v2.3.0.md>`_ - | If you want to use AlphaFold trained against an older cutoff date, switch to Galaxy version ``2.1.2`` (which was trained to data up to 2018-04-30). - | - **What it does** *What is AlphaFold?* @@ -362,6 +417,7 @@ | You can choose to input either a file from your Galaxy history or paste a sequence into a text box. | If you choose the ``multimer`` option, you can supply a FASTA file containing **multiple sequences** to be folded concurrently into a multimer. | + | For pairwise screening of target-candidate with multimer, you can submit a list of paired protein sequences in batch mode (i.e. two protein sequences in each FASTA file). | **Outputs** @@ -380,7 +436,7 @@ *PDB files* - | Five PDB (Protein Data Bank) files are be created, ordered by rank, as predicted by AlphaFold. + | PDB (Protein Data Bank) files (5 by default) are be created, ordered by rank, as predicted by AlphaFold. The tool produces 5 models by default, but this can be reduced with the "Limit model outputs" for a reduced run time. | These files describe the molecular structures and can be used for downstream analysis. e.g. *in silico* molecular docking. | **PLEASE NOTE** that all outputs have been renamed to their respective rank order, including model and model.pkl files. | @@ -421,6 +477,12 @@ | | + *timings.json (optional)* + + | A JSON-formatted text file containing the timings for each phase of the prediction. + | + | + **AlphaFold configuration** | We have configured AlphaFold to run with the parameters suggested by default on `AlphaFold's GitHub <https://github.com/deepmind/alphafold>`_.
--- a/macro_output.xml Wed May 08 06:26:55 2024 +0000 +++ b/macro_output.xml Sun Jul 28 20:09:55 2024 +0000 @@ -1,9 +1,17 @@ <macros> <xml name="output_pdb_models"> - <data name="model5" format="pdb" from_work_dir="output/alphafold/ranked_4.pdb" label="${tool.name} on ${on_string}: PDB ranked 4"/> - <data name="model4" format="pdb" from_work_dir="output/alphafold/ranked_3.pdb" label="${tool.name} on ${on_string}: PDB ranked 3"/> - <data name="model3" format="pdb" from_work_dir="output/alphafold/ranked_2.pdb" label="${tool.name} on ${on_string}: PDB ranked 2"/> - <data name="model2" format="pdb" from_work_dir="output/alphafold/ranked_1.pdb" label="${tool.name} on ${on_string}: PDB ranked 1"/> + <data name="model5" format="pdb" from_work_dir="output/alphafold/ranked_4.pdb" label="${tool.name} on ${on_string}: PDB ranked 4"> + <filter>advanced['limit_model_outputs'] > 4</filter> + </data> + <data name="model4" format="pdb" from_work_dir="output/alphafold/ranked_3.pdb" label="${tool.name} on ${on_string}: PDB ranked 3"> + <filter>advanced['limit_model_outputs'] > 3</filter> + </data> + <data name="model3" format="pdb" from_work_dir="output/alphafold/ranked_2.pdb" label="${tool.name} on ${on_string}: PDB ranked 2"> + <filter>advanced['limit_model_outputs'] > 2</filter> + </data> + <data name="model2" format="pdb" from_work_dir="output/alphafold/ranked_1.pdb" label="${tool.name} on ${on_string}: PDB ranked 1"> + <filter>advanced['limit_model_outputs'] > 1</filter> + </data> <data name="model1" format="pdb" from_work_dir="output/alphafold/ranked_0.pdb" label="${tool.name} on ${on_string}: PDB ranked 0"/> </xml> @@ -16,6 +24,7 @@ > <filter>outputs['pae_csv']</filter> <filter>model_preset != "monomer"</filter> + <filter>advanced['limit_model_outputs'] > 4</filter> </data> <data name="pae_ranked_3" @@ -25,6 +34,7 @@ > <filter>outputs['pae_csv']</filter> <filter>model_preset != "monomer"</filter> + <filter>advanced['limit_model_outputs'] > 3</filter> </data> <data name="pae_ranked_2" @@ -34,6 +44,7 @@ > <filter>outputs['pae_csv']</filter> <filter>model_preset != "monomer"</filter> + <filter>advanced['limit_model_outputs'] > 2</filter> </data> <data name="pae_ranked_1" @@ -43,6 +54,7 @@ > <filter>outputs['pae_csv']</filter> <filter>model_preset != "monomer"</filter> + <filter>advanced['limit_model_outputs'] > 1</filter> </data> <data name="pae_ranked_0" @@ -63,6 +75,7 @@ label="${tool.name} on ${on_string}: ranked_4.pkl" > <filter>outputs['model_pkls']</filter> + <filter>advanced['limit_model_outputs'] > 4</filter> </data> <data name="output_ranked_3_pkl" @@ -71,6 +84,7 @@ label="${tool.name} on ${on_string}: ranked_3.pkl" > <filter>outputs['model_pkls']</filter> + <filter>advanced['limit_model_outputs'] > 3</filter> </data> <data name="output_ranked_2_pkl" @@ -79,6 +93,7 @@ label="${tool.name} on ${on_string}: ranked_2.pkl" > <filter>outputs['model_pkls']</filter> + <filter>advanced['limit_model_outputs'] > 2</filter> </data> <data name="output_ranked_1_pkl" @@ -87,6 +102,7 @@ label="${tool.name} on ${on_string}: ranked_1.pkl" > <filter>outputs['model_pkls']</filter> + <filter>advanced['limit_model_outputs'] > 1</filter> </data> <data name="output_ranked_0_pkl" @@ -106,6 +122,7 @@ label="${tool.name} on ${on_string}: pLDDT/PAE plot ranked 4" > <filter>outputs['plots']</filter> + <filter>advanced['limit_model_outputs'] > 4</filter> </data> <data name="plot_ranked_3" @@ -114,6 +131,7 @@ label="${tool.name} on ${on_string}: pLDDT/PAE plot ranked 3" > <filter>outputs['plots']</filter> + <filter>advanced['limit_model_outputs'] > 3</filter> </data> <data name="plot_ranked_2" @@ -122,6 +140,7 @@ label="${tool.name} on ${on_string}: pLDDT/PAE plot ranked 2" > <filter>outputs['plots']</filter> + <filter>advanced['limit_model_outputs'] > 2</filter> </data> <data name="plot_ranked_1" @@ -130,6 +149,7 @@ label="${tool.name} on ${on_string}: pLDDT/PAE plot ranked 1" > <filter>outputs['plots']</filter> + <filter>advanced['limit_model_outputs'] > 1</filter> </data> <data name="plot_ranked_0" @@ -173,4 +193,15 @@ <filter>outputs['relax_json']</filter> </data> </xml> + + <xml name="output_timings_json"> + <data + name="output_timings_json" + format="json" + from_work_dir="output/alphafold/timings.json" + label="${tool.name} on ${on_string}: timings.json" + > + <filter>outputs['timings_json']</filter> + </data> + </xml> </macros>
--- a/macro_test_output.xml Wed May 08 06:26:55 2024 +0000 +++ b/macro_test_output.xml Sun Jul 28 20:09:55 2024 +0000 @@ -26,6 +26,14 @@ </output> </xml> + <xml name="test_output_timings_json"> + <output name="output_timings_json"> + <assert_contents> + <has_size min="500" /> + </assert_contents> + </output> + </xml> + <xml name="test_output_pdb_models"> <output name="model1"> <assert_contents>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/alphafold.html Sun Jul 28 20:09:55 2024 +0000 @@ -0,0 +1,656 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> + + <head> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + + <title> Alphafold structure prediction </title> + + <link rel="preconnect" href="https://fonts.googleapis.com"> + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> + <link href="https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap" rel="stylesheet"> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> + <script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.1.0/chroma.min.js" integrity="sha512-yocoLferfPbcwpCMr8v/B0AB4SWpJlouBwgE0D3ZHaiP1nuu5djZclFEIj9znuqghaZ3tdCMRrreLoM8km+jIQ==" crossorigin="anonymous"></script> + + <style type="text/css"> + * { + margin: 0; + padding: 0; + } + html, body { + width: 100%; + font-size: 1rem; + } + body { + font-family: 'Ubuntu', sans-serif; + } + canvas { + background-color: white; + } + h1, h2, h3, h4, h5, h6 { + color: dodgerblue; + text-align: center; + font-weight: lighter; + } + h1 { + margin: 2rem; + font-size: 3rem; + } + h2 { + font-size: 2rem; + margin-top: 1rem; + margin-bottom: .5rem; + } + button.btn { + color: #ccc; + margin: 1rem; + padding: .5rem; + font-size: 1rem; + min-width: 4rem; + border: none; + border-radius: .5rem; + background-color: grey; + transition-duration: 0.25s; + cursor: pointer; + } + button.btn.selected { + color: #eee; + background-color: dodgerblue; + } + button.btn.green { + color: #eee; + background-color: #10941f; + } + button.btn:focus { + outline: none; + color: inherit; + } + button.btn:hover { + color: white; + box-shadow: 0 0 10px dodgerblue; + } + button.btn.green:hover { + color: white; + box-shadow: 0 0 10px limegreen; + } + .main { + min-height: 90vh; + position: relative; + } + .flex { + display: flex; + justify-content: center; + align-items: center; + padding: 1rem; + } + .col { + flex-direction: column; + flex-grow: 0; + } + .controls { + padding-bottom: 10vh; + } + .box { + padding: .5rem 1rem; + margin: .5rem auto; + width: fit-content; + border-radius: 1rem; + } + .mono { + font-family: monospace; + color: #555; + background-color: #ddd; + padding: .25rem; + border-radius: .25rem; + } + .space-1 { + line-height: 1.2; + } + .space-2 { + line-height: 1.5; + } + .relative { + position: relative; + } + .legend { + max-width: 350px; + } + .legend .scale { + display: flex; + flex-direction: column; + align-items: center; + } + .legend .color { + width: 150px; + height: 30px; + justify-content: space-between; + background: linear-gradient( + 90deg, + rgba(255,55,0,1) 0%, + rgba(216,224,6,1) 33%, + rgba(34,213,238,1) 66%, + rgba(3,30,148,1) 100% + ); + } + .legend .ticks { + margin-top: -1rem; + width: 180px; + justify-content: space-between; + } + #ngl-root-parent { + width: 40vw; + height: 30vw; + margin: auto; + position: relative; + } + #ngl-root { + width: 40vw; + height: 30vw; + border-radius: 15px; + border: 1px solid grey; + } + #ngl-nothing { + position: absolute; + top: 0; + left: 0; + display: none; + text-align: center; + width: 40vw; + height: 30vw; + padding-top: 12vw; + } + #ngl-loading { + position: absolute; + top: 0; + left: 0; + display: flex; + justify-content: center; + align-items: center; + width: 800px; + height: 600px; + width: 40vw; + height: 30vw; + } + #ngl-loading svg { + width: 30%; + height: 30%; + width: 10vw; + height: 10vw; + } + + /* Responsive */ + @media (max-width: 1400px) { + :root { + font-size: 10pt; + } + button.btn { + margin: .5rem; + padding: .25rem; + } + .box { + padding: .5rem; + margin: .5rem auto; + } + .legend { + max-width: 200px; + } + .help-text { + font-size: 0.8rem; + } + .mono { + padding: .25rem .5rem; + } + } + @media (max-width: 1000px) { + :root { + font-size: 8pt; + } + } + @media (max-width: 800px) { + :root { + font-size: 6pt; + } + } + </style> + + <script src="https://cdn.rawgit.com/arose/ngl/v2.0.0-dev.37/dist/ngl.js"></script> + </head> + + + <body> + <h1> Alphafold structure prediction </h1> + + <div class="main flex"> + <div class="col relative"> + <div id="ngl-root-parent"> + + <div id="ngl-root"></div> + + <div id="ngl-nothing"> + Select a representation to display + </div> + + <div id="ngl-loading"> + <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"> + <g transform="rotate(0 50 50)"> + <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> + <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.9166666666666666s" repeatCount="indefinite"></animate> + </rect> + </g><g transform="rotate(30 50 50)"> + <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> + <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.8333333333333334s" repeatCount="indefinite"></animate> + </rect> + </g><g transform="rotate(60 50 50)"> + <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> + <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.75s" repeatCount="indefinite"></animate> + </rect> + </g><g transform="rotate(90 50 50)"> + <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> + <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.6666666666666666s" repeatCount="indefinite"></animate> + </rect> + </g><g transform="rotate(120 50 50)"> + <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> + <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5833333333333334s" repeatCount="indefinite"></animate> + </rect> + </g><g transform="rotate(150 50 50)"> + <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> + <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.5s" repeatCount="indefinite"></animate> + </rect> + </g><g transform="rotate(180 50 50)"> + <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> + <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.4166666666666667s" repeatCount="indefinite"></animate> + </rect> + </g><g transform="rotate(210 50 50)"> + <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> + <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.3333333333333333s" repeatCount="indefinite"></animate> + </rect> + </g><g transform="rotate(240 50 50)"> + <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> + <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.25s" repeatCount="indefinite"></animate> + </rect> + </g><g transform="rotate(270 50 50)"> + <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> + <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.16666666666666666s" repeatCount="indefinite"></animate> + </rect> + </g><g transform="rotate(300 50 50)"> + <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> + <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="-0.08333333333333333s" repeatCount="indefinite"></animate> + </rect> + </g><g transform="rotate(330 50 50)"> + <rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill="#88879e"> + <animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animate> + </rect> + </g> + </svg> + </div> + </div> + + <div class="flex"> + <div class="box space-1"> + <p> + <span class="mono">Scroll up/down</span> + to zoom in and out + </p> + <p> + <span class="mono">Click + drag</span> + to rotate the structure + </p> + <p> + <span class="mono">CTRL + click + drag</span> + to move the structure + </p> + <p> + <span class="mono">Click</span> + an atom to bring it into focus + </p> + </div> + + <div class="box legend"> + <div class="scale"> + <div class="color"></div> + <div class="flex ticks"> + <div><50</div> + <div>70</div> + <div>90+</div> + </div> + </div> + + <div> + <p class="text-center"> + <small> + Alphafold produces a + <a href="https://alphafold.ebi.ac.uk/faq#faq-5" target="_blank"> + per-residue confidence score (pLDDT) + </a> + between 0 and 100. Some regions below 50 pLDDT may be + unstructured in isolation. + </small> + </p> + </div> + </div> + </div> + </div> + + <div class="flex col controls"> + <div class="box text-center"> + <h3> Select model </h3> + <p>The top-ranked structures predicted by Alphafold</p> + <div> + <button class="btn selected" id="btn-ranked_0" onclick="setModel(0);"> + Ranked 0 + </button> + + <button class="btn" id="btn-ranked_1" onclick="setModel(1);"> + Ranked 1 + </button> + + <button class="btn" id="btn-ranked_2" onclick="setModel(2);"> + Ranked 2 + </button> + + <button class="btn" id="btn-ranked_3" onclick="setModel(3);"> + Ranked 3 + </button> + + <button class="btn" id="btn-ranked_4" onclick="setModel(4);"> + Ranked 4 + </button> + </div> + </div> + + <div class="box text-center"> + <h3> Toggle representations </h3> + <div> + <button class="btn selected" id="btn-cartoon" onclick="toggleModelRepresentation('cartoon');"> + Cartoon + </button> + + <button class="btn" id="btn-ball-stick" onclick="toggleModelRepresentation('ball+stick');"> + Ball + stick + </button> + + <button class="btn" id="btn-surface" onclick="toggleModelRepresentation('surface');"> + Surface + </button> + + <button class="btn" id="btn-backbone" onclick="toggleModelRepresentation('backbone');"> + Backbone + </button> + </div> + </div> + + <div class="box text-center"> + <h3> Actions </h3> + <div> + <button class="btn selected" id="btn-toggle-spin" onclick="toggleSpin();"> + Toggle spin + </button> + + <button class="btn" id="btn-toggle-dark" onclick="toggleDark();"> + Dark mode + </button> + </div> + </div> + + <div class="box text-center"> + <h3> Download </h3> + <div> + <button class="btn green" onclick="downloadPng();"> + Snapshot + </button> + + <button class="btn green" onclick="downloadPdb();"> + PDB + </button> + </div> + </div> + </div> + </div> + </body> + + + <script type="text/javascript"> + + // Render NGLviewer for PDB files + + // State management has been implemented with vanilla Js but could have used + // Vue - it's a fairly simple use case so a global 'state' object works fine + // without complicating things too much. + + + // Define a custom color scheme to represent model confidence consistently + // across different representations + // ------------------------------------------------------------------------ + const colorScale = chroma.scale([ + 'red', 'yellow', 'green', 'cyan', 'blue' + ]).mode('lab').domain([0, 0.9]); + + const confidenceScheme = NGL.ColormakerRegistry.addScheme(function (params) { + this.atomColor = function (atom) { + // Actually model confidence (pLDDT) + const c = atom.bfactor; + const BREAK_RED = 40; // Below this is just plain red + let range, r, g, b; + + if (c < BREAK_RED) { + return 0xFF0000; + } + const p = (c - BREAK_RED) / (100 - BREAK_RED) + return eval(colorScale(p).hex().replace('#', '0x')); + }; + }); + + // NGL color schemes https://nglviewer.org/ngl/api/manual/usage/coloring.html + const COLORSCHEME = confidenceScheme; //'bfactor' + + const MODELS = [ + 'ranked_0.pdb', + 'ranked_1.pdb', + 'ranked_2.pdb', + 'ranked_3.pdb', + 'ranked_4.pdb', + ] + + const REPRESENTATIONS = [ + 'cartoon', + 'ball+stick', + 'surface', + 'backbone', + ] + + const DEFAULT_REPRESENTATION = REPRESENTATIONS[0]; + const MAX_CLICK_INTERVAL_MS = 500; // For debouncing model clicks + + let stage; + let nonceSetModel; + + let state = { + model: 0, + modelObject: null, + representations: {}, + colorScheme: 'bfactor', + darkMode: false, + loading: 1, + spin: true, + } + + const uri = (i) => MODELS[i]; + // Switch to this function to return sample model URI (local dev) + // const uri = (i) => `https://raw.githubusercontent.com/neoformit/alphafold-galaxy/main/data/${MODELS[i]}`; + + document.addEventListener("DOMContentLoaded", async function () { + // Can set debug for development if NGL is being... funny + // NGL.setDebug(true) + + // Create NGL Stage object + stage = new NGL.Stage("ngl-root", { backgroundColor: 'white' }); + + // Handle window resizing + window.addEventListener("resize", () => stage.handleResize()); + + loadModel(); + while (true) { + if (!state.loading) { + // Reload page if NGL failed to display. Weird occassional bug. + const canvas = document.querySelector('#ngl-root canvas'); + canvas.height < 50 && window.reload(); + break + } + await new Promise(resolve => setTimeout(resolve, 500)); + } + }); + + // Models ------------------------------------------------------------------ + + const setModel = (ix) => { + state.model = ix; + stage.removeComponent(state.modelObject); + setLoading(1); + + // Debounce rapid model clicking with a nonce + nonceSetModel = new Object(); + const localNonce = nonceSetModel; + setTimeout( () => { + if (localNonce === nonceSetModel) { + // The user has stopped clicking, hurray... + loadModel().then(updateButtons); + } + }, MAX_CLICK_INTERVAL_MS); + } + + const loadModel = () => { + reps = Object.keys(state.representations); + if (reps.length) { + state.representations = {}; + } else { + reps = [DEFAULT_REPRESENTATION]; + } + + // Load PDB entry + return stage.loadFile(uri(state.model)).then( (o) => { + state.modelObject = o; + reps.forEach( (r) => addModelRepresentation(r) ); + stage.setSpin(state.spin); + o.autoView(); + setLoading(0); + }) + } + + // Representations --------------------------------------------------------- + + const toggleModelRepresentation = (rep) => { + rep in state.representations ? + removeModelRepresentation(rep) + : addModelRepresentation(rep) + } + + const addModelRepresentation = (rep) => { + state.representations[rep] = + state.modelObject.addRepresentation(rep, {colorScheme: COLORSCHEME}); + updateButtons(); + } + + const removeModelRepresentation = (rep) => { + o = state.representations[rep]; + state.modelObject.removeRepresentation(o); + delete state.representations[rep]; + updateButtons(); + } + + const clearModelRepresentations = () => { + state.modelObject && state.modelObject.removeAllRepresentations(); + state.representations = {}; + } + + // Actions ----------------------------------------------------------------- + + const toggleDark = () => { + state.darkMode = !state.darkMode; + stage.setParameters({ + backgroundColor: state.darkMode ? 'black' : 'white', + }); + const btn = document.querySelector('#btn-toggle-dark'); + btn && btn.classList.toggle('selected'); + } + + const setLoading = (state) => { + document.getElementById('ngl-loading') + .style.display = state ? 'flex' : 'none'; + state.loading = state; + } + + const toggleSpin = () => { + stage.toggleSpin(); + const btn = document.querySelector('#btn-toggle-spin'); + btn && btn.classList.toggle('selected'); + state.spin = !state.spin; + } + + const downloadPng = () => { + const params = { + factor: 3, + antialias: true, + } + stage.makeImage(params).then( (blob) => { + const name = MODELS[state.model].replace('.pdb', '.png'); + const url = URL.createObjectURL(blob); + makeDownload(url, name); + }) + } + + const downloadPdb = () => { + const url = uri(state.model); + const name = `alphafold_${MODELS[state.model]}`; + makeDownload(url, name); + } + + const makeDownload = (url, name) => { + // Will not work with cross-origin urls (i.e. during development) + console.log(`Creating file download for ${name}, href ${url}`); + const saveLink = document.createElement('a'); + saveLink.href = url; + saveLink.download = name; + document.body.appendChild(saveLink); + saveLink.dispatchEvent( + new MouseEvent('click', { + bubbles: true, + cancelable: true, + view: window + }) + ); + document.body.removeChild(saveLink); + } + + const updateButtons = () => { + MODELS.forEach( (name, i) => { + const id = `#btn-${name.replace('.pdb', '')}`; + const btn = document.querySelector(id); + if (!btn) return + i == state.model ? + btn.classList.add('selected') + : btn.classList.remove('selected'); + }) + + REPRESENTATIONS.forEach( (name) => { + const id = `#btn-${name}`.replace('+', '-'); + const btn = document.querySelector(id); + if (!btn) return + if (name in state.representations) { + btn.classList.add('selected') + } else { + btn.classList.remove('selected'); + } + }); + + // Show "Nothing to display" if no representations are selected + document.querySelector('#ngl-nothing').style.display = + Object.keys(state.representations).length ? + 'none' + : 'block'; + } + + </script> + +</html>
--- a/scripts/outputs.py Wed May 08 06:26:55 2024 +0000 +++ b/scripts/outputs.py Sun Jul 28 20:09:55 2024 +0000 @@ -42,6 +42,12 @@ 'multimer': 'iptm+ptm', } +HTML_PATH = Path(__file__).parent / "alphafold.html" +HTML_OUTPUT_FILENAME = 'alphafold.html' +HTML_BUTTON_ATTR = 'class="btn" id="btn-ranked_{rank}"' +HTML_BUTTON_ATTR_DISABLED = ( + 'class="btn disabled" id="btn-ranked_{rank}" disabled') + class Settings: """Parse and store settings/config.""" @@ -188,7 +194,7 @@ """Write per-model confidence scores.""" path = context.settings.workdir / OUTPUTS['model_confidence_scores'] with open(path, 'w') as f: - for rank in range(1, 6): + for rank in range(1, len(context.model_pkl_paths) + 1): score = ranking.get_plddt_for_rank(rank) f.write(f'ranked_{rank - 1}\t{score:.2f}\n') @@ -300,6 +306,22 @@ plt.savefig(png_path) +def template_html(context: ExecutionContext): + """Template HTML file. + + Remove buttons that are redundant with limited model outputs. + """ + print("Templating HTML file...") + with open(HTML_PATH) as f: + html = f.read() + for i in range(len(context.model_pkl_paths), 5): + btn_id = HTML_BUTTON_ATTR.format(rank=i) + btn_attr_disabled = HTML_BUTTON_ATTR_DISABLED.format(rank=i) + html = html.replace(btn_id, btn_attr_disabled) + with open(context.settings.output_dir / HTML_OUTPUT_FILENAME, 'w') as f: + f.write(html) + + def main(): """Parse output files and generate additional output files.""" settings = Settings() @@ -307,6 +329,7 @@ ranking = ResultRanking(context) write_confidence_scores(ranking, context) rekey_relax_metrics(ranking, context) + template_html(context) # Optional outputs if settings.output_model_pkls: