Try on DesignSafe

System Specifications#

How to Retrieve Queue Information for a Tapis System

by Silvia Mazzoni, DesignSafe, 2025

You can use Tapis to retrieve detailed specifications for any registered system — for example, Stampede3 — using:

t.systems.getSystem(systemId=system_id)

This returns a complete system configuration, including available job queues and their limits.

The utility function:#

Since queue details are especially useful when selecting where to run your job (e.g., based on max runtime, cores per node, or job limits), we’ll use a utility function to extract and format this information.

  • Retrieves the system’s metadata

  • Parses the nested dictionary

  • Converts the queue information into a clean, readable Pandas DataFrame

You can then compare queues and choose the one that best matches your job requirements.

Visit TAPIS documentation on Systems

User Input#

system_id = 'stampede3'
Using local utilities library

Connect to Tapis#

t=OpsUtils.connect_tapis()
 -- Checking Tapis token --
 Token loaded from file. Token is still valid!
 Token expires at: 2025-08-22T03:13:01+00:00
 Token expires in: 3:52:24.962947
-- LOG IN SUCCESSFUL! --

Get System Info#

system_def = t.systems.getSystem(systemId=system_id)
# this command returns a nested TapisResults object

Pretty-print system_def#

display_tapis_results.py
# ../OpsUtils/OpsUtils/Tapis/display_tapis_results.py
def display_tapis_results(thisAppSchema):
    """
    Pretty-print a Tapis App schema (or any nested TapisResult/dict/list) in a
    JSON-like format with readable grouping and indentation.

    Behavior
    --------
    - Accepts a TapisResult, dict, or list.
    - Groups and prints keys in the order: scalars → lists → nested dicts.
    - Handles TapisResult values by flattening their internal __dict__.
    - Prints arrays either inline (simple scalars) or expanded (objects).
    - Shows a header with app id and version (if present).

    Parameters
    ----------
    thisAppSchema : tapipy.tapis.TapisResult | dict | list
        The app schema or object to render.

    Returns
    -------
    None
        This function prints to stdout. It does not return a value.

    Example
    -------
    # Given a Tapis app schema returned by tapipy:
    display_tapis_results(app_schema)

    Author
    ------
    Silvia Mazzoni, DesignSafe (silviamazzoni@yahoo.com)

    Date
    ----
    2025-08-14

    Version
    -------
    1.0
    """
    try:
        from tapipy.tapis import TapisResult
    except Exception:
        class TapisResult:  # sentinel so isinstance checks won’t crash if tapipy not available
            pass

    def _is_simple_scalar(x):
        return isinstance(x, (type(None), bool, int, float, str))

    def _quote_if_str(x):
        return f'"{x}"' if isinstance(x, str) else x

    def print_nested(key_prefix, obj, indent=1):
        sp = '  ' * indent

        # Unwrap TapisResult to dict
        if isinstance(obj, TapisResult):
            obj = obj.__dict__

        # Dict handling
        if isinstance(obj, dict):
            dict_keys, list_keys, scalar_keys = [], [], []
            for k, v in obj.items():
                v_unwrap = v.__dict__ if isinstance(v, TapisResult) else v
                if isinstance(v_unwrap, dict):
                    dict_keys.append(k)
                elif isinstance(v_unwrap, list):
                    list_keys.append(k)
                else:
                    scalar_keys.append(k)

            # print in order: scalars, lists, dicts
            ordered = [*scalar_keys, *list_keys, *dict_keys]
            for i, k in enumerate(ordered):
                v = obj[k]
                v_unwrap = v.__dict__ if isinstance(v, TapisResult) else v

                # Nested dict or object
                if isinstance(v_unwrap, dict):
                    # opening brace for top-level objects
                    if key_prefix:
                        print(f'{sp}{key_prefix}{k}: ' + '{')
                        next_prefix = ""
                    else:
                        print(f'{sp}{k}: ' + '{')
                        next_prefix = ""
                    print_nested(next_prefix, v_unwrap, indent + 1)
                    print(f'{sp}' + '}')
                    if i != len(ordered) - 1:
                        pass  # stylistically omit commas for readability

                # Lists
                elif isinstance(v_unwrap, list):
                    # Decide inline vs expanded
                    contains_objects = any(
                        (isinstance(it, (dict, TapisResult))) for it in v_unwrap
                    )
                    label = f'{sp}{key_prefix}{k}: ' if key_prefix else f'{sp}{k}: '
                    if not v_unwrap:
                        print(label + '[]')
                    elif contains_objects:
                        print(label + '[')
                        for j, it in enumerate(v_unwrap):
                            it_unwrap = it.__dict__ if isinstance(it, TapisResult) else it
                            if isinstance(it_unwrap, dict):
                                print('  ' * (indent + 1) + '{')
                                print_nested("", it_unwrap, indent + 2)
                                print('  ' * (indent + 1) + '}')
                            else:
                                val = _quote_if_str(it_unwrap)
                                print('  ' * (indent + 1) + f'{val}')
                            if j != len(v_unwrap) - 1:
                                pass  # omit commas for readability
                        print(sp + ']')
                    else:
                        # all simple values → inline
                        vals = [_quote_if_str(x) for x in v_unwrap]
                        print(label + f'[{", ".join(map(str, vals))}]')

                # Scalars
                else:
                    val = _quote_if_str(v_unwrap)
                    label = f'{sp}{key_prefix}{k}: ' if key_prefix else f'{sp}{k}: '
                    print(label + f'{val}')

        # List handling (rare for top-level)
        elif isinstance(obj, list):
            sp = '  ' * indent
            if not obj:
                print(sp + '[]')
                return
            contains_objects = any(isinstance(it, (dict, TapisResult)) for it in obj)
            if contains_objects:
                print(sp + '[')
                for it in obj:
                    it_unwrap = it.__dict__ if isinstance(it, TapisResult) else it
                    if isinstance(it_unwrap, dict):
                        print('  ' * (indent + 1) + '{')
                        print_nested("", it_unwrap, indent + 2)
                        print('  ' * (indent + 1) + '}')
                    else:
                        print('  ' * (indent + 1) + f'{_quote_if_str(it_unwrap)}')
                print(sp + ']')
            else:
                vals = [_quote_if_str(x) for x in obj]
                print(sp + f'[{", ".join(map(str, vals))}]')

        # Fallback scalar
        else:
            val = _quote_if_str(obj)
            print(sp + f'{val}')

    # Header
    print('########################################')
    print('########################################')
    # Body (start with a label to indicate the root)
    print('{')
    print_nested("", thisAppSchema, indent=1)
    print('}')
    print('########################################')
OpsUtils.display_tapis_results(system_def)
########################################
########################################
{
  isPublic: True
  isDynamicEffectiveUser: True
  tenant: "designsafe"
  id: "stampede3"
  description: "System for running jobs on the Stampede3 HPC system."
  systemType: "LINUX"
  owner: "wma_prtl"
  host: "stampede3.tacc.utexas.edu"
  enabled: True
  effectiveUserId: "silvia"
  defaultAuthnMethod: "TMS_KEYS"
  authnCredential: None
  bucketName: None
  rootDir: "/"
  port: 22
  useProxy: False
  proxyHost: None
  proxyPort: -1
  dtnSystemId: None
  canExec: True
  canRunBatch: True
  enableCmdPrefix: True
  mpiCmd: None
  jobWorkingDir: "HOST_EVAL($SCRATCH)/tapis/${JobUUID}"
  jobMaxJobs: 2147483647
  jobMaxJobsPerUser: 2147483647
  batchScheduler: "SLURM"
  batchDefaultLogicalQueue: "skx"
  batchSchedulerProfile: "tacc-apptainer"
  importRefId: None
  uuid: "2f9af736-6ab4-474f-b342-f57937f2340e"
  allowChildren: False
  parentId: None
  deleted: False
  created: "2024-02-26T21:19:26.544184Z"
  updated: "2025-08-05T18:50:03.747079Z"
  sharedWithUsers: []
  jobRuntimes: [
    {
      runtimeType: "SINGULARITY"
      version: None
    }
  ]
  jobEnvVariables: []
  batchLogicalQueues: [
    {
      name: "icx"
      description: None
      hpcQueueName: "icx"
      maxJobs: -1
      maxJobsPerUser: 20
      minNodeCount: 1
      maxNodeCount: 32
      minCoresPerNode: 1
      maxCoresPerNode: 80
      minMemoryMB: 1
      maxMemoryMB: 256000
      minMinutes: 1
      maxMinutes: 2880
    }
    {
      name: "skx"
      description: None
      hpcQueueName: "skx"
      maxJobs: -1
      maxJobsPerUser: 60
      minNodeCount: 1
      maxNodeCount: 256
      minCoresPerNode: 1
      maxCoresPerNode: 48
      minMemoryMB: 1
      maxMemoryMB: 192000
      minMinutes: 1
      maxMinutes: 2880
    }
    {
      name: "skx-dev"
      description: None
      hpcQueueName: "skx-dev"
      maxJobs: -1
      maxJobsPerUser: 3
      minNodeCount: 1
      maxNodeCount: 16
      minCoresPerNode: 1
      maxCoresPerNode: 48
      minMemoryMB: 1
      maxMemoryMB: 192000
      minMinutes: 1
      maxMinutes: 120
    }
    {
      name: "pvc"
      description: None
      hpcQueueName: "pvc"
      maxJobs: -1
      maxJobsPerUser: 4
      minNodeCount: 1
      maxNodeCount: 4
      minCoresPerNode: 1
      maxCoresPerNode: 96
      minMemoryMB: 1
      maxMemoryMB: 128000
      minMinutes: 1
      maxMinutes: 2880
    }
    {
      name: "spr"
      description: None
      hpcQueueName: "spr"
      maxJobs: -1
      maxJobsPerUser: 36
      minNodeCount: 1
      maxNodeCount: 32
      minCoresPerNode: 1
      maxCoresPerNode: 112
      minMemoryMB: 1
      maxMemoryMB: 128000
      minMinutes: 1
      maxMinutes: 2880
    }
    {
      name: "nvdimm"
      description: None
      hpcQueueName: "nvdimm"
      maxJobs: -1
      maxJobsPerUser: 3
      minNodeCount: 1
      maxNodeCount: 1
      minCoresPerNode: 1
      maxCoresPerNode: 80
      minMemoryMB: 1
      maxMemoryMB: 4000000
      minMinutes: 1
      maxMinutes: 2880
    }
    {
      name: "h100"
      description: None
      hpcQueueName: "h100"
      maxJobs: -1
      maxJobsPerUser: 4
      minNodeCount: 1
      maxNodeCount: 4
      minCoresPerNode: 1
      maxCoresPerNode: 96
      minMemoryMB: 1
      maxMemoryMB: 1000000
      minMinutes: 1
      maxMinutes: 2880
    }
  ]
  jobCapabilities: []
  tags: []
  notes: {
    label: "Stampede3"
  }
}
########################################

Get Specs on System-Specific QUEUES#

We are going to use a utility function that will get the systm info and reformat the nested dictionary into a dataframe. You can use this info to slect your queue

get_system_queues.py
# ../OpsUtils/OpsUtils/Tapis/get_system_queues.py
def get_system_queues(t, system_id="stampede3", display=True):
    """
    Retrieve and display the batch queues available on a given Tapis system.

    This function queries the system definition from Tapis, extracts the list of
    batch queues, builds a Pandas DataFrame for easy inspection, and optionally
    displays it in a transposed format (with queue names as columns).

    It also returns a dictionary keyed by queue name, so you can look up individual
    queue properties programmatically.

    Parameters
    ----------
    t : Tapis
        An authenticated Tapis client (from connect_tapis()).

    system_id : str, default="stampede3"
        The ID of the Tapis-registered system to query (such as "stampede3" on DesignSafe).

    display : bool, default=True
        If True, displays the transposed DataFrame of queues for easy exploration.

    Returns
    -------
    dict
        A dictionary where each key is a queue name and the value is a dictionary
        of that queue's properties.

    Example
    -------
    queues_info = get_system_queues(t, system_id="stampede3", display=True)
    print(queues_info["skx-normal"])
    """
    # code by Silvia Mazzoni, 2025
    import pandas as pd
    from tapipy.tapis import Tapis
    system_def = t.systems.getSystem(systemId=system_id)
    # Convert each TapisResult to a dictionary
    queue_dicts = [queue.__dict__ for queue in system_def.batchLogicalQueues]
    
    # Create the DataFrame
    queues_df = pd.DataFrame(queue_dicts)
    queues_df.set_index('name', inplace=True)
    
    # Optional: display the DataFrame nicely
    from IPython.display import display
    # display(queues_df)
    
    transposed_df = queues_df.T
    if display:
        display(transposed_df)
    # Return as dictionary keyed by queue name
    return {q["name"]: q for q in queue_dicts}
queue_dict = OpsUtils.get_system_queues(t,system_id="stampede3",display=True)
name icx skx skx-dev pvc spr nvdimm h100
description None None None None None None None
hpcQueueName icx skx skx-dev pvc spr nvdimm h100
maxJobs -1 -1 -1 -1 -1 -1 -1
maxJobsPerUser 20 60 3 4 36 3 4
minNodeCount 1 1 1 1 1 1 1
maxNodeCount 32 256 16 4 32 1 4
minCoresPerNode 1 1 1 1 1 1 1
maxCoresPerNode 80 48 48 96 112 80 96
minMemoryMB 1 1 1 1 1 1 1
maxMemoryMB 256000 192000 192000 128000 128000 4000000 1000000
minMinutes 1 1 1 1 1 1 1
maxMinutes 2880 2880 120 2880 2880 2880 2880