DICOM Basics using Java - Modality Worklist (MWL)

Introduction

This article is part of my series of articles on the DICOM standard. If you are totally new to DICOM, please have a quick look at my earlier article titled "Introduction to the DICOM Standard". In this tutorial, we will explore the DICOM Modality Worklist (MWL) service, which is essential for integrating imaging modalities with hospital information systems.

What is Modality Worklist?

Modality Worklist is a DICOM service that allows imaging modalities (CT, MR, US, X-Ray, etc.) to query the hospital information system (HIS) or Radiology Information System (RIS) for scheduled procedures. This eliminates manual data entry at the modality and ensures patient demographic data is consistent across the enterprise.

The Theory Behind Worklist Integration

Modality Worklist addresses one of the most fundamental challenges in healthcare IT: the Master Patient Index (MPI) problem. In any healthcare organization, patient identity must flow consistently from administrative systems (where patients are registered) to clinical systems (where care is delivered). Without this flow, the same patient might appear under different IDs across systems, leading to dangerous fragmentation of medical records.

The Information Flow Problem

Consider the pre-worklist workflow at an imaging center:

  1. Patient registration creates demographic record in HIS
  2. Physician orders an imaging study in the order entry system
  3. Radiology schedules the procedure in the RIS
  4. Technologist manually re-types patient information at the scanner
  5. Images are created with potentially mismatched patient data

This manual transcription creates multiple failure modes: typos, misheard names, transposed digits in patient IDs, and "John Smith" confusion between different patients with similar names. Studies have shown that up to 8% of radiology exams had patient identification discrepancies before worklist adoption.

The IHE Scheduled Workflow Profile

Modality Worklist is a cornerstone of the IHE (Integrating the Healthcare Enterprise) Scheduled Workflow (SWF) integration profile. IHE defines how systems should interact in real clinical scenarios, and SWF specifies the complete workflow from order entry through image availability. Key actors include:

  • ADT (Admission/Discharge/Transfer): Provides patient demographics via HL7
  • Order Placer: Creates the imaging order
  • Department System Scheduler/Order Filler: Schedules procedures and serves MWL
  • Acquisition Modality: Queries MWL and performs imaging
  • Image Manager/Archive: Stores and manages images (PACS)

Query Matching Semantics

MWL queries use DICOM's hierarchical matching semantics. Understanding these is crucial for efficient queries:

  • Single Value Matching: Exact match (e.g., PatientID = "12345")
  • Wild Card Matching: Pattern match using * and ? (e.g., PatientName = "Smith*")
  • Range Matching: Date/time ranges (e.g., ScheduledDate = "20240101-20240131")
  • Sequence Matching: Match within nested sequences
  • Universal Matching: Empty attribute returns all values (zero-length query)

Key benefits of Modality Worklist:

  • Reduced data entry errors - Patient demographics are pulled from the RIS, not entered manually
  • Improved efficiency - Technologists can select procedures from a list rather than typing information
  • Better data consistency - Same patient information is used across all systems
  • Workflow integration - Imaging is tied to scheduled orders from the ordering physician

How MWL Works

Modality Worklist uses the DICOM C-FIND operation with its own information model organized around the Scheduled Procedure Step entity, rather than the Patient/Study/Series/Image hierarchy used in standard Query/Retrieve. It uses the Modality Worklist Information Model — FIND SOP Class (UID 1.2.840.10008.5.1.4.31). The workflow is:

  1. Modality sends a C-FIND request with query parameters (patient name, scheduled date, modality type, etc.)
  2. Worklist provider (RIS/HIS) searches scheduled procedures matching the criteria
  3. Results are returned containing patient demographics and procedure details
  4. Technologist selects the appropriate procedure from the list
  5. Patient and procedure information is automatically populated in the images

Tools for Tutorial

“The art of medicine consists of amusing the patient while nature cures the disease.” ~ Voltaire

Key MWL Query Attributes

When querying a Modality Worklist, you typically include the following attributes:

Patient-Level Attributes:

  • Patient Name (0010,0010)
  • Patient ID (0010,0020)
  • Patient Birth Date (0010,0030)
  • Patient Sex (0010,0040)

Study-Level Attributes:

  • Study Instance UID (0020,000D)
  • Accession Number (0008,0050)
  • Referring Physician (0008,0090)

Scheduled Procedure Step Sequence (0040,0100):

  • Scheduled Station AE Title (0040,0001)
  • Scheduled Procedure Step Start Date (0040,0002)
  • Scheduled Procedure Step Start Time (0040,0003)
  • Modality (0008,0060)
  • Scheduled Procedure Step Description (0040,0007)

Example: MWL Query in Java

package com.saravanansubramanian.dicom.pixelmedtutorial;

import com.pixelmed.dicom.Attribute;
import com.pixelmed.dicom.AttributeList;
import com.pixelmed.dicom.AttributeTag;
import com.pixelmed.dicom.CodeStringAttribute;
import com.pixelmed.dicom.DicomException;
import com.pixelmed.dicom.SOPClass;
import com.pixelmed.dicom.SequenceAttribute;
import com.pixelmed.dicom.SpecificCharacterSet;
import com.pixelmed.dicom.TagFromName;
import com.pixelmed.network.FindSOPClassSCU;
import com.pixelmed.network.IdentifierHandler;

/**
 * DICOM Modality Worklist Query Demo
 */
public class ModalityWorklistQueryDemo {

    public static void main(String[] args) {
        try {
            System.out.println("=== DICOM Modality Worklist Query Demo ===\n");

            SpecificCharacterSet specificCharacterSet = new SpecificCharacterSet((String[]) null);
            AttributeList identifier = new AttributeList();

            // === Patient-level attributes to retrieve ===
            identifier.putNewAttribute(TagFromName.PatientName, specificCharacterSet);
            identifier.putNewAttribute(TagFromName.PatientID, specificCharacterSet);
            identifier.putNewAttribute(TagFromName.PatientBirthDate);
            identifier.putNewAttribute(TagFromName.PatientSex);
            identifier.putNewAttribute(TagFromName.PatientWeight);

            // === Study-level attributes ===
            identifier.putNewAttribute(TagFromName.StudyInstanceUID);
            identifier.putNewAttribute(TagFromName.AccessionNumber, specificCharacterSet);
            identifier.putNewAttribute(TagFromName.RequestingPhysician, specificCharacterSet);
            identifier.putNewAttribute(TagFromName.ReferringPhysicianName, specificCharacterSet);

            // === Requested Procedure attributes ===
            identifier.putNewAttribute(TagFromName.RequestedProcedureID, specificCharacterSet);
            identifier.putNewAttribute(TagFromName.RequestedProcedureDescription, specificCharacterSet);
            identifier.putNewAttribute(TagFromName.RequestedProcedurePriority, specificCharacterSet);

            // === Scheduled Procedure Step Sequence ===
            SequenceAttribute scheduledProcedureStepSequence =
                new SequenceAttribute(TagFromName.ScheduledProcedureStepSequence);
            AttributeList scheduledProcedureStepItem = new AttributeList();

            // Add attributes to query within the sequence
            scheduledProcedureStepItem.putNewAttribute(
                TagFromName.ScheduledStationAETitle, specificCharacterSet);
            scheduledProcedureStepItem.putNewAttribute(
                TagFromName.ScheduledProcedureStepStartDate);
            scheduledProcedureStepItem.putNewAttribute(
                TagFromName.ScheduledProcedureStepStartTime);
            scheduledProcedureStepItem.putNewAttribute(
                TagFromName.ScheduledPerformingPhysicianName, specificCharacterSet);
            scheduledProcedureStepItem.putNewAttribute(
                TagFromName.ScheduledProcedureStepDescription, specificCharacterSet);
            scheduledProcedureStepItem.putNewAttribute(
                TagFromName.ScheduledProcedureStepID, specificCharacterSet);
            scheduledProcedureStepItem.putNewAttribute(
                TagFromName.ScheduledProcedureStepLocation, specificCharacterSet);

            // Filter by modality (e.g., CT, MR, US, XA, etc.)
            Attribute modalityAttr = new CodeStringAttribute(TagFromName.Modality);
            modalityAttr.addValue("CT"); // Query for CT procedures only
            scheduledProcedureStepItem.put(modalityAttr);

            // Filter by scheduled date range
            scheduledProcedureStepItem.putNewAttribute(TagFromName.ScheduledProcedureStepStartDate)
                .addValue("20240101-20241231");

            scheduledProcedureStepSequence.addItem(scheduledProcedureStepItem);
            identifier.put(scheduledProcedureStepSequence);

            System.out.println("Querying Modality Worklist...");
            System.out.println("Target AE: ORTHANC on localhost:4242\n");

            // Perform the MWL query
            new FindSOPClassSCU(
                "localhost",                                          // hostname
                4242,                                                 // port
                "ORTHANC",                                           // called AE title
                "WORKLISTSCU",                                       // calling AE title
                SOPClass.ModalityWorklistInformationModelFind,       // MWL SOP Class
                identifier,                                          // query attributes
                new WorklistResponseHandler()                        // response handler
            );

            System.out.println("\nMWL Query completed.");

        } catch (Exception e) {
            e.printStackTrace(System.err);
            System.exit(1);
        }
    }
}

/**
 * Custom handler to process MWL query responses
 */
class WorklistResponseHandler extends IdentifierHandler {

    private int responseCount = 0;

    @Override
    public void doSomethingWithIdentifier(AttributeList responseIdentifier) throws DicomException {

        responseCount++;
        System.out.println("=== Worklist Item #" + responseCount + " ===");

        // Extract patient information
        String patientName = getAttributeValue(responseIdentifier, TagFromName.PatientName);
        String patientId = getAttributeValue(responseIdentifier, TagFromName.PatientID);
        String patientBirthDate = getAttributeValue(responseIdentifier, TagFromName.PatientBirthDate);
        String patientSex = getAttributeValue(responseIdentifier, TagFromName.PatientSex);

        System.out.println("Patient Name: " + patientName);
        System.out.println("Patient ID: " + patientId);
        System.out.println("Birth Date: " + patientBirthDate);
        System.out.println("Sex: " + patientSex);

        // Extract procedure information
        String accessionNumber = getAttributeValue(responseIdentifier, TagFromName.AccessionNumber);
        String requestedProcedure = getAttributeValue(
            responseIdentifier, TagFromName.RequestedProcedureDescription);

        System.out.println("Accession Number: " + accessionNumber);
        System.out.println("Requested Procedure: " + requestedProcedure);

        // Extract scheduled procedure step information
        Attribute spsSequenceAttr = responseIdentifier.get(TagFromName.ScheduledProcedureStepSequence);
        if (spsSequenceAttr != null && spsSequenceAttr instanceof SequenceAttribute) {
            SequenceAttribute spsSequence = (SequenceAttribute) spsSequenceAttr;
            if (spsSequence.getNumberOfItems() > 0) {
                AttributeList spsItem = spsSequence.getItem(0);

                String scheduledDate = getAttributeValue(spsItem,
                    TagFromName.ScheduledProcedureStepStartDate);
                String scheduledTime = getAttributeValue(spsItem,
                    TagFromName.ScheduledProcedureStepStartTime);
                String modality = getAttributeValue(spsItem, TagFromName.Modality);
                String stationAE = getAttributeValue(spsItem,
                    TagFromName.ScheduledStationAETitle);

                System.out.println("Scheduled Date: " + scheduledDate);
                System.out.println("Scheduled Time: " + scheduledTime);
                System.out.println("Modality: " + modality);
                System.out.println("Station AE: " + stationAE);
            }
        }

        System.out.println();
    }

    private String getAttributeValue(AttributeList list, AttributeTag tag) {
        Attribute attr = list.get(tag);
        if (attr != null) {
            String value = attr.getSingleStringValueOrNull();
            return value != null ? value : "(empty)";
        }
        return "(not present)";
    }
}

“The greatest wealth is health.” ~ Virgil

Setting Up Orthanc Worklist Plugin

To use Orthanc as a Modality Worklist provider, you need to configure the Worklist plugin. The standard Orthanc Worklist plugin expects worklist entries as DICOM binary worklist files (.wl) placed in the WorklistsDatabase folder. Below is an example of the worklist data structure shown in JSON for readability -- in practice, you would need to convert this to a DICOM binary worklist file (e.g., using the dump2dcm tool from the DCMTK toolkit) before placing it in the WorklistsDatabase folder:

{
  "PatientName": "DOE^JOHN",
  "PatientID": "12345",
  "PatientBirthDate": "19800101",
  "PatientSex": "M",
  "AccessionNumber": "ACC001",
  "RequestedProcedureDescription": "CT CHEST WITH CONTRAST",
  "ScheduledProcedureStepSequence": [
    {
      "Modality": "CT",
      "ScheduledStationAETitle": "CT_SCANNER",
      "ScheduledProcedureStepStartDate": "20240115",
      "ScheduledProcedureStepStartTime": "090000",
      "ScheduledProcedureStepDescription": "CT CHEST WITH CONTRAST"
    }
  ]
}

MWL in the Clinical Workflow

Here's how MWL fits into a typical radiology workflow:

  1. Order Entry: Physician orders an imaging exam in the EMR/HIS
  2. Scheduling: Radiology schedules the procedure in the RIS
  3. Worklist Query: Modality queries the RIS for scheduled procedures
  4. Patient Check-in: Technologist selects the patient from the worklist
  5. Image Acquisition: Images are acquired with correct patient/procedure info
  6. MPPS (Optional): Modality reports procedure status back to RIS
  7. Storage: Images are sent to PACS via C-STORE

Conclusion

Modality Worklist is a critical component of healthcare imaging workflows. It ensures that patient and procedure information flows seamlessly from the ordering system to the imaging modality, reducing errors and improving efficiency. In the next tutorial in this series, I will cover Modality Performed Procedure Step (MPPS) which, combined with MWL, provides complete workflow integration between RIS and imaging modalities. See you then!