changeset 0:0c3f56c85e98 draft default tip

planemo upload for repository https://github.com/galaxyproject/tools-iuc/main/tools/swissmodel_modelling_api commit 43b5bef8757185b4c077effd0bad846f25d408db
author iuc
date Thu, 11 Dec 2025 19:32:14 +0000
parents
children
files LICENSE README.rst sm_api_wrapper.py swissmodel_api.xml test-data/model_01.pdb
diffstat 5 files changed, 1263 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE	Thu Dec 11 19:32:14 2025 +0000
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 SIB, University of Basel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.rst	Thu Dec 11 19:32:14 2025 +0000
@@ -0,0 +1,42 @@
+============================================
+SWISS-MODEL Modelling API Galaxy integration
+============================================
+
+This is a wrapper for the
+`SWISS-MODEL API <https://swissmodel.expasy.org/docs/help#modelling_api>`_.
+
+`SWISS-MODEL <https://swissmodel.expasy.org/>`_ is a web-based integrated
+service dedicated to protein structure homology modelling.
+
+With the API wrapper, you are able to build protein models
+
+- fully automated (Automodel mode)
+- for a specific alignment provided by the user (Alignment mode)
+- based on user provider template coordinates (User template mode)
+
+Models can be created in ModelCIF format and legacy PDB format.
+
+Documentation on how to use the tool inside Galaxy is in the tool XML file.
+
+Installation
+------------
+
+Needs Python (>=3.11) and the Python requests package (2.32.5).
+
+Attribution
+-----------
+
+When you publish or report results using SWISS-MODEL, please cite the relevant
+publications found on https://swissmodel.expasy.org/docs/references (and on the
+Galaxy tool page, once installed).
+
+License
+-------
+
+License of this Galaxy tool wrapper can be found in `LICENSE <LICENSE>`_.
+
+Terms of use of the SWISS-MODEL server can be found at
+https://swissmodel.expasy.org/docs/terms_of_use.
+
+Modelling results of SWISS-MODEL are licensed under the
+`CC BY-SA 4.0 Creative Commons Attribution-ShareAlike 4.0 International License <https://creativecommons.org/licenses/by-sa/4.0/legalcode>`_.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sm_api_wrapper.py	Thu Dec 11 19:32:14 2025 +0000
@@ -0,0 +1,428 @@
+"""Wrapper for the SWISS-MODEL API."""
+
+import argparse
+import json
+import os
+import sys
+import time
+from urllib.parse import urlsplit
+
+import requests
+
+
+class _SmApiWhisperer:
+    """Parent class for talking to the SWISS-MODEL API."""
+
+    PROJECT_TYPE = ""
+
+    def __init__(self, targets, token, project_title="Untitled Project"):
+        self.project_id = None
+        self.project_title = project_title
+        self.targets = targets
+        self.token = token
+
+    def get_json_payload(self):
+        """Needs to be implemented per project type."""
+        raise NotImplementedError
+
+    def submit_request(self):
+        """Send off a request to the SM API."""
+        json_payload = self.get_json_payload()
+        json_payload["project_title"] = self.project_title
+        try:
+            response = requests.post(
+                f"https://swissmodel.expasy.org/{self.PROJECT_TYPE}",
+                headers={"Authorization": f"Token {self.token}"},
+                json=json_payload,
+                timeout=60,
+            )
+        except requests.exceptions.ConnectTimeout:
+            print(
+                "SWISS-MODEL seems to temporarily unavailable",
+                file=sys.stderr,
+            )
+            sys.exit(3)
+        if response.ok is not True:
+            raise RuntimeError(
+                f"Submitting modelling job failed ({response.status_code})"
+            )
+        self.project_id = response.json()["project_id"]
+
+        return response.status_code
+
+    def wait(self):
+        """Poll the API for job to be finished."""
+        response = None
+        # Wait at the end, there is a chance that this project is already
+        # available from cache.
+        while True:
+            # Update the status from the server
+            # response = requests.get(
+            #     f"https://swissmodel.expasy.org/project/{self.project_id}/"
+            #     + "models/summary/",
+            #     headers={"Authorization": f"Token {self.token}"},
+            #     timeout=360,
+            # )
+            response = requests.get(
+                f"https://swissmodel.expasy.org/project/{self.project_id}/"
+                + "models/full-details/",
+                headers={"Authorization": f"Token {self.token}"},
+                timeout=360,
+            )
+            # Update the status
+            status = response.json()["status"]
+            if status.upper() in ["COMPLETED", "FAILED"]:
+                break
+            # Wait for some time before the next request
+            time.sleep(17)
+
+        return response.json()
+
+    def fetch_results(
+        self, response_object, output_dir, fetch_modelcif=True, fetch_pdb=True
+    ):
+        """Get results of the modelling job."""
+
+        def _store_model_json(model_json, outdir):
+            fname = f"model_{model_json['model_id']}.json"
+            with open(
+                os.path.join(outdir, "JSON", fname), "w", encoding="utf8"
+            ) as jfh:
+                json.dump(model_json, jfh)
+
+        def _fetch_file(url, file_type, outdir):
+            response = requests.get(url, timeout=360)
+            if response.ok is not True:
+                raise RuntimeError(
+                    f"Fetching {file_type} output failed ("
+                    + f"{response.status_code})."
+                )
+            try:
+                os.mkdir(os.path.join(outdir, file_type))
+            except FileExistsError:
+                pass
+            fname = f"model_{os.path.basename(urlsplit(url).path)}"
+            with open(os.path.join(outdir, file_type, fname), "wb") as mfh:
+                for chunk in response.iter_content(chunk_size=8192):
+                    mfh.write(chunk)
+
+        # make sure a JSON directory exists
+        os.mkdir(os.path.join(output_dir, "JSON"))
+        if response_object["status"] == "COMPLETED":
+            for model in response_object["models"]:
+                _store_model_json(model, output_dir)
+                if fetch_modelcif:
+                    _fetch_file(model["modelcif_url"], "ModelCIF", output_dir)
+                if fetch_pdb:
+                    _fetch_file(model["coordinates_url"], "PDB", output_dir)
+
+
+class _AutoModelWhisperer(_SmApiWhisperer):
+    """SM automodel project."""
+
+    PROJECT_TYPE = "automodel"
+
+    def get_json_payload(self):
+        """Payload for automodel mode."""
+        return {"target_sequences": self.targets}
+
+
+class _AlignmentWhisperer(_SmApiWhisperer):
+    """SM alignemt project."""
+
+    PROJECT_TYPE = "alignment"
+
+    def __init__(
+        self,
+        targets,
+        token,
+        template_sequence,
+        template_seqres_offset,
+        pdb_id,
+        auth_asym_id,
+        assembly_id,
+        project_title="Untitled Project",
+    ):
+        # Not sure how to reduce the number of arguments as they are required
+        # by the API, so make an exception in Pylint.
+        # pylint: disable=too-many-arguments,too-many-positional-arguments
+        """Initialise alignment mode, add mode-specific info to the method."""
+        super().__init__(targets, token, project_title=project_title)
+        self.assembly_id = assembly_id
+        self.auth_asym_id = auth_asym_id
+        self.pdb_id = pdb_id.lower()
+        self.template_seqres_offset = template_seqres_offset
+        self.template_sequence = template_sequence
+
+    def get_json_payload(self):
+        """Payload for alignment mode."""
+
+        return {
+            "assembly_id": self.assembly_id,
+            "auth_asym_id": self.auth_asym_id,
+            "pdb_id": self.pdb_id,
+            "target_sequences": self.targets,
+            "template_seqres_offset": self.template_seqres_offset,
+            "template_sequence": self.template_sequence,
+        }
+
+
+class _UserTemplateWhisperer(_SmApiWhisperer):
+    """SM user-template project."""
+
+    PROJECT_TYPE = "user_template"
+
+    def __init__(
+        self,
+        targets,
+        token,
+        template_file,
+        project_title="Untitled Project",
+    ):
+        """Initialise user template mode."""
+        super().__init__(targets, token, project_title=project_title)
+        self.template_file = template_file
+
+    def get_json_payload(self):
+        """Payload for user upload mode."""
+        with open(self.template_file, encoding="utf8") as tfh:
+            template_coordinates = tfh.read()
+
+        return {
+            "project_title": self.project_title,
+            "target_sequences": self.targets,
+            "template_coordinates": template_coordinates,
+        }
+
+
+def _defastarise_targets(sequences):
+    """In case some of the targets carry FastA headers, remove them."""
+    targets = []
+    for seq in sequences:
+        seq = seq.split(" ")
+        if len(seq) > 1:
+            if seq[0].strip().startswith((">", "__gt__")):
+                targets.append("".join(seq[1:]))
+            else:
+                targets.append("".join(seq))
+        else:
+            targets.extend(seq)
+
+    return targets
+
+
+def _parse_args():
+    """Get command line arguments."""
+    parser = argparse.ArgumentParser(description=__doc__)
+
+    parser.add_argument(
+        "-d",
+        "--project-title",
+        help="Title for the modelling project",
+        metavar="<TITLE>",
+    )
+    parser.add_argument(
+        "-m",
+        "--no-modelcif",
+        help="Do not download models in ModelCIF format.",
+        default=False,
+        action="store_true",
+    )
+    parser.add_argument(
+        "-l",
+        "--fetch-pdb",
+        help="Download models in PDB legacy format.",
+        default=False,
+        action="store_true",
+    )
+    parser.add_argument(
+        "-t",
+        "--template-sequence",
+        help="The template sequence used for alignment mode",
+        metavar="<SEQUENCE>",
+    )
+    # ToDo: do we need the offset from the user? Doesn't interactive alignment
+    #       mode compute it?
+    parser.add_argument(
+        "-o",
+        "--template-seqres-offset",
+        help="Offset of the template sequence segment compared to the full "
+        + "template sequence",
+        metavar="<NUMBER>",
+        type=int,
+    )
+    parser.add_argument(
+        "-p",
+        "--pdb-id",
+        help="PDB ID (SMTL ID) for the template used in alignment mode",
+        metavar="<PDB ID>",
+    )
+    parser.add_argument(
+        "-c",
+        "--auth-asym-id",
+        help="The chain name to be used in alignment mode",
+        metavar="<CHAIN NAME>",
+    )
+    parser.add_argument(
+        "-a",
+        "--assembly-id",
+        help="ID of the assembly of the SMTL template to be used in alignment "
+        + "mode",
+        metavar="<NUMBER>",
+        type=int,
+    )
+    parser.add_argument(
+        "-f",
+        "--template-file",
+        help="PDB formatted file to serve as template for modelling",
+        metavar="<PDB FILE>",
+    )
+    parser.add_argument(
+        "project_type",
+        choices=("alignment", "automodel", "usertemplate"),
+        help="Kind of project ('alignmet', 'automodel', 'usertemplate')",
+        metavar="<PROJECT TYPE>",
+    )
+    metas = {
+        "outdir": "<OUTPUT DIRECTORY>",
+        "target_sequences": "<SEQUENCE[S]>",
+        "token": "<TOKEN>",
+    }
+    parser.add_argument(
+        "token",
+        help="Authentication token for SWISS-MODEL",
+        metavar=metas["token"],
+    )
+    parser.add_argument(
+        "outdir",
+        help="Directory to store results in",
+        metavar=metas["outdir"],
+    )
+    parser.add_argument(
+        "target_sequences",
+        help="Target sequence to be modelled; to add multiple sequences, "
+        + "delimit with a space",
+        metavar=metas["target_sequences"],
+        nargs=argparse.REMAINDER,
+    )
+
+    opts = parser.parse_args()
+
+    # Make sure arguments for the different modelling modes are there
+    req_opts = {
+        "alignment": [
+            "assembly_id",
+            "auth_asym_id",
+            "pdb_id",
+            "template_seqres_offset",
+            "template_sequence",
+        ],
+        "automodel": [],
+        "usertemplate": ["template_file"],
+    }
+    # check mandatory arguments
+    for req in req_opts[opts.project_type]:
+        value = getattr(opts, req)
+        if value is None:
+            print(
+                f"Option '--{req.replace('_', '-')}' missing for "
+                + f"'{opts.project_type}' mode",
+                file=sys.stderr,
+            )
+            sys.exit(2)
+        if isinstance(value, str) and len(value) == 0:
+            print(
+                f"Option '--{req.replace('_', '-')}' can not be an empty "
+                + "string",
+                file=sys.stderr,
+            )
+            sys.exit(2)
+    # check positional arguments
+    for req, mta in metas.items():
+        value = getattr(opts, req)
+        if isinstance(value, str):
+            if len(value) == 0:
+                print(
+                    f"Argument of '{mta}' can not be an empty string",
+                    file=sys.stderr,
+                )
+                sys.exit(2)
+        elif isinstance(value, list):
+            if len(value) == 0 or not all(value):
+                print(
+                    f"Argument of '{mta}' can not be an empty",
+                    file=sys.stderr,
+                )
+                sys.exit(2)
+        else:
+            raise RuntimeError(
+                f"Value with unknown type '{type(value).__name__}' found for "
+                + f"'{mta}'"
+            )
+    # check optional & positional arguments
+    for opt in ["project_title"]:
+        value = getattr(opts, opt)
+        if value is not None and len(value) == 0:
+            print(
+                f"Option '--{opt.replace('_', '-')}' can not have an empty "
+                + "string as value",
+                file=sys.stderr,
+            )
+            sys.exit(2)
+
+    return opts
+
+
+def _main():
+    """Run as script."""
+    opts = _parse_args()
+
+    target_sequences = _defastarise_targets(opts.target_sequences)
+    # determine class
+    whsprr = None
+    if opts.project_type.lower() == "automodel":
+        whsprr = _AutoModelWhisperer(
+            target_sequences, opts.token, project_title=opts.project_title
+        )
+    elif opts.project_type.lower() == "alignment":
+        template_sequence = _defastarise_targets([opts.template_sequence])
+        assert len(template_sequence) == 1
+        template_sequence = template_sequence[0]
+        whsprr = _AlignmentWhisperer(
+            target_sequences,
+            opts.token,
+            template_sequence,
+            opts.template_seqres_offset,
+            opts.pdb_id,
+            opts.auth_asym_id,
+            opts.assembly_id,
+            project_title=opts.project_title,
+        )
+    elif opts.project_type.lower() == "usertemplate":
+        whsprr = _UserTemplateWhisperer(
+            target_sequences,
+            opts.token,
+            opts.template_file,
+            project_title=opts.project_title,
+        )
+    else:
+        raise RuntimeError(
+            f"Not a suitable project type: '{opts.project_type}'"
+        )
+    # run the modelling job and wait for it to finish
+    whsprr.submit_request()
+    response = whsprr.wait()
+    whsprr.fetch_results(
+        response,
+        opts.outdir,
+        fetch_modelcif=not opts.no_modelcif,
+        fetch_pdb=opts.fetch_pdb,
+    )
+
+    sys.exit(0)
+
+
+if __name__ == "__main__":
+    _main()
+
+#  LocalWords:  Pylint
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/swissmodel_api.xml	Thu Dec 11 19:32:14 2025 +0000
@@ -0,0 +1,390 @@
+<tool name="SWISS-MODEL Modelling API" id="swissmodel_modelling_api" profile="21.05" version="2.0.0">
+    <description>
+        Fully automated protein structure homology-modelling
+    </description>
+    <xrefs>
+        <xref type="bio.tools">swiss_model</xref>
+    </xrefs>
+    <requirements>
+        <requirement type="package" version="3.11">python</requirement>
+        <requirement type="package" version="2.32.5">requests</requirement>
+    </requirements>
+    <command detect_errors="aggressive"><![CDATA[
+mkdir -p 'output_dir/'
+&& python3 '$__tool_directory__/sm_api_wrapper.py'
+                          $output_format.modelcif
+                          $output_format.pdb
+                          #if $project_title
+                            --project-title '${project_title}'
+                          #end if
+                          #if $project_type == 'alignment'
+                            --assembly-id '${assembly_id}'
+                            --auth-asym-id '${auth_asym_id}'
+                            --pdb-id '${pdb_id}'
+                            --template-seqres-offset '${template_seqres_offset}'
+                            --template-sequence '${template_sequence}'
+                          #elif $project_type == 'usertemplate'
+                            --template-file '${template_file}'
+                          #end if
+                          '${project.project_type}'
+                          '${token}' output_dir
+                          #if $project_type == 'alignment'
+                            '${target_sequence}'
+                          #else
+                            #for $i in $target_list
+                              '${i.sequence}'
+                            #end for
+                          #end if
+&& find output_dir -type f -name '*.gz' -exec gzip -d {} +
+]]>
+    </command>
+    <inputs>
+        <param name="token" type="text" optional="false" label="API token">
+            <help><![CDATA[
+Token to authenticate to the SWISS-MODEL API. Can be managed on your
+<a href="https://swissmodel.expasy.org/login_page" target="_blank"> SWISS-MODEL account</a> page.
+            ]]></help>
+        </param>
+        <param name="project_title" type="text" value="Untitled project" optional="true" label="Project title"/>
+        <conditional name="project">
+            <param name="project_type" type="select" value="automodel" optional="false" label="Modelling setup" display="radio">
+                <option value="automodel" selected="true">Automodel mode</option>
+                <option value="alignment">Alignment mode</option>
+                <option value="usertemplate">User template mode</option>
+            </param>
+            <!-- AutoModel Mode Options -->
+            <when value="automodel">
+                <repeat name="target_list" min="1"
+                        help="Sequence(s) to be modelled. Add multiple sequences for hetero modelling." title="Target sequence">
+                    <param name="sequence" type="text" optional="false"
+                           label="Sequence"/>
+                </repeat>
+            </when>
+            <!-- Alignment Mode Options -->
+            <when value="alignment">
+                <param name="target_sequence" type="text" optional="false"
+                       label="Target sequence"
+                       help="Target sequence as part of the alignment"/>
+                <param name="pdb_id" type="text" optional="false"
+                       label="PDB/ SMTL ID">
+                    <help><![CDATA[
+PDB ID of the template SMTL entry as found in the
+<a href="https://swissmodel.expasy.org/templates/" target="_blank"> SWISS-MODEL
+Template Library</a>.
+                    ]]></help>
+                    <validator type="regex" message="Only PDB-like IDs allowed, e.g. '1crn'.">^\d[A-Za-z0-9]{3}$</validator>
+                </param>
+                <param name="assembly_id" type="integer" min="0" value="0" optional="false"
+                       label="Assembly ID">
+                    <help><![CDATA[
+Assembly ID of the template as found in the
+<a href="https://swissmodel.expasy.org/templates/" target="_blank"> SWISS-MODEL
+Template Library</a>.
+                    ]]></help>
+                </param>
+                <param name="auth_asym_id" type="text" optional="false"
+                       label="Chain name">
+                    <help><![CDATA[
+Chain name in the template assembly as found in the
+<a href="https://swissmodel.expasy.org/templates/" target="_blank"> SWISS-MODEL
+Template Library</a>.
+                    ]]></help>
+                </param>
+                <param name="template_sequence" type="text" optional="false"
+                       label="Template sequence"
+                       help="Template sequence as part of the alignment"/>
+                <param name="template_seqres_offset" type="integer" value="0" optional="false" label="Template sequence offset">
+                    <help><![CDATA[
+Offset of the template sequence segment with respect to the full template
+sequence.
+                    ]]></help>
+                </param>
+            </when>
+            <!-- User Template Mode Options -->
+            <when value="usertemplate">
+                <repeat name="target_list" min="1"
+                        help="Sequence(s) to be modelled. Add multiple sequences for hetero modelling." title="Target sequence">
+                    <param name="sequence" type="text" optional="false"
+                           label="Sequence"/>
+                </repeat>
+                <param name="template_file" type="data" format="txt" optional="false" label="Template coordinates file"
+                       help="File with template coordinaqtes in PDB legacy format."/>
+            </when>
+        </conditional>
+        <section name="output_format" title="Output format options">
+            <param name="modelcif" type="boolean" truevalue="" falsevalue="--no-modelcif" checked="true" label="ModelCIF format"/>
+            <param name="pdb" type="boolean" truevalue="--fetch-pdb" falsevalue="" checked="false" label="PDB legacy format"/>
+        </section>
+    </inputs>
+    <outputs>
+        <collection name="json_collection" type="list"
+                    label="Metadata files for $project_title">
+            <discover_datasets directory="output_dir/JSON" format="json" pattern="(?P&lt;name&gt;.+)\.json$"/>
+        </collection>
+        <collection name="pdb_collection" type="list"
+                    label="PDB files for $project_title">
+            <discover_datasets directory="output_dir/PDB" format="pdb" pattern="(?P&lt;name&gt;.+)\.pdb$"/>
+            <filter>output_format['pdb'] == True</filter>
+        </collection>
+        <collection name="modelcif_collection" type="list"
+                    label="ModelCIF files for $project_title">
+            <discover_datasets directory="output_dir/ModelCIF" format="cif" pattern="(?P&lt;name&gt;.+)\.cif$"/>
+            <filter>output_format['modelcif'] == True</filter>
+        </collection>
+    </outputs>
+    <tests>
+        <!-- w/o params, all will be empty strings, fail -->
+        <test expect_exit_code="2" expect_failure="true">
+            <assert_stderr>
+                <has_line line="Argument of '&lt;SEQUENCE[S]&gt;' can not be an empty"/>
+            </assert_stderr>
+        </test>
+        <!-- with sequences, everything else empty, fail -->
+        <test expect_exit_code="2" expect_failure="true">
+            <conditional name="project">
+                <repeat name="target_list">
+                    <param name="sequence" value="MVVKAVCVINGDAKGTVFFEQESSGTPVKVSGEVCGL"/>
+                </repeat>
+                <repeat name="target_list">
+                    <param name="sequence" value="AKGLHGFHVHEFGDNTNGCMSSGPHFNPYGKE"/>
+                </repeat>
+            </conditional>
+            <assert_stderr>
+                <has_line line="Argument of '&lt;TOKEN&gt;' can not be an empty string"/>
+            </assert_stderr>
+        </test>
+        <!-- sequence and token (complete automodel job), fail (no real token) -->
+        <test expect_exit_code="1" expect_failure="true">
+            <conditional name="project">
+                <repeat name="target_list">
+                    <param name="sequence" value="MVVKAVCVINGDAKGTVFFEQESSGTPVKVSGEVCGL"/>
+                </repeat>
+            </conditional>
+            <param name="token" value="NOTAVALIDTOKEN"/>
+            <assert_stderr>
+                <has_line line="RuntimeError: Submitting modelling job failed (401)"/>
+            </assert_stderr>
+        </test>
+        <!-- alignment mode tests, as few params as possible, fail -->
+        <test expect_exit_code="2" expect_failure="true">
+            <conditional name="project">
+                <param name="project_type" value="alignment"/>
+                <param name="assembly_id" value="0"/>
+                <param name="template_seqres_offset" value="0"/>
+            </conditional>
+            <assert_stderr>
+                <has_line line="Option '--auth-asym-id' can not be an empty string"/>
+            </assert_stderr>
+        </test>
+        <!-- alignment mode tests, with auth_asym_id, fail -->
+        <test expect_exit_code="2" expect_failure="true">
+            <conditional name="project">
+                <param name="assembly_id" value="0"/>
+                <param name="auth_asym_id" value="A"/>
+                <param name="project_type" value="alignment"/>
+                <param name="template_seqres_offset" value="0"/>
+            </conditional>
+            <assert_stderr>
+                <has_line line="Option '--pdb-id' can not be an empty string"/>
+            </assert_stderr>
+        </test>
+        <!-- alignment mode tests, with pdb_id, fail -->
+        <test expect_exit_code="2" expect_failure="true">
+            <conditional name="project">
+                <param name="assembly_id" value="0"/>
+                <param name="auth_asym_id" value="A"/>
+                <param name="pdb_id" value="1AKE"/>
+                <param name="project_type" value="alignment"/>
+                <param name="template_seqres_offset" value="0"/>
+            </conditional>
+            <assert_stderr>
+                <has_line line="Option '--template-sequence' can not be an empty string"/>
+            </assert_stderr>
+        </test>
+        <!-- alignment mode tests, with template_sequence, fail -->
+        <test expect_exit_code="2" expect_failure="true">
+            <conditional name="project">
+                <param name="assembly_id" value="0"/>
+                <param name="auth_asym_id" value="A"/>
+                <param name="pdb_id" value="1AKE"/>
+                <param name="project_type" value="alignment"/>
+                <param name="template_seqres_offset" value="0"/>
+                <param name="template_sequence" value="MVVKAVCVINGDAKGTVFFEQESSGTPV"/>
+            </conditional>
+            <assert_stderr>
+                <has_line line="Argument of '&lt;SEQUENCE[S]&gt;' can not be an empty"/>
+            </assert_stderr>
+        </test>
+        <!-- alignment mode tests, with sequence, fail -->
+        <test expect_exit_code="2" expect_failure="true">
+            <conditional name="project">
+                <param name="assembly_id" value="0"/>
+                <param name="auth_asym_id" value="A"/>
+                <param name="pdb_id" value="1AKE"/>
+                <param name="project_type" value="alignment"/>
+                <param name="target_sequence" value="MVVKAVCVINGDAKGTVFFEQESSGTPVKVSG"/>
+                <param name="template_seqres_offset" value="0"/>
+                <param name="template_sequence" value="MVVKAVCVINGDAKGTVFFEQESSGTPV"/>
+            </conditional>
+            <assert_stderr>
+                <has_line line="Argument of '&lt;TOKEN&gt;' can not be an empty string"/>
+            </assert_stderr>
+        </test>
+        <!-- alignment mode tests, with token, fail -->
+        <test expect_exit_code="1" expect_failure="true">
+            <conditional name="project">
+                <param name="assembly_id" value="0"/>
+                <param name="auth_asym_id" value="A"/>
+                <param name="pdb_id" value="1AKE"/>
+                <param name="project_type" value="alignment"/>
+                <param name="target_sequence" value="MVVKAVCVINGDAKGTVFFEQESSGTPVKVSG"/>
+                <param name="template_seqres_offset" value="0"/>
+                <param name="template_sequence" value="MVVKAVCVINGDAKGTVFFEQESSGTPV"/>
+            </conditional>
+            <param name="token" value="NOTAVALIDTOKEN"/>
+            <assert_stderr>
+                <has_line line="RuntimeError: Submitting modelling job failed (401)"/>
+            </assert_stderr>
+        </test>
+        <!-- usertemplate mode tests, as few params as possible, fail -->
+        <test expect_exit_code="2" expect_failure="true">
+            <conditional name="project">
+                <param name="project_type" value="usertemplate"/>
+                <param name="template_file" value="model_01.pdb"/>
+            </conditional>
+            <assert_stderr>
+                <has_line line="Argument of '&lt;SEQUENCE[S]&gt;' can not be an empty"/>
+            </assert_stderr>
+        </test>
+        <!-- usertemplate mode tests, with sequence, fail -->
+        <test expect_exit_code="2" expect_failure="true">
+            <conditional name="project">
+                <param name="project_type" value="usertemplate"/>
+                <param name="template_file" value="model_01.pdb"/>
+                <repeat name="target_list">
+                    <param name="sequence" value="MVVKAVCVINGDAKGTVFFEQESSGTPVKVSGEVCGL"/>
+                </repeat>
+                <repeat name="target_list">
+                    <param name="sequence" value="AKGLHGFHVHEFGDNTNGCMSSGPHFNPYGKE"/>
+                </repeat>
+            </conditional>
+            <assert_stderr>
+                <has_line line="Argument of '&lt;TOKEN&gt;' can not be an empty string"/>
+            </assert_stderr>
+        </test>
+        <!-- usertemplate mode tests, with token, fail -->
+        <test expect_exit_code="1" expect_failure="true">
+            <conditional name="project">
+                <param name="project_type" value="usertemplate"/>
+                <param name="template_file" value="model_01.pdb"/>
+                <repeat name="target_list">
+                    <param name="sequence" value="MVVKAVCVINGDAKGTVFFEQESSGTPVKVSGEVCGL"/>
+                </repeat>
+                <repeat name="target_list">
+                    <param name="sequence" value="AKGLHGFHVHEFGDNTNGCMSSGPHFNPYGKE"/>
+                </repeat>
+            </conditional>
+            <param name="token" value="NOTAVALIDTOKEN"/>
+            <assert_stderr>
+                <has_line line="RuntimeError: Submitting modelling job failed (401)"/>
+            </assert_stderr>
+        </test>
+    </tests>
+    <help><![CDATA[
+The SWISS-MODEL Modelling API tool gives you convenient access to homology models build by `SWISS-MODEL <https://swissmodel.expasy.org>`_ via our `API <https://swissmodel.expasy.org/docs/help#modelling_api>`_.
+
+By default, the tool fetches model coordinates in ModelCIF format from the SWISS-MODEL API. SWISS-MODEL enriches its ModelCIF files with lots of extra information, essential for later reproduction of the computational experiment and enabling for easy deposition at archiving sites, like `Model Archive <https://modelarchive.org>`_. Output format can be toggled in the *Output format options* section for legacy PDB format, in case you are dealing with old-school tools in your workflows. Regardless of the format, output file names follow the pattern ``model_<N>.[cif|pdb]``.
+
+The SWISS-MODEL API provides three major modelling modes: Automodel, alignment and template upload mode.
+
+Automodel mode
+--------------
+
+This mode corresponds to the "Sequence(s)" input mode and just hitting the "Build Model" button on our `web page <https://swissmodel.expasy.org/interactive>`_.
+
+Automodel goes from sequence(s) to structural model. A single sequence for input leads to monomers or homomers. Multiple sequences for input produces heteromers, incorporating all target sequences. Multiple sequences input is sometimes mistaken for multi-model modelling tasks but its not, to produce models for more than one target sequence, you need to run the tool for each sequence separately.
+
+Inside an automodel job, the following happens:
+
+1. Template search in `PDB <https://www.rcsb.org>`_ and AlphaFold DB v4 sequences
+2. Template selection by estimated quality and target sequence coverage
+
+   - gain high model confidence
+   - cover as much of the target sequence, maybe by different models per region
+   - employ models for equal regions if structurally diverse
+
+3. Build models
+
+Automodel options
+~~~~~~~~~~~~~~~~~
+
+Only one or more sequences required, that's all! Single sequence produces monomers and/ or homomers. Multiple sequences produces heteromers.
+
+
+Alignment mode
+--------------
+
+This mode corresponds to the "Target-Template Alignment" input mode on our `web page <https://swissmodel.expasy.org/interactive#alignment>`_.
+
+The alignment mode still builds a model for a target sequence and a template from the `SWISS-MODEL template library <https://swissmodel.expasy.org/templates/>`_ (**SMTL**). But instead of creating the alignment from its own template search run, SWISS-MODEL will use an alignment provided by yourself. The alignment will be mapped onto the template sequence and then a structural model will be build.
+
+To properly map a your alignment within the SMTL, you need to provide some extra information. SWISS-MODEL needs the identifier of the template. That's a common PDB ID, it just needs to exist in the SMTL. Then you need to identify the biological assembly of the entry you want your model to be based on... which can be easily done from the SMTL page. The assembly ID comes with the SMTL ID, its the number after the PDB ID and alternative assemblies of an entry can be found at the bottom of an entry page. The last component is the chain name you used for the alignment. Be aware, the SMTL uses its own chain names, so the original PDB chain name may not work. A mapping of chain names is provided on SMTL entry pages.
+
+Alignment mode options
+~~~~~~~~~~~~~~~~~~~~~~
+
+- Target sequence: first part of the alignment
+- Template sequence: second sequence of the alignment
+- Template sequence offset: position the template sequence starts at with respect to the full sequence; When using only a sub-sequence of the full sequence, it may be hard to automatically map to the right region in case of repeats, so you need to provide the offset
+- PDB/ SMTL ID: PDB ID of the SMTL entry the alignment refers to
+- Assembly ID: ID of the biological assembly to be used as template
+- Chain name: ID of the chain to be used for modelling, needs to be the SMTL name, not the PDB name
+
+
+User template mode
+------------------
+
+This mode corresponds to the "User Template" input mode on our `web page <https://swissmodel.expasy.org/interactive#structure>`_.
+
+The user template mode builds a model for a target sequence and template coordinates provided by you instead of using a SMTL entry for coordinates. The sequence alignment between target and template sequence is still calculated by SWISS-MODEL using HHblits and BLAST, or local alignment algorithm if both methods fail. With the alignment, a structural model is build based on the provided template coordinates.
+
+Inside a user template job, the following happens:
+
+1. Analyse uploaded coordinates
+2. Calculate alignments with HHblits & BLAST
+3. Choose "better" alignment or calculate Smith-Waterman alignment
+4. Build model
+
+User template mode options
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+- Target sequence: sequence to be modelled (post multiple for heteros)
+- Template coordinate file: your template file, must be PDB formatted
+
+Attribution/ License
+~~~~~~~~~~~~~~~~~~~~
+
+Terms of use can be found at https://swissmodel.expasy.org/docs/terms_of_use
+
+Modelling results of SWISS-MODEL are licensed under the
+`CC BY-SA 4.0 Creative Commons Attribution-ShareAlike 4.0 International License <https://creativecommons.org/licenses/by-sa/4.0/legalcode>`_.
+
+]]>
+    </help>
+    <citations>
+        <!-- SWISS-MODEL -->
+        <citation type="doi">10.1093/nar/gky427</citation>
+        <!-- ProMod3 -->
+        <citation type="doi">10.1371/journal.pcbi.1008667</citation>
+        <!-- QMEANDisCo -->
+        <citation type="doi">10.1093/bioinformatics/btz828</citation>
+        <!-- Quaternary Structure Prediction/ QSQE -->
+        <citation type="doi">10.1038/s41598-017-09654-8</citation>
+        <!-- lDDT -->
+        <citation type="doi">10.1093/bioinformatics/btt473</citation>
+    </citations>
+    <creator>
+        <person email="stefan.bienert@unibas.ch" familyName="Bienert" givenName="Stefan"/>
+        <organization name="Swiss Institute of Bioinformatics" alternateName="SIB" url="https://www.sib.swiss"/>
+        <organization name="Biozentrum, University of Basel" alternateName="Biozentrum" url="https://www.biozentrum.unibas.ch"/>
+    </creator>
+</tool>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/model_01.pdb	Thu Dec 11 19:32:14 2025 +0000
@@ -0,0 +1,382 @@
+TITLE     SWISS-MODEL SERVER (https://swissmodel.expasy.org)
+TITLE    2 Untitled Project
+EXPDTA    THEORETICAL MODEL (SWISS-MODEL SERVER)
+AUTHOR    SWISS-MODEL SERVER (SEE REFERENCE IN JRNL Records)
+REVDAT   1   03-DEC-25 1MOD    1       14:47
+JRNL        AUTH   A.WATERHOUSE,M.BERTONI,S.BIENERT,G.STUDER,G.TAURIELLO,
+JRNL        AUTH 2 R.GUMIENNY,F.T.HEER,T.A.P.DE BEER,C.REMPFER,L.BORDOLI,
+JRNL        AUTH 3 R.LEPORE,T.SCHWEDE
+JRNL        TITL   SWISS-MODEL: HOMOLOGY MODELLING OF PROTEIN STRUCTURES AND
+JRNL        TITL 2 COMPLEXES
+JRNL        REF    NUCLEIC.ACIDS.RES..           V.  46 W296  2018
+JRNL        PMID   29788355
+JRNL        DOI    10.1093/nar/gky427
+REMARK   1
+REMARK   1 REFERENCE 1
+REMARK   1  AUTH   S.BIENERT,A.WATERHOUSE,T.A.P.DE BEER,G.TAURIELLO,G.STUDER,
+REMARK   1  AUTH 2 L.BORDOLI,T.SCHWEDE
+REMARK   1  TITL   THE SWISS-MODEL REPOSITORY - NEW FEATURES AND FUNCTIONALITY
+REMARK   1  REF    NUCLEIC.ACIDS.RES..           V.  45       2017
+REMARK   1  REFN                   ISSN 0305-1048
+REMARK   1  PMID   27899672
+REMARK   1  DOI    10.1093/nar/gkw1132
+REMARK   1
+REMARK   1 REFERENCE 2
+REMARK   1  AUTH   N.GUEX,M.C.PEITSCH,T.SCHWEDE
+REMARK   1  TITL   AUTOMATED COMPARATIVE PROTEIN STRUCTURE MODELING WITH
+REMARK   1  TITL 2 SWISS-MODEL AND SWISS-PDBVIEWER: A HISTORICAL PERSPECTIVE
+REMARK   1  REF    ELECTROPHORESIS               V.  30       2009
+REMARK   1  REFN                   ISSN 0173-0835
+REMARK   1  PMID   19517507
+REMARK   1  DOI    10.1002/elps.200900140
+REMARK   1
+REMARK   1 REFERENCE 3
+REMARK   1  AUTH   G.STUDER,G.TAURIELLO,S.BIENERT,M.BIASINI,N.JOHNER,T.SCHWEDE
+REMARK   1  TITL   PROMOD3 - A VERSATILE HOMOLOGY MODELLING TOOLBOX
+REMARK   1  REF    PLOS COMP. BIOL.              V.  17       2021
+REMARK   1  REFN                   ISSN 
+REMARK   1  PMID   33507980
+REMARK   1  DOI    10.1371/journal.pcbi.1008667
+REMARK   1
+REMARK   1 REFERENCE 4
+REMARK   1  AUTH   G.STUDER,C.REMPFER,A.WATERHOUSE,R.GUMIENNY,J.HAAS,T.SCHWEDE
+REMARK   1  TITL   QMEANDISCO - DISTANCE CONSTRAINTS APPLIED ON MODEL QUALITY 
+REMARK   1  TITL 2 ESTIMATION
+REMARK   1  REF    BIOINFORMATICS                V.  36       2020
+REMARK   1  REFN                   ISSN 
+REMARK   1  PMID   31697312
+REMARK   1  DOI    10.1093/bioinformatics/btz828
+REMARK   1
+REMARK   1 REFERENCE 5
+REMARK   1  AUTH   P.BENKERT,M.BIASINI,T.SCHWEDE
+REMARK   1  TITL   TOWARD THE ESTIMATION OF THE ABSOLUTE QUALITY OF INDIVIDUAL
+REMARK   1  TITL 2 PROTEIN STRUCTURE MODELS
+REMARK   1  REF    BIOINFORMATICS                V.  27       2011
+REMARK   1  REFN                   ISSN 1367-4803
+REMARK   1  PMID   21134891
+REMARK   1  DOI    10.1093/bioinformatics/btq662
+REMARK   1
+REMARK   1 REFERENCE 6
+REMARK   1  AUTH   M.BERTONI,F.KIEFER,M.BIASINI,L.BORDOLI,T.SCHWEDE
+REMARK   1  TITL   MODELING PROTEIN QUATERNARY STRUCTURE OF HOMO- AND
+REMARK   1  TITL 2 HETERO-OLIGOMERS BEYOND BINARY INTERACTIONS BY HOMOLOGY
+REMARK   1  REF    SCI.REP.                      V.   7       2017
+REMARK   1  REFN                   ISSN
+REMARK   1  PMID   28874689
+REMARK   1  DOI    10.1038/s41598-017-09654-8
+REMARK   1
+REMARK   1 DISCLAIMER
+REMARK   1 The SWISS-MODEL SERVER produces theoretical models for proteins.
+REMARK   1 The results of any theoretical modelling procedure is
+REMARK   1 NON-EXPERIMENTAL and MUST be considered with care. These models may
+REMARK   1 contain significant errors. This is especially true for automated
+REMARK   1 modeling since there is no human intervention during model
+REMARK   1 building. Please read the header section and the logfile carefully
+REMARK   1 to know what templates and alignments were used during the model
+REMARK   1 building process. All information by the SWISS-MODEL SERVER is
+REMARK   1 provided "AS-IS", without any warranty, expressed or implied.
+REMARK   2
+REMARK   2 COPYRIGHT NOTICE
+REMARK   2 This SWISS-MODEL protein model is copyright. It is produced by the
+REMARK   2 SWISS-MODEL server, developed by the Computational Structural
+REMARK   2 Biology Group at the SIB Swiss Institute of Bioinformatics at the
+REMARK   2 Biozentrum, University of Basel (https://swissmodel.expasy.org). This
+REMARK   2 model is licensed under the CC BY-SA 4.0 Creative Commons
+REMARK   2 Attribution-ShareAlike 4.0 International License
+REMARK   2 (https://creativecommons.org/licenses/by-sa/4.0/legalcode), i.e. you
+REMARK   2 can copy and redistribute the model in any medium or format,
+REMARK   2 transform and build upon the model for any purpose, even
+REMARK   2 commercially, under the following terms:
+REMARK   2 Attribution - You must give appropriate credit, provide a link to
+REMARK   2 the license, and indicate if changes were made. You may do so in any
+REMARK   2 reasonable manner, but not in any way that suggests the licensor
+REMARK   2 endorses you or your use. When you publish, patent or distribute
+REMARK   2 results that were fully or partially based on the model, please cite
+REMARK   2 the corresponding papers mentioned under JRNL.
+REMARK   2 ShareAlike - If you remix, transform, or build upon the material,
+REMARK   2 you must distribute your contributions under the same license as the
+REMARK   2 original.
+REMARK   2 No additional restrictions - you may not apply legal terms or
+REMARK   2 technological measures that legally restrict others from doing
+REMARK   2 anything the license permits.
+REMARK   2 Find a human-readable summary of (and not a substitute for) the
+REMARK   2 CC BY-SA 4.0 license at this link:
+REMARK   2 https://creativecommons.org/licenses/by-sa/4.0/
+REMARK   3 
+REMARK   3 MODEL INFORMATION
+REMARK   3  SMVERSN 2025-11.2
+REMARK   3  ENGIN   PROMOD3
+REMARK   3  VERSN   3.6.0
+REMARK   3  OSTAT   monomer
+REMARK   3  OSRSN   MONOMER
+REMARK   3  QSPRD   0.000
+REMARK   3  GMQE    0.60
+REMARK   3  QMNV    4.5.0
+REMARK   3  QMNDG   0.50
+REMARK   3  MODT    FALSE
+REMARK   3 
+REMARK   3 TEMPLATE 1
+REMARK   3  PDBID   9vel
+REMARK   3  CHAIN   r
+REMARK   3  MMCIF   N
+REMARK   3  PDBV    2025-11-21
+REMARK   3  SMTLE   9vel.1.N
+REMARK   3  SMTLV   2025-12-01
+REMARK   3  MTHD    ELECTRON MICROSCOPY 0.00 A
+REMARK   3  FOUND   HHblits
+REMARK   3  GMQE    0.85
+REMARK   3  SIM     0.63
+REMARK   3  SID     100.00
+REMARK   3  OSTAT   monomer
+REMARK   3  ALN N TRG RGRNPEATGDYDDHIWVTEEVEVRPLVDEV
+REMARK   3  ALN N TPL RGRNPEATGDYDDHIWVTEEVEVRPLVDEV
+REMARK   3  ALN N OFF 92
+ATOM      1  N   ARG N   1     347.942 282.863 -68.043  1.00  0.77           N  
+ATOM      2  CA  ARG N   1     346.686 283.693 -68.216  1.00  0.77           C  
+ATOM      3  C   ARG N   1     346.823 285.004 -67.474  1.00  0.77           C  
+ATOM      4  O   ARG N   1     347.536 285.033 -66.480  1.00  0.77           O  
+ATOM      5  CB  ARG N   1     345.457 282.942 -67.613  1.00  0.77           C  
+ATOM      6  CG  ARG N   1     345.009 281.679 -68.376  1.00  0.77           C  
+ATOM      7  CD  ARG N   1     343.759 281.017 -67.769  1.00  0.77           C  
+ATOM      8  NE  ARG N   1     343.440 279.801 -68.596  1.00  0.77           N  
+ATOM      9  CZ  ARG N   1     342.675 279.792 -69.699  1.00  0.77           C  
+ATOM     10  NH1 ARG N   1     342.176 280.907 -70.220  1.00  0.77           N  
+ATOM     11  NH2 ARG N   1     342.397 278.632 -70.292  1.00  0.77           N  
+ATOM     12  N   GLY N   2     346.176 286.100 -67.912  1.00  0.85           N  
+ATOM     13  CA  GLY N   2     346.233 287.368 -67.207  1.00  0.85           C  
+ATOM     14  C   GLY N   2     344.948 288.090 -67.454  1.00  0.85           C  
+ATOM     15  O   GLY N   2     344.016 287.531 -68.035  1.00  0.85           O  
+ATOM     16  N   ARG N   3     344.877 289.360 -67.038  1.00  0.46           N  
+ATOM     17  CA  ARG N   3     343.712 290.199 -67.189  1.00  0.46           C  
+ATOM     18  C   ARG N   3     344.216 291.557 -67.588  1.00  0.46           C  
+ATOM     19  O   ARG N   3     345.277 291.981 -67.120  1.00  0.46           O  
+ATOM     20  CB  ARG N   3     342.904 290.360 -65.873  1.00  0.46           C  
+ATOM     21  CG  ARG N   3     342.207 289.059 -65.425  1.00  0.46           C  
+ATOM     22  CD  ARG N   3     341.483 289.136 -64.069  1.00  0.46           C  
+ATOM     23  NE  ARG N   3     340.328 290.095 -64.178  1.00  0.46           N  
+ATOM     24  CZ  ARG N   3     339.076 289.778 -64.524  1.00  0.46           C  
+ATOM     25  NH1 ARG N   3     338.744 288.554 -64.881  1.00  0.46           N  
+ATOM     26  NH2 ARG N   3     338.181 290.734 -64.691  1.00  0.46           N  
+ATOM     27  N   ASN N   4     343.491 292.250 -68.475  1.00  0.51           N  
+ATOM     28  CA  ASN N   4     343.856 293.547 -68.987  1.00  0.51           C  
+ATOM     29  C   ASN N   4     343.432 294.634 -67.981  1.00  0.51           C  
+ATOM     30  O   ASN N   4     342.243 294.685 -67.666  1.00  0.51           O  
+ATOM     31  CB  ASN N   4     343.165 293.766 -70.365  1.00  0.51           C  
+ATOM     32  CG  ASN N   4     344.085 294.529 -71.305  1.00  0.51           C  
+ATOM     33  OD1 ASN N   4     345.233 294.801 -70.993  1.00  0.51           O  
+ATOM     34  ND2 ASN N   4     343.575 294.849 -72.519  1.00  0.51           N  
+ATOM     35  N   PRO N   5     344.273 295.502 -67.415  1.00  0.53           N  
+ATOM     36  CA  PRO N   5     343.868 296.476 -66.398  1.00  0.53           C  
+ATOM     37  C   PRO N   5     342.880 297.525 -66.890  1.00  0.53           C  
+ATOM     38  O   PRO N   5     342.084 298.021 -66.095  1.00  0.53           O  
+ATOM     39  CB  PRO N   5     345.185 297.155 -65.974  1.00  0.53           C  
+ATOM     40  CG  PRO N   5     346.281 296.150 -66.339  1.00  0.53           C  
+ATOM     41  CD  PRO N   5     345.721 295.465 -67.587  1.00  0.53           C  
+ATOM     42  N   GLU N   6     342.971 297.909 -68.177  1.00  0.53           N  
+ATOM     43  CA  GLU N   6     342.193 298.943 -68.821  1.00  0.53           C  
+ATOM     44  C   GLU N   6     341.153 298.433 -69.814  1.00  0.53           C  
+ATOM     45  O   GLU N   6     340.723 299.181 -70.691  1.00  0.53           O  
+ATOM     46  CB  GLU N   6     343.155 299.945 -69.529  1.00  0.53           C  
+ATOM     47  CG  GLU N   6     343.966 299.439 -70.767  1.00  0.53           C  
+ATOM     48  CD  GLU N   6     345.280 298.701 -70.496  1.00  0.53           C  
+ATOM     49  OE1 GLU N   6     345.541 298.322 -69.327  1.00  0.53           O  
+ATOM     50  OE2 GLU N   6     346.040 298.533 -71.484  1.00  0.53           O  
+ATOM     51  N   ALA N   7     340.692 297.160 -69.695  1.00  0.55           N  
+ATOM     52  CA  ALA N   7     339.657 296.587 -70.551  1.00  0.55           C  
+ATOM     53  C   ALA N   7     338.387 297.428 -70.616  1.00  0.55           C  
+ATOM     54  O   ALA N   7     337.884 297.922 -69.602  1.00  0.55           O  
+ATOM     55  CB  ALA N   7     339.295 295.154 -70.099  1.00  0.55           C  
+ATOM     56  N   THR N   8     337.841 297.649 -71.827  1.00  0.54           N  
+ATOM     57  CA  THR N   8     336.755 298.606 -72.002  1.00  0.54           C  
+ATOM     58  C   THR N   8     335.416 297.919 -71.890  1.00  0.54           C  
+ATOM     59  O   THR N   8     334.381 298.559 -71.736  1.00  0.54           O  
+ATOM     60  CB  THR N   8     336.790 299.355 -73.336  1.00  0.54           C  
+ATOM     61  OG1 THR N   8     336.652 298.493 -74.456  1.00  0.54           O  
+ATOM     62  CG2 THR N   8     338.143 300.067 -73.481  1.00  0.54           C  
+ATOM     63  N   GLY N   9     335.422 296.573 -71.949  1.00  0.50           N  
+ATOM     64  CA  GLY N   9     334.211 295.793 -71.850  1.00  0.50           C  
+ATOM     65  C   GLY N   9     334.512 294.358 -71.536  1.00  0.50           C  
+ATOM     66  O   GLY N   9     335.506 294.018 -70.906  1.00  0.50           O  
+ATOM     67  N   ASP N  10     333.640 293.454 -72.014  1.00  0.45           N  
+ATOM     68  CA  ASP N  10     333.667 292.063 -71.618  1.00  0.45           C  
+ATOM     69  C   ASP N  10     334.439 291.198 -72.629  1.00  0.45           C  
+ATOM     70  O   ASP N  10     334.543 289.984 -72.496  1.00  0.45           O  
+ATOM     71  CB  ASP N  10     332.200 291.550 -71.524  1.00  0.45           C  
+ATOM     72  CG  ASP N  10     331.360 292.224 -70.442  1.00  0.45           C  
+ATOM     73  OD1 ASP N  10     331.852 293.141 -69.742  1.00  0.45           O  
+ATOM     74  OD2 ASP N  10     330.175 291.817 -70.336  1.00  0.45           O  
+ATOM     75  N   TYR N  11     335.013 291.822 -73.687  1.00  0.44           N  
+ATOM     76  CA  TYR N  11     335.597 291.113 -74.819  1.00  0.44           C  
+ATOM     77  C   TYR N  11     337.122 291.222 -74.841  1.00  0.44           C  
+ATOM     78  O   TYR N  11     337.780 290.494 -75.580  1.00  0.44           O  
+ATOM     79  CB  TYR N  11     335.033 291.646 -76.176  1.00  0.44           C  
+ATOM     80  CG  TYR N  11     333.595 291.223 -76.408  1.00  0.44           C  
+ATOM     81  CD1 TYR N  11     332.527 291.778 -75.678  1.00  0.44           C  
+ATOM     82  CD2 TYR N  11     333.297 290.264 -77.395  1.00  0.44           C  
+ATOM     83  CE1 TYR N  11     331.205 291.371 -75.914  1.00  0.44           C  
+ATOM     84  CE2 TYR N  11     331.973 289.875 -77.651  1.00  0.44           C  
+ATOM     85  CZ  TYR N  11     330.927 290.430 -76.906  1.00  0.44           C  
+ATOM     86  OH  TYR N  11     329.588 290.068 -77.159  1.00  0.44           O  
+ATOM     87  N   ASP N  12     337.727 292.112 -74.022  1.00  0.48           N  
+ATOM     88  CA  ASP N  12     339.149 292.371 -73.996  1.00  0.48           C  
+ATOM     89  C   ASP N  12     339.741 292.237 -72.585  1.00  0.48           C  
+ATOM     90  O   ASP N  12     340.875 292.648 -72.347  1.00  0.48           O  
+ATOM     91  CB  ASP N  12     339.431 293.771 -74.645  1.00  0.48           C  
+ATOM     92  CG  ASP N  12     338.722 294.969 -74.009  1.00  0.48           C  
+ATOM     93  OD1 ASP N  12     337.659 294.802 -73.358  1.00  0.48           O  
+ATOM     94  OD2 ASP N  12     339.247 296.097 -74.187  1.00  0.48           O  
+ATOM     95  N   ASP N  13     339.001 291.627 -71.620  1.00  0.49           N  
+ATOM     96  CA  ASP N  13     339.417 291.508 -70.228  1.00  0.49           C  
+ATOM     97  C   ASP N  13     340.450 290.388 -70.065  1.00  0.49           C  
+ATOM     98  O   ASP N  13     341.564 290.587 -69.602  1.00  0.49           O  
+ATOM     99  CB  ASP N  13     338.159 291.275 -69.321  1.00  0.49           C  
+ATOM    100  CG  ASP N  13     338.382 291.574 -67.846  1.00  0.49           C  
+ATOM    101  OD1 ASP N  13     339.457 292.085 -67.451  1.00  0.49           O  
+ATOM    102  OD2 ASP N  13     337.472 291.215 -67.048  1.00  0.49           O  
+ATOM    103  N   HIS N  14     340.128 289.163 -70.533  1.00  0.47           N  
+ATOM    104  CA  HIS N  14     340.987 288.000 -70.371  1.00  0.47           C  
+ATOM    105  C   HIS N  14     341.961 287.838 -71.518  1.00  0.47           C  
+ATOM    106  O   HIS N  14     341.580 287.753 -72.683  1.00  0.47           O  
+ATOM    107  CB  HIS N  14     340.188 286.682 -70.248  1.00  0.47           C  
+ATOM    108  CG  HIS N  14     339.345 286.625 -69.017  1.00  0.47           C  
+ATOM    109  ND1 HIS N  14     338.139 287.295 -69.012  1.00  0.47           N  
+ATOM    110  CD2 HIS N  14     339.519 285.971 -67.847  1.00  0.47           C  
+ATOM    111  CE1 HIS N  14     337.601 287.036 -67.851  1.00  0.47           C  
+ATOM    112  NE2 HIS N  14     338.392 286.230 -67.088  1.00  0.47           N  
+ATOM    113  N   ILE N  15     343.266 287.759 -71.196  1.00  0.45           N  
+ATOM    114  CA  ILE N  15     344.338 287.740 -72.171  1.00  0.45           C  
+ATOM    115  C   ILE N  15     345.403 286.751 -71.722  1.00  0.45           C  
+ATOM    116  O   ILE N  15     345.323 286.141 -70.647  1.00  0.45           O  
+ATOM    117  CB  ILE N  15     344.987 289.122 -72.378  1.00  0.45           C  
+ATOM    118  CG1 ILE N  15     345.520 289.727 -71.049  1.00  0.45           C  
+ATOM    119  CG2 ILE N  15     343.989 290.067 -73.096  1.00  0.45           C  
+ATOM    120  CD1 ILE N  15     346.472 290.913 -71.267  1.00  0.45           C  
+ATOM    121  N   TRP N  16     346.447 286.550 -72.543  1.00  0.38           N  
+ATOM    122  CA  TRP N  16     347.589 285.724 -72.223  1.00  0.38           C  
+ATOM    123  C   TRP N  16     348.797 286.627 -72.212  1.00  0.38           C  
+ATOM    124  O   TRP N  16     348.919 287.528 -73.039  1.00  0.38           O  
+ATOM    125  CB  TRP N  16     347.791 284.576 -73.243  1.00  0.38           C  
+ATOM    126  CG  TRP N  16     346.609 283.630 -73.292  1.00  0.38           C  
+ATOM    127  CD1 TRP N  16     345.436 283.762 -73.983  1.00  0.38           C  
+ATOM    128  CD2 TRP N  16     346.506 282.377 -72.581  1.00  0.38           C  
+ATOM    129  NE1 TRP N  16     344.608 282.678 -73.762  1.00  0.38           N  
+ATOM    130  CE2 TRP N  16     345.271 281.818 -72.904  1.00  0.38           C  
+ATOM    131  CE3 TRP N  16     347.411 281.723 -71.741  1.00  0.38           C  
+ATOM    132  CZ2 TRP N  16     344.893 280.568 -72.411  1.00  0.38           C  
+ATOM    133  CZ3 TRP N  16     347.037 280.464 -71.242  1.00  0.38           C  
+ATOM    134  CH2 TRP N  16     345.802 279.895 -71.571  1.00  0.38           C  
+ATOM    135  N   VAL N  17     349.702 286.422 -71.243  1.00  0.39           N  
+ATOM    136  CA  VAL N  17     350.885 287.221 -71.040  1.00  0.39           C  
+ATOM    137  C   VAL N  17     352.040 286.258 -70.982  1.00  0.39           C  
+ATOM    138  O   VAL N  17     351.840 285.058 -70.773  1.00  0.39           O  
+ATOM    139  CB  VAL N  17     350.832 288.062 -69.760  1.00  0.39           C  
+ATOM    140  CG1 VAL N  17     349.788 289.182 -69.964  1.00  0.39           C  
+ATOM    141  CG2 VAL N  17     350.525 287.212 -68.499  1.00  0.39           C  
+ATOM    142  N   THR N  18     353.264 286.762 -71.211  1.00  0.39           N  
+ATOM    143  CA  THR N  18     354.489 285.987 -71.297  1.00  0.39           C  
+ATOM    144  C   THR N  18     355.395 286.423 -70.167  1.00  0.39           C  
+ATOM    145  O   THR N  18     355.400 287.603 -69.805  1.00  0.39           O  
+ATOM    146  CB  THR N  18     355.199 286.142 -72.648  1.00  0.39           C  
+ATOM    147  OG1 THR N  18     356.273 285.223 -72.751  1.00  0.39           O  
+ATOM    148  CG2 THR N  18     355.728 287.569 -72.913  1.00  0.39           C  
+ATOM    149  N   GLU N  19     356.150 285.487 -69.563  1.00  0.44           N  
+ATOM    150  CA  GLU N  19     357.011 285.741 -68.430  1.00  0.44           C  
+ATOM    151  C   GLU N  19     358.244 284.882 -68.578  1.00  0.44           C  
+ATOM    152  O   GLU N  19     358.190 283.798 -69.161  1.00  0.44           O  
+ATOM    153  CB  GLU N  19     356.384 285.338 -67.064  1.00  0.44           C  
+ATOM    154  CG  GLU N  19     354.940 285.845 -66.836  1.00  0.44           C  
+ATOM    155  CD  GLU N  19     354.356 285.465 -65.476  1.00  0.44           C  
+ATOM    156  OE1 GLU N  19     355.008 284.703 -64.719  1.00  0.44           O  
+ATOM    157  OE2 GLU N  19     353.216 285.930 -65.207  1.00  0.44           O  
+ATOM    158  N   GLU N  20     359.373 285.336 -68.008  1.00  0.43           N  
+ATOM    159  CA  GLU N  20     360.658 284.685 -68.114  1.00  0.43           C  
+ATOM    160  C   GLU N  20     360.979 284.091 -66.758  1.00  0.43           C  
+ATOM    161  O   GLU N  20     360.750 284.714 -65.718  1.00  0.43           O  
+ATOM    162  CB  GLU N  20     361.790 285.684 -68.482  1.00  0.43           C  
+ATOM    163  CG  GLU N  20     361.513 286.545 -69.742  1.00  0.43           C  
+ATOM    164  CD  GLU N  20     361.573 285.730 -71.028  1.00  0.43           C  
+ATOM    165  OE1 GLU N  20     362.709 285.380 -71.438  1.00  0.43           O  
+ATOM    166  OE2 GLU N  20     360.494 285.481 -71.622  1.00  0.43           O  
+ATOM    167  N   VAL N  21     361.514 282.862 -66.721  1.00  0.44           N  
+ATOM    168  CA  VAL N  21     361.780 282.144 -65.491  1.00  0.44           C  
+ATOM    169  C   VAL N  21     363.192 281.620 -65.539  1.00  0.44           C  
+ATOM    170  O   VAL N  21     363.803 281.502 -66.601  1.00  0.44           O  
+ATOM    171  CB  VAL N  21     360.813 280.979 -65.242  1.00  0.44           C  
+ATOM    172  CG1 VAL N  21     359.425 281.552 -64.881  1.00  0.44           C  
+ATOM    173  CG2 VAL N  21     360.742 280.031 -66.466  1.00  0.44           C  
+ATOM    174  N   GLU N  22     363.774 281.324 -64.363  1.00  0.42           N  
+ATOM    175  CA  GLU N  22     365.105 280.766 -64.244  1.00  0.42           C  
+ATOM    176  C   GLU N  22     365.196 279.331 -64.773  1.00  0.42           C  
+ATOM    177  O   GLU N  22     364.281 278.522 -64.603  1.00  0.42           O  
+ATOM    178  CB  GLU N  22     365.553 280.821 -62.765  1.00  0.42           C  
+ATOM    179  CG  GLU N  22     367.068 280.606 -62.518  1.00  0.42           C  
+ATOM    180  CD  GLU N  22     367.379 280.048 -61.130  1.00  0.42           C  
+ATOM    181  OE1 GLU N  22     366.427 279.727 -60.370  1.00  0.42           O  
+ATOM    182  OE2 GLU N  22     368.584 279.872 -60.836  1.00  0.42           O  
+ATOM    183  N   VAL N  23     366.319 278.977 -65.424  1.00  0.46           N  
+ATOM    184  CA  VAL N  23     366.585 277.649 -65.947  1.00  0.46           C  
+ATOM    185  C   VAL N  23     367.448 276.913 -64.948  1.00  0.46           C  
+ATOM    186  O   VAL N  23     368.474 277.417 -64.503  1.00  0.46           O  
+ATOM    187  CB  VAL N  23     367.278 277.717 -67.309  1.00  0.46           C  
+ATOM    188  CG1 VAL N  23     367.886 276.363 -67.752  1.00  0.46           C  
+ATOM    189  CG2 VAL N  23     366.221 278.194 -68.325  1.00  0.46           C  
+ATOM    190  N   ARG N  24     367.046 275.684 -64.569  1.00  0.30           N  
+ATOM    191  CA  ARG N  24     367.776 274.864 -63.627  1.00  0.30           C  
+ATOM    192  C   ARG N  24     368.215 273.583 -64.322  1.00  0.30           C  
+ATOM    193  O   ARG N  24     367.541 273.175 -65.271  1.00  0.30           O  
+ATOM    194  CB  ARG N  24     366.907 274.522 -62.395  1.00  0.30           C  
+ATOM    195  CG  ARG N  24     366.621 275.769 -61.536  1.00  0.30           C  
+ATOM    196  CD  ARG N  24     366.063 275.436 -60.149  1.00  0.30           C  
+ATOM    197  NE  ARG N  24     364.598 275.759 -60.147  1.00  0.30           N  
+ATOM    198  CZ  ARG N  24     364.099 276.933 -59.739  1.00  0.30           C  
+ATOM    199  NH1 ARG N  24     364.868 277.919 -59.327  1.00  0.30           N  
+ATOM    200  NH2 ARG N  24     362.785 277.141 -59.791  1.00  0.30           N  
+ATOM    201  N   PRO N  25     369.321 272.924 -63.955  1.00  0.53           N  
+ATOM    202  CA  PRO N  25     369.676 271.601 -64.466  1.00  0.53           C  
+ATOM    203  C   PRO N  25     368.613 270.528 -64.254  1.00  0.53           C  
+ATOM    204  O   PRO N  25     368.025 270.474 -63.174  1.00  0.53           O  
+ATOM    205  CB  PRO N  25     370.980 271.224 -63.728  1.00  0.53           C  
+ATOM    206  CG  PRO N  25     371.503 272.543 -63.148  1.00  0.53           C  
+ATOM    207  CD  PRO N  25     370.225 273.337 -62.881  1.00  0.53           C  
+ATOM    208  N   LEU N  26     368.358 269.654 -65.251  1.00  0.52           N  
+ATOM    209  CA  LEU N  26     367.525 268.469 -65.109  1.00  0.52           C  
+ATOM    210  C   LEU N  26     368.129 267.397 -64.206  1.00  0.52           C  
+ATOM    211  O   LEU N  26     367.440 266.776 -63.411  1.00  0.52           O  
+ATOM    212  CB  LEU N  26     367.241 267.855 -66.504  1.00  0.52           C  
+ATOM    213  CG  LEU N  26     366.189 268.640 -67.314  1.00  0.52           C  
+ATOM    214  CD1 LEU N  26     366.233 268.223 -68.793  1.00  0.52           C  
+ATOM    215  CD2 LEU N  26     364.775 268.424 -66.738  1.00  0.52           C  
+ATOM    216  N   VAL N  27     369.451 267.160 -64.334  1.00  0.58           N  
+ATOM    217  CA  VAL N  27     370.172 266.141 -63.592  1.00  0.58           C  
+ATOM    218  C   VAL N  27     371.467 266.789 -63.162  1.00  0.58           C  
+ATOM    219  O   VAL N  27     372.085 267.489 -63.964  1.00  0.58           O  
+ATOM    220  CB  VAL N  27     370.488 264.906 -64.453  1.00  0.58           C  
+ATOM    221  CG1 VAL N  27     371.424 263.911 -63.724  1.00  0.58           C  
+ATOM    222  CG2 VAL N  27     369.163 264.201 -64.811  1.00  0.58           C  
+ATOM    223  N   ASP N  28     371.888 266.585 -61.896  1.00  0.39           N  
+ATOM    224  CA  ASP N  28     373.170 267.026 -61.398  1.00  0.39           C  
+ATOM    225  C   ASP N  28     373.599 266.019 -60.323  1.00  0.39           C  
+ATOM    226  O   ASP N  28     372.984 265.957 -59.262  1.00  0.39           O  
+ATOM    227  CB  ASP N  28     373.037 268.472 -60.835  1.00  0.39           C  
+ATOM    228  CG  ASP N  28     374.385 269.159 -60.717  1.00  0.39           C  
+ATOM    229  OD1 ASP N  28     375.418 268.496 -60.981  1.00  0.39           O  
+ATOM    230  OD2 ASP N  28     374.383 270.374 -60.388  1.00  0.39           O  
+ATOM    231  N   GLU N  29     374.627 265.180 -60.590  1.00  0.60           N  
+ATOM    232  CA  GLU N  29     375.059 264.105 -59.708  1.00  0.60           C  
+ATOM    233  C   GLU N  29     376.577 264.061 -59.759  1.00  0.60           C  
+ATOM    234  O   GLU N  29     377.188 264.450 -60.759  1.00  0.60           O  
+ATOM    235  CB  GLU N  29     374.515 262.702 -60.120  1.00  0.60           C  
+ATOM    236  CG  GLU N  29     372.971 262.538 -60.018  1.00  0.60           C  
+ATOM    237  CD  GLU N  29     372.417 262.498 -58.591  1.00  0.60           C  
+ATOM    238  OE1 GLU N  29     373.169 262.152 -57.646  1.00  0.60           O  
+ATOM    239  OE2 GLU N  29     371.191 262.758 -58.462  1.00  0.60           O  
+ATOM    240  N   VAL N  30     377.215 263.603 -58.669  1.00  0.58           N  
+ATOM    241  CA  VAL N  30     378.653 263.539 -58.481  1.00  0.58           C  
+ATOM    242  C   VAL N  30     378.983 262.061 -58.132  1.00  0.58           C  
+ATOM    243  O   VAL N  30     378.044 261.296 -57.778  1.00  0.58           O  
+ATOM    244  CB  VAL N  30     379.117 264.538 -57.396  1.00  0.58           C  
+ATOM    245  CG1 VAL N  30     380.652 264.558 -57.224  1.00  0.58           C  
+ATOM    246  CG2 VAL N  30     378.659 265.964 -57.780  1.00  0.58           C  
+ATOM    247  OXT VAL N  30     380.169 261.655 -58.258  1.00  0.58           O  
+TER     248      VAL N  30                                                      
+END