diff --git a/docs/source/development/architecture.rst b/docs/source/development/architecture.rst
index 4daf5c908..5627b6c78 100644
--- a/docs/source/development/architecture.rst
+++ b/docs/source/development/architecture.rst
@@ -49,7 +49,7 @@ The dictionary has the following structure:
             "pseudo_family": "SSSP/1.3/PBEsol/efficiency",
             "kpoints_distance": 0.5,
         },
-        "bands": {"kpath_2d": "hexagonal"},
+        "bands": {},
         "pdos": {...},
         "plugin_1": {...},
         "plugin_2": {...},
diff --git a/docs/source/development/plugin.rst b/docs/source/development/plugin.rst
index 9176e69fd..5199e70cf 100644
--- a/docs/source/development/plugin.rst
+++ b/docs/source/development/plugin.rst
@@ -210,7 +210,7 @@ The `parameters` passed to the `get_builder` function has the following structur
             "pseudo_family": "SSSP/1.3/PBEsol/efficiency",
             "kpoints_distance": 0.5,
         },
-        "bands": {"kpath_2d": "hexagonal"},
+        "bands": {},
         "pdos": {...},
         "eos": {...},
         "plugin_1": {...},
diff --git a/setup.cfg b/setup.cfg
index 96b30824d..ae279ba08 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -31,6 +31,7 @@ install_requires =
     aiida-pseudo~=1.4
     filelock~=3.8
     importlib-resources~=5.2
+    aiida-wannier90-workflows==2.3.0
 python_requires = >=3.9
 
 [options.packages.find]
@@ -60,6 +61,9 @@ aiidalab_qe.properties =
     electronic_structure = aiidalab_qe.plugins.electronic_structure:electronic_structure
     xas = aiidalab_qe.plugins.xas:xas
 
+aiida.workflows =
+    aiidalab_qe.bands_workchain = aiidalab_qe.plugins.bands.bands_workchain:BandsWorkChain
+
 [aiidalab]
 title = Quantum ESPRESSO
 description = Perform Quantum ESPRESSO calculations
diff --git a/src/aiidalab_qe/app/configuration/workflow.py b/src/aiidalab_qe/app/configuration/workflow.py
index 47db49cc0..408906ee5 100644
--- a/src/aiidalab_qe/app/configuration/workflow.py
+++ b/src/aiidalab_qe/app/configuration/workflow.py
@@ -38,14 +38,6 @@ class WorkChainSettings(Panel):
         """<div style="padding-top: 0px; padding-bottom: 0px">
         <h4>Properties</h4></div>"""
     )
-    properties_help = ipw.HTML(
-        """<div style="line-height: 140%; padding-top: 10px; padding-bottom: 0px">
-        The band structure workflow will
-        automatically detect the default path in reciprocal space using the
-        <a href="https://www.materialscloud.org/work/tools/seekpath" target="_blank">
-        SeeK-path tool</a>.</div>"""
-    )
-
     protocol_title = ipw.HTML(
         """<div style="padding-top: 0px; padding-bottom: 0px">
         <h4>Protocol</h4></div>"""
@@ -116,7 +108,6 @@ def update_reminder_info(change, name=name):
             if name in setting_entries:
                 self.properties[name].run.observe(update_reminder_info, "value")
 
-        self.property_children.append(self.properties_help)
         self.children = [
             self.structure_title,
             self.structure_help,
diff --git a/src/aiidalab_qe/app/parameters/qeapp.yaml b/src/aiidalab_qe/app/parameters/qeapp.yaml
index c00c0f1c1..1fba8c032 100644
--- a/src/aiidalab_qe/app/parameters/qeapp.yaml
+++ b/src/aiidalab_qe/app/parameters/qeapp.yaml
@@ -29,6 +29,8 @@ codes:
         code: dos-7.2@localhost
     projwfc:
         code: projwfc-7.2@localhost
+    projwfc_bands:
+        code: projwfc-7.2@localhost
     pw:
         code: pw-7.2@localhost
     pp:
diff --git a/src/aiidalab_qe/plugins/bands/__init__.py b/src/aiidalab_qe/plugins/bands/__init__.py
index 3d67f0a7b..b38797141 100644
--- a/src/aiidalab_qe/plugins/bands/__init__.py
+++ b/src/aiidalab_qe/plugins/bands/__init__.py
@@ -1,5 +1,6 @@
 # from aiidalab_qe.bands.result import Result
 from aiidalab_qe.common.panel import OutlinePanel
+from aiidalab_qe.common.widgets import QEAppComputationalResourcesWidget
 
 from .result import Result
 from .setting import Setting
@@ -15,8 +16,15 @@ class BandsOutline(OutlinePanel):
 """
 
 
+projwfc_code = QEAppComputationalResourcesWidget(
+    description="projwfc.x",
+    default_calc_job_plugin="quantumespresso.projwfc",
+)
+
+
 bands = {
     "outline": BandsOutline,
+    "code": {"projwfc_bands": projwfc_code},
     "setting": Setting,
     "result": Result,
     "workchain": workchain_and_builder,
diff --git a/src/aiidalab_qe/plugins/bands/bands_workchain.py b/src/aiidalab_qe/plugins/bands/bands_workchain.py
new file mode 100644
index 000000000..d5401341d
--- /dev/null
+++ b/src/aiidalab_qe/plugins/bands/bands_workchain.py
@@ -0,0 +1,382 @@
+import math
+
+import numpy as np
+
+from aiida import orm
+from aiida.common import AttributeDict
+from aiida.engine import WorkChain
+from aiida.plugins import DataFactory, WorkflowFactory
+
+GAMMA = "\u0393"
+
+PwBandsWorkChain = WorkflowFactory("quantumespresso.pw.bands")
+ProjwfcBandsWorkChain = WorkflowFactory("wannier90_workflows.projwfcbands")
+KpointsData = DataFactory("core.array.kpoints")
+
+
+def points_per_branch(vector_a, vector_b, reciprocal_cell, bands_kpoints_distance):
+    """function to calculate the number of points per branch depending on the kpoints_distance and the reciprocal cell"""
+    scaled_vector_a = np.array(vector_a)
+    scaled_vector_b = np.array(vector_b)
+    reciprocal_vector_a = scaled_vector_a.dot(reciprocal_cell)
+    reciprocal_vector_b = scaled_vector_b.dot(reciprocal_cell)
+    distance = np.linalg.norm(reciprocal_vector_a - reciprocal_vector_b)
+    return max(
+        2, int(np.round(distance / bands_kpoints_distance))
+    )  # at least two points for each segment, including both endpoints explicitly
+
+
+def calculate_bands_kpoints_distance(kpoints_distance):
+    """function to calculate the bands_kpoints_distance depending on the kpoints_distance"""
+    if kpoints_distance >= 0.5:
+        return 0.1
+    elif 0.15 < kpoints_distance < 0.5:
+        return 0.025
+    else:
+        return 0.015
+
+
+def generate_kpath_1d(structure, kpoints_distance):
+    """Return a kpoints object for one dimensional systems (from Gamma to X)
+    The number of kpoints is calculated based on the kpoints_distance (as in the PwBandsWorkChain protocol)
+    """
+    kpoints = KpointsData()
+    kpoints.set_cell_from_structure(structure)
+    reciprocal_cell = kpoints.reciprocal_cell
+    bands_kpoints_distance = calculate_bands_kpoints_distance(kpoints_distance)
+
+    # Number of points per branch
+    num_points_per_branch = points_per_branch(
+        [0.0, 0.0, 0.0],
+        [0.5, 0.0, 0.0],
+        reciprocal_cell,
+        bands_kpoints_distance,
+    )
+    # Generate the kpoints
+    points = np.linspace(
+        start=[0.0, 0.0, 0.0],
+        stop=[0.5, 0.0, 0.0],
+        endpoint=True,
+        num=num_points_per_branch,
+    )
+    kpoints.set_kpoints(points.tolist())
+    kpoints.labels = [[0, GAMMA], [len(points) - 1, "X"]]
+    return kpoints
+
+
+def generate_kpath_2d(structure, kpoints_distance, kpath_2d):
+    """Return a kpoints object for two dimensional systems based on the selected 2D symmetry path
+    The number of kpoints is calculated based on the kpoints_distance (as in the PwBandsWorkChain protocol)
+    The 2D symmetry paths are defined as in The Journal of Physical Chemistry Letters 2022 13 (50), 11581-11594 (https://pubs.acs.org/doi/10.1021/acs.jpclett.2c02972)
+    """
+    kpoints = KpointsData()
+    kpoints.set_cell_from_structure(structure)
+    reciprocal_cell = kpoints.reciprocal_cell
+    bands_kpoints_distance = calculate_bands_kpoints_distance(kpoints_distance)
+
+    # dictionary with the 2D symmetry paths
+    selected_paths = {
+        "hexagonal": {
+            "path": [
+                [0.0, 0.0, 0.0],
+                [0.5, 0.0, 0.0],
+                [0.33333, 0.33333, 0.0],
+                [1.0, 0.0, 0.0],
+            ],
+            "labels": [GAMMA, "M", "K", GAMMA],
+        },
+        "square": {
+            "path": [
+                [0.0, 0.0, 0.0],
+                [0.5, 0.0, 0.0],
+                [0.5, 0.5, 0.0],
+                [1.0, 0.0, 0.0],
+            ],
+            "labels": [GAMMA, "X", "M", GAMMA],
+        },
+        "rectangular": {
+            "path": [
+                [0.0, 0.0, 0.0],
+                [0.5, 0.0, 0.0],
+                [0.5, 0.5, 0.0],
+                [0.0, 0.5, 0.0],
+                [1.0, 0.0, 0.0],
+            ],
+            "labels": [GAMMA, "X", "S", "Y", GAMMA],
+        },
+    }
+    # if the selected path is centered_rectangular or oblique, the path is calculated based on the reciprocal cell
+    if kpath_2d in ["centered_rectangular", "oblique"]:
+        a1 = reciprocal_cell[0]
+        a2 = reciprocal_cell[1]
+        norm_a1 = np.linalg.norm(a1)
+        norm_a2 = np.linalg.norm(a2)
+        cos_gamma = (
+            a1.dot(a2) / (norm_a1 * norm_a2)
+        )  # Angle between a1 and a2 # like in https://pubs.acs.org/doi/10.1021/acs.jpclett.2c02972
+        gamma = np.arccos(cos_gamma)
+        eta = (1 - (norm_a1 / norm_a2) * cos_gamma) / (2 * np.power(np.sin(gamma), 2))
+        nu = 0.5 - (eta * norm_a2 * cos_gamma) / norm_a1
+        selected_paths["centered_rectangular"] = {
+            "path": [
+                [0.0, 0.0, 0.0],
+                [0.5, 0.0, 0.0],
+                [1 - eta, nu, 0],
+                [0.5, 0.5, 0.0],
+                [eta, 1 - nu, 0.0],
+                [1.0, 0.0, 0.0],
+            ],
+            "labels": [GAMMA, "X", "H_1", "C", "H", GAMMA],
+        }
+        selected_paths["oblique"] = {
+            "path": [
+                [0.0, 0.0, 0.0],
+                [0.5, 0.0, 0.0],
+                [1 - eta, nu, 0],
+                [0.5, 0.5, 0.0],
+                [eta, 1 - nu, 0.0],
+                [0.0, 0.5, 0.0],
+                [1.0, 0.0, 0.0],
+            ],
+            "labels": [GAMMA, "X", "H_1", "C", "H", "Y", GAMMA],
+        }
+    path = selected_paths[kpath_2d]["path"]
+    labels = selected_paths[kpath_2d]["labels"]
+    branches = zip(path[:-1], path[1:])
+
+    all_kpoints = []  # List to hold all k-points
+    label_map = []  # List to hold labels and their corresponding k-point indices
+
+    # Calculate the number of points per branch and generate the kpoints
+    index_offset = 0  # Start index for each segment
+    for (start, end), label_start, _ in zip(branches, labels[:-1], labels[1:]):
+        num_points_per_branch = points_per_branch(
+            start, end, reciprocal_cell, bands_kpoints_distance
+        )
+        # Exclude endpoint except for the last segment to prevent duplication
+        points = np.linspace(start, end, num=num_points_per_branch, endpoint=False)
+        all_kpoints.extend(points)
+        label_map.append(
+            (index_offset, label_start)
+        )  # Label for the start of the segment
+        index_offset += len(points)
+
+    # Include the last point and its label
+    all_kpoints.append(path[-1])
+    label_map.append((index_offset, labels[-1]))  # Label for the last point
+
+    # Set the kpoints and their labels in KpointsData
+    kpoints.set_kpoints(all_kpoints)
+    kpoints.labels = label_map
+
+    return kpoints
+
+
+def determine_symmetry_path(structure):
+    # Tolerance for checking equality
+    cell_lengths = structure.cell_lengths
+    cell_angles = structure.cell_angles
+    tolerance = 1e-3
+
+    # Define symmetry conditions and their corresponding types in a dictionary
+    symmetry_conditions = {
+        (
+            math.isclose(cell_lengths[0], cell_lengths[1], abs_tol=tolerance)
+            and math.isclose(cell_angles[2], 120.0, abs_tol=tolerance)
+        ): "hexagonal",
+        (
+            math.isclose(cell_lengths[0], cell_lengths[1], abs_tol=tolerance)
+            and math.isclose(cell_angles[2], 90.0, abs_tol=tolerance)
+        ): "square",
+        (
+            not math.isclose(cell_lengths[0], cell_lengths[1], abs_tol=tolerance)
+            and math.isclose(cell_angles[2], 90.0, abs_tol=tolerance)
+        ): "rectangular",
+        (
+            math.isclose(
+                cell_lengths[1] * math.cos(math.radians(cell_angles[2])),
+                cell_lengths[0] / 2,
+                abs_tol=tolerance,
+            )
+        ): "rectangular_centered",
+        (
+            not math.isclose(cell_lengths[0], cell_lengths[1], abs_tol=tolerance)
+            and not math.isclose(cell_angles[2], 90.0, abs_tol=tolerance)
+        ): "oblique",
+    }
+
+    # Check for symmetry type based on conditions
+    for condition, symmetry_type in symmetry_conditions.items():
+        if condition:
+            return symmetry_type
+
+    raise ValueError("Invalid symmetry type")
+
+
+class BandsWorkChain(WorkChain):
+    "Workchain to compute the electronic band structure"
+
+    label = "bands"
+
+    @classmethod
+    def define(cls, spec):
+        super().define(spec)
+
+        spec.input("structure", valid_type=orm.StructureData)
+        spec.expose_inputs(
+            PwBandsWorkChain,
+            namespace="bands",
+            exclude=["structure", "relax"],
+            namespace_options={
+                "required": False,
+                "populate_defaults": False,
+                "help": "Inputs for the `PwBandsWorkChain`, simulation mode normal.",
+            },
+        )
+        spec.expose_inputs(
+            ProjwfcBandsWorkChain,
+            namespace="bands_projwfc",
+            exclude=["structure", "relax"],
+            namespace_options={
+                "required": False,
+                "populate_defaults": False,
+                "help": "Inputs for the `ProjwfcBandsWorkChain`, simulation mode fat_bands.",
+            },
+        )
+
+        spec.expose_outputs(
+            PwBandsWorkChain,
+            namespace="bands",
+            namespace_options={
+                "required": False,
+                "help": "Outputs of the `PwBandsWorkChain`.",
+            },
+        )
+        spec.expose_outputs(
+            ProjwfcBandsWorkChain,
+            namespace="bands_projwfc",
+            namespace_options={
+                "required": False,
+                "help": "Outputs of the `PwBandsWorkChain`.",
+            },
+        )
+
+        spec.outline(cls.setup, cls.run_bands, cls.results)
+
+        spec.exit_code(
+            400, "ERROR_WORKCHAIN_FAILED", message="The workchain bands failed."
+        )
+
+    @classmethod
+    def get_builder_from_protocol(
+        cls,
+        pw_code,
+        projwfc_code,
+        structure,
+        simulation_mode="normal",
+        protocol=None,
+        overrides=None,
+        **kwargs,
+    ):
+        """Return a BandsWorkChain builder prepopulated with inputs following the specified protocol
+
+        :param structure: the ``StructureData`` instance to use.
+        :param pw_code: the ``Code`` instance configured for the ``quantumespresso.pw`` plugin.
+        :param protocol: protocol to use, if not specified, the default will be used.
+        :param projwfc_code: the ``Code`` instance configured for the ``quantumespresso.projwfc`` plugin.
+        :param simulation_mode: hat type of simulation to run normal band or fat bands.
+
+        """
+
+        builder = cls.get_builder()
+
+        if simulation_mode == "normal":
+            builder_bands = PwBandsWorkChain.get_builder_from_protocol(
+                pw_code, structure, protocol, overrides=overrides, **kwargs
+            )
+            builder.pop("bands_projwfc", None)
+            builder_bands.pop("relax", None)
+            builder_bands.pop("structure", None)
+            builder.bands = builder_bands
+
+        elif simulation_mode == "fat_bands":
+            builder_bands_projwfc = ProjwfcBandsWorkChain.get_builder_from_protocol(
+                pw_code,
+                projwfc_code,
+                structure,
+                protocol=protocol,
+                overrides=overrides,
+                **kwargs,
+            )
+            builder.pop("bands", None)
+            builder_bands_projwfc.pop("relax", None)
+            builder_bands_projwfc.pop("structure", None)
+            builder.bands_projwfc = builder_bands_projwfc
+
+        else:
+            raise ValueError(f"Unknown simulation_mode: {simulation_mode}")
+
+        # Handle periodic boundary conditions (PBC)
+        if structure.pbc != (True, True, True):
+            kpoints_distance = overrides.get("scf", {}).get(
+                "kpoints_distance",
+                builder.get("bands", {}).get("scf", {}).get("kpoints_distance"),
+            )
+            kpoints = None
+            if structure.pbc == (True, False, False):
+                kpoints = generate_kpath_1d(structure, kpoints_distance)
+            elif structure.pbc == (True, True, False):
+                kpoints = generate_kpath_2d(
+                    structure=structure,
+                    kpoints_distance=kpoints_distance,
+                    kpath_2d=determine_symmetry_path(structure),
+                )
+
+            if simulation_mode == "normal":
+                builder.bands.pop("bands_kpoints_distance", None)
+                builder.bands.update({"bands_kpoints": kpoints})
+            elif simulation_mode == "fat_bands":
+                builder.bands_projwfc.pop("bands_kpoints_distance", None)
+                builder.bands_projwfc.update({"bands_kpoints": kpoints})
+
+        builder.structure = structure
+        return builder
+
+    def setup(self):
+        """Define the current workchain"""
+        self.ctx.current_structure = self.inputs.structure
+        if "bands" in self.inputs:
+            self.ctx.key = "bands"
+            self.ctx.workchain = PwBandsWorkChain
+        elif "bands_projwfc" in self.inputs:
+            self.ctx.key = "bands_projwfc"
+            self.ctx.workchain = ProjwfcBandsWorkChain
+        else:
+            self.report("No bands workchain specified")
+            return self.exit_codes.ERROR_WORKCHAIN_FAILED
+
+    def run_bands(self):
+        """Run the bands workchain"""
+        inputs = AttributeDict(
+            self.exposed_inputs(self.ctx.workchain, namespace=self.ctx.key)
+        )
+        inputs.metadata.call_link_label = self.ctx.key
+        inputs.structure = self.ctx.current_structure
+        future = self.submit(self.ctx.workchain, **inputs)
+        self.report(f"submitting `WorkChain` <PK={future.pk}>")
+        self.to_context(**{self.ctx.key: future})
+
+    def results(self):
+        """Attach the bands results"""
+        workchain = self.ctx[self.ctx.key]
+
+        if not workchain.is_finished_ok:
+            self.report("Bands workchain failed")
+            return self.exit_codes.ERROR_WORKCHAIN_FAILED
+        else:
+            self.out_many(
+                self.exposed_outputs(
+                    self.ctx[self.ctx.key], self.ctx.workchain, namespace=self.ctx.key
+                )
+            )
+            self.report("Bands workchain completed successfully")
diff --git a/src/aiidalab_qe/plugins/bands/result.py b/src/aiidalab_qe/plugins/bands/result.py
index 03b7630cd..7ca4b206c 100644
--- a/src/aiidalab_qe/plugins/bands/result.py
+++ b/src/aiidalab_qe/plugins/bands/result.py
@@ -14,11 +14,22 @@ def __init__(self, node=None, **kwargs):
         super().__init__(node=node, **kwargs)
 
     def _update_view(self):
-        # Check if the workchain has the outputs
-        try:
-            bands_node = self.node.outputs.bands
-        except AttributeError:
-            bands_node = None
+        # Initialize bands_node to None by default
+        bands_node = None
+
+        # Check if the workchain has the 'bands' output
+        if hasattr(self.node.outputs, "bands"):
+            bands_output = self.node.outputs.bands
+
+            # Check for 'bands' or 'bands_projwfc' attributes within 'bands' output
+            if hasattr(bands_output, "bands"):
+                bands_node = bands_output.bands
+            elif hasattr(bands_output, "bands_projwfc"):
+                bands_node = bands_output.bands_projwfc
+            else:
+                # If neither 'bands' nor 'bands_projwfc' exist, use 'bands_output' itself
+                # This is the case for compatibility with older versions of the plugin
+                bands_node = bands_output
 
         _bands_plot_view = BandPdosWidget(bands=bands_node)
         self.children = [
diff --git a/src/aiidalab_qe/plugins/bands/setting.py b/src/aiidalab_qe/plugins/bands/setting.py
index 12f012279..05ff99e4e 100644
--- a/src/aiidalab_qe/plugins/bands/setting.py
+++ b/src/aiidalab_qe/plugins/bands/setting.py
@@ -18,39 +18,37 @@ def __init__(self, **kwargs):
             options=["fast", "moderate", "precise"],
             value="moderate",
         )
-        self.kpath_2d_help = ipw.HTML(
-            """<div style="line-height: 140%; padding-top: 0px; padding-bottom: 5px">
-            If your system has periodicity xy. Please select one of the five 2D Bravais lattices corresponding to your system.
-            </div>"""
+        self.properties_help = ipw.HTML(
+            """<div style="line-height: 140%; padding-top: 10px; padding-bottom: 0px">
+        The band structure workflow will
+        automatically detect the default path in reciprocal space using the
+        <a href="https://www.materialscloud.org/work/tools/seekpath" target="_blank">
+        SeeK-path tool</a>.
+        <br><br>
+        Fat Bands is a band structure plot that includes the angular momentum contributions from specific atoms or orbitals to each energy band. The thickness of the bands represents the strength of these contributions, providing insight into the electronic structure.
+        </div>"""
         )
-        self.kpath_2d = ipw.Dropdown(
-            description="Lattice:",
-            options=[
-                ("Hexagonal", "hexagonal"),
-                ("Square", "square"),
-                ("Rectangular", "rectangular"),
-                ("Centered Rectangular", "centered_rectangular"),
-                ("Oblique", "oblique"),
-            ],
-            value="hexagonal",
+        self.projwfc_bands = ipw.Checkbox(
+            description="Fat bands calculation",
+            value=False,
+            style={"description_width": "initial"},
         )
         self.children = [
             self.settings_title,
-            self.kpath_2d_help,
-            self.kpath_2d,
+            self.properties_help,
+            self.projwfc_bands,
         ]
         super().__init__(**kwargs)
 
     def get_panel_value(self):
         """Return a dictionary with the input parameters for the plugin."""
         return {
-            "kpath_2d": self.kpath_2d.value,
+            "projwfc_bands": self.projwfc_bands.value,
         }
 
     def set_panel_value(self, input_dict):
-        """Load a dictionary with the input parameters for the plugin."""
-        self.kpath_2d.value = input_dict.get("kpath_2d", "hexagonal")
+        self.projwfc_bands.value = input_dict.get("projwfc_bands", False)
 
     def reset(self):
         """Reset the panel to its default values."""
-        self.kpath_2d.value = "hexagonal"
+        self.projwfc_bands.value = False
diff --git a/src/aiidalab_qe/plugins/bands/workchain.py b/src/aiidalab_qe/plugins/bands/workchain.py
index c3cc5eb78..863a7dc0b 100644
--- a/src/aiidalab_qe/plugins/bands/workchain.py
+++ b/src/aiidalab_qe/plugins/bands/workchain.py
@@ -1,180 +1,48 @@
-import numpy as np
-
-from aiida.plugins import DataFactory, WorkflowFactory
+from aiida.plugins import WorkflowFactory
 from aiida_quantumespresso.common.types import ElectronicType, SpinType
 from aiidalab_qe.plugins.utils import set_component_resources
 
-GAMMA = "\u0393"
-
-PwBandsWorkChain = WorkflowFactory("quantumespresso.pw.bands")
-KpointsData = DataFactory("core.array.kpoints")
-
+BandsWorkChain = WorkflowFactory("aiidalab_qe.bands_workchain")
+# from .bands_workchain import BandsWorkChain
 
-def points_per_branch(vector_a, vector_b, reciprocal_cell, bands_kpoints_distance):
-    """function to calculate the number of points per branch depending on the kpoints_distance and the reciprocal cell"""
-    scaled_vector_a = np.array(vector_a)
-    scaled_vector_b = np.array(vector_b)
-    reciprocal_vector_a = scaled_vector_a.dot(reciprocal_cell)
-    reciprocal_vector_b = scaled_vector_b.dot(reciprocal_cell)
-    distance = np.linalg.norm(reciprocal_vector_a - reciprocal_vector_b)
-    return max(
-        2, int(np.round(distance / bands_kpoints_distance))
-    )  # at least two points for each segment, including both endpoints explicitly
 
-
-def calculate_bands_kpoints_distance(kpoints_distance):
-    """function to calculate the bands_kpoints_distance depending on the kpoints_distance"""
-    if kpoints_distance >= 0.5:
-        return 0.1
-    elif 0.15 < kpoints_distance < 0.5:
-        return 0.025
-    else:
-        return 0.015
-
-
-def generate_kpath_1d(structure, kpoints_distance):
-    """Return a kpoints object for one dimensional systems (from Gamma to X)
-    The number of kpoints is calculated based on the kpoints_distance (as in the PwBandsWorkChain protocol)
-    """
-    kpoints = KpointsData()
-    kpoints.set_cell_from_structure(structure)
-    reciprocal_cell = kpoints.reciprocal_cell
-    bands_kpoints_distance = calculate_bands_kpoints_distance(kpoints_distance)
-
-    # Number of points per branch
-    num_points_per_branch = points_per_branch(
-        [0.0, 0.0, 0.0],
-        [0.5, 0.0, 0.0],
-        reciprocal_cell,
-        bands_kpoints_distance,
-    )
-    # Generate the kpoints
-    points = np.linspace(
-        start=[0.0, 0.0, 0.0],
-        stop=[0.5, 0.0, 0.0],
-        endpoint=True,
-        num=num_points_per_branch,
-    )
-    kpoints.set_kpoints(points.tolist())
-    kpoints.labels = [[0, GAMMA], [len(points) - 1, "X"]]
-    return kpoints
-
-
-def generate_kpath_2d(structure, kpoints_distance, kpath_2d):
-    """Return a kpoints object for two dimensional systems based on the selected 2D symmetry path
-    The number of kpoints is calculated based on the kpoints_distance (as in the PwBandsWorkChain protocol)
-    The 2D symmetry paths are defined as in The Journal of Physical Chemistry Letters 2022 13 (50), 11581-11594 (https://pubs.acs.org/doi/10.1021/acs.jpclett.2c02972)
-    """
-    kpoints = KpointsData()
-    kpoints.set_cell_from_structure(structure)
-    reciprocal_cell = kpoints.reciprocal_cell
-    bands_kpoints_distance = calculate_bands_kpoints_distance(kpoints_distance)
-
-    # dictionary with the 2D symmetry paths
-    selected_paths = {
-        "hexagonal": {
-            "path": [
-                [0.0, 0.0, 0.0],
-                [0.5, 0.0, 0.0],
-                [0.33333, 0.33333, 0.0],
-                [1.0, 0.0, 0.0],
-            ],
-            "labels": [GAMMA, "M", "K", GAMMA],
-        },
-        "square": {
-            "path": [
-                [0.0, 0.0, 0.0],
-                [0.5, 0.0, 0.0],
-                [0.5, 0.5, 0.0],
-                [1.0, 0.0, 0.0],
-            ],
-            "labels": [GAMMA, "X", "M", GAMMA],
-        },
-        "rectangular": {
-            "path": [
-                [0.0, 0.0, 0.0],
-                [0.5, 0.0, 0.0],
-                [0.5, 0.5, 0.0],
-                [0.0, 0.5, 0.0],
-                [1.0, 0.0, 0.0],
-            ],
-            "labels": [GAMMA, "X", "S", "Y", GAMMA],
-        },
-    }
-    # if the selected path is centered_rectangular or oblique, the path is calculated based on the reciprocal cell
-    if kpath_2d in ["centered_rectangular", "oblique"]:
-        a1 = reciprocal_cell[0]
-        a2 = reciprocal_cell[1]
-        norm_a1 = np.linalg.norm(a1)
-        norm_a2 = np.linalg.norm(a2)
-        cos_gamma = (
-            a1.dot(a2) / (norm_a1 * norm_a2)
-        )  # Angle between a1 and a2 # like in https://pubs.acs.org/doi/10.1021/acs.jpclett.2c02972
-        gamma = np.arccos(cos_gamma)
-        eta = (1 - (norm_a1 / norm_a2) * cos_gamma) / (2 * np.power(np.sin(gamma), 2))
-        nu = 0.5 - (eta * norm_a2 * cos_gamma) / norm_a1
-        selected_paths["centered_rectangular"] = {
-            "path": [
-                [0.0, 0.0, 0.0],
-                [0.5, 0.0, 0.0],
-                [1 - eta, nu, 0],
-                [0.5, 0.5, 0.0],
-                [eta, 1 - nu, 0.0],
-                [1.0, 0.0, 0.0],
-            ],
-            "labels": [GAMMA, "X", "H_1", "C", "H", GAMMA],
-        }
-        selected_paths["oblique"] = {
-            "path": [
-                [0.0, 0.0, 0.0],
-                [0.5, 0.0, 0.0],
-                [1 - eta, nu, 0],
-                [0.5, 0.5, 0.0],
-                [eta, 1 - nu, 0.0],
-                [0.0, 0.5, 0.0],
-                [1.0, 0.0, 0.0],
-            ],
-            "labels": [GAMMA, "X", "H_1", "C", "H", "Y", GAMMA],
-        }
-    path = selected_paths[kpath_2d]["path"]
-    labels = selected_paths[kpath_2d]["labels"]
-    branches = zip(path[:-1], path[1:])
-
-    all_kpoints = []  # List to hold all k-points
-    label_map = []  # List to hold labels and their corresponding k-point indices
-
-    # Calculate the number of points per branch and generate the kpoints
-    index_offset = 0  # Start index for each segment
-    for (start, end), label_start, _label_end in zip(branches, labels[:-1], labels[1:]):
-        num_points_per_branch = points_per_branch(
-            start, end, reciprocal_cell, bands_kpoints_distance
+def check_codes(pw_code, projwfc_code):
+    """Check that the codes are installed on the same computer."""
+    if (
+        not any(
+            [
+                pw_code is None,
+                projwfc_code is None,
+            ]
+        )
+        and len(
+            {
+                pw_code.computer.pk,
+                projwfc_code.computer.pk,
+            }
+        )
+        != 1
+    ):
+        raise ValueError(
+            "All selected codes must be installed on the same computer. This is because the "
+            "BandsWorkChain calculations rely on large files that are not retrieved by AiiDA."
         )
-        # Exclude endpoint except for the last segment to prevent duplication
-        points = np.linspace(start, end, num=num_points_per_branch, endpoint=False)
-        all_kpoints.extend(points)
-        label_map.append(
-            (index_offset, label_start)
-        )  # Label for the start of the segment
-        index_offset += len(points)
-
-    # Include the last point and its label
-    all_kpoints.append(path[-1])
-    label_map.append((index_offset, labels[-1]))  # Label for the last point
-
-    # Set the kpoints and their labels in KpointsData
-    kpoints.set_kpoints(all_kpoints)
-    kpoints.labels = label_map
-
-    return kpoints
 
 
 def update_resources(builder, codes):
-    set_component_resources(builder.scf.pw, codes.get("pw"))
-    set_component_resources(builder.bands.pw, codes.get("pw"))
+    if "bands" in builder:
+        set_component_resources(builder.bands.scf.pw, codes.get("pw"))
+        set_component_resources(builder.bands.bands.pw, codes.get("pw"))
+    elif "bands_projwfc" in builder:
+        set_component_resources(builder.bands_projwfc.scf.pw, codes.get("pw"))
+        set_component_resources(builder.bands_projwfc.bands.pw, codes.get("pw"))
+        set_component_resources(
+            builder.bands_projwfc.projwfc.projwfc, codes.get("projwfc_bands")
+        )
 
 
 def get_builder(codes, structure, parameters, **kwargs):
-    """Get a builder for the PwBandsWorkChain."""
+    """Get a builder for the BandsWorkChain."""
     from copy import deepcopy
 
     pw_code = codes.get("pw")["code"]
@@ -188,14 +56,25 @@ def get_builder(codes, structure, parameters, **kwargs):
     bands_overrides.pop("kpoints_distance", None)
     bands_overrides["pw"]["parameters"]["SYSTEM"].pop("smearing", None)
     bands_overrides["pw"]["parameters"]["SYSTEM"].pop("degauss", None)
+
+    check_codes(pw_code, codes.get("projwfc_bands")["code"])
+
     overrides = {
         "scf": scf_overrides,
         "bands": bands_overrides,
         "relax": relax_overrides,
     }
-    bands = PwBandsWorkChain.get_builder_from_protocol(
-        code=pw_code,
+
+    if parameters["bands"]["projwfc_bands"]:
+        simulation_mode = "fat_bands"
+    else:
+        simulation_mode = "normal"
+
+    bands_builder = BandsWorkChain.get_builder_from_protocol(
+        pw_code=pw_code,
+        projwfc_code=codes.get("projwfc_bands")["code"],
         structure=structure,
+        simulation_mode=simulation_mode,
         protocol=protocol,
         electronic_type=ElectronicType(parameters["workchain"]["electronic_type"]),
         spin_type=SpinType(parameters["workchain"]["spin_type"]),
@@ -203,44 +82,18 @@ def get_builder(codes, structure, parameters, **kwargs):
         overrides=overrides,
         **kwargs,
     )
+    update_resources(bands_builder, codes)
 
-    if structure.pbc != (True, True, True):
-        kpoints_distance = parameters["advanced"]["kpoints_distance"]
-        if structure.pbc == (True, False, False):
-            kpoints = generate_kpath_1d(structure, kpoints_distance)
-        elif structure.pbc == (True, True, False):
-            kpoints = generate_kpath_2d(
-                structure, kpoints_distance, parameters["bands"]["kpath_2d"]
-            )
-        bands.pop("bands_kpoints_distance")
-        bands.update({"bands_kpoints": kpoints})
-
-    # pop the inputs that are excluded from the expose_inputs
-    bands.pop("relax")
-    bands.pop("structure", None)
-    bands.pop("clean_workdir", None)
-    # update resources
-    update_resources(bands, codes)
-
-    if scf_overrides["pw"]["parameters"]["SYSTEM"].get("tot_magnetization") is not None:
-        bands.scf["pw"]["parameters"]["SYSTEM"].pop("starting_magnetization", None)
-        bands.bands["pw"]["parameters"]["SYSTEM"].pop("starting_magnetization", None)
-
-    return bands
+    return bands_builder
 
 
 def update_inputs(inputs, ctx):
     """Update the inputs using context."""
     inputs.structure = ctx.current_structure
-    inputs.scf.pw.parameters = inputs.scf.pw.parameters.get_dict()
-    if ctx.current_number_of_bands:
-        inputs.scf.pw.parameters.setdefault("SYSTEM", {}).setdefault(
-            "nbnd", ctx.current_number_of_bands
-        )
 
 
 workchain_and_builder = {
-    "workchain": PwBandsWorkChain,
+    "workchain": BandsWorkChain,
     "exclude": ("structure", "relax"),
     "get_builder": get_builder,
     "update_inputs": update_inputs,
diff --git a/src/aiidalab_qe/plugins/electronic_structure/result.py b/src/aiidalab_qe/plugins/electronic_structure/result.py
index 1142ae450..890e92c3a 100644
--- a/src/aiidalab_qe/plugins/electronic_structure/result.py
+++ b/src/aiidalab_qe/plugins/electronic_structure/result.py
@@ -19,10 +19,23 @@ def _update_view(self):
         except AttributeError:
             pdos_node = None
 
-        try:
-            bands_node = self.node.outputs.bands
-        except AttributeError:
-            bands_node = None
+        # Initialize bands_node to None by default
+        bands_node = None
+
+        # Check if the workchain has the 'bands' output
+        if hasattr(self.node.outputs, "bands"):
+            bands_output = self.node.outputs.bands
+
+            # Check for 'bands' or 'bands_projwfc' attributes within 'bands' output
+            if hasattr(bands_output, "bands"):
+                bands_node = bands_output.bands
+            elif hasattr(bands_output, "bands_projwfc"):
+                bands_node = bands_output.bands_projwfc
+            else:
+                # If neither 'bands' nor 'bands_projwfc' exist, use 'bands_output' itself
+                # This is the case for compatibility with older versions of the plugin
+                bands_node = bands_output
+
         _bands_dos_widget = BandPdosWidget(bands=bands_node, pdos=pdos_node)
         # update the electronic structure tab
         self.children = [_bands_dos_widget]
diff --git a/tests/conftest.py b/tests/conftest.py
index c65842d28..20d9bae4e 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -90,6 +90,14 @@ def _generate_structure_data(name="silicon", pbc=(True, True, True)):
 
             for site in sites:
                 structure.append_atom(position=site[2], symbols=site[0], name=site[1])
+
+        elif name == "MoS2":
+            cell = [[3.1922, 0, 0], [-1.5961, 2.7646, 0], [0, 0, 13.3783]]
+            structure = orm.StructureData(cell=cell)
+            structure.append_atom(position=(-0.0, 1.84, 10.03), symbols="Mo")
+            structure.append_atom(position=(1.6, 0.92, 8.47), symbols="S")
+            structure.append_atom(position=(1.6, 0.92, 11.6), symbols="S")
+
         structure.pbc = pbc
 
         return structure
@@ -289,6 +297,16 @@ def projwfc_code(aiida_local_code_factory):
     )
 
 
+@pytest.fixture
+def projwfc_bands_code(aiida_local_code_factory):
+    """Return a `Code` configured for the projwfc.x executable."""
+    return aiida_local_code_factory(
+        label="projwfc_bands",
+        executable="bash",
+        entry_point="quantumespresso.projwfc",
+    )
+
+
 @pytest.fixture()
 def workchain_settings_generator():
     """Return a function that generates a workchain settings dictionary."""
@@ -316,7 +334,7 @@ def _smearing_settings_generator(**kwargs):
 
 
 @pytest.fixture
-def app(pw_code, dos_code, projwfc_code):
+def app(pw_code, dos_code, projwfc_code, projwfc_bands_code):
     from aiidalab_qe.app.main import App
 
     # Since we use `qe_auto_setup=False`, which will skip the pseudo library installation
@@ -329,10 +347,12 @@ def app(pw_code, dos_code, projwfc_code):
     app.submit_step.pw_code.code_selection.refresh()
     app.submit_step.codes["dos"].code_selection.refresh()
     app.submit_step.codes["projwfc"].code_selection.refresh()
+    app.submit_step.codes["projwfc_bands"].code_selection.refresh()
 
     app.submit_step.pw_code.value = pw_code.uuid
     app.submit_step.codes["dos"].value = dos_code.uuid
     app.submit_step.codes["projwfc"].value = projwfc_code.uuid
+    app.submit_step.codes["projwfc_bands"].value = projwfc_bands_code.uuid
 
     yield app
 
@@ -558,17 +578,17 @@ def generate_bands_workchain(
     """Generate an instance of a the WorkChain."""
 
     def _generate_bands_workchain(structure):
-        from copy import deepcopy
-
         from aiida import engine
         from aiida.orm import Dict
-        from aiida_quantumespresso.workflows.pw.bands import PwBandsWorkChain
+        from aiidalab_qe.plugins.bands.bands_workchain import BandsWorkChain
 
         pseudo_family = f"SSSP/{SSSP_VERSION}/PBEsol/efficiency"
 
         inputs = {
-            "code": fixture_code("quantumespresso.pw"),
+            "pw_code": fixture_code("quantumespresso.pw"),
+            "projwfc_code": fixture_code("quantumespresso.projwfc"),
             "structure": structure,
+            "simulation_mode": "normal",
             "overrides": {
                 "scf": {
                     "pseudo_family": pseudo_family,
@@ -586,20 +606,31 @@ def _generate_bands_workchain(structure):
                 },
             },
         }
-        builder = PwBandsWorkChain.get_builder_from_protocol(**inputs)
+        builder = BandsWorkChain.get_builder_from_protocol(**inputs)
         inputs = builder._inputs()
-        inputs["relax"]["base_final_scf"] = deepcopy(inputs["relax"]["base"])
-        wkchain = generate_workchain(PwBandsWorkChain, inputs)
+        wkchain = generate_workchain(BandsWorkChain, inputs)
         wkchain.setup()
         # run bands and return the process
-        output_parameters = Dict(dict={"fermi_energy": 2.0})
-        output_parameters.store()
-        wkchain.out("scf_parameters", output_parameters)
-        wkchain.out("band_parameters", output_parameters)
+        fermi_dict = Dict(dict={"fermi_energy": 2.0})
+        fermi_dict.store()
+        output_parameters = {
+            "bands": {
+                "scf_parameters": fermi_dict,
+                "band_parameters": fermi_dict,
+            }
+        }
+
+        wkchain.out(
+            "bands.scf_parameters", output_parameters["bands"]["scf_parameters"]
+        )
+        wkchain.out(
+            "bands.band_parameters", output_parameters["bands"]["band_parameters"]
+        )
+
         #
         band_structure = generate_bands_data()
         band_structure.store()
-        wkchain.out("band_structure", band_structure)
+        wkchain.out("bands.band_structure", band_structure)
         wkchain.update_outputs()
         #
         bands_node = wkchain.node
@@ -616,6 +647,7 @@ def generate_qeapp_workchain(
     generate_workchain,
     generate_pdos_workchain,
     generate_bands_workchain,
+    fixture_code,
 ):
     """Generate an instance of the WorkChain."""
 
@@ -632,6 +664,7 @@ def _generate_qeapp_workchain(
     ):
         from copy import deepcopy
 
+        from aiida.orm import Dict
         from aiida.orm.utils.serialize import serialize
         from aiidalab_qe.app.configuration import ConfigureQeAppWorkChainStep
         from aiidalab_qe.app.submission import SubmitQeAppWorkChainStep
@@ -677,13 +710,37 @@ def _generate_qeapp_workchain(
         # step 3 setup code and resources
         s3: SubmitQeAppWorkChainStep = app.submit_step
         s3.pw_code.num_cpus.value = 4
+
         builder = s3._create_builder()
         inputs = builder._inputs()
         inputs["relax"]["base_final_scf"] = deepcopy(inputs["relax"]["base"])
+
+        # Setting up inputs for bands_projwfc
+        inputs["bands"]["bands_projwfc"]["scf"]["pw"] = deepcopy(
+            inputs["bands"]["bands"]["scf"]["pw"]
+        )
+        inputs["bands"]["bands_projwfc"]["bands"]["pw"] = deepcopy(
+            inputs["bands"]["bands"]["bands"]["pw"]
+        )
+        inputs["bands"]["bands_projwfc"]["bands"]["pw"]["code"] = inputs["bands"][
+            "bands"
+        ]["bands"]["pw"]["code"]
+        inputs["bands"]["bands_projwfc"]["scf"]["pw"]["code"] = inputs["bands"][
+            "bands"
+        ]["scf"]["pw"]["code"]
+
+        inputs["bands"]["bands_projwfc"]["projwfc"]["projwfc"]["code"] = fixture_code(
+            "quantumespresso.projwfc"
+        )
+        inputs["bands"]["bands_projwfc"]["projwfc"]["projwfc"]["parameters"] = Dict(
+            {"PROJWFC": {"DeltaE": 0.01}}
+        ).store()
+
         if run_bands:
             inputs["properties"].append("bands")
         if run_pdos:
             inputs["properties"].append("pdos")
+
         wkchain = generate_workchain(QeAppWorkChain, inputs)
         wkchain.setup()
         # mock output
@@ -697,11 +754,11 @@ def _generate_qeapp_workchain(
                 wkchain.exposed_outputs(pdos.node, PdosWorkChain, namespace="pdos")
             )
         if run_bands:
-            from aiida_quantumespresso.workflows.pw.bands import PwBandsWorkChain
+            from aiidalab_qe.plugins.bands.bands_workchain import BandsWorkChain
 
             bands = generate_bands_workchain(structure)
             wkchain.out_many(
-                wkchain.exposed_outputs(bands.node, PwBandsWorkChain, namespace="bands")
+                wkchain.exposed_outputs(bands.node, BandsWorkChain, namespace="bands")
             )
         wkchain.update_outputs()
         # set ui_parameters
diff --git a/tests/test_plugins_bands.py b/tests/test_plugins_bands.py
index aaa34225a..00e7a75c8 100644
--- a/tests/test_plugins_bands.py
+++ b/tests/test_plugins_bands.py
@@ -39,15 +39,22 @@ def test_result(generate_qeapp_workchain):
 def test_structure_1d(generate_qeapp_workchain, generate_structure_data):
     structure = generate_structure_data("silicon", pbc=(True, False, False))
     wkchain = generate_qeapp_workchain(structure=structure)
-    assert "bands_kpoints_distance" not in wkchain.inputs.bands
-    assert "bands_kpoints" in wkchain.inputs.bands
-    assert len(wkchain.inputs.bands.bands_kpoints.labels) == 2
+    assert "bands_kpoints_distance" not in wkchain.inputs.bands.bands
+    assert "bands_kpoints" in wkchain.inputs.bands.bands
+    assert len(wkchain.inputs.bands.bands.bands_kpoints.labels) == 2
+    assert wkchain.inputs.bands.bands.bands_kpoints.labels == [(0, "Γ"), (9, "X")]
 
 
 @pytest.mark.usefixtures("sssp")
 def test_structure_2d(generate_qeapp_workchain, generate_structure_data):
-    structure = generate_structure_data("silicon", pbc=(True, True, False))
+    structure = generate_structure_data("MoS2", pbc=(True, True, False))
     wkchain = generate_qeapp_workchain(structure=structure)
-    assert "bands_kpoints_distance" not in wkchain.inputs.bands
-    assert "bands_kpoints" in wkchain.inputs.bands
-    assert len(wkchain.inputs.bands.bands_kpoints.labels) == 4
+    assert "bands_kpoints_distance" not in wkchain.inputs.bands.bands
+    assert "bands_kpoints" in wkchain.inputs.bands.bands
+    assert len(wkchain.inputs.bands.bands.bands_kpoints.labels) == 4
+    assert wkchain.inputs.bands.bands.bands_kpoints.labels == [
+        (0, "Γ"),
+        (11, "M"),
+        (18, "K"),
+        (31, "Γ"),
+    ]
diff --git a/tests/test_submit_qe_workchain.py b/tests/test_submit_qe_workchain.py
index b21f1e38f..181554392 100644
--- a/tests/test_submit_qe_workchain.py
+++ b/tests/test_submit_qe_workchain.py
@@ -73,8 +73,11 @@ def test_create_builder_insulator(
     # check and validate the builder
     got = builder_to_readable_dict(builder)
 
-    assert got["bands"]["scf"]["pw"]["parameters"]["SYSTEM"]["occupations"] == "fixed"
-    assert "smearing" not in got["bands"]["scf"]["pw"]["parameters"]["SYSTEM"]
+    assert (
+        got["bands"]["bands"]["scf"]["pw"]["parameters"]["SYSTEM"]["occupations"]
+        == "fixed"
+    )
+    assert "smearing" not in got["bands"]["bands"]["scf"]["pw"]["parameters"]["SYSTEM"]
 
 
 @pytest.mark.usefixtures("sssp")
@@ -111,7 +114,7 @@ def test_create_builder_advanced_settings(
     # test tot_charge is updated in the three steps
     for parameters in [
         got["relax"]["base"],
-        got["bands"]["scf"],
+        got["bands"]["bands"]["scf"],
         got["pdos"]["scf"],
         got["pdos"]["nscf"],
     ]:
diff --git a/tests/test_submit_qe_workchain/test_create_builder_default.yml b/tests/test_submit_qe_workchain/test_create_builder_default.yml
index 4b9f604cc..3ad9d35b7 100644
--- a/tests/test_submit_qe_workchain/test_create_builder_default.yml
+++ b/tests/test_submit_qe_workchain/test_create_builder_default.yml
@@ -20,7 +20,7 @@ advanced:
         vdw_corr: none
     pseudos: {}
 bands:
-  kpath_2d: hexagonal
+  projwfc_bands: false
 codes:
   dos:
     cpus: 1
@@ -34,6 +34,12 @@ codes:
     max_wallclock_seconds: 43200
     nodes: 1
     ntasks_per_node: 1
+  projwfc_bands:
+    cpus: 1
+    cpus_per_task: 1
+    max_wallclock_seconds: 43200
+    nodes: 1
+    ntasks_per_node: 1
   pw:
     cpus: 2
     cpus_per_task: 1