hpcflow-new2 0.2.0a163__py3-none-any.whl → 0.2.0a166__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hpcflow/_version.py +1 -1
- hpcflow/sdk/core/actions.py +24 -7
- hpcflow/sdk/core/commands.py +14 -2
- hpcflow/sdk/core/errors.py +16 -0
- hpcflow/sdk/core/parameters.py +26 -2
- hpcflow/sdk/core/task.py +209 -59
- hpcflow/sdk/core/task_schema.py +1 -1
- hpcflow/sdk/persistence/base.py +6 -0
- hpcflow/sdk/persistence/zarr.py +2 -0
- hpcflow/tests/unit/test_action.py +170 -0
- hpcflow/tests/unit/test_element_set.py +10 -0
- hpcflow/tests/unit/test_input_source.py +601 -1
- hpcflow/tests/unit/test_persistence.py +4 -1
- hpcflow/tests/unit/test_task.py +1 -1
- {hpcflow_new2-0.2.0a163.dist-info → hpcflow_new2-0.2.0a166.dist-info}/METADATA +1 -1
- {hpcflow_new2-0.2.0a163.dist-info → hpcflow_new2-0.2.0a166.dist-info}/RECORD +18 -18
- {hpcflow_new2-0.2.0a163.dist-info → hpcflow_new2-0.2.0a166.dist-info}/WHEEL +0 -0
- {hpcflow_new2-0.2.0a163.dist-info → hpcflow_new2-0.2.0a166.dist-info}/entry_points.txt +0 -0
@@ -1,10 +1,16 @@
|
|
1
1
|
import numpy as np
|
2
2
|
import pytest
|
3
3
|
from hpcflow.app import app as hf
|
4
|
-
from hpcflow.sdk.core.errors import
|
4
|
+
from hpcflow.sdk.core.errors import (
|
5
|
+
InapplicableInputSourceElementIters,
|
6
|
+
MissingInputs,
|
7
|
+
NoCoincidentInputSources,
|
8
|
+
UnavailableInputSource,
|
9
|
+
)
|
5
10
|
from hpcflow.sdk.core.test_utils import (
|
6
11
|
P1_parameter_cls as P1,
|
7
12
|
P1_sub_parameter_cls as P1_sub,
|
13
|
+
make_schemas,
|
8
14
|
)
|
9
15
|
|
10
16
|
|
@@ -660,3 +666,597 @@ def test_sub_parameter_task_input_source_allowed_when_root_parameter_is_task_out
|
|
660
666
|
],
|
661
667
|
"p2": [hf.InputSource.local()],
|
662
668
|
}
|
669
|
+
|
670
|
+
|
671
|
+
def test_raise_unavailable_input_source(null_config, tmp_path):
|
672
|
+
t1 = hf.Task(schema=hf.task_schemas.test_t1_ps, inputs={"p1": 1})
|
673
|
+
t2 = hf.Task(
|
674
|
+
schema=hf.task_schemas.test_t1_ps,
|
675
|
+
input_sources={"p1": [hf.InputSource.local()]},
|
676
|
+
)
|
677
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
678
|
+
with pytest.raises(UnavailableInputSource):
|
679
|
+
hf.Workflow.from_template(wkt, path=tmp_path)
|
680
|
+
|
681
|
+
|
682
|
+
def test_input_source_specify_element_iters(null_config, tmp_path):
|
683
|
+
t1 = hf.Task(
|
684
|
+
schema=hf.task_schemas.test_t1_ps,
|
685
|
+
sequences=[
|
686
|
+
hf.ValueSequence(
|
687
|
+
path="inputs.p1",
|
688
|
+
values=[{"a": 1}, {"a": 2}, {"a": 3}],
|
689
|
+
),
|
690
|
+
],
|
691
|
+
)
|
692
|
+
t2 = hf.Task(
|
693
|
+
schema=hf.task_schemas.test_t1_ps,
|
694
|
+
input_sources={
|
695
|
+
"p1": [
|
696
|
+
hf.InputSource.task(
|
697
|
+
task_ref=0, task_source_type="input", element_iters=[0, 2]
|
698
|
+
)
|
699
|
+
]
|
700
|
+
},
|
701
|
+
)
|
702
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
703
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
704
|
+
assert len(wk.tasks[1].elements) == 2
|
705
|
+
assert [i.value["a"] for i in wk.tasks[1].inputs.p1] == [1, 3]
|
706
|
+
|
707
|
+
|
708
|
+
def test_input_source_raise_on_inapplicable_specified_element_iters(
|
709
|
+
null_config, tmp_path
|
710
|
+
):
|
711
|
+
t1 = hf.Task(
|
712
|
+
schema=hf.task_schemas.test_t1_ps,
|
713
|
+
sequences=[
|
714
|
+
hf.ValueSequence(
|
715
|
+
path="inputs.p1",
|
716
|
+
values=[{"a": 1}, {"a": 2}, {"a": 3}],
|
717
|
+
),
|
718
|
+
],
|
719
|
+
)
|
720
|
+
t2 = hf.Task(
|
721
|
+
schema=hf.task_schemas.test_t1_ps,
|
722
|
+
input_sources={
|
723
|
+
"p1": [
|
724
|
+
hf.InputSource.task(
|
725
|
+
task_ref=0, task_source_type="input", element_iters=[0, 4]
|
726
|
+
)
|
727
|
+
]
|
728
|
+
},
|
729
|
+
)
|
730
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
731
|
+
with pytest.raises(InapplicableInputSourceElementIters):
|
732
|
+
hf.Workflow.from_template(wkt, path=tmp_path)
|
733
|
+
|
734
|
+
|
735
|
+
def test_input_source_specify_element_iters_and_where(null_config, tmp_path):
|
736
|
+
"""Test the where argument further filters the element_iters argument."""
|
737
|
+
t1 = hf.Task(
|
738
|
+
schema=hf.task_schemas.test_t1_ps,
|
739
|
+
sequences=[
|
740
|
+
hf.ValueSequence(
|
741
|
+
path="inputs.p1",
|
742
|
+
values=[{"a": 1}, {"a": 2}, {"a": 3}],
|
743
|
+
),
|
744
|
+
],
|
745
|
+
)
|
746
|
+
t2 = hf.Task(
|
747
|
+
schema=hf.task_schemas.test_t1_ps,
|
748
|
+
input_sources={
|
749
|
+
"p1": [
|
750
|
+
hf.InputSource.task(
|
751
|
+
task_ref=0,
|
752
|
+
task_source_type="input",
|
753
|
+
element_iters=[0, 2],
|
754
|
+
where=hf.Rule(path="inputs.p1.a", condition={"value.equal_to": 3}),
|
755
|
+
)
|
756
|
+
]
|
757
|
+
},
|
758
|
+
)
|
759
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
760
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
761
|
+
assert len(wk.tasks[1].elements) == 1
|
762
|
+
assert [i.value["a"] for i in wk.tasks[1].inputs.p1] == [3]
|
763
|
+
|
764
|
+
|
765
|
+
def test_element_iters_order_with_allow_non_coincident_task_sources_False(
|
766
|
+
null_config, tmp_path
|
767
|
+
):
|
768
|
+
t1 = hf.Task(
|
769
|
+
schema=hf.task_schemas.test_t1_ps,
|
770
|
+
sequences=[
|
771
|
+
hf.ValueSequence(
|
772
|
+
path="inputs.p1",
|
773
|
+
values=[11, 12, 13],
|
774
|
+
),
|
775
|
+
],
|
776
|
+
)
|
777
|
+
t2 = hf.Task(
|
778
|
+
schema=hf.task_schemas.test_t1_ps,
|
779
|
+
input_sources={
|
780
|
+
"p1": [
|
781
|
+
hf.InputSource.task(
|
782
|
+
task_ref=0, task_source_type="input", element_iters=[2, 0, 1]
|
783
|
+
)
|
784
|
+
],
|
785
|
+
},
|
786
|
+
allow_non_coincident_task_sources=False,
|
787
|
+
)
|
788
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
789
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
790
|
+
|
791
|
+
assert len(wk.tasks[1].elements) == 3
|
792
|
+
assert [i.value for i in wk.tasks[1].inputs.p1] == [13, 11, 12]
|
793
|
+
|
794
|
+
|
795
|
+
def test_element_iters_order_with_allow_non_coincident_task_sources_True(
|
796
|
+
null_config, tmp_path
|
797
|
+
):
|
798
|
+
t1 = hf.Task(
|
799
|
+
schema=hf.task_schemas.test_t1_ps,
|
800
|
+
sequences=[
|
801
|
+
hf.ValueSequence(
|
802
|
+
path="inputs.p1",
|
803
|
+
values=[11, 12, 13],
|
804
|
+
),
|
805
|
+
],
|
806
|
+
)
|
807
|
+
t2 = hf.Task(
|
808
|
+
schema=hf.task_schemas.test_t1_ps,
|
809
|
+
input_sources={
|
810
|
+
"p1": [
|
811
|
+
hf.InputSource.task(
|
812
|
+
task_ref=0, task_source_type="input", element_iters=[2, 0, 1]
|
813
|
+
)
|
814
|
+
],
|
815
|
+
},
|
816
|
+
allow_non_coincident_task_sources=True,
|
817
|
+
)
|
818
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
819
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
820
|
+
|
821
|
+
assert len(wk.tasks[1].elements) == 3
|
822
|
+
assert [i.value for i in wk.tasks[1].inputs.p1] == [13, 11, 12]
|
823
|
+
|
824
|
+
|
825
|
+
def test_element_iters_order_with_allow_non_coincident_task_sources_True_multiple_sources(
|
826
|
+
null_config, tmp_path
|
827
|
+
):
|
828
|
+
"""Test no-reordering of specified element iterations of sources from the same task."""
|
829
|
+
s1 = make_schemas([[{"p1": None, "p2": None}, ("p3",), "t1"]])
|
830
|
+
|
831
|
+
t1 = hf.Task(
|
832
|
+
schema=s1,
|
833
|
+
sequences=[
|
834
|
+
hf.ValueSequence(
|
835
|
+
path="inputs.p1",
|
836
|
+
values=[11, 12, 13],
|
837
|
+
),
|
838
|
+
hf.ValueSequence(
|
839
|
+
path="inputs.p2",
|
840
|
+
values=[21, 22, 23],
|
841
|
+
),
|
842
|
+
],
|
843
|
+
)
|
844
|
+
t2 = hf.Task(
|
845
|
+
schema=s1,
|
846
|
+
input_sources={
|
847
|
+
"p1": [
|
848
|
+
hf.InputSource.task(
|
849
|
+
task_ref=0, task_source_type="input", element_iters=[0, 1]
|
850
|
+
)
|
851
|
+
],
|
852
|
+
"p2": [
|
853
|
+
hf.InputSource.task(
|
854
|
+
task_ref=0, task_source_type="input", element_iters=[1, 0]
|
855
|
+
)
|
856
|
+
],
|
857
|
+
},
|
858
|
+
allow_non_coincident_task_sources=True,
|
859
|
+
)
|
860
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
861
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
862
|
+
|
863
|
+
assert len(wk.tasks[1].elements) == 2
|
864
|
+
assert [i.value for i in wk.tasks[1].inputs.p1] == [11, 12]
|
865
|
+
assert [i.value for i in wk.tasks[1].inputs.p2] == [22, 21]
|
866
|
+
|
867
|
+
|
868
|
+
def test_element_iters_order_with_allow_non_coincident_task_sources_False_multiple_sources(
|
869
|
+
null_config, tmp_path
|
870
|
+
):
|
871
|
+
"""Test reordering of specified element iterations of sources from the same task."""
|
872
|
+
s1 = make_schemas([[{"p1": None, "p2": None}, ("p3",), "t1"]])
|
873
|
+
|
874
|
+
t1 = hf.Task(
|
875
|
+
schema=s1,
|
876
|
+
sequences=[
|
877
|
+
hf.ValueSequence(
|
878
|
+
path="inputs.p1",
|
879
|
+
values=[11, 12, 13],
|
880
|
+
),
|
881
|
+
hf.ValueSequence(
|
882
|
+
path="inputs.p2",
|
883
|
+
values=[21, 22, 23],
|
884
|
+
),
|
885
|
+
],
|
886
|
+
)
|
887
|
+
t2 = hf.Task(
|
888
|
+
schema=s1,
|
889
|
+
input_sources={
|
890
|
+
"p1": [
|
891
|
+
hf.InputSource.task(
|
892
|
+
task_ref=0, task_source_type="input", element_iters=[0, 1]
|
893
|
+
)
|
894
|
+
],
|
895
|
+
"p2": [
|
896
|
+
hf.InputSource.task(
|
897
|
+
task_ref=0, task_source_type="input", element_iters=[1, 0]
|
898
|
+
)
|
899
|
+
],
|
900
|
+
},
|
901
|
+
allow_non_coincident_task_sources=False,
|
902
|
+
)
|
903
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
904
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
905
|
+
|
906
|
+
assert len(wk.tasks[1].elements) == 2
|
907
|
+
assert [i.value for i in wk.tasks[1].inputs.p1] == [11, 12]
|
908
|
+
assert [i.value for i in wk.tasks[1].inputs.p2] == [21, 22]
|
909
|
+
|
910
|
+
|
911
|
+
def test_not_allow_non_coincident_task_sources(null_config, tmp_path):
|
912
|
+
"""Test only one coincident element from the two input sources"""
|
913
|
+
s1 = make_schemas([[{"p1": None, "p2": None}, ("p3",), "t1"]])
|
914
|
+
t1 = hf.Task(
|
915
|
+
schema=s1,
|
916
|
+
inputs={"p1": 1},
|
917
|
+
sequences=[
|
918
|
+
hf.ValueSequence(path="inputs.p2", values=[21, 22, 23]),
|
919
|
+
],
|
920
|
+
)
|
921
|
+
t2 = hf.Task(
|
922
|
+
schema=s1,
|
923
|
+
input_sources={
|
924
|
+
"p1": [
|
925
|
+
hf.InputSource.task(
|
926
|
+
task_ref=0, task_source_type="input", element_iters=[0, 1]
|
927
|
+
)
|
928
|
+
],
|
929
|
+
"p2": [
|
930
|
+
hf.InputSource.task(
|
931
|
+
task_ref=0, task_source_type="input", element_iters=[1, 2]
|
932
|
+
)
|
933
|
+
],
|
934
|
+
},
|
935
|
+
allow_non_coincident_task_sources=False,
|
936
|
+
)
|
937
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
938
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
939
|
+
|
940
|
+
assert len(wk.tasks[1].elements) == 1
|
941
|
+
assert [i.value for i in wk.tasks[1].inputs.p2] == [22]
|
942
|
+
|
943
|
+
|
944
|
+
def test_allow_non_coincident_task_sources(null_config, tmp_path):
|
945
|
+
"""Test can combine inputs from non-coincident element iterations of the same task."""
|
946
|
+
s1 = make_schemas([[{"p1": None, "p2": None}, ("p3",), "t1"]])
|
947
|
+
t1 = hf.Task(
|
948
|
+
schema=s1,
|
949
|
+
sequences=[
|
950
|
+
hf.ValueSequence(
|
951
|
+
path="inputs.p1",
|
952
|
+
values=[11, 12, 13],
|
953
|
+
),
|
954
|
+
hf.ValueSequence(
|
955
|
+
path="inputs.p2",
|
956
|
+
values=[21, 22, 23],
|
957
|
+
),
|
958
|
+
],
|
959
|
+
)
|
960
|
+
t2 = hf.Task(
|
961
|
+
schema=s1,
|
962
|
+
input_sources={
|
963
|
+
"p1": [
|
964
|
+
hf.InputSource.task(
|
965
|
+
task_ref=0, task_source_type="input", element_iters=[0, 1]
|
966
|
+
)
|
967
|
+
],
|
968
|
+
"p2": [
|
969
|
+
hf.InputSource.task(
|
970
|
+
task_ref=0, task_source_type="input", element_iters=[1, 2]
|
971
|
+
)
|
972
|
+
],
|
973
|
+
},
|
974
|
+
allow_non_coincident_task_sources=True,
|
975
|
+
)
|
976
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
977
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
978
|
+
|
979
|
+
assert len(wk.tasks[1].elements) == 2
|
980
|
+
assert [i.value for i in wk.tasks[1].inputs.p1] == [11, 12]
|
981
|
+
assert [i.value for i in wk.tasks[1].inputs.p2] == [22, 23]
|
982
|
+
|
983
|
+
|
984
|
+
def test_input_source_task_input_from_multiple_element_sets_with_param_sequence(
|
985
|
+
null_config, tmp_path
|
986
|
+
):
|
987
|
+
t1 = hf.Task(
|
988
|
+
schema=hf.task_schemas.test_t1_ps,
|
989
|
+
element_sets=[
|
990
|
+
hf.ElementSet(inputs={"p1": {"a": 1}}),
|
991
|
+
hf.ElementSet(
|
992
|
+
sequences=[
|
993
|
+
hf.ValueSequence(
|
994
|
+
path="inputs.p1",
|
995
|
+
values=[{"a": 2}, {"a": 3}],
|
996
|
+
),
|
997
|
+
],
|
998
|
+
),
|
999
|
+
],
|
1000
|
+
)
|
1001
|
+
t2 = hf.Task(schema=hf.task_schemas.test_t1_ps)
|
1002
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
1003
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
1004
|
+
assert len(wk.tasks[1].elements) == 3
|
1005
|
+
assert [i.value["a"] for i in wk.tasks[1].inputs.p1] == [1, 2, 3]
|
1006
|
+
|
1007
|
+
|
1008
|
+
def test_raise_no_coincident_input_sources(null_config, tmp_path):
|
1009
|
+
s1 = make_schemas([[{"p1": None, "p2": None}, ("p3",), "t1"]])
|
1010
|
+
t1 = hf.Task(
|
1011
|
+
schema=s1,
|
1012
|
+
inputs={"p1": 100},
|
1013
|
+
sequences=[
|
1014
|
+
hf.ValueSequence.from_range(path="inputs.p2", start=0, stop=4),
|
1015
|
+
],
|
1016
|
+
)
|
1017
|
+
t2 = hf.Task(
|
1018
|
+
schema=s1,
|
1019
|
+
allow_non_coincident_task_sources=False,
|
1020
|
+
input_sources={
|
1021
|
+
"p1": [
|
1022
|
+
hf.InputSource.task(
|
1023
|
+
task_ref=0, task_source_type="input", element_iters=[0, 1]
|
1024
|
+
)
|
1025
|
+
],
|
1026
|
+
"p2": [
|
1027
|
+
hf.InputSource.task(
|
1028
|
+
task_ref=0, task_source_type="input", element_iters=[2, 3]
|
1029
|
+
)
|
1030
|
+
],
|
1031
|
+
},
|
1032
|
+
)
|
1033
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
1034
|
+
with pytest.raises(NoCoincidentInputSources):
|
1035
|
+
hf.Workflow.from_template(wkt, path=tmp_path)
|
1036
|
+
|
1037
|
+
|
1038
|
+
def test_input_source_task_input_from_multiple_element_sets_with_sub_param_sequence(
|
1039
|
+
null_config, tmp_path
|
1040
|
+
):
|
1041
|
+
t1 = hf.Task(
|
1042
|
+
schema=hf.task_schemas.test_t1_ps,
|
1043
|
+
element_sets=[
|
1044
|
+
hf.ElementSet(inputs={"p1": {"a": 1}}),
|
1045
|
+
hf.ElementSet(
|
1046
|
+
inputs={"p1": {"a": 1}},
|
1047
|
+
sequences=[
|
1048
|
+
hf.ValueSequence(
|
1049
|
+
path="inputs.p1.a",
|
1050
|
+
values=[2, 3],
|
1051
|
+
),
|
1052
|
+
],
|
1053
|
+
),
|
1054
|
+
],
|
1055
|
+
)
|
1056
|
+
t2 = hf.Task(schema=hf.task_schemas.test_t1_ps)
|
1057
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
1058
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
1059
|
+
assert len(wk.tasks[1].elements) == 3
|
1060
|
+
assert [i.value["a"] for i in wk.tasks[1].inputs.p1] == [1, 2, 3]
|
1061
|
+
|
1062
|
+
|
1063
|
+
def test_input_source_task_input_from_multiple_element_sets_with_sub_param_sequence_manual_sources_root_param(
|
1064
|
+
null_config, tmp_path
|
1065
|
+
):
|
1066
|
+
t1 = hf.Task(
|
1067
|
+
schema=hf.task_schemas.test_t1_ps,
|
1068
|
+
element_sets=[
|
1069
|
+
hf.ElementSet(inputs={"p1": {"a": 1}}),
|
1070
|
+
hf.ElementSet(
|
1071
|
+
inputs={"p1": {"a": 1}},
|
1072
|
+
sequences=[
|
1073
|
+
hf.ValueSequence(
|
1074
|
+
path="inputs.p1.a",
|
1075
|
+
values=[2, 3],
|
1076
|
+
),
|
1077
|
+
],
|
1078
|
+
),
|
1079
|
+
],
|
1080
|
+
)
|
1081
|
+
t2 = hf.Task(
|
1082
|
+
schema=hf.task_schemas.test_t1_ps,
|
1083
|
+
input_sources={
|
1084
|
+
"p1": [
|
1085
|
+
hf.InputSource.task(
|
1086
|
+
task_ref=0, task_source_type="input", element_iters=[0, 1]
|
1087
|
+
)
|
1088
|
+
]
|
1089
|
+
},
|
1090
|
+
)
|
1091
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
1092
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
1093
|
+
assert len(wk.tasks[1].elements) == 2
|
1094
|
+
assert [i.value["a"] for i in wk.tasks[1].inputs.p1] == [1, 2]
|
1095
|
+
|
1096
|
+
|
1097
|
+
def test_input_source_inputs_from_multiple_element_sets_with_sub_parameter_sequences_complex(
|
1098
|
+
null_config, tmp_path
|
1099
|
+
):
|
1100
|
+
t1 = hf.Task(
|
1101
|
+
schema=hf.task_schemas.test_t1_ps,
|
1102
|
+
element_sets=[
|
1103
|
+
hf.ElementSet(
|
1104
|
+
inputs={"p1": {"a": 1}},
|
1105
|
+
sequences=[
|
1106
|
+
hf.ValueSequence(
|
1107
|
+
path="inputs.p1.a",
|
1108
|
+
values=[2],
|
1109
|
+
),
|
1110
|
+
],
|
1111
|
+
),
|
1112
|
+
hf.ElementSet(
|
1113
|
+
inputs={"p1": {"a": 1}},
|
1114
|
+
sequences=[
|
1115
|
+
hf.ValueSequence(
|
1116
|
+
path="inputs.p1.c",
|
1117
|
+
values=[2, 3],
|
1118
|
+
),
|
1119
|
+
],
|
1120
|
+
),
|
1121
|
+
hf.ElementSet(
|
1122
|
+
inputs={"p1": {"a": 1}},
|
1123
|
+
sequences=[
|
1124
|
+
hf.ValueSequence(
|
1125
|
+
path="inputs.p1.b",
|
1126
|
+
values=[22, 33],
|
1127
|
+
),
|
1128
|
+
hf.ValueSequence(
|
1129
|
+
path="inputs.p1.a",
|
1130
|
+
values=[4, 5],
|
1131
|
+
),
|
1132
|
+
],
|
1133
|
+
),
|
1134
|
+
],
|
1135
|
+
)
|
1136
|
+
t2 = hf.Task(schema=hf.task_schemas.test_t1_ps)
|
1137
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
1138
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
1139
|
+
|
1140
|
+
assert len(wk.tasks[1].elements) == 5
|
1141
|
+
assert [i.value for i in wk.tasks[1].inputs.p1] == [
|
1142
|
+
{"a": 2},
|
1143
|
+
{"a": 1, "c": 2},
|
1144
|
+
{"a": 1, "c": 3},
|
1145
|
+
{"a": 4, "b": 22},
|
1146
|
+
{"a": 5, "b": 33},
|
1147
|
+
]
|
1148
|
+
|
1149
|
+
|
1150
|
+
def test_input_source_inputs_from_multiple_element_sets_with_sub_parameter_sequences_complex_reordered_iters(
|
1151
|
+
null_config, tmp_path
|
1152
|
+
):
|
1153
|
+
t1 = hf.Task(
|
1154
|
+
schema=hf.task_schemas.test_t1_ps,
|
1155
|
+
element_sets=[
|
1156
|
+
hf.ElementSet(
|
1157
|
+
inputs={"p1": {"a": 1}},
|
1158
|
+
sequences=[
|
1159
|
+
hf.ValueSequence(
|
1160
|
+
path="inputs.p1.a",
|
1161
|
+
values=[2],
|
1162
|
+
),
|
1163
|
+
],
|
1164
|
+
),
|
1165
|
+
hf.ElementSet(
|
1166
|
+
inputs={"p1": {"a": 1}},
|
1167
|
+
sequences=[
|
1168
|
+
hf.ValueSequence(
|
1169
|
+
path="inputs.p1.c",
|
1170
|
+
values=[2, 3],
|
1171
|
+
),
|
1172
|
+
],
|
1173
|
+
),
|
1174
|
+
hf.ElementSet(
|
1175
|
+
inputs={"p1": {"a": 1}},
|
1176
|
+
sequences=[
|
1177
|
+
hf.ValueSequence(
|
1178
|
+
path="inputs.p1.b",
|
1179
|
+
values=[22, 33],
|
1180
|
+
),
|
1181
|
+
hf.ValueSequence(
|
1182
|
+
path="inputs.p1.a",
|
1183
|
+
values=[4, 5],
|
1184
|
+
),
|
1185
|
+
],
|
1186
|
+
),
|
1187
|
+
],
|
1188
|
+
)
|
1189
|
+
t2 = hf.Task(
|
1190
|
+
schema=hf.task_schemas.test_t1_ps,
|
1191
|
+
input_sources={
|
1192
|
+
# reordered p1.c elem iters:
|
1193
|
+
"p1.c": [
|
1194
|
+
hf.InputSource.task(
|
1195
|
+
task_ref=0, task_source_type="input", element_iters=[2, 1]
|
1196
|
+
)
|
1197
|
+
]
|
1198
|
+
},
|
1199
|
+
allow_non_coincident_task_sources=True, # to maintain custom ordering
|
1200
|
+
)
|
1201
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
1202
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
1203
|
+
|
1204
|
+
assert len(wk.tasks[1].elements) == 5
|
1205
|
+
assert [i.value for i in wk.tasks[1].inputs.p1] == [
|
1206
|
+
{"a": 2},
|
1207
|
+
{"a": 1, "c": 3},
|
1208
|
+
{"a": 1, "c": 2},
|
1209
|
+
{"a": 4, "b": 22},
|
1210
|
+
{"a": 5, "b": 33},
|
1211
|
+
]
|
1212
|
+
|
1213
|
+
|
1214
|
+
def test_input_source_inputs_from_multiple_element_sets_with_sub_parameter_sequences_mixed_padding(
|
1215
|
+
null_config, tmp_path
|
1216
|
+
):
|
1217
|
+
|
1218
|
+
t1 = hf.Task(
|
1219
|
+
schema=hf.task_schemas.test_t1_ps,
|
1220
|
+
element_sets=[
|
1221
|
+
hf.ElementSet(
|
1222
|
+
inputs={"p1": {"a": 1}},
|
1223
|
+
),
|
1224
|
+
hf.ElementSet(
|
1225
|
+
inputs={"p1": {"a": 1}},
|
1226
|
+
nesting_order={"inputs.p1.a": 0, "inputs.p1.b": 1},
|
1227
|
+
sequences=[
|
1228
|
+
hf.ValueSequence(
|
1229
|
+
path="inputs.p1.a",
|
1230
|
+
values=[4, 5],
|
1231
|
+
),
|
1232
|
+
hf.ValueSequence(
|
1233
|
+
path="inputs.p1.b",
|
1234
|
+
values=[22],
|
1235
|
+
),
|
1236
|
+
],
|
1237
|
+
),
|
1238
|
+
],
|
1239
|
+
)
|
1240
|
+
t2 = hf.Task(
|
1241
|
+
schema=hf.task_schemas.test_t1_ps,
|
1242
|
+
# `p1.b` has a different nesting order to the root param `p1`, so it will not be
|
1243
|
+
# "padded" to have the same multiplicity as `p1`/`p1.a`. With a higher nesting
|
1244
|
+
# order, it will be "applied" to all other elements, meaning we'll gain a value
|
1245
|
+
# for `p1.b` for all elements (including from the first element set, which didn't
|
1246
|
+
# have a value for `p1.b`):
|
1247
|
+
nesting_order={
|
1248
|
+
"inputs.p1": 0,
|
1249
|
+
"inputs.p1.a": 0,
|
1250
|
+
"inputs.p1.b": 1,
|
1251
|
+
},
|
1252
|
+
)
|
1253
|
+
wkt = hf.WorkflowTemplate(name="test", tasks=[t1, t2])
|
1254
|
+
wk = hf.Workflow.from_template(wkt, path=tmp_path)
|
1255
|
+
|
1256
|
+
assert len(wk.tasks[1].elements) == 4
|
1257
|
+
assert [i.value for i in wk.tasks[1].inputs.p1] == [
|
1258
|
+
{"a": 1, "b": 22},
|
1259
|
+
{"a": 1, "b": 22},
|
1260
|
+
{"a": 5, "b": 22},
|
1261
|
+
{"a": 5, "b": 22},
|
1262
|
+
]
|
@@ -74,6 +74,7 @@ def test_store_pending_add_EAR(tmp_path):
|
|
74
74
|
EAR_ID = store.add_EAR(
|
75
75
|
elem_iter_ID=0,
|
76
76
|
action_idx=0,
|
77
|
+
commands_idx=[],
|
77
78
|
data_idx={},
|
78
79
|
metadata={},
|
79
80
|
)
|
@@ -83,6 +84,7 @@ def test_store_pending_add_EAR(tmp_path):
|
|
83
84
|
is_pending=True,
|
84
85
|
elem_iter_ID=0,
|
85
86
|
action_idx=0,
|
87
|
+
commands_idx=[],
|
86
88
|
data_idx={},
|
87
89
|
metadata={},
|
88
90
|
)
|
@@ -186,7 +188,7 @@ def test_get_task_elements_single_element_iter_EAR_pending(tmp_path):
|
|
186
188
|
store = JSONPersistentStore.make_test_store_from_spec(
|
187
189
|
[{"elements": [{"iterations": [{}]}]}], dir=tmp_path
|
188
190
|
)
|
189
|
-
store.add_EAR(elem_iter_ID=0, action_idx=0, data_idx={}, metadata={})
|
191
|
+
store.add_EAR(elem_iter_ID=0, action_idx=0, commands_idx=[], data_idx={}, metadata={})
|
190
192
|
assert store.get_task_elements(0, slice(0, None)) == [
|
191
193
|
{
|
192
194
|
"id": 0,
|
@@ -209,6 +211,7 @@ def test_get_task_elements_single_element_iter_EAR_pending(tmp_path):
|
|
209
211
|
"is_pending": True,
|
210
212
|
"elem_iter_ID": 0,
|
211
213
|
"action_idx": 0,
|
214
|
+
"commands_idx": [],
|
212
215
|
"data_idx": {},
|
213
216
|
"metadata": {},
|
214
217
|
}
|
hpcflow/tests/unit/test_task.py
CHANGED
@@ -721,7 +721,7 @@ def test_expected_name_two_schemas_both_with_method_and_implementation():
|
|
721
721
|
def test_raise_on_negative_nesting_order():
|
722
722
|
s1 = make_schemas([[{"p1": None}, ()]])
|
723
723
|
with pytest.raises(TaskTemplateInvalidNesting):
|
724
|
-
hf.Task(schema=s1, nesting_order={"p1": -1})
|
724
|
+
hf.Task(schema=s1, nesting_order={"inputs.p1": -1})
|
725
725
|
|
726
726
|
|
727
727
|
# TODO: test resolution of elements and with raise MissingInputs
|