Skip to content

Commit 861000d

Browse files
authored
Refactor/feb24 (#1249)
* dramatic refactorings courtesy of angel and codeclimate * refactor Ensemble.copy_back * add per-worker sim_dir test as non-extra * this may actually be a pydantic issue that autodoc-pydantic hasn't addressed yet * fixes model fields not rendering in docs; turns out pydantic.create_model doesn't play nicely with sphinx. other small fixes * undo pydantic version pin in requirements.txt * fix GenSpecs code sample
1 parent 96b44ed commit 861000d

8 files changed

Lines changed: 97 additions & 119 deletions

File tree

docs/data_structures/gen_specs.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Can be constructed and passed to libEnsemble as a Python class or a dictionary.
2323
2424
gen_specs = GenSpecs(
2525
gen_f=gen_random_sample,
26-
out=[("x", float, (1,))],
26+
outputs=[("x", float, (1,))],
2727
user={
2828
"lower": np.array([-3]),
2929
"upper": np.array([3]),

docs/data_structures/sim_specs.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Can be constructed and passed to libEnsemble as a Python class or a dictionary.
2323
sim_specs = SimSpecs(
2424
sim_f=sim_find_sine,
2525
inputs=["x"],
26-
out=[("y", float)],
26+
outputs=[("y", float)],
2727
user={"batch": 1234},
2828
)
2929
...

docs/nitpicky

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ py:class numpy.typing.NDArray
1313
py:class numpy._typing._generic_alias.ScalarType
1414
py:class numpy._typing._dtype_like._DTypeDict
1515
py:class numpy._typing._dtype_like._SupportsDType
16+
py:class numpy._typing._array_like._ScalarType_co
1617

1718
# Pending on python docs links issue #11975
1819
py:class list
@@ -34,6 +35,7 @@ py:class int
3435
py:class <class 'dict'>
3536
py:class <class 'int'>
3637
py:class +ScalarType
38+
py:class collections.abc.Sequence
3739

3840
# Internal paths that are verified importable but Sphinx can't find
3941
py:class libensemble.resources.platforms.Aurora

libensemble/history.py

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -55,27 +55,9 @@ def __init__(
5555
specs_dtype_list = list(set(libE_fields + sum([k.get("out", []) for k in specs if k], [])))
5656

5757
if len(H0):
58-
# a whole lot of work to parse numpy dtypes to python types and 2- or 3-tuples
59-
# - dtypes aren't iterable, but you can index into them
60-
# - must split out actual numpy type if subdtype refers to sub-array
61-
# - then convert that type into a python type in the best way known so far...
62-
# - we need to make sure the size of string types is preserved
63-
# - if sub-array shape, save as 3-tuple
64-
65-
H0_fields = []
66-
for i in range(len(H0.dtype.names)):
67-
dtype = H0.dtype[i]
68-
subd = dtype.subdtype[0] if dtype.subdtype else dtype
69-
pytype = type(subd.type(0).item()) # kinda redundant innit?
70-
size = int(dtype.str.split("<U")[-1]) if "<U" in dtype.str else dtype.shape
71-
if size:
72-
H0_fields.append((H0.dtype.names[i], pytype, size))
73-
else:
74-
H0_fields.append((H0.dtype.names[i], pytype))
75-
7658
# remove duplicate fields from specs dtype list if those already in H0 (H0 takes precedence)
7759
pruned_specs_dtype_list = [i for i in specs_dtype_list if i[0] not in H0.dtype.names]
78-
H_fields = list(set(pruned_specs_dtype_list + H0_fields))
60+
H_fields = list(set(pruned_specs_dtype_list + H0.dtype.descr))
7961

8062
H = np.zeros(L + len(H0), dtype=H_fields)
8163

libensemble/sim_funcs/executor_hworld.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def custom_polling_loop(exctr, task, timeout_sec=5.0, delay=0.3):
7171
@output_data([("f", float), ("cstat", int)])
7272
def executor_hworld(H, _, sim_specs, info):
7373
"""Tests launching and polling task and exiting on task finish"""
74+
7475
exctr = info["executor"]
7576
cores = sim_specs["user"]["cores"]
7677
ELAPSED_TIMEOUT = "elapsed_timeout" in sim_specs["user"]

libensemble/tests/functionality_tests/test_sim_dirs_per_worker.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
# Do not change these lines - they are parsed by run-tests.sh
1414
# TESTSUITE_COMMS: mpi local tcp
1515
# TESTSUITE_NPROCS: 2 4
16-
# TESTSUITE_EXTRA: true
1716

1817
import os
1918

libensemble/utils/output_directory.py

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -187,35 +187,33 @@ def prep_calc_dir(self, Work: dict, calc_iter: dict, workerID: int, calc_type: i
187187

188188
def copy_back(self) -> None:
189189
"""Copy back all ensemble dir contents to launch location"""
190-
if self.ensemble_dir.exists() and self.ensemble_copy_back and self.loc_stack:
191-
no_calc_dirs = not self.sim_dirs_make or not self.gen_dirs_make
192-
193-
for dire in self.loc_stack.dirs.values():
194-
dire = Path(dire)
195-
dest_path = self.copybackdir / Path(dire.stem)
196-
if dire == self.ensemble_dir: # occurs when no_calc_dirs is True
197-
continue # otherwise, entire ensemble dir copied into copyback dir
198-
190+
if not self.ensemble_dir.exists() or not self.ensemble_copy_back or not self.loc_stack:
191+
return
192+
193+
for dire in self.loc_stack.dirs.values():
194+
dire = Path(dire)
195+
dest_path = self.copybackdir / Path(dire.stem)
196+
if dire == self.ensemble_dir: # occurs when no_calc_dirs is True
197+
continue # otherwise, entire ensemble dir copied into copyback dir
198+
199+
if self.allow_overwrite:
200+
shutil.rmtree(dest_path, ignore_errors=True)
201+
shutil.copytree(dire, dest_path, symlinks=True, dirs_exist_ok=True)
202+
if dire.stem.startswith("worker"):
203+
return # Worker dir (with all contents) has been copied.
204+
205+
# If not using calc dirs, likely miscellaneous files to copy back
206+
if not self.sim_dirs_make or not self.gen_dirs_make:
207+
p = re.compile(r"((^sim)|(^gen))\d")
208+
for filep in [i for i in os.listdir(self.ensemble_dir) if not p.match(i)]: # each noncalc_dir file
209+
source_path = self.ensemble_dir / filep
210+
dest_path = self.copybackdir / filep
199211
if self.allow_overwrite:
200212
shutil.rmtree(dest_path, ignore_errors=True)
201-
shutil.copytree(dire, dest_path, symlinks=True, dirs_exist_ok=True)
202-
if dire.stem.startswith("worker"):
203-
return # Worker dir (with all contents) has been copied.
204-
205-
# If not using calc dirs, likely miscellaneous files to copy back
206-
if no_calc_dirs:
207-
p = re.compile(r"((^sim)|(^gen))\d")
208-
for filep in [i for i in os.listdir(self.ensemble_dir) if not p.match(i)]: # each noncalc_dir file
209-
source_path = os.path.join(self.ensemble_dir, filep)
210-
dest_path = os.path.join(self.copybackdir, filep)
211-
if self.allow_overwrite:
212-
shutil.rmtree(dest_path, ignore_errors=True)
213-
try:
214-
if os.path.isdir(source_path):
215-
shutil.copytree(source_path, dest_path, symlinks=True)
216-
else:
217-
shutil.copy(source_path, dest_path, follow_symlinks=False)
218-
except FileExistsError:
219-
continue
220-
except shutil.SameFileError: # creating an identical symlink
221-
continue
213+
try:
214+
if os.path.isdir(source_path):
215+
shutil.copytree(source_path, dest_path, symlinks=True)
216+
else:
217+
shutil.copy(source_path, dest_path, follow_symlinks=False)
218+
except (FileExistsError, shutil.SameFileError): # creating an identical symlink
219+
continue
Lines changed: 63 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import sys
2+
13
from pydantic import Field, create_model
24

35
from libensemble import specs
@@ -62,25 +64,16 @@ class Config:
6264
specs._EnsembleSpecs.model_config = model_config
6365
platforms.Platform.model_config = model_config
6466

65-
specs.SimSpecs.model_fields["inputs"] = FieldInfo.merge_field_infos(
66-
specs.SimSpecs.model_fields["inputs"], Field(alias="in")
67-
)
67+
model = specs.SimSpecs.model_fields
68+
model["inputs"] = FieldInfo.merge_field_infos(model["inputs"], Field(alias="in"))
69+
model["outputs"] = FieldInfo.merge_field_infos(model["outputs"], Field(alias="out"))
6870

69-
specs.SimSpecs.model_fields["outputs"] = FieldInfo.merge_field_infos(
70-
specs.SimSpecs.model_fields["outputs"], Field(alias="out")
71-
)
71+
model = specs.GenSpecs.model_fields
72+
model["inputs"] = FieldInfo.merge_field_infos(model["inputs"], Field(alias="in"))
73+
model["outputs"] = FieldInfo.merge_field_infos(model["outputs"], Field(alias="out"))
7274

73-
specs.GenSpecs.model_fields["inputs"] = FieldInfo.merge_field_infos(
74-
specs.GenSpecs.model_fields["inputs"], Field(alias="in")
75-
)
76-
77-
specs.GenSpecs.model_fields["outputs"] = FieldInfo.merge_field_infos(
78-
specs.GenSpecs.model_fields["outputs"], Field(alias="out")
79-
)
80-
81-
specs.AllocSpecs.model_fields["outputs"] = FieldInfo.merge_field_infos(
82-
specs.AllocSpecs.model_fields["outputs"], Field(alias="out")
83-
)
75+
model = specs.AllocSpecs.model_fields
76+
model["outputs"] = FieldInfo.merge_field_infos(model["outputs"], Field(alias="out"))
8477

8578
specs.SimSpecs.model_rebuild(force=True)
8679
specs.GenSpecs.model_rebuild(force=True)
@@ -90,57 +83,60 @@ class Config:
9083
specs._EnsembleSpecs.model_rebuild(force=True)
9184
platforms.Platform.model_rebuild(force=True)
9285

93-
specs.SimSpecs = create_model(
94-
"SimSpecs",
95-
__base__=specs.SimSpecs,
96-
__validators__={
97-
"check_valid_out": check_valid_out,
98-
"check_valid_in": check_valid_in,
99-
"simf_set_in_out_from_attrs": simf_set_in_out_from_attrs,
100-
},
101-
)
86+
# the create_model function removes fields for rendering in docs
87+
if "sphinx" not in sys.modules:
88+
89+
specs.SimSpecs = create_model(
90+
"SimSpecs",
91+
__base__=specs.SimSpecs,
92+
__validators__={
93+
"check_valid_out": check_valid_out,
94+
"check_valid_in": check_valid_in,
95+
"simf_set_in_out_from_attrs": simf_set_in_out_from_attrs,
96+
},
97+
)
10298

103-
specs.GenSpecs = create_model(
104-
"GenSpecs",
105-
__base__=specs.GenSpecs,
106-
__validators__={
107-
"check_valid_out": check_valid_out,
108-
"check_valid_in": check_valid_in,
109-
"genf_set_in_out_from_attrs": genf_set_in_out_from_attrs,
110-
},
111-
)
99+
specs.GenSpecs = create_model(
100+
"GenSpecs",
101+
__base__=specs.GenSpecs,
102+
__validators__={
103+
"check_valid_out": check_valid_out,
104+
"check_valid_in": check_valid_in,
105+
"genf_set_in_out_from_attrs": genf_set_in_out_from_attrs,
106+
},
107+
)
112108

113-
specs.LibeSpecs = create_model(
114-
"LibeSpecs",
115-
__base__=specs.LibeSpecs,
116-
__validators__={
117-
"check_valid_comms_type": check_valid_comms_type,
118-
"set_platform_specs_to_class": set_platform_specs_to_class,
119-
"check_input_dir_exists": check_input_dir_exists,
120-
"check_inputs_exist": check_inputs_exist,
121-
"check_any_workers_and_disable_rm_if_tcp": check_any_workers_and_disable_rm_if_tcp,
122-
"enable_save_H_when_every_K": enable_save_H_when_every_K,
123-
"set_workflow_dir": set_workflow_dir,
124-
},
125-
)
109+
specs.LibeSpecs = create_model(
110+
"LibeSpecs",
111+
__base__=specs.LibeSpecs,
112+
__validators__={
113+
"check_valid_comms_type": check_valid_comms_type,
114+
"set_platform_specs_to_class": set_platform_specs_to_class,
115+
"check_input_dir_exists": check_input_dir_exists,
116+
"check_inputs_exist": check_inputs_exist,
117+
"check_any_workers_and_disable_rm_if_tcp": check_any_workers_and_disable_rm_if_tcp,
118+
"enable_save_H_when_every_K": enable_save_H_when_every_K,
119+
"set_workflow_dir": set_workflow_dir,
120+
},
121+
)
126122

127-
specs._EnsembleSpecs = create_model(
128-
"_EnsembleSpecs",
129-
__base__=specs._EnsembleSpecs,
130-
__validators__={
131-
"check_exit_criteria": check_exit_criteria,
132-
"check_output_fields": check_output_fields,
133-
"check_H0": check_H0,
134-
"check_provided_ufuncs": check_provided_ufuncs,
135-
},
136-
)
123+
specs._EnsembleSpecs = create_model(
124+
"_EnsembleSpecs",
125+
__base__=specs._EnsembleSpecs,
126+
__validators__={
127+
"check_exit_criteria": check_exit_criteria,
128+
"check_output_fields": check_output_fields,
129+
"check_H0": check_H0,
130+
"check_provided_ufuncs": check_provided_ufuncs,
131+
},
132+
)
137133

138-
platforms.Platform = create_model(
139-
"Platform",
140-
__base__=platforms.Platform,
141-
__validators__={
142-
"check_gpu_setting_type": check_gpu_setting_type,
143-
"check_mpi_runner_type": check_mpi_runner_type,
144-
"check_logical_cores": check_logical_cores,
145-
},
146-
)
134+
platforms.Platform = create_model(
135+
"Platform",
136+
__base__=platforms.Platform,
137+
__validators__={
138+
"check_gpu_setting_type": check_gpu_setting_type,
139+
"check_mpi_runner_type": check_mpi_runner_type,
140+
"check_logical_cores": check_logical_cores,
141+
},
142+
)

0 commit comments

Comments
 (0)