Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
862a776
first round of refactoring runners.py, Runner base class for normal i…
jlnav Jan 8, 2024
e6874a6
refactoring classes so class attributes aren't passed around internal…
jlnav Jan 9, 2024
e17eabe
ThreadRunner uses comms.QCommThread, slightly modified, to launch its…
jlnav Jan 9, 2024
83493d0
handful of small changes from experimental/gen_on_manager_inplace
jlnav Jan 10, 2024
6ad870c
first incredibly long and ugly concatenation of "pipeline" and "state…
jlnav Jan 10, 2024
d14b0aa
progress
jlnav Jan 11, 2024
ab32e3f
bugfixes, first "working" refactor of manager can run 1d_sampling usi…
jlnav Jan 11, 2024
68d8855
removing now-redundant content from manager, trying to see if we can …
jlnav Jan 12, 2024
33ea282
restore version of manager from develop. specify iterations for worker.
jlnav Jan 17, 2024
843df39
remove pipelines.py. will start simpler
jlnav Jan 17, 2024
3aeab06
undoing "iterations" change in worker, seeing if we can simply submit…
jlnav Jan 17, 2024
b083a21
add attempted update_state_on_local_gen_msg and handle_msg_from_local…
jlnav Jan 17, 2024
231e2b7
use _Worker class to correctly index into W and wcomms. add initial o…
jlnav Jan 17, 2024
d251363
add "threaded" tentative option to sim/gen_specs
jlnav Jan 17, 2024
368bf93
fix ThreadRunner shutdown when that worker didn't launch a thread
jlnav Jan 17, 2024
744620d
adds test-case to functionality tests, fixes alloc_f libE_info usable…
jlnav Jan 18, 2024
ca14b7c
Merge branch 'develop' into refactor/user_function_handling_modules
jlnav Jan 18, 2024
cd6f0db
make resources reflect develop?
jlnav Jan 18, 2024
0952067
Merge branch 'develop' into refactor/user_function_handling_modules
jlnav Jan 22, 2024
884d61b
remove old symlink
jlnav Jan 22, 2024
dfb0fbb
print evaluated lines in check_libe_stats for now
jlnav Jan 22, 2024
ec236ed
only want to perform this specific datetime check on indexes 5 and 6 …
jlnav Jan 22, 2024
7b94467
Merge branch 'develop' into refactor/user_function_handling_modules
jlnav Jan 24, 2024
f06148a
a much simpler indexing solution from shuds
jlnav Jan 24, 2024
d584152
add comment for why using self.W.iterable in "for wrk in self.W.itera…
jlnav Jan 24, 2024
592c8c4
add __len__ and __iter__ to indexer
jlnav Jan 24, 2024
59ca40a
add __setitem__
jlnav Jan 24, 2024
d8a3a42
adjust alloc_support to not use w - 1 indexing
jlnav Jan 24, 2024
1839ff2
just pass in the iterable for now. resource changes coming in another…
jlnav Jan 24, 2024
43e98a9
Merge branch 'develop' into refactor/user_function_handling_modules
jlnav Jan 25, 2024
65fc121
Merge branch 'develop' into refactor/user_function_handling_modules
jlnav Feb 7, 2024
95badb1
Merge branch 'develop' into refactor/user_function_handling_modules
jlnav Feb 20, 2024
1fcf91f
Merge branch 'develop' into refactor/user_function_handling_modules
jlnav Feb 23, 2024
ad525bb
add tentative gen_on_manager option, separate additional_worker_launc…
jlnav Feb 23, 2024
fe64869
various refactors based on PR suggestions, then manager-refactors bas…
jlnav Feb 26, 2024
dcf6db7
fix persistent filter, update avail/running gens counters
jlnav Feb 26, 2024
ba05900
update unit test, bugfix
jlnav Feb 26, 2024
482ec15
update persistent allocs, but also add backwards-compatibility check …
jlnav Feb 26, 2024
3d06b1c
fix persistent sim test
jlnav Feb 26, 2024
9165d7d
move _WorkerIndexer into libensemble.utils, also use within Persisten…
jlnav Feb 26, 2024
f7ba205
manager also needs to send workflow_dir location to worker 0
jlnav Feb 26, 2024
376e450
missed an alloc
jlnav Feb 27, 2024
ac52a9f
Merge branch 'develop' into refactor/user_function_handling_modules
jlnav Feb 27, 2024
6375058
make alloc_f's libE_info additional worker option match libE_specs
jlnav Feb 27, 2024
c07a565
removes manager_runs_additional_worker in favor of gen_on_manager. pa…
jlnav Feb 28, 2024
c46802e
turning W["active"] back to an int
jlnav Feb 28, 2024
2ee9466
experimenting with gen_on_manager with give_pregenerated_work - worke…
jlnav Feb 28, 2024
9ebe767
I think for sim workers, the only requirement is that they're not gen…
jlnav Feb 28, 2024
09d030c
fixing alloc unit test based on passing wrapped W into alloc
jlnav Feb 28, 2024
2f631e0
refactoring Worker array fields to more closely match develop. worker…
jlnav Feb 29, 2024
ab39de6
fix tests
jlnav Mar 1, 2024
550ca1f
missed a revert in alloc
jlnav Mar 1, 2024
e7591b6
undo inconsequential tiny changes to allocs
jlnav Mar 1, 2024
68b991a
run each of the test_GPU_gen_resources tests also with the gen runnin…
jlnav Mar 1, 2024
c433ecb
simply gen_workers parameter description for avail_worker_ids
jlnav Mar 6, 2024
e78056b
debugging consecutive libE calls with gen_on_manager
jlnav Mar 8, 2024
f30233c
debugging......
jlnav Mar 8, 2024
6d0f9d2
cleaning up debugging, removing comm from Executor upon worker exiting
jlnav Mar 8, 2024
97c2c53
clarification comment
jlnav Mar 8, 2024
73d4b4c
bugfix
jlnav Mar 11, 2024
13fecde
filter for gen_workers within avail_worker_ids, if set and there are …
jlnav Mar 13, 2024
0bcfc79
refactor give_sim_work_first for running on gen_workers if no points_…
jlnav Mar 13, 2024
45cbd16
it turns out that values set by validators are still considered "unse…
jlnav Mar 13, 2024
2bc504c
starting to create unit test
jlnav Mar 13, 2024
aa4db8a
finish up unit test
jlnav Mar 14, 2024
429adb4
it turns out that values set by validators are still considered "unse…
jlnav Mar 13, 2024
b1f9108
starting to create unit test
jlnav Mar 13, 2024
6fa18ef
finish up unit test
jlnav Mar 14, 2024
dbdf88f
platform_specs sometimes seems to be at risk of disappearing when we …
jlnav Mar 14, 2024
eacf46f
Merge branch 'bugfix/ensemble_libE_specs_attrs_passthrough' into refa…
jlnav Mar 15, 2024
e4d4b08
refactor fast_alloc for gen workers
jlnav Mar 15, 2024
ffbe6c9
better test comment
jlnav Mar 15, 2024
14c8b1f
refactor inverse_bayes_allocf
jlnav Mar 15, 2024
45e99b2
trying to refcator only_one_gen_alloc, but currently doesnt pass test…
jlnav Mar 15, 2024
6f713dc
refactor aposmm alloc, move skip_cancled_points line
jlnav Mar 15, 2024
4aa386f
Update fast_alloc
shuds13 Mar 15, 2024
0b12af2
refactor start_fd_persistent
jlnav Mar 15, 2024
97cdfdb
refactor start_persistent_local_opt_gens
jlnav Mar 15, 2024
77f880e
typo
jmlarson1 Mar 16, 2024
2780f10
fast_alloc alloc_f: don't overwrite sim_worker with gen_work for a gi…
jlnav Mar 18, 2024
655a1ba
return Work after packing up gen work
jlnav Mar 18, 2024
15719c7
do next_to_give check within avail_worker_ids loop
jlnav Mar 18, 2024
3138a39
add libE_specs["gen_workers"] option, adjust ensure_one_active_gen so…
jlnav Mar 18, 2024
2093629
update give_pregenerated_work and start_only_persistent to only give …
jlnav Mar 19, 2024
8a50e60
refactor fast_alloc_and_pausing
jlnav Mar 19, 2024
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
5 changes: 4 additions & 1 deletion docs/data_structures/libE_specs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ libEnsemble is primarily customized by setting options within a ``LibeSpecs`` cl
Manager/Worker communications mode: ``'mpi'``, ``'local'``, or ``'tcp'``.

**nworkers** [int]:
Number of worker processes in ``"local"`` or ``"tcp"``.
Number of worker processes in ``"local"``, ``"threads"``, or ``"tcp"``.

**manager_runs_additional_worker** [int] = False
Manager process can launch an additional threaded worker

**mpi_comm** [MPI communicator] = ``MPI.COMM_WORLD``:
libEnsemble MPI communicator.
Expand Down
1 change: 0 additions & 1 deletion examples/calling_scripts/tutorial_calling.py

This file was deleted.

18 changes: 11 additions & 7 deletions libensemble/comms/comms.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,11 @@ def mail_flag(self):


class QCommLocal(Comm):
def __init__(self, main, nworkers, *args, **kwargs):
def __init__(self, main, *args, **kwargs):
self._result = None
self._exception = None
self._done = False
self._ufunc = kwargs.get("ufunc", False)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self._ufunc appears not to be used currently


def _is_result_msg(self, msg):
"""Return true if message indicates final result (and set result/except)."""
Expand Down Expand Up @@ -208,10 +209,13 @@ def result(self, timeout=None):
return self._result

@staticmethod
def _qcomm_main(comm, main, *args, **kwargs):
def _qcomm_main(comm, main, *fargs, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably keep as *args by convention

"""Main routine -- handles return values and exceptions."""
try:
_result = main(comm, *args, **kwargs)
if not kwargs.get("ufunc"):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call something like user_func to not confuse with ufunc for universal functions.

_result = main(comm, *fargs, **kwargs)
else:
_result = main(*fargs)
comm.send(CommResult(_result))
except Exception as e:
comm.send(CommResultErr(str(e), format_exc()))
Expand All @@ -233,12 +237,12 @@ def __exit__(self, etype, value, traceback):
class QCommThread(QCommLocal):
"""Launch a user function in a thread with an attached QComm."""

def __init__(self, main, nworkers, *args, **kwargs):
def __init__(self, main, nworkers, *fargs, **kwargs):
self.inbox = thread_queue.Queue()
self.outbox = thread_queue.Queue()
super().__init__(self, main, nworkers, *args, **kwargs)
super().__init__(self, main, *fargs, **kwargs)
comm = QComm(self.inbox, self.outbox, nworkers)
self.handle = Thread(target=QCommThread._qcomm_main, args=(comm, main) + args, kwargs=kwargs)
self.handle = Thread(target=QCommThread._qcomm_main, args=(comm, main) + fargs, kwargs=kwargs)

def terminate(self, timeout=None):
"""Terminate the thread.
Expand All @@ -260,7 +264,7 @@ class QCommProcess(QCommLocal):
def __init__(self, main, nworkers, *args, **kwargs):
self.inbox = Queue()
self.outbox = Queue()
super().__init__(self, main, nworkers, *args, **kwargs)
super().__init__(self, main, *args, **kwargs)
comm = QComm(self.inbox, self.outbox, nworkers)
self.handle = Process(target=QCommProcess._qcomm_main, args=(comm, main) + args, kwargs=kwargs)

Expand Down
2 changes: 1 addition & 1 deletion libensemble/executors/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ def set_workerID(self, workerid) -> None:
"""Sets the worker ID for this executor"""
self.workerID = workerid

def set_worker_info(self, comm, workerid=None) -> None:
def set_worker_info(self, comm=None, workerid=None) -> None:
"""Sets info for this executor"""
self.workerID = workerid
self.comm = comm
Expand Down
141 changes: 106 additions & 35 deletions libensemble/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
import numpy.typing as npt
from numpy.lib.recfunctions import repack_fields

from libensemble.comms.comms import CommFinishedException
from libensemble.comms.comms import CommFinishedException, QCommThread
from libensemble.executors.executor import Executor
from libensemble.message_numbers import (
EVAL_GEN_TAG,
EVAL_SIM_TAG,
Expand All @@ -37,7 +38,7 @@
from libensemble.utils.misc import extract_H_ranges
from libensemble.utils.output_directory import EnsembleDirectory
from libensemble.utils.timer import Timer
from libensemble.worker import WorkerErrMsg
from libensemble.worker import WorkerErrMsg, worker_main

logger = logging.getLogger(__name__)
# For debug messages - uncomment
Expand Down Expand Up @@ -154,6 +155,48 @@ def filter_nans(array: npt.NDArray) -> npt.NDArray:
"""


class _Worker:
"""Wrapper class for Worker array and worker comms"""

def __init__(self, W: npt.NDArray, wid: int, wcomms: list = []):
self.__dict__["_W"] = W
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How different from self._W = W

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I overloaded __getattr__, which is called for .attribute access even in the constructor. This is a workaround.

if 0 in W["worker_id"]: # Contains "0" for manager. Otherwise first entry is Worker 1
self.__dict__["_wididx"] = wid
else:
self.__dict__["_wididx"] = wid - 1
self.__dict__["_wcomms"] = wcomms

def __setattr__(self, field, value):
self._W[self._wididx][field] = value

def __getattr__(self, field):
return self._W[self._wididx][field]

def update_state_on_alloc(self, Work: dict):
self.active = Work["tag"]
if "persistent" in Work["libE_info"]:
self.persis_state = Work["tag"]
if Work["libE_info"].get("active_recv", False):
self.active_recv = Work["tag"]
else:
assert "active_recv" not in Work["libE_info"], "active_recv worker must also be persistent"

def update_persistent_state(self):
self.persis_state = 0
if self.active_recv:
self.active = 0
self.active_recv = 0

def send(self, tag, data):
self._wcomms[self._wididx].send(tag, data)

def mail_flag(self):
return self._wcomms[self._wididx].mail_flag()

def recv(self):
return self._wcomms[self._wididx].recv()


class Manager:
"""Manager class for libensemble."""

Expand Down Expand Up @@ -209,6 +252,30 @@ def __init__(
(1, "stop_val", self.term_test_stop_val),
]

if self.libE_specs.get("manager_runs_additional_worker", False):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think should be an "else" here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what would go in "else", though. We do extra setup for an additional worker, but there's nothing alternative to do otherwise.

Copy link
Member

@shuds13 shuds13 Feb 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lines that are always done above

        else:
            self.W = np.zeros(len(self.wcomms), dtype=Manager.worker_dtype)
            self.W["worker_id"] = np.arange(len(self.wcomms)) + 1


dtypes = {
EVAL_SIM_TAG: repack_fields(hist.H[sim_specs["in"]]).dtype,
EVAL_GEN_TAG: repack_fields(hist.H[gen_specs["in"]]).dtype,
}

self.W = np.zeros(len(self.wcomms) + 1, dtype=Manager.worker_dtype)
self.W["worker_id"] = np.arange(len(self.wcomms) + 1)
local_worker_comm = QCommThread(
worker_main,
len(self.wcomms),
sim_specs,
gen_specs,
libE_specs,
0,
False,
Resources.resources,
Executor.executor,
)
self.wcomms = [local_worker_comm] + self.wcomms
local_worker_comm.run()
local_worker_comm.send(0, dtypes)

temp_EnsembleDirectory = EnsembleDirectory(libE_specs=libE_specs)
self.resources = Resources.resources
self.scheduler_opts = self.libE_specs.get("scheduler_opts", {})
Expand Down Expand Up @@ -266,7 +333,8 @@ def term_test(self, logged: bool = True) -> Union[bool, int]:
def _kill_workers(self) -> None:
"""Kills the workers"""
for w in self.W["worker_id"]:
self.wcomms[w - 1].send(STOP_TAG, MAN_SIGNAL_FINISH)
worker = _Worker(self.W, w, self.wcomms)
worker.send(STOP_TAG, MAN_SIGNAL_FINISH)

# --- Checkpointing logic

Expand Down Expand Up @@ -320,15 +388,16 @@ def _init_every_k_save(self, complete=False) -> None:

def _check_work_order(self, Work: dict, w: int, force: bool = False) -> None:
"""Checks validity of an allocation function order"""
assert w != 0, "Can't send to worker 0; this is the manager."
if self.W[w - 1]["active_recv"]:
# assert w != 0, "Can't send to worker 0; this is the manager."
worker = _Worker(self.W, w, self.wcomms)
if worker.active_recv:
assert "active_recv" in Work["libE_info"], (
"Messages to a worker in active_recv mode should have active_recv"
f"set to True in libE_info. Work['libE_info'] is {Work['libE_info']}"
)
else:
if not force:
assert self.W[w - 1]["active"] == 0, (
assert worker.active == 0, (
"Allocation function requested work be sent to worker %d, an already active worker." % w
)
work_rows = Work["libE_info"]["H_rows"]
Expand Down Expand Up @@ -370,13 +439,15 @@ def _send_work_order(self, Work: dict, w: int) -> None:
"""Sends an allocation function order to a worker"""
logger.debug(f"Manager sending work unit to worker {w}")

worker = _Worker(self.W, w, self.wcomms)

if self.resources:
self._set_resources(Work, w)

self.wcomms[w - 1].send(Work["tag"], Work)
worker.send(Work["tag"], Work)

if Work["tag"] == EVAL_GEN_TAG:
self.W[w - 1]["gen_started_time"] = time.time()
worker.gen_started_time = time.time()

work_rows = Work["libE_info"]["H_rows"]
work_name = calc_type_strings[Work["tag"]]
Expand All @@ -386,18 +457,14 @@ def _send_work_order(self, Work: dict, w: int) -> None:
H_to_be_sent = np.empty(len(work_rows), dtype=new_dtype)
for i, row in enumerate(work_rows):
H_to_be_sent[i] = repack_fields(self.hist.H[Work["H_fields"]][row])
self.wcomms[w - 1].send(0, H_to_be_sent)

worker.send(0, H_to_be_sent)

def _update_state_on_alloc(self, Work: dict, w: int):
"""Updates a workers' active/idle status following an allocation order"""
self.W[w - 1]["active"] = Work["tag"]
if "libE_info" in Work:
if "persistent" in Work["libE_info"]:
self.W[w - 1]["persis_state"] = Work["tag"]
if Work["libE_info"].get("active_recv", False):
self.W[w - 1]["active_recv"] = Work["tag"]
else:
assert "active_recv" not in Work["libE_info"], "active_recv worker must also be persistent"

worker = _Worker(self.W, w, self.wcomms)
worker.update_state_on_alloc(Work)

work_rows = Work["libE_info"]["H_rows"]
if Work["tag"] == EVAL_SIM_TAG:
Expand Down Expand Up @@ -432,7 +499,8 @@ def _receive_from_workers(self, persis_info: dict) -> dict:
while new_stuff:
new_stuff = False
for w in self.W["worker_id"]:
if self.wcomms[w - 1].mail_flag():
worker = _Worker(self.W, w, self.wcomms)
if worker.mail_flag():
new_stuff = True
self._handle_msg_from_worker(persis_info, w)

Expand All @@ -445,38 +513,37 @@ def _update_state_on_worker_msg(self, persis_info: dict, D_recv: dict, w: int) -
calc_status = D_recv["calc_status"]
Manager._check_received_calc(D_recv)

worker = _Worker(self.W, w, self.wcomms)

keep_state = D_recv["libE_info"].get("keep_state", False)
if w not in self.persis_pending and not self.W[w - 1]["active_recv"] and not keep_state:
self.W[w - 1]["active"] = 0
if w not in self.persis_pending and not worker.active_recv and not keep_state:
worker.active = 0

if calc_status in [FINISHED_PERSISTENT_SIM_TAG, FINISHED_PERSISTENT_GEN_TAG]:
final_data = D_recv.get("calc_out", None)
if isinstance(final_data, np.ndarray):
if calc_status is FINISHED_PERSISTENT_GEN_TAG and self.libE_specs.get("use_persis_return_gen", False):
self.hist.update_history_x_in(w, final_data, self.W[w - 1]["gen_started_time"])
self.hist.update_history_x_in(w, final_data, worker.gen_started_time)
elif calc_status is FINISHED_PERSISTENT_SIM_TAG and self.libE_specs.get("use_persis_return_sim", False):
self.hist.update_history_f(D_recv, self.kill_canceled_sims)
else:
logger.info(_PERSIS_RETURN_WARNING)
self.W[w - 1]["persis_state"] = 0
if self.W[w - 1]["active_recv"]:
self.W[w - 1]["active"] = 0
self.W[w - 1]["active_recv"] = 0
worker.update_persistent_state()
if w in self.persis_pending:
self.persis_pending.remove(w)
self.W[w - 1]["active"] = 0
worker.active = 0
self._freeup_resources(w)
else:
if calc_type == EVAL_SIM_TAG:
self.hist.update_history_f(D_recv, self.kill_canceled_sims)
if calc_type == EVAL_GEN_TAG:
self.hist.update_history_x_in(w, D_recv["calc_out"], self.W[w - 1]["gen_started_time"])
self.hist.update_history_x_in(w, D_recv["calc_out"], worker.gen_started_time)
assert (
len(D_recv["calc_out"]) or np.any(self.W["active"]) or self.W[w - 1]["persis_state"]
len(D_recv["calc_out"]) or np.any(self.W["active"]) or worker.persis_state
), "Gen must return work when is is the only thing active and not persistent."
if "libE_info" in D_recv and "persistent" in D_recv["libE_info"]:
# Now a waiting, persistent worker
self.W[w - 1]["persis_state"] = calc_type
worker.persis_state = calc_type
else:
self._freeup_resources(w)

Expand All @@ -485,14 +552,15 @@ def _update_state_on_worker_msg(self, persis_info: dict, D_recv: dict, w: int) -

def _handle_msg_from_worker(self, persis_info: dict, w: int) -> None:
"""Handles a message from worker w"""
worker = _Worker(self.W, w, self.wcomms)
try:
msg = self.wcomms[w - 1].recv()
msg = worker.recv()
tag, D_recv = msg
except CommFinishedException:
logger.debug(f"Finalizing message from Worker {w}")
return
if isinstance(D_recv, WorkerErrMsg):
self.W[w - 1]["active"] = 0
worker.active = 0
logger.debug(f"Manager received exception from worker {w}")
if not self.WorkerExc:
self.WorkerExc = True
Expand Down Expand Up @@ -525,7 +593,8 @@ def _kill_cancelled_sims(self) -> None:
kill_ids = self.hist.H["sim_id"][kill_sim_rows]
kill_on_workers = self.hist.H["sim_worker"][kill_sim_rows]
for w in kill_on_workers:
self.wcomms[w - 1].send(STOP_TAG, MAN_SIGNAL_KILL)
worker = _Worker(self.W, w, self.wcomms)
worker.send(STOP_TAG, MAN_SIGNAL_KILL)
self.hist.H["kill_sent"][kill_ids] = True

# --- Handle termination
Expand All @@ -542,6 +611,7 @@ def _final_receive_and_kill(self, persis_info: dict) -> (dict, int, int):
# Send a handshake signal to each persistent worker.
if any(self.W["persis_state"]):
for w in self.W["worker_id"][self.W["persis_state"] > 0]:
worker = _Worker(self.W, w, self.wcomms)
logger.debug(f"Manager sending PERSIS_STOP to worker {w}")
if self.libE_specs.get("final_gen_send", False):
rows_to_send = np.where(self.hist.H["sim_ended"] & ~self.hist.H["gen_informed"])[0]
Expand All @@ -555,10 +625,10 @@ def _final_receive_and_kill(self, persis_info: dict) -> (dict, int, int):
self._send_work_order(work, w)
self.hist.update_history_to_gen(rows_to_send)
else:
self.wcomms[w - 1].send(PERSIS_STOP, MAN_SIGNAL_KILL)
if not self.W[w - 1]["active"]:
worker.send(PERSIS_STOP, MAN_SIGNAL_KILL)
if not worker.active:
# Re-activate if necessary
self.W[w - 1]["active"] = self.W[w - 1]["persis_state"]
worker.active = worker.persis_state
self.persis_pending.append(w)

exit_flag = 0
Expand Down Expand Up @@ -601,6 +671,7 @@ def _get_alloc_libE_info(self) -> dict:
"use_resource_sets": self.use_resource_sets,
"gen_num_procs": self.gen_num_procs,
"gen_num_gpus": self.gen_num_gpus,
"manager_additional_worker": self.libE_specs.get("manager_runs_additional_worker", False),
}

def _alloc_work(self, H: npt.NDArray, persis_info: dict) -> dict:
Expand Down
Loading