Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions dask_jobqueue/lsf.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def __init__(
memory_string = lsf_format_bytes_ceil(mem, lsf_units=lsf_units)
header_lines.append("#BSUB -M %s" % memory_string)
if walltime is not None:
header_lines.append("#BSUB -W %s" % walltime)
header_lines.append("#BSUB -W %s" % lsf_format_walltime(walltime))

# Skip requested header directives
header_lines = list(
Expand Down Expand Up @@ -136,6 +136,37 @@ def lsf_format_bytes_ceil(n, lsf_units="mb"):
return "%d" % math.ceil(n / (1000 ** converter[lsf_units]))


def lsf_format_walltime(walltime):
"""Format a walltime as ``[hour:]minute``, which is what LSF requires

The ``HH:MM:SS`` format accepted by the other job schedulers is converted,
rounding any leftover seconds up to the next minute so that the job is
never given less time than requested. Any other value is returned
unchanged.

Parameters
----------
walltime: str
Walltime of the job

Examples
--------
>>> lsf_format_walltime("01:30:00")
'01:30'
>>> lsf_format_walltime("00:00:30")
'00:01'
>>> lsf_format_walltime("01:30")
'01:30'
"""
parts = str(walltime).split(":")
if len(parts) != 3 or not all(part.isdigit() for part in parts):
return walltime
hours, minutes, seconds = (int(part) for part in parts)
total_minutes = hours * 60 + minutes + math.ceil(seconds / 60)
hours, minutes = divmod(total_minutes, 60)
return "%02d:%02d" % (hours, minutes)


def lsf_detect_units():
"""Try to autodetect the unit scaling on an LSF system"""
# Search for automatically, Using docs from LSF 9.1.3 for search/defaults
Expand Down Expand Up @@ -188,7 +219,8 @@ class LSFCluster(JobQueueCluster):
mem : int
Request memory in bytes. Passed to `#BSUB -M` option.
walltime : str
Walltime for each worker job in HH:MM. Passed to `#BSUB -W` option.
Walltime for each worker job in HH:MM. A walltime given as HH:MM:SS is
converted to HH:MM, rounding up. Passed to `#BSUB -W` option.
{cluster}
job_extra : list
Deprecated: use ``job_extra_directives`` instead. This parameter will be removed in a future version.
Expand Down
21 changes: 21 additions & 0 deletions dask_jobqueue/tests/test_lsf.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,27 @@ def test_header():
assert "#BSUB -P" not in cluster.job_header


@pytest.mark.parametrize(
"walltime, expected",
[
("01:00:00", "01:00"),
("00:30:00", "00:30"),
("2:15:00", "02:15"),
("00:00:30", "00:01"),
("01:59:01", "02:00"),
("48:00:00", "48:00"),
("01:00", "01:00"),
("1:00", "1:00"),
("90", "90"),
],
)
def test_walltime_format(walltime, expected):
assert lsf.lsf_format_walltime(walltime) == expected

with LSFCluster(walltime=walltime, cores=1, memory="1GB") as cluster:
assert "#BSUB -W %s" % expected in cluster.job_header.splitlines()


def test_job_script():
with LSFCluster(walltime="00:02", processes=4, cores=8, memory="28GB") as cluster:
job_script = cluster.job_script()
Expand Down
2 changes: 2 additions & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Changelog
Development version
-------------------

- Fixed ``LSFCluster`` generating an invalid ``#BSUB -W`` directive for a walltime in ``HH:MM:SS`` format (:issue:`703`)

0.9.0 / 2024-08-22
------------------

Expand Down